Compare commits

...

44 Commits

Author SHA1 Message Date
Electron Bot
a498e0c033 Bump v5.0.0-beta.2 2019-02-03 08:46:03 -08:00
Shelley Vohr
0f66918825 Revert "build: hack around GitHub upload API failure / flake (#16665)"
This reverts commit 233d346d5a.
2019-02-03 08:44:35 -08:00
Shelley Vohr
050b866b6a Revert "Bump v5.0.0-beta.2"
This reverts commit 8115a899df.
2019-02-03 08:44:13 -08:00
Electron Bot
8115a899df Bump v5.0.0-beta.2 2019-02-02 20:52:53 -08:00
Shelley Vohr
3dcb7cfef9 Revert "ci: build mac on CircleCI (#16656)"
This reverts commit 16dfa56c77.
2019-02-02 20:51:24 -08:00
Shelley Vohr
502f24e50d Revert "Bump v5.0.0-beta.2"
This reverts commit 6ebef9d876.
2019-02-02 20:38:58 -08:00
Electron Bot
6ebef9d876 Bump v5.0.0-beta.2 2019-02-02 20:33:36 -08:00
Shelley Vohr
60af6c2791 feat: promisify cookies api (#16702)
* feat: promisify cookies api (#16464)

* feat: promisify the Cookie API

* chore: update specs to test promisified cookies

* chore: add deprecate wrapper for cookie callback API

* docs: update docs to cookie promise changes

* chore: remove redundant namespace use

* docs: improve cookie example

* docs: restore docs for cookie callback API

* chore: restore cookie callback tests

* fix: syntax of cookie promise return types

* fix: use new promisify helper
2019-02-02 18:55:41 -08:00
Shelley Vohr
6d76da55e2 feat: promisify contentTracing.getCategories() (#16583) (#16624)
* feat: promisify contentTracing.getCategories()

* deprecate contentTracing/getCategories
2019-02-01 17:46:09 -08:00
Shelley Vohr
b31057ca16 feat: promisify contentTracing recording APIs (#16584) (#16642)
* feat: promisify contentTracing.startRecording()

* feat: promisify contentTracing.stopRecording()

* test: convert specs for new promisified apis

* chore: deprecate and ensure legacy tests work
2019-02-01 12:31:33 -08:00
John Kleinschmidt
16dfa56c77 ci: build mac on CircleCI (#16656) 2019-02-01 14:19:35 -05:00
Shelley Vohr
a7ed504575 fix: deprecate promise functions properly (#16643) 2019-02-01 10:37:25 -08:00
trop[bot]
dc4c32972d Update menu.md (#16677) 2019-02-01 09:40:01 -08:00
trop[bot]
d1d0efbd07 fix: shutdown after message loop is ready (#16672) 2019-02-01 09:39:46 -08:00
trop[bot]
afa684ad45 docs: fix referrer typedef in OnCompletedDetails (#16675) 2019-02-01 09:39:15 -08:00
trop[bot]
f66d21e426 fix: show proper clerk notes in release notes script (backport: 5-0-x) (#16679)
* fix: Note detection in PR

* fix: 'BREAKING CHANGE' detection in PR body

* fix: when to include PRs that landed in other branches too

* fix: when available, use clerk's notes
2019-02-01 08:34:33 -08:00
trop[bot]
9a68ce87eb feat: add ELECTRON_DISABLE_SANDBOX env var (#16662) 2019-02-01 08:33:11 -08:00
Electron Bot
ecb737760c Revert "Bump v5.0.0-beta.2"
This reverts commit 5d32cd0269.
2019-02-01 08:02:29 -08:00
Electron Bot
5d32cd0269 Bump v5.0.0-beta.2 2019-02-01 07:52:08 -08:00
Shelley Vohr
499efd5ee7 Revert "Bump v5.0.0-beta.3"
This reverts commit a879981dfb.
2019-02-01 07:49:42 -08:00
Shelley Vohr
c4115ed783 Revert "Bump v5.0.0-beta.2"
This reverts commit 5be0851434.
2019-02-01 07:49:24 -08:00
trop[bot]
969a97b54f ci: Run Windows Electron tests first to show those failures first (backport: 5-0-x) (#16655)
* Run electron tests first to show those failures first

Enable logging on CI

* disable failing tests on Windows 32 bit

* Temporarily disable testing mksnapshot as that seems to hang
2019-01-31 19:23:37 -05:00
Electron Bot
5be0851434 Bump v5.0.0-beta.2 2019-01-31 15:51:42 -08:00
trop[bot]
233d346d5a build: hack around GitHub upload API failure / flake (#16665) 2019-01-31 15:50:16 -08:00
Electron Bot
a879981dfb Bump v5.0.0-beta.3 2019-01-31 14:08:49 -08:00
Electron Bot
3d90bd4e8e Revert "Bump v5.0.0-beta.2"
This reverts commit ba1dd09be2.
2019-01-31 12:14:07 -08:00
Electron Bot
ba1dd09be2 Bump v5.0.0-beta.2 2019-01-31 10:31:46 -08:00
trop[bot]
80aa832ebd chore: fix 'browserView' typo (#16644) 2019-01-31 12:13:01 -06:00
trop[bot]
a950e1d040 chore: Fix typo in AtomDownloadManagerDelegate::OnDownloadSaveDialogDone (#16650)
I believe the existing code was fine, but better be safe than sorry.
This typo was introduced in #16612.
2019-01-31 07:48:37 -08:00
trop[bot]
4d7ddcd750 fix: use async save dialog for anchor download attribute (#16640) 2019-01-31 16:16:34 +09:00
Nitish Sakhawalkar
d6612d230b chore: Move webFrame scheme privilege methods to main process (#16625)
* feat: move webFrame scheme privilege methods to main process (#16416)

* chore: deprecate webFrame.registerURLSchemeAsPrivileged

* Add register schemes protocol api

* update branch to enable browser process API

* Revert deprecation changes

* Fetch API support

* Updated api to take an array, still working on tests

* Update tests

* Remove web frame API

* Minor changes

* update scheme registrations on browser and renderer process

* fix: enable ses.getBlobData spec

* Update breaking changes doc

* fix: update docs for protocol API (#16601)

* fix: update docs for protocol API

* upddate source for new attribute name

* update electron-typescript-definitions package
2019-01-30 14:57:20 -08:00
trop[bot]
1d9abfdb10 fix: expose aes-cfb ciphers from boringssl (#16618)
Ref #16195
2019-01-30 14:17:07 -08:00
trop[bot]
871ba507a6 fix: enable and update osr (backport: 5-0-x) (#16616)
* fix: enable OSR

* fix some macos errors

* fix client reset not in guard

* fix things not rendering on mac
2019-01-30 12:18:06 -08:00
trop[bot]
5d64df141b fix: don't forward IPC filtering events to app for dev-tools and extensions (#16613) 2019-01-30 09:58:05 -08:00
trop[bot]
ae846204cb docs: remove nonexistent contentTracing methods (#16614) 2019-01-30 08:51:59 -08:00
trop[bot]
6dcf5c5c79 fix: execute session preload scripts in sandboxed renderers (#16578) 2019-01-30 08:23:48 -08:00
trop[bot]
e55a9b35b6 fix: check process.isMainFrame in sandboxed_renderer/init.js (#16533) 2019-01-29 21:10:53 -08:00
Nitish Sakhawalkar
ae85864959 fix: correctly destroy spellcheck client (#16525)
* fix: Destroy spellcheck client

* Address review comments
2019-01-29 15:59:57 -08:00
trop[bot]
5cc0539919 fix: expose ripemd160 hash from boringssl (#16572)
Ref #16195
2019-01-29 15:58:24 -08:00
trop[bot]
b070774f5c fix: registerStreamProtocol callback with large chunks (backport: 5-0-x) (#16553) 2019-01-28 14:07:14 -08:00
trop[bot]
7a8548d48f docs: cancel is optional in OnHeadersReceivedResponse (#16549) 2019-01-25 09:48:50 -08:00
Electron Bot
ac172abda7 Bump v5.0.0-beta.1 2019-01-22 14:11:16 -08:00
Samuel Attard
29e5195c63 build: fix version bump script for first beta 2019-01-22 14:09:10 -08:00
Samuel Attard
3926d9d717 chore: reset npm version to 5.0.0-beta.0 2019-01-22 13:32:32 -08:00
73 changed files with 1592 additions and 908 deletions

View File

@@ -1 +1 @@
5.0.0-nightly.20190122
5.0.0-beta.2

View File

@@ -81,8 +81,6 @@ test_script:
if ((-Not (Test-Path Env:\ELECTRON_RELEASE)) -And ($env:GN_CONFIG -in "testing", "release")) {
$env:RUN_TESTS="true"
}
- if "%RUN_TESTS%"=="true" ( echo Verifying non proprietary ffmpeg & python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg )
- if "%RUN_TESTS%"=="true" ( echo Verifying mksnapshot & python electron\script\verify-mksnapshot.py --build-dir out\Default --source-root %cd% )
- ps: >-
if ($env:RUN_TESTS -eq 'true') {
New-Item .\out\Default\gen\node_headers\Release -Type directory
@@ -91,8 +89,9 @@ test_script:
echo "Skipping tests for $env:GN_CONFIG build"
}
- cd electron
- if "%RUN_TESTS%"=="true" ( echo Running test suite & npm run test -- --ci )
- if "%RUN_TESTS%"=="true" ( echo Running test suite & npm run test -- --ci --enable-logging)
- cd ..
- if "%RUN_TESTS%"=="true" ( echo Verifying non proprietary ffmpeg & python electron\script\verify-ffmpeg.py --build-dir out\Default --source-root %cd% --ffmpeg-path out\ffmpeg )
deploy_script:
- cd electron
- ps: >-

View File

@@ -213,18 +213,30 @@ base::RefCountedMemory* AtomContentClient::GetDataResourceBytes(
}
void AtomContentClient::AddAdditionalSchemes(Schemes* schemes) {
schemes->standard_schemes.push_back("chrome-extension");
std::vector<std::string> splited;
ConvertStringWithSeparatorToVector(&splited, ",",
switches::kRegisterServiceWorkerSchemes);
switches::kServiceWorkerSchemes);
for (const std::string& scheme : splited)
schemes->service_worker_schemes.push_back(scheme);
schemes->service_worker_schemes.push_back(url::kFileScheme);
ConvertStringWithSeparatorToVector(&splited, ",", switches::kStandardSchemes);
for (const std::string& scheme : splited)
schemes->standard_schemes.push_back(scheme);
schemes->standard_schemes.push_back("chrome-extension");
ConvertStringWithSeparatorToVector(&splited, ",", switches::kSecureSchemes);
for (const std::string& scheme : splited)
schemes->secure_schemes.push_back(scheme);
ConvertStringWithSeparatorToVector(&splited, ",",
switches::kBypassCSPSchemes);
for (const std::string& scheme : splited)
schemes->csp_bypassing_schemes.push_back(scheme);
ConvertStringWithSeparatorToVector(&splited, ",", switches::kCORSSchemes);
for (const std::string& scheme : splited)
schemes->cors_enabled_schemes.push_back(scheme);
}
void AtomContentClient::AddPepperPlugins(

View File

@@ -164,6 +164,9 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) {
if (env->HasVar("ELECTRON_ENABLE_STACK_DUMPING"))
base::debug::EnableInProcessStackDumping();
if (env->HasVar("ELECTRON_DISABLE_SANDBOX"))
command_line->AppendSwitch(service_manager::switches::kNoSandbox);
chrome::RegisterPathProvider();
#if defined(OS_MACOSX)

View File

@@ -8,6 +8,7 @@
#include "atom/common/native_mate_converters/callback.h"
#include "atom/common/native_mate_converters/file_path_converter.h"
#include "atom/common/native_mate_converters/value_converter.h"
#include "atom/common/promise_util.h"
#include "base/bind.h"
#include "base/files/file_util.h"
#include "content/public/browser/tracing_controller.h"
@@ -65,23 +66,53 @@ scoped_refptr<TracingController::TraceDataEndpoint> GetTraceDataEndpoint(
result_file_path, base::Bind(callback, result_file_path));
}
void StopRecording(const base::FilePath& path,
const CompletionCallback& callback) {
void OnRecordingStopped(scoped_refptr<atom::util::Promise> promise,
const base::FilePath& path) {
promise->Resolve(path);
}
v8::Local<v8::Promise> StopRecording(v8::Isolate* isolate,
const base::FilePath& path) {
scoped_refptr<atom::util::Promise> promise = new atom::util::Promise(isolate);
TracingController::GetInstance()->StopTracing(
GetTraceDataEndpoint(path, callback));
GetTraceDataEndpoint(path, base::Bind(&OnRecordingStopped, promise)));
return promise->GetHandle();
}
bool GetCategories(
const base::RepeatingCallback<void(const std::set<std::string>&)>&
callback) {
return TracingController::GetInstance()->GetCategories(
base::BindOnce(callback));
void OnCategoriesAvailable(scoped_refptr<atom::util::Promise> promise,
const std::set<std::string>& categories) {
promise->Resolve(categories);
}
bool StartTracing(const base::trace_event::TraceConfig& trace_config,
const base::RepeatingCallback<void()>& callback) {
return TracingController::GetInstance()->StartTracing(
trace_config, base::BindOnce(callback));
v8::Local<v8::Promise> GetCategories(v8::Isolate* isolate) {
scoped_refptr<atom::util::Promise> promise = new atom::util::Promise(isolate);
bool success = TracingController::GetInstance()->GetCategories(
base::BindOnce(&OnCategoriesAvailable, promise));
if (!success)
promise->RejectWithErrorMessage("Could not get categories.");
return promise->GetHandle();
}
void OnTracingStarted(scoped_refptr<atom::util::Promise> promise) {
promise->Resolve();
}
v8::Local<v8::Promise> StartTracing(
v8::Isolate* isolate,
const base::trace_event::TraceConfig& trace_config) {
scoped_refptr<atom::util::Promise> promise = new atom::util::Promise(isolate);
bool success = TracingController::GetInstance()->StartTracing(
trace_config, base::BindOnce(&OnTracingStarted, promise));
if (!success)
promise->RejectWithErrorMessage("Could not start tracing");
return promise->GetHandle();
}
bool GetTraceBufferUsage(

View File

@@ -136,6 +136,21 @@ inline net::CookieStore* GetCookieStore(
return getter->GetURLRequestContext()->cookie_store();
}
void ResolvePromiseWithCookies(scoped_refptr<util::Promise> promise,
net::CookieList cookieList) {
promise->Resolve(cookieList);
}
void ResolvePromise(scoped_refptr<util::Promise> promise) {
promise->Resolve();
}
// Resolve |promise| in UI thread.
void ResolvePromiseInUI(scoped_refptr<util::Promise> promise) {
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI},
base::BindOnce(ResolvePromise, std::move(promise)));
}
// Run |callback| on UI thread.
void RunCallbackInUI(const base::Closure& callback) {
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::UI}, callback);
@@ -143,25 +158,28 @@ void RunCallbackInUI(const base::Closure& callback) {
// Remove cookies from |list| not matching |filter|, and pass it to |callback|.
void FilterCookies(std::unique_ptr<base::DictionaryValue> filter,
const Cookies::GetCallback& callback,
scoped_refptr<util::Promise> promise,
const net::CookieList& list) {
net::CookieList result;
for (const auto& cookie : list) {
if (MatchesCookie(filter.get(), cookie))
result.push_back(cookie);
}
RunCallbackInUI(base::Bind(callback, Cookies::SUCCESS, result));
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::UI},
base::BindOnce(ResolvePromiseWithCookies, std::move(promise), result));
}
// Receives cookies matching |filter| in IO thread.
void GetCookiesOnIO(scoped_refptr<net::URLRequestContextGetter> getter,
std::unique_ptr<base::DictionaryValue> filter,
const Cookies::GetCallback& callback) {
scoped_refptr<util::Promise> promise) {
std::string url;
filter->GetString("url", &url);
auto filtered_callback =
base::Bind(FilterCookies, base::Passed(&filter), callback);
base::Bind(FilterCookies, base::Passed(&filter), std::move(promise));
// Empty url will match all url cookies.
if (url.empty())
@@ -172,31 +190,42 @@ void GetCookiesOnIO(scoped_refptr<net::URLRequestContextGetter> getter,
}
// Removes cookie with |url| and |name| in IO thread.
void RemoveCookieOnIOThread(scoped_refptr<net::URLRequestContextGetter> getter,
const GURL& url,
const std::string& name,
const base::Closure& callback) {
void RemoveCookieOnIO(scoped_refptr<net::URLRequestContextGetter> getter,
const GURL& url,
const std::string& name,
scoped_refptr<util::Promise> promise) {
GetCookieStore(getter)->DeleteCookieAsync(
url, name, base::BindOnce(RunCallbackInUI, callback));
url, name, base::BindOnce(ResolvePromiseInUI, promise));
}
// Resolves/rejects the |promise| in UI thread.
void SettlePromiseInUI(scoped_refptr<util::Promise> promise,
const std::string& errmsg) {
if (errmsg.empty()) {
promise->Resolve();
} else {
promise->RejectWithErrorMessage(errmsg);
}
}
// Callback of SetCookie.
void OnSetCookie(const Cookies::SetCallback& callback, bool success) {
RunCallbackInUI(
base::Bind(callback, success ? Cookies::SUCCESS : Cookies::FAILED));
void OnSetCookie(scoped_refptr<util::Promise> promise, bool success) {
const std::string errmsg = success ? "" : "Setting cookie failed";
RunCallbackInUI(base::Bind(SettlePromiseInUI, std::move(promise), errmsg));
}
// Flushes cookie store in IO thread.
void FlushCookieStoreOnIOThread(
scoped_refptr<net::URLRequestContextGetter> getter,
const base::Closure& callback) {
GetCookieStore(getter)->FlushStore(base::BindOnce(RunCallbackInUI, callback));
scoped_refptr<util::Promise> promise) {
GetCookieStore(getter)->FlushStore(
base::BindOnce(ResolvePromiseInUI, promise));
}
// Sets cookie with |details| in IO thread.
void SetCookieOnIO(scoped_refptr<net::URLRequestContextGetter> getter,
std::unique_ptr<base::DictionaryValue> details,
const Cookies::SetCallback& callback) {
scoped_refptr<util::Promise> promise) {
std::string url, name, value, domain, path;
bool secure = false;
bool http_only = false;
@@ -237,7 +266,7 @@ void SetCookieOnIO(scoped_refptr<net::URLRequestContextGetter> getter,
GURL(url), name, value, domain, path, creation_time, expiration_time,
last_access_time, secure, http_only,
net::CookieSameSite::DEFAULT_MODE, net::COOKIE_PRIORITY_DEFAULT));
auto completion_callback = base::BindOnce(OnSetCookie, callback);
auto completion_callback = base::BindOnce(OnSetCookie, std::move(promise));
if (!canonical_cookie || !canonical_cookie->IsCanonical()) {
std::move(completion_callback).Run(false);
return;
@@ -267,43 +296,56 @@ Cookies::Cookies(v8::Isolate* isolate, AtomBrowserContext* browser_context)
Cookies::~Cookies() {}
void Cookies::Get(const base::DictionaryValue& filter,
const GetCallback& callback) {
v8::Local<v8::Promise> Cookies::Get(const base::DictionaryValue& filter) {
scoped_refptr<util::Promise> promise = new util::Promise(isolate());
auto copy = base::DictionaryValue::From(
base::Value::ToUniquePtrValue(filter.Clone()));
auto* getter = browser_context_->GetRequestContext();
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(GetCookiesOnIO, base::RetainedRef(getter), std::move(copy),
callback));
promise));
return promise->GetHandle();
}
void Cookies::Remove(const GURL& url,
const std::string& name,
const base::Closure& callback) {
v8::Local<v8::Promise> Cookies::Remove(const GURL& url,
const std::string& name) {
scoped_refptr<util::Promise> promise = new util::Promise(isolate());
auto* getter = browser_context_->GetRequestContext();
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(RemoveCookieOnIOThread, base::RetainedRef(getter), url,
name, callback));
base::BindOnce(RemoveCookieOnIO, base::RetainedRef(getter), url, name,
promise));
return promise->GetHandle();
}
void Cookies::Set(const base::DictionaryValue& details,
const SetCallback& callback) {
v8::Local<v8::Promise> Cookies::Set(const base::DictionaryValue& details) {
scoped_refptr<util::Promise> promise = new util::Promise(isolate());
auto copy = base::DictionaryValue::From(
base::Value::ToUniquePtrValue(details.Clone()));
auto* getter = browser_context_->GetRequestContext();
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(SetCookieOnIO, base::RetainedRef(getter), std::move(copy),
callback));
promise));
return promise->GetHandle();
}
void Cookies::FlushStore(const base::Closure& callback) {
v8::Local<v8::Promise> Cookies::FlushStore() {
scoped_refptr<util::Promise> promise = new util::Promise(isolate());
auto* getter = browser_context_->GetRequestContext();
base::PostTaskWithTraits(FROM_HERE, {BrowserThread::IO},
base::BindOnce(FlushCookieStoreOnIOThread,
base::RetainedRef(getter), callback));
base::RetainedRef(getter), promise));
return promise->GetHandle();
}
void Cookies::OnCookieChanged(const CookieDetails* details) {

View File

@@ -10,6 +10,7 @@
#include "atom/browser/api/trackable_object.h"
#include "atom/browser/net/cookie_details.h"
#include "atom/common/promise_util.h"
#include "base/callback_list.h"
#include "native_mate/handle.h"
#include "net/cookies/canonical_cookie.h"
@@ -35,9 +36,6 @@ class Cookies : public mate::TrackableObject<Cookies> {
FAILED,
};
using GetCallback = base::Callback<void(Error, const net::CookieList&)>;
using SetCallback = base::Callback<void(Error)>;
static mate::Handle<Cookies> Create(v8::Isolate* isolate,
AtomBrowserContext* browser_context);
@@ -49,12 +47,10 @@ class Cookies : public mate::TrackableObject<Cookies> {
Cookies(v8::Isolate* isolate, AtomBrowserContext* browser_context);
~Cookies() override;
void Get(const base::DictionaryValue& filter, const GetCallback& callback);
void Remove(const GURL& url,
const std::string& name,
const base::Closure& callback);
void Set(const base::DictionaryValue& details, const SetCallback& callback);
void FlushStore(const base::Closure& callback);
v8::Local<v8::Promise> Get(const base::DictionaryValue& filter);
v8::Local<v8::Promise> Set(const base::DictionaryValue& details);
v8::Local<v8::Promise> Remove(const GURL& url, const std::string& name);
v8::Local<v8::Promise> FlushStore();
// CookieChangeNotifier subscription:
void OnCookieChanged(const CookieDetails*);

View File

@@ -24,47 +24,119 @@
using content::BrowserThread;
namespace atom {
namespace api {
namespace {
// List of registered custom standard schemes.
std::vector<std::string> g_standard_schemes;
struct SchemeOptions {
bool standard = false;
bool secure = false;
bool bypassCSP = false;
bool allowServiceWorkers = false;
bool supportFetchAPI = false;
bool corsEnabled = false;
};
struct CustomScheme {
std::string scheme;
SchemeOptions options;
};
} // namespace
namespace mate {
template <>
struct Converter<CustomScheme> {
static bool FromV8(v8::Isolate* isolate,
v8::Local<v8::Value> val,
CustomScheme* out) {
mate::Dictionary dict;
if (!ConvertFromV8(isolate, val, &dict))
return false;
if (!dict.Get("scheme", &(out->scheme)))
return false;
mate::Dictionary opt;
// options are optional. Default values specified in SchemeOptions are used
if (dict.Get("privileges", &opt)) {
opt.Get("standard", &(out->options.standard));
opt.Get("supportFetchAPI", &(out->options.supportFetchAPI));
opt.Get("secure", &(out->options.secure));
opt.Get("bypassCSP", &(out->options.bypassCSP));
opt.Get("allowServiceWorkers", &(out->options.allowServiceWorkers));
opt.Get("supportFetchAPI", &(out->options.supportFetchAPI));
opt.Get("corsEnabled", &(out->options.corsEnabled));
}
return true;
}
};
} // namespace mate
namespace atom {
namespace api {
std::vector<std::string> GetStandardSchemes() {
return g_standard_schemes;
}
void RegisterStandardSchemes(const std::vector<std::string>& schemes,
mate::Arguments* args) {
g_standard_schemes = schemes;
void RegisterSchemesAsPrivileged(v8::Local<v8::Value> val,
mate::Arguments* args) {
std::vector<CustomScheme> custom_schemes;
if (!mate::ConvertFromV8(args->isolate(), val, &custom_schemes)) {
args->ThrowError("Argument must be an array of custom schemes.");
return;
}
mate::Dictionary opts;
bool secure = false;
args->GetNext(&opts) && opts.Get("secure", &secure);
// Dynamically register the schemes.
auto* policy = content::ChildProcessSecurityPolicy::GetInstance();
for (const std::string& scheme : schemes) {
url::AddStandardScheme(scheme.c_str(), url::SCHEME_WITH_HOST);
if (secure) {
url::AddSecureScheme(scheme.c_str());
std::vector<std::string> secure_schemes, cspbypassing_schemes, fetch_schemes,
service_worker_schemes, cors_schemes;
for (const auto& custom_scheme : custom_schemes) {
// Register scheme to privileged list (https, wss, data, chrome-extension)
if (custom_scheme.options.standard) {
auto* policy = content::ChildProcessSecurityPolicy::GetInstance();
url::AddStandardScheme(custom_scheme.scheme.c_str(),
url::SCHEME_WITH_HOST);
g_standard_schemes.push_back(custom_scheme.scheme);
policy->RegisterWebSafeScheme(custom_scheme.scheme);
}
if (custom_scheme.options.secure) {
secure_schemes.push_back(custom_scheme.scheme);
url::AddSecureScheme(custom_scheme.scheme.c_str());
}
if (custom_scheme.options.bypassCSP) {
cspbypassing_schemes.push_back(custom_scheme.scheme);
url::AddCSPBypassingScheme(custom_scheme.scheme.c_str());
}
if (custom_scheme.options.corsEnabled) {
cors_schemes.push_back(custom_scheme.scheme);
url::AddCorsEnabledScheme(custom_scheme.scheme.c_str());
}
if (custom_scheme.options.supportFetchAPI) {
fetch_schemes.push_back(custom_scheme.scheme);
}
if (custom_scheme.options.allowServiceWorkers) {
service_worker_schemes.push_back(custom_scheme.scheme);
}
policy->RegisterWebSafeScheme(scheme);
}
// Add the schemes to command line switches, so child processes can also
// register them.
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
atom::switches::kStandardSchemes, base::JoinString(schemes, ","));
if (secure) {
const auto AppendSchemesToCmdLine = [](const char* switch_name,
std::vector<std::string> schemes) {
// Add the schemes to command line switches, so child processes can also
// register them.
base::CommandLine::ForCurrentProcess()->AppendSwitchASCII(
atom::switches::kSecureSchemes, base::JoinString(schemes, ","));
}
switch_name, base::JoinString(schemes, ","));
};
AppendSchemesToCmdLine(atom::switches::kSecureSchemes, secure_schemes);
AppendSchemesToCmdLine(atom::switches::kBypassCSPSchemes,
cspbypassing_schemes);
AppendSchemesToCmdLine(atom::switches::kCORSSchemes, cors_schemes);
AppendSchemesToCmdLine(atom::switches::kFetchSchemes, fetch_schemes);
AppendSchemesToCmdLine(atom::switches::kServiceWorkerSchemes,
service_worker_schemes);
AppendSchemesToCmdLine(atom::switches::kStandardSchemes, g_standard_schemes);
}
Protocol::Protocol(v8::Isolate* isolate, AtomBrowserContext* browser_context)
@@ -73,12 +145,6 @@ Protocol::Protocol(v8::Isolate* isolate, AtomBrowserContext* browser_context)
}
Protocol::~Protocol() {}
void Protocol::RegisterServiceWorkerSchemes(
const std::vector<std::string>& schemes) {
atom::AtomBrowserClient::SetCustomServiceWorkerSchemes(schemes);
}
void Protocol::UnregisterProtocol(const std::string& scheme,
mate::Arguments* args) {
CompletionCallback callback;
@@ -195,8 +261,6 @@ void Protocol::BuildPrototype(v8::Isolate* isolate,
v8::Local<v8::FunctionTemplate> prototype) {
prototype->SetClassName(mate::StringToV8(isolate, "Protocol"));
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
.SetMethod("registerServiceWorkerSchemes",
&Protocol::RegisterServiceWorkerSchemes)
.SetMethod("registerStringProtocol",
&Protocol::RegisterProtocol<URLRequestStringJob>)
.SetMethod("registerBufferProtocol",
@@ -228,16 +292,16 @@ void Protocol::BuildPrototype(v8::Isolate* isolate,
namespace {
void RegisterStandardSchemes(const std::vector<std::string>& schemes,
mate::Arguments* args) {
void RegisterSchemesAsPrivileged(v8::Local<v8::Value> val,
mate::Arguments* args) {
if (atom::Browser::Get()->is_ready()) {
args->ThrowError(
"protocol.registerStandardSchemes should be called before "
"protocol.registerSchemesAsPrivileged should be called before "
"app is ready");
return;
}
atom::api::RegisterStandardSchemes(schemes, args);
atom::api::RegisterSchemesAsPrivileged(val, args);
}
void Initialize(v8::Local<v8::Object> exports,
@@ -246,7 +310,7 @@ void Initialize(v8::Local<v8::Object> exports,
void* priv) {
v8::Isolate* isolate = context->GetIsolate();
mate::Dictionary dict(isolate, exports);
dict.SetMethod("registerStandardSchemes", &RegisterStandardSchemes);
dict.SetMethod("registerSchemesAsPrivileged", &RegisterSchemesAsPrivileged);
dict.SetMethod("getStandardSchemes", &atom::api::GetStandardSchemes);
}

View File

@@ -34,8 +34,9 @@ namespace atom {
namespace api {
std::vector<std::string> GetStandardSchemes();
void RegisterStandardSchemes(const std::vector<std::string>& schemes,
mate::Arguments* args);
void RegisterSchemesAsPrivileged(v8::Local<v8::Value> val,
mate::Arguments* args);
class Protocol : public mate::TrackableObject<Protocol> {
public:
@@ -94,9 +95,6 @@ class Protocol : public mate::TrackableObject<Protocol> {
DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler);
};
// Register schemes that can handle service worker.
void RegisterServiceWorkerSchemes(const std::vector<std::string>& schemes);
// Register the protocol with certain request job.
template <typename RequestJob>
void RegisterProtocol(const std::string& scheme,

View File

@@ -114,9 +114,6 @@ namespace {
// Next navigation should not restart renderer process.
bool g_suppress_renderer_process_restart = false;
// Custom schemes to be registered to handle service worker.
base::NoDestructor<std::string> g_custom_service_worker_schemes;
bool IsSameWebSite(content::BrowserContext* browser_context,
const GURL& src_url,
const GURL& dest_url) {
@@ -148,11 +145,6 @@ void AtomBrowserClient::SuppressRendererProcessRestartForOnce() {
g_suppress_renderer_process_restart = true;
}
void AtomBrowserClient::SetCustomServiceWorkerSchemes(
const std::vector<std::string>& schemes) {
*g_custom_service_worker_schemes = base::JoinString(schemes, ",");
}
AtomBrowserClient* AtomBrowserClient::Get() {
return g_browser_client;
}
@@ -477,18 +469,15 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches(
return;
// Copy following switches to child process.
static const char* const kCommonSwitchNames[] = {switches::kStandardSchemes,
switches::kEnableSandbox,
switches::kSecureSchemes};
static const char* const kCommonSwitchNames[] = {
switches::kStandardSchemes, switches::kEnableSandbox,
switches::kSecureSchemes, switches::kBypassCSPSchemes,
switches::kCORSSchemes, switches::kFetchSchemes,
switches::kServiceWorkerSchemes};
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
kCommonSwitchNames,
arraysize(kCommonSwitchNames));
// The registered service worker schemes.
if (!g_custom_service_worker_schemes->empty())
command_line->AppendSwitchASCII(switches::kRegisterServiceWorkerSchemes,
*g_custom_service_worker_schemes);
#if defined(OS_WIN)
// Append --app-user-model-id.
PWSTR current_app_id;

View File

@@ -49,10 +49,6 @@ class AtomBrowserClient : public content::ContentBrowserClient,
// Don't force renderer process to restart for once.
static void SuppressRendererProcessRestartForOnce();
// Custom schemes to be registered to handle service worker.
static void SetCustomServiceWorkerSchemes(
const std::vector<std::string>& schemes);
NotificationPresenter* GetNotificationPresenter();
void WebNotificationAllowed(int render_process_id,

View File

@@ -455,7 +455,7 @@ bool AtomBrowserMainParts::MainMessageLoopRun(int* result_code) {
void AtomBrowserMainParts::PreDefaultMainMessageLoopRun(
base::OnceClosure quit_closure) {
Browser::SetMainMessageLoopQuitClosure(std::move(quit_closure));
Browser::Get()->SetMainMessageLoopQuitClosure(std::move(quit_closure));
}
void AtomBrowserMainParts::PostMainMessageLoopStart() {

View File

@@ -100,23 +100,58 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated(
if (relay)
window = relay->GetNativeWindow();
auto* web_preferences = WebContentsPreferences::From(web_contents);
bool offscreen =
!web_preferences || web_preferences->IsEnabled(options::kOffscreen);
// Show save dialog if save path was not set already on item
base::FilePath path;
GetItemSavePath(item, &path);
// Show save dialog if save path was not set already on item
file_dialog::DialogSettings settings;
GetItemSaveDialogOptions(item, &settings);
if (!settings.parent_window)
settings.parent_window = window;
settings.force_detached = offscreen;
if (settings.title.size() == 0)
settings.title = item->GetURL().spec();
if (!settings.default_path.empty())
settings.default_path = default_path;
if (path.empty() && file_dialog::ShowSaveDialog(settings, &path)) {
if (path.empty()) {
file_dialog::DialogSettings settings;
GetItemSaveDialogOptions(item, &settings);
if (!settings.parent_window)
settings.parent_window = window;
if (settings.title.size() == 0)
settings.title = item->GetURL().spec();
if (!settings.default_path.empty())
settings.default_path = default_path;
auto* web_preferences = WebContentsPreferences::From(web_contents);
const bool offscreen =
!web_preferences || web_preferences->IsEnabled(options::kOffscreen);
settings.force_detached = offscreen;
auto dialog_callback =
base::Bind(&AtomDownloadManagerDelegate::OnDownloadSaveDialogDone,
base::Unretained(this), download_id, callback);
file_dialog::ShowSaveDialog(settings, dialog_callback);
} else {
callback.Run(path, download::DownloadItem::TARGET_DISPOSITION_PROMPT,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path,
download::DOWNLOAD_INTERRUPT_REASON_NONE);
}
}
#if defined(MAS_BUILD)
void AtomDownloadManagerDelegate::OnDownloadSaveDialogDone(
uint32_t download_id,
const content::DownloadTargetCallback& download_callback,
bool result,
const base::FilePath& path,
const std::string& bookmark)
#else
void AtomDownloadManagerDelegate::OnDownloadSaveDialogDone(
uint32_t download_id,
const content::DownloadTargetCallback& download_callback,
bool result,
const base::FilePath& path)
#endif
{
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto* item = download_manager_->GetDownload(download_id);
if (!item)
return;
if (result) {
// Remember the last selected download directory.
AtomBrowserContext* browser_context = static_cast<AtomBrowserContext*>(
download_manager_->GetBrowserContext());
@@ -133,12 +168,16 @@ void AtomDownloadManagerDelegate::OnDownloadPathGenerated(
}
// Running the DownloadTargetCallback with an empty FilePath signals that the
// download should be cancelled.
// If user cancels the file save dialog, run the callback with empty FilePath.
callback.Run(path, download::DownloadItem::TARGET_DISPOSITION_PROMPT,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, path,
path.empty() ? download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
: download::DOWNLOAD_INTERRUPT_REASON_NONE);
// download should be cancelled. If user cancels the file save dialog, run
// the callback with empty FilePath.
const base::FilePath download_path = result ? path : base::FilePath();
const auto interrupt_reason =
download_path.empty() ? download::DOWNLOAD_INTERRUPT_REASON_USER_CANCELED
: download::DOWNLOAD_INTERRUPT_REASON_NONE;
download_callback.Run(download_path,
download::DownloadItem::TARGET_DISPOSITION_PROMPT,
download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS,
download_path, interrupt_reason);
}
void AtomDownloadManagerDelegate::Shutdown() {

View File

@@ -25,10 +25,6 @@ class AtomDownloadManagerDelegate : public content::DownloadManagerDelegate {
explicit AtomDownloadManagerDelegate(content::DownloadManager* manager);
~AtomDownloadManagerDelegate() override;
void OnDownloadPathGenerated(uint32_t download_id,
const content::DownloadTargetCallback& callback,
const base::FilePath& default_path);
// content::DownloadManagerDelegate:
void Shutdown() override;
bool DetermineDownloadTarget(
@@ -45,6 +41,25 @@ class AtomDownloadManagerDelegate : public content::DownloadManagerDelegate {
void GetItemSaveDialogOptions(download::DownloadItem* item,
file_dialog::DialogSettings* settings);
void OnDownloadPathGenerated(uint32_t download_id,
const content::DownloadTargetCallback& callback,
const base::FilePath& default_path);
#if defined(MAS_BUILD)
void OnDownloadSaveDialogDone(
uint32_t download_id,
const content::DownloadTargetCallback& download_callback,
bool result,
const base::FilePath& path,
const std::string& bookmark);
#else
void OnDownloadSaveDialogDone(
uint32_t download_id,
const content::DownloadTargetCallback& download_callback,
bool result,
const base::FilePath& path);
#endif
content::DownloadManager* download_manager_;
base::WeakPtrFactory<AtomDownloadManagerDelegate> weak_ptr_factory_;

View File

@@ -25,9 +25,6 @@
namespace atom {
// Null until/unless the default main message loop is running.
base::NoDestructor<base::OnceClosure> g_quit_main_message_loop;
Browser::LoginItemSettings::LoginItemSettings() = default;
Browser::LoginItemSettings::~LoginItemSettings() = default;
Browser::LoginItemSettings::LoginItemSettings(const LoginItemSettings& other) =
@@ -95,11 +92,12 @@ void Browser::Shutdown() {
for (BrowserObserver& observer : observers_)
observer.OnQuit();
if (*g_quit_main_message_loop) {
std::move(*g_quit_main_message_loop).Run();
if (quit_main_message_loop_) {
std::move(quit_main_message_loop_).Run();
} else {
// There is no message loop available so we are in early stage.
exit(0);
// There is no message loop available so we are in early stage, wait until
// the quit_main_message_loop_ is available.
// Exiting now would leave defunct processes behind.
}
}
@@ -196,7 +194,10 @@ void Browser::PreMainMessageLoopRun() {
}
void Browser::SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure) {
*g_quit_main_message_loop = std::move(quit_closure);
if (is_shutdown_)
std::move(quit_closure).Run();
else
quit_main_message_loop_ = std::move(quit_closure);
}
void Browser::NotifyAndShutdown() {

View File

@@ -244,7 +244,7 @@ class Browser : public WindowListObserver {
// Stores the supplied |quit_closure|, to be run when the last Browser
// instance is destroyed.
static void SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure);
void SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure);
void AddObserver(BrowserObserver* obs) { observers_.AddObserver(obs); }
@@ -287,6 +287,9 @@ class Browser : public WindowListObserver {
// The browser is being shutdown.
bool is_shutdown_ = false;
// Null until/unless the default main message loop is running.
base::OnceClosure quit_main_message_loop_;
int badge_count_ = 0;
util::Promise* ready_promise_ = nullptr;

View File

@@ -141,6 +141,7 @@ void URLRequestStreamJob::StartAsync(
}
void URLRequestStreamJob::OnData(std::vector<char>&& buffer) { // NOLINT
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (write_buffer_.empty()) {
// Quick branch without copying.
write_buffer_ = std::move(buffer);
@@ -175,7 +176,7 @@ void URLRequestStreamJob::OnError(int error) {
int URLRequestStreamJob::ReadRawData(net::IOBuffer* dest, int dest_size) {
response_start_time_ = base::TimeTicks::Now();
if (ended_)
if (ended_ && write_buffer_.empty())
return 0;
// When write_buffer_ is empty, there is no data valable yet, we have to save

View File

@@ -252,6 +252,50 @@ class AtomBeginFrameTimer : public viz::DelayBasedTimeSourceClient {
DISALLOW_COPY_AND_ASSIGN(AtomBeginFrameTimer);
};
#if !defined(OS_MACOSX)
class AtomDelegatedFrameHostClient : public content::DelegatedFrameHostClient {
public:
explicit AtomDelegatedFrameHostClient(OffScreenRenderWidgetHostView* view)
: view_(view) {}
ui::Layer* DelegatedFrameHostGetLayer() const override {
return view_->GetRootLayer();
}
bool DelegatedFrameHostIsVisible() const override {
return view_->IsShowing();
}
SkColor DelegatedFrameHostGetGutterColor() const override {
if (view_->render_widget_host()->delegate() &&
view_->render_widget_host()->delegate()->IsFullscreenForCurrentTab()) {
return SK_ColorWHITE;
}
return *view_->GetBackgroundColor();
}
void OnFrameTokenChanged(uint32_t frame_token) override {
view_->render_widget_host()->DidProcessFrame(frame_token);
}
float GetDeviceScaleFactor() const override {
return view_->GetDeviceScaleFactor();
}
std::vector<viz::SurfaceId> CollectSurfaceIdsForEviction() override {
return view_->render_widget_host()->CollectSurfaceIdsForEviction();
}
void OnBeginFrame(base::TimeTicks frame_time) override {}
void InvalidateLocalSurfaceIdOnEviction() override {}
private:
OffScreenRenderWidgetHostView* const view_;
DISALLOW_COPY_AND_ASSIGN(AtomDelegatedFrameHostClient);
};
#endif // !defined(OS_MACOSX)
OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
bool transparent,
bool painting,
@@ -274,18 +318,22 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
weak_ptr_factory_(this) {
DCHECK(render_widget_host_);
bool is_guest_view_hack = parent_host_view_ != nullptr;
current_device_scale_factor_ = kDefaultScaleFactor;
#if !defined(OS_MACOSX)
local_surface_id_allocator_.GenerateId();
local_surface_id_allocation_ =
local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation();
delegated_frame_host_client_.reset(new AtomDelegatedFrameHostClient(this));
delegated_frame_host_ = std::make_unique<content::DelegatedFrameHost>(
AllocateFrameSinkId(is_guest_view_hack), this,
AllocateFrameSinkId(is_guest_view_hack),
delegated_frame_host_client_.get(),
true /* should_register_frame_sink_id */);
root_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR));
#endif
current_device_scale_factor_ = kDefaultScaleFactor;
local_surface_id_ = local_surface_id_allocator_.GenerateId();
#if defined(OS_MACOSX)
last_frame_root_background_color_ = SK_ColorTRANSPARENT;
CreatePlatformWidget(is_guest_view_hack);
@@ -374,7 +422,7 @@ void OffScreenRenderWidgetHostView::SendBeginFrame(
begin_frame_number_++;
if (renderer_compositor_frame_sink_)
renderer_compositor_frame_sink_->OnBeginFrame(begin_frame_args);
renderer_compositor_frame_sink_->OnBeginFrame(begin_frame_args, {});
}
void OffScreenRenderWidgetHostView::InitAsChild(gfx::NativeView) {
@@ -433,8 +481,9 @@ void OffScreenRenderWidgetHostView::Show() {
browser_compositor_->SetRenderWidgetHostIsHidden(false);
#else
delegated_frame_host_->AttachToCompositor(compositor_.get());
delegated_frame_host_->WasShown(GetLocalSurfaceId(),
GetRootLayer()->bounds().size(), false);
delegated_frame_host_->WasShown(
GetLocalSurfaceIdAllocation().local_surface_id(),
GetRootLayer()->bounds().size(), false);
#endif
if (render_widget_host_)
@@ -525,19 +574,22 @@ void OffScreenRenderWidgetHostView::TakeFallbackContentFrom(
void OffScreenRenderWidgetHostView::DidCreateNewRendererCompositorFrameSink(
viz::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink) {
renderer_compositor_frame_sink_ = renderer_compositor_frame_sink;
#if defined(OS_MACOSX)
browser_compositor_->DidCreateNewRendererCompositorFrameSink(
renderer_compositor_frame_sink_);
#else
if (GetDelegatedFrameHost()) {
GetDelegatedFrameHost()->DidCreateNewRendererCompositorFrameSink(
renderer_compositor_frame_sink_);
}
#endif
}
void OffScreenRenderWidgetHostView::SubmitCompositorFrame(
const viz::LocalSurfaceId& local_surface_id,
viz::CompositorFrame frame,
base::Optional<viz::HitTestRegionList> hit_test_region_list) {
TRACE_EVENT0("electron",
"OffScreenRenderWidgetHostView::SubmitCompositorFrame");
#if defined(OS_MACOSX)
last_frame_root_background_color_ = frame.metadata.root_background_color;
#endif
@@ -587,7 +639,7 @@ void OffScreenRenderWidgetHostView::SubmitCompositorFrame(
}
void OffScreenRenderWidgetHostView::ClearCompositorFrame() {
GetDelegatedFrameHost()->ClearDelegatedFrame();
NOTREACHED();
}
void OffScreenRenderWidgetHostView::ResetFallbackToFirstNavigationSurface() {
@@ -667,9 +719,6 @@ void OffScreenRenderWidgetHostView::Destroy() {
void OffScreenRenderWidgetHostView::SetTooltipText(const base::string16&) {}
void OffScreenRenderWidgetHostView::SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params&) {}
uint32_t OffScreenRenderWidgetHostView::GetCaptureSequenceNumber() const {
return latest_capture_sequence_number_;
}
@@ -698,7 +747,6 @@ void OffScreenRenderWidgetHostView::InitAsGuest(
content::RenderWidgetHostView* parent_host_view,
content::RenderWidgetHostViewGuest* guest_view) {
parent_host_view_->AddGuestHostView(this);
parent_host_view_->RegisterGuestViewFrameSwappedCallback(guest_view);
}
void OffScreenRenderWidgetHostView::TransformPointToRootSurface(
@@ -745,40 +793,6 @@ OffScreenRenderWidgetHostView::CreateViewForWidget(
render_widget_host, embedder_host_view, size());
}
#if !defined(OS_MACOSX)
ui::Layer* OffScreenRenderWidgetHostView::DelegatedFrameHostGetLayer() const {
return const_cast<ui::Layer*>(root_layer_.get());
}
bool OffScreenRenderWidgetHostView::DelegatedFrameHostIsVisible() const {
return is_showing_;
}
SkColor OffScreenRenderWidgetHostView::DelegatedFrameHostGetGutterColor()
const {
if (render_widget_host_->delegate() &&
render_widget_host_->delegate()->IsFullscreenForCurrentTab()) {
return SK_ColorWHITE;
}
return background_color_;
}
void OffScreenRenderWidgetHostView::OnFirstSurfaceActivation(
const viz::SurfaceInfo& surface_info) {}
void OffScreenRenderWidgetHostView::OnBeginFrame(base::TimeTicks frame_time) {}
void OffScreenRenderWidgetHostView::OnFrameTokenChanged(uint32_t frame_token) {
render_widget_host_->DidProcessFrame(frame_token);
}
const viz::LocalSurfaceId& OffScreenRenderWidgetHostView::GetLocalSurfaceId()
const {
return local_surface_id_;
}
#endif // !defined(OS_MACOSX)
const viz::FrameSinkId& OffScreenRenderWidgetHostView::GetFrameSinkId() const {
return GetDelegatedFrameHost()->frame_sink_id();
}
@@ -875,21 +889,6 @@ void OffScreenRenderWidgetHostView::ProxyViewDestroyed(
Invalidate();
}
void OffScreenRenderWidgetHostView::RegisterGuestViewFrameSwappedCallback(
content::RenderWidgetHostViewGuest* guest_host_view) {
guest_host_view->RegisterFrameSwappedCallback(base::BindOnce(
&OffScreenRenderWidgetHostView::OnGuestViewFrameSwapped,
weak_ptr_factory_.GetWeakPtr(), base::Unretained(guest_host_view)));
}
void OffScreenRenderWidgetHostView::OnGuestViewFrameSwapped(
content::RenderWidgetHostViewGuest* guest_host_view) {
InvalidateBounds(gfx::ConvertRectToPixel(current_device_scale_factor_,
guest_host_view->GetViewBounds()));
RegisterGuestViewFrameSwappedCallback(guest_host_view);
}
std::unique_ptr<viz::SoftwareOutputDevice>
OffScreenRenderWidgetHostView::CreateSoftwareOutputDevice(
ui::Compositor* compositor) {
@@ -925,7 +924,7 @@ void OffScreenRenderWidgetHostView::SetNeedsBeginFrames(
begin_frame_timer_->SetActive(needs_begin_frames);
if (software_output_device_) {
software_output_device_->SetActive(needs_begin_frames && painting_, false);
software_output_device_->SetActive(painting_, false);
}
}
@@ -937,8 +936,6 @@ void OffScreenRenderWidgetHostView::SetWantsAnimateOnlyBeginFrames() {
void OffScreenRenderWidgetHostView::OnPaint(const gfx::Rect& damage_rect,
const SkBitmap& bitmap) {
TRACE_EVENT0("electron", "OffScreenRenderWidgetHostView::OnPaint");
HoldResize();
if (parent_callback_) {
@@ -1023,10 +1020,6 @@ void OffScreenRenderWidgetHostView::SynchronizeVisualProperties() {
}
ResizeRootLayer(false);
if (render_widget_host_)
render_widget_host_->SynchronizeVisualProperties();
GetDelegatedFrameHost()->EmbedSurface(
local_surface_id_, size_, cc::DeadlinePolicy::UseDefaultDeadline());
}
void OffScreenRenderWidgetHostView::SendMouseEvent(
@@ -1192,6 +1185,13 @@ ui::Layer* OffScreenRenderWidgetHostView::GetRootLayer() const {
return root_layer_.get();
}
#if !defined(OS_MACOSX)
const viz::LocalSurfaceIdAllocation&
OffScreenRenderWidgetHostView::GetLocalSurfaceIdAllocation() const {
return local_surface_id_allocation_;
}
#endif // defined(OS_MACOSX)
content::DelegatedFrameHost*
OffScreenRenderWidgetHostView::GetDelegatedFrameHost() const {
return delegated_frame_host_.get();
@@ -1204,11 +1204,6 @@ void OffScreenRenderWidgetHostView::SetupFrameRate(bool force) {
frame_rate_threshold_us_ = 1000000 / frame_rate_;
if (GetCompositor()) {
GetCompositor()->SetAuthoritativeVSyncInterval(
base::TimeDelta::FromMicroseconds(frame_rate_threshold_us_));
}
if (copy_frame_generator_.get()) {
copy_frame_generator_->set_frame_rate_threshold_us(
frame_rate_threshold_us_);
@@ -1257,28 +1252,32 @@ void OffScreenRenderWidgetHostView::ResizeRootLayer(bool force) {
size == GetRootLayer()->bounds().size())
return;
const gfx::Size& size_in_pixels =
gfx::ConvertSizeToPixel(current_device_scale_factor_, size);
local_surface_id_ = local_surface_id_allocator_.GenerateId();
GetRootLayer()->SetBounds(gfx::Rect(size));
GetCompositor()->SetScaleAndSize(current_device_scale_factor_, size_in_pixels,
local_surface_id_);
#if defined(OS_MACOSX)
bool resized = UpdateNSViewAndDisplay();
#else
const gfx::Size& size_in_pixels =
gfx::ConvertSizeToPixel(current_device_scale_factor_, size);
local_surface_id_allocator_.GenerateId();
local_surface_id_allocation_ =
local_surface_id_allocator_.GetCurrentLocalSurfaceIdAllocation();
GetCompositor()->SetScaleAndSize(current_device_scale_factor_, size_in_pixels,
local_surface_id_allocation_);
bool resized = true;
GetDelegatedFrameHost()->EmbedSurface(
local_surface_id_, size, cc::DeadlinePolicy::UseDefaultDeadline());
local_surface_id_allocation_.local_surface_id(), size,
cc::DeadlinePolicy::UseDefaultDeadline());
#endif
// Note that |render_widget_host_| will retrieve resize parameters from the
// DelegatedFrameHost, so it must have SynchronizeVisualProperties called
// after.
if (resized && render_widget_host_)
if (resized && render_widget_host_) {
render_widget_host_->SynchronizeVisualProperties();
}
}
viz::FrameSinkId OffScreenRenderWidgetHostView::AllocateFrameSinkId(

View File

@@ -66,13 +66,12 @@ class AtomBeginFrameTimer;
#if defined(OS_MACOSX)
class MacHelper;
#else
class AtomDelegatedFrameHostClient;
#endif
class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
public ui::CompositorDelegate,
#if !defined(OS_MACOSX)
public content::DelegatedFrameHostClient,
#endif
public OffscreenViewProxyObserver {
public:
OffScreenRenderWidgetHostView(bool transparent,
@@ -118,7 +117,6 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
void SetActive(bool active) override;
void ShowDefinitionForSelection() override;
void SpeakSelection() override;
bool ShouldContinueToPauseForFrame() override;
bool UpdateNSViewAndDisplay();
#endif // defined(OS_MACOSX)
@@ -144,8 +142,6 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
void Destroy(void) override;
void SetTooltipText(const base::string16&) override;
content::CursorManager* GetCursorManager() override;
void SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params&) override;
void CopyFromSurface(
const gfx::Rect& src_rect,
const gfx::Size& output_size,
@@ -170,18 +166,8 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
content::RenderWidgetHost*,
content::WebContentsView*) override;
#if !defined(OS_MACOSX)
// content::DelegatedFrameHostClient:
int DelegatedFrameHostGetGpuMemoryBufferClientId(void) const;
ui::Layer* DelegatedFrameHostGetLayer(void) const override;
bool DelegatedFrameHostIsVisible(void) const override;
SkColor DelegatedFrameHostGetGutterColor() const override;
void OnFirstSurfaceActivation(const viz::SurfaceInfo& surface_info) override;
void OnBeginFrame(base::TimeTicks frame_time) override;
void OnFrameTokenChanged(uint32_t frame_token) override;
#endif // !defined(OS_MACOSX)
const viz::LocalSurfaceId& GetLocalSurfaceId() const override;
const viz::LocalSurfaceIdAllocation& GetLocalSurfaceIdAllocation()
const override;
const viz::FrameSinkId& GetFrameSinkId() const override;
void DidNavigate() override;
@@ -220,16 +206,13 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
void RemoveViewProxy(OffscreenViewProxy* proxy);
void ProxyViewDestroyed(OffscreenViewProxy* proxy) override;
void RegisterGuestViewFrameSwappedCallback(
content::RenderWidgetHostViewGuest* guest_host_view);
void OnGuestViewFrameSwapped(
content::RenderWidgetHostViewGuest* guest_host_view);
void OnPaint(const gfx::Rect& damage_rect, const SkBitmap& bitmap);
void OnPopupPaint(const gfx::Rect& damage_rect, const SkBitmap& bitmap);
void OnProxyViewPaint(const gfx::Rect& damage_rect) override;
bool IsPopupWidget() const { return popup_type_ != blink::kWebPopupTypeNone; }
bool IsPopupWidget() const {
return widget_type_ == content::WidgetType::kPopup;
}
void HoldResize();
void ReleaseResize();
@@ -322,7 +305,7 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
bool paint_callback_running_ = false;
viz::LocalSurfaceId local_surface_id_;
viz::LocalSurfaceIdAllocation local_surface_id_allocation_;
viz::ParentLocalSurfaceIdAllocator local_surface_id_allocator_;
std::unique_ptr<ui::Layer> root_layer_;
@@ -349,6 +332,8 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
// Selected text on the renderer.
std::string selected_text_;
#else
std::unique_ptr<AtomDelegatedFrameHostClient> delegated_frame_host_client_;
#endif
content::MouseWheelPhaseHandler mouse_wheel_phase_handler_;

View File

@@ -11,13 +11,15 @@
#include "ui/accelerated_widget_mac/accelerated_widget_mac.h"
#include "ui/display/screen.h"
#include "components/viz/common/features.h"
namespace atom {
class MacHelper : public content::BrowserCompositorMacClient,
public ui::AcceleratedWidgetMacNSView {
public:
explicit MacHelper(OffScreenRenderWidgetHostView* view) : view_(view) {
[view_->GetNativeView() setWantsLayer:YES];
[view_->GetNativeView().GetNativeNSView() setWantsLayer:YES];
}
virtual ~MacHelper() {}
@@ -44,25 +46,14 @@ class MacHelper : public content::BrowserCompositorMacClient,
void DestroyCompositorForShutdown() override {}
bool SynchronizeVisualProperties(
const base::Optional<viz::LocalSurfaceId>&
child_allocated_local_surface_id) override {
auto* browser_compositor = view_->browser_compositor();
if (child_allocated_local_surface_id) {
browser_compositor->UpdateRendererLocalSurfaceIdFromChild(
*child_allocated_local_surface_id);
} else {
browser_compositor->AllocateNewRendererLocalSurfaceId();
}
if (auto* host = browser_compositor->GetDelegatedFrameHost()) {
host->EmbedSurface(browser_compositor->GetRendererLocalSurfaceId(),
browser_compositor->GetRendererSize(),
cc::DeadlinePolicy::UseDefaultDeadline());
}
bool OnBrowserCompositorSurfaceIdChanged() override {
return view_->render_widget_host()->SynchronizeVisualProperties();
}
std::vector<viz::SurfaceId> CollectSurfaceIdsForEviction() override {
return view_->render_widget_host()->CollectSurfaceIdsForEviction();
}
private:
OffScreenRenderWidgetHostView* view_;
@@ -76,20 +67,20 @@ void OffScreenRenderWidgetHostView::ShowDefinitionForSelection() {}
void OffScreenRenderWidgetHostView::SpeakSelection() {}
bool OffScreenRenderWidgetHostView::UpdateNSViewAndDisplay() {
return browser_compositor_->UpdateNSViewAndDisplay(
return browser_compositor_->UpdateSurfaceFromNSView(
GetRootLayer()->bounds().size(), GetDisplay());
}
bool OffScreenRenderWidgetHostView::ShouldContinueToPauseForFrame() {
return browser_compositor_->ShouldContinueToPauseForFrame();
}
void OffScreenRenderWidgetHostView::CreatePlatformWidget(
bool is_guest_view_hack) {
mac_helper_ = new MacHelper(this);
browser_compositor_.reset(new content::BrowserCompositorMac(
mac_helper_, mac_helper_, render_widget_host_->is_hidden(), GetDisplay(),
AllocateFrameSinkId(is_guest_view_hack)));
if (!base::FeatureList::IsEnabled(features::kVizDisplayCompositor)) {
SetNeedsBeginFrames(true);
}
}
void OffScreenRenderWidgetHostView::DestroyPlatformWidget() {
@@ -132,14 +123,15 @@ display::Display OffScreenRenderWidgetHostView::GetDisplay() {
void OffScreenRenderWidgetHostView::OnDidUpdateVisualPropertiesComplete(
const cc::RenderFrameMetadata& metadata) {
DCHECK_EQ(current_device_scale_factor_, metadata.device_scale_factor);
browser_compositor_->SynchronizeVisualProperties(
browser_compositor_->UpdateSurfaceFromChild(
metadata.device_scale_factor, metadata.viewport_size_in_pixels,
metadata.local_surface_id.value_or(viz::LocalSurfaceId()));
metadata.local_surface_id_allocation.value_or(
viz::LocalSurfaceIdAllocation()));
}
const viz::LocalSurfaceId& OffScreenRenderWidgetHostView::GetLocalSurfaceId()
const {
return browser_compositor_->GetRendererLocalSurfaceId();
const viz::LocalSurfaceIdAllocation&
OffScreenRenderWidgetHostView::GetLocalSurfaceIdAllocation() const {
return browser_compositor_->GetRendererLocalSurfaceIdAllocation();
}
ui::Compositor* OffScreenRenderWidgetHostView::GetCompositor() const {

View File

@@ -128,7 +128,7 @@ OffScreenWebContentsView::CreateViewForWidget(
}
content::RenderWidgetHostViewBase*
OffScreenWebContentsView::CreateViewForPopupWidget(
OffScreenWebContentsView::CreateViewForChildWidget(
content::RenderWidgetHost* render_widget_host) {
content::WebContentsImpl* web_contents_impl =
static_cast<content::WebContentsImpl*>(web_contents_);

View File

@@ -57,7 +57,7 @@ class OffScreenWebContentsView : public content::WebContentsView,
content::RenderWidgetHostViewBase* CreateViewForWidget(
content::RenderWidgetHost* render_widget_host,
bool is_guest_view_hack) override;
content::RenderWidgetHostViewBase* CreateViewForPopupWidget(
content::RenderWidgetHostViewBase* CreateViewForChildWidget(
content::RenderWidgetHost* render_widget_host) override;
void SetPageTitle(const base::string16& title) override;
void RenderViewCreated(content::RenderViewHost* host) override;

View File

@@ -17,9 +17,9 @@
<key>CFBundleIconFile</key>
<string>electron.icns</string>
<key>CFBundleVersion</key>
<string>5.0.0-nightly.20190122</string>
<string>5.0.0-beta.2</string>
<key>CFBundleShortVersionString</key>
<string>5.0.0-nightly.20190122</string>
<string>5.0.0-beta.2</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.developer-tools</string>
<key>LSMinimumSystemVersion</key>

View File

@@ -50,8 +50,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 5,0,0,20190122
PRODUCTVERSION 5,0,0,20190122
FILEVERSION 5,0,0,2
PRODUCTVERSION 5,0,0,2
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L

View File

@@ -9,7 +9,7 @@
#define ATOM_MINOR_VERSION 0
#define ATOM_PATCH_VERSION 0
// clang-format off
#define ATOM_PRE_RELEASE_VERSION -nightly.20190122
#define ATOM_PRE_RELEASE_VERSION -beta.2
// clang-format on
#ifndef ATOM_STRINGIFY

View File

@@ -179,11 +179,20 @@ const char kDisableHttpCache[] = "disable-http-cache";
const char kStandardSchemes[] = "standard-schemes";
// Register schemes to handle service worker.
const char kRegisterServiceWorkerSchemes[] = "register-service-worker-schemes";
const char kServiceWorkerSchemes[] = "service-worker-schemes";
// Register schemes as secure.
const char kSecureSchemes[] = "secure-schemes";
// Register schemes as bypassing CSP.
const char kBypassCSPSchemes[] = "bypasscsp-schemes";
// Register schemes as support fetch API.
const char kFetchSchemes[] = "fetch-schemes";
// Register schemes as CORS enabled.
const char kCORSSchemes[] = "cors-schemes";
// The browser process app model ID
const char kAppUserModelId[] = "app-user-model-id";

View File

@@ -89,8 +89,11 @@ extern const char kPpapiFlashPath[];
extern const char kPpapiFlashVersion[];
extern const char kDisableHttpCache[];
extern const char kStandardSchemes[];
extern const char kRegisterServiceWorkerSchemes[];
extern const char kServiceWorkerSchemes[];
extern const char kSecureSchemes[];
extern const char kBypassCSPSchemes[];
extern const char kFetchSchemes[];
extern const char kCORSSchemes[];
extern const char kAppUserModelId[];
extern const char kAppPath[];

View File

@@ -4,6 +4,8 @@
#include "atom/renderer/api/atom_api_web_frame.h"
#include <utility>
#include "atom/common/api/api_messages.h"
#include "atom/common/api/event_emitter_caller.h"
#include "atom/common/native_mate_converters/blink_converter.h"
@@ -29,7 +31,6 @@
#include "third_party/blink/public/web/web_script_execution_callback.h"
#include "third_party/blink/public/web/web_script_source.h"
#include "third_party/blink/public/web/web_view.h"
#include "third_party/blink/renderer/platform/weborigin/scheme_registry.h"
#include "url/url_util.h"
#include "atom/common/node_includes.h"
@@ -137,6 +138,26 @@ class FrameSpellChecker : public content::RenderFrameVisitor {
} // namespace
class AtomWebFrameObserver : public content::RenderFrameObserver {
public:
explicit AtomWebFrameObserver(
content::RenderFrame* render_frame,
std::unique_ptr<SpellCheckClient> spell_check_client)
: content::RenderFrameObserver(render_frame),
spell_check_client_(std::move(spell_check_client)) {}
~AtomWebFrameObserver() final {}
// RenderFrameObserver implementation.
void OnDestruct() final {
spell_check_client_.reset();
// Frame observers should delete themselves
delete this;
}
private:
std::unique_ptr<SpellCheckClient> spell_check_client_;
};
WebFrame::WebFrame(v8::Isolate* isolate)
: web_frame_(blink::WebLocalFrame::FrameForCurrentContext()) {
Init(isolate);
@@ -229,63 +250,15 @@ void WebFrame::SetSpellCheckProvider(mate::Arguments* args,
return;
}
auto client =
auto spell_check_client =
std::make_unique<SpellCheckClient>(language, args->isolate(), provider);
// Set spellchecker for all live frames in the same process or
// in the sandbox mode for all live sub frames to this WebFrame.
FrameSpellChecker spell_checker(
client.get(), content::RenderFrame::FromWebFrame(web_frame_));
auto* render_frame = content::RenderFrame::FromWebFrame(web_frame_);
FrameSpellChecker spell_checker(spell_check_client.get(), render_frame);
content::RenderFrame::ForEach(&spell_checker);
spell_check_client_.swap(client);
web_frame_->SetSpellCheckPanelHostClient(spell_check_client_.get());
}
void WebFrame::RegisterURLSchemeAsBypassingCSP(const std::string& scheme) {
// Register scheme to bypass pages's Content Security Policy.
blink::SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy(
WTF::String::FromUTF8(scheme.data(), scheme.length()));
}
void WebFrame::RegisterURLSchemeAsPrivileged(const std::string& scheme,
mate::Arguments* args) {
// TODO(deepak1556): blink::SchemeRegistry methods should be called
// before any renderer threads are created. Fixing this would break
// current api. Change it with 2.0.
// Read optional flags
bool secure = true;
bool bypassCSP = true;
bool allowServiceWorkers = true;
bool supportFetchAPI = true;
bool corsEnabled = true;
if (args->Length() == 2) {
mate::Dictionary options;
if (args->GetNext(&options)) {
options.Get("secure", &secure);
options.Get("bypassCSP", &bypassCSP);
options.Get("allowServiceWorkers", &allowServiceWorkers);
options.Get("supportFetchAPI", &supportFetchAPI);
options.Get("corsEnabled", &corsEnabled);
}
}
// Register scheme to privileged list (https, wss, data, chrome-extension)
WTF::String privileged_scheme(
WTF::String::FromUTF8(scheme.data(), scheme.length()));
if (bypassCSP) {
blink::SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy(
privileged_scheme);
}
if (allowServiceWorkers) {
blink::SchemeRegistry::RegisterURLSchemeAsAllowingServiceWorkers(
privileged_scheme);
}
if (supportFetchAPI) {
blink::SchemeRegistry::RegisterURLSchemeAsSupportingFetchAPI(
privileged_scheme);
}
if (corsEnabled) {
url::AddCorsEnabledScheme(scheme.c_str());
}
web_frame_->SetSpellCheckPanelHostClient(spell_check_client.get());
new AtomWebFrameObserver(render_frame, std::move(spell_check_client));
}
void WebFrame::InsertText(const std::string& text) {
@@ -500,10 +473,6 @@ void WebFrame::BuildPrototype(v8::Isolate* isolate,
&WebFrame::AllowGuestViewElementDefinition)
.SetMethod("getWebFrameId", &WebFrame::GetWebFrameId)
.SetMethod("setSpellCheckProvider", &WebFrame::SetSpellCheckProvider)
.SetMethod("registerURLSchemeAsBypassingCSP",
&WebFrame::RegisterURLSchemeAsBypassingCSP)
.SetMethod("registerURLSchemeAsPrivileged",
&WebFrame::RegisterURLSchemeAsPrivileged)
.SetMethod("insertText", &WebFrame::InsertText)
.SetMethod("insertCSS", &WebFrame::InsertCSS)
.SetMethod("executeJavaScript", &WebFrame::ExecuteJavaScript)

View File

@@ -26,8 +26,6 @@ namespace atom {
namespace api {
class SpellCheckClient;
class WebFrame : public mate::Wrappable<WebFrame> {
public:
static mate::Handle<WebFrame> Create(v8::Isolate* isolate);
@@ -59,10 +57,6 @@ class WebFrame : public mate::Wrappable<WebFrame> {
const std::string& language,
v8::Local<v8::Object> provider);
void RegisterURLSchemeAsBypassingCSP(const std::string& scheme);
void RegisterURLSchemeAsPrivileged(const std::string& scheme,
mate::Arguments* args);
// Editing.
void InsertText(const std::string& text);
void InsertCSS(const std::string& css);
@@ -97,8 +91,6 @@ class WebFrame : public mate::Wrappable<WebFrame> {
v8::Local<v8::Value> FindFrameByRoutingId(int routing_id) const;
v8::Local<v8::Value> RoutingId() const;
std::unique_ptr<SpellCheckClient> spell_check_client_;
blink::WebLocalFrame* web_frame_;
DISALLOW_COPY_AND_ASSIGN(WebFrame);

View File

@@ -163,6 +163,25 @@ void RendererClientBase::RenderThreadStarted() {
blink::SchemeRegistry::RegisterURLSchemeAsSecure(
WTF::String::FromUTF8(scheme.data(), scheme.length()));
std::vector<std::string> fetch_enabled_schemes =
ParseSchemesCLISwitch(command_line, switches::kFetchSchemes);
for (const std::string& scheme : fetch_enabled_schemes) {
blink::WebSecurityPolicy::RegisterURLSchemeAsSupportingFetchAPI(
blink::WebString::FromASCII(scheme));
}
std::vector<std::string> service_worker_schemes =
ParseSchemesCLISwitch(command_line, switches::kServiceWorkerSchemes);
for (const std::string& scheme : service_worker_schemes)
blink::WebSecurityPolicy::RegisterURLSchemeAsAllowingServiceWorkers(
blink::WebString::FromASCII(scheme));
std::vector<std::string> csp_bypassing_schemes =
ParseSchemesCLISwitch(command_line, switches::kBypassCSPSchemes);
for (const std::string& scheme : csp_bypassing_schemes)
blink::SchemeRegistry::RegisterURLSchemeAsBypassingContentSecurityPolicy(
WTF::String::FromUTF8(scheme.data(), scheme.length()));
// Allow file scheme to handle service worker by default.
// FIXME(zcbenz): Can this be moved elsewhere?
blink::WebSecurityPolicy::RegisterURLSchemeAsAllowingServiceWorkers("file");

View File

@@ -8,7 +8,7 @@ declare_args() {
# Allow running Electron as a node binary.
enable_run_as_node = true
enable_osr = false
enable_osr = true
enable_view_api = false

View File

@@ -57,6 +57,11 @@ The following `webPreferences` option default values are deprecated in favor of
Child windows opened with the `nativeWindowOpen` option will always have Node.js integration disabled.
## Privileged Schemes Registration
Renderer process APIs `webFrame.setRegisterURLSchemeAsPrivileged` and `webFrame.registerURLSchemeAsBypassingCSP` as well as browser process API `protocol.registerStandardSchemes` have been removed.
A new API, `protocol.registerSchemesAsPrivileged` has been added and should be used for registering custom schemes with the required privileges. Custom schemes are required to be registered before app ready.
# Planned Breaking API Changes (4.0)
The following list includes the breaking API changes planned for Electron 4.0.

View File

@@ -12,7 +12,6 @@ result.
**Note:** You should not use this module until the `ready` event of the app
module is emitted.
```javascript
const { app, contentTracing } = require('electron')
@@ -43,11 +42,18 @@ The `contentTracing` module has the following methods:
* `callback` Function
* `categories` String[]
Get a set of category groups. The category groups can change as new code paths
are reached.
Get a set of category groups. The category groups can change as new code paths are reached.
Once all child processes have acknowledged the `getCategories` request the `callback` is invoked with an array of category groups.
**[Deprecated Soon](promisification.md)**
### `contentTracing.getCategories()`
Returns `Promise<String[]>` - resolves with an array of category groups once all child processes have acknowledged the `getCategories` request
Get a set of category groups. The category groups can change as new code paths are reached.
Once all child processes have acknowledged the `getCategories` request the
`callback` is invoked with an array of category groups.
### `contentTracing.startRecording(options, callback)`
@@ -60,6 +66,19 @@ Recording begins immediately locally and asynchronously on child processes
as soon as they receive the EnableRecording request. The `callback` will be
called once all child processes have acknowledged the `startRecording` request.
**[Deprecated Soon](promisification.md)**
### `contentTracing.startRecording(options)`
* `options` ([TraceCategoriesAndOptions](structures/trace-categories-and-options.md) | [TraceConfig](structures/trace-config.md))
Returns `Promise<void>` - resolved once all child processes have acknowledged the `startRecording` request.
Start recording on all processes.
Recording begins immediately locally and asynchronously on child processes
as soon as they receive the EnableRecording request.
### `contentTracing.stopRecording(resultFilePath, callback)`
* `resultFilePath` String
@@ -81,47 +100,24 @@ Trace data will be written into `resultFilePath` if it is not empty or into a
temporary file. The actual file path will be passed to `callback` if it's not
`null`.
### `contentTracing.startMonitoring(options, callback)`
**[Deprecated Soon](promisification.md)**
* `options` Object
* `categoryFilter` String
* `traceOptions` String
* `callback` Function
Start monitoring on all processes.
Monitoring begins immediately locally and asynchronously on child processes as
soon as they receive the `startMonitoring` request.
Once all child processes have acknowledged the `startMonitoring` request the
`callback` will be called.
### `contentTracing.stopMonitoring(callback)`
* `callback` Function
Stop monitoring on all processes.
Once all child processes have acknowledged the `stopMonitoring` request the
`callback` is called.
### `contentTracing.captureMonitoringSnapshot(resultFilePath, callback)`
### `contentTracing.stopRecording(resultFilePath)`
* `resultFilePath` String
* `callback` Function
* `resultFilePath` String
Get the current monitoring traced data.
Returns `Promise<String>` - resolves with a file that contains the traced data once all child processes have acknowledged the `stopRecording` request
Stop recording on all processes.
Child processes typically cache trace data and only rarely flush and send
trace data back to the main process. This is because it may be an expensive
operation to send the trace data over IPC and we would like to avoid unneeded
runtime overhead from tracing. So, to end tracing, we must asynchronously ask
all child processes to flush any pending trace data.
Once all child processes have acknowledged the `captureMonitoringSnapshot`
request the `callback` will be called with a file that contains the traced data.
trace data back to the main process. This helps to minimize the runtime overhead
of tracing since sending trace data over IPC can be an expensive operation. So,
to end tracing, we must asynchronously ask all child processes to flush any
pending trace data.
Trace data will be written into `resultFilePath` if it is not empty or into a
temporary file.
### `contentTracing.getTraceBufferUsage(callback)`

View File

@@ -13,21 +13,30 @@ For example:
const { session } = require('electron')
// Query all cookies.
session.defaultSession.cookies.get({}, (error, cookies) => {
console.log(error, cookies)
})
session.defaultSession.cookies.get({})
.then((cookies) => {
console.log(cookies)
}).catch((error) => {
console.log(error)
})
// Query all cookies associated with a specific url.
session.defaultSession.cookies.get({ url: 'http://www.github.com' }, (error, cookies) => {
console.log(error, cookies)
})
session.defaultSession.cookies.get({ url: 'http://www.github.com' })
.then((cookies) => {
console.log(cookies)
}).catch((error) => {
console.log(error)
})
// Set a cookie with the given cookie data;
// may overwrite equivalent cookies if they exist.
const cookie = { url: 'http://www.github.com', name: 'dummy_name', value: 'dummy' }
session.defaultSession.cookies.set(cookie, (error) => {
if (error) console.error(error)
})
session.defaultSession.cookies.set(cookie)
.then(() => {
// success
}, (error) => {
console.error(error)
})
```
### Instance Events
@@ -55,6 +64,23 @@ expired.
The following methods are available on instances of `Cookies`:
#### `cookies.get(filter)`
* `filter` Object
* `url` String (optional) - Retrieves cookies which are associated with
`url`. Empty implies retrieving cookies of all urls.
* `name` String (optional) - Filters cookies by name.
* `domain` String (optional) - Retrieves cookies whose domains match or are
subdomains of `domains`.
* `path` String (optional) - Retrieves cookies whose path matches `path`.
* `secure` Boolean (optional) - Filters cookies by their Secure property.
* `session` Boolean (optional) - Filters out session or persistent cookies.
Returns `Promise<Cookie[]>` - A promise which resolves an array of cookie objects.
Sends a request to get all cookies matching `filter`, and resolves a promise with
the response.
#### `cookies.get(filter, callback)`
* `filter` Object
@@ -73,6 +99,28 @@ The following methods are available on instances of `Cookies`:
Sends a request to get all cookies matching `filter`, `callback` will be called
with `callback(error, cookies)` on complete.
**[Deprecated Soon](promisification.md)**
#### `cookies.set(details)`
* `details` Object
* `url` String - The url to associate the cookie with.
* `name` String (optional) - The name of the cookie. Empty by default if omitted.
* `value` String (optional) - The value of the cookie. Empty by default if omitted.
* `domain` String (optional) - The domain of the cookie. Empty by default if omitted.
* `path` String (optional) - The path of the cookie. Empty by default if omitted.
* `secure` Boolean (optional) - Whether the cookie should be marked as Secure. Defaults to
false.
* `httpOnly` Boolean (optional) - Whether the cookie should be marked as HTTP only.
Defaults to false.
* `expirationDate` Double (optional) - The expiration date of the cookie as the number of
seconds since the UNIX epoch. If omitted then the cookie becomes a session
cookie and will not be retained between sessions.
Returns `Promise<void>` - A promise which resolves when the cookie has been set
Sets a cookie with `details`.
#### `cookies.set(details, callback)`
* `details` Object
@@ -94,6 +142,17 @@ with `callback(error, cookies)` on complete.
Sets a cookie with `details`, `callback` will be called with `callback(error)`
on complete.
**[Deprecated Soon](promisification.md)**
#### `cookies.remove(url, name)`
* `url` String - The URL associated with the cookie.
* `name` String - The name of cookie to remove.
Returns `Promise<void>` - A promise which resolves when the cookie has been removed
Removes the cookies matching `url` and `name`
#### `cookies.remove(url, name, callback)`
* `url` String - The URL associated with the cookie.
@@ -103,8 +162,18 @@ on complete.
Removes the cookies matching `url` and `name`, `callback` will called with
`callback()` on complete.
**[Deprecated Soon](promisification.md)**
#### `cookies.flushStore()`
Returns `Promise<void>` - A promise which resolves when the cookie store has been flushed
Writes any unwritten cookies data to disk.
#### `cookies.flushStore(callback)`
* `callback` Function
Writes any unwritten cookies data to disk.
**[Deprecated Soon](promisification.md)**

View File

@@ -269,7 +269,7 @@ const { remote } = require('electron')
const { Menu, MenuItem } = remote
const menu = new Menu()
menu.append(new MenuItem({ label: 'MenuItem1', click() { console.log('item 1 clicked') } })))
menu.append(new MenuItem({ label: 'MenuItem1', click() { console.log('item 1 clicked') } }))
menu.append(new MenuItem({ type: 'separator' }))
menu.append(new MenuItem({ label: 'MenuItem2', type: 'checkbox', checked: true }))

View File

@@ -11,17 +11,7 @@ When a majority of affected functions are migrated, this flag will be enabled by
- [app.importCertificate(options, callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#importCertificate)
- [request.write(chunk[, encoding][, callback])](https://github.com/electron/electron/blob/master/docs/api/client-request.md#write)
- [request.end([chunk][, encoding][, callback])](https://github.com/electron/electron/blob/master/docs/api/client-request.md#end)
- [contentTracing.getCategories(callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#getCategories)
- [contentTracing.startRecording(options, callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#startRecording)
- [contentTracing.stopRecording(resultFilePath, callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#stopRecording)
- [contentTracing.startMonitoring(options, callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#startMonitoring)
- [contentTracing.stopMonitoring(callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#stopMonitoring)
- [contentTracing.captureMonitoringSnapshot(resultFilePath, callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#captureMonitoringSnapshot)
- [contentTracing.getTraceBufferUsage(callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#getTraceBufferUsage)
- [cookies.get(filter, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#get)
- [cookies.set(details, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#set)
- [cookies.remove(url, name, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#remove)
- [cookies.flushStore(callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#flushStore)
- [debugger.sendCommand(method[, commandParams, callback])](https://github.com/electron/electron/blob/master/docs/api/debugger.md#sendCommand)
- [dialog.showOpenDialog([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showOpenDialog)
- [dialog.showSaveDialog([browserWindow, ]options[, callback])](https://github.com/electron/electron/blob/master/docs/api/dialog.md#showSaveDialog)
@@ -52,10 +42,17 @@ When a majority of affected functions are migrated, this flag will be enabled by
### Converted Functions
- [win.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/browser-window.md#capturePage)
- [webviewTag.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/webview-tag.md#capturePage)
- [contents.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#capturePage)
- [app.getFileIcon(path[, options], callback)](https://github.com/electron/electron/blob/master/docs/api/app.md#getFileIcon)
- [shell.openExternal(url[, options, callback])](https://github.com/electron/electron/blob/master/docs/api/shell.md#openExternal)
- [contents.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/web-contents.md#capturePage)
- [contentTracing.getCategories(callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#getCategories)
- [contentTracing.startRecording(options, callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#startRecording)
- [contentTracing.stopRecording(resultFilePath, callback)](https://github.com/electron/electron/blob/master/docs/api/content-tracing.md#stopRecording)
- [cookies.flushStore(callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#flushStore)
- [cookies.get(filter, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#get)
- [cookies.remove(url, name, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#remove)
- [cookies.set(details, callback)](https://github.com/electron/electron/blob/master/docs/api/cookies.md#set)
- [desktopCapturer.getSources(options, callback)](https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md#getSources)
- [protocol.isProtocolHandled(scheme, callback)](https://github.com/electron/electron/blob/master/docs/api/protocol.md#isProtocolHandled)
- [desktopCapturer.getSources(options, callback)](https://github.com/electron/electron/blob/master/docs/api/desktop-capturer.md#getSources)
- [shell.openExternal(url[, options, callback])](https://github.com/electron/electron/blob/master/docs/api/shell.md#openExternal)
- [webviewTag.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/webview-tag.md#capturePage)
- [win.capturePage([rect, ]callback)](https://github.com/electron/electron/blob/master/docs/api/browser-window.md#capturePage)

View File

@@ -28,12 +28,26 @@ of the `app` module gets emitted.
The `protocol` module has the following methods:
### `protocol.registerStandardSchemes(schemes[, options])`
### `protocol.registerSchemesAsPrivileged(customSchemes)`
* `schemes` String[] - Custom schemes to be registered as standard schemes.
* `options` Object (optional)
* `secure` Boolean (optional) - `true` to register the scheme as secure.
Default `false`.
* `customSchemes` [CustomScheme[]](structures/custom-scheme.md)
**Note:** This method can only be used before the `ready` event of the `app`
module gets emitted and can be called only once.
Registers the `scheme` as standard, secure, bypasses content security policy for resources,
allows registering ServiceWorker and supports fetch API.
Specify a privilege with the value of `true` to enable the capability.
An example of registering a privileged scheme, with bypassing Content Security Policy:
```javascript
const { protocol } = require('electron')
protocol.registerSchemesAsPrivileged([
{ scheme: 'foo', privileges: { bypassCSP: true } }
])
```
A standard scheme adheres to what RFC 3986 calls [generic URI
syntax](https://tools.ietf.org/html/rfc3986#section-3). For example `http` and
@@ -59,23 +73,7 @@ error for the scheme.
By default web storage apis (localStorage, sessionStorage, webSQL, indexedDB, cookies)
are disabled for non standard schemes. So in general if you want to register a
custom protocol to replace the `http` protocol, you have to register it as a standard scheme:
```javascript
const { app, protocol } = require('electron')
protocol.registerStandardSchemes(['atom'])
app.on('ready', () => {
protocol.registerHttpProtocol('atom', '...')
})
```
**Note:** This method can only be used before the `ready` event of the `app`
module gets emitted.
### `protocol.registerServiceWorkerSchemes(schemes)`
* `schemes` String[] - Custom schemes to be registered to handle service workers.
custom protocol to replace the `http` protocol, you have to register it as a standard scheme.
### `protocol.registerFileProtocol(scheme, handler[, completion])`

View File

@@ -0,0 +1,10 @@
# CustomScheme Object
* `scheme` String - Custom schemes to be registered with options.
* `privileges` Object (optional)
* `standard` Boolean (optional) - Default false.
* `secure` Boolean (optional) - Default false.
* `bypassCSP` Boolean (optional) - Default false.
* `allowServiceWorkers` Boolean (optional) - Default false.
* `supportFetchAPI` Boolean (optional) - Default false.
* `corsEnabled` Boolean (optional) - Default false.

View File

@@ -95,34 +95,6 @@ webFrame.setSpellCheckProvider('en-US', {
})
```
### `webFrame.registerURLSchemeAsBypassingCSP(scheme)`
* `scheme` String
Resources will be loaded from this `scheme` regardless of the current page's
Content Security Policy.
### `webFrame.registerURLSchemeAsPrivileged(scheme[, options])`
* `scheme` String
* `options` Object (optional)
* `secure` Boolean (optional) - Default true.
* `bypassCSP` Boolean (optional) - Default true.
* `allowServiceWorkers` Boolean (optional) - Default true.
* `supportFetchAPI` Boolean (optional) - Default true.
* `corsEnabled` Boolean (optional) - Default true.
Registers the `scheme` as secure, bypasses content security policy for resources,
allows registering ServiceWorker and supports fetch API.
Specify an option with the value of `false` to omit it from the registration.
An example of registering a privileged scheme, without bypassing Content Security Policy:
```javascript
const { webFrame } = require('electron')
webFrame.registerURLSchemeAsPrivileged('foo', { bypassCSP: false })
```
### `webFrame.insertText(text)`
* `text` String

View File

@@ -32,7 +32,7 @@ const filter = {
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
details.requestHeaders['User-Agent'] = 'MyAgent'
callback({ cancel: false, requestHeaders: details.requestHeaders })
callback({ requestHeaders: details.requestHeaders })
})
```
@@ -130,7 +130,7 @@ response are visible by the time this listener is fired.
* `responseHeaders` Object
* `callback` Function
* `response` Object
* `cancel` Boolean
* `cancel` Boolean (optional)
* `responseHeaders` Object (optional) - When provided, the server is assumed
to have responded with these headers.
* `statusLine` String (optional) - Should be provided when overriding
@@ -200,6 +200,7 @@ redirect is about to occur.
* `method` String
* `webContentsId` Integer (optional)
* `resourceType` String
* `referrer` String
* `timestamp` Double
* `responseHeaders` Object
* `fromCache` Boolean

View File

@@ -36,7 +36,7 @@ Object.assign(app, {
}
})
app.getFileIcon = deprecate.promisify(app.getFileIcon, 3)
app.getFileIcon = deprecate.promisify(app.getFileIcon)
const nativeAppMetrics = app.getAppMetrics
app.getAppMetrics = () => {

View File

@@ -1,3 +1,9 @@
'use strict'
const { deprecate } = require('electron')
const contentTracing = process.atomBinding('content_tracing')
module.exports = process.atomBinding('content_tracing')
contentTracing.startRecording = deprecate.promisify(contentTracing.startRecording)
contentTracing.stopRecording = deprecate.promisify(contentTracing.stopRecording)
contentTracing.getCategories = deprecate.promisify(contentTracing.getCategories)
module.exports = contentTracing

View File

@@ -2,7 +2,22 @@
const { EventEmitter } = require('events')
const { app, deprecate } = require('electron')
const { fromPartition, Session, Cookies } = process.atomBinding('session')
const { Session, Cookies } = process.atomBinding('session')
const realFromPartition = process.atomBinding('session').fromPartition
const wrappedSymbol = Symbol('wrapped-deprecate')
const fromPartition = (partition) => {
const session = realFromPartition(partition)
if (!session[wrappedSymbol]) {
session[wrappedSymbol] = true
const { cookies } = session
cookies.flushStore = deprecate.promisify(cookies.flushStore)
cookies.get = deprecate.promisify(cookies.get)
cookies.remove = deprecate.promisify(cookies.remove)
cookies.set = deprecate.promisify(cookies.set)
}
return session
}
// Public API.
Object.defineProperties(exports, {
@@ -20,6 +35,6 @@ Object.setPrototypeOf(Session.prototype, EventEmitter.prototype)
Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype)
Session.prototype._init = function () {
this.protocol.isProtocolHandled = deprecate.promisify(this.protocol.isProtocolHandled, 2)
this.protocol.isProtocolHandled = deprecate.promisify(this.protocol.isProtocolHandled)
app.emit('session-created', this)
}

View File

@@ -358,6 +358,17 @@ const addReplyInternalToEvent = (event) => {
})
}
const safeProtocols = new Set([
'chrome-devtools:',
'chrome-extension:'
])
const isWebContentsTrusted = function (contents) {
const pageURL = contents._getURL()
const { protocol } = url.parse(pageURL)
return safeProtocols.has(protocol)
}
// Add JavaScript wrappers for WebContents class.
WebContents.prototype._init = function () {
// The navigation controller.
@@ -367,7 +378,7 @@ WebContents.prototype._init = function () {
// render-view-deleted event, so ignore the listeners warning.
this.setMaxListeners(0)
this.capturePage = deprecate.promisify(this.capturePage, 2)
this.capturePage = deprecate.promisify(this.capturePage)
// Dispatch IPC messages to the ipc module.
this.on('-ipc-message', function (event, [channel, ...args]) {
@@ -428,7 +439,9 @@ WebContents.prototype._init = function () {
for (const eventName of forwardedEvents) {
this.on(eventName, (event, ...args) => {
app.emit(eventName, event, this, ...args)
if (!isWebContentsTrusted(event.sender)) {
app.emit(eventName, event, this, ...args)
}
})
}
@@ -441,7 +454,7 @@ WebContents.prototype._init = function () {
})
// Handle window.open for BrowserWindow and BrowserView.
if (['browserview', 'window'].includes(this.getType())) {
if (['browserView', 'window'].includes(this.getType())) {
// Make new windows requested by links behave like "window.open".
this.on('-new-window', (event, url, frameName, disposition,
additionalFeatures, postData,

View File

@@ -534,8 +534,7 @@ ipcMain.on('ELECTRON_BROWSER_CLIPBOARD_WRITE_FIND_TEXT', function (event, text)
setReturnValue(event, () => electron.clipboard.writeFindText(text))
})
ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
const preloadPath = event.sender._getPreloadPath()
const getPreloadScript = function (preloadPath) {
let preloadSrc = null
let preloadError = null
if (preloadPath) {
@@ -545,10 +544,17 @@ ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
preloadError = errorUtils.serialize(err)
}
}
return { preloadPath, preloadSrc, preloadError }
}
ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
const preloadPaths = [
...(event.sender.session ? event.sender.session.getPreloads() : []),
event.sender._getPreloadPath()
]
event.returnValue = {
preloadPath,
preloadSrc,
preloadError,
preloadScripts: preloadPaths.map(path => getPreloadScript(path)),
isRemoteModuleEnabled: event.sender._isRemoteModuleEnabled(),
isWebViewTagEnabled: guestViewManager.isWebViewTagEnabled(event.sender),
process: {

View File

@@ -69,27 +69,27 @@ const deprecate = {
})
},
promisify: (fn, cbParamIndex) => {
promisify: (fn) => {
const fnName = fn.name || 'function'
const oldName = `${fnName} with callbacks`
const newName = `${fnName} with Promises`
const warn = warnOnce(oldName, newName)
return function (...params) {
const cb = params.splice(cbParamIndex, 1)[0]
let cb
if (params.length > 0 && typeof params[params.length - 1] === 'function') {
cb = params.pop()
}
const promise = fn.apply(this, params)
if (typeof cb !== 'function') return promise
if (!cb) return promise
if (process.enablePromiseAPIs) warn()
return promise
.then(res => {
process.nextTick(() => {
cb(null, res)
cb.length === 2 ? cb(null, res) : cb(res)
})
}, err => {
process.nextTick(() => {
cb(err)
})
process.nextTick(() => cb(err))
})
}
},

View File

@@ -56,4 +56,4 @@ const getSources = (options) => {
})
}
exports.getSources = deprecate.promisify(getSources, 1)
exports.getSources = deprecate.promisify(getSources)

View File

@@ -17,7 +17,7 @@ function handleFocusBlur (guestInstanceId) {
}
module.exports = function (contextIsolation, webviewTag, guestInstanceId) {
// Load webview tag implementation.
// Don't allow recursive `<webview>`.
if (webviewTag && guestInstanceId == null) {
const webViewImpl = require('@electron/internal/renderer/web-view/web-view-impl')
if (contextIsolation) {

View File

@@ -29,7 +29,7 @@ Object.setPrototypeOf(process, EventEmitter.prototype)
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const {
preloadPath, preloadSrc, preloadError, isRemoteModuleEnabled, isWebViewTagEnabled, process: processProps
preloadScripts, isRemoteModuleEnabled, isWebViewTagEnabled, process: processProps
} = ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD')
process.isRemoteModuleEnabled = isRemoteModuleEnabled
@@ -127,7 +127,9 @@ switch (window.location.protocol) {
const guestInstanceId = binding.guestInstanceId && parseInt(binding.guestInstanceId)
// Load webview tag implementation.
require('@electron/internal/renderer/web-view/web-view-init')(contextIsolation, isWebViewTagEnabled, guestInstanceId)
if (process.isMainFrame) {
require('@electron/internal/renderer/web-view/web-view-init')(contextIsolation, isWebViewTagEnabled, guestInstanceId)
}
const errorUtils = require('@electron/internal/common/error-utils')
@@ -162,18 +164,22 @@ function runPreloadScript (preloadSrc) {
preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate, clearImmediate)
}
try {
if (preloadSrc) {
runPreloadScript(preloadSrc)
} else if (preloadError) {
throw errorUtils.deserialize(preloadError)
}
} catch (error) {
console.error(`Unable to load preload script: ${preloadPath}`)
console.error(`${error}`)
for (const { preloadPath, preloadSrc, preloadError } of preloadScripts) {
try {
if (preloadSrc) {
runPreloadScript(preloadSrc)
} else if (preloadError) {
throw errorUtils.deserialize(preloadError)
}
} catch (error) {
console.error(`Unable to load preload script: ${preloadPath}`)
console.error(`${error}`)
ipcRenderer.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadPath, errorUtils.serialize(error))
ipcRenderer.send('ELECTRON_BROWSER_PRELOAD_ERROR', preloadPath, errorUtils.serialize(error))
}
}
// Warn about security issues
require('@electron/internal/renderer/security-warnings')()
if (process.isMainFrame) {
require('@electron/internal/renderer/security-warnings')()
}

27
package-lock.json generated
View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "5.0.0-nightly.20190122",
"version": "5.0.0-beta.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@@ -2951,9 +2951,9 @@
}
},
"electron-typescript-definitions": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/electron-typescript-definitions/-/electron-typescript-definitions-4.0.0.tgz",
"integrity": "sha512-UekaKgK8omivfj37xs1G09j5MctHCFSyPYvW67j30xYHVCvgjDLur1Vqd7CaaJCDuaYLsHjsmms4QjG8uOFb4A==",
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/electron-typescript-definitions/-/electron-typescript-definitions-6.0.0.tgz",
"integrity": "sha512-W2CHJ1bUtbDXXzIAEbQ+s2XewpSdUUqNECJmhniV0GbPoIhwjAg90++BkMxH6wPBxHWOxesvUMipUEeJN9+wlQ==",
"dev": true,
"requires": {
"@types/node": "^7.0.18",
@@ -4384,7 +4384,8 @@
"version": "2.1.1",
"resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@@ -4849,7 +4850,8 @@
"version": "5.1.1",
"resolved": false,
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"dev": true
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@@ -4913,6 +4915,7 @@
"resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@@ -4961,13 +4964,15 @@
"version": "1.0.2",
"resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true
"dev": true,
"optional": true
},
"yallist": {
"version": "3.0.2",
"resolved": false,
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
"dev": true
"dev": true,
"optional": true
}
}
},
@@ -13104,9 +13109,9 @@
}
},
"write-file-atomic": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz",
"integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==",
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz",
"integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==",
"dev": true,
"requires": {
"graceful-fs": "^4.1.11",

View File

@@ -1,6 +1,6 @@
{
"name": "electron",
"version": "5.0.0-nightly.20190122",
"version": "5.0.0-beta.2",
"repository": "https://github.com/electron/electron",
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
"devDependencies": {
@@ -16,7 +16,7 @@
"dotenv-safe": "^4.0.4",
"dugite": "^1.45.0",
"electron-docs-linter": "^2.4.0",
"electron-typescript-definitions": "^4.0.0",
"electron-typescript-definitions": "^6.0.0",
"eslint": "^5.6.0",
"eslint-config-standard": "^12.0.0",
"eslint-plugin-mocha": "^5.2.0",

View File

@@ -1,2 +1,4 @@
add_ec_group_order_bits_for_openssl_compatibility.patch
add_ec_key_key2buf_for_openssl_compatibility.patch
expose_ripemd160.patch
expose_aes-cfb.patch

View File

@@ -0,0 +1,84 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Fri, 18 Jan 2019 14:23:28 -0800
Subject: expose aes-{128,256}-cfb
diff --git a/crypto/cipher_extra/cipher_extra.c b/crypto/cipher_extra/cipher_extra.c
index 1b23ad32f8cff2a00512ba58d24b47b628e7920c..be7ef07b2c188a76890deb0f305cf92fcc57a64e 100644
--- a/crypto/cipher_extra/cipher_extra.c
+++ b/crypto/cipher_extra/cipher_extra.c
@@ -101,10 +101,14 @@ const EVP_CIPHER *EVP_get_cipherbyname(const char *name) {
return EVP_des_ede3_cbc();
} else if (OPENSSL_strcasecmp(name, "aes-128-cbc") == 0) {
return EVP_aes_128_cbc();
+ } else if (OPENSSL_strcasecmp(name, "aes-128-cfb") == 0) {
+ return EVP_aes_128_cfb128();
} else if (OPENSSL_strcasecmp(name, "aes-192-cbc") == 0) {
return EVP_aes_192_cbc();
} else if (OPENSSL_strcasecmp(name, "aes-256-cbc") == 0) {
return EVP_aes_256_cbc();
+ } else if (OPENSSL_strcasecmp(name, "aes-256-cfb") == 0) {
+ return EVP_aes_256_cfb128();
} else if (OPENSSL_strcasecmp(name, "aes-128-ctr") == 0) {
return EVP_aes_128_ctr();
} else if (OPENSSL_strcasecmp(name, "aes-192-ctr") == 0) {
diff --git a/decrepit/cfb/cfb.c b/decrepit/cfb/cfb.c
index d3a176163303a202baeb1f95727c6ed3525439d6..21d108a7b73d454aa6b0e324df4b67088d60302a 100644
--- a/decrepit/cfb/cfb.c
+++ b/decrepit/cfb/cfb.c
@@ -57,4 +57,12 @@ static const EVP_CIPHER aes_128_cfb128 = {
NULL /* cleanup */, NULL /* ctrl */,
};
+static const EVP_CIPHER aes_256_cfb128 = {
+ NID_aes_128_cfb128, 1 /* block_size */, 32 /* key_size */,
+ 16 /* iv_len */, sizeof(EVP_CFB_CTX), EVP_CIPH_CFB_MODE,
+ NULL /* app_data */, aes_cfb_init_key, aes_cfb128_cipher,
+ NULL /* cleanup */, NULL /* ctrl */,
+};
+
const EVP_CIPHER *EVP_aes_128_cfb128(void) { return &aes_128_cfb128; }
+const EVP_CIPHER *EVP_aes_256_cfb128(void) { return &aes_256_cfb128; }
diff --git a/decrepit/evp/evp_do_all.c b/decrepit/evp/evp_do_all.c
index acc4719b7e9c4c4461fc6142f2ae9156b407915b..8b008a401ec2f2d0673f6876609dd5786cace4c2 100644
--- a/decrepit/evp/evp_do_all.c
+++ b/decrepit/evp/evp_do_all.c
@@ -20,10 +20,12 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
const char *unused, void *arg),
void *arg) {
callback(EVP_aes_128_cbc(), "AES-128-CBC", NULL, arg);
+ callback(EVP_aes_128_cfb128(), "AES-128-CFB", NULL, arg);
callback(EVP_aes_128_ctr(), "AES-128-CTR", NULL, arg);
callback(EVP_aes_128_ecb(), "AES-128-ECB", NULL, arg);
callback(EVP_aes_128_ofb(), "AES-128-OFB", NULL, arg);
callback(EVP_aes_256_cbc(), "AES-256-CBC", NULL, arg);
+ callback(EVP_aes_256_cfb128(), "AES-256-CFB", NULL, arg);
callback(EVP_aes_256_ctr(), "AES-256-CTR", NULL, arg);
callback(EVP_aes_256_ecb(), "AES-256-ECB", NULL, arg);
callback(EVP_aes_256_ofb(), "AES-256-OFB", NULL, arg);
@@ -38,10 +40,12 @@ void EVP_CIPHER_do_all_sorted(void (*callback)(const EVP_CIPHER *cipher,
// OpenSSL returns everything twice, the second time in lower case.
callback(EVP_aes_128_cbc(), "aes-128-cbc", NULL, arg);
+ callback(EVP_aes_128_cfb128(), "aes-128-cfb", NULL, arg);
callback(EVP_aes_128_ctr(), "aes-128-ctr", NULL, arg);
callback(EVP_aes_128_ecb(), "aes-128-ecb", NULL, arg);
callback(EVP_aes_128_ofb(), "aes-128-ofb", NULL, arg);
callback(EVP_aes_256_cbc(), "aes-256-cbc", NULL, arg);
+ callback(EVP_aes_256_cfb128(), "aes-256-cfb", NULL, arg);
callback(EVP_aes_256_ctr(), "aes-256-ctr", NULL, arg);
callback(EVP_aes_256_ecb(), "aes-256-ecb", NULL, arg);
callback(EVP_aes_256_ofb(), "aes-256-ofb", NULL, arg);
diff --git a/include/openssl/cipher.h b/include/openssl/cipher.h
index 59634138cb60237f008eb99e7d8df54da7629c1a..b30b8434b301fb5b8630ae954698b6fee255df77 100644
--- a/include/openssl/cipher.h
+++ b/include/openssl/cipher.h
@@ -421,6 +421,7 @@ OPENSSL_EXPORT const EVP_CIPHER *EVP_aes_192_ofb(void);
// EVP_aes_128_cfb128 is only available in decrepit.
OPENSSL_EXPORT const EVP_CIPHER *EVP_aes_128_cfb128(void);
+OPENSSL_EXPORT const EVP_CIPHER *EVP_aes_256_cfb128(void);
// The following flags do nothing and are included only to make it easier to
// compile code with BoringSSL.

View File

@@ -0,0 +1,95 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Fri, 18 Jan 2019 13:56:52 -0800
Subject: expose ripemd160
This adds references to the decrepit/ module from non-decrepit source,
which is not allowed in upstream. Until upstream has a way to interface
with node.js that allows exposing additional digests without patching,
this patch is required to provide ripemd160 support in the nodejs crypto
module.
diff --git a/crypto/digest_extra/digest_extra.c b/crypto/digest_extra/digest_extra.c
index 4b4bb38135e6089eaf6f47afda0199567a2397ef..43b7eca808b82a032055f56ce726ce4f38c5f2c5 100644
--- a/crypto/digest_extra/digest_extra.c
+++ b/crypto/digest_extra/digest_extra.c
@@ -81,6 +81,7 @@ static const struct nid_to_digest nid_to_digest_mapping[] = {
{NID_sha384, EVP_sha384, SN_sha384, LN_sha384},
{NID_sha512, EVP_sha512, SN_sha512, LN_sha512},
{NID_md5_sha1, EVP_md5_sha1, SN_md5_sha1, LN_md5_sha1},
+ {NID_ripemd160, EVP_ripemd160, SN_ripemd160, LN_ripemd160},
// As a remnant of signing |EVP_MD|s, OpenSSL returned the corresponding
// hash function when given a signature OID. To avoid unintended lax parsing
// of hash OIDs, this is no longer supported for lookup by OID or NID.
diff --git a/crypto/fipsmodule/digest/digests.c b/crypto/fipsmodule/digest/digests.c
index f2fa349c2b32ae88766624af3109ece4b1d69909..bcaed59c5401bef071acba9b9919d9069e3ccd4d 100644
--- a/crypto/fipsmodule/digest/digests.c
+++ b/crypto/fipsmodule/digest/digests.c
@@ -63,6 +63,7 @@
#include <openssl/md5.h>
#include <openssl/nid.h>
#include <openssl/sha.h>
+#include <openssl/ripemd.h>
#include "internal.h"
#include "../delocate.h"
@@ -277,4 +278,27 @@ DEFINE_METHOD_FUNCTION(EVP_MD, EVP_md5_sha1) {
out->ctx_size = sizeof(MD5_SHA1_CTX);
}
+static void ripemd160_init(EVP_MD_CTX *ctx) {
+ CHECK(RIPEMD160_Init(ctx->md_data));
+}
+
+static void ripemd160_update(EVP_MD_CTX *ctx, const void *data, size_t count) {
+ CHECK(RIPEMD160_Update(ctx->md_data, data, count));
+}
+
+static void ripemd160_final(EVP_MD_CTX *ctx, uint8_t *md) {
+ CHECK(RIPEMD160_Final(md, ctx->md_data));
+}
+
+DEFINE_METHOD_FUNCTION(EVP_MD, EVP_ripemd160) {
+ out->type = NID_ripemd160;
+ out->md_size = RIPEMD160_DIGEST_LENGTH;
+ out->flags = 0;
+ out->init = ripemd160_init;
+ out->update = ripemd160_update;
+ out->final = ripemd160_final;
+ out->block_size = 64;
+ out->ctx_size = sizeof(RIPEMD160_CTX);
+}
+
#undef CHECK
diff --git a/decrepit/evp/evp_do_all.c b/decrepit/evp/evp_do_all.c
index 38b8f9f78f76050174096740596ac59a0fe18757..acc4719b7e9c4c4461fc6142f2ae9156b407915b 100644
--- a/decrepit/evp/evp_do_all.c
+++ b/decrepit/evp/evp_do_all.c
@@ -66,6 +66,7 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher,
callback(EVP_sha256(), "SHA256", NULL, arg);
callback(EVP_sha384(), "SHA384", NULL, arg);
callback(EVP_sha512(), "SHA512", NULL, arg);
+ callback(EVP_ripemd160(), "RIPEMD160", NULL, arg);
callback(EVP_md4(), "md4", NULL, arg);
callback(EVP_md5(), "md5", NULL, arg);
@@ -74,4 +75,5 @@ void EVP_MD_do_all_sorted(void (*callback)(const EVP_MD *cipher,
callback(EVP_sha256(), "sha256", NULL, arg);
callback(EVP_sha384(), "sha384", NULL, arg);
callback(EVP_sha512(), "sha512", NULL, arg);
+ callback(EVP_ripemd160(), "ripemd160", NULL, arg);
}
diff --git a/include/openssl/digest.h b/include/openssl/digest.h
index 1a1ca29732afae317c8e8740c629e8922fc83093..48ebdd1eb93b3febecddbc2545b7aae583f21525 100644
--- a/include/openssl/digest.h
+++ b/include/openssl/digest.h
@@ -88,6 +88,9 @@ OPENSSL_EXPORT const EVP_MD *EVP_sha512(void);
// MD5 and SHA-1, as used in TLS 1.1 and below.
OPENSSL_EXPORT const EVP_MD *EVP_md5_sha1(void);
+// EVP_ripemd160 is in decrepit and not available by default.
+OPENSSL_EXPORT const EVP_MD *EVP_ripemd160(void);
+
// EVP_get_digestbynid returns an |EVP_MD| for the given NID, or NULL if no
// such digest is known.
OPENSSL_EXPORT const EVP_MD *EVP_get_digestbynid(int nid);

View File

@@ -1,16 +1,15 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Thu, 20 Sep 2018 17:48:05 -0700
Subject: boringssl_build_gn.patch
Subject: boringssl BUILD.gn
Build BoringSSL with some extra functions that nodejs needs. Only affects
the GN build; with the GYP build, nodejs is still built with OpenSSL.
Build BoringSSL with some extra functions that nodejs needs.
diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn
index 6e4fc85f124ed6dd4a7ac1812686fa59c4e50cdf..2fbd44c0ac71bbd84706ef50dd0d98b2466d4295 100644
index 6e4fc85f124ed6dd4a7ac1812686fa59c4e50cdf..fd45cfcb50fb659ff8d5a07b06aeecc8f0ecd3ee 100644
--- a/third_party/boringssl/BUILD.gn
+++ b/third_party/boringssl/BUILD.gn
@@ -45,6 +45,13 @@ config("no_asm_config") {
@@ -45,6 +45,19 @@ config("no_asm_config") {
all_sources = crypto_sources + ssl_sources
all_headers = crypto_headers + ssl_headers
@@ -20,6 +19,12 @@ index 6e4fc85f124ed6dd4a7ac1812686fa59c4e50cdf..2fbd44c0ac71bbd84706ef50dd0d98b2
+ "src/decrepit/evp/evp_do_all.c",
+ "src/decrepit/xts/xts.c",
+ ]
+
+ all_sources += [
+ "src/decrepit/ripemd/internal.h",
+ "src/decrepit/ripemd/ripemd.c",
+ "src/decrepit/cfb/cfb.c",
+ ]
+}
# Windows' assembly is built with NASM. The other platforms use the platform

View File

@@ -47,7 +47,7 @@ async function nextBeta (v) {
tags.sort((t1, t2) => semver.gt(t1, t2))
// increment the latest existing beta tag or start at beta.1 if it's a new beta line
return tags.length === 0 ? semver.inc(next, 'beta', 'prerelease') : semver.inc(tags.pop(), 'prerelease')
return tags.length === 0 ? `${next}-beta.1` : semver.inc(tags.pop(), 'prerelease')
}
async function getElectronVersion () {

View File

@@ -63,6 +63,35 @@ const setPullRequest = (commit, owner, repo, number) => {
}
}
const getNoteFromClerk = async (number, owner, repo) => {
const comments = await getComments(number, owner, repo)
if (!comments && !comments.data) {
return
}
const CLERK_LOGIN = 'release-clerk[bot]'
const PERSIST_LEAD = '**Release Notes Persisted**\n\n'
const QUOTE_LEAD = '> '
for (const comment of comments.data.reverse()) {
if (comment.user.login !== CLERK_LOGIN) {
continue
}
if (!comment.body.startsWith(PERSIST_LEAD)) {
continue
}
const note = comment.body
.slice(PERSIST_LEAD.length).trim() // remove PERSIST_LEAD
.split('\r?\n') // break into lines
.map(line => line.trim())
.filter(line => line.startsWith(QUOTE_LEAD)) // notes are quoted
.map(line => line.slice(QUOTE_LEAD.length)) // unquote the lines
.join(' ') // join the note lines
.trim()
return note
}
}
// copied from https://github.com/electron/clerk/blob/master/src/index.ts#L4-L13
const OMIT_FROM_RELEASE_NOTES_KEYS = [
'no-notes',
@@ -81,10 +110,12 @@ const getNoteFromBody = body => {
}
const NOTE_PREFIX = 'Notes: '
const NOTE_HEADER = '#### Release Notes'
let note = body
.split(/\r?\n\r?\n/) // split into paragraphs
.map(paragraph => paragraph.trim())
.map(paragraph => paragraph.startsWith(NOTE_HEADER) ? paragraph.slice(NOTE_HEADER.length).trim() : paragraph)
.find(paragraph => paragraph.startsWith(NOTE_PREFIX))
if (note) {
@@ -185,9 +216,9 @@ const parseCommitMessage = (commitMessage, owner, repo, commit = {}) => {
// https://www.conventionalcommits.org/en
if (commitMessage
.split(/\r?\n\r?\n/) // split into paragraphs
.map(paragraph => paragraph.trim())
.some(paragraph => paragraph.startsWith('BREAKING CHANGE'))) {
.split(/\r?\n/) // split into lines
.map(line => line.trim())
.some(line => line.startsWith('BREAKING CHANGE'))) {
commit.type = 'breaking-change'
}
@@ -295,6 +326,22 @@ const getPullRequest = async (number, owner, repo) => {
})
}
const getComments = async (number, owner, repo) => {
const name = `${owner}-${repo}-pull-${number}-comments`
return checkCache(name, async () => {
try {
return await octokit.issues.listComments({ number, owner, repo, per_page: 100 })
} catch (error) {
// Silently eat 404s.
// We can get a bad pull number if someone manually lists
// an issue number in PR number notation, e.g. 'fix: foo (#123)'
if (error.code !== 404) {
throw error
}
}
})
}
const addRepoToPool = async (pool, repo, from, to) => {
const commonAncestor = await getCommonAncestor(repo.dir, from, to)
const oldHashes = await getLocalCommitHashes(repo.dir, from)
@@ -393,6 +440,28 @@ const getDependencyCommits = async (pool, from, to) => {
: getDependencyCommitsGN(pool, from, to)
}
// Changes are interesting if they make a change relative to a previous
// release in the same series. For example if you fix a Y.0.0 bug, that
// should be included in the Y.0.1 notes even if it's also tropped back
// to X.0.1.
//
// The phrase 'previous release' is important: if this is the first
// prerelease or first stable release in a series, we omit previous
// branches' changes. Otherwise we will have an overwhelmingly long
// list of mostly-irrelevant changes.
const shouldIncludeMultibranchChanges = (version) => {
let show = true
if (semver.valid(version)) {
const prerelease = semver.prerelease(version)
show = prerelease
? parseInt(prerelease.pop()) > 1
: semver.patch(version) > 0
}
return show
}
/***
**** Main
***/
@@ -445,8 +514,19 @@ const getNotes = async (fromRef, toRef, newVersion) => {
// scrape PRs for release note 'Notes:' comments
for (const commit of pool.commits) {
let pr = commit.pr
let prSubject
while (pr && !commit.note) {
const note = await getNoteFromClerk(pr.number, pr.owner, pr.repo)
if (note) {
commit.note = note
}
// if we already have all the data we need, stop scraping the PRs
if (commit.note && commit.type && prSubject) {
break
}
const prData = await getPullRequest(pr.number, pr.owner, pr.repo)
if (!prData || !prData.data) {
break
@@ -472,9 +552,7 @@ const getNotes = async (fromRef, toRef, newVersion) => {
.filter(commit => commit.note !== NO_NOTES)
.filter(commit => !((commit.note || commit.subject).match(/^[Bb]ump v\d+\.\d+\.\d+/)))
// if this is a stable release,
// remove notes for changes that already landed in a previous major/minor series
if (semver.valid(newVersion) && !semver.prerelease(newVersion)) {
if (!shouldIncludeMultibranchChanges(newVersion)) {
// load all the prDatas
await Promise.all(
pool.commits.map(commit => new Promise(async (resolve) => {

View File

@@ -836,7 +836,7 @@ describe('app module', () => {
})
})
describe('getFileIcon() API', (done) => {
describe('getFileIcon() API', () => {
const iconPath = path.join(__dirname, 'fixtures/assets/icon.ico')
const sizes = {
small: 16,
@@ -859,7 +859,7 @@ describe('app module', () => {
})
// TODO(codebytere): remove when promisification is complete
it('fetches a non-empty icon (callback)', () => {
it('fetches a non-empty icon (callback)', (done) => {
app.getFileIcon(iconPath, (icon) => {
expect(icon.isEmpty()).to.be.false()
done()
@@ -875,7 +875,7 @@ describe('app module', () => {
})
// TODO(codebytere): remove when promisification is complete
it('fetches normal icon size by default (callback)', () => {
it('fetches normal icon size by default (callback)', (done) => {
app.getFileIcon(iconPath, (icon) => {
const size = icon.getSize()
@@ -903,7 +903,7 @@ describe('app module', () => {
})
// TODO(codebytere): remove when promisification is complete
it('fetches a normal icon (callback)', () => {
it('fetches a normal icon (callback)', (done) => {
app.getFileIcon(iconPath, { size: 'normal' }, (icon) => {
const size = icon.getSize()

View File

@@ -507,7 +507,7 @@ describe('BrowserWindow module', () => {
})
// TODO(codebytere): remove when promisification is complete
it('returns a Promise with a Buffer (callback)', () => {
it('returns a Promise with a Buffer (callback)', (done) => {
w.capturePage({
x: 0,
y: 0,
@@ -540,24 +540,25 @@ describe('BrowserWindow module', () => {
})
// TODO(codebytere): remove when promisification is complete
it('preserves transparency (callback)', async () => {
const w = await openTheWindow({
it('preserves transparency (callback)', (done) => {
openTheWindow({
show: false,
width: 400,
height: 400,
transparent: true
})
const p = emittedOnce(w, 'ready-to-show')
w.loadURL('data:text/html,<html><body background-color: rgba(255,255,255,0)></body></html>')
await p
w.show()
w.capturePage((image) => {
const imgBuffer = image.toPNG()
// Check the 25th byte in the PNG.
// Values can be 0,2,3,4, or 6. We want 6, which is RGB + Alpha
expect(imgBuffer[25]).to.equal(6)
done()
}).then(w => {
const p = emittedOnce(w, 'ready-to-show')
w.loadURL('data:text/html,<html><body background-color: rgba(255,255,255,0)></body></html>')
p.then(() => {
w.show()
w.capturePage((image) => {
const imgBuffer = image.toPNG()
// Check the 25th byte in the PNG.
// Values can be 0,2,3,4, or 6. We want 6, which is RGB + Alpha
expect(imgBuffer[25]).to.equal(6)
done()
})
})
})
})
})
@@ -1382,23 +1383,29 @@ describe('BrowserWindow module', () => {
assert.deepStrictEqual(defaultSession.getPreloads(), preloads)
})
it('loads the script before other scripts in window including normal preloads', function (done) {
ipcMain.once('vars', function (event, preload1, preload2, preload3) {
assert.strictEqual(preload1, 'preload-1')
assert.strictEqual(preload2, 'preload-1-2')
assert.strictEqual(preload3, 'preload-1-2-3')
done()
const generateSpecs = (description, sandbox) => {
describe(description, () => {
it('loads the script before other scripts in window including normal preloads', function (done) {
ipcMain.once('vars', function (event, preload1, preload2) {
assert.strictEqual(preload1, 'preload-1')
assert.strictEqual(preload2, 'preload-1-2')
done()
})
w.destroy()
w = new BrowserWindow({
show: false,
webPreferences: {
sandbox,
preload: path.join(fixtures, 'module', 'get-global-preload.js')
}
})
w.loadURL('about:blank')
})
})
w.destroy()
w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true,
preload: path.join(fixtures, 'module', 'set-global-preload-3.js')
}
})
w.loadFile(path.join(fixtures, 'api', 'preloads.html'))
})
}
generateSpecs('without sandbox', false)
generateSpecs('with sandbox', true)
})
describe('"additionalArguments" option', () => {

View File

@@ -28,6 +28,28 @@ describe('contentTracing', () => {
}
})
const record = async (options, outputFilePath, recordTimeInMilliseconds = 1e3) => {
await app.whenReady()
await contentTracing.startRecording(options)
await timeout(recordTimeInMilliseconds)
const resultFilePath = await contentTracing.stopRecording(outputFilePath)
return resultFilePath
}
// TODO(codebytere): remove when promisification is complete
const recordCallback = async (options, outputFilePath, recordTimeInMilliseconds = 1e3) => {
await app.whenReady()
await startRecording(options)
await timeout(recordTimeInMilliseconds)
const resultFilePath = await stopRecording(outputFilePath)
return resultFilePath
}
// TODO(codebytere): remove when promisification is complete
const startRecording = async (options) => {
return new Promise((resolve) => {
contentTracing.startRecording(options, () => {
@@ -36,6 +58,7 @@ describe('contentTracing', () => {
})
}
// TODO(codebytere): remove when promisification is complete
const stopRecording = async (filePath) => {
return new Promise((resolve) => {
contentTracing.stopRecording(filePath, (resultFilePath) => {
@@ -44,16 +67,6 @@ describe('contentTracing', () => {
})
}
const record = async (options, outputFilePath, recordTimeInMilliseconds = 1e3) => {
await app.whenReady()
await startRecording(options)
await timeout(recordTimeInMilliseconds)
const resultFilePath = await stopRecording(outputFilePath)
return resultFilePath
}
const outputFilePath = getPathInATempFolder('trace.json')
beforeEach(() => {
if (fs.existsSync(outputFilePath)) {
@@ -82,6 +95,18 @@ describe('contentTracing', () => {
`the trace output file is empty, check "${outputFilePath}"`)
})
// TODO(codebytere): remove when promisification is complete
it('accepts an empty config (callback)', async () => {
const config = {}
await recordCallback(config, outputFilePath)
expect(fs.existsSync(outputFilePath)).to.be.true()
const fileSizeInKiloBytes = getFileSizeInKiloBytes(outputFilePath)
expect(fileSizeInKiloBytes).to.be.above(0,
`the trace output file is empty, check "${outputFilePath}"`)
})
it('accepts a trace config', async () => {
// (alexeykuzmin): All categories are excluded on purpose,
// so only metadata gets into the output file.
@@ -104,6 +129,29 @@ describe('contentTracing', () => {
check "${outputFilePath}"`)
})
// TODO(codebytere): remove when promisification is complete
it('accepts a trace config (callback)', async () => {
// (alexeykuzmin): All categories are excluded on purpose,
// so only metadata gets into the output file.
const config = {
excluded_categories: ['*']
}
await recordCallback(config, outputFilePath)
expect(fs.existsSync(outputFilePath)).to.be.true()
// If the `excluded_categories` param above is not respected
// the file size will be above 50KB.
const fileSizeInKiloBytes = getFileSizeInKiloBytes(outputFilePath)
const expectedMaximumFileSize = 10 // Depends on a platform.
expect(fileSizeInKiloBytes).to.be.above(0,
`the trace output file is empty, check "${outputFilePath}"`)
expect(fileSizeInKiloBytes).to.be.below(expectedMaximumFileSize,
`the trace output file is suspiciously large (${fileSizeInKiloBytes}KB),
check "${outputFilePath}"`)
})
it('accepts "categoryFilter" and "traceOptions" as a config', async () => {
// (alexeykuzmin): All categories are excluded on purpose,
// so only metadata gets into the output file.
@@ -126,6 +174,30 @@ describe('contentTracing', () => {
`the trace output file is suspiciously large (${fileSizeInKiloBytes}KB),
check "${outputFilePath}"`)
})
// TODO(codebytere): remove when promisification is complete
it('accepts "categoryFilter" and "traceOptions" as a config (callback)', async () => {
// (alexeykuzmin): All categories are excluded on purpose,
// so only metadata gets into the output file.
const config = {
categoryFilter: '__ThisIsANonexistentCategory__',
traceOptions: ''
}
await recordCallback(config, outputFilePath)
expect(fs.existsSync(outputFilePath)).to.be.true()
// If the `categoryFilter` param above is not respected
// the file size will be above 50KB.
const fileSizeInKiloBytes = getFileSizeInKiloBytes(outputFilePath)
const expectedMaximumFileSize = 10 // Depends on a platform.
expect(fileSizeInKiloBytes).to.be.above(0,
`the trace output file is empty, check "${outputFilePath}"`)
expect(fileSizeInKiloBytes).to.be.below(expectedMaximumFileSize,
`the trace output file is suspiciously large (${fileSizeInKiloBytes}KB),
check "${outputFilePath}"`)
})
})
describe('stopRecording', function () {
@@ -136,6 +208,12 @@ describe('contentTracing', () => {
expect(resultFilePath).to.be.a('string').and.be.equal(outputFilePath)
})
// TODO(codebytere): remove when promisification is complete
it('calls its callback with a result file path (callback)', async () => {
const resultFilePath = await recordCallback(/* options */ {}, outputFilePath)
expect(resultFilePath).to.be.a('string').and.be.equal(outputFilePath)
})
// FIXME(alexeykuzmin): https://github.com/electron/electron/issues/16019
xit('creates a temporary file when an empty string is passed', async function () {
const resultFilePath = await record(/* options */ {}, /* outputFilePath */ '')

View File

@@ -138,7 +138,7 @@ describe('deprecations', () => {
it('acts as a pass-through for promise-based invocations', async () => {
enableCallbackWarnings()
promiseFunc = deprecate.promisify(promiseFunc, 1)
promiseFunc = deprecate.promisify(promiseFunc)
const actual = await promiseFunc(expected)
expect(actual).to.equal(expected)
@@ -147,7 +147,7 @@ describe('deprecations', () => {
it('warns exactly once for callback-based invocations', (done) => {
enableCallbackWarnings()
promiseFunc = deprecate.promisify(promiseFunc, 1)
promiseFunc = deprecate.promisify(promiseFunc)
let callbackCount = 0
const invocationCount = 3

View File

@@ -65,6 +65,7 @@ describe('desktopCapturer', () => {
const callback = (error, sources) => {
callCount++
expect(error).to.be.null()
expect(sources).to.not.be.null()
if (callCount === 2) done()
}

View File

@@ -550,12 +550,12 @@ describe('net module', () => {
handleUnexpectedURL(request, response)
}
})
customSession.cookies.set({
url: `${server.url}`,
name: 'test',
value: '11111'
}, (error) => {
if (error) return done(error)
}).then(() => { // resolved
const urlRequest = net.request({
method: 'GET',
url: `${server.url}${requestUrl}`,
@@ -575,6 +575,8 @@ describe('net module', () => {
assert.strictEqual(urlRequest.getHeader(cookieHeaderName),
cookieHeaderValue)
urlRequest.end()
}, (error) => {
done(error)
})
})

View File

@@ -654,16 +654,40 @@ describe('protocol module', () => {
})
})
})
it('can handle large responses', async () => {
const data = Buffer.alloc(128 * 1024)
const handler = (request, callback) => {
callback(getStream(data.length, data))
}
await new Promise((resolve, reject) => {
protocol.registerStreamProtocol(protocolName, handler, err => {
if (err) return reject(err)
resolve()
})
})
const r = await new Promise((resolve, reject) => {
$.ajax({
url: protocolName + '://fake-host',
cache: false,
success: resolve,
error: (xhr, errorType, error) => {
reject(error || new Error(`Request failed: ${xhr.status}`))
}
})
})
assert.strictEqual(r.length, data.length)
})
})
describe('protocol.isProtocolHandled', (done) => {
describe('protocol.isProtocolHandled', () => {
it('returns true for about:', async () => {
const result = await protocol.isProtocolHandled('about')
assert.strictEqual(result, true)
})
// TODO(codebytere): remove when promisification is complete
it('returns true for about: (callback)', () => {
it('returns true for about: (callback)', (done) => {
protocol.isProtocolHandled('about', (result) => {
assert.strictEqual(result, true)
done()
@@ -676,7 +700,7 @@ describe('protocol module', () => {
})
// TODO(codebytere): remove when promisification is complete
it('returns true for file: (callback)', () => {
it('returns true for file: (callback)', (done) => {
protocol.isProtocolHandled('file', (result) => {
assert.strictEqual(result, true)
done()
@@ -698,7 +722,7 @@ describe('protocol module', () => {
assert.strictEqual(result, false)
})
it('returns true for custom protocol', () => {
it('returns true for custom protocol', (done) => {
const emptyHandler = (request, callback) => callback()
protocol.registerStringProtocol(protocolName, emptyHandler, async (error) => {
assert.strictEqual(error, null)
@@ -709,7 +733,7 @@ describe('protocol module', () => {
})
// TODO(codebytere): remove when promisification is complete
it('returns true for custom protocol (callback)', () => {
it('returns true for custom protocol (callback)', (done) => {
const emptyHandler = (request, callback) => callback()
protocol.registerStringProtocol(protocolName, emptyHandler, (error) => {
assert.strictEqual(error, null)
@@ -720,7 +744,7 @@ describe('protocol module', () => {
})
})
it('returns true for intercepted protocol', () => {
it('returns true for intercepted protocol', (done) => {
const emptyHandler = (request, callback) => callback()
protocol.interceptStringProtocol('http', emptyHandler, async (error) => {
assert.strictEqual(error, null)
@@ -731,7 +755,7 @@ describe('protocol module', () => {
})
// TODO(codebytere): remove when promisification is complete
it('returns true for intercepted protocol (callback)', () => {
it('returns true for intercepted protocol (callback)', (done) => {
const emptyHandler = (request, callback) => callback()
protocol.interceptStringProtocol('http', emptyHandler, (error) => {
assert.strictEqual(error, null)
@@ -1063,7 +1087,7 @@ describe('protocol module', () => {
})
})
describe('protocol.registerStandardSchemes', () => {
describe('protocol.registerSchemesAsPrivileged standard', () => {
const standardScheme = remote.getGlobal('standardScheme')
const origin = `${standardScheme}://fake-host`
const imageURL = `${origin}/test.png`
@@ -1165,4 +1189,102 @@ describe('protocol module', () => {
})
})
})
describe('protocol.registerSchemesAsPrivileged cors-fetch', function () {
const standardScheme = remote.getGlobal('standardScheme')
const fixtures = path.resolve(__dirname, 'fixtures')
let w = null
beforeEach((done) => {
protocol.unregisterProtocol(standardScheme, () => done())
})
afterEach((done) => {
closeWindow(w).then(() => {
w = null
done()
})
})
it('supports fetch api by default', (done) => {
const url = 'file://' + fixtures + '/assets/logo.png'
window.fetch(url)
.then(function (response) {
assert(response.ok)
done()
})
.catch(function (err) {
done('unexpected error : ' + err)
})
})
it('allows CORS requests by default', (done) => {
allowsCORSRequests('cors', 200, `<html>
<script>
const {ipcRenderer} = require('electron')
fetch('cors://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
it('disallows CORS, but allows fetch requests, when specified', (done) => {
allowsCORSRequests('no-cors', 'failed', `<html>
<script>
const {ipcRenderer} = require('electron')
fetch('no-cors://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
it('allows CORS, but disallows fetch requests, when specified', (done) => {
allowsCORSRequests('no-fetch', 'failed', `<html>
<script>
const {ipcRenderer} = require('electron')
fetch('no-fetch://myhost').then(function
(response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
function allowsCORSRequests (corsScheme, expected, content, done) {
const url = standardScheme + '://fake-host'
w = new BrowserWindow({
show: false,
webPreferences: {
nodeIntegration: true
}
})
const handler = (request, callback) => {
callback({ data: content, mimeType: 'text/html' })
}
protocol.registerStringProtocol(standardScheme, handler, (error) => {
if (error) { return done(error) }
})
protocol.registerStringProtocol(corsScheme,
(request, callback) => {
callback('')
}, (error) => {
if (error) { return done(error) }
ipcMain.once('response', function (event, status) {
assert.strictEqual(status, expected)
protocol.unregisterProtocol(corsScheme, () => done())
})
w.loadURL(url)
})
}
})
})

View File

@@ -68,151 +68,212 @@ describe('session module', () => {
})
describe('ses.cookies', () => {
it('should get cookies', (done) => {
const name = '0'
const value = '0'
it('should get cookies with promises', (done) => {
const server = http.createServer((req, res) => {
res.setHeader('Set-Cookie', ['0=0'])
res.setHeader('Set-Cookie', [`${name}=${value}`])
res.end('finished')
server.close()
})
server.listen(0, '127.0.0.1', () => {
const port = server.address().port
w.webContents.once('did-finish-load', () => {
w.webContents.session.cookies.get({ url }, (error, list) => {
if (error) return done(error)
for (let i = 0; i < list.length; i++) {
const cookie = list[i]
if (cookie.name === '0') {
if (cookie.value === '0') {
return done()
} else {
return done(`cookie value is ${cookie.value} while expecting 0`)
}
}
}
done('Can\'t find cookie')
})
w.webContents.once('did-finish-load', async () => {
const list = await w.webContents.session.cookies.get({ url })
const cookie = list.find(cookie => cookie.name === name)
expect(cookie).to.exist.and.to.have.property('value', value)
done()
})
const { port } = server.address()
w.loadURL(`${url}:${port}`)
})
})
it('calls back with an error when setting a cookie with missing required fields', (done) => {
session.defaultSession.cookies.set({
url: '',
name: '1',
value: '1'
}, (error) => {
assert(error, 'Should have an error')
assert.strictEqual(error.message, 'Setting cookie failed')
done()
it('should get cookies with callbacks', (done) => {
const server = http.createServer((req, res) => {
res.setHeader('Set-Cookie', [`${name}=${value}`])
res.end('finished')
server.close()
})
})
it('should over-write the existent cookie', (done) => {
session.defaultSession.cookies.set({
url,
name: '1',
value: '1'
}, (error) => {
if (error) return done(error)
session.defaultSession.cookies.get({ url }, (error, list) => {
if (error) return done(error)
for (let i = 0; i < list.length; i++) {
const cookie = list[i]
if (cookie.name === '1') {
if (cookie.value === '1') {
return done()
} else {
return done(`cookie value is ${cookie.value} while expecting 1`)
}
}
}
done('Can\'t find cookie')
})
})
})
it('should remove cookies', (done) => {
session.defaultSession.cookies.set({
url: url,
name: '2',
value: '2'
}, (error) => {
if (error) return done(error)
session.defaultSession.cookies.remove(url, '2', () => {
session.defaultSession.cookies.get({ url }, (error, list) => {
server.listen(0, '127.0.0.1', () => {
w.webContents.once('did-finish-load', () => {
w.webContents.session.cookies.get({ url }, (error, list) => {
if (error) return done(error)
for (let i = 0; i < list.length; i++) {
const cookie = list[i]
if (cookie.name === '2') return done('Cookie not deleted')
}
const cookie = list.find(cookie => cookie.name === name)
expect(cookie).to.exist.and.to.have.property('value', value)
done()
})
})
const { port } = server.address()
w.loadURL(`${url}:${port}`)
})
})
it('sets cookies with promises', async () => {
let error
try {
const { cookies } = session.defaultSession
const name = '1'
const value = '1'
await cookies.set({ url, name, value })
} catch (e) {
error = e
}
expect(error).to.be.undefined(error)
})
it('sets cookies with callbacks', (done) => {
const { cookies } = session.defaultSession
const name = '1'
const value = '1'
cookies.set({ url, name, value }, (error, list) => done(error))
})
it('yields an error when setting a cookie with missing required fields', async () => {
let error
try {
const { cookies } = session.defaultSession
const name = '1'
const value = '1'
await cookies.set({ url: '', name, value })
} catch (e) {
error = e
}
expect(error).is.an('Error')
expect(error).to.have.property('message').which.equals('Setting cookie failed')
})
it('should overwrite previous cookies', async () => {
let error
try {
const { cookies } = session.defaultSession
const name = 'DidOverwrite'
for (const value of [ 'No', 'Yes' ]) {
await cookies.set({ url, name, value })
const list = await cookies.get({ url })
assert(list.some(cookie => cookie.name === name && cookie.value === value))
}
} catch (e) {
error = e
}
expect(error).to.be.undefined(error)
})
it('should remove cookies with promises', async () => {
let error
try {
const { cookies } = session.defaultSession
const name = '2'
const value = '2'
await cookies.set({ url, name, value })
await cookies.remove(url, name)
const list = await cookies.get({ url })
assert(!list.some(cookie => cookie.name === name && cookie.value === value))
} catch (e) {
error = e
}
expect(error).to.be.undefined(error)
})
it('should remove cookies with callbacks', (done) => {
const { cookies } = session.defaultSession
const name = '2'
const value = '2'
cookies.set({ url, name, value }, (error) => {
if (error) return done(error)
cookies.remove(url, name, (error) => {
if (error) return done(error)
cookies.get({ url }, (error, list) => {
if (error) return done(error)
assert(!list.some(cookie => cookie.name === name))
done()
})
})
})
})
it('should set cookie for standard scheme', (done) => {
const standardScheme = remote.getGlobal('standardScheme')
const origin = standardScheme + '://fake-host'
session.defaultSession.cookies.set({
url: origin,
name: 'custom',
value: '1'
}, (error) => {
if (error) return done(error)
session.defaultSession.cookies.get({ url: origin }, (error, list) => {
if (error) return done(error)
assert.strictEqual(list.length, 1)
assert.strictEqual(list[0].name, 'custom')
assert.strictEqual(list[0].value, '1')
assert.strictEqual(list[0].domain, 'fake-host')
done()
})
})
it('should set cookie for standard scheme', async () => {
let error
try {
const { cookies } = session.defaultSession
const standardScheme = remote.getGlobal('standardScheme')
const domain = 'fake-host'
const url = `${standardScheme}://${domain}`
const name = 'custom'
const value = '1'
await cookies.set({ url, name, value })
const list = await cookies.get({ url })
expect(list).to.have.lengthOf(1)
expect(list[0]).to.have.property('name', name)
expect(list[0]).to.have.property('value', value)
expect(list[0]).to.have.property('domain', domain)
} catch (e) {
error = e
}
expect(error).to.be.undefined(error)
})
it('emits a changed event when a cookie is added or removed', (done) => {
const { cookies } = session.fromPartition('cookies-changed')
it('emits a changed event when a cookie is added or removed', async () => {
let error
const changes = []
cookies.once('changed', (event, cookie, cause, removed) => {
assert.strictEqual(cookie.name, 'foo')
assert.strictEqual(cookie.value, 'bar')
assert.strictEqual(cause, 'explicit')
assert.strictEqual(removed, false)
try {
const { cookies } = session.fromPartition('cookies-changed')
const name = 'foo'
const value = 'bar'
const eventName = 'changed'
const listener = (event, cookie, cause, removed) => { changes.push({ cookie, cause, removed }) }
cookies.once('changed', (event, cookie, cause, removed) => {
assert.strictEqual(cookie.name, 'foo')
assert.strictEqual(cookie.value, 'bar')
assert.strictEqual(cause, 'explicit')
assert.strictEqual(removed, true)
done()
})
cookies.on(eventName, listener)
await cookies.set({ url, name, value })
await cookies.remove(url, name)
cookies.off(eventName, listener)
cookies.remove(url, 'foo', (error) => {
if (error) return done(error)
})
})
cookies.set({
url: url,
name: 'foo',
value: 'bar'
}, (error) => {
if (error) return done(error)
})
expect(changes).to.have.lengthOf(2)
expect(changes.every(change => change.cookie.name === name))
expect(changes.every(change => change.cookie.value === value))
expect(changes.every(change => change.cause === 'explicit'))
expect(changes[0].removed).to.be.false()
expect(changes[1].removed).to.be.true()
} catch (e) {
error = e
}
expect(error).to.be.undefined(error)
})
describe('ses.cookies.flushStore(callback)', () => {
it('flushes the cookies to disk and invokes the callback when done', (done) => {
session.defaultSession.cookies.set({
url: url,
name: 'foo',
value: 'bar'
}, (error) => {
if (error) return done(error)
session.defaultSession.cookies.flushStore(() => {
done()
describe('ses.cookies.flushStore()', async () => {
describe('flushes the cookies to disk and invokes the callback when done', async () => {
it('with promises', async () => {
let error
try {
const name = 'foo'
const value = 'bar'
const { cookies } = session.defaultSession
await cookies.set({ url, name, value })
await cookies.flushStore()
} catch (e) {
error = e
}
expect(error).to.be.undefined(error)
})
it('with callbacks', (done) => {
const name = 'foo'
const value = 'bar'
const { cookies } = session.defaultSession
cookies.set({ url, name, value }, error => {
if (error) return done(error)
cookies.flushStore(error => done(error))
})
})
})
@@ -232,7 +293,7 @@ describe('session module', () => {
{ env: { PHASE: phase, ...process.env } }
)
appProcess.stdout.on('data', (data) => { output += data })
appProcess.stdout.on('data', data => { output += data })
appProcess.stdout.on('end', () => {
output = output.replace(/(\r\n|\n|\r)/gm, '')
assert.strictEqual(output, result)
@@ -620,12 +681,9 @@ describe('session module', () => {
})
})
// FIXME: Disabled with C71 upgrade
// Re-enable with new api from
// https://github.com/electron/electron/tree/webframe-scheme-api
xdescribe('ses.getBlobData(identifier, callback)', () => {
describe('ses.getBlobData(identifier, callback)', () => {
it('returns blob data for uuid', (done) => {
const scheme = 'temp'
const scheme = 'cors-blob'
const protocol = session.defaultSession.protocol
const url = `${scheme}://host`
before(() => {
@@ -648,8 +706,6 @@ describe('session module', () => {
})
const content = `<html>
<script>
const {webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('${scheme}')
let fd = new FormData();
fd.append('file', new Blob(['${postData}'], {type:'application/json'}));
fetch('${url}', {method:'POST', body: fd });

View File

@@ -4,7 +4,7 @@ const dirtyChai = require('dirty-chai')
const path = require('path')
const { closeWindow } = require('./window-helpers')
const { remote, webFrame } = require('electron')
const { BrowserWindow, protocol, ipcMain } = remote
const { BrowserWindow, ipcMain } = remote
const { expect } = chai
chai.use(dirtyChai)
@@ -20,124 +20,6 @@ describe('webFrame module', function () {
return closeWindow(w).then(function () { w = null })
})
// FIXME: Disabled with C70.
xdescribe('webFrame.registerURLSchemeAsPrivileged', function () {
it('supports fetch api by default', function (done) {
const url = 'file://' + fixtures + '/assets/logo.png'
window.fetch(url).then(function (response) {
assert(response.ok)
done()
}).catch(function (err) {
done('unexpected error : ' + err)
})
})
it('allows CORS requests by default', function (done) {
allowsCORSRequests(200, `<html>
<script>
const {ipcRenderer, webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('cors1')
fetch('cors1://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
it('allows CORS and fetch requests when specified', function (done) {
allowsCORSRequests(200, `<html>
<script>
const {ipcRenderer, webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('cors2', { supportFetchAPI: true, corsEnabled: true })
fetch('cors2://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
it('allows CORS and fetch requests when half-specified', function (done) {
allowsCORSRequests(200, `<html>
<script>
const {ipcRenderer, webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('cors3', { supportFetchAPI: true })
fetch('cors3://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
it('disallows CORS, but allows fetch requests, when specified', function (done) {
allowsCORSRequests('failed', `<html>
<script>
const {ipcRenderer, webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('cors4', { supportFetchAPI: true, corsEnabled: false })
fetch('cors4://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
it('allows CORS, but disallows fetch requests, when specified', function (done) {
allowsCORSRequests('failed', `<html>
<script>
const {ipcRenderer, webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('cors5', { supportFetchAPI: false, corsEnabled: true })
fetch('cors5://myhost').then(function (response) {
ipcRenderer.send('response', response.status)
}).catch(function (response) {
ipcRenderer.send('response', 'failed')
})
</script>
</html>`, done)
})
let runNumber = 1
function allowsCORSRequests (expected, content, done) {
const standardScheme = remote.getGlobal('standardScheme') + runNumber
const corsScheme = 'cors' + runNumber
runNumber++
const url = standardScheme + '://fake-host'
w = new BrowserWindow({ show: false })
after(function (done) {
protocol.unregisterProtocol(corsScheme, function () {
protocol.unregisterProtocol(standardScheme, function () {
done()
})
})
})
const handler = function (request, callback) {
callback({ data: content, mimeType: 'text/html' })
}
protocol.registerStringProtocol(standardScheme, handler, function (error) {
if (error) return done(error)
})
protocol.registerStringProtocol(corsScheme, function (request, callback) {
callback('')
}, function (error) {
if (error) return done(error)
ipcMain.once('response', function (event, status) {
assert.strictEqual(status, expected)
done()
})
w.loadURL(url)
})
}
})
it('supports setting the visual and layout zoom level limits', function () {
assert.doesNotThrow(function () {
webFrame.setVisualZoomLevelLimits(1, 50)

View File

@@ -3,51 +3,30 @@ const { app, session } = require('electron')
app.on('ready', async function () {
const url = 'http://foo.bar'
const persistentSession = session.fromPartition('persist:ence-test')
const name = 'test'
const value = 'true'
const set = () => {
return new Promise((resolve, reject) => {
persistentSession.cookies.set({
url,
name: 'test',
value: 'true',
expirationDate: Date.now() + 60000
}, error => {
if (error) {
reject(error)
} else {
resolve()
}
})
})
}
const set = () => persistentSession.cookies.set({
url,
name,
value,
expirationDate: Date.now() + 60000
})
const get = () => {
return new Promise((resolve, reject) => {
persistentSession.cookies.get({ url }, (error, list) => {
if (error) {
reject(error)
} else {
resolve(list)
}
})
})
}
const get = () => persistentSession.cookies.get({
url
})
const maybeRemove = (pred) => {
return new Promise((resolve, reject) => {
const maybeRemove = async (pred) => new Promise(async (resolve, reject) => {
try {
if (pred()) {
persistentSession.cookies.remove(url, 'test', error => {
if (error) {
reject(error)
} else {
resolve()
}
})
} else {
resolve()
await persistentSession.cookies.remove(url, name)
}
})
}
resolve()
} catch (error) {
reject(error)
}
})
try {
await maybeRemove(() => process.env.PHASE === 'one')

View File

@@ -0,0 +1 @@
require('electron').ipcRenderer.send('vars', window.preload1, window.preload2)

View File

@@ -1 +0,0 @@
window.preload3 = window.preload2 + '-3'

View File

@@ -428,6 +428,30 @@ describe('node feature', () => {
})
})
describe('crypto', () => {
it('should list the ripemd160 hash in getHashes', () => {
expect(require('crypto').getHashes()).to.include('ripemd160')
})
it('should be able to create a ripemd160 hash and use it', () => {
const hash = require('crypto').createHash('ripemd160')
hash.update('electron-ripemd160')
expect(hash.digest('hex')).to.equal('fa7fec13c624009ab126ebb99eda6525583395fe')
})
it('should list aes-{128,256}-cfb in getCiphers', () => {
expect(require('crypto').getCiphers()).to.include.members(['aes-128-cfb', 'aes-256-cfb'])
})
it('should be able to create an aes-128-cfb cipher', () => {
require('crypto').createCipheriv('aes-128-cfb', '0123456789abcdef', '0123456789abcdef')
})
it('should be able to create an aes-256-cfb cipher', () => {
require('crypto').createCipheriv('aes-256-cfb', '0123456789abcdef0123456789abcdef', '0123456789abcdef')
})
})
it('includes the electron version in process.versions', () => {
expect(process.versions)
.to.have.own.property('electron')

32
spec/package-lock.json generated
View File

@@ -72,7 +72,7 @@
},
"bl": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"resolved": "http://registry.npmjs.org/bl/-/bl-1.2.2.tgz",
"integrity": "sha512-e8tQYnZodmebYDWGH7KMRvtzKXaJHx3BbilrgZCfvyLUYdKpK1t5PSPmpkny/SgiTSCnjfLW7v5rlONXVFkQEA==",
"optional": true,
"requires": {
@@ -228,7 +228,7 @@
},
"commander": {
"version": "2.15.1",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"resolved": "http://registry.npmjs.org/commander/-/commander-2.15.1.tgz",
"integrity": "sha512-VlfT9F3V0v+jr4yxPc5gg9s62/fIVWsd2Bk2iD435um1NlGMYdVCq+MjcXnhYq2icNOizHr1kK+5TI6H0Hy0ag==",
"dev": true
},
@@ -267,7 +267,7 @@
},
"dbus-native": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/dbus-native/-/dbus-native-0.2.5.tgz",
"resolved": "http://registry.npmjs.org/dbus-native/-/dbus-native-0.2.5.tgz",
"integrity": "sha512-ocxMKCV7QdiNhzhFSeEMhj258OGtvpANSb3oWGiotmI5h1ZIse0TMPcSLiXSpqvbYvQz2Y5RsYPMNYLWhg9eBw==",
"dev": true,
"requires": {
@@ -358,7 +358,7 @@
},
"duplexer": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
"resolved": "http://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
"integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E=",
"dev": true
},
@@ -512,7 +512,7 @@
},
"get-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=",
"dev": true
},
@@ -753,7 +753,7 @@
},
"mkdirp": {
"version": "0.5.1",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
"requires": {
"minimist": "0.0.8"
@@ -761,7 +761,7 @@
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
}
}
@@ -941,7 +941,7 @@
},
"os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"resolved": "http://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=",
"optional": true
},
@@ -958,7 +958,7 @@
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"resolved": "http://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
"dev": true
},
@@ -1000,7 +1000,7 @@
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"resolved": "http://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=",
"dev": true
},
@@ -1018,7 +1018,7 @@
},
"pause-stream": {
"version": "0.0.11",
"resolved": "https://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
"resolved": "http://registry.npmjs.org/pause-stream/-/pause-stream-0.0.11.tgz",
"integrity": "sha1-/lo0sMvOErWqaitAPuLnO2AvFEU=",
"dev": true,
"requires": {
@@ -1139,7 +1139,7 @@
},
"rimraf": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
"resolved": "http://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz",
"integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=",
"dev": true
},
@@ -1305,7 +1305,7 @@
},
"string_decoder": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"resolved": "http://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
"requires": {
"safe-buffer": "~5.1.0"
@@ -1321,7 +1321,7 @@
},
"strip-eof": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"resolved": "http://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz",
"integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=",
"dev": true
},
@@ -1391,7 +1391,7 @@
},
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"resolved": "http://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=",
"dev": true
},
@@ -1486,7 +1486,7 @@
},
"wrap-ansi": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"resolved": "http://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
"dev": true,
"requires": {

View File

@@ -97,7 +97,14 @@ global.nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS
// Register app as standard scheme.
global.standardScheme = 'app'
global.zoomScheme = 'zoom'
protocol.registerStandardSchemes([global.standardScheme, global.zoomScheme], { secure: true })
protocol.registerSchemesAsPrivileged([
{ scheme: global.standardScheme, privileges: { standard: true, secure: true } },
{ scheme: global.zoomScheme, privileges: { standard: true, secure: true } },
{ scheme: 'cors', privileges: { corsEnabled: true, supportFetchAPI: true } },
{ scheme: 'cors-blob', privileges: { corsEnabled: true, supportFetchAPI: true } },
{ scheme: 'no-cors', privileges: { supportFetchAPI: true } },
{ scheme: 'no-fetch', privileges: { corsEnabled: true } }
])
app.on('window-all-closed', function () {
app.quit()

View File

@@ -70,18 +70,24 @@ steps:
python electron/script/verify-ffmpeg.py --source-root "$PWD" --build-dir out/Default --ffmpeg-path out/ffmpeg
displayName: Verify non proprietary ffmpeg
timeoutInMinutes: 5
env:
ELECTRON_DISABLE_SANDBOX: 1
- bash: |
cd src
python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default
displayName: Verify mksnapshot
timeoutInMinutes: 5
env:
ELECTRON_DISABLE_SANDBOX: 1
- bash: |
cd src
./out/Default/electron electron/spec --ci
displayName: 'Run Electron tests'
timeoutInMinutes: 10
env:
ELECTRON_DISABLE_SANDBOX: 1
- task: PublishTestResults@2
displayName: 'Publish Test Results'