blob: c7e2ba29b890061215af00b1a636e6a49ad07e15 [file] [log] [blame]
/*
* Copyright (C) 2006, 2007, 2009 Apple Inc. All rights reserved.
*
* 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 "core/html/HTMLFrameOwnerElement.h"
#include "bindings/core/v8/ExceptionMessages.h"
#include "bindings/core/v8/ExceptionState.h"
#include "core/dom/AXObjectCache.h"
#include "core/dom/ExceptionCode.h"
#include "core/dom/events/Event.h"
#include "core/exported/WebPluginContainerImpl.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameClient.h"
#include "core/frame/LocalFrameView.h"
#include "core/frame/RemoteFrameView.h"
#include "core/layout/LayoutEmbeddedContent.h"
#include "core/loader/DocumentLoader.h"
#include "core/loader/FrameLoadRequest.h"
#include "core/loader/FrameLoader.h"
#include "core/page/Page.h"
#include "core/page/scrolling/RootScrollerController.h"
#include "core/timing/DOMWindowPerformance.h"
#include "core/timing/WindowPerformance.h"
#include "platform/heap/HeapAllocator.h"
#include "platform/weborigin/SecurityOrigin.h"
#include "public/platform/modules/fetch/fetch_api_request.mojom-shared.h"
namespace blink {
namespace {
using PluginSet = PersistentHeapHashSet<Member<WebPluginContainerImpl>>;
PluginSet& PluginsPendingDispose() {
DEFINE_STATIC_LOCAL(PluginSet, set, ());
return set;
}
} // namespace
SubframeLoadingDisabler::SubtreeRootSet&
SubframeLoadingDisabler::DisabledSubtreeRoots() {
DEFINE_STATIC_LOCAL(SubtreeRootSet, nodes, ());
return nodes;
}
// static
int HTMLFrameOwnerElement::PluginDisposeSuspendScope::suspend_count_ = 0;
void HTMLFrameOwnerElement::PluginDisposeSuspendScope::
PerformDeferredPluginDispose() {
DCHECK_EQ(suspend_count_, 1);
suspend_count_ = 0;
PluginSet dispose_set;
PluginsPendingDispose().swap(dispose_set);
for (const auto& plugin : dispose_set) {
plugin->Dispose();
}
}
HTMLFrameOwnerElement::HTMLFrameOwnerElement(const QualifiedName& tag_name,
Document& document)
: HTMLElement(tag_name, document),
content_frame_(nullptr),
embedded_content_view_(nullptr),
sandbox_flags_(kSandboxNone) {}
LayoutEmbeddedContent* HTMLFrameOwnerElement::GetLayoutEmbeddedContent() const {
// HTMLObjectElement and HTMLEmbedElement may return arbitrary layoutObjects
// when using fallback content.
if (!GetLayoutObject() || !GetLayoutObject()->IsLayoutEmbeddedContent())
return nullptr;
return ToLayoutEmbeddedContent(GetLayoutObject());
}
void HTMLFrameOwnerElement::SetContentFrame(Frame& frame) {
// Make sure we will not end up with two frames referencing the same owner
// element.
DCHECK(!content_frame_ || content_frame_->Owner() != this);
// Disconnected frames should not be allowed to load.
DCHECK(isConnected());
content_frame_ = &frame;
for (ContainerNode* node = this; node; node = node->ParentOrShadowHostNode())
node->IncrementConnectedSubframeCount();
}
void HTMLFrameOwnerElement::ClearContentFrame() {
if (!content_frame_)
return;
DCHECK_EQ(content_frame_->Owner(), this);
content_frame_ = nullptr;
for (ContainerNode* node = this; node; node = node->ParentOrShadowHostNode())
node->DecrementConnectedSubframeCount();
}
void HTMLFrameOwnerElement::DisconnectContentFrame() {
if (!ContentFrame())
return;
// Removing a subframe that was still loading can impact the result of
// AllDescendantsAreComplete that is consulted by Document::ShouldComplete.
// Therefore we might need to re-check this after removing the subframe. The
// re-check is not needed for local frames (which will handle re-checking from
// FrameLoader::DidFinishNavigation that responds to LocalFrame::Detach).
// OTOH, re-checking is required for OOPIFs - see https://crbug.com/779433.
Document& parent_doc = GetDocument();
bool have_to_check_if_parent_is_completed = !parent_doc.IsLoadCompleted() &&
ContentFrame()->IsRemoteFrame() &&
ContentFrame()->IsLoading();
// FIXME: Currently we don't do this in removedFrom because this causes an
// unload event in the subframe which could execute script that could then
// reach up into this document and then attempt to look back down. We should
// see if this behavior is really needed as Gecko does not allow this.
ContentFrame()->Detach(FrameDetachType::kRemove);
// Check if removing the subframe caused |parent_doc| to finish loading.
if (have_to_check_if_parent_is_completed)
parent_doc.CheckCompleted();
}
HTMLFrameOwnerElement::~HTMLFrameOwnerElement() {
// An owner must by now have been informed of detachment
// when the frame was closed.
DCHECK(!content_frame_);
}
Document* HTMLFrameOwnerElement::contentDocument() const {
return (content_frame_ && content_frame_->IsLocalFrame())
? ToLocalFrame(content_frame_)->GetDocument()
: nullptr;
}
DOMWindow* HTMLFrameOwnerElement::contentWindow() const {
return content_frame_ ? content_frame_->DomWindow() : nullptr;
}
void HTMLFrameOwnerElement::SetSandboxFlags(SandboxFlags flags) {
sandbox_flags_ = flags;
// Recalculate the container policy in case the allow-same-origin flag has
// changed.
container_policy_ = ConstructContainerPolicy(nullptr, nullptr);
// Don't notify about updates if ContentFrame() is null, for example when
// the subframe hasn't been created yet.
if (ContentFrame()) {
GetDocument().GetFrame()->Client()->DidChangeFramePolicy(
ContentFrame(), sandbox_flags_, container_policy_);
}
}
bool HTMLFrameOwnerElement::IsKeyboardFocusable() const {
return content_frame_ && HTMLElement::IsKeyboardFocusable();
}
void HTMLFrameOwnerElement::DisposePluginSoon(WebPluginContainerImpl* plugin) {
if (PluginDisposeSuspendScope::suspend_count_) {
PluginsPendingDispose().insert(plugin);
PluginDisposeSuspendScope::suspend_count_ |= 1;
} else
plugin->Dispose();
}
void HTMLFrameOwnerElement::UpdateContainerPolicy(Vector<String>* messages,
bool* old_syntax) {
container_policy_ = ConstructContainerPolicy(messages, old_syntax);
// Don't notify about updates if ContentFrame() is null, for example when
// the subframe hasn't been created yet.
if (ContentFrame()) {
GetDocument().GetFrame()->Client()->DidChangeFramePolicy(
ContentFrame(), sandbox_flags_, container_policy_);
}
}
void HTMLFrameOwnerElement::FrameOwnerPropertiesChanged() {
// Don't notify about updates if ContentFrame() is null, for example when
// the subframe hasn't been created yet.
if (ContentFrame()) {
GetDocument().GetFrame()->Client()->DidChangeFrameOwnerProperties(this);
}
}
void HTMLFrameOwnerElement::AddResourceTiming(const ResourceTimingInfo& info) {
// Resource timing info should only be reported if the subframe is attached.
DCHECK(ContentFrame() && ContentFrame()->IsLocalFrame());
DOMWindowPerformance::performance(*GetDocument().domWindow())
->GenerateAndAddResourceTiming(info, localName());
}
void HTMLFrameOwnerElement::DispatchLoad() {
DispatchScopedEvent(Event::Create(EventTypeNames::load));
}
const ParsedFeaturePolicy& HTMLFrameOwnerElement::ContainerPolicy() const {
return container_policy_;
}
Document* HTMLFrameOwnerElement::getSVGDocument(
ExceptionState& exception_state) const {
Document* doc = contentDocument();
if (doc && doc->IsSVGDocument())
return doc;
return nullptr;
}
void HTMLFrameOwnerElement::SetEmbeddedContentView(
EmbeddedContentView* embedded_content_view) {
if (embedded_content_view == embedded_content_view_)
return;
Document* doc = contentDocument();
if (doc && doc->GetFrame()) {
bool will_be_display_none = !embedded_content_view;
if (IsDisplayNone() != will_be_display_none) {
doc->WillChangeFrameOwnerProperties(
MarginWidth(), MarginHeight(), ScrollingMode(), will_be_display_none);
}
}
if (embedded_content_view_) {
if (embedded_content_view_->IsAttached()) {
embedded_content_view_->DetachFromLayout();
if (embedded_content_view_->IsPluginView())
DisposePluginSoon(ToWebPluginContainerImpl(embedded_content_view_));
else
embedded_content_view_->Dispose();
}
}
embedded_content_view_ = embedded_content_view;
FrameOwnerPropertiesChanged();
LayoutEmbeddedContent* layout_embedded_content =
ToLayoutEmbeddedContent(GetLayoutObject());
if (!layout_embedded_content)
return;
if (embedded_content_view_) {
// TODO(crbug.com/729196): Trace why LocalFrameView::DetachFromLayout
// crashes. Perhaps view is getting reattached while document is shutting
// down.
if (doc) {
CHECK_NE(doc->Lifecycle().GetState(), DocumentLifecycle::kStopping);
}
layout_embedded_content->UpdateOnEmbeddedContentViewChange();
DCHECK_EQ(GetDocument().View(), layout_embedded_content->GetFrameView());
DCHECK(layout_embedded_content->GetFrameView());
embedded_content_view_->AttachToLayout();
}
GetDocument().GetRootScrollerController().DidUpdateIFrameFrameView(*this);
if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache())
cache->ChildrenChanged(layout_embedded_content);
}
EmbeddedContentView* HTMLFrameOwnerElement::ReleaseEmbeddedContentView() {
if (!embedded_content_view_)
return nullptr;
if (embedded_content_view_->IsAttached())
embedded_content_view_->DetachFromLayout();
LayoutEmbeddedContent* layout_embedded_content =
ToLayoutEmbeddedContent(GetLayoutObject());
if (layout_embedded_content) {
if (AXObjectCache* cache = GetDocument().ExistingAXObjectCache())
cache->ChildrenChanged(layout_embedded_content);
}
return embedded_content_view_.Release();
}
bool HTMLFrameOwnerElement::LoadOrRedirectSubframe(
const KURL& url,
const AtomicString& frame_name,
bool replace_current_item) {
UpdateContainerPolicy();
if (ContentFrame()) {
ContentFrame()->Navigate(GetDocument(), url, replace_current_item,
UserGestureStatus::kNone);
return true;
}
if (!SubframeLoadingDisabler::CanLoadFrame(*this))
return false;
if (GetDocument().GetFrame()->GetPage()->SubframeCount() >=
Page::kMaxNumberOfFrames)
return false;
LocalFrame* child_frame =
GetDocument().GetFrame()->Client()->CreateFrame(frame_name, this);
DCHECK_EQ(ContentFrame(), child_frame);
if (!child_frame)
return false;
ResourceRequest request(url);
ReferrerPolicy policy = ReferrerPolicyAttribute();
if (policy != kReferrerPolicyDefault) {
request.SetHTTPReferrer(SecurityPolicy::GenerateReferrer(
policy, url, GetDocument().OutgoingReferrer()));
}
FrameLoadType child_load_type = kFrameLoadTypeInitialInChildFrame;
if (!GetDocument().LoadEventFinished() &&
GetDocument().Loader()->LoadType() ==
kFrameLoadTypeReloadBypassingCache) {
child_load_type = kFrameLoadTypeReloadBypassingCache;
request.SetCacheMode(mojom::FetchCacheMode::kBypassCache);
}
// Plug-ins should not load via service workers as plug-ins may have their
// own origin checking logic that may get confused if service workers respond
// with resources from another origin.
// https://w3c.github.io/ServiceWorker/#implementer-concerns
if (IsPlugin())
request.SetServiceWorkerMode(WebURLRequest::ServiceWorkerMode::kNone);
child_frame->Loader().Load(FrameLoadRequest(&GetDocument(), request),
child_load_type);
return true;
}
void HTMLFrameOwnerElement::Trace(blink::Visitor* visitor) {
visitor->Trace(content_frame_);
visitor->Trace(embedded_content_view_);
HTMLElement::Trace(visitor);
FrameOwner::Trace(visitor);
}
} // namespace blink