mirror of
				https://github.com/mozilla/gecko-dev.git
				synced 2025-11-04 02:09:05 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			402 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			402 lines
		
	
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/* 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/. */
 | 
						|
 | 
						|
const lazy = {};
 | 
						|
 | 
						|
ChromeUtils.defineESModuleGetters(lazy, {
 | 
						|
  E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
 | 
						|
  PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
 | 
						|
  setTimeout: "resource://gre/modules/Timer.sys.mjs",
 | 
						|
});
 | 
						|
 | 
						|
export class PageInfoChild extends JSWindowActorChild {
 | 
						|
  async receiveMessage(message) {
 | 
						|
    let window = this.contentWindow;
 | 
						|
    let document = window.document;
 | 
						|
 | 
						|
    //Handles two different types of messages: one for general info (PageInfo:getData)
 | 
						|
    //and one for media info (PageInfo:getMediaData)
 | 
						|
    switch (message.name) {
 | 
						|
      case "PageInfo:getData": {
 | 
						|
        return Promise.resolve({
 | 
						|
          metaViewRows: this.getMetaInfo(document),
 | 
						|
          docInfo: this.getDocumentInfo(document),
 | 
						|
          windowInfo: this.getWindowInfo(window),
 | 
						|
        });
 | 
						|
      }
 | 
						|
      case "PageInfo:getMediaData": {
 | 
						|
        return Promise.resolve({
 | 
						|
          mediaItems: await this.getDocumentMedia(document),
 | 
						|
        });
 | 
						|
      }
 | 
						|
      case "PageInfo:getPartitionKey": {
 | 
						|
        return Promise.resolve({
 | 
						|
          partitionKey: await this.getPartitionKey(document),
 | 
						|
        });
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return undefined;
 | 
						|
  }
 | 
						|
 | 
						|
  getPartitionKey(document) {
 | 
						|
    let partitionKey = document.cookieJarSettings.partitionKey;
 | 
						|
    return partitionKey;
 | 
						|
  }
 | 
						|
 | 
						|
  getMetaInfo(document) {
 | 
						|
    let metaViewRows = [];
 | 
						|
 | 
						|
    // Get the meta tags from the page.
 | 
						|
    let metaNodes = document.getElementsByTagName("meta");
 | 
						|
 | 
						|
    for (let metaNode of metaNodes) {
 | 
						|
      metaViewRows.push([
 | 
						|
        metaNode.name ||
 | 
						|
          metaNode.httpEquiv ||
 | 
						|
          metaNode.getAttribute("property"),
 | 
						|
        metaNode.content,
 | 
						|
      ]);
 | 
						|
    }
 | 
						|
 | 
						|
    return metaViewRows;
 | 
						|
  }
 | 
						|
 | 
						|
  getWindowInfo(window) {
 | 
						|
    let windowInfo = {};
 | 
						|
    windowInfo.isTopWindow = window == window.top;
 | 
						|
 | 
						|
    let hostName = null;
 | 
						|
    try {
 | 
						|
      hostName = Services.io.newURI(window.location.href).displayHost;
 | 
						|
    } catch (exception) {}
 | 
						|
 | 
						|
    windowInfo.hostName = hostName;
 | 
						|
    return windowInfo;
 | 
						|
  }
 | 
						|
 | 
						|
  getDocumentInfo(document) {
 | 
						|
    let docInfo = {};
 | 
						|
    docInfo.title = document.title;
 | 
						|
    docInfo.location = document.location.toString();
 | 
						|
    try {
 | 
						|
      docInfo.location = Services.io.newURI(
 | 
						|
        document.location.toString()
 | 
						|
      ).displaySpec;
 | 
						|
    } catch (exception) {}
 | 
						|
    docInfo.referrer = document.referrer;
 | 
						|
    try {
 | 
						|
      if (document.referrer) {
 | 
						|
        docInfo.referrer = Services.io.newURI(document.referrer).displaySpec;
 | 
						|
      }
 | 
						|
    } catch (exception) {}
 | 
						|
    docInfo.compatMode = document.compatMode;
 | 
						|
    docInfo.contentType = document.contentType;
 | 
						|
    docInfo.characterSet = document.characterSet;
 | 
						|
    docInfo.lastModified = document.lastModified;
 | 
						|
    docInfo.principal = document.nodePrincipal;
 | 
						|
    docInfo.cookieJarSettings = lazy.E10SUtils.serializeCookieJarSettings(
 | 
						|
      document.cookieJarSettings
 | 
						|
    );
 | 
						|
 | 
						|
    let documentURIObject = {};
 | 
						|
    documentURIObject.spec = document.documentURIObject.spec;
 | 
						|
    docInfo.documentURIObject = documentURIObject;
 | 
						|
 | 
						|
    docInfo.isContentWindowPrivate =
 | 
						|
      lazy.PrivateBrowsingUtils.isContentWindowPrivate(document.ownerGlobal);
 | 
						|
 | 
						|
    return docInfo;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Returns an array that stores all mediaItems found in the document
 | 
						|
   * Calls getMediaItems for all nodes within the constructed tree walker and forms
 | 
						|
   * resulting array.
 | 
						|
   */
 | 
						|
  async getDocumentMedia(document) {
 | 
						|
    let nodeCount = 0;
 | 
						|
    let content = document.ownerGlobal;
 | 
						|
    let iterator = document.createTreeWalker(
 | 
						|
      document,
 | 
						|
      content.NodeFilter.SHOW_ELEMENT
 | 
						|
    );
 | 
						|
 | 
						|
    let totalMediaItems = [];
 | 
						|
 | 
						|
    while (iterator.nextNode()) {
 | 
						|
      let mediaItems = this.getMediaItems(document, iterator.currentNode);
 | 
						|
 | 
						|
      if (++nodeCount % 500 == 0) {
 | 
						|
        // setTimeout every 500 elements so we don't keep blocking the content process.
 | 
						|
        await new Promise(resolve => lazy.setTimeout(resolve, 10));
 | 
						|
      }
 | 
						|
      totalMediaItems.push(...mediaItems);
 | 
						|
    }
 | 
						|
 | 
						|
    return totalMediaItems;
 | 
						|
  }
 | 
						|
 | 
						|
  getMediaItems(document, elem) {
 | 
						|
    // Check for images defined in CSS (e.g. background, borders)
 | 
						|
    let computedStyle = elem.ownerGlobal.getComputedStyle(elem);
 | 
						|
    // A node can have multiple media items associated with it - for example,
 | 
						|
    // multiple background images.
 | 
						|
    let mediaItems = [];
 | 
						|
    let content = document.ownerGlobal;
 | 
						|
 | 
						|
    let addMedia = (url, type, alt, el, isBg, altNotProvided = false) => {
 | 
						|
      let element = this.serializeElementInfo(document, url, el, isBg);
 | 
						|
      mediaItems.push({
 | 
						|
        url,
 | 
						|
        type,
 | 
						|
        alt,
 | 
						|
        altNotProvided,
 | 
						|
        element,
 | 
						|
        isBg,
 | 
						|
      });
 | 
						|
    };
 | 
						|
 | 
						|
    if (computedStyle) {
 | 
						|
      let addImgFunc = (type, urls) => {
 | 
						|
        for (let url of urls) {
 | 
						|
          addMedia(url, type, "", elem, true, true);
 | 
						|
        }
 | 
						|
      };
 | 
						|
      // FIXME: This is missing properties. See the implementation of
 | 
						|
      // getCSSImageURLs for a list of properties.
 | 
						|
      //
 | 
						|
      // If you don't care about the message you can also pass "all" here and
 | 
						|
      // get all the ones the browser knows about.
 | 
						|
      addImgFunc("bg-img", computedStyle.getCSSImageURLs("background-image"));
 | 
						|
      addImgFunc(
 | 
						|
        "border-img",
 | 
						|
        computedStyle.getCSSImageURLs("border-image-source")
 | 
						|
      );
 | 
						|
      addImgFunc("list-img", computedStyle.getCSSImageURLs("list-style-image"));
 | 
						|
      addImgFunc("cursor", computedStyle.getCSSImageURLs("cursor"));
 | 
						|
    }
 | 
						|
 | 
						|
    // One swi^H^H^Hif-else to rule them all.
 | 
						|
    if (content.HTMLImageElement.isInstance(elem)) {
 | 
						|
      addMedia(
 | 
						|
        elem.currentSrc,
 | 
						|
        "img",
 | 
						|
        elem.getAttribute("alt"),
 | 
						|
        elem,
 | 
						|
        false,
 | 
						|
        !elem.hasAttribute("alt")
 | 
						|
      );
 | 
						|
    } else if (content.SVGImageElement.isInstance(elem)) {
 | 
						|
      try {
 | 
						|
        // Note: makeURLAbsolute will throw if either the baseURI is not a valid URI
 | 
						|
        //       or the URI formed from the baseURI and the URL is not a valid URI.
 | 
						|
        if (elem.href.baseVal) {
 | 
						|
          let href = Services.io.newURI(
 | 
						|
            elem.href.baseVal,
 | 
						|
            null,
 | 
						|
            Services.io.newURI(elem.baseURI)
 | 
						|
          ).spec;
 | 
						|
          addMedia(href, "img", "", elem, false);
 | 
						|
        }
 | 
						|
      } catch (e) {}
 | 
						|
    } else if (content.HTMLVideoElement.isInstance(elem)) {
 | 
						|
      addMedia(elem.currentSrc, "video", "", elem, false);
 | 
						|
    } else if (content.HTMLAudioElement.isInstance(elem)) {
 | 
						|
      addMedia(elem.currentSrc, "audio", "", elem, false);
 | 
						|
    } else if (content.HTMLLinkElement.isInstance(elem)) {
 | 
						|
      if (elem.rel && /\bicon\b/i.test(elem.rel)) {
 | 
						|
        addMedia(elem.href, "link", "", elem, false);
 | 
						|
      }
 | 
						|
    } else if (
 | 
						|
      content.HTMLInputElement.isInstance(elem) ||
 | 
						|
      content.HTMLButtonElement.isInstance(elem)
 | 
						|
    ) {
 | 
						|
      if (elem.type.toLowerCase() == "image") {
 | 
						|
        addMedia(
 | 
						|
          elem.src,
 | 
						|
          "input",
 | 
						|
          elem.getAttribute("alt"),
 | 
						|
          elem,
 | 
						|
          false,
 | 
						|
          !elem.hasAttribute("alt")
 | 
						|
        );
 | 
						|
      }
 | 
						|
    } else if (content.HTMLObjectElement.isInstance(elem)) {
 | 
						|
      addMedia(elem.data, "object", this.getValueText(elem), elem, false);
 | 
						|
    } else if (content.HTMLEmbedElement.isInstance(elem)) {
 | 
						|
      addMedia(elem.src, "embed", "", elem, false);
 | 
						|
    }
 | 
						|
 | 
						|
    return mediaItems;
 | 
						|
  }
 | 
						|
 | 
						|
  /**
 | 
						|
   * Set up a JSON element object with all the instanceOf and other infomation that
 | 
						|
   * makePreview in pageInfo.js uses to figure out how to display the preview.
 | 
						|
   */
 | 
						|
 | 
						|
  serializeElementInfo(document, url, item, isBG) {
 | 
						|
    let result = {};
 | 
						|
    let content = document.ownerGlobal;
 | 
						|
 | 
						|
    let imageText;
 | 
						|
    if (
 | 
						|
      !isBG &&
 | 
						|
      !content.SVGImageElement.isInstance(item) &&
 | 
						|
      !content.ImageDocument.isInstance(document)
 | 
						|
    ) {
 | 
						|
      imageText = item.title || item.alt;
 | 
						|
 | 
						|
      if (!imageText && !content.HTMLImageElement.isInstance(item)) {
 | 
						|
        imageText = this.getValueText(item);
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    result.imageText = imageText;
 | 
						|
    result.longDesc = item.longDesc;
 | 
						|
    result.numFrames = 1;
 | 
						|
 | 
						|
    if (
 | 
						|
      content.HTMLObjectElement.isInstance(item) ||
 | 
						|
      content.HTMLEmbedElement.isInstance(item) ||
 | 
						|
      content.HTMLLinkElement.isInstance(item)
 | 
						|
    ) {
 | 
						|
      result.mimeType = item.type;
 | 
						|
    }
 | 
						|
 | 
						|
    if (
 | 
						|
      !result.mimeType &&
 | 
						|
      !isBG &&
 | 
						|
      item instanceof Ci.nsIImageLoadingContent
 | 
						|
    ) {
 | 
						|
      // Interface for image loading content.
 | 
						|
      let imageRequest = item.getRequest(
 | 
						|
        Ci.nsIImageLoadingContent.CURRENT_REQUEST
 | 
						|
      );
 | 
						|
      if (imageRequest) {
 | 
						|
        result.mimeType = imageRequest.mimeType;
 | 
						|
        let image =
 | 
						|
          !(imageRequest.imageStatus & imageRequest.STATUS_ERROR) &&
 | 
						|
          imageRequest.image;
 | 
						|
        if (image) {
 | 
						|
          result.numFrames = image.numFrames;
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    // If we have a data url, get the MIME type from the url.
 | 
						|
    if (!result.mimeType && url.startsWith("data:")) {
 | 
						|
      let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
 | 
						|
      if (dataMimeType) {
 | 
						|
        result.mimeType = dataMimeType[1].toLowerCase();
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    result.HTMLLinkElement = content.HTMLLinkElement.isInstance(item);
 | 
						|
    result.HTMLInputElement = content.HTMLInputElement.isInstance(item);
 | 
						|
    result.HTMLImageElement = content.HTMLImageElement.isInstance(item);
 | 
						|
    result.HTMLObjectElement = content.HTMLObjectElement.isInstance(item);
 | 
						|
    result.SVGImageElement = content.SVGImageElement.isInstance(item);
 | 
						|
    result.HTMLVideoElement = content.HTMLVideoElement.isInstance(item);
 | 
						|
    result.HTMLAudioElement = content.HTMLAudioElement.isInstance(item);
 | 
						|
 | 
						|
    if (isBG) {
 | 
						|
      // Items that are showing this image as a background
 | 
						|
      // image might not necessarily have a width or height,
 | 
						|
      // so we'll dynamically generate an image and send up the
 | 
						|
      // natural dimensions.
 | 
						|
      let img = content.document.createElement("img");
 | 
						|
      img.src = url;
 | 
						|
      result.naturalWidth = img.naturalWidth;
 | 
						|
      result.naturalHeight = img.naturalHeight;
 | 
						|
    } else if (!content.SVGImageElement.isInstance(item)) {
 | 
						|
      // SVG items do not have integer values for height or width,
 | 
						|
      // so we must handle them differently in order to correctly
 | 
						|
      // serialize
 | 
						|
 | 
						|
      // Otherwise, we can use the current width and height
 | 
						|
      // of the image.
 | 
						|
      result.width = item.width;
 | 
						|
      result.height = item.height;
 | 
						|
    }
 | 
						|
 | 
						|
    if (content.SVGImageElement.isInstance(item)) {
 | 
						|
      result.SVGImageElementWidth = item.width.baseVal.value;
 | 
						|
      result.SVGImageElementHeight = item.height.baseVal.value;
 | 
						|
    }
 | 
						|
 | 
						|
    result.baseURI = item.baseURI;
 | 
						|
 | 
						|
    return result;
 | 
						|
  }
 | 
						|
 | 
						|
  // Other Misc Stuff
 | 
						|
  // Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
 | 
						|
  // parse a node to extract the contents of the node
 | 
						|
  getValueText(node) {
 | 
						|
    let valueText = "";
 | 
						|
    let content = node.ownerGlobal;
 | 
						|
 | 
						|
    // Form input elements don't generally contain information that is useful to our callers, so return nothing.
 | 
						|
    if (
 | 
						|
      content.HTMLInputElement.isInstance(node) ||
 | 
						|
      content.HTMLSelectElement.isInstance(node) ||
 | 
						|
      content.HTMLTextAreaElement.isInstance(node)
 | 
						|
    ) {
 | 
						|
      return valueText;
 | 
						|
    }
 | 
						|
 | 
						|
    // Otherwise recurse for each child.
 | 
						|
    let length = node.childNodes.length;
 | 
						|
 | 
						|
    for (let i = 0; i < length; i++) {
 | 
						|
      let childNode = node.childNodes[i];
 | 
						|
      let nodeType = childNode.nodeType;
 | 
						|
 | 
						|
      // Text nodes are where the goods are.
 | 
						|
      if (nodeType == content.Node.TEXT_NODE) {
 | 
						|
        valueText += " " + childNode.nodeValue;
 | 
						|
      } else if (nodeType == content.Node.ELEMENT_NODE) {
 | 
						|
        // And elements can have more text inside them.
 | 
						|
        // Images are special, we want to capture the alt text as if the image weren't there.
 | 
						|
        if (content.HTMLImageElement.isInstance(childNode)) {
 | 
						|
          valueText += " " + this.getAltText(childNode);
 | 
						|
        } else {
 | 
						|
          valueText += " " + this.getValueText(childNode);
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
 | 
						|
    return this.stripWS(valueText);
 | 
						|
  }
 | 
						|
 | 
						|
  // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
 | 
						|
  // Traverse the tree in search of an img or area element and grab its alt tag.
 | 
						|
  getAltText(node) {
 | 
						|
    let altText = "";
 | 
						|
 | 
						|
    if (node.alt) {
 | 
						|
      return node.alt;
 | 
						|
    }
 | 
						|
    let length = node.childNodes.length;
 | 
						|
    for (let i = 0; i < length; i++) {
 | 
						|
      if ((altText = this.getAltText(node.childNodes[i]) != undefined)) {
 | 
						|
        // stupid js warning...
 | 
						|
        return altText;
 | 
						|
      }
 | 
						|
    }
 | 
						|
    return "";
 | 
						|
  }
 | 
						|
 | 
						|
  // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
 | 
						|
  // Strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space.
 | 
						|
  stripWS(text) {
 | 
						|
    let middleRE = /\s+/g;
 | 
						|
    let endRE = /(^\s+)|(\s+$)/g;
 | 
						|
 | 
						|
    text = text.replace(middleRE, " ");
 | 
						|
    return text.replace(endRE, "");
 | 
						|
  }
 | 
						|
}
 |