Changeset 293409 in webkit


Ignore:
Timestamp:
Apr 25, 2022 9:07:01 PM (3 months ago)
Author:
Devin Rousso
Message:

Web Inspector: add UI for blocking requests
https://bugs.webkit.org/show_bug.cgi?id=239674

Reviewed by Patrick Angle.

Source/WebCore:

Test: http/tests/inspector/network/intercept-request-with-error.html

  • inspector/agents/InspectorNetworkAgent.h:

(WebCore::InspectorNetworkAgent::errorDomain): Added.

  • inspector/agents/InspectorNetworkAgent.cpp:

(WebCore::InspectorNetworkAgent::toResourceErrorType): Added.
(WebCore::InspectorNetworkAgent::interceptRequestWithError):
Unify the resource error message for consistency.
Mark the resource error as coming from Web Inspector so that it can identified (see below).

  • inspector/agents/WebConsoleAgent.cpp:

(WebCore::WebConsoleAgent::didFailLoading):

  • loader/DocumentThreadableLoader.cpp:

(WebCore::DocumentThreadableLoader::logErrorAndFail):

  • loader/SubresourceLoader.cpp:

(WebCore::SubresourceLoader::didFail):
Don't add console messages for requests that were blocked by Web Inspector.

  • inspector/agents/page/PageNetworkAgent.h:
  • inspector/agents/page/PageNetworkAgent.cpp:

(WebCore::PageNetworkAgent::addConsoleMessage): Added.

  • inspector/agents/worker/WorkerNetworkAgent.h:
  • inspector/agents/worker/WorkerNetworkAgent.cpp:

(WebCore::WorkerNetworkAgent::addConsoleMessage): Added.
Add a virtual method so that InspectorNetworkAgent can log to the console (PageNetworkAgent
uses the Page, WorkerNetworkAgent uses the WorkerOrWorkletGlobalScope, etc.).

Source/WebInspectorUI:

  • UserInterface/Models/LocalResourceOverride.js:

(WI.LocalResourceOverride):
(WI.LocalResourceOverride.create):
(WI.LocalResourceOverride.displayNameForNetworkStageOfType): Added.
(WI.LocalResourceOverride.displayNameForType):
(WI.LocalResourceOverride.displayNameForResourceErrorType): Added.
(WI.LocalResourceOverride.fromJSON):
(WI.LocalResourceOverride.prototype.toJSON):
(WI.LocalResourceOverride.prototype.get resourceErrorType): Added.
(WI.LocalResourceOverride.prototype.set resourceErrorType): Added.
(WI.LocalResourceOverride.prototype.get canMapToFile):
Add WI.LocalResourceOverride.ResourceErrorType (and a corresponding member variable) that
is be used when blocking matching requests.

  • UserInterface/Controllers/NetworkManager.js:

(WI.NetworkManager.supportsBlockingRequests): Added.
(WI.NetworkManager.prototype.async requestIntercepted):
(WI.NetworkManager.prototype._handleResourceOverrideResourceErrorTypeChanged): Added.
Make sure to save WI.LocalResourceOverride.InterceptType.Block whenever the
WI.LocalResourceOverride.ResourceErrorType changes.

  • UserInterface/Views/ContextMenuUtilities.js:

(WI.appendContextMenuItemsForSourceCode):

  • UserInterface/Views/ResourceContentView.js:

(WI.ResourceContentView):
(WI.ResourceContentView.prototype._populateCreateLocalResourceOverrideContextMenu):
(WI.ResourceContentView.prototype._handleCreateLocalResourceOverride): Deleted.
Add contextmenu items for "Block Request URL".

  • UserInterface/Views/LocalResourceOverrideRequestContentView.js:

(WI.LocalResourceOverrideRequestContentView):
(WI.LocalResourceOverrideRequestContentView.prototype.initialLayout):
(WI.LocalResourceOverrideRequestContentView.prototype.initialLayout.addOption): Added.

  • UserInterface/Views/LocalResourceOverrideRequestContentView.css:

(.content-view.text.local-resource-override-request > .message-text-view select): Added.
There will be no request or response content for WI.LocalResourceOverride.InterceptType.Block
so show a <select> for choosing the WI.LocalResourceOverride.ResourceErrorType.

  • UserInterface/Views/LocalResourceOverridePopover.js:

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

  • UserInterface/Views/LocalResourceOverridePopover.css:

(.popover .local-resource-override-popover-content:is(.response, .block) .editor.url): Renamed from .popover .local-resource-override-popover-content.response .editor.url.
Only show the URL editor for WI.LocalResourceOverride.InterceptType.Block.

  • UserInterface/Models/Resource.js:

(WI.Resource.classNamesForResource):

  • UserInterface/Views/ResourceTreeElement.js:

(WI.ResourceTreeElement.prototype._updateResource):
(WI.ResourceTreeElement.prototype._updateIcon):
(WI.ResourceTreeElement.prototype._loadingDidFail): Added.

  • UserInterface/Views/ResourceIcons.css:

(.resource-icon.override.skip-network .icon): Added.
(body:not(.window-inactive, .window-docked-inactive) :is(.table, .data-grid):focus-within .selected .resource-icon.override.skip-network .icon, body:not(.window-inactive, .window-docked-inactive) .tree-outline:focus-within .selected.resource-icon.override.skip-network .icon): Added.
(@media (prefers-color-scheme: dark) .resource-icon.override.skip-network .icon): Added.

  • UserInterface/Images/SkipNetwork.svg: Added.

Add a new icon for when requests do not involve any network activity.

  • UserInterface/Views/ContentView.js:

(WI.ContentView.createFromRepresentedObject):
(WI.ContentView.resolvedRepresentedObjectForRepresentedObject):

  • UserInterface/Views/FontResourceContentView.js:

(WI.FontResourceContentView.prototype.dropZoneShouldAppearForDragEvent):

  • UserInterface/Views/ImageResourceContentView.js:

(WI.ImageResourceContentView.prototype.dropZoneShouldAppearForDragEvent):

  • UserInterface/Views/LocalResourceOverrideTreeElement.js:

(WI.LocalResourceOverrideTreeElement.prototype.willDismissPopover):
Use positive checks for the desired WI.LocalResourceOverride.InterceptType instead of
a negative check for the one value meant to be exlucded in the expectation that no new
enum values would get added (which this patch proves to be false).

  • UserInterface/Views/LocalResourceOverrideLabelView.js:

(WI.LocalResourceOverrideLabelView.prototype.initialLayout):
Use the new WI.LocalResourceOverride.displayNameForNetworkStageOfType instead of doing
that work here.

  • Localizations/en.lproj/localizedStrings.js:

LayoutTests:

  • http/tests/inspector/network/intercept-request-with-error.html: Added.
  • http/tests/inspector/network/intercept-request-with-error-expected.txt: Added.
  • inspector/network/interceptRequestWithError.html: Removed.
  • inspector/network/interceptRequestWithError-expected.txt: Removed.
  • platform/mac-wk1/TestExpectations:
  • platform/mac-wk2/TestExpectations:

Reworked this test to use WI.LocalResourceOverride instead of the protocol.

Location:
trunk
Files:
3 added
2 deleted
31 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r293367 r293409  
     12022-04-25  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: add UI for blocking requests
     4        https://bugs.webkit.org/show_bug.cgi?id=239674
     5
     6        Reviewed by Patrick Angle.
     7
     8        * http/tests/inspector/network/intercept-request-with-error.html: Added.
     9        * http/tests/inspector/network/intercept-request-with-error-expected.txt: Added.
     10        * inspector/network/interceptRequestWithError.html: Removed.
     11        * inspector/network/interceptRequestWithError-expected.txt: Removed.
     12        * platform/mac-wk1/TestExpectations:
     13        * platform/mac-wk2/TestExpectations:
     14        Reworked this test to use `WI.LocalResourceOverride` instead of the protocol.
     15
    1162022-04-25  Devin Rousso  <drousso@apple.com>
    217
  • trunk/LayoutTests/platform/mac-wk1/TestExpectations

    r293367 r293409  
    925925http/tests/inspector/network/intercept-request-subresource-with-response.html [ Skip ]
    926926http/tests/inspector/network/intercept-request-subresource-with-response-error-status-code.html [ Skip ]
     927http/tests/inspector/network/intercept-request-with-error.html [ Skip ]
    927928http/tests/inspector/network/intercept-request-with-response.html [ Skip ]
    928929http/tests/inspector/network/local-resource-override-basic.html [ Skip ]
     
    933934inspector/network/intercept-aborted-request.html [ Skip ]
    934935inspector/network/interceptContinue.html [ Skip ]
    935 inspector/network/interceptRequestWithError.html [ Skip ]
    936936
    937937webkit.org/b/164933 http/tests/misc/link-rel-icon-beforeload.html [ Failure ]
  • trunk/LayoutTests/platform/mac-wk2/TestExpectations

    r293223 r293409  
    15871587webkit.org/b/230117 [ BigSur Debug ] http/tests/inspector/network/fetch-response-body.html [ Pass Failure ]
    15881588webkit.org/b/230117 [ BigSur Debug ] http/tests/inspector/network/intercept-request-properties.html [ Pass Failure ]
     1589webkit.org/b/230117 [ BigSur Debug ] http/tests/inspector/network/intercept-request-with-error.html [ Pass Failure ]
    15891590webkit.org/b/230117 [ BigSur Debug ] http/tests/inspector/network/intercept-request-with-response.html [ Pass Failure ]
    15901591webkit.org/b/230117 [ BigSur Debug ] http/tests/inspector/target/pause-on-inline-debugger-statement.html [ Pass Failure ]
  • trunk/Source/WebCore/ChangeLog

    r293368 r293409  
     12022-04-25  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: add UI for blocking requests
     4        https://bugs.webkit.org/show_bug.cgi?id=239674
     5
     6        Reviewed by Patrick Angle.
     7
     8        Test: http/tests/inspector/network/intercept-request-with-error.html
     9
     10        * inspector/agents/InspectorNetworkAgent.h:
     11        (WebCore::InspectorNetworkAgent::errorDomain): Added.
     12        * inspector/agents/InspectorNetworkAgent.cpp:
     13        (WebCore::InspectorNetworkAgent::toResourceErrorType): Added.
     14        (WebCore::InspectorNetworkAgent::interceptRequestWithError):
     15        Unify the resource error message for consistency.
     16        Mark the resource error as coming from Web Inspector so that it can identified (see below).
     17
     18        * inspector/agents/WebConsoleAgent.cpp:
     19        (WebCore::WebConsoleAgent::didFailLoading):
     20        * loader/DocumentThreadableLoader.cpp:
     21        (WebCore::DocumentThreadableLoader::logErrorAndFail):
     22        * loader/SubresourceLoader.cpp:
     23        (WebCore::SubresourceLoader::didFail):
     24        Don't add console messages for requests that were blocked by Web Inspector.
     25
     26        * inspector/agents/page/PageNetworkAgent.h:
     27        * inspector/agents/page/PageNetworkAgent.cpp:
     28        (WebCore::PageNetworkAgent::addConsoleMessage): Added.
     29        * inspector/agents/worker/WorkerNetworkAgent.h:
     30        * inspector/agents/worker/WorkerNetworkAgent.cpp:
     31        (WebCore::WorkerNetworkAgent::addConsoleMessage): Added.
     32        Add a `virtual` method so that `InspectorNetworkAgent` can log to the console (`PageNetworkAgent`
     33        uses the `Page`, `WorkerNetworkAgent` uses the `WorkerOrWorkletGlobalScope`, etc.).
     34
    1352022-04-25  Devin Rousso  <drousso@apple.com>
    236
  • trunk/Source/WebCore/inspector/agents/InspectorNetworkAgent.cpp

    r293368 r293409  
    7070#include "TextResourceDecoder.h"
    7171#include "ThreadableLoaderClient.h"
     72#include "WebConsoleAgent.h"
    7273#include <wtf/URL.h>
    7374#include "WebSocket.h"
     
    13121313}
    13131314
     1315static ResourceError::Type toResourceErrorType(Protocol::Network::ResourceErrorType protocolResourceErrorType)
     1316{
     1317    switch (protocolResourceErrorType) {
     1318    case Protocol::Network::ResourceErrorType::General:
     1319        return ResourceError::Type::General;
     1320
     1321    case Protocol::Network::ResourceErrorType::AccessControl:
     1322        return ResourceError::Type::AccessControl;
     1323
     1324    case Protocol::Network::ResourceErrorType::Cancellation:
     1325        return ResourceError::Type::Cancellation;
     1326
     1327    case Protocol::Network::ResourceErrorType::Timeout:
     1328        return ResourceError::Type::Timeout;
     1329    }
     1330
     1331    ASSERT_NOT_REACHED();
     1332    return ResourceError::Type::Null;
     1333}
     1334
    13141335Protocol::ErrorStringOr<void> InspectorNetworkAgent::interceptRequestWithError(const Protocol::Network::RequestId& requestId, Protocol::Network::ResourceErrorType errorType)
    13151336{
     
    13221343        return makeUnexpected("Unable to abort request, it has already been processed"_s);
    13231344
    1324     switch (errorType) {
    1325     case Protocol::Network::ResourceErrorType::General:
    1326         loader.didFail(ResourceError(errorDomainWebKitInternal, 0, loader.url(), "Request intercepted"_s, ResourceError::Type::General));
    1327         return { };
    1328 
    1329     case Protocol::Network::ResourceErrorType::AccessControl:
    1330         loader.didFail(ResourceError(errorDomainWebKitInternal, 0, loader.url(), "Access denied"_s, ResourceError::Type::AccessControl));
    1331         return { };
    1332 
    1333     case Protocol::Network::ResourceErrorType::Cancellation:
    1334         loader.didFail(ResourceError(errorDomainWebKitInternal, 0, loader.url(), "Request canceled"_s, ResourceError::Type::Cancellation));
    1335         return { };
    1336 
    1337     case Protocol::Network::ResourceErrorType::Timeout:
    1338         loader.didFail(ResourceError(errorDomainWebKitInternal, 0, loader.url(), "Request timed out"_s, ResourceError::Type::Timeout));
    1339         return { };
    1340     }
    1341 
    1342     ASSERT_NOT_REACHED();
     1345    addConsoleMessage(makeUnique<Inspector::ConsoleMessage>(MessageSource::Network, MessageType::Log, MessageLevel::Info, makeString("Web Inspector blocked ", loader.url().string(), " from loading"), loader.identifier().toUInt64()));
     1346
     1347    loader.didFail(ResourceError(InspectorNetworkAgent::errorDomain(), 0, loader.url(), "Blocked by Web Inspector"_s, toResourceErrorType(errorType)));
    13431348    return { };
    13441349}
  • trunk/Source/WebCore/inspector/agents/InspectorNetworkAgent.h

    r293368 r293409  
    4444
    4545namespace Inspector {
     46class ConsoleMessage;
    4647class InjectedScriptManager;
    4748}
     
    6970public:
    7071    ~InspectorNetworkAgent() override;
     72
     73    static constexpr ASCIILiteral errorDomain() { return "InspectorNetworkAgent"_s; }
    7174
    7275    static bool shouldTreatAsText(const String& mimeType);
     
    139142    virtual void setResourceCachingDisabledInternal(bool) = 0;
    140143    virtual ScriptExecutionContext* scriptExecutionContext(Inspector::Protocol::ErrorString&, const Inspector::Protocol::Network::FrameId&) = 0;
     144    virtual void addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&&) = 0;
    141145    virtual bool shouldForceBufferingNetworkResourceData() const = 0;
    142146
  • trunk/Source/WebCore/inspector/agents/WebConsoleAgent.cpp

    r287486 r293409  
    3030#include "CommandLineAPIHost.h"
    3131#include "DOMWindow.h"
     32#include "InspectorNetworkAgent.h"
    3233#include "InspectorWebAgentBase.h"
    3334#include "JSExecState.h"
     
    7172void WebConsoleAgent::didFailLoading(ResourceLoaderIdentifier requestIdentifier, const ResourceError& error)
    7273{
     74    if (error.domain() == InspectorNetworkAgent::errorDomain())
     75        return;
     76
    7377    // Report failures only.
    7478    if (error.isCancellation())
  • trunk/Source/WebCore/inspector/agents/page/PageNetworkAgent.cpp

    r287327 r293409  
    3232#include "InstrumentingAgents.h"
    3333#include "Page.h"
     34#include "PageConsoleClient.h"
    3435#include "WebSocket.h"
    3536#include "WebSocketChannel.h"
     
    117118}
    118119
     120void PageNetworkAgent::addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&& message)
     121{
     122    m_inspectedPage.console().addMessage(WTFMove(message));
     123}
     124
    119125} // namespace WebCore
  • trunk/Source/WebCore/inspector/agents/page/PageNetworkAgent.h

    r277880 r293409  
    4545    void setResourceCachingDisabledInternal(bool);
    4646    ScriptExecutionContext* scriptExecutionContext(Inspector::Protocol::ErrorString&, const Inspector::Protocol::Network::FrameId&);
     47    void addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&&);
    4748    bool shouldForceBufferingNetworkResourceData() const { return false; }
    4849
  • trunk/Source/WebCore/inspector/agents/worker/WorkerNetworkAgent.cpp

    r277880 r293409  
    7272}
    7373
     74void WorkerNetworkAgent::addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&& message)
     75{
     76    m_globalScope.addConsoleMessage(WTFMove(message));
     77}
     78
    7479} // namespace WebCore
  • trunk/Source/WebCore/inspector/agents/worker/WorkerNetworkAgent.h

    r277880 r293409  
    4343    void setResourceCachingDisabledInternal(bool);
    4444    ScriptExecutionContext* scriptExecutionContext(Inspector::Protocol::ErrorString&, const Inspector::Protocol::Network::FrameId&);
     45    void addConsoleMessage(std::unique_ptr<Inspector::ConsoleMessage>&&);
    4546    bool shouldForceBufferingNetworkResourceData() const { return true; }
    4647
  • trunk/Source/WebCore/loader/DocumentThreadableLoader.cpp

    r292793 r293409  
    4545#include "FrameLoader.h"
    4646#include "InspectorInstrumentation.h"
     47#include "InspectorNetworkAgent.h"
    4748#include "LegacySchemeRegistry.h"
    4849#include "LoaderStrategy.h"
     
    755756{
    756757    if (m_shouldLogError == ShouldLogError::Yes) {
    757         if (error.isAccessControl() && !error.localizedDescription().isEmpty())
     758        if (error.isAccessControl() && error.domain() != InspectorNetworkAgent::errorDomain() && !error.localizedDescription().isEmpty())
    758759            m_document.addConsoleMessage(MessageSource::Security, MessageLevel::Error, error.localizedDescription());
    759760        logError(m_document, error, m_options.initiator);
  • trunk/Source/WebCore/loader/SubresourceLoader.cpp

    r291992 r293409  
    4040#include "FrameLoader.h"
    4141#include "HTTPParsers.h"
     42#include "InspectorNetworkAgent.h"
    4243#include "LinkLoader.h"
    4344#include "Logging.h"
     
    767768    LOG(ResourceLoading, "Failed to load '%s'.\n", m_resource->url().string().latin1().data());
    768769
    769     if (m_frame->document() && error.isAccessControl() && m_resource->type() != CachedResource::Type::Ping)
     770    if (m_frame->document() && error.isAccessControl() && error.domain() != InspectorNetworkAgent::errorDomain() && m_resource->type() != CachedResource::Type::Ping)
    770771        m_frame->document()->addConsoleMessage(MessageSource::Security, MessageLevel::Error, error.localizedDescription());
    771772
  • trunk/Source/WebInspectorUI/ChangeLog

    r293338 r293409  
     12022-04-25  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: add UI for blocking requests
     4        https://bugs.webkit.org/show_bug.cgi?id=239674
     5
     6        Reviewed by Patrick Angle.
     7
     8        * UserInterface/Models/LocalResourceOverride.js:
     9        (WI.LocalResourceOverride):
     10        (WI.LocalResourceOverride.create):
     11        (WI.LocalResourceOverride.displayNameForNetworkStageOfType): Added.
     12        (WI.LocalResourceOverride.displayNameForType):
     13        (WI.LocalResourceOverride.displayNameForResourceErrorType): Added.
     14        (WI.LocalResourceOverride.fromJSON):
     15        (WI.LocalResourceOverride.prototype.toJSON):
     16        (WI.LocalResourceOverride.prototype.get resourceErrorType): Added.
     17        (WI.LocalResourceOverride.prototype.set resourceErrorType): Added.
     18        (WI.LocalResourceOverride.prototype.get canMapToFile):
     19        Add `WI.LocalResourceOverride.ResourceErrorType` (and a corresponding member variable) that
     20        is be used when blocking matching requests.
     21
     22        * UserInterface/Controllers/NetworkManager.js:
     23        (WI.NetworkManager.supportsBlockingRequests): Added.
     24        (WI.NetworkManager.prototype.async requestIntercepted):
     25        (WI.NetworkManager.prototype._handleResourceOverrideResourceErrorTypeChanged): Added.
     26        Make sure to save `WI.LocalResourceOverride.InterceptType.Block` whenever the
     27        `WI.LocalResourceOverride.ResourceErrorType` changes.
     28
     29        * UserInterface/Views/ContextMenuUtilities.js:
     30        (WI.appendContextMenuItemsForSourceCode):
     31        * UserInterface/Views/ResourceContentView.js:
     32        (WI.ResourceContentView):
     33        (WI.ResourceContentView.prototype._populateCreateLocalResourceOverrideContextMenu):
     34        (WI.ResourceContentView.prototype._handleCreateLocalResourceOverride): Deleted.
     35        Add contextmenu items for "Block Request URL".
     36
     37        * UserInterface/Views/LocalResourceOverrideRequestContentView.js:
     38        (WI.LocalResourceOverrideRequestContentView):
     39        (WI.LocalResourceOverrideRequestContentView.prototype.initialLayout):
     40        (WI.LocalResourceOverrideRequestContentView.prototype.initialLayout.addOption): Added.
     41        * UserInterface/Views/LocalResourceOverrideRequestContentView.css:
     42        (.content-view.text.local-resource-override-request > .message-text-view select): Added.
     43        There will be no request or response content for `WI.LocalResourceOverride.InterceptType.Block`
     44        so show a `<select>` for choosing the `WI.LocalResourceOverride.ResourceErrorType`.
     45
     46        * UserInterface/Views/LocalResourceOverridePopover.js:
     47        (WI.LocalResourceOverridePopover.prototype.get serializedData):
     48        (WI.LocalResourceOverridePopover.prototype.show):
     49        * UserInterface/Views/LocalResourceOverridePopover.css:
     50        (.popover .local-resource-override-popover-content:is(.response, .block) .editor.url): Renamed from `.popover .local-resource-override-popover-content.response .editor.url`.
     51        Only show the URL editor for `WI.LocalResourceOverride.InterceptType.Block`.
     52
     53        * UserInterface/Models/Resource.js:
     54        (WI.Resource.classNamesForResource):
     55        * UserInterface/Views/ResourceTreeElement.js:
     56        (WI.ResourceTreeElement.prototype._updateResource):
     57        (WI.ResourceTreeElement.prototype._updateIcon):
     58        (WI.ResourceTreeElement.prototype._loadingDidFail): Added.
     59        * UserInterface/Views/ResourceIcons.css:
     60        (.resource-icon.override.skip-network .icon): Added.
     61        (body:not(.window-inactive, .window-docked-inactive) :is(.table, .data-grid):focus-within .selected .resource-icon.override.skip-network .icon, body:not(.window-inactive, .window-docked-inactive) .tree-outline:focus-within .selected.resource-icon.override.skip-network .icon): Added.
     62        (@media (prefers-color-scheme: dark) .resource-icon.override.skip-network .icon): Added.
     63        * UserInterface/Images/SkipNetwork.svg: Added.
     64        Add a new icon for when requests do not involve any network activity.
     65
     66        * UserInterface/Views/ContentView.js:
     67        (WI.ContentView.createFromRepresentedObject):
     68        (WI.ContentView.resolvedRepresentedObjectForRepresentedObject):
     69        * UserInterface/Views/FontResourceContentView.js:
     70        (WI.FontResourceContentView.prototype.dropZoneShouldAppearForDragEvent):
     71        * UserInterface/Views/ImageResourceContentView.js:
     72        (WI.ImageResourceContentView.prototype.dropZoneShouldAppearForDragEvent):
     73        * UserInterface/Views/LocalResourceOverrideTreeElement.js:
     74        (WI.LocalResourceOverrideTreeElement.prototype.willDismissPopover):
     75        Use positive checks for the desired `WI.LocalResourceOverride.InterceptType` instead of
     76        a negative check for the one value meant to be exlucded in the expectation that no new
     77        enum values would get added (which this patch proves to be false).
     78
     79        * UserInterface/Views/LocalResourceOverrideLabelView.js:
     80        (WI.LocalResourceOverrideLabelView.prototype.initialLayout):
     81        Use the new `WI.LocalResourceOverride.displayNameForNetworkStageOfType` instead of doing
     82        that work here.
     83
     84        * Localizations/en.lproj/localizedStrings.js:
     85
    1862022-04-25  Devin Rousso  <drousso@apple.com>
    287
  • trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js

    r292084 r293409  
    106106localizedStrings["2D"] = "2D";
    107107localizedStrings["720p"] = "720p";
     108/* Text indicating that the local override intercepts the request phase of network activity. */
     109localizedStrings["Access Control @ Local Override Type"] = "Access Control";
    108110localizedStrings["Accessibility"] = "Accessibility";
    109111localizedStrings["Action"] = "Action";
     
    234236/* Part of the 'Blackboxed - %d call frames' label shown in the debugger call stack when paused instead of subsequent call frames that have been blackboxed. */
    235237localizedStrings["Blackboxed @ Debugger Call Stack"] = "Blackboxed";
     238/* Text indicating that the local override will always block the network activity. */
     239localizedStrings["Block @ Local Override Type"] = "Block";
     240localizedStrings["Block Request URL"] = "Block Request URL";
     241localizedStrings["Block URL with %s error"] = "Block URL with %s error";
    236242localizedStrings["Block Variables"] = "Block Variables";
    237243/* Input label for the blur radius of a CSS box shadow */
     
    275281/* Tooltip for a timestamp marker that represents when a CSS animation/transition is canceled */
    276282localizedStrings["Canceled"] = "Canceled";
     283/* Text indicating that the local override will always block the network activity. */
     284localizedStrings["Cancellation @ Local Override Type"] = "Cancellation";
    277285localizedStrings["Canvas"] = "Canvas";
    278286localizedStrings["Canvas %d"] = "Canvas %d";
     
    725733localizedStrings["Garbage Collection"] = "Garbage Collection";
    726734localizedStrings["General"] = "General";
     735/* Text indicating that the local override intercepts the response phase of network activity. */
     736localizedStrings["General @ Local Override Type"] = "General";
    727737localizedStrings["Getter"] = "Getter";
    728738localizedStrings["Global Code"] = "Global Code";
     
    12161226localizedStrings["Request Data"] = "Request Data";
    12171227localizedStrings["Request Headers"] = "Request Headers";
    1218 /* Label indicating that the shown content is from a request local override. */
    1219 localizedStrings["Request Override @ Local Override Content View"] = "Request Override";
     1228/* Text indicating that the local override replaces the request of the network activity. */
     1229localizedStrings["Request Override @ Local Override Network Stage"] = "Request Override";
    12201230localizedStrings["Requesting: %s"] = "Requesting: %s";
    12211231localizedStrings["Required"] = "Required";
     
    12441254localizedStrings["Response Cookies"] = "Response Cookies";
    12451255localizedStrings["Response Headers"] = "Response Headers";
    1246 /* Label indicating that the shown content is from a response local override. */
    1247 localizedStrings["Response Override @ Local Override Content View"] = "Response Override";
     1256/* Text indicating that the local override replaces the response of the network activity. */
     1257localizedStrings["Response Override @ Local Override Network Stage"] = "Response Override";
    12481258localizedStrings["Response:"] = "Response:";
    12491259localizedStrings["Restart (%s)"] = "Restart (%s)";
     
    15621572localizedStrings["This object is referenced by internal objects"] = "This object is referenced by internal objects";
    15631573localizedStrings["This resource came from a local override"] = "This resource came from a local override";
     1574localizedStrings["This resource was blocked by a local override"] = "This resource was blocked by a local override";
    15641575localizedStrings["This resource was loaded from a local override"] = "This resource was loaded from a local override";
    15651576localizedStrings["This resource was loaded from a service worker"] = "This resource was loaded from a service worker";
     
    15761587/* Name of Timelines Tab */
    15771588localizedStrings["Timelines Tab Name"] = "Timelines";
     1589/* Text indicating that the local override will skip all network activity and instead immediately serve the response. */
     1590localizedStrings["Timeout @ Local Override Type"] = "Timeout";
    15781591localizedStrings["Timer %d Fired"] = "Timer %d Fired";
    15791592localizedStrings["Timer %d Installed"] = "Timer %d Installed";
  • trunk/Source/WebInspectorUI/UserInterface/Controllers/NetworkManager.js

    r292084 r293409  
    6262            WI.Resource.addEventListener(WI.Resource.Event.RequestDataDidChange, this._handleResourceContentChangedForLocalResourceOverride, this);
    6363            WI.LocalResourceOverride.addEventListener(WI.LocalResourceOverride.Event.DisabledChanged, this._handleResourceOverrideDisabledChanged, this);
     64            WI.LocalResourceOverride.addEventListener(WI.LocalResourceOverride.Event.ResourceErrorTypeChanged, this._handleResourceOverrideResourceErrorTypeChanged, this);
    6465
    6566            WI.Target.registerInitializationPromise((async () => {
     
    7273                    let supported = false;
    7374                    switch (localResourceOverride.type) {
     75                    case WI.LocalResourceOverride.InterceptType.Block:
     76                        supported = WI.NetworkManager.supportsBlockingRequests();
     77                        break;
     78
    7479                    case WI.LocalResourceOverride.InterceptType.Request:
    7580                        supported = WI.NetworkManager.supportsOverridingRequests();
     
    114119        return InspectorFrontendHost.supportsShowCertificate
    115120            && InspectorBackend.hasCommand("Network.getSerializedCertificate");
     121    }
     122
     123    static supportsBlockingRequests()
     124    {
     125        // COMPATIBILITY (iOS 13.4): Network.interceptRequestWithError did not exist yet.
     126        return InspectorBackend.hasCommand("Network.interceptRequestWithError");
    116127    }
    117128
     
    960971
    961972            switch (localResourceOverride.type) {
     973            case WI.LocalResourceOverride.InterceptType.Block:
     974                target.NetworkAgent.interceptRequestWithError.invoke({
     975                    requestId,
     976                    errorType: localResourceOverride.resourceErrorType,
     977                });
     978                return;
     979
    962980            case WI.LocalResourceOverride.InterceptType.Request: {
    963981                target.NetworkAgent.interceptWithRequest.invoke({
     
    15051523    }
    15061524
     1525    _handleResourceOverrideResourceErrorTypeChanged(event)
     1526    {
     1527        console.assert(WI.NetworkManager.supportsBlockingRequests());
     1528
     1529        let localResourceOverride = event.target;
     1530        WI.objectStores.localResourceOverrides.putObject(localResourceOverride);
     1531    }
     1532
    15071533    _handleBootstrapScriptContentDidChange(event)
    15081534    {
  • trunk/Source/WebInspectorUI/UserInterface/Models/LocalResourceOverride.js

    r292084 r293409  
    2626WI.LocalResourceOverride = class LocalResourceOverride extends WI.Object
    2727{
    28     constructor(url, type, localResource, {isCaseSensitive, isRegex, disabled} = {})
     28    constructor(url, type, localResource, {resourceErrorType, isCaseSensitive, isRegex, disabled} = {})
    2929    {
    3030        console.assert(url && typeof url === "string", url);
     
    3232        console.assert(localResource instanceof WI.LocalResource, localResource);
    3333        console.assert(!localResource.localResourceOverride, localResource);
     34        console.assert(!resourceErrorType || Object.values(WI.LocalResourceOverride.ResourceErrorType).includes(resourceErrorType), resourceErrorType);
    3435        console.assert(isCaseSensitive === undefined || typeof isCaseSensitive === "boolean", isCaseSensitive);
    3536        console.assert(isRegex === undefined || typeof isRegex === "boolean", isRegex);
     
    4243        this._type = type;
    4344        this._localResource = localResource;
     45        this._resourceErrorType = resourceErrorType || WI.LocalResourceOverride.ResourceErrorType.General;
    4446        this._isCaseSensitive = isCaseSensitive !== undefined ? isCaseSensitive : true;
    4547        this._isRegex = isRegex !== undefined ? isRegex : false;
     
    5153    // Static
    5254
    53     static create(url, type, {requestURL, requestMethod, requestHeaders, requestData, responseMIMEType, responseContent, responseBase64Encoded, responseStatusCode, responseStatusText, responseHeaders, isCaseSensitive, isRegex, disabled} = {})
     55    static create(url, type, {requestURL, requestMethod, requestHeaders, requestData, responseMIMEType, responseContent, responseBase64Encoded, responseStatusCode, responseStatusText, responseHeaders, resourceErrorType, isCaseSensitive, isRegex, disabled} = {})
    5456    {
    5557        let localResource = new WI.LocalResource({
    5658            request: {
    57                 url: requestURL || "",
     59                url: requestURL || url || "",
    5860                method: requestMethod,
    5961                headers: requestHeaders,
     
    6971            },
    7072        });
    71         return new WI.LocalResourceOverride(url, type, localResource, {isCaseSensitive, isRegex, disabled});
     73        return new WI.LocalResourceOverride(url, type, localResource, {resourceErrorType, isCaseSensitive, isRegex, disabled});
     74    }
     75
     76    static displayNameForNetworkStageOfType(type)
     77    {
     78        switch (type) {
     79        case WI.LocalResourceOverride.InterceptType.Block:
     80        case WI.LocalResourceOverride.InterceptType.Request:
     81            return WI.UIString("Request Override", "Request Override @ Local Override Network Stage", "Text indicating that the local override replaces the request of the network activity.");
     82
     83        case WI.LocalResourceOverride.InterceptType.Response:
     84        case WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork:
     85            return WI.UIString("Response Override", "Response Override @ Local Override Network Stage", "Text indicating that the local override replaces the response of the network activity.");
     86        }
     87
     88        console.assert(false, "Unknown type: ", type);
     89        return "";
    7290    }
    7391
     
    7593    {
    7694        switch (type) {
     95        case WI.LocalResourceOverride.InterceptType.Block:
     96            return WI.UIString("Block", "Block @ Local Override Type", "Text indicating that the local override will always block the network activity.");
     97
    7798        case WI.LocalResourceOverride.InterceptType.Request:
    7899            return WI.UIString("Request", "Request @ Local Override Type", "Text indicating that the local override intercepts the request phase of network activity.");
     100
    79101        case WI.LocalResourceOverride.InterceptType.Response:
    80102            return WI.UIString("Response", "Response @ Local Override Type", "Text indicating that the local override intercepts the response phase of network activity.");
     103
    81104        case WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork:
    82105            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.");
     
    87110    }
    88111
     112    static displayNameForResourceErrorType(resourceErrorType)
     113    {
     114        switch (resourceErrorType) {
     115        case WI.LocalResourceOverride.ResourceErrorType.AccessControl:
     116            return WI.UIString("Access Control", "Access Control @ Local Override Type", "Text indicating that the local override will block the network activity with an access error.");
     117
     118        case WI.LocalResourceOverride.ResourceErrorType.Cancellation:
     119            return WI.UIString("Cancellation", "Cancellation @ Local Override Type", "Text indicating that the local override will block the network activity with a cancellation error.");
     120
     121        case WI.LocalResourceOverride.ResourceErrorType.General:
     122            return WI.UIString("General", "General @ Local Override Type", "Text indicating that the local override will block the network activity with a general error.");
     123
     124        case WI.LocalResourceOverride.ResourceErrorType.Timeout:
     125            return WI.UIString("Timeout", "Timeout @ Local Override Type", "Text indicating that the local override will block the network activity with an timeout error.");
     126        }
     127
     128        console.assert(false, "Unknown resource error type: ", resourceErrorType);
     129        return "";
     130    }
     131
    89132    // Import / Export
    90133
    91134    static fromJSON(json)
    92135    {
    93         let {url, type, localResource: localResourceJSON, isCaseSensitive, isRegex, disabled} = json;
     136        let {url, type, localResource: localResourceJSON, resourceErrorType, isCaseSensitive, isRegex, disabled} = json;
    94137
    95138        let localResource = WI.LocalResource.fromJSON(localResourceJSON);
     
    99142        type ??= WI.LocalResourceOverride.InterceptType.Response;
    100143
    101         return new WI.LocalResourceOverride(url, type, localResource, {isCaseSensitive, isRegex, disabled});
     144        return new WI.LocalResourceOverride(url, type, localResource, {resourceErrorType, isCaseSensitive, isRegex, disabled});
    102145    }
    103146
     
    113156        };
    114157
     158        if (this._resourceErrorType)
     159            json.resourceErrorType = this._resourceErrorType;
     160
    115161        if (key === WI.ObjectStore.toJSONSymbol)
    116162            json[WI.objectStores.localResourceOverrides.keyPath] = this._url;
     
    134180    }
    135181
     182    get resourceErrorType()
     183    {
     184        return this._resourceErrorType;
     185    }
     186
     187    set resourceErrorType(resourceErrorType)
     188    {
     189        console.assert(Object.values(WI.LocalResourceOverride.ResourceErrorType).includes(resourceErrorType), resourceErrorType);
     190
     191        if (this._resourceErrorType === resourceErrorType)
     192            return;
     193
     194        this._resourceErrorType = resourceErrorType;
     195
     196        this.dispatchEventToListeners(WI.LocalResourceOverride.Event.ResourceErrorTypeChanged);
     197    }
     198
    136199    get disabled()
    137200    {
     
    171234
    172235        switch (this._type) {
     236        case WI.LocalResourceOverride.InterceptType.Block:
    173237        case WI.LocalResourceOverride.InterceptType.Request:
    174238            return false;
     
    220284
    221285WI.LocalResourceOverride.InterceptType = {
     286    Block: "block",
    222287    Request: "request",
    223288    Response: "response",
     
    225290};
    226291
     292WI.LocalResourceOverride.ResourceErrorType = {
     293    AccessControl: "AccessControl",
     294    Cancellation: "Cancellation",
     295    General: "General",
     296    Timeout: "Timeout",
     297};
     298
    227299WI.LocalResourceOverride.Event = {
    228300    DisabledChanged: "local-resource-override-disabled-state-did-change",
     301    ResourceErrorTypeChanged: "local-resource-override-resource-error-type-changed",
    229302};
  • trunk/Source/WebInspectorUI/UserInterface/Models/Resource.js

    r292037 r293409  
    185185        let classes = [];
    186186
     187        let localResourceOverride = resource.localResourceOverride || WI.networkManager.localResourceOverridesForURL(resource.url).filter((localResourceOverride) => !localResourceOverride.disabled)[0];
    187188        let isOverride = !!resource.localResourceOverride;
    188189        let wasOverridden = resource.responseSource === WI.Resource.ResponseSource.InspectorOverride;
    189         let shouldBeOverridden = resource.isLoading() && WI.networkManager.localResourceOverridesForURL(resource.url).some((localResourceOverride) => !localResourceOverride.disabled);
    190         if (isOverride || wasOverridden || shouldBeOverridden)
     190        let shouldBeOverridden = resource.isLoading() && localResourceOverride;
     191        let shouldBeBlocked = (resource.failed || isOverride) && localResourceOverride?.type === WI.LocalResourceOverride.InterceptType.Block;
     192        if (isOverride || wasOverridden || shouldBeOverridden || shouldBeBlocked) {
    191193            classes.push("override");
     194
     195            if (shouldBeBlocked || localResourceOverride?.type === WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork)
     196                classes.push("skip-network");
     197        }
    192198
    193199        if (resource.type === WI.Resource.Type.Other) {
  • trunk/Source/WebInspectorUI/UserInterface/Views/ContentView.js

    r283859 r293409  
    109109
    110110        if (representedObject instanceof WI.LocalResourceOverride) {
    111             if (representedObject.type === WI.LocalResourceOverride.InterceptType.Request)
     111            if (representedObject.type === WI.LocalResourceOverride.InterceptType.Block || representedObject.type === WI.LocalResourceOverride.InterceptType.Request)
    112112                return new WI.LocalResourceOverrideRequestContentView(representedObject);
    113113            return WI.ContentView.createFromRepresentedObject(representedObject.localResource);
     
    272272
    273273        if (representedObject instanceof WI.LocalResourceOverride) {
    274             if (representedObject.type !== WI.LocalResourceOverride.InterceptType.Request)
     274            if (representedObject.type === WI.LocalResourceOverride.InterceptType.Response || representedObject.type === WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork)
    275275                return representedObject.localResource;
    276276            return representedObject;
  • trunk/Source/WebInspectorUI/UserInterface/Views/ContextMenuUtilities.js

    r288029 r293409  
    9696                });
    9797            });
     98
     99            if (WI.NetworkManager.supportsBlockingRequests()) {
     100                contextMenu.appendItem(WI.UIString("Block Request URL"), async () => {
     101                    let localResourceOverride = await sourceCode.createLocalResourceOverride(WI.LocalResourceOverride.InterceptType.Block);
     102                    WI.networkManager.addLocalResourceOverride(localResourceOverride);
     103                });
     104            }
    98105        } else {
    99106            let localResourceOverride = WI.networkManager.localResourceOverridesForURL(sourceCode.url)[0];
  • trunk/Source/WebInspectorUI/UserInterface/Views/FontResourceContentView.js

    r270604 r293409  
    121121            return false;
    122122
    123         // Request overrides cannot be created/updated from a file as files don't have network info.
    124123        let localResourceOverride = this.resource.localResourceOverride || existingOverrides[0];
    125         if (localResourceOverride?.type === WI.LocalResourceOverride.InterceptType.Request)
     124        if (localResourceOverride && !localResourceOverride.canMapToFile)
    126125            return false;
    127126
  • trunk/Source/WebInspectorUI/UserInterface/Views/ImageResourceContentView.js

    r277279 r293409  
    179179            return false;
    180180
    181         // Request overrides cannot be created/updated from a file as files don't have network info.
    182181        let localResourceOverride = this.resource.localResourceOverride || existingOverrides[0];
    183         if (localResourceOverride?.type === WI.LocalResourceOverride.InterceptType.Request)
     182        if (localResourceOverride && !localResourceOverride.canMapToFile)
    184183            return false;
    185184
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideLabelView.js

    r270604 r293409  
    4343        let labelElement = document.createElement("span");
    4444        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.");
     45        labelElement.textContent = WI.LocalResourceOverride.displayNameForNetworkStageOfType(this._localResourceOverride.type);
    4946
    5047        let urlElement = document.createElement("span");
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverridePopover.css

    r293334 r293409  
    7070}
    7171
    72 .popover .local-resource-override-popover-content.response .editor.url {
     72.popover .local-resource-override-popover-content:is(.response, .block) .editor.url {
    7373    width: 330px;
    7474}
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverridePopover.js

    r293178 r293409  
    7777            if (!name || !value)
    7878                continue;
    79             if (data.type !== WI.LocalResourceOverride.InterceptType.Request) {
     79            if (data.type === WI.LocalResourceOverride.InterceptType.Response || data.type === WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork) {
    8080                if (name.toLowerCase() === "content-type")
    8181                    continue;
     
    126126        if (!data.responseMIMEType && data.requestURL) {
    127127            data.responseMIMEType = WI.mimeTypeForFileExtension(WI.fileExtensionForURL(data.requestURL));
    128             if (data.type !== WI.LocalResourceOverride.InterceptType.Request)
     128            if (data.type === WI.LocalResourceOverride.InterceptType.Response || data.type === WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork)
    129129                headers["Content-Type"] = data.responseMIMEType;
    130130        }
     
    200200
    201201        this._typeSelectElement = document.createElement("select");
    202         for (let type of [WI.LocalResourceOverride.InterceptType.Request, WI.LocalResourceOverride.InterceptType.Response]) {
     202        for (let type of [WI.LocalResourceOverride.InterceptType.Request, WI.LocalResourceOverride.InterceptType.Response, WI.LocalResourceOverride.InterceptType.Block]) {
    203203            let optionElement = this._typeSelectElement.appendChild(document.createElement("option"));
    204204            optionElement.textContent = WI.LocalResourceOverride.displayNameForType(type);
     
    502502
    503503        let toggleInputsForType = (initializeHeaders) => {
     504            let isBlock = this._typeSelectElement.value === WI.LocalResourceOverride.InterceptType.Block;
    504505            let isRequest = this._typeSelectElement.value === WI.LocalResourceOverride.InterceptType.Request;
     506            let isResponse = this._typeSelectElement.value === WI.LocalResourceOverride.InterceptType.Response || this._typeSelectElement.value === WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork;
     507
     508            popoverContentElement.classList.toggle("block", isBlock);
    505509            popoverContentElement.classList.toggle("request", isRequest);
    506             popoverContentElement.classList.toggle("response", !isRequest);
    507 
     510            popoverContentElement.classList.toggle("response", isResponse);
     511
     512            initializeHeaders &&= !isBlock;
    508513            if (initializeHeaders) {
    509514                let headers = isRequest ? requestHeaders : responseHeaders;
     
    520525
    521526            if (requestURLRow)
    522                 requestURLRow.element.hidden = !isRequest;
     527                requestURLRow.element.hidden = isResponse || isBlock;
    523528            if (methodRowElement)
    524                 methodRowElement.hidden = !isRequest;
    525 
    526             mimeTypeRow.element.hidden = isRequest;
    527             statusCodeRow.element.hidden = isRequest;
     529                methodRowElement.hidden = isResponse || isBlock;
     530            mimeTypeRow.element.hidden = isRequest || isBlock;
     531            statusCodeRow.element.hidden = isRequest || isBlock;
     532            headersRow.hidden = isBlock;
    528533            if (optionsRowElement)
    529                 optionsRowElement.hidden = isRequest;
     534                optionsRowElement.hidden = isRequest || isBlock;
    530535
    531536            if (isRequest) {
     
    534539                if (contentTypeDataGridNode.parent)
    535540                    this._headersDataGrid.removeChild(contentTypeDataGridNode);
    536             } else {
     541            }
     542
     543            if (isResponse) {
    537544                this._mimeTypeCodeMirror.refresh();
    538545                this._statusCodeCodeMirror.refresh();
     
    542549                    this._headersDataGrid.insertChild(contentTypeDataGridNode, 0);
    543550            }
     551
    544552            toggleHeadersDataGridVisibility();
    545553        };
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideRequestContentView.css

    r270604 r293409  
    3838    top: var(--navigation-bar-height);
    3939}
     40
     41 .content-view.text.local-resource-override-request > .message-text-view select {
     42    font-size: inherit;
     43    vertical-align: text-bottom;
     44 }
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideRequestContentView.js

    r270604 r293409  
    2929    {
    3030        console.assert(localResourceOverride instanceof WI.LocalResourceOverride, localResourceOverride);
    31         console.assert(localResourceOverride.type === WI.LocalResourceOverride.InterceptType.Request, localResourceOverride);
    32         console.assert(WI.NetworkManager.supportsOverridingRequests());
     31        console.assert(localResourceOverride.type === WI.LocalResourceOverride.InterceptType.Block || localResourceOverride.type === WI.LocalResourceOverride.InterceptType.Request, localResourceOverride);
     32        console.assert(localResourceOverride.type !== WI.LocalResourceOverride.InterceptType.Block || WI.NetworkManager.supportsBlockingRequests(), localResourceOverride);
     33        console.assert(localResourceOverride.type !== WI.LocalResourceOverride.InterceptType.Request || WI.NetworkManager.supportsOverridingRequests(), localResourceOverride);
    3334
    3435        let localResource = localResourceOverride.localResource;
     
    6970        this.textEditor.addEventListener(WI.TextEditor.Event.ContentDidChange, this._handleTextEditorContentDidChange, this);
    7071
    71         let requestMethod = this.representedObject.localResource.requestMethod;
    72         if (!WI.HTTPUtilities.RequestMethodsWithBody.has(requestMethod)) {
    73             let message = WI.createMessageTextView(WI.UIString("%s requests do not have a body").format(requestMethod));
    74             this.element.appendChild(message);
     72        let message = null;
     73        switch (this.representedObject.type) {
     74        case WI.LocalResourceOverride.InterceptType.Block: {
     75            let selectElement = document.createElement("select");
     76
     77            function addOption(resourceErrorType) {
     78                let optionElement = selectElement.appendChild(document.createElement("option"));
     79                optionElement.textContent = WI.LocalResourceOverride.displayNameForResourceErrorType(resourceErrorType);
     80                optionElement.value = resourceErrorType;
     81            }
     82            addOption(WI.LocalResourceOverride.ResourceErrorType.General);
     83            addOption(WI.LocalResourceOverride.ResourceErrorType.Timeout);
     84            addOption(WI.LocalResourceOverride.ResourceErrorType.Cancellation);
     85            addOption(WI.LocalResourceOverride.ResourceErrorType.AccessControl);
     86
     87            selectElement.value = this.representedObject.resourceErrorType;
     88            selectElement.addEventListener("change", (event) => {
     89                this.representedObject.resourceErrorType = selectElement.value;
     90            });
     91
     92            message = document.createDocumentFragment();
     93            String.format(WI.UIString("Block URL with %s error"), [selectElement], String.standardFormatters, message, (a, b) => {
     94                a.append(b);
     95                return a;
     96            });
     97            break;
    7598        }
     99
     100        case WI.LocalResourceOverride.InterceptType.Request: {
     101            let requestMethod = this.representedObject.localResource.requestMethod;
     102            if (!WI.HTTPUtilities.RequestMethodsWithBody.has(requestMethod))
     103                message = WI.UIString("%s requests do not have a body").format(requestMethod);
     104            break;
     105        }
     106        }
     107        if (message)
     108            this.element.appendChild(WI.createMessageTextView(message));
    76109    }
    77110
  • trunk/Source/WebInspectorUI/UserInterface/Views/LocalResourceOverrideTreeElement.js

    r293338 r293409  
    133133        let wasSelected = this.selected;
    134134
    135         if (serializedData.type !== WI.LocalResourceOverride.InterceptType.Request) {
     135        if (serializedData.type === WI.LocalResourceOverride.InterceptType.Response || serializedData.type === WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork) {
    136136            let revision = this._localResourceOverride.localResource.currentRevision;
    137137            serializedData.responseContent = revision.content;
  • trunk/Source/WebInspectorUI/UserInterface/Views/ResourceContentView.js

    r292172 r293409  
    8888                this._createLocalResourceOverrideButtonNavigationItem.enabled = false; // Enabled when the content is available.
    8989                this._createLocalResourceOverrideButtonNavigationItem.visibilityPriority = WI.NavigationItem.VisibilityPriority.Low;
    90                 if (WI.NetworkManager.supportsOverridingRequests())
     90                if (WI.NetworkManager.supportsOverridingRequests() || WI.NetworkManager.supportsBlockingRequests())
    9191                    WI.addMouseDownContextMenuHandlers(this._createLocalResourceOverrideButtonNavigationItem.element, this._populateCreateLocalResourceOverrideContextMenu.bind(this));
    9292                else
     
    335335            return;
    336336
    337         contextMenu.appendItem(WI.UIString("Create Request Local Override"), () => {
    338             // Request overrides cannot be created from a file as files don't have network info.
    339             this._createAndShowLocalResourceOverride(WI.LocalResourceOverride.InterceptType.Request);
    340         });
     337        if (WI.NetworkManager.supportsOverridingRequests()) {
     338            contextMenu.appendItem(WI.UIString("Create Request Local Override"), () => {
     339                // Request overrides cannot be created from a file as files don't have network info.
     340                this._createAndShowLocalResourceOverride(WI.LocalResourceOverride.InterceptType.Request);
     341            });
     342        }
    341343
    342344        contextMenu.appendItem(WI.UIString("Create Response Local Override"), () => {
     
    345347            });
    346348        });
     349
     350        if (WI.NetworkManager.supportsBlockingRequests()) {
     351            contextMenu.appendItem(WI.UIString("Block Request URL"), async () => {
     352                let localResourceOverride = await this._resource.createLocalResourceOverride(WI.LocalResourceOverride.InterceptType.Block);
     353                WI.networkManager.addLocalResourceOverride(localResourceOverride);
     354            });
     355        }
    347356    }
    348357
  • trunk/Source/WebInspectorUI/UserInterface/Views/ResourceIcons.css

    r259329 r293409  
    109109}
    110110
     111.resource-icon.override.skip-network .icon {
     112    content: url(../Images/SkipNetwork.svg#light);
     113}
     114
     115body:not(.window-inactive, .window-docked-inactive) :is(.table, .data-grid):focus-within .selected .resource-icon.override.skip-network .icon,
     116body:not(.window-inactive, .window-docked-inactive) .tree-outline:focus-within .selected.resource-icon.override.skip-network .icon {
     117    content: url(../Images/SkipNetwork.svg#dark);
     118}
     119
    111120@media (prefers-color-scheme: dark) {
    112121    .resource-icon .icon {
     
    190199        content: url(../Images/ClippingIcons.svg#range-dark);
    191200    }
    192 }
     201
     202    .resource-icon.override.skip-network .icon {
     203        content: url(../Images/SkipNetwork.svg#dark);
     204    }
     205}
  • trunk/Source/WebInspectorUI/UserInterface/Views/ResourceTreeElement.js

    r270604 r293409  
    140140            this._resource.removeEventListener(WI.Resource.Event.TypeDidChange, this._typeDidChange, this);
    141141            this._resource.removeEventListener(WI.Resource.Event.LoadingDidFinish, this.updateStatus, this);
    142             this._resource.removeEventListener(WI.Resource.Event.LoadingDidFail, this.updateStatus, this);
     142            this._resource.removeEventListener(WI.Resource.Event.LoadingDidFail, this._loadingDidFail, this);
    143143            this._resource.removeEventListener(WI.Resource.Event.ResponseReceived, this._responseReceived, this);
    144144        }
     
    151151        resource.addEventListener(WI.Resource.Event.TypeDidChange, this._typeDidChange, this);
    152152        resource.addEventListener(WI.Resource.Event.LoadingDidFinish, this.updateStatus, this);
    153         resource.addEventListener(WI.Resource.Event.LoadingDidFail, this.updateStatus, this);
     153        resource.addEventListener(WI.Resource.Event.LoadingDidFail, this._loadingDidFail, this);
    154154        resource.addEventListener(WI.Resource.Event.ResponseReceived, this._responseReceived, this);
    155155
     
    261261    _updateIcon()
    262262    {
     263        let localResourceOverride = this._resource.localResourceOverride || WI.networkManager.localResourceOverridesForURL(this._resource.url).filter((localResourceOverride) => !localResourceOverride.disabled)[0];
    263264        let isOverride = !!this._resource.localResourceOverride;
    264265        let wasOverridden = this._resource.responseSource === WI.Resource.ResponseSource.InspectorOverride;
    265         if (isOverride || wasOverridden)
     266        let shouldBeOverridden = this._resource.isLoading() && localResourceOverride;
     267        let shouldBeBlocked = (this._resource.failed || isOverride) && localResourceOverride?.type === WI.LocalResourceOverride.InterceptType.Block;
     268        if (isOverride || wasOverridden || shouldBeOverridden || shouldBeBlocked) {
    266269            this.addClassName("override");
     270
     271            if (shouldBeBlocked || localResourceOverride?.type === WI.LocalResourceOverride.InterceptType.ResponseSkippingNetwork)
     272                this.addClassName("skip-network");
     273        } else {
     274            this.removeClassName("override");
     275            this.removeClassName("skip-network");
     276        }
     277
     278        if (wasOverridden)
     279            this.iconElement.title = WI.UIString("This resource was loaded from a local override");
     280        else if (shouldBeBlocked)
     281            this.iconElement.title = WI.UIString("This resource was blocked by a local override");
    267282        else
    268             this.removeClassName("override");
    269 
    270         this.iconElement.title = wasOverridden ? WI.UIString("This resource was loaded from a local override") : "";
     283            this.iconElement.title = "";
    271284    }
    272285
     
    285298    }
    286299
     300    _loadingDidFail(event)
     301    {
     302        this.updateStatus();
     303        this._updateIcon();
     304    }
     305
    287306    _responseReceived(event)
    288307    {
Note: See TracChangeset for help on using the changeset viewer.