From 4a4b81cf51524b5f74a877e8e7ce66881b5ac383 Mon Sep 17 00:00:00 2001 From: Tab Atkins Date: Tue, 12 Dec 2017 12:11:29 -0800 Subject: [PATCH 1/3] Rewrite :visited rules to match reality, based on dbaron's spec. --- selectors-4/Overview.bs | 194 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 176 insertions(+), 18 deletions(-) diff --git a/selectors-4/Overview.bs b/selectors-4/Overview.bs index 04da7ff3ef1..cd5ddebcd44 100644 --- a/selectors-4/Overview.bs +++ b/selectors-4/Overview.bs @@ -27,6 +27,12 @@ Ignored Vars: identifier, extended filtering @@ -1906,35 +1912,187 @@ The Hyperlink Pseudo-class: '':any-link'' - User agents commonly display unvisited hyperlinks differently from - previously visited ones. Selectors - provides the pseudo-classes :link and - :visited to distinguish them: + User agents commonly display unvisited hyperlinks + differently from previously visited ones. + Selectors provides the pseudo-classes + :link and :visited + to distinguish them. + Roughly: - After some amount of time, user agents may choose to return a visited link to the (unvisited) '':link'' state. - The two states are mutually exclusive. + The two states are mutually exclusive + (no element can ever match '':link:visited''), + but their actual interaction is more complex than that, + for privacy reasons explained below. + + In full, the behavior is actually that the '':link'' pseudo-class applies to all links, + but the '':visited'' pseudo-class applies the following behavior and restrictions: + +
+ To apply :visited styling to an element |el|: + + 1. Check to see if |el| has a relevant link. + The relevant link for an element + is the element itself, + if it's an element that would match '':link'', + or else the closest ancestor that would match '':link''. + + If |el| does not have a relevant link, + then '':visited'' has no effect on the element. + + 2. Otherwise, compute |el|’s style, + treating only its relevant link as matching '':visited'' + (and thus, not '':link''). + All other links must be treated as matching '':link''. + + 3. From the styling results, + record the values of the allowed :visited properties: + + * 'color' + * 'background-color' + * the 'border-color' sub-properties + * 'outline-color' + * 'column-rule-color' + * 'fill-color' + * 'stroke-color' + + These are |el|’s :visited styles. + + 4. If |el|’s relevant link is actually visited, + then for the allowed :visited properties, + use |el|’s :visited styles for any purposes + that won't allow the document itself to tell what the style is; + in all other contexts, + use the normal, all-'':link'', styling. +
- The following selector represents links carrying class - footnote and already visited: + For example, with the following common example style sheet: + +
+		:link { color: blue; }
+		:visited { color: purple; }
+		
+ + Visited links show up as purple only for rendering to the screen. + If the document uses JS to query the element’s style, + it will instead report ''color: blue'', + as if the element only matched '':link''. + + Similarly, if HTML's <{canvas}> element develops an ability to render HTML to the canvas with CSS styling, + then either the links would be styled blue + (because by default, <{canvas}> gives the document the ability to inspect the results), + or the visited links will be purple, + but <{canvas}> will restrict the page's ability to inspect the pixels of the result. +
+ +
+ The special '':visited'' behavior also means + that a single element might be styled by a mix of '':link'' and '':visited'' rules. + For example: + +
+		:link {
+			color: blue;
+			background-image: url("unvisited.png");
+		}
+		:visited {
+			color: purple;
+			background-image: url("visited.png");
+		}
+		
+ + With this style sheet, + a visited link will be colored purple, + but have the "unvisited.png" background, + because 'background-image' is not one of the allowed :visited properties, + and so whatever value it gets from a '':visited'' style + is not recorded in the element's :visited styles. +
+ +
+ Another consequence of the special '':visited'' behavior + is that some selectors that look reasonable + will never match. + For example: + +
+		:visited + span { color: red; }
+		
-
.footnote:visited 
+ Even tho this style sheet is applying an allowed :visited property to the span element, + the span’s relevant link can never be its previous sibling + (it can only be the span or one of its ancestors). + Since the [=apply :visited styling|:visible styling algorithm=] + only checks if the element's relevant link is '':visited'' + (and treats all other links on the page as unvisited), + this selector will never actually match anything.
- Since it is possible for style sheet authors to abuse the :link and :visited pseudo-classes - to determine which sites a user has visited without the user's consent, - UAs may treat all links as unvisited links - or implement other measures to preserve the user's privacy - while rendering visited and unvisited links differently. +
+ Why does '':visited'' have this strange behavior and restrictions? + + Originally, '':link'' and '':visited'' did indeed work in the simple way + described at the beginning of this chapter, + like two ordinary mutually-exclusive pseudo-classes. + It was eventually discovered, however, + that this allowed pages to determine what other sites a user had visited, + by listing a bunch of links off-screen + and using JS to tell whether they were styled with '':link'' or '':visited''. + + This was bad both for user's privacy + (for obvious reasons, sharing a user's browsing history with everyone is bad) + and for their security + (phishing attacks could, for example, tell which bank website a user visited, + and render their phishing page to match that bank specifically, + making it more likely to fool the user). + + Rendering visited links in a different style was too useful to throw away entirely, + so instead user agents developed the above algorithm + to "lie" about the style in some contexts, + but still rendering the link with '':visited'' styles normally, + without paying too much extra computation time. + + The very limited list of allowed :visited properties + further limits the possibility of a page figuring things out. + If '':visited'' could apply any property, + pages could still, + for example, + apply a particular 'background-image' only when a link is visited, + and then record whether that image was loaded from their server, + giving them the exact information we were trying to hide! + Similarly, any layout-affecting property, + like 'width', + might affect the positions of other elements on the page; + lying about these knock-on effects to hide the styling + would be much more expensive for the user agent. + Limiting it just a handful of properties that can only apply colors + ensures that as little information is leakable as possible. +
+ + User agents may treat all links as unvisited at all times. + (In particular, they can offer a user preference for this, + allowing the user to decide whether the benefit of seeing when a link has been visited + is more or less important + than the possibility of that information being leaked to random pages on the internet.) + + If the user agent uses some form of "link pre-loading" + to find resource urls in a stylesheet + and begin loading them before it's known that they'll actually be used on the page, + they might find a url that would only be applied by an element matching '':visited'' + (which is impossible, as '':visited'' rules can't cause any properties to apply that load images). + User agents with this behavior must ensure that either + such urls are never loaded (preferable), + or at least that such urls are loaded at the same time + regardless of whether a link is visited or not. +

The Target Pseudo-class: '':target''

@@ -3293,7 +3451,7 @@ Grammar in a <>. In some circumstances, it can be followed by more <>s or <>s - in the same <>, + in the same <>, but these are specified on a case-by-case basis. (For example, the user-action pseudo-classes are allowed after any pseudo-element, and the tree-abiding pseudo-elements From fd857981a3a085d4bab2900f3072e4e3f1ae4d9b Mon Sep 17 00:00:00 2001 From: Tab Atkins Date: Tue, 12 Dec 2017 12:38:20 -0800 Subject: [PATCH 2/3] typo --- selectors-4/Overview.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selectors-4/Overview.bs b/selectors-4/Overview.bs index cd5ddebcd44..fde4d97bef8 100644 --- a/selectors-4/Overview.bs +++ b/selectors-4/Overview.bs @@ -2073,7 +2073,7 @@ The Link History Pseudo-classes: '':link'' and '':visited'' might affect the positions of other elements on the page; lying about these knock-on effects to hide the styling would be much more expensive for the user agent. - Limiting it just a handful of properties that can only apply colors + Limiting it to just a handful of properties that can only apply colors ensures that as little information is leakable as possible. From 93079d589f43be04d806d63397eea1a80596cfd1 Mon Sep 17 00:00:00 2001 From: Tab Atkins Jr Date: Tue, 30 Jan 2018 16:49:12 -0800 Subject: [PATCH 3/3] Accept glazou's suggested wording tweak --- selectors-4/Overview.bs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/selectors-4/Overview.bs b/selectors-4/Overview.bs index fde4d97bef8..abdc7fc59e5 100644 --- a/selectors-4/Overview.bs +++ b/selectors-4/Overview.bs @@ -1912,7 +1912,7 @@ The Hyperlink Pseudo-class: '':any-link'' - User agents commonly display unvisited hyperlinks + User agents commonly render unvisited hyperlinks differently from previously visited ones. Selectors provides the pseudo-classes :link and :visited