blob: c9cae485740401a8a42c9bfc5f4e3a2836bd7fcf [file] [log] [blame]
[email protected]4fa18202014-03-13 06:19:261// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
rockot4a038f92015-11-24 21:52:465#include "content/renderer/mojo_context_state.h"
[email protected]4fa18202014-03-13 06:19:266
avi1023d012015-12-25 02:39:147#include <stddef.h>
8
rockot8df5c722016-01-26 07:15:149#include <map>
10#include <string>
11
[email protected]4fa18202014-03-13 06:19:2612#include "base/bind.h"
rockot8df5c722016-01-26 07:15:1413#include "base/lazy_instance.h"
leon.han21e0e482017-02-23 04:13:3214#include "base/memory/ptr_util.h"
rockot8df5c722016-01-26 07:15:1415#include "base/memory/ref_counted.h"
16#include "base/memory/ref_counted_memory.h"
[email protected]4fa18202014-03-13 06:19:2617#include "base/stl_util.h"
rockot8df5c722016-01-26 07:15:1418#include "content/grit/content_resources.h"
Takashi Toyoshima2796e7e2017-08-31 08:13:0119#include "content/public/child/child_url_loader_factory_getter.h"
rockot8df5c722016-01-26 07:15:1420#include "content/public/common/content_client.h"
sammc853eb9c2014-10-14 16:27:5921#include "content/public/renderer/render_frame.h"
[email protected]4fa18202014-03-13 06:19:2622#include "content/public/renderer/resource_fetcher.h"
alexclarkea50533ee2016-07-29 11:34:4823#include "content/renderer/mojo_bindings_controller.h"
rockot4a038f92015-11-24 21:52:4624#include "content/renderer/mojo_main_runner.h"
[email protected]4fa18202014-03-13 06:19:2625#include "gin/converter.h"
26#include "gin/modules/module_registry.h"
27#include "gin/per_context_data.h"
28#include "gin/public/context_holder.h"
29#include "gin/try_catch.h"
rockot8df5c722016-01-26 07:15:1430#include "mojo/public/js/constants.h"
mek966863c2016-02-04 23:39:0531#include "third_party/WebKit/public/platform/WebSecurityOrigin.h"
[email protected]4fa18202014-03-13 06:19:2632#include "third_party/WebKit/public/platform/WebURLResponse.h"
yhirano505d1e62017-05-08 10:37:3533#include "third_party/WebKit/public/web/WebLocalFrame.h"
[email protected]4fa18202014-03-13 06:19:2634#include "third_party/WebKit/public/web/WebScriptSource.h"
35
36using v8::Context;
37using v8::HandleScope;
38using v8::Isolate;
39using v8::Object;
40using v8::ObjectTemplate;
41using v8::Script;
42
43namespace content {
44
45namespace {
46
[email protected]2b578402014-07-29 19:51:3647void RunMain(base::WeakPtr<gin::Runner> runner,
deepak.s750d68f2015-04-30 07:32:4148 v8::Local<v8::Value> module) {
[email protected]2b578402014-07-29 19:51:3649 v8::Isolate* isolate = runner->GetContextHolder()->isolate();
deepak.s750d68f2015-04-30 07:32:4150 v8::Local<v8::Function> start;
[email protected]2b578402014-07-29 19:51:3651 CHECK(gin::ConvertFromV8(isolate, module, &start));
[email protected]420c1ba12014-07-30 09:54:0752 runner->Call(start, runner->global(), 0, NULL);
[email protected]2b578402014-07-29 19:51:3653}
54
rockot8df5c722016-01-26 07:15:1455using ModuleSourceMap =
56 std::map<std::string, scoped_refptr<base::RefCountedMemory>>;
57
dchengcedca5612016-04-09 01:40:1558base::LazyInstance<std::unique_ptr<ModuleSourceMap>>::Leaky g_module_sources;
rockot8df5c722016-01-26 07:15:1459
60scoped_refptr<base::RefCountedMemory> GetBuiltinModuleData(
61 const std::string& path) {
62 static const struct {
63 const char* path;
64 const int id;
65 } kBuiltinModuleResources[] = {
wangjimmy336e07d2017-04-13 18:46:3266 {mojo::kAssociatedBindingsModuleName, IDR_MOJO_ASSOCIATED_BINDINGS_JS},
wangjimmy43ca78b2017-02-14 22:08:2667 {mojo::kBindingsModuleName, IDR_MOJO_BINDINGS_JS},
68 {mojo::kBufferModuleName, IDR_MOJO_BUFFER_JS},
69 {mojo::kCodecModuleName, IDR_MOJO_CODEC_JS},
70 {mojo::kConnectorModuleName, IDR_MOJO_CONNECTOR_JS},
71 {mojo::kControlMessageHandlerModuleName,
72 IDR_MOJO_CONTROL_MESSAGE_HANDLER_JS},
73 {mojo::kControlMessageProxyModuleName, IDR_MOJO_CONTROL_MESSAGE_PROXY_JS},
74 {mojo::kInterfaceControlMessagesMojom,
75 IDR_MOJO_INTERFACE_CONTROL_MESSAGES_MOJOM_JS},
wangjimmy087549e2017-03-30 17:49:5776 {mojo::kInterfaceEndpointClientModuleName,
77 IDR_MOJO_INTERFACE_ENDPOINT_CLIENT_JS},
78 {mojo::kInterfaceEndpointHandleModuleName,
79 IDR_MOJO_INTERFACE_ENDPOINT_HANDLE_JS},
wangjimmy43ca78b2017-02-14 22:08:2680 {mojo::kInterfaceTypesModuleName, IDR_MOJO_INTERFACE_TYPES_JS},
wangjimmy087549e2017-03-30 17:49:5781 {mojo::kPipeControlMessageHandlerModuleName,
82 IDR_MOJO_PIPE_CONTROL_MESSAGE_HANDLER_JS},
83 {mojo::kPipeControlMessageProxyModuleName,
84 IDR_MOJO_PIPE_CONTROL_MESSAGE_PROXY_JS},
85 {mojo::kPipeControlMessagesMojom,
86 IDR_MOJO_PIPE_CONTROL_MESSAGES_MOJOM_JS},
wangjimmy43ca78b2017-02-14 22:08:2687 {mojo::kRouterModuleName, IDR_MOJO_ROUTER_JS},
88 {mojo::kUnicodeModuleName, IDR_MOJO_UNICODE_JS},
89 {mojo::kValidatorModuleName, IDR_MOJO_VALIDATOR_JS},
rockot8df5c722016-01-26 07:15:1490 };
91
dchengcedca5612016-04-09 01:40:1592 std::unique_ptr<ModuleSourceMap>& module_sources = g_module_sources.Get();
rockot8df5c722016-01-26 07:15:1493 if (!module_sources) {
94 // Initialize the module source map on first access.
95 module_sources.reset(new ModuleSourceMap);
96 for (size_t i = 0; i < arraysize(kBuiltinModuleResources); ++i) {
97 const auto& resource = kBuiltinModuleResources[i];
98 scoped_refptr<base::RefCountedMemory> data =
99 GetContentClient()->GetDataResourceBytes(resource.id);
100 DCHECK_GT(data->size(), 0u);
101 module_sources->insert(std::make_pair(std::string(resource.path), data));
102 }
103 }
104
105 DCHECK(module_sources);
106 auto source_iter = module_sources->find(path);
107 if (source_iter == module_sources->end())
108 return nullptr;
109 return source_iter->second;
110}
111
alexclarkea50533ee2016-07-29 11:34:48112std::string GetModulePrefixForBindingsType(MojoBindingsType bindings_type,
yhirano505d1e62017-05-08 10:37:35113 blink::WebLocalFrame* frame) {
alexclarkea50533ee2016-07-29 11:34:48114 switch (bindings_type) {
115 case MojoBindingsType::FOR_WEB_UI:
Blink Reformat1c4d759e2017-04-09 16:34:54116 return frame->GetSecurityOrigin().ToString().Utf8() + "/";
alexclarkea50533ee2016-07-29 11:34:48117 case MojoBindingsType::FOR_LAYOUT_TESTS:
118 return "layout-test-mojom://";
alexclarkea50533ee2016-07-29 11:34:48119 }
120 NOTREACHED();
121 return "";
122}
123
[email protected]4fa18202014-03-13 06:19:26124} // namespace
125
yhirano505d1e62017-05-08 10:37:35126MojoContextState::MojoContextState(blink::WebLocalFrame* frame,
rockot8df5c722016-01-26 07:15:14127 v8::Local<v8::Context> context,
alexclarkea50533ee2016-07-29 11:34:48128 MojoBindingsType bindings_type)
[email protected]4fa18202014-03-13 06:19:26129 : frame_(frame),
sammcafd813ed2015-11-30 01:46:46130 module_added_(false),
alexclarkea50533ee2016-07-29 11:34:48131 module_prefix_(GetModulePrefixForBindingsType(bindings_type, frame)) {
[email protected]4fa18202014-03-13 06:19:26132 gin::PerContextData* context_data = gin::PerContextData::From(context);
133 gin::ContextHolder* context_holder = context_data->context_holder();
rockot4a038f92015-11-24 21:52:46134 runner_.reset(new MojoMainRunner(frame_, context_holder));
[email protected]4fa18202014-03-13 06:19:26135 gin::Runner::Scope scoper(runner_.get());
136 gin::ModuleRegistry::From(context)->AddObserver(this);
sammc853eb9c2014-10-14 16:27:59137 content::RenderFrame::FromWebFrame(frame)
138 ->EnsureMojoBuiltinsAreAvailable(context_holder->isolate(), context);
rockot8df5c722016-01-26 07:15:14139 v8::Local<v8::Object> install_target;
alexclarkea50533ee2016-07-29 11:34:48140 if (bindings_type == MojoBindingsType::FOR_LAYOUT_TESTS) {
alokpa3aeda6b2017-01-20 01:30:17141 // In layout tests we install the module system under 'gin.define'
rockot8df5c722016-01-26 07:15:14142 // for now to avoid globally exposing something as generic as 'define'.
143 //
144 // TODO(rockot): Remove this if/when we can integrate gin + ES6 modules.
145 install_target = v8::Object::New(context->GetIsolate());
146 gin::SetProperty(context->GetIsolate(), context->Global(),
alokpa3aeda6b2017-01-20 01:30:17147 gin::StringToSymbol(context->GetIsolate(), "gin"),
rockot8df5c722016-01-26 07:15:14148 install_target);
149 } else {
150 // Otherwise we're fine installing a global 'define'.
151 install_target = context->Global();
152 }
153 gin::ModuleRegistry::InstallGlobals(context->GetIsolate(), install_target);
[email protected]4fa18202014-03-13 06:19:26154 // Warning |frame| may be destroyed.
155 // TODO(sky): add test for this.
156}
157
rockot4a038f92015-11-24 21:52:46158MojoContextState::~MojoContextState() {
[email protected]4fa18202014-03-13 06:19:26159 gin::Runner::Scope scoper(runner_.get());
160 gin::ModuleRegistry::From(
161 runner_->GetContextHolder()->context())->RemoveObserver(this);
162}
163
rockot4a038f92015-11-24 21:52:46164void MojoContextState::Run() {
[email protected]4fa18202014-03-13 06:19:26165 gin::ContextHolder* context_holder = runner_->GetContextHolder();
[email protected]4fa18202014-03-13 06:19:26166 gin::ModuleRegistry::From(context_holder->context())->LoadModule(
167 context_holder->isolate(),
168 "main",
[email protected]420c1ba12014-07-30 09:54:07169 base::Bind(RunMain, runner_->GetWeakPtr()));
[email protected]4fa18202014-03-13 06:19:26170}
171
rockot4a038f92015-11-24 21:52:46172void MojoContextState::FetchModules(const std::vector<std::string>& ids) {
[email protected]4fa18202014-03-13 06:19:26173 gin::Runner::Scope scoper(runner_.get());
174 gin::ContextHolder* context_holder = runner_->GetContextHolder();
175 gin::ModuleRegistry* registry = gin::ModuleRegistry::From(
176 context_holder->context());
177 for (size_t i = 0; i < ids.size(); ++i) {
178 if (fetched_modules_.find(ids[i]) == fetched_modules_.end() &&
179 registry->available_modules().count(ids[i]) == 0) {
rockot8df5c722016-01-26 07:15:14180 scoped_refptr<base::RefCountedMemory> data = GetBuiltinModuleData(ids[i]);
181 if (data)
182 runner_->Run(std::string(data->front_as<char>(), data->size()), ids[i]);
183 else
184 FetchModule(ids[i]);
[email protected]4fa18202014-03-13 06:19:26185 }
186 }
187}
188
rockot4a038f92015-11-24 21:52:46189void MojoContextState::FetchModule(const std::string& id) {
Takashi Toyoshima2796e7e2017-08-31 08:13:01190 static const net::NetworkTrafficAnnotationTag network_traffic_annotation_tag =
191 net::DefineNetworkTrafficAnnotation("mojo_context_state", R"(
192 semantics {
193 sender: "MojoContextState"
194 description:
195 "Chrome does fetch for AMD-style module loading of Mojo JavaScript "
196 "bindings."
197 trigger:
198 "When AMD-style module loading of Mojo JavaScript bindings is used."
199 data:
200 "Load JavaScript files for Mojo bindings from embedded resources. "
201 "Nothing is sent over networks."
202 destination: OTHER
203 }
204 policy {
205 cookies_allowed: NO
206 setting: "These requests cannot be disabled in settings."
207 policy_exception_justification:
208 "Not implemented. Without these requests, Chrome will not work."
209 })");
210
sammcafd813ed2015-11-30 01:46:46211 const GURL url(module_prefix_ + id);
[email protected]4fa18202014-03-13 06:19:26212 // TODO(sky): better error checks here?
213 DCHECK(url.is_valid() && !url.is_empty());
214 DCHECK(fetched_modules_.find(id) == fetched_modules_.end());
215 fetched_modules_.insert(id);
Takashi Toyoshimabeba9322017-09-07 06:27:14216 module_fetchers_.push_back(ResourceFetcher::Create(url));
217 module_fetchers_.back()->Start(
218 frame_, blink::WebURLRequest::kRequestContextScript,
219 RenderFrame::FromWebFrame(frame_)
220 ->GetDefaultURLLoaderFactoryGetter()
221 ->GetNetworkLoaderFactory(),
222 network_traffic_annotation_tag,
223 base::BindOnce(&MojoContextState::OnFetchModuleComplete,
224 base::Unretained(this), module_fetchers_.back().get(),
225 id));
[email protected]4fa18202014-03-13 06:19:26226}
227
rockot4a038f92015-11-24 21:52:46228void MojoContextState::OnFetchModuleComplete(
[email protected]4fa18202014-03-13 06:19:26229 ResourceFetcher* fetcher,
sammc2eb97bd2016-03-11 05:24:39230 const std::string& id,
[email protected]4fa18202014-03-13 06:19:26231 const blink::WebURLResponse& response,
232 const std::string& data) {
Blink Reformat1c4d759e2017-04-09 16:34:54233 if (response.IsNull()) {
sammc2eb97bd2016-03-11 05:24:39234 LOG(ERROR) << "Failed to fetch source for module \"" << id << "\"";
235 return;
236 }
Blink Reformat1c4d759e2017-04-09 16:34:54237 DCHECK_EQ(module_prefix_ + id, response.Url().GetString().Utf8());
[email protected]4fa18202014-03-13 06:19:26238 // We can't delete fetch right now as the arguments to this function come from
239 // it and are used below. Instead use a scope_ptr to cleanup.
leon.han21e0e482017-02-23 04:13:32240 auto iter =
241 std::find_if(module_fetchers_.begin(), module_fetchers_.end(),
242 [fetcher](const std::unique_ptr<ResourceFetcher>& item) {
243 return item.get() == fetcher;
244 });
245 std::unique_ptr<ResourceFetcher> deleter = std::move(*iter);
246 module_fetchers_.erase(iter);
247
[email protected]4fa18202014-03-13 06:19:26248 if (data.empty()) {
sammc2eb97bd2016-03-11 05:24:39249 LOG(ERROR) << "Fetched empty source for module \"" << id << "\"";
250 return;
[email protected]4fa18202014-03-13 06:19:26251 }
252
sammc2eb97bd2016-03-11 05:24:39253 runner_->Run(data, id);
[email protected]4fa18202014-03-13 06:19:26254}
255
rockot4a038f92015-11-24 21:52:46256void MojoContextState::OnDidAddPendingModule(
[email protected]4fa18202014-03-13 06:19:26257 const std::string& id,
258 const std::vector<std::string>& dependencies) {
259 FetchModules(dependencies);
260
261 gin::ContextHolder* context_holder = runner_->GetContextHolder();
262 gin::ModuleRegistry* registry = gin::ModuleRegistry::From(
263 context_holder->context());
264 registry->AttemptToLoadMoreModules(context_holder->isolate());
265}
266
267} // namespace content