mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-12-23 06:35:51 +00:00
Video improvements
This commit is contained in:
@@ -0,0 +1,248 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import JavaScriptCore
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
|
||||
private var ObjCKey_ContextReference: Int?
|
||||
|
||||
@objc private protocol JsCorePolyfillsExport: JSExport {
|
||||
func postMessage(_ object: JSValue)
|
||||
func consoleLog(_ object: JSValue)
|
||||
func consoleLog(_ object: JSValue, _ arg1: JSValue)
|
||||
func consoleLog(_ object: JSValue, _ arg1: JSValue, _ arg2: JSValue)
|
||||
func performanceNow() -> Double
|
||||
}
|
||||
|
||||
@objc private final class JsCorePolyfills: NSObject, JsCorePolyfillsExport {
|
||||
private let queue: Queue
|
||||
private let context: WebViewNativeJSContextImpl.Reference
|
||||
|
||||
init(queue: Queue, context: WebViewNativeJSContextImpl.Reference) {
|
||||
self.queue = queue
|
||||
self.context = context
|
||||
|
||||
super.init()
|
||||
}
|
||||
|
||||
@objc func postMessage(_ object: JSValue) {
|
||||
guard object.isObject else {
|
||||
return
|
||||
}
|
||||
guard let message = object.toDictionary() as? [String: Any] else {
|
||||
return
|
||||
}
|
||||
let context = self.context
|
||||
self.queue.async {
|
||||
guard let context = context.context else {
|
||||
return
|
||||
}
|
||||
let handleScriptMessage = context.handleScriptMessage
|
||||
|
||||
Queue.mainQueue().async {
|
||||
handleScriptMessage(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func consoleLog(_ object: JSValue) {
|
||||
#if DEBUG
|
||||
print("\(object)")
|
||||
#endif
|
||||
}
|
||||
|
||||
@objc func consoleLog(_ object: JSValue, _ arg1: JSValue) {
|
||||
#if DEBUG
|
||||
print("\(object) \(arg1)")
|
||||
#endif
|
||||
}
|
||||
|
||||
@objc func consoleLog(_ object: JSValue, _ arg1: JSValue, _ arg2: JSValue) {
|
||||
#if DEBUG
|
||||
print("\(object) \(arg1) \(arg2)")
|
||||
#endif
|
||||
}
|
||||
|
||||
@objc func performanceNow() -> Double {
|
||||
return CFAbsoluteTimeGetCurrent()
|
||||
}
|
||||
}
|
||||
|
||||
@objc private protocol TimerJSExport: JSExport {
|
||||
func setTimeout(_ callback: JSValue, _ ms: Double) -> Int32
|
||||
func setInterval(_ callback: JSValue, _ ms: Double) -> Int32
|
||||
|
||||
func clearTimeout(_ id: Int32)
|
||||
}
|
||||
|
||||
@objc private class TimeoutPolyfill: NSObject, TimerJSExport {
|
||||
private let queue: Queue
|
||||
|
||||
private var timers: [Int32: SwiftSignalKit.Timer] = [:]
|
||||
private var nextId: Int32 = 0
|
||||
|
||||
init(queue: Queue) {
|
||||
self.queue = queue
|
||||
}
|
||||
|
||||
deinit {
|
||||
for (_, timer) in self.timers {
|
||||
timer.invalidate()
|
||||
}
|
||||
}
|
||||
|
||||
func register(jsContext: JSContext) {
|
||||
jsContext.evaluateScript("""
|
||||
function setTimeout(...args) {
|
||||
if (args.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const [callback, delay = 0, ...callbackArgs] = args;
|
||||
|
||||
return _timeoutPolyfill.setTimeout(() => {
|
||||
callback(...callbackArgs);
|
||||
}, delay);
|
||||
}
|
||||
|
||||
function setInterval(...args) {
|
||||
if (args.length === 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const [callback, delay = 0, ...callbackArgs] = args;
|
||||
|
||||
return _timeoutPolyfill.setInterval(() => {
|
||||
callback(...callbackArgs);
|
||||
}, delay);
|
||||
}
|
||||
|
||||
function clearTimeout(indentifier) {
|
||||
_timeoutPolyfill.clearTimeout(indentifier)
|
||||
}
|
||||
|
||||
function clearInterval(indentifier) {
|
||||
_timeoutPolyfill.clearTimeout(indentifier)
|
||||
}
|
||||
"""
|
||||
)
|
||||
}
|
||||
|
||||
func clearTimeout(_ id: Int32) {
|
||||
let timer = self.timers.removeValue(forKey: id)
|
||||
timer?.invalidate()
|
||||
}
|
||||
|
||||
func setTimeout(_ callback: JSValue, _ ms: Double) -> Int32 {
|
||||
return self.createTimer(callback: callback, ms: ms, repeats: false)
|
||||
}
|
||||
|
||||
func setInterval(_ callback: JSValue, _ ms: Double) -> Int32 {
|
||||
return self.createTimer(callback: callback, ms: ms, repeats: true)
|
||||
}
|
||||
|
||||
func createTimer(callback: JSValue, ms: Double, repeats: Bool) -> Int32 {
|
||||
let timeInterval = ms / 1000.0
|
||||
|
||||
let id = self.nextId
|
||||
self.nextId += 1
|
||||
let timer = SwiftSignalKit.Timer(timeout: timeInterval, repeat: repeats, completion: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
callback.call(withArguments: nil)
|
||||
|
||||
if !repeats {
|
||||
self.timers.removeValue(forKey: id)
|
||||
}
|
||||
}, queue: self.queue)
|
||||
self.timers[id] = timer
|
||||
timer.start()
|
||||
|
||||
return id
|
||||
}
|
||||
}
|
||||
|
||||
final class WebViewNativeJSContextImpl: HLSJSContext {
|
||||
fileprivate final class Reference {
|
||||
weak var context: WebViewNativeJSContextImpl.Impl?
|
||||
|
||||
init(context: WebViewNativeJSContextImpl.Impl) {
|
||||
self.context = context
|
||||
}
|
||||
}
|
||||
|
||||
fileprivate final class Impl {
|
||||
let queue: Queue
|
||||
let context: JSContext
|
||||
let handleScriptMessage: ([String: Any]) -> Void
|
||||
|
||||
init(queue: Queue, handleScriptMessage: @escaping ([String: Any]) -> Void) {
|
||||
self.queue = queue
|
||||
self.context = JSContext()
|
||||
self.handleScriptMessage = handleScriptMessage
|
||||
|
||||
#if DEBUG
|
||||
if #available(iOS 16.4, *) {
|
||||
self.context.isInspectable = true
|
||||
}
|
||||
#endif
|
||||
|
||||
self.context.exceptionHandler = { context, exception in
|
||||
if let exception {
|
||||
Logger.shared.log("WebViewNativeJSContextImpl", "JS exception: \(exception)")
|
||||
#if DEBUG
|
||||
print("JS exception: \(exception)")
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
let timeoutPolyfill = TimeoutPolyfill(queue: self.queue)
|
||||
self.context.setObject(timeoutPolyfill, forKeyedSubscript: "_timeoutPolyfill" as (NSCopying & NSObjectProtocol))
|
||||
timeoutPolyfill.register(jsContext: self.context)
|
||||
|
||||
self.context.setObject(JsCorePolyfills(queue: self.queue, context: Reference(context: self)), forKeyedSubscript: "_JsCorePolyfills" as (NSCopying & NSObjectProtocol))
|
||||
|
||||
let bundle = Bundle(for: WebViewHLSJSContextImpl.self)
|
||||
let bundlePath = bundle.bundlePath + "/HlsBundle.bundle"
|
||||
if let indexJsString = try? String(contentsOf: URL(fileURLWithPath: bundlePath + "/headless_prologue.js"), encoding: .utf8) {
|
||||
self.context.evaluateScript(indexJsString, withSourceURL: URL(fileURLWithPath: "index/index.bundle.js"))
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
|
||||
if let indexJsString = try? String(contentsOf: URL(fileURLWithPath: bundlePath + "/index.bundle.js"), encoding: .utf8) {
|
||||
self.context.evaluateScript(indexJsString, withSourceURL: URL(fileURLWithPath: "index.bundle.js"))
|
||||
} else {
|
||||
assertionFailure()
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
print("WebViewNativeJSContextImpl.deinit")
|
||||
}
|
||||
|
||||
func evaluateJavaScript(_ string: String) {
|
||||
self.context.evaluateScript(string)
|
||||
}
|
||||
}
|
||||
|
||||
static let sharedQueue = Queue(name: "WebViewNativeJSContextImpl", qos: .default)
|
||||
|
||||
private let queue: Queue
|
||||
private let impl: QueueLocalObject<Impl>
|
||||
|
||||
init(handleScriptMessage: @escaping ([String: Any]) -> Void) {
|
||||
let queue = WebViewNativeJSContextImpl.sharedQueue
|
||||
self.queue = queue
|
||||
self.impl = QueueLocalObject(queue: queue, generate: {
|
||||
return Impl(queue: queue, handleScriptMessage: handleScriptMessage)
|
||||
})
|
||||
}
|
||||
|
||||
func evaluateJavaScript(_ string: String) {
|
||||
self.impl.with { impl in
|
||||
impl.evaluateJavaScript(string)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user