-
Notifications
You must be signed in to change notification settings - Fork 339
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
fix: issue 1909: call chromiumLaunch with parameter userDataDir instead of --user-data-dir and set of --profile-directory #1920
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment below, and please undo the unrelated formatting changes from the patch.
src/extension-runners/chromium.js
Outdated
chromeFlags.push(`--user-data-dir=${this.params.chromiumProfile}`); | ||
const pathParts = path.parse(this.params.chromiumProfile); | ||
const profileDir = pathParts.base; | ||
chromeFlags.push(`--profile-directory=${profileDir}`); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This does not implement what I described at #1909 (comment)
According to your comment at #1909 (comment), the wrong --user-data-dir
is being used. To fix that, the directory should be passed via userDataDir
instead of chromeFlags
.
The --profile-directory
flag is not necessary. Anyone who cares can use the method that I described in #1909 (comment)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes exactly! I changed it this way to have it fully compatible with --firefox-profile, so that the user provides the full path to the profile, and not the folder containing the profiles. This way the user also doesn't have to deal with the undocumented --profile-directory flag. The documentation of --chromium-profile ("Path to a custom Chromium profile") remains correct this way.
If the default --user-data-dir is correct, it is also possible to provide only the name of the profile, like for Firefox.
Do you have concerns?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To simplify @Rob--W , --chromium-profile = --user-data-dir + --profile-directory
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the most common scenarios (using a temporary directory, or even a copy of a single-user profile), just passing --user-data-dir
is sufficient. I don't want to complicate web-ext
with --profile-directory
, because this is a special case AND already possible without special support from web-ext
(i.e. the --arg
flag).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okay for me @Rob--W, but it will mean:
- change the documentation of
--chromium-profile
from "Path to a custom Chromium profile." to "Path to the directory containing the profiles" (or equivalent). Can you organize this change? - loose the compatibility with
--firefox-profile
I don't agree with it, but if you insist I do the change since I would like to have it finally somehow working.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are two bugs here:
- According to your comment at Unable to use custom chromium profile.
--chromium-profile
flag is not getting respected. #1909 (comment) , the--chromium-profile
flag fails to match the specified directory on Windows (even though it works fine on Linux). This is a bug that we should certainly fix. - Now in this PR and thread, you are advocating for support of a specific "User Account" within a given user data directory, via the
--profile-directory
flag. This is more work than needed to fix the previous bug, and I didn't feel the need to delay the fix of the previous bug for this new feature.
I'm not adamant against supporting User Accounts, but note that it will take some effort. I'll post a separate comment with lots of more details, I hope that it helps.
Since you appear to be very interested in supporting Chrome's "Profiles" other than
(edited to use numbers instead of bullet points) |
What should I do? |
Lines 404 to 447 in f2043c7
In order to support this for Chrome, there should also be something similar.
I mentioned this not only because of potential compat concerns, but also to point out that the odds of having proper support for this feature increases if the official upstream project properly supports the feature.
The default behavior should be to treat the flag as I'll have to discuss this with other project members, but I currently believe that the path forwards is 7 and/or awaiting official upstream support (in chrome-launcher). |
I've discussed this with @rpl and we have agreed that the following approach will provide the requested functionality with reasonable compatibility:
Note that this approach favors the use of To detect whether a directory looks like a user-data-dir: Check if the "Local State" file and "Default" directory exist. |
d53b0db
to
9a5e329
Compare
…ad of --user-data-dir
…recognize if it is meant as --user-data-dir or --user-data-dir/--profile-directory
9a5e329
to
9315b78
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One big function with multiple nested if-else blocks is not very readable. To increase the readability and maintainability, I suggest to move the new logic to a separate method. That avoids the need for bloating the setupInstance
function, and makes it easier to specifically create unit tests without too much boilerplate.
Specifically, untangle the current logic and split it in three parts:
- Find out user-data-dir and profile-directory
- Copy directory locations if needed.
- Pass userDataDir / chromeFlags if needed (this is a relatively small part that does belong to
setupInstance
).
This makes the code easier to follow, and also easier to test.
On the tests: There are three tests, for the following case:
- path is a profile-directory, with keepProfileChanges
- path is a profile-directory, but parent is not a user-data-dir, with keepProfileChanges - exit out of caution.
- path is a profile-directory, parent is not a user-data-dir, without keepProfileChanges.
This only covers some common/desired situations, but is not complete, as there are several conditions that are not covered. Since we are working with user data, I want to make sure that all relevant states are fully covered by unit tests (we don't want to accidentally damage real user profiles). I expect that tests will be smaller and easier to write after splitting the logic in three parts as I suggested.
src/extension-runners/chromium.js
Outdated
default as ChromeLauncher, | ||
launch as defaultChromiumLaunch, | ||
LaunchedChrome, Launcher, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will need to change due to the chromium-launcher update from #1931.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reverted this import
src/extension-runners/chromium.js
Outdated
const localStateCandidate = path.join(dirCandidate, 'Local State'); | ||
const defaultCandidate = path.join(dirCandidate, 'Default'); | ||
// Local State and Default are typical for the user-data-dir | ||
return fs.pathExistsSync(localStateCandidate) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since we're trying hard to minimize the risk of misidentification, let's verify that the given paths are really directories / files. We have util.isDirectory
and util.isFile
for that: https://github.com/mozilla/web-ext/blob/588550008c1d0381736f435956095255200707d5/src/util/is-directory.js
For files we also have file.fileExists
- https://github.com/mozilla/web-ext/blob/588550008c1d0381736f435956095255200707d5/src/util/file-exists.js
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I switched to isDirectory. The use of fs-extra is now reduced, and we could reevaluate if it is still justified.
src/extension-runners/chromium.js
Outdated
&& !isUserDataDir(chromiumProfile); | ||
|
||
if (isProfileDirAndNotUserData) { | ||
const pathParts = path.parse(chromiumProfile); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use the following:
const { dir: parentDir, base: profileName } = path.parse(chromiumProfile);
The parentDir
variable makes it very obvious that we're working with parent directories, which removes the need for the // now check if the parent dir is a user-data-dir
comment below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
like it
src/extension-runners/chromium.js
Outdated
// 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. | ||
userDataDir = Launcher.prototype.makeTmpDir(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't use undocumented internal implementation details from an external library.
The web-ext
project itself already has an utility to create temporary directories here: https://github.com/mozilla/web-ext/blob/9315b781ec73eead946c07551a24a413ddb1338a/src/util/temp-dir.js
An example of usage is here: https://github.com/mozilla/web-ext/tree/9315b781ec73eead946c07551a24a413ddb1338a/src/extension-runners
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I switched to TempDir, but with TempDir you cannot just get a temp path without creating the directory itself. I have to delete it and rewrite it after, which is quite ugly. My method would like to just get a path, and create the actual dir separately...
src/extension-runners/chromium.js
Outdated
} | ||
chromeFlags.push(`--profile-directory='${profileDirName}'`); | ||
} else { | ||
userDataDir = chromiumProfile; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prepend comment to explain what chromiumProfile
means at this point, so that the reader doesn't have to scroll all the way up.
And this doesn't properly handle the --keep-profile-changes
case - see my top-level review comment for suggestions on improving this.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I made an early return statement for it, so it should be clear now
@@ -65,6 +65,7 @@ | |||
"es6-error": "4.1.1", | |||
"event-to-promise": "0.8.0", | |||
"firefox-profile": "1.3.1", | |||
"fs-extra": "9.0.1", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not keen on adding new dependencies because it generally results in more maintenance load (e.g. having to publish new versions when a security issue is found in the dependency). But since we're already indirectly depending on this module, I'm fine with it.
In a follow-up issue/PR we can probably replace mz
with this, since mz
's functionality overlaps with fs-extra
.
…recognize if it is meant as --user-data-dir or --user-data-dir/--profile-directory
I think my 3 tests test all the pathes. Do you see another test case? |
I have an issue with the error test. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think my 3 tests test all the pathes. Do you see another test case?
Yes, lots of cases are missing. The purpose of splitting getProfilePaths
(renamed from getProfileParts
- see below) is to make it easier to unit test every possible case. There are bugs in the implementation, and before fixing the bugs that I pointed out, I suggest that you first write some unit tests with expected behavior, and then run the tests with your current (buggy) implementation. Those tests are then expected to fail. After fixing the bugs, the tests are expected to pass.
Think of all possible ways that getProfilePaths
can fail, and for each scenario that you can come up with, create a temporary directory with that filesystem layout. Think of missing files/directories, files being directories, directories being files, a file/directory being a symlink to a file/directory, and generally anything that can cause confusion on whether the given path refers to a user-data-dir or a profile-directory.
With thorough unit tests for getProfilePaths
, the number of tests with an instance of ChromiumExtensionRunner
is reasonably less. The interesting cases are combinations of: userDataDir exists/does not exist, profileDir exists/does not, with and without --keep-profile-changes
.
(the next part is for completeness, but please focus on the previous parts before concerning yourself with the next part.)
Finally, after having those unit tests, I also recommend to actually run web-ext run
with special characters in the profile directory name, and see if Chrome starts as expected. Think of double quotes and single quotes (apostrophes) (at the start, in the middle and at the end of the directory name). These characters have special meanings in the shell, so make sure to properly escape them, or use webExt
as a library and pass the variables directly as the chromiumProfile
parameter. If you find that there is a bug (e.g. the path name is incorrectly interpreted), then fix it and add a unit test.
I have an issue with the error test.
npm run test
is passing on my computer but failing on the CI pipeline.
The tests themselves pass on CI, but the code coverage check fails because there is a regression in code coverage. When I open the coverage file, I can clearly see an uncovered condition, which should really have been covered by a unit test: https://coveralls.io/builds/31520533/source?filename=src%2Fextension-runners%2Fchromium.js#L129
Sometimes the coveralls report is incorrect, but in this case it did correctly point to missing coverage (and incidentally, an actual bug that would have been revealed if there was test coverage).
src/extension-runners/chromium.js
Outdated
const localStateCandidate = path.join(dirCandidate, 'Local State'); | ||
const defaultCandidate = path.join(dirCandidate, 'Default'); | ||
// Local State and Default are typical for the user-data-dir | ||
return await isDirectory(localStateCandidate) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"Local State" is a file, not a directory.
And replace the "Candidate" suffix with "Path", everywhere.
src/extension-runners/chromium.js
Outdated
const securePreferencesCandidate = path.join( | ||
dirCandidate, | ||
'Secure Preferences'); | ||
//Secure Preferences is typical for a profile dir |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"profile dir" -> "profile dir inside a user data dir."
src/extension-runners/chromium.js
Outdated
} | ||
|
||
static async getProfileParts(params: any): Promise<{ | ||
userDataDir: (string | false), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Make it a ?string
(= string or null
/ undefined
) instead of a union type composed of string + false.
src/extension-runners/chromium.js
Outdated
return await isDirectory(securePreferencesCandidate); | ||
} | ||
|
||
static async getProfileParts(params: any): Promise<{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Rename to getProfilePaths(chromiumProfile: ?string)
params: any
is too broad, see below.
src/extension-runners/chromium.js
Outdated
// the changes to be kept. we copy this directory to a temporary | ||
// user data dir. | ||
const tmpDir = new TempDir(); | ||
await tmpDir.create(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's not the responsibility of this function to create a temporary non-existing directory. getProfilePaths
should only parse the path and return its best guess. Callers can then use this to copy the directories if needed.
src/extension-runners/chromium.js
Outdated
@@ -127,8 +193,21 @@ export class ChromiumExtensionRunner { | |||
chromeFlags.push(...this.params.args); | |||
} | |||
|
|||
if (this.params.chromiumProfile) { | |||
chromeFlags.push(`--user-data-dir=${this.params.chromiumProfile}`); | |||
const profileParts = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of profileParts
, expand the variables as follows (note with let
instead of const
because we may update the value - see further).
let { userDataDir, profileDirName } = ...
and then check keepProfileChanges
and copy / change the path if needed:
If keepProfileChanges
is falsey:
- Create a new temp dir.
- If
profileDirName
is not set, copyuserDataDir
's content to that temp dir. - If
profileDirName
is set, copy profileDirName (as a directory, not just its content) to that temp dir. - Update
userDataDir
to that temp dir location.
If keepProfileChanges
is truthy:
- If
profileDirName
is set, butuserDataDir
is not a user data dir: Report an error. (basically move part of the logic that you've currently put ingetProfileParts
here)
src/extension-runners/chromium.js
Outdated
await ChromiumExtensionRunner.getProfileParts(this.params); | ||
|
||
if (profileParts.userDataDir | ||
&& profileParts.profileDirName |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This shows a bug: If the user only specified --user-data-dir
, then the absence of --keep-profile-changes
is not processed.
src/extension-runners/chromium.js
Outdated
&& !this.params.keepProfileChanges | ||
) { | ||
// copy profile dir to this temp user data dir. | ||
fs.copySync(this.params.chromiumProfile, path.join( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's use await fs.copy
, as there is no real need for this to be synchronous.
The use of fs-extra is now reduced, and we could reevaluate if it is still justified.
There are multiple (dev) dependencies with overlapping functionality. For example, mz
and fs-extra
both offer promisified fs
methods, and fs-extra
and copy-dir
both offer ways to copy. I think that fs-extra
is the best replacement in the long term.
…userDataDir if chromiumProfile is not set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new tests and changes looks good. I only have a few minor comments below.
src/extension-runners/chromium.js
Outdated
); | ||
} else { | ||
await fs.copy(path.join( | ||
userDataDir), path.join( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These path.join
calls aren't needed, just pass them as-is to fs.copy
.
await fs.outputFile(path.join(tmpPath, 'Local State'), ''); | ||
|
||
assert.isTrue(await fileExists(path.join(tmpPath, 'Local State'))); | ||
assert.isTrue(await isDirectory(path.join(tmpPath, 'Default'))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These two assertions are redundant. It is part of the test setup and we assume that the test setup was successful.
' to chrome', async () => withTempDir( | ||
async (tmpDir) => { | ||
const tmpPath = tmpDir.path(); | ||
await fs.mkdirsSync(path.join(tmpPath, 'userDataDir/Default')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
mkdirs
}) | ||
); | ||
|
||
it('does copy the profile and pass user-data-dir and profile-directory' + |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You also have to verify that the relevant directories/files have been copied as expected.
'user-data-dir directory'); | ||
} | ||
} else if (userDataDir) { | ||
// the user provided an existing profile directory but doesn't want |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's unconditionally create a temporary directory, so that we have full control over whether the temp file is deleted (see also my other comment below about #1957).
Currently, due to the way that chrome-launcher is implemented, passing userDataDir: null
(instead of userDataDir: undefined
) causes the temporary profile to be left behind: https://github.com/GoogleChrome/chrome-launcher/blob/08406b2804c6efba7b602fb276397df00cd0041f/src/chrome-launcher.ts#L373
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment hasn't been addressed yet.
To address it, you need to remove if (userDataDir) {
here (to turn this into } else {
) and it to the } else {
below.
} else if (userDataDir) { // <-- Here
await fs.copy(userDataDir, tmpDirPath);
|
||
const tmpPath = tmpDir.path(); | ||
await fs.mkdirs(path.join(tmpPath, 'Default')); | ||
await fs.mkdirs(path.join(tmpPath, 'Local State')); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add comment that this should be a file (for those unfamiliar with how the profile is supposed to look like, which is almost everyone, I guess).
Also for other tests below.
…ad of --user-data-dir
…recognize if it is meant as --user-data-dir or --user-data-dir/--profile-directory
…recognize if it is meant as --user-data-dir or --user-data-dir/--profile-directory
…userDataDir if chromiumProfile is not set
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for all of your changes. Could you make one more change (see below, because that causes chrome-launcher
to leave temp files behind)? After that it should be good to land.
The tests time out for some reason, but I guess that it's unrelated to your patch.
'user-data-dir directory'); | ||
} | ||
} else if (userDataDir) { | ||
// the user provided an existing profile directory but doesn't want |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment hasn't been addressed yet.
To address it, you need to remove if (userDataDir) {
here (to turn this into } else {
) and it to the } else {
below.
} else if (userDataDir) { // <-- Here
await fs.copy(userDataDir, tmpDirPath);
src/extension-runners/chromium.js
Outdated
// the changes to be kept. we copy this directory to a temporary | ||
// user data dir. | ||
const tmpDir = new TempDir(); | ||
// TODO: Remove tmpDir - https://github.com/mozilla/web-ext/issues/1957 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry for the confusion, as Luca mentioned at https://github.com/mozilla/web-ext/pull/1920/files#r454511824 , the tmp
library already removes the file at exit. So this comment can be removed.
I locally checked out the PR to prepare for merging, but noticed that the test failure is actually caused by your changes. Minimal reproduction:
In that test, you're not calling |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's green \o/
We got it! Thank you @Rob--W for accompanying me in my fist open source PR. |
You're welcome, and thanks again for raising attention on issue #1909 and submitting a patch! |
Thanks for the patch, @pearnaly! 🎉 Your contribution has been added to our recognition wiki. Would you be interested in creating an account on mozillians.org? I'd be happy to vouch for you! |
Thank you Caitlin!
i created an account: pearnaly
Have a nice day
Viele Grüße
Paul-Émile Arnaly
Am Mo., 20. Juli 2020 um 22:00 Uhr schrieb Caitlin Neiman <
notifications@github.com>:
… Thanks for the patch, @pearnaly <https://github.com/pearnaly>! 🎉 Your
contribution has been added to our recognition wiki
<https://wiki.mozilla.org/Add-ons/Contribute/Recognition>.
Would you be interested in creating an account on mozillians.org? I'd be
happy to vouch for you!
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#1920 (comment)>, or
unsubscribe
<https://github.com/notifications/unsubscribe-auth/AEDBRHOJAIF4ADC52ITGCHTR4SO4ZANCNFSM4NSV3LLQ>
.
|
Excellent! Your profile has been vouched. 🎉 Welcome onboard! We look forward to seeing you around. |
fix: issue 1909: call chromiumLaunch with parameter userDataDir instead of --user-data-dir and set of --profile-directory
See discussion #1909
Thank you @Rob--W