Commit 5eca7944 authored by Jeremy Roman's avatar Jeremy Roman Committed by Commit Bot

Portals: Refactor "can have guest" logic to more closely resemble the spec.

Specifically, this bit:
https://wicg.github.io/portals/#htmlportalelement-may-have-a-guest-browsing-context

And explicit tracking of the adopted predecessor element and just-adopted flag:
https://wicg.github.io/portals/#portalactivateevent-adopted-predecessor-element
https://wicg.github.io/portals/#htmlportalelement-just-adopted-flag

Some additional refactoring to follow.

Change-Id: If7ef036ed8ab6e95e7857d143cfeada41a9eb8fc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1760682Reviewed-by: default avatarLucas Gadani <lfg@chromium.org>
Commit-Queue: Jeremy Roman <jbroman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#688285}
parent 29144d27
......@@ -107,6 +107,7 @@ ScriptValue PortalActivateEvent::data(ScriptState* script_state) {
void PortalActivateEvent::Trace(blink::Visitor* visitor) {
Event::Trace(visitor);
visitor->Trace(document_);
visitor->Trace(adopted_portal_);
visitor->Trace(data_);
visitor->Trace(ports_);
visitor->Trace(data_from_init_);
......@@ -127,14 +128,27 @@ HTMLPortalElement* PortalActivateEvent::adoptPredecessor(
return nullptr;
}
HTMLPortalElement* portal = MakeGarbageCollected<HTMLPortalElement>(
DCHECK(!adopted_portal_);
adopted_portal_ = MakeGarbageCollected<HTMLPortalElement>(
*document_, predecessor_portal_token_, std::move(predecessor_portal_),
std::move(predecessor_portal_client_receiver_));
std::move(on_portal_activated_callback_).Run(true);
return portal;
return adopted_portal_;
}
void PortalActivateEvent::DetachPortalIfNotAdopted() {
void PortalActivateEvent::ExpireAdoptionLifetime() {
// End the special privilege associated with any adopted portals.
// This may destroy the guest contents.
if (adopted_portal_) {
adopted_portal_->ExpireAdoptionLifetime();
// We no longer need to hold the adopted portal, so stop drop the GC
// reference.
adopted_portal_ = nullptr;
}
// End the special privilege associated with the predecessor contents if it
// was not adopted. This may destroy the guest contents.
if (predecessor_portal_) {
std::move(on_portal_activated_callback_).Run(false);
predecessor_portal_.reset();
......
......@@ -72,10 +72,14 @@ class CORE_EXPORT PortalActivateEvent : public Event {
ScriptValue data(ScriptState*);
HTMLPortalElement* adoptPredecessor(ExceptionState& exception_state);
void DetachPortalIfNotAdopted();
// Invoked when this event should no longer keep its guest contents alive
// due to the portalactivate event.
void ExpireAdoptionLifetime();
private:
Member<Document> document_;
Member<HTMLPortalElement> adopted_portal_;
base::UnguessableToken predecessor_portal_token_;
mojo::AssociatedRemote<mojom::blink::Portal> predecessor_portal_;
mojo::PendingAssociatedReceiver<mojom::blink::PortalClient>
......
......@@ -2585,11 +2585,15 @@ void WebLocalFrameImpl::OnPortalActivated(
GetFrame()->DomWindow()->DispatchEvent(*event);
if (debugger)
debugger->ExternalAsyncTaskFinished(blink_data.sender_stack_trace_id);
event->DetachPortalIfNotAdopted();
event->ExpireAdoptionLifetime();
// After dispatching the portalactivate event, we check to see if we need to
// cleanup the portal hosting the predecessor. If the portal was created,
// but wasn't inserted or activated, we destroy it.
//
// TODO(jbroman): This should probably be done as part of
// ExpireAdoptionLifetime, now that the event knows about the adopted
// HTMLPortalElement explicitly.
HTMLPortalElement* portal_element =
DocumentPortals::From(*(GetFrame()->GetDocument()))
.GetPortal(portal_token);
......
......@@ -55,6 +55,12 @@ HTMLPortalElement::HTMLPortalElement(
remote_portal_(std::move(remote_portal)),
portal_client_receiver_(this, std::move(portal_client_receiver)) {
if (remote_portal_) {
was_just_adopted_ = true;
DCHECK(CanHaveGuestContents())
<< "<portal> element was created with an existing contents but is not "
"permitted to have one";
// If the portal element hosts a predecessor from activation, it can be
// activated before being inserted into the DOM, and we need to keep track
// of it from creation.
......@@ -78,6 +84,29 @@ void HTMLPortalElement::ConsumePortal() {
portal_client_receiver_.reset();
}
void HTMLPortalElement::ExpireAdoptionLifetime() {
was_just_adopted_ = false;
}
// https://wicg.github.io/portals/#htmlportalelement-may-have-a-guest-browsing-context
HTMLPortalElement::GuestContentsEligibility
HTMLPortalElement::GetGuestContentsEligibility() const {
// Non-HTML documents aren't eligible at all.
if (!GetDocument().IsHTMLDocument())
return GuestContentsEligibility::kIneligible;
LocalFrame* frame = GetDocument().GetFrame();
const bool is_connected = frame && isConnected();
if (!is_connected && !was_just_adopted_)
return GuestContentsEligibility::kIneligible;
const bool is_top_level = frame && frame->IsMainFrame();
if (!is_top_level)
return GuestContentsEligibility::kNotTopLevel;
return GuestContentsEligibility::kEligible;
}
void HTMLPortalElement::Navigate() {
KURL url = GetNonEmptyURLAttribute(html_names::kSrcAttr);
if (!remote_portal_ || url.IsEmpty())
......@@ -304,18 +333,20 @@ HTMLPortalElement::InsertionNotificationRequest HTMLPortalElement::InsertedInto(
ContainerNode& node) {
auto result = HTMLFrameOwnerElement::InsertedInto(node);
if (!node.IsInDocumentTree() || !GetDocument().IsHTMLDocument() ||
!GetDocument().GetFrame())
switch (GetGuestContentsEligibility()) {
case GuestContentsEligibility::kIneligible:
return result;
// We don't support embedding portals in nested browsing contexts.
if (!GetDocument().GetFrame()->IsMainFrame()) {
case GuestContentsEligibility::kNotTopLevel:
GetDocument().AddConsoleMessage(ConsoleMessage::Create(
mojom::ConsoleMessageSource::kRendering,
mojom::ConsoleMessageLevel::kWarning,
"Cannot use <portal> in a nested browsing context."));
return result;
}
case GuestContentsEligibility::kEligible:
break;
};
if (remote_portal_) {
// The interface is already bound if the HTMLPortalElement is adopting the
......
......@@ -79,7 +79,26 @@ class CORE_EXPORT HTMLPortalElement : public HTMLFrameOwnerElement,
// terminate the portal interface.
void ConsumePortal();
// Invoked when this element should no longer keep its guest contents alive
// due to recent adoption.
void ExpireAdoptionLifetime();
private:
enum class GuestContentsEligibility {
// Can have a guest contents.
kEligible,
// Would be eligible except that it is not top-level.
kNotTopLevel,
// Ineligible for additional reasons.
kIneligible,
};
GuestContentsEligibility GetGuestContentsEligibility() const;
bool CanHaveGuestContents() const {
return GetGuestContentsEligibility() == GuestContentsEligibility::kEligible;
}
// Navigates the portal to |url_|.
void Navigate();
......@@ -115,6 +134,9 @@ class CORE_EXPORT HTMLPortalElement : public HTMLFrameOwnerElement,
mojo::AssociatedRemote<mojom::blink::Portal> remote_portal_;
mojo::AssociatedReceiver<mojom::blink::PortalClient> portal_client_receiver_;
// Temporarily set to keep this element alive after adoption.
bool was_just_adopted_ = false;
};
} // 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