Merge branch 'support-chromium-sandbox' of https://github.com/tarruda/electron into tarruda-support-chromium-sandbox
This commit is contained in:
commit
458c4dd129
35 changed files with 1055 additions and 100 deletions
|
@ -11,7 +11,9 @@
|
||||||
#include "atom/browser/atom_browser_client.h"
|
#include "atom/browser/atom_browser_client.h"
|
||||||
#include "atom/browser/relauncher.h"
|
#include "atom/browser/relauncher.h"
|
||||||
#include "atom/common/google_api_key.h"
|
#include "atom/common/google_api_key.h"
|
||||||
|
#include "atom/common/options_switches.h"
|
||||||
#include "atom/renderer/atom_renderer_client.h"
|
#include "atom/renderer/atom_renderer_client.h"
|
||||||
|
#include "atom/renderer/atom_sandboxed_renderer_client.h"
|
||||||
#include "atom/utility/atom_content_utility_client.h"
|
#include "atom/utility/atom_content_utility_client.h"
|
||||||
#include "base/command_line.h"
|
#include "base/command_line.h"
|
||||||
#include "base/debug/stack_trace.h"
|
#include "base/debug/stack_trace.h"
|
||||||
|
@ -29,7 +31,7 @@ namespace {
|
||||||
const char* kRelauncherProcess = "relauncher";
|
const char* kRelauncherProcess = "relauncher";
|
||||||
|
|
||||||
bool IsBrowserProcess(base::CommandLine* cmd) {
|
bool IsBrowserProcess(base::CommandLine* cmd) {
|
||||||
std::string process_type = cmd->GetSwitchValueASCII(switches::kProcessType);
|
std::string process_type = cmd->GetSwitchValueASCII(::switches::kProcessType);
|
||||||
return process_type.empty();
|
return process_type.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +74,7 @@ bool AtomMainDelegate::BasicStartupComplete(int* exit_code) {
|
||||||
|
|
||||||
// Only enable logging when --enable-logging is specified.
|
// Only enable logging when --enable-logging is specified.
|
||||||
std::unique_ptr<base::Environment> env(base::Environment::Create());
|
std::unique_ptr<base::Environment> env(base::Environment::Create());
|
||||||
if (!command_line->HasSwitch(switches::kEnableLogging) &&
|
if (!command_line->HasSwitch(::switches::kEnableLogging) &&
|
||||||
!env->HasVar("ELECTRON_ENABLE_LOGGING")) {
|
!env->HasVar("ELECTRON_ENABLE_LOGGING")) {
|
||||||
settings.logging_dest = logging::LOG_NONE;
|
settings.logging_dest = logging::LOG_NONE;
|
||||||
logging::SetMinLogLevel(logging::LOG_NUM_SEVERITIES);
|
logging::SetMinLogLevel(logging::LOG_NUM_SEVERITIES);
|
||||||
|
@ -115,17 +117,23 @@ void AtomMainDelegate::PreSandboxStartup() {
|
||||||
|
|
||||||
auto command_line = base::CommandLine::ForCurrentProcess();
|
auto command_line = base::CommandLine::ForCurrentProcess();
|
||||||
std::string process_type = command_line->GetSwitchValueASCII(
|
std::string process_type = command_line->GetSwitchValueASCII(
|
||||||
switches::kProcessType);
|
::switches::kProcessType);
|
||||||
|
|
||||||
// Only append arguments for browser process.
|
// Only append arguments for browser process.
|
||||||
if (!IsBrowserProcess(command_line))
|
if (!IsBrowserProcess(command_line))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Disable renderer sandbox for most of node's functions.
|
if (command_line->HasSwitch(switches::kEnableSandbox)) {
|
||||||
command_line->AppendSwitch(switches::kNoSandbox);
|
// Disable setuid sandbox since it is not longer required on linux(namespace
|
||||||
|
// sandbox is available on most distros).
|
||||||
|
command_line->AppendSwitch(::switches::kDisableSetuidSandbox);
|
||||||
|
} else {
|
||||||
|
// Disable renderer sandbox for most of node's functions.
|
||||||
|
command_line->AppendSwitch(::switches::kNoSandbox);
|
||||||
|
}
|
||||||
|
|
||||||
// Allow file:// URIs to read other file:// URIs by default.
|
// Allow file:// URIs to read other file:// URIs by default.
|
||||||
command_line->AppendSwitch(switches::kAllowFileAccessFromFiles);
|
command_line->AppendSwitch(::switches::kAllowFileAccessFromFiles);
|
||||||
|
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
// Enable AVFoundation.
|
// Enable AVFoundation.
|
||||||
|
@ -140,7 +148,13 @@ content::ContentBrowserClient* AtomMainDelegate::CreateContentBrowserClient() {
|
||||||
|
|
||||||
content::ContentRendererClient*
|
content::ContentRendererClient*
|
||||||
AtomMainDelegate::CreateContentRendererClient() {
|
AtomMainDelegate::CreateContentRendererClient() {
|
||||||
renderer_client_.reset(new AtomRendererClient);
|
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
|
||||||
|
switches::kEnableSandbox)) {
|
||||||
|
renderer_client_.reset(new AtomSandboxedRendererClient);
|
||||||
|
} else {
|
||||||
|
renderer_client_.reset(new AtomRendererClient);
|
||||||
|
}
|
||||||
|
|
||||||
return renderer_client_.get();
|
return renderer_client_.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -258,17 +258,25 @@ void OnCapturePageDone(base::Callback<void(const gfx::Image&)> callback,
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
WebContents::WebContents(v8::Isolate* isolate,
|
WebContents::WebContents(v8::Isolate* isolate,
|
||||||
content::WebContents* web_contents)
|
content::WebContents* web_contents,
|
||||||
|
Type type)
|
||||||
: content::WebContentsObserver(web_contents),
|
: content::WebContentsObserver(web_contents),
|
||||||
embedder_(nullptr),
|
embedder_(nullptr),
|
||||||
type_(REMOTE),
|
type_(type),
|
||||||
request_id_(0),
|
request_id_(0),
|
||||||
background_throttling_(true),
|
background_throttling_(true),
|
||||||
enable_devtools_(true) {
|
enable_devtools_(true) {
|
||||||
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
|
|
||||||
|
|
||||||
Init(isolate);
|
if (type == REMOTE) {
|
||||||
AttachAsUserData(web_contents);
|
web_contents->SetUserAgentOverride(GetBrowserContext()->GetUserAgent());
|
||||||
|
Init(isolate);
|
||||||
|
AttachAsUserData(web_contents);
|
||||||
|
} else {
|
||||||
|
const mate::Dictionary options = mate::Dictionary::CreateEmpty(isolate);
|
||||||
|
auto session = Session::CreateFrom(isolate, GetBrowserContext());
|
||||||
|
session_.Reset(isolate, session.ToV8());
|
||||||
|
InitWithSessionAndOptions(isolate, web_contents, session, options);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
WebContents::WebContents(v8::Isolate* isolate,
|
WebContents::WebContents(v8::Isolate* isolate,
|
||||||
|
@ -336,6 +344,13 @@ WebContents::WebContents(v8::Isolate* isolate,
|
||||||
web_contents = content::WebContents::Create(params);
|
web_contents = content::WebContents::Create(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InitWithSessionAndOptions(isolate, web_contents, session, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebContents::InitWithSessionAndOptions(v8::Isolate* isolate,
|
||||||
|
content::WebContents *web_contents,
|
||||||
|
mate::Handle<api::Session> session,
|
||||||
|
const mate::Dictionary& options) {
|
||||||
Observe(web_contents);
|
Observe(web_contents);
|
||||||
InitWithWebContents(web_contents, session->browser_context());
|
InitWithWebContents(web_contents, session->browser_context());
|
||||||
|
|
||||||
|
@ -408,6 +423,31 @@ void WebContents::OnCreateWindow(const GURL& target_url,
|
||||||
Emit("new-window", target_url, frame_name, disposition);
|
Emit("new-window", target_url, frame_name, disposition);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WebContents::WebContentsCreated(content::WebContents* source_contents,
|
||||||
|
int opener_render_frame_id,
|
||||||
|
const std::string& frame_name,
|
||||||
|
const GURL& target_url,
|
||||||
|
content::WebContents* new_contents) {
|
||||||
|
v8::Locker locker(isolate());
|
||||||
|
v8::HandleScope handle_scope(isolate());
|
||||||
|
auto api_web_contents = CreateFrom(isolate(), new_contents, BROWSER_WINDOW);
|
||||||
|
Emit("-web-contents-created", api_web_contents, target_url, frame_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WebContents::AddNewContents(content::WebContents* source,
|
||||||
|
content::WebContents* new_contents,
|
||||||
|
WindowOpenDisposition disposition,
|
||||||
|
const gfx::Rect& initial_rect,
|
||||||
|
bool user_gesture,
|
||||||
|
bool* was_blocked) {
|
||||||
|
v8::Locker locker(isolate());
|
||||||
|
v8::HandleScope handle_scope(isolate());
|
||||||
|
auto api_web_contents = CreateFrom(isolate(), new_contents);
|
||||||
|
Emit("-add-new-contents", api_web_contents, disposition, user_gesture,
|
||||||
|
initial_rect.x(), initial_rect.y(), initial_rect.width(),
|
||||||
|
initial_rect.height());
|
||||||
|
}
|
||||||
|
|
||||||
content::WebContents* WebContents::OpenURLFromTab(
|
content::WebContents* WebContents::OpenURLFromTab(
|
||||||
content::WebContents* source,
|
content::WebContents* source,
|
||||||
const content::OpenURLParams& params) {
|
const content::OpenURLParams& params) {
|
||||||
|
@ -801,7 +841,14 @@ void WebContents::NavigationEntryCommitted(
|
||||||
details.is_in_page, details.did_replace_entry);
|
details.is_in_page, details.did_replace_entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
int WebContents::GetID() const {
|
int64_t WebContents::GetID() const {
|
||||||
|
int64_t process_id = web_contents()->GetRenderProcessHost()->GetID();
|
||||||
|
int64_t routing_id = web_contents()->GetRoutingID();
|
||||||
|
int64_t rv = (process_id << 32) + routing_id;
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
int WebContents::GetProcessID() const {
|
||||||
return web_contents()->GetRenderProcessHost()->GetID();
|
return web_contents()->GetRenderProcessHost()->GetID();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1496,6 +1543,7 @@ void WebContents::BuildPrototype(v8::Isolate* isolate,
|
||||||
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
|
mate::ObjectTemplateBuilder(isolate, prototype->PrototypeTemplate())
|
||||||
.MakeDestroyable()
|
.MakeDestroyable()
|
||||||
.SetMethod("getId", &WebContents::GetID)
|
.SetMethod("getId", &WebContents::GetID)
|
||||||
|
.SetMethod("getProcessId", &WebContents::GetProcessID)
|
||||||
.SetMethod("equal", &WebContents::Equal)
|
.SetMethod("equal", &WebContents::Equal)
|
||||||
.SetMethod("_loadURL", &WebContents::LoadURL)
|
.SetMethod("_loadURL", &WebContents::LoadURL)
|
||||||
.SetMethod("downloadURL", &WebContents::DownloadURL)
|
.SetMethod("downloadURL", &WebContents::DownloadURL)
|
||||||
|
@ -1605,7 +1653,15 @@ mate::Handle<WebContents> WebContents::CreateFrom(
|
||||||
return mate::CreateHandle(isolate, static_cast<WebContents*>(existing));
|
return mate::CreateHandle(isolate, static_cast<WebContents*>(existing));
|
||||||
|
|
||||||
// Otherwise create a new WebContents wrapper object.
|
// Otherwise create a new WebContents wrapper object.
|
||||||
return mate::CreateHandle(isolate, new WebContents(isolate, web_contents));
|
return mate::CreateHandle(isolate, new WebContents(isolate, web_contents,
|
||||||
|
REMOTE));
|
||||||
|
}
|
||||||
|
|
||||||
|
mate::Handle<WebContents> WebContents::CreateFrom(
|
||||||
|
v8::Isolate* isolate, content::WebContents* web_contents, Type type) {
|
||||||
|
// Otherwise create a new WebContents wrapper object.
|
||||||
|
return mate::CreateHandle(isolate, new WebContents(isolate, web_contents,
|
||||||
|
type));
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|
|
@ -58,6 +58,8 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||||
// Create from an existing WebContents.
|
// Create from an existing WebContents.
|
||||||
static mate::Handle<WebContents> CreateFrom(
|
static mate::Handle<WebContents> CreateFrom(
|
||||||
v8::Isolate* isolate, content::WebContents* web_contents);
|
v8::Isolate* isolate, content::WebContents* web_contents);
|
||||||
|
static mate::Handle<WebContents> CreateFrom(
|
||||||
|
v8::Isolate* isolate, content::WebContents* web_contents, Type type);
|
||||||
|
|
||||||
// Create a new WebContents.
|
// Create a new WebContents.
|
||||||
static mate::Handle<WebContents> Create(
|
static mate::Handle<WebContents> Create(
|
||||||
|
@ -66,7 +68,8 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||||
static void BuildPrototype(v8::Isolate* isolate,
|
static void BuildPrototype(v8::Isolate* isolate,
|
||||||
v8::Local<v8::FunctionTemplate> prototype);
|
v8::Local<v8::FunctionTemplate> prototype);
|
||||||
|
|
||||||
int GetID() const;
|
int64_t GetID() const;
|
||||||
|
int GetProcessID() const;
|
||||||
Type GetType() const;
|
Type GetType() const;
|
||||||
bool Equal(const WebContents* web_contents) const;
|
bool Equal(const WebContents* web_contents) const;
|
||||||
void LoadURL(const GURL& url, const mate::Dictionary& options);
|
void LoadURL(const GURL& url, const mate::Dictionary& options);
|
||||||
|
@ -191,16 +194,34 @@ class WebContents : public mate::TrackableObject<WebContents>,
|
||||||
v8::Local<v8::Value> Debugger(v8::Isolate* isolate);
|
v8::Local<v8::Value> Debugger(v8::Isolate* isolate);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
WebContents(v8::Isolate* isolate, content::WebContents* web_contents);
|
WebContents(v8::Isolate* isolate,
|
||||||
|
content::WebContents* web_contents,
|
||||||
|
Type type);
|
||||||
WebContents(v8::Isolate* isolate, const mate::Dictionary& options);
|
WebContents(v8::Isolate* isolate, const mate::Dictionary& options);
|
||||||
~WebContents();
|
~WebContents();
|
||||||
|
|
||||||
|
void InitWithSessionAndOptions(v8::Isolate* isolate,
|
||||||
|
content::WebContents *web_contents,
|
||||||
|
mate::Handle<class Session> session,
|
||||||
|
const mate::Dictionary& options);
|
||||||
|
|
||||||
// content::WebContentsDelegate:
|
// content::WebContentsDelegate:
|
||||||
bool AddMessageToConsole(content::WebContents* source,
|
bool AddMessageToConsole(content::WebContents* source,
|
||||||
int32_t level,
|
int32_t level,
|
||||||
const base::string16& message,
|
const base::string16& message,
|
||||||
int32_t line_no,
|
int32_t line_no,
|
||||||
const base::string16& source_id) override;
|
const base::string16& source_id) override;
|
||||||
|
void WebContentsCreated(content::WebContents* source_contents,
|
||||||
|
int opener_render_frame_id,
|
||||||
|
const std::string& frame_name,
|
||||||
|
const GURL& target_url,
|
||||||
|
content::WebContents* new_contents) override;
|
||||||
|
void AddNewContents(content::WebContents* source,
|
||||||
|
content::WebContents* new_contents,
|
||||||
|
WindowOpenDisposition disposition,
|
||||||
|
const gfx::Rect& initial_rect,
|
||||||
|
bool user_gesture,
|
||||||
|
bool* was_blocked) override;
|
||||||
content::WebContents* OpenURLFromTab(
|
content::WebContents* OpenURLFromTab(
|
||||||
content::WebContents* source,
|
content::WebContents* source,
|
||||||
const content::OpenURLParams& params) override;
|
const content::OpenURLParams& params) override;
|
||||||
|
|
|
@ -72,20 +72,33 @@ v8::Local<v8::Value> ToBuffer(v8::Isolate* isolate, void* val, int size) {
|
||||||
|
|
||||||
Window::Window(v8::Isolate* isolate, v8::Local<v8::Object> wrapper,
|
Window::Window(v8::Isolate* isolate, v8::Local<v8::Object> wrapper,
|
||||||
const mate::Dictionary& options) {
|
const mate::Dictionary& options) {
|
||||||
// Use options.webPreferences to create WebContents.
|
mate::Handle<class WebContents> web_contents;
|
||||||
mate::Dictionary web_preferences = mate::Dictionary::CreateEmpty(isolate);
|
// If no WebContents was passed to the constructor, create it from options.
|
||||||
options.Get(options::kWebPreferences, &web_preferences);
|
if (!options.Get("webContents", &web_contents)) {
|
||||||
|
// Use options.webPreferences to create WebContents.
|
||||||
|
mate::Dictionary web_preferences = mate::Dictionary::CreateEmpty(isolate);
|
||||||
|
options.Get(options::kWebPreferences, &web_preferences);
|
||||||
|
|
||||||
// Copy the backgroundColor to webContents.
|
// Copy the backgroundColor to webContents.
|
||||||
v8::Local<v8::Value> value;
|
v8::Local<v8::Value> value;
|
||||||
if (options.Get(options::kBackgroundColor, &value))
|
if (options.Get(options::kBackgroundColor, &value))
|
||||||
web_preferences.Set(options::kBackgroundColor, value);
|
web_preferences.Set(options::kBackgroundColor, value);
|
||||||
|
|
||||||
v8::Local<v8::Value> transparent;
|
v8::Local<v8::Value> transparent;
|
||||||
if (options.Get("transparent", &transparent))
|
if (options.Get("transparent", &transparent))
|
||||||
web_preferences.Set("transparent", transparent);
|
web_preferences.Set("transparent", transparent);
|
||||||
// Creates the WebContents used by BrowserWindow.
|
|
||||||
auto web_contents = WebContents::Create(isolate, web_preferences);
|
// Creates the WebContents used by BrowserWindow.
|
||||||
|
web_contents = WebContents::Create(isolate, web_preferences);
|
||||||
|
}
|
||||||
|
|
||||||
|
Init(isolate, wrapper, options, web_contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Window::Init(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Object> wrapper,
|
||||||
|
const mate::Dictionary& options,
|
||||||
|
mate::Handle<class WebContents> web_contents) {
|
||||||
web_contents_.Reset(isolate, web_contents.ToV8());
|
web_contents_.Reset(isolate, web_contents.ToV8());
|
||||||
api_web_contents_ = web_contents.get();
|
api_web_contents_ = web_contents.get();
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,10 @@ class Window : public mate::TrackableObject<Window>,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void Init(v8::Isolate* isolate,
|
||||||
|
v8::Local<v8::Object> wrapper,
|
||||||
|
const mate::Dictionary& options,
|
||||||
|
mate::Handle<class WebContents> web_contents);
|
||||||
// APIs for NativeWindow.
|
// APIs for NativeWindow.
|
||||||
void Close();
|
void Close();
|
||||||
void Focus();
|
void Focus();
|
||||||
|
|
|
@ -99,6 +99,44 @@ content::WebContents* AtomBrowserClient::GetWebContentsFromProcessID(
|
||||||
return WebContentsPreferences::GetWebContentsFromProcessID(process_id);
|
return WebContentsPreferences::GetWebContentsFromProcessID(process_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AtomBrowserClient::ShouldCreateNewSiteInstance(
|
||||||
|
content::BrowserContext* browser_context,
|
||||||
|
content::SiteInstance* current_instance,
|
||||||
|
const GURL& url) {
|
||||||
|
|
||||||
|
if (url.SchemeIs(url::kJavaScriptScheme))
|
||||||
|
// "javacript:" scheme should always use same SiteInstance
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!IsRendererSandboxed(current_instance->GetProcess()->GetID()))
|
||||||
|
// non-sandboxed renderers should always create a new SiteInstance
|
||||||
|
return true;
|
||||||
|
|
||||||
|
// Create new a SiteInstance if navigating to a different site.
|
||||||
|
auto src_url = current_instance->GetSiteURL();
|
||||||
|
return
|
||||||
|
!content::SiteInstance::IsSameWebSite(browser_context, src_url, url) &&
|
||||||
|
// `IsSameWebSite` doesn't seem to work for some URIs such as `file:`,
|
||||||
|
// handle these scenarios by comparing only the site as defined by
|
||||||
|
// `GetSiteForURL`.
|
||||||
|
content::SiteInstance::GetSiteForURL(browser_context, url) != src_url;
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomBrowserClient::AddSandboxedRendererId(int process_id) {
|
||||||
|
base::AutoLock auto_lock(sandboxed_renderers_lock_);
|
||||||
|
sandboxed_renderers_.insert(process_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomBrowserClient::RemoveSandboxedRendererId(int process_id) {
|
||||||
|
base::AutoLock auto_lock(sandboxed_renderers_lock_);
|
||||||
|
sandboxed_renderers_.erase(process_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AtomBrowserClient::IsRendererSandboxed(int process_id) {
|
||||||
|
base::AutoLock auto_lock(sandboxed_renderers_lock_);
|
||||||
|
return sandboxed_renderers_.count(process_id);
|
||||||
|
}
|
||||||
|
|
||||||
void AtomBrowserClient::RenderProcessWillLaunch(
|
void AtomBrowserClient::RenderProcessWillLaunch(
|
||||||
content::RenderProcessHost* host) {
|
content::RenderProcessHost* host) {
|
||||||
int process_id = host->GetID();
|
int process_id = host->GetID();
|
||||||
|
@ -106,6 +144,13 @@ void AtomBrowserClient::RenderProcessWillLaunch(
|
||||||
host->AddFilter(new TtsMessageFilter(process_id, host->GetBrowserContext()));
|
host->AddFilter(new TtsMessageFilter(process_id, host->GetBrowserContext()));
|
||||||
host->AddFilter(
|
host->AddFilter(
|
||||||
new WidevineCdmMessageFilter(process_id, host->GetBrowserContext()));
|
new WidevineCdmMessageFilter(process_id, host->GetBrowserContext()));
|
||||||
|
|
||||||
|
content::WebContents* web_contents = GetWebContentsFromProcessID(process_id);
|
||||||
|
if (WebContentsPreferences::IsSandboxed(web_contents)) {
|
||||||
|
AddSandboxedRendererId(host->GetID());
|
||||||
|
// ensure the sandboxed renderer id is removed later
|
||||||
|
host->AddObserver(this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
content::SpeechRecognitionManagerDelegate*
|
content::SpeechRecognitionManagerDelegate*
|
||||||
|
@ -155,8 +200,7 @@ void AtomBrowserClient::OverrideSiteInstanceForNavigation(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart renderer process for all navigations except "javacript:" scheme.
|
if (!ShouldCreateNewSiteInstance(browser_context, current_instance, url))
|
||||||
if (url.SchemeIs(url::kJavaScriptScheme))
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
scoped_refptr<content::SiteInstance> site_instance =
|
scoped_refptr<content::SiteInstance> site_instance =
|
||||||
|
@ -188,6 +232,7 @@ void AtomBrowserClient::AppendExtraCommandLineSwitches(
|
||||||
// Copy following switches to child process.
|
// Copy following switches to child process.
|
||||||
static const char* const kCommonSwitchNames[] = {
|
static const char* const kCommonSwitchNames[] = {
|
||||||
switches::kStandardSchemes,
|
switches::kStandardSchemes,
|
||||||
|
switches::kEnableSandbox
|
||||||
};
|
};
|
||||||
command_line->CopySwitchesFrom(
|
command_line->CopySwitchesFrom(
|
||||||
*base::CommandLine::ForCurrentProcess(),
|
*base::CommandLine::ForCurrentProcess(),
|
||||||
|
@ -281,6 +326,11 @@ bool AtomBrowserClient::CanCreateWindow(
|
||||||
bool* no_javascript_access) {
|
bool* no_javascript_access) {
|
||||||
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
||||||
|
|
||||||
|
if (IsRendererSandboxed(render_process_id)) {
|
||||||
|
*no_javascript_access = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (delegate_) {
|
if (delegate_) {
|
||||||
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
|
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
|
||||||
base::Bind(&api::App::OnCreateWindow,
|
base::Bind(&api::App::OnCreateWindow,
|
||||||
|
@ -338,6 +388,7 @@ void AtomBrowserClient::RenderProcessHostDestroyed(
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
RemoveSandboxedRendererId(process_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace atom
|
} // namespace atom
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
#define ATOM_BROWSER_ATOM_BROWSER_CLIENT_H_
|
#define ATOM_BROWSER_ATOM_BROWSER_CLIENT_H_
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <set>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -108,8 +109,19 @@ class AtomBrowserClient : public brightray::BrowserClient,
|
||||||
void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;
|
void RenderProcessHostDestroyed(content::RenderProcessHost* host) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool ShouldCreateNewSiteInstance(content::BrowserContext* browser_context,
|
||||||
|
content::SiteInstance* current_instance,
|
||||||
|
const GURL& dest_url);
|
||||||
|
// Add/remove a process id to `sandboxed_renderers_`.
|
||||||
|
void AddSandboxedRendererId(int process_id);
|
||||||
|
void RemoveSandboxedRendererId(int process_id);
|
||||||
|
bool IsRendererSandboxed(int process_id);
|
||||||
|
|
||||||
// pending_render_process => current_render_process.
|
// pending_render_process => current_render_process.
|
||||||
std::map<int, int> pending_processes_;
|
std::map<int, int> pending_processes_;
|
||||||
|
// Set that contains the process ids of all sandboxed renderers
|
||||||
|
std::set<int> sandboxed_renderers_;
|
||||||
|
base::Lock sandboxed_renderers_lock_;
|
||||||
|
|
||||||
std::unique_ptr<AtomResourceDispatcherHostDelegate>
|
std::unique_ptr<AtomResourceDispatcherHostDelegate>
|
||||||
resource_dispatcher_host_delegate_;
|
resource_dispatcher_host_delegate_;
|
||||||
|
|
|
@ -391,6 +391,10 @@ void NativeWindow::RequestToClosePage() {
|
||||||
if (window_unresposive_closure_.IsCancelled())
|
if (window_unresposive_closure_.IsCancelled())
|
||||||
ScheduleUnresponsiveEvent(5000);
|
ScheduleUnresponsiveEvent(5000);
|
||||||
|
|
||||||
|
if (!web_contents())
|
||||||
|
// Already closed by renderer
|
||||||
|
return;
|
||||||
|
|
||||||
if (web_contents()->NeedToFireBeforeUnload())
|
if (web_contents()->NeedToFireBeforeUnload())
|
||||||
web_contents()->DispatchBeforeUnload();
|
web_contents()->DispatchBeforeUnload();
|
||||||
else
|
else
|
||||||
|
|
|
@ -97,6 +97,12 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches(
|
||||||
command_line->AppendSwitchASCII(switches::kNodeIntegration,
|
command_line->AppendSwitchASCII(switches::kNodeIntegration,
|
||||||
node_integration ? "true" : "false");
|
node_integration ? "true" : "false");
|
||||||
|
|
||||||
|
// If the `sandbox` option was passed to the BrowserWindow's webPreferences,
|
||||||
|
// pass `--enable-sandbox` to the renderer so it won't have any node.js
|
||||||
|
// integration.
|
||||||
|
if (IsSandboxed(web_contents))
|
||||||
|
command_line->AppendSwitch(switches::kEnableSandbox);
|
||||||
|
|
||||||
// The preload script.
|
// The preload script.
|
||||||
base::FilePath::StringType preload;
|
base::FilePath::StringType preload;
|
||||||
if (web_preferences.GetString(options::kPreloadScript, &preload)) {
|
if (web_preferences.GetString(options::kPreloadScript, &preload)) {
|
||||||
|
@ -194,6 +200,21 @@ void WebContentsPreferences::AppendExtraCommandLineSwitches(
|
||||||
command_line->AppendSwitch(cc::switches::kEnableBeginFrameScheduling);
|
command_line->AppendSwitch(cc::switches::kEnableBeginFrameScheduling);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WebContentsPreferences::IsSandboxed(content::WebContents* web_contents) {
|
||||||
|
WebContentsPreferences* self;
|
||||||
|
if (!web_contents)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
self = FromWebContents(web_contents);
|
||||||
|
if (!self)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
base::DictionaryValue& web_preferences = self->web_preferences_;
|
||||||
|
bool sandboxed = false;
|
||||||
|
web_preferences.GetBoolean("sandbox", &sandboxed);
|
||||||
|
return sandboxed;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
void WebContentsPreferences::OverrideWebkitPrefs(
|
void WebContentsPreferences::OverrideWebkitPrefs(
|
||||||
content::WebContents* web_contents, content::WebPreferences* prefs) {
|
content::WebContents* web_contents, content::WebPreferences* prefs) {
|
||||||
|
|
|
@ -36,6 +36,8 @@ class WebContentsPreferences
|
||||||
static void AppendExtraCommandLineSwitches(
|
static void AppendExtraCommandLineSwitches(
|
||||||
content::WebContents* web_contents, base::CommandLine* command_line);
|
content::WebContents* web_contents, base::CommandLine* command_line);
|
||||||
|
|
||||||
|
static bool IsSandboxed(content::WebContents* web_contents);
|
||||||
|
|
||||||
// Modify the WebPreferences according to |web_contents|'s preferences.
|
// Modify the WebPreferences according to |web_contents|'s preferences.
|
||||||
static void OverrideWebkitPrefs(
|
static void OverrideWebkitPrefs(
|
||||||
content::WebContents* web_contents, content::WebPreferences* prefs);
|
content::WebContents* web_contents, content::WebPreferences* prefs);
|
||||||
|
|
|
@ -104,7 +104,7 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||||
dict.SetMethod("setRemoteObjectFreer", &atom::RemoteObjectFreer::BindTo);
|
dict.SetMethod("setRemoteObjectFreer", &atom::RemoteObjectFreer::BindTo);
|
||||||
dict.SetMethod("createIDWeakMap", &atom::api::KeyWeakMap<int32_t>::Create);
|
dict.SetMethod("createIDWeakMap", &atom::api::KeyWeakMap<int32_t>::Create);
|
||||||
dict.SetMethod("createDoubleIDWeakMap",
|
dict.SetMethod("createDoubleIDWeakMap",
|
||||||
&atom::api::KeyWeakMap<std::pair<int32_t, int32_t>>::Create);
|
&atom::api::KeyWeakMap<std::pair<int64_t, int32_t>>::Create);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -117,6 +117,9 @@ const char kDisableBlinkFeatures[] = "disableBlinkFeatures";
|
||||||
|
|
||||||
namespace switches {
|
namespace switches {
|
||||||
|
|
||||||
|
// Enable chromium sandbox.
|
||||||
|
const char kEnableSandbox[] = "enable-sandbox";
|
||||||
|
|
||||||
// Enable plugins.
|
// Enable plugins.
|
||||||
const char kEnablePlugins[] = "enable-plugins";
|
const char kEnablePlugins[] = "enable-plugins";
|
||||||
|
|
||||||
|
|
|
@ -67,6 +67,7 @@ extern const char kDisableBlinkFeatures[];
|
||||||
|
|
||||||
namespace switches {
|
namespace switches {
|
||||||
|
|
||||||
|
extern const char kEnableSandbox[];
|
||||||
extern const char kEnablePlugins[];
|
extern const char kEnablePlugins[];
|
||||||
extern const char kPpapiFlashPath[];
|
extern const char kPpapiFlashPath[];
|
||||||
extern const char kPpapiFlashVersion[];
|
extern const char kPpapiFlashVersion[];
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// Use of this source code is governed by the MIT license that can be
|
// Use of this source code is governed by the MIT license that can be
|
||||||
// found in the LICENSE file.
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "atom/renderer/api/atom_api_renderer_ipc.h"
|
||||||
#include "atom/common/api/api_messages.h"
|
#include "atom/common/api/api_messages.h"
|
||||||
#include "atom/common/native_mate_converters/string16_converter.h"
|
#include "atom/common/native_mate_converters/string16_converter.h"
|
||||||
#include "atom/common/native_mate_converters/value_converter.h"
|
#include "atom/common/native_mate_converters/value_converter.h"
|
||||||
|
@ -15,7 +16,9 @@ using content::RenderView;
|
||||||
using blink::WebLocalFrame;
|
using blink::WebLocalFrame;
|
||||||
using blink::WebView;
|
using blink::WebView;
|
||||||
|
|
||||||
namespace {
|
namespace atom {
|
||||||
|
|
||||||
|
namespace api {
|
||||||
|
|
||||||
RenderView* GetCurrentRenderView() {
|
RenderView* GetCurrentRenderView() {
|
||||||
WebLocalFrame* frame = WebLocalFrame::frameForCurrentContext();
|
WebLocalFrame* frame = WebLocalFrame::frameForCurrentContext();
|
||||||
|
@ -69,6 +72,8 @@ void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||||
dict.SetMethod("sendSync", &SendSync);
|
dict.SetMethod("sendSync", &SendSync);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace api
|
||||||
|
|
||||||
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_renderer_ipc, Initialize)
|
} // namespace atom
|
||||||
|
|
||||||
|
NODE_MODULE_CONTEXT_AWARE_BUILTIN(atom_renderer_ipc, atom::api::Initialize)
|
||||||
|
|
30
atom/renderer/api/atom_api_renderer_ipc.h
Normal file
30
atom/renderer/api/atom_api_renderer_ipc.h
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Copyright (c) 2016 GitHub, Inc.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef ATOM_RENDERER_API_ATOM_API_RENDERER_IPC_H_
|
||||||
|
#define ATOM_RENDERER_API_ATOM_API_RENDERER_IPC_H_
|
||||||
|
|
||||||
|
#include "base/values.h"
|
||||||
|
#include "native_mate/arguments.h"
|
||||||
|
|
||||||
|
namespace atom {
|
||||||
|
|
||||||
|
namespace api {
|
||||||
|
|
||||||
|
void Send(mate::Arguments* args,
|
||||||
|
const base::string16& channel,
|
||||||
|
const base::ListValue& arguments);
|
||||||
|
|
||||||
|
base::string16 SendSync(mate::Arguments* args,
|
||||||
|
const base::string16& channel,
|
||||||
|
const base::ListValue& arguments);
|
||||||
|
|
||||||
|
void Initialize(v8::Local<v8::Object> exports, v8::Local<v8::Value> unused,
|
||||||
|
v8::Local<v8::Context> context, void* priv);
|
||||||
|
|
||||||
|
} // namespace api
|
||||||
|
|
||||||
|
} // namespace atom
|
||||||
|
|
||||||
|
#endif // ATOM_RENDERER_API_ATOM_API_RENDERER_IPC_H_
|
|
@ -59,9 +59,33 @@ std::vector<v8::Local<v8::Value>> ListValueToVector(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EmitIPCEvent(blink::WebFrame* frame,
|
base::StringPiece NetResourceProvider(int key) {
|
||||||
const base::string16& channel,
|
if (key == IDR_DIR_HEADER_HTML) {
|
||||||
const base::ListValue& args) {
|
base::StringPiece html_data =
|
||||||
|
ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
|
||||||
|
IDR_DIR_HEADER_HTML);
|
||||||
|
return html_data;
|
||||||
|
}
|
||||||
|
return base::StringPiece();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
AtomRenderViewObserver::AtomRenderViewObserver(
|
||||||
|
content::RenderView* render_view,
|
||||||
|
AtomRendererClient* renderer_client)
|
||||||
|
: content::RenderViewObserver(render_view),
|
||||||
|
document_created_(false) {
|
||||||
|
// Initialise resource for directory listing.
|
||||||
|
net::NetModule::SetResourceProvider(NetResourceProvider);
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomRenderViewObserver::~AtomRenderViewObserver() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomRenderViewObserver::EmitIPCEvent(blink::WebFrame* frame,
|
||||||
|
const base::string16& channel,
|
||||||
|
const base::ListValue& args) {
|
||||||
if (!frame || frame->isWebRemoteFrame())
|
if (!frame || frame->isWebRemoteFrame())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -87,30 +111,6 @@ void EmitIPCEvent(blink::WebFrame* frame,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
base::StringPiece NetResourceProvider(int key) {
|
|
||||||
if (key == IDR_DIR_HEADER_HTML) {
|
|
||||||
base::StringPiece html_data =
|
|
||||||
ui::ResourceBundle::GetSharedInstance().GetRawDataResource(
|
|
||||||
IDR_DIR_HEADER_HTML);
|
|
||||||
return html_data;
|
|
||||||
}
|
|
||||||
return base::StringPiece();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
AtomRenderViewObserver::AtomRenderViewObserver(
|
|
||||||
content::RenderView* render_view,
|
|
||||||
AtomRendererClient* renderer_client)
|
|
||||||
: content::RenderViewObserver(render_view),
|
|
||||||
document_created_(false) {
|
|
||||||
// Initialise resource for directory listing.
|
|
||||||
net::NetModule::SetResourceProvider(NetResourceProvider);
|
|
||||||
}
|
|
||||||
|
|
||||||
AtomRenderViewObserver::~AtomRenderViewObserver() {
|
|
||||||
}
|
|
||||||
|
|
||||||
void AtomRenderViewObserver::DidCreateDocumentElement(
|
void AtomRenderViewObserver::DidCreateDocumentElement(
|
||||||
blink::WebLocalFrame* frame) {
|
blink::WebLocalFrame* frame) {
|
||||||
document_created_ = true;
|
document_created_ = true;
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "base/strings/string16.h"
|
#include "base/strings/string16.h"
|
||||||
#include "content/public/renderer/render_view_observer.h"
|
#include "content/public/renderer/render_view_observer.h"
|
||||||
|
#include "third_party/WebKit/public/web/WebFrame.h"
|
||||||
|
|
||||||
namespace base {
|
namespace base {
|
||||||
class ListValue;
|
class ListValue;
|
||||||
|
@ -24,6 +25,10 @@ class AtomRenderViewObserver : public content::RenderViewObserver {
|
||||||
protected:
|
protected:
|
||||||
virtual ~AtomRenderViewObserver();
|
virtual ~AtomRenderViewObserver();
|
||||||
|
|
||||||
|
virtual void EmitIPCEvent(blink::WebFrame* frame,
|
||||||
|
const base::string16& channel,
|
||||||
|
const base::ListValue& args);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// content::RenderViewObserver implementation.
|
// content::RenderViewObserver implementation.
|
||||||
void DidCreateDocumentElement(blink::WebLocalFrame* frame) override;
|
void DidCreateDocumentElement(blink::WebLocalFrame* frame) override;
|
||||||
|
|
201
atom/renderer/atom_sandboxed_renderer_client.cc
Normal file
201
atom/renderer/atom_sandboxed_renderer_client.cc
Normal file
|
@ -0,0 +1,201 @@
|
||||||
|
// Copyright (c) 2016 GitHub, Inc.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "atom/renderer/atom_sandboxed_renderer_client.h"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "atom_natives.h" // NOLINT: This file is generated with js2c
|
||||||
|
|
||||||
|
#include "atom/common/api/api_messages.h"
|
||||||
|
#include "atom/common/native_mate_converters/string16_converter.h"
|
||||||
|
#include "atom/common/native_mate_converters/value_converter.h"
|
||||||
|
#include "atom/common/options_switches.h"
|
||||||
|
#include "atom/renderer/api/atom_api_renderer_ipc.h"
|
||||||
|
#include "atom/renderer/atom_render_view_observer.h"
|
||||||
|
#include "base/command_line.h"
|
||||||
|
#include "content/public/renderer/render_frame.h"
|
||||||
|
#include "content/public/renderer/render_frame_observer.h"
|
||||||
|
#include "content/public/renderer/render_view.h"
|
||||||
|
#include "content/public/renderer/render_view_observer.h"
|
||||||
|
#include "ipc/ipc_message_macros.h"
|
||||||
|
#include "third_party/WebKit/public/web/WebFrame.h"
|
||||||
|
#include "third_party/WebKit/public/web/WebKit.h"
|
||||||
|
#include "third_party/WebKit/public/web/WebLocalFrame.h"
|
||||||
|
#include "third_party/WebKit/public/web/WebScriptSource.h"
|
||||||
|
#include "third_party/WebKit/public/web/WebView.h"
|
||||||
|
|
||||||
|
namespace atom {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const std::string kBindingKey = "binding";
|
||||||
|
|
||||||
|
class AtomSandboxedRenderFrameObserver : public content::RenderFrameObserver {
|
||||||
|
public:
|
||||||
|
AtomSandboxedRenderFrameObserver(content::RenderFrame* frame,
|
||||||
|
AtomSandboxedRendererClient* renderer_client)
|
||||||
|
: content::RenderFrameObserver(frame),
|
||||||
|
render_frame_(frame),
|
||||||
|
world_id_(-1),
|
||||||
|
renderer_client_(renderer_client) {}
|
||||||
|
|
||||||
|
// content::RenderFrameObserver:
|
||||||
|
void DidClearWindowObject() override {
|
||||||
|
// Make sure every page will get a script context created.
|
||||||
|
render_frame_->GetWebFrame()->executeScript(
|
||||||
|
blink::WebScriptSource("void 0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DidCreateScriptContext(v8::Handle<v8::Context> context,
|
||||||
|
int extension_group,
|
||||||
|
int world_id) override {
|
||||||
|
if (world_id_ != -1 && world_id_ != world_id)
|
||||||
|
return;
|
||||||
|
world_id_ = world_id;
|
||||||
|
renderer_client_->DidCreateScriptContext(context, render_frame_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WillReleaseScriptContext(v8::Local<v8::Context> context,
|
||||||
|
int world_id) override {
|
||||||
|
if (world_id_ != world_id)
|
||||||
|
return;
|
||||||
|
renderer_client_->WillReleaseScriptContext(context, render_frame_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OnDestruct() override {
|
||||||
|
delete this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
content::RenderFrame* render_frame_;
|
||||||
|
int world_id_;
|
||||||
|
AtomSandboxedRendererClient* renderer_client_;
|
||||||
|
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(AtomSandboxedRenderFrameObserver);
|
||||||
|
};
|
||||||
|
|
||||||
|
class AtomSandboxedRenderViewObserver : public AtomRenderViewObserver {
|
||||||
|
public:
|
||||||
|
AtomSandboxedRenderViewObserver(content::RenderView* render_view,
|
||||||
|
AtomSandboxedRendererClient* renderer_client)
|
||||||
|
: AtomRenderViewObserver(render_view, nullptr),
|
||||||
|
renderer_client_(renderer_client) {
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void EmitIPCEvent(blink::WebFrame* frame,
|
||||||
|
const base::string16& channel,
|
||||||
|
const base::ListValue& args) override {
|
||||||
|
if (!frame || frame->isWebRemoteFrame())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto isolate = blink::mainThreadIsolate();
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
auto context = frame->mainWorldScriptContext();
|
||||||
|
v8::Context::Scope context_scope(context);
|
||||||
|
v8::Local<v8::Value> argv[] = {
|
||||||
|
mate::ConvertToV8(isolate, channel),
|
||||||
|
mate::ConvertToV8(isolate, args)
|
||||||
|
};
|
||||||
|
renderer_client_->InvokeBindingCallback(
|
||||||
|
context,
|
||||||
|
"onMessage",
|
||||||
|
std::vector<v8::Local<v8::Value>>(argv, argv + 2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
AtomSandboxedRendererClient* renderer_client_;
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(AtomSandboxedRenderViewObserver);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
|
||||||
|
AtomSandboxedRendererClient::AtomSandboxedRendererClient() {
|
||||||
|
}
|
||||||
|
|
||||||
|
AtomSandboxedRendererClient::~AtomSandboxedRendererClient() {
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomSandboxedRendererClient::RenderFrameCreated(
|
||||||
|
content::RenderFrame* render_frame) {
|
||||||
|
new AtomSandboxedRenderFrameObserver(render_frame, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomSandboxedRendererClient::RenderViewCreated(
|
||||||
|
content::RenderView* render_view) {
|
||||||
|
new AtomSandboxedRenderViewObserver(render_view, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomSandboxedRendererClient::DidCreateScriptContext(
|
||||||
|
v8::Handle<v8::Context> context, content::RenderFrame* render_frame) {
|
||||||
|
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
|
||||||
|
std::string preload_script = command_line->GetSwitchValueASCII(
|
||||||
|
switches::kPreloadScript);
|
||||||
|
if (preload_script.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
auto isolate = context->GetIsolate();
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
v8::Context::Scope context_scope(context);
|
||||||
|
// Wrap the bundle into a function that receives the binding object and the
|
||||||
|
// preload script path as arguments.
|
||||||
|
std::string preload_bundle_native(node::preload_bundle_native,
|
||||||
|
node::preload_bundle_native + sizeof(node::preload_bundle_native));
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "(function(binding, preloadPath) {\n";
|
||||||
|
ss << preload_bundle_native << "\n";
|
||||||
|
ss << "})";
|
||||||
|
std::string preload_wrapper = ss.str();
|
||||||
|
// Compile the wrapper and run it to get the function object
|
||||||
|
auto script = v8::Script::Compile(
|
||||||
|
mate::ConvertToV8(isolate, preload_wrapper)->ToString());
|
||||||
|
auto func = v8::Handle<v8::Function>::Cast(
|
||||||
|
script->Run(context).ToLocalChecked());
|
||||||
|
// Create and initialize the binding object
|
||||||
|
auto binding = v8::Object::New(isolate);
|
||||||
|
api::Initialize(binding, v8::Null(isolate), context, nullptr);
|
||||||
|
v8::Local<v8::Value> args[] = {
|
||||||
|
binding,
|
||||||
|
mate::ConvertToV8(isolate, preload_script)
|
||||||
|
};
|
||||||
|
// Execute the function with proper arguments
|
||||||
|
ignore_result(func->Call(context, v8::Null(isolate), 2, args));
|
||||||
|
// Store the bindingt privately for handling messages from the main process.
|
||||||
|
auto binding_key = mate::ConvertToV8(isolate, kBindingKey)->ToString();
|
||||||
|
auto private_binding_key = v8::Private::ForApi(isolate, binding_key);
|
||||||
|
context->Global()->SetPrivate(context, private_binding_key, binding);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomSandboxedRendererClient::WillReleaseScriptContext(
|
||||||
|
v8::Handle<v8::Context> context, content::RenderFrame* render_frame) {
|
||||||
|
auto isolate = context->GetIsolate();
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
v8::Context::Scope context_scope(context);
|
||||||
|
InvokeBindingCallback(context, "onExit", std::vector<v8::Local<v8::Value>>());
|
||||||
|
}
|
||||||
|
|
||||||
|
void AtomSandboxedRendererClient::InvokeBindingCallback(
|
||||||
|
v8::Handle<v8::Context> context,
|
||||||
|
std::string callback_name,
|
||||||
|
std::vector<v8::Handle<v8::Value>> args) {
|
||||||
|
auto isolate = context->GetIsolate();
|
||||||
|
auto binding_key = mate::ConvertToV8(isolate, kBindingKey)->ToString();
|
||||||
|
auto private_binding_key = v8::Private::ForApi(isolate, binding_key);
|
||||||
|
auto global_object = context->Global();
|
||||||
|
v8::Local<v8::Value> value;
|
||||||
|
if (!global_object->GetPrivate(context, private_binding_key).ToLocal(&value))
|
||||||
|
return;
|
||||||
|
if (value.IsEmpty() || !value->IsObject())
|
||||||
|
return;
|
||||||
|
auto binding = value->ToObject();
|
||||||
|
auto callback_key = mate::ConvertToV8(isolate, callback_name)->ToString();
|
||||||
|
auto callback_value = binding->Get(callback_key);
|
||||||
|
DCHECK(callback_value->IsFunction()); // set by sandboxed_renderer/init.js
|
||||||
|
auto callback = v8::Handle<v8::Function>::Cast(callback_value);
|
||||||
|
ignore_result(callback->Call(context, binding, args.size(), &args[0]));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace atom
|
37
atom/renderer/atom_sandboxed_renderer_client.h
Normal file
37
atom/renderer/atom_sandboxed_renderer_client.h
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright (c) 2016 GitHub, Inc.
|
||||||
|
// Use of this source code is governed by the MIT license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
#ifndef ATOM_RENDERER_ATOM_SANDBOXED_RENDERER_CLIENT_H_
|
||||||
|
#define ATOM_RENDERER_ATOM_SANDBOXED_RENDERER_CLIENT_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "content/public/renderer/content_renderer_client.h"
|
||||||
|
#include "content/public/renderer/render_frame.h"
|
||||||
|
|
||||||
|
namespace atom {
|
||||||
|
|
||||||
|
class AtomSandboxedRendererClient : public content::ContentRendererClient {
|
||||||
|
public:
|
||||||
|
AtomSandboxedRendererClient();
|
||||||
|
virtual ~AtomSandboxedRendererClient();
|
||||||
|
|
||||||
|
void DidCreateScriptContext(
|
||||||
|
v8::Handle<v8::Context> context, content::RenderFrame* render_frame);
|
||||||
|
void WillReleaseScriptContext(
|
||||||
|
v8::Handle<v8::Context> context, content::RenderFrame* render_frame);
|
||||||
|
void InvokeBindingCallback(v8::Handle<v8::Context> context,
|
||||||
|
std::string callback_name,
|
||||||
|
std::vector<v8::Handle<v8::Value>> args);
|
||||||
|
// content::ContentRendererClient:
|
||||||
|
void RenderFrameCreated(content::RenderFrame*) override;
|
||||||
|
void RenderViewCreated(content::RenderView*) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
DISALLOW_COPY_AND_ASSIGN(AtomSandboxedRendererClient);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace atom
|
||||||
|
|
||||||
|
#endif // ATOM_RENDERER_ATOM_SANDBOXED_RENDERER_CLIENT_H_
|
53
electron.gyp
53
electron.gyp
|
@ -5,6 +5,7 @@
|
||||||
'company_name%': 'GitHub, Inc',
|
'company_name%': 'GitHub, Inc',
|
||||||
'company_abbr%': 'github',
|
'company_abbr%': 'github',
|
||||||
'version%': '1.4.1',
|
'version%': '1.4.1',
|
||||||
|
'js2c_input_dir': '<(SHARED_INTERMEDIATE_DIR)/js2c',
|
||||||
},
|
},
|
||||||
'includes': [
|
'includes': [
|
||||||
'filenames.gypi',
|
'filenames.gypi',
|
||||||
|
@ -410,14 +411,64 @@
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
}, # target app2asar
|
}, # target app2asar
|
||||||
|
{
|
||||||
|
'target_name': 'atom_js2c_copy',
|
||||||
|
'type': 'none',
|
||||||
|
'copies': [
|
||||||
|
{
|
||||||
|
'destination': '<(js2c_input_dir)',
|
||||||
|
'files': [
|
||||||
|
'<@(js2c_sources)',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}, # target atom_js2c_copy
|
||||||
|
{
|
||||||
|
'target_name': 'atom_browserify',
|
||||||
|
'type': 'none',
|
||||||
|
'dependencies': [
|
||||||
|
# depend on this target to ensure the '<(js2c_input_dir)' is created
|
||||||
|
'atom_js2c_copy',
|
||||||
|
],
|
||||||
|
'actions': [
|
||||||
|
{
|
||||||
|
'action_name': 'atom_browserify',
|
||||||
|
'inputs': [
|
||||||
|
'<@(browserify_entries)',
|
||||||
|
# Any js file under `lib/` can be included in the preload bundle.
|
||||||
|
# Add all js sources as dependencies so any change to a js file will
|
||||||
|
# trigger a rebuild of the bundle(and consequently of js2c).
|
||||||
|
'<@(js_sources)',
|
||||||
|
],
|
||||||
|
'outputs': [
|
||||||
|
'<(js2c_input_dir)/preload_bundle.js',
|
||||||
|
],
|
||||||
|
'action': [
|
||||||
|
'npm',
|
||||||
|
'run',
|
||||||
|
'browserify',
|
||||||
|
'--',
|
||||||
|
'<@(browserify_entries)',
|
||||||
|
'-o',
|
||||||
|
'<@(_outputs)',
|
||||||
|
],
|
||||||
|
}
|
||||||
|
],
|
||||||
|
}, # target atom_browserify
|
||||||
{
|
{
|
||||||
'target_name': 'atom_js2c',
|
'target_name': 'atom_js2c',
|
||||||
'type': 'none',
|
'type': 'none',
|
||||||
|
'dependencies': [
|
||||||
|
'atom_js2c_copy',
|
||||||
|
'atom_browserify',
|
||||||
|
],
|
||||||
'actions': [
|
'actions': [
|
||||||
{
|
{
|
||||||
'action_name': 'atom_js2c',
|
'action_name': 'atom_js2c',
|
||||||
'inputs': [
|
'inputs': [
|
||||||
|
# List all input files that should trigger a rebuild with js2c
|
||||||
'<@(js2c_sources)',
|
'<@(js2c_sources)',
|
||||||
|
'<(js2c_input_dir)/preload_bundle.js',
|
||||||
],
|
],
|
||||||
'outputs': [
|
'outputs': [
|
||||||
'<(SHARED_INTERMEDIATE_DIR)/atom_natives.h',
|
'<(SHARED_INTERMEDIATE_DIR)/atom_natives.h',
|
||||||
|
@ -426,7 +477,7 @@
|
||||||
'python',
|
'python',
|
||||||
'tools/js2c.py',
|
'tools/js2c.py',
|
||||||
'<@(_outputs)',
|
'<@(_outputs)',
|
||||||
'<@(_inputs)',
|
'<(js2c_input_dir)',
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
@ -61,6 +61,7 @@
|
||||||
'lib/renderer/api/desktop-capturer.js',
|
'lib/renderer/api/desktop-capturer.js',
|
||||||
'lib/renderer/api/exports/electron.js',
|
'lib/renderer/api/exports/electron.js',
|
||||||
'lib/renderer/api/ipc-renderer.js',
|
'lib/renderer/api/ipc-renderer.js',
|
||||||
|
'lib/renderer/api/ipc-renderer-setup.js',
|
||||||
'lib/renderer/api/remote.js',
|
'lib/renderer/api/remote.js',
|
||||||
'lib/renderer/api/screen.js',
|
'lib/renderer/api/screen.js',
|
||||||
'lib/renderer/api/web-frame.js',
|
'lib/renderer/api/web-frame.js',
|
||||||
|
@ -69,6 +70,9 @@
|
||||||
'lib/renderer/extensions/storage.js',
|
'lib/renderer/extensions/storage.js',
|
||||||
'lib/renderer/extensions/web-navigation.js',
|
'lib/renderer/extensions/web-navigation.js',
|
||||||
],
|
],
|
||||||
|
'browserify_entries': [
|
||||||
|
'lib/sandboxed_renderer/init.js',
|
||||||
|
],
|
||||||
'js2c_sources': [
|
'js2c_sources': [
|
||||||
'lib/common/asar.js',
|
'lib/common/asar.js',
|
||||||
'lib/common/asar_init.js',
|
'lib/common/asar_init.js',
|
||||||
|
@ -424,6 +428,7 @@
|
||||||
'atom/common/platform_util_linux.cc',
|
'atom/common/platform_util_linux.cc',
|
||||||
'atom/common/platform_util_mac.mm',
|
'atom/common/platform_util_mac.mm',
|
||||||
'atom/common/platform_util_win.cc',
|
'atom/common/platform_util_win.cc',
|
||||||
|
'atom/renderer/api/atom_api_renderer_ipc.h',
|
||||||
'atom/renderer/api/atom_api_renderer_ipc.cc',
|
'atom/renderer/api/atom_api_renderer_ipc.cc',
|
||||||
'atom/renderer/api/atom_api_spell_check_client.cc',
|
'atom/renderer/api/atom_api_spell_check_client.cc',
|
||||||
'atom/renderer/api/atom_api_spell_check_client.h',
|
'atom/renderer/api/atom_api_spell_check_client.h',
|
||||||
|
@ -435,6 +440,8 @@
|
||||||
'atom/renderer/atom_renderer_client.h',
|
'atom/renderer/atom_renderer_client.h',
|
||||||
'atom/renderer/content_settings_observer.cc',
|
'atom/renderer/content_settings_observer.cc',
|
||||||
'atom/renderer/content_settings_observer.h',
|
'atom/renderer/content_settings_observer.h',
|
||||||
|
'atom/renderer/atom_sandboxed_renderer_client.cc',
|
||||||
|
'atom/renderer/atom_sandboxed_renderer_client.h',
|
||||||
'atom/renderer/guest_view_container.cc',
|
'atom/renderer/guest_view_container.cc',
|
||||||
'atom/renderer/guest_view_container.h',
|
'atom/renderer/guest_view_container.h',
|
||||||
'atom/renderer/node_array_buffer_bridge.cc',
|
'atom/renderer/node_array_buffer_bridge.cc',
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
const {ipcMain} = require('electron')
|
const {ipcMain} = require('electron')
|
||||||
const {EventEmitter} = require('events')
|
const {EventEmitter} = require('events')
|
||||||
const {BrowserWindow} = process.atomBinding('window')
|
const {BrowserWindow} = process.atomBinding('window')
|
||||||
|
const v8Util = process.atomBinding('v8_util')
|
||||||
|
|
||||||
Object.setPrototypeOf(BrowserWindow.prototype, EventEmitter.prototype)
|
Object.setPrototypeOf(BrowserWindow.prototype, EventEmitter.prototype)
|
||||||
|
|
||||||
|
@ -26,6 +27,34 @@ BrowserWindow.prototype._init = function () {
|
||||||
ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, disposition, options)
|
ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, disposition, options)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.webContents.on('-web-contents-created', (event, webContents, url,
|
||||||
|
frameName) => {
|
||||||
|
v8Util.setHiddenValue(webContents, 'url-framename', {url, frameName})
|
||||||
|
})
|
||||||
|
// Create a new browser window for the native implementation of
|
||||||
|
// "window.open"(sandbox mode only)
|
||||||
|
this.webContents.on('-add-new-contents', (event, webContents, disposition,
|
||||||
|
userGesture, left, top, width,
|
||||||
|
height) => {
|
||||||
|
let urlFrameName = v8Util.getHiddenValue(webContents, 'url-framename')
|
||||||
|
if ((disposition !== 'foreground-tab' && disposition !== 'new-window') ||
|
||||||
|
!urlFrameName) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let {url, frameName} = urlFrameName
|
||||||
|
v8Util.deleteHiddenValue(webContents, 'url-framename')
|
||||||
|
const options = {
|
||||||
|
show: true,
|
||||||
|
x: left,
|
||||||
|
y: top,
|
||||||
|
width: width || 800,
|
||||||
|
height: height || 600,
|
||||||
|
webContents: webContents
|
||||||
|
}
|
||||||
|
ipcMain.emit('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_OPEN', event, url, frameName, disposition, options)
|
||||||
|
})
|
||||||
|
|
||||||
// window.resizeTo(...)
|
// window.resizeTo(...)
|
||||||
// window.moveTo(...)
|
// window.moveTo(...)
|
||||||
this.webContents.on('move', (event, size) => {
|
this.webContents.on('move', (event, size) => {
|
||||||
|
|
|
@ -123,7 +123,7 @@ const hookWebContentsEvents = function (webContents) {
|
||||||
sendToBackgroundPages('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', {
|
sendToBackgroundPages('CHROME_WEBNAVIGATION_ONBEFORENAVIGATE', {
|
||||||
frameId: 0,
|
frameId: 0,
|
||||||
parentFrameId: -1,
|
parentFrameId: -1,
|
||||||
processId: webContents.getId(),
|
processId: webContents.getProcessId(),
|
||||||
tabId: tabId,
|
tabId: tabId,
|
||||||
timeStamp: Date.now(),
|
timeStamp: Date.now(),
|
||||||
url: url
|
url: url
|
||||||
|
@ -134,7 +134,7 @@ const hookWebContentsEvents = function (webContents) {
|
||||||
sendToBackgroundPages('CHROME_WEBNAVIGATION_ONCOMPLETED', {
|
sendToBackgroundPages('CHROME_WEBNAVIGATION_ONCOMPLETED', {
|
||||||
frameId: 0,
|
frameId: 0,
|
||||||
parentFrameId: -1,
|
parentFrameId: -1,
|
||||||
processId: webContents.getId(),
|
processId: webContents.getProcessId(),
|
||||||
tabId: tabId,
|
tabId: tabId,
|
||||||
timeStamp: Date.now(),
|
timeStamp: Date.now(),
|
||||||
url: url
|
url: url
|
||||||
|
|
|
@ -57,7 +57,30 @@ const createGuest = function (embedder, url, frameName, options) {
|
||||||
}
|
}
|
||||||
options.webPreferences.openerId = embedder.id
|
options.webPreferences.openerId = embedder.id
|
||||||
guest = new BrowserWindow(options)
|
guest = new BrowserWindow(options)
|
||||||
guest.loadURL(url)
|
if (!options.webContents || url !== 'about:blank') {
|
||||||
|
// We should not call `loadURL` if the window was constructed from an
|
||||||
|
// existing webContents(window.open in a sandboxed renderer) and if the url
|
||||||
|
// is not 'about:blank'.
|
||||||
|
//
|
||||||
|
// Navigating to the url when creating the window from an existing
|
||||||
|
// webContents would not be necessary(it will navigate there anyway), but
|
||||||
|
// apparently there's a bug that allows the child window to be scripted by
|
||||||
|
// the opener, even when the child window is from another origin.
|
||||||
|
//
|
||||||
|
// That's why the second condition(url !== "about:blank") is required: to
|
||||||
|
// force `OverrideSiteInstanceForNavigation` to be called and consequently
|
||||||
|
// spawn a new renderer if the new window is targeting a different origin.
|
||||||
|
//
|
||||||
|
// If the URL is "about:blank", then it is very likely that the opener just
|
||||||
|
// wants to synchronously script the popup, for example:
|
||||||
|
//
|
||||||
|
// let popup = window.open()
|
||||||
|
// popup.document.body.write('<h1>hello</h1>')
|
||||||
|
//
|
||||||
|
// The above code would not work if a navigation to "about:blank" is done
|
||||||
|
// here, since the window would be cleared of all changes in the next tick.
|
||||||
|
guest.loadURL(url)
|
||||||
|
}
|
||||||
|
|
||||||
// When |embedder| is destroyed we should also destroy attached guest, and if
|
// When |embedder| is destroyed we should also destroy attached guest, and if
|
||||||
// guest is closed by user then we should prevent |embedder| from double
|
// guest is closed by user then we should prevent |embedder| from double
|
||||||
|
@ -72,8 +95,19 @@ const createGuest = function (embedder, url, frameName, options) {
|
||||||
embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
|
embedder.send('ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_' + guestId)
|
||||||
embedder.removeListener('render-view-deleted', closedByEmbedder)
|
embedder.removeListener('render-view-deleted', closedByEmbedder)
|
||||||
}
|
}
|
||||||
embedder.once('render-view-deleted', closedByEmbedder)
|
if (!options.webPreferences.sandbox) {
|
||||||
guest.once('closed', closedByUser)
|
// These events should only be handled when the guest window is opened by a
|
||||||
|
// non-sandboxed renderer for two reasons:
|
||||||
|
//
|
||||||
|
// - `render-view-deleted` is emitted when the popup is closed by the user,
|
||||||
|
// and that will eventually result in NativeWindow::NotifyWindowClosed
|
||||||
|
// using a dangling pointer since `destroy()` would have been called by
|
||||||
|
// `closeByEmbedded`
|
||||||
|
// - No need to emit `ELECTRON_GUEST_WINDOW_MANAGER_WINDOW_CLOSED_` since
|
||||||
|
// there's no renderer code listening to it.,
|
||||||
|
embedder.once('render-view-deleted', closedByEmbedder)
|
||||||
|
guest.once('closed', closedByUser)
|
||||||
|
}
|
||||||
|
|
||||||
if (frameName) {
|
if (frameName) {
|
||||||
frameToGuest[frameName] = guest
|
frameToGuest[frameName] = guest
|
||||||
|
|
|
@ -5,6 +5,8 @@ const electron = require('electron')
|
||||||
const v8Util = process.atomBinding('v8_util')
|
const v8Util = process.atomBinding('v8_util')
|
||||||
const {ipcMain, isPromise, webContents} = electron
|
const {ipcMain, isPromise, webContents} = electron
|
||||||
|
|
||||||
|
const fs = require('fs')
|
||||||
|
|
||||||
const objectsRegistry = require('./objects-registry')
|
const objectsRegistry = require('./objects-registry')
|
||||||
|
|
||||||
const hasProp = {}.hasOwnProperty
|
const hasProp = {}.hasOwnProperty
|
||||||
|
@ -235,6 +237,13 @@ ipcMain.on('ELECTRON_BROWSER_REQUIRE', function (event, module) {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
ipcMain.on('ELECTRON_BROWSER_READ_FILE', function (event, file) {
|
||||||
|
fs.readFile(file, (err, data) => {
|
||||||
|
if (err) event.returnValue = {err: err.message}
|
||||||
|
else event.returnValue = {data: data.toString()}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, module) {
|
ipcMain.on('ELECTRON_BROWSER_GET_BUILTIN', function (event, module) {
|
||||||
try {
|
try {
|
||||||
event.returnValue = valueToMeta(event.sender, electron[module])
|
event.returnValue = valueToMeta(event.sender, electron[module])
|
||||||
|
|
29
lib/renderer/api/ipc-renderer-setup.js
Normal file
29
lib/renderer/api/ipc-renderer-setup.js
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
module.exports = function (ipcRenderer, binding) {
|
||||||
|
ipcRenderer.send = function (...args) {
|
||||||
|
return binding.send('ipc-message', args)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcRenderer.sendSync = function (...args) {
|
||||||
|
return JSON.parse(binding.sendSync('ipc-message-sync', args))
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcRenderer.sendToHost = function (...args) {
|
||||||
|
return binding.send('ipc-message-host', args)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
|
||||||
|
if (typeof webContentsId !== 'number') {
|
||||||
|
throw new TypeError('First argument has to be webContentsId')
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcRenderer.send('ELECTRON_BROWSER_SEND_TO', false, webContentsId, channel, ...args)
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
|
||||||
|
if (typeof webContentsId !== 'number') {
|
||||||
|
throw new TypeError('First argument has to be webContentsId')
|
||||||
|
}
|
||||||
|
|
||||||
|
ipcRenderer.send('ELECTRON_BROWSER_SEND_TO', true, webContentsId, channel, ...args)
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,33 +5,6 @@ const v8Util = process.atomBinding('v8_util')
|
||||||
|
|
||||||
// Created by init.js.
|
// Created by init.js.
|
||||||
const ipcRenderer = v8Util.getHiddenValue(global, 'ipc')
|
const ipcRenderer = v8Util.getHiddenValue(global, 'ipc')
|
||||||
|
require('./ipc-renderer-setup')(ipcRenderer, binding)
|
||||||
ipcRenderer.send = function (...args) {
|
|
||||||
return binding.send('ipc-message', args)
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcRenderer.sendSync = function (...args) {
|
|
||||||
return JSON.parse(binding.sendSync('ipc-message-sync', args))
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcRenderer.sendToHost = function (...args) {
|
|
||||||
return binding.send('ipc-message-host', args)
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcRenderer.sendTo = function (webContentsId, channel, ...args) {
|
|
||||||
if (typeof webContentsId !== 'number') {
|
|
||||||
throw new TypeError('First argument has to be webContentsId')
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcRenderer.send('ELECTRON_BROWSER_SEND_TO', false, webContentsId, channel, ...args)
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcRenderer.sendToAll = function (webContentsId, channel, ...args) {
|
|
||||||
if (typeof webContentsId !== 'number') {
|
|
||||||
throw new TypeError('First argument has to be webContentsId')
|
|
||||||
}
|
|
||||||
|
|
||||||
ipcRenderer.send('ELECTRON_BROWSER_SEND_TO', true, webContentsId, channel, ...args)
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = ipcRenderer
|
module.exports = ipcRenderer
|
||||||
|
|
56
lib/sandboxed_renderer/init.js
Normal file
56
lib/sandboxed_renderer/init.js
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
/* eslint no-eval: "off" */
|
||||||
|
/* global binding, preloadPath, process, Buffer */
|
||||||
|
const events = require('events')
|
||||||
|
|
||||||
|
const ipcRenderer = new events.EventEmitter()
|
||||||
|
const proc = new events.EventEmitter()
|
||||||
|
// eval in window scope:
|
||||||
|
// http://www.ecma-international.org/ecma-262/5.1/#sec-10.4.2
|
||||||
|
const geval = eval
|
||||||
|
|
||||||
|
require('../renderer/api/ipc-renderer-setup')(ipcRenderer, binding)
|
||||||
|
|
||||||
|
binding.onMessage = function (channel, args) {
|
||||||
|
ipcRenderer.emit.apply(ipcRenderer, [channel].concat(args))
|
||||||
|
}
|
||||||
|
|
||||||
|
binding.onExit = function () {
|
||||||
|
proc.emit('exit')
|
||||||
|
}
|
||||||
|
|
||||||
|
const preloadModules = new Map([
|
||||||
|
['electron', {
|
||||||
|
ipcRenderer: ipcRenderer
|
||||||
|
}]
|
||||||
|
])
|
||||||
|
|
||||||
|
function preloadRequire (module) {
|
||||||
|
if (preloadModules.has(module)) {
|
||||||
|
return preloadModules.get(module)
|
||||||
|
}
|
||||||
|
throw new Error('module not found')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch the source for the preload
|
||||||
|
let preloadSrc = ipcRenderer.sendSync('ELECTRON_BROWSER_READ_FILE', preloadPath)
|
||||||
|
if (preloadSrc.err) {
|
||||||
|
throw new Error(preloadSrc.err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wrap the source into a function receives a `require` function as argument.
|
||||||
|
// Browserify bundles can make use of this, as explained in:
|
||||||
|
// https://github.com/substack/node-browserify#multiple-bundles
|
||||||
|
//
|
||||||
|
// For example, the user can create a browserify bundle with:
|
||||||
|
//
|
||||||
|
// $ browserify -x electron preload.js > renderer.js
|
||||||
|
//
|
||||||
|
// and any `require('electron')` calls in `preload.js` will work as expected
|
||||||
|
// since browserify won't try to include `electron` in the bundle and will fall
|
||||||
|
// back to the `preloadRequire` function above.
|
||||||
|
let preloadWrapperSrc = `(function(require, process, Buffer, global) {
|
||||||
|
${preloadSrc.data}
|
||||||
|
})`
|
||||||
|
|
||||||
|
let preloadFn = geval(preloadWrapperSrc)
|
||||||
|
preloadFn(preloadRequire, proc, Buffer, global)
|
|
@ -3,6 +3,7 @@
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"asar": "^0.11.0",
|
"asar": "^0.11.0",
|
||||||
|
"browserify": "^13.1.0",
|
||||||
"electabul": "~0.0.4",
|
"electabul": "~0.0.4",
|
||||||
"electron-docs-linter": "^1.6.2",
|
"electron-docs-linter": "^1.6.2",
|
||||||
"request": "*",
|
"request": "*",
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"bootstrap": "python ./script/bootstrap.py",
|
"bootstrap": "python ./script/bootstrap.py",
|
||||||
|
"browserify": "browserify",
|
||||||
"build": "python ./script/build.py -c D",
|
"build": "python ./script/build.py -c D",
|
||||||
"clean": "python ./script/clean.py",
|
"clean": "python ./script/clean.py",
|
||||||
"coverage": "npm run instrument-code-coverage && npm test -- --use-instrumented-asar",
|
"coverage": "npm run instrument-code-coverage && npm test -- --use-instrumented-asar",
|
||||||
|
|
|
@ -14,10 +14,7 @@ SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||||
VENDOR_DIR = os.path.join(SOURCE_ROOT, 'vendor')
|
VENDOR_DIR = os.path.join(SOURCE_ROOT, 'vendor')
|
||||||
PYTHON_26_URL = 'https://chromium.googlesource.com/chromium/deps/python_26'
|
PYTHON_26_URL = 'https://chromium.googlesource.com/chromium/deps/python_26'
|
||||||
|
|
||||||
if os.environ.has_key('CI'):
|
NPM = 'npm'
|
||||||
NPM = os.path.join(SOURCE_ROOT, 'node_modules', '.bin', 'npm')
|
|
||||||
else:
|
|
||||||
NPM = 'npm'
|
|
||||||
if sys.platform in ['win32', 'cygwin']:
|
if sys.platform in ['win32', 'cygwin']:
|
||||||
NPM += '.cmd'
|
NPM += '.cmd'
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,11 @@ def main():
|
||||||
execute([npm, 'install', 'npm@2.12.1'])
|
execute([npm, 'install', 'npm@2.12.1'])
|
||||||
|
|
||||||
log_versions()
|
log_versions()
|
||||||
|
# Add "./node_modules/.bin" to the beginning of $PATH, which will ensure
|
||||||
|
# future "npm" invocations use the right version.
|
||||||
|
node_bin_dir = os.path.join(SOURCE_ROOT, 'node_modules', '.bin')
|
||||||
|
os.environ['PATH'] = os.path.pathsep.join([node_bin_dir,
|
||||||
|
os.environ.get('PATH', '')])
|
||||||
|
|
||||||
is_release = os.environ.has_key('ELECTRON_RELEASE')
|
is_release = os.environ.has_key('ELECTRON_RELEASE')
|
||||||
args = ['--target_arch=' + target_arch]
|
args = ['--target_arch=' + target_arch]
|
||||||
|
|
|
@ -16,6 +16,7 @@ const ipcRenderer = require('electron').ipcRenderer
|
||||||
const BrowserWindow = remote.require('electron').BrowserWindow
|
const BrowserWindow = remote.require('electron').BrowserWindow
|
||||||
|
|
||||||
const isCI = remote.getGlobal('isCi')
|
const isCI = remote.getGlobal('isCi')
|
||||||
|
const {protocol} = remote
|
||||||
|
|
||||||
describe('browser-window module', function () {
|
describe('browser-window module', function () {
|
||||||
var fixtures = path.resolve(__dirname, 'fixtures')
|
var fixtures = path.resolve(__dirname, 'fixtures')
|
||||||
|
@ -572,6 +573,198 @@ describe('browser-window module', function () {
|
||||||
w.loadURL('file://' + path.join(fixtures, 'api', 'blank.html'))
|
w.loadURL('file://' + path.join(fixtures, 'api', 'blank.html'))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
describe('"sandbox" option', function () {
|
||||||
|
function waitForEvents (emitter, events, callback) {
|
||||||
|
let count = events.length
|
||||||
|
for (let event of events) {
|
||||||
|
emitter.once(event, () => {
|
||||||
|
if (!--count) callback()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const preload = path.join(fixtures, 'module', 'preload-sandbox.js')
|
||||||
|
|
||||||
|
// http protocol to simulate accessing a another domain. this is required
|
||||||
|
// because the code paths for cross domain popups is different.
|
||||||
|
function crossDomainHandler (request, callback) {
|
||||||
|
callback({
|
||||||
|
mimeType: 'text/html',
|
||||||
|
data: `<html><body><h1>${request.url}</h1></body></html>`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
before(function (done) {
|
||||||
|
protocol.interceptStringProtocol('http', crossDomainHandler, function () {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
after(function (done) {
|
||||||
|
protocol.uninterceptProtocol('http', function () {
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('exposes ipcRenderer to preload script', function (done) {
|
||||||
|
ipcMain.once('answer', function (event, test) {
|
||||||
|
assert.equal(test, 'preload')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
w.destroy()
|
||||||
|
w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
sandbox: true,
|
||||||
|
preload: preload
|
||||||
|
}
|
||||||
|
})
|
||||||
|
w.loadURL('file://' + path.join(fixtures, 'api', 'preload.html'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('exposes "exit" event to preload script', function (done) {
|
||||||
|
w.destroy()
|
||||||
|
w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
sandbox: true,
|
||||||
|
preload: preload
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let htmlPath = path.join(fixtures, 'api', 'sandbox.html?exit-event')
|
||||||
|
const pageUrl = 'file://' + htmlPath
|
||||||
|
w.loadURL(pageUrl)
|
||||||
|
ipcMain.once('answer', function (event, url) {
|
||||||
|
let expectedUrl = pageUrl
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
expectedUrl = 'file:///' + htmlPath.replace(/\\/g, '/')
|
||||||
|
}
|
||||||
|
assert.equal(url, expectedUrl)
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should open windows in same domain with cross-scripting enabled', function (done) {
|
||||||
|
w.destroy()
|
||||||
|
w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
sandbox: true,
|
||||||
|
preload: preload
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let htmlPath = path.join(fixtures, 'api', 'sandbox.html?window-open')
|
||||||
|
const pageUrl = 'file://' + htmlPath
|
||||||
|
w.loadURL(pageUrl)
|
||||||
|
w.webContents.once('new-window', (e, url, frameName, disposition, options) => {
|
||||||
|
let expectedUrl = pageUrl
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
expectedUrl = 'file:///' + htmlPath.replace(/\\/g, '/')
|
||||||
|
}
|
||||||
|
assert.equal(url, expectedUrl)
|
||||||
|
assert.equal(frameName, 'popup!')
|
||||||
|
assert.equal(options.x, 50)
|
||||||
|
assert.equal(options.y, 60)
|
||||||
|
assert.equal(options.width, 500)
|
||||||
|
assert.equal(options.height, 600)
|
||||||
|
ipcMain.once('answer', function (event, html) {
|
||||||
|
assert.equal(html, '<h1>scripting from opener</h1>')
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should open windows in another domain with cross-scripting disabled', function (done) {
|
||||||
|
w.destroy()
|
||||||
|
w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
sandbox: true,
|
||||||
|
preload: preload
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let htmlPath = path.join(fixtures, 'api', 'sandbox.html?window-open-external')
|
||||||
|
const pageUrl = 'file://' + htmlPath
|
||||||
|
w.loadURL(pageUrl)
|
||||||
|
w.webContents.once('new-window', (e, url, frameName, disposition, options) => {
|
||||||
|
assert.equal(url, 'http://www.google.com/#q=electron')
|
||||||
|
assert.equal(options.x, 55)
|
||||||
|
assert.equal(options.y, 65)
|
||||||
|
assert.equal(options.width, 505)
|
||||||
|
assert.equal(options.height, 605)
|
||||||
|
ipcMain.once('child-loaded', function (event, openerIsNull, html) {
|
||||||
|
assert(openerIsNull)
|
||||||
|
assert.equal(html, '<h1>http://www.google.com/#q=electron</h1>')
|
||||||
|
ipcMain.once('answer', function (event, exceptionMessage) {
|
||||||
|
assert(/Blocked a frame with origin/.test(exceptionMessage))
|
||||||
|
done()
|
||||||
|
})
|
||||||
|
w.webContents.send('child-loaded')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should set ipc event sender correctly', function (done) {
|
||||||
|
w.destroy()
|
||||||
|
w = new BrowserWindow({
|
||||||
|
show: false,
|
||||||
|
webPreferences: {
|
||||||
|
sandbox: true,
|
||||||
|
preload: preload
|
||||||
|
}
|
||||||
|
})
|
||||||
|
let htmlPath = path.join(fixtures, 'api', 'sandbox.html?verify-ipc-sender')
|
||||||
|
const pageUrl = 'file://' + htmlPath
|
||||||
|
w.loadURL(pageUrl)
|
||||||
|
w.webContents.once('new-window', (e, url, frameName, disposition, options) => {
|
||||||
|
let parentWc = w.webContents
|
||||||
|
let childWc = options.webContents
|
||||||
|
assert.notEqual(parentWc, childWc)
|
||||||
|
ipcMain.once('parent-ready', function (event) {
|
||||||
|
assert.equal(parentWc, event.sender)
|
||||||
|
parentWc.send('verified')
|
||||||
|
})
|
||||||
|
ipcMain.once('child-ready', function (event) {
|
||||||
|
assert.equal(childWc, event.sender)
|
||||||
|
childWc.send('verified')
|
||||||
|
})
|
||||||
|
waitForEvents(ipcMain, [
|
||||||
|
'parent-answer',
|
||||||
|
'child-answer'
|
||||||
|
], done)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('event handling', function () {
|
||||||
|
it('works for window events', function (done) {
|
||||||
|
waitForEvents(w, [
|
||||||
|
'page-title-updated'
|
||||||
|
], done)
|
||||||
|
w.loadURL('file://' + path.join(fixtures, 'api', 'sandbox.html?window-events'))
|
||||||
|
})
|
||||||
|
|
||||||
|
it('works for web contents events', function (done) {
|
||||||
|
waitForEvents(w.webContents, [
|
||||||
|
'did-navigate',
|
||||||
|
'did-fail-load',
|
||||||
|
'did-stop-loading'
|
||||||
|
], done)
|
||||||
|
w.loadURL('file://' + path.join(fixtures, 'api', 'sandbox.html?webcontents-stop'))
|
||||||
|
waitForEvents(w.webContents, [
|
||||||
|
'did-finish-load',
|
||||||
|
'did-frame-finish-load',
|
||||||
|
'did-navigate-in-page',
|
||||||
|
'will-navigate',
|
||||||
|
'did-start-loading',
|
||||||
|
'did-stop-loading',
|
||||||
|
'did-frame-finish-load',
|
||||||
|
'dom-ready'
|
||||||
|
], done)
|
||||||
|
w.loadURL('file://' + path.join(fixtures, 'api', 'sandbox.html?webcontents-events'))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('beforeunload handler', function () {
|
describe('beforeunload handler', function () {
|
||||||
|
|
76
spec/fixtures/api/sandbox.html
vendored
Normal file
76
spec/fixtures/api/sandbox.html
vendored
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
<html>
|
||||||
|
<script type="text/javascript" charset="utf-8">
|
||||||
|
if (window.opener) {
|
||||||
|
window.callback = function() {
|
||||||
|
opener.require('electron').ipcRenderer.send('answer', document.body.innerHTML)
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
const {ipcRenderer} = require('electron')
|
||||||
|
const tests = {
|
||||||
|
'window-events': () => {
|
||||||
|
document.title = 'changed'
|
||||||
|
},
|
||||||
|
'webcontents-stop': () => {
|
||||||
|
stop()
|
||||||
|
},
|
||||||
|
'webcontents-events': () => {
|
||||||
|
addEventListener('load', () => {
|
||||||
|
location.hash = 'in-page-navigate'
|
||||||
|
setTimeout(() => {
|
||||||
|
location.reload()
|
||||||
|
}, 50)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
'exit-event': () => {
|
||||||
|
process.on('exit', () => {
|
||||||
|
ipcRenderer.send('answer', location.href)
|
||||||
|
})
|
||||||
|
location.assign('http://www.google.com')
|
||||||
|
},
|
||||||
|
'window-open': () => {
|
||||||
|
addEventListener('load', () => {
|
||||||
|
popup = open(window.location.href, 'popup!', 'top=60,left=50,width=500,height=600')
|
||||||
|
popup.addEventListener('DOMContentLoaded', () => {
|
||||||
|
popup.document.write('<h1>scripting from opener</h1>')
|
||||||
|
popup.callback()
|
||||||
|
}, false)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
'window-open-external': () => {
|
||||||
|
addEventListener('load', () => {
|
||||||
|
ipcRenderer.once('child-loaded', (e) => {
|
||||||
|
try {
|
||||||
|
let childDoc = popup.document
|
||||||
|
} catch (e) {
|
||||||
|
ipcRenderer.send('answer', e.message)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
popup = open('http://www.google.com/#q=electron', '', 'top=65,left=55,width=505,height=605')
|
||||||
|
})
|
||||||
|
},
|
||||||
|
'verify-ipc-sender': () => {
|
||||||
|
popup = open()
|
||||||
|
ipcRenderer.once('verified', () => {
|
||||||
|
ipcRenderer.send('parent-answer')
|
||||||
|
})
|
||||||
|
popup.ipcRenderer.once('verified', () => {
|
||||||
|
popup.ipcRenderer.send('child-answer')
|
||||||
|
})
|
||||||
|
setTimeout(() => {
|
||||||
|
ipcRenderer.send('parent-ready')
|
||||||
|
popup.ipcRenderer.send('child-ready')
|
||||||
|
}, 50)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addEventListener('unload', () => {
|
||||||
|
if (window.popup)
|
||||||
|
popup.close()
|
||||||
|
}, false)
|
||||||
|
|
||||||
|
let [,test] = window.location.href.split('?')
|
||||||
|
if (tests.hasOwnProperty(test))
|
||||||
|
tests[test]()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</html>
|
13
spec/fixtures/module/preload-sandbox.js
vendored
Normal file
13
spec/fixtures/module/preload-sandbox.js
vendored
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
(function () {
|
||||||
|
const {ipcRenderer} = require('electron')
|
||||||
|
window.ipcRenderer = ipcRenderer
|
||||||
|
if (location.protocol === 'file:') {
|
||||||
|
window.test = 'preload'
|
||||||
|
window.require = require
|
||||||
|
window.process = process
|
||||||
|
} else if (location.href !== 'about:blank') {
|
||||||
|
addEventListener('DOMContentLoaded', () => {
|
||||||
|
ipcRenderer.send('child-loaded', window.opener == null, document.body.innerHTML)
|
||||||
|
}, false)
|
||||||
|
}
|
||||||
|
})()
|
|
@ -1,6 +1,7 @@
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
|
|
||||||
import contextlib
|
import contextlib
|
||||||
|
import glob
|
||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
@ -11,7 +12,7 @@ SOURCE_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
natives = os.path.abspath(sys.argv[1])
|
natives = os.path.abspath(sys.argv[1])
|
||||||
js_source_files = sys.argv[2:]
|
js_source_files = glob.glob('{0}/*.js'.format(sys.argv[2]))
|
||||||
|
|
||||||
call_js2c(natives, js_source_files)
|
call_js2c(natives, js_source_files)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue