Web Inspector: Separate creating a style sheet from adding a new rule in the protocol
authorcommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 28 Aug 2015 17:59:27 +0000 (17:59 +0000)
committercommit-queue@webkit.org <commit-queue@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 28 Aug 2015 17:59:27 +0000 (17:59 +0000)
https://bugs.webkit.org/show_bug.cgi?id=148502

Patch by Joseph Pecoraro <pecoraro@apple.com> on 2015-08-28
Reviewed by Timothy Hatcher.

Source/JavaScriptCore:

* inspector/protocol/CSS.json:
Add CSS.createStyleSheet. Modify CSS.addRule.

Source/WebCore:

Tests: inspector/css/createStyleSheet.html
       inspector/css/manager-preferredInspectorStyleSheetForFrame.html

* inspector/InspectorCSSAgent.h:
Allow for multiple inspector style sheets per document.

* inspector/InspectorCSSAgent.cpp:
(WebCore::InspectorCSSAgent::createStyleSheet):
(WebCore::InspectorCSSAgent::createInspectorStyleSheetForDocument): Added.
(WebCore::InspectorCSSAgent::viaInspectorStyleSheet): Deleted.
Extract and generalize creating a via-inspector stylesheet here.

(WebCore::InspectorCSSAgent::addRule):
Lookup stylesheet to add a rule to via the provided stylesheet id.

(WebCore::InspectorCSSAgent::bindStyleSheet):
(WebCore::InspectorCSSAgent::detectOrigin):
Update to account for a list of stylesheets per document instead of one.

* inspector/InspectorStyleSheet.cpp:
(WebCore::InspectorStyleSheet::addRule):
(WebCore::InspectorStyleSheetForInlineStyle::setStyleText):

Source/WebInspectorUI:

The backend allows creating multiple stylesheets. This patch makes
the frontend have a preferred InspectorStyleSheet per-frame.

* UserInterface/Base/Object.js:
(WebInspector.Object.singleFireEventListener):
Return the wrapped listener so it can be removed if needed.

* UserInterface/Models/CSSStyleSheet.js:
(WebInspector.CSSStyleSheet.prototype.isInspectorStyleSheet):
Helper to check if the current stylesheet is an inspector created stylesheet.

* UserInterface/Controllers/CSSStyleManager.js:
(WebInspector.CSSStyleManager.prototype.preferredInspectorStyleSheetForFrame):
(WebInspector.CSSStyleManager.prototype.preferredInspectorStyleSheetForFrame.documentNodeAvailable):
(WebInspector.CSSStyleManager.prototype.preferredInspectorStyleSheetForFrame.bodyNodeAvailable):
(WebInspector.CSSStyleManager.prototype.preferredInspectorStyleSheetForFrame.cssRuleAvailable):
(WebInspector.CSSStyleManager.prototype._inspectorStyleSheetsForFrame):
Lookup the inspector stylesheet for a particular frame. If one doesn't exist create it.
For legacy backends, exploit "addRule" to create the inspector stylesheet.

* UserInterface/Models/DOMNode.js:
* UserInterface/Models/DOMNodeStyles.js:
(WebInspector.DOMNodeStyles.prototype.addEmptyRule.inspectorStyleSheetAvailable):
(WebInspector.DOMNodeStyles.prototype.addEmptyRule):
Currently add all new rules to the inspector stylesheet.

LayoutTests:

* inspector/css/createStyleSheet-expected.txt: Added.
* inspector/css/createStyleSheet.html: Added.
* inspector/css/manager-preferredInspectorStyleSheetForFrame-expected.txt: Added.
* inspector/css/manager-preferredInspectorStyleSheetForFrame.html: Added.
* inspector/css/stylesheet-events-inspector-stylesheet-expected.txt:
* inspector/css/stylesheet-events-inspector-stylesheet.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@189104 268f45cc-cd09-0410-ab3c-d52691b4dbfc

19 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/css/createStyleSheet-expected.txt [new file with mode: 0644]
LayoutTests/inspector/css/createStyleSheet.html [new file with mode: 0644]
LayoutTests/inspector/css/manager-preferredInspectorStyleSheetForFrame-expected.txt [new file with mode: 0644]
LayoutTests/inspector/css/manager-preferredInspectorStyleSheetForFrame.html [new file with mode: 0644]
LayoutTests/inspector/css/stylesheet-events-inspector-stylesheet-expected.txt
LayoutTests/inspector/css/stylesheet-events-inspector-stylesheet.html
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/protocol/CSS.json
Source/WebCore/ChangeLog
Source/WebCore/inspector/InspectorCSSAgent.cpp
Source/WebCore/inspector/InspectorCSSAgent.h
Source/WebCore/inspector/InspectorStyleSheet.cpp
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/UserInterface/Base/Object.js
Source/WebInspectorUI/UserInterface/Controllers/CSSStyleManager.js
Source/WebInspectorUI/UserInterface/Models/CSSStyleSheet.js
Source/WebInspectorUI/UserInterface/Models/DOMNode.js
Source/WebInspectorUI/UserInterface/Models/DOMNodeStyles.js

index 9903e00..608c63e 100644 (file)
@@ -1,3 +1,17 @@
+2015-08-28  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Separate creating a style sheet from adding a new rule in the protocol
+        https://bugs.webkit.org/show_bug.cgi?id=148502
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/css/createStyleSheet-expected.txt: Added.
+        * inspector/css/createStyleSheet.html: Added.
+        * inspector/css/manager-preferredInspectorStyleSheetForFrame-expected.txt: Added.
+        * inspector/css/manager-preferredInspectorStyleSheetForFrame.html: Added.
+        * inspector/css/stylesheet-events-inspector-stylesheet-expected.txt:
+        * inspector/css/stylesheet-events-inspector-stylesheet.html:
+
 2015-08-28  Xabier Rodriguez Calvar  <calvaris@igalia.com>
 
         Layout Test streams/reference-implementation/readable-stream.html is flaky
diff --git a/LayoutTests/inspector/css/createStyleSheet-expected.txt b/LayoutTests/inspector/css/createStyleSheet-expected.txt
new file mode 100644 (file)
index 0000000..ca5f903
--- /dev/null
@@ -0,0 +1,27 @@
+Test CSS.createStyleSheet.
+
+
+== Running test suite: CSS.createStyleSheet
+-- Running test case: CheckNoStyleSheets
+PASS: Should be no stylesheets.
+
+-- Running test case: CreateInspectorStyleSheetCall1
+PASS: Should increase the list of stylesheets.
+PASS: Added StyleSheet origin should be 'inspector'.
+PASS: StyleSheet.isInspectorStyleSheet() should be true.
+PASS: Added StyleSheet frame should be the main frame.
+
+-- Running test case: CreateInspectorStyleSheetCall2
+PASS: Should increase the list of stylesheets.
+PASS: Added StyleSheet origin should be 'inspector'.
+PASS: StyleSheet.isInspectorStyleSheet() should be true.
+PASS: Added StyleSheet frame should be the main frame.
+PASS: Added StyleSheet should be different from the last added stylesheet.
+
+-- Running test case: CreateInspectorStyleSheetCall3
+PASS: Should increase the list of stylesheets.
+PASS: Added StyleSheet origin should be 'inspector'.
+PASS: StyleSheet.isInspectorStyleSheet() should be true.
+PASS: Added StyleSheet frame should be the main frame.
+PASS: Added StyleSheet should be different from the last added stylesheet.
+
diff --git a/LayoutTests/inspector/css/createStyleSheet.html b/LayoutTests/inspector/css/createStyleSheet.html
new file mode 100644 (file)
index 0000000..20ba709
--- /dev/null
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+function test()
+{
+    let addedStyleSheet;
+    let mainFrame = WebInspector.frameResourceManager.mainFrame;
+
+    let suite = InspectorTest.createAsyncSuite("CSS.createStyleSheet");
+
+    suite.addTestCase({
+        name: "CheckNoStyleSheets",
+        description: "Ensure there are no stylesheets.",
+        test: (resolve, reject) => {
+            InspectorTest.expectThat(WebInspector.cssStyleManager.styleSheets.length === 0, "Should be no stylesheets.");
+            resolve();
+        }
+    });
+
+    for (let i = 1; i <= 3; ++i) {
+        suite.addTestCase({
+            name: "CreateInspectorStyleSheetCall" + i,
+            description: "Should create a new inspector stylesheet.",
+            test: (resolve, reject) => {
+                CSSAgent.createStyleSheet(mainFrame.id);
+                WebInspector.cssStyleManager.singleFireEventListener(WebInspector.CSSStyleManager.Event.StyleSheetAdded, function(event) {
+                    InspectorTest.expectThat(WebInspector.cssStyleManager.styleSheets.length === i, "Should increase the list of stylesheets.");
+                    InspectorTest.expectThat(event.data.styleSheet.origin === WebInspector.CSSStyleSheet.Type.Inspector, "Added StyleSheet origin should be 'inspector'.");
+                    InspectorTest.expectThat(event.data.styleSheet.isInspectorStyleSheet(), "StyleSheet.isInspectorStyleSheet() should be true.");
+                    InspectorTest.expectThat(event.data.styleSheet.parentFrame === mainFrame, "Added StyleSheet frame should be the main frame.");
+                    if (addedStyleSheet)
+                        InspectorTest.expectThat(event.data.styleSheet !== addedStyleSheet, "Added StyleSheet should be different from the last added stylesheet.");
+                    addedStyleSheet = event.data.styleSheet;
+                    resolve();
+                });
+            }
+        });
+    }
+
+    WebInspector.cssStyleManager.singleFireEventListener(WebInspector.CSSStyleManager.Event.StyleSheetRemoved, function(event) {
+        InspectorTest.assert(false, "Should not be removing any StyleSheets in this test.");
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Test CSS.createStyleSheet.</p>
+</body>
+</html>
diff --git a/LayoutTests/inspector/css/manager-preferredInspectorStyleSheetForFrame-expected.txt b/LayoutTests/inspector/css/manager-preferredInspectorStyleSheetForFrame-expected.txt
new file mode 100644 (file)
index 0000000..1e000d2
--- /dev/null
@@ -0,0 +1,34 @@
+Test CSSManager.preferredInspectorStyleSheetForFrame.
+
+
+
+== Running test suite: CSSManager.preferredInspectorStyleSheetForFrame
+-- Running test case: CheckNoStyleSheets
+PASS: Should be no stylesheets.
+
+-- Running test case: CreateMainFrameInspectorStyleSheet
+PASS: Added StyleSheet origin should be 'inspector'.
+PASS: StyleSheet.isInspectorStyleSheet() should be true.
+PASS: Added StyleSheet frame should be the main frame.
+PASS: Should be one stylesheet.
+PASS: StyleSheet origin is inspector.
+PASS: StyleSheet.isInspectorStyleSheet() should be true.
+
+-- Running test case: AnotherMainFrameRequestDoesNothing
+PASS: Should still be one stylesheet.
+PASS: StyleSheet origin should be 'inspector'.
+PASS: StyleSheet.isInspectorStyleSheet() should be true.
+
+-- Running test case: CreateSubFrameInspectorStyleSheet
+PASS: Added StyleSheet origin should be 'inspector'.
+PASS: StyleSheet.isInspectorStyleSheet() should be true.
+PASS: Added StyleSheet frame should be a child frame.
+PASS: Should be two stylesheets.
+PASS: StyleSheet origin should be 'inspector'.
+PASS: StyleSheet.isInspectorStyleSheet() should be true.
+
+-- Running test case: AnotherSubFrameRequestDoesNothing
+PASS: Should be two stylesheets.
+PASS: StyleSheet origin should be 'inspector'.
+PASS: StyleSheet.isInspectorStyleSheet() should be true.
+
diff --git a/LayoutTests/inspector/css/manager-preferredInspectorStyleSheetForFrame.html b/LayoutTests/inspector/css/manager-preferredInspectorStyleSheetForFrame.html
new file mode 100644 (file)
index 0000000..4a09e8f
--- /dev/null
@@ -0,0 +1,104 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script>
+function test()
+{
+    let mainFrame = WebInspector.frameResourceManager.mainFrame;
+    let childFrame = mainFrame.childFrames[0];
+
+    let suite = InspectorTest.createAsyncSuite("CSSManager.preferredInspectorStyleSheetForFrame");
+
+    suite.addTestCase({
+        name: "CheckNoStyleSheets",
+        description: "Ensure there are no stylesheets.",
+        test: (resolve, reject) => {
+            InspectorTest.expectThat(WebInspector.cssStyleManager.styleSheets.length === 0, "Should be no stylesheets.");
+            resolve();
+        }
+    });
+
+    suite.addTestCase({
+        name: "CreateMainFrameInspectorStyleSheet",
+        description: "First request for main frame should create inspector stylesheet.",
+        test: (resolve, reject) => {
+            WebInspector.cssStyleManager.singleFireEventListener(WebInspector.CSSStyleManager.Event.StyleSheetAdded, function(event) {
+                InspectorTest.expectThat(event.data.styleSheet.origin === WebInspector.CSSStyleSheet.Type.Inspector, "Added StyleSheet origin should be 'inspector'.");
+                InspectorTest.expectThat(event.data.styleSheet.isInspectorStyleSheet(), "StyleSheet.isInspectorStyleSheet() should be true.");
+                InspectorTest.expectThat(event.data.styleSheet.parentFrame === mainFrame, "Added StyleSheet frame should be the main frame.");
+            });
+            WebInspector.cssStyleManager.preferredInspectorStyleSheetForFrame(mainFrame, function(styleSheet) {
+                InspectorTest.expectThat(WebInspector.cssStyleManager.styleSheets.length === 1, "Should be one stylesheet.");
+                InspectorTest.expectThat(styleSheet.origin === WebInspector.CSSStyleSheet.Type.Inspector, "StyleSheet origin is inspector.");
+                InspectorTest.expectThat(styleSheet.isInspectorStyleSheet(), "StyleSheet.isInspectorStyleSheet() should be true.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "AnotherMainFrameRequestDoesNothing",
+        description: "Second request for main frame should not create a new stylesheet.",
+        test: (resolve, reject) => {
+            let listener = WebInspector.cssStyleManager.singleFireEventListener(WebInspector.CSSStyleManager.Event.StyleSheetAdded, function(event) {
+                InspectorTest.assert(false, "Should not create a new StyleSheet, should reuse the existing one");
+            });
+            WebInspector.cssStyleManager.preferredInspectorStyleSheetForFrame(mainFrame, function(styleSheet) {
+                InspectorTest.expectThat(WebInspector.cssStyleManager.styleSheets.length === 1, "Should still be one stylesheet.");
+                InspectorTest.expectThat(styleSheet.origin === WebInspector.CSSStyleSheet.Type.Inspector, "StyleSheet origin should be 'inspector'.");
+                InspectorTest.expectThat(styleSheet.isInspectorStyleSheet(), "StyleSheet.isInspectorStyleSheet() should be true.");
+                WebInspector.cssStyleManager.removeEventListener(WebInspector.CSSStyleManager.Event.StyleSheetAdded, listener, null);
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "CreateSubFrameInspectorStyleSheet",
+        description: "First request for subframe should create inspector stylesheet.",
+        test: (resolve, reject) => {
+            WebInspector.cssStyleManager.singleFireEventListener(WebInspector.CSSStyleManager.Event.StyleSheetAdded, function(event) {
+                InspectorTest.expectThat(event.data.styleSheet.origin === WebInspector.CSSStyleSheet.Type.Inspector, "Added StyleSheet origin should be 'inspector'.");
+                InspectorTest.expectThat(event.data.styleSheet.isInspectorStyleSheet(), "StyleSheet.isInspectorStyleSheet() should be true.");
+                InspectorTest.expectThat(event.data.styleSheet.parentFrame === childFrame, "Added StyleSheet frame should be a child frame.");
+            });
+            WebInspector.cssStyleManager.preferredInspectorStyleSheetForFrame(childFrame, function(styleSheet) {
+                InspectorTest.expectThat(WebInspector.cssStyleManager.styleSheets.length === 2, "Should be two stylesheets.");
+                InspectorTest.expectThat(styleSheet.origin === WebInspector.CSSStyleSheet.Type.Inspector, "StyleSheet origin should be 'inspector'.");
+                InspectorTest.expectThat(styleSheet.isInspectorStyleSheet(), "StyleSheet.isInspectorStyleSheet() should be true.");
+                resolve();
+            });
+        }
+    });
+
+    suite.addTestCase({
+        name: "AnotherSubFrameRequestDoesNothing",
+        description: "Second request for main frame should not create a new stylesheet.",
+        test: (resolve, reject) => {
+            let listener = WebInspector.cssStyleManager.singleFireEventListener(WebInspector.CSSStyleManager.Event.StyleSheetAdded, function(event) {
+                InspectorTest.assert(false, "Should not create a new StyleSheet, should reuse the existing one");
+            });
+            WebInspector.cssStyleManager.preferredInspectorStyleSheetForFrame(childFrame, function(styleSheet) {
+                InspectorTest.expectThat(WebInspector.cssStyleManager.styleSheets.length === 2, "Should be two stylesheets.");
+                InspectorTest.expectThat(styleSheet.origin === WebInspector.CSSStyleSheet.Type.Inspector, "StyleSheet origin should be 'inspector'.");
+                InspectorTest.expectThat(styleSheet.isInspectorStyleSheet(), "StyleSheet.isInspectorStyleSheet() should be true.");
+                WebInspector.cssStyleManager.removeEventListener(WebInspector.CSSStyleManager.Event.StyleSheetAdded, listener, null);
+                resolve();
+            });
+        }
+    });
+
+    WebInspector.cssStyleManager.singleFireEventListener(WebInspector.CSSStyleManager.Event.StyleSheetRemoved, function(event) {
+        InspectorTest.assert(false, "Should not be removing any StyleSheets in this test.");
+    });
+
+    suite.runTestCasesAndFinish();
+}
+</script>
+</head>
+<body onload="runTest()">
+    <p>Test CSSManager.preferredInspectorStyleSheetForFrame.</p>
+    <iframe src="about:blank"></iframe>
+</body>
+</html>
index cd49920..27d0fdd 100644 (file)
@@ -7,5 +7,5 @@ PASS: Should be no stylesheets.
 
 -- Running test case: CreateInspectorStyleSheet
 PASS: Should be one stylesheet.
-PASS: StyleSheet origin is inspector.
+PASS: StyleSheet origin should be 'inspector'.
 
index 92be553..fe66477 100644 (file)
@@ -7,8 +7,6 @@ function test()
 {
     let suite = InspectorTest.createAsyncSuite("CSS.StyleSheetEvents.InspectorStyleSheet");
 
-    let bodyNodeId;
-
     suite.addTestCase({
         name: "CheckNoStyleSheets",
         description: "Ensure there are currently no stylesheets.",
@@ -22,24 +20,17 @@ function test()
         name: "CreateInspectorStyleSheet",
         description: "Creating an inspector stylesheet adds a stylesheet.",
         test: (resolve, reject) => {
+            CSSAgent.createStyleSheet(WebInspector.frameResourceManager.mainFrame.id);
             WebInspector.cssStyleManager.singleFireEventListener(WebInspector.CSSStyleManager.Event.StyleSheetAdded, function(event) {
                 InspectorTest.expectThat(WebInspector.cssStyleManager.styleSheets.length === 1, "Should be one stylesheet.");
                 InspectorTest.assert(event.data.styleSheet instanceof WebInspector.CSSStyleSheet, "Event data should be a CSSStyleSheet");
-                InspectorTest.expectThat(event.data.styleSheet.origin === WebInspector.CSSStyleSheet.Type.Inspector, "StyleSheet origin is inspector.");
+                InspectorTest.expectThat(event.data.styleSheet.origin === WebInspector.CSSStyleSheet.Type.Inspector, "StyleSheet origin should be 'inspector'.");
                 resolve();
             });
-
-            // FIXME: Currently the only way to create an inspector stylesheet is through `CSS.addRule`.
-            CSSAgent.addRule(bodyNodeId, "body");
         }
     });
 
-    WebInspector.domTreeManager.requestDocument(function(documentNode) {
-        WebInspector.domTreeManager.querySelector(documentNode.id, "body", function(contentNodeId) {
-            bodyNodeId = contentNodeId;
-            suite.runTestCasesAndFinish();
-        });
-    });
+    suite.runTestCasesAndFinish();
 }
 </script>
 </head>
index 67f7980..ea87f51 100644 (file)
@@ -1,3 +1,13 @@
+2015-08-28  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Separate creating a style sheet from adding a new rule in the protocol
+        https://bugs.webkit.org/show_bug.cgi?id=148502
+
+        Reviewed by Timothy Hatcher.
+
+        * inspector/protocol/CSS.json:
+        Add CSS.createStyleSheet. Modify CSS.addRule.
+
 2015-08-28  Mark Lam  <mark.lam@apple.com>
 
         ScratchRegisterAllocator::preserveReusedRegistersByPushing() should allow room for C helper calls and keep sp properly aligned.
index 5ade06a..7abbe22 100644 (file)
             "description": "Modifies the rule selector."
         },
         {
+            "name": "createStyleSheet",
+            "parameters": [
+                { "name": "frameId", "$ref": "Network.FrameId", "description": "Identifier of the frame where the new \"inspector\" stylesheet should be created." }
+            ],
+            "returns": [
+                { "name": "styleSheetId", "$ref": "StyleSheetId", "description": "Identifier of the created \"inspector\" stylesheet." }
+            ],
+            "description": "Creates a new special \"inspector\" stylesheet in the frame with given <code>frameId</code>."
+        },
+        {
             "name": "addRule",
             "parameters": [
-                { "name": "contextNodeId", "$ref": "DOM.NodeId" },
+                { "name": "styleSheetId", "$ref": "StyleSheetId" },
                 { "name": "selector", "type": "string" }
             ],
             "returns": [
                 { "name": "rule", "$ref": "CSSRule", "description": "The newly created rule." }
             ],
-            "description": "Creates a new empty rule with the given <code>selector</code> in a special \"inspector\" stylesheet in the owner document of the context node."
+            "description": "Creates a new empty rule with the given <code>selector</code> in a stylesheet with given <code>styleSheetId</code>."
         },
         {
             "name": "getSupportedCSSProperties",
index 2351811..1f633fd 100644 (file)
@@ -1,3 +1,33 @@
+2015-08-28  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Separate creating a style sheet from adding a new rule in the protocol
+        https://bugs.webkit.org/show_bug.cgi?id=148502
+
+        Reviewed by Timothy Hatcher.
+
+        Tests: inspector/css/createStyleSheet.html
+               inspector/css/manager-preferredInspectorStyleSheetForFrame.html
+
+        * inspector/InspectorCSSAgent.h:
+        Allow for multiple inspector style sheets per document.
+
+        * inspector/InspectorCSSAgent.cpp:
+        (WebCore::InspectorCSSAgent::createStyleSheet):
+        (WebCore::InspectorCSSAgent::createInspectorStyleSheetForDocument): Added.
+        (WebCore::InspectorCSSAgent::viaInspectorStyleSheet): Deleted.
+        Extract and generalize creating a via-inspector stylesheet here.
+
+        (WebCore::InspectorCSSAgent::addRule):
+        Lookup stylesheet to add a rule to via the provided stylesheet id.
+
+        (WebCore::InspectorCSSAgent::bindStyleSheet):
+        (WebCore::InspectorCSSAgent::detectOrigin):
+        Update to account for a list of stylesheets per document instead of one.
+
+        * inspector/InspectorStyleSheet.cpp:
+        (WebCore::InspectorStyleSheet::addRule):
+        (WebCore::InspectorStyleSheetForInlineStyle::setStyleText):
+
 2015-08-28  Timothy Horton  <timothy_horton@apple.com>
 
         [Mac] Right-clicking on GIFs spins the UI process for a while
index 67d1880..d9b5089 100644 (file)
@@ -41,6 +41,7 @@
 #include "HTMLStyleElement.h"
 #include "InspectorDOMAgent.h"
 #include "InspectorHistory.h"
+#include "InspectorPageAgent.h"
 #include "InstrumentingAgents.h"
 #include "NamedFlowCollection.h"
 #include "Node.h"
@@ -747,13 +748,78 @@ void InspectorCSSAgent::setRuleSelector(ErrorString& errorString, const Inspecto
     errorString = InspectorDOMAgent::toErrorString(ec);
 }
 
-void InspectorCSSAgent::addRule(ErrorString& errorString, const int contextNodeId, const String& selector, RefPtr<Inspector::Protocol::CSS::CSSRule>& result)
+void InspectorCSSAgent::createStyleSheet(ErrorString& errorString, const String& frameId, String* styleSheetId)
 {
-    Node* node = m_domAgent->assertNode(errorString, contextNodeId);
-    if (!node)
+    Frame* frame = m_domAgent->pageAgent()->frameForId(frameId);
+    if (!frame) {
+        errorString = ASCIILiteral("No frame for given id found");
+        return;
+    }
+
+    Document* document = frame->document();
+    if (!document) {
+        errorString = ASCIILiteral("No document for frame");
         return;
+    }
+
+    InspectorStyleSheet* inspectorStyleSheet = createInspectorStyleSheetForDocument(*document);
+    if (!inspectorStyleSheet) {
+        errorString = ASCIILiteral("Could not create stylesheet for the frame.");
+        return;
+    }
+
+    *styleSheetId = inspectorStyleSheet->id();
+}
+
+InspectorStyleSheet* InspectorCSSAgent::createInspectorStyleSheetForDocument(Document& document)
+{
+    if (!document.isHTMLDocument() && !document.isSVGDocument())
+        return nullptr;
+
+    ExceptionCode ec = 0;
+    RefPtr<Element> styleElement = document.createElement("style", ec);
+    if (ec)
+        return nullptr;
+
+    styleElement->setAttribute("type", "text/css", ec);
+    if (ec)
+        return nullptr;
+
+    ContainerNode* targetNode;
+    // HEAD is absent in ImageDocuments, for example.
+    if (auto* head = document.head())
+        targetNode = head;
+    else if (auto* body = document.bodyOrFrameset())
+        targetNode = body;
+    else
+        return nullptr;
+
+    // Inserting this <style> into the document will trigger activeStyleSheetsUpdated
+    // and we will create an InspectorStyleSheet for this <style>'s CSSStyleSheet.
+    // Set this flag, so when we create it, we put it into the via inspector map.
+    m_creatingViaInspectorStyleSheet = true;
+    InlineStyleOverrideScope overrideScope(document);
+    targetNode->appendChild(styleElement, ec);
+    m_creatingViaInspectorStyleSheet = false;
+    if (ec)
+        return nullptr;
+
+    auto iterator = m_documentToInspectorStyleSheet.find(&document);
+    ASSERT(iterator != m_documentToInspectorStyleSheet.end());
+    if (iterator == m_documentToInspectorStyleSheet.end())
+        return nullptr;
 
-    InspectorStyleSheet* inspectorStyleSheet = viaInspectorStyleSheet(&node->document(), true);
+    auto& inspectorStyleSheetsForDocument = iterator->value;
+    ASSERT(!inspectorStyleSheetsForDocument.isEmpty());
+    if (inspectorStyleSheetsForDocument.isEmpty())
+        return nullptr;
+
+    return inspectorStyleSheetsForDocument.last().get();
+}
+
+void InspectorCSSAgent::addRule(ErrorString& errorString, const String& styleSheetId, const String& selector, RefPtr<Inspector::Protocol::CSS::CSSRule>& result)
+{
+    InspectorStyleSheet* inspectorStyleSheet = assertStyleSheetForId(errorString, styleSheetId);
     if (!inspectorStyleSheet) {
         errorString = ASCIILiteral("No target stylesheet found");
         return;
@@ -905,59 +971,19 @@ InspectorStyleSheet* InspectorCSSAgent::bindStyleSheet(CSSStyleSheet* styleSheet
         inspectorStyleSheet = InspectorStyleSheet::create(m_domAgent->pageAgent(), id, styleSheet, detectOrigin(styleSheet, document), InspectorDOMAgent::documentURLString(document), this);
         m_idToInspectorStyleSheet.set(id, inspectorStyleSheet);
         m_cssStyleSheetToInspectorStyleSheet.set(styleSheet, inspectorStyleSheet);
-        if (m_creatingViaInspectorStyleSheet)
-            m_documentToInspectorStyleSheet.add(document, inspectorStyleSheet);
+        if (m_creatingViaInspectorStyleSheet) {
+            auto& inspectorStyleSheetsForDocument = m_documentToInspectorStyleSheet.add(document, Vector<RefPtr<InspectorStyleSheet>>()).iterator->value;
+            inspectorStyleSheetsForDocument.append(inspectorStyleSheet);
+        }
     }
     return inspectorStyleSheet.get();
 }
 
-InspectorStyleSheet* InspectorCSSAgent::viaInspectorStyleSheet(Document* document, bool createIfAbsent)
-{
-    if (!document) {
-        ASSERT(!createIfAbsent);
-        return nullptr;
-    }
-
-    if (!document->isHTMLDocument() && !document->isSVGDocument())
-        return nullptr;
-
-    RefPtr<InspectorStyleSheet> inspectorStyleSheet = m_documentToInspectorStyleSheet.get(document);
-    if (inspectorStyleSheet || !createIfAbsent)
-        return inspectorStyleSheet.get();
-
-    ExceptionCode ec = 0;
-    RefPtr<Element> styleElement = document->createElement("style", ec);
-    if (!ec)
-        styleElement->setAttribute("type", "text/css", ec);
-    if (!ec) {
-        ContainerNode* targetNode;
-        // HEAD is absent in ImageDocuments, for example.
-        if (auto* head = document->head())
-            targetNode = head;
-        else if (auto* body = document->bodyOrFrameset())
-            targetNode = body;
-        else
-            return nullptr;
-
-        // Inserting this <style> into the document will trigger activeStyleSheetsUpdated
-        // and we will create an InspectorStyleSheet for this <style>'s CSSStyleSheet.
-        // Set this flag, so when we create it, we put it into the via inspector hash map.
-        m_creatingViaInspectorStyleSheet = true;
-        InlineStyleOverrideScope overrideScope(document);
-        targetNode->appendChild(styleElement, ec);
-        m_creatingViaInspectorStyleSheet = false;
-    }
-    if (ec)
-        return nullptr;
-
-    return m_documentToInspectorStyleSheet.get(document);
-}
-
 InspectorStyleSheet* InspectorCSSAgent::assertStyleSheetForId(ErrorString& errorString, const String& styleSheetId)
 {
     IdToInspectorStyleSheet::iterator it = m_idToInspectorStyleSheet.find(styleSheetId);
     if (it == m_idToInspectorStyleSheet.end()) {
-        errorString = ASCIILiteral("No style sheet with given id found");
+        errorString = ASCIILiteral("No stylesheet with given id found");
         return nullptr;
     }
     return it->value.get();
@@ -970,13 +996,17 @@ Inspector::Protocol::CSS::StyleSheetOrigin InspectorCSSAgent::detectOrigin(CSSSt
 
     if (pageStyleSheet && !pageStyleSheet->ownerNode() && pageStyleSheet->href().isEmpty())
         return Inspector::Protocol::CSS::StyleSheetOrigin::UserAgent;
-    
+
     if (pageStyleSheet && pageStyleSheet->ownerNode() && pageStyleSheet->ownerNode()->nodeName() == "#document")
         return Inspector::Protocol::CSS::StyleSheetOrigin::User;
 
-    InspectorStyleSheet* viaInspectorStyleSheetForOwner = viaInspectorStyleSheet(ownerDocument, false);
-    if (viaInspectorStyleSheetForOwner && pageStyleSheet == viaInspectorStyleSheetForOwner->pageStyleSheet())
-        return Inspector::Protocol::CSS::StyleSheetOrigin::Inspector;
+    auto iterator = m_documentToInspectorStyleSheet.find(ownerDocument);
+    if (iterator != m_documentToInspectorStyleSheet.end()) {
+        for (auto& inspectorStyleSheet : iterator->value) {
+            if (pageStyleSheet == inspectorStyleSheet->pageStyleSheet())
+                return Inspector::Protocol::CSS::StyleSheetOrigin::Inspector;
+        }
+    }
 
     return Inspector::Protocol::CSS::StyleSheetOrigin::Regular;
 }
index 8da74a9..3e9b41e 100644 (file)
@@ -45,21 +45,16 @@ class CSSFrontendDispatcher;
 namespace WebCore {
 
 class CSSRule;
-class CSSRuleList;
-class CSSStyleDeclaration;
 class CSSStyleRule;
 class CSSStyleSheet;
+class ChangeRegionOversetTask;
 class Document;
-class DocumentStyleSheetCollection;
 class Element;
-class InspectorCSSOMWrappers;
 class InstrumentingAgents;
-class NameNodeMap;
 class Node;
 class NodeList;
 class StyleResolver;
 class StyleRule;
-class ChangeRegionOversetTask;
 
 class InspectorCSSAgent final
     : public InspectorAgentBase
@@ -71,8 +66,8 @@ class InspectorCSSAgent final
 public:
     class InlineStyleOverrideScope {
     public:
-        InlineStyleOverrideScope(SecurityContext* context)
-            : m_contentSecurityPolicy(context->contentSecurityPolicy())
+        InlineStyleOverrideScope(SecurityContext& context)
+            : m_contentSecurityPolicy(context.contentSecurityPolicy())
         {
             m_contentSecurityPolicy->setOverrideAllowInlineStyle(true);
         }
@@ -119,7 +114,8 @@ public:
     virtual void setStyleSheetText(ErrorString&, const String& styleSheetId, const String& text) override;
     virtual void setStyleText(ErrorString&, const Inspector::InspectorObject& styleId, const String& text, RefPtr<Inspector::Protocol::CSS::CSSStyle>& result) override;
     virtual void setRuleSelector(ErrorString&, const Inspector::InspectorObject& ruleId, const String& selector, RefPtr<Inspector::Protocol::CSS::CSSRule>& result) override;
-    virtual void addRule(ErrorString&, int contextNodeId, const String& selector, RefPtr<Inspector::Protocol::CSS::CSSRule>& result) override;
+    virtual void createStyleSheet(ErrorString&, const String& frameId, String* styleSheetId) override;
+    virtual void addRule(ErrorString&, const String& styleSheetId, const String& selector, RefPtr<Inspector::Protocol::CSS::CSSRule>& result) override;
     virtual void getSupportedCSSProperties(ErrorString&, RefPtr<Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSPropertyInfo>>& result) override;
     virtual void getSupportedSystemFontFamilyNames(ErrorString&, RefPtr<Inspector::Protocol::Array<String>>& result) override;
     virtual void forcePseudoState(ErrorString&, int nodeId, const Inspector::InspectorArray& forcedPseudoClasses) override;
@@ -135,7 +131,7 @@ private:
     typedef HashMap<String, RefPtr<InspectorStyleSheet>> IdToInspectorStyleSheet;
     typedef HashMap<CSSStyleSheet*, RefPtr<InspectorStyleSheet>> CSSStyleSheetToInspectorStyleSheet;
     typedef HashMap<Node*, RefPtr<InspectorStyleSheetForInlineStyle>> NodeToInspectorStyleSheet; // bogus "stylesheets" with elements' inline styles
-    typedef HashMap<RefPtr<Document>, RefPtr<InspectorStyleSheet>> DocumentToViaInspectorStyleSheet; // "via inspector" stylesheets
+    typedef HashMap<RefPtr<Document>, Vector<RefPtr<InspectorStyleSheet>>> DocumentToViaInspectorStyleSheet; // "via inspector" stylesheets
     typedef HashMap<int, unsigned> NodeIdToForcedPseudoState;
 
     void resetNonPersistentData();
@@ -150,8 +146,8 @@ private:
 
     String unbindStyleSheet(InspectorStyleSheet*);
     InspectorStyleSheet* bindStyleSheet(CSSStyleSheet*);
-    InspectorStyleSheet* viaInspectorStyleSheet(Document*, bool createIfAbsent);
     InspectorStyleSheet* assertStyleSheetForId(ErrorString&, const String&);
+    InspectorStyleSheet* createInspectorStyleSheetForDocument(Document&);
     Inspector::Protocol::CSS::StyleSheetOrigin detectOrigin(CSSStyleSheet* pageStyleSheet, Document* ownerDocument);
 
     RefPtr<Inspector::Protocol::CSS::CSSRule> buildObjectForRule(StyleRule*, StyleResolver&, Element*);
index b56e871..9761edf 100644 (file)
@@ -726,7 +726,7 @@ CSSStyleRule* InspectorStyleSheet::addRule(const String& selector, ExceptionCode
 
     styleSheetText.append(selector);
     styleSheetText.appendLiteral(" {}");
-    // Using setText() as this operation changes the style sheet rule set.
+    // Using setText() as this operation changes the stylesheet rule set.
     setText(styleSheetText.toString(), ASSERT_NO_EXCEPTION);
 
     fireStyleSheetChanged();
@@ -1295,7 +1295,7 @@ bool InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style,
     ASSERT_UNUSED(style, style == inlineStyle());
 
     {
-        InspectorCSSAgent::InlineStyleOverrideScope overrideScope(&m_element->document());
+        InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->document());
         m_element->setAttribute("style", text, ec);
     }
 
index 27ec878..b6c52ca 100644 (file)
@@ -1,5 +1,38 @@
 2015-08-28  Joseph Pecoraro  <pecoraro@apple.com>
 
+        Web Inspector: Separate creating a style sheet from adding a new rule in the protocol
+        https://bugs.webkit.org/show_bug.cgi?id=148502
+
+        Reviewed by Timothy Hatcher.
+
+        The backend allows creating multiple stylesheets. This patch makes
+        the frontend have a preferred InspectorStyleSheet per-frame.
+
+        * UserInterface/Base/Object.js:
+        (WebInspector.Object.singleFireEventListener):
+        Return the wrapped listener so it can be removed if needed.
+
+        * UserInterface/Models/CSSStyleSheet.js:
+        (WebInspector.CSSStyleSheet.prototype.isInspectorStyleSheet):
+        Helper to check if the current stylesheet is an inspector created stylesheet.
+
+        * UserInterface/Controllers/CSSStyleManager.js:
+        (WebInspector.CSSStyleManager.prototype.preferredInspectorStyleSheetForFrame):
+        (WebInspector.CSSStyleManager.prototype.preferredInspectorStyleSheetForFrame.documentNodeAvailable):
+        (WebInspector.CSSStyleManager.prototype.preferredInspectorStyleSheetForFrame.bodyNodeAvailable):
+        (WebInspector.CSSStyleManager.prototype.preferredInspectorStyleSheetForFrame.cssRuleAvailable):
+        (WebInspector.CSSStyleManager.prototype._inspectorStyleSheetsForFrame):
+        Lookup the inspector stylesheet for a particular frame. If one doesn't exist create it.
+        For legacy backends, exploit "addRule" to create the inspector stylesheet.
+
+        * UserInterface/Models/DOMNode.js:
+        * UserInterface/Models/DOMNodeStyles.js:
+        (WebInspector.DOMNodeStyles.prototype.addEmptyRule.inspectorStyleSheetAvailable):
+        (WebInspector.DOMNodeStyles.prototype.addEmptyRule):
+        Currently add all new rules to the inspector stylesheet.
+
+2015-08-28  Joseph Pecoraro  <pecoraro@apple.com>
+
         Web Inspector: Type Profiler does not understand Functions within Default Argument Expressions
         https://bugs.webkit.org/show_bug.cgi?id=148557
 
index 2134036..06d0229 100644 (file)
@@ -63,6 +63,7 @@ WebInspector.Object = class Object
         }.bind(this);
 
         this.addEventListener(eventType, wrappedCallback, null);
+        return wrappedCallback;
     }
 
     static removeEventListener(eventType, listener, thisObject)
index f9901d6..b32b933 100644 (file)
@@ -167,6 +167,92 @@ WebInspector.CSSStyleManager = class CSSStyleManager extends WebInspector.Object
         return styles;
     }
 
+    preferredInspectorStyleSheetForFrame(frame, callback)
+    {
+        var inspectorStyleSheets = this._inspectorStyleSheetsForFrame(frame);
+        for (let styleSheet of inspectorStyleSheets) {
+            if (styleSheet[WebInspector.CSSStyleManager.PreferredInspectorStyleSheetSymbol]) {
+                callback(styleSheet);
+                return;
+            }
+        }
+
+        if (CSSAgent.createStyleSheet) {
+            CSSAgent.createStyleSheet(frame.id, function(error, styleSheetId) {
+                let styleSheet = WebInspector.cssStyleManager.styleSheetForIdentifier(styleSheetId);
+                styleSheet[WebInspector.CSSStyleManager.PreferredInspectorStyleSheetSymbol] = true;
+                callback(styleSheet);
+            });
+            return;
+        }
+
+        // COMPATIBILITY (iOS 9): CSS.createStyleSheet did not exist.
+        // Legacy backends can only create the Inspector StyleSheet through CSS.addRule.
+        // Exploit that to create the Inspector StyleSheet for the document.body node in
+        // this frame, then get the StyleSheet for the new rule.
+
+        let expression = appendWebInspectorSourceURL("document");
+        let contextId = frame.pageExecutionContext.id;
+        RuntimeAgent.evaluate.invoke({expression, objectGroup: "", includeCommandLineAPI: false, doNotPauseOnExceptionsAndMuteConsole: true, contextId, returnByValue: false, generatePreview: false}, documentAvailable);
+
+        function documentAvailable(error, documentRemoteObjectPayload)
+        {
+            if (error) {
+                callback(null);
+                return;
+            }
+
+            let remoteObject = WebInspector.RemoteObject.fromPayload(documentRemoteObjectPayload);
+            remoteObject.pushNodeToFrontend(documentNodeAvailable.bind(null, remoteObject));
+        }
+
+        function documentNodeAvailable(remoteObject, documentNodeId)
+        {
+            remoteObject.release();
+
+            if (!documentNodeId) {
+                callback(null);
+                return;
+            }
+
+            DOMAgent.querySelector(documentNodeId, "body", bodyNodeAvailable);
+        }
+
+        function bodyNodeAvailable(error, bodyNodeId)
+        {
+            if (error) {
+                console.error(error);
+                callback(null);
+                return;
+            }
+
+            let selector = ""; // Intentionally empty.
+            CSSAgent.addRule(bodyNodeId, selector, cssRuleAvailable);
+        }
+
+        function cssRuleAvailable(error, payload)
+        {
+            if (error || !payload.ruleId) {
+                callback(null);
+                return;
+            }
+
+            let styleSheetId = payload.ruleId.styleSheetId;
+            let styleSheet = WebInspector.cssStyleManager.styleSheetForIdentifier(styleSheetId);
+            if (!styleSheet) {
+                callback(null);
+                return;
+            }
+
+            styleSheet[WebInspector.CSSStyleManager.PreferredInspectorStyleSheetSymbol] = true;
+
+            console.assert(styleSheet.isInspectorStyleSheet());
+            console.assert(styleSheet.parentFrame === frame);
+
+            callback(styleSheet);
+        }
+    }
+
     // Protected
 
     mediaQueryResultChanged()
@@ -216,6 +302,18 @@ WebInspector.CSSStyleManager = class CSSStyleManager extends WebInspector.Object
 
     // Private
 
+    _inspectorStyleSheetsForFrame(frame)
+    {
+        let styleSheets = [];
+
+        for (let styleSheet of this.styleSheets) {
+            if (styleSheet.isInspectorStyleSheet() && styleSheet.parentFrame === frame)
+                styleSheets.push(styleSheet);
+        }
+
+        return styleSheets;
+    }
+
     _nodePseudoClassesDidChange(event)
     {
         var node = event.target;
@@ -443,3 +541,4 @@ WebInspector.CSSStyleManager.Event = {
 };
 
 WebInspector.CSSStyleManager.ForceablePseudoClasses = ["active", "focus", "hover", "visited"];
+WebInspector.CSSStyleManager.PreferredInspectorStyleSheetSymbol = Symbol("css-style-manager-preferred-inspector-stylesheet");
index b294dcf..4a64fe4 100644 (file)
@@ -112,6 +112,11 @@ WebInspector.CSSStyleSheet = class CSSStyleSheet extends WebInspector.SourceCode
         return this._hasInfo;
     }
 
+    isInspectorStyleSheet()
+    {
+        return this._origin === WebInspector.CSSStyleSheet.Type.Inspector;
+    }
+
     isInlineStyleTag()
     {
         return this._inlineStyleTag;
index d06c355..c26d1be 100644 (file)
@@ -128,6 +128,18 @@ WebInspector.DOMNode = class DOMNode extends WebInspector.Object
 
     // Public
 
+    get frameIdentifier()
+    {
+        return this._frameIdentifier || this.ownerDocument.frameIdentifier;
+    }
+
+    get frame()
+    {
+        if (!this._frame)
+            this._frame = WebInspector.frameResourceManager.frameForIdentifier(this.frameIdentifier);
+        return this._frame;
+    }
+
     get children()
     {
         if (!this._children)
@@ -643,11 +655,6 @@ WebInspector.DOMNode = class DOMNode extends WebInspector.Object
                 callback.apply(null, arguments);
         };
     }
-
-    get frameIdentifier()
-    {
-        return this._frameIdentifier;
-    }
 };
 
 WebInspector.DOMNode.Event = {
index ab96be6..e7e0091 100644 (file)
@@ -235,9 +235,20 @@ WebInspector.DOMNodeStyles = class DOMNodeStyles extends WebInspector.Object
             this.refresh();
         }
 
-        var selector = this._node.appropriateSelectorFor(true);
+        let selector = this._node.appropriateSelectorFor(true);
 
-        CSSAgent.addRule.invoke({contextNodeId: this._node.id, selector}, addedRule.bind(this));
+        // COMPATIBILITY (iOS 9): Before CSS.createStyleSheet, CSS.addRule could be called with a contextNode.
+        if (!CSSAgent.createStyleSheet) {
+            CSSAgent.addRule.invoke({contextNodeId: this._node.id , selector}, addedRule.bind(this));
+            return;
+        }
+
+        function inspectorStyleSheetAvailable(styleSheet)
+        {
+            CSSAgent.addRule(styleSheet.id, selector, addedRule.bind(this));
+        }
+
+        WebInspector.cssStyleManager.preferredInspectorStyleSheetForFrame(this._node.frame, inspectorStyleSheetAvailable.bind(this));
     }
 
     addRuleWithSelector(selector)