mirror of
https://github.com/electron/electron.git
synced 2026-02-19 03:14:51 -05:00
Compare commits
180 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4495f66310 | ||
|
|
5de7f2abdb | ||
|
|
01c892dfc3 | ||
|
|
abd3fee870 | ||
|
|
9313af3f3b | ||
|
|
8ed07298cc | ||
|
|
126b468eb7 | ||
|
|
0035a7b663 | ||
|
|
74644e53e8 | ||
|
|
308ee50831 | ||
|
|
4f26aab86f | ||
|
|
1ebd9761fb | ||
|
|
67c7554b80 | ||
|
|
d158cce9a3 | ||
|
|
5a73ec2ec8 | ||
|
|
a03da04578 | ||
|
|
d45c29087b | ||
|
|
00200c707e | ||
|
|
b228166900 | ||
|
|
d281859cf5 | ||
|
|
e00d217cfb | ||
|
|
00f40e84db | ||
|
|
dde5b3abe6 | ||
|
|
97bffde34c | ||
|
|
a6a1cc3bbb | ||
|
|
22e71497ec | ||
|
|
a32529b8fa | ||
|
|
732f053acd | ||
|
|
90fc709194 | ||
|
|
1d2b926962 | ||
|
|
fbc7b7f8ad | ||
|
|
4279f2f3f8 | ||
|
|
b8eec43b31 | ||
|
|
ce30d6c11f | ||
|
|
9f1e674f30 | ||
|
|
2f2761fa47 | ||
|
|
411f2be579 | ||
|
|
28a160fc1f | ||
|
|
2663d3e10d | ||
|
|
e55d7d6f1e | ||
|
|
092796f5f4 | ||
|
|
3f6d86c872 | ||
|
|
bdc9faa083 | ||
|
|
3e35f77da4 | ||
|
|
464f144a2b | ||
|
|
8f92396ed4 | ||
|
|
4e41a79404 | ||
|
|
d3c153e85a | ||
|
|
503b488153 | ||
|
|
005a65da79 | ||
|
|
36da7dcd5f | ||
|
|
0e5f734a1a | ||
|
|
3aeaf10e64 | ||
|
|
d70942190c | ||
|
|
ac400e5cb9 | ||
|
|
333d5fb5d2 | ||
|
|
f2c12ccbf5 | ||
|
|
79af1ea6f1 | ||
|
|
1ed7875b0b | ||
|
|
67a690e536 | ||
|
|
5f3bedd1e0 | ||
|
|
670a875792 | ||
|
|
faf82245dc | ||
|
|
066eeb5b5f | ||
|
|
77ec86b894 | ||
|
|
bf82dc7896 | ||
|
|
b4c5a30469 | ||
|
|
7ed6e2b909 | ||
|
|
8edd18be90 | ||
|
|
3bc9ae7f5b | ||
|
|
760c2327b4 | ||
|
|
fcb5069d24 | ||
|
|
221ab080a1 | ||
|
|
addf069f26 | ||
|
|
80221e52d9 | ||
|
|
3403b4a4de | ||
|
|
a1ac930f4d | ||
|
|
ed8396c6f5 | ||
|
|
a457d8823a | ||
|
|
2d44dcb8be | ||
|
|
85da731867 | ||
|
|
3af5fdb831 | ||
|
|
f1b197acbb | ||
|
|
d483c81887 | ||
|
|
3ef980ae2b | ||
|
|
36e68b46df | ||
|
|
f611dfdb5c | ||
|
|
148d0d8d29 | ||
|
|
79a1382126 | ||
|
|
b0d5ba1996 | ||
|
|
088dc70dae | ||
|
|
ad686cca68 | ||
|
|
48a4b58cc1 | ||
|
|
c1ef824e4c | ||
|
|
a2eb532720 | ||
|
|
0cb53ceb9d | ||
|
|
afe4e610ae | ||
|
|
74d90fbb33 | ||
|
|
f3bd8f6133 | ||
|
|
863c511fbf | ||
|
|
03d8689ded | ||
|
|
ff2bc1d705 | ||
|
|
a1df8befde | ||
|
|
75909685f7 | ||
|
|
2e0a015168 | ||
|
|
740b2797c5 | ||
|
|
5f372f7ecc | ||
|
|
e4f4a1d9f9 | ||
|
|
bdc255fa9e | ||
|
|
e8e542b3e8 | ||
|
|
dd89aa6c77 | ||
|
|
79caff69f5 | ||
|
|
67b10135b6 | ||
|
|
6a86831b7c | ||
|
|
dffd17ab70 | ||
|
|
cf98934dd9 | ||
|
|
2fff138740 | ||
|
|
82bc83c1de | ||
|
|
3a699741da | ||
|
|
237ad9a49d | ||
|
|
45158bdad5 | ||
|
|
cf4861b575 | ||
|
|
a76adba2b3 | ||
|
|
6689dce5ba | ||
|
|
9667b835ca | ||
|
|
58295a3198 | ||
|
|
b60125f782 | ||
|
|
2af6d9ced7 | ||
|
|
664c184fcb | ||
|
|
c125576d48 | ||
|
|
6030146b45 | ||
|
|
17297d85f8 | ||
|
|
65bbf21c08 | ||
|
|
00da6ab43b | ||
|
|
d6d2a46821 | ||
|
|
423bdda1b1 | ||
|
|
849aeced8f | ||
|
|
05a7485880 | ||
|
|
3db1b5a49a | ||
|
|
1c272503c3 | ||
|
|
1ebb8975a8 | ||
|
|
fcf4c9cef1 | ||
|
|
3e5349bf39 | ||
|
|
66988adb0e | ||
|
|
91be3f343f | ||
|
|
475006c10d | ||
|
|
d71a7d89bb | ||
|
|
abcba2b509 | ||
|
|
d06c20f836 | ||
|
|
3ab99663eb | ||
|
|
a253323ea1 | ||
|
|
4abe5a2963 | ||
|
|
aada5fb167 | ||
|
|
4efed0f5ed | ||
|
|
475a1e30d9 | ||
|
|
4aedc2c21a | ||
|
|
71668858db | ||
|
|
2573d52860 | ||
|
|
989c2605b3 | ||
|
|
afcfd473d0 | ||
|
|
1499bafe59 | ||
|
|
419fae76b4 | ||
|
|
c7b85a104a | ||
|
|
c6ea424858 | ||
|
|
ba23c0d600 | ||
|
|
d4e716477e | ||
|
|
34dbe5f176 | ||
|
|
a09fa53d80 | ||
|
|
c140c84b32 | ||
|
|
55ab1d7370 | ||
|
|
cfa247967f | ||
|
|
311f7ac6bb | ||
|
|
52a44facfd | ||
|
|
5acb7a0226 | ||
|
|
afcf94a85c | ||
|
|
c42b468966 | ||
|
|
e0257acdf5 | ||
|
|
b555434aa0 | ||
|
|
9574a4f472 | ||
|
|
2f167ce3d1 |
@@ -544,7 +544,6 @@ jobs:
|
||||
TARGET_ARCH: x64
|
||||
macos:
|
||||
xcode: "9.0"
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
@@ -643,7 +642,6 @@ jobs:
|
||||
MAS_BUILD: 1
|
||||
macos:
|
||||
xcode: "9.0"
|
||||
resource_class: xlarge
|
||||
steps:
|
||||
- checkout
|
||||
- run:
|
||||
|
||||
7
.env.example
Normal file
7
.env.example
Normal file
@@ -0,0 +1,7 @@
|
||||
# These env vars are only necessary for creating Electron releases.
|
||||
# See docs/development/releasing.md
|
||||
|
||||
APPVEYOR_TOKEN=
|
||||
CIRCLE_TOKEN=
|
||||
ELECTRON_GITHUB_TOKEN=
|
||||
VSTS_TOKEN=
|
||||
69
appveyor.yml
Normal file
69
appveyor.yml
Normal file
@@ -0,0 +1,69 @@
|
||||
build_cloud: electron-16
|
||||
image: electron-16-vs2015
|
||||
build_script:
|
||||
- ps: >-
|
||||
if($env:SKIP_GYP_BUILD -eq "true") {
|
||||
Write-warning "Skipping debug build for older branch"; Exit-AppveyorBuild
|
||||
} elseif(($env:APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME -split "/")[0] -eq ($env:APPVEYOR_REPO_NAME -split "/")[0]) {
|
||||
Write-warning "Skipping PR build for branch"; Exit-AppveyorBuild
|
||||
} else {
|
||||
Add-Path "$env:ProgramFiles (x86)\Windows Kits\10\Debuggers\x64"
|
||||
$env:path = "$env:ProgramFiles (x86)\Windows Kits\10\Debuggers\x64;$env:path"
|
||||
if($env:APPVEYOR_SCHEDULED_BUILD -eq 'True') {
|
||||
$env:RUN_RELEASE_BUILD = "1"
|
||||
}
|
||||
$Message = (git log --format=%B -n 1 HEAD) | Out-String
|
||||
if ((Test-Path Env:\RUN_RELEASE_BUILD)) {
|
||||
$env:ELECTRON_RELEASE = '1'
|
||||
Write-Output "release build triggered from api"
|
||||
}
|
||||
if ((Test-Path Env:\ELECTRON_RELEASE)) {
|
||||
Write-Output "Running release build"
|
||||
python script\bootstrap.py --target_arch=$env:TARGET_ARCH
|
||||
python script\build.py -c R
|
||||
python script\create-dist.py
|
||||
} else {
|
||||
Write-Output "Running debug build"
|
||||
python script\bootstrap.py --target_arch=$env:TARGET_ARCH --dev
|
||||
python script\build.py -c D
|
||||
}
|
||||
if ($? -ne 'True') {
|
||||
throw "Build failed with exit code $?"
|
||||
} else {
|
||||
"Build succeeded."
|
||||
}
|
||||
Push-AppveyorArtifact out
|
||||
}
|
||||
test_script:
|
||||
- ps: >-
|
||||
if (Test-Path Env:\ELECTRON_RELEASE) {
|
||||
Write-Output "Skipping tests for release build"
|
||||
} else {
|
||||
Write-Output "Running tests for debug build"
|
||||
python script\test.py --ci --rebuild_native_modules
|
||||
if ($LASTEXITCODE -ne '0') {
|
||||
throw "Tests failed with exit code $LASTEXITCODE"
|
||||
} else {
|
||||
Write-Output "Tests succeeded."
|
||||
}
|
||||
python script\verify-ffmpeg.py
|
||||
if ($LASTEXITCODE -ne '0') {
|
||||
throw "Verify ffmpeg failed with exit code $LASTEXITCODE"
|
||||
} else {
|
||||
"Verify ffmpeg succeeded."
|
||||
}
|
||||
}
|
||||
artifacts:
|
||||
- path: test-results.xml
|
||||
name: test-results.xml
|
||||
deploy_script:
|
||||
- ps: >-
|
||||
if (Test-Path Env:\ELECTRON_RELEASE) {
|
||||
if (Test-Path Env:\RUN_RELEASE_BUILD) {
|
||||
Write-Output "Uploading Electron release distribution to s3"
|
||||
& python script\upload.py --upload_to_s3
|
||||
} else {
|
||||
Write-Output "Uploading Electron release distribution to github releases"
|
||||
& python script\upload.py
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,12 @@
|
||||
|
||||
using content::BrowserThread;
|
||||
|
||||
namespace {
|
||||
|
||||
static scoped_nsobject<NSMenu> applicationMenu_;
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace api {
|
||||
@@ -135,7 +141,18 @@ void Menu::SetApplicationMenu(Menu* base_menu) {
|
||||
base::scoped_nsobject<AtomMenuController> menu_controller(
|
||||
[[AtomMenuController alloc] initWithModel:menu->model_.get()
|
||||
useDefaultAccelerator:YES]);
|
||||
[NSApp setMainMenu:[menu_controller menu]];
|
||||
|
||||
NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop];
|
||||
[currentRunLoop cancelPerformSelector:@selector(setMainMenu:)
|
||||
target:NSApp
|
||||
argument:applicationMenu_];
|
||||
applicationMenu_.reset([[menu_controller menu] retain]);
|
||||
[[NSRunLoop currentRunLoop]
|
||||
performSelector:@selector(setMainMenu:)
|
||||
target:NSApp
|
||||
argument:applicationMenu_
|
||||
order:0
|
||||
modes:[NSArray arrayWithObject:NSDefaultRunLoopMode]];
|
||||
|
||||
// Ensure the menu_controller_ is destroyed after main menu is set.
|
||||
menu_controller.swap(menu->menu_controller_);
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
#include "base/guid.h"
|
||||
#include "base/strings/string_number_conversions.h"
|
||||
#include "base/strings/string_util.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "brightray/browser/media/media_device_id_salt.h"
|
||||
#include "brightray/browser/net/devtools_network_conditions.h"
|
||||
#include "brightray/browser/net/devtools_network_controller_handle.h"
|
||||
@@ -47,7 +46,6 @@
|
||||
#include "net/http/http_auth_handler_factory.h"
|
||||
#include "net/http/http_auth_preferences.h"
|
||||
#include "net/proxy/proxy_config_service_fixed.h"
|
||||
#include "net/proxy/proxy_service.h"
|
||||
#include "net/url_request/static_http_user_agent_settings.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
@@ -232,59 +230,6 @@ const char kPersistPrefix[] = "persist:";
|
||||
// Referenced session objects.
|
||||
std::map<uint32_t, v8::Global<v8::Object>> g_sessions;
|
||||
|
||||
class ResolveProxyHelper {
|
||||
public:
|
||||
ResolveProxyHelper(AtomBrowserContext* browser_context,
|
||||
const GURL& url,
|
||||
const Session::ResolveProxyCallback& callback)
|
||||
: callback_(callback),
|
||||
original_thread_(base::ThreadTaskRunnerHandle::Get()) {
|
||||
scoped_refptr<net::URLRequestContextGetter> context_getter =
|
||||
browser_context->url_request_context_getter();
|
||||
context_getter->GetNetworkTaskRunner()->PostTask(
|
||||
FROM_HERE,
|
||||
base::Bind(&ResolveProxyHelper::ResolveProxy,
|
||||
base::Unretained(this), context_getter, url));
|
||||
}
|
||||
|
||||
void OnResolveProxyCompleted(int result) {
|
||||
std::string proxy;
|
||||
if (result == net::OK)
|
||||
proxy = proxy_info_.ToPacString();
|
||||
original_thread_->PostTask(FROM_HERE,
|
||||
base::Bind(callback_, proxy));
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
void ResolveProxy(scoped_refptr<net::URLRequestContextGetter> context_getter,
|
||||
const GURL& url) {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
|
||||
net::ProxyService* proxy_service =
|
||||
context_getter->GetURLRequestContext()->proxy_service();
|
||||
net::CompletionCallback completion_callback =
|
||||
base::Bind(&ResolveProxyHelper::OnResolveProxyCompleted,
|
||||
base::Unretained(this));
|
||||
|
||||
// Start the request.
|
||||
int result = proxy_service->ResolveProxy(
|
||||
url, "GET", &proxy_info_, completion_callback, &pac_req_, nullptr,
|
||||
net::NetLogWithSource());
|
||||
|
||||
// Completed synchronously.
|
||||
if (result != net::ERR_IO_PENDING)
|
||||
completion_callback.Run(result);
|
||||
}
|
||||
|
||||
Session::ResolveProxyCallback callback_;
|
||||
net::ProxyInfo proxy_info_;
|
||||
net::ProxyService::PacRequest* pac_req_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> original_thread_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ResolveProxyHelper);
|
||||
};
|
||||
|
||||
// Runs the callback in UI thread.
|
||||
void RunCallbackInUI(const base::Callback<void()>& callback) {
|
||||
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback);
|
||||
@@ -490,8 +435,10 @@ void Session::OnDownloadCreated(content::DownloadManager* manager,
|
||||
}
|
||||
}
|
||||
|
||||
void Session::ResolveProxy(const GURL& url, ResolveProxyCallback callback) {
|
||||
new ResolveProxyHelper(browser_context(), url, callback);
|
||||
void Session::ResolveProxy(
|
||||
const GURL& url,
|
||||
const ResolveProxyHelper::ResolveProxyCallback& callback) {
|
||||
browser_context_->GetResolveProxyHelper()->ResolveProxy(url, callback);
|
||||
}
|
||||
|
||||
template<Session::CacheAction action>
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
|
||||
#include "atom/browser/api/trackable_object.h"
|
||||
#include "atom/browser/atom_blob_reader.h"
|
||||
#include "atom/browser/net/resolve_proxy_helper.h"
|
||||
#include "base/values.h"
|
||||
#include "content/public/browser/download_manager.h"
|
||||
#include "native_mate/handle.h"
|
||||
@@ -39,8 +40,6 @@ namespace api {
|
||||
class Session: public mate::TrackableObject<Session>,
|
||||
public content::DownloadManager::Observer {
|
||||
public:
|
||||
using ResolveProxyCallback = base::Callback<void(std::string)>;
|
||||
|
||||
enum class CacheAction {
|
||||
CLEAR,
|
||||
STATS,
|
||||
@@ -62,7 +61,8 @@ class Session: public mate::TrackableObject<Session>,
|
||||
v8::Local<v8::FunctionTemplate> prototype);
|
||||
|
||||
// Methods.
|
||||
void ResolveProxy(const GURL& url, ResolveProxyCallback callback);
|
||||
void ResolveProxy(const GURL& url,
|
||||
const ResolveProxyHelper::ResolveProxyCallback& callback);
|
||||
template<CacheAction action>
|
||||
void DoCacheAction(const net::CompletionCallback& callback);
|
||||
void ClearStorageData(mate::Arguments* args);
|
||||
|
||||
@@ -206,7 +206,7 @@ void Tray::PopUpContextMenu(mate::Arguments* args) {
|
||||
|
||||
void Tray::SetContextMenu(v8::Isolate* isolate, mate::Handle<Menu> menu) {
|
||||
menu_.Reset(isolate, menu.ToV8());
|
||||
tray_icon_->SetContextMenu(menu->model());
|
||||
tray_icon_->SetContextMenu(menu.IsEmpty() ? nullptr : menu->model());
|
||||
}
|
||||
|
||||
gfx::Rect Tray::GetBounds() {
|
||||
|
||||
@@ -113,7 +113,7 @@ struct Converter<atom::SetSizeParams> {
|
||||
return false;
|
||||
bool autosize;
|
||||
if (params.Get("enableAutoSize", &autosize))
|
||||
out->enable_auto_size.reset(new bool(true));
|
||||
out->enable_auto_size.reset(new bool(autosize));
|
||||
gfx::Size size;
|
||||
if (params.Get("min", &size))
|
||||
out->min_size.reset(new gfx::Size(size));
|
||||
@@ -310,7 +310,8 @@ WebContents::WebContents(v8::Isolate* isolate,
|
||||
type_(type),
|
||||
request_id_(0),
|
||||
background_throttling_(true),
|
||||
enable_devtools_(true) {
|
||||
enable_devtools_(true),
|
||||
is_dom_ready_(false) {
|
||||
const mate::Dictionary options = mate::Dictionary::CreateEmpty(isolate);
|
||||
if (type == REMOTE) {
|
||||
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
|
||||
@@ -331,6 +332,10 @@ WebContents::WebContents(v8::Isolate* isolate, const mate::Dictionary& options)
|
||||
request_id_(0),
|
||||
background_throttling_(true),
|
||||
enable_devtools_(true) {
|
||||
// WebContents may need to emit events when it is garbage collected, so it
|
||||
// has to be deleted in the first gc callback.
|
||||
MarkHighMemoryUsage();
|
||||
|
||||
// Read options.
|
||||
options.Get("backgroundThrottling", &background_throttling_);
|
||||
|
||||
@@ -795,8 +800,10 @@ void WebContents::DidChangeThemeColor(SkColor theme_color) {
|
||||
|
||||
void WebContents::DocumentLoadedInFrame(
|
||||
content::RenderFrameHost* render_frame_host) {
|
||||
if (!render_frame_host->GetParent())
|
||||
if (!render_frame_host->GetParent()) {
|
||||
is_dom_ready_ = true;
|
||||
Emit("dom-ready");
|
||||
}
|
||||
}
|
||||
|
||||
void WebContents::DidFinishLoad(content::RenderFrameHost* render_frame_host,
|
||||
@@ -818,6 +825,7 @@ void WebContents::DidFailLoad(content::RenderFrameHost* render_frame_host,
|
||||
}
|
||||
|
||||
void WebContents::DidStartLoading() {
|
||||
is_dom_ready_ = false;
|
||||
Emit("did-start-loading");
|
||||
}
|
||||
|
||||
@@ -1339,6 +1347,10 @@ bool WebContents::IsAudioMuted() {
|
||||
return web_contents()->IsAudioMuted();
|
||||
}
|
||||
|
||||
bool WebContents::IsDOMReady() const {
|
||||
return is_dom_ready_;
|
||||
}
|
||||
|
||||
void WebContents::Print(mate::Arguments* args) {
|
||||
PrintSettings settings = { false, false, base::string16() };
|
||||
if (args->Length() >= 1 && !args->GetNext(&settings)) {
|
||||
@@ -1930,6 +1942,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
||||
&WebContents::SetIgnoreMenuShortcuts)
|
||||
.SetMethod("setAudioMuted", &WebContents::SetAudioMuted)
|
||||
.SetMethod("isAudioMuted", &WebContents::IsAudioMuted)
|
||||
.SetMethod("isDomReady", &WebContents::IsDOMReady)
|
||||
.SetMethod("undo", &WebContents::Undo)
|
||||
.SetMethod("redo", &WebContents::Redo)
|
||||
.SetMethod("cut", &WebContents::Cut)
|
||||
|
||||
@@ -125,6 +125,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||
void SetIgnoreMenuShortcuts(bool ignore);
|
||||
void SetAudioMuted(bool muted);
|
||||
bool IsAudioMuted();
|
||||
bool IsDOMReady() const;
|
||||
void Print(mate::Arguments* args);
|
||||
std::vector<printing::PrinterBasicInfo> GetPrinterList();
|
||||
void SetEmbedder(const WebContents* embedder);
|
||||
@@ -430,6 +431,9 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||
// Whether to enable devtools.
|
||||
bool enable_devtools_;
|
||||
|
||||
// Whether page's document is ready.
|
||||
bool is_dom_ready_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(WebContents);
|
||||
};
|
||||
|
||||
|
||||
@@ -450,9 +450,11 @@ gfx::Rect Window::GetContentBounds() {
|
||||
}
|
||||
|
||||
void Window::SetSize(int width, int height, mate::Arguments* args) {
|
||||
gfx::Size size = window_->GetMinimumSize();
|
||||
size.SetToMax(gfx::Size(width, height));
|
||||
bool animate = false;
|
||||
args->GetNext(&animate);
|
||||
window_->SetSize(gfx::Size(width, height), animate);
|
||||
window_->SetSize(size, animate);
|
||||
}
|
||||
|
||||
std::vector<int> Window::GetSize() {
|
||||
|
||||
@@ -448,7 +448,6 @@ void AtomBrowserClient::SiteInstanceDeleting(
|
||||
|
||||
brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts(
|
||||
const content::MainFunctionParams&) {
|
||||
v8::V8::Initialize(); // Init V8 before creating main parts.
|
||||
return new AtomBrowserMainParts;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "atom/browser/net/atom_network_delegate.h"
|
||||
#include "atom/browser/net/atom_url_request_job_factory.h"
|
||||
#include "atom/browser/net/http_protocol_handler.h"
|
||||
#include "atom/browser/net/resolve_proxy_helper.h"
|
||||
#include "atom/browser/web_view_manager.h"
|
||||
#include "atom/common/atom_version.h"
|
||||
#include "atom/common/chrome_version.h"
|
||||
@@ -226,6 +227,14 @@ AtomBlobReader* AtomBrowserContext::GetBlobReader() {
|
||||
return blob_reader_.get();
|
||||
}
|
||||
|
||||
ResolveProxyHelper* AtomBrowserContext::GetResolveProxyHelper() {
|
||||
if (!resolve_proxy_helper_.get()) {
|
||||
resolve_proxy_helper_ =
|
||||
base::MakeRefCounted<ResolveProxyHelper>(url_request_context_getter());
|
||||
}
|
||||
return resolve_proxy_helper_.get();
|
||||
}
|
||||
|
||||
// static
|
||||
scoped_refptr<AtomBrowserContext> AtomBrowserContext::From(
|
||||
const std::string& partition, bool in_memory,
|
||||
|
||||
@@ -18,6 +18,7 @@ class AtomBlobReader;
|
||||
class AtomDownloadManagerDelegate;
|
||||
class AtomNetworkDelegate;
|
||||
class AtomPermissionManager;
|
||||
class ResolveProxyHelper;
|
||||
class WebViewManager;
|
||||
|
||||
class AtomBrowserContext : public brightray::BrowserContext {
|
||||
@@ -51,6 +52,8 @@ class AtomBrowserContext : public brightray::BrowserContext {
|
||||
// brightray::BrowserContext:
|
||||
void RegisterPrefs(PrefRegistrySimple* pref_registry) override;
|
||||
|
||||
ResolveProxyHelper* GetResolveProxyHelper();
|
||||
|
||||
AtomBlobReader* GetBlobReader();
|
||||
AtomCookieDelegate* cookie_delegate() const {
|
||||
return cookie_delegate_.get();
|
||||
@@ -62,6 +65,7 @@ class AtomBrowserContext : public brightray::BrowserContext {
|
||||
~AtomBrowserContext() override;
|
||||
|
||||
private:
|
||||
scoped_refptr<ResolveProxyHelper> resolve_proxy_helper_;
|
||||
std::unique_ptr<AtomDownloadManagerDelegate> download_manager_delegate_;
|
||||
std::unique_ptr<WebViewManager> guest_manager_;
|
||||
std::unique_ptr<AtomPermissionManager> permission_manager_;
|
||||
|
||||
@@ -151,6 +151,14 @@ void AtomBrowserMainParts::PostEarlyInitialization() {
|
||||
|
||||
// Wrap the uv loop with global env.
|
||||
node_bindings_->set_uv_env(env);
|
||||
|
||||
// We already initialized the feature list in
|
||||
// brightray::BrowserMainParts::PreEarlyInitialization(), but
|
||||
// the user JS script would not have had a chance to alter the command-line
|
||||
// switches at that point. Lets reinitialize it here to pick up the
|
||||
// command-line changes.
|
||||
base::FeatureList::ClearInstanceForTesting();
|
||||
brightray::BrowserMainParts::InitializeFeatureList();
|
||||
}
|
||||
|
||||
int AtomBrowserMainParts::PreCreateThreads() {
|
||||
|
||||
@@ -77,6 +77,11 @@ bool Browser::RemoveAsDefaultProtocolClient(const std::string& protocol,
|
||||
}
|
||||
}
|
||||
|
||||
// No other app was found set it to none instead of setting it back to itself.
|
||||
if ([identifier isEqualToString:(__bridge NSString*)other]) {
|
||||
other = base::mac::NSToCFCast(@"None");
|
||||
}
|
||||
|
||||
OSStatus return_code = LSSetDefaultHandlerForURLScheme(protocol_cf, other);
|
||||
return return_code == noErr;
|
||||
}
|
||||
@@ -210,6 +215,65 @@ Browser::LoginItemSettings Browser::GetLoginItemSettings(
|
||||
return settings;
|
||||
}
|
||||
|
||||
// copied from GetLoginItemForApp in base/mac/mac_util.mm
|
||||
LSSharedFileListItemRef GetLoginItemForApp() {
|
||||
base::ScopedCFTypeRef<LSSharedFileListRef> login_items(
|
||||
LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL));
|
||||
if (!login_items.get()) {
|
||||
LOG(ERROR) << "Couldn't get a Login Items list.";
|
||||
return NULL;
|
||||
}
|
||||
base::scoped_nsobject<NSArray> login_items_array(
|
||||
base::mac::CFToNSCast(LSSharedFileListCopySnapshot(login_items, NULL)));
|
||||
NSURL* url = [NSURL fileURLWithPath:[base::mac::MainBundle() bundlePath]];
|
||||
for (NSUInteger i = 0; i < [login_items_array count]; ++i) {
|
||||
LSSharedFileListItemRef item =
|
||||
reinterpret_cast<LSSharedFileListItemRef>(login_items_array[i]);
|
||||
CFURLRef item_url_ref = NULL;
|
||||
if (LSSharedFileListItemResolve(item, 0, &item_url_ref, NULL) == noErr &&
|
||||
item_url_ref) {
|
||||
base::ScopedCFTypeRef<CFURLRef> item_url(item_url_ref);
|
||||
if (CFEqual(item_url, url)) {
|
||||
CFRetain(item);
|
||||
return item;
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void RemoveFromLoginItems() {
|
||||
base::ScopedCFTypeRef<LSSharedFileListRef> list(
|
||||
LSSharedFileListCreate(NULL, kLSSharedFileListSessionLoginItems, NULL));
|
||||
if (!list) {
|
||||
LOG(ERROR) << "Unable to access shared file list";
|
||||
return;
|
||||
}
|
||||
|
||||
if (GetLoginItemForApp() != NULL) {
|
||||
base::scoped_nsobject<NSArray> login_items_array(
|
||||
base::mac::CFToNSCast(LSSharedFileListCopySnapshot(list, NULL)));
|
||||
|
||||
if (!login_items_array) {
|
||||
LOG(ERROR) << "No items in list of auto-loaded apps";
|
||||
return;
|
||||
}
|
||||
|
||||
for (NSUInteger i = 0; i < [login_items_array count]; ++i) {
|
||||
LSSharedFileListItemRef item =
|
||||
reinterpret_cast<LSSharedFileListItemRef>(login_items_array[i]);
|
||||
CFURLRef url_ref = NULL;
|
||||
if (LSSharedFileListItemResolve(item, 0, &url_ref, NULL) == noErr &&
|
||||
item) {
|
||||
base::ScopedCFTypeRef<CFURLRef> url(url_ref);
|
||||
if ([[base::mac::CFToNSCast(url.get()) path]
|
||||
hasPrefix:[[NSBundle mainBundle] bundlePath]])
|
||||
LSSharedFileListItemRemove(list, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Browser::SetLoginItemSettings(LoginItemSettings settings) {
|
||||
#if defined(MAS_BUILD)
|
||||
platform_util::SetLoginItemEnabled(settings.open_at_login);
|
||||
@@ -217,7 +281,7 @@ void Browser::SetLoginItemSettings(LoginItemSettings settings) {
|
||||
if (settings.open_at_login)
|
||||
base::mac::AddToLoginItems(settings.open_as_hidden);
|
||||
else
|
||||
base::mac::RemoveFromLoginItems();
|
||||
RemoveFromLoginItems();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <unistd.h>
|
||||
#include <uv.h>
|
||||
#include <iostream>
|
||||
#include <utility>
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "device/bluetooth/dbus/dbus_thread_manager_linux.h"
|
||||
@@ -23,7 +24,7 @@ std::string get_executable_basename() {
|
||||
if (!uv_exepath(buf, &buf_size)) {
|
||||
rv = strrchr(static_cast<const char*>(buf), '/') + 1;
|
||||
}
|
||||
return std::move(rv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -32,39 +33,37 @@ namespace atom {
|
||||
|
||||
PowerObserverLinux::PowerObserverLinux()
|
||||
: lock_owner_name_(get_executable_basename()), weak_ptr_factory_(this) {
|
||||
auto dbus_thread_manager = bluez::DBusThreadManagerLinux::Get();
|
||||
if (dbus_thread_manager) {
|
||||
bus_ = dbus_thread_manager->GetSystemBus();
|
||||
if (bus_) {
|
||||
logind_ = bus_->GetObjectProxy(kLogindServiceName,
|
||||
dbus::ObjectPath(kLogindObjectPath));
|
||||
logind_->WaitForServiceToBeAvailable(
|
||||
base::Bind(&PowerObserverLinux::OnLoginServiceAvailable,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
} else {
|
||||
LOG(WARNING) << "Failed to get system bus connection";
|
||||
}
|
||||
} else {
|
||||
LOG(WARNING) << "DBusThreadManagerLinux instance isn't available";
|
||||
auto* bus = bluez::DBusThreadManagerLinux::Get()->GetSystemBus();
|
||||
if (!bus) {
|
||||
LOG(WARNING) << "Failed to get system bus connection";
|
||||
return;
|
||||
}
|
||||
|
||||
// set up the logind proxy
|
||||
|
||||
const auto weakThis = weak_ptr_factory_.GetWeakPtr();
|
||||
|
||||
logind_ = bus->GetObjectProxy(kLogindServiceName,
|
||||
dbus::ObjectPath(kLogindObjectPath));
|
||||
logind_->ConnectToSignal(
|
||||
kLogindManagerInterface, "PrepareForShutdown",
|
||||
base::BindRepeating(&PowerObserverLinux::OnPrepareForShutdown, weakThis),
|
||||
base::BindRepeating(&PowerObserverLinux::OnSignalConnected, weakThis));
|
||||
logind_->ConnectToSignal(
|
||||
kLogindManagerInterface, "PrepareForSleep",
|
||||
base::BindRepeating(&PowerObserverLinux::OnPrepareForSleep, weakThis),
|
||||
base::BindRepeating(&PowerObserverLinux::OnSignalConnected, weakThis));
|
||||
logind_->WaitForServiceToBeAvailable(base::BindRepeating(
|
||||
&PowerObserverLinux::OnLoginServiceAvailable, weakThis));
|
||||
}
|
||||
|
||||
PowerObserverLinux::~PowerObserverLinux() = default;
|
||||
|
||||
void PowerObserverLinux::OnLoginServiceAvailable(bool service_available) {
|
||||
if (!service_available) {
|
||||
LOG(WARNING) << kLogindServiceName << " not available";
|
||||
return;
|
||||
}
|
||||
// Connect to PrepareForShutdown/PrepareForSleep signals
|
||||
logind_->ConnectToSignal(kLogindManagerInterface, "PrepareForShutdown",
|
||||
base::Bind(&PowerObserverLinux::OnPrepareForShutdown,
|
||||
weak_ptr_factory_.GetWeakPtr()),
|
||||
base::Bind(&PowerObserverLinux::OnSignalConnected,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
logind_->ConnectToSignal(kLogindManagerInterface, "PrepareForSleep",
|
||||
base::Bind(&PowerObserverLinux::OnPrepareForSleep,
|
||||
weak_ptr_factory_.GetWeakPtr()),
|
||||
base::Bind(&PowerObserverLinux::OnSignalConnected,
|
||||
weak_ptr_factory_.GetWeakPtr()));
|
||||
// Take sleep inhibit lock
|
||||
BlockSleep();
|
||||
}
|
||||
@@ -120,8 +119,10 @@ void PowerObserverLinux::SetShutdownHandler(base::Callback<bool()> handler) {
|
||||
|
||||
void PowerObserverLinux::OnInhibitResponse(base::ScopedFD* scoped_fd,
|
||||
dbus::Response* response) {
|
||||
dbus::MessageReader reader(response);
|
||||
reader.PopFileDescriptor(scoped_fd);
|
||||
if (response != nullptr) {
|
||||
dbus::MessageReader reader(response);
|
||||
reader.PopFileDescriptor(scoped_fd);
|
||||
}
|
||||
}
|
||||
|
||||
void PowerObserverLinux::OnPrepareForSleep(dbus::Signal* signal) {
|
||||
@@ -156,7 +157,7 @@ void PowerObserverLinux::OnPrepareForShutdown(dbus::Signal* signal) {
|
||||
}
|
||||
}
|
||||
|
||||
void PowerObserverLinux::OnSignalConnected(const std::string& interface,
|
||||
void PowerObserverLinux::OnSignalConnected(const std::string& /*interface*/,
|
||||
const std::string& signal,
|
||||
bool success) {
|
||||
LOG_IF(WARNING, !success) << "Failed to connect to " << signal;
|
||||
|
||||
@@ -19,6 +19,7 @@ namespace atom {
|
||||
class PowerObserverLinux : public base::PowerObserver {
|
||||
public:
|
||||
PowerObserverLinux();
|
||||
~PowerObserverLinux() override;
|
||||
|
||||
protected:
|
||||
void BlockSleep();
|
||||
@@ -39,7 +40,6 @@ class PowerObserverLinux : public base::PowerObserver {
|
||||
|
||||
base::Callback<bool()> should_shutdown_;
|
||||
|
||||
scoped_refptr<dbus::Bus> bus_;
|
||||
scoped_refptr<dbus::ObjectProxy> logind_;
|
||||
std::string lock_owner_name_;
|
||||
base::ScopedFD sleep_lock_;
|
||||
|
||||
@@ -25,6 +25,7 @@ void NativeBrowserViewViews::SetBounds(const gfx::Rect& bounds) {
|
||||
void NativeBrowserViewViews::SetBackgroundColor(SkColor color) {
|
||||
auto* view = GetInspectableWebContentsView()->GetView();
|
||||
view->SetBackground(views::CreateSolidBackground(color));
|
||||
view->SchedulePaint();
|
||||
}
|
||||
|
||||
// static
|
||||
|
||||
@@ -340,7 +340,9 @@ gfx::Size NativeWindow::GetContentMinimumSize() const {
|
||||
gfx::Size NativeWindow::GetContentMaximumSize() const {
|
||||
gfx::Size maximum_size = GetContentSizeConstraints().GetMaximumSize();
|
||||
#if defined(OS_WIN)
|
||||
return GetExpandedWindowSize(this, maximum_size);
|
||||
return GetContentSizeConstraints().HasMaximumSize()
|
||||
? GetExpandedWindowSize(this, maximum_size)
|
||||
: maximum_size;
|
||||
#else
|
||||
return maximum_size;
|
||||
#endif
|
||||
|
||||
@@ -213,6 +213,7 @@ class NativeWindowMac : public NativeWindow,
|
||||
bool was_maximizable_;
|
||||
bool was_movable_;
|
||||
NSRect original_frame_;
|
||||
NSInteger original_level_;
|
||||
NSUInteger simple_fullscreen_mask_;
|
||||
|
||||
base::scoped_nsobject<NSColor> background_color_before_vibrancy_;
|
||||
|
||||
@@ -380,6 +380,7 @@ bool ScopedDisableResize::disable_resize_ = false;
|
||||
|
||||
// Set window style to hide the toolbar, otherwise the toolbar will show in
|
||||
// fullscreen mode.
|
||||
[window setTitlebarAppearsTransparent:NO];
|
||||
shell_->SetStyleMask(true, NSFullSizeContentViewWindowMask);
|
||||
}
|
||||
}
|
||||
@@ -397,6 +398,7 @@ bool ScopedDisableResize::disable_resize_ = false;
|
||||
// Turn off the style for toolbar.
|
||||
if (base::mac::IsAtLeastOS10_10() &&
|
||||
shell_->title_bar_style() == atom::NativeWindowMac::HIDDEN_INSET) {
|
||||
[window setTitlebarAppearsTransparent:YES];
|
||||
shell_->SetStyleMask(false, NSFullSizeContentViewWindowMask);
|
||||
}
|
||||
}
|
||||
@@ -728,10 +730,20 @@ enum {
|
||||
|
||||
// Custom window button methods
|
||||
|
||||
- (BOOL)windowShouldClose:(id)sender { return YES; }
|
||||
|
||||
- (void)performClose:(id)sender {
|
||||
if (shell_->title_bar_style() == atom::NativeWindowMac::CUSTOM_BUTTONS_ON_HOVER)
|
||||
if (shell_->title_bar_style() ==
|
||||
atom::NativeWindowMac::CUSTOM_BUTTONS_ON_HOVER) {
|
||||
[[self delegate] windowShouldClose:self];
|
||||
else
|
||||
} else if (shell_->IsSimpleFullScreen()) {
|
||||
if([[self delegate] respondsToSelector:@selector(windowShouldClose:)]) {
|
||||
if(![[self delegate] windowShouldClose:self]) return;
|
||||
} else if([self respondsToSelector:@selector(windowShouldClose:)]) {
|
||||
if(![self windowShouldClose:self]) return;
|
||||
}
|
||||
[self close];
|
||||
} else
|
||||
[super performClose:sender];
|
||||
}
|
||||
|
||||
@@ -1045,6 +1057,10 @@ NativeWindowMac::NativeWindowMac(
|
||||
|
||||
RegisterInputEventObserver(
|
||||
web_contents->GetWebContents()->GetRenderViewHost());
|
||||
|
||||
original_frame_ = [window_ frame];
|
||||
original_level_ = [window_ level];
|
||||
|
||||
}
|
||||
|
||||
NativeWindowMac::~NativeWindowMac() {
|
||||
@@ -1069,6 +1085,11 @@ void NativeWindowMac::Close() {
|
||||
}
|
||||
|
||||
void NativeWindowMac::CloseImmediately() {
|
||||
// Retain the child window before closing it. If the last reference to the
|
||||
// NSWindow goes away inside -[NSWindow close], then bad stuff can happen.
|
||||
// See e.g. http://crbug.com/616701.
|
||||
base::scoped_nsobject<NSWindow> child_window(window_,
|
||||
base::scoped_policy::RETAIN);
|
||||
[window_ close];
|
||||
}
|
||||
|
||||
@@ -1139,12 +1160,12 @@ bool NativeWindowMac::IsEnabled() {
|
||||
|
||||
void NativeWindowMac::SetEnabled(bool enable) {
|
||||
if (enable) {
|
||||
[window_ endSheet: [window_ attachedSheet]];
|
||||
} else {
|
||||
[window_ beginSheet: window_ completionHandler:^(NSModalResponse returnCode) {
|
||||
NSLog(@"modal enabled");
|
||||
return;
|
||||
}];
|
||||
} else {
|
||||
[window_ endSheet: [window_ attachedSheet]];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1411,8 +1432,9 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
|
||||
if (simple_fullscreen && !is_simple_fullscreen_) {
|
||||
is_simple_fullscreen_ = true;
|
||||
|
||||
// Take note of the current window size
|
||||
// Take note of the current window size and level
|
||||
original_frame_ = [window frame];
|
||||
original_level_ = [window level];
|
||||
|
||||
simple_fullscreen_options_ = [NSApp currentSystemPresentationOptions];
|
||||
simple_fullscreen_mask_ = [window styleMask];
|
||||
@@ -1428,6 +1450,13 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
|
||||
|
||||
NSRect fullscreenFrame = [window.screen frame];
|
||||
|
||||
// If our app has dock hidden, set the window level higher so another app's
|
||||
// menu bar doesn't appear on top of our fullscreen app.
|
||||
if ([[NSRunningApplication currentApplication] activationPolicy] !=
|
||||
NSApplicationActivationPolicyRegular) {
|
||||
window.level = NSPopUpMenuWindowLevel;
|
||||
}
|
||||
|
||||
if ( !fullscreen_window_title() ) {
|
||||
// Hide the titlebar
|
||||
SetStyleMask(false, NSTitledWindowMask);
|
||||
@@ -1462,6 +1491,7 @@ void NativeWindowMac::SetSimpleFullScreen(bool simple_fullscreen) {
|
||||
}
|
||||
|
||||
[window setFrame:original_frame_ display: YES animate: YES];
|
||||
window.level = original_level_;
|
||||
|
||||
[NSApp setPresentationOptions:simple_fullscreen_options_];
|
||||
|
||||
|
||||
@@ -608,6 +608,7 @@ void NativeWindowViews::SetResizable(bool resizable) {
|
||||
// both the minimum and maximum size to the window size to achieve it.
|
||||
if (resizable) {
|
||||
SetContentSizeConstraints(old_size_constraints_);
|
||||
SetMaximizable(maximizable_);
|
||||
} else {
|
||||
old_size_constraints_ = GetContentSizeConstraints();
|
||||
resizable_ = false;
|
||||
|
||||
85
atom/browser/net/resolve_proxy_helper.cc
Normal file
85
atom/browser/net/resolve_proxy_helper.cc
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright (c) 2018 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "atom/browser/net/resolve_proxy_helper.h"
|
||||
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "net/url_request/url_request_context.h"
|
||||
#include "net/url_request/url_request_context_getter.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
ResolveProxyHelper::ResolveProxyHelper(net::URLRequestContextGetter* getter)
|
||||
: context_getter_(getter),
|
||||
original_thread_(base::ThreadTaskRunnerHandle::Get()) {}
|
||||
|
||||
ResolveProxyHelper::~ResolveProxyHelper() {
|
||||
// Clear all pending requests if the ProxyService is still alive.
|
||||
pending_requests_.clear();
|
||||
}
|
||||
|
||||
void ResolveProxyHelper::ResolveProxy(const GURL& url,
|
||||
const ResolveProxyCallback& callback) {
|
||||
// Enqueue the pending request.
|
||||
pending_requests_.push_back(PendingRequest(url, callback));
|
||||
|
||||
// If nothing is in progress, start.
|
||||
if (pending_requests_.size() == 1)
|
||||
StartPendingRequest();
|
||||
}
|
||||
|
||||
void ResolveProxyHelper::SendProxyResult(const std::string& proxy) {
|
||||
CHECK(!pending_requests_.empty());
|
||||
|
||||
const auto& completed_request = pending_requests_.front();
|
||||
if (!completed_request.callback.is_null())
|
||||
completed_request.callback.Run(proxy);
|
||||
|
||||
// Clear the current (completed) request.
|
||||
pending_requests_.pop_front();
|
||||
|
||||
// Start the next request.
|
||||
if (!pending_requests_.empty())
|
||||
StartPendingRequest();
|
||||
}
|
||||
|
||||
void ResolveProxyHelper::StartPendingRequest() {
|
||||
auto& request = pending_requests_.front();
|
||||
context_getter_->GetNetworkTaskRunner()->PostTask(
|
||||
FROM_HERE,
|
||||
base::BindOnce(&ResolveProxyHelper::StartPendingRequestInIO,
|
||||
base::Unretained(this), request.url, request.pac_req));
|
||||
}
|
||||
|
||||
void ResolveProxyHelper::OnResolveProxyCompleted(int result) {
|
||||
std::string proxy;
|
||||
if (result == net::OK)
|
||||
proxy = proxy_info_.ToPacString();
|
||||
|
||||
original_thread_->PostTask(
|
||||
FROM_HERE, base::BindOnce(&ResolveProxyHelper::SendProxyResult,
|
||||
base::Unretained(this), proxy));
|
||||
}
|
||||
|
||||
void ResolveProxyHelper::StartPendingRequestInIO(
|
||||
const GURL& url,
|
||||
net::ProxyService::PacRequest* pac_req) {
|
||||
// Verify the request wasn't started yet.
|
||||
DCHECK(nullptr == pac_req);
|
||||
|
||||
auto proxy_service = context_getter_->GetURLRequestContext()->proxy_service();
|
||||
|
||||
// Start the request.
|
||||
int result = proxy_service->ResolveProxy(
|
||||
url, std::string(), &proxy_info_,
|
||||
base::Bind(&ResolveProxyHelper::OnResolveProxyCompleted,
|
||||
base::Unretained(this)),
|
||||
&pac_req, nullptr, net::NetLogWithSource());
|
||||
|
||||
// Completed synchronously.
|
||||
if (result != net::ERR_IO_PENDING)
|
||||
OnResolveProxyCompleted(result);
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
59
atom/browser/net/resolve_proxy_helper.h
Normal file
59
atom/browser/net/resolve_proxy_helper.h
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright (c) 2018 GitHub, Inc.
|
||||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef ATOM_BROWSER_NET_RESOLVE_PROXY_HELPER_H_
|
||||
#define ATOM_BROWSER_NET_RESOLVE_PROXY_HELPER_H_
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
#include "base/memory/ref_counted.h"
|
||||
#include "net/proxy/proxy_service.h"
|
||||
#include "url/gurl.h"
|
||||
|
||||
namespace net {
|
||||
class URLRequestContextGetter;
|
||||
}
|
||||
|
||||
namespace atom {
|
||||
|
||||
class ResolveProxyHelper
|
||||
: public base::RefCountedThreadSafe<ResolveProxyHelper> {
|
||||
public:
|
||||
using ResolveProxyCallback = base::Callback<void(std::string)>;
|
||||
explicit ResolveProxyHelper(net::URLRequestContextGetter* getter);
|
||||
|
||||
void ResolveProxy(const GURL& url, const ResolveProxyCallback& callback);
|
||||
|
||||
private:
|
||||
friend class base::RefCountedThreadSafe<ResolveProxyHelper>;
|
||||
struct PendingRequest {
|
||||
public:
|
||||
PendingRequest(const GURL& url, const ResolveProxyCallback& callback)
|
||||
: url(url), callback(callback), pac_req(nullptr) {}
|
||||
|
||||
GURL url;
|
||||
ResolveProxyCallback callback;
|
||||
net::ProxyService::PacRequest* pac_req;
|
||||
};
|
||||
|
||||
~ResolveProxyHelper();
|
||||
|
||||
void StartPendingRequest();
|
||||
void StartPendingRequestInIO(const GURL& request,
|
||||
net::ProxyService::PacRequest* pac_req);
|
||||
void SendProxyResult(const std::string& proxy);
|
||||
void OnResolveProxyCompleted(int result);
|
||||
|
||||
net::ProxyInfo proxy_info_;
|
||||
std::deque<PendingRequest> pending_requests_;
|
||||
scoped_refptr<net::URLRequestContextGetter> context_getter_;
|
||||
scoped_refptr<base::SingleThreadTaskRunner> original_thread_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ResolveProxyHelper);
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
#endif // ATOM_BROWSER_NET_RESOLVE_PROXY_HELPER_H_
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
#include "atom/browser/node_debugger.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "base/command_line.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "libplatform/libplatform.h"
|
||||
@@ -28,10 +30,15 @@ void NodeDebugger::Start(node::NodePlatform* platform) {
|
||||
node::DebugOptions options;
|
||||
for (auto& arg : base::CommandLine::ForCurrentProcess()->argv()) {
|
||||
#if defined(OS_WIN)
|
||||
options.ParseOption("Electron", base::UTF16ToUTF8(arg));
|
||||
const std::string nice_arg = base::UTF16ToUTF8(arg);
|
||||
#else
|
||||
options.ParseOption("Electron", arg);
|
||||
const std::string& nice_arg = arg;
|
||||
#endif
|
||||
// Stop handling arguments after a "--" to be consistent with Chromium
|
||||
if (nice_arg == "--")
|
||||
break;
|
||||
|
||||
options.ParseOption("Electron", nice_arg);
|
||||
}
|
||||
|
||||
if (options.inspector_enabled()) {
|
||||
|
||||
@@ -17,9 +17,9 @@
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>electron.icns</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>2.0.1</string>
|
||||
<string>2.0.15</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.0.1</string>
|
||||
<string>2.0.15</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
<string>public.app-category.developer-tools</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
||||
@@ -56,8 +56,8 @@ END
|
||||
//
|
||||
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 2,0,1,0
|
||||
PRODUCTVERSION 2,0,1,0
|
||||
FILEVERSION 2,0,15,0
|
||||
PRODUCTVERSION 2,0,15,0
|
||||
FILEFLAGSMASK 0x3fL
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
@@ -74,12 +74,12 @@ BEGIN
|
||||
BEGIN
|
||||
VALUE "CompanyName", "GitHub, Inc."
|
||||
VALUE "FileDescription", "Electron"
|
||||
VALUE "FileVersion", "2.0.1"
|
||||
VALUE "FileVersion", "2.0.15"
|
||||
VALUE "InternalName", "electron.exe"
|
||||
VALUE "LegalCopyright", "Copyright (C) 2015 GitHub, Inc. All rights reserved."
|
||||
VALUE "OriginalFilename", "electron.exe"
|
||||
VALUE "ProductName", "Electron"
|
||||
VALUE "ProductVersion", "2.0.1"
|
||||
VALUE "ProductVersion", "2.0.15"
|
||||
VALUE "SquirrelAwareVersion", "1"
|
||||
END
|
||||
END
|
||||
|
||||
@@ -91,7 +91,9 @@ bool TriggerAcceleratorTableCommand(AcceleratorTable* table,
|
||||
if (base::ContainsKey(*table, accelerator)) {
|
||||
const accelerator_util::MenuItem& item = (*table)[accelerator];
|
||||
if (item.model->IsEnabledAt(item.position)) {
|
||||
item.model->ActivatedAt(item.position);
|
||||
const auto event_flags =
|
||||
accelerator.MaskOutKeyEventFlags(accelerator.modifiers());
|
||||
item.model->ActivatedAt(item.position, event_flags);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +123,9 @@ static base::scoped_nsobject<NSMenu> recentDocumentsMenuSwap_;
|
||||
[menu_ cancelTracking];
|
||||
isMenuOpen_ = NO;
|
||||
model_->MenuWillClose();
|
||||
closeCallback.Run();
|
||||
if (!closeCallback.is_null()) {
|
||||
BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, closeCallback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,8 @@ class GtkMessageBox : public NativeWindowObserver {
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const std::string& checkbox_label,
|
||||
bool checkbox_checked)
|
||||
bool checkbox_checked,
|
||||
const gfx::ImageSkia& icon)
|
||||
: cancel_id_(cancel_id),
|
||||
checkbox_checked_(false),
|
||||
parent_(static_cast<NativeWindow*>(parent_window)) {
|
||||
@@ -56,6 +57,21 @@ class GtkMessageBox : public NativeWindowObserver {
|
||||
if (!title.empty())
|
||||
gtk_window_set_title(GTK_WINDOW(dialog_), title.c_str());
|
||||
|
||||
if (!icon.isNull()) {
|
||||
// No easy way to obtain this programmatically, but GTK+'s docs
|
||||
// define GTK_ICON_SIZE_DIALOG to be 48 pixels
|
||||
static constexpr int pixel_width = 48;
|
||||
static constexpr int pixel_height = 48;
|
||||
GdkPixbuf* pixbuf = libgtkui::GdkPixbufFromSkBitmap(*icon.bitmap());
|
||||
GdkPixbuf* scaled_pixbuf = gdk_pixbuf_scale_simple(
|
||||
pixbuf, pixel_width, pixel_height, GDK_INTERP_BILINEAR);
|
||||
GtkWidget* w = gtk_image_new_from_pixbuf(scaled_pixbuf);
|
||||
gtk_message_dialog_set_image(GTK_MESSAGE_DIALOG(dialog_), w);
|
||||
gtk_widget_show(w);
|
||||
g_clear_pointer(&scaled_pixbuf, gdk_pixbuf_unref);
|
||||
g_clear_pointer(&pixbuf, gdk_pixbuf_unref);
|
||||
}
|
||||
|
||||
if (!checkbox_label.empty()) {
|
||||
GtkWidget* message_area =
|
||||
gtk_message_dialog_get_message_area(GTK_MESSAGE_DIALOG(dialog_));
|
||||
@@ -66,15 +82,15 @@ class GtkMessageBox : public NativeWindowObserver {
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(check_button),
|
||||
checkbox_checked);
|
||||
gtk_container_add(GTK_CONTAINER(message_area), check_button);
|
||||
gtk_widget_show(check_button);
|
||||
}
|
||||
|
||||
// Add buttons.
|
||||
GtkDialog* dialog = GTK_DIALOG(dialog_);
|
||||
for (size_t i = 0; i < buttons.size(); ++i) {
|
||||
GtkWidget* button = gtk_dialog_add_button(
|
||||
GTK_DIALOG(dialog_), TranslateToStock(i, buttons[i]), i);
|
||||
if (static_cast<int>(i) == default_id)
|
||||
gtk_widget_grab_focus(button);
|
||||
gtk_dialog_add_button(dialog, TranslateToStock(i, buttons[i]), i);
|
||||
}
|
||||
gtk_dialog_set_default_response(dialog, default_id);
|
||||
|
||||
// Parent window.
|
||||
if (parent_) {
|
||||
@@ -122,7 +138,7 @@ class GtkMessageBox : public NativeWindowObserver {
|
||||
}
|
||||
|
||||
void Show() {
|
||||
gtk_widget_show_all(dialog_);
|
||||
gtk_widget_show(dialog_);
|
||||
// We need to call gtk_window_present after making the widgets visible to
|
||||
// make sure window gets correctly raised and gets focus.
|
||||
int time = ui::X11EventSource::GetInstance()->GetTimestamp();
|
||||
@@ -195,9 +211,9 @@ int ShowMessageBox(NativeWindow* parent,
|
||||
const std::string& title,
|
||||
const std::string& message,
|
||||
const std::string& detail,
|
||||
const gfx::ImageSkia& /*icon*/) {
|
||||
const gfx::ImageSkia& icon) {
|
||||
return GtkMessageBox(parent, type, buttons, default_id, cancel_id, title,
|
||||
message, detail, "", false)
|
||||
message, detail, "", false, icon)
|
||||
.RunSynchronous();
|
||||
}
|
||||
|
||||
@@ -212,10 +228,10 @@ void ShowMessageBox(NativeWindow* parent,
|
||||
const std::string& detail,
|
||||
const std::string& checkbox_label,
|
||||
bool checkbox_checked,
|
||||
const gfx::ImageSkia& /*icon*/,
|
||||
const gfx::ImageSkia& icon,
|
||||
const MessageBoxCallback& callback) {
|
||||
(new GtkMessageBox(parent, type, buttons, default_id, cancel_id, title,
|
||||
message, detail, checkbox_label, checkbox_checked))
|
||||
message, detail, checkbox_label, checkbox_checked, icon))
|
||||
->RunAsynchronous(callback);
|
||||
}
|
||||
|
||||
@@ -223,7 +239,8 @@ void ShowErrorBox(const base::string16& title, const base::string16& content) {
|
||||
if (Browser::Get()->is_ready()) {
|
||||
GtkMessageBox(nullptr, MESSAGE_BOX_TYPE_ERROR, {"OK"}, -1, 0, "Error",
|
||||
base::UTF16ToUTF8(title).c_str(),
|
||||
base::UTF16ToUTF8(content).c_str(), "", false)
|
||||
base::UTF16ToUTF8(content).c_str(), "", false,
|
||||
gfx::ImageSkia())
|
||||
.RunSynchronous();
|
||||
} else {
|
||||
fprintf(stderr, ANSI_TEXT_BOLD ANSI_BACKGROUND_GRAY ANSI_FOREGROUND_RED
|
||||
|
||||
@@ -96,11 +96,6 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window,
|
||||
NSArray* ns_buttons = [alert buttons];
|
||||
int button_count = static_cast<int>([ns_buttons count]);
|
||||
|
||||
// Bind cancel id button to escape key if there is more than one button
|
||||
if (button_count > 1 && cancel_id >= 0 && cancel_id < button_count) {
|
||||
[[ns_buttons objectAtIndex:cancel_id] setKeyEquivalent:@"\e"];
|
||||
}
|
||||
|
||||
if (default_id >= 0 && default_id < button_count) {
|
||||
// Focus the button at default_id if the user opted to do so.
|
||||
// The first button added gets set as the default selected.
|
||||
@@ -109,6 +104,11 @@ NSAlert* CreateNSAlert(NativeWindow* parent_window,
|
||||
[[ns_buttons objectAtIndex:default_id] setKeyEquivalent:@"\r"];
|
||||
}
|
||||
|
||||
// Bind cancel id button to escape key if there is more than one button
|
||||
if (button_count > 1 && cancel_id >= 0 && cancel_id < button_count) {
|
||||
[[ns_buttons objectAtIndex:cancel_id] setKeyEquivalent:@"\e"];
|
||||
}
|
||||
|
||||
if (!checkbox_label.empty()) {
|
||||
alert.showsSuppressionButton = YES;
|
||||
alert.suppressionButton.title = base::SysUTF8ToNSString(checkbox_label);
|
||||
|
||||
@@ -40,9 +40,16 @@ const CGFloat kVerticalTitleMargin = 2;
|
||||
|
||||
@implementation StatusItemView
|
||||
|
||||
- (void)dealloc {
|
||||
trayIcon_ = nil;
|
||||
menuController_ = nil;
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (id)initWithImage:(NSImage*)image icon:(atom::TrayIconCocoa*)icon {
|
||||
image_.reset([image copy]);
|
||||
trayIcon_ = icon;
|
||||
menuController_ = nil;
|
||||
highlight_mode_ = atom::TrayIcon::HighlightMode::SELECTION;
|
||||
forceHighlight_ = NO;
|
||||
inMouseEventSequence_ = NO;
|
||||
@@ -85,6 +92,7 @@ const CGFloat kVerticalTitleMargin = 2;
|
||||
trackingArea_.reset();
|
||||
}
|
||||
[[NSStatusBar systemStatusBar] removeStatusItem:statusItem_];
|
||||
[statusItem_ setView:nil];
|
||||
statusItem_.reset();
|
||||
}
|
||||
|
||||
@@ -389,6 +397,11 @@ const CGFloat kVerticalTitleMargin = 2;
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (void)setNeedsDisplay:(BOOL)display {
|
||||
[self updateAttributedTitle];
|
||||
[super setNeedsDisplay:display];
|
||||
}
|
||||
|
||||
- (BOOL)shouldHighlight {
|
||||
switch (highlight_mode_) {
|
||||
case atom::TrayIcon::HighlightMode::ALWAYS:
|
||||
@@ -449,11 +462,18 @@ void TrayIconCocoa::SetContextMenu(AtomMenuModel* menu_model) {
|
||||
// Substribe to MenuClosed event.
|
||||
if (menu_model_)
|
||||
menu_model_->RemoveObserver(this);
|
||||
menu_model->AddObserver(this);
|
||||
|
||||
// Create native menu.
|
||||
menu_.reset([[AtomMenuController alloc] initWithModel:menu_model
|
||||
useDefaultAccelerator:NO]);
|
||||
menu_model_ = menu_model;
|
||||
|
||||
if (menu_model) {
|
||||
menu_model->AddObserver(this);
|
||||
// Create native menu.
|
||||
menu_.reset([[AtomMenuController alloc] initWithModel:menu_model
|
||||
useDefaultAccelerator:NO]);
|
||||
} else {
|
||||
menu_.reset();
|
||||
}
|
||||
|
||||
[status_item_view_ setMenuController:menu_.get()];
|
||||
}
|
||||
|
||||
|
||||
@@ -209,7 +209,9 @@ void GlobalMenuBarX11::SetMenu(AtomMenuModel* menu_model) {
|
||||
DbusmenuMenuitem* root_item = menuitem_new();
|
||||
menuitem_property_set(root_item, kPropertyLabel, "Root");
|
||||
menuitem_property_set_bool(root_item, kPropertyVisible, true);
|
||||
BuildMenuFromModel(menu_model, root_item);
|
||||
if (menu_model != nullptr) {
|
||||
BuildMenuFromModel(menu_model, root_item);
|
||||
}
|
||||
|
||||
server_set_root(server_, root_item);
|
||||
g_object_unref(root_item);
|
||||
|
||||
@@ -4,12 +4,19 @@
|
||||
|
||||
#include "atom/browser/ui/views/menu_bar.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "atom/browser/ui/views/menu_delegate.h"
|
||||
#include "atom/browser/ui/views/submenu_button.h"
|
||||
#include "ui/base/models/menu_model.h"
|
||||
#include "ui/views/background.h"
|
||||
#include "ui/views/layout/box_layout.h"
|
||||
|
||||
#if defined(USE_X11)
|
||||
#include "chrome/browser/ui/libgtkui/gtk_util.h"
|
||||
#endif
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "ui/gfx/color_utils.h"
|
||||
#endif
|
||||
@@ -130,13 +137,17 @@ void MenuBar::RefreshColorCache(const ui::NativeTheme* theme) {
|
||||
if (!theme)
|
||||
theme = ui::NativeTheme::GetInstanceForNativeUi();
|
||||
if (theme) {
|
||||
background_color_ =
|
||||
theme->GetSystemColor(ui::NativeTheme::kColorId_MenuBackgroundColor);
|
||||
#if defined(USE_X11)
|
||||
const std::string menubar_selector = "GtkMenuBar#menubar";
|
||||
background_color_ = libgtkui::GetBgColor(menubar_selector);
|
||||
|
||||
enabled_color_ = theme->GetSystemColor(
|
||||
ui::NativeTheme::kColorId_EnabledMenuItemForegroundColor);
|
||||
disabled_color_ = theme->GetSystemColor(
|
||||
ui::NativeTheme::kColorId_DisabledMenuItemForegroundColor);
|
||||
#else
|
||||
background_color_ =
|
||||
theme->GetSystemColor(ui::NativeTheme::kColorId_MenuBackgroundColor);
|
||||
#endif
|
||||
}
|
||||
#if defined(OS_WIN)
|
||||
|
||||
@@ -25,4 +25,11 @@ bool AtomDesktopWindowTreeHostWin::PreHandleMSG(
|
||||
return delegate_->PreHandleMSG(message, w_param, l_param, result);
|
||||
}
|
||||
|
||||
bool AtomDesktopWindowTreeHostWin::HasNativeFrame() const {
|
||||
// Since we never use chromium's titlebar implementation, we can just say
|
||||
// that we use a native titlebar. This will disable the repaint locking when
|
||||
// DWM composition is disabled.
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -27,6 +27,7 @@ class AtomDesktopWindowTreeHostWin : public views::DesktopWindowTreeHostWin {
|
||||
protected:
|
||||
bool PreHandleMSG(
|
||||
UINT message, WPARAM w_param, LPARAM l_param, LRESULT* result) override;
|
||||
bool HasNativeFrame() const override;
|
||||
|
||||
private:
|
||||
MessageHandlerDelegate* delegate_; // weak ref
|
||||
|
||||
@@ -201,8 +201,13 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches(
|
||||
|
||||
// --background-color.
|
||||
std::string color;
|
||||
if (web_preferences.GetString(options::kBackgroundColor, &color))
|
||||
if (web_preferences.GetString(options::kBackgroundColor, &color)) {
|
||||
command_line->AppendSwitchASCII(switches::kBackgroundColor, color);
|
||||
} else if (!(web_preferences.GetBoolean("offscreen", &b) && b)) {
|
||||
// For non-OSR WebContents, we expect to have white background, see
|
||||
// https://github.com/electron/electron/issues/13764 for more.
|
||||
command_line->AppendSwitchASCII(switches::kBackgroundColor, "#fff");
|
||||
}
|
||||
|
||||
// --guest-instance-id, which is used to identify guest WebContents.
|
||||
int guest_instance_id = 0;
|
||||
@@ -331,6 +336,11 @@ void WebContentsPreferences::OverrideWebkitPrefs(
|
||||
std::string encoding;
|
||||
if (self->web_preferences_.GetString("defaultEncoding", &encoding))
|
||||
prefs->default_encoding = encoding;
|
||||
|
||||
bool node_integration = false;
|
||||
self->web_preferences_.GetBoolean(options::kNodeIntegration,
|
||||
&node_integration);
|
||||
prefs->node_integration = node_integration;
|
||||
}
|
||||
|
||||
bool WebContentsPreferences::GetInteger(const std::string& attributeName,
|
||||
|
||||
@@ -227,6 +227,7 @@ void WebDialogHelper::RunFileChooser(
|
||||
flags |= file_dialog::FILE_DIALOG_MULTI_SELECTIONS;
|
||||
case content::FileChooserParams::Open:
|
||||
flags |= file_dialog::FILE_DIALOG_OPEN_FILE;
|
||||
flags |= file_dialog::FILE_DIALOG_TREAT_PACKAGE_APP_AS_DIRECTORY;
|
||||
break;
|
||||
case content::FileChooserParams::UploadFolder:
|
||||
flags |= file_dialog::FILE_DIALOG_OPEN_DIRECTORY;
|
||||
|
||||
@@ -223,6 +223,7 @@ NativeImage::NativeImage(v8::Isolate* isolate, const gfx::Image& image)
|
||||
isolate->AdjustAmountOfExternalAllocatedMemory(
|
||||
image_.ToImageSkia()->bitmap()->computeSize64());
|
||||
}
|
||||
MarkHighMemoryUsage();
|
||||
}
|
||||
|
||||
#if defined(OS_WIN)
|
||||
@@ -237,6 +238,7 @@ NativeImage::NativeImage(v8::Isolate* isolate, const base::FilePath& hicon_path)
|
||||
isolate->AdjustAmountOfExternalAllocatedMemory(
|
||||
image_.ToImageSkia()->bitmap()->computeSize64());
|
||||
}
|
||||
MarkHighMemoryUsage();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace std {
|
||||
template <typename Type1, typename Type2>
|
||||
struct hash<std::pair<Type1, Type2>> {
|
||||
std::size_t operator()(std::pair<Type1, Type2> value) const {
|
||||
return base::HashInts<Type1, Type2>(value.first, value.second);
|
||||
return base::HashInts(base::Hash(value.first), value.second);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -114,8 +114,9 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||
dict.SetMethod("setRemoteCallbackFreer", &atom::RemoteCallbackFreer::BindTo);
|
||||
dict.SetMethod("setRemoteObjectFreer", &atom::RemoteObjectFreer::BindTo);
|
||||
dict.SetMethod("createIDWeakMap", &atom::api::KeyWeakMap<int32_t>::Create);
|
||||
dict.SetMethod("createDoubleIDWeakMap",
|
||||
&atom::api::KeyWeakMap<std::pair<int64_t, int32_t>>::Create);
|
||||
dict.SetMethod(
|
||||
"createDoubleIDWeakMap",
|
||||
&atom::api::KeyWeakMap<std::pair<std::string, int32_t>>::Create);
|
||||
dict.SetMethod("requestGarbageCollectionForTesting",
|
||||
&RequestGarbageCollectionForTesting);
|
||||
dict.SetMethod("isSameOrigin", &IsSameOrigin);
|
||||
|
||||
@@ -15,17 +15,20 @@ namespace atom {
|
||||
// static
|
||||
void RemoteCallbackFreer::BindTo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id,
|
||||
content::WebContents* web_contents) {
|
||||
new RemoteCallbackFreer(isolate, target, object_id, web_contents);
|
||||
new RemoteCallbackFreer(isolate, target, context_id, object_id, web_contents);
|
||||
}
|
||||
|
||||
RemoteCallbackFreer::RemoteCallbackFreer(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id,
|
||||
content::WebContents* web_contents)
|
||||
: ObjectLifeMonitor(isolate, target),
|
||||
content::WebContentsObserver(web_contents),
|
||||
context_id_(context_id),
|
||||
object_id_(object_id) {
|
||||
}
|
||||
|
||||
@@ -36,6 +39,7 @@ void RemoteCallbackFreer::RunDestructor() {
|
||||
base::string16 channel =
|
||||
base::ASCIIToUTF16("ELECTRON_RENDERER_RELEASE_CALLBACK");
|
||||
base::ListValue args;
|
||||
args.AppendString(context_id_);
|
||||
args.AppendInteger(object_id_);
|
||||
auto frame_host = web_contents()->GetMainFrame();
|
||||
if (frame_host) {
|
||||
|
||||
@@ -4,6 +4,9 @@
|
||||
|
||||
#ifndef ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_
|
||||
#define ATOM_COMMON_API_REMOTE_CALLBACK_FREER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "atom/common/api/object_life_monitor.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
|
||||
@@ -14,12 +17,14 @@ class RemoteCallbackFreer : public ObjectLifeMonitor,
|
||||
public:
|
||||
static void BindTo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id,
|
||||
content::WebContents* web_conents);
|
||||
|
||||
protected:
|
||||
RemoteCallbackFreer(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id,
|
||||
content::WebContents* web_conents);
|
||||
~RemoteCallbackFreer() override;
|
||||
@@ -30,6 +35,7 @@ class RemoteCallbackFreer : public ObjectLifeMonitor,
|
||||
void RenderViewDeleted(content::RenderViewHost*) override;
|
||||
|
||||
private:
|
||||
std::string context_id_;
|
||||
int object_id_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(RemoteCallbackFreer);
|
||||
|
||||
@@ -27,14 +27,19 @@ content::RenderFrame* GetCurrentRenderFrame() {
|
||||
} // namespace
|
||||
|
||||
// static
|
||||
void RemoteObjectFreer::BindTo(
|
||||
v8::Isolate* isolate, v8::Local<v8::Object> target, int object_id) {
|
||||
new RemoteObjectFreer(isolate, target, object_id);
|
||||
void RemoteObjectFreer::BindTo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id) {
|
||||
new RemoteObjectFreer(isolate, target, context_id, object_id);
|
||||
}
|
||||
|
||||
RemoteObjectFreer::RemoteObjectFreer(
|
||||
v8::Isolate* isolate, v8::Local<v8::Object> target, int object_id)
|
||||
RemoteObjectFreer::RemoteObjectFreer(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id)
|
||||
: ObjectLifeMonitor(isolate, target),
|
||||
context_id_(context_id),
|
||||
object_id_(object_id),
|
||||
routing_id_(MSG_ROUTING_NONE) {
|
||||
content::RenderFrame* render_frame = GetCurrentRenderFrame();
|
||||
@@ -55,6 +60,7 @@ void RemoteObjectFreer::RunDestructor() {
|
||||
base::string16 channel = base::ASCIIToUTF16("ipc-message");
|
||||
base::ListValue args;
|
||||
args.AppendString("ELECTRON_BROWSER_DEREFERENCE");
|
||||
args.AppendString(context_id_);
|
||||
args.AppendInteger(object_id_);
|
||||
render_frame->Send(new AtomFrameHostMsg_Message(render_frame->GetRoutingID(),
|
||||
channel, args));
|
||||
|
||||
@@ -5,23 +5,30 @@
|
||||
#ifndef ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_
|
||||
#define ATOM_COMMON_API_REMOTE_OBJECT_FREER_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "atom/common/api/object_life_monitor.h"
|
||||
|
||||
namespace atom {
|
||||
|
||||
class RemoteObjectFreer : public ObjectLifeMonitor {
|
||||
public:
|
||||
static void BindTo(
|
||||
v8::Isolate* isolate, v8::Local<v8::Object> target, int object_id);
|
||||
static void BindTo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id);
|
||||
|
||||
protected:
|
||||
RemoteObjectFreer(
|
||||
v8::Isolate* isolate, v8::Local<v8::Object> target, int object_id);
|
||||
RemoteObjectFreer(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id);
|
||||
~RemoteObjectFreer() override;
|
||||
|
||||
void RunDestructor() override;
|
||||
|
||||
private:
|
||||
std::string context_id_;
|
||||
int object_id_;
|
||||
int routing_id_;
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
#define ATOM_MAJOR_VERSION 2
|
||||
#define ATOM_MINOR_VERSION 0
|
||||
#define ATOM_PATCH_VERSION 1
|
||||
#define ATOM_PATCH_VERSION 15
|
||||
// #define ATOM_PRE_RELEASE_VERSION
|
||||
|
||||
#ifndef ATOM_STRINGIFY
|
||||
|
||||
@@ -168,6 +168,7 @@ void NodeBindings::Initialize() {
|
||||
|
||||
// Init node.
|
||||
// (we assume node::Init would not modify the parameters under embedded mode).
|
||||
// NOTE: If you change this line, please ping @codebytere or @MarshallOfSound
|
||||
node::Init(nullptr, nullptr, nullptr, nullptr);
|
||||
|
||||
#if defined(OS_WIN)
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include <windows.h>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/sys_info.h"
|
||||
|
||||
extern "C" {
|
||||
#include "vendor/node/deps/uv/src/win/internal.h"
|
||||
@@ -16,6 +17,17 @@ namespace atom {
|
||||
|
||||
NodeBindingsWin::NodeBindingsWin(BrowserEnvironment browser_env)
|
||||
: NodeBindings(browser_env) {
|
||||
// on single-core the io comp port NumberOfConcurrentThreads needs to be 2
|
||||
// to avoid cpu pegging likely caused by a busy loop in PollEvents
|
||||
if (base::SysInfo::NumberOfProcessors() == 1) {
|
||||
// the expectation is the uv_loop_ has just been initialized
|
||||
// which makes iocp replacement safe
|
||||
CHECK_EQ(0u, uv_loop_->active_handles);
|
||||
|
||||
if (uv_loop_->iocp && uv_loop_->iocp != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(uv_loop_->iocp);
|
||||
uv_loop_->iocp = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 2);
|
||||
}
|
||||
}
|
||||
|
||||
NodeBindingsWin::~NodeBindingsWin() {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include "atom/renderer/atom_render_frame_observer.h"
|
||||
#include "atom/renderer/web_worker_observer.h"
|
||||
#include "base/command_line.h"
|
||||
#include "content/public/common/web_preferences.h"
|
||||
#include "content/public/renderer/render_frame.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "third_party/WebKit/public/web/WebDocument.h"
|
||||
@@ -82,11 +83,22 @@ void AtomRendererClient::RunScriptsAtDocumentEnd(
|
||||
|
||||
void AtomRendererClient::DidCreateScriptContext(
|
||||
v8::Handle<v8::Context> context, content::RenderFrame* render_frame) {
|
||||
RendererClientBase::DidCreateScriptContext(context, render_frame);
|
||||
|
||||
// Only allow node integration for the main frame, unless it is a devtools
|
||||
// extension page.
|
||||
if (!render_frame->IsMainFrame() && !IsDevToolsExtension(render_frame))
|
||||
return;
|
||||
|
||||
// Don't allow node integration if this is a child window and it does not have
|
||||
// node integration enabled. Otherwise we would have memory leak in the child
|
||||
// window since we don't clean up node environments.
|
||||
//
|
||||
// TODO(zcbenz): We shouldn't allow node integration even for the top frame.
|
||||
if (!render_frame->GetWebkitPreferences().node_integration &&
|
||||
render_frame->GetWebFrame()->Opener())
|
||||
return;
|
||||
|
||||
// Prepare the node bindings.
|
||||
if (!node_integration_initialized_) {
|
||||
node_integration_initialized_ = true;
|
||||
|
||||
@@ -92,6 +92,12 @@ void InitializeBindings(v8::Local<v8::Object> binding,
|
||||
b.SetMethod("getArgv", GetArgv);
|
||||
b.SetMethod("getProcessMemoryInfo", &AtomBindings::GetProcessMemoryInfo);
|
||||
b.SetMethod("getSystemMemoryInfo", &AtomBindings::GetSystemMemoryInfo);
|
||||
|
||||
// Pass in CLI flags needed to setup the renderer
|
||||
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
||||
if (command_line->HasSwitch(switches::kGuestInstanceID))
|
||||
b.Set(options::kGuestInstanceID,
|
||||
command_line->GetSwitchValueASCII(switches::kGuestInstanceID));
|
||||
}
|
||||
|
||||
class AtomSandboxedRenderFrameObserver : public AtomRenderFrameObserver {
|
||||
@@ -153,6 +159,7 @@ void AtomSandboxedRendererClient::RenderViewCreated(
|
||||
|
||||
void AtomSandboxedRendererClient::DidCreateScriptContext(
|
||||
v8::Handle<v8::Context> context, content::RenderFrame* render_frame) {
|
||||
RendererClientBase::DidCreateScriptContext(context, render_frame);
|
||||
|
||||
// Only allow preload for the main frame or
|
||||
// For devtools we still want to run the preload_bundle script
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
#include "atom/renderer/preferences_manager.h"
|
||||
#include "base/command_line.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/process/process_handle.h"
|
||||
#include "base/strings/string_split.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "chrome/renderer/media/chrome_key_systems.h"
|
||||
#include "chrome/renderer/pepper/pepper_helper.h"
|
||||
#include "chrome/renderer/printing/print_web_view_helper.h"
|
||||
@@ -44,6 +46,14 @@
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
// This is defined in later versions of Chromium, remove this if you see
|
||||
// compiler complaining duplicate defines.
|
||||
#if defined(OS_WIN) || defined(OS_FUCHSIA)
|
||||
#define CrPRIdPid "ld"
|
||||
#else
|
||||
#define CrPRIdPid "d"
|
||||
#endif
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
@@ -78,6 +88,19 @@ RendererClientBase::RendererClientBase() {
|
||||
RendererClientBase::~RendererClientBase() {
|
||||
}
|
||||
|
||||
void RendererClientBase::DidCreateScriptContext(
|
||||
v8::Handle<v8::Context> context,
|
||||
content::RenderFrame* render_frame) {
|
||||
// global.setHidden("contextId", `${processId}-${++nextContextId}`)
|
||||
std::string context_id = base::StringPrintf(
|
||||
"%" CrPRIdPid "-%d", base::GetCurrentProcId(), ++next_context_id_);
|
||||
v8::Isolate* isolate = context->GetIsolate();
|
||||
v8::Local<v8::String> key = mate::StringToSymbol(isolate, "contextId");
|
||||
v8::Local<v8::Private> private_key = v8::Private::ForApi(isolate, key);
|
||||
v8::Local<v8::Value> value = mate::ConvertToV8(isolate, context_id);
|
||||
context->Global()->SetPrivate(context, private_key, value);
|
||||
}
|
||||
|
||||
void RendererClientBase::AddRenderBindings(
|
||||
v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> binding_object) {
|
||||
|
||||
@@ -21,7 +21,7 @@ class RendererClientBase : public content::ContentRendererClient {
|
||||
virtual ~RendererClientBase();
|
||||
|
||||
virtual void DidCreateScriptContext(
|
||||
v8::Handle<v8::Context> context, content::RenderFrame* render_frame) = 0;
|
||||
v8::Handle<v8::Context> context, content::RenderFrame* render_frame);
|
||||
virtual void WillReleaseScriptContext(
|
||||
v8::Handle<v8::Context> context, content::RenderFrame* render_frame) = 0;
|
||||
virtual void DidClearWindowObject(content::RenderFrame* render_frame);
|
||||
@@ -57,6 +57,9 @@ class RendererClientBase : public content::ContentRendererClient {
|
||||
private:
|
||||
std::unique_ptr<PreferencesManager> preferences_manager_;
|
||||
bool isolated_world_;
|
||||
|
||||
// An increasing ID used for indentifying an V8 context in this process.
|
||||
int next_context_id_ = 0;
|
||||
};
|
||||
|
||||
} // namespace atom
|
||||
|
||||
@@ -4,6 +4,13 @@
|
||||
|
||||
#include "atom/utility/atom_content_utility_client.h"
|
||||
|
||||
#include "content/public/common/service_manager_connection.h"
|
||||
#include "content/public/common/simple_connection_filter.h"
|
||||
#include "content/public/utility/utility_thread.h"
|
||||
#include "mojo/public/cpp/bindings/strong_binding.h"
|
||||
#include "net/proxy/mojo_proxy_resolver_factory_impl.h"
|
||||
#include "services/service_manager/public/cpp/binder_registry.h"
|
||||
|
||||
#if defined(OS_WIN)
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "chrome/utility/printing_handler_win.h"
|
||||
@@ -11,6 +18,16 @@
|
||||
|
||||
namespace atom {
|
||||
|
||||
namespace {
|
||||
|
||||
void CreateProxyResolverFactory(
|
||||
net::interfaces::ProxyResolverFactoryRequest request) {
|
||||
mojo::MakeStrongBinding(base::MakeUnique<net::MojoProxyResolverFactoryImpl>(),
|
||||
std::move(request));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
AtomContentUtilityClient::AtomContentUtilityClient() {
|
||||
#if defined(OS_WIN)
|
||||
handlers_.push_back(base::MakeUnique<printing::PrintingHandlerWin>());
|
||||
@@ -20,6 +37,23 @@ AtomContentUtilityClient::AtomContentUtilityClient() {
|
||||
AtomContentUtilityClient::~AtomContentUtilityClient() {
|
||||
}
|
||||
|
||||
void AtomContentUtilityClient::UtilityThreadStarted() {
|
||||
content::ServiceManagerConnection* connection =
|
||||
content::ChildThread::Get()->GetServiceManagerConnection();
|
||||
|
||||
// NOTE: Some utility process instances are not connected to the Service
|
||||
// Manager. Nothing left to do in that case.
|
||||
if (!connection)
|
||||
return;
|
||||
|
||||
auto registry = base::MakeUnique<service_manager::BinderRegistry>();
|
||||
registry->AddInterface<net::interfaces::ProxyResolverFactory>(
|
||||
base::Bind(CreateProxyResolverFactory),
|
||||
base::ThreadTaskRunnerHandle::Get());
|
||||
connection->AddConnectionFilter(
|
||||
base::MakeUnique<content::SimpleConnectionFilter>(std::move(registry)));
|
||||
}
|
||||
|
||||
bool AtomContentUtilityClient::OnMessageReceived(
|
||||
const IPC::Message& message) {
|
||||
#if defined(OS_WIN)
|
||||
|
||||
@@ -20,6 +20,7 @@ class AtomContentUtilityClient : public content::ContentUtilityClient {
|
||||
AtomContentUtilityClient();
|
||||
~AtomContentUtilityClient() override;
|
||||
|
||||
void UtilityThreadStarted() override;
|
||||
bool OnMessageReceived(const IPC::Message& message) override;
|
||||
|
||||
private:
|
||||
|
||||
@@ -116,6 +116,8 @@
|
||||
'<(libchromiumcontent_dir)/libcommon.a',
|
||||
# services/device/wake_lock/power_save_blocker/
|
||||
'<(libchromiumcontent_dir)/libpower_save_blocker.a',
|
||||
# net/proxy/mojo_*
|
||||
'<(libchromiumcontent_dir)/libnet_proxy_service.a',
|
||||
# Friends of libpdf.a:
|
||||
# On Linux we have to use "--whole-archive" to include
|
||||
# all symbols, otherwise there will be plenty of
|
||||
@@ -200,6 +202,8 @@
|
||||
'<(libchromiumcontent_dir)/libcommon.a',
|
||||
# services/device/wake_lock/power_save_blocker/
|
||||
'<(libchromiumcontent_dir)/libpower_save_blocker.a',
|
||||
# net/proxy/mojo_*
|
||||
'<(libchromiumcontent_dir)/libnet_proxy_service.a',
|
||||
# Friends of libpdf.a:
|
||||
'<(libchromiumcontent_dir)/libpdf.a',
|
||||
'<(libchromiumcontent_dir)/libppapi_cpp_objects.a',
|
||||
@@ -268,6 +272,20 @@
|
||||
],
|
||||
},
|
||||
}],
|
||||
# In the OSX 10.10 SDK, CoreBluetooth became a top level framework.
|
||||
# Previously, it was nested in IOBluetooth. In order for Chrome to run on
|
||||
# OSes older than OSX 10.10, the top level CoreBluetooth framework must be
|
||||
# weakly linked.
|
||||
['mac_sdk=="10.10" and libchromiumcontent_component==0', {
|
||||
'direct_dependent_settings': {
|
||||
'xcode_settings': {
|
||||
'OTHER_LDFLAGS': [
|
||||
'-weak_framework',
|
||||
'CoreBluetooth',
|
||||
],
|
||||
},
|
||||
},
|
||||
}],
|
||||
]
|
||||
}], # OS=="mac"
|
||||
['OS=="win"', {
|
||||
@@ -296,6 +314,8 @@
|
||||
'<(libchromiumcontent_dir)/common.lib',
|
||||
# services/device/wake_lock/power_save_blocker/
|
||||
'<(libchromiumcontent_dir)/power_save_blocker.lib',
|
||||
# net/proxy/mojo_*
|
||||
'<(libchromiumcontent_dir)/net_proxy_service.lib',
|
||||
# Friends of pdf.lib:
|
||||
'<(libchromiumcontent_dir)/pdf.lib',
|
||||
'<(libchromiumcontent_dir)/ppapi_cpp_objects.lib',
|
||||
|
||||
@@ -100,14 +100,12 @@ BrowserContext::~BrowserContext() {
|
||||
}
|
||||
|
||||
void BrowserContext::InitPrefs() {
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
auto prefs_path = GetPath().Append(FILE_PATH_LITERAL("Preferences"));
|
||||
PrefServiceFactory prefs_factory;
|
||||
scoped_refptr<JsonPrefStore> pref_store =
|
||||
base::MakeRefCounted<JsonPrefStore>(prefs_path);
|
||||
{
|
||||
base::ThreadRestrictions::ScopedAllowIO allow_io;
|
||||
pref_store->ReadPrefs(); // Synchronous.
|
||||
}
|
||||
pref_store->ReadPrefs(); // Synchronous.
|
||||
prefs_factory.set_user_prefs(pref_store);
|
||||
|
||||
auto registry = make_scoped_refptr(new PrefRegistrySimple);
|
||||
|
||||
@@ -184,10 +184,20 @@ void OverrideAppLogsPath() {
|
||||
}
|
||||
#endif
|
||||
|
||||
void BrowserMainParts::PreEarlyInitialization() {
|
||||
void BrowserMainParts::InitializeFeatureList() {
|
||||
auto* cmd_line = base::CommandLine::ForCurrentProcess();
|
||||
const auto enable_features =
|
||||
cmd_line->GetSwitchValueASCII(switches::kEnableFeatures);
|
||||
const auto disable_features =
|
||||
cmd_line->GetSwitchValueASCII(switches::kDisableFeatures);
|
||||
|
||||
std::unique_ptr<base::FeatureList> feature_list(new base::FeatureList);
|
||||
feature_list->InitializeFromCommandLine("", "");
|
||||
feature_list->InitializeFromCommandLine(enable_features, disable_features);
|
||||
base::FeatureList::SetInstance(std::move(feature_list));
|
||||
}
|
||||
|
||||
void BrowserMainParts::PreEarlyInitialization() {
|
||||
InitializeFeatureList();
|
||||
OverrideAppLogsPath();
|
||||
#if defined(USE_X11)
|
||||
views::LinuxUI::SetInstance(BuildGtkUi());
|
||||
|
||||
@@ -46,6 +46,8 @@ class BrowserMainParts : public content::BrowserMainParts {
|
||||
int PreCreateThreads() override;
|
||||
void PostDestroyThreads() override;
|
||||
|
||||
void InitializeFeatureList();
|
||||
|
||||
private:
|
||||
#if defined(OS_MACOSX)
|
||||
void InitializeMainNib();
|
||||
|
||||
@@ -698,6 +698,8 @@ void InspectableWebContentsImpl::WebContentsDestroyed() {
|
||||
for (const auto& pair : pending_requests_)
|
||||
delete pair.first;
|
||||
|
||||
pending_requests_.clear();
|
||||
|
||||
if (view_ && view_->GetDelegate())
|
||||
view_->GetDelegate()->DevToolsClosed();
|
||||
}
|
||||
|
||||
117
brightray/browser/net/chrome_mojo_proxy_resolver_factory.cc
Normal file
117
brightray/browser/net/chrome_mojo_proxy_resolver_factory.cc
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "brightray/browser/net/chrome_mojo_proxy_resolver_factory.h"
|
||||
|
||||
#include <utility>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/memory/ptr_util.h"
|
||||
#include "base/memory/singleton.h"
|
||||
#include "base/single_thread_task_runner.h"
|
||||
#include "base/strings/utf_string_conversions.h"
|
||||
#include "base/threading/thread_task_runner_handle.h"
|
||||
#include "content/public/browser/browser_thread.h"
|
||||
#include "content/public/browser/utility_process_host.h"
|
||||
#include "content/public/browser/utility_process_host_client.h"
|
||||
#include "services/service_manager/public/cpp/interface_provider.h"
|
||||
|
||||
namespace {
|
||||
const int kUtilityProcessIdleTimeoutSeconds = 5;
|
||||
}
|
||||
|
||||
// static
|
||||
ChromeMojoProxyResolverFactory* ChromeMojoProxyResolverFactory::GetInstance() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
return base::Singleton<
|
||||
ChromeMojoProxyResolverFactory,
|
||||
base::LeakySingletonTraits<ChromeMojoProxyResolverFactory>>::get();
|
||||
}
|
||||
|
||||
ChromeMojoProxyResolverFactory::ChromeMojoProxyResolverFactory() {
|
||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||
}
|
||||
|
||||
ChromeMojoProxyResolverFactory::~ChromeMojoProxyResolverFactory() {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
}
|
||||
|
||||
std::unique_ptr<base::ScopedClosureRunner>
|
||||
ChromeMojoProxyResolverFactory::CreateResolver(
|
||||
const std::string& pac_script,
|
||||
mojo::InterfaceRequest<net::interfaces::ProxyResolver> req,
|
||||
net::interfaces::ProxyResolverFactoryRequestClientPtr client) {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
if (!resolver_factory_)
|
||||
CreateFactory();
|
||||
|
||||
if (!resolver_factory_) {
|
||||
// If factory creation failed, close |req|'s message pipe, which should
|
||||
// cause a connection error.
|
||||
req = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
idle_timer_.Stop();
|
||||
num_proxy_resolvers_++;
|
||||
resolver_factory_->CreateResolver(pac_script, std::move(req),
|
||||
std::move(client));
|
||||
return base::MakeUnique<base::ScopedClosureRunner>(
|
||||
base::Bind(&ChromeMojoProxyResolverFactory::OnResolverDestroyed,
|
||||
base::Unretained(this)));
|
||||
}
|
||||
|
||||
void ChromeMojoProxyResolverFactory::CreateFactory() {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
DCHECK(!resolver_factory_);
|
||||
DCHECK(!weak_utility_process_host_);
|
||||
|
||||
DVLOG(1) << "Attempting to create utility process for proxy resolver";
|
||||
content::UtilityProcessHost* utility_process_host =
|
||||
content::UtilityProcessHost::Create(
|
||||
scoped_refptr<content::UtilityProcessHostClient>(),
|
||||
base::ThreadTaskRunnerHandle::Get());
|
||||
utility_process_host->SetName(base::ASCIIToUTF16("Electron Proxy Resolver"));
|
||||
bool process_started = utility_process_host->Start();
|
||||
if (process_started) {
|
||||
BindInterface(utility_process_host, &resolver_factory_);
|
||||
weak_utility_process_host_ = utility_process_host->AsWeakPtr();
|
||||
} else {
|
||||
LOG(ERROR) << "Unable to connect to utility process";
|
||||
return;
|
||||
}
|
||||
|
||||
resolver_factory_.set_connection_error_handler(base::Bind(
|
||||
&ChromeMojoProxyResolverFactory::DestroyFactory, base::Unretained(this)));
|
||||
}
|
||||
|
||||
void ChromeMojoProxyResolverFactory::DestroyFactory() {
|
||||
resolver_factory_.reset();
|
||||
delete weak_utility_process_host_.get();
|
||||
weak_utility_process_host_.reset();
|
||||
}
|
||||
|
||||
void ChromeMojoProxyResolverFactory::OnResolverDestroyed() {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
DCHECK_GT(num_proxy_resolvers_, 0u);
|
||||
if (--num_proxy_resolvers_ == 0) {
|
||||
// When all proxy resolvers have been destroyed, the proxy resolver factory
|
||||
// is no longer needed. However, new proxy resolvers may be created
|
||||
// shortly after being destroyed (e.g. due to a network change).
|
||||
//
|
||||
// On desktop, where a utility process is used, if the utility process is
|
||||
// shut down immediately, this would cause unnecessary process churn, so
|
||||
// wait for an idle timeout before shutting down the proxy resolver utility
|
||||
// process.
|
||||
idle_timer_.Start(
|
||||
FROM_HERE,
|
||||
base::TimeDelta::FromSeconds(kUtilityProcessIdleTimeoutSeconds), this,
|
||||
&ChromeMojoProxyResolverFactory::OnIdleTimeout);
|
||||
}
|
||||
}
|
||||
|
||||
void ChromeMojoProxyResolverFactory::OnIdleTimeout() {
|
||||
DCHECK(thread_checker_.CalledOnValidThread());
|
||||
DCHECK_EQ(num_proxy_resolvers_, 0u);
|
||||
DestroyFactory();
|
||||
}
|
||||
73
brightray/browser/net/chrome_mojo_proxy_resolver_factory.h
Normal file
73
brightray/browser/net/chrome_mojo_proxy_resolver_factory.h
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2015 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef BRIGHTRAY_BROWSER_NET_CHROME_MOJO_PROXY_RESOLVER_FACTORY_H_
|
||||
#define BRIGHTRAY_BROWSER_NET_CHROME_MOJO_PROXY_RESOLVER_FACTORY_H_
|
||||
|
||||
#include <stddef.h>
|
||||
#include <string>
|
||||
|
||||
#include "base/macros.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "base/threading/thread_checker.h"
|
||||
#include "base/timer/timer.h"
|
||||
#include "net/proxy/mojo_proxy_resolver_factory.h"
|
||||
|
||||
namespace content {
|
||||
class UtilityProcessHost;
|
||||
}
|
||||
|
||||
namespace base {
|
||||
template <typename Type>
|
||||
struct DefaultSingletonTraits;
|
||||
} // namespace base
|
||||
|
||||
// A factory used to create connections to Mojo proxy resolver services. On
|
||||
// Android, the proxy resolvers will run in the browser process, and on other
|
||||
// platforms, they'll all be run in the same utility process. Utility process
|
||||
// crashes are detected and the utility process is automatically restarted.
|
||||
class ChromeMojoProxyResolverFactory : public net::MojoProxyResolverFactory {
|
||||
public:
|
||||
static ChromeMojoProxyResolverFactory* GetInstance();
|
||||
|
||||
// Overridden from net::MojoProxyResolverFactory:
|
||||
std::unique_ptr<base::ScopedClosureRunner> CreateResolver(
|
||||
const std::string& pac_script,
|
||||
mojo::InterfaceRequest<net::interfaces::ProxyResolver> req,
|
||||
net::interfaces::ProxyResolverFactoryRequestClientPtr client) override;
|
||||
|
||||
private:
|
||||
friend struct base::DefaultSingletonTraits<ChromeMojoProxyResolverFactory>;
|
||||
ChromeMojoProxyResolverFactory();
|
||||
~ChromeMojoProxyResolverFactory() override;
|
||||
|
||||
// Creates the proxy resolver factory. On desktop, creates a new utility
|
||||
// process before creating it out of process. On Android, creates it on the
|
||||
// current thread.
|
||||
void CreateFactory();
|
||||
|
||||
// Destroys |resolver_factory_|.
|
||||
void DestroyFactory();
|
||||
|
||||
// Invoked each time a proxy resolver is destroyed.
|
||||
void OnResolverDestroyed();
|
||||
|
||||
// Invoked once an idle timeout has elapsed after all proxy resolvers are
|
||||
// destroyed.
|
||||
void OnIdleTimeout();
|
||||
|
||||
net::interfaces::ProxyResolverFactoryPtr resolver_factory_;
|
||||
|
||||
base::WeakPtr<content::UtilityProcessHost> weak_utility_process_host_;
|
||||
|
||||
size_t num_proxy_resolvers_ = 0;
|
||||
|
||||
base::OneShotTimer idle_timer_;
|
||||
|
||||
base::ThreadChecker thread_checker_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ChromeMojoProxyResolverFactory);
|
||||
};
|
||||
|
||||
#endif // BRIGHTRAY_BROWSER_NET_CHROME_MOJO_PROXY_RESOLVER_FACTORY_H_
|
||||
@@ -52,8 +52,7 @@ void NetLog::StartLogging() {
|
||||
base::FilePath log_path =
|
||||
command_line->GetSwitchValuePath(switches::kLogNetLog);
|
||||
std::unique_ptr<base::Value> constants(GetConstants());
|
||||
net::NetLogCaptureMode capture_mode =
|
||||
net::NetLogCaptureMode::IncludeCookiesAndCredentials();
|
||||
net::NetLogCaptureMode capture_mode = net::NetLogCaptureMode::Default();
|
||||
|
||||
file_net_log_observer_ =
|
||||
net::FileNetLogObserver::CreateUnbounded(log_path, std::move(constants));
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#include "base/threading/sequenced_worker_pool.h"
|
||||
#include "base/threading/worker_pool.h"
|
||||
#include "brightray/browser/browser_client.h"
|
||||
#include "brightray/browser/net/chrome_mojo_proxy_resolver_factory.h"
|
||||
#include "brightray/browser/net/devtools_network_controller_handle.h"
|
||||
#include "brightray/browser/net/devtools_network_transaction_factory.h"
|
||||
#include "brightray/browser/net/require_ct_delegate.h"
|
||||
@@ -41,7 +42,7 @@
|
||||
#include "net/proxy/proxy_config_service.h"
|
||||
#include "net/proxy/proxy_script_fetcher_impl.h"
|
||||
#include "net/proxy/proxy_service.h"
|
||||
#include "net/proxy/proxy_service_v8.h"
|
||||
#include "net/proxy/proxy_service_mojo.h"
|
||||
#include "net/ssl/channel_id_service.h"
|
||||
#include "net/ssl/default_channel_id_store.h"
|
||||
#include "net/ssl/ssl_config_service_defaults.h"
|
||||
@@ -229,14 +230,12 @@ net::URLRequestContext* URLRequestContextGetter::GetURLRequestContext() {
|
||||
storage_->set_proxy_service(net::ProxyService::CreateFixed(
|
||||
proxy_config));
|
||||
} else {
|
||||
storage_->set_proxy_service(
|
||||
net::CreateProxyServiceUsingV8ProxyResolver(
|
||||
std::move(proxy_config_service_),
|
||||
new net::ProxyScriptFetcherImpl(url_request_context_.get()),
|
||||
dhcp_factory.Create(url_request_context_.get()),
|
||||
host_resolver.get(),
|
||||
nullptr,
|
||||
url_request_context_->network_delegate()));
|
||||
storage_->set_proxy_service(net::CreateProxyServiceUsingMojoFactory(
|
||||
ChromeMojoProxyResolverFactory::GetInstance(),
|
||||
std::move(proxy_config_service_),
|
||||
new net::ProxyScriptFetcherImpl(url_request_context_.get()),
|
||||
dhcp_factory.Create(url_request_context_.get()), host_resolver.get(),
|
||||
nullptr, url_request_context_->network_delegate()));
|
||||
}
|
||||
|
||||
std::vector<std::string> schemes;
|
||||
|
||||
@@ -63,6 +63,8 @@
|
||||
'browser/net/devtools_network_transaction.h',
|
||||
'browser/net/devtools_network_upload_data_stream.cc',
|
||||
'browser/net/devtools_network_upload_data_stream.h',
|
||||
'browser/net/chrome_mojo_proxy_resolver_factory.cc',
|
||||
'browser/net/chrome_mojo_proxy_resolver_factory.h',
|
||||
'browser/net/require_ct_delegate.cc',
|
||||
'browser/net/require_ct_delegate.h',
|
||||
'browser/net_log.cc',
|
||||
|
||||
@@ -147,6 +147,7 @@
|
||||
'BUILDING_V8_SHARED',
|
||||
'BUILDING_V8_PLATFORM_SHARED',
|
||||
'BUILDING_V8_BASE_SHARED',
|
||||
'NODE_WITHOUT_NODE_OPTIONS',
|
||||
],
|
||||
'conditions': [
|
||||
['OS=="mac" and libchromiumcontent_component==0', {
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> Define keyboard shortcuts.
|
||||
|
||||
Accelerators are Strings that can contain multiple modifiers and key codes,
|
||||
Accelerators are Strings that can contain multiple modifiers and a single key code,
|
||||
combined by the `+` character, and are used to define keyboard shortcuts
|
||||
throughout your application.
|
||||
|
||||
|
||||
@@ -64,6 +64,9 @@ terminating the application.
|
||||
then `before-quit` is emitted *after* emitting `close` event on all windows and
|
||||
closing them.
|
||||
|
||||
**Note:** On Windows, this event will not be emitted if the app is closed due
|
||||
to a shutdown/restart of the system or a user logout.
|
||||
|
||||
### Event: 'will-quit'
|
||||
|
||||
Returns:
|
||||
@@ -77,6 +80,9 @@ terminating the application.
|
||||
See the description of the `window-all-closed` event for the differences between
|
||||
the `will-quit` and `window-all-closed` events.
|
||||
|
||||
**Note:** On Windows, this event will not be emitted if the app is closed due
|
||||
to a shutdown/restart of the system or a user logout.
|
||||
|
||||
### Event: 'quit'
|
||||
|
||||
Returns:
|
||||
@@ -86,6 +92,9 @@ Returns:
|
||||
|
||||
Emitted when the application is quitting.
|
||||
|
||||
**Note:** On Windows, this event will not be emitted if the app is closed due
|
||||
to a shutdown/restart of the system or a user logout.
|
||||
|
||||
### Event: 'open-file' _macOS_
|
||||
|
||||
Returns:
|
||||
@@ -356,6 +365,22 @@ assistive technologies, such as screen readers, are enabled or disabled.
|
||||
See https://www.chromium.org/developers/design-documents/accessibility for more
|
||||
details.
|
||||
|
||||
### Event: 'session-created'
|
||||
|
||||
Returns:
|
||||
|
||||
* `session` [Session](session.md)
|
||||
|
||||
Emitted when Electron has created a new `session`.
|
||||
|
||||
```javascript
|
||||
const {app} = require('electron')
|
||||
|
||||
app.on('session-created', (event, session) => {
|
||||
console.log(session)
|
||||
})
|
||||
```
|
||||
|
||||
## Methods
|
||||
|
||||
The `app` object has the following methods:
|
||||
@@ -522,8 +547,9 @@ Overrides the current application's name.
|
||||
|
||||
### `app.getLocale()`
|
||||
|
||||
Returns `String` - The current application locale. Possible return values are documented
|
||||
[here](locales.md).
|
||||
Returns `String` - The current application locale. Possible return values are documented [here](locales.md).
|
||||
|
||||
To set the locale, you'll want to use a command line switch at app startup, which may be found [here](https://github.com/electron/electron/blob/master/docs/api/chrome-command-line-switches.md).
|
||||
|
||||
**Note:** When distributing your packaged app, you have to also ship the
|
||||
`locales` folder.
|
||||
|
||||
@@ -202,7 +202,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
than screen. Default is `false`.
|
||||
* `backgroundColor` String (optional) - Window's background color as a hexadecimal value,
|
||||
like `#66CD00` or `#FFF` or `#80FFFFFF` (alpha is supported). Default is
|
||||
`#FFF` (white).
|
||||
`#FFF` (white). If `transparent` is set to `true`, only values with transparent (`#00-------`) or opaque (`#FF-----`) alpha values are respected.
|
||||
* `hasShadow` Boolean (optional) - Whether window should have a shadow. This is only
|
||||
implemented on macOS. Default is `true`.
|
||||
* `opacity` Number (optional) - Set the initial opacity of the window, between 0.0 (fully
|
||||
@@ -287,7 +287,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
between the web pages even when you specified different values for them,
|
||||
including but not limited to `preload`, `sandbox` and `nodeIntegration`.
|
||||
So it is suggested to use exact same `webPreferences` for web pages with
|
||||
the same `affinity`.
|
||||
the same `affinity`. _This property is experimental_
|
||||
* `zoomFactor` Number (optional) - The default zoom factor of the page, `3.0` represents
|
||||
`300%`. Default is `1.0`.
|
||||
* `javascript` Boolean (optional) - Enables JavaScript support. Default is `true`.
|
||||
@@ -361,7 +361,7 @@ It creates a new `BrowserWindow` with native properties as set by the `options`.
|
||||
script. You can use the `will-attach-webview` event on [webContents](web-contents.md)
|
||||
to strip away the `preload` script and to validate or alter the
|
||||
`<webview>`'s initial settings.
|
||||
* `additionArguments` String[] (optional) - A list of strings that will be appended
|
||||
* `additionalArguments` String[] (optional) - A list of strings that will be appended
|
||||
to `process.argv` in the renderer process of this app. Useful for passing small
|
||||
bits of data down to renderer process preload scripts.
|
||||
|
||||
@@ -870,7 +870,9 @@ Disable or enable the window.
|
||||
* `height` Integer
|
||||
* `animate` Boolean (optional) _macOS_
|
||||
|
||||
Resizes the window to `width` and `height`.
|
||||
Resizes the window to `width` and `height`. If `width` or `height`
|
||||
are below any set minimum size constraints the window will snap to
|
||||
its minimum size.
|
||||
|
||||
#### `win.getSize()`
|
||||
|
||||
@@ -1216,7 +1218,7 @@ mode set (but with a value within the valid range), `normal` will be assumed.
|
||||
|
||||
#### `win.setOverlayIcon(overlay, description)` _Windows_
|
||||
|
||||
* `overlay` [NativeImage](native-image.md) - the icon to display on the bottom
|
||||
* `overlay` [NativeImage](native-image.md) | null - the icon to display on the bottom
|
||||
right corner of the taskbar icon. If this parameter is `null`, the overlay is
|
||||
cleared
|
||||
* `description` String - a description that will be provided to Accessibility
|
||||
|
||||
@@ -95,6 +95,8 @@ When specifying a `role` on macOS, `label` and `accelerator` are the only
|
||||
options that will affect the menu item. All other options will be ignored.
|
||||
Lowercase `role`, e.g. `toggledevtools`, is still supported.
|
||||
|
||||
**Nota Bene:** The `enabled` and `visibility` properties are not available for top-level menu items in the tray on MacOS.
|
||||
|
||||
### Instance Properties
|
||||
|
||||
The following properties are available on instances of `MenuItem`:
|
||||
|
||||
@@ -67,6 +67,8 @@ that contains the user information dictionary sent along with the notification.
|
||||
* `callback` Function
|
||||
* `event` String
|
||||
* `userInfo` Object
|
||||
|
||||
Returns `Number` - The ID of this subscription
|
||||
|
||||
Subscribes to native notifications of macOS, `callback` will be called with
|
||||
`callback(event, userInfo)` when the corresponding `event` happens. The
|
||||
@@ -96,6 +98,8 @@ Removes the subscriber with `id`.
|
||||
* `callback` Function
|
||||
* `event` String
|
||||
* `userInfo` Object
|
||||
|
||||
Returns `Number` - The ID of this subscription
|
||||
|
||||
Same as `subscribeNotification`, but uses `NSNotificationCenter` for local defaults.
|
||||
This is necessary for events such as `NSUserDefaultsDidChangeNotification`.
|
||||
|
||||
@@ -194,7 +194,7 @@ Sets the `image` associated with this tray icon.
|
||||
|
||||
#### `tray.setPressedImage(image)` _macOS_
|
||||
|
||||
* `image` [NativeImage](native-image.md)
|
||||
* `image` ([NativeImage](native-image.md) | String)
|
||||
|
||||
Sets the `image` associated with this tray icon when pressed on macOS.
|
||||
|
||||
@@ -262,7 +262,7 @@ The `position` is only available on Windows, and it is (0, 0) by default.
|
||||
|
||||
#### `tray.setContextMenu(menu)`
|
||||
|
||||
* `menu` Menu
|
||||
* `menu` Menu | null
|
||||
|
||||
Sets the context menu for this icon.
|
||||
|
||||
|
||||
@@ -1016,7 +1016,7 @@ Use `page-break-before: always; ` CSS style to force to print to a new page.
|
||||
* `options` Object
|
||||
* `marginsType` Integer (optional) - Specifies the type of margins to use. Uses 0 for
|
||||
default margin, 1 for no margin, and 2 for minimum margin.
|
||||
* `pageSize` String (optional) - Specify page size of the generated PDF. Can be `A3`,
|
||||
* `pageSize` String | Size (optional) - Specify page size of the generated PDF. Can be `A3`,
|
||||
`A4`, `A5`, `Legal`, `Letter`, `Tabloid` or an Object containing `height`
|
||||
and `width` in microns.
|
||||
* `printBackground` Boolean (optional) - Whether to print CSS backgrounds.
|
||||
|
||||
@@ -173,6 +173,7 @@ Set the security origin of the isolated world.
|
||||
Returns `Object`:
|
||||
|
||||
* `images` [MemoryUsageDetails](structures/memory-usage-details.md)
|
||||
* `scripts` [MemoryUsageDetails](structures/memory-usage-details.md)
|
||||
* `cssStyleSheets` [MemoryUsageDetails](structures/memory-usage-details.md)
|
||||
* `xslStyleSheets` [MemoryUsageDetails](structures/memory-usage-details.md)
|
||||
* `fonts` [MemoryUsageDetails](structures/memory-usage-details.md)
|
||||
|
||||
@@ -550,7 +550,7 @@ Prints `webview`'s web page. Same as `webContents.print([options])`.
|
||||
* `options` Object
|
||||
* `marginsType` Integer (optional) - Specifies the type of margins to use. Uses 0 for
|
||||
default margin, 1 for no margin, and 2 for minimum margin.
|
||||
* `pageSize` String (optional) - Specify page size of the generated PDF. Can be `A3`,
|
||||
* `pageSize` String | Size (optional) - Specify page size of the generated PDF. Can be `A3`,
|
||||
`A4`, `A5`, `Legal`, `Letter`, `Tabloid` or an Object containing `height`
|
||||
and `width` in microns.
|
||||
* `printBackground` Boolean (optional) - Whether to print CSS backgrounds.
|
||||
|
||||
@@ -24,7 +24,12 @@ Returns [`BrowserWindowProxy`](browser-window-proxy.md) - Creates a new window
|
||||
and returns an instance of `BrowserWindowProxy` class.
|
||||
|
||||
The `features` string follows the format of standard browser, but each feature
|
||||
has to be a field of `BrowserWindow`'s options.
|
||||
has to be a field of `BrowserWindow`'s options. These are the features you can set via `features` string: `zoomFactor`, `nodeIntegration`, `preload`, `javascript`, `contextIsolation`, `webviewTag`.
|
||||
|
||||
For example:
|
||||
```js
|
||||
window.open('https://github.com', '_blank', 'nodeIntegration=no')
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
|
||||
|
||||
@@ -2,11 +2,31 @@
|
||||
|
||||
This document describes the process for releasing a new version of Electron.
|
||||
|
||||
## Set your tokens and environment variables
|
||||
You'll need Electron S3 credentials in order to create and
|
||||
upload an Electron release. Contact a team member for more
|
||||
information.
|
||||
|
||||
There are a handful of `*_TOKEN` environment variables needed by the release
|
||||
scripts. Once you've generated these per-user tokens, you may want to keep
|
||||
them in a local file that you can `source` when starting a release.
|
||||
* `ELECTRON_GITHUB_TOKEN`:
|
||||
Create as described at https://github.com/settings/tokens/new,
|
||||
giving the token repo access scope.
|
||||
* `APPVEYOR_TOKEN`:
|
||||
Create a token from https://windows-ci.electronjs.org/api-token
|
||||
If you don't have an account, ask a team member to add you.
|
||||
* `CIRCLE_TOKEN`:
|
||||
Create a token from "Personal API Tokens" at https://circleci.com/account/api
|
||||
* `VSTS_TOKEN`:
|
||||
Create a Personal Access Token at https://github.visualstudio.com/_usersSettings/tokens
|
||||
with the scope of `Build (read and execute)`.
|
||||
|
||||
## Determine which branch to release from
|
||||
|
||||
- **If releasing beta,** run the scripts below from `master`.
|
||||
- **If releasing a stable version,** run the scripts below from `1-7-x` or
|
||||
`1-6-x`, depending on which version you are releasing for.
|
||||
- **If releasing a stable version,** run the scripts below from the branch
|
||||
you're stabilizing.
|
||||
|
||||
## Find out what version change is needed
|
||||
Run `npm run prepare-release -- --notesOnly` to view auto generated release
|
||||
@@ -14,6 +34,11 @@ notes. The notes generated should help you determine if this is a major, minor,
|
||||
patch, or beta version change. Read the
|
||||
[Version Change Rules](../tutorial/electron-versioning.md#semver) for more information.
|
||||
|
||||
**NB:** If releasing from a branch, e.g. 1-8-x, check out the branch with
|
||||
`git checkout 1-8-x` rather than `git checkout -b remotes/origin/1-8-x`.
|
||||
The scripts need `git rev-parse --abbrev-ref HEAD` to return a short name,
|
||||
e.g. no `remotes/origin/`
|
||||
|
||||
## Run the prepare-release script
|
||||
The prepare release script will do the following:
|
||||
1. Check if a release is already in process and if so it will halt.
|
||||
@@ -55,10 +80,11 @@ npm run prepare-release -- --stable
|
||||
The `prepare-release` script will trigger the builds via API calls.
|
||||
To monitor the build progress, see the following pages:
|
||||
|
||||
- [mac-ci.electronjs.org/blue/organizations/jenkins/electron-mas-x64-release/activity](https://mac-ci.electronjs.org/blue/organizations/jenkins/electron-mas-x64-release/activity) for Mac App Store
|
||||
- [mac-ci.electronjs.org/blue/organizations/jenkins/electron-osx-x64-release/activity](https://mac-ci.electronjs.org/blue/organizations/jenkins/electron-osx-x64-release/activity) for OS X
|
||||
- [circleci.com/gh/electron/electron](https://circleci.com/gh/electron) for Linux
|
||||
- [windows-ci.electronjs.org/project/AppVeyor/electron](https://windows-ci.electronjs.org/project/AppVeyor/electron) for Windows
|
||||
- [electron-release-mas-x64](https://github.visualstudio.com/electron/_build/index?context=allDefinitions&path=%5C&definitionId=19&_a=completed) for MAS builds.
|
||||
- [electron-release-osx-x64](https://github.visualstudio.com/electron/_build/index?context=allDefinitions&path=%5C&definitionId=18&_a=completed) for OSX builds.
|
||||
- [circleci.com/gh/electron/electron](https://circleci.com/gh/electron) for Linux builds.
|
||||
- [windows-ci.electronjs.org/project/AppVeyor/electron-39ng6](https://windows-ci.electronjs.org/project/AppVeyor/electron-39ng6) for Windows 32-bit builds.
|
||||
- [windows-ci.electronjs.org/project/AppVeyor/electron](https://windows-ci.electronjs.org/project/AppVeyor/electron) for Windows 64-bit builds.
|
||||
|
||||
## Compile release notes
|
||||
|
||||
@@ -159,14 +185,15 @@ This release is published to [npm](https://www.npmjs.com/package/electron) under
|
||||
|
||||
## Edit the release draft
|
||||
|
||||
1. Visit [the releases page] and you'll see a new draft release with placeholder release notes.
|
||||
1. Edit the release and add release notes.
|
||||
1. Uncheck the `prerelease` checkbox if you're publishing a stable release; leave it checked for beta releases.
|
||||
1. Click 'Save draft'. **Do not click 'Publish release'!**
|
||||
1. Wait for all builds to pass before proceeding.
|
||||
1. You can run `npm run release -- --validateRelease` to verify that all of the
|
||||
required files have been created for the release.
|
||||
|
||||
1. Visit [the releases page] and you'll see a new draft release with placeholder
|
||||
release notes.
|
||||
2. Edit the release and add release notes.
|
||||
3. Click 'Save draft'. **Do not click 'Publish release'!**
|
||||
4. Wait for all builds to pass before proceeding.
|
||||
5. In the branch, verify that the release's files have been created:
|
||||
```sh
|
||||
$ npm run release -- --validateRelease
|
||||
```
|
||||
|
||||
## Publish the release
|
||||
|
||||
@@ -181,17 +208,68 @@ on Windows by node-gyp to build native modules.
|
||||
5. Validate that all of the required files are present on GitHub and S3 and have
|
||||
the correct checksums as specified in the SHASUMS files.
|
||||
6. Publish the release on GitHub
|
||||
7. Delete the `release` branch.
|
||||
|
||||
## Publish to npm
|
||||
|
||||
Once the publish is successful, run `npm run publish-to-npm` to publish to
|
||||
release to npm.
|
||||
Before publishing to npm, you'll need to log into npm as Electron. Optionally,
|
||||
you may find [npmrc](https://www.npmjs.com/package/npmrc) to be a useful way
|
||||
to keep Electron's profile side-by-side with your own:
|
||||
```sh
|
||||
$ sudo npm install -g npmrc
|
||||
$ npmrc -c electron
|
||||
Removing old .npmrc (default)
|
||||
Activating .npmrc "electron"
|
||||
```
|
||||
|
||||
The Electron account's credentials are kept by GitHub.
|
||||
"Electron - NPM" for the URL "https://www.npmjs.com/login".
|
||||
```sh
|
||||
$ npm login
|
||||
Username: electron
|
||||
Password:
|
||||
Email: (this IS public) electron@github.com
|
||||
```
|
||||
|
||||
Publish the release to npm.
|
||||
```sh
|
||||
$ npm whoami
|
||||
electron
|
||||
$ npm run publish-to-npm
|
||||
```
|
||||
|
||||
[the releases page]: https://github.com/electron/electron/releases
|
||||
[this bump commit]: https://github.com/electron/electron/commit/78ec1b8f89b3886b856377a1756a51617bc33f5a
|
||||
[versioning]: /docs/tutorial/electron-versioning.md
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
## Rerun broken builds
|
||||
|
||||
If a release build fails for some reason, you can use `script/ci-release-build.js` to rerun a release build:
|
||||
|
||||
### Rerun all linux builds:
|
||||
```sh
|
||||
node script/ci-release-build.js --ci=CircleCI --ghRelease TARGET_BRANCH
|
||||
(TARGET_BRANCH) is the branch you are releasing from.
|
||||
```
|
||||
|
||||
### Rerun all macOS builds:
|
||||
```sh
|
||||
node script/ci-release-build.js --ci=VSTS --ghRelease TARGET_BRANCH
|
||||
(TARGET_BRANCH) is the branch you are releasing from.
|
||||
```
|
||||
|
||||
### Rerun all Windows builds:
|
||||
```sh
|
||||
node script/ci-release-build.js --ci=AppVeyor --ghRelease TARGET_BRANCH
|
||||
(TARGET_BRANCH) is the branch you are releasing from.
|
||||
```
|
||||
|
||||
Additionally you can pass a job name to the script to run an individual job, eg:
|
||||
````sh
|
||||
node script/ci-release-build.js --ci=AppVeyor --ghRelease --job=electron-x64 TARGET_BRANCH
|
||||
```
|
||||
|
||||
## Fix missing binaries of a release manually
|
||||
|
||||
In the case of a corrupted release with broken CI machines, we might have to
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
'product_name%': 'Electron',
|
||||
'company_name%': 'GitHub, Inc',
|
||||
'company_abbr%': 'github',
|
||||
'version%': '2.0.1',
|
||||
'version%': '2.0.15',
|
||||
'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c',
|
||||
},
|
||||
'includes': [
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
'lib/renderer/inspector.js',
|
||||
'lib/renderer/override.js',
|
||||
'lib/renderer/security-warnings.js',
|
||||
'lib/renderer/web-frame-init.js',
|
||||
'lib/renderer/window-setup.js',
|
||||
'lib/renderer/web-view/guest-view-internal.js',
|
||||
'lib/renderer/web-view/web-view.js',
|
||||
@@ -273,6 +274,8 @@
|
||||
'atom/browser/net/http_protocol_handler.h',
|
||||
'atom/browser/net/js_asker.cc',
|
||||
'atom/browser/net/js_asker.h',
|
||||
'atom/browser/net/resolve_proxy_helper.cc',
|
||||
'atom/browser/net/resolve_proxy_helper.h',
|
||||
'atom/browser/net/url_request_about_job.cc',
|
||||
'atom/browser/net/url_request_about_job.h',
|
||||
'atom/browser/net/url_request_async_asar_job.cc',
|
||||
|
||||
@@ -49,6 +49,21 @@ BrowserWindow.prototype._init = function () {
|
||||
return
|
||||
}
|
||||
|
||||
if (webContents.getLastWebPreferences().nodeIntegration === true) {
|
||||
const message =
|
||||
'Enabling Node.js integration in child windows opened with the ' +
|
||||
'"nativeWindowOpen" option will cause memory leaks, please turn off ' +
|
||||
'the "nodeIntegration" option.\\n' +
|
||||
'See https://github.com/electron/electron/pull/15076 for more.'
|
||||
// console is only available after DOM is created.
|
||||
const printWarning = () => this.webContents.executeJavaScript(`console.warn('${message}')`)
|
||||
if (this.webContents.isDomReady()) {
|
||||
printWarning()
|
||||
} else {
|
||||
this.webContents.once('dom-ready', printWarning)
|
||||
}
|
||||
}
|
||||
|
||||
let {url, frameName} = urlFrameName
|
||||
v8Util.deleteHiddenValue(webContents, 'url-framename')
|
||||
const options = {
|
||||
|
||||
@@ -240,6 +240,23 @@ const attachGuest = function (event, elementInstanceId, guestInstanceId, params)
|
||||
webPreferences.disablePopups = true
|
||||
}
|
||||
|
||||
// Security options that guest will always inherit from embedder
|
||||
const inheritedWebPreferences = new Map([
|
||||
['contextIsolation', true],
|
||||
['javascript', false],
|
||||
['nativeWindowOpen', true],
|
||||
['nodeIntegration', false],
|
||||
['sandbox', true]
|
||||
])
|
||||
|
||||
// Inherit certain option values from embedder
|
||||
const lastWebPreferences = embedder.getLastWebPreferences()
|
||||
for (const [name, value] of inheritedWebPreferences) {
|
||||
if (lastWebPreferences[name] === value) {
|
||||
webPreferences[name] = value
|
||||
}
|
||||
}
|
||||
|
||||
embedder.emit('will-attach-webview', event, webPreferences, params)
|
||||
if (event.defaultPrevented) {
|
||||
if (guest.viewInstanceId == null) guest.viewInstanceId = params.instanceId
|
||||
@@ -331,6 +348,10 @@ ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', function (event, params,
|
||||
event.sender.send(`ELECTRON_RESPONSE_${requestId}`, createGuest(event.sender, params))
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', function (event, params) {
|
||||
event.returnValue = createGuest(event.sender, params)
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', function (event, elementInstanceId, guestInstanceId, params) {
|
||||
attachGuest(event, elementInstanceId, guestInstanceId, params)
|
||||
})
|
||||
|
||||
@@ -27,11 +27,11 @@ const mergeOptions = function (child, parent, visited) {
|
||||
for (const key in parent) {
|
||||
if (key === 'isBrowserView') continue
|
||||
if (!hasProp.call(parent, key)) continue
|
||||
if (key in child) continue
|
||||
if (key in child && key !== 'webPreferences') continue
|
||||
|
||||
const value = parent[key]
|
||||
if (typeof value === 'object') {
|
||||
child[key] = mergeOptions({}, value, visited)
|
||||
child[key] = mergeOptions(child[key] || {}, value, visited)
|
||||
} else {
|
||||
child[key] = value
|
||||
}
|
||||
|
||||
@@ -11,22 +11,22 @@ class ObjectsRegistry {
|
||||
this.storage = {}
|
||||
|
||||
// Stores the IDs of objects referenced by WebContents.
|
||||
// (webContentsId) => [id]
|
||||
// (webContentsContextId) => [id]
|
||||
this.owners = {}
|
||||
}
|
||||
|
||||
// Register a new object and return its assigned ID. If the object is already
|
||||
// registered then the already assigned ID would be returned.
|
||||
add (webContents, obj) {
|
||||
add (webContents, contextId, obj) {
|
||||
// Get or assign an ID to the object.
|
||||
const id = this.saveToStorage(obj)
|
||||
|
||||
// Add object to the set of referenced objects.
|
||||
const webContentsId = webContents.getId()
|
||||
let owner = this.owners[webContentsId]
|
||||
const webContentsContextId = `${webContents.id}-${contextId}`
|
||||
let owner = this.owners[webContentsContextId]
|
||||
if (!owner) {
|
||||
owner = this.owners[webContentsId] = new Set()
|
||||
this.registerDeleteListener(webContents, webContentsId)
|
||||
owner = this.owners[webContentsContextId] = new Set()
|
||||
this.registerDeleteListener(webContents, contextId)
|
||||
}
|
||||
if (!owner.has(id)) {
|
||||
owner.add(id)
|
||||
@@ -43,25 +43,28 @@ class ObjectsRegistry {
|
||||
}
|
||||
|
||||
// Dereference an object according to its ID.
|
||||
remove (webContentsId, id) {
|
||||
// Dereference from the storage.
|
||||
this.dereference(id)
|
||||
|
||||
// Also remove the reference in owner.
|
||||
let owner = this.owners[webContentsId]
|
||||
// Note that an object may be double-freed (cleared when page is reloaded, and
|
||||
// then garbage collected in old page).
|
||||
remove (webContents, contextId, id) {
|
||||
const webContentsContextId = `${webContents.id}-${contextId}`
|
||||
let owner = this.owners[webContentsContextId]
|
||||
if (owner) {
|
||||
// Remove the reference in owner.
|
||||
owner.delete(id)
|
||||
// Dereference from the storage.
|
||||
this.dereference(id)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear all references to objects refrenced by the WebContents.
|
||||
clear (webContentsId) {
|
||||
let owner = this.owners[webContentsId]
|
||||
clear (webContents, contextId) {
|
||||
const webContentsContextId = `${webContents.id}-${contextId}`
|
||||
let owner = this.owners[webContentsContextId]
|
||||
if (!owner) return
|
||||
|
||||
for (let id of owner) this.dereference(id)
|
||||
|
||||
delete this.owners[webContentsId]
|
||||
delete this.owners[webContentsContextId]
|
||||
}
|
||||
|
||||
// Private: Saves the object into storage and assigns an ID for it.
|
||||
@@ -80,6 +83,8 @@ class ObjectsRegistry {
|
||||
|
||||
// Private: Dereference the object from store.
|
||||
dereference (id) {
|
||||
if (process.env.ELECTRON_DISABLE_REMOTE_DEREFERENCING) return
|
||||
|
||||
let pointer = this.storage[id]
|
||||
if (pointer == null) {
|
||||
return
|
||||
@@ -91,13 +96,13 @@ class ObjectsRegistry {
|
||||
}
|
||||
}
|
||||
|
||||
// Private: Clear the storage when webContents is reloaded/navigated.
|
||||
registerDeleteListener (webContents, webContentsId) {
|
||||
// Private: Clear the storage when renderer process is destoryed.
|
||||
registerDeleteListener (webContents, contextId) {
|
||||
const processId = webContents.getProcessId()
|
||||
const listener = (event, deletedProcessId) => {
|
||||
if (deletedProcessId === processId) {
|
||||
webContents.removeListener('render-view-deleted', listener)
|
||||
this.clear(webContentsId)
|
||||
this.clear(webContents, contextId)
|
||||
}
|
||||
}
|
||||
webContents.on('render-view-deleted', listener)
|
||||
|
||||
@@ -55,7 +55,7 @@ let getObjectPrototype = function (object) {
|
||||
}
|
||||
|
||||
// Convert a real value into meta data.
|
||||
let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
|
||||
let valueToMeta = function (sender, contextId, value, optimizeSimpleObject = false) {
|
||||
// Determine the type of value.
|
||||
const meta = { type: typeof value }
|
||||
if (meta.type === 'object') {
|
||||
@@ -83,14 +83,14 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
|
||||
|
||||
// Fill the meta object according to value's type.
|
||||
if (meta.type === 'array') {
|
||||
meta.members = value.map((el) => valueToMeta(sender, el))
|
||||
meta.members = value.map((el) => valueToMeta(sender, contextId, el))
|
||||
} else if (meta.type === 'object' || meta.type === 'function') {
|
||||
meta.name = value.constructor ? value.constructor.name : ''
|
||||
|
||||
// Reference the original value if it's an object, because when it's
|
||||
// passed to renderer we would assume the renderer keeps a reference of
|
||||
// it.
|
||||
meta.id = objectsRegistry.add(sender, value)
|
||||
meta.id = objectsRegistry.add(sender, contextId, value)
|
||||
meta.members = getObjectMembers(value)
|
||||
meta.proto = getObjectPrototype(value)
|
||||
} else if (meta.type === 'buffer') {
|
||||
@@ -100,7 +100,7 @@ let valueToMeta = function (sender, value, optimizeSimpleObject = false) {
|
||||
// Instead they should appear in the renderer process
|
||||
value.then(function () {}, function () {})
|
||||
|
||||
meta.then = valueToMeta(sender, function (onFulfilled, onRejected) {
|
||||
meta.then = valueToMeta(sender, contextId, function (onFulfilled, onRejected) {
|
||||
value.then(onFulfilled, onRejected)
|
||||
})
|
||||
} else if (meta.type === 'error') {
|
||||
@@ -168,7 +168,7 @@ const removeRemoteListenersAndLogWarning = (meta, args, callIntoRenderer) => {
|
||||
}
|
||||
|
||||
// Convert array of meta data from renderer into array of real values.
|
||||
const unwrapArgs = function (sender, args) {
|
||||
const unwrapArgs = function (sender, contextId, args) {
|
||||
const metaToValue = function (meta) {
|
||||
let i, len, member, ref, returnValue
|
||||
switch (meta.type) {
|
||||
@@ -177,7 +177,7 @@ const unwrapArgs = function (sender, args) {
|
||||
case 'remote-object':
|
||||
return objectsRegistry.get(meta.id)
|
||||
case 'array':
|
||||
return unwrapArgs(sender, meta.value)
|
||||
return unwrapArgs(sender, contextId, meta.value)
|
||||
case 'buffer':
|
||||
return Buffer.from(meta.value)
|
||||
case 'date':
|
||||
@@ -203,26 +203,26 @@ const unwrapArgs = function (sender, args) {
|
||||
return returnValue
|
||||
}
|
||||
case 'function': {
|
||||
// Merge webContentsId and meta.id, since meta.id can be the same in
|
||||
// Merge contextId and meta.id, since meta.id can be the same in
|
||||
// different webContents.
|
||||
const webContentsId = sender.getId()
|
||||
const objectId = [webContentsId, meta.id]
|
||||
const objectId = [contextId, meta.id]
|
||||
|
||||
// Cache the callbacks in renderer.
|
||||
if (rendererFunctions.has(objectId)) {
|
||||
return rendererFunctions.get(objectId)
|
||||
}
|
||||
|
||||
const webContentsId = sender.getId()
|
||||
let callIntoRenderer = function (...args) {
|
||||
if (!sender.isDestroyed() && webContentsId === sender.getId()) {
|
||||
sender.send('ELECTRON_RENDERER_CALLBACK', meta.id, valueToMeta(sender, args))
|
||||
sender.send('ELECTRON_RENDERER_CALLBACK', contextId, meta.id, valueToMeta(sender, contextId, args))
|
||||
} else {
|
||||
removeRemoteListenersAndLogWarning(meta, args, callIntoRenderer)
|
||||
}
|
||||
}
|
||||
Object.defineProperty(callIntoRenderer, 'length', { value: meta.length })
|
||||
|
||||
v8Util.setRemoteCallbackFreer(callIntoRenderer, meta.id, sender)
|
||||
v8Util.setRemoteCallbackFreer(callIntoRenderer, contextId, meta.id, sender)
|
||||
rendererFunctions.set(objectId, callIntoRenderer)
|
||||
return callIntoRenderer
|
||||
}
|
||||
@@ -235,19 +235,19 @@ const unwrapArgs = function (sender, args) {
|
||||
|
||||
// Call a function and send reply asynchronously if it's a an asynchronous
|
||||
// style function and the caller didn't pass a callback.
|
||||
const callFunction = function (event, func, caller, args) {
|
||||
const callFunction = function (event, contextId, func, caller, args) {
|
||||
let funcMarkedAsync, funcName, funcPassedCallback, ref, ret
|
||||
funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
|
||||
funcPassedCallback = typeof args[args.length - 1] === 'function'
|
||||
try {
|
||||
if (funcMarkedAsync && !funcPassedCallback) {
|
||||
args.push(function (ret) {
|
||||
event.returnValue = valueToMeta(event.sender, ret, true)
|
||||
event.returnValue = valueToMeta(event.sender, contextId, ret, true)
|
||||
})
|
||||
func.apply(caller, args)
|
||||
} else {
|
||||
ret = func.apply(caller, args)
|
||||
event.returnValue = valueToMeta(event.sender, ret, true)
|
||||
event.returnValue = valueToMeta(event.sender, contextId, ret, true)
|
||||
}
|
||||
} catch (error) {
|
||||
// Catch functions thrown further down in function invocation and wrap
|
||||
@@ -258,45 +258,45 @@ const callFunction = function (event, func, caller, args) {
|
||||
}
|
||||
}
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, module) {
|
||||
ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, contextId, module) {
|
||||
try {
|
||||
event.returnValue = valueToMeta(event.sender, process.mainModule.require(module))
|
||||
event.returnValue = valueToMeta(event.sender, contextId, process.mainModule.require(module))
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, module) {
|
||||
ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, contextId, module) {
|
||||
try {
|
||||
event.returnValue = valueToMeta(event.sender, electron[module])
|
||||
event.returnValue = valueToMeta(event.sender, contextId, electron[module])
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_GLOBAL', function (event, name) {
|
||||
ipcMain.on('ELECTRON_BROWSER_GLOBAL', function (event, contextId, name) {
|
||||
try {
|
||||
event.returnValue = valueToMeta(event.sender, global[name])
|
||||
event.returnValue = valueToMeta(event.sender, contextId, global[name])
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_CURRENT_WINDOW', function (event) {
|
||||
ipcMain.on('ELECTRON_BROWSER_CURRENT_WINDOW', function (event, contextId) {
|
||||
try {
|
||||
event.returnValue = valueToMeta(event.sender, event.sender.getOwnerBrowserWindow())
|
||||
event.returnValue = valueToMeta(event.sender, contextId, event.sender.getOwnerBrowserWindow())
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event) {
|
||||
event.returnValue = valueToMeta(event.sender, event.sender)
|
||||
ipcMain.on('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', function (event, contextId) {
|
||||
event.returnValue = valueToMeta(event.sender, contextId, event.sender)
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, id, args) {
|
||||
ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, contextId, id, args) {
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args)
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
let constructor = objectsRegistry.get(id)
|
||||
|
||||
if (constructor == null) {
|
||||
@@ -306,30 +306,30 @@ ipcMain.on('ELECTRON_BROWSER_CONSTRUCTOR', function (event, id, args) {
|
||||
// Call new with array of arguments.
|
||||
// http://stackoverflow.com/questions/1606797/use-of-apply-with-new-operator-is-this-possible
|
||||
let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))()
|
||||
event.returnValue = valueToMeta(event.sender, obj)
|
||||
event.returnValue = valueToMeta(event.sender, contextId, obj)
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_FUNCTION_CALL', function (event, id, args) {
|
||||
ipcMain.on('ELECTRON_BROWSER_FUNCTION_CALL', function (event, contextId, id, args) {
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args)
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
let func = objectsRegistry.get(id)
|
||||
|
||||
if (func == null) {
|
||||
throwRPCError(`Cannot call function on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
callFunction(event, func, global, args)
|
||||
callFunction(event, contextId, func, global, args)
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, id, method, args) {
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, contextId, id, method, args) {
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args)
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
let object = objectsRegistry.get(id)
|
||||
|
||||
if (object == null) {
|
||||
@@ -339,30 +339,30 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_CONSTRUCTOR', function (event, id, method, a
|
||||
// Call new with array of arguments.
|
||||
let constructor = object[method]
|
||||
let obj = new (Function.prototype.bind.apply(constructor, [null].concat(args)))()
|
||||
event.returnValue = valueToMeta(event.sender, obj)
|
||||
event.returnValue = valueToMeta(event.sender, contextId, obj)
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, id, method, args) {
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_CALL', function (event, contextId, id, method, args) {
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args)
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
let obj = objectsRegistry.get(id)
|
||||
|
||||
if (obj == null) {
|
||||
throwRPCError(`Cannot call function '${method}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
callFunction(event, obj[method], obj, args)
|
||||
callFunction(event, contextId, obj[method], obj, args)
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, id, name, args) {
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, contextId, id, name, args) {
|
||||
try {
|
||||
args = unwrapArgs(event.sender, args)
|
||||
args = unwrapArgs(event.sender, contextId, args)
|
||||
let obj = objectsRegistry.get(id)
|
||||
|
||||
if (obj == null) {
|
||||
@@ -376,7 +376,7 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, id, name, args) {
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, id, name) {
|
||||
ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, contextId, id, name) {
|
||||
try {
|
||||
let obj = objectsRegistry.get(id)
|
||||
|
||||
@@ -384,25 +384,25 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_GET', function (event, id, name) {
|
||||
throwRPCError(`Cannot get property '${name}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
event.returnValue = valueToMeta(event.sender, obj[name])
|
||||
event.returnValue = valueToMeta(event.sender, contextId, obj[name])
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_DEREFERENCE', function (event, id) {
|
||||
objectsRegistry.remove(event.sender.getId(), id)
|
||||
ipcMain.on('ELECTRON_BROWSER_DEREFERENCE', function (event, contextId, id) {
|
||||
objectsRegistry.remove(event.sender, contextId, id)
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_CONTEXT_RELEASE', (e, contextId) => {
|
||||
objectsRegistry.clear(contextId)
|
||||
e.returnValue = null
|
||||
ipcMain.on('ELECTRON_BROWSER_CONTEXT_RELEASE', (event, contextId) => {
|
||||
objectsRegistry.clear(event.sender, contextId)
|
||||
event.returnValue = null
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, guestInstanceId) {
|
||||
ipcMain.on('ELECTRON_BROWSER_GUEST_WEB_CONTENTS', function (event, contextId, guestInstanceId) {
|
||||
try {
|
||||
let guestViewManager = require('./guest-view-manager')
|
||||
event.returnValue = valueToMeta(event.sender, guestViewManager.getGuest(guestInstanceId))
|
||||
event.returnValue = valueToMeta(event.sender, contextId, guestViewManager.getGuest(guestInstanceId))
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(error)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
const {Buffer} = require('buffer')
|
||||
const childProcess = require('child_process')
|
||||
const path = require('path')
|
||||
const util = require('util')
|
||||
|
||||
const hasProp = {}.hasOwnProperty
|
||||
|
||||
@@ -66,47 +67,40 @@
|
||||
let nextInode = 0
|
||||
|
||||
const uid = process.getuid != null ? process.getuid() : 0
|
||||
|
||||
const gid = process.getgid != null ? process.getgid() : 0
|
||||
|
||||
const fakeTime = new Date()
|
||||
const msec = (date) => (date || fakeTime).getTime()
|
||||
|
||||
const asarStatsToFsStats = function (stats) {
|
||||
return {
|
||||
dev: 1,
|
||||
ino: ++nextInode,
|
||||
mode: 33188,
|
||||
nlink: 1,
|
||||
uid: uid,
|
||||
gid: gid,
|
||||
rdev: 0,
|
||||
atime: stats.atime || fakeTime,
|
||||
birthtime: stats.birthtime || fakeTime,
|
||||
mtime: stats.mtime || fakeTime,
|
||||
ctime: stats.ctime || fakeTime,
|
||||
size: stats.size,
|
||||
isFile: function () {
|
||||
return stats.isFile
|
||||
},
|
||||
isDirectory: function () {
|
||||
return stats.isDirectory
|
||||
},
|
||||
isSymbolicLink: function () {
|
||||
return stats.isLink
|
||||
},
|
||||
isBlockDevice: function () {
|
||||
return false
|
||||
},
|
||||
isCharacterDevice: function () {
|
||||
return false
|
||||
},
|
||||
isFIFO: function () {
|
||||
return false
|
||||
},
|
||||
isSocket: function () {
|
||||
return false
|
||||
}
|
||||
const {Stats, constants} = require('fs')
|
||||
|
||||
let mode = constants.S_IROTH ^ constants.S_IRGRP ^ constants.S_IRUSR ^ constants.S_IWUSR
|
||||
|
||||
if (stats.isFile) {
|
||||
mode ^= constants.S_IFREG
|
||||
} else if (stats.isDirectory) {
|
||||
mode ^= constants.S_IFDIR
|
||||
} else if (stats.isLink) {
|
||||
mode ^= constants.S_IFLNK
|
||||
}
|
||||
|
||||
return new Stats(
|
||||
1, // dev
|
||||
mode, // mode
|
||||
1, // nlink
|
||||
uid,
|
||||
gid,
|
||||
0, // rdev
|
||||
undefined, // blksize
|
||||
++nextInode, // ino
|
||||
stats.size,
|
||||
undefined, // blocks,
|
||||
msec(stats.atime), // atim_msec
|
||||
msec(stats.mtime), // mtim_msec
|
||||
msec(stats.ctime), // ctim_msec
|
||||
msec(stats.birthtime) // birthtim_msec
|
||||
)
|
||||
}
|
||||
|
||||
// Create a ENOENT error.
|
||||
@@ -217,6 +211,28 @@
|
||||
arguments[arg] = newPath
|
||||
return old.apply(this, arguments)
|
||||
}
|
||||
if (old[util.promisify.custom]) {
|
||||
module[name][util.promisify.custom] = function () {
|
||||
const p = arguments[arg]
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return old[util.promisify.custom].apply(this, arguments)
|
||||
}
|
||||
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
return new Promise(() => invalidArchiveError(asarPath))
|
||||
}
|
||||
|
||||
const newPath = archive.copyFileOut(filePath)
|
||||
if (!newPath) {
|
||||
return new Promise(() => notFoundError(asarPath, filePath))
|
||||
}
|
||||
|
||||
arguments[arg] = newPath
|
||||
return old[util.promisify.custom].apply(this, arguments)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Override fs APIs.
|
||||
@@ -373,6 +389,18 @@
|
||||
})
|
||||
}
|
||||
|
||||
fs.exists[util.promisify.custom] = function (p) {
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
if (!isAsar) {
|
||||
return exists[util.promisify.custom](p)
|
||||
}
|
||||
const archive = getOrCreateArchive(asarPath)
|
||||
if (!archive) {
|
||||
return new Promise(() => invalidArchiveError(asarPath))
|
||||
}
|
||||
return Promise.resolve(archive.stat(filePath) !== false)
|
||||
}
|
||||
|
||||
const {existsSync} = fs
|
||||
fs.existsSync = function (p) {
|
||||
const [isAsar, asarPath, filePath] = splitPath(p)
|
||||
@@ -680,18 +708,28 @@
|
||||
// called by `childProcess.{exec,execSync}`, causing
|
||||
// Electron to consider the full command as a single path
|
||||
// to an archive.
|
||||
['exec', 'execSync'].forEach(function (functionName) {
|
||||
const old = childProcess[functionName]
|
||||
childProcess[functionName] = function () {
|
||||
const {exec, execSync} = childProcess
|
||||
childProcess.exec = invokeWithNoAsar(exec)
|
||||
childProcess.exec[util.promisify.custom] = invokeWithNoAsar(exec[util.promisify.custom])
|
||||
childProcess.execSync = invokeWithNoAsar(execSync)
|
||||
|
||||
function invokeWithNoAsar (func) {
|
||||
return function () {
|
||||
const processNoAsarOriginalValue = process.noAsar
|
||||
process.noAsar = true
|
||||
try {
|
||||
return old.apply(this, arguments)
|
||||
return func.apply(this, arguments)
|
||||
} finally {
|
||||
process.noAsar = processNoAsarOriginalValue
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Strictly implementing the flags of fs.copyFile is hard, just do a simple
|
||||
// implementation for now. Doing 2 copies won't spend much time more as OS
|
||||
// has filesystem caching.
|
||||
overrideAPI(fs, 'copyFile')
|
||||
overrideAPISync(fs, 'copyFileSync')
|
||||
|
||||
overrideAPI(fs, 'open')
|
||||
overrideAPI(childProcess, 'execFile')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
const timers = require('timers')
|
||||
const util = require('util')
|
||||
|
||||
process.atomBinding = require('./atom-binding-setup')(process.binding, process.type)
|
||||
|
||||
@@ -8,11 +9,21 @@ process.atomBinding = require('./atom-binding-setup')(process.binding, process.t
|
||||
// which would delay the callbacks for arbitrary long time. So we should
|
||||
// initiatively activate the uv loop once setImmediate and process.nextTick is
|
||||
// called.
|
||||
var wrapWithActivateUvLoop = function (func) {
|
||||
return function () {
|
||||
process.activateUvLoop()
|
||||
return func.apply(this, arguments)
|
||||
const wrapWithActivateUvLoop = function (func) {
|
||||
return wrap(func, function (func) {
|
||||
return function () {
|
||||
process.activateUvLoop()
|
||||
return func.apply(this, arguments)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function wrap (func, wrapper) {
|
||||
const wrapped = wrapper(func)
|
||||
if (func[util.promisify.custom]) {
|
||||
wrapped[util.promisify.custom] = wrapper(func[util.promisify.custom])
|
||||
}
|
||||
return wrapped
|
||||
}
|
||||
|
||||
process.nextTick = wrapWithActivateUvLoop(process.nextTick)
|
||||
|
||||
@@ -11,6 +11,18 @@ const resolvePromise = Promise.resolve.bind(Promise)
|
||||
const callbacksRegistry = new CallbacksRegistry()
|
||||
const remoteObjectCache = v8Util.createIDWeakMap()
|
||||
|
||||
// An unique ID that can represent current context.
|
||||
const contextId = v8Util.getHiddenValue(global, 'contextId')
|
||||
|
||||
// Notify the main process when current context is going to be released.
|
||||
// Note that when the renderer process is destroyed, the message may not be
|
||||
// sent, we also listen to the "render-view-deleted" event in the main process
|
||||
// to guard that situation.
|
||||
process.on('exit', () => {
|
||||
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
|
||||
ipcRenderer.sendSync(command, contextId)
|
||||
})
|
||||
|
||||
// Convert the arguments object into an array of meta data.
|
||||
function wrapArgs (args, visited = new Set()) {
|
||||
const valueToMeta = (value) => {
|
||||
@@ -109,7 +121,7 @@ function setObjectMembers (ref, object, metaId, members) {
|
||||
} else {
|
||||
command = 'ELECTRON_BROWSER_MEMBER_CALL'
|
||||
}
|
||||
const ret = ipcRenderer.sendSync(command, metaId, member.name, wrapArgs(args))
|
||||
const ret = ipcRenderer.sendSync(command, contextId, metaId, member.name, wrapArgs(args))
|
||||
return metaToValue(ret)
|
||||
}
|
||||
|
||||
@@ -128,7 +140,7 @@ function setObjectMembers (ref, object, metaId, members) {
|
||||
} else if (member.type === 'get') {
|
||||
descriptor.get = () => {
|
||||
const command = 'ELECTRON_BROWSER_MEMBER_GET'
|
||||
const meta = ipcRenderer.sendSync(command, metaId, member.name)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, metaId, member.name)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
@@ -136,7 +148,7 @@ function setObjectMembers (ref, object, metaId, members) {
|
||||
descriptor.set = (value) => {
|
||||
const args = wrapArgs([value])
|
||||
const command = 'ELECTRON_BROWSER_MEMBER_SET'
|
||||
const meta = ipcRenderer.sendSync(command, metaId, member.name, args)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, metaId, member.name, args)
|
||||
if (meta != null) metaToValue(meta)
|
||||
return value
|
||||
}
|
||||
@@ -166,7 +178,7 @@ function proxyFunctionProperties (remoteMemberFunction, metaId, name) {
|
||||
if (loaded) return
|
||||
loaded = true
|
||||
const command = 'ELECTRON_BROWSER_MEMBER_GET'
|
||||
const meta = ipcRenderer.sendSync(command, metaId, name)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, metaId, name)
|
||||
setObjectMembers(remoteMemberFunction, remoteMemberFunction, meta.id, meta.members)
|
||||
}
|
||||
|
||||
@@ -226,7 +238,7 @@ function metaToValue (meta) {
|
||||
} else {
|
||||
command = 'ELECTRON_BROWSER_FUNCTION_CALL'
|
||||
}
|
||||
const obj = ipcRenderer.sendSync(command, meta.id, wrapArgs(args))
|
||||
const obj = ipcRenderer.sendSync(command, contextId, meta.id, wrapArgs(args))
|
||||
return metaToValue(obj)
|
||||
}
|
||||
ret = remoteFunction
|
||||
@@ -239,7 +251,7 @@ function metaToValue (meta) {
|
||||
Object.defineProperty(ret.constructor, 'name', { value: meta.name })
|
||||
|
||||
// Track delegate obj's lifetime & tell browser to clean up when object is GCed.
|
||||
v8Util.setRemoteObjectFreer(ret, meta.id)
|
||||
v8Util.setRemoteObjectFreer(ret, contextId, meta.id)
|
||||
v8Util.setHiddenValue(ret, 'atomId', meta.id)
|
||||
remoteObjectCache.set(meta.id, ret)
|
||||
return ret
|
||||
@@ -257,57 +269,51 @@ function metaToPlainObject (meta) {
|
||||
}
|
||||
|
||||
// Browser calls a callback in renderer.
|
||||
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', (event, id, args) => {
|
||||
ipcRenderer.on('ELECTRON_RENDERER_CALLBACK', (event, passedContextId, id, args) => {
|
||||
if (passedContextId !== contextId) {
|
||||
// The invoked callback belongs to an old page in this renderer.
|
||||
return
|
||||
}
|
||||
callbacksRegistry.apply(id, metaToValue(args))
|
||||
})
|
||||
|
||||
// A callback in browser is released.
|
||||
ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', (event, id) => {
|
||||
ipcRenderer.on('ELECTRON_RENDERER_RELEASE_CALLBACK', (event, passedContextId, id) => {
|
||||
if (passedContextId !== contextId) {
|
||||
// The freed callback belongs to an old page in this renderer.
|
||||
return
|
||||
}
|
||||
callbacksRegistry.remove(id)
|
||||
})
|
||||
|
||||
process.on('exit', () => {
|
||||
const command = 'ELECTRON_BROWSER_CONTEXT_RELEASE'
|
||||
ipcRenderer.sendSync(command, initialContext)
|
||||
})
|
||||
|
||||
exports.require = (module) => {
|
||||
const command = 'ELECTRON_BROWSER_REQUIRE'
|
||||
const meta = ipcRenderer.sendSync(command, module)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, module)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
// Alias to remote.require('electron').xxx.
|
||||
exports.getBuiltin = (module) => {
|
||||
const command = 'ELECTRON_BROWSER_GET_BUILTIN'
|
||||
const meta = ipcRenderer.sendSync(command, module)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, module)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
exports.getCurrentWindow = () => {
|
||||
const command = 'ELECTRON_BROWSER_CURRENT_WINDOW'
|
||||
const meta = ipcRenderer.sendSync(command)
|
||||
const meta = ipcRenderer.sendSync(command, contextId)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
// Get current WebContents object.
|
||||
exports.getCurrentWebContents = () => {
|
||||
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS'))
|
||||
}
|
||||
|
||||
const CONTEXT_ARG = '--context-id='
|
||||
let initialContext = process.argv.find(arg => arg.startsWith(CONTEXT_ARG))
|
||||
if (initialContext) {
|
||||
initialContext = parseInt(initialContext.substr(CONTEXT_ARG.length), 10)
|
||||
} else {
|
||||
// In sandbox we need to pull this from remote
|
||||
initialContext = exports.getCurrentWebContents().getId()
|
||||
return metaToValue(ipcRenderer.sendSync('ELECTRON_BROWSER_CURRENT_WEB_CONTENTS', contextId))
|
||||
}
|
||||
|
||||
// Get a global object in browser.
|
||||
exports.getGlobal = (name) => {
|
||||
const command = 'ELECTRON_BROWSER_GLOBAL'
|
||||
const meta = ipcRenderer.sendSync(command, name)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, name)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
@@ -324,7 +330,7 @@ exports.createFunctionWithReturnValue = (returnValue) => {
|
||||
// Get the guest WebContents from guestInstanceId.
|
||||
exports.getGuestWebContents = (guestInstanceId) => {
|
||||
const command = 'ELECTRON_BROWSER_GUEST_WEB_CONTENTS'
|
||||
const meta = ipcRenderer.sendSync(command, guestInstanceId)
|
||||
const meta = ipcRenderer.sendSync(command, contextId, guestInstanceId)
|
||||
return metaToValue(meta)
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
const events = require('events')
|
||||
const path = require('path')
|
||||
const Module = require('module')
|
||||
const resolvePromise = Promise.resolve.bind(Promise)
|
||||
|
||||
// We modified the original process.argv to let node.js load the
|
||||
// init.js, we need to restore it here.
|
||||
@@ -26,7 +25,6 @@ var v8Util = process.atomBinding('v8_util')
|
||||
v8Util.setHiddenValue(global, 'ipc', new events.EventEmitter())
|
||||
|
||||
// Use electron module after everything is ready.
|
||||
const electron = require('electron')
|
||||
|
||||
const {
|
||||
warnAboutNodeWithRemoteContent,
|
||||
@@ -40,40 +38,7 @@ const {
|
||||
shouldLogSecurityWarnings
|
||||
} = require('./security-warnings')
|
||||
|
||||
// Call webFrame method.
|
||||
electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => {
|
||||
electron.webFrame[method](...args)
|
||||
})
|
||||
|
||||
electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
|
||||
const result = electron.webFrame[method](...args)
|
||||
event.sender.send(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, result)
|
||||
})
|
||||
|
||||
electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
|
||||
const responseCallback = function (result) {
|
||||
resolvePromise(result)
|
||||
.then((resolvedResult) => {
|
||||
event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, null, resolvedResult)
|
||||
})
|
||||
.catch((resolvedError) => {
|
||||
if (resolvedError instanceof Error) {
|
||||
// Errors get lost, because: JSON.stringify(new Error('Message')) === {}
|
||||
// Take the serializable properties and construct a generic object
|
||||
resolvedError = {
|
||||
message: resolvedError.message,
|
||||
stack: resolvedError.stack,
|
||||
name: resolvedError.name,
|
||||
__ELECTRON_SERIALIZED_ERROR__: true
|
||||
}
|
||||
}
|
||||
|
||||
event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, resolvedError)
|
||||
})
|
||||
}
|
||||
args.push(responseCallback)
|
||||
electron.webFrame[method](...args)
|
||||
})
|
||||
require('./web-frame-init')()
|
||||
|
||||
// Process command line arguments.
|
||||
let nodeIntegration = 'false'
|
||||
|
||||
@@ -3,8 +3,7 @@ window.onload = function () {
|
||||
window.InspectorFrontendHost.showContextMenuAtPoint = createMenu
|
||||
|
||||
// Use dialog API to override file chooser dialog.
|
||||
// Note: It will be moved to UI after Chrome 57.
|
||||
window.Bindings.createFileSelectorElement = createFileSelectorElement
|
||||
window.UI.createFileSelectorElement = createFileSelectorElement
|
||||
}
|
||||
|
||||
window.confirm = function (message, title) {
|
||||
|
||||
38
lib/renderer/web-frame-init.js
Normal file
38
lib/renderer/web-frame-init.js
Normal file
@@ -0,0 +1,38 @@
|
||||
const electron = require('electron')
|
||||
|
||||
module.exports = () => {
|
||||
// Call webFrame method
|
||||
electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_WEB_FRAME_METHOD', (event, method, args) => {
|
||||
electron.webFrame[method](...args)
|
||||
})
|
||||
|
||||
electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_SYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
|
||||
const result = electron.webFrame[method](...args)
|
||||
event.sender.send(`ELECTRON_INTERNAL_BROWSER_SYNC_WEB_FRAME_RESPONSE_${requestId}`, result)
|
||||
})
|
||||
|
||||
electron.ipcRenderer.on('ELECTRON_INTERNAL_RENDERER_ASYNC_WEB_FRAME_METHOD', (event, requestId, method, args) => {
|
||||
const responseCallback = function (result) {
|
||||
Promise.resolve(result)
|
||||
.then((resolvedResult) => {
|
||||
event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, null, resolvedResult)
|
||||
})
|
||||
.catch((resolvedError) => {
|
||||
if (resolvedError instanceof Error) {
|
||||
// Errors get lost, because: JSON.stringify(new Error('Message')) === {}
|
||||
// Take the serializable properties and construct a generic object
|
||||
resolvedError = {
|
||||
message: resolvedError.message,
|
||||
stack: resolvedError.stack,
|
||||
name: resolvedError.name,
|
||||
__ELECTRON_SERIALIZED_ERROR__: true
|
||||
}
|
||||
}
|
||||
|
||||
event.sender.send(`ELECTRON_INTERNAL_BROWSER_ASYNC_WEB_FRAME_RESPONSE_${requestId}`, resolvedError)
|
||||
})
|
||||
}
|
||||
args.push(responseCallback)
|
||||
electron.webFrame[method](...args)
|
||||
})
|
||||
}
|
||||
@@ -100,6 +100,9 @@ module.exports = {
|
||||
ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST', params, requestId)
|
||||
ipcRenderer.once(`ELECTRON_RESPONSE_${requestId}`, callback)
|
||||
},
|
||||
createGuestSync: function (params) {
|
||||
return ipcRenderer.sendSync('ELECTRON_GUEST_VIEW_MANAGER_CREATE_GUEST_SYNC', params)
|
||||
},
|
||||
attachGuest: function (elementInstanceId, guestInstanceId, params) {
|
||||
ipcRenderer.send('ELECTRON_GUEST_VIEW_MANAGER_ATTACH_GUEST', elementInstanceId, guestInstanceId, params)
|
||||
webFrame.attachGuest(elementInstanceId)
|
||||
|
||||
@@ -230,7 +230,7 @@ class SrcAttribute extends WebViewAttribute {
|
||||
}
|
||||
|
||||
parse () {
|
||||
if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId) {
|
||||
if (!this.webViewImpl.elementAttached || !this.webViewImpl.attributes[webViewConstants.ATTRIBUTE_PARTITION].validPartitionId || !this.getValue()) {
|
||||
return
|
||||
}
|
||||
if (this.webViewImpl.guestInstanceId == null) {
|
||||
@@ -240,9 +240,6 @@ class SrcAttribute extends WebViewAttribute {
|
||||
}
|
||||
return
|
||||
}
|
||||
if (!this.getValue()) {
|
||||
return
|
||||
}
|
||||
|
||||
// Navigate to |this.src|.
|
||||
const opts = {}
|
||||
|
||||
@@ -145,9 +145,7 @@ class WebViewImpl {
|
||||
|
||||
onElementResize (newSize) {
|
||||
// Dispatch the 'resize' event.
|
||||
const resizeEvent = new Event('resize', {
|
||||
bubbles: true
|
||||
})
|
||||
const resizeEvent = new Event('resize')
|
||||
|
||||
// Using client size values, because when a webview is transformed `newSize`
|
||||
// is incorrect
|
||||
@@ -171,6 +169,10 @@ class WebViewImpl {
|
||||
})
|
||||
}
|
||||
|
||||
createGuestSync () {
|
||||
this.attachGuestInstance(guestViewInternal.createGuestSync(this.buildParams()))
|
||||
}
|
||||
|
||||
dispatchEvent (webViewEvent) {
|
||||
this.webviewNode.dispatchEvent(webViewEvent)
|
||||
}
|
||||
@@ -418,7 +420,11 @@ const registerWebViewElement = function () {
|
||||
|
||||
// WebContents associated with this webview.
|
||||
proto.getWebContents = function () {
|
||||
return v8Util.getHiddenValue(this, 'internal').webContents
|
||||
const internal = v8Util.getHiddenValue(this, 'internal')
|
||||
if (!internal.webContents) {
|
||||
internal.createGuestSync()
|
||||
}
|
||||
return internal.webContents
|
||||
}
|
||||
|
||||
window.WebView = webFrame.registerEmbedderCustomElement('webview', {
|
||||
|
||||
@@ -32,6 +32,8 @@ const preloadModules = new Map([
|
||||
['timers', require('timers')]
|
||||
])
|
||||
|
||||
require('../renderer/web-frame-init')()
|
||||
|
||||
// Pass different process object to the preload script(which should not have
|
||||
// access to things like `process.atomBinding`).
|
||||
const preloadProcess = new events.EventEmitter()
|
||||
@@ -57,6 +59,16 @@ if (window.location.protocol === 'chrome-devtools:') {
|
||||
require('../renderer/inspector')
|
||||
}
|
||||
|
||||
if (binding.guestInstanceId) {
|
||||
process.guestInstanceId = parseInt(binding.guestInstanceId)
|
||||
}
|
||||
|
||||
if (!process.guestInstanceId && preloadProcess.argv.indexOf('--webview-tag=true') !== -1) {
|
||||
// don't allow recursive `<webview>`
|
||||
require('../renderer/web-view/web-view')
|
||||
require('../renderer/web-view/web-view-attributes')
|
||||
}
|
||||
|
||||
// Wrap the script into a function executed in global scope. It won't have
|
||||
// access to the current scope, so we'll expose a few objects as arguments:
|
||||
//
|
||||
|
||||
13
npm/cli.js
13
npm/cli.js
@@ -4,7 +4,18 @@ var electron = require('./')
|
||||
|
||||
var proc = require('child_process')
|
||||
|
||||
var child = proc.spawn(electron, process.argv.slice(2), {stdio: 'inherit'})
|
||||
var child = proc.spawn(electron, process.argv.slice(2), {stdio: 'inherit', windowsHide: false})
|
||||
child.on('close', function (code) {
|
||||
process.exit(code)
|
||||
})
|
||||
|
||||
const handleTerminationSignal = function (signal) {
|
||||
process.on(signal, function signalHandler () {
|
||||
if (!child.killed) {
|
||||
child.kill(signal)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
handleTerminationSignal('SIGINT')
|
||||
handleTerminationSignal('SIGTERM')
|
||||
|
||||
@@ -21,6 +21,12 @@ if (installedVersion === version && fs.existsSync(path.join(__dirname, platformP
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
var mirror
|
||||
|
||||
if (version.indexOf('nightly') !== -1) {
|
||||
mirror = 'https://github.com/electron/nightlies/releases/download/v'
|
||||
}
|
||||
|
||||
// downloads if not cached
|
||||
download({
|
||||
cache: process.env.electron_config_cache,
|
||||
@@ -29,7 +35,8 @@ download({
|
||||
arch: process.env.npm_config_arch,
|
||||
strictSSL: process.env.npm_config_strict_ssl === 'true',
|
||||
force: process.env.force_no_cache === 'true',
|
||||
quiet: process.env.npm_config_loglevel === 'silent' || process.env.CI
|
||||
quiet: process.env.npm_config_loglevel === 'silent' || process.env.CI,
|
||||
mirror
|
||||
}, extractFile)
|
||||
|
||||
// unzips and makes path.txt point at the correct executable
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "electron",
|
||||
"version": "2.0.1",
|
||||
"version": "2.0.15",
|
||||
"repository": "https://github.com/electron/electron",
|
||||
"description": "Build cross platform desktop apps with JavaScript, HTML, and CSS",
|
||||
"devDependencies": {
|
||||
|
||||
@@ -5,12 +5,12 @@ import re
|
||||
import sys
|
||||
import argparse
|
||||
|
||||
from lib.util import execute, get_electron_version, parse_version, scoped_cwd
|
||||
|
||||
from lib.util import execute, get_electron_version, parse_version, scoped_cwd, \
|
||||
is_nightly, is_beta, is_stable, get_next_nightly, get_next_beta, \
|
||||
get_next_stable_from_pre, get_next_stable_from_stable, clean_parse_version
|
||||
|
||||
SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
@@ -34,14 +34,7 @@ def main():
|
||||
action='store',
|
||||
default=None,
|
||||
dest='bump',
|
||||
help='increment [major | minor | patch | beta]'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--stable',
|
||||
action='store_true',
|
||||
default= False,
|
||||
dest='stable',
|
||||
help='promote to stable (i.e. remove `-beta.x` suffix)'
|
||||
help='increment [stable | beta | nightly]'
|
||||
)
|
||||
parser.add_argument(
|
||||
'--dry-run',
|
||||
@@ -52,36 +45,56 @@ def main():
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
curr_version = get_electron_version()
|
||||
|
||||
if args.bump not in ['stable', 'beta', 'nightly']:
|
||||
raise Exception('bump must be set to either stable, beta or nightly')
|
||||
|
||||
if is_nightly(curr_version):
|
||||
if args.bump == 'nightly':
|
||||
version = get_next_nightly(curr_version)
|
||||
elif args.bump == 'beta':
|
||||
version = get_next_beta(curr_version)
|
||||
elif args.bump == 'stable':
|
||||
version = get_next_stable_from_pre(curr_version)
|
||||
else:
|
||||
not_reached()
|
||||
elif is_beta(curr_version):
|
||||
if args.bump == 'nightly':
|
||||
version = get_next_nightly(curr_version)
|
||||
elif args.bump == 'beta':
|
||||
version = get_next_beta(curr_version)
|
||||
elif args.bump == 'stable':
|
||||
version = get_next_stable_from_pre(curr_version)
|
||||
else:
|
||||
not_reached()
|
||||
elif is_stable(curr_version):
|
||||
if args.bump == 'nightly':
|
||||
version = get_next_nightly(curr_version)
|
||||
elif args.bump == 'beta':
|
||||
raise Exception("You can\'t bump to a beta from stable")
|
||||
elif args.bump == 'stable':
|
||||
version = get_next_stable_from_stable(curr_version)
|
||||
else:
|
||||
not_reached()
|
||||
else:
|
||||
raise Exception("Invalid current version: " + curr_version)
|
||||
|
||||
if args.new_version == None and args.bump == None and args.stable == False:
|
||||
parser.print_help()
|
||||
return 1
|
||||
|
||||
increments = ['major', 'minor', 'patch', 'beta']
|
||||
|
||||
curr_version = get_electron_version()
|
||||
versions = parse_version(re.sub('-beta', '', curr_version))
|
||||
|
||||
if args.bump in increments:
|
||||
versions = increase_version(versions, increments.index(args.bump))
|
||||
if versions[3] == '0':
|
||||
# beta starts at 1
|
||||
versions = increase_version(versions, increments.index('beta'))
|
||||
|
||||
if args.stable == True:
|
||||
versions[3] = '0'
|
||||
|
||||
if args.new_version != None:
|
||||
versions = parse_version(re.sub('-beta', '', args.new_version))
|
||||
|
||||
version = '.'.join(versions[:3])
|
||||
suffix = '' if versions[3] == '0' else '-beta.' + versions[3]
|
||||
versions = clean_parse_version(version)
|
||||
suffix = ''
|
||||
if '-' in version:
|
||||
suffix = '-' + version.split('-')[1]
|
||||
versions[3] = parse_version(version)[3]
|
||||
version = version.split('-')[0]
|
||||
|
||||
if args.dry_run:
|
||||
print 'new version number would be: {0}\n'.format(version + suffix)
|
||||
return 0
|
||||
|
||||
|
||||
with scoped_cwd(SOURCE_ROOT):
|
||||
update_electron_gyp(version, suffix)
|
||||
update_win_rc(version, versions)
|
||||
@@ -92,6 +105,9 @@ def main():
|
||||
|
||||
print 'Bumped to version: {0}'.format(version + suffix)
|
||||
|
||||
def not_reached():
|
||||
raise Exception('Unreachable code was reached')
|
||||
|
||||
def increase_version(versions, index):
|
||||
for i in range(index + 1, 4):
|
||||
versions[i] = '0'
|
||||
@@ -100,7 +116,8 @@ def increase_version(versions, index):
|
||||
|
||||
|
||||
def update_electron_gyp(version, suffix):
|
||||
pattern = re.compile(" *'version%' *: *'[0-9.]+(-beta[0-9.]*)?'")
|
||||
pattern = re.compile(" *'version%' *: *'[0-9.]+(-beta[0-9.]*)?(-dev)?"
|
||||
+ "(-nightly[0-9.]*)?'")
|
||||
with open('electron.gyp', 'r') as f:
|
||||
lines = f.readlines()
|
||||
|
||||
@@ -192,7 +209,14 @@ def update_package_json(version, suffix):
|
||||
|
||||
|
||||
def tag_version(version, suffix):
|
||||
execute(['git', 'commit', '-a', '-m', 'Bump v{0}'.format(version + suffix)])
|
||||
execute([
|
||||
'git',
|
||||
'commit',
|
||||
'-a',
|
||||
'-m',
|
||||
'Bump v{0}'.format(version + suffix),
|
||||
'-n'
|
||||
])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -1,6 +1,14 @@
|
||||
require('dotenv-safe').load()
|
||||
|
||||
const assert = require('assert')
|
||||
const request = require('request')
|
||||
const buildAppVeyorURL = 'https://windows-ci.electronjs.org/api/builds'
|
||||
const vstsURL = 'https://github.visualstudio.com/electron/_apis/build'
|
||||
|
||||
const appVeyorJobs = {
|
||||
'electron-x64': 'electron',
|
||||
'electron-ia32': 'electron-39ng6'
|
||||
}
|
||||
|
||||
const circleCIJobs = [
|
||||
'electron-linux-arm',
|
||||
@@ -10,6 +18,11 @@ const circleCIJobs = [
|
||||
'electron-linux-x64'
|
||||
]
|
||||
|
||||
const vstsJobs = [
|
||||
'electron-release-mas-x64',
|
||||
'electron-release-osx-x64'
|
||||
]
|
||||
|
||||
async function makeRequest (requestOptions, parseResponse) {
|
||||
return new Promise((resolve, reject) => {
|
||||
request(requestOptions, (err, res, body) => {
|
||||
@@ -21,8 +34,13 @@ async function makeRequest (requestOptions, parseResponse) {
|
||||
resolve(body)
|
||||
}
|
||||
} else {
|
||||
console.error('Error occurred while requesting:', requestOptions.url)
|
||||
if (parseResponse) {
|
||||
console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), requestOptions)
|
||||
try {
|
||||
console.log('Error: ', `(status ${res.statusCode})`, err || JSON.parse(res.body), requestOptions)
|
||||
} catch (err) {
|
||||
console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions)
|
||||
}
|
||||
} else {
|
||||
console.log('Error: ', `(status ${res.statusCode})`, err || res.body, requestOptions)
|
||||
}
|
||||
@@ -33,7 +51,6 @@ async function makeRequest (requestOptions, parseResponse) {
|
||||
}
|
||||
|
||||
async function circleCIcall (buildUrl, targetBranch, job, options) {
|
||||
assert(process.env.CIRCLE_TOKEN, 'CIRCLE_TOKEN not found in environment')
|
||||
console.log(`Triggering CircleCI to run build job: ${job} on branch: ${targetBranch} with release flag.`)
|
||||
let buildRequest = {
|
||||
'build_parameters': {
|
||||
@@ -62,12 +79,21 @@ async function circleCIcall (buildUrl, targetBranch, job, options) {
|
||||
}, true).catch(err => {
|
||||
console.log('Error calling CircleCI:', err)
|
||||
})
|
||||
console.log(`Check ${circleResponse.build_url} for status. (${job})`)
|
||||
console.log(`CircleCI release build request for ${job} successful. Check ${circleResponse.build_url} for status.`)
|
||||
}
|
||||
|
||||
async function buildAppVeyor (targetBranch, options) {
|
||||
console.log(`Triggering AppVeyor to run build on branch: ${targetBranch} with release flag.`)
|
||||
assert(process.env.APPVEYOR_TOKEN, 'APPVEYOR_TOKEN not found in environment')
|
||||
function buildAppVeyor (targetBranch, options) {
|
||||
const validJobs = Object.keys(appVeyorJobs)
|
||||
if (options.job) {
|
||||
assert(validJobs.includes(options.job), `Unknown AppVeyor CI job name: ${options.job}. Valid values are: ${validJobs}.`)
|
||||
callAppVeyor(targetBranch, options.job, options)
|
||||
} else {
|
||||
validJobs.forEach((job) => callAppVeyor(targetBranch, job, options))
|
||||
}
|
||||
}
|
||||
|
||||
async function callAppVeyor (targetBranch, job, options) {
|
||||
console.log(`Triggering AppVeyor to run build job: ${job} on branch: ${targetBranch} with release flag.`)
|
||||
let environmentVariables = {}
|
||||
|
||||
if (options.ghRelease) {
|
||||
@@ -90,7 +116,7 @@ async function buildAppVeyor (targetBranch, options) {
|
||||
},
|
||||
body: JSON.stringify({
|
||||
accountName: 'AppVeyor',
|
||||
projectSlug: 'electron',
|
||||
projectSlug: appVeyorJobs[job],
|
||||
branch: targetBranch,
|
||||
environmentVariables
|
||||
}),
|
||||
@@ -99,20 +125,84 @@ async function buildAppVeyor (targetBranch, options) {
|
||||
let appVeyorResponse = await makeRequest(requestOpts, true).catch(err => {
|
||||
console.log('Error calling AppVeyor:', err)
|
||||
})
|
||||
const buildUrl = `https://windows-ci.electronjs.org/project/AppVeyor/electron/build/${appVeyorResponse.version}`
|
||||
console.log(`AppVeyor release build request successful. Check build status at ${buildUrl}`)
|
||||
const buildUrl = `https://windows-ci.electronjs.org/project/AppVeyor/${appVeyorJobs[job]}/build/${appVeyorResponse.version}`
|
||||
console.log(`AppVeyor release build request for ${job} successful. Check build status at ${buildUrl}`)
|
||||
}
|
||||
|
||||
function buildCircleCI (targetBranch, options) {
|
||||
const circleBuildUrl = `https://circleci.com/api/v1.1/project/github/electron/electron/tree/${targetBranch}?circle-token=${process.env.CIRCLE_TOKEN}`
|
||||
if (options.job) {
|
||||
assert(circleCIJobs.includes(options.job), `Unknown CI job name: ${options.job}.`)
|
||||
assert(circleCIJobs.includes(options.job), `Unknown CircleCI job name: ${options.job}. Valid values are: ${circleCIJobs}.`)
|
||||
circleCIcall(circleBuildUrl, targetBranch, options.job, options)
|
||||
} else {
|
||||
circleCIJobs.forEach((job) => circleCIcall(circleBuildUrl, targetBranch, job, options))
|
||||
}
|
||||
}
|
||||
|
||||
async function buildVSTS (targetBranch, options) {
|
||||
if (options.job) {
|
||||
assert(vstsJobs.includes(options.job), `Unknown VSTS CI job name: ${options.job}. Valid values are: ${vstsJobs}.`)
|
||||
}
|
||||
console.log(`Triggering VSTS to run build on branch: ${targetBranch} with release flag.`)
|
||||
let environmentVariables = {}
|
||||
|
||||
if (!options.ghRelease) {
|
||||
environmentVariables.UPLOAD_TO_S3 = 1
|
||||
}
|
||||
|
||||
if (options.automaticRelease) {
|
||||
environmentVariables.AUTO_RELEASE = 'true'
|
||||
}
|
||||
|
||||
let requestOpts = {
|
||||
url: `${vstsURL}/definitions?api-version=4.1`,
|
||||
auth: {
|
||||
user: '',
|
||||
password: process.env.VSTS_TOKEN
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
}
|
||||
let vstsResponse = await makeRequest(requestOpts, true).catch(err => {
|
||||
console.log('Error calling VSTS to get build definitions:', err)
|
||||
})
|
||||
let buildsToRun = []
|
||||
if (options.job) {
|
||||
buildsToRun = vstsResponse.value.filter(build => build.name === options.job)
|
||||
} else {
|
||||
buildsToRun = vstsResponse.value.filter(build => vstsJobs.includes(build.name))
|
||||
}
|
||||
buildsToRun.forEach((build) => callVSTSBuild(build, targetBranch, environmentVariables))
|
||||
}
|
||||
|
||||
async function callVSTSBuild (build, targetBranch, environmentVariables) {
|
||||
let buildBody = {
|
||||
definition: build,
|
||||
sourceBranch: targetBranch,
|
||||
priority: 'high'
|
||||
}
|
||||
if (Object.keys(environmentVariables).length !== 0) {
|
||||
buildBody.parameters = JSON.stringify(environmentVariables)
|
||||
}
|
||||
let requestOpts = {
|
||||
url: `${vstsURL}/builds?api-version=4.1`,
|
||||
auth: {
|
||||
user: '',
|
||||
password: process.env.VSTS_TOKEN
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(buildBody),
|
||||
method: 'POST'
|
||||
}
|
||||
let vstsResponse = await makeRequest(requestOpts, true).catch(err => {
|
||||
console.log(`Error calling VSTS for job ${build.name}`, err)
|
||||
})
|
||||
console.log(`VSTS release build request for ${build.name} successful. Check ${vstsResponse._links.web.href} for status.`)
|
||||
}
|
||||
|
||||
function runRelease (targetBranch, options) {
|
||||
if (options.ci) {
|
||||
switch (options.ci) {
|
||||
@@ -124,10 +214,19 @@ function runRelease (targetBranch, options) {
|
||||
buildAppVeyor(targetBranch, options)
|
||||
break
|
||||
}
|
||||
case 'VSTS': {
|
||||
buildVSTS(targetBranch, options)
|
||||
break
|
||||
}
|
||||
default: {
|
||||
console.log(`Error! Unknown CI: ${options.ci}.`)
|
||||
process.exit(1)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buildCircleCI(targetBranch, options)
|
||||
buildAppVeyor(targetBranch, options)
|
||||
buildVSTS(targetBranch, options)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,7 +239,7 @@ if (require.main === module) {
|
||||
const targetBranch = args._[0]
|
||||
if (args._.length < 1) {
|
||||
console.log(`Trigger CI to build release builds of electron.
|
||||
Usage: ci-release-build.js [--job=CI_JOB_NAME] [--ci=CircleCI|AppVeyor] [--ghRelease] [--automaticRelease] TARGET_BRANCH
|
||||
Usage: ci-release-build.js [--job=CI_JOB_NAME] [--ci=CircleCI|AppVeyor|VSTS] [--ghRelease] [--automaticRelease] TARGET_BRANCH
|
||||
`)
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
39
script/find-release.js
Normal file
39
script/find-release.js
Normal file
@@ -0,0 +1,39 @@
|
||||
if (!process.env.CI) require('dotenv-safe').load()
|
||||
|
||||
const GitHub = require('github')
|
||||
const github = new GitHub()
|
||||
|
||||
if (process.argv.length < 3) {
|
||||
console.log('Usage: find-release version')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const version = process.argv[2]
|
||||
|
||||
async function findRelease () {
|
||||
github.authenticate({type: 'token', token: process.env.ELECTRON_GITHUB_TOKEN})
|
||||
let releases = await github.repos.getReleases({
|
||||
owner: 'electron',
|
||||
repo: version.indexOf('nightly') > 0 ? 'nightlies' : 'electron'
|
||||
})
|
||||
let targetRelease = releases.data.find(release => {
|
||||
return release.tag_name === version
|
||||
})
|
||||
let returnObject = {}
|
||||
|
||||
if (targetRelease) {
|
||||
returnObject = {
|
||||
id: targetRelease.id,
|
||||
draft: targetRelease.draft,
|
||||
exists: true
|
||||
}
|
||||
} else {
|
||||
returnObject = {
|
||||
exists: false,
|
||||
draft: false
|
||||
}
|
||||
}
|
||||
console.log(JSON.stringify(returnObject))
|
||||
}
|
||||
|
||||
findRelease()
|
||||
29
script/get-last-major-for-master.js
Normal file
29
script/get-last-major-for-master.js
Normal file
@@ -0,0 +1,29 @@
|
||||
const { GitProcess } = require('dugite')
|
||||
const path = require('path')
|
||||
const semver = require('semver')
|
||||
const gitDir = path.resolve(__dirname, '..')
|
||||
|
||||
async function determineNextMajorForMaster () {
|
||||
let branchNames
|
||||
let result = await GitProcess.exec(['branch', '-a', '--remote', '--list', 'origin/[0-9]-[0-9]-x'], gitDir)
|
||||
if (result.exitCode === 0) {
|
||||
branchNames = result.stdout.trim().split('\n')
|
||||
const filtered = branchNames.map(b => b.replace('origin/', ''))
|
||||
return getNextReleaseBranch(filtered)
|
||||
} else {
|
||||
throw new Error('Release branches could not be fetched.')
|
||||
}
|
||||
}
|
||||
|
||||
function getNextReleaseBranch (branches) {
|
||||
const converted = branches.map(b => b.replace(/-/g, '.').replace('x', '0'))
|
||||
const next = converted.reduce((v1, v2) => {
|
||||
return semver.gt(v1, v2) ? v1 : v2
|
||||
})
|
||||
return parseInt(next.split('.')[0], 10)
|
||||
}
|
||||
|
||||
determineNextMajorForMaster().then(console.info).catch((err) => {
|
||||
console.error(err)
|
||||
process.exit(1)
|
||||
})
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
REQUESTS_DIR = os.path.abspath(os.path.join(__file__, '..', '..', '..',
|
||||
'vendor', 'requests'))
|
||||
sys.path.append(os.path.join(REQUESTS_DIR, 'build', 'lib'))
|
||||
sys.path.append(os.path.join(REQUESTS_DIR, 'build', 'lib.linux-x86_64-2.7'))
|
||||
import requests
|
||||
|
||||
GITHUB_URL = 'https://api.github.com'
|
||||
GITHUB_UPLOAD_ASSET_URL = 'https://uploads.github.com'
|
||||
|
||||
class GitHub:
|
||||
def __init__(self, access_token):
|
||||
self._authorization = 'token %s' % access_token
|
||||
|
||||
pattern = '^/repos/{0}/{0}/releases/{1}/assets$'.format('[^/]+', '[0-9]+')
|
||||
self._releases_upload_api_pattern = re.compile(pattern)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
return _Callable(self, '/%s' % attr)
|
||||
|
||||
def send(self, method, path, **kw):
|
||||
if not 'headers' in kw:
|
||||
kw['headers'] = dict()
|
||||
headers = kw['headers']
|
||||
headers['Authorization'] = self._authorization
|
||||
headers['Accept'] = 'application/vnd.github.manifold-preview'
|
||||
|
||||
# Switch to a different domain for the releases uploading API.
|
||||
if self._releases_upload_api_pattern.match(path):
|
||||
url = '%s%s' % (GITHUB_UPLOAD_ASSET_URL, path)
|
||||
else:
|
||||
url = '%s%s' % (GITHUB_URL, path)
|
||||
# Data are sent in JSON format.
|
||||
if 'data' in kw:
|
||||
kw['data'] = json.dumps(kw['data'])
|
||||
|
||||
r = getattr(requests, method)(url, **kw).json()
|
||||
if 'message' in r:
|
||||
raise Exception(json.dumps(r, indent=2, separators=(',', ': ')))
|
||||
return r
|
||||
|
||||
|
||||
class _Executable:
|
||||
def __init__(self, gh, method, path):
|
||||
self._gh = gh
|
||||
self._method = method
|
||||
self._path = path
|
||||
|
||||
def __call__(self, **kw):
|
||||
return self._gh.send(self._method, self._path, **kw)
|
||||
|
||||
|
||||
class _Callable(object):
|
||||
def __init__(self, gh, name):
|
||||
self._gh = gh
|
||||
self._name = name
|
||||
|
||||
def __call__(self, *args):
|
||||
if len(args) == 0:
|
||||
return self
|
||||
|
||||
name = '%s/%s' % (self._name, '/'.join([str(arg) for arg in args]))
|
||||
return _Callable(self._gh, name)
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if attr in ['get', 'put', 'post', 'patch', 'delete']:
|
||||
return _Executable(self._gh, attr, self._name)
|
||||
|
||||
name = '%s/%s' % (self._name, attr)
|
||||
return _Callable(self._gh, name)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user