Chromium Code Reviews| Index: content/browser/web_contents/web_contents_impl.cc |
| diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc |
| index bb4128c8f0fda709aef73e59c5b2819c0d1bd7d6..7d8261b22f7c55537b27838106f8a8a33b7e84ee 100644 |
| --- a/content/browser/web_contents/web_contents_impl.cc |
| +++ b/content/browser/web_contents/web_contents_impl.cc |
| @@ -328,28 +328,30 @@ WebContentsImpl::ColorChooserInfo::~ColorChooserInfo() { |
| } |
| // WebContentsImpl::WebContentsTreeNode ---------------------------------------- |
| -WebContentsImpl::WebContentsTreeNode::WebContentsTreeNode() |
| - : outer_web_contents_(nullptr), |
| +WebContentsImpl::WebContentsTreeNode::WebContentsTreeNode( |
| + WebContentsImpl* web_contents) |
| + : web_contents_(web_contents), |
| + outer_web_contents_(nullptr), |
| outer_contents_frame_tree_node_id_( |
| - FrameTreeNode::kFrameTreeNodeInvalidId) { |
| -} |
| + FrameTreeNode::kFrameTreeNodeInvalidId) {} |
| WebContentsImpl::WebContentsTreeNode::~WebContentsTreeNode() { |
| // Remove child pointer from our parent. |
| if (outer_web_contents_) { |
| - ChildrenSet& child_ptrs_in_parent = |
| + ChildrenMap& child_ptrs_in_parent = |
| outer_web_contents_->node_->inner_web_contents_tree_nodes_; |
| - ChildrenSet::iterator iter = child_ptrs_in_parent.find(this); |
| + ChildrenMap::iterator iter = |
| + child_ptrs_in_parent.find(outer_contents_frame_tree_node_id_); |
| DCHECK(iter != child_ptrs_in_parent.end()); |
| - child_ptrs_in_parent.erase(this); |
| + child_ptrs_in_parent.erase(iter); |
| } |
| // Remove parent pointers from our children. |
| // TODO(lazyboy): We should destroy the children WebContentses too. If the |
| // children do not manage their own lifetime, then we would leak their |
| // WebContentses. |
| - for (WebContentsTreeNode* child : inner_web_contents_tree_nodes_) |
| - child->outer_web_contents_ = nullptr; |
| + for (auto& child : inner_web_contents_tree_nodes_) |
| + child.second->outer_web_contents_ = nullptr; |
| } |
| void WebContentsImpl::WebContentsTreeNode::ConnectToOuterWebContents( |
| @@ -360,9 +362,22 @@ void WebContentsImpl::WebContentsTreeNode::ConnectToOuterWebContents( |
| outer_contents_frame->frame_tree_node()->frame_tree_node_id(); |
| if (!outer_web_contents_->node_) |
| - outer_web_contents_->node_.reset(new WebContentsTreeNode()); |
| + outer_web_contents_->node_.reset( |
| + new WebContentsTreeNode(outer_web_contents_)); |
| + |
| + outer_web_contents_->node_ |
| + ->inner_web_contents_tree_nodes_[outer_contents_frame_tree_node_id_] = |
| + this; |
| +} |
| - outer_web_contents_->node_->inner_web_contents_tree_nodes_.insert(this); |
| +const WebContentsImpl* |
| +WebContentsImpl::WebContentsTreeNode::FindInnerWebContentsAtFrame( |
| + const FrameTreeNode* node) const { |
| + auto iter = inner_web_contents_tree_nodes_.find(node->frame_tree_node_id()); |
| + if (iter != inner_web_contents_tree_nodes_.end()) { |
| + return iter->second->web_contents_; |
| + } |
| + return nullptr; |
| } |
| // WebContentsImpl ------------------------------------------------------------- |
| @@ -1393,7 +1408,7 @@ void WebContentsImpl::AttachToOuterWebContentsFrame( |
| CreateRenderWidgetHostViewForRenderManager(GetRenderViewHost()); |
| // Create a link to our outer WebContents. |
| - node_.reset(new WebContentsTreeNode()); |
| + node_.reset(new WebContentsTreeNode(this)); |
| node_->ConnectToOuterWebContents( |
| static_cast<WebContentsImpl*>(outer_web_contents), |
| static_cast<RenderFrameHostImpl*>(outer_contents_frame)); |
| @@ -1793,6 +1808,15 @@ RenderWidgetHostImpl* WebContentsImpl::GetFocusedRenderWidgetHost( |
| if (!focused_frame) |
| return receiving_widget; |
| + // If there is an inner web contents (<webview>, <guestview>, ...) with focus, |
| + // return its RenderWidgetHost. This will recurse to an arbitrary number of |
| + // WebContents to the deepest focused one. |
| + WebContentsImpl* inner_contents = |
| + (node_) ? node_->FindInnerWebContentsAtFrame(focused_frame) : nullptr; |
| + if (inner_contents) { |
| + return inner_contents->GetFocusedRenderWidgetHost( |
| + inner_contents->GetMainFrame()->GetRenderWidgetHost()); |
| + } |
| // The view may be null if a subframe's renderer process has crashed while |
| // the subframe has focus. Drop the event in that case. Do not give |
| // it to the main frame, so that the user doesn't unexpectedly type into the |
| @@ -4485,6 +4509,66 @@ void WebContentsImpl::EnsureOpenerProxiesExist(RenderFrameHost* source_rfh) { |
| } |
| } |
| +void WebContentsImpl::SetFocusedFrame(FrameTreeNode* node, |
| + SiteInstance* source) { |
| + SetFocusedFrameInternal(node, source, true /* can visit outer contents */); |
| + |
| + RenderWidgetHostImpl* rwh = node->current_frame_host()->GetRenderWidgetHost(); |
| + if (rwh) |
| + rwh->Focus(); |
| +} |
| + |
| +void WebContentsImpl::SetFocusedFrameInternal(FrameTreeNode* node, |
| + SiteInstance* source, |
| + bool can_visit_outer_contents) { |
| + // Approach: Recursively unfocus any inner web contents. Then set |
| + // representation of this web contents in the outer contents to be focused, |
| + // finally focus the node in current frame tree. |
| + // |
| + // Usually only one of unfocusing children and focusing self in parent would |
| + // happen. If unfocusing a child, then we're already focused in our parent, if |
| + // we have one. |
| + |
| + FrameTreeNode* old_focused_node = frame_tree_.GetFocusedFrame(); |
| + |
| + if (old_focused_node != nullptr && node_) { |
| + if (!node || old_focused_node != node) { |
| + WebContentsImpl* inner_contents = |
| + node_->FindInnerWebContentsAtFrame(old_focused_node); |
| + if (inner_contents) |
| + inner_contents->SetFocusedFrameInternal(nullptr, source, false); |
| + } |
| + } |
| + |
| + // focus in parent. |
| + WebContentsImpl* outer_contents = GetOuterWebContents(); |
| + // If we're coming from parent, we don't want to recurse back into the parent. |
| + if (outer_contents && can_visit_outer_contents) { |
| + FrameTreeNode* outer_node = |
| + outer_contents->frame_tree_.FindByID(GetOuterDelegateFrameTreeNodeId()); |
| + if (outer_node == nullptr) { |
| + // With browser plugin, we will have an outer contents but no node in the |
| + // outer contents' frame tree. |
| + // HACK: We are assuming that the guest contents can only be embedded |
| + // within the embedder's main frame. |
| + outer_contents->SetFocusedFrameInternal( |
| + outer_contents->frame_tree_.root(), source, true); |
| + } else { |
| + // Notify outer about our focus change. |
| + SiteInstance* outer_node_site_instance = |
| + outer_node->parent()->current_frame_host()->GetSiteInstance(); |
|
alexmos
2016/05/19 00:08:09
I don't think you need parent() here -- seems like
avallee
2016/05/24 20:07:08
lfg suggested the change as the outer_node does no
|
| + RenderFrameProxyHost* outer_proxy = |
| + frame_tree_.root()->render_manager()->GetRenderFrameProxyHost( |
| + outer_node_site_instance); |
| + if (outer_proxy) |
| + outer_proxy->SetFocusedFrame(); |
| + outer_contents->SetFocusedFrameInternal(outer_node, source, true); |
| + } |
| + } |
| + // Set focus for myself. |
| + frame_tree_.SetFocusedFrame(node, source); |
| +} |
| + |
| bool WebContentsImpl::AddMessageToConsole(int32_t level, |
| const base::string16& message, |
| int32_t line_no, |