Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tie BC creation to availability of a valid src + BC connectedness. #138

Merged
merged 10 commits into from
Jun 20, 2019
188 changes: 122 additions & 66 deletions index.bs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,13 @@ spec:url; type:dfn; text:scheme
element which embeds its rendered output and receives messages sent from the
portal browsing context.

<div class="note">
A <{portal}> element may only be a [=host element=] while it is
[=browsing-context connected=] or during the dispatch of the
{{Window/portalactivate!!event}} event from which it was obtained
using {{PortalActivateEvent/adoptPredecessor()}}.
</div>

The <dfn>host browsing context</dfn> of a [=portal browsing context=] is its
[=host element=]'s [=node document|document=]'s [=browsing context=].

Expand All @@ -116,6 +123,15 @@ spec:url; type:dfn; text:scheme

1. [=Assert=]: The [=portal state=] of |predecessorBrowsingContext| is "`none`".

1. Set the [=host element=] of |successorBrowsingContext| to null.

User agents *should*, however, attempt to preserve the rendering of the
guest browsing context until |predecessorBrowsingContext| has been replaced
with |portalBrowsingContext| in the rendering.

Note: This is intended to avoid a visual glitch, such as a "white flash", where
the guest browsing context briefly disappears.

1. Set the [=portal state=] of |predecessorBrowsingContext| to "`orphaned`".

1. Update the user interface to replace |predecessorBrowsingContext| with |successorBrowsingContext|
Expand Down Expand Up @@ -151,16 +167,40 @@ spec:url; type:dfn; text:scheme

1. [=Dispatch=] |event| to |successorWindow|.

1. If |event|'s [=PortalActivateEvent/predecessor browsing context=] is not null, then
[=queue a task=] from the [=portal task source=] to the
[=event loop=] associated with |predecessorBrowsingContext| to
resolve |promise| with undefined.
1. Let |adoptedPredecessorElement| be |event|'s [=PortalActivateEvent/adopted predecessor element=].

1. If |adoptedPredecessorElement| is not null, then:

1. Set |adoptedPredecessorElement|'s [=just-adopted flag=] to false.

1. If |element| [=may have a guest browsing context|may not have a guest browsing context=] and its [=guest browsing context=] is not null, then [=discard a browsing context|discard=] it.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The use of discard here vs. close below warrants a note, I think.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done; wdyt?


<div class="note">
This unceremoniously [=discard a browsing context|discards=]
the browsing context, as if the element had been removed from
the document after previously being attached. This is
distinct from the case where the predecessor was never
adopted, below, which [=close a browsing context|closes=] the
browsing context, which dispatches the
{{Window/unload!!event}} event, somewhat similarly to if it
had performed an ordinary navigation.

1. If |predecessorBrowsingContext| is not a [=portal browsing context=], [=close a browsing context|close=] it.
Typically authors would not call
{{PortalActivateEvent/adoptPredecessor()}} unless they intend
to insert it into the document before the [=just-adopted flag=]
becomes false.
</div>

The user agent *should not* ask the user for confirmation during the
[=prompt to unload=] step (and so the browsing context should be
[=discard a browsing context|discarded=]).
1. Otherwise:

1. [=Queue a task=] from the [=portal task source=] to the [=event loop=]
associated with |predecessorBrowsingContext| to resolve |promise| with undefined.

1. [=Close a browsing context|Close=] |predecessorBrowsingContext|.

The user agent *should not* ask the user for confirmation during the
[=prompt to unload=] step (and so the browsing context should be
[=discard a browsing context|discarded=]).
</section>

<wpt>
Expand All @@ -186,6 +226,8 @@ spec:url; type:dfn; text:scheme

1. Let |portalElement| be the result of [=creating an element=] given |document|, `portal`, and the [=HTML namespace=].

1. Set |portalElement|'s [=just-adopted flag=] to true.

1. [=Assert=]: |portalElement| is an {{HTMLPortalElement}}.

1. [=Queue a task=] from the [=portal task source=]
Expand Down Expand Up @@ -240,6 +282,10 @@ spec:url; type:dfn; text:scheme
browsing context</dfn>, which is the [=portal browsing context=] whose [=host
element=] is |portalElement|, or null if no such browsing context exists.

A <{portal}> element has a <dfn for="HTMLPortalElement">just-adopted
flag</dfn>, which is a [=boolean=] and is initially false. It is set during
dispatch of the {{Window/portalactivate!!event}} event.

The <dfn element-attr for="portal">src</dfn> attribute gives the [=URL=] of a
page that the [=guest browsing context=] is to contain. The attribute, if
present, must be a [=valid non-empty URL potentially surrounded by spaces=].
Expand Down Expand Up @@ -308,14 +354,6 @@ spec:url; type:dfn; text:scheme
|options|["{{PortalActivateOptions/transfer}}"]).
Rethrow any exceptions.

1. Set the [=guest browsing context=] of [=this=] to null.
User agents *should*, however, attempt to preserve the rendering of the
guest browsing context until |predecessorBrowsingContext| has been replaced
with |portalBrowsingContext| in the rendering.

Note: This is intended to avoid a visual glitch, such as a "white flash", where
the guest browsing context briefly disappears.

1. Let |promise| be a new [=promise=].

1. Run the steps to [=activate a portal browsing context|activate=] |portalBrowsingContext|
Expand Down Expand Up @@ -400,6 +438,28 @@ spec:url; type:dfn; text:scheme
</wpt>
</section>

<section algorithm="htmlportalelement-may-have-guest-browsing-context">
To determine whether a <{portal}> element <dfn for="HTMLPortalElement">may have a guest browsing context</dfn>, run the following steps:

1. If |element|'s [=node document|document=]'s [=browsing context=] is not a [=top-level browsing context=], then return false.

<wpt>
portals-nested.html
</wpt>

<p class="note">
The user agent may choose to emit a warning if the author attempts to
use a <{portal}> element in a [=nested browsing context=], as this is not
supported.
</p>

1. If |element| is [=browsing-context connected=], then return true.
Copy link
Collaborator

@lucasgadani lucasgadani Jun 14, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also add "and its browsing context is top-level"?

Maybe this will complicate the algorithm below, but it might be worth changing, as it expresses more clearly the situations where a portal may have a browsing context.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done; PTAL?


1. If |element|'s [=just-adopted flag=] is true, then return true.

1. Return false.
</section>

<section algorithm="htmlportalelement-close">
To <dfn for="HTMLPortalElement">close a <{portal}> element</dfn> |element|, run the following steps:

Expand All @@ -408,25 +468,39 @@ spec:url; type:dfn; text:scheme
The user agent *should not* ask the user for confirmation during the
[=prompt to unload=] step (and so the browsing context should be
[=discard a browsing context|discarded=]).

1. Set |element|'s [=guest browsing context=] to null.
</section>

<section algorithm="htmlportalelement-setsourceurl">
To <dfn for="HTMLPortalElement">set the source URL of a <{portal}> element</dfn> |element|, run the following steps:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"set the source URL" is feeling like a worse name for this algorithm now.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure what a better name is. It's principally used when the embedder directs the portal BC to navigate by changing the src attribute. It is also called on BC connection, mainly because we didn't navigate earlier when src was set.

"Possibly navigate" sounds a little vague. Any suggestions?


1. Let |guestBrowsingContext| be |element|'s [=guest browsing context=].
1. [=Assert=]: |element| [=may have a guest browsing context=].

1. [=Assert=]: |guestBrowsingContext| is not null.
1. Let |hostBrowsingContext| be |element|'s [=node document|document=]'s [=browsing context=].

1. [=Assert=]: |hostBrowsingContext| is a [=top-level browsing context=].

1. If |element| has no <{portal/src}> attribute specified, or its value is the empty string,
then [=close a portal element|close=] |element| and abort these steps.
then [=close a portal element|close=] |element| and return.

1. [=Parse a URL|Parse=] the value of the <{portal/src}> attribute. If that is not successful,
then [=close a portal element|close=] |element| and abort these steps.
then [=close a portal element|close=] |element| and return.

Otherwise, let |url| be the [=resulting URL record=].

1. If the [=scheme=] of |url| is not an [=HTTP(S) scheme=], then abort these steps.
1. If the [=scheme=] of |url| is not an [=HTTP(S) scheme=], then [=close a portal element|close=]
|element| and return.

1. If |element|'s [=guest browsing context=] is null, then run the following steps:

1. Let |newBrowsingContext| be the result of
[=create a new top-level browsing context|creating a new top-level browsing context=].

1. Set the [=portal state=] of |newBrowsingContext| to "`portal`", and set
the [=host element=] of |newBrowsingContext| to |element|.

1. Let |guestBrowsingContext| be |element|'s [=guest browsing context=].

1. [=Assert=]: |guestBrowsingContext| is not null.

1. Let |resource| be a new [=request=] whose [=request URL|URL=] is |url|
and whose [=request referrer policy|referrer policy=] is the current state of
Expand Down Expand Up @@ -457,48 +531,25 @@ spec:url; type:dfn; text:scheme
Whenever a <{portal}> element |element| has its <{portal/src}> attribute set,
changed, or removed, run the following steps:

1. If |element|'s [=guest browsing context=] is null, abort these steps.

1. [=set the source URL of a portal element|Set the source URL=] of |element|.
1. If |element| [=may have a guest browsing context=], then [=set the source URL of a portal element|set the source URL=] of |element|.

Whenever a <{portal}> element |element| [=becomes browsing-context connected=], run the following steps:

1. If |element|'s [=guest browsing context=] is null, abort these steps.

1. Let |hostBrowsingContext| be |element|'s [=node document|document=]'s [=browsing context=].

1. [=Assert=]: |hostBrowsingContext| is not null.

1. If |hostBrowsingContext| is a nested browsing context, then abort these steps.

<p class="note">
The user agent may choose to emit a warning if the author attempts to
use a <{portal}> element in a nested browsing context, as this is not
supported.
</p>
1. If |element| [=may have a guest browsing context|may not have a guest browsing context=], then abort these steps.

1. Let |newBrowsingContext| be the result of [=create a new top-level browsing context|creating a new top-level browsing context=].
1. If |element|'s [=guest browsing context=] is not null, then abort these steps.

1. Set the [=portal state=] of |newBrowsingContext| to "`portal`", and set
the [=host element=] of |newBrowsingContext| to |element|.

1. Set |element|'s [=guest browsing context=] to |newBrowsingContext|.
<div class="note">
This ensures that a newly [=adopt the predecessor browsing context|adopted=]
<{portal}> element can be inserted into the document without navigating
it.
</div>

1. [=set the source URL of a portal element|Set the source URL=] of |element|.

<wpt>
portals-nested.html
</wpt>

Whenever a <{portal}> element |element| [=becomes browsing-context disconnected=], run the following steps:

1. Let |guestBrowsingContext| be |element|'s [=guest browsing context=].

1. If |guestBrowsingContext| is null, then abort these steps.

1. [=discard a browsing context|Discard=] |guestBrowsingContext|.

1. Set |element|'s [=guest browsing context=] to null.
1. If |element| [=may have a guest browsing context|may not have a guest browsing context=] and its [=guest browsing context=] is not null, then [=discard a browsing context|discard=] it.

<div class="issue">
It might be convenient to not immediately detach the portal element, but instead to do so
Expand All @@ -514,16 +565,15 @@ spec:url; type:dfn; text:scheme

1. [=discard a browsing context|Discard=] |guestBrowsingContext|.

1. Set |element|'s [=guest browsing context=] to null.

<div class="note">
In particular, this means a <{portal}> element loses its [=guest browsing
context=] if it is moved to the [=active document=] of a [=nested browsing
context=].

Similarly, the steps when a <{portal}> element [=becomes browsing-context
connected=] prevent elements from creating a new [=guest browsing context=]
while inside such documents.
Similarly, the steps when a <{portal}> element's
[=set the source URL of a portal element|source URL is set=] prevent
elements from creating a new [=guest browsing context=] while inside such
documents.

It is therefore impossible to embed a [=portal browsing context=] in a
[=nested browsing context=].
Expand Down Expand Up @@ -701,14 +751,17 @@ spec:url; type:dfn; text:scheme

A {{PortalActivateEvent}} has an associated <dfn for="PortalActivateEvent">predecessor browsing context</dfn>,
which is a [=top-level browsing context=] or null, a <dfn for="PortalActivateEvent">successor window</dfn>, which is
a {{Window}}, and an <dfn for="PortalActivateEvent">activation promise</dfn>, which is a [=promise=].
a {{Window}}, an <dfn for="PortalActivateEvent">activation promise</dfn>, which is a [=promise=], and a
<dfn for="PortalActivateEvent">adopted predecessor element</dfn>, which is a <{portal}> element or null.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that this is an element doesn't appear to be used. A boolean would work just as well. Right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's used to clear the "just adopted" flag after portalactivate dispatch.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, my bad.


<section algorithm="portalactivateevent-event-constructing-steps">
The [=event constructing steps=] for {{PortalActivateEvent}}, given an |event|, are as follows:

1. Set |event|'s [=PortalActivateEvent/predecessor browsing context=] to null.

1. Set |event|'s [=PortalActivateEvent/successor window=] to null.

1. Set |event|'s [=PortalActivateEvent/adopted predecessor element=] to null.
</section>

<wpt>
Expand All @@ -718,15 +771,16 @@ spec:url; type:dfn; text:scheme
<section algorithm="portalactivateevent-adoptpredecessor">
The <dfn method for="PortalActivateEvent"><code>adoptPredecessor()</code></dfn> method *must* run these steps:

1. Let |predecessorBrowsingContext| be [=this=]'s [=PortalActivateEvent/predecessor browsing context=].
1. If [=this=]'s [=PortalActivateEvent/adopted predecessor element=] is not null, throw an "{{InvalidStateError}}" {{DOMException}}.

1. If |predecessorBrowsingContext| is null, throw an "{{InvalidStateError}}" {{DOMException}}.

1. Clear [=this=]'s [=PortalActivateEvent/predecessor browsing context=].
1. Let |predecessorBrowsingContext| be [=this=]'s [=PortalActivateEvent/predecessor browsing context=].

1. Let |successorWindow| be [=this=]'s [=PortalActivateEvent/successor window=].

1. Run the steps to [=adopt the predecessor browsing context=] |predecessorBrowsingContext| in |successorWindow|, and return the result.
1. Run the steps to [=adopt the predecessor browsing context=] |predecessorBrowsingContext| in |successorWindow|,
and let |adoptedPredecessorElement| be the result.

1. Set [=this=]'s [=PortalActivateEvent/adopted predecessor element=] to |adoptedPredecessorElement|.

1. [=Queue a task=] from the [=portal task source=] to the [=event loop=] associated with
|predecessorBrowsingContext| to resolve [=this=]'s [=activation promise=] with undefined.
Expand All @@ -736,6 +790,8 @@ spec:url; type:dfn; text:scheme
ordering issues between the task to resolve the activation promise and the task
to deliver the message.

1. Return |adoptedPredecessorElement|.

<wpt>
portals-adopt-predecessor.html
</wpt>
Expand Down