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