1

Based on a flag I want to enable or disable the console.log in the production versions. But I don't want to expose that option to my users because then everyone will ask about it and waste my time.

If I allow such flag to be stored in user preferences thru the Options dialog, till those preferences are loaded, my code will be spewing out the console.log output till that flag is loaded from the chrome storage.

I can create a debug flag cookie using extensions but unfortunately my extension works on many sites.

If I disable console.log altogether in production versions but when clients face problem then at that moment I'll not be able to look into the console.log to debug it. So this is not feasible.

Is there any what my extension can know from the first line that console.log needs to be bypassed or not every time the extension is run( includes BG script, content script and the options dialog)?

user5858
  • 1,082
  • 4
  • 39
  • 79
  • What's the lifetime expectancy of a set flag? One session? The entire time it's installed? One page visit? As in, how often do you expect it to be changed. – Patrick Roberts Jun 03 '17 at 02:37
  • @PatrickRoberts as long as browser is open – user5858 Jun 03 '17 at 04:42
  • You can use synchronous `localStorage` in internal extension pages, but not in content scripts obviously. Anyway why don't you provide two versions of the extension? – wOxxOm Jun 03 '17 at 07:53
  • 2
    Under what circumstances is getting the sate of the flag from `chrome.storage` insufficient? If it's really an issue, one solution would be to queue the output you would normally be sending to `console.log` until such time as you know if you are supposed to do so. Depending on the state of the flag you receive, you can then either output the queue and change to no longer queuing, or drop what's in the queue and change to automatically doing nothing with the normal output. – Makyen Jun 04 '17 at 04:12
  • @Makyen we have environment variables in the unix/windows which can change the behavior of progam to run. Something like that I'd like to set so that the extension when runs know to spew out console.log – user5858 Jun 04 '17 at 07:44
  • That much was clear from your question. You did not answer my question as to what circumstances made it impossible/undesireable for you to delay all processing until the config data is available (i.e. in the async callback). Except for some specific circumstances, this is a normal issue for asynchronous programming, which is most commonly solved by just waiting for the data. Sometimes, that is not a sufficient solution, but that is dependent on the exact circumstances. – Makyen Jun 04 '17 at 17:45
  • @Makyen I agree with you that a debug flag can be loaded from saved Options. However I don't want to expose any developer options to naive users who will be confused with debug option and moreover I'd not want to divulge this option to everybody. – user5858 Jun 06 '17 at 04:43
  • @user5858, I'm not sure what your point is. You have a complete HTML page of options. You just pick one of a plethora of ways to hide additional config options until you tell the person you want to know about it how to display the extra options. For instance, it could be that they have to be holding some key while clicking on a particular part of your options page. When they do, additional options are displayed. Exactly how to do it is something you could ask in another question, but you would need to decide exactly what you want, otherwise it would be too opinion based for SO, maybe [ux.se]. – Makyen Jun 06 '17 at 05:03
  • You can try with [deblog](https://github.com/msalafia/deblog). You can configure your custom methods for logging and disable them as you prefer. The console.log will be untouched. – msalafia May 22 '22 at 14:10

1 Answers1

2

There is nothing that will set a value in all contexts which you don't have to obtain asynchronously. You're going to have to deal with it.

Primary method: Wait for the info. Delay execution until available.

The normal way to deal with this type of situation, is to just put all processing which might need the information into the program flow that begins with the callback of the the asynchronous call to storage.*. This is normal and expected with asynchronous programming. In almost all cases, this is how you should deal with it.

Waiting for the info should be easy for scripts in the background context (the background script/event page, popups, options pages, etc.) For anything other than a background script, these scripts are almost all user-interaction based functionality, which means the delay for a fetch to storage.local should be trivial. If it's not, you can always copy the value to a variable in the background page, which can be synchronously accessed directly by any script in the background context (see: Communicate between scripts in the background context (background script, browser action, page action, options page, etc.)).

For the background script, you could also use localStorage, which is synchronous. However, the data will not be available in all contexts.

For content scripts, needing to delay for the asynchronous data might mean moving your injection time earlier. In other words, you might need to inject (run_at or runAt) at document_start instead of document_idle or document_end. You would then need to delay the rest of processing until after the document is ready/loaded.

For content scripts already loaded at document_start

Specifically for console.log()

For your specific issue, output via console.log(), you can queue the output which would normally be sent to console.log() until after you have the data back from the async call to storage.local. Once you have the data, you can then either A) output everything in the queue and set everything to output normally, or B) dump everything in the queue and set every call to be dropped.

All of this can easily be done with just overwriting the function pointer at console.log to point to the queue function, the normal console.log(), or a null function. Thus, there would be no need to change how the rest of your script calls console.log(). If you are using other functions in console, it would get a bit more complicated, but console.log() is easy (I've done it, for performance reasons during testing, not for this particular issue).

In general (i.e. for things which can not be queue'ed for later)

This one is at best, non-trivial. There's no good solution for scripts injected via a manifest.json content_scripts entry.

Partition your code

Separate your code into what must be done immediately and what can wait for the callback. This may require you to manipulate the page such that some actions in the normal loading process are delayed until after you have the information. What you would actually need to do here will vary depending on exactly what you are wanting to accomplish. The best solution may end up being to have your code error on one side or the other of what your configuration would change, then patch things up to what they should be once you get the configuration information that indicates it should be the other way.

For example, an extension that is intended to prevent JavaScript from running in the page, but has a whitelist of permitted scripts/domains could just go ahead and remove all of the <script> elements as they are inserted. Then, once the configuration information is available, the permitted <scripts> could be re-inserted into the DOM.

Use chrome.tabs.executeScript()

With chrome.tabs.executeScript() you can inject a constructed script with configuration information prior to the injection of your main script. These scripts are in the same context so the first script can set variable(s) containing config information. For more information see: Pass a parameter to a content script injected using chrome.tabs.executeScript()

The background script (which executes the chrome.tabs.executeScript()) is in a different process than the process for the tab (the one that actually injects the script). Thus, when exactly a script is injected is going to be indeterminate due to the asynchronous nature of having multiple proceses. This means that you can not 100% guarantee that your script will be injected prior to the initial HTML being loaded into the DOM. You can usually inject prior to that, but not always.

When to inject for the earliest possible time is a bit complicated. One of the problems is that if you call tabs.executeScript() too early in the process of loading a new page, the injection does not take place in the new page. My testing (which I need to go back an re-do as I believe it could be improved) indicated:

Related: Google chrome extension: how to inject script immediately after page reload?

Makyen
  • 31,849
  • 12
  • 86
  • 121