1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 ChromeUtils.defineESModuleGetters(lazy, {
8 E10SUtils: "resource://gre/modules/E10SUtils.sys.mjs",
9 PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.sys.mjs",
10 setTimeout: "resource://gre/modules/Timer.sys.mjs",
13 export class PageInfoChild extends JSWindowActorChild {
14 async receiveMessage(message) {
15 let window = this.contentWindow;
16 let document = window.document;
18 //Handles two different types of messages: one for general info (PageInfo:getData)
19 //and one for media info (PageInfo:getMediaData)
20 switch (message.name) {
21 case "PageInfo:getData": {
22 return Promise.resolve({
23 metaViewRows: this.getMetaInfo(document),
24 docInfo: this.getDocumentInfo(document),
25 windowInfo: this.getWindowInfo(window),
28 case "PageInfo:getMediaData": {
29 return Promise.resolve({
30 mediaItems: await this.getDocumentMedia(document),
33 case "PageInfo:getPartitionKey": {
34 return Promise.resolve({
35 partitionKey: await this.getPartitionKey(document),
43 getPartitionKey(document) {
44 let partitionKey = document.cookieJarSettings.partitionKey;
48 getMetaInfo(document) {
49 let metaViewRows = [];
51 // Get the meta tags from the page.
52 let metaNodes = document.getElementsByTagName("meta");
54 for (let metaNode of metaNodes) {
58 metaNode.getAttribute("property"),
66 getWindowInfo(window) {
68 windowInfo.isTopWindow = window == window.top;
72 hostName = Services.io.newURI(window.location.href).displayHost;
73 } catch (exception) {}
75 windowInfo.hostName = hostName;
79 getDocumentInfo(document) {
81 docInfo.title = document.title;
82 docInfo.location = document.location.toString();
84 docInfo.location = Services.io.newURI(
85 document.location.toString()
87 } catch (exception) {}
88 docInfo.referrer = document.referrer;
90 if (document.referrer) {
91 docInfo.referrer = Services.io.newURI(document.referrer).displaySpec;
93 } catch (exception) {}
94 docInfo.compatMode = document.compatMode;
95 docInfo.contentType = document.contentType;
96 docInfo.characterSet = document.characterSet;
97 docInfo.lastModified = document.lastModified;
98 docInfo.principal = document.nodePrincipal;
99 docInfo.cookieJarSettings = lazy.E10SUtils.serializeCookieJarSettings(
100 document.cookieJarSettings
103 let documentURIObject = {};
104 documentURIObject.spec = document.documentURIObject.spec;
105 docInfo.documentURIObject = documentURIObject;
107 docInfo.isContentWindowPrivate =
108 lazy.PrivateBrowsingUtils.isContentWindowPrivate(document.ownerGlobal);
114 * Returns an array that stores all mediaItems found in the document
115 * Calls getMediaItems for all nodes within the constructed tree walker and forms
118 async getDocumentMedia(document) {
120 let content = document.ownerGlobal;
121 let iterator = document.createTreeWalker(
123 content.NodeFilter.SHOW_ELEMENT
126 let totalMediaItems = [];
128 while (iterator.nextNode()) {
129 let mediaItems = this.getMediaItems(document, iterator.currentNode);
131 if (++nodeCount % 500 == 0) {
132 // setTimeout every 500 elements so we don't keep blocking the content process.
133 await new Promise(resolve => lazy.setTimeout(resolve, 10));
135 totalMediaItems.push(...mediaItems);
138 return totalMediaItems;
141 getMediaItems(document, elem) {
142 // Check for images defined in CSS (e.g. background, borders)
143 let computedStyle = elem.ownerGlobal.getComputedStyle(elem);
144 // A node can have multiple media items associated with it - for example,
145 // multiple background images.
147 let content = document.ownerGlobal;
149 let addMedia = (url, type, alt, el, isBg, altNotProvided = false) => {
150 let element = this.serializeElementInfo(document, url, el, isBg);
162 let addImgFunc = (type, urls) => {
163 for (let url of urls) {
164 addMedia(url, type, "", elem, true, true);
167 // FIXME: This is missing properties. See the implementation of
168 // getCSSImageURLs for a list of properties.
170 // If you don't care about the message you can also pass "all" here and
171 // get all the ones the browser knows about.
172 addImgFunc("bg-img", computedStyle.getCSSImageURLs("background-image"));
175 computedStyle.getCSSImageURLs("border-image-source")
177 addImgFunc("list-img", computedStyle.getCSSImageURLs("list-style-image"));
178 addImgFunc("cursor", computedStyle.getCSSImageURLs("cursor"));
181 // One swi^H^H^Hif-else to rule them all.
182 if (content.HTMLImageElement.isInstance(elem)) {
186 elem.getAttribute("alt"),
189 !elem.hasAttribute("alt")
191 } else if (content.SVGImageElement.isInstance(elem)) {
193 // Note: makeURLAbsolute will throw if either the baseURI is not a valid URI
194 // or the URI formed from the baseURI and the URL is not a valid URI.
195 if (elem.href.baseVal) {
196 let href = Services.io.newURI(
199 Services.io.newURI(elem.baseURI)
201 addMedia(href, "img", "", elem, false);
204 } else if (content.HTMLVideoElement.isInstance(elem)) {
205 addMedia(elem.currentSrc, "video", "", elem, false);
206 } else if (content.HTMLAudioElement.isInstance(elem)) {
207 addMedia(elem.currentSrc, "audio", "", elem, false);
208 } else if (content.HTMLLinkElement.isInstance(elem)) {
209 if (elem.rel && /\bicon\b/i.test(elem.rel)) {
210 addMedia(elem.href, "link", "", elem, false);
213 content.HTMLInputElement.isInstance(elem) ||
214 content.HTMLButtonElement.isInstance(elem)
216 if (elem.type.toLowerCase() == "image") {
220 elem.getAttribute("alt"),
223 !elem.hasAttribute("alt")
226 } else if (content.HTMLObjectElement.isInstance(elem)) {
227 addMedia(elem.data, "object", this.getValueText(elem), elem, false);
228 } else if (content.HTMLEmbedElement.isInstance(elem)) {
229 addMedia(elem.src, "embed", "", elem, false);
236 * Set up a JSON element object with all the instanceOf and other infomation that
237 * makePreview in pageInfo.js uses to figure out how to display the preview.
240 serializeElementInfo(document, url, item, isBG) {
242 let content = document.ownerGlobal;
247 !content.SVGImageElement.isInstance(item) &&
248 !content.ImageDocument.isInstance(document)
250 imageText = item.title || item.alt;
252 if (!imageText && !content.HTMLImageElement.isInstance(item)) {
253 imageText = this.getValueText(item);
257 result.imageText = imageText;
258 result.longDesc = item.longDesc;
259 result.numFrames = 1;
262 content.HTMLObjectElement.isInstance(item) ||
263 content.HTMLEmbedElement.isInstance(item) ||
264 content.HTMLLinkElement.isInstance(item)
266 result.mimeType = item.type;
272 item instanceof Ci.nsIImageLoadingContent
274 // Interface for image loading content.
275 let imageRequest = item.getRequest(
276 Ci.nsIImageLoadingContent.CURRENT_REQUEST
279 result.mimeType = imageRequest.mimeType;
281 !(imageRequest.imageStatus & imageRequest.STATUS_ERROR) &&
284 result.numFrames = image.numFrames;
289 // If we have a data url, get the MIME type from the url.
290 if (!result.mimeType && url.startsWith("data:")) {
291 let dataMimeType = /^data:(image\/[^;,]+)/i.exec(url);
293 result.mimeType = dataMimeType[1].toLowerCase();
297 result.HTMLLinkElement = content.HTMLLinkElement.isInstance(item);
298 result.HTMLInputElement = content.HTMLInputElement.isInstance(item);
299 result.HTMLImageElement = content.HTMLImageElement.isInstance(item);
300 result.HTMLObjectElement = content.HTMLObjectElement.isInstance(item);
301 result.SVGImageElement = content.SVGImageElement.isInstance(item);
302 result.HTMLVideoElement = content.HTMLVideoElement.isInstance(item);
303 result.HTMLAudioElement = content.HTMLAudioElement.isInstance(item);
306 // Items that are showing this image as a background
307 // image might not necessarily have a width or height,
308 // so we'll dynamically generate an image and send up the
309 // natural dimensions.
310 let img = content.document.createElement("img");
312 result.naturalWidth = img.naturalWidth;
313 result.naturalHeight = img.naturalHeight;
314 } else if (!content.SVGImageElement.isInstance(item)) {
315 // SVG items do not have integer values for height or width,
316 // so we must handle them differently in order to correctly
319 // Otherwise, we can use the current width and height
321 result.width = item.width;
322 result.height = item.height;
325 if (content.SVGImageElement.isInstance(item)) {
326 result.SVGImageElementWidth = item.width.baseVal.value;
327 result.SVGImageElementHeight = item.height.baseVal.value;
330 result.baseURI = item.baseURI;
336 // Modified from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html
337 // parse a node to extract the contents of the node
340 let content = node.ownerGlobal;
342 // Form input elements don't generally contain information that is useful to our callers, so return nothing.
344 content.HTMLInputElement.isInstance(node) ||
345 content.HTMLSelectElement.isInstance(node) ||
346 content.HTMLTextAreaElement.isInstance(node)
351 // Otherwise recurse for each child.
352 let length = node.childNodes.length;
354 for (let i = 0; i < length; i++) {
355 let childNode = node.childNodes[i];
356 let nodeType = childNode.nodeType;
358 // Text nodes are where the goods are.
359 if (nodeType == content.Node.TEXT_NODE) {
360 valueText += " " + childNode.nodeValue;
361 } else if (nodeType == content.Node.ELEMENT_NODE) {
362 // And elements can have more text inside them.
363 // Images are special, we want to capture the alt text as if the image weren't there.
364 if (content.HTMLImageElement.isInstance(childNode)) {
365 valueText += " " + this.getAltText(childNode);
367 valueText += " " + this.getValueText(childNode);
372 return this.stripWS(valueText);
375 // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
376 // Traverse the tree in search of an img or area element and grab its alt tag.
383 let length = node.childNodes.length;
384 for (let i = 0; i < length; i++) {
385 if ((altText = this.getAltText(node.childNodes[i]) != undefined)) {
386 // stupid js warning...
393 // Copied from the Links Panel v2.3, http://segment7.net/mozilla/links/links.html.
394 // Strip leading and trailing whitespace, and replace multiple consecutive whitespace characters with a single space.
396 let middleRE = /\s+/g;
397 let endRE = /(^\s+)|(\s+$)/g;
399 text = text.replace(middleRE, " ");
400 return text.replace(endRE, "");