The default bug view has changed. See this FAQ.

Pass initial options to content scripts without async messaging (contentScriptOptions equivalent)

NEW
Unassigned

Status

()

Toolkit
WebExtensions: Untriaged
P2
normal
2 months ago
9 days ago

People

(Reporter: The 8472, Unassigned)

Tracking

(Blocks: 1 bug)

Trunk
Points:
---

Firefox Tracking Flags

(firefox53 affected)

Details

(Whiteboard: [design-decision-approved] triaged)

(Reporter)

Description

2 months ago
Scripts running during `document-start` that want to ensure they can apply some user-configrable behavior before any page script runs and before the page is painted cannot wait for asynchronous messaging or storage APIs. They need to be passed some initial set of options which can be changed from the background page or panel. In case there is no guarantee that background pages load before the first tab does this would also have to be persisted across restarts so there's no race between background script initialization and content scripts reading that data.

I think currently the only way to achieve something like that is to use synchronous XHRs and intercepting them with webrequest, which is pretty ugly and not great for load times.

The addon-sdk provides `contentScriptOptions`[1], bootstrapped extensions have many ways to achieve this such as preloading the options in a per-process JSM, reading preferences, using `nsIContentProcessMessageManager.initialProcessData` (which makes this 0-RTT even during startup) or using synchronous messages.

bug 1323433 proposes a superset of this but has been deemed low-prio due to complexity. So one-way API to preload data for content script might be simpler to implement.

[1] https://developer.mozilla.org/en-US/Add-ons/SDK/Guides/Content_Scripts#Passing_configuration_options

Updated

2 months ago
Whiteboard: [design-decision-needed] triaged

Comment 1

a month ago
To be discussed at 2/21 WebExtensions triage meeting. 

Agenda: https://docs.google.com/document/d/1H4sjnRFc87NZXZsM6XIgwWaoV2bdRFepiG28G7WzgPs/edit

Comment 2

a month ago
This would be hugely useful to NoScript, even if it could be polyfilled on non supporting platforms with an obvious performance and complexity cost.
Blocks: 1214733

Comment 3

18 days ago
Approved; P2. Put on tracking schedule for 57.
Flags: needinfo?(amckay)

Updated

18 days ago
webextensions: --- → ?
Flags: needinfo?(amckay)
Priority: -- → P2
Whiteboard: [design-decision-needed] triaged → [design-decision-approved] triaged
(In reply to The 8472 from comment #0)
> In case there is no guarantee that background pages load before the first tab
> does this would also have to be persisted across restarts so there's no race
> between background script initialization and content scripts reading that data.
There is no such guarantee.  Additionally, content scripts are only loaded after the background page is done, so you are not guaranteed to be able to run your code before the page's scripts anyway, and must code with that in mind (luckily, only an issue for the first tab in practice).

> I think currently the only way to achieve something like that is to use
> synchronous XHRs and intercepting them with webrequest, which is pretty ugly
> and not great for load times.
Even this is not guaranteed to work for the first tab, as all the webRequest.*.addListener methods are async anyway, and could end up missing the requests at startup.

> bootstrapped extensions have many ways to achieve this such as preloading the
> options in a per-process JSM, reading preferences, using `.initialProcessData`
> (which makes this 0-RTT even during startup) or using synchronous messages.
All of these necessarily affect the startup performance (of the content process), and it could be especially bad if we allow unbound extension data, which can for some addons (ad/tracker/xx blockers) measure in megabytes.

Because of this concern, my current plan is to only guarantee sync data availability with some sane limit on data size (something like 4k per extension).  Any extension that needs more than that could check for availability of the config data in sync, with an event as a fallback.  (Again, this would only be an issue for the first tab loaded per process, as we would obviously preload/cache the data for subsequent tabs).

So the API would look something like:

  from content script:
    browser.runtime.configData;   // ready only property 
    browser.runtime.onConfigDataAvailable.addListener(...);

  from background page:
    browser.runtime.setConfigData(value);
(In reply to Tomislav Jovanovic :zombie from comment #4)
> (In reply to The 8472 from comment #0)

> Even this is not guaranteed to work for the first tab, as all the
> webRequest.*.addListener methods are async anyway, and could end up missing
> the requests at startup.
> 
This sounds incredibly bad for any adblocker and, of course, NoScript.
Are you sure of this? If so, this *must* be fixed ASAP.
Flags: needinfo?(tomica)

Comment 6

9 days ago
> Because of this concern, my current plan is to only guarantee sync data
> availability with some sane limit on data size (something like 4k per extension).

Arbitrary limits like this make for seemingly broken features.  For Greasemonkey, I can't guarantee that (the sum of all installed) scripts' source are any particular size.  As a user, I'd prefer a little slowness to features I depend on sometimes working and sometimes not.

Alternately: if there's a circumstance in which I (any extension author) can guarantee a tiny thing works, but not other things, I'm likely to apply a really dirty hack like: notice that I'm in a start-up case and can only run 4k of code, make that small code "block for a while and reload the page" so the document_start event happens again, then be outside of the startup case and able to make my feature work.  Which is obviously worse than just letting me pass more data to begin with.  The user experience will still be that start up takes a long time, but it's likely to be even worse than just letting me pass some data directly.
(Reporter)

Comment 7

9 days ago
> For Greasemonkey, I can't guarantee that (the sum of all installed) scripts' source are any particular size. 

In my opinion scripts or similarly large data should not be stored in such configuration storage as strings. Blobs (if structured clone is allowed) or Blob URIs would be far better. Blob URIs can load their content into the process lazily thus avoiding the bloat and they can use shared memory.

We need a way to eval scripts from URIs synchronously, like loading a <script> tag, but in the sandbox of an addon or in terms of a ES6 Realm[1][2] instead of the content page.
The easiest approach here would be to expose the subscript loader[3] to addons in some way.

I have put some effort into loading things from URIs in greasemonkey and it improved performance and reduced footprint. I would hate to see webextensions undoing those gains.

[1] http://www.ecma-international.org/ecma-262/6.0/#sec-code-realms
[2] https://gist.github.com/dherman/7568885
[3] https://developer.mozilla.org/en-US/docs/Mozilla/Tech/XPCOM/Reference/Interface/mozIJSSubScriptLoader#loadSubScript()

Comment 8

9 days ago
> This sounds incredibly bad for any adblocker and, of course, NoScript.
> Are you sure of this? If so, this *must* be fixed ASAP.

Agree, for Greasemonkey, this will break features.  (From the user's perspective: sometimes things work, sometimes (first tab?) things don't work or break in confusing ways.)

> In my opinion scripts or similarly large data should not be stored in such configuration storage as strings.

Probably off-topic so please email me directly, but: what option do I have under WebExtension besides string?  AFAICT I only get storage.local, which only stores strings, and I can only talk to content processes with sendMessage() (and alikes) which only send strings?

I'd like to be able to store (in background) a blob, later get (only) its URL and pass that URL (synchronously) to content script(s), but I know of no way to do any of those things.
(In reply to Giorgio Maone [:mao] from comment #5)
> This sounds incredibly bad for any adblocker and, of course, NoScript.
> Are you sure of this? If so, this *must* be fixed ASAP.

From my understanding and from bug 1343940 comment #3, that is the case, at least theoretically.  However, I'm not sure if this is ever an issue in practice.

(Also, from my limited understanding how *blockers work, they already must account for situations like this because of a number of hack techniques that web pages employ).


(In reply to Anthony Lieuallen from comment #6)
> Arbitrary limits like this make for seemingly broken features.  

I agree the number is arbitrary, but any limit would be.  It is not ideal, but if the only alternative is unbound amount of data (from unbound number of extensions), I don't think that would be acceptable when firefox starts using 4-8 processes, considering both memory and performance implications.

> Greasemonkey, I can't guarantee that (the sum of all installed) scripts'
> source are any particular size.  As a user, I'd prefer a little slowness to
> features I depend on sometimes working and sometimes not.

Content scripts already don't guarantee that your code will be executed when you specify, so if you don't account for that, it already works only sometimes.

> I'm likely to apply a really dirty hack [...] Which is obviously worse 
> than just letting me pass more data to begin

Not necessarily.  If I have userscripts enabled for only a few domains (common?), then slowing down *every* process load on the odd chance that the first tab in the process is one of those is not better IMO.
Flags: needinfo?(tomica)
You need to log in before you can comment on or make changes to this bug.