blob: cc24d583ac920c5d7f0d667b0890fd323c58280a [file] [log] [blame]
/*
* Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
* 1999 Lars Knoll <knoll@kde.org>
* 1999 Antti Koivisto <koivisto@kde.org>
* 2000 Simon Hausmann <hausmann@kde.org>
* 2000 Stefan Schimanski <1Stein@gmx.de>
* 2001 George Staikos <staikos@kde.org>
* Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011 Apple Inc. All
* rights reserved.
* Copyright (C) 2005 Alexey Proskuryakov <ap@nypop.com>
* Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
* Copyright (C) 2008 Eric Seidel <eric@webkit.org>
* Copyright (C) 2008 Google Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "third_party/blink/renderer/core/frame/frame.h"
#include <memory>
#include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom-blink.h"
#include "third_party/blink/public/web/web_local_frame_client.h"
#include "third_party/blink/public/web/web_remote_frame_client.h"
#include "third_party/blink/renderer/bindings/core/v8/window_proxy_manager.h"
#include "third_party/blink/renderer/core/accessibility/ax_object_cache.h"
#include "third_party/blink/renderer/core/dom/document_type.h"
#include "third_party/blink/renderer/core/dom/events/event.h"
#include "third_party/blink/renderer/core/dom/increment_load_event_delay_count.h"
#include "third_party/blink/renderer/core/dom/node_computed_style.h"
#include "third_party/blink/renderer/core/execution_context/window_agent_factory.h"
#include "third_party/blink/renderer/core/frame/local_dom_window.h"
#include "third_party/blink/renderer/core/frame/page_dismissal_scope.h"
#include "third_party/blink/renderer/core/frame/remote_frame_owner.h"
#include "third_party/blink/renderer/core/frame/settings.h"
#include "third_party/blink/renderer/core/frame/web_remote_frame_impl.h"
#include "third_party/blink/renderer/core/html/html_frame_element_base.h"
#include "third_party/blink/renderer/core/input/event_handler.h"
#include "third_party/blink/renderer/core/layout/layout_embedded_content.h"
#include "third_party/blink/renderer/core/loader/empty_clients.h"
#include "third_party/blink/renderer/core/loader/form_submission.h"
#include "third_party/blink/renderer/core/page/focus_controller.h"
#include "third_party/blink/renderer/core/page/page.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/platform/bindings/script_forbidden_scope.h"
#include "third_party/blink/renderer/platform/instrumentation/instance_counters.h"
#include "third_party/blink/renderer/platform/instrumentation/use_counter.h"
#include "third_party/blink/renderer/platform/loader/fetch/resource_error.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
namespace blink {
// static
Frame* Frame::ResolveFrame(const FrameToken& frame_token) {
if (frame_token.Is<RemoteFrameToken>())
return RemoteFrame::FromFrameToken(frame_token.GetAs<RemoteFrameToken>());
DCHECK(frame_token.Is<LocalFrameToken>());
return LocalFrame::FromFrameToken(frame_token.GetAs<LocalFrameToken>());
}
Frame::~Frame() {
InstanceCounters::DecrementCounter(InstanceCounters::kFrameCounter);
DCHECK(!owner_);
DCHECK(IsDetached());
}
void Frame::Trace(Visitor* visitor) const {
visitor->Trace(tree_node_);
visitor->Trace(page_);
visitor->Trace(owner_);
visitor->Trace(window_proxy_manager_);
visitor->Trace(dom_window_);
visitor->Trace(client_);
visitor->Trace(opener_);
visitor->Trace(parent_);
visitor->Trace(previous_sibling_);
visitor->Trace(next_sibling_);
visitor->Trace(first_child_);
visitor->Trace(last_child_);
visitor->Trace(provisional_frame_);
visitor->Trace(navigation_rate_limiter_);
visitor->Trace(window_agent_factory_);
visitor->Trace(opened_frame_tracker_);
}
bool Frame::Detach(FrameDetachType type) {
DCHECK(client_);
// Detach() can be re-entered, so this can't simply DCHECK(IsAttached()).
DCHECK(!IsDetached());
lifecycle_.AdvanceTo(FrameLifecycle::kDetaching);
PageDismissalScope in_page_dismissal;
if (!DetachImpl(type))
return false;
DCHECK(!IsDetached());
DCHECK(client_);
GetPage()->GetFocusController().FrameDetached(this);
// FrameDetached() can fire JS event listeners, so `this` might have been
// reentrantly detached.
if (!client_)
return false;
DCHECK(!IsDetached());
// TODO(dcheng): FocusController::FrameDetached() *should* fire JS events,
// hence the above check for `client_` being null. However, when this was
// previously placed before the `FrameDetached()` call, nothing crashes, which
// is suspicious. Investigate if we really don't need to fire JS events--and
// if we don't, move `forbid_scripts` up to be instantiated sooner and
// simplify this code.
ScriptForbiddenScope forbid_scripts;
if (type == FrameDetachType::kRemove) {
if (provisional_frame_) {
provisional_frame_->Detach(FrameDetachType::kRemove);
}
SetOpener(nullptr);
// Clearing the window proxies can call back into `LocalFrameClient`, so
// this must be done before nulling out `client_` below.
GetWindowProxyManager()->ClearForClose();
} else {
// In the case of a swap, detach is carefully coordinated with `Swap()`.
// Intentionally avoid clearing the opener with `SetOpener(nullptr)` here,
// since `Swap()` needs the original value to clone to the new frame.
DCHECK_EQ(FrameDetachType::kSwap, type);
// Clearing the window proxies can call back into `LocalFrameClient`, so
// this must be done before nulling out `client_` below.
// `ClearForSwap()` preserves the v8::Objects that represent the global
// proxies; `Swap()` will later use `ReleaseGlobalProxies()` +
// `SetGlobalProxies()` to adopt the global proxies into the new frame.
GetWindowProxyManager()->ClearForSwap();
}
// After this, we must no longer talk to the client since this clears
// its owning reference back to our owning LocalFrame.
client_->Detached(type);
client_ = nullptr;
// Mark the frame as detached once |client_| is null, as most of the frame has
// been torn down at this point.
// TODO(dcheng): Once https://crbug.com/820782 is fixed, Frame::Client() will
// also assert that it is only accessed when the frame is not detached.
lifecycle_.AdvanceTo(FrameLifecycle::kDetached);
// TODO(dcheng): This currently needs to happen after calling
// FrameClient::Detached() to make it easier for FrameClient::Detached()
// implementations to detect provisional frames and avoid removing them from
// the frame tree. https://crbug.com/578349.
DisconnectOwnerElement();
page_ = nullptr;
embedding_token_ = base::nullopt;
return true;
}
void Frame::DisconnectOwnerElement() {
if (!owner_)
return;
// TODO(https://crbug.com/578349): If this is a provisional frame, the frame
// owner doesn't actually point to this frame, so don't clear it. Note that
// this can't use IsProvisional() because the |client_| is null already.
if (owner_->ContentFrame() == this)
owner_->ClearContentFrame();
owner_ = nullptr;
}
Page* Frame::GetPage() const {
return page_;
}
bool Frame::IsMainFrame() const {
return !Tree().Parent();
}
bool Frame::IsCrossOriginToMainFrame() const {
DCHECK(GetSecurityContext());
const SecurityOrigin* security_origin =
GetSecurityContext()->GetSecurityOrigin();
return !security_origin->CanAccess(
Tree().Top().GetSecurityContext()->GetSecurityOrigin());
}
bool Frame::IsCrossOriginToParentFrame() const {
DCHECK(GetSecurityContext());
if (IsMainFrame())
return false;
Frame* parent = Tree().Parent();
const SecurityOrigin* parent_security_origin =
parent->GetSecurityContext()->GetSecurityOrigin();
const SecurityOrigin* security_origin =
GetSecurityContext()->GetSecurityOrigin();
return !security_origin->CanAccess(parent_security_origin);
}
HTMLFrameOwnerElement* Frame::DeprecatedLocalOwner() const {
return DynamicTo<HTMLFrameOwnerElement>(owner_.Get());
}
static ChromeClient& GetEmptyChromeClient() {
DEFINE_STATIC_LOCAL(Persistent<EmptyChromeClient>, client,
(MakeGarbageCollected<EmptyChromeClient>()));
return *client;
}
ChromeClient& Frame::GetChromeClient() const {
if (Page* page = this->GetPage())
return page->GetChromeClient();
return GetEmptyChromeClient();
}
Frame* Frame::FindUnsafeParentScrollPropagationBoundary() {
Frame* current_frame = this;
Frame* ancestor_frame = Tree().Parent();
while (ancestor_frame) {
if (!ancestor_frame->GetSecurityContext()->GetSecurityOrigin()->CanAccess(
GetSecurityContext()->GetSecurityOrigin()))
return current_frame;
current_frame = ancestor_frame;
ancestor_frame = ancestor_frame->Tree().Parent();
}
return nullptr;
}
LayoutEmbeddedContent* Frame::OwnerLayoutObject() const {
if (!DeprecatedLocalOwner())
return nullptr;
return DeprecatedLocalOwner()->GetLayoutEmbeddedContent();
}
Settings* Frame::GetSettings() const {
if (GetPage())
return &GetPage()->GetSettings();
return nullptr;
}
WindowProxy* Frame::GetWindowProxy(DOMWrapperWorld& world) {
return window_proxy_manager_->GetWindowProxy(world);
}
void Frame::DidChangeVisibilityState() {
HeapVector<Member<Frame>> child_frames;
for (Frame* child = Tree().FirstChild(); child;
child = child->Tree().NextSibling())
child_frames.push_back(child);
for (wtf_size_t i = 0; i < child_frames.size(); ++i)
child_frames[i]->DidChangeVisibilityState();
}
void Frame::NotifyUserActivationInFrameTree(
mojom::blink::UserActivationNotificationType notification_type) {
for (Frame* node = this; node; node = node->Tree().Parent()) {
node->user_activation_state_.Activate(notification_type);
}
// See the "Same-origin Visibility" section in |UserActivationState| class
// doc.
auto* local_frame = DynamicTo<LocalFrame>(this);
if (local_frame &&
RuntimeEnabledFeatures::UserActivationSameOriginVisibilityEnabled()) {
const SecurityOrigin* security_origin =
local_frame->GetSecurityContext()->GetSecurityOrigin();
Frame& root = Tree().Top();
for (Frame* node = &root; node; node = node->Tree().TraverseNext(&root)) {
auto* local_frame_node = DynamicTo<LocalFrame>(node);
if (local_frame_node &&
security_origin->CanAccess(
local_frame_node->GetSecurityContext()->GetSecurityOrigin())) {
node->user_activation_state_.Activate(notification_type);
}
}
}
}
bool Frame::ConsumeTransientUserActivationInFrameTree() {
bool was_active = user_activation_state_.IsActive();
Frame& root = Tree().Top();
// To record UMA once per consumption, we arbitrarily picked the LocalFrame
// for root.
if (IsA<LocalFrame>(root))
root.user_activation_state_.RecordPreconsumptionUma();
for (Frame* node = &root; node; node = node->Tree().TraverseNext(&root))
node->user_activation_state_.ConsumeIfActive();
return was_active;
}
void Frame::ClearUserActivationInFrameTree() {
for (Frame* node = this; node; node = node->Tree().TraverseNext(this))
node->user_activation_state_.Clear();
}
void Frame::SetOwner(FrameOwner* owner) {
owner_ = owner;
UpdateInertIfPossible();
UpdateInheritedEffectiveTouchActionIfPossible();
}
bool Frame::IsAdSubframe() const {
return ad_frame_type_ != mojom::blink::AdFrameType::kNonAd;
}
bool Frame::IsAdRoot() const {
return ad_frame_type_ == mojom::blink::AdFrameType::kRootAd;
}
void Frame::UpdateInertIfPossible() {
if (auto* frame_owner_element =
DynamicTo<HTMLFrameOwnerElement>(owner_.Get())) {
if (frame_owner_element->IsInert())
SetIsInert(true);
}
}
void Frame::UpdateInheritedEffectiveTouchActionIfPossible() {
if (owner_) {
Frame* owner_frame = owner_->ContentFrame();
if (owner_frame) {
SetInheritedEffectiveTouchAction(
owner_frame->InheritedEffectiveTouchAction());
}
}
}
void Frame::UpdateVisibleToHitTesting() {
bool parent_visible_to_hit_testing = true;
if (auto* parent = Tree().Parent())
parent_visible_to_hit_testing = parent->GetVisibleToHitTesting();
bool self_visible_to_hit_testing = true;
if (auto* local_owner = DynamicTo<HTMLFrameOwnerElement>(owner_.Get())) {
self_visible_to_hit_testing =
local_owner->GetLayoutObject()
? local_owner->GetLayoutObject()->Style()->VisibleToHitTesting()
: true;
}
bool visible_to_hit_testing =
parent_visible_to_hit_testing && self_visible_to_hit_testing;
bool changed = visible_to_hit_testing_ != visible_to_hit_testing;
visible_to_hit_testing_ = visible_to_hit_testing;
if (changed)
DidChangeVisibleToHitTesting();
}
const std::string& Frame::ToTraceValue() {
// token's ToString() is latin1.
if (!trace_value_)
trace_value_ = devtools_frame_token_.ToString();
return trace_value_.value();
}
void Frame::SetEmbeddingToken(const base::UnguessableToken& embedding_token) {
embedding_token_ = embedding_token;
if (auto* owner = DynamicTo<HTMLFrameOwnerElement>(Owner())) {
// The embedding token is also used as the AXTreeID to reference the child
// accessibility tree for an HTMLFrameOwnerElement, so we need to notify the
// AXObjectCache object whenever this changes, to get the AX tree updated.
if (AXObjectCache* cache = owner->GetDocument().ExistingAXObjectCache())
cache->EmbeddingTokenChanged(owner);
}
}
Frame::Frame(FrameClient* client,
Page& page,
FrameOwner* owner,
Frame* parent,
Frame* previous_sibling,
FrameInsertType insert_type,
const FrameToken& frame_token,
WindowProxyManager* window_proxy_manager,
WindowAgentFactory* inheriting_agent_factory)
: tree_node_(this),
page_(&page),
owner_(owner),
ad_frame_type_(mojom::blink::AdFrameType::kNonAd),
client_(client),
window_proxy_manager_(window_proxy_manager),
parent_(parent),
navigation_rate_limiter_(*this),
window_agent_factory_(inheriting_agent_factory
? inheriting_agent_factory
: MakeGarbageCollected<WindowAgentFactory>()),
is_loading_(false),
devtools_frame_token_(client->GetDevToolsFrameToken()),
frame_token_(frame_token) {
InstanceCounters::IncrementCounter(InstanceCounters::kFrameCounter);
if (parent_ && insert_type == FrameInsertType::kInsertInConstructor) {
parent_->InsertAfter(this, previous_sibling);
} else {
CHECK(!previous_sibling);
}
}
void Frame::Initialize() {
// This frame must either be local or remote.
DCHECK_NE(IsLocalFrame(), IsRemoteFrame());
if (owner_)
owner_->SetContentFrame(*this);
else
page_->SetMainFrame(this);
}
void Frame::FocusImpl() {
// This uses FocusDocumentView rather than SetFocusedFrame so that blur
// events are properly dispatched on any currently focused elements.
// It is currently only used when replicating focus changes for
// cross-process frames so |notify_embedder| is false to avoid sending
// DidFocus updates from FocusController to the browser process,
// which already knows the latest focused frame.
GetPage()->GetFocusController().FocusDocumentView(
this, false /* notify_embedder */);
}
void Frame::ApplyFrameOwnerProperties(
mojom::blink::FrameOwnerPropertiesPtr properties) {
// At the moment, this is only used to replicate frame owner properties
// for frames with a remote owner.
auto* owner = To<RemoteFrameOwner>(Owner());
owner->SetBrowsingContextContainerName(properties->name);
owner->SetScrollbarMode(properties->scrollbar_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->SetColorScheme(properties->color_scheme);
owner->SetRequiredCsp(properties->required_csp);
}
void Frame::InsertAfter(Frame* new_child, Frame* previous_sibling) {
// Parent must match the one set in the constructor
CHECK_EQ(new_child->parent_, this);
Frame* 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;
}
Tree().InvalidateScopedChildCount();
GetPage()->IncrementSubframeCount();
}
void Frame::ScheduleFormSubmission(FrameScheduler* scheduler,
FormSubmission* form_submission) {
form_submit_navigation_task_ = PostCancellableTask(
*scheduler->GetTaskRunner(TaskType::kDOMManipulation), FROM_HERE,
WTF::Bind(&FormSubmission::Navigate, WrapPersistent(form_submission)));
}
void Frame::CancelFormSubmission() {
form_submit_navigation_task_.Cancel();
}
bool Frame::IsFormSubmissionPending() {
return form_submit_navigation_task_.IsActive();
}
void Frame::FocusPage(LocalFrame* originating_frame) {
// We only allow focus to move to the |frame|'s page when the request comes
// from a user gesture. (See https://bugs.webkit.org/show_bug.cgi?id=33389.)
if (originating_frame &&
LocalFrame::HasTransientUserActivation(originating_frame)) {
// Ask the broswer process to focus the page.
GetPage()->GetChromeClient().FocusPage();
// Tattle on the frame that called |window.focus()|.
originating_frame->GetLocalFrameHostRemote().DidCallFocus();
}
// Always report the attempt to focus the page to the Chrome client for
// testing purposes (i.e. see WebViewTest.FocusExistingFrameOnNavigate()).
GetPage()->GetChromeClient().DidFocusPage();
}
void Frame::SetOpenerDoNotNotify(Frame* opener) {
if (opener_)
opener_->opened_frame_tracker_.Remove(this);
if (opener)
opener->opened_frame_tracker_.Add(this);
opener_ = opener;
}
Frame* Frame::Top() {
Frame* parent;
for (parent = this; parent->Parent(); parent = parent->Parent()) {
}
return parent;
}
bool Frame::Swap(WebFrame* new_web_frame) {
DCHECK(IsAttached());
using std::swap;
// Important: do not cache frame tree pointers (e.g. `previous_sibling_`,
// `next_sibling_`, `first_child_`, `last_child_`) here. It is possible for
// `Detach()` to mutate the frame tree and cause cached values to become
// invalid.
FrameOwner* owner = owner_;
FrameSwapScope frame_swap_scope(owner);
Page* page = page_;
AtomicString name = Tree().GetName();
// TODO(dcheng): This probably isn't necessary if we fix the ordering of
// events in `Swap()`, e.g. `Detach()` should not happen before
// `new_web_frame` is swapped in.
// 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.
auto* parent_local_frame = DynamicTo<LocalFrame>(parent_.Get());
std::unique_ptr<IncrementLoadEventDelayCount> delay_parent_load =
parent_local_frame ? std::make_unique<IncrementLoadEventDelayCount>(
*parent_local_frame->GetDocument())
: nullptr;
// 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.
if (!Detach(FrameDetachType::kSwap)) {
// If the Swap() fails, it should be because the frame has been detached
// already. Otherwise the caller will not detach the frame when we return
// false, and the browser and renderer will disagree about the destruction
// of |this|.
CHECK(IsDetached());
return false;
}
// Otherwise, on a successful `Detach()` for swap, `this` is now detached--but
// crucially--still linked into the frame tree.
if (provisional_frame_) {
// `this` is about to be replaced, so if `provisional_frame_` is set, it
// should match `frame` which is being swapped in.
DCHECK_EQ(provisional_frame_, WebFrame::ToCoreFrame(*new_web_frame));
provisional_frame_ = nullptr;
}
v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
WindowProxyManager::GlobalProxyVector global_proxies;
GetWindowProxyManager()->ReleaseGlobalProxies(global_proxies);
if (new_web_frame->IsWebRemoteFrame()) {
CHECK(!WebFrame::ToCoreFrame(*new_web_frame));
To<WebRemoteFrameImpl>(new_web_frame)
->InitializeCoreFrame(*page, owner, WebFrame::FromCoreFrame(parent_),
nullptr, FrameInsertType::kInsertLater, name,
&window_agent_factory());
// At this point, a `RemoteFrame` will have already updated
// `Page::MainFrame()` or `FrameOwner::ContentFrame()` as appropriate, and
// its `parent_` pointer is also populated.
} else {
// This is local frame created by `WebLocalFrame::CreateProvisional()`. The
// `parent` pointer was set when it was constructed; however,
// `Page::MainFrame()` or `FrameOwner::ContentFrame()` updates are deferred
// until after `new_frame` is linked into the frame tree.
// TODO(dcheng): Make local and remote frame updates more uniform.
}
Frame* new_frame = WebFrame::ToCoreFrame(*new_web_frame);
CHECK(new_frame);
// At this point, `new_frame->parent_` is correctly set, but `new_frame`'s
// sibling pointers are both still null and not yet updated. In addition, the
// parent frame (if any) still has not updated its `first_child_` and
// `last_child_` pointers.
CHECK_EQ(new_frame->parent_, parent_);
CHECK(!new_frame->previous_sibling_);
CHECK(!new_frame->next_sibling_);
if (previous_sibling_) {
previous_sibling_->next_sibling_ = new_frame;
}
swap(previous_sibling_, new_frame->previous_sibling_);
if (next_sibling_) {
next_sibling_->previous_sibling_ = new_frame;
}
swap(next_sibling_, new_frame->next_sibling_);
if (parent_) {
if (parent_->first_child_ == this) {
parent_->first_child_ = new_frame;
}
if (parent_->last_child_ == this) {
parent_->last_child_ = new_frame;
}
// Not strictly necessary, but keep state as self-consistent as possible.
parent_ = nullptr;
}
if (Frame* opener = opener_) {
SetOpenerDoNotNotify(nullptr);
new_frame->SetOpenerDoNotNotify(opener);
}
opened_frame_tracker_.TransferTo(new_frame);
// Clone the state of the current Frame into the one being swapped in.
if (auto* new_local_frame = DynamicTo<LocalFrame>(new_frame)) {
// A `LocalFrame` being swapped in is created provisionally, so
// `Page::MainFrame()` or `FrameOwner::ContentFrame()` needs to be updated
// to point to the newly swapped-in frame.
DCHECK_EQ(owner, new_local_frame->Owner());
if (owner) {
owner->SetContentFrame(*new_local_frame);
if (auto* frame_owner_element = DynamicTo<HTMLFrameOwnerElement>(owner)) {
frame_owner_element->SetEmbeddedContentView(new_local_frame->View());
}
} else {
Page* other_page = new_local_frame->GetPage();
other_page->SetMainFrame(new_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",
::blink::ToTraceValue(new_local_frame));
}
}
new_frame->GetWindowProxyManager()->SetGlobalProxies(global_proxies);
if (auto* frame_owner_element = DynamicTo<HTMLFrameOwnerElement>(owner)) {
if (auto* new_local_frame = DynamicTo<LocalFrame>(new_frame)) {
probe::FrameOwnerContentUpdated(new_local_frame, frame_owner_element);
} else if (auto* old_local_frame = DynamicTo<LocalFrame>(this)) {
// TODO(dcheng): What is this probe for? Shouldn't it happen *before*
// detach?
probe::FrameOwnerContentUpdated(old_local_frame, frame_owner_element);
}
}
return true;
}
void Frame::RemoveChild(Frame* child) {
CHECK_EQ(child->parent_, this);
child->parent_ = nullptr;
if (first_child_ == child) {
first_child_ = child->next_sibling_;
} else {
CHECK(child->previous_sibling_)
<< " child " << child << " child->previous_sibling_ "
<< child->previous_sibling_;
child->previous_sibling_->next_sibling_ = child->next_sibling_;
}
if (last_child_ == child) {
last_child_ = child->previous_sibling_;
} else {
CHECK(child->next_sibling_);
child->next_sibling_->previous_sibling_ = child->previous_sibling_;
}
child->previous_sibling_ = child->next_sibling_ = nullptr;
Tree().InvalidateScopedChildCount();
GetPage()->DecrementSubframeCount();
}
void Frame::DetachFromParent() {
if (!Parent())
return;
// 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.
if (auto* local_frame = DynamicTo<LocalFrame>(this)) {
if (local_frame->IsProvisional()) {
return;
}
}
Parent()->RemoveChild(this);
}
STATIC_ASSERT_ENUM(FrameDetachType::kRemove,
WebRemoteFrameClient::DetachType::kRemove);
STATIC_ASSERT_ENUM(FrameDetachType::kSwap,
WebRemoteFrameClient::DetachType::kSwap);
} // namespace blink