Commit b658d53b authored by Keishi Hattori's avatar Keishi Hattori Committed by Commit Bot

Revert "Abstract portal activation handling to avoid hack of making a fake promise."

This reverts commit fdc228c7.

Reason for revert: portal-activate-default.html test timeout failure
https://ci.chromium.org/p/chromium/builders/ci/Mac10.10%20Tests/55803

Original change's description:
> Abstract portal activation handling to avoid hack of making a fake promise.
> 
> This is somewhat verbose, but allows us to avoid the unhandled rejection
> being visible to script without having to make this hack even worse.
> 
> Instead of actually calling activate(), this mirrors the spec structure
> by simply calling to underlying logic, passing a resolver where it makes
> sense and not (in this case, passing a substitute object) where it
> doesn't.
> 
> Bug: 1102081
> Change-Id: I0eecf253928bfce39afcddccf54e9bc6f6c0d549
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2285340
> Reviewed-by: Adithya Srinivasan <adithyas@chromium.org>
> Commit-Queue: Jeremy Roman <jbroman@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#786480}

TBR=jbroman@chromium.org,adithyas@chromium.org

Change-Id: I00da9eee8625ca5da3a09efb7078e22a277dbfd8
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Bug: 1102081
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2288712Reviewed-by: default avatarKeishi Hattori <keishi@chromium.org>
Commit-Queue: Keishi Hattori <keishi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#786596}
parent fceff3a2
...@@ -296,14 +296,6 @@ scoped_refptr<SerializedScriptValue> SerializedScriptValue::NullValue() { ...@@ -296,14 +296,6 @@ scoped_refptr<SerializedScriptValue> SerializedScriptValue::NullValue() {
return Create(reinterpret_cast<const char*>(kNullData), sizeof(kNullData)); return Create(reinterpret_cast<const char*>(kNullData), sizeof(kNullData));
} }
scoped_refptr<SerializedScriptValue> SerializedScriptValue::UndefinedValue() {
// The format here may fall a bit out of date, because we support
// deserializing SSVs written by old browser versions.
static const uint8_t kUndefinedData[] = {0xFF, 17, 0xFF, 13, '_', 0x00};
return Create(reinterpret_cast<const char*>(kUndefinedData),
sizeof(kUndefinedData));
}
String SerializedScriptValue::ToWireString() const { String SerializedScriptValue::ToWireString() const {
// Add the padding '\0', but don't put it in |data_buffer_|. // Add the padding '\0', but don't put it in |data_buffer_|.
// This requires direct use of uninitialized strings, though. // This requires direct use of uninitialized strings, though.
......
...@@ -162,7 +162,6 @@ class CORE_EXPORT SerializedScriptValue ...@@ -162,7 +162,6 @@ class CORE_EXPORT SerializedScriptValue
~SerializedScriptValue(); ~SerializedScriptValue();
static scoped_refptr<SerializedScriptValue> NullValue(); static scoped_refptr<SerializedScriptValue> NullValue();
static scoped_refptr<SerializedScriptValue> UndefinedValue();
String ToWireString() const; String ToWireString() const;
......
...@@ -517,8 +517,6 @@ blink_core_sources("html") { ...@@ -517,8 +517,6 @@ blink_core_sources("html") {
"portal/html_portal_element.h", "portal/html_portal_element.h",
"portal/portal_activate_event.cc", "portal/portal_activate_event.cc",
"portal/portal_activate_event.h", "portal/portal_activate_event.h",
"portal/portal_activation_delegate.cc",
"portal/portal_activation_delegate.h",
"portal/portal_contents.cc", "portal/portal_contents.cc",
"portal/portal_contents.h", "portal/portal_contents.h",
"portal/portal_host.cc", "portal/portal_host.cc",
......
...@@ -29,7 +29,6 @@ ...@@ -29,7 +29,6 @@
#include "third_party/blink/renderer/core/html/html_unknown_element.h" #include "third_party/blink/renderer/core/html/html_unknown_element.h"
#include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h" #include "third_party/blink/renderer/core/html/parser/html_parser_idioms.h"
#include "third_party/blink/renderer/core/html/portal/document_portals.h" #include "third_party/blink/renderer/core/html/portal/document_portals.h"
#include "third_party/blink/renderer/core/html/portal/portal_activation_delegate.h"
#include "third_party/blink/renderer/core/html/portal/portal_contents.h" #include "third_party/blink/renderer/core/html/portal/portal_contents.h"
#include "third_party/blink/renderer/core/html/portal/portal_post_message_helper.h" #include "third_party/blink/renderer/core/html/portal/portal_post_message_helper.h"
#include "third_party/blink/renderer/core/html_names.h" #include "third_party/blink/renderer/core/html_names.h"
...@@ -115,54 +114,6 @@ bool HTMLPortalElement::IsCurrentlyWithinFrameLimit() const { ...@@ -115,54 +114,6 @@ bool HTMLPortalElement::IsCurrentlyWithinFrameLimit() const {
return page->SubframeCount() < Page::MaxNumberOfFrames(); return page->SubframeCount() < Page::MaxNumberOfFrames();
} }
String HTMLPortalElement::PreActivateChecksCommon() {
if (!portal_)
return "The HTMLPortalElement is not associated with a portal context.";
if (DocumentPortals::From(GetDocument()).IsPortalInDocumentActivating())
return "Another portal in this document is activating.";
if (GetDocument().GetPage()->InsidePortal())
return "Cannot activate a portal that is inside another portal.";
if (GetDocument().BeforeUnloadStarted()) {
return "Cannot activate portal while document is in beforeunload or has "
"started unloading.";
}
return String();
}
void HTMLPortalElement::ActivateDefault() {
ExecutionContext* context = GetExecutionContext();
if (!CheckPortalsEnabledOrWarn() || !context)
return;
String pre_activate_error = PreActivateChecksCommon();
if (pre_activate_error) {
context->AddConsoleMessage(mojom::blink::ConsoleMessageSource::kRendering,
mojom::blink::ConsoleMessageLevel::kWarning,
pre_activate_error);
return;
}
// Quickly encode undefined without actually invoking script.
BlinkTransferableMessage data;
data.message = SerializedScriptValue::UndefinedValue();
data.message->UnregisterMemoryAllocatedWithCurrentScriptContext();
data.sender_origin =
GetExecutionContext()->GetSecurityOrigin()->IsolatedCopy();
if (ThreadDebugger* debugger =
ThreadDebugger::From(V8PerIsolateData::MainThreadIsolate())) {
data.sender_stack_trace_id =
debugger->StoreCurrentStackTrace("activate (implicit)");
}
PortalContents* portal = std::exchange(portal_, nullptr);
portal->Activate(std::move(data),
PortalActivationDelegate::ForConsole(context));
}
bool HTMLPortalElement::CheckWithinFrameLimitOrWarn() const { bool HTMLPortalElement::CheckWithinFrameLimitOrWarn() const {
if (IsCurrentlyWithinFrameLimit()) if (IsCurrentlyWithinFrameLimit())
return true; return true;
...@@ -310,11 +261,30 @@ ScriptPromise HTMLPortalElement::activate(ScriptState* script_state, ...@@ -310,11 +261,30 @@ ScriptPromise HTMLPortalElement::activate(ScriptState* script_state,
ExceptionState& exception_state) { ExceptionState& exception_state) {
if (!CheckPortalsEnabledOrThrow(exception_state)) if (!CheckPortalsEnabledOrThrow(exception_state))
return ScriptPromise(); return ScriptPromise();
if (!portal_) {
String pre_activate_error = PreActivateChecksCommon(); exception_state.ThrowDOMException(
if (pre_activate_error) { DOMExceptionCode::kInvalidStateError,
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError, "The HTMLPortalElement is not associated with a portal context.");
pre_activate_error); return ScriptPromise();
}
if (DocumentPortals::From(GetDocument()).IsPortalInDocumentActivating()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"activate() has already been called on another "
"HTMLPortalElement in this document.");
return ScriptPromise();
}
if (GetDocument().GetPage()->InsidePortal()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"Cannot activate a portal that is inside another portal.");
return ScriptPromise();
}
if (GetDocument().BeforeUnloadStarted()) {
exception_state.ThrowDOMException(
DOMExceptionCode::kInvalidStateError,
"Cannot activate portal while document is in beforeunload or has "
"started unloading.");
return ScriptPromise(); return ScriptPromise();
} }
...@@ -324,12 +294,7 @@ ScriptPromise HTMLPortalElement::activate(ScriptState* script_state, ...@@ -324,12 +294,7 @@ ScriptPromise HTMLPortalElement::activate(ScriptState* script_state,
return ScriptPromise(); return ScriptPromise();
PortalContents* portal = std::exchange(portal_, nullptr); PortalContents* portal = std::exchange(portal_, nullptr);
ScriptPromiseResolver* resolver = return portal->Activate(script_state, std::move(data));
MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
portal->Activate(std::move(data), PortalActivationDelegate::ForPromise(
resolver, exception_state));
return promise;
} }
void HTMLPortalElement::postMessage(ScriptState* script_state, void HTMLPortalElement::postMessage(ScriptState* script_state,
...@@ -481,9 +446,27 @@ void HTMLPortalElement::DefaultEventHandler(Event& event) { ...@@ -481,9 +446,27 @@ void HTMLPortalElement::DefaultEventHandler(Event& event) {
// Support the new behavior whereby clicking (or equivalent operations via // Support the new behavior whereby clicking (or equivalent operations via
// keyboard and other input modalities) a portal element causes it to activate // keyboard and other input modalities) a portal element causes it to activate
// unless prevented. // unless prevented.
//
// TODO(jbroman): Ideally we wouldn't need to bring up a script state context
// to do it, but presently it's how errors are reported and also required for
// serialization of the (absent) activation data.
if (RuntimeEnabledFeatures::PortalsDefaultActivationEnabled() && if (RuntimeEnabledFeatures::PortalsDefaultActivationEnabled() &&
event.type() == event_type_names::kDOMActivate) { event.type() == event_type_names::kDOMActivate) {
ActivateDefault(); if (LocalFrame* frame = GetDocument().GetFrame()) {
ScriptState* script_state = ToScriptStateForMainWorld(frame);
ScriptState::Scope scope(script_state);
ExceptionState exception_state(script_state->GetIsolate(),
ExceptionState::kUnknownContext, nullptr,
nullptr);
activate(ToScriptStateForMainWorld(frame),
MakeGarbageCollected<PortalActivateOptions>(), exception_state);
if (exception_state.HadException()) {
// The exception will be reported as an unhandled promise. This is
// slightly weird, but gets the point across.
ScriptPromise::Reject(script_state, exception_state);
exception_state.ClearException();
}
}
event.SetDefaultHandled(); event.SetDefaultHandled();
} }
......
...@@ -81,13 +81,6 @@ class CORE_EXPORT HTMLPortalElement : public HTMLFrameOwnerElement { ...@@ -81,13 +81,6 @@ class CORE_EXPORT HTMLPortalElement : public HTMLFrameOwnerElement {
void PortalContentsWillBeDestroyed(PortalContents*); void PortalContentsWillBeDestroyed(PortalContents*);
private: private:
// Returns a null string if the checks passed, and a suitable error otherwise.
String PreActivateChecksCommon();
// Performs a default activation (e.g. due to an unprevented click), as
// opposed to one requested by invoking HTMLPortalElement::activate.
void ActivateDefault();
// Checks whether the Portals feature is enabled for this document, and logs a // Checks whether the Portals feature is enabled for this document, and logs a
// warning to the developer if not. Doing basically anything with an // warning to the developer if not. Doing basically anything with an
// HTMLPortalElement in a document which doesn't support portals is forbidden. // HTMLPortalElement in a document which doesn't support portals is forbidden.
......
// 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/portal_activation_delegate.h"
#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
#include "third_party/blink/public/mojom/portal/portal.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/loader/fetch/console_logger.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
namespace {
class PromiseActivationDelegate
: public GarbageCollected<PromiseActivationDelegate>,
public PortalActivationDelegate {
USING_GARBAGE_COLLECTED_MIXIN(PromiseActivationDelegate);
public:
PromiseActivationDelegate(ScriptPromiseResolver* resolver,
const ExceptionState& exception_state)
: resolver_(resolver),
context_(exception_state.Context()),
interface_name_(exception_state.InterfaceName()),
property_name_(exception_state.PropertyName()) {}
void ActivationDidSucceed() final { resolver_->Resolve(); }
void ActivationDidFail(const String& message) final {
ScriptState* script_state = resolver_->GetScriptState();
ScriptState::Scope scope(script_state);
ExceptionState exception_state(script_state->GetIsolate(), context_,
interface_name_, property_name_);
exception_state.ThrowDOMException(DOMExceptionCode::kInvalidStateError,
message);
resolver_->Reject(exception_state);
}
void ActivationWasAbandoned() final { resolver_->Detach(); }
void Trace(Visitor* visitor) const final { visitor->Trace(resolver_); }
private:
Member<ScriptPromiseResolver> resolver_;
// Needed to reconstruct ExceptionState. Ideally these would be bundled.
// See https://crbug.com/991544.
ExceptionState::ContextType context_;
const char* interface_name_;
const char* property_name_;
};
} // namespace
PortalActivationDelegate* PortalActivationDelegate::ForPromise(
ScriptPromiseResolver* resolver,
const ExceptionState& exception_state) {
return MakeGarbageCollected<PromiseActivationDelegate>(resolver,
exception_state);
}
namespace {
class ConsoleActivationDelegate
: public GarbageCollected<ConsoleActivationDelegate>,
public PortalActivationDelegate {
USING_GARBAGE_COLLECTED_MIXIN(ConsoleActivationDelegate);
public:
explicit ConsoleActivationDelegate(ConsoleLogger* logger) : logger_(logger) {}
void ActivationDidSucceed() final {}
void ActivationDidFail(const String& message) final {
logger_->AddConsoleMessage(mojom::blink::ConsoleMessageSource::kRendering,
mojom::blink::ConsoleMessageLevel::kWarning,
message);
}
void ActivationWasAbandoned() final {}
void Trace(Visitor* visitor) const final { visitor->Trace(logger_); }
private:
Member<ConsoleLogger> logger_;
};
} // namespace
PortalActivationDelegate* PortalActivationDelegate::ForConsole(
ConsoleLogger* logger) {
return MakeGarbageCollected<ConsoleActivationDelegate>(logger);
}
} // namespace blink
// 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PORTAL_PORTAL_ACTIVATION_DELEGATE_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PORTAL_PORTAL_ACTIVATION_DELEGATE_H_
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
namespace WTF {
class String;
} // namespace WTF
namespace blink {
class ConsoleLogger;
class ExceptionState;
class ScriptPromiseResolver;
// Handles the result of portal activation, reporting it in a suitable way.
class PortalActivationDelegate : public GarbageCollectedMixin {
public:
// Creates a delegate which reports completion through promise resolution.
// Copies the metadata from the supplied ExceptionState.
static PortalActivationDelegate* ForPromise(ScriptPromiseResolver*,
const ExceptionState&);
// Creates a delegate which logs errors to the console.
static PortalActivationDelegate* ForConsole(ConsoleLogger*);
// Called when activation is complete.
virtual void ActivationDidSucceed() = 0;
// Called when activation fails, with a suggested message.
virtual void ActivationDidFail(const WTF::String& message) = 0;
// Called when activation is abandoned without completion, e.g. due to a
// corrupt state or disconnection.
virtual void ActivationWasAbandoned() = 0;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_HTML_PORTAL_PORTAL_ACTIVATION_DELEGATE_H_
...@@ -8,12 +8,12 @@ ...@@ -8,12 +8,12 @@
#include "base/time/time.h" #include "base/time/time.h"
#include "third_party/blink/public/mojom/portal/portal.mojom-blink-forward.h" #include "third_party/blink/public/mojom/portal/portal.mojom-blink-forward.h"
#include "third_party/blink/public/mojom/referrer.mojom-blink.h" #include "third_party/blink/public/mojom/referrer.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/core/dom/increment_load_event_delay_count.h" #include "third_party/blink/renderer/core/dom/increment_load_event_delay_count.h"
#include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h" #include "third_party/blink/renderer/core/execution_context/execution_context_lifecycle_observer.h"
#include "third_party/blink/renderer/core/frame/remote_frame.h" #include "third_party/blink/renderer/core/frame/remote_frame.h"
#include "third_party/blink/renderer/core/html/portal/document_portals.h" #include "third_party/blink/renderer/core/html/portal/document_portals.h"
#include "third_party/blink/renderer/core/html/portal/html_portal_element.h" #include "third_party/blink/renderer/core/html/portal/html_portal_element.h"
#include "third_party/blink/renderer/core/html/portal/portal_activation_delegate.h"
#include "third_party/blink/renderer/core/html/portal/portal_post_message_helper.h" #include "third_party/blink/renderer/core/html/portal/portal_post_message_helper.h"
#include "third_party/blink/renderer/core/inspector/console_message.h" #include "third_party/blink/renderer/core/inspector/console_message.h"
#include "third_party/blink/renderer/core/loader/document_load_timing.h" #include "third_party/blink/renderer/core/loader/document_load_timing.h"
...@@ -40,7 +40,7 @@ PortalContents::PortalContents( ...@@ -40,7 +40,7 @@ PortalContents::PortalContents(
DocumentPortals::From(GetDocument()).RegisterPortalContents(this); DocumentPortals::From(GetDocument()).RegisterPortalContents(this);
} }
PortalContents::~PortalContents() = default; PortalContents::~PortalContents() {}
RemoteFrame* PortalContents::GetFrame() const { RemoteFrame* PortalContents::GetFrame() const {
if (portal_element_) if (portal_element_)
...@@ -48,15 +48,16 @@ RemoteFrame* PortalContents::GetFrame() const { ...@@ -48,15 +48,16 @@ RemoteFrame* PortalContents::GetFrame() const {
return nullptr; return nullptr;
} }
void PortalContents::Activate(BlinkTransferableMessage data, ScriptPromise PortalContents::Activate(ScriptState* script_state,
PortalActivationDelegate* delegate) { BlinkTransferableMessage data) {
DCHECK(!IsActivating()); DCHECK(!IsActivating());
DCHECK(portal_element_); DCHECK(portal_element_);
// Mark this contents as having activation in progress. // Mark this contents as having activation in progress.
DocumentPortals& document_portals = DocumentPortals::From(GetDocument()); DocumentPortals& document_portals = DocumentPortals::From(GetDocument());
document_portals.SetActivatingPortalContents(this); document_portals.SetActivatingPortalContents(this);
activation_delegate_ = delegate; activate_resolver_ =
MakeGarbageCollected<ScriptPromiseResolver>(script_state);
// Request activation from the browser process. // Request activation from the browser process.
// This object (and thus the Mojo connection it owns) remains alive while the // This object (and thus the Mojo connection it owns) remains alive while the
...@@ -67,13 +68,27 @@ void PortalContents::Activate(BlinkTransferableMessage data, ...@@ -67,13 +68,27 @@ void PortalContents::Activate(BlinkTransferableMessage data,
// Dissociate from the element. The element is expected to do the same. // Dissociate from the element. The element is expected to do the same.
portal_element_ = nullptr; portal_element_ = nullptr;
return activate_resolver_->Promise();
} }
void PortalContents::OnActivateResponse( void PortalContents::OnActivateResponse(
mojom::blink::PortalActivateResult result) { mojom::blink::PortalActivateResult result) {
auto reject = [&](const char* message) { auto reject = [&](DOMExceptionCode code, const char* message) {
if (GetDocument().GetExecutionContext()) if (!GetDocument().GetExecutionContext())
activation_delegate_->ActivationDidFail(message); return;
ScriptState* script_state = activate_resolver_->GetScriptState();
ScriptState::Scope scope(script_state);
// TODO(jbroman): It's slightly unfortunate to hard-code the string
// HTMLPortalElement here. Ideally this would be threaded through from
// there and carried with the ScriptPromiseResolver. See
// https://crbug.com/991544.
ExceptionState exception_state(script_state->GetIsolate(),
ExceptionState::kExecutionContext,
"HTMLPortalElement", "activate");
exception_state.ThrowDOMException(code, message);
activate_resolver_->Reject(exception_state);
}; };
bool should_destroy_contents = false; bool should_destroy_contents = false;
...@@ -83,29 +98,32 @@ void PortalContents::OnActivateResponse( ...@@ -83,29 +98,32 @@ void PortalContents::OnActivateResponse(
page->SetInsidePortal(true); page->SetInsidePortal(true);
FALLTHROUGH; FALLTHROUGH;
case mojom::blink::PortalActivateResult::kPredecessorWillUnload: case mojom::blink::PortalActivateResult::kPredecessorWillUnload:
activation_delegate_->ActivationDidSucceed(); activate_resolver_->Resolve();
should_destroy_contents = true; should_destroy_contents = true;
break; break;
case mojom::blink::PortalActivateResult:: case mojom::blink::PortalActivateResult::
kRejectedDueToPredecessorNavigation: kRejectedDueToPredecessorNavigation:
reject("A top-level navigation is in progress."); reject(DOMExceptionCode::kInvalidStateError,
"A top-level navigation is in progress.");
break; break;
case mojom::blink::PortalActivateResult::kRejectedDueToPortalNotReady: case mojom::blink::PortalActivateResult::kRejectedDueToPortalNotReady:
reject("The portal was not yet ready or was blocked."); reject(DOMExceptionCode::kInvalidStateError,
"The portal was not yet ready or was blocked.");
break; break;
case mojom::blink::PortalActivateResult::kRejectedDueToErrorInPortal: case mojom::blink::PortalActivateResult::kRejectedDueToErrorInPortal:
reject("The portal is in an error state."); reject(DOMExceptionCode::kInvalidStateError,
"The portal is in an error state.");
break; break;
case mojom::blink::PortalActivateResult::kDisconnected: case mojom::blink::PortalActivateResult::kDisconnected:
// Only called when |remote_portal_| is disconnected. This usually happens // Only called when |remote_portal_| is disconnected. This usually happens
// when the browser/test runner is being shut down. // when the browser/test runner is being shut down.
activation_delegate_->ActivationWasAbandoned(); activate_resolver_->Detach();
break; break;
case mojom::blink::PortalActivateResult::kAbortedDueToBug: case mojom::blink::PortalActivateResult::kAbortedDueToBug:
// This should never happen. Ignore this and wait for the frame to be // This should never happen. Ignore this and wait for the frame to be
// discarded by the browser, if it hasn't already. // discarded by the browser, if it hasn't already.
activation_delegate_->ActivationWasAbandoned(); activate_resolver_->Detach();
return; return;
} }
...@@ -113,7 +131,7 @@ void PortalContents::OnActivateResponse( ...@@ -113,7 +131,7 @@ void PortalContents::OnActivateResponse(
DCHECK_EQ(document_portals.GetActivatingPortalContents(), this); DCHECK_EQ(document_portals.GetActivatingPortalContents(), this);
document_portals.ClearActivatingPortalContents(); document_portals.ClearActivatingPortalContents();
activation_delegate_ = nullptr; activate_resolver_ = nullptr;
if (should_destroy_contents) if (should_destroy_contents)
Destroy(); Destroy();
...@@ -203,7 +221,7 @@ void PortalContents::DispatchLoadEvent() { ...@@ -203,7 +221,7 @@ void PortalContents::DispatchLoadEvent() {
void PortalContents::Trace(Visitor* visitor) const { void PortalContents::Trace(Visitor* visitor) const {
visitor->Trace(document_); visitor->Trace(document_);
visitor->Trace(portal_element_); visitor->Trace(portal_element_);
visitor->Trace(activation_delegate_); visitor->Trace(activate_resolver_);
} }
} // namespace blink } // namespace blink
...@@ -20,8 +20,10 @@ namespace blink { ...@@ -20,8 +20,10 @@ namespace blink {
class Document; class Document;
class HTMLPortalElement; class HTMLPortalElement;
class KURL; class KURL;
class PortalActivationDelegate;
class RemoteFrame; class RemoteFrame;
class ScriptPromise;
class ScriptPromiseResolver;
class ScriptState;
class SecurityOrigin; class SecurityOrigin;
struct BlinkTransferableMessage; struct BlinkTransferableMessage;
...@@ -48,7 +50,7 @@ class PortalContents : public GarbageCollected<PortalContents>, ...@@ -48,7 +50,7 @@ class PortalContents : public GarbageCollected<PortalContents>,
bool IsValid() const { return remote_portal_.is_bound(); } bool IsValid() const { return remote_portal_.is_bound(); }
// Returns true if this contents is currently being activated. // Returns true if this contents is currently being activated.
bool IsActivating() const { return activation_delegate_; } bool IsActivating() const { return activate_resolver_; }
// Returns an unguessable token which uniquely identifies the contents, if // Returns an unguessable token which uniquely identifies the contents, if
// valid. // valid.
...@@ -60,7 +62,7 @@ class PortalContents : public GarbageCollected<PortalContents>, ...@@ -60,7 +62,7 @@ class PortalContents : public GarbageCollected<PortalContents>,
// Activates the portal contents, and produces a promise which resolves when // Activates the portal contents, and produces a promise which resolves when
// complete. The caller is expected to do all necessary preflight checks in // complete. The caller is expected to do all necessary preflight checks in
// advance. // advance.
void Activate(BlinkTransferableMessage data, PortalActivationDelegate*); ScriptPromise Activate(ScriptState*, BlinkTransferableMessage data);
// Posts a message which will be delivered in the guest contents via the // Posts a message which will be delivered in the guest contents via the
// PortalHost object. // PortalHost object.
...@@ -108,7 +110,7 @@ class PortalContents : public GarbageCollected<PortalContents>, ...@@ -108,7 +110,7 @@ class PortalContents : public GarbageCollected<PortalContents>,
// Set if the portal contents is currently being activated. // Set if the portal contents is currently being activated.
// If so it will be the activating portal contents of the associated // If so it will be the activating portal contents of the associated
// DocumentPortals. // DocumentPortals.
Member<PortalActivationDelegate> activation_delegate_; Member<ScriptPromiseResolver> activate_resolver_;
// Uniquely identifies the portal, this token is used by the browser process // Uniquely identifies the portal, this token is used by the browser process
// to reference this portal when communicating with the renderer. // to reference this portal when communicating with the renderer.
......
...@@ -14,13 +14,11 @@ promise_test(async t => { ...@@ -14,13 +14,11 @@ promise_test(async t => {
await new Promise(resolve => portal.onload = resolve); await new Promise(resolve => portal.onload = resolve);
let activated = new Promise(resolve => bc.onmessage = e => resolve(e.data)); let activated = new Promise(resolve => bc.onmessage = e => resolve(e.data));
portal.click(); portal.click();
let {event, data} = await activated; assert_equals(await activated, 'portalactivate');
assert_equals(event, 'portalactivate');
assert_equals(data, undefined);
} finally { } finally {
w.close(); w.close();
} }
}, "Clicking should activate with undefined data."); }, "Clicking should activate.");
promise_test(async t => { promise_test(async t => {
assert_implements("HTMLPortalElement" in self); assert_implements("HTMLPortalElement" in self);
...@@ -32,26 +30,13 @@ promise_test(async t => { ...@@ -32,26 +30,13 @@ promise_test(async t => {
portal.onclick = e => e.preventDefault(); portal.onclick = e => e.preventDefault();
w.document.body.appendChild(portal); w.document.body.appendChild(portal);
await new Promise(resolve => portal.onload = resolve); await new Promise(resolve => portal.onload = resolve);
bc.onmessage = t.unreached_func('activation should not occur'); let timedOut = new Promise(resolve => t.step_timeout(() => resolve('timeout'), 3000));
let activated = new Promise(resolve => bc.onmessage = e => resolve(e.data));
portal.click(); portal.click();
await new Promise(resolve => t.step_timeout(resolve, 3000)); let result = await Promise.race([activated, timedOut]);
assert_equals(result, 'timeout');
} finally { } finally {
w.close(); w.close();
} }
}, "Clicking shouldn't activate if prevented."); }, "Clicking shouldn't activate if prevented.");
// Script didn't create the promise so it shouldn't observe one.
// This forecloses a naive implementation of this behavior that simply calls the WebIDL operation.
promise_test(async t => {
assert_implements("HTMLPortalElement" in self);
const w = await openBlankPortalHost();
try {
const portal = w.document.createElement('portal');
w.onunhandledrejection = t.unreached_func('unhandledrejection event should not fire');
portal.click();
await new Promise(resolve => t.step_timeout(resolve, 3000));
} finally {
w.close();
}
}, "Failed activation should not surface as an unhandled promise rejection.");
</script> </script>
<!DOCTYPE html> <!DOCTYPE html>
<script> <script>
onportalactivate = e => { onportalactivate = () => {
let bc = new BroadcastChannel(new URL(location).searchParams.get('bc')); let bc = new BroadcastChannel(new URL(location).searchParams.get('bc'));
bc.postMessage({event: 'portalactivate', data: e.data}); bc.postMessage('portalactivate');
bc.close(); bc.close();
}; };
</script> </script>
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