new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-pageActions.js
@@ -0,0 +1,649 @@
+/* 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/. */
+
+var BrowserPageActions = {
+ /**
+ * The main page action button in the urlbar (DOM node)
+ */
+ get mainButtonNode() {
+ delete this.mainButtonNode;
+ return this.mainButtonNode = document.getElementById("pageActionButton");
+ },
+
+ /**
+ * The main page action panel DOM node (DOM node)
+ */
+ get panelNode() {
+ delete this.panelNode;
+ return this.panelNode = document.getElementById("pageActionPanel");
+ },
+
+ /**
+ * The photonmultiview node in the main page action panel (DOM node)
+ */
+ get multiViewNode() {
+ delete this.multiViewNode;
+ return this.multiViewNode = document.getElementById("pageActionPanelMultiView");
+ },
+
+ /**
+ * The main panelview node in the main page action panel (DOM node)
+ */
+ get mainViewNode() {
+ delete this.mainViewNode;
+ return this.mainViewNode = document.getElementById("pageActionPanelMainView");
+ },
+
+ /**
+ * The vbox body node in the main panelview node (DOM node)
+ */
+ get mainViewBodyNode() {
+ delete this.mainViewBodyNode;
+ return this.mainViewBodyNode = this.mainViewNode.querySelector(".panel-subview-body");
+ },
+
+ /**
+ * Inits. Call to init.
+ */
+ init() {
+ for (let action of PageActions.actions) {
+ this.placeAction(action, PageActions.insertBeforeActionIDInUrlbar(action));
+ }
+ },
+
+ /**
+ * Adds or removes as necessary DOM nodes for the given action.
+ *
+ * @param action (PageActions.Action, required)
+ * The action to place.
+ * @param panelInsertBeforeID (string, required)
+ * The ID of the action in the panel before which the given action
+ * action should be inserted.
+ * @param urlbarInsertBeforeID (string, required)
+ * If the action is shown in the urlbar, then this is ID of the action
+ * in the urlbar before which the given action should be inserted.
+ */
+ placeAction(action, panelInsertBeforeID, urlbarInsertBeforeID) {
+ if (action.__isSeparator) {
+ this._appendPanelSeparator(action);
+ return;
+ }
+ this.placeActionInPanel(action, panelInsertBeforeID);
+ this.placeActionInUrlbar(action, urlbarInsertBeforeID);
+ },
+
+ /**
+ * Adds or removes as necessary DOM nodes for the action in the panel.
+ *
+ * @param action (PageActions.Action, required)
+ * The action to place.
+ * @param insertBeforeID (string, required)
+ * The ID of the action in the panel before which the given action
+ * action should be inserted.
+ */
+ placeActionInPanel(action, insertBeforeID) {
+ let id = this._panelButtonNodeIDForActionID(action.id);
+ let node = document.getElementById(id);
+ if (!node) {
+ let panelViewNode;
+ [node, panelViewNode] = this._makePanelButtonNodeForAction(action);
+ node.id = id;
+ let insertBeforeNode = null;
+ if (insertBeforeID) {
+ let insertBeforeNodeID =
+ this._panelButtonNodeIDForActionID(insertBeforeID);
+ insertBeforeNode = document.getElementById(insertBeforeNodeID);
+ }
+ this.mainViewBodyNode.insertBefore(node, insertBeforeNode);
+ action.onPlacedInPanel(node);
+ if (panelViewNode) {
+ action.subview.onPlaced(panelViewNode);
+ }
+ }
+ return node;
+ },
+
+ _makePanelButtonNodeForAction(action) {
+ let buttonNode = document.createElement("toolbarbutton");
+ buttonNode.classList.add(
+ "subviewbutton",
+ "subviewbutton-iconic",
+ "pageAction-panel-button"
+ );
+ buttonNode.setAttribute("label", action.title);
+ if (action.iconURL) {
+ buttonNode.style.listStyleImage = `url('${action.iconURL}')`;
+ }
+ if (action.nodeAttributes) {
+ for (let name in action.nodeAttributes) {
+ buttonNode.setAttribute(name, action.nodeAttributes[name]);
+ }
+ }
+ let panelViewNode = null;
+ if (action.subview) {
+ buttonNode.classList.add("subviewbutton-nav");
+ panelViewNode = this._makePanelViewNodeForAction(action, false);
+ this.multiViewNode.appendChild(panelViewNode);
+ }
+ buttonNode.addEventListener("command", event => {
+ if (panelViewNode) {
+ action.subview.onShowing(panelViewNode);
+ this.multiViewNode.showSubView(panelViewNode, buttonNode);
+ return;
+ }
+ if (action.wantsIframe) {
+ this._toggleTempPanelForAction(action);
+ return;
+ }
+ this.panelNode.hidePopup();
+ action.onCommand(event, buttonNode);
+ });
+ return [buttonNode, panelViewNode];
+ },
+
+ _makePanelViewNodeForAction(action, forUrlbar) {
+ let panelViewNode = document.createElement("panelview");
+ let placementID = forUrlbar ? "urlbar" : "panel";
+ panelViewNode.id = `pageAction-${placementID}-${action.id}-subview`;
+ panelViewNode.classList.add("PanelUI-subView");
+ let bodyNode = document.createElement("vbox");
+ bodyNode.id = panelViewNode.id + "-body";
+ bodyNode.classList.add("panel-subview-body");
+ panelViewNode.appendChild(bodyNode);
+ for (let button of action.subview.buttons) {
+ let buttonNode = document.createElement("toolbarbutton");
+ let buttonNodeID =
+ forUrlbar ? this._urlbarButtonNodeIDForActionID(action.id) :
+ this._panelButtonNodeIDForActionID(action.id);
+ buttonNodeID += "-" + button.id;
+ buttonNode.id = buttonNodeID;
+ buttonNode.classList.add("subviewbutton", "subviewbutton-iconic");
+ buttonNode.setAttribute("label", button.title);
+ if (button.shortcut) {
+ buttonNode.setAttribute("shortcut", button.shortcut);
+ }
+ if (button.disabled) {
+ buttonNode.setAttribute("disabled", "true");
+ }
+ buttonNode.addEventListener("command", event => {
+ button.onCommand(event, buttonNode);
+ });
+ bodyNode.appendChild(buttonNode);
+ }
+ return panelViewNode;
+ },
+
+ _toggleTempPanelForAction(action) {
+ let panelNodeID = "pageActionTempPanel";
+ let panelNode = document.getElementById(panelNodeID);
+ if (panelNode) {
+ panelNode.hidePopup();
+ return;
+ }
+
+ panelNode = document.createElement("panel");
+ panelNode.id = panelNodeID;
+ panelNode.classList.add("cui-widget-panel");
+ panelNode.setAttribute("role", "group");
+ panelNode.setAttribute("type", "arrow");
+ panelNode.setAttribute("flip", "slide");
+ panelNode.setAttribute("noautofocus", "true");
+
+ let panelViewNode = null;
+ let iframeNode = null;
+
+ if (action.subview) {
+ let multiViewNode = document.createElement("photonpanelmultiview");
+ panelViewNode = this._makePanelViewNodeForAction(action, true);
+ multiViewNode.appendChild(panelViewNode);
+ panelNode.appendChild(multiViewNode);
+ } else if (action.wantsIframe) {
+ iframeNode = document.createElement("iframe");
+ iframeNode.setAttribute("type", "content");
+ panelNode.appendChild(iframeNode);
+ }
+
+ let popupSet = document.getElementById("mainPopupSet");
+ popupSet.appendChild(panelNode);
+ panelNode.addEventListener("popuphidden", () => {
+ panelNode.remove();
+ }, { once: true });
+
+ if (panelViewNode) {
+ action.subview.onPlaced(panelViewNode);
+ action.subview.onShowing(panelViewNode);
+ }
+
+ this.panelNode.hidePopup();
+
+ let urlbarNodeID = this._urlbarButtonNodeIDForActionID(action.id);
+ let anchorNode =
+ document.getElementById(urlbarNodeID) || this.mainButtonNode;
+ panelNode.openPopup(anchorNode, "bottomcenter topright");
+
+ if (iframeNode) {
+ action.onIframeShown(iframeNode, panelNode);
+ }
+ },
+
+ /**
+ * Adds or removes as necessary a DOM node for the given action in the urlbar.
+ *
+ * @param action (PageActions.Action, required)
+ * The action to place.
+ * @param insertBeforeID (string, required)
+ * If the action is shown in the urlbar, then this is ID of the action
+ * in the urlbar before which the given action should be inserted.
+ */
+ placeActionInUrlbar(action, insertBeforeID) {
+ let id = this._urlbarButtonNodeIDForActionID(action.id);
+ let node = document.getElementById(id);
+
+ if (!action.shownInUrlbar) {
+ if (node) {
+ if (action.__urlbarNodeInMarkup) {
+ node.hidden = true;
+ } else {
+ node.remove();
+ }
+ }
+ return null;
+ }
+
+ let newlyPlaced = false;
+ if (action.__urlbarNodeInMarkup) {
+ newlyPlaced = node && node.hidden;
+ node.hidden = false;
+ } else if (!node) {
+ newlyPlaced = true;
+ node = this._makeUrlbarButtonNode(action);
+ node.id = id;
+ }
+
+ if (newlyPlaced) {
+ let parentNode = this.mainButtonNode.parentNode;
+ let insertBeforeNode = null;
+ if (insertBeforeID) {
+ let insertBeforeNodeID =
+ this._urlbarButtonNodeIDForActionID(insertBeforeID);
+ insertBeforeNode = document.getElementById(insertBeforeNodeID);
+ }
+ parentNode.insertBefore(node, insertBeforeNode);
+ action.onPlacedInUrlbar(node);
+
+ // urlbar buttons should always have tooltips, so if the node doesn't have
+ // one, then as a last resort use the label of the corresponding panel
+ // button. Why not set tooltiptext to action.title when the node is
+ // created? Because the consumer may set a title dynamically.
+ if (!node.hasAttribute("tooltiptext")) {
+ let panelNodeID = this._panelButtonNodeIDForActionID(action.id);
+ let panelNode = document.getElementById(panelNodeID);
+ if (panelNode) {
+ node.setAttribute("tooltiptext", panelNode.getAttribute("label"));
+ }
+ }
+ }
+
+ return node;
+ },
+
+ _makeUrlbarButtonNode(action) {
+ let buttonNode = document.createElement("image");
+ buttonNode.classList.add("urlbar-icon");
+ if (action.tooltip) {
+ buttonNode.setAttribute("tooltiptext", action.tooltip);
+ }
+ if (action.iconURL) {
+ buttonNode.style.listStyleImage = `url('${action.iconURL}')`;
+ }
+ if (action.nodeAttributes) {
+ for (let name in action.nodeAttributes) {
+ buttonNode.setAttribute(name, action.nodeAttributes[name]);
+ }
+ }
+ buttonNode.addEventListener("click", event => {
+ if (event.button != 0) {
+ return;
+ }
+ if (action.subview || action.wantsIframe) {
+ this._toggleTempPanelForAction(action);
+ return;
+ }
+ action.onCommand(event, buttonNode);
+ });
+ return buttonNode;
+ },
+
+ _appendPanelSeparator(action) {
+ let node = document.createElement("toolbarseparator");
+ node.id = this._panelButtonNodeIDForActionID(action.id);
+ this.mainViewBodyNode.appendChild(node);
+ },
+
+ /**
+ * Removes all the DOM nodes of the given action.
+ *
+ * @param action (PageActions.Action, required)
+ * The action to remove.
+ */
+ removeAction(action) {
+ this._removeActionFromPanel(action);
+ this._removeActionFromUrlbar(action);
+ },
+
+ _removeActionFromPanel(action) {
+ let id = this._panelButtonNodeIDForActionID(action.id);
+ let node = document.getElementById(id);
+ if (node) {
+ node.remove();
+ }
+ if (action.subview) {
+ let panelViewNodeID = this._panelViewNodeIDFromActionID(action.id);
+ let panelViewNode = document.getElementById(panelViewNodeID);
+ if (panelViewNode) {
+ panelViewNode.remove();
+ }
+ }
+ },
+
+ _removeActionFromUrlbar(action) {
+ let id = this._urlbarButtonNodeIDForActionID(action.id);
+ let node = document.getElementById(id);
+ if (node) {
+ node.remove();
+ }
+ },
+
+ /**
+ * Updates the DOM nodes of an action to reflect its changed iconURL.
+ *
+ * @param action (PageActions.Action, required)
+ * The action to update.
+ */
+ updateActionIconURL(action) {
+ let url = action.iconURL ? `url('${action.iconURL}')` : null;
+ let nodeIDs = [
+ this._panelButtonNodeIDForActionID(action.id),
+ this._urlbarButtonNodeIDForActionID(action.id),
+ ];
+ for (let nodeID of nodeIDs) {
+ let node = document.getElementById(nodeID);
+ if (node) {
+ if (url) {
+ node.style.listStyleImage = url;
+ } else {
+ node.style.removeProperty("list-style-image");
+ }
+ }
+ }
+ },
+
+ /**
+ * Updates the DOM nodes of an action to reflect its changed title.
+ *
+ * @param action (PageActions.Action, required)
+ * The action to update.
+ */
+ updateActionTitle(action) {
+ let id = this._panelButtonNodeIDForActionID(action.id);
+ let node = document.getElementById(id);
+ if (node) {
+ node.setAttribute("label", action.title);
+ }
+ },
+
+ /**
+ * Returns the action for a node.
+ *
+ * @param node (DOM node, required)
+ * A button DOM node, either one that's shown in the page action panel
+ * or the urlbar.
+ * @return (PageAction.Action) The node's related action, or null if none.
+ */
+ actionForNode(node) {
+ if (!node) {
+ return null;
+ }
+ let actionID = this._actionIDForNodeID(node.id);
+ return PageActions.actionForID(actionID);
+ },
+
+ _panelButtonNodeIDForActionID(actionID) {
+ return "pageAction-panel-" + actionID;
+ },
+
+ _urlbarButtonNodeIDForActionID(actionID) {
+ let action = PageActions.actionForID(actionID);
+ if (action && action.urlbarIDOverride) {
+ return action.urlbarIDOverride;
+ }
+ return "pageAction-urlbar-" + actionID;
+ },
+
+ _actionIDForNodeID(nodeID) {
+ if (!nodeID) {
+ return null;
+ }
+ let match = nodeID.match(/^pageAction-(?:panel|urlbar)-(.+)$/);
+ return match ? match[1] : null;
+ },
+
+ /**
+ * Call this when the main page action button in the urlbar is activated.
+ *
+ * @param event (DOM event, required)
+ * The click or whatever event.
+ */
+ mainButtonClicked(event) {
+ event.stopPropagation();
+
+ if ((event.type == "click" && event.button != 0) ||
+ (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
+ event.keyCode != KeyEvent.DOM_VK_RETURN)) {
+ return;
+ }
+
+ for (let action of PageActions.actions) {
+ let buttonNodeID = this._panelButtonNodeIDForActionID(action.id);
+ let buttonNode = document.getElementById(buttonNodeID);
+ action.onShowingInPanel(buttonNode);
+ }
+
+ this.panelNode.hidden = false;
+ this.panelNode.openPopup(this.mainButtonNode, {
+ position: "bottomcenter topright",
+ triggerEvent: event,
+ });
+ },
+
+ /**
+ * Call this on the contextmenu event. Note that this is called before
+ * onContextMenuShowing.
+ *
+ * @param event (DOM event, required)
+ * The contextmenu event.
+ */
+ onContextMenu(event) {
+ let node = event.originalTarget;
+ this._contextAction = this.actionForNode(node);
+ },
+
+ /**
+ * Call this on the context menu's popupshowing event.
+ *
+ * @param event (DOM event, required)
+ * The popupshowing event.
+ * @param popup (DOM node, required)
+ * The context menu popup DOM node.
+ */
+ onContextMenuShowing(event, popup) {
+ if (event.target != popup) {
+ return;
+ }
+ // Right now there's only one item in the context menu, to toggle the
+ // context action's shown-in-urlbar state. Update it now.
+ let toggleItem = popup.firstChild;
+ let toggleItemLabel = null;
+ if (this._contextAction) {
+ toggleItem.disabled = false;
+ if (this._contextAction.shownInUrlbar) {
+ toggleItemLabel = toggleItem.getAttribute("remove-label");
+ }
+ }
+ if (!toggleItemLabel) {
+ toggleItemLabel = toggleItem.getAttribute("add-label");
+ }
+ toggleItem.label = toggleItemLabel;
+ },
+
+ /**
+ * Call this from the context menu's toggle menu item.
+ */
+ toggleShownInUrlbarForContextAction() {
+ if (!this._contextAction) {
+ return;
+ }
+ this._contextAction.shownInUrlbar = !this._contextAction.shownInUrlbar;
+ },
+
+ _contextAction: null,
+
+ /**
+ * A bunch of strings (labels for actions and the like) are defined in DTD,
+ * but actions are created in JS. So what we do is add a bunch of attributes
+ * to the page action panel's definition in the markup, whose values hold
+ * these DTD strings. Then when each built-in action is set up, we get the
+ * related strings from the panel node and set up the action's node with them.
+ *
+ * The convention is to set for example the "title" property in an action's JS
+ * definition to the name of the attribute on the panel node that holds the
+ * actual title string. Then call this function, passing the action's related
+ * DOM node and the name of the attribute that you are setting on the DOM
+ * node -- "label" or "title" in this example (either will do).
+ *
+ * @param node (DOM node, required)
+ * The node of an action you're setting up.
+ * @param attrName (string, required)
+ * The name of the attribute *on the node you're setting up*.
+ */
+ takeNodeAttributeFromPanel(node, attrName) {
+ let panelAttrName = node.getAttribute(attrName);
+ if (!panelAttrName && attrName == "title") {
+ attrName = "label";
+ panelAttrName = node.getAttribute(attrName);
+ }
+ if (panelAttrName) {
+ let attrValue = this.panelNode.getAttribute(panelAttrName);
+ if (attrValue) {
+ node.setAttribute(attrName, attrValue);
+ }
+ }
+ },
+};
+
+
+// built-in actions below //////////////////////////////////////////////////////
+
+// bookmark
+BrowserPageActions.bookmark = {
+ onShowingInPanel(buttonNode) {
+ // Update the button label via the bookmark observer.
+ BookmarkingUI.updateBookmarkPageMenuItem();
+ },
+
+ onCommand(event, buttonNode) {
+ BrowserPageActions.panelNode.hidePopup();
+ BookmarkingUI.onStarCommand(event);
+ },
+};
+
+// copy URL
+BrowserPageActions.copyURL = {
+ onPlacedInPanel(buttonNode) {
+ BrowserPageActions.takeNodeAttributeFromPanel(buttonNode, "title");
+ },
+
+ onCommand(event, buttonNode) {
+ BrowserPageActions.panelNode.hidePopup();
+ Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper)
+ .copyString(gBrowser.selectedBrowser.currentURI.spec);
+ },
+};
+
+// email link
+BrowserPageActions.emailLink = {
+ onPlacedInPanel(buttonNode) {
+ BrowserPageActions.takeNodeAttributeFromPanel(buttonNode, "title");
+ },
+
+ onCommand(event, buttonNode) {
+ BrowserPageActions.panelNode.hidePopup();
+ MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);
+ },
+};
+
+// send to device
+BrowserPageActions.sendToDevice = {
+ onPlacedInPanel(buttonNode) {
+ BrowserPageActions.takeNodeAttributeFromPanel(buttonNode, "title");
+ },
+
+ onSubviewPlaced(panelViewNode) {
+ let bodyNode = panelViewNode.firstChild;
+ for (let node of bodyNode.childNodes) {
+ BrowserPageActions.takeNodeAttributeFromPanel(node, "title");
+ BrowserPageActions.takeNodeAttributeFromPanel(node, "shortcut");
+ }
+ },
+
+ onShowingInPanel(buttonNode) {
+ let browser = gBrowser.selectedBrowser;
+ let url = browser.currentURI.spec;
+ if (gSync.isSendableURI(url)) {
+ buttonNode.removeAttribute("disabled");
+ } else {
+ buttonNode.setAttribute("disabled", "true");
+ }
+ },
+
+ onShowingSubview(panelViewNode) {
+ let browser = gBrowser.selectedBrowser;
+ let url = browser.currentURI.spec;
+ let title = browser.contentTitle;
+
+ let bodyNode = panelViewNode.firstChild;
+
+ // This is on top because it also clears the device list between state
+ // changes.
+ gSync.populateSendTabToDevicesMenu(bodyNode, url, title, (clientId, name, clientType) => {
+ if (!name) {
+ return document.createElement("toolbarseparator");
+ }
+ let item = document.createElement("toolbarbutton");
+ item.classList.add("pageAction-sendToDevice-device", "subviewbutton");
+ if (clientId) {
+ item.classList.add("subviewbutton-iconic");
+ }
+ item.setAttribute("tooltiptext", name);
+ return item;
+ });
+
+ bodyNode.removeAttribute("state");
+ // In the first ~10 sec after startup, Sync may not be loaded and the list
+ // of devices will be empty.
+ if (gSync.syncConfiguredAndLoading) {
+ bodyNode.setAttribute("state", "notready");
+ // Force a background Sync
+ Services.tm.dispatchToMainThread(async () => {
+ await Weave.Service.sync([]); // [] = clients engine only
+ // There's no way Sync is still syncing at this point, but we check
+ // anyway to avoid infinite looping.
+ if (!window.closed && !gSync.syncConfiguredAndLoading) {
+ this.onShowingSubview(panelViewNode);
+ }
+ });
+ }
+ },
+};
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -36,17 +36,17 @@ XPCOMUtils.defineLazyPreferenceGetter(th
ReaderParent:false, RecentWindow:false, SafeBrowsing: false,
SessionStore:false,
ShortcutUtils:false, SimpleServiceDiscovery:false, SitePermissions:false,
Social:false, TabCrashHandler:false, TelemetryStopwatch:false,
Translation:false, UITour:false, Utils:false, UpdateUtils:false,
Weave:false,
WebNavigationFrames: false, fxAccounts:false, gDevTools:false,
gDevToolsBrowser:false, webrtcUI:false, ZoomUI:false,
- Marionette:false,
+ Marionette:false, PageActions:false,
*/
/**
* IF YOU ADD OR REMOVE FROM THIS LIST, PLEASE UPDATE THE LIST ABOVE AS WELL.
* XXX Bug 1325373 is for making eslint detect these automatically.
*/
[
["AboutHome", "resource:///modules/AboutHome.jsm"],
@@ -64,16 +64,17 @@ XPCOMUtils.defineLazyPreferenceGetter(th
["E10SUtils", "resource:///modules/E10SUtils.jsm"],
["ExtensionsUI", "resource:///modules/ExtensionsUI.jsm"],
["FormValidationHandler", "resource:///modules/FormValidationHandler.jsm"],
["GMPInstallManager", "resource://gre/modules/GMPInstallManager.jsm"],
["LightweightThemeManager", "resource://gre/modules/LightweightThemeManager.jsm"],
["Log", "resource://gre/modules/Log.jsm"],
["LoginManagerParent", "resource://gre/modules/LoginManagerParent.jsm"],
["NewTabUtils", "resource://gre/modules/NewTabUtils.jsm"],
+ ["PageActions", "resource:///modules/PageActions.jsm"],
["PageThumbs", "resource://gre/modules/PageThumbs.jsm"],
["PluralForm", "resource://gre/modules/PluralForm.jsm"],
["Preferences", "resource://gre/modules/Preferences.jsm"],
["PrivateBrowsingUtils", "resource://gre/modules/PrivateBrowsingUtils.jsm"],
["ProcessHangMonitor", "resource:///modules/ProcessHangMonitor.jsm"],
["PromiseUtils", "resource://gre/modules/PromiseUtils.jsm"],
["ReaderMode", "resource://gre/modules/ReaderMode.jsm"],
["ReaderParent", "resource:///modules/ReaderParent.jsm"],
@@ -1385,16 +1386,17 @@ var gBrowserInit = {
gURLBar.setAttribute("readonly", "true");
gURLBar.setAttribute("enablehistory", "false");
}
// Misc. inits.
TabletModeUpdater.init();
CombinedStopReload.init();
gPrivateBrowsingUI.init();
+ BrowserPageActions.init();
if (window.matchMedia("(-moz-os-version: windows-win8)").matches &&
window.matchMedia("(-moz-windows-default-theme)").matches) {
let windowFrameColor = new Color(...Cu.import("resource:///modules/Windows8WindowFrameColor.jsm", {})
.Windows8WindowFrameColor.get());
// Default to black for foreground text.
if (!windowFrameColor.isContrastRatioAcceptable(new Color(0, 0, 0))) {
document.documentElement.setAttribute("darkwindowframe", "true");
@@ -7979,116 +7981,16 @@ var gIdentityHandler = {
container.appendChild(nameLabel);
container.appendChild(stateLabel);
container.appendChild(button);
return container;
}
};
-var gPageActionButton = {
- get button() {
- delete this.button;
- return this.button = document.getElementById("urlbar-page-action-button");
- },
-
- get panel() {
- delete this.panel;
- return this.panel = document.getElementById("page-action-panel");
- },
-
- get sendToDeviceBody() {
- delete this.sendToDeviceBody;
- return this.sendToDeviceBody = document.getElementById("page-action-sendToDeviceView-body");
- },
-
- onEvent(event) {
- event.stopPropagation();
-
- if ((event.type == "click" && event.button != 0) ||
- (event.type == "keypress" && event.charCode != KeyEvent.DOM_VK_SPACE &&
- event.keyCode != KeyEvent.DOM_VK_RETURN)) {
- return; // Left click, space or enter only
- }
-
- this._preparePanelToBeShown();
- this.panel.hidden = false;
- this.panel.openPopup(this.button, {
- position: "bottomcenter topright",
- triggerEvent: event,
- });
- },
-
- _preparePanelToBeShown() {
- // Update the bookmark item's label.
- BookmarkingUI.updateBookmarkPageMenuItem();
-
- // Update the send-to-device item's disabled state.
- let browser = gBrowser.selectedBrowser;
- let url = browser.currentURI.spec;
- let sendToDeviceItem =
- document.getElementById("page-action-send-to-device-button");
- sendToDeviceItem.disabled = !gSync.isSendableURI(url);
- },
-
- copyURL() {
- this.panel.hidePopup();
- Cc["@mozilla.org/widget/clipboardhelper;1"]
- .getService(Ci.nsIClipboardHelper)
- .copyString(gBrowser.selectedBrowser.currentURI.spec);
- },
-
- emailLink() {
- this.panel.hidePopup();
- MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);
- },
-
- showSendToDeviceView(subviewButton) {
- this.setupSendToDeviceView();
- PanelUI.showSubView("page-action-sendToDeviceView", subviewButton);
- },
-
- setupSendToDeviceView() {
- let browser = gBrowser.selectedBrowser;
- let url = browser.currentURI.spec;
- let title = browser.contentTitle;
- let body = this.sendToDeviceBody;
-
- // This is on top because it also clears the device list between state changes.
- gSync.populateSendTabToDevicesMenu(body, url, title, (clientId, name, clientType) => {
- if (!name) {
- return document.createElement("toolbarseparator");
- }
- let item = document.createElement("toolbarbutton");
- item.classList.add("page-action-sendToDevice-device", "subviewbutton");
- if (clientId) {
- item.classList.add("subviewbutton-iconic");
- }
- item.setAttribute("tooltiptext", name);
- return item;
- });
-
- body.removeAttribute("state");
- // In the first ~10 sec after startup, Sync may not be loaded and the list
- // of devices will be empty.
- if (gSync.syncConfiguredAndLoading) {
- body.setAttribute("state", "notready");
- // Force a background Sync
- Services.tm.dispatchToMainThread(async () => {
- await Weave.Service.sync([]); // [] = clients engine only
- // There's no way Sync is still syncing at this point, but we check
- // anyway to avoid infinite looping.
- if (!window.closed && !gSync.syncConfiguredAndLoading) {
- this.setupSendToDeviceView();
- }
- });
- }
- },
-};
-
/**
* Fired on the "marionette-remote-control" system notification,
* indicating if the browser session is under remote control.
*/
const gRemoteControl = {
observe(subject, topic, data) {
gRemoteControl.updateVisualCue(data);
},
--- a/browser/base/content/global-scripts.inc
+++ b/browser/base/content/global-scripts.inc
@@ -8,16 +8,17 @@
# tools/lint/eslint/eslint-plugin-mozilla/lib/environments/browser-window.js
<script type="application/javascript" src="chrome://browser/content/browser.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-captivePortal.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-compacttheme.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-feeds.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-media.js"/>
+<script type="application/javascript" src="chrome://browser/content/browser-pageActions.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-places.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-plugins.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-sidebar.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-tabsintitlebar.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-trackingprotection.js"/>
#ifdef MOZ_DATA_REPORTING
<script type="application/javascript" src="chrome://browser/content/browser-data-submission-info-bar.js"/>
--- a/browser/base/jar.mn
+++ b/browser/base/jar.mn
@@ -69,16 +69,17 @@ browser.jar:
content/browser/browser-customization.js (content/browser-customization.js)
content/browser/browser-data-submission-info-bar.js (content/browser-data-submission-info-bar.js)
content/browser/browser-compacttheme.js (content/browser-compacttheme.js)
content/browser/browser-feeds.js (content/browser-feeds.js)
content/browser/browser-fullScreenAndPointerLock.js (content/browser-fullScreenAndPointerLock.js)
content/browser/browser-fullZoom.js (content/browser-fullZoom.js)
content/browser/browser-gestureSupport.js (content/browser-gestureSupport.js)
content/browser/browser-media.js (content/browser-media.js)
+ content/browser/browser-pageActions.js (content/browser-pageActions.js)
content/browser/browser-places.js (content/browser-places.js)
content/browser/browser-plugins.js (content/browser-plugins.js)
content/browser/browser-safebrowsing.js (content/browser-safebrowsing.js)
content/browser/browser-sidebar.js (content/browser-sidebar.js)
content/browser/browser-social.js (content/browser-social.js)
content/browser/browser-sync.js (content/browser-sync.js)
* content/browser/browser-tabPreviews.xml (content/browser-tabPreviews.xml)
#ifdef CAN_DRAW_IN_TITLEBAR