Merge branch 'master' into use-dot-env
This commit is contained in:
commit
6ea0e73b47
37 changed files with 506 additions and 308 deletions
54
BUILD.gn
54
BUILD.gn
|
@ -217,7 +217,8 @@ static_library("electron_lib") {
|
|||
"//components/printing/common",
|
||||
"//components/security_state/content",
|
||||
"//components/viz/service",
|
||||
"//content/public/browser",
|
||||
"//content/public/app:both",
|
||||
"//content/public/child",
|
||||
"//device/geolocation",
|
||||
"//gin",
|
||||
"//net:net_resources",
|
||||
|
@ -240,6 +241,14 @@ static_library("electron_lib") {
|
|||
"brightray",
|
||||
"build/node",
|
||||
]
|
||||
include_dirs = [
|
||||
"chromium_src",
|
||||
".",
|
||||
"$target_gen_dir",
|
||||
# TODO(nornagon): replace usage of SchemeRegistry by an actually exported
|
||||
# API of blink, then delete this include dir.
|
||||
"//third_party/WebKit/Source",
|
||||
]
|
||||
if (enable_desktop_capturer) {
|
||||
deps += [ "//third_party/webrtc/modules/desktop_capture" ]
|
||||
}
|
||||
|
@ -274,20 +283,15 @@ static_library("electron_lib") {
|
|||
defines = [
|
||||
# Disable warnings for g_settings_list_schemas.
|
||||
"GLIB_DISABLE_DEPRECATION_WARNINGS",
|
||||
|
||||
# Import V8 symbols from shared library (node.dll / libnode.so)
|
||||
"USING_V8_SHARED",
|
||||
"USING_V8_PLATFORM_SHARED",
|
||||
"USING_V8_BASE_SHARED",
|
||||
]
|
||||
include_dirs = [
|
||||
"chromium_src",
|
||||
".",
|
||||
"$target_gen_dir",
|
||||
# TODO(nornagon): replace usage of SchemeRegistry by an actually exported
|
||||
# API of blink, then delete this include dir.
|
||||
"//third_party/WebKit/Source",
|
||||
]
|
||||
if (is_component_build) {
|
||||
defines += [
|
||||
# Import V8 symbols from shared library (node.dll / libnode.so)
|
||||
"USING_V8_SHARED",
|
||||
"USING_V8_PLATFORM_SHARED",
|
||||
"USING_V8_BASE_SHARED",
|
||||
]
|
||||
}
|
||||
if (is_linux || is_win) {
|
||||
deps += [ "//third_party/breakpad:client" ]
|
||||
include_dirs += [
|
||||
|
@ -433,14 +437,16 @@ if (is_mac) {
|
|||
]
|
||||
}
|
||||
|
||||
bundle_data("electron_framework_libraries") {
|
||||
public_deps = [ "build/node" ]
|
||||
sources = [
|
||||
"$root_out_dir/libnode.dylib"
|
||||
]
|
||||
outputs = [
|
||||
"{{bundle_contents_dir}}/Libraries/{{source_file_part}}"
|
||||
]
|
||||
if (is_component_build) {
|
||||
bundle_data("electron_framework_libraries") {
|
||||
public_deps = [ "build/node" ]
|
||||
sources = [
|
||||
"$root_out_dir/libnode.dylib"
|
||||
]
|
||||
outputs = [
|
||||
"{{bundle_contents_dir}}/Libraries/{{source_file_part}}"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
bundle_data("electron_crashpad_helper") {
|
||||
|
@ -465,13 +471,15 @@ if (is_mac) {
|
|||
deps = [
|
||||
"//base",
|
||||
"//base:i18n",
|
||||
":electron_framework_libraries",
|
||||
":electron_framework_resources",
|
||||
":electron_xibs",
|
||||
]
|
||||
if (!is_mas_build) {
|
||||
deps += [ ":electron_crashpad_helper" ]
|
||||
}
|
||||
if (is_component_build) {
|
||||
deps += [ ":electron_framework_libraries" ]
|
||||
}
|
||||
info_plist = "atom/common/resources/mac/Info.plist"
|
||||
extra_substitutions = [
|
||||
"ATOM_BUNDLE_ID=$electron_mac_bundle_id.framework",
|
||||
|
|
2
DEPS
2
DEPS
|
@ -2,7 +2,7 @@ vars = {
|
|||
'chromium_version':
|
||||
'66.0.3359.181',
|
||||
'libchromiumcontent_revision':
|
||||
'0628cd925e513bfd873524621a91834b366899f1',
|
||||
'a55a9ce536db60702630c4b9d94dcb2145fc3b24',
|
||||
'node_version':
|
||||
'v10.2.0-35-g4879332def',
|
||||
|
||||
|
|
|
@ -50,11 +50,6 @@ class SystemPreferences : public mate::EventEmitter<SystemPreferences>
|
|||
#if defined(OS_WIN)
|
||||
bool IsAeroGlassEnabled();
|
||||
|
||||
typedef HRESULT(STDAPICALLTYPE* DwmGetColorizationColor)(DWORD*, BOOL*);
|
||||
DwmGetColorizationColor dwmGetColorizationColor =
|
||||
(DwmGetColorizationColor)GetProcAddress(LoadLibraryW(L"dwmapi.dll"),
|
||||
"DwmGetColorizationColor");
|
||||
|
||||
std::string GetAccentColor();
|
||||
std::string GetColor(const std::string& color, mate::Arguments* args);
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
// Use of this source code is governed by the MIT license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include <dwmapi.h>
|
||||
#include <iomanip>
|
||||
|
||||
#include "atom/browser/api/atom_api_system_preferences.h"
|
||||
|
@ -38,7 +39,7 @@ std::string SystemPreferences::GetAccentColor() {
|
|||
DWORD color = 0;
|
||||
BOOL opaque = FALSE;
|
||||
|
||||
if (FAILED(dwmGetColorizationColor(&color, &opaque))) {
|
||||
if (FAILED(DwmGetColorizationColor(&color, &opaque))) {
|
||||
return "";
|
||||
}
|
||||
|
||||
|
|
|
@ -1434,6 +1434,10 @@ bool WebContents::IsAudioMuted() {
|
|||
return web_contents()->IsAudioMuted();
|
||||
}
|
||||
|
||||
bool WebContents::IsCurrentlyAudible() {
|
||||
return web_contents()->IsCurrentlyAudible();
|
||||
}
|
||||
|
||||
void WebContents::Print(mate::Arguments* args) {
|
||||
PrintSettings settings = {false, false, base::string16()};
|
||||
if (args->Length() >= 1 && !args->GetNext(&settings)) {
|
||||
|
@ -2018,6 +2022,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
|||
.SetMethod("setIgnoreMenuShortcuts", &WebContents::SetIgnoreMenuShortcuts)
|
||||
.SetMethod("setAudioMuted", &WebContents::SetAudioMuted)
|
||||
.SetMethod("isAudioMuted", &WebContents::IsAudioMuted)
|
||||
.SetMethod("isCurrentlyAudible", &WebContents::IsCurrentlyAudible)
|
||||
.SetMethod("undo", &WebContents::Undo)
|
||||
.SetMethod("redo", &WebContents::Redo)
|
||||
.SetMethod("cut", &WebContents::Cut)
|
||||
|
|
|
@ -143,6 +143,7 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
|||
void SetIgnoreMenuShortcuts(bool ignore);
|
||||
void SetAudioMuted(bool muted);
|
||||
bool IsAudioMuted();
|
||||
bool IsCurrentlyAudible();
|
||||
void Print(mate::Arguments* args);
|
||||
std::vector<printing::PrinterBasicInfo> GetPrinterList();
|
||||
void SetEmbedder(const WebContents* embedder);
|
||||
|
|
|
@ -26,7 +26,8 @@ FrameSubscriber::FrameSubscriber(v8::Isolate* isolate,
|
|||
: content::WebContentsObserver(web_contents),
|
||||
isolate_(isolate),
|
||||
callback_(callback),
|
||||
only_dirty_(only_dirty) {}
|
||||
only_dirty_(only_dirty),
|
||||
weak_ptr_factory_(this) {}
|
||||
|
||||
FrameSubscriber::~FrameSubscriber() = default;
|
||||
|
||||
|
@ -64,7 +65,7 @@ void FrameSubscriber::DidReceiveCompositorFrame() {
|
|||
|
||||
view->CopyFromSurface(
|
||||
gfx::Rect(), view->GetViewBounds().size(),
|
||||
base::BindOnce(&FrameSubscriber::Done, base::Unretained(this),
|
||||
base::BindOnce(&FrameSubscriber::Done, weak_ptr_factory_.GetWeakPtr(),
|
||||
GetDamageRect()));
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "content/public/browser/web_contents.h"
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/memory/weak_ptr.h"
|
||||
#include "components/viz/common/frame_sinks/copy_output_result.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "ui/gfx/image/image.h"
|
||||
|
@ -39,6 +40,8 @@ class FrameSubscriber : public content::WebContentsObserver {
|
|||
FrameCaptureCallback callback_;
|
||||
bool only_dirty_;
|
||||
|
||||
base::WeakPtrFactory<FrameSubscriber> weak_ptr_factory_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(FrameSubscriber);
|
||||
};
|
||||
|
||||
|
|
|
@ -12,17 +12,27 @@
|
|||
#include "atom/common/native_mate_converters/gurl_converter.h"
|
||||
#include "atom/common/node_includes.h"
|
||||
#include "base/hash.h"
|
||||
#include "base/process/process_handle.h"
|
||||
#include "base/strings/stringprintf.h"
|
||||
#include "native_mate/dictionary.h"
|
||||
#include "url/origin.h"
|
||||
#include "v8/include/v8-profiler.h"
|
||||
|
||||
// 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 std {
|
||||
|
||||
// The hash function used by DoubleIDWeakMap.
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -90,6 +100,16 @@ int32_t GetObjectHash(v8::Local<v8::Object> object) {
|
|||
return object->GetIdentityHash();
|
||||
}
|
||||
|
||||
std::string GetContextID(v8::Isolate* isolate) {
|
||||
// When a page is reloaded, V8 and blink may have optimizations that do not
|
||||
// free blink::WebLocalFrame and v8::Context and reuse them for the new page,
|
||||
// while we always recreate node::Environment when a page is loaded.
|
||||
// So the only reliable way to return an identity for a page, is to return the
|
||||
// address of the node::Environment instance.
|
||||
node::Environment* env = node::Environment::GetCurrent(isolate);
|
||||
return base::StringPrintf("%" CrPRIdPid "-%p", base::GetCurrentProcId(), env);
|
||||
}
|
||||
|
||||
void TakeHeapSnapshot(v8::Isolate* isolate) {
|
||||
isolate->GetHeapProfiler()->TakeHeapSnapshot();
|
||||
}
|
||||
|
@ -112,12 +132,14 @@ void Initialize(v8::Local<v8::Object> exports,
|
|||
dict.SetMethod("setHiddenValue", &SetHiddenValue);
|
||||
dict.SetMethod("deleteHiddenValue", &DeleteHiddenValue);
|
||||
dict.SetMethod("getObjectHash", &GetObjectHash);
|
||||
dict.SetMethod("getContextId", &GetContextID);
|
||||
dict.SetMethod("takeHeapSnapshot", &TakeHeapSnapshot);
|
||||
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) {}
|
||||
|
||||
RemoteCallbackFreer::~RemoteCallbackFreer() {}
|
||||
|
@ -34,6 +37,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);
|
||||
|
|
|
@ -29,14 +29,17 @@ content::RenderFrame* GetCurrentRenderFrame() {
|
|||
// static
|
||||
void RemoteObjectFreer::BindTo(v8::Isolate* isolate,
|
||||
v8::Local<v8::Object> target,
|
||||
const std::string& context_id,
|
||||
int object_id) {
|
||||
new RemoteObjectFreer(isolate, target, object_id);
|
||||
new RemoteObjectFreer(isolate, target, context_id, 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();
|
||||
|
@ -56,6 +59,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,6 +5,8 @@
|
|||
#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 {
|
||||
|
@ -13,17 +15,20 @@ class RemoteObjectFreer : public ObjectLifeMonitor {
|
|||
public:
|
||||
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,
|
||||
const std::string& context_id,
|
||||
int object_id);
|
||||
~RemoteObjectFreer() override;
|
||||
|
||||
void RunDestructor() override;
|
||||
|
||||
private:
|
||||
std::string context_id_;
|
||||
int object_id_;
|
||||
int routing_id_;
|
||||
|
||||
|
|
|
@ -9,7 +9,9 @@
|
|||
|
||||
// Include common headers for using node APIs.
|
||||
|
||||
#ifdef NODE_SHARED_MODE
|
||||
#define BUILDING_NODE_EXTENSION
|
||||
#endif
|
||||
|
||||
// The following define makes sure that we do not include the macros
|
||||
// again. But we still need the tracing functions, so declaring them.
|
||||
|
|
|
@ -240,33 +240,6 @@ bool ShowItemInFolder(const base::FilePath& full_path) {
|
|||
if (dir.empty())
|
||||
return false;
|
||||
|
||||
typedef HRESULT(WINAPI * SHOpenFolderAndSelectItemsFuncPtr)(
|
||||
PCIDLIST_ABSOLUTE pidl_Folder, UINT cidl, PCUITEMID_CHILD_ARRAY pidls,
|
||||
DWORD flags);
|
||||
|
||||
static SHOpenFolderAndSelectItemsFuncPtr open_folder_and_select_itemsPtr =
|
||||
NULL;
|
||||
static bool initialize_open_folder_proc = true;
|
||||
if (initialize_open_folder_proc) {
|
||||
initialize_open_folder_proc = false;
|
||||
// The SHOpenFolderAndSelectItems API is exposed by shell32 version 6
|
||||
// and does not exist in Win2K. We attempt to retrieve this function export
|
||||
// from shell32 and if it does not exist, we just invoke ShellExecute to
|
||||
// open the folder thus losing the functionality to select the item in
|
||||
// the process.
|
||||
HMODULE shell32_base = GetModuleHandle(L"shell32.dll");
|
||||
if (!shell32_base) {
|
||||
NOTREACHED() << " " << __FUNCTION__ << "(): Can't open shell32.dll";
|
||||
return false;
|
||||
}
|
||||
open_folder_and_select_itemsPtr =
|
||||
reinterpret_cast<SHOpenFolderAndSelectItemsFuncPtr>(
|
||||
GetProcAddress(shell32_base, "SHOpenFolderAndSelectItems"));
|
||||
}
|
||||
if (!open_folder_and_select_itemsPtr) {
|
||||
return ui::win::OpenFolderViaShell(dir);
|
||||
}
|
||||
|
||||
Microsoft::WRL::ComPtr<IShellFolder> desktop;
|
||||
HRESULT hr = SHGetDesktopFolder(desktop.GetAddressOf());
|
||||
if (FAILED(hr))
|
||||
|
@ -290,8 +263,8 @@ bool ShowItemInFolder(const base::FilePath& full_path) {
|
|||
|
||||
const ITEMIDLIST* highlight[] = {file_item};
|
||||
|
||||
hr = (*open_folder_and_select_itemsPtr)(dir_item, arraysize(highlight),
|
||||
highlight, NULL);
|
||||
hr = SHOpenFolderAndSelectItems(dir_item, arraysize(highlight), highlight,
|
||||
NULL);
|
||||
if (!FAILED(hr))
|
||||
return true;
|
||||
|
||||
|
|
|
@ -81,11 +81,19 @@ base::CommandLine::StringVector GetArgv() {
|
|||
return base::CommandLine::ForCurrentProcess()->argv();
|
||||
}
|
||||
|
||||
v8::Local<v8::Value> CreatePreloadScript(v8::Isolate* isolate,
|
||||
v8::Local<v8::String> preloadSrc) {
|
||||
auto script = v8::Script::Compile(preloadSrc);
|
||||
auto func = script->Run();
|
||||
return func;
|
||||
}
|
||||
|
||||
void InitializeBindings(v8::Local<v8::Object> binding,
|
||||
v8::Local<v8::Context> context) {
|
||||
auto* isolate = context->GetIsolate();
|
||||
mate::Dictionary b(isolate, binding);
|
||||
b.SetMethod("get", GetBinding);
|
||||
b.SetMethod("createPreloadScript", CreatePreloadScript);
|
||||
b.SetMethod("crash", AtomBindings::Crash);
|
||||
b.SetMethod("hang", AtomBindings::Hang);
|
||||
b.SetMethod("getArgv", GetArgv);
|
||||
|
|
|
@ -13,7 +13,6 @@ action("configure_node") {
|
|||
args = [
|
||||
"--enable-static",
|
||||
"--release-urlbase=https://atom.io/download/electron",
|
||||
"--shared",
|
||||
"--shared-openssl",
|
||||
"--shared-openssl-includes=" + rebase_path("//third_party/boringssl/src/include"),
|
||||
"--shared-openssl-libname=boringssl" + ssl_libname_suffix,
|
||||
|
@ -29,6 +28,9 @@ action("configure_node") {
|
|||
"--config-out-dir=" + rebase_path(target_gen_dir),
|
||||
"--no-run-gyp",
|
||||
]
|
||||
if (is_component_build) {
|
||||
args += [ "--shared" ]
|
||||
}
|
||||
outputs = [
|
||||
"$target_gen_dir/config.gypi",
|
||||
]
|
||||
|
@ -75,6 +77,7 @@ action("gyp_node") {
|
|||
"-D", "llvm_dir=" + rebase_path("//third_party/llvm-build/Release+Asserts"),
|
||||
"-D", "libcxx_dir=" + rebase_path("//buildtools/third_party/libc++"),
|
||||
"-D", "libcxxabi_dir=" + rebase_path("//buildtools/third_party/libc++abi"),
|
||||
"-D", "is_component_build=$is_component_build",
|
||||
"-Goutput_dir=./$target_out_dir", # bizarrely, gyp generates from the build root instead of from cwd
|
||||
"-fninja",
|
||||
rebase_path("//third_party/electron_node/node.gyp", root_build_dir),
|
||||
|
@ -93,16 +96,53 @@ action("build_node") {
|
|||
script = "//electron/build/run-ninja.py"
|
||||
args = [
|
||||
"-C", rebase_path(target_out_dir, root_build_dir) + "/$node_configuration",
|
||||
"node_lib"
|
||||
"node_lib",
|
||||
"libuv", "nghttp2", "cares", "http_parser", "zlib"
|
||||
]
|
||||
if (is_mac) {
|
||||
outputs = [ "$target_out_dir/$node_configuration/libnode.dylib" ]
|
||||
if (is_component_build) {
|
||||
outputs = [ "$target_out_dir/$node_configuration/libnode.dylib" ]
|
||||
} else {
|
||||
outputs = [
|
||||
"$target_out_dir/$node_configuration/libnode.a",
|
||||
"$target_out_dir/$node_configuration/libcares.a",
|
||||
"$target_out_dir/$node_configuration/libhttp_parser.a",
|
||||
"$target_out_dir/$node_configuration/libnghttp2.a",
|
||||
"$target_out_dir/$node_configuration/libuv.a",
|
||||
"$target_out_dir/$node_configuration/libzlib.a",
|
||||
]
|
||||
}
|
||||
}
|
||||
if (is_linux) {
|
||||
outputs = [ "$target_out_dir/$node_configuration/lib/libnode.so" ]
|
||||
if (is_component_build) {
|
||||
outputs = [ "$target_out_dir/$node_configuration/lib/libnode.so" ]
|
||||
} else {
|
||||
outputs = [
|
||||
"$target_out_dir/$node_configuration/obj/third_party/electron_node/libnode.a",
|
||||
"$target_out_dir/$node_configuration/obj/third_party/electron_node/deps/uv/libuv.a",
|
||||
"$target_out_dir/$node_configuration/obj/third_party/electron_node/deps/nghttp2/libnghttp2.a",
|
||||
"$target_out_dir/$node_configuration/obj/third_party/electron_node/deps/cares/libcares.a",
|
||||
"$target_out_dir/$node_configuration/obj/third_party/electron_node/deps/http_parser/libhttp_parser.a",
|
||||
"$target_out_dir/$node_configuration/obj/third_party/electron_node/deps/zlib/libzlib.a",
|
||||
]
|
||||
}
|
||||
}
|
||||
if (is_win) {
|
||||
outputs = [ "$target_out_dir/$node_configuration/node.dll.lib", "$target_out_dir/$node_configuration/node.dll" ]
|
||||
if (is_component_build) {
|
||||
outputs = [
|
||||
"$target_out_dir/$node_configuration/node.dll.lib",
|
||||
"$target_out_dir/$node_configuration/node.dll",
|
||||
]
|
||||
} else {
|
||||
outputs = [
|
||||
"$target_out_dir/$node_configuration/obj/third_party/electron_node/node.lib",
|
||||
"$target_out_dir/$node_configuration/obj/third_party/electron_node/deps/uv/libuv.lib",
|
||||
"$target_out_dir/$node_configuration/obj/third_party/electron_node/deps/nghttp2/nghttp2.lib",
|
||||
"$target_out_dir/$node_configuration/obj/third_party/electron_node/deps/cares/cares.lib",
|
||||
"$target_out_dir/$node_configuration/obj/third_party/electron_node/deps/http_parser/http_parser.lib",
|
||||
"$target_out_dir/$node_configuration/obj/third_party/electron_node/deps/zlib/zlib.lib",
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -120,17 +160,25 @@ config("node_config") {
|
|||
"//third_party/electron_node/deps/uv/include",
|
||||
"//third_party/electron_node/deps/cares/include",
|
||||
]
|
||||
libs = [ node_libs[0] ]
|
||||
if (is_win && is_component_build) {
|
||||
# Windows builds need both the .dll and the .dll.lib copied, but only the
|
||||
# .dll.lib goes in the `libs` list.
|
||||
libs = [ node_libs[0] ]
|
||||
} else {
|
||||
libs = node_libs
|
||||
}
|
||||
cflags_cc = [
|
||||
"-Wno-deprecated-declarations",
|
||||
]
|
||||
defines = [
|
||||
# We need to access internal implementations of Node.
|
||||
"NODE_WANT_INTERNALS=1",
|
||||
"NODE_SHARED_MODE",
|
||||
"HAVE_OPENSSL=1",
|
||||
"HAVE_INSPECTOR=1",
|
||||
]
|
||||
if (is_component_build) {
|
||||
defines += [ "NODE_SHARED_MODE" ]
|
||||
}
|
||||
}
|
||||
|
||||
group("node") {
|
||||
|
|
|
@ -5,6 +5,8 @@
|
|||
'v8_enable_inspector': 1,
|
||||
|
||||
'shlib_suffix': '<(shlib_suffix_gn)',
|
||||
|
||||
'is_component_build%': 'false',
|
||||
},
|
||||
'conditions': [
|
||||
['OS=="linux"', {
|
||||
|
@ -32,7 +34,13 @@
|
|||
}]
|
||||
]
|
||||
}
|
||||
}]
|
||||
}],
|
||||
['OS=="win"', {
|
||||
'make_global_settings': [
|
||||
['CC', '<(llvm_dir)/bin/clang-cl'],
|
||||
# Also defining CXX doesn't appear to be necessary.
|
||||
]
|
||||
}],
|
||||
],
|
||||
'target_defaults': {
|
||||
'target_conditions': [
|
||||
|
@ -47,35 +55,44 @@
|
|||
'EVP_CTRL_AEAD_SET_IVLEN=EVP_CTRL_GCM_SET_IVLEN',
|
||||
'EVP_CTRL_CCM_SET_TAG=EVP_CTRL_GCM_SET_TAG',
|
||||
'EVP_CTRL_AEAD_GET_TAG=EVP_CTRL_GCM_GET_TAG',
|
||||
'WIN32_LEAN_AND_MEAN',
|
||||
],
|
||||
'conditions': [
|
||||
['OS=="win"', {
|
||||
'libraries': [
|
||||
'-lv8.dll',
|
||||
'-lv8_libbase.dll',
|
||||
'-lv8_libplatform.dll',
|
||||
'-licuuc.dll',
|
||||
'-ldbghelp',
|
||||
'conditions': [
|
||||
['is_component_build=="true"', {
|
||||
'libraries': [
|
||||
'-lv8.dll',
|
||||
'-lv8_libbase.dll',
|
||||
'-lv8_libplatform.dll',
|
||||
'-licuuc.dll',
|
||||
'-ldbghelp',
|
||||
],
|
||||
'msvs_settings': {
|
||||
# Change location of some hard-coded paths.
|
||||
'VCLinkerTool': {
|
||||
'AdditionalOptions!': [
|
||||
'/WHOLEARCHIVE:<(PRODUCT_DIR)\\lib\\zlib<(STATIC_LIB_SUFFIX)',
|
||||
'/WHOLEARCHIVE:<(PRODUCT_DIR)\\lib\\libuv<(STATIC_LIB_SUFFIX)',
|
||||
],
|
||||
'AdditionalOptions': [
|
||||
'/WHOLEARCHIVE:<(PRODUCT_DIR)\\obj\\third_party\\electron_node\\deps\\zlib\\zlib<(STATIC_LIB_SUFFIX)',
|
||||
'/WHOLEARCHIVE:<(PRODUCT_DIR)\\obj\\third_party\\electron_node\\deps\\uv\\libuv<(STATIC_LIB_SUFFIX)',
|
||||
],
|
||||
},
|
||||
},
|
||||
}]
|
||||
],
|
||||
'msvs_settings': {
|
||||
# Change location of some hard-coded paths.
|
||||
'VCLinkerTool': {
|
||||
'AdditionalOptions!': [
|
||||
'/WHOLEARCHIVE:<(PRODUCT_DIR)\\lib\\zlib<(STATIC_LIB_SUFFIX)',
|
||||
'/WHOLEARCHIVE:<(PRODUCT_DIR)\\lib\\libuv<(STATIC_LIB_SUFFIX)',
|
||||
],
|
||||
'AdditionalOptions': [
|
||||
'/WHOLEARCHIVE:<(PRODUCT_DIR)\\obj\\third_party\\electron_node\\deps\\zlib\\zlib<(STATIC_LIB_SUFFIX)',
|
||||
'/WHOLEARCHIVE:<(PRODUCT_DIR)\\obj\\third_party\\electron_node\\deps\\uv\\libuv<(STATIC_LIB_SUFFIX)',
|
||||
],
|
||||
},
|
||||
},
|
||||
}, {
|
||||
'libraries': [
|
||||
'-lv8',
|
||||
'-lv8_libbase',
|
||||
'-lv8_libplatform',
|
||||
'-licuuc',
|
||||
'conditions': [
|
||||
['is_component_build=="true"', {
|
||||
'libraries': [
|
||||
'-lv8',
|
||||
'-lv8_libbase',
|
||||
'-lv8_libplatform',
|
||||
'-licuuc',
|
||||
]
|
||||
}]
|
||||
]
|
||||
}]
|
||||
]
|
||||
|
|
|
@ -832,6 +832,10 @@ Mute the audio on the current web page.
|
|||
|
||||
Returns `Boolean` - Whether this page has been muted.
|
||||
|
||||
#### `contents.isCurrentlyAudible()`
|
||||
|
||||
Returns `Boolean` - Whether audio is currently playing.
|
||||
|
||||
#### `contents.setZoomFactor(factor)`
|
||||
|
||||
* `factor` Number - Zoom factor.
|
||||
|
|
|
@ -450,6 +450,10 @@ Set guest page muted.
|
|||
|
||||
Returns `Boolean` - Whether guest page has been muted.
|
||||
|
||||
#### `<webview>.isCurrentlyAudible()`
|
||||
|
||||
Returns `Boolean` - Whether audio is currently playing.
|
||||
|
||||
### `<webview>.undo()`
|
||||
|
||||
Executes editing command `undo` in page.
|
||||
|
|
|
@ -17,11 +17,15 @@ 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)`.
|
||||
|
||||
Once you've generated these tokens, put them in a `.env` file in the root directory
|
||||
of the project. This file is gitignored, and will be loaded into the
|
||||
environment by the release scripts.
|
||||
|
||||
|
||||
## Determine which branch to release from
|
||||
|
||||
- **If releasing beta,** run the scripts below from `master`.
|
||||
|
@ -87,8 +91,11 @@ $ ./script/bump-version.py --bump minor --dry-run
|
|||
The `prepare-release` script will trigger the builds via API calls.
|
||||
To monitor the build progress, see the following pages:
|
||||
|
||||
- [circleci.com/gh/electron/electron](https://circleci.com/gh/electron) for OS X and 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
|
||||
|
||||
|
@ -195,35 +202,13 @@ under the `beta` tag and can be installed via `npm install electron@beta`.
|
|||
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. Uncheck the `prerelease` checkbox if you're publishing a stable release;
|
||||
leave it checked for beta releases.
|
||||
4. Click 'Save draft'. **Do not click 'Publish release'!**
|
||||
5. Wait for all builds to pass before proceeding.
|
||||
6. In the `release` branch, verify that the release's files have been created:
|
||||
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
|
||||
$ git rev-parse --abbrev-ref HEAD
|
||||
release
|
||||
$ npm run release -- --validateRelease
|
||||
```
|
||||
|
||||
## Merge temporary branch (pre-2-0-x branches only)
|
||||
Once the release builds have finished, merge the `release` branch back into
|
||||
the source release branch using the `merge-release` script.
|
||||
If the branch cannot be successfully merged back this script will automatically
|
||||
rebase the `release` branch and push the changes which will trigger the release
|
||||
builds again, which means you will need to wait for the release builds to run
|
||||
again before proceeding.
|
||||
|
||||
### Merging back into master
|
||||
```sh
|
||||
npm run merge-release -- master
|
||||
```
|
||||
|
||||
### Merging back into old release branch
|
||||
```sh
|
||||
npm run merge-release -- 1-7-x
|
||||
```
|
||||
|
||||
## Publish the release
|
||||
|
||||
Once the merge has finished successfully, run the `release` script
|
||||
|
@ -237,7 +222,6 @@ 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
|
||||
|
||||
|
@ -267,16 +251,39 @@ electron
|
|||
$ npm run publish-to-npm
|
||||
```
|
||||
|
||||
Note: In general you should be using the latest Node during this
|
||||
process; however, older versions of the `publish-to-npm` script
|
||||
may have trouble with Node 7 or higher. If you have trouble with
|
||||
this in an older branch, try running with an older version of Node,
|
||||
e.g. a 6.x LTS.
|
||||
|
||||
[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
|
||||
|
|
|
@ -7,7 +7,7 @@ and then enable it in your application.
|
|||
## Prepare a Copy of Flash Plugin
|
||||
|
||||
On macOS and Linux, the details of the Pepper Flash plugin can be found by
|
||||
navigating to `chrome://plugins` in the Chrome browser. Its location and version
|
||||
navigating to `chrome://flash` in the Chrome browser. Its location and version
|
||||
are useful for Electron's Pepper Flash support. You can also copy it to another
|
||||
location.
|
||||
|
||||
|
|
|
@ -363,6 +363,7 @@
|
|||
],
|
||||
'link_settings': {
|
||||
'libraries': [
|
||||
'-ldwmapi.lib',
|
||||
'-limm32.lib',
|
||||
'-lgdi32.lib',
|
||||
'-loleacc.lib',
|
||||
|
|
|
@ -17,16 +17,15 @@ class ObjectsRegistry {
|
|||
|
||||
// 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]
|
||||
let owner = this.owners[contextId]
|
||||
if (!owner) {
|
||||
owner = this.owners[webContentsId] = new Set()
|
||||
this.registerDeleteListener(webContents, webContentsId)
|
||||
owner = this.owners[contextId] = new Set()
|
||||
this.registerDeleteListener(webContents, contextId)
|
||||
}
|
||||
if (!owner.has(id)) {
|
||||
owner.add(id)
|
||||
|
@ -43,25 +42,26 @@ 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 (contextId, id) {
|
||||
let owner = this.owners[contextId]
|
||||
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 (contextId) {
|
||||
let owner = this.owners[contextId]
|
||||
if (!owner) return
|
||||
|
||||
for (let id of owner) this.dereference(id)
|
||||
|
||||
delete this.owners[webContentsId]
|
||||
delete this.owners[contextId]
|
||||
}
|
||||
|
||||
// Private: Saves the object into storage and assigns an ID for it.
|
||||
|
@ -92,12 +92,12 @@ class ObjectsRegistry {
|
|||
}
|
||||
|
||||
// Private: Clear the storage when webContents is reloaded/navigated.
|
||||
registerDeleteListener (webContents, webContentsId) {
|
||||
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(contextId)
|
||||
}
|
||||
}
|
||||
webContents.on('render-view-deleted', listener)
|
||||
|
|
|
@ -56,7 +56,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') {
|
||||
|
@ -84,14 +84,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, optimizeSimpleObject))
|
||||
meta.members = value.map((el) => valueToMeta(sender, contextId, el, optimizeSimpleObject))
|
||||
} 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') {
|
||||
|
@ -101,7 +101,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') {
|
||||
|
@ -132,12 +132,12 @@ const plainObjectToMeta = function (obj) {
|
|||
}
|
||||
|
||||
// Convert Error into meta data.
|
||||
const exceptionToMeta = function (sender, error) {
|
||||
const exceptionToMeta = function (sender, contextId, error) {
|
||||
return {
|
||||
type: 'exception',
|
||||
message: error.message,
|
||||
stack: error.stack || error,
|
||||
cause: valueToMeta(sender, error.cause)
|
||||
cause: valueToMeta(sender, contextId, error.cause)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,7 +169,7 @@ const removeRemoteListenersAndLogWarning = (sender, meta, 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) {
|
||||
switch (meta.type) {
|
||||
case 'value':
|
||||
|
@ -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 bufferUtils.metaToBuffer(meta.value)
|
||||
case 'date':
|
||||
|
@ -201,26 +201,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(this, meta, 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
|
||||
}
|
||||
|
@ -233,18 +233,18 @@ 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) {
|
||||
const funcMarkedAsync = v8Util.getHiddenValue(func, 'asynchronous')
|
||||
const 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 {
|
||||
const 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
|
||||
|
@ -257,105 +257,105 @@ 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(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, 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(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, 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(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, 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(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, 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) {
|
||||
throwRPCError(`Cannot call constructor on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
event.returnValue = valueToMeta(event.sender, new constructor(...args))
|
||||
event.returnValue = valueToMeta(event.sender, contextId, new constructor(...args))
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, 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(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, 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) {
|
||||
throwRPCError(`Cannot call constructor '${method}' on missing remote object ${id}`)
|
||||
}
|
||||
|
||||
event.returnValue = valueToMeta(event.sender, new object[method](...args))
|
||||
event.returnValue = valueToMeta(event.sender, contextId, new object[method](...args))
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, 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(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, 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) {
|
||||
|
@ -365,11 +365,11 @@ ipcMain.on('ELECTRON_BROWSER_MEMBER_SET', function (event, id, name, args) {
|
|||
obj[name] = args[0]
|
||||
event.returnValue = null
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
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)
|
||||
|
||||
|
@ -377,14 +377,14 @@ 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(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, 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(contextId, id)
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_CONTEXT_RELEASE', (e, contextId) => {
|
||||
|
@ -392,16 +392,16 @@ ipcMain.on('ELECTRON_BROWSER_CONTEXT_RELEASE', (e, contextId) => {
|
|||
e.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(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, requestId, guestInstanceId, method, ...args) {
|
||||
ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, contextId, requestId, guestInstanceId, method, ...args) {
|
||||
try {
|
||||
let guestViewManager = require('./guest-view-manager')
|
||||
let guest = guestViewManager.getGuest(guestInstanceId)
|
||||
|
@ -413,7 +413,7 @@ ipcMain.on('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', function (event, request
|
|||
}
|
||||
guest[method].apply(guest, args)
|
||||
} catch (error) {
|
||||
event.returnValue = exceptionToMeta(event.sender, error)
|
||||
event.returnValue = exceptionToMeta(event.sender, contextId, error)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -9,6 +9,18 @@ const bufferUtils = require('../../common/buffer-utils')
|
|||
const callbacksRegistry = new CallbacksRegistry()
|
||||
const remoteObjectCache = v8Util.createIDWeakMap()
|
||||
|
||||
// An unique ID that can represent current context.
|
||||
const contextId = v8Util.getContextId()
|
||||
|
||||
// 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) => {
|
||||
|
@ -107,7 +119,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)
|
||||
}
|
||||
|
||||
|
@ -126,7 +138,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)
|
||||
}
|
||||
|
||||
|
@ -134,7 +146,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
|
||||
}
|
||||
|
@ -164,7 +176,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)
|
||||
}
|
||||
|
||||
|
@ -224,7 +236,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
|
||||
|
@ -237,7 +249,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
|
||||
|
@ -264,60 +276,51 @@ function metaToException (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 (process.webContentsId) {
|
||||
// set by sandbox renderer init script
|
||||
initialContext = process.webContentsId
|
||||
} else if (initialContext) {
|
||||
initialContext = parseInt(initialContext.substr(CONTEXT_ARG.length), 10)
|
||||
} else {
|
||||
// if not available, pull 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)
|
||||
}
|
||||
|
||||
|
@ -334,7 +337,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)
|
||||
}
|
||||
|
||||
|
|
|
@ -8,6 +8,9 @@ const webViewConstants = require('./web-view-constants')
|
|||
|
||||
const hasProp = {}.hasOwnProperty
|
||||
|
||||
// An unique ID that can represent current context.
|
||||
const contextId = v8Util.getContextId()
|
||||
|
||||
// ID generator.
|
||||
let nextId = 0
|
||||
|
||||
|
@ -344,6 +347,7 @@ const registerWebViewElement = function () {
|
|||
'inspectElement',
|
||||
'setAudioMuted',
|
||||
'isAudioMuted',
|
||||
'isCurrentlyAudible',
|
||||
'undo',
|
||||
'redo',
|
||||
'cut',
|
||||
|
@ -396,7 +400,7 @@ const registerWebViewElement = function () {
|
|||
const createNonBlockHandler = function (m) {
|
||||
return function (...args) {
|
||||
const internal = v8Util.getHiddenValue(this, 'internal')
|
||||
ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', null, internal.guestInstanceId, m, ...args)
|
||||
ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', contextId, null, internal.guestInstanceId, m, ...args)
|
||||
}
|
||||
}
|
||||
for (const method of nonblockMethods) {
|
||||
|
@ -410,7 +414,7 @@ const registerWebViewElement = function () {
|
|||
hasUserGesture = false
|
||||
}
|
||||
const requestId = getNextId()
|
||||
ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', requestId, internal.guestInstanceId, 'executeJavaScript', code, hasUserGesture)
|
||||
ipcRenderer.send('ELECTRON_BROWSER_ASYNC_CALL_TO_GUEST_VIEW', contextId, requestId, internal.guestInstanceId, 'executeJavaScript', code, hasUserGesture)
|
||||
ipcRenderer.once(`ELECTRON_RENDERER_ASYNC_CALL_TO_GUEST_VIEW_RESPONSE_${requestId}`, function (event, result) {
|
||||
if (callback) callback(result)
|
||||
})
|
||||
|
|
|
@ -112,10 +112,8 @@ if (preloadSrc) {
|
|||
${preloadSrc}
|
||||
})`
|
||||
|
||||
// eval in window scope:
|
||||
// http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.2
|
||||
const geval = eval
|
||||
const preloadFn = geval(preloadWrapperSrc)
|
||||
// eval in window scope
|
||||
const preloadFn = binding.createPreloadScript(preloadWrapperSrc)
|
||||
const {setImmediate, clearImmediate} = require('timers')
|
||||
preloadFn(preloadRequire, preloadProcess, Buffer, global, setImmediate, clearImmediate)
|
||||
} else if (preloadError) {
|
||||
|
|
|
@ -120,7 +120,7 @@ async function callAppVeyor (targetBranch, job, 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}`
|
||||
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}`)
|
||||
}
|
||||
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
'use strict'
|
||||
|
||||
/* eslint-disable no-unused-expressions */
|
||||
|
||||
const {expect} = require('chai')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const {nativeImage} = require('electron')
|
||||
const path = require('path')
|
||||
|
||||
const {expect} = chai
|
||||
chai.use(dirtyChai)
|
||||
|
||||
describe('nativeImage module', () => {
|
||||
const ImageFormat = {
|
||||
PNG: 'png',
|
||||
|
@ -111,16 +113,16 @@ describe('nativeImage module', () => {
|
|||
expect(empty.toDataURL()).to.equal('data:image/png;base64,')
|
||||
expect(empty.toDataURL({scaleFactor: 2.0})).to.equal('data:image/png;base64,')
|
||||
expect(empty.getSize()).to.deep.equal({width: 0, height: 0})
|
||||
expect(empty.getBitmap()).to.be.empty
|
||||
expect(empty.getBitmap({scaleFactor: 2.0})).to.be.empty
|
||||
expect(empty.toBitmap()).to.be.empty
|
||||
expect(empty.toBitmap({scaleFactor: 2.0})).to.be.empty
|
||||
expect(empty.toJPEG(100)).to.be.empty
|
||||
expect(empty.toPNG()).to.be.empty
|
||||
expect(empty.toPNG({scaleFactor: 2.0})).to.be.empty
|
||||
expect(empty.getBitmap()).to.be.empty()
|
||||
expect(empty.getBitmap({scaleFactor: 2.0})).to.be.empty()
|
||||
expect(empty.toBitmap()).to.be.empty()
|
||||
expect(empty.toBitmap({scaleFactor: 2.0})).to.be.empty()
|
||||
expect(empty.toJPEG(100)).to.be.empty()
|
||||
expect(empty.toPNG()).to.be.empty()
|
||||
expect(empty.toPNG({scaleFactor: 2.0})).to.be.empty()
|
||||
|
||||
if (process.platform === 'darwin') {
|
||||
expect(empty.getNativeHandle()).to.be.empty
|
||||
expect(empty.getNativeHandle()).to.be.empty()
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@ -135,7 +137,7 @@ describe('nativeImage module', () => {
|
|||
|
||||
const imageB = nativeImage.createFromBuffer(imageA.toPNG())
|
||||
expect(imageB.getSize()).to.deep.equal({width: 538, height: 190})
|
||||
expect(imageA.toBitmap().equals(imageB.toBitmap())).to.be.true
|
||||
expect(imageA.toBitmap().equals(imageB.toBitmap())).to.be.true()
|
||||
|
||||
const imageC = nativeImage.createFromBuffer(imageA.toJPEG(100))
|
||||
expect(imageC.getSize()).to.deep.equal({width: 538, height: 190})
|
||||
|
@ -214,7 +216,7 @@ describe('nativeImage module', () => {
|
|||
expect(imageTwo.getSize()).to.deep.equal(
|
||||
{width: imageData.width, height: imageData.height})
|
||||
|
||||
expect(imageOne.toBitmap().equals(imageTwo.toBitmap())).to.be.true
|
||||
expect(imageOne.toBitmap().equals(imageTwo.toBitmap())).to.be.true()
|
||||
})
|
||||
|
||||
it('supports a scale factor', () => {
|
||||
|
@ -249,7 +251,7 @@ describe('nativeImage module', () => {
|
|||
expect(imageC.getSize()).to.deep.equal(
|
||||
{width: imageData.width, height: imageData.height})
|
||||
|
||||
expect(imageB.toBitmap().equals(imageC.toBitmap())).to.be.true
|
||||
expect(imageB.toBitmap().equals(imageC.toBitmap())).to.be.true()
|
||||
})
|
||||
|
||||
it('supports a scale factor', () => {
|
||||
|
@ -280,21 +282,21 @@ describe('nativeImage module', () => {
|
|||
it('loads images from paths relative to the current working directory', () => {
|
||||
const imagePath = `.${path.sep}${path.join('spec', 'fixtures', 'assets', 'logo.png')}`
|
||||
const image = nativeImage.createFromPath(imagePath)
|
||||
expect(image.isEmpty()).to.be.false
|
||||
expect(image.isEmpty()).to.be.false()
|
||||
expect(image.getSize()).to.deep.equal({width: 538, height: 190})
|
||||
})
|
||||
|
||||
it('loads images from paths with `.` segments', () => {
|
||||
const imagePath = `${path.join(__dirname, 'fixtures')}${path.sep}.${path.sep}${path.join('assets', 'logo.png')}`
|
||||
const image = nativeImage.createFromPath(imagePath)
|
||||
expect(image.isEmpty()).to.be.false
|
||||
expect(image.isEmpty()).to.be.false()
|
||||
expect(image.getSize()).to.deep.equal({width: 538, height: 190})
|
||||
})
|
||||
|
||||
it('loads images from paths with `..` segments', () => {
|
||||
const imagePath = `${path.join(__dirname, 'fixtures', 'api')}${path.sep}..${path.sep}${path.join('assets', 'logo.png')}`
|
||||
const image = nativeImage.createFromPath(imagePath)
|
||||
expect(image.isEmpty()).to.be.false
|
||||
expect(image.isEmpty()).to.be.false()
|
||||
expect(image.getSize()).to.deep.equal({width: 538, height: 190})
|
||||
})
|
||||
|
||||
|
@ -325,7 +327,7 @@ describe('nativeImage module', () => {
|
|||
|
||||
const imagePath = path.join(__dirname, 'fixtures', 'assets', 'icon.ico')
|
||||
const image = nativeImage.createFromPath(imagePath)
|
||||
expect(image.isEmpty()).to.be.false
|
||||
expect(image.isEmpty()).to.be.false()
|
||||
expect(image.getSize()).to.deep.equal({width: 256, height: 256})
|
||||
})
|
||||
})
|
||||
|
@ -355,7 +357,7 @@ describe('nativeImage module', () => {
|
|||
}
|
||||
|
||||
const image = nativeImage.createFromNamedImage('NSActionTemplate')
|
||||
expect(image.isEmpty()).to.be.false
|
||||
expect(image.isEmpty()).to.be.false()
|
||||
})
|
||||
|
||||
it('returns allows an HSL shift for a valid image on darwin', function () {
|
||||
|
@ -366,7 +368,7 @@ describe('nativeImage module', () => {
|
|||
}
|
||||
|
||||
const image = nativeImage.createFromNamedImage('NSActionTemplate', [0.5, 0.2, 0.8])
|
||||
expect(image.isEmpty()).to.be.false
|
||||
expect(image.isEmpty()).to.be.false()
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -425,7 +427,7 @@ describe('nativeImage module', () => {
|
|||
const cropB = image.crop({width: 25, height: 64, x: 30, y: 40})
|
||||
expect(cropA.getSize()).to.deep.equal({width: 25, height: 64})
|
||||
expect(cropB.getSize()).to.deep.equal({width: 25, height: 64})
|
||||
expect(cropA.toPNG().equals(cropB.toPNG())).to.be.false
|
||||
expect(cropA.toPNG().equals(cropB.toPNG())).to.be.false()
|
||||
})
|
||||
})
|
||||
|
||||
|
@ -471,7 +473,7 @@ describe('nativeImage module', () => {
|
|||
buffer: 'invalid'
|
||||
})
|
||||
|
||||
expect(image.isEmpty()).to.be.false
|
||||
expect(image.isEmpty()).to.be.false()
|
||||
expect(image.getSize()).to.deep.equal({width: 1, height: 1})
|
||||
|
||||
expect(image.toDataURL({scaleFactor: 1.0})).to.equal(imageDataOne.dataUrl)
|
||||
|
@ -506,7 +508,7 @@ describe('nativeImage module', () => {
|
|||
dataURL: 'invalid'
|
||||
})
|
||||
|
||||
expect(image.isEmpty()).to.be.false
|
||||
expect(image.isEmpty()).to.be.false()
|
||||
expect(image.getSize()).to.deep.equal({width: 1, height: 1})
|
||||
|
||||
expect(image.toDataURL({scaleFactor: 1.0})).to.equal(imageDataOne.dataUrl)
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
const assert = require('assert')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
const http = require('http')
|
||||
const fs = require('fs')
|
||||
const os = require('os')
|
||||
|
@ -10,13 +11,15 @@ const appPath = path.join(__dirname, 'fixtures', 'api', 'net-log')
|
|||
const dumpFile = path.join(os.tmpdir(), 'net_log.json')
|
||||
const dumpFileDynamic = path.join(os.tmpdir(), 'net_log_dynamic.json')
|
||||
|
||||
const {expect} = chai
|
||||
chai.use(dirtyChai)
|
||||
const isCI = remote.getGlobal('isCi')
|
||||
|
||||
describe('netLog module', () => {
|
||||
let server
|
||||
const connections = new Set()
|
||||
|
||||
before((done) => {
|
||||
before(done => {
|
||||
server = http.createServer()
|
||||
server.listen(0, '127.0.0.1', () => {
|
||||
server.url = `http://127.0.0.1:${server.address().port}`
|
||||
|
@ -33,7 +36,7 @@ describe('netLog module', () => {
|
|||
})
|
||||
})
|
||||
|
||||
after((done) => {
|
||||
after(done => {
|
||||
for (const connection of connections) {
|
||||
connection.destroy()
|
||||
}
|
||||
|
@ -52,36 +55,35 @@ describe('netLog module', () => {
|
|||
}
|
||||
})
|
||||
|
||||
it('should begin and end logging to file when .startLogging() and .stopLogging() is called', (done) => {
|
||||
assert(!netLog.currentlyLogging)
|
||||
assert.equal(netLog.currentlyLoggingPath, '')
|
||||
it('should begin and end logging to file when .startLogging() and .stopLogging() is called', done => {
|
||||
expect(netLog.currentlyLogging).to.be.false()
|
||||
expect(netLog.currentlyLoggingPath).to.equal('')
|
||||
|
||||
netLog.startLogging(dumpFileDynamic)
|
||||
|
||||
assert(netLog.currentlyLogging)
|
||||
assert.equal(netLog.currentlyLoggingPath, dumpFileDynamic)
|
||||
expect(netLog.currentlyLogging).to.be.true()
|
||||
expect(netLog.currentlyLoggingPath).to.equal(dumpFileDynamic)
|
||||
|
||||
netLog.stopLogging((path) => {
|
||||
assert(!netLog.currentlyLogging)
|
||||
assert.equal(netLog.currentlyLoggingPath, '')
|
||||
expect(netLog.currentlyLogging).to.be.false()
|
||||
expect(netLog.currentlyLoggingPath).to.equal('')
|
||||
|
||||
assert.equal(path, dumpFileDynamic)
|
||||
|
||||
assert(fs.existsSync(dumpFileDynamic))
|
||||
expect(path).to.equal(dumpFileDynamic)
|
||||
expect(fs.existsSync(dumpFileDynamic)).to.be.true()
|
||||
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should silence when .stopLogging() is called without calling .startLogging()', (done) => {
|
||||
assert(!netLog.currentlyLogging)
|
||||
assert.equal(netLog.currentlyLoggingPath, '')
|
||||
it('should silence when .stopLogging() is called without calling .startLogging()', done => {
|
||||
expect(netLog.currentlyLogging).to.be.false()
|
||||
expect(netLog.currentlyLoggingPath).to.equal('')
|
||||
|
||||
netLog.stopLogging((path) => {
|
||||
assert(!netLog.currentlyLogging)
|
||||
assert.equal(netLog.currentlyLoggingPath, '')
|
||||
netLog.stopLogging(path => {
|
||||
expect(netLog.currentlyLogging).to.be.false()
|
||||
expect(netLog.currentlyLoggingPath).to.equal('')
|
||||
|
||||
assert.equal(path, '')
|
||||
expect(path).to.equal('')
|
||||
|
||||
done()
|
||||
})
|
||||
|
@ -89,7 +91,7 @@ describe('netLog module', () => {
|
|||
|
||||
// The following tests are skipped on Linux CI
|
||||
|
||||
it('should begin and end logging automatically when --log-net-log is passed', (done) => {
|
||||
it('should begin and end logging automatically when --log-net-log is passed', done => {
|
||||
if (isCI && process.platform === 'linux') {
|
||||
done()
|
||||
return
|
||||
|
@ -103,12 +105,12 @@ describe('netLog module', () => {
|
|||
})
|
||||
|
||||
appProcess.once('exit', () => {
|
||||
assert(fs.existsSync(dumpFile))
|
||||
expect(fs.existsSync(dumpFile)).to.be.true()
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should begin and end logging automtically when --log-net-log is passed, and behave correctly when .startLogging() and .stopLogging() is called', (done) => {
|
||||
it('should begin and end logging automtically when --log-net-log is passed, and behave correctly when .startLogging() and .stopLogging() is called', done => {
|
||||
if (isCI && process.platform === 'linux') {
|
||||
done()
|
||||
return
|
||||
|
@ -123,18 +125,18 @@ describe('netLog module', () => {
|
|||
}
|
||||
})
|
||||
|
||||
appProcess.stdout.on('data', (data) => {
|
||||
appProcess.stdout.on('data', data => {
|
||||
console.log(data.toString())
|
||||
})
|
||||
|
||||
appProcess.once('exit', () => {
|
||||
assert(fs.existsSync(dumpFile))
|
||||
assert(fs.existsSync(dumpFileDynamic))
|
||||
expect(fs.existsSync(dumpFile)).to.be.true()
|
||||
expect(fs.existsSync(dumpFileDynamic)).to.be.true()
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should end logging automatically when only .startLogging() is called', (done) => {
|
||||
it('should end logging automatically when only .startLogging() is called', done => {
|
||||
if (isCI && process.platform === 'linux') {
|
||||
done()
|
||||
return
|
||||
|
@ -149,7 +151,7 @@ describe('netLog module', () => {
|
|||
})
|
||||
|
||||
appProcess.once('exit', () => {
|
||||
assert(fs.existsSync(dumpFileDynamic))
|
||||
expect(fs.existsSync(dumpFileDynamic)).to.be.true()
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
//
|
||||
// See https://pypi.python.org/pypi/python-dbusmock to read about dbusmock.
|
||||
|
||||
const assert = require('assert')
|
||||
const {expect} = require('chai')
|
||||
const dbus = require('dbus-native')
|
||||
const Promise = require('bluebird')
|
||||
|
||||
|
@ -27,12 +27,12 @@ const skip = process.platform !== 'linux' ||
|
|||
before(async () => {
|
||||
// init app
|
||||
app.setName(appName)
|
||||
app.setDesktopName(appName + '.desktop')
|
||||
app.setDesktopName(`${appName}.desktop`)
|
||||
// init dbus
|
||||
const path = '/org/freedesktop/Notifications'
|
||||
const iface = 'org.freedesktop.DBus.Mock'
|
||||
const bus = dbus.sessionBus()
|
||||
console.log('session bus: ' + process.env.DBUS_SESSION_BUS_ADDRESS)
|
||||
console.log(`session bus: ${process.env.DBUS_SESSION_BUS_ADDRESS}`)
|
||||
const service = bus.getService(serviceName)
|
||||
const getInterface = Promise.promisify(service.getInterface, {context: service})
|
||||
mock = await getInterface(path, iface)
|
||||
|
@ -48,10 +48,10 @@ const skip = process.platform !== 'linux' ||
|
|||
app.setVersion(realAppVersion)
|
||||
})
|
||||
|
||||
describe('Notification module using ' + serviceName, () => {
|
||||
describe(`Notification module using ${serviceName}`, () => {
|
||||
function onMethodCalled (done) {
|
||||
function cb (name) {
|
||||
console.log('onMethodCalled: ' + name)
|
||||
console.log(`onMethodCalled: ${name}`)
|
||||
if (name === 'Notify') {
|
||||
mock.removeListener('MethodCalled', cb)
|
||||
console.log('done')
|
||||
|
@ -83,7 +83,7 @@ const skip = process.platform !== 'linux' ||
|
|||
}
|
||||
}
|
||||
|
||||
before((done) => {
|
||||
before(done => {
|
||||
mock.on('MethodCalled', onMethodCalled(done))
|
||||
// lazy load Notification after we listen to MethodCalled mock signal
|
||||
Notification = require('electron').remote.Notification
|
||||
|
@ -98,14 +98,16 @@ const skip = process.platform !== 'linux' ||
|
|||
n.show()
|
||||
})
|
||||
|
||||
it('should call ' + serviceName + ' to show notifications', async () => {
|
||||
it(`should call ${serviceName} to show notifications`, async () => {
|
||||
const calls = await getCalls()
|
||||
assert(calls.length >= 1)
|
||||
expect(calls).to.be.an('array').of.lengthOf.at.least(1)
|
||||
|
||||
let lastCall = calls[calls.length - 1]
|
||||
let methodName = lastCall[1]
|
||||
assert.equal(methodName, 'Notify')
|
||||
expect(methodName).to.equal('Notify')
|
||||
|
||||
let args = unmarshalDBusNotifyArgs(lastCall[2])
|
||||
assert.deepEqual(args, {
|
||||
expect(args).to.deep.equal({
|
||||
app_name: appName,
|
||||
replaces_id: 0,
|
||||
app_icon: '',
|
||||
|
|
|
@ -4,12 +4,18 @@ const assert = require('assert')
|
|||
const http = require('http')
|
||||
const path = require('path')
|
||||
const {closeWindow} = require('./window-helpers')
|
||||
const {emittedOnce} = require('./events-helpers')
|
||||
const chai = require('chai')
|
||||
const dirtyChai = require('dirty-chai')
|
||||
|
||||
const {ipcRenderer, remote} = require('electron')
|
||||
const {BrowserWindow, webContents, ipcMain, session} = remote
|
||||
const {expect} = chai
|
||||
|
||||
const isCi = remote.getGlobal('isCi')
|
||||
|
||||
chai.use(dirtyChai)
|
||||
|
||||
/* The whole webContents API doesn't use standard callbacks */
|
||||
/* eslint-disable standard/no-callback-literal */
|
||||
|
||||
|
@ -116,6 +122,21 @@ describe('webContents module', () => {
|
|||
})
|
||||
})
|
||||
|
||||
describe('isCurrentlyAudible() API', () => {
|
||||
it('returns whether audio is playing', async () => {
|
||||
w.loadURL(`file://${path.join(__dirname, 'fixtures', 'api', 'is-currently-audible.html')}`)
|
||||
w.show()
|
||||
await emittedOnce(w.webContents, 'did-finish-load')
|
||||
|
||||
expect(w.webContents.isCurrentlyAudible()).to.be.false()
|
||||
|
||||
w.webContents.send('play')
|
||||
await emittedOnce(ipcMain, 'playing')
|
||||
|
||||
expect(w.webContents.isCurrentlyAudible()).to.be.true()
|
||||
})
|
||||
})
|
||||
|
||||
describe('getWebPreferences() API', () => {
|
||||
it('should not crash when called for devTools webContents', (done) => {
|
||||
w.webContents.openDevTools()
|
||||
|
|
21
spec/fixtures/api/is-currently-audible.html
vendored
Normal file
21
spec/fixtures/api/is-currently-audible.html
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
<html>
|
||||
<body>
|
||||
<div id="video"></div>
|
||||
<script type="text/javascript" charset="utf-8">
|
||||
const {ipcRenderer} = window.top != null ? window.top.require('electron') : require('electron')
|
||||
ipcRenderer.on('play', (event) => {
|
||||
const context = new window.AudioContext();
|
||||
const oscillator = context.createOscillator();
|
||||
|
||||
// A beep
|
||||
oscillator.type = 'sine';
|
||||
oscillator.frequency.value = 440
|
||||
oscillator.connect(context.destination)
|
||||
oscillator.start()
|
||||
|
||||
// It'll take a few ms before the beep shows up
|
||||
setTimeout(() => event.sender.send('playing'), 100)
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
10
spec/fixtures/module/preload-context.js
vendored
Normal file
10
spec/fixtures/module/preload-context.js
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
var test = 'test' // eslint-disable-line
|
||||
|
||||
const types = {
|
||||
require: typeof require,
|
||||
electron: typeof electron,
|
||||
window: typeof window,
|
||||
localVar: typeof window.test
|
||||
}
|
||||
|
||||
console.log(JSON.stringify(types))
|
|
@ -249,6 +249,22 @@ describe('<webview> tag', function () {
|
|||
})
|
||||
})
|
||||
|
||||
it('runs in the correct scope when sandboxed', async () => {
|
||||
const message = await startLoadingWebViewAndWaitForMessage(webview, {
|
||||
preload: `${fixtures}/module/preload-context.js`,
|
||||
src: `file://${fixtures}/api/blank.html`,
|
||||
webpreferences: 'sandbox=yes'
|
||||
})
|
||||
|
||||
const types = JSON.parse(message)
|
||||
expect(types).to.include({
|
||||
require: 'function', // arguments passed to it should be availale
|
||||
electron: 'undefined', // objects from the scope it is called from should not be available
|
||||
window: 'object', // the window object should be available
|
||||
localVar: 'undefined' // but local variables should not be exposed to the window
|
||||
})
|
||||
})
|
||||
|
||||
it('preload script can require modules that still use "process" and "Buffer" when nodeintegration is off', async () => {
|
||||
const message = await startLoadingWebViewAndWaitForMessage(webview, {
|
||||
preload: `${fixtures}/module/preload-node-off-wrapper.js`,
|
||||
|
|
2
vendor/libchromiumcontent
vendored
2
vendor/libchromiumcontent
vendored
|
@ -1 +1 @@
|
|||
Subproject commit 5ad14b556198ba9a5422154aba2f9b7914dc528c
|
||||
Subproject commit a55a9ce536db60702630c4b9d94dcb2145fc3b24
|
Loading…
Reference in a new issue