feat: allow customizing browser data location (#33554)

* feat: redirect Electron/Chromium cache location

* fix: network services should also use browserData

* test: browserData

* chore: no need to explicitly create dir

* feat: browserData => sessionData

* test: check existings of specific items

* docs: add background on userData and sessionData

Co-authored-by: emmanuel.kimmerlin@thomsonreuters.com <emmanuel.kimmerlin@thomsonreuters.com>
This commit is contained in:
Cheng Zhao 2022-05-09 23:26:57 +09:00 committed by GitHub
parent 03e68e2efe
commit 9483e714c4
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 128 additions and 18 deletions

View file

@ -635,8 +635,18 @@ Returns `string` - The current application directory.
* `%APPDATA%` on Windows
* `$XDG_CONFIG_HOME` or `~/.config` on Linux
* `~/Library/Application Support` on macOS
* `userData` The directory for storing your app's configuration files, which by
default it is the `appData` directory appended with your app's name.
* `userData` The directory for storing your app's configuration files, which
by default is the `appData` directory appended with your app's name. By
convention files storing user data should be written to this directory, and
it is not recommended to write large files here because some environments
may backup this directory to cloud storage.
* `sessionData` The directory for storing data generated by `Session`, such
as localStorage, cookies, disk cache, downloaded dictionaries, network
state, devtools files. By default this points to `userData`. Chromium may
write very large disk cache here, so if your app does not rely on browser
storage like localStorage or cookies to save user data, it is recommended
to set this directory to other locations to avoid polluting the `userData`
directory.
* `temp` Temporary directory.
* `exe` The current executable file.
* `module` The `libchromiumcontent` library.
@ -686,9 +696,9 @@ In that case, the directory should be created with `fs.mkdirSync` or similar.
You can only override paths of a `name` defined in `app.getPath`.
By default, web pages' cookies and caches will be stored under the `userData`
By default, web pages' cookies and caches will be stored under the `sessionData`
directory. If you want to change this location, you have to override the
`userData` path before the `ready` event of the `app` module is emitted.
`sessionData` path before the `ready` event of the `app` module is emitted.
### `app.getVersion()`

View file

@ -134,11 +134,14 @@ bool ElectronPathProvider(int key, base::FilePath* result) {
break;
case chrome::DIR_APP_DICTIONARIES:
// TODO(nornagon): can we just default to using Chrome's logic here?
if (!base::PathService::Get(chrome::DIR_USER_DATA, &cur))
if (!base::PathService::Get(DIR_SESSION_DATA, &cur))
return false;
cur = cur.Append(base::FilePath::FromUTF8Unsafe("Dictionaries"));
create_dir = true;
break;
case DIR_SESSION_DATA:
// By default and for backward, equivalent to DIR_USER_DATA.
return base::PathService::Get(chrome::DIR_USER_DATA, result);
case DIR_USER_CACHE: {
#if BUILDFLAG(IS_POSIX)
int parent_key = base::DIR_CACHE;

View file

@ -473,6 +473,8 @@ IconLoader::IconSize GetIconSizeByString(const std::string& size) {
int GetPathConstant(const std::string& name) {
if (name == "appData")
return DIR_APP_DATA;
else if (name == "sessionData")
return DIR_SESSION_DATA;
else if (name == "userData")
return chrome::DIR_USER_DATA;
else if (name == "cache")

View file

@ -110,7 +110,7 @@ void BrowserProcessImpl::PostEarlyInitialization() {
// Only use a persistent prefs store when cookie encryption is enabled as that
// is the only key that needs it
base::FilePath prefs_path;
CHECK(base::PathService::Get(chrome::DIR_USER_DATA, &prefs_path));
CHECK(base::PathService::Get(electron::DIR_SESSION_DATA, &prefs_path));
prefs_path = prefs_path.Append(FILE_PATH_LITERAL("Local State"));
base::ThreadRestrictions::ScopedAllowIO allow_io;
scoped_refptr<JsonPrefStore> user_pref_store =

View file

@ -1139,11 +1139,11 @@ void ElectronBrowserClient::OnNetworkServiceCreated(
std::vector<base::FilePath>
ElectronBrowserClient::GetNetworkContextsParentDirectory() {
base::FilePath user_data_dir;
base::PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
DCHECK(!user_data_dir.empty());
base::FilePath session_data;
base::PathService::Get(DIR_SESSION_DATA, &session_data);
DCHECK(!session_data.empty());
return {user_data_dir};
return {session_data};
}
std::string ElectronBrowserClient::GetProduct() {

View file

@ -120,8 +120,7 @@ ElectronBrowserContext::ElectronBrowserContext(const std::string& partition,
base::StringToInt(command_line->GetSwitchValueASCII(switches::kDiskCacheSize),
&max_cache_size_);
CHECK(base::PathService::Get(chrome::DIR_USER_DATA, &path_));
base::PathService::Get(DIR_SESSION_DATA, &path_);
if (!in_memory && !partition.empty())
path_ = path_.Append(FILE_PATH_LITERAL("Partitions"))
.Append(base::FilePath::FromUTF8Unsafe(

View file

@ -501,7 +501,7 @@ void ElectronBrowserMainParts::PostCreateMainMessageLoop() {
// https://source.chromium.org/chromium/chromium/src/+/master:chrome/common/chrome_switches.cc;l=689;drc=9d82515060b9b75fa941986f5db7390299669ef1
config->should_use_preference =
command_line.HasSwitch(::switches::kEnableEncryptionSelection);
base::PathService::Get(chrome::DIR_USER_DATA, &config->user_data_path);
base::PathService::Get(DIR_SESSION_DATA, &config->user_data_path);
OSCrypt::SetConfig(std::move(config));
#endif
#if BUILDFLAG(IS_POSIX)

View file

@ -91,10 +91,10 @@ const char kBrowserCloseMethod[] = "Browser.close";
// static
void DevToolsManagerDelegate::StartHttpHandler() {
base::FilePath user_dir;
base::PathService::Get(chrome::DIR_USER_DATA, &user_dir);
base::FilePath session_data;
base::PathService::Get(DIR_SESSION_DATA, &session_data);
content::DevToolsAgentHost::StartRemoteDebuggingServer(
CreateSocketFactory(), user_dir, base::FilePath());
CreateSocketFactory(), session_data, base::FilePath());
}
DevToolsManagerDelegate::DevToolsManagerDelegate() = default;

View file

@ -23,7 +23,8 @@ enum {
PATH_START = 11000,
DIR_USER_CACHE = PATH_START, // Directory where user cache can be written.
DIR_APP_LOGS, // Directory where app logs live
DIR_APP_LOGS, // Directory where app logs live.
DIR_SESSION_DATA, // Where cookies, localStorage are stored.
#if BUILDFLAG(IS_WIN)
DIR_RECENT, // Directory where recent files live

View file

@ -3,7 +3,7 @@ import * as cp from 'child_process';
import * as https from 'https';
import * as http from 'http';
import * as net from 'net';
import * as fs from 'fs';
import * as fs from 'fs-extra';
import * as path from 'path';
import { promisify } from 'util';
import { app, BrowserWindow, Menu, session, net as electronNet } from 'electron/main';
@ -1078,6 +1078,54 @@ describe('app module', () => {
expect(() => { app.getPath(badPath as any); }).to.throw();
});
describe('sessionData', () => {
const appPath = path.join(__dirname, 'fixtures', 'apps', 'set-path');
const appName = fs.readJsonSync(path.join(appPath, 'package.json')).name;
const userDataPath = path.join(app.getPath('appData'), appName);
const tempBrowserDataPath = path.join(app.getPath('temp'), appName);
const sessionFiles = [
'Preferences',
'Code Cache',
'Local Storage',
'IndexedDB',
'Service Worker'
];
const hasSessionFiles = (dir: string) => {
for (const file of sessionFiles) {
if (!fs.existsSync(path.join(dir, file))) {
return false;
}
}
return true;
};
beforeEach(() => {
fs.removeSync(userDataPath);
fs.removeSync(tempBrowserDataPath);
});
it('writes to userData by default', () => {
expect(hasSessionFiles(userDataPath)).to.equal(false);
cp.spawnSync(process.execPath, [appPath]);
expect(hasSessionFiles(userDataPath)).to.equal(true);
});
it('can be changed', () => {
expect(hasSessionFiles(userDataPath)).to.equal(false);
cp.spawnSync(process.execPath, [appPath, 'sessionData', tempBrowserDataPath]);
expect(hasSessionFiles(userDataPath)).to.equal(false);
expect(hasSessionFiles(tempBrowserDataPath)).to.equal(true);
});
it('changing userData affects default sessionData', () => {
expect(hasSessionFiles(userDataPath)).to.equal(false);
cp.spawnSync(process.execPath, [appPath, 'userData', tempBrowserDataPath]);
expect(hasSessionFiles(userDataPath)).to.equal(false);
expect(hasSessionFiles(tempBrowserDataPath)).to.equal(true);
});
});
});
describe('setAppLogsPath(path)', () => {

View file

@ -0,0 +1,43 @@
const http = require('http');
const { app, ipcMain, BrowserWindow } = require('electron');
if (process.argv.length > 3) {
app.setPath(process.argv[2], process.argv[3]);
}
const html = `
<script>
async function main() {
localStorage.setItem('myCat', 'Tom')
const db = indexedDB.open('db-name', 1)
await new Promise(resolve => db.onsuccess = resolve)
await navigator.serviceWorker.register('sw.js', {scope: './'})
}
main().then(() => {
require('electron').ipcRenderer.send('success')
})
</script>
`;
const js = 'console.log("From service worker")';
app.once('ready', () => {
ipcMain.on('success', () => {
app.quit();
});
const server = http.createServer((request, response) => {
if (request.url === '/') {
response.writeHead(200, { 'Content-Type': 'text/html' });
response.end(html);
} else if (request.url === '/sw.js') {
response.writeHead(200, { 'Content-Type': 'text/javascript' });
response.end(js);
}
}).listen(0, '127.0.0.1', () => {
const serverUrl = 'http://127.0.0.1:' + server.address().port;
const mainWindow = new BrowserWindow({ show: false, webPreferences: { webSecurity: true, nodeIntegration: true, contextIsolation: false } });
mainWindow.loadURL(serverUrl);
});
});

View file

@ -0,0 +1,4 @@
{
"name": "electron-test-set-path",
"main": "main.js"
}