setTarget: function()

in suite/base/content/nsContextMenu.js [587:868]


  setTarget: function(aNode, aRangeParent, aRangeOffset) {
    // Currently "isRemote" is always false.
    //this.isRemote = gContextMenuContentData && gContextMenuContentData.isRemote;

    const xulNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";

    // Initialize contextual info.
    this.onImage               = false;
    this.onLoadedImage         = false;
    this.onCompletedImage      = false;
    this.onStandaloneImage     = false;
    this.onCanvas              = false;
    this.onVideo               = false;
    this.onAudio               = false;
    this.onMetaDataItem        = false;
    this.onTextInput           = false;
    this.onNumeric             = false;
    this.onKeywordField        = false;
    this.mediaURL              = "";
    this.onLink                = false;
    this.onMailtoLink          = false;
    this.onSaveableLink        = false;
    this.inDirList             = false;
    this.link                  = null;
    this.linkURL               = "";
    this.linkURI               = null;
    this.linkProtocol          = "";
    this.linkHasNoReferrer     = false;
    this.onMathML              = false;
    this.inFrame               = false;
    this.inSyntheticDoc        = false;
    this.hasBGImage            = false;
    this.bgImageURL            = "";
    this.autoDownload          = false;
    this.isTextSelected        = false;
    this.isContentSelected     = false;
    this.onEditableArea        = false;
    this.canSpellCheck         = false;
    this.onPassword            = false;

    // Remember the node that was clicked.
    this.target = aNode;

    if (aNode.nodeType == Node.DOCUMENT_NODE ||
        // Not display on XUL element but relax for <label class="text-link">
        (aNode.namespaceURI == xulNS && !isXULTextLinkLabel(aNode))) {
      this.shouldDisplay = false;
      return;
    }

    let editFlags = SpellCheckHelper.isEditable(this.target, window);
    this.browser = this.target.ownerDocument.defaultView
                              .QueryInterface(Ci.nsIInterfaceRequestor)
                              .getInterface(Ci.nsIWebNavigation)
                              .QueryInterface(Ci.nsIDocShell)
                              .chromeEventHandler;
    this.principal = this.target.ownerDocument.nodePrincipal;

    this.autoDownload = Services.prefs.getBoolPref("browser.download.useDownloadDir");

    // Check if we are in a synthetic document (stand alone image, video, etc.).
    this.inSyntheticDoc = this.target.ownerDocument.mozSyntheticDocument;
    // First, do checks for nodes that never have children.
    if (this.target.nodeType == Node.ELEMENT_NODE) {
      // See if the user clicked on an image.
      if (this.target instanceof Ci.nsIImageLoadingContent &&
          this.target.currentURI) {
        this.onImage = true;

        var request =
          this.target.getRequest(Ci.nsIImageLoadingContent.CURRENT_REQUEST);
        if (request && (request.imageStatus & request.STATUS_SIZE_AVAILABLE))
          this.onLoadedImage = true;
        if (request &&
            (request.imageStatus & request.STATUS_LOAD_COMPLETE) &&
            !(request.imageStatus & request.STATUS_ERROR)) {
          this.onCompletedImage = true;
        }

        this.mediaURL = this.target.currentURI.spec;

        if (this.target.ownerDocument instanceof ImageDocument)
          this.onStandaloneImage = true;
      }
      else if (this.target instanceof HTMLCanvasElement) {
        this.onCanvas = true;
      }
      else if (this.target instanceof HTMLVideoElement) {
        // Gecko always creates a HTMLVideoElement when loading an ogg file
        // directly. If the media is actually audio, be smarter and provide
        // a context menu with audio operations.
        if (this.target.readyState >= this.target.HAVE_METADATA &&
            (this.target.videoWidth == 0 || this.target.videoHeight == 0))
          this.onAudio = true;
        else
          this.onVideo = true;

        this.mediaURL = this.target.currentSrc || this.target.src;
      }
      else if (this.target instanceof HTMLAudioElement) {
        this.onAudio = true;
        this.mediaURL = this.target.currentSrc || this.target.src;
      }
      else if (editFlags & (SpellCheckHelper.INPUT | SpellCheckHelper.TEXTAREA)) {
        this.onTextInput = (editFlags & SpellCheckHelper.TEXTINPUT) !== 0;
        this.onNumeric = (editFlags & SpellCheckHelper.NUMERIC) !== 0;
        this.onEditableArea = (editFlags & SpellCheckHelper.EDITABLE) !== 0;
        this.onPassword = (editFlags & SpellCheckHelper.PASSWORD) !== 0;
        if (this.onEditableArea) {
          InlineSpellCheckerUI.init(this.target.editor);
          InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
        }
        this.onKeywordField = (editFlags & SpellCheckHelper.KEYWORD);
      }
      else if ( this.target instanceof HTMLHtmlElement ) {
        // pages with multiple <body>s are lame. we'll teach them a lesson.
        var bodyElt = this.target.ownerDocument.body;
        if (bodyElt) {
          var computedURL = this.getComputedURL(bodyElt, "background-image");
          if (computedURL) {
            this.hasBGImage = true;
            this.bgImageURL = makeURLAbsolute(bodyElt.baseURI, computedURL);
          }
        }
      }
      else if ("HTTPIndex" in content &&
               content.HTTPIndex instanceof Ci.nsIHTTPIndex) {
        this.inDirList = true;
        // Bubble outward till we get to an element with URL attribute
        // (which should be the href).
        var root = this.target;
        while (root && !this.link) {
          if (root.tagName == "tree") {
            // Hit root of tree; must have clicked in empty space;
            // thus, no link.
            break;
          }

          if (root.getAttribute("URL")) {
            // Build pseudo link object so link-related functions work.
            this.onLink = true;
            this.link = {href: root.getAttribute("URL"),
                         getAttribute: function(attr) {
                           if (attr == "title") {
                             return root.firstChild.firstChild.getAttribute("label");
                           } else {
                             return "";
                           }
                         }
                        };
            this.linkURL = this.getLinkURL();
            this.linkURI = this.getLinkURI();
            this.linkProtocol = this.getLinkProtocol();
            this.onMailtoLink = (this.linkProtocol == "mailto");

            // If element is a directory, then you can't save it.
            this.onSaveableLink = root.getAttribute("container") != "true";
          }
          else {
            root = root.parentNode;
          }
        }
      }

      this.canSpellCheck = this._isSpellCheckEnabled(this.target);
    }
    else if (this.target.nodeType == Node.TEXT_NODE) {
      // For text nodes, look at the parent node to determine the spellcheck attribute.
      this.canSpellCheck = this.target.parentNode &&
                           this._isSpellCheckEnabled(this.target);
    }

    // We have meta data on images.
    this.onMetaDataItem = this.onImage;

    // Bubble out, looking for items of interest
    const NS_MathML = "http://www.w3.org/1998/Math/MathML";
    const XMLNS = "http://www.w3.org/XML/1998/namespace";
    var elem = this.target;
    while (elem) {
      if (elem.nodeType == Node.ELEMENT_NODE) {
        // Link?
        if (!this.onLink &&
            (isXULTextLinkLabel(elem) ||
             (elem instanceof HTMLAnchorElement && elem.href) ||
             (elem instanceof HTMLAreaElement && elem.href) ||
             elem instanceof HTMLLinkElement ||
             (elem.namespaceURI == NS_MathML && elem.hasAttribute("href")) ||
             elem.getAttributeNS("http://www.w3.org/1999/xlink", "type") == "simple")) {
          // Clicked on a link.
          this.onLink = true;
          this.onMetaDataItem = true;
          // Remember corresponding element.
          this.link = elem;
          this.linkURL = this.getLinkURL();
          this.linkURI = this.getLinkURI();
          this.linkProtocol = this.getLinkProtocol();
          this.onMailtoLink = (this.linkProtocol == "mailto");
          // Remember if it is saveable.
          this.onSaveableLink = this.isLinkSaveable();
          this.linkHasNoReferrer = BrowserUtils.linkHasNoReferrer(elem);
        }

        // Text input?
        if (!this.onTextInput) {
          // Clicked on a link.
          this.onTextInput = this.isTargetATextBox(elem);
        }

        // Metadata item?
        if (!this.onMetaDataItem) {
          // We currently display metadata on anything which fits
          // the below test.
          if ((elem instanceof HTMLQuoteElement && elem.cite) ||
              (elem instanceof HTMLTableElement && elem.summary) ||
              (elem instanceof HTMLModElement && (elem.cite || elem.dateTime)) ||
              (elem instanceof HTMLElement && (elem.title || elem.lang)) ||
              elem.getAttributeNS(XMLNS, "lang")) {
            dump("On metadata item.\n");
            this.onMetaDataItem = true;
          }
        }

        // Background image?  Don't bother if we've already found a
        // background image further down the hierarchy.  Otherwise,
        // we look for the computed background-image style.
        if (!this.hasBGImage) {
          var bgImgUrl = this.getComputedURL(elem, "background-image");
          if (bgImgUrl) {
            this.hasBGImage = true;
            this.bgImageURL = makeURLAbsolute(elem.baseURI, bgImgUrl);
          }
        }
      }
      elem = elem.parentNode;
    }

    // See if the user clicked on MathML
    if ((this.target.nodeType == Node.TEXT_NODE &&
         this.target.parentNode.namespaceURI == NS_MathML) ||
        (this.target.namespaceURI == NS_MathML))
      this.onMathML = true;

    // See if the user clicked in a frame.
    var docDefaultView = this.target.ownerDocument.defaultView;
    if (docDefaultView != docDefaultView.top)
      this.inFrame = true;

    // if the document is editable, show context menu like in text inputs
    if (!this.onEditableArea) {
      if (editFlags & SpellCheckHelper.CONTENTEDITABLE) {
        // If this.onEditableArea is false but editFlags is CONTENTEDITABLE,
        // then the document itself must be editable.
        this.onTextInput       = true;
        this.onKeywordField    = false;
        this.onImage           = false;
        this.onLoadedImage     = false;
        this.onCompletedImage  = false;
        this.onMathML          = false;
        this.inFrame           = false;
        this.hasBGImage        = false;
        this.onEditableArea    = true;
        var win = this.target.ownerDocument.defaultView;
        var editingSession = win.QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIWebNavigation)
                                .QueryInterface(Ci.nsIInterfaceRequestor)
                                .getInterface(Ci.nsIEditingSession);
        InlineSpellCheckerUI.init(editingSession.getEditorForWindow(win));
        InlineSpellCheckerUI.initFromEvent(aRangeParent, aRangeOffset);
        var canSpell = InlineSpellCheckerUI.canSpellCheck && this.canSpellCheck;
        this.showItem("spell-check-enabled", canSpell);
        this.showItem("spell-separator", canSpell);
      }
    }

    function isXULTextLinkLabel(node) {
      return node.namespaceURI == xulNS &&
             node.tagName == "label" &&
             node.classList.contains('text-link') &&
             node.href;
    }
  },