1 // 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 5 #include "content/renderer/web_ui_mojo_context_state.h" 6 7 #include "base/bind.h" 8 #include "base/stl_util.h" 9 #include "content/public/renderer/resource_fetcher.h" 10 #include "content/renderer/web_ui_runner.h" 11 #include "gin/converter.h" 12 #include "gin/modules/module_registry.h" 13 #include "gin/per_context_data.h" 14 #include "gin/public/context_holder.h" 15 #include "gin/try_catch.h" 16 #include "mojo/bindings/js/core.h" 17 #include "mojo/bindings/js/handle.h" 18 #include "mojo/bindings/js/support.h" 19 #include "third_party/WebKit/public/platform/WebURLResponse.h" 20 #include "third_party/WebKit/public/web/WebFrame.h" 21 #include "third_party/WebKit/public/web/WebScriptSource.h" 22 23 using v8::Context; 24 using v8::HandleScope; 25 using v8::Isolate; 26 using v8::Object; 27 using v8::ObjectTemplate; 28 using v8::Script; 29 30 namespace content { 31 32 namespace { 33 34 // All modules have this prefixed to them when downloading. 35 // TODO(sky): move this into some common place. 36 const char kModulePrefix[] = "chrome://mojo/"; 37 38 void RunMain(base::WeakPtr<gin::Runner> runner, 39 mojo::ScopedMessagePipeHandle* handle, 40 v8::Handle<v8::Value> module) { 41 v8::Isolate* isolate = runner->GetContextHolder()->isolate(); 42 v8::Handle<v8::Function> start; 43 CHECK(gin::ConvertFromV8(isolate, module, &start)); 44 v8::Handle<v8::Value> args[] = { 45 gin::ConvertToV8(isolate, mojo::Handle(handle->release().value())) }; 46 runner->Call(start, runner->global(), 1, args); 47 } 48 49 } // namespace 50 51 // WebUIMojo ------------------------------------------------------------------- 52 53 WebUIMojoContextState::WebUIMojoContextState(blink::WebFrame* frame, 54 v8::Handle<v8::Context> context) 55 : frame_(frame), 56 module_added_(false) { 57 gin::PerContextData* context_data = gin::PerContextData::From(context); 58 gin::ContextHolder* context_holder = context_data->context_holder(); 59 runner_.reset(new WebUIRunner(frame_, context_holder)); 60 gin::Runner::Scope scoper(runner_.get()); 61 gin::ModuleRegistry::From(context)->AddObserver(this); 62 runner_->RegisterBuiltinModules(); 63 gin::ModuleRegistry::InstallGlobals(context->GetIsolate(), context->Global()); 64 // Warning |frame| may be destroyed. 65 // TODO(sky): add test for this. 66 } 67 68 WebUIMojoContextState::~WebUIMojoContextState() { 69 gin::Runner::Scope scoper(runner_.get()); 70 gin::ModuleRegistry::From( 71 runner_->GetContextHolder()->context())->RemoveObserver(this); 72 } 73 74 void WebUIMojoContextState::SetHandle(mojo::ScopedMessagePipeHandle handle) { 75 gin::ContextHolder* context_holder = runner_->GetContextHolder(); 76 mojo::ScopedMessagePipeHandle* passed_handle = 77 new mojo::ScopedMessagePipeHandle(handle.Pass()); 78 gin::ModuleRegistry::From(context_holder->context())->LoadModule( 79 context_holder->isolate(), 80 "main", 81 base::Bind(RunMain, runner_->GetWeakPtr(), base::Owned(passed_handle))); 82 } 83 84 void WebUIMojoContextState::FetchModules(const std::vector<std::string>& ids) { 85 gin::Runner::Scope scoper(runner_.get()); 86 gin::ContextHolder* context_holder = runner_->GetContextHolder(); 87 gin::ModuleRegistry* registry = gin::ModuleRegistry::From( 88 context_holder->context()); 89 for (size_t i = 0; i < ids.size(); ++i) { 90 if (fetched_modules_.find(ids[i]) == fetched_modules_.end() && 91 registry->available_modules().count(ids[i]) == 0) { 92 FetchModule(ids[i]); 93 } 94 } 95 } 96 97 void WebUIMojoContextState::FetchModule(const std::string& id) { 98 const GURL url(kModulePrefix + id); 99 // TODO(sky): better error checks here? 100 DCHECK(url.is_valid() && !url.is_empty()); 101 DCHECK(fetched_modules_.find(id) == fetched_modules_.end()); 102 fetched_modules_.insert(id); 103 ResourceFetcher* fetcher = ResourceFetcher::Create(url); 104 module_fetchers_.push_back(fetcher); 105 fetcher->Start(frame_, blink::WebURLRequest::TargetIsScript, 106 base::Bind(&WebUIMojoContextState::OnFetchModuleComplete, 107 base::Unretained(this), fetcher)); 108 } 109 110 void WebUIMojoContextState::OnFetchModuleComplete( 111 ResourceFetcher* fetcher, 112 const blink::WebURLResponse& response, 113 const std::string& data) { 114 DCHECK_EQ(kModulePrefix, 115 response.url().string().utf8().substr(0, arraysize(kModulePrefix) - 1)); 116 const std::string module = 117 response.url().string().utf8().substr(arraysize(kModulePrefix) - 1); 118 // We can't delete fetch right now as the arguments to this function come from 119 // it and are used below. Instead use a scope_ptr to cleanup. 120 scoped_ptr<ResourceFetcher> deleter(fetcher); 121 module_fetchers_.weak_erase( 122 std::find(module_fetchers_.begin(), module_fetchers_.end(), fetcher)); 123 if (data.empty()) { 124 NOTREACHED(); 125 return; // TODO(sky): log something? 126 } 127 128 runner_->Run(data, module); 129 } 130 131 void WebUIMojoContextState::OnDidAddPendingModule( 132 const std::string& id, 133 const std::vector<std::string>& dependencies) { 134 FetchModules(dependencies); 135 136 gin::ContextHolder* context_holder = runner_->GetContextHolder(); 137 gin::ModuleRegistry* registry = gin::ModuleRegistry::From( 138 context_holder->context()); 139 registry->AttemptToLoadMoreModules(context_holder->isolate()); 140 } 141 142 } // namespace content 143