diff options
author | gorhill <rhill@raymondhill.net> | 2015-06-17 13:49:43 -0400 |
---|---|---|
committer | gorhill <rhill@raymondhill.net> | 2015-06-17 14:06:59 -0400 |
commit | 34485a04658ae7d8357dabe5e42b18e3e066a4b9 (patch) | |
tree | f54dd3ef03a63194886edfe52407221774205f77 | |
parent | 9406e40fdc1644040234b3e0b240e24fa576a214 (diff) | |
download | uBlock-34485a04658ae7d8357dabe5e42b18e3e066a4b9.zip uBlock-34485a04658ae7d8357dabe5e42b18e3e066a4b9.tar.gz uBlock-34485a04658ae7d8357dabe5e42b18e3e066a4b9.tar.bz2 |
code review
-rw-r--r-- | platform/firefox/css/legacy-toolbar-button.css | 52 | ||||
-rw-r--r-- | platform/firefox/frameModule.js | 11 | ||||
-rw-r--r-- | platform/firefox/vapi-background.js | 1101 | ||||
-rw-r--r-- | platform/firefox/vapi-client.js | 4 | ||||
-rw-r--r-- | platform/firefox/vapi-common.js | 31 | ||||
-rw-r--r-- | src/js/scriptlets/element-picker.js | 3 | ||||
-rwxr-xr-x | tools/make-firefox.sh | 1 |
7 files changed, 672 insertions, 531 deletions
diff --git a/platform/firefox/css/legacy-toolbar-button.css b/platform/firefox/css/legacy-toolbar-button.css index f29052a..37900cb 100644 --- a/platform/firefox/css/legacy-toolbar-button.css +++ b/platform/firefox/css/legacy-toolbar-button.css @@ -1,42 +1,44 @@ -#uBlock-legacy-button { - list-style-image: url('../img/browsericons/icon-large.svg'); +#uBlock0-legacy-button { + list-style-image: url('../img/browsericons/icon-large.svg'); } -#uBlock-legacy-button.off { - list-style-image: url('../img/browsericons/icon-large-off.svg'); +#uBlock0-legacy-button.off { + list-style-image: url('../img/browsericons/icon-large-off.svg'); } -toolbar[iconsize="small"] #uBlock-legacy-button { - list-style-image: url('../img/browsericons/icon16.svg'); +toolbar[iconsize="small"] #uBlock0-legacy-button { + list-style-image: url('../img/browsericons/icon16.svg'); } -toolbar[iconsize="small"] #uBlock-legacy-button.off { - list-style-image: url('../img/browsericons/icon16-off.svg'); +toolbar[iconsize="small"] #uBlock0-legacy-button.off { + list-style-image: url('../img/browsericons/icon16-off.svg'); } -#uBlock-legacy-button[badge]::before { - position: fixed; - margin-top: -2px; - padding: 1px 2px; - font-size: 9px; - font-weight: bold; +#uBlock0-legacy-button[badge]::before { + background: #555; color: #fff; - background: #666; content: attr(badge); + font: bold 10px sans-serif; + margin-top: -2px; + padding: 0 2px; + position: fixed; } -/* This hack required because if the before content changes it de-pops the popup (without firing any events). So just hide it instead. Note, can't actually *hide* it, or the same thing happens. '*/ -#uBlock-legacy-button[badge=""]::before { - padding: 0; +/* This hack required because if the before content changes it de-pops the + popup (without firing any events). So just hide it instead. Note, can't + actually *hide* it, or the same thing happens. +**/ +#uBlock0-legacy-button[badge=""]::before { + padding: 0; } /* Override off state when in palette */ -toolbarpaletteitem #uBlock-legacy-button.off { - list-style-image: url('../img/browsericons/icon-large.svg'); +toolbarpaletteitem #uBlock0-legacy-button.off { + list-style-image: url('../img/browsericons/icon-large.svg'); } /* Override badge when in palette */ -toolbarpaletteitem #uBlock-legacy-button[badge]::before { - content: none; +toolbarpaletteitem #uBlock0-legacy-button[badge]::before { + content: none; } /* Prevent pale moon from showing the arrow underneath the button */ -#uBlock-legacy-button .toolbarbutton-menu-dropmarker { - -moz-box-orient: horizontal; -}
\ No newline at end of file +#uBlock0-legacy-button .toolbarbutton-menu-dropmarker { + -moz-box-orient: horizontal; +} diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js index 1d759a8..87edc75 100644 --- a/platform/firefox/frameModule.js +++ b/platform/firefox/frameModule.js @@ -331,17 +331,6 @@ const contentObserver = { let docReady = (e) => { let doc = e.target; doc.removeEventListener(e.type, docReady, true); - - if (doc.docShell) { - // It is possible, in some cases (#1140) for document-element-inserted to occur *before* nsIWebProgressListener.onLocationChange, so ensure that the URL is correct before continuing - let messageManager = doc.docShell.getInterface(Ci.nsIContentFrameMessageManager); - - messageManager.sendSyncMessage(locationChangedMessageName, { - url: loc.href, - noRefresh: true, // If the URL is the same, then don't refresh it so that if this occurs after onLocationChange, no the block count isn't reset - }); - } - lss(this.contentBaseURI + 'contentscript-end.js', sandbox); if ( doc.querySelector('a[href^="abp:"]') ) { diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js index 48a6538..0503088 100644 --- a/platform/firefox/vapi-background.js +++ b/platform/firefox/vapi-background.js @@ -60,7 +60,7 @@ vAPI.app.restart = function() { /******************************************************************************/ // Set default preferences for user to find in about:config -vAPI.localStorage.setDefaultBool("forceLegacyToolbarButton", false); +vAPI.localStorage.setDefaultBool('forceLegacyToolbarButton', false); /******************************************************************************/ @@ -70,11 +70,7 @@ vAPI.localStorage.setDefaultBool("forceLegacyToolbarButton", false); var cleanupTasks = []; // This must be updated manually, every time a new task is added/removed - -// Fixed by github.com/AlexVallat: -// https://github.com/AlexVallat/uBlock/commit/7b781248f00cbe3d61b1cc367c440db80fa06049 -// several instances of cleanupTasks.push, but one is unique to fennec, and three to desktop. -var expectedNumberOfCleanups = vAPI.fennec ? 7 : 9; +var expectedNumberOfCleanups = 7; window.addEventListener('unload', function() { if ( typeof vAPI.app.onShutdown === 'function' ) { @@ -484,88 +480,6 @@ vAPI.storage = (function() { /******************************************************************************/ -var windowWatcher = { - onReady: function(e) { - if ( e ) { - this.removeEventListener(e.type, windowWatcher.onReady); - } - - var wintype = this.document.documentElement.getAttribute('windowtype'); - - if ( wintype !== 'navigator:browser' ) { - return; - } - - var attachToTabBrowser = function(window, tabBrowser) { - if (!tabBrowser) { - return; - } - - var tabContainer; - if ( tabBrowser.deck ) { - // Fennec - tabContainer = tabBrowser.deck; - } else if (tabBrowser.tabContainer) { - // desktop Firefox - tabContainer = tabBrowser.tabContainer; - vAPI.contextMenu.register(window.document); - if (vAPI.toolbarButton.attachToNewWindow) { - vAPI.toolbarButton.attachToNewWindow(window); - } - } else { - return; - } - - tabContainer.addEventListener('TabClose', tabWatcher.onTabClose); - tabContainer.addEventListener('TabSelect', tabWatcher.onTabSelect); - // when new window is opened TabSelect doesn't run on the selected tab? - } - - var win = this; - var tabBrowser = getTabBrowser(win); - if ( !tabBrowser ) { - // On some platforms, the tab browser isn't immediately available, try waiting a bit - win.setTimeout(function() { - attachToTabBrowser(win, getTabBrowser(win)); - }, 250); - } else { - attachToTabBrowser(win, tabBrowser); - } - - }, - - observe: function(win, topic) { - if ( topic === 'domwindowopened' ) { - win.addEventListener('DOMContentLoaded', this.onReady); - } - } -}; - -/******************************************************************************/ - -var tabWatcher = { - onTabClose: function({target}) { - // target is tab in Firefox, browser in Fennec - var tabId = vAPI.tabs.getTabId(target); - vAPI.tabs.onClosed(tabId); - delete vAPI.toolbarButton.tabs[tabId]; - }, - - onTabSelect: function({target}) { - vAPI.setIcon(vAPI.tabs.getTabId(target), getOwnerWindow(target)); - }, -}; - -/******************************************************************************/ - -vAPI.isBehindTheSceneTabId = function(tabId) { - return tabId.toString() === '-1'; -}; - -vAPI.noTabId = '-1'; - -/******************************************************************************/ - var getTabBrowser = function(win) { return vAPI.fennec && win.BrowserApp || win.gBrowser || null; }; @@ -643,12 +557,8 @@ vAPI.tabs.get = function(tabId, callback) { var browser; if ( tabId === null ) { - win = Services.wm.getMostRecentWindow('navigator:browser'); - var tabBrowser = getTabBrowser(win); - if (tabBrowser) { - tab = tabBrowser.selectedTab; - tabId = this.getTabId(tab); - } + browser = tabWatcher.currentBrowser(); + tabId = tabWatcher.tabIdFromTarget(browser); } else { browser = tabWatcher.browserFromTabId(tabId); } @@ -1031,38 +941,51 @@ var tabWatcher = (function() { vAPI.setIcon(tabIdFromTarget(target), getOwnerWindow(target)); }; - var onWindowLoad = function(ev) { - if ( ev ) { - this.removeEventListener(ev.type, onWindowLoad); - } - - var wintype = this.document.documentElement.getAttribute('windowtype'); - if ( wintype !== 'navigator:browser' ) { - return; - } - - var tabBrowser = getTabBrowser(this); + var attachToTabBrowser = function(window) { + var tabBrowser = getTabBrowser(window); if ( !tabBrowser ) { - return; + return false; } var tabContainer; - if ( tabBrowser.deck ) { - // Fennec + if ( tabBrowser.deck ) { // Fennec tabContainer = tabBrowser.deck; - } else if ( tabBrowser.tabContainer ) { - // desktop Firefox + } else if ( tabBrowser.tabContainer ) { // Firefox tabContainer = tabBrowser.tabContainer; - vAPI.contextMenu.register(this.document); + vAPI.contextMenu.register(window.document); } else { - return; + return true; + } + + if ( typeof vAPI.toolbarButton.attachToNewWindow === 'function' ) { + vAPI.toolbarButton.attachToNewWindow(window); } + tabContainer.addEventListener('TabOpen', onOpen); tabContainer.addEventListener('TabShow', onShow); tabContainer.addEventListener('TabClose', onClose); + // when new window is opened TabSelect doesn't run on the selected tab? tabContainer.addEventListener('TabSelect', onSelect); - // when new window is opened TabSelect doesn't run on the selected tab? + return true; + }; + + var onWindowLoad = function(ev) { + if ( ev ) { + this.removeEventListener(ev.type, onWindowLoad); + } + + var wintype = this.document.documentElement.getAttribute('windowtype'); + if ( wintype !== 'navigator:browser' ) { + return; + } + + // On some platforms, the tab browser isn't immediately available, + // try waiting a bit if this happens. + var win = this; + if ( attachToTabBrowser(win) === false ) { + vAPI.setTimeout(attachToTabBrowser.bind(null, win), 250); + } }; var onWindowUnload = function() { @@ -1854,6 +1777,7 @@ vAPI.net.registerListeners = function() { }; /******************************************************************************/ +/******************************************************************************/ vAPI.toolbarButton = { id: location.host + '-button', @@ -1861,240 +1785,462 @@ vAPI.toolbarButton = { viewId: location.host + '-panel', label: vAPI.app.name, tooltiptext: vAPI.app.name, - tabs: {/*tabId: {badge: 0, img: boolean}*/} + tabs: {/*tabId: {badge: 0, img: boolean}*/}, + init: null, + codePath: '' }; /******************************************************************************/ -// Toolbar button UI for desktop Firefox -vAPI.toolbarButton.init = function() { - if ( vAPI.fennec ) { - // Menu UI for Fennec - var tb = { - menuItemIds: new WeakMap(), - label: vAPI.app.name, - tabs: {} - }; - vAPI.toolbarButton = tb; +// Fennec - tb.getMenuItemLabel = function(tabId) { - var label = this.label; - if ( tabId === undefined ) { - return label; - } - var tabDetails = this.tabs[tabId]; - if ( !tabDetails ) { - return label; - } - if ( !tabDetails.img ) { - label += ' (' + vAPI.i18n('fennecMenuItemBlockingOff') + ')'; - } else if ( tabDetails.badge ) { - label += ' (' + tabDetails.badge + ')'; - } - return label; - }; +(function() { + if ( !vAPI.fennec ) { + return; + } - tb.onClick = function() { - var win = Services.wm.getMostRecentWindow('navigator:browser'); - var curTabId = tabWatcher.tabIdFromTarget(getTabBrowser(win).selectedTab); - vAPI.tabs.open({ - url: 'popup.html?tabId=' + curTabId, - index: -1, - select: true - }); - }; + var tbb = vAPI.toolbarButton; + + tbb.codePath = 'fennec'; + + var menuItemIds = new WeakMap(); - tb.updateState = function(win, tabId) { - var id = this.menuItemIds.get(win); + var shutdown = function() { + for ( var win of vAPI.tabs.getWindows() ) { + var id = menuItemIds.get(win); if ( !id ) { - return; + continue; } - win.NativeWindow.menu.update(id, { - name: this.getMenuItemLabel(tabId) - }); - }; + win.NativeWindow.menu.remove(id); + menuItemIds.delete(win); + } + }; + + tbb.getMenuItemLabel = function(tabId) { + var label = this.label; + if ( tabId === undefined ) { + return label; + } + var tabDetails = this.tabs[tabId]; + if ( !tabDetails ) { + return label; + } + if ( !tabDetails.img ) { + label += ' (' + vAPI.i18n('fennecMenuItemBlockingOff') + ')'; + } else if ( tabDetails.badge ) { + label += ' (' + tabDetails.badge + ')'; + } + return label; + }; + + tbb.onClick = function() { + var win = Services.wm.getMostRecentWindow('navigator:browser'); + var curTabId = tabWatcher.tabIdFromTarget(getTabBrowser(win).selectedTab); + vAPI.tabs.open({ + url: 'popup.html?tabId=' + curTabId, + index: -1, + select: true + }); + }; + + tbb.updateState = function(win, tabId) { + var id = menuItemIds.get(win); + if ( !id ) { + return; + } + win.NativeWindow.menu.update(id, { + name: this.getMenuItemLabel(tabId) + }); + }; + tbb.init = function() { // Only actually expecting one window under Fennec (note, not tabs, windows) for ( var win of vAPI.tabs.getWindows() ) { - var label = tb.getMenuItemLabel(); + var label = this.getMenuItemLabel(); var id = win.NativeWindow.menu.add({ name: label, - callback: tb.onClick + callback: this.onClick }); - tb.menuItemIds.set(win, id); + menuItemIds.set(win, id); } - cleanupTasks.push(function() { - for ( var win of vAPI.tabs.getWindows() ) { - var id = tb.menuItemIds.get(win); - if ( id ) { - win.NativeWindow.menu.remove(id); - tb.menuItemIds.delete(win); - } - } - }); + cleanupTasks.push(shutdown); + }; +})(); + +/******************************************************************************/ +// Non-Fennec: common code paths. + +(function() { + if ( vAPI.fennec ) { return; } - vAPI.messaging.globalMessageManager.addMessageListener( - location.host + ':closePopup', - vAPI.toolbarButton.onPopupCloseRequested - ); + var tbb = vAPI.toolbarButton; - cleanupTasks.push(function() { - vAPI.messaging.globalMessageManager.removeMessageListener( - location.host + ':closePopup', - vAPI.toolbarButton.onPopupCloseRequested - ); - }); + tbb.onViewShowing = function({target}) { + target.firstChild.setAttribute('src', vAPI.getURL('popup.html')); + }; + + tbb.onViewHiding = function({target}) { + target.parentNode.style.maxWidth = ''; + target.firstChild.setAttribute('src', 'about:blank'); + }; + + tbb.updateState = function(win, tabId) { + var button = win.document.getElementById(this.id); + + if ( !button ) { + return; + } + + var icon = this.tabs[tabId]; + + button.setAttribute('badge', icon && icon.badge || ''); + button.classList.toggle('off', !icon || !icon.img); + }; + + tbb.populatePanel = function(doc, panel) { + panel.setAttribute('id', this.viewId); + + var iframe = doc.createElement('iframe'); + iframe.setAttribute('type', 'content'); + + panel.appendChild(iframe); + + var resizeTimer = null; + + var resizePopupDelayed = function(attempts) { + if ( resizeTimer !== null ) { + return; + } + + // Sanity check + attempts = (attempts || 0) + 1; + if ( attempts > 1/*000*/ ) { + console.error('uBlock0> resizePopupDelayed: giving up after too many attempts'); + return; + } + + resizeTimer = vAPI.setTimeout(resizePopup, 10, attempts); + }; + + var resizePopup = function(attempts) { + resizeTimer = null; + var body = iframe.contentDocument.body; + panel.parentNode.style.maxWidth = 'none'; + // https://github.com/chrisaljoudi/uBlock/issues/730 + // Voodoo programming: this recipe works + var toPixelString = pixels => pixels.toString() + 'px'; + + var clientHeight = body.clientHeight; + iframe.style.height = toPixelString(clientHeight); + panel.style.height = toPixelString(clientHeight + (panel.boxObject.height - panel.clientHeight)); + + var clientWidth = body.clientWidth; + iframe.style.width = toPixelString(clientWidth); + panel.style.width = toPixelString(clientWidth + (panel.boxObject.width - panel.clientWidth)); + + if ( iframe.clientHeight !== body.clientHeight || iframe.clientWidth !== body.clientWidth ) { + resizePopupDelayed(attempts); + } + }; + + var onPopupReady = function() { + var win = this.contentWindow; + + if ( !win || win.location.host !== location.host ) { + return; + } + + if ( typeof tbb.onBeforePopupReady === 'function' ) { + tbb.onBeforePopupReady.call(this); + } - var CustomizableUI; + new win.MutationObserver(resizePopupDelayed).observe(win.document.body, { + attributes: true, + characterData: true, + subtree: true + }); - var forceLegacyToolbarButton = vAPI.localStorage.getBool("forceLegacyToolbarButton"); - if (!forceLegacyToolbarButton) { + resizePopupDelayed(); + }; + + iframe.addEventListener('load', onPopupReady, true); + }; +})(); + +/******************************************************************************/ + +// Firefox 28 and less + +(function() { + var tbb = vAPI.toolbarButton; + if ( tbb.init !== null ) { + return; + } + var CustomizableUI = null; + var forceLegacyToolbarButton = vAPI.localStorage.getBool('forceLegacyToolbarButton'); + if ( !forceLegacyToolbarButton ) { try { CustomizableUI = Cu.import('resource:///modules/CustomizableUI.jsm', null).CustomizableUI; } catch (ex) { } } + if ( CustomizableUI !== null ) { + return; + } - if (!CustomizableUI) { - // Create a fallback non-customizable UI button - var sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService); - var styleSheetUri = Services.io.newURI(vAPI.getURL("css/legacy-toolbar-button.css"), null, null); - var legacyButtonId = "uBlock-legacy-button"; // NOTE: must match legacy-toolbar-button.css - this.id = legacyButtonId; - this.viewId = legacyButtonId + "-panel"; - - if (!sss.sheetRegistered(styleSheetUri, sss.AUTHOR_SHEET)) { - sss.loadAndRegisterSheet(styleSheetUri, sss.AUTHOR_SHEET); // Register global so it works in all windows, including palette - } - - var addLegacyToolbarButton = function(window) { - var document = window.document; - var toolbox = document.getElementById('navigator-toolbox') || document.getElementById('mail-toolbox'); - - if (toolbox) { - var palette = toolbox.palette; - - if (!palette) { - // palette might take a little longer to appear on some platforms, give it a small delay and try again - window.setTimeout(function() { - if (toolbox.palette) { - addLegacyToolbarButton(window); - } - }, 250); - return; - } + tbb.codePath = 'legacy'; + tbb.id = 'uBlock0-legacy-button'; // NOTE: must match legacy-toolbar-button.css + tbb.viewId = tbb.id + '-panel'; - var toolbarButton = document.createElement('toolbarbutton'); - toolbarButton.setAttribute('id', legacyButtonId); - toolbarButton.setAttribute('type', 'menu'); // type = panel would be more accurate, but doesn't look as good - toolbarButton.setAttribute('removable', 'true'); - toolbarButton.setAttribute('class', 'toolbarbutton-1 chromeclass-toolbar-additional'); - toolbarButton.setAttribute('label', vAPI.toolbarButton.label); - - var toolbarButtonPanel = document.createElement("panel"); - // toolbarButtonPanel.setAttribute('level', 'parent'); NOTE: Setting level to parent breaks the popup for PaleMoon under linux (mouse pointer misaligned with content). For some reason. - vAPI.toolbarButton.populatePanel(document, toolbarButtonPanel); - toolbarButtonPanel.addEventListener('popupshowing', vAPI.toolbarButton.onViewShowing); - toolbarButtonPanel.addEventListener('popuphiding', vAPI.toolbarButton.onViewHiding); - toolbarButton.appendChild(toolbarButtonPanel); - - palette.appendChild(toolbarButton); - - vAPI.toolbarButton.closePopup = function() { - toolbarButtonPanel.hidePopup(); - } + var sss = null; + var styleSheetUri = null; - if (!vAPI.localStorage.getBool('legacyToolbarButtonAdded')) { - // No button yet so give it a default location. If forcing the button, just put in in the palette rather than on any specific toolbar (who knows what toolbars will be available or visible!) - var toolbar = !forceLegacyToolbarButton && document.getElementById('nav-bar'); - if (toolbar) { - toolbar.appendChild(toolbarButton); - toolbar.setAttribute('currentset', toolbar.currentSet); - document.persist(toolbar.id, 'currentset'); - } - vAPI.localStorage.setBool('legacyToolbarButtonAdded', 'true'); - } else { - // Find the place to put the button - var toolbars = toolbox.externalToolbars.slice(); - for (var child of toolbox.children) { - if (child.localName === 'toolbar') { - toolbars.push(child); - } - } + var addLegacyToolbarButton = function(window) { + var document = window.document; - for (var toolbar of toolbars) { - var currentsetString = toolbar.getAttribute('currentset'); - if (currentsetString) { - var currentset = currentsetString.split(','); - var index = currentset.indexOf(legacyButtonId); - if (index >= 0) { - // Found our button on this toolbar - but where on it? - var before = null; - for (var i = index + 1; i < currentset.length; i++) { - before = document.getElementById(currentset[i]); - if (before) { - toolbar.insertItem(legacyButtonId, before); - break; - } - } - if (!before) { - toolbar.insertItem(legacyButtonId); - } - } - } - } + var toolbox = document.getElementById('navigator-toolbox') || document.getElementById('mail-toolbox'); + if ( !toolbox ) { + return; + } + + // palette might take a little longer to appear on some platforms, + // give it a small delay and try again. + var palette = toolbox.palette; + if ( !palette ) { + vAPI.setTimeout(function() { + if ( toolbox.palette ) { + addLegacyToolbarButton(window); } - } + }, 250); + return; } + + var toolbarButton = document.createElement('toolbarbutton'); + toolbarButton.setAttribute('id', tbb.id); + // type = panel would be more accurate, but doesn't look as good + toolbarButton.setAttribute('type', 'menu'); + toolbarButton.setAttribute('removable', 'true'); + toolbarButton.setAttribute('class', 'toolbarbutton-1 chromeclass-toolbar-additional'); + toolbarButton.setAttribute('label', tbb.label); + + var toolbarButtonPanel = document.createElement("panel"); + // NOTE: Setting level to parent breaks the popup for PaleMoon under + // linux (mouse pointer misaligned with content). For some reason. + // toolbarButtonPanel.setAttribute('level', 'parent'); + tbb.populatePanel(document, toolbarButtonPanel); + toolbarButtonPanel.addEventListener('popupshowing', tbb.onViewShowing); + toolbarButtonPanel.addEventListener('popuphiding', tbb.onViewHiding); + toolbarButton.appendChild(toolbarButtonPanel); - vAPI.toolbarButton.attachToNewWindow = function(win) { - addLegacyToolbarButton(win); + palette.appendChild(toolbarButton); + + tbb.closePopup = function() { + toolbarButtonPanel.hidePopup(); + }; + + // No button yet so give it a default location. If forcing the button, + // just put in in the palette rather than on any specific toolbar (who + // knows what toolbars will be available or visible!) + var toolbar; + if ( !vAPI.localStorage.getBool('legacyToolbarButtonAdded') ) { + toolbar = document.getElementById('nav-bar'); + if ( toolbar ) { + toolbar.appendChild(toolbarButton); + toolbar.setAttribute('currentset', toolbar.currentSet); + document.persist(toolbar.id, 'currentset'); + } + vAPI.localStorage.setBool('legacyToolbarButtonAdded', 'true'); + return; + } + + // Find the place to put the button + var toolbars = toolbox.externalToolbars.slice(); + for ( var child of toolbox.children ) { + if ( child.localName === 'toolbar' ) { + toolbars.push(child); + } } - cleanupTasks.push(function() { - for ( var win of vAPI.tabs.getWindows() ) { - var toolbarButton = win.document.getElementById(legacyButtonId); - if (toolbarButton) { - toolbarButton.parentNode.removeChild(toolbarButton); + for ( toolbar of toolbars ) { + var currentsetString = toolbar.getAttribute('currentset'); + if ( !currentsetString ) { + continue; + } + var currentset = currentsetString.split(','); + var index = currentset.indexOf(tbb.id); + if ( index === -1 ) { + continue; + } + // Found our button on this toolbar - but where on it? + var before = null; + for ( var i = index + 1; i < currentset.length; i++ ) { + before = document.getElementById(currentset[i]); + if ( before === null ) { + continue; } + toolbar.insertItem(tbb.id, before); + break; } + if ( before === null ) { + toolbar.insertItem(tbb.id); + } + } + }; + + var onPopupCloseRequested = function({target}) { + if ( typeof tbb.closePopup === 'function' ) { + tbb.closePopup(target); + } + }; - if (sss.sheetRegistered(styleSheetUri, sss.AUTHOR_SHEET)) { - sss.unregisterSheet(styleSheetUri, sss.AUTHOR_SHEET); + var shutdown = function() { + for ( var win of vAPI.tabs.getWindows() ) { + var toolbarButton = win.document.getElementById(tbb.id); + if ( toolbarButton ) { + toolbarButton.parentNode.removeChild(toolbarButton); } - }.bind(this)); + } + if ( sss === null ) { + return; + } + if ( sss.sheetRegistered(styleSheetUri, sss.AUTHOR_SHEET) ) { + sss.unregisterSheet(styleSheetUri, sss.AUTHOR_SHEET); + } + sss = null; + styleSheetUri = null; + + vAPI.messaging.globalMessageManager.removeMessageListener( + location.host + ':closePopup', + onPopupCloseRequested + ); + }; + + tbb.attachToNewWindow = function(win) { + addLegacyToolbarButton(win); + }; + + tbb.init = function() { + vAPI.messaging.globalMessageManager.addMessageListener( + location.host + ':closePopup', + onPopupCloseRequested + ); + + sss = Cc["@mozilla.org/content/style-sheet-service;1"].getService(Ci.nsIStyleSheetService); + styleSheetUri = Services.io.newURI(vAPI.getURL("css/legacy-toolbar-button.css"), null, null); + + // Register global so it works in all windows, including palette + if ( !sss.sheetRegistered(styleSheetUri, sss.AUTHOR_SHEET) ) { + sss.loadAndRegisterSheet(styleSheetUri, sss.AUTHOR_SHEET); + } + + cleanupTasks.push(shutdown); + }; +})(); + +/******************************************************************************/ + +// Firefox Australis < 36. + +(function() { + var tbb = vAPI.toolbarButton; + if ( tbb.init !== null ) { return; } + if ( Services.vc.compare(Services.appinfo.platformVersion, '36.0') >= 0 ) { + return null; + } + if ( vAPI.localStorage.getBool('forceLegacyToolbarButton') ) { + return null; + } + var CustomizableUI = null; + try { + CustomizableUI = Cu.import('resource:///modules/CustomizableUI.jsm', null).CustomizableUI; + } catch (ex) { + } + if ( CustomizableUI === null ) { + return; + } + tbb.codePath = 'australis'; + tbb.CustomizableUI = CustomizableUI; + tbb.defaultArea = CustomizableUI.AREA_NAVBAR; + + var styleURI = null; + + var onPopupCloseRequested = function({target}) { + if ( typeof tbb.closePopup === 'function' ) { + tbb.closePopup(target); + } + }; + + var shutdown = function() { + CustomizableUI.destroyWidget(tbb.id); + + for ( var win of vAPI.tabs.getWindows() ) { + var panel = win.document.getElementById(tbb.viewId); + panel.parentNode.removeChild(panel); + win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .removeSheet(styleURI, 1); + } + + vAPI.messaging.globalMessageManager.removeMessageListener( + location.host + ':closePopup', + onPopupCloseRequested + ); + }; + + tbb.onBeforeCreated = function(doc) { + var panel = doc.createElement('panelview'); + + this.populatePanel(doc, panel); - this.CustomizableUI = CustomizableUI; - - this.defaultArea = CustomizableUI.AREA_NAVBAR; - this.styleURI = [ - '#' + this.id + '.off {', - 'list-style-image: url(', - vAPI.getURL('img/browsericons/icon16-off.svg'), - ');', - '}', - '#' + this.id + ' {', - 'list-style-image: url(', - vAPI.getURL('img/browsericons/icon16.svg'), - ');', - '}', - '#' + this.viewId + ', #' + this.viewId + ' > iframe {', - 'width: 160px;', - 'height: 290px;', - 'overflow: hidden !important;', - '}' - ]; - - var platformVersion = Services.appinfo.platformVersion; - - if ( Services.vc.compare(platformVersion, '36.0') < 0 ) { - this.styleURI.push( + doc.getElementById('PanelUI-multiView').appendChild(panel); + + doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .loadSheet(styleURI, 1); + }; + + tbb.onBeforePopupReady = function() { + // https://github.com/gorhill/uBlock/issues/83 + // Add `portrait` class if width is constrained. + try { + this.contentDocument.body.classList.toggle( + 'portrait', + CustomizableUI.getWidget(tbb.id).areaType === CustomizableUI.TYPE_MENU_PANEL + ); + } catch (ex) { + /* noop */ + } + }; + + tbb.init = function() { + vAPI.messaging.globalMessageManager.addMessageListener( + location.host + ':closePopup', + onPopupCloseRequested + ); + + var style = [ + '#' + this.id + '.off {', + 'list-style-image: url(', + vAPI.getURL('img/browsericons/icon16-off.svg'), + ');', + '}', + '#' + this.id + ' {', + 'list-style-image: url(', + vAPI.getURL('img/browsericons/icon16.svg'), + ');', + '}', + '#' + this.viewId + ',', + '#' + this.viewId + ' > iframe {', + 'width: 160px;', + 'height: 290px;', + 'overflow: hidden !important;', + '}', '#' + this.id + '[badge]:not([badge=""])::after {', 'position: absolute;', 'margin-left: -16px;', @@ -2106,236 +2252,229 @@ vAPI.toolbarButton.init = function() { 'background: #666;', 'content: attr(badge);', '}' - ); - } else { - this.CUIEvents = {}; - var updateBadge = function() { - var wId = vAPI.toolbarButton.id; - var buttonInPanel = CustomizableUI.getWidget(wId).areaType === CustomizableUI.TYPE_MENU_PANEL; - - for ( var win of vAPI.tabs.getWindows() ) { - var button = win.document.getElementById(wId); - if ( button === null ) { - continue; - } - if ( buttonInPanel ) { - button.classList.remove('badged-button'); - continue; - } - button.classList.add('badged-button'); - } - - if ( buttonInPanel ) { - return; - } + ]; - // Anonymous elements need some time to be reachable - vAPI.setTimeout(this.updateBadgeStyle, 250); - }.bind(this.CUIEvents); + styleURI = Services.io.newURI( + 'data:text/css,' + encodeURIComponent(style.join('')), + null, + null + ); - // https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/CustomizableUI.jsm#Listeners - this.CUIEvents.onCustomizeEnd = updateBadge; - this.CUIEvents.onWidgetAdded = updateBadge; - this.CUIEvents.onWidgetUnderflow = updateBadge; + this.closePopup = function(tabBrowser) { + CustomizableUI.hidePanelForNode( + tabBrowser.ownerDocument.getElementById(this.viewId) + ); + }; - this.CUIEvents.updateBadgeStyle = function() { - var css = [ - 'background: #666', - 'color: #fff' - ].join(';'); + CustomizableUI.createWidget(this); - for ( var win of vAPI.tabs.getWindows() ) { - var button = win.document.getElementById(vAPI.toolbarButton.id); - if ( button === null ) { - continue; - } - var badge = button.ownerDocument.getAnonymousElementByAttribute( - button, - 'class', - 'toolbarbutton-badge' - ); - if ( !badge ) { - return; - } + cleanupTasks.push(shutdown); + }; +})(); - badge.style.cssText = css; - } - }; +/******************************************************************************/ - this.onCreated = function(button) { - button.setAttribute('badge', ''); - vAPI.setTimeout(updateBadge, 250); - }; +// Firefox Australis >= 36. - CustomizableUI.addListener(this.CUIEvents); +(function() { + var tbb = vAPI.toolbarButton; + if ( tbb.init !== null ) { + return; } + if ( Services.vc.compare(Services.appinfo.platformVersion, '36.0') < 0 ) { + return null; + } + if ( vAPI.localStorage.getBool('forceLegacyToolbarButton') ) { + return null; + } + var CustomizableUI = null; + try { + CustomizableUI = Cu.import('resource:///modules/CustomizableUI.jsm', null).CustomizableUI; + } catch (ex) { + } + if ( CustomizableUI === null ) { + return null; + } + tbb.CustomizableUI = CustomizableUI; + tbb.defaultArea = CustomizableUI.AREA_NAVBAR; - this.styleURI = Services.io.newURI( - 'data:text/css,' + encodeURIComponent(this.styleURI.join('')), - null, - null - ); + var CUIEvents = {}; - this.closePopup = function(tabBrowser) { - CustomizableUI.hidePanelForNode( - tabBrowser.ownerDocument.getElementById(vAPI.toolbarButton.viewId) - ); - }; + var badgeCSSRules = [ + 'background: #666', + 'color: #fff' + ].join(';'); - CustomizableUI.createWidget(this); + var updateBadgeStyle = function() { + for ( var win of vAPI.tabs.getWindows() ) { + var button = win.document.getElementById(tbb.id); + if ( button === null ) { + continue; + } + var badge = button.ownerDocument.getAnonymousElementByAttribute( + button, + 'class', + 'toolbarbutton-badge' + ); + if ( !badge ) { + continue; + } - cleanupTasks.push(function() { - if ( this.CUIEvents ) { - CustomizableUI.removeListener(this.CUIEvents); + badge.style.cssText = badgeCSSRules; } + }; + + var updateBadge = function() { + var wId = tbb.id; + var buttonInPanel = CustomizableUI.getWidget(wId).areaType === CustomizableUI.TYPE_MENU_PANEL; - CustomizableUI.destroyWidget(this.id); - for ( var win of vAPI.tabs.getWindows() ) { - var panel = win.document.getElementById(this.viewId); - panel.parentNode.removeChild(panel); - win.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .removeSheet(this.styleURI, 1); + var button = win.document.getElementById(wId); + if ( button === null ) { + continue; + } + if ( buttonInPanel ) { + button.classList.remove('badged-button'); + continue; + } + button.classList.add('badged-button'); } - }.bind(this)); - this.init = null; -}; + if ( buttonInPanel ) { + return; + } -/******************************************************************************/ + // Anonymous elements need some time to be reachable + vAPI.setTimeout(updateBadgeStyle, 250); + }.bind(CUIEvents); -vAPI.toolbarButton.onPopupCloseRequested = function({target}) { - if (vAPI.toolbarButton.closePopup) { - vAPI.toolbarButton.closePopup(target); - } -} + // https://developer.mozilla.org/en-US/docs/Mozilla/JavaScript_code_modules/CustomizableUI.jsm#Listeners + CUIEvents.onCustomizeEnd = updateBadge; + CUIEvents.onWidgetAdded = updateBadge; + CUIEvents.onWidgetUnderflow = updateBadge; -/******************************************************************************/ + var onPopupCloseRequested = function({target}) { + if ( typeof tbb.closePopup === 'function' ) { + tbb.closePopup(target); + } + }; -vAPI.toolbarButton.onBeforeCreated = function(doc) { - var panel = doc.createElement('panelview'); - - vAPI.toolbarButton.populatePanel(doc, panel); + var shutdown = function() { + CustomizableUI.removeListener(CUIEvents); + CustomizableUI.destroyWidget(tbb.id); - doc.getElementById('PanelUI-multiView').appendChild(panel); + for ( var win of vAPI.tabs.getWindows() ) { + var panel = win.document.getElementById(tbb.viewId); + panel.parentNode.removeChild(panel); + win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .removeSheet(styleURI, 1); + } - doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils) - .loadSheet(this.styleURI, 1); -}; -vAPI.toolbarButton.populatePanel = function(doc, panel) { - panel.setAttribute('id', this.viewId); + vAPI.messaging.globalMessageManager.removeMessageListener( + location.host + ':closePopup', + onPopupCloseRequested + ); + }; - var iframe = doc.createElement('iframe'); - iframe.setAttribute('type', 'content'); + var styleURI = null; - panel.appendChild(iframe); + tbb.onBeforeCreated = function(doc) { + var panel = doc.createElement('panelview'); - var updateTimer = null; - var delayedResize = function(attempts) { - if ( updateTimer ) { - return; - } + this.populatePanel(doc, panel); - // Sanity check - attempts = (attempts || 0) + 1; - if (attempts > 1/*000*/) { - debugger; - console.error('uBlock> delayedResize: giving up after too many attempts'); - return; - } + doc.getElementById('PanelUI-multiView').appendChild(panel); - updateTimer = vAPI.setTimeout(resizePopup, 10, attempts); + doc.defaultView.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .loadSheet(styleURI, 1); }; - var resizePopup = function(attempts) { - updateTimer = null; - var body = iframe.contentDocument.body; - panel.parentNode.style.maxWidth = 'none'; - // https://github.com/chrisaljoudi/uBlock/issues/730 - // Voodoo programming: this recipe works - var toPixelString = pixels => pixels.toString() + 'px'; - var clientHeight = body.clientHeight; - iframe.style.height = toPixelString(clientHeight); - panel.style.height = toPixelString(clientHeight + (panel.boxObject.height - panel.clientHeight)); - - var clientWidth = body.clientWidth; - iframe.style.width = toPixelString(clientWidth); - panel.style.width = toPixelString(clientWidth + (panel.boxObject.width - panel.clientWidth)); - - if ( iframe.clientHeight !== body.clientHeight || iframe.clientWidth !== body.clientWidth ) { - delayedResize(attempts); - } + tbb.onCreated = function(button) { + button.setAttribute('badge', ''); + vAPI.setTimeout(updateBadge, 250); }; - var CustomizableUI = this.CustomizableUI; - var onPopupReady = function() { - var win = this.contentWindow; - - if ( !win || win.location.host !== location.host ) { - return; - } - - if (CustomizableUI) { - // https://github.com/gorhill/uBlock/issues/83 - // Add `portrait` class if width is constrained. - try { - iframe.contentDocument.body.classList.toggle( - 'portrait', - CustomizableUI.getWidget(vAPI.toolbarButton.id).areaType === CustomizableUI.TYPE_MENU_PANEL - ); - } catch (ex) { - /* noop */ - } + tbb.onBeforePopupReady = function() { + // https://github.com/gorhill/uBlock/issues/83 + // Add `portrait` class if width is constrained. + try { + this.contentDocument.body.classList.toggle( + 'portrait', + CustomizableUI.getWidget(tbb.id).areaType === CustomizableUI.TYPE_MENU_PANEL + ); + } catch (ex) { + /* noop */ } + }; - new win.MutationObserver(delayedResize).observe(win.document.body, { - attributes: true, - characterData: true, - subtree: true - }); - - delayedResize(); + tbb.closePopup = function(tabBrowser) { + CustomizableUI.hidePanelForNode( + tabBrowser.ownerDocument.getElementById(tbb.viewId) + ); }; - iframe.addEventListener('load', onPopupReady, true); -}; + tbb.init = function() { + vAPI.messaging.globalMessageManager.addMessageListener( + location.host + ':closePopup', + onPopupCloseRequested + ); -/******************************************************************************/ + CustomizableUI.addListener(CUIEvents); + + var style = [ + '#' + this.id + '.off {', + 'list-style-image: url(', + vAPI.getURL('img/browsericons/icon16-off.svg'), + ');', + '}', + '#' + this.id + ' {', + 'list-style-image: url(', + vAPI.getURL('img/browsericons/icon16.svg'), + ');', + '}', + '#' + this.viewId + ',', + '#' + this.viewId + ' > iframe {', + 'width: 160px;', + 'height: 290px;', + 'overflow: hidden !important;', + '}' + ]; -vAPI.toolbarButton.onViewShowing = function({target}) { - target.firstChild.setAttribute('src', vAPI.getURL('popup.html')); -}; + styleURI = Services.io.newURI( + 'data:text/css,' + encodeURIComponent(style.join('')), + null, + null + ); -/******************************************************************************/ + CustomizableUI.createWidget(this); -vAPI.toolbarButton.onViewHiding = function({target}) { - target.parentNode.style.maxWidth = ''; - target.firstChild.setAttribute('src', 'about:blank'); -}; + cleanupTasks.push(shutdown); + }; +})(); /******************************************************************************/ -vAPI.toolbarButton.updateState = function(win, tabId) { - var button = win.document.getElementById(this.id); +// No toolbar button. - if ( !button ) { - return; +(function() { + // Just to ensure the number of cleanup tasks is as expected: toolbar + // button code is one single cleanup task regardless of platform. + if ( vAPI.toolbarButton.init === null ) { + cleanupTasks.push(function(){}); } - - var icon = this.tabs[tabId]; - - button.setAttribute('badge', icon && icon.badge || ''); - button.classList.toggle('off', !icon || !icon.img); -}; +})(); /******************************************************************************/ -vAPI.toolbarButton.init(); +if ( vAPI.toolbarButton.init !== null ) { + vAPI.toolbarButton.init(); +} /******************************************************************************/ +/******************************************************************************/ vAPI.contextMenu = { contextMap: { diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js index 8ca8b88..88b11da 100644 --- a/platform/firefox/vapi-client.js +++ b/platform/firefox/vapi-client.js @@ -38,8 +38,8 @@ vAPI.sessionId = String.fromCharCode(Date.now() % 26 + 97) + /******************************************************************************/ -vAPI.setTimeout = vAPI.setTimeout || function(callback, delay, args) { - return setTimeout(function(args) { callback(args); }, delay, args); +vAPI.setTimeout = vAPI.setTimeout || function(callback, delay, extra) { + return setTimeout(function(a) { callback(a); }, delay, extra); }; /******************************************************************************/ diff --git a/platform/firefox/vapi-common.js b/platform/firefox/vapi-common.js index 2ded85e..9cd21bb 100644 --- a/platform/firefox/vapi-common.js +++ b/platform/firefox/vapi-common.js @@ -40,8 +40,8 @@ var vAPI = self.vAPI = self.vAPI || {}; /******************************************************************************/ -vAPI.setTimeout = vAPI.setTimeout || function(callback, delay, args) { - return setTimeout(function(args) { callback(args); }, delay, args); +vAPI.setTimeout = vAPI.setTimeout || function(callback, delay, extra) { + return setTimeout(function(a) { callback(a); }, delay, extra); }; /******************************************************************************/ @@ -125,14 +125,19 @@ vAPI.closePopup = function() { // background page or auxiliary pages. // This storage is optional, but it is nice to have, for a more polished user // experience. -const branchName = 'extensions.' + location.host + '.'; + vAPI.localStorage = { - PB: Services.prefs.getBranch(branchName), + pbName: '', + pb: null, str: Components.classes['@mozilla.org/supports-string;1'] - .createInstance(Components.interfaces.nsISupportsString), + .createInstance(Components.interfaces.nsISupportsString), + init: function(pbName) { + this.pbName = pbName; + this.pb = Services.prefs.getBranch(pbName); + }, getItem: function(key) { try { - return this.PB.getComplexValue( + return this.pb.getComplexValue( key, Components.interfaces.nsISupportsString ).data; @@ -142,7 +147,7 @@ vAPI.localStorage = { }, setItem: function(key, value) { this.str.data = value; - this.PB.setComplexValue( + this.pb.setComplexValue( key, Components.interfaces.nsISupportsString, this.str @@ -150,25 +155,27 @@ vAPI.localStorage = { }, getBool: function(key) { try { - return this.PB.getBoolPref(key); + return this.pb.getBoolPref(key); } catch (ex) { return null; } }, setBool: function(key, value) { - this.PB.setBoolPref(key, value); + this.pb.setBoolPref(key, value); }, setDefaultBool: function(key, defaultValue) { - Services.prefs.getDefaultBranch(branchName).setBoolPref(key, defaultValue); + Services.prefs.getDefaultBranch(this.pbName).setBoolPref(key, defaultValue); }, removeItem: function(key) { - this.PB.clearUserPref(key); + this.pb.clearUserPref(key); }, clear: function() { - this.PB.deleteBranch(''); + this.pb.deleteBranch(''); } }; +vAPI.localStorage.init('extensions.' + location.host + '.'); + /******************************************************************************/ })(); diff --git a/src/js/scriptlets/element-picker.js b/src/js/scriptlets/element-picker.js index cad0c4a..24e878c 100644 --- a/src/js/scriptlets/element-picker.js +++ b/src/js/scriptlets/element-picker.js @@ -952,6 +952,9 @@ var startPicker = function(details) { showDialog({ modifier: true }); return; } + + // A target was specified, but it wasn't found: abort. + stopPicker(); }; /******************************************************************************/ diff --git a/tools/make-firefox.sh b/tools/make-firefox.sh index 85beb60..e5da0e7 100755 --- a/tools/make-firefox.sh +++ b/tools/make-firefox.sh @@ -17,6 +17,7 @@ cp -R src/lib $DES/ cp -R src/_locales $DES/ cp src/*.html $DES/ mv $DES/img/icon_128.png $DES/icon.png +cp platform/firefox/css/* $DES/css/ cp platform/firefox/vapi-*.js $DES/js/ cp platform/firefox/bootstrap.js $DES/ cp platform/firefox/frame*.js $DES/ |