Skip to content
This repository has been archived by the owner on Oct 30, 2023. It is now read-only.

Commit

Permalink
feat: allow customizing browser data location (electron#33554)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
2 people authored and khalwa committed Feb 22, 2023
1 parent 5808e3c commit 427f1f5
Show file tree
Hide file tree
Showing 12 changed files with 128 additions and 18 deletions.
18 changes: 14 additions & 4 deletions docs/api/app.md
Expand Up @@ -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.
Expand Down Expand Up @@ -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()`

Expand Down
5 changes: 4 additions & 1 deletion shell/app/electron_main_delegate.cc
Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions shell/browser/api/electron_api_app.cc
Expand Up @@ -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")
Expand Down
2 changes: 1 addition & 1 deletion shell/browser/browser_process_impl.cc
Expand Up @@ -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 =
Expand Down
8 changes: 4 additions & 4 deletions shell/browser/electron_browser_client.cc
Expand Up @@ -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() {
Expand Down
3 changes: 1 addition & 2 deletions shell/browser/electron_browser_context.cc
Expand Up @@ -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(
Expand Down
2 changes: 1 addition & 1 deletion shell/browser/electron_browser_main_parts.cc
Expand Up @@ -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)
Expand Down
6 changes: 3 additions & 3 deletions shell/browser/ui/devtools_manager_delegate.cc
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion shell/common/electron_paths.h
Expand Up @@ -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
Expand Down
50 changes: 49 additions & 1 deletion spec-main/api-app-spec.ts
Expand Up @@ -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';
Expand Down Expand Up @@ -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)', () => {
Expand Down
43 changes: 43 additions & 0 deletions spec-main/fixtures/apps/set-path/main.js
@@ -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);
});
});
4 changes: 4 additions & 0 deletions spec-main/fixtures/apps/set-path/package.json
@@ -0,0 +1,4 @@
{
"name": "electron-test-set-path",
"main": "main.js"
}

0 comments on commit 427f1f5

Please sign in to comment.