// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "content/renderer/web_ui_mojo.h" #include "content/common/view_messages.h" #include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_view.h" #include "content/renderer/web_ui_mojo_context_state.h" #include "gin/per_context_data.h" #include "third_party/WebKit/public/web/WebKit.h" #include "third_party/WebKit/public/web/WebLocalFrame.h" #include "third_party/WebKit/public/web/WebView.h" #include "v8/include/v8.h" namespace content { namespace { const char kWebUIMojoContextStateKey[] = "WebUIMojoContextState"; struct WebUIMojoContextStateData : public base::SupportsUserData::Data { scoped_ptr<WebUIMojoContextState> state; }; } // namespace WebUIMojo::MainFrameObserver::MainFrameObserver(WebUIMojo* web_ui_mojo) : RenderFrameObserver(RenderFrame::FromWebFrame( web_ui_mojo->render_view()->GetWebView()->mainFrame())), web_ui_mojo_(web_ui_mojo) { } WebUIMojo::MainFrameObserver::~MainFrameObserver() { } void WebUIMojo::MainFrameObserver::WillReleaseScriptContext( v8::Handle<v8::Context> context, int world_id) { web_ui_mojo_->DestroyContextState(context); } void WebUIMojo::MainFrameObserver::DidFinishDocumentLoad() { web_ui_mojo_->OnDidFinishDocumentLoad(); } WebUIMojo::WebUIMojo(RenderView* render_view) : RenderViewObserver(render_view), RenderViewObserverTracker<WebUIMojo>(render_view), main_frame_observer_(this), did_finish_document_load_(false) { CreateContextState(); } void WebUIMojo::SetBrowserHandle(mojo::ScopedMessagePipeHandle handle) { if (did_finish_document_load_) SetHandleOnContextState(handle.Pass()); else pending_handle_ = handle.Pass(); } WebUIMojo::~WebUIMojo() { } void WebUIMojo::CreateContextState() { v8::HandleScope handle_scope(blink::mainThreadIsolate()); blink::WebLocalFrame* frame = render_view()->GetWebView()->mainFrame()->toWebLocalFrame(); v8::Handle<v8::Context> context = frame->mainWorldScriptContext(); gin::PerContextData* context_data = gin::PerContextData::From(context); WebUIMojoContextStateData* data = new WebUIMojoContextStateData; data->state.reset(new WebUIMojoContextState( render_view()->GetWebView()->mainFrame(), context)); context_data->SetUserData(kWebUIMojoContextStateKey, data); } void WebUIMojo::DestroyContextState(v8::Handle<v8::Context> context) { gin::PerContextData* context_data = gin::PerContextData::From(context); if (!context_data) return; context_data->RemoveUserData(kWebUIMojoContextStateKey); } void WebUIMojo::OnDidFinishDocumentLoad() { did_finish_document_load_ = true; if (pending_handle_.is_valid()) SetHandleOnContextState(pending_handle_.Pass()); } void WebUIMojo::SetHandleOnContextState(mojo::ScopedMessagePipeHandle handle) { DCHECK(did_finish_document_load_); v8::HandleScope handle_scope(blink::mainThreadIsolate()); WebUIMojoContextState* state = GetContextState(); if (state) state->SetHandle(handle.Pass()); } WebUIMojoContextState* WebUIMojo::GetContextState() { blink::WebLocalFrame* frame = render_view()->GetWebView()->mainFrame()->toWebLocalFrame(); v8::HandleScope handle_scope(blink::mainThreadIsolate()); v8::Handle<v8::Context> context = frame->mainWorldScriptContext(); gin::PerContextData* context_data = gin::PerContextData::From(context); if (!context_data) return NULL; WebUIMojoContextStateData* context_state = static_cast<WebUIMojoContextStateData*>( context_data->GetUserData(kWebUIMojoContextStateKey)); return context_state ? context_state->state.get() : NULL; } void WebUIMojo::DidClearWindowObject(blink::WebLocalFrame* frame) { if (frame != render_view()->GetWebView()->mainFrame()) return; // NOTE: this function may be called early on twice. From the constructor // mainWorldScriptContext() may trigger this to be called. If we are created // before the page is loaded (which is very likely), then on first load this // is called. In the case of the latter we may have already supplied the // handle to the context state so that if we destroy now the handle is // lost. If this is the result of the first load then the contextstate should // be empty and we don't need to destroy it. WebUIMojoContextState* state = GetContextState(); if (state && !state->module_added()) return; v8::HandleScope handle_scope(blink::mainThreadIsolate()); DestroyContextState(frame->mainWorldScriptContext()); CreateContextState(); } } // namespace content