| // 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 "public/web/WebFrame.h" |
| |
| #include <algorithm> |
| #include "bindings/core/v8/WindowProxyManager.h" |
| #include "core/dom/IncrementLoadEventDelayCount.h" |
| #include "core/dom/UserGestureIndicator.h" |
| #include "core/exported/WebRemoteFrameImpl.h" |
| #include "core/frame/LocalFrame.h" |
| #include "core/frame/LocalFrameView.h" |
| #include "core/frame/OpenedFrameTracker.h" |
| #include "core/frame/RemoteFrame.h" |
| #include "core/frame/RemoteFrameOwner.h" |
| #include "core/frame/WebLocalFrameImpl.h" |
| #include "core/html/HTMLFrameElementBase.h" |
| #include "core/html/HTMLFrameOwnerElement.h" |
| #include "core/html_names.h" |
| #include "core/page/Page.h" |
| #include "core/probe/CoreProbes.h" |
| #include "platform/heap/Handle.h" |
| #include "platform/instrumentation/tracing/TraceEvent.h" |
| #include "public/web/WebElement.h" |
| #include "public/web/WebFrameOwnerProperties.h" |
| #include "third_party/WebKit/common/sandbox_flags.h" |
| |
| namespace blink { |
| |
| bool WebFrame::Swap(WebFrame* frame) { |
| using std::swap; |
| Frame* old_frame = ToCoreFrame(*this); |
| if (!old_frame->IsAttached()) |
| return false; |
| |
| // Unload the current Document in this frame: this calls unload handlers, |
| // detaches child frames, etc. Since this runs script, make sure this frame |
| // wasn't detached before continuing with the swap. |
| // FIXME: There is no unit test for this condition, so one needs to be |
| // written. |
| if (!old_frame->PrepareForCommit()) |
| return false; |
| |
| // If there is a local parent, it might incorrectly declare itself complete |
| // during the detach phase of this swap. Suppress its completion until swap is |
| // over, at which point its completion will be correctly dependent on its |
| // newly swapped-in child. |
| std::unique_ptr<IncrementLoadEventDelayCount> delay_parent_load = |
| parent_ && parent_->IsWebLocalFrame() |
| ? IncrementLoadEventDelayCount::Create( |
| *ToWebLocalFrameImpl(parent_)->GetFrame()->GetDocument()) |
| : nullptr; |
| |
| if (parent_) { |
| if (parent_->first_child_ == this) |
| parent_->first_child_ = frame; |
| if (parent_->last_child_ == this) |
| parent_->last_child_ = frame; |
| // FIXME: This is due to the fact that the |frame| may be a provisional |
| // local frame, because we don't know if the navigation will result in |
| // an actual page or something else, like a download. The PlzNavigate |
| // project will remove the need for provisional local frames. |
| frame->parent_ = parent_; |
| } |
| |
| if (previous_sibling_) { |
| previous_sibling_->next_sibling_ = frame; |
| swap(previous_sibling_, frame->previous_sibling_); |
| } |
| if (next_sibling_) { |
| next_sibling_->previous_sibling_ = frame; |
| swap(next_sibling_, frame->next_sibling_); |
| } |
| |
| if (opener_) { |
| frame->SetOpener(opener_); |
| SetOpener(nullptr); |
| } |
| opened_frame_tracker_->TransferTo(frame); |
| |
| Page* page = old_frame->GetPage(); |
| AtomicString name = old_frame->Tree().GetName(); |
| FrameOwner* owner = old_frame->Owner(); |
| |
| v8::HandleScope handle_scope(v8::Isolate::GetCurrent()); |
| WindowProxyManager::GlobalProxyVector global_proxies; |
| old_frame->GetWindowProxyManager()->ClearForSwap(); |
| old_frame->GetWindowProxyManager()->ReleaseGlobalProxies(global_proxies); |
| |
| // Although the Document in this frame is now unloaded, many resources |
| // associated with the frame itself have not yet been freed yet. |
| old_frame->Detach(FrameDetachType::kSwap); |
| |
| // Clone the state of the current Frame into the one being swapped in. |
| // FIXME: This is a bit clunky; this results in pointless decrements and |
| // increments of connected subframes. |
| if (frame->IsWebLocalFrame()) { |
| // TODO(dcheng): in an ideal world, both branches would just use |
| // WebFrame's initializeCoreFrame() helper. However, Blink |
| // currently requires a 'provisional' local frame to serve as a |
| // placeholder for loading state when swapping to a local frame. |
| // In this case, the core LocalFrame is already initialized, so just |
| // update a bit of state. |
| LocalFrame& local_frame = *ToWebLocalFrameImpl(frame)->GetFrame(); |
| DCHECK_EQ(owner, local_frame.Owner()); |
| if (owner) { |
| owner->SetContentFrame(local_frame); |
| |
| if (owner->IsLocal()) { |
| ToHTMLFrameOwnerElement(owner)->SetEmbeddedContentView( |
| local_frame.View()); |
| } |
| } else { |
| local_frame.GetPage()->SetMainFrame(&local_frame); |
| // This trace event is needed to detect the main frame of the |
| // renderer in telemetry metrics. See crbug.com/692112#c11. |
| TRACE_EVENT_INSTANT1("loading", "markAsMainFrame", |
| TRACE_EVENT_SCOPE_THREAD, "frame", &local_frame); |
| } |
| local_frame.SetIsProvisional(false); |
| } else { |
| ToWebRemoteFrameImpl(frame)->InitializeCoreFrame(*page, owner, name); |
| } |
| |
| Frame* new_frame = ToCoreFrame(*frame); |
| |
| if (parent_ && old_frame->HasBeenActivated()) |
| new_frame->UpdateUserActivationInFrameTree(); |
| |
| new_frame->GetWindowProxyManager()->SetGlobalProxies(global_proxies); |
| |
| parent_ = nullptr; |
| |
| if (owner && owner->IsLocal()) { |
| if (new_frame && new_frame->IsLocalFrame()) { |
| probe::frameOwnerContentUpdated(ToLocalFrame(new_frame), |
| ToHTMLFrameOwnerElement(owner)); |
| } else if (old_frame && old_frame->IsLocalFrame()) { |
| probe::frameOwnerContentUpdated(ToLocalFrame(old_frame), |
| ToHTMLFrameOwnerElement(owner)); |
| } |
| } |
| |
| return true; |
| } |
| |
| void WebFrame::Detach() { |
| ToCoreFrame(*this)->Detach(FrameDetachType::kRemove); |
| } |
| |
| WebSecurityOrigin WebFrame::GetSecurityOrigin() const { |
| return WebSecurityOrigin( |
| ToCoreFrame(*this)->GetSecurityContext()->GetSecurityOrigin()); |
| } |
| |
| void WebFrame::SetFrameOwnerPolicy( |
| WebSandboxFlags flags, |
| const blink::ParsedFeaturePolicy& container_policy) { |
| // At the moment, this is only used to replicate sandbox flags and container |
| // policy for frames with a remote owner. |
| RemoteFrameOwner* owner = ToRemoteFrameOwner(ToCoreFrame(*this)->Owner()); |
| DCHECK(owner); |
| owner->SetSandboxFlags(static_cast<SandboxFlags>(flags)); |
| owner->SetContainerPolicy(container_policy); |
| } |
| |
| WebInsecureRequestPolicy WebFrame::GetInsecureRequestPolicy() const { |
| return ToCoreFrame(*this)->GetSecurityContext()->GetInsecureRequestPolicy(); |
| } |
| |
| std::vector<unsigned> WebFrame::GetInsecureRequestToUpgrade() const { |
| SecurityContext::InsecureNavigationsSet* set = |
| ToCoreFrame(*this)->GetSecurityContext()->InsecureNavigationsToUpgrade(); |
| return SecurityContext::SerializeInsecureNavigationSet(*set); |
| } |
| |
| void WebFrame::SetFrameOwnerProperties( |
| const WebFrameOwnerProperties& properties) { |
| // At the moment, this is only used to replicate frame owner properties |
| // for frames with a remote owner. |
| RemoteFrameOwner* owner = ToRemoteFrameOwner(ToCoreFrame(*this)->Owner()); |
| DCHECK(owner); |
| |
| Frame* frame = ToCoreFrame(*this); |
| DCHECK(frame); |
| |
| if (frame->IsLocalFrame()) { |
| ToLocalFrame(frame)->GetDocument()->WillChangeFrameOwnerProperties( |
| properties.margin_width, properties.margin_height, |
| static_cast<ScrollbarMode>(properties.scrolling_mode), |
| properties.is_display_none); |
| } |
| |
| owner->SetBrowsingContextContainerName(properties.name); |
| owner->SetScrollingMode(properties.scrolling_mode); |
| owner->SetMarginWidth(properties.margin_width); |
| owner->SetMarginHeight(properties.margin_height); |
| owner->SetAllowFullscreen(properties.allow_fullscreen); |
| owner->SetAllowPaymentRequest(properties.allow_payment_request); |
| owner->SetIsDisplayNone(properties.is_display_none); |
| owner->SetCsp(properties.required_csp); |
| } |
| |
| void WebFrame::Collapse(bool collapsed) { |
| FrameOwner* owner = ToCoreFrame(*this)->Owner(); |
| DCHECK(owner->IsLocal()); |
| ToHTMLFrameOwnerElement(owner)->SetCollapsed(collapsed); |
| } |
| |
| WebFrame* WebFrame::Opener() const { |
| return opener_; |
| } |
| |
| void WebFrame::SetOpener(WebFrame* opener) { |
| if (opener_) |
| opener_->opened_frame_tracker_->Remove(this); |
| if (opener) |
| opener->opened_frame_tracker_->Add(this); |
| opener_ = opener; |
| } |
| |
| void WebFrame::ClearOpener() { |
| SetOpener(nullptr); |
| } |
| |
| void WebFrame::InsertAfter(WebFrame* new_child, WebFrame* previous_sibling) { |
| new_child->parent_ = this; |
| |
| WebFrame* next; |
| if (!previous_sibling) { |
| // Insert at the beginning if no previous sibling is specified. |
| next = first_child_; |
| first_child_ = new_child; |
| } else { |
| DCHECK_EQ(previous_sibling->parent_, this); |
| next = previous_sibling->next_sibling_; |
| previous_sibling->next_sibling_ = new_child; |
| new_child->previous_sibling_ = previous_sibling; |
| } |
| |
| if (next) { |
| new_child->next_sibling_ = next; |
| next->previous_sibling_ = new_child; |
| } else { |
| last_child_ = new_child; |
| } |
| |
| ToCoreFrame(*this)->Tree().InvalidateScopedChildCount(); |
| ToCoreFrame(*this)->GetPage()->IncrementSubframeCount(); |
| } |
| |
| void WebFrame::AppendChild(WebFrame* child) { |
| // TODO(dcheng): Original code asserts that the frames have the same Page. |
| // We should add an equivalent check... figure out what. |
| InsertAfter(child, last_child_); |
| } |
| |
| void WebFrame::RemoveChild(WebFrame* child) { |
| child->parent_ = nullptr; |
| |
| if (first_child_ == child) |
| first_child_ = child->next_sibling_; |
| else |
| child->previous_sibling_->next_sibling_ = child->next_sibling_; |
| |
| if (last_child_ == child) |
| last_child_ = child->previous_sibling_; |
| else |
| child->next_sibling_->previous_sibling_ = child->previous_sibling_; |
| |
| child->previous_sibling_ = child->next_sibling_ = nullptr; |
| |
| ToCoreFrame(*this)->Tree().InvalidateScopedChildCount(); |
| ToCoreFrame(*this)->GetPage()->DecrementSubframeCount(); |
| } |
| |
| void WebFrame::SetParent(WebFrame* parent) { |
| parent_ = parent; |
| } |
| |
| WebFrame* WebFrame::Parent() const { |
| return parent_; |
| } |
| |
| WebFrame* WebFrame::Top() const { |
| WebFrame* frame = const_cast<WebFrame*>(this); |
| for (WebFrame* parent = frame; parent; parent = parent->parent_) |
| frame = parent; |
| return frame; |
| } |
| |
| WebFrame* WebFrame::FirstChild() const { |
| return first_child_; |
| } |
| |
| WebFrame* WebFrame::NextSibling() const { |
| return next_sibling_; |
| } |
| |
| WebFrame* WebFrame::TraverseNext() const { |
| if (Frame* frame = ToCoreFrame(*this)) |
| return FromFrame(frame->Tree().TraverseNext()); |
| return nullptr; |
| } |
| |
| WebFrame* WebFrame::FromFrameOwnerElement(const WebNode& web_node) { |
| Node* node = web_node; |
| |
| if (!node->IsFrameOwnerElement()) |
| return nullptr; |
| return FromFrame(ToHTMLFrameOwnerElement(node)->ContentFrame()); |
| } |
| |
| bool WebFrame::IsLoading() const { |
| if (Frame* frame = ToCoreFrame(*this)) |
| return frame->IsLoading(); |
| return false; |
| } |
| |
| WebFrame* WebFrame::FromFrame(Frame* frame) { |
| if (!frame) |
| return nullptr; |
| |
| if (frame->IsLocalFrame()) |
| return WebLocalFrameImpl::FromFrame(ToLocalFrame(*frame)); |
| return WebRemoteFrameImpl::FromFrame(ToRemoteFrame(*frame)); |
| } |
| |
| WebFrame::WebFrame(WebTreeScopeType scope) |
| : scope_(scope), |
| parent_(nullptr), |
| previous_sibling_(nullptr), |
| next_sibling_(nullptr), |
| first_child_(nullptr), |
| last_child_(nullptr), |
| opener_(nullptr), |
| opened_frame_tracker_(new OpenedFrameTracker) {} |
| |
| WebFrame::~WebFrame() { |
| opened_frame_tracker_.reset(nullptr); |
| } |
| |
| void WebFrame::TraceFrame(Visitor* visitor, WebFrame* frame) { |
| if (!frame) |
| return; |
| |
| if (frame->IsWebLocalFrame()) |
| visitor->Trace(ToWebLocalFrameImpl(frame)); |
| else |
| visitor->Trace(ToWebRemoteFrameImpl(frame)); |
| } |
| |
| void WebFrame::TraceFrames(Visitor* visitor, WebFrame* frame) { |
| DCHECK(frame); |
| TraceFrame(visitor, frame->parent_); |
| for (WebFrame* child = frame->FirstChild(); child; |
| child = child->NextSibling()) |
| TraceFrame(visitor, child); |
| } |
| |
| void WebFrame::Close() { |
| opened_frame_tracker_->Dispose(); |
| } |
| |
| void WebFrame::DetachFromParent() { |
| // TODO(dcheng): This should really just check if there's a parent, and call |
| // RemoveChild() if so. Once provisional frames are removed, this check can be |
| // simplified to just check Parent(). See https://crbug.com/578349. |
| const blink::Frame* frame = ToCoreFrame(*this); |
| if (frame->Owner() && frame->Owner()->ContentFrame() == frame) |
| Parent()->RemoveChild(this); |
| } |
| |
| Frame* WebFrame::ToCoreFrame(const WebFrame& frame) { |
| if (frame.IsWebLocalFrame()) |
| return ToWebLocalFrameImpl(frame).GetFrame(); |
| if (frame.IsWebRemoteFrame()) |
| return ToWebRemoteFrameImpl(frame).GetFrame(); |
| NOTREACHED(); |
| return nullptr; |
| } |
| |
| } // namespace blink |