├── .github └── workflows │ └── build-validate-publish.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── IDL.md ├── LICENSE.md ├── README.md ├── SECURITY_AND_PRIVACY.md ├── spec └── index.bs └── w3c.json /.github/workflows/build-validate-publish.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | on: 3 | pull_request: {} 4 | push: 5 | branches: [main] 6 | 7 | jobs: 8 | main: 9 | name: Build, Validate, and Publish 10 | runs-on: ubuntu-20.04 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: w3c/spec-prod@v2 14 | with: 15 | SOURCE: spec/index.bs 16 | TOOLCHAIN: bikeshed 17 | GH_PAGES_BRANCH: gh-pages 18 | BUILD_FAIL_ON: nothing 19 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | All documentation, code and communication under this repository are covered by the [W3C Code of Ethics and Professional Conduct](https://www.w3.org/Consortium/cepc/). 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Web Platform Incubator Community Group 2 | 3 | This repository is being used for work in the W3C Web Platform Incubator Community Group, governed by the [W3C Community License 4 | Agreement (CLA)](http://www.w3.org/community/about/agreements/cla/). To make substantive contributions, 5 | you must join the CG. 6 | 7 | If you are not the sole contributor to a contribution (pull request), please identify all 8 | contributors in the pull request comment. 9 | 10 | To add a contributor (other than yourself, that's automatic), mark them one per line as follows: 11 | 12 | ``` 13 | +@github_username 14 | ``` 15 | 16 | If you added a contributor by mistake, you can remove them in a comment with: 17 | 18 | ``` 19 | -@github_username 20 | ``` 21 | 22 | If you are making a pull request on behalf of someone else but you had no part in designing the 23 | feature, you can remove yourself with the above syntax. 24 | -------------------------------------------------------------------------------- /IDL.md: -------------------------------------------------------------------------------- 1 | # Proposed WebIDL 2 | 3 | ## Definition of ContentIndex 4 | ```webidl 5 | enum ContentCategory { 6 | "", 7 | "homepage", 8 | "article", 9 | "video", 10 | "audio", 11 | // ... 12 | }; 13 | 14 | dictionary ContentDescription { 15 | required DOMString id; 16 | required DOMString title; 17 | required DOMString description; 18 | ContentCategory category = ""; 19 | sequence icons = []; 20 | required USVString url; 21 | }; 22 | 23 | interface ContentIndex { 24 | Promise add(ContentDescription description); 25 | Promise delete(DOMString id); 26 | Promise> getAll(); 27 | }; 28 | ``` 29 | 30 | ## Additions to the Service Worker Registration 31 | ```webidl 32 | partial interface ServiceWorkerRegistration { 33 | [SameObject] readonly attribute ContentIndex index; 34 | }; 35 | ``` 36 | 37 | ## Additions to the Service Worker Global Scope 38 | ```webidl 39 | dictionary ContentIndexEventInit : ExtendableEventInit { 40 | required DOMString id; 41 | }; 42 | 43 | interface ContentIndexEvent : ExtendableEvent { 44 | constructor(DOMString type, ContentIndexEventInit init); 45 | readonly attribute DOMString id; 46 | }; 47 | 48 | partial interface ServiceWorkerGlobalScope { 49 | attribute EventHandler oncontentdelete; 50 | }; 51 | ``` 52 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | All Reports in this Repository are licensed by Contributors 2 | under the 3 | [W3C Software and Document License](http://www.w3.org/Consortium/Legal/2015/copyright-software-and-document). 4 | 5 | Contributions to Specifications are made under the 6 | [W3C CLA](https://www.w3.org/community/about/agreements/cla/). 7 | 8 | Contributions to Test Suites are made under the 9 | [W3C 3-clause BSD License](https://www.w3.org/Consortium/Legal/2008/03-bsd-license.html) 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Content Indexing 2 | **Written**: 2019-04-29
3 | **Updated**: 2019-05-08
4 | **Spec**: [Draft](https://wicg.github.io/content-index/spec/) 5 | 6 | High quality offline-enabled web content is not easily discoverable by users right now. They would have to know which 7 | websites work offline or install a PWA to be able to browse through content while offline. This is not a great user 8 | experience as there is no central point to discover available content. To address this, we propose a new API to allow 9 | developers to tell the browser about their specific content. 10 | 11 | The **content index** allows websites to register their offline enabled content in the browser. This allows the browser 12 | to improve their offline capabilities and offer content to users to browse through while offline. This data could also 13 | be used to improve on-device search and augment browsing history. 14 | 15 | ## Why do we need this? 16 | Unrealiable or even unavailable network connections are very common on mobile devices. Even in connected cities like 17 | London, people have very limited connectivity while travelling underground. This API would allow browsers to show 18 | meaningful content to users in these situations and sites to increase user engagement. 19 | 20 | Browser vendors are already looking for content relevant to the user, based on their browsing history, and make it 21 | available to be consumed offline. This is not ideal as it ignores the entity with the most knowledge of that content - 22 | the providers themselves. With this API they can highlight user specific, high quality content through the browser. 23 | Grouping content by a category (e.g. 'article', 'video', 'audio') allows an even richer experience as the browser is 24 | able to understand what kind of content is available and show a relevant UI. 25 | 26 | ### Usage scenario 1 27 | A news publisher has a website that uses service workers to allow its users to read news articles offline. Highly 28 | engaged users of this website may see a link to the site in their browsers home screen, but have no way of knowing if 29 | there are any new articles available to read beforehand. The news site can leverage web notifications for high priority 30 | breaking news articles, but should not use them for less important ones. By using this API, the news site can simply 31 | expose its content to the browser which can then surface that content to the user. Users can then browse available 32 | content in a central location, even while offline. 33 | 34 | ### Usage scenario 2 35 | A blog publishes regular podcasts to its users. It is available as a PWA and uses background fetch to download the audio 36 | files. An embedded media player then allows users to listen to these podcasts. With this API, these podcasts can be 37 | surfaced in the OS, allowing users to search for their downloaded content through native UI surfaces. This integration 38 | is only available with native apps at the moment. 39 | 40 | ## Goals 41 | - Allow users to easily find content even while **offline** 42 | - Surface high quality content in native spaces (example: [Android Slices](https://developer.android.com/guide/slices)) 43 | 44 | ## Non-goals 45 | - Storage of the offline content itself
46 | *We expect developers to use more specialized APIs to store content (see 47 | [Service Worker Caches](https://w3c.github.io/ServiceWorker/#cache-objects) or 48 | [Web Storage](https://www.w3.org/TR/webstorage/)).* 49 | 50 | ## Broader API landscape 51 | ### Service Worker 52 | We propose to add this API as an extension to [Service Workers](https://w3c.github.io/ServiceWorker/). This allows 53 | browsers to check if the given content is actually available offline. This also makes it easier for developers, as the 54 | entries get removed automatically if the service worker is unregistered (and therefore can not provide the offline 55 | content anymore). 56 | 57 | ### CacheStorage API 58 | The [CacheStorage API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) allows websites to cache requests and, 59 | for example, use the cached content in the `fetch` event of a service worker. This makes it easy to ensure that some 60 | content is available offline, and is one of the steps to create high quality 61 | [Progressive Web Apps](https://codelabs.developers.google.com/codelabs/your-first-pwapp/#5). 62 | 63 | ### Web Packaging 64 | [Web Packaging](https://github.com/WICG/webpackage) is a proposed API to bundle resources of a website together, so they 65 | can be shared offline. This also allows them to be securely distributed as a bundle. This API plays nicely together with 66 | **Content Indexing**, making it easier to ensure all necessary content is available offline. 67 | 68 | ## Security and Privacy 69 | Developers have control over which content they want to make available to the browser. The lifetime of an entry in the 70 | **content index** is comparable to that of Notifications, but with a less intrusive UX and more structured content. When 71 | adding personalized content, websites can simply remove entries on logout (and close all open Notifications). The 72 | storage required to store the entries of the index itself count towards the quota of the origin. 73 | [This document](SECURITY_AND_PRIVACY.md) contains additional answers of the [security & privacy questionnaire](https://www.w3.org/TR/security-privacy-questionnaire/). 74 | 75 | ## Abuse Considerations 76 | Browsers surfacing content can lead to increased exposure to a website, which would lead to increased profit for said 77 | website. This might cause malicious/spammy websites to aggressivley register offline-capable content in order to 78 | maximize their chances of exposure. The spec does not enforce a cap on how many content index entries can be registered, 79 | nor does it enforce that the content should be displayed. Therefore, it is important that browsers to choose the 80 | appropriate content to surface at the right time. 81 | 82 | That remains to be an implementer choice, but some useful signals browsers can use are: 83 | - The user's engagement with the website 84 | - The freshness of the content 85 | - The user's engagement with other surfaced content from the website 86 | 87 | Browsers can also apply a per-origin cap for the content to surface, and penalize websites that don't clean-up 88 | expired content and user-deleted content. 89 | 90 | ## Examples 91 | Please see [this separate document](IDL.md) for the proposed WebIDL additions. 92 | 93 | ### General usage 94 | ```javascript 95 | // Add an article to the content index 96 | await swRegistration.index.add({ 97 | id: 'article-123', 98 | title: 'Article title', 99 | description: 'Amazing article about things!', 100 | category: 'article', 101 | icons: [ 102 | { 103 | src: 'https://website.dev/img/article-123.png', 104 | sizes: '64x64', 105 | type: 'image/png', 106 | }, 107 | ], 108 | url: 'https://website.dev/articles/123', 109 | }); 110 | 111 | // Delete an entry from the content index 112 | await swRegistration.index.delete('article-123'); 113 | 114 | // List all entries in the content index 115 | const entries = await swRegistration.index.getAll(); 116 | ``` 117 | 118 | ### Combined with other APIs 119 | Sending breaking news articles via Push API allows websites to keep their users up to date. Adding these articles to the 120 | **content index** allows the browser to highlight them and make them discoverable later on. In this example we make use 121 | of the [CacheStorage API](https://developer.mozilla.org/en-US/docs/Web/API/Cache) to cache content resources, and the 122 | [Indexed Database](https://w3c.github.io/IndexedDB/) to store the structured content. 123 | ```javascript 124 | async function handlePush(data) { 125 | // Fetch additional data about pushed content 126 | const news = await fetch(`/api/news/${data.id}`); 127 | 128 | // Store content in database and cache resources 129 | await Promise.all([db.add(news), cache.add(news.icons[0].src)]); 130 | 131 | // Add content to content index 132 | if ('index' in self.registration) { 133 | await self.registration.index.add({ 134 | id: news.id, 135 | title: news.title, 136 | description: news.description, 137 | category: 'article', 138 | icons: news.icons, 139 | url: `/news/${news.id}`, 140 | }); 141 | } 142 | 143 | // Display a notification 144 | return self.registration.showNotification(news.title, { 145 | tag: news.id, 146 | body: news.description, 147 | icon: news.icons[0].src, 148 | }); 149 | } 150 | 151 | // Handle web push event in service worker 152 | self.addEventListener('push', event => event.waitUntil(handlePush(event.data.json()))); 153 | 154 | // Handle content deletion event in service worker. 155 | // This is called when a user (or useragent) has deleted the content. 156 | self.addEventListener('contentdelete', event => { 157 | event.waitUntil(Promise.all([ 158 | // Delete cache & DB entries using `event.id`. 159 | ])); 160 | }); 161 | ``` 162 | 163 | When used together with the proposed 164 | [Periodic Background Sync API](https://github.com/beverloo/periodic-background-sync), this allows websites to 165 | automatically sync fresh content and make it available to users. 166 | ```javascript 167 | // Add an article to the content index 168 | function addArticleToIndex(article) { 169 | return self.registration.index.add({ 170 | id: article.id, 171 | title: article.title, 172 | description: article.description, 173 | category: 'article', 174 | icons: article.icons, 175 | url: '/articles/' + article.id, 176 | }); 177 | } 178 | 179 | // Fetch new content, cache it and add it to the content index 180 | async function updateLatestNews() { 181 | const latestNews = await fetch('/latest-news'); 182 | // TODO: cache content 183 | if ('index' in self.registration) { 184 | await Promise.all(latestNews.map(addArticleToIndex)); 185 | } 186 | } 187 | 188 | // Handle periodic sync event in service worker 189 | self.addEventListener('periodicsync', event => { 190 | if (event.registration.tag === 'get-latest-news') { 191 | event.waitUntil(updateLatestNews()); 192 | } 193 | }); 194 | ``` 195 | 196 | ## Alternatives considered 197 | ### Extending the Cache interface 198 | One of the requirements for this API is that the exposed content is available offline. In the case of an article this 199 | could be implemented by simply adding the response to the 200 | [Service Worker Cache](https://developers.google.com/web/ilt/pwa/caching-files-with-service-worker). We could extend 201 | this API to specify that specific cached entries can be exposed to the user. 202 | 203 | This would limit some use cases as new content would have to be served from a server. When using the **content index**, 204 | developers could generate and store content offline and then add it to the index, making it available without any 205 | network connection. 206 | 207 | ## References 208 | * [Push API](https://w3c.github.io/push-api/) 209 | * [Background Sync](https://wicg.github.io/BackgroundSync/spec/) 210 | * [Periodic Background Sync](https://github.com/beverloo/periodic-background-sync) 211 | * [Notifications API](https://github.com/whatwg/notifications) 212 | 213 | Many thanks to [@beverloo](https://github.com/beverloo) and [@jakearchibald](https://github.com/jakearchibald) for their 214 | ideas, input and discussion. 215 | -------------------------------------------------------------------------------- /SECURITY_AND_PRIVACY.md: -------------------------------------------------------------------------------- 1 | # Answers to [Security and Privacy Questionnaire](https://www.w3.org/TR/security-privacy-questionnaire/) 2 | 3 | ### 2.1 What information might this feature expose to Web sites or other parties, and for what purposes is that exposure necessary? 4 | 5 | This feature does not give access to any other information than what the Web site itself supplies to this API. The supplied information can then be used to surface offline available content to the user to be consumed when there is no network connection. 6 | 7 | ### 2.2 Is this specification exposing the minimum amount of information necessary to power the feature? 8 | 9 | No extra information exposed. 10 | 11 | ### 2.3 How does this specification deal with personal information or personally-identifiable information or information derived thereof? 12 | 13 | This feature does not give access to any other information than what the Web site supplies to this API. 14 | 15 | ### 2.4 How does this specification deal with sensitive information? 16 | 17 | This feature does not give access to any other information than what the Web site supplies to this API. Furthermore, the data is tied to a service worker registration, and therefore to encrypted and authenticated origins. 18 | 19 | ### 2.5 Does this specification introduce new state for an origin that persists across browsing sessions? 20 | 21 | Yes, however, this data is tied to a service worker registration and its lifetime. See [service worker privacy section](https://www.w3.org/TR/service-workers-1/#privacy). 22 | 23 | ### 2.6 What information from the underlying platform, e.g. configuration data, is exposed by this specification to an origin? 24 | 25 | None. 26 | 27 | ### 2.7 Does this specification allow an origin access to sensors on a user’s device? 28 | 29 | No. 30 | 31 | ### 2.8 What data does this specification expose to an origin? Please also document what data is identical to data exposed by other features, in the same or different contexts. 32 | 33 | This feature only exposes data that the origin itself supplied to the API. 34 | 35 | ### 2.9 Does this specification enable new script execution/loading mechanisms? 36 | 37 | No new script loading mechanism enabled. The user agent might load new resources (image data) when a new entry is added with this API. 38 | 39 | ### 2.10 Does this specification allow an origin to access other devices? 40 | 41 | No. 42 | 43 | ### 2.11 Does this specification allow an origin some measure of control over a user agent’s native UI? 44 | 45 | The user agent might display information from an origin that uses this API. This includes text and image / icon data. However, the user agent is in full control over if, how and when this data should be displayed in its UI. 46 | 47 | ### 2.12 What temporary identifiers might this this specification create or expose to the web? 48 | 49 | All identifiers are provided by the Web site itself. 50 | 51 | ### 2.13 How does this specification distinguish between behavior in first-party and third-party contexts? 52 | 53 | The data will only be available to first-party contexts. 54 | 55 | ### 2.14 How does this specification work in the context of a user agent’s Private \ Browsing or "incognito" mode? 56 | 57 | It will work the same way. During an active "incognito" session, the user agent might show the passed data in its UI, however, as soon as that session ends, the data will be removed together with all other data tied to the service worker registration. 58 | 59 | ### 2.15 Does this specification have a "Security Considerations" and "Privacy Considerations" section? 60 | 61 | Yes. See the [explainer](README.md#security-and-privacy). 62 | 63 | ### 2.16 Does this specification allow downgrading default security characteristics? 64 | 65 | No. 66 | -------------------------------------------------------------------------------- /spec/index.bs: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 19 |
 20 | spec: html; urlPrefix: https://html.spec.whatwg.org/multipage/
 21 |     type: dfn
 22 |         urlPrefix: browsers.html
 23 |             text: ancestor origins list; for: Location; url: concept-location-ancestor-origins-list
 24 | 
 25 | spec: page-visibility; urlPrefix: https://www.w3.org/TR/page-visibility/
 26 |     type: attribute; text: visibilityState; for: Document; url: dom-document-visibilitystate
 27 | 
 28 | spec: service-workers; urlPrefix: https://www.w3.org/TR/service-workers/
 29 |     type: dfn; text: create window client; url: create-windowclient-algorithm
 30 | 
 31 | spec: image-resource; urlPrefix: https://w3c.github.io/image-resource/
 32 |     type: dictionary; text: ImageResource; url: dom-imageresource
 33 |     type: dfn; text: image resource; url: image-resource
 34 |     type: dfn; text: src; for:image resource; url: dfn-src
 35 |     type: dfn; text: parsing; url: processing-an-imageresource-from-an-api
 36 | 
37 | 38 | 56 | 57 | Introduction {#intro} 58 | ===================== 59 | 60 | High quality offline-enabled web content is not easily discoverable by users right now. Users would have to know which 61 | websites work offline, or they would need to have an installed PWA, to be able to browse through content while offline. 62 | This is not a great user experience as there no entry points to discover available content. To address this, the spec 63 | covers a new API which allows developers to tell the browser about their specific content. 64 | 65 | The content index allows websites to register their offline enabled content with the browser. The browser can then 66 | improve the website's offline capabilities and offer content to users to browse through while offline. This data could 67 | also be used to improve on-device search and augment browsing history. 68 | 69 | Using the API can help users more easily discover content for the following example use cases: 70 | 71 | * A news website prefetching the latest articles in the background. 72 | * A content streaming app registering downloaded content with the browser. 73 | 74 | ## Example ## {#example} 75 | 76 |
77 | Registering an offline news article in a [=service worker=]. 78 | 79 |
 80 |     function deleteArticleResources(id) {
 81 |       return Promise.all([
 82 |         caches.open('offline-articles')
 83 |             .then(articlesCache => articlesCache.delete(`/article/${id}`)),
 84 |         // This is a no-op if the function was called as a result of the
 85 |         // `contentdelete` event.
 86 |         self.registration.index.delete(id),
 87 |       ]);
 88 |     }
 89 |     
 90 |     self.addEventListener('activate', event => {
 91 |       // When the service worker is activated, remove old content.
 92 |       event.waitUntil(async function() {
 93 |         const descriptions = await self.registration.index.getAll();
 94 |         const oldDescriptions =
 95 |             descriptions.filter(description => shouldRemoveOldArticle(description));
 96 |         await Promise.all(
 97 |             oldDescriptions.map(description => deleteArticleResources(description.id)));
 98 |       }());
 99 |     });
100 |     
101 |     self.addEventListener('push', event => {
102 |       const payload = event.data.json();
103 |     
104 |       // Fetch & store the article, then register it.
105 |       event.waitUntil(async function() {
106 |         const articlesCache = await caches.open('offline-articles');
107 |         await articlesCache.add(`/article/${payload.id}`);
108 |         await self.registration.index.add({
109 |           id: payload.id,
110 |           title: payload.title,
111 |           description: payload.description,
112 |           category: 'article',
113 |           icons: payload.icons,
114 |           url: `/article/${payload.id}`,
115 |         });
116 |     
117 |         // Show a notification if urgent.
118 |       }());
119 |     });
120 |     
121 |     self.addEventListener('contentdelete', event => {
122 |       // Clear the underlying content after user-deletion.
123 |       event.waitUntil(deleteArticleResources(event.id));
124 |     });    
125 |   
126 | 127 | In the above example, shouldRemoveOldArticle is a developer-defined function. 128 |
129 | 130 | Privacy Considerations {#privacy-considerations} 131 | ================================================ 132 | 133 | Firing the contentdelete event may reveal the user's IP address after the user has left the page. Exploiting 134 | this can be used for tracking location history. The user agent SHOULD limit tracking by capping the duration of the 135 | event. 136 | 137 | When firing the contentdelete event, the user agent SHOULD prevent the website from adding new content. 138 | This prevents spammy websites from re-adding the same content, and the users from being shown content they just 139 | deleted. 140 | 141 | [=Displaying=] all registered content can cause malicious websites to spam users with their content to maximize 142 | exposure. User agents are strongly encouraged to not surface all content, but rather choose the appropriate content 143 | to [=display=] based on a set of user agent defined signals, aimed at improving the user experience. 144 | 145 | Infrastructure {#infrastructure} 146 | ================================ 147 | 148 | ## Extensions to service worker registration ## {#service-worker-registration-concept-extensions} 149 | 150 | A [=/service worker registration=] additionally has: 151 | 152 |
153 | * Content index entries (a [=map=]), where each key is a DOMString, and each item is a 154 | [=content index entry=]. 155 | * An entry edit queue (a [=parallel queue=]), initially the result of 156 | [=starting a new parallel queue=]. 157 |
158 | 159 | ## Content index entry ## {#content-index-entry-concept} 160 | 161 | A content index entry consists of: 162 | 163 |
164 | * A description (a {{ContentDescription}}). 165 | * A launch url (a [=/URL=]). 166 | * A service worker registration (a [=/service worker registration=]). 167 | * icons (a [=/list=] of [=/responses=] that are image decodable). 168 |
169 | 170 | ### [=Display=] ### {#content-index-entry-display} 171 | 172 | The user agent MAY [=display=] a [=/content index entry=] (|entry|) at any time, as long as |entry| [=map/exists=] in 173 | a [=/service worker registration=]'s [=content index entries=]. 174 | 175 | Note: User agents should limit surfaced content to avoid showing too many entries to a user. 176 | 177 |
178 | To display a [=/content index entry=] (|entry|), the user agent MUST present a user interface that 179 | follows these rules: 180 | 181 | * The UI MUST prominently display the |entry|'s [=content index entry/service worker registration=]'s [=service 182 | worker registration/scope url=]'s [=url/origin=]. 183 | * The UI MUST display |entry|'s [=content index entry/description=]'s {{ContentDescription/title}}. 184 | * The UI MAY display |entry|'s [=content index entry/description=]'s {{ContentDescription/description}}. 185 | * The UI MAY use |entry|'s [=content index entry/description=]'s {{ContentDescription/category}} in display 186 | decisions. 187 | * The UI MAY display any of |entry|'s [=content index entry/icons=] as images. 188 | * The UI SHOULD provide a way for the user to delete the underlying |entry| exposed by the UI, in which case 189 | run [=delete a content index entry=] for |entry|. 190 | * The UI MUST provide a way for the user to activate it (for example by clicking), in which case run 191 | [=activate a content index entry=] for |entry|. 192 |
193 | 194 | ### [=Undisplay=] ### {#content-index-entry-undisplay} 195 | 196 |
197 | To undisplay a [=/content index entry=] (|entry|), the user agent MUST remove all UI associated with 198 | running [=display=] on |entry|. 199 |
200 | 201 | Algorithms {#algorithms} 202 | ======================== 203 | 204 | ## [=Delete a content index entry=] ## {#delete-a-content-index-entry-algorithm} 205 | 206 |
207 | To delete a content index entry for |entry| (a [=content index entry=]), run these steps: 208 | 209 | 1. Let |id| be |entry|'s [=content index entry/description=]'s {{ContentDescription/id}}. 210 | 1. Let |contentIndexEntries| be |entry|'s [=content index entry/service worker registration=]'s 211 | [=content index entries=]. 212 | 1. [=Enqueue the following steps=] to |entry|'s [=content index entry/service worker registration=]'s 213 | [=entry edit queue=]: 214 | 1. [=Undisplay=] |entry|. 215 | 1. [=map/Remove=] |contentIndexEntries|[|id|]. 216 | 1. [=Fire a content delete event=] for |entry|. 217 |
218 | 219 | ## [=Activate a content index entry=] ## {#activate-a-content-index-entry-algorithm} 220 | 221 |
222 | To activate a content index entry for |entry| (a [=content index entry=]), run these steps: 223 | 224 | 1. Let |activeWorker| be |entry|'s [=content index entry/service worker registration=]'s [=active worker=]. 225 | 1. If |activeWorker| is null, abort these steps. 226 | 1. Let |newContext| be a new [=top-level browsing context=]. 227 | 1. [=Queue a task=] to run the following steps on |newContext|'s {{Window}} object's 228 | [=environment settings object=]'s [=responsible event loop=] using the [=user interaction task source=]: 229 | 1. HandleNavigate: [=Navigate=] |newContext| to |entry|'s [=content index entry/launch url=] with 230 | [=exceptions enabled flag|exceptions enabled=] and [=replacement enabled=]. 231 | 1. If the algorithm steps invoked in the step labeled HandleNavigate [=throws=] an exception, abort 232 | these steps. 233 | 1. Let |frameType| be "`top-level`". 234 | 1. Let |visibilityState| be |newContext|'s [=active document=]'s {{Document/visibilityState}} attribute value. 235 | 1. Let |focusState| be the result of running the [=has focus steps=] with |newContext|'s [=active document=] as 236 | the argument. 237 | 1. Let |ancestorOriginsList| be |newContext|'s [=active document=]'s [=relevant global object=]'s {{Location}} 238 | object's [=Location/ancestor origins list=]'s associated list. 239 | 1. Let |serviceWorkerEventLoop| be |activeWorker|'s [=service worker/global object=]'s [=event loop=]. 240 | 1. [=Queue a task=] to run the following steps on |serviceWorkerEventLoop| using the 241 | [=DOM manipulation task source=]: 242 | 1. If |newContext|'s {{Window}} object's [=environment settings object=]'s [=creation URL=]'s 243 | [=environment settings object/origin=] is not the [=same origin|same=] as the |activeWorker|'s 244 | [=environment settings object/origin=], abort these steps. 245 | 1. Run [=Create Window Client=] with |newContext|'s {{Window}} object's [=environment settings object=], 246 | |frameType|, |visibilityState|, |focusState|, and |ancestorOriginsList| as the arguments. 247 | 248 | Issue: Is creating a new Browsing Context the right thing to do here? 249 | (issue) 250 |
251 | 252 | ## [=Fire a content delete event=] ## {#fire-content-delete-event-algorithm} 253 | 254 |
255 | To fire a content delete event for |entry| (a [=content index entry=]), [=fire a functional event=] named 256 | "contentdelete" using {{ContentIndexEvent}} on |entry|'s [=content index entry/service worker registration=] with 257 | the following properties: 258 | : {{ContentIndexEvent/id}} 259 | :: |entry|'s [=content index entry/description=]'s {{ContentDescription/id}}. 260 |
261 | 262 | API {#api} 263 | ========== 264 | 265 | ## Extensions to {{ServiceWorkerGlobalScope}} ## {#extensions-to-service-worker-global} 266 | 267 | 272 | 273 | ### Events ### {#service-worker-global-events} 274 | 275 | The following is the event handler (and its corresponding event handler event type) that must be 276 | supported, as event handler IDL attributes, by all objects implementing {{ServiceWorker}} interface: 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 |
[=event handler event type=][=event handler=]Interface
contentdelete{{ServiceWorkerGlobalScope/oncontentdelete}}{{ContentIndexEvent}}
294 | 295 | ## Extensions to {{ServiceWorkerRegistration}} ## {#extensions-to-service-worker-registration} 296 | 297 | 302 | 303 |
304 | 305 | A {{ServiceWorkerRegistration}} has a content index (a {{ContentIndex}}), initially a 306 | new {{ContentIndex}} whose [=ContentIndex/service worker registration=] is the [=context 307 | object=]'s [=/service worker registration=]. 308 | 309 | The index attribute's getter must return the [=context object=]'s 310 | [=ServiceWorkerRegistration/content index=]. 311 |
312 | 313 | ## {{ContentIndex}} ## {#content-index} 314 | 315 | 340 | 341 |
342 | 343 | A {{ContentIndex}} has a service worker registration (a [=/service worker registration=]). 344 | 345 | ### {{ContentIndex/add()}} ### {#content-index-add} 346 | 347 |
348 | The add(|description|) method, when invoked, must return [=a new promise=] |promise| and run 349 | these steps [=in parallel=]: 350 | 351 | 1. Let |registration| be the [=context object=]'s [=ContentIndex/service worker registration=]. 352 | 1. If |registration|'s [=active worker=] is null, [=reject=] |promise| with a {{TypeError}} and abort these steps. 353 | 1. If any of |description|'s {{ContentDescription/id}}, {{ContentDescription/title}}, 354 | {{ContentDescription/description}}, or {{ContentDescription/url}} is the empty [=/string=], [=reject=] 355 | |promise| with a {{TypeError}} and abort these steps. 356 | 1. Let |launchURL| be the result of [=URL parser|parsing=] |description|'s {{ContentDescription/url}} with 357 | [=context object=]'s [=relevant settings object=]'s [=API base URL=]. 358 | 359 | Note: A new [=/service worker registration=] might be introduced later with a narrower scope. 360 | 1. Let |matchedRegistration| be the result of running [=Match Service Worker Registration=] algorithm with 361 | |launchURL| as its argument. 362 | 1. If |matchedRegistration| is not equal to |registration|, [=reject=] |promise| with a {{TypeError}} and abort 363 | these steps. 364 | 1. If |registration|'s [=active worker=]'s [=set of extended events=] does not [=set/contain=] a {{FetchEvent}}, 365 | [=reject=] |promise| with a {{TypeError}} and abort these steps. 366 | 1. Let |icons| be an empty [=/list=]. 367 | 1. Optionally, the user agent MAY select icons to use from |description|'s {{ContentDescription/icons}}. In which 368 | case run the following steps [=list/for each=] [=image resource=] (|resource|) of |description|'s {{ContentDescription/icons}}' 369 | selected icons after successfully [=parsing=] it: 370 | 1. Let |response| be the result of awaiting a [=/fetch=] using a new [=/request=] with the following 371 | properties: 372 | : [=request/URL=] 373 | :: |resource|'s [=image resource/src=]. 374 | : [=request/Client=] 375 | :: [=context object=]'s [=relevant settings object=]. 376 | : [=request/Keepalive flag=] 377 | :: Set. 378 | : [=request/Destination=] 379 | :: "`image`". 380 | : [=request/Mode=] 381 | :: "`no-cors`". 382 | : [=request/Credentials mode=] 383 | :: "`include`". 384 | 1. If |response| is a [=network error=], [=reject=] |promise| with a {{TypeError}} and abort these steps. 385 | 1. If |response| cannot be decoded as an image, [=reject=] |promise| with a {{TypeError}} and abort these 386 | steps. 387 | 1. [=list/Append=] |response| to |icons|. 388 | 389 | 1. Let |entry| be a new [=/content index entry=] with: 390 | : [=content index entry/description=] 391 | :: |description|. 392 | : [=content index entry/launch url=] 393 | :: |launchURL| 394 | : [=content index entry/service worker registration=] 395 | :: |registration|. 396 | : [=content index entry/icons=] 397 | :: |icons| 398 | 1. Let |id| be |description|'s {{ContentDescription/id}}. 399 | 1. Let |contentIndexEntries| be |registration|'s [=content index entries=]. 400 | 1. [=Enqueue the following steps=] to |registration|'s [=entry edit queue=]: 401 | 1. [=map/Set=] |contentIndexEntries|[|id|] to |entry|. 402 | 1. Optionally, the user agent MAY [=display=] |entry|. 403 | 1. [=Resolve=] |promise| with undefined. 404 | 405 | Note: Adding a description with an existing ID would overwrite the previous value. 406 |
407 | 408 | ### {{ContentIndex/delete()}} ### {#content-index-delete} 409 | 410 |
411 | The delete(|id|) method, when invoked, must return [=a new promise=] |promise| and run these 412 | steps [=in parallel=]: 413 | 414 | 1. Let |registration| be the [=context object=]'s [=ContentIndex/service worker registration=]. 415 | 1. Let |contentIndexEntries| be |registration|'s [=content index entries=]. 416 | 1. [=Enqueue the following steps=] to |registration|'s [=entry edit queue=]: 417 | 1. [=Undisplay=] |contentIndexEntries|[|id|]. 418 | 1. [=map/Remove=] |contentIndexEntries|[|id|]. 419 | 1. [=Resolve=] |promise| with undefined. 420 |
421 | 422 | ### {{ContentIndex/getAll()}} ### {#content-index-getall} 423 | 424 |
425 | The getAll() method, when invoked, must return [=a new promise=] |promise| and run these 426 | steps [=in parallel=]: 427 | 428 | 1. Let |registration| be the [=context object=]'s [=ContentIndex/service worker registration=]. 429 | 1. Let |contentIndexEntries| be |registration|'s [=content index entries=]. 430 | 1. Let |descriptions| be an empty [=/list=]. 431 | 1. [=Enqueue the following steps=] to |registration|'s [=entry edit queue=]: 432 | 1. [=map/For each=] id → |entry| of |contentIndexEntries|: 433 | 1. [=list/Append=] |entry|'s [=content index entry/description=] to |descriptions|. 434 | 1. [=Resolve=] |promise| with |descriptions|. 435 |
436 | 437 |
438 | 439 | 440 | 441 | ## {{ContentIndexEvent}} ## {#content-index-event} 442 | 443 | 454 | -------------------------------------------------------------------------------- /w3c.json: -------------------------------------------------------------------------------- 1 | { 2 | "group": [80485] 3 | , "contacts": ["yoavweiss"] 4 | , "repo-type": "cg-report" 5 | } 6 | --------------------------------------------------------------------------------