diff --git a/src/content/webglHook.js b/src/content/webglHook.js index d627d9a..8f710fa 100644 --- a/src/content/webglHook.js +++ b/src/content/webglHook.js @@ -1,91 +1,41 @@ -if (typeof exportFunction === "function") ns.on("capabilities", event => { +// depends on nscl/content/patchWindow.js +"use strict"; +ns.on("capabilities", event => { debug("WebGL Hook", document.URL, document.documentElement && document.documentElement.innerHTML, ns.capabilities); // DEV_ONLY if (ns.allows("webgl")) return; - - // win: window object to modify. - // modifyTarget: callback to function that modifies the desired properties - // or methods. Callback must take target window as argument. - function modifyWindow(win, modifyTarget) { + let env = {eventName: `nsWebgl:${uuid()}`}; + window.addEventListener(env.eventName, e => { + let canvas = e.target; + if (!(canvas instanceof HTMLCanvasElement)) return; + let request = { + id: "noscript-webgl", + type: "webgl", + url: document.URL, + documentUrl: document.URL, + embeddingDocument: true, + }; + seen.record({policyType: "webgl", request, allowed: false}); try { - modifyTarget(win); - modifyWindowOpenMethod(win, modifyTarget); - modifyFramingElements(win, modifyTarget); + let ph = PlaceHolder.create("webgl", request); + ph.replace(canvas); + PlaceHolder.listen(); } catch (e) { - if (e instanceof DOMException && e.name === "SecurityError") { - // In case someone tries to access SOP restricted window. - // We can just ignore this. - } else throw e; + error(e); } - } + notifyPage(); + }, true); - function modifyWindowOpenMethod(win, modifyTarget) { - let windowOpen = win.wrappedJSObject ? win.wrappedJSObject.open : win.open; - exportFunction(function(...args) { - let newWin = windowOpen.call(this, ...args); - if (newWin) modifyWindow(newWin, modifyTarget); - return newWin; - }, win, {defineAs: "open"}); - } - - function modifyFramingElements(win, modifyTarget) { - for (let property of ["contentWindow", "contentDocument"]) { - for (let interface of ["Frame", "IFrame", "Object"]) { - let proto = win[`HTML${interface}Element`].prototype; - modifyContentProperties(proto, property, modifyTarget) - } - } - } - - function modifyContentProperties(proto, property, modifyTarget) { - let descriptor = Object.getOwnPropertyDescriptor(proto, property); - let origGetter = descriptor.get; - let replacementFn; - - if (property === "contentWindow") { replacementFn = function() { - let win = origGetter.call(this); - if (win) modifyWindow(win, modifyTarget); - return win; - }} - if (property === "contentDocument") { replacementFn = function() { - let document = origGetter.call(this); - if (document && document.defaultView) modifyWindow(document.defaultView, modifyTarget); - return document; - }} - - descriptor.get = exportFunction(replacementFn, proto, {defineAs: `get $property`}); - let wrappedProto = proto.wrappedJSObject || proto; - Object.defineProperty(wrappedProto, property, descriptor); - } - - // - - function modifyGetContext(win) { + function modifyGetContext(win, env) { let proto = win.HTMLCanvasElement.prototype; let getContext = proto.getContext; exportFunction(function(type, ...rest) { if (type && type.toLowerCase().includes("webgl")) { - let request = { - id: "noscript-webgl", - type: "webgl", - url: document.URL, - documentUrl: document.URL, - embeddingDocument: true, - }; - seen.record({policyType: "webgl", request, allowed: false}); - try { - let ph = PlaceHolder.create("webgl", request); - ph.replace(this); - PlaceHolder.listen(); - } catch (e) { - error(e); - } - notifyPage(); + this.dispatchEvent(new Event(env.eventName)); return null; } return getContext.call(this, type, ...rest); }, proto, {defineAs: "getContext"}); } - modifyWindow(window, modifyGetContext); - + patchWindow(modifyGetContext, env); }); diff --git a/src/nscl/content/patchWindow.js b/src/nscl/content/patchWindow.js index 0afa98d..bc99181 100644 --- a/src/nscl/content/patchWindow.js +++ b/src/nscl/content/patchWindow.js @@ -1,6 +1,6 @@ "use strict"; -function patchWindow(patchingCallback, targetWindow = window) { +function patchWindow(patchingCallback, env = {}) { let nativeExport = this && this.exportFunction || typeof exportFunction == "function"; if (!nativeExport) { // Chromium @@ -26,7 +26,7 @@ function patchWindow(patchingCallback, targetWindow = window) { patchWindow, exportFunction, cloneInto, - }).patchWindow(${patchingCallback}); + }).patchWindow(${patchingCallback}, ${JSON.stringify(env)}); })(); `; document.documentElement.insertBefore(script, document.documentElement.firstChild); @@ -39,7 +39,7 @@ function patchWindow(patchingCallback, targetWindow = window) { // or methods. Callback must take target window as argument. function modifyWindow(win, modifyTarget) { try { - modifyTarget(win.wrappedJSObject || win); + modifyTarget(win.wrappedJSObject || win, env); modifyWindowOpenMethod(win, modifyTarget); modifyFramingElements(win, modifyTarget); } catch (e) { @@ -88,6 +88,6 @@ function patchWindow(patchingCallback, targetWindow = window) { let wrappedProto = proto.wrappedJSObject || proto; Object.defineProperty(wrappedProto, property, descriptor); } - - return modifyWindow(targetWindow, patchingCallback); + + return modifyWindow(window, patchingCallback); }