Commit 6b727cd1 authored by Jeremy Roman's avatar Jeremy Roman Committed by Commit Bot

Warn/throw if an HTMLPortalElement is used in a document where it does not belong.

This could happen if such an element is created in a document with
portals enabled and moved into a document with portals disabled.

Bug: 1040627
Change-Id: I8925a02d21c7d6a124def303d903d0b715836d93
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2013473
Auto-Submit: Jeremy Roman <jbroman@chromium.org>
Commit-Queue: Lucas Gadani <lfg@chromium.org>
Reviewed-by: default avatarLucas Gadani <lfg@chromium.org>
Cr-Commit-Position: refs/heads/master@{#734098}
parent 87e17b24
...@@ -1275,6 +1275,7 @@ jumbo_source_set("unit_tests") { ...@@ -1275,6 +1275,7 @@ jumbo_source_set("unit_tests") {
"html/parser/html_tree_builder_simulator_test.cc", "html/parser/html_tree_builder_simulator_test.cc",
"html/parser/html_view_source_parser_test.cc", "html/parser/html_view_source_parser_test.cc",
"html/parser/text_resource_decoder_test.cc", "html/parser/text_resource_decoder_test.cc",
"html/portal/html_portal_element_test.cc",
"html/shadow/progress_shadow_element_test.cc", "html/shadow/progress_shadow_element_test.cc",
"html/time_ranges_test.cc", "html/time_ranges_test.cc",
"html/track/text_track_list_test.cc", "html/track/text_track_list_test.cc",
......
...@@ -97,6 +97,34 @@ void HTMLPortalElement::PortalContentsWillBeDestroyed(PortalContents* portal) { ...@@ -97,6 +97,34 @@ void HTMLPortalElement::PortalContentsWillBeDestroyed(PortalContents* portal) {
portal_ = nullptr; portal_ = nullptr;
} }
bool HTMLPortalElement::CheckPortalsEnabledOrWarn() const {
Document& document = GetDocument();
if (RuntimeEnabledFeatures::PortalsEnabled(&document))
return true;
// TODO(jbroman): Consider linking to origin trial info if applicable.
document.AddConsoleMessage(ConsoleMessage::Create(
mojom::blink::ConsoleMessageSource::kRendering,
mojom::blink::ConsoleMessageLevel::kWarning,
"An operation was prevented because a <portal> was moved to a document "
"where it is not enabled."));
return false;
}
bool HTMLPortalElement::CheckPortalsEnabledOrThrow(
ExceptionState& exception_state) const {
Document& document = GetDocument();
if (RuntimeEnabledFeatures::PortalsEnabled(&document))
return true;
// TODO(jbroman): Consider linking to origin trial info if applicable.
exception_state.ThrowDOMException(
DOMExceptionCode::kNotSupportedError,
"An operation was prevented because a <portal> was moved to a document "
"where it is not enabled.");
return false;
}
// https://wicg.github.io/portals/#htmlportalelement-may-have-a-guest-browsing-context // https://wicg.github.io/portals/#htmlportalelement-may-have-a-guest-browsing-context
HTMLPortalElement::GuestContentsEligibility HTMLPortalElement::GuestContentsEligibility
HTMLPortalElement::GetGuestContentsEligibility() const { HTMLPortalElement::GetGuestContentsEligibility() const {
...@@ -120,6 +148,9 @@ HTMLPortalElement::GetGuestContentsEligibility() const { ...@@ -120,6 +148,9 @@ HTMLPortalElement::GetGuestContentsEligibility() const {
} }
void HTMLPortalElement::Navigate() { void HTMLPortalElement::Navigate() {
if (!CheckPortalsEnabledOrWarn())
return;
if (portal_) { if (portal_) {
portal_->Navigate(GetNonEmptyURLAttribute(html_names::kSrcAttr), portal_->Navigate(GetNonEmptyURLAttribute(html_names::kSrcAttr),
ReferrerPolicyAttribute()); ReferrerPolicyAttribute());
...@@ -186,6 +217,8 @@ BlinkTransferableMessage ActivateDataAsMessage( ...@@ -186,6 +217,8 @@ BlinkTransferableMessage ActivateDataAsMessage(
ScriptPromise HTMLPortalElement::activate(ScriptState* script_state, ScriptPromise HTMLPortalElement::activate(ScriptState* script_state,
PortalActivateOptions* options, PortalActivateOptions* options,
ExceptionState& exception_state) { ExceptionState& exception_state) {
if (!CheckPortalsEnabledOrThrow(exception_state))
return ScriptPromise();
if (!portal_) { if (!portal_) {
exception_state.ThrowDOMException( exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError, DOMExceptionCode::kInvalidStateError,
...@@ -231,6 +264,9 @@ void HTMLPortalElement::postMessage(ScriptState* script_state, ...@@ -231,6 +264,9 @@ void HTMLPortalElement::postMessage(ScriptState* script_state,
const ScriptValue& message, const ScriptValue& message,
const WindowPostMessageOptions* options, const WindowPostMessageOptions* options,
ExceptionState& exception_state) { ExceptionState& exception_state) {
if (!CheckPortalsEnabledOrThrow(exception_state))
return;
if (!portal_) { if (!portal_) {
exception_state.ThrowDOMException( exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError, DOMExceptionCode::kInvalidStateError,
...@@ -278,6 +314,9 @@ HTMLPortalElement::InsertionNotificationRequest HTMLPortalElement::InsertedInto( ...@@ -278,6 +314,9 @@ HTMLPortalElement::InsertionNotificationRequest HTMLPortalElement::InsertedInto(
ContainerNode& node) { ContainerNode& node) {
auto result = HTMLFrameOwnerElement::InsertedInto(node); auto result = HTMLFrameOwnerElement::InsertedInto(node);
if (!CheckPortalsEnabledOrWarn())
return result;
switch (GetGuestContentsEligibility()) { switch (GetGuestContentsEligibility()) {
case GuestContentsEligibility::kIneligible: case GuestContentsEligibility::kIneligible:
return result; return result;
......
...@@ -77,6 +77,12 @@ class CORE_EXPORT HTMLPortalElement : public HTMLFrameOwnerElement { ...@@ -77,6 +77,12 @@ class CORE_EXPORT HTMLPortalElement : public HTMLFrameOwnerElement {
void PortalContentsWillBeDestroyed(PortalContents*); void PortalContentsWillBeDestroyed(PortalContents*);
private: private:
// Checks whether the Portals feature is enabled for this document, and logs a
// warning to the developer if not. Doing basically anything with an
// HTMLPortalElement in a document which doesn't support portals is forbidden.
bool CheckPortalsEnabledOrWarn() const;
bool CheckPortalsEnabledOrThrow(ExceptionState&) const;
enum class GuestContentsEligibility { enum class GuestContentsEligibility {
// Can have a guest contents. // Can have a guest contents.
kEligible, kEligible,
......
// Copyright 2020 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 "third_party/blink/renderer/core/html/portal/html_portal_element.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/frame/window_post_message_options.h"
#include "third_party/blink/renderer/core/html/portal/portal_activate_options.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/inspector/console_message_storage.h"
#include "third_party/blink/renderer/core/testing/page_test_base.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/runtime_enabled_features.h"
#include "third_party/blink/renderer/platform/testing/runtime_enabled_features_test_helpers.h"
namespace blink {
namespace {
using HTMLPortalElementTest = PageTestBase;
// Virtually all operations should bail out before anything else if this
// HTMLPortalElement is not in a document where portals are enabled.
//
// For this test, we currently emulate this by just turning them off everywhere.
// :)
TEST_F(HTMLPortalElementTest, PortalsDisabledInDocument) {
Document& document = GetDocument();
auto* portal = MakeGarbageCollected<HTMLPortalElement>(document);
ScopedPortalsForTest disable_portals(false);
ASSERT_FALSE(RuntimeEnabledFeatures::PortalsEnabled(&document));
DummyExceptionStateForTesting exception_state;
ScriptState* script_state = ToScriptStateForMainWorld(&GetFrame());
const auto& console_messages = GetPage().GetConsoleMessageStorage();
portal->activate(script_state, MakeGarbageCollected<PortalActivateOptions>(),
exception_state);
EXPECT_TRUE(exception_state.HadException());
EXPECT_EQ(DOMExceptionCode::kNotSupportedError,
exception_state.CodeAs<DOMExceptionCode>());
exception_state.ClearException();
portal->postMessage(
script_state, ScriptValue::CreateNull(script_state->GetIsolate()),
MakeGarbageCollected<WindowPostMessageOptions>(), exception_state);
EXPECT_TRUE(exception_state.HadException());
EXPECT_EQ(DOMExceptionCode::kNotSupportedError,
exception_state.CodeAs<DOMExceptionCode>());
exception_state.ClearException();
auto next_console_message = console_messages.size();
GetDocument().body()->appendChild(portal, ASSERT_NO_EXCEPTION);
EXPECT_EQ(next_console_message + 1, console_messages.size());
EXPECT_TRUE(console_messages.at(next_console_message)
->Message()
.Contains("was moved to a document"));
next_console_message = console_messages.size();
portal->setAttribute(html_names::kSrcAttr, "http://example.com/",
ASSERT_NO_EXCEPTION);
EXPECT_EQ(next_console_message + 1, console_messages.size());
EXPECT_TRUE(console_messages.at(next_console_message)
->Message()
.Contains("was moved to a document"));
}
} // namespace
} // 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