| Index: lib/ui.js |
| =================================================================== |
| --- a/lib/ui.js |
| +++ b/lib/ui.js |
| @@ -26,16 +26,27 @@ let {FilterStorage} = require("filterSto |
| let {FilterNotifier} = require("filterNotifier"); |
| let {RequestNotifier} = require("requestNotifier"); |
| let {Filter} = require("filterClasses"); |
| let {Subscription, SpecialSubscription, DownloadableSubscription} = require("subscriptionClasses"); |
| let {Synchronizer} = require("synchronizer"); |
| let {KeySelector} = require("keySelector"); |
| let {Notification} = require("notification"); |
| +let CustomizableUI; |
| +try |
| +{ |
| + ({CustomizableUI}) = Cu.import("resource:///modules/CustomizableUI.jsm", null); |
| +} |
| +catch (e) |
| +{ |
| + // No built-in CustomizableUI API, use our own implementation. |
| + ({CustomizableUI}) = require("customizableUI"); |
| +} |
| + |
| /** |
| * Filter corresponding with "disable on site" menu item (set in fillIconMent()). |
| * @type Filter |
| */ |
| let siteWhitelist = null; |
| /** |
| * Filter corresponding with "disable on site" menu item (set in fillIconMenu()). |
| * @type Filter |
| @@ -92,21 +103,19 @@ let optionsObserver = |
| addCommandHandler("adblockplus-filters", UI.openFiltersDialog.bind(UI)); |
| let {Sync} = require("sync"); |
| let syncEngine = Sync.getEngine(); |
| hideElement("adblockplus-sync", !syncEngine); |
| let {defaultToolbarPosition, statusbarPosition} = require("appSupport"); |
| - let hasToolbar = defaultToolbarPosition && !defaultToolbarPosition.isAddonBar; |
| - let hasAddonBar = defaultToolbarPosition && defaultToolbarPosition.isAddonBar; |
| + let hasToolbar = defaultToolbarPosition; |
| let hasStatusBar = statusbarPosition; |
| - hideElement("adblockplus-showinaddonbar", !hasAddonBar); |
| hideElement("adblockplus-showintoolbar", !hasToolbar); |
| hideElement("adblockplus-showinstatusbar", !hasStatusBar); |
| let checkbox = doc.querySelector("setting[type=bool]"); |
| if (checkbox) |
| initCheckboxes(); |
| function initCheckboxes() |
| @@ -133,33 +142,22 @@ let optionsObserver = |
| }); |
| setChecked("adblockplus-sync", syncEngine && syncEngine.enabled); |
| addCommandHandler("adblockplus-sync", function() |
| { |
| this.value = UI.toggleSync(); |
| }); |
| - let window = null; |
| - for (window in UI.applicationWindows) |
| - break; |
| - |
| - if (window) |
| + setChecked("adblockplus-showintoolbar", UI.isToolbarIconVisible()); |
| + addCommandHandler("adblockplus-showintoolbar", function() |
| { |
| - setChecked("adblockplus-showinaddonbar", UI.isToolbarIconVisible(window)); |
| - setChecked("adblockplus-showintoolbar", UI.isToolbarIconVisible(window)); |
| - |
| - let handler = function() |
| - { |
| - UI.toggleToolbarIcon(); |
| - this.value = UI.isToolbarIconVisible(window); |
| - }; |
| - addCommandHandler("adblockplus-showinaddonbar", handler); |
| - addCommandHandler("adblockplus-showintoolbar", handler); |
| - } |
| + UI.toggleToolbarIcon(); |
| + this.value = UI.isToolbarIconVisible(); |
| + }); |
| let list = doc.getElementById("adblockplus-subscription-list"); |
| if (list) |
| { |
| // Load subscriptions data |
| let request = new XMLHttpRequest(); |
| request.mozBackgroundRequest = true; |
| request.open("GET", "chrome://adblockplus/content/ui/subscriptions.xml"); |
| @@ -314,19 +312,17 @@ let UI = exports.UI = |
| this.initDone(); |
| }.bind(this); |
| FilterNotifier.addListener(listener); |
| } |
| else |
| filtersLoaded = true; |
| // Initialize UI after the session is restored |
| - let window = null; |
| - for (window in this.applicationWindows) |
| - break; |
| + let window = this.currentWindow; |
| if (!window && "nsISessionStore" in Ci) |
| { |
| // No application windows yet, the application must be starting up. Wait |
| // for session to be restored before initializing our UI. |
| new SessionRestoreObserver(function() |
| { |
| sessionRestored = true; |
| if (overlayLoaded && filtersLoaded && sessionRestored) |
| @@ -406,16 +402,45 @@ let UI = exports.UI = |
| * Gets called once the initialization is finished and Adblock Plus elements |
| * can be added to the UI. |
| */ |
| initDone: function() |
| { |
| let {WindowObserver} = require("windowObserver"); |
| new WindowObserver(this); |
| + // Add toolbar icon |
| + let {defaultToolbarPosition} = require("appSupport"); |
| + if ("abp-toolbarbutton" in this.overlay && defaultToolbarPosition) |
| + { |
| + CustomizableUI.createWidget({ |
| + id: "abp-toolbarbutton", |
| + positionAttribute: "abp-iconposition", |
| + defaultArea: defaultToolbarPosition.parent, |
| + defaultBefore: defaultToolbarPosition.before, |
| + defaultAfter: defaultToolbarPosition.after, |
| + removable: true, |
| + onCreated: function(node) |
| + { |
| + for (let attribute of this.overlay["abp-toolbarbutton"].attributes) |
| + node.setAttribute(attribute.name, attribute.value); |
| + if (!node.firstChild) |
| + for (let child of this.overlay["abp-toolbarbutton"].children) |
| + node.appendChild(child.cloneNode(true)); |
| + if ("addClass" in defaultToolbarPosition) |
| + icon.classList.add(defaultToolbarPosition.addClass); |
| + |
| + this.updateIconState(node.ownerDocument.defaultView, node); |
| + }.bind(this), |
| + onClick: this.onIconClick, |
| + onCommand: this.onIconCommand |
| + }); |
| + onShutdown.add(CustomizableUI.destroyWidget.bind(CustomizableUI, "abp-toolbarbutton")); |
| + } |
| + |
| // Listen for pref and filters changes |
| Prefs.addListener(function(name) |
| { |
| if (name == "enabled" || name == "defaulttoolbaraction" || name == "defaultstatusbaraction") |
| this.updateState(); |
| else if (name == "showinstatusbar") |
| { |
| for (let window in this.applicationWindows) |
| @@ -464,37 +489,16 @@ let UI = exports.UI = |
| Utils.runAsync(this.applyToWindow.bind(this, window, true)); |
| return; |
| } |
| // Add general items to the document |
| for (let i = 0; i < this.overlay.all.length; i++) |
| window.document.documentElement.appendChild(this.overlay.all[i].cloneNode(true)); |
| - // Add toolbar icon |
| - if ("abp-toolbarbutton" in this.overlay) |
| - { |
| - let toolbox = this.getToolbox(window); |
| - if (toolbox) |
| - { |
| - // Insert toolbar button asynchronously, otherwise it will show up |
| - // before our stylesheet loads |
| - Utils.runAsync(function() |
| - { |
| - toolbox.addEventListener("aftercustomization", this.onToolbarCustomization, false); |
| - |
| - let {defaultToolbarPosition} = require("appSupport"); |
| - let icon = this.overlay["abp-toolbarbutton"].cloneNode(true); |
| - if ("addClass" in defaultToolbarPosition) |
| - icon.classList.add(defaultToolbarPosition.addClass); |
| - this.restoreToolbarIcon(toolbox, icon); |
| - }.bind(this)); |
| - } |
| - } |
| - |
| // Add status bar icon |
| this.updateStatusbarIcon(window); |
| // Add tools menu item |
| if ("abp-menuitem" in this.overlay) |
| { |
| let {toolsMenu} = require("appSupport"); |
| let [parent, before] = this.resolveInsertionPoint(window, toolsMenu); |
| @@ -574,29 +578,16 @@ let UI = exports.UI = |
| else |
| { |
| let clone = window.document.getElementById(id); |
| if (clone) |
| clone.parentNode.removeChild(clone); |
| } |
| } |
| - if ("abp-toolbarbutton" in this.overlay) |
| - { |
| - let toolbox = this.getToolbox(window); |
| - if (toolbox) |
| - { |
| - toolbox.removeEventListener("aftercustomization", this.onToolbarCustomization, false); |
| - |
| - let paletteItem = this.getPaletteItem(toolbox); |
| - if (paletteItem) |
| - paletteItem.parentNode.removeChild(paletteItem); |
| - } |
| - } |
| - |
| window.removeEventListener("popupshowing", this.onPopupShowing, false); |
| window.removeEventListener("keypress", this.onKeyPress, false); |
| removeBrowserLocationListeners(window); |
| removeBrowserClickListeners(window); |
| }, |
| /** |
| * The overlay information to be used when adding elements to the UI. |
| @@ -622,26 +613,36 @@ let UI = exports.UI = |
| { |
| let window = enumerator.getNext().QueryInterface(Ci.nsIDOMWindow); |
| if (isKnownWindow(window)) |
| yield window; |
| } |
| }, |
| /** |
| + * Returns the top-most application window or null if none exists. |
| + * @type Window |
| + */ |
| + get currentWindow() |
| + { |
| + for (let window of this.applicationWindows) |
| + return window; |
| + return null; |
| + }, |
| + |
| + /** |
| * Opens a URL in the browser window. If browser window isn't passed as parameter, |
| * this function attempts to find a browser window. If an event is passed in |
| * it should be passed in to the browser if possible (will e.g. open a tab in |
| * background depending on modifiers keys). |
| */ |
| loadInBrowser: function(/**String*/ url, /**Window*/ currentWindow, /**Event*/ event) |
| { |
| if (!currentWindow) |
| - for (currentWindow in this.applicationWindows) |
| - break; |
| + currentWindow = this.currentWindow; |
| let {addTab} = require("appSupport"); |
| if (currentWindow && addTab) |
| addTab(currentWindow, url, event); |
| else |
| { |
| let protocolService = Cc["@mozilla.org/uriloader/external-protocol-service;1"].getService(Ci.nsIExternalProtocolService); |
| protocolService.loadURI(Services.io.newURI(url, null, null), null); |
| @@ -946,19 +947,19 @@ let UI = exports.UI = |
| /** |
| * Looks up an element with given ID in the window. If a list of IDs is given |
| * will try all of them until an element exists. |
| */ |
| findElement: function(/**Window*/ window, /**String|String[]*/ id) /**Element*/ |
| { |
| if (id instanceof Array) |
| { |
| - for (let i = 0; i < id.length; i++) |
| + for (let candidate of id) |
| { |
| - let result = window.document.getElementById(id[i]); |
| + let result = window.document.getElementById(candidate); |
| if (result) |
| return result; |
| } |
| return null; |
| } |
| else |
| return window.document.getElementById(id); |
| }, |
| @@ -989,188 +990,26 @@ let UI = exports.UI = |
| if (before && before.parentNode != parent) |
| before = null; |
| } |
| return [parent, before]; |
| }, |
| /** |
| - * Finds the toolbox element responsible for the toolbar where Adblock Plus |
| - * icon should be placed. |
| - */ |
| - getToolbox: function(/**Window*/ window) /**Element*/ |
| - { |
| - let {defaultToolbarPosition} = require("appSupport"); |
| - if (!defaultToolbarPosition || !defaultToolbarPosition.parent) |
| - return null; |
| - |
| - let toolbar = this.findElement(window, defaultToolbarPosition.parent); |
| - if (!toolbar) |
| - return null; |
| - |
| - let toolbox = toolbar.toolbox; |
| - if (toolbox && ("palette" in toolbox) && toolbox.palette) |
| - return toolbox; |
| - else |
| - return null; |
| - }, |
| - |
| - /** |
| - * Restores the Adblock Plus icon at its original position. |
| - */ |
| - restoreToolbarIcon: function(/**Element*/ toolbox, /**Element*/ icon) |
| - { |
| - // Insert into the palette first |
| - toolbox.palette.insertBefore(icon, toolbox.palette.firstChild); |
| - |
| - // Now find where we should put the icon |
| - let position = toolbox.getAttribute("abp-iconposition"); |
| - if (!/^\S*,\S*,\S*$/.test(position)) |
| - position = null; |
| - |
| - if (position == null) |
| - { |
| - // No explicitly saved position but maybe we can find it in a currentset |
| - // attribute somewhere. |
| - let toolbars = toolbox.externalToolbars.slice(); |
| - for (let child = toolbox.firstElementChild; child; child = child.nextElementSibling) |
| - if (child.localName == "toolbar") |
| - toolbars.push(child); |
| - for (let i = 0; i < toolbars.length; i++) |
| - { |
| - let toolbar = toolbars[i]; |
| - let currentSet = toolbar.getAttribute("currentset"); |
| - if (currentSet) |
| - { |
| - let items = currentSet.split(","); |
| - let index = items.indexOf("abp-toolbarbutton"); |
| - if (index >= 0) |
| - { |
| - position = "visible," + toolbar.id + "," + (index + 1 < items.length ? items[index + 1] : ""); |
| - toolbox.setAttribute("abp-iconposition", position); |
| - toolbox.ownerDocument.persist(toolbox.id, "abp-iconposition"); |
| - break; |
| - } |
| - } |
| - } |
| - } |
| - |
| - this.showToolbarIcon(toolbox, position); |
| - }, |
| - |
| - /** |
| - * Finds the toolbar button in the toolbar palette. |
| - */ |
| - getPaletteItem: function(/**Element*/ toolbox) /**Element*/ |
| - { |
| - for (var child = toolbox.palette.firstElementChild; child; child = child.nextElementSibling) |
| - if (child.id == "abp-toolbarbutton") |
| - return child; |
| - |
| - return null; |
| - }, |
| - |
| - /** |
| - * Called after toolbar customization, sets up our icon and remembers its |
| - * position. |
| - */ |
| - onToolbarCustomization: function(/**Event*/ event) |
| - { |
| - let toolbox = event.currentTarget; |
| - let icon = toolbox.ownerDocument.getElementById("abp-toolbarbutton"); |
| - |
| - let position = toolbox.getAttribute("abp-iconposition") || "hidden,,"; |
| - if (icon && icon.parentNode.localName == "toolbar") |
| - { |
| - this.updateIconState(icon.ownerDocument.defaultView, icon); |
| - icon.addEventListener("click", this.onIconClick, false); |
| - icon.addEventListener("command", this.onIconClick, false); |
| - position = "visible," + icon.parentNode.id + "," + (icon.nextSibling ? icon.nextSibling.id : ""); |
| - } |
| - else |
| - position = position.replace(/^visible,/, "hidden,") |
| - |
| - toolbox.setAttribute("abp-iconposition", position); |
| - toolbox.ownerDocument.persist(toolbox.id, "abp-iconposition"); |
| - }, |
| - |
| - /** |
| - * Shows or hides toolbar icon based on a remembered position. |
| - */ |
| - showToolbarIcon: function(/**Element*/ toolbox, /**String*/ position) |
| - { |
| - let visible, parent, before; |
| - if (position) |
| - { |
| - [visible, parent, before] = position.split(",", 3); |
| - parent = toolbox.ownerDocument.getElementById(parent); |
| - if (before == "") |
| - before = null; |
| - else |
| - before = toolbox.ownerDocument.getElementById(before); |
| - if (before && before.parentNode != parent) |
| - before = null; |
| - } |
| - else |
| - { |
| - let {defaultToolbarPosition} = require("appSupport"); |
| - visible = "visible"; |
| - [parent, before] = this.resolveInsertionPoint(toolbox.ownerDocument.defaultView, defaultToolbarPosition); |
| - |
| - if (parent && parent.collapsed) |
| - { |
| - // First time we insert the toolbar icon, make sure it is actually visible |
| - parent.setAttribute("collapsed", "false"); |
| - toolbox.ownerDocument.persist(parent.id, "collapsed"); |
| - } |
| - } |
| - |
| - if (parent && parent.localName != "toolbar") |
| - parent = null; |
| - |
| - if (visible != "visible") |
| - { |
| - // Hide icon if it is currently visible |
| - let icon = toolbox.ownerDocument.getElementById("abp-toolbarbutton"); |
| - if (icon && icon.parentNode.localName == "toolbar") |
| - toolbox.palette.appendChild(icon); |
| - } |
| - else if (parent) |
| - { |
| - // Add the icon to the toolbar |
| - let items = parent.currentSet.split(","); |
| - let index = (before ? items.indexOf(before.id) : -1); |
| - if (index < 0) |
| - before = null; |
| - parent.insertItem("abp-toolbarbutton", before, null, false); |
| - } |
| - |
| - this.onToolbarCustomization({currentTarget: toolbox}); |
| - }, |
| - |
| - /** |
| * Toggles visibility state of the toolbar icon. |
| */ |
| toggleToolbarIcon: function() |
| { |
| - for (let window in this.applicationWindows) |
| + if (this.isToolbarIconVisible()) |
| + CustomizableUI.removeWidgetFromArea("abp-toolbarbutton"); |
| + else |
| { |
| - let toolbox = this.getToolbox(window); |
| - if (!toolbox) |
| - continue; |
| - |
| - let position = toolbox.getAttribute("abp-iconposition"); |
| - if (position) |
| - { |
| - let parts = position.split(","); |
| - parts[0] = (parts[0] == "visible" ? "hidden" : "visible"); |
| - position = parts.join(","); |
| - } |
| - this.showToolbarIcon(toolbox, position); |
| + let {defaultToolbarPosition} = require("appSupport"); |
| + CustomizableUI.addWidgetToArea("abp-toolbarbutton", defaultToolbarPosition.parent); |
| } |
| }, |
| /** |
| * Updates Adblock Plus icon state for all windows. |
| */ |
| updateState: function() |
| { |
| @@ -1623,26 +1462,23 @@ let UI = exports.UI = |
| setDisabled("abp-command-sendReport", !location || !Policy.isBlockableScheme(location) || location.scheme == "mailto"); |
| setChecked(prefix + "disabled", !Prefs.enabled); |
| setChecked(prefix + "frameobjects", Prefs.frameobjects); |
| setChecked(prefix + "slowcollapse", !Prefs.fastcollapse); |
| setChecked(prefix + "savestats", Prefs.savestats); |
| let {defaultToolbarPosition, statusbarPosition} = require("appSupport"); |
| - let hasToolbar = defaultToolbarPosition && !defaultToolbarPosition.isAddonBar; |
| - let hasAddonBar = defaultToolbarPosition && defaultToolbarPosition.isAddonBar; |
| + let hasToolbar = defaultToolbarPosition; |
| let hasStatusBar = statusbarPosition; |
| - hideElement(prefix + "showinaddonbar", !hasAddonBar || prefix == "abp-toolbar-"); |
| hideElement(prefix + "showintoolbar", !hasToolbar || prefix == "abp-toolbar-"); |
| hideElement(prefix + "showinstatusbar", !hasStatusBar); |
| - hideElement(prefix + "iconSettingsSeparator", (prefix == "abp-toolbar-" || (!hasAddonBar && !hasToolbar)) && !hasStatusBar); |
| + hideElement(prefix + "iconSettingsSeparator", (prefix == "abp-toolbar-" || !hasToolbar) && !hasStatusBar); |
| - setChecked(prefix + "showinaddonbar", this.isToolbarIconVisible(window)); |
| - setChecked(prefix + "showintoolbar", this.isToolbarIconVisible(window)); |
| + setChecked(prefix + "showintoolbar", this.isToolbarIconVisible()); |
| setChecked(prefix + "showinstatusbar", Prefs.showinstatusbar); |
| let {Sync} = require("sync"); |
| let syncEngine = Sync.getEngine(); |
| hideElement(prefix + "sync", !syncEngine); |
| setChecked(prefix + "sync", syncEngine && syncEngine.enabled); |
| let defAction = (!window.document.popupNode || window.document.popupNode.id == "abp-toolbarbutton" ? |
| @@ -1830,20 +1666,20 @@ let UI = exports.UI = |
| command.doCommand(); |
| } |
| } |
| }, |
| /** |
| * Checks whether the toolbar icon is currently displayed. |
| */ |
| - isToolbarIconVisible: function(/**Window*/ window) |
| + isToolbarIconVisible: function() /**Boolean*/ |
| { |
| - let button = window.document.getElementById("abp-toolbarbutton"); |
| - return (button && button.parentNode && button.parentNode.localName == "toolbar" && !button.parentNode.collapsed); |
| + let placement = CustomizableUI.getPlacementOfWidget("abp-toolbarbutton"); |
| + return !!placement; |
| }, |
| /** |
| * Stores the selected hotkeys, initialized when the user presses a key. |
| */ |
| hotkeys: null, |
| /** |
| @@ -1923,22 +1759,19 @@ let UI = exports.UI = |
| for each (let id in ["abp-status-contributebutton", "abp-toolbar-contributebutton", "abp-menuitem-contributebutton"]) |
| { |
| let button = window.document.getElementById(id); |
| if (button) |
| button.hidden = true; |
| } |
| }, |
| - _showNextNotification: function(notification) |
| + _showNextNotification: function() |
| { |
| - let window = null; |
| - for (window in this.applicationWindows) |
| - break; |
| - |
| + let window = this.currentWindow; |
| if (!window) |
| return; |
| let button = window.document.getElementById("abp-toolbarbutton") |
| || window.document.getElementById("abp-status"); |
| if (!button) |
| return; |
| @@ -1996,17 +1829,16 @@ let UI = exports.UI = |
| let panel = window.document.getElementById("abp-notification"); |
| panel.openPopup(button, "bottomcenter topcenter", 0, 0, false, false, null); |
| } |
| }; |
| UI.onPopupShowing = UI.onPopupShowing.bind(UI); |
| UI.onKeyPress = UI.onKeyPress.bind(UI); |
| UI.onIconClick = UI.onIconClick.bind(UI); |
| -UI.onToolbarCustomization = UI.onToolbarCustomization.bind(UI); |
| UI.init(); |
| /** |
| * List of event handers to be registered for each window. For each event |
| * handler the element ID, event and the actual event handler are listed. |
| * @type Array |
| */ |
| let eventHandlers = [ |