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

Unable to open the Extensions page with --start-url when running in Chrome #1979

Open
ghostwords opened this issue Jul 29, 2020 · 29 comments
Open

Comments

@ghostwords
Copy link

Is this a feature request or a bug?

Bug

What is the current behavior?

Running an extension in Chrome with --start-url "chrome://extensions" (or "about:extensions") fails to open the Extensions page. (The extensions page is useful for inspecting your extension's background page.)

Using --start-url to open about:debugging does work in Firefox.

Versions

Chrome 84
web-ext 5.0.0

@rpl
Copy link
Member

rpl commented Jul 31, 2020

It seems that Chrome does ignore chrome://extensions (also other chrome:// urls, like chrome://version) passed on the command line, e.g. executing on the command line

google-chrome http://developer.mozilla.org

does start Chrome with the given url loaded in the first tab

On the contrary:

google-chrome chrome://version

does start Chrome but it doesn't load that chrome url in the first tab.

I have the feeling that this may be an intended behavior, I briefly looked if Chrome does provide a command line flag to override this behavior, but at the moment I haven't spotted any that may be related.

We may have to close this as a wontfix if we don't have a way to tell Chrome to allow chrome:// urls as start urls, but for now I'm keeping this open to take another look and think a bit if there may be other options to achieve that.

@Rob--W
Copy link
Member

Rob--W commented Jul 31, 2020

Chrome extensions can open many chrome:-pages.

@rpl
Copy link
Member

rpl commented Jul 31, 2020

Chrome extensions can open many chrome:-pages.

that's true, and I just verified that chrome://extensions is one of those that can be opened successfully using chrome.tabs.create, and so one option to fix this in web-ext would be to special case the chrome://extension url (or any chrome url) in the chrome extension runner and make the companion extension that we install automatically to auto-reload the target extension able to call chrome.tabs.create for us.

@Rob--W
Copy link
Member

Rob--W commented Aug 6, 2020

I'll mentor this bug.

@DeepikaKaranji
Copy link

Hi @Rob--W !

May I work on this bug?
If yes, can I have the STR?
I went through https://extensionworkshop.com/documentation/develop/getting-started-with-web-ext/, but a little more clarity on where chrome comes into the picture will be more helpful.
Thanks!

@Rob--W
Copy link
Member

Rob--W commented Aug 11, 2020

@DeepikaKaranji Taking a step back, the core of the issue is that trying to open chrome://extensions doesn't work from the command line. Extensions are able to do so though, by passing that url to chrome.tabs.create.

web-ext uses a helper extension (via createReloadManagerExtension) in Chrome to support its functionality. To resolve this bug you have to forward the URL to that helper extension instead of the command line. The relevant source code is around these lines:

// Create the extension that will manage the addon reloads
this.reloadManagerExtension = await this.createReloadManagerExtension();
// Start chrome pointing it to a given profile dir
const extensions = [this.reloadManagerExtension].concat(
this.params.extensions.map(({sourceDir}) => sourceDir)
).join(',');
const {chromiumBinary} = this.params;
log.debug('Starting Chromium instance...');
if (chromiumBinary) {
log.debug(`(chromiumBinary: ${chromiumBinary})`);
}
const chromeFlags = [...DEFAULT_CHROME_FLAGS];
chromeFlags.push(`--load-extension=${extensions}`);
if (this.params.args) {
chromeFlags.push(...this.params.args);
}
// eslint-disable-next-line prefer-const
let {userDataDir, profileDirName} =
await ChromiumExtensionRunner.getProfilePaths(
this.params.chromiumProfile);
if (userDataDir && this.params.keepProfileChanges) {
if (profileDirName
&& !await ChromiumExtensionRunner.isUserDataDir(userDataDir)) {
throw new Error('The profile you provided is not in a ' +
'user-data-dir. The changes cannot be kept. Please either ' +
'remove --keep-profile-changes or use a profile in a ' +
'user-data-dir directory');
}
} else if (!this.params.keepProfileChanges) {
// the user provided an existing profile directory but doesn't want
// the changes to be kept. we copy this directory to a temporary
// user data dir.
const tmpDir = new TempDir();
await tmpDir.create();
const tmpDirPath = tmpDir.path();
if (userDataDir && profileDirName) {
// copy profile dir to this temp user data dir.
await fs.copy(path.join(
userDataDir,
profileDirName), path.join(
tmpDirPath,
profileDirName),
);
} else if (userDataDir) {
await fs.copy(userDataDir, tmpDirPath);
}
userDataDir = tmpDirPath;
}
if (profileDirName) {
chromeFlags.push(`--profile-directory=${profileDirName}`);
}
let startingUrl;
if (this.params.startUrl) {
const startingUrls = Array.isArray(this.params.startUrl) ?
this.params.startUrl : [this.params.startUrl];
startingUrl = startingUrls.shift();
chromeFlags.push(...startingUrls);
}
this.chromiumInstance = await this.chromiumLaunch({
enableExtensions: true,
chromePath: chromiumBinary,
chromeFlags,

With those two code snippets, you can probably get started. Let me know if there are still questions!

@caitmuenster
Copy link

Hey @DeepikaKaranji, how's it going with this issue?

@DeepikaKaranji
Copy link

DeepikaKaranji commented Sep 15, 2020

Hello @caitmuenster , I was AFK for a bit, I've set up the environment, and I have gotten started with this issue yesterday.

@DeepikaKaranji
Copy link

DeepikaKaranji commented Sep 23, 2020

@Rob--W

How I thought I'll approach this was:

  1. See what gets called when I run google-chrome http://developer.mozilla.org from command line.
  2. From that, understand which variable exactly holds the URL that is being passed in command line.
  3. See where that http URL is getting passed.
  4. Find out when the createReloadManagerExtension is actually called.
  5. And of course, other in-between things I need check as I work.

What I've understood/ not understood so far:

  1. createReloadManagerExtension() is called everytime ChromiumExtensionRunner object is instantiated.
  2. bg.js is run as a background task, but I'm not sure where to find bg.js to see what it does, and if I actually need to worry about it.
  3. I can see that setEnabled has some extensionID and values in it, which is what I think I should carry the chrome:- URL, but I'm not too clear on why its being passed as a string to setEnabled. I could also use some help in understanding how to log these values to see if chrome:- URL is actually getting passed there when I do eventually write my logic.
    const bgPage = `(function bgPage() {
    async function getAllDevExtensions() {
    const allExtensions = await new Promise(
    r => chrome.management.getAll(r));
    return allExtensions.filter((extension) => {
    return extension.installType === "development" &&
    extension.id !== chrome.runtime.id;
    });
    }
    const setEnabled = (extensionId, value) =>
    chrome.runtime.id == extensionId ?
    new Promise.resolve() :
    new Promise(r => chrome.management.setEnabled(extensionId, value, r));
    async function reloadExtension(extensionId) {
    await setEnabled(extensionId, false);
    await setEnabled(extensionId, true);
    }
  4. Can you also please tell me how I can achieve Step-1 in my above approach?
    Thank you.

@Rob--W
Copy link
Member

Rob--W commented Sep 23, 2020

@Rob--W

How I thought I'll approach this was:

  1. See what gets called when I run google-chrome http://developer.mozilla.org from command line.

This step is incorrect. I think that the misunderstanding is that you think that you need to change the behavior of how google-chrome [url] is handled. That is not the case; how Chrome handles command-line flags is not something that we can control.

google-chrome http://developer.mozilla.org works, and is not a surprise.
google-chrome chrome://extensions does not, however. That's why we want to filter that URL, and open it via the helper extension (in bg.js) instead. So the part to change is what parameters are passed to the Chrome binary (i.e. without chrome://extensions) and forward the URL in a different way instead (see below).

  1. bg.js is run as a background task, but I'm not sure where to find bg.js to see what it does, and if I actually need to worry about it.

bg.js is written to a temporary directory that is loaded as a background page of the helper extension. That directory is loaded as an extension via the --load-extension flag for Chrome.

  1. I can see that setEnabled has some extensionID and values in it, which is what I think I should carry the chrome:- URL, but I'm not too clear on why its being passed as a string to setEnabled. I could also use some help in understanding how to log these values to see if chrome:- URL is actually getting passed there when I do eventually write my logic.

setEnabled is not the interesting part. Rather, the part that handles the communication between the web-ext application and the helper extension is. A WebSocket is used for communication, and the message is received and handled at the ws.onmessage listener in bg.js. I suggest to look at how the (only) existing message is sent (you can search/grep for the message type in the code base) to try and understand how the communication happens.

@caitmuenster
Copy link

Hey @DeepikaKaranji, how's it going with this issue?

@DeepikaKaranji
Copy link

Hi @caitmuenster !
I already spoke to @Rob--W and told him that have some exams going on right now. I will resume work on 20th October. He said its alright, so I hope its okay.

@caitmuenster
Copy link

No worries at all, @DeepikaKaranji! :) Good luck on your exams!

@ariain
Copy link

ariain commented Oct 13, 2020

@Rob--W @caitmuenster can I work on this issue, I have a rough idea about the solution now after reading the above conversation...

@DeepikaKaranji
Copy link

Hi @ariain , I have already started working on this, will make the PR soon. I hope that is okay. Thanks for understanding :)

@caitmuenster
Copy link

Hi @ariain, there are some other good-first-bugs in our add-ons repositories if you'd like to work on one of those. It looks like this issue on the code manager repo is currently open, as is this issue in the webextension-polyfill repo.

@ariain
Copy link

ariain commented Oct 13, 2020

@DeepikaKaranji I totally understand... @caitmuenster I would like to work on them, thanks for suggesting...

@DeepikaKaranji
Copy link

@Rob--W
I wrote a small test in test-chromium.js (which I know isn't entirely right), so as to pass the "chrome://" urls and catch them.

it('does open chrome:// urls', async () => {
   const {params} = prepareExtensionRunnerParams({
      params: {startUrl: 'chrome://extensions'},
    });
    const runnerInstance = new ChromiumExtensionRunner(params);
    await runnerInstance.run();

    const {reloadManagerExtension} = runnerInstance;

    sinon.assert.calledOnce(params.chromiumLaunch);
    sinon.assert.calledWithMatch(params.chromiumLaunch, {
      ignoreDefaultFlags: true,
      enableExtensions: true,
      chromePath: undefined,
      chromeFlags: [
        ...DEFAULT_CHROME_FLAGS,
        '--load-extension=${reloadManagerExtension},/fake/sourceDir',
      ],
      startingUrl: 'chrome://extensions',
    });
    await runnerInstance.exit();
  });

I added the below snippet at line 221 in chromium.js:

if (startingUrl.startsWith('chrome://')) {
        console.log('*********** CAUGHT ************');
        console.log(this.params.extensions);

        this.reloadManagerExtension = await this.createReloadManagerExtension();
        // Start chrome pointing it to a given profile dir
        const PseudoExtension = [this.reloadManagerExtension].concat(
          this.params.extensions.map(({sourceDir}) => sourceDir)
        ).join(',');
        console.log(PseudoExtension);
      }
      chromeFlags.push(...startingUrls);
    }

I called it PseudoExtension because from you have mentioned that Extensions are able launch "chrome://" URLs.
I also think I should remove chromeFlags.push(), because my assumption is that any normal url is being added to the chromeFlags string array - to be executed by command line.

But in this case, I need to prevent that from happening, and pass the "chrome://" url to the createReloadManagerExtension helper extension.

The test however does not work, but I just wanted to check if I'm on the right track. Thank you!

@Rob--W
Copy link
Member

Rob--W commented Oct 20, 2020

The test expectation is incorrect. The chrome://extensions URL shouldn't be on the command line at all (because Chrome's command line does not support the chrome:-URL, which is what this bug is about).

I don't know what you're trying to do with the PseudoExtension. There is already an extension (the reload manager). You don't have to load a new extension or anything like that, just send a message to the existing extension.

@DeepikaKaranji
Copy link

Alright, I'll do that, thanks.

@caitmuenster
Copy link

Hey @DeepikaKaranji, just wanted to check in with you about this issue. Are you still interested in working on it?

@caitmuenster
Copy link

Hey @DeepikaKaranji, we are going to open this up to other contributors since we haven't heard from you in awhile. If you'd like to finish it off, please feel free to come back to it! O

@CatWithNineLives
Copy link

Hello @caitmuenster, I was looking for good-first-issues in the add-ons repositories and this one seemed ideal - may I work on it?

@caitmuenster
Copy link

Go for it, @CatWithNineLives! If you need any help, please tag [@]Rob--W in a comment.

@caitmuenster
Copy link

Hey @CatWithNineLives, we haven't heard from you in awhile so we're going to open this up to other contributors. If you want to work on it, please feel free to submit a PR. :)

@kodergeek
Copy link

kodergeek commented Sep 16, 2021

Hi @caitmuenster, I saw this issue a few days ago and realized that it's open for contribution. I would like to work on this bug and hopefully fix it if I may? :)

@kodergeek
Copy link

Hey @Rob--W. I have looked a bit in the code and based on your comments above, I believe I have an understanding how to fix this bug.

I have made some changes at chromium.js to detect presence of chrome://extensins in the url array list, and have added necessary code for Websocket communications also at chromium.js and chromium.js

My issue at the moment is that I can't seem to find the appropriate location in code to invoke the new websocket communication openTabWithExtensionsUrl() method which is supposed to communicates with bg.js, and get it to work. I invoke openTabWithExtensionsUrl() in chromium.js method setupInstance() after line this.chromiumInstance = await this.chromiumLaunch is run, which supposedly would launch chrome. But this has no effect, and when I look closely, at that stage wssBroadcast() isn't able to send messages to bg.js, because number of wss clients is zero. (code execution ends up in if (clients.size === 0) )

I tried to confirm this by testing what happens when I swap the current websocket message with the new one I created when R is pressed, so that whenever you press R to reload it sends the new message instead of webExtReloadAllExtensions, and then I can see that a new tab is being opened and loads chrome's extension page.

I'm not sure if there is some extra action taking place outside of chromium.js before which chrome is fully loaded, and asynchronous JavaScript isn't my strong side, so I'm a bit confused in the multitude of asyncs and promises used in code, so I'd appreciate if you could give me some tips. My current commit is available at https://github.com/kodergeek/web-ext/blob/bug-1979/src/extension-runners/chromium.js

@Rob--W
Copy link
Member

Rob--W commented Sep 22, 2021

You could wait for the first WebSocket connection and post the initialization message to open the extension tab.

Feel free to open a pull request for work-in-progress code; then it is easier to provide feedback at specific lines.

@kodergeek
Copy link

kodergeek commented Sep 22, 2021

Thanks for your feedback. I have created a pull request so you can point to specific lines easily.

#2321

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

No branches or pull requests

8 participants