new file mode 100644
--- /dev/null
+++ b/browser/base/content/browser-pageActions.js
@@ -0,0 +1,687 @@
+/* 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);
+ }
+ },
+
+ /**
+ * Adds or removes as necessary DOM nodes for the given action.
+ *
+ * @param action (PageActions.Action, required)
+ * The action to place.
+ * @param urlbarIndex (int, required)
+ * If the action is shown in the urlbar, then this is the index within
+ * the actions in the urlbar at which the action should be placed.
+ * Ignored otherwise.
+ */
+ placeAction(action, urlbarIndex) {
+ if (action.isSeparator) {
+ this._placePanelSeparator();
+ return;
+ }
+ this.placeActionInPanel(action);
+ this.placeActionInUrlbar(action, urlbarIndex);
+ },
+
+ /**
+ * Adds or removes as necessary DOM nodes for the action in the panel.
+ *
+ * @param action (PageActions.Action, required)
+ * The action to place.
+ */
+ placeActionInPanel(action) {
+ let id = this._panelButtonNodeIDForActionID(action.id);
+ let node = document.getElementById(id);
+ if (!node) {
+ let panelViewNode;
+ [node, panelViewNode] = this._makePanelButtonNodeForAction(action);
+ node.id = id;
+ let insertAfterNode = null;
+ if (action.insertAfter) {
+ let insertAfterNodeID = this._panelButtonNodeIDForActionID(action.insertAfter);
+ insertAfterNode = document.getElementById(insertAfterNodeID);
+ }
+ let insertBeforeNode =
+ insertAfterNode ? insertAfterNode.nextSibling : null;
+ 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", () => {
+ if (panelViewNode) {
+ action.subview.onShowing(panelViewNode);
+ this.multiViewNode.showSubView(panelViewNode, buttonNode);
+ return;
+ }
+ if (action.wantsIframe) {
+ this._toggleTempPanelForAction(action);
+ return;
+ }
+ this.panelNode.hidePopup();
+ action.onCommand(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");
+ panelViewNode.setAttribute("title", action.subview.title);
+ 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", () => {
+ button.onCommand(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 urlbarIndex (int, required)
+ * If the action is shown in the urlbar, then this is the index within
+ * the actions in the urlbar at which the action should be placed.
+ * Ignored otherwise.
+ */
+ placeActionInUrlbar(action, urlbarIndex) {
+ let id = this._urlbarButtonNodeIDForActionID(action.id);
+ let node = document.getElementById(id);
+ if (!action.shownInUrlbar) {
+ if (node) {
+ node.remove();
+ }
+ return null;
+ }
+ if (!node) {
+ node = this._makeUrlbarButtonNode(action);
+ node.id = id;
+ let parentNode = this.mainButtonNode.parentNode;
+ let insertBeforeNode = null;
+ let index = 0;
+ for (let childNode of parentNode.childNodes) {
+ if (childNode == this.mainButtonNode) {
+ continue;
+ }
+ if (index == urlbarIndex) {
+ insertBeforeNode = childNode;
+ break;
+ }
+ index++;
+ }
+ parentNode.insertBefore(node, insertBeforeNode);
+ action.onPlacedInUrlbar(node);
+ }
+ 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(buttonNode);
+ });
+ return buttonNode;
+ },
+
+ _placePanelSeparator() {
+ let node = document.createElement("toolbarseparator");
+ 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 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, "bottomcenter topright");
+ },
+
+ /**
+ * 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 = {
+ /**
+ * The onShowingInPanel handler for the action.
+ *
+ * @param buttonNode (DOM node, required)
+ * The buttonNode arg passed to the action's onShowingInPanel.
+ */
+ onShowingInPanel(buttonNode) {
+ // Update the button label via the bookmark observer.
+ BookmarkingUI.updateBookmarkPageMenuItem();
+ },
+
+ /**
+ * The onCommand handler for the action.
+ *
+ * @param buttonNode (DOM node, required)
+ * The buttonNode arg passed to the action's onCommand.
+ */
+ onCommand(buttonNode) {
+ BrowserPageActions.panelNode.hidePopup();
+ let commandNode = document.getElementById("Browser:AddBookmarkAs");
+ commandNode.doCommand();
+ },
+};
+
+// copy URL
+BrowserPageActions.copyURL = {
+ /**
+ * The onPlacedInPanel handler for the action.
+ *
+ * @param buttonNode (DOM node, required)
+ * The buttonNode arg passed to the action's onPlacedInPanel.
+ */
+ onPlacedInPanel(buttonNode) {
+ BrowserPageActions.takeNodeAttributeFromPanel(buttonNode, "title");
+ },
+
+ /**
+ * The onCommand handler for the action.
+ *
+ * @param buttonNode (DOM node, required)
+ * The buttonNode arg passed to the action's onCommand.
+ */
+ onCommand(buttonNode) {
+ BrowserPageActions.panelNode.hidePopup();
+ Cc["@mozilla.org/widget/clipboardhelper;1"]
+ .getService(Ci.nsIClipboardHelper)
+ .copyString(gBrowser.selectedBrowser.currentURI.spec);
+ },
+};
+
+// email link
+BrowserPageActions.emailLink = {
+ /**
+ * The onPlacedInPanel handler for the action.
+ *
+ * @param buttonNode (DOM node, required)
+ * The buttonNode arg passed to the action's onPlacedInPanel.
+ */
+ onPlacedInPanel(buttonNode) {
+ BrowserPageActions.takeNodeAttributeFromPanel(buttonNode, "title");
+ },
+
+ /**
+ * The onCommand handler for the action.
+ *
+ * @param buttonNode (DOM node, required)
+ * The buttonNode arg passed to the action's onCommand.
+ */
+ onCommand(buttonNode) {
+ BrowserPageActions.panelNode.hidePopup();
+ MailIntegration.sendLinkForBrowser(gBrowser.selectedBrowser);
+ },
+};
+
+// send to device
+BrowserPageActions.sendToDevice = {
+ /**
+ * The onPlacedInPanel handler for the action.
+ *
+ * @param buttonNode (DOM node, required)
+ * The buttonNode arg passed to the action's onPlacedInPanel.
+ */
+ onPlacedInPanel(buttonNode) {
+ BrowserPageActions.takeNodeAttributeFromPanel(buttonNode, "title");
+ },
+
+ /**
+ * The onSubviewPlaced handler for the action.
+ *
+ * @param panelViewNode (DOM node, required)
+ * The panelViewNode arg passed to the action's onSubviewPlaced.
+ */
+ onSubviewPlaced(panelViewNode) {
+ BrowserPageActions.takeNodeAttributeFromPanel(panelViewNode, "title");
+ let bodyNode = panelViewNode.firstChild;
+ for (let node of bodyNode.childNodes) {
+ BrowserPageActions.takeNodeAttributeFromPanel(node, "title");
+ BrowserPageActions.takeNodeAttributeFromPanel(node, "shortcut");
+ }
+ },
+
+ /**
+ * The onShowingInPanel handler for the action.
+ *
+ * @param buttonNode (DOM node, required)
+ * The buttonNode arg passed to the action's onShowingInPanel.
+ */
+ onShowingInPanel(buttonNode) {
+ let browser = gBrowser.selectedBrowser;
+ let url = browser.currentURI.spec;
+ if (gSync.isSendableURI(url)) {
+ buttonNode.removeAttribute("disabled");
+ } else {
+ buttonNode.setAttribute("disabled", "true");
+ }
+ },
+
+ /**
+ * The onShowingSubview handler for the action.
+ *
+ * @param panelViewNode (DOM node, required)
+ * The panelViewNode arg passed to the action's onShowingSubview.
+ */
+ 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;
+ });
+
+ if (!gSync.isSignedIn) {
+ // Could be unconfigured or unverified
+ bodyNode.setAttribute("state", "notsignedin");
+ return;
+ }
+
+ // In the first ~10 sec after startup, Sync may not be loaded and the list
+ // of devices will be empty.
+ if (!gSync.syncReady) {
+ bodyNode.setAttribute("state", "notready");
+ // Force a background Sync
+ Services.tm.dispatchToMainThread(() => {
+ Weave.Service.sync([]); // [] = clients engine only
+ if (!window.closed && gSync.syncReady) {
+ this.onShowingSubview(panelViewNode);
+ }
+ });
+ return;
+ }
+ if (!gSync.remoteClients.length) {
+ bodyNode.setAttribute("state", "nodevice");
+ return;
+ }
+
+ bodyNode.setAttribute("state", "signedin");
+ },
+
+ /**
+ * Call when the "Firefox Account Required" button is clicked.
+ *
+ * @param buttonNode (DOM node, required)
+ * The button node.
+ */
+ fxaButtonClicked(buttonNode) {
+ let panelNode = buttonNode.parentNode;
+ while (panelNode && panelNode.localName != "panel") {
+ panelNode = panelNode.parentNode;
+ }
+ if (panelNode) {
+ panelNode.hidePopup();
+ }
+ gSync.openPrefs();
+ },
+};
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -34,17 +34,17 @@ XPCOMUtils.defineLazyGetter(this, "exten
PluralForm:false, Preferences:false, PrivateBrowsingUtils:false,
ProcessHangMonitor:false, PromiseUtils:false, ReaderMode:false,
ReaderParent:false, RecentWindow:false, SessionStore:false,
ShortcutUtils:false, SimpleServiceDiscovery:false, SitePermissions:false,
Social:false, TabCrashHandler:false, Task:false, TelemetryStopwatch:false,
Translation:false, UITour:false, UpdateUtils:false, Weave:false,
WebNavigationFrames: false, fxAccounts:false, gDevTools:false,
gDevToolsBrowser:false, webrtcUI:false, FullZoomUI: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"],
@@ -60,16 +60,17 @@ XPCOMUtils.defineLazyGetter(this, "exten
["ExtensionsUI", "resource:///modules/ExtensionsUI.jsm"],
["FormValidationHandler", "resource:///modules/FormValidationHandler.jsm"],
["FullZoomUI", "resource:///modules/FullZoomUI.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"],
@@ -1408,16 +1409,18 @@ var gBrowserInit = {
gBrowser.addEventListener("InsecureLoginFormsStateChange", function() {
gIdentityHandler.refreshForInsecureLoginForms();
});
gBrowser.addEventListener("PermissionStateChange", function() {
gIdentityHandler.refreshIdentityBlock();
});
+ BrowserPageActions.init();
+
let uriToLoad = this._getUriToLoad();
if (uriToLoad && uriToLoad != "about:blank") {
if (uriToLoad instanceof Ci.nsIArray) {
let count = uriToLoad.length;
let specs = [];
for (let i = 0; i < count; i++) {
let urisstring = uriToLoad.queryElementAt(i, Ci.nsISupportsString);
specs.push(urisstring.data);
@@ -7903,128 +7906,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, "bottomcenter topright");
- },
-
- _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;
- });
-
- if (!gSync.isSignedIn) {
- // Could be unconfigured or unverified
- body.setAttribute("state", "notsignedin");
- return;
- }
-
- // In the first ~10 sec after startup, Sync may not be loaded and the list
- // of devices will be empty.
- if (!gSync.syncReady) {
- body.setAttribute("state", "notready");
- // Force a background Sync
- Services.tm.dispatchToMainThread(() => {
- Weave.Service.sync([]); // [] = clients engine only
- if (!window.closed && gSync.syncReady) {
- this.setupSendToDeviceView();
- }
- });
- return;
- }
- if (!gSync.remoteClients.length) {
- body.setAttribute("state", "nodevice");
- return;
- }
-
- body.setAttribute("state", "signedin");
- },
-
- fxaButtonClicked() {
- this.panel.hidePopup();
- gSync.openPrefs();
- },
-};
-
/**
* 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
@@ -19,16 +19,17 @@
<script type="application/javascript" src="chrome://browser/content/browser-ctrlTab.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-customization.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-fullScreenAndPointerLock.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-fullZoom.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-gestureSupport.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-refreshblocker.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-safebrowsing.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-sidebar.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-social.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-sync.js"/>
<script type="application/javascript" src="chrome://browser/content/browser-tabsintitlebar.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-refreshblocker.js (content/browser-refreshblocker.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)