Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[BUG] Request Interception Breaks Worker importScripts with Remote Resource #5952

Closed
rwoll opened this issue Mar 25, 2021 · 15 comments · Fixed by #6170
Closed

[BUG] Request Interception Breaks Worker importScripts with Remote Resource #5952

rwoll opened this issue Mar 25, 2021 · 15 comments · Fixed by #6170
Assignees

Comments

@rwoll
Copy link
Member

rwoll commented Mar 25, 2021

Context:

  • Playwright Version: 1.11.0-next (but effects at least the last couple of versions)
  • Operating System: macOS
  • Node.js version: v14.16.0
  • Browser: Chromium
  • Extra: -

Code Snippet

it('should not break remote worker importScripts',  (test, { browserName }) => {
  test.fail(browserName === "chromium");
}, async ({ page, server, context }) => {
  context.route('**', async request => {
    await request.continue();
  });
  await page.goto(server.PREFIX + '/worker/worker-http-import.html');
  await page.waitForSelector("#status:has-text('finished')");
});

/worker/worker-http-import.html:

<!DOCTYPE html>
<html>
<head>
    <title>Remote importScripts Test</title>
</head>
<html>
    <div id="status">-</div>
    <script>
    new Worker('worker-http-import.js').addEventListener("message", ({ data }) => {
        document.getElementById("status").innerText = data;
    });
    </script>
</html>
</html>

/worker/worker-http-import.js:

console.log("hello from worker-http-import.js");
importScripts("./import-me.js")
console.log("successfully imported");
self.postMessage("finished");

/worker/import-me.js:

console.log("hello from import-me.js");

Describe the bug

Request interception causes importScripts("./import-me.js") to hang. If you
disable request interception (i.e. comment out the context.route(…) bits),
the importScripts succeeds, hello from import-me.js is logged, and the waitForSelector
succeeds and matches the finished message that's bubbled up.


Additional Debugging Notes

  • With DEBUG=pw:protocol we see a Fetch.requestPaused for http://localhost:4747/import-me.js,
    but no protocol messages about that domain after when we would eventually expect
    a requestWillBeSent and a resume.
  • For http://localhost:4747/import-me.js, requestWillBeSent is undefined here in crNetworkManager.js:
    if (requestWillBeSentEvent) {
  • The test passes for Firefox and Webkit.
  • Not all network requests originating from the worker hang. For example, in
    the below demo (fetch-worker.js) the fetch request succeeds.
  • Not all importScripts hang. For example, in the below demos (inline-import-worker.js
    and blob-import-worker.js) a Data URL and Object URL are successfully imported.

Here's a real world example that's broken:

const playwright = require("playwright");
const http = require("http");

const server = http.createServer((req, res) => {
  switch (req.url) {
    case "/main":
      res.writeHead(200);
      res.end(`
                <!DOCTYPE html>
                <html>
                    <head>
                        <title>Demo</title>
                    </head>
                    <body>
                        <h1>Demo</h1>
                        <iframe width="100%" height="300" src="//jsfiddle.net/pdfjs/wagvs9Lf/embedded/result/" allowfullscreen="allowfullscreen" allowpaymentrequest frameborder="0"></iframe>
                    </body>
                </html>
            `);
      return;
    default:
      res.writeHead(404);
      res.end("404 Not Found");
      return;
  }
});

server.listen(4747);

(async () => {
  const browser = await playwright.chromium.launch({ headless: false });
  const ctx = await browser.newContext();
  const page = await ctx.newPage();
  ctx.route("**", async (route) => {
    await route.continue();
  });
  await page.goto("http://localhost:4747/main");
  await page.frame({
    url: 'http://fiddle.jshell.net/pdfjs/wagvs9Lf/show/light/'
  }).click('text=14');
  await browser.close();
  server.close();
})()

If you comment out ctx.route(…) and re-run, it will succeed.

And below are various demos/experiments I used to narrow down why a website wasn't
working. We started with a 3rd-party PDF reader (https://mozilla.github.io/pdf.js/) appearing
subtly broken and hanging only when connected with Playwright, and were not sure
where the problem was, but have hopefully started to narrow it down! Unfortunately, we encounter a handful of libraries that use Web Workers with remote importScripts that are broken during testing because of this bug.

const pw = require('playwright');
const http = require('http');

const server = http.createServer((req, res) => {
  switch (req.url) {
    case "/":
      res.writeHead(200);
      res.end(`
                <!DOCTYPE html>
                <html>
                    <head>
                        <title>Broken Web Worker Demo</title>
                    </head>
                    <body>
                        <table>
                            <thead>
                                <tr>
                                    <th>Worker</th>
                                    <th>Status</th>
                                </tr>
                            </thead>
                            <tbody>
                                <tr>
                                    <td>basic-worker</td>
                                    <td id="basic-worker">-</td>
                                </tr>
                                <tr>
                                  <td>fetch-worker</td>
                                  <td id="fetch-worker">-</td>
                                </tr>
                                <tr>
                                    <td>inline-import-worker</td>
                                    <td id="inline-import-worker">-</td>
                                </tr>
                                <tr>
                                    <td>http-import-worker</td>
                                    <td id="http-import-worker">-</td>
                                </tr>
                                <tr>
                                    <td>blob-import-worker</td>
                                    <td id="blob-import-worker">-</td>
                                </tr>
                            </tbody>
                        </table>
                        <script>
                            const newWorker = (id) => {
                                new Worker("/" + id + ".js").addEventListener("message", ({ data }) => {
                                    document.getElementById(id).innerText = data;
                                });
                            };

                            newWorker("basic-worker");
                            newWorker("fetch-worker");
                            newWorker("inline-import-worker");
                            newWorker("http-import-worker");
                            newWorker("blob-import-worker");
                        </script>
                    </body>
                </html>
            `);
      return;
    case `/basic-worker.js`:
      res.setHeader("Content-Type", "text/javascript");
      res.writeHead(200);
      res.end(`
                self.postMessage("running");
                console.log("basic-worker.js fully ran");
                self.postMessage("ran");
      `);
      return;
    case `/fetch-worker.js`:
      res.setHeader("Content-Type", "text/javascript");
      res.writeHead(200);
      res.end(`
                self.postMessage("running");
                fetch("/test.json").then(resp => resp.json()).then(({ data }) => {
                  if (data === "hello") {
                    console.log("fetch-worker.js fully ran");
                    self.postMessage("ran");
                  }
                });
      `);
      return;
    case `/inline-import-worker.js`:
      res.setHeader("Content-Type", "text/javascript");
      res.writeHead(200);
      res.end(`
                self.postMessage("running");
                importScripts('data:text/javascript,console.log("inline-import-worker.js fully ran")');
                self.postMessage("ran");
            `);
      return;
    case `/http-import-worker.js`:
      res.setHeader("Content-Type", "text/javascript");
      res.writeHead(200);
      res.end(`
                self.postMessage("running");
                importScripts("http://localhost:4747/import-me.js");
                console.log("http-import-worker.js fully ran");
                self.postMessage("ran");
            `);
      return;
    case `/import-me.js`:
      res.setHeader("Content-Type", "text/javascript");
      res.writeHead(200);
      res.end(`console.log("import-me.js fully ran");`);
      return;
    case `/blob-import-worker.js`:
      res.setHeader("Content-Type", "text/javascript");
      res.writeHead(200);
      res.end(`
                self.postMessage("running");
                const oURL = URL.createObjectURL(new Blob(['console.log("blob-import-worker.js fully ran")'], { type: "application/javascript"}));
                importScripts(oURL);
                self.postMessage("ran");
            `);
      return;
    case `/test.json`:
      res.setHeader("Content-Type", "application/json");
      res.writeHead(200);
      res.end(JSON.stringify({ data: "hello" }));
      return;
    default:
      res.writeHead(404)
      res.end(`${req.url} not found`);
      return;
  }
});
server.listen(4747);

(async () => {
  const browser = await pw.chromium.launch({ headless: false });
  const ctx = await browser.newContext();
  const page = await ctx.newPage();
  ctx.route("**", async (route) => {
    await route.continue();
  });
  await page.goto("http://localhost:4747");
  await page.waitForSelector("#basic-worker:has-text('ran')");
  await page.waitForSelector("#fetch-worker:has-text('ran')");
  await page.waitForSelector("#inline-import-worker:has-text('ran')");
  await page.waitForSelector("#http-import-worker:has-text('ran')");
  await page.waitForSelector("#blob-import-worker:has-text('ran')");
  await browser.close();
  server.close();
})()

If you run the above, you'll notice all entries in the table flip to ran
except http-import-worker. If you comment out the ctx.route(…) bits, it
succeeds.

Thank you for all your work and let me know if you have some additional tips
for debugging or need more information! I would love help getting to the bottom
of this! (I will contribute an MR with the test case shortly.)

@rwoll
Copy link
Member Author

rwoll commented Mar 25, 2021

Some more debugging info 🎉

If you circumvent Playwright's crNetworkManager's request interception (by not using ctx.route(…)), and replace it with the following:

  // ctx.route("**", async (route) => {
  //   await route.continue();
  // });
  let foundImportJS = false
  const cdp = await ctx.newCDPSession(page);
  cdp.on("Fetch.requestPaused", (evt) => {
    if (evt.request.url === "http://localhost:4747/import-me.js") {
      foundImportJS = true;
    }
    cdp.send("Fetch.continueRequest", { requestId: evt.requestId });
  });
  await cdp.send('Network.enable');
  await Promise.all([
    cdp.send('Network.setCacheDisabled', { cacheDisabled: true }),
    cdp.send('Fetch.enable', {
      handleAuthRequests: true,
      patterns: [{urlPattern: '*'}],
    }),
  ]);

the tests run clean.

@rwoll
Copy link
Member Author

rwoll commented Mar 25, 2021

More debugging 🐛 :

To debug the first test case in the issue (it('should not break remote worker importScripts'), I added three log lines to crNetworkManager:

  _onRequest(workerFrame: frames.Frame | undefined, requestWillBeSentEvent: Protocol.Network.requestWillBeSentPayload, requestPausedEvent: Protocol.Fetch.requestPausedPayload | null) {
    console.log("_onRequest", requestWillBeSentEvent.request.url);
    
  }

  _onRequestWillBeSent(workerFrame: frames.Frame | undefined, event: Protocol.Network.requestWillBeSentPayload) {
    console.log("_onRequestWillBeSent", event.request.url);
    
  }

  _onRequestPaused(workerFrame: frames.Frame | undefined, event: Protocol.Fetch.requestPausedPayload) {
    console.log("_onRequestPaused", event.request.url);
    
  }

After running the test (which times out), I see the following logged:

interception.spec.ts - should not break remote worker importScripts
_onRequestWillBeSent http://localhost:8907/worker/worker-http-import.html
_onRequestPaused http://localhost:8907/worker/worker-http-import.html
_onRequest http://localhost:8907/worker/worker-http-import.html
_onRequestWillBeSent http://localhost:8907/worker/worker-http-import.js
_onRequestPaused http://localhost:8907/worker/worker-http-import.js
_onRequest http://localhost:8907/worker/worker-http-import.js
_onRequestPaused http://localhost:8907/worker/import-me.js

Notably, there is no _onRequestWillBeSent for import-me.js; should there be?

@rwoll
Copy link
Member Author

rwoll commented Mar 25, 2021

Modifying crNetworkManager to always continue requests by adding the following line

      this._client.send('Fetch.continueRequest', {
        requestId: event.requestId,
      });

to

this._requestIdToRequestPausedEvent.set(requestId, event);

allows the test to pass but then _onRequestWill be sent comes after _onRequestPaused:

_onRequestWillBeSent http://localhost:8907/worker/worker-http-import.html
_onRequestPaused http://localhost:8907/worker/worker-http-import.html
_onRequest http://localhost:8907/worker/worker-http-import.html
_onRequestWillBeSent http://localhost:8907/worker/worker-http-import.js
_onRequestPaused http://localhost:8907/worker/worker-http-import.js
_onRequest http://localhost:8907/worker/worker-http-import.js
_onRequestPaused http://localhost:8907/worker/import-me.js
_onRequestWillBeSent http://localhost:8907/worker/import-me.js
_onRequest http://localhost:8907/worker/import-me.js

The code on master appears to deadlock: For this particular request (import-me.js) _onRequestPaused gets invoked before _onRequestWillBeSent, so there's no record of the requestWillBeSentEvent in _requestIdToRequestWillBeSentEvent in _onRequestPaused which falls through to not resolving the request and placing it in _requestIdToRequestPausedEvent expecting it to be handled from that map with the a corresponding _onRequestWillBeSent which won't happen unless the request is resolved…deadlock.

rwoll added a commit to rwoll/playwright that referenced this issue Mar 25, 2021
@pavelfeldman pavelfeldman self-assigned this Mar 26, 2021
@rwoll
Copy link
Member Author

rwoll commented Mar 27, 2021

@pavelfeldman I had a chance to setup my machine with a Chromium debug build this evening, and think I've found some useful additional bits. (Wow, does compilation of Chromium for the first time on a new machine take a good deal of time!)

I'm operating under the assumption that Chrome SHOULD send us Network.requestWillBeSent before Fetch.requestPaused for import-me.js like it does for the other requests and as the Playwright code assumes it does to avoid deadlock. Let me know if that's a wrong assumption.

If you run the failing test case (#5953) with Playwright pointed at a local Chromium build with the following logging in inspector_network_agent.cc/InspectorNetworkAgent::WillSendRequestInternal:

diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index 6370da0d58..8cc24776cc 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -36,6 +36,7 @@
 #include "base/containers/span.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/logging.h"
 #include "build/build_config.h"
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
@@ -1122,6 +1123,7 @@ void InspectorNetworkAgent::WillSendRequestInternal(
   Maybe<String> maybe_frame_id;
   if (!frame_id.IsEmpty())
     maybe_frame_id = frame_id;
+  VLOG(1) << "WillSendRequestInternal:: URL:" << request_info->getUrl();
   GetFrontend()->requestWillBeSent(
       request_id, loader_id, documentURL, std::move(request_info),
       base::TimeTicks::Now().since_origin().InSecondsF(),

you will see that, in fact, the requestWillBeSent gets constructed and logged for import-me.js, but the Frontend doesn't flush it out and we never see it sent to the CDP channel (before the Fetch.requestPaused):

[…] WillSendRequestInternal:: URL:"http://localhost:4747/http-import-worker.js"
[…] WillSendRequestInternal:: URL:"http://localhost:4747/favicon.ico"
[…] WillSendRequestInternal:: URL:"http://localhost:4747/import-me.js"

However, if we experiment and remove the guard to the deliberate flush (and therefore immediately flush):

diff --git a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
index 6370da0d58..651a6eec83 100644
--- a/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
+++ b/third_party/blink/renderer/core/inspector/inspector_network_agent.cc
@@ -36,6 +36,7 @@
 #include "base/containers/span.h"
 #include "base/macros.h"
 #include "base/memory/scoped_refptr.h"
+#include "base/logging.h"
 #include "build/build_config.h"
 #include "net/base/ip_address.h"
 #include "net/base/ip_endpoint.h"
@@ -1122,14 +1123,15 @@ void InspectorNetworkAgent::WillSendRequestInternal(
   Maybe<String> maybe_frame_id;
   if (!frame_id.IsEmpty())
     maybe_frame_id = frame_id;
+  VLOG(1) << "WillSendRequestInternal:: URL:" << request_info->getUrl();
   GetFrontend()->requestWillBeSent(
       request_id, loader_id, documentURL, std::move(request_info),
       base::TimeTicks::Now().since_origin().InSecondsF(),
       base::Time::Now().ToDoubleT(), std::move(initiator_object),
       BuildObjectForResourceResponse(redirect_response), resource_type,
       std::move(maybe_frame_id), request.HasUserGesture());
-  if (is_handling_sync_xhr_)
-    GetFrontend()->flush();
+  // if (is_handling_sync_xhr_)
+  GetFrontend()->flush();
 
   if (pending_xhr_replay_data_) {
     resources_data_->SetXHRReplayData(request_id,

the test PASSES since we now see the the Network.requestWillBeSent before the Fetch.requestPaused for import-me.js! ✅ 🎉

If you think I'm on the right track (and Chrome is misbehaving here), please let me know and I will contribute a test to Chromium and then work with you on figuring out the appropriate patch. (I'm assuming always flushing won't fly 😄 .)

I've also noticed that Fetch and Network are separate domains. Do they queue independently and are therefore going to cause us to see unexpected orderings of combined events depending on when each of them flushes? (If they queue in the same queue, the order would be preserved regardless of when we eventually flush.)

/cc @aslushnikov since it looks like he fixed a similar timing/flushing bug for sync xhrs.

@rwoll
Copy link
Member Author

rwoll commented Mar 28, 2021

I opened a repro test case in Chromium: https://chromium-review.googlesource.com/c/chromium/src/+/2789674.

This is operating on the assumption that the Network.requestWillBeSent should come in before the Fetch.requestWillBePaused. (If you add the flush change from above, the test passes. Or, if you don't add the flush change, but replace importScripts(' final.js') with fetch('final.js') the Chromium test case passes. (I don't know enough about the internals of fetch vs loadScripts and how they interact with the chromium network stack to understand why the ordering of events differs, although maybe the synchronous nature of importScripts is relevant?)

Also, I noticed for network activity from workers the Fetch.requestPaused comes in on the parent session, but the corresponding Network.requestWillBeSent comes in on the newly attached worker target. Does each target flush events independently?

@pavelfeldman
Copy link
Member

Wow, great job! The order of the events is not that important as long as we get both requestPaused and requestWillBeSent. But it is essential that we receive requestWillBeSent and your flush pointer is a great discovery. Workers should flush messages upon exiting the task, which does not happen.

Something like this:
https://source.chromium.org/chromium/chromium/src/+/master:third_party/blink/renderer/core/exported/web_dev_tools_agent_impl.cc;l=528;drc=ec0fe0e6c1d5827f109e6e7f9ee49155ef1e38f7;bpv=0;bpt=1

should happen for workers too. @dgozman: could you take a look?

@pavelfeldman
Copy link
Member

@rwoll
Copy link
Member Author

rwoll commented Mar 29, 2021

@pavelfeldman Thanks for the pointers! (No pun intended 😄 ) I guess order doesn't matter, but it does matter that we receive both events independently and that they are not blocked by one another. (Currently, Network.requestWillBeSent isn't getting flushed until Fetch.requestPaused gets continued and then this causes PW crNetworkManager to deadlock.)

I suppose my upstream test should be modified to not assert an ordering, but to assert that we see both events for final.js without actually continuing the request.

@rwoll
Copy link
Member Author

rwoll commented Mar 30, 2021

I updated the test and will try to upload upstream later. git cl upload keeps hanging and wanting to upload 1GB+ for a small change from the previous test, so I likely did not set something up correctly on the chromium tooling side.

@rwoll
Copy link
Member Author

rwoll commented Mar 30, 2021

Phew 🌬️ I fixed my chromium setup and was able to upload a new patch here with the failing test case: https://chromium-review.googlesource.com/c/chromium/src/+/2793525 1

I haven't yet had a chance to stick debuggers in the worker_inspector_controller to get my bearing in the code base and try to see what's wrong. My (naïve) instinct is to try adding a flush in the WorkerInspectorController::WillProcessTask although that would double the worker flush calls mostly unnecessarily (and may not even work—TBD). My hunch is that, with remote importScripts, the script task blocks until the request is continued, so we maybe don't hit the flush call in WorkerInspectorController::DidProcessTask while the request has yet to be continued. Although, I haven't yet been able to piece together where the blockage would be. (Maybe in worker_global_scope.cc::WorkerGlobalScope::FetchClassicImportedScript…LoadSynchronously?)

In the next day or so, I'll stick some debug statements in the code to understand the flow of things (and what a "task" even means in this context). One thing that's caught my attention, is that if you replace importScripts('final.js') with fetch('final.js') both Fetch.requestPaused and Network.requestWillBeSent do infact get flushed without needing to continue the paused request. What accounts for the difference? Does their JS behavior (one being sync blocking the worker JS thread, the other being async) have to do with it?


1 Ignore the other—now abandoned—change. I suppose I should have uploaded a new patch to the old review 🤦 . Learning!

@rwoll
Copy link
Member Author

rwoll commented Mar 30, 2021

Some notes/questions from debugging this morning:

  1. My (naïve) instinct is to try adding a flush in the WorkerInspectorController::WillProcessTask

    Aside from being an over correction, this wouldn't work as it's too early. requestWillBeSent in network_inspector_agent.cc will not have been invoked yet.

  2. Without continuing the final.js request, we do in fact block on worker_global_scope.cc::WorkerGlobalScope::FetchClassicImportedScript…LoadSynchronously which prevents us from hitting WorkerInspectorController::DidProcessTask which (I think) is the location where we end up flushing once the request is continued.

  3. While we are blocked in LoadSynchronously on the fetcher, we do generate the RequestWillBeSent in the the nework inspector, we just don't yet flush it.

@pavelfeldman or @dgozman: Since it looks like we won't get the flush from WorkerInspectorController::DidProcessTask until after the request is continued, would it make sense to somehow expose we are handling a synchronous import and then conditionally flush here like we do for sync xhr requests? I don't see enough info on any of the objects in WillSendRequestInternal to tell this case is a sync case. Is there another hook like WillLoadXHR that I could listen for to know its a sync request within the network inspector?

Thanks for y'alls help (and patience)! I definitely have a LOT of learning/reading to do to understand even the basic architecture of the rendered.

@rwoll
Copy link
Member Author

rwoll commented Mar 31, 2021

I don't see enough info on any of the objects in WillSendRequestInternal to tell this case is a sync case. Is there another hook like WillLoadXHR that I could listen for to know its a sync request within the network inspector?

It looks like one option could be to update the signature of InspectorNetworkAgent::WillSendRequest to pass along the SynchronousPolicy that gets set here and is available to us when calling resource_load_observer->WillSendRequest(…) in resource_fetcher.cc. Then we could conditionally flush in inspector_network_agent.cc. This is, of course, assuming there's not a more direct way to invoke flush at the right time in worker_inspector_controller.cc. Afterall, perhaps passing the SynchronousPolicy around is a bandaid to some other issue with flushing elsewhere…

@rwoll
Copy link
Member Author

rwoll commented Apr 1, 2021

I went with a simpler approach than changing WillSendRequest. Upstream patch available for review. I'll carry on implementation detail discussions there now that we have a candidate.

Thanks!

/cc @pavelfeldman @dgozman

rwoll added a commit to rwoll/playwright that referenced this issue Apr 2, 2021
rwoll added a commit to rwoll/playwright that referenced this issue Apr 9, 2021
rwoll added a commit to rwoll/playwright that referenced this issue Apr 9, 2021
rwoll added a commit to rwoll/playwright that referenced this issue Apr 9, 2021
rwoll added a commit to rwoll/playwright that referenced this issue Apr 10, 2021
aslushnikov pushed a commit that referenced this issue Apr 10, 2021
mjfroman pushed a commit to mjfroman/moz-libwebrtc-third-party that referenced this issue Oct 14, 2022
Network.requestWillBeSent is not getting flushed until
its corresponding Fetch.requestPaused is continued
when using importScripts with a remote resource in a
worker.

Both events should be emitted and flushed without needing to
continue the request for the imported script.

NB: This change assumes there's not a more direct/appropriate
time to invoke flush in WorkerInspectorController itself.

See discussion here: microsoft/playwright#5952 (comment)

Change-Id: I759d8c0e38d515bb07e8cebb73623b75baada5c8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2793525
Reviewed-by: Andrey Kosyakov <caseq@chromium.org>
Reviewed-by: Dmitry Gozman <dgozman@chromium.org>
Commit-Queue: Dmitry Gozman <dgozman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#868876}
GitOrigin-RevId: 031a7948c827a0e50000e1c5da40a11489cf4124
@Jabbar2010
Copy link

Jabbar2010 commented Mar 16, 2023

This bug is not fixed, version: 1.30, I can't intercept the request which is loaded from a worker by importScript
@rwoll

@dgozman
Copy link
Contributor

dgozman commented Mar 16, 2023

@Jabbar2010 Please file a new issue with a repro, filling in the "Bug Report" template.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants