Web Inspector: Add regular expression support to XHR breakpoints
authormattbaker@apple.com <mattbaker@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Apr 2017 23:09:55 +0000 (23:09 +0000)
committermattbaker@apple.com <mattbaker@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 20 Apr 2017 23:09:55 +0000 (23:09 +0000)
https://bugs.webkit.org/show_bug.cgi?id=170099
<rdar://problem/31558082>

Reviewed by Joseph Pecoraro.

Source/JavaScriptCore:

* inspector/protocol/DOMDebugger.json:
New optional `isRegex` parameter denotes whether `url` contains
a regular expression.

Source/WebCore:

* inspector/InspectorDOMDebuggerAgent.cpp:
(WebCore::InspectorDOMDebuggerAgent::setXHRBreakpoint):
(WebCore::InspectorDOMDebuggerAgent::willSendXMLHttpRequest):
Use ContentSearchUtilities for both Text and RegularExpression breakpoints.

* inspector/InspectorDOMDebuggerAgent.h:
Associate XHR breakpoint with a type.

Source/WebInspectorUI:

* Localizations/en.lproj/localizedStrings.js:
New strings for the Add XHR Breakpoint popover.

* UserInterface/Controllers/DOMDebuggerManager.js:
(WebInspector.DOMDebuggerManager):
(WebInspector.DOMDebuggerManager.prototype.addXHRBreakpoint):
Update for new XHRBreakpoint constructor.
(WebInspector.DOMDebuggerManager.prototype._updateXHRBreakpoint):
Set `isRegex` flag.

* UserInterface/Main.html:
New files for CodeMirror mode and breakpoint popover.

* UserInterface/Models/XHRBreakpoint.js:
Breakpoint can be of type Text or RegularExpression.
(WebInspector.XHRBreakpoint):
(WebInspector.XHRBreakpoint.prototype.get type):
(WebInspector.XHRBreakpoint.prototype.get serializableInfo):

* UserInterface/Views/CodeMirrorRegexMode.css: Added.
(.cm-s-default .cm-regex-character-set):
(.cm-s-default .cm-regex-character-set-negate):
(.cm-s-default :matches(.cm-regex-escape, .cm-regex-escape-2, .cm-regex-escape-3)):
(.cm-s-default :matches(.cm-regex-group, .cm-regex-lookahead)):
(.cm-s-default .cm-regex-quantifier):
(.cm-s-default :matches(.cm-regex-literal, .cm-regex-special, .cm-regex-backreference)):
Colors for regular expression syntax highlighting. The regex mode emits
more class names than we have separate colors for, allowing for further
customization in the future.

* UserInterface/Views/CodeMirrorRegexMode.js: Added.
(characterSetTokenizer):
(consumeEscapeSequence):
(tokenBase):
New CodeMirror mode for highlighting "text/x-regex" content.

* UserInterface/Views/DebuggerSidebarPanel.js:
Use new popover type for creating XHR breakpoints.
(WebInspector.DebuggerSidebarPanel.prototype._addXHRBreakpointButtonClicked):
(WebInspector.DebuggerSidebarPanel.prototype.willDismissPopover):
(WebInspector.DebuggerSidebarPanel):

* UserInterface/Views/XHRBreakpointPopover.css: Added.
(.popover .xhr-breakpoint-content):
(.popover .xhr-breakpoint-content > .editor-wrapper):
(.popover .xhr-breakpoint-content > .editor-wrapper > .editor):
(body[dir=ltr] .popover .xhr-breakpoint-content > .editor-wrapper > .editor):
(body[dir=rtl] .popover .xhr-breakpoint-content > .editor-wrapper > .editor):
(.popover .xhr-breakpoint-content > .editor-wrapper > .editor > .CodeMirror):
(.popover .xhr-breakpoint-content > .editor-wrapper > .editor > .CodeMirror-scroll):
Styles for new popover.

* UserInterface/Views/XHRBreakpointPopover.js: Added.
New popover class. Includes a picker for selecting breakpoint type
(Text or RegularExpression), and has an improved popover caption. Input
field style and placeholder text changes based on the breakpoint type.

(WebInspector.XHRBreakpointPopover):
(WebInspector.XHRBreakpointPopover.prototype.get result):
(WebInspector.XHRBreakpointPopover.prototype.get type):
(WebInspector.XHRBreakpointPopover.prototype.get value):
(WebInspector.XHRBreakpointPopover.prototype.show.addOption):
(WebInspector.XHRBreakpointPopover.prototype.show):
(WebInspector.XHRBreakpointPopover.prototype._createEditor):
(WebInspector.XHRBreakpointPopover.prototype._updateEditor):
(WebInspector.XHRBreakpointPopover.prototype._presentOverTargetElement):

* UserInterface/Views/XHRBreakpointTreeElement.js:
(WebInspector.XHRBreakpointTreeElement):
Differentiate breakpoint type by enclosing subtitle in either double quotes
or regex-literal delimiters, depending on the type of breakpoint.

LayoutTests:

Update tests and expectations for new XHR breakpoint type.

* inspector/dom-debugger/xhr-breakpoints-expected.txt:
* inspector/dom-debugger/xhr-breakpoints.html:

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

20 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/dom-debugger/xhr-breakpoints-expected.txt
LayoutTests/inspector/dom-debugger/xhr-breakpoints.html
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/protocol/DOMDebugger.json
Source/WebCore/ChangeLog
Source/WebCore/inspector/InspectorDOMDebuggerAgent.cpp
Source/WebCore/inspector/InspectorDOMDebuggerAgent.h
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Controllers/DOMDebuggerManager.js
Source/WebInspectorUI/UserInterface/Main.html
Source/WebInspectorUI/UserInterface/Models/XHRBreakpoint.js
Source/WebInspectorUI/UserInterface/Views/CodeMirrorRegexMode.css [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/CodeMirrorRegexMode.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/DebuggerSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/Variables.css
Source/WebInspectorUI/UserInterface/Views/XHRBreakpointPopover.css [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/XHRBreakpointPopover.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/XHRBreakpointTreeElement.js

index 40f6890..e9ae296 100644 (file)
@@ -1,3 +1,16 @@
+2017-04-20  Matt Baker  <mattbaker@apple.com>
+
+        Web Inspector: Add regular expression support to XHR breakpoints
+        https://bugs.webkit.org/show_bug.cgi?id=170099
+        <rdar://problem/31558082>
+
+        Reviewed by Joseph Pecoraro.
+
+        Update tests and expectations for new XHR breakpoint type.
+
+        * inspector/dom-debugger/xhr-breakpoints-expected.txt:
+        * inspector/dom-debugger/xhr-breakpoints.html:
+
 2017-04-20  Wenson Hsieh  <wenson_hsieh@apple.com>
 
         Inline anchor elements cannot be dragged when starting the drag from a block descendant
index 4014710..1f22f80 100644 (file)
@@ -13,7 +13,7 @@ CALL STACK:
 1: [P] Global Code
 -- Running test teardown.
 
--- Running test case: BreakOnXMLHttpRequestContainingURL
+-- Running test case: BreakOnXMLHttpRequestContainingText
 PASS: Breakpoint should not be disabled initially.
 PASS: Added breakpoint for URL: data
 Sending XMLHttpRequest.
@@ -25,6 +25,18 @@ CALL STACK:
 1: [P] Global Code
 -- Running test teardown.
 
+-- Running test case: BreakOnXMLHttpRequestMatchingRegularExpression
+PASS: Breakpoint should not be disabled initially.
+PASS: Added breakpoint for URL: data[A-Z]*.(json|txt|png)
+Sending XMLHttpRequest.
+PAUSED:
+PASS: Pause reason should be XHR.
+PASS: Pause data URL should match breakpoint URL.
+CALL STACK:
+0: [F] loadResourceXHR
+1: [P] Global Code
+-- Running test teardown.
+
 -- Running test case: ShouldNotPauseOnDisabledBreakpoint
 PASS: Breakpoint should not be disabled initially.
 Disabling breakpoint.
index 4283e21..fd37ec3 100644 (file)
@@ -41,14 +41,14 @@ function test()
         });
     }
 
-    function addBreakpointForURL(url, disabled) {
+    function addBreakpoint(type, url, disabled) {
         return new Promise((resolve, reject) => {
             let mainFrame = WebInspector.frameResourceManager.mainFrame;
             InspectorTest.assert(mainFrame, "Missing main frame.");
             if (!mainFrame)
                 reject();
 
-            let breakpoint = new WebInspector.XHRBreakpoint(url);
+            let breakpoint = new WebInspector.XHRBreakpoint(type, url);
             WebInspector.domDebuggerManager.awaitEvent(WebInspector.DOMDebuggerManager.Event.XHRBreakpointAdded)
             .then(() => {
                 InspectorTest.expectFalse(breakpoint.disabled, "Breakpoint should not be disabled initially.");
@@ -93,12 +93,40 @@ function test()
     });
 
     suite.addTestCase({
-        name: "BreakOnXMLHttpRequestContainingURL",
-        description: "Check that debugger pauses for XMLHttpRequests.",
+        name: "BreakOnXMLHttpRequestContainingText",
+        description: "Check that debugger pauses on a URL containing text.",
         teardown,
         test(resolve, reject) {
             let expectedBreakpointURL;
-            addBreakpointForURL("data")
+            addBreakpoint(WebInspector.XHRBreakpoint.Type.Text, "data")
+            .then((breakpoint) => {
+                expectedBreakpointURL = breakpoint.url;
+
+                InspectorTest.log("Sending XMLHttpRequest.");
+                InspectorTest.evaluateInPage("loadResourceXHR()");
+                return WebInspector.debuggerManager.awaitEvent(WebInspector.DebuggerManager.Event.Paused);
+            })
+            .then(() => {
+                let targetData = WebInspector.debuggerManager.dataForTarget(WebInspector.debuggerManager.activeCallFrame.target);
+                InspectorTest.log("PAUSED:");
+                InspectorTest.expectEqual(targetData.pauseReason, WebInspector.DebuggerManager.PauseReason.XHR, "Pause reason should be XHR.");
+                InspectorTest.expectEqual(targetData.pauseData.breakpointURL, expectedBreakpointURL, "Pause data URL should match breakpoint URL.");
+
+                logActiveStackTrace();
+
+                return WebInspector.debuggerManager.resume();
+            })
+            .then(resolve, reject);
+        }
+    });
+
+    suite.addTestCase({
+        name: "BreakOnXMLHttpRequestMatchingRegularExpression",
+        description: "Check that debugger pauses based on a URL matching a regex pattern.",
+        teardown,
+        test(resolve, reject) {
+            let expectedBreakpointURL;
+            addBreakpoint(WebInspector.XHRBreakpoint.Type.RegularExpression, "data[A-Z]*\.(json|txt|png)")
             .then((breakpoint) => {
                 expectedBreakpointURL = breakpoint.url;
 
@@ -126,7 +154,7 @@ function test()
         teardown,
         test(resolve, reject) {
             const disabled = true;
-            addBreakpointForURL("data", disabled)
+            addBreakpoint(WebInspector.XHRBreakpoint.Type.Text, "data", disabled)
             .then((breakpoint) => {
                 InspectorTest.log("Sending XMLHttpRequest.");
                 return Promise.race([awaitEvaluateInPage("loadResourceXHR()"), rejectOnPause()]);
@@ -144,7 +172,7 @@ function test()
         description: "Check that debugger does not pause for breakpoint with different URL.",
         teardown,
         test(resolve, reject) {
-            addBreakpointForURL("nonexistant-url")
+            addBreakpoint(WebInspector.XHRBreakpoint.Type.Text, "nonexistant-url")
             .then((breakpoint) => {
                 InspectorTest.log("Sending XMLHttpRequest.");
                 return Promise.race([awaitEvaluateInPage("loadResourceXHR()"), rejectOnPause()]);
@@ -162,7 +190,7 @@ function test()
         description: "Check that debugger does not pause for removed breakpoint.",
         teardown,
         test(resolve, reject) {
-            addBreakpointForURL("data")
+            addBreakpoint(WebInspector.XHRBreakpoint.Type.Text, "data")
             .then((breakpoint) => {
                 let promise = WebInspector.domDebuggerManager.awaitEvent(WebInspector.DOMDebuggerManager.Event.XHRBreakpointRemoved);
 
index 51e6c43..b693a68 100644 (file)
@@ -1,3 +1,15 @@
+2017-04-20  Matt Baker  <mattbaker@apple.com>
+
+        Web Inspector: Add regular expression support to XHR breakpoints
+        https://bugs.webkit.org/show_bug.cgi?id=170099
+        <rdar://problem/31558082>
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/protocol/DOMDebugger.json:
+        New optional `isRegex` parameter denotes whether `url` contains
+        a regular expression.
+
 2017-04-15  Filip Pizlo  <fpizlo@apple.com>
 
         Optimize SharedArrayBuffer in the DFG+FTL
index f170cc6..9ef02c9 100644 (file)
@@ -58,7 +58,8 @@
         {
             "name": "setXHRBreakpoint",
             "parameters": [
-                { "name": "url", "type": "string", "description": "Resource URL substring. All XHRs having this substring in the URL will get stopped upon. An empty string will pause on all XHRs." }
+                { "name": "url", "type": "string", "description": "Resource URL substring or regular expression. All XHRs having this substring in the URL will get stopped upon. An empty string will pause on all XHRs." },
+                { "name": "isRegex", "type": "boolean", "optional": true, "descriprion": "Whether the URL string is a regular expression." }
             ],
             "description": "Sets breakpoint on XMLHttpRequest."
         },
index a44c0a7..3c18248 100644 (file)
@@ -1,3 +1,19 @@
+2017-04-20  Matt Baker  <mattbaker@apple.com>
+
+        Web Inspector: Add regular expression support to XHR breakpoints
+        https://bugs.webkit.org/show_bug.cgi?id=170099
+        <rdar://problem/31558082>
+
+        Reviewed by Joseph Pecoraro.
+
+        * inspector/InspectorDOMDebuggerAgent.cpp:
+        (WebCore::InspectorDOMDebuggerAgent::setXHRBreakpoint):
+        (WebCore::InspectorDOMDebuggerAgent::willSendXMLHttpRequest):
+        Use ContentSearchUtilities for both Text and RegularExpression breakpoints.
+
+        * inspector/InspectorDOMDebuggerAgent.h:
+        Associate XHR breakpoint with a type.
+
 2017-04-20  Eric Carlson  <eric.carlson@apple.com>
 
         [MediaStream] Cleanup and simplify CoreAudioCaptureSource
index d6354f6..92c9e9e 100644 (file)
 #include "HTMLElement.h"
 #include "InspectorDOMAgent.h"
 #include "InstrumentingAgents.h"
+#include <inspector/ContentSearchUtilities.h>
 #include <inspector/InspectorFrontendDispatchers.h>
 #include <inspector/InspectorValues.h>
+#include <yarr/RegularExpression.h>
 
 namespace {
 
@@ -372,14 +374,15 @@ void InspectorDOMDebuggerAgent::pauseOnNativeEventIfNeeded(bool isDOMEvent, cons
         m_debuggerAgent->schedulePauseOnNextStatement(Inspector::DebuggerFrontendDispatcher::Reason::EventListener, WTFMove(eventData));
 }
 
-void InspectorDOMDebuggerAgent::setXHRBreakpoint(ErrorString&, const String& url)
+void InspectorDOMDebuggerAgent::setXHRBreakpoint(ErrorString&, const String& url, const bool* const optionalIsRegex)
 {
     if (url.isEmpty()) {
         m_pauseOnAllXHRsEnabled = true;
         return;
     }
 
-    m_xhrBreakpoints.add(url);
+    bool isRegex = optionalIsRegex ? *optionalIsRegex : false;
+    m_xhrBreakpoints.set(url, isRegex ? XHRBreakpointType::RegularExpression : XHRBreakpointType::Text);
 }
 
 void InspectorDOMDebuggerAgent::removeXHRBreakpoint(ErrorString&, const String& url)
@@ -401,9 +404,12 @@ void InspectorDOMDebuggerAgent::willSendXMLHttpRequest(const String& url)
     if (m_pauseOnAllXHRsEnabled)
         breakpointURL = emptyString();
     else {
-        for (auto& breakpoint : m_xhrBreakpoints) {
-            if (url.contains(breakpoint)) {
-                breakpointURL = breakpoint;
+        for (auto& entry : m_xhrBreakpoints) {
+            const auto& query = entry.key;
+            bool isRegex = entry.value == XHRBreakpointType::RegularExpression;
+            auto regex = ContentSearchUtilities::createSearchRegex(query, false, isRegex);
+            if (regex.match(url) != -1) {
+                breakpointURL = query;
                 break;
             }
         }
index 6b57c19..2513f84 100644 (file)
@@ -57,7 +57,7 @@ public:
     virtual ~InspectorDOMDebuggerAgent();
 
     // DOMDebugger API
-    void setXHRBreakpoint(ErrorString&, const String& url) override;
+    void setXHRBreakpoint(ErrorString&, const String& url, const bool* const optionalIsRegex) override;
     void removeXHRBreakpoint(ErrorString&, const String& url) override;
     void setEventListenerBreakpoint(ErrorString&, const String& eventName) override;
     void removeEventListenerBreakpoint(ErrorString&, const String& eventName) override;
@@ -100,7 +100,10 @@ private:
 
     HashMap<Node*, uint32_t> m_domBreakpoints;
     HashSet<String> m_eventListenerBreakpoints;
-    HashSet<String> m_xhrBreakpoints;
+
+    enum class XHRBreakpointType { Text, RegularExpression };
+
+    HashMap<String, XHRBreakpointType> m_xhrBreakpoints;
     bool m_pauseOnAllXHRsEnabled { false };
 };
 
index 930bdc9..0031838 100644 (file)
@@ -1,3 +1,83 @@
+2017-04-20  Matt Baker  <mattbaker@apple.com>
+
+        Web Inspector: Add regular expression support to XHR breakpoints
+        https://bugs.webkit.org/show_bug.cgi?id=170099
+        <rdar://problem/31558082>
+
+        Reviewed by Joseph Pecoraro.
+
+        * Localizations/en.lproj/localizedStrings.js:
+        New strings for the Add XHR Breakpoint popover.
+
+        * UserInterface/Controllers/DOMDebuggerManager.js:
+        (WebInspector.DOMDebuggerManager):
+        (WebInspector.DOMDebuggerManager.prototype.addXHRBreakpoint):
+        Update for new XHRBreakpoint constructor.
+        (WebInspector.DOMDebuggerManager.prototype._updateXHRBreakpoint):
+        Set `isRegex` flag.
+
+        * UserInterface/Main.html:
+        New files for CodeMirror mode and breakpoint popover.
+
+        * UserInterface/Models/XHRBreakpoint.js:
+        Breakpoint can be of type Text or RegularExpression.
+        (WebInspector.XHRBreakpoint):
+        (WebInspector.XHRBreakpoint.prototype.get type):
+        (WebInspector.XHRBreakpoint.prototype.get serializableInfo):
+
+        * UserInterface/Views/CodeMirrorRegexMode.css: Added.
+        (.cm-s-default .cm-regex-character-set):
+        (.cm-s-default .cm-regex-character-set-negate):
+        (.cm-s-default :matches(.cm-regex-escape, .cm-regex-escape-2, .cm-regex-escape-3)):
+        (.cm-s-default :matches(.cm-regex-group, .cm-regex-lookahead)):
+        (.cm-s-default .cm-regex-quantifier):
+        (.cm-s-default :matches(.cm-regex-literal, .cm-regex-special, .cm-regex-backreference)):
+        Colors for regular expression syntax highlighting. The regex mode emits
+        more class names than we have separate colors for, allowing for further
+        customization in the future.
+
+        * UserInterface/Views/CodeMirrorRegexMode.js: Added.
+        (characterSetTokenizer):
+        (consumeEscapeSequence):
+        (tokenBase):
+        New CodeMirror mode for highlighting "text/x-regex" content.
+
+        * UserInterface/Views/DebuggerSidebarPanel.js:
+        Use new popover type for creating XHR breakpoints.
+        (WebInspector.DebuggerSidebarPanel.prototype._addXHRBreakpointButtonClicked):
+        (WebInspector.DebuggerSidebarPanel.prototype.willDismissPopover):
+        (WebInspector.DebuggerSidebarPanel):
+
+        * UserInterface/Views/XHRBreakpointPopover.css: Added.
+        (.popover .xhr-breakpoint-content):
+        (.popover .xhr-breakpoint-content > .editor-wrapper):
+        (.popover .xhr-breakpoint-content > .editor-wrapper > .editor):
+        (body[dir=ltr] .popover .xhr-breakpoint-content > .editor-wrapper > .editor):
+        (body[dir=rtl] .popover .xhr-breakpoint-content > .editor-wrapper > .editor):
+        (.popover .xhr-breakpoint-content > .editor-wrapper > .editor > .CodeMirror):
+        (.popover .xhr-breakpoint-content > .editor-wrapper > .editor > .CodeMirror-scroll):
+        Styles for new popover.
+
+        * UserInterface/Views/XHRBreakpointPopover.js: Added.
+        New popover class. Includes a picker for selecting breakpoint type
+        (Text or RegularExpression), and has an improved popover caption. Input
+        field style and placeholder text changes based on the breakpoint type.
+
+        (WebInspector.XHRBreakpointPopover):
+        (WebInspector.XHRBreakpointPopover.prototype.get result):
+        (WebInspector.XHRBreakpointPopover.prototype.get type):
+        (WebInspector.XHRBreakpointPopover.prototype.get value):
+        (WebInspector.XHRBreakpointPopover.prototype.show.addOption):
+        (WebInspector.XHRBreakpointPopover.prototype.show):
+        (WebInspector.XHRBreakpointPopover.prototype._createEditor):
+        (WebInspector.XHRBreakpointPopover.prototype._updateEditor):
+        (WebInspector.XHRBreakpointPopover.prototype._presentOverTargetElement):
+
+        * UserInterface/Views/XHRBreakpointTreeElement.js:
+        (WebInspector.XHRBreakpointTreeElement):
+        Differentiate breakpoint type by enclosing subtitle in either double quotes
+        or regex-literal delimiters, depending on the type of breakpoint.
+
 2017-04-19  Nikita Vasilyev  <nvasilyev@apple.com>
 
         REGRESSION (r209882): Web Inspector: Console's filter bar has text search field, but the next/previous buttons don't do anything
index 8ceb8bf..fd04c64 100644 (file)
@@ -126,8 +126,8 @@ localizedStrings["Bottom"] = "Bottom";
 localizedStrings["Boundary"] = "Boundary";
 localizedStrings["Box Model"] = "Box Model";
 localizedStrings["Box Shadow"] = "Box Shadow";
+localizedStrings["Break on request with URL:"] = "Break on request with URL:";
 localizedStrings["Break on…"] = "Break on…";
-localizedStrings["Break when URL contains:"] = "Break when URL contains:";
 localizedStrings["Breakdown"] = "Breakdown";
 localizedStrings["Breakdown of each memory category at the end of the selected time range"] = "Breakdown of each memory category at the end of the selected time range";
 localizedStrings["Breakpoints"] = "Breakpoints";
@@ -209,6 +209,7 @@ localizedStrings["Console cleared at %s"] = "Console cleared at %s";
 localizedStrings["Console opened at %s"] = "Console opened at %s";
 localizedStrings["Console:"] = "Console:";
 localizedStrings["Container Regions"] = "Container Regions";
+localizedStrings["Containing"] = "Containing";
 localizedStrings["Content"] = "Content";
 localizedStrings["Content Flow"] = "Content Flow";
 localizedStrings["Content Security Policy violation of directive: %s"] = "Content Security Policy violation of directive: %s";
@@ -519,6 +520,7 @@ localizedStrings["Main Frame"] = "Main Frame";
 localizedStrings["Manifest URL"] = "Manifest URL";
 localizedStrings["Margin"] = "Margin";
 localizedStrings["Mass"] = "Mass";
+localizedStrings["Matching"] = "Matching";
 localizedStrings["Max"] = "Max";
 localizedStrings["Max Comparison"] = "Max Comparison";
 localizedStrings["Maximum"] = "Maximum";
@@ -655,6 +657,7 @@ localizedStrings["Refresh"] = "Refresh";
 localizedStrings["Refresh watch expressions"] = "Refresh watch expressions";
 localizedStrings["Region Flow"] = "Region Flow";
 localizedStrings["Region announced in its entirety."] = "Region announced in its entirety.";
+localizedStrings["Regular Expression"] = "Regular Expression";
 localizedStrings["Reload page (%s)\nReload ignoring cache (%s)"] = "Reload page (%s)\nReload ignoring cache (%s)";
 localizedStrings["Removals"] = "Removals";
 localizedStrings["Remove Watch Expression"] = "Remove Watch Expression";
index 8bfc536..abe9a6c 100644 (file)
@@ -37,8 +37,7 @@ WebInspector.DOMDebuggerManager = class DOMDebuggerManager extends WebInspector.
         this._xhrBreakpoints = [];
         this._allRequestsBreakpointEnabledSetting = new WebInspector.Setting("break-on-all-requests", false);
 
-        const emptyURL = "";
-        this._allRequestsBreakpoint = new WebInspector.XHRBreakpoint(emptyURL, !this._allRequestsBreakpointEnabledSetting.value);
+        this._allRequestsBreakpoint = new WebInspector.XHRBreakpoint(null, null, !this._allRequestsBreakpointEnabledSetting.value);
 
         WebInspector.DOMBreakpoint.addEventListener(WebInspector.DOMBreakpoint.Event.DisabledStateDidChange, this._domBreakpointDisabledStateDidChange, this);
         WebInspector.XHRBreakpoint.addEventListener(WebInspector.XHRBreakpoint.Event.DisabledStateDidChange, this._xhrBreakpointDisabledStateDidChange, this);
@@ -60,7 +59,7 @@ WebInspector.DOMDebuggerManager = class DOMDebuggerManager extends WebInspector.
             }
 
             for (let cookie of this._xhrBreakpointsSetting.value) {
-                let breakpoint = new WebInspector.XHRBreakpoint(cookie.url, cookie.disabled);
+                let breakpoint = new WebInspector.XHRBreakpoint(cookie.type, cookie.url, cookie.disabled);
                 this.addXHRBreakpoint(breakpoint);
             }
 
@@ -190,7 +189,7 @@ WebInspector.DOMDebuggerManager = class DOMDebuggerManager extends WebInspector.
         if (this._xhrBreakpoints.includes(breakpoint))
             return;
 
-        if (this._xhrBreakpoints.some((entry) => entry.url === breakpoint.url))
+        if (this._xhrBreakpoints.some((entry) => entry.type === breakpoint.type && entry.url === breakpoint.url))
             return;
 
         this._xhrBreakpoints.push(breakpoint);
@@ -358,8 +357,10 @@ WebInspector.DOMDebuggerManager = class DOMDebuggerManager extends WebInspector.
 
         if (breakpoint.disabled)
             DOMDebuggerAgent.removeXHRBreakpoint(breakpoint.url, breakpointUpdated);
-        else
-            DOMDebuggerAgent.setXHRBreakpoint(breakpoint.url, breakpointUpdated);
+        else {
+            let isRegex = breakpoint.type === WebInspector.XHRBreakpoint.Type.RegularExpression;
+            DOMDebuggerAgent.setXHRBreakpoint(breakpoint.url, isRegex, breakpointUpdated);
+        }
     }
 
     _resolveXHRBreakpoint(breakpoint)
index 57d0caa..ff96d12 100644 (file)
@@ -49,6 +49,7 @@
     <link rel="stylesheet" href="Views/CircleChart.css">
     <link rel="stylesheet" href="Views/ClusterContentView.css">
     <link rel="stylesheet" href="Views/CodeMirrorOverrides.css">
+    <link rel="stylesheet" href="Views/CodeMirrorRegexMode.css">
     <link rel="stylesheet" href="Views/CollectionContentView.css">
     <link rel="stylesheet" href="Views/ColorPicker.css">
     <link rel="stylesheet" href="Views/ColorWheel.css">
     <link rel="stylesheet" href="Views/VisualStyleTimingEditor.css">
     <link rel="stylesheet" href="Views/VisualStyleUnitSlider.css">
     <link rel="stylesheet" href="Views/WebSocketContentView.css">
+    <link rel="stylesheet" href="Views/XHRBreakpointPopover.css">
 
     <link rel="stylesheet" href="Controllers/CodeMirrorCompletionController.css">
     <link rel="stylesheet" href="Controllers/CodeMirrorDragToAdjustNumberController.css">
     <script src="Views/CodeMirrorAdditions.js"></script>
     <script src="Views/CodeMirrorEditor.js"></script>
     <script src="Views/CodeMirrorFormatters.js"></script>
+    <script src="Views/CodeMirrorRegexMode.js"></script>
     <script src="Views/CodeMirrorTextMarkers.js"></script>
     <script src="Views/CollectionContentView.js"></script>
     <script src="Views/ColorPicker.js"></script>
     <script src="Views/WebSocketDataGridNode.js"></script>
     <script src="Views/WebSocketResourceTreeElement.js"></script>
     <script src="Views/WorkerTreeElement.js"></script>
+    <script src="Views/XHRBreakpointPopover.js"></script>
     <script src="Views/XHRBreakpointTreeElement.js"></script>
 
     <script src="Views/VisualStyleDetailsPanel.js"></script>
index c071ae8..85554d0 100644 (file)
 
 WebInspector.XHRBreakpoint = class XHRBreakpoint extends WebInspector.Object
 {
-    constructor(url, disabled)
+    constructor(type, url, disabled)
     {
         super();
 
-        this._url = url;
+        this._type = type || WebInspector.XHRBreakpoint.Type.Text;
+        this._url = url || "";
         this._disabled = disabled || false;
     }
 
     // Public
 
+    get type() { return this._type; }
     get url() { return this._url; }
 
     get disabled()
@@ -54,7 +56,7 @@ WebInspector.XHRBreakpoint = class XHRBreakpoint extends WebInspector.Object
 
     get serializableInfo()
     {
-        let info = {url: this._url};
+        let info = {type: this._type, url: this._url};
         if (this._disabled)
             info.disabled = true;
 
@@ -73,3 +75,8 @@ WebInspector.XHRBreakpoint.Event = {
     DisabledStateDidChange: "xhr-breakpoint-disabled-state-did-change",
     ResolvedStateDidChange: "xhr-breakpoint-resolved-state-did-change",
 };
+
+WebInspector.XHRBreakpoint.Type = {
+    Text: "text",
+    RegularExpression: "regex",
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/CodeMirrorRegexMode.css b/Source/WebInspectorUI/UserInterface/Views/CodeMirrorRegexMode.css
new file mode 100644 (file)
index 0000000..2362fae
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+.cm-s-default .cm-regex-character-set {
+    color: var(--syntax-highlight-string-color);
+}
+
+.cm-s-default .cm-regex-character-set-negate {
+    color: var(--syntax-highlight-number-color);
+}
+
+.cm-s-default :matches(.cm-regex-escape, .cm-regex-escape-2, .cm-regex-escape-3) {
+    color: var(--syntax-highlight-symbol-color);
+}
+
+.cm-s-default :matches(.cm-regex-group, .cm-regex-lookahead) {
+    color: var(--syntax-highlight-regex-group-color);
+}
+
+.cm-s-default .cm-regex-quantifier {
+    color: var(--syntax-highlight-number-color);
+}
+
+.cm-s-default :matches(.cm-regex-literal, .cm-regex-special, .cm-regex-backreference) {
+    color: var(--syntax-highlight-boolean-color);
+}
\ No newline at end of file
diff --git a/Source/WebInspectorUI/UserInterface/Views/CodeMirrorRegexMode.js b/Source/WebInspectorUI/UserInterface/Views/CodeMirrorRegexMode.js
new file mode 100644 (file)
index 0000000..8825e7d
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2017 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+CodeMirror.defineMode("regex", function() {
+    function characterSetTokenizer(stream, state) {
+        let context = state.currentContext();
+
+        if (context.negatedCharacterSet === undefined) {
+            context.negatedCharacterSet = stream.eat("^");
+            if (context.negatedCharacterSet)
+                return "regex-character-set-negate";
+        }
+
+        while (stream.peek()) {
+            if (stream.eat("\\"))
+                return consumeEscapeSequence(stream);
+
+            if (stream.eat("]")) {
+                state.tokenize = tokenBase;
+                return state.popContext();
+            }
+
+            stream.next();
+        }
+
+        return "error";
+    }
+
+    function consumeEscapeSequence(stream) {
+        if (stream.match(/[bBdDwWsStrnvf0]/))
+            return "regex-special";
+
+        if (stream.eat("x")) {
+            if (stream.match(/[0-9a-fA-F]{2}/))
+                return "regex-escape";
+            return "error";
+        }
+
+        if (stream.eat("u")) {
+            if (stream.match(/[0-9a-fA-F]{4}/))
+                return "regex-escape-2";
+            return "error";
+        }
+
+        if (stream.eat("c")) {
+            if (stream.match(/[A-Z]/))
+                return "regex-escape-3";
+            return "error";
+        }
+
+        if (stream.match(/[1-9]/))
+            return "regex-backreference";
+
+        if (stream.next())
+            return "regex-literal";
+
+        return "error";
+    }
+
+    function tokenBase(stream, state) {
+        let ch = stream.next();
+
+        if (ch === "\\")
+            return consumeEscapeSequence(stream);
+
+        // Match start of capturing/noncapturing group, or positive/negative lookaheads.
+        if (ch === "(") {
+            let style;
+            if (stream.match(/\?[=!]/))
+                style = "regex-lookahead";
+            else {
+                stream.match(/\?:/);
+                style = "regex-group";
+            }
+
+            return state.pushContext(stream, style, ")");
+        }
+
+        let context = state.currentContext();
+        if (context && context.type === ch)
+            return state.popContext();
+
+        // Match quantifiers: *, +, ?, {n}, {n,}, and {n,m}
+        if (/[*+?]/.test(ch))
+            return "regex-quantifier";
+
+        if (ch === "{") {
+            if (stream.match(/\d+}/))
+                return "regex-quantifier";
+
+            let matches = stream.match(/(\d+),(\d+)?}/);
+            if (!matches)
+                return "error";
+
+            let minimum = parseInt(matches[1]);
+            let maximum = parseInt(matches[2]);
+            if (minimum > maximum)
+                return "error";
+
+            return "regex-quantifier";
+        }
+
+        // Match character sets.
+        if (ch === "[") {
+            state.tokenize = characterSetTokenizer;
+            return state.pushContext(stream, "regex-character-set");
+        }
+
+        // Match miscelleneous special characters.
+        if (/[.^$|]/.test(ch))
+            return "regex-special";
+
+        return null;
+    }
+
+    return {
+        startState: function() {
+            let contextStack = [];
+            return {
+                currentContext: function() {
+                    return contextStack.length ? contextStack[contextStack.length - 1] : null;
+                },
+                pushContext: function(stream, data, type) {
+                    let context = {data, type};
+                    contextStack.push(context);
+                    return data;
+                },
+                popContext: function() {
+                    console.assert(contextStack.length, "Tried to pop empty context stack.");
+                    return contextStack.pop().data;
+                },
+                tokenize: tokenBase,
+            };
+        },
+        token: function(stream, state) {
+            return state.tokenize(stream, state);
+        }
+    };
+});
+
+CodeMirror.defineMIME("text/x-regex", "regex");
\ No newline at end of file
index e64804a..3f0d20c 100644 (file)
@@ -1154,7 +1154,7 @@ WebInspector.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WebInspec
 
     _addXHRBreakpointButtonClicked(event)
     {
-        let popover = new WebInspector.InputPopover(WebInspector.UIString("Break when URL contains:"), this);
+        let popover = new WebInspector.XHRBreakpointPopover(this);
         popover.show(event.target.element, [WebInspector.RectEdge.MAX_Y, WebInspector.RectEdge.MIN_Y, WebInspector.RectEdge.MAX_X]);
     }
 
@@ -1169,7 +1169,7 @@ WebInspector.DebuggerSidebarPanel = class DebuggerSidebarPanel extends WebInspec
         if (!url)
             return;
 
-        WebInspector.domDebuggerManager.addXHRBreakpoint(new WebInspector.XHRBreakpoint(url));
+        WebInspector.domDebuggerManager.addXHRBreakpoint(new WebInspector.XHRBreakpoint(popover.type, url));
     }
 };
 
index e04cd1b..498b44e 100644 (file)
@@ -76,6 +76,8 @@
     --syntax-highlight-regexp-color: hsl(20, 100%, 44%);
     --syntax-highlight-symbol-color: hsl(172, 45%, 45%);
 
+    --syntax-highlight-regex-group-color: var(--text-color-gray-medium);
+
     --memory-javascript-fill-color: hsl(269, 65%, 75%);
     --memory-javascript-stroke-color: hsl(269, 33%, 50%);
     --memory-images-fill-color: hsl(0, 65%, 75%);
diff --git a/Source/WebInspectorUI/UserInterface/Views/XHRBreakpointPopover.css b/Source/WebInspectorUI/UserInterface/Views/XHRBreakpointPopover.css
new file mode 100644 (file)
index 0000000..3e60ab0
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2017 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+.popover .xhr-breakpoint-content {
+    padding: 5px;
+    margin: 2px;
+}
+
+.popover .xhr-breakpoint-content > .editor-wrapper {
+    margin-top: 4px;
+    display: flex;
+    flex-direction: row;
+}
+
+.popover .xhr-breakpoint-content > .editor-wrapper > .editor {
+    width: 180px;
+    padding: 4px 0 2px 0;
+    -webkit-appearance: textfield;
+    border: 1px solid hsl(0, 0%, 78%);
+    background: white;
+
+    --editor-margin-start: 4px;
+}
+
+body[dir=ltr] .popover .xhr-breakpoint-content > .editor-wrapper > .editor {
+    margin-left: var(--editor-margin-start);
+}
+
+body[dir=rtl] .popover .xhr-breakpoint-content > .editor-wrapper > .editor {
+    margin-right: var(--editor-margin-start);
+}
+
+.popover .xhr-breakpoint-content > .editor-wrapper > .editor > .CodeMirror {
+    width: calc(100% - 2px);
+    height: auto;
+}
+
+.popover .xhr-breakpoint-content > .editor-wrapper > .editor > .CodeMirror-scroll {
+    width: calc(100% - 2px);
+    overflow: hidden;
+}
diff --git a/Source/WebInspectorUI/UserInterface/Views/XHRBreakpointPopover.js b/Source/WebInspectorUI/UserInterface/Views/XHRBreakpointPopover.js
new file mode 100644 (file)
index 0000000..556eaeb
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2017 Apple 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:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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.
+ */
+
+WebInspector.XHRBreakpointPopover = class XHRBreakpointPopover extends WebInspector.Popover
+{
+    constructor(delegate)
+    {
+        super(delegate);
+
+        this._result = WebInspector.InputPopover.Result.None;
+        this._type = WebInspector.XHRBreakpoint.Type.Text;
+        this._value = null;
+
+        this._codeMirror = null;
+        this._targetElement = null;
+        this._preferredEdges = null;
+
+        this.windowResizeHandler = this._presentOverTargetElement.bind(this);
+    }
+
+    // Public
+
+    get result() { return this._result; }
+    get type() { return this._type; }
+    get value() { return this._value; }
+
+    show(targetElement, preferredEdges)
+    {
+        this._targetElement = targetElement;
+        this._preferredEdges = preferredEdges;
+
+        let contentElement = document.createElement("div");
+        contentElement.classList.add("xhr-breakpoint-content");
+
+        let label = document.createElement("div");
+        label.classList.add("label");
+        label.textContent = WebInspector.UIString("Break on request with URL:");
+
+        let editorWrapper = document.createElement("div");
+        editorWrapper.classList.add("editor-wrapper");
+
+        let selectElement = document.createElement("select");
+
+        function addOption(text, value)
+        {
+            let optionElement = document.createElement("option");
+            optionElement.textContent = text;
+            optionElement.value = value;
+            selectElement.append(optionElement);
+        }
+
+        addOption(WebInspector.UIString("Containing"), WebInspector.XHRBreakpoint.Type.Text);
+        addOption(WebInspector.UIString("Matching"), WebInspector.XHRBreakpoint.Type.RegularExpression);
+
+        selectElement.value = this._type;
+        selectElement.addEventListener("change", (event) => {
+            this._type = event.target.value;
+            this._updateEditor();
+            this._codeMirror.focus();
+        });
+
+        editorWrapper.append(selectElement, this._createEditor());
+        contentElement.append(label, editorWrapper);
+
+        this.content = contentElement;
+
+        this._presentOverTargetElement();
+    }
+
+    // Private
+
+    _createEditor()
+    {
+        let editorElement = document.createElement("div");
+        editorElement.classList.add("editor");
+
+        this._codeMirror = WebInspector.CodeMirrorEditor.create(editorElement, {
+            lineWrapping: false,
+            matchBrackets: false,
+            scrollbarStyle: null,
+            value: "",
+        });
+
+        this._codeMirror.addKeyMap({
+            "Enter": () => {
+                this._result = WebInspector.InputPopover.Result.Committed;
+                this._value = this._codeMirror.getValue().trim();
+                this.dismiss();
+            },
+        });
+
+        this._updateEditor();
+
+        return editorElement;
+    }
+
+    _updateEditor()
+    {
+        let placeholder;
+        let mimeType;
+        if (this._type === WebInspector.XHRBreakpoint.Type.Text) {
+            placeholder = WebInspector.UIString("Text");
+            mimeType = "text/plain";
+        } else {
+            placeholder = WebInspector.UIString("Regular Expression");
+            mimeType = "text/x-regex";
+        }
+
+        this._codeMirror.setOption("mode", mimeType);
+        this._codeMirror.setOption("placeholder", placeholder);
+    }
+
+    _presentOverTargetElement()
+    {
+        if (!this._targetElement)
+            return;
+
+        let targetFrame = WebInspector.Rect.rectFromClientRect(this._targetElement.getBoundingClientRect());
+        this.present(targetFrame, this._preferredEdges);
+
+        // CodeMirror needs a refresh after the popover displays, to layout, otherwise it doesn't appear.
+        setTimeout(() => {
+            this._codeMirror.refresh();
+            this._codeMirror.focus();
+            this.update();
+        }, 0);
+    }
+};
index 544cb83..1dce1f3 100644 (file)
@@ -35,7 +35,10 @@ WebInspector.XHRBreakpointTreeElement = class XHRBreakpointTreeElement extends W
         let subtitle;
         if (!title) {
             title = WebInspector.UIString("URL");
-            subtitle = doubleQuotedString(breakpoint.url);
+            if (breakpoint.type === WebInspector.XHRBreakpoint.Type.Text)
+                subtitle = doubleQuotedString(breakpoint.url);
+            else
+                subtitle = "/" + breakpoint.url + "/";
         }
 
         super(["breakpoint", className], title, subtitle, breakpoint);