diff --git a/app/atom_main_delegate.cc b/app/atom_main_delegate.cc index 40a42d721741..d8f0c693d494 100644 --- a/app/atom_main_delegate.cc +++ b/app/atom_main_delegate.cc @@ -51,6 +51,15 @@ void AtomMainDelegate::PreSandboxStartup() { InitializeResourceBundle(); CommandLine* command_line = CommandLine::ForCurrentProcess(); + std::string process_type = command_line->GetSwitchValueASCII( + switches::kProcessType); + + // Don't append arguments for renderer process. + if (process_type == switches::kRendererProcess) + return; + + // Add a flag to mark the start of switches added by atom-shell. + command_line->AppendSwitch("atom-shell-switches-start"); // Disable renderer sandbox for most of node's functions. command_line->AppendSwitch(switches::kNoSandbox); @@ -60,7 +69,7 @@ void AtomMainDelegate::PreSandboxStartup() { command_line->AppendSwitch(switches::kDisableAcceleratedCompositing); // Add a flag to mark the end of switches added by atom-shell. - command_line->AppendSwitch("no-more-switches"); + command_line->AppendSwitch("atom-shell-switches-end"); } void AtomMainDelegate::InitializeResourceBundle() { diff --git a/browser/atom_browser_client.cc b/browser/atom_browser_client.cc index 19617002a816..1f7351c92864 100644 --- a/browser/atom_browser_client.cc +++ b/browser/atom_browser_client.cc @@ -4,14 +4,39 @@ #include "browser/atom_browser_client.h" +#include "base/command_line.h" #include "browser/atom_browser_context.h" #include "browser/atom_browser_main_parts.h" +#include "browser/native_window.h" #include "browser/net/atom_url_request_context_getter.h" +#include "browser/window_list.h" +#include "common/options_switches.h" +#include "content/public/browser/render_process_host.h" +#include "content/public/browser/site_instance.h" +#include "content/public/browser/web_contents.h" #include "webkit/common/webpreferences.h" namespace atom { -AtomBrowserClient::AtomBrowserClient() { +namespace { + +struct FindByProcessId { + explicit FindByProcessId(int child_process_id) + : child_process_id_(child_process_id) { + } + + bool operator() (NativeWindow* const window) { + int id = window->GetWebContents()->GetRenderProcessHost()->GetID(); + return id == child_process_id_; + } + + int child_process_id_; +}; + +} // namespace + +AtomBrowserClient::AtomBrowserClient() + : dying_render_process_(NULL) { } AtomBrowserClient::~AtomBrowserClient() { @@ -50,10 +75,44 @@ bool AtomBrowserClient::ShouldSwapProcessesForNavigation( content::SiteInstance* site_instance, const GURL& current_url, const GURL& new_url) { + if (site_instance->HasProcess()) + dying_render_process_ = site_instance->GetProcess(); + // Restart renderer process for all navigations. return true; } +void AtomBrowserClient::AppendExtraCommandLineSwitches( + CommandLine* command_line, + int child_process_id) { + WindowList* list = WindowList::GetInstance(); + NativeWindow* window = NULL; + + // Find the owner of this child process. + WindowList::const_iterator iter = std::find_if( + list->begin(), list->end(), FindByProcessId(child_process_id)); + if (iter != list->end()) + window = *iter; + + // If the render process is a newly started one, which means the window still + // uses the old going-to-be-swapped render process, then we try to find the + // window from the swapped render process. + if (window == NULL && dying_render_process_ != NULL) { + child_process_id = dying_render_process_->GetID(); + WindowList::const_iterator iter = std::find_if( + list->begin(), list->end(), FindByProcessId(child_process_id)); + if (iter != list->end()) + window = *iter; + } + + // Append --node-integration to renderer process. + if (window != NULL) + command_line->AppendSwitchASCII(switches::kNodeIntegration, + window->node_integration()); + + dying_render_process_ = NULL; +} + brightray::BrowserMainParts* AtomBrowserClient::OverrideCreateBrowserMainParts( const content::MainFunctionParams&) { return new AtomBrowserMainParts; diff --git a/browser/atom_browser_client.h b/browser/atom_browser_client.h index 3443f0a707d9..4fe1131dc428 100644 --- a/browser/atom_browser_client.h +++ b/browser/atom_browser_client.h @@ -25,11 +25,16 @@ class AtomBrowserClient : public brightray::BrowserClient { content::SiteInstance* site_instance, const GURL& current_url, const GURL& new_url) OVERRIDE; + virtual void AppendExtraCommandLineSwitches(CommandLine* command_line, + int child_process_id) OVERRIDE; private: virtual brightray::BrowserMainParts* OverrideCreateBrowserMainParts( const content::MainFunctionParams&) OVERRIDE; + // The render process which would be swapped out soon. + content::RenderProcessHost* dying_render_process_; + DISALLOW_COPY_AND_ASSIGN(AtomBrowserClient); }; diff --git a/browser/default_app/main.js b/browser/default_app/main.js index 2aff83e30b0d..b3e3bcb03640 100644 --- a/browser/default_app/main.js +++ b/browser/default_app/main.js @@ -1,5 +1,4 @@ var app = require('app'); -var dialog = require('dialog'); var path = require('path'); var optimist = require('optimist'); @@ -9,7 +8,7 @@ app.on('window-all-closed', function() { app.quit(); }); -var argv = optimist(process.argv.slice(1)).argv; +var argv = optimist(process.argv.slice(1)).boolean('ci').argv; // Start the specified app if there is one specified in command line, otherwise // start the default app. diff --git a/browser/lib/init.coffee b/browser/lib/init.coffee index 950d4671ac56..1b22b3d259b3 100644 --- a/browser/lib/init.coffee +++ b/browser/lib/init.coffee @@ -9,6 +9,11 @@ process.resourcesPath = path.resolve process.argv[1], '..', '..', '..' # we need to restore it here. process.argv.splice 1, 1 +# Pick out switches appended by atom-shell. +startMark = process.argv.indexOf '--atom-shell-switches-start' +endMark = process.argv.indexOf '--atom-shell-switches-end' +process.execArgv = process.argv.splice startMark, endMark - startMark + 1 + # Add browser/api/lib to require's search paths, # which contains javascript part of Atom's built-in libraries. globalPaths = require('module').globalPaths diff --git a/browser/native_window.cc b/browser/native_window.cc index 7b923adf2c3b..c81625c367d6 100644 --- a/browser/native_window.cc +++ b/browser/native_window.cc @@ -48,16 +48,19 @@ NativeWindow::NativeWindow(content::WebContents* web_contents, : content::WebContentsObserver(web_contents), has_frame_(true), is_closed_(false), + node_integration_("all"), weak_factory_(this), inspectable_web_contents_( brightray::InspectableWebContents::Create(web_contents)) { options->GetBoolean(switches::kFrame, &has_frame_); + // Read icon before window is created. std::string icon; - if (options->GetString(switches::kIcon, &icon)) { - if (!SetIcon(icon)) - LOG(ERROR) << "Failed to set icon to " << icon; - } + if (options->GetString(switches::kIcon, &icon) && !SetIcon(icon)) + LOG(ERROR) << "Failed to set icon to " << icon; + + // Read iframe security before any navigation. + options->GetString(switches::kNodeIntegration, &node_integration_); web_contents->SetDelegate(this); diff --git a/browser/native_window.h b/browser/native_window.h index 54cb539d755d..ad6e8a152b76 100644 --- a/browser/native_window.h +++ b/browser/native_window.h @@ -140,6 +140,7 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, } bool has_frame() const { return has_frame_; } + std::string node_integration() const { return node_integration_; } protected: explicit NativeWindow(content::WebContents* web_contents, @@ -219,6 +220,9 @@ class NativeWindow : public brightray::DefaultWebContentsDelegate, // The windows has been closed. bool is_closed_; + // The security token of iframe. + std::string node_integration_; + // Closure that would be called when window is unresponsive when closing, // it should be cancelled when we can prove that the window is responsive. base::CancelableClosure window_unresposive_closure_; diff --git a/common/options_switches.cc b/common/options_switches.cc index 6f068a19d258..f3d849ef176c 100644 --- a/common/options_switches.cc +++ b/common/options_switches.cc @@ -31,6 +31,8 @@ const char kKiosk[] = "kiosk"; // Make windows stays on the top of all other windows. const char kAlwaysOnTop[] = "always-on-top"; +const char kNodeIntegration[] = "node-integration"; + } // namespace switches } // namespace atom diff --git a/common/options_switches.h b/common/options_switches.h index ee61044df2ef..ad6b68b2eaa5 100644 --- a/common/options_switches.h +++ b/common/options_switches.h @@ -26,6 +26,7 @@ extern const char kResizable[]; extern const char kFullscreen[]; extern const char kKiosk[]; extern const char kAlwaysOnTop[]; +extern const char kNodeIntegration[]; } // namespace switches diff --git a/docs/api/browser/browser-window.md b/docs/api/browser/browser-window.md index 3500b9c68040..66b875771ebd 100644 --- a/docs/api/browser/browser-window.md +++ b/docs/api/browser/browser-window.md @@ -18,9 +18,6 @@ win.show(); You can also create a window without chrome by using [Frameless Window](frameless-window.md) API. - -**Note:** Be careful not to use `window` as the variable name. - ## Class: BrowserWindow `BrowserWindow` is an @@ -44,11 +41,31 @@ You can also create a window without chrome by using * `show` Boolean - Whether window should be shown when created * `frame` Boolean - Specify `false` to create a [Frameless Window](frameless-window.md) + * `node-integration` String - Can be `all`, `except-iframe`, + `manual-enable-iframe` or `disable`. Creates a new `BrowserWindow` with native properties set by the `options`. Usually you only need to set the `width` and `height`, other properties will have decent default values. +By default the `node-integration` option is `all`, which means node integration +is available to the main page and all its iframes. You can also set it to +`except-iframe`, which would disable node integration in all iframes, or +`manual-enable-iframe`, which is like `except-iframe`, but would enable iframes +whose name is suffixed by `-enable-node-integration`. And setting to `disable` +would disable the node integration in both the main page and its iframes. + +An example of enable node integration in iframe with `node-integration` set to +`manual-enable-iframe`: + +```html + + + + + +``` + ### Event: 'page-title-updated' * `event` Event diff --git a/renderer/atom_render_view_observer.cc b/renderer/atom_render_view_observer.cc index 960be5c0207f..69d811bf5acf 100644 --- a/renderer/atom_render_view_observer.cc +++ b/renderer/atom_render_view_observer.cc @@ -5,12 +5,14 @@ #include "renderer/atom_render_view_observer.h" #include "common/api/api_messages.h" +#include "content/public/renderer/render_view.h" #include "ipc/ipc_message_macros.h" #include "renderer/api/atom_renderer_bindings.h" #include "renderer/atom_renderer_client.h" #include "third_party/WebKit/public/web/WebDraggableRegion.h" #include "third_party/WebKit/public/web/WebDocument.h" #include "third_party/WebKit/public/web/WebFrame.h" +#include "third_party/WebKit/public/web/WebView.h" #include "common/v8/node_common.h" @@ -53,6 +55,13 @@ bool AtomRenderViewObserver::OnMessageReceived(const IPC::Message& message) { void AtomRenderViewObserver::OnBrowserMessage(const string16& channel, const base::ListValue& args) { + if (!render_view()->GetWebView()) + return; + + WebKit::WebFrame* frame = render_view()->GetWebView()->mainFrame(); + if (!renderer_client_->IsNodeBindingEnabled(frame)) + return; + renderer_client_->atom_bindings()->OnBrowserMessage( render_view(), channel, args); } diff --git a/renderer/atom_renderer_client.cc b/renderer/atom_renderer_client.cc index c89b6c256e68..b5e606a0e588 100644 --- a/renderer/atom_renderer_client.cc +++ b/renderer/atom_renderer_client.cc @@ -6,23 +6,52 @@ #include +#include "base/command_line.h" #include "common/node_bindings.h" +#include "common/options_switches.h" #include "renderer/api/atom_renderer_bindings.h" #include "renderer/atom_render_view_observer.h" +#include "third_party/WebKit/public/web/WebFrame.h" #include "common/v8/node_common.h" namespace atom { +namespace { + +const char* kExceptIframe = "except-iframe"; +const char* kManualEnableIframe = "manual-enable-iframe"; +const char* kDisable = "disable"; +const char* kEnableNodeIntegration = "enable-node-integration"; + +} // namespace + AtomRendererClient::AtomRendererClient() - : node_bindings_(NodeBindings::Create(false)), - atom_bindings_(new AtomRendererBindings) { + : node_integration_(ALL), + main_frame_(NULL) { + // Translate the token. + std::string token = CommandLine::ForCurrentProcess()-> + GetSwitchValueASCII(switches::kNodeIntegration); + if (token == kExceptIframe) + node_integration_ = EXCEPT_IFRAME; + else if (token == kManualEnableIframe) + node_integration_ = MANUAL_ENABLE_IFRAME; + else if (token == kDisable) + node_integration_ = DISABLE; + + if (IsNodeBindingEnabled()) { + node_bindings_.reset(NodeBindings::Create(false)); + atom_bindings_.reset(new AtomRendererBindings); + } } AtomRendererClient::~AtomRendererClient() { } void AtomRendererClient::RenderThreadStarted() { + if (!IsNodeBindingEnabled()) + return; + node_bindings_->Initialize(); node_bindings_->PrepareMessageLoop(); @@ -43,6 +72,13 @@ void AtomRendererClient::DidCreateScriptContext(WebKit::WebFrame* frame, v8::Handle context, int extension_group, int world_id) { + // The first web frame is the main frame. + if (main_frame_ == NULL) + main_frame_ = frame; + + if (!IsNodeBindingEnabled(frame)) + return; + v8::Context::Scope scope(context); // Check the existance of process object to prevent duplicate initialization. @@ -70,6 +106,9 @@ void AtomRendererClient::WillReleaseScriptContext( WebKit::WebFrame* frame, v8::Handle context, int world_id) { + if (!IsNodeBindingEnabled(frame)) + return; + node::Environment* env = node::Environment::GetCurrent(context); if (env == NULL) { LOG(ERROR) << "Encounter a non-node context when releasing script context"; @@ -108,4 +147,21 @@ bool AtomRendererClient::ShouldFork(WebKit::WebFrame* frame, return true; } +bool AtomRendererClient::IsNodeBindingEnabled(WebKit::WebFrame* frame) { + if (node_integration_ == DISABLE) + return false; + // Node integration is enabled in main frame unless explictly disabled. + else if (frame == main_frame_) + return true; + else if (node_integration_ == MANUAL_ENABLE_IFRAME && + frame != NULL && + frame->uniqueName().utf8().find(kEnableNodeIntegration) + == std::string::npos) + return false; + else if (node_integration_ == EXCEPT_IFRAME && frame != NULL) + return false; + else + return true; +} + } // namespace atom diff --git a/renderer/atom_renderer_client.h b/renderer/atom_renderer_client.h index 3dde5eb1d950..bc7aab740a5e 100644 --- a/renderer/atom_renderer_client.h +++ b/renderer/atom_renderer_client.h @@ -23,9 +23,18 @@ class AtomRendererClient : public content::ContentRendererClient { AtomRendererClient(); virtual ~AtomRendererClient(); + bool IsNodeBindingEnabled(WebKit::WebFrame* frame = NULL); + AtomRendererBindings* atom_bindings() const { return atom_bindings_.get(); } private: + enum NodeIntegration { + ALL, + EXCEPT_IFRAME, + MANUAL_ENABLE_IFRAME, + DISABLE, + }; + virtual void RenderThreadStarted() OVERRIDE; virtual void RenderViewCreated(content::RenderView*) OVERRIDE; virtual void DidCreateScriptContext(WebKit::WebFrame* frame, @@ -47,6 +56,12 @@ class AtomRendererClient : public content::ContentRendererClient { scoped_ptr node_bindings_; scoped_ptr atom_bindings_; + // The level of node integration we should support. + NodeIntegration node_integration_; + + // The main frame. + WebKit::WebFrame* main_frame_; + DISALLOW_COPY_AND_ASSIGN(AtomRendererClient); };