https://bugs.webkit.org/show_bug.cgi?id=77875
Reviewed by Yury Semikhatsky.
Source/WebCore:
This change introduces InspectorHistory::Action that encapsulates all DOM modifications
initiated by the inspector. There is a way to undo these actions up until the undoable
state marker.
Tests: inspector/elements/undo-dom-edits-2.html
inspector/elements/undo-dom-edits.html
inspector/styles/undo-add-property.html
inspector/styles/undo-change-property.html
inspector/styles/undo-property-toggle.html
* CMakeLists.txt:
* GNUmakefile.list.am:
* Target.pri:
* WebCore.gypi:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.xcodeproj/project.pbxproj:
* inspector/Inspector.json:
* inspector/InspectorAllInOne.cpp:
* inspector/InspectorCSSAgent.cpp:
(InspectorCSSAgent::StyleSheetAction):
(WebCore::InspectorCSSAgent::StyleSheetAction::StyleSheetAction):
(WebCore::InspectorCSSAgent::StyleSheetAction::perform):
(WebCore::InspectorCSSAgent::StyleSheetAction::undo):
(WebCore):
(InspectorCSSAgent::SetStyleSheetTextAction):
(WebCore::InspectorCSSAgent::SetStyleSheetTextAction::SetStyleSheetTextAction):
(WebCore::InspectorCSSAgent::SetStyleSheetTextAction::perform):
(WebCore::InspectorCSSAgent::SetStyleSheetTextAction::undo):
(InspectorCSSAgent::SetPropertyTextAction):
(WebCore::InspectorCSSAgent::SetPropertyTextAction::SetPropertyTextAction):
(WebCore::InspectorCSSAgent::SetPropertyTextAction::toString):
(WebCore::InspectorCSSAgent::SetPropertyTextAction::perform):
(WebCore::InspectorCSSAgent::SetPropertyTextAction::undo):
(WebCore::InspectorCSSAgent::SetPropertyTextAction::mergeId):
(WebCore::InspectorCSSAgent::SetPropertyTextAction::merge):
(InspectorCSSAgent::TogglePropertyAction):
(WebCore::InspectorCSSAgent::TogglePropertyAction::TogglePropertyAction):
(WebCore::InspectorCSSAgent::TogglePropertyAction::perform):
(WebCore::InspectorCSSAgent::TogglePropertyAction::undo):
(WebCore::InspectorCSSAgent::getStyleSheetText):
(WebCore::InspectorCSSAgent::setStyleSheetText):
(WebCore::InspectorCSSAgent::setPropertyText):
(WebCore::InspectorCSSAgent::toggleProperty):
* inspector/InspectorCSSAgent.h:
(InspectorCSSAgent):
* inspector/InspectorDOMAgent.cpp:
(InspectorDOMAgent::DOMAction):
(WebCore::InspectorDOMAgent::DOMAction::DOMAction):
(WebCore::InspectorDOMAgent::DOMAction::perform):
(WebCore::InspectorDOMAgent::DOMAction::undo):
(WebCore):
(InspectorDOMAgent::RemoveChildAction):
(WebCore::InspectorDOMAgent::RemoveChildAction::RemoveChildAction):
(WebCore::InspectorDOMAgent::RemoveChildAction::perform):
(WebCore::InspectorDOMAgent::RemoveChildAction::undo):
(InspectorDOMAgent::InsertBeforeAction):
(WebCore::InspectorDOMAgent::InsertBeforeAction::InsertBeforeAction):
(WebCore::InspectorDOMAgent::InsertBeforeAction::perform):
(WebCore::InspectorDOMAgent::InsertBeforeAction::undo):
(InspectorDOMAgent::RemoveAttributeAction):
(WebCore::InspectorDOMAgent::RemoveAttributeAction::RemoveAttributeAction):
(WebCore::InspectorDOMAgent::RemoveAttributeAction::perform):
(WebCore::InspectorDOMAgent::RemoveAttributeAction::undo):
(InspectorDOMAgent::SetAttributeAction):
(WebCore::InspectorDOMAgent::SetAttributeAction::SetAttributeAction):
(WebCore::InspectorDOMAgent::SetAttributeAction::perform):
(WebCore::InspectorDOMAgent::SetAttributeAction::undo):
(InspectorDOMAgent::SetOuterHTMLAction):
(WebCore::InspectorDOMAgent::SetOuterHTMLAction::SetOuterHTMLAction):
(WebCore::InspectorDOMAgent::SetOuterHTMLAction::perform):
(WebCore::InspectorDOMAgent::SetOuterHTMLAction::undo):
(WebCore::InspectorDOMAgent::SetOuterHTMLAction::newNode):
(InspectorDOMAgent::ReplaceWholeTextAction):
(WebCore::InspectorDOMAgent::ReplaceWholeTextAction::ReplaceWholeTextAction):
(WebCore::InspectorDOMAgent::ReplaceWholeTextAction::perform):
(WebCore::InspectorDOMAgent::ReplaceWholeTextAction::undo):
(WebCore::InspectorDOMAgent::InspectorDOMAgent):
(WebCore::InspectorDOMAgent::reset):
(WebCore::InspectorDOMAgent::setAttributeValue):
(WebCore::InspectorDOMAgent::setAttributesAsText):
(WebCore::InspectorDOMAgent::removeAttribute):
(WebCore::InspectorDOMAgent::removeNode):
(WebCore::InspectorDOMAgent::setNodeName):
(WebCore::InspectorDOMAgent::setOuterHTML):
(WebCore::InspectorDOMAgent::setNodeValue):
(WebCore::InspectorDOMAgent::moveTo):
(WebCore::InspectorDOMAgent::undo):
(WebCore::InspectorDOMAgent::markUndoableState):
* inspector/InspectorDOMAgent.h:
(InspectorDOMAgent):
(WebCore::InspectorDOMAgent::history):
* inspector/InspectorHistory.cpp: Added.
(WebCore::InspectorHistory::Action::Action):
(WebCore):
(WebCore::InspectorHistory::Action::~Action):
(WebCore::InspectorHistory::Action::toString):
(WebCore::InspectorHistory::Action::isUndoableStateMark):
(WebCore::InspectorHistory::Action::mergeId):
(WebCore::InspectorHistory::Action::merge):
(WebCore::InspectorHistory::InspectorHistory):
(WebCore::InspectorHistory::~InspectorHistory):
(WebCore::InspectorHistory::perform):
(WebCore::InspectorHistory::markUndoableState):
(WebCore::InspectorHistory::undo):
(WebCore::InspectorHistory::reset):
* inspector/InspectorHistory.h: Added.
(WebCore):
(InspectorHistory):
(Action):
* inspector/InspectorStyleSheet.cpp:
(WebCore::InspectorStyle::setPropertyText):
(WebCore::InspectorStyle::styleText):
(WebCore::InspectorStyleSheet::addRule):
(WebCore::InspectorStyleSheet::buildObjectForStyleSheet):
(WebCore::InspectorStyleSheet::buildObjectForStyle):
(WebCore::InspectorStyleSheet::setPropertyText):
(WebCore::InspectorStyleSheet::getText):
(WebCore::InspectorStyleSheetForInlineStyle::getText):
* inspector/InspectorStyleSheet.h:
(InspectorStyle):
(InspectorStyleSheet):
(InspectorStyleSheetForInlineStyle):
* inspector/front-end/CSSStyleModel.js:
(WebInspector.CSSProperty.prototype.setText):
* inspector/front-end/ElementsPanel.js:
(WebInspector.ElementsPanel.prototype._selectedNodeChanged):
(WebInspector.ElementsPanel.prototype._updateSidebars):
(WebInspector.ElementsPanel.prototype.handleShortcut):
* inspector/front-end/StylesSidebarPane.js:
(WebInspector.StylePropertiesSection.prototype.onpopulate):
(WebInspector.StylePropertiesSection.prototype.addNewBlankProperty):
(WebInspector.ComputedStylePropertiesSection.prototype.onpopulate):
(WebInspector.StylePropertyTreeElement):
(WebInspector.StylePropertyTreeElement.prototype):
LayoutTests:
* http/tests/inspector/elements-test.js:
(initialize_ElementTest.InspectorTest.rangeText):
(initialize_ElementTest.InspectorTest.generateUndoTest):
* inspector/elements/undo-dom-edits-2-expected.txt: Added.
* inspector/elements/undo-dom-edits-2.html: Added.
* inspector/elements/undo-dom-edits-expected.txt: Added.
* inspector/elements/undo-dom-edits.html: Added.
* inspector/styles/undo-add-property-expected.txt: Added.
* inspector/styles/undo-add-property.html: Added.
* inspector/styles/undo-change-property-expected.txt: Added.
* inspector/styles/undo-change-property.html: Added.
* inspector/styles/undo-property-toggle-expected.txt: Added.
* inspector/styles/undo-property-toggle.html: Added.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@106953
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2012-02-07 Pavel Feldman <pfeldman@google.com>
+
+ Web Inspector: add generic support for undo-ing DOM edits.
+ https://bugs.webkit.org/show_bug.cgi?id=77875
+
+ Reviewed by Yury Semikhatsky.
+
+ * http/tests/inspector/elements-test.js:
+ (initialize_ElementTest.InspectorTest.rangeText):
+ (initialize_ElementTest.InspectorTest.generateUndoTest):
+ * inspector/elements/undo-dom-edits-2-expected.txt: Added.
+ * inspector/elements/undo-dom-edits-2.html: Added.
+ * inspector/elements/undo-dom-edits-expected.txt: Added.
+ * inspector/elements/undo-dom-edits.html: Added.
+ * inspector/styles/undo-add-property-expected.txt: Added.
+ * inspector/styles/undo-add-property.html: Added.
+ * inspector/styles/undo-change-property-expected.txt: Added.
+ * inspector/styles/undo-change-property.html: Added.
+ * inspector/styles/undo-property-toggle-expected.txt: Added.
+ * inspector/styles/undo-property-toggle.html: Added.
+
2012-02-07 Tony Gentilcore <tonyg@chromium.org>
Unreviewed, more svg rebaselines following change in r106918.
if (!range)
return "[undefined-undefined]";
return "[" + range.start + "-" + range.end + "]";
-};
+}
+
+InspectorTest.generateUndoTest = function(testBody)
+{
+ function result(next)
+ {
+ var testNode = InspectorTest.expandedNodeWithId(/function\s([^(]*)/.exec(testBody)[1]);
+ InspectorTest.addResult("Initial:");
+ InspectorTest.dumpElementsTree(testNode);
+
+ testBody(step1);
+
+ function step1()
+ {
+ InspectorTest.addResult("Post-action:");
+ InspectorTest.dumpElementsTree(testNode);
+ DOMAgent.undo(step2);
+ }
+
+ function step2()
+ {
+ InspectorTest.addResult("Post-undo (initial):");
+ InspectorTest.dumpElementsTree(testNode);
+ next();
+ }
+ }
+ result.toString = function()
+ {
+ return testBody.toString();
+ }
+ return result;
+}
};
--- /dev/null
+Tests that DOM modifications done in the Elements panel are undoable (Part 2).
+
+
+Running: testSetUp
+
+Running: testSetAttribute
+Initial:
+- <div id="testSetAttribute">
+ <div foo="attribute value" id="node-to-set-attribute"></div>
+ </div>
+Post-action:
+- <div id="testSetAttribute">
+ <div id="node-to-set-attribute" bar="edited attribute"></div>
+ </div>
+Post-undo (initial):
+- <div id="testSetAttribute">
+ <div id="node-to-set-attribute" foo="attribute value"></div>
+ </div>
+
+Running: testRemoveAttribute
+Initial:
+- <div id="testRemoveAttribute">
+ <div foo="attribute value" id="node-to-remove-attribute"></div>
+ </div>
+Post-action:
+- <div id="testRemoveAttribute">
+ <div id="node-to-remove-attribute"></div>
+ </div>
+Post-undo (initial):
+- <div id="testRemoveAttribute">
+ <div id="node-to-remove-attribute" foo="attribute value"></div>
+ </div>
+
+Running: testAddAttribute
+Initial:
+- <div id="testAddAttribute">
+ <div id="node-to-add-attribute"></div>
+ </div>
+Post-action:
+- <div id="testAddAttribute">
+ <div id="node-to-add-attribute" newattr="new-value"></div>
+ </div>
+Post-undo (initial):
+- <div id="testAddAttribute">
+ <div id="node-to-add-attribute"></div>
+ </div>
+
--- /dev/null
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/elements-test.js"></script>
+<script>
+
+function test()
+{
+ var testSuite = [];
+
+ function testSetUp(next)
+ {
+ InspectorTest.expandElementsTree(next);
+ }
+ testSuite.push(testSetUp);
+
+
+ function testSetAttribute(callback)
+ {
+ var node = InspectorTest.expandedNodeWithId("node-to-set-attribute");
+ node.setAttribute("foo", "bar=\"edited attribute\"", callback);
+ }
+ testSuite.push(InspectorTest.generateUndoTest(testSetAttribute));
+
+
+ function testRemoveAttribute(callback)
+ {
+ var node = InspectorTest.expandedNodeWithId("node-to-remove-attribute");
+ node.removeAttribute("foo", callback);
+ }
+ testSuite.push(InspectorTest.generateUndoTest(testRemoveAttribute));
+
+
+ function testAddAttribute(callback)
+ {
+ var node = InspectorTest.expandedNodeWithId("node-to-add-attribute");
+ node.setAttribute("", "newattr=\"new-value\"", callback);
+ }
+ testSuite.push(InspectorTest.generateUndoTest(testAddAttribute));
+
+
+ InspectorTest.runTestSuite(testSuite);
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+<p>
+Tests that DOM modifications done in the Elements panel are undoable (Part 2).
+</p>
+
+<div style="display:none">
+ <div id="testSetAttribute">
+ <div foo="attribute value" id="node-to-set-attribute"></div>
+ </div>
+
+ <div id="testRemoveAttribute">
+ <div foo="attribute value" id="node-to-remove-attribute"></div>
+ </div>
+
+ <div id="testAddAttribute">
+ <div id="node-to-add-attribute"></div>
+ </div>
+</div>
+
+</body>
+</html>
--- /dev/null
+Tests that DOM modifications done in the Elements panel are undoable.
+
+
+Running: testSetUp
+
+Running: testRemove
+Initial:
+- <div id="testRemove">
+ <div id="node-to-remove"></div>
+ </div>
+Post-action:
+- <div id="testRemove">
+ </div>
+Post-undo (initial):
+- <div id="testRemove">
+ <div id="node-to-remove"></div>
+ </div>
+
+Running: testSetNodeName
+Initial:
+- <div id="testSetNodeName">
+ <div id="node-to-set-name"></div>
+ </div>
+Post-action:
+- <div id="testSetNodeName">
+ <span id="node-to-set-name"></span>
+ </div>
+Post-undo (initial):
+- <div id="testSetNodeName">
+ <div id="node-to-set-name"></div>
+ </div>
+
+Running: testSetNodeValue
+Initial:
+- <div id="testSetNodeValue">
+ <div id="node-to-set-value">Text</div>
+ </div>
+Post-action:
+- <div id="testSetNodeValue">
+ <div id="node-to-set-value">New Text</div>
+ </div>
+Post-undo (initial):
+- <div id="testSetNodeValue">
+ <div id="node-to-set-value">Text</div>
+ </div>
+
+Running: testEditAsHTML
+Initial:
+- <div id="testEditAsHTML">
+ - <div id="node-to-edit-as-html">
+ <span id="span">Text</span>
+ </div>
+ </div>
+Post-action:
+- <div id="testEditAsHTML">
+ - <div id="node-to-edit-as-html">
+ <div id="span2">Text2</div>
+ </div>
+ </div>
+Post-undo (initial):
+- <div id="testEditAsHTML">
+ - <div id="node-to-edit-as-html">
+ <span id="span">Text</span>
+ </div>
+ </div>
+
--- /dev/null
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/elements-test.js"></script>
+<script>
+
+function test()
+{
+ var testSuite = [];
+
+ function testSetUp(next)
+ {
+ InspectorTest.expandElementsTree(next);
+ }
+ testSuite.push(testSetUp);
+
+
+ function testRemove(callback)
+ {
+ var node = InspectorTest.expandedNodeWithId("node-to-remove");
+ node.removeNode(callback);
+ }
+ testSuite.push(InspectorTest.generateUndoTest(testRemove));
+
+
+ function testSetNodeName(callback)
+ {
+ var node = InspectorTest.expandedNodeWithId("node-to-set-name");
+ node.setNodeName("span", callback);
+ }
+ testSuite.push(InspectorTest.generateUndoTest(testSetNodeName));
+
+
+ function testSetNodeValue(callback)
+ {
+ var node = InspectorTest.expandedNodeWithId("node-to-set-value");
+ node.firstChild.setNodeValue("New Text", callback);
+ }
+ testSuite.push(InspectorTest.generateUndoTest(testSetNodeValue));
+
+ function testEditAsHTML(callback)
+ {
+ var node = InspectorTest.expandedNodeWithId("node-to-edit-as-html");
+ node.setOuterHTML("<div id=\"node-to-edit-as-html\"><div id=\"span2\">Text2</div></div>", callback);
+ }
+ testSuite.push(InspectorTest.generateUndoTest(testEditAsHTML));
+
+
+ InspectorTest.runTestSuite(testSuite);
+}
+
+</script>
+</head>
+
+<body onload="runTest()">
+<p>
+Tests that DOM modifications done in the Elements panel are undoable.
+</p>
+
+<div style="display:none">
+ <div id="testRemove">
+ <div id="node-to-remove"></div>
+ </div>
+
+ <div id="testSetNodeName">
+ <div id="node-to-set-name"></div>
+ </div>
+
+ <div id="testSetNodeValue">
+ <div id="node-to-set-value">Text</div>
+ </div>
+
+ <div id="testEditAsHTML">
+ <div id="node-to-edit-as-html"><span id="span">Text</span></div>
+ </div>
+</div>
+
+</body>
+</html>
--- /dev/null
+Tests that adding a property is undone properly.
+
+Initial value
+[expanded]
+element.style { ()
+
+======== Matched CSS Rules ========
+[expanded]
+#container { (undo-add-property.html:7)
+font-weight: bold;
+
+[expanded]
+div { (user agent stylesheet)
+display: block;
+
+
+After adding property
+[expanded]
+element.style { ()
+
+======== Matched CSS Rules ========
+[expanded]
+#container { (undo-add-property.html:7)
+font-weight: bold;
+margin-left: 1px;
+
+[expanded]
+div { (user agent stylesheet)
+display: block;
+
+
+After undo
+[expanded]
+element.style { ()
+
+======== Matched CSS Rules ========
+[expanded]
+#container { (undo-add-property.html:7)
+font-weight: bold;
+
+[expanded]
+div { (user agent stylesheet)
+display: block;
+
+
+
--- /dev/null
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/elements-test.js"></script>
+
+<style>
+#container {
+ font-weight: bold
+}
+</style>
+
+<script>
+
+function test()
+{
+ InspectorTest.selectNodeAndWaitForStyles("container", step1);
+
+ function step1()
+ {
+ InspectorTest.addResult("Initial value");
+ InspectorTest.dumpSelectedElementStyles(true);
+
+ var treeItem = InspectorTest.getMatchedStylePropertyTreeItem("font-weight");
+ var treeElement = treeItem.section.addNewBlankProperty();
+ treeElement.startEditing();
+ treeElement.nameElement.textContent = "margin-left";
+ treeElement.nameElement.dispatchEvent(InspectorTest.createKeyEvent("Enter"));
+ treeElement.valueElement.textContent = "1px";
+ treeElement.nameElement.dispatchEvent(InspectorTest.createKeyEvent("Enter"));
+ InspectorTest.runAfterPendingDispatches(step2);
+ }
+
+ function step2()
+ {
+ InspectorTest.addResult("After adding property");
+ InspectorTest.dumpSelectedElementStyles(true);
+ DOMAgent.undo(step3);
+ }
+
+ function step3()
+ {
+ InspectorTest.waitForStyles("container", step4);
+ WebInspector.panels.elements._updateSidebars();
+ }
+
+ function step4()
+ {
+ InspectorTest.addResult("After undo");
+ InspectorTest.dumpSelectedElementStyles(true);
+ InspectorTest.completeTest();
+ }
+}
+</script>
+</head>
+
+<body onload="runTest()">
+<p>
+Tests that adding a property is undone properly.
+</p>
+
+<div id="container">
+</div>
+
+</body>
+</html>
--- /dev/null
+Tests that changing a property is undone properly.
+
+Initial value
+[expanded]
+element.style { ()
+
+======== Matched CSS Rules ========
+[expanded]
+#container { (undo-change-property.html:7)
+font-weight: bold;
+
+[expanded]
+div { (user agent stylesheet)
+display: block;
+
+
+After changing property
+[expanded]
+element.style { ()
+
+======== Matched CSS Rules ========
+[expanded]
+#container { (undo-change-property.html:7)
+font-weight: normal;
+
+[expanded]
+div { (user agent stylesheet)
+display: block;
+
+
+After undo
+[expanded]
+element.style { ()
+
+======== Matched CSS Rules ========
+[expanded]
+#container { (undo-change-property.html:7)
+font-weight: bold;
+
+[expanded]
+div { (user agent stylesheet)
+display: block;
+
+
+
--- /dev/null
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/elements-test.js"></script>
+
+<style>
+#container {
+ font-weight: bold
+}
+</style>
+
+<script>
+
+function test()
+{
+ InspectorTest.selectNodeAndWaitForStyles("container", step1);
+
+ function step1()
+ {
+ InspectorTest.addResult("Initial value");
+ InspectorTest.dumpSelectedElementStyles(true);
+
+ var treeItem = InspectorTest.getMatchedStylePropertyTreeItem("font-weight");
+ treeItem.applyStyleText("font-weight: normal", true, false);
+ InspectorTest.waitForStyles("container", step2);
+ }
+
+ function step2()
+ {
+ InspectorTest.addResult("After changing property");
+ InspectorTest.dumpSelectedElementStyles(true);
+ DOMAgent.undo(step3);
+ }
+
+ function step3()
+ {
+ InspectorTest.waitForStyles("container", step4);
+ WebInspector.panels.elements._updateSidebars();
+ }
+
+ function step4()
+ {
+ InspectorTest.addResult("After undo");
+ InspectorTest.dumpSelectedElementStyles(true);
+ InspectorTest.completeTest();
+ }
+}
+</script>
+</head>
+
+<body onload="runTest()">
+<p>
+Tests that changing a property is undone properly.
+</p>
+
+<div id="container">
+</div>
+
+</body>
+</html>
--- /dev/null
+Tests that disabling style is undone properly.
+
+Before disable
+font-weight: bold;
+After disable
+/-- overloaded --/ /-- disabled --/ font-weight: bold;
+After undo
+font-weight: bold;
+
--- /dev/null
+<html>
+<head>
+<script src="../../http/tests/inspector/inspector-test.js"></script>
+<script src="../../http/tests/inspector/elements-test.js"></script>
+<script>
+
+function test()
+{
+ InspectorTest.selectNodeAndWaitForStyles("container", step1);
+
+ function step1(node)
+ {
+ InspectorTest.addResult("Before disable");
+ var treeItem = InspectorTest.getElementStylePropertyTreeItem("font-weight");
+ InspectorTest.dumpStyleTreeItem(treeItem, "");
+
+ treeItem.toggleEnabled({ target: { checked: false } });
+ InspectorTest.waitForStyles("container", step2);
+ }
+
+ function step2()
+ {
+ InspectorTest.addResult("After disable");
+ var treeItem = InspectorTest.getElementStylePropertyTreeItem("font-weight");
+ InspectorTest.dumpStyleTreeItem(treeItem, "");
+
+ DOMAgent.undo();
+ InspectorTest.waitForStyles("container", step3);
+ }
+
+ function step3()
+ {
+ InspectorTest.addResult("After undo");
+ var treeItem = InspectorTest.getElementStylePropertyTreeItem("font-weight");
+ InspectorTest.dumpStyleTreeItem(treeItem, "");
+
+ InspectorTest.completeTest();
+ }
+}
+</script>
+</head>
+
+<body onload="runTest()">
+<p>
+Tests that disabling style is undone properly.
+</p>
+
+<div id="container" style="font-weight:bold">
+</div>
+
+</body>
+</html>
inspector/InspectorFileSystemAgent.cpp
inspector/InspectorFrontendClientLocal.cpp
inspector/InspectorFrontendHost.cpp
+ inspector/InspectorHistory.cpp
inspector/InspectorIndexedDBAgent.cpp
inspector/InspectorInstrumentation.cpp
inspector/InspectorMemoryAgent.cpp
+2012-02-07 Pavel Feldman <pfeldman@google.com>
+
+ Web Inspector: add generic support for undo-ing DOM edits.
+ https://bugs.webkit.org/show_bug.cgi?id=77875
+
+ Reviewed by Yury Semikhatsky.
+
+ This change introduces InspectorHistory::Action that encapsulates all DOM modifications
+ initiated by the inspector. There is a way to undo these actions up until the undoable
+ state marker.
+
+ Tests: inspector/elements/undo-dom-edits-2.html
+ inspector/elements/undo-dom-edits.html
+ inspector/styles/undo-add-property.html
+ inspector/styles/undo-change-property.html
+ inspector/styles/undo-property-toggle.html
+
+ * CMakeLists.txt:
+ * GNUmakefile.list.am:
+ * Target.pri:
+ * WebCore.gypi:
+ * WebCore.vcproj/WebCore.vcproj:
+ * WebCore.xcodeproj/project.pbxproj:
+ * inspector/Inspector.json:
+ * inspector/InspectorAllInOne.cpp:
+ * inspector/InspectorCSSAgent.cpp:
+ (InspectorCSSAgent::StyleSheetAction):
+ (WebCore::InspectorCSSAgent::StyleSheetAction::StyleSheetAction):
+ (WebCore::InspectorCSSAgent::StyleSheetAction::perform):
+ (WebCore::InspectorCSSAgent::StyleSheetAction::undo):
+ (WebCore):
+ (InspectorCSSAgent::SetStyleSheetTextAction):
+ (WebCore::InspectorCSSAgent::SetStyleSheetTextAction::SetStyleSheetTextAction):
+ (WebCore::InspectorCSSAgent::SetStyleSheetTextAction::perform):
+ (WebCore::InspectorCSSAgent::SetStyleSheetTextAction::undo):
+ (InspectorCSSAgent::SetPropertyTextAction):
+ (WebCore::InspectorCSSAgent::SetPropertyTextAction::SetPropertyTextAction):
+ (WebCore::InspectorCSSAgent::SetPropertyTextAction::toString):
+ (WebCore::InspectorCSSAgent::SetPropertyTextAction::perform):
+ (WebCore::InspectorCSSAgent::SetPropertyTextAction::undo):
+ (WebCore::InspectorCSSAgent::SetPropertyTextAction::mergeId):
+ (WebCore::InspectorCSSAgent::SetPropertyTextAction::merge):
+ (InspectorCSSAgent::TogglePropertyAction):
+ (WebCore::InspectorCSSAgent::TogglePropertyAction::TogglePropertyAction):
+ (WebCore::InspectorCSSAgent::TogglePropertyAction::perform):
+ (WebCore::InspectorCSSAgent::TogglePropertyAction::undo):
+ (WebCore::InspectorCSSAgent::getStyleSheetText):
+ (WebCore::InspectorCSSAgent::setStyleSheetText):
+ (WebCore::InspectorCSSAgent::setPropertyText):
+ (WebCore::InspectorCSSAgent::toggleProperty):
+ * inspector/InspectorCSSAgent.h:
+ (InspectorCSSAgent):
+ * inspector/InspectorDOMAgent.cpp:
+ (InspectorDOMAgent::DOMAction):
+ (WebCore::InspectorDOMAgent::DOMAction::DOMAction):
+ (WebCore::InspectorDOMAgent::DOMAction::perform):
+ (WebCore::InspectorDOMAgent::DOMAction::undo):
+ (WebCore):
+ (InspectorDOMAgent::RemoveChildAction):
+ (WebCore::InspectorDOMAgent::RemoveChildAction::RemoveChildAction):
+ (WebCore::InspectorDOMAgent::RemoveChildAction::perform):
+ (WebCore::InspectorDOMAgent::RemoveChildAction::undo):
+ (InspectorDOMAgent::InsertBeforeAction):
+ (WebCore::InspectorDOMAgent::InsertBeforeAction::InsertBeforeAction):
+ (WebCore::InspectorDOMAgent::InsertBeforeAction::perform):
+ (WebCore::InspectorDOMAgent::InsertBeforeAction::undo):
+ (InspectorDOMAgent::RemoveAttributeAction):
+ (WebCore::InspectorDOMAgent::RemoveAttributeAction::RemoveAttributeAction):
+ (WebCore::InspectorDOMAgent::RemoveAttributeAction::perform):
+ (WebCore::InspectorDOMAgent::RemoveAttributeAction::undo):
+ (InspectorDOMAgent::SetAttributeAction):
+ (WebCore::InspectorDOMAgent::SetAttributeAction::SetAttributeAction):
+ (WebCore::InspectorDOMAgent::SetAttributeAction::perform):
+ (WebCore::InspectorDOMAgent::SetAttributeAction::undo):
+ (InspectorDOMAgent::SetOuterHTMLAction):
+ (WebCore::InspectorDOMAgent::SetOuterHTMLAction::SetOuterHTMLAction):
+ (WebCore::InspectorDOMAgent::SetOuterHTMLAction::perform):
+ (WebCore::InspectorDOMAgent::SetOuterHTMLAction::undo):
+ (WebCore::InspectorDOMAgent::SetOuterHTMLAction::newNode):
+ (InspectorDOMAgent::ReplaceWholeTextAction):
+ (WebCore::InspectorDOMAgent::ReplaceWholeTextAction::ReplaceWholeTextAction):
+ (WebCore::InspectorDOMAgent::ReplaceWholeTextAction::perform):
+ (WebCore::InspectorDOMAgent::ReplaceWholeTextAction::undo):
+ (WebCore::InspectorDOMAgent::InspectorDOMAgent):
+ (WebCore::InspectorDOMAgent::reset):
+ (WebCore::InspectorDOMAgent::setAttributeValue):
+ (WebCore::InspectorDOMAgent::setAttributesAsText):
+ (WebCore::InspectorDOMAgent::removeAttribute):
+ (WebCore::InspectorDOMAgent::removeNode):
+ (WebCore::InspectorDOMAgent::setNodeName):
+ (WebCore::InspectorDOMAgent::setOuterHTML):
+ (WebCore::InspectorDOMAgent::setNodeValue):
+ (WebCore::InspectorDOMAgent::moveTo):
+ (WebCore::InspectorDOMAgent::undo):
+ (WebCore::InspectorDOMAgent::markUndoableState):
+ * inspector/InspectorDOMAgent.h:
+ (InspectorDOMAgent):
+ (WebCore::InspectorDOMAgent::history):
+ * inspector/InspectorHistory.cpp: Added.
+ (WebCore::InspectorHistory::Action::Action):
+ (WebCore):
+ (WebCore::InspectorHistory::Action::~Action):
+ (WebCore::InspectorHistory::Action::toString):
+ (WebCore::InspectorHistory::Action::isUndoableStateMark):
+ (WebCore::InspectorHistory::Action::mergeId):
+ (WebCore::InspectorHistory::Action::merge):
+ (WebCore::InspectorHistory::InspectorHistory):
+ (WebCore::InspectorHistory::~InspectorHistory):
+ (WebCore::InspectorHistory::perform):
+ (WebCore::InspectorHistory::markUndoableState):
+ (WebCore::InspectorHistory::undo):
+ (WebCore::InspectorHistory::reset):
+ * inspector/InspectorHistory.h: Added.
+ (WebCore):
+ (InspectorHistory):
+ (Action):
+ * inspector/InspectorStyleSheet.cpp:
+ (WebCore::InspectorStyle::setPropertyText):
+ (WebCore::InspectorStyle::styleText):
+ (WebCore::InspectorStyleSheet::addRule):
+ (WebCore::InspectorStyleSheet::buildObjectForStyleSheet):
+ (WebCore::InspectorStyleSheet::buildObjectForStyle):
+ (WebCore::InspectorStyleSheet::setPropertyText):
+ (WebCore::InspectorStyleSheet::getText):
+ (WebCore::InspectorStyleSheetForInlineStyle::getText):
+ * inspector/InspectorStyleSheet.h:
+ (InspectorStyle):
+ (InspectorStyleSheet):
+ (InspectorStyleSheetForInlineStyle):
+ * inspector/front-end/CSSStyleModel.js:
+ (WebInspector.CSSProperty.prototype.setText):
+ * inspector/front-end/ElementsPanel.js:
+ (WebInspector.ElementsPanel.prototype._selectedNodeChanged):
+ (WebInspector.ElementsPanel.prototype._updateSidebars):
+ (WebInspector.ElementsPanel.prototype.handleShortcut):
+ * inspector/front-end/StylesSidebarPane.js:
+ (WebInspector.StylePropertiesSection.prototype.onpopulate):
+ (WebInspector.StylePropertiesSection.prototype.addNewBlankProperty):
+ (WebInspector.ComputedStylePropertiesSection.prototype.onpopulate):
+ (WebInspector.StylePropertyTreeElement):
+ (WebInspector.StylePropertyTreeElement.prototype):
+
2012-02-07 Sheriff Bot <webkit.review.bot@gmail.com>
Unreviewed, rolling out r106935.
Source/WebCore/inspector/InspectorFrontendClientLocal.h \
Source/WebCore/inspector/InspectorFrontendHost.cpp \
Source/WebCore/inspector/InspectorFrontendHost.h \
+ Source/WebCore/inspector/InspectorHistory.cpp \
+ Source/WebCore/inspector/InspectorHistory.h \
Source/WebCore/inspector/InspectorIndexedDBAgent.h \
Source/WebCore/inspector/InspectorIndexedDBAgent.cpp \
Source/WebCore/inspector/InspectorInstrumentation.cpp \
inspector/InspectorDOMStorageResource.cpp \
inspector/InspectorFrontendClientLocal.cpp \
inspector/InspectorFrontendHost.cpp \
+ inspector/InspectorHistory.cpp \
inspector/InspectorInstrumentation.cpp \
inspector/InspectorMemoryAgent.cpp \
inspector/InspectorPageAgent.cpp \
inspector/InspectorFrontendClient.h \
inspector/InspectorFrontendClientLocal.h \
inspector/InspectorFrontendHost.h \
+ inspector/InspectorHistory.h \
inspector/InspectorInstrumentation.h \
inspector/InspectorMemoryAgent.h \
inspector/InspectorPageAgent.h \
'inspector/InspectorFrontendClientLocal.cpp',
'inspector/InspectorFrontendHost.cpp',
'inspector/InspectorFrontendHost.h',
+ 'inspector/InspectorHistory.cpp',
+ 'inspector/InspectorHistory.h',
'inspector/InspectorIndexedDBAgent.cpp',
'inspector/InspectorIndexedDBAgent.h',
'inspector/InspectorInstrumentation.cpp',
>
</File>
<File
+ RelativePath="..\inspector\InspectorHistory.cpp"
+ >
+ <FileConfiguration
+ Name="Release|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ <FileConfiguration
+ Name="Production|Win32"
+ ExcludedFromBuild="true"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ />
+ </FileConfiguration>
+ </File>
+ <File
+ RelativePath="..\inspector\InspectorHistory.h"
+ >
+ </File>
+ <File
RelativePath="..\inspector\InspectorIndexedDBAgent.cpp"
>
<FileConfiguration
7A1F2B52126C61B20006A7E6 /* InspectorClient.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A1F2B51126C61B20006A7E6 /* InspectorClient.cpp */; };
7A24587B1021EAF4000A00AA /* InspectorDOMAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A2458791021EAF4000A00AA /* InspectorDOMAgent.cpp */; };
7A24587C1021EAF4000A00AA /* InspectorDOMAgent.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A24587A1021EAF4000A00AA /* InspectorDOMAgent.h */; settings = {ATTRIBUTES = (Private, ); }; };
+ 7A54857F14E02D51006AE05A /* InspectorHistory.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A54857D14E02D51006AE05A /* InspectorHistory.cpp */; };
+ 7A54858014E02D51006AE05A /* InspectorHistory.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A54857E14E02D51006AE05A /* InspectorHistory.h */; };
7A674BDB0F9EBF4E006CF099 /* PageGroupLoadDeferrer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A674BD90F9EBF4E006CF099 /* PageGroupLoadDeferrer.cpp */; };
7A674BDC0F9EBF4E006CF099 /* PageGroupLoadDeferrer.h in Headers */ = {isa = PBXBuildFile; fileRef = 7A674BDA0F9EBF4E006CF099 /* PageGroupLoadDeferrer.h */; };
7A74ECBA101839A600BF939E /* InspectorDOMStorageAgent.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 7A74ECB8101839A500BF939E /* InspectorDOMStorageAgent.cpp */; };
7A1F2B51126C61B20006A7E6 /* InspectorClient.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorClient.cpp; sourceTree = "<group>"; };
7A2458791021EAF4000A00AA /* InspectorDOMAgent.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorDOMAgent.cpp; sourceTree = "<group>"; };
7A24587A1021EAF4000A00AA /* InspectorDOMAgent.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorDOMAgent.h; sourceTree = "<group>"; };
+ 7A54857D14E02D51006AE05A /* InspectorHistory.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = InspectorHistory.cpp; sourceTree = "<group>"; };
+ 7A54857E14E02D51006AE05A /* InspectorHistory.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InspectorHistory.h; sourceTree = "<group>"; };
7A563E5412DE32B000F4536D /* InjectedScriptSource.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = InjectedScriptSource.h; sourceTree = "<group>"; };
7A563F9512DF5C9100F4536D /* InjectedScriptSource.js */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.javascript; path = InjectedScriptSource.js; sourceTree = "<group>"; };
7A674BD90F9EBF4E006CF099 /* PageGroupLoadDeferrer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PageGroupLoadDeferrer.cpp; sourceTree = "<group>"; };
7A0E770B10C00A8800A0276E /* InspectorFrontendHost.cpp */,
7A0E770C10C00A8800A0276E /* InspectorFrontendHost.h */,
7A0E770D10C00A8800A0276E /* InspectorFrontendHost.idl */,
+ 7A54857D14E02D51006AE05A /* InspectorHistory.cpp */,
+ 7A54857E14E02D51006AE05A /* InspectorHistory.h */,
7ACD88D114C08BD60084EDD2 /* InspectorIndexedDBAgent.cpp */,
7ACD88D214C08BD60084EDD2 /* InspectorIndexedDBAgent.h */,
20D629241253690B00081543 /* InspectorInstrumentation.cpp */,
1AAADDBF14DC640700AF64B3 /* ScrollingTreeState.h in Headers */,
1AAADDE414DC8C8F00AF64B3 /* ScrollingTreeNode.h in Headers */,
1AAADDE914DC8DF800AF64B3 /* ScrollingTreeNodeMac.h in Headers */,
+ 7A54858014E02D51006AE05A /* InspectorHistory.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
1AAADDDA14DC74EC00AF64B3 /* ScrollingTreeStateMac.mm in Sources */,
1AAADDE314DC8C8F00AF64B3 /* ScrollingTreeNode.cpp in Sources */,
1AAADDE814DC8DF800AF64B3 /* ScrollingTreeNodeMac.mm in Sources */,
+ 7A54857F14E02D51006AE05A /* InspectorHistory.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
],
"description": "Toggles mouse event-based touch event emulation.",
"hidden": true
+ },
+ {
+ "name": "undo",
+ "description": "Undoes the last performed action.",
+ "hidden": true
+ },
+ {
+ "name": "markUndoableState",
+ "description": "Marks last undoable state.",
+ "hidden": true
}
],
"events": [
#include "InspectorFileSystemAgent.cpp"
#include "InspectorFrontendClientLocal.cpp"
#include "InspectorFrontendHost.cpp"
+#include "InspectorHistory.cpp"
#include "InspectorIndexedDBAgent.cpp"
#include "InspectorInstrumentation.cpp"
#include "InspectorMemoryAgent.cpp"
#include "DOMWindow.h"
#include "HTMLHeadElement.h"
#include "InspectorDOMAgent.h"
+#include "InspectorHistory.h"
#include "InspectorState.h"
#include "InspectorValues.h"
#include "InstrumentingAgents.h"
return result.release();
}
+class InspectorCSSAgent::StyleSheetAction : public InspectorHistory::Action {
+ WTF_MAKE_NONCOPYABLE(StyleSheetAction);
+public:
+ StyleSheetAction(const String& name, InspectorCSSAgent* cssAgent, const String& styleSheetId)
+ : InspectorHistory::Action(name)
+ , m_cssAgent(cssAgent)
+ , m_styleSheetId(styleSheetId)
+ {
+ }
+
+ virtual bool perform(ErrorString* errorString)
+ {
+ InspectorStyleSheet* styleSheet = m_cssAgent->assertStyleSheetForId(errorString, m_styleSheetId);
+ if (!styleSheet)
+ return false;
+ return perform(styleSheet, errorString);
+ }
+
+ virtual bool undo(ErrorString* errorString)
+ {
+ InspectorStyleSheet* styleSheet = m_cssAgent->assertStyleSheetForId(errorString, m_styleSheetId);
+ if (!styleSheet)
+ return false;
+ return undo(styleSheet, errorString);
+ }
+
+ virtual bool perform(InspectorStyleSheet*, ErrorString*) = 0;
+
+ virtual bool undo(InspectorStyleSheet*, ErrorString*) = 0;
+
+protected:
+ InspectorCSSAgent* m_cssAgent;
+ String m_styleSheetId;
+};
+
+class InspectorCSSAgent::SetStyleSheetTextAction : public InspectorCSSAgent::StyleSheetAction {
+ WTF_MAKE_NONCOPYABLE(SetStyleSheetTextAction);
+public:
+ SetStyleSheetTextAction(InspectorCSSAgent* cssAgent, const String& styleSheetId, const String& text)
+ : InspectorCSSAgent::StyleSheetAction("SetStyleSheetText", cssAgent, styleSheetId)
+ , m_text(text)
+ {
+ }
+
+ virtual bool perform(InspectorStyleSheet* inspectorStyleSheet, ErrorString*)
+ {
+ if (!inspectorStyleSheet->getText(&m_oldText))
+ return false;
+
+ if (inspectorStyleSheet->setText(m_text)) {
+ inspectorStyleSheet->reparseStyleSheet(m_text);
+ return true;
+ }
+ return false;
+ }
+
+ virtual bool undo(InspectorStyleSheet* inspectorStyleSheet, ErrorString*)
+ {
+ if (inspectorStyleSheet->setText(m_oldText)) {
+ inspectorStyleSheet->reparseStyleSheet(m_oldText);
+ return true;
+ }
+ return false;
+ }
+
+private:
+ String m_text;
+ String m_oldText;
+};
+
+class InspectorCSSAgent::SetPropertyTextAction : public InspectorCSSAgent::StyleSheetAction {
+ WTF_MAKE_NONCOPYABLE(SetPropertyTextAction);
+public:
+ SetPropertyTextAction(InspectorCSSAgent* cssAgent, const String& styleSheetId, const InspectorCSSId& cssId, unsigned propertyIndex, const String& text, bool overwrite)
+ : InspectorCSSAgent::StyleSheetAction("SetPropertyText", cssAgent, styleSheetId)
+ , m_cssId(cssId)
+ , m_propertyIndex(propertyIndex)
+ , m_text(text)
+ , m_overwrite(overwrite)
+ {
+ }
+
+ virtual String toString()
+ {
+ return mergeId() + ": " + m_oldText + " -> " + m_text;
+ }
+
+ virtual bool perform(InspectorStyleSheet* inspectorStyleSheet, ErrorString* errorString)
+ {
+ String oldText;
+ bool result = inspectorStyleSheet->setPropertyText(errorString, m_cssId, m_propertyIndex, m_text, m_overwrite, &oldText);
+ m_oldText = oldText.stripWhiteSpace();
+ // FIXME: remove this once the model handles this case.
+ if (!m_oldText.endsWith(";"))
+ m_oldText += ";";
+ return result;
+ }
+
+ virtual bool undo(InspectorStyleSheet* inspectorStyleSheet, ErrorString* errorString)
+ {
+ String placeholder;
+ return inspectorStyleSheet->setPropertyText(errorString, m_cssId, m_propertyIndex, m_overwrite ? m_oldText : "", true, &placeholder);
+ }
+
+ virtual String mergeId()
+ {
+ return String::format("SetPropertyText %s:%u:%s", m_styleSheetId.utf8().data(), m_propertyIndex, m_overwrite ? "true" : "false");
+ }
+
+ virtual void merge(PassOwnPtr<Action> action)
+ {
+ ASSERT(action->mergeId() == mergeId());
+
+ SetPropertyTextAction* other = static_cast<SetPropertyTextAction*>(action.get());
+ m_text = other->m_text;
+ }
+
+private:
+ InspectorCSSId m_cssId;
+ unsigned m_propertyIndex;
+ String m_text;
+ String m_oldText;
+ bool m_overwrite;
+};
+
+class InspectorCSSAgent::TogglePropertyAction : public InspectorCSSAgent::StyleSheetAction {
+ WTF_MAKE_NONCOPYABLE(TogglePropertyAction);
+public:
+ TogglePropertyAction(InspectorCSSAgent* cssAgent, const String& styleSheetId, const InspectorCSSId& cssId, unsigned propertyIndex, bool disable)
+ : InspectorCSSAgent::StyleSheetAction("ToggleProperty", cssAgent, styleSheetId)
+ , m_cssId(cssId)
+ , m_propertyIndex(propertyIndex)
+ , m_disable(disable)
+ {
+ }
+
+ virtual bool perform(InspectorStyleSheet* inspectorStyleSheet, ErrorString* errorString)
+ {
+ return inspectorStyleSheet->toggleProperty(errorString, m_cssId, m_propertyIndex, m_disable);
+ }
+
+ virtual bool undo(InspectorStyleSheet* inspectorStyleSheet, ErrorString* errorString)
+ {
+ return inspectorStyleSheet->toggleProperty(errorString, m_cssId, m_propertyIndex, !m_disable);
+ }
+
+private:
+ InspectorCSSId m_cssId;
+ unsigned m_propertyIndex;
+ bool m_disable;
+};
+
// static
CSSStyleRule* InspectorCSSAgent::asCSSStyleRule(CSSRule* rule)
{
if (!inspectorStyleSheet)
return;
- inspectorStyleSheet->text(result);
+ inspectorStyleSheet->getText(result);
}
void InspectorCSSAgent::setStyleSheetText(ErrorString* errorString, const String& styleSheetId, const String& text)
{
- InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
- if (!inspectorStyleSheet)
- return;
-
- if (inspectorStyleSheet->setText(text))
- inspectorStyleSheet->reparseStyleSheet(text);
- else
- *errorString = "Internal error setting style sheet text";
+ m_domAgent->history()->perform(adoptPtr(new SetStyleSheetTextAction(this, styleSheetId, text)), errorString);
+ m_domAgent->history()->markUndoableState();
}
void InspectorCSSAgent::setPropertyText(ErrorString* errorString, const RefPtr<InspectorObject>& fullStyleId, int propertyIndex, const String& text, bool overwrite, RefPtr<InspectorObject>& result)
if (!inspectorStyleSheet)
return;
- bool success = inspectorStyleSheet->setPropertyText(errorString, compoundId, propertyIndex, text, overwrite);
+ bool success = m_domAgent->history()->perform(adoptPtr(new SetPropertyTextAction(this, compoundId.styleSheetId(), compoundId, propertyIndex, text, overwrite)), errorString);
if (success)
result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
}
if (!inspectorStyleSheet)
return;
- bool success = inspectorStyleSheet->toggleProperty(errorString, compoundId, propertyIndex, disable);
+ bool success = m_domAgent->history()->perform(adoptPtr(new TogglePropertyAction(this, compoundId.styleSheetId(), compoundId, propertyIndex, disable)), errorString);
if (success)
result = inspectorStyleSheet->buildObjectForStyle(inspectorStyleSheet->styleForId(compoundId));
+ m_domAgent->history()->markUndoableState();
}
void InspectorCSSAgent::setRuleSelector(ErrorString* errorString, const RefPtr<InspectorObject>& fullRuleId, const String& selector, RefPtr<InspectorObject>& result)
void didProcessRule();
private:
+ class StyleSheetAction;
+ class SetStyleSheetTextAction;
+ class SetPropertyTextAction;
+ class TogglePropertyAction;
+
InspectorCSSAgent(InstrumentingAgents*, InspectorState*, InspectorDOMAgent*);
typedef HashMap<String, RefPtr<InspectorStyleSheet> > IdToInspectorStyleSheet;
m_elements.clear();
}
+class InspectorDOMAgent::DOMAction : public InspectorHistory::Action {
+public:
+ DOMAction(const String& name) : InspectorHistory::Action(name) { }
+
+ virtual bool perform(ErrorString* errorString)
+ {
+ ExceptionCode ec = 0;
+ bool result = perform(ec);
+ if (ec) {
+ ExceptionCodeDescription description(ec);
+ *errorString = description.name;
+ }
+ return result && !ec;
+ }
+
+ virtual bool undo(ErrorString* errorString)
+ {
+ ExceptionCode ec = 0;
+ bool result = undo(ec);
+ if (ec) {
+ ExceptionCodeDescription description(ec);
+ *errorString = description.name;
+ }
+ return result && !ec;
+ }
+
+ virtual bool perform(ExceptionCode&) = 0;
+
+ virtual bool undo(ExceptionCode&) = 0;
+
+private:
+ RefPtr<Node> m_parentNode;
+ RefPtr<Node> m_node;
+ RefPtr<Node> m_anchorNode;
+};
+
+class InspectorDOMAgent::RemoveChildAction : public InspectorDOMAgent::DOMAction {
+ WTF_MAKE_NONCOPYABLE(RemoveChildAction);
+public:
+ RemoveChildAction(Node* parentNode, Node* node)
+ : InspectorDOMAgent::DOMAction("RemoveChild")
+ , m_parentNode(parentNode)
+ , m_node(node)
+ {
+ }
+
+ virtual bool perform(ExceptionCode& ec)
+ {
+ m_anchorNode = m_node->nextSibling();
+ return m_parentNode->removeChild(m_node.get(), ec);
+ }
+
+ virtual bool undo(ExceptionCode& ec)
+ {
+ return m_parentNode->insertBefore(m_node.get(), m_anchorNode.get(), ec);
+ }
+
+private:
+ RefPtr<Node> m_parentNode;
+ RefPtr<Node> m_node;
+ RefPtr<Node> m_anchorNode;
+};
+
+class InspectorDOMAgent::InsertBeforeAction : public InspectorDOMAgent::DOMAction {
+ WTF_MAKE_NONCOPYABLE(InsertBeforeAction);
+public:
+ InsertBeforeAction(Node* parentNode, Node* node, Node* anchorNode)
+ : InspectorDOMAgent::DOMAction("InsertBefore")
+ , m_parentNode(parentNode)
+ , m_node(node)
+ , m_anchorNode(anchorNode)
+ {
+ }
+
+ virtual bool perform(ExceptionCode& ec)
+ {
+ if (m_node->parentNode()) {
+ m_removeChildAction = adoptPtr(new RemoveChildAction(m_node->parentNode(), m_node.get()));
+ if (!m_removeChildAction->perform(ec))
+ return false;
+ }
+ return m_parentNode->insertBefore(m_node.get(), m_anchorNode.get(), ec);
+ }
+
+ virtual bool undo(ExceptionCode& ec)
+ {
+ if (m_removeChildAction)
+ return m_removeChildAction->undo(ec);
+
+ return m_parentNode->removeChild(m_node.get(), ec);
+ }
+
+private:
+ RefPtr<Node> m_parentNode;
+ RefPtr<Node> m_node;
+ RefPtr<Node> m_anchorNode;
+ OwnPtr<RemoveChildAction> m_removeChildAction;
+};
+
+class InspectorDOMAgent::RemoveAttributeAction : public InspectorDOMAgent::DOMAction {
+ WTF_MAKE_NONCOPYABLE(RemoveAttributeAction);
+public:
+ RemoveAttributeAction(Element* element, const String& name)
+ : InspectorDOMAgent::DOMAction("RemoveAttribute")
+ , m_element(element)
+ , m_name(name)
+ {
+ }
+
+ virtual bool perform(ExceptionCode&)
+ {
+ m_value = m_element->getAttribute(m_name);
+ m_element->removeAttribute(m_name);
+ return true;
+ }
+
+ virtual bool undo(ExceptionCode& ec)
+ {
+ m_element->setAttribute(m_name, m_value, ec);
+ return true;
+ }
+
+private:
+ RefPtr<Element> m_element;
+ String m_name;
+ String m_value;
+};
+
+class InspectorDOMAgent::SetAttributeAction : public InspectorDOMAgent::DOMAction {
+ WTF_MAKE_NONCOPYABLE(SetAttributeAction);
+public:
+ SetAttributeAction(Element* element, const String& name, const String& value)
+ : InspectorDOMAgent::DOMAction("SetAttribute")
+ , m_element(element)
+ , m_name(name)
+ , m_value(value)
+ , m_hadAttribute(false)
+ {
+ }
+
+ virtual bool perform(ExceptionCode& ec)
+ {
+ m_hadAttribute = m_element->hasAttribute(m_name);
+ if (m_hadAttribute)
+ m_oldValue = m_element->getAttribute(m_name);
+ m_element->setAttribute(m_name, m_value, ec);
+ return !ec;
+ }
+
+ virtual bool undo(ExceptionCode& ec)
+ {
+ if (m_hadAttribute)
+ m_element->setAttribute(m_name, m_oldValue, ec);
+ else
+ m_element->removeAttribute(m_name);
+ return true;
+ }
+
+private:
+ RefPtr<Element> m_element;
+ String m_name;
+ String m_value;
+ bool m_hadAttribute;
+ String m_oldValue;
+};
+
+class InspectorDOMAgent::SetOuterHTMLAction : public InspectorDOMAgent::DOMAction {
+ WTF_MAKE_NONCOPYABLE(SetOuterHTMLAction);
+public:
+ SetOuterHTMLAction(Node* node, const String& html)
+ : InspectorDOMAgent::DOMAction("SetOuterHTML")
+ , m_node(node)
+ , m_html(html)
+ , m_newNode(0)
+ {
+ }
+
+ virtual bool perform(ExceptionCode& ec)
+ {
+ m_oldHTML = createMarkup(m_node.get());
+ DOMEditor domEditor(m_node->ownerDocument());
+ m_newNode = domEditor.patchNode(m_node.get(), m_html, ec);
+ return !ec;
+ }
+
+ virtual bool undo(ExceptionCode& ec)
+ {
+ DOMEditor domEditor(m_node->ownerDocument());
+ domEditor.patchNode(m_node.get(), m_oldHTML, ec);
+ return !ec;
+ }
+
+ Node* newNode()
+ {
+ return m_newNode;
+ }
+
+private:
+ RefPtr<Node> m_node;
+ String m_html;
+ String m_oldHTML;
+ Node* m_newNode;
+};
+
+class InspectorDOMAgent::ReplaceWholeTextAction : public InspectorDOMAgent::DOMAction {
+ WTF_MAKE_NONCOPYABLE(ReplaceWholeTextAction);
+public:
+ ReplaceWholeTextAction(Text* textNode, const String& text)
+ : DOMAction("ReplaceWholeText")
+ , m_textNode(textNode)
+ , m_text(text)
+ {
+ }
+
+ virtual bool perform(ExceptionCode& ec)
+ {
+ m_oldText = m_textNode->wholeText();
+ m_textNode->replaceWholeText(m_text, ec);
+ return true;
+ }
+
+ virtual bool undo(ExceptionCode& ec)
+ {
+ m_textNode->replaceWholeText(m_oldText, ec);
+ return true;
+ }
+
+private:
+ RefPtr<Text> m_textNode;
+ String m_text;
+ String m_oldText;
+};
+
InspectorDOMAgent::InspectorDOMAgent(InstrumentingAgents* instrumentingAgents, InspectorPageAgent* pageAgent, InspectorClient* client, InspectorState* inspectorState, InjectedScriptManager* injectedScriptManager)
: InspectorBaseAgent<InspectorDOMAgent>("DOM", instrumentingAgents, inspectorState)
, m_pageAgent(pageAgent)
, m_domListener(0)
, m_lastNodeId(1)
, m_searchingForNode(false)
+ , m_history(adoptPtr(new InspectorHistory()))
{
}
void InspectorDOMAgent::reset()
{
- ErrorString error;
+ m_history->reset();
m_searchResults.clear();
discardBindings();
if (m_revalidateStyleAttrTask)
if (!element)
return;
- ExceptionCode ec = 0;
- element->setAttribute(name, value, ec);
- if (ec)
- *errorString = "Internal error: could not set attribute value";
+ m_history->perform(adoptPtr(new SetAttributeAction(element, name, value)), errorString);
+ m_history->markUndoableState();
}
void InspectorDOMAgent::setAttributesAsText(ErrorString* errorString, int elementId, const String& text, const String* const name)
Element* childElement = toElement(child);
if (!childElement->hasAttributes() && name) {
- element->removeAttribute(*name);
+ m_history->perform(adoptPtr(new RemoveAttributeAction(element, *name)), errorString);
return;
}
// Add attribute pair
const Attribute* attribute = childElement->attributeItem(i);
foundOriginalAttribute = foundOriginalAttribute || (name && attribute->name().toString() == *name);
- element->setAttribute(attribute->name(), attribute->value());
+ if (!m_history->perform(adoptPtr(new SetAttributeAction(element, attribute->name().toString(), attribute->value())), errorString))
+ return;
}
- if (!foundOriginalAttribute && name) {
- element->removeAttribute(*name);
- return;
- }
+ if (!foundOriginalAttribute && name && !name->stripWhiteSpace().isEmpty())
+ m_history->perform(adoptPtr(new RemoveAttributeAction(element, *name)), errorString);
+
+ m_history->markUndoableState();
}
void InspectorDOMAgent::removeAttribute(ErrorString* errorString, int elementId, const String& name)
{
Element* element = assertElement(errorString, elementId);
- if (element)
- element->removeAttribute(name);
+ if (!element)
+ return;
+
+ m_history->perform(adoptPtr(new RemoveAttributeAction(element, name)), errorString);
+ m_history->markUndoableState();
}
void InspectorDOMAgent::removeNode(ErrorString* errorString, int nodeId)
return;
}
- ExceptionCode ec = 0;
- parentNode->removeChild(node, ec);
- if (ec)
- *errorString = "Could not remove node due to DOM exception";
+ m_history->perform(adoptPtr(new RemoveChildAction(parentNode, node)), errorString);
+ m_history->markUndoableState();
}
-void InspectorDOMAgent::setNodeName(ErrorString*, int nodeId, const String& tagName, int* newId)
+void InspectorDOMAgent::setNodeName(ErrorString* errorString, int nodeId, const String& tagName, int* newId)
{
*newId = 0;
// Copy over the original node's children.
Node* child;
- while ((child = oldNode->firstChild()))
- newElem->appendChild(child, ec);
+ while ((child = oldNode->firstChild())) {
+ if (!m_history->perform(adoptPtr(new InsertBeforeAction(newElem.get(), child, 0)), errorString))
+ return;
+ }
// Replace the old node with the new node
ContainerNode* parent = oldNode->parentNode();
- parent->insertBefore(newElem, oldNode->nextSibling(), ec);
- parent->removeChild(oldNode, ec);
-
- if (ec)
+ if (!m_history->perform(adoptPtr(new InsertBeforeAction(parent, newElem.get(), oldNode->nextSibling())), errorString))
+ return;
+ if (!m_history->perform(adoptPtr(new RemoveChildAction(parent, oldNode)), errorString))
return;
+ m_history->markUndoableState();
*newId = pushNodePathToFrontend(newElem.get());
if (m_childrenRequested.contains(nodeId))
DOMEditor domEditor(document);
- ExceptionCode ec = 0;
- Node* newNode = domEditor.patchNode(node, outerHTML, ec);
- if (ec) {
- ExceptionCodeDescription description(ec);
- *errorString = description.name;
+ OwnPtr<SetOuterHTMLAction> action = adoptPtr(new SetOuterHTMLAction(node, outerHTML));
+ SetOuterHTMLAction* rawAction = action.get();
+ Node* newNode = 0;
+ if (!m_history->perform(action.release(), errorString))
return;
- }
+ m_history->markUndoableState();
+
+ newNode = rawAction->newNode();
if (!newNode) {
// The only child node has been deleted.
return;
}
- Text* textNode = static_cast<Text*>(node);
- ExceptionCode ec = 0;
- textNode->replaceWholeText(value, ec);
- if (ec)
- *errorString = "DOM Error while setting the node value";
+ m_history->perform(adoptPtr(new ReplaceWholeTextAction(static_cast<Text*>(node), value)), errorString);
}
void InspectorDOMAgent::getEventListenersForNode(ErrorString*, int nodeId, RefPtr<InspectorArray>& listenersArray)
m_client->hideHighlight();
}
-void InspectorDOMAgent::moveTo(ErrorString* error, int nodeId, int targetElementId, const int* const anchorNodeId, int* newNodeId)
+void InspectorDOMAgent::moveTo(ErrorString* errorString, int nodeId, int targetElementId, const int* const anchorNodeId, int* newNodeId)
{
- Node* node = assertNode(error, nodeId);
+ Node* node = assertNode(errorString, nodeId);
if (!node)
return;
- Element* targetElement = assertElement(error, targetElementId);
+ Element* targetElement = assertElement(errorString, targetElementId);
if (!targetElement)
return;
Node* anchorNode = 0;
if (anchorNodeId && *anchorNodeId) {
- anchorNode = assertNode(error, *anchorNodeId);
+ anchorNode = assertNode(errorString, *anchorNodeId);
if (!anchorNode)
return;
if (anchorNode->parentNode() != targetElement) {
- *error = "Anchor node must be child of the target element";
+ *errorString = "Anchor node must be child of the target element";
return;
}
}
- ExceptionCode ec = 0;
- bool success = targetElement->insertBefore(node, anchorNode, ec);
- if (ec || !success) {
- *error = "Could not drop node";
+ if (!m_history->perform(adoptPtr(new InsertBeforeAction(targetElement, node, anchorNode)), errorString))
return;
- }
+ m_history->markUndoableState();
+
*newNodeId = pushNodePathToFrontend(node);
}
#endif
}
+void InspectorDOMAgent::undo(ErrorString* errorString)
+{
+ m_history->undo(errorString);
+}
+
+void InspectorDOMAgent::markUndoableState(ErrorString*)
+{
+ m_history->markUndoableState();
+}
+
void InspectorDOMAgent::resolveNode(ErrorString* error, int nodeId, const String* const objectGroup, RefPtr<InspectorObject>& result)
{
String objectGroupName = objectGroup ? *objectGroup : "";
#include "InjectedScriptManager.h"
#include "InspectorBaseAgent.h"
#include "InspectorFrontend.h"
+#include "InspectorHistory.h"
#include "InspectorValues.h"
#include "Timer.h"
virtual void highlightFrame(ErrorString*, const String& frameId, const RefPtr<InspectorObject>* color, const RefPtr<InspectorObject>* outlineColor);
virtual void moveTo(ErrorString*, int nodeId, int targetNodeId, const int* anchorNodeId, int* newNodeId);
virtual void setTouchEmulationEnabled(ErrorString*, bool);
+ virtual void undo(ErrorString*);
+ virtual void markUndoableState(ErrorString*);
Node* highlightedNode() const;
void drawHighlight(GraphicsContext&) const;
void getHighlight(Highlight*) const;
+ InspectorHistory* history() { return m_history.get(); }
+
// We represent embedded doms as a part of the same hierarchy. Hence we treat children of frame owners differently.
// We also skip whitespace text nodes conditionally. Following methods encapsulate these specifics.
static Node* innerFirstChild(Node*);
Node* assertNode(ErrorString*, int nodeId);
private:
+ class DOMAction;
+ class RemoveChildAction;
+ class InsertBeforeAction;
+ class RemoveAttributeAction;
+ class SetAttributeAction;
+ class SetOuterHTMLAction;
+ class ReplaceWholeTextAction;
+
InspectorDOMAgent(InstrumentingAgents*, InspectorPageAgent*, InspectorClient*, InspectorState*, InjectedScriptManager*);
void setSearchingForNode(bool enabled, InspectorObject* highlightConfig);
OwnPtr<HighlightData> m_highlightData;
RefPtr<Node> m_nodeToFocus;
bool m_searchingForNode;
+ OwnPtr<InspectorHistory> m_history;
};
#endif // ENABLE(INSPECTOR)
--- /dev/null
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "InspectorHistory.h"
+
+#if ENABLE(INSPECTOR)
+
+#include "Node.h"
+
+namespace WebCore {
+
+namespace {
+
+class UndoableStateMark : public InspectorHistory::Action {
+public:
+ UndoableStateMark() : InspectorHistory::Action("[UndoableState]") { }
+
+ virtual bool perform(ErrorString*) { return true; }
+
+ virtual bool undo(ErrorString*) { return true; }
+
+ virtual bool isUndoableStateMark() { return true; }
+};
+
+}
+
+InspectorHistory::Action::Action(const String& name) : m_name(name)
+{
+}
+
+InspectorHistory::Action::~Action()
+{
+}
+
+String InspectorHistory::Action::toString()
+{
+ return m_name;
+}
+
+bool InspectorHistory::Action::isUndoableStateMark()
+{
+ return false;
+}
+
+String InspectorHistory::Action::mergeId()
+{
+ return "";
+}
+
+void InspectorHistory::Action::merge(PassOwnPtr<Action>)
+{
+}
+
+InspectorHistory::InspectorHistory() { }
+
+InspectorHistory::~InspectorHistory() { }
+
+bool InspectorHistory::perform(PassOwnPtr<Action> action, ErrorString* errorString)
+{
+ if (!action->perform(errorString))
+ return false;
+
+ if (!m_history.isEmpty() && !action->mergeId().isEmpty() && action->mergeId() == m_history.first()->mergeId())
+ m_history.first()->merge(action);
+ else
+ m_history.prepend(action);
+
+ return true;
+}
+
+void InspectorHistory::markUndoableState()
+{
+ m_history.prepend(adoptPtr(new UndoableStateMark()));
+}
+
+bool InspectorHistory::undo(ErrorString* errorString)
+{
+ while (!m_history.isEmpty() && m_history.first()->isUndoableStateMark())
+ m_history.removeFirst();
+
+ while (!m_history.isEmpty()) {
+ OwnPtr<Action> first = m_history.takeFirst();
+ if (!first->undo(errorString)) {
+ m_history.clear();
+ return false;
+ }
+
+ if (first->isUndoableStateMark())
+ break;
+ }
+
+ return true;
+}
+
+void InspectorHistory::reset()
+{
+ m_history.clear();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(INSPECTOR)
--- /dev/null
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef InspectorHistory_h
+#define InspectorHistory_h
+
+#include "ExceptionCode.h"
+
+#include <wtf/Deque.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class ContainerNode;
+class Element;
+class Node;
+
+typedef String ErrorString;
+
+#if ENABLE(INSPECTOR)
+
+class InspectorHistory {
+ WTF_MAKE_NONCOPYABLE(InspectorHistory);
+public:
+ class Action {
+ public:
+ Action(const String& name);
+ virtual ~Action();
+ virtual String toString();
+
+ virtual bool isUndoableStateMark();
+
+ virtual String mergeId();
+ virtual void merge(PassOwnPtr<Action>);
+
+ virtual bool perform(ErrorString*) = 0;
+ virtual bool undo(ErrorString*) = 0;
+ private:
+ String m_name;
+ };
+
+ InspectorHistory();
+ virtual ~InspectorHistory();
+
+ bool perform(PassOwnPtr<Action>, ErrorString*);
+ void markUndoableState();
+
+ bool undo(ErrorString*);
+ void reset();
+
+private:
+ void dump();
+ Deque<OwnPtr<Action> > m_history;
+};
+
+#endif // ENABLE(INSPECTOR)
+
+} // namespace WebCore
+
+#endif // !defined(InspectorHistory_h)
//
// The propertyText (if not empty) is checked to be a valid style declaration (containing at least one property). If not,
// the method returns false (denoting an error).
-bool InspectorStyle::setPropertyText(ErrorString* errorString, unsigned index, const String& propertyText, bool overwrite)
+bool InspectorStyle::setPropertyText(ErrorString* errorString, unsigned index, const String& propertyText, bool overwrite, String* oldText)
{
ASSERT(m_parentStyleSheet);
DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
}
InspectorStyleTextEditor editor(&allProperties, &m_disabledProperties, text, newLineAndWhitespaceDelimiters());
- if (overwrite)
+ if (overwrite) {
+ *oldText = allProperties.at(index).rawText;
editor.replaceProperty(index, propertyText);
- else
+ } else
editor.insertProperty(index, propertyText, sourceData->styleSourceData->styleBodyRange.length());
return applyStyleText(editor.styleText());
return false;
String styleSheetText;
- bool success = m_parentStyleSheet->text(&styleSheetText);
+ bool success = m_parentStyleSheet->getText(&styleSheetText);
if (!success)
return false;
CSSStyleRule* InspectorStyleSheet::addRule(const String& selector)
{
String styleSheetText;
- bool success = text(&styleSheetText);
+ bool success = getText(&styleSheetText);
if (!success)
return 0;
result->setArray("rules", cssRules.release());
String styleSheetText;
- bool success = text(&styleSheetText);
+ bool success = getText(&styleSheetText);
if (success)
result->setString("text", styleSheetText);
// Style text cannot be retrieved without stylesheet, so set cssText here.
if (sourceData) {
String sheetText;
- bool success = text(&sheetText);
+ bool success = getText(&sheetText);
if (success) {
const SourceRange& bodyRange = sourceData->styleSourceData->styleBodyRange;
result->setString("cssText", sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
return result.release();
}
-bool InspectorStyleSheet::setPropertyText(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite)
+bool InspectorStyleSheet::setPropertyText(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, String* oldText)
{
RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
if (!inspectorStyle) {
return false;
}
- return inspectorStyle->setPropertyText(errorString, propertyIndex, text, overwrite);
+ return inspectorStyle->setPropertyText(errorString, propertyIndex, text, overwrite, oldText);
}
bool InspectorStyleSheet::toggleProperty(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, bool disable)
return success;
}
-bool InspectorStyleSheet::text(String* result) const
+bool InspectorStyleSheet::getText(String* result) const
{
if (!ensureText())
return false;
m_ruleSourceData.clear();
}
-bool InspectorStyleSheetForInlineStyle::text(String* result) const
+bool InspectorStyleSheetForInlineStyle::getText(String* result) const
{
if (!m_isStyleTextValid) {
m_styleText = elementStyleText();
PassRefPtr<InspectorObject> buildObjectForStyle() const;
PassRefPtr<InspectorArray> buildArrayForComputedStyle() const;
bool hasDisabledProperties() const { return !m_disabledProperties.isEmpty(); }
- bool setPropertyText(ErrorString*, unsigned index, const String& text, bool overwrite);
+ bool setPropertyText(ErrorString*, unsigned index, const String& text, bool overwrite, String* oldText);
bool toggleProperty(ErrorString*, unsigned index, bool disable);
private:
PassRefPtr<InspectorObject> buildObjectForStyleSheetInfo();
PassRefPtr<InspectorObject> buildObjectForRule(CSSStyleRule*);
PassRefPtr<InspectorObject> buildObjectForStyle(CSSStyleDeclaration*);
- bool setPropertyText(ErrorString*, const InspectorCSSId&, unsigned propertyIndex, const String& text, bool overwrite);
+ bool setPropertyText(ErrorString*, const InspectorCSSId&, unsigned propertyIndex, const String& text, bool overwrite, String* oldPropertyText);
bool toggleProperty(ErrorString*, const InspectorCSSId&, unsigned propertyIndex, bool disable);
- virtual bool text(String* result) const;
+ virtual bool getText(String* result) const;
virtual CSSStyleDeclaration* styleForId(const InspectorCSSId&) const;
protected:
static PassRefPtr<InspectorStyleSheetForInlineStyle> create(const String& id, PassRefPtr<Element> element, const String& origin);
void didModifyElementAttribute();
- virtual bool text(String* result) const;
+ virtual bool getText(String* result) const;
virtual CSSStyleDeclaration* styleForId(const InspectorCSSId& id) const { ASSERT_UNUSED(id, !id.ordinal()); return inlineStyle(); }
protected:
// An index past all the properties adds a new property to the style.
CSSAgent.setPropertyText(this.ownerStyle.id, this.index, propertyText, this.index < this.ownerStyle.pastLastSourcePropertyIndex(), callback.bind(this));
+ if (majorChange)
+ DOMAgent.markUndoableState();
},
setValue: function(newValue, majorChange, userCallback)
this.updateBreadcrumb(false);
+ this._updateSidebars();
+
+ if (selectedNode) {
+ ConsoleAgent.addInspectedNode(selectedNode.id);
+ this._lastValidSelectedNode = selectedNode;
+ }
+ },
+
+ _updateSidebars: function()
+ {
for (var pane in this.sidebarPanes)
this.sidebarPanes[pane].needsUpdate = true;
this.updateMetrics();
this.updateProperties();
this.updateEventListeners();
-
- if (selectedNode) {
- ConsoleAgent.addInspectedNode(selectedNode.id);
- this._lastValidSelectedNode = selectedNode;
- }
},
_reset: function()
{
// Cmd/Control + Shift + C should be a shortcut to clicking the Node Search Button.
// This shortcut matches Firebug.
- if (event.keyIdentifier === "U+0043") { // C key
+ if (event.keyIdentifier === "U+0043") { // C key
if (WebInspector.isMac())
var isNodeSearchKey = event.metaKey && !event.ctrlKey && !event.altKey && event.shiftKey;
else
event.handled = true;
return;
}
+ return;
}
+ if (WebInspector.KeyboardShortcut.eventHasCtrlOrMeta(event) && event.keyIdentifier === "U+005A") // Z key
+ DOMAgent.undo(this._updateSidebars.bind(this));
},
handleCopyEvent: function(event)
var inherited = this.isPropertyInherited(property.name);
var overloaded = this.isPropertyOverloaded(property.name, isShorthand);
- var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded);
+ var item = new WebInspector.StylePropertyTreeElement(this, this._parentPane, this.styleRule, style, property, isShorthand, inherited, overloaded);
this.propertiesTreeOutline.appendChild(item);
handledProperties[property.name] = property;
}
{
var style = this.styleRule.style;
var property = style.newBlankProperty();
- var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this.styleRule, style, property, false, false, false);
+ var item = new WebInspector.StylePropertyTreeElement(this, this._parentPane, this.styleRule, style, property, false, false, false);
this.propertiesTreeOutline.appendChild(item);
item.listItemElement.textContent = "";
item._newProperty = true;
for (var i = 0; i < uniqueProperties.length; ++i) {
var property = uniqueProperties[i];
var inherited = this._isPropertyInherited(property.name);
- var item = new WebInspector.StylePropertyTreeElement(null, this.styleRule, style, property, false, inherited, false);
+ var item = new WebInspector.StylePropertyTreeElement(this, null, this.styleRule, style, property, false, inherited, false);
this.propertiesTreeOutline.appendChild(item);
this._propertyTreeElements[property.name] = item;
}
* @extends {TreeElement}
* @param {?WebInspector.StylesSidebarPane} parentPane
*/
-WebInspector.StylePropertyTreeElement = function(parentPane, styleRule, style, property, shorthand, inherited, overloaded)
+WebInspector.StylePropertyTreeElement = function(section, parentPane, styleRule, style, property, shorthand, inherited, overloaded)
{
+ this.section = section;
this._parentPane = parentPane;
this._styleRule = styleRule;
this.style = style;
}
var liveProperty = this.style.getLiveProperty(name);
- var item = new WebInspector.StylePropertyTreeElement(this._parentPane, this._styleRule, this.style, liveProperty, false, inherited, overloaded);
+ var item = new WebInspector.StylePropertyTreeElement(this, this._parentPane, this._styleRule, this.style, liveProperty, false, inherited, overloaded);
this.appendChild(item);
}
},