summaryrefslogtreecommitdiff
path: root/toolkit/devtools/inspector/test/doc_frame_script.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/devtools/inspector/test/doc_frame_script.js')
-rw-r--r--toolkit/devtools/inspector/test/doc_frame_script.js308
1 files changed, 308 insertions, 0 deletions
diff --git a/toolkit/devtools/inspector/test/doc_frame_script.js b/toolkit/devtools/inspector/test/doc_frame_script.js
new file mode 100644
index 000000000..3eb534900
--- /dev/null
+++ b/toolkit/devtools/inspector/test/doc_frame_script.js
@@ -0,0 +1,308 @@
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+"use strict";
+
+// A helper frame-script for brower/devtools/inspector tests.
+//
+// Most listeners in the script expect "Test:"-namespaced messages from chrome,
+// then execute code upon receiving, and immediately send back a message.
+// This is so that chrome test code can execute code in content and wait for a
+// response.
+// Some listeners do not send a response message back.
+
+let {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+let {LayoutHelpers} = Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", {});
+let DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
+let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"]
+ .getService(Ci.mozIJSSubScriptLoader);
+let EventUtils = {};
+loader.loadSubScript("chrome://marionette/content/EventUtils.js", EventUtils);
+
+/**
+ * If the test page creates and triggeres the custom event
+ * "test-page-processing-done", then the Test:TestPageProcessingDone message
+ * will be sent to the parent process for tests to wait for this event if needed.
+ */
+addEventListener("DOMWindowCreated", () => {
+ content.addEventListener("test-page-processing-done", () => {
+ sendAsyncMessage("Test:TestPageProcessingDone");
+ }, false);
+});
+
+/**
+ * Given an actorID and connection prefix, get the corresponding actor from the
+ * debugger-server connection.
+ * @param {String} actorID
+ * @param {String} connPrefix
+ */
+function getHighlighterActor(actorID, connPrefix) {
+ let {DebuggerServer} = Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
+ if (!DebuggerServer.initialized) {
+ return;
+ }
+
+ let conn = DebuggerServer._connections[connPrefix];
+ if (!conn) {
+ return;
+ }
+
+ return conn.getActor(actorID);
+}
+
+/**
+ * Get the instance of CanvasFrameAnonymousContentHelper used by a given
+ * highlighter actor.
+ * The instance provides methods to get/set attributes/text/style on nodes of
+ * the highlighter, inserted into the nsCanvasFrame.
+ * @see /toolkit/devtools/server/actors/highlighter.js
+ * @param {String} actorID
+ * @param {String} connPrefix
+ */
+function getHighlighterCanvasFrameHelper(actorID, connPrefix) {
+ let actor = getHighlighterActor(actorID, connPrefix);
+ if (actor && actor._highlighter) {
+ return actor._highlighter.markup;
+ }
+}
+
+/**
+ * Get a value for a given attribute name, on one of the elements of the box
+ * model highlighter, given its ID.
+ * @param {Object} msg The msg.data part expects the following properties
+ * - {String} nodeID The full ID of the element to get the attribute for
+ * - {String} name The name of the attribute to get
+ * - {String} actorID The highlighter actor ID
+ * - {String} connPrefix The highlighter actor ID's connection prefix
+ * @return {String} The value, if found, null otherwise
+ */
+addMessageListener("Test:GetHighlighterAttribute", function(msg) {
+ let {nodeID, name, actorID, connPrefix} = msg.data;
+
+ let value;
+ let helper = getHighlighterCanvasFrameHelper(actorID, connPrefix);
+ if (helper) {
+ value = helper.getAttributeForElement(nodeID, name);
+ }
+
+ sendAsyncMessage("Test:GetHighlighterAttribute", value);
+});
+
+/**
+ * Get the textcontent of one of the elements of the box model highlighter,
+ * given its ID.
+ * @param {Object} msg The msg.data part expects the following properties
+ * - {String} nodeID The full ID of the element to get the attribute for
+ * - {String} actorID The highlighter actor ID
+ * - {String} connPrefix The highlighter connection prefix
+ * @return {String} The textcontent value
+ */
+addMessageListener("Test:GetHighlighterTextContent", function(msg) {
+ let {nodeID, actorID, connPrefix} = msg.data;
+
+ let value;
+ let helper = getHighlighterCanvasFrameHelper(actorID, connPrefix);
+ if (helper) {
+ value = helper.getTextContentForElement(nodeID);
+ }
+
+ sendAsyncMessage("Test:GetHighlighterTextContent", value);
+});
+
+/**
+ * Get the number of box-model highlighters created by the SelectorHighlighter
+ * @param {Object} msg The msg.data part expects the following properties:
+ * - {String} actorID The highlighter actor ID
+ * - {String} connPrefix The highlighter connection prefix
+ * @return {Number} The number of box-model highlighters created, or null if the
+ * SelectorHighlighter was not found.
+ */
+addMessageListener("Test:GetSelectorHighlighterBoxNb", function(msg) {
+ let {actorID, connPrefix} = msg.data;
+ let {_highlighter: h} = getHighlighterActor(actorID, connPrefix);
+ if (!h || !h._highlighters) {
+ sendAsyncMessage("Test:GetSelectorHighlighterBoxNb", null);
+ } else {
+ sendAsyncMessage("Test:GetSelectorHighlighterBoxNb", h._highlighters.length);
+ }
+});
+
+/**
+ * Subscribe to the box-model highlighter's update event, modify an attribute of
+ * the currently highlighted node and send a message when the highlighter has
+ * updated.
+ * @param {Object} msg The msg.data part expects the following properties
+ * - {String} the name of the attribute to be changed
+ * - {String} the new value for the attribute
+ * - {String} actorID The highlighter actor ID
+ * - {String} connPrefix The highlighter connection prefix
+ */
+addMessageListener("Test:ChangeHighlightedNodeWaitForUpdate", function(msg) {
+ // The name and value of the attribute to be changed
+ let {name, value, actorID, connPrefix} = msg.data;
+ let {_highlighter: h} = getHighlighterActor(actorID, connPrefix);
+
+ h.once("updated", () => {
+ sendAsyncMessage("Test:ChangeHighlightedNodeWaitForUpdate");
+ });
+
+ h.currentNode.setAttribute(name, value);
+});
+
+/**
+ * Subscribe to a given highlighter event and respond when the event is received.
+ * @param {Object} msg The msg.data part expects the following properties
+ * - {String} event The name of the highlighter event to listen to
+ * - {String} actorID The highlighter actor ID
+ * - {String} connPrefix The highlighter connection prefix
+ */
+addMessageListener("Test:WaitForHighlighterEvent", function(msg) {
+ let {event, actorID, connPrefix} = msg.data;
+ let {_highlighter: h} = getHighlighterActor(actorID, connPrefix);
+
+ h.once(event, () => {
+ sendAsyncMessage("Test:WaitForHighlighterEvent");
+ });
+});
+
+/**
+ * Change the zoom level of the page.
+ * Optionally subscribe to the box-model highlighter's update event and waiting
+ * for it to refresh before responding.
+ * @param {Object} msg The msg.data part expects the following properties
+ * - {Number} level The new zoom level
+ * - {String} actorID Optional. The highlighter actor ID
+ * - {String} connPrefix Optional. The highlighter connection prefix
+ */
+addMessageListener("Test:ChangeZoomLevel", function(msg) {
+ let {level, actorID, connPrefix} = msg.data;
+ dumpn("Zooming page to " + level);
+
+ if (actorID) {
+ let {_highlighter: h} = getHighlighterActor(actorID, connPrefix);
+ h.once("updated", () => {
+ sendAsyncMessage("Test:ChangeZoomLevel");
+ });
+ }
+
+ let docShell = content.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebNavigation)
+ .QueryInterface(Ci.nsIDocShell);
+ docShell.contentViewer.fullZoom = level;
+
+ if (!actorID) {
+ sendAsyncMessage("Test:ChangeZoomLevel");
+ }
+});
+
+/**
+ * Get the element at the given x/y coordinates.
+ * @param {Object} msg The msg.data part expects the following properties
+ * - {Number} x
+ * - {Number} y
+ * @return {DOMNode} The CPOW of the element
+ */
+addMessageListener("Test:ElementFromPoint", function(msg) {
+ let {x, y} = msg.data;
+ dumpn("Getting the element at " + x + "/" + y);
+
+ let helper = new LayoutHelpers(content);
+ let element = helper.getElementFromPoint(content.document, x, y);
+ sendAsyncMessage("Test:ElementFromPoint", null, {element});
+});
+
+/**
+ * Get all box-model regions' adjusted boxquads for the given element
+ * @param {Object} msg The msg.data part should contain the node selector.
+ * @return {Object} An object with each property being a box-model region, each
+ * of them being an array of objects with the p1/p2/p3/p4 properties.
+ */
+addMessageListener("Test:GetAllAdjustedQuads", function(msg) {
+ let {selector} = msg.data;
+ let node = superQuerySelector(selector);
+
+ let regions = {};
+
+ let helper = new LayoutHelpers(content);
+ for (let boxType of ["content", "padding", "border", "margin"]) {
+ regions[boxType] = helper.getAdjustedQuads(node, boxType);
+ }
+
+ sendAsyncMessage("Test:GetAllAdjustedQuads", regions);
+});
+
+/**
+ * Synthesize a mouse event on an element. This handler doesn't send a message
+ * back. Consumers should listen to specific events on the inspector/highlighter
+ * to know when the event got synthesized.
+ * @param {Object} msg The msg.data part expects the following properties:
+ * - {Number} x
+ * - {Number} y
+ * - {Boolean} center If set to true, x/y will be ignored and
+ * synthesizeMouseAtCenter will be used instead
+ * - {Object} options Other event options
+ * The msg.objects part should be the element.
+ * @param {Object} data Event detail properties:
+ */
+addMessageListener("Test:SynthesizeMouse", function(msg) {
+ let {node} = msg.objects;
+ let {x, y, center, options, selector} = msg.data;
+
+ if (!node && selector) {
+ node = superQuerySelector(selector);
+ }
+
+ if (center) {
+ EventUtils.synthesizeMouseAtCenter(node, options, node.ownerDocument.defaultView);
+ } else {
+ EventUtils.synthesizeMouse(node, x, y, options, node.ownerDocument.defaultView);
+ }
+
+ // Most consumers won't need to listen to this message, unless they want to
+ // wait for the mouse event to be synthesized and don't have another event
+ // to listen to instead.
+ sendAsyncMessage("Test:SynthesizeMouse");
+});
+
+/**
+ * Check that an element currently has a pseudo-class lock.
+ * @param {Object} msg The msg.data part expects the following properties:
+ * - {String} pseudo The pseudoclass to check for
+ * The msg.objects part should be the element.
+ * @param {Object}
+ * @return {Boolean}
+ */
+addMessageListener("Test:HasPseudoClassLock", function(msg) {
+ let {node} = msg.objects;
+ let {pseudo} = msg.data
+ sendAsyncMessage("Test:HasPseudoClassLock", DOMUtils.hasPseudoClassLock(node, pseudo));
+});
+
+/**
+ * Like document.querySelector but can go into iframes too.
+ * ".container iframe || .sub-container div" will first try to find the node
+ * matched by ".container iframe" in the root document, then try to get the
+ * content document inside it, and then try to match ".sub-container div" inside
+ * this document.
+ * Any selector coming before the || separator *MUST* match a frame node.
+ * @param {String} superSelector.
+ * @return {DOMNode} The node, or null if not found.
+ */
+function superQuerySelector(superSelector, root=content.document) {
+ let frameIndex = superSelector.indexOf("||");
+ if (frameIndex === -1) {
+ return root.querySelector(superSelector);
+ } else {
+ let rootSelector = superSelector.substring(0, frameIndex).trim();
+ let childSelector = superSelector.substring(frameIndex+2).trim();
+ root = root.querySelector(rootSelector);
+ if (!root || !root.contentWindow) {
+ return null;
+ }
+
+ return superQuerySelector(childSelector, root.contentWindow.document);
+ }
+}
+
+let dumpn = msg => dump(msg + "\n");