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")) { if ((-Not (Test-Path Env:\ELECTRON_RELEASE)) -And ($env:GN_CONFIG -in "testing", "release")) {
$env:RUN_TESTS="true" $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: >- - ps: >-
if ($env:RUN_TESTS -eq 'true') { if ($env:RUN_TESTS -eq 'true') {
New-Item .\out\Default\gen\node_headers\Release -Type directory New-Item .\out\Default\gen\node_headers\Release -Type directory
@@ -91,8 +89,9 @@ test_script:
echo "Skipping tests for $env:GN_CONFIG build" echo "Skipping tests for $env:GN_CONFIG build"
} }
- cd electron - 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 .. - 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: deploy_script:
- cd electron - cd electron
- ps: >- - ps: >-

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -34,8 +34,9 @@ namespace atom {
namespace api { namespace api {
std::vector<std::string> GetStandardSchemes(); 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> { class Protocol : public mate::TrackableObject<Protocol> {
public: public:
@@ -94,9 +95,6 @@ class Protocol : public mate::TrackableObject<Protocol> {
DISALLOW_COPY_AND_ASSIGN(CustomProtocolHandler); 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. // Register the protocol with certain request job.
template <typename RequestJob> template <typename RequestJob>
void RegisterProtocol(const std::string& scheme, void RegisterProtocol(const std::string& scheme,

View File

@@ -114,9 +114,6 @@ namespace {
// Next navigation should not restart renderer process. // Next navigation should not restart renderer process.
bool g_suppress_renderer_process_restart = false; 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, bool IsSameWebSite(content::BrowserContext* browser_context,
const GURL& src_url, const GURL& src_url,
const GURL& dest_url) { const GURL& dest_url) {
@@ -148,11 +145,6 @@ void AtomBrowserClient::SuppressRendererProcessRestartForOnce() {
g_suppress_renderer_process_restart = true; 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() { AtomBrowserClient* AtomBrowserClient::Get() {
return g_browser_client; return g_browser_client;
} }
@@ -477,18 +469,15 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches(
return; return;
// Copy following switches to child process. // Copy following switches to child process.
static const char* const kCommonSwitchNames[] = {switches::kStandardSchemes, static const char* const kCommonSwitchNames[] = {
switches::kEnableSandbox, switches::kStandardSchemes, switches::kEnableSandbox,
switches::kSecureSchemes}; switches::kSecureSchemes, switches::kBypassCSPSchemes,
switches::kCORSSchemes, switches::kFetchSchemes,
switches::kServiceWorkerSchemes};
command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), command_line->CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(),
kCommonSwitchNames, kCommonSwitchNames,
arraysize(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) #if defined(OS_WIN)
// Append --app-user-model-id. // Append --app-user-model-id.
PWSTR current_app_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. // Don't force renderer process to restart for once.
static void SuppressRendererProcessRestartForOnce(); static void SuppressRendererProcessRestartForOnce();
// Custom schemes to be registered to handle service worker.
static void SetCustomServiceWorkerSchemes(
const std::vector<std::string>& schemes);
NotificationPresenter* GetNotificationPresenter(); NotificationPresenter* GetNotificationPresenter();
void WebNotificationAllowed(int render_process_id, void WebNotificationAllowed(int render_process_id,

View File

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

View File

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

View File

@@ -25,10 +25,6 @@ class AtomDownloadManagerDelegate : public content::DownloadManagerDelegate {
explicit AtomDownloadManagerDelegate(content::DownloadManager* manager); explicit AtomDownloadManagerDelegate(content::DownloadManager* manager);
~AtomDownloadManagerDelegate() override; ~AtomDownloadManagerDelegate() override;
void OnDownloadPathGenerated(uint32_t download_id,
const content::DownloadTargetCallback& callback,
const base::FilePath& default_path);
// content::DownloadManagerDelegate: // content::DownloadManagerDelegate:
void Shutdown() override; void Shutdown() override;
bool DetermineDownloadTarget( bool DetermineDownloadTarget(
@@ -45,6 +41,25 @@ class AtomDownloadManagerDelegate : public content::DownloadManagerDelegate {
void GetItemSaveDialogOptions(download::DownloadItem* item, void GetItemSaveDialogOptions(download::DownloadItem* item,
file_dialog::DialogSettings* settings); 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_; content::DownloadManager* download_manager_;
base::WeakPtrFactory<AtomDownloadManagerDelegate> weak_ptr_factory_; base::WeakPtrFactory<AtomDownloadManagerDelegate> weak_ptr_factory_;

View File

@@ -25,9 +25,6 @@
namespace atom { 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() = default; Browser::LoginItemSettings::~LoginItemSettings() = default;
Browser::LoginItemSettings::LoginItemSettings(const LoginItemSettings& other) = Browser::LoginItemSettings::LoginItemSettings(const LoginItemSettings& other) =
@@ -95,11 +92,12 @@ void Browser::Shutdown() {
for (BrowserObserver& observer : observers_) for (BrowserObserver& observer : observers_)
observer.OnQuit(); observer.OnQuit();
if (*g_quit_main_message_loop) { if (quit_main_message_loop_) {
std::move(*g_quit_main_message_loop).Run(); std::move(quit_main_message_loop_).Run();
} else { } else {
// There is no message loop available so we are in early stage. // There is no message loop available so we are in early stage, wait until
exit(0); // 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) { 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() { 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 // Stores the supplied |quit_closure|, to be run when the last Browser
// instance is destroyed. // instance is destroyed.
static void SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure); void SetMainMessageLoopQuitClosure(base::OnceClosure quit_closure);
void AddObserver(BrowserObserver* obs) { observers_.AddObserver(obs); } void AddObserver(BrowserObserver* obs) { observers_.AddObserver(obs); }
@@ -287,6 +287,9 @@ class Browser : public WindowListObserver {
// The browser is being shutdown. // The browser is being shutdown.
bool is_shutdown_ = false; bool is_shutdown_ = false;
// Null until/unless the default main message loop is running.
base::OnceClosure quit_main_message_loop_;
int badge_count_ = 0; int badge_count_ = 0;
util::Promise* ready_promise_ = nullptr; util::Promise* ready_promise_ = nullptr;

View File

@@ -141,6 +141,7 @@ void URLRequestStreamJob::StartAsync(
} }
void URLRequestStreamJob::OnData(std::vector<char>&& buffer) { // NOLINT void URLRequestStreamJob::OnData(std::vector<char>&& buffer) { // NOLINT
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
if (write_buffer_.empty()) { if (write_buffer_.empty()) {
// Quick branch without copying. // Quick branch without copying.
write_buffer_ = std::move(buffer); write_buffer_ = std::move(buffer);
@@ -175,7 +176,7 @@ void URLRequestStreamJob::OnError(int error) {
int URLRequestStreamJob::ReadRawData(net::IOBuffer* dest, int dest_size) { int URLRequestStreamJob::ReadRawData(net::IOBuffer* dest, int dest_size) {
response_start_time_ = base::TimeTicks::Now(); response_start_time_ = base::TimeTicks::Now();
if (ended_) if (ended_ && write_buffer_.empty())
return 0; return 0;
// When write_buffer_ is empty, there is no data valable yet, we have to save // 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); 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( OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
bool transparent, bool transparent,
bool painting, bool painting,
@@ -274,18 +318,22 @@ OffScreenRenderWidgetHostView::OffScreenRenderWidgetHostView(
weak_ptr_factory_(this) { weak_ptr_factory_(this) {
DCHECK(render_widget_host_); DCHECK(render_widget_host_);
bool is_guest_view_hack = parent_host_view_ != nullptr; bool is_guest_view_hack = parent_host_view_ != nullptr;
current_device_scale_factor_ = kDefaultScaleFactor;
#if !defined(OS_MACOSX) #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>( 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 */); true /* should_register_frame_sink_id */);
root_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR)); root_layer_.reset(new ui::Layer(ui::LAYER_SOLID_COLOR));
#endif #endif
current_device_scale_factor_ = kDefaultScaleFactor;
local_surface_id_ = local_surface_id_allocator_.GenerateId();
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
last_frame_root_background_color_ = SK_ColorTRANSPARENT; last_frame_root_background_color_ = SK_ColorTRANSPARENT;
CreatePlatformWidget(is_guest_view_hack); CreatePlatformWidget(is_guest_view_hack);
@@ -374,7 +422,7 @@ void OffScreenRenderWidgetHostView::SendBeginFrame(
begin_frame_number_++; begin_frame_number_++;
if (renderer_compositor_frame_sink_) 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) { void OffScreenRenderWidgetHostView::InitAsChild(gfx::NativeView) {
@@ -433,8 +481,9 @@ void OffScreenRenderWidgetHostView::Show() {
browser_compositor_->SetRenderWidgetHostIsHidden(false); browser_compositor_->SetRenderWidgetHostIsHidden(false);
#else #else
delegated_frame_host_->AttachToCompositor(compositor_.get()); delegated_frame_host_->AttachToCompositor(compositor_.get());
delegated_frame_host_->WasShown(GetLocalSurfaceId(), delegated_frame_host_->WasShown(
GetRootLayer()->bounds().size(), false); GetLocalSurfaceIdAllocation().local_surface_id(),
GetRootLayer()->bounds().size(), false);
#endif #endif
if (render_widget_host_) if (render_widget_host_)
@@ -525,19 +574,22 @@ void OffScreenRenderWidgetHostView::TakeFallbackContentFrom(
void OffScreenRenderWidgetHostView::DidCreateNewRendererCompositorFrameSink( void OffScreenRenderWidgetHostView::DidCreateNewRendererCompositorFrameSink(
viz::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink) { viz::mojom::CompositorFrameSinkClient* renderer_compositor_frame_sink) {
renderer_compositor_frame_sink_ = 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()) { if (GetDelegatedFrameHost()) {
GetDelegatedFrameHost()->DidCreateNewRendererCompositorFrameSink( GetDelegatedFrameHost()->DidCreateNewRendererCompositorFrameSink(
renderer_compositor_frame_sink_); renderer_compositor_frame_sink_);
} }
#endif
} }
void OffScreenRenderWidgetHostView::SubmitCompositorFrame( void OffScreenRenderWidgetHostView::SubmitCompositorFrame(
const viz::LocalSurfaceId& local_surface_id, const viz::LocalSurfaceId& local_surface_id,
viz::CompositorFrame frame, viz::CompositorFrame frame,
base::Optional<viz::HitTestRegionList> hit_test_region_list) { base::Optional<viz::HitTestRegionList> hit_test_region_list) {
TRACE_EVENT0("electron",
"OffScreenRenderWidgetHostView::SubmitCompositorFrame");
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
last_frame_root_background_color_ = frame.metadata.root_background_color; last_frame_root_background_color_ = frame.metadata.root_background_color;
#endif #endif
@@ -587,7 +639,7 @@ void OffScreenRenderWidgetHostView::SubmitCompositorFrame(
} }
void OffScreenRenderWidgetHostView::ClearCompositorFrame() { void OffScreenRenderWidgetHostView::ClearCompositorFrame() {
GetDelegatedFrameHost()->ClearDelegatedFrame(); NOTREACHED();
} }
void OffScreenRenderWidgetHostView::ResetFallbackToFirstNavigationSurface() { void OffScreenRenderWidgetHostView::ResetFallbackToFirstNavigationSurface() {
@@ -667,9 +719,6 @@ void OffScreenRenderWidgetHostView::Destroy() {
void OffScreenRenderWidgetHostView::SetTooltipText(const base::string16&) {} void OffScreenRenderWidgetHostView::SetTooltipText(const base::string16&) {}
void OffScreenRenderWidgetHostView::SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params&) {}
uint32_t OffScreenRenderWidgetHostView::GetCaptureSequenceNumber() const { uint32_t OffScreenRenderWidgetHostView::GetCaptureSequenceNumber() const {
return latest_capture_sequence_number_; return latest_capture_sequence_number_;
} }
@@ -698,7 +747,6 @@ void OffScreenRenderWidgetHostView::InitAsGuest(
content::RenderWidgetHostView* parent_host_view, content::RenderWidgetHostView* parent_host_view,
content::RenderWidgetHostViewGuest* guest_view) { content::RenderWidgetHostViewGuest* guest_view) {
parent_host_view_->AddGuestHostView(this); parent_host_view_->AddGuestHostView(this);
parent_host_view_->RegisterGuestViewFrameSwappedCallback(guest_view);
} }
void OffScreenRenderWidgetHostView::TransformPointToRootSurface( void OffScreenRenderWidgetHostView::TransformPointToRootSurface(
@@ -745,40 +793,6 @@ OffScreenRenderWidgetHostView::CreateViewForWidget(
render_widget_host, embedder_host_view, size()); 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 { const viz::FrameSinkId& OffScreenRenderWidgetHostView::GetFrameSinkId() const {
return GetDelegatedFrameHost()->frame_sink_id(); return GetDelegatedFrameHost()->frame_sink_id();
} }
@@ -875,21 +889,6 @@ void OffScreenRenderWidgetHostView::ProxyViewDestroyed(
Invalidate(); 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> std::unique_ptr<viz::SoftwareOutputDevice>
OffScreenRenderWidgetHostView::CreateSoftwareOutputDevice( OffScreenRenderWidgetHostView::CreateSoftwareOutputDevice(
ui::Compositor* compositor) { ui::Compositor* compositor) {
@@ -925,7 +924,7 @@ void OffScreenRenderWidgetHostView::SetNeedsBeginFrames(
begin_frame_timer_->SetActive(needs_begin_frames); begin_frame_timer_->SetActive(needs_begin_frames);
if (software_output_device_) { 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, void OffScreenRenderWidgetHostView::OnPaint(const gfx::Rect& damage_rect,
const SkBitmap& bitmap) { const SkBitmap& bitmap) {
TRACE_EVENT0("electron", "OffScreenRenderWidgetHostView::OnPaint");
HoldResize(); HoldResize();
if (parent_callback_) { if (parent_callback_) {
@@ -1023,10 +1020,6 @@ void OffScreenRenderWidgetHostView::SynchronizeVisualProperties() {
} }
ResizeRootLayer(false); ResizeRootLayer(false);
if (render_widget_host_)
render_widget_host_->SynchronizeVisualProperties();
GetDelegatedFrameHost()->EmbedSurface(
local_surface_id_, size_, cc::DeadlinePolicy::UseDefaultDeadline());
} }
void OffScreenRenderWidgetHostView::SendMouseEvent( void OffScreenRenderWidgetHostView::SendMouseEvent(
@@ -1192,6 +1185,13 @@ ui::Layer* OffScreenRenderWidgetHostView::GetRootLayer() const {
return root_layer_.get(); 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* content::DelegatedFrameHost*
OffScreenRenderWidgetHostView::GetDelegatedFrameHost() const { OffScreenRenderWidgetHostView::GetDelegatedFrameHost() const {
return delegated_frame_host_.get(); return delegated_frame_host_.get();
@@ -1204,11 +1204,6 @@ void OffScreenRenderWidgetHostView::SetupFrameRate(bool force) {
frame_rate_threshold_us_ = 1000000 / frame_rate_; frame_rate_threshold_us_ = 1000000 / frame_rate_;
if (GetCompositor()) {
GetCompositor()->SetAuthoritativeVSyncInterval(
base::TimeDelta::FromMicroseconds(frame_rate_threshold_us_));
}
if (copy_frame_generator_.get()) { if (copy_frame_generator_.get()) {
copy_frame_generator_->set_frame_rate_threshold_us( copy_frame_generator_->set_frame_rate_threshold_us(
frame_rate_threshold_us_); frame_rate_threshold_us_);
@@ -1257,28 +1252,32 @@ void OffScreenRenderWidgetHostView::ResizeRootLayer(bool force) {
size == GetRootLayer()->bounds().size()) size == GetRootLayer()->bounds().size())
return; 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)); GetRootLayer()->SetBounds(gfx::Rect(size));
GetCompositor()->SetScaleAndSize(current_device_scale_factor_, size_in_pixels,
local_surface_id_);
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
bool resized = UpdateNSViewAndDisplay(); bool resized = UpdateNSViewAndDisplay();
#else #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; bool resized = true;
GetDelegatedFrameHost()->EmbedSurface( GetDelegatedFrameHost()->EmbedSurface(
local_surface_id_, size, cc::DeadlinePolicy::UseDefaultDeadline()); local_surface_id_allocation_.local_surface_id(), size,
cc::DeadlinePolicy::UseDefaultDeadline());
#endif #endif
// Note that |render_widget_host_| will retrieve resize parameters from the // Note that |render_widget_host_| will retrieve resize parameters from the
// DelegatedFrameHost, so it must have SynchronizeVisualProperties called // DelegatedFrameHost, so it must have SynchronizeVisualProperties called
// after. // after.
if (resized && render_widget_host_) if (resized && render_widget_host_) {
render_widget_host_->SynchronizeVisualProperties(); render_widget_host_->SynchronizeVisualProperties();
}
} }
viz::FrameSinkId OffScreenRenderWidgetHostView::AllocateFrameSinkId( viz::FrameSinkId OffScreenRenderWidgetHostView::AllocateFrameSinkId(

View File

@@ -66,13 +66,12 @@ class AtomBeginFrameTimer;
#if defined(OS_MACOSX) #if defined(OS_MACOSX)
class MacHelper; class MacHelper;
#else
class AtomDelegatedFrameHostClient;
#endif #endif
class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase, class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
public ui::CompositorDelegate, public ui::CompositorDelegate,
#if !defined(OS_MACOSX)
public content::DelegatedFrameHostClient,
#endif
public OffscreenViewProxyObserver { public OffscreenViewProxyObserver {
public: public:
OffScreenRenderWidgetHostView(bool transparent, OffScreenRenderWidgetHostView(bool transparent,
@@ -118,7 +117,6 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
void SetActive(bool active) override; void SetActive(bool active) override;
void ShowDefinitionForSelection() override; void ShowDefinitionForSelection() override;
void SpeakSelection() override; void SpeakSelection() override;
bool ShouldContinueToPauseForFrame() override;
bool UpdateNSViewAndDisplay(); bool UpdateNSViewAndDisplay();
#endif // defined(OS_MACOSX) #endif // defined(OS_MACOSX)
@@ -144,8 +142,6 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
void Destroy(void) override; void Destroy(void) override;
void SetTooltipText(const base::string16&) override; void SetTooltipText(const base::string16&) override;
content::CursorManager* GetCursorManager() override; content::CursorManager* GetCursorManager() override;
void SelectionBoundsChanged(
const ViewHostMsg_SelectionBounds_Params&) override;
void CopyFromSurface( void CopyFromSurface(
const gfx::Rect& src_rect, const gfx::Rect& src_rect,
const gfx::Size& output_size, const gfx::Size& output_size,
@@ -170,18 +166,8 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
content::RenderWidgetHost*, content::RenderWidgetHost*,
content::WebContentsView*) override; content::WebContentsView*) override;
#if !defined(OS_MACOSX) const viz::LocalSurfaceIdAllocation& GetLocalSurfaceIdAllocation()
// content::DelegatedFrameHostClient: const override;
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::FrameSinkId& GetFrameSinkId() const override; const viz::FrameSinkId& GetFrameSinkId() const override;
void DidNavigate() override; void DidNavigate() override;
@@ -220,16 +206,13 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
void RemoveViewProxy(OffscreenViewProxy* proxy); void RemoveViewProxy(OffscreenViewProxy* proxy);
void ProxyViewDestroyed(OffscreenViewProxy* proxy) override; 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 OnPaint(const gfx::Rect& damage_rect, const SkBitmap& bitmap);
void OnPopupPaint(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; 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 HoldResize();
void ReleaseResize(); void ReleaseResize();
@@ -322,7 +305,7 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
bool paint_callback_running_ = false; bool paint_callback_running_ = false;
viz::LocalSurfaceId local_surface_id_; viz::LocalSurfaceIdAllocation local_surface_id_allocation_;
viz::ParentLocalSurfaceIdAllocator local_surface_id_allocator_; viz::ParentLocalSurfaceIdAllocator local_surface_id_allocator_;
std::unique_ptr<ui::Layer> root_layer_; std::unique_ptr<ui::Layer> root_layer_;
@@ -349,6 +332,8 @@ class OffScreenRenderWidgetHostView : public content::RenderWidgetHostViewBase,
// Selected text on the renderer. // Selected text on the renderer.
std::string selected_text_; std::string selected_text_;
#else
std::unique_ptr<AtomDelegatedFrameHostClient> delegated_frame_host_client_;
#endif #endif
content::MouseWheelPhaseHandler mouse_wheel_phase_handler_; content::MouseWheelPhaseHandler mouse_wheel_phase_handler_;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -179,11 +179,20 @@ const char kDisableHttpCache[] = "disable-http-cache";
const char kStandardSchemes[] = "standard-schemes"; const char kStandardSchemes[] = "standard-schemes";
// Register schemes to handle service worker. // Register schemes to handle service worker.
const char kRegisterServiceWorkerSchemes[] = "register-service-worker-schemes"; const char kServiceWorkerSchemes[] = "service-worker-schemes";
// Register schemes as secure. // Register schemes as secure.
const char kSecureSchemes[] = "secure-schemes"; 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 // The browser process app model ID
const char kAppUserModelId[] = "app-user-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 kPpapiFlashVersion[];
extern const char kDisableHttpCache[]; extern const char kDisableHttpCache[];
extern const char kStandardSchemes[]; extern const char kStandardSchemes[];
extern const char kRegisterServiceWorkerSchemes[]; extern const char kServiceWorkerSchemes[];
extern const char kSecureSchemes[]; extern const char kSecureSchemes[];
extern const char kBypassCSPSchemes[];
extern const char kFetchSchemes[];
extern const char kCORSSchemes[];
extern const char kAppUserModelId[]; extern const char kAppUserModelId[];
extern const char kAppPath[]; extern const char kAppPath[];

View File

@@ -4,6 +4,8 @@
#include "atom/renderer/api/atom_api_web_frame.h" #include "atom/renderer/api/atom_api_web_frame.h"
#include <utility>
#include "atom/common/api/api_messages.h" #include "atom/common/api/api_messages.h"
#include "atom/common/api/event_emitter_caller.h" #include "atom/common/api/event_emitter_caller.h"
#include "atom/common/native_mate_converters/blink_converter.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_execution_callback.h"
#include "third_party/blink/public/web/web_script_source.h" #include "third_party/blink/public/web/web_script_source.h"
#include "third_party/blink/public/web/web_view.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 "url/url_util.h"
#include "atom/common/node_includes.h" #include "atom/common/node_includes.h"
@@ -137,6 +138,26 @@ class FrameSpellChecker : public content::RenderFrameVisitor {
} // namespace } // 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) WebFrame::WebFrame(v8::Isolate* isolate)
: web_frame_(blink::WebLocalFrame::FrameForCurrentContext()) { : web_frame_(blink::WebLocalFrame::FrameForCurrentContext()) {
Init(isolate); Init(isolate);
@@ -229,63 +250,15 @@ void WebFrame::SetSpellCheckProvider(mate::Arguments* args,
return; return;
} }
auto client = auto spell_check_client =
std::make_unique<SpellCheckClient>(language, args->isolate(), provider); std::make_unique<SpellCheckClient>(language, args->isolate(), provider);
// Set spellchecker for all live frames in the same process or // Set spellchecker for all live frames in the same process or
// in the sandbox mode for all live sub frames to this WebFrame. // in the sandbox mode for all live sub frames to this WebFrame.
FrameSpellChecker spell_checker( auto* render_frame = content::RenderFrame::FromWebFrame(web_frame_);
client.get(), content::RenderFrame::FromWebFrame(web_frame_)); FrameSpellChecker spell_checker(spell_check_client.get(), render_frame);
content::RenderFrame::ForEach(&spell_checker); content::RenderFrame::ForEach(&spell_checker);
spell_check_client_.swap(client); web_frame_->SetSpellCheckPanelHostClient(spell_check_client.get());
web_frame_->SetSpellCheckPanelHostClient(spell_check_client_.get()); new AtomWebFrameObserver(render_frame, std::move(spell_check_client));
}
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());
}
} }
void WebFrame::InsertText(const std::string& text) { void WebFrame::InsertText(const std::string& text) {
@@ -500,10 +473,6 @@ void WebFrame::BuildPrototype(v8::Isolate* isolate,
&WebFrame::AllowGuestViewElementDefinition) &WebFrame::AllowGuestViewElementDefinition)
.SetMethod("getWebFrameId", &WebFrame::GetWebFrameId) .SetMethod("getWebFrameId", &WebFrame::GetWebFrameId)
.SetMethod("setSpellCheckProvider", &WebFrame::SetSpellCheckProvider) .SetMethod("setSpellCheckProvider", &WebFrame::SetSpellCheckProvider)
.SetMethod("registerURLSchemeAsBypassingCSP",
&WebFrame::RegisterURLSchemeAsBypassingCSP)
.SetMethod("registerURLSchemeAsPrivileged",
&WebFrame::RegisterURLSchemeAsPrivileged)
.SetMethod("insertText", &WebFrame::InsertText) .SetMethod("insertText", &WebFrame::InsertText)
.SetMethod("insertCSS", &WebFrame::InsertCSS) .SetMethod("insertCSS", &WebFrame::InsertCSS)
.SetMethod("executeJavaScript", &WebFrame::ExecuteJavaScript) .SetMethod("executeJavaScript", &WebFrame::ExecuteJavaScript)

View File

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

View File

@@ -163,6 +163,25 @@ void RendererClientBase::RenderThreadStarted() {
blink::SchemeRegistry::RegisterURLSchemeAsSecure( blink::SchemeRegistry::RegisterURLSchemeAsSecure(
WTF::String::FromUTF8(scheme.data(), scheme.length())); 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. // Allow file scheme to handle service worker by default.
// FIXME(zcbenz): Can this be moved elsewhere? // FIXME(zcbenz): Can this be moved elsewhere?
blink::WebSecurityPolicy::RegisterURLSchemeAsAllowingServiceWorkers("file"); blink::WebSecurityPolicy::RegisterURLSchemeAsAllowingServiceWorkers("file");

View File

@@ -8,7 +8,7 @@ declare_args() {
# Allow running Electron as a node binary. # Allow running Electron as a node binary.
enable_run_as_node = true enable_run_as_node = true
enable_osr = false enable_osr = true
enable_view_api = false 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. 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) # Planned Breaking API Changes (4.0)
The following list includes the breaking API changes planned for Electron 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 **Note:** You should not use this module until the `ready` event of the app
module is emitted. module is emitted.
```javascript ```javascript
const { app, contentTracing } = require('electron') const { app, contentTracing } = require('electron')
@@ -43,11 +42,18 @@ The `contentTracing` module has the following methods:
* `callback` Function * `callback` Function
* `categories` String[] * `categories` String[]
Get a set of category groups. The category groups can change as new code paths Get a set of category groups. The category groups can change as new code paths are reached.
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)` ### `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 as soon as they receive the EnableRecording request. The `callback` will be
called once all child processes have acknowledged the `startRecording` request. 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)` ### `contentTracing.stopRecording(resultFilePath, callback)`
* `resultFilePath` String * `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 temporary file. The actual file path will be passed to `callback` if it's not
`null`. `null`.
### `contentTracing.startMonitoring(options, callback)` **[Deprecated Soon](promisification.md)**
* `options` Object ### `contentTracing.stopRecording(resultFilePath)`
* `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)`
* `resultFilePath` String * `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 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 trace data back to the main process. This helps to minimize the runtime overhead
operation to send the trace data over IPC and we would like to avoid unneeded of tracing since sending trace data over IPC can be an expensive operation. So,
runtime overhead from tracing. So, to end tracing, we must asynchronously ask to end tracing, we must asynchronously ask all child processes to flush any
all child processes to flush any pending trace data. 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 will be written into `resultFilePath` if it is not empty or into a
temporary file.
### `contentTracing.getTraceBufferUsage(callback)` ### `contentTracing.getTraceBufferUsage(callback)`

View File

@@ -13,21 +13,30 @@ For example:
const { session } = require('electron') const { session } = require('electron')
// Query all cookies. // Query all cookies.
session.defaultSession.cookies.get({}, (error, cookies) => { session.defaultSession.cookies.get({})
console.log(error, cookies) .then((cookies) => {
}) console.log(cookies)
}).catch((error) => {
console.log(error)
})
// Query all cookies associated with a specific url. // Query all cookies associated with a specific url.
session.defaultSession.cookies.get({ url: 'http://www.github.com' }, (error, cookies) => { session.defaultSession.cookies.get({ url: 'http://www.github.com' })
console.log(error, cookies) .then((cookies) => {
}) console.log(cookies)
}).catch((error) => {
console.log(error)
})
// Set a cookie with the given cookie data; // Set a cookie with the given cookie data;
// may overwrite equivalent cookies if they exist. // may overwrite equivalent cookies if they exist.
const cookie = { url: 'http://www.github.com', name: 'dummy_name', value: 'dummy' } const cookie = { url: 'http://www.github.com', name: 'dummy_name', value: 'dummy' }
session.defaultSession.cookies.set(cookie, (error) => { session.defaultSession.cookies.set(cookie)
if (error) console.error(error) .then(() => {
}) // success
}, (error) => {
console.error(error)
})
``` ```
### Instance Events ### Instance Events
@@ -55,6 +64,23 @@ expired.
The following methods are available on instances of `Cookies`: 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)` #### `cookies.get(filter, callback)`
* `filter` Object * `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 Sends a request to get all cookies matching `filter`, `callback` will be called
with `callback(error, cookies)` on complete. 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)` #### `cookies.set(details, callback)`
* `details` Object * `details` Object
@@ -94,6 +142,17 @@ with `callback(error, cookies)` on complete.
Sets a cookie with `details`, `callback` will be called with `callback(error)` Sets a cookie with `details`, `callback` will be called with `callback(error)`
on complete. 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)` #### `cookies.remove(url, name, callback)`
* `url` String - The URL associated with the cookie. * `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 Removes the cookies matching `url` and `name`, `callback` will called with
`callback()` on complete. `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)` #### `cookies.flushStore(callback)`
* `callback` Function * `callback` Function
Writes any unwritten cookies data to disk. 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, MenuItem } = remote
const menu = new Menu() 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({ type: 'separator' }))
menu.append(new MenuItem({ label: 'MenuItem2', type: 'checkbox', checked: true })) 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) - [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.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) - [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) - [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) - [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.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) - [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 ### 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) - [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) - [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: The `protocol` module has the following methods:
### `protocol.registerStandardSchemes(schemes[, options])` ### `protocol.registerSchemesAsPrivileged(customSchemes)`
* `schemes` String[] - Custom schemes to be registered as standard schemes. * `customSchemes` [CustomScheme[]](structures/custom-scheme.md)
* `options` Object (optional)
* `secure` Boolean (optional) - `true` to register the scheme as secure.
Default `false`. **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 A standard scheme adheres to what RFC 3986 calls [generic URI
syntax](https://tools.ietf.org/html/rfc3986#section-3). For example `http` and 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) 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 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: 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.
### `protocol.registerFileProtocol(scheme, handler[, completion])` ### `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)` ### `webFrame.insertText(text)`
* `text` String * `text` String

View File

@@ -32,7 +32,7 @@ const filter = {
session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => { session.defaultSession.webRequest.onBeforeSendHeaders(filter, (details, callback) => {
details.requestHeaders['User-Agent'] = 'MyAgent' 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 * `responseHeaders` Object
* `callback` Function * `callback` Function
* `response` Object * `response` Object
* `cancel` Boolean * `cancel` Boolean (optional)
* `responseHeaders` Object (optional) - When provided, the server is assumed * `responseHeaders` Object (optional) - When provided, the server is assumed
to have responded with these headers. to have responded with these headers.
* `statusLine` String (optional) - Should be provided when overriding * `statusLine` String (optional) - Should be provided when overriding
@@ -200,6 +200,7 @@ redirect is about to occur.
* `method` String * `method` String
* `webContentsId` Integer (optional) * `webContentsId` Integer (optional)
* `resourceType` String * `resourceType` String
* `referrer` String
* `timestamp` Double * `timestamp` Double
* `responseHeaders` Object * `responseHeaders` Object
* `fromCache` Boolean * `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 const nativeAppMetrics = app.getAppMetrics
app.getAppMetrics = () => { app.getAppMetrics = () => {

View File

@@ -1,3 +1,9 @@
'use strict' '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 { EventEmitter } = require('events')
const { app, deprecate } = require('electron') 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. // Public API.
Object.defineProperties(exports, { Object.defineProperties(exports, {
@@ -20,6 +35,6 @@ Object.setPrototypeOf(Session.prototype, EventEmitter.prototype)
Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype) Object.setPrototypeOf(Cookies.prototype, EventEmitter.prototype)
Session.prototype._init = function () { 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) 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. // Add JavaScript wrappers for WebContents class.
WebContents.prototype._init = function () { WebContents.prototype._init = function () {
// The navigation controller. // The navigation controller.
@@ -367,7 +378,7 @@ WebContents.prototype._init = function () {
// render-view-deleted event, so ignore the listeners warning. // render-view-deleted event, so ignore the listeners warning.
this.setMaxListeners(0) this.setMaxListeners(0)
this.capturePage = deprecate.promisify(this.capturePage, 2) this.capturePage = deprecate.promisify(this.capturePage)
// Dispatch IPC messages to the ipc module. // Dispatch IPC messages to the ipc module.
this.on('-ipc-message', function (event, [channel, ...args]) { this.on('-ipc-message', function (event, [channel, ...args]) {
@@ -428,7 +439,9 @@ WebContents.prototype._init = function () {
for (const eventName of forwardedEvents) { for (const eventName of forwardedEvents) {
this.on(eventName, (event, ...args) => { 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. // 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". // Make new windows requested by links behave like "window.open".
this.on('-new-window', (event, url, frameName, disposition, this.on('-new-window', (event, url, frameName, disposition,
additionalFeatures, postData, 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)) setReturnValue(event, () => electron.clipboard.writeFindText(text))
}) })
ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) { const getPreloadScript = function (preloadPath) {
const preloadPath = event.sender._getPreloadPath()
let preloadSrc = null let preloadSrc = null
let preloadError = null let preloadError = null
if (preloadPath) { if (preloadPath) {
@@ -545,10 +544,17 @@ ipcMain.on('ELECTRON_BROWSER_SANDBOX_LOAD', function (event) {
preloadError = errorUtils.serialize(err) 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 = { event.returnValue = {
preloadPath, preloadScripts: preloadPaths.map(path => getPreloadScript(path)),
preloadSrc,
preloadError,
isRemoteModuleEnabled: event.sender._isRemoteModuleEnabled(), isRemoteModuleEnabled: event.sender._isRemoteModuleEnabled(),
isWebViewTagEnabled: guestViewManager.isWebViewTagEnabled(event.sender), isWebViewTagEnabled: guestViewManager.isWebViewTagEnabled(event.sender),
process: { process: {

View File

@@ -69,27 +69,27 @@ const deprecate = {
}) })
}, },
promisify: (fn, cbParamIndex) => { promisify: (fn) => {
const fnName = fn.name || 'function' const fnName = fn.name || 'function'
const oldName = `${fnName} with callbacks` const oldName = `${fnName} with callbacks`
const newName = `${fnName} with Promises` const newName = `${fnName} with Promises`
const warn = warnOnce(oldName, newName) const warn = warnOnce(oldName, newName)
return function (...params) { 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) const promise = fn.apply(this, params)
if (!cb) return promise
if (typeof cb !== 'function') return promise
if (process.enablePromiseAPIs) warn() if (process.enablePromiseAPIs) warn()
return promise return promise
.then(res => { .then(res => {
process.nextTick(() => { process.nextTick(() => {
cb(null, res) cb.length === 2 ? cb(null, res) : cb(res)
}) })
}, err => { }, err => {
process.nextTick(() => { process.nextTick(() => cb(err))
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) { module.exports = function (contextIsolation, webviewTag, guestInstanceId) {
// Load webview tag implementation. // Don't allow recursive `<webview>`.
if (webviewTag && guestInstanceId == null) { if (webviewTag && guestInstanceId == null) {
const webViewImpl = require('@electron/internal/renderer/web-view/web-view-impl') const webViewImpl = require('@electron/internal/renderer/web-view/web-view-impl')
if (contextIsolation) { if (contextIsolation) {

View File

@@ -29,7 +29,7 @@ Object.setPrototypeOf(process, EventEmitter.prototype)
const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal') const ipcRenderer = require('@electron/internal/renderer/ipc-renderer-internal')
const { const {
preloadPath, preloadSrc, preloadError, isRemoteModuleEnabled, isWebViewTagEnabled, process: processProps preloadScripts, isRemoteModuleEnabled, isWebViewTagEnabled, process: processProps
} = ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD') } = ipcRenderer.sendSync('ELECTRON_BROWSER_SANDBOX_LOAD')
process.isRemoteModuleEnabled = isRemoteModuleEnabled process.isRemoteModuleEnabled = isRemoteModuleEnabled
@@ -127,7 +127,9 @@ switch (window.location.protocol) {
const guestInstanceId = binding.guestInstanceId && parseInt(binding.guestInstanceId) const guestInstanceId = binding.guestInstanceId && parseInt(binding.guestInstanceId)
// Load webview tag implementation. // 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') const errorUtils = require('@electron/internal/common/error-utils')
@@ -162,18 +164,22 @@ function runPreloadScript (preloadSrc) {
preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate, clearImmediate) preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate, clearImmediate)
} }
try { for (const { preloadPath, preloadSrc, preloadError } of preloadScripts) {
if (preloadSrc) { try {
runPreloadScript(preloadSrc) if (preloadSrc) {
} else if (preloadError) { runPreloadScript(preloadSrc)
throw errorUtils.deserialize(preloadError) } else if (preloadError) {
} throw errorUtils.deserialize(preloadError)
} catch (error) { }
console.error(`Unable to load preload script: ${preloadPath}`) } catch (error) {
console.error(`${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 // 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", "name": "electron",
"version": "5.0.0-nightly.20190122", "version": "5.0.0-beta.2",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
@@ -2951,9 +2951,9 @@
} }
}, },
"electron-typescript-definitions": { "electron-typescript-definitions": {
"version": "4.0.0", "version": "6.0.0",
"resolved": "https://registry.npmjs.org/electron-typescript-definitions/-/electron-typescript-definitions-4.0.0.tgz", "resolved": "https://registry.npmjs.org/electron-typescript-definitions/-/electron-typescript-definitions-6.0.0.tgz",
"integrity": "sha512-UekaKgK8omivfj37xs1G09j5MctHCFSyPYvW67j30xYHVCvgjDLur1Vqd7CaaJCDuaYLsHjsmms4QjG8uOFb4A==", "integrity": "sha512-W2CHJ1bUtbDXXzIAEbQ+s2XewpSdUUqNECJmhniV0GbPoIhwjAg90++BkMxH6wPBxHWOxesvUMipUEeJN9+wlQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@types/node": "^7.0.18", "@types/node": "^7.0.18",
@@ -4384,7 +4384,8 @@
"version": "2.1.1", "version": "2.1.1",
"resolved": false, "resolved": false,
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=",
"dev": true "dev": true,
"optional": true
}, },
"aproba": { "aproba": {
"version": "1.2.0", "version": "1.2.0",
@@ -4849,7 +4850,8 @@
"version": "5.1.1", "version": "5.1.1",
"resolved": false, "resolved": false,
"integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==", "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==",
"dev": true "dev": true,
"optional": true
}, },
"safer-buffer": { "safer-buffer": {
"version": "2.1.2", "version": "2.1.2",
@@ -4913,6 +4915,7 @@
"resolved": false, "resolved": false,
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"dev": true, "dev": true,
"optional": true,
"requires": { "requires": {
"ansi-regex": "^2.0.0" "ansi-regex": "^2.0.0"
} }
@@ -4961,13 +4964,15 @@
"version": "1.0.2", "version": "1.0.2",
"resolved": false, "resolved": false,
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=",
"dev": true "dev": true,
"optional": true
}, },
"yallist": { "yallist": {
"version": "3.0.2", "version": "3.0.2",
"resolved": false, "resolved": false,
"integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=", "integrity": "sha1-hFK0u36Dx8GI2AQcGoN8dz1ti7k=",
"dev": true "dev": true,
"optional": true
} }
} }
}, },
@@ -13104,9 +13109,9 @@
} }
}, },
"write-file-atomic": { "write-file-atomic": {
"version": "2.3.0", "version": "2.4.2",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.3.0.tgz", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.2.tgz",
"integrity": "sha512-xuPeK4OdjWqtfi59ylvVL0Yn35SF3zgcAcv7rBPFHVaEapaDr4GdGgm3j7ckTwH9wHL7fGmgfAnb0+THrHb8tA==", "integrity": "sha512-s0b6vB3xIVRLWywa6X9TOMA7k9zio0TMOsl9ZnDkliA/cfJlpHXAscj0gbHVJiTdIuAYpIyqS5GW91fqm6gG5g==",
"dev": true, "dev": true,
"requires": { "requires": {
"graceful-fs": "^4.1.11", "graceful-fs": "^4.1.11",

View File

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

View File

@@ -1,2 +1,4 @@
add_ec_group_order_bits_for_openssl_compatibility.patch add_ec_group_order_bits_for_openssl_compatibility.patch
add_ec_key_key2buf_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 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Jeremy Apthorp <nornagon@nornagon.net> From: Jeremy Apthorp <nornagon@nornagon.net>
Date: Thu, 20 Sep 2018 17:48:05 -0700 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 Build BoringSSL with some extra functions that nodejs needs.
the GN build; with the GYP build, nodejs is still built with OpenSSL.
diff --git a/third_party/boringssl/BUILD.gn b/third_party/boringssl/BUILD.gn 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 --- a/third_party/boringssl/BUILD.gn
+++ b/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_sources = crypto_sources + ssl_sources
all_headers = crypto_headers + ssl_headers all_headers = crypto_headers + ssl_headers
@@ -20,6 +19,12 @@ index 6e4fc85f124ed6dd4a7ac1812686fa59c4e50cdf..2fbd44c0ac71bbd84706ef50dd0d98b2
+ "src/decrepit/evp/evp_do_all.c", + "src/decrepit/evp/evp_do_all.c",
+ "src/decrepit/xts/xts.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 # 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)) 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 // 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 () { 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 // copied from https://github.com/electron/clerk/blob/master/src/index.ts#L4-L13
const OMIT_FROM_RELEASE_NOTES_KEYS = [ const OMIT_FROM_RELEASE_NOTES_KEYS = [
'no-notes', 'no-notes',
@@ -81,10 +110,12 @@ const getNoteFromBody = body => {
} }
const NOTE_PREFIX = 'Notes: ' const NOTE_PREFIX = 'Notes: '
const NOTE_HEADER = '#### Release Notes'
let note = body let note = body
.split(/\r?\n\r?\n/) // split into paragraphs .split(/\r?\n\r?\n/) // split into paragraphs
.map(paragraph => paragraph.trim()) .map(paragraph => paragraph.trim())
.map(paragraph => paragraph.startsWith(NOTE_HEADER) ? paragraph.slice(NOTE_HEADER.length).trim() : paragraph)
.find(paragraph => paragraph.startsWith(NOTE_PREFIX)) .find(paragraph => paragraph.startsWith(NOTE_PREFIX))
if (note) { if (note) {
@@ -185,9 +216,9 @@ const parseCommitMessage = (commitMessage, owner, repo, commit = {}) => {
// https://www.conventionalcommits.org/en // https://www.conventionalcommits.org/en
if (commitMessage if (commitMessage
.split(/\r?\n\r?\n/) // split into paragraphs .split(/\r?\n/) // split into lines
.map(paragraph => paragraph.trim()) .map(line => line.trim())
.some(paragraph => paragraph.startsWith('BREAKING CHANGE'))) { .some(line => line.startsWith('BREAKING CHANGE'))) {
commit.type = '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 addRepoToPool = async (pool, repo, from, to) => {
const commonAncestor = await getCommonAncestor(repo.dir, from, to) const commonAncestor = await getCommonAncestor(repo.dir, from, to)
const oldHashes = await getLocalCommitHashes(repo.dir, from) const oldHashes = await getLocalCommitHashes(repo.dir, from)
@@ -393,6 +440,28 @@ const getDependencyCommits = async (pool, from, to) => {
: getDependencyCommitsGN(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 **** Main
***/ ***/
@@ -445,8 +514,19 @@ const getNotes = async (fromRef, toRef, newVersion) => {
// scrape PRs for release note 'Notes:' comments // scrape PRs for release note 'Notes:' comments
for (const commit of pool.commits) { for (const commit of pool.commits) {
let pr = commit.pr let pr = commit.pr
let prSubject let prSubject
while (pr && !commit.note) { 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) const prData = await getPullRequest(pr.number, pr.owner, pr.repo)
if (!prData || !prData.data) { if (!prData || !prData.data) {
break break
@@ -472,9 +552,7 @@ const getNotes = async (fromRef, toRef, newVersion) => {
.filter(commit => commit.note !== NO_NOTES) .filter(commit => commit.note !== NO_NOTES)
.filter(commit => !((commit.note || commit.subject).match(/^[Bb]ump v\d+\.\d+\.\d+/))) .filter(commit => !((commit.note || commit.subject).match(/^[Bb]ump v\d+\.\d+\.\d+/)))
// if this is a stable release, if (!shouldIncludeMultibranchChanges(newVersion)) {
// remove notes for changes that already landed in a previous major/minor series
if (semver.valid(newVersion) && !semver.prerelease(newVersion)) {
// load all the prDatas // load all the prDatas
await Promise.all( await Promise.all(
pool.commits.map(commit => new Promise(async (resolve) => { 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 iconPath = path.join(__dirname, 'fixtures/assets/icon.ico')
const sizes = { const sizes = {
small: 16, small: 16,
@@ -859,7 +859,7 @@ describe('app module', () => {
}) })
// TODO(codebytere): remove when promisification is complete // 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) => { app.getFileIcon(iconPath, (icon) => {
expect(icon.isEmpty()).to.be.false() expect(icon.isEmpty()).to.be.false()
done() done()
@@ -875,7 +875,7 @@ describe('app module', () => {
}) })
// TODO(codebytere): remove when promisification is complete // 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) => { app.getFileIcon(iconPath, (icon) => {
const size = icon.getSize() const size = icon.getSize()
@@ -903,7 +903,7 @@ describe('app module', () => {
}) })
// TODO(codebytere): remove when promisification is complete // 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) => { app.getFileIcon(iconPath, { size: 'normal' }, (icon) => {
const size = icon.getSize() const size = icon.getSize()

View File

@@ -507,7 +507,7 @@ describe('BrowserWindow module', () => {
}) })
// TODO(codebytere): remove when promisification is complete // 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({ w.capturePage({
x: 0, x: 0,
y: 0, y: 0,
@@ -540,24 +540,25 @@ describe('BrowserWindow module', () => {
}) })
// TODO(codebytere): remove when promisification is complete // TODO(codebytere): remove when promisification is complete
it('preserves transparency (callback)', async () => { it('preserves transparency (callback)', (done) => {
const w = await openTheWindow({ openTheWindow({
show: false, show: false,
width: 400, width: 400,
height: 400, height: 400,
transparent: true transparent: true
}) }).then(w => {
const p = emittedOnce(w, 'ready-to-show') const p = emittedOnce(w, 'ready-to-show')
w.loadURL('data:text/html,<html><body background-color: rgba(255,255,255,0)></body></html>') w.loadURL('data:text/html,<html><body background-color: rgba(255,255,255,0)></body></html>')
await p p.then(() => {
w.show() w.show()
w.capturePage((image) => {
w.capturePage((image) => { const imgBuffer = image.toPNG()
const imgBuffer = image.toPNG() // Check the 25th byte in the PNG.
// Check the 25th byte in the PNG. // Values can be 0,2,3,4, or 6. We want 6, which is RGB + Alpha
// Values can be 0,2,3,4, or 6. We want 6, which is RGB + Alpha expect(imgBuffer[25]).to.equal(6)
expect(imgBuffer[25]).to.equal(6) done()
done() })
})
}) })
}) })
}) })
@@ -1382,23 +1383,29 @@ describe('BrowserWindow module', () => {
assert.deepStrictEqual(defaultSession.getPreloads(), preloads) assert.deepStrictEqual(defaultSession.getPreloads(), preloads)
}) })
it('loads the script before other scripts in window including normal preloads', function (done) { const generateSpecs = (description, sandbox) => {
ipcMain.once('vars', function (event, preload1, preload2, preload3) { describe(description, () => {
assert.strictEqual(preload1, 'preload-1') it('loads the script before other scripts in window including normal preloads', function (done) {
assert.strictEqual(preload2, 'preload-1-2') ipcMain.once('vars', function (event, preload1, preload2) {
assert.strictEqual(preload3, 'preload-1-2-3') assert.strictEqual(preload1, 'preload-1')
done() 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, generateSpecs('without sandbox', false)
webPreferences: { generateSpecs('with sandbox', true)
nodeIntegration: true,
preload: path.join(fixtures, 'module', 'set-global-preload-3.js')
}
})
w.loadFile(path.join(fixtures, 'api', 'preloads.html'))
})
}) })
describe('"additionalArguments" option', () => { 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) => { const startRecording = async (options) => {
return new Promise((resolve) => { return new Promise((resolve) => {
contentTracing.startRecording(options, () => { contentTracing.startRecording(options, () => {
@@ -36,6 +58,7 @@ describe('contentTracing', () => {
}) })
} }
// TODO(codebytere): remove when promisification is complete
const stopRecording = async (filePath) => { const stopRecording = async (filePath) => {
return new Promise((resolve) => { return new Promise((resolve) => {
contentTracing.stopRecording(filePath, (resultFilePath) => { 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') const outputFilePath = getPathInATempFolder('trace.json')
beforeEach(() => { beforeEach(() => {
if (fs.existsSync(outputFilePath)) { if (fs.existsSync(outputFilePath)) {
@@ -82,6 +95,18 @@ describe('contentTracing', () => {
`the trace output file is empty, check "${outputFilePath}"`) `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 () => { it('accepts a trace config', async () => {
// (alexeykuzmin): All categories are excluded on purpose, // (alexeykuzmin): All categories are excluded on purpose,
// so only metadata gets into the output file. // so only metadata gets into the output file.
@@ -104,6 +129,29 @@ describe('contentTracing', () => {
check "${outputFilePath}"`) 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 () => { it('accepts "categoryFilter" and "traceOptions" as a config', async () => {
// (alexeykuzmin): All categories are excluded on purpose, // (alexeykuzmin): All categories are excluded on purpose,
// so only metadata gets into the output file. // so only metadata gets into the output file.
@@ -126,6 +174,30 @@ describe('contentTracing', () => {
`the trace output file is suspiciously large (${fileSizeInKiloBytes}KB), `the trace output file is suspiciously large (${fileSizeInKiloBytes}KB),
check "${outputFilePath}"`) 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 () { describe('stopRecording', function () {
@@ -136,6 +208,12 @@ describe('contentTracing', () => {
expect(resultFilePath).to.be.a('string').and.be.equal(outputFilePath) 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 // FIXME(alexeykuzmin): https://github.com/electron/electron/issues/16019
xit('creates a temporary file when an empty string is passed', async function () { xit('creates a temporary file when an empty string is passed', async function () {
const resultFilePath = await record(/* options */ {}, /* outputFilePath */ '') 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 () => { it('acts as a pass-through for promise-based invocations', async () => {
enableCallbackWarnings() enableCallbackWarnings()
promiseFunc = deprecate.promisify(promiseFunc, 1) promiseFunc = deprecate.promisify(promiseFunc)
const actual = await promiseFunc(expected) const actual = await promiseFunc(expected)
expect(actual).to.equal(expected) expect(actual).to.equal(expected)
@@ -147,7 +147,7 @@ describe('deprecations', () => {
it('warns exactly once for callback-based invocations', (done) => { it('warns exactly once for callback-based invocations', (done) => {
enableCallbackWarnings() enableCallbackWarnings()
promiseFunc = deprecate.promisify(promiseFunc, 1) promiseFunc = deprecate.promisify(promiseFunc)
let callbackCount = 0 let callbackCount = 0
const invocationCount = 3 const invocationCount = 3

View File

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

View File

@@ -550,12 +550,12 @@ describe('net module', () => {
handleUnexpectedURL(request, response) handleUnexpectedURL(request, response)
} }
}) })
customSession.cookies.set({ customSession.cookies.set({
url: `${server.url}`, url: `${server.url}`,
name: 'test', name: 'test',
value: '11111' value: '11111'
}, (error) => { }).then(() => { // resolved
if (error) return done(error)
const urlRequest = net.request({ const urlRequest = net.request({
method: 'GET', method: 'GET',
url: `${server.url}${requestUrl}`, url: `${server.url}${requestUrl}`,
@@ -575,6 +575,8 @@ describe('net module', () => {
assert.strictEqual(urlRequest.getHeader(cookieHeaderName), assert.strictEqual(urlRequest.getHeader(cookieHeaderName),
cookieHeaderValue) cookieHeaderValue)
urlRequest.end() 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 () => { it('returns true for about:', async () => {
const result = await protocol.isProtocolHandled('about') const result = await protocol.isProtocolHandled('about')
assert.strictEqual(result, true) assert.strictEqual(result, true)
}) })
// TODO(codebytere): remove when promisification is complete // TODO(codebytere): remove when promisification is complete
it('returns true for about: (callback)', () => { it('returns true for about: (callback)', (done) => {
protocol.isProtocolHandled('about', (result) => { protocol.isProtocolHandled('about', (result) => {
assert.strictEqual(result, true) assert.strictEqual(result, true)
done() done()
@@ -676,7 +700,7 @@ describe('protocol module', () => {
}) })
// TODO(codebytere): remove when promisification is complete // TODO(codebytere): remove when promisification is complete
it('returns true for file: (callback)', () => { it('returns true for file: (callback)', (done) => {
protocol.isProtocolHandled('file', (result) => { protocol.isProtocolHandled('file', (result) => {
assert.strictEqual(result, true) assert.strictEqual(result, true)
done() done()
@@ -698,7 +722,7 @@ describe('protocol module', () => {
assert.strictEqual(result, false) assert.strictEqual(result, false)
}) })
it('returns true for custom protocol', () => { it('returns true for custom protocol', (done) => {
const emptyHandler = (request, callback) => callback() const emptyHandler = (request, callback) => callback()
protocol.registerStringProtocol(protocolName, emptyHandler, async (error) => { protocol.registerStringProtocol(protocolName, emptyHandler, async (error) => {
assert.strictEqual(error, null) assert.strictEqual(error, null)
@@ -709,7 +733,7 @@ describe('protocol module', () => {
}) })
// TODO(codebytere): remove when promisification is complete // 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() const emptyHandler = (request, callback) => callback()
protocol.registerStringProtocol(protocolName, emptyHandler, (error) => { protocol.registerStringProtocol(protocolName, emptyHandler, (error) => {
assert.strictEqual(error, null) 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() const emptyHandler = (request, callback) => callback()
protocol.interceptStringProtocol('http', emptyHandler, async (error) => { protocol.interceptStringProtocol('http', emptyHandler, async (error) => {
assert.strictEqual(error, null) assert.strictEqual(error, null)
@@ -731,7 +755,7 @@ describe('protocol module', () => {
}) })
// TODO(codebytere): remove when promisification is complete // 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() const emptyHandler = (request, callback) => callback()
protocol.interceptStringProtocol('http', emptyHandler, (error) => { protocol.interceptStringProtocol('http', emptyHandler, (error) => {
assert.strictEqual(error, null) assert.strictEqual(error, null)
@@ -1063,7 +1087,7 @@ describe('protocol module', () => {
}) })
}) })
describe('protocol.registerStandardSchemes', () => { describe('protocol.registerSchemesAsPrivileged standard', () => {
const standardScheme = remote.getGlobal('standardScheme') const standardScheme = remote.getGlobal('standardScheme')
const origin = `${standardScheme}://fake-host` const origin = `${standardScheme}://fake-host`
const imageURL = `${origin}/test.png` 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', () => { 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) => { const server = http.createServer((req, res) => {
res.setHeader('Set-Cookie', ['0=0']) res.setHeader('Set-Cookie', [`${name}=${value}`])
res.end('finished') res.end('finished')
server.close() server.close()
}) })
server.listen(0, '127.0.0.1', () => { server.listen(0, '127.0.0.1', () => {
const port = server.address().port w.webContents.once('did-finish-load', async () => {
w.webContents.once('did-finish-load', () => { const list = await w.webContents.session.cookies.get({ url })
w.webContents.session.cookies.get({ url }, (error, list) => { const cookie = list.find(cookie => cookie.name === name)
if (error) return done(error)
for (let i = 0; i < list.length; i++) { expect(cookie).to.exist.and.to.have.property('value', value)
const cookie = list[i] done()
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')
})
}) })
const { port } = server.address()
w.loadURL(`${url}:${port}`) w.loadURL(`${url}:${port}`)
}) })
}) })
it('calls back with an error when setting a cookie with missing required fields', (done) => { it('should get cookies with callbacks', (done) => {
session.defaultSession.cookies.set({ const server = http.createServer((req, res) => {
url: '', res.setHeader('Set-Cookie', [`${name}=${value}`])
name: '1', res.end('finished')
value: '1' server.close()
}, (error) => {
assert(error, 'Should have an error')
assert.strictEqual(error.message, 'Setting cookie failed')
done()
}) })
}) server.listen(0, '127.0.0.1', () => {
w.webContents.once('did-finish-load', () => {
it('should over-write the existent cookie', (done) => { w.webContents.session.cookies.get({ url }, (error, list) => {
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) => {
if (error) return done(error) if (error) return done(error)
for (let i = 0; i < list.length; i++) { const cookie = list.find(cookie => cookie.name === name)
const cookie = list[i] expect(cookie).to.exist.and.to.have.property('value', value)
if (cookie.name === '2') return done('Cookie not deleted') 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() done()
}) })
}) })
}) })
}) })
it('should set cookie for standard scheme', (done) => { it('should set cookie for standard scheme', async () => {
const standardScheme = remote.getGlobal('standardScheme') let error
const origin = standardScheme + '://fake-host' try {
session.defaultSession.cookies.set({ const { cookies } = session.defaultSession
url: origin, const standardScheme = remote.getGlobal('standardScheme')
name: 'custom', const domain = 'fake-host'
value: '1' const url = `${standardScheme}://${domain}`
}, (error) => { const name = 'custom'
if (error) return done(error) const value = '1'
session.defaultSession.cookies.get({ url: origin }, (error, list) => {
if (error) return done(error) await cookies.set({ url, name, value })
assert.strictEqual(list.length, 1) const list = await cookies.get({ url })
assert.strictEqual(list[0].name, 'custom')
assert.strictEqual(list[0].value, '1') expect(list).to.have.lengthOf(1)
assert.strictEqual(list[0].domain, 'fake-host') expect(list[0]).to.have.property('name', name)
done() 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) => { it('emits a changed event when a cookie is added or removed', async () => {
const { cookies } = session.fromPartition('cookies-changed') let error
const changes = []
cookies.once('changed', (event, cookie, cause, removed) => { try {
assert.strictEqual(cookie.name, 'foo') const { cookies } = session.fromPartition('cookies-changed')
assert.strictEqual(cookie.value, 'bar') const name = 'foo'
assert.strictEqual(cause, 'explicit') const value = 'bar'
assert.strictEqual(removed, false) const eventName = 'changed'
const listener = (event, cookie, cause, removed) => { changes.push({ cookie, cause, removed }) }
cookies.once('changed', (event, cookie, cause, removed) => { cookies.on(eventName, listener)
assert.strictEqual(cookie.name, 'foo') await cookies.set({ url, name, value })
assert.strictEqual(cookie.value, 'bar') await cookies.remove(url, name)
assert.strictEqual(cause, 'explicit') cookies.off(eventName, listener)
assert.strictEqual(removed, true)
done()
})
cookies.remove(url, 'foo', (error) => { expect(changes).to.have.lengthOf(2)
if (error) return done(error) 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()
cookies.set({ expect(changes[1].removed).to.be.true()
url: url, } catch (e) {
name: 'foo', error = e
value: 'bar' }
}, (error) => { expect(error).to.be.undefined(error)
if (error) return done(error)
})
}) })
describe('ses.cookies.flushStore(callback)', () => { describe('ses.cookies.flushStore()', async () => {
it('flushes the cookies to disk and invokes the callback when done', (done) => { describe('flushes the cookies to disk and invokes the callback when done', async () => {
session.defaultSession.cookies.set({ it('with promises', async () => {
url: url, let error
name: 'foo', try {
value: 'bar' const name = 'foo'
}, (error) => { const value = 'bar'
if (error) return done(error) const { cookies } = session.defaultSession
session.defaultSession.cookies.flushStore(() => {
done() 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 } } { env: { PHASE: phase, ...process.env } }
) )
appProcess.stdout.on('data', (data) => { output += data }) appProcess.stdout.on('data', data => { output += data })
appProcess.stdout.on('end', () => { appProcess.stdout.on('end', () => {
output = output.replace(/(\r\n|\n|\r)/gm, '') output = output.replace(/(\r\n|\n|\r)/gm, '')
assert.strictEqual(output, result) assert.strictEqual(output, result)
@@ -620,12 +681,9 @@ describe('session module', () => {
}) })
}) })
// FIXME: Disabled with C71 upgrade describe('ses.getBlobData(identifier, callback)', () => {
// Re-enable with new api from
// https://github.com/electron/electron/tree/webframe-scheme-api
xdescribe('ses.getBlobData(identifier, callback)', () => {
it('returns blob data for uuid', (done) => { it('returns blob data for uuid', (done) => {
const scheme = 'temp' const scheme = 'cors-blob'
const protocol = session.defaultSession.protocol const protocol = session.defaultSession.protocol
const url = `${scheme}://host` const url = `${scheme}://host`
before(() => { before(() => {
@@ -648,8 +706,6 @@ describe('session module', () => {
}) })
const content = `<html> const content = `<html>
<script> <script>
const {webFrame} = require('electron')
webFrame.registerURLSchemeAsPrivileged('${scheme}')
let fd = new FormData(); let fd = new FormData();
fd.append('file', new Blob(['${postData}'], {type:'application/json'})); fd.append('file', new Blob(['${postData}'], {type:'application/json'}));
fetch('${url}', {method:'POST', body: fd }); fetch('${url}', {method:'POST', body: fd });

View File

@@ -4,7 +4,7 @@ const dirtyChai = require('dirty-chai')
const path = require('path') const path = require('path')
const { closeWindow } = require('./window-helpers') const { closeWindow } = require('./window-helpers')
const { remote, webFrame } = require('electron') const { remote, webFrame } = require('electron')
const { BrowserWindow, protocol, ipcMain } = remote const { BrowserWindow, ipcMain } = remote
const { expect } = chai const { expect } = chai
chai.use(dirtyChai) chai.use(dirtyChai)
@@ -20,124 +20,6 @@ describe('webFrame module', function () {
return closeWindow(w).then(function () { w = null }) 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 () { it('supports setting the visual and layout zoom level limits', function () {
assert.doesNotThrow(function () { assert.doesNotThrow(function () {
webFrame.setVisualZoomLevelLimits(1, 50) webFrame.setVisualZoomLevelLimits(1, 50)

View File

@@ -3,51 +3,30 @@ const { app, session } = require('electron')
app.on('ready', async function () { app.on('ready', async function () {
const url = 'http://foo.bar' const url = 'http://foo.bar'
const persistentSession = session.fromPartition('persist:ence-test') const persistentSession = session.fromPartition('persist:ence-test')
const name = 'test'
const value = 'true'
const set = () => { const set = () => persistentSession.cookies.set({
return new Promise((resolve, reject) => { url,
persistentSession.cookies.set({ name,
url, value,
name: 'test', expirationDate: Date.now() + 60000
value: 'true', })
expirationDate: Date.now() + 60000
}, error => {
if (error) {
reject(error)
} else {
resolve()
}
})
})
}
const get = () => { const get = () => persistentSession.cookies.get({
return new Promise((resolve, reject) => { url
persistentSession.cookies.get({ url }, (error, list) => { })
if (error) {
reject(error)
} else {
resolve(list)
}
})
})
}
const maybeRemove = (pred) => { const maybeRemove = async (pred) => new Promise(async (resolve, reject) => {
return new Promise((resolve, reject) => { try {
if (pred()) { if (pred()) {
persistentSession.cookies.remove(url, 'test', error => { await persistentSession.cookies.remove(url, name)
if (error) {
reject(error)
} else {
resolve()
}
})
} else {
resolve()
} }
}) resolve()
} } catch (error) {
reject(error)
}
})
try { try {
await maybeRemove(() => process.env.PHASE === 'one') 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', () => { it('includes the electron version in process.versions', () => {
expect(process.versions) expect(process.versions)
.to.have.own.property('electron') .to.have.own.property('electron')

32
spec/package-lock.json generated
View File

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

View File

@@ -97,7 +97,14 @@ global.nativeModulesEnabled = !process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS
// Register app as standard scheme. // Register app as standard scheme.
global.standardScheme = 'app' global.standardScheme = 'app'
global.zoomScheme = 'zoom' 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.on('window-all-closed', function () {
app.quit() 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 python electron/script/verify-ffmpeg.py --source-root "$PWD" --build-dir out/Default --ffmpeg-path out/ffmpeg
displayName: Verify non proprietary ffmpeg displayName: Verify non proprietary ffmpeg
timeoutInMinutes: 5 timeoutInMinutes: 5
env:
ELECTRON_DISABLE_SANDBOX: 1
- bash: | - bash: |
cd src cd src
python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default python electron/script/verify-mksnapshot.py --source-root "$PWD" --build-dir out/Default
displayName: Verify mksnapshot displayName: Verify mksnapshot
timeoutInMinutes: 5 timeoutInMinutes: 5
env:
ELECTRON_DISABLE_SANDBOX: 1
- bash: | - bash: |
cd src cd src
./out/Default/electron electron/spec --ci ./out/Default/electron electron/spec --ci
displayName: 'Run Electron tests' displayName: 'Run Electron tests'
timeoutInMinutes: 10 timeoutInMinutes: 10
env:
ELECTRON_DISABLE_SANDBOX: 1
- task: PublishTestResults@2 - task: PublishTestResults@2
displayName: 'Publish Test Results' displayName: 'Publish Test Results'