diff --git a/index.html b/index.html index 26cf484..d5b0686 100644 --- a/index.html +++ b/index.html @@ -24,6 +24,7 @@ "file-system-access", "fs", "mimesniff", + "urlpattern", "web-app-launch", "window-controls-overlay" ], @@ -92,14 +93,20 @@

mode/window-controls-overlay=] and the user agent supports this, then return that |candidate_display_mode:DisplayModeType|. +
  • If |candidate_display_mode:DisplayModeType| is [=display + mode/tabbed=] and the user agent supports this, then return that + |candidate_display_mode:DisplayModeType|. +
  • This member is intended to be only used for advanced cases, where the developer wants explicit control over the fallback order of their - display modes. Otherwise, the [=manifest/display=] member is sufficient - for most use cases. + display modes, or for modes that are not available in the basic + display modes + list. Otherwise, the [=manifest/display=] member is sufficient for + most use cases.

    @@ -139,6 +146,15 @@

    [=display mode/window-controls-overlay=]
    +
    + tabbed +
    +
    + The web application can have multiple [=application contexts=] + combined in a single operating-system-level window. For example, + this could mean the user agent displays a tab strip UI to allow the + user to switch between the application contexts. +

    @@ -207,6 +223,9 @@

    |json:ordered map|, and [=ordered map=] |manifest:ordered map|):

      +
    1. [=Process the `tab_strip` member=], passing |json|, |manifest| and + |manifest URL|. +
    2. [=Process the `note_taking` member=], passing |json|, |manifest| and |manifest URL|.
    3. @@ -218,6 +237,442 @@

    +
    +

    + tab_strip member +

    +

    + The `tab_strip` member of the Web Application Manifest is + an object that contains + information about how the application is intended to behave in the + [=display mode/tabbed=] display mode. It has the following members: +

    +
      +
    • [=tab_strip/home_tab=] +
    • +
    • [=tab_strip/new_tab_button=] +
    • +
    +
    +

    + home_tab member +

    +

    + The `home_tab` member of the [=tab_strip=] object is an ordered map + that contains information about a special "home tab" that is intended + to serve as the top-level menu for the application. It contains the + following members: +

    +
      +
    • [=home_tab/scope_patterns=] +
    • +
    +

    + The scope_patterns member is a list of + {{URLPatternInput}}s that define the [=within home tab scope|scope of + the home tab=] relative to the [=manifest URL=]. +

    + +

    + An application has a home tab if the applied [=display + mode=] of the application is [=display mode/tabbed=], and the + [=Document/processed manifest=] includes a non-null [=home_tab=] + member of the [=tab_strip=] member. +

    +

    + The home tab context is an optional [=application + context=] that has special properties compared to other application + contexts. If the application [=has a home tab=], every application + window SHOULD feature a [=home tab context=]. If not, then the + application windows SHOULD NOT have a [=home tab context=]. +

    +

    + How the [=home tab context=] is presented is at the discretion of the + user agent, but it SHOULD have a different appearance to normal + application contexts. +

    +

    + A [=URL=] |url:URL| is said to be within home tab scope if + and only if: +

    +
      +
    • the application [=has a home tab=], and +
    • +
    • |url| is [=manifest/within scope=] of the manifest, and +
    • +
    • at least one of the following: +
        +
      • |url| [=URL/equals=] the [=start URL=], with + [=URL/equals/exclude fragments=] set to true, or +
      • +
      • Applying [=URL pattern/match=] given any element of the + [=scope_patterns=] member of the [=home_tab=] member of the + [=tab_strip=] member of the [=Document/processed manifest=] and + |url| returns a {{URLPatternResult}}. +
      • +
      +
    • +
    +

    + A URL is is outside of home tab scope if it is not + [=within home tab scope=]. +

    + +
    +

    + Every window has a home tab +

    +

    + If the application [=has a home tab=], whenever a new application + window is created (for example when launching the application, or + when moving a tab to a new window), the user agent MUST create a + new [=home tab context=] in that window. A newly created [=home tab + context=] SHOULD be navigated to the [=start URL=], which by + definition is [=within home tab scope=]. +

    +
    +
    +

    + Navigations concerning the home tab scope +

    +

    + When [=navigate|navigating=] the [=top-level traversable=] + associated with a [=home tab context=] to a [=URL=] |url:URL| that + is [=outside of home tab scope=], the following steps are run: +

    +
      +
    1. Let [=top-level traversable=] |tab:toplevel traversable| be the + result of choosing a navigable with a target of + "_blank" and noopener true. +
    2. +
    3. Instead of [=navigating=] the home-tab traversable, + [=navigate=] |tab| with the same parameters. +
    4. +
    5. [=applied|Apply=] the current [=application manifest=] to + |tab|'s [=top-level browsing context=]. +
    6. +
    7. The user agent SHOULD place |tab| in the same window as the + home-tab navigable. +
    8. +
    9. Focus |tab|. +
    10. +
    +

    +

    + When [=navigate|navigating=] a [=top-level traversable=] with a + [=display mode=] of [=display mode/tabbed=] that is not associated + with a [=home tab context=] (i.e. a non-home tab) to a [=URL=] + |url:URL| that is [=within home tab scope=], the following steps + are run: +

    +
      +
    1. Let |hometab:toplevel traversable| be the [=top-level + traversable=] of the [=home tab context=] associated with the + current window. +
    2. +
    3. Instead of [=navigating=] the top-level traversable, + [=navigate=] |hometab| with the same parameters. +
    4. +
    5. Focus |hometab|. +
    6. +
    + +
    +
    +

    + Home tab invariants +

    +

    + The above rules are intended to ensure that the following + invariants are always true, for applications that [=has a home + tab|have a home tab=]: +

    +
      +
    • every application window has exactly one [=home tab context=], + and +
    • +
    • every [=home tab context=]'s active document's [=Document/URL=] + is [=within home tab scope=] (unless the document's URL has changed + since it was created), and +
    • +
    • every non-home-tab [=application context=]'s active document's + [=Document/URL=] is [=outside of home tab scope=] (unless the + document's URL has changed since it was created). +
    • +
    +

    + User agents will not dynamically move documents between home-tab + and non-home-tab contexts if they change their [=URL/fragment=], or + use the {{History}} API to modify their display URL into or out of + the home tab scope, because no navigation is taking place. For this + reason, the above invariants only care about the [=Document/URLs=] + that documents had at the time of their creation. +

    +

    + For single-page applications that "pretend" to navigate by + modifying their URLs, this may result in undesirable behaviour that + breaks the above invariants (e.g. if the user clicks a link from + the home tab to dynamically change the URL to a non-home page, they + will stay inside the home tab because it is not actually + navigating). To avoid this situation, the application can detect + when it is in tabbed application mode and change its behavior to + perform actual navigations into and out of the home tab scope, + rather than modifying the URL. +

    +
    +
    +
    +

    + new_tab_button member +

    +

    + The [=tab_strip/new_tab_button=] member is an ordered map that + describes the behaviour of a UI affordance (such as a button) which, + when clicked/activated, opens a new [=application context=] within + the application window. It has the following members: +

    +
      +
    • [=new_tab_button/url=] +
    • +
    +

    + The url member + is a string that represents a URL relative to the [=manifest URL=] + that is [=manifest/within scope=] of a [=Document/processed + manifest=]. +

    +

    + An application has a new tab button if the + [=Document/processed manifest=]'s [=new_tab_button=]'s + [=new_tab_button/url=] member is [=outside of home tab scope=]. If + the application does not [=has a new tab button|have a new tab + button=], the user agent SHOULD NOT make the "new tab" affordance + available to the end user. +

    + +

    + When the new tab button is activated by the end user, the following + steps are run: +

    +
      +
    1. [=Create a new application context=] in the current window and + focus it. +
    2. +
    3. Navigate it to the value of the [=new_tab_button/url=] member of + [=new_tab_button=]. +
    4. +
    +
    +
    +

    + Processing the `tab_strip` member +

    +

    + To process the `tab_strip` member, given [=ordered map=] + |json:ordered map|, [=ordered map=] |manifest:ordered map|, and + [=URL=] |manifest URL:URL|, run the following during the + + extension point in [=processing a manifest=]: +

    +
      +
    1. Set |manifest|["tab_strip"] to a new [=ordered map=]. +
    2. +
    3. If |json|["tab_strip"] does not exist or |json|["tab_strip"] is + not an [=ordered map=]: +
        +
      1. Set |manifest|["tab_strip"]["new_tab_button"]["url"] to + |manifest|["start_url"]. +
      2. +
      3. Return. +
      4. +
      +
    4. +
    5. [=Process the `home_tab` member=] passing |json|["tab_strip"], + |manifest|["tab_strip"], and |manifest URL|. +
    6. +
    7. [=Process the `new_tab_button` member=] passing + |json|["tab_strip"], |manifest|["tab_strip"], |manifest URL|, and + |manifest|["start_url"]. +
    8. +
    +
    +

    + Processing the `home_tab` member +

    +

    + To process the `home_tab` member, given [=ordered map=] + |json tab strip:ordered map|, [=ordered map=] |manifest tab + strip:ordered map|, and [=URL=] |manifest URL:URL|, run the + following: +

    +
      +
    1. If |json tab strip|["home_tab"] does not exist or |json tab + strip|["home_tab"] not an [=ordered map=], return. +
    2. +
    3. Let |home tab:ordered map| be a new [=ordered map=]. +
    4. +
    5. [=Process the `scope_patterns` member=] passing |json tab + strip|["home_tab"]["scope_patterns"], |home tab| and |manifest + URL|. +
    6. +
    7. Set |manifest tab strip|["home_tab"] to |home tab|. +
    8. +
    +
    +
    +

    + Processing the `new_tab_button` member +

    +

    + To process the `new_tab_button` member, given [=ordered + map=] |json tab strip:ordered map|, [=ordered map=] |manifest tab + strip:ordered map|, [=URL=] |manifest URL:URL|, and [=URL=] |start + URL:URL|, run the following: +

    +
      +
    1. Set |manifest tab strip|["new_tab_button"]["url"] to |start + URL|. +
    2. +
    3. If |json tab strip|["new_tab_button"] does not exist or |json + tab strip|["new_tab_button"] is not an [=ordered map=], return. +
    4. +
    5. Let |url:URL| be the result of [=URL Parser|parsing=] |json tab + strip|["new_tab_button"]["url"] with |manifest URL| as the base + URL. +
    6. +
    7. If |url| is failure, return. +
    8. +
    9. If |url| is not [=URL/within scope=] of |manifest URL|, return. +
    10. +
    11. Set |manifest tab strip|["new_tab_button"]["url"] to |url|. +
    12. +
    +
    +
    +

    + Processing the `scope_patterns` member +

    +

    + To process the `scope_patterns` member, given [=ordered + map=] |json home tab:ordered map|, [=ordered map=] |manifest home + tab:ordered map| and [=URL=] |manifest URL:URL|, run the following: +

    +
      +
    1. Let |processed scope patterns:list| be a new [=list=]. +
    2. +
    3. Set |manifest home tab|["scope_patterns"] to |processed scope + patterns|. +
    4. +
    5. If |json home tab|["scope_patterns"] doesn't exist or |json + home tab|["scope_patterns"] is not a [=list=], return. +
    6. +
    7. For each |entry:URLPatternInit| of |json home + tab|["scope_patterns"]: +
        +
      1. Let |pattern:URL pattern| be the result of [=build a URL + pattern from an infra value|building a URL pattern from an + infra value=] |entry| given |manifest URL|. If this process + throws or returns null, continue. +
      2. +
      3. Append |pattern| to |processed scope patterns|. +
      4. +
      +
    8. +
    +

    +
    +
    +
    +

    + Usage Example +

    +
    +        {
    +          "name": "Tabbed App Example",
    +          "start_url": "/",
    +          "display": "standalone",
    +          "display_override": ["tabbed"],
    +          "tab_strip": {
    +            "home_tab": {
    +              "scope_patterns": [
    +                {"pathname": "/"},
    +                {"pathname": "/index.html"}
    +              ]
    +            },
    +            "new_tab_button": {
    +              "url": "/create"
    +            }
    +          }
    +        }
    +        
    +

    + This example is a tabbed web app that falls back to a single-document + standalone window if tabbed mode is not supported. Any navigation to + the main index page (either / or + /index.html) is opened in the [=home tab context=]. The + new tab button will open a new tab at /create. +

    +

    + Note that the [=URL/query=] part of the URL is ignored by default + when matching against [=tab_strip/home_tab/scope_patterns=] (so a + navigation to /index.html?utm_source=foo will open in + the home tab). However, when matching against [=start URL=], the + [=URL/query=] must match exactly, so sites that want to ignore the + query are advised to explicitly include the [=start URL=]'s + [=URL/path=] as a scope pattern. +

    +
    +

    `share_target` member