diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js index 3ff0a00eb..01f922bcb 100644 --- a/platform/firefox/frameModule.js +++ b/platform/firefox/frameModule.js @@ -19,7 +19,7 @@ Home: https://github.com/gorhill/uBlock */ -/* global Services, Components, XPCOMUtils */ +/* global Services, Components, XPCOMUtils, __URI__ */ 'use strict'; @@ -36,11 +36,15 @@ Cu['import']('resource://gre/modules/XPCOMUtils.jsm'); /******************************************************************************/ -const getMessager = win => - win.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDocShell) - .sameTypeRootTreeItem.QueryInterface(Ci.nsIDocShell) +const getMessageManager = function(context) { + return context + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDocShell) + .sameTypeRootTreeItem + .QueryInterface(Ci.nsIDocShell) .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIContentFrameMessageManager); +}; /******************************************************************************/ @@ -49,8 +53,7 @@ const contentPolicy = { classID: Components.ID('{e6d173c8-8dbf-4189-a6fd-189e8acffd27}'), contractID: '@' + appName + '/content-policy;1', ACCEPT: Ci.nsIContentPolicy.ACCEPT, - REJECT: Ci.nsIContentPolicy.REJECT_REQUEST, - requestMessageName: appName + ':onBeforeRequest', + messageName: appName + ':shouldLoad', get componentRegistrar() { return Components.manager.QueryInterface(Ci.nsIComponentRegistrar); }, @@ -93,6 +96,7 @@ const contentPolicy = { false ); }, + // https://bugzil.la/612921 shouldLoad: function(type, location, origin, context) { if (!context || !/^https?$/.test(location.scheme)) { return this.ACCEPT; @@ -102,23 +106,17 @@ const contentPolicy = { ? context.contentWindow || context : (context.ownerDocument || context).defaultView; - if (!win) { - return this.ACCEPT; + if (win) { + getMessageManager(win).sendSyncMessage(this.messageName, { + url: location.spec, + type: type, + frameId: type === 6 ? -1 : (win === win.top ? 0 : 1), + parentFrameId: win === win.top ? -1 : 0 + }); } - let result = getMessager(win).sendSyncMessage(this.requestMessageName, { - url: location.spec, - type: type, - tabId: -1, // determined in background script - frameId: type === 6 ? -1 : (win === win.top ? 0 : 1), - parentFrameId: win === win.top ? -1 : 0 - })[0]; - - return result === true ? this.REJECT : this.ACCEPT; - }/*, - shouldProcess: function() { return this.ACCEPT; - }*/ + } }; /******************************************************************************/ @@ -126,7 +124,7 @@ const contentPolicy = { const docObserver = { contentBaseURI: 'chrome://' + appName + '/content/', initContext: function(win, sandbox) { - let messager = getMessager(win); + let messager = getMessageManager(win); if (sandbox) { win = Cu.Sandbox([win], { diff --git a/platform/firefox/frameScript.js b/platform/firefox/frameScript.js index c93c7e006..275787390 100644 --- a/platform/firefox/frameScript.js +++ b/platform/firefox/frameScript.js @@ -23,20 +23,18 @@ /******************************************************************************/ +// https://bugzil.la/673569 + (function(frameScriptContext) { 'use strict'; /******************************************************************************/ -let appName; +let appName = Components.stack.filename.match(/:\/\/([^\/]+)/)[1]; let listeners = {}; -try { throw new Error; } catch (ex) { - appName = ex.fileName.match(/:\/\/([^\/]+)/)[1]; -} - -Components.utils['import']('chrome://' + appName + '/content/frameModule.js', {}); +Components.utils['import'](Components.stack.filename.replace('Script', 'Module'), {}); /******************************************************************************/ diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index 0e350a8e1..6f4b33468 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -321,6 +321,7 @@ vAPI.tabs.registerListeners = function() { // onClosed - handled in windowWatcher.onTabClose // onPopup ? + for (var win of this.getWindows()) { windowWatcher.onReady.call(win); } @@ -600,10 +601,21 @@ vAPI.setIcon = function(tabId, img, badge) { } var button = curWin.document.getElementById(vAPI.toolbarButton.widgetId); + + if (!button) { + return; + } + + /*if (!button.classList.contains('badged-button')) { + button.classList.add('badged-button'); + }*/ + var icon = vAPI.tabIcons[tabId]; + button.setAttribute('badge', icon && icon.badge || ''); - icon = vAPI.getURL(icon && icon.img || 'img/browsericons/icon16-off.svg'); - button.style.listStyleImage = 'url(' + icon + ')'; + button.image = vAPI.getURL( + button.image && icon && icon.img || 'img/browsericons/icon16-off.svg' + ); }; /******************************************************************************/ @@ -623,11 +635,11 @@ vAPI.toolbarButton.init = function() { defaultArea: CustomizableUI.AREA_NAVBAR, label: vAPI.app.name, tooltiptext: vAPI.app.name, - onViewShowing: function(e) { - e.target.firstChild.setAttribute('src', vAPI.getURL('popup.html')); + onViewShowing: function({target}) { + target.firstChild.setAttribute('src', vAPI.getURL('popup.html')); }, - onViewHiding: function(e) { - e.target.firstChild.setAttribute('src', 'about:blank'); + onViewHiding: function({target}) { + target.firstChild.setAttribute('src', 'about:blank'); } }); @@ -645,11 +657,9 @@ vAPI.toolbarButton.register = function(doc) { var panel = doc.createElement('panelview'); panel.setAttribute('id', this.panelId); - var iframe = panel.appendChild(doc.createElement('iframe')); + var iframe = doc.createElement('iframe'); iframe.setAttribute('type', 'content'); - panel.style.overflow = iframe.style.overflow = 'hidden'; - doc.getElementById('PanelUI-multiView') .appendChild(panel) .appendChild(iframe); @@ -672,14 +682,13 @@ vAPI.toolbarButton.register = function(doc) { }; var onPopupReady = function() { - if (!this.contentWindow - || this.contentWindow.location.host !== location.host) { + var win = this.contentWindow; + + if (!win || win.location.host !== location.host) { return; } - var mutObs = this.contentWindow.MutationObserver; - - (new mutObs(delayedResize)).observe(this.contentDocument, { + new win.MutationObserver(delayedResize).observe(win.document, { childList: true, attributes: true, characterData: true, @@ -712,7 +721,7 @@ vAPI.toolbarButton.register = function(doc) { '#' + this.panelId + ', #' + this.panelId + ' > iframe {', 'width: 180px;', 'height: 310px;', - 'transition: width .1s, height .1s;', + 'overflow: hidden !important;', '}' ].join('')); @@ -860,14 +869,127 @@ vAPI.messaging.broadcast = function(message) { /******************************************************************************/ -vAPI.net = { - beforeRequestMessageName: location.host + ':onBeforeRequest' +vAPI.net = {}; + +/******************************************************************************/ + +var httpObserver = { + ABORT: Components.results.NS_BINDING_ABORTED, + lastRequest: { + url: null, + type: null, + tabId: null, + frameId: null, + parentFrameId: null + }, + observe: function(httpChannel, topic) { + if (!(httpChannel instanceof Ci.nsIHttpChannel)) { + return; + } + + httpChannel = httpChannel.QueryInterface(Ci.nsIHttpChannel); + var URI = httpChannel.URI, tabId, result; + + // the first distinct character + topic = topic.charAt(8); + + // http-on-modify-request + if (topic === 'm') { + // var onHeadersReceived = vAPI.net.onHeadersReceived; + + return; + } + + // http-on-examine-request + if (topic === 'e') { + try { + tabId = httpChannel.getProperty('tabId'); + } catch (ex) { + return; + } + + if (!tabId) { + return; + } + + var header = 'Content-Security-Policy'; + + try { + result = httpChannel.getResponseHeader(header); + } catch (ex) { + result = null; + } + + result = vAPI.net.onHeadersReceived.callback({ + url: URI.spec, + tabId: tabId, + parentFrameId: -1, + responseHeaders: result ? [{name: header, value: result}] : [] + }); + + if (result) { + httpChannel.setResponseHeader( + header, + result.responseHeaders[0].value, + true + ); + } + + return; + } + + // http-on-opening-request + + var lastRequest = this.lastRequest; + + if (!lastRequest.url || lastRequest.url !== URI.spec) { + lastRequest.url = null; + return; + } + + // Important! When loading file via XHR for mirroring, + // the URL will be the same, so it could fall into an infinite loop + lastRequest.url = null; + + if (lastRequest.type === 'main_frame' + && httpChannel instanceof Ci.nsIWritablePropertyBag) { + httpChannel.setProperty('tabId', lastRequest.tabId); + } + + var onBeforeRequest = vAPI.net.onBeforeRequest; + + if (!onBeforeRequest.types.has(lastRequest.type)) { + return; + } + + result = onBeforeRequest.callback({ + url: URI.spec, + type: lastRequest.type, + tabId: lastRequest.tabId, + frameId: lastRequest.frameId, + parentFrameId: lastRequest.parentFrameId + }); + + if (!result || typeof result !== 'object') { + return; + } + + if (result.cancel === true) { + httpChannel.cancel(this.ABORT); + } + else if (result.redirectUrl) { + httpChannel.redirectionLimit = 1; + httpChannel.redirectTo( + Services.io.newURI(result.redirectUrl, null, null) + ); + } + } }; /******************************************************************************/ vAPI.net.registerListeners = function() { - var types = { + var typeMap = { 2: 'script', 3: 'image', 4: 'stylesheet', @@ -877,42 +999,36 @@ vAPI.net.registerListeners = function() { 11: 'xmlhttprequest' }; - var onBeforeRequest = this.onBeforeRequest; + this.onBeforeRequest.types = new Set(this.onBeforeRequest.types); - this.onBeforeRequest = function(e) { - var details = e.data; - - details.type = types[details.type] || 'other'; - details.tabId = vAPI.tabs.getTabId(e.target); - - if (onBeforeRequest.types.indexOf(details.type) === -1) { - return false; - } - - var block = onBeforeRequest.callback(details); - - if (block && typeof block === 'object') { - if (block.cancel === true) { - return true; - } - else if (block.redirectURL) { - return block.redirectURL; - } - } - - return false; + var shouldLoadListenerMessageName = location.host + ':shouldLoad'; + var shouldLoadListener = function(e) { + var lastRequest = httpObserver.lastRequest; + lastRequest.url = e.data.url; + lastRequest.type = typeMap[e.data.type] || 'other'; + lastRequest.tabId = vAPI.tabs.getTabId(e.target); + lastRequest.frameId = e.data.frameId; + lastRequest.parentFrameId = e.data.parentFrameId; }; vAPI.messaging.globalMessageManager.addMessageListener( - this.beforeRequestMessageName, - this.onBeforeRequest + shouldLoadListenerMessageName, + shouldLoadListener ); + Services.obs.addObserver(httpObserver, 'http-on-opening-request', false); + // Services.obs.addObserver(httpObserver, 'http-on-modify-request', false); + Services.obs.addObserver(httpObserver, 'http-on-examine-response', false); + vAPI.unload.push(function() { vAPI.messaging.globalMessageManager.removeMessageListener( - vAPI.net.beforeRequestMessageName, - vAPI.net.onBeforeRequest + shouldLoadListenerMessageName, + shouldLoadListener ); + + Services.obs.removeObserver(httpObserver, 'http-on-opening-request'); + // Services.obs.removeObserver(httpObserver, 'http-on-modify-request'); + Services.obs.removeObserver(httpObserver, 'http-on-examine-response'); }); }; diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index a70000510..d187a5f08 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -95,10 +95,10 @@ vAPI.messaging = { this.channels['vAPI'] = {}; this.channels['vAPI'].listener = function(msg) { - if (msg.cmd === 'injectScript') { + if ( msg.cmd === 'injectScript' ) { var details = msg.details; - if (!details.allFrames && window !== window.top) { + if ( !details.allFrames && window !== window.top ) { return; } @@ -108,12 +108,14 @@ vAPI.messaging = { }, close: function() { - if (this.connector) { - removeMessageListener(this.connectorId, this.connector); - this.connector = null; - this.channels = {}; - this.listeners = {}; + if ( !this.connector ) { + return; } + + removeMessageListener(this.connectorId, this.connector); + this.connector = null; + this.channels = {}; + this.listeners = {}; }, channel: function(channelName, callback) {