Coder Social home page Coder Social logo

Comments (32)

Waakul avatar Waakul commented on July 4, 2024 3

After we merged #7417 it should be a bit faster. Surely not slower.

it's neither, it doesn't even load the plugins.

from scratchaddons.

OneShot-Niko avatar OneShot-Niko commented on July 4, 2024 1

@OneShot-Niko That is interesting. Note that Firefox implements event pages, not service workers. Event pages do retain memory as far as I know. So the new delay should be 100% attributed to the time it takes Firefox to wake up the event page, unless I'm missing something here. I'll test myself soon, if you've got any additional details add them to this issue. Of course, the fix number 1 listed in my comment above (making content scripts aware of user settings) would fix this in Firefox no matter what the actual root cause is.

Did a bit of messing around and it seems like it's like you said - Just the background script stopping and then needing to wake up.

2024-05-21.15-48-33.mp4

If it'd help, I'm on FF 126.0 (32 bit) via SA commit e6f38f6

from scratchaddons.

Waakul avatar Waakul commented on July 4, 2024 1

What if we could just prevent it from sleeping in the first place?

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024 1

This might be obvious, but of course we can do better than Stylus:

  1. Only a few addons want to run ASAP. This includes injectAsStyleElt:true and runAtComplete:false.
  2. We only run on the scratch.mit.edu origin, so we can use its caching mechanisms, such as IndexedDB. We're not only restricted to chrome.storage.
  3. In the store releases, JS and CSS files cannot change unless the version number changes, unlike Stylus. A <filename X version number> cache is sufficient.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024 1

Here's how "fast CSS injection" (for dark mode and 2.0→3.0) worked in Manifest V2. I think it's nice if anyone reading can understand how it worked, so that we can think of the best solution for MV3.

  1. The user clicks a link. A request to scratch.mit.edu is now happening over the internet. The browser keeps displaying the old page for now.
  2. The webRequest onBeforeRequest is very rapidly dispatched to our background page, which would always be running as this is a persistent background page in MV2.
  3. The background page starts pre-calculating which addons will need to run, as it already knows the URL of the request. Most of this work is synchronous - it's just a for loop on the manifests and looking at the addon settings. Again, as our MV2 background page was not short-lived, the manifests and settings are already available.
  4. The point above includes asynchronously fetching (reading from disk) any CSS files that have injectAsStyleElt:true. Those userstyles are directly stored as CSS text instead of just a reference to a URL.
  5. When the browser receives the first packet(s) of the HTML response for the page, the browser starts constructing the DOM. At that point, extensions content scripts with document_start are injected to the page. That includes our content scripts.
  6. Our content scripts tells the background page "hi, what do I need to inject?" and the background page can respond rapidly, as it already precalculated everything. This response message includes the raw dark-www CSS if applicable, so when the content script receives that, it can immediately (synchronously) add that CSS to the document. Remember that a <link rel=stylesheet href="..."> could take some time to apply. Instead, a <style> tag is used.
  7. By the point the browser is confident it can render the first frame of the page, our CSS is already there, so no flashes happen. Exactly when the first frame is rendered depends in a number of factors: network latency, HTML size of the requested page, whether the HTML was received in multiple packets separated by 100+ milliseconds each or if they all came together, whether Scratch CSS that the browser considers "high priority" through some complicated algorithm is already available, etc.

Back in MV2, applying dark mode would only take a few seconds if a Scratch page was the default new-tab to open in the browser. Why that is the case should be obvious now, I think.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024 1

So, we use a content script to run addon userscripts and inject addon userstyles on Scratch pages. What if our content script(s) didn't need the service worker in order to do its job? If all we use the SW for is to get addon manifests, user settings, and other data, then maybe we could store all the data that we need in the IndexedDB of Scratch instead of it being handled by the SW, as an example.

I guess that's we'll do, I can't think of any alternative option.

All addon manifests are already stored in chrome.storage.session. Addon settings (includes whether addons are enabled or not) are already available in chrome.storage.sync to content scripts. This information should be enough for content scripts to decide themselves which JS and CSS files to inject while the page is still loading.

I was thinking about using IndexedDB to store the raw CSS text files, but we should instead consider the Cache API which was built for exactly this purpose. When I opened issue #4561 I was not aware of the fact the cache API is available in all execution contexts, not just service workers. (this has nothing to do with our background service worker, this is about website service workers)

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024 1

After some experiments, I'm not sure if the Cache API is fast enough for our use case. I still see some flashing.

I think the way to go is something that is synchronous or fast enough.

  • The localStorage API has a limit of ~5MB per origin, and is synchronous, so it's impossible that we miss the first frame.

  • The chrome.storage.session API has a 10MB limit for the whole extension and is callback/promise based, but after some tests it looks fast enough, as it reads directly from RAM.

  • EDIT: of course, it would also be synchronous if the dark mode CSS was "inlined" inside the JS file, as part of a build step.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024 1

We could also look at other extensions (Stylish, Dark Reader, etc.) to see how they've fixed this.

from scratchaddons.

mxmou avatar mxmou commented on July 4, 2024 1

How should we handle the CSS caching in the development version of the extension? I'm not sure.
Right now, if I change any CSS file and reload the page, the new version of the file is applied. It is quite handy.

We should any of the following options:
(...)
2. Only use the cache if on the stable build. While developing, "load ASAP" CSS files would not load any faster than any other userstyles.

This is probably the best solution. Option 3 is also good, but it's more complex and I don't think how fast the styles are loaded is very important while developing.

from scratchaddons.

mybearworld avatar mybearworld commented on July 4, 2024

I can reproduce this, it takes a lot longer for Scratch Addons to run its scripts than before the update.

from scratchaddons.

OneShot-Niko avatar OneShot-Niko commented on July 4, 2024

Can repro on Firefox/Gecko

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

Okay, it seems that this issue is actually a combination of two existing issues.

  1. Consider that the time it takes to wake up the service worker is outside of our control. It's possible that in some devices and contexts, that's the biggest portion of the delay. A possible fix for this situation is described in issue #4561 which specifically focuses on the dark mode and related addons. The idea is to make content scripts self-aware so that they can inject CSS into the page even if the background context is gone or still waking up.

  2. After the service worker has been restarted, at least some of the delay is because of our own code. PR #7417 attempts to reduce this delay as much as possible, unless the service worker is being executed for the first time of the session.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

Can repro on Firefox/Gecko

@OneShot-Niko That is interesting. Note that Firefox implements event pages, not service workers. Event pages do retain memory as far as I know. So the new delay should be 100% attributed to the time it takes Firefox to wake up the event page, unless I'm missing something here. I'll test myself soon, if you've got any additional details add them to this issue.
Of course, the fix number 1 listed in my comment above (making content scripts aware of user settings) would fix this in Firefox no matter what the actual root cause is.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

Version v1.38.0 should be rolling out to Firefox users soon, so we might get additional feedback.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

Event pages do retain memory as far as I know.

Okay, after some testing, I was wrong. Firefox event pages behave similarly to Chrome service workers. The only benefit is DOM APIs, and probably shutting down less often. See do not rely on global variables.
This means there's no reason to avoid chrome.storage.session on Firefox.

Unfortunately, until v1.37.1 we used persistent background pages because the non-persistent ones were not compatible with webRequestBlocking.

Ideally we should reconsider the whole extension architecture with this new paradigm. The good news is that proper mobile support is closer than ever. (ideally we should also consider running Scratch Addons on top of Tampermonkey, decoupling the settings page from the extension completely, etc.)

from scratchaddons.

Waakul avatar Waakul commented on July 4, 2024

This doesn't seem to happen with the stylish extension even though it uses mv3. Maybe a clue to sorta fix this in scratch addons?

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

After we merged #7417 it should be a bit faster. Surely not slower.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

In my Chrome browser, it takes around 3 seconds to wake up the service worker, so that's three seconds we cannot speed up in any way.

from scratchaddons.

DNin01 avatar DNin01 commented on July 4, 2024

In my Chrome browser, it takes around 3 seconds to wake up the service worker, so that's three seconds we cannot speed up in any way.

Ouch. For me, it's usually fast enough to start up before the page loads. I haven't tested that extensively, though.

What if we could just prevent it from sleeping in the first place?

Google, unfortunately, does not allow that.1

it's neither, it doesn't even load the extensions.

Yeah, sorry about that. It should be fixed next release.

Footnotes

  1. In rare cases, it is necessary to extend the lifetime indefinitely. We have identified enterprise and education as the biggest use cases, and we specifically allow this there, but we do not support this in general. In these exceptional circumstances, keeping a service worker alive can be achieved by periodically calling a trivial extension API. It is important to note that this recommendation only applies to extensions running on managed devices for enterprise or education use cases. It is not allowed in other cases and the Chrome extension team reserves the right to take action against those extensions in the future.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

In my Chrome browser, it takes around 3 seconds to wake up the service worker, so that's three seconds we cannot speed up in any way.

Of course, by "in any way" I mean any architecture that waits for the background context to determine whether dark mode (or any other addon) needs to be injected.

from scratchaddons.

DNin01 avatar DNin01 commented on July 4, 2024

By the way, Stylus, the userstyle manager, will apparently not be migrating to MV3 due to this issue.

openstyles/stylus#1430 (comment)

In other words ManifestV3 is inherently inferior for userstyles as observed with the original Stylish extension if you open a page, read it for more than 30 seconds, open another page on a fast site. It seems unreasonable to break Stylus as well, so I think we should just keep using ManifestV2 until a native API is implemented.

from scratchaddons.

Waakul avatar Waakul commented on July 4, 2024

the userstyle manager stylish seems to be using manifest v3, i checked

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

I believe Stylish is not open-source anymore, so Stylus is preferred by the community.

from scratchaddons.

Waakul avatar Waakul commented on July 4, 2024

but stylish seems interesting, how does it even not delay the injection by even a second?

from scratchaddons.

DNin01 avatar DNin01 commented on July 4, 2024

So, we use a content script to run addon userscripts and inject addon userstyles on Scratch pages. What if our content script(s) didn't need the service worker in order to do its job? If all we use the SW for is to get addon manifests, user settings, and other data, then maybe we could store all the data that we need in the IndexedDB of Scratch instead of it being handled by the SW, as an example.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

In my Chrome browser, it takes around 3 seconds to wake up the service worker, so that's three seconds we cannot speed up in any way.

It is quite ridiculous that it takes so much time. In my device, loading a service worker happens slower than loading multiple HTML, CSS, JS, and image assets through the internet across fiber optic cables under the pacific ocean.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

Ideally our 4xx and 5xx status code detection (e.g. 404) should not depend on the background service-worker. It will be tricky to get that right.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

How should we handle the CSS caching in the development version of the extension? I'm not sure.
Right now, if I change any CSS file and reload the page, the new version of the file is applied. It is quite handy.

We should any of the following options:

  1. Require a manual cache clear, by reloading the extension or through some other way. Currently, a page reload is sufficient. Sounds like a bad idea.
  2. Only use the cache if on the stable build. While developing, "load ASAP" CSS files would not load any faster than any other userstyles.
  3. We could get creative if we want: while the page is loading we inject the cached CSS to the page, but we also fetch the file anyway. If it changed, we dynamically change the CSS of the page and update the cache. If it didn't change, then we don't do anything.
    This alternative has one positive aspect: the logic to replace outdated CSS with new fresh CSS could be reused to build a development mode where any addons that are "currently in development" don't even need a page reload. We could "poll" the userstyles from disk every few seconds and automatically apply the changes.

Would like to have @mxmou's opinion if he happens to have any.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

Chrome supports the blocking="render" attribute: see renderBlockingStatus.

The keyword here is Flash of Unstyled Contents ("FOUC"). Unfortunately, most online resources assume that the reader is building a static HTML page, which is not our case.

I also wonder if document.write() or document.implementation.createHTMLDocument() could be of any help. Here's a small YouTube clip about those.

from scratchaddons.

DNin01 avatar DNin01 commented on July 4, 2024

I haven't brought this up until now because I was not sure we were going to use it, but there is a way to dynamically declare content scripts and CSS. It's just like the "content_scripts" key in the extension manifest, but you can change it on the fly, instead of setting up predetermined JS and CSS to use that can't be changed without modifying manifest.json.

Userstyles with "injectAsStyleElt": true and userscripts with "runAtComplete": false could be dynamically registered as content when their corresponding addons are enabled, and unregistered when the addon is disabled. Obviously, we also have to keep in mind the addon APIs, dynamic enable/disable, etc., so maybe it's not that simple (we might even have to completely change how the addon loader works), but you get the idea.

That might be fast enough to get any time-sensitive JS or CSS injected as soon as the document loads, and we could use a storage API to store user settings for quick access.

On that note, storing arbitrary data in the localStorage of a website that it isn't expecting should be done with caution. If chrome.storage.session doesn't create any noticeable compromises, we should probably use it instead.

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

Yes, extensions can register styles along with JS content scripts (and now dynamically!), but a side-effect is that the priority system of the stylesheets is different in that case. So that is why I went with the simplest option some years ago, which was relying in the DOM tree only to dictate which stylesheets have more priority than others.

See MDN: CSS Cascade

(Honestly, I never researched whether content script CSS can be "uninjected" from the page (dynamicDisable we'd say) - it may be possible, but it is clear that our use-case of handling hundreds of stylesheets is not common)

from scratchaddons.

WorldLanguages avatar WorldLanguages commented on July 4, 2024

As a first step all injectAsStyleElt addons will use normal <link> stylesheets instead, so we can forget about the caching for now. We will only cache raw CSS text if it's really needed to speed things up.

It will take me some time to move the "which styles should be injected" logic to the content script context.

from scratchaddons.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.