| // Copyright 2015 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 "components/html_viewer/html_frame.h" |
| |
| #include <algorithm> |
| #include <limits> |
| |
| #include "base/bind.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_util.h" |
| #include "base/thread_task_runner_handle.h" |
| #include "cc/blink/web_layer_impl.h" |
| #include "cc/surfaces/surface_id.h" |
| #include "components/html_viewer/ax_provider_impl.h" |
| #include "components/html_viewer/blink_basic_type_converters.h" |
| #include "components/html_viewer/blink_input_events_type_converters.h" |
| #include "components/html_viewer/blink_text_input_type_converters.h" |
| #include "components/html_viewer/blink_url_request_type_converters.h" |
| #include "components/html_viewer/devtools_agent_impl.h" |
| #include "components/html_viewer/geolocation_client_impl.h" |
| #include "components/html_viewer/global_state.h" |
| #include "components/html_viewer/html_factory.h" |
| #include "components/html_viewer/html_frame_delegate.h" |
| #include "components/html_viewer/html_frame_properties.h" |
| #include "components/html_viewer/html_frame_tree_manager.h" |
| #include "components/html_viewer/html_widget.h" |
| #include "components/html_viewer/media_factory.h" |
| #include "components/html_viewer/stats_collection_controller.h" |
| #include "components/html_viewer/touch_handler.h" |
| #include "components/html_viewer/web_layer_tree_view_impl.h" |
| #include "components/html_viewer/web_storage_namespace_impl.h" |
| #include "components/html_viewer/web_url_loader_impl.h" |
| #include "components/mus/public/cpp/scoped_view_ptr.h" |
| #include "components/mus/public/cpp/view.h" |
| #include "components/mus/public/cpp/view_tree_connection.h" |
| #include "components/mus/vm/ids.h" |
| #include "mojo/application/public/cpp/application_impl.h" |
| #include "mojo/application/public/cpp/connect.h" |
| #include "mojo/application/public/interfaces/shell.mojom.h" |
| #include "mojo/common/common_type_converters.h" |
| #include "mojo/converters/geometry/geometry_type_converters.h" |
| #include "skia/ext/refptr.h" |
| #include "third_party/WebKit/public/platform/Platform.h" |
| #include "third_party/WebKit/public/platform/WebHTTPHeaderVisitor.h" |
| #include "third_party/WebKit/public/platform/WebSecurityOrigin.h" |
| #include "third_party/WebKit/public/platform/WebSize.h" |
| #include "third_party/WebKit/public/web/WebConsoleMessage.h" |
| #include "third_party/WebKit/public/web/WebDocument.h" |
| #include "third_party/WebKit/public/web/WebElement.h" |
| #include "third_party/WebKit/public/web/WebInputEvent.h" |
| #include "third_party/WebKit/public/web/WebKit.h" |
| #include "third_party/WebKit/public/web/WebLocalFrame.h" |
| #include "third_party/WebKit/public/web/WebNavigationPolicy.h" |
| #include "third_party/WebKit/public/web/WebRemoteFrame.h" |
| #include "third_party/WebKit/public/web/WebRemoteFrameClient.h" |
| #include "third_party/WebKit/public/web/WebScriptSource.h" |
| #include "third_party/WebKit/public/web/WebView.h" |
| #include "third_party/mojo/src/mojo/public/cpp/system/data_pipe.h" |
| #include "third_party/skia/include/core/SkCanvas.h" |
| #include "third_party/skia/include/core/SkColor.h" |
| #include "third_party/skia/include/core/SkDevice.h" |
| #include "ui/gfx/geometry/dip_util.h" |
| #include "ui/gfx/geometry/size.h" |
| #include "url/gurl.h" |
| #include "url/origin.h" |
| #include "url/url_constants.h" |
| |
| using mojo::AxProvider; |
| using mojo::Rect; |
| using mojo::ServiceProviderPtr; |
| using mojo::URLResponsePtr; |
| using mus::View; |
| using web_view::mojom::HTMLMessageEvent; |
| using web_view::mojom::HTMLMessageEventPtr; |
| |
| namespace html_viewer { |
| namespace { |
| |
| const size_t kMaxTitleChars = 4 * 1024; |
| |
| web_view::mojom::NavigationTargetType WebNavigationPolicyToNavigationTarget( |
| blink::WebNavigationPolicy policy) { |
| switch (policy) { |
| case blink::WebNavigationPolicyCurrentTab: |
| return web_view::mojom::NAVIGATION_TARGET_TYPE_EXISTING_FRAME; |
| case blink::WebNavigationPolicyNewBackgroundTab: |
| case blink::WebNavigationPolicyNewForegroundTab: |
| case blink::WebNavigationPolicyNewWindow: |
| case blink::WebNavigationPolicyNewPopup: |
| return web_view::mojom::NAVIGATION_TARGET_TYPE_NEW_FRAME; |
| default: |
| return web_view::mojom::NAVIGATION_TARGET_TYPE_NO_PREFERENCE; |
| } |
| } |
| |
| HTMLFrame* GetPreviousSibling(HTMLFrame* frame) { |
| DCHECK(frame->parent()); |
| auto iter = std::find(frame->parent()->children().begin(), |
| frame->parent()->children().end(), frame); |
| return (iter == frame->parent()->children().begin()) ? nullptr : *(--iter); |
| } |
| |
| // See surface_layer.h for a description of this callback. |
| void SatisfyCallback(cc::SurfaceSequence sequence) { |
| // TODO(fsamuel): Implement this. |
| } |
| |
| // See surface_layer.h for a description of this callback. |
| void RequireCallback(cc::SurfaceId surface_id, |
| cc::SurfaceSequence sequence) { |
| // TODO(fsamuel): Implement this. |
| } |
| |
| } // namespace |
| |
| HTMLFrame::HTMLFrame(CreateParams* params) |
| : frame_tree_manager_(params->manager), |
| parent_(params->parent), |
| view_(nullptr), |
| id_(params->id), |
| web_frame_(nullptr), |
| delegate_(params->delegate), |
| pending_navigation_(false), |
| weak_factory_(this) { |
| if (parent_) |
| parent_->children_.push_back(this); |
| |
| if (params->view && params->view->id() == id_) |
| SetView(params->view); |
| |
| SetReplicatedFrameStateFromClientProperties(params->properties, &state_); |
| |
| if (!parent_) { |
| CreateRootWebWidget(); |
| |
| // This is the root of the tree (aka the main frame). |
| // Expected order for creating webframes is: |
| // . Create local webframe (first webframe must always be local). |
| // . Set as main frame on WebView. |
| // . Swap to remote (if not local). |
| blink::WebLocalFrame* local_web_frame = |
| blink::WebLocalFrame::create(state_.tree_scope, this); |
| // We need to set the main frame before creating children so that state is |
| // properly set up in blink. |
| web_view()->setMainFrame(local_web_frame); |
| |
| // The resize and setDeviceScaleFactor() needs to be after setting the main |
| // frame. |
| const gfx::Size size_in_pixels(params->view->bounds().width, |
| params->view->bounds().height); |
| const gfx::Size size_in_dips = gfx::ConvertSizeToDIP( |
| params->view->viewport_metrics().device_pixel_ratio, size_in_pixels); |
| web_view()->resize(size_in_dips); |
| web_frame_ = local_web_frame; |
| web_view()->setDeviceScaleFactor(global_state()->device_pixel_ratio()); |
| if (id_ != params->view->id()) { |
| blink::WebRemoteFrame* remote_web_frame = |
| blink::WebRemoteFrame::create(state_.tree_scope, this); |
| local_web_frame->swap(remote_web_frame); |
| web_frame_ = remote_web_frame; |
| } else { |
| // Setup a DevTools agent if this is the local main frame and the browser |
| // side has set relevant client properties. |
| mojo::Array<uint8_t> devtools_id = |
| GetValueFromClientProperties("devtools-id", params->properties); |
| if (!devtools_id.is_null()) { |
| mojo::Array<uint8_t> devtools_state = |
| GetValueFromClientProperties("devtools-state", params->properties); |
| std::string devtools_state_str = devtools_state.To<std::string>(); |
| devtools_agent_.reset(new DevToolsAgentImpl( |
| web_frame_->toWebLocalFrame(), devtools_id.To<std::string>(), |
| devtools_state.is_null() ? nullptr : &devtools_state_str)); |
| } |
| |
| // Collect startup perf data for local main frames in test environments. |
| // Child frames aren't tracked, and tracking remote frames is redundant. |
| startup_performance_data_collector_ = |
| StatsCollectionController::Install(web_frame_, GetApp()); |
| } |
| } else if (!params->is_local_create_child && params->view && |
| id_ == params->view->id()) { |
| // Frame represents the local frame, and it isn't the root of the tree. |
| HTMLFrame* previous_sibling = GetPreviousSibling(this); |
| blink::WebFrame* previous_web_frame = |
| previous_sibling ? previous_sibling->web_frame() : nullptr; |
| CHECK(!parent_->IsLocal()); |
| web_frame_ = parent_->web_frame()->toWebRemoteFrame()->createLocalChild( |
| state_.tree_scope, state_.name, state_.sandbox_flags, this, |
| previous_web_frame); |
| CreateLocalRootWebWidget(web_frame_->toWebLocalFrame()); |
| } else if (!parent_->IsLocal()) { |
| web_frame_ = parent_->web_frame()->toWebRemoteFrame()->createRemoteChild( |
| state_.tree_scope, state_.name, state_.sandbox_flags, this); |
| } else { |
| CHECK(params->is_local_create_child); |
| |
| blink::WebLocalFrame* child_web_frame = |
| blink::WebLocalFrame::create(state_.tree_scope, this); |
| web_frame_ = child_web_frame; |
| parent_->web_frame_->appendChild(child_web_frame); |
| } |
| |
| DVLOG(2) << "HTMLFrame init this=" << this << " id=" << id_ |
| << " local=" << IsLocal() |
| << " parent=" << (parent_ ? parent_->id_ : 0u); |
| |
| if (!IsLocal()) { |
| blink::WebRemoteFrame* remote_web_frame = web_frame_->toWebRemoteFrame(); |
| if (remote_web_frame) { |
| remote_web_frame->setReplicatedOrigin(state_.origin); |
| remote_web_frame->setReplicatedName(state_.name); |
| } |
| } |
| } |
| |
| void HTMLFrame::Close() { |
| if (GetWebWidget()) { |
| // Closing the root widget (WebView) implicitly detaches. For children |
| // (which have a WebFrameWidget) a detach() is required. Use a temporary |
| // as if 'this' is the root the call to GetWebWidget()->close() deletes |
| // 'this'. |
| const bool is_child = parent_ != nullptr; |
| GetWebWidget()->close(); |
| if (is_child) |
| web_frame_->detach(); |
| } else { |
| web_frame_->detach(); |
| } |
| } |
| |
| const HTMLFrame* HTMLFrame::FindFrame(uint32_t id) const { |
| if (id == id_) |
| return this; |
| |
| for (const HTMLFrame* child : children_) { |
| const HTMLFrame* match = child->FindFrame(id); |
| if (match) |
| return match; |
| } |
| return nullptr; |
| } |
| |
| blink::WebView* HTMLFrame::web_view() { |
| blink::WebWidget* web_widget = |
| html_widget_ ? html_widget_->GetWidget() : nullptr; |
| return web_widget && web_widget->isWebView() |
| ? static_cast<blink::WebView*>(web_widget) |
| : nullptr; |
| } |
| |
| blink::WebWidget* HTMLFrame::GetWebWidget() { |
| return html_widget_ ? html_widget_->GetWidget() : nullptr; |
| } |
| |
| bool HTMLFrame::IsLocal() const { |
| return web_frame_->isWebLocalFrame(); |
| } |
| |
| bool HTMLFrame::HasLocalDescendant() const { |
| if (IsLocal()) |
| return true; |
| |
| for (HTMLFrame* child : children_) { |
| if (child->HasLocalDescendant()) |
| return true; |
| } |
| return false; |
| } |
| |
| void HTMLFrame::LoadRequest(const blink::WebURLRequest& request) { |
| DCHECK(IsLocal()); |
| |
| DVLOG(2) << "HTMLFrame::LoadRequest this=" << this << " id=" << id_ |
| << " URL=" << GURL(request.url()); |
| |
| pending_navigation_ = false; |
| web_frame_->toWebLocalFrame()->loadRequest(request); |
| } |
| |
| HTMLFrame::~HTMLFrame() { |
| DVLOG(2) << "~HTMLFrame this=" << this << " id=" << id_; |
| |
| DCHECK(children_.empty()); |
| |
| if (parent_) { |
| auto iter = |
| std::find(parent_->children_.begin(), parent_->children_.end(), this); |
| parent_->children_.erase(iter); |
| } |
| parent_ = nullptr; |
| |
| frame_tree_manager_->OnFrameDestroyed(this); |
| |
| if (delegate_) |
| delegate_->OnFrameDestroyed(); |
| |
| if (view_) { |
| view_->RemoveObserver(this); |
| mus::ScopedViewPtr::DeleteViewOrViewManager(view_); |
| } |
| } |
| |
| blink::WebMediaPlayer* HTMLFrame::createMediaPlayer( |
| blink::WebLocalFrame* frame, |
| const blink::WebURL& url, |
| blink::WebMediaPlayerClient* client, |
| blink::WebMediaPlayerEncryptedMediaClient* encrypted_client, |
| blink::WebContentDecryptionModule* initial_cdm) { |
| return global_state()->media_factory()->CreateMediaPlayer( |
| frame, url, client, encrypted_client, initial_cdm, GetApp()->shell()); |
| } |
| |
| blink::WebFrame* HTMLFrame::createChildFrame( |
| blink::WebLocalFrame* parent, |
| blink::WebTreeScopeType scope, |
| const blink::WebString& frame_name, |
| blink::WebSandboxFlags sandbox_flags) { |
| DCHECK(IsLocal()); // Can't create children of remote frames. |
| DCHECK_EQ(parent, web_frame_); |
| DCHECK(view_); // If we're local we have to have a view. |
| // Create the view that will house the frame now. We embed once we know the |
| // url (see decidePolicyForNavigation()). |
| mus::View* child_view = view_->connection()->CreateView(); |
| ReplicatedFrameState child_state; |
| child_state.name = frame_name; |
| child_state.tree_scope = scope; |
| child_state.sandbox_flags = sandbox_flags; |
| mojo::Map<mojo::String, mojo::Array<uint8_t>> client_properties; |
| client_properties.mark_non_null(); |
| ClientPropertiesFromReplicatedFrameState(child_state, &client_properties); |
| |
| child_view->SetVisible(true); |
| view_->AddChild(child_view); |
| |
| HTMLFrame::CreateParams params(frame_tree_manager_, this, child_view->id(), |
| child_view, client_properties, nullptr); |
| params.is_local_create_child = true; |
| HTMLFrame* child_frame = GetFirstAncestorWithDelegate() |
| ->delegate_->GetHTMLFactory() |
| ->CreateHTMLFrame(¶ms); |
| child_frame->owned_view_.reset(new mus::ScopedViewPtr(child_view)); |
| |
| web_view::mojom::FrameClientPtr client_ptr; |
| child_frame->frame_client_binding_.reset( |
| new mojo::Binding<web_view::mojom::FrameClient>( |
| child_frame, mojo::GetProxy(&client_ptr))); |
| server_->OnCreatedFrame(GetProxy(&(child_frame->server_)), client_ptr.Pass(), |
| child_view->id(), client_properties.Pass()); |
| return child_frame->web_frame_; |
| } |
| |
| void HTMLFrame::frameDetached(blink::WebFrame* web_frame, |
| blink::WebFrameClient::DetachType type) { |
| if (type == blink::WebFrameClient::DetachType::Swap) { |
| web_frame->close(); |
| return; |
| } |
| |
| DCHECK(type == blink::WebFrameClient::DetachType::Remove); |
| FrameDetachedImpl(web_frame); |
| } |
| |
| blink::WebCookieJar* HTMLFrame::cookieJar(blink::WebLocalFrame* frame) { |
| // TODO(darin): Blink does not fallback to the Platform provided WebCookieJar. |
| // Either it should, as it once did, or we should find another solution here. |
| return blink::Platform::current()->cookieJar(); |
| } |
| |
| blink::WebNavigationPolicy HTMLFrame::decidePolicyForNavigation( |
| const NavigationPolicyInfo& info) { |
| // If we have extraData() it means we already have the url response |
| // (presumably because we are being called via Navigate()). In that case we |
| // can go ahead and navigate locally. |
| if (info.urlRequest.extraData()) { |
| DCHECK_EQ(blink::WebNavigationPolicyCurrentTab, info.defaultPolicy); |
| return blink::WebNavigationPolicyCurrentTab; |
| } |
| |
| // about:blank is treated as the same origin and is always allowed for frames. |
| if (parent_ && info.urlRequest.url() == GURL(url::kAboutBlankURL) && |
| info.defaultPolicy == blink::WebNavigationPolicyCurrentTab) { |
| return blink::WebNavigationPolicyCurrentTab; |
| } |
| |
| // Ask the Frame to handle the navigation. Returning |
| // WebNavigationPolicyHandledByClient to inform blink that the navigation is |
| // being handled. |
| DVLOG(2) << "HTMLFrame::decidePolicyForNavigation calls " |
| << "Frame::RequestNavigate this=" << this << " id=" << id_ |
| << " URL=" << GURL(info.urlRequest.url()); |
| |
| mojo::URLRequestPtr url_request = mojo::URLRequest::From(info.urlRequest); |
| server_->RequestNavigate( |
| WebNavigationPolicyToNavigationTarget(info.defaultPolicy), id_, |
| url_request.Pass()); |
| |
| // TODO(yzshen): crbug.com/532556 If the server side drops the request, |
| // this frame will be in permenant-loading state. We should send a |
| // notification to mark this frame as not loading in that case. We also need |
| // to better keep track of multiple pending navigations. |
| pending_navigation_ = true; |
| return blink::WebNavigationPolicyHandledByClient; |
| } |
| |
| bool HTMLFrame::hasPendingNavigation(blink::WebLocalFrame* frame) { |
| return pending_navigation_; |
| } |
| |
| void HTMLFrame::didHandleOnloadEvents(blink::WebLocalFrame* frame) { |
| DVLOG(2) << "XXX HTMLFrame::didHandleOnloadEvents id=" << id_; |
| static bool recorded = false; |
| if (!recorded && startup_performance_data_collector_) { |
| startup_performance_data_collector_->SetFirstWebContentsMainFrameLoadTime( |
| base::Time::Now().ToInternalValue()); |
| recorded = true; |
| } |
| } |
| |
| void HTMLFrame::didAddMessageToConsole(const blink::WebConsoleMessage& message, |
| const blink::WebString& source_name, |
| unsigned source_line, |
| const blink::WebString& stack_trace) { |
| VLOG(1) << "[" << source_name.utf8() << "(" << source_line << ")] " |
| << message.text.utf8(); |
| } |
| |
| void HTMLFrame::didFinishLoad(blink::WebLocalFrame* frame) { |
| if (GetFirstAncestorWithDelegate() == this) |
| delegate_->OnFrameDidFinishLoad(); |
| } |
| |
| void HTMLFrame::didNavigateWithinPage(blink::WebLocalFrame* frame, |
| const blink::WebHistoryItem& history_item, |
| blink::WebHistoryCommitType commit_type) { |
| server_->DidNavigateLocally(history_item.urlString().utf8()); |
| } |
| |
| blink::WebGeolocationClient* HTMLFrame::geolocationClient() { |
| if (!geolocation_client_impl_) |
| geolocation_client_impl_.reset(new GeolocationClientImpl); |
| return geolocation_client_impl_.get(); |
| } |
| |
| blink::WebEncryptedMediaClient* HTMLFrame::encryptedMediaClient() { |
| return global_state()->media_factory()->GetEncryptedMediaClient(); |
| } |
| |
| void HTMLFrame::didStartLoading(bool to_different_document) { |
| server_->LoadingStateChanged(true, 0.0); |
| } |
| |
| void HTMLFrame::didStopLoading() { |
| server_->LoadingStateChanged(false, 1.0); |
| } |
| |
| void HTMLFrame::didChangeLoadProgress(double load_progress) { |
| server_->LoadingStateChanged(true, load_progress); |
| } |
| |
| void HTMLFrame::dispatchLoad() { |
| // According to comments of WebFrameClient::dispatchLoad(), this should only |
| // be called when the parent frame is remote. |
| DCHECK(parent_ && !parent_->IsLocal()); |
| server_->DispatchLoadEventToParent(); |
| } |
| |
| void HTMLFrame::didChangeName(blink::WebLocalFrame* frame, |
| const blink::WebString& name) { |
| state_.name = name; |
| server_->SetClientProperty(kPropertyFrameName, |
| FrameNameToClientProperty(name)); |
| } |
| |
| void HTMLFrame::didCommitProvisionalLoad( |
| blink::WebLocalFrame* frame, |
| const blink::WebHistoryItem& item, |
| blink::WebHistoryCommitType commit_type) { |
| state_.origin = FrameOrigin(frame); |
| server_->SetClientProperty(kPropertyFrameOrigin, |
| FrameOriginToClientProperty(frame)); |
| |
| // TODO(erg): We need to pass way more information from here through to the |
| // other side. See FrameHostMsg_DidCommitProvisionalLoad_Params. It is a grab |
| // bag of everything and it looks like a combination of |
| // NavigatorImpl::DidNavigate and |
| // NavigationControllerImpl::RendererDidNavigate use everything passed |
| // through. |
| server_->DidCommitProvisionalLoad(); |
| } |
| |
| void HTMLFrame::didReceiveTitle(blink::WebLocalFrame* frame, |
| const blink::WebString& title, |
| blink::WebTextDirection direction) { |
| // TODO(beng): handle |direction|. |
| mojo::String formatted; |
| if (!title.isNull()) { |
| formatted = |
| mojo::String::From(base::string16(title).substr(0, kMaxTitleChars)); |
| } |
| server_->TitleChanged(formatted); |
| } |
| |
| void HTMLFrame::Bind( |
| web_view::mojom::FramePtr frame, |
| mojo::InterfaceRequest<web_view::mojom::FrameClient> frame_client_request) { |
| DCHECK(IsLocal()); |
| server_ = frame.Pass(); |
| server_.set_connection_error_handler( |
| base::Bind(&HTMLFrame::Close, base::Unretained(this))); |
| frame_client_binding_.reset(new mojo::Binding<web_view::mojom::FrameClient>( |
| this, frame_client_request.Pass())); |
| } |
| |
| void HTMLFrame::SetValueFromClientProperty(const std::string& name, |
| mojo::Array<uint8_t> new_data) { |
| if (IsLocal()) |
| return; |
| |
| // Only the name and origin dynamically change. |
| if (name == kPropertyFrameOrigin) { |
| state_.origin = FrameOriginFromClientProperty(new_data); |
| web_frame_->toWebRemoteFrame()->setReplicatedOrigin(state_.origin); |
| } else if (name == kPropertyFrameName) { |
| state_.name = FrameNameFromClientProperty(new_data); |
| web_frame_->toWebRemoteFrame()->setReplicatedName(state_.name); |
| } |
| } |
| |
| HTMLFrame* HTMLFrame::GetFirstAncestorWithDelegate() { |
| HTMLFrame* frame = this; |
| while (frame && !frame->delegate_) |
| frame = frame->parent_; |
| return frame; |
| } |
| |
| mojo::ApplicationImpl* HTMLFrame::GetApp() { |
| return GetFirstAncestorWithDelegate()->delegate_->GetApp(); |
| } |
| |
| web_view::mojom::Frame* HTMLFrame::GetServerFrame() { |
| // Prefer an ancestor with a server Frame. |
| for (HTMLFrame* frame = this; frame; frame = frame->parent_) { |
| if (frame->server_.get()) |
| return frame->server_.get(); |
| } |
| |
| // We're a remote frame with no local frame ancestors. Use the server Frame |
| // from the local frame of the HTMLFrameTreeManager. |
| return frame_tree_manager_->local_frame_->server_.get(); |
| } |
| |
| void HTMLFrame::SetView(mus::View* view) { |
| if (view_) |
| view_->RemoveObserver(this); |
| view_ = view; |
| if (view_) |
| view_->AddObserver(this); |
| } |
| |
| void HTMLFrame::CreateRootWebWidget() { |
| DCHECK(!html_widget_); |
| if (view_) { |
| HTMLWidgetRootLocal::CreateParams create_params(GetApp(), global_state(), |
| view_); |
| html_widget_.reset( |
| delegate_->GetHTMLFactory()->CreateHTMLWidgetRootLocal(&create_params)); |
| } else { |
| html_widget_.reset(new HTMLWidgetRootRemote); |
| } |
| } |
| |
| void HTMLFrame::CreateLocalRootWebWidget(blink::WebLocalFrame* local_frame) { |
| DCHECK(!html_widget_); |
| DCHECK(IsLocal()); |
| html_widget_.reset( |
| new HTMLWidgetLocalRoot(GetApp(), global_state(), view_, local_frame)); |
| } |
| |
| void HTMLFrame::UpdateFocus() { |
| blink::WebWidget* web_widget = GetWebWidget(); |
| if (!web_widget || !view_) |
| return; |
| const bool is_focused = view_ && view_->HasFocus(); |
| web_widget->setFocus(is_focused); |
| if (web_widget->isWebView()) |
| static_cast<blink::WebView*>(web_widget)->setIsActive(is_focused); |
| } |
| |
| void HTMLFrame::SwapToRemote() { |
| DVLOG(2) << "HTMLFrame::SwapToRemote this=" << this << " id=" << id_; |
| |
| DCHECK(IsLocal()); |
| |
| HTMLFrameDelegate* delegate = delegate_; |
| delegate_ = nullptr; |
| |
| blink::WebRemoteFrame* remote_frame = |
| blink::WebRemoteFrame::create(state_.tree_scope, this); |
| remote_frame->initializeFromFrame(web_frame_->toWebLocalFrame()); |
| // swap() ends up calling us back and we then close the frame, which deletes |
| // it. |
| web_frame_->swap(remote_frame); |
| if (owned_view_) { |
| surface_layer_ = |
| cc::SurfaceLayer::Create(cc_blink::WebLayerImpl::LayerSettings(), |
| base::Bind(&SatisfyCallback), |
| base::Bind(&RequireCallback)); |
| surface_layer_->SetSurfaceId( |
| cc::SurfaceId(owned_view_->view()->id()), |
| global_state()->device_pixel_ratio(), |
| owned_view_->view()->bounds().To<gfx::Rect>().size()); |
| |
| web_layer_.reset(new cc_blink::WebLayerImpl(surface_layer_)); |
| } |
| remote_frame->setRemoteWebLayer(web_layer_.get()); |
| remote_frame->setReplicatedName(state_.name); |
| remote_frame->setReplicatedOrigin(state_.origin); |
| remote_frame->setReplicatedSandboxFlags(state_.sandbox_flags); |
| |
| // Tell the frame that it is actually loading. This prevents its parent |
| // from prematurely dispatching load event. |
| remote_frame->didStartLoading(); |
| pending_navigation_ = false; |
| |
| web_frame_ = remote_frame; |
| SetView(nullptr); |
| server_.reset(); |
| frame_client_binding_.reset(); |
| if (delegate) |
| delegate->OnFrameSwappedToRemote(); |
| } |
| |
| void HTMLFrame::SwapToLocal( |
| HTMLFrameDelegate* delegate, |
| mus::View* view, |
| const mojo::Map<mojo::String, mojo::Array<uint8_t>>& properties) { |
| DVLOG(2) << "HTMLFrame::SwapToLocal this=" << this << " id=" << id_; |
| CHECK(!IsLocal()); |
| // It doesn't make sense for the root to swap to local. |
| CHECK(parent_); |
| delegate_ = delegate; |
| SetView(view); |
| SetReplicatedFrameStateFromClientProperties(properties, &state_); |
| blink::WebLocalFrame* local_web_frame = |
| blink::WebLocalFrame::create(state_.tree_scope, this); |
| local_web_frame->initializeToReplaceRemoteFrame( |
| web_frame_->toWebRemoteFrame(), state_.name, state_.sandbox_flags); |
| // The swap() ends up calling to frameDetached() and deleting the old. |
| web_frame_->swap(local_web_frame); |
| web_frame_ = local_web_frame; |
| |
| web_layer_.reset(); |
| } |
| |
| void HTMLFrame::SwapDelegate(HTMLFrameDelegate* delegate) { |
| DCHECK(IsLocal()); |
| HTMLFrameDelegate* old_delegate = delegate_; |
| delegate_ = delegate; |
| delegate->OnSwap(this, old_delegate); |
| } |
| |
| HTMLFrame* HTMLFrame::FindFrameWithWebFrame(blink::WebFrame* web_frame) { |
| if (web_frame_ == web_frame) |
| return this; |
| for (HTMLFrame* child_frame : children_) { |
| HTMLFrame* result = child_frame->FindFrameWithWebFrame(web_frame); |
| if (result) |
| return result; |
| } |
| return nullptr; |
| } |
| |
| void HTMLFrame::FrameDetachedImpl(blink::WebFrame* web_frame) { |
| DCHECK_EQ(web_frame_, web_frame); |
| |
| while (!children_.empty()) { |
| HTMLFrame* child = children_.front(); |
| child->Close(); |
| DCHECK(children_.empty() || children_.front() != child); |
| } |
| |
| if (web_frame->parent()) |
| web_frame->parent()->removeChild(web_frame); |
| |
| delete this; |
| } |
| |
| void HTMLFrame::OnViewBoundsChanged(View* view, |
| const Rect& old_bounds, |
| const Rect& new_bounds) { |
| DCHECK_EQ(view, view_); |
| if (html_widget_) |
| html_widget_->OnViewBoundsChanged(view); |
| } |
| |
| void HTMLFrame::OnViewDestroyed(View* view) { |
| DCHECK_EQ(view, view_); |
| view_->RemoveObserver(this); |
| view_ = nullptr; |
| Close(); |
| } |
| |
| void HTMLFrame::OnViewInputEvent(View* view, const mojo::EventPtr& event) { |
| if (event->pointer_data && event->pointer_data->location) { |
| // Blink expects coordintes to be in DIPs. |
| event->pointer_data->location->x /= global_state()->device_pixel_ratio(); |
| event->pointer_data->location->y /= global_state()->device_pixel_ratio(); |
| event->pointer_data->location->screen_x /= |
| global_state()->device_pixel_ratio(); |
| event->pointer_data->location->screen_y /= |
| global_state()->device_pixel_ratio(); |
| } |
| |
| blink::WebWidget* web_widget = GetWebWidget(); |
| |
| if (!touch_handler_ && web_widget) |
| touch_handler_.reset(new TouchHandler(web_widget)); |
| |
| if (touch_handler_ && (event->action == mojo::EVENT_TYPE_POINTER_DOWN || |
| event->action == mojo::EVENT_TYPE_POINTER_UP || |
| event->action == mojo::EVENT_TYPE_POINTER_CANCEL || |
| event->action == mojo::EVENT_TYPE_POINTER_MOVE) && |
| event->pointer_data && |
| event->pointer_data->kind == mojo::POINTER_KIND_TOUCH) { |
| touch_handler_->OnTouchEvent(*event); |
| return; |
| } |
| |
| if (!web_widget) |
| return; |
| |
| scoped_ptr<blink::WebInputEvent> web_event = |
| event.To<scoped_ptr<blink::WebInputEvent>>(); |
| if (web_event) |
| web_widget->handleInputEvent(*web_event); |
| } |
| |
| void HTMLFrame::OnViewFocusChanged(mus::View* gained_focus, |
| mus::View* lost_focus) { |
| UpdateFocus(); |
| } |
| |
| void HTMLFrame::OnConnect(web_view::mojom::FramePtr frame, |
| uint32_t change_id, |
| uint32_t view_id, |
| web_view::mojom::ViewConnectType view_connect_type, |
| mojo::Array<web_view::mojom::FrameDataPtr> frame_data, |
| const OnConnectCallback& callback) { |
| // This is called if this frame is created by way of OnCreatedFrame(). |
| callback.Run(); |
| } |
| |
| void HTMLFrame::OnFrameAdded(uint32_t change_id, |
| web_view::mojom::FrameDataPtr frame_data) { |
| frame_tree_manager_->ProcessOnFrameAdded(this, change_id, frame_data.Pass()); |
| } |
| |
| void HTMLFrame::OnFrameRemoved(uint32_t change_id, uint32_t frame_id) { |
| frame_tree_manager_->ProcessOnFrameRemoved(this, change_id, frame_id); |
| } |
| |
| void HTMLFrame::OnFrameClientPropertyChanged(uint32_t frame_id, |
| const mojo::String& name, |
| mojo::Array<uint8_t> new_value) { |
| frame_tree_manager_->ProcessOnFrameClientPropertyChanged(this, frame_id, name, |
| new_value.Pass()); |
| } |
| |
| void HTMLFrame::OnPostMessageEvent(uint32_t source_frame_id, |
| uint32_t target_frame_id, |
| HTMLMessageEventPtr serialized_event) { |
| NOTIMPLEMENTED(); // For message ports. |
| |
| HTMLFrame* target = frame_tree_manager_->root_->FindFrame(target_frame_id); |
| HTMLFrame* source = frame_tree_manager_->root_->FindFrame(source_frame_id); |
| if (!target || !source) { |
| DVLOG(1) << "Invalid source or target for PostMessage"; |
| return; |
| } |
| |
| if (!target->IsLocal()) { |
| DVLOG(1) << "Target for PostMessage is not lot local"; |
| return; |
| } |
| |
| blink::WebLocalFrame* target_web_frame = |
| target->web_frame_->toWebLocalFrame(); |
| |
| blink::WebSerializedScriptValue serialized_script_value; |
| serialized_script_value = blink::WebSerializedScriptValue::fromString( |
| serialized_event->data.To<blink::WebString>()); |
| |
| // We must pass in the target_origin to do the security check on this side, |
| // since it may have changed since the original postMessage call was made. |
| blink::WebSecurityOrigin target_origin; |
| if (!serialized_event->target_origin.is_null()) { |
| target_origin = blink::WebSecurityOrigin::createFromString( |
| serialized_event->target_origin.To<blink::WebString>()); |
| } |
| |
| // TODO(esprehn): Shouldn't this also fill in channels like RenderFrameImpl? |
| blink::WebMessagePortChannelArray channels; |
| blink::WebDOMMessageEvent msg_event(serialized_script_value, |
| serialized_event->source_origin.To<blink::WebString>(), |
| source->web_frame_, target_web_frame->document(), channels); |
| |
| target_web_frame->dispatchMessageEventWithOriginCheck(target_origin, |
| msg_event); |
| } |
| |
| void HTMLFrame::OnWillNavigate(const mojo::String& origin, |
| const OnWillNavigateCallback& callback) { |
| bool should_swap = true; |
| |
| if (this == frame_tree_manager_->local_frame_) { |
| HTMLFrame* new_local_frame = frame_tree_manager_->FindNewLocalFrame(); |
| if (!new_local_frame) { |
| // All local frames are descendants of |this|. In this case, the whole |
| // frame tree in the current process is going to be deleted very soon. We |
| // don't have to swap. |
| should_swap = false; |
| } else { |
| frame_tree_manager_->local_frame_ = new_local_frame; |
| } |
| } |
| |
| DVLOG(2) << "HTMLFrame::OnWillNavigate this=" << this << " id=" << id_ |
| << " local=" << IsLocal() << " should_swap=" << should_swap; |
| callback.Run(); |
| if (should_swap) { |
| SwapToRemote(); |
| const blink::WebSecurityOrigin security_origin( |
| blink::WebSecurityOrigin::createFromString( |
| blink::WebString::fromUTF8(origin))); |
| web_frame_->toWebRemoteFrame()->setReplicatedOrigin(security_origin); |
| } |
| } |
| |
| void HTMLFrame::OnFrameLoadingStateChanged(uint32_t frame_id, bool loading) { |
| HTMLFrame* frame = frame_tree_manager_->root_->FindFrame(frame_id); |
| // TODO(yzshen): (Apply to this method and the one below.) Is it possible that |
| // at this point the frame is already hosting a different document? |
| if (frame && !frame->IsLocal()) { |
| if (loading) |
| frame->web_frame_->toWebRemoteFrame()->didStartLoading(); |
| else |
| frame->web_frame_->toWebRemoteFrame()->didStopLoading(); |
| } |
| } |
| |
| void HTMLFrame::OnDispatchFrameLoadEvent(uint32_t frame_id) { |
| HTMLFrame* frame = frame_tree_manager_->root_->FindFrame(frame_id); |
| if (frame && !frame->IsLocal()) |
| frame->web_frame_->toWebRemoteFrame()->DispatchLoadEventForFrameOwner(); |
| } |
| |
| void HTMLFrame::frameDetached(blink::WebRemoteFrameClient::DetachType type) { |
| if (type == blink::WebRemoteFrameClient::DetachType::Swap) { |
| web_frame_->close(); |
| return; |
| } |
| |
| DCHECK(type == blink::WebRemoteFrameClient::DetachType::Remove); |
| FrameDetachedImpl(web_frame_); |
| } |
| |
| void HTMLFrame::postMessageEvent(blink::WebLocalFrame* source_web_frame, |
| blink::WebRemoteFrame* target_web_frame, |
| blink::WebSecurityOrigin target_origin, |
| blink::WebDOMMessageEvent web_event) { |
| NOTIMPLEMENTED(); // message_ports aren't implemented yet. |
| |
| HTMLFrame* source_frame = |
| frame_tree_manager_->root_->FindFrameWithWebFrame(source_web_frame); |
| DCHECK(source_frame); |
| HTMLFrame* target_frame = |
| frame_tree_manager_->root_->FindFrameWithWebFrame(target_web_frame); |
| DCHECK(target_frame); |
| |
| HTMLMessageEventPtr event(HTMLMessageEvent::New()); |
| event->data = mojo::Array<uint8_t>::From(web_event.data().toString()); |
| event->source_origin = mojo::String::From(web_event.origin()); |
| if (!target_origin.isNull()) |
| event->target_origin = mojo::String::From(target_origin.toString()); |
| |
| source_frame->server_->PostMessageEventToFrame(target_frame->id_, |
| event.Pass()); |
| } |
| |
| void HTMLFrame::initializeChildFrame(const blink::WebRect& frame_rect, |
| float scale_factor) { |
| // NOTE: |scale_factor| is always 1. |
| const gfx::Rect rect_in_dip(frame_rect.x, frame_rect.y, frame_rect.width, |
| frame_rect.height); |
| const gfx::Rect rect_in_pixels(gfx::ConvertRectToPixel( |
| global_state()->device_pixel_ratio(), rect_in_dip)); |
| const mojo::RectPtr mojo_rect_in_pixels(mojo::Rect::From(rect_in_pixels)); |
| view_->SetBounds(*mojo_rect_in_pixels); |
| } |
| |
| void HTMLFrame::navigate(const blink::WebURLRequest& request, |
| bool should_replace_current_entry) { |
| // TODO: support |should_replace_current_entry|. |
| NOTIMPLEMENTED(); // for |should_replace_current_entry |
| mojo::URLRequestPtr url_request = mojo::URLRequest::From(request); |
| GetServerFrame()->RequestNavigate( |
| web_view::mojom::NAVIGATION_TARGET_TYPE_EXISTING_FRAME, id_, |
| url_request.Pass()); |
| } |
| |
| void HTMLFrame::reload(bool ignore_cache, bool is_client_redirect) { |
| NOTIMPLEMENTED(); |
| } |
| |
| void HTMLFrame::frameRectsChanged(const blink::WebRect& frame_rect) { |
| // Only the owner of view can update its size. |
| if (!owned_view_) |
| return; |
| |
| const gfx::Rect rect_in_dip(frame_rect.x, frame_rect.y, frame_rect.width, |
| frame_rect.height); |
| const gfx::Rect rect_in_pixels(gfx::ConvertRectToPixel( |
| global_state()->device_pixel_ratio(), rect_in_dip)); |
| const mojo::RectPtr mojo_rect_in_pixels(mojo::Rect::From(rect_in_pixels)); |
| owned_view_->view()->SetBounds(*mojo_rect_in_pixels); |
| |
| if (!surface_layer_) |
| return; |
| |
| surface_layer_->SetSurfaceId( |
| cc::SurfaceId(owned_view_->view()->id()), |
| global_state()->device_pixel_ratio(), |
| owned_view_->view()->bounds().To<gfx::Rect>().size()); |
| } |
| |
| } // namespace mojo |