123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008 |
- /* 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/. */
- "use strict";
- require("devtools/shared/fronts/styles");
- require("devtools/shared/fronts/highlighters");
- require("devtools/shared/fronts/layout");
- const { SimpleStringFront } = require("devtools/shared/fronts/string");
- const {
- Front,
- FrontClassWithSpec,
- custom,
- preEvent,
- types
- } = require("devtools/shared/protocol.js");
- const {
- inspectorSpec,
- nodeSpec,
- nodeListSpec,
- walkerSpec
- } = require("devtools/shared/specs/inspector");
- const promise = require("promise");
- const defer = require("devtools/shared/defer");
- const { Task } = require("devtools/shared/task");
- const { Class } = require("sdk/core/heritage");
- const events = require("sdk/event/core");
- const object = require("sdk/util/object");
- const nodeConstants = require("devtools/shared/dom-node-constants.js");
- loader.lazyRequireGetter(this, "CommandUtils",
- "devtools/client/shared/developer-toolbar", true);
- const HIDDEN_CLASS = "__fx-devtools-hide-shortcut__";
- /**
- * Convenience API for building a list of attribute modifications
- * for the `modifyAttributes` request.
- */
- const AttributeModificationList = Class({
- initialize: function (node) {
- this.node = node;
- this.modifications = [];
- },
- apply: function () {
- let ret = this.node.modifyAttributes(this.modifications);
- return ret;
- },
- destroy: function () {
- this.node = null;
- this.modification = null;
- },
- setAttributeNS: function (ns, name, value) {
- this.modifications.push({
- attributeNamespace: ns,
- attributeName: name,
- newValue: value
- });
- },
- setAttribute: function (name, value) {
- this.setAttributeNS(undefined, name, value);
- },
- removeAttributeNS: function (ns, name) {
- this.setAttributeNS(ns, name, undefined);
- },
- removeAttribute: function (name) {
- this.setAttributeNS(undefined, name, undefined);
- }
- });
- /**
- * Client side of the node actor.
- *
- * Node fronts are strored in a tree that mirrors the DOM tree on the
- * server, but with a few key differences:
- * - Not all children will be necessary loaded for each node.
- * - The order of children isn't guaranteed to be the same as the DOM.
- * Children are stored in a doubly-linked list, to make addition/removal
- * and traversal quick.
- *
- * Due to the order/incompleteness of the child list, it is safe to use
- * the parent node from clients, but the `children` request should be used
- * to traverse children.
- */
- const NodeFront = FrontClassWithSpec(nodeSpec, {
- initialize: function (conn, form, detail, ctx) {
- // The parent node
- this._parent = null;
- // The first child of this node.
- this._child = null;
- // The next sibling of this node.
- this._next = null;
- // The previous sibling of this node.
- this._prev = null;
- Front.prototype.initialize.call(this, conn, form, detail, ctx);
- },
- /**
- * Destroy a node front. The node must have been removed from the
- * ownership tree before this is called, unless the whole walker front
- * is being destroyed.
- */
- destroy: function () {
- Front.prototype.destroy.call(this);
- },
- // Update the object given a form representation off the wire.
- form: function (form, detail, ctx) {
- if (detail === "actorid") {
- this.actorID = form;
- return;
- }
- // backward-compatibility: shortValue indicates we are connected to old server
- if (form.shortValue) {
- // If the value is not complete, set nodeValue to null, it will be fetched
- // when calling getNodeValue()
- form.nodeValue = form.incompleteValue ? null : form.shortValue;
- }
- // Shallow copy of the form. We could just store a reference, but
- // eventually we'll want to update some of the data.
- this._form = object.merge(form);
- this._form.attrs = this._form.attrs ? this._form.attrs.slice() : [];
- if (form.parent) {
- // Get the owner actor for this actor (the walker), and find the
- // parent node of this actor from it, creating a standin node if
- // necessary.
- let parentNodeFront = ctx.marshallPool().ensureParentFront(form.parent);
- this.reparent(parentNodeFront);
- }
- if (form.inlineTextChild) {
- this.inlineTextChild =
- types.getType("domnode").read(form.inlineTextChild, ctx);
- } else {
- this.inlineTextChild = undefined;
- }
- },
- /**
- * Returns the parent NodeFront for this NodeFront.
- */
- parentNode: function () {
- return this._parent;
- },
- /**
- * Process a mutation entry as returned from the walker's `getMutations`
- * request. Only tries to handle changes of the node's contents
- * themselves (character data and attribute changes), the walker itself
- * will keep the ownership tree up to date.
- */
- updateMutation: function (change) {
- if (change.type === "attributes") {
- // We'll need to lazily reparse the attributes after this change.
- this._attrMap = undefined;
- // Update any already-existing attributes.
- let found = false;
- for (let i = 0; i < this.attributes.length; i++) {
- let attr = this.attributes[i];
- if (attr.name == change.attributeName &&
- attr.namespace == change.attributeNamespace) {
- if (change.newValue !== null) {
- attr.value = change.newValue;
- } else {
- this.attributes.splice(i, 1);
- }
- found = true;
- break;
- }
- }
- // This is a new attribute. The null check is because of Bug 1192270,
- // in the case of a newly added then removed attribute
- if (!found && change.newValue !== null) {
- this.attributes.push({
- name: change.attributeName,
- namespace: change.attributeNamespace,
- value: change.newValue
- });
- }
- } else if (change.type === "characterData") {
- this._form.nodeValue = change.newValue;
- } else if (change.type === "pseudoClassLock") {
- this._form.pseudoClassLocks = change.pseudoClassLocks;
- } else if (change.type === "events") {
- this._form.hasEventListeners = change.hasEventListeners;
- }
- },
- // Some accessors to make NodeFront feel more like an nsIDOMNode
- get id() {
- return this.getAttribute("id");
- },
- get nodeType() {
- return this._form.nodeType;
- },
- get namespaceURI() {
- return this._form.namespaceURI;
- },
- get nodeName() {
- return this._form.nodeName;
- },
- get displayName() {
- let {displayName, nodeName} = this._form;
- // Keep `nodeName.toLowerCase()` for backward compatibility
- return displayName || nodeName.toLowerCase();
- },
- get doctypeString() {
- return "<!DOCTYPE " + this._form.name +
- (this._form.publicId ? " PUBLIC \"" + this._form.publicId + "\"" : "") +
- (this._form.systemId ? " \"" + this._form.systemId + "\"" : "") +
- ">";
- },
- get baseURI() {
- return this._form.baseURI;
- },
- get className() {
- return this.getAttribute("class") || "";
- },
- get hasChildren() {
- return this._form.numChildren > 0;
- },
- get numChildren() {
- return this._form.numChildren;
- },
- get hasEventListeners() {
- return this._form.hasEventListeners;
- },
- get isBeforePseudoElement() {
- return this._form.isBeforePseudoElement;
- },
- get isAfterPseudoElement() {
- return this._form.isAfterPseudoElement;
- },
- get isPseudoElement() {
- return this.isBeforePseudoElement || this.isAfterPseudoElement;
- },
- get isAnonymous() {
- return this._form.isAnonymous;
- },
- get isInHTMLDocument() {
- return this._form.isInHTMLDocument;
- },
- get tagName() {
- return this.nodeType === nodeConstants.ELEMENT_NODE ? this.nodeName : null;
- },
- get isDocumentElement() {
- return !!this._form.isDocumentElement;
- },
- // doctype properties
- get name() {
- return this._form.name;
- },
- get publicId() {
- return this._form.publicId;
- },
- get systemId() {
- return this._form.systemId;
- },
- getAttribute: function (name) {
- let attr = this._getAttribute(name);
- return attr ? attr.value : null;
- },
- hasAttribute: function (name) {
- this._cacheAttributes();
- return (name in this._attrMap);
- },
- get hidden() {
- let cls = this.getAttribute("class");
- return cls && cls.indexOf(HIDDEN_CLASS) > -1;
- },
- get attributes() {
- return this._form.attrs;
- },
- get pseudoClassLocks() {
- return this._form.pseudoClassLocks || [];
- },
- hasPseudoClassLock: function (pseudo) {
- return this.pseudoClassLocks.some(locked => locked === pseudo);
- },
- get isDisplayed() {
- // The NodeActor's form contains the isDisplayed information as a boolean
- // starting from FF32. Before that, the property is missing
- return "isDisplayed" in this._form ? this._form.isDisplayed : true;
- },
- get isTreeDisplayed() {
- let parent = this;
- while (parent) {
- if (!parent.isDisplayed) {
- return false;
- }
- parent = parent.parentNode();
- }
- return true;
- },
- getNodeValue: custom(function () {
- // backward-compatibility: if nodevalue is null and shortValue is defined, the actual
- // value of the node needs to be fetched on the server.
- if (this._form.nodeValue === null && this._form.shortValue) {
- return this._getNodeValue();
- }
- let str = this._form.nodeValue || "";
- return promise.resolve(new SimpleStringFront(str));
- }, {
- impl: "_getNodeValue"
- }),
- // Accessors for custom form properties.
- getFormProperty: function (name) {
- return this._form.props ? this._form.props[name] : null;
- },
- hasFormProperty: function (name) {
- return this._form.props ? (name in this._form.props) : null;
- },
- get formProperties() {
- return this._form.props;
- },
- /**
- * Return a new AttributeModificationList for this node.
- */
- startModifyingAttributes: function () {
- return AttributeModificationList(this);
- },
- _cacheAttributes: function () {
- if (typeof this._attrMap != "undefined") {
- return;
- }
- this._attrMap = {};
- for (let attr of this.attributes) {
- this._attrMap[attr.name] = attr;
- }
- },
- _getAttribute: function (name) {
- this._cacheAttributes();
- return this._attrMap[name] || undefined;
- },
- /**
- * Set this node's parent. Note that the children saved in
- * this tree are unordered and incomplete, so shouldn't be used
- * instead of a `children` request.
- */
- reparent: function (parent) {
- if (this._parent === parent) {
- return;
- }
- if (this._parent && this._parent._child === this) {
- this._parent._child = this._next;
- }
- if (this._prev) {
- this._prev._next = this._next;
- }
- if (this._next) {
- this._next._prev = this._prev;
- }
- this._next = null;
- this._prev = null;
- this._parent = parent;
- if (!parent) {
- // Subtree is disconnected, we're done
- return;
- }
- this._next = parent._child;
- if (this._next) {
- this._next._prev = this;
- }
- parent._child = this;
- },
- /**
- * Return all the known children of this node.
- */
- treeChildren: function () {
- let ret = [];
- for (let child = this._child; child != null; child = child._next) {
- ret.push(child);
- }
- return ret;
- },
- /**
- * Do we use a local target?
- * Useful to know if a rawNode is available or not.
- *
- * This will, one day, be removed. External code should
- * not need to know if the target is remote or not.
- */
- isLocalToBeDeprecated: function () {
- return !!this.conn._transport._serverConnection;
- },
- /**
- * Get an nsIDOMNode for the given node front. This only works locally,
- * and is only intended as a stopgap during the transition to the remote
- * protocol. If you depend on this you're likely to break soon.
- */
- rawNode: function (rawNode) {
- if (!this.isLocalToBeDeprecated()) {
- console.warn("Tried to use rawNode on a remote connection.");
- return null;
- }
- const { DebuggerServer } = require("devtools/server/main");
- let actor = DebuggerServer._searchAllConnectionsForActor(this.actorID);
- if (!actor) {
- // Can happen if we try to get the raw node for an already-expired
- // actor.
- return null;
- }
- return actor.rawNode;
- }
- });
- exports.NodeFront = NodeFront;
- /**
- * Client side of a node list as returned by querySelectorAll()
- */
- const NodeListFront = FrontClassWithSpec(nodeListSpec, {
- initialize: function (client, form) {
- Front.prototype.initialize.call(this, client, form);
- },
- destroy: function () {
- Front.prototype.destroy.call(this);
- },
- marshallPool: function () {
- return this.parent();
- },
- // Update the object given a form representation off the wire.
- form: function (json) {
- this.length = json.length;
- },
- item: custom(function (index) {
- return this._item(index).then(response => {
- return response.node;
- });
- }, {
- impl: "_item"
- }),
- items: custom(function (start, end) {
- return this._items(start, end).then(response => {
- return response.nodes;
- });
- }, {
- impl: "_items"
- })
- });
- exports.NodeListFront = NodeListFront;
- /**
- * Client side of the DOM walker.
- */
- const WalkerFront = FrontClassWithSpec(walkerSpec, {
- // Set to true if cleanup should be requested after every mutation list.
- autoCleanup: true,
- /**
- * This is kept for backward-compatibility reasons with older remote target.
- * Targets previous to bug 916443
- */
- pick: custom(function () {
- return this._pick().then(response => {
- return response.node;
- });
- }, {impl: "_pick"}),
- initialize: function (client, form) {
- this._createRootNodePromise();
- Front.prototype.initialize.call(this, client, form);
- this._orphaned = new Set();
- this._retainedOrphans = new Set();
- },
- destroy: function () {
- Front.prototype.destroy.call(this);
- },
- // Update the object given a form representation off the wire.
- form: function (json) {
- this.actorID = json.actor;
- this.rootNode = types.getType("domnode").read(json.root, this);
- this._rootNodeDeferred.resolve(this.rootNode);
- // FF42+ the actor starts exposing traits
- this.traits = json.traits || {};
- },
- /**
- * Clients can use walker.rootNode to get the current root node of the
- * walker, but during a reload the root node might be null. This
- * method returns a promise that will resolve to the root node when it is
- * set.
- */
- getRootNode: function () {
- return this._rootNodeDeferred.promise;
- },
- /**
- * Create the root node promise, triggering the "new-root" notification
- * on resolution.
- */
- _createRootNodePromise: function () {
- this._rootNodeDeferred = defer();
- this._rootNodeDeferred.promise.then(() => {
- events.emit(this, "new-root");
- });
- },
- /**
- * When reading an actor form off the wire, we want to hook it up to its
- * parent front. The protocol guarantees that the parent will be seen
- * by the client in either a previous or the current request.
- * So if we've already seen this parent return it, otherwise create
- * a bare-bones stand-in node. The stand-in node will be updated
- * with a real form by the end of the deserialization.
- */
- ensureParentFront: function (id) {
- let front = this.get(id);
- if (front) {
- return front;
- }
- return types.getType("domnode").read({ actor: id }, this, "standin");
- },
- /**
- * See the documentation for WalkerActor.prototype.retainNode for
- * information on retained nodes.
- *
- * From the client's perspective, `retainNode` can fail if the node in
- * question is removed from the ownership tree before the `retainNode`
- * request reaches the server. This can only happen if the client has
- * asked the server to release nodes but hasn't gotten a response
- * yet: Either a `releaseNode` request or a `getMutations` with `cleanup`
- * set is outstanding.
- *
- * If either of those requests is outstanding AND releases the retained
- * node, this request will fail with noSuchActor, but the ownership tree
- * will stay in a consistent state.
- *
- * Because the protocol guarantees that requests will be processed and
- * responses received in the order they were sent, we get the right
- * semantics by setting our local retained flag on the node only AFTER
- * a SUCCESSFUL retainNode call.
- */
- retainNode: custom(function (node) {
- return this._retainNode(node).then(() => {
- node.retained = true;
- });
- }, {
- impl: "_retainNode",
- }),
- unretainNode: custom(function (node) {
- return this._unretainNode(node).then(() => {
- node.retained = false;
- if (this._retainedOrphans.has(node)) {
- this._retainedOrphans.delete(node);
- this._releaseFront(node);
- }
- });
- }, {
- impl: "_unretainNode"
- }),
- releaseNode: custom(function (node, options = {}) {
- // NodeFront.destroy will destroy children in the ownership tree too,
- // mimicking what the server will do here.
- let actorID = node.actorID;
- this._releaseFront(node, !!options.force);
- return this._releaseNode({ actorID: actorID });
- }, {
- impl: "_releaseNode"
- }),
- findInspectingNode: custom(function () {
- return this._findInspectingNode().then(response => {
- return response.node;
- });
- }, {
- impl: "_findInspectingNode"
- }),
- querySelector: custom(function (queryNode, selector) {
- return this._querySelector(queryNode, selector).then(response => {
- return response.node;
- });
- }, {
- impl: "_querySelector"
- }),
- getNodeActorFromObjectActor: custom(function (objectActorID) {
- return this._getNodeActorFromObjectActor(objectActorID).then(response => {
- return response ? response.node : null;
- });
- }, {
- impl: "_getNodeActorFromObjectActor"
- }),
- getStyleSheetOwnerNode: custom(function (styleSheetActorID) {
- return this._getStyleSheetOwnerNode(styleSheetActorID).then(response => {
- return response ? response.node : null;
- });
- }, {
- impl: "_getStyleSheetOwnerNode"
- }),
- getNodeFromActor: custom(function (actorID, path) {
- return this._getNodeFromActor(actorID, path).then(response => {
- return response ? response.node : null;
- });
- }, {
- impl: "_getNodeFromActor"
- }),
- /*
- * Incrementally search the document for a given string.
- * For modern servers, results will be searched with using the WalkerActor
- * `search` function (includes tag names, attributes, and text contents).
- * Only 1 result is sent back, and calling the method again with the same
- * query will send the next result. When there are no more results to be sent
- * back, null is sent.
- * @param {String} query
- * @param {Object} options
- * - "reverse": search backwards
- * - "selectorOnly": treat input as a selector string (don't search text
- * tags, attributes, etc)
- */
- search: custom(Task.async(function* (query, options = { }) {
- let nodeList;
- let searchType;
- let searchData = this.searchData = this.searchData || { };
- let selectorOnly = !!options.selectorOnly;
- // Backwards compat. Use selector only search if the new
- // search functionality isn't implemented, or if the caller (tests)
- // want it.
- if (selectorOnly || !this.traits.textSearch) {
- searchType = "selector";
- if (this.traits.multiFrameQuerySelectorAll) {
- nodeList = yield this.multiFrameQuerySelectorAll(query);
- } else {
- nodeList = yield this.querySelectorAll(this.rootNode, query);
- }
- } else {
- searchType = "search";
- let result = yield this._search(query, options);
- nodeList = result.list;
- }
- // If this is a new search, start at the beginning.
- if (searchData.query !== query ||
- searchData.selectorOnly !== selectorOnly) {
- searchData.selectorOnly = selectorOnly;
- searchData.query = query;
- searchData.index = -1;
- }
- if (!nodeList.length) {
- return null;
- }
- // Move search result cursor and cycle if necessary.
- searchData.index = options.reverse ? searchData.index - 1 :
- searchData.index + 1;
- if (searchData.index >= nodeList.length) {
- searchData.index = 0;
- }
- if (searchData.index < 0) {
- searchData.index = nodeList.length - 1;
- }
- // Send back the single node, along with any relevant search data
- let node = yield nodeList.item(searchData.index);
- return {
- type: searchType,
- node: node,
- resultsLength: nodeList.length,
- resultsIndex: searchData.index,
- };
- }), {
- impl: "_search"
- }),
- _releaseFront: function (node, force) {
- if (node.retained && !force) {
- node.reparent(null);
- this._retainedOrphans.add(node);
- return;
- }
- if (node.retained) {
- // Forcing a removal.
- this._retainedOrphans.delete(node);
- }
- // Release any children
- for (let child of node.treeChildren()) {
- this._releaseFront(child, force);
- }
- // All children will have been removed from the node by this point.
- node.reparent(null);
- node.destroy();
- },
- /**
- * Get any unprocessed mutation records and process them.
- */
- getMutations: custom(function (options = {}) {
- return this._getMutations(options).then(mutations => {
- let emitMutations = [];
- for (let change of mutations) {
- // The target is only an actorID, get the associated front.
- let targetID;
- let targetFront;
- if (change.type === "newRoot") {
- // We may receive a new root without receiving any documentUnload
- // beforehand. Like when opening tools in middle of a document load.
- if (this.rootNode) {
- this._createRootNodePromise();
- }
- this.rootNode = types.getType("domnode").read(change.target, this);
- this._rootNodeDeferred.resolve(this.rootNode);
- targetID = this.rootNode.actorID;
- targetFront = this.rootNode;
- } else {
- targetID = change.target;
- targetFront = this.get(targetID);
- }
- if (!targetFront) {
- console.trace("Got a mutation for an unexpected actor: " + targetID +
- ", please file a bug on bugzilla.mozilla.org!");
- continue;
- }
- let emittedMutation = object.merge(change, { target: targetFront });
- if (change.type === "childList" ||
- change.type === "nativeAnonymousChildList") {
- // Update the ownership tree according to the mutation record.
- let addedFronts = [];
- let removedFronts = [];
- for (let removed of change.removed) {
- let removedFront = this.get(removed);
- if (!removedFront) {
- console.error("Got a removal of an actor we didn't know about: " +
- removed);
- continue;
- }
- // Remove from the ownership tree
- removedFront.reparent(null);
- // This node is orphaned unless we get it in the 'added' list
- // eventually.
- this._orphaned.add(removedFront);
- removedFronts.push(removedFront);
- }
- for (let added of change.added) {
- let addedFront = this.get(added);
- if (!addedFront) {
- console.error("Got an addition of an actor we didn't know " +
- "about: " + added);
- continue;
- }
- addedFront.reparent(targetFront);
- // The actor is reconnected to the ownership tree, unorphan
- // it.
- this._orphaned.delete(addedFront);
- addedFronts.push(addedFront);
- }
- // Before passing to users, replace the added and removed actor
- // ids with front in the mutation record.
- emittedMutation.added = addedFronts;
- emittedMutation.removed = removedFronts;
- // If this is coming from a DOM mutation, the actor's numChildren
- // was passed in. Otherwise, it is simulated from a frame load or
- // unload, so don't change the front's form.
- if ("numChildren" in change) {
- targetFront._form.numChildren = change.numChildren;
- }
- } else if (change.type === "frameLoad") {
- // Nothing we need to do here, except verify that we don't have any
- // document children, because we should have gotten a documentUnload
- // first.
- for (let child of targetFront.treeChildren()) {
- if (child.nodeType === nodeConstants.DOCUMENT_NODE) {
- console.trace("Got an unexpected frameLoad in the inspector, " +
- "please file a bug on bugzilla.mozilla.org!");
- }
- }
- } else if (change.type === "documentUnload") {
- if (targetFront === this.rootNode) {
- this._createRootNodePromise();
- }
- // We try to give fronts instead of actorIDs, but these fronts need
- // to be destroyed now.
- emittedMutation.target = targetFront.actorID;
- emittedMutation.targetParent = targetFront.parentNode();
- // Release the document node and all of its children, even retained.
- this._releaseFront(targetFront, true);
- } else if (change.type === "unretained") {
- // Retained orphans were force-released without the intervention of
- // client (probably a navigated frame).
- for (let released of change.nodes) {
- let releasedFront = this.get(released);
- this._retainedOrphans.delete(released);
- this._releaseFront(releasedFront, true);
- }
- } else {
- targetFront.updateMutation(change);
- }
- // Update the inlineTextChild property of the target for a selected list of
- // mutation types.
- if (change.type === "inlineTextChild" ||
- change.type === "childList" ||
- change.type === "nativeAnonymousChildList") {
- if (change.inlineTextChild) {
- targetFront.inlineTextChild =
- types.getType("domnode").read(change.inlineTextChild, this);
- } else {
- targetFront.inlineTextChild = undefined;
- }
- }
- emitMutations.push(emittedMutation);
- }
- if (options.cleanup) {
- for (let node of this._orphaned) {
- // This will move retained nodes to this._retainedOrphans.
- this._releaseFront(node);
- }
- this._orphaned = new Set();
- }
- events.emit(this, "mutations", emitMutations);
- });
- }, {
- impl: "_getMutations"
- }),
- /**
- * Handle the `new-mutations` notification by fetching the
- * available mutation records.
- */
- onMutations: preEvent("new-mutations", function () {
- // Fetch and process the mutations.
- this.getMutations({cleanup: this.autoCleanup}).catch(() => {});
- }),
- isLocal: function () {
- return !!this.conn._transport._serverConnection;
- },
- // XXX hack during transition to remote inspector: get a proper NodeFront
- // for a given local node. Only works locally.
- frontForRawNode: function (rawNode) {
- if (!this.isLocal()) {
- console.warn("Tried to use frontForRawNode on a remote connection.");
- return null;
- }
- const { DebuggerServer } = require("devtools/server/main");
- let walkerActor = DebuggerServer._searchAllConnectionsForActor(this.actorID);
- if (!walkerActor) {
- throw Error("Could not find client side for actor " + this.actorID);
- }
- let nodeActor = walkerActor._ref(rawNode);
- // Pass the node through a read/write pair to create the client side actor.
- let nodeType = types.getType("domnode");
- let returnNode = nodeType.read(
- nodeType.write(nodeActor, walkerActor), this);
- let top = returnNode;
- let extras = walkerActor.parents(nodeActor, {sameTypeRootTreeItem: true});
- for (let extraActor of extras) {
- top = nodeType.read(nodeType.write(extraActor, walkerActor), this);
- }
- if (top !== this.rootNode) {
- // Imported an already-orphaned node.
- this._orphaned.add(top);
- walkerActor._orphaned
- .add(DebuggerServer._searchAllConnectionsForActor(top.actorID));
- }
- return returnNode;
- },
- removeNode: custom(Task.async(function* (node) {
- let previousSibling = yield this.previousSibling(node);
- let nextSibling = yield this._removeNode(node);
- return {
- previousSibling: previousSibling,
- nextSibling: nextSibling,
- };
- }), {
- impl: "_removeNode"
- }),
- });
- exports.WalkerFront = WalkerFront;
- /**
- * Client side of the inspector actor, which is used to create
- * inspector-related actors, including the walker.
- */
- var InspectorFront = FrontClassWithSpec(inspectorSpec, {
- initialize: function (client, tabForm) {
- Front.prototype.initialize.call(this, client);
- this.actorID = tabForm.inspectorActor;
- // XXX: This is the first actor type in its hierarchy to use the protocol
- // library, so we're going to self-own on the client side for now.
- this.manage(this);
- },
- destroy: function () {
- delete this.walker;
- Front.prototype.destroy.call(this);
- },
- getWalker: custom(function (options = {}) {
- return this._getWalker(options).then(walker => {
- this.walker = walker;
- return walker;
- });
- }, {
- impl: "_getWalker"
- }),
- getPageStyle: custom(function () {
- return this._getPageStyle().then(pageStyle => {
- // We need a walker to understand node references from the
- // node style.
- if (this.walker) {
- return pageStyle;
- }
- return this.getWalker().then(() => {
- return pageStyle;
- });
- });
- }, {
- impl: "_getPageStyle"
- }),
- pickColorFromPage: custom(Task.async(function* (toolbox, options) {
- if (toolbox) {
- // If the eyedropper was already started using the gcli command, hide it so we don't
- // end up with 2 instances of the eyedropper on the page.
- let {target} = toolbox;
- let requisition = yield CommandUtils.createRequisition(target, {
- environment: CommandUtils.createEnvironment({target})
- });
- yield requisition.updateExec("eyedropper --hide");
- }
- yield this._pickColorFromPage(options);
- }), {
- impl: "_pickColorFromPage"
- })
- });
- exports.InspectorFront = InspectorFront;
|