зеркало из https://github.com/mozilla/gecko-dev.git
Bug 848731 - Deleting nodes or container frames now resets the markup, highlighter, css views accordingly. r=paul
This commit is contained in:
Родитель
e5197f8a17
Коммит
e2c6feedd7
|
@ -102,6 +102,7 @@ HTMLBreadcrumbs.prototype = {
|
|||
this.selection.on("new-node-front", this.update);
|
||||
this.selection.on("pseudoclass", this.updateSelectors);
|
||||
this.selection.on("attribute-changed", this.updateSelectors);
|
||||
this.inspector.on("markupmutation", this.update);
|
||||
this.update();
|
||||
},
|
||||
|
||||
|
@ -349,6 +350,7 @@ HTMLBreadcrumbs.prototype = {
|
|||
this.selection.off("new-node-front", this.update);
|
||||
this.selection.off("pseudoclass", this.updateSelectors);
|
||||
this.selection.off("attribute-changed", this.updateSelectors);
|
||||
this.inspector.off("markupmutation", this.update);
|
||||
|
||||
this.container.removeEventListener("underflow", this.onscrollboxreflow, false);
|
||||
this.container.removeEventListener("overflow", this.onscrollboxreflow, false);
|
||||
|
@ -611,7 +613,7 @@ HTMLBreadcrumbs.prototype = {
|
|||
/**
|
||||
* Update the breadcrumbs display when a new node is selected.
|
||||
*/
|
||||
update: function BC_update()
|
||||
update: function BC_update(reason)
|
||||
{
|
||||
this.inspector.hideNodeMenu();
|
||||
|
||||
|
@ -632,7 +634,8 @@ HTMLBreadcrumbs.prototype = {
|
|||
let idx = this.indexOf(this.selection.nodeFront);
|
||||
|
||||
// Is the node already displayed in the breadcrumbs?
|
||||
if (idx > -1) {
|
||||
// (and there are no mutations that need re-display of the crumbs)
|
||||
if (idx > -1 && reason !== "markupmutation") {
|
||||
// Yes. We select it.
|
||||
this.setCursor(idx);
|
||||
} else {
|
||||
|
@ -666,7 +669,7 @@ HTMLBreadcrumbs.prototype = {
|
|||
doneUpdating(this.selection.nodeFront);
|
||||
this.selectionGuardEnd(err);
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
|
||||
|
|
|
@ -432,7 +432,9 @@ InspectorPanel.prototype = {
|
|||
},
|
||||
|
||||
/**
|
||||
* When a node is deleted, select its parent node.
|
||||
* When a node is deleted, select its parent node or the defaultNode if no
|
||||
* parent is found (may happen when deleting an iframe inside which the
|
||||
* node was selected).
|
||||
*/
|
||||
onDetached: function InspectorPanel_onDetached(event, parentNode) {
|
||||
this.cancelLayoutChange();
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
* 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";
|
||||
|
||||
const {Cu, Ci} = require("chrome");
|
||||
let EventEmitter = require("devtools/shared/event-emitter");
|
||||
|
||||
|
@ -76,32 +78,38 @@ Selection.prototype = {
|
|||
let pseudoChange = false;
|
||||
let detached = false;
|
||||
let parentNode = null;
|
||||
|
||||
for (let m of mutations) {
|
||||
if (!attributeChange && m.type == "attributes") {
|
||||
attributeChange = true;
|
||||
}
|
||||
if (m.type == "childList") {
|
||||
if (!detached && this.isNode() && !this.isConnected()) {
|
||||
parentNode = m.target;
|
||||
if (!detached && !this.isConnected()) {
|
||||
if (this.isNode()) {
|
||||
parentNode = m.target;
|
||||
}
|
||||
detached = true;
|
||||
}
|
||||
}
|
||||
if (m.type == "pseudoClassLock"){
|
||||
if (m.type == "pseudoClassLock") {
|
||||
pseudoChange = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (attributeChange)
|
||||
// Fire our events depending on what changed in the mutations array
|
||||
if (attributeChange) {
|
||||
this.emit("attribute-changed");
|
||||
if (pseudoChange)
|
||||
}
|
||||
if (pseudoChange) {
|
||||
this.emit("pseudoclass");
|
||||
}
|
||||
if (detached) {
|
||||
this.emit("detached", parentNode ? parentNode.rawNode() : null);
|
||||
this.emit("detached-front", parentNode);
|
||||
}
|
||||
},
|
||||
|
||||
destroy: function SN_destroy() {
|
||||
destroy: function() {
|
||||
this.setNode(null);
|
||||
this.setWalker(null);
|
||||
},
|
||||
|
@ -117,7 +125,7 @@ Selection.prototype = {
|
|||
},
|
||||
|
||||
// Not remote-safe
|
||||
setNode: function SN_setNode(value, reason="unknown") {
|
||||
setNode: function(value, reason="unknown") {
|
||||
if (value) {
|
||||
value = this._walker.frontForRawNode(value);
|
||||
}
|
||||
|
@ -168,13 +176,13 @@ Selection.prototype = {
|
|||
return this._nodeFront;
|
||||
},
|
||||
|
||||
isRoot: function SN_isRootNode() {
|
||||
isRoot: function() {
|
||||
return this.isNode() &&
|
||||
this.isConnected() &&
|
||||
this._nodeFront.isDocumentElement;
|
||||
},
|
||||
|
||||
isNode: function SN_isNode() {
|
||||
isNode: function() {
|
||||
if (!this._nodeFront) {
|
||||
return false;
|
||||
}
|
||||
|
@ -188,11 +196,11 @@ Selection.prototype = {
|
|||
return true;
|
||||
},
|
||||
|
||||
isLocal: function SN_nsLocal() {
|
||||
isLocal: function() {
|
||||
return !!this._node;
|
||||
},
|
||||
|
||||
isConnected: function SN_isConnected() {
|
||||
isConnected: function() {
|
||||
let node = this._nodeFront;
|
||||
if (!node || !node.actorID) {
|
||||
return false;
|
||||
|
@ -220,58 +228,58 @@ Selection.prototype = {
|
|||
return false;
|
||||
},
|
||||
|
||||
isHTMLNode: function SN_isHTMLNode() {
|
||||
isHTMLNode: function() {
|
||||
let xhtml_ns = "http://www.w3.org/1999/xhtml";
|
||||
return this.isNode() && this.node.namespaceURI == xhtml_ns;
|
||||
},
|
||||
|
||||
// Node type
|
||||
|
||||
isElementNode: function SN_isElementNode() {
|
||||
isElementNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ELEMENT_NODE;
|
||||
},
|
||||
|
||||
isAttributeNode: function SN_isAttributeNode() {
|
||||
isAttributeNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ATTRIBUTE_NODE;
|
||||
},
|
||||
|
||||
isTextNode: function SN_isTextNode() {
|
||||
isTextNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.TEXT_NODE;
|
||||
},
|
||||
|
||||
isCDATANode: function SN_isCDATANode() {
|
||||
isCDATANode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.CDATA_SECTION_NODE;
|
||||
},
|
||||
|
||||
isEntityRefNode: function SN_isEntityRefNode() {
|
||||
isEntityRefNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ENTITY_REFERENCE_NODE;
|
||||
},
|
||||
|
||||
isEntityNode: function SN_isEntityNode() {
|
||||
isEntityNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.ENTITY_NODE;
|
||||
},
|
||||
|
||||
isProcessingInstructionNode: function SN_isProcessingInstructionNode() {
|
||||
isProcessingInstructionNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.PROCESSING_INSTRUCTION_NODE;
|
||||
},
|
||||
|
||||
isCommentNode: function SN_isCommentNode() {
|
||||
isCommentNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.PROCESSING_INSTRUCTION_NODE;
|
||||
},
|
||||
|
||||
isDocumentNode: function SN_isDocumentNode() {
|
||||
isDocumentNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE;
|
||||
},
|
||||
|
||||
isDocumentTypeNode: function SN_isDocumentTypeNode() {
|
||||
isDocumentTypeNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE;
|
||||
},
|
||||
|
||||
isDocumentFragmentNode: function SN_isDocumentFragmentNode() {
|
||||
isDocumentFragmentNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.DOCUMENT_FRAGMENT_NODE;
|
||||
},
|
||||
|
||||
isNotationNode: function SN_isNotationNode() {
|
||||
isNotationNode: function() {
|
||||
return this.isNode() && this.nodeFront.nodeType == Ci.nsIDOMNode.NOTATION_NODE;
|
||||
},
|
||||
}
|
||||
};
|
||||
|
|
|
@ -16,6 +16,8 @@ support-files = head.js
|
|||
[browser_inspector_bug_831693_search_suggestions.html]
|
||||
[browser_inspector_bug_835722_infobar_reappears.js]
|
||||
[browser_inspector_bug_840156_destroy_after_navigation.js]
|
||||
[browser_inspector_bug_848731_reset_selection_on_delete.js]
|
||||
[browser_inspector_bug_848731_reset_selection_on_delete.html]
|
||||
[browser_inspector_changes.js]
|
||||
[browser_inspector_cmd_inspect.html]
|
||||
[browser_inspector_cmd_inspect.js]
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>node delete - reset selection - test</title>
|
||||
</head>
|
||||
<body>
|
||||
<ul id="deleteChildren">
|
||||
<li id="deleteManually">Delete me via the inspector</li>
|
||||
<li id="deleteAutomatically">Delete me via javascript</li>
|
||||
</ul>
|
||||
<iframe id="deleteIframe" src="data:text/html,%3C!DOCTYPE%20html%3E%3Chtml%20lang%3D%22en%22%3E%3Cbody%3E%3Cp%20id%3D%22deleteInIframe%22%3EDelete my container iframe%3C%2Fp%3E%3C%2Fbody%3E%3C%2Fhtml%3E"></iframe>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,140 @@
|
|||
/* -*- Mode: Javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
// Test that when nodes are being deleted in the page, the current selection
|
||||
// and therefore the markup view, css rule view, computed view, font view,
|
||||
// box model view, and breadcrumbs, reset accordingly to show the right node
|
||||
|
||||
const TEST_PAGE = "http://mochi.test:8888/browser/browser/devtools/inspector/test/browser_inspector_bug_848731_reset_selection_on_delete.html";
|
||||
|
||||
function test() {
|
||||
let inspector, toolbox;
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Create a tab, load test HTML, wait for load and start the tests
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
waitForFocus(function() {
|
||||
openInspector((aInspector, aToolbox) => {
|
||||
inspector = aInspector;
|
||||
toolbox = aToolbox;
|
||||
startTests();
|
||||
});
|
||||
}, content);
|
||||
}, true);
|
||||
content.location = TEST_PAGE;
|
||||
|
||||
function startTests() {
|
||||
testManuallyDeleteSelectedNode();
|
||||
}
|
||||
|
||||
function getContainerForRawNode(rawNode) {
|
||||
let front = inspector.markup.walker.frontForRawNode(rawNode);
|
||||
let container = inspector.markup.getContainer(front);
|
||||
return container;
|
||||
}
|
||||
|
||||
// 1 - select a node, right click, hit delete, verify that its parent is selected
|
||||
// and that all other tools are fine
|
||||
function testManuallyDeleteSelectedNode() {
|
||||
// Select our div
|
||||
let div = content.document.getElementById("deleteManually");
|
||||
inspector.selection.setNode(div);
|
||||
inspector.once("inspector-updated", () => {
|
||||
is(inspector.selection.node, div);
|
||||
|
||||
// Get the node container in the markup view
|
||||
let container = getContainerForRawNode(div);
|
||||
|
||||
// Simulate right-click
|
||||
EventUtils.synthesizeMouse(container.tagLine, 2, 2,
|
||||
{type: "contextmenu", button: 2}, inspector.panelWin);
|
||||
|
||||
// And react to the popup shown event
|
||||
let contextMenu = inspector.panelDoc.getElementById("inspectorPopupSet");
|
||||
contextMenu.addEventListener("popupshown", function contextShown() {
|
||||
contextMenu.removeEventListener("popupshown", contextShown, false);
|
||||
|
||||
// Click on the delete sub-menu item
|
||||
inspector.panelDoc.getElementById("node-menu-delete").click();
|
||||
|
||||
// Once updated, make sure eveything is in place, and move on
|
||||
inspector.once("inspector-updated", () => {
|
||||
let parent = content.document.getElementById("deleteChildren");
|
||||
assertNodeSelectedAndPanelsUpdated(parent, "ul#deleteChildren");
|
||||
testAutomaticallyDeleteSelectedNode();
|
||||
});
|
||||
}, false);
|
||||
});
|
||||
}
|
||||
|
||||
// 2 - select a node, delete it via javascript, verify the same things as 1
|
||||
function testAutomaticallyDeleteSelectedNode() {
|
||||
// Select our second div
|
||||
let div = content.document.getElementById("deleteAutomatically");
|
||||
inspector.selection.setNode(div);
|
||||
inspector.once("inspector-updated", () => {
|
||||
is(inspector.selection.node, div);
|
||||
|
||||
// Now remove that div via javascript
|
||||
let parent = content.document.getElementById("deleteChildren");
|
||||
parent.removeChild(div);
|
||||
|
||||
// Once updated, make sure eveything is in place, and move on
|
||||
inspector.once("inspector-updated", () => {
|
||||
assertNodeSelectedAndPanelsUpdated(parent, "ul#deleteChildren");
|
||||
testDeleteSelectedNodeContainerFrame();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 3 - select a node inside an iframe, delete the iframe via javascript, verify that the default node is selected
|
||||
// and that all other tools are fine
|
||||
function testDeleteSelectedNodeContainerFrame() {
|
||||
// Select our third test element, inside the iframe
|
||||
let iframe = content.document.getElementById("deleteIframe");
|
||||
let div = iframe.contentDocument.getElementById("deleteInIframe");
|
||||
inspector.selection.setNode(div);
|
||||
inspector.once("inspector-updated", () => {
|
||||
is(inspector.selection.node, div);
|
||||
|
||||
// Now remove that parent iframe via javascript
|
||||
let parent = content.document.body;
|
||||
parent.removeChild(iframe);
|
||||
|
||||
// Once updated, make sure eveything is in place, and move on
|
||||
inspector.once("inspector-updated", () => {
|
||||
assertNodeSelectedAndPanelsUpdated(parent, "body");
|
||||
endTests();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function endTests() {
|
||||
executeSoon(() => {
|
||||
toolbox.destroy();
|
||||
toolbox = inspector = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
function assertNodeSelectedAndPanelsUpdated(node, crumbLabel) {
|
||||
// Right node selected?
|
||||
is(inspector.selection.nodeFront, getNodeFront(node),
|
||||
"The right node is selected");
|
||||
|
||||
// breadcrumbs updated?
|
||||
let breadcrumbs = inspector.panelDoc.getElementById("inspector-breadcrumbs");
|
||||
is(breadcrumbs.querySelector("button[checked=true]").textContent, crumbLabel,
|
||||
"The right breadcrumb is selected");
|
||||
|
||||
// Highlighter is shown?
|
||||
ok(!inspector.highlighter.isHidden(), "The highlighter is shown");
|
||||
}
|
||||
}
|
|
@ -28,7 +28,9 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
loader.lazyGetter(this, "DOMParser", function() {
|
||||
return Cc["@mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
|
||||
});
|
||||
loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/autocomplete-popup").AutocompletePopup);
|
||||
loader.lazyGetter(this, "AutocompletePopup", () => {
|
||||
return require("devtools/shared/autocomplete-popup").AutocompletePopup
|
||||
});
|
||||
|
||||
/**
|
||||
* Vocabulary for the purposes of this file:
|
||||
|
@ -48,8 +50,7 @@ loader.lazyGetter(this, "AutocompletePopup", () => require("devtools/shared/auto
|
|||
* @param iframe aFrame
|
||||
* An iframe in which the caller has kindly loaded markup-view.xhtml.
|
||||
*/
|
||||
function MarkupView(aInspector, aFrame, aControllerWindow)
|
||||
{
|
||||
function MarkupView(aInspector, aFrame, aControllerWindow) {
|
||||
this._inspector = aInspector;
|
||||
this.walker = this._inspector.walker;
|
||||
this._frame = aFrame;
|
||||
|
@ -101,8 +102,7 @@ exports.MarkupView = MarkupView;
|
|||
MarkupView.prototype = {
|
||||
_selectedContainer: null,
|
||||
|
||||
template: function MT_template(aName, aDest, aOptions={stack: "markup-view.xhtml"})
|
||||
{
|
||||
template: function(aName, aDest, aOptions={stack: "markup-view.xhtml"}) {
|
||||
let node = this.doc.getElementById("template-" + aName).cloneNode(true);
|
||||
node.removeAttribute("id");
|
||||
template(node, aDest, aOptions);
|
||||
|
@ -113,8 +113,7 @@ MarkupView.prototype = {
|
|||
* Get the MarkupContainer object for a given node, or undefined if
|
||||
* none exists.
|
||||
*/
|
||||
getContainer: function MT_getContainer(aNode)
|
||||
{
|
||||
getContainer: function(aNode) {
|
||||
return this._containers.get(aNode);
|
||||
},
|
||||
|
||||
|
@ -148,8 +147,7 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* Highlight the inspector selected node.
|
||||
*/
|
||||
_onNewSelection: function MT__onNewSelection()
|
||||
{
|
||||
_onNewSelection: function() {
|
||||
let done = this._inspector.updating("markup-view");
|
||||
if (this._inspector.selection.isNode()) {
|
||||
this.showNode(this._inspector.selection.nodeFront, true).then(() => {
|
||||
|
@ -166,8 +164,7 @@ MarkupView.prototype = {
|
|||
* Create a TreeWalker to find the next/previous
|
||||
* node for selection.
|
||||
*/
|
||||
_selectionWalker: function MT__seletionWalker(aStart)
|
||||
{
|
||||
_selectionWalker: function(aStart) {
|
||||
let walker = this.doc.createTreeWalker(
|
||||
aStart || this._elt,
|
||||
Ci.nsIDOMNodeFilter.SHOW_ELEMENT,
|
||||
|
@ -187,8 +184,7 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* Key handling.
|
||||
*/
|
||||
_onKeyDown: function MT__KeyDown(aEvent)
|
||||
{
|
||||
_onKeyDown: function(aEvent) {
|
||||
let handled = true;
|
||||
|
||||
// Ignore keystrokes that originated in editors.
|
||||
|
@ -286,8 +282,7 @@ MarkupView.prototype = {
|
|||
* Delete a node from the DOM.
|
||||
* This is an undoable action.
|
||||
*/
|
||||
deleteNode: function MC__deleteNode(aNode)
|
||||
{
|
||||
deleteNode: function(aNode) {
|
||||
if (aNode.isDocumentElement ||
|
||||
aNode.nodeType == Ci.nsIDOMNode.DOCUMENT_TYPE_NODE) {
|
||||
return;
|
||||
|
@ -315,7 +310,7 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* If an editable item is focused, select its container.
|
||||
*/
|
||||
_onFocus: function MC__onFocus(aEvent) {
|
||||
_onFocus: function(aEvent) {
|
||||
let parent = aEvent.target;
|
||||
while (!parent.container) {
|
||||
parent = parent.parentNode;
|
||||
|
@ -334,8 +329,7 @@ MarkupView.prototype = {
|
|||
* @param aIgnoreFocus aIgnoreFocus
|
||||
* If falsy, keyboard focus will be moved to the container too.
|
||||
*/
|
||||
navigate: function MT__navigate(aContainer, aIgnoreFocus)
|
||||
{
|
||||
navigate: function(aContainer, aIgnoreFocus) {
|
||||
if (!aContainer) {
|
||||
return;
|
||||
}
|
||||
|
@ -362,8 +356,7 @@ MarkupView.prototype = {
|
|||
* Whether the newly imported node should be flashed
|
||||
* @returns MarkupContainer The MarkupContainer object for this element.
|
||||
*/
|
||||
importNode: function MT_importNode(aNode, aFlashNode)
|
||||
{
|
||||
importNode: function(aNode, aFlashNode) {
|
||||
if (!aNode) {
|
||||
return null;
|
||||
}
|
||||
|
@ -395,8 +388,7 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* Mutation observer used for included nodes.
|
||||
*/
|
||||
_mutationObserver: function MT__mutationObserver(aMutations)
|
||||
{
|
||||
_mutationObserver: function(aMutations) {
|
||||
for (let mutation of aMutations) {
|
||||
let type = mutation.type;
|
||||
let target = mutation.target;
|
||||
|
@ -437,8 +429,7 @@ MarkupView.prototype = {
|
|||
* Given a list of mutations returned by the mutation observer, flash the
|
||||
* corresponding containers to attract attention.
|
||||
*/
|
||||
_flashMutatedNodes: function MT_flashMutatedNodes(aMutations)
|
||||
{
|
||||
_flashMutatedNodes: function(aMutations) {
|
||||
let addedOrEditedContainers = new Set();
|
||||
let removedContainers = new Set();
|
||||
|
||||
|
@ -481,8 +472,7 @@ MarkupView.prototype = {
|
|||
* Make sure the given node's parents are expanded and the
|
||||
* node is scrolled on to screen.
|
||||
*/
|
||||
showNode: function MT_showNode(aNode, centered)
|
||||
{
|
||||
showNode: function(aNode, centered) {
|
||||
let container = this.importNode(aNode);
|
||||
let parent = aNode;
|
||||
while ((parent = parent.parentNode())) {
|
||||
|
@ -501,8 +491,7 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* Expand the container's children.
|
||||
*/
|
||||
_expandContainer: function MT__expandContainer(aContainer)
|
||||
{
|
||||
_expandContainer: function(aContainer) {
|
||||
return this._updateChildren(aContainer, {expand: true}).then(() => {
|
||||
aContainer.expanded = true;
|
||||
});
|
||||
|
@ -511,8 +500,7 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* Expand the node's children.
|
||||
*/
|
||||
expandNode: function MT_expandNode(aNode)
|
||||
{
|
||||
expandNode: function(aNode) {
|
||||
let container = this._containers.get(aNode);
|
||||
this._expandContainer(container);
|
||||
},
|
||||
|
@ -522,8 +510,7 @@ MarkupView.prototype = {
|
|||
*
|
||||
* @param aContainer The container to expand.
|
||||
*/
|
||||
_expandAll: function MT_expandAll(aContainer)
|
||||
{
|
||||
_expandAll: function(aContainer) {
|
||||
return this._expandContainer(aContainer).then(() => {
|
||||
let child = aContainer.children.firstChild;
|
||||
let promises = [];
|
||||
|
@ -541,8 +528,7 @@ MarkupView.prototype = {
|
|||
* @param aContainer The node to expand, or null
|
||||
* to start from the top.
|
||||
*/
|
||||
expandAll: function MT_expandAll(aNode)
|
||||
{
|
||||
expandAll: function(aNode) {
|
||||
aNode = aNode || this._rootNode;
|
||||
return this._expandAll(this._containers.get(aNode));
|
||||
},
|
||||
|
@ -550,14 +536,12 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* Collapse the node's children.
|
||||
*/
|
||||
collapseNode: function MT_collapseNode(aNode)
|
||||
{
|
||||
collapseNode: function(aNode) {
|
||||
let container = this._containers.get(aNode);
|
||||
container.expanded = false;
|
||||
},
|
||||
|
||||
setNodeExpanded: function(aNode, aExpanded)
|
||||
{
|
||||
setNodeExpanded: function(aNode, aExpanded) {
|
||||
if (aExpanded) {
|
||||
this.expandNode(aNode);
|
||||
} else {
|
||||
|
@ -568,8 +552,7 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* Mark the given node selected.
|
||||
*/
|
||||
markNodeAsSelected: function MT_markNodeAsSelected(aNode)
|
||||
{
|
||||
markNodeAsSelected: function(aNode) {
|
||||
let container = this._containers.get(aNode);
|
||||
if (this._selectedContainer === container) {
|
||||
return false;
|
||||
|
@ -589,8 +572,7 @@ MarkupView.prototype = {
|
|||
* Make sure that every ancestor of the selection are updated
|
||||
* and included in the list of visible children.
|
||||
*/
|
||||
_ensureVisible: function(node)
|
||||
{
|
||||
_ensureVisible: function(node) {
|
||||
while (node) {
|
||||
let container = this._containers.get(node);
|
||||
let parent = node.parentNode();
|
||||
|
@ -608,8 +590,7 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* Unmark selected node (no node selected).
|
||||
*/
|
||||
unmarkSelectedNode: function MT_unmarkSelectedNode()
|
||||
{
|
||||
unmarkSelectedNode: function() {
|
||||
if (this._selectedContainer) {
|
||||
this._selectedContainer.selected = false;
|
||||
this._selectedContainer = null;
|
||||
|
@ -619,8 +600,7 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* Called when the markup panel initiates a change on a node.
|
||||
*/
|
||||
nodeChanged: function MT_nodeChanged(aNode)
|
||||
{
|
||||
nodeChanged: function(aNode) {
|
||||
if (aNode === this._inspector.selection.nodeFront) {
|
||||
this._inspector.change("markupview");
|
||||
}
|
||||
|
@ -667,8 +647,7 @@ MarkupView.prototype = {
|
|||
* @return a promise that will be resolved when the children are ready
|
||||
* (which may be immediately).
|
||||
*/
|
||||
_updateChildren: function(aContainer, options)
|
||||
{
|
||||
_updateChildren: function(aContainer, options) {
|
||||
let expand = options && options.expand;
|
||||
let flash = options && options.flash;
|
||||
|
||||
|
@ -772,8 +751,7 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* Return a list of the children to display for this container.
|
||||
*/
|
||||
_getVisibleChildren: function MV__getVisibleChildren(aContainer, aCentered)
|
||||
{
|
||||
_getVisibleChildren: function(aContainer, aCentered) {
|
||||
let maxChildren = aContainer.maxChildren || this.maxChildren;
|
||||
if (maxChildren == -1) {
|
||||
maxChildren = undefined;
|
||||
|
@ -788,8 +766,7 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* Tear down the markup panel.
|
||||
*/
|
||||
destroy: function MT_destroy()
|
||||
{
|
||||
destroy: function() {
|
||||
gDevTools.off("pref-changed", this._handlePrefChange);
|
||||
|
||||
this.undo.destroy();
|
||||
|
@ -802,18 +779,23 @@ MarkupView.prototype = {
|
|||
delete this._boundFocus;
|
||||
|
||||
if (this._boundUpdatePreview) {
|
||||
this._frame.contentWindow.removeEventListener("scroll", this._boundUpdatePreview, true);
|
||||
this._frame.contentWindow.removeEventListener("scroll",
|
||||
this._boundUpdatePreview, true);
|
||||
delete this._boundUpdatePreview;
|
||||
}
|
||||
|
||||
if (this._boundResizePreview) {
|
||||
this._frame.contentWindow.removeEventListener("resize", this._boundResizePreview, true);
|
||||
this._frame.contentWindow.removeEventListener("overflow", this._boundResizePreview, true);
|
||||
this._frame.contentWindow.removeEventListener("underflow", this._boundResizePreview, true);
|
||||
this._frame.contentWindow.removeEventListener("resize",
|
||||
this._boundResizePreview, true);
|
||||
this._frame.contentWindow.removeEventListener("overflow",
|
||||
this._boundResizePreview, true);
|
||||
this._frame.contentWindow.removeEventListener("underflow",
|
||||
this._boundResizePreview, true);
|
||||
delete this._boundResizePreview;
|
||||
}
|
||||
|
||||
this._frame.contentWindow.removeEventListener("keydown", this._boundKeyDown, false);
|
||||
this._frame.contentWindow.removeEventListener("keydown",
|
||||
this._boundKeyDown, false);
|
||||
delete this._boundKeyDown;
|
||||
|
||||
this._inspector.selection.off("new-node-front", this._boundOnNewSelection);
|
||||
|
@ -830,8 +812,7 @@ MarkupView.prototype = {
|
|||
/**
|
||||
* Initialize the preview panel.
|
||||
*/
|
||||
_initPreview: function MT_initPreview()
|
||||
{
|
||||
_initPreview: function() {
|
||||
if (!Services.prefs.getBoolPref("devtools.inspector.markupPreview")) {
|
||||
return;
|
||||
}
|
||||
|
@ -845,21 +826,23 @@ MarkupView.prototype = {
|
|||
this._previewWidth = this._preview.getBoundingClientRect().width;
|
||||
|
||||
this._boundResizePreview = this._resizePreview.bind(this);
|
||||
this._frame.contentWindow.addEventListener("resize", this._boundResizePreview, true);
|
||||
this._frame.contentWindow.addEventListener("overflow", this._boundResizePreview, true);
|
||||
this._frame.contentWindow.addEventListener("underflow", this._boundResizePreview, true);
|
||||
this._frame.contentWindow.addEventListener("resize",
|
||||
this._boundResizePreview, true);
|
||||
this._frame.contentWindow.addEventListener("overflow",
|
||||
this._boundResizePreview, true);
|
||||
this._frame.contentWindow.addEventListener("underflow",
|
||||
this._boundResizePreview, true);
|
||||
|
||||
this._boundUpdatePreview = this._updatePreview.bind(this);
|
||||
this._frame.contentWindow.addEventListener("scroll", this._boundUpdatePreview, true);
|
||||
this._frame.contentWindow.addEventListener("scroll",
|
||||
this._boundUpdatePreview, true);
|
||||
this._updatePreview();
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Move the preview viewbox.
|
||||
*/
|
||||
_updatePreview: function MT_updatePreview()
|
||||
{
|
||||
_updatePreview: function() {
|
||||
let win = this._frame.contentWindow;
|
||||
|
||||
if (win.scrollMaxY == 0) {
|
||||
|
@ -876,7 +859,8 @@ MarkupView.prototype = {
|
|||
let scrollTo
|
||||
if (height >= win.innerHeight) {
|
||||
scrollTo = -(height - win.innerHeight) * (win.scrollY / win.scrollMaxY);
|
||||
this._previewBar.setAttribute("style", "height:" + height + "px;transform:translateY(" + scrollTo + "px)");
|
||||
this._previewBar.setAttribute("style", "height:" + height +
|
||||
"px;transform:translateY(" + scrollTo + "px)");
|
||||
} else {
|
||||
this._previewBar.setAttribute("style", "height:100%");
|
||||
}
|
||||
|
@ -886,14 +870,14 @@ MarkupView.prototype = {
|
|||
|
||||
let height = ~~(win.innerHeight * ratio) + "px";
|
||||
let top = ~~(win.scrollY * ratio) + "px";
|
||||
this._viewbox.setAttribute("style", "height:" + height + ";transform: translateY(" + top + ")");
|
||||
this._viewbox.setAttribute("style", "height:" + height +
|
||||
";transform: translateY(" + top + ")");
|
||||
},
|
||||
|
||||
/**
|
||||
* Hide the preview while resizing, to avoid slowness.
|
||||
*/
|
||||
_resizePreview: function MT_resizePreview()
|
||||
{
|
||||
_resizePreview: function() {
|
||||
let win = this._frame.contentWindow;
|
||||
this._previewBar.classList.add("hide");
|
||||
win.clearTimeout(this._resizePreviewTimeout);
|
||||
|
@ -1122,12 +1106,14 @@ MarkupContainer.prototype = {
|
|||
this.highlighter.classList.add("theme-bg-darker");
|
||||
}
|
||||
if (this.closeTagLine) {
|
||||
this.closeTagLine.querySelector(".highlighter").classList.add("theme-bg-darker");
|
||||
this.closeTagLine.querySelector(".highlighter").classList.add(
|
||||
"theme-bg-darker");
|
||||
}
|
||||
} else {
|
||||
this.highlighter.classList.remove("theme-bg-darker");
|
||||
if (this.closeTagLine) {
|
||||
this.closeTagLine.querySelector(".highlighter").classList.remove("theme-bg-darker");
|
||||
this.closeTagLine.querySelector(".highlighter").classList.remove(
|
||||
"theme-bg-darker");
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -1204,8 +1190,7 @@ RootContainer.prototype = {
|
|||
/**
|
||||
* Creates an editor for simple nodes.
|
||||
*/
|
||||
function GenericEditor(aContainer, aNode)
|
||||
{
|
||||
function GenericEditor(aContainer, aNode) {
|
||||
this.elt = aContainer.doc.createElement("span");
|
||||
this.elt.className = "editor";
|
||||
this.elt.textContent = aNode.nodeName;
|
||||
|
@ -1217,8 +1202,7 @@ function GenericEditor(aContainer, aNode)
|
|||
* @param MarkupContainer aContainer The container owning this editor.
|
||||
* @param DOMNode aNode The node being edited.
|
||||
*/
|
||||
function DoctypeEditor(aContainer, aNode)
|
||||
{
|
||||
function DoctypeEditor(aContainer, aNode) {
|
||||
this.elt = aContainer.doc.createElement("span");
|
||||
this.elt.className = "editor comment";
|
||||
this.elt.textContent = '<!DOCTYPE ' + aNode.name +
|
||||
|
@ -1235,8 +1219,7 @@ function DoctypeEditor(aContainer, aNode)
|
|||
* @param DOMNode aNode The node being edited.
|
||||
* @param string aTemplate The template id to use to build the editor.
|
||||
*/
|
||||
function TextEditor(aContainer, aNode, aTemplate)
|
||||
{
|
||||
function TextEditor(aContainer, aNode, aTemplate) {
|
||||
this.node = aNode;
|
||||
this._selected = false;
|
||||
|
||||
|
@ -1282,8 +1265,7 @@ TextEditor.prototype = {
|
|||
this.update();
|
||||
},
|
||||
|
||||
update: function TE_update()
|
||||
{
|
||||
update: function() {
|
||||
if (!this.selected || !this.node.incompleteValue) {
|
||||
let text = this.node.shortValue;
|
||||
// XXX: internationalize the elliding
|
||||
|
@ -1312,8 +1294,7 @@ TextEditor.prototype = {
|
|||
* @param MarkupContainer aContainer The container owning this editor.
|
||||
* @param Element aNode The node being edited.
|
||||
*/
|
||||
function ElementEditor(aContainer, aNode)
|
||||
{
|
||||
function ElementEditor(aContainer, aNode) {
|
||||
this.doc = aContainer.doc;
|
||||
this.undo = aContainer.undo;
|
||||
this.template = aContainer.markup.template.bind(aContainer.markup);
|
||||
|
@ -1386,8 +1367,7 @@ ElementEditor.prototype = {
|
|||
/**
|
||||
* Update the state of the editor from the node.
|
||||
*/
|
||||
update: function EE_update(parseColors=true)
|
||||
{
|
||||
update: function(parseColors=true) {
|
||||
let attrs = this.node.attributes;
|
||||
if (!attrs) {
|
||||
return;
|
||||
|
@ -1420,8 +1400,7 @@ ElementEditor.prototype = {
|
|||
return this.node.startModifyingAttributes();
|
||||
},
|
||||
|
||||
_createAttribute: function EE_createAttribute(aAttr, aBefore = null)
|
||||
{
|
||||
_createAttribute: function(aAttr, aBefore = null) {
|
||||
// Create the template editor, which will save some variables here.
|
||||
let data = {
|
||||
attrName: aAttr.name,
|
||||
|
@ -1497,7 +1476,6 @@ ElementEditor.prototype = {
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
// Figure out where we should place the attribute.
|
||||
let before = aBefore;
|
||||
if (aAttr.name == "id") {
|
||||
|
@ -1539,8 +1517,7 @@ ElementEditor.prototype = {
|
|||
* set of attributes, used to place new attributes where the
|
||||
* user put them.
|
||||
*/
|
||||
_applyAttributes: function EE__applyAttributes(aValue, aAttrNode, aDoMods, aUndoMods)
|
||||
{
|
||||
_applyAttributes: function(aValue, aAttrNode, aDoMods, aUndoMods) {
|
||||
let attrs = parseAttributeValues(aValue, this.doc);
|
||||
for (let attr of attrs) {
|
||||
// Create an attribute editor next to the current attribute if needed.
|
||||
|
@ -1554,8 +1531,7 @@ ElementEditor.prototype = {
|
|||
* Saves the current state of the given attribute into an attribute
|
||||
* modification list.
|
||||
*/
|
||||
_saveAttribute: function(aName, aUndoMods)
|
||||
{
|
||||
_saveAttribute: function(aName, aUndoMods) {
|
||||
let node = this.node;
|
||||
if (node.hasAttribute(aName)) {
|
||||
let oldValue = node.getAttribute(aName);
|
||||
|
@ -1568,7 +1544,7 @@ ElementEditor.prototype = {
|
|||
/**
|
||||
* Called when the tag name editor has is done editing.
|
||||
*/
|
||||
onTagEdit: function EE_onTagEdit(aVal, aCommit) {
|
||||
onTagEdit: function(aVal, aCommit) {
|
||||
if (!aCommit || aVal == this.rawNode.tagName) {
|
||||
return;
|
||||
}
|
||||
|
@ -1623,7 +1599,8 @@ ElementEditor.prototype = {
|
|||
};
|
||||
|
||||
function nodeDocument(node) {
|
||||
return node.ownerDocument || (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
|
||||
return node.ownerDocument ||
|
||||
(node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
|
||||
}
|
||||
|
||||
function truncateString(str, maxLength) {
|
||||
|
@ -1648,10 +1625,12 @@ function truncateString(str, maxLength) {
|
|||
function parseAttributeValues(attr, doc) {
|
||||
attr = attr.trim();
|
||||
|
||||
let parse = DOMParser.parseFromString;
|
||||
|
||||
// Handle bad user inputs by appending a " or ' if it fails to parse without them.
|
||||
let el = DOMParser.parseFromString("<div " + attr + "></div>", "text/html").body.childNodes[0] ||
|
||||
DOMParser.parseFromString("<div " + attr + "\"></div>", "text/html").body.childNodes[0] ||
|
||||
DOMParser.parseFromString("<div " + attr + "'></div>", "text/html").body.childNodes[0];
|
||||
let el = parse("<div " + attr + "></div>", "text/html").body.childNodes[0] ||
|
||||
parse("<div " + attr + "\"></div>", "text/html").body.childNodes[0] ||
|
||||
parse("<div " + attr + "'></div>", "text/html").body.childNodes[0];
|
||||
let div = doc.createElement("div");
|
||||
|
||||
let attributes = [];
|
||||
|
|
Загрузка…
Ссылка в новой задаче