Run Tizen Webapps in single process mode
[platform/framework/web/crosswalk-tizen.git] / atom / renderer / atom_renderer_client.cc
1 // Copyright (c) 2013 GitHub, Inc.
2 // Use of this source code is governed by the MIT license that can be
3 // found in the LICENSE file.
4
5 #include "atom/renderer/atom_renderer_client.h"
6
7 #include <string>
8 #include <vector>
9
10 #if defined(USE_EFL)
11 #include "atom/common/atom_command_line.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "content/common/wrt/wrt_url_parse.h"
14 #include "content/public/renderer/render_thread.h"
15 #include "content/public/renderer/render_view.h"
16 #include "tizen_src/ewk/efl_integration/wrt/wrtwidget.h"
17 #include "third_party/WebKit/public/web/WebView.h"
18 #endif
19 #include "atom_natives.h"  // NOLINT: This file is generated with js2c
20
21 #include "atom/common/api/atom_bindings.h"
22 #include "atom/common/api/event_emitter_caller.h"
23 #include "atom/common/asar/asar_util.h"
24 #include "atom/common/atom_constants.h"
25 #include "atom/common/node_bindings.h"
26 #include "atom/common/options_switches.h"
27 #include "atom/renderer/api/atom_api_renderer_ipc.h"
28 #include "atom/renderer/atom_render_frame_observer.h"
29 #include "atom/renderer/atom_render_view_observer.h"
30 #include "atom/renderer/node_array_buffer_bridge.h"
31 #include "atom/renderer/web_worker_observer.h"
32 #include "base/command_line.h"
33 #include "content/public/renderer/render_frame.h"
34 #include "native_mate/dictionary.h"
35 #include "third_party/WebKit/public/web/WebDocument.h"
36 #include "third_party/WebKit/public/web/WebLocalFrame.h"
37 #include "tizen/common/env_variables.h"
38
39 #include "atom/common/node_includes.h"
40
41 namespace atom {
42
43 namespace {
44
45 bool IsDevToolsExtension(content::RenderFrame* render_frame) {
46   return static_cast<GURL>(render_frame->GetWebFrame()->document().url())
47       .SchemeIs("chrome-extension");
48 }
49
50 #if defined(USE_EFL)
51 //scoped_ptr<const char* []> StringVectorToArgArray(
52 const char** StringVectorToArgArray(
53     const std::vector<std::string>& vector) {
54   const char** array = new const char*[vector.size()];
55   for (size_t i = 0; i < vector.size(); ++i)
56     array[i] = vector[i].c_str();
57   return array;
58 }
59 #endif
60
61 }  // namespace
62
63 AtomRendererClient::AtomRendererClient()
64     : node_integration_initialized_(false) {
65   if (!::tizen::is_single_process) {
66     node_bindings_.reset(NodeBindings::Create(NodeBindings::RENDERER));
67     atom_bindings_.reset(new AtomBindings(node_bindings_->uv_loop()));
68   }
69   isolated_world_ = base::CommandLine::ForCurrentProcess()->HasSwitch(
70       switches::kContextIsolation);
71 }
72
73 AtomRendererClient::~AtomRendererClient() {
74   asar::ClearArchives();
75 }
76
77 void AtomRendererClient::RenderThreadStarted() {
78 #if defined(USE_EFL)
79   // In case of Chromium-efl, AtomMain is not called when renderer process
80   // is executed, because it is executed by zygote.
81   // So we need to initialize AtomCommandLine at the other place.
82   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
83   base::CommandLine::StringVector argv = command_line->argv();
84   const char** c_argv = StringVectorToArgArray(argv);
85   atom::AtomCommandLine::Init(argv.size(), c_argv);
86   std::string tizen_app_id = command_line->GetSwitchValueASCII(
87       "widget-id");
88
89   WrtWidget* wrt_widget = new WrtWidget;
90   if (wrt_widget == nullptr) {
91     return;
92   }
93   widget_.reset(wrt_widget);
94   content::RenderThread* thread = content::RenderThread::Get();
95   thread->AddObserver(wrt_widget->GetObserver());
96   std::string theme = command_line->GetSwitchValueASCII("widget-theme");
97   std::string encoded_bundle = command_line->GetSwitchValueASCII("widget-encoded-bundle");
98   std::string scale = command_line->GetSwitchValueASCII("widget-scale");
99   double scale_factor = 0;
100   base::StringToDouble(scale, &scale_factor);
101   wrt_widget->SetWidgetInfo(tizen_app_id, scale_factor, theme,
102                             encoded_bundle);
103 #endif
104   OverrideNodeArrayBuffer();
105   RendererClientBase::RenderThreadStarted();
106 }
107
108 void AtomRendererClient::RenderFrameCreated(
109     content::RenderFrame* render_frame) {
110   RendererClientBase::RenderFrameCreated(render_frame);
111 }
112
113 void AtomRendererClient::RenderViewCreated(content::RenderView* render_view) {
114   new AtomRenderViewObserver(render_view, this);
115   RendererClientBase::RenderViewCreated(render_view);
116 }
117
118 void AtomRendererClient::RunScriptsAtDocumentStart(
119     content::RenderFrame* render_frame) {
120   if (::tizen::is_single_process)
121     return;
122   // Inform the document start pharse.
123   node::Environment* env = node_bindings_->uv_env();
124   if (env) {
125     v8::HandleScope handle_scope(env->isolate());
126     mate::EmitEvent(env->isolate(), env->process_object(), "document-start");
127   }
128 }
129
130 void AtomRendererClient::RunScriptsAtDocumentEnd(
131     content::RenderFrame* render_frame) {
132   if (::tizen::is_single_process)
133     return;
134   // Inform the document end pharse.
135   node::Environment* env = node_bindings_->uv_env();
136   if (env) {
137     v8::HandleScope handle_scope(env->isolate());
138     mate::EmitEvent(env->isolate(), env->process_object(), "document-end");
139   }
140 }
141
142 void AtomRendererClient::DidCreateScriptContext(
143     v8::Handle<v8::Context> context, content::RenderFrame* render_frame) {
144   // Tizen device API is required both for main frame and iFrames
145 #if defined(USE_EFL)
146   if (widget_) {
147     const content::RenderView* render_view = render_frame->GetRenderView();
148     widget_->StartSession(context, render_view->GetRoutingID(),
149                         render_frame->GetWebFrame()->document().baseURL().string().utf8().c_str());
150   }
151 #endif
152   if (::tizen::is_single_process)
153     return;
154   // Only allow node integration for the main frame, unless it is a devtools
155   // extension page.
156   if (!render_frame->IsMainFrame() && !IsDevToolsExtension(render_frame))
157     return;
158
159   // Prepare the node bindings.
160   if (!node_integration_initialized_) {
161     node_integration_initialized_ = true;
162     node_bindings_->Initialize();
163     node_bindings_->PrepareMessageLoop();
164   }
165
166   // Setup node environment for each window.
167   node::Environment* env = node_bindings_->CreateEnvironment(context);
168
169   // Add Electron extended APIs.
170   atom_bindings_->BindTo(env->isolate(), env->process_object());
171   AddRenderBindings(env->isolate(), env->process_object());
172
173   // Load everything.
174   node_bindings_->LoadEnvironment(env);
175
176   if (node_bindings_->uv_env() == nullptr) {
177     // Make uv loop being wrapped by window context.
178     node_bindings_->set_uv_env(env);
179
180     // Give the node loop a run to make sure everything is ready.
181     node_bindings_->RunMessageLoop();
182   }
183 }
184
185 void AtomRendererClient::WillReleaseScriptContext(
186     v8::Handle<v8::Context> context, content::RenderFrame* render_frame) {
187   // Required both for main/sub and iframe
188 #if defined(USE_EFL)
189   if (widget_)
190     widget_->StopSession(context);
191 #endif
192   if (::tizen::is_single_process)
193     return;
194   // Only allow node integration for the main frame, unless it is a devtools
195   // extension page.
196   if (!render_frame->IsMainFrame() && !IsDevToolsExtension(render_frame))
197     return;
198
199   node::Environment* env = node::Environment::GetCurrent(context);
200   if (env)
201     mate::EmitEvent(env->isolate(), env->process_object(), "exit");
202
203   // The main frame may be replaced.
204   if (env == node_bindings_->uv_env())
205     node_bindings_->set_uv_env(nullptr);
206
207   // Destroy the node environment.
208   node::FreeEnvironment(env);
209   atom_bindings_->EnvironmentDestroyed(env);
210 }
211
212 bool AtomRendererClient::ShouldFork(blink::WebLocalFrame* frame,
213                                     const GURL& url,
214                                     const std::string& http_method,
215                                     bool is_initial_navigation,
216                                     bool is_server_redirect,
217                                     bool* send_referrer) {
218   // Handle all the navigations and reloads in browser.
219   // FIXME We only support GET here because http method will be ignored when
220   // the OpenURLFromTab is triggered, which means form posting would not work,
221   // we should solve this by patching Chromium in future.
222   *send_referrer = true;
223   return http_method == "GET";
224 }
225
226 void AtomRendererClient::DidInitializeWorkerContextOnWorkerThread(
227     v8::Local<v8::Context> context) {
228   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
229           switches::kNodeIntegrationInWorker)) {
230     WebWorkerObserver::GetCurrent()->ContextCreated(context);
231   }
232 }
233
234 void AtomRendererClient::WillDestroyWorkerContextOnWorkerThread(
235     v8::Local<v8::Context> context) {
236   if (base::CommandLine::ForCurrentProcess()->HasSwitch(
237           switches::kNodeIntegrationInWorker)) {
238     WebWorkerObserver::GetCurrent()->ContextWillDestroy(context);
239   }
240 }
241
242 v8::Local<v8::Context> AtomRendererClient::GetContext(
243     blink::WebFrame* frame, v8::Isolate* isolate) {
244   if (isolated_world())
245     return frame->worldScriptContext(
246         isolate, World::ISOLATED_WORLD, ExtensionGroup::MAIN_GROUP);
247   else
248     return frame->mainWorldScriptContext();
249 }
250
251 void AtomRendererClient::SetupMainWorldOverrides(
252     v8::Handle<v8::Context> context) {
253   // Setup window overrides in the main world context
254   v8::Isolate* isolate = context->GetIsolate();
255
256   // Wrap the bundle into a function that receives the binding object as
257   // an argument.
258   std::string bundle(node::isolated_bundle_data,
259       node::isolated_bundle_data + sizeof(node::isolated_bundle_data));
260   std::string wrapper = "(function (binding, require) {\n" + bundle + "\n})";
261   auto script = v8::Script::Compile(
262       mate::ConvertToV8(isolate, wrapper)->ToString());
263   auto func = v8::Handle<v8::Function>::Cast(
264       script->Run(context).ToLocalChecked());
265
266   auto binding = v8::Object::New(isolate);
267   api::Initialize(binding, v8::Null(isolate), context, nullptr);
268
269   // Pass in CLI flags needed to setup window
270   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
271   mate::Dictionary dict(isolate, binding);
272   if (command_line->HasSwitch(switches::kGuestInstanceID))
273     dict.Set(options::kGuestInstanceID,
274              command_line->GetSwitchValueASCII(switches::kGuestInstanceID));
275   if (command_line->HasSwitch(switches::kOpenerID))
276     dict.Set(options::kOpenerID,
277              command_line->GetSwitchValueASCII(switches::kOpenerID));
278   dict.Set("hiddenPage", command_line->HasSwitch(switches::kHiddenPage));
279
280   v8::Local<v8::Value> args[] = { binding };
281   ignore_result(func->Call(context, v8::Null(isolate), 1, args));
282 }
283
284
285 }  // namespace atom