Commit 3fa68570 authored by Philip Rogers's avatar Philip Rogers Committed by Commit Bot

Composite frames that are cross-origin to their parent

This patch fixes a bug in https://crrev.com/732194 where subframes that
were cross-origin to their parent were not composited. This mistake was
due to a misreading of Frame::IsCrossOriginSubframe which returns true
if the frame is cross origin to the main frame. "IsCrossOriginSubframe"
has been renamed to "IsCrossOriginToMainFrame" in
https://crrev.com/737198 to prevent similar mistakes.

This patch adds Frame::IsCrossOriginToParentFrame and uses it when
deciding whether to composite an iframe.

Bug: 1047497, 1014273
Change-Id: Iecef138be2b083953559fb856d60939cdfbc8c60
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2034031
Commit-Queue: Philip Rogers <pdr@chromium.org>
Reviewed-by: default avatarStefan Zager <szager@chromium.org>
Reviewed-by: default avatarDaniel Cheng <dcheng@chromium.org>
Auto-Submit: Philip Rogers <pdr@chromium.org>
Cr-Commit-Position: refs/heads/master@{#739016}
parent d704d1f2
...@@ -5739,6 +5739,8 @@ void Document::setDomain(const String& raw_domain, ...@@ -5739,6 +5739,8 @@ void Document::setDomain(const String& raw_domain,
? WebFeature::kDocumentDomainSetWithDefaultPort ? WebFeature::kDocumentDomainSetWithDefaultPort
: WebFeature::kDocumentDomainSetWithNonDefaultPort); : WebFeature::kDocumentDomainSetWithNonDefaultPort);
bool was_cross_origin_to_main_frame = frame_->IsCrossOriginToMainFrame(); bool was_cross_origin_to_main_frame = frame_->IsCrossOriginToMainFrame();
bool was_cross_origin_to_parent_frame =
frame_->IsCrossOriginToParentFrame();
GetMutableSecurityOrigin()->SetDomainFromDOM(new_domain); GetMutableSecurityOrigin()->SetDomainFromDOM(new_domain);
bool is_cross_origin_to_main_frame = frame_->IsCrossOriginToMainFrame(); bool is_cross_origin_to_main_frame = frame_->IsCrossOriginToMainFrame();
if (FrameScheduler* frame_scheduler = frame_->GetFrameScheduler()) if (FrameScheduler* frame_scheduler = frame_->GetFrameScheduler())
...@@ -5760,6 +5762,21 @@ void Document::setDomain(const String& raw_domain, ...@@ -5760,6 +5762,21 @@ void Document::setDomain(const String& raw_domain,
} }
} }
if (View() && was_cross_origin_to_parent_frame !=
frame_->IsCrossOriginToParentFrame()) {
View()->CrossOriginToParentFrameChanged();
}
// Notify all child frames if their cross-origin-to-parent status changed.
// TODO(pdr): This will notify even if |Frame::IsCrossOriginToParentFrame|
// is the same. Track whether each child was cross-origin-to-parent before
// and after changing the domain, and only notify the changed ones.
for (Frame* child = frame_->Tree().FirstChild(); child;
child = child->Tree().NextSibling()) {
auto* child_local_frame = DynamicTo<LocalFrame>(child);
if (child_local_frame && child_local_frame->View())
child_local_frame->View()->CrossOriginToParentFrameChanged();
}
frame_->GetScriptController().UpdateSecurityOrigin(GetSecurityOrigin()); frame_->GetScriptController().UpdateSecurityOrigin(GetSecurityOrigin());
} }
} }
......
...@@ -136,6 +136,22 @@ bool Frame::IsCrossOriginToMainFrame() const { ...@@ -136,6 +136,22 @@ bool Frame::IsCrossOriginToMainFrame() const {
Tree().Top().GetSecurityContext()->GetSecurityOrigin()); 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();
// TODO(dcheng, crbug.com/1049612): The parent security origin should not be
// null.
if (!parent_security_origin)
return true;
const SecurityOrigin* security_origin =
GetSecurityContext()->GetSecurityOrigin();
return !security_origin->CanAccess(parent_security_origin);
}
HTMLFrameOwnerElement* Frame::DeprecatedLocalOwner() const { HTMLFrameOwnerElement* Frame::DeprecatedLocalOwner() const {
return DynamicTo<HTMLFrameOwnerElement>(owner_.Get()); return DynamicTo<HTMLFrameOwnerElement>(owner_.Get());
} }
......
...@@ -107,7 +107,7 @@ class CORE_EXPORT Frame : public GarbageCollected<Frame> { ...@@ -107,7 +107,7 @@ class CORE_EXPORT Frame : public GarbageCollected<Frame> {
// //
// Important notes: // Important notes:
// - This function is not appropriate for determining if a subframe is // - This function is not appropriate for determining if a subframe is
// cross-origin to its parent. // cross-origin to its parent (see: |IsCrossOriginToParentFrame|).
// - The return value must NOT be cached. A frame can be reused across // - The return value must NOT be cached. A frame can be reused across
// navigations, so the return value can change over time. // navigations, so the return value can change over time.
// - The return value is inaccurate for a detached frame: it always // - The return value is inaccurate for a detached frame: it always
...@@ -115,6 +115,9 @@ class CORE_EXPORT Frame : public GarbageCollected<Frame> { ...@@ -115,6 +115,9 @@ class CORE_EXPORT Frame : public GarbageCollected<Frame> {
// TODO(dcheng): Move this to LocalDOMWindow and figure out the right // TODO(dcheng): Move this to LocalDOMWindow and figure out the right
// behavior for detached windows. // behavior for detached windows.
bool IsCrossOriginToMainFrame() const; bool IsCrossOriginToMainFrame() const;
// Returns true if this frame is a subframe and is cross-origin to the parent
// frame. See |IsCrossOriginToMainFrame| for important notes.
bool IsCrossOriginToParentFrame() const;
FrameOwner* Owner() const; FrameOwner* Owner() const;
void SetOwner(FrameOwner*); void SetOwner(FrameOwner*);
......
...@@ -3985,9 +3985,6 @@ void LocalFrameView::DeliverSynchronousIntersectionObservations() { ...@@ -3985,9 +3985,6 @@ void LocalFrameView::DeliverSynchronousIntersectionObservations() {
} }
void LocalFrameView::CrossOriginToMainFrameChanged() { void LocalFrameView::CrossOriginToMainFrameChanged() {
if (auto* owner = frame_->DeprecatedLocalOwner())
owner->FrameCrossOriginStatusChanged();
// If any of these conditions hold, then a change in cross-origin status does // If any of these conditions hold, then a change in cross-origin status does
// not affect throttling. // not affect throttling.
if (lifecycle_updates_throttled_ || IsSubtreeThrottled() || if (lifecycle_updates_throttled_ || IsSubtreeThrottled() ||
...@@ -4004,6 +4001,11 @@ void LocalFrameView::CrossOriginToMainFrameChanged() { ...@@ -4004,6 +4001,11 @@ void LocalFrameView::CrossOriginToMainFrameChanged() {
true); true);
} }
void LocalFrameView::CrossOriginToParentFrameChanged() {
if (auto* owner = frame_->DeprecatedLocalOwner())
owner->FrameCrossOriginToParentFrameChanged();
}
void LocalFrameView::VisibilityForThrottlingChanged() { void LocalFrameView::VisibilityForThrottlingChanged() {
if (FrameScheduler* frame_scheduler = frame_->GetFrameScheduler()) { if (FrameScheduler* frame_scheduler = frame_->GetFrameScheduler()) {
// TODO(szager): Per crbug.com/994443, maybe this should be: // TODO(szager): Per crbug.com/994443, maybe this should be:
......
...@@ -621,6 +621,7 @@ class CORE_EXPORT LocalFrameView final ...@@ -621,6 +621,7 @@ class CORE_EXPORT LocalFrameView final
void MapLocalToRemoteRootFrame(TransformState&); void MapLocalToRemoteRootFrame(TransformState&);
void CrossOriginToMainFrameChanged(); void CrossOriginToMainFrameChanged();
void CrossOriginToParentFrameChanged();
// The visual viewport can supply scrollbars. // The visual viewport can supply scrollbars.
void VisualViewportScrollbarsChanged(); void VisualViewportScrollbarsChanged();
......
...@@ -561,7 +561,7 @@ void HTMLFrameOwnerElement::ParseAttribute( ...@@ -561,7 +561,7 @@ void HTMLFrameOwnerElement::ParseAttribute(
} }
} }
void HTMLFrameOwnerElement::FrameCrossOriginStatusChanged() { void HTMLFrameOwnerElement::FrameCrossOriginToParentFrameChanged() {
if (base::FeatureList::IsEnabled( if (base::FeatureList::IsEnabled(
blink::features::kCompositeCrossOriginIframes)) { blink::features::kCompositeCrossOriginIframes)) {
SetNeedsCompositingUpdate(); SetNeedsCompositingUpdate();
......
...@@ -74,7 +74,7 @@ class CORE_EXPORT HTMLFrameOwnerElement : public HTMLElement, ...@@ -74,7 +74,7 @@ class CORE_EXPORT HTMLFrameOwnerElement : public HTMLElement,
return embedded_content_view_; return embedded_content_view_;
} }
void FrameCrossOriginStatusChanged(); void FrameCrossOriginToParentFrameChanged();
class PluginDisposeSuspendScope { class PluginDisposeSuspendScope {
STACK_ALLOCATED(); STACK_ALLOCATED();
......
...@@ -133,7 +133,7 @@ bool LayoutEmbeddedContent::RequiresAcceleratedCompositing() const { ...@@ -133,7 +133,7 @@ bool LayoutEmbeddedContent::RequiresAcceleratedCompositing() const {
return true; return true;
if (base::FeatureList::IsEnabled( if (base::FeatureList::IsEnabled(
blink::features::kCompositeCrossOriginIframes) && blink::features::kCompositeCrossOriginIframes) &&
content_frame->IsCrossOriginToMainFrame()) { content_frame->IsCrossOriginToParentFrame()) {
return true; return true;
} }
} }
......
...@@ -1507,8 +1507,8 @@ void DocumentLoader::InstallNewDocument( ...@@ -1507,8 +1507,8 @@ void DocumentLoader::InstallNewDocument(
previous_security_origin = frame_->GetDocument()->GetSecurityOrigin(); previous_security_origin = frame_->GetDocument()->GetSecurityOrigin();
} }
bool was_cross_origin_to_main_frame = bool was_cross_origin_to_parent_frame =
previous_security_origin && frame_->IsCrossOriginToMainFrame(); previous_security_origin && frame_->IsCrossOriginToParentFrame();
// In some rare cases, we'll re-use a LocalDOMWindow for a new Document. For // In some rare cases, we'll re-use a LocalDOMWindow for a new Document. For
// example, when a script calls window.open("..."), the browser gives // example, when a script calls window.open("..."), the browser gives
...@@ -1567,9 +1567,10 @@ void DocumentLoader::InstallNewDocument( ...@@ -1567,9 +1567,10 @@ void DocumentLoader::InstallNewDocument(
if (!loading_url_as_javascript_) if (!loading_url_as_javascript_)
DidCommitNavigation(); DidCommitNavigation();
if (was_cross_origin_to_main_frame != frame_->IsCrossOriginToMainFrame()) { if (was_cross_origin_to_parent_frame !=
frame_->IsCrossOriginToParentFrame()) {
if (auto* owner = frame_->DeprecatedLocalOwner()) if (auto* owner = frame_->DeprecatedLocalOwner())
owner->FrameCrossOriginStatusChanged(); owner->FrameCrossOriginToParentFrameChanged();
} }
if (initiator_origin) { if (initiator_origin) {
......
...@@ -1104,6 +1104,34 @@ TEST_P(CompositingSimTest, PromoteCrossOriginIframeAfterLoading) { ...@@ -1104,6 +1104,34 @@ TEST_P(CompositingSimTest, PromoteCrossOriginIframeAfterLoading) {
EXPECT_TRUE(CcLayerByDOMElementId("iframe")); EXPECT_TRUE(CcLayerByDOMElementId("iframe"));
} }
// An iframe that is cross-origin to the parent should be composited. This test
// sets up nested frames with domains A -> B -> A. Both the child and grandchild
// frames should be composited because they are cross-origin to their parent.
TEST_P(CompositingSimTest, PromoteCrossOriginToParent) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatureState(
blink::features::kCompositeCrossOriginIframes, true);
SimRequest main_resource("https://origin-a.com/a.html", "text/html");
SimRequest child_resource("https://origin-b.com/b.html", "text/html");
SimRequest grandchild_resource("https://origin-a.com/c.html", "text/html");
LoadURL("https://origin-a.com/a.html");
main_resource.Complete(R"HTML(
<!DOCTYPE html>
<iframe id="main_iframe" src="https://origin-b.com/b.html"></iframe>
)HTML");
child_resource.Complete(R"HTML(
<!DOCTYPE html>
<iframe id="child_iframe" src="https://origin-a.com/c.html"></iframe>
)HTML");
grandchild_resource.Complete("<!DOCTYPE html>");
Compositor().BeginFrame();
EXPECT_TRUE(CcLayerByDOMElementId("main_iframe"));
EXPECT_TRUE(CcLayerByDOMElementId("child_iframe"));
}
// Initially the iframe is cross-origin and should be composited. After changing // Initially the iframe is cross-origin and should be composited. After changing
// to same-origin, the frame should no longer be composited. // to same-origin, the frame should no longer be composited.
TEST_P(CompositingSimTest, PromoteCrossOriginIframeAfterDomainChange) { TEST_P(CompositingSimTest, PromoteCrossOriginIframeAfterDomainChange) {
...@@ -1137,4 +1165,51 @@ TEST_P(CompositingSimTest, PromoteCrossOriginIframeAfterDomainChange) { ...@@ -1137,4 +1165,51 @@ TEST_P(CompositingSimTest, PromoteCrossOriginIframeAfterDomainChange) {
EXPECT_FALSE(CcLayerByDOMElementId("iframe")); EXPECT_FALSE(CcLayerByDOMElementId("iframe"));
} }
// This test sets up nested frames with domains A -> B -> A. Initially, the
// child frame and grandchild frame should be composited. After changing the
// child frame to A (same-origin), both child and grandchild frames should no
// longer be composited.
TEST_P(CompositingSimTest, PromoteCrossOriginToParentIframeAfterDomainChange) {
base::test::ScopedFeatureList feature_list;
feature_list.InitWithFeatureState(
blink::features::kCompositeCrossOriginIframes, true);
SimRequest main_resource("https://origin-a.com/a.html", "text/html");
SimRequest child_resource("https://sub.origin-a.com/b.html", "text/html");
SimRequest grandchild_resource("https://origin-a.com/c.html", "text/html");
LoadURL("https://origin-a.com/a.html");
main_resource.Complete(R"HTML(
<!DOCTYPE html>
<iframe id="main_iframe" src="https://sub.origin-a.com/b.html"></iframe>
)HTML");
child_resource.Complete(R"HTML(
<!DOCTYPE html>
<iframe id="child_iframe" src="https://origin-a.com/c.html"></iframe>
)HTML");
grandchild_resource.Complete("<!DOCTYPE html>");
Compositor().BeginFrame();
EXPECT_TRUE(CcLayerByDOMElementId("main_iframe"));
EXPECT_TRUE(CcLayerByDOMElementId("child_iframe"));
auto* main_iframe_element =
To<HTMLIFrameElement>(GetDocument().getElementById("main_iframe"));
NonThrowableExceptionState exception_state;
GetDocument().setDomain(String("origin-a.com"), exception_state);
auto* child_iframe_element = To<HTMLIFrameElement>(
main_iframe_element->contentDocument()->getElementById("child_iframe"));
child_iframe_element->contentDocument()->setDomain(String("origin-a.com"),
exception_state);
main_iframe_element->contentDocument()->setDomain(String("origin-a.com"),
exception_state);
// We may not have scheduled a visual update so force an update instead of
// using BeginFrame.
UpdateAllLifecyclePhases();
EXPECT_FALSE(CcLayerByDOMElementId("main_iframe"));
EXPECT_FALSE(CcLayerByDOMElementId("child_iframe"));
}
} // namespace blink } // namespace blink
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment