Comments (32)
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 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.
What if we could just prevent it from sleeping in the first place?
from scratchaddons.
This might be obvious, but of course we can do better than Stylus:
- Only a few addons want to run ASAP. This includes
injectAsStyleElt:true
andrunAtComplete:false
. - We only run on the
scratch.mit.edu
origin, so we can use its caching mechanisms, such as IndexedDB. We're not only restricted tochrome.storage
. - 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.
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.
- 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. - 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. - 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.
- 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. - 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. - 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. - 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.
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.
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.
We could also look at other extensions (Stylish, Dark Reader, etc.) to see how they've fixed this.
from scratchaddons.
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.
I can reproduce this, it takes a lot longer for Scratch Addons to run its scripts than before the update.
from scratchaddons.
Can repro on Firefox/Gecko
from scratchaddons.
Okay, it seems that this issue is actually a combination of two existing issues.
-
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.
-
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.
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.
Version v1.38.0 should be rolling out to Firefox users soon, so we might get additional feedback.
from scratchaddons.
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.
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.
After we merged #7417 it should be a bit faster. Surely not slower.
from scratchaddons.
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.
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
-
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.
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.
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.
the userstyle manager stylish seems to be using manifest v3, i checked
from scratchaddons.
I believe Stylish is not open-source anymore, so Stylus is preferred by the community.
from scratchaddons.
but stylish seems interesting, how does it even not delay the injection by even a second?
from scratchaddons.
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.
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.
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.
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:
- 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.
- Only use the cache if on the stable build. While developing, "load ASAP" CSS files would not load any faster than any other userstyles.
- 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.
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.
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.
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.
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)
- Revamp `mediarecorder`'s "Start Recording" modal HOT 8
- Doesn't work on Android Firefox since the recent update HOT 7
- `mediarecorder`: custom shortcut to stop- and start recording HOT 2
- 3.0 Scratchblocks on forums: Apply custom block styles HOT 1
- Jump to custom block definition: add context menu option HOT 1
- Remove `handle-licenses.js` HOT 1
- Remove unnecessary `MANIFEST_VERSION` constants HOT 1
- Rearrangeable custom block inputs: Adding or rearranging an empty-string label creates "%l" labels. HOT 7
- Settings page works - but addons don't - on v1.38.1 HOT 37
- Service worker restart breaks regex matches
- Scratch Addons refuses to work on some pages in newest Chrome update. HOT 2
- Auto-hiding palette does not work with dropdowns or text regions HOT 1
- Hover over a custom block input to see the name of it as a tooltip HOT 1
- Legal: Should the licenses page include the author and copyright year? HOT 1
- An extension to clear certain lists on save HOT 5
- Interpolation HOT 3
- Finding the root cause of the constant log-outs HOT 12
- Invalid addon.json causes error page to repeatedly open
- Addons turning off automatically maybe because of synchronization HOT 2
- `turbowarp-player`: gamepad, fullscreen and player options do not follow darkmode scheme HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from scratchaddons.