/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * PMkit shim for 'sdk/ui/button', (c) JustOff, 2017 */ "use strict"; module.metadata = { "stability": "experimental", "engines": { "Palemoon": "> 27" } }; const { Ci, Cc } = require('chrome'); const prefs = require('../preferences/service'); const buttonsList = new Map(); const LOCATION_PREF_ROOT = "extensions.sdk-button-location."; let gWindowListener; function getLocation(id) { let toolbarId = "nav-bar", nextItemId = ""; let location = prefs.get(LOCATION_PREF_ROOT + id); if (location && location.indexOf(",") !== -1) { [toolbarId, nextItemId] = location.split(","); } return [toolbarId, nextItemId]; } function saveLocation(id, toolbarId, nextItemId) { let _toolbarId = toolbarId || ""; let _nextItemId = nextItemId || ""; prefs.set(LOCATION_PREF_ROOT + id, [_toolbarId, _nextItemId].join(",")); } // Insert button into window function insertButton(aWindow, id, onBuild) { // Build button and save reference to it let doc = aWindow.document; let b = onBuild(doc, id); aWindow[id] = b; // Add to the customization palette let toolbox = doc.getElementById("navigator-toolbox"); toolbox.palette.appendChild(b); // Retrieve button location from preferences let [toolbarId, nextItemId] = getLocation(id); let toolbar = toolbarId != "" && doc.getElementById(toolbarId); if (toolbar) { // Handle special items with dynamic ids let match = /^(separator|spacer|spring)\[(\d+)\]$/.exec(nextItemId); if (match !== null) { let dynItems = toolbar.querySelectorAll("toolbar" + match[1]); if (match[2] < dynItems.length) { nextItemId = dynItems[match[2]].id; } } let nextItem = nextItemId != "" && doc.getElementById(nextItemId); // If nextItem not in toolbar then retrieve it by reading currentset attribute if (!(nextItem && nextItem.parentNode && nextItem.parentNode.id == toolbarId)) { nextItem = null; let currentSet = toolbar.getAttribute("currentset"); let ids = (currentSet == "__empty") ? [] : currentSet.split(","); let idx = ids.indexOf(id); if (idx != -1) { for (let i = idx; i < ids.length; i++) { nextItem = doc.getElementById(ids[i]); if (nextItem) break; } } } // Finally insert button in the right toolbar and in the right position toolbar.insertItem(id, nextItem, null, false); } } // Remove button from window function removeButton(aWindow, id) { let b = aWindow[id]; b.parentNode.removeChild(b); delete aWindow[id]; } // Save locations of buttons after customization function afterCustomize(e) { for (let [id] of buttonsList) { let toolbox = e.target; let b = toolbox.parentNode.querySelector("#" + id); let toolbarId = null, nextItem = null, nextItemId = null; if (b) { let parent = b.parentNode; nextItem = b.nextSibling; if (parent && parent.localName == "toolbar") { toolbarId = parent.id; nextItemId = nextItem && nextItem.id; } } // Handle special items with dynamic ids let match = /^(separator|spacer|spring)\d+$/.exec(nextItemId); if (match !== null) { let dynItems = nextItem.parentNode.querySelectorAll("toolbar" + match[1]); for (let i = 0; i < dynItems.length; i++) { if (dynItems[i].id == nextItemId) { nextItemId = match[1] + "[" + i + "]"; break; } } } saveLocation(id, toolbarId, nextItemId); } } // Global window observer function browserWindowObserver(handlers) { this.handlers = handlers; } browserWindowObserver.prototype = { observe: function(aSubject, aTopic, aData) { if (aTopic == "domwindowopened") { aSubject.QueryInterface(Ci.nsIDOMWindow).addEventListener("load", this, false); } else if (aTopic == "domwindowclosed") { if (aSubject.document.documentElement.getAttribute("windowtype") == "navigator:browser") { this.handlers.onShutdown(aSubject); } } }, handleEvent: function(aEvent) { let aWindow = aEvent.currentTarget; aWindow.removeEventListener(aEvent.type, this, false); if (aWindow.document.documentElement.getAttribute("windowtype") == "navigator:browser") { this.handlers.onStartup(aWindow); } } }; // Run on every window startup function browserWindowStartup(aWindow) { for (let [id, onBuild] of buttonsList) { insertButton(aWindow, id, onBuild); } aWindow.addEventListener("aftercustomization", afterCustomize, false); }; // Run on every window shutdown function browserWindowShutdown(aWindow) { for (let [id, onBuild] of buttonsList) { removeButton(aWindow, id); } aWindow.removeEventListener("aftercustomization", afterCustomize, false); } // Main object const buttons = { createButton: function(aProperties) { // If no buttons were inserted yet, setup global window observer if (buttonsList.size == 0) { let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher); gWindowListener = new browserWindowObserver({ onStartup: browserWindowStartup, onShutdown: browserWindowShutdown }); ww.registerNotification(gWindowListener); } // Add button to list buttonsList.set(aProperties.id, aProperties.onBuild); // Inster button to all open windows let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator); let winenu = wm.getEnumerator("navigator:browser"); while (winenu.hasMoreElements()) { let win = winenu.getNext(); insertButton(win, aProperties.id, aProperties.onBuild); // When first button inserted, add afterCustomize listener if (buttonsList.size == 1) { win.addEventListener("aftercustomization", afterCustomize, false); } } }, destroyButton: function(id) { // Remove button from list buttonsList.delete(id); // If no more buttons exist, remove global window observer if (buttonsList.size == 0) { let ww = Cc["@mozilla.org/embedcomp/window-watcher;1"].getService(Ci.nsIWindowWatcher); ww.unregisterNotification(gWindowListener); gWindowListener = null; } // Remove button from all open windows let wm = Cc["@mozilla.org/appshell/window-mediator;1"].getService(Ci.nsIWindowMediator); let winenu = wm.getEnumerator("navigator:browser"); while (winenu.hasMoreElements()) { let win = winenu.getNext(); removeButton(win, id); // If no more buttons exist, remove afterCustomize listener if (buttonsList.size == 0) { win.removeEventListener("aftercustomization", afterCustomize, false); } } }, getNode: function(id, window) { return window[id]; } }; exports.buttons = buttons;