From a1be17c1cea81ebb1e8b131a662c698d78f3f7f2 Mon Sep 17 00:00:00 2001 From: wolfbeast Date: Mon, 4 Jun 2018 13:17:38 +0200 Subject: Issue #303 Part 1: Move basilisk files from /browser to /application/basilisk --- application/basilisk/base/content/nsContextMenu.js | 1851 ++++++++++++++++++++ 1 file changed, 1851 insertions(+) create mode 100644 application/basilisk/base/content/nsContextMenu.js (limited to 'application/basilisk/base/content/nsContextMenu.js') diff --git a/application/basilisk/base/content/nsContextMenu.js b/application/basilisk/base/content/nsContextMenu.js new file mode 100644 index 000000000..097caf367 --- /dev/null +++ b/application/basilisk/base/content/nsContextMenu.js @@ -0,0 +1,1851 @@ +/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set ts=2 sw=2 sts=2 et tw=80: */ +# 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/. + +Components.utils.import("resource://gre/modules/ContextualIdentityService.jsm"); +Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); +Components.utils.import("resource://gre/modules/InlineSpellChecker.jsm"); +Components.utils.import("resource://gre/modules/LoginManagerContextMenu.jsm"); +Components.utils.import("resource://gre/modules/BrowserUtils.jsm"); +Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); +Components.utils.import("resource://gre/modules/Services.jsm"); + + +XPCOMUtils.defineLazyModuleGetter(this, "LoginHelper", + "resource://gre/modules/LoginHelper.jsm"); + +var gContextMenuContentData = null; + +function nsContextMenu(aXulMenu, aIsShift) { + this.shouldDisplay = true; + this.initMenu(aXulMenu, aIsShift); +} + +// Prototype for nsContextMenu "class." +nsContextMenu.prototype = { + initMenu: function CM_initMenu(aXulMenu, aIsShift) { + // Get contextual info. + this.setTarget(document.popupNode, document.popupRangeParent, + document.popupRangeOffset); + if (!this.shouldDisplay) + return; + + this.hasPageMenu = false; + this.isContentSelected = !this.selectionInfo.docSelectionIsCollapsed; + if (!aIsShift) { + if (this.isRemote) { + this.hasPageMenu = + PageMenuParent.addToPopup(gContextMenuContentData.customMenuItems, + this.browser, aXulMenu); + } + else { + this.hasPageMenu = PageMenuParent.buildAndAddToPopup(this.target, aXulMenu); + } + + let subject = { + menu: aXulMenu, + tab: gBrowser ? gBrowser.getTabForBrowser(this.browser) : undefined, + isContentSelected: this.isContentSelected, + inFrame: this.inFrame, + isTextSelected: this.isTextSelected, + onTextInput: this.onTextInput, + onLink: this.onLink, + onImage: this.onImage, + onVideo: this.onVideo, + onAudio: this.onAudio, + onCanvas: this.onCanvas, + onEditableArea: this.onEditableArea, + srcUrl: this.mediaURL, + frameUrl: gContextMenuContentData ? gContextMenuContentData.docLocation : undefined, + pageUrl: this.browser ? this.browser.currentURI.spec : undefined, + linkUrl: this.linkURL, + selectionText: this.isTextSelected ? this.selectionInfo.text : undefined, + }; + subject.wrappedJSObject = subject; + Services.obs.notifyObservers(subject, "on-build-contextmenu", null); + } + + this.isFrameImage = document.getElementById("isFrameImage"); + this.ellipsis = "\u2026"; + try { + this.ellipsis = gPrefService.getComplexValue("intl.ellipsis", + Ci.nsIPrefLocalizedString).data; + } catch (e) { } + + // Reset after "on-build-contextmenu" notification in case selection was + // changed during the notification. + this.isContentSelected = !this.selectionInfo.docSelectionIsCollapsed; + this.onPlainTextLink = false; + + let bookmarkPage = document.getElementById("context-bookmarkpage"); + if (bookmarkPage) + BookmarkingUI.onCurrentPageContextPopupShowing(); + + // Initialize (disable/remove) menu items. + this.initItems(); + + // Register this opening of the menu with telemetry: + this._checkTelemetryForMenu(aXulMenu); + }, + + hiding: function CM_hiding() { + gContextMenuContentData = null; + InlineSpellCheckerUI.clearSuggestionsFromMenu(); + InlineSpellCheckerUI.clearDictionaryListFromMenu(); + InlineSpellCheckerUI.uninit(); + LoginManagerContextMenu.clearLoginsFromMenu(document); + + // This handler self-deletes, only run it if it is still there: + if (this._onPopupHiding) { + this._onPopupHiding(); + } + }, + + initItems: function CM_initItems() { + this.initPageMenuSeparator(); + this.initOpenItems(); + this.initNavigationItems(); + this.initViewItems(); + this.initMiscItems(); + this.initSpellingItems(); + this.initSaveItems(); + this.initClipboardItems(); + this.initMediaPlayerItems(); + this.initLeaveDOMFullScreenItems(); + this.initClickToPlayItems(); + this.initPasswordManagerItems(); + this.initSyncItems(); + }, + + initPageMenuSeparator: function CM_initPageMenuSeparator() { + this.showItem("page-menu-separator", this.hasPageMenu); + }, + + initOpenItems: function CM_initOpenItems() { + var isMailtoInternal = false; + if (this.onMailtoLink) { + var mailtoHandler = Cc["@mozilla.org/uriloader/external-protocol-service;1"]. + getService(Ci.nsIExternalProtocolService). + getProtocolHandlerInfo("mailto"); + isMailtoInternal = (!mailtoHandler.alwaysAskBeforeHandling && + mailtoHandler.preferredAction == Ci.nsIHandlerInfo.useHelperApp && + (mailtoHandler.preferredApplicationHandler instanceof Ci.nsIWebHandlerApp)); + } + + if (this.isTextSelected && !this.onLink && + this.selectionInfo && this.selectionInfo.linkURL) { + this.linkURL = this.selectionInfo.linkURL; + try { + this.linkURI = makeURI(this.linkURL); + } catch (ex) {} + + this.linkTextStr = this.selectionInfo.linkText; + this.onPlainTextLink = true; + } + + var inContainer = false; + if (gContextMenuContentData.userContextId) { + inContainer = true; + var item = document.getElementById("context-openlinkincontainertab"); + + item.setAttribute("data-usercontextid", gContextMenuContentData.userContextId); + + var label = + ContextualIdentityService.getUserContextLabel(gContextMenuContentData.userContextId); + item.setAttribute("label", + gBrowserBundle.formatStringFromName("userContextOpenLink.label", + [label], 1)); + } + + var shouldShow = this.onSaveableLink || isMailtoInternal || this.onPlainTextLink; + var isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(window); + var showContainers = Services.prefs.getBoolPref("privacy.userContext.enabled"); + this.showItem("context-openlink", shouldShow && !isWindowPrivate); + this.showItem("context-openlinkprivate", shouldShow); + this.showItem("context-openlinkintab", shouldShow && !inContainer); + this.showItem("context-openlinkincontainertab", shouldShow && inContainer); + this.showItem("context-openlinkinusercontext-menu", shouldShow && !isWindowPrivate && showContainers); + this.showItem("context-openlinkincurrent", this.onPlainTextLink); + this.showItem("context-sep-open", shouldShow); + }, + + initNavigationItems: function CM_initNavigationItems() { + var shouldShow = !(this.isContentSelected || this.onLink || this.onImage || + this.onCanvas || this.onVideo || this.onAudio || + this.onTextInput); + this.showItem("context-navigation", shouldShow); + this.showItem("context-sep-navigation", shouldShow); + + let stopped = XULBrowserWindow.stopCommand.getAttribute("disabled") == "true"; + + let stopReloadItem = ""; + if (shouldShow) { + stopReloadItem = (stopped) ? "reload" : "stop"; + } + + this.showItem("context-reload", stopReloadItem == "reload"); + this.showItem("context-stop", stopReloadItem == "stop"); + + // XXX: Stop is determined in browser.js; the canStop broadcaster is broken + //this.setItemAttrFromNode( "context-stop", "disabled", "canStop" ); + }, + + initLeaveDOMFullScreenItems: function CM_initLeaveFullScreenItem() { + // only show the option if the user is in DOM fullscreen + var shouldShow = (this.target.ownerDocument.fullscreenElement != null); + this.showItem("context-leave-dom-fullscreen", shouldShow); + + // Explicitly show if in DOM fullscreen, but do not hide it has already been shown + if (shouldShow) + this.showItem("context-media-sep-commands", true); + }, + + initSaveItems: function CM_initSaveItems() { + var shouldShow = !(this.onTextInput || this.onLink || + this.isContentSelected || this.onImage || + this.onCanvas || this.onVideo || this.onAudio); + this.showItem("context-savepage", shouldShow); + + // Save link depends on whether we're in a link, or selected text matches valid URL pattern. + this.showItem("context-savelink", this.onSaveableLink || this.onPlainTextLink); + + // Save image depends on having loaded its content, video and audio don't. + this.showItem("context-saveimage", this.onLoadedImage || this.onCanvas); + this.showItem("context-savevideo", this.onVideo); + this.showItem("context-saveaudio", this.onAudio); + this.showItem("context-video-saveimage", this.onVideo); + this.setItemAttr("context-savevideo", "disabled", !this.mediaURL); + this.setItemAttr("context-saveaudio", "disabled", !this.mediaURL); + // Send media URL (but not for canvas, since it's a big data: URL) + this.showItem("context-sendimage", this.onImage); + this.showItem("context-sendvideo", this.onVideo); + this.showItem("context-castvideo", this.onVideo); + this.showItem("context-sendaudio", this.onAudio); + let mediaIsBlob = this.mediaURL.startsWith("blob:"); + this.setItemAttr("context-sendvideo", "disabled", !this.mediaURL || mediaIsBlob); + this.setItemAttr("context-sendaudio", "disabled", !this.mediaURL || mediaIsBlob); + let shouldShowCast = Services.prefs.getBoolPref("browser.casting.enabled"); + // getServicesForVideo alone would be sufficient here (it depends on + // SimpleServiceDiscovery.services), but SimpleServiceDiscovery is guaranteed + // to be already loaded, since we load it on startup in nsBrowserGlue, + // and CastingApps isn't, so check SimpleServiceDiscovery.services first + // to avoid needing to load CastingApps.jsm if we don't need to. + shouldShowCast = shouldShowCast && this.mediaURL && + SimpleServiceDiscovery.services.length > 0 && + CastingApps.getServicesForVideo(this.target).length > 0; + this.setItemAttr("context-castvideo", "disabled", !shouldShowCast); + }, + + initViewItems: function CM_initViewItems() { + // View source is always OK, unless in directory listing. + this.showItem("context-viewpartialsource-selection", + this.isContentSelected); + this.showItem("context-viewpartialsource-mathml", + this.onMathML && !this.isContentSelected); + + var shouldShow = !(this.isContentSelected || + this.onImage || this.onCanvas || + this.onVideo || this.onAudio || + this.onLink || this.onTextInput); + var showInspect = gPrefService.getBoolPref("devtools.inspector.enabled"); + this.showItem("context-viewsource", shouldShow); + this.showItem("context-viewinfo", shouldShow); + this.showItem("inspect-separator", showInspect); + this.showItem("context-inspect", showInspect); + + this.showItem("context-sep-viewsource", shouldShow); + + // Set as Desktop background depends on whether an image was clicked on, + // and only works if we have a shell service. + var haveSetDesktopBackground = false; +#ifdef HAVE_SHELL_SERVICE + // Only enable Set as Desktop Background if we can get the shell service. + var shell = getShellService(); + if (shell) + haveSetDesktopBackground = shell.canSetDesktopBackground; +#endif + this.showItem("context-setDesktopBackground", + haveSetDesktopBackground && this.onLoadedImage); + + if (haveSetDesktopBackground && this.onLoadedImage) { + document.getElementById("context-setDesktopBackground") + .disabled = gContextMenuContentData.disableSetDesktopBackground; + } + + // Reload image depends on an image that's not fully loaded + this.showItem("context-reloadimage", (this.onImage && !this.onCompletedImage)); + + // View image depends on having an image that's not standalone + // (or is in a frame), or a canvas. + this.showItem("context-viewimage", (this.onImage && + (!this.inSyntheticDoc || this.inFrame)) || this.onCanvas); + + // View video depends on not having a standalone video. + this.showItem("context-viewvideo", this.onVideo && (!this.inSyntheticDoc || this.inFrame)); + this.setItemAttr("context-viewvideo", "disabled", !this.mediaURL); + + // View background image depends on whether there is one, but don't make + // background images of a stand-alone media document available. + this.showItem("context-viewbgimage", shouldShow && + !this._hasMultipleBGImages && + !this.inSyntheticDoc); + this.showItem("context-sep-viewbgimage", shouldShow && + !this._hasMultipleBGImages && + !this.inSyntheticDoc); + document.getElementById("context-viewbgimage") + .disabled = !this.hasBGImage; + + this.showItem("context-viewimageinfo", this.onImage); + this.showItem("context-viewimagedesc", this.onImage && this.imageDescURL !== ""); + }, + + initMiscItems: function CM_initMiscItems() { + // Use "Bookmark This Link" if on a link. + let bookmarkPage = document.getElementById("context-bookmarkpage"); + this.showItem(bookmarkPage, + !(this.isContentSelected || this.onTextInput || this.onLink || + this.onImage || this.onVideo || this.onAudio || this.onCanvas)); + bookmarkPage.setAttribute("tooltiptext", bookmarkPage.getAttribute("buttontooltiptext")); + + this.showItem("context-bookmarklink", (this.onLink && !this.onMailtoLink) || + this.onPlainTextLink); + this.showItem("context-keywordfield", + this.onTextInput && this.onKeywordField); + this.showItem("frame", this.inFrame); + + let showSearchSelect = (this.isTextSelected || this.onLink) && !this.onImage; + this.showItem("context-searchselect", showSearchSelect); + if (showSearchSelect) { + this.formatSearchContextItem(); + } + + // srcdoc cannot be opened separately due to concerns about web + // content with about:srcdoc in location bar masquerading as trusted + // chrome/addon content. + // No need to also test for this.inFrame as this is checked in the parent + // submenu. + this.showItem("context-showonlythisframe", !this.inSrcdocFrame); + this.showItem("context-openframeintab", !this.inSrcdocFrame); + this.showItem("context-openframe", !this.inSrcdocFrame); + this.showItem("context-bookmarkframe", !this.inSrcdocFrame); + this.showItem("open-frame-sep", !this.inSrcdocFrame); + + this.showItem("frame-sep", this.inFrame && this.isTextSelected); + + // Hide menu entries for images, show otherwise + if (this.inFrame) { + if (BrowserUtils.mimeTypeIsTextBased(this.target.ownerDocument.contentType)) + this.isFrameImage.removeAttribute('hidden'); + else + this.isFrameImage.setAttribute('hidden', 'true'); + } + + // BiDi UI + this.showItem("context-sep-bidi", !this.onNumeric && top.gBidiUI); + this.showItem("context-bidi-text-direction-toggle", + this.onTextInput && !this.onNumeric && top.gBidiUI); + this.showItem("context-bidi-page-direction-toggle", + !this.onTextInput && top.gBidiUI); + }, + + initSpellingItems: function() { + var canSpell = InlineSpellCheckerUI.canSpellCheck && + !InlineSpellCheckerUI.initialSpellCheckPending && + this.canSpellCheck; + let showDictionaries = canSpell && InlineSpellCheckerUI.enabled; + var onMisspelling = InlineSpellCheckerUI.overMisspelling; + var showUndo = canSpell && InlineSpellCheckerUI.canUndo(); + this.showItem("spell-check-enabled", canSpell); + this.showItem("spell-separator", canSpell); + document.getElementById("spell-check-enabled") + .setAttribute("checked", canSpell && InlineSpellCheckerUI.enabled); + + this.showItem("spell-add-to-dictionary", onMisspelling); + this.showItem("spell-undo-add-to-dictionary", showUndo); + + // suggestion list + this.showItem("spell-suggestions-separator", onMisspelling || showUndo); + if (onMisspelling) { + var suggestionsSeparator = + document.getElementById("spell-add-to-dictionary"); + var numsug = + InlineSpellCheckerUI.addSuggestionsToMenu(suggestionsSeparator.parentNode, + suggestionsSeparator, 5); + this.showItem("spell-no-suggestions", numsug == 0); + } + else + this.showItem("spell-no-suggestions", false); + + // dictionary list + this.showItem("spell-dictionaries", showDictionaries); + if (canSpell) { + var dictMenu = document.getElementById("spell-dictionaries-menu"); + var dictSep = document.getElementById("spell-language-separator"); + let count = InlineSpellCheckerUI.addDictionaryListToMenu(dictMenu, dictSep); + this.showItem(dictSep, count > 0); + this.showItem("spell-add-dictionaries-main", false); + } + else if (this.onEditableArea) { + // when there is no spellchecker but we might be able to spellcheck + // add the add to dictionaries item. This will ensure that people + // with no dictionaries will be able to download them + this.showItem("spell-language-separator", showDictionaries); + this.showItem("spell-add-dictionaries-main", showDictionaries); + } + else + this.showItem("spell-add-dictionaries-main", false); + }, + + initClipboardItems: function() { + // Copy depends on whether there is selected text. + // Enabling this context menu item is now done through the global + // command updating system + // this.setItemAttr( "context-copy", "disabled", !this.isTextSelected() ); + goUpdateGlobalEditMenuItems(); + + this.showItem("context-undo", this.onTextInput); + this.showItem("context-sep-undo", this.onTextInput); + this.showItem("context-cut", this.onTextInput); + this.showItem("context-copy", + this.isContentSelected || this.onTextInput); + this.showItem("context-paste", this.onTextInput); + this.showItem("context-delete", this.onTextInput); + this.showItem("context-sep-paste", this.onTextInput); + this.showItem("context-selectall", !(this.onLink || this.onImage || + this.onVideo || this.onAudio || + this.inSyntheticDoc) || + this.isDesignMode); + this.showItem("context-sep-selectall", this.isContentSelected ); + + // XXX dr + // ------ + // nsDocumentViewer.cpp has code to determine whether we're + // on a link or an image. we really ought to be using that... + + // Copy email link depends on whether we're on an email link. + this.showItem("context-copyemail", this.onMailtoLink); + + // Copy link location depends on whether we're on a non-mailto link. + this.showItem("context-copylink", this.onLink && !this.onMailtoLink); + this.showItem("context-sep-copylink", this.onLink && + (this.onImage || this.onVideo || this.onAudio)); + +#ifdef CONTEXT_COPY_IMAGE_CONTENTS + // Copy image contents depends on whether we're on an image. + this.showItem("context-copyimage-contents", this.onImage); +#endif + // Copy image location depends on whether we're on an image. + this.showItem("context-copyimage", this.onImage); + this.showItem("context-copyvideourl", this.onVideo); + this.showItem("context-copyaudiourl", this.onAudio); + this.setItemAttr("context-copyvideourl", "disabled", !this.mediaURL); + this.setItemAttr("context-copyaudiourl", "disabled", !this.mediaURL); + this.showItem("context-sep-copyimage", this.onImage || + this.onVideo || this.onAudio); + }, + + initMediaPlayerItems: function() { + var onMedia = (this.onVideo || this.onAudio); + // Several mutually exclusive items... play/pause, mute/unmute, show/hide + this.showItem("context-media-play", onMedia && (this.target.paused || this.target.ended)); + this.showItem("context-media-pause", onMedia && !this.target.paused && !this.target.ended); + this.showItem("context-media-mute", onMedia && !this.target.muted); + this.showItem("context-media-unmute", onMedia && this.target.muted); + this.showItem("context-media-playbackrate", onMedia && this.target.duration != Number.POSITIVE_INFINITY); + this.showItem("context-media-loop", onMedia); + this.showItem("context-media-showcontrols", onMedia && !this.target.controls); + this.showItem("context-media-hidecontrols", this.target.controls && (this.onVideo || (this.onAudio && !this.inSyntheticDoc))); + this.showItem("context-video-fullscreen", this.onVideo && this.target.ownerDocument.fullscreenElement == null); + this.showItem("context-media-eme-learnmore", this.onDRMMedia); + this.showItem("context-media-eme-separator", this.onDRMMedia); + + // Disable them when there isn't a valid media source loaded. + if (onMedia) { + this.setItemAttr("context-media-playbackrate-050x", "checked", this.target.playbackRate == 0.5); + this.setItemAttr("context-media-playbackrate-100x", "checked", this.target.playbackRate == 1.0); + this.setItemAttr("context-media-playbackrate-125x", "checked", this.target.playbackRate == 1.25); + this.setItemAttr("context-media-playbackrate-150x", "checked", this.target.playbackRate == 1.5); + this.setItemAttr("context-media-playbackrate-200x", "checked", this.target.playbackRate == 2.0); + this.setItemAttr("context-media-loop", "checked", this.target.loop); + var hasError = this.target.error != null || + this.target.networkState == this.target.NETWORK_NO_SOURCE; + this.setItemAttr("context-media-play", "disabled", hasError); + this.setItemAttr("context-media-pause", "disabled", hasError); + this.setItemAttr("context-media-mute", "disabled", hasError); + this.setItemAttr("context-media-unmute", "disabled", hasError); + this.setItemAttr("context-media-playbackrate", "disabled", hasError); + this.setItemAttr("context-media-playbackrate-050x", "disabled", hasError); + this.setItemAttr("context-media-playbackrate-100x", "disabled", hasError); + this.setItemAttr("context-media-playbackrate-125x", "disabled", hasError); + this.setItemAttr("context-media-playbackrate-150x", "disabled", hasError); + this.setItemAttr("context-media-playbackrate-200x", "disabled", hasError); + this.setItemAttr("context-media-showcontrols", "disabled", hasError); + this.setItemAttr("context-media-hidecontrols", "disabled", hasError); + if (this.onVideo) { + let canSaveSnapshot = !this.onDRMMedia && this.target.readyState >= this.target.HAVE_CURRENT_DATA; + this.setItemAttr("context-video-saveimage", "disabled", !canSaveSnapshot); + this.setItemAttr("context-video-fullscreen", "disabled", hasError); + } + } + this.showItem("context-media-sep-commands", onMedia); + }, + + initClickToPlayItems: function() { + this.showItem("context-ctp-play", this.onCTPPlugin); + this.showItem("context-ctp-hide", this.onCTPPlugin); + this.showItem("context-sep-ctp", this.onCTPPlugin); + }, + + initPasswordManagerItems: function() { + let loginFillInfo = gContextMenuContentData && gContextMenuContentData.loginFillInfo; + + // If we could not find a password field we + // don't want to show the form fill option. + let showFill = loginFillInfo && loginFillInfo.passwordField.found; + + // Disable the fill option if the user has set a master password + // or if the password field or target field are disabled. + let disableFill = !loginFillInfo || + !Services.logins || + !Services.logins.isLoggedIn || + loginFillInfo.passwordField.disabled || + (!this.onPassword && loginFillInfo.usernameField.disabled); + + this.showItem("fill-login-separator", showFill); + this.showItem("fill-login", showFill); + this.setItemAttr("fill-login", "disabled", disableFill); + + // Set the correct label for the fill menu + let fillMenu = document.getElementById("fill-login"); + if (this.onPassword) { + fillMenu.setAttribute("label", fillMenu.getAttribute("label-password")); + fillMenu.setAttribute("accesskey", fillMenu.getAttribute("accesskey-password")); + } else { + fillMenu.setAttribute("label", fillMenu.getAttribute("label-login")); + fillMenu.setAttribute("accesskey", fillMenu.getAttribute("accesskey-login")); + } + + if (!showFill || disableFill) { + return; + } + let documentURI = gContextMenuContentData.documentURIObject; + let fragment = LoginManagerContextMenu.addLoginsToMenu(this.target, this.browser, documentURI); + + this.showItem("fill-login-no-logins", !fragment); + + if (!fragment) { + return; + } + let popup = document.getElementById("fill-login-popup"); + let insertBeforeElement = document.getElementById("fill-login-no-logins"); + popup.insertBefore(fragment, insertBeforeElement); + }, + + initSyncItems: function() { + gFxAccounts.initPageContextMenu(this); + }, + + openPasswordManager: function() { + LoginHelper.openPasswordManager(window, gContextMenuContentData.documentURIObject.host); + }, + + inspectNode: function() { + let {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {}); + let gBrowser = this.browser.ownerGlobal.gBrowser; + let target = devtools.TargetFactory.forTab(gBrowser.selectedTab); + + return gDevTools.showToolbox(target, "inspector").then(toolbox => { + let inspector = toolbox.getCurrentPanel(); + + // new-node-front tells us when the node has been selected, whether the + // browser is remote or not. + let onNewNode = inspector.selection.once("new-node-front"); + + this.browser.messageManager.sendAsyncMessage("debug:inspect", {}, {node: this.target}); + inspector.walker.findInspectingNode().then(nodeFront => { + inspector.selection.setNodeFront(nodeFront, "browser-context-menu"); + }); + + return onNewNode.then(() => { + // Now that the node has been selected, wait until the inspector is + // fully updated. + return inspector.once("inspector-updated"); + }); + }); + }, + + // Set various context menu attributes based on the state of the world. + setTarget: function (aNode, aRangeParent, aRangeOffset) { + // gContextMenuContentData.isRemote tells us if the event came from a remote + // process. gContextMenuContentData can be null if something (like tests) + // opens the context menu directly. + let editFlags; + this.isRemote = gContextMenuContentData && gContextMenuContentData.isRemote; + if (this.isRemote) { + aNode = gContextMenuContentData.event.target; + aRangeParent = gContextMenuContentData.event.rangeParent; + aRangeOffset = gContextMenuContentData.event.rangeOffset; + editFlags = gContextMenuContentData.editFlags; + } + + const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; + if (aNode.nodeType == Node.DOCUMENT_NODE || + // Not display on XUL element but relax for