Changeset 270604 in webkit


Ignore:
Timestamp:
Dec 9, 2020 2:33:47 PM (20 months ago)
Author:
Devin Rousso
Message:

Web Inspector: add UI for request interception
https://bugs.webkit.org/show_bug.cgi?id=217032
<rdar://problem/69925768>

Reviewed by Brian Burg.

Source/WebInspectorUI:

  • UserInterface/Models/LocalResourceOverride.js:

(WI.LocalResourceOverride):
(WI.LocalResourceOverride.create):
(WI.LocalResourceOverride.displayNameForType): Added.
(WI.LocalResourceOverride.fromJSON):
(WI.LocalResourceOverride.prototype.toJSON):
(WI.LocalResourceOverride.prototype.get url):
(WI.LocalResourceOverride.prototype.get urlComponents): Added.
(WI.LocalResourceOverride.prototype.get displayName): Added.
(WI.LocalResourceOverride.prototype.displayURL): Added.
(WI.LocalResourceOverride.prototype.matches):
(WI.LocalResourceOverride.prototype.equals): Added.
(WI.LocalResourceOverride.prototype.saveIdentityToCookie):
Add a WI.LocalResourceOverride.InterceptType.Request and pass through request information
to the WI.LocalResource.

  • UserInterface/Models/SourceCode.js:

(WI.SourceCode.prototype.get localResourceOverride): Added.

  • UserInterface/Models/Resource.js:

(WI.Resource.classNamesForResource):
(WI.Resource.prototype.get supportsScriptBlackboxing):
(WI.Resource.prototype.async createLocalResourceOverride):
(WI.Resource.prototype.updateLocalResourceOverrideRequestData):
(WI.Resource.prototype.get isLocalResourceOverride): Deleted.

  • UserInterface/Models/LocalResource.js:

(WI.LocalResource.prototype.toJSON):
(WI.LocalResource.prototype.get localResourceOverride): Added.
(WI.LocalResource.prototype.get isLocalResourceOverride): Deleted.
Replace get isLocalResourceOverride with get localResourceOverride (on WI.SourceCode
so that non-WI.Resource also can call it) by having the related WI.LocalResourceOverride
set _localResourceOverride.

  • UserInterface/Controllers/NetworkManager.js:

(WI.NetworkManager.supportsOverridingRequests): Added.
(WI.NetworkManager.prototype.get localResourceOverrides):
(WI.NetworkManager.prototype.addLocalResourceOverride):
(WI.NetworkManager.prototype.removeLocalResourceOverride):
(WI.NetworkManager.prototype.localResourceOverridesForURL): Added.
(WI.NetworkManager.prototype.canBeOverridden):
(WI.NetworkManager.prototype.requestIntercepted):
(WI.NetworkManager.prototype.responseIntercepted):
(WI.NetworkManager.prototype._handleResourceContentChangedForLocalResourceOverride): Added.
(WI.NetworkManager.prototype.localResourceOverrideForURL): Deleted.
(WI.NetworkManager.prototype._handleResourceContentDidChange): Deleted.
Provide all WI.LocalResourceOverride that match the given URL sorted by how close/exact
the WI.LocalResourceOverride is to the given URL (i.e. exact < case-insensitive < regex).
Add logic to invoke Network.interceptWithRequest when the WI.LocalResourceOverride is
WI.LocalResourceOverride.InterceptType.Request.

  • UserInterface/Views/LocalResourceOverridePopover.js:

(WI.LocalResourceOverridePopover):
(WI.LocalResourceOverridePopover.prototype.get serializedData):
(WI.LocalResourceOverridePopover.prototype.show):

  • UserInterface/Views/LocalResourceOverridePopover.css:

(.popover .local-resource-override-popover-content.request .editor:is(.url, .redirect)): Added.
(.popover .local-resource-override-popover-content.response .editor.url): Added.
(.popover .local-resource-override-popover-content .data-grid): Added.
(.popover .local-resource-override-popover-content .reference-page-link-container): Added.
(body[dir=ltr] .popover .local-resource-override-popover-content .reference-page-link-container):
(body[dir=rtl] .popover .local-resource-override-popover-content .reference-page-link-container):
(.popover .local-resource-override-popover-content .editor.url): Deleted.
(.popover .local-resource-override-popover-content .add-header): Deleted.
(.popover .local-resource-override-popover-content .add-header + .reference-page-link-container): Deleted.
Replace the MIME type, status code, and status text inputs with redirect and method inputs
when editing a request override. For brand new overrides, show a type dropdown that allows
for dynamically updating what inputs are shown (only until the popover is dismissed, at
which point the type is set forever).

  • UserInterface/Views/LocalResourceOverrideRequestContentView.js: Added.

(WI.LocalResourceOverrideRequestContentView):
(WI.LocalResourceOverrideRequestContentView.prototype.get navigationItems):
(WI.LocalResourceOverrideRequestContentView.prototype.get saveData):
(WI.LocalResourceOverrideRequestContentView.prototype.initialLayout):
(WI.LocalResourceOverrideRequestContentView.prototype._handleRemoveLocalResourceOverride):
(WI.LocalResourceOverrideRequestContentView.prototype._handleTextEditorContentDidChange):

  • UserInterface/Views/LocalResourceOverrideRequestContentView.css: Added.

(.content-view.text.local-resource-override-request):
(.content-view.text.local-resource-override-request > .text-editor):
(.content-view.text.local-resource-override-request > .message-text-view):

  • UserInterface/Views/ContentView.js:

(WI.ContentView.createFromRepresentedObject):
(WI.ContentView.resolvedRepresentedObjectForRepresentedObject):
Create a special WI.TextContentView for showing WI.LocalResourceOverride request data.
Only allow it to be edited if the current method allows for request data.

  • UserInterface/Views/ResourceContentView.js:

(WI.ResourceContentView):
(WI.ResourceContentView.prototype.get resource):
(WI.ResourceContentView.prototype.requestLocalResourceOverrideInitialContent):
(WI.ResourceContentView.prototype.async _createAndShowLocalResourceOverride):
(WI.ResourceContentView.prototype._populateCreateLocalResourceOverrideContextMenu):
(WI.ResourceContentView.prototype._handleCreateLocalResourceOverride):
(WI.ResourceContentView.prototype._handleImportLocalResourceOverride):
(WI.ResourceContentView.prototype.async _handleRemoveLocalResourceOverride): Added.
(WI.ResourceContentView.prototype._handleLocalResourceOverrideChanged): Added.
(WI.ResourceContentView.prototype.get showingLocalResourceOverride): Deleted.
Show a contextmenu when clicking on the create override navigation item with items for
creating either a request override or a response override.

  • UserInterface/Controllers/CSSManager.js:

(WI.CSSManager.prototype._resourceContentDidChange):

  • UserInterface/Views/ContextMenuUtilities.js:

(WI.appendContextMenuItemsForSourceCode):

  • UserInterface/Views/FontResourceContentView.js:

(WI.FontResourceContentView.prototype.contentAvailable):
(WI.FontResourceContentView.prototype.dropZoneShouldAppearForDragEvent):
(WI.FontResourceContentView.prototype.dropZoneHandleDragEnter):
(WI.FontResourceContentView.prototype.dropZoneHandleDrop):

  • UserInterface/Views/ImageResourceContentView.js:

(WI.ImageResourceContentView.prototype.contentAvailable):
(WI.ImageResourceContentView.prototype.dropZoneShouldAppearForDragEvent):
(WI.ImageResourceContentView.prototype.dropZoneHandleDragEnter):
(WI.ImageResourceContentView.prototype.dropZoneHandleDrop):

  • UserInterface/Views/LocalResourceOverrideLabelView.js:

(WI.LocalResourceOverrideLabelView):
(WI.LocalResourceOverrideLabelView.prototype.initialLayout):

  • UserInterface/Views/LocalResourceOverrideTreeElement.js:

(WI.LocalResourceOverrideTreeElement):
(WI.LocalResourceOverrideTreeElement.prototype.get mainTitleText):
(WI.LocalResourceOverrideTreeElement.prototype.willDismissPopover):

  • UserInterface/Views/LocalResourceOverrideWarningView.js:

(WI.LocalResourceOverrideWarningView):
(WI.LocalResourceOverrideWarningView.prototype.initialLayout):
(WI.LocalResourceOverrideWarningView.prototype._updateContent):
(WI.LocalResourceOverrideWarningView.prototype._handleLocalResourceOverrideAddedOrRemoved):

  • UserInterface/Views/NavigationSidebarPanel.js:

(WI.NavigationSidebarPanel.prototype.pruneStaleResourceTreeElements):

  • UserInterface/Views/OpenResourceDialog.js:

(WI.OpenResourceDialog.prototype._populateResourceTreeOutline):

  • UserInterface/Views/SourceCodeTextEditor.js:

(WI.SourceCodeTextEditor.prototype.get _supportsDebugging):

  • UserInterface/Views/SourcesNavigationSidebarPanel.js:

(WI.SourcesNavigationSidebarPanel.prototype._willDismissLocalOverridePopover):
(WI.SourcesNavigationSidebarPanel.prototype._closeContentViewsFilter):
(WI.SourcesNavigationSidebarPanel.prototype._addLocalOverride):
(WI.SourcesNavigationSidebarPanel.prototype._handleTreeSelectionDidChange):

  • UserInterface/Views/ResourceTreeElement.js:

(WI.ResourceTreeElement.prototype.get mainTitleText):
(WI.ResourceTreeElement.prototype._updateTitles):
(WI.ResourceTreeElement.prototype._updateIcon):

  • UserInterface/Views/TextResourceContentView.js:

(WI.TextResourceContentView):
(WI.TextResourceContentView.prototype.get navigationItems):
(WI.TextResourceContentView.prototype.requestLocalResourceOverrideInitialContent):
(WI.TextResourceContentView.prototype._shouldBeEditable):
Replace usage of get isLocalResourceOverride with get localResourceOverride.

  • UserInterface/Base/HTTPUtilities.js:

Add constants for known request methods.

  • Localizations/en.lproj/localizedStrings.js:
  • UserInterface/Main.html:
  • UserInterface/Test.html:
  • UserInterface/Images/DocumentIcons.svg:
  • UserInterface/Base/ReferencePage.js:

Add url for local overrides page.

LayoutTests:

  • inspector/network/intercept-aborted-request.html: Renamed from LayoutTests/http/tests/inspector/network/intercept-aborted-request.html.
  • inspector/network/intercept-aborted-request-expected.txt: Renamed from LayoutTests/http/tests/inspector/network/intercept-aborted-request-expected.txt.
  • inspector/network/interceptContinue.html: Renamed from LayoutTests/http/tests/inspector/network/intercept-request-continue.html.
  • inspector/network/interceptContinue-expected.txt: Renamed from LayoutTests/http/tests/inspector/network/intercept-request-continue.html.
  • inspector/network/interceptRequestWithError.html: Renamed from LayoutTests/http/tests/inspector/network/intercept-request-subresource-with-error.html.
  • inspector/network/interceptRequestWithError-expected.txt: Renamed from LayoutTests/http/tests/inspector/network/intercept-request-subresource-with-error.html.
  • http/tests/inspector/network/intercept-request-fragment.html:
  • http/tests/inspector/network/intercept-request-main-resource.html:
  • http/tests/inspector/network/intercept-request-main-resource-with-response.html:
  • http/tests/inspector/network/intercept-request-properties.html:
  • http/tests/inspector/network/intercept-request-properties-expected.txt:
  • http/tests/inspector/network/intercept-request-subresource.html:
  • http/tests/inspector/network/intercept-request-subresource-with-response.html:
  • http/tests/inspector/network/intercept-request-with-response.html:
  • http/tests/inspector/network/intercept-request-with-response-expected.txt:
  • http/tests/inspector/network/local-resource-override-basic.html:
  • http/tests/inspector/network/local-resource-override-basic-expected.txt:
  • http/tests/inspector/network/local-resource-override-main-resource.html:
  • http/tests/inspector/network/local-resource-override-script-tag.html:
  • http/tests/inspector/network/resource-response-inspector-override.html:
  • inspector/network/local-resource-override-continue-response.html: Removed.
  • inspector/network/local-resource-override-continue-response-expected.txt: Removed.

Merged into LayoutTests/inspector/network/interceptContinue.html.

  • platform/mac-wk1/TestExpectations:
Location:
trunk
Files:
7 added
8 deleted
45 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r270598 r270604  
     12020-12-09  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: add UI for request interception
     4        https://bugs.webkit.org/show_bug.cgi?id=217032
     5        <rdar://problem/69925768>
     6
     7        Reviewed by Brian Burg.
     8
     9        * inspector/network/intercept-aborted-request.html: Renamed from LayoutTests/http/tests/inspector/network/intercept-aborted-request.html.
     10        * inspector/network/intercept-aborted-request-expected.txt: Renamed from LayoutTests/http/tests/inspector/network/intercept-aborted-request-expected.txt.
     11        * inspector/network/interceptContinue.html: Renamed from LayoutTests/http/tests/inspector/network/intercept-request-continue.html.
     12        * inspector/network/interceptContinue-expected.txt: Renamed from LayoutTests/http/tests/inspector/network/intercept-request-continue.html.
     13        * inspector/network/interceptRequestWithError.html: Renamed from LayoutTests/http/tests/inspector/network/intercept-request-subresource-with-error.html.
     14        * inspector/network/interceptRequestWithError-expected.txt: Renamed from LayoutTests/http/tests/inspector/network/intercept-request-subresource-with-error.html.
     15        * http/tests/inspector/network/intercept-request-fragment.html:
     16        * http/tests/inspector/network/intercept-request-main-resource.html:
     17        * http/tests/inspector/network/intercept-request-main-resource-with-response.html:
     18        * http/tests/inspector/network/intercept-request-properties.html:
     19        * http/tests/inspector/network/intercept-request-properties-expected.txt:
     20        * http/tests/inspector/network/intercept-request-subresource.html:
     21        * http/tests/inspector/network/intercept-request-subresource-with-response.html:
     22        * http/tests/inspector/network/intercept-request-with-response.html:
     23        * http/tests/inspector/network/intercept-request-with-response-expected.txt:
     24        * http/tests/inspector/network/local-resource-override-basic.html:
     25        * http/tests/inspector/network/local-resource-override-basic-expected.txt:
     26        * http/tests/inspector/network/local-resource-override-main-resource.html:
     27        * http/tests/inspector/network/local-resource-override-script-tag.html:
     28        * http/tests/inspector/network/resource-response-inspector-override.html:
     29
     30        * inspector/network/local-resource-override-continue-response.html: Removed.
     31        * inspector/network/local-resource-override-continue-response-expected.txt: Removed.
     32        Merged into LayoutTests/inspector/network/interceptContinue.html.
     33
     34        * platform/mac-wk1/TestExpectations:
     35
    1362020-12-09  John Wilander  <wilander@apple.com>
    237
  • trunk/LayoutTests/http/tests/inspector/network/intercept-request-fragment.html

    r263072 r270604  
    1818    let suite = InspectorTest.createAsyncSuite("Network.interceptWithRequest");
    1919
    20     NetworkAgent.addInterception.invoke({
    21         url: "http://127.0.0.1:8000/inspector/network/resources/stylesheet.css",
    22         stage: InspectorBackend.Enum.Network.NetworkStage.Request,
    23     });
    24 
    2520    function addBaselineTestCase({name, description, expression}) {
    2621        suite.addTestCase({
     
    2924            async test() {
    3025                InspectorTest.log("Triggering load...");
    31                 let [requestEvent, responseEvent, fetchResponse] = await Promise.all([
     26                let [requestEvent, responseEvent] = await Promise.all([
    3227                    WI.Frame.awaitEvent(WI.Frame.Event.ResourceWasAdded),
    3328                    WI.Resource.awaitEvent(WI.Resource.Event.ResponseReceived),
    34                     RuntimeAgent.evaluate(expression),
     29                    InspectorTest.evaluateInPage(expression),
    3530                ]);
    3631                InspectorTest.log("Request URL: " + requestEvent.data.resource.url);
     
    4439            description,
    4540            async test() {
     41                let localResourceOverride = WI.LocalResourceOverride.create("http://127.0.0.1:8000/inspector/network/resources/stylesheet.css", WI.LocalResourceOverride.InterceptType.Request, {
     42                    requestURL: url,
     43                });
     44                WI.networkManager.addLocalResourceOverride(localResourceOverride);
     45
    4646                InspectorTest.log("Triggering load...");
    47                 let [requestEvent, requestInterceptedEvent, fetchResponse] = await Promise.all([
    48                     WI.Frame.awaitEvent(WI.Frame.Event.ResourceWasAdded),
    49                     WI.networkManager.awaitEvent(WI.NetworkManager.Event.RequestIntercepted),
    50                     RuntimeAgent.evaluate(expression),
     47                await Promise.all([
     48                    WI.Frame.awaitEvent(WI.Frame.Event.ResourceWasAdded).then((event) => {
     49                        InspectorTest.log("Request URL: " + event.data.resource.url);
     50                    }),
     51                    WI.Resource.awaitEvent(WI.Resource.Event.ResponseReceived).then((event) => {
     52                        InspectorTest.log("Response URL: " + event.target.url);
     53                    }),
     54                    InspectorTest.evaluateInPage(expression),
    5155                ]);
    52                 InspectorTest.log("Request URL: " + requestEvent.data.resource.url);
    5356
    54                 let [responseEvent, interceptResponse] = await Promise.all([
    55                     WI.Resource.awaitEvent(WI.Resource.Event.ResponseReceived),
    56                     NetworkAgent.interceptWithRequest.invoke({
    57                         requestId: requestInterceptedEvent.data.requestId,
    58                         url,
    59                     }),
    60                 ]);
    61                 InspectorTest.log("Response URL: " + responseEvent.target.url);
     57                WI.networkManager.removeLocalResourceOverride(localResourceOverride);
    6258            }
    6359        });
  • trunk/LayoutTests/http/tests/inspector/network/intercept-request-main-resource-with-response.html

    r263072 r270604  
    1313        description: "Main resource uses request interception on next page load and fulfills request",
    1414        async test() {
    15             await NetworkAgent.addInterception.invoke({
    16                 url: "http://127.0.0.1:8000/inspector/network/intercept-request-main-resource-with-response.html",
    17                 stage: InspectorBackend.Enum.Network.NetworkStage.Request,
     15            let localResourceOverride = WI.LocalResourceOverride.create("http://127.0.0.1:8000/inspector/network/intercept-request-main-resource-with-response.html", WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork, {
     16                responseMIMEType: "text/html",
     17                responseContent: `<!DOCTYPE html><html><head><script src="../resources/inspector-test.js"></`+`script></head><body><p>Overridden page content</p><script>alert("REPLACED HTML CONTENT"); TestPage.completeTest();</`+`script></body></html>`,
     18                responseBase64Encoded: false,
     19                responseStatus: 200,
     20                responseStatusText: "OK",
     21                responseHeaders: {},
    1822            });
     23            WI.networkManager.addLocalResourceOverride(localResourceOverride);
    1924
    20             WI.networkManager.singleFireEventListener(WI.NetworkManager.Event.RequestIntercepted, (event) => {
    21                 let {target, requestId, request} = event.data;
    22                 NetworkAgent.interceptRequestWithResponse.invoke({
    23                     requestId,
    24                     mimeType: "text/html",
    25                     content: `<!DOCTYPE html><html><head><script src="../resources/inspector-test.js"></`+`script></head><body><p>Overridden page content</p><script>alert("REPLACED HTML CONTENT"); TestPage.completeTest();</`+`script></body></html>`,
    26                     base64Encoded: false,
    27                     status: 200,
    28                     statusText: "OK",
    29                     headers: {},
    30                 });
    31             });
    3225            await InspectorTest.reloadPage({ignoreCache: false, revalidateAllResources: true});
    3326        }
  • trunk/LayoutTests/http/tests/inspector/network/intercept-request-main-resource.html

    r263072 r270604  
    1313        description: "Main resource uses request interception on next page load",
    1414        async test() {
    15             await NetworkAgent.addInterception.invoke({
    16                 url: "http://127.0.0.1:8000/inspector/network/intercept-request-main-resource.html",
    17                 stage: InspectorBackend.Enum.Network.NetworkStage.Request,
     15            let localResourceOverride = WI.LocalResourceOverride.create("http://127.0.0.1:8000/inspector/network/intercept-request-main-resource.html", WI.LocalResourceOverride.InterceptType.Request, {
     16                requestURL: "http://127.0.0.1:8000/inspector/network/resources/intercept-request-overriden-page.html",
    1817            });
    19 
    20             WI.networkManager.singleFireEventListener(WI.NetworkManager.Event.RequestIntercepted, (event) => {
    21                 let {target, requestId, request} = event.data;
    22                 NetworkAgent.interceptWithRequest.invoke({
    23                     url: "http://127.0.0.1:8000/inspector/network/resources/intercept-request-overriden-page.html",
    24                     requestId,
    25                 });
    26             });
     18            WI.networkManager.addLocalResourceOverride(localResourceOverride);
    2719
    2820            await InspectorTest.reloadPage({ignoreCache: false, revalidateAllResources: true});
  • trunk/LayoutTests/http/tests/inspector/network/intercept-request-properties-expected.txt

    r263072 r270604  
    5959Triggering load...
    6060Request details:
    61   The URL can’t be shown
     61  URI: /inspector/network/resources/intercept-echo.php
     62  Response URL: http://127.0.0.1:8000/inspector/network/resources/intercept-echo.php
     63  Method: GET
     64  Request Headers:
    6265
    6366-- Running test case: Network.interceptRequest.Headers
  • trunk/LayoutTests/http/tests/inspector/network/intercept-request-properties.html

    r263072 r270604  
    3636{
    3737    let suite = InspectorTest.createAsyncSuite("Network.interceptWithRequest");
    38 
    39     NetworkAgent.addInterception.invoke({
    40         url: "http://127.0.0.1:8000/inspector/network/resources/intercept-echo.php",
    41         stage: InspectorBackend.Enum.Network.NetworkStage.Request,
    42     });
    4338
    4439    function logRequest(result) {
     
    8378    }
    8479
    85     function addTestCase({name, description, expression, overrides}) {
     80    function addTestCase({name, description, expression, override}) {
    8681        suite.addTestCase({
    8782            name,
    8883            description,
    8984            async test() {
     85                let localResourceOverride = WI.LocalResourceOverride.create("http://127.0.0.1:8000/inspector/network/resources/intercept-echo.php", WI.LocalResourceOverride.InterceptType.Request, override);
     86                WI.networkManager.addLocalResourceOverride(localResourceOverride);
     87
    9088                InspectorTest.log("Triggering load...");
    91                 let [requestInterceptedEvent, fetchResponse] = await Promise.all([
    92                     WI.networkManager.awaitEvent(WI.NetworkManager.Event.RequestIntercepted),
    93                     RuntimeAgent.evaluate(expression),
    94                 ]);
    95 
    96                 await NetworkAgent.interceptWithRequest.invoke({
    97                     requestId: requestInterceptedEvent.data.requestId,
    98                     ...overrides,
    99                 });
    100 
    101                 let response = await RuntimeAgent.awaitPromise(fetchResponse.result.objectId, true);
     89                let response = await InspectorTest.evaluateInPage(expression);
     90                response = await RuntimeAgent.awaitPromise(response.objectId, true);
    10291                InspectorTest.log("Request details:");
    10392                logRequest(response.result);
     93
     94                WI.networkManager.removeLocalResourceOverride(localResourceOverride);
    10495            }
    10596        });
     
    110101        description: "Tests request method interception",
    111102        expression: "fetchData('resources/intercept-echo.php')",
    112         overrides: { method: "POST" },
     103        override: { requestMethod: "POST" },
    113104    });
    114105
     
    117108        description: "Tests request method interception with DELETE",
    118109        expression: "fetchData('resources/intercept-echo.php')",
    119         overrides: { method: "DELETE" },
     110        override: { requestMethod: "DELETE" },
    120111    });
    121112
     
    124115        description: "Tests request method interception with NONSTANDARD",
    125116        expression: "fetchData('resources/intercept-echo.php')",
    126         overrides: { method: "NONSTANDARD" },
     117        override: { requestMethod: "NONSTANDARD" },
    127118    });
    128119
     
    131122        description: "Tests request method interception with empty string",
    132123        expression: "fetchData('resources/intercept-echo.php')",
    133         overrides: { method: "" },
     124        override: { requestMethod: "" },
    134125    });
    135126
     
    138129        description: "Tests request method interception with different URL",
    139130        expression: "fetchData('resources/intercept-echo.php')",
    140         overrides: { url: "http://127.0.0.1:8000/inspector/network/resources/intercept-echo.php?newURL=value" },
     131        override: { requestURL: "http://127.0.0.1:8000/inspector/network/resources/intercept-echo.php?newURL=value" },
    141132    });
    142133
     
    145136        description: "Tests request method interception with URL with fragment",
    146137        expression: "fetchData('resources/intercept-echo.php')",
    147         overrides: { url: "http://127.0.0.1:8000/inspector/network/resources/intercept-echo.php#fragment" },
     138        override: { requestURL: "http://127.0.0.1:8000/inspector/network/resources/intercept-echo.php#fragment" },
    148139    });
    149140
     
    152143        description: "Tests request method interception with empty URL",
    153144        expression: "fetchData('resources/intercept-echo.php')",
    154         overrides: { url: "" },
     145        override: { requestURL: "" },
    155146    });
    156147
     
    159150        description: "Tests request headers interception",
    160151        expression: "fetchData('resources/intercept-echo.php')",
    161         overrides: { headers: { "X-Value": "overridden" } },
     152        override: { requestHeaders: { "X-Value": "overridden" } },
    162153    });
    163154
     
    166157        description: "Tests request post data interception",
    167158        expression: "postData('resources/intercept-echo.php')",
    168         overrides: {
    169             method: "POST",
    170             postData: btoa("value=overridden"),
     159        override: {
     160            requestMethod: "POST",
     161            requestHeaders: {
     162                "Content-Type": "application/x-www-form-urlencoded",
     163                "Content-Length": 16,
     164            },
     165            requestData: "value=overridden",
    171166        },
    172167    });
  • trunk/LayoutTests/http/tests/inspector/network/intercept-request-subresource-with-response.html

    r263072 r270604  
    1313        description: "Subresource uses request interception with response",
    1414        async test() {
    15             await NetworkAgent.addInterception.invoke({
    16                 url: "http://127.0.0.1:8000/inspector/network/resources/override.js",
    17                 stage: InspectorBackend.Enum.Network.NetworkStage.Request,
     15            let localResourceOverride = WI.LocalResourceOverride.create("http://127.0.0.1:8000/inspector/network/resources/override.js", WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork, {
     16                responseMIMEType: "text/javascript",
     17                responseContent: `alert("OVERRIDDEN override.js TEXT"); TestPage.dispatchEventToFrontend("OverrideContentDidLoad");`,
     18                responseBase64Encoded: false,
     19                responseStatus: 200,
     20                responseStatusText: "OK",
     21                responseHeaders: {},
    1822            });
     23            WI.networkManager.addLocalResourceOverride(localResourceOverride);
    1924
    20             WI.networkManager.singleFireEventListener(WI.NetworkManager.Event.RequestIntercepted, (event) => {
    21                 let {target, requestId, request} = event.data;
    22                 NetworkAgent.interceptRequestWithResponse.invoke({
    23                     requestId,
    24                     mimeType: "text/javascript",
    25                     content: `alert("OVERRIDDEN override.js TEXT"); TestPage.dispatchEventToFrontend("OverrideContentDidLoad");`,
    26                     base64Encoded: false,
    27                     status: 200,
    28                     statusText: "OK",
    29                     headers: {},
    30                 });
    31             });
     25            await Promise.all([
     26                InspectorTest.awaitEvent("OverrideContentDidLoad"),
     27                InspectorTest.reloadPage({ignoreCache: false, revalidateAllResources: true}),
     28            ]);
    3229
    33             await InspectorTest.reloadPage({ignoreCache: false, revalidateAllResources: true});
    34             await InspectorTest.awaitEvent("OverrideContentDidLoad");
     30            WI.networkManager.removeLocalResourceOverride(localResourceOverride);
    3531        }
    3632    });
  • trunk/LayoutTests/http/tests/inspector/network/intercept-request-subresource.html

    r263072 r270604  
    1313        description: "Subresource uses request interception",
    1414        async test() {
    15             await NetworkAgent.addInterception.invoke({
    16                 url: "http://127.0.0.1:8000/inspector/network/resources/override.js",
    17                 stage: InspectorBackend.Enum.Network.NetworkStage.Request,
     15            let localResourceOverride = WI.LocalResourceOverride.create("http://127.0.0.1:8000/inspector/network/resources/override.js", WI.LocalResourceOverride.InterceptType.Request, {
     16                requestURL: "http://127.0.0.1:8000/inspector/network/resources/intercept-request-overriden-script.js",
    1817            });
    19             WI.networkManager.singleFireEventListener(WI.NetworkManager.Event.RequestIntercepted, (event) => {
    20                 let {target, requestId, request} = event.data;
    21                 NetworkAgent.interceptWithRequest.invoke({
    22                     url: "http://127.0.0.1:8000/inspector/network/resources/intercept-request-overriden-script.js",
    23                     requestId,
    24                 });
    25             });
    26             await InspectorTest.reloadPage({ignoreCache: false, revalidateAllResources: true});
    27             await InspectorTest.awaitEvent("OverrideContentDidLoad");
     18            WI.networkManager.addLocalResourceOverride(localResourceOverride);
     19
     20            await Promise.all([
     21                InspectorTest.awaitEvent("OverrideContentDidLoad"),
     22                InspectorTest.reloadPage({ignoreCache: false, revalidateAllResources: true}),
     23            ]);
     24
     25            WI.networkManager.removeLocalResourceOverride(localResourceOverride);
    2826        }
    2927    });
  • trunk/LayoutTests/http/tests/inspector/network/intercept-request-with-response-expected.txt

    r263072 r270604  
    55== Running test suite: Network.interceptRequestWithResponse
    66-- Running test case: Network.interceptRequestWithResponse.Text
     7Creating Local Resource Override for: http://127.0.0.1:8000/inspector/network/resources/override.txt
    78Triggering load...
    8 Response details:
     9Resource Loaded:
    910  URL: http://127.0.0.1:8000/inspector/network/resources/override.txt
    1011  MIME Type: text/plain
    1112  Status: 987 Override Status Text
     13  Response Source: Symbol(inspector-override)
    1214  Response Headers:
    13     content-type: text/plain
    14     x-override-header-1: Override-Header-Value-1
    15     x-override-header-2: Override-Header-Value-2
     15    Content-Type: text/plain
     16    X-Override-Header-1: Override-Header-Value-1
     17    X-Override-Header-2: Override-Header-Value-2
    1618  Content: PASS - OVERRIDDEN TEXT
    1719
    1820-- Running test case: Network.interceptRequestWithResponse.JavaScript
     21Creating Local Resource Override for: http://127.0.0.1:8000/inspector/network/resources/override.txt
    1922Triggering load...
    20 Response details:
     23Resource Loaded:
    2124  URL: http://127.0.0.1:8000/inspector/network/resources/override.txt
    2225  MIME Type: application/javascript
    2326  Status: 200 Super OK
     27  Response Source: Symbol(inspector-override)
    2428  Response Headers:
    25     content-type: application/javascript
    26     x-custom-header: Header value
     29    Content-Type: application/javascript
     30    X-Custom-Header: Header value
    2731  Content: /* PASS */ (function() { /* OVERRIDDEN */ })();
    2832
    2933-- Running test case: Network.interceptRequestWithResponse.Image
     34Creating Local Resource Override for: http://127.0.0.1:8000/inspector/network/resources/override.txt
    3035Triggering load...
    31 Response details:
     36Resource Loaded:
    3237  URL: http://127.0.0.1:8000/inspector/network/resources/override.txt
    3338  MIME Type: image/png
    3439  Status: 200 OK
     40  Response Source: Symbol(inspector-override)
    3541  Response Headers:
    36     content-type: image/png
    37   Content: <data>
     42    Content-Type: image/png
     43  Content: [base64] PGRhdGE+
    3844
    3945-- Running test case: Network.interceptRequestWithResponse.URL.QueryString
     46Creating Local Resource Override for: http://127.0.0.1:8000/inspector/network/resources/override.txt
    4047Triggering load...
    41 Response details:
     48Resource Loaded:
    4249  URL: http://127.0.0.1:8000/inspector/network/resources/override.txt?s=2
    4350  MIME Type: text/plain
    4451  Status: 200 OK
     52  Response Source: <not-InspectorOverride>
    4553  Response Headers:
    46     content-type: text/plain
    47     x-expected: PASS
     54    Accept-Ranges: <filtered>
     55    Connection: <filtered>
     56    Content-Length: 29
     57    Content-Type: text/plain
     58    Date: <filtered>
     59    ETag: <filtered>
     60    Keep-Alive: <filtered>
     61    Last-Modified: <filtered>
     62    Server: <filtered>
     63  Content: default override.txt content
     64
     65
     66-- Running test case: Network.interceptRequestWithResponse.URL.Fragment
     67Creating Local Resource Override for: http://127.0.0.1:8000/inspector/network/resources/override.txt
     68Triggering load...
     69Resource Loaded:
     70  URL: http://127.0.0.1:8000/inspector/network/resources/override.txt#frag
     71  MIME Type: text/plain
     72  Status: 200 OK
     73  Response Source: Symbol(inspector-override)
     74  Response Headers:
     75    Content-Type: text/plain
     76    X-Expected: PASS
    4877  Content: PASS
    4978
    50 -- Running test case: Network.interceptRequestWithResponse.URL.Fragment
     79-- Running test case: Network.interceptRequestWithResponse.URL.CaseSensitive
     80Creating Local Resource Override for: http://127.0.0.1:8000/inspector/network/resources/override.txt?case=sensitive
    5181Triggering load...
    52 Response details:
    53   URL: http://127.0.0.1:8000/inspector/network/resources/override.txt
     82Resource Loaded:
     83  URL: http://127.0.0.1:8000/inspector/network/resources/override.txt?CaSe=SeNsItIvE
    5484  MIME Type: text/plain
    5585  Status: 200 OK
     86  Response Source: Symbol(inspector-override)
    5687  Response Headers:
    57     content-type: text/plain
    58     x-expected: PASS
     88    Content-Type: text/plain
     89    X-Expected: PASS
     90  Content: PASS
     91
     92-- Running test case: Network.interceptRequestWithResponse.URL.IsRegex
     93Creating Local Resource Override for: \/override\.txt\?t=\d+
     94Triggering load...
     95Resource Loaded:
     96  URL: http://127.0.0.1:8000/inspector/network/resources/override.txt?t=123456789
     97  MIME Type: text/plain
     98  Status: 200 OK
     99  Response Source: Symbol(inspector-override)
     100  Response Headers:
     101    Content-Type: text/plain
     102    X-Expected: PASS
     103  Content: PASS
     104
     105-- Running test case: Network.interceptRequestWithResponse.URL.IsCaseSensitiveRegex
     106Creating Local Resource Override for: \/OvErRiDe\.TxT\?t=\d+
     107Triggering load...
     108Resource Loaded:
     109  URL: http://127.0.0.1:8000/inspector/network/resources/override.txt?t=123456789
     110  MIME Type: text/plain
     111  Status: 200 OK
     112  Response Source: Symbol(inspector-override)
     113  Response Headers:
     114    Content-Type: text/plain
     115    X-Expected: PASS
    59116  Content: PASS
    60117
    61118-- Running test case: Network.interceptRequestWithResponse.404
     119Creating Local Resource Override for: http://127.0.0.1:8000/inspector/network/resources/override.txt
    62120Triggering load...
    63 Response details:
     121Resource Loaded:
    64122  URL: http://127.0.0.1:8000/inspector/network/resources/override.txt
    65123  MIME Type: text/plain
    66124  Status: 404 Not Found
     125  Response Source: Symbol(inspector-override)
    67126  Response Headers:
    68     content-type: text/plain
    69     x-expected: PASS
     127    Content-Type: text/plain
     128    X-Expected: PASS
    70129  Content: PASS
    71130
  • trunk/LayoutTests/http/tests/inspector/network/intercept-request-with-response.html

    r263072 r270604  
    55<script src="../resources/inspector-test.js"></script>
    66<script>
    7 
    8 async function triggerOverrideLoad(urlSuffix) {
     7function triggerOverrideLoad(urlSuffix) {
    98    let url = "http://127.0.0.1:8000/inspector/network/resources/override.txt";
    109    if (urlSuffix)
    1110        url += urlSuffix;
    12 
    13     try {
    14         let response = await fetch(url);
    15         return {
    16             url: response.url,
    17             text: await response.text(),
    18             status: response.status,
    19             statusText: response.statusText,
    20             headers: Array.from(response.headers),
    21         };
    22     } catch (e) {
    23         return { error: e.message };
    24     }
     11    fetch(url).then(() => {
     12        TestPage.dispatchEventToFrontend("LoadComplete");
     13    });
    2514}
    2615
     
    2817{
    2918    let suite = InspectorTest.createAsyncSuite("Network.interceptRequestWithResponse");
    30    
    31     NetworkAgent.addInterception.invoke({
    32         url: ".*override\.txt.*",
    33         isRegex: true,
    34         stage: InspectorBackend.Enum.Network.NetworkStage.Request,
    35     });
    36 
    37     function logResponse(response) {
    38         response.headers = new Map(response.headers);
    39         InspectorTest.log(`  URL: ${response.url}`);
    40         InspectorTest.log(`  MIME Type: ${response.headers.get("content-type")}`);
    41         InspectorTest.log(`  Status: ${response.status} ${response.statusText}`);
     19
     20    async function logResource(resource) {
     21        let responseSource = resource.responseSource === WI.Resource.ResponseSource.InspectorOverride ? String(resource.responseSource) : "<not-InspectorOverride>";
     22        InspectorTest.log(`  URL: ${resource.url}`);
     23        InspectorTest.log(`  MIME Type: ${resource.mimeType}`);
     24        InspectorTest.log(`  Status: ${resource.statusCode} ${resource.statusText}`);
     25        InspectorTest.log(`  Response Source: ${responseSource}`);
    4226        InspectorTest.log(`  Response Headers:`);
    43         let keys = Array.from(response.headers.keys());
     27        let keys = Object.keys(resource.responseHeaders);
    4428        keys.sort();
    4529        for (let name of keys) {
    46             let value = response.headers.get(name);
    47             if (!name.startsWith("x-") && !name.startsWith("content-"))
    48                 continue;
     30            let value = resource.responseHeaders[name];
     31            if (!name.startsWith("X-") && !name.startsWith("Content-"))
     32                value = "<filtered>";
    4933            InspectorTest.log(`    ${name}: ${value}`);
    5034        }
    51         InspectorTest.log(`  Content: ${response.text}`);
     35
     36        let {rawContent, rawBase64Encoded} = await resource.requestContent();
     37        InspectorTest.log(`  Content: ${rawBase64Encoded ? "[base64] " : ""}${rawContent}`);
    5238    }
    5339
    54     function addTestCase({name, description, expression, responseData}) {
     40    function addTestCase({name, description, expression, overrides}) {
    5541        suite.addTestCase({
    5642            name,
    5743            description,
    5844            async test() {
     45                let localResourceOverrides = overrides.map((override) => {
     46                    InspectorTest.log("Creating Local Resource Override for: " + override.url);
     47                    let localResourceOverride = WI.LocalResourceOverride.create(override.url, WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork, override);
     48                    WI.networkManager.addLocalResourceOverride(localResourceOverride);
     49                    return localResourceOverride;
     50                });
     51
    5952                InspectorTest.log("Triggering load...");
    60                 let [requestInterceptedEvent, fetchResponse] = await Promise.all([
    61                     WI.networkManager.awaitEvent(WI.NetworkManager.Event.RequestIntercepted),
    62                     RuntimeAgent.evaluate(expression),
     53                let [resourceWasAddedEvent, responseReceivedEvent, loadCompleteEvent] = await Promise.all([
     54                    WI.Frame.awaitEvent(WI.Frame.Event.ResourceWasAdded),
     55                    WI.Resource.awaitEvent(WI.Resource.Event.ResponseReceived),
     56                    InspectorTest.awaitEvent("LoadComplete"),
     57                    InspectorTest.evaluateInPage(expression),
    6358                ]);
    6459
    65                 await NetworkAgent.interceptRequestWithResponse.invoke({
    66                     requestId: requestInterceptedEvent.data.requestId,
    67                     ...responseData,
    68                 });
    69 
    70                 let response = await RuntimeAgent.awaitPromise(fetchResponse.result.objectId, true);
    71                 InspectorTest.log("Response details:");
    72                 logResponse(response.result.value);
     60                InspectorTest.log("Resource Loaded:");
     61                let resource = resourceWasAddedEvent.data.resource;
     62                await logResource(resource);
     63
     64                for (let localResourceOverride of localResourceOverrides)
     65                    WI.networkManager.removeLocalResourceOverride(localResourceOverride);
    7366            }
    7467        });
     
    7972        description: "Intercept request with text content.",
    8073        expression: `triggerOverrideLoad()`,
    81         responseData: {
    82             content: `PASS - OVERRIDDEN TEXT`,
    83             base64Encoded: false,
    84             mimeType: "text/plain",
    85             status: 987,
    86             statusText: "Override Status Text",
    87             headers: {
     74        overrides: [{
     75            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
     76            responseMIMEType: "text/plain",
     77            responseContent: `PASS - OVERRIDDEN TEXT`,
     78            responseBase64Encoded: false,
     79            responseStatusCode: 987,
     80            responseStatusText: "Override Status Text",
     81            responseHeaders: {
    8882                "X-Override-Header-1": "Override-Header-Value-1",
    8983                "X-Override-Header-2": "Override-Header-Value-2",
    9084            },
    91         },
     85        }],
    9286    });
    9387
     
    9690        description: "Intercept request with javascript content.",
    9791        expression: `triggerOverrideLoad()`,
    98         responseData: {
    99             content: `/* PASS */ (function() { /* OVERRIDDEN */ })();`,
    100             base64Encoded: false,
    101             mimeType: "application/javascript",
    102             status: 200,
    103             statusText: "Super OK",
    104             headers: {
     92        overrides: [{
     93            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
     94            responseMIMEType: "application/javascript",
     95            responseContent: `/* PASS */ (function() { /* OVERRIDDEN */ })();`,
     96            responseBase64Encoded: false,
     97            responseStatusCode: 200,
     98            responseStatusText: "Super OK",
     99            responseHeaders: {
    105100                "X-Custom-Header": "Header value",
    106101            },
    107         },
     102        }],
    108103    });
    109104
     
    112107        description: "Intercept request with image content.",
    113108        expression: `triggerOverrideLoad()`,
    114         responseData: {
    115             content: btoa("<data>"),
    116             base64Encoded: true,
    117             mimeType: "image/png",
    118             status: 200,
    119             statusText: "OK",
    120             headers: {},
    121         },
     109        overrides: [{
     110            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
     111            responseMIMEType: "image/png",
     112            responseContent: btoa("<data>"),
     113            responseBase64Encoded: true,
     114            responseStatusCode: 200,
     115            responseStatusText: "OK",
     116            responseHeaders: {},
     117        }],
    122118    });
    123119
     
    126122        description: "Test overrides with different query strings.",
    127123        expression: `triggerOverrideLoad("?s=2")`,
    128         responseData: {
    129             base64Encoded: false,
    130             content: "PASS",
    131             mimeType: "text/plain",
    132             status: 200,
    133             statusText: "OK",
    134             headers: {"X-Expected": "PASS"},
    135         },
     124        overrides: [{
     125            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
     126            responseMIMEType: "text/plain",
     127            responseCase64Encoded: false,
     128            responseBontent: "PASS",
     129            responseStatusCode: 200,
     130            responseStatusText: "OK",
     131            responseHeaders: {"X-Expected": "PASS"},
     132        }],
    136133    });
    137134
     
    140137        description: "Test override for a load with a fragment.",
    141138        expression: `triggerOverrideLoad("#frag")`,
    142         responseData: {
    143             content: "PASS",
    144             base64Encoded: false,
    145             mimeType: "text/plain",
    146             status: 200,
    147             statusText: "OK",
    148             headers: {"X-Expected": "PASS"},
    149         },
     139        overrides: [{
     140            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
     141            responseMIMEType: "text/plain",
     142            responseContent: "PASS",
     143            responseBase64Encoded: false,
     144            responseStatusCode: 200,
     145            responseStatusText: "OK",
     146            responseHeaders: {"X-Expected": "PASS"},
     147        }],
     148    });
     149
     150    addTestCase({
     151        name: "Network.interceptRequestWithResponse.URL.CaseSensitive",
     152        description: "Test override for a load with a fragment.",
     153        expression: `triggerOverrideLoad("?CaSe=SeNsItIvE")`,
     154        overrides: [{
     155            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt?case=sensitive",
     156            responseMIMEType: "text/plain",
     157            responseContent: "PASS",
     158            responseBase64Encoded: false,
     159            responseStatusCode: 200,
     160            responseStatusText: "OK",
     161            responseHeaders: {"X-Expected": "PASS"},
     162            isCaseSensitive: false,
     163        }]
     164    });
     165
     166    addTestCase({
     167        name: "Network.interceptRequestWithResponse.URL.IsRegex",
     168        description: "Test override for a load with a fragment.",
     169        expression: `triggerOverrideLoad("?t=123456789")`,
     170        overrides: [{
     171            url: "\\/override\\.txt\\?t=\\d+",
     172            responseMIMEType: "text/plain",
     173            responseContent: "PASS",
     174            responseBase64Encoded: false,
     175            responseStatusCode: 200,
     176            responseStatusText: "OK",
     177            responseHeaders: {"X-Expected": "PASS"},
     178            isRegex: true,
     179        }]
     180    });
     181
     182    addTestCase({
     183        name: "Network.interceptRequestWithResponse.URL.IsCaseSensitiveRegex",
     184        description: "Test override for a load with a fragment.",
     185        expression: `triggerOverrideLoad("?t=123456789")`,
     186        overrides: [{
     187            url: "\\/OvErRiDe\\.TxT\\?t=\\d+",
     188            responseMIMEType: "text/plain",
     189            responseContent: "PASS",
     190            responseBase64Encoded: false,
     191            responseStatusCode: 200,
     192            responseStatusText: "OK",
     193            responseHeaders: {"X-Expected": "PASS"},
     194            isCaseSensitive: false,
     195            isRegex: true,
     196        }]
    150197    });
    151198
     
    154201        description: "Test for a 404 override.",
    155202        expression: `triggerOverrideLoad()`,
    156         responseData: {
    157             content: "PASS",
    158             base64Encoded: false,
    159             mimeType: "text/plain",
    160             status: 404,
    161             statusText: "Not Found",
    162             headers: {"X-Expected": "PASS"},
    163         },
     203        overrides: [{
     204            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
     205            responseMIMEType: "text/plain",
     206            responseContent: "PASS",
     207            responseBase64Encoded: false,
     208            responseStatusCode: 404,
     209            responseStatusText: "Not Found",
     210            responseHeaders: {"X-Expected": "PASS"},
     211        }],
    164212    });
    165213
  • trunk/LayoutTests/http/tests/inspector/network/local-resource-override-basic-expected.txt

    r252614 r270604  
    198198-- Running test teardown.
    199199
    200 -- Running test case: LocalResourceOverride.URL.Fragment
    201 PASS: LocalResourceOverride creation should strip fragments.
    202 
  • trunk/LayoutTests/http/tests/inspector/network/local-resource-override-basic.html

    r267723 r270604  
    4646                for (let override of overrides) {
    4747                    InspectorTest.log("Creating Local Resource Override for: " + override.url);
    48                     let localResourceOverride = WI.LocalResourceOverride.create(WI.LocalResourceOverride.InterceptType.Response, override);
     48                    let localResourceOverride = WI.LocalResourceOverride.create(override.url, WI.LocalResourceOverride.InterceptType.Response, override);
    4949                    WI.networkManager.addLocalResourceOverride(localResourceOverride);
    5050                    localResourceOverrides.push(localResourceOverride);
     
    8383        overrides: [{
    8484            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
    85             mimeType: "text/plain",
    86             content: `PASS - OVERRIDDEN TEXT`,
    87             base64Encoded: false,
    88             statusCode: 987,
    89             statusText: "Override Status Text",
    90             headers: {
     85            responseMIMEType: "text/plain",
     86            responseContent: `PASS - OVERRIDDEN TEXT`,
     87            responseBase64Encoded: false,
     88            responseStatusCode: 987,
     89            responseStatusText: "Override Status Text",
     90            responseHeaders: {
    9191                "X-Override-Header-1": "Override-Header-Value-1",
    9292                "X-Override-Header-2": "Override-Header-Value-2",
     
    101101        overrides: [{
    102102            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
    103             mimeType: "application/javascript",
    104             content: `/* PASS */ (function() { /* OVERRIDDEN */ })();`,
    105             base64Encoded: false,
    106             statusCode: 200,
    107             statusText: "Super OK",
    108             headers: {
     103            responseMIMEType: "application/javascript",
     104            responseContent: `/* PASS */ (function() { /* OVERRIDDEN */ })();`,
     105            responseBase64Encoded: false,
     106            responseStatusCode: 200,
     107            responseStatusText: "Super OK",
     108            responseHeaders: {
    109109                "X-Custom-Header": "Header value",
    110110            },
     
    118118        overrides: [{
    119119            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
    120             mimeType: "image/png",
    121             content: btoa("<data>"),
    122             base64Encoded: true,
    123             statusCode: 200,
    124             statusText: "OK",
    125             headers: {},
     120            responseMIMEType: "image/png",
     121            responseContent: btoa("<data>"),
     122            responseBase64Encoded: true,
     123            responseStatusCode: 200,
     124            responseStatusText: "OK",
     125            responseHeaders: {},
    126126        }]
    127127    });
     
    133133        overrides: [{
    134134            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt?s=1",
    135             mimeType: "text/plain",
    136             content: "FAIL",
    137             base64Encoded: false,
    138             statusCode: 500,
    139             statusText: "FAIL",
    140             headers: {"X-Expected": "FAIL"},
     135            responseMIMEType: "text/plain",
     136            responseContent: "FAIL",
     137            responseBase64Encoded: false,
     138            responseStatusCode: 500,
     139            responseStatusText: "FAIL",
     140            responseHeaders: {"X-Expected": "FAIL"},
    141141        }, {
    142142            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt?s=2",
    143             mimeType: "text/plain",
    144             content: "PASS",
    145             base64Encoded: false,
    146             statusCode: 200,
    147             statusText: "OK",
    148             headers: {"X-Expected": "PASS"},
     143            responseMIMEType: "text/plain",
     144            responseContent: "PASS",
     145            responseBase64Encoded: false,
     146            responseStatusCode: 200,
     147            responseStatusText: "OK",
     148            responseHeaders: {"X-Expected": "PASS"},
    149149        }]
    150150    });
     
    156156        overrides: [{
    157157            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
    158             mimeType: "text/plain",
    159             content: "PASS",
    160             base64Encoded: false,
    161             statusCode: 200,
    162             statusText: "OK",
    163             headers: {"X-Expected": "PASS"},
     158            responseMIMEType: "text/plain",
     159            responseContent: "PASS",
     160            responseBase64Encoded: false,
     161            responseStatusCode: 200,
     162            responseStatusText: "OK",
     163            responseHeaders: {"X-Expected": "PASS"},
    164164        }]
    165165    });
     
    171171        overrides: [{
    172172            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt?case=sensitive",
    173             mimeType: "text/plain",
    174             content: "PASS",
    175             base64Encoded: false,
    176             statusCode: 200,
    177             statusText: "OK",
    178             headers: {"X-Expected": "PASS"},
     173            responseMIMEType: "text/plain",
     174            responseContent: "PASS",
     175            responseBase64Encoded: false,
     176            responseStatusCode: 200,
     177            responseStatusText: "OK",
     178            responseHeaders: {"X-Expected": "PASS"},
    179179            isCaseSensitive: false,
    180180        }]
     
    187187        overrides: [{
    188188            url: "\\/override\\.txt\\?t=\\d+",
    189             mimeType: "text/plain",
    190             content: "PASS",
    191             base64Encoded: false,
    192             statusCode: 200,
    193             statusText: "OK",
    194             headers: {"X-Expected": "PASS"},
     189            responseMIMEType: "text/plain",
     190            responseContent: "PASS",
     191            responseBase64Encoded: false,
     192            responseStatusCode: 200,
     193            responseStatusText: "OK",
     194            responseHeaders: {"X-Expected": "PASS"},
    195195            isRegex: true,
    196196        }]
     
    203203        overrides: [{
    204204            url: "\\/OvErRiDe\\.TxT\\?t=\\d+",
    205             mimeType: "text/plain",
    206             content: "PASS",
    207             base64Encoded: false,
    208             statusCode: 200,
    209             statusText: "OK",
    210             headers: {"X-Expected": "PASS"},
     205            responseMIMEType: "text/plain",
     206            responseContent: "PASS",
     207            responseBase64Encoded: false,
     208            responseStatusCode: 200,
     209            responseStatusText: "OK",
     210            responseHeaders: {"X-Expected": "PASS"},
    211211            isCaseSensitive: false,
    212212            isRegex: true,
     
    220220        overrides: [{
    221221            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
    222             mimeType: "text/plain",
    223             content: "PASS",
    224             base64Encoded: false,
    225             statusCode: 404,
    226             statusText: "Not Found",
    227             headers: {"X-Expected": "PASS"},
     222            responseMIMEType: "text/plain",
     223            responseContent: "PASS",
     224            responseBase64Encoded: false,
     225            responseStatusCode: 404,
     226            responseStatusText: "Not Found",
     227            responseHeaders: {"X-Expected": "PASS"},
    228228            disabled: false,
    229229        }]
     
    236236        overrides: [{
    237237            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
    238             mimeType: "text/plain",
    239             content: "PASS",
    240             base64Encoded: false,
    241             statusCode: 200,
    242             statusText: "OK",
    243             headers: {"X-Expected": "PASS"},
     238            responseMIMEType: "text/plain",
     239            responseContent: "PASS",
     240            responseBase64Encoded: false,
     241            responseStatusCode: 200,
     242            responseStatusText: "OK",
     243            responseHeaders: {"X-Expected": "PASS"},
    244244            disabled: false,
    245245        }]
     
    252252        overrides: [{
    253253            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
    254             mimeType: "text/plain",
    255             content: "FAIL",
    256             base64Encoded: false,
    257             statusCode: 500,
    258             statusText: "FAIL",
    259             headers: {"X-Expected": "FAIL"},
     254            responseMIMEType: "text/plain",
     255            responseContent: "FAIL",
     256            responseBase64Encoded: false,
     257            responseStatusCode: 500,
     258            responseStatusText: "FAIL",
     259            responseHeaders: {"X-Expected": "FAIL"},
    260260            disabled: true,
    261261        }]
     
    270270        overrides: [{
    271271            url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
    272             mimeType: "text/plain",
    273             content: "FAIL",
    274             base64Encoded: false,
    275             statusCode: 500,
    276             statusText: "FAIL",
    277             headers: {"X-Expected": "FAIL"},
    278         }]
    279     });
    280 
    281     suite.addTestCase({
    282         name: "LocalResourceOverride.URL.Fragment",
    283         description: "LocalResourceOverride creation strips a fragment",
    284         async test() {
    285             let localResourceOverride = WI.LocalResourceOverride.create(WI.LocalResourceOverride.InterceptType.Response, {
    286                 url: "http://127.0.0.1:8000/inspector/network/resources/override.txt#test",
    287                 mimeType: "text/plain",
    288                 content: "OVERRIDDEN TEXT",
    289                 base64Encoded: false,
    290                 statusCode: 200,
    291                 statusText: "OK",
    292                 headers: {},
    293             });
    294 
    295             InspectorTest.expectEqual(localResourceOverride.localResource.url, "http://127.0.0.1:8000/inspector/network/resources/override.txt", "LocalResourceOverride creation should strip fragments.");
    296         }
     272            responseMIMEType: "text/plain",
     273            responseContent: "FAIL",
     274            responseBase64Encoded: false,
     275            responseStatusCode: 500,
     276            responseStatusText: "FAIL",
     277            responseHeaders: {"X-Expected": "FAIL"},
     278        }]
    297279    });
    298280
  • trunk/LayoutTests/http/tests/inspector/network/local-resource-override-main-resource.html

    r267723 r270604  
    1313        description: "Main resource uses override content on next page load",
    1414        async test() {
    15             WI.networkManager.addLocalResourceOverride(WI.LocalResourceOverride.create(WI.LocalResourceOverride.InterceptType.Response, {
    16                 url: "http://127.0.0.1:8000/inspector/network/local-resource-override-main-resource.html",
    17                 mimeType: "text/html",
    18                 content: `<!DOCTYPE html><html><head><script src="../resources/inspector-test.js"></`+`script></head><body><p>Overridden page content</p><script>alert("REPLACED HTML CONTENT"); TestPage.completeTest();</`+`script></body></html>`,
    19                 base64Encoded: false,
    20                 statusCode: 200,
    21                 statusText: "OK",
    22                 headers: {},
     15            WI.networkManager.addLocalResourceOverride(WI.LocalResourceOverride.create("http://127.0.0.1:8000/inspector/network/local-resource-override-main-resource.html", WI.LocalResourceOverride.InterceptType.Response, {
     16                responseMIMEType: "text/html",
     17                responseContent: `<!DOCTYPE html><html><head><script src="../resources/inspector-test.js"></`+`script></head><body><p>Overridden page content</p><script>alert("REPLACED HTML CONTENT"); TestPage.completeTest();</`+`script></body></html>`,
     18                responseBase64Encoded: false,
     19                responseStatusCode: 200,
     20                responseStatusText: "OK",
     21                responseHeaders: {},
    2322            }));
    2423
  • trunk/LayoutTests/http/tests/inspector/network/local-resource-override-script-tag.html

    r267723 r270604  
    1313        description: "<script> load uses override content on next page load",
    1414        async test() {
    15             WI.networkManager.addLocalResourceOverride(WI.LocalResourceOverride.create(WI.LocalResourceOverride.InterceptType.Response, {
    16                 url: "http://127.0.0.1:8000/inspector/network/resources/override.js",
    17                 mimeType: "text/javascript",
    18                 content: `alert("OVERRIDDEN override.js TEXT"); TestPage.dispatchEventToFrontend("OverrideContentDidLoad");`,
    19                 base64Encoded: false,
    20                 statusCode: 200,
    21                 statusText: "OK",
    22                 headers: {},
     15            WI.networkManager.addLocalResourceOverride(WI.LocalResourceOverride.create("http://127.0.0.1:8000/inspector/network/resources/override.js", WI.LocalResourceOverride.InterceptType.Response, {
     16                responseMIMEType: "text/javascript",
     17                responseContent: `alert("OVERRIDDEN override.js TEXT"); TestPage.dispatchEventToFrontend("OverrideContentDidLoad");`,
     18                responseBase64Encoded: false,
     19                responseStatusCode: 200,
     20                responseStatusText: "OK",
     21                responseHeaders: {},
    2322            }));
    2423
  • trunk/LayoutTests/http/tests/inspector/network/resource-response-inspector-override.html

    r267723 r270604  
    4343        statusCode: 987,
    4444        async setup() {
    45             WI.networkManager.addLocalResourceOverride(WI.LocalResourceOverride.create(WI.LocalResourceOverride.InterceptType.Response, {
    46                 url: "http://127.0.0.1:8000/inspector/network/resources/override.txt",
    47                 mimeType: "text/plain",
    48                 content: "Overridden Text",
    49                 base64Encoded: false,
    50                 statusCode: 987,
    51                 statusText: "Status Text",
    52                 headers: {},
     45            WI.networkManager.addLocalResourceOverride(WI.LocalResourceOverride.create("http://127.0.0.1:8000/inspector/network/resources/override.txt", WI.LocalResourceOverride.InterceptType.Response, {
     46                responseMIMEType: "text/plain",
     47                responseContent: "Overridden Text",
     48                responseBase64Encoded: false,
     49                responseStatusCode: 987,
     50                responseStatusText: "Status Text",
     51                responseHeaders: {},
    5352            }));
    5453        }
  • trunk/LayoutTests/platform/mac-wk1/TestExpectations

    r270397 r270604  
    594594http/tests/performance/paint-timing [ Skip ]
    595595
    596 # Local Overrides not available in WebKit1
    597 http/tests/inspector/network/local-resource-override-basic.html [ Failure ]
    598 http/tests/inspector/network/local-resource-override-main-resource.html [ Failure ]
    599 http/tests/inspector/network/local-resource-override-script-tag.html [ Failure ]
    600 http/tests/inspector/network/resource-response-inspector-override.html [ Failure ]
    601 inspector/network/local-resource-override-continue-response.html [ Skip ]
    602 
    603 # Request interception is not available in WebKit1
    604 http/tests/inspector/network/intercept-aborted-request.html [ Skip ]
    605 http/tests/inspector/network/intercept-request-continue.html [ Skip ]
     596# Network interception is not available in WebKit1
    606597http/tests/inspector/network/intercept-request-fragment.html [ Skip ]
    607598http/tests/inspector/network/intercept-request-main-resource.html [ Skip ]
     
    609600http/tests/inspector/network/intercept-request-properties.html [ Skip ]
    610601http/tests/inspector/network/intercept-request-subresource.html [ Skip ]
    611 http/tests/inspector/network/intercept-request-subresource-with-error.html [ Skip ]
    612602http/tests/inspector/network/intercept-request-subresource-with-response.html [ Skip ]
    613603http/tests/inspector/network/intercept-request-with-response.html [ Skip ]
     604http/tests/inspector/network/local-resource-override-basic.html [ Skip ]
     605http/tests/inspector/network/local-resource-override-main-resource.html [ Skip ]
     606http/tests/inspector/network/local-resource-override-script-tag.html [ Skip ]
     607http/tests/inspector/network/resource-response-inspector-override.html [ Skip ]
     608inspector/network/intercept-aborted-request.html [ Skip ]
     609inspector/network/interceptContinue.html [ Skip ]
     610inspector/network/interceptRequestWithError.html [ Skip ]
    614611
    615612webkit.org/b/164933 http/tests/misc/link-rel-icon-beforeload.html [ Failure ]
  • trunk/Source/WebInspectorUI/ChangeLog

    r270445 r270604  
     12020-12-09  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: add UI for request interception
     4        https://bugs.webkit.org/show_bug.cgi?id=217032
     5        <rdar://problem/69925768>
     6
     7        Reviewed by Brian Burg.
     8
     9        * UserInterface/Models/LocalResourceOverride.js:
     10        (WI.LocalResourceOverride):
     11        (WI.LocalResourceOverride.create):
     12        (WI.LocalResourceOverride.displayNameForType): Added.
     13        (WI.LocalResourceOverride.fromJSON):
     14        (WI.LocalResourceOverride.prototype.toJSON):
     15        (WI.LocalResourceOverride.prototype.get url):
     16        (WI.LocalResourceOverride.prototype.get urlComponents): Added.
     17        (WI.LocalResourceOverride.prototype.get displayName): Added.
     18        (WI.LocalResourceOverride.prototype.displayURL): Added.
     19        (WI.LocalResourceOverride.prototype.matches):
     20        (WI.LocalResourceOverride.prototype.equals): Added.
     21        (WI.LocalResourceOverride.prototype.saveIdentityToCookie):
     22        Add a `WI.LocalResourceOverride.InterceptType.Request` and pass through request information
     23        to the `WI.LocalResource`.
     24
     25        * UserInterface/Models/SourceCode.js:
     26        (WI.SourceCode.prototype.get localResourceOverride): Added.
     27        * UserInterface/Models/Resource.js:
     28        (WI.Resource.classNamesForResource):
     29        (WI.Resource.prototype.get supportsScriptBlackboxing):
     30        (WI.Resource.prototype.async createLocalResourceOverride):
     31        (WI.Resource.prototype.updateLocalResourceOverrideRequestData):
     32        (WI.Resource.prototype.get isLocalResourceOverride): Deleted.
     33        * UserInterface/Models/LocalResource.js:
     34        (WI.LocalResource.prototype.toJSON):
     35        (WI.LocalResource.prototype.get localResourceOverride): Added.
     36        (WI.LocalResource.prototype.get isLocalResourceOverride): Deleted.
     37        Replace `get isLocalResourceOverride` with `get localResourceOverride` (on `WI.SourceCode`
     38        so that non-`WI.Resource` also can call it) by having the related `WI.LocalResourceOverride`
     39        set `_localResourceOverride`.
     40
     41        * UserInterface/Controllers/NetworkManager.js:
     42        (WI.NetworkManager.supportsOverridingRequests): Added.
     43        (WI.NetworkManager.prototype.get localResourceOverrides):
     44        (WI.NetworkManager.prototype.addLocalResourceOverride):
     45        (WI.NetworkManager.prototype.removeLocalResourceOverride):
     46        (WI.NetworkManager.prototype.localResourceOverridesForURL): Added.
     47        (WI.NetworkManager.prototype.canBeOverridden):
     48        (WI.NetworkManager.prototype.requestIntercepted):
     49        (WI.NetworkManager.prototype.responseIntercepted):
     50        (WI.NetworkManager.prototype._handleResourceContentChangedForLocalResourceOverride): Added.
     51        (WI.NetworkManager.prototype.localResourceOverrideForURL): Deleted.
     52        (WI.NetworkManager.prototype._handleResourceContentDidChange): Deleted.
     53        Provide all `WI.LocalResourceOverride` that match the given URL sorted by how close/exact
     54        the `WI.LocalResourceOverride` is to the given URL (i.e. exact < case-insensitive < regex).
     55        Add logic to invoke `Network.interceptWithRequest` when the `WI.LocalResourceOverride` is
     56        `WI.LocalResourceOverride.InterceptType.Request`.
     57
     58        * UserInterface/Views/LocalResourceOverridePopover.js:
     59        (WI.LocalResourceOverridePopover):
     60        (WI.LocalResourceOverridePopover.prototype.get serializedData):
     61        (WI.LocalResourceOverridePopover.prototype.show):
     62        * UserInterface/Views/LocalResourceOverridePopover.css:
     63        (.popover .local-resource-override-popover-content.request .editor:is(.url, .redirect)): Added.
     64        (.popover .local-resource-override-popover-content.response .editor.url): Added.
     65        (.popover .local-resource-override-popover-content .data-grid): Added.
     66        (.popover .local-resource-override-popover-content .reference-page-link-container): Added.
     67        (body[dir=ltr] .popover .local-resource-override-popover-content .reference-page-link-container):
     68        (body[dir=rtl] .popover .local-resource-override-popover-content .reference-page-link-container):
     69        (.popover .local-resource-override-popover-content .editor.url): Deleted.
     70        (.popover .local-resource-override-popover-content .add-header): Deleted.
     71        (.popover .local-resource-override-popover-content .add-header + .reference-page-link-container): Deleted.
     72        Replace the MIME type, status code, and status text inputs with redirect and method inputs
     73        when editing a request override. For brand new overrides, show a type dropdown that allows
     74        for dynamically updating what inputs are shown (only until the popover is dismissed, at
     75        which point the type is set forever).
     76
     77        * UserInterface/Views/LocalResourceOverrideRequestContentView.js: Added.
     78        (WI.LocalResourceOverrideRequestContentView):
     79        (WI.LocalResourceOverrideRequestContentView.prototype.get navigationItems):
     80        (WI.LocalResourceOverrideRequestContentView.prototype.get saveData):
     81        (WI.LocalResourceOverrideRequestContentView.prototype.initialLayout):
     82        (WI.LocalResourceOverrideRequestContentView.prototype._handleRemoveLocalResourceOverride):
     83        (WI.LocalResourceOverrideRequestContentView.prototype._handleTextEditorContentDidChange):
     84        * UserInterface/Views/LocalResourceOverrideRequestContentView.css: Added.
     85        (.content-view.text.local-resource-override-request):
     86        (.content-view.text.local-resource-override-request > .text-editor):
     87        (.content-view.text.local-resource-override-request > .message-text-view):
     88        * UserInterface/Views/ContentView.js:
     89        (WI.ContentView.createFromRepresentedObject):
     90        (WI.ContentView.resolvedRepresentedObjectForRepresentedObject):
     91        Create a special `WI.TextContentView` for showing `WI.LocalResourceOverride` request data.
     92        Only allow it to be edited if the current method allows for request data.
     93
     94        * UserInterface/Views/ResourceContentView.js:
     95        (WI.ResourceContentView):
     96        (WI.ResourceContentView.prototype.get resource):
     97        (WI.ResourceContentView.prototype.requestLocalResourceOverrideInitialContent):
     98        (WI.ResourceContentView.prototype.async _createAndShowLocalResourceOverride):
     99        (WI.ResourceContentView.prototype._populateCreateLocalResourceOverrideContextMenu):
     100        (WI.ResourceContentView.prototype._handleCreateLocalResourceOverride):
     101        (WI.ResourceContentView.prototype._handleImportLocalResourceOverride):
     102        (WI.ResourceContentView.prototype.async _handleRemoveLocalResourceOverride): Added.
     103        (WI.ResourceContentView.prototype._handleLocalResourceOverrideChanged): Added.
     104        (WI.ResourceContentView.prototype.get showingLocalResourceOverride): Deleted.
     105        Show a contextmenu when clicking on the create override navigation item with items for
     106        creating either a request override or a response override.
     107
     108        * UserInterface/Controllers/CSSManager.js:
     109        (WI.CSSManager.prototype._resourceContentDidChange):
     110        * UserInterface/Views/ContextMenuUtilities.js:
     111        (WI.appendContextMenuItemsForSourceCode):
     112        * UserInterface/Views/FontResourceContentView.js:
     113        (WI.FontResourceContentView.prototype.contentAvailable):
     114        (WI.FontResourceContentView.prototype.dropZoneShouldAppearForDragEvent):
     115        (WI.FontResourceContentView.prototype.dropZoneHandleDragEnter):
     116        (WI.FontResourceContentView.prototype.dropZoneHandleDrop):
     117        * UserInterface/Views/ImageResourceContentView.js:
     118        (WI.ImageResourceContentView.prototype.contentAvailable):
     119        (WI.ImageResourceContentView.prototype.dropZoneShouldAppearForDragEvent):
     120        (WI.ImageResourceContentView.prototype.dropZoneHandleDragEnter):
     121        (WI.ImageResourceContentView.prototype.dropZoneHandleDrop):
     122        * UserInterface/Views/LocalResourceOverrideLabelView.js:
     123        (WI.LocalResourceOverrideLabelView):
     124        (WI.LocalResourceOverrideLabelView.prototype.initialLayout):
     125        * UserInterface/Views/LocalResourceOverrideTreeElement.js:
     126        (WI.LocalResourceOverrideTreeElement):
     127        (WI.LocalResourceOverrideTreeElement.prototype.get mainTitleText):
     128        (WI.LocalResourceOverrideTreeElement.prototype.willDismissPopover):
     129        * UserInterface/Views/LocalResourceOverrideWarningView.js:
     130        (WI.LocalResourceOverrideWarningView):
     131        (WI.LocalResourceOverrideWarningView.prototype.initialLayout):
     132        (WI.LocalResourceOverrideWarningView.prototype._updateContent):
     133        (WI.LocalResourceOverrideWarningView.prototype._handleLocalResourceOverrideAddedOrRemoved):
     134        * UserInterface/Views/NavigationSidebarPanel.js:
     135        (WI.NavigationSidebarPanel.prototype.pruneStaleResourceTreeElements):
     136        * UserInterface/Views/OpenResourceDialog.js:
     137        (WI.OpenResourceDialog.prototype._populateResourceTreeOutline):
     138        * UserInterface/Views/SourceCodeTextEditor.js:
     139        (WI.SourceCodeTextEditor.prototype.get _supportsDebugging):
     140        * UserInterface/Views/SourcesNavigationSidebarPanel.js:
     141        (WI.SourcesNavigationSidebarPanel.prototype._willDismissLocalOverridePopover):
     142        (WI.SourcesNavigationSidebarPanel.prototype._closeContentViewsFilter):
     143        (WI.SourcesNavigationSidebarPanel.prototype._addLocalOverride):
     144        (WI.SourcesNavigationSidebarPanel.prototype._handleTreeSelectionDidChange):
     145        * UserInterface/Views/ResourceTreeElement.js:
     146        (WI.ResourceTreeElement.prototype.get mainTitleText):
     147        (WI.ResourceTreeElement.prototype._updateTitles):
     148        (WI.ResourceTreeElement.prototype._updateIcon):
     149        * UserInterface/Views/TextResourceContentView.js:
     150        (WI.TextResourceContentView):
     151        (WI.TextResourceContentView.prototype.get navigationItems):
     152        (WI.TextResourceContentView.prototype.requestLocalResourceOverrideInitialContent):
     153        (WI.TextResourceContentView.prototype._shouldBeEditable):
     154        Replace usage of `get isLocalResourceOverride` with `get localResourceOverride`.
     155
     156        * UserInterface/Base/HTTPUtilities.js:
     157        Add constants for known request methods.
     158
     159        * Localizations/en.lproj/localizedStrings.js:
     160        * UserInterface/Main.html:
     161        * UserInterface/Test.html:
     162        * UserInterface/Images/DocumentIcons.svg:
     163
     164        * UserInterface/Base/ReferencePage.js:
     165        Add url for local overrides page.
     166
    11672020-12-04  Adam Roben  <aroben@apple.com>
    2168
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r269712 r270604  
    5353localizedStrings["%s (%s)"] = "%s (%s)";
    5454localizedStrings["%s (%s, %s)"] = "%s (%s, %s)";
    55 localizedStrings["%s (Case Insensitive)"] = "%s (Case Insensitive)";
     55/* Label for case-insensitive URL match pattern of a local override. */
     56localizedStrings["%s (Case Insensitive) @ Local Override"] = "%s (Case Insensitive)";
    5657localizedStrings["%s (default)"] = "%s (default)";
    5758localizedStrings["%s (hidden)"] = "%s (hidden)";
     
    6061localizedStrings["%s Fired"] = "%s Fired";
    6162localizedStrings["%s Prototype"] = "%s Prototype";
     63/* Format string for the suggested filename when saving the content for a request local override. */
     64localizedStrings["%s Request Data @ Local Override Request Content View"] = "%s Request Data";
    6265localizedStrings["%s Result"] = "%s Result";
    6366localizedStrings["%s \u2013 %s"] = "%s \u2013 %s";
     
    6871localizedStrings["%s eval\n%s async"] = "%s eval\n%s async";
    6972localizedStrings["%s interval"] = "%s interval";
     73localizedStrings["%s requests do not have a body"] = "%s requests do not have a body";
    7074localizedStrings["%s total"] = "%s total";
    7175localizedStrings["%s transferred"] = "%s transferred";
     
    9296localizedStrings["1 match"] = "1 match";
    9397localizedStrings["1080p"] = "1080p";
     98localizedStrings["2D"] = "2D";
    9499localizedStrings["720p"] = "720p";
    95 localizedStrings["Screen size:"] = "Screen size:";
    96 localizedStrings["2D"] = "2D";
    97100localizedStrings["Accessibility"] = "Accessibility";
    98101localizedStrings["Action"] = "Action";
     
    367370localizedStrings["Create Breakpoint"] = "Create Breakpoint";
    368371localizedStrings["Create Local Override"] = "Create Local Override";
     372localizedStrings["Create Request Local Override"] = "Create Request Local Override";
    369373localizedStrings["Create Resource"] = "Create Resource";
     374localizedStrings["Create Response Local Override"] = "Create Response Local Override";
    370375localizedStrings["Create audit:"] = "Create audit:";
    371376localizedStrings["Cross-Origin Restrictions"] = "Cross-Origin Restrictions";
     
    808813localizedStrings["Lowest: %s"] = "Lowest: %s";
    809814localizedStrings["MIME Type"] = "MIME Type";
     815/* Label for MIME type input for the local override currently being edited. */
     816localizedStrings["MIME Type @ Local Override Popover"] = "MIME Type";
    810817localizedStrings["MIME Type:"] = "MIME Type:";
    811818localizedStrings["MSE Logging:"] = "MSE Logging:";
     
    936943localizedStrings["Over 1 ms"] = "Over 1 ms";
    937944localizedStrings["Over 15 ms"] = "Over 15 ms";
    938 localizedStrings["Override"] = "Override";
    939945localizedStrings["Overview"] = "Overview";
    940946localizedStrings["Owns"] = "Owns";
     
    10271033localizedStrings["Recording Warning: %s"] = "Recording Warning: %s";
    10281034localizedStrings["Recordings"] = "Recordings";
     1035localizedStrings["Redirect"] = "Redirect";
    10291036localizedStrings["Redirect Response"] = "Redirect Response";
    10301037localizedStrings["Redirects"] = "Redirects";
     
    10521059localizedStrings["Request (DOM Tree)"] = "Request (DOM Tree)";
    10531060localizedStrings["Request (Object Tree)"] = "Request (Object Tree)";
     1061/* Text indicating that the local override intercepts the request phase of network activity. */
     1062localizedStrings["Request @ Local Override Type"] = "Request";
    10541063localizedStrings["Request Cookies"] = "Request Cookies";
    10551064localizedStrings["Request Data"] = "Request Data";
    10561065localizedStrings["Request Headers"] = "Request Headers";
     1066/* Label indicating that the shown content is from a request local override. */
     1067localizedStrings["Request Override @ Local Override Content View"] = "Request Override";
    10571068localizedStrings["Requesting: %s"] = "Requesting: %s";
    10581069localizedStrings["Required"] = "Required";
     
    10751086localizedStrings["Response (Object Tree)"] = "Response (Object Tree)";
    10761087localizedStrings["Response (Text)"] = "Response (Text)";
     1088/* Text indicating that the local override will skip all network activity and instead immediately serve the response. */
     1089localizedStrings["Response (skip network) @ Local Override Type"] = "Response (skip network)";
     1090/* Text indicating that the local override intercepts the response phase of network activity. */
     1091localizedStrings["Response @ Local Override Type"] = "Response";
    10771092localizedStrings["Response Cookies"] = "Response Cookies";
    10781093localizedStrings["Response Headers"] = "Response Headers";
     1094/* Label indicating that the shown content is from a response local override. */
     1095localizedStrings["Response Override @ Local Override Content View"] = "Response Override";
    10791096localizedStrings["Response:"] = "Response:";
    10801097localizedStrings["Restart (%s)"] = "Restart (%s)";
     
    11241141localizedStrings["Scope Chain"] = "Scope Chain";
    11251142localizedStrings["Screen Shot %s-%s-%s at %s.%s.%s"] = "Screen Shot %s-%s-%s at %s.%s.%s";
     1143localizedStrings["Screen size:"] = "Screen size:";
    11261144localizedStrings["Script"] = "Script";
    11271145localizedStrings["Script Element %d"] = "Script Element %d";
     
    12551273localizedStrings["Statistics"] = "Statistics";
    12561274localizedStrings["Status"] = "Status";
     1275/* Label for the HTTP status code input for the local override currently being edited. */
     1276localizedStrings["Status @ Local Override Popover"] = "Status";
    12571277localizedStrings["Step"] = "Step";
    12581278localizedStrings["Step (%s or %s)"] = "Step (%s or %s)";
  • trunk/Source/WebInspectorUI/UserInterface/Base/HTTPUtilities.js

    r249504 r270604  
    8787    }
    8888};
     89
     90WI.HTTPUtilities.RequestMethod = {
     91    CONNECT: "CONNECT",
     92    DELETE: "DELETE",
     93    GET: "GET",
     94    HEAD: "HEAD",
     95    OPTIONS: "OPTIONS",
     96    PATCH: "PATCH",
     97    POST: "POST",
     98    PUT: "PUT",
     99    TRACE: "TRACE",
     100};
     101
     102WI.HTTPUtilities.RequestMethodsWithBody = new Set([
     103    WI.HTTPUtilities.RequestMethod.DELETE,
     104    WI.HTTPUtilities.RequestMethod.PATCH,
     105    WI.HTTPUtilities.RequestMethod.POST,
     106    WI.HTTPUtilities.RequestMethod.PUT,
     107]);
  • trunk/Source/WebInspectorUI/UserInterface/Base/ReferencePage.js

    r266480 r270604  
    2828    EventBreakpoints: "event-breakpoints",
    2929    JavaScriptBreakpoints: "javascript-breakpoints",
     30    LocalOverrides: "local-overrides",
    3031    URLBreakpoints: "url-breakpoints",
    3132};
  • trunk/Source/WebInspectorUI/UserInterface/Controllers/CSSManager.js

    r266885 r270604  
    570570
    571571        // Ignore changes to resource overrides, those are not live on the page.
    572         if (resource.isLocalResourceOverride)
     572        if (resource.localResourceOverride)
    573573            return;
    574574
  • trunk/Source/WebInspectorUI/UserInterface/Controllers/NetworkManager.js

    r267723 r270604  
    4444        this._downloadingSourceMaps = new Set;
    4545
    46         this._localResourceOverrides = new Set;
     46        this._localResourceOverrides = [];
    4747        this._harImportLocalResourceMap = new Set;
    4848
     
    5959
    6060        if (NetworkManager.supportsOverridingResponses()) {
    61             WI.Resource.addEventListener(WI.SourceCode.Event.ContentDidChange, this._handleResourceContentDidChange, this);
     61            WI.Resource.addEventListener(WI.SourceCode.Event.ContentDidChange, this._handleResourceContentChangedForLocalResourceOverride, this);
     62            WI.Resource.addEventListener(WI.Resource.Event.RequestDataDidChange, this._handleResourceContentChangedForLocalResourceOverride, this);
    6263            WI.LocalResourceOverride.addEventListener(WI.LocalResourceOverride.Event.DisabledChanged, this._handleResourceOverrideDisabledChanged, this);
    6364
     
    7172                    let supported = false;
    7273                    switch (localResourceOverride.type) {
     74                    case WI.LocalResourceOverride.InterceptType.Request:
     75                        supported = WI.NetworkManager.supportsOverridingRequests();
     76                        break;
     77
    7378                    case WI.LocalResourceOverride.InterceptType.Response:
    7479                        supported = WI.NetworkManager.supportsOverridingResponses();
     
    111116    }
    112117
     118    static supportsOverridingRequests()
     119    {
     120        // COMPATIBILITY (iOS 13.4): Network.interceptWithRequest did not exist yet.
     121        return InspectorBackend.hasCommand("Network.interceptWithRequest");
     122    }
     123
    113124    static supportsOverridingRequestsWithResponses()
    114125    {
     
    201212
    202213    get mainFrame() { return this._mainFrame; }
     214    get localResourceOverrides() { return this._localResourceOverrides; }
    203215    get bootstrapScript() { return this._bootstrapScript; }
    204216
     
    206218    {
    207219        return Array.from(this._frameIdentifierMap.values());
    208     }
    209 
    210     get localResourceOverrides()
    211     {
    212         return Array.from(this._localResourceOverrides);
    213220    }
    214221
     
    373380        console.assert(localResourceOverride instanceof WI.LocalResourceOverride);
    374381
    375         console.assert(!this._localResourceOverrides.has(localResourceOverride), "Already had an existing local resource override.");
    376         this._localResourceOverrides.add(localResourceOverride);
     382        console.assert(!this._localResourceOverrides.includes(localResourceOverride));
     383        this._localResourceOverrides.push(localResourceOverride);
    377384
    378385        if (!this._restoringLocalResourceOverrides)
     
    389396        console.assert(localResourceOverride instanceof WI.LocalResourceOverride);
    390397
    391         if (!this._localResourceOverrides.delete(localResourceOverride)) {
     398        if (!this._localResourceOverrides.remove(localResourceOverride)) {
    392399            console.assert(false, "Attempted to remove a local resource override that was not known.");
    393400            return;
     
    406413    }
    407414
    408     localResourceOverrideForURL(url)
    409     {
    410         for (let localResourceOverride of this._localResourceOverrides) {
    411             if (localResourceOverride.matches(url))
    412                 return localResourceOverride;
    413         }
    414         return null;
     415    localResourceOverridesForURL(url)
     416    {
     417        // Order local resource overrides based on how closely they match the given URL. As an example,
     418        // a regular expression is likely going to match more URLs than a case-insensitive string.
     419        const rankFunctions = [
     420            (localResourceOverride) => localResourceOverride.isCaseSensitive && !localResourceOverride.isRegex,  // exact match
     421            (localResourceOverride) => !localResourceOverride.isCaseSensitive && !localResourceOverride.isRegex, // case-insensitive
     422            (localResourceOverride) => localResourceOverride.isCaseSensitive && localResourceOverride.isRegex,   // case-sensitive regex
     423            (localResourceOverride) => !localResourceOverride.isCaseSensitive && localResourceOverride.isRegex,  // case-insensitive regex
     424        ];
     425        return this._localResourceOverrides
     426            .filter((localResourceOverride) => localResourceOverride.matches(url))
     427            .sort((a, b) => {
     428                let aRank = rankFunctions.findIndex((rankFunction) => rankFunction(a));
     429                let bRank = rankFunctions.findIndex((rankFunction) => rankFunction(b));
     430                return aRank - bRank;
     431            });
    415432    }
    416433
     
    423440            return false;
    424441
    425         if (resource.isLocalResourceOverride)
     442        if (resource.localResourceOverride)
    426443            return false;
    427444
     
    430447            return false;
    431448
    432         let existingOverride = this.localResourceOverrideForURL(resource.url);
    433         if (existingOverride)
     449        if (this.localResourceOverridesForURL(resource.url).length)
    434450            return false;
    435451
     
    933949    requestIntercepted(target, requestId, request)
    934950    {
    935         if (window.InspectorTest) {
    936             // FIXME: <https://webkit.org/b/217032> Web Inspector: add UI for request interception
    937             this.dispatchEventToListeners(WI.NetworkManager.Event.RequestIntercepted, {target, requestId, request});
    938             return;
    939         }
    940 
    941951        let url = WI.urlWithoutFragment(request.url);
    942         let localResourceOverride = this.localResourceOverrideForURL(url);
    943         if (!localResourceOverride || localResourceOverride.disabled) {
    944             target.NetworkAgent.interceptContinue.invoke({
    945                 requestId,
    946                 stage: InspectorBackend.Enum.Network.NetworkStage.Request,
    947             });
    948             return;
    949         }
    950 
    951         console.assert(localResourceOverride.type === WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork, localResourceOverride);
    952 
    953         let localResource = localResourceOverride.localResource;
    954         let revision = localResource.currentRevision;
    955 
    956         console.assert(revision.mimeType === localResource.mimeType);
    957 
    958         target.NetworkAgent.interceptRequestWithResponse.invoke({
     952        for (let localResourceOverride of this.localResourceOverridesForURL(url)) {
     953            if (localResourceOverride.disabled)
     954                continue;
     955
     956            let localResource = localResourceOverride.localResource;
     957            let revision = localResource.currentRevision;
     958
     959            switch (localResourceOverride.type) {
     960            case WI.LocalResourceOverride.InterceptType.Request: {
     961                target.NetworkAgent.interceptWithRequest.invoke({
     962                    requestId,
     963                    url: localResource.url || undefined,
     964                    method: localResource.requestMethod ?? undefined,
     965                    headers: localResource.requestHeaders,
     966                    postData: (WI.HTTPUtilities.RequestMethodsWithBody.has(localResource.requestMethod) && localResource.requestData) ? btoa(localResource.requestData) : undefined,
     967                });
     968                return;
     969            }
     970
     971            case WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork:
     972                console.assert(revision.mimeType === localResource.mimeType);
     973                target.NetworkAgent.interceptRequestWithResponse.invoke({
     974                    requestId,
     975                    content: revision.content,
     976                    base64Encoded: !!revision.base64Encoded,
     977                    mimeType: revision.mimeType ?? undefined,
     978                    status: !isNaN(localResource.statusCode) ? localResource.statusCode : 200,
     979                    statusText: !isNaN(localResource.statusCode) ? (localResource.statusText ?? "") : WI.HTTPUtilities.statusTextForStatusCode(200),
     980                    headers: localResource.responseHeaders,
     981                });
     982                return;
     983            }
     984        }
     985
     986        // It's possible for a response regex override to overlap a request regex override, in
     987        // which case we should silently continue the request if the response regex override was
     988        // used instead (e.g. it was added first).
     989        target.NetworkAgent.interceptContinue.invoke({
    959990            requestId,
    960             content: revision.content,
    961             base64Encoded: !!revision.base64Encoded,
    962             mimeType: revision.mimeType,
    963             status: !isNaN(localResource.statusCode) ? localResource.statusCode : 200,
    964             statusText: localResource.statusText ?? WI.HTTPUtilities.statusTextForStatusCode(200),
    965             headers: localResource.responseHeaders ?? {},
     991            stage: InspectorBackend.Enum.Network.NetworkStage.Request,
    966992        });
    967993    }
     
    970996    {
    971997        let url = WI.urlWithoutFragment(response.url);
    972         let localResourceOverride = this.localResourceOverrideForURL(url);
    973         if (!localResourceOverride || localResourceOverride.disabled) {
    974             target.NetworkAgent.interceptContinue.invoke({
    975                 requestId,
    976                 stage: InspectorBackend.Enum.Network.NetworkStage.Response,
    977             });
    978             return;
    979         }
    980 
    981         console.assert(localResourceOverride.type === WI.LocalResourceOverride.InterceptType.Response, localResourceOverride);
    982 
    983         let localResource = localResourceOverride.localResource;
    984         let revision = localResource.currentRevision;
    985 
    986         let content = revision.content;
    987         let base64Encoded = revision.base64Encoded;
    988         let mimeType = revision.mimeType;
    989         let statusCode = localResource.statusCode;
    990         let statusText = localResource.statusText;
    991         let responseHeaders = localResource.responseHeaders;
    992         console.assert(revision.mimeType === localResource.mimeType);
    993 
    994         if (isNaN(statusCode))
    995             statusCode = undefined;
    996         if (!statusText)
    997             statusText = undefined;
    998         if (!responseHeaders)
    999             responseHeaders = undefined;
    1000 
    1001         target.NetworkAgent.interceptWithResponse(requestId, content, base64Encoded, mimeType, statusCode, statusText, responseHeaders);
     998        for (let localResourceOverride of this.localResourceOverridesForURL(url)) {
     999            if (localResourceOverride.disabled)
     1000                continue;
     1001
     1002            let localResource = localResourceOverride.localResource;
     1003            let revision = localResource.currentRevision;
     1004
     1005            switch (localResourceOverride.type) {
     1006            case WI.LocalResourceOverride.InterceptType.Response:
     1007                console.assert(revision.mimeType === localResource.mimeType);
     1008                target.NetworkAgent.interceptWithResponse.invoke({
     1009                    requestId,
     1010                    content: revision.content,
     1011                    base64Encoded: !!revision.base64Encoded,
     1012                    mimeType: revision.mimeType ?? undefined,
     1013                    status: !isNaN(localResource.statusCode) ? localResource.statusCode : undefined,
     1014                    statusText: !isNaN(localResource.statusCode) ? (localResource.statusText ?? "") : undefined,
     1015                    headers: localResource.responseHeaders,
     1016                });
     1017                return;
     1018            }
     1019        }
     1020
     1021        // It's possible for a request regex override to overlap a response regex override, in
     1022        // which case we should silently continue the response if the request regex override was
     1023        // used instead (e.g. it was added first).
     1024        target.NetworkAgent.interceptContinue.invoke({
     1025            requestId,
     1026            stage: InspectorBackend.Enum.Network.NetworkStage.Response,
     1027        });
    10021028    }
    10031029
     
    14421468    }
    14431469
    1444     _handleResourceContentDidChange(event)
    1445     {
    1446         let resource = event.target;
    1447         if (!(resource instanceof WI.Resource))
    1448             return;
    1449 
    1450         if (!resource.isLocalResourceOverride)
    1451             return;
    1452 
    1453         let localResourceOverride = this.localResourceOverrideForURL(resource.url);
    1454         console.assert(localResourceOverride);
     1470    _handleResourceContentChangedForLocalResourceOverride(event)
     1471    {
     1472        let localResourceOverride = event.target.localResourceOverride;
    14551473        if (!localResourceOverride)
    14561474            return;
     
    15271545    LocalResourceOverrideAdded: "network-manager-local-resource-override-added",
    15281546    LocalResourceOverrideRemoved: "network-manager-local-resource-override-removed",
    1529     RequestIntercepted: "network-manager-request-intercepted"
    15301547};
  • trunk/Source/WebInspectorUI/UserInterface/Images/DocumentIcons.svg

    r259329 r270604  
    3434            }
    3535
     36            .generic.override.light {
     37                --border: hsl(226, 70%, 75%);
     38                --paper: hsl(0, 0%, 18%);
     39            }
     40
     41            .generic.override.dark {
     42                --border: hsl(226, 30%, 20%);
     43                --paper: hsl(0, 0%, 90%);
     44            }
     45
    3646            .markup.override.light {
    3747                --border: hsl(226, 70%, 75%);
     
    156166    </symbol>
    157167
    158     <g id="generic-light" class="light"><use href="#doc"/></g>
    159     <g id="generic-light-override" class="light override"><use href="#doc"/></g>
    160     <g id="generic-dark" class="dark"><use href="#doc"/></g>
    161     <g id="generic-dark-override" class="dark override"><use href="#doc"/></g>
    162     <g id="css-light" class="light"><use href="#doc"/><use href="#css"/></g>
     168    <g id="generic-light" class="generic light"><use href="#doc"/></g>
     169    <g id="generic-light-override" class="generic light override"><use href="#doc"/></g>
     170    <g id="generic-dark" class="generic dark"><use href="#doc"/></g>
     171    <g id="generic-dark-override" class="generic dark override"><use href="#doc"/></g>
     172    <g id="css-light" class="css light"><use href="#doc"/><use href="#css"/></g>
    163173    <g id="css-light-override" class="css light override"><use href="#doc"/><use href="#css"/></g>
    164     <g id="css-dark" class="dark"><use href="#doc"/><use href="#css"/></g>
     174    <g id="css-dark" class="css dark"><use href="#doc"/><use href="#css"/></g>
    165175    <g id="css-dark-override" class="css dark override"><use href="#doc"/><use href="#css"/></g>
    166     <g id="font-light" class="light"><use href="#doc"/><use href="#font"/></g>
     176    <g id="font-light" class="font light"><use href="#doc"/><use href="#font"/></g>
    167177    <g id="font-light-override" class="font light override"><use href="#doc"/><use href="#font"/></g>
    168     <g id="font-dark" class="dark"><use href="#doc"/><use href="#font"/></g>
     178    <g id="font-dark" class="font dark"><use href="#doc"/><use href="#font"/></g>
    169179    <g id="font-dark-override" class="font dark override"><use href="#doc"/><use href="#font"/></g>
    170     <g id="image-light" class="light"><use href="#doc"/><use href="#image"/></g>
     180    <g id="image-light" class="image light"><use href="#doc"/><use href="#image"/></g>
    171181    <g id="image-light-override" class="image light override"><use href="#doc"/><use href="#image"/></g>
    172     <g id="image-dark" class="dark"><use href="#doc"/><use href="#image"/></g>
     182    <g id="image-dark" class="image dark"><use href="#doc"/><use href="#image"/></g>
    173183    <g id="image-dark-override" class="image dark override"><use href="#doc"/><use href="#image"/></g>
    174     <g id="gl-light" class="light"><use href="#doc"/><use href="#gl"/></g>
     184    <g id="gl-light" class="gl light"><use href="#doc"/><use href="#gl"/></g>
    175185    <g id="gl-light-override" class="gl light override"><use href="#doc"/><use href="#gl"/></g>
    176     <g id="gl-dark" class="dark"><use href="#doc"/><use href="#gl"/></g>
     186    <g id="gl-dark" class="gl dark"><use href="#doc"/><use href="#gl"/></g>
    177187    <g id="gl-dark-override" class="gl dark override"><use href="#doc"/><use href="#gl"/></g>
    178     <g id="js-light" class="light"><use href="#doc"/><use href="#js"/></g>
     188    <g id="js-light" class="js light"><use href="#doc"/><use href="#js"/></g>
    179189    <g id="js-light-override" class="js light override"><use href="#doc"/><use href="#js"/></g>
    180     <g id="js-dark" class="dark"><use href="#doc"/><use href="#js"/></g>
     190    <g id="js-dark" class="js dark"><use href="#doc"/><use href="#js"/></g>
    181191    <g id="js-dark-override" class="js dark override"><use href="#doc"/><use href="#js"/></g>
    182     <g id="markup-light" class="light"><use href="#doc"/><use href="#markup"/></g>
     192    <g id="markup-light" class="markup light"><use href="#doc"/><use href="#markup"/></g>
    183193    <g id="markup-light-override" class="markup light override"><use href="#doc"/><use href="#markup"/></g>
    184     <g id="markup-dark" class="dark"><use href="#doc"/><use href="#markup"/></g>
     194    <g id="markup-dark" class="markup dark"><use href="#doc"/><use href="#markup"/></g>
    185195    <g id="markup-dark-override" class="markup dark override"><use href="#doc"/><use href="#markup"/></g>
    186     <g id="websocket-light" class="light"><use href="#websocket"/></g>
    187     <g id="websocket-dark" class="dark"><use href="#websocket"/></g>
    188     <g id="worker-light" class="light"><use href="#doc"/><use href="#worker"/></g>
     196    <g id="websocket-light" class="websocket light"><use href="#websocket"/></g>
     197    <g id="websocket-dark" class="websocket dark"><use href="#websocket"/></g>
     198    <g id="worker-light" class="worker light"><use href="#doc"/><use href="#worker"/></g>
    189199    <g id="worker-light-override" class="worker light override"><use href="#doc"/><use href="#worker"/></g>
    190     <g id="worker-dark" class="dark"><use href="#doc"/><use href="#worker"/></g>
     200    <g id="worker-dark" class="worker dark"><use href="#doc"/><use href="#worker"/></g>
    191201    <g id="worker-dark-override" class="worker dark override"><use href="#doc"/><use href="#worker"/></g>
    192202</svg>
  • trunk/Source/WebInspectorUI/UserInterface/Main.html

    r269701 r270604  
    139139    <link rel="stylesheet" href="Views/LocalResourceOverrideLabelView.css">
    140140    <link rel="stylesheet" href="Views/LocalResourceOverridePopover.css">
     141    <link rel="stylesheet" href="Views/LocalResourceOverrideRequestContentView.css">
    141142    <link rel="stylesheet" href="Views/LocalResourceOverrideTreeElement.css">
    142143    <link rel="stylesheet" href="Views/LocalResourceOverrideWarningView.css">
     
    548549    <script src="Views/ResourceContentView.js"></script>
    549550    <script src="Views/TabContentView.js"></script>
     551    <script src="Views/TextContentView.js"></script>
    550552    <script src="Views/TimelineDataGrid.js"></script>
    551553    <script src="Views/TimelineDataGridNode.js"></script>
     
    738740    <script src="Views/LocalResourceOverrideLabelView.js"></script>
    739741    <script src="Views/LocalResourceOverridePopover.js"></script>
     742    <script src="Views/LocalResourceOverrideRequestContentView.js"></script>
    740743    <script src="Views/LocalResourceOverrideTreeElement.js"></script>
    741744    <script src="Views/LocalResourceOverrideWarningView.js"></script>
     
    836839    <script src="Views/StorageSidebarPanel.js"></script>
    837840    <script src="Views/SyntaxHighlightingSupport.js"></script>
    838     <script src="Views/TextContentView.js"></script>
    839841    <script src="Views/TextNavigationItem.js"></script>
    840842    <script src="Views/TextResourceContentView.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Models/LocalResource.js

    r251024 r270604  
    3030// Construction values try to mimic protocol inputs to WI.Resource:
    3131//
    32 //     request: { url, method, headers, timestamp, walltime, finishedTimestamp data }
     32//     request: { url, method, headers, timestamp, walltime, finishedTimestamp, data }
    3333//     response: { mimeType, headers, statusCode, statusText, failureReasonText, content, base64Encoded }
    3434//     metrics: { responseSource, protocol, priority, remoteAddress, connectionIdentifier, sizes }
    3535//     timing: { startTime, domainLookupStart, domainLookupEnd, connectStart, connectEnd, secureConnectionStart, requestStart, responseStart, responseEnd }
    36 //     isLocalResourceOverride: <boolean>
    3736
    3837WI.LocalResource = class LocalResource extends WI.Resource
    3938{
    40     constructor({request, response, metrics, timing, isLocalResourceOverride})
     39    constructor({request, response, metrics, timing})
    4140    {
    4241        console.assert(request);
     
    6160        this._statusText = response.statusText || null;
    6261        this._responseHeaders = response.headers || {};
    63         this._failureReasonText = response.failureReasonText;
     62        this._failureReasonText = response.failureReasonText || null;
    6463        this._timingData = new WI.ResourceTimingData(this, timing);
    6564
     
    7574        this._responseBodySize = !isNaN(metrics.responseBodyDecodedSize) ? metrics.responseBodyDecodedSize : NaN;
    7675
    77         // LocalResource specific.
    78         this._isLocalResourceOverride = isLocalResourceOverride || false;
     76        // Set by `WI.LocalResourceOverride`.
     77        this._localResourceOverride = null;
    7978
    8079        // Finalize WI.Resource.
     
    8483
    8584        // Finalize WI.SourceCode.
    86         let content = response.content;
    87         let base64Encoded = response.base64Encoded;
     85        let content = response.content || "";
     86        let base64Encoded = response.base64Encoded || false;
    8887        this._originalRevision = new WI.SourceCodeRevision(this, content, base64Encoded, this._mimeType);
    8988        this._currentRevision = this._originalRevision;
     
    216215            request: {
    217216                url: this.url,
     217                method: this.requestMethod,
     218                headers: this.requestHeaders,
     219                data: this.requestData,
    218220            },
    219221            response: {
     
    225227                base64Encoded: this.currentRevision.base64Encoded,
    226228            },
    227             isLocalResourceOverride: this._isLocalResourceOverride,
    228229        };
    229230    }
     
    231232    // Public
    232233
    233     get isLocalResourceOverride()
    234     {
    235         return this._isLocalResourceOverride;
    236     }
     234    get localResourceOverride() { return this._localResourceOverride; }
    237235
    238236    // Protected
  • trunk/Source/WebInspectorUI/UserInterface/Models/LocalResourceOverride.js

    r267723 r270604  
    2626WI.LocalResourceOverride = class LocalResourceOverride extends WI.Object
    2727{
    28     constructor(type, localResource, {isCaseSensitive, isRegex, disabled} = {})
    29     {
     28    constructor(url, type, localResource, {isCaseSensitive, isRegex, disabled} = {})
     29    {
     30        console.assert(url && typeof url === "string", url);
    3031        console.assert(Object.values(WI.LocalResourceOverride.InterceptType).includes(type), type);
    3132        console.assert(localResource instanceof WI.LocalResource, localResource);
    32         console.assert(localResource.isLocalResourceOverride, localResource);
    33         console.assert(localResource.url, localResource);
     33        console.assert(!localResource.localResourceOverride, localResource);
    3434        console.assert(isCaseSensitive === undefined || typeof isCaseSensitive === "boolean", isCaseSensitive);
    3535        console.assert(isRegex === undefined || typeof isRegex === "boolean", isRegex);
     
    3838        super();
    3939
     40        this._url = url;
     41        this._urlComponents = null;
    4042        this._type = type;
    4143        this._localResource = localResource;
     
    4345        this._isRegex = isRegex !== undefined ? isRegex : false;
    4446        this._disabled = disabled !== undefined ? disabled : false;
     47
     48        this._localResource._localResourceOverride = this;
    4549    }
    4650
    4751    // Static
    4852
    49     static create(type, {url, mimeType, content, base64Encoded, statusCode, statusText, headers, isCaseSensitive, isRegex, disabled})
     53    static create(url, type, {requestURL, requestMethod, requestHeaders, requestData, responseMIMEType, responseContent, responseBase64Encoded, responseStatusCode, responseStatusText, responseHeaders, isCaseSensitive, isRegex, disabled} = {})
    5054    {
    5155        let localResource = new WI.LocalResource({
    5256            request: {
    53                 url: isRegex ? url : WI.urlWithoutFragment(url),
     57                url: requestURL || "",
     58                method: requestMethod,
     59                headers: requestHeaders,
     60                data: requestData,
    5461            },
    5562            response: {
    56                 headers,
    57                 mimeType,
    58                 statusCode,
    59                 statusText,
    60                 content,
    61                 base64Encoded,
     63                headers: responseHeaders,
     64                mimeType: responseMIMEType,
     65                statusCode: responseStatusCode,
     66                statusText: responseStatusText,
     67                content: responseContent,
     68                base64Encoded: responseBase64Encoded,
    6269            },
    63             isLocalResourceOverride: true,
    6470        });
    65 
    66         return new WI.LocalResourceOverride(type, localResource, {isCaseSensitive, isRegex, disabled});
     71        return new WI.LocalResourceOverride(url, type, localResource, {isCaseSensitive, isRegex, disabled});
     72    }
     73
     74    static displayNameForType(type)
     75    {
     76        switch (type) {
     77        case WI.LocalResourceOverride.InterceptType.Request:
     78            return WI.UIString("Request", "Request @ Local Override Type", "Text indicating that the local override intercepts the request phase of network activity.");
     79        case WI.LocalResourceOverride.InterceptType.Response:
     80            return WI.UIString("Response", "Response @ Local Override Type", "Text indicating that the local override intercepts the response phase of network activity.");
     81        case WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork:
     82            return WI.UIString("Response (skip network)", "Response (skip network) @ Local Override Type", "Text indicating that the local override will skip all network activity and instead immediately serve the response.");
     83        }
     84
     85        console.assert(false, "Unknown type: ", type);
     86        return "";
    6787    }
    6888
     
    7191    static fromJSON(json)
    7292    {
    73         let {type, localResource, isCaseSensitive, isRegex, disabled} = json;
     93        let {url, type, localResource: localResourceJSON, isCaseSensitive, isRegex, disabled} = json;
     94
     95        let localResource = WI.LocalResource.fromJSON(localResourceJSON);
    7496
    7597        // COMPATIBILITY (iOS 13.4): Network.interceptWithRequest/Network.interceptRequestWithResponse did not exist yet.
     98        url ??= localResource.url;
    7699        type ??= WI.LocalResourceOverride.InterceptType.Response;
    77100
    78         return new WI.LocalResourceOverride(type, WI.LocalResource.fromJSON(localResource), {isCaseSensitive, isRegex, disabled});
     101        return new WI.LocalResourceOverride(url, type, localResource, {isCaseSensitive, isRegex, disabled});
    79102    }
    80103
     
    82105    {
    83106        let json = {
     107            url: this._url,
    84108            type: this._type,
    85109            localResource: this._localResource.toJSON(key),
     
    90114
    91115        if (key === WI.ObjectStore.toJSONSymbol)
    92             json[WI.objectStores.localResourceOverrides.keyPath] = this._localResource.url;
     116            json[WI.objectStores.localResourceOverrides.keyPath] = this._url;
    93117
    94118        return json;
     
    97121    // Public
    98122
     123    get url() { return this._url; }
    99124    get type() { return this._type; }
    100     get url() { return this._localResource.url; }
    101125    get localResource() { return this._localResource; }
    102126    get isCaseSensitive() { return this._isCaseSensitive; }
    103127    get isRegex() { return this._isRegex; }
    104128
     129    get urlComponents()
     130    {
     131        if (!this._urlComponents)
     132            this._urlComponents = parseURL(this._url);
     133        return this._urlComponents;
     134    }
     135
    105136    get disabled()
    106137    {
     
    118149    }
    119150
     151    get displayName()
     152    {
     153        return this.displayURL();
     154    }
     155
     156    displayURL({full} = {})
     157    {
     158        if (this._isRegex)
     159            return "/" + this._url + "/" + (!this._isCaseSensitive ? "i" : "");
     160
     161        let displayName = full ? this._url : WI.displayNameForURL(this._url, this.urlComponents);
     162        if (!this._isCaseSensitive)
     163            displayName = WI.UIString("%s (Case Insensitive)", "%s (Case Insensitive) @ Local Override", "Label for case-insensitive URL match pattern of a local override.").format(displayName);
     164        return displayName;
     165    }
     166
    120167    matches(url)
    121168    {
    122169        if (this._isRegex) {
    123             let regex = new RegExp(this.url, !this._isCaseSensitive ? "i" : "");
     170            let regex = new RegExp(this._url, !this._isCaseSensitive ? "i" : "");
    124171            return regex.test(url);
    125172        }
    126173
    127174        if (!this._isCaseSensitive)
    128             return String(url).toLowerCase() === this.url.toLowerCase();
    129 
    130         return url === this.url;
     175            return String(url).toLowerCase() === this._url.toLowerCase();
     176
     177        return url === this._url;
     178    }
     179
     180    equals(localResourceOverrideOrSerializedData)
     181    {
     182        console.assert(localResourceOverrideOrSerializedData instanceof WI.LocalResourceOverride || (localResourceOverrideOrSerializedData && typeof localResourceOverrideOrSerializedData === "object"), localResourceOverrideOrSerializedData);
     183
     184        return localResourceOverrideOrSerializedData.url === this._url
     185            && localResourceOverrideOrSerializedData.isCaseSensitive === this._isCaseSensitive
     186            && localResourceOverrideOrSerializedData.isRegex === this._isRegex;
    131187    }
    132188
     
    135191    saveIdentityToCookie(cookie)
    136192    {
     193        cookie["local-resource-override-url"] = this._url;
    137194        cookie["local-resource-override-type"] = this._type;
    138         cookie["local-resource-override-url"] = this._localResource.url;
    139195        cookie["local-resource-override-is-case-sensitive"] = this._isCaseSensitive;
    140196        cookie["local-resource-override-is-regex"] = this._isRegex;
     
    146202
    147203WI.LocalResourceOverride.InterceptType = {
     204    Request: "request",
    148205    Response: "response",
    149206    ResponseSkippingNetwork: "response-skipping-network",
  • trunk/Source/WebInspectorUI/UserInterface/Models/Resource.js

    r269359 r270604  
    184184        let classes = [];
    185185
    186         let isOverride = resource.isLocalResourceOverride;
     186        let isOverride = !!resource.localResourceOverride;
    187187        let wasOverridden = resource.responseSource === WI.Resource.ResponseSource.InspectorOverride;
    188         let shouldBeOverridden = resource.isLoading() && WI.networkManager.localResourceOverrideForURL(resource.url);
     188        let shouldBeOverridden = resource.isLoading() && WI.networkManager.localResourceOverridesForURL(resource.url).some((localResourceOverride) => !localResourceOverride.disabled);
    189189        if (isOverride || wasOverridden || shouldBeOverridden)
    190190            classes.push("override");
     
    364364    get supportsScriptBlackboxing()
    365365    {
    366         if (this.isLocalResourceOverride)
     366        if (this.localResourceOverride)
    367367            return false;
    368368        if (!this.finished || this.failed)
     
    428428    {
    429429        return this._parentFrame ? this._parentFrame.mainResource === this : false;
    430     }
    431 
    432     get isLocalResourceOverride()
    433     {
    434         return false;
    435430    }
    436431
     
    10631058    }
    10641059
    1065     async createLocalResourceOverride({mimeType, base64Encoded, content} = {})
    1066     {
    1067         console.assert(!this.isLocalResourceOverride);
     1060    async createLocalResourceOverride(type, {mimeType, base64Encoded, content} = {})
     1061    {
     1062        console.assert(!this.localResourceOverride);
    10681063        console.assert(WI.NetworkManager.supportsOverridingResponses());
    10691064
    1070         mimeType ??= this.mimeType ?? WI.mimeTypeForFileExtension(WI.fileExtensionForFilename(this.urlComponents.lastPathComponent));
    1071 
    1072         if (base64Encoded === undefined || content === undefined) {
    1073             try {
    1074                 let {rawContent, rawBase64Encoded} = await this.requestContent();
    1075                 content ??= rawContent;
    1076                 base64Encoded ??= rawBase64Encoded;
    1077             } catch {
    1078                 content ??= "";
    1079                 base64Encoded ??= !WI.shouldTreatMIMETypeAsText(mimeType);
     1065        let resourceData = {
     1066            requestURL: this.url,
     1067        };
     1068
     1069        switch (type) {
     1070        case WI.LocalResourceOverride.InterceptType.Request:
     1071            resourceData.requestMethod = this.requestMethod ?? WI.HTTPUtilities.RequestMethod.GET;
     1072            resourceData.requestHeaders = Object.shallowCopy(this.requestHeaders);
     1073            resourceData.requestData = this.requestData ?? "";
     1074            break;
     1075
     1076        case WI.LocalResourceOverride.InterceptType.Response:
     1077        case WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork:
     1078            resourceData.responseMIMEType = this.mimeType ?? WI.mimeTypeForFileExtension(WI.fileExtensionForFilename(this.urlComponents.lastPathComponent));
     1079            resourceData.responseStatusCode = this.statusCode;
     1080            resourceData.responseStatusText = this.statusText;
     1081            if (!resourceData.responseStatusCode) {
     1082                resourceData.responseStatusCode = 200;
     1083                resourceData.responseStatusText = null;
    10801084            }
    1081         }
    1082 
    1083         return WI.LocalResourceOverride.create(WI.LocalResourceOverride.InterceptType.Response, {
    1084             url: this.url,
    1085             mimeType,
    1086             content,
    1087             base64Encoded,
    1088             statusCode: this.statusCode,
    1089             statusText: this.statusText,
    1090             headers: this.responseHeaders,
    1091         });
     1085            resourceData.responseStatusText ||= WI.HTTPUtilities.statusTextForStatusCode(resourceData.responseStatusCode);
     1086
     1087            if (base64Encoded === undefined || content === undefined) {
     1088                try {
     1089                    let {rawContent, rawBase64Encoded} = await this.requestContent();
     1090                    content ??= rawContent;
     1091                    base64Encoded ??= rawBase64Encoded;
     1092                } catch {
     1093                    content ??= "";
     1094                    base64Encoded ??= !WI.shouldTreatMIMETypeAsText(resourceData.mimeType);
     1095                }
     1096            }
     1097            resourceData.responseContent = content;
     1098            resourceData.responseBase64Encoded = base64Encoded;
     1099            resourceData.responseHeaders = Object.shallowCopy(this.responseHeaders);
     1100            break;
     1101        }
     1102
     1103        return WI.LocalResourceOverride.create(WI.urlWithoutFragment(this.url), type, resourceData);
     1104    }
     1105
     1106    updateLocalResourceOverrideRequestData(data)
     1107    {
     1108        console.assert(this.localResourceOverride);
     1109
     1110        if (data === this._requestData)
     1111            return;
     1112
     1113        this._requestData = data;
     1114
     1115        this.dispatchEventToListeners(WI.Resource.Event.RequestDataDidChange);
    10921116    }
    10931117
     
    12191243    TypeDidChange: "resource-type-did-change",
    12201244    RequestHeadersDidChange: "resource-request-headers-did-change",
     1245    RequestDataDidChange: "resource-request-data-did-change",
    12211246    ResponseReceived: "resource-response-received",
    12221247    LoadingDidFinish: "resource-loading-did-finish",
  • trunk/Source/WebInspectorUI/UserInterface/Models/SourceCode.js

    r264669 r270604  
    142142    }
    143143
     144    get localResourceOverride()
     145    {
     146        // Overridden by subclasses if needed.
     147        return null;
     148    }
     149
    144150    get sourceMaps()
    145151    {
  • trunk/Source/WebInspectorUI/UserInterface/Test.html

    r269701 r270604  
    5858    <script src="Base/DOMUtilities.js"></script>
    5959    <script src="Base/FileUtilities.js"></script>
     60    <script src="Base/HTTPUtilities.js"></script>
    6061    <script src="Base/ImageUtilities.js"></script>
    6162    <script src="Base/MIMETypeUtilities.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js

    r270134 r270604  
    9999        }
    100100
    101         if (representedObject instanceof WI.LocalResourceOverride)
     101        if (representedObject instanceof WI.LocalResourceOverride) {
     102            if (representedObject.type === WI.LocalResourceOverride.InterceptType.Request)
     103                return new WI.LocalResourceOverrideRequestContentView(representedObject);
    102104            return WI.ContentView.createFromRepresentedObject(representedObject.localResource);
     105        }
    103106
    104107        if (representedObject instanceof WI.DOMStorageObject)
     
    238241            if (representedObject.sourceCodeLocation)
    239242                return representedObject.sourceCodeLocation.displaySourceCode;
     243            return representedObject;
    240244        }
    241245
     
    243247            if (representedObject.domNode)
    244248                return WI.ContentView.resolvedRepresentedObjectForRepresentedObject(representedObject.domNode);
     249            return representedObject;
    245250        }
    246251
     
    248253            if (representedObject.frame)
    249254                return WI.ContentView.resolvedRepresentedObjectForRepresentedObject(representedObject.frame);
     255            return representedObject;
    250256        }
    251257
     
    256262            return representedObject.sourceCode;
    257263
    258         if (representedObject instanceof WI.LocalResourceOverride)
    259             return representedObject.localResource;
     264        if (representedObject instanceof WI.LocalResourceOverride) {
     265            if (representedObject.type !== WI.LocalResourceOverride.InterceptType.Request)
     266                return representedObject.localResource;
     267            return representedObject;
     268        }
    260269
    261270        return representedObject;
  • trunk/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js

    r269755 r270604  
    7878        if (WI.networkManager.canBeOverridden(sourceCode)) {
    7979            contextMenu.appendSeparator();
    80             contextMenu.appendItem(WI.UIString("Create Local Override"), async () => {
    81                 let resource = sourceCode;
    82                 let localResourceOverride = await resource.createLocalResourceOverride();
     80
     81            if (WI.NetworkManager.supportsOverridingRequests()) {
     82                contextMenu.appendItem(WI.UIString("Create Request Local Override"), async () => {
     83                    let localResourceOverride = await sourceCode.createLocalResourceOverride(WI.LocalResourceOverride.InterceptType.Request);
     84                    WI.networkManager.addLocalResourceOverride(localResourceOverride);
     85                    WI.showLocalResourceOverride(localResourceOverride, {
     86                        initiatorHint: WI.TabBrowser.TabNavigationInitiator.ContextMenu,
     87                    });
     88                });
     89            }
     90
     91            contextMenu.appendItem(WI.UIString("Create Response Local Override"), async () => {
     92                let localResourceOverride = await sourceCode.createLocalResourceOverride(WI.LocalResourceOverride.InterceptType.Response);
    8393                WI.networkManager.addLocalResourceOverride(localResourceOverride);
    8494                WI.showLocalResourceOverride(localResourceOverride, {
     
    8797            });
    8898        } else {
    89             let localResourceOverride = WI.networkManager.localResourceOverrideForURL(sourceCode.url);
     99            let localResourceOverride = WI.networkManager.localResourceOverridesForURL(sourceCode.url)[0];
    90100            if (localResourceOverride) {
    91101                contextMenu.appendSeparator();
     
    110120    contextMenu.appendSeparator();
    111121
    112     if (location && (sourceCode instanceof WI.Script || (sourceCode instanceof WI.Resource && sourceCode.type === WI.Resource.Type.Script && !sourceCode.isLocalResourceOverride))) {
     122    if (location && (sourceCode instanceof WI.Script || (sourceCode instanceof WI.Resource && sourceCode.type === WI.Resource.Type.Script && !sourceCode.localResourceOverride))) {
    113123        let existingBreakpoint = WI.debuggerManager.breakpointForSourceCodeLocation(location);
    114124        if (existingBreakpoint) {
     
    143153    WI.appendContextMenuItemsForURL(contextMenu, sourceCode.url, {sourceCode, location});
    144154
    145     if (sourceCode instanceof WI.Resource && !sourceCode.isLocalResourceOverride) {
     155    if (sourceCode instanceof WI.Resource && !sourceCode.localResourceOverride) {
    146156        if (sourceCode.urlComponents.scheme !== "data") {
    147157            contextMenu.appendItem(WI.UIString("Copy as cURL"), () => {
  • trunk/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.js

    r270134 r270604  
    7171            this.addSubview(dropZoneView);
    7272
    73             if (this.showingLocalResourceOverride)
     73            if (this.resource.localResourceOverride)
    7474                this.resource.addEventListener(WI.SourceCode.Event.ContentDidChange, this._handleLocalResourceContentDidChange, this);
    7575        }
     
    117117    dropZoneShouldAppearForDragEvent(dropZone, event)
    118118    {
     119        let existingOverrides = WI.networkManager.localResourceOverridesForURL(this.resource.url);
     120        if (existingOverrides.length > 1)
     121            return false;
     122
     123        // Request overrides cannot be created/updated from a file as files don't have network info.
     124        let localResourceOverride = this.resource.localResourceOverride || existingOverrides[0];
     125        if (localResourceOverride?.type === WI.LocalResourceOverride.InterceptType.Request)
     126            return false;
     127
    119128        return event.dataTransfer.types.includes("Files");
    120129    }
     
    122131    dropZoneHandleDragEnter(dropZone, event)
    123132    {
    124         if (this.showingLocalResourceOverride)
     133        if (this.resource.localResourceOverride)
    125134            dropZone.text = WI.UIString("Update Font");
    126         else if (WI.networkManager.localResourceOverrideForURL(this.resource.url))
     135        else if (WI.networkManager.localResourceOverridesForURL(this.resource.url).length)
    127136            dropZone.text = WI.UIString("Update Local Override");
    128137        else
     
    139148
    140149        WI.FileUtilities.readData(files, async ({dataURL, mimeType, base64Encoded, content}) => {
    141             let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this.resource.url);
    142             if (!localResourceOverride && !this.showingLocalResourceOverride) {
    143                 localResourceOverride = await this.resource.createLocalResourceOverride();
     150            let localResourceOverride = this.resource.localResourceOverride || WI.networkManager.localResourceOverridesForURL(this.resource.url)[0];
     151            if (!localResourceOverride) {
     152                localResourceOverride = await this.resource.createLocalResourceOverride(WI.LocalResourceOverride.InterceptType.Response);
    144153                WI.networkManager.addLocalResourceOverride(localResourceOverride);
    145154            }
    146 
    147155            console.assert(localResourceOverride);
    148156
     
    150158            revision.updateRevisionContent(content, {base64Encoded, mimeType});
    151159
    152             if (!this.showingLocalResourceOverride)
     160            if (!this.resource.localResourceOverride)
    153161                WI.showLocalResourceOverride(localResourceOverride);
    154162        });
  • trunk/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js

    r270134 r270604  
    9393            this.addSubview(dropZoneView);
    9494
    95             if (this.showingLocalResourceOverride)
     95            if (this.resource.localResourceOverride)
    9696                this.resource.addEventListener(WI.SourceCode.Event.ContentDidChange, this._handleLocalResourceContentDidChange, this);
    9797        }
     
    124124            return false;
    125125
     126        let existingOverrides = WI.networkManager.localResourceOverridesForURL(this.resource.url);
     127        if (existingOverrides.length > 1)
     128            return false;
     129
     130        // Request overrides cannot be created/updated from a file as files don't have network info.
     131        let localResourceOverride = this.resource.localResourceOverride || existingOverrides[0];
     132        if (localResourceOverride?.type === WI.LocalResourceOverride.InterceptType.Request)
     133            return false;
     134
    126135        // Appear if the drop contains a file.
    127136        return event.dataTransfer.types.includes("Files");
     
    130139    dropZoneHandleDragEnter(dropZone, event)
    131140    {
    132         if (this.showingLocalResourceOverride)
     141        if (this.resource.localResourceOverride)
    133142            dropZone.text = WI.UIString("Update Image");
    134         else if (WI.networkManager.localResourceOverrideForURL(this.resource.url))
     143        else if (WI.networkManager.localResourceOverridesForURL(this.resource.url).length)
    135144            dropZone.text = WI.UIString("Update Local Override");
    136145        else
     
    147156
    148157        WI.FileUtilities.readData(files, async ({dataURL, mimeType, base64Encoded, content}) => {
    149             let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this.resource.url);
    150             if (!localResourceOverride && !this.showingLocalResourceOverride) {
    151                 localResourceOverride = await this.resource.createLocalResourceOverride();
     158            let localResourceOverride = this.resource.localResourceOverride || WI.networkManager.localResourceOverridesForURL(this.resource.url)[0];
     159            if (!localResourceOverride) {
     160                localResourceOverride = await this.resource.createLocalResourceOverride(WI.LocalResourceOverride.InterceptType.Response);
    152161                WI.networkManager.addLocalResourceOverride(localResourceOverride);
    153162            }
    154 
    155163            console.assert(localResourceOverride);
    156164
     
    158166            revision.updateRevisionContent(content, {base64Encoded, mimeType});
    159167
    160             if (!this.showingLocalResourceOverride)
     168            if (!this.resource.localResourceOverride)
    161169                WI.showLocalResourceOverride(localResourceOverride);
    162170        });
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideLabelView.js

    r249504 r270604  
    2626WI.LocalResourceOverrideLabelView = class LocalResourceOverrideLabelView extends WI.View
    2727{
    28     constructor(localResource)
     28    constructor(localResourceOverride)
    2929    {
    30         console.assert(localResource.isLocalResourceOverride);
     30        console.assert(localResourceOverride instanceof WI.LocalResourceOverride, localResourceOverride);
    3131
    3232        super();
    3333
    34         this._localResource = localResource;
     34        this._localResourceOverride = localResourceOverride;
    3535
    3636        this.element.classList.add("local-resource-override-label-view");
     
    4141    initialLayout()
    4242    {
    43         this._labelElement = document.createElement("span");
    44         this._labelElement.className = "label";
    45         this._labelElement.textContent = WI.UIString("Override");
     43        let labelElement = document.createElement("span");
     44        labelElement.className = "label";
     45        if (this._localResourceOverride.type === WI.LocalResourceOverride.InterceptType.Request)
     46            labelElement.textContent = WI.UIString("Request Override", "Request Override @ Local Override Content View", "Label indicating that the shown content is from a request local override.");
     47        else
     48            labelElement.textContent = WI.UIString("Response Override", "Response Override @ Local Override Content View", "Label indicating that the shown content is from a response local override.");
    4649
    47         this._urlElement = document.createElement("span");
    48         this._urlElement.textContent = this._localResource.url;
    49         this._urlElement.className = "url";
     50        let urlElement = document.createElement("span");
     51        urlElement.textContent = this._localResourceOverride.displayURL({full: true});
     52        urlElement.className = "url";
    5053
    5154        let container = this.element.appendChild(document.createElement("div"));
    52         container.append(this._labelElement, " ", this._urlElement);
     55        container.append(labelElement, " ", urlElement);
    5356    }
    5457};
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverridePopover.css

    r269166 r270604  
    6666}
    6767
    68 .popover .local-resource-override-popover-content .editor.url {
    69     width: 324px;
     68.popover .local-resource-override-popover-content.request .editor:is(.url, .redirect) {
     69    width: 344px;
     70}
     71
     72.popover .local-resource-override-popover-content.response .editor.url {
     73    width: 330px;
    7074}
    7175
     
    9195}
    9296
     97.popover .local-resource-override-popover-content .data-grid {
     98    margin-bottom: 6px;
     99}
     100
    93101.popover .local-resource-override-popover-content .data-grid tr.header-content-type > :matches(.name-column, .value-column) {
    94102    opacity: 0.5;
    95 }
    96 
    97 .popover .local-resource-override-popover-content .add-header {
    98     margin-top: 8px;
    99 }
    100 
    101 .popover .local-resource-override-popover-content .add-header + .reference-page-link-container {
    102     margin-top: 6px;
    103103}
    104104
     
    107107}
    108108
    109 body[dir=ltr] .popover .local-resource-override-popover-content .reference-page-link-container {
    110     float: right;
    111 }
    112 
    113 body[dir=rtl] .popover .local-resource-override-popover-content .reference-page-link-container {
    114     float: left;
     109.popover .local-resource-override-popover-content .reference-page-link-container {
     110    position: absolute;
     111    inset-inline-end: 1.5em;
     112    bottom: 1.25em;
    115113}
    116114
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverridePopover.js

    r267723 r270604  
    3030        super(delegate);
    3131
     32        this._typeSelectElement = null;
    3233        this._urlCodeMirror = null;
    3334        this._isCaseSensitiveCheckbox = null;
    3435        this._isRegexCheckbox = null;
     36        this._requestURLCodeMirror = null;
     37        this._methodSelectElement = null;
    3538        this._mimeTypeCodeMirror = null;
    3639        this._statusCodeCodeMirror = null;
    3740        this._statusTextCodeMirror = null;
    3841        this._headersDataGrid = null;
    39 
     42        this._skipNetworkCheckbox = null;
     43
     44        this._originalRequestURL = null;
    4045        this._serializedDataWhenShown = null;
    4146
     
    5055            return null;
    5156
    52         let url = this._urlCodeMirror.getValue();
    53         if (!url)
     57        // COMPATIBILITY (iOS 13.4): `Network.addInterception` did not exist yet.
     58        let data = {
     59            type: this._skipNetworkCheckbox?.checked ? WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork : this._typeSelectElement.value,
     60            url: WI.urlWithoutFragment(this._urlCodeMirror.getValue()),
     61            isCaseSensitive: !this._isCaseSensitiveCheckbox || this._isCaseSensitiveCheckbox.checked,
     62            isRegex: !!this._isRegexCheckbox?.checked,
     63        };
     64
     65        if (!data.url)
    5466            return null;
    5567
    56         let isRegex = this._isRegexCheckbox && this._isRegexCheckbox.checked;
    57         if (!isRegex) {
     68        if (!data.isRegex) {
    5869            const schemes = ["http:", "https:", "file:"];
    59             if (!schemes.some((scheme) => url.toLowerCase().startsWith(scheme)))
     70            if (!schemes.some((scheme) => data.url.toLowerCase().startsWith(scheme)))
    6071                return null;
    6172        }
    62 
    63         // NOTE: We can allow an empty mimeType / statusCode / statusText to pass
    64         // network values through, but lets require them for overrides so that
    65         // the popover doesn't have to have an additional state for "pass through".
    66 
    67         let mimeType = this._mimeTypeCodeMirror.getValue() || this._mimeTypeCodeMirror.getOption("placeholder");
    68         if (!mimeType)
    69             return null;
    70 
    71         let statusCode = parseInt(this._statusCodeCodeMirror.getValue());
    72         if (isNaN(statusCode))
    73             statusCode = parseInt(this._statusCodeCodeMirror.getOption("placeholder"));
    74         if (isNaN(statusCode) || statusCode < 0)
    75             return null;
    76 
    77         let statusText = this._statusTextCodeMirror.getValue() || this._statusTextCodeMirror.getOption("placeholder");
    78         if (!statusText)
    79             return null;
    8073
    8174        let headers = {};
     
    8477            if (!name || !value)
    8578                continue;
    86             if (name.toLowerCase() === "content-type")
    87                 continue;
    88             if (name.toLowerCase() === "set-cookie")
    89                 continue;
     79            if (data.type !== WI.LocalResourceOverride.InterceptType.Request) {
     80                if (name.toLowerCase() === "content-type")
     81                    continue;
     82                if (name.toLowerCase() === "set-cookie")
     83                    continue;
     84            }
    9085            headers[name] = value;
    9186        }
    9287
    93         let data = {
    94             type: this._skipNetworkCheckbox?.checked ? WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork : WI.LocalResourceOverride.InterceptType.Response,
    95             url,
    96             mimeType,
    97             statusCode,
    98             statusText,
    99             headers,
    100         };
    101 
    102         if (this._isCaseSensitiveCheckbox)
    103             data.isCaseSensitive = this._isCaseSensitiveCheckbox.checked;
    104 
    105         if (this._isRegexCheckbox)
    106             data.isRegex = this._isRegexCheckbox.checked;
     88        switch (data.type) {
     89        case WI.LocalResourceOverride.InterceptType.Request:
     90            data.requestURL = this._requestURLCodeMirror.getValue();
     91            data.requestMethod = this._methodSelectElement.value;
     92            data.requestHeaders = headers;
     93            break;
     94
     95        case WI.LocalResourceOverride.InterceptType.Response:
     96        case WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork:
     97            // NOTE: We can allow an empty mimeType / statusCode / statusText to pass
     98            // network values through, but lets require them for overrides so that
     99            // the popover doesn't have to have an additional state for "pass through".
     100
     101            data.responseMIMEType = this._mimeTypeCodeMirror.getValue() || this._mimeTypeCodeMirror.getOption("placeholder");
     102            if (!data.responseMIMEType)
     103                return null;
     104
     105            data.responseStatusCode = parseInt(this._statusCodeCodeMirror.getValue());
     106            if (isNaN(data.responseStatusCode))
     107                data.responseStatusCode = parseInt(this._statusCodeCodeMirror.getOption("placeholder"));
     108            if (isNaN(data.responseStatusCode) || data.responseStatusCode < 0)
     109                return null;
     110
     111            data.responseStatusText = this._statusTextCodeMirror.getValue() || this._statusTextCodeMirror.getOption("placeholder");
     112            if (!data.responseStatusText)
     113                return null;
     114
     115            data.responseHeaders = headers;
     116            break;
     117        }
     118
     119        if (!data.requestURL) {
     120            if (data.isCaseSensitive && !data.isRegex)
     121                data.requestURL = data.url;
     122            else if (this._originalRequestURL)
     123                data.requestURL = this._originalRequestURL;
     124        }
     125
     126        if (!data.responseMIMEType && data.requestURL) {
     127            data.responseMIMEType = WI.mimeTypeForFileExtension(WI.fileExtensionForURL(data.requestURL));
     128            if (data.type !== WI.LocalResourceOverride.InterceptType.Request)
     129                headers["Content-Type"] = data.responseMIMEType;
     130        }
    107131
    108132        // No change.
     
    122146        let localResource = localResourceOverride ? localResourceOverride.localResource : null;
    123147
    124         let data = {};
    125         let resourceData = {};
     148        let placeholderData = {};
     149        let valueData = {};
    126150        if (localResource) {
    127             data.url = resourceData.url = localResource.url;
    128             data.mimeType = resourceData.mimeType = localResource.mimeType;
    129             data.statusCode = resourceData.statusCode = String(localResource.statusCode);
    130             data.statusText = resourceData.statusText = localResource.statusText;
    131         }
    132 
    133         if (!data.url)
    134             data.url = this._defaultURL();
    135 
    136         if (!data.mimeType)
    137             data.mimeType = "text/javascript";
    138 
    139         if (!data.statusCode || data.statusCode === "NaN") {
    140             data.statusCode = "200";
    141             resourceData.statusCode = undefined;
    142         }
    143 
    144         if (!data.statusText) {
    145             data.statusText = WI.HTTPUtilities.statusTextForStatusCode(parseInt(data.statusCode));
    146             resourceData.statusText = undefined;
    147         }
    148 
    149         let responseHeaders = localResource ? localResource.responseHeaders : {};
     151            placeholderData.url = valueData.url = localResourceOverride.url;
     152            placeholderData.requestURL = valueData.requestURL = localResource.url;
     153            placeholderData.method = valueData.method = localResource.requestMethod;
     154            placeholderData.mimeType = valueData.mimeType = localResource.mimeType;
     155            placeholderData.statusCode = valueData.statusCode = String(localResource.statusCode);
     156            placeholderData.statusText = valueData.statusText = localResource.statusText;
     157        }
     158
     159        placeholderData.url ||= this._defaultURL();
     160        placeholderData.requestURL ||= placeholderData.url;
     161        placeholderData.method ||= "GET";
     162        placeholderData.mimeType ||= "text/javascript";
     163        if (!placeholderData.statusCode || placeholderData.statusCode === "NaN") {
     164            placeholderData.statusCode = "200";
     165            placeholderData.statusText = undefined;
     166            valueData.statusCode = undefined;
     167            valueData.statusText = undefined;
     168        }
     169        if (!placeholderData.statusText) {
     170            placeholderData.statusText = WI.HTTPUtilities.statusTextForStatusCode(parseInt(placeholderData.statusCode));
     171            valueData.statusText = undefined;
     172        }
     173
     174        let requestHeaders = localResource?.requestHeaders ?? {};
     175        let responseHeaders = localResource?.responseHeaders ?? {};
    150176
    151177        let popoverContentElement = document.createElement("div");
     
    170196            labelElement.setAttribute("for", inputField.id);
    171197
    172             return {codeMirror, dataElement};
    173         };
    174 
    175         let urlRow = createRow(WI.UIString("URL"), "url", resourceData.url || "", data.url);
     198            return {element: row, dataElement, codeMirror};
     199        };
     200
     201        this._typeSelectElement = document.createElement("select");
     202        for (let type of [WI.LocalResourceOverride.InterceptType.Request, WI.LocalResourceOverride.InterceptType.Response]) {
     203            let optionElement = this._typeSelectElement.appendChild(document.createElement("option"));
     204            optionElement.textContent = WI.LocalResourceOverride.displayNameForType(type);
     205            optionElement.value = type;
     206        }
     207        this._typeSelectElement.value = localResourceOverride?.type ?? WI.LocalResourceOverride.InterceptType.Request;
     208
     209        if (!localResourceOverride && WI.NetworkManager.supportsOverridingRequests()) {
     210            let typeRowElement = table.appendChild(document.createElement("tr"));
     211
     212            let typeHeaderElement = typeRowElement.appendChild(document.createElement("th"));
     213
     214            let typeLabelElement = typeHeaderElement.appendChild(document.createElement("label"));
     215            typeLabelElement.textContent = WI.UIString("Type");
     216
     217            let typeDataElement = typeRowElement.appendChild(document.createElement("td"));
     218            typeDataElement.appendChild(this._typeSelectElement);
     219
     220            this._typeSelectElement.addEventListener("change", (event) => {
     221                toggleInputsForType();
     222                this.update();
     223            });
     224
     225            this._typeSelectElement.id = "local-resource-override-popover-type-input-field";
     226            typeLabelElement.setAttribute("for", this._typeSelectElement.id);
     227        }
     228
     229        let urlRow = createRow(WI.UIString("URL"), "url", valueData.url || "", placeholderData.url);
    176230        this._urlCodeMirror = urlRow.codeMirror;
    177231
     
    191245        };
    192246
     247        // COMPATIBILITY (iOS 13.4): `Network.addInterception` did not exist yet.
    193248        if (InspectorBackend.hasCommand("Network.addInterception", "caseSensitive")) {
    194249            let isCaseSensitiveLabel = urlRow.dataElement.appendChild(document.createElement("label"));
     
    202257        }
    203258
     259        // COMPATIBILITY (iOS 13.4): `Network.addInterception` did not exist yet.
    204260        if (InspectorBackend.hasCommand("Network.addInterception", "isRegex")) {
    205261            let isRegexLabel = urlRow.dataElement.appendChild(document.createElement("label"));
     
    216272        }
    217273
    218         let mimeTypeRow = createRow(WI.UIString("MIME Type"), "mime", resourceData.mimeType || "", data.mimeType);
     274        let requestURLRow = null;
     275        let methodRowElement = null;
     276        if (WI.NetworkManager.supportsOverridingRequests()) {
     277            requestURLRow = createRow(WI.UIString("Redirect"), "redirect", valueData.requestURL || "", placeholderData.requestURL);
     278            this._requestURLCodeMirror = requestURLRow.codeMirror;
     279
     280            methodRowElement = table.appendChild(document.createElement("tr"));
     281
     282            let methodHeaderElement = methodRowElement.appendChild(document.createElement("th"));
     283
     284            let methodLabelElement = methodHeaderElement.appendChild(document.createElement("label"));
     285            methodLabelElement.textContent = WI.UIString("Method");
     286
     287            let methodDataElement = methodRowElement.appendChild(document.createElement("td"));
     288
     289            this._methodSelectElement = methodDataElement.appendChild(document.createElement("select"));
     290
     291            [
     292                WI.HTTPUtilities.RequestMethod.GET,
     293                WI.HTTPUtilities.RequestMethod.POST,
     294                null, // divider
     295                WI.HTTPUtilities.RequestMethod.HEAD,
     296                WI.HTTPUtilities.RequestMethod.PATCH,
     297                WI.HTTPUtilities.RequestMethod.PUT,
     298                WI.HTTPUtilities.RequestMethod.DELETE,
     299                null, // divider
     300                WI.HTTPUtilities.RequestMethod.OPTIONS,
     301                WI.HTTPUtilities.RequestMethod.CONNECT,
     302                WI.HTTPUtilities.RequestMethod.TRACE,
     303            ].forEach((method) => {
     304                if (!method) {
     305                    this._methodSelectElement.appendChild(document.createElement("hr"));
     306                    return;
     307                }
     308
     309                let optionElement = this._methodSelectElement.appendChild(document.createElement("option"));
     310                optionElement.textContent = method;
     311            });
     312
     313            this._methodSelectElement.value = valueData.method || placeholderData.method;
     314
     315            this._methodSelectElement.id = "local-resource-override-popover-method-input-field";
     316            methodLabelElement.setAttribute("for", this._methodSelectElement.id);
     317        }
     318
     319        let mimeTypeRow = createRow(WI.UIString("MIME Type", "MIME Type @ Local Override Popover", "Label for MIME type input for the local override currently being edited."), "mime", valueData.mimeType || "", placeholderData.mimeType);
    219320        this._mimeTypeCodeMirror = mimeTypeRow.codeMirror;
    220321
    221         let statusCodeRow = createRow(WI.UIString("Status"), "status", resourceData.statusCode || "", data.statusCode);
     322        let statusCodeRow = createRow(WI.UIString("Status", "Status @ Local Override Popover", "Label for the HTTP status code input for the local override currently being edited."), "status", valueData.statusCode || "", placeholderData.statusCode);
    222323        this._statusCodeCodeMirror = statusCodeRow.codeMirror;
    223324
    224325        let statusTextEditorElement = statusCodeRow.dataElement.appendChild(document.createElement("div"));
    225326        statusTextEditorElement.className = "editor status-text";
    226         this._statusTextCodeMirror = this._createEditor(statusTextEditorElement, {value: resourceData.statusText || "", placeholder: data.statusText});
     327        this._statusTextCodeMirror = this._createEditor(statusTextEditorElement, {value: valueData.statusText || "", placeholder: placeholderData.statusText});
    227328
    228329        let editCallback = () => {};
     
    236337                siblingToSelect.select();
    237338
    238             this._headersDataGrid.updateLayoutIfNeeded();
     339            toggleHeadersDataGridVisibility();
    239340            this.update();
    240341        };
     
    261362        };
    262363
    263         let contentTypeDataGridNode = addDataGridNodeForHeader("Content-Type", data.mimeType, {selectable: false, editable: false, classNames: ["header-content-type"]});
    264 
    265         for (let name in responseHeaders) {
    266             if (name.toLowerCase() === "content-type")
    267                 continue;
    268             if (name.toLowerCase() === "set-cookie")
    269                 continue;
    270             addDataGridNodeForHeader(name, responseHeaders[name]);
    271         }
     364        let toggleHeadersDataGridVisibility = (force) => {
     365            this._headersDataGrid.element.hidden = force !== undefined ? force : !this._headersDataGrid.hasChildren;
     366            this._headersDataGrid.updateLayoutIfNeeded();
     367        };
     368
     369        let contentTypeDataGridNode = addDataGridNodeForHeader(WI.unlocalizedString("Content-Type"), valueData.mimeType || placeholderData.mimeType, {selectable: false, editable: false, classNames: ["header-content-type"]});
    272370
    273371        let headersRow = table.appendChild(document.createElement("tr"));
     
    277375        headersLabel.textContent = WI.UIString("Headers");
    278376        headersData.appendChild(this._headersDataGrid.element);
    279         this._headersDataGrid.updateLayoutIfNeeded();
    280377
    281378        let addHeaderButton = headersData.appendChild(document.createElement("button"));
     
    288385            });
    289386            this._headersDataGrid.appendChild(newNode);
    290             this._headersDataGrid.updateLayoutIfNeeded();
     387            toggleHeadersDataGridVisibility(false);
    291388            this.update();
    292389            this._headersDataGrid.startEditingNode(newNode);
    293390        });
    294391
     392        let optionsRowElement = null;
    295393        if (WI.NetworkManager.supportsOverridingRequestsWithResponses()) {
    296             let optionsRow = table.appendChild(document.createElement("tr"));
    297             optionsRow.className = "options";
    298 
    299             let optionsHeader = optionsRow.appendChild(document.createElement("th"));
     394            optionsRowElement = table.appendChild(document.createElement("tr"));
     395            optionsRowElement.className = "options";
     396
     397            let optionsHeader = optionsRowElement.appendChild(document.createElement("th"));
    300398
    301399            let optionsLabel = optionsHeader.appendChild(document.createElement("label"));
    302400            optionsLabel.textContent = WI.UIString("Options");
    303401
    304             let optionsData = optionsRow.appendChild(document.createElement("td"));
     402            let optionsData = optionsRowElement.appendChild(document.createElement("td"));
    305403
    306404            let skipNetworkLabel = optionsData.appendChild(document.createElement("label"));
     
    312410
    313411            skipNetworkLabel.appendChild(document.createTextNode(WI.UIString("Skip Network", "Skip Network @ Local Override Popover Options", "Label for checkbox that controls whether the local override will actually perform a network request or skip it to immediately serve the response.")));
    314 
    315             optionsData.appendChild(WI.createReferencePageLink("local-overrides", "configuring-local-overrides"));
    316         } else
    317             headersData.appendChild(WI.createReferencePageLink("local-overrides", "configuring-local-overrides"));
     412        }
     413
     414        popoverContentElement.appendChild(WI.createReferencePageLink(WI.ReferencePage.LocalOverrides, "configuring-local-overrides"));
    318415
    319416        let incrementStatusCode = () => {
     
    404501        updateURLCodeMirrorMode();
    405502
     503        let toggleInputsForType = (initializeHeaders) => {
     504            let isRequest = this._typeSelectElement.value === WI.LocalResourceOverride.InterceptType.Request;
     505            popoverContentElement.classList.toggle("request", isRequest);
     506            popoverContentElement.classList.toggle("response", !isRequest);
     507
     508            if (initializeHeaders) {
     509                let headers = isRequest ? requestHeaders : responseHeaders;
     510                for (let name in headers) {
     511                    if (!isRequest) {
     512                        if (name.toLowerCase() === "content-type")
     513                            continue;
     514                        if (name.toLowerCase() === "set-cookie")
     515                            continue;
     516                    }
     517                    addDataGridNodeForHeader(name, headers[name]);
     518                }
     519            }
     520
     521            if (requestURLRow)
     522                requestURLRow.element.hidden = !isRequest;
     523            if (methodRowElement)
     524                methodRowElement.hidden = !isRequest;
     525
     526            mimeTypeRow.element.hidden = isRequest;
     527            statusCodeRow.element.hidden = isRequest;
     528            if (optionsRowElement)
     529                optionsRowElement.hidden = isRequest;
     530
     531            if (isRequest) {
     532                this._requestURLCodeMirror.refresh();
     533
     534                if (contentTypeDataGridNode.parent)
     535                    this._headersDataGrid.removeChild(contentTypeDataGridNode);
     536            } else {
     537                this._mimeTypeCodeMirror.refresh();
     538                this._statusCodeCodeMirror.refresh();
     539                this._statusTextCodeMirror.refresh();
     540
     541                if (!contentTypeDataGridNode.parent)
     542                    this._headersDataGrid.insertChild(contentTypeDataGridNode, 0);
     543            }
     544            toggleHeadersDataGridVisibility();
     545        };
     546        toggleInputsForType(true);
     547
     548        this._originalRequestURL = localResource?.url ?? null;
    406549        this._serializedDataWhenShown = this.serializedData;
    407550
     
    412555        setTimeout(() => {
    413556            this._urlCodeMirror.refresh();
     557            this._requestURLCodeMirror?.refresh();
    414558            this._mimeTypeCodeMirror.refresh();
    415559            this._statusCodeCodeMirror.refresh();
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideRequestContentView.css

    r270601 r270604  
    2424 */
    2525
    26 WI.ReferencePage = {
    27     DOMBreakpoints: "dom-breakpoints",
    28     EventBreakpoints: "event-breakpoints",
    29     JavaScriptBreakpoints: "javascript-breakpoints",
    30     URLBreakpoints: "url-breakpoints",
    31 };
     26.content-view.text.local-resource-override-request {
     27    display: flex;
     28    flex-direction: column;
     29}
     30
     31.content-view.text.local-resource-override-request > .text-editor {
     32    position: relative;
     33    width: 100%;
     34    height: 100%;
     35}
     36
     37.content-view.text.local-resource-override-request > .message-text-view {
     38    top: var(--navigation-bar-height);
     39}
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideTreeElement.js

    r267723 r270604  
    2626WI.LocalResourceOverrideTreeElement = class LocalResourceOverrideTreeElement extends WI.ResourceTreeElement
    2727{
    28     constructor(localResource, representedObject, options)
     28    constructor(localResourceOverride, options)
    2929    {
    30         console.assert(localResource instanceof WI.LocalResource);
    31         console.assert(localResource.isLocalResourceOverride);
    32         console.assert(representedObject instanceof WI.LocalResourceOverride);
     30        console.assert(localResourceOverride instanceof WI.LocalResourceOverride, localResourceOverride);
    3331
    34         super(localResource, representedObject, options);
     32        super(localResourceOverride.localResource, localResourceOverride, options);
    3533
    36         this._localResourceOverride = representedObject;
     34        this._localResourceOverride = localResourceOverride;
    3735
    3836        this._popover = null;
     
    4341    get mainTitleText()
    4442    {
    45         let text;
    46         if (this.representedObject.isRegex) {
    47             text = "/" + this.resource.url + "/";
    48             if (!this.representedObject.isCaseSensitive)
    49                 text += "i";
    50         } else {
    51             text = super.mainTitleText;
    52             if (!this.representedObject.isCaseSensitive)
    53                 text = WI.UIString("%s (Case Insensitive)").format(text);
    54         }
    55         return text;
     43        return this.representedObject.displayName;
    5644    }
    5745
     
    132120            return;
    133121
    134         let {type, url, isCaseSensitive, isRegex, mimeType, statusCode, statusText, headers} = serializedData;
    135 
    136122        // Do not conflict with an existing override unless we are modifying ourselves.
    137         let existingOverride = WI.networkManager.localResourceOverrideForURL(url);
    138         if (existingOverride && existingOverride !== this._localResourceOverride) {
     123        if (WI.networkManager.localResourceOverrides.some((existingOverride) => existingOverride !== this._localResourceOverride && existingOverride.equals(this._localResourceOverride))) {
    139124            InspectorFrontendHost.beep();
    140125            return;
     
    143128        let wasSelected = this.selected;
    144129
    145         let revision = this._localResourceOverride.localResource.currentRevision;
    146         let newLocalResourceOverride = WI.LocalResourceOverride.create(type, {
    147             url,
    148             isCaseSensitive,
    149             isRegex,
    150             mimeType,
    151             statusCode,
    152             statusText,
    153             headers,
    154             content: revision.content,
    155             base64Encoded: revision.base64Encoded,
    156         });
     130        if (serializedData.type !== WI.LocalResourceOverride.InterceptType.Request) {
     131            let revision = this._localResourceOverride.localResource.currentRevision;
     132            serializedData.responseContent = revision.content;
     133            serializedData.responseBase64Encoded = revision.base64Encoded;
     134        }
    157135
     136        let newLocalResourceOverride = WI.LocalResourceOverride.create(serializedData.url, serializedData.type, serializedData);
    158137        WI.networkManager.removeLocalResourceOverride(this._localResourceOverride);
    159138        WI.networkManager.addLocalResourceOverride(newLocalResourceOverride);
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideWarningView.js

    r269359 r270604  
    2828    constructor(resource)
    2929    {
    30         console.assert(!resource.isLocalResourceOverride);
     30        console.assert(resource instanceof WI.Resource, resource);
     31        console.assert(!resource.localResourceOverride, resource);
    3132
    3233        super();
     
    6465            const cookie = null;
    6566            const options = {ignoreNetworkTab: true, ignoreSearchTab: true};
    66             let overrideResource = WI.networkManager.localResourceOverrideForURL(this._resource.url);
    67             WI.showRepresentedObject(overrideResource, cookie, options);
     67            let localResourceOverride = WI.networkManager.localResourceOverridesForURL(this._resource.url)[0];
     68            WI.showRepresentedObject(localResourceOverride, cookie, options);
    6869        });
    6970
     
    8182            return;
    8283
    83         let hasLocalResourceOverride = !!WI.networkManager.localResourceOverrideForURL(this._resource.url);
    84         this._revealButton.hidden = !hasLocalResourceOverride;
     84        this._revealButton.hidden = !WI.networkManager.localResourceOverridesForURL(this._resource.url).length;
    8585
    8686        let resourceShowingOverrideContent = this._resource.responseSource === WI.Resource.ResponseSource.InspectorOverride;
     
    9090    _handleLocalResourceOverrideAddedOrRemoved(event)
    9191    {
    92         if (this._resource.url !== event.data.localResourceOverride.url)
     92        let {localResourceOverride} = event.data;
     93        if (!localResourceOverride.matches(this._resource.url))
    9394            return;
    9495
  • trunk/Source/WebInspectorUI/UserInterface/Views/NavigationSidebarPanel.js

    r270134 r270604  
    502502                // Local Overrides are never stale resources.
    503503                let resource = treeElement.resource;
    504                 if (resource.isLocalResourceOverride)
     504                if (resource.localResourceOverride)
    505505                    continue;
    506506
  • trunk/Source/WebInspectorUI/UserInterface/Views/OpenResourceDialog.js

    r267723 r270604  
    125125            treeElement.mainTitle = createHighlightedTitleFragment(resource.displayName, result.matchingTextRanges);
    126126
    127             if (resource instanceof WI.LocalResource && resource.isLocalResourceOverride)
     127            if (resource instanceof WI.LocalResource && resource.localResourceOverride)
    128128                treeElement.subtitle = WI.UIString("Local Override");
    129129
  • trunk/Source/WebInspectorUI/UserInterface/Views/ResourceContentView.js

    r269359 r270604  
    5959        }
    6060
    61         this._showingLocalResourceOverride = false;
    62 
    6361        if (WI.NetworkManager.supportsOverridingResponses()) {
    64             if (resource.isLocalResourceOverride) {
    65                 this._showingLocalResourceOverride = true;
    66 
    67                 this._localResourceOverrideBannerView = new WI.LocalResourceOverrideLabelView(resource);
     62            if (resource.localResourceOverride) {
     63                this._localResourceOverrideBannerView = new WI.LocalResourceOverrideLabelView(resource.localResourceOverride);
    6864
    6965                this._importLocalResourceOverrideButtonNavigationItem = new WI.ButtonNavigationItem("import-local-resource-override", WI.UIString("Import"), "Images/Import.svg", 15, 15);
     
    7975
    8076                this._createLocalResourceOverrideButtonNavigationItem = new WI.ButtonNavigationItem("create-local-resource-override", this.createLocalResourceOverrideTooltip, "Images/NavigationItemNetworkOverride.svg", 13, 14);
    81                 this._createLocalResourceOverrideButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleCreateLocalResourceOverride, this);
    8277                this._createLocalResourceOverrideButtonNavigationItem.enabled = false; // Enabled when the content is available.
    8378                this._createLocalResourceOverrideButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
     79                if (WI.NetworkManager.supportsOverridingRequests())
     80                    WI.addMouseDownContextMenuHandlers(this._createLocalResourceOverrideButtonNavigationItem.element, this._populateCreateLocalResourceOverrideContextMenu.bind(this));
     81                else
     82                    this._createLocalResourceOverrideButtonNavigationItem.addEventListener(WI.ButtonNavigationItem.Event.Clicked, this._handleCreateLocalResourceOverride, this);
    8483            }
    8584
     
    9291
    9392    get resource() { return this._resource; }
    94     get showingLocalResourceOverride() { return this._showingLocalResourceOverride; }
    9593
    9694    get navigationItems()
     
    139137    }
    140138
    141     requestLocalResourceOverrideInitialContent(callback)
     139    requestLocalResourceOverrideInitialContent()
    142140    {
    143141        // Overridden by subclasses if needed.
    144142
    145         WI.FileUtilities.import(async (fileList) => {
    146             console.assert(fileList.length === 1);
    147 
    148             this._getContentForLocalResourceOverrideFromFile(fileList[0], ({mimeType, base64Encoded, content}) => {
    149                 callback({mimeType, base64Encoded, content});
     143        return new Promise((resolve, reject) => {
     144            WI.FileUtilities.import(async (fileList) => {
     145                console.assert(fileList.length === 1);
     146
     147                this._getContentForLocalResourceOverrideFromFile(fileList[0], ({mimeType, base64Encoded, content}) => {
     148                    resolve({mimeType, base64Encoded, content});
     149                });
    150150            });
    151151        });
     
    296296    }
    297297
     298    async _createAndShowLocalResourceOverride(type, {requestInitialContent} = {})
     299    {
     300        let initialContent = requestInitialContent ? await this.requestLocalResourceOverrideInitialContent() : {};
     301        let localResourceOverride = await this._resource.createLocalResourceOverride(type, initialContent);
     302        WI.networkManager.addLocalResourceOverride(localResourceOverride);
     303        WI.showLocalResourceOverride(localResourceOverride);
     304    }
     305
     306    _populateCreateLocalResourceOverrideContextMenu(contextMenu, event)
     307    {
     308        contextMenu.appendItem(WI.UIString("Create Request Local Override"), () => {
     309            // Request overrides cannot be created from a file as files don't have network info.
     310            this._createAndShowLocalResourceOverride(WI.LocalResourceOverride.InterceptType.Request);
     311        });
     312
     313        contextMenu.appendItem(WI.UIString("Create Response Local Override"), () => {
     314            this._createAndShowLocalResourceOverride(WI.LocalResourceOverride.InterceptType.Response, {
     315                requestInitialContent: !event.shiftKey,
     316            });
     317        });
     318    }
     319
    298320    _handleCreateLocalResourceOverride(event)
    299321    {
    300322        let {nativeEvent} = event.data;
    301323
    302         let createLocalResourceOverride = async (initialContent) => {
    303             let localResourceOverride = await this._resource.createLocalResourceOverride(initialContent);
    304             WI.networkManager.addLocalResourceOverride(localResourceOverride);
    305             WI.showLocalResourceOverride(localResourceOverride);
    306         };
    307 
    308         if (nativeEvent.shiftKey)
    309             createLocalResourceOverride({});
    310         else
    311             this.requestLocalResourceOverrideInitialContent(createLocalResourceOverride);
     324        this._createAndShowLocalResourceOverride(WI.LocalResourceOverride.InterceptType.Response, {
     325            requestInitialContent: !nativeEvent.shiftKey,
     326        });
    312327    }
    313328
    314329    _handleImportLocalResourceOverride(event)
    315330    {
    316         console.assert(this._showingLocalResourceOverride);
     331        let localResourceOverride = this.resource.localResourceOverride || WI.networkManager.localResourceOverridesForURL(this.resource.url)[0];
     332        console.assert(localResourceOverride);
    317333
    318334        WI.FileUtilities.import(async (fileList) => {
    319335            console.assert(fileList.length === 1);
    320 
    321             let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this.resource.url);
    322             console.assert(localResourceOverride);
    323336
    324337            await this._getContentForLocalResourceOverrideFromFile(fileList[0], ({mimeType, base64Encoded, content}) => {
     
    327340            });
    328341
    329             if (!this.showingLocalResourceOverride)
     342            if (!this._resource.localResourceOverride)
    330343                WI.showLocalResourceOverride(localResourceOverride);
    331344        });
     
    334347    _handleRemoveLocalResourceOverride(event)
    335348    {
    336         console.assert(this._showingLocalResourceOverride);
    337 
    338         let localResourceOverride = WI.networkManager.localResourceOverrideForURL(this._resource.url);
     349        let localResourceOverride = this.resource.localResourceOverride || WI.networkManager.localResourceOverridesForURL(this._resource.url)[0];
     350        console.assert(localResourceOverride);
    339351        WI.networkManager.removeLocalResourceOverride(localResourceOverride);
    340352    }
     
    342354    _handleLocalResourceOverrideChanged(event)
    343355    {
    344         if (this._resource.url !== event.data.localResourceOverride.url)
     356        let {localResourceOverride} = event.data;
     357        if (!localResourceOverride.matches(this._resource.url))
    345358            return;
    346359
  • trunk/Source/WebInspectorUI/UserInterface/Views/ResourceTreeElement.js

    r267411 r270604  
    164164    get mainTitleText()
    165165    {
     166        // Overridden by subclasses if needed.
    166167        return WI.displayNameForURL(this._resource.url, this._resource.urlComponents, {
    167168            allowDirectoryAsName: this._allowDirectoryAsName,
     
    190191
    191192        if (!this._hideOrigin) {
    192             if (this._resource.isLocalResourceOverride) {
    193                 // Show the host for a local resource override if it is different from the main frame.
    194                 let localResourceOverrideHost = urlComponents.host;
    195                 let mainFrameHost = (WI.networkManager.mainFrame && WI.networkManager.mainFrame.mainResource) ? WI.networkManager.mainFrame.mainResource.urlComponents.host : null;
    196                 let subtitle = localResourceOverrideHost !== mainFrameHost ? localResourceOverrideHost : null;
    197                 this.subtitle = this.mainTitle !== subtitle ? subtitle : null;
     193            if (this._resource.localResourceOverride) {
     194                if (WI.NetworkManager.supportsOverridingRequests())
     195                    this.subtitle = WI.LocalResourceOverride.displayNameForType(this._resource.localResourceOverride.type);
     196                else {
     197                    // Show the host for a local resource override if it is different from the main frame.
     198                    let localResourceOverrideHost = urlComponents.host;
     199                    let mainFrameHost = (WI.networkManager.mainFrame && WI.networkManager.mainFrame.mainResource) ? WI.networkManager.mainFrame.mainResource.urlComponents.host : null;
     200                    let subtitle = localResourceOverrideHost !== mainFrameHost ? localResourceOverrideHost : null;
     201                    this.subtitle = this.mainTitle !== subtitle ? subtitle : null;
     202                }
    198203            } else {
    199204                // Show the host as the subtitle if it is different from the main resource or if this is the main frame's main resource.
     
    256261    _updateIcon()
    257262    {
    258         let isOverride = this._resource.isLocalResourceOverride;
     263        let isOverride = !!this._resource.localResourceOverride;
    259264        let wasOverridden = this._resource.responseSource === WI.Resource.ResponseSource.InspectorOverride;
    260 
    261265        if (isOverride || wasOverridden)
    262266            this.addClassName("override");
  • trunk/Source/WebInspectorUI/UserInterface/Views/SourceCodeTextEditor.js

    r270134 r270604  
    12151215    {
    12161216        if (this._sourceCode instanceof WI.Resource) {
    1217             if (this._sourceCode.isLocalResourceOverride)
     1217            if (this._sourceCode.localResourceOverride)
    12181218                return false;
    12191219            return this._sourceCode.type === WI.Resource.Type.Document || this._sourceCode.type === WI.Resource.Type.Script;
  • trunk/Source/WebInspectorUI/UserInterface/Views/SourcesNavigationSidebarPanel.js

    r269754 r270604  
    542542
    543543        if (representedObject instanceof WI.LocalResource) {
    544             let localResourceOverride = WI.networkManager.localResourceOverrideForURL(representedObject.url);
     544            let localResourceOverride = representedObject.localResourceOverride || WI.networkManager.localResourceOverridesForURL(representedObject.url)[0];
    545545            return this._localOverridesTreeOutline.findTreeElement(localResourceOverride);
    546546        }
     
    852852        }
    853853
    854         let {type, url, isCaseSensitive, isRegex, mimeType, statusCode, statusText, headers} = serializedData;
    855 
    856854        // Do not conflict with an existing override.
    857         let existingOverride = WI.networkManager.localResourceOverrideForURL(url);
    858         if (existingOverride) {
     855        if (WI.networkManager.localResourceOverrides.some((existingOverride) => existingOverride.equals(serializedData))) {
    859856            InspectorFrontendHost.beep();
    860857            return;
    861858        }
    862859
    863         let localResourceOverride = WI.LocalResourceOverride.create(type, {
    864             url,
    865             isCaseSensitive,
    866             isRegex,
    867             mimeType,
    868             statusCode,
    869             statusText,
    870             headers,
    871             content: "",
    872             base64Encoded: false,
    873         });
    874 
     860        let localResourceOverride = WI.LocalResourceOverride.create(serializedData.url, serializedData.type, serializedData);
    875861        WI.networkManager.addLocalResourceOverride(localResourceOverride);
    876862        WI.showLocalResourceOverride(localResourceOverride);
     
    956942    {
    957943        // Local Resource Override content views do not need to be closed across page navigations.
    958         if (contentView.representedObject instanceof WI.LocalResource && contentView.representedObject.isLocalResourceOverride)
     944        if (contentView.representedObject instanceof WI.LocalResource && contentView.representedObject.localResourceOverride)
    959945            return false;
    960946
     
    14891475            localOverrideTreeElement = new WI.BootstrapScriptTreeElement(localOverride);
    14901476        else if (localOverride instanceof WI.LocalResourceOverride)
    1491             localOverrideTreeElement = new WI.LocalResourceOverrideTreeElement(localOverride.localResource, localOverride);
     1477            localOverrideTreeElement = new WI.LocalResourceOverrideTreeElement(localOverride);
    14921478        console.assert(localOverrideTreeElement);
    14931479
     
    19391925
    19401926        if (treeElement instanceof WI.LocalResourceOverrideTreeElement) {
    1941             WI.showRepresentedObject(treeElement.representedObject.localResource);
     1927            WI.showRepresentedObject(treeElement.representedObject);
    19421928            return;
    19431929        }
  • trunk/Source/WebInspectorUI/UserInterface/Views/TextResourceContentView.js

    r270134 r270604  
    7979        items.push(this._prettyPrintButtonNavigationItem);
    8080
    81         if (!this.showingLocalResourceOverride)
     81        if (!this.resource.localResourceOverride)
    8282            items.push(this._showTypesButtonNavigationItem, this._codeCoverageButtonNavigationItem);
    8383
     
    136136    }
    137137
    138     requestLocalResourceOverrideInitialContent(callback)
    139     {
    140         callback({
     138    requestLocalResourceOverrideInitialContent()
     139    {
     140        return Promise.resolve({
    141141            mimeType: this.resource.mimeType,
    142142            base64Encoded: this.resource.base64Encoded,
     
    327327            return true;
    328328
    329         if (this.showingLocalResourceOverride)
     329        if (this.resource.localResourceOverride)
    330330            return true;
    331331
Note: See TracChangeset for help on using the changeset viewer.