diff --git a/.github/ISSUE_TEMPLATE/acquisition-info.md b/.github/ISSUE_TEMPLATE/acquisition-info.md deleted file mode 100644 index 806774faa..000000000 --- a/.github/ISSUE_TEMPLATE/acquisition-info.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Acquisition Info -about: new issue -title: "[Acquisition Info] " -labels: Acquisition Info -assignees: kyerebo - ---- - - diff --git a/.github/ISSUE_TEMPLATE/allow-use-to-reference-entire-files.md b/.github/ISSUE_TEMPLATE/allow-use-to-reference-entire-files.md deleted file mode 100644 index fcc4b3804..000000000 --- a/.github/ISSUE_TEMPLATE/allow-use-to-reference-entire-files.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Allow use to reference entire files -about: new issue -title: "[SVG Use reference entire file] <TITLE HERE>" -labels: SVG Use reference entire file -assignees: goldenboy777 - ---- diff --git a/.github/ISSUE_TEMPLATE/aria-virtual-content.md b/.github/ISSUE_TEMPLATE/aria-virtual-content.md deleted file mode 100644 index 630038dc5..000000000 --- a/.github/ISSUE_TEMPLATE/aria-virtual-content.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: ARIA Virtual Content -about: new issue -title: "[ARIA Virtual Content] <TITLE HERE>" -labels: '' -assignees: kbabbitt - ---- - - diff --git a/.github/ISSUE_TEMPLATE/at-rule-feature-detection.md b/.github/ISSUE_TEMPLATE/at-rule-feature-detection.md deleted file mode 100644 index 162e55245..000000000 --- a/.github/ISSUE_TEMPLATE/at-rule-feature-detection.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: At-Rule Feature Detection -about: new issue -title: "[At-Rule Feature Detection] <TITLE HERE>" -labels: AtRuleFeatureDetection -assignees: kbabbitt - ---- - - diff --git a/.github/ISSUE_TEMPLATE/browser-binding-context-bpop.md b/.github/ISSUE_TEMPLATE/browser-binding-context-bpop.md deleted file mode 100644 index 3d98e11bd..000000000 --- a/.github/ISSUE_TEMPLATE/browser-binding-context-bpop.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Browser Binding Context - BPoP -about: new issue -title: "[Browser Binding Context - BPoP] <TITLE HERE>" -labels: Browser Binding Context BPoP -assignees: sameerag - ---- - - diff --git a/.github/ISSUE_TEMPLATE/call-stacks-in-crash-reports.md b/.github/ISSUE_TEMPLATE/call-stacks-in-crash-reports.md deleted file mode 100644 index 487fb9b98..000000000 --- a/.github/ISSUE_TEMPLATE/call-stacks-in-crash-reports.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Call Stacks In Crash Reports -about: new issue -title: "[Call Stacks In Crash Reports] <TITLE HERE>" -labels: Call Stacks In Crash Reports -assignees: issackjohn - ---- \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/calling-notifications.md b/.github/ISSUE_TEMPLATE/calling-notifications.md deleted file mode 100644 index f9a2b1098..000000000 --- a/.github/ISSUE_TEMPLATE/calling-notifications.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Calling Notifications -about: new issue -title: "[Calling Notifications] <TITLE HERE>" -labels: Calling Notifications -assignees: liminzhu - ---- - - diff --git a/.github/ISSUE_TEMPLATE/contextual-logging-with-console-context.md b/.github/ISSUE_TEMPLATE/contextual-logging-with-console-context.md deleted file mode 100644 index ae1ea83a2..000000000 --- a/.github/ISSUE_TEMPLATE/contextual-logging-with-console-context.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Contextual Logging With Console Context -about: new issue -title: "[Contextual Logging With Console Context] <TITLE HERE>" -labels: Contextual Logging With Console Context -assignees: leahmsft - ---- - - diff --git a/.github/ISSUE_TEMPLATE/css-gap-decorations.md b/.github/ISSUE_TEMPLATE/css-gap-decorations.md deleted file mode 100644 index d257fdb7b..000000000 --- a/.github/ISSUE_TEMPLATE/css-gap-decorations.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: CSS Gap Decorations -about: new issue -title: "[CSS Gap Decorations] <TITLE HERE>" -labels: CSSGapDecorations -assignees: kbabbitt - ---- - - diff --git a/.github/ISSUE_TEMPLATE/data-transfer-for-input-event.md b/.github/ISSUE_TEMPLATE/data-transfer-for-input-event.md deleted file mode 100644 index 90b1023f8..000000000 --- a/.github/ISSUE_TEMPLATE/data-transfer-for-input-event.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Data Transfer For Input Event -about: new issue -title: "[Data Transfer For Input Event] <TITLE HERE>" -labels: DataTransferForInputEvent -assignees: pranavmodi - ---- diff --git a/.github/ISSUE_TEMPLATE/declarative-shadow-dom-style-sharing.md b/.github/ISSUE_TEMPLATE/declarative-shadow-dom-style-sharing.md deleted file mode 100644 index c380a6313..000000000 --- a/.github/ISSUE_TEMPLATE/declarative-shadow-dom-style-sharing.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Declarative Shadow DOM Style Sharing -about: new issue -title: "[Declarative Shadow DOM Style Sharing] <TITLE HERE>" -labels: DeclarativeShadowDOMStyleSharing -assignees: kurtcattischmidt - ---- diff --git a/.github/ISSUE_TEMPLATE/delayed-clipboard-rendering.md b/.github/ISSUE_TEMPLATE/delayed-clipboard-rendering.md deleted file mode 100644 index 395d445e9..000000000 --- a/.github/ISSUE_TEMPLATE/delayed-clipboard-rendering.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Delayed Clipboard Rendering -about: new issue -title: "[Delayed Clipboard Rendering] <TITLE HERE>" -labels: Delayed Clipboard Rendering -assignees: anaskim - ---- - - diff --git a/.github/ISSUE_TEMPLATE/delayed-messages..md b/.github/ISSUE_TEMPLATE/delayed-messages..md deleted file mode 100644 index 1b16bebe7..000000000 --- a/.github/ISSUE_TEMPLATE/delayed-messages..md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Delayed Messages -about: new issue -title: "[Delayed Messages] <TITLE HERE>" -labels: DelayedMessages -assignees: joone - ---- - - diff --git a/.github/ISSUE_TEMPLATE/digital-goods.md b/.github/ISSUE_TEMPLATE/digital-goods.md deleted file mode 100644 index c7b859b1c..000000000 --- a/.github/ISSUE_TEMPLATE/digital-goods.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: Digital Goods -about: Issues for Digital Goods -title: "[Digital Goods] <TITLE HERE>" -labels: Digital Goods API - ---- diff --git a/.github/ISSUE_TEMPLATE/document-subtitle.md b/.github/ISSUE_TEMPLATE/document-subtitle.md deleted file mode 100644 index d9d99c065..000000000 --- a/.github/ISSUE_TEMPLATE/document-subtitle.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Document Subtitle -about: new issue -title: "[Document Subtitle] <TITLE HERE>" -labels: 'DocumentSubtitle' -assignees: diekus - ---- - - diff --git a/.github/ISSUE_TEMPLATE/download-url.md b/.github/ISSUE_TEMPLATE/download-url.md deleted file mode 100644 index c65e752b5..000000000 --- a/.github/ISSUE_TEMPLATE/download-url.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: DownloadURL -about: new issue -title: "[DownloadURL] <TITLE HERE>" -labels: DownloadURL -assignees: joone - ---- - - diff --git a/.github/ISSUE_TEMPLATE/draggable-app-region.md b/.github/ISSUE_TEMPLATE/draggable-app-region.md deleted file mode 100644 index b130a3fb7..000000000 --- a/.github/ISSUE_TEMPLATE/draggable-app-region.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: Draggable App Region -about: new issue -title: "[CSS Draggable] <TITLE HERE>" -labels: Draggable App Region -assignees: diekus ---- \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/gamepad-event-driven-input-api.md b/.github/ISSUE_TEMPLATE/gamepad-event-driven-input-api.md deleted file mode 100644 index 534767ae5..000000000 --- a/.github/ISSUE_TEMPLATE/gamepad-event-driven-input-api.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: "Gamepad Event-Driven Input API" -about: new issue -title: "[Gamepad Event-Driven Input API] <TITLE HERE>" -labels: "Gamepad Event-Driven Input API" -assignees: stevebe, gabrielbrito, snehagarwal - ---- \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/getSelectionBoundingClientRect.md b/.github/ISSUE_TEMPLATE/getSelectionBoundingClientRect.md deleted file mode 100644 index 0e697f2bc..000000000 --- a/.github/ISSUE_TEMPLATE/getSelectionBoundingClientRect.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: getSelectionBoundingClientRect() -about: new issue -title: "[getSelectionBoundingClientRect()] <TITLE HERE>" -labels: GetSelectionBoundingClientRect -assignees: anaskim ---- \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/hapticsdevice-api.md b/.github/ISSUE_TEMPLATE/hapticsdevice-api.md deleted file mode 100644 index b2ea7349f..000000000 --- a/.github/ISSUE_TEMPLATE/hapticsdevice-api.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: HapticsDevice API -about: Describe this issue template's purpose here. -title: "[HapticsDevice] Issue" -labels: 'WebHaptics' -assignees: bmathwig - ---- - - diff --git a/.github/ISSUE_TEMPLATE/highlight-events.md b/.github/ISSUE_TEMPLATE/highlight-events.md deleted file mode 100644 index 09ed45a14..000000000 --- a/.github/ISSUE_TEMPLATE/highlight-events.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Highlight Events -about: new issue -title: "[Highlight Events] <TITLE HERE>" -labels: HighlightEvents -assignees: ffiori - ---- - - diff --git a/.github/ISSUE_TEMPLATE/highlightsfrompoint.md b/.github/ISSUE_TEMPLATE/highlightsfrompoint.md deleted file mode 100644 index aa1f17ad3..000000000 --- a/.github/ISSUE_TEMPLATE/highlightsfrompoint.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: highlightsFromPoint API -about: new issue -title: "[highlightsFromPoint] <TITLE HERE>" -labels: highlightsFromPoint, HighlightEvents -assignees: ffiori - ---- - - diff --git a/.github/ISSUE_TEMPLATE/imageresource-color-scheme.md b/.github/ISSUE_TEMPLATE/imageresource-color-scheme.md deleted file mode 100644 index 0a5090b38..000000000 --- a/.github/ISSUE_TEMPLATE/imageresource-color-scheme.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: ImageResource Color Scheme -about: new issue -title: "[ImageResource Color Scheme] <TITLE HERE>" -labels: ImageResource Color Scheme -assignees: aarongustafson - ---- - - diff --git a/.github/ISSUE_TEMPLATE/local-folder-access.md b/.github/ISSUE_TEMPLATE/local-folder-access.md deleted file mode 100644 index e4518c7bc..000000000 --- a/.github/ISSUE_TEMPLATE/local-folder-access.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: LocalFolder Access -about: new issue -title: "[LocalFolder Access] <TITLE HERE>" -labels: LocalFolder Access -assignees: luhuangMSFT - ---- - - diff --git a/.github/ISSUE_TEMPLATE/materials.md b/.github/ISSUE_TEMPLATE/materials.md deleted file mode 100644 index ae4a06ae0..000000000 --- a/.github/ISSUE_TEMPLATE/materials.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Materials for Web Apps -about: new issue -title: "[Materials] <TITLE HERE>" -labels: Materials -assignees: diekus - ---- - - diff --git a/.github/ISSUE_TEMPLATE/network-efficiency-guardrails.md b/.github/ISSUE_TEMPLATE/network-efficiency-guardrails.md deleted file mode 100644 index 6813993e9..000000000 --- a/.github/ISSUE_TEMPLATE/network-efficiency-guardrails.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Network Efficiency Guardrails -about: new issue -title: "[NetworkEfficiencyGuardrails] <TITLE HERE>" -labels: Network Efficiency Guardrails -assignees: lflores-ms, victorhuangwq - ---- diff --git a/.github/ISSUE_TEMPLATE/opaque-range.md b/.github/ISSUE_TEMPLATE/opaque-range.md deleted file mode 100644 index db1729c87..000000000 --- a/.github/ISSUE_TEMPLATE/opaque-range.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: OpaqueRange -about: new issue -title: "[OpaqueRange] <TITLE HERE>" -labels: OpaqueRange -assignees: t-andresre ---- \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/page-interaction-restriction-manager.md b/.github/ISSUE_TEMPLATE/page-interaction-restriction-manager.md deleted file mode 100644 index f56d831b7..000000000 --- a/.github/ISSUE_TEMPLATE/page-interaction-restriction-manager.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Page Interaction Restriction Manager -about: new issue -title: "[Page Interaction Restriction Manager] <TITLE HERE>" -labels: Page Interaction Restriction Manager -assignees: jineens - ---- diff --git a/.github/ISSUE_TEMPLATE/pen-action.md b/.github/ISSUE_TEMPLATE/pen-action.md deleted file mode 100644 index 4c99e6229..000000000 --- a/.github/ISSUE_TEMPLATE/pen-action.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: pen-action -about: new issue -title: "[pen-action] <TITLE HERE>" -labels: Pen Action -assignees: BoCupp-Microsoft - ---- - - diff --git a/.github/ISSUE_TEMPLATE/pen-events.md b/.github/ISSUE_TEMPLATE/pen-events.md deleted file mode 100644 index 98594a13e..000000000 --- a/.github/ISSUE_TEMPLATE/pen-events.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Pen Events -about: new issue -title: "[Pen Events] <TITLE HERE>" -labels: Pen Events -assignees: BoCupp-Microsoft - ---- - - diff --git a/.github/ISSUE_TEMPLATE/performance-control-of-embedded-content.md b/.github/ISSUE_TEMPLATE/performance-control-of-embedded-content.md deleted file mode 100644 index 31925390f..000000000 --- a/.github/ISSUE_TEMPLATE/performance-control-of-embedded-content.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Performance Control of Embedded Content -about: new issue -title: "[PerformanceControlOfEmbeddedContent] <TITLE HERE>" -labels: Performance Control of Embedded Content -assignees: lflores-ms, nishitha-burman - ---- - - diff --git a/.github/ISSUE_TEMPLATE/performance-timing-during-browser-start.md b/.github/ISSUE_TEMPLATE/performance-timing-during-browser-start.md deleted file mode 100644 index 2aeda408d..000000000 --- a/.github/ISSUE_TEMPLATE/performance-timing-during-browser-start.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Performance timing during browser start -about: new issue -title: "[Perf Timing during launch] <TITLE HERE>" -labels: '' -assignees: '' - ---- - - diff --git a/.github/ISSUE_TEMPLATE/performancenavigationtiming-protocol-launch-timing-information.md b/.github/ISSUE_TEMPLATE/performancenavigationtiming-protocol-launch-timing-information.md deleted file mode 100644 index 4f45c3d05..000000000 --- a/.github/ISSUE_TEMPLATE/performancenavigationtiming-protocol-launch-timing-information.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: PerformanceNavigationTiming Protocol Launch Timing Information -about: Issues for PerformanceNavigationTimingProtocol Launch Timing Information -title: '' -labels: 'PerformanceNavigationTimingProtocolLaunch' -assignees: atulkatti - ---- - - diff --git a/.github/ISSUE_TEMPLATE/platform-authentication-api.md b/.github/ISSUE_TEMPLATE/platform-authentication-api.md deleted file mode 100644 index e4d96ac91..000000000 --- a/.github/ISSUE_TEMPLATE/platform-authentication-api.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -name: Enterprise Platform Authentication Broker API -about: new issue -title: "[Platform Auth API] <TITLE HERE>" -labels: Enterprise Platform Authentication -assignees: coder-linping - ---- - diff --git a/.github/ISSUE_TEMPLATE/popup.md b/.github/ISSUE_TEMPLATE/popup.md deleted file mode 100644 index c6e0d8316..000000000 --- a/.github/ISSUE_TEMPLATE/popup.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Popup -about: new issue -title: "[Popup] <TITLE HERE>" -labels: '' -assignees: BoCupp-Microsoft - ---- - - diff --git a/.github/ISSUE_TEMPLATE/prompt-api.md b/.github/ISSUE_TEMPLATE/prompt-api.md deleted file mode 100644 index 6e1fad6a3..000000000 --- a/.github/ISSUE_TEMPLATE/prompt-api.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: "Prompt API" -about: "Report an issue about the Prompt API in Microsoft Edge" -title: "[Prompt API] <TITLE HERE>" -labels: "Prompt API" -assignees: sohchatt ---- -Thank you for reporting an issue with the Prompt API in Microsoft Edge! - -If you want to send feedback, questions, or discuss about your scenario for built-in AI instead, please [add a comment in issue #1012](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/1012). - -Before reporting an issue, please check the [Prompt API documentation](https://learn.microsoft.com/microsoft-edge/web-platform/prompt-api), which includes instructions about how to enable the API in Microsoft Edge. - -### What happened? - -Please describe the issue you encountered. Also tell us, what did you expect to happen? - -### Device performance class - -In Edge, go to `edge://on-device-internals/` and find the Device performance class value under Tools. - -### Is device capable? - -In Edge, go to `edge://on-device-internals/` and find the Device capable value under Model Status. - -### Edge channel and version - -The Microsoft Edge channel and version you are using. Go to `edge://version` for details. -For example: Edge Dev 138.0.3309.2. - -### Device information - -Please provide the following information about your device: Operating system, version, and architecture. -For example: Windows 11, version 24H2, x64 - -### GPU information - -Tell us the GPU information of your device. Go to `edge://gpu` and search for GL_RENDERER. diff --git a/.github/ISSUE_TEMPLATE/proofreader-api.md b/.github/ISSUE_TEMPLATE/proofreader-api.md deleted file mode 100644 index d8e8f08c9..000000000 --- a/.github/ISSUE_TEMPLATE/proofreader-api.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: "Proofreader API" -about: "Report an bug with the Proofreader API in Microsoft Edge" -title: "[Proofreader API] <TITLE HERE>" -labels: "Proofreader API" -assignees: sohchatt ---- -Thank you for reporting a bug with the Proofreader API in Microsoft Edge. - -If you want to share feedback, ask questions, or discuss about your use case intead of reporting a bug, please add a comment to [issue #1238 [Proofreader API] Feedback for the Proofreader API developer preview in Microsoft Edge](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/1238). - -Before reporting your bug, check the [Proofreader API documentation](https://learn.microsoft.com/microsoft-edge/web-platform/proofreader-api), which includes instructions about how to enable the API in Microsoft Edge. - -### Bug description - -Enter a description of the issue you encountered, and state what you expected to happen instead. - -### Device performance class - -Enter the performance class of your device. - -To find the device performance class: -1. In Microsoft Edge, go to `edge://on-device-internals`. -2. Select the **Tools** tab, on the left. -3. Note the **Device performance class** value, such as **Very High**. - -### Edge channel and version - -Enter the channel and version of Microsoft Edge you are using. - -To find the Edge channel and version: -1. In Microsoft Edge, go to `edge://version`. -2. Note the **Microsoft Edge** value, such as **146.0.3837.0 (Official build) canary (arm64)**. - -### Device information - -Provide the following information about your device: -* The operating system, for example Windows 11. -* The version of the operating system, for example 24H2. -* The architecture of the operating system, for example x64. diff --git a/.github/ISSUE_TEMPLATE/pwa-widgets.md b/.github/ISSUE_TEMPLATE/pwa-widgets.md deleted file mode 100644 index 9354f795d..000000000 --- a/.github/ISSUE_TEMPLATE/pwa-widgets.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: PWA Widgets -about: new issue -title: "[PWA Widgets] <TITLE HERE>" -labels: 'Widgets' -assignees: aarongustafson - ---- - - diff --git a/.github/ISSUE_TEMPLATE/ratings-and-reviews-prompt.md b/.github/ISSUE_TEMPLATE/ratings-and-reviews-prompt.md deleted file mode 100644 index 7afb9a8db..000000000 --- a/.github/ISSUE_TEMPLATE/ratings-and-reviews-prompt.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Ratings & Reviews Prompt -about: new issue -title: "[Ratings & Reviews Prompt] <TITLE HERE>" -labels: Ratings and Reviews -assignees: aarongustafson, diekus - ---- - - diff --git a/.github/ISSUE_TEMPLATE/resource-timing-content-encoding.md b/.github/ISSUE_TEMPLATE/resource-timing-content-encoding.md deleted file mode 100644 index fab0ce8af..000000000 --- a/.github/ISSUE_TEMPLATE/resource-timing-content-encoding.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Resource Timing Content Encoding -about: new issue -title: "[Resource Timing Content Encoding] <TITLE HERE>" -labels: Resource Timing Content Encoding -assignees: guohuideng2024 - ---- diff --git a/.github/ISSUE_TEMPLATE/resource-timing-initiator-info.md b/.github/ISSUE_TEMPLATE/resource-timing-initiator-info.md deleted file mode 100644 index 6ba3c2b5e..000000000 --- a/.github/ISSUE_TEMPLATE/resource-timing-initiator-info.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: Resource Timing Initiator Info -about: new issue -title: "[Resource Timing Initiator Info] <TITLE HERE>" -labels: Resource Timing Initiator Info -assignees: guohuideng2024 - ---- diff --git a/.github/ISSUE_TEMPLATE/search-providers-reset.md b/.github/ISSUE_TEMPLATE/search-providers-reset.md deleted file mode 100644 index 2ecd6980a..000000000 --- a/.github/ISSUE_TEMPLATE/search-providers-reset.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: Search Providers Reset -about: Issues for Search Providers Reset -title: "[Search Providers Reset] <TITLE HERE>" -labels: Search Providers Reset - ---- diff --git a/.github/ISSUE_TEMPLATE/selective-clipboard-format-read.md b/.github/ISSUE_TEMPLATE/selective-clipboard-format-read.md deleted file mode 100644 index 293c2c10f..000000000 --- a/.github/ISSUE_TEMPLATE/selective-clipboard-format-read.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Selective Clipboard Format Read -about: new issue -title: "[Selective Clipboard Format Read] <TITLE HERE>" -labels: SelectiveClipboardFormatRead -assignees: ragoulik - ---- - - diff --git a/.github/ISSUE_TEMPLATE/split-tab.md b/.github/ISSUE_TEMPLATE/split-tab.md deleted file mode 100644 index 97fb8e7ce..000000000 --- a/.github/ISSUE_TEMPLATE/split-tab.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Split Tab -about: new issue -title: "[Split Tab] <TITLE HERE>" -labels: Split Tab -assignees: xuzhengyi1995 - ---- - - diff --git a/.github/ISSUE_TEMPLATE/systray-icon.md b/.github/ISSUE_TEMPLATE/systray-icon.md deleted file mode 100644 index e0988d3a4..000000000 --- a/.github/ISSUE_TEMPLATE/systray-icon.md +++ /dev/null @@ -1,8 +0,0 @@ ---- -name: System Status Icon -about: new issue -title: "[System Status Icon] <TITLE HERE>" -labels: 'System Status Icon' -assignees: diekus - ---- diff --git a/.github/ISSUE_TEMPLATE/tooltip-pseudo.md b/.github/ISSUE_TEMPLATE/tooltip-pseudo.md deleted file mode 100644 index e87203bb8..000000000 --- a/.github/ISSUE_TEMPLATE/tooltip-pseudo.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: CSS Tooltip Pseudo Element -about: new issue -title: "[CSS ::tooltip] <TITLE HERE>" -labels: TooltipPseudo -assignees: alisonmaher ---- \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/translator-api.md b/.github/ISSUE_TEMPLATE/translator-api.md deleted file mode 100644 index be21b18a0..000000000 --- a/.github/ISSUE_TEMPLATE/translator-api.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -name: "Translator API" -about: "Report an issue about the Translator API in Microsoft Edge" -title: "[Translator API] <TITLE HERE>" -labels: "Translator API" -assignees: sohchatt ---- -Thank you for reporting an issue with the Translator API in Microsoft Edge! - -If you want to send feedback, questions, or discuss about your scenario for built-in translation instead, please [add a comment in issue #1157](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/1157). - -Before reporting an issue, please check the [Translator API documentation](https://learn.microsoft.com/microsoft-edge/web-platform/translator-api), which includes instructions about how to enable the API in Microsoft Edge. - -### What happened? - -Please describe the issue you encountered. Also tell us, what did you expect to happen? - -### Device performance class - -In Edge, go to `edge://on-device-internals/` and find the Device performance class value under Tools. - -### Is translation model downloaded? - -In Edge, go to `edge://on-device-translation-internals` and ensure that language packs for your source and target languages have been installed. - -### Edge channel and version - -The Microsoft Edge channel and version you are using. Go to `edge://version` for details. -For example: Edge Dev 142.0.3553.0. - -### Device information - -Please provide the following information about your device: Operating system, version, and architecture. -For example: Windows 11, version 24H2, x64 diff --git a/.github/ISSUE_TEMPLATE/trust-token-issuer-redemption-statistics.md b/.github/ISSUE_TEMPLATE/trust-token-issuer-redemption-statistics.md deleted file mode 100644 index 3b972155c..000000000 --- a/.github/ISSUE_TEMPLATE/trust-token-issuer-redemption-statistics.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Trust Token Issuer Redemption Statistics -about: new issue -title: "[Trust Token Issuer Redemption Statistics] <TITLE HERE>" -labels: Trust Token Issuer Redemption Statistics -assignees: Brandr0id, erik-anderson - ---- - - diff --git a/.github/ISSUE_TEMPLATE/url-protocol-handler-registration.md b/.github/ISSUE_TEMPLATE/url-protocol-handler-registration.md deleted file mode 100644 index f435f3831..000000000 --- a/.github/ISSUE_TEMPLATE/url-protocol-handler-registration.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: URL Protocol Handler Registration -about: new issue -title: "[URL Protocol Handler Registration] <TITLE HERE>" -labels: URLProtocolHandler -assignees: ericlaw1979, fabiorocha, joselea - ---- - - diff --git a/.github/ISSUE_TEMPLATE/web-haptics b/.github/ISSUE_TEMPLATE/web-haptics deleted file mode 100644 index 75bbb9a9f..000000000 --- a/.github/ISSUE_TEMPLATE/web-haptics +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: Web Haptics -about: new issue -title: "[Web Haptics] <TITLE HERE>" -labels: Web Haptics -assignees: liminzhu ---- \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/web-install-api.md b/.github/ISSUE_TEMPLATE/web-install-api.md deleted file mode 100644 index 6e2a057a9..000000000 --- a/.github/ISSUE_TEMPLATE/web-install-api.md +++ /dev/null @@ -1,10 +0,0 @@ ---- -name: Web Install API -about: new issue -title: "[Web Install] <TITLE HERE>" -labels: Web Install API -assignees: diekus - ---- - - diff --git a/.github/ISSUE_TEMPLATE/writing-assistance-api.md b/.github/ISSUE_TEMPLATE/writing-assistance-api.md deleted file mode 100644 index a790882b1..000000000 --- a/.github/ISSUE_TEMPLATE/writing-assistance-api.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: "Writing assistance APIs" -about: "Report an issue about the Writing Assistance APIs in Microsoft Edge" -title: "[Writing Assistance APIs] <TITLE HERE>" -labels: "Writing Assistance APIs" -assignees: sohchatt ---- -Thank you for reporting an issue with the Writing Assistance APIs in Microsoft Edge! - -If you want to send feedback, questions, or discuss about your scenario for built-in AI instead, please [add a comment in issue #1031](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/1031). - -Before reporting an issue, please check the [Writing Assistance APIs documentation](https://learn.microsoft.com/microsoft-edge/web-platform/writing-assistance-apis), which includes instructions about how to enable the API in Microsoft Edge. - -### What happened? - -Please describe the issue you encountered. Also tell us, what did you expect to happen? - -### Device performance class - -In Edge, go to `edge://on-device-internals/` and find the Device performance class value under Tools. - -### Is device capable? - -In Edge, go to `edge://on-device-internals/` and find the Device capable value under Model Status. - -### Edge channel and version - -The Microsoft Edge channel and version you are using. Go to `edge://version` for details. -For example: Edge Dev 138.0.3309.2. - -### Device information - -Please provide the following information about your device: Operating system, version, and architecture. -For example: Windows 11, version 24H2, x64 - -### GPU information - -Tell us the GPU information of your device. Go to `edge://gpu` and search for GL_RENDERER. diff --git a/.gitignore b/.gitignore deleted file mode 100644 index e6621d74a..000000000 --- a/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -.vscode/settings.json -.DS_Store \ No newline at end of file diff --git a/Accessibility/AriaNotify/explainer.md b/Accessibility/AriaNotify/explainer.md deleted file mode 100644 index f6feb6745..000000000 --- a/Accessibility/AriaNotify/explainer.md +++ /dev/null @@ -1,545 +0,0 @@ -# ARIA Notify - -Authors: [Doug Geoffray](), [Alison Maher](), [Sara Tang](https://github.com/sartang), [Travis Leithead](https://github.com/travisleithead), [Daniel Libby](https://github.com/dlibby-), [Andy Luhrs](https://github.com/aluhrs13) - -## Introduction - -### Abstract -For people who are blind or have low vision, identifying dynamic changes (non-user-initiated) in the content of a web -app is very challenging. ARIA live regions are the only mechanism available today that communicate content changes down -to the accessibility layer so that users can hear about them. ARIA live regions are inconsistently implemented, have -poor developer ergonomics, and are being used in ways that they weren't designed for (e.g., as a confirmation of action -or notification-like API for changes unrelated to "live regions"). We propose an imperative notification API designed to -replace the usage of ARIA live regions in scenarios where a visual "live region" isn't necessary. - -### Background -Screen readers provide an audible presentation of web content for various kinds of users with disabilities (e.g., those -with limited or no vision). The screen reader knows what to say based on the semantic structure of a document. Screen -readers move through the content much the same way a sighted user might scan through the document with their eyes. When -something about the document changes (above the fold), sighted users are quick to notice the change. When something -below the fold (offscreen) changes, sighted users have no way of knowing that there was a change nor how important a -change it might be. This latter case is the conundrum for non-sighted users in general: how and when should changes in -the content be brought to their attention? - -Screen readers and content authors work together to try and solve this problem. One way screen readers are informed -about content changes is through ARIA live regions. A live region is an element (and its children) that is expected to -change dynamically (such as a message chat), and for which the changes should be announced to the user. - -The design of live regions is intended to give maximum flexibility to screen readers to implement an experience that is -best for their users. Web authors provide hints via attributes on the live region element in order to influence the -spoken output, such as: - - `aria-atomic`: should the whole text content of the element be notified or just the changes since the last update? - - `aria-relevant`: which content changes are relevant for the notification? Additions or removals (or both)? - - `aria-busy`: signals that a batch of changes are coming and to wait until the batch is complete before notifying. - - `aria-live`: a general signal of the priority of the region's changes: "assertive" or "polite". - -### Problems with Consistency and Predictability -Content authors have a difficult time creating consistent and predictable notification experiences for their users with -accessibility needs even with the above-mentioned controls. One of the reasons is due to the variation in screen reader -implementation approaches. In other cases, the inner workings of a browser's accessibility tree are the source of the -problem. Some examples: - 1. Screen reader output varies greatly depending on the complexity of the live region's content (e.g., elements and - nested elements). To get consistency, content authors will strip-out all the richness (and semantics) of the HTML - content in the live region, leaving only text in hopes of getting a more uniform experience. - 2. When content authors update the DOM in a live region, those changes may or may not get sent by the browser to a - screen reader. [In one - case](https://docs.google.com/document/d/1NaQS90h_LPD1YduCk2Gj4i5GMycjnksbQIpVJ67ooCA/edit?resourcekey=0-_z0yTNYZkPteppA1UGGNPw#heading=h.opld0djiwaju), - it was discovered that the browser's implementation was not properly detecting changes to the live region. - 3. The available priority controls (`assertive` vs. `polite`) are not well specified and are up to the interpretation - of screen readers. In one instance, an author wanted to make a live region announcement immediately following a user - action to supplement it with related context. However, the `polite` setting was too polite; a subsequent focus change - would always mute the announcement. The `assertive` setting was too assertive and caused subsequent (important) focus - change context to be lost while the assertive announcement was made. - -Content authors still rely on live regions because that is the only tool available for the job. They do the best that -they can, resorting to ugly "hacks", fragile coding patterns, and blatant misuse of ARIA live regions. There is a better -way. - -### Additional Concerns - - Live regions are built around the assumption that a visual change needs to be announced, hence they are tightly - coupled with DOM nodes. Many important changes are not necessarily tied to a visual action. In these cases, "live - region hacks" are employed using offscreen DOM nodes to host the live region -- there is no surrounding context (an - important consideration for many screen readers), nor any area to focus (for low-vision users). - - Offscreen live regions (see above) do not play a visual role in the content's presentation and as a result are - subject to second-class treatment. They can be forgotten about during content updates, accidentally broken due to - missing testing, or simply relegated to an "accessible version" of the site, usually because of performance overhead - concerns. Accessibility should be designed into the content experience from the start, not bolted-on as an extra or - add-on feature. - -## Use Cases -### Keyboard action confirmation -Keyboard commands (especially those without a corresponding UI affordance) when activated may need to confirm the -associated state change with the user. The following cases are variations on this theme: - 1. **Glow text command**: User is editing text, highlights a word and presses Shift+Alt+Y which makes it glow blue. No - UI elements were triggered or changed state, but the user should hear some confirmation that the action was successful, - such as "selected text glowing blue". - 2. **Set Presence**: In a chat application, the user presses Shift+Alt+4 to toggle their presence state to "do not - disturb". The application responds with "presence set to do not disturb". Overall priority: The user presses Shift+Alt+4, then immediately issues a command to the screen reader to jump to - the next header. The response "presence set to do not disturb" may be deferred or pre-empted - by the announcement of the focus change event depending on the content author's design. - -### Failed or delayed actions -According to common screen reader etiquette, user actions where the context is clear are assumed to be successful by -virtue of issuing the command to do the action itself (no specific confirmation of the action is needed); however, if -the action fails, is delayed, or no focus or state changes are generated, the user should then be notified. Otherwise, -the user's understanding about the state of the app could be wrong. - 1. **Longer than usual**: User composes an email and presses send. In normal circumstances, the message is sent, and a - focus change occurs (no confirmation of "sent" is needed). However, due to networking conditions, the send action is - taking longer than usual. The user hears "message is taking longer than usual to send". - 2. **Fail to paste**: User thought they had copied some text onto the clipboard, but in the context of editing, when - they issue the "paste" keyboard shortcut, nothing is in the clipboard. As a result, nothing pastes into the app. The - app triggers the screen reader to announce the failed action: "nothing to paste". - -### Secondary actions -In addition to a primary (implicit) action, some actions have secondary or follow-up effects that should be announced -beyond the immediate effect of the primary action. - 1. **Auto fill**: In a spreadsheet, an action that sets a cell's value may be assumed to happen (no announcement) or - could be announced as a side-effect of changing the cell's value (e.g., using a live region). In either case, this - would be the normal expectation for the user. However, as a result of setting the value, the spreadsheet takes a - secondary action of auto-filling a range of corresponding values in the cell's column. The screen reader links the - announcement "autofilled values in cells A2 through A20" to the user's last action and ensures they are correlated. - -### Goals - - Offer an alternative to "offscreen live region" scenarios that: - - serves content authors' needs first; has easy-to-use developer ergonomics - - solves the consistency and predictability problems of live regions - - Provide a design framework for improvements to live regions as a "declarative version" of the notification API. - - removes guesswork on what to speak from a DOM node by providing an exact string - -## Proposed Solution -A new API, `ariaNotify`, enables content authors to directly tell a screen reader what to read. The behavior would be -similar to an ARIA live region, but without the guesswork and previously described inconsistencies in processing. In the -simplest scenario, the content author calls `ariaNotify` with a string to read. The language of the string is assumed -to match the document's language. The function can be called from the document or from an element. When called from an -element, the element's nearest ancestor's lang attribute is used to infer the language. - -`ariaNotify` is an asynchronous API. There is no guarantee that a screen reader will read the text at that moment, nor -is there a way to know that a screen reader is available at all! Well-designed web applications will use `ariaNotify` to -provide appropriate notifications for accessibility whether or not their users require a screen reader or not. - -#### Example 1 -```js -// Dispatch a message associated with the document: -document.ariaNotify( "John Doe is connected" ); - -// Dispatch a message associated with an element: -document.querySelector("#richEditRegion1") - .ariaNotify( "Selected text glowing blue" ); -``` - -`ariaNotify` does not return a value. The call to the API has no web-observable side effects, and its use should not -infer that the user is using assistive technology. - -The above code immediately dispatches the first notification to the platform API with designation to the document node. -The second notification then follows with designation to the `#richEditRegion1` node element. It can be assumed that the -platform API will dispatch the notifications in the order received to any listening assistive technology, i.e. screen -reader. - -A screen reader must not only manage notifications from `ariaNotify`, but it also must manage all of the messages from -other sources, such as the OS, other applications, input keystrokes from the user, focus changes, ARIA live region -updates, etc. This explainer does not specify nor constrain the screen reader regarding the ordering of `ariaNotify` -notifications with respect to these other messages that exist in some total order of the screen reader's message queue. - -### Managing pending notifications -Given that each call to `ariaNotify` will immediately dispatch the message to the platform notification API, and the -platform notification API will immediately dispatch to all registered listeners (i.e. screen readers), the screen -reader will effectively prioritize and queue up the notifications, as it may not be able to fully dispatch (i.e. -speak/Braille) the current notification before a new notification arrives. Each screen reader is responsible for -managing the prioritization and queuing of the notifications, along with all other system notifications, etc. `ariaNotify` will also support communicating priority information (i.e. place the notification ahead or behind pending notifications) using the `priority` property to ensure the notification is placed ahead of lesser priority -notifications. - -`priority` indicates where the screen reader should add the notification in relationship to any existing pending -notifications: - - `high` - - Screen reader should add this string to the end of any other pending high priority notifications but before all - normal priority pending notifications - - `normal` - (default) - - Screen reader should add this string to the end of all pending notifications. - -#### Example 2 -```js -// Dispatch a notification updating background task status -- normal/low priority -document.ariaNotify( "Background task completed", - { "priority":"normal" }); - -// Dispatch a high priority notification that data may be lost -document.ariaNotify("Unable to save changes, lost connection to server", - { "priority":"high" }); -``` - -Assuming the initial normal priority string hasn't already started to be acted upon (spoken/brailled), the high priority -item is guaranteed to be placed ahead of the lower priority and will be processed first, followed by the lower priority -notification. This ensures that important messages that the user should be aware of are processed and are supplied to -the user first. - -## iframes and use in subresources -As iframes and other embedded content comes from external sources, web authors of the top-level context will not be -permitted to add notifications within the embedded content. - -On the other hand, the web authors of the iframe will be able to add notifications to their content. We will create a new `Permissions-Policy` called `aria-notify`. -By default, `aria-notify` is allowed in iframes. Authors can opt iframes out of aria-notify by: - - `<iframe allow="aria-notify 'none'">` - - `Permissions-Policy: aria-notify=(self)` - -## Relationship to ARIA Live Regions -There are some similarities between `ariaNotify` and the existing ARIA live regions. Some functionality provided by `ariaNotify` is not supported and cannot be mapped back directly to ARIA live regions. - -In the case of operating systems that do not yet support `ariaNotify`, we propose the following fallback mechanism using the same -backend as the existing ARIA live regions: - - The message payload for `ariaNotify` is equivalent to the contents of an ARIA live region. - - `"priority: high"` corresponds to ARIA live's `aria-live="assertive"` - - `"priority: normal"` corresponds to ARIA live's `aria-live="polite"` - -## Open Issues - -### Spamming mitigations -The general nature of a notification API means that authors could use it for scenarios that are already handled by -screen readers (such as for focus-change actions) resulting in confusing double-announcements (in the worst case) or -extra unwanted verbosity (in the best case). - -Note: screen readers will tune their behavior for the best customer experiences. Screen readers already add custom logic -for handling app-and-site-specific scenarios and are keen to extend that value to websites that make use of -`ariaNotify`. For this reason, known & popular sites that abuse `ariaNotify` can be mitigated at the screen reader level -without requiring particular mitigations in browsers. This does not preclude mitigation strategies that UAs may to -include. - -Finally, malicious attackers can use the API as a Denial-of-Service against AT users. - -Opportunities exist to mitigate against these possibilities: - - Make use of [User Activation](https://html.spec.whatwg.org/multipage/interaction.html#tracking-user-activation) - primitives to limit usage of this API to only actions taken by the user. - -### `interrupt` -A previous version of this explainer included an `interrupt` property (now described below). It seems unlikely that any implementers will include this property in their initial implementation, but including it in the initial spec would allow app developers to plan ahead when they initially start using `ariaNotify` and the behavior will be progressively enhanced when `interrupt` is implemented. - -## Future considerations -`ariaNotify` can be extended in the future to handle more functionality as needs arise. We've discussed a few of these in-depth and have included them below. - -### Braille and speech markup -There may be a need for a web author to supply a Braille specific string separate from the speech string. For example, an author could supply "3 stars" as the speech string to indicate a retail item's rating. However, to better map within a Braille display, the author could supply `***` as a Braille alternative string. The API could easily be extended by adding another optional property for Braille strings. For example: - -```js -document.ariaNotify( "3 stars", {"braille":"***"} ); -``` - -There may also be a use case where an author would want to allow the speech string to be marked up to guarantee a -specific pronunciation. This can be useful in cases where the speech engine may not produce the best experience for the -user. - -For example, maybe you would like "911" pronounce as "9 1 1" in some cases. Or in a spreadsheet, you may want to hear "a 1" spoken with a long "a" sound instead of a short "a" sound (i.e. "ay 1" as opposed to "uh 1"). The API could easily be extended by adding another property for strings marked up with, say, SSML: - -```js -document.ariaNotify( "911", {"SSML":"<say-as -interpret-as=\x22\"""digits\x22""">911" }); -``` - -Related issues: -- [w3c/aria #2334](https://github.com/w3c/aria/issues/2334) -- [w3c/aria #2335](https://github.com/w3c/aria/issues/2335) - -### Interruption -#### Use Case -Looking at our set presence example above where the user pressed Shift+Alt+4 to toggle their presence state to "do not - disturb". The application responds with "presence set to do not disturb". With configurable interruption, the application could handle more complex scenarios: - - Most recent notification priority: The user presses Shift+Alt+3 by mistake, and then quickly presses Shift+Alt+4. - The application began to respond with "presence..." [set to busy] but interrupts itself with the latest response - "presence set to do not disturb". - - Overall priority: The user presses Shift+Alt+4, then immediately issues a command to the screen reader to jump to - the next header. The response "presence set to do not disturb" may interrupted (in addition to deferred or pre-empted) - by the announcement of the focus change event depending on the content author's design. - -#### Proposal - `interrupt` -`ariaNotify` could also handle interruptibility implications (i.e. silence the currently speaking notification and/or flush pending notifications) via the `interrupt` property. `interrupt` indicates whether or not the screen reader should interrupt an existing notification from speaking and -whether or not it should remove any other pending notifications. Note that the functionality of `interrupt` is dependent -on the source, priority, and interrupt settings of the current and pending notifications. - - `none` - (default) - - Do not interrupt anything and do not flush any pending notifications. Simply add the notification to pending - notifications as per its priority. - - `all` - - Step 1: If a notification with the same source, priority, and interrupt (`all`) is speaking, immediately silence - that string. - - Step 2: If any pending notifications with the same source, priority, and interrupt (`all`) are being held, - remove/flush all of them. - - Step 3: Add the notification to pending notifications as per its priority. - - `pending` - - Step 1: If a notification with the same source, priority, and interrupt (`pending`) is speaking, allow the current - notification being spoken to fully complete. - - Step 2: If any pending notification with the same source, priority, and interrupt (`pending`) is being held, - remove/flush all of them. - - Step 3: Add the notification to pending notifications as per its priority. - -Related issue - [w3c/aria #2331](https://github.com/w3c/aria/issues/2331) - -#### Example 3 - `interrupt` -Here is a simple example showing three outcomes for the same scenario (a progress bar which reports its status at every percent increment): - -##### Example 3.1 -`interrupt:none` - Every progress bar percentage from 1% to 100% will be spoken. - -```js -let percent = 0; -function simulateProgress() { - percent += 1; - updateProgressBarVisual(percent); - // Report progress to ariaNotify. interrupt:none will cause each percent - // update to be fully spoken - document.querySelector("#progressBar") - .ariaNotify( "Progress is ${currentValue}", - { "priority":"normal", - "interrupt":"none" }); -} - -if (percent < 100) { - setTimeout(simulateProgress, 100); -} -``` - -##### Example 3.2 -`interrupt:all` - Each new progress bar percentage will interrupt/silence the currently speaking percentage, flush any -pending percentages, and add the latest. - -Because the percentage is likely updating before each percentage fully speaks, the user will either hear nothing or the -first part of each/some percentage until the last is processed where the user will hear the full string "Progress is -100". - -```js -let percent = 0; -function simulateProgress() { - percent += 1; - updateProgressBarVisual(percent); - // Report progress to ariaNotify. interrupt all will cause each percentage - // update to interrupt any existing percentage that may be speaking, flush any - // pending percentages, and add the latest - document.querySelector("#progressBar") - .ariaNotify( "Progress is ${currentValue}", - { "priority":"normal", - "interrupt":"all" }); - } - -if (percent < 100) { - setTimeout(simulateProgress, 100); -} -``` - -##### Example 3.3 -`interrupt:pending` -- Assuming the first percentage "Progress is 1" is processed and sent to the synthesizer before the -next percentage is sent, it will be completely spoken. Regardless, while any percentage is being spoken, that percentage -will be allowed to finish speaking, other pending percentages sent to `ariaNotify` will be thrown out, and the latest -percentage will be added. - -How long it takes to speak the current percentage will determine the number of subsequent percentages that will be -skipped. A slower speech rate will cause more percentages to be ignored. A faster speech rate will allow more -percentages to be spoken. When the current percentage fully speaks, the next percentage that was allowed to be held will -speak, and the process will repeat. Finally, the last percentage "Progress is 100" will be spoken. - -```js -let percent = 0; -function simulateProgress() { - percent += 1; - updateProgressBarVisual(percent); - // Report progress to ariaNotify. interrupt pending will allow the current - // percentage to finish speaking, flush any pending percentages, and add the - // latest - document.querySelector("#progressBar") - .ariaNotify( "Progress is ${currentValue}", - { "priority":"normal", - "interrupt":"pending" }); -} - -if (percent < 100) { - setTimeout(simulateProgress, 100); -} -``` -The only difference between the three snippets is the `interrupt` setting. Each of the three settings produces a big -experience difference for the user. - -##### Fallback -In the case of operating systems that do not yet support `ariaNotify`, ARIA live regions do not support interruptibility, so all behavior of `interrupt` defaults to `none`. - -The addition of `interrupt` would mean that there is no exact mapping of `ariaNotify` back to ARIA live regions, and our proposal reflects a best effort -to achieve similar behavior. There are cases where we will not be able to get the intended behavior using ARIA live -regions: - -```js -element.ariaNotify("This message is normal.", - { "priority": "normal", - "interrupt": "none"}); - -element.ariaNotify("This message should interrupt", - { "priority": "normal", "interrupt": "all" }); -``` - -In the above case, when `ariaNotify` is supported, the expected behavior would be for the second notification to silence -the current one and flush all other queued notifications from element with priority: `"normal"`. However, the fallback is -not able to silence or flush existing notifications, as that behavior is not supported in ARIA live regions. In the -case that the web browser does not yet support `ariaNotify`, it is the responsibility of the web author to detect and -fallback to ARIA live regions. The above conversion may serve as a guide on how to do so. One can detect whether or not -`ariaNotify` is supported by checking if the method exists on the document or element in question: - -```js -if ("ariaNotify" in element) { - element.ariaNotify(...); -} -``` - -### Screen reader customizations for user preference -#### Use Case -User is editing text using bold, italic, underline, etc.. By default, the - application responds with confirmations such as "bold on" / "bold off" as they toggle each state. As the application - sends the confirmation for the user's actions, it also attached a unique identifier indicating the string is a - confirmation for a basic editing command. Based on this identifier, the screen reader gives their users the following - choices: - - Speak and Braille the confirmation notice, as normal - - Speak but do not flash the confirmation in Braille - - Filter/suppress the entire confirmation from speech and Braille - - Replace speech with a quick confirmation tone - - Any other option the screen reader believes would be beneficial to their users - -#### Proposal - `type` -Screen readers offer the flexibility to customize the notification experience for their users. Customization options -for user preferences include disabling, prioritizing, filtering, and providing alternate output for notifications (such -as the concept of [earcons](https://en.wikipedia.org/wiki/Earcon)). Without additional context, only two customization -options can be offered: options that apply to all `ariaNotify` notifications universally or customization on a -per-notification-string basis. - -To aid in customization, `ariaNotify` provides a method to give context of the notification (`type`). This -explainer provides a set of potential suggestions but allows for arbitrary non-localized strings to be used by the -content author. All strings will be processed by the user agent according to a fixed algorithm ([ASCII -encode](https://infra.spec.whatwg.org/#ascii-encode), then [ASCII -lowercase](https://infra.spec.whatwg.org/#ascii-lowercase), and finally, [strip leading and trailing ASCII -whitespace](https://infra.spec.whatwg.org/#strip-leading-and-trailing-ascii-whitespace)) before the notification is sent -to the platform API (invalid strings will throw an exception). - -When no `type` is explicitly provided by the content author, the `type` is set to `notify` by -default. - -To specify a `type`, pass the string as the second parameter. Alternatively, the `type` may be -expressed in an object form with property `type`. For example: - -```js -// Notify of a long-running async task starting and ending -document.ariaNotify( - "Uploading file untitled-1 to the cloud.", - "task-progress-started" ); -// ... -myfile.asyncFileUpload().then( () => { - document.ariaNotify( "File untitled-1 uploaded.", { - type: "task-progress-finished" } ); -}); -``` - -Screen readers may allow their users to filter out these task-progress `type`, may make these notifications -only available at particular verbosity levels, or may replace the output strings with audio cues. - -#### Example 5 - `type` -```js -// User has initiated an action which starts a generation process of data. -// During the status of the generation, a more critical status needs to be -// sent to the user -document.querySelector("#dataStatus") - .ariaNotify( "generating content", - { "type":"statusUpdate" }); - -document.querySelector("#dataStatus") - .ariaNotify( "processing data ", - { "type":"statusUpdate" }); - -document.querySelector("#dataStatus") - .ariaNotify( "counting items ", - { "type":"statusUpdate" } ); - -document.ariaNotify( " server connection lost ", - { "priority":"high", - "type":"serverStatus" } ); -``` - -As content is being generated, the user is informed of that status. When something more serious occurs, such as losing server access, the server error is prioritized above any pending status updates. Screen readers can choose to offer settings for users to manage specific types, like supressing minor `statusUpdate` notifications entirely. - -#### Fallback -In the case of operating systems that do not yet support `ariaNotify`, `type` is dropped entirely when falling back to ARIA live regions. - -#### Open Issue - Predefined types -The use of `type` give the screen reader contextual information regarding the notification which allows for -creative approaches to dispatching the information to their users. The question then arises of whether the API should -create a predetermined set of `type` names for common/expected scenarios or whether having predefined names is -pointless given no matter the list, it will always fall short. - -Possible examples of predefined `type` could be something like: - - Recent action completion status: `action-completion-success`, `action-completion-warning`, - `action-completion-failure` - - Async/indeterminate task progress: `task-progress-started`, `task-progress-ongoing`, `task-progress-blocked`, - `task-progress-canceled`, `task-progress-finished` - - Navigational boundary endpoints: `boundary-beginning`, `boundary-middle`, `boundary-end` - - Value-relative state changes: `value-increase`, `value-decrease` - - User interface state: - - `ui-clickable / ui-clicked` - - `ui-enabled / ui-disabled` - - `ui-editable / ui-readonly` - - `ui-selected / ui-unselected` - - Related issue - [w3c/aria #2330](https://github.com/w3c/aria/issues/2330) - -## FAQ -**Is this API going to lead to privacy concerns for AT users?** - -No. This API has been designed to be "write-only," meaning that its use should have no other apparent observable -side-effects that could be used for fingerprinting. - -See Security and Privacy section for additional details. - -**Are Element-level notifications really necessary?** - -Adding `ariaNotify` to Elements was driven by several goals: - - Resolve the question of how language input should be provided. To keep the API simple, we are able to leverage the - lang attribute that is used to override the document language for specific subtrees. `ariaNotify` can use the nearest - ancestor element's lang attribute as a language hint (or the document's default language). - - Screen readers can filter/prioritize notifications based on the element associated with the notification queue. E.g., - the element's current visibility in the User Agent, the element's proximity to the focused element. (Same potential - options available with ARIA live regions today.) - -**Can this API allow for output verbosity preferences?** - -Screen reader users can customize the verbosity of the information (and context) that is read to them via settings. -Screen reader vendors can also adapt the screen reader on a per site or per app basis for the best experience of their -users. `ariaNotify` offers `type` as a mechanism to allow screen reader vendors or users to customize not only the -general use of `ariaNotify` on websites, but also individual notifications by `type` (or specific notification -string instances in the limit). - -**Tooling help** - -It's very difficult today to test that ARIA live regions are working and how they are working. Tooling, [such as the -work proposed here](https://docs.google.com/document/d/1ZRBC4VJwsb-dlLmcZJgYlz1qn7MmDwNKkyfbd8nbLEA/edit), should be -available for content authors to validate the behavior of both ARIA live regions and `ariaNotify`. - -## Alternate Solutions - The design of this API is loosely inspired by the [UIA Notification API](https://docs.microsoft.com/en-us/windows/win32/api/uiautomationcoreapi/nf-uiautomationcoreapi-uiaraisenotificationevent). - Previous discussions for a Notifications API in the AOM and ARIA groups: - - [Issue 3 - Use Case: Accessibility Notifications](https://github.com/wicg/aom/issues/3) - - [Issue 84 - Live region properties vs announcement notification](https://github.com/WICG/aom/issues/84) - - [Issue 832 - Do we need a notifications API in ARIA](https://github.com/w3c/aria/issues/832) - -## Privacy & Security Considerations - 1. **Timing.** The use of the API provides no return value, but parameters must still be processed. Implementations - should take care to avoid optimizing-out the synchronous aspects of processing this API, as predictable timing - differences from when the API is "working" (a screen reader is connected) vs. "not working" (no screen reader - connected) could still be used to infer the presence of a user with an active screen reader. - 2. **Readback.** Any readback of configuration settings for a screen reader via an API have the potential of exposing a - connected (vs. not connected) screen reader, and as such is an easy target for fingerprinting screen reader users, an - undesirable outcome. Similarly, confirmation of notifications (such as via a fulfilled promise) have similar traits and - are avoided in this proposal. - 3. **Authoritative-sounding notifications.** Announcements could be crafted to deceive the user into thinking they are - navigating trusted UI in the browser by arbitrarily reading out control areas/names that match a particular target - browser's trusted UI. - - Mitigations should be applied to suppress notifications when focus moves outside of the web content. - - Additional mitigations to block certain trusted phrases related to the browser's trusted UI could be considered. - - Implementations may choose to audibly differentiate notification phrases coming from `ariaNotify` in order to make it - clear that they are content author controlled. - 4. **Secure Context.** Does it make sense to offer this feature only to Secure Contexts? Should usage of this API be - automatically granted to 3rd party browsing contexts? Currently thinking "no" in order to have maximum possibility of - reach within the accessible community on all websites that should be made accessible, whether secure context-enabled or - not. - 5. **Data Limits** (See [Security and Privacy Questionnaire - #2.7](https://www.w3.org/TR/security-privacy-questionnaire/#send-to-platform)) Should there be a practical limit on the - amount of text that can be sent in one parameter to the API? Just like multiple-call DoS attacks, one call with an - enormous amount of text could tie up an AT or cause a hang as data is marshalled across boundaries. - diff --git a/Accessibility/CaretBrowsing/CB_Example.gif b/Accessibility/CaretBrowsing/CB_Example.gif deleted file mode 100644 index 745fc1a3f..000000000 Binary files a/Accessibility/CaretBrowsing/CB_Example.gif and /dev/null differ diff --git a/Accessibility/CaretBrowsing/designDoc.md b/Accessibility/CaretBrowsing/designDoc.md deleted file mode 100644 index 78406d2fe..000000000 --- a/Accessibility/CaretBrowsing/designDoc.md +++ /dev/null @@ -1,92 +0,0 @@ -# Native Caret Browsing Design Document - -## Contact emails - -Bruce.Long@microsoft.com, pcupp@microsoft.com, Grisha.Lyukshin@microsoft.com - -## Introduction - -This document provides an overview of the proposed changes in Chromium to support native caret browsing. For a high-level description of native caret browsing and the motivation for introducing the feature, see the associated [explainer doc](explainer.md). - - -## Changes in the Renderer Process - -### Making the caret visible in non-editable content - -Currently in Chromium, you can already place a caret in non-editable content by clicking in it, but the caret won't be rendered since it is at a non-editable position. Allowing the caret to be rendered at non-editable positions just requires a couple of changes to the [FrameCaret](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/frame_caret.h) class, where there is logic to determine if the caret should be rendered (in [UpdateStyleAndLayoutIfNeeded](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/frame_caret.cc?dr=CSs&q=FrameCaret::UpdateStyleAndLayoutIfNeeded&g=0&l=145)) or should blink (in [ShouldBlinkCaret](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/frame_caret.cc?q=FrameCaret::ShouldBlinkCaret&dr=CSs&l=202)). In both cases the change is to convert an "if editable" condition to an "if editable *or* caret browsing is enabled" condition. The [FrameSelection](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/frame_selection.h) class also needs a similar update in [ShouldPaintCaret](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/frame_selection.cc?q=+FrameSelection::ShouldPaintCaret&dr=CSs&g=0&l=519) to prevent an assertion failure. - -### Allowing the user to move the caret in non-editable content - -Chromium has a set of move commands that are used to move the caret in editable content when the user presses a key (e.g., an arrow key). All of the editing commands, including the move commands, have an "is_enabled" function associated with them that determines when the command is enabled. (See the [kEditorCommands](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/commands/editor_command.cc?q=kEditorCommands&dr=C&l=1291) array in editing_commands.cc for the associations.) For the move commands, the "is_enabled" function is currently [EnabledInEditableText](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/commands/editor_command.cc?type=cs&q=EnabledInEditableText&sq=package:chromium&g=0&l=1081), which only allows the command to execute when the active caret/selection is in editable content. To allow the user to move the caret through non-editable content, a new "is_enabled" function is added and associated with the commands that move the caret: EnabledInEditableTextOrCaretBrowsing. - -In addition, two "is_enabled" functions that return true when there is an active visible caret in editable content need to be updated to "if editable *or* caret browsing is enabled" since a caret can now be visible in non-editable content. These "is_enabled" functions are [EnabledVisibleSelection](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/commands/editor_command.cc?type=cs&q=EnabledVisibleSelection&sq=package:chromium&g=0&l=1035) and [EnabledVisibleSelectionAndMark](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/commands/editor_command.cc?type=cs&q=EnabledVisibleSelection&sq=package:chromium&g=0&l=1052), and they are used for commands related to modifying a selection (e.g., pressing Shift+ArrowRight to turn a caret into a selection). - -### Moving focus when the caret enters/exits focusable elements - -While caret browsing, if you move the caret into an interactive element such as an anchor element, that element needs to become focused so that the user can activate it with the keyboard (e.g., by pressing Enter). This is done by updating the "move" commands that handle moving the caret in response to user input. These commands are implemented by static methods in the [MoveCommands](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/commands/move_commands.h?q=MoveCommands&sq=package:chromium&dr=CSs&l=48) class. A new UpdateFocusForCaretBrowsing method is added to MoveCommands, and each move command that moves the caret is updated so that that method it called after the caret is moved. If the caret is at a non-editable position and caret browsing is enabled, new method UpdateFocusForCaretBrowsing moves focus to the nearest focusable ancestor of caret. If there is no focusable ancestor then the FocusedElement is set to null, which means that the body will be the active element by default. - -Note: A CORE_EXPORT macro is added to the MoveCommands class declaration so that MoveCommands functions can be called in unit tests. (The webkit_unit_tests binary does not statically link to the product code, so classes/functions that you want to directly reference in a unit test need to exported from whatever DLL they are in, from blink_core in this case.) - -### Moving the caret to the focused element (if not already there) - -If there is no caret in focus (e.g., because you haven't placed a caret, or because you moved focus away from the caret by tabbing) then pressing keys that would move the caret need to cause the caret to move to the active element so that you can start caret browsing from there. This is done by adding an UpdateSelectionForCaretBrowsing function to the MoveCommands class and having the commands that move the caret call that function before moving the caret. If there is no selection in focus then that method moves the caret to the first position in that element. (Note: If the element is one that cannot contain the caret, like an IMG element, then the caret is moved in front of the element.) - -### The "is caret browsing enabled" setting in the renderer - -The primary "is caret browsing enabled" preference/setting is owned by the browser process, but the renderer process has a copy of it that gets updated by the browser process whenever it changes. The renderer's copy is in the blink::Settings. The setting is defined in a JSON file ([settings.json5](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/frame/settings.json5?q=settings.json5&sq=package:chromium&dr)) and gets added to the Settings class through code generation. The setting is exposed outside of Blink through a [WebSettings](https://cs.chromium.org/chromium/src/third_party/blink/public/web/web_settings.h?q=WebSettings&sq=package:chromium&dr=CSs) interface that is implemented by a [WebSettingsImpl](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/exported/web_settings_impl.h?q=WebSettingsImpl&sq=package:chromium&dr=CSs) class. When the browser sends updated preference values to the renderer, the update happens in [RenderViewImpl::OnSetRendererPrefs](https://cs.chromium.org/chromium/src/content/renderer/render_view_impl.cc?type=cs&q=RenderViewImpl::OnSetRendererPrefs&sq=package:chromium&g=0&l=1935). All new browser tabs respect the current caret browsing setting. - - -## Changes in the Browser Process - -### Caret browsing preferences/settings in the browser - -There are two user preferences/settings for caret browsing: an "is caret browsing enabled" setting and a "show caret browsing dialog" setting. These are accessed in code through strings that are defined in the [prefs namespace](https://cs.chromium.org/chromium/src/chrome/common/pref_names.cc?q=pref_names.cc): kCaretBrowsingEnabled and kShowCaretBrowsingDialog. The initial values for these preferences are set in [RegisterBrowserUserPrefs](https://cs.chromium.org/chromium/src/chrome/browser/ui/browser_ui_prefs.cc?q=RegisterBrowserUserPrefs&dr=CSs&l=52). Both values are persisted in a Preferences file on the local file system. However, we do not want caret browsing to be enabled at the start of a new browser session, since if a user opts for "don't show the dialog again," they may not realize that they are in caret browsing mode and may not remember how to turn it off. As discussed in the previous section, when caret browsing mode is toggled, the state change needs to be broadcast to all renderer processes, as well as have the setting honored when a new renderer process is launched. It is easiest to do this using the existing preferences implementation, but on browser process launch ignore the setting that is persisted to permanent storage and start with caret browsing turned off. This is done by resetting the preference in [ProfileManager::DoFinalInit](https://cs.chromium.org/chromium/src/chrome/browser/profiles/profile_manager.cc?type=cs&q=ProfileManager::DoFinalInit&sq=package:chromium&g=0&l=1283). - -The kCaretBrowsingEnabled preference is registered with a [PrefChangeRegistrar](https://cs.chromium.org/chromium/src/components/prefs/pref_change_registrar.h?q=PrefChangeRegistrar&dr=CSs) in [PrefWatcher](https://cs.chromium.org/chromium/src/chrome/browser/ui/prefs/pref_watcher.h?dr=CSs&q=PrefWatch&g=0&l=20) so that whenever its value changes a callback is called ([PrefWatcher::UpdateRendererPreferences](https://cs.chromium.org/chromium/src/chrome/browser/ui/prefs/pref_watcher.cc?dr=CSs&g=0&l=105)) that will send the updated preference values to the renderer processes. The preferences are sent through a [RendererPreferences](https://cs.chromium.org/chromium/src/out/win-Debug/gen/third_party/blink/public/mojom/renderer_preferences.mojom.h?q=RendererPreferences&dr=CSs) object. There is a corresponding entry for caret_browsing_enabled under RendererPreferences in [common_param_traits_macros.h](https://cs.chromium.org/chromium/src/chrome/common/common_param_traits_macros.h?q=common_param_traits_macros.h&dr=CSs) that is needed for mojo. Before it is sent to the renderer, the RendererPreferences object gets populated with the user preference values in [UpdateFromSystemSettings](https://cs.chromium.org/chromium/src/chrome/browser/renderer_preferences_util.cc?dr=CSs&g=0&l=80). - -### The caret browsing dialog - -The confirmation dialog that is shown when you press F7 is implemented by a new CaretBrowsingDialogDelegate class. The class inherits from a [DialogDelegateView](https://cs.chromium.org/chromium/src/ui/views/window/dialog_delegate.h?sq=package:chromium&g=0&l=178) class and overrides the methods it needs in order to provide the correct strings and functionality. The strings for the dialog are defined in [generated_resources.grd](https://cs.chromium.org/chromium/src/chrome/app/generated_resources.grd?q=generated_resources.grd&dr). A ShowCaretBrowsingDialog method is added to the [BrowserWindow](https://cs.chromium.org/chromium/src/chrome/browser/ui/browser_window.h?type=cs&q=+BrowserWindow&g=0&l=100) interface, which is implemented by [BrowserView](https://cs.chromium.org/chromium/src/chrome/browser/ui/views/frame/browser_view.h?type=cs&q=BrowserView&g=0&l=99). This method calls a static method of CaretBrowsingDialogDelegate to show the dialog. (A stub implementation of ShowCaretBrowsingDialog is also added to [TestBrowserWindow](https://cs.chromium.org/chromium/src/chrome/test/base/test_browser_window.h?type=cs&q=TestBrowserWindow&g=0&l=36).) - -### The caret browsing experimental feature flag (chrome://flags) - -There is a "caret-browsing" experimental feature flag that determines if pressing F7 is ignored by the browser or if F7 starts the "toggle caret browsing mode" flow. - -### Mapping F7 to the "Toggle Caret Browsing" browser command - -A browser command is defined for toggling caret browsing mode in [chrome_command_ids.h](https://cs.chromium.org/chromium/src/chrome/app/chrome_command_ids.h?q=chrome_command_ids.h&dr=CSs), and the F7 key is mapped to this command in [chrome_dll.rc](https://cs.chromium.org/chromium/src/chrome/app/chrome_dll.rc?q=chrome_dll.rc&dr) and [accelerator_table.cc](https://cs.chromium.org/chromium/src/ash/accelerators/accelerator_table.cc?q=accelerator_table.cc&dr). When F7 is pressed, if the caret browsing feature flag is enabled, the [BrowserCommandController::ExecuteCommandWithDisposition method](https://cs.chromium.org/chromium/src/chrome/browser/ui/browser_command_controller.cc?type=cs&q=ExecuteCommandWithDisposition&g=0&l=296) will be called. The command controller calls the ToggleCaretBrowsingMode method (in [browser_commands.cc](https://cs.chromium.org/chromium/src/chrome/browser/ui/browser_commands.cc?q=browser_commands.cc&dr)), which will either launch the confirmation dialog or toggle the caret browsing mode directly, depending on the value of the "show caret browsing dialog" setting. - -## Caret movement behavior - -### Relation to existing caret and selection navigation -Caret movement for native caret browsing using the arrow keys follows the same model as caret movement in editable regions or the selection focus in editable or read-only regions of the document. It is a non-goal to derive any separate model for moving the caret that would lead to a disjoint experience when, for example, the user extends the caret into a selection by holding shift while pressing an arrow key. - -This movement model constrains caret movement within the same regions that establish natural boundaries for selection, i.e. the caret can move from position X to position Y if a selection can start at X and end at Y. This precludes moving into or out of controls, across regions with different content-editabilities, or across iframes. - -### Relation to tab navigation -Movement between selection boundaries established by controls and other elements that are intrinsically focusable is generally possible with tab navigation unless the author has explicitly declared these elements to not be tab stops. Moving focus also moves the caret into the focusable element, so in this way, tab navigation facilitates moving the caret into regions that are not otherwise accessible using caret browsing alone and is complementary to the caret browsing feature. - -### Relation to spatial navigation -The [spatial navigation](https://drafts.csswg.org/css-nav-1/) feature enables movement between focusable items and scrolling based on the directional input received and the spatial arrangement of focusable items on the page. - -Caret browsing would typically be used to place the caret, extend it into a selection, and copy content to the clipboard. Spatial navigation moves focus between focusable elements; it is to caret browsing as tab navigation is to caret browsing. It should be complementary, but because (for the moment) they consume the same input (arrow keys) you really need to use just one or the other and not both at the same time. - -The situation is similar to how caret browsing interacts with scrolling: caret browsing handles the arrow key and as a result the caret moves instead of the page scrolling (unless the caret would navigate out of the viewport in which case it is scrolled back into view). - -If both spatial navigation and caret browsing are enabled, caret browsing will take priority over spatial navigation. You can toggle caret browsing on and off with F7 so it is easy to get back into a mode that moves between focusable elements with the arrow keys instead of moving the caret. - -It is not even clear that on devices where spatial navigation shines (like navigating with a TV remote) that you would ever want to enable caret browsing--it may be that the flags controlling the features will never be enabled by default on the same device. - -## Performance -Implementation of caret browsing is not expected to impact the performance of any existing features. - - -## Testing plan -Unit test coverage to verify that toggling the caret browsing preference works will be added to [BrowserCommandsTest](https://cs.chromium.org/chromium/src/chrome/browser/browser_commands_unittest.cc?q=BrowserCommandsTest&sq=package:chromium&dr=CSs) (run under unit_test.exe). Further unit test coverage for the feature will be added to blink_unittests.exe: -- the existing [EditingCommandTest](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/commands/editing_command_test.cc?type=cs&q=EditingCommandTest&g=0&l=36) will have new test cases added -- a new suite of move command tests called MoveCommandsTest will be implemented -- a test to verify the caret blinks when caret browsing is enabled will be added to the existing [FrameCaretTest](https://cs.chromium.org/chromium/src/third_party/blink/renderer/core/editing/frame_caret_test.cc?type=cs&q=FrameCaretTest&g=0&l=19). - - -## Implementation and Shipping Plan -The feature will initially be behind a runtime flag and disabled by default. We will follow the standard Chromium guidelines to determine when to enable the feature by default. diff --git a/Accessibility/CaretBrowsing/edgeCaretBrosingPrompt.png b/Accessibility/CaretBrowsing/edgeCaretBrosingPrompt.png deleted file mode 100644 index 2f81c3a0d..000000000 Binary files a/Accessibility/CaretBrowsing/edgeCaretBrosingPrompt.png and /dev/null differ diff --git a/Accessibility/CaretBrowsing/explainer.md b/Accessibility/CaretBrowsing/explainer.md deleted file mode 100644 index 796d09a5d..000000000 --- a/Accessibility/CaretBrowsing/explainer.md +++ /dev/null @@ -1,50 +0,0 @@ -# Proposal: Native Caret browsing - -## Motivation: -Caret browsing enables users to navigate web content using the keyboard keys and common shortcuts for character, word and line level navigation. Caret browsing enables full range of text navigation and selection functionality within web content without additional pointing devices like mice, trackpads and touchpads. - -Sample caret browsing experience in Microsoft Edge on Wikipedia: -![Visual sample of using caret browsing to explore Wikipedia front page](CB_Example.gif) - -### Current problems: -Today Chromium users can download Caret Browsing extension from the Chrome Web Store. There are two problems with this approach: - -* Extensions might be blocked in a work environment due to enterprise policies. See [Issue 611798] (https://crbug.com/611798); -* Extensions might not be available in "Incognito" tabs, or when Guest profile is used; -* There are additional barriers for users of all abilities, since they need to find and install an extension and the functionality is not readily available when it might be needed; -* Applications that use Chromium don't have the platform capability to enable native caret browsing. - -## Proposal: -This proposal targets the following use cases: - -* Enable native caret browsing in all contexts including enterprises, "Incognito" tabs and Guest profile; -* Make caret browsing available to users all the time out of the box in the browser; -* Add a platform capability, so that Chromium applications can enable caret browsing for their users with small amount of work in the application. - -### Non-Goals -Native caret browsing doesn't aim to replace extensions, they would continue to work as they do today having the first opportunity to handle default F7 activation shortcut. - -## Solution: -The solution proposed in this explainer is to introduce native caret browsing in Chromium, a feature that is available in Mozilla Firefox, Microsoft Edge and Internet Explorer. - -In order to do that we suggest implementing a current common activation shortcut: F7 and confirm caret browsing activation with a dialog. - -Caret browsing activation confirmation in Microsoft Edge: -![Microsoft Edge browser dialog that asks user to confirm enabling caret browsing and allow to set the preference to never ask again in the future](edgeCaretBrosingPrompt.png) - -Caret browsing activation confirmation in Mozilla Firefox: -![Mozilla Firefox browser dialog that asks user to confirm enabling caret browsing and allow to set the preference to never ask again in the future](firefoxCaretBrosingPrompt.png) - -In some cases, function keys may not be available and so alternative shortcuts might be needed. - -We welcome your feedback and opinion on ways we can help improve Chromium caret navigation and browsing in the future! - - -## Links - -* Caret Navigation: https://en.wikipedia.org/wiki/Caret_navigation -* Caret Browsing extension in Chrome Web Store: https://chrome.google.com/webstore/detail/caret-browsing/fklpgenihifpccgiifchnihilipmbffg -* Keyboard shortcuts in Windows (Copy, paste, and other general keyboard shortcuts): https://support.microsoft.com/en-us/help/12445/windows-keyboard-shortcuts - ---- -[Related issues](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/Caret%20Browsing) | [Open a new issue](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new?title=%5BCaret%20Browsing%5D) diff --git a/Accessibility/CaretBrowsing/firefoxCaretBrosingPrompt.png b/Accessibility/CaretBrowsing/firefoxCaretBrosingPrompt.png deleted file mode 100644 index 644313c08..000000000 Binary files a/Accessibility/CaretBrowsing/firefoxCaretBrosingPrompt.png and /dev/null differ diff --git a/Accessibility/CaretBrowsing/i2i.md b/Accessibility/CaretBrowsing/i2i.md deleted file mode 100644 index 9bc100df6..000000000 --- a/Accessibility/CaretBrowsing/i2i.md +++ /dev/null @@ -1,60 +0,0 @@ -# Intent to Implement: Native Caret Browsing - - -## Contact emails -Bruce.Long@microsoft.com, Amit.Jain@microsoft.com, Grisha.Lyukshin@microsoft.com -  -## Explainer - -[Native Caret Browsing Explainer](explainer.md) - - -## Design Doc - -[Native Caret Browsing Design Document](designDoc.md) -  - -A TAG review is not requested, because the feature is not intended to be introduced as web standards. -It is not expected that web developers will need to make changes to their content as a result of this work - -## Summary -We are proposing the implementation of native caret browsing in Chromium. In caret browsing a moveable cursor is placed on a web page, allowing a user to select and navigate text with just a keyboard. Caret browsing mode will be toggled by an activation key (F7), with a confirmation dialog displayed. The native implementation of this feature will obviate the need to install a browser extension. -  -## Motivation -Caret browsing enables users to navigate web content using the keyboard keys and common shortcuts for character, word and line level navigation. Caret browsing allows a full range of text navigation and selection functionality within web content without relying on additional pointing devices like mice, trackpads and touchpads, so is an important accessibility feature. - -Today Chromium users can download a Caret Browsing extension from the Chrome Web Store. There are several problems with this approach: - -* Extensions might be blocked in a work environment due to enterprise policies (see [Issue 611798: Enterprise users can't install accessibility extensions](https://crbug.com/611798)). -* Extensions might not be available in "Incognito" tabs, or when a Guest profile is used. -* There are additional barriers for users of all abilities, since they need to find and install an extension and the functionality is not readily available when it might be needed. -* Applications that use Chromium don't have the platform capability to enable native caret browsing. -  -## Risk -### Interoperability and Compatibility -Mozilla Firefox, Microsoft Edge and Internet Explorer already natively support caret browsing. Native caret browsing doesn't aim to replace extensions; they would continue to work as they do today having the first opportunity to handle the default activation shortcut. -  -* IE: Shipped -* Edge: Shipped -* Chromium: In progress -* Firefox: Shipped -* Safari: N/A (there is [system-wide assistive support](https://discussions.apple.com/thread/250114777)) -* Web/Framework developers: N/A - -### Ergonomics -Performance should not be significantly impacted. Native caret browsing will rely on the same implementation for rendering a caret and moving it around as already used within editable content in Chromium. -### Activation -The feature is not exposed to the web API layer. The feature will initially be behind a runtime flag and disabled by default. - -  -## Debuggability -No special DevTools support is required to debug this feature. -  -## Will this feature be supported on all six Blink platforms (Windows, Mac, Linux, Chrome OS, Android, and Android WebView)? -Yes, with the caveat that function keys such as F7 may not be available and so alternative shortcuts might be needed. - -## Link to entry on the [feature dashboard](https://www.chromestatus.com/features) -TBD--but not a web-facing change in Blink. -  -## Requesting approval to ship? -No. The feature will initially be implemented behind a runtime flag. diff --git a/Accessibility/ConfirmationOfAction/explainer.md b/Accessibility/ConfirmationOfAction/explainer.md deleted file mode 100644 index 7053d2a18..000000000 --- a/Accessibility/ConfirmationOfAction/explainer.md +++ /dev/null @@ -1,302 +0,0 @@ -# Confirmation of Action - -Authors: [Travis Leithead](https://github.com/travisleithead), [Daniel Libby](https://github.com/dlibby-) - -## Abstract - -For limited-vision or non-sighted users, identifying dynamic changes in the content -of a web app is very challenging. ARIA live regions are the only mechanism available -today that communicate content changes down to the accessibility layer so that users -can hear about them. ARIA live regions are stretched far beyond their original use -cases as authors struggle to use them in scenarios that they weren't designed for. -We propose a notification API purpose-built to communicate to the accessibility layer -for scenarios in which ARIA live regions are a poor choice. One of these scenarios is -a "confirmation of action" where the action in question is not necessarily tied to UI -(elements) in the app. - -## Status of this Document - -This document is a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. -* This document status: **ARCHIVED** -* Current venue: [AOM](https://wicg.github.io/aom/) within the [W3C Web Incubator Community Group](https://wicg.io/) -* **Current version: https://wicg.github.io/aom/notification-api.html** - -## Introduction - -Screen readers provide an audible presentation of web content for various kinds -of users with disabilities (e.g., those with limited or no vision). The screen -reader knows what to say based on the semantic structure of a document. Screen -readers move through the content much the same way a sighted user might scan -through the document with their eyes. When something about the document changes -(above the fold), sighted users are quick to notice and process the change. When -something below the fold (offscreen) changes, sighted users have no way of knowing -that there was a change nor how important a change it might be. This is the -conundrum for non-sighted users in general: how and when should changes in the -content be brought to their attention? - -Screen readers and content authors work together to try and solve this problem. -One way screen readers are informed about what might be an important change is -by the content author's use of ARIA live regions. A live region is an element (and -its children) that is expected to change dynamically, and for which the changes -should be announced to the user. The live region can be configured with two -different assertiveness settings: `polite` and `assertive`. - -Unfortunately, live regions are essentially _the only way_ for content authors -to express changes in the document to assistive technology. Given the lack of -other solutions, content authors use live regions in some unusual ways that far -exceed the use-cases for which they were envisioned. Today's usage patterns and -pre-existing issues with the feature make it a challenge to use effectively: - -* Screen reader output varies greatly depending on the complexity of the live - region's content. Content authors that want a consistent experience in different - screen readers often strip-out all the richness (and semantics) of the HTML - content in the live region, leaving only simple text content in hopes of - getting a more uniform experience. - -* Multiple live regions in use at a time introduce timing and precedence concerns - for which content authors have limited control (e.g., `polite` and `assertive`) - set basic expectations around precedence of announcements, but offer little - in the way of expressing timing (apart from the moment the change is made), or - other controlling factors like interruptibility. - -* Live regions are built around the assumption that a _visual_ change needs to be - announced, hence they are tightly coupled with DOM nodes. Many changes important - to announce are not necessarily tied to a visual action. In these cases, "live - region hacks" are employed using offscreen DOM nodes to host the live region. In - these cases there is no surrounding context (an important consideration for many - screen readers), nor any presentation to show. Worse yet, since these "live region - hacks" do not play a role in the normal presentation flow of the content, they - are usually omitted for performance reasons until it is determined that a - particular user needs an "accessible version" of the site (or by heuristically - trying to detect this--which is not a recommended practice). Accessibility should - be designed into the experience from the start, and not bolted-on as an extra or - add-on feature. - -* Live-region only offer two verbosity extremes that may not strike the right - balance desired by content authors. `polite` may not be assertive enough (e.g., - in some screen readers is canceled/overridden by other basic operations like focus - changes) and `assertive` may be too aggressive (e.g., interrupts or delays other - relevant context changes). - -## Goals - -* Find a solution that can expand the capabilities presently offered by live-regions - to offer additional desired behavior -* Look into the scenarios where "live region hacks" are being used and understand the - use cases and tailor an experience for those use cases. Replace the usage of "live - region hacks" on the web with a more appropriate solution. - -## Non-Goals - -* We do not want to replace live-regions with an alternate technology. Live regions work - for a set of typical use cases and fulfill those cases when content authors use them - appropriately. - -## Use Cases - -The following use cases represent current "live region hacks" where live regions are -stretched beyond their intended usage. These usage patterns are better served by a new -solution that compliments live regions. - -### Keyboard action confirmation - -Keyboard commands not associated with UI often do not have an affordance for confirming -their state. The following cases are variations on this theme: - -1. **Glow text command:** User is editing text, highlights a word and presses - `Shift`+`Alt`+`Y` which makes it glow blue. No UI elements were triggered or changed - state, but the user should hear some confirmation that the action was successful, - such as "selected text is now glowing blue." - -2. **Set Presence**. In a chat application, the user presses `Shift`+`Alt`+`4` to toggle - their *presence* state to `do not disturb`. The application responds with "presence set - to do not disturb." - - 2.1. **Most recent notification priority:** the user presses `Shift`+`Alt`+`3` by mistake, - and then quickly presses `Shift`+`Alt`+`4`. The application began to respond with - "presence..." [set to busy] but interrupts itself with the latest response "presence - set to do not disturb." - - 2.2. **Overall priority.** The user presses `Shift`+`Alt`+`4`, then immediately issues an - AT command to jump to the next header. The response "presence set to do not disturb" - is not announced because the focus change to the next header and subsequent contextual - read-out preempted it. - -### Failed or delayed actions - -According to common screen reader etiquette, user actions are assumed to be successful by -virtue of issuing the command to do the action itself (no specific confirmation of the action -needed); however, if the action fails or is delayed, the user should then be notified. In these -unexpected cases users should be notified, otherwise their understanding about the state of the -app will be off. - -3. **Longer than usual.** User completes typing a mail message, presses _send_. In the normal - flow, no confirmation of "sent" is needed because this is assumed by the action, and focus - is redirected to the message list or next message. However, due to some networking conditions, - the _send_ action is taking longer than usual. The user should hear "message is taking longer - than usual to send". - - 3.1. **High overall priority.** After pressing _send_, the user resumes navigating through - the message list. At the conclusion of reading an existing email subject line, the AT - breaks in with "message is taking longer than usual to send". (Note, there should be - some means of separating this announcement from the prior email text, lest it be - considered a part of the email subject line). - -4. **Fail to paste.** User thought they had copied some text onto the clipboard, but in the - context of editing when they issue the paste keyboard shortcut, nothing is in the clipboard - and nothing pastes into the app. In this case, it is appropriate for the app to note the - failed action: "failed to paste, clipboard empty". - -### Secondary actions - -In addition to a primary (implicit) action, some actions have secondary or follow-up effects -that should be announced beyond the immediate effect of the primary action. - -5. **Auto fill.** In a spreadsheet, an action that sets a cell's value may be assumed to happen - (no announcement) or could be announced as a side-effect of changing the cell's value (e.g., - using a live region). In either case this would be the normal expectation for the user. However, - as a result of setting the value, the spreadsheet triggered a secondary action of autofilling a - range of corresponding values in the cell's column. This is an opportunity for the app to - additionally announce "autofilled values in cells A2 through A20". - -## Proposed Solution - -(This version inspired by [UIA Notification API](https://docs.microsoft.com/en-us/windows/win32/api/uiautomationcoreapi/nf-uiautomationcoreapi-uiaraisenotificationevent)) - -For use cases that don't have an express UI tie-in, it makes sense to provide a solution -not expressly tied to HTML element, but for which other document-centric information can -be inferred (such as language). - -```js -// As the assistive technology to notify the user, given a specific string -document.ariaNotify( "Selected text is now glowing blue." ); -``` -A screen reader or other assistive technology tool would speak or show "selected text is now -glowing blue". For users without assistive technology tools running, nothing would happen. -The call to the API has no web-observable side effects and its use should not infer that the -user is using assistive technology. - -Here the content author provides a label to group a set of related notification about the -clipboard. - -```js -// Use a label to categorize this notification -document.ariaNotify( "Paste failed.", { label: "clipboard" } ); -document.ariaNotify( "Text copied to clipboard.", { label: "clipboard" } ); -``` - -The label is used to group or categorize similar notifications. Assistive technology may -choose to use these labels to provide a filtering mechanism for users. - -Other means of expressing priority and coalescing behavior for similarly-labelled -notifications may be defined. - -```js -// Experiment with priority and message combining -let ariaNotifyOptions = { - required: true, - priority: "important", - label: "formatting commands" -}; -// (Each of these triggered by rapid keyboard shortcut usage, of course) -document.ariaNotify( "Text bolded", ariaNotifyOptions ); -document.ariaNotify( "Text unbolded", ariaNotifyOptions ); -document.ariaNotify( "Text bolded", ariaNotifyOptions ); -document.ariaNotify( "Text unbolded", ariaNotifyOptions ); -``` - -The above `priority` and `required` options are only suggestions. We invite the community -to provide feedback on what priority or coalescing behavior is appropriate given the range -of platforms and assistive technologies on the web. - -## Considerations & Open Issues - -#### Only plain text as input? - -Should the API allow for richer formatted text? Formatted text could provide hints -for expressiveness and pronunciation (TTML and WebVTT are potential candidates). - -What about supporting non-textual cues? We think other platform capabilities (like -`<audio>.play()`) can be used to handle non-textual output, and that non-textual -based messages don't need to be handled by this API. - -#### Document vs Element exposure - -With a single API offered on the `document` object, the notion of priority and -language (see below) must be made explicit in the API. As an alternative, an -element-based API could use facets of its context to make some of the priority -and language, etc. more implicit. For example, an element's visibility state, -layout position, level in the DOM hierarchy, and computed language value (via -`lang` attributes) could be used to infer context and priority for a notification -queued from that element. - -#### Language input - -Should language preference or hints be added to the API? Presumably the author's -language can be implied by context outside of this proposal (i.e., explicitly by -required language tags or implicitly via textual analysis at the AT layer). In -some scenarios it might be helpful to offer notifications in a language not -inferred from other context (such as how automated helpdesk phone systems might -offer assistance in alternate languages--prompts spoken in those alternate -languages). - -#### Catering to verbosity preferences? - -It may be useful to enable authors to offer multiple levels of verbosity for a -notification depending on how a user has configured their AT. For example, if ATs -are configured for minimal output, perhaps a single word could additionally be -provided that generalizes the full text of the notification. - -Alternatively, ATs may process the text content and apply heuristics to shorten -the text automatically (without complicating the API or relying on authors to furnish -additional terse phrases). - -#### Too easy to abuse? - -Will this API be a "footgun" that will lead to inappropriate usage patterns? The -general nature of a notification API means that authors could use it for scenarios -that are already handled by the AT (such as for focus-change actions) resulting in -confusing double-announcements (in the worst case) or extra unwanted verbosity (in -the best case). - -Note: ATs tune their behavior for the best customer experiences. AT provided -verbosity settings matching user preferences could conflict with author expectations -leading to poor experiences. - -Authors may also apply the use of this API too liberally, confirming many trivial -user actions where typical expectations do not require any notification. - -We can consider making use of -[User Activation](https://html.spec.whatwg.org/multipage/interaction.html#tracking-user-activation) -primitives to limit usage of this API to only actions taken by the author, and to -avoid the risk of denial-of-service type attacks on ATs through this API. - -## Privacy and Security Considerations - -1. **Readback.** Any readback of configuration settings for an AT via an API have the - potential of exposing a connected (vs. not connected) AT, and as such is an easy - target for fingerprinting AT users, an undesirable outcome. Similarly, confirmation - of notifications (such as via a fulfilled promise) have similar traits and are - avoided in this proposal. -2. **Authoritative-sounding notifications.** Announcements could be crafted to deceive - the user into thinking they are navigating trusted UI in the browser by arbitrarily - reading out control areas/names that match a particular target browser’s trusted UI. - * Mitigations should be applied to suppress notifications when focus moves outside - of the web content. - * Additional mitigations to block certain trusted phrases related to the browser's - trusted UI could be considered. -3. **Secure Context**. Does it make sense to offer this feature only to Secure Contexts? - Should usage of this API be automatically granted to 3rd party browsing contexts? -4. **Data Limits** (See [Security and Privacy Questionnaire #2.7](https://www.w3.org/TR/security-privacy-questionnaire/#send-to-platform)) - Should there be a practical limit on the amount of text that can be sent in one call - to the API? Just like multiple-call DoS attacks, one call with an enormous amount of - text could tie up an AT or cause a hang as data is marshalled across boundaries. - -## Alternative Solutions - -Previous discussions for a Notifications API in the AOM and ARIA groups: -* [Issue 3 - Use Case: Accessibility Notifications](https://github.com/wicg/aom/issues/3) -* [Issue 84 - Live region properties vs announcement notification](https://github.com/WICG/aom/issues/84) -* [Issue 832 - Do we need a notifications API in ARIA](https://github.com/w3c/aria/issues/832) - - diff --git a/Accessibility/HighContrast/BackplateExample.jpg b/Accessibility/HighContrast/BackplateExample.jpg deleted file mode 100644 index 63e25ae3d..000000000 Binary files a/Accessibility/HighContrast/BackplateExample.jpg and /dev/null differ diff --git a/Accessibility/HighContrast/BrowserInHighContrast.jpg b/Accessibility/HighContrast/BrowserInHighContrast.jpg deleted file mode 100644 index d7d3be8c9..000000000 Binary files a/Accessibility/HighContrast/BrowserInHighContrast.jpg and /dev/null differ diff --git a/Accessibility/HighContrast/BrowserWithoutHighContrast.jpg b/Accessibility/HighContrast/BrowserWithoutHighContrast.jpg deleted file mode 100644 index 8d22d9c22..000000000 Binary files a/Accessibility/HighContrast/BrowserWithoutHighContrast.jpg and /dev/null differ diff --git a/Accessibility/HighContrast/TurnOffHighContrast.jpg b/Accessibility/HighContrast/TurnOffHighContrast.jpg deleted file mode 100644 index 965365980..000000000 Binary files a/Accessibility/HighContrast/TurnOffHighContrast.jpg and /dev/null differ diff --git a/Accessibility/HighContrast/TurnOnHighContrast.jpg b/Accessibility/HighContrast/TurnOnHighContrast.jpg deleted file mode 100644 index 070c7f96b..000000000 Binary files a/Accessibility/HighContrast/TurnOnHighContrast.jpg and /dev/null differ diff --git a/Accessibility/HighContrast/explainer.md b/Accessibility/HighContrast/explainer.md deleted file mode 100644 index 789ba13a3..000000000 --- a/Accessibility/HighContrast/explainer.md +++ /dev/null @@ -1,171 +0,0 @@ -# High Contrast Explainer - -Authors: [Rossen Atanassov](https://github.com/atanassov), [Alison Maher](https://github.com/amaher23) - -Last Updated: 2019-01-18 - -## Status of this Document -This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. -* This document status: **ARCHIVED** -* Current venue: [W3C CSS Working Group](https://www.w3.org/Style/CSS/) | [w3c/csswg-drafts](https://github.com/w3c/csswg-drafts) -* Current version: - * [System Colors](https://drafts.csswg.org/css-color/#css-system-colors) section of [CSS Color Module Level 4](https://drafts.csswg.org/css-color) | ![GitHub issues by-label](https://img.shields.io/github/issues/w3c/csswg-drafts/css-color-4) - * ['forced-color-adjust'](https://drafts.csswg.org/css-color-adjust-1/#forced) of [CSS Color Adjustment Module Level 1](https://drafts.csswg.org/css-color-adjust-1) | ![GitHub issues by-label](https://img.shields.io/github/issues/w3c/csswg-drafts/css-color-adjust-1) - * ['forced-colors'](https://drafts.csswg.org/mediaqueries-5/#forced-colors) of [Media Queries Level 5](https://drafts.csswg.org/mediaqueries-5) | ![GitHub issues by-label](https://img.shields.io/github/issues/w3c/csswg-drafts/mediaqueries-5) - -## Overview - -High contrast is a [Windows accessibility feature](https://docs.microsoft.com/en-us/windows/desktop/w8cookbook/high-contrast-mode) intended to increase the readability of text through color contrast. Individuals with low vision may find it more comfortable to read content when there is a strong contrast between foreground and background colors. High contrast is a useful feature in increasing the readability of screen-based text for such users. - -The Windows platform provides built-in [high contrast color themes](https://docs.microsoft.com/en-us/windows/uwp/design/accessibility/high-contrast-themes) such as the more popular "black-on-white" and "white-on-black" themes. Besides the default themes, users can customize the colors and create their own themes. Applications can make use of these color themes and propagate them into their content model. In the case of the web browser, high contrast colors are propagated to website pages as a set of user agent styles, thus increasing readability of the text and allowing a coherent experience across the Windows OS and various applications. - -Microsoft Edge and IE are currently the only browsers to support the high contrast feature using Windows high contrast themes. Many of the features described in this document were first shipped in 2012 with IE 10 and continue to use the ```-ms-``` vendor prefix for names and values. - -When high contrast is currently enabled in Chrome, a popup is displayed prompting the user to install the [High Contrast extension](https://chrome.google.com/webstore/detail/high-contrast/djcfdncoelnlbldjfhinnjlhdjlikmph). This extension uses CSS/SVG filter effects overlaid on the entire webpage using its own predefined themes. The advantage of enabling high contrast in the core platform, in comparison to the extension-based approach, is that it provides a more seamless experience for users with the rest of the Windows OS. This includes not just the browser-context, but also other Chromium-powered applications. - -## User Flow - -1. User enables high contrast on their Windows device. -!['Turn on high contrast’ toggled to the ‘on’ state within Windows Settings > Ease of Access > High Contrast](TurnOnHighContrast.jpg) -2. User opens the Chrome browser, and the chosen high contrast theme ("white-on-black" in this case) is used throughout their entire browsing experience. -![Opened browser to 'https://www.wikipedia.org/' and high contrast colors are used throughout the webpage](BrowserInHighContrast.jpg) -3. User turns high contrast off. -!['Turn on high contrast’ toggled to the ‘off’ state within Windows Settings > Ease of Access > High Contrast](TurnOffHighContrast.jpg) -4. The opened browser is dynamically updated to use the original site-defined colors. -![Opened browser to 'https://www.wikipedia.org/' and site-defined colors are used throughout the webpage](BrowserWithoutHighContrast.jpg) - -## CSS Media Query - -In order to allow developer defined high contrast rules for webpages, a [high contrast media query type](https://msdn.microsoft.com/en-us/library/hh771830(v=vs.85).aspx) would be added called ```high-contrast```. This CSS media query type is currently supported by Microsoft Edge and IE. If a ```high-contrast``` media query evaluates to true, any styles defined within that media query *will* be used when in high contrast and will *not* be overridden by the high contrast feature. - -#### Possible values - -Value | Description ---- | --- -**active** | The subsequent style rules will be applied when high contrast is enabled under any theme, custom or not. -**black-on-white** | The subsequent style rules will be applied when high contrast is enabled under the black-on-white color theme. -**white-on-black** | The subsequent style rules will be applied when high contrast is enabled under the white-on-black color theme. - -#### Example usage - -```html -<style> - @media (high-contrast: active) { - p { color: red; } - } - @media (high-contrast: black-on-white) { - p { color: blue; } - } - @media (high-contrast: white-on-black) { - p { color: green; } - } - body { - color: orange; - } -</style> -<body> - <p>Some Text</p> -<body> -``` -In the HTML code snippet above, ```"Some Text"``` will appear orange when high contrast is disabled. When high contrast is enabled under the "black-on-white" high contrast color scheme, ```"Some Text"``` will appear blue. ```"Some Text"``` will appear green when high contrast is enabled under the "white-on-black" high contrast color scheme. In any other high contrast color scheme (for example, under a custom high contrast theme), ```"Some Text"``` will appear red. - - -## CSS Properties - -To provide readability between foreground and background colors, high contrast color schemes would override defined webpage styles for the following CSS properties: -* ```background-color``` -* ```color``` -* ```border-bottom-color``` -* ```border-top-color``` -* ```border-left-color``` -* ```border-right-color``` -* ```box-shadow``` -* ```column-rule-color``` -* ```outline-color``` -* ```text-shadow``` -* ```-webkit-tap-highlight-color``` -* ```background-image``` (***only*** in the case of text/date/file input control types, as well as for ```select```, ```option```, and ```optgroup``` HTML tags) -_____ -To allow for further developer customization of the high contrast feature, a [CSS property](https://msdn.microsoft.com/en-us/library/hh771863(v=vs.85).aspx), ```high-contrast-adjust```, would be added. This CSS property type is currently supported by Microsoft Edge and IE. This property can be used to override the effects of high contrast. - -#### Possible values - -Value | Description ---- | --- -**auto** | Indicates that the applicable CSS styles *will* be overridden when high contrast is enabled. -**none** | Indicates that the applicable CSS styles will *not* be overridden when high contrast is enabled. - -#### Example usage - -```html -<style> - body { - high-contrast-adjust: none; - color: orange; - } -</style> -<body> - <p>Some Text</p> -<body> -``` -In the HTML code snippet above, ```"Some Text"``` will appear orange whether or not high contrast is enabled because ```high-contrast-adjust``` is set to ```none```, effectively preventing high contrast from affecting its color. - -## Cascade Order -As mentioned previously, high contrast color schemes work by overriding user defined webpage styles for various CSS properties in order to ensure readability. The process model for these high contrast overrides is as follows: - -Given an element and a declaration from a CSS rule whose selector matches that element, the application of that declaration will be suppressed if all of the following conditions are met: - -1. The declaration is for a CSS property in the set of properties that are adjusted for high contrast (as defined in -[CSS Properties](#css-properties)) - -2. High contrast mode is enabled in the host environment - -3. The computed value of ```high-contrast-adjust``` on the element is ```auto``` - -4. The rule is **not** contained (directly or indirectly) inside an ```@media``` block matching the ```high-contrast``` media feature - -5. The rule is **not** defined in the default UA style sheet - -If all of the above conditions are met, the computed color value of the CSS property is overridden by a system color value. - -## System Colors -High contrast relies on system color keywords to fetch the appropriate theme colors, which are deprecated from the CSS Color standard in both [Level 3](https://drafts.csswg.org/css-color-3/#css2-system) and [Level 4](https://drafts.csswg.org/css-color-4/#system-colors). Blink currently does have support for these keywords, but they're currently mapped to hard-coded values instead of being plumbed through to the system color API. There is a derived class ```LayoutThemeWin```, but it currently doesn't add any functionality for this. Functionality can be added here to support the required system color keywords. - -In addition to existing CSS system color keywords, a new system color keyword would be added called ```hotlight``` that defines the system color for hyperlinks. It is important to track and store this system color because a developer might choose to unset high contrast styles for an ancestor of a link, but the high contrast link styles for descendent links must be preserved. - -This system color keyword is currently supported by Microsoft Edge and IE. On Windows, the value for ```hotlight``` should map to the ```COLOR_HOTLIGHT``` system color. On other platforms, it should map to the default color used for links. - -#### Example usage - -```html -<style> - a:link { - color: hotlight; - } -</style> -``` - -## Ensuring Readability -The goal of high contrast is to ensure a certain level of contrast between foreground and background colors. A problem arises with images. If text lies atop an image, altering the color of the text in high contrast will not guarantee its readability. One option would be to override images to allow text readability. This solution, however, is not an ideal one, as it can alter the context of a webpage for users under high contrast. - -Instead, a preferred solution is to draw a so-called "readability backplate" behind all text to ensure contrast for text lying above images. As illustrated in the screenshots below, adding a backplate behind text in high contrast can drastically increase its readability. This solution is currently used in Microsoft Edge to ensure the readability of text in high contrast. - -![High contrast readability backplate](BackplateExample.jpg) - -This backplate does not replace the background of an element, but rather is drawn on an intermediary layer: - -![Exploded diagram showing three separate layers: element text contents, a backplate rectangle, and the element background](exploded-diagram.png) - -As the diagram demonstrates, an element's text content is rendered using the ```WindowText``` system color and a backplate with a ```Window``` system color fill is drawn behind the text. These are then layered on top of the element's background (with ```background-color``` being filtered out). In the case of links, the text would instead use the appropriate high contrast link color. - -## Open questions - -Should the high contrast readability backplate be customizable for developers? In other words, should there be a similar ```high-contrast-backplate``` CSS property to allow certain styles of the backplate to be customized? -* CSS Properties that could apply: ```padding```, ```border-radius```, ```opacity```. - -In order to support existing content, we will need to add an alias for ```-ms-``` properties and values to our implementation. Is this an acceptable solution? - -Is ```hotlight``` an appropriate name for the system color keyword for hyperlinks, or would a more neutral name such as ```link``` be preferred? - ---- -[Related issues](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/High%20Contrast) | [Open a new issue](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new?title=%5BHigh%20Contrast%5D) diff --git a/Accessibility/HighContrast/exploded-diagram.png b/Accessibility/HighContrast/exploded-diagram.png deleted file mode 100644 index 137573b48..000000000 Binary files a/Accessibility/HighContrast/exploded-diagram.png and /dev/null differ diff --git a/Accessibility/PrefersContrast/explainer.md b/Accessibility/PrefersContrast/explainer.md deleted file mode 100644 index 1137bd4ec..000000000 --- a/Accessibility/PrefersContrast/explainer.md +++ /dev/null @@ -1,53 +0,0 @@ -# prefers-contrast Explainer - -The [`prefers-contrast` media query](https://drafts.csswg.org/mediaqueries-5/#prefers-contrast) gives authors a way to determine whether the user has specified a desire for increased or decreased contrast in the underlying OS. - -## Motivation - -Some users may find it difficult to read text when the foreground to background color contrast ratios are too high or too low. Many operating systems have an underlying method to apply a contrast preference across their system. For example, on Windows, High Contrast can be used to enforce a limited color palette across applications. Although the default themes guarantee a high color contrast between the text and background (>10:1), it is possible for users to customize these colors, which may result in a low contrast preference. On macOS and iOS, users have the option to apply an Increased Contrast mode. Additionally, on Linux, users can make use of High Contrast GTK themes. - -It is difficult to produce a universal solution that automatically and effectively reflects these contrast preferences on an arbitrary webpage. For instance, consider the case where text is surrounded by a `border-image`. A user requesting a specific contrast preference may find that the `border-image` distracts from the text. By using the `prefers-contrast` media query, an author can remove the `border-image` entirely and increase readability: - -<img width="593" alt="prefers-contrast-example" src="prefers-contrast-example.png"> - -``` -#borderimg { - border: 10px solid transparent; - padding: 15px; - border-image: url(border.png) 30 round; -} - -@media(prefers-contrast) { - #borderimg { - border-image: initial; - } -} -``` - -By exposing the system contrast settings to the authors through the `prefers-contrast` media query, authors can adjust the style of their site according to the contrast preference of their users. - -## Current design - -`prefers-contrast` supports four values, as well as its boolean context: - - `no-preference`: indicating the user has not specified a contrast preference - - `more`: indicating the user has specified a preference for a higher level of contrast - - `less`: indicating the user has specified a preference for a lower level of contrast - - `custom`: indicating the user has specified a preference for contrast that is neither high nor low - -On MacOS and Linux, we will match `(prefers-contrast: more)` if Increased Contrast Mode or High Contrast Mode is enabled, and `(prefers-contrast: no-preference)` otherwise. - -Similarly, on Windows, if High Contrast is not enabled, we match `(prefers-contrast: no-preference)`. However, with Windows High Contrast Mode, users have the option to choose an arbitrary color palette. In order to determine whether this palette represents a contrast preference of `more`, `less`, or `custom`, we compare the contrast ratio between the foreground and background system colors. The current specification does not indicate what ratios should be used as the cut-offs; [we are experimentally using ratios inspired by WCAG](https://www.w3.org/WAI/WCAG21/Understanding/contrast-enhanced), matching `more` if the ratio is 7:1 or greater, `less` if the ratio is 2.5:1 or less, and `custom` otherwise. - -## Prefers-contrast vs forced-colors - -`prefers-contrast` differs from the existing [`forced-colors` media query](https://drafts.csswg.org/mediaqueries-5/#descdef-media-forced-colors). In particular, the `forced-colors` media query can be used in detecting whether [Forced Colors Mode](https://www.w3.org/TR/css-color-adjust-1/#forced) is enabled. Forced Colors Mode currently can only be triggered by High Contrast on Windows. Thus, no other OS contrast setting will influence the `forced-colors` media query. On the other hand, `prefers-contrast` gives authors a tool for detecting contrast preferences across all related OS settings in order to adjust their styles accordingly. - -## Design considerations - -`prefers-contrast` had initially been designed to support three values: `high`, `low`, and `no-preference`. However, these values [failed to properly capture Increased Contrast Mode](https://github.com/w3c/csswg-drafts/issues/2943) on macOS and iOS. More specifically, Increased Contrast is different from Window’s High Contrast Mode in that the result is not a true “high” contrast, but an intermediary “increased” contrast state. To encompass the varying OS settings, the values of `high` and `low` were updated to a more general `more` and `less`. - -This three-value design, however, was flagged as potentially problematic for Forced Colors Mode users that have a color scheme with contrast ratios which are not considered particularly high or low. More specifically, if an author were to apply styles using the `prefers-contrast` boolean context to, for example, reduce visual complexity for all users with a contrast preference, any user whose Forced Color Scheme did not match `more` or `less` would fail to see these updated styles. - -To address this issue initially, `forced` was added as a value to `prefers-contrast`. This value matched whenever Forced Colors Mode was enabled, no matter what contrast ratio was being used. As a result, any Forced Colors Mode user (including those with a contrast ratio between `more` and `less`) would match in the `prefers-contrast` boolean context. - -However, `forced` has since been removed because `(prefers-contrast: forced)` was a [duplicate of `(forced-colors: active)` and could lead to author confusion](https://github.com/w3c/csswg-drafts/issues/5433). Instead, `custom` was added as a value to `prefers-contrast`. This value accounts for contrast preferences that are neither `more` nor `less`. As a result, all Forced Colors Mode users will continue to be accounted for in the `prefers-contrast` boolean context without creating redundancies with the existing `forced-colors` media query. diff --git a/Accessibility/PrefersContrast/prefers-contrast-example.png b/Accessibility/PrefersContrast/prefers-contrast-example.png deleted file mode 100644 index c3f19e644..000000000 Binary files a/Accessibility/PrefersContrast/prefers-contrast-example.png and /dev/null differ diff --git a/Accessibility/PreserveParentColor/explainer.md b/Accessibility/PreserveParentColor/explainer.md deleted file mode 100644 index b38be8826..000000000 --- a/Accessibility/PreserveParentColor/explainer.md +++ /dev/null @@ -1,90 +0,0 @@ -# preserve-parent-color Explainer - -`preserve-parent-color` is a new value for the -[`forced-color-adjust`](https://www.w3.org/TR/css-color-adjust-1) CSS property. - -## Motivation - -`forced-color-adjust` is a CSS property that allows developers to -opt out of [Forced Colors Mode](https://www.w3.org/TR/css-color-adjust-1/#forced). - -Previously, there were two supported values: `auto` and `none`, which can be -used to control whether or not an element's styles are adjusted by the UA in -Forced Colors Mode. - -A third value, `preserve-parent-color`, was introduced to allow an element to -inherit its parent's used `color` value. In other words, `preserve-parent-color` -provides the ability for an element to inherit its parent's Forced Colors Mode -adjusted `color` value. - -The intention of `preserve-parent-color` is to get a reasonable behavior for SVG -icons that utilize `currentColor` when styling `fill` or `stroke` in Forced -Colors Mode, as described in [this -thread](https://github.com/w3c/csswg-drafts/issues/6310). By changing the -default value of `forced-color-adjust` for SVGs from `none` to -`preserve-parent-color`, SVG icons that make use of `currentColor` will now -inherit the used `color` value of its parent, as expected. - -``` -<style> - body { - background-color: #293472; - color: #cedbe3; - } - - .icon { - color: currentColor; - } -</style> -<body> - <svg class="icon">[SVG stuff]</svg> - <span>Contoso Closet</span> -</body> -``` - -<table> - <tr style="vertical-align:middle;"> - <td style="text-align:right;">(a)</td> - <td style="text-align:left;"><img src="fcm-off.png"></td> - </tr> - <tr style="vertical-align:middle;"> - <td style="text-align:right;">(b)</td> - <td style="text-align:left;"><img src="fcm-expected.png"></td> - </tr> - <tr style="vertical-align:middle;"> - <td style="text-align:right;">(c)</td> - <td style="text-align:left;"><img src="fcm-current.png"></td> - </tr> - <tr style="vertical-align:middle;"> - <td style="text-align:center;" colspan=2>Figure 1: Forced Colors Mode off (top) vs. Expected behavior, Forced Colors Mode on (middle) vs. Current behavior, Forced Colors Mode on (bottom)</td> - </tr> -</table> - -The use of `currentColor` when styling an SVG icon is a common pattern used by -authors to ensure an accessible experience in Forced Colors Mode. An author -would expect the logo to automatically adjust to use the `CanvasText` system -color for `fill` and `stroke` in Forced Colors Mode, as a result of setting each -to `currentColor`, as illustrated in Figure 1b. - -This behavior, however, became broken when we moved from [forcing colors at -computed value time to used value -time](https://github.com/w3c/csswg-drafts/issues/4915). Instead of inheriting -`CanvasText`, as before, the logo would inherit the computed `color` value of -its parent, as in Figure 1c, resulting in a logo that is no longer cohesive in -Forced Colors Mode. - -The new `preserve-parent-color` value was added to address this common SVG use -case. It is important to note that this may break SVGs that expect the opposite -inheritance behavior for the ‘color’ property. However, the behavior of -`preserve-parent-color` handles the most common SVG use cases, and the behavior -better matches legacy implementations of High Contrast Mode. - -## Current Design - -`forced-color-adjust` supports three values: - - `auto`: auto-adjustment of an element's style by the UA is allowed in Forced Colors Mode - - `none`: auto-adjustment of an element's style by the UA is not allowed in Forced Colors Mode - - `preserve-parent-color`: if the `color` property inherit from its parent, - compute to the used color of the parent's `color` value in Forced Colors -Mode. Otherwise, acts the same as `none`. - diff --git a/Accessibility/PreserveParentColor/fcm-current.png b/Accessibility/PreserveParentColor/fcm-current.png deleted file mode 100644 index e6fe0c3e2..000000000 Binary files a/Accessibility/PreserveParentColor/fcm-current.png and /dev/null differ diff --git a/Accessibility/PreserveParentColor/fcm-expected.png b/Accessibility/PreserveParentColor/fcm-expected.png deleted file mode 100644 index abbbb6435..000000000 Binary files a/Accessibility/PreserveParentColor/fcm-expected.png and /dev/null differ diff --git a/Accessibility/PreserveParentColor/fcm-off.png b/Accessibility/PreserveParentColor/fcm-off.png deleted file mode 100644 index ef1b8753d..000000000 Binary files a/Accessibility/PreserveParentColor/fcm-off.png and /dev/null differ diff --git a/Accessibility/UIA/explainer.md b/Accessibility/UIA/explainer.md deleted file mode 100644 index 0b429c5ee..000000000 --- a/Accessibility/UIA/explainer.md +++ /dev/null @@ -1,54 +0,0 @@ -# UI Automation Provider Mappings Explainer - -Authors: [Rossen Atanassov](https://github.com/atanassov), [Melanie Richards](https://github.com/melanierichards) - -## Introduction - -[Microsoft UI Automation (UIA)](https://docs.microsoft.com/en-us/dotnet/framework/ui-automation/ui-automation-overview) provides programmatic access to most user interface (UI) elements of desktop applications, as well as web content and web applications. This API enables assistive technology (AT) products, such as screen readers, to provide information about applications, their UI and contents to end users. With this information, ATs can allow the user to manipulate applications by means other than standard input. - -At a high level, UIA exposes two sets of APIs, *provider APIs*, those implemented by a web browser for example, and *client APIs*, those implemented by an AT. This document’s focus is on implementing the *provider* APIs inside Chromium. These APIs are not exposed to web developers, and it is not expected that web developers should change the way they build sites and web apps—these APIs are meant to map web content into a format useful to C/C++ programmers. - -## Why is a UI Automation implementation needed? - -A UIA implementation benefits the browser/web platform, AT clients, and ultimately the end user. - -By supporting UIA, Chromium-based browsers can: - -* **Ensure that their platform is available to users who choose to browse with UIA-powered ATs, such as Narrator.** While UIA provides proxies from other accessibility APIs—namely MSAA and in preview builds, IA2—these proxies are not as performant as native UIA support, and do not cover the full capabilities of native UIA interfaces. In order to provide the most enjoyable and performant experience for these AT users, full UIA support is recommended. -* **Provide accessibility mappings in a secure fashion.** As part of the Windows platform, UIA allows providers to expose robust information about their UI and contents to ATs, while insulating providers (the browser) from third-party AT code. UIA enables better process sandboxing and a stronger security model for the browser and its processes, ultimately keeping users more secure. -* **Take advantage of the official Windows accessibility API.** Cross-OS web platforms have the challenge of providing a consistently delightful user experience—while also leveraging the extra goodness each platform has to offer, such that the browser feels “at home” on the given operating system. Controls are presented in UIA in a consistent fashion from application to application, and so ATs can enable interactions in the browser that are natural alongside the rest of the OS ecosystem. - -With UIA, ATs can enhance web browsing for their users in Chromium-based browsers. ATs can: - -* **Innovate on top of rich text-level interaction and smooth reading experiences.** TextPattern is a UIA interface that was designed to allow interactivity and text reading at various levels of fidelity—characters, words, sentences, paragraphs, or even an entire document. Combine this with extensive information about text attributes, and ATs have a powerful set of text APIs with which to provide quite tailored functions for a great browsing experience. -* **Spend less time writing to different accessibility APIs, and more time on core capabilities.** When the same API is supported across an OS ecosystem, including the web platform, ATs do not need to implement per-application logic to account for differences in how content and controls are structured. This is a great benefit as it allows ATs to focus on providing innovative and performant experiences for any application, including the browser. -* **Take advantage of a continually-evolving platform.** As the official accessibility API of the Windows platform, UIA continually invests in performance gains and extensions to the API surface, based on AT vendor feedback. ATs can take advantage of this continued investment to likewise evolve their offerings. - -At the heart of all this are users of assistive tech, who are empowered to browse the web with tools that are efficient, robust, and secure. - -## High-level summary of provider APIs - -In order to complete UI Automation support in Chromium, this project will implement the provider APIs for: - -* **The [accessibility tree](https://docs.microsoft.com/en-us/windows/desktop/winauto/uiauto-eventsoverview) and [control types](https://docs.microsoft.com/en-us/windows/desktop/winauto/uiauto-controltypesoverview):** This work will leverage Chromium’s platform-agnostic accessibility tree abstraction layer, and map its elements to the appropriate UIA control types. For reference, in case of web applications, a control could be represented by one or more HTML elements, such as a `<a>` or `<select>` and its `<option>` elements. -* **[Properties](https://docs.microsoft.com/en-us/windows/desktop/winauto/uiauto-propertiesoverview):** mappings from ARIA and host language semantics into control properties. These can include what state the control is in, the control’s relationship to other controls, its styles, value, and placement, and many other useful properties (such as alternative text). -* **[Control patterns](https://docs.microsoft.com/en-us/windows/desktop/winauto/uiauto-controlpatternsoverview):** methods allowing interaction with controls, often with interaction models specific to their types. Many control patterns implement UI idioms such as "this element is toggle-able, here's a method to toggle it" or "this element accepts a range of values, here are the min/max/current values and a method for changing the current value." Some control patterns expose additional structural information, such as "this tree of nodes represents a table with rows and columns." TextPattern, as previously mentioned, is a robust control pattern which marks up text content with both structure and style information; it also enables linear navigation over text content. -* **[Events](https://docs.microsoft.com/en-us/windows/desktop/winauto/uiauto-eventsoverview):** notifications for structural and property changes of UI controls. Similarly to other accessibility APIs, UIA events inform ATs of changes which can be surfaced to end users. - -## Example of control interfaces - -For an example of how these APIs work in conjunction to provide complete interactivity with a control, refer to documentation for the [Button control type](https://docs.microsoft.com/en-us/windows/desktop/winauto/uiauto-supportbuttoncontroltype) (which `<button>` and elements with `role="button"` map into). - -## Current workarounds - -Providing UI and text information for the purposes of accessibility technologies is already possible today, even without UI Automation. Existing technologies such as MSAA and IA2 are examples of platform APIs that allow ATs to observe and interact with the browser and its web contents. - -Implementing UI Automation support on the Windows platform is not intended to replace any existing platform API support, but to offer a mature and evolving API choice to assistive technologies and their users. - -## Additional information - -* For detailed information on UI Automation provider APIs, please refer to [UI Automation Providers Overview](https://docs.microsoft.com/en-us/windows/desktop/winauto/uiauto-providersoverview). -* Each accessibility API has its own nomenclature. For a quick sense of the differences between API mappings, refer to the tables in mapping specifications such as [Core-AAM](https://w3c.github.io/core-aam/), [HTML-AAM](https://w3c.github.io/html-aam/), [Graphics-AAM](https://w3c.github.io/graphics-aam/), and [SVG-AAM](https://w3c.github.io/svg-aam/). - ---- -[Related issues](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/UI%20Automation) | [Open a new issue](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new?title=%5BUI%20Automation%5D) diff --git a/Accessibility/UIA/i2i.md b/Accessibility/UIA/i2i.md deleted file mode 100644 index 650b54c7e..000000000 --- a/Accessibility/UIA/i2i.md +++ /dev/null @@ -1,93 +0,0 @@ -# Intent to Implement and Ship: UIAutomation Provider Mappings - -## Contact emails - -Rossen.Atanassov@microsoft.com, Melanie.Richards@microsoft.com - -## Explainer - -[UI Automation Explainer](explainer.md) - -## Design doc/Spec - -[UI Automation Providers Overview](https://docs.microsoft.com/en-us/windows/desktop/winauto/uiauto-providersoverview) - -A TAG review is not requested, as these interfaces are specific to the Windows platform and not intended to be introduced as web standards. It is not expected that web developers will need to make changes to their content as a result of this work, as UIAutomation support reflects lower-level implementation details, and will not be replacing or removing accessibility APIs already supported in Chromium. - -## Summary - -Microsoft UI Automation (UIA) provides programmatic access to most user interface (UI) elements of desktop applications, as well as web content and web applications. This API enables assistive technology (AT) products, such as screen readers, to provide information about applications, their UI and contents to end users. With this information ATs can allow user to manipulate applications by means other than standard input. - -At a high level, UIA achieves its functionality by exposing two sets of APIs, *provider APIs*, those implemented by a web browser for example, and *client APIs*, those implemented by an AT. This document’s focus is on implementing the *provider* APIs inside Chromium. These APIs are not exposed to web developers, and it is not expected that web developers should change the way they build sites and web apps—these APIs are meant to map web content into a format useful to C/C++ programmers. - -## Motivation - -A UIA implementation benefits the browser/web platform, AT clients, and ultimately the end user. -By supporting UIA, Chromium-based browsers can: - -* Ensure that their platform is available to users who choose to browse with UIA-powered ATs, such as Narrator. -* Provide accessibility mappings in a secure fashion. -* Take advantage of the official Windows accessibility API. - -With UIA, ATs can enhance web browsing for their users in Chromium-based browsers. ATs can: - -* Innovate on top of rich text-level interaction and smooth reading experiences. -* Spend less time writing to different accessibility APIs, and more time on core capabilities. -* Take advantage of a continually-evolving platform, and the performance gains and extended capabilities that come along with it. - -For extended background on motivations, please refer to the [explainer](explainer.md). - -## Risks - -### Interoperability and Compatibility - -**Interoperability risk:** This work has no impact to the core web platform exposed to JavaScript developers. As such the risk of introducing interoperability gaps in the web layer is low to none. - -A UIA implementation has no bearing on browser support for any other assistive technology platform APIs, such as MSAA and IA2, as these APIs are distinct from UIA. While interoperable support would be ideal for primarily-UIA-based AT experiences, other browsers are free to choose to support other accessibility APIs, in addition to or instead of UIA. - -* Edge: Shipped -* Chrome: In progress -* Firefox: No signals -* Safari: N/A -* Web / Framework developers: N/A - -**Compatibility risk:** same as Interoperability risk - -### Ergonomics - -UIA doesn’t pose any specific performance concerns that aren’t applicable to any other assistive API, such as IA2. - -In order to extend UIA support from basic to full coverage, this work will continue to leverage Chromium’s platform-agnostic accessibility tree abstraction layer, and will map tree elements to the appropriate UIA control types. Support of UIA interfaces will be consistent with other UIA providers, as outlined in [UI Automation Providers Overview](https://docs.microsoft.com/en-us/windows/desktop/winauto/uiauto-providersoverview). For an overview of how web content semantics will map to UIA, refer to the following mapping specifications: [Core-AAM](https://w3c.github.io/core-aam/), [HTML-AAM](https://w3c.github.io/html-aam/), [Graphics-AAM](https://w3c.github.io/graphics-aam/), and [SVG-AAM](https://w3c.github.io/svg-aam/). - -### Activation - -Since the feature isn’t exposed to the web API layer, it has no bearing to web developers nor is it expected to pose any challenges or additional requirements to them. - -In Chromium today, accessibility API code is activated only when ATs are attached, or the user has specified via [browsername]://accessibility that the browser should be run in accessibility mode. - -## Debuggability - -UIA is implemented and debugged at the browser layer, thus is not intended to be exposed to DevTools. At the same time, it is useful to allow web developers to observe the information provided to UIA–the control types, properties and supported patterns. Since DevTools support isn’t critical to this feature, it can be considered TBD. However, it is reasonable to expect that these DevTools will be similar to representations found cross-browser today: - -* A representation of the current document’s accessibility tree -* A list of key-value pairs for the computed accessibility mappings of the currently-inspected node - -## Will this feature be supported on all six Blink platforms (Windows, Mac, Linux, Chrome OS, Android, and Android WebView)? - -No. UIAutomation provider APIs are specific to Windows and will only be supported on Windows 7+ platforms. - -## Is this feature fully tested by [web-platform-tests](https://chromium.googlesource.com/chromium/src/+/master/docs/testing/web_platform_tests.md)? - -This feature is not a web standard, but mappings from ARIA and host language semantics into UIA can be tested. The relevant test suites include: - -* [CORE-AAM-1.1](https://github.com/w3c/test-results/tree/gh-pages/core-aam-1.1) -* [CORE-AAM-1.2](https://github.com/w3c/test-results/tree/gh-pages/core-aam-1.2) -* [GRAPHICS-AAM](https://github.com/w3c/test-results/tree/gh-pages/graphics-aam) - -## Link to entry on the feature dashboard - -<TBD> - -## Requesting approval to ship? - -Yes diff --git a/Accessibility/VirtualContent/explainer.md b/Accessibility/VirtualContent/explainer.md deleted file mode 100644 index 1f7aecce7..000000000 --- a/Accessibility/VirtualContent/explainer.md +++ /dev/null @@ -1,244 +0,0 @@ -# ARIA Virtual Content - -Author: [Kevin Babbitt](https://github.com/kbabbitt), [Melanie Richards](https://github.com/melanierichards) - -Last updated: 2021-01-07 - -## Status of this Document -This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. -* This document status: **ARCHIVED** -* Current venue: [W3C Web Incubator Community Group](https://wicg.io/) -* Current version: https://github.com/WICG/accessible-loading-and-searching-of-content/blob/master/explainer.md - -## Introduction - -*Virtualized content* allows an author to efficiently represent a large body of information at once. In this practice, only a -fraction of the information is actually present in the markup - typically the portion that is currently visible, plus a small amount -preceding or following in order to allow for scrolling. As the user scrolls the web page, JavaScript code will load more content and -insert it into the markup at the appropriate location. This process is referred to as *realizing* the content. - -Virtualized content presents challenges for users of assistive technologies (ATs). Generally, an AT's view of the document is limited -to what is physically present in the markup. Without some indication that further content exists, the AT cannot accurately describe and -navigate the web page. For example, some ATs allow the user to skim the headings in a document by jumping from one to the next. After -reading the last realized heading, the AT has no way of knowing that another heading might exist in virtualized content, nor does it -have a way of discovering that there is even virtualized content to begin with. The AT, and the user, thus incorrectly conclude that the -last heading in the document has been reached. - -## Goals - -* Enable ATs to recognize and account for virtualized content when navigating a web page. -* Protect the privacy of AT users by avoiding mechanisms that would allow for fingerprinting. - -## Proposed API - -A new attribute, `aria-virtualcontent`, indicates whether an element contains virtualized content. The default value is `none`. - -A **virtual content container** is any element that has a non-default value for `aria-virtualcontent`. - -A **virtual content edge** is an edge of a virtual content container where the webpage can realize content. - -The `aria-virtualcontent` attribute can have one of several keyword values, or multiple values separated by spaces, establishing which edges of the container are virtual content edges: `block-end`, `block-start`, `inline-end`, `inline-start`. - -*For potential future expansions of this API, such as hint(s) about the nature of the virtualized content, please refer to [Potential additions to proposed API](#potential-additions-to-proposed-api).* - -Marking an element as a virtual content container establishes a contract between the web page and ATs. Specifically: -* If the virtual content container is also a [scroll container](https://www.w3.org/TR/css-overflow-3/#scroll-container), the web page ***MUST*** begin steps to realize content no later than when the virtual content container is scrolled to a limit where a virtual content edge exists. -* If the virtual content container is not a [scroll container](https://www.w3.org/TR/css-overflow-3/#scroll-container), the web page ***MUST*** begin steps to realize content no later than when a virtual content edge enters the [scrollport](https://www.w3.org/TR/css-overflow-3/#scrollport) of its nearest ancestor [scroll container](https://www.w3.org/TR/css-overflow-3/#scroll-container). - -## Example 1: Document with Headings - -The following simplified example shows one potential usage pattern for `aria-virtualcontent`. - -``` -<html> -<head> - <script src="virtualcontent.js"></script> -</head> -<body> - <main id="main" aria-virtualcontent="block-end" aria-busy="false"> - <h1>Section 1</h1> - ... - <h1>Section 2</h1> - <h2>Section 2.1</h2> - ... - <!-- etc... ---> - <div id="loading_spinner" role="presentation" style="visibility:hidden"> - <img src="loading.gif" role="presentation"> - </div> - </main> - <div id="announcer" aria-live="assertive" style="width: 0; height: 0; overflow: hidden"></div> -</body> -</html> -``` - -virtualcontent.js: -``` -// Issues a request to the server for the next section of content. -function begin_load_next_section() { - // Issue request to server. - // ... -} - -// Callback triggered when the server responds with next section of content. -function end_load_next_section(response) { - // Signal to the AT that we're loading more content. - document.getElementById("main").setAttribute("aria-busy", "true"); - - // Insert response payload into the DOM. - document.getElementById("main").appendChild(...); - - // Signal to the AT that we're done loading. - document.getElementById("main").setAttribute("aria-busy", "false"); - - // Signal to the user that we're done loading. - document.getElementById("loading_spinner").style.visibility = "hidden"; - document.getElementById("announcer").textContent = "Next section loaded."; - - // Update the state of the virtual content container. - if (response.isAtEndOfDocument) { - document.getElementById("main").removeAttribute("aria-virtualcontent"); - } -} - -// Listens for scroll events and responds accordingly. -function on_scroll_changed() { - // Check whether the user has scrolled to the bottom of the page. - let target = document.documentElement; - let top = (target && target.scrollTop) || document.body.scrollTop; - if (top + target.clientHeight >= target.scrollHeight) { - // Issue request to the server. - begin_load_next_section(); - - // Signal to the user that we're loading more content. - document.getElementById("loading_spinner").style.visibility = "visible"; - document.getElementById("announcer").textContent = "Loading additional content."; - } -} - -// Initialize scroll handler. -window.addEventListener("scroll", on_scroll_changed); -``` - -### Walkthrough - -#### Initial Conditions -* The web page is in a steady state. -* The `main` element is partially visible in the viewport. This element's bottom edge is below the bottom edge of the viewport. Because it has `aria-virtualcontent` set, it is a virtual content container. -* An AT has its reading cursor on the last heading element inside the virtual content container. - -#### Scenario -1. The user directs the AT to navigate to the next heading in the document. -2. Using the platform accessibility API, the AT searches the web page for a subsequent heading. It finds none. -3. The AT searches the parent chain of the last heading element (i.e. the current location of its reading cursor). It finds the `main` element and sees that it is a virtual content container. -4. The AT reacts to the presence of a virtual content container by looking for a scrollable element, starting with the virtual content container itself and searching the parent chain from there. It finds the scroller for the document itself and asks the user agent to scroll to the bottom of the page. *(Privacy note: APIs already exist on some operating systems that allow an AT to do this without fingerprinting the user.)* -5. The user agent scrolls as requested by the AT and generates a scroll event. -6. The `on_scroll_changed()` function is called in reaction to the scroll event. Script code issues a request to a backing server for additional content, displays a loading spinner, and announces that additional content is being loaded using an ARIA live region. -7. The backing server sends back a payload with additional content. Upon receiving the payload, the `end_load_next_section()` function is called. Script code inserts the new content into the DOM at the bottom of the virtual content container, hides the loading spinner, and announces to the user that additional content is available. -8. The user agent notifies the platform accessibility API that the web page content has changed. -9. The user hears the announcement that new content has been loaded. He/she once again directs the AT to navigate to the next heading. -10. Using the platform accessibility API, the AT searches the web page for a subsequent heading. -11. The AT finds a heading in the newly loaded content, moves its reading cursor to that heading, and reads the heading to the user. -12. The user continues navigating through document headings in the same fashion. -13. Eventually, the backing server sends a content payload which also contains a signal that the end of the document has been reached. Script code removes the `aria-virtualcontent` attribute from the `main` element to indicate there is no more virtualized content. -14. The next time the user attempts to navigate to the next heading, the AT searches for a virtual content container, finds none, and announces there are no further headings in the document. - -## Example 2: Tabular data - -This example illustrates one use of `aria-virtualcontent` for virtualized content in the inline direction. The document in this scenario is a table of bug reports with the following columns: report ID, report date, status, assigned to, title, fix date, fixed by. - -In a fully-realized table, a typical AT would walk the table in row-major order, stopping and reading out each cell in turn. In this example, the content author has incorporated script that keeps only a subset of columns realized depending on viewport width, and the viewport width is such that the script will keep no more than five columns realized at a time. The AT achieves the same reading order as for a fully-realized table by checking for virtualized content in the inline direction whenever it's ready to advance from the last realized cell in a row. - -``` -<html> -<body> -<table aria-virtualcontent="block-end inline-end" aria-colcount="7" aria-busy="false"> - <thead> - <tr> - <th>ID</th> - <th>Status</th> - <th>Assigned To</th> - <th>Report Date</th> - <th>Title</th> - <!-- <th>Fix Date</th> - Virtualized --> - <!-- <th>Fixed By</th> - Virtualized --> - </tr> - </thead> - <tbody> - <tr> - <td>338</td> - <td>Closed</td> - <td>Alice</td> - <td>April 21</td> - <td>Widget freezes up when the network is slow</td> - <!-- <td>Fix Date</td> - Virtualized --> - <!-- <td>Fixed By</td> - Virtualized --> - </tr> - <tr> - <td>342</td> - <td>Fixed</td> - <td>Bob</td> - <td>April 22</td> - <td>Crash when shift-double-clicking the widget</td> - <!-- <td>Fix Date</td> - Virtualized --> - <!-- <td>Fixed By</td> - Virtualized --> - </tr> - </tbody> -</table> -</body> -</html> -``` - -Backing script performing similar functions as Example 1's virtualcontent.js is assumed to be present. - -### Walkthrough - -An AT navigating the above content by table cells might result in the following flow: -1. The AT stops on and reads out each of the five realized cells in the header row. -2. Upon reaching the end of the row, the AT discovers that the table has virtualized content in the inline-end direction and scrolls the document in that direction. -3. Backing script realizes headers and data for the "Fix date" and "Fixed by" columns. It also virtualizes headers and data for the "ID" and "Status" columns. There are no longer any virtualized columns in the inline-end direction, but there are now virtualized columns in the inline-start direction. Accordingly, in the table's `aria-virtualcontent` attribute, script replaces the `inline-end` token with `inline-start`. -4. The AT stops on and reads out the two newly realized cells in the header row. -5. Upon reaching the end of the row, the AT discovers that the table no longer contains virtualized content in the inline-end direction. The next realized cell is the "Assigned To" cell in the row for bug 338. However, the table contains virtualized content in the inline-start direction, so the AT recognizes that the next realized cell is not the next cell in the overall table. The AT notes to itself that it is moving into the row for bug 338 and scrolls the document in the inline-start direction. -6. Backing script realizes headers and data for the "ID" and "Status" columns. It also virtualizes headers and data for the "Fix date" and "Fixed by" columns. There are no longer any virtualized columns in the inline-start direction, but there are now virtualized columns in the inline-end direction. Accordingly, in the table's `aria-virtualcontent` attribute, script replaces the `inline-start` token with `inline-end`. -7. The AT moves to the first realized cell in the row for bug 338. In turn, it stops on and reads out each of the realized cells in that row. -8. After reading the last realized cell in the row for bug 338, the AT checks for virtualized content in the inline-end direction and finds there is some. It scrolls the document in the inline-end direction. -9. Backing script realizes content in the inline-end direction, virtualizes content in the inline-start direction, and updates the table's `aria-virtualcontent` attribute, the same as in step 3. -10. The AT stops on and reads out the two newly realized cells in the row for bug 338. -11. Upon reaching the end of the row, the AT discovers that the table no longer contains virtualized content in the inline-end direction, then moves on to the next row (the row for bug 342) and checks for virtualized content in the inline-start direction, the same as in step 5. -12. Backing script realizes content in the inline-start direction, virtualizes content in the inline-end direction, and updates the table's `aria-virtualcontent` attribute, the same as in step 6. -13. The AT stops on and reads out each cell in the row for bug 342, scrolling as necessary to realize additional cells. The flow is the same as for the previous row, as outlined in steps 7-10. -14. After reading the last cell in the row for bug 342, the AT discovers that the table no longer contains virtualized content in the inline-end direction. It looks for another row in the table and finds there are no more realized rows. -15. However, the AT also discovers that the table has virtualized content in the block-end direction. The AT issues two scroll requests: first in the inline-start direction to return to the first columns in the table, then in the block-end direction to load additional rows. -16. Backing script realizes the next few rows in the table, and the AT continues reading. - -## Potential additions to proposed API - -The proposed API aims for flexibility in assistive technologies’ user experiences. -If deemed useful by the community, -the API could potentially be extended to provide more precise hints. - -### Virtualized content type hints - -The `aria-virtualcontent` attribute or a related attribute (e.g. `aria-virtualcontenttypes`) -could provide a hint to assistive technologies as to important types of content that are currently virtualized. -Such a hint could take space-separated keywords as its value (e.g. `headings`, `tables`), -and a default, implicit value of `any`. -Assistive technologies could use this hint when implementing various navigational modes, -such as "move by heading". -This token list would necessarily need to be scoped to a limited subset of keywords, -to avoid introducing extensive microformats. - -## Alternate solutions - -### The `feed` role - -[WAI ARIA 1.1](https://www.w3.org/TR/wai-aria-1.1/) defines the [`feed`](https://www.w3.org/TR/wai-aria-1.1/#feed) role, which -implements one solution for allowing ATs to interact with virtualized content, in the context of a scrollable list of articles. -However, `feed` has limitations: -* Because it is implemented as a role, it cannot coexist with other roles. This limits its applicability - for example, one might -imagine virtualizing portions of a spreadsheet whose role is best defined as `table` or `grid`. -* There is no explicit indication that virtualized content exists; in other words it does not allow the AT to distinguish between -the end of realized content and the actual end of content. This can result in the AT giving incorrect cues to the user as described -in the introduction. - ---- -[Related issues](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/Virtual%20Content) | [Open a new issue](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new?title=%5BVirtual%20Content%5D) diff --git a/AcquisitionInfo/explainer.md b/AcquisitionInfo/explainer.md deleted file mode 100644 index dd81a41bc..000000000 --- a/AcquisitionInfo/explainer.md +++ /dev/null @@ -1,389 +0,0 @@ - -# API for Acquisition Attribution of Web Apps - -Author: [Alex Kyereboah](https://github.com/akyereboah_microsoft) - -## Status of this Document - -This document is a starting point for engaging the community and standards bodies in developing -collaborative solutions fit for standardization. As the solutions to problems described in this -document progress along the standards-track, we will retain this document as an archive and use -this section to keep the community up-to-date with the most current standards venue and content -location of future work and discussions. - -* This document status: **Active** -* Expected venue: [Microsoft Edge Explainers](https://github.com/MicrosoftEdge/MSEdgeExplainers) -* **Current version: this document** - -## Introduction - -One of the major issues blocking the adoption of store platform advertisements is the lack of -support for third-party (3P) attribution for Progressive Web Apps (PWAs). While attribution and -acquisition information may look slightly different based on platform, PWAs lack this capability -across all Store platforms (Microsoft Store, Google Play Store, Apple App Store). On -Windows, this limitation prevents PWAs published in the Microsoft Store from obtaining the -Campaign ID which identifies the ad campaign source of app acquisition. While -[Universal Windows Platform (UWP) apps can access this information through a WinRT API][WinRT API], -the absence of similar functionality for PWAs poses a gap between native applications and -PWAs. We aim to not only bridge this gap, but to enable similar functionality surrounding attribution -for browser-installed web applications through the introduction of the **Acquisition Info API**. - -## Goals - -1. Provide a web API around 3P acquisition attribution for PWAs acquired through an app store or directly -from the browser. - -## Non-Goals - -1. Define how a UA can retrieve acquisition information from every platform-specific Store. - -## Store-install Scenario (Windows) - -A web app developer has submitted their app to the Microsoft Store, and they have created ad -campaigns to promote their app. When a user sees and clicks on an advertisement that leads them to -acquire the web app from the Store, the ad Campaign ID associated with the advertisement -is recorded by the Store, and associated with the user's app entitlement. On any given launch -of the Store-acquired web app, the web app developer can call the JavaScript API to request the -acquisition information from the Store in order to retrieve the Campaign ID associated with that -user/app acquisition. The web app developer will then be able to use this information to help -determine which ad campaigns were most effective to best inform their future ad investment -decisions. - -## Proposed Solution - -The Acquisition Info API introduces the `navigator.acquisitionInfoProvider` attribute which allows -developers to access a promise method that can return acquisition information for installed -web apps. The API will be `undefined` when run as a normal tab in the browser (which explicity excludes -support for the [`browser` display mode][display mode]). To obtain a valid value, the site needs to run as an -installed web app. Browser extensions are not eligible to call this API. - -### The current shape of acquisition information - -Currently, there are extensive solutions across multiple platforms for obtaining acquisition -information in native applications beyond the WinRT API solution for Windows platform. The Google -Play Store has the [Play Install Referrer API][Play Install Referrer API] to retrieve referral content from Google Play such as -referrer URL, while the Apple App Store has the [Apple Ads Attribution API][Apple Ads Attribution API] that provides payloads -with information such as campaign ID. However, all of these solutions are only available for use by -native applications and there exists no equivalent for web apps. The Acquisition Info API aims to -continue efforts to close the gap between native apps and web apps by providing that missing -functionality. - -### Accessing the provider as an attribute - -`navigator.acquisitionInfoProvider` accesses the acquisition info provider attribute of -`navigator`, which provides access to the `getDetails()` method that surfaces various acquisition details, -including the Campaign ID. There is no need for a parameter input when calling this method, -as the user agent (UA) is expected to derive application context through the UA or store platform user account. - -```js -// Detecting if the API is supported in this context. -if (navigator.acquisitionInfoProvider) { - try { - let details = await navigator.acquisitionInfoProvider.getDetails(); - // App may log results here for backend attribution purposes - console.log("Install source was: ", details.installSource); - consle.log("From campaign: ", details.campaignId); - // Use the returned dictionary here. - ... - } catch (error) { - if (error.name === 'NetworkError') { - // There was a network failure in accessing the Store endpoint. - return; - } else if (error.name === 'AccessError') { - // There was an error in accessing UA provided offline values. - return; - } - } -} -``` - -### Accessing acquisition details - -Once we've accessed the provider, we can asynchronously retrieve a dictionary payload of -acquisition details through the `getDetails()` method which returns a ScriptPromise object. - -```js -details = await navigator.acquisitionInfoProvider.getDetails(); -let installSource = details["installSource"]; // e.g. Microsoft Store -let campaignId = details["campaignId"]; // e.g. adCampaign202306 -``` - -`installSource` is a required dictionary property with values dependent on the following scenarios: - -1. `Microsoft Store` | `Google Play Store` | `Apple App Store` - The web app was installed through one of these store platforms. - -2. `Policy` - The web app was installed due to an enterprise policy. - -3. `Sync` - The web app was installed as part of app sync. - -4. `Preinstall` - The web app was preinstalled on the device. - -5. `<install origin>` - For [same-domain installation](#same-domain-installation), the domain name of the web app. -For [cross-domain installation](#web-install-api-cross-domain-installation), the installer's domain name. - -While `installSource` remains a required property, depending on the platform of the acquisition the pairs contained within -the dictionary may differ to fit different store use cases. - -> Payload examples are taken from [Play Install Referrer][Play Install Referrer API] and [Apple Ads Attribution][Apple Ads Attribution API] -documentation and are not final. - -```js -details = { - installSource: "Google Play Store", - installReferrer: "utm_source=google-play&utm_medium=organic", - referrerClickTimestampSeconds: 1623214800, - installBeginTimestampSeconds: 1623214810, - installBeginReferrer: "utm_source=google-play&utm_medium=referral&utm_campaign=campaign-123", - installVersion: "1.0.0", - googlePlayInstant: false -} -``` - -```js -details = { - installSource: "Apple App Store", - attribution: "true", - orgId: "40669820", - campaignId: "542370539", - conversionType: "Download", - adGroupId: "542317095", - countryOrRegion: "US", - keywordId: "87675432", - adId: "542317136" -} -``` - -In the event that a web application has referral information that has expired for that specific -application, or in any other case where acquisition info is missing for the web application, -the API would return empty values for the associated properties in the dictionary. This is consistent with -existing behavior in store information retrieval API for native applications. - -```js -details = { - installSource: "Microsoft Store", - campaignId: "" -} -``` - -## Attribution on browser installs - -While attribution is currently not being tracked in the same capacity as Store installations for browser-initiated -installations of web applications, the Acquisition Info API could enable this sort of attribution capability. - -### Same-domain installation - -Currently the main method of installing a PWA through the browser is navigating to the desired web application and -initiating a same-domain installation. It may be the case that the intial navigation was due to an ad click. -By having a standardized dictionary property (`attributionId`) that the UA is responsible for capturing to keep track of ad attribution, -it becomes possible for the Acquistion Info API to return attribution information for browser-installed web applications as well. -This `attributionId` dictionary property could be captured by the UA through a query string parameter such -as `__a_id` in the GET parameters for the website. `httpReferrer` and `installTimestamp` are proposed as properties that should -also be captured by the UA at install time and are included in every dictionary result for a browser-installed app. - -#### Example same-domain use case - -1. User clicks on a Bing ads campaign on `bar.com` that navigates to `foo.com?__a_id=bingAdsAug2023`. - -2. User installs the `foo.com` PWA from `foo.com`. - -3. UA captures the attribution information to be eventually returned at the time of installation. - -4. Calling the Acquisition Info API `getDetails()` for `foo.com` PWA produces the following payload. - -> Proposed dictionary properties are not final and subject to change based on feedback. - -```js -details = { - installSource: "foo.com", - attributionId: "bingAdsAug2023", - httpReferrer: "https://bar.com/", - installTimestamp: "2023-08-16 10:30:00 UTC" -} -``` - -### Web Install API (Cross-domain installation) - -With the addition of the [Web Install API][Web Install API], there exists a possibility of cross-domain -browser-initiated installations that may have attribution information through the new capability -of webapps to install other webapps. The Web Install API contains a `referral-info` parameter for its installation -method, which contains an object that can hold arbitrary attribution information. The UA can capture the information -contained in `referral-info` at install time so that the Acquisition Info API can return the data later. - -#### Example cross-domain use case - -For example, a user clicks on a Bing ads campaign on `bar.com` that navigates to the Microsoft Store website (`apps.microsoft.com`) product landing -page for `foo.com`. `foo.com` may have defined that they want the Microsoft Store to also pass the region of installation and version of the app -being installed as part of the acquisition information. This could be accomplished in the following fashion: - -From `apps.microsoft.com`: - -```js -if ('install' in navigator) { - // Build the referral-info object - var referralInfo = { - region: "US", - installVersion: "1.0.0.0" - }; - // Web install with additional attribution information - const appInstalled = await navigator.install("https://www.foo.com/app", "https://www.foo.com/install_url", {"referral-info": referralInfo}); -} -``` - -Result of a `navigator.acquisitionInfoProvider.getDetails()` call -from the installed web app, `foo.com`: - -```js -details = { - installSource: "apps.microsoft.com", - attributionId: "bingAdsAug2023", - httpReferrer: "https://bar.com/", - installTimestamp: "2023-08-16 10:30:00 UTC", - region: "US", - installVersion: "1.0.0.0" -} -``` - -## Other attribution cases - -### Policy applications - -It's possible that a policy app could have associated attribution, perhaps implemented through its URL parameters. In this case queries -to the API should reflect the type of app in its install source as well as that attribution information which will follow the same property -convention as browser installations. - -```js -details = { - installSource: "Policy", - attributionId: "bingAdsAug2023" -} -``` - -### Synced applications - -When PWAs are synced across devices for the same user, the UA may make it easier to install that same set of PWAs. In this case, -the acquisition information that is recorded for the originally acquired application should be maintained to be the same for all -other installations. This applies to any and all other installation for the same user profile across any other device supported -by the UA. - -For example, a user may discover App A through an ad campaign run on the Microsoft Store. The user proceeds to install App A on -Device A. Running `getDetails()` from the newly acquired app would return the following: - -```js -details = { - installSource: "apps.microsoft.com", - attributionId: "adCampaign", - ... // Additional attribution information -} -``` - -The same user logs in on Device B and logs into the profile that installed App A on Device A. Sync would install App A once again -but on Device B, at which point the `getDetails()` payload would return the following: - -```js -details = { - installSource: "apps.microsoft.com", - attributionId: "adCampaign", - sync: true, - ... // Additional attribution information -} -``` - -The `installSource`, `attributionId`, and *any other attribution information* that existed on Device A is passed on. Addtionally, -a boolean property called `sync` is added to the dictionary which indicates that the app in question is a synced application -brought over from a previous device. This ensures that we are able to accurately track which users were historically impacted -by which acquisition campaigns, even across devices. - -## Considered Alternatives - -### Flattening the API - -Right now, there is only one method available under the `acquisitionInfoProvider` attribute, so it seems a natural course of action -to remove the inbetween layer of `acquisitionInfoProvider` and instead have a `navigator.getAcquisitionInfoDetails()` method. - -```js -// Detecting if the API is supported in this context. -if (getAcquisitionInfoDetails in navigator) { - try { - let details = await navigator.getAcquisitionInfoDetails(); - // Use the returned dictionary here. - ... - } -} -``` - -However, doing so eliminates the possibility of future additional functionality attached to the attribute. In this case, ensuring -all of the acquisition info related methods remain under `acquisitionInfoProvider` provides clean categorization rather than cluttering -navigator with multiple possible methods. - -### Attaching to the Get Installed Related Apps API - -The possiblity of attaching acquisition as metadata to the [`navigator.getInstallRelatedApps()`][gIRA] API as it already returns -installed app data such as `url` and `platform` exists. - -```js -const relatedApps = await navigator.getInstalledRelatedApps(); -relatedApps.forEach((app) => { - console.log(app.installSource, app.campaignId); -}); -``` - -However, considering the information that the method covers, it likely isn't -the correct place to put acquisition related information. It's not a clear user experience mapping to call the Get Installed Related Apps -API to get acquisition related information. Additionally, since in the proposed solution for the Acquisition Info API a myraid of acquistion -related attributes is able to be returned, the Get Installed Related Apps API return fields would quickly become overcrowded with unrelated information. - -### Non-API web app start-up params - -When an installed web application is launched, acquisition information such as campaign ID could be inserted into the launch URL parameters for the web app. -However, this would mean any networked communications would have to take place during the start-up process of the web application, and acquisition information would -only be retrievable on the landing page of the installed web application prior to any navigation within the page. The flexibility of the acquisition information would -also become greatly limited without overcrowding the query parameters. - -## Privacy and Security Considerations - -1. _Lack of validation_: It is assumed that the install source does not perform any validation - or sanitation of the acquisition data retrieved itself. - -2. _User control regarding acquisition information_: For browser installed web apps, it is recommended that the UA clears saved acquisition information - if the user clears cached site data. For store installed web apps, we recognize that different vendors may have specific preferences and requirements - regarding this aspect, so we defer to the individual install sources for handling of stored acquisition information. - -3. _Potential misuse of acquisition identifiers_: Acquisition identifiers could potentially be - manipulated or obscured by ad networks or referral sources, similar to the existing practices - in advertising. However, this behavior is not specific to the proposed API and is already - possible with ad campaigns. - -This API can only be invoked in a top-level [secure context][secure context]. This is in order to prevent attackers from accessing privacy information that -is being exposed in this API. - -## Glossary - -| Term | Definition | -| ----------- | ------------------------------------------------------------------- | -| Attribution | Identifying the source that led to an installation. | -| Acquisition | Installation of the application in question. | -| PWA | Progressive Web App | -| UWP | Universal Windows Platform | -| API | Application Programming Interface | -| WinRT | Windows Runtime | -| Store | Microsoft Store | -| App | Application | -| Campaign ID | A unique identifier that is associated with a specific ad campaign. | -| UA | User Agent | - -[WinRT API]: https://learn.microsoft.com/en-us/uwp/api/windows.services.store.storecollectiondata.campaignid -[display mode]: https://developer.mozilla.org/en-US/docs/Web/Manifest/display -[Play Install Referrer API]: https://developer.android.com/google/play/installreferrer -[Apple Ads Attribution API]: https://developer.apple.com/documentation/ad_services -[secure context]: https://w3c.github.io/webappsec-secure-contexts/ -[Web Install API]: https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/WebInstall/explainer.md -[gIRA]: https://web.dev/get-installed-related-apps/#use - -## Open Questions - -### How should we be filtering the information returned in `getDetails()`? - -The flexibility outlined in the current return fields offers a potential vulnerability in not filtering the -information being surfaced. This means that fields could even contain unintended information. -If we were to mitigate this in scoping down the fields that are returned down to specific values or lengths, -what would that look like? What values are we interested in across all platforms? diff --git a/AnimationSmoothness/explainer.md b/AnimationSmoothness/explainer.md deleted file mode 100644 index fae6b470c..000000000 --- a/AnimationSmoothness/explainer.md +++ /dev/null @@ -1,192 +0,0 @@ -# A More Precise Way to Measure Animation Smoothness - -[comment]: < ** (*Same-Origin*) > - -Authors: [Jenna Sasson](https://github.com/jenna-sasson) - -## Status of this Document - -This document is a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. - -* This document status: **Active** -* Expected venue: [W3C Web Performance Working Group](https://www.w3.org/groups/wg/webperf/) -* **Current version: this document** - -## Introduction - -Smooth web animation is essential for a positive user experience. To understand the user’s experience with animation, quantifying their experience is an important initial step that allows web and browser developers to optimize their pages/engines and generate a more pleasing user experience. - -Various metrics have been used in the past to try and understand the user’s experience in this space. Some of these were accessible to the webpage, while others were internal browser metrics. Examples of these include: - -* Framerate – the number of frames displayed to the user over time. -* Frame latency – the time it takes for a single frame to work through a browser’s pipelines and display to the user. -* Interaction to Next Paint – the time from a user interaction until the result of that interaction is displayed to the user. -* Consistency of the animation – a measure of how consistent the framerate is over time. -* High framerate variations – framerate classifications that differentiate between changes the user may not notice (variations but still high framerates) and those they may notice (variations involving transitions between high and low framerates). -* Completeness of content – frame classification that includes information about whether the frame includes updates from all desired sources for a given display update or only a subset (or none) of them. - -This proposal attempts to define an API that offers a comprehensive quantification of the user’s experience regarding animation smoothness, enabling developers to create better user experiences. - -## Goals - -* Webpage accessible API that captures user-perceived framerate accurately, taking into account both the main and compositor threads. -* An approach that doesn’t cause webpage performance regressions. -* Enabling a web developer to control what time interval is considered. -* The solution will measure as many of the above properties as possible - -## Non-goals - -* Improving/controlling animation smoothness. This proposal is purely for an API to better understand existing behavior. -* Evaluate an individual animation’s smoothness. The API is focused on the user’s overall experience for entire browser window’s content. - -## User Research - -## Use Cases - -### 1. Web Developers Understanding On-demand Animations - -Animation smoothness can be difficult to measure when relating to user interaction. An average metric isn't always effective because in certain apps, animation begins on a user click. There doesn't need to be a continuous measurement since without a user click, there is no animation. Some examples of this include scrolling a long grid or document, selecting or highlighting large areas of the screen, resizing images, dragging objects across the screen, or animations triggered by mouse movement or clicks. - -### 2. Measuring animation and graphics performance of browsers - -The public benchmark, MotionMark, measures how well different browsers render animation. For each test, MotionMark calculates the most complex animation that the browser can render at certain frame rate. The test starts with a very complex animation that makes the frame rate drop to about half of the expected rate. Then, MotionMark gradually reduces the animation's complexity until the frame rate returns to the expected rate. Through this process, MotionMark can determine the most complex animation that the browser can handle while maintaining an appropriate frame rate and uses this information to give the browser a score. To get an accurate score, it is crucial that MotionMark can measure frame rate precisely. Currently, MotionMark measures frame rate based on rAF calls, which can be impacted by other tasks on the main thread besides animation. It also doesn't take into account animations on the compositor thread. The method using rAF to measure frame rate doesn't reflect the user's actual experience. - -### 3. Gaming - -Higher frames per second (fps) lead to smoother animations and a more enjoyable gaming experience. Additionally, poor animation can significantly impact gameplay elements, like how quickly characters can move or decisions can be made, which affects the overall user experience. In continual tension with the desire for smooth animations, game developers are constantly striving to make their visuals higher quality and immersive. To guarantee smooth gameplay, developers need a way to understand how their game’s animations are performing. - -### 4. Testing animation performance on different hardware - -Testing the performance of animation on different hardware/browser combinations may expose performance issues that could not be seen with existing metrics. - -### 5. Improving animation libraries - -Animation libraries measure frame rate in different ways. For example, GSAP and PixiJS use tickers to measure FPS, but the developer must add custom logic to run each tick to measure frame rate. Three.js uses a second library, stats.js, to measure frame rate, and anime.js and Motion libraries use rAF calling. It would be beneficial for libraries to have a built-in way to measure FPS. A built-in method would be more convenient and allow for more seamless integration with each library's animation loop, leading to more accurate results. Immediate feedback would make debugging and resolving issues easier. Ideally, this would also standardize a way to measure FPS leading to consistency across libraries. - -## Prior Art - -The below prior art exists for understanding animation smoothness today. - -### RAF - -#### Description - -One of the current ways to measure smoothness is by measuring frames per second (fps) using `requestAnimationFrame()` polling. - -Animation frames are rendered on the screen when there is a change that needs to be updated. If they are not updated in a certain amount of time, the browser drops a frame, which may affect animation smoothness. - -The rAF method has the browser call a function (rAF) to update the animation before the screen refreshes. By counting how often rAF is called, you can determine the FPS. If the browser skips calling rAF, a potential frame rendering opportunity was skipped. This method helps understand how well the browser handles animations. - -#### Limitations - -Using rAF to determine the FPS can be energy intensive and inaccurate. This approach can negatively impact battery life by preventing the skipping of unnecessary steps in the rendering pipeline. While this is not usually the case, using rAF inefficiently can lead to dropped or partially presented frames, making the animation less smooth. It’s not the best method for understanding animation smoothness because it does not take into account factors like compositor offload and offscreen canvas. While rAF can be useful, it isn’t the most accurate and relying on it too heavily can lead to energy waste and suboptimal performance. - -#### [Reference](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame) - -### Long Animation Frames API - -#### Description - -A long animation frame (LoAF) occurs when a frame takes more than 50ms to render. The Long Animation Frames API allows developers to identify long animation frames by keeping track of the time it takes for frames to complete. If the frame exceeds the threshold, it is flagged. - -#### Limitations -requestAnimationFrame() and long animation frames serve different purposes. requestAnimationFrame() schedules a rendering opportunity and includes a callback to observe that rendering, while long animation frames only observe rendering events without participating in their scheduling. LoAF focuses on pinpointing performance issues and responsiveness. Although both APIs track animation frames and provide different metrics at varying frequencies, neither offer the precision required for measuring animation smoothness. - -#### [Reference](https://github.com/w3c/long-animation-frames) - -### RequestVideoFrameCallback - -#### Description - -requestVideoFrameCallback() is a method used with HTMLVideoElement. It allows a developer to run a callback every time a new frame of video is about to appear. It functions similarly to requestAnimationFrame() but is specific to video elements. requestVideoFrameCallback() is called based on the video's frame rate, while rAF is called based on the display's refresh rate. Additionally, requestVideoFrameCallback() provides the callback time and video frame metadata, whereas rAF only provides a timestamp. - -#### Limitations - -requestVideoFrameCallback() can offer developers metrics like video frame render delays and Real-Time Transport Protocol (RTP) timestamps, and it's relatively easy to implement, but there are some limitations. requestVideoFrameCallback() may be inconsistent between browsers, with varying timestamp references for the same video frame. It may also be unreliable for precisely measuring frame times, as callbacks can be delayed or skipped if the system is busy, especially on slower machines. - -#### [Reference](https://developer.mozilla.org/en-US/docs/Web/API/HTMLVideoElement/requestVideoFrameCallback) - -### FPS-Emitter - -#### Description - -In the past, Edge had a library called fps-emitter that emits an update event. Once a new instance of FPS-emitter is called, it starts tracking frames per second via the rAF method. When the FPS changes, the result is returned as an EventEmitter. This method builds off the rAF method described above and faces similar limitations. - -#### Limitations - -While FPS-emitter is a helpful way to measure events that slow down performance, it is not the most precise way to measure the actual smoothness of the animation. This is because there are other processes in addition to UI-blocking events executing independently to render the animation, which can impact the user perceived frame rate and can't be detected just by looking at rAF calls. - -#### [Reference](https://github.com/MicrosoftEdge/fps-emitter) - -## Proposed Approach - -There is no proposed approach yet identified by this explainer. Instead, there are a variety of alternatives that we would like to discuss with the broader community. - -### Option 1: Direct Query - -This solution would involve querying the animation smoothness data directly. JavaScript would call an API that measures some frame information at a specific point in time. To measure the frame information, the API would be called multiple times, using the values to calculate an average frame rate. - -`window.frameinfo() ` or `performance.frameinfo()` - -### Option 2: Start and End Markers - -JavaScript Performance markers are used to track points in time. In this solution, developers could mark a start and end point on the performance timeline and measure the duration between the two markers, with frame information as a property. - -* 2a - - `performance.mark("myMarker")` - - `window.frameinfo("myMarker")` <- Framerate since that marker - -* 2b - - `performance.mark("myMarker")` - - `performance.measure("myMarker", "endMarker")` - -This option works similarly to the [Frame Timing API](https://wicg.github.io/frame-timing/#dom-performanceframetiming) by using start and end markers. Frame startTime and frame endTime are returned by the Performance object's now() method; the distance between the two points is frame duration. When the duration of a frame is too long, it is clear that there was a rendering issue. A PerformanceFrameTiming object is created and added to the performance entry buffer of each active web page, which developers can then access for information. - -### Option 3: Event Listener - -Adding an event listener for frame rate changes would alert developers about large drops in frame rate since it would not be necessary to know if the rate drops by a frame or two. Instead, the developer could set the event listener to alert when the frame rate drops by n. Or, similarly to the long task API's duration threshold, the developer could set a min and max fps. The event listener would fire only if the FPS is above the max or below the min. - -`window.addEventListener("frameratechange", (event) =>{doSomething();})` - -### Option 4: Performance Observer - -This option works similarly to both [LoAF API](https://github.com/w3c/long-animation-frames) and the [Paint Timing API](https://www.w3.org/TR/paint-timing/), which both use the performance observer and follow a pattern that developers expect to use when improving performance. When observing long animation frames, developers can specify the entry types they want to the performance observer to processes. For example, if performance observer reports which animation frames are too long, the event listener would send an alert when the frame rate drops by a certain amount. The two APIs differ in the amount of information given. The LoAF API can give more specific metrics for long animations, while event listeners provide a more general way of monitoring frame rate. - -```javascript -function perfObserver(list, observer) { - list.getEntries().forEach((entry) => { - if (entry.entryType === "frameSmoothness") { - console.log(${entry.name}'s startTime: ${entry.startTime}); - } - }); -} - -const observer = new PerformanceObserver(perfObserver); -observer.observe({ entryTypes: ["frameSmoothness"] }); -``` - -## Alternatives Considered - -For the event listener scenario, it was determined that using granularity would not give a useful measure of frame info due to lack of detail. The granularity was modeled after the compute pressure API. -`window.addEventListener("frameratechange", (event) =>{doSomething();})` - -## Concerns/Open Questions - -1. The user-perceived smoothness is influenced by both the main thread and the compositor thread. Accurate measurement of frame rates must account for both. Since the compositor thread operates independently of the main thread, it can be difficult to get its frame rate data. However, an accurate frame rate measurements needs to take into account both measurements. -1. Similar to the abandoned [Frame Timing interface](https://wicg.github.io/frame-timing/#introduction). We are currently gathering historical context on how this relates and why it is no longer being pursued. -1. Questions to Consider: - * Should content missing from the compositor frame due to delayed tile rasterization be tracked? - * Should the fps be something that a web page can query anytime? Or only reported out when the browser misses some target? - * How will this API work with variable rate monitors or on screens with higher refresh rates? - * How will this API take into account situations where the compositor thread produces frames that are missing content from the main thread? - * How will this API measure both the compositor and the main thread when they may have differing frame rates. The compositor thread can usually run at a higher frame rate than the main thread due to its simpler tasks and multiple threads and processes contribute to smoothness metrics. - * Should a developer be able to target a subset of time based on an interaction triggering an animation? - * How will this API be interoperable and return meaningful metrics applicable to all browser engines? - -## Acknowledgements - -Thank you to [Sam Fortiner](https://github.com/sfortiner), Olga Gerchikov, and [Andy Luhrs](https://github.com/aluhrs13) for their valuable feedback. diff --git a/AtRuleFeatureDetection/explainer.md b/AtRuleFeatureDetection/explainer.md deleted file mode 100644 index 7c7bef704..000000000 --- a/AtRuleFeatureDetection/explainer.md +++ /dev/null @@ -1,140 +0,0 @@ -# At-Rule Feature Detection - -## Authors: - -- [Kevin Babbitt](https://github.com/kbabbitt) (Microsoft) - -## Participate -- [Issue tracker](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/AtRuleFeatureDetection) - -## Table of Contents - -<!-- START doctoc generated TOC please keep comment here to allow auto update --> -<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> - - -- [At-Rule Feature Detection](#at-rule-feature-detection) - - [Authors:](#authors) - - [Participate](#participate) - - [Table of Contents](#table-of-contents) - - [Introduction](#introduction) - - [User-Facing Problem](#user-facing-problem) - - [Goals](#goals) - - [Non-goals](#non-goals) - - [Proposed Approach](#proposed-approach) - - [Detect whether an at-rule name is recognized at all](#detect-whether-an-at-rule-name-is-recognized-at-all) - - [Detect whether an at-rule, with optional prelude and/or block, is supported](#detect-whether-an-at-rule-with-optional-prelude-andor-block-is-supported) - - [Special case: The forgiving grammar of media queries](#special-case-the-forgiving-grammar-of-media-queries) - - [Detect whether a given declaration is supported within an at-rule block](#detect-whether-a-given-declaration-is-supported-within-an-at-rule-block) - - [Accessibility, Privacy, and Security Considerations](#accessibility-privacy-and-security-considerations) - - [Stakeholder Feedback / Opposition](#stakeholder-feedback--opposition) - - [References \& acknowledgements](#references--acknowledgements) - -<!-- END doctoc generated TOC please keep comment here to allow auto update --> - -## Introduction - -Feature detection is a [W3C TAG design principle](https://www.w3.org/TR/design-principles/#feature-detect) -and a tool that Web authors rely on for graceful degradation of their pages. - -[CSS Conditional Rules](https://www.w3.org/TR/css-conditional/) introduces the `@supports` rule and API -extensions to allow authors to feature-detect CSS properties. -In this explainer, we describe an expansion to feature detection in CSS that allows authors to detect -support for at-rules, including specific features of at-rules. - -## User-Facing Problem - -There have been many scenarios described that call for feature detection of at-rules and sub-portions of at-rule grammar. Some examples: - -- In the [Blink intent-to-ship thread for `@property`](https://groups.google.com/a/chromium.org/g/blink-dev/c/3ygpsew53a0/m/Ar_OPlthAwAJ), it was pointed out that authors need a mechanism to detect support so that they can fall back to `CSS.registerProperty()` if needed. -- A [StackOverflow question](https://stackoverflow.com/questions/44244221/is-it-possible-to-do-a-css-supports-check-on-a-media-rule) asks whether it is possible to detect support for `@media` features, for example to detect if the user agent can return a yes/no answer for `@media (pointer)`. -- A [Mastodon post](https://mastodon.social/@xro/113106213499516093) asks whether it is possible to test for style query support. -- At time of writing, several in-development CSS features propose to implement new at-rules. These include [`@sheet`](../AtSheet/explainer.md) as well as [CSS Functions and Mixins](https://css.oddbird.net/sasslike/mixins-functions/). - -### Goals - -Allow authors to feature-detect newly introduced at-rules. - -At-rule feature detection should be available in all contexts where CSS allows conditioning based on support -of a feature. This includes, but is not limited to, -`@supports`, `CSS.supports()`, `@import ... supports()`, and `@when supports()`. - -### Non-goals - -- Allow authors to feature-detect new enhancements to existing at-rules, such as: - - New media query features and other additions to at-rule preludes - - New descriptors that may be introduced to rules such as `@font-face` -- Detect non at-rules like `@charset`: - -#### CSS `@charset` -The CSS `@charset` rule, despite its appearance, is -[not an at-rule](https://drafts.csswg.org/css-syntax/#charset-rule). -Rather, `@charset` is a marker that can appear only as the first few bytes of a stylesheet file. It signals to -the user agent what character encoding should be used to decode the contents of the stylesheet. - -CSS feature detection has, since its inception, relied on the test of "does it parse successfully?" to -answer the question of whether a property-value pair is supported. Because of its special nature, user agents -may have a different parsing implementation for `@charset` compared to true at-rules, which might not be as easily -reused for feature detection. - -At the same time, there is far less of a use case for feature-detecting `@charset` compared to true at-rules. -`@charset` is part of the [Baseline](https://developer.mozilla.org/en-US/docs/Web/CSS/@charset) feature set long -supported in all major browsers. On the modern Web, the encouraged solution for character encoding issues in -CSS is to use UTF-8. - -Accordingly, this explainer does not propose making `@charset` feature-detectable using `at-rule()`. - -#### Context Aware feature detection -As [mentioned before](#detect-whether-an-at-rule-name-is-recognized-at-all), the `at-rule()` feature returns true if the at-rule name is recognised in any context. This introduces the risk of false positives. As per CSSWG resolutions [#12622](https://github.com/w3c/csswg-drafts/issues/12622) and [#6966](https://github.com/w3c/csswg-drafts/issues/6966#issuecomment-3205037703) the new `@supports-condition` at-rule is introduced as a way to define and name complex support queries, including the ones that need to account for context. - -## Proposed Approach - -The `at-rule()` function can be used for feature detection in the following way: - -### Detect whether an at-rule name is recognized at all - -The `at-rule()` function can be passed just an at-rule name. -The result is true if the implementation would recognize it as an at-rule in any context, false otherwise. -This form is useful for detecting entire new features implemented as at-rules, including features such as -[`@starting-style`](https://www.w3.org/TR/css-transitions-2/#defining-before-change-style) -that do not appear at top-level stylesheet context. - -```css -/* Use reusable styles encapsulated in @sheet if supported. */ -@import "reusable-styles.css" supports(at-rule(@sheet)); - -/* Fall back to tooling-expanded styles if not. */ -@import "expanded-styles.css" supports(not(at-rule(@sheet))); - -/* ... */ - -/* Set up a pop-in transition with @starting-style if it's supported. */ -@supports at-rule(@starting-style) { - .card { - transition-property: opacity, transform; - transition-duration: 0.5s; - @starting-style { - opacity: 0; - transform: scale(0); - } - } -} -``` - -## Accessibility, Privacy, and Security Considerations - -No accessibility, privacy, or security considerations have been identified for this feature. - -## Stakeholder Feedback / Opposition - -Feedback from other implementors will be collected as part of the Blink launch process. - -## References & acknowledgements - -This explainer describes a feature which others have already put significant work into. -Many thanks for the efforts of: - -- Fuqiao Xue, who brought the original feature request to the CSSWG in Issue [#2463](https://github.com/w3c/csswg-drafts/issues/2463). -- Tab Atkins-Bittner, who proposed expansion of the grammar in Issue [#6966](https://github.com/w3c/csswg-drafts/issues/6966). -- Steinar H. Gunderson, who implemented the behavior that the CSSWG resolved on in [#2463](https://github.com/w3c/csswg-drafts/issues/2463) in Chromium behind a feature flag. -- Anders Hartvoll Ruud, who raised important clarifying questions in Issues [#11116](https://github.com/w3c/csswg-drafts/issues/11116), [#11117](https://github.com/w3c/csswg-drafts/issues/11117), and [#11118](https://github.com/w3c/csswg-drafts/issues/11118). diff --git a/AtRuleFeatureDetection/security-privacy-questionnaire.md b/AtRuleFeatureDetection/security-privacy-questionnaire.md deleted file mode 100644 index f53b3132f..000000000 --- a/AtRuleFeatureDetection/security-privacy-questionnaire.md +++ /dev/null @@ -1,95 +0,0 @@ -# Security and Privacy Questionnaire for `@supports at-rule` - -- _What information does this feature expose, and for what purposes?_ - - The feature exposes the browser's set of supported CSS "at-rules". This is important as it can allow authors to implement graceful degradation of their pages. The `@supports` rule already allows authors to feature-detect CSS properties, and the `at-rule()` feature aims to allow feature detection for at-rules. - - No identifying information is exposed. - -- _Do features in your specification expose the minimum amount of information necessary to implement the intended functionality?_ - - Yes, only the minimum amount of information is exposed. - -- _Do the features in your specification expose personal information, personally-identifiable information (PII), or information derived from either?_ - - No PII or derived information is exposed. - -- _How do the features in your specification deal with sensitive information?_ - - The feature does not deal with any sensitive information. - -- _Does data exposed by your specification carry related but distinct information that may not be obvious to users?_ - - No. - -- _Do the features in your specification introduce state that persists across browsing sessions?_ - - No. - -- _Do the features in your specification expose information about the underlying platform to origins_ - - Yes. The feature allows a developer to tailor site UX depending on at-rule feature support. This is useful mostly for newer features that are launching, but once an at-rule is available as a standard and implemented in the web platform it is something that is unlikely to change. - -- _Does this specification allow an origin to send data to the underlying platform?_ - - No. - -- _Do features in this specification enable access to device sensors?_ - - No. - -- _Do features in this specification enable new script execution/loading mechanisms?_ - - No. - -- _Do features in this specification allow an origin to access other devices?_ - - No. - -- _Do features in this specification allow an origin some measure of control over a user agent’s native UI?_ - - No. - -- _What temporary identifiers do the features in this specification create or expose to the web?_ - - None. - -- _How does this specification distinguish between behavior in first-party and third-party contexts?_ - - The functionality isn't affected between first and third party contexts. - -- _How do the features in this specification work in the context of a browser’s Private Browsing or Incognito mode?_ - - They work the same. - -- _Does this specification have both "Security Considerations" and "Privacy Considerations" sections?_ - - Yes. - -- _Do features in your specification enable origins to downgrade default security protections?_ - - No. - -- _What happens when a document that uses your feature is kept alive in BFCache (instead of getting destroyed) after navigation, and potentially gets reused on future navigations back to the document?_ - - Browser support for a given at-rule does not change across navigations. Depending on the implementation, the browser might re-evaluate `@supports at-rule(...)` conditions, or it might use a previously cached evaluation result. - -- _What happens when a document that uses your feature gets disconnected?_ - - N/A. - -- _Does your spec define when and how new kinds of errors should be raised?_ - - N/A. - -- _Does your feature allow sites to learn about the user’s use of assistive technology?_ - - No, CSS does not currently have any dedicated at-rule specifically designed for assistive technology. - -- _What should this questionnaire have asked?_ - - N/A. - - - - diff --git a/AtSheet/explainer.md b/AtSheet/explainer.md deleted file mode 100644 index 3a9e5bf5a..000000000 --- a/AtSheet/explainer.md +++ /dev/null @@ -1,293 +0,0 @@ -# `@sheet` - -## Authors: - -- Andy Luhrs -- Kurt Catti-Schmidt - -Much of this explainer is consolidating and iterating on a CSSWG discussion around [Justin Fagnani](https://github.com/justinfagnani)'s proposal for multiple stylesheets in a single file [here](https://github.com/w3c/csswg-drafts/issues/5629). - -## Participate -- [Issue tracker](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/AtSheet) -- [Discussion forum](https://github.com/w3c/csswg-drafts/issues/5629) - -## Status of this Document - -This document is intended as a starting point for engaging the community and -standards bodies in developing collaborative solutions fit for standardization. -As the solutions to problems described in this document progress along the -standards-track, we will retain this document as an archive and use this section -to keep the community up-to-date with the most current standards venue and -content location of future work and discussions. - -* This document status: **Active** -* Expected venue: [CSS Working Group](https://www.w3.org/Style/CSS/) -* Current version: this document - -## Introduction -When developing web components, web authors often encounter challenges with distributing global styles into shadow roots and sharing styles across different shadow roots. Declarative shadow DOM (DSD) enables creation of shadow DOM without JavaScript. However, adding styles to DSD requires the developer to either use JavaScript to put a shared stylesheet into `adoptedStyleSheets`, or to duplicate the styles in a `<style>` element for each component instance. - -Additionally, bundling of stylesheets is difficult for developers who are distributing web components. They either need to ship many small stylesheets, or use workarounds like `@import url("data...")` which are suboptimal for performance and don't interact well with other patterns. - -We propose an enhancement to allow the declaration of new stylesheets via an `@sheet` CSS block and using existing mechanisims such as `@import`, `<link>`, and CSS module script `import` to apply those shared styles to DSDs without the use of JavaScript. - -We're currently investigating this and [Declarative CSS modules](../ShadowDOM/explainer.md) in parallel. - -## Goals -* Allow the reuse of styles in markup-based shadow DOM without requiring JavaScript. -* Allow reuse of styles in markup-based shadow DOM without requiring external network requests. -* Allow web authors to selectively pass in global styles from the parent document. -* Allow component authors to bundle their CSS into a single file. -* Allow named `@sheet` references to fully integrate with existing CSS inclusion methods such as `@import` statements and `<link>` tags. - - -## Non-goals -Some developers have expressed interest in CSS selectors crossing through the Shadow DOM, as discussed in [issue 909](https://github.com/WICG/webcomponents/issues/909#issuecomment-1977487651). While this scenario is related to sharing styles with Shadow DOM elements, it is solving a different problem and should be addressed separately. - -## Proposal - `@sheet` -Create a new `@sheet` CSS block, for separating style sheets with named identifiers. - -```css -@sheet foo { - div { - color: red; - } -} - -@sheet bar { - div { - font-family: sans-serif; - } -} - -div { - color: blue; -} -``` -This stylesheet will create three CSS sheets - The default sheet, `foo`, and `bar`. All following examples will use this stylesheet with the name of `sheet.css`. - -### Importing a specific sheet via `@import` -```html -<style> - @import foo from "sheet.css"; -</style> -``` - -This will import only the rules for `foo` - in this case, the `div { color: red; }` rule. This will *not* import any rules from `sheet.css` outside of "foo". - -### Importing a specific sheet via the `<link>` tag -```html -<link rel="stylesheet" href="sheet.css" sheet="foo" /> -``` - -This will also import only the rules for "foo" - in this case, the `div { color: red; }` rule. This will *not* import any rules from `sheet.css` outside of "foo". - -### Importing a base set of inline styles into a Declarative Shadow DOM - -Shadow DOM isolates styles, but fragment identifiers from the light DOM are global and referenceable from shadow DOM (but not vice versa). - -Combined with [Local References In <link> Tags](../LocalReferenceLinkRel/explainer.md), developers may define a set of styles for their components upfront, -and then reference these styles declaratively from their component. By using inline styles instead of an external .css file, a Flash of Unstyled Content (FOUC) can be avoided in many cases. - -This example demonstates importing `@sheet` references from the light DOM into a Declarative Shadow DOM: - -```html -<style id="sheet"> -@sheet foo { - div { - color: red; - } -} -</style> -<template shadowrootmode="open"> - <link rel="stylesheet" href="#sheet" sheet="foo" /> - <span>I'm in the shadow DOM</span> -</template> -``` -or imported from JavaScript: -```html -<script> - import {foo} from './sheet.css' with {type: 'css'}; - ... - shadow.adoptedStyleSheets = [foo]; -</script> -``` - -## Detailed design discussion - -#### Specific Changes to HTML and CSS - -This proposal augments the HTML `<link>` tag by introducing the `sheet` attribute, which scopes the specified style reference to rules within an `@sheet` identifier. - -A separate proposal to support fragment identifiers to same-document `<style>` in the `href` attribute for `<link>` tags is described in [Local References In <link> Tags](../LocalReferenceLinkRel/explainer.md). - -This proposal augments the CSS `@import` syntax by adding the `from` keyword, with comma-separated list of `@sheet` identifiers ( `@import foo, bar from "sheet.css";`). - -The `@sheet` fragment syntax (`<link rel="stylesheet" href="sheet.css#foo" />`) that was agreed upon in https://lists.w3.org/Archives/Public/www-style/2023Apr/0004.html should be revisited with these new applications in mind, as it is not compatible with same-document `<style>` references. - -#### Named Imports with Imperative Shadow DOM - -`sheet.JavaScript` can also be imported via JavaScript as follows: - -```JavaScript -import baz, { bar } from 'sheet.css' with { type: 'css' } -``` - -`baz` will reference style rules outside of any `@sheet` blocks as a Default Import (in this case, the `div { color: blue; } ` rule). - -`bar` will reference style rules within the `@sheet bar` block as a Named Import (in this case, the `div { color: red; } ` rule). - -Named imports may be renamed as part of this import process: - -```JavaScript -import baz, { bar as renamed } from 'sheet.css' with { type: 'css' } -``` - -`bar` will be renamed to `renamed`. - -The default import may be omitted, importing only the named `@sheet`: - -```JavaScript -import { bar } from 'sheet.css' with { type: 'css' } -``` - -Any of these `import` examples can then be used to set the `adoptedStyleSheets` attribute on a Shadow DOM node: - -```JavaScript -import { bar } from 'sheet.css' with { type: 'css' } -document.adoptedStyleSheets = [bar]; -shadowRoot.adoptedStyleSheets = [bar]; -``` - -#### Performance - -`@sheet` has several performance benefits. The primary benefit is reduced network requests, as it allows mutliple stylesheets to be downloaded in one network request. - -`@sheet` can also reduce the work done by the style engine. Currently, Declarative Shadow DOM requires either a) that style rules are duplicated within shadow roots, or b) entire stylesheets are shared via `<link>` tags. `@sheet` allows for granular control over style rules shared between the parent document and shadow roots without additional network requests or duplicated styles. - -Using `@sheet` may also yield some benefits to file compression. With a dictionary-based compression scheme, if two stylesheets contain many similar tokens (e.g. CSS rules and selectors), combining them via `@sheet` and then compressing may yield a higher compression ratio than compressing them as separate files. - -```JavaScript -// The following two imports should only make a single network request. -import { foo } from 'sheet.css' with { type: 'css' }; -import { bar } from 'sheet.css' with { type: 'css' } -``` - -```html -<style> - /* The following two imports should only make a single network request. */ - @import foo from "sheet.css"; - @import bar from "sheet.css"; -</style> -``` - -```html -<!-- The following two link tags should only make a single network request. --> -<link rel="stylesheet" href="sheet.css" sheet="foo" /> -<link rel="stylesheet" href="sheet.css" sheet="bar" /> -``` - -#### Interaction with CSSOM - - -Named `@sheet` rules are a new type of `CSSGroupingRule` with a required `name` attribute reflecting the `@sheet` identifier: - -``` -[Exposed=Window] -interface CSSSheetRule : CSSGroupingRule { - readonly attribute CSSOMString name; -}; -``` - -This also expands the [existing](https://drafts.csswg.org/cssom/#cssstylesheet) CSSOM `CSSStyleSheet` definition with a `StyleSheetList` of nested `CSSStyleSheet` objects to access nested `@sheet` references: - -``` -[Exposed=Window] -interface CSSStyleSheet : StyleSheet { - [SameObject] readonly attribute StyleSheetList nestedStyleSheets; -}; -``` - -The [existing](https://drafts.csswg.org/cssom-1/#cssimportrule) `CSSImportRule` is extended with `sheetNames` and `namedStyleSheets`. - -``` -partial interface CSSImportRule { - [SameObject] readonly attribute SheetNameList sheetNames; - [SameObject] readonly attribute StyleSheetList namedStyleSheets; -}; -``` - -The new `SheetNameList` interface exposes a list of sheet names for a `CSSImportRule`. - -``` -[Exposed=Window] -interface SheetNameList { - readonly attribute unsigned long length; - getter CSSOMString? item(unsigned long index); -}; -``` - -## Considered alternatives - -1. [Declarative CSS Modules](../ShadowDOM/explainer.md) are another mechanism for sharing styles between Declarative Shadow DOM and light DOM without the use of JavaScript. -2. Some additional alternatives to parts of the problems discussed here are discussed in the [Alternate proposals](../ShadowDOM/explainer.md#alternate-proposals) section of that explainer. - -## Open Issues - -1. ~~[#934](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/934) - Whether rules are applied automatically for `@sheet` definitions, or whether they need to be imported to apply. The CSS Working Group did not have a consensus.~~ Rules are not applied by default - they must be deliberately included by sheet identifier. -2. ~~[#935](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/935) - Fragment-only identifiers (without a URL) should allow inline `@sheet` references on the same document to be included globally (even within shadow roots). This wasn't brought up in the CSSWG discussions at all, but is important for DSD without requiring an external file (to avoid FOUC).~~ Same-document local sheet references are supported. -3. [#936](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/936) - Behavior of `@import` - should `@import` be possible within `@sheet` at all, should it be allowed if it's the first/only statement, or should it be blocked? There was discussion of this in the CSSWG, but no conclusion was reached. This was briefly discussed in this CSSWG conversation: https://lists.w3.org/Archives/Public/www-style/2023Apr/0004.html -4. [#937](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/937) - What happens with multiple `@sheet` definitions with the same identifier? First-definition wins, or do they get merged like `@layer`? Again, this was brought up in the CSSWG but not resolved (https://github.com/w3c/csswg-drafts/issues/5629#issuecomment-1498299448). Note that it's possible to have a "Flash of other-styled content" if it's last-defintion-wins, as the first definition may apply, then a later definition from an external CSS file may override it. -5. [#938](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/938) - Do we want to be able to access sheets declared in shadow DOM from light DOM? For example: -```html -<template shadowrootmode="open"> - <style id="sheet"> - @sheet foo { - div { - color: red; - } - } - </style> - <link rel="stylesheet" href="#foo" sheet="foo" /> - <span>I'm in the shadow DOM</span> -</template> - -<link rel="stylesheet" href="#sheet" sheet="foo" /> -<span>I'm in the light DOM</span> -``` -6. [#939](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/939) - The name `nestedStyleSheets` is up for discussion. -7. [#939](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/939) - Should we add `name` to the `StyleSheet` interface or overload the existing `title` attribute instead? -8. ~~[#940](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/940) - If a stylesheet contains named `@sheet` references *and* rules outside of the `@sheet` references, what happens in all cases when a fragment identifier is *not* specified? For example:~~ - -sheet.css: - -```css -@sheet foo { - div{ - color: red; - } -} -div { - color: blue; -} -``` - -```html -<style> - @import "sheet.css" /* Does the @sheet "foo" get dropped? */ -</style> -<link rel="stylesheet" href="sheet.css"> <!-- Does the @sheet "foo" get dropped? --> -``` - -Sheet "foo" gets dropped, as it was not explicitly imported. - -## References & acknowledgements -Many thanks for valuable feedback and advice from: - -- Alison Maher -- Daniel Clark -- Justin Fagnani -- Noam Rosenthal -- Tab Atkins Jr. -- Tien Mai -- Westbrook Johnson diff --git a/AudioContextInterruptedState/explainer.md b/AudioContextInterruptedState/explainer.md deleted file mode 100644 index b503e596d..000000000 --- a/AudioContextInterruptedState/explainer.md +++ /dev/null @@ -1,235 +0,0 @@ -# AudioContext Interrupted State - -## Authors: - -- [Gabriel Santana Brito](https://github.com/gabrielsanbrito) -- [Steve Becker](https://github.com/SteveBeckerMSFT) - -## Participate -- https://github.com/WebAudio/web-audio-api/issues/2392 - -## Status of this Document -This document is a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. - -* This document status: **Archived** -* Expected venue: [Audio WG](https://www.w3.org/groups/wg/audio/) -* Current version: this document - -## Introduction - -The [Web Audio API] is widely used to add advanced audio capabilities to web applications, like web-based games and music applications. One of the API's features is the [AudioContext ] interface, which represents an audio graph. An `AudioContext` can find itself in one of three states: [`"suspended"`], [`"running"`], or [`"closed"`]. Once an `AudioContext` is in the `"running"` state, it can only pause media playback by transitioning to the `"suspended"` state when, and only when, user code calls [`AudioContext.suspend()`] on this `AudioContext`. However, there are situations where we might want to let the User Agent (UA) decide when to interrupt playback - e.g., the proposed [`"media-playback-while-not-visible"` permission policy], the proposed [Audio Session API], or during a phone call where the calling application will need exclusive access to the audio hardware. To support these scenarios, we propose adding a new `"interrupted"` state to the [`AudioContextState`] enum. - -## Goals - -The main goal of this proposal is to allow the UA to be able to interrupt `AudioContext` playback when needed, given that there are a couple of user scenarios - e.g., incoming phone calls, screen lock, etc - and proposed web API's that could make good use of this functionality. - -### "media-playback-while-not-visible" permission policy - -The [`"media-playback-while-not-visible"` permission policy] allows the UA to pause media playback on unrendered iframes. However, since the [Web Audio API does not allow the UA to suspend audio playback][audiocontext-suspended-by-user-slot], the proposed permission policy has no mechanism available to pause media playback. - -### Audio Session API - -Similarly, the [Audio Session API] could also make use of the `"interrupted"` state. Whenever the [`AudioSessionState`] is `"interrupted"`, the AudioContext of the document would also transition to the `"interrupted"` state. As a matter of fact, the `AudioContext`'s `"interrupted"` state has been [implemented on WebKit](https://github.com/WebKit/WebKit/blob/1e8ea6e4777297ce82e6c911caa7cce2cc32e6a9/Source/WebCore/Modules/webaudio/AudioContextState.idl) and is currently being used by the Audio Session API. - -In Safari, on MacOS Sonoma 14.5, if there an active web page with the `AudioContext` in the `"running"` state, and if we set the audio session type to `"auto"` with `navigator.audioSession.type = "auto"`; whenever the laptop's screen is locked, the AudioContext will transition to the `"interrupted"` state. When the screen is unlocked, the state will automatically switch to `"running"` again. - -### Exclusive access to audio hardware - -There are scenarios where another application may acquire exclusive access to audio hardware. For example, when a phone call is in progress. In this situation, if there is already an `AudioContext` in the `running` state, it makes sense that the UA pauses the `AudioContext` using the `interrupted` state while the call is in progress. - -## The `"interrupted"` state - -This explainer proposes adding the `"interrupted"` state to the `AudioContextState` enum, as shown below: - -```js -enum AudioContextState { - "suspended", - "running", - "closed", - "interrupted" -}; -``` - -Whenever an `AudioContext` transitions to either `"suspended"` or `"interrupted"`, audio playback would halt. The main difference between `"suspended"` and `"interrupted"` is that an `AudioContext` can only move to the `"suspended"` state if the [user triggered][audiocontext-suspended-by-user-slot] the state change; while the UA can transition the context to the `"interrupted"` state if there is a need for that. - -With the addition of the `"interrupted"` state, the following state transitions would also be introduced: -- `"running"` -> `"interrupted"`; - - Would happen whenever the UA needs to interrupt audio playback. -- `"suspended"` -> `"interrupted"`; - - Shouldn't happen automatically. This transition should happen only if there is an ongoing interruption and [`AudioContext.resume()`] is called. -- `"interrupted"` -> `"running"`; - - By the time that the cause of the interruption ceases to exist, the UA can transition to `"running"` if audio playback is allowed to resume automatically. -- `"interrupted"` -> `"suspended"`; and - - By the time that the cause of the interruption ceases to exist, the UA can transition to `"suspended"` if audio playback is **not** allowed to resume automatically or if [`AudioContext.suspend()`] has been called during the interruption. -- `"interrupted"` -> `"closed"`. - - The `AudioContext`'s state should move immediately to `"closed"` when `AudioContext.close()` is called. - -The state transition from `"suspended"` to `"interrupted"` should not happen automatically, due to privacy concerns. Doing this transition automatically every time an interruption occurs might unnecessarily expose too much information to web pages - e.g., when a phone call comes in. To prevent fingerprinting in this scenario, the AudioContext should remain in the `"suspended"` state, even though it is interrupted behind the curtains. However, calling [`AudioContext.resume()`] should trigger the state change from `"suspended"` to `"interrupted"` if the interruption is still active. Therefore, while in the `"suspended"` state with an ongoing interruption: -- Calling `AudioContext.resume()` would return a rejected promise and transition to the `"interrupted"` state; -- Calling `AudioContext.suspend()` is a NOOP; - -Finally, while in the `"interrupted"` state: -- Calling `AudioContext.resume()` would return a rejected promise; -- Calling `AudioContext.suspend()` would change the `AudioContext`'s state to `"suspended"` and return a promise that should resolve when the `AudioContext`'s state has transitioned to `"suspended"`. -- Calling `AudioContext.close()` would return a promise that should resolve when the `AudioContext`'s state has transitioned to `"closed"` - -## Key scenarios - -The `"interrupted"` state can be used by a couple of proposed API's, like the [`"media-playback-while-not-visible"` permission policy] and the [Audio Session API] (this list is not exhaustive). - -### "media-playback-while-not-visible" permission policy - -Whenever an iframe, which has the `"media-playback-while-not-visible"` permission policy enabled, is not rendered anymore, the UA would transition the iframe's AudioContext to the `"interrupted"` state to pause audio rendering. - -In the example below, we should have the following behavior: -1. When the iframe is loaded, the application should initially print `"suspended"` on the console. -2. Clicking on the "play-audio-btn" button, should start the AudioContext and `"running"` should be printed on the console. -3. Clicking on the "iframe-visibility-btn" button, should hide the iframe and `"media-playback-while-not-visible"` will interrupt the AudioContext. Thus, `"interrupted"` should be printed on the console. -4. Clicking again on the "iframe-visibility-btn" button, should show the iframe. The `"media-playback-while-not-visible"` permission policy will end the interruption and move the AudioContext to the `"running"` state. As a result, `"running"` should be printed on the console. - -```html -<!-- index.html --> -<!DOCTYPE html> -<html> - <body> - <button id="iframe-visibility-btn">Hide iframe</button> - <div> - <iframe id="audiocontext-iframe" src="audiocontext-iframe.html" allow="media-playback-while-not-visible 'none'; autoplay *"></iframe> - </div> - <script> - const HIDE_IFRAME_BTN_STR = "Hide iframe"; - const SHOW_IFRAME_BTN_STR = "Show iframe"; - - const iframe_visibility_btn = document.getElementById("iframe-visibility-btn"); - - iframe_visibility_btn.addEventListener("click", () => { - const audiocontext_iframe = document.getElementById("audiocontext-iframe"); - if (iframe_visibility_btn.textContent === HIDE_IFRAME_BTN_STR) { - // Hide the iframe - audiocontext_iframe.style.setProperty("display", "none"); - iframe_visibility_btn.textContent = SHOW_IFRAME_BTN_STR - } else { - // Show the iframe - audiocontext_iframe.style.setProperty("display", "block"); - iframe_visibility_btn.textContent = HIDE_IFRAME_BTN_STR - } - }); - </script> - </body> -</html> -``` - -```html -<!-- audiocontext-iframe.html --> -<!DOCTYPE html> -<html> - <body> - <button id="play-audio-btn">Play audio</button> - <script> - const audio_context = new AudioContext(); - const oscillator = audio_context.createOscillator(); - oscillator.connect(audio_context.destination); - - const play_audio_btn = document.getElementById("play-audio-btn"); - - audio_context.addEventListener("statechange", () => { - console.log(audio_context.state); - }); - - play_audio_btn.addEventListener("click", () => { - oscillator.start(); - }); - - window.addEventListener("load", () => { - console.log(audio_context.state); - }); - </script> - </body> -</html> -``` - -### Audio Session API - -Whenever the Audio Session API needs to pause media playback, the document's active AudioContext would transition to the `"interrupted"` state. Let's say that a UA decides that the Web Audio API should not play any audio whenever the screen gets locked and the navigator's object [`AudioSessionType`] is set to `"auto"`. Since the current Web Audio API spec does not allow the UA to transition the `AudioContext`'s state to `"suspended"`, the UA can instead move the `AudioContext` to the `"interrupted"` state. Once the interruption ends, if the UA does not resume playback automatically, the application code can monitor the `AudioContext`'s state changes and be able to call `AudioContext.resume()` when the `AudioContext` is in the `"suspended"` state. - -Given the snippet below, where the `AudioSession` type is set to `"auto"`, we would have the following behavior: -1. Page is loaded and the AudioContext is `"suspended"`. -2. After clicking on the "Play Audio" button, the `AudioContext` state will be `"running"`. -3. The screen gets locked. At this moment, based on the the Audio Session API type, the UA should interrupt the `AudioContext`, and the context's state should now be `"interrupted"`. -4. The screen gets unlocked. The UA should lift the interruption and the `AudioContext` should transition to the `"running"` state. - -```js -<!DOCTYPE html> -<html> - <body> - <button id="play-audio-btn">Play audio</button> - <script> - const audio_context = new AudioContext(); - const oscillator = audio_context.createOscillator(); - oscillator.connect(audio_context.destination); - - const play_audio_btn = document.getElementById("play-audio-btn"); - - audio_context.addEventListener("statechange", () => { - console.log(audio_context.state); - }); - - play_audio_btn.addEventListener("click", () => { - oscillator.start(); - }); - - window.addEventListener("load", () => { - navigator.audioSession.type = "auto"; - console.log(audio_context.state); - }); - </script> - </body> -</html> -``` - -## Web compatibility risks - -When `AudioContext.resume()` is called for an `AudioContext` in the `"closed"` state, the returned promise is rejected. With this proposal, the same behavior will happen when `AudioContext.resume()` is called while the `AudioContext` interrupted (could happen either during `"suspended"` or `"interrupted"`). In this case, a web page that is not aware of the existence of the "`interrupted`" state might think that the `AudioContext` has been closed. - -## Privacy considerations - -Moving the `AudioContexts` to the `"interrupted"` state might giveaway some information about the user's behavior - for example, when the user started a voice call or locked the screen. However, since "interrupting" an `AudioContext` could be associated with several types of interruptions, exposing this new state shouldn't be a major concern. Moreover, to mitigate privacy concerns, this proposal forbids automatically transitioning from `"suspended"` to `"interrupted"` to avoid unnecessarily exposing interruptions. This state transition will only happen if [`AudioContext.resume()`] is called. - -## Considered alternatives - -This sections lists a number of alternatives taken into consideration prior to and during the writing of this document. - -### Re-use the `"suspended"` state - -We first considered in the [`"media-playback-while-not-visible"` permission policy] proposal using the `"suspended"` state whenever media playback needed to be paused. However, this is not possible, because [only user code can suspend][audiocontext-suspended-by-user-slot] an `AudioContext`. - -## Stakeholder Feedback / Opposition - -- Chromium : Positive - - Chromium engineers have shown support for this feature in the [github WebAudio repository](https://github.com/WebAudio/web-audio-api/issues/2392#issuecomment-844465877). -- WebKit : Positive - - This feature is shipped in Safari: - - [[MDN] Resuming interrupted play states in iOS Safari](https://developer.mozilla.org/en-US/docs/Web/API/BaseAudioContext/state#resuming_interrupted_play_states_in_ios_safari) - - [[iOS] When Web Audio is interrupted by a phone call, it cannot be restarted.](https://github.com/WebKit/WebKit/commit/c2e380f844e9bbb3afea4d9ca8213f11e56a7ec4) -- Gecko : No signals - -## References & acknowledgements - -Many thanks for valuable feedback and advice from: -- [Erik Anderson](https://github.com/erik-anderson) -- [Rahul Singh](https://github.com/rahulsingh-msft) -- [Sunggook Chue](https://github.com/sunggook) - -[AudioContext]: https://webaudio.github.io/web-audio-api/#AudioContext -[`AudioContextState`]: https://webaudio.github.io/web-audio-api/#enumdef-audiocontextstate -[`AudioContext.resume()`]: https://webaudio.github.io/web-audio-api/#dom-audiocontext-resume -[`AudioContext.suspend()`]: https://webaudio.github.io/web-audio-api/#dom-audiocontext-suspend -[`AudioSessionState`]: https://w3c.github.io/audio-session/#enumdef-audiosessionstate -[`AudioSessionType`]: https://w3c.github.io/audio-session/#enumdef-audiosessiontype -[Audio Session API]: https://w3c.github.io/audio-session/ -[`"closed"`]: https://webaudio.github.io/web-audio-api/#dom-audiocontextstate-closed -[`"media-playback-while-not-visible"` permission policy]: https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/IframeMediaPause/iframe_media_pausing.md -[`"running"`]: https://webaudio.github.io/web-audio-api/#dom-audiocontextstate-running -[`"suspended"`]: https://webaudio.github.io/web-audio-api/#dom-audiocontextstate-suspended -[audiocontext-suspended-by-user-slot]: https://webaudio.github.io/web-audio-api/#dom-audiocontext-suspended-by-user-slot -[Web Audio API]: https://webaudio.github.io/web-audio-api/ diff --git a/AudioOffload/AudioArch.png b/AudioOffload/AudioArch.png deleted file mode 100644 index 235c4e94e..000000000 Binary files a/AudioOffload/AudioArch.png and /dev/null differ diff --git a/AudioOffload/AudioOffload-Metrics2-Chart.png b/AudioOffload/AudioOffload-Metrics2-Chart.png deleted file mode 100644 index ea48a43de..000000000 Binary files a/AudioOffload/AudioOffload-Metrics2-Chart.png and /dev/null differ diff --git a/AudioOffload/explainer.md b/AudioOffload/explainer.md deleted file mode 100644 index c68803863..000000000 --- a/AudioOffload/explainer.md +++ /dev/null @@ -1,705 +0,0 @@ -# Enable Hardware-offloaded Audio Processing for Audio Content on Windows -**Author:** William Carr (drawn on work from Isuru Pathirana) - -# Summary Description -Hardware Accelerated Audio Processing allows audio processing to be performed on dedicated audio processors. This process is commonly referred to as audio offload (the processing is offloaded from the computer's main CPU to the dedicated audio processor(s)). Because audio processing is a computationally expensive process, the use of specialized hardware can improve power efficiency, resulting in longer battery life. To read more about the general concept of audio offload on Windows, please see: https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/hardware-offloaded-audio-processing - -This proposal lays out a path to enable the use of audio offload for the clear audio pipeline in Chromium on Windows when playback is using an audio device that supports hardware audio acceleration. - -To see substantial improvements in battery life, the audio offload must be paired with large audio buffers. If large audio buffers are not used, then the computer's main CPU must still wake frequently to feed audio from the application to the hardware; by increasing the buffer size, we space out these wakes allowing longer windows of opportunity for the main CPU to stay in a lower power state. - -# Goals -* Improve battery life for media playback scenarios through the HTML Media Elements on Windows devices with supported hardware -* Limit adverse effects on media playback scenarios compared to existing low latency non-offloaded audio pipeline - -# Non-goals -* We are explicitly not targeting WebRTC scenarios where low latency is a requirement -* We are explicitly not targeting Web Audio scenarios where low latency may be a requirement -* We are explicitly not targeting non-Windows Platforms -* It is not a goal to disable the audio offload feature when connected to an external power source. Although the audio offload feature's primary goal is to improve battery life, attempting to enable and disable the feature based on whether the machine is currently on battery power introduces additional complexity to the architecture. It would also prevent us from achieving the desired power savings when a media stream is started while the device is on an external power source and then switched over to battery power since, during a mid-stream switch to use offload, an unacceptable gap in playback would occur. - -# Use Cases -The primary use case criteria we're looking to improve incorporates all the following elements: -* Windows OS that supports classic application (win32) audio offload: This feature was first added in the 19H2 timeframe but will be serviced back to RS3. -* Device supports hardware audio acceleration for audio offload -* Device on battery power -* Unencrypted media consumption scenarios with audio aspect - -A typical user scenario would be a user watching a video on www.youtube.com (*clear content*) on a Surface Laptop (*device that supports audio hardware acceleration*) running Windows 10 19H1 w/ servicing fix to add audio offload (*OS supporting classic audio offload*) while on battery power. - -Measurements of a partial implementation of audio offload show potential savings of up to 150mW, ~5% improvement in battery life, for a simple video playback experience. - -# Proposal -## High Level Audio Architecture -A high level overview of chromium's audio architecture with a newly proposed WASAPIAudioOffloadOutputStream class. Changes in this explainer are not limited to this new class but span across pieces of the audio stack shown here. -![Image of High Level Audio Architecture](AudioArch.png) - -## Audio Process -On Windows OS's that support audio offload for classic (Win32) applications through the Windows Audio Service API (WASAPI), the calling process must meet **one** of the following requirements: -1. The process is running within a Windows Universal Application Package (UAP) -2. The process is running as a Low Privilege App Container (LPAC) -3. The process is running with an integrity level at or below Low - -The Chromium Audio stack currently interacts with WASAPI in the Browser process which is not an ideal candidate for any of the above. - -The recommendation for this proposal is to choose option **3**. In order to meet this requirement we propose the following two pieces of work: - -1. Move Chromium's Audio Process to its own process utilizing the existing work behind feature flags AudioServiceOutOfProcess and AudioServiceAudioStreams ->[Chromium Bug Tracking enabling Audio Process](https://crbug.com/672469) - ->Audio Process was enabled by default on Linux on July 12th, 2019, it is not yet enabled by default on Windows -2. Change the Audio Process's integrity level to Low -``` -sandbox::ResultCode SandboxWin::StartSandboxedProcess(base::CommandLine* cmd_line, - const std::string& process_type, - const base::HandlesToInheritVector& handles_to_inherit, - service_manager::SandboxDelegate* delegate, - base::Process* process) { - ... - if (sandbox_type == service_manager::SANDBOX_TYPE_AUDIO) { - policy->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW); - } - ... -} -``` - -With these two changes, the WASAPI code in Chromium will be called from a low-integrity level process allowing it to utilize audio offload. - -## Low Latency Opt-Out -Audio offload is only intended to be used for media streams since the use of a larger endpoint buffer size introduces increased latency from when the audio source renders a frame to when that frame is played back by the audio device. - -In order to allow audio scenarios like WebRTC or Web Audio to continue to use the existing low latency options, we'll introduce a new AudioParameters::Format::AUDIO_PCM_16_HIGH_LATENCY specifically for audio offload. -``` - enum Format { - AUDIO_PCM_LINEAR = 0, // PCM is 'raw' amplitude samples. - AUDIO_PCM_LOW_LATENCY, // Linear PCM, low latency requested. - AUDIO_PCM_16_HIGH_LATENCY, // 16 bit PCM, high latency for offload. - AUDIO_BITSTREAM_AC3, // Compressed AC3 bitstream. - AUDIO_BITSTREAM_EAC3, // Compressed E-AC3 bitstream. - AUDIO_FAKE, // Creates a fake AudioOutputStream object. - AUDIO_FORMAT_LAST = AUDIO_FAKE, // Only used for validation of format. - }; - ``` - -> OPEN QUESTION: Should a website be able to opt-out? Any stream playing file backed data should be fine opting-in to a higher latency but a stream that is showing a live stream (e.g. sports) may prefer to opt-out to reduce latency. - -> OPEN QUESTION: Would using the existing latency hint be a better architecture? - -## Setting up the WASAPI Audio Client for Audio Offload -A few high level caveats that influence this proposal: - -1.Audio offload introduces enough behavioral changes that instead of modifying the existing WASAPIAudioOutputStream class, we propose creating a separate class: WASAPIAudioOffloadOutputStream. - -2.Audio offload provides optimal power advantages when used in conjunction with larger audio buffers, but these larger audio buffers introduce a trade off by having higher latency. For this reason scenarios like WebRTC are better suited to continue using a non-offload stream. - -3.Audio offload is only supported for Share Mode streams, exclusive streams are not supported. - -4.Audio offload resources are limited so we cannot guarantee audio acceleration until we initialize our Audio Client. As a result we want to support gracefully falling back to a non-offload stream without having to teardown & rebuild the entire pipeline. - -### Audio Offload Format -Audio offload devices typically only support a 16-bit integer pulse code modulation (PCM) format. Since we want to facilitate gracefully falling back to a non-offloaded stream we do not want to alter the default audio output parameters. Instead we create a secondary audio format in the WASAPIAudioOffloadOutputStream class specifically to be used for audio offload clients, this format uses the sub-format KSDATAFORMAT_SUBTYPE_PCM and corresponding values for 16-bit integer samples: - -``` - WAVEFORMATEX* format_audio_offload = &format_audio_offload_.Format; - format_audio_offload->wFormatTag = WAVE_FORMAT_EXTENSIBLE; - format_audio_offload->nChannels = params.channels(); - format_audio_offload->nSamplesPerSec = params.sample_rate(); - format_audio_offload->wBitsPerSample = 16; - format_audio_offload->nBlockAlign = - (format_audio_offload->wBitsPerSample / 8) * - format_audio_offload->nChannels; - format_audio_offload->nAvgBytesPerSec = - format_audio_offload->nSamplesPerSec * format_audio_offload->nBlockAlign; - format_audio_offload->cbSize = - sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX); - - format_audio_offload_.Samples.wValidBitsPerSample = - format_audio_offload->wBitsPerSample; - format_audio_offload_.dwChannelMask = - CoreAudioUtil::GetChannelConfig(device_id, eRender); - format_audio_offload_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; - ``` - -### Determining Audio Offload Device Support - -In order to enable audio offload we must first determine if the target audio device supports offload. To do this, we'll query the device via IAudioClient2::IsOffloadCapable: -``` -Microsoft::WRL::ComPtr<IAudioClient2> audio_client2; -HRESULT hr = audio_client_offload.As(&audio_client2); - -if (SUCCEEDED(hr) && audio_client2.Get()) { - BOOL offloadCapable; - hr = audio_client2->IsOffloadCapable(AudioCategory_Media, - &offloadCapable); -``` - -Next, we'll setup the client to enable audio offload via IAudioClient2::SetClientProperties: -``` -AudioClientProperties clientProperties = {0}; -clientProperties.cbSize = sizeof(AudioClientProperties); -clientProperties.bIsOffload = true; -clientProperties.eCategory = AudioCategory_Media; - -hr = audio_client2->SetClientProperties(&clientProperties); -``` - -Finally we'll check to make sure this device supports our offload format using the CoreAudioUtil::IsFormatSupported helper: -``` -if (CoreAudioUtil::IsFormatSupported(audio_client_offload.Get(), - share_mode_, &format_audio_offload_)) { - use_audio_offload = true; -} -``` -At this point we've determined whether audio offload & our audio format are supported by the current audio client. However we have not yet successfully initialized the audio client and obtained an audio offload resource. - -### Late Binding -An audio client utilizing audio offload requires binding to one of the limited hardware resources - once bound that resource will be unavailable to other clients until it is released. The act of binding to this resource occurs as part of calling IAudioClient::Initialize. To responsibly use these resources we don't want to bind to them unless they are actively being used, so we delay initializing our audio client until absolutely necessary - this is called Late Binding. - -In order to facilitate Late Binding, we won't initialize our IAudioClient when WASAPIAudioOffloadOutputStream::Open is called (compare this behavior with WASAPIAudioOutputStream which does call IAudioClient::Initialize during Open). - -Instead binding will be performed by calling IAudioClient::Initialize when WASAPIAudioOffloadOutputStream::Start is called - as this is our first indication that the stream wants to transition to a playing state. - -### Fallback -Since initialization for audio offload may fail if all of the hardware offload resources are in use, we want to seamlessly fallback to using a non-offloaded audio client. To support this a new initialization helper function will be introduced, CoreAudioUtil::SharedModeInitializeForOffload. This function will behave similar to CoreAudioUtil::SharedModeInitialize: - -``` -HRESULT SharedModeInitializeForOffload(IAudioClient* client, - WaveFormatWrapper format, - HANDLE event_handle, - uint32_t requested_buffer_size, - uint32_t* endpoint_buffer_size, - const GUID* session_guid, - IAudioClient* offload_client, - WaveFormatWrapper format_audio_offload, - bool use_audio_offload, - bool* using_audio_offload) { - *using_audio_offload = false; - - if (use_audio_offload && offload_client) { - // Attempt to initialize the audio client for Audio Offload - hr = offload_client->Initialize(AUDCLNT_SHAREMODE_SHARED, stream_flags, - DesiredAudioOffloadBufferDuration, 0, - format_audio_offload, session_guid); - - if(hr == AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED) - { - // Align buffer size - ... - hr = offload_client->Initialize(AUDCLNT_SHAREMODE_SHARED, - stream_flags, - m_hnsRenderBuffer, - 0, - format, - session_guid); - } - - if (SUCCEEDED(hr)) { - *using_audio_offload = true; - } - } - - if (!*using_audio_offload) { - // Fallback to SharedModeInitialization logic to initialize client - ... - } - - IAudioClient* client_chosen = *using_audio_offload ? offload_client : client; - ... -``` - -### Early Release -Similar to Late Binding - the idea behind Early Release is that an IAudioClient bound to an offload resource shouldn't be held longer than is needed as this may prevent other clients from being able to use the resource. As a result when a stream is paused or stopped we want to ensure the client releases the resource. - -We actually end up getting Early Release for free, due to how we handle mixing, from the existing Chromium audio architecture. Here's how that currently works: - -1. When a stream is paused AudioRendererMixer::RemoveMixerInput is called to remove the stream -2. For audio offload the mixer will have only a single input (see Mixing below for more details) -3. When AudioRendererMixer::Render is called when there are no inputs to master_converter_ the last_play_time_ is not updated -4. When AudioRendererMixer::Render is called and last_play_time_ hasn't been updated for greater than pause_delay_ (currently 10 seconds) then it will issue a pause command to the audio sink -5. The Chromium audio pipeline currently treats pauses & stops as synonymous at the audio output stream layer and on a WASAPIAudioOffloadOutputStream::Stop call we will reset the IAudioClient causing the audio offload resource to be released - -As a result of this we do not currently need to introduce logic into the WASAPIAudioOffloadOutputStream class to explicitly early release the IAudioClient - -### Pre-Roll (Audio Output Stream) -Typically when AudioOutputStream::Start is called the output stream would fill the initial audio output buffer with silence, this allows us to exercise & prepare the audio client and since we're utilizing small buffers (20ms) there is little complication. When using audio offload however two things complicate this: -1. For audio offload a typical audio output buffer will be ~2s worth of audio frames (*output device dependent*) -2. An IAudioRendererClient for audio offload must always provide an entire buffer of this ~2s length (you cannot request a smaller buffer through IAudioRendererClient::GetBuffer) - -As a result of these constrained factors preparing the initial buffer with silence would mean introducing a noticeable two second gap before stream playback could begin as this ~2s of silence was rendered. - -Instead when using audio offload we must pre-roll actual audio data from the stream source, we can achieve this by calling WASAPIAudioOffloadOutputStream::RenderAudioFromSource in WASAPIAudioOffloadOutputStream::Start before calling IAudioClient::Start: - -``` - -bool WASAPIAudioOffloadOutputStream::PreRollAudio() { - last_position_ = 0; - last_qpc_position_ = 0; - - if (using_audio_offload_) { - UINT64 device_frequency = 0; - num_written_frames_ = 0; - - HRESULT hr = audio_clock_->GetFrequency(&device_frequency); - if (FAILED(hr)) { - return false; - } - if (!RenderAudioFromSource(device_frequency)) { - return false; - } - } else { - if (!CoreAudioUtil::FillRenderEndpointBufferWithSilence( - audio_client_.Get(), audio_render_client_.Get())) { - // retry once - opened_ = false; - audio_client_.Reset(); - audio_render_client_.Reset(); - if (!Open() || !PerformBinding() || !CoreAudioUtil::FillRenderEndpointBufferWithSilence( - audio_client_.Get(), audio_render_client_.Get())) { - return false; - } - } - - num_written_frames_ = endpoint_buffer_size_frames_; - } - - return true; -} -``` - -### Render Audio From Source -In a case where we've failed to setup or initialize our IAudioClient for audio offload, we're able to render audio from the source in the same manner that the existing WASAPIAudioOffloadStream::RenderAudioFromSource works since we've kept the same AudioParameters for our AudioSourceCallback. - -When we are rendering to an IAudioClient setup for audio offload, the process changes a bit: -1. When using audio offload IAudioRendererClient::GetBuffer must always request a buffer the size of the endpoint buffer (in our case this means we'll normally be getting ~2s buffer in frames) -``` -hr = audio_render_client_->GetBuffer(endpoint_buffer_size_frames_, - &audio_data); -``` -2. Our input packet size is still ~20ms worth of frames so we need to build up our output buffer from multiple audio buses before we can call IAudioRendererClient::ReleaseBuffer. We'll build up this buffer by making multiple calls to AudioSourceCallback::OnMoreData and appending them to our output buffer. -``` -audio_bus_->ToInterleaved<SignedInt16SampleTypeTraits>( - frames_filled, - reinterpret_cast<int16_t*>(audio_data + - (n * packet_size_frames_ * - format_audio_offload_.Format.nBlockAlign))); -``` -3. The samples coming from our calls to AudioSourceCallback::OnMoreData are in the IEEE Float format but our IAudioClient device expects 16-bit integer PCM formatted samples. We'll utilize the sample type traits argument on our AudioBus::ToInterleaved call to perform this conversion -``` -audio_bus_->ToInterleaved<SignedInt16SampleTypeTraits>( -``` - -All together it looks something like this: -``` -if (using_audio_offload_) { - // Grab all available space in the rendering endpoint buffer - // into which the client can write a data packet. - hr = audio_render_client_->GetBuffer(endpoint_buffer_size_frames_, - &audio_data); - if (FAILED(hr)) { - DLOG(ERROR) << "Audio Offload: Failed to use rendering audio buffer: " - << std::hex << hr; - return false; - } - - for (size_t n = 0; n < num_packets; ++n) { - ... derive delay & delay_timestamp ... - - int frames_filled = - source_->OnMoreData(delay, delay_timestamp, 0, audio_bus_.get()); - uint32_t num_filled_bytes = frames_filled * - format_audio_offload_.Format.nBlockAlign; - DCHECK_LE(num_filled_bytes, packet_size_bytes_); - - audio_bus_->Scale(volume_); - - audio_bus_->ToInterleaved<SignedInt16SampleTypeTraits>( - frames_filled, - reinterpret_cast<int16_t*>(audio_data + - (n * packet_size_frames_ * - format_audio_offload_.Format.nBlockAlign))); - - num_written_frames_ += packet_size_frames_; - } - - hr = audio_render_client_->ReleaseBuffer(num_available_frames, 0); -``` -## Pausing -The current Chromium architecture facilitates a pause by removing the paused source as a mixer input (see AudioRendererMixer::RemoveMixerInput); any queued output buffers are allowed to finish rendering. This works well when the queued output is on the order of tens of milliseconds, but will break down for audio offload where the queued output can be on the order of seconds. - -In order to respond to Pause events with limited delay when using audio offload, we'll issue a Pause command in conjunction with removing the mixer input. - -``` -void AudioRendererMixer::RemoveMixerInput( - const AudioParameters& input_params, - AudioConverter::InputCallback* input) { - ... - - if (input_params.format() == AudioParameters::AUDIO_PCM_16_HIGH_LATENCY || - output_params_.format() == AudioParameters::AUDIO_PCM_16_HIGH_LATENCY) { - // For high latency streams we need to tell the audio device to explicitly - // pause otherwise it will play queued audio for a perceivable amount of time - // after having it's input removed - audio_sink_->Pause(); - playing_ = false; - } -} -``` - ->OPEN ISSUE: Should a pause concept be introduced (and plumbed through) to the Audio Output Stream layer? Today Pause & Stop are conflated by the time we get to the Audio Output Stream (there is no AudioOutputStream::Pause), by issuing a Pause event when RemoveMixerInput is called we cause an AudioOutputStream::Stop which causes the output stream to tear down or reset the audio client. Since we're being more aggressive about issuing this Pause this means in the case of a quick pause/unpause sequence for audio offload we'll have to recreate our IAudioClient & perform a refeed or reseek action. - -## Mixing -Basic audio mixing will introduce a latency equal to the duration of the mixed buffers - which for audio offload isn't a big problem since at the point of mixing our buffers are still small (20ms). However, because we combine these buffers into a larger (~2s) buffer before sending them to the audio device, mixing multiple streams will create a latency equal to our output buffer duration even though the input buffers are all the same duration. - -When a new stream is added to a mix, a stream transitions its play state (pause<->play), or a stream is removed from the mix that change will not take effect until all the audio buffers currently queued to the audio device have finished rendering and the first audio buffer is rendered with the changed mixer input. - -When we're talking about 20ms input & output buffers, that typically introduces a reasonable delay of 20-40ms. When we're dealing with 2s output buffers, that typical delay becomes a very noticeable 2-4s. - -There are two options to handle this: -1. Whenever mixer input is altered we could attempt to mix the new set of inputs backtracking based on the number of queued frames currently in the audio device and then purge the audio device buffer and add this new mixed buffer. This could be very fragile however as we attempt to purge & transition - introducing gaps in playback. The current architecture also isn't very well set up to handle this backtracking efficiently (we haven't cached this unrendered data close to the mixer) -2. When using audio offloadable streams, opt-out of mixing entirely by only having a single stream be mixer - -For this proposal we suggest option #2 given the ease of execution and reduced risk. - -For option #2 the changes may look something like the following. - -Introduce a unique identifier when using an audio offload stream: -``` -AudioRendererMixerManager::MixerKey::MixerKey( - int source_render_frame_id, - const media::AudioParameters& params, - media::AudioLatency::LatencyType latency, - const std::string& device_id) - : source_render_frame_id(source_render_frame_id), - params(params), - latency(latency), - device_id(device_id) { - if (params.format() == media::AudioParameters::AUDIO_PCM_16_HIGH_LATENCY) { - audio_offload_guid = base::GenerateGUID(); - } -} -``` - -Check the audio offload key in MixerKeyCompare: -``` -struct MixerKeyCompare { - bool operator()(const MixerKey& a, const MixerKey& b) const { - if (a.source_render_frame_id != b.source_render_frame_id) - return a.source_render_frame_id < b.source_render_frame_id; - if (a.params.channels() != b.params.channels()) - return a.params.channels() < b.params.channels(); - - if (a.latency != b.latency) - return a.latency < b.latency; - - // Ignore format(), and frames_per_buffer(), these parameters do not - // affect mixer reuse. All AudioRendererMixer units disable FIFO, so - // frames_per_buffer() can be safely ignored. - if (a.params.channel_layout() != b.params.channel_layout()) - return a.params.channel_layout() < b.params.channel_layout(); - if (a.params.effects() != b.params.effects()) - return a.params.effects() < b.params.effects(); - - // If a or b has the AUDIO_PCM_16_INTEGER_HIGH_LATENCY format then we - // need to ensure it is not mixed - if ((a.params.format() == - media::AudioParameters::AUDIO_PCM_16_HIGH_LATENCY || - b.params.format() == - media::AudioParameters::AUDIO_PCM_16_HIGH_LATENCY) && - (a.params.format() != b.params.format() || - a.audio_offload_guid != b.audio_offload_guid)) { - return a.audio_offload_guid < b.audio_offload_guid; - } - - if (media::AudioDeviceDescription::IsDefaultDevice(a.device_id) && - media::AudioDeviceDescription::IsDefaultDevice(b.device_id)) { - // Both device IDs represent the same default device => do not compare - // them. - return false; - } - - return a.device_id < b.device_id; - } -}; -``` - -# Metrics -These are preliminary measurements from a partial implementation of the audio offload feature running on a Surface Pro 4 device on RS5. - -For this test a ~12 minute run of the Tears of Steel MP4 video was run 3x with audio offload feature enabled & 3x without with EMI tracing enabled. - -The audio offload runs showed an average savings of ~150mW improvement. - -**Audio Offload Runs** - -Audio Offload Average Power: **5993.3 mW** -* Run 1: 6068.9 mW -* Run 2: 5975.6 mW -* Run 3: 5935.4 mW -<pre> -| Line # | Channel Name | Start Time (ns) | End Time (ns) | Energy (mJ) | Power (mW) | -|--------------|--------------------|-----------------------|---------------------|-------------------|------------------| -| 1 | EMI_BATTERY | | | 32705362 | 60689 | -| 2 | EMI_3VSB | | | 1321727 | 2452 | -| 3 | EMI_BACKLT | | | 1068008 | 1981 | -| 4 | EMI_5VSB | | | 311834 | 578 | -| 5 | EMI_CORE | | | 233385 | 433 | -| 6 | EMI_1P2V_DUA | | | 195375 | 362 | -| 7 | EMI_1VSB | | | 95357 | 176 | -| 8 | EMI_GPU | | | 72902 | 135 | -| 9 | EMI_3P3VSSD | | | 71592 | 132 | -| 10 | EMI_1P8_VSB | | | 49081 | 91 | -| 11 | EMI_WLAN | | | 24382 | 45 | -| | | | | | | -| Line # | Channel Name | Start Time (ns) | End Time (ns) | Energy (mJ) | Power (mW) | -| 1 | EMI_BATTERY | | | 32268340 | 59756 | -| 2 | EMI_3VSB | | | 1310437 | 2426 | -| 3 | EMI_BACKLT | | | 1058634 | 1960 | -| 4 | EMI_5VSB | | | 309271 | 572 | -| 5 | EMI_CORE | | | 225163 | 416 | -| 6 | EMI_1P2V_DUA | | | 190624 | 353 | -| 7 | EMI_1VSB | | | 92898 | 172 | -| 8 | EMI_GPU | | | 71998 | 133 | -| 9 | EMI_3P3VSSD | | | 69273 | 128 | -| 10 | EMI_1P8_VSB | | | 47449 | 87 | -| 11 | EMI_WLAN | | | 19463 | 36 | -| | | | | | | -| Line # | Channel Name | Start Time (ns) | End Time (ns) | Energy (mJ) | Power (mW) | -| 1 | EMI_BATTERY | | | 35611592 | 59354 | -| 2 | EMI_3VSB | | | 1438604 | 2397 | -| 3 | EMI_BACKLT | | | 1183001 | 1971 | -| 4 | EMI_5VSB | | | 347155 | 578 | -| 5 | EMI_CORE | | | 249628 | 416 | -| 6 | EMI_1P2V_DUA | | | 205014 | 341 | -| 7 | EMI_1VSB | | | 99860 | 166 | -| 8 | EMI_GPU | | | 79588 | 132 | -| 9 | EMI_3P3VSSD | | | 63097 | 105 | -| 10 | EMI_1P8_VSB | | | 56161 | 93 | -| 11 | EMI_WLAN | | | 14517 | 24 | -| 10 | EMI_1P8_VSB | | | 47494 | 85 | -| 11 | EMI_WLAN | | | 12160 | 21 | -</pre> - -**Non-Audio Offload Runs** - -Non-Audio Offload Average Power: **6148.133333 mW** -* Run 1: 6237.4 mW -* Run 2: 6099.0 mW -* Run 3: 6108.0 mW -<pre> -| Line # | Channel Name | Start Time (ns) | End Time (ns) | Energy (mJ) | Power (mW) | -|--------------|--------------------|-----------------------|---------------------|-------------------|------------------| -| 1 | EMI_BATTERY | | | 32093284 | 62374 | -| 2 | EMI_3VSB | | | 1258539 | 2446 | -| 3 | EMI_BACKLT | | | 1031555 | 2004 | -| 4 | EMI_5VSB | | | 310408 | 603 | -| 5 | EMI_CORE | | | 242490 | 471 | -| 6 | EMI_1P2V_DUA | | | 187028 | 363 | -| 7 | EMI_1VSB | | | 93139 | 181 | -| 8 | EMI_3P3VSSD | | | 83937 | 163 | -| 9 | EMI_GPU | | | 67777 | 131 | -| 10 | EMI_1P8_VSB | | | 50039 | 97 | -| 11 | EMI_WLAN | | | 30514 | 59 | -| | | | | | | -| Line # | Channel Name | Start Time (ns) | End Time (ns) | Energy (mJ) | Power (mW) | -| 1 | EMI_BATTERY | | | 36593131 | 60990 | -| 2 | EMI_3VSB | | | 1437721 | 2396 | -| 3 | EMI_BACKLT | | | 1207011 | 2011 | -| 4 | EMI_5VSB | | | 357909 | 596 | -| 5 | EMI_CORE | | | 272882 | 454 | -| 6 | EMI_1P2V_DUA | | | 212324 | 353 | -| 7 | EMI_1VSB | | | 104181 | 173 | -| 8 | EMI_3P3VSSD | | | 83722 | 139 | -| 9 | EMI_GPU | | | 79145 | 131 | -| 10 | EMI_1P8_VSB | | | 51662 | 86 | -| 11 | EMI_WLAN | | | 13793 | 23 | -| | | | | | | -| Line # | Channel Name | Start Time (ns) | End Time (ns) | Energy (mJ) | Power (mW) | -| 1 | EMI_BATTERY | | | 33990240 | 61080 | -| 2 | EMI_3VSB | | | 1336069 | 2400 | -| 3 | EMI_BACKLT | | | 1110473 | 1995 | -| 4 | EMI_5VSB | | | 333166 | 598 | -| 5 | EMI_CORE | | | 255979 | 460 | -| 6 | EMI_1P2V_DUA | | | 196598 | 353 | -| 7 | EMI_1VSB | | | 97019 | 174 | -| 8 | EMI_3P3VSSD | | | 82081 | 147 | -| 9 | EMI_GPU | | | 73457 | 131 | -| 10 | EMI_1P8_VSB | | | 47494 | 85 | -| 11 | EMI_WLAN | | | 12160 | 21 | -</pre> - -This is a second set of tests run on a chromium build using different sized Audio Offload buffers -The test device -* Surface Pro 4 -* Windows RS5 - -Audio buffer cases run -* No Audio Offload -* 20 ms -* 100 ms -* 1000 ms -* 2000 ms - -For each case I took 3 power traces. Each power trace measured one full playback of the ToS video (~12 minutes), from: http://rundown.azureedge.net/videos/tears-of-steel-1080p-yt-stream.mp4 - -The first run for each case was from a fresh install (so no cached data), and the second & third runs had cached data - helps illustrate that the benefit of audio offload for each case. Between runs the browser was closed and re-launched and between buffer sizes the browser was uninstalled and a fresh instance installed. - -Each trace was pared down to the 100 second - 700 second time window to reflect steady state data. - -High level metrics -<pre> -| Buffer | Average Power Utilization (mW) | -|------------------|----------------------------------| -| No Audio Offload | 6691.7 | -| 20 ms | 6545.7 | -| 100 ms | 6100.7 | -| 1000 ms | 6056.6 | -| 2000 ms | 6044.7 | -</pre> - -![High level metrics line chart](AudioOffload-Metrics2-Chart.png) - -(It is worth noting in the non-cached case the 1000ms -> 2000 ms gap was actually much wider at 6343.9 mW vs 6305.1 mW respectively. It may be worth taking further traces of the non-cached case to see if this is consistently in the ~40mW range; since non-cached may be a better representation of a typical user) - -Note: The Power column is 10x the actual value due to HW reporting. - -Full data set: - -<pre> -| No Audio Offload | | | | | | | | | | | | AVERAGE | | -|--------------------------------------------|--------------------|-------------------|------------------|-------------|--------------------|-------------------|------------------|-------------|--------------------|-------------------|------------------|--------------|----------------| -| Run 1 | Channel Name | Energy (mJ) | Power (mW) | Run 2 | Channel Name | Energy (mJ) | Power (mW) | Run 3 | Channel Name | Energy (mJ) | Power (mW) | | | -| 1 | EMI_BATTERY | 44814248 | 67901 | | EMI_BATTERY | 42593021 | 66074 | | EMI_BATTERY | 44070669 | 66776 | NAO | 6691.7 | -| 2 | EMI_BACKLT | 1708736 | 2589 | | EMI_BACKLT | 1662030 | 2578 | | EMI_BACKLT | 1692029 | 2563 | | | -| 3 | EMI_3VSB | 1618233 | 2451 | | EMI_3VSB | 1527382 | 2369 | | EMI_3VSB | 1577493 | 2390 | | | -| 4 | EMI_5VSB | 395468 | 599 | | EMI_5VSB | 384806 | 596 | | EMI_5VSB | 394867 | 598 | | | -| 5 | EMI_CORE | 301204 | 456 | | EMI_CORE | 269807 | 418 | | EMI_CORE | 286514 | 434 | | | -| 6 | EMI_1P2V_DUA | 236878 | 359 | | EMI_1P2V_DUA | 238476 | 370 | | EMI_1P2V_DUA | 259413 | 393 | | | -| 7 | EMI_GPU | 194855 | 295 | | EMI_GPU | 178680 | 277 | | EMI_GPU | 190931 | 289 | | | -| 8 | EMI_1VSB | 116014 | 175 | | EMI_1VSB | 104343 | 161 | | EMI_1VSB | 108395 | 164 | | | -| 9 | EMI_3P3VSSD | 99151 | 150 | | EMI_3P3VSSD | 65421 | 101 | | EMI_3P3VSSD | 72624 | 110 | | | -| 10 | EMI_1P8_VSB | 54357 | 82 | | EMI_1P8_VSB | 55064 | 85 | | EMI_1P8_VSB | 61169 | 92 | | | -| 11 | EMI_WLAN | 50507 | 76 | | EMI_WLAN | 26562 | 41 | | EMI_WLAN | 22015 | 33 | | | -| | | | | | | | | | | | | | | -| Audio Offload - 20 ms audio buffer | | | | | | | | | | | | | | -| Run 1 | Channel Name | Energy (mJ) | Power (mW) | Run 2 | Channel Name | Energy (mJ) | Power (mW) | Run 3 | Channel Name | Energy (mJ) | Power (mW) | | | -| 1 | EMI_BATTERY | 44971205 | 69190 | | EMI_BATTERY | 39843778 | 63468 | | EMI_BATTERY | 42053025 | 63714 | AO20 | 6545.733 | -| 2 | EMI_3VSB | 1764520 | 2714 | | EMI_3VSB | 1518035 | 2418 | | EMI_3VSB | 1627861 | 2466 | | | -| 3 | EMI_BACKLT | 1304695 | 2007 | | EMI_BACKLT | 1251637 | 1993 | | EMI_BACKLT | 1317121 | 1995 | | | -| 4 | EMI_5VSB | 374062 | 575 | | EMI_5VSB | 353454 | 563 | | EMI_5VSB | 370182 | 560 | | | -| 5 | EMI_CORE | 321371 | 494 | | EMI_CORE | 265045 | 422 | | EMI_CORE | 284011 | 430 | | | -| 6 | EMI_3P3VSSD | 206248 | 317 | | EMI_1P2V_DUA | 183285 | 292 | | EMI_1P2V_DUA | 194387 | 294 | | | -| 7 | EMI_1P2V_DUA | 198394 | 305 | | EMI_WLAN | 119743 | 190 | | EMI_WLAN | 123563 | 187 | | | -| 8 | EMI_WLAN | 152184 | 234 | | EMI_1VSB | 111348 | 177 | | EMI_1VSB | 118213 | 179 | | | -| 9 | EMI_1VSB | 145415 | 223 | | EMI_GPU | 87136 | 138 | | EMI_GPU | 92896 | 140 | | | -| 10 | EMI_GPU | 97050 | 149 | | EMI_3P3VSSD | 65118 | 103 | | EMI_3P3VSSD | 77869 | 117 | | | -| 11 | EMI_1P8_VSB | 51648 | 79 | | EMI_1P8_VSB | 49447 | 78 | | EMI_1P8_VSB | 54217 | 82 | | | -| | | | | | | | | | | | | | | -| Audio Offload - 100 ms audio buffer | | | | | | | | | | | | | | -| Run 1 | Channel Name | Energy (mJ) | Power (mW) | Run 2 | Channel Name | Energy (mJ) | Power (mW) | Run 3 | Channel Name | Energy (mJ) | Power (mW) | | | -| 1 | EMI_BATTERY | 42153046 | 63867 | | EMI_BATTERY | 36277033 | 59600 | | EMI_BATTERY | 39304940 | 59553 | AO100 | 6100.667 | -| 2 | EMI_3VSB | 1787692 | 2708 | | EMI_3VSB | 1495413 | 2456 | | EMI_3VSB | 1617294 | 2450 | | | -| 3 | EMI_BACKLT | 1323515 | 2005 | | EMI_BACKLT | 1210933 | 1989 | | EMI_BACKLT | 1318176 | 1997 | | | -| 4 | EMI_5VSB | 386191 | 585 | | EMI_5VSB | 344931 | 566 | | EMI_5VSB | 378101 | 572 | | | -| 5 | EMI_CORE | 267935 | 405 | | EMI_CORE | 216001 | 354 | | EMI_CORE | 230322 | 349 | | | -| 6 | EMI_1P2V_DUA | 202800 | 307 | | EMI_1P2V_DUA | 180512 | 296 | | EMI_1P2V_DUA | 196713 | 298 | | | -| 7 | EMI_3P3VSSD | 188407 | 285 | | EMI_WLAN | 111870 | 183 | | EMI_WLAN | 125305 | 189 | | | -| 8 | EMI_WLAN | 174201 | 263 | | EMI_1VSB | 106970 | 175 | | EMI_1VSB | 116143 | 176 | | | -| 9 | EMI_1VSB | 142248 | 215 | | EMI_GPU | 85158 | 140 | | EMI_GPU | 91018 | 138 | | | -| 10 | EMI_GPU | 95724 | 145 | | EMI_3P3VSSD | 71703 | 117 | | EMI_3P3VSSD | 76565 | 115 | | | -| 11 | EMI_1P8_VSB | 52426 | 79 | | EMI_1P8_VSB | 48544 | 79 | | EMI_1P8_VSB | 51916 | 78 | | | -| | | | | | | | | | | | | | | -| Audio Offload - 1000 ms audio buffer | | | | | | | | | | | | | | -| Run 1 | Channel Name | Energy (mJ) | Power (mW) | Run 2 | Channel Name | Energy (mJ) | Power (mW) | Run 3 | Channel Name | Energy (mJ) | Power (mW) | | | -| 1 | EMI_BATTERY | 41869354 | 63439 | | EMI_BATTERY | 39078562 | 59211 | | EMI_BATTERY | 38287629 | 59048 | AO1000 | 6056.6 | -| 2 | EMI_3VSB | 1763425 | 2671 | | EMI_3VSB | 1603688 | 2429 | | EMI_3VSB | 1572502 | 2425 | | | -| 3 | EMI_BACKLT | 1307644 | 1981 | | EMI_BACKLT | 1305178 | 1977 | | EMI_BACKLT | 1277530 | 1970 | | | -| 4 | EMI_5VSB | 381656 | 578 | | EMI_5VSB | 376611 | 570 | | EMI_5VSB | 366443 | 565 | | | -| 5 | EMI_CORE | 268288 | 406 | | EMI_CORE | 226795 | 343 | | EMI_CORE | 223992 | 345 | | | -| 6 | EMI_1P2V_DUA | 203146 | 307 | | EMI_1P2V_DUA | 195566 | 296 | | EMI_1P2V_DUA | 192514 | 296 | | | -| 7 | EMI_3P3VSSD | 175309 | 265 | | EMI_WLAN | 123096 | 186 | | EMI_WLAN | 120873 | 186 | | | -| 8 | EMI_WLAN | 157150 | 238 | | EMI_1VSB | 113753 | 172 | | EMI_1VSB | 111232 | 171 | | | -| 9 | EMI_1VSB | 138309 | 209 | | EMI_GPU | 93464 | 141 | | EMI_GPU | 92421 | 142 | | | -| 10 | EMI_GPU | 97803 | 148 | | EMI_3P3VSSD | 66416 | 100 | | EMI_3P3VSSD | 63081 | 97 | | | -| 11 | EMI_1P8_VSB | 59212 | 89 | | EMI_1P8_VSB | 58351 | 88 | | EMI_1P8_VSB | 51928 | 80 | | | -| | | | | | | | | | | | | | | -| Audio Offload - 2000 ms audio buffer | | | | | | | | | | | | | | -| Run 1 | Channel Name | Energy (mJ) | Power (mW) | Run 2 | Channel Name | Energy (mJ) | Power (mW) | Run 3 | Channel Name | Energy (mJ) | Power (mW) | | | -| 1 | EMI_BATTERY | 41613092 | 63051 | | EMI_BATTERY | 37417867 | 59077 | | EMI_BATTERY | 39080059 | 59213 | AO2000 | 6044.7 | -| 2 | EMI_3VSB | 1751014 | 2653 | | EMI_3VSB | 1526648 | 2410 | | EMI_3VSB | 1600920 | 2425 | | | -| 3 | EMI_BACKLT | 1305670 | 1978 | | EMI_BACKLT | 1249835 | 1973 | | EMI_BACKLT | 1311269 | 1986 | | | -| 4 | EMI_5VSB | 380640 | 576 | | EMI_5VSB | 360819 | 569 | | EMI_5VSB | 372227 | 563 | | | -| 5 | EMI_CORE | 261956 | 396 | | EMI_CORE | 216556 | 341 | | EMI_CORE | 224375 | 340 | | | -| 6 | EMI_1P2V_DUA | 206243 | 312 | | EMI_1P2V_DUA | 189608 | 299 | | EMI_1P2V_DUA | 196531 | 297 | | | -| 7 | EMI_WLAN | 165177 | 250 | | EMI_WLAN | 117519 | 185 | | EMI_WLAN | 122464 | 185 | | | -| 8 | EMI_3P3VSSD | 163436 | 247 | | EMI_1VSB | 108400 | 171 | | EMI_1VSB | 112678 | 170 | | | -| 9 | EMI_1VSB | 136134 | 206 | | EMI_GPU | 87891 | 138 | | EMI_GPU | 92290 | 139 | | | -| 10 | EMI_GPU | 94089 | 142 | | EMI_3P3VSSD | 58539 | 92 | | EMI_1P8_VSB | 60689 | 91 | | | -| 11 | EMI_1P8_VSB | 53591 | 81 | | EMI_1P8_VSB | 49755 | 78 | | EMI_3P3VSSD | 59131 | 89 | | | -</pre> - -# Phase II -This explainer covers the initial work to get audio offload enabled - but the feature is not complete with just this work. There are some additional pieces of work that will be required to make audio offload work well enough to be enabled by default. - -## Refeeding vs Reseeking ->OPEN ISSUE: Whether to use Refeeding or Reseeking is still under discussion. - -In the current Chromium audio architecture, when a stream is paused or a device change occurs, any audio buffers currently queued to the audio device is played out, because there are typically only a couple of these buffers and they are only 20ms there's little perceived lag in the user experience between the queued audio playing out and entering the paused state or having audio begin playing out on the new device. - -In the case of audio offload, where we're utilizing larger audio buffers, the user experience has a very perceivable lag time (typically 2 to 4 seconds) between the user performing the action and the audio device reacting to it. - -As discussed in the Pause section above we can mitigate the time between when a Pause command is issued and when the audio device stops playing out the queued buffers (and this behavior holds true for a Device Change since a Device Change will also issue a Pause command). - -The problem is that any data currently queued to the audio device will currently be lost. - -To address this problem there are two potential approaches. - -1. Reseeking: The entire media pipeline is rewound to the timestamp correlating with the last frame that the audio client rendered. When IAudioClient::Stop is called the number of queued frames is used to calculate the appropriate timestamp to seek media playback to. When media playback is resumed (either on the new device or when a Play command is issued) it will re-render the previously queued data. -2. Refeeding - Unrendered audio buffers are cached as they are sent through the audio pipeline. When IAudioClient::Stop is called the number of queued frames is used to calculate the appropriate number of frames to refed from the cached data back to the IAudioRendererClient. Since the audio endpoint may change - meaning that we may need to move from an offloaded stream to a non-offloaded stream - we'd likely want to cache the audio bus data in the Audio Output Controller before it has been altered by the the Audio Converter & Audio Pull FIFO or by any processing in WASAPIAudioOffloadOutputStream::RenderAudioFromSource. - ->OPEN ISSUE: Reseeking: What would an architecture for this look like? - ->OPEN ISSUE: Refeeding: How do we keep Audio & Video in sync with this approach? What accounts for the Video playback position & how do we make it aware of the refed audio portion? - ->OPEN ISSUE: Refeeding: If we're partway through a single audio bus how do we handle that? Since we're required to completely fill the audio output buffer we need to land on a audio bus boundary. We could pre-fill the first audio bus with silence accounting for the previously rendered portion, we could skip the rest of the partially rendered audio bus (these buses are only 20ms input buses), or we could go into the business of combining buses but we'd have to do this indefinitely (performance implications?) for the rest of the audio playback or plumb through a partial read of an audio bus through the Audio Sync Reader. - -## Underflow and Audio Renderer Pre-Rolling -Underflow is a starvation of the audio pipeline due to the audio source not being able to render enough data to keep up with the requests from the audio client. The result is that the audio device will run out of audio buffer data to render before it's received the next buffer to render. - -When Chromium detects this case, it takes action by increasing the amount of data that the audio renderer algorithm is buffering - taking a stepped approach in an aim to minimize the amount of buffered data to what is necessary. - -This works pretty well for the non-audio offload case where we have a pretty even cadence of requesting a single audio bus of data every 20ms. - -However - due to audio offload having to build up 2 second buffers it ends up with a burstier behavior where it will request 1000 audio buses every 2 seconds - the amount of data requested remains the same. Due to this bursty nature the audio offload stream tends to be more likely to end up in the underflow state at the beginning of the stream. In response to an underflow, the audio pipeline will temporarily pause the stream while it awaits more data from the audio renderer. This can result in stuttered playback when the audio offload stream is first starting up. - -We have a couple of options to address this (which may be used in conjunction) -1. Increase the initial & max buffer sizes used by the audio renderer algorithm for the audio offload scenario: -``` -void AudioRendererAlgorithm::Initialize(const AudioParameters& params, - bool is_encrypted) { - CHECK(params.IsValid()); - - channels_ = params.channels(); - samples_per_second_ = params.sample_rate(); - is_bitstream_format_ = params.IsBitstreamFormat(); - - if (params.format() == AudioParameters::AUDIO_PCM_16_HIGH_LATENCY) { - initial_capacity_ = capacity_ = std::max( - static_cast<int64_t>(params.frames_per_buffer()) * 2, - AudioTimestampHelper::TimeToFrames( - is_encrypted - ? audio_renderer_algorithm_params_.starting_capacity_for_encrypted_large_buffers - : audio_renderer_algorithm_params_.starting_capacity_large_buffers, - samples_per_second_)); - max_capacity_ = std::max( - initial_capacity_, - AudioTimestampHelper::TimeToFrames( - audio_renderer_algorithm_params_.max_capacity, samples_per_second_)); - } - ... -``` - -2. Introduce the concept of pre-rolling into the Chromium audio pipeline. Similar to how we have introduced pre-rolling to the WASAPIAudioOffloadOutputStream where we ensure that IAudioRendererClient has sufficient initial data before calling IAudioClient::Start, we could similarly ensure that our audio renderer has buffered sufficient data before initializing playback -> OPEN QUESTION: What would the architecture for Audio Renderer Pre-Rolling look like? How does it differ from changing the Audio Renderer Algorithm starting capacity? - ---- -[Related issues](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/Audio%20Offload) | [Open a new issue](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new?title=%5BAudio%20Offload%5D) diff --git a/AudioStreamCategory/explainer.md b/AudioStreamCategory/explainer.md deleted file mode 100644 index fdb35e5a6..000000000 --- a/AudioStreamCategory/explainer.md +++ /dev/null @@ -1,55 +0,0 @@ -# Audio Stream Category -Authors: [Sam Dallstream](https://github.com/sjdallst), [Greg Whitworth](https://github.com/gregwhitworth), [Rahul Singh](https://github.com/rahulsingh-msft) - -## Status of this Document -This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. -* This document status: **Archived** -* Expected venue: [W3C mst-content-hint](https://w3c.github.io/mst-content-hint/) -* Current version: this document - -## Introduction - -The Audio Category is a proposed addition to the [mst-content-hint spec](https://github.com/w3c/mst-content-hint) that will allow websites to set a ```contentHint``` on a ```MediaStreamTrack``` that specifies that the track is meant for speech recognition by a machine. - -The ```contentHint``` we are proposing is ```speechRecognition```. - -## Background - -We believe there is a general need to differentiate between streams intended for human consumption and streams meant to be used for transcription by a machine because there are many differences in the optimizations that are applied for each scenario. Specifically, requirements for communications between humans can be found in the [ETSI TS 126 131 specification](https://www.etsi.org/deliver/etsi_ts/126100_126199/126131/12.03.00_60/ts_126131v120300p.pdf), and include optimizations in noise suppression like the addition of pink noise in order to increase user satisfaction, which is in direct opposition to the needs of a speech recognition system. There is also a draft of testing methods for speech recognition systems that outlines some of the different requirements for those systems [STQ63-260v0210](https://drive.google.com/file/d/1y_i7NkXbCuRWznYRl9dacy3xDdH2e7-m/view?usp=sharing). - -The proposed solution below was inspired by the categories that Windows offers for audio streams. These categories allow you to specify what kind of audio stream you want (ex: “speech” for when someone is dictating into a mic), which gives the operating system a chance to optimize the stream for that type of input. After some research, we found that similar categories exist across [Android](https://developer.android.com/reference/android/media/AudioAttributes.html), [iOS](https://developer.apple.com/documentation/avfoundation/avaudiosessionmode?language=objc), and, of course, [Windows](https://docs.microsoft.com/en-us/windows-hardware/drivers/audio/audio-signal-processing-modes). - -## Proposed Solution - -We plan to follow the lead of native applications across Android, iOS, and Windows, and extend the list of content-hints for the developer to choose from when working with a stream. We will adapt this to the web by modifying the [mst-content-hint API](https://w3c.github.io/mst-content-hint/). For operating systems, such as Mac, that do not have one to one mappings of these categories, a best effort approach will be taken to applying categories. - -## Proposed API - -Add the ```speechRecognition``` option to ```contentHint``` for audio tracks. - -### IDL - -[Extension to MediaStreamTrack](https://w3c.github.io/mst-content-hint/#mediastreamtrack-extension) -``` -partial interface MediaStreamTrack { - attribute DOMString contentHint; -}; -``` - -## Examples - -### Example 1: Get an audio stream and set the category set to “speech” -``` -const constraints = {volume: 1}; -navigator.mediaDevices.getUserMedia({ audio : constraints}) - .then(handleMediaStreamAcquired.bind(this), - handleMediaStreamAcquiredError.bind(this)); - -function handleMediaStreamAcquired(mediaStream) { - mediaStream.getTracks()[0].contentHint = 'speechRecognition'; -} - -function handleMediaStreamAcquiredError(mediaStreamError) { - console.log(mediaStreamError); -} -``` diff --git a/BackToOpener/explainer.md b/BackToOpener/explainer.md deleted file mode 100644 index b8d64c497..000000000 --- a/BackToOpener/explainer.md +++ /dev/null @@ -1,126 +0,0 @@ -# Back to Opener: Seamless Back Navigation in New Tabs - -Authors: [Victor Huang](https://github.com/victorhuangwq) - -## Status of this Document - -This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. - -This document status: **Archived** -Expected venue: N/A -Current version: We are pursuing this feature as a [Chromium feature](https://issues.chromium.org/issues/448173940) instead of a web platform one. - -<!-- - [Discussion forum] --> - -## Introduction - -In scenarios such as chat interfaces (e.g., ChatGPT, Google AI mode, Copilot, Gemini) and search engines (e.g., Yahoo, Bing, Perplexity), users often have significant context associated with the current document and prefer opening links in new browsing contexts (e.g., via `target="_blank"`) to support multitasking and preserve context. However, this practice disables the browser's back button, preventing users from easily returning to the original document, and contributes to tab proliferation by leaving multiple browsing contexts open without a clear navigation path back to the opener document. - -This proposal introduces an opt‑in mechanism that signals the browser to insert the opener's URL as an initial entry in the destination browsing context's session history. When the user navigates back in the destination browsing context, the UA will automatically return focus to the opener browsing context and close the destination browsing context, provided the originating document is still active. This feature enhances user experience by supporting a logical back navigation flow and reducing the proliferation of browsing contexts (perceived by users as tab clutter). - -## User-Facing Problem - -The default behavior of opening links in new browsing contexts, as commonly used by chat interfaces and search engines, can frustrate users by causing proliferation of browsing contexts and making it difficult to return to the original conversation or context. Traditional [guidance](https://www.nngroup.com/articles/new-browser-windows-and-tabs/) warns against breaking the user's flow by opening new browsing contexts. At the same time, modern chat interfaces prioritize multitasking and context preservation, thus preferring to open links in new browsing contexts. - -This proposal bridges the gap by creating a clear navigation pathway back to the originating document, ensuring users can effortlessly return without manually navigating through multiple browsing contexts. It additionally helps reduce the proliferation of browsing contexts perceived as tab clutter. - -### Evidence of User Need - -#### User Demand - -The practice of opening links in new browsing contexts leads to user frustration due to tab proliferation and the disruption of standard back-button navigation. When a new tab is opened, the back button is not exposed, which breaks the user's mental model of navigation and removes their ability to easily return to the previous page ([dinghy.studio](https://www.dinghy.studio/blog/opening-links-in-new-tabs/)). This issue is particularly prevalent on search engine results pages, where users are unable to use the back button to return to their query result, and forces them to manage an increasing number of open tabs ([How-To Geek](https://www.howtogeek.com/734902/how-to-stop-microsoft-edge-from-opening-links-in-new-tabs/)). - -From an accessibility standpoint, this behavior can be disorienting and problematic. It deviates from the expected navigation model. Informing users that a link opens in a new tab helps them understand that the back button will not work as expected, which is particularly important for screen reader users who would otherwise need to manually navigate back to the previous window ([Accessibility Guidelines](https://accessibilityguidelines.com/articles/links-in-new-tabs.html)). - -The demand for a solution is also reflected in community feature requests for browsers like [Mozilla](https://connect.mozilla.org/t5/ideas/back-button-closes-newly-created-tabs/idi-p/8428) and [Vivaldi](https://forum.vivaldi.net/topic/56508/preserve-tab-history-when-a-link-is-opened-in-new-tab), where users have explicitly asked for functionality that allows the back button to return to the opener tab. - -#### Prior Art - -The user need is further underscored by the development of browser extensions designed to provide this functionality. These extensions serve as a form of prior art and demonstrate a clear demand for a native solution. Examples include: - -- **Chromium Extensions:** [Tab Origin](https://microsoftedge.microsoft.com/addons/detail/tab-origin/pjokhhddbfamccemjneocheekkoognbo?hl=en-US), [Last Tab Back](https://chromewebstore.google.com/detail/last-tab-back/oijipkokfkhgojikimbbcafnbppebnhe?pli=1) -- **Legacy Firefox Add-on:** [BackTrack Tab History](https://www.ghacks.net/2017/03/01/backtrack-tab-history-add-on-for-firefox/) - -While these add-ons validate the user need, a native implementation within the browser would offer a more integrated, secure, and reliable experience. - -## Goals and Use Cases - -The primary goal is to allow web developers to maintain a connected navigation experience, where the source context is always within reach even when content is loaded in new browsing contexts. Key use cases include: - -- Search Engines: Users can click on search results and then return to their search results document. -- Conversational Chat Interfaces: Users can click on links and navigate back to the conversation document. - -## Non-Goals - -- Any PWA (Progressive Web App) specific behavior or functionality. -- Modifying the behavior of existing links that do not explicitly opt-in to this feature. - -## Proposed Approach - -Developers can signal their intent via `window.open()` and `<a>` elements. The browser will then handle the navigation logic, ensuring that when the user navigates back in the destination browsing context, it automatically returns focus to the opener browsing context and closes the destination browsing context if the opener is still active. - -- For `window.open()`, we propose introducing a new [`windowFeatures`](https://developer.mozilla.org/en-US/docs/Web/API/Window/open#windowfeatures) parameter called `addOpenerToHistory`. When this feature is specified, the browser will add the opener's URL to the destination browsing context's history. This windowFeatures would only apply if `target="_blank"` - -```javascript -window.open("https://www.destination.com", "_blank", "addOpenerToHistory") -``` - -- For `<a>` elements, we propose introducing a new [`rel`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/rel) attribute value called `addOpenerToHistory`. Similarly, when this value is specified, the browser will add the opener's URL to the destination browsing context's history. - -```html -<a href="https://www.destination.com" target="_blank" rel="addOpenerToHistory">Example Destination</a> -``` - -### Expected Behavior -Upon clicking the back button in the destination browsing context, the UA will check if the opener browsing context is still active: - -- Active: The UA will automatically return focus to the opener browsing context and close the destination browsing context. -- Inactive (e.g., closed or navigated away): UA will navigate back to the opener's URL in the destination browsing context. - -## Alternatives Considered - -### Policy Header Approach - -An alternative approach could involve using a policy header or meta tag to signal the browser to add the opener to the history. With this policy in place, the browser would automatically apply the "back-to-opener" behavior for all new-tab links originating from that site. - -Pros: - -- Simplifies implementation for developers, as they wouldn't need to modify individual links or scripts. -- Ensures consistent behavior across the site without requiring additional attributes. - -Cons: - -- Less flexible than the opt-in mechanism, as it applies to all links on the site, potentially leading to unintended behavior for links that shouldn't have this functionality. - -### UA-Defined Behavior - -Another alternative we considered was an approach outside the Web Platform, leaving it up to the user agent to determine whether to add the opener to the history based on heuristics or user preferences. - -Pros: - -- Reduces the need for developers to explicitly signal their intent, potentially leading to a more consistent user experience across different sites. -- Could be implemented as a browser feature without requiring changes to web standards. - -Cons: - -- Inconsistency Risks: This behavior might vary across browsers and UA versions, leading to unpredictable user experiences. -- In quickly evolving markets like chat interfaces, a manual override is important—developers should be able to opt into the behavior when needed rather than rely solely on UA heuristics. - -## Privacy and Security Considerations - -Even though the opener's URL is added to the destination browsing context's history, this does not expose any additional information about the opener browsing context to the destination browsing context. This is because a browsing context cannot query the history of another browsing context and can only use its own history to navigate back and forth. - -Interaction with `rel="noopener"` and `rel="noreferrer"` has also been considered. The implementation of this proposal should not rely on the presence of the `Referer` header or the `window.opener` property, as this would not be compatible with `rel="noopener"` or `rel="noreferrer"`. - -## Stakeholder Feedback / Opposition - -A primary concern is the potential for an inconsistent user experience, as this is an opt-in feature. Users might be confused by "back-to-opener" behavior that isn't present on all sites opened in a new browsing context. Discoverability is also a challenge, as users may not know which links will have this enhanced navigation. - -This feedback raises the question of whether such behavior should be controlled by the user agent rather than the web platform. However, this proposal prioritizes developer opt-in to grant explicit control and avoid altering the behavior of existing web content in unintended ways. - -## References & acknowledgements - -Thank you to the following: - -- [Erik Anderson](https://github.com/erik-anderson), for the initial direction -- [Andy Luhrs](https://github.com/aluhrs13) diff --git a/BindingContext/explainer.md b/BindingContext/explainer.md deleted file mode 100644 index 9e5142ae4..000000000 --- a/BindingContext/explainer.md +++ /dev/null @@ -1,185 +0,0 @@ -# Demonstrating Proof-of-Possession in the Browser Application (BPoP) - -## Table of Contents -<!-- TOC --> - -## Authors - -- [Sameera Gajjarapu](mailto:sameera.gajjarapu@microsoft.com) -- [Will Bartlett](mailto:wibartle@microsoft.com) - -## Status of this Document - -This document is a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. - -* This document status: **Active** -* Expected venue: to be determined -* **Current version: this document** - -## Motivation - -The motivation for BPoP closely follows the motivations for [IETF DPoP](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-dpop), i.e. "to prevent unauthorized or illegitimate parties from using leaked or stolen access tokens, by binding a token to a public key upon issuance and requiring that the client proves possession of the corresponding private key when using the token [...]" except that, rather than binding an access token (issued by an identity provider), BPoP binds a browser artifact (such as a cookie) issued by a website. - -This explainer makes direct analogs to DPoP, e.g. defining a "BPoP Proof" to match DPoP's "DPoP Proof". - -## Use Cases - -The primary use case for BPoP is binding an authentication cookie. Cookies remain among the most common mechanism web servers use to store authentication state about a user. Malicious actors steal such authentication cookies and compromise user data. - -A website that is its own standalone identity provider (i.e. a website that accepts a username and password) could activate BPoP as part of rending the login form. Then, on the subsequent request, when the website verifies the username and password and issues an authentication cookie, the website could also verify the BPoP proof and record the public key associated with the BPoP proof in the authentication cookie. If this website had user submitted content and such content was subsequently used as part of a stored cross site scripting (XSS) attack, this attack would be unable to steal the BPoP private key and thus the attacker would be unable to use any stolen cookies. - -A website that uses a federated identity provider could activate BPoP as part of redirecting to the federated identity provider. Then, on the response back from the federated identity provider, when the website verifies the federation response and issues an authentication cookie, the website could also verify the BPoP proof and record the public key associated with the BPoP proof in the authentication cookie. If this website were vulnerable to a reflected XSS which stole the authentication cookie, the attacker would be unable to use that stolen cookie, as the attacker would be unable to produce a BPoP proof. - -BPoP is also not strictly limited to cookies - it can be used to bind any artifact which is issued and accepted by the same web server (e.g. an ASP.NET ViewState). - -### CNAMEs - -One prominent place where authentication cookies may be shared with multiple parties is authentication cookies set in a top-level domain (example.com) but shared among sub-domains operated as distinct services. For example, an organization named Example might have distinct sub-domains `support.example.com`, `store.example.com`, and `www.example.com`, each operated as a separate service, but capable of reading a shared authentication cookie in `example.com`. By binding cookies to a public private key pair, signing over the specific origin used in the request, and limiting the authentication cookies so they can only be used with such a signature, BPoP prevents a compromised subdomain like `support.example.com` from being leveraged to attack another subdomain like `store.example.com`. - -While it is possible for `example.com` to properly audience constrain cookies today (e.g. by issuing one cookie for each subdomain, rather than one cookie in the top-level domain), doing so in practice has proven to be prohibitively cumbersome for many deployments. - -## Detailed example - -### Server activation - -A server returns a response header `BPoP` to active binding. `BPoP` is a [structured header](https://www.rfc-editor.org/rfc/rfc8941.html) whose value is a dictionary. The following keys are recognized: - - `enabled` is a mandatory boolean - - `subdomains` is an optional boolean whose value is `false` if omitted. - - `SameSite` is an optional token whose value is either `None`, `Lax`, or `Strict` and whose default is `Lax` if omitted. - - `algs` is a optional string list indicating algorithms supported by the website for BPoP proofs, per [RFC7518](https://datatracker.ietf.org/doc/html/rfc7518). MUST NOT include none or any identifier for a symmetric algorithm (MAC). By default, it is the list `["RS256", "ES256"]` - -A web server may also optionally return a `BPoP-Nonce` header, containing a nonce value to be included in BPoP proofs sent to them. The nonce syntax in ABNF used by [RFC6749](https://www.rfc-editor.org/rfc/rfc6749.html) is `nonce = 1*NQCHAR`. - -Thus a typical server might activate BPoP like: - -``` -BPoP: enabled -BPoP-Nonce: eyJ7S_zG.eyJH0-Z.HX4w-7v -``` - -Such a response header indicates to a browser client that it SHOULD generate a proof of possession key and attach a BPoP proof to future requests. If a browser client does not support any of the algorithms in `algs`, or for any other reason, the browser may skip BPoP. If the browser skips BPoP, the web server SHOULD continue to issue cookies without binding, unless forbidden by the web server's security policy. - -### Browser BPoP proofs - -A BPoP proof is a signed [CWT](https://www.rfc-editor.org/rfc/rfc8392.html). A BPoP proof demonstrates to the server that the client holds the private key that was used to sign the BPoP proof CWT. This enables websites to bind issued browser storage artifacts (e.g cookies) to the corresponding public key and to verify the key binding of all artifacts they receive, which prevents said artifacts from being used by any entity that does not have access to the private key. - -The COSE header of a BPoP CWT MUST contain at least the following parameters: - - `typ` with value `bpop+jwt` - - `alg` a digital signature algorithm identifier chosen from the list indicated by the server. - - `jwk` representing the public key chosen by the client, in JSON Web Key (JWK) format [RFC7517](https://datatracker.ietf.org/doc/html/rfc7517). - -The payload of a BPoP CWT MUST contain at least the following claims: - - `iat` creation timestamp of the CWT - - `hth` with value equal to the host value of the http request. - -Following a `BPoP-Nonce` header, the BPoP proof must also contain a claim `nonce` with value equal to that header. - -The client sends a BPoP proof on future HTTP requests. - -``` -BPoP: eyJ0eXAiOiJicG9wK2p3dCIsImFsZyI6IkVTMjU2IiwiandrIjp7Imt0eSI6IkV - DIiwieCI6Imw4dEZyaHgtMzR0VjNoUklDUkRZOXpDa0RscEJoRjQyVVFVZldWQVdCR - nMiLCJ5IjoiOVZFNGpmX09rX282NHpiVFRsY3VOSmFqSG10NnY5VERWclUwQ2R2R1J - EQSIsImNydiI6IlAtMjU2In19.eyJodGgiOiJzZXJ2ZXIuZXhhbXBsZS5jb20iLCJp - YXQiOjE1NjIyNjI2MTZ9.2-GxA6T8lP4vfrg8v-FdWP0A0zdrj8igiMLvqRMUvwnQg - 4PtFLbdLXiOSsX0x7NVY-FNyJK70nfbV37xRZT3Lg -``` - -**TODO:** update this example to be a CWT instead of a JWT. - -The client is expected to cache BPoP proofs and re-use them, until rejected by the server. - -### Storage model - -The client maintains a list of origins that have activated BPoP and their associated configs (`subdomains`, `algs`, and `SameSite`). When BPoP is deactivated, e.g.: - -``` -BPoP: enabled=?0 -``` - -The origin is removed from the list and the config is discarded. - -If an origin that has previously configured BPoP, e.g.: -``` -BPoP: enabled, subdomains -``` - -Reconfigures BPoP with a different configuration: -``` -BPoP: enabled, SameSite=None -``` - -The latest configuration replaces the previous configuration. Replacement occurs for the entire config, not just for configuration elements who appear in the `BPoP` header. - -### Retrieval model - -The browser only attaches BPoP proofs to "secure" protocols (as defined by the user agent). - -The browser maintains one public private key pair per sub entity to the effective top-level domain (eTLD+1). That is, if `a.example.com`, `b.example.com`, and `c.example.com` each activate BPoP, they share a single public private key pair. - -The browser attaches BPoP proofs to a request if there exists a config that either: - - Has an exact match between the BPoP origin and the canonicalized host of the retrieval's URI and the `subdomains` flag is false - - Has a domain match between the BPoP origin and the canonicalized host of the retrieval's URI and the `subdomains` flag is true - -The semantics of `SameSite` match the cookie attributes. That is, if the browser would not attach a cookie with SameSite=Lax to a request, and the server has initialized BPoP with SameSite=Lax, the browser should not attach a BPoP proof to the request. - -### Server challenge - -A server may reject a BPoP proof because its nonce is missing or out of date: - -``` -HTTP/1.1 401 Unauthorized -WWW-Authenticate: BPoP error="use_bpop_nonce", error_description="Web server requires nonce in BPoP proof" -BPoP-Nonce: eyJ7S_zG.eyJH0-Z.HX4w-7v -``` - -In such a case, the browser should update the BPoP proof and retry the request immediately. Servers SHALL not ask for more than one retry this way. - -Such retries are intended to be seen by the client as part of a single HTTP fetch. That is, update [Fetch section 4.3 HTTP Fetch](https://fetch.spec.whatwg.org/#http-fetch) with additional steps. If *response* is a 401 status code, and the response includes a `WWW-Authentication` header indicating the `BPoP` scheme with `error` equal to `use_bpop_nonce` and the response includes a `BPoP-Nonce` header, store the nonce, regenerate the BPoP Proof, and set the *actualResponse* to the result of running HTTP-network-or-cache fetch for the updated *fetchParams*. - -### Server update - -A server may also return a new BPoP nonce on any 200 response. - -``` -HTTP/1.1 200 Ok -BPoP-Nonce: eyJ7S_zG.eyJH0-Z.HX4w-7v -``` - -The client SHOULD start using the new nonce on the next request. - -### Javascript - -For [Fetch](https://fetch.spec.whatwg.org/), the client should include a BPoP proof when *includeCredentials* is true. - -## Privacy Considerations - -This proposal is intended to operate in the two modes already shipping in most majors browsers - a standard mode for broad audiences and an enterprise mode (equivalent to Chromium's "enterprise policy"). These modes should appear effectively the same from the perspective of a server which is unaware of enterprise features. - -In standard mode: - - The browser uses one public private key pair per eTLD+1. - - The browser chooses where these keys are stored (software, hardware, roaming, etc.) - - The browser is licensed to clear these keys at any time for any reason (like cookies). The browser should avoid clearing these keys too frequently as such clears may be disruptive to the user experience (e.g. require the user to sign in again). - - The browser SHOULD clear these keys as part of any other "delete site data" experience. - -If the user's machine is governed by an enterprise policy, this spec envisions that the behavior of BPoP changes as follows: - - Instead of the browser choosing keys for BPoP proofs, the enterprise policy provider chooses the keys. - - The enterprise policy provider may augment the BPoP proof payload with additional claims. - -Note that neither of these changes should impact the interoperability of BPoP. As long as the web service supports the selected algorithm for the BPoP proof, the web service should be able to verify the proof and bind its cookies to that proof. - -## Alternative Solutions - -### TLS Token Binding - -[RFC 8472](https://www.rfc-editor.org/rfc/rfc8472) defines a pattern for binding authentication tokens (including cookies) to a TLS channel. While browsers initially sent positive signals, Chromium eventually opted to [remove TLS token binding](https://groups.google.com/a/chromium.org/g/blink-dev/c/OkdLUyYmY1E/m/YJrsadYKDQAJ) in part due to the "engineering costs, maintenance costs, \[...\]". TLS token binding presented a number of challenges which are not present in this proposal: - - TLS token binding is not compatible with certain network stacks (e.g. HTTP3 0-RTT) - - TLS token binding is not compatible with common corporate network proxies which terminate and proxy connections to inspect traffic - - TLS token binding requires connections be kept open or resumable - not always practical over typical cookie lifetimes. - - TLS token binding deeply coupled TLS keys to authentication security, requiring integration between MDM providers and TLS stacks to satisfy enterprise management scenarios and requirements (like all keys being kept in hardware). - -Instead: - - BPoP is agnostic to network stack, being an application-layer HTTP header. - - BPoP is passed through by corporate network proxies which break and inspect incoming traffic - - BPoP requires no underlying connections, functioning at the same "site data" storage layer as cookies. - - BPoP leaves the TLS layer alone. Instead, MDM providers only need to implement a narrow interface (generating a BPoP proof from their registered keys) \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 89fa0cc53..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,10 +0,0 @@ -# Microsoft Open Source Code of Conduct - -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). - -Resources: - -- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) -- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns -- Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support) \ No newline at end of file diff --git a/CSSAnchoredPositioning/explainer.md b/CSSAnchoredPositioning/explainer.md deleted file mode 100644 index d9a2082cf..000000000 --- a/CSSAnchoredPositioning/explainer.md +++ /dev/null @@ -1,638 +0,0 @@ -# CSS Anchored Positioning - -Authors: - -* [Bo Cupp](https://github.com/BoCupp) (Microsoft) -* [Melanie Richards](https://github.com/melanierichards) (Microsoft) -* [Dan Clark](https://github.com/dandclark) (Microsoft) -* [Ionel Popescu](https://github.com/ipopescu93) (Microsoft) - -Acknowledgements: - -The anchored positioning scheme proposed here is influenced by Ian Kilpatrick and Tab Atkin's ideas on [extending position: absolute/fixed](https://gist.github.com/bfgeek/60d4f57092eadcda0d4f32a8eb23b4c8). Thank you Ian and Tab! - -## Status of this Document -This document is a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. - -* This document status: **Obsolete** -* Current venue: [W3C CSS Working Group](https://www.w3.org/Style/CSS/) | [w3c/csswg-drafts](https://github.com/w3c/csswg-drafts) -* **Current version: [CSS Anchor Positioning](https://drafts.csswg.org/css-anchor-1/) - -## Introduction - -When building interactive components or applications, authors frequently want to leverage UI elements that can render in a "top-layer". Examples of such UI elements include content pickers, teaching UI, tooltips, and menus. ["Enabling Popups"](../Popup/explainer.md) introduced a new `popup` element to make many of these top-layer elements easier to author. - -Authors frequently wish to "pin" or "anchor" such top-layer UI to a point on another element, referred to here as an "anchor element". How the top-layer UI is positioned with respect to its anchor element is further influenced or constrained by the edges of the layout viewport. - -Take, for example, a menu whose top-left point is anchored to the bottom-left point on a menu button. When there is insufficient space in the viewport below the button, the popup menu should be rendered above the menu button instead: - -![A button labeled 'anchor' above a menu popup. When the anchor button is close to the bottom edge of the viewport boundary, the menu is instead anchored to the top of the button](./images/Intro.png) - -This document proposes a mechanism to achieve that positioning in CSS. - -### Note on top-layer - -For the sake of this proposal, consider "top-layer" elements to be those whose positioning is specially managed by the browser. These elements are rendered in a new stacking layer closest to the user, or "above" all other UI in the web document. Refer to the [full-screen specification](https://fullscreen.spec.whatwg.org/#top-layer) for a working definition of "top layer". - -## Goals - -Enable authors to: - -* Anchor the position of browser-managed, top-layer elements (such as the proposed `popup`) to any other element, while accounting for the bounds of the layout viewport. -* Maintain a logical content order that matches the visual order, if the visual order has been impacted by anchored positioning.* -* Declaratively express a repositioning scheme for the anchored element in cases where there is not sufficient space in the layout viewport. -* Respond to repositioning events in order to make adjustments to directionally-aware styles on the anchored element. - -*Wherever possible, authors should ensure that the DOM and visual order match without requiring special handling. As this may prove difficult for some anchoring use cases, an anchored positioning scheme should provide a way to heal this mismatch and ensure accessible content traversal. - -## Use cases - -Example use cases include: - -* Temporal "teaching" elements that are applied to arbitrary pieces of UI in order to instruct the user about changes in the web application, provide introductory instruction on how to use the application, etc. -* Popups, such as menus invoked by a button. -* Indicator decorations, such as a "new!" bubble. - -## Proposed API - -### Declaring an anchor element - -First, the author must provide an anchor element, using the `anchor` HTML attribute proposed in ["Enabling Popups"](../Popup/explainer.md): - -```HTML -<button id="myButton" popup="myPopup">Anchor</button> -<popup id="myPopup" anchor="myButton">Anchored element</popup> -``` - -This could alternatively be expressed in script: - -```JS -document.getElementById('myPopup').anchor = document.getElementById('myButton'); -``` - -Authors must not anchor an element to one of its descendents. - -### Declaring preferred anchored position, with fallbacks - -In CSS, the author positions the element by: - -* Using a `fixed` positioning scheme -* Declaring sets of preferred and fallback positions using the `position-set` property and associated at-rules. -* Leveraging a new `anchor` CSS function. `anchor()` enables authors to reference edges of the anchor element where CSS lengths are used. - -Suppose that an author would like to anchor a menu to a button. They would prefer that the popup's top-left corner is anchored to the bottom-left corner of a button: - -![A button labeled 'anchor' above a menu popup](./images/Simple-Menu.png) - -However, if there were insufficient space in the block direction to render the popup menu, it would be acceptable to anchor the bottom edge of the popup to the top edge of the button: - -![The anchor button is close to the bottom edge of the viewport boundary, so the menu is anchored to its top](./images/Simple-Menu-V-Reposition.png) - -If there is insufficient space in the inline direction, it would be acceptable to anchor the popup's right-hand edge to the right-hand edge of the button. If there is not enough space in both directions the popup could be positioned as follows: - -![The anchor button is close to the bottom-right corner of the viewport boundary, so the menu is anchored to its bottom top](./images/Simple-Menu-H-V-Reposition.png) - -Here is how the author would express this in CSS: - -```html -<button id="myButton" popup="myPopup">Anchor</button> -<popup id="myPopup" anchor="myButton" role="menu">…</popup> -<style> - #myPopup { - position: fixed; - position-set: buttonMenuPos; - overflow: auto; - - /* The popup is at least as wide as the button */ - min-width: calc(anchor(right) - anchor(left)); - - /* The popup is at least as tall as 2 menu items */ - min-height: 6em; - } - - /* This example intentionally left verbose */ - @position-set buttonMenuPos { - /* First try to align the top, left edge of the popup - with the bottom, left edge of the button. */ - 1 { - top: anchor(bottom); - left: anchor(left); - - /* The popup is no wider than the distance from the - left button edge to the right edge of the viewport */ - max-width: calc(100vw - anchor(left)); - - /* clamp the height of the popup to be no taller than - the distance between the bottom of the button and the - bottom of the layout viewport. */ - max-height: calc(100vh - anchor(bottom)); - } - - /* Next try to align the bottom, left edge of the popup - with the top, left edge of the button. */ - 2 { - bottom: anchor(top); - left: anchor(left); - - max-width: calc(100vw - anchor(left)); - - /* clamp the height of the popup to be no taller than - the distance between the top of the button and the - bottom of the layout viewport. */ - max-height: anchor(top); - } - - /* Next try to align the top, right edge of the popup - with the bottom, right edge of the button. */ - 3 { - top: anchor(bottom); - right: anchor(right); - - /* The popup is no wider than the distance from the - right button edge to left edge of the viewport. */ - max-width: anchor(right); - - max-height: calc(100vh - anchor(bottom)); - } - - /* Finally, try to align the bottom, right edge of the popup - with the top, right edge of the button. Other positions are possible, - but this is the final option the author would like the rendering - engine to try. */ - 4 { - bottom: anchor(top); - right: anchor(right); - - /* The popup is no wider than the distance from the - right button edge to left edge of the viewport. */ - max-width: anchor(right); - - max-height: anchor(top); - } - } -</style> -``` - -_Sets are 1-based for consistency with other CSS features._ - -Breaking this apart: - -* The author uses the `@position-set` at-rule to create a custom-named set of candidate positions. 1-based integers are used as selectors for sets of positioning rules. -* This custom set name is then referenced as the value of the `position-set` property declared on the popup to be anchored. -* The `anchor(edge)` CSS function resolves to a length which is the distance between the opposing starting edge of the layout viewport and the named border-box edge of the anchoring element. The named edge can be physical (`top`, `left`, `bottom`, `right`) or logical (`block-start`, `inline-start`, `block-end`, `inline-end`). - -How the user agent will process these rules: - -* The user agent will begin at the position set at the lowest non-zero integer (e.g. position set `1`). -* The layout process will determine if the popup overflows the layout viewport when using the rules at the given selector. Rules influencing inline direction will be processed, then block direction. -* If there is overflow of the layout viewport, layout will try the position set at the next integer-selector (e.g. `2`). -* If all position sets in a given direction would cause overflow, the user agent will use the preferred position (at selector `1`). - -### Position set functions - -This scheme could be further extended by allowing functions in the `position-set` property. For example, `largestWidth(buttonMenuPos)` could use the first position set which causes the largest possible width without causing viewport overflow. `minOverflow(buttonMenuPos)` would choose the position set which causes the _least_ amount of overflow, if all options would cause overflow. - -In the previous example, the author declared a `min-height` to ensure that their viewport-aware `max-height` values didn't shrink to too small of a value. Instead of setting a static `min-height`, that author could instead use a `largestHeight` function: - -```html -<button id="myButton" popup="myPopup">Anchor</button> -<popup id="myPopup" anchor="myButton" role="menu">…</popup> -<style> - #myPopup { - position: fixed; - position-set: largestHeight(buttonMenuPos); - overflow: auto; - - /* The popup is at least as wide as the button */ - min-width: calc(anchor(right) - anchor(left)); - } - - @position-set buttonMenuPos { - … - } -``` - -The user agent will now favor the position set that results in the largest height. Suppose that position set `1` would not cause overflow of the layout viewport, but would cause overflow of the popup's contents, introducing a subscroller. Position set `2` provides enough space to render the popup such that all 4 menu items are visible without overflow. The user agent would use position set `2`. - -**Note:** if these functions are not introduced to `position-set` values, user agent processing rules for calculating layout viewport overflow would need to be changed to ignore `max-width` and `max-height`. That way, positioning logic can optimize for the anchored element's natural dimensions. This is necessary for cases where position set `1` would never cause viewport overflow (due to a viewport-aware `max-*` value and a missing or very small `min-*` value), but position set `2` would result in no content overflow. - -### DRY-ing up declared position sets - -In the first example, the author declared four separate position sets that the user agent should attempt in positioning the popup. Many of these styles are shared between the position sets, and could become cumbersome if the author adds more candidate positions. An author that wants to "DRY" up their code can instead use comma-separated position set selectors for shared styles. Here is the same example using comma-separated selectors: - -```html -<button id="myButton" popup="myPopup">Anchor</button> -<popup id="myPopup" anchor="myButton" role="menu">…</popup> -<style> - #myPopup { - position: fixed; - position-set: buttonMenuPos; - min-width: calc(anchor(right) - anchor(left)); - min-height: 6em; - } - - @position-set buttonMenuPos { - /* INLINE DIRECTION: first try to align the left edge of the popup - with the top, left edge of the button. */ - 1, 2 { - left: anchor(left); - max-width: calc(100vw - anchor(left)); - } - - /* INLINE DIRECTION: then try to align the right edge of the popup - with the right edge of the button. */ - 3, 4 { - right: anchor(right); - max-width: anchor(right); - } - - /* BLOCK DIRECTION: first try to align the top edge of the popup - with the bottom edge of the button. */ - 1, 3 { - top: anchor(bottom); - max-height: calc(100vh - anchor(bottom)); - } - - /* BLOCK DIRECTION: then try to align the bottom edge of the popup - with the top edge of the button. */ - 2, 4 { - bottom: anchor(top); - max-height: anchor(top); - } - } -</style> -``` - -To the user agent, there is no logical difference between this example and the previous syntax. Rules at the `1,2` selector and `1,3` selector will be processed together for position set `1`. If a property is declared more than once for a given position set, normal document order resolution applies. - -### Vertically center an element of unknown height next to its anchor - -Some authors may wish to center an element of unknown dimension with the midpoint of its anchor element. For example, this "teaching UI" is vertically centered next to the button it describes: - -![A button labeled 'anchor'. Next to it is a popup that says 'New! I'm some sort of educational UI' with additional filler text. The popup is vertically centered next to the button, with an arrow pointing to the button](./images/Center-Right.png) - -This is achievable using the `anchor()` function in `calc()` expressions, along with a `transform`: - -```html -<button id="myButton" popup="myPopup">Anchor</button> -<popup id="myPopup" anchor="myButton">…</popup> -<style> - #myPopup { - position: fixed; - position-set: verticallyCenteredPopup; - - /* The width will be fixed and the callout - will be as tall as it needs to be to fit - its contents. */ - width: 400px; - } - - @position-set verticallyCenteredPopup { - 1, 2 { - --anchorMidpoint: calc(anchor(top) + ((anchor(bottom) - anchor(top)) / 2)); - - /* align the top edge of the popup - with the middle of the anchor. */ - top: var(--anchorMidpoint); - /* clamp the height of the popup to be no taller than - double the minimum of a) the distance between the anchor - midpoint and the top of the layout viewport, or - b) the distance between the anchor midpoint and the - bottom of the layout viewport. */ - max-height: calc(min(var(--anchorMidpoint), calc(100vh - var(--anchorMidpoint))) * 2); - - /* vertically center the popup. */ - transform: translateY(-50%); - } - - 1 { - /* try to align the left edge of the popup - with the right edge of the anchor. */ - left: anchor(right); - } - - 2 { - /* try to align the right edge of the popup - with the left edge of the anchor. */ - right: anchor(left); - } - } -</style> -``` - -**Open question:** should `transform` participate in the user agent's calculations as to whether the position set will cause overflow? The `max-height` in this example ensures there will be no vertical overflow of the layout viewport, regardless of whether or not `transform` participates. - -### Applicability of the anchor positioning scheme - -This proposal currently restricts the applicability of the `anchor(edge)` CSS function to anchorable elements which have their display managed by the user agent (`popup` is the first example of such an element). This is because using both the edges of an anchor element and the edges of the layout viewport in layout calculations would require layout to be updated when the user scrolls, which would break independent scrolling. - -### Additional details - -* Similarly to `@keyframes`, properties included within `@position-set` will take precedence over other cascade values of those same properties. -* Authors may declare any CSS properties in the position sets, not just those impacting position or box dimensions. For example, perhaps a popup should have a light blue background only if is displayed to the right of its anchor. - -### Using JavaScript for position-aware operations - -When top-layer UI elements are shown, a `beforeshow` event will be dispatched prior to the display of those elements. The events are dispatched for each newly-shown element from bottom to top in the top-layer stack. This event is fired after input processing, and before (but as close as possible to) frame lifecycle methods: i.e. pre-cascade, pre-layout, pre-paint. Authors may use these events to handle adjustments to top-layer UI elements based on their position. - -For example, an author may want to adjust which "pointer" arrow is used for teaching UI: - -<img alt="Callout arrows positioned at different corners of an anchor button, depending on where the popup is positioned." src="./images/Directionally-aware.png"> - -If the teaching UI uses a fallback position set but the arrow decoration is not updated to match, it may appear to reference the wrong element: - -![The explanatory popup has shifted over, but the arrow is still pointing in the same direction, so it looks like it's pointing to a link instead of the button](./images/Decoration-H-Reposition-Bad.png) - -An author could provide this directionally-aware decoration using the `beforeshow` event, like so: - -```html -<button id="myButton" popup="myPopup">Anchor</button> -<popup id="myPopup" anchor="myButton"> - <img class="arrow top left" id="tlArrow" src="tl_arrow.png"> - <img class="arrow top right" id="trArrow" src="tr_arrow.png"> - <img class="arrow bottom left" id="blArrow" src="tr_arrow.png"> - <img class="arrow bottom right" id="brArrow" src="br_arrow.png"> - <p> - Lorem ipsum dolor sit amet, consectetur adipiscing elit... - </p> -</popup> -<style> - /* Position each arrow in its proper corner. - Note that top, right refers to when the popup - is positioned to the top, right of the anchor, - not when the arrow is in the top, right - position. */ - .arrow { - position: absolute; - width: 20px; - height: 20px; - } - .arrow.top { - bottom: -20px; - } - .arrow.bottom { - top: -20px; - } - .arrow.right { - left: 0; - } - .arrow.left { - right: 0; - } - - #myPopup { - position: fixed; - position-set: teachingUIPos; - - /* The width will be fixed and the callout - will be as tall as it needs to be to fit - its contents. */ - width: 400px; - } - - @position-set teachingUIPos { - 1, 2 { - left: anchor(right); - } - - 3, 4 { - right: anchor(left); - } - - 1, 3 { - bottom: anchor(top); - } - - 2, 4 { - top: anchor(bottom); - } - } -</style> -<script type="module"> - const popup = document.querySelector("#myPopup") - const tr_arrow = document.querySelector("#trArrow") - const tl_arrow = document.querySelector("#tlArrow") - const br_arrow = document.querySelector("#brArrow") - const bl_arrow = document.querySelector("#blArrow") - - popup.addEventListener("beforeshow", () => { - const pr = popup.getBoundingClientRect() - const ar = popup.anchor.getBoundingClientRect() - - tr_arrow.style.display = "none"; - tl_arrow.style.display = "none"; - br_arrow.style.display = "none"; - bl_arrow.style.display = "none"; - - if (pr.bottom >= ar.top) { - if (pr.left >= ar.right) { - // top, right display - tr_arrow.style.display = "block"; - } else { - // top, left display - tl_arrow.style.display = "block"; - } - } - else { - if (pr.left >= ar.right) { - // bottom, right display - br_arrow.style.display = "block"; - } else { - // bottom, left display - bl_arrow.style.display = "block"; - } - } - }) -</script> -``` - -## Privacy and security considerations - -### Privacy - -No considerable privacy concerns are expected, but we welcome community feedback. - -### Security - -To avoid attackers using top-layer elements to spoof trusted UI (browser, operating system, or top-level document), `popup`s are [not permitted to break out of their parent document](https://open-ui.org/components/popup.research.explainer#privacy-and-security-considerations). Similarly, elements cannot be anchored to each other across document boundaries. - -No other security concerns are expected. - -## Alternative solutions - -### Alternate A: Extending existing properties with comma-separated values - -One drawback of the proposed @-rules-based solution is that it creates a dependency on layout when determining the computed value of various CSS properties. This is relevant not only to `element.computedStyleMap()` and `window.getComputedStyle()` methods, but features like CSS transitions which need to detect changes at the computed-value level. - -As an alternative to the proposed @-rules syntax, existing properties could be extended to take a `position-set()` function as their value, with a set of comma-separated options representing preferred and fallback positions. This alternative could help ease some of the dependencies on layout, and is a viable option for consideration. We welcome author feedback on an @-rules- vs function-based approach. - -Consider the scenario where a menu should be positioned above its anchor button if there is insufficient space to display the menu below the button: - -![The anchor button is close to the right-hand edge of the viewport boundary, so the menu is anchored to its bottom right](./images/Simple-Menu-H-Reposition.png) - -The author would use comma-separated values to express a preferred position, then provide fallback positions: - -```html -<button id="myButton" popup="myPopup">Anchor</button> -<popup id="myPopup" anchor="myButton" role="menu">…</popup> -<style> - #myPopup { - /* First try to align the top, left edge of the popup - with the bottom, left edge of the button. */ - top: position-set(anchor(bottom), auto); - left: position-set(anchor(left), auto); - - /* Next try to align the bottom, right edge of the popup - with the top, right edge of the button. */ - bottom: position-set(auto, anchor(top)); - right: position-set(auto, anchor(right)); - - /* the popup is at least as wide as the button */ - min-width: calc(anchor(right) - anchor(left)); - - /* but is no wider than the distance from the - left button edge to the right edge of the viewport, - OR - the right button edge to left edge of the viewport. */ - max-width: position-set(calc(100vw - anchor(left)), anchor(right)); - - /* clamp the height of the the popup to be no taller than - the distance between the bottom of the button and the - bottom of the layout viewport. */ - max-height: calc(100vh - anchor(bottom)); - } -</style> -``` - -Repositioning requires that properties affecting the size of the margin box of the anchored element take multiple values. The values would be tried by the user agent's layout engine in succession until a set is found that doesn't result in overflow of the anchored element's preferred margin box size. - -First, properties affecting the inline direction of the margin box would be adjusted: - -* `left`, `right` -* `margin-left`, `margin-right`, `margin-inline-start`, `margin-inline-end` -* `width`, `min-width`, `max-width`, `min-inline-size`, `max-inline-size` -* `padding-left`, `padding-right`, `padding-inline-start`, `padding-inline-end` -* `border-left`, `border-right`, `border-inline-start`, `border-inline-end` - -In the example above, the first set of author-specified properties having an effect on the inline margin box size would be: - -```css - #my-popup { - left: anchor(left); - right: auto; - min-width: calc(anchor(right) - anchor(left)); - max-width: calc(100vw - anchor(left)); - } -``` - -Ignoring max-width, the layout process of the user agent would determine if the popup overflows the layout viewport. If it does, the next set of properties to be tried would be: - -```css - #my-popup { - left: auto; - right: anchor(right); - min-width: calc(anchor(right) - anchor(left)); - max-width: anchor(right); - } -``` - -The properties `left`, `right`, and `max-width` had a second value supplied which was used in the second attempt by the layout engine to find a non-overflowing result for the popup. `min-width` did not have a second value, so its first and only value was used. More generally, the number of layout attempts (assuming all results overflow) is equal to the maximum number of values supplied across the properties affecting the inline margin box size. When one of those properties has fewer values, the value used for a particular layout iteration is the iteration number modulo the length of that property's list. - -After the properties affecting the inline margin box size are resolved, the block size of the anchored element's margin box would go through a similar process using the resolved inline margin box property values as an input. - -Here is the example markup further modified to account for repositioning in the block direction: - -![The anchor button is close to the bottom edge of the viewport boundary, so the menu is anchored to its top](./images/Simple-Menu-V-Reposition.png) - -```html -<button id="myButton" popup="myPopup">Anchor</button> -<popup id="myPopup" anchor="myButton" role="menu">…</popup> -<style> - #myPopup { - /* First try to align the top, left edge of the popup - with the bottom, left edge of the button. */ - top: anchor(bottom), auto; - left: anchor(left), auto; - - /* Next try to align the bottom, right edge of the popup - with the top, right edge of the button. */ - bottom: auto, anchor(top); - right: auto, anchor(right); - - /* the popup is at least as wide as the button */ - min-width: calc(anchor(right) - anchor(left)); - - /* but is no wider than the distance from the - left button edge to the right edge of the viewport, - OR - the right button edge to left edge of the viewport. */ - max-width: calc(100vw - anchor(left)), anchor(right); - - /* clamp the height of the the popup to be no taller than: - 1. the distance between the bottom of the button - and the bottom of the layout viewport - OR - 2. the distance between the top of the button - and the top of the layout viewport */ - max-height: calc(100vh - anchor(bottom)), anchor(top); - } -</style> -``` - -If there is not enough space in both block and inline directions the resulting popup position would be positioned as follows: - -![The anchor button is close to the right-hand edge of the viewport boundary, so the menu is anchored to its bottom right](./images/Simple-Menu-H-V-Reposition.png) - -### Alternate B: A set of properties specific to anchored positioning - -**We opted not to go with this approach as it introduces many new primitives that are somewhat redundant to existing CSS features.** - -A previous (internal) version of this CSS anchored positioning scheme used the `anchor` HTML attribute as described previously, and introduced a batch of new CSS properties and values: - -* `position: anchor`, to specify the anchored positioning scheme -* `anchor-point`, to specify the point on the anchor to which the anchored element should be pinned. -* `anchored-origin`, to specify the point on the anchored element which should be pinned to the anchor. -* `anchor-point-inline-adjust`, `anchor-point-block-adjust`, `anchored-origin-inline-adjust`, and `anchored-origin-block-adjust` (along with `anchor-point-adjust` and `anchored-origin-adjust` shorthands), to specify how an anchored element should be positioned. -* `anchored-origin` supported a new keyword called `delegate`, which would delegate some anchoring responsibilities to a descendent of the anchored element (refer to "How do we support MacOS-style select anchoring?"). -* A new keyword `push` enabled authors to express to the user agent, "instead of snapping to a specific point on the anchor, push this anchored element an arbitrary number of pixels to ensure that it aligns with the viewport edge". - -## Open questions - -### How should maintenance of logical order work? - -One of the goals for this proposal states: "Maintain a logical content order that matches the visual order, if the visual order has been impacted by anchored positioning." - -Most likely, the `anchor` attribute should be the hook that hoists an anchored element somewhere else in the logical order. There are some interesting [interactions with the `popup` attribute](../Popup/explainer.md#open-questions) to consider, as well as where exactly the anchored element gets hoisted to. Open questions under this umbrella: - -* Should the anchored element be treated as a sibling of the anchor element, immediately succeeding it in tab cycles / accessibility tree structures? -* What happens if the context to which the anchored element is hoisted has a restrictive content model where said anchored element is not valid? -* In many cases, the visual order of anchored elements should already follow the DOM order, and no fixup is required. As a performance consideration: should anchored element hoisting be opt-in by the author, so that this complexity/logic can be skipped unless it is actually needed? - -A conceivable alternative is to impose restrictions on which elements can serve as an anchor, based on DOM structure. However, this may prove overly restrictive without actually solving the issue. For example: - -* **Supposing a sibling relationship is preferred:** either the anchor or anchored element may live inside non-semantic, non-interactive wrapper elements, which would break the sibling relationship even if focus/logical order is still valid. Alternately, sibling relationships do not guarantee a next-previous sibling relationship (i.e. there may be other semantically "interesting" and/or focusable siblings between the anchor and anchored element siblings). -* **Supposing we pick a number of ancestors to traverse through:** such a number will always be arbitrary, likely to confuse authors, and brittle to minor dynamic DOM structure changes. - -### Should the user agent provide a default repositioning scheme? - -Such an algorithm would need to be predictable, straightforward to implement for interoperability, and simple to understand in lay terms. As an example, UAs could mirror edge positions (e.g. a popup's top-left corner pinned to the anchor's bottom-left => top-right corner pinned to bottom-right) and nudge center positions (e.g. a popup with its top-center pinned will be nudged just enough pixels horizontally in order to stay in the viewport). Should authors need to a lightweight opt-in for default repositioning logic? - -### How could we better support directionally-aware styles? - -In this proposal's examples, we demonstrate how JavaScript could be used to update directionally-aware decorations, such as an arrow pointing to the anchor element: - -![An explanatory popup anchored to the bottom left of a button, with an arrow pointing to the bottom right of the button](./images/Decoration.png) - -It is likely that some authors would prefer to handle this in CSS only. The concern with providing a CSS-based solution is that layout computation needs to be completed in order to determine whether the anchored element should be repositioned. And yet, authors likely want to influence layout/rendering based on repositioning. Perhaps a solution could force an additional cycle. - -There is an opportunity to explore CSS-based solutions for styling descendents (including generated content) based on which position set was chosen. Perhaps nested selectors could be an interesting pathway to explore. - -### How might we support MacOS-style select anchoring? - -There are some cases where an author may want to delegate positioning to a descendent of the anchored element. For example, the author may want to mimic MacOS-style `<select>`s, where the selected `option` is centered over the `select`'s button part: - -![A custom select element where the selected option is centered over the select button](./images/MacOS-Style.png) - -With this logic, the vertical positioning of the entire listbox is determined by the selected `option`. Selecting a different `option` will change the listbox's position over the `<select`> button in the block direction. - -How might we support delegation of anchored positioning to a descendent? Alternatively, if this particular positioning logic is unique enough, it may be best handled by script (instead of complicating CSS API surface to absorb this use case). diff --git a/CSSAnchoredPositioning/images/Center-Right.png b/CSSAnchoredPositioning/images/Center-Right.png deleted file mode 100644 index a83d65313..000000000 Binary files a/CSSAnchoredPositioning/images/Center-Right.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/Decoration-H-Reposition-Bad.png b/CSSAnchoredPositioning/images/Decoration-H-Reposition-Bad.png deleted file mode 100644 index 489f93f59..000000000 Binary files a/CSSAnchoredPositioning/images/Decoration-H-Reposition-Bad.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/Decoration-H-Reposition.png b/CSSAnchoredPositioning/images/Decoration-H-Reposition.png deleted file mode 100644 index c5ba83e1e..000000000 Binary files a/CSSAnchoredPositioning/images/Decoration-H-Reposition.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/Decoration.png b/CSSAnchoredPositioning/images/Decoration.png deleted file mode 100644 index d8e5730a6..000000000 Binary files a/CSSAnchoredPositioning/images/Decoration.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/Directionally-aware.png b/CSSAnchoredPositioning/images/Directionally-aware.png deleted file mode 100644 index d11737f9c..000000000 Binary files a/CSSAnchoredPositioning/images/Directionally-aware.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/Intro.png b/CSSAnchoredPositioning/images/Intro.png deleted file mode 100644 index b1348fa62..000000000 Binary files a/CSSAnchoredPositioning/images/Intro.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/MacOS-Style-H-Reposition.png b/CSSAnchoredPositioning/images/MacOS-Style-H-Reposition.png deleted file mode 100644 index 85d324e43..000000000 Binary files a/CSSAnchoredPositioning/images/MacOS-Style-H-Reposition.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/MacOS-Style-V-Reposition.png b/CSSAnchoredPositioning/images/MacOS-Style-V-Reposition.png deleted file mode 100644 index ef5688035..000000000 Binary files a/CSSAnchoredPositioning/images/MacOS-Style-V-Reposition.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/MacOS-Style.png b/CSSAnchoredPositioning/images/MacOS-Style.png deleted file mode 100644 index bca627d15..000000000 Binary files a/CSSAnchoredPositioning/images/MacOS-Style.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/Popup-with-Clipped-Transformed-Anchor.png b/CSSAnchoredPositioning/images/Popup-with-Clipped-Transformed-Anchor.png deleted file mode 100644 index 2f3ee525c..000000000 Binary files a/CSSAnchoredPositioning/images/Popup-with-Clipped-Transformed-Anchor.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/Simple-Menu-H-Reposition.png b/CSSAnchoredPositioning/images/Simple-Menu-H-Reposition.png deleted file mode 100644 index 08b52095d..000000000 Binary files a/CSSAnchoredPositioning/images/Simple-Menu-H-Reposition.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/Simple-Menu-H-V-Reposition.png b/CSSAnchoredPositioning/images/Simple-Menu-H-V-Reposition.png deleted file mode 100644 index c29957ef7..000000000 Binary files a/CSSAnchoredPositioning/images/Simple-Menu-H-V-Reposition.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/Simple-Menu-V-Reposition.png b/CSSAnchoredPositioning/images/Simple-Menu-V-Reposition.png deleted file mode 100644 index 81c171c1c..000000000 Binary files a/CSSAnchoredPositioning/images/Simple-Menu-V-Reposition.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/Simple-Menu.png b/CSSAnchoredPositioning/images/Simple-Menu.png deleted file mode 100644 index 729c25778..000000000 Binary files a/CSSAnchoredPositioning/images/Simple-Menu.png and /dev/null differ diff --git a/CSSAnchoredPositioning/images/callout-positions.png b/CSSAnchoredPositioning/images/callout-positions.png deleted file mode 100644 index fa76fa52e..000000000 Binary files a/CSSAnchoredPositioning/images/callout-positions.png and /dev/null differ diff --git a/CSSColorMix/color-mix-squares.png b/CSSColorMix/color-mix-squares.png deleted file mode 100644 index 03f6616ba..000000000 Binary files a/CSSColorMix/color-mix-squares.png and /dev/null differ diff --git a/CSSColorMix/explainer.md b/CSSColorMix/explainer.md deleted file mode 100644 index bf0a89117..000000000 --- a/CSSColorMix/explainer.md +++ /dev/null @@ -1,149 +0,0 @@ -# CSS color-mix() function - -## Status of this Document -This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to -problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the -most current standards venue and content location of future work and discussions. -* This document status: **Archived** -* Expected venue: [CSSWG](https://drafts.csswg.org/) -* Current version: this document - -## Introduction - -Quoting from the [editor's draft](https://drafts.csswg.org/css-color-5/#intro): -> Web developers, design tools and design system developers often use color functions to assist in scaling the design of their component color relations. With the increasing -usage of design systems that support multiple platforms and multiple user preferences, like the increased capability of Dark Mode in UI, this becomes even more useful to not -need to manually set color, and to instead have a single source from which schemes are calculated. - -> Currently Sass, calc() on HSL values, or PostCSS is used to do this. However, preprocessors are unable to work on dynamically adjusted colors; all current solutions are -restricted to the sRGB gamut and to the perceptual limitations of HSL (colors are bunched up in the color wheel, and two colors with visually different lightness, like yellow -and blue, can have the same HSL lightness). - -## Goals - -* Allow authors to blend colors directly in CSS, in color gamuts beyond sRGB and in dynamic scenarios. - -## Use Cases - -One example application of `color-mix()` is tinting. The css-color-5 spec also proposes `color-adjust()`, which can adjust the lightness of a color up or down. This is -analogous to adjusting the tint of a paint color by mixing it with a pure white or pure black paint. The `color-mix()` function provides greater flexibility, by allowing for -adjustment using colors other than pure white or pure black. Doing so can provide "warmth" or "coolness" to a color palette, as seen in -[this example](https://alistapart.com/article/mixing-color-for-the-web-with-sass/#section5) using Sass. - -## Proposed Solution - -Proposed syntax for `color-mix()` function is specified in [css-color-5](https://drafts.csswg.org/css-color-5/#color-mix). In brief: -- Two (or more) `<color>`s are accepted as input. -- The author selects a color space for mixing (defaults to lch if not specified, but this is predicated on support from the user agent). -- The author specifies the relative weights of each input color, on a channel-by-channel basis. - -### Example 1: Tinting - -This example tints the borders of two elements using off-white and off-black adjustments, in sRGB colorspace, to create a simple customized 3D effect: - -``` html -<style> -:root { - --tint-color: rgb(242, 234, 138); - --shade-color: rgb(54, 52, 31); -} -.first { - --base-color: rgb(134, 146, 191); -} -.second { - --base-color: rgb(191, 134, 134); -} -.square { - width: 50px; - height: 50px; - border: 25px solid; - display: inline-block; - margin: 5px; - background-color: var(--base-color); - --highlight-color: color-mix(srgb var(--base-color) 25%, var(--tint-color)); - --shadow-color: color-mix(srgb var(--base-color) 25%, var(--shade-color)); - border-left-color: var(--highlight-color); - border-top-color: var(--highlight-color); - border-right-color: var(--shadow-color); - border-bottom-color: var(--shadow-color); -} -</style> -<div class="first square"></div> -<div class="second square"></div> -``` - -Here's what the custom tinted output looks like: - -![Rendered markup of squares tinted with color-mix](color-mix-squares.png) - -Contrast with adjustment using pure white and black: - -![Rendered markup of squares tinted with pure white and black](pure-tint-squares.png) - -### Example 2: Dynamic tinting - -This example uses a CSS Animation that adjusts the tint color to generate a "sunrise" effect. - -``` html -<script> - window.CSS.registerProperty({ - name: '--sun-color', - syntax: '<color>', - inherits: true, - initialValue: 'rgb(0, 0, 0)', -}); -</script> -<style> -@keyframes sunrise { - 0% { --sun-color: rgb(0, 0, 0); } - 50% { --sun-color: rgb(255, 129, 0); } - 100% { --sun-color: rgb(255, 240, 232); } -} -.container { - display: flex; - align-items: center; - font-size: 48px; - --base-color: rgb(134, 146, 191); - animation: sunrise 5s infinite linear; -} -.container div { - margin: 5px; -} -.square { - width: 100px; - height: 100px; -} -.sun { - background-color: var(--sun-color); -} -.sample { - background-color: var(--base-color); -} -.result { - background-color: color-mix(srgb var(--base-color) 25%, var(--sun-color)); -} -</style> -<div class="container"> - <div class="sun square"></div> - <div>+</div> - <div class="sample square"></div> - <div>=</div> - <div class="result square"></div> -</div> -``` - -Here's the rendered result: - -![Rendered markup of squares tinted with animated "sun" color](sunrise.gif) - - -## Privacy and Security Considerations - -### Privacy - -* Evaluating a `color-mix()` function may have a higher computational cost than using a directly specified color, especially if gamut conversions are required. Combining this -feature with `:visited` rules may allow for a timing attack that exposes the user's browsing history. - -### Security - -There are no known security impacts of this feature. diff --git a/CSSColorMix/pure-tint-squares.png b/CSSColorMix/pure-tint-squares.png deleted file mode 100644 index 4d1a95e9f..000000000 Binary files a/CSSColorMix/pure-tint-squares.png and /dev/null differ diff --git a/CSSColorMix/sunrise.gif b/CSSColorMix/sunrise.gif deleted file mode 100644 index 3217a997f..000000000 Binary files a/CSSColorMix/sunrise.gif and /dev/null differ diff --git a/CSSGapDecorations/explainer.md b/CSSGapDecorations/explainer.md deleted file mode 100644 index a8017760b..000000000 --- a/CSSGapDecorations/explainer.md +++ /dev/null @@ -1,757 +0,0 @@ -# CSS Gap Decorations - -## Authors - -- [Kevin Babbitt](https://github.com/kbabbitt) - -## Participate - -- [Issue tracker](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/CSSGapDecorations) - -## Status of this Document - -This document is intended as a starting point for engaging the community and -standards bodies in developing collaborative solutions fit for standardization. -As the solutions to problems described in this document progress along the -standards-track, we will retain this document as an archive and use this section -to keep the community up-to-date with the most current standards venue and -content location of future work and discussions. - -* This document status: **Active** -* Expected venue: [CSS Working Group](https://www.w3.org/Style/CSS/) -* Current version: this document - -## Table of Contents - -<!-- [You can generate a Table of Contents for markdown documents using a tool like [doctoc](https://github.com/thlorenz/doctoc).] --> - -<!-- START doctoc generated TOC please keep comment here to allow auto update --> -<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> - -- [CSS Gap Decorations](#css-gap-decorations) - - [Authors](#authors) - - [Participate](#participate) - - [Status of this Document](#status-of-this-document) - - [Table of Contents](#table-of-contents) - - [Introduction](#introduction) - - [Goals](#goals) - - [Non-goals](#non-goals) - - [User research](#user-research) - - [Properties](#properties) - - [Width, style, and color](#width-style-and-color) - - [Extending or shortening gap decoration segments](#extending-or-shortening-gap-decoration-segments) - - [Interaction with spanning items](#interaction-with-spanning-items) - - [Paint order](#paint-order) - - [Decorations next to empty areas](#decorations-next-to-empty-areas) - - [Key scenarios](#key-scenarios) - - [Scenario 1: Horizontal lines between CSS grid rows](#scenario-1-horizontal-lines-between-css-grid-rows) - - [Scenario 2: Lines dividing items in both directions of a grid](#scenario-2-lines-dividing-items-in-both-directions-of-a-grid) - - [Scenario 3: Segmented gap decorations](#scenario-3-segmented-gap-decorations) - - [Scenario 4: Grid layout with white space in leading columns](#scenario-4-grid-layout-with-white-space-in-leading-columns) - - [Scenario 5: Column decorations only between items](#scenario-5-column-decorations-only-between-items) - - [Future ideas](#future-ideas) - - [Images](#images) - - [Corner joins](#corner-joins) - - [Propagation of gap decorations into subgrids](#propagation-of-gap-decorations-into-subgrids) - - [Extensions to decoration visibility controls](#extensions-to-decoration-visibility-controls) - - [Placement of gap decorations](#placement-of-gap-decorations) - - [Scenario: Calendar grid with header column](#scenario-calendar-grid-with-header-column) - - [Scenario: Different lines for different gaps, applied to a sub-area of a grid](#scenario-different-lines-for-different-gaps-applied-to-a-sub-area-of-a-grid) - - [Scenario: Periodic Table omitting decorations from certain areas](#scenario-periodic-table-omitting-decorations-from-certain-areas) - - [Dropped ideas](#dropped-ideas) - - [Logical properties for flex and masonry containers](#logical-properties-for-flex-and-masonry-containers) - - [Considered alternatives](#considered-alternatives) - - [Alternative 1: 2021 draft specification](#alternative-1-2021-draft-specification) - - [Alternative 2: Using pseudo-elements](#alternative-2-using-pseudo-elements) - - [References \& acknowledgements](#references--acknowledgements) - -<!-- END doctoc generated TOC please keep comment here to allow auto update --> - -## Introduction - -CSS multi-column containers allow for -[rules](https://drafts.csswg.org/css-multicol-1/#cr) to be drawn between -columns. Applying similar styling to other container layouts such as grid and -flex has been widely sought after, as seen in the discussion for CSS Working -Group issue [2748](https://github.com/w3c/csswg-drafts/issues/2748) and in -several StackOverflow questions ( -[[1]](https://stackoverflow.com/questions/45884630/css-grid-is-it-possible-to-apply-color-to-grid-gaps) -[[2]](https://stackoverflow.com/questions/59899641/is-it-possible-to-draw-all-css-grid-lines-as-dotted-borders-or-outlines-if-js-i) -[[3]](https://stackoverflow.com/questions/47882924/preventing-double-borders-in-css-grid) -[[4]](https://stackoverflow.com/questions/67479163/css-border-doubling-with-flex) -). Currently, developers seeking to draw such decorations must resort to -non-ergonomic workarounds such as these examples: - -- https://www.smashingmagazine.com/2017/09/css-grid-gotchas-stumbling-blocks/#how-do-i-add-backgrounds-and-borders-to-grid-areas -- https://x.com/geddski/status/1004731709764534274 - -## Goals - -* Extend CSS [column rule - properties](https://drafts.csswg.org/css-multicol-1/#column-gaps-and-rules) to - apply to other container layouts such as grid, flex, and masonry. -* Introduce row-direction gap decorations on CSS container layouts. -* Allow gap decorations to vary over a given container to handle cases such as - alternating row separators. - -## Non-goals - -* Gap decorations on CSS Tables. The [CSS Tables - specification](https://drafts.csswg.org/css-tables-3/) is currently Not Ready - for Implementation, and there are interoperability differences among engines. - Additionally, authors can achieve many of the scenarios covered by this - explainer in a table already using cell borders. -* Images in gap decorations. Further exploration is needed into the best way to - handle these, so this scenario is left as a [future idea](#images). - -## User research - -Use cases in this explainer were collected from the discussion in issue -[2748](https://github.com/w3c/csswg-drafts/issues/2748). Additional inspiration -was drawn from discussions in issues -[5080](https://github.com/w3c/csswg-drafts/issues/5080), -[6748](https://github.com/w3c/csswg-drafts/issues/6748), and -[9482](https://github.com/w3c/csswg-drafts/issues/9482). - -## Properties - -Unless otherwise noted, corresponding `row-` and `column-` properties should be -assumed to have identical syntax. All such pairs of properties also have `gap-` -shorthands that apply the same values in both directions. - -For property grammar details, please see the -[Editor's Draft](https://drafts.csswg.org/css-gaps-1/). - -### Width, style, and color - -In addition to replicating the existing column-rule properties in the row -direction, we expand the syntax of both sets of properties to allow for multiple -definitions. If a given property has fewer list entries than the number of gaps, -the list is cycled through from the beginning as needed. - -Authors may also use familiar syntax from CSS Grid such as `repeat()` -and `auto` to create patterns of line definitions. Note that while `repeat()` and `auto` -are inspired by CSS Grid, they may also be used to create patterns of decorations -in flex, multi-column, and masonry containers. - -Shorthands are also available to combine the width, style, and color properties. - -```css -.alternate-red-blue { - display: grid; - grid-template: repeat(auto-fill, 30px) / repeat(3, 100px); - grid-gap: 10px; - row-rule: 1px solid; - row-rule-color: red blue; -} -``` -<image src="images/example-red-blue.png"> - -```css -.alternate-heavy-light { - display: grid; - grid-template: repeat(auto-fill, 30px) / repeat(3, 100px); - grid-gap: 10px; - row-rule: 2px solid black, 1px solid lightgray; -} -``` -<image src="images/example-heavy-light.png"> - -Like column rules in multi-column layout, gap decorations in other layout -containers do not take up space and do not affect the layout of items in the -container. Conceptually, gap decorations are considered after layout has -completed, and in particular after we already know the full extent of the -[implicit grid](https://drafts.csswg.org/css-grid-2/#implicit-grid) in grid -layout, or the number of lines in flex layout, or the number of columns in -multi-column layout, or the number of tracks in masonry layout. Thus, the -`repeat()` grammar, while modeled after the `grid-template` properties, is -simpler for gap decorations as there are fewer unknowns to consider. - -```css -.varying-widths { - dispay: grid; - grid-template: repeat(auto-fill, 30px) / repeat(3, 100px); - row-gap: 9px; - row-rule: 5px solid black, repeat(auto, 1px solid black), 3px solid black; -} -.item { - height: 30px; - padding: 5px; - border: 1px dotted lightgray; -} -``` - -<image src="images/example-width-style-color.png"> - -### Extending or shortening gap decoration segments - -By default, gap decorations are painted as continuous segments that extend as -far as possible along the centerline of a given gap. The decoration is painted -from one gap T intersection to another, with both endpoints at the centers of -the T crossings and the decoration proceeding along the stems of both Ts. In -grid layout, row decorations are painted on top of column decorations by -default; changing this behavior is covered in a later section of this document. - -```css -.grid-with-spans { - display: grid; - grid-template: repeat(4, 100px) / repeat(4, 100px); - gap: 20px; - row-rule: 6px solid red; - column-rule: 6px solid blue; -} -``` - -<image src="images/example-grid-with-spans.png"> - -```css -.flex { - display: flex; - flex-wrap: wrap; - gap: 20px; - width: 500px; - row-rule: 6px solid red; - column-rule: 6px solid blue; -} -``` -<image src="images/example-flex.png"> - -Authors may adjust the positions of endpoints relative to gap intersections, -either as a fixed distance or as a percentage of the width of the intersection. -The "zero point" is the edge of the intersection, with negative values extending -into the intersection and positive values receding from it. - -```css -.inset-0px { - column-rule-inset: 0px; -} -``` -<image src="images/example-column-inset-0px.png"> - -```css -.inset-5px { - column-rule-inset: -5px; -} -``` -<image src="images/example-column-inset-5px.png"> - -```css -.inset-negative-5px { - column-rule-inset: 5px; -} -``` -<image src="images/example-column-inset-minus-5px.png"> - -Authors may also adjust endpoints more granularly, making a distinction between "edge" -endpoints (which fall on the edge of the container), -and "interior" endpoints (any endpoint that is not an "edge"). - -```css -.edge-interior-insets { - column-rule-edge-inset: 0px; - column-rule-interior-inset: 5px; -} -``` - -<image src="images/example-column-interior-inset-5px.png"> - -Similarly, authors can have even more granular control to adjust the positions of endpoints, -making a distinction between "start" and "end" endpoints, in addition to the "edge" and "interior" distinction. - -```css -.start-end-edge-interior-insets { - column-rule-start-edge-inset: 0px; - column-rule-end-edge-inset: 8px; - column-rule-start-interior-inset: 0px; - column-rule-end-interior-inset: 8px; -} -``` - -<image src="images/example-column-start-end-edge-interior-insets.png"> - -### Interaction with spanning items - -Authors may also change the set of intersections where gap decorations break, -from the default "T intersections" behavior to either "all intersections" or "no intersections." -In the latter case, gap decorations paint "behind" items in the container. - -```css -.t-intersections { - gap-rule-break: spanning-item; - gap-rule-inset: 0px; -} -``` -<image src="images/example-break-spanning-item.png"> - -```css -.all-intersections { - gap-rule-break: intersection; - gap-rule-inset: 0px; -} -``` -<image src="images/example-break-intersection.png"> - -```css -.no-intersections { - gap-rule-break: none; -} -``` -<image src="images/example-break-none.png"> - -### Paint order - -When row and column gap decorations overlap, authors can control their painting -order. - -```css -rule-overlap: [ row-over-column | column-over-row ] -``` - -```css -.row-over-column { - row-rule: 6px solid red; - column-rule: 6px solid blue; - rule-overlap: row-over-column; -} -``` -<image src="images/example-row-over-column.png"> - -```css -.column-over-row { - row-rule: 5px solid red; - column-rule: 5px solid blue; - rule-overlap: column-over-row; -} -``` -<image src="images/example-column-over-row.png"> - -### Decorations next to empty areas - -By default, gap decoration segments appear throughout a container. -In some cases, authors may not want to paint segments next to empty areas. -The `*-rule-visibility-items` properties allow control over this. - -```css -.container { - display: grid; - grid-template: repeat(3, 100px) / repeat(3, 100px); - gap: 10px; - rule: 1px solid black; - rule-break: intersection; - rule-visibility-items: all; /* initial value */ -} -.item { - background: lightgray; -} -``` -```html -<div class="container"> - <div class="item" style="grid-area: 1 / 1">Item 1</div> - <div class="item" style="grid-area: 2 / 1">Item 2</div> - <div class="item" style="grid-area: 2 / 2">Item 3</div> - <div class="item" style="grid-area: 3 / 1">Item 4</div> -</div> -``` - -<image src="images/example-rule-visibility-items-all.png"> - -```css -.container { - display: grid; - grid-template: repeat(3, 100px) / repeat(3, 100px); - gap: 10px; - rule: 1px solid black; - rule-break: intersection; - rule-visibility-items: around; -} -.item { - background: lightgray; -} -``` -```html -<div class="container"> - <div class="item" style="grid-area: 1 / 1">Item 1</div> - <div class="item" style="grid-area: 2 / 1">Item 2</div> - <div class="item" style="grid-area: 2 / 2">Item 3</div> - <div class="item" style="grid-area: 3 / 1">Item 4</div> -</div> -``` - -<image src="images/example-rule-visibility-items-around.png"> - -```css -.container { - display: grid; - grid-template: repeat(3, 100px) / repeat(3, 100px); - gap: 10px; - rule: 1px solid black; - rule-break: intersection; - rule-visibility-items: between; -} -.item { - background: lightgray; -} -``` -```html -<div class="container"> - <div class="item" style="grid-area: 1 / 1">Item 1</div> - <div class="item" style="grid-area: 2 / 1">Item 2</div> - <div class="item" style="grid-area: 2 / 2">Item 3</div> - <div class="item" style="grid-area: 3 / 1">Item 4</div> -</div> -``` - -<image src="images/example-rule-visibility-items-between.png"> - -Note that `rule-visibility-items` in the examples above is a shorthand -for `column-rule-visibility-items` and `row-rule-visibility-items`, -which can also be set independently: - -```css -.container { - display: grid; - grid-template: repeat(3, 100px) / repeat(3, 100px); - gap: 10px; - rule: 1px solid black; - rule-break: intersection; - column-rule-visibility-items: around; - row-rule-visibility-items: between; -} -.item { - background: lightgray; -} -``` -```html -<div class="container"> - <div class="item" style="grid-area: 1 / 1">Item 1</div> - <div class="item" style="grid-area: 2 / 1">Item 2</div> - <div class="item" style="grid-area: 2 / 2">Item 3</div> - <div class="item" style="grid-area: 3 / 1">Item 4</div> -</div> -``` - -<image src="images/example-rule-visibility-items-independent.png"> - -## Key scenarios - -### Scenario 1: Horizontal lines between CSS grid rows - -https://github.com/w3c/csswg-drafts/issues/2748#issuecomment-446379068, which -links to: https://codepen.io/urlyman/pen/yGNOya - -> The desired effect is a line appearing only between the grid rows, and -> extending unbroken across the column gaps. -> -> Note that I don't want a line to appear above or beneath all rows, only in the -> gaps between rows. - -```css -.container { - row-rule: 1px solid #ccc; -} -``` - -<image src="images/csswg-drafts-issues-2748-issuecomment-446379068.png"> - -### Scenario 2: Lines dividing items in both directions of a grid - -https://github.com/w3c/csswg-drafts/issues/2748#issuecomment-595663212 - -```css -.container { - rule: thick solid green; -} -``` - -<image src="images/csswg-drafts-issues-2748-issuecomment-595663212.png"> - -### Scenario 3: Segmented gap decorations - -https://github.com/w3c/csswg-drafts/issues/2748#issuecomment-446781218 - last -example - -```css -.container { - rule: 1px solid black; - column-rule-inset: 0px; -} -``` - -<image -src="images/csswg-drafts-issues-2748-issuecomment-446781218-last-example.png"> - -### Scenario 4: Grid layout with white space in leading columns - -https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/1099 - -```css -.layout { - display: grid; - grid-template-areas: - ". . content author" - ". . content social"; - gap: 5px; - rule: 1px solid gray; - rule-visibility-items: around; - border-top: 1px solid gray; -} -``` - -<image src="images/explainer-issue-1099.png"> - -### Scenario 5: Column decorations only between items - -https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/1100 - -```css -.layout { - display: grid; - grid-template-columns: 400px 1000px; - column-gap: 90px; - row-gap: 50px; - column-rule: 1px solid white; - column-rule-visibility-items: between; -} -``` - -<image src="images/explainer-issue-1100.png"> - -## Future ideas - -### Images - -Much like `border-image`, support for images in gap decorations would allow for -more decorative separators to be used. These could be purely design choices, or -they could be used to achieve practical effects such as coupon borders. -Examples: - -* https://github.com/w3c/csswg-drafts/issues/2748#issuecomment-446781218 - third example - - <image src="images/csswg-drafts-issues-2748-issuecomment-446781218-third-example.png"> - -* https://github.com/w3c/csswg-drafts/issues/2748#issuecomment-621983931 - - <image src="images/csswg-drafts-issues-2748-issuecomment-621983931-first-example.png"> - -However, unlike `border-image`, gap decoration images need to cover -significantly more cases, such as T intersections and cross intersections. More -detail and examination of this issue: - -* https://github.com/w3c/csswg-drafts/issues/5080#issuecomment-1526585163 -* https://github.com/w3c/csswg-drafts/issues/2748#issuecomment-623039817 - -### Corner joins - -In [Issue 985](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/985), it -was suggested that we apply a `border-radius` like property to gap decorations -to allow for more flexible styling near intersections. We could also potentially -reuse concepts from `corner-shape` for even more flexibility. This idea is tracked -in [CSSWG Issue 12150](https://github.com/w3c/csswg-drafts/issues/12150). - -### Propagation of gap decorations into subgrids - -CSS Grid Level 2 defines the -[subgrid](https://www.w3.org/TR/css-grid-2/#subgrids) feature. A subgrid matches -up its grid lines to lines in the parent grid. Accordingly, gaps will also align -between a subgrid and its parent grid, though the sizes of these gaps may -differ. There may be use cases for propagating gap decorations from the parent -grid into corresponding gaps in the subgrid; we could perhaps do this with a -special keyword on the `*-rule-width`, `*-rule-style`, and `*-rule-color` -properties. See [CSSWG Issue -12326](https://github.com/w3c/csswg-drafts/issues/12326) for further discussion. - -### Extensions to decoration visibility controls - -Design discussions for `*-rule-visibility-items` also considered companion -`*-rule-visibility-self` properties which would allow the container-wide value to -be overridden on specific items. For example, an author who wants to draw -decorations only around a specific item in the container might set -`rule-visibility-items: none` on the container, and -`rule-visibility-self: around` on the specific item that they want to draw around. - -`start-side` and `end-side` have also been suggested as additional values for both -`*-rule-visibility-items` and `*-rule-visibility-self`, to draw decorations only -on one side or the other of items. - -### Placement of gap decorations - -An author may want to apply different sets of gap decorations to different -regions of a given container layout. We refer to such regions as a *gap -decoration areas*. The examples below illustrate how these might work on a grid -container; gap decoration areas on other container types have not yet been -explored. - -The author defines these areas using the `rule-areas` property. Each area is -defined by giving it first a name, then a tuple of numbers or line names which -work exactly as they would in the `grid-area` property: - -```css - rule-areas: --first-row 1 / 1 / 2 / -1, --first-column 1 / 1 / -1 / 2; -``` - -On a grid container, the above value of `rule-areas` would define an area named -`--first-row` that includes the grid lines within and around the first row (row -line 1, column line 1 to row line 2, column line -1) and an area named -`--first-column` that includes the grid lines within and around the first column -(row line 1, column line 1 to row line -1, column line 2). These areas are -inclusive of the grid lines on their edges. - -Then, on other gap decoration properties such as `*-rule-width`, `*-rule-style`, -and `*-rule-color`, the author can then specify first a "default" set of values -for the container, then a named area, then a set of values that applies to that -area, and so on: - -```css - rule: 1px solid black, 1px solid gray [--first-row] 3px solid black, 5px solid black [--first-column] 1px solid blue; -``` - -Cycling behavior applies in named areas the same as it does elsewhere, and where -multiple values would cover the same segment of a gap, the last one that applies -will "win". Thus, the value above would apply alternating 1px solid black and -1px solid gray rules to the grid in general, then override gaps in the first row -with alternating 3px solid black and 5px solid black rules, then on top of that -override gaps in the first column with 1px solid blue rules. - -#### Scenario: Calendar grid with header column - -```css -.grid-multiple-decoration-areas { - display: grid; - grid-template-rows: [top] 30px [main-top] repeat(6, 30px) [bottom]; - grid-template-columns: [left] 100px [main-left] repeat(3, 100px) [right]; - gap: 10px; - rule-areas: --month-column left / top / main-left / bottom; - row-rule: 1px solid black [--month-column] 1px solid lightblue; - column-rule: [--month-column] 1px solid lightblue; -} -``` - -<image src="images/example-multiple-areas.png"> - -#### Scenario: Different lines for different gaps, applied to a sub-area of a grid - -https://github.com/w3c/csswg-drafts/issues/2748#issuecomment-595889781 - -```css -.container { - rule-style: solid: - rule-color: lightgray; - rule-areas: --main 2 / 2 / -1 / -1; - column-rule-width: [--main] 1px repeat(auto, 2px) 1px; - row-rule-width: [--main] 0px repeat(auto, 2px 1px); -} -``` - -<image src="images/csswg-drafts-issues-2748-issuecomment-595889781.png"> - -#### Scenario: Periodic Table omitting decorations from certain areas - -https://github.com/w3c/csswg-drafts/issues/12024#issuecomment-3086244002 - -```css -.container { - display: grid; - grid-template: repeat(4, 280px) / repeat(8, auto); - gap: 20px; - rule-areas: --top-center 1 / 4 / 2 / 6, --bottom-left -2 / 1 / -1 / 2, --bottom-right -2 / -1 / -1 / -1; - column-rule: 18px solid red [--top-center] none [--bottom-left] none [--bottom-right] none; -} -``` - -<image src="images/csswg-drafts-issue-12024-issuecomment-3086244002-first-example.png"> - -## Dropped ideas - -### Logical properties for flex and masonry containers - -*This idea was dropped based on feedback raised in the [initial proposal discussion](https://github.com/w3c/csswg-drafts/issues/10393).* - -These are designed to enable scenarios where authors wish to switch -`flex-direction` or `masonry-direction` based on space constraints or other -factors. - -| Property | row or row-reverse direction | column or column-reverse direction | -|------------------|------------------------------|------------------------------------| -| main-rule-width | row-rule-width | column-rule-width | -| main-rule-style | row-rule-style | column-rule-style | -| main-rule-color | row-rule-color | column-rule-color | -| main-rule | row-rule | column-rule | -| cross-rule-width | column-rule-width | row-rule-width | -| cross-rule-style | column-rule-style | row-rule-style | -| cross-rule-color | column-rule-color | row-rule-color | -| cross-rule | column-rule | row-rule | - -And so on for other properties. - -For flex and masonry containers, the logical properties map based on -`flex-direction` or `masonry-direction` following the convention above. - -For grid containers, `main` maps to `row`, and `cross` maps to `column`. - -For multi-column containers, `main` maps to `column`, and `cross` maps to `row`. - -## Considered alternatives - -### Alternative 1: 2021 draft specification - -In 2021, Mats Palmgren from Mozilla posted a [draft -specification](https://matspalmgren.github.io/css-gap-decorations/Overview.html) -for gap decorations. We believe the proposal in this explainer improves on -developer ergonomics by (a) reusing concepts from grid layout such as repeat and -grid lines, and (b) simplifying the model for fine-tuning segment placement. We -also believe the proposal in this explainer offers developers more flexibility -even absent support for gap decoration images; see Scenario 3 for one example. - -### Alternative 2: Using pseudo-elements - -An alternative approach to `column-rule-*` and `row-rule-*` properties would be -to introduce pseudo-elements representing gaps, for example: - -```css -.container { - display: grid; - grid-template: auto / auto; - gap: 5px; -} -.container::column-gaps { - background: red; - width: 1px; -} -.container::row-gaps { - background: blue; - width: 1px; -} -``` - -In some ways, this would be more powerful, as it would allow for more -flexibility for what can be placed in the gaps. - -However, this approach also comes with drawbacks. Varying gap decorations over a -container becomes much harder. One might imagine a `::row-gaps::nth(even)` -pseudo selector to style every other row gap. However, certain container types -such as grid can automatically generate rows and columns depending on their -contents. That means we don't know until layout time how many such pseudo styles -we need to produce, which creates a wrong-way dependency between layout and -style. It would also mean that, for large containers, we would incur the costs -of calculating and storing styles for every single gap. That would be a large -overhead to absorb, especially considering that the more common case is to have -at most a single decoration style for a given container. - -## References & acknowledgements - -Many thanks for valuable feedback and advice from: - -- <a href="https://github.com/alico-cra">@alico-cra</a> -- Ahmad Shadeed -- Alison Maher -- Benoît Rouleau -- Elika Etemad -- Ian Kilpatrick -- Josh Tumath -- Kurt Catti-Schmidt -- Lea Verou -- Oliver Williams -- Rachel Andrew -- Sam Davis Omekara Jr. -- Sebastian Zartner -- Tab Atkins-Bittner diff --git a/CSSGapDecorations/images/csswg-drafts-issue-12024-issuecomment-3086244002-first-example.png b/CSSGapDecorations/images/csswg-drafts-issue-12024-issuecomment-3086244002-first-example.png deleted file mode 100644 index 6c7bd49a2..000000000 Binary files a/CSSGapDecorations/images/csswg-drafts-issue-12024-issuecomment-3086244002-first-example.png and /dev/null differ diff --git a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-1765376966-1.png b/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-1765376966-1.png deleted file mode 100644 index fa12b32a0..000000000 Binary files a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-1765376966-1.png and /dev/null differ diff --git a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-1765376966-2.png b/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-1765376966-2.png deleted file mode 100644 index c31455b00..000000000 Binary files a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-1765376966-2.png and /dev/null differ diff --git a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-446379068.png b/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-446379068.png deleted file mode 100644 index 8e4c215ae..000000000 Binary files a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-446379068.png and /dev/null differ diff --git a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-446781218-last-example.png b/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-446781218-last-example.png deleted file mode 100644 index 8b02a8e39..000000000 Binary files a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-446781218-last-example.png and /dev/null differ diff --git a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-446781218-third-example.png b/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-446781218-third-example.png deleted file mode 100644 index 01c816a9e..000000000 Binary files a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-446781218-third-example.png and /dev/null differ diff --git a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-595663212.png b/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-595663212.png deleted file mode 100644 index e99ba0ed7..000000000 Binary files a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-595663212.png and /dev/null differ diff --git a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-595889781.png b/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-595889781.png deleted file mode 100644 index e631badb6..000000000 Binary files a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-595889781.png and /dev/null differ diff --git a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-621983931-first-example.png b/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-621983931-first-example.png deleted file mode 100644 index 515fff39a..000000000 Binary files a/CSSGapDecorations/images/csswg-drafts-issues-2748-issuecomment-621983931-first-example.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-break-intersection.png b/CSSGapDecorations/images/example-break-intersection.png deleted file mode 100644 index e16c55ef1..000000000 Binary files a/CSSGapDecorations/images/example-break-intersection.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-break-none.png b/CSSGapDecorations/images/example-break-none.png deleted file mode 100644 index 09d692a6a..000000000 Binary files a/CSSGapDecorations/images/example-break-none.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-break-spanning-item.png b/CSSGapDecorations/images/example-break-spanning-item.png deleted file mode 100644 index b4f8b840c..000000000 Binary files a/CSSGapDecorations/images/example-break-spanning-item.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-column-inset-0px.png b/CSSGapDecorations/images/example-column-inset-0px.png deleted file mode 100644 index f48e8e9db..000000000 Binary files a/CSSGapDecorations/images/example-column-inset-0px.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-column-inset-50percent.png b/CSSGapDecorations/images/example-column-inset-50percent.png deleted file mode 100644 index 04ca53f4c..000000000 Binary files a/CSSGapDecorations/images/example-column-inset-50percent.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-column-inset-5px.png b/CSSGapDecorations/images/example-column-inset-5px.png deleted file mode 100644 index 07b8a2c92..000000000 Binary files a/CSSGapDecorations/images/example-column-inset-5px.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-column-inset-minus-5px.png b/CSSGapDecorations/images/example-column-inset-minus-5px.png deleted file mode 100644 index ee3cd4d5e..000000000 Binary files a/CSSGapDecorations/images/example-column-inset-minus-5px.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-column-interior-inset-5px.png b/CSSGapDecorations/images/example-column-interior-inset-5px.png deleted file mode 100644 index 60b7e26ea..000000000 Binary files a/CSSGapDecorations/images/example-column-interior-inset-5px.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-column-over-row.png b/CSSGapDecorations/images/example-column-over-row.png deleted file mode 100644 index 68e7ade1d..000000000 Binary files a/CSSGapDecorations/images/example-column-over-row.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-column-start-end-edge-interior-insets.png b/CSSGapDecorations/images/example-column-start-end-edge-interior-insets.png deleted file mode 100644 index 9e1b64af3..000000000 Binary files a/CSSGapDecorations/images/example-column-start-end-edge-interior-insets.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-flex.png b/CSSGapDecorations/images/example-flex.png deleted file mode 100644 index 331606c1f..000000000 Binary files a/CSSGapDecorations/images/example-flex.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-grid-with-spans.png b/CSSGapDecorations/images/example-grid-with-spans.png deleted file mode 100644 index b3c0fbe6c..000000000 Binary files a/CSSGapDecorations/images/example-grid-with-spans.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-heavy-light.png b/CSSGapDecorations/images/example-heavy-light.png deleted file mode 100644 index 6b5ee175d..000000000 Binary files a/CSSGapDecorations/images/example-heavy-light.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-multiple-areas.png b/CSSGapDecorations/images/example-multiple-areas.png deleted file mode 100644 index 0e6389a9f..000000000 Binary files a/CSSGapDecorations/images/example-multiple-areas.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-red-blue.png b/CSSGapDecorations/images/example-red-blue.png deleted file mode 100644 index eca9c0ab7..000000000 Binary files a/CSSGapDecorations/images/example-red-blue.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-row-over-column.png b/CSSGapDecorations/images/example-row-over-column.png deleted file mode 100644 index 299621d53..000000000 Binary files a/CSSGapDecorations/images/example-row-over-column.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-rule-break-none.png b/CSSGapDecorations/images/example-rule-break-none.png deleted file mode 100644 index e4a157f1b..000000000 Binary files a/CSSGapDecorations/images/example-rule-break-none.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-rule-visibility-items-all.png b/CSSGapDecorations/images/example-rule-visibility-items-all.png deleted file mode 100644 index a7206a775..000000000 Binary files a/CSSGapDecorations/images/example-rule-visibility-items-all.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-rule-visibility-items-around.png b/CSSGapDecorations/images/example-rule-visibility-items-around.png deleted file mode 100644 index 4e66bec66..000000000 Binary files a/CSSGapDecorations/images/example-rule-visibility-items-around.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-rule-visibility-items-between.png b/CSSGapDecorations/images/example-rule-visibility-items-between.png deleted file mode 100644 index def736d60..000000000 Binary files a/CSSGapDecorations/images/example-rule-visibility-items-between.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-rule-visibility-items-independent.png b/CSSGapDecorations/images/example-rule-visibility-items-independent.png deleted file mode 100644 index a3d709a57..000000000 Binary files a/CSSGapDecorations/images/example-rule-visibility-items-independent.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-segment-inset-0px.png b/CSSGapDecorations/images/example-segment-inset-0px.png deleted file mode 100644 index 0a0f06b9d..000000000 Binary files a/CSSGapDecorations/images/example-segment-inset-0px.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-segment-inset-minus-5px.png b/CSSGapDecorations/images/example-segment-inset-minus-5px.png deleted file mode 100644 index 52ead55e1..000000000 Binary files a/CSSGapDecorations/images/example-segment-inset-minus-5px.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-segment-inset-plus-5px.png b/CSSGapDecorations/images/example-segment-inset-plus-5px.png deleted file mode 100644 index bb47fb01c..000000000 Binary files a/CSSGapDecorations/images/example-segment-inset-plus-5px.png and /dev/null differ diff --git a/CSSGapDecorations/images/example-width-style-color.png b/CSSGapDecorations/images/example-width-style-color.png deleted file mode 100644 index e52c45bc5..000000000 Binary files a/CSSGapDecorations/images/example-width-style-color.png and /dev/null differ diff --git a/CSSGapDecorations/images/explainer-issue-1099.png b/CSSGapDecorations/images/explainer-issue-1099.png deleted file mode 100644 index 7e09d4d5c..000000000 Binary files a/CSSGapDecorations/images/explainer-issue-1099.png and /dev/null differ diff --git a/CSSGapDecorations/images/explainer-issue-1100.png b/CSSGapDecorations/images/explainer-issue-1100.png deleted file mode 100644 index 10d8dea19..000000000 Binary files a/CSSGapDecorations/images/explainer-issue-1100.png and /dev/null differ diff --git a/CSSGapDecorations/security-privacy-questionnaire.md b/CSSGapDecorations/security-privacy-questionnaire.md deleted file mode 100644 index c778163eb..000000000 --- a/CSSGapDecorations/security-privacy-questionnaire.md +++ /dev/null @@ -1,76 +0,0 @@ -# Security and Privacy Questionnaire for CSS Gap Decorations - -https://www.w3.org/TR/security-privacy-questionnaire/#questions - -## 2.1 What information might this feature expose to Web sites or other parties, and for what purposes is that exposure necessary? - -This feature does not expose any new information to Web sites or other parties. - -## 2.2 Do features in your specification expose the minimum amount of information necessary to enable their intended uses? - -Yes (none). - -## 2.3 How do the features in your specification deal with personal information, personally-identifiable information (PII), or information derived from them? - -No handling of personal information, PII, or information derived from them. - -## 2.4 How do the features in your specification deal with sensitive information? - -No handling of sensitive information. - -## 2.5 Do the features in your specification introduce new state for an origin that persists across browsing sessions? - -No. - -## 2.6 Do the features in your specification expose information about the underlying platform to origins? - -No. - -## 2.7 Does this specification allow an origin to send data to the underlying platform? - -No. - -## 2.8 Do features in this specification enable access to device sensors? - -No. - -## 2.9 Do features in this specification enable new script execution/loading mechanisms? - -No. - -## 2.10 Do features in this specification allow an origin to access other devices? - -No. - -## 2.11 Do features in this specification allow an origin some measure of control over a user agent’s native UI? - -No. - -## 2.12 What temporary identifiers do the features in this specification create or expose to the web? - -None. - -## 2.13 How does this specification distinguish between behavior in first-party and third-party contexts? - -No distinction; no special considerations for use of the feature by third-party resources. - -## 2.14 How do the features in this specification work in the context of a browser’s Private Browsing or Incognito mode? - -No change to behavior in Private Browsing or Incognito modes. -No features of the specification that would allow for correlation of a single user's activity across normal and private browsing / incognito modes. - -## 2.15 Does this specification have both "Security Considerations" and "Privacy Considerations" sections? - -Yes. - -## 2.16 Do features in your specification enable origins to downgrade default security protections? - -No. - -## 2.17 How does your feature handle non-"fully active" documents? - -Gap decorations are purely a painted effect and should be handled no differently from existing painted effects such as backgrounds and borders. - -## 2.18 What should this questionnaire have asked? - -No special considerations for this feature. diff --git a/CSSModules/v1Explainer.md b/CSSModules/v1Explainer.md deleted file mode 100644 index 8fa6eef3a..000000000 --- a/CSSModules/v1Explainer.md +++ /dev/null @@ -1,162 +0,0 @@ -# CSS Modules V1 Explainer - -Author: [@dandclark](https://github.com/dandclark) - -## Status of this Document -This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. -* This document status: **ARCHIVED** -* Current venue: [Web Components Incubator of the W3C Web Applications Working Group and WHATWG HTML and DOM Workstreams](https://github.com/w3c/webcomponents/) | [w3c/webcomponents](https://github.com/w3c/webcomponents/) | ![GitHub issues by-label](https://img.shields.io/github/issues/w3c/webcomponents/modules) -* Current version: [CSS Modules V1 Explainer](https://github.com/w3c/webcomponents/blob/gh-pages/proposals/css-modules-v1-explainer.md) - -## Introduction - -This document proposes an extension of the ES Script Modules system to include CSS Modules. These will allow web developers to load CSS into a component definition in a manner that interacts seamlessly with other module types. - -## Why are CSS Modules needed? - -The introduction of ES6 JavaScript Modules has provided several benefits for web developers including more componentized code and better dependency management. However, solutions for including CSS in component definitions are lacking. Current practices all have one or more of the following rough edges: - -* Side effects like appending `<style>` elements to the document. If this is done in the top-level scope of the document then it breaks shadow root style scoping. If it is done inside a shadow root then each individual instance of the component must include its own `<style>` element in its shadow root instance. -* Inlined CSS text as a string in JavaScript. This is not optimally performant (it's processed by both the JS and CSS parsers) and is a poor developer experience. -* Dynamically `fetch()`ing CSS is generally not statically analyzable and requires careful dependency management by the developer for complex applications. - -CSS modules solves these issues by extending the ES modules infrastructure to allow importing a CSSStyleSheet object from a CSS file, which can then be added to the document or a shadowRoot via the [adoptedStyleSheets](https://wicg.github.io/construct-stylesheets/#using-constructed-stylesheets) array. - -There is demand for this functionality in the developer community -- see [this thread](https://github.com/w3c/webcomponents/issues/759) where there a number of developers have expressed interest. The popularity of CSS loaders in JS bundlers is also indicative of demand for this functionality. - -## Importing a CSS Module -CSS modules will be imported using the same `import` statements currently used for other ES modules: - -```JavaScript -import styles from "styles.css"; -document.adoptedStyleSheets = [...document.adoptedStyleSheets, styles]; -``` - -The default export of the module is the CSSStyleSheet generated from the CSS file. A CSS module has no named exports. - -## Some implementation details - -The MIME-type in the HTTP response header is checked to determine how a given module should be interpreted. A MIME-type of `text/css` will be treated as a CSS module. Each imported CSS Module will have its own [module record](https://tc39.github.io/ecma262/#sec-abstract-module-records) as introduced in the ES6 spec and will participate in the module map and module dependency graphs. - -The V1 of CSS Modules will be built using Synthetic Modules. Specifically, to create a new CSS module given a fetched `text/css` file: -1. Create a CSSStyleSheet() via the [constructor](https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-cssstylesheet). -1. Call [CSSStyleSheet.replaceSync](https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replacesync) on the new sheet with the contents of the file as the argument (see [here](#why-is-cssstylesheetreplacesync-used-instead-of-cssstylesheetreplace) for discussion of why this is `replaceSync` rather than `replace`). An error thrown from this call causes the module creation to fail with a parse error. -1. Create a new Synthetic module via [CreateSyntheticModule](https://heycam.github.io/webidl/#createsyntheticmodule) with `"default"` as the sole entry of `exportNames` and with `evaluationSteps` that calls `SetMutableBinding("default", sheet)` where `sheet` is the CSSStyleSheet created in step 1. -1. Create a new CSS module script with the Synthetic module created in step 3 as its record. - -## Why is [CSSStyleSheet.replaceSync](https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replacesync) used instead of [CSSStyleSheet.replace](https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replace)? - -This proposal describes a limited V1 of CSS modules that do not support `@import`s. The reason for this is that it's not clear whether an `@import` in a CSS module should be treated as its own CSS module in the module graph, or whether CSS modules should be leaf modules. There are 3 possibilities under consideration: - -1. CSS Modules are leaf modules, and don't allow `@import` references (following the example of [replaceSync](https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replacesync) in constructable stylesheets). This is the V1 implementation described in this document, and this is why step 2 of of CSS module creation as described [here](#some-implementation-details) uses [replaceSync](https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replacesync) instead of [replace](https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replace); [replaceSync](https://wicg.github.io/construct-stylesheets/#dom-cssstylesheet-replacesync) throws if given input containing `@import` rules. -1. CSS modules are leaf modules; prior to creating the module record for a CSS module, load the full `@import` tree of its stylesheet and if any fail to resolve, treat it as a parse error for the module. -1. CSS Modules are non-leaf (cyclic) modules. Process a CSS Module's `@import`ed stylesheets as its requested module children in the module graph, with their own module records. They will Instantiate and Evaluate as distinct modules. - -Option 1 seems needlessly restrictive in the long-term. - -One of the main differences between options 2 and 3 is that 3 implies that if a CSS file is `@import`ed multiple times for a given realm, each import would share a single CSSStyleSheet between them (because a module is only instantiated/evaluated once for a given module specifier). There are potential memory/performance gains to be found here in cases where a developer includes a stylesheet multiple times by mistake or because of shared CSS dependencies. On the other hand, this is a divergence from the existing behavior where multiple @imports of the same .css file each come with their own CSSStyleSheet. - -@justinfagnani pointed out [here](https://github.com/w3c/webcomponents/issues/759#issuecomment-490670571), [here](https://github.com/w3c/webcomponents/issues/759#issuecomment-490692275), and [here](https://github.com/w3c/webcomponents/issues/759#issuecomment-491474158) that the sharing of `@import`ed stylesheets in option 3 could enable scenarios where a tool editing CSS or a theming system could dynamically change the shared sheet and have the changes applied in all the different importers of the sheet. - -However, as @tabatkins discussed [here](https://github.com/w3c/webcomponents/issues/759#issuecomment-490685490), option 3 is a significant departure from the current `@import` behavior in a way that can't be reproduced dynamically: the CSS object model can't be used to make multiple sheets depend on a single child stylesheet. The `.parentStyleSheet` and `.ownerRule` references also pose a problem here as these currently reference only a single sheet and become ambiguous if a sheet were to have multiple importers. - -The discussion following [this comment](https://github.com/w3c/webcomponents/issues/759#issuecomment-490256626) contains more extensive discussion of this issue. Given the lack of consensus on far, we're moving forward with a V1 that sidesteps the question by going with option 1. This is forward-compatible given that any use of `@import` in a CSS module will prevent the module from loading. Making progress on this now rather than waiting for a final option 2 vs 3 decision is advantageous given that we can get earlier developer feedback on the feature and a better perspective of how it is used in practice, both of which may help us in making these design decisions. Additionally, completing a V1 of CSS modules unblocks progress on [HTML modules](../HTMLModules/designDoc.md). - -## Example: Custom Element Definition using CSS Modules - -The following is an example of how a custom element might be defined today, where CSS is included inline as a JavaScript string: - -```JavaScript -<!doctype html> -<html> - <head> - <script type="module"> - class HTML5Element extends HTMLElement { - constructor() { - super(); - let shadowRoot = this.attachShadow({ mode: "open" }); - - let style = document.createElement("style"); - style.innerText = ` - .outerDiv { - border:0.1em solid blue; - display:inline-block; - padding: 0.4em; - } - - .devText { - font-weight: bold; - font-size: 1.2em; - text-align: center; - margin-top: 0.3em; - } - - .mainImage { - height:254px; - } - `; - - let outerDiv = document.createElement("div"); - outerDiv.className = "outerDiv"; - let mainImage = document.createElement("img"); - mainImage.className = "mainImage"; - mainImage.src = "https://www.w3.org/html/logo/downloads/HTML5_Logo_512.png"; - let devText = document.createElement("div"); - devText.className = "devText"; - devText.innerText = "CSS Modules Are Great!"; - - this.shadowRoot.appendChild(outerDiv); - outerDiv.appendChild(mainImage); - outerDiv.appendChild(devText); - this.shadowRoot.appendChild(style); - } - } - - window.customElements.define("my-html5-element", HTML5Element); - </script> - </head> - <body> - <my-html5-element></my-html5-element> - </body> -</html> -``` - -The following example shows shows how the same custom element definition could incorporate a CSS module to avoid CSS-as-a-JS-string (or inserting a `<style>` tag, etc.): - -```JavaScript -<!doctype html> -<html> - <head> - <script type="module"> - import styles from './html5Element.css'; - - class HTML5Element extends HTMLElement { - constructor() { - super(); - let shadowRoot = this.attachShadow({ mode: "closed" }); - - this.shadowRoot.adoptedStyleSheets = [styles]; - - let outerDiv = document.createElement("div"); - outerDiv.className = "outerDiv"; - let mainImage = document.createElement("img"); - mainImage.className = "mainImage"; - mainImage.src = "https://www.w3.org/html/logo/downloads/HTML5_Logo_512.png"; - let devText = document.createElement("div"); - devText.className = "devText"; - devText.innerText = "CSS Modules Are Great!"; - - shadowRoot.appendChild(outerDiv); - outerDiv.appendChild(mainImage); - outerDiv.appendChild(devText); - } - } - - window.customElements.define("my-html5-element", HTML5Element); - </script> - </head> - <body> - <my-html5-element></my-html5-element> - </body> -</html> -``` diff --git a/CSSTooltipPseudo/explainer.md b/CSSTooltipPseudo/explainer.md deleted file mode 100644 index a15343536..000000000 --- a/CSSTooltipPseudo/explainer.md +++ /dev/null @@ -1,956 +0,0 @@ -# CSS ::tooltip Pseudo Element - -## Authors - -- [Alison Maher](https://github.com/alisonmaher) -- [Lea Verou](https://github.com/LeaVerou) (author of the [original -proposal](https://github.com/w3c/csswg-drafts/issues/8930)) - -## Participate - -- [Issue tracker](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/TooltipPseudo) -- [CSSWG Github Proposal](https://github.com/w3c/csswg-drafts/issues/8930) - -## Status of this Document - -This document is intended as a starting point for engaging the community and -standards bodies in developing collaborative solutions fit for standardization. -As the solutions to problems described in this document progress along the -standards-track, we will retain this document as an archive and use this section -to keep the community up-to-date with the most current standards venue and -content location of future work and discussions. - -* This document status: **Active** -* Expected venue: [CSS Working Group](https://www.w3.org/Style/CSS/) -* Current version: this document - -## Table of Contents - -<!-- START doctoc generated TOC please keep comment here to allow auto update --> -<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --> -**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* - -- [Introduction](#introduction) -- [Goals](#goals) -- [Non-goals](#non-goals) -- [User research](#user-research) -- [Existing and upcoming tools available for custom tooltips](#existing-and-upcoming-tools-available-for-custom-tooltips) - - [Use an existing library or component](#use-an-existing-library-or-component) - - [Create a fully custom tooltip](#create-a-fully-custom-tooltip) - - [Utilize `popover`, CSS anchor positioning, and/or `interestfor`](#utilize-popover-css-anchor-positioning-andor-interestfor) -- [Current landscape of built-in tooltips](#current-landscape-of-built-in-tooltips) - - [Simple tooltips across browsers](#simple-tooltips-across-browsers) - - [Chromium](#chromium) - - [Gecko](#gecko) - - [Webkit](#webkit) - - [Longer tooltips across browsers](#longer-tooltips-across-browsers) - - [Chromium](#chromium-1) - - [Gecko](#gecko-1) - - [Webkit](#webkit-1) -- [The `::tooltip` pseudo element](#the-tooltip-pseudo-element) - - [How to trigger `::tooltip` default styles?](#how-to-trigger-tooltip-default-styles) - - [How to set the `::tooltip` content?](#how-to-set-the-tooltip-content) - - [Positioning for `::tooltip`](#positioning-for-tooltip) - - [`::tooltip` sizing](#tooltip-sizing) - - [Default styles for `::tooltip`](#default-styles-for-tooltip) - - [What styles can be applied to `::tooltip`?](#what-styles-can-be-applied-to-tooltip) - - [`::tooltip` style inheritance](#tooltip-style-inheritance) - - [What user interaction triggers the built-in tooltip?](#what-user-interaction-triggers-the-built-in-tooltip) - - [Dependencies on non-stable features](#dependencies-on-non-stable-features) -- [Key scenarios](#key-scenarios) - - [Scenario 1: Opt-into the new base `::tooltip` styles](#scenario-1-opt-into-the-new-base-tooltip-styles) - - [Scenario 2: Adjusting the look and feel of built-in tooltips](#scenario-2-adjusting-the-look-and-feel-of-built-in-tooltips) - - [Scenario 3: Adjusting tooltip timing](#scenario-3-adjusting-tooltip-timing) - - [Scenario 4: Setting the name using `title` without rendering a tooltip](#scenario-4-setting-the-name-using-title-without-rendering-a-tooltip) - - [Scenario 5: Providing custom positioning to a tooltip](#scenario-5-providing-custom-positioning-to-a-tooltip) -- [Accessibility Considerations](#accessibility-considerations) - - [What is the current accessibility experience of tooltips across browsers?](#what-is-the-current-accessibility-experience-of-tooltips-across-browsers) - - [Proposed improvements](#proposed-improvements) - - [Zoom](#zoom) -- [Security Considerations](#security-considerations) -- [Privacy Considerations](#privacy-considerations) -- [Open questions](#open-questions) -- [Future ideas](#future-ideas) - - [Tooltip pointer/arrow](#tooltip-pointerarrow) - - [Customizing `::tooltip` user interactions](#customizing-tooltip-user-interactions) - - [CSS Anchor Positioning tooltip defaults](#css-anchor-positioning-tooltip-defaults) -- [Considered alternatives](#considered-alternatives) - - [Alternative 1: The `interestfor` attribute](#alternative-1-the-interestfor-attribute) -- [References & acknowledgements](#references--acknowledgements) - -<!-- END doctoc generated TOC please keep comment here to allow auto update --> - -## Introduction - -Tooltips are used by authors to provide additional contextul information to users -for a given interactive element on the screen. Tooltips are often presented as -a textbox that appears on hover (or potentially some other user interaction, like -keyboard focus, or long-press on a touchscreen device) on the element the -tooltip is associated with. - -Authors can utilize the browser's built-in tooltips by setting the -[`title`](https://html.spec.whatwg.org/multipage/dom.html#attr-title) -attribute on an element of interest, or via the [`<title>`]( -https://html.spec.whatwg.org/multipage/semantics.html#the-title-element) -element in SVG. However, the tooltip that appears as a result is not -customizable in any way by the author. - -This lack of customizability has been a source of common frustration for authors -who want to keep a consistent look and feel across all of their UI. - -This document explores a new `::tooltip` pseudo element that aims to provide authors -with a low effort mechanism to adjust styles for browser built-in tooltips without -having to create a fully custom solution, leaving positioning, input handling, and -accessibility for their tooltips up to the browser. - -## Goals - -* Provide a low effort way for authors to style core properties on browser -built-in tooltips (for example, color properties, font properties, etc.) -* This mechanism should handle accessibility, positioning, and input handling for -the author by default. -* Allow authors to override the default positioning behavior of the built-in -tooltips. -* Allow authors to change the timing of built-in tooltips. -* Improve accessibility of the `title` attribute, particularly for keyboard users. -* Guarantee security best practices, given that tooltips today can extend past -the bounds of the browser window. -* Styling built-in tooltips should produce consistent results across all browsers -on the same OS, ensuring an interoperable experience for authors. - -## Non-goals - -* This proposal is not meant to support tooltips with arbitrary HTML content, which -can be covered by existing solutions, like `popover`, CSS Anchor positioning, and -the not-yet-available `interestfor` attribute. -* Provide a means to set a [pointer](#tooltip-pointerarrow) on the `title` based -tooltip. The solution should be specified to allow for this functionality in -the future, though. - -## User research - -Use cases in this explainer were collected from the discussion in issue -[8930](https://github.com/w3c/csswg-drafts/issues/8930). Additional inspiration -was drawn from discussions in issues -[9447](https://github.com/w3c/csswg-drafts/issues/9447), and OpenUI issue -[730](https://github.com/openui/open-ui/issues/730). - -There are also many articles on the web that walk authors through various -options for creating custom tooltips using tools available to them today. -One such [article](https://blog.replaybird.com/css-tooltip-examples/) -walks through different options, along with various examples and use cases -for tooltips on the web. - -## Existing and upcoming tools available for custom tooltips - -If an author would like to adjust the style of their tooltip today, they must resort -to one of the following alternative approaches to the built-in tooltip that they -get with the `title` attribute. - -### Use an existing library or component - -Authors can use an existing library or component for a fully customizable tooltip. -Some example libraries include: - - https://fluent2.microsoft.design/components/web/react/core/tooltip/usage - - https://www.opentip.org/ - - https://floating-ui.com/ - - https://www.npmjs.com/search?ranking=popularity&q=tooltip - - Many more - -It is clear that there are many libraries and components for creating custom tooltips -that authors can use to solve their need for more customization. - -If the library chosen is well architected to properly handle accessibility, this can -also be a convenient way to get such functionality "for free". However, relying on -external library to get accessibility right can be risky, which may limit the usability -if not done correctly. - -The other downside of this approach is that it requires heavy use of scripting, -which can be slower, and shouldn't be necessary if the author simply wants to -adjust some basic styles. - -### Create a fully custom tooltip - -Authors can also choose to create their own custom tooltip or component, which -requires their own input management, proper accessibility support and proper -handling of the tooltip positioning, which can be complex. - -This does allow authors to achieve their goal of full customizability, but it -often requires heavy use of scripting, or use of `::before` or `::after` -pseudos, which can make getting accessibility correct quite difficult. - -As such, this solution requires a lot of work to get right, especially if all they -want to do is adjust a few simple styles on the built-in tooltip. - -#### Utilize `popover`, CSS anchor positioning, and/or `interestfor` - -Although creating a custom tooltip can be cumbersome for an author who is looking -to just adjust a few simple styles, they do have some new (and upcoming) web -APIs, that can help them in accomplishing a fully customizable tooltip more -easily: - - The [`popover`]( - https://html.spec.whatwg.org/multipage/popover.html#dom-popover) attribute, - which is [baseline]( - https://developer.mozilla.org/en-US/docs/Glossary/Baseline/Compatibility) - across browsers as of April 2024. - - [CSS Anchor Positioning](https://drafts.csswg.org/css-anchor-position-1/), - which is not yet baseline, but is part of the [Interop 2025 effort]( - https://github.com/web-platform-tests/interop/blob/main/2025/README.md#css-anchor-positioning). - - The [`interestfor`]( - https://open-ui.org/components/interest-invokers.explainer/#keyboard) attribute, - which is currently being investigated, with a prototype already in working - shape in Chromium. - -One benefit that authors get with `interestfor` over the other existing solutions -is that it creates a more accessible experience out-of-the-box and handles input -handling for the author, creating a more seamless approach than is available with -the `popover`attribute on its own. It is also a great solution if an author wants -custom HTML within their tooltip, as opposed to simple textual content. - -However, `interestfor` does have some limitations. Currently, `interestfor` -is only allowed on buttons and links, which is more limited than the `title` -attribute. -_____________________________ - -All of these options do allow authors to create more custom tooltip experiences -than what is currently available through the browser's built-in option using the -`title` attribute, but these options often require more work than should be -necessary for a simple style adjustment. - -## Current landscape of built-in tooltips - -The rendering of tooltips via the `title` attribute is up to each UA. Below -is a brief survey of how some of the top browser engines render built-in -tooltips today, in no particular order. - -### Simple tooltips across browsers - -In this section is a comparison of basic tooltip rendering across each -browser engine using the code example below. - -```html -<button title="This is a button!">Button Example</button> -``` - -#### Chromium - -In Chromium, the tooltip is rendered above all web content, and the position -of the tooltip is dependent on the position of the mouse and the element it -is anchored to. - -The style of the tooltip that is rendered is very simple: a white or black -background with black or white text and a matching border. - -On Windows the simple test case above renders as follows: - -![Screenshot of tooltip in Chromium on Windows]( -images/simple-button-chromium-windows.png) - -On Linux, the style of the tooltip defaults to a darker color scheme: - -![Screenshot of tooltip in Chromium on Linux]( -images/simple-button-chromium-linux.png) - -On MacOS, Chromium leaves the rendering up to the OS itself, producing the -following result: - -![Screenshot of tooltip in Chromium on Mac]( -images/simple-button-chromium-mac.png) - -On mobile, tooltips are not activatable in Chromium, with the -exception of tooltips on an image, which do appear in the context -menu upon long press on Android. - -#### Gecko - -In Gecko, the tooltip is similarly rendered above all web content, and the -position of the tooltip is dependent on the position of the mouse and the -element it is anchored to. - -The style of the tooltip that is rendered is arguably more modern in -style than Chromium and has more of an offset from the anchored element. - -On Windows the simple test case above renders as follows: - -![Screenshot of tooltip in Gecko on Windows]( -images/simple-button-firefox-windows.png) - -On Linux, the style of the tooltip defaults to a darker color scheme, -with no border, and slightly more padding: - -![Screenshot of tooltip in Gecko on Linux]( -images/simple-button-firefox-linux.png) - -On MacOS, Gecko matches the OS tooltip styles, producing the following -result: - -![Screenshot of tooltip in Gecko on Mac]( -images/simple-button-firefox-mac.png) - -On mobile, tooltips are not activatable in Gecko, with the -exception of tooltips on an image, which do appear in the context -menu upon long press. - -#### Webkit - -In Webkit, the tooltip is similarly rendered above all web content, and the -position of the tooltip is dependent on the position of the mouse and the -element it is anchored to. - -The style of the tooltip that is rendered is in accordance with the platform, -similarly to Chromium and Firefox on MacOS. - -![Screenshot of tooltip in Webkit on Mac]( -images/simple-button-webkit-mac.png) - -On mobile, tooltips are not activatable in Webkit, with the -exception of tooltips on an image, which do appear in the context -menu upon long press. - -### Longer tooltips across browsers - -Below is an overview of how each major browser engine handles longer -tooltip text, in no particular order. - -#### Chromium - -In Chromium, built-in tooltips add ellipses to the tooltip text if the -text is greater than or equal to 1025 characters. The tooltip is also -allowed to escape the bounds of the window. For example: - -![Screenshot of a long tooltip with an ellipses in Chromium on Windows]( -images/out-of-bounds-chromium-windows.png) - -#### Gecko - -In Gecko, I was *not* able to find a limit for built-in tooltip length for -which an ellipses was added, like it does in Chromium. However, tooltips -are also able to escape the bounds of the window in Gecko, but the tooltip -width stays more constrained than in Chromium. For example: - -![Screenshot of a long tooltip in Gecko on Windows]( -images/out-of-bounds-firefox-windows.png) - - -#### Webkit - -In Webkit, I was *not* able to find a limit for built-in tooltip length for -which an ellipses was added, like it does in Chromium. However, tooltips -are also able to escape the bounds of the window in Webkit. In this case, -the tooltip appears to have uneven padding. For example: - -![Screenshot of a long tooltip in Webkit on Mac]( -images/out-of-bounds-webkit-mac.png) - -_________________________ - -It is clear that the tooltips provided by the top browser engines are -inconsistent in styling between each other and across OS, and there are -divergences in positioning and some behaviors with longer tooltip text. - -This inconsistency and lack of customizability leads authors to find -alternatives that often require more work and maintenance. - -## The `::tooltip` pseudo element - -Many have proposed a pseudo for styling browser tooltips over the years, -with the [first proposal]( -https://lists.w3.org/Archives/Public/www-style/2000Apr/0014.html) -originating around April 2000, and the most [recent proposal]( -https://github.com/w3c/csswg-drafts/issues/8930) -from Lea Verou in 2023. - -The proposal in this document takes close inspiration from these, by -introducing a new pseudo element, called `::tooltip`, that can be -used by authors to style the browser tooltip that is displayed when -the `title` attribute is set on an HTML element. - -The `::tooltip` pseudo element will have default UA styles that will -be required to be applied when an author adjusts tooltip styles -within a `::tooltip` pseudo element selector. - -The idea behind this proposal is that authors would be able to -create simple text-based tooltips, and adjust the styles of color -properties, shadowing, border properties, font properties, etc -to better match the look and feel of their site, leaving -positioning, input handling, and accessibility up to the browser. - -### How to trigger `::tooltip` default styles? - -Currently, browser default tooltips are triggered by the `title` -attribute being set, or by the presence of the `<title>` element for -SVG elements. - -We could have the default `::tooltip` apply in all cases by default, -but this may be a breaking change for some browsers. Instead, the UA -should be able to style tooltips in their own chosen way, unless -`::tooltip` is triggered in some way. - -Authors may also want to trigger this path without triggering any -styles beyond those that are default. As such, there requires -some mechanism for authors to trigger the new `::tooltip`-based -default. - -An initial suggestion would be to utilize [`appearance: base`]( -https://www.w3.org/TR/css-forms-1/#valdef-appearance-base), similar -to what was done for [customizable select elements]( -https://developer.mozilla.org/en-US/docs/Learn_web_development/Extensions/Forms/Customizable_select), that an author can use within the `::tooltip` -pseudo element to trigger the new base default styles and -customizability. - -For example, in order to set the `color` of your tooltip to `red`, -one could apply the following styles: - -```css -::tooltip { - appearance: base; - color: red; -} -``` - -The downside of this is that it requires explicit opt-in, rather than -only needing to set the `color` to `red`, but would allow an author to -trigger the new base tooltip styles without having to set any styles. -This triggering mechanism remains an open question and is welcome -to additional suggestions and input from the wider community. - -### How to set the `::tooltip` content? - -There has been debate in the [most recent proposal]( -https://lists.w3.org/Archives/Public/www-style/2000Apr/0014.html) -for `::tooltip` for how best to set the content of the `::tooltip` -pseudo element. - -The content itself will be pulled from the `title` attribute, or in -the case of SVG, the `<title>` element. The initial proposal is to -utilize the `content` property for applying the content to the -`::tooltip` pseudo element itself. - -The initial suggestion by Lea Verou in the CSSWG proposal was to -set this via the default stylesheet in the following manner: - -```css -::tooltip { - content: attr(title); - ... -} -``` - -There were some concerns that this wouldn't work well for SVGs, -since SVGs use `<title>` instead of the `title` attribute for -built-in tooltips. [@bleper](https://github.com/bleper) put -together a [potential proposal]( -https://github.com/w3c/csswg-drafts/issues/8930#issuecomment-1581848840) -for how to address this issue. - -However, a simpler approach may be to define what `normal` means -uniquely for `::tooltip` such that `normal` is equivalent to -`content: attr(title)` in all cases, but SVG, where in SVG, -`normal` is equivalent to the content of the `<title>` element. - -This item remains an open question and is welcome to input from -the wider community. - -### Positioning for `::tooltip` - -There has been discussion in the CSSWG around whether the positioning -of `::tooltip` should be UA magic, or if the author should be able to -customize this. The main concern being around security, given that -tooltips can escape the bounds of the browser window today (see -[Security Considerations](#security-considerations) for more details). - -Given the proposal to not allow tooltips styled through the -`::tooltip` psuedo element to escape the bounds of the windows (see -[Security Considerations](#security-considerations) for more details), -this proposal also includes the ability for authors to customize the -tooltip positioning. - -As such, this proposal will utilize [CSS Anchor Positioning]( -https://drafts.csswg.org/css-anchor-position-1/) to define the default -position of the `::tooltip` pseudo element, which will allow authors -to override this in the way that they see fit. - -As a result, this means that the element associated with the `title` -attribute (or `<title>` element in the case of SVG) will be an implied -anchor for the rendered tooltip. - -### `::tooltip` sizing - -This proposal sizes the default `::tooltip`-based tooltip according to -default sizing definitions (where the size of the tooltip is based on -the size of its content.) This also means that the tooltip will be -constrained by the viewport in the inline direction, but may overflow -the viewport in the block direction, if too large, as depicted in -the image, below. - -![Screenshot of a tooltip with base tooltip pseudo styling overflowing -the viewport in the block direction](images/tooltip-overflow.png) - -We could instead consider whether the default tooltip in this case should -have a non-default sizing definition. For example, the width of the -tooltip could be constrained to be no larger than some percentage of -the viewport width using the `max-width` property. - -This, and whether authors should be allowed to adjust the sizing -properties within a `::tooltip` pseudo element, remain open -questions for further discussion. - -### Default styles for `::tooltip` - -When the new default `::tooltip` styles are triggered via the -`appearance` property, a set of default styles would be applied -by the UA. - -These styles were mostly pulled directly from Lea Verou's initial -[proposal]( -https://github.com/w3c/csswg-drafts/issues/8930) with a couple of -minor edits based on the new proposal for an updated definition of -`content: normal` within the `::tooltip` pseudo element, as well -as the proposal to define the position via CSS Anchor Positioning -(which was included as an option in Lea's original proposal, as -well). - -```css -/* UA styles */ -::tooltip { - /* content: normal; is implied */ - color: InfoText; - background: InfoBackground; - font-size: .8rem; - box-shadow: .1rem .1rem .2rem rgb(0 0 0 / .2); - position: absolute; - position-area: top right; - position-try-fallbacks: top left, top center, bottom right, bottom left, bottom center, center right, center left; - transition: 0s 3s visibility; - - @starting-style { - visibility: hidden; - } -} -``` - -These proposed set of initial UA styles is a starting point and is -open to further discussion. - -To see some of these styles in action, check out the [CodePen demo]( -https://codepen.io/alisonmaher/pen/MYYxpJZ) utilizing `interestfor` -to render the tooltip. You'll note that this requires some changes -to the styles above (notably, this doesn't include the defined -`transition` and sets `margin` to `0` to account for the default -`popover` styles). Since this is based on `interestfor`, at the -time of writing, this demo is required to be tested in a Chromium-based -browser with [Web Platform Experimental features flag]( -chrome://flags/#enable-experimental-web-platform-features) enabled. - -### What styles can be applied to `::tooltip`? - -Not all styles should or need to be applied within the context -of a simple text-based tooltip. For example, we likely don't -want tooltips with images, lists, scrollbars, or non-default -`display` types or `z-index`. As such, there should be a limited list -of styles that are valid within a `::tooltip` pseudo element; that -list can be expanded on in the future. - -There is an open [CSSWG issue]( -https://github.com/w3c/csswg-drafts/issues/9447) to discuss what -properties should be styleable within a `::tooltip` pseudo element. -As such, the complete list of properties is up for discussion, but -as the proposal notes, the core set of properties should include: -- `content` for setting the text displayed in the tooltip (whether -authors should be allowed to change this is up for debate, as it -may have some accessibility implications if the tooltip and `title` -have different text) -- `appearance` as a means to trigger new default tooltip styles -- All font properties, `text-transform`, `letter-spacing`, -`word-spacing`, and the i18n text properties -- `background`, `color`, `box-shadow`, `text-shadow`, -`border-radius`, `border`, `padding`, `opacity` (including their -longhands) -- `color-scheme` and `forced-color-adjust` (these two were not -included in the initial [CSSWG proposal]( -https://github.com/w3c/csswg-drafts/issues/9447), but would likely -be helpful for authors to guarantee consistent theming capabilites -with the rest of their page.) -- Transitions -- `transform`, `transform-origin`, `rotate`, `scale`, `translate` -- Custom properties -- Given that the proposal suggests using anchor positioning for -default styling of the position of `::tooltip`, we should also -include [CSS Anchor Positioning]( -https://drafts.csswg.org/css-anchor-position-1/) properties -for author customization - -The list above should handle the majority of styling use cases -for authors, however the [current proposal]( -https://github.com/w3c/csswg-drafts/issues/9447) also notes that -the following properties would be nice to have, as well: -- `clip-path` -- Filters and `backdrop-filter` -- `animation` -- `mix-blend-mode` -- `border-image` -- `outline` -- `text-decoration` -- `fill` and `stroke` - -As noted, the first list of valid properties is still up for -debate, so any thoughts or input are welcome as we continue to -explore the [open issue]( -https://github.com/w3c/csswg-drafts/issues/9447). - -### `::tooltip` style inheritance - -To allow `::tooltip` to inherit styles from the rest of the page, -it should be grouped within the list of [Tree-Abiding -Pseudo-elements](https://drafts.csswg.org/css-pseudo/#treelike). - -This would allow `::tooltip` to inherit properties, like `font-family`, -from the rest of the page, making it easier for authors to create -a consistent look and feel across all their UI. - -### What user interaction triggers the built-in tooltip? - -Currently, browsers show `title`-based tooltips when a user hovers -over the element associated with the tooltip. - -No major browser engine appears to currently support touch interactions -for these built-in tooltips, except for tooltips on images. However, this should -be something for browser vendors to consider along with support for -`::tooltip`, similar to what is [proposed with `interestfor`]( -https://open-ui.org/components/interest-invokers.explainer/#hids-and-interest). - -All browsers, except Microsoft Edge, also do not invoke the `title`-based -tooltips on keyboard focus. See [Accessibility Considerations]( -#accessibility-considerations) for more details. - -Authors may also want to be able to customize the interaction(s) that -trigger built-in tooltips using the `::tooltip` pseudo element. This -functionality is being considered as a [future addition](#future-ideas) -to this proposal. - -### Dependencies on non-stable features - -This proposal makes use of [CSS Anchor Positioning]( -https://drafts.csswg.org/css-anchor-position-1/), to define the default -positioning of the base `::tooltip` styles. This feature is not yet -baseline, but it is part of [Interop 2025]( -https://github.com/web-platform-tests/interop/blob/main/2025/README.md#css-anchor-positioning). - -There is also a thought on whether `::tooltip` should be defined to be -based on `interestfor`, given there is an overlap between the two -features. However, this is something that should likely be left up to the -UA on the technology involved in rendering their tooltips, as long as -the base set of styles and specified interactions are met. - -## Key scenarios - -### Scenario 1: Opt-into the new base `::tooltip` styles - -In its most base form, an author could opt-into the new `::tooltip` base -styles without adjusting any of its styles in order to get a consistent basic -styling for tooltips across browsers. An author could accomplish this with -the following HTML and CSS. - -```html -<style> - ::tooltip { - appearance: base; - } -</style> - -<button title="This is a button!"> - The tooltip shown will follow the UA base styles across all browsers. -</button> -``` - -### Scenario 2: Adjusting the look and feel of built-in tooltips - -If an author doesn't like the base `::tooltip` style, they can adjust the -styles as follows: - -```html -<style> - ::tooltip { - appearance: base; - background: orange; - color: blue; - } -</style> - -<button title="This is a button!"> - The tooltip shown will have a blue background and orange text. -</button> -``` - -### Scenario 3: Adjusting tooltip timing - -An author may not like the timing of the built-in tooltip popup. An author -can change this by adjusting the tooltip `transition`, as demonstrated in -the example below: - -```html -<style> - ::tooltip { - appearance: base; - transition: 0s 1s visibility; - } -</style> - -<button title="This is a button!"> - The tooltip shown will be shown more quickly than the default UA - provided styles. -</button> -``` -One may find it is strange that an author could adjust the `transition` -of the `visibility` property, without being able to update the `visibility` -property itself. This is because the UA owns the user interaction, and as such, -the UA owns the resulting visibility of the tooltip. - -Disallowing author control of `visibility` itself allows UAs to lazily calculate -tooltip styles under the `::tooltip` pseudo element until it is determined by -the UA to display the tooltip itself. If an author were able to adjust -`visibility`, we would need to calculate `::tooltip` styles for all elements -to determine if the tooltip would be shown or not, which would be less -performant. - -Although we don't allow author adjustment of `visibility`, authors may still -find it useful to adjust the timing the tooltip is shown, which we are proposing -can be accomplished via adjustment to the `transition` defined in the UA default -styles for `::tooltip`. - -### Scenario 4: Setting the name using `title` without rendering a tooltip - -There is no existing way to set an [accessible description]( -https://developer.mozilla.org/en-US/docs/Glossary/Accessible_description#accessible_description_computation) for most -elements without using ARIA or without a tooltip being shown (via -the `title` attribute). - -We can consider making this possible through the `::tooltip` proposal -utilizing `content: none` as follows: - -```css -::tooltip { - appearance: base; - content: none; -} -``` - -Whether authors should be able to change the `content` of these tooltips -is still an open question. There may be accessibility implications with -allowing the tooltip text to be different than the `title`. If we decide -that is not allowed, this would require a different mechanism (like -utilizing `visibility`) if we want to support this use case for authors. - -### Scenario 5: Providing custom positioning to a tooltip - -Given that this proposal uses CSS Anchor Positioning to define the base -positions of the `::tooltip` pseudo element, this would open up an -opportunity for authors to adjust these styles, similar to the example -below: - -```css -::tooltip { - appearance: base; - position-area: start span-all; -} -``` - -## Accessibility Considerations - -According to the [WCAG]( -https://www.w3.org/WAI/WCAG21/Understanding/content-on-hover-or-focus.html), -“Content which can be triggered via pointer hover should also be able to be triggered by keyboard focus.” As such, tooltips should also be keyboard -accessible, and per guidance should also be dismissible. - -### What is the current accessibility experience of tooltips across browsers? - -Unlike [`interestfor`]( -https://open-ui.org/components/interest-invokers.explainer/#why-is-interestfor-not-unlimited-like-title-is), -the `title` attribute is allowed on any element, even if that element -isn't interactable. This can pose problems for accessiblity, because -if the element isn't focusable, it means that the associated tooltip -is not keyboard accessible. - -On top of this, no browser, outside of Microsoft Edge, exposes the -built-in tooltip on focus, even if the associated element is focusable. -This means that browser built-in tooltips are not accessible -out-of-the-box, which is one of the goals of this proposal. - -### Proposed improvements - -To improve the current situation, the `::tooltip` element cannot be -triggered or styled if the element associated with the tooltip is -not focusable. - -As part of this effort, browser vendors should also work on triggering -their built-in tooltips on focus when the element associated with the -tooltip is focusable, and utilize the escape key to trigger tooltip -dismissal. - -One consideration for improving this further is to force existing -elements with an associated `title`-based tooltips to have a -`tabindex` of `0` if it is not already focusable. However, this is -not ideal, as it could lead a container to become focusable that was -not before, leading to undesirable focus changes if a user incorrectly -applies focus just outside of a target element within that container. - -An alternative for browser vendors to consider is including a user -setting that forces a `tabindex` of `0` in such cases, allowing the -user more control over the resulting behavior as they see fit. - -Creating a more accessible tooltip experience for users is one of -the key goals of this proposal, so additional ideas from the -community that should be considered to help in further improving -the current situation are very much welcome. - -### Zoom - -Built-in tooltips today do not adhere to user preferred zoom levels, -making them less accessible than the content they are associated with. - -Making these tooltips stylable through the new `::tooltip` pseudo -element should allow for a more accessible experience in such -scenarios. - -## Security Considerations - -As noted in the section on the [current landscape of built-in -tooltips](#current-landscape-of-built-in-tooltips), browser built-in -tooltips are currently allowed to escpape the bounds of the browser -window. - -For tooltips styled via the `::tooltip` pseudo element, UAs **must** instead -stay within the bounds of the web page. This would avoid [security concerns]( -https://textslashplain.com/2017/01/14/the-line-of-death/) when rendering -outside the bounds of the browser window, which becomes exacerbated when -styleability is involved, because this can more easily trick users into -clicking on a spot on the screen that could lead to a security vulnerability. - -This behavior would be limiting for Iframes in particular, as this would mean -that tooltips in such scenarios would also not be allowed to escape the bounds -of the Iframe window. This, however, would be consistent with the behavior -authors get with custom JS tooltips already, so applying the same restriction -here likely wouldn’t be a major downgrade for authors. - -With styled tooltips being constrained to the browser window bounds, depending -on the UA choice for rendering technology, UAs will need to ensure that the -tooltip remains isolated from the rest of the document and the resulting -layout, as to ensure that styled tooltips do not cause unexpected side -effects on page content, which is guarenteed of tooltips across browsers -today. - -## Privacy Considerations - -There are no known privacy considerations to take into account as part of -this proposal. - -## Open questions - -- [What styles should be allowed within the `::tooltip` pseudo -element?](https://github.com/w3c/csswg-drafts/issues/9447) -- Should `::tooltip` be based on `interestfor` or should that -detail be left up to the UA? -- Should we consider another method for triggering `::tooltip` and its -base styles than the `appearance` property? -- Is `content` the best mechanism for setting the tooltip text from -`title` within the `::tooltip` psuedo element? -- Should authors be allowed to change the `content` of the `::tooltip`? -- Should authors be able to change the positioning of a `::tooltip` or -should this be magic left up to the UA? -- Should authors be able to customize the size of the `::tooltip` through -sizing properties? -- What are the right default UA styles for `::tooltip`? -- Does it make sense for `::tooltip` to be a [Tree-Abiding -Pseudo-element](https://drafts.csswg.org/css-pseudo/#treelike)? -- How should `::tooltip` interact with `interestfor` and any other -`popover` on the same element? -- Are there other ways we improve the current accessibility landscape -of the `title` attribute? -- Should the text within `::tooltip` be selectable? -- How should `::tooltip` be represented in the DOM tree? -- Should we use `visibility` for showing the `::tooltip` transition, -or [`position-visibility`]( -https://drafts.csswg.org/css-anchor-position-1/#position-visibility)? -If `position-visibility`, we may want to consider adding a new value -for "always hidden". -- Should tooltips generate their own events? - -## Future ideas - -### Tooltip pointer/arrow - -Many authors like to add a pointer, nub or arrow with their tooltip, to -create a clearer tie between the tooltip and its associated content, as -illustrated by the below screenshot from [Wikipedia]( -https://en.wikipedia.org/wiki/Tooltip): - -![Screenshot from Wikipedia showcasing a tooltip with a light blue background -and an arrow pointing to the link it is associate with]( -images/tooltip-with-arrow.png) - -A future consideration for `::tooltip` is to add a way for authors to include -a pointer/arrow with their tooltip that is associated with their `::tooltip` -psuedo. - -This addition would require allowing sub-psuedo elements within `::tooltip`. -There would also need to be a way to know the position of the tooltip itself -to position the arrow against, which can be accomplished with CSS Anchor -Positioning, along with the proposed [`::tether` pseudo element]( -https://github.com/w3c/csswg-drafts/issues/9271). - -### Customizing `::tooltip` user interactions - -For now, the UA will control the user interactions that trigger a built-in -tooltip, but authors may wish to change the interaction behavior. Exploring -options to allow more author control of these interactions will be considered -in future versions of the feature. - -### CSS Anchor Positioning tooltip defaults - -If an author would like to produce consistent positioning that is acheived -with `::tooltip` when creating fully custom tooltips via `interestfor` or other -methods, we may want to consider adding new keywords to CSS Anchor Positioning -properties for the default behavior defined for `::tooltip` in the UA stylesheet. - -This would allow authors to more easily create a consistent postitioning behavior -as built-in tooltips. - -## Considered alternatives - -### Alternative 1: The `interestfor` attribute - -`interestfor` is a great solution for authors when creating custom tooltips. -However, if an author is only adjusting a few simple styles, `interestfor` may -be a bit more cumbersome than utilizing the browser built-in tooltips and adjusting -such styles in CSS. - -`interestfor` is also limited to links and buttons, although this restriction -could be expanded in the future. - -## References & acknowledgements - -Many thanks for valuable feedback and advice from: - -- [@bleper](https://github.com/bleper) -- Andy Luhrs -- Benjamin Beaudry -- Daniel Clark -- Dominik Röttsches -- Hoch Hochkeppel -- Kevin Babbitt -- Kurt Catti-Schmidt -- Lea Verou -- Mason Freed -- Scott O'Hara - -Thanks to the following proposals, projects, libraries, frameworks, and languages for their work on similar problems that influenced this proposal. -- A special thank you to Lea Verou for her [detailed proposal of -`::tooltip`]( -https://github.com/w3c/csswg-drafts/issues/8930) -and for reviving the idea in the CSSWG, which was a valuable source of -inspiration for this document. -- Thank you to Tantek Çelik, whose [original proposal]( -https://lists.w3.org/Archives/Public/www-style/2000Apr/0014.html) kicked -off discussion for `::tooltip` back in April of 2000. -- Thank you to Scott O'Hara, who also recently brought a similar proposal to the -[OpenUI](https://github.com/openui/open-ui/issues/730) group. diff --git a/CSSTooltipPseudo/images/out-of-bounds-chromium-windows.png b/CSSTooltipPseudo/images/out-of-bounds-chromium-windows.png deleted file mode 100644 index d8d310a83..000000000 Binary files a/CSSTooltipPseudo/images/out-of-bounds-chromium-windows.png and /dev/null differ diff --git a/CSSTooltipPseudo/images/out-of-bounds-firefox-windows.png b/CSSTooltipPseudo/images/out-of-bounds-firefox-windows.png deleted file mode 100644 index 02ca14d24..000000000 Binary files a/CSSTooltipPseudo/images/out-of-bounds-firefox-windows.png and /dev/null differ diff --git a/CSSTooltipPseudo/images/out-of-bounds-webkit-mac.png b/CSSTooltipPseudo/images/out-of-bounds-webkit-mac.png deleted file mode 100644 index 745adb083..000000000 Binary files a/CSSTooltipPseudo/images/out-of-bounds-webkit-mac.png and /dev/null differ diff --git a/CSSTooltipPseudo/images/simple-button-chromium-linux.png b/CSSTooltipPseudo/images/simple-button-chromium-linux.png deleted file mode 100644 index ff980e72f..000000000 Binary files a/CSSTooltipPseudo/images/simple-button-chromium-linux.png and /dev/null differ diff --git a/CSSTooltipPseudo/images/simple-button-chromium-mac.png b/CSSTooltipPseudo/images/simple-button-chromium-mac.png deleted file mode 100644 index 084e164dd..000000000 Binary files a/CSSTooltipPseudo/images/simple-button-chromium-mac.png and /dev/null differ diff --git a/CSSTooltipPseudo/images/simple-button-chromium-windows.png b/CSSTooltipPseudo/images/simple-button-chromium-windows.png deleted file mode 100644 index a16b26b1c..000000000 Binary files a/CSSTooltipPseudo/images/simple-button-chromium-windows.png and /dev/null differ diff --git a/CSSTooltipPseudo/images/simple-button-firefox-linux.png b/CSSTooltipPseudo/images/simple-button-firefox-linux.png deleted file mode 100644 index b53f2c69d..000000000 Binary files a/CSSTooltipPseudo/images/simple-button-firefox-linux.png and /dev/null differ diff --git a/CSSTooltipPseudo/images/simple-button-firefox-mac.png b/CSSTooltipPseudo/images/simple-button-firefox-mac.png deleted file mode 100644 index a0f5b5c29..000000000 Binary files a/CSSTooltipPseudo/images/simple-button-firefox-mac.png and /dev/null differ diff --git a/CSSTooltipPseudo/images/simple-button-firefox-windows.png b/CSSTooltipPseudo/images/simple-button-firefox-windows.png deleted file mode 100644 index 45527e0a5..000000000 Binary files a/CSSTooltipPseudo/images/simple-button-firefox-windows.png and /dev/null differ diff --git a/CSSTooltipPseudo/images/simple-button-webkit-mac.png b/CSSTooltipPseudo/images/simple-button-webkit-mac.png deleted file mode 100644 index af960a7e3..000000000 Binary files a/CSSTooltipPseudo/images/simple-button-webkit-mac.png and /dev/null differ diff --git a/CSSTooltipPseudo/images/tooltip-overflow.png b/CSSTooltipPseudo/images/tooltip-overflow.png deleted file mode 100644 index 84f345349..000000000 Binary files a/CSSTooltipPseudo/images/tooltip-overflow.png and /dev/null differ diff --git a/CSSTooltipPseudo/images/tooltip-with-arrow.png b/CSSTooltipPseudo/images/tooltip-with-arrow.png deleted file mode 100644 index 9b4cc7b30..000000000 Binary files a/CSSTooltipPseudo/images/tooltip-with-arrow.png and /dev/null differ diff --git a/CSSTooltipPseudo/security-privacy-questionnaire.md b/CSSTooltipPseudo/security-privacy-questionnaire.md deleted file mode 100644 index 2fe1956d9..000000000 --- a/CSSTooltipPseudo/security-privacy-questionnaire.md +++ /dev/null @@ -1,76 +0,0 @@ -# Security and Privacy Questionnaire for CSS Gap Decorations - -https://www.w3.org/TR/security-privacy-questionnaire/#questions - -## 2.1 What information might this feature expose to Web sites or other parties, and for what purposes is that exposure necessary? - -This feature does not expose any new information to Web sites or other parties. - -## 2.2 Do features in your specification expose the minimum amount of information necessary to enable their intended uses? - -Yes (none). - -## 2.3 How do the features in your specification deal with personal information, personally-identifiable information (PII), or information derived from them? - -No handling of personal information, PII, or information derived from them. - -## 2.4 How do the features in your specification deal with sensitive information? - -No handling of sensitive information. - -## 2.5 Do the features in your specification introduce new state for an origin that persists across browsing sessions? - -No. - -## 2.6 Do the features in your specification expose information about the underlying platform to origins? - -No. - -## 2.7 Does this specification allow an origin to send data to the underlying platform? - -No. - -## 2.8 Do features in this specification enable access to device sensors? - -No. - -## 2.9 Do features in this specification enable new script execution/loading mechanisms? - -No. - -## 2.10 Do features in this specification allow an origin to access other devices? - -No. - -## 2.11 Do features in this specification allow an origin some measure of control over a user agent’s native UI? - -Yes, it would allow an author to adjust the styles produced by the default UA tooltips inside of web content. - -## 2.12 What temporary identifiers do the features in this specification create or expose to the web? - -None. - -## 2.13 How does this specification distinguish between behavior in first-party and third-party contexts? - -No distinction; no special considerations for use of the feature by third-party resources. - -## 2.14 How do the features in this specification work in the context of a browser’s Private Browsing or Incognito mode? - -No change to behavior in Private Browsing or Incognito modes. -No features of the specification that would allow for correlation of a single user's activity across normal and private browsing / incognito modes. - -## 2.15 Does this specification have both "Security Considerations" and "Privacy Considerations" sections? - -Yes. - -## 2.16 Do features in your specification enable origins to downgrade default security protections? - -No. - -## 2.17 How does your feature handle non-"fully active" documents? - -`::tooltip` should not be handled any differently than the current browser default tooltips. - -## 2.18 What should this questionnaire have asked? - -No special considerations for this feature. diff --git a/CacheAPIResponseMetadata/explainer.md b/CacheAPIResponseMetadata/explainer.md deleted file mode 100644 index c5f768ad5..000000000 --- a/CacheAPIResponseMetadata/explainer.md +++ /dev/null @@ -1,179 +0,0 @@ -# Adding Response Metadata to Cache API Explainer - -Authors: [Aaron Gustafson](https://github.com/aarongustafson), [Jungkee Song](https://github.com/jungkees) - -## Status of this Document -This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. -* This document status: **Active** -* Expected venue: [W3C Web Incubator Community Group](https://wicg.io/) -* Current version: this document - -## Introduction - -Currently, many sites are using Service Workers and the Cache API to store absolutely everything they can, often relying on the browser to evict content when there is storage pressure. Sites that want to be more proactive in managing their caches are limited to pruning responses solely based on when they were cached, typically iterating over the keys of a given cache and deleting the first item until they reduce the quantity of cached responses below a predefined threshold. That might be a good strategy if all responses were equal, but they aren’t. Some payloads are large, others are small. Some resources are computationally-intensive to produce, others are static files served by a CDN. Some are only accessed once, others are accessed with great frequency, often depending on both the site itself and the particular user interacting with it. - -Having a little more clarity on the shape and form of cached responses would enable developers to be more intentional about what they do with cached content. - -## Caching response metadata - -It would be quite useful to have access to metadata about each cached item. Much of the useful metadata is managed by the user agent, for example: - -- Timestamp for when the `Response` was added to the cache -- Timestamp for the last time the `Response` was retrieved from the cache -- How many times a given `Response` has been retrieved from the cache -- The amount of space the cached `Response` occupies on disk - -It’s worth noting that the size on disk could be inferred from the `Response`’s "Content-Length" header (or "Transfer-Encoding"), but these methods are unreliable. A better approach is to have the browser actually fill in this data so it is available even if these headers are omitted. - -Taken together, this Explainer proposes adding the following read-only attributes to the `Response` class, as [defined by the Fetch API](https://fetch.spec.whatwg.org/#response): - -- `cachedAt`: timestamp added when the Response is cached -- `lastResponseAt`: timestamp updated [each time a cached Response is returned by the Service Worker](#observing-responses); must be at least as new as `cachedAt` -- `responseCount`: a number that increments [each time a cached Response is returned by the Service Worker](#observing-responses) -- `size`: computed disk size of the Request/Response object pair (or 0 for opaque `Response`s) - -If adding these directly to the `Response` is not feasible, these could be added as properties of an object assigned to a single key, such as `Response.cacheData`. Ideally, however, these could be added to a subclass of the `Response` interface used in the `Cache API` context specifically: - -```idl -interface CachedResponse : Response { - readonly attribute DOMTimeStamp cachedAt; - readonly attribute DOMTimeStamp lastResponseAt; - readonly attribute unsigned long long responseCount; - readonly attribute unsigned long long size; -}; -``` - -These keys would only be available for all `CachedResponse`s with the exception of opaque `Request`s, which must always report a size of "0". - -## Goal - -Enable developers to access a richer set of information about their cached Requests in order to help them make better decisions with respect to purging content. - -## Non-goals - -Provide developers with more convenient access to other metadata that could be obtained by reading the `Response` headers (e.g., "Server-Timing"). - -## Use Cases - -Websites that want to limit their own cache often purge items based on when they were cached (inferred by the order in which they are added to the cache, reflected in `cache.keys`): - -```js -function trimCache(cacheName, maxItems) { - // open the cache - caches.open(cacheName) - .then( cache => { - // get the keys and count them - cache.keys() - .then(keys => { - // Do we have more than we should? - if (keys.length > maxItems) { - // delete the oldest item and run trim again - cache.delete(keys[0]) - .then( () => { - trimCache(cacheName, maxItems) - }); - } - }); - }); -} -``` - -This assumes all cached `Response`s are equal in terms of both usefulness to the end-user and how much disk space they are occupying. Being able to retrieve information such as when they were last accessed and how much space they occupy on disk could enable them to make smarter decisions around cache eviction. - -## Examples - -**Example 1:** Remove content that is very large (> 5 MB) and has not been accessed in more than 30 days: - -```js -async function trimCache( cacheName ) { - const large = 5000000; // 5 MB - const old = Date.now() - 2592000000 // 30 days ago - - const cache = await caches.open( cacheName ); - - // Collect Request objects - for (const request of await cache.keys()) { - cache - .match(request) - .then(response => { - if ( response.size > large && - response.lastResponseAt < old ) - { - cache.delete( request ); - } - }); - } -} -``` - -**Example 2:** Remove content accessed fewer than 5 times, with the last time being more than 90 days ago: - -```js -async function trimCache( cacheName ) { - - const old = Date.now() - 7776000000 // 90 days ago - - const cache = await caches.open( cacheName ); - for (const request of await cache.keys()) { - cache - .match(request) - .then(response => { - if ( response.responseCount < 5 && - response.lastResponseAt < old ) - { - cache.delete( request ); - } - }); - } -} -``` - -**Example 3:** Remove content that only got used when it was cached, over 180 days ago: - -```js -async function trimCache( cacheName ) { - - const old = Date.now() - 15552000000 // 180 days ago - - const cache = await caches.open( cacheName ); - for (const request of await cache.keys()) { - cache - .match(request) - .then(response => { - let cached_at = new Date( response.cachedAt ), - accessed_at = new Date( response.lastResponseAt ); - // If year, month, and day match + old - if ( cached_at.getFullYear() === accessed_at.getFullYear() && - cached_at.getMonth() === accessed_at.getMonth() && - cached_at.getDate() === accessed_at.getDate() && - response.cachedAt < old ) - { - cache.delete( request ); - } - }); - } -} -``` - -## Observing Responses - -This spec draws a distinction between accessing a cached resource and actually responding with that resource. The act of merely reading a cached resource via the Cache API would not update its `lastResponseAt` timestamp or increment its `responseCount`. For these values to be updated, the cached response would actually need to be sent to the browser using [`FetchEvent.respondWith()`](https://w3c.github.io/ServiceWorker/#dom-fetchevent-respondwith). - -As an example of why this matters, consider [this scenario](https://remysharp.com/2019/09/05/offline-listings): an author builds functionality into their offline page that exposes all web pages that have been cached, so a user can see what content they have access to offline. As part of that JavaScript program, they read the contents of the cache looking for HTML pages and extract the contents of the `title` and `meta[name=description]` elements and then present that content (along with a link to the resource) in the `body` of the document. In this scenario, the resource has been used, but it was not provided as a response, so it would not trigger an update of `lastResponseAt` or `responseCount`. Following the link, however, results in the Service Worker responding to the `FetchEvent` with the cached response and that *would* trigger an update of `lastResponseAt` and `responseCount`. - -## Implementation Notes - -* Testing is required to confirm whether or not there are performance issues with respect to updating `lastResponseAt` and `responseCount` ([Issue #148](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/148)) - -## Privacy Considerations - -It is possible that the timestamps stored in the `cachedAt` and `lastResponseAt` properties could be used for fingerprinting. As developers already have the ability to use `performance.now()` to get timestamps when resources are cached, this new functionality does not introduce any new fingerprinting risk. - -It is possible that `responseCount` could be used for fingerprinting (leveraging the [User Behavior fingerprinting vector](https://2019.www.torproject.org/projects/torbrowser/design/#fingerprinting-linkability)), but the `responseCount` property does not introduce any fingerprinting surface not already exposed via the Cache API. - -To eliminate the possibility that a first-party website could use this functionality to snoop on the content of a third-party request service, User Agents are required to report a `size` of 0 for all opaque `Response`s. - -## Open Questions - -1. Would it be worthwhile to introduce a mechanism by which developers specifically opt-out of updating details like `lastResponseAt` and/or `responseCount` when retrieving an item from the cache? -2. The `trimCache` examples could be run in the main thread or the ServiceWorker, but in either instance could potentially cause the script to lock up. Would it make sense to introduce an async iterator to the Cache API in order to reduce the need for repeatedly calling match? Could there be an async iterator that efficiently enables cache pruning based on these keys? \ No newline at end of file diff --git a/Canvas/Available-Width.png b/Canvas/Available-Width.png deleted file mode 100644 index a7531006e..000000000 Binary files a/Canvas/Available-Width.png and /dev/null differ diff --git a/Canvas/Example1.html b/Canvas/Example1.html deleted file mode 100644 index c7700ce2e..000000000 --- a/Canvas/Example1.html +++ /dev/null @@ -1,40 +0,0 @@ -<title>CanvasFormattedText samples - - -Text wrap width: - \ No newline at end of file diff --git a/Canvas/Example1.png b/Canvas/Example1.png deleted file mode 100644 index db23a1b99..000000000 Binary files a/Canvas/Example1.png and /dev/null differ diff --git a/Canvas/Example2.html b/Canvas/Example2.html deleted file mode 100644 index 7b3d1a088..000000000 --- a/Canvas/Example2.html +++ /dev/null @@ -1,38 +0,0 @@ -CanvasFormattedText samples - - -Text wrap width: - \ No newline at end of file diff --git a/Canvas/Example2.png b/Canvas/Example2.png deleted file mode 100644 index cd80441bb..000000000 Binary files a/Canvas/Example2.png and /dev/null differ diff --git a/Canvas/FormattedText.md b/Canvas/FormattedText.md deleted file mode 100644 index ec38ad805..000000000 --- a/Canvas/FormattedText.md +++ /dev/null @@ -1,500 +0,0 @@ -Formatted Text -============= - -Authors: - [sushraja-msft](https://github.com/sushraja-msft), - [travisleithead](https://github.com/travisleithead) - -## Status of this Document -This document is intended as a starting point for engaging the community and standards -bodies in developing collaborative solutions fit for standardization. As the solutions -to problems described in this document progress along the standards-track, we will -retain this document as an archive and use this section to keep the community up-to-date -with the most current standards venue and content location of future work and discussions. -* This document status: **`ARCHIVED`** -* Current venue: [W3C Web Incubator Community Group](https://wicg.io/) -* Current version: https://github.com/WICG/canvas-formatted-text/blob/main/README.md - -## Introduction - -Applications like word processors, spreadsheets, PDF viewers, etc., face a -choice when moving their legacy presentation algorithms to the web. The view layer of -these applications can be designed to output HTML, SVG or use Canvas. Canvas -is an expedient choice for some view models since it can easily present data models -where the view model and the data model are not tightly coupled. Additionally, the -Canvas APIs map well to the imperative APIs the legacy algorithms used for rendering -content. - -In gaming and mixed reality (VR and AR) scenarios today, Canvas is the only logical -choice for presenting a rapidly-changing view. - -In these scenarios, it is often necessary to present text to the user. The 2D Canvas -API currently provides a relatively simplistic text rendering capability: A single run -of text can be -[measured](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/measureText) -and [rendered](https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/fillText) -**as a single line**, optionally compressed to fit -within a certain width. If the developer needs to present more than just a few words -(e.g., a paragraph of text) then things get very complicated very quickly. Some options -might include trying a hybrid approach of overlaying HTML elements with the Canvas to -leverage HTML's native text rendering and wrapping capabilities, or attempting to -write your own line breaking logic in JavaScript using the primitive Canvas text measuring -and rendering APIs as a starting point. Neither of these options are very desirable, -and the HTML hybrid approach may not be available depending on the scenario (e.g., in -VR headsets). - -Why would a JavaScript implementation of line-breaking and wrapping be hard? While this -may seem trivial at first glance, there are a number of challenges especially for -applications that must be designed to handle rendering of text in multiple languages. -Consider the complexity involved in the following requirements for a robust line -breaking algorithm: - -* Identify break opportunities between words or - [Graphemes](https://en.wikipedia.org/wiki/Grapheme). Break opportunities are - based primarily on the Unicode Spec but also use dictionaries for languages - like Thai and French that dictate additional line breaking rules. -* Identify grapheme clusters. Graphemes are character combinations (such as - [Diacritics](https://en.wikipedia.org/wiki/Diacritic) and - [Ligatures](https://en.wikipedia.org/wiki/Orthographic_ligature)) that result - in a single glyph and hence should not be broken up. - E.g.: `g` (Latin small letter G 0067) + `◌̈ ` (Combining dieresis 0308) = `g̈` -* Handle Bidi text. For proper Bidi rendering the bidi level context needs to be - considered across lines. -* Text Shaping and Kerning. These features can affect the measured pixel length - of a line. - -Javascript libraries could perform line breaking, but as noted above, this is -an arduous task. This gets more complicated if text with different formatting -characteristics (e.g., size, bold, italic) are needed. The browser already has -a powerful line breaking, text shaping component used for regular HTML layout. -Why not allow the Canvas API to leverage it? This is the feedback we've heard -from web developers who have struggled with implementing JavaScript-based -multiline text rendering APIs for Canvas. Adding this native capability to -Canvas would both dramatically simplify and improve the correctness and -performance of their code. - -This proposal aims to make it easy for web developers to get correct and fast -multiline formatted text rendering functionality in Canvas, while preserving -the low-level flexibility for positioning text that is already present in the -existing Canvas API. It takes inspiration from -[related APIs](https://docs.microsoft.com/en-us/dotnet/framework/wpf/advanced/drawing-formatted-text) -as a starting point. - -The proposal below introduces a Canvas formatted text API and shows how it can -be used for both simple and advanced use cases. During the design we considered -a number of [alternatives](#alternatives-considered) before landing on the current -proposal. We welcome your comments and suggestions; specifically note the -[list of open issues and questions](#open-issues-and-questions) below. - -## Usage - -Consider a use case where a web developer wants to render the text “the quick -**brown** fox jumps over the lazy dog” into a horizontal space 250px wide and have -the text wrap instead of compress-to-fit. In order to enable -this scenario, we introduce a new object `CanvasFormattedText` which will keep track -of all the formatted text. We also introduce two new formatted text drawing APIs onto -the canvas context: `strokeFormattedText` and `fillFormattedText`. - -First we will consider how to represent the bolded text in the example above? -Regular HTML uses markup elements to style parts of text. For the 2d Canvas API -we split the text into "runs" and allow each run to have its own optional formatting: - -```js -const c = document.getElementById( "myCanvas" ); -const context = c.getContext( "2d" ); - -// Collect text into a CanvasFormattedText object -let formattedText = new CanvasFormattedText(); -formattedText.appendRun( { text: "the quick " } ); -formattedText.appendRun( { text: "brown", font: `bold ${ context.font }` } ); -formattedText.appendRun( { text: " fox jumps over the lazy dog" } ); -``` - -### CanvasFormattedText object - -The `CanvasFormattedText` object represents a collection of text runs together with formatting -information. - -The `appendRun` function takes an object with the following properties: -* `text` - required string - the text run itself -* `font` - optional string - unique font settings for this run (accepts the same values - as the 2D context's `font` property). -* `color` - optional string - unique color values for this run (the string is parsed as - a CSS `` value). - -The object provided to `appendRun` is a `CanvasFormattedTextRun` dictionary and represents text -content along with the fill/stroke style overrides for the text. Developers provide contiguous -text with identical style as a single run. The font and color values when specified override the -default values that would be used from the canvas context -[Text Styles](https://www.w3.org/TR/2dcontext/#text-styles). - -The styles for a run do not depend on or apply on top of the previous run. If this were not the -case, removing or adding a text run would have an undesirable ripple effect of changing styles on -all subsequent runs. - -Note: The proposal avoids using indices to indicate styling ranges, as these indices change with -text run insertion and deletion. - -Next, to render the text into multiple lines, we use the new `fillFormattedText` API -which takes a max width to wrap the text at. - -```js -context.font = "18pt Arial"; -context.fillFormattedText( formattedText, /*x*/0, /*y*/50, /*wrapWidth*/250 ); -``` - -This would produce the following output on the canvas: - -Wrapped text rendered in a canvas. - -### Bidi Text - -No additional work is needed from web developers to support bidi text. -Implementations will perform bidi analysis on the `CanvasFormattedText`'s text runs -and create internal bidi runs if necessary. An example demonstrating bidi text follows: - -```js -const context = document.getElementById( "myCanvas" ).getContext( "2d" ); -context.font = "30px Arial"; -let canvasFormattedText = new CanvasFormattedText(); -canvasFormattedText.appendRun( { text: "Sample arabic بمدينة مَايِنْتْس، ألمانيا text." } ); -context.fillFormattedText( canvasFormattedText, /*x*/0, /*y*/30, /*wrapWidth*/350 ); -``` - -produces the following output on the canvas: - -Wrapped text rendered in a canvas. - -### Pre-existing text controls - -The other text styles `textAlign`, `textBaseline` on the canvas context control justification -and baseline alignment of the multiline text relative to provided x/y coordinates. - -CSS styles on the canvas element affect text rendering. These CSS properties are - -- `line-height` - Specifies height of a line. -- `direction` - Sets the initial direction for bidi analysis. -- `word-break` / `word-wrap` - Controls break opportunities for text wrapping. - - -## Advanced Usage - -While the single shot `fillFormattedText` and `strokeFormattedText` APIs gets us a -long way in adding text wrapping support, it is also conceivable that developers will -want to render text one line at a time. This offers advanced control over both position -and available width used for each line. Additionally, rendering text one line at a time -also provides developers the flexibility to render only the content visible onscreen or -content that changed. - -To render one line at a time, we need to introduce a few additional objects and concepts. -First of all, we need to be able to indicate what portion of the aggregate `CanvasFormattedText` -text runs we need to render on each separate line, and we need to be able to specify a maximum -width to render them into (which can change from line to line). The API is designed to allow -an iterative approach to line rendering, but is also flexible enough to allow for many other -scenarios. - -### Position objects - -In both bidi and regular text scenarios lines always contain a contiguous range -of characters from the source text. The position at which to start the next line -can be represented with an offset in that text. - -To indicate a position to begin rendering a text run, we define a position object that has three -components: -1. `formattedText` - required `CanvasFormattedText` reference - the container to which this - position object belongs. -2. `textRunIndex` - required number - the index of a text run object contained by the - `CanvasFormattedText` (e.g., the object associated with the text run "the quick " in - the example above). -3. `textRunOffset` - required number - the offset into the text run value itself. For example: - a `2` would indicate a position starting at the "e" in the string "the quick". - -For convenience, one of these objects (a `CanvasFormattedTextPosition` dictionary) can be obtained -from the `CanvasFormattedText` using the `beginPosition()` function. - -We use the position object to indicate the starting position within the `CanvasFormattedText` -object from which we'd like to start rendering a line. But, where should the ending position be? -Rather than try to guess or iteratively test, we can have the API figure this out for us. All it -needs to know is how much space we'd like to allocate for the line. - -### CanvasFormattedTextLine object - -The desired width of the line and the starting position is provided to the `measureFormattedText` -function, a new formatted text measurement API on the canvas context. It returns a -`CanvasFormattedTextLine` object that is ready for rendering and contains some other helpful -information packed into it. To render the line to the canvas we introduce `fillFormattedTextLine` -and `strokeFormattedTextLine` which conveniently take a `CanvasFormattedTextLine` object and an x/y -coordinate where the text should be rendered to the canvas. The `CanvasFormattedTextLine` keeps the -line state (such as the desired width) so that only the location where the line should be rendered -is needed when it comes time to draw. - -Each `CanvasFormattedTextLine` provides a `nextPosition()` function that returns a position object -for where the next line should start from in the case that there is more text to render from the -`CanvasFormattedText` object. If the line represents the last line of text to render, then -`nextPosition` returns `null`. - -Here's an example that shows how these objects relate to each other, illustrating the use of -`fillFormattedTextLine` to render each line of our first example. - -```js -const context = document.getElementById("myCanvas").getContext("2d"); - -// Collect text into a CanvasFormattedText object -let formattedText = new CanvasFormattedText(); -formattedText.appendRun( { text: "the quick " } ); -formattedText.appendRun( { text: "brown", font: `bold ${context.font}` } ); -formattedText.appendRun( { text: " fox jumps over the lazy dog" } ); - -let startPosition = formattedText.beginPosition(); -while ( startPosition ) { - let currentLineObject = context.measureFormattedText( startPosition, /*wrapWidth*/250 ); - context.fillFormattedTextLine( currentLineObject, /*x*/0, y ); - startPosition = currentLineObject.nextPosition(); - y += (currentLineObject.height); -} -``` - -We can use this additional flexibility to adjust each line's width and position to accommodate -any other objects being presented to the canvas. In this example, we adjust the lines to wrap -around an image. - -```js -const context = document.getElementById("target").getContext("2d"); -// Grab a previously loaded element to render on the canvas. -const image = document.getElementById("myImg"); - -let sampleString = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Mi proin sed libero enim sed faucibus turpis. Sed augue lacus viverra vitae. Adipiscing tristique risus nec feugiat. Odio facilisis mauris sit amet massa. Non arcu risus quis varius quam quisque id diam vel. Scelerisque varius morbi enim nunc faucibus a pellentesque sit amet. Lectus proin nibh nisl condimentum. Eget mauris pharetra et ultrices neque ornare aenean euismod elementum. Eget magna fermentum iaculis eu non. Id aliquet risus feugiat in ante. Gravida in fermentum et sollicitudin ac. "; - -// Draw the image on the top left of the canvas. -let x = 0, y = 0; -// Define margins around the image. -let topLeftMargin = 10, bottomRightMargin = 10; -context.drawImage( image, topLeftMargin + x, topLeftMargin + y ); - -// Setup the formatted text object. -let formattedText = new CanvasFormattedText(); -formattedText.appendRun( { text: sampleString } ); - -const fontHeight = 20; -context.font = `${ fontHeight }px Arial`; - -// Draw each line adjusting available width if there is an image on the -// left that reduces the available width. -const maxWidth = context.canvas.width - bottomRightMargin; -y += fontHeight; -let lineStartPosition = formattedText.beginPosition(); -do { - // Determine x position. - if ( y <= ( topLeftMargin + image.height + bottomRightMargin ) ) { - x = topLeftMargin + image.width + bottomRightMargin; - } - else { - x = topLeftMargin; - } - let line = context.measureFormattedText( lineStartPosition, maxWidth - x ); - context.fillFormattedTextLine( line, x, y ); - y += line.height; - lineStartPosition = line.nextPosition(); -} while ( lineStartPosition ); - -``` - -This would produce the following output on the canvas - -Example use case for rendering multiline text with varying available width - -The separation of measuring to obtain a `CanvasFormattedTextLine` object and the request to -draw the line using `fillFormattedTextLine` or `strokeFormattedTextLine` allows for a -useful scenario in adjusting line separation; `CanvasFormattedTextLine` objects can be collected -and used to sum the total height needed to render all the lines allowing the developer to -distribute the line-spacing as desired. - -Additional `TextMetrics` info can be obtained from each `CanvasFormattedTextLine` object by iterating -over the `lineSegments` array. A "line segment" is either: -1. The same as the developer-provided text run, or; -2. Part of the developer-provided text run which can happen when the line wrapped in the middle - of a text run, or when text with different bidi characteristics are encountered - (e.g., RTL inside of LTR text). - -Line segment objects have 4 properties: -* `beginPosition` - a reference to the text run and offset where this segment starts -* `endPosition` - a reference to the text run and offset where this segment ends -* `isRTL` - a boolean indicating if the segment's text is right-to-left rendered -* `textMetrics` - a reference to the existing canvas `TextMetrics` object for this segment. - -Segment information can be helpful when desiring to simulate cursor movement over a line or to -draw backgrounds, underlines, borders, etc., with the combination of the `isRTL` value and -proposed `TextMetrics`'s `advances` (see related issues -[3994](https://github.com/whatwg/html/issues/3994), -[4030](https://github.com/whatwg/html/issues/4030), and -[4034](https://github.com/whatwg/html/issues/4034)) - -## WebIDL details - -```webidl - -// Extends the existing CanvasText mixin interface, which is included in: -// * CanvasRenderingContext2D -// * OffscreenCanvasRenderingContext2D -// (notably not included in PaintRenderingContext2D; -// see :https://drafts.css-houdini.org/css-paint-api-1/#2d-rendering-context) -partial interface CanvasText { - // Render entire CanvasFormattedText with line wrapping (one-shot) - void fillFormattedText(CanvasFormattedText formattedText, - double x, - double y, - double wrapWidth); - void strokeFormattedText(CanvasFormattedText formattedText, - double x, - double y, - double wrapWidth); - - // Line-at-a-time text measurement - CanvasFormattedTextLine measureFormattedText(CanvasFormattedTextPosition textPosition, - double wrapWidth); - - // Line-at-a-time text rendering (given a previously measured line). - void fillFormattedTextLine(CanvasFormattedTextLine line, - double x, - double y); - void strokeFormattedTextLine(CanvasFormattedTextLine line, - double x, - double y); -}; - - -[Exposed=Window,Worker] -interface CanvasFormattedText { - constructor(); - - CanvasFormattedTextPosition beginPosition(); - getter CanvasFormattedTextRun getRun(unsigned long index); - CanvasFormattedTextRun appendRun(CanvasFormattedTextRun newRun); - - readonly attribute unsigned long length; -}; - - -dictionary CanvasFormattedTextRun { - required DOMString text; - DOMString font; - DOMString color; -}; - - -dictionary CanvasFormattedTextPosition { - required CanvasFormattedText formattedText; - required unsigned long textRunIndex; - required unsigned long textRunOffset; -}; - - -// Returned exclusively from measureFormattedText() -[Exposed=Window,Worker] -interface CanvasFormattedTextLine { - readonly attribute double height; - readonly attribute double width; - CanvasFormattedTextPosition? nextPosition(); - readonly attribute FrozenArray lineSegments; -}; - - -dictionary CanvasFormattedTextLineSegment { - required CanvasFormattedTextPosition beginPosition; - required CanvasFormattedTextPosition endPosition; - required boolean isRTL; - required TextMetrics textMetrics; -}; -``` - -## Open issues and questions - -Please review and comment on our [existing open issues](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues?q=is%3Aissue+is%3Aopen+label%3A%22Canvas+Formatted+Text%22). - -Additionally, here are a few minor tweaks suggested for the current API surface (subject to change): - * Recommend adding a constructor overload of `CanvasFormattedText` that takes a list - of `CanvasFormattedTextRun` objects, e.g., - `constructor( sequence formattedRunsInit );` - * Recommend renaming 'appendRun' to 'addRun' to match Path2D:addPath() nomenclature; - * Recommend returning CanvasFormattedText object [this] from 'appendRun' to allow - chaining calls together... - * Recommend making `CanvasFormattedText` object an iterable, e.g., - `iterable;` - * Recommend dropping the 'getRun' function name (getter is probably good enough) - * `CanvasFormattedTextRun`:`color` could be named "style" to more closely match - related stroke/fillStyle. Will patterns and gradients work here too? - * `CanvasFormattedTextLine` has `width` and `height`, might also benefit from - additional TextMetrics interface methods: `actualBoundingBoxLeft` (and Right, - Ascent, Descent) properties. - -## Alternatives Considered - -### Imperative model -The proposal here addresses two separate problems. One of styling ranges of text -and having an object model and two of auto wrapping text. - -An alternative design considered was to support auto wrapping without requiring -that the developer provides all the text upfront. Similar to canvas path, the -developer would call `setTextWrapWidth( availableWidth )` and follow through with -multiple calls to `fillText` on the canvas context that renders text and advances -a cursor forward. - -Such an imperative style API was not pursued for two reasons. With bidi text, the -entire width of a right-to-left segment needs to be determined before any of the -`fillText` calls can be rendered. This issue can be addressed by adding a finalization -step say `finalizeFillText()`. However still, such an imperative API adds a performance -cost in terms of recreating the formatted text when the developer is simply trying to -redraw the same text content for a changing available width. - -## Accessibility Considerations - -Making the Canvas accessible is a persistent challenge for the web today. Several -efforts are underway, including a -[promising solution](https://github.com/WICG/aom/blob/gh-pages/explainer.md#use-case-4-adding-non-dom-nodes-virtual-nodes-to-the-accessibility-tree) -as part of the Accessible Object Model (AOM) family of proposals. - -Meanwhile, web developers are encouraged to use the canvas element's fallback content -to provide HTML markup that describes the canvas' current state. For text used as part -of this API proposal, the complete source text of the `CanvasFormattedText` should be -placed in block-styled -[flow content](https://html.spec.whatwg.org/multipage/dom.html#flow-content-2) (such -as a `

` element), with formatted sections wrapped in appropriate -[phrasing content](https://html.spec.whatwg.org/multipage/dom.html#phrasing-content-2) -(such as `` and styled to match the `CanvasFormattedTextRun` formatting.) The -markup should make use of -[ARIA Live Regions](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Live_Regions) -to be sure assistive technologies (ATs) pickup and announce any dynamic changes. - -Looking generally at what low-level features are necessary to make text fully accessible -to an AT (which may become future requirements for canvas) we envision the following needs -(partially met by this proposal): - -* Word bounds/breaks - used to support "navigate by word" AT features. Break opportunities - (in HTML) are calculated as part of layout, and as part of the `fillFormattedText`, - `strokeFormattedText` and `measureFormattedText` APIs to find where line breaks will be - possible. However, the meta-data about these break opportunities are not exposed to the - developer (as noted in open issues). -* Character bounds - used by ATs for character-by-character navigation. The - `CanvasFormattedTextLineSegment` surfaces `TextMetrics` that will include an `advances` - array of positions used to describe character bounds. -* Format boundaries - provide opportunities for the AT to optionally add emphasis or - pass over certain runs of text. The developer has already separated ranges of - similarly-formatted runs of text into `CanvasFormattedTextRun`s which can be iterated at - any time to calculate the offset positions to meet this requirement. - -We are interested in hearing about additional community feedback related to accessibility -and thoughts on the related open issues. - -## Privacy Considerations - -HTML5 canvas is a browser finger printing vector -(see [canvas fingerprinting](https://en.wikipedia.org/wiki/>Canvas_fingerprinting)). -Fingerprinting happens through APIs `getImageData`, `toDataURL`, etc. that allow -readback of renderer content exposing machine specific rendering artifacts. -This proposal adds the ability to render multiple lines of text, potential -differences in text wrapping across browsers could contribute to additional -fingerprinting. The existing implementer mitigations in some user agents that -prompt users on canvas readback continues to work here. - -We are currently evaluating whether this API would increase fingerprinting surface -area and will update this section with our findings. We welcome any community feedback. diff --git a/Canvas/README.md b/Canvas/README.md deleted file mode 100644 index 6d6a8c517..000000000 --- a/Canvas/README.md +++ /dev/null @@ -1,2 +0,0 @@ -If you’re looking for Canvas Formatted Text, see: -* https://github.com/WICG/canvas-formatted-text/blob/main/README.md diff --git a/Canvas/SetAndResetClip.md b/Canvas/SetAndResetClip.md deleted file mode 100644 index 0e414cd1c..000000000 --- a/Canvas/SetAndResetClip.md +++ /dev/null @@ -1,246 +0,0 @@ -# Set and Reset Clip() - -Authors: - [sushraja-msft](https://github.com/sushraja-msft), - [travisleithead](https://github.com/travisleithead) - -## Status of this Document -This document is a starting point for engaging the community and standards -bodies in developing collaborative solutions fit for standardization. As the solutions -to problems described in this document progress along the standards-track, we will -retain this document as an archive and use this section to keep the community up-to-date -with the most current standards venue and content location of future work and discussions. -* This document status: **Active** -* Expected venue: [W3C Web Incubator Community Group](https://wicg.io/) -* Current version: this document - -## Background - -Canvas 2d contexts support the ability to create clipping regions via the `context.clip()` API. -Once a clipping region is set on the canvas, subsequent drawing commands only render the -intersection of the drawing command's region with that of the clipping region. If an existing -clipping region is set, calling `clip()` again will result in a clipping region that is the -intersection of the last clipping region with the requested region. -[[see spec](https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-clip)] - -## Clipping Concerns - -Four areas of concern illustrate the use cases and context for our proposal. - -### Animation loops - -When using `clip()` in an animation loop (e.g., via a `requestAnimationFrame` callback), say -to spotlight a main character, developers will quickly realize that the "current clipping region" -exhibits the somewhat unexpected intersection-with-previous-clip behavior. If the center of -attention is moving, the clip region intersection behavior quickly results in an empty clipping -region in which everything is getting clipped: - -```js -let spot = { x: 50, y: 50 }; -function animationFrameCallback() { - context.clearRect( 0, 0, canvas.width, canvas.height ); - let spotlight = new Path2D(); - spotlight.arc( spot.x, spot.y, /*...*/ ); - context.clip( spotlight ); // Create a circle clipping region centered at 'spot' - - // Draw main character here... - - // Move the spotlight to the right - spot.x++ - - requestAnimationFrame( animationFrameCallback ); -} -``` - -At this point, the developer will naturally check the documentation for a way to reset the clipping -region, but won't be able to find such an API because it doesn't exist. Eventually, they may stumble -upon the [unintuitive workaround](https://stackoverflow.com/questions/25282793/how-to-remove-the-clip-of-a-region-in-html-5-canvas) for this limitation. - -### Multiple clips - -In many scenarios, in order to render the desired effect, multiple clipping regions (that do not -intersect) are needed. For example, imagine rendering a series of contacts, where each contact image -is clipped to a particular shape. - -![Rectangular contact image being clipped to a circle shape](photoclip.png) - -The desired result is a grid of images. - -![Four rows of 7 contact images, all clipped to a circle shape](photogallery.png) - -The natural approach to rendering this grid involves writing a few loops and processing each -contact image's clip and drawing logic within the nested loop body. However, if done sequentially -in that way, none of the images beyond the first one would render because of the cumulative -nature of `clip()`. - -Fortunately, since clipping regions are based on paths, and because paths can have independent -sub-paths (e.g., `path.rect()` and `path.arc()` both create independent sub-paths of `path`), it -is possible to create a single path to use as a clipping region that can address this use case. -Unfortunately, the result leads to a poor design pattern: the forced separation of the logic -that sets up the clipping region from the logic that performs the drawing after the clipping -region is established: - -```js -// draw background and labels -// ... - -// setup clipping path (with sub-paths for each contact image) -let comboClipRegion = new Path2D(); -for ( let row = 0; row < 4; row++ ) { - for ( let col = 0; col < 7; col++ ) { - comboClipRegion.arc( col * WIDTHSPACING, row * HEIGHTSPACING, /*...*/ ); - } -} -// Set the clip region -context.clip( comboClipRegion ); -// Draw the contact images into the "holes" provided by the current clipping region -for ( let row = 0; row < 4; row++ ) { - for ( let col = 0; col < 7; col++ ) { - context.drawImage( getImg( col, row ), col * WIDTHSPACING, row * HEIGHTSPACING, /*...*/ ); - } -} -``` - -### Performance - -The current clipping region is one of the features managed by the canvas's -[drawing state](https://html.spec.whatwg.org/multipage/canvas.html#the-canvas-state). This makes -it possible to workaround the previous two concerns by pushing a "clean" canvas state onto the -stack of drawing states using `context.save()` before setting the clipping region, performing related -drawing instructions, and then popping off the state using `context.restore()` to -discard or "reset" the clipping region. This is the state-of-the-art in managing -clipping regions, but it has a few potential drawbacks: - -1. It may not be clear where in code to place the `save()` and `restore()` points. -2. Managing clipping state with `save()` and `restore()` could interfere with other drawing - state that is managed by the stack of drawing states, such as the current transform, - associated fill or stroke styles, the global alpha, shadows, etc. -3. The current path is not one of the things managed by the stack of drawing state. Care must - be taken when using `clip()` against the current path and assuming that `restore()` will - also reset the current path to what it was prior to a `save()`. -3. Excessive use of `save()` and `restore()` can be a performance bottleneck, since these APIs - must store and assign a copy of **all** of the related drawing states-- not just the clipping path. - -### API Consistency - -The behavior of the clipping region is very similar to how the canvas' transformation matrix works, as both: - -* are managed by the drawing state (with `save()` and `restore()`). -* have a way to set their state (e.g., for transformation matrices `transform(a,b,c,d,e,f)`). -* apply cumulative behavior when adjusted sequentially (e.g., `clip()` followed by - `clip()` again, as described previously. For transformation matrices: `scale()` then `rotate()`, - etc.) - -However, the current transformation matrix enjoys some special benefits, that we would like to -bring to clipping regions. - -* `setTransform()` allows the current transformation matrix to be overwritten with a completely - new transformation matrix-- notably without applying any cumulative effects. -* `resetTransform()` allows the current transformation matrix to be set back to its initial state - (the identity matrix). - -Given the previous concerns around clipping regions, and the API disparity between clipping regions -and transformation matrices, it seems prudent to add the obviously-missing clipping region APIs -proposed below. - -## Proposal - -Following the established pattern set by the canvas transformation matrix APIs, we propose the -following: - -### setClip() - -`setClip()` replaces the current clipping region on the canvas context with whatever is the -current path -(the [current default path](https://html.spec.whatwg.org/multipage/canvas.html#current-default-path)). - -```js -context.rect( 0, 0, 100, 50 ); -context.clip(); -context.beginPath(); -context.rect( 50, 0, 100, 50 ); -context.setClip(); // The current clip is the last rectangle ('clip()' here would yield a square). -``` - -`setClip()` supports replacing the current clipping region with an explicitly provided -path object: - -```js -let path = new Path2D(); -path.rect( 0, 0, 100, 50 ); -context.setClip( path ); -``` - -As with transforms, `restore()` will restore the clipping region to the state it was in at the point -of calling `save()`. - -### resetClip() - -`resetClip()` puts the clipping region back in the state it was in when the context was initialized. -From the spec: "set to the largest infinite surface (i.e. by default, no clipping occurs)". - -## Examples - -Using our proposed APIs, we can re-write the animation example from earlier: - -```js -let spot = { x: 50, y: 50 }; -function animationFrameCallback() { - context.clearRect( 0, 0, canvas.width, canvas.height ); - context.resetClip(); // Tada! Perfect. - let spotlight = new Path2D(); - spotlight.arc( spot.x, spot.y, /*...*/ ); - context.clip( spotlight ); // Create a circle clipping region centered at 'spot' - - // Draw main character here... - - // Move the spotlight to the right - spot.x++ - - requestAnimationFrame( animationFrameCallback ); -} -``` - -We can also make our contact list grid code more efficient by eliminating -an entire double "for" loop: - -```js -// draw background and labels - -// Draw the photos with individual clipping regions -for ( let row = 0; row < 4; row++ ) { - for ( let col = 0; col < 7; col++ ) { - context.beginPath(); - context.arc( col * WIDTHSPACING, row * HEIGHTSPACING, /*...*/ ); - context.setClip(); - context.drawImage( getImg( col, row ), col * WIDTHSPACING, row * HEIGHTSPACING, /*...*/ ); - } -} -// Reset the clip region for whatever comes next -context.resetClip(); -``` - -In performance-sensitive applications that need to selectively manage a clipping region, -this can be done without the overhead of `save()` and `restore()` pairs. - -## Formal Definition - -A more formal definition, highlighting that the CanvasFillRule will still be respected in -both usages of `setClip()`: - -```webidl -interface mixin CanvasDrawPath { - void setClip(optional CanvasFillRule fillRule = "nonzero"); - void setClip(Path2D path, optional CanvasFillRule fillRule = "nonzero"); - void resetClip(); -}; -``` - -## Alternatives considered - -* None - -## Privacy Considerations - -We do not expect this API to introduce additional fingerprinting capabilities or other privacy -concerns, but we welcome community feedback. diff --git a/Canvas/photoclip.png b/Canvas/photoclip.png deleted file mode 100644 index 08b16dc05..000000000 Binary files a/Canvas/photoclip.png and /dev/null differ diff --git a/Canvas/photogallery.png b/Canvas/photogallery.png deleted file mode 100644 index a9d2c3357..000000000 Binary files a/Canvas/photogallery.png and /dev/null differ diff --git a/ClipboardAPI/ClipboardChangeEvent/README.md b/ClipboardAPI/ClipboardChangeEvent/README.md deleted file mode 100644 index d2bfa4bf3..000000000 --- a/ClipboardAPI/ClipboardChangeEvent/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# Introducing the `clipboardchange` Event: A New Way to Interact with the Clipboard - -The `clipboardchange` event is a new web platform feature that allows web applications to respond to changes in the system clipboard in a secure and efficient manner. This document provides an overview of the feature, how to use it, and how you can participate in its development through our Origin Trial. - -## The Problem: Inefficient Clipboard Polling - -Modern web applications, especially rich text editors and productivity tools, often need to know what *kind* of content is on the clipboard to provide a better user experience. For example, a web-based image editor might want to enable a "Paste" button only when there's an image on the clipboard. - -Previously, the only way to achieve this was to repeatedly poll the clipboard using `navigator.clipboard.read()`, which is inefficient and can have a noticeable performance impact. - -## The Solution: The `clipboardchange` Event - -The `clipboardchange` event provides a much more efficient and privacy-preserving solution. Instead of polling, you can now listen for an event that fires whenever the clipboard's content changes. - -### Key Features - -* **Efficient:** The event is dispatched by the browser only when a change occurs, eliminating the need for polling loops. -* **Privacy-Preserving:** For security reasons, the event **does not** expose the actual content of the clipboard. Instead, it provides an array of native MIME types for the available data (e.g., `"text/plain"`, `"image/png"`). -* **No User Prompts:** Because no sensitive content is exposed, this API does not require a user permission prompt, leading to a smoother user experience. -* **Focus-Aware:** The event only fires when your document has focus, preventing background pages from snooping on clipboard activity. -* **Cross-Platform:** Works on all platforms except iOS. - -## How to Use It - -Using the `clipboardchange` event is as simple as adding an event listener to `navigator.clipboard`: - -```javascript -if ('clipboard' in navigator && 'addEventListener' in navigator.clipboard) { - navigator.clipboard.addEventListener('clipboardchange', event => { - console.log('Clipboard content changed!'); - - // The event.types property contains an array of MIME types - console.log('Available MIME types:', event.types); - - // Example: Enable a "Paste Image" button if a PNG is on the clipboard - const pasteImageButton = document.getElementById('paste-image-button'); - if (event.types.includes('image/png')) { - pasteImageButton.disabled = false; - } else { - pasteImageButton.disabled = true; - } - }); -} else { - console.log('The clipboardchange event is not supported in this browser.'); -} -``` - -## Availability: Try it with Origin Trials! - -The `clipboardchange` event is currently available as an [Origin Trial](https://developer.chrome.com/docs/web-platform/origin-trials/) in Chrome and Microsoft Edge versions 140-142. This allows you to use the feature on your production site and provide valuable feedback to browser vendors before it's finalized. - -To participate, you'll need to: -1. **Register for the Origin Trial:** [Link to your Origin Trial registration page will go here]. -2. **Add the Origin Trial Token:** Once you have your token, add it to your pages via a `` tag or an HTTP header. - -```html - - -``` - -We encourage you to try it out and see how it can improve your application! - -## Provide Feedback - -Your feedback is crucial to the development of this feature. If you encounter any issues, have suggestions, or want to share how you're using the `clipboardchange` event, please: - -**Log an issue here:** [https://github.com/w3c/clipboard-apis/issues](https://github.com/w3c/clipboard-apis/issues) - -We look forward to hearing from you! - -## Further Reading and References - -* [Explainer Document](./clipboard-change-event-explainer.md) -* [Chrome Platform Status Entry](https://chromestatus.com/feature/5085102657503232) -* [W3C Specification Proposed Changes (PR)](https://github.com/w3c/clipboard-apis/pull/239) -* [W3C Specification (Current Editor's Draft)](https://w3c.github.io/clipboard-apis/#clipboard-event-clipboardchange) \ No newline at end of file diff --git a/ClipboardAPI/ClipboardChangeEvent/clipboard-change-event-example-app.html b/ClipboardAPI/ClipboardChangeEvent/clipboard-change-event-example-app.html deleted file mode 100644 index b7bae56fc..000000000 --- a/ClipboardAPI/ClipboardChangeEvent/clipboard-change-event-example-app.html +++ /dev/null @@ -1,117 +0,0 @@ - - - - - - - Clipboard Format Checker - - - - -

Clipboardchange event demo app - Paste formats viewer

-

- This HTML application demonstrates the use of the clipboardchange event to monitor - and display clipboard data in various formats. The app listens for changes to the - clipboard using the clipboardchange event. It then displays the clipboard data in a table - with columns for Text, Image, and HTML formats. -
- When the clipboard content changes, the app updates the table to show the current clipboard data. - The app includes buttons for pasting clipboard data as Text, Image, and HTML. - These buttons are initially disabled and are enabled based on the available clipboard formats. - -

-
- - - -
-
- - - - - - - - - - - - - - - -
TextImageHTML
N/AN/AN/A
- - - \ No newline at end of file diff --git a/ClipboardAPI/ClipboardChangeEvent/clipboard-change-event-explainer.md b/ClipboardAPI/ClipboardChangeEvent/clipboard-change-event-explainer.md deleted file mode 100644 index d3e5abead..000000000 --- a/ClipboardAPI/ClipboardChangeEvent/clipboard-change-event-explainer.md +++ /dev/null @@ -1,215 +0,0 @@ -# clipboardchange event API explainer - -## Authors: -- Rohan Raja (roraja@microsoft.com) - -## Participate -Feature request: [Async Clipboard: Add support for 'clipboardchange' event [41442253] - Chromium](https://issues.chromium.org/issues/41442253) -Spec: [Clipboard API and events (w3.org)](https://www.w3.org/TR/clipboard-apis/#clipboard-event-clipboardchange) - -## Table of Contents - - - - -- [1. Introduction](#1-introduction) -- [2. User scenarios](#2-user-scenarios) - - [2.1 Scenario: Show available paste formats in web based editors](#21-scenario-show-available-paste-formats-in-web-based-editors) - - [2.2.1 Copy multiple cells should show multiple paste options in Excel online](#221-copy-multiple-cells-should-show-multiple-paste-options-in-excel-online) - - [2.2.2 Copy plain text should show only single paste option in Excel online](#222-copy-plain-text-should-show-only-single-paste-option-in-excel-online) - - [2.2.3 Multiple paste options in Google sheets](#223-multiple-paste-options-in-google-sheets) - - [2.2 Scenario: Sync clipboard with a remote desktop](#22-scenario-sync-clipboard-with-a-remote-desktop) -- [3. Motivation - Alternative to inefficient polling of clipboard](#3-motivation---alternative-to-inefficient-polling-of-clipboard) -- [4. Proposed Approach](#4-proposed-approach) - - [4.1 Proposed IDL and example javascript code:](#41-proposed-idl-and-example-javascript-code) - - [4.1.1 IDL changes](#411-idl-changes) - - [4.1.2 Sample JS code](#412-sample-js-code) - - [4.2 Clipboard data types - Available in event payload](#42-clipboard-data-types---available-in-event-payload) - - [4.3 Clipboard contents - Not available in event payload](#43-clipboard-contents---not-available-in-event-payload) - - [4.4 Permissions and Interop - No user permission required](#44-permissions-and-interop---no-user-permission-required) - - [Pros](#pros) - - [4.5 Page focus requirement](#45-page-focus-requirement) - - [4.6 Event bubble up and cancellation](#46-event-bubble-up-and-cancellation) -- [5 Alternatives considered](#5-alternatives-considered) - - [5.1 Transient user activation requirement](#51-transient-user-activation-requirement) - - [Pros:](#pros) - - [Cons:](#cons) - - [5.2 API Signature alternate: Use DataTransfer object of ClipboardEvent class](#52-api-signature-alternate-use-datatransfer-object-of-clipboardevent-class) -- [6 Appendix](#6-appendix) - - [6.1 APIs provided by all OS to listen to clipboardchange event:](#61-apis-provided-by-all-os-to-listen-to-clipboardchange-event) - - [6.2 Permission prompt mechanism in various browsers](#62-permission-prompt-mechanism-in-various-browsers) - - [6.3 Reading clipboard contents within the clipboardchange event handler](#63-reading-clipboard-contents-within-the-clipboardchange-event-handler) - - [6.4 Custom clipboard data types and clipboardchange event](#64-custom-clipboard-data-types-and-clipboardchange-event) -- [7 Open issues](#7-open-issues) - - [7.1 Fencedframe](#71-fencedframe) -- [8 References & acknowledgements](#8-references--acknowledgements) - - - -## 1. Introduction - -The `clipboardchange` event aims to provide an efficient and secure way of notifying web applications about changes to the system clipboard. This allows web applications to provide rich user experiences like dynamic contextual menu options based on available clipboard MIME types and ability to efficiently sync clipboard in web based virtual desktop clients. - -Today, this can be achieved in Chromium by calling async clipboard read API in a polling approach (assuming clipboard-read permissions are granted) which is obviously inefficient. Also, polling is not feasible on [Firefox](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API) and [Safari](https://webkit.org/blog/10855/async-clipboard-api/) as these browsers rely on combination of user activation and user gesture for reading clipboard through async API. - -Hence `clipboardchange` event is being proposed. This event will be fired, in a secure manner by firing only when document is in focus and requiring that clipboard contents are not part of this event. - -## 2. User scenarios - -### 2.1 Scenario: Show available paste formats in web based editors -Web-based editors like Word Online, Excel Online, Google Sheets, and similar applications support paste operations in multiple formats, such as CSV, images, HTML, and plain text. These editors may have separate paste functionality depending on the data type which is pasted, hence the web UI might have different paste buttons for each data type. The clipboard change event can be used to detect the change in available formats in clipboard and reflect the same on the UI as soon as it is changed. - -**Example scenario with clipboardchange event**: Imagine a user working on a report in Word Online. They copy a table from Excel, which is available in multiple formats: plain text, HTML, and CSV. As soon as the user copies the table, the `clipboardchange` event fires, and Word Online's UI updates to show buttons for "Paste as Text," "Paste as HTML," and "Paste as CSV." The user can then choose the most suitable format for their report with a single click, streamlining their workflow. - -**Scenario without clipboardchange event**: If the user copies plain text then without clipboardchange notification, the web page would continue showing the "Paste as HTML" and "Paste as image" options. Clicking on "Paste as image" would require the web page to show some kind of error message. This unnecessary error scenario can be avoided by monitoring the clipboard and disabling the un-needed data type buttons upon clipboard change, which would prevent user clicking the invalid type button in the first place. - -#### 2.2.1 Copy multiple cells should show multiple paste options in Excel online -![](img/paste-format-1.png) - -#### 2.2.2 Copy plain text should show only single paste option in Excel online -![](img/paste-format-2.png) - -#### 2.2.3 Multiple paste options in Google sheets -![](img/google-sheets.png) - -### 2.2 Scenario: Sync clipboard with a remote desktop -When a user copies text or an image on their local machine, a web-based remote desktop application can detect that clipboard contents have changed by listening for the `clipboardchange` event. Upon detecting the change (which happens when 'clipboardchange' event is triggered on the web app when the page regains focus), the application can re-read the clipboard and send the updated clipboard content to the remote desktop environment. - -![](img/sync-clipboard-scenario.png) - - -## 3. Motivation - Alternative to inefficient polling of clipboard -Today, a web-app can monitor the system clipboard by polling and reading the clipboard through async clipboard API at regular intervals. However, polling is not efficient. Moreover, browsers like Firefox and Safari [require user gesture](#63-reading-clipboard-contents-within-the-clipboardchange-event-handler) for reading clipboard which makes polling infeasible in those browsers. This feature aims to introduce an efficient way of notifying web apps when clipboard changes which works in all browsers. -Additionally we must ensure that we monitor the clipboard only when absolutely required, that is, there is at least one document having required permissions and is listening to the clipboard change event. This will be described in design details. - -## 4. Proposed Approach - -### 4.1 Proposed IDL and example javascript code: - -#### 4.1.1 IDL changes -```typescript -interface ClipboardChangeEvent : Event { - readonly attribute FrozenArray types; -}; -``` - -#### 4.1.2 Sample JS code -```javascript - // Event handler for clipboardchange event which contains the data types present in clipboard - function onClipboardChanged(event) { - document.getElementById("text_paste_button").disabled = !(event.types.includes('text/plain')); - document.getElementById("html_paste_button").disabled = !(event.types.includes('text/html')); - document.getElementById("png_paste_button").disabled = !(event.types.includes('image/png')); - } - - navigator.clipboard.addEventListener("clipboardchange", onClipboardChanged); -``` - -A sample web application which demonstrates the usage of "clipboardchange" event for showing available paste formats for rich web editors [Scenario 2.2](#21-scenario-show-available-paste-formats-in-web-based-editors) can be found [here](./clipboard-change-event-example-app.html). - -### 4.2 Clipboard data types - Available in event payload - -The ClipboardChange event object will have a `types` member that lists all the available native formats available on the clipboard. [Custom formats](#64-custom-clipboard-data-types-and-clipboardchange-event) will not be included in this list. - -```typescript -interface ClipboardChangeEvent{ - types: Array; // MIME types available in the clipboard when the event was fired -} -``` -The `types` member can be used to detect available data types present on the clipboard and then reflect the same on the UI as per [this scenario](#21-scenario-show-available-paste-formats-in-web-based-editors). - -### 4.3 Clipboard contents - Not available in event payload - -This API doesn't intend to provide any user clipboard contents as part of the event payload. - -### 4.4 Permissions and Interop - No user permission required - -When fired, this API indicates that the clipboard has changed and provides the current MIME types present on the clipboard. Since the actual contents of the clipboard are not exposed, there is no need for user permissions. - -##### Pros -1.) Simpler user experience with no permission prompts / user gesture requirements. -2.) Provides interop out of the box without need to implement new permissions. - -### 4.5 Page focus requirement - -The clipboardchange event will not fire if the target document is not focused. If clipboard changes occur while the document is not in focus, a single clipboardchange event will be fired when the document comes back into focus. Historical clipboard change information will not be available, only the available types when the page gained focus will be included in the types member. - -### 4.6 Event bubble up and cancellation - -Since the clipboardchange event is not triggered by a user action and the event is not associated to any DOM element, hence this event doesn't bubble up and is not cancellable. - -## 5 Alternatives considered - -### 5.1 Transient user activation requirement -This approach allows the clipboardchange event to be fired for a short duration after the user loses page focus, such as up to 5 seconds. This ensures that clipboard changes occurring immediately after focus loss are still captured, enhancing user experience without compromising security. - -##### Pros: -1. Clipboard changes occurring immediately after the user loses focus are still captured, ensuring the web app can respond promptly when the user returns. -Example: A web app can pre-process clipboard data while the user is in another application, reducing wait time when the user comes back. -2. Limits the duration for which clipboard monitoring is allowed after focus loss, reducing the risk of prolonged unauthorized access. - -##### Cons: -1. The short duration might not be sufficient for some use cases where clipboard changes occur after the specified time. -2. Still requires monitoring for a brief period after focus loss, which could lead to resource usage if many pages implement this. -Example: Multiple tabs monitoring clipboard changes for 5 seconds could still cause a temporary spike in resource usage. - -### 5.2 API Signature alternate: Use DataTransfer object of ClipboardEvent class - -The clipboardchange event can be considered a [ClipboardEvent](https://www.w3.org/TR/clipboard-apis/#clipboard-event-interfaces) which includes a [DataTransfer](https://html.spec.whatwg.org/multipage/dnd.html#datatransfer) object as "clipboardData" property. This is similar to other clipboard related events like [cut](https://w3c.github.io/clipboard-apis/#clipboard-event-cut), [copy](https://w3c.github.io/clipboard-apis/#clipboard-event-copy) or [paste](https://w3c.github.io/clipboard-apis/#clipboard-event-paste) events. The clipboard types can be read using "clipboardData.types" property. However, methods to access actual clipboard data like "getData" won't be accessible. Calling these inaccessible methods would return an "undefined" or equivalent null value. - -```javascript - // Event handler for clipboardchange event which contains the data types present in clipboard - async function onClipboardChanged(event) { - const clipboardTypes = event.clipboardData.types; - document.getElementById("text_paste_button").disabled = !(clipboardTypes.includes('text/plain')); - } - - navigator.clipboard.addEventListener("clipboardchange", onClipboardChanged); -``` -One clear issue with this approach is that we are providing all the methods of DataTransfer API as part of this event even though only "types" property of DataTransfer is needed by this event. - -## 6 Appendix - -### 6.1 APIs provided by all OS to listen to clipboardchange event: - -| OS | API | -|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Windows | We can use the [AddClipboardFormatListener](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-addclipboardformatlistener) function (winuser.h) which posts a [WM_CLIPBOARDUPDATE](https://learn.microsoft.com/en-us/windows/win32/dataxchg/wm-clipboardupdate) message whenever the clipboard changes. | -| MacOS | No API provided, need to poll OS clipboard for changes | -| Linux | TBD | -| ChromeOS | TBD | -| Android / iOS | TBD | - -### 6.2 Permission prompt mechanism in various browsers - -Today browser engines have different approaches to clipboard API permissions. While Chromium has [this permissions model](https://github.com/w3c/clipboard-apis/blob/main/explainer.adoc#clipboard-permissions) for clipboard, [Firefox](https://developer.mozilla.org/en-US/docs/Web/API/Clipboard_API) and [Safari](https://webkit.org/blog/10855/async-clipboard-api/) rely on combination of user activation and user gesture for web pages to access user clipboard contents. This strict requirement is present because a user's clipboard contents are highly sensitive and can contain private data like passwords, security tokens. - -**Permission prompt in Chromium** -![](img/chrome-permission-prompt.png) - -**Permission prompt in Firefox** -![](img/firefox-paste-button.png) - -### 6.3 Reading clipboard contents within the clipboardchange event handler - -To get the changed clipboard data within the event handler, the [read](https://w3c.github.io/clipboard-apis/#dom-clipboard-read) or [readText](https://w3c.github.io/clipboard-apis/#dom-clipboard-readtext) methods of the [Async clipboard API](https://w3c.github.io/clipboard-apis/#async-clipboard-api) can be used, given the web page has sufficient permissions. Note that in browsers which don't have permission based access to clipboard (like Firefox), a call to async clipboard read might require user gesture like clicking paste tablet. In those browsers, web authors can instead show a "Sync" button on the UI, which can be enabled upon receiving clipboardchange event and disabled again once user clicks the "Sync" button. - -### 6.4 Custom clipboard data types and clipboardchange event - -Custom clipboard data types are not part of this event because if custom MIME types are exposed (without user consent) a web page can know which applications a user is working on providing fingerprinting surface for malicious sites. Moreover, not all browsers support custom clipboard data types. - -## 7 Open issues - -### 7.1 Fencedframe - -The clipboardchange event could be used as a communication channel between the host and the fencedframe, constituting a privacy threat. Hence the feasibility of this event within a fencedframe needs to be discussed. - -## 8 References & acknowledgements - -Many thanks for valuable feedback and advice from: - -- Luke Klimek (zgroza@chromium.org) -- Mike Jackson (mjackson@microsoft.com) -- Prashant Nevase (pnevase@microsoft.com) -- Rakesh Goulikar (ragoulik@microsoft.com) -- Sanket Joshi (sajos@microsoft.com) diff --git a/ClipboardAPI/ClipboardChangeEvent/img/chrome-permission-prompt.png b/ClipboardAPI/ClipboardChangeEvent/img/chrome-permission-prompt.png deleted file mode 100644 index 8cff30e39..000000000 Binary files a/ClipboardAPI/ClipboardChangeEvent/img/chrome-permission-prompt.png and /dev/null differ diff --git a/ClipboardAPI/ClipboardChangeEvent/img/firefox-paste-button.png b/ClipboardAPI/ClipboardChangeEvent/img/firefox-paste-button.png deleted file mode 100644 index 65fc195c9..000000000 Binary files a/ClipboardAPI/ClipboardChangeEvent/img/firefox-paste-button.png and /dev/null differ diff --git a/ClipboardAPI/ClipboardChangeEvent/img/google-sheets.png b/ClipboardAPI/ClipboardChangeEvent/img/google-sheets.png deleted file mode 100644 index 670997b0d..000000000 Binary files a/ClipboardAPI/ClipboardChangeEvent/img/google-sheets.png and /dev/null differ diff --git a/ClipboardAPI/ClipboardChangeEvent/img/paste-format-1.png b/ClipboardAPI/ClipboardChangeEvent/img/paste-format-1.png deleted file mode 100644 index e9e20f0b9..000000000 Binary files a/ClipboardAPI/ClipboardChangeEvent/img/paste-format-1.png and /dev/null differ diff --git a/ClipboardAPI/ClipboardChangeEvent/img/paste-format-2.png b/ClipboardAPI/ClipboardChangeEvent/img/paste-format-2.png deleted file mode 100644 index d00a085eb..000000000 Binary files a/ClipboardAPI/ClipboardChangeEvent/img/paste-format-2.png and /dev/null differ diff --git a/ClipboardAPI/ClipboardChangeEvent/img/sync-clipboard-scenario.png b/ClipboardAPI/ClipboardChangeEvent/img/sync-clipboard-scenario.png deleted file mode 100644 index 94f059e8d..000000000 Binary files a/ClipboardAPI/ClipboardChangeEvent/img/sync-clipboard-scenario.png and /dev/null differ diff --git a/ClipboardAPI/SelectiveClipboardFormatRead/experiment.html b/ClipboardAPI/SelectiveClipboardFormatRead/experiment.html deleted file mode 100644 index be25ccd58..000000000 --- a/ClipboardAPI/SelectiveClipboardFormatRead/experiment.html +++ /dev/null @@ -1,227 +0,0 @@ - - - - - Clipboard Performance Test - - - - -

Clipboard Performance Test

- -
-
-

How to use

-
    -
  1. Write only text to clipboard by clicking on "Write 1MB Text Only" button.
  2. -
  3. Read from clipboard by clicking on "Read from Clipboard" button.
  4. -
  5. Write text and HTML to clipboard by clicking on "Write 1MB Text + 3MB HTML" button.
  6. -
  7. Read from clipboard by clicking on "Read from Clipboard" button.
  8. -
  9. Read selected format from clipboard by clicking on "Selected Format Read from Clipboard" button.
  10. -
- -
- Note: Do not consider the results when clipboard read permission is asked for the first time. -
-
- - - -

Write Data to Clipboard

- - - -

Read Data from Clipboard

- - -

Selected Format Read

-
-
- -
- -
- -
- -
- - - -
- - - - - - - - - - - - - -
#TimeClipboard Read TypeRead Time (ms)
- -
- - - - - \ No newline at end of file diff --git a/ClipboardAPI/SelectiveClipboardFormatRead/experiment_styles.css b/ClipboardAPI/SelectiveClipboardFormatRead/experiment_styles.css deleted file mode 100644 index 11a1fc818..000000000 --- a/ClipboardAPI/SelectiveClipboardFormatRead/experiment_styles.css +++ /dev/null @@ -1,106 +0,0 @@ -body { - font-family: Arial, sans-serif; - max-width: 800px; - margin: 0 auto; - padding: 20px; -} - -.controls { - margin-bottom: 20px; - padding: 20px; - border: 1px solid #ddd; - background-color: #f5f5f5; - border-radius: 5px; -} - -button { - margin: 10px 5px; - padding: 10px 20px; - font-size: 16px; - background-color: #007cba; - color: white; - border: none; - border-radius: 5px; - cursor: pointer; -} - -button:hover { - background-color: #005a87; -} - -.results-table { - width: 100%; - border-collapse: collapse; - margin-top: 20px; -} - -.results-table th, -.results-table td { - border: 1px solid #ddd; - padding: 10px; - text-align: left; -} - -.results-table th { - background-color: #f2f2f2; - font-weight: bold; -} - -.results-table tr:nth-child(even) { - background-color: #f9f9f9; -} - -.log { - margin-top: 20px; - padding: 10px; - border: 1px solid #ccc; - background-color: #f9f9f9; - font-family: monospace; - height: 200px; - overflow-y: auto; -} - -.success { - color: green; -} - -.error { - color: red; -} - -.info { - color: blue; -} - -.how-to-use { - background-color: #e8f4f8; - border-left: 4px solid #007cba; - padding: 15px 20px; - margin-bottom: 20px; - border-radius: 5px; -} - -.how-to-use h3 { - margin-top: 0; - color: #005a87; - font-size: 18px; -} - -.how-to-use ol { - padding-left: 20px; - line-height: 1.6; -} - -.how-to-use li { - margin-bottom: 8px; - color: #333; -} - -.how-to-use .note { - background-color: #fff3cd; - border: 1px solid #ffeaa7; - border-radius: 4px; - padding: 10px; - margin-top: 15px; - font-size: 14px; -} \ No newline at end of file diff --git a/ClipboardAPI/SelectiveClipboardFormatRead/explainer.md b/ClipboardAPI/SelectiveClipboardFormatRead/explainer.md deleted file mode 100644 index 03d3521bc..000000000 --- a/ClipboardAPI/SelectiveClipboardFormatRead/explainer.md +++ /dev/null @@ -1,232 +0,0 @@ -# Selective Clipboard Format Read - -## Improving Paste Performance through Selective Clipboard Reads - -**Author:** [Abhishek Singh](https://github.com/abhishek06020) - -**Co-authors:** [Ashish Kumar](https://github.com/ashishkum-ms), [Rakesh Goulikar](https://github.com/ragoulik), [Rohan Raja](https://github.com/roraja), [Shweta Bindal](https://github.com/shwetabin) - -## Participate -- [Issue tracker](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/SelectiveClipboardFormatRead) -- [Open a new issue](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new?assignees=ragoulik&labels=SelectiveClipboardFormatRead&template=selective-clipboard-format-read.md&title=%5BSelective+Clipboard+Format+Read%5D+%3CTITLE+HERE%3E) - - - -## Table of Contents - -- [Introduction](#introduction) -- [User Problem](#user-problem) -- [Goals](#goals) -- [Non-Goals](#non-goals) -- [Proposal](#proposal) -- [Boundary Scenarios](#boundary-scenarios) -- [Pros](#pros) -- [Cons](#cons) -- [Considered Alternative: No API Signature Change but Defer Actual Read Until ClipboardItem.getType()](#considered-alternative-no-api-signature-change-but-defer-actual-read-until-clipboarditemgettype) - - [Pros of Alternate Approach](#pros-of-alternate-approach) - - [Cons of Alternate Approach](#cons-of-alternate-approach) -- [Accessibility, Privacy, and Security Considerations](#accessibility-privacy-and-security-considerations) -- [Appendix](#appendix) - - [Appendix 1: Proposed IDL](#appendix-1-proposed-idl) - - [Appendix 2: Read Time Analysis and Takeaways](#appendix-2-read-time-analysis-and-takeaways) -- [References and Acknowledgements](#references-and-acknowledgements) - - - -## Introduction - -This proposal introduces selective clipboard format reading, an enhancement to the [Asynchronous Clipboard read](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read) API that allows web applications to selectively read specific MIME types from the clipboard, making reads more efficient by avoiding retrieval of formats that are not needed. - -```js -// Example Javascript code -const items = await navigator.clipboard.read({ - types: ['text/plain', 'text/html'] // Specify mime types to be fetched from platform clipboard -}); -``` - -The current implementation of [navigator.clipboard.read()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read) copies all available clipboard formats from the operating system's clipboard into the browser's memory, regardless of what the web application needs. - -Letting web authors specify which formats to read (like only `["text/plain"]` or `["text/html"]`) in the [read()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read) API helps the browser avoid copying unnecessary data from the OS clipboard. This saves CPU cycles, improves perceived responsiveness in the API call, while also optimizing power usage by the browser. - -## User Problem - -Web applications that support rich content editing, such as document editors, email clients, and data grids, routinely deal with multiple types of clipboard payloads, including large HTML fragments, images, and custom MIME types. These apps often implement features like “Paste as plain text” or “Paste with formatting,” where only a subset of the clipboard data is needed. - -However, the current [navigator.clipboard.read()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read), that gets triggered by the paste options, indiscriminately fetches all available formats from the clipboard, regardless of what the application needs. This blanket behaviour adds significant overhead, especially when large data formats like HTML or images are present and are not required by the app. - -The impact is especially pronounced in large-scale web applications, such as online spreadsheets and document editors, that collectively handle hundreds of millions of paste interactions across their user base, where maintaining responsiveness during each operation is critical. Delays caused by fetching and discarding irrelevant clipboard data degrade user experience and add avoidable memory and CPU costs.(refer [Appendix 2](#appendix-2-read-time-analysis-and-takeaways) for an example read-time analysis demonstrating performance impact in a representative scenario) - -## Goals - -- Improve copy-paste responsiveness for large data by avoiding unnecessary reads, especially when only specific formats like plaintext or HTML are needed by web authors. -- Ensure interoperability across different platforms. - -## Non-Goals - -- Modifying clipboard writing or other clipboard APIs such as [readText()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-readtext). -- This proposal does not define any rules for how the browser should prioritize or rank different clipboard formats internally. - -## Proposal - -We propose API signature changes to the [clipboard.read()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read) API that allow web authors to specify the MIME types they intend to read. This browser implementation will selectively read only the requested formats, instead of reading all available data formats as is currently done. - -We propose to rename the optional argument [`ClipboardUnsanitizedFormats`](https://www.w3.org/TR/clipboard-apis/#dictdef-clipboardunsanitizedformats) of [read()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read) API to [`ClipboardReadOptions`](#appendix-1-proposed-idl) and extend this object to include a new `types` property which is a list of mime types to be retrieved. - -Existing implementations and web applications that use [navigator.clipboard.read()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read) will continue to behave as before when `types` is `undefined`, receiving all available clipboard formats. - -If a MIME type is provided in [`unsanitized`](https://www.w3.org/TR/clipboard-apis/#dom-clipboardunsanitizedformats-unsanitized) but not requested in `types`, the clipboard content for the provided type will not be read from the OS clipboard and hence will be unavailable in the clipboard read response. - -**Example: Selective reading of requested MIME types** -```js -// Scenario: OS clipboard contains 'text/plain' and 'text/html' data -const items = await navigator.clipboard.read({ - types: ['text/plain'] -}); - -const item = items[0]; -const availableTypes = item.types; // ['text/plain']. Note: Only available requested types are present. - -const plainTextBlob = await item.getType('text/plain'); -const text = await plainTextBlob.text(); -``` - -**Example: Reading unsanitized HTML** -```js -// Scenario: OS clipboard contains 'text/plain' and 'text/html' data -const items = await navigator.clipboard.read({ - types: ['text/html'], - unsanitized: ['text/html'] -}); - -const item = items[0]; -const availableTypes = item.types; // ['text/html'] - -const unsanitizedHtml = await item.getType('text/html'); -``` - -**Example: When types is undefined or empty** -```js -// Scenario: OS clipboard contains 'text/plain' and 'text/html' data - -// Example 1: Default behavior with no types -const items1 = await navigator.clipboard.read(); // or navigator.clipboard.read({types: undefined}); -const item1 = items1[0]; -const availableTypes1 = item1.types; // ['text/plain', 'text/html'] - -// Example 2: Behavior with empty types -const items2 = await navigator.clipboard.read({ - types: [] -}); -const item2 = items2[0]; -const availableTypes2 = item2.types; // [] -``` - -Please refer [Appendix 1](#appendix-1-proposed-idl) for the proposed IDL. - - -## Boundary Scenarios - -- The [types](https://www.w3.org/TR/clipboard-apis/#dom-clipboarditem-types) property of ClipboardItem will return only the intersection of the requested types and the types available in the system clipboard. If a particular type is requested in the input but not present in the platform clipboard or is invalid, then the [types](https://www.w3.org/TR/clipboard-apis/#dom-clipboarditem-types) value won’t include that format, and any call to [getType(type)](https://www.w3.org/TR/clipboard-apis/#dom-clipboarditem-gettype) for that format will result in a rejected promise with error message "The type was not found". This way, a web author can verify if a requested type is present in the platform clipboard. -- If multiple instances of the same format are provided in the request, the duplicates will be ignored and only one instance will be considered during processing. - -```js -// Scenario: OS clipboard contains 'text/plain' and 'text/html' data -const items = await navigator.clipboard.read({ - types: ['text/plain', 'text/javascript', 'text/plain'], - unsanitized: ['text/html'] -}); - -const item = items[0]; - -// Only returns types that were both requested AND available on clipboard -const availableTypes = item.types; // ['text/plain'] - -// ✅ Resolves successfully -const plainText = await item.getType('text/plain'); - -// ❌ Throws error: The type was not found -// Type is requested in the types filter but it is invalid -const jsText = await item.getType('text/javascript'); - -// ❌ Throws error: The type was not found -// Type is valid and available in OS clipboard but not requested in the types filter -const html = await item.getType('text/html'); -``` - -## Pros -- This approach is backward compatible. - -## Cons - -- Adding both `[types]` and `[unsanitized]` to [`ClipboardReadOptions`](#appendix-1-proposed-idl) may cause confusion about how they interact. - -## Considered Alternative: No API Signature Change but Defer Actual Read Until ClipboardItem.getType() - - -An alternative approach defers clipboard data retrieval from the OS until the web app explicitly calls [getType()](https://www.w3.org/TR/clipboard-apis/#dom-clipboarditem-gettype). In this model, [navigator.clipboard.read()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read) returns [ClipboardItem](https://www.w3.org/TR/clipboard-apis/#clipboarditem) objects listing available MIME types, but without the data. The browser fetches the requested data only when [getType(mimeType)](https://www.w3.org/TR/clipboard-apis/#dom-clipboarditem-gettype) is called, caching it to avoid repeated clipboard accesses for the same type. - -If the clipboard contents change between the call to [read()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read) and a subsequent call to [getType()](https://www.w3.org/TR/clipboard-apis/#dom-clipboarditem-gettype), the promise may be rejected in case the requested type is no longer present in the clipboard. This behaviour is consistent with the current Clipboard API semantics. - -```js -// No data is read from the clipboard after read call completes -const items = await navigator.clipboard.read(); // [text, img] -const item = items[0]; // text - -// returns all available data types present at the time of read call -const allTypes = item.types; // [text, img] - -const plainText = await item.getType('text/plain'); // Data is lazily fetched here -``` - -### Pros of Alternate Approach - -- Preserves the existing [read()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read) API shape (no API changes). -- Provides the details of the available MIME types on the Clipboard. - -### Cons of Alternate Approach - -- This approach is not backward compatible because [ClipboardItem](https://www.w3.org/TR/clipboard-apis/#clipboarditem) no longer stores Blob data at [read()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read) time. As a result, it can't be used as a persistent cache for clipboard contents like today, where [getType()](https://www.w3.org/TR/clipboard-apis/#dom-clipboarditem-gettype) reliably returns the same data without re-reading the system clipboard. -- Another drawback of this approach is that the behavior may feel unintuitive: calling [read()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read) suggests immediate clipboard access, but actual data retrieval is deferred until [getType()](https://www.w3.org/TR/clipboard-apis/#dom-clipboarditem-gettype) is invoked. -- Developers must anticipate potential latency when calling [getType()](https://www.w3.org/TR/clipboard-apis/#dom-clipboarditem-gettype) which contrasts with today’s expectation of immediate access. -- Clipboard state may change between [read()](https://www.w3.org/TR/clipboard-apis/#dom-clipboard-read) and [getType()](https://www.w3.org/TR/clipboard-apis/#dom-clipboarditem-gettype) calls, leading to promise being rejected with error message 'The type was not found'. - -## Accessibility, Privacy, and Security Considerations - -This proposal has no known impact on accessibility or privacy and does not alter the permission or security model of the Async Clipboard API ([navigator.clipboard](https://www.w3.org/TR/clipboard-apis/#clipboard)). A user gesture requirement (transient user activation) and existing async clipboard API security measures (focus document, permission prompts) will remain as they are. - - -## Appendix - -### Appendix 1: Proposed IDL -```webidl -[Exposed=Window] -interface Clipboard { - Promise> read(optional ClipboardReadOptions options = {}); -}; - -dictionary ClipboardReadOptions { - sequence types; // Optional: Filter returned clipboard items by MIME types - sequence unsanitized; // Optional: Request unsanitized data for specific MIME types -}; -``` - -### Appendix 2: Read Time Analysis and Takeaways - -We ran experiments simulating real-world clipboard usage to evaluate the performance impact of selectively reading specific clipboard formats. The results showed substantial improvements in the read time when applications read only the required formats instead of the entire clipboard. For example, in a scenario where the clipboard payload was 7.7 MB (comprising 0.7 MB of plain text and 7 MB of HTML), selectively reading just the text reduced the read time by 93%—from 179.5 ms down to 10.8 ms. - -As we scaled up the data size, we observed that read times increased proportionally with payload size, reinforcing that the benefits of selective reads become more significant with larger clipboard data. Moreover, the type of format had a notable impact on performance. HTML formats consistently exhibited higher read latencies compared to plain text, even when only slightly larger in size, likely due to additional processing like browser-side sanitization for security. Avoiding unnecessary HTML reads can deliver substantial latency improvements, especially in mixed-format clipboards where the application only needs text. - -**Reproducibility :** -For developers interested in reproducing these results or running similar benchmarks, we’ve published a minimal [experiment](./experiment.html) demonstrating Selective Clipboard Format Read and associated timing comparisons. -To use live demo, open [this](https://ashishkum-ms.github.io/cr-contributions/sfr/performace_experiment.html) in a browser that supports the Selective Clipboard Format Read. - -## References and Acknowledgements -References : -- [Github discussion](https://github.com/w3c/clipboard-apis/issues/191) -- [Clipboard APIs spec issue](https://github.com/w3c/clipboard-apis/issues/240) - -Many thanks for valuable feedback and advice from: -- [Anupam Snigdha](https://github.com/snianu) -- [Daniel Clark](https://github.com/dandclark) -- [Evan Stade](https://github.com/evanstade) -- [Sanket Joshi](https://github.com/sanketj) diff --git a/ClipboardAPI/tag-security-privacy-clipboard-supports.md b/ClipboardAPI/tag-security-privacy-clipboard-supports.md deleted file mode 100644 index 0cae3049d..000000000 --- a/ClipboardAPI/tag-security-privacy-clipboard-supports.md +++ /dev/null @@ -1,97 +0,0 @@ -### Questions from https://www.w3.org/TR/security-privacy-questionnaire/ - -## 2. Questions to Consider - -### 2.1. What information might this feature expose to Web sites or other parties, and for what purposes is that exposure necessary? - -The `supports` API detects whether a clipboard format type is supported or not. It doesn't interact with the system clipboard. It returns true if a Browser supports a clipboard format type, else, false. - -### 2.2. Do features in your specification expose the minimum amount of information necessary to enable their intended uses? - -Yes. - -### 2.3. How do the features in your specification deal with personal information, personally-identifiable information (PII), or information derived from them? - -No PII or any information derived from them is exposed via this API. - -### 2.4. How do the features in your specification deal with sensitive information? - -This feature doesn't deal with any sensitive information. - -### 2.5. Do the features in your specification introduce new state for an origin that persists across browsing sessions? - -The API detects the clipboard format types that are supported by the Browser. It is a static information so it persists across browsing sessions. However, it doesn't interact with the system clipboard, nor does it expose any data from the clipboard. - -### 2.6. Do the features in your specification expose information about the underlying platform to origins? - -It doesn't expose information about the underlying platform. - -### 2.7. Does this specification allow an origin to send data to the underlying platform? - -No. - -### 2.8. Do features in this specification enable access to device sensors? - -No. - -### 2.9. Do features in this specification enable new script execution/loading mechanisms? - -No. - -### 2.10. Do features in this specification allow an origin to access other devices? - -No. - -### 2.11. Do features in this specification allow an origin some measure of control over a user agent’s native UI? - -No. - -### 2.12. What temporary identifiers do the features in this specification create or expose to the web? - -None. - -### 2.13. How does this specification distinguish between behavior in first-party and third-party contexts? - -It doesn't distinguish between behavior in first-party and third-party contexts as it's a static information about supported clipboard format types. - -### 2.14. How do the features in this specification work in the context of a browser’s Private Browsing or Incognito mode? - -It works the same way in incognito mode. - -### 2.15. Does this specification have both "Security Considerations" and "Privacy Considerations" sections? - -N/A. - -### 2.16. Do features in your specification enable origins to downgrade default security protections? - -No. - -### 2.17. How does your feature handle non-"fully active" documents? - -No interaction with documents regardless of its state. - -### 2.18. What should this questionnaire have asked? - -N/A - -## 3. Threat Models - -### 3.1. Passive Network Attackers - -No threat. - -### 3.2. Active Network Attackers - -The API is only available from a secure context. - -### 3.3. Same-Origin Policy Violations - -It doesn't leak data across origins. - -### 3.4. Third-Party Tracking - -No interaction with third-party pages. - -### 3.5. Legitimate Misuse - -We don't think there is any risk of legitimate misuse of this API. diff --git a/ClipboardAPI/tag-security-privacy-clipboard-svg.md b/ClipboardAPI/tag-security-privacy-clipboard-svg.md deleted file mode 100644 index 1d52d627b..000000000 --- a/ClipboardAPI/tag-security-privacy-clipboard-svg.md +++ /dev/null @@ -1,97 +0,0 @@ -### Questions from https://www.w3.org/TR/security-privacy-questionnaire/ - -## 2. Questions to Consider - -### 2.1. What information might this feature expose to Web sites or other parties, and for what purposes is that exposure necessary? - -This feature exposes the `image/svg+xml` MIME type to the clipboard using the async clipboard API to support copy-paste of SVG images. All the restrictions related to secure context, permission etc apply for reading this format. During `write()`, the format is parsed using the `DOMParser` that uses an XML parser to return a well-formed SVG document. During `read()`, `image/svg+xml` is strictly processed by-default using the HTML fragment parser that inlines styles and also strips out security-sensitive tags. If `image/svg+xml` is in the [`unsanitized`](https://w3c.github.io/clipboard-apis/#dom-clipboardunsanitizedformats-unsanitized) list, then it's returned without any processing. - -### 2.2. Do features in your specification expose the minimum amount of information necessary to enable their intended uses? - -Yes. - -### 2.3. How do the features in your specification deal with personal information, personally-identifiable information (PII), or information derived from them? - -No PII information is exposed in this new clipboard format support. - -### 2.4. How do the features in your specification deal with sensitive information? - -This feature doesn't deal with any sensitive information. - -### 2.5. Do the features in your specification introduce new state for an origin that persists across browsing sessions? - -No. - -### 2.6. Do the features in your specification expose information about the underlying platform to origins? - -No. - -### 2.7. Does this specification allow an origin to send data to the underlying platform? - -Yes. This exposes a new `image/svg+xml` MIME type to the clipboard using the existing async clipboard API. The content is processed using the `DOMParser` that uses an XML parser to return a well-formed SVG document before it gets written to the clipboard. - -### 2.8. Do features in this specification enable access to device sensors? - -No. - -### 2.9. Do features in this specification enable new script execution/loading mechanisms? - -No. - -### 2.10. Do features in this specification allow an origin to access other devices? - -No. - -### 2.11. Do features in this specification allow an origin some measure of control over a user agent’s native UI? - -No. - -### 2.12. What temporary identifiers do the features in this specification create or expose to the web? - -None. - -### 2.13. How does this specification distinguish between behavior in first-party and third-party contexts? - -It doesn't distinguish between behavior in first-party and third-party contexts as the async clipboard APIs already have restrictions via Permissions. - -### 2.14. How do the features in this specification work in the context of a browser’s Private Browsing or Incognito mode? - -It works the same way in incognito mode. - -### 2.15. Does this specification have both "Security Considerations" and "Privacy Considerations" sections? - -Yes. https://docs.google.com/document/d/1jq8QSCQRdNy99rnPusmW8is62c22PVuq-Sk-tMT2tRk/edit?usp=sharing. - -### 2.16. Do features in your specification enable origins to downgrade default security protections? - -No. - -### 2.17. How does your feature handle non-"fully active" documents? - -No interaction with documents regardless of its state. Web authors have to provide the SVG image content via the async clipboard APIs. - -### 2.18. What should this questionnaire have asked? - -N/A - -## 3. Threat Models - -### 3.1. Passive Network Attackers - -No threat. - -### 3.2. Active Network Attackers - -The API is only available from a secure context. - -### 3.3. Same-Origin Policy Violations - -It doesn't leak data across origins. - -### 3.4. Third-Party Tracking - -No interaction with third-party pages. - -### 3.5. Legitimate Misuse - -We don't think there is any risk of legitimate misuse of this API. Browser parses the SVG image string provided by the web author before the parsed content is written to the system clipboard. The target apps that reads this data will not be at risk of parsing malformed SVG images. diff --git a/ClipboardAPI/tag-security-privacy-clipboard-unsanitized-read.md b/ClipboardAPI/tag-security-privacy-clipboard-unsanitized-read.md deleted file mode 100644 index ec8089d6c..000000000 --- a/ClipboardAPI/tag-security-privacy-clipboard-unsanitized-read.md +++ /dev/null @@ -1,99 +0,0 @@ -### Questions from https://www.w3.org/TR/security-privacy-questionnaire/ - -## 2. Questions to Consider - -### 2.1. What information might this feature expose to Web sites or other parties, and for what purposes is that exposure necessary? - -This feature exposes unsanitized HTML to the clipboard using the async clipboard API. This information is already exposed by the existing DataTransfer API, so it's not something new that we're exposing with this feature. -Sites have to opt into reading the unsanitized HTML content, else, they will get sanitized HTML by-default. To read unsanitized HTML via the async clipboard API, all the restrictions related to secure context, permission etc apply. - -### 2.2. Do features in your specification expose the minimum amount of information necessary to enable their intended uses? - -Yes. - -### 2.3. How do the features in your specification deal with personal information, personally-identifiable information (PII), or information derived from them? - -This API doesn't expose any new information that can't already be accessed via existing DataTransfer clipboard APIs. - -### 2.4. How do the features in your specification deal with sensitive information? - -This feature doesn't deal with any sensitive information. - -### 2.5. Do the features in your specification introduce new state for an origin that persists across browsing sessions? - -No. - -### 2.6. Do the features in your specification expose information about the underlying platform to origins? - -No. - -### 2.7. Does this specification allow an origin to send data to the underlying platform? - -No. - -### 2.8. Do features in this specification enable access to device sensors? - -No. - -### 2.9. Do features in this specification enable new script execution/loading mechanisms? - -No. - -### 2.10. Do features in this specification allow an origin to access other devices? - -No. - -### 2.11. Do features in this specification allow an origin some measure of control over a user agent’s native UI? - -No. - -### 2.12. What temporary identifiers do the features in this specification create or expose to the web? - -None. - -### 2.13. How does this specification distinguish between behavior in first-party and third-party contexts? - -It doesn't distinguish between behavior in first-party and third-party contexts as the async clipboard APIs already have restrictions via Permissions. - -### 2.14. How do the features in this specification work in the context of a browser’s Private Browsing or Incognito mode? - -It works the same way in incognito mode. - -### 2.15. Does this specification have both "Security Considerations" and "Privacy Considerations" sections? - -Yes. https://github.com/w3c/editing/blob/gh-pages/docs/clipboard-unsanitized/explainer.md#privacy-and-security. - -### 2.16. Do features in your specification enable origins to downgrade default security protections? - -No. - -### 2.17. How does your feature handle non-"fully active" documents? - -No interaction with documents regardless of its state. Web authors have to provide the HTML content via the async clipboard APIs. - -### 2.18. What should this questionnaire have asked? - -N/A - -## 3. Threat Models - -### 3.1. Passive Network Attackers - -No threat. - -### 3.2. Active Network Attackers - -The API is only available from a secure context. - -### 3.3. Same-Origin Policy Violations - -It doesn't leak data across origins. - -### 3.4. Third-Party Tracking - -No interaction with third-party pages. - -### 3.5. Legitimate Misuse - -We don't think there is any risk of legitimate misuse of this API. Browser parses the HTML string provided by the web author and creates a well-formed HTML -document before it writes to the system clipboard. The target apps that reads this data will not be at risk of parsing malformed HTML. diff --git a/ClipboardPickle/HOWTO.md b/ClipboardPickle/HOWTO.md deleted file mode 100644 index 5677df3a5..000000000 --- a/ClipboardPickle/HOWTO.md +++ /dev/null @@ -1,40 +0,0 @@ -# How to use the Async Clipboard API to read and write unsanitized HTML -**Last updated: March, 2023** - -Reading and writing unsanitized HTML to and from the clipboard is currently available in Chromium-based browsers in 113 and later behind the flag `ClipboardUnsanitizedContent`. -1. Download Microsoft Edge ([Canary Channel](https://www.microsoftedgeinsider.com/en-us/download/canary)). -2. Launch Edge with the command line flag `--enable-blink-features=ClipboardUnsanitizedContentNavigate`. - -## Example - -The write method doesn't change it's shape: -```javascript -const textInput = '

Hello, World!

'; -const blobInput = new Blob([textInput], { type: 'text/html' }); -const clipboardItem = new ClipboardItem({ 'text/html': blobInput }); -await navigator.clipboard.write([clipboardItem]); -``` - -Writing unsanitized HTML to the clipboard: -```html -

Hello, World!

-``` - -For reference, this would be the sanitized output: -```html -

Hello, World!

-``` - -The read method now accepts a dictionary with the `unsanitized` keyword and the `text/html` MIME type. -```javascript -const clipboardItems = await navigator.clipboard.read({ unsanitized: ['text/html'] }); -const blobOutput = await clipboardItems[0].getType('text/html'); -const outputHtml = await (new Response(blobOutput)).text(); -``` - -Reading unsanitized HTML to the clipboard: -```html -

Hello, World!

-``` - -This example in full can be found in https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/ClipboardPickle/unsanitized-html-demo.html diff --git a/ClipboardPickle/tag-security-privacy.md b/ClipboardPickle/tag-security-privacy.md deleted file mode 100644 index 874ceb043..000000000 --- a/ClipboardPickle/tag-security-privacy.md +++ /dev/null @@ -1,91 +0,0 @@ -### Questions from https://www.w3.org/TR/security-privacy-questionnaire/ - -## 2. Questions to Consider - -### 2.1. What information might this feature expose to Web sites or other parties, and for what purposes is that exposure necessary? - -This feature introduces custom clipboard formats with unsanitized content that will be exposed to both native apps and websites. Websites or native apps need to explicitly opt-in to consume these formats which will mitigate the concerns about remote code execution in legacy apps. The existing Web Platform has an API that supports the most popular standardized data types (text, image, rich text) across all platforms. However, it does not scale to the long tail of specialized formats. In particular, custom formats, non-web-standard formats like TIFF (a large image format), and proprietary formats like .docx (a document format), are not supported by the current Web Platform. Pickling for Async Clipboard API aims to provide a solution to this problem, by letting web applications read and write custom, unsanitized, web-originated payloads using a standardized pickling format. - -### 2.2. Is this specification exposing the minimum amount of information necessary to power the feature? - -Yes, this information is necessary to enable the native apps to communicate with the websites through clipboard copy-paste and vice versa. Thus, providing a much better paste experience on the web for contents with complex formatting info such as Excel tables, MS Word formatting, Figma content etc. - -### 2.3. How does this specification deal with personal information or personally-identifiable information or information derived thereof? - -Through the custom clipboard formats, PII may be transferable from web to native apps or vice versa. However, the content in the custom format may be less visible/obvious to the users. This is also true for the existing DataTransfer APIs that expose unsanitized HTML content in the standard HTML format(via setData/getData methods), but there may be metadata present in the custom format that wouldn't be typically included in the HTML format. The parsing rules for the custom format content and what data is included in the format, have to be defined by the native and web apps that read/write this format, so that alleviates some privacy concerns regarding who can read the sensitive data (if present) in the custom formats. -This feature also adds a user gesture requirement on top of existing async clipboard API security measures. More details are [available here](https://github.com/w3c/editing/blob/gh-pages/docs/clipboard-pickling/explainer.md#user-gesture-requirement) in the explainer. - -### 2.4. How does this specification deal with sensitive information? - -Same as PII. - -### 2.5. Does this specification introduce new state for an origin that persists across browsing sessions? - -By design the contents of the clipboard do persist across browsing sessions until the user overwrites the content by copying from another application. There is no long term persistence or ability to read without permission or user gestures, however, that would allow reliable user tracking to be implemented based on the contents of the clipboard. - -### 2.6. What information from the underlying platform, e.g. configuration data, is exposed by this specification to an origin? - -As native applications adopt pickled format names to increase their level of interop with the web, sites that are given permission to read the clipboard will see formats that might help reveal what application the user copied content from. - -### 2.7. Does this specification allow an origin access to sensors on a user’s device - -No. - -### 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. - -Unsanitized data that authors have put on the clipboard will be exposed to sites that have been granted permission to read the clipboard. -Custom formats are currently supported by all major browsers, but not in a standardized way. This proposal is standardizing the way the content is represented on the clipboard to increase interop between browser implementations, in addition to introducing a mechanism so that well known formats like HTML can simultaneously exist as a custom format and a sanitized format. - -### 2.9. Does this specification enable new script execution/loading mechanisms? - -No. - -### 2.10. Does this specification allow an origin to access other devices? - -No. - -### 2.11. Does this specification allow an origin some measure of control over a user agent’s native UI? - -No. - -### 2.12. What temporary identifiers might this specification create or expose to the web? - -None. - -### 2.13. How does this specification distinguish between behavior in first-party and third-party contexts? - -It follows the same access restrictions as any other Async clipboard APIs: such as focused document, permission prompts. User activation is added as a requirement. - -### 2.14. How does this specification work in the context of a user agent’s Private \ Browsing or "incognito" mode? - -Pickling Clipboard APIs should work the same way in incognito mode. - -### 2.15. Does this specification have a "Security Considerations" and "Privacy Considerations" section? - -Yes. Security Considerations are [here](https://www.w3.org/TR/clipboard-apis/#security), and Privacy Considerations are [here](https://www.w3.org/TR/clipboard-apis/#privacy). - -### 2.16. Does this specification allow downgrading default security characteristics? - -No. - -## 3. Threat Models - -### 3.1. Passive Network Attackers - -No new information visible to a passive network attacker is exposed by custom pickled clipboard formats. - -### 3.2. Active Network Attackers - -The API is only available from a secure context. - -### 3.3. Same-Origin Policy Violations - -The data present in the clipboard can be accessed by any origin, but it must adhere to the existing security restrictions of the async clipboard APIs which involves user activation, active document requirement, permission granted explicitly by the user etc. - -### 3.4. Third-Party Tracking - -Clipboard contents are transient and don't contain a persistent value that could be used for tracking. Additionally, the contents of the clipboard cannot be read without permission, which also prevents sites from using clipboard content for tracking purposes. - -### 3.5. Legitimate Misuse - -Risk of legitimate misuse seems low given the security measures put in place and the contents of the clipboard being available because the user intends to share the data between applications. \ No newline at end of file diff --git a/ClipboardPickle/unsanitized-html-demo.html b/ClipboardPickle/unsanitized-html-demo.html deleted file mode 100644 index df1f4a862..000000000 --- a/ClipboardPickle/unsanitized-html-demo.html +++ /dev/null @@ -1,33 +0,0 @@ - - -
- - -
-
- - - diff --git a/ConditionalMarkersExposure/explainer.md b/ConditionalMarkersExposure/explainer.md deleted file mode 100644 index e27be2499..000000000 --- a/ConditionalMarkersExposure/explainer.md +++ /dev/null @@ -1,189 +0,0 @@ -# JavaScript Self-Profiling API: Conditional Marker Exposure - -## Table of Contents -1. [Introduction](#introduction) -2. [Problem Statement](#problem-statement) -3. [Proposed Solution](#proposed-solution) -4. [Behavioral Changes](#behavioral-changes) -5. [Security Analysis](#security-analysis) -6. [API Impact](#api-impact) -7. [Examples](#examples) -8. [Compatibility](#compatibility) - -## Introduction - -This explainer describes a proposed modification to the JavaScript Self-Profiling API that enables conditional exposure of `ProfilerMarker` values based on context isolation status. The change allows selective disclosure of markers based on their security sensitivity, improving developer experience while maintaining security boundaries. - -## Problem Statement - -### Current Limitation - -The current JavaScript Self-Profiling API markers feature has some limitations: - -- **All-or-nothing approach**: Markers are either completely available in Cross-Origin Isolated contexts or completely unavailable in non-isolated contexts -- **Overly restrictive**: Safe markers that don't pose security risks are unnecessarily gated behind Cross-Origin Isolation -- **Limited flexibility**: No mechanism for selective marker exposure based on security sensitivity - -### Goals - -- Enable selective marker exposure based on security sensitivity -- Allow safe markers (`layout`, `style`) in non-isolated contexts -- Maintain explicit control over marker feature availability -- Preserve security boundaries while improving developer experience - -## Proposed Solution - -### Key Changes - -1. **Graduated disclosure**: Allow certain safe markers (`layout`, `style`) in non-isolated contexts -2. **Security-based filtering**: Continue to restrict sensitive markers (`gc`, `paint`, `script`) to Cross-Origin Isolated contexts -3. **Transparent operation**: No changes to the API surface; filtering happens internally - -### Rationale - -Layout and style state information is already exposed through other browser APIs (DOM APIs and CSSOM), making these markers safe for broader availability without introducing new security risks. - -## Behavioral Changes - -| Context Type | Feature Flag | Available Markers | -|-------------|-------------|-------------------| -| Cross-Origin Isolated | Enabled | All markers (`gc`, `layout`, `paint`, `script`, `style`) | -| Cross-Origin Isolated | Disabled | None | -| Non-Isolated | Enabled | Limited (`layout`, `style` only) | -| Non-Isolated | Disabled | None | - -## Security Analysis - -### Safe Markers in Non-Isolated Contexts - -- **`layout`**: Layout state information is already available via DOM APIs such as `getBoundingClientRect()`, `getComputedStyle()`, and layout-triggering operations -- **`style`**: Style computation state is accessible through CSSOM APIs and style recalculation is observable via existing timing mechanisms - -### Restricted Markers - -The following markers remain restricted to Cross-Origin Isolated contexts due to potential security implications: - -- **`gc`**: Garbage collection timing could leak memory patterns and cross-origin resource information -- **`paint`**: Paint timing might reveal information about cross-origin opaque resources that wouldn't pass Timing-Allow-Origin checks -- **`script`**: Script execution state could expose sensitive timing information about cross-origin activities - -### Security Model - -This approach follows the principle of graduated disclosure, where: -1. Information already available through other APIs can be safely exposed -2. Sensitive timing information requires explicit Cross-Origin Isolation opt-in -3. The overall feature remains controlled by user agent implementation - -## API Impact - -### For Developers - -The API surface remains unchanged from a developer perspective. The conditional exposure happens transparently: - -```javascript -// Cross-origin isolated context -const profiler = new Profiler({sampleInterval: 10, maxBufferSize: 1000}); -const trace = await profiler.stop(); -// trace.samples may contain all marker types - -// Non-isolated context -const profiler = new Profiler({sampleInterval: 10, maxBufferSize: 1000}); -const trace = await profiler.stop(); -// trace.samples may contain layout/style markers only -``` - -### Trace Format - -No changes to trace structure. The `marker` field in samples will be: -- Present with full marker data (Cross-Origin Isolated contexts) -- Present with filtered marker data (non-isolated contexts) -- Absent (feature disabled or no marker available) - -## Examples - -### Cross-Origin Isolated Context - -```javascript -// Set required headers for Cross-Origin Isolation: -// Cross-Origin-Embedder-Policy: require-corp -// Cross-Origin-Opener-Policy: same-origin - -const profiler = new Profiler({sampleInterval: 5, maxBufferSize: 1000}); - -// Trigger various browser activities -document.body.style.color = 'red'; // Style recalculation -document.body.offsetHeight; // Layout -new Array(1000000); // Potential GC - -const trace = await profiler.stop(); - -// Sample output may include all marker types: -console.log(trace.samples); -// [ -// { timestamp: 1234.5, stackId: 2, marker: "gc" }, -// { timestamp: 1235.0, stackId: 3, marker: "layout" }, -// { timestamp: 1235.5, stackId: 1, marker: "style" }, -// { timestamp: 1236.0, stackId: 4, marker: "paint" } -// ] -``` - -### Non-Isolated Context - -```javascript -// Regular context (no special headers required) - -const profiler = new Profiler({sampleInterval: 5, maxBufferSize: 1000}); - -// Trigger various browser activities -document.body.style.color = 'blue'; // Style recalculation -document.body.offsetHeight; // Layout -new Array(1000000); // Potential GC (won't be exposed) - -const trace = await profiler.stop(); - -// Sample output with filtered markers: -console.log(trace.samples); -// [ -// { timestamp: 1234.5, stackId: 2 }, // gc marker filtered out -// { timestamp: 1235.0, stackId: 3, marker: "layout" }, // allowed -// { timestamp: 1235.5, stackId: 1, marker: "style" }, // allowed -// { timestamp: 1236.0, stackId: 4 } // paint marker filtered out -// ] -``` - -### Feature Disabled - -```javascript -const profiler = new Profiler({sampleInterval: 5, maxBufferSize: 1000}); -const trace = await profiler.stop(); - -// No markers regardless of context isolation status: -console.log(trace.samples); -// [ -// { timestamp: 1234.5, stackId: 2 }, -// { timestamp: 1235.0, stackId: 3 }, -// { timestamp: 1235.5, stackId: 1 } -// ] -``` - -## Compatibility - -### Backward Compatibility - -- **No breaking changes**: Existing code continues to work without modification -- **Progressive enhancement**: Non-isolated contexts gain access to layout/style markers -- **Consistent behavior**: Cross-Origin Isolated contexts maintain full marker access - -### Forward Compatibility - -- **Extensible model**: Framework ready for additional "safe" markers in the future -- **Clear categorization**: Established security model for evaluating new marker types -- **Implementer guidance**: Clear criteria for determining marker safety - -## References - -- [JavaScript Self-Profiling API Specification](https://wicg.github.io/js-self-profiling/) -- [Cross-Origin Isolation](https://html.spec.whatwg.org/multipage/webappapis.html#concept-settings-object-cross-origin-isolated-capability) -- [Performance Timeline API](https://w3c.github.io/performance-timeline/) -- [TAG Security Review Discussion](https://github.com/w3ctag/design-reviews/issues/682) -- [Element Timing Security Considerations](https://wicg.github.io/element-timing/#sec-security) diff --git a/ConditionalMarkersExposure/implementation.md b/ConditionalMarkersExposure/implementation.md deleted file mode 100644 index 45db286e0..000000000 --- a/ConditionalMarkersExposure/implementation.md +++ /dev/null @@ -1,155 +0,0 @@ -# Conditional Marker Exposure Implementation - -## Overview - -This design document describes the implementation requirements for conditional marker exposure in the JavaScript Self-Profiling API. - -## Key Implementation Requirements - -### 1. IDL Changes - -Remove the `CrossOriginIsolated` attribute from the `ProfilerSample` dictionary: - -```diff -dictionary ProfilerSample { - required DOMHighResTimeStamp timestamp; - unsigned long long stackId; -- [RuntimeEnabled=ExperimentalJSProfilerMarkers, CrossOriginIsolated] ProfilerMarker marker; -+ [RuntimeEnabled=ExperimentalJSProfilerMarkers] ProfilerMarker marker; -}; -``` - -### 2. New Method: `GetMarker()` - -Implement the core logic in a new `GetMarker()` method in `ProfilerTraceBuilder`: - -```cpp -std::optional ProfilerTraceBuilder::GetMarker( - const v8::EmbedderStateTag embedder_state, - const v8::StateTag fallback_state) { - - // 1. Check feature flag - const bool are_markers_enabled = - RuntimeEnabledFeatures::ExperimentalJSProfilerMarkersEnabled(); - if (!are_markers_enabled) { - return std::nullopt; - } - - // 2. Get marker from state - std::optional marker = - BlinkStateToMarker(embedder_state, fallback_state); - if (!marker.has_value()) { - return marker; - } - - // 3. Apply security filtering for non-isolated contexts - ExecutionContext* execution_context = ExecutionContext::From(script_state_); - const bool is_cross_origin_isolated = - execution_context->CrossOriginIsolatedCapabilityOrDisabledWebSecurity(); - if (!is_cross_origin_isolated) { - marker = ProfileMarkerToPublicMarker(*marker); - } - - return marker; -} -``` - -### 3. Filtering Logic - -Implement `ProfileMarkerToPublicMarker()` to filter sensitive markers: - -```cpp -inline std::optional ProfileMarkerToPublicMarker( - const V8ProfilerMarker marker) { - switch (marker.AsEnum()) { - case V8ProfilerMarker::Enum::kStyle: - case V8ProfilerMarker::Enum::kLayout: - return marker; // Allow these in non-isolated contexts - default: - return std::nullopt; // Filter out everything else - } -} -``` - -### 4. Integration Point - -Replace the direct call to `BlinkStateToMarker` with the new `GetMarker()` method: - -```cpp -// Before: -if (std::optional marker = - BlinkStateToMarker(embedder_state, state)) { - sample->setMarker(*marker); -} - -// After: -if (std::optional marker = - GetMarker(embedder_state, state)) { - sample->setMarker(*marker); -} -``` - -## Security Model - -**Safe markers (allowed in non-isolated contexts):** -- `style`: Style computation state is accessible through CSSOM APIs -- `layout`: Layout state information is already available via DOM APIs (getBoundingClientRect, etc.) - -**Restricted markers (require Cross-Origin Isolation):** -- `gc`: Garbage collection timing could leak memory patterns and cross-origin resource information -- `paint`: Paint timing might reveal information about cross-origin opaque resources -- `script`: Script execution state could expose sensitive timing information about cross-origin activities - -## Implementation Verification - -### Test Coverage - -The implementation includes comprehensive test coverage for all scenarios: - -```cpp -// Test isolated context - all markers available -TEST_F(ProfilerTraceBuilderTest, AddVMStateMarkerCrossOriginIsolated) { - // Enable feature and set cross-origin isolation - // Verify all marker types are exposed -} - -// Test non-isolated context - filtered markers only -TEST_F(ProfilerTraceBuilderTest, AddEmbedderStateMarker) { - // Default non-isolated context - // Verify only kStyle and kLayout markers are exposed -} - -// Test feature disabled -TEST_F(ProfilerTraceBuilderTest, AddEmbedderStateMarkerFeatureDisabled) { - // Disable ExperimentalJSProfilerMarkersEnabled - // Verify no markers are exposed regardless of context -} -``` - -### Behavioral Verification - -Second implementers can verify correct behavior with these test patterns: - -**Cross-Origin Isolated Context:** -```javascript -// Headers: Cross-Origin-Embedder-Policy: require-corp -// Cross-Origin-Opener-Policy: same-origin -const trace = await profiler.stop(); -// Should expose all marker types: gc, layout, paint, script, style -``` - -**Non-Isolated Context:** -```javascript -// Regular context (no special headers) -const trace = await profiler.stop(); -// Should expose only: layout, style markers -// Should filter out: gc, paint, script markers -``` - -**Feature Disabled:** -```javascript -// ExperimentalJSProfilerMarkersEnabled = false -const trace = await profiler.stop(); -// Should not expose any markers regardless of isolation status -``` - diff --git a/ContextualLoggingWithConsoleContext/console-sidebar-with-context-filters.png b/ContextualLoggingWithConsoleContext/console-sidebar-with-context-filters.png deleted file mode 100644 index 139035606..000000000 Binary files a/ContextualLoggingWithConsoleContext/console-sidebar-with-context-filters.png and /dev/null differ diff --git a/ContextualLoggingWithConsoleContext/console-with-context-logs-badges.png b/ContextualLoggingWithConsoleContext/console-with-context-logs-badges.png deleted file mode 100644 index bfb6b1f4c..000000000 Binary files a/ContextualLoggingWithConsoleContext/console-with-context-logs-badges.png and /dev/null differ diff --git a/ContextualLoggingWithConsoleContext/console-with-context-logs-filtered.png b/ContextualLoggingWithConsoleContext/console-with-context-logs-filtered.png deleted file mode 100644 index bcf9ead29..000000000 Binary files a/ContextualLoggingWithConsoleContext/console-with-context-logs-filtered.png and /dev/null differ diff --git a/ContextualLoggingWithConsoleContext/explainer.md b/ContextualLoggingWithConsoleContext/explainer.md deleted file mode 100644 index d57388e11..000000000 --- a/ContextualLoggingWithConsoleContext/explainer.md +++ /dev/null @@ -1,205 +0,0 @@ -# DevTools contextual logging with `console.context()` - -## Authors - - - *[Leah Tu](https://github.com/leahmsft)*, Microsoft Edge - - *[Patrick Brosset](https://github.com/captainbrosset)*, Microsoft Edge - -## Participate - -- For feedback about this explainer, open [an issue on the MSEdgeExplainers repo](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new?template=contextual-logging-with-console-context.md). -- The expected venue for this is the [Console spec](https://console.spec.whatwg.org/). For feedback about the spec changes, see [console.context()](https://github.com/whatwg/console/issues/193) on the spec's repo. - -## Table of Contents - -* [Status of this feature](#status-of-this-feature) -* [Introduction](#introduction) -* [User-facing problem](#user-facing-problem) -* [Goals](#goals) - * [Non-goals](#non-goals) -* [Proposed approach](#proposed-approach) - * [Current experience in Chromium](#current-experience-in-chromium) - * [Proposed improvements](#proposed-improvements) - * [Changes to the `console.context()` method](#changes-to-the-consolecontext-method) - * [Changes to the Console tool UI](#changes-to-the-console-tool-ui) -* [Alternatives considered](#alternatives-considered) -* [Accessibility, privacy, and security considerations](#accessibility-privacy-and-security-considerations) -* [Stakeholder feedback / opposition](#stakeholder-feedback--opposition) -* [References & acknowledgements](#references--acknowledgements) - -## Status of this feature - -An initial version of this feature has been available in [Chromium](https://chromium-review.googlesource.com/c/v8/v8/+/522128) since 2017, and Microsoft Edge starting with version 79. This explainer proposes improvements to the feature. - -## Introduction - -This project aims to enhance the debugging process for large web applications by improving Chromium's `console.context()` method and enhancing filter options in the Console UI. This method allows developers to define logging contexts, making it easier to filter and identify log messages. Our improvements include introducing an optional color argument to `console.context()` for quick visual differentiation and updating the Console UI with context badges and additional filter support. These changes will make debugging more efficient, improve log readability, and simplify contextual logger creation. - -## User-facing problem - -When debugging web applications with large code bases having many components from many different teams, it can be challenging for developers to filter through the many log messages that appear in the Console tool and find the relevant ones. Often, developers want to filter the messages so that only the ones from the component they're working on appear. For example, a developer might want to see the logs that are emitted by a given UI control only, or by a database utility module only. Other times, developers want to see all logs, but quickly identify which components of their app they belong to. - -Developers can use existing techniques that help with this use case, but these techniques have limitations: - -* Use the `console.group()` API to group log messages together. However: - - * This requires developers to always open a group before logging and then close it. - * Unrelated logs can inadvertently get included into groups. - * Nested groups lead to visually nested log messages in the Console, which might not always be desirable if developers want to focus only on their component. - -* Prefix log messages with a name. - - * This can be tedious and error prone. Developers can build their own console logging utility to handle this systematically, which requires extra work and maintenance. - -## Goals - -Our goal is to improve on the experimental `console.context()` method, which exists in Chromium, to provide a better solution for logging messages from a multi-component web app's code base, and more easily filter messages in the Console. - -The `console.context(contextName)` method returns an instance of an object that implements the same methods as [the `console` namespace](https://console.spec.whatwg.org/#console-namespace). Developers can create different contexts for the different parts of their apps. Messages logged from a context object _belong_ to the context and can be filtered in the Console tool, currently by typing `context:contextName` in the Console's search field. - -Our goals are to: - -1. Improve the debugging process for large web applications, by making it easier and faster to navigate many console log messages thanks to context-based filtering. -2. Improve the overall readability of the console, by making logs that belong to different logging contexts easier to distinguish visually. -3. Make it easy for developers to create contextual loggers, without requiring them to write their own utility code. - -### Non-goals - -We are not proposing to change the current method for applying a color to individual log messages. This capability will still work for log messages to a context. - -## Proposed approach - -This proposal addresses three main use cases: - -1. Emit logs from a specific context of an application. -2. Filter the output of the Console tool to show only the logs from a specific context. -3. Distinguish logs from different contexts visually. - -### Current experience in Chromium - -The experimental `console.context()` method is already available in Chromium, and addresses some of the above use cases, to some extent. The following items describe how well each use case is currently addressed: - -1. Emit logs from a specific context of an application. - - Developers can create different loggers for specific contexts of their application by using the `console.context()` method. Developers can then use the returned logger to log messages as normal. - - ```javascript - const myLogger = console.context("name-of-my-context"); - myLogger.log("This is a log message from my context"); - myLogger.warn("This is a warning message from my context"); - ``` - - ✅ **This use case is addressed.** - -1. Filter the output of the Console tool to show only the logs from a specific context. - - Developers can filter the Console tool by typing `context:name-of-my-context` in the Console's search field. This will show only the logs that were emitted from that context. - - ![DevTools Console panel with filtered context logs](console-with-context-logs-filtered.png) - - ⚠️ **This use case is partially addressed. Developers must know about the search syntax, and remember context names. The filtering UI is not user-friendly.** - -1. Distinguish logs from different contexts visually. - - Developers can use `%c` log formatting to set a color for each log message in a given context, and/or write some code to prefix the log messages with the context name. For example: - - ```javascript - const myLogger = console.context("name-of-my-context"); - myLogger.log("%c[MY CONTEXT] This is a log message from my component", "background-color:lemonchiffon;"); - ``` - - ❌ **This use case is not addressed. Extra effort and code is required for each and every message being logged.** - -### Proposed improvements - -We're proposing to improve the existing Chromium experience by making changes to the `console.context()` method and by adding in new functionality in the Console tool. - -#### Changes to the `console.context()` method - -1. Add a second, optional `color` argument to `console.context()`. - - Syntax examples: - - ```javascript - const myLogger = console.context("app"); - const myColoredLogger = console.context("storage", { color: "lemonchiffon" }); - ``` - - Adding a color to a contextual logger instance will help developers easily find messages at a glace in the Console tool, without needing to filter out other messages. - - Giving developers the ability to specify a color at the logger level makes it easier, faster, and less error prone than what the current experience requires. - - If no color is provided, we propose assigning a random color that hasn't been used yet when the new logger instance is created. This will ensure that all context log messages are easily distinguishable. - -#### Changes to the Console tool UI - -> [!NOTE] -> This section is non-normative. -> -> Conforming user agents are not required to implement this UI. It is explanatory for how console contexts may be used for the purposes of surfacing information to developer tools which consume console messages. - -1. Display context badges next to messages logged from a context. - - Since all contexts will have a name and an assigned color (whether randomly or manually assigned), we propose to display these information in the form of a badge, next to logged messages. - - This will make messages easy to read and help developers see the context for any message at a glance, even without needing to filter messages for their context. - - ![DevTools Console panel with badges on context logs](console-with-context-logs-badges.png) - - Displaying the color of the context in a badge, rather than as the message background color, has benefits too: - - * Messages may be logs, warnings, or errors, and therefore already have a background color that's important to keep. - * Messages can be formatted with `%c` to have a custom background color, and we don't want to override that. - -1. Add a new filter option for contexts. - - We propose to keep the existing `context:context-name` search syntax, but also add new filtering UI in the Console sidebar. - - To make filtering for contextual logs more user-friendly, we propose adding context names to the Console sidebar in a new section, so that developers can easily click on a context to filter out everything else. - - ![DevTools Console panel sidebar with context filters](console-sidebar-with-context-filters.png) - - As seen above, one category is created in the sidebar for each context. Categories can also be expanded to show the logs from various levels. This way, developers can optionally filter the logs emitted when using the `error()`, `warn()`, `info()`, and `debug()` methods. - -## Alternatives considered - -We have a few options for color customization outlined below. If users want the ability to customize, then we'd like to keep things as simple as possible for users. - -### `console.context()` remains the same, with no option for color customization - -One option is to not provide any new way for user customization on a context's log messages. That is, `console.context()` will remain as it is with only one string argument to name the context. If a user wants to attempt to control the colors to distinguish logs, they can use the `%c` formatting for each message in a context (as described above in [Current experience in Chromium](#current-experience-in-chromium)). - -In the Console UI, the display of context log messages will be determined by browser implementors. We will generate unique colors for context badges based on the user's theme (light or dark) and ensure these colors meet accessibility contrast standards in Chromium DevTools. - -### Apply `%c` formatting in a context name - -Another option is to just apply `%c` to the context name, like how you would to console logs. - - > [!NOTE] - > See https://github.com/whatwg/console/issues/193#issuecomment-2760521319 for a discussion about accepting `%c` formatting instead. - -### Show badges as outlined instead of filled - -In the Console UI, one consideration for filled color badges is the readability of the text color. Certain badge background colors might make the text difficult to read. To address this, we could allow users to specify two colors in `console.context()`: one for the badge background and one for the badge text. However, this level of customization might be overly complex and unnecessary. - -Instead, we could simplify it by using outlined badges with the label in the same color.`console.context()` would have a second optional `color` argument which we would use as the color for the badge outline and label, making color customization easier for users. Additionally, users could use `light-dark()` to specify a color for the context in both light and dark modes. - -## Accessibility, privacy, and security considerations - -We don't expect particular privacy or security implications for this feature. Console contexts are strings that do not expose private or sensitive information in any more ways than log messages do. They also not open new attack vectors that console messages do not already have. - -Low color contrast in a context badge can lead to accessibility issues: - -* For user-provided colors, we are not planning on addressing the potential issue. The Console tool will not change the color that were provided by the user. This is similar to how the tool currently does not change the color of messages that are logged with `%c` formatting. Users are expected to provide colors that are accessible. -* For colors provided by the Console tool, when the user does not provide a color, we will ensure that the random color combination is accessible. **TODO: needs more details.** - -## Stakeholder feedback / opposition - -- Mozilla: Positive. [Bug](https://bugzilla.mozilla.org/show_bug.cgi?id=1948870), [specification discussion](https://github.com/whatwg/console/issues/193#issuecomment-2690631598). -- Web developers: Positive. [LinkedIn post from Stefan Judis which got 277 reactions](https://www.linkedin.com/posts/stefan-judis_if-youre-a-log-debugging-person-arent-activity-7297262562084081664-WPiy?utm_source=share&utm_medium=member_desktop&rcm=ACoAAABm63wB9NIFWK7Z8l7ky8iGh6Y2nJRE5dY). - -## References & acknowledgements - -* Thank you to Alexey Kozyatinskiy, Dmitry Gozman, Jakob Linke, Igor Sheludko, and Pavel Feldman for writing and reviewing the code for the initial version of `console.context()` in V8 ([Chromium CL](https://chromium-review.googlesource.com/c/v8/v8/+/522128)). -* Thank you to Nicolas Chevobbe, from Firefox DevTools, for iterating with us on the design of the API in the [Console spec discussion](https://github.com/whatwg/console/issues/193). -* Many thanks for valuable feedback and advice from: Rob Paveza, Sam Fortiner, and Vidal Guillermo Diazleal Ortega diff --git a/ControlUICustomization/catDogSelect.png b/ControlUICustomization/catDogSelect.png deleted file mode 100644 index 22b15e6e8..000000000 Binary files a/ControlUICustomization/catDogSelect.png and /dev/null differ diff --git a/ControlUICustomization/explainer.md b/ControlUICustomization/explainer.md deleted file mode 100644 index 2d6ab73d2..000000000 --- a/ControlUICustomization/explainer.md +++ /dev/null @@ -1,560 +0,0 @@ -# Enabling Custom Control UI - -Authors: -* [Dan Clark](https://github.com/dandclark) (Microsoft) -* [Bo Cupp](https://github.com/BoCupp) (Microsoft) -* [Mason Freed](https://github.com/mfreed7) (Google) -* [Ionel Popescu](https://github.com/ipopescu93) (Microsoft) -* [Melanie Richards](https://github.com/melanierichards) (Microsoft) -* [Greg Whitworth](https://github.com/gregwhitworth) (Salesforce) - -## Status of this Document -This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. -* This document status: **Active** -* Expected venue: [W3C Web Incubator Community Group](https://wicg.io/) -* Current version: this document - -## Introduction - -A common frustration for developers who try to work with the browser's built-in form controls (`` types) is that they cannot customize the appearance of these controls to fit their site's design or user experience. In a survey of [web developers about form controls and components](http://gwhitworth.com/surveys/controls-components), the top reason that devs rewrite their own versions of these controls is the inability to sufficiently customize the appearance of the native controls. - -When developers reimplement form controls from scratch, they're not able to leverage the work done on the Web Platform to optimize performance, reliability, and accessibility of the native controls. This proposal enables web developers to customize the native controls to fit their site while leaning on investments in the web platform, saving time for developers and improving experience of the end users who interact with the controls. - - -## Goals - -* Developers can style any arbitrary part of a native control. -* Developers can add arbitrary content into any part of a native control (with some limitations; some interactive content like other controls and things like ` - - -``` \ No newline at end of file diff --git a/IframeMediaPause/iframe_media_pausing.md b/IframeMediaPause/iframe_media_pausing.md deleted file mode 100644 index 09cabed01..000000000 --- a/IframeMediaPause/iframe_media_pausing.md +++ /dev/null @@ -1,296 +0,0 @@ -# Pausing iframe media when not visible - -Authors: [Gabriel Brito](https://github.com/gabrielsanbrito), [Steve Becker](https://github.com/SteveBeckerMSFT), [Sunggook Chue](https://github.com/sunggook), Ravikiran Ramachandra - -## Status of this Document -This document is a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. -* This document status: **Archived** -* Expected venue: [Web Platform Incubator Community Group (WICG)](https://github.com/WICG/iframe-media-pausing) -* Current version: **https://github.com/WICG/iframe-media-pausing/blob/main/explainer.md** - -## Introduction - -Web applications that host embedded media content via iframes may wish to respond to application input by temporarily hiding the media content. These applications may not want to unload the entire iframe when it's not rendered since it could generate user-perceptible performance and experience issues when showing the media content again. At the same time, the user could have a negative experience if the media continues to play and emit audio when not rendered. This proposal aims to provide web applications with the ability to control embedded media content in such a way that guarantees their users have a good experience when the iframe's render status is changed. - -## Goals - -Propose a mechanism to allow embedder documents to limitedly control embedded iframe media playback based on whether the embedded iframe is rendered or not: -- When the iframe is not rendered, the embedder is able to pause the iframe media playback; and -- When the iframe becomes rendered again, the embedder is able to resume the iframe media playback. - -## Non-Goals -It is not a goal of this proposal to allow embedders to arbitrarily control when to play, pause, stop, resume, etc, the media playback of a rendered iframe. - -## Use Cases -- Websites that toggle the visibility of 3rd-party content which, for user experience reasons, should not be allowed to play back audio while not visible. - - Without this capability, these sites must completely unload the iframe to guarantee that audio stops and will not play again at some future point. This in turn results in an undesirable user experience when the iframe is made visible again: it must perform a fresh navigation which the user would need to wait for. Additionally, the unload-and-load action may result in the user losing unsaved data, e.g. form field entries. -- A website which loads video advertisements and wants to guarantee the user doesn’t hear the ad when not visible. - -## Proposed solution: media-playback-while-not-visible Permission Policy - -We propose creating a new "media-playback-while-not-visible" [permission policy] that would pause any media being played by iframes which are not currently rendered. For example, this would apply whenever the iframe’s `"display"` CSS property is set to `"none"` or when the the `"visibility"` property is set to `"hidden"` or `"collapse"`. - -This policy will have a default value of '*', meaning that all of the nested iframes are allowed to play media when not rendered. The example below show how this permission policy could be used to prevent all the nested iframes from playing media. By doing it this way, even other iframes embedded by "foo.media.com" shouldn’t be allowed to play media if not rendered. - -```html - - - - - iframe pausing - - - - - -``` - -Similarly, the top-level document is also capable of setting this policy on itself by setting the `Permissions-Policy` HTTP header. In the example below, lets' consider a top-level document served by `example.com`. Given the current `Permissions-Policy` HTTP header setup, only iframes that have the same origin as the top-level document (`example.com`) will be able to enable the `media-playback-while-not-visible` policy. - -`example.com`: - -```HTTP -Permissions-Policy: media-playback-while-not-visible=(self) -``` - -```HTML - -``` - -In this case, `example.com` serves a document that embeds an iframe with a document from `https://foo.media.com`. Since the HTTP header only allows documents from `https://example.com` to inherit `media-playback-while-not-visible`, the iframe will not be able to use the feature. - -In the past, the `"execution-while-not-rendered"` and `"execution-while-out-of-viewport"` permission policies have been proposed as additions to the [Page Lifecycle API] draft specification. However, these policies freeze all iframe JavaScript execution when not rendered, which is not desirable for the featured use case. Moreover, this proposal has not been adopted or standardized. - -## Interoperability with other Web API's - -Given that there exists many ways for a website to render audio in the broader web platform, this proposal has points of contact with many API's. To be more specific, there are two scenarios where this interaction might happen. Let's consider an iframe, which is not allowed to play `media-playback-while-not-visible`: -- Scenario 1: When the iframe is not rendered and it attempts to play audio; and - - Callers should treat this scenario as if they weren't allowed to start media playback. Like when the [`autoplay` permission policy] is set to `'none'` for an iframe. -- Scenario 2: When the iframe is already playing audio and stops being rendered during media playback. - - Callers should treat this scenario as if the user had paused media playback. - -The following subsections covers how this proposal could interact with Web APIs that render audio. - -### HTMLMediaElements - -HTMLMediaElement media playback is started and paused, respectively, with the [`play()`] and [`pause()`] methods. For scenario 1, the media element shouldn't be [allowed to play] and `play()` should return a promise rejected with `"NotAllowedError"`. In this case, the website could easily handle this like shown below. - -```js -const videoElem = document.querySelector("video"); -let startPlayPromise = videoElem.play(); - -if (startPlayPromise !== undefined) { - startPlayPromise - .then(() => { - // Start whatever you need to do only after playback - // has begun. - }) - .catch((error) => { - if (error.name === "NotAllowedError") { - showPlayButton(videoElem); - } else { - // Handle a load or playback error - } - }); -} -``` -<*Snippet extracted from [MDN](https://developer.mozilla.org/en-US/docs/Web/Media/Autoplay_guide)*> - -For the scenario 2, when the iframe is not rendered anymore, the user agent must run the same steps as it would if the [`pause()`](https://html.spec.whatwg.org/multipage/media.html#dom-media-pause) method was invoked on the media element. Documents should listen for the [`pause`](https://html.spec.whatwg.org/multipage/media.html#event-media-pause) events and treat it as if the user had paused it. - -```js -const videoElem = document.querySelector("video"); - -videoElem.addEventListener("pause", (event) => { - // Video has been paused, because either pause() has been called or - // the document is not-rendered. - console.log("Video paused"); -}); -``` - -### Web Audio API - -_This Web Audio API integration is dependent on the successful specification and implementation of the new AudioContextState `"interrupted"` proposed in this [explainer][`"interrupted"`]._ - -The Web Audio API renders audio through an [AudioContext](https://webaudio.github.io/web-audio-api/#AudioContext) object. We propose that the `AudioContext` shouldn't be [allowed to start](https://webaudio.github.io/web-audio-api/#allowed-to-start) whenever it is not rendered and disallowed by the `media-playback-while-not-visible` policy. - -For scenario 1, if the iframe is not rendered, any `AudioContext` will not be [allowed to start]. Therefore, [creating][AudioContext constructor] a new `AudioContext` should initially put it into the [`"suspended"`] state. Consequently, attempting to start playback by calling [`resume()`] shouldn't output any audio and move the `AudioContext` to the [`"interrupted"`] state. In this case, the webpage can then listen to [`statechange`] events to determine when the interruption is over. - -```js -// AudioContext being created in a not rendered iframe, where -// media-playback-while-not-visible is not allowed. -let audioCtx = new AudioContext(); -let oscillator = audioCtx.createOscillator(); -oscillator.connect(audioCtx.destination); - -audioCtx.onStateChange = () => { - console.log(audioCtx.state); -} - -// should print 'suspended' -console.log(audioCtx.state) -oscillator.start(0); -// `audioCtx.onStateChange` should print 'interrupted' -``` - -Similarly, for scenario 2, when the iframe becomes not rendered during audio playback, the user agent should interrupt the `AudioContext` by moving it to the [`"interrupted"`] state. Likewise, when the interruption is over, the UA should move the `AudioContext` back to the [`"running"`] state. Webpages can monitor these transitions by listening to the [`statechange`] event. - -The snippet below show this could work for scenario 2. Let's assume that the `AudioContext` in the iframe is [allowed to start]. When the web page is initialized, the `AudioContext` will be able to play audio and will transition to the [`"running"`] state. If the user clicks on the `"iframe_visibility_btn"`, the frame will get hidden and the `AudioContext` should be put in the [`"interrupted"`] state. Likewise, pressing again the button will show the iframe again and audio playback will be resumed. - -```html - - - - - - - - - - - - - - - -``` - -### Web Speech API - -The [Web Speech API] proposes a [SpeechSynthesis interface]. The latter interface allows websites to create text-to-speech output by calling [`window.speechSynthesis.speak`] with a [`SpeechSynthesisUtterance`], which represents the text-to-be-said. - -For both scenarios, the iframe should listen for utterance errors when calling `window.speechSynthesis.speak()`. For scenario 1 it should fail with a [`"not-allowed"` SpeechSynthesisErrorCode]; and, for scenario 2, it should fail with an [`"interrupted"` SpeechSynthesisErrorCode]. - -```js -let utterance = new SpeechSynthesisUtterance('blabla'); - -utterance.addEventListener('error', (event) => { - if (event.error === "not-allowed") { - console.log("iframe is not rendered yet"); - } else if (event.error === "interrupted") { - console.log("iframe was hidden during speak call"); - } -}) - -window.speechSynthesis.speak(utterance); -``` - -### Interoperability with autoplay - -This proposal does not affect autoplay behavior unless the media-playing iframe is not rendered. If the frame is not rendered, all media playback must be paused. If a frame that is not rendered has autoplay permission, the autoplay permission should continue to be respected if/when the frame becomes rendered in the future. - -### Interoperability with `execution-while-not-rendered` and `execution-while-out-of-viewport` - -Both `execution-while-not-rendered` and `execution-while-out-of-viewport` permission policies should take precedence over `media-playback-while-not-visible`. Therefore, in the case that we have an iframe with colliding permissions for the same origin, `media-playback-while-not-visible` should only be considered if the iframe is allowed to execute. The user agent should perform the following checks: - -1. If the origin is not [allowed to use] the [`"execution-while-not-rendered"`] feature, then: - 1. If the iframe is not [being rendered], freeze execution of the iframe context and return. -2. If the origin is not [allowed to use] the [`"execution-while-out-of-viewport"`] feature, then: - 1. If the iframe does not intersect the [viewport], freeze execution of the iframe context and return. -3. If the origin is not [allowed to use] the [`"media-playback-while-not-visible"`](#proposed-solution-media-playback-while-not-visible-permission-policy) feature, then: - 1. If the iframe is not [being rendered], pause all media playback from the iframe context and return. - -## Alternative Solutions - -This section exposes some of the alternative solutions that we came across before coming up with the chosen proposal. - -### Add a "muted" attribute to the HTMLIFrameElement - -Similarly to the [HTMLMediaElement.muted](https://html.spec.whatwg.org/multipage/media.html#htmlmediaelement) attribute, the [HTMLIFrameElement](https://www.w3.org/TR/2011/WD-html5-20110525/the-iframe-element.html#the-iframe-element) could have a `muted` boolean attribute. Whenever it is set, all the HTMLMediaElements – i.e., audio and video elements – embedded in the nested iframes should also be muted. As shown in the example below, this attribute could be set directly on the iframe HTML tag and dynamically modified using JavaScript. - -```html - - -  -    -    iframe muting -  -  -    -    -    -  - -``` - -This alternative was not selected as the preferred one, because we think that pausing media playback is preferable to just muting it. - -The [Web Speech API](https://wicg.github.io/speech-api/) proposes a [SpeechSynthesis interface](https://wicg.github.io/speech-api/#tts-section). The latter interface allows websites to create text-to-speech output by calling [`window.speechSynthesis.speak`](https://wicg.github.io/speech-api/#dom-speechsynthesis-speak) with a [`SpeechSynthesisUtterance`](https://wicg.github.io/speech-api/#speechsynthesisutterance), which represents the text-to-be-said. - -For both scenarios, the iframe should listen for utterance errors when calling `window.speechSynthesis.speak()`. For scenario 1 it should fail with a [`"not-allowed" SpeechSynthesisErrorCode`](https://wicg.github.io/speech-api/#dom-speechsynthesiserrorcode-not-allowed) SpeechSyntesis error; and, for scenario 2, it should fail with an [`"interrupted" SpeechSynthesisErrorCode`](https://wicg.github.io/speech-api/#dom-speechsynthesiserrorcode-interrupted) error. - -## Accessibility, Privacy, and Security Considerations - -There is a possibility that the embedded could infer that the permission policy is in effect and it is being hidden. For example, a webpage could have both an HTMLVideoElement and an AudioContext playing audio simultaneously. If at some point they both get, respectively, paused and interrupted within a short time frame; then, the website could infer that an ancestor frame is applying the permission policy on it and that it has been hidden by one of the ancestors. - -Nevertheless, we believe that this would be very low impact, because a web page could make a similar observation by monitoring when it’s unloaded as well as via the [IntersectionObserver API]. - -[AudioContext constructor]: https://webaudio.github.io/web-audio-api/#dom-audiocontext-audiocontext -[allowed to play]: https://html.spec.whatwg.org/multipage/media.html#allowed-to-play -[allowed to start]: https://webaudio.github.io/web-audio-api/#allowed-to-start -[allowed to use]: https://html.spec.whatwg.org/multipage/iframe-embed-object.html#allowed-to-use -[`autoplay` permission policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy/autoplay -[being rendered]: https://html.spec.whatwg.org/multipage/rendering.html#being-rendered -[`"execution-while-not-rendered"`]: https://wicg.github.io/page-lifecycle/#execution-while-not-rendered -[`"execution-while-out-of-viewport"`]: https://wicg.github.io/page-lifecycle/#execution-while-out-of-viewport -[`"interrupted"`]: https://github.com/MicrosoftEdge/MSEdgeExplainers/blob/main/AudioContextInterruptedState/explainer.md -[`"interrupted"` SpeechSynthesisErrorCode]: ttps://wicg.github.io/speech-api/#dom-speechsynthesiserrorcode-interrupted -[IntersectionObserver API]: https://w3c.github.io/IntersectionObserver/#intersection-observer-interface -[`"not-allowed"` SpeechSynthesisErrorCode]: https://wicg.github.io/speech-api/#dom-speechsynthesiserrorcode-not-allowed -[Page Lifecycle API]: https://wicg.github.io/page-lifecycle/#feature-policies -[permission policy]: https://www.w3.org/TR/permissions-policy/ -[`pause()`]: https://html.spec.whatwg.org/multipage/media.html#dom-media-pause -[`play()`]: https://html.spec.whatwg.org/multipage/media.html#dom-media-play -[`SpeechSynthesisUtterance`]: https://wicg.github.io/speech-api/#speechsynthesisutterance -[SpeechSynthesis interface]: https://wicg.github.io/speech-api/#tts-section -[`statechange`]: https://webaudio.github.io/web-audio-api/#eventdef-baseaudiocontext-statechange -[`"suspended"`]: https://webaudio.github.io/web-audio-api/#dom-audiocontextstate-suspended -[`resume()`]: https://webaudio.github.io/web-audio-api/#dom-audiocontext-resume -[`"running"`]: https://webaudio.github.io/web-audio-api/#dom-audiocontextstate-running -[viewport]: https://www.w3.org/TR/CSS2/visuren.html#viewport -[Web Speech API]: https://wicg.github.io/speech-api/ -[`window.speechSynthesis.speak`]: https://wicg.github.io/speech-api/#dom-speechsynthesis-speak \ No newline at end of file diff --git a/ImageResource-color_scheme/1.png b/ImageResource-color_scheme/1.png deleted file mode 100644 index f09d2ae5a..000000000 Binary files a/ImageResource-color_scheme/1.png and /dev/null differ diff --git a/ImageResource-color_scheme/2.png b/ImageResource-color_scheme/2.png deleted file mode 100644 index 0aa787ce5..000000000 Binary files a/ImageResource-color_scheme/2.png and /dev/null differ diff --git a/ImageResource-color_scheme/3.png b/ImageResource-color_scheme/3.png deleted file mode 100644 index ae9e4fe80..000000000 Binary files a/ImageResource-color_scheme/3.png and /dev/null differ diff --git a/ImageResource-color_scheme/4.png b/ImageResource-color_scheme/4.png deleted file mode 100644 index 3add1a8d8..000000000 Binary files a/ImageResource-color_scheme/4.png and /dev/null differ diff --git a/ImageResource-color_scheme/explainer.md b/ImageResource-color_scheme/explainer.md deleted file mode 100644 index 4c1a2319a..000000000 --- a/ImageResource-color_scheme/explainer.md +++ /dev/null @@ -1,84 +0,0 @@ -# ImageResource Color Scheme Explainer - -Authors: [Aaron Gustafson](https://github.com/aarongustafson) - -## Status of this Document -This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. -* This document status: **Archived** -* Expected venue: [W3C Web Applications Working Group](https://www.w3.org/2019/webapps/) -* Current version: this document - -## Introduction - -With operating systems beginning to offer multiple color schemes (e.g., “dark mode” and “light mode”) and with [CSS offering a media query for that user preference](https://drafts.csswg.org/mediaqueries-5/#descdef-media-prefers-color-scheme), it is important to enable web apps to offer appropriate iconography for the context. As such, we need to upgrade the [ImageResource dictionary](https://w3c.github.io/manifest/#dom-imageresource) to include an optional member indicating which color scheme a given image should be associated with. - -## Goals - -* Enable authors to (optionally) identify ImageResource (icons, etc.) within their Web App Manifest as being appropriate for specific color schemes -* Enable user agents and operating systems to display the appropriate ImageResource, given the user’s chosen color scheme - -## Use Cases - -Many websites are beginning to look to the operating system for cues about how the user would prefer their apps be rendered (e.g., “dark mode”). There is a media query which allows them to toggle the color-related styles applied to their website (or PWA), but their app icon (referenced in the Web App Manifest) remains unaffected. In certain cases, it may make sense for an app’s icon to adjust to match the user’s chosen color scheme for maximum legibility. - -For example, consider [the VS Code Insiders logo as displayed in Windows 10](#vs-code): - -
- -![VS Code Insiders logo against a dark background (Windows 10)](1.png) -![VS Code Insiders logo against a light background (Windows 10)](2.png) - -
- -In the default colors of Windows 10 (and Dark Mode), the taskbar is black and the green color of the icon has a contrast ratio of 5.84:1, which is enough to meet WCAG AA for user interface components. By contrast, in Windows 10’s Light Mode, the very same logo is set against a medium grey background and only achieves a contrast ratio of 1.56:1. This is but one example. Enabling developers to indicate different image asset options for different color schemes would help avoid this issue. - -In their implementation of Windows’ Jump List feature, [the Twitter PWA implements white PNG icons](#twitter): - -
- -![The Twitter PWA’s Jump List against a dark background (Windows 10)](3.png) - -
- -When in Light Mode, Windows 10 automatically converts the white icons to black to keep them legible: - -
- -![The Twitter PWA’s Jump List against a light background (Windows 10)](4.png) - -
- -Such automatic conversion can be helpful when you are dealing with black-to-white inversion, but it falls apart when you are dealing with other colors (and multiple colors). It would be better to allow a developer to specifically define the icon to use in each situation. - - -## API Proposal - -We are proposing that a new member be added to the [ImageResource dictionary](https://w3c.github.io/manifest/#dom-imageresource) defined in the [Web App Manifest](https://w3c.github.io/manifest/): `color_scheme`. This optional member would accept any values allowable in [the `prefers-color-scheme` media query](https://drafts.csswg.org/mediaqueries-5/#descdef-media-prefers-color-scheme): - -```json -"icons": [ - { - "src": "/icons/play-later.svg", - "type": "image/svg+xml", - "purpose": "any", - "color_scheme": "light" - }, - { - "src": "/icons/play-later-reverse.svg", - "type": "image/svg+xml", - "purpose": "any", - "color_scheme": "dark" - } -] -``` - -Without a defined `color_scheme`, the value of "light" would be assigned. - -## Open Questions - -1. Would it make sense to use the existing [`purpose` member of an ImageResource](https://w3c.github.io/manifest/#dom-imageresource-purpose)? -2. With SVGs, might it be possible to pick up foreground and background colors directly from the OS (along the lines of `currentColor`)? -3. If an author specifies that they have "no preference" for an icon, does that indicate than an OS should feel empowered to automatically mutate the colors to try to provide the most readable image? (For example: the white shortcut icons being automagically reversed in Windows.) - ---- -[Related issues](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/ImageResource%20Color%20Scheme) | [Open a new issue](https://github.com/MicrosoftEdge/MSEdgeExplainers/issues/new?title=%5BImageResource%20Color%20Scheme%5D) diff --git a/IndexedDbGetAllEntries/explainer.md b/IndexedDbGetAllEntries/explainer.md deleted file mode 100644 index 64799c251..000000000 --- a/IndexedDbGetAllEntries/explainer.md +++ /dev/null @@ -1,327 +0,0 @@ -# IndexedDB: getAllRecords() - -## Author: - -- [Steve Becker](https://github.com/SteveBeckerMSFT) - -## Participate - -- https://github.com/w3c/IndexedDB/issues/206 -- https://github.com/w3c/IndexedDB/issues/130 - -## Status of this Document -This document was a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. - -* This document status: **Archived** -* Venue: [W3C Web Applications Working Group](https://www.w3.org/groups/wg/webapps/) -* Spec: [Indexed Database API 3.0](https://w3c.github.io/IndexedDB/#dom-idbobjectstore-getallrecords) - -## Introduction - -[`IndexedDB`](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API) is a transactional database for client-side storage. Each record in the database contains a key-value pair. [`getAll()`](https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/getAll) enumerates database record values sorted by key in ascending order. [`getAllKeys()`](https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/getAllKeys) enumerates database record primary keys sorted by key in ascending order. - -This explainer proposes a new operation, `getAllRecords()`, which combines [`getAllKeys()`](https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/getAllKeys) with [`getAll()`](https://developer.mozilla.org/en-US/docs/Web/API/IDBObjectStore/getAll) to enumerate both primary keys and values at the same time. For an [`IDBIndex`](https://developer.mozilla.org/en-US/docs/Web/API/IDBIndex), `getAllRecords()` also provides the record's index key in addition to the primary key and value. Lastly, `getAllRecords()` offers a new direction option to enumerate records sorted by key in descending order. - -To add the direction option to the existing `getAll()` and `getAllKeys()` operations, this explainer proposes new function overloads that accept the same argument as `getAllRecords()`: the `IDBGetAllOptions` dictionary. - -## Goals - -Decrease the latency of database read operations. By retrieving the primary key, value and index key for database records through a single operation, `getAllRecords()` reduces the number of JavaScript events required to read records. Each JavaScript event runs as a task on the main JavaScript thread. These tasks can introduce overhead when reading records requires a sequence of tasks that go back and forth between the main JavaScript thread and the IndexedDB I/O thread. - -For batched record iteration, for example, retrieving *N* records at a time, the primary and index keys provided by `getAllRecords()` can eliminate the need for an [`IDBCursor`](https://developer.mozilla.org/en-US/docs/Web/API/IDBCursor), which further reduces the number of JavaScript events required. To read the next *N* records, instead of advancing a cursor to determine the range of the next batch, getAllRecords() can use the primary key or the index key retrieved by the results from the previous batch. - -Update the existing operations `getAll()` and `getAllKeys()` to support the same query options as `getAllRecords()`, which adds direction. For some scenarios, `getAll()` and `getAllKeys()` may suffice. For example, developers may use `getAllKeys()` to defer loading values until needed. For records with inline keys, `getAll()` already retrieves both key and value. - -## `IDBObject::getAllRecords()` and `IDBIndex::getAllRecords()` - -This explainer proposes adding `getAllRecords()` to both [`IDBObjectStore`](https://www.w3.org/TR/IndexedDB/#idbobjectstore) and [`IDBIndex`](https://www.w3.org/TR/IndexedDB/#idbindex). `getAllRecords()` creates a new `IDBRequest` that queries its `IDBObjectStore` or `IDBIndex` owner. The `IDBRequest` completes with an array of `IDBRecord` results. Each `IDBRecord` contains the `key`, `primaryKey` and `value` attributes. For `IDBIndex`, `key` is the record's index key. For `IDBObjectStore`, both `key` and `primaryKey` return the same value. The pre-existing [`IDBCursorWithValue`](https://www.w3.org/TR/IndexedDB/#idbcursorwithvalue) interface contains the same attributes and values for both `IDBObjectStore` and `IDBIndex`. However, unlike `getAllRecords()`, a cursor may only read one record at a time. - -## Adding direction to `getAll()` and `getAllKeys()` - -This explainer proposes using `getAllRecords()` as feature detection for direction support in `getAllKeys()` and `getAll()`. `getAllRecords()` introduces the `IDBGetAllOptions` dictionary, which developers may also use with `getAll()` and `getAllKeys()`. Before using `IDBGetAllOptions`, developers must check for the existence of `getAllRecords()` in `IDBObjectStore` or `IDBIndex`. If developers provide both the `IDBGetAllOptions` argument and the `count` argument, the extra `count` argument is ignored like any other extra argument. The `count` property in `IDBGetAllOptions` is used instead. - -## Compatibility risk - -Overloading `getAll()` and `getAllKeys()` to accept the `IDBGetAllOptions` dictionary introduces compatibility risk. Prior to this proposal, when passed a dictionary argument, both `getAll()` and `getAllKeys()` throw an exception after [failing to convert the dictionary to a key range](https://w3c.github.io/IndexedDB/#convert-a-value-to-a-key-range). After the overload, `getAllKeys()` and `getAll()` will no longer throw for dictionary input. When the `IDBGetAllOptions` dictionary initializes with its default values, it creates a query that retrieves all of the keys or values from the entire database. - -Since using a dictionary with `getAll()` and `getAllKeys()` is a programming error, we believe compat risk is low. - -## Key scenarios - -### Read multiple database records through a single request - -```js -// Define a helper that creates a basic read transaction using `getAllRecords()`. -// Wraps the transaction in a promise that resolves with the query results or -// rejects after an error. Queries `object_store_name` unless `optional_index_name` -// is defined. -async function get_all_records_with_promise( - database, - object_store_name, - query_options, - optional_index_name -) { - return await new Promise((fulfill, reject) => { - // Create a read-only transaction. - const read_transaction = database.transaction( - object_store_name, - 'readonly' - ); - - // Get the object store or index to query. - const object_store = read_transaction.objectStore(object_store_name); - let query_target = object_store; - if (optional_index_name) { - query_target = object_store.index(optional_index_name); - } - - // Start the getAllRecords() request. - const request = query_target.getAllRecords(query_options); - - // Resolve promise with results after success. - request.onsuccess = (event) => { - fulfill(request.result); - }; - - // Reject promise with error after failure. - request.onerror = () => { - reject(request.error); - }; - read_transaction.onerror = () => { - reject(read_transaction.error); - }; - }); -} - -// Read the first 5 records from an object store in the database. -const records = await get_all_records_with_promise( - database, - kObjectStoreName, - /*query_options=*/ { count: 5 } -); -console.log( - 'The second record in the database contains: ' + - `primaryKey: ${records[1].primaryKey}, key: ${records[1].key}, value: ${records[1].value}` -); -``` - -### Read multiple database records into a Map - -Developers may use the results from `getAllRecords()` to construct a new [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) that contains a key-value pair for each database record returned by the query. - -```js -// This example uses the `get_all_records_with_promise()` helper defined above. -// -// Read the last 9 records from an index. -const records = await get_all_records_with_promise( - database, - kObjectStoreName, - /*query_options=*/ { count: 9, direction: 'prev' }, - kIndexName -); - -// Map the record's index key to the record's value -const map = new Map(records.map(({ key, value }) => [key, value])); - -// Returns the database record value for the index `key` when the record exists -// in `map`. -const value = map.get(key); - -// Use the following to create an iterator for each database record in `map`: -const index_key_iterator = map.keys(); -const value_iterator = map.values(); -const entry_iterator = map.entries(); // Enumerate both index keys and values. -``` - -### Support paginated cursors using batch record iteration - -Many scenarios read *N* database records at a time, waiting to read the next batch of records until needed. For example, a UI may display *N* records, starting with the last record in descending order. As the user scrolls, the UI will display new content by reading the next *N* records. - -To support this access pattern, the UI calls `getAllRecords()` with the options `direction: 'prev'` and `count: N` to retrieve *N* records at a time in descending order. After the initial batch, the UI must specify the upper bound of the next batch using the primary key or index key from the `getAllRecords()` results of the previous batch. - -```js -// This example uses the `get_all_records_with_promise()` helper defined above. -// -// Create a batch iterator where each call to `next()` retrieves `batch_size` database -// records in `direction` order from `object_store_name` or `optional_index_name`. -async function* idb_batch_record_iterator( - database, - object_store_name, - direction, - batch_size, - optional_index_name -) { - let is_done = false; - - // Begin the iteration unbounded to retrieve the first or last `batch_size` records. - let query; - - while (!is_done) { - const records = await get_all_records_with_promise( - database, - object_store_name, - /*query_options=*/ { query, count: batch_size, direction }, - optional_index_name - ); - - if (records.length < batch_size) { - // We've iterated through all the database records! - is_done = true; - return records; - } - - // Store the lower or upper bound for the next iteration. - const last_record = records[records.length - 1]; - if (direction === 'next' || direction === 'nextunique') { - query = IDBKeyRange.lowerBound(last_record.key, /*exclusive=*/ true); - } else { // direction === 'prev' || direction === 'prevunique' - query = IDBKeyRange.upperBound(last_record.key, /*exclusive=*/ true); - } - yield records; - } -} - -// Create a reverse iterator that reads 5 records from an index at a time. -const reverse_iterator = idb_batch_record_iterator( - database, - 'my_object_store', - /*direction=*/ 'prev', - /*batch_size=*/ 5, - 'my_index' -); - -// Get the last 5 records. -let results = await reverse_iterator.next(); -let records = results.value; -console.log( - 'The first record contains: ' + - `primaryKey: ${records[0].primaryKey}, key: ${records[0].key}, value: ${records[0].value}` -); - -// Get the next batch of 5 records. -if (!results.done) { - results = await reverse_iterator.next(); -} -``` - -### Use direction with `getAllKeys()` after feature detection - -`getAllRecords()` introduces the `IDBGetAllOptions` dictionary, which developers may also use with `getAll()` and `getAllKeys()`. Before using `IDBGetAllOptions`, developers must check for the existence of `getAllRecords()` in `IDBObjectStore` or `IDBIndex`. - -```js -const read_transaction = database.transaction('my_object_store', 'readonly'); -const object_store = read_transaction.objectStore('my_object_store'); - -// Use feature detection to determine if this browser supports `getAllRecords()`. -if ('getAllRecords' in object_store) { - // Request the last 5 primary keys in `object_store`. - const get_all_options = { - direction: 'prev', - count: 5 - }; - const request = object_store.getAllKeys(get_all_options); -} else { - // Fallback to a cursor with direction: 'prev' for this query. -} -``` - -## Considered alternatives - -### `getAllEntries()` - -Similar to `getAllRecords()` but [provides results as an array of entries](https://github.com/w3c/IndexedDB/issues/206#issuecomment-566205600). Each entry is a two or three element array containing the record's key, value and optional index key. For example: - -`IDBObjectStore` entries provide array values with two elements: `[ [primaryKey1, value1], [primaryKey2, value2], ... ]` - -`IDBIndex` entries provide array values with three elements: `[ [primaryKey1, value1, indexKey1], [primaryKey2, value2, indexKey2], ... ]` - -Developers may directly use the entry results to construct a `Map` or `Object` since the entry results are inspired by ECMAScript's [Map.prototype.entries()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/entries). However, `getAllEntries()` has unusual ergonomics, requiring indices like `0` and `1` to access the record properties like `key` and `value`. Also, IndexedDB database records do not map cleanly to ECMAScript entries. For `IDBIndex`, the results contain a third element for index key. For an alternate form, `[[ indexKey1, [ primaryKey1, value1]], [ indexKey2, [ primaryKey2, value2]], ... ]`, the index key cannot always serve as the entry's key since the index key may not be unique across all records. - -## WebIDL - -```js -dictionary IDBGetAllOptions { - // A key or an `IDBKeyRange` identifying the records to retrieve. - any query = null; - - // The maximum number of results to retrieve. - [EnforceRange] unsigned long count; - - // Determines how to enumerate and sort results. - // Use 'prev' to enumerate and sort results by key in descending order. - IDBCursorDirection direction = 'next'; -}; - -interface IDBRecord { - // For `IDBIndex` records, `key` is the index key. For `IDBObjectStore` - // records, `key` is the same as `primaryKey`. - readonly attribute any key; - readonly attribute any primaryKey; - readonly attribute any value; -}; - -[Exposed=(Window,Worker)] -partial interface IDBObjectStore { - // After the `getAllRecords()` request completes, the `IDBRequest::result` property - // contains an array of records: - // `[[primaryKey1, value1], [primaryKey2, value2], ... ]` - [NewObject, RaisesException] - IDBRequest getAllRecords(optional IDBGetAllRecordsOptions options = {}); - - // For `getAll()` and `getAllKeys()`, add support for the direction option - // through a new overload, which accepts a `IDBGetAllOptions` dictionary as - // the first and only argument. - // - // IDBRequest getAll(optional IDBGetAllOptions options); - // IDBRequest getAllKeys(optional IDBGetAllOptions options); - // - [NewObject, RaisesException] - IDBRequest getAll(optional any query_or_options = null, - optional [EnforceRange] unsigned long count); - [NewObject, RaisesException] - IDBRequest getAllKeys(optional any query_or_options = null, - optional [EnforceRange] unsigned long count); -} - -[Exposed=(Window,Worker)] -partial interface IDBIndex { - // Produces the same type of results as `IDBObjectStore::getAllRecords()` above, - // but each entry also includes the record's index key at array index 2: - // `[[primaryKey1, value1, indexKey1], [primaryKey2, value2, indexKey2], ... ]` - [NewObject, RaisesException] - IDBRequest getAllRecords(optional IDBGetAllRecordsOptions options = {}); - - // Like `IDBObjectStore` above, IDBIndex overloads `getAll()` and `getAllKeys()` - // to support direction: - // - // IDBRequest getAll(optional IDBGetAllOptions options); - // IDBRequest getAllKeys(optional IDBGetAllOptions options); - // - [NewObject, RaisesException] - IDBRequest getAll(optional any query_or_options = null, - optional [EnforceRange] unsigned long count); - [NewObject, RaisesException] - IDBRequest getAllKeys(optional any query_or_options = null, - optional [EnforceRange] unsigned long count); -} -``` - -## Stakeholder Feedback / Opposition - -- Web Developers: Positive - - Developers have reported the limitations addressed by `getAllRecords()`. A few examples: - - ["You cannot build a paginated cursor in descending order."](https://nolanlawson.com/2021/08/22/speeding-up-indexeddb-reads-and-writes/) - - ["An example where getAll() could help but needs to retrieve the index key and primary key."](https://stackoverflow.com/questions/44349168/speeding-up-indexeddb-search-with-multiple-workers) -- Chromium: Positive -- Webkit: No signals -- Gecko: No signals - -## References & acknowledgements - -Special thanks to [Joshua Bell](https://github.com/inexorabletash) who proposed `getAllRecords()` in the [W3C IndexedDB issue](https://github.com/w3c/IndexedDB/issues/206). - -Many thanks for valuable feedback and advice from: - -- [Rahul Singh](https://github.com/rahulsingh-msft) -- [Foromo Daniel Soromou](https://github.com/fosoromo_microsoft) -- [Evan Stade](https://github.com/evanstade) diff --git a/InstallTimePermissionsPrompt/1.png b/InstallTimePermissionsPrompt/1.png deleted file mode 100644 index 23baf44c8..000000000 Binary files a/InstallTimePermissionsPrompt/1.png and /dev/null differ diff --git a/InstallTimePermissionsPrompt/2.png b/InstallTimePermissionsPrompt/2.png deleted file mode 100644 index 5449aedc5..000000000 Binary files a/InstallTimePermissionsPrompt/2.png and /dev/null differ diff --git a/InstallTimePermissionsPrompt/3.png b/InstallTimePermissionsPrompt/3.png deleted file mode 100644 index 534cc8cab..000000000 Binary files a/InstallTimePermissionsPrompt/3.png and /dev/null differ diff --git a/InstallTimePermissionsPrompt/Explainer.md b/InstallTimePermissionsPrompt/Explainer.md deleted file mode 100644 index ea899bf7e..000000000 --- a/InstallTimePermissionsPrompt/Explainer.md +++ /dev/null @@ -1,113 +0,0 @@ -# First Run Permissions Prompt Explainer - -Authors: - -* [Aaron Gustafson](https://github.com/aarongustafson) -* [Diego Gonzalez](https://github.com/diekus) - -Acknowledgements: - -* [Melanie Richards](https://github.com/melanierichards) - -**Authors’ Note: This is an early idea and we expect to experiment a bit with it before coming to a final recommendation.** - -## Status of this Document - -This document is intended as a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. - -* This document status: **Active** -* Expected venue: [W3C Web Incubator Community Group](https://wicg.io/) -* Current version: this document - -## Introduction - -An increasing number of sensitive APIs have been added to the web platform. To ensure user privacy is protected, these APIs are restricted to secure origins and typically require an explicit user permissions grant in order for the API to be available to a given domain. As web applications have become more advanced, these permissions request prompts have proliferated. This proliferation has led to users having "permissions request fatigue," the major symptom of which is a compulsive denial permissions prompts, especially if the prompt is non-contextual or if several requests are shown in rapid succession. This situation is problematic for many applications that rely on permission grants for their core functionality (e.g., a video conferencing app not being granted access to the camera and microphone). - -## Goals - -* Establish a mechanism by which developers can enumerate critical permissions that browsers could prompt users to consider when the web app is first run (including post-installation). - -## Non-goals - -* Enable developers to require permission grants for installation to proceed. -* Replace the option of using "just-in-time" permissions prompts. -* Block access to APIs if they are not declared according to the [API proposal](#api-proposal). - -## Use Cases - -A user browses to a new social media site and opts to install the service as a PWA to try it out. The developer has declared that they are interested in access to their users’ camera(s) and location. They also want to be able to send the user push notifications. When the web app is re-parented during install, the user is presented with these requests and chooses to grant push notification access, but denies access to the camera and location because they aren’t interested in using the service’s features that rely on those APIs. - -A user has purchased a new device that comes with a PWA-based communication service pre-installed. When they first launch the PWA, they are asked to grant access to the device’s camera(s) and microphone, enable push notifications, and grant the app the ability to send and receive phone calls and text messages. Based on how they anticipate using the app, the user chooses to grant or deny each of these permissions. - -## Additional Use Cases - -An app catalog is onboarding a new web app. The catalog reads in the `permissions` values and maps each to a user-facing string to include in the product page. - -When a user views the "Site Permissions" UI in the web app’s wrapper, the browser considers the `permissions` values when prioritizing the order and/or display of the domain’s permissions. - -## Prior Art - -Prior to the launch of Android 6.0, apps in the Play Store were accompanied by a declaration of which permissions the application would be granted upon install. This model was discontinued—in favor of a more web-like runtime request model—because many apps were taking advantage of the opportunity to get access to as much sensitive user data as they could, without the user being able to deny access to any of the individual APIs. The problem with this model was that users had to accept all of the permissions in order to install the app. Their only other choice was to not install the app (which may not have been an option in some cases). The idea set forth in this explainer improves upon that approach by enabling users to approve or deny (or defer) permissions on an individual basis. - -Some Implementors also currently bundle requests for camera and microphone access within a single permissions prompt when requests are made via `getUserMedia()`: - -
- -![In Chromium, permissions for both microphone & camera access can be requested at once.](2.png) - -
- -
- -![Firefox’s batch prompt also enables users to choose the device to use](3.png) - -
- -## API Proposal - -As the document that governs the installation and presentation of PWAs within an operating system, the [Web App Manifest](https://w3c.github.io/manifest/) makes the most sense as a home for this feature. This proposal would add a new, optional member to the manifest dictionary: `permissions`. This member would accept an array of one or more [PermissionName strings](https://w3c.github.io/permissions/#enumdef-permissionname) indicating the permissions being requested. For example: - -```json -"permissions": [ - "camera", - "microphone", - "notifications", - "push" -] -``` - -This example would request four permissions: [Camera access](https://w3c.github.io/permissions/#dom-permissionname-camera), [Microphone access](https://w3c.github.io/permissions/#dom-permissionname-camera) and the ability to [send notifications](https://w3c.github.io/permissions/#dom-permissionname-notifications) and [push notifications](https://w3c.github.io/permissions/#dom-permissionname-push). - -The values supplied in `permissions` should be authored from most important to least important. If the item count exceeds the number of permissions the UI allows, any excess will not be a part of the request. - -## User Interface - -The user interface presented should enumerate each of the permissions being requested. Implementors are free to include additional information beyond the name of the permission if they so desire (e.g., what the API is used for, brief privacy information, etc.). The user interface MUST provide a means of approving and denying each permission individually. The implementor MAY provide an "Ask me later" (or similar) option. - -The implementor MAY choose a default setting (or no default setting) for each permission. If a user has interacted with the site previously in a browsing session, the implementor SHOULD default to the existing permission setting for each permission requested (e.g., if camera access was previously denied for this origin, it should default to "Deny" in this UI). - -The implementor MAY choose to include an "Accept All" or similar UI control to auto-grant all permissions requests. - -Implementors MUST still provide access to audit these permissions. - -Implementors MAY choose to limit the number of permissions displayed in the UI. The values supplied in `permissions` are ordered from most to least important, meaning any items that exceed the UI limit may be safely dropped. - -### Example - -An implementor could choose to present these permissions within the installation prompt like this: - -
- -![Hypothetical install prompt that incorporates permission requests between the app identification and the Install & Cancel buttons](1.png) - -
- -## Interplay with Enterprise Policies - -If the implementor supports use of some form of enterprise-level permissions policy (e.g., forbidding certain permissions for all apps or automatically granting specific permissions to individual apps), they may modify the display of the enumerated permissions shown during install. If the policy enforces a specific setting globally or for a specific app, the toggle control should be disabled and the implementor should include some language to the effect of "This permission is controlled by your organization." - -## Open Questions - -3. **Should the order of the strings in the `permissions` array dictate the order in which the permissions requests are displayed in the installation UI?** On one hand, consistency in display order could make it more familiar to users, but on the other, having it be different each time might cause users to pay more attention to it. Worth exploring. -4. **If a user denies a given permission, should the developer be granted one additional chance to request the permission in context?** "Second chance" requests are beyond the scope of this Explainer. -5. **As denial of certain permissions can cripple a PWA’s ability to function as intended, should we consider providing some mechanism for developers to indicate critical permissions?** The `permissions` array should enumerate only critical permissions, but user acceptance is not guaranteed. If there are non-critical permissions (e.g., a permission that only comes into play for certain scenarios), developers should continue to use runtime permissions prompting. diff --git a/LICENSE.txt b/LICENSE.txt deleted file mode 100644 index b8deacfb0..000000000 --- a/LICENSE.txt +++ /dev/null @@ -1,396 +0,0 @@ -Copyright © Microsoft Corporation. -This work is licensed under a Creative Commons Attribution 4.0 International License: - -Attribution 4.0 International - -======================================================================= - -Creative Commons Corporation ("Creative Commons") is not a law firm and -does not provide legal services or legal advice. Distribution of -Creative Commons public licenses does not create a lawyer-client or -other relationship. Creative Commons makes its licenses and related -information available on an "as-is" basis. Creative Commons gives no -warranties regarding its licenses, any material licensed under their -terms and conditions, or any related information. Creative Commons -disclaims all liability for damages resulting from their use to the -fullest extent possible. - -Using Creative Commons Public Licenses - -Creative Commons public licenses provide a standard set of terms and -conditions that creators and other rights holders may use to share -original works of authorship and other material subject to copyright -and certain other rights specified in the public license below. The -following considerations are for informational purposes only, are not -exhaustive, and do not form part of our licenses. - - Considerations for licensors: Our public licenses are - intended for use by those authorized to give the public - permission to use material in ways otherwise restricted by - copyright and certain other rights. Our licenses are - irrevocable. Licensors should read and understand the terms - and conditions of the license they choose before applying it. - Licensors should also secure all rights necessary before - applying our licenses so that the public can reuse the - material as expected. Licensors should clearly mark any - material not subject to the license. This includes other CC- - licensed material, or material used under an exception or - limitation to copyright. More considerations for licensors: - wiki.creativecommons.org/Considerations_for_licensors - - Considerations for the public: By using one of our public - licenses, a licensor grants the public permission to use the - licensed material under specified terms and conditions. If - the licensor's permission is not necessary for any reason--for - example, because of any applicable exception or limitation to - copyright--then that use is not regulated by the license. Our - licenses grant only permissions under copyright and certain - other rights that a licensor has authority to grant. Use of - the licensed material may still be restricted for other - reasons, including because others have copyright or other - rights in the material. A licensor may make special requests, - such as asking that all changes be marked or described. - Although not required by our licenses, you are encouraged to - respect those requests where reasonable. More_considerations - for the public: - wiki.creativecommons.org/Considerations_for_licensees - -======================================================================= - -Creative Commons Attribution 4.0 International Public License - -By exercising the Licensed Rights (defined below), You accept and agree -to be bound by the terms and conditions of this Creative Commons -Attribution 4.0 International Public License ("Public License"). To the -extent this Public License may be interpreted as a contract, You are -granted the Licensed Rights in consideration of Your acceptance of -these terms and conditions, and the Licensor grants You such rights in -consideration of benefits the Licensor receives from making the -Licensed Material available under these terms and conditions. - - -Section 1 -- Definitions. - - a. Adapted Material means material subject to Copyright and Similar - Rights that is derived from or based upon the Licensed Material - and in which the Licensed Material is translated, altered, - arranged, transformed, or otherwise modified in a manner requiring - permission under the Copyright and Similar Rights held by the - Licensor. For purposes of this Public License, where the Licensed - Material is a musical work, performance, or sound recording, - Adapted Material is always produced where the Licensed Material is - synched in timed relation with a moving image. - - b. Adapter's License means the license You apply to Your Copyright - and Similar Rights in Your contributions to Adapted Material in - accordance with the terms and conditions of this Public License. - - c. Copyright and Similar Rights means copyright and/or similar rights - closely related to copyright including, without limitation, - performance, broadcast, sound recording, and Sui Generis Database - Rights, without regard to how the rights are labeled or - categorized. For purposes of this Public License, the rights - specified in Section 2(b)(1)-(2) are not Copyright and Similar - Rights. - - d. Effective Technological Measures means those measures that, in the - absence of proper authority, may not be circumvented under laws - fulfilling obligations under Article 11 of the WIPO Copyright - Treaty adopted on December 20, 1996, and/or similar international - agreements. - - e. Exceptions and Limitations means fair use, fair dealing, and/or - any other exception or limitation to Copyright and Similar Rights - that applies to Your use of the Licensed Material. - - f. Licensed Material means the artistic or literary work, database, - or other material to which the Licensor applied this Public - License. - - g. Licensed Rights means the rights granted to You subject to the - terms and conditions of this Public License, which are limited to - all Copyright and Similar Rights that apply to Your use of the - Licensed Material and that the Licensor has authority to license. - - h. Licensor means the individual(s) or entity(ies) granting rights - under this Public License. - - i. Share means to provide material to the public by any means or - process that requires permission under the Licensed Rights, such - as reproduction, public display, public performance, distribution, - dissemination, communication, or importation, and to make material - available to the public including in ways that members of the - public may access the material from a place and at a time - individually chosen by them. - - j. Sui Generis Database Rights means rights other than copyright - resulting from Directive 96/9/EC of the European Parliament and of - the Council of 11 March 1996 on the legal protection of databases, - as amended and/or succeeded, as well as other essentially - equivalent rights anywhere in the world. - - k. You means the individual or entity exercising the Licensed Rights - under this Public License. Your has a corresponding meaning. - - -Section 2 -- Scope. - - a. License grant. - - 1. Subject to the terms and conditions of this Public License, - the Licensor hereby grants You a worldwide, royalty-free, - non-sublicensable, non-exclusive, irrevocable license to - exercise the Licensed Rights in the Licensed Material to: - - a. reproduce and Share the Licensed Material, in whole or - in part; and - - b. produce, reproduce, and Share Adapted Material. - - 2. Exceptions and Limitations. For the avoidance of doubt, where - Exceptions and Limitations apply to Your use, this Public - License does not apply, and You do not need to comply with - its terms and conditions. - - 3. Term. The term of this Public License is specified in Section - 6(a). - - 4. Media and formats; technical modifications allowed. The - Licensor authorizes You to exercise the Licensed Rights in - all media and formats whether now known or hereafter created, - and to make technical modifications necessary to do so. The - Licensor waives and/or agrees not to assert any right or - authority to forbid You from making technical modifications - necessary to exercise the Licensed Rights, including - technical modifications necessary to circumvent Effective - Technological Measures. For purposes of this Public License, - simply making modifications authorized by this Section 2(a) - (4) never produces Adapted Material. - - 5. Downstream recipients. - - a. Offer from the Licensor -- Licensed Material. Every - recipient of the Licensed Material automatically - receives an offer from the Licensor to exercise the - Licensed Rights under the terms and conditions of this - Public License. - - b. No downstream restrictions. You may not offer or impose - any additional or different terms or conditions on, or - apply any Effective Technological Measures to, the - Licensed Material if doing so restricts exercise of the - Licensed Rights by any recipient of the Licensed - Material. - - 6. No endorsement. Nothing in this Public License constitutes or - may be construed as permission to assert or imply that You - are, or that Your use of the Licensed Material is, connected - with, or sponsored, endorsed, or granted official status by, - the Licensor or others designated to receive attribution as - provided in Section 3(a)(1)(A)(i). - - b. Other rights. - - 1. Moral rights, such as the right of integrity, are not - licensed under this Public License, nor are publicity, - privacy, and/or other similar personality rights; however, to - the extent possible, the Licensor waives and/or agrees not to - assert any such rights held by the Licensor to the limited - extent necessary to allow You to exercise the Licensed - Rights, but not otherwise. - - 2. Patent and trademark rights are not licensed under this - Public License. - - 3. To the extent possible, the Licensor waives any right to - collect royalties from You for the exercise of the Licensed - Rights, whether directly or through a collecting society - under any voluntary or waivable statutory or compulsory - licensing scheme. In all other cases the Licensor expressly - reserves any right to collect such royalties. - - -Section 3 -- License Conditions. - -Your exercise of the Licensed Rights is expressly made subject to the -following conditions. - - a. Attribution. - - 1. If You Share the Licensed Material (including in modified - form), You must: - - a. retain the following if it is supplied by the Licensor - with the Licensed Material: - - i. identification of the creator(s) of the Licensed - Material and any others designated to receive - attribution, in any reasonable manner requested by - the Licensor (including by pseudonym if - designated); - - ii. a copyright notice; - - iii. a notice that refers to this Public License; - - iv. a notice that refers to the disclaimer of - warranties; - - v. a URI or hyperlink to the Licensed Material to the - extent reasonably practicable; - - b. indicate if You modified the Licensed Material and - retain an indication of any previous modifications; and - - c. indicate the Licensed Material is licensed under this - Public License, and include the text of, or the URI or - hyperlink to, this Public License. - - 2. You may satisfy the conditions in Section 3(a)(1) in any - reasonable manner based on the medium, means, and context in - which You Share the Licensed Material. For example, it may be - reasonable to satisfy the conditions by providing a URI or - hyperlink to a resource that includes the required - information. - - 3. If requested by the Licensor, You must remove any of the - information required by Section 3(a)(1)(A) to the extent - reasonably practicable. - - 4. If You Share Adapted Material You produce, the Adapter's - License You apply must not prevent recipients of the Adapted - Material from complying with this Public License. - - -Section 4 -- Sui Generis Database Rights. - -Where the Licensed Rights include Sui Generis Database Rights that -apply to Your use of the Licensed Material: - - a. for the avoidance of doubt, Section 2(a)(1) grants You the right - to extract, reuse, reproduce, and Share all or a substantial - portion of the contents of the database; - - b. if You include all or a substantial portion of the database - contents in a database in which You have Sui Generis Database - Rights, then the database in which You have Sui Generis Database - Rights (but not its individual contents) is Adapted Material; and - - c. You must comply with the conditions in Section 3(a) if You Share - all or a substantial portion of the contents of the database. - -For the avoidance of doubt, this Section 4 supplements and does not -replace Your obligations under this Public License where the Licensed -Rights include other Copyright and Similar Rights. - - -Section 5 -- Disclaimer of Warranties and Limitation of Liability. - - a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE - EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS - AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF - ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS, - IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION, - WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR - PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS, - ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT - KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT - ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU. - - b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE - TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION, - NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT, - INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES, - COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR - USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN - ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR - DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR - IN PART, THIS LIMITATION MAY NOT APPLY TO YOU. - - c. The disclaimer of warranties and limitation of liability provided - above shall be interpreted in a manner that, to the extent - possible, most closely approximates an absolute disclaimer and - waiver of all liability. - - -Section 6 -- Term and Termination. - - a. This Public License applies for the term of the Copyright and - Similar Rights licensed here. However, if You fail to comply with - this Public License, then Your rights under this Public License - terminate automatically. - - b. Where Your right to use the Licensed Material has terminated under - Section 6(a), it reinstates: - - 1. automatically as of the date the violation is cured, provided - it is cured within 30 days of Your discovery of the - violation; or - - 2. upon express reinstatement by the Licensor. - - For the avoidance of doubt, this Section 6(b) does not affect any - right the Licensor may have to seek remedies for Your violations - of this Public License. - - c. For the avoidance of doubt, the Licensor may also offer the - Licensed Material under separate terms or conditions or stop - distributing the Licensed Material at any time; however, doing so - will not terminate this Public License. - - d. Sections 1, 5, 6, 7, and 8 survive termination of this Public - License. - - -Section 7 -- Other Terms and Conditions. - - a. The Licensor shall not be bound by any additional or different - terms or conditions communicated by You unless expressly agreed. - - b. Any arrangements, understandings, or agreements regarding the - Licensed Material not stated herein are separate from and - independent of the terms and conditions of this Public License. - - -Section 8 -- Interpretation. - - a. For the avoidance of doubt, this Public License does not, and - shall not be interpreted to, reduce, limit, restrict, or impose - conditions on any use of the Licensed Material that could lawfully - be made without permission under this Public License. - - b. To the extent possible, if any provision of this Public License is - deemed unenforceable, it shall be automatically reformed to the - minimum extent necessary to make it enforceable. If the provision - cannot be reformed, it shall be severed from this Public License - without affecting the enforceability of the remaining terms and - conditions. - - c. No term or condition of this Public License will be waived and no - failure to comply consented to unless expressly agreed to by the - Licensor. - - d. Nothing in this Public License constitutes or may be interpreted - as a limitation upon, or waiver of, any privileges and immunities - that apply to the Licensor or You, including from the legal - processes of any jurisdiction or authority. - - -======================================================================= - -Creative Commons is not a party to its public licenses. -Notwithstanding, Creative Commons may elect to apply one of its public -licenses to material it publishes and in those instances will be -considered the "Licensor." Except for the limited purpose of indicating -that material is shared under a Creative Commons public license or as -otherwise permitted by the Creative Commons policies published at -creativecommons.org/policies, Creative Commons does not authorize the -use of the trademark "Creative Commons" or any other trademark or logo -of Creative Commons without its prior written consent including, -without limitation, in connection with any unauthorized modifications -to any of its public licenses or any other arrangements, -understandings, or agreements concerning use of licensed material. For -the avoidance of doubt, this paragraph does not form part of the public -licenses. - -Creative Commons may be contacted at creativecommons.org. diff --git a/LocalFolder/explainer.md b/LocalFolder/explainer.md deleted file mode 100644 index e21acd8aa..000000000 --- a/LocalFolder/explainer.md +++ /dev/null @@ -1,195 +0,0 @@ -# ApplicationData.LocalFolder Access From Microsoft Store PWA - -Author: [Lu Huang](https://github.com/luhuangMSFT) - -## Status of this Document - -This document is a starting point for engaging the community and standards bodies in developing collaborative solutions fit for standardization. As the solutions to problems described in this document progress along the standards-track, we will retain this document as an archive and use this section to keep the community up-to-date with the most current standards venue and content location of future work and discussions. - -* This document status: **Active** -* Expected venue: [Microsoft Edge Explainers](https://github.com/MicrosoftEdge/MSEdgeExplainers) -* **Current version: this document** - -## Introduction -[UWP apps](https://learn.microsoft.com/en-us/windows/uwp/get-started/universal-application-platform-guide) often make use of the [`ApplicationData.LocalFolder`](https://learn.microsoft.com/en-us/uwp/api/windows.storage.applicationdata.localfolder) WinRT API for access to local storage. This API manages storage in a system file directory unique to the [Package Family Name (PFN)](https://learn.microsoft.com/en-us/windows/apps/desktop/modernize/package-identity-overview#package-family-name) of the installed app package. A UWP app distributed by the [Microsoft Store](https://apps.microsoft.com/home) may be replaced with a PWA app with the same PFN. When this update takes place on client machines, existing files in the `LocalFolder` directory become inaccessible to the PWA as WinRT APIs are not directly exposed to the web. - -In this explainer, we propose a solution that allows Microsoft Store PWAs to read and delete files from the `LocalFolder` directory belonging to their PFN. This allows apps to provide a more seamless user experience after an update and to reclaim storage space. - -The proposed solution works by making use of the [Origin Private File System (OPFS)](https://web.dev/articles/origin-private-file-system) and the [File System Access](https://wicg.github.io/file-system-access/) APIs. It exposes an app's `LocalFolder` file system directory as an entry in the app origin's OPFS root directory. - -## Goals - -* Enable a Microsoft Store PWA to: - * read files from the `LocalFolder` directory specific to its PFN. - * delete any file or sub-directory in its `LocalFolder` directory. -* Avoid prompting end users to provide input or confirmation. -* Avoid naming collisions with other OPFS root entries. -* Ensure that new behavior described here is only exposed to apps that opt in. -* Avoid introducing a new Web API that is only available on the Edge browser. - -## Non-goals - -* Enable a Microsoft Store PWA to create new files or modify existing files in its `LocalFolder` directory. -* Support web apps not installed or managed by the Microsoft Store. -* Support web apps on non-Windows platforms. -* Standardization as a web specification. - -## Proposed Solution - -### Existing behavior - -[`navigator.storage.getDirectory()`](https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/getDirectory) is an existing API that returns a promise to a [FileSystemDirectoryHandle](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle) which represents the root of directory the origin's OPFS storage space. By default, an origin's OPFS root directory has no entries. - -### New behavior - -When configured correctly, the directory handle for OPFS root will contain an additional entry that represents the app's `LocalFolder` directory. This entry can be retrieved as a `FileSystemDirectoryHandle` by calling [`.entries()`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/entries) or [`.getDirectoryHandle(...)`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/getDirectoryHandle) on the OPFS root directory handle. - -Like other OPFS handles, the `LocalFolder` directory handle and its contents will have [`readwrite`](https://wicg.github.io/file-system-access/#dom-filesystempermissionmode-readwrite) permission by default. Unlike other handles with `readwrite` permission, a [FileSystemFileHandle](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle) from `LocalFolder` will always throw a [`NoModificationAllowedError`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createWritable#nomodificationallowederror) DOMException if [`createWritable()`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle/createWritable#exceptions) is used, or if [`getFileHandle()`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/getFileHandle) or [`getDirectoryHandle()`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/getDirectoryHandle) is used with the `create` option being true. This allows data in `LocalFolder` to be read and deleted but not created or edited. - -#### Configuration - -An entry for `LocalFolder` will only be visible in the Microsoft Store PWA context and when the [`related_applications`](https://developer.mozilla.org/en-US/docs/Web/Manifest/related_applications) field in the app's web app manifest is configured correctly. - -To be in a Microsoft Store PWA context: -* The PWA must be installed locally from the Microsoft Store -* `navigator.storage.getDirectory()` must be called from: - * A top level document - * From the main thread and not a Web Worker - * A document with a URL within the [scope](https://w3c.github.io/manifest/#understanding-scope) of the PWA - -In order to opt in to enabling `LocalFolder` access, the app needs to configure `related_applications` in its web app manifest to [identify the PFN of its Windows app package](https://web.dev/articles/get-installed-related-apps#tell_your_website_about_your_windows_app). Only the web app manifest part of the configuration needed to support `getInstalledRelatedApps` is needed here to enable `LocalFolder` access. - -```JSON - "related_applications": [{ - "platform": "windows", - "id": "PACKAGE_FAMILY_NAME!APPLICATION_ID" - }] -``` - -*Note: `LocalFolder` can be accessed from a document in any [`display-mode`](https://developer.mozilla.org/en-US/docs/Web/CSS/@media/display-mode). The loaded document does not have to be in an app window but does have to be in scope of the installed app.* - -#### Entry name - -The LocalFolder entry within the OPFS root directory can be found under the name `microsoft_store_app_local_folder_{PFN}` where `{PFN}` is the Package Family Name of the app. - -#### Deletion - -Contents in `LocalFolder` can be deleted while the `LocalFolder` entry cannot itself be deleted - this is to mimic the effects of the WinRT [`ClearAsync(...)`](https://learn.microsoft.com/en-us/uwp/api/windows.storage.applicationdata.clearasync?view=winrt-22621#windows-storage-applicationdata-clearasync(windows-storage-applicationdatalocality)) API. `LocalFolder`'s contents can be deleted by calling [`FileSystemHandle:remove()`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemHandle/remove) or [`FileSystemDirectoryHandle:removeEntry()`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/removeEntry). - -See example usage in [section below](#deleting-localfolder-and-its-contents). - -| Action | Result | -|--------|--------| -| `.remove()` on OPFS root handle | Will not enumerate or delete `LocalFolder` even if the [`recursive`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemHandle/remove#recursive) option is used. | -| `.removeEntry(...)` on OPFS root handle using `LocalFolder`'s correct entry name | Will not enumerate or delete `LocalFolder` even if the [`recursive`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/removeEntry#recursive) option is used. No DOMException will be thrown. This is consistent with calling .removeEntry() on names that cannot be found. | -| `.remove()` on `LocalFolder`'s handle | Will throw an [`InvalidModificationError`](https://developer.mozilla.org/en-US/docs/Web/API/DOMException#invalidmodificationerror) DOMException. Has no effect on `LocalFolder` directory and its contents. | -| `.remove()` or `.removeEntry(...)` on any handle within `LocalFolder` | Identified handles will be removed. | ------------------- - -#### Storage quota and eviction - -As the `LocalFolder` directory is in a system file directory separate from Chromium's storage location for OPFS, whether an entry for `LocalFolder` is presented in OPFS root's entries does not affect the available storage estimate returned by [`navigator.storage.estimate()`](https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/estimate). As `LocalFolder` takes up space on disk, clearing the contents of `LocalFolder` can increase the estimate of available storage. - -In Chromium, the underlying storage for OPFS does participate in [eviction](https://developer.mozilla.org/en-US/docs/Web/API/Storage_API/Storage_quotas_and_eviction_criteria#when_is_data_evicted) if an origin is not marked as [persistent](https://storage.spec.whatwg.org/#persistence). As the `LocalFolder` directory is in a system file directory separate from OPFS's underlying storage, it will not be affected by origin based storage eviction. - -#### Avoiding name collisions - -To avoid name collisions, the LocalFolder entry under the OPFS root directory is assigned a specific name that contains the Package Family Name. If an entry with the same name already exists under the OPFS root directory, an entry representing the LocalFolder directory will not be made visible until the existing entry is renamed or otherwise removed. - -### Examples - -#### Searching for LocalFolder - -```JS - // Using .getDirectoryHandle() - let opfsRoot = await navigator.storage.getDirectory(); - let localFolder = await opfsRoot.getDirectoryHandle("microsoft_store_app_local_folder_APPID.37853FC22B2CE_6rarf9sa4v8jt"); -``` -*Example: looking for the `LocalFolder` entry under the OPFS root directory.* - -*Note: the `create` parameter should be `{create:false}` to determine if the system `LocalFolder` is present. If `create` is true, `getDirectoryHandle` will always return a valid handle as it creates a directory handle of the same name in actual OPFS storage. If omitted, `create` is false by default.* - -```JS - // .entries() - let opfsRoot = await navigator.storage.getDirectory(); - for await (const [key, value] of opfsRoot.entries()) { - if (key === 'microsoft_store_app_local_folder_APPID.37853FC22B2CE_6rarf9sa4v8jt') { - // ... - } - } -``` -*Example: Looking for the `LocalFolder` entry by iterating through entries under the OPFS root directory.* - -#### Deleting LocalFolder and its contents - -```JS - let localStorage = await opfsRoot.getDirectoryHandle("microsoft_store_app_local_folder_APPID.37853FC22B2CE_6rarf9sa4v8jt"); - for await (const [key, value] of localStorage.entries()) { - value.remove({ recursive: true }); - } -``` -*Example: deleting contents of `LocalFolder` by calling `.remove()` on its child handles.* - -*Note: The [`recursive`](https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryHandle/removeEntry#recursive) parameter needs to be `true` to delete sub-directories recursively. By default, it is `false`. It has no effect on file handles.* - - -## Similar storage folders - -There are other WinRT storage APIs similar to LocalFolder that can be exposed the same way through OPFS directory handles, but are not currently planned to be supported. - -* [LocalCacheFolder](https://learn.microsoft.com/en-us/uwp/api/windows.storage.applicationdata.localfolder?view=winrt-22621) -* [RoamingFolder](https://learn.microsoft.com/en-us/uwp/api/windows.storage.applicationdata.roamingfolder?view=winrt-22621) -* [SharedLocalFolder](https://learn.microsoft.com/en-us/uwp/api/windows.storage.applicationdata.sharedlocalfolder?view=winrt-22621) -* [TemporaryFolder](https://learn.microsoft.com/en-us/uwp/api/windows.storage.applicationdata.temporaryfolder?view=winrt-22621) - -## Alternatives considered - -### New Web API - -We considered creating a new API on `navigator` to return a `FileSystemDirectoryHandle` of the app's LocalFolder directory. This was rejected primarily because of the long term cost of maintaining a non-standard API. - -### File migration - -We considered a solution that migrates the contents of the LocalFolder directory into OPFS storage. The drawbacks to this solution includes: - -* It could take a long time to migrate large files. There is no way for the calling code to register a callback to wait for migration to be completed without introducing a new API, negating the advantage of not creating a new API on `navigator`. -* Migration of files could fail due the lack of storage space and other I/O errors. There is no good way to communicate such errors to the calling code without introducing a new API. - -## Security risks - -1. A malicious party could attempt to gain access to files outside of the boundary of the `LocalFolder` directory. - -1. A malicious party could create executable files on a user-visible part of the file system without requiring the user to provide input through a file picker UI. - -1. Third party scripts could read data in `LocalFolder` even though the app developer is not aware of this feature. - -1. Scripts in embedded iframes could try to access data in `LocalFolder`. - -### Mitigations - -1. The `FileSystemHandle` design does not support walking up the directory structure. - -1. The `LocalFolder` directory handle and its contents will not allow the modification of existing files or creation of new files. This prevents the creation of executable files and the creation of files outside of the LocalFolder directory. Additionally, the implementation of the File System Access API in Chromium prevents creation of some executable file extensions. - -1. The app has to opt in explictly using the `related_applications` field in the web app manifest. The app developer must understand the risks involved and be responsible for the actions of third party scripts used. Most apps that do not intend to access `LocalFolder` will not expose it unknowingly to third parties. - -1. The `LocalFolder` handle entry will not be visible to cross-origin iframes as OPFS storage is partitioned by origin. - - -## Privacy risks - -1. `LocalFolder` data in the file system can be accessed by a PWA through a Web API without prompting the user for permission. There could be personally identifiable information (PII) contained within this data. - -1. Any script (including those from third-party origins that may not be owned by the application developer) that is loaded into a document from the same origin as the top-level frame can use the presence of a `LocalFolder` entry to determine if a Windows Store app associated with the origin is installed on the client machine. This information could potentially be used for fingerprinting. - * Inspecting `display-mode` is an existing method to check if a site is installed locally as an app but this method does not work when the user navigates the site in a normal browser tab or fullscreen. - * The fingerprinting surface increases but not significantly as there is an existing method to determine if apps are installed. - * As the installed app information would be different for each site, it cannot be easily used in a comparable fingerprint from different sites. - * App installation status changes due to user actions and cannot be reliably used to form a stable fingerprint identifying the client. - -### Mitigations - -1. The user consented to the app's use of local storage through app installation from the Microsoft Store. We think it is acceptable for a newer version of this app to access the data previously created using the same PFN. The app (both the UWP and PWA implementation) has permission from the user to store and access the data even if it contains PII. The app needs to take care to protect user data. - -1. Requiring the app to include the `related_applications` in its web app manifest limits the risk to apps that publicly declares a relation to a Windows app package. - - diff --git a/LocalReferenceLinkRel/explainer.md b/LocalReferenceLinkRel/explainer.md deleted file mode 100644 index b72f29a9a..000000000 --- a/LocalReferenceLinkRel/explainer.md +++ /dev/null @@ -1,352 +0,0 @@ -# Local References In `` Tags - -## Authors - -- Kurt Catti-Schmidt -- Noam Rosenthal - -## Participate - -- [Issue tracker](https://github.com/MicrosoftEdge/MSEdgeExplainers/labels/LRLR) -- [Discussion forum](https://github.com/whatwg/html/issues/11019) - -## Status of this Document - -This document is intended as a starting point for engaging the community and -standards bodies in developing collaborative solutions fit for standardization. -As the solutions to problems described in this document progress along the -standards-track, we will retain this document as an archive and use this section -to keep the community up-to-date with the most current standards venue and -content location of future work and discussions. - -- This document status: **Active** -- Expected venue: [WHAT Working Group](https://whatwg.org/) -- Current version: this document - -## Introduction - -Modern web development practices have converged towards building reusable -components instead of building monolithic documents. Frameworks such as React -and Angular have popularized this approach, which has made its way natively to -the web platform via Web Components. - -Web Components allow for defining custom elements, and these custom elements -often rely on Shadow DOM for ID encapsulation. But Shadow DOM doesn't just -encapsulate ID's - it encapsulates style as well. The current preferred approach -for styling elements under a Shadow DOM is through `` tags, but these tags -have several downsides, namely that they must be an external file or a dataURI. -The performance of an external file and developer ergonomics of generating a -dataURI make both of these options difficult to use. Because of that, developers -using Custom Elements have expressed strong interest in addressing these issues. - -This explainer proposes a solution to this situation by allowing another option -for sharing styles via the `` tag - local references to ` -

Outside Shadow DOM

- -``` - -With this functionality, the text "Inside Shadow DOM" will by styled blue, due -to the `` node's stylesheet -applying (along with the `color: blue` rule applying via the `p` selector in -that stylesheet). - -### Scoping - -```html - - -

- Outside Shadow DOM. Styles defined inside the Shadow Root are not applied, so - this text is not blue. -

-``` - -Due to existing Shadow DOM scoping behaviors, ` -

Inside Shadow DOM

- - -``` - -

- Styles defined inside the parent Shadow Root are not applied, so "Inside Nested Shadow DOM" is not blue. -

- -```html - - -``` - -

- Styles defined inside the sibling Shadow Root are not applied, so "Inside Sibling Shadow DOM" is not blue. -

- -In contrast to this proposal, [Declarative CSS Modules](../ShadowDOM/explainer.md) always have global scope. Shadow DOM scoping could be modified though other means, as discussed -in [this thread](https://github.com/whatwg/html/issues/11364). All of the suggestions in that thread would -allow for this feature to work with any shadow root, regardless of scope. - -For example, the "Referencetarget inspired solution on `