From 524bab530acdff646fdc4d1168a450f621099268 Mon Sep 17 00:00:00 2001 From: Thiago de Arruda Date: Sun, 21 Aug 2016 07:30:21 -0300 Subject: [PATCH] Improve AtomSandboxedRendererClient to support preload scripts. Add RenderFrameObserver/RenderViewObserver subclasses that implement the necessary support for preload scripts in sandboxed renderers. --- .../atom_sandboxed_renderer_client.cc | 186 ++++++++++++++++++ .../renderer/atom_sandboxed_renderer_client.h | 15 ++ 2 files changed, 201 insertions(+) diff --git a/atom/renderer/atom_sandboxed_renderer_client.cc b/atom/renderer/atom_sandboxed_renderer_client.cc index 3aae5e2db68..47d9d0ded6c 100644 --- a/atom/renderer/atom_sandboxed_renderer_client.cc +++ b/atom/renderer/atom_sandboxed_renderer_client.cc @@ -4,12 +4,198 @@ #include "atom/renderer/atom_sandboxed_renderer_client.h" +#include + +#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 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 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 argv[] = { + mate::ConvertToV8(isolate, channel), + mate::ConvertToV8(isolate, args) + }; + renderer_client_->InvokeBindingCallback( + context, + "onMessage", + std::vector>(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 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::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 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 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>()); +} + +void AtomSandboxedRendererClient::InvokeBindingCallback( + v8::Handle context, + std::string callback_name, + std::vector> 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 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::Cast(callback_value); + ignore_result(callback->Call(context, binding, args.size(), &args[0])); +} + } // namespace atom diff --git a/atom/renderer/atom_sandboxed_renderer_client.h b/atom/renderer/atom_sandboxed_renderer_client.h index c4b4289d8ed..66d82789155 100644 --- a/atom/renderer/atom_sandboxed_renderer_client.h +++ b/atom/renderer/atom_sandboxed_renderer_client.h @@ -4,7 +4,11 @@ #ifndef ATOM_RENDERER_ATOM_SANDBOXED_RENDERER_CLIENT_H_ #define ATOM_RENDERER_ATOM_SANDBOXED_RENDERER_CLIENT_H_ +#include +#include + #include "content/public/renderer/content_renderer_client.h" +#include "content/public/renderer/render_frame.h" namespace atom { @@ -13,6 +17,17 @@ class AtomSandboxedRendererClient : public content::ContentRendererClient { AtomSandboxedRendererClient(); virtual ~AtomSandboxedRendererClient(); + void DidCreateScriptContext( + v8::Handle context, content::RenderFrame* render_frame); + void WillReleaseScriptContext( + v8::Handle context, content::RenderFrame* render_frame); + void InvokeBindingCallback(v8::Handle context, + std::string callback_name, + std::vector> args); + // content::ContentRendererClient: + void RenderFrameCreated(content::RenderFrame*) override; + void RenderViewCreated(content::RenderView*) override; + private: DISALLOW_COPY_AND_ASSIGN(AtomSandboxedRendererClient); };