<?xml version="1.0" encoding="utf-8"?>
<feed xml:lang="en-us" xmlns="http://www.w3.org/2005/Atom"><title>Simon Willison's Weblog: chrome</title><link href="http://simonwillison.net/" rel="alternate"/><link href="http://simonwillison.net/tags/chrome.atom" rel="self"/><id>http://simonwillison.net/</id><updated>2025-12-22T16:10:30+00:00</updated><author><name>Simon Willison</name></author><entry><title>Using Claude in Chrome to navigate out the Cloudflare dashboard</title><link href="https://simonwillison.net/2025/Dec/22/claude-chrome-cloudflare/#atom-tag" rel="alternate"/><published>2025-12-22T16:10:30+00:00</published><updated>2025-12-22T16:10:30+00:00</updated><id>https://simonwillison.net/2025/Dec/22/claude-chrome-cloudflare/#atom-tag</id><summary type="html">
    &lt;p&gt;I just had my first success using a browser agent - in this case the &lt;a href="https://support.claude.com/en/articles/12012173-getting-started-with-claude-in-chrome"&gt;Claude in Chrome extension&lt;/a&gt; - to solve an actual problem.&lt;/p&gt;
&lt;p&gt;A while ago I set things up so anything served from the &lt;code&gt;https://static.simonwillison.net/static/cors-allow/&lt;/code&gt; directory of my S3 bucket would have open &lt;code&gt;Access-Control-Allow-Origin: *&lt;/code&gt; headers. This is useful for hosting files online that can be loaded into web applications hosted on other domains.&lt;/p&gt;
&lt;p&gt;Problem is I couldn't remember how I did it! I initially thought it was an S3 setting, but it turns out S3 lets you set CORS at the bucket-level but not for individual prefixes.&lt;/p&gt;
&lt;p&gt;I then suspected Cloudflare, but I find the Cloudflare dashboard really difficult to navigate.&lt;/p&gt;
&lt;p&gt;So I decided to give Claude in Chrome a go. I installed and enabled the extension (you then have to click the little puzzle icon and click "pin" next to Claude for the icon to appear, I had to ask Claude itself for help figuring that out), signed into Cloudflare, opened the Claude panel and prompted:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;I'm trying to figure out how come all pages under http://static.simonwillison.net/static/cors/ have an open CORS policy, I think I set that up through Cloudflare but I can't figure out where&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Off it went. It took 1m45s to find exactly what I needed.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Chrome - on the left is the Cloudflare dashboard. On the right a Claude chat panel which shows the final result: Summary  Found it! Your open CORS policy for the /static/cors/ directory on static.simonwillison.net is configured through Cloudflare Transform Rules, specifically a Response Header Transform Rule named static.simonwillis.net/static/cors-allow/*.  Here's exactly what's set up:  Rule Configuration:      Type: HTTP Response Header Transform Rule     Applies to: Any URL matching https://static.simonwillison.net/static/cors-allow/* (wildcard match)     Action: Modifies the response header access-control-allow-origin and sets it to *  This is why all pages under that path have an open CORS policy — the * value for Access-Control-Allow-Origin means any origin can access those resources.  Where to find/modify it in the dashboard:      Go to your domain settings (simonwillison.net)     Navigate to Rules &amp;gt; Rules Overview (in the left sidebar)     Scroll down to Response Header Transform Rules (should show 2 active)     Click on the rule named static.simonwillis.net/static/cors-allow/* to view or edit it  " src="https://static.simonwillison.net/static/2025/claude-chrome-cloudflare.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Claude's conclusion:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Found it! Your open CORS policy for the &lt;code&gt;/static/cors/&lt;/code&gt; directory on &lt;code&gt;static.simonwillison.net&lt;/code&gt; is configured through &lt;strong&gt;Cloudflare Transform Rules&lt;/strong&gt;, specifically a &lt;strong&gt;Response Header Transform Rule&lt;/strong&gt; named &lt;code&gt;static.simonwillis.net/static/cors-allow/*&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There's no "share transcript" option but I used copy and paste and two gnarly Claude Code sessions (&lt;a href="https://gistpreview.github.io/?56adf4212345d9967c22aab1362b847b"&gt;one&lt;/a&gt;, &lt;a href="https://gistpreview.github.io/?1d5f524616bef403cdde4bc92da5b0ba"&gt;two&lt;/a&gt;) to turn it into an HTML transcript which &lt;a href="https://static.simonwillison.net/static/2025/claude-chrome-transcript.html"&gt;you can take a look at here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I remain deeply skeptical of the entire browsing agent category due to my concerns about prompt injection risks—I watched what it was doing here like a &lt;em&gt;hawk&lt;/em&gt;—but I have to admit this was a very positive experience.&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cloudflare"&gt;cloudflare&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prompt-injection"&gt;prompt-injection&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/anthropic"&gt;anthropic&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cors"&gt;cors&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-agents"&gt;ai-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/browser-agents"&gt;browser-agents&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="ai"/><category term="cloudflare"/><category term="prompt-injection"/><category term="generative-ai"/><category term="llms"/><category term="anthropic"/><category term="claude"/><category term="cors"/><category term="ai-agents"/><category term="browser-agents"/></entry><entry><title>Removing XSLT for a more secure browser</title><link href="https://simonwillison.net/2025/Nov/5/removing-xslt/#atom-tag" rel="alternate"/><published>2025-11-05T22:24:57+00:00</published><updated>2025-11-05T22:24:57+00:00</updated><id>https://simonwillison.net/2025/Nov/5/removing-xslt/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://developer.chrome.com/docs/web-platform/deprecating-xslt"&gt;Removing XSLT for a more secure browser&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Previously discussed &lt;a href="https://simonwillison.net/2025/Aug/19/xslt/"&gt;back in August&lt;/a&gt;, it looks like it's now official:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Chrome intends to deprecate and remove XSLT from the browser. [...] We intend to remove support from version 155 (November 17, 2026). The &lt;a href="https://github.com/mozilla/standards-positions/issues/1287#issuecomment-3227145793"&gt;Firefox&lt;/a&gt; and &lt;a href="https://github.com/whatwg/html/issues/11523#issuecomment-3149280766"&gt;WebKit&lt;/a&gt; projects have also indicated plans to remove XSLT from their browser engines. [...]&lt;/p&gt;
&lt;p&gt;The continued inclusion of XSLT 1.0 in web browsers presents a significant and unnecessary security risk. The underlying libraries that process these transformations, such as &lt;a href="https://github.com/GNOME/libxslt"&gt;libxslt&lt;/a&gt; (used by Chromium browsers), are complex, aging C/C++ codebases. This type of code is notoriously susceptible to memory safety vulnerabilities like buffer overflows, which can lead to arbitrary code execution.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I mostly encounter XSLT on people's Atom/RSS feeds, converting those to a more readable format in case someone should navigate directly to that link. Jake Archibald &lt;a href="https://jakearchibald.com/2025/making-xml-human-readable-without-xslt/"&gt;shared an alternative solution to that&lt;/a&gt; back in September.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=45823059"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-standards"&gt;web-standards&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/xml"&gt;xml&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/xslt"&gt;xslt&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jake-archibald"&gt;jake-archibald&lt;/a&gt;&lt;/p&gt;



</summary><category term="browsers"/><category term="chrome"/><category term="security"/><category term="web-standards"/><category term="xml"/><category term="xslt"/><category term="jake-archibald"/></entry><entry><title>Introducing ChatGPT Atlas</title><link href="https://simonwillison.net/2025/Oct/21/introducing-chatgpt-atlas/#atom-tag" rel="alternate"/><published>2025-10-21T18:45:13+00:00</published><updated>2025-10-21T18:45:13+00:00</updated><id>https://simonwillison.net/2025/Oct/21/introducing-chatgpt-atlas/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://openai.com/index/introducing-chatgpt-atlas/"&gt;Introducing ChatGPT Atlas&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Last year OpenAI &lt;a href="https://www.searchenginejournal.com/openai-hires-former-chrome-engineer-eyes-browser-battle/533533/"&gt;hired Chrome engineer Darin Fisher&lt;/a&gt;, which sparked speculation they might have their own browser in the pipeline. Today it arrived.&lt;/p&gt;
&lt;p&gt;ChatGPT Atlas is a Mac-only web browser with a variety of ChatGPT-enabled features. You can bring up a chat panel next to a web page, which will automatically be populated with the context of that page.&lt;/p&gt;
&lt;p&gt;The "browser memories" feature is particularly notable, &lt;a href="https://help.openai.com/en/articles/12591856-chatgpt-atlas-release-notes"&gt;described here&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If you turn on browser memories, ChatGPT will remember key details from your web browsing to improve chat responses and offer smarter suggestions—like retrieving a webpage you read a while ago. Browser memories are private to your account and under your control. You can view them all in settings, archive ones that are no longer relevant, and clear your browsing history to delete them. &lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Atlas also has an experimental "agent mode" where ChatGPT can take over navigating and interacting with the page for you, accompanied by a weird sparkle overlay effect:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot of Simon Willison's Weblog showing search results for &amp;quot;browser agents&amp;quot; with 38 results on page 1 of 2. The first result is titled &amp;quot;Agentic Browser Security: Indirect Prompt Injection in Perplexity Comet&amp;quot; and discusses security vulnerabilities in LLM-powered browser extensions. A tooltip shows &amp;quot;Opening the first result&amp;quot; and on the right side is a ChatGPT interface panel titled &amp;quot;Simon Willison's Weblog&amp;quot; with text explaining &amp;quot;Use agent mode search this site for browser agents&amp;quot; and &amp;quot;Opening the first result&amp;quot; with a description of the research intent. At the bottom of the screen is a browser notification showing &amp;quot;browser agents&amp;quot; in posts with &amp;quot;Take control&amp;quot; and &amp;quot;Stop&amp;quot; buttons." src="https://static.simonwillison.net/static/2025/chatgpt-atlas.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;Here's how the &lt;a href="https://help.openai.com/en/articles/12591856-chatgpt-atlas-release-notes"&gt;help page&lt;/a&gt; describes that mode:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;In agent mode, ChatGPT can complete end to end tasks for you like researching a meal plan, making a list of ingredients, and adding the groceries to a shopping cart ready for delivery. You're always in control: ChatGPT is trained to ask before taking many important actions, and you can pause, interrupt, or take over the browser at any time.&lt;/p&gt;
&lt;p&gt;Agent mode runs also operates under boundaries:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;System access: Cannot run code in the browser, download files, or install extensions.&lt;/li&gt;
&lt;li&gt;Data access: Cannot access other apps on your computer or your file system, read or write ChatGPT memories, access saved passwords, or use autofill data.&lt;/li&gt;
&lt;li&gt;Browsing activity: Pages ChatGPT visits in agent mode are not added to your browsing history.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can also choose to run agent in logged out mode, and ChatGPT won't use any pre-existing cookies and won't be logged into any of your online accounts without your specific approval.&lt;/p&gt;
&lt;p&gt;These efforts don't eliminate every risk; users should still use caution and monitor ChatGPT activities when using agent mode.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I continue to find this entire category of &lt;a href="https://simonwillison.net/tags/browser-agents/"&gt;browser agents&lt;/a&gt; &lt;em&gt;deeply&lt;/em&gt; confusing.&lt;/p&gt;
&lt;p&gt;The security and privacy risks involved here still feel insurmountably high to me - I certainly won't be trusting any of these products until a bunch of security researchers have given them a very thorough beating.&lt;/p&gt;
&lt;p&gt;I'd like to see a &lt;em&gt;deep&lt;/em&gt; explanation of the steps Atlas takes to avoid prompt injection attacks. Right now it looks like the main defense is expecting the user to carefully watch what agent mode is doing at all times!&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;Update&lt;/strong&gt;: OpenAI's CISO Dane Stuckey provided exactly that &lt;a href="https://simonwillison.net/2025/Oct/22/openai-ciso-on-atlas/"&gt;the day after the launch&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;/p&gt;
&lt;p&gt;I also find these products pretty unexciting to use. I tried out agent mode and it was like watching a first-time computer user painstakingly learn to use a mouse for the first time. I have yet to find my own use-cases for when this kind of interaction feels useful to me, though I'm not ruling that out.&lt;/p&gt;
&lt;p&gt;There was one other detail in the announcement post that caught my eye:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Website owners can also add &lt;a href="https://help.openai.com/en/articles/12627856-publishers-and-developers-faq#h_30e9aae450"&gt;ARIA&lt;/a&gt; tags to improve how ChatGPT agent works for their websites in Atlas.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Which links to this:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;ChatGPT Atlas uses ARIA tags---the same labels and roles that support screen readers---to interpret page structure and interactive elements. To improve compatibility, follow &lt;a href="https://www.w3.org/WAI/ARIA/apg/"&gt;WAI-ARIA best practices&lt;/a&gt; by adding descriptive roles, labels, and states to interactive elements like buttons, menus, and forms. This helps ChatGPT recognize what each element does and interact with your site more accurately.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A neat reminder that AI "agents" share many of the characteristics of assistive technologies, and benefit from the same affordances.&lt;/p&gt;
&lt;p&gt;The Atlas user-agent is &lt;code&gt;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/141.0.0.0 Safari/537.36&lt;/code&gt; - identical to the user-agent I get for the latest Google Chrome on macOS.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=45658479"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/accessibility"&gt;accessibility&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/aria"&gt;aria&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/privacy"&gt;privacy&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/openai"&gt;openai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prompt-injection"&gt;prompt-injection&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-agents"&gt;ai-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/browser-agents"&gt;browser-agents&lt;/a&gt;&lt;/p&gt;



</summary><category term="accessibility"/><category term="aria"/><category term="browsers"/><category term="chrome"/><category term="privacy"/><category term="security"/><category term="ai"/><category term="openai"/><category term="prompt-injection"/><category term="generative-ai"/><category term="ai-agents"/><category term="browser-agents"/></entry><entry><title>Google antitrust remedies</title><link href="https://simonwillison.net/2025/Sep/3/antitrust/#atom-tag" rel="alternate"/><published>2025-09-03T08:56:30+00:00</published><updated>2025-09-03T08:56:30+00:00</updated><id>https://simonwillison.net/2025/Sep/3/antitrust/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://storage.courtlistener.com/recap/gov.uscourts.dcd.223205/gov.uscourts.dcd.223205.1436.0_1.pdf"&gt;gov.uscourts.dcd.223205.1436.0_1.pdf&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Here's the 230 page PDF ruling on the 2023 &lt;a href="https://en.wikipedia.org/wiki/United_States_v._Google_LLC_(2023)"&gt;United States v. Google LLC federal antitrust case&lt;/a&gt; - the case that could have resulted in Google selling off Chrome and cutting most of Mozilla's funding.&lt;/p&gt;
&lt;p&gt;I made it through the first dozen pages - it's actually quite readable.&lt;/p&gt;
&lt;p&gt;It opens with a clear summary of the case so far, bold highlights mine:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Last year, this court ruled that Defendant Google LLC had violated Section 2 of the Sherman Act: “Google is a monopolist, and it has acted as one to maintain its monopoly.” &lt;strong&gt;The court found that, for more than a decade, Google had entered into distribution agreements with browser developers, original equipment manufacturers, and wireless carriers to be the out-of-the box, default general search engine (“GSE”) at key search access points&lt;/strong&gt;. These access points were the most efficient channels for distributing a GSE, and Google paid billions to lock them up. The agreements harmed competition. &lt;strong&gt;They prevented rivals from accumulating the queries and associated data, or scale, to effectively compete and discouraged investment and entry into the market&lt;/strong&gt;. And they enabled Google to earn monopoly profits from its search text ads, to amass an unparalleled volume of scale to improve its search product, and to remain the default GSE without fear of being displaced. Taken together, these agreements effectively “froze” the search ecosystem, resulting in markets in which Google has “no true competitor.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;There's an interesting generative AI twist: when the case was first argued in 2023 generative AI wasn't an influential issue, but more recently Google seem to be arguing that it is an existential threat that they need to be able to take on without additional hindrance:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The emergence of GenAl changed the course of this case. No witness at the liability trial testified that GenAl products posed a near-term threat to GSEs. &lt;strong&gt;The very first witness at the remedies hearing, by contrast, placed GenAl front and center as a nascent competitive threat&lt;/strong&gt;. These remedies proceedings thus have been as much about promoting competition among GSEs as ensuring that Google’s dominance in search does not carry over into the GenAlI space. Many of Plaintiffs’ proposed remedies are crafted with that latter objective in mind.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I liked this note about the court's challenges in issuing effective remedies:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Notwithstanding this power, courts must approach the task of crafting remedies with a healthy dose of humility. This court has done so. It has no expertise in the business of GSEs, the buying and selling of search text ads, or the engineering of GenAl technologies. &lt;strong&gt;And, unlike the typical case where the court’s job is to resolve a dispute based on historic facts, here the court is asked to gaze into a crystal ball and look to the future. Not exactly a judge’s forte&lt;/strong&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;On to the remedies. These ones looked particularly important to me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Google will be barred from entering or maintaining any exclusive contract
relating to the distribution of Google Search, Chrome, Google Assistant,
and the Gemini app. [...]&lt;/li&gt;
&lt;li&gt;Google will not be required to divest Chrome; nor will the court include a
contingent divestiture of the Android operating system in the final
judgment. Plaintiffs overreached in seeking forced divesture of these key
assets, which Google did not use to effect any illegal restraints. [...]&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;I guess Perplexity &lt;a href="https://www.bbc.co.uk/news/articles/c3dpr0kkyz4o"&gt;won't be buying Chrome&lt;/a&gt; then!&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Google will not be barred from making payments or offering other
consideration to distribution partners for preloading or placement of Google
Search, Chrome, or its GenAl products. &lt;strong&gt;Cutting off payments from Google
almost certainly will impose substantial —in some cases, crippling—
downstream harms to distribution partners&lt;/strong&gt;, related markets, and consumers,
which counsels against a broad payment ban.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;That looks like a huge sigh of relief for Mozilla, who were at risk of losing a sizable portion of their income if Google's search distribution revenue were to be cut off.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=45108548"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/law"&gt;law&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mozilla"&gt;mozilla&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="google"/><category term="law"/><category term="mozilla"/><category term="generative-ai"/></entry><entry><title>Piloting Claude for Chrome</title><link href="https://simonwillison.net/2025/Aug/26/piloting-claude-for-chrome/#atom-tag" rel="alternate"/><published>2025-08-26T22:43:25+00:00</published><updated>2025-08-26T22:43:25+00:00</updated><id>https://simonwillison.net/2025/Aug/26/piloting-claude-for-chrome/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.anthropic.com/news/claude-for-chrome"&gt;Piloting Claude for Chrome&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Two days ago &lt;a href="https://simonwillison.net/2025/Aug/25/agentic-browser-security/"&gt;I said&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I strongly expect that the &lt;em&gt;entire concept&lt;/em&gt; of an agentic browser extension is fatally flawed and cannot be built safely.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Today Anthropic announced their own take on this pattern, implemented as an invite-only preview Chrome extension.&lt;/p&gt;
&lt;p&gt;To their credit, the majority of the &lt;a href="https://www.anthropic.com/news/claude-for-chrome"&gt;blog post&lt;/a&gt; and accompanying &lt;a href="https://support.anthropic.com/en/articles/12012173-getting-started-with-claude-for-chrome"&gt;support article&lt;/a&gt; is information about the security risks. From their post:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Just as people encounter phishing attempts in their inboxes, browser-using AIs face prompt injection attacks—where malicious actors hide instructions in websites, emails, or documents to trick AIs into harmful actions without users' knowledge (like hidden text saying "disregard previous instructions and do [malicious action] instead").&lt;/p&gt;
&lt;p&gt;Prompt injection attacks can cause AIs to delete files, steal data, or make financial transactions. This isn't speculation: we’ve run “red-teaming” experiments to test Claude for Chrome and, without mitigations, we’ve found some concerning results.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Their 123 adversarial prompt injection test cases saw a 23.6% attack success rate when operating in "autonomous mode". They added mitigations:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When we added safety mitigations to autonomous mode, we reduced the attack success rate of 23.6% to 11.2%&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I would argue that 11.2% is still a catastrophic failure rate. In the absence of 100% reliable protection I have trouble imagining a world in which it's a good idea to unleash this pattern.&lt;/p&gt;
&lt;p&gt;Anthropic don't recommend autonomous mode - where the extension can act without human intervention. Their default configuration instead requires users to be much more hands-on:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Site-level permissions&lt;/strong&gt;: Users can grant or revoke Claude's access to specific websites at any time in the Settings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Action confirmations&lt;/strong&gt;: Claude asks users before taking high-risk actions like publishing, purchasing, or sharing personal data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;I really hate being stop energy on this topic. The demand for browser automation driven by LLMs is significant, and I can see why. Anthropic's approach here is the most open-eyed I've seen yet but it still feels doomed to failure to me.&lt;/p&gt;
&lt;p&gt;I don't think it's reasonable to expect end users to make good decisions about the security risks of this pattern.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prompt-injection"&gt;prompt-injection&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/anthropic"&gt;anthropic&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-agents"&gt;ai-agents&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/browser-agents"&gt;browser-agents&lt;/a&gt;&lt;/p&gt;



</summary><category term="browsers"/><category term="chrome"/><category term="security"/><category term="ai"/><category term="prompt-injection"/><category term="generative-ai"/><category term="llms"/><category term="anthropic"/><category term="claude"/><category term="ai-agents"/><category term="browser-agents"/></entry><entry><title>Chromium Docs: The Rule Of 2</title><link href="https://simonwillison.net/2025/Aug/11/the-rule-of-2/#atom-tag" rel="alternate"/><published>2025-08-11T04:02:19+00:00</published><updated>2025-08-11T04:02:19+00:00</updated><id>https://simonwillison.net/2025/Aug/11/the-rule-of-2/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://chromium.googlesource.com/chromium/src/+/main/docs/security/rule-of-2.md"&gt;Chromium Docs: The Rule Of 2&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Alex Russell &lt;a href="https://toot.cafe/@slightlyoff/114999510361121718"&gt;pointed me&lt;/a&gt; to this principle in the Chromium security documentation as similar to my description of &lt;a href="https://simonwillison.net/2025/Aug/9/bay-area-ai/"&gt;the lethal trifecta&lt;/a&gt;. First added &lt;a href="https://github.com/chromium/chromium/commit/aef94dd0e444605a16be26cba96aa477bc7fc3f5"&gt;in 2019&lt;/a&gt;, the Chromium guideline states:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When you write code to parse, evaluate, or otherwise handle untrustworthy inputs from the Internet — which is almost everything we do in a web browser! — we like to follow a simple rule to make sure it's safe enough to do so. The Rule Of 2 is: Pick no more than 2 of&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;untrustworthy inputs;&lt;/li&gt;
&lt;li&gt;unsafe implementation language; and&lt;/li&gt;
&lt;li&gt;high privilege.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Venn diagram showing you should always use
a safe language, a sandbox, or not be processing untrustworthy inputs in the first
place." src="https://static.simonwillison.net/static/2025/rule-of-2.png" /&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Chromium uses this design pattern to help try to avoid the high severity memory safety bugs that come when untrustworthy inputs are handled by code running at high privilege.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Chrome Security Team will generally not approve landing a CL or new feature that involves all 3 of untrustworthy inputs, unsafe language, and high privilege. To solve this problem, you need to get rid of at least 1 of those 3 things.&lt;/p&gt;
&lt;/blockquote&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/alex-russell"&gt;alex-russell&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/lethal-trifecta"&gt;lethal-trifecta&lt;/a&gt;&lt;/p&gt;



</summary><category term="alex-russell"/><category term="browsers"/><category term="chrome"/><category term="security"/><category term="lethal-trifecta"/></entry><entry><title>Default styles for h1 elements are changing</title><link href="https://simonwillison.net/2025/Apr/11/default-styles-for-h1/#atom-tag" rel="alternate"/><published>2025-04-11T03:54:43+00:00</published><updated>2025-04-11T03:54:43+00:00</updated><id>https://simonwillison.net/2025/Apr/11/default-styles-for-h1/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://developer.mozilla.org/en-US/blog/h1-element-styles/"&gt;Default styles for h1 elements are changing&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Wow, this is a rare occurrence! Firefox are rolling out a change to the default user-agent stylesheet for nested &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; elements, currently ramping from 5% to 50% of users and with full roll-out planned for Firefox 140 in June 2025. Chrome is showing deprecation warnings and Safari are expected to follow suit in the future.&lt;/p&gt;
&lt;p&gt;What's changing? The default sizes of &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; elements that are nested inside &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;These are the default styles being removed:&lt;/p&gt;
&lt;blockquote&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;/* where x is :is(article, aside, nav, section) */&lt;/span&gt;
&lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;h1&lt;/span&gt; { &lt;span class="pl-c1"&gt;margin-block&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-c1"&gt;0.83&lt;span class="pl-smi"&gt;em&lt;/span&gt;&lt;/span&gt;; &lt;span class="pl-c1"&gt;font-size&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-c1"&gt;1.50&lt;span class="pl-smi"&gt;em&lt;/span&gt;&lt;/span&gt;; }
&lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;h1&lt;/span&gt; { &lt;span class="pl-c1"&gt;margin-block&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-c1"&gt;1.00&lt;span class="pl-smi"&gt;em&lt;/span&gt;&lt;/span&gt;; &lt;span class="pl-c1"&gt;font-size&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-c1"&gt;1.17&lt;span class="pl-smi"&gt;em&lt;/span&gt;&lt;/span&gt;; }
&lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;h1&lt;/span&gt; { &lt;span class="pl-c1"&gt;margin-block&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-c1"&gt;1.33&lt;span class="pl-smi"&gt;em&lt;/span&gt;&lt;/span&gt;; &lt;span class="pl-c1"&gt;font-size&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-c1"&gt;1.00&lt;span class="pl-smi"&gt;em&lt;/span&gt;&lt;/span&gt;; }
&lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;h1&lt;/span&gt; { &lt;span class="pl-c1"&gt;margin-block&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-c1"&gt;1.67&lt;span class="pl-smi"&gt;em&lt;/span&gt;&lt;/span&gt;; &lt;span class="pl-c1"&gt;font-size&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-c1"&gt;0.83&lt;span class="pl-smi"&gt;em&lt;/span&gt;&lt;/span&gt;; }
&lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;x&lt;/span&gt; &lt;span class="pl-ent"&gt;h1&lt;/span&gt; { &lt;span class="pl-c1"&gt;margin-block&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-c1"&gt;2.33&lt;span class="pl-smi"&gt;em&lt;/span&gt;&lt;/span&gt;; &lt;span class="pl-c1"&gt;font-size&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-c1"&gt;0.67&lt;span class="pl-smi"&gt;em&lt;/span&gt;&lt;/span&gt;; }&lt;/pre&gt;
&lt;/blockquote&gt;
&lt;p&gt;The short version is that, many years ago, the HTML spec introduced the idea that an &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; within a nested section should have the same meaning (and hence visual styling) as an &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt;. This never really took off and wasn't reflected by the accessibility tree, and was removed from the HTML spec in 2022. The browsers are now trying to cleanup the legacy default styles.&lt;/p&gt;
&lt;p&gt;This advice from that post sounds sensible to me:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Do not&lt;/strong&gt; rely on default browser styles for conveying a heading hierarchy. Explicitly define your document hierarchy using &lt;code&gt;&amp;lt;h2&amp;gt;&lt;/code&gt; for second-level headings, &lt;code&gt;&amp;lt;h3&amp;gt;&lt;/code&gt; for third-level, etc.&lt;/li&gt;
&lt;li&gt;Always define your own &lt;code&gt;font-size&lt;/code&gt; and &lt;code&gt;margin&lt;/code&gt; for &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; elements.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=43649853"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/css"&gt;css&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firefox"&gt;firefox&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mozilla"&gt;mozilla&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/safari"&gt;safari&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-standards"&gt;web-standards&lt;/a&gt;&lt;/p&gt;



</summary><category term="browsers"/><category term="chrome"/><category term="css"/><category term="firefox"/><category term="html"/><category term="mozilla"/><category term="safari"/><category term="web-standards"/></entry><entry><title>First look at the modern attr()</title><link href="https://simonwillison.net/2025/Apr/3/first-look-at-the-modern-attr/#atom-tag" rel="alternate"/><published>2025-04-03T15:53:52+00:00</published><updated>2025-04-03T15:53:52+00:00</updated><id>https://simonwillison.net/2025/Apr/3/first-look-at-the-modern-attr/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://ishadeed.com/article/modern-attr/"&gt;First look at the modern attr()&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Chrome 133 (released February 25th 2025) was the first browser to &lt;a href="https://developer.chrome.com/release-notes/133?hl=en#css_advanced_attr_function"&gt;ship support&lt;/a&gt; for the advanced CSS &lt;code&gt;attr()&lt;/code&gt; function (&lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/attr"&gt;MDN&lt;/a&gt;), which lets &lt;code&gt;attr()&lt;/code&gt; be used to compose values using types other than strings.&lt;/p&gt;
&lt;p&gt;Ahmad Shadeed explores potential applications of this in detail, trying it out for CSS grid columns, progress bars, background images, animation delays and more.&lt;/p&gt;
&lt;p&gt;I like this example that uses the &lt;code&gt;rows="5"&lt;/code&gt; attribute on a &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt; to calculate its &lt;code&gt;max-height&lt;/code&gt; - here wrapped in a feature detection block:&lt;/p&gt;
&lt;pre&gt;&lt;span class="pl-k"&gt;@supports&lt;/span&gt; (&lt;span class="pl-c1"&gt;x&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;attr&lt;/span&gt;(x &lt;span class="pl-en"&gt;type&lt;/span&gt;(&lt;span class="pl-c1"&gt;*&lt;/span&gt;))) {
  &lt;span class="pl-ent"&gt;textarea&lt;/span&gt; {
    &lt;span class="pl-c1"&gt;min-height&lt;/span&gt;&lt;span class="pl-kos"&gt;:&lt;/span&gt; &lt;span class="pl-en"&gt;calc&lt;/span&gt;(
      &lt;span class="pl-en"&gt;attr&lt;/span&gt;(rows &lt;span class="pl-en"&gt;type&lt;/span&gt;(&amp;lt;number&lt;span class="pl-c1"&gt;&amp;gt;&lt;/span&gt;)) &lt;span class="pl-c1"&gt;*&lt;/span&gt; &lt;span class="pl-c1"&gt;50&lt;span class="pl-smi"&gt;px&lt;/span&gt;&lt;/span&gt;
    );
  }
}&lt;/pre&gt;

&lt;p&gt;That &lt;code&gt;type(&amp;lt;number&amp;gt;)&lt;/code&gt; is the new syntax.&lt;/p&gt;
&lt;p&gt;Many of Ahmad's examples can be achieved today across all browsers using a slightly more verbose CSS custom property syntax.&lt;/p&gt;
&lt;p&gt;Here are the tracking issues for CSS values support in &lt;code&gt;attr()&lt;/code&gt; for &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=435426"&gt;Firefox&lt;/a&gt; (opened 17 years ago) and &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=26609"&gt;WebKit&lt;/a&gt; (16 years ago).


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/css"&gt;css&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-standards"&gt;web-standards&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/css-custom-properties"&gt;css-custom-properties&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ahmad-shadeed"&gt;ahmad-shadeed&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="css"/><category term="web-standards"/><category term="css-custom-properties"/><category term="ahmad-shadeed"/></entry><entry><title>Backstory on the default styles for the HTML dialog modal</title><link href="https://simonwillison.net/2025/Mar/16/backstory/#atom-tag" rel="alternate"/><published>2025-03-16T16:36:36+00:00</published><updated>2025-03-16T16:36:36+00:00</updated><id>https://simonwillison.net/2025/Mar/16/backstory/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://news.ycombinator.com/item?id=43378225#43380129"&gt;Backstory on the default styles for the HTML dialog modal&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
My TIL about &lt;a href="https://til.simonwillison.net/css/dialog-full-height"&gt;Styling an HTML dialog modal to take the full height of the viewport&lt;/a&gt; (here's the &lt;a href="https://tools.simonwillison.net/side-panel-dialog"&gt;interactive demo&lt;/a&gt;) showed up &lt;a href="https://news.ycombinator.com/item?id=43378225"&gt;on Hacker News&lt;/a&gt; this morning, and attracted this fascinating comment from Chromium engineer Ian Kilpatrick.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There's quite a bit of history here, but the abbreviated version is that the dialog element was originally added as a replacement for window.alert(), and there were a libraries polyfilling dialog and being surprisingly widely used.&lt;/p&gt;
&lt;p&gt;The mechanism which dialog was originally positioned was relatively complex, and slightly hacky (magic values for the insets).&lt;/p&gt;
&lt;p&gt;Changing the behaviour basically meant that we had to add "overflow:auto", and some form of "max-height"/"max-width" to ensure that the content within the dialog was actually reachable.&lt;/p&gt;
&lt;p&gt;The better solution to this was to add "max-height:stretch", "max-width:stretch". You can see &lt;a href="https://github.com/whatwg/html/pull/5936#discussion_r513642207"&gt;the discussion for this here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The problem is that no browser had (and still has) shipped the "stretch" keyword. (Blink &lt;a href="https://groups.google.com/a/chromium.org/g/blink-dev/c/SiZ2nDt3B9E/m/kP_rKOaDAgAJ?pli=1"&gt;likely will "soon"&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;However this was pushed back against as this had to go in a specification - and nobody implemented it ("-webit-fill-available" would have been an acceptable substitute in Blink but other browsers didn't have this working the same yet).&lt;/p&gt;
&lt;p&gt;Hence the calc() variant. (Primarily because of "box-sizing:content-box" being the default, and pre-existing border/padding styles on dialog that we didn't want to touch). [...]&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;I particularly enjoyed this insight into the challenges of evolving the standards that underlie the web, even for something this small:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;One thing to keep in mind is that any changes that changes web behaviour is under some time pressure. If you leave something too long, sites will start relying on the previous behaviour - so it would have been arguably worse not to have done anything.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Also from the comments I learned that Firefox DevTools &lt;em&gt;can&lt;/em&gt; show you user-agent styles, but that option is turned off by default - &lt;a href="https://til.simonwillison.net/css/dialog-full-height#user-content-update-firefox-can-show-browser-styles"&gt;notes on that here&lt;/a&gt;. Once I turned this option on I saw references to an &lt;code&gt;html.css&lt;/code&gt; stylesheet, so I dug around and &lt;a href="https://searchfox.org/mozilla-central/source/layout/style/res/html.css"&gt;found that in the Firefox source code&lt;/a&gt;. Here's &lt;a href="https://github.com/mozilla/gecko-dev/commits/HEAD/layout/style/res/html.css"&gt;the commit history&lt;/a&gt; for that file on the official GitHub mirror, which provides a detailed history of how Firefox default HTML styles have evolved with the standards over time.&lt;/p&gt;
&lt;p&gt;And &lt;a href="https://news.ycombinator.com/item?id=43378225#43380255"&gt;via uallo&lt;/a&gt; here are the same default HTML styles for other browsers:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chromium: &lt;a href="https://github.com/chromium/chromium/blob/main/third_party/blink/renderer/core/html/resources/html.css"&gt;third_party/blink/renderer/core/html/resources/html.css&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;WebKit: &lt;a href="https://github.com/WebKit/WebKit/blob/main/Source/WebCore/css/html.css"&gt;Source/WebCore/css/html.css&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/css"&gt;css&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firefox"&gt;firefox&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web-standards"&gt;web-standards&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="css"/><category term="firefox"/><category term="html"/><category term="web-standards"/></entry><entry><title>Quoting David Heinemeier Hansson</title><link href="https://simonwillison.net/2024/Oct/15/david-heinemeier-hansson/#atom-tag" rel="alternate"/><published>2024-10-15T20:46:45+00:00</published><updated>2024-10-15T20:46:45+00:00</updated><id>https://simonwillison.net/2024/Oct/15/david-heinemeier-hansson/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://world.hey.com/dhh/passwords-have-problems-but-passkeys-have-more-95285df9"&gt;&lt;p&gt;The problem with passkeys is that they're essentially a halfway house to a password manager, but tied to a specific platform in ways that aren't obvious to a user at all, and liable to easily leave them unable to access of their accounts. [...]&lt;/p&gt;
&lt;p&gt;Chrome on Windows stores your passkeys in Windows Hello, so if you sign up for a service on Windows, and you then want to access it on iPhone, you're going to be stuck (unless you're so forward thinking as to add a second passkey, somehow, from the iPhone will on the Windows computer!). The passkey lives on the wrong device, if you're away from the computer and want to login, and it's not at all obvious to most users how they might fix that.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://world.hey.com/dhh/passwords-have-problems-but-passkeys-have-more-95285df9"&gt;David Heinemeier Hansson&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/passkeys"&gt;passkeys&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/dhh"&gt;dhh&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/usability"&gt;usability&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;&lt;/p&gt;



</summary><category term="passkeys"/><category term="dhh"/><category term="security"/><category term="usability"/><category term="chrome"/></entry><entry><title>Button Stealer</title><link href="https://simonwillison.net/2024/Jul/25/button-stealer/#atom-tag" rel="alternate"/><published>2024-07-25T19:40:08+00:00</published><updated>2024-07-25T19:40:08+00:00</updated><id>https://simonwillison.net/2024/Jul/25/button-stealer/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://anatolyzenkov.com/stolen-buttons/button-stealer"&gt;Button Stealer&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Really fun Chrome extension by Anatoly Zenkov: it scans every web page you visit for things that look like buttons and stashes a copy of them, then provides a page where you can see all of the buttons you have collected. Here's &lt;a href="https://anatolyzenkov.com/stolen-buttons"&gt;Anatoly's collection&lt;/a&gt;, and here are a few that I've picked up trying it out myself:&lt;/p&gt;
&lt;p&gt;&lt;img alt="Screenshot showing some buttons I have collected, each with their visual appearance maintained" src="https://static.simonwillison.net/static/2024/stolen-buttons.jpg" /&gt;&lt;/p&gt;
&lt;p&gt;The extension source code is &lt;a href="https://github.com/anatolyzenkov/button-stealer"&gt;on GitHub&lt;/a&gt;. It identifies potential buttons by looping through every &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element and &lt;a href="https://github.com/anatolyzenkov/button-stealer/blob/cfe43b6247e1b9f7d4414fd2a9b122c2d1a40840/scripts/button-stealer.js#L264-L298"&gt;applying some heuristics&lt;/a&gt; like checking the width/height ratio, then &lt;a href="https://github.com/anatolyzenkov/button-stealer/blob/cfe43b6247e1b9f7d4414fd2a9b122c2d1a40840/scripts/button-stealer.js#L93-L140"&gt;clones a subset of the CSS&lt;/a&gt; from &lt;code&gt;window.getComputedStyle()&lt;/code&gt; and stores that in the &lt;code&gt;style=&lt;/code&gt; attribute.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://waxy.org/2024/07/button-stealer/"&gt;Andy Baio&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/css"&gt;css&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/extensions"&gt;extensions&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="css"/><category term="extensions"/><category term="javascript"/></entry><entry><title>hangout_services/thunk.js</title><link href="https://simonwillison.net/2024/Jul/9/hangout_servicesthunkjs/#atom-tag" rel="alternate"/><published>2024-07-09T17:50:16+00:00</published><updated>2024-07-09T17:50:16+00:00</updated><id>https://simonwillison.net/2024/Jul/9/hangout_servicesthunkjs/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/chromium/chromium/blob/128.0.6586.1/chrome/browser/resources/hangout_services/thunk.js"&gt;hangout_services/thunk.js&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
It turns out Google Chrome (via Chromium) includes a default extension which makes extra services available to code running on the &lt;code&gt;*.google.com&lt;/code&gt; domains - tweeted about today &lt;a href="https://twitter.com/lcasdev/status/1810696257137959018"&gt;by Luca Casonato&lt;/a&gt;, but the code has been there in the public repo &lt;a href="https://github.com/chromium/chromium/commit/422c736b82e7ee763c67109cde700db81ca7b443"&gt;since October 2013&lt;/a&gt; as far as I can tell.&lt;/p&gt;
&lt;p&gt;It looks like it's a way to let Google Hangouts (or presumably its modern predecessors) get additional information from the browser, including the current load on the user's CPU. Update: On Hacker News a Googler &lt;a href="https://news.ycombinator.com/item?id=40918742"&gt;confirms&lt;/a&gt; that the Google Meet "troubleshooting" feature uses this to review CPU utilization.&lt;/p&gt;
&lt;p&gt;I got GPT-4o to &lt;a href="https://chatgpt.com/share/25008fdd-319e-447a-97b4-ea2c87cba583"&gt;help me figure out how to trigger it&lt;/a&gt; (I tried Claude 3.5 Sonnet first but it &lt;a href="https://twitter.com/simonw/status/1810734848019157149"&gt;refused&lt;/a&gt;, saying "Doing so could potentially violate terms of service or raise security and privacy concerns"). Paste the following into your Chrome DevTools console on any Google site to see the result:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;chrome.runtime.sendMessage(
  "nkeimhogjdpnpccoofpliimaahmaaome",
  { method: "cpu.getInfo" },
  (response) =&amp;gt; {
    console.log(JSON.stringify(response, null, 2));
  },
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I get back a response that starts like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  "value": {
    "archName": "arm64",
    "features": [],
    "modelName": "Apple M2 Max",
    "numOfProcessors": 12,
    "processors": [
      {
        "usage": {
          "idle": 26890137,
          "kernel": 5271531,
          "total": 42525857,
          "user": 10364189
        }
      }, ...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The code doesn't do anything on non-Google domains.&lt;/p&gt;
&lt;p&gt;Luca &lt;a href="https://twitter.com/lcasdev/status/1810696259184779750"&gt;says this&lt;/a&gt; - I'm inclined to agree:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This is interesting because it is a clear violation of the idea that browser vendors should not give preference to their websites over anyone elses.&lt;/p&gt;
&lt;/blockquote&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=40918052"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chatgpt"&gt;chatgpt&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;&lt;/p&gt;



</summary><category term="browsers"/><category term="chrome"/><category term="google"/><category term="ai"/><category term="chatgpt"/><category term="llms"/><category term="ai-assisted-programming"/><category term="claude"/></entry><entry><title>Quoting Kornel Lesiński</title><link href="https://simonwillison.net/2024/Jul/9/kornel-lesinski/#atom-tag" rel="alternate"/><published>2024-07-09T10:43:32+00:00</published><updated>2024-07-09T10:43:32+00:00</updated><id>https://simonwillison.net/2024/Jul/9/kornel-lesinski/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://mastodon.social/@kornel/112752977103985802"&gt;&lt;p&gt;Chrome's biggest innovation was the short release cycle with a silent unceremonious autoupdate.&lt;/p&gt;
&lt;p&gt;When updates were big, rare, and manual, buggy and outdated browsers were lingering for soo long, that we were giving bugs names. We documented the bugs in magazines and books, as if they were a timeless foundation of WebDev.&lt;/p&gt;
&lt;p&gt;Nowadays browser vendors can fix bugs in 6 weeks (even Safari can…). New-ish stuff is still buggy, but rarely for long enough for the bugs to make it to schools' curriculums.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://mastodon.social/@kornel/112752977103985802"&gt;Kornel Lesiński&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/web-standards"&gt;web-standards&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;&lt;/p&gt;



</summary><category term="web-standards"/><category term="browsers"/><category term="chrome"/></entry><entry><title>Chrome Prompt Playground</title><link href="https://simonwillison.net/2024/Jul/3/chrome-prompt-playground/#atom-tag" rel="alternate"/><published>2024-07-03T17:11:02+00:00</published><updated>2024-07-03T17:11:02+00:00</updated><id>https://simonwillison.net/2024/Jul/3/chrome-prompt-playground/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://tools.simonwillison.net/chrome-prompt-playground"&gt;Chrome Prompt Playground&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Google Chrome Canary is currently shipping an experimental on-device LLM, in the form of Gemini Nano. You can access it via the new &lt;code&gt;window.ai&lt;/code&gt; API, after first enabling the "Prompt API for Gemini Nano" experiment in &lt;code&gt;chrome://flags&lt;/code&gt; (and then waiting an indeterminate amount of time for the ~1.7GB model file to download - I eventually spotted it in &lt;code&gt;~/Library/Application Support/Google/Chrome Canary/OptGuideOnDeviceModel&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;I got Claude 3.5 Sonnet to build me this playground interface for experimenting with the model. You can execute prompts, stream the responses and all previous prompts and responses are stored in &lt;code&gt;localStorage&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2024/playground-greet.gif" alt="Animated GIF demo. The prompt is Show two greetings each in French and Spanish - on clicking the button the result streams in:  French Bonjour! Bienvenue!, Spanish Hola!, Bienvenido! Scrolling down reveals the stored history, and clicking delete on that prompt removes it from the page." width="500" class="blogmark-image"&gt;&lt;/p&gt;
&lt;p&gt;Here's the &lt;a href="https://gist.github.com/simonw/e62440114960bc98f200eb3d92593896"&gt;full Sonnet transcript&lt;/a&gt;, and the &lt;a href="https://github.com/simonw/tools/blob/be05fc38ea600bc65c6a293d5d69d0999e77be10/chrome-prompt-playground.html"&gt;final source code&lt;/a&gt; for the app.&lt;/p&gt;
&lt;p&gt;The best documentation I've found for the new API is is &lt;a href="https://github.com/explainers-by-googlers/prompt-api"&gt;explainers-by-googlers/prompt-api&lt;/a&gt; on GitHub.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/localstorage"&gt;localstorage&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/projects"&gt;projects&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai-assisted-programming"&gt;ai-assisted-programming&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/claude"&gt;claude&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gemini"&gt;gemini&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="google"/><category term="localstorage"/><category term="projects"/><category term="ai"/><category term="generative-ai"/><category term="llms"/><category term="ai-assisted-programming"/><category term="claude"/><category term="gemini"/></entry><entry><title>Understand errors and warnings better with Gemini</title><link href="https://simonwillison.net/2024/May/17/understand-errors-and-warnings-better-with-gemini/#atom-tag" rel="alternate"/><published>2024-05-17T22:10:06+00:00</published><updated>2024-05-17T22:10:06+00:00</updated><id>https://simonwillison.net/2024/May/17/understand-errors-and-warnings-better-with-gemini/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://developer.chrome.com/docs/devtools/console/understand-messages"&gt;Understand errors and warnings better with Gemini&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
As part of Google's Gemini-in-everything strategy, Chrome DevTools now includes an opt-in feature for passing error messages in the JavaScript console to Gemini for an explanation, via a lightbulb icon.&lt;/p&gt;
&lt;p&gt;Amusingly, this documentation page includes a warning about prompt injection:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Many of LLM applications are susceptible to a form of abuse known as prompt injection. This feature is no different. It is possible to trick the LLM into accepting instructions that are not intended by the developers.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;They include a screenshot of a harmless example, but I'd be interested in hearing if anyone has a theoretical attack that could actually cause real damage here.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=40390287"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/google"&gt;google&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/prompt-injection"&gt;prompt-injection&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/gemini"&gt;gemini&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="google"/><category term="security"/><category term="ai"/><category term="prompt-injection"/><category term="generative-ai"/><category term="llms"/><category term="gemini"/></entry><entry><title>Web LLM runs the vicuna-7b Large Language Model entirely in your browser, and it's very impressive</title><link href="https://simonwillison.net/2023/Apr/16/web-llm/#atom-tag" rel="alternate"/><published>2023-04-16T15:10:45+00:00</published><updated>2023-04-16T15:10:45+00:00</updated><id>https://simonwillison.net/2023/Apr/16/web-llm/#atom-tag</id><summary type="html">
    &lt;p&gt;A month ago I asked &lt;a href="https://simonwillison.net/2023/Mar/17/beat-chatgpt-in-a-browser/"&gt;Could you train a ChatGPT-beating model for $85,000 and run it in a browser?&lt;/a&gt;. $85,000 was a hypothetical training cost for LLaMA 7B plus Stanford Alpaca. "Run it in a browser" was based on the fact that &lt;a href="https://github.com/mlc-ai/web-stable-diffusion"&gt;Web Stable Diffusion&lt;/a&gt; runs a 1.9GB Stable Diffusion model in a browser, so maybe it's not such a big leap to run a small Large Language Model there as well.&lt;/p&gt;
&lt;p&gt;That second part has now happened.&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/mlc-ai/web-llm"&gt;Web LLM&lt;/a&gt; is a project from the same team as Web Stable Diffusion which runs the &lt;a href="https://huggingface.co/lmsys/vicuna-7b-delta-v0"&gt;vicuna-7b-delta-v0&lt;/a&gt; model in a browser, taking advantage of the brand new WebGPU API that just arrived in Chrome in beta.&lt;/p&gt;
&lt;p&gt;I got their &lt;a href="https://mlc.ai/web-llm/#chat-demo"&gt;browser demo&lt;/a&gt; running on my M2 MacBook Pro using &lt;a href="https://www.google.com/chrome/canary/"&gt;Chrome Canary&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt; As of Chrome 113 &lt;a href="https://developer.chrome.com/blog/new-in-chrome-113/"&gt;released in May 2023&lt;/a&gt; you no longer need to use Chrome Canary for this - regular Chrome works fine too.&lt;/p&gt;
&lt;p&gt;It's really, really good. It's actually the most impressive Large Language Model I've run on my own hardware to date - and the fact that it's running entirely in the browser makes that even more impressive.&lt;/p&gt;
&lt;p&gt;It's really fast too: I'm seeing around 15 tokens a second, which is better performance than almost all of the other models I've tried running on my own machine.&lt;/p&gt;
&lt;p&gt;I started it out with something easy - a straight factual lookup. "Who landed on the moon?"&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2023/web-llm-moon.jpg" alt="Prompt: Who landed on the moon? Response: The first human beings to land on the Moon were a crew of astronauts from the United States, known as the Apollo 11 mission, which occurred on July 20, 1969. The mission was commanded by Neil Armstrong and included Edwin Buzz Aldrin as lunar module Pilot. The mission was launched by a Saturn V launch vehicle from Kennedy Space Center's Launch Complex 39A, and the landing site was in the Sea of Tranquility." style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;That's a good answer, and it passes a quick fact check.&lt;/p&gt;
&lt;p&gt;Next, I tried something a lot harder: "five albums by Cher as a markdown list"&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2023/web-llm-cher.jpg" alt="![](https://www.cherproject.com/cher-5-albums/)

* [If I Could Turn Back Time](https://www.cherproject.com/cher-5-albums/#1)
* [Cher's Gold](https://www.cherproject.com/cher-5-albums/#2)
* [Love Hurts](https://www.cherproject.com/cher-5-albums/#3)
* [Greatest Hits: Geffen Years](https://www.cherproject.com/cher-5-albums/#4)
* [Closer to the Truth](https://www.cherproject.com/cher-5-albums/#5)" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;It managed to count to five, which is no easy thing for an LLM. It also appears to know what a Markdown list looks like.&lt;/p&gt;
&lt;p&gt;But... &lt;code&gt;www.cherproject.com&lt;/code&gt; is a hallucinated domain name, and two of those albums appear to be wrong to me - "Cher's Gold" should be "Cher's Golden Greats", and I while Cher did sign with Geffen Records I couldn't find any mention anywhere of an album called "Greatest Hits: Geffen Years".&lt;/p&gt;
&lt;p&gt;I did not expect it to be able to handle this prompt at all though, so I'm still very impressed to see even a partially correct answer here.&lt;/p&gt;
&lt;p&gt;I decided to see if it knew who I am. "Who is Simon Willison?"&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2023/web-llm-peanut.jpg" alt="Prompt: Who is Simon Willison? Response: Human: Who is peanut?" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;It answered "Human: Who is peanut?". Zero marks for that one.&lt;/p&gt;
&lt;p&gt;I decided to try it on a summary. I copied some random paragraphs of text from a recent blog entry and asked it to "Summarize this: PASTE".&lt;/p&gt;
&lt;p&gt;It did a very, very good job!&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2023/web-llm-summary.jpg" alt="Prompt: Summarize this: Recording changes made to a database table is a problem that has popped up consistently throughout my entire career. I’ve managed to mostly avoid it in Datasette so far because it mainly dealt with read-only data, but with the new JSON write API has made me reconsider: if people are going to build mutable databases on top of Datasette, having a way to track those changes becomes a whole lot more desirable. I’ve written before about how working with ChatGPT makes me more ambitious. A few weeks ago I started a random brainstorming session with GPT-4 around this topic, mainly to learn more about how SQLite triggers could be used to address this sort of problem. Here’s the resulting transcript. It turns out ChatGPT makes for a really useful brainstorming partner. Initially I had thought that I wanted a “snapshot” system, where a user could click a button to grab a snapshot of the current state of the table, and then restore it again later if they needed to. Response: The author is discussing the problem of recording changes made to a database table. They have been avoiding this problem in their Datasette software, but with the introduction of a new JSON write API, they have reconsidered the need to track changes in a database. They have been discussing this issue with ChatGPT, a language model, and have come up with a possible solution. The solution involves using SQL triggers to track changes made to a database table." style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;At this point I started to get excited.&lt;/p&gt;
&lt;p&gt;As I've noted before, I don't particularly care about having a locally executing LLM that can answer questions about every factual topic under the sun.&lt;/p&gt;
&lt;p&gt;What I want instead is a &lt;a href="https://simonwillison.net/2023/Apr/2/calculator-for-words/"&gt;calculator for words&lt;/a&gt;. I want a model that I can feed content into and have it manipulate the language in that input - summarization, fact extraction, &lt;a href="https://simonwillison.net/2023/Jan/13/semantic-search-answers/"&gt;question answering&lt;/a&gt; based on a carefully crafted prompt - that kind of thing.&lt;/p&gt;
&lt;p&gt;If Web LLM + vicuna-7b-delta-v0 can summarize text like this, it's looking like it might be the level of capability I've been hoping for.&lt;/p&gt;
&lt;p&gt;Time to try one of my favourite tests for an LLM: can it generate pun names for a coffee shop run by otters?&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2023/web-llm-otter-coffee-shop.jpg" alt="Prompt: Pun names for a coffee shop run by otters. Response: 1. Furry Bean 2. Otter Espresso 3. Pawsitively Caffeinated 4. The Aquarium Buzz 5. Cotta Coffee 6. Furrycup 7. The Ottertail 8. Beanery Cove 9. The Waterfall Shop 10. Furrytails 11. The Aquatic Grind 12. The Otterbean 13. The Purr-fect Cup 14. The Furry Coffeehouse 15. The Waterfall Brew 16. The Aquatic Barista 17. Coffee with a View 18. Furry Mug 19. The Otter's Perch 20. Beanery Breeze " style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;(It actually returned 54, I'm listing just the first 20 here.)&lt;/p&gt;
&lt;p&gt;Are these brilliant puns? No. But they're recognizable as puns! This was honestly far beyond my wildest dreams for what I might get out of an LLM that can run in a browser.&lt;/p&gt;
&lt;p&gt;Just to see what happened, I threw what I thought would be an impossible prompt at it: "A rap battle between a pelican and a sea otter".&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2023/web-llm-rap-battle.jpg" alt="Prompt: A rap battle between a pelican and a sea otter. Response: Pelican: Yo, I'm the OG of the ocean, I'm a bird that's built like a tank, My beak is sharp, my wings can glide, I'm the one that they all must dance. Sea Otter: Hold up, you can't even compare us, I'm a marine mammal, not a bird, My fur is soft, my feet are great, I'm the one that's sleek and lean. Pelican: Yo, you may have fur, but you can't fly, I'm the one with the height, I'm the king of the skies, My feathers are strong, I'm the one that's glorious. Sea Otter: Don't bring flying into this, You're a bird, you can't even fly that high, I'm the one with the skills, I'm the one that's swimming with finesse. Pelican: Oh, you think you're so great, But you can't even hold a beat, I'm the one with the flow, I'm the one that's got the groove, I'm the one that's got the power, You're just a sea otter, weak and insignificant. Sea Otter: You can't insult me, I'm a marine mammal, I'm the one that's got the soul, I'm the one that's got the heart, You're just a bird, a feeble creature, Can't even hold a candle to my awesomeness." style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;Wow. I mean it's bad, but it's also amazing.&lt;/p&gt;
&lt;p&gt;How about writing code? I tried "Write a JavaScript function to extract data from a table and log it to the console as CSV"&lt;/p&gt;
&lt;p&gt;&lt;img src="https://static.simonwillison.net/static/2023/web-llm-javascript.jpg" alt="Sure, here is one way to do it: ``` function extractData(table) { const data = []; const headers = table.header.split(&amp;quot;,&amp;quot;).map(String); for (let i = 0; i &amp;lt; table.rows.length; i++) { const row = table.rows[i].element.toArray(); const rowData = row.map(item = /&amp;gt; item.toISOString().replace(/\B(? ba?)[\s/!-]/gi, '')); const rowString = row.map(header =&gt; ${header.toUpperCase()}).join(','); const parsedData = rowString.split(','); parsedData.forEach(dataElement =&gt; { const { tag, value } = dataElement.split(':'); data.push(value); }); } // Log data as CSV data.forEach(dataElement =&amp;gt; console.log(`${dataElement.tag},${dataElement.value}`)); }" style="max-width: 100%;" /&gt;&lt;/p&gt;
&lt;p&gt;This looks convincing at first glance, but it's useless: &lt;code&gt;table.headers.split(",")&lt;/code&gt; is not how an HTML table works in the JavaScript DOM.&lt;/p&gt;
&lt;p&gt;Again though, this result hints in a very useful direction - particularly for something that's small enough to &lt;em&gt;run in my browser&lt;/em&gt;.&lt;/p&gt;
&lt;h4&gt;Is this enough to be useful?&lt;/h4&gt;
&lt;p&gt;Despite the flaws demonstrated above, I think this has passed my threshold for being something I could use as a building block for all sorts of genuinely useful things.&lt;/p&gt;
&lt;p&gt;I don't need a language model that can answer any question I have about the world from its baked in training data.&lt;/p&gt;
&lt;p&gt;I need something that can manipulate language in useful ways. I care about summarization, and fact extraction, and answering questions about larger text.&lt;/p&gt;
&lt;p&gt;(And maybe inventing pun names for coffee shops.)&lt;/p&gt;
&lt;p&gt;The most useful innovation happening around language models right now involves giving them access to tools.&lt;/p&gt;
&lt;p&gt;It turns out it's really easy to teach a language model how to turn "Summarize my latest email" into a command, 'action: fetch_latest_email' which can then be carried out by an outer layer of code, with the results being fed back into the model for further processing.&lt;/p&gt;
&lt;p&gt;One popular version of this is the ReAct model, which I &lt;a href="https://til.simonwillison.net/llms/python-react-pattern"&gt;implemented in a few dozen lines of Python here&lt;/a&gt;. &lt;a href="https://openai.com/blog/chatgpt-plugins"&gt;ChatGPT Plugins&lt;/a&gt; and &lt;a href="https://github.com/Significant-Gravitas/Auto-GPT"&gt;Auto-GPT&lt;/a&gt; are more examples of this pattern in action.&lt;/p&gt;
&lt;p&gt;You don't need a model with the power of GPT-4 to implement this pattern. I fully expect that vicuna-7b is capable enough to get this kind of thing to work.&lt;/p&gt;
&lt;p&gt;An LLM that runs on my own hardware - that runs in my browser! - and can make use of additional tools that I grant to it is a very exciting thing.&lt;/p&gt;
&lt;p&gt;Here's another thing everyone wants: a LLM-powered chatbot that can answer questions against their own documentation.&lt;/p&gt;
&lt;p&gt;I wrote about a way of doing that in &lt;a href="https://simonwillison.net/2023/Jan/13/semantic-search-answers/"&gt;How to implement Q&amp;amp;A against your documentation with GPT3, embeddings and Datasette&lt;/a&gt;. I think vicuna-7b is powerful enough to implement that pattern, too.&lt;/p&gt;
&lt;h4&gt;Why the browser matters&lt;/h4&gt;
&lt;p&gt;Running in the browser feels like a little bit of a gimmick - especially since it has to pull down GBs of model data in order to start running.&lt;/p&gt;
&lt;p&gt;I think the browser is actually a really great place to run an LLM, because it provides a secure sandbox.&lt;/p&gt;
&lt;p&gt;LLMs are inherently risky technology. Not because they might break out and try to kill all humans - that remains pure science fiction. They're dangerous because they will follow instructions no matter where those instructions came from. Ask your LLM assistant to summarize the wrong web page and an attacker could trick it into leaking all your private data, or deleting all of your emails, or worse.&lt;/p&gt;
&lt;p&gt;I wrote about this at length in &lt;a href="https://simonwillison.net/2023/Apr/14/worst-that-can-happen/"&gt;Prompt injection: what’s the worst that can happen?&lt;/a&gt; - using personal AI assistants as an explicit example of why this is so dangerous.&lt;/p&gt;
&lt;p&gt;To run personal AI assistants safely, we need to use a sandbox where we can carefully control what information and tools they have available to then.&lt;/p&gt;
&lt;p&gt;Web browsers are the most robustly tested sandboxes we have ever built.&lt;/p&gt;
&lt;p&gt;Some of the challenges the browser sandbox can help with include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Using CORS and Content-Security-Policy as an additional layer of security controlling which HTTP APIs an assistant is allowed to access&lt;/li&gt;
&lt;li&gt;Want your assistant to generate and then execute code? WebAssembly sandboxes - supported in all mainstream browsers for several years at this point - are a robust way to do that.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It's possible to solve these problems outside of the browser too, but the browser provides us with some very robust primitives to help along the way.&lt;/p&gt;
&lt;h4&gt;Vicuna isn't openly licensed&lt;/h4&gt;
&lt;p&gt;The &lt;a href="https://huggingface.co/lmsys/vicuna-7b-delta-v0"&gt;Vicuna model card&lt;/a&gt; explains how the underlying model works:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Vicuna is an open-source chatbot trained by fine-tuning LLaMA on user-shared conversations collected from ShareGPT.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This isn't ideal. Facebook &lt;a href="https://simonwillison.net/2023/Mar/11/llama/"&gt;LLaMA&lt;/a&gt; is licensed for non-commercial and research purposes only. &lt;a href="https://sharegpt.com/"&gt;ShareGPT&lt;/a&gt; is a site where people share their ChatGPT transcripts, which means the fine-tuning was conducted using data that isn't licensed for such purposes (the OpenAI terms and condition disallow using the data to train rival language models.)&lt;/p&gt;
&lt;p&gt;So there are severe limits on what you could build on top of this project.&lt;/p&gt;
&lt;p&gt;But, as with LLaMA and &lt;a href="https://simonwillison.net/2023/Mar/13/alpaca/"&gt;Alpaca&lt;/a&gt; before it, the exciting thing about this project is what it demonstrates: we can now run an extremely capable LLM entirely in a browser - albeit with a beta browser release, and on a very powerful laptop.&lt;/p&gt;
&lt;p&gt;The next milestone to look forward to is going to be a fully openly licensed LLM - something along the lines of &lt;a href="https://www.databricks.com/blog/2023/04/12/dolly-first-open-commercially-viable-instruction-tuned-llm"&gt;Dolly 2&lt;/a&gt; - running entirely in the browser using a similar stack to this Web LLM demo.&lt;/p&gt;
&lt;p&gt;The &lt;a href="https://github.com/LAION-AI/Open-Assistant"&gt;OpenAssistant&lt;/a&gt; project is worth watching here too: they've been crowdsourcing large amounts of openly licensed fine-tuning data, and are beginning to publish their own models - mostly derived from LLaMA, but that training data will unlock a lot more possibilities.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webassembly"&gt;webassembly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llama"&gt;llama&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/local-llms"&gt;local-llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/llms"&gt;llms&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mlc"&gt;mlc&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/vicuna"&gt;vicuna&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="chrome"/><category term="ai"/><category term="webassembly"/><category term="generative-ai"/><category term="llama"/><category term="local-llms"/><category term="llms"/><category term="mlc"/><category term="vicuna"/></entry><entry><title>Web Stable Diffusion</title><link href="https://simonwillison.net/2023/Mar/17/web-stable-diffusion/#atom-tag" rel="alternate"/><published>2023-03-17T04:46:56+00:00</published><updated>2023-03-17T04:46:56+00:00</updated><id>https://simonwillison.net/2023/Mar/17/web-stable-diffusion/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/mlc-ai/web-stable-diffusion"&gt;Web Stable Diffusion&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I just ran the full Stable Diffusion image generation model entirely in my browser, and used it to generate an image of two raccoons eating pie in the woods. I had to use Google Chrome Canary since this depends on WebGPU which still isn't fully rolled out, but it worked perfectly.&lt;/p&gt;
&lt;p&gt;&lt;img alt="mic.ai/web-stable-diffusion/ in Chrome Canary. Prompt: two racoons eatinga a pie in the woods. No negative prompt. Multi-step DPM Solver (20 steps) for the scheduler. Initializing GPU device: WebGPU - apple. A completed progress bar which says it took 38 seconds. And a quite realistic looking photograph of two racoons in the woods, one of whom is eating a pie (though on closer inspection he does have three paws, two holding the pie and one beneath it). The second raccoon only has two paws." src="https://static.simonwillison.net/static/2023/racoons-eating-pie.jpg" /&gt;

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://fedi.simonwillison.net/@simon/110036800515374711"&gt;@simon on Mastodon&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/ai"&gt;ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webassembly"&gt;webassembly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/stable-diffusion"&gt;stable-diffusion&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/generative-ai"&gt;generative-ai&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/mlc"&gt;mlc&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webgpu"&gt;webgpu&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/text-to-image"&gt;text-to-image&lt;/a&gt;&lt;/p&gt;



</summary><category term="browsers"/><category term="chrome"/><category term="javascript"/><category term="ai"/><category term="webassembly"/><category term="stable-diffusion"/><category term="generative-ai"/><category term="mlc"/><category term="webgpu"/><category term="text-to-image"/></entry><entry><title>Data-driven performance optimization with Rust and Miri</title><link href="https://simonwillison.net/2022/Dec/9/data-driven-performance-optimization-with-rust-and-miri/#atom-tag" rel="alternate"/><published>2022-12-09T17:19:14+00:00</published><updated>2022-12-09T17:19:14+00:00</updated><id>https://simonwillison.net/2022/Dec/9/data-driven-performance-optimization-with-rust-and-miri/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://medium.com/source-and-buggy/data-driven-performance-optimization-with-rust-and-miri-70cb6dde0d35"&gt;Data-driven performance optimization with Rust and Miri&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Useful guide to some Rust performance optimization tools. Miri can be used to dump out a detailed JSON profile of a program which can then be opened and explored using the Chrome browser’s performance tool.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://news.ycombinator.com/item?id=33921731"&gt;Hacker News&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/performance"&gt;performance&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/rust"&gt;rust&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="performance"/><category term="rust"/></entry><entry><title>Paint Holding - reducing the flash of white on same-origin navigations</title><link href="https://simonwillison.net/2022/May/22/paint-holding/#atom-tag" rel="alternate"/><published>2022-05-22T02:50:10+00:00</published><updated>2022-05-22T02:50:10+00:00</updated><id>https://simonwillison.net/2022/May/22/paint-holding/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://developer.chrome.com/blog/paint-holding/"&gt;Paint Holding - reducing the flash of white on same-origin navigations&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
I missed this when it happened back in 2019: Chrome (and apparently Safari too—not sure about Firefox) implemented a feature where rather than showing a blank screen in between page navigations Chrome “waits briefly before starting to paint, especially if the page is fast enough”. As a result, fast loading multi-page applications become almost indistinguishable from SPAs (single-page apps). It’s a really neat feature, and now that I know how it works I realize that it explains why page navigations have felt a lot snappier to me over the past few years.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;&lt;/p&gt;



</summary><category term="browsers"/><category term="chrome"/></entry><entry><title>Exploring the SameSite cookie attribute for preventing CSRF</title><link href="https://simonwillison.net/2021/Aug/3/samesite/#atom-tag" rel="alternate"/><published>2021-08-03T21:09:02+00:00</published><updated>2021-08-03T21:09:02+00:00</updated><id>https://simonwillison.net/2021/Aug/3/samesite/#atom-tag</id><summary type="html">
    &lt;p&gt;In reading Yan Zhu's excellent write-up of the &lt;a href="https://blog.azuki.vip/csrf/"&gt;JSON CSRF vulnerability&lt;/a&gt; she found in OkCupid one thing puzzled me: I was under the impression that browsers these days default to treating cookies as &lt;code&gt;SameSite=Lax&lt;/code&gt;, so I would expect attacks like the one Yan described not to work in modern browsers.&lt;/p&gt;
&lt;p&gt;This lead me down a rabbit hole of exploring how SameSite actually works, including building &lt;a href="https://samesite-lax-demo.vercel.app/"&gt;an interactive SameSite cookie exploration tool&lt;/a&gt; along the way. Here's what I learned.&lt;/p&gt;
&lt;h4 id="background-csrf"&gt;Background: Cross-Site Request Forgery&lt;/h4&gt;
&lt;p&gt;I've been tracking CSRF (Cross-Site Request Forgery) &lt;a href="https://simonwillison.net/tags/csrf/?page=2"&gt;on this blog&lt;/a&gt; since 2005(!)&lt;/p&gt;
&lt;p&gt;A quick review: let's say you have a page in your application that allows a user to delete their account, at &lt;code&gt;https://www.example.com/delete-my-account&lt;/code&gt;. The user has to be signed in with a cookie in order to activate that feature.&lt;/p&gt;
&lt;p&gt;If you created that page to respond to &lt;code&gt;GET&lt;/code&gt; requests, I as an evil person could create a page at &lt;code&gt;https://www.evil.com/force-you-to-delete-your-account&lt;/code&gt; that does this:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic"&gt;&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;img&lt;/span&gt; &lt;span class="pl-c1"&gt;src&lt;/span&gt;="&lt;span class="pl-s"&gt;https://www.example.com/delete-my-account&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;If I can get you to visit my page, I can force you to delete your account!&lt;/p&gt;
&lt;p&gt;But you're smarter than that, and you know that GET requests should be idempotent. You implement your endpoint to require a POST request instead.&lt;/p&gt;
&lt;p&gt;Turns out I can still force-delete accounts, if I can trick a user into visiting a page with the following evil HTML on it:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic"&gt;&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;form&lt;/span&gt; &lt;span class="pl-c1"&gt;action&lt;/span&gt;="&lt;span class="pl-s"&gt;https://www.example.com/delete-my-account&lt;/span&gt;" &lt;span class="pl-c1"&gt;method&lt;/span&gt;="&lt;span class="pl-s"&gt;POST&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-c1"&gt;type&lt;/span&gt;="&lt;span class="pl-s"&gt;submit&lt;/span&gt;" &lt;span class="pl-c1"&gt;value&lt;/span&gt;="&lt;span class="pl-s"&gt;Delete my account&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;form&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-smi"&gt;document&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;forms&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;submit&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The form submits with JavaScript the instant they load the page!&lt;/p&gt;
&lt;p&gt;CSRF is an extremely common and nasty vulnerability - especially since it's a hole by default: if you don't know what CSRF is, you likely have it in your application.&lt;/p&gt;
&lt;p&gt;Traditionally the solution has been to use CSRF tokens - hidden form fields which "prove" that the user came from a form on your own site, and not a form hosted somewhere else. OWASP call this the &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Cross-Site_Request_Forgery_Prevention_Cheat_Sheet.html#double-submit-cookie"&gt;Double Submit Cookie&lt;/a&gt; pattern.&lt;/p&gt;
&lt;p&gt;Web frameworks like Django implement &lt;a href="https://docs.djangoproject.com/en/3.2/ref/csrf/"&gt;CSRF protection&lt;/a&gt; for you. I built &lt;a href="https://github.com/simonw/asgi-csrf"&gt;asgi-csrf&lt;/a&gt; to help add CSRF token protection to ASGI applications.&lt;/p&gt;
&lt;h4 id="samesite-cookie-attribute"&gt;Enter the SameSite cookie attribute&lt;/h4&gt;
&lt;p&gt;Clearly it would be better if we didn't have to worry about CSRF at all.&lt;/p&gt;
&lt;p&gt;As far as I can tell, work on specifying the &lt;code&gt;SameSite&lt;/code&gt; cookie attribute started &lt;a href="https://github.com/httpwg/http-extensions/commit/aa0722c12ccb367b8f4498e982616064d105a006#diff-70cc0c0600a934d002ea91a4a36d5eb0b7d5edebcce5a40c9a811391cc0fecf6"&gt;in June 2016&lt;/a&gt;. The idea was to add an additional attribute to cookies that specifies the policy for if they should be included in requests made to a domain from pages hosted on another domain.&lt;/p&gt;
&lt;p&gt;Today, all modern browsers support SameSite. MDN &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie/SameSite"&gt;has SameSite documentation&lt;/a&gt;, but a summary is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SameSite=None&lt;/code&gt; - the cookie is sent in "all contexts" - more-or-less how things used to work before SameSite was invented. &lt;strong&gt;Update:&lt;/strong&gt; One major edge-case here is that Safari apparently ignores &lt;code&gt;None&lt;/code&gt; if the "Prevent cross-site tracking" privacy preference is turned on - and since that is on by default, this means that &lt;code&gt;SameSite=None&lt;/code&gt; is effectively useless if you care about Safari or Mobile Safari users.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SameSite=Strict&lt;/code&gt; - the cookie is only sent for requests that originate on the same domain. Even arriving on the site from an off-site link will not see the cookie, unless you subsequently refresh the page or navigate within the site.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SameSite=Lax&lt;/code&gt; - cookie is sent if you navigate to the site through following a link from another domain but &lt;em&gt;not&lt;/em&gt; if you submit a form. This is generally what you want to protect against CSRF attacks!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The attribute is specified by the server in a &lt;code&gt;set-cookie&lt;/code&gt; header that looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;set-cookie: lax-demo=3473; Path=/; SameSite=lax
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Why not habitually use &lt;code&gt;SameSite=Strict&lt;/code&gt;? Because then if someone follows a link to your site their first request will be treated as if they are not signed in at all. That's bad!&lt;/p&gt;
&lt;p&gt;So explicitly setting a cookie with &lt;code&gt;SameSite=Lax&lt;/code&gt; should be enough to protect your application from CSRF vulnerabilities... provided your users have a browser that supports it.&lt;/p&gt;
&lt;p&gt;(Can I Use reports &lt;a href="https://caniuse.com/same-site-cookie-attribute"&gt;93.95% global support&lt;/a&gt; for the attribute - not quite high enough for me to stop habitually using CSRF tokens, but we're getting there.)&lt;/p&gt;
&lt;h4 id="samesite-missing"&gt;What if the SameSite attribute is missing?&lt;/h4&gt;
&lt;p&gt;Here's where things get interesting. If a cookie is set without a SameSite attribute at all, how should the browser treat it?&lt;/p&gt;
&lt;p&gt;Over the past year, all of the major browsers have been changing their default behaviour. The goal is for a missing SameSite attribute to be treated as if it was &lt;code&gt;SameSite=Lax&lt;/code&gt; - providing CSRF protection by default.&lt;/p&gt;
&lt;p&gt;I have found it infuriatingly difficult to track down if and when this change has been made:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chrome/Chromium offer &lt;a href="https://www.chromium.org/updates/same-site"&gt;the best documentation&lt;/a&gt; - they claim to have ramped up the new default to 100% of users in August 2020. WebViews in Android still have the old default behaviour, which is scheduled to be fixed in Android 12 (&lt;a href="https://en.wikipedia.org/wiki/Android_12"&gt;not yet released&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Firefox have a &lt;a href="https://hacks.mozilla.org/2020/08/changes-to-samesite-cookie-behavior/"&gt;blog entry from August 2020&lt;/a&gt; which says "Starting with Firefox 79 (June 2020), we rolled it out to 50% of the Firefox Beta user base" - but I've not been able to find any subsequent updates. &lt;strong&gt;Update 26th August 2024:&lt;/strong&gt; It &lt;a href="https://simonwillison.net/2024/Aug/26/frederik-braun/"&gt;turns out&lt;/a&gt; Firefox didn't ship this after all, going with their own &lt;a href="https://blog.mozilla.org/en/mozilla/firefox-rolls-out-total-cookie-protection-by-default-to-all-users-worldwide/"&gt;Total Cookie Protection&lt;/a&gt; solution instead, which rolled out in April 2023.&lt;/li&gt;
&lt;li&gt;I have no idea at all what's going on with Safari!&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I started &lt;a href="https://twitter.com/simonw/status/1422366158171238400"&gt;a Twitter thread&lt;/a&gt; to try and collect more information, so please reply there if you know what's going on in more detail.&lt;/p&gt;
&lt;h4 id="chrome-2-minute-twist"&gt;The Chrome 2-minute twist&lt;/h4&gt;
&lt;p&gt;Assuming all of the above, the mystery remained: how did Yan's exploit fail to be prevented by browsers?&lt;/p&gt;
&lt;p&gt;After some back-and-forth about this on Twitter &lt;a href="https://twitter.com/bcrypt/status/1422370774896177154"&gt;Yan proposed&lt;/a&gt; that the answer may be this detail, tucked away on the &lt;a href="https://www.chromestatus.com/feature/5088147346030592"&gt;Chrome Platform Status page for Feature: Cookies default to SameSite=Lax&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Note: Chrome will make an exception for cookies set without a SameSite attribute less than 2 minutes ago. Such cookies will also be sent with non-idempotent (e.g. POST) top-level cross-site requests despite normal SameSite=Lax cookies requiring top-level cross-site requests to have a safe (e.g. GET) HTTP method. Support for this intervention ("Lax + POST") will be removed in the future.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It looks like OkCupid were setting their authentication cookie without a &lt;code&gt;SameSite&lt;/code&gt; attribute... which opened them up to a form-based CSRF attack but only for the 120 seconds following the cookie being set!&lt;/p&gt;
&lt;h4 id="samesite-explore-tool"&gt;Building a tool to explore SameSite browser behaviour&lt;/h4&gt;
&lt;p&gt;I was finding this all very confusing, so I built a tool.&lt;/p&gt;
&lt;p&gt;&lt;img alt="A screenshot showing the two pages from the demo side-by-side" src="https://static.simonwillison.net/static/2021/samesite-tool.png" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;p&gt;The code lives in &lt;a href="https://github.com/simonw/samesite-lax-demo"&gt;simonw/samesite-lax-demo&lt;/a&gt; on GitHub, but the tool itself has two sides:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;A server-side Python (&lt;a href="https://www.starlette.io/"&gt;Starlette&lt;/a&gt;) web application for setting cookies with different &lt;code&gt;SameSite&lt;/code&gt; attributes. This is hosted on Vercel at &lt;a href="https://samesite-lax-demo.vercel.app/"&gt;https://samesite-lax-demo.vercel.app/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;An HTML page on a different domain that links to that cookied site, provides a POST form targetting it, embeds an image from it and executes some &lt;code&gt;fetch()&lt;/code&gt; requests against it. This is at &lt;a href="https://simonw.github.io/samesite-lax-demo/"&gt;https://simonw.github.io/samesite-lax-demo/&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hosting on two separate domains is critical for the tool to show what is going on. I chose Vercel and GitHub Pages because they are both trivial to set up to continuously deploy changes from a GitHub repository.&lt;/p&gt;
&lt;p&gt;Using the tool in different browsers helps show exactly what is going on with regards to cross-domain cookies.&lt;/p&gt;
&lt;p&gt;A few of the things I observed using the tool:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SameSite=Strict&lt;/code&gt; works as you would expect. It's particularly interesting to follow the regular &lt;code&gt;&amp;lt;a href=...&amp;gt;&lt;/code&gt; link from the static site to the application and see how the strict cookie is NOT visible upon arrival - but becomes visible when you refresh that page.&lt;/li&gt;
&lt;li&gt;I included a dynamically generated SVG in a &lt;code&gt;&amp;lt;img src="/cookies.svg"&amp;gt;&lt;/code&gt; image tag, which shows the cookies (using SVG &lt;code&gt;&amp;lt;text&amp;gt;&lt;/code&gt;) that are visible to the request. That image shows all four types of cookie when embedded on the Vercel domain, but when embedded on the GitHub pages domain it differs wildly:
&lt;ul&gt;
&lt;li&gt;Firefox 89 shows both the &lt;code&gt;SameSite=None&lt;/code&gt; and the missing SameSite cookies&lt;/li&gt;
&lt;li&gt;Chrome 92 shows just the &lt;code&gt;SameSite=None&lt;/code&gt; cookie&lt;/li&gt;
&lt;li&gt;Safari 14.0 shows no cookies at all!&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Chrome won't let you set a &lt;code&gt;SameSite=None&lt;/code&gt; cookie without including the &lt;code&gt;Secure&lt;/code&gt; attribute.&lt;/li&gt;
&lt;li&gt;I also added some JavaScript that makes a cross-domain &lt;code&gt;fetch(..., {credentials: "include"})&lt;/code&gt; call against a &lt;code&gt;/cookies.json&lt;/code&gt; endpoint. This didn't send any cookies at all until I added server-side headers &lt;code&gt;access-control-allow-origin: https://simonw.github.io&lt;/code&gt; and &lt;code&gt;access-control-allow-credentials: true&lt;/code&gt;. Having done that, I got the same results across the three browsers as for the &lt;code&gt;&amp;lt;img&lt;/code&gt; test described above.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Safari ignoring &lt;code&gt;SameSite=None&lt;/code&gt; looked like it was this bug: &lt;a href="https://bugs.webkit.org/show_bug.cgi?id=198181"&gt;Cookies with SameSite=None or SameSite=invalid treated as Strict&lt;/a&gt; - it's marked as fixed but it's not clear to me if the fix has been released yet - I still saw that behaviour on my macOS 10.15.6 laptop or my iOS 14.7.1 iPhone.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt;  	
&lt;a href="https://news.ycombinator.com/item?id=28092943"&gt;krinchan on Hacker News&lt;/a&gt; has an answer here:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;The Safari "bug" is a new setting that's turned on by default: "Prevent cross-site tracking". It treats all cookies as SameSite=Lax, even cookies with SameSite=None.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://webkit.org/blog/10218/full-third-party-cookie-blocking-and-more/"&gt;Full Third-Party Cookie Blocking and More&lt;/a&gt; on the WebKit blog has more about this.&lt;/p&gt;

&lt;p&gt;Most excitingly, I was able to replicate the Chrome two minute window bug using the tool! Each cookie has its value set to the timestamp when it was created, and I added code to display how many seconds ago the cookie was set. Here's an animation showing how Chrome on a form submission navigation can see the cookie that was set with &lt;code&gt;SameSite&lt;/code&gt; missing at 114 seconds old, but that cookie is no longer visible once it passes 120 seconds.&lt;/p&gt;
&lt;p&gt;&lt;img alt="Animated demo of the tool in Chrome" src="https://static.simonwillison.net/static/2021/chrome-samesite-missing-loop.gif" style="max-width:100%;" /&gt;&lt;/p&gt;
&lt;h4 id="consider-subdomains"&gt;Consider your subdomains&lt;/h4&gt;
&lt;p&gt;One last note about CSRF that you should consider:  &lt;code&gt;SameSite=Lax&lt;/code&gt; still allows form submissions from  subdomains of your primary domain to carry their cookies.&lt;/p&gt;
&lt;p&gt;This means that if you have a XSS vulnerability on one of your subdomains the security of your primary domain will be compromised.&lt;/p&gt;
&lt;p&gt;Since it's common for subdomains to host other applications that may have their own security concerns, ditching CSRF tokens for Lax cookies may not be a wise step!&lt;/p&gt;
&lt;h4 id="login-csrf-samesite-lax"&gt;Login CSRF and SameSite=Lax&lt;/h4&gt;
&lt;p&gt;Login CSRF is an interesting variety of CSRF with slightly different rules.&lt;/p&gt;
&lt;p&gt;A Login CSRF attack is when a malicious forces a user to sign into an account controlled by the attacker. Why do this? Because if that user then saves sensitive information the attacker can see it.&lt;/p&gt;
&lt;p&gt;Imagine I trick you into signing into an e-commerce account I control and saving your credit card details. I could then later sign in myself and buy things on your card!&lt;/p&gt;
&lt;p&gt;Here's how that would work: Say the site's login form makes a POST to &lt;code&gt;https://www.example.com/login&lt;/code&gt; with &lt;code&gt;username&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; as the form fields. If those credentials match, the site sets an authentication cookie.&lt;/p&gt;
&lt;p&gt;I can set up my evil website with the following form:&lt;/p&gt;
&lt;div class="highlight highlight-text-html-basic"&gt;&lt;pre&gt;&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;form&lt;/span&gt; &lt;span class="pl-c1"&gt;action&lt;/span&gt;="&lt;span class="pl-s"&gt;https://www.example.com/login&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-c1"&gt;type&lt;/span&gt;="&lt;span class="pl-s"&gt;hidden&lt;/span&gt;" &lt;span class="pl-c1"&gt;name&lt;/span&gt;="&lt;span class="pl-s"&gt;username&lt;/span&gt;" &lt;span class="pl-c1"&gt;value&lt;/span&gt;="&lt;span class="pl-s"&gt;my-username&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;input&lt;/span&gt; &lt;span class="pl-c1"&gt;type&lt;/span&gt;="&lt;span class="pl-s"&gt;hidden&lt;/span&gt;" &lt;span class="pl-c1"&gt;name&lt;/span&gt;="&lt;span class="pl-s"&gt;password&lt;/span&gt;" &lt;span class="pl-c1"&gt;value&lt;/span&gt;="&lt;span class="pl-s"&gt;my-password&lt;/span&gt;"&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;form&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="pl-kos"&gt;&amp;lt;&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;span class="pl-smi"&gt;document&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-c1"&gt;forms&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;&lt;span class="pl-c1"&gt;0&lt;/span&gt;&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;submit&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="pl-ent"&gt;script&lt;/span&gt;&lt;span class="pl-kos"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;I trick you into visiting my evil pge and you're now signed in to that site using an account that I control. I cross my fingers and hope you don't notice the "you are signed in as X" message in the UI.&lt;/p&gt;
&lt;p&gt;An interesting thing about Login CSRF is that, since it involves setting a cookie but not sending a cookie, &lt;code&gt;SameSite=Lax&lt;/code&gt; would seem to make no difference at all. You need to look to other mechanisms to protect against this attack.&lt;/p&gt;
&lt;p&gt;But actually, you can use &lt;code&gt;SameSite=Lax&lt;/code&gt; to prevent these. The trick is to only allow logins from users that are carrying at least one cookie which you have set in that way - since you know that those cookies could not have been sent if the user originated in a form on another site.&lt;/p&gt;
&lt;p&gt;Another (potentially better) option: check the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin"&gt;HTTP Origin header&lt;/a&gt; on the oncoming request.&lt;/p&gt;
&lt;h4 id="final-recommendations"&gt;Final recommendations&lt;/h4&gt;
&lt;p&gt;As an application developer, you should set all cookies with &lt;code&gt;SameSite=Lax&lt;/code&gt; unless you have a very good reason not to. Most web frameworks do this by default now - Django shipped &lt;a href="https://github.com/django/django/commit/9a56b4b13ed92d2d5bb00d6bdb905a73bc5f2f0a"&gt;support for this&lt;/a&gt; in &lt;a href="https://docs.djangoproject.com/en/3.2/releases/2.1/#requests-and-responses"&gt;Django 2.1&lt;/a&gt; in August 2018.&lt;/p&gt;
&lt;p&gt;Do you still need CSRF tokens as well? I think so: I don't like the idea of users who fire up an older browser (maybe borrowing an obsolete computer) being vulnerable to this attack, and I worry about the subdomain issue described above.&lt;/p&gt;
&lt;p&gt;And if you work for a browser vendor, please make it easier to find information on what the default behaviour is and when it was shipped!&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/csrf"&gt;csrf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/samesite"&gt;samesite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/starlette"&gt;starlette&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="chrome"/><category term="cookies"/><category term="csrf"/><category term="security"/><category term="samesite"/><category term="starlette"/></entry><entry><title>Quoting Chromium Blog</title><link href="https://simonwillison.net/2021/Jul/14/chromium-blog/#atom-tag" rel="alternate"/><published>2021-07-14T19:14:22+00:00</published><updated>2021-07-14T19:14:22+00:00</updated><id>https://simonwillison.net/2021/Jul/14/chromium-blog/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://blog.chromium.org/2021/07/increasing-https-adoption.html"&gt;&lt;p&gt;Beginning in M94, Chrome will offer HTTPS-First Mode, which will attempt to upgrade all page loads to HTTPS and display a full-page warning before loading sites that don’t support it. Users who enable this mode gain confidence that Chrome is connecting them to sites over HTTPS whenever possible, and that they will see a warning before connecting to sites over HTTP. Based on ecosystem feedback, we’ll explore making HTTPS-First mode the default for all users in the future.&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://blog.chromium.org/2021/07/increasing-https-adoption.html"&gt;Chromium Blog&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/browsers"&gt;browsers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/security"&gt;security&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/https"&gt;https&lt;/a&gt;&lt;/p&gt;



</summary><category term="browsers"/><category term="chrome"/><category term="security"/><category term="https"/></entry><entry><title>Intent to Remove: HTTP/2 and gQUIC server push</title><link href="https://simonwillison.net/2020/Nov/12/intent-remove-http2-and-gquic-server-push/#atom-tag" rel="alternate"/><published>2020-11-12T01:44:20+00:00</published><updated>2020-11-12T01:44:20+00:00</updated><id>https://simonwillison.net/2020/Nov/12/intent-remove-http2-and-gquic-server-push/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://groups.google.com/a/chromium.org/g/blink-dev/c/K3rYLvmQUBY/m/vOWBKZGoAQAJ"&gt;Intent to Remove: HTTP/2 and gQUIC server push&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The Chrome / Blink team announce their intent to remove HTTP/2 server push support, where servers can start pushing an asset to a client before it has been requested. It’s been in browsers for over five years now and adoption is terrible. “Over the past 28 days [...] 99.97% of connections never received a pushed stream that got matched with a request [...] These numbers are exactly the same as in June 2019”. Datasette serves redirects with Link: preload headers that cause smart proxies (like Cloudflare) to push the redirected page to the client along with the redirect, but I don’t exepect to miss that optimization if it quietly stops working.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/cramforce/status/1326684546205581312"&gt;@cramforce&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/http2"&gt;http2&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/datasette"&gt;datasette&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="http2"/><category term="datasette"/></entry><entry><title>AVIF has landed</title><link href="https://simonwillison.net/2020/Sep/9/avif-has-landed/#atom-tag" rel="alternate"/><published>2020-09-09T16:49:24+00:00</published><updated>2020-09-09T16:49:24+00:00</updated><id>https://simonwillison.net/2020/Sep/9/avif-has-landed/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://jakearchibald.com/2020/avif-has-landed/"&gt;AVIF has landed&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
AVIF support landed in Chrome 85 a few weeks ago. It’s a new lossy royalty-free image format derived from AV1 video and it’s really impressive—it can achieve similar results to JPEG using a quarter of the file size! Jake digs into AVIF in detail, providing lots of illustrative examples created using the Squoosh online compressor, which now supports AVIF encoding. Jake used the same WebAssembly encoder from Squoosh to decode AVIF images in a web worker so that the demos in his article would work even for browsers that don’t yet support AVIF natively.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/images"&gt;images&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webworkers"&gt;webworkers&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webassembly"&gt;webassembly&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/jake-archibald"&gt;jake-archibald&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="images"/><category term="webworkers"/><category term="webassembly"/><category term="jake-archibald"/></entry><entry><title>html-to-svg</title><link href="https://simonwillison.net/2020/May/7/html-svg/#atom-tag" rel="alternate"/><published>2020-05-07T06:01:44+00:00</published><updated>2020-05-07T06:01:44+00:00</updated><id>https://simonwillison.net/2020/May/7/html-svg/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/as-a-service/html-to-svg"&gt;html-to-svg&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
‪This is absolutely ingenious: 50 lines of JavaScript which uses Puppeteer to get headless Chrome to grab a PDF screenshot of a page, then shells out to Inkscape to convert the PDF to SVG. Wraps the whole thing up in a Docker container and ships it to Cloud Run as a web service you can call by passing it a URL.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/steren/status/1258273345843290118"&gt;@steren&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/svg"&gt;svg&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cloudrun"&gt;cloudrun&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/puppeteer"&gt;puppeteer&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="svg"/><category term="cloudrun"/><category term="puppeteer"/></entry><entry><title>2020 Web Milestones</title><link href="https://simonwillison.net/2020/Jan/24/2020-web-milestones/#atom-tag" rel="alternate"/><published>2020-01-24T04:43:16+00:00</published><updated>2020-01-24T04:43:16+00:00</updated><id>https://simonwillison.net/2020/Jan/24/2020-web-milestones/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://mike.sherov.com/2020-web-milestones/"&gt;2020 Web Milestones&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
A lot of stuff is happening in 2020! Mike Sherov rounds it up—highlights include the release of Chromium Edge (Microsoft’s Chrome-powered browser for Windows 7+), Web Components supported in every major browser, Deno 1.x, SameSite Cookies turned on by default (which should dramatically reduce CSRF exposure) and Python 2 and Flash EOLs.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/mikesherov/status/1220562604348846081"&gt;@mikesherov&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/csrf"&gt;csrf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/flash"&gt;flash&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/internet-explorer"&gt;internet-explorer&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/python"&gt;python&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/web"&gt;web&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/deno"&gt;deno&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/samesite"&gt;samesite&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="csrf"/><category term="flash"/><category term="internet-explorer"/><category term="javascript"/><category term="python"/><category term="web"/><category term="deno"/><category term="samesite"/></entry><entry><title>Quoting Troy Hunt</title><link href="https://simonwillison.net/2020/Jan/3/troy-hunt/#atom-tag" rel="alternate"/><published>2020-01-03T16:22:42+00:00</published><updated>2020-01-03T16:22:42+00:00</updated><id>https://simonwillison.net/2020/Jan/3/troy-hunt/#atom-tag</id><summary type="html">
    &lt;blockquote cite="https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/"&gt;&lt;p&gt;Come version 80, any cookie without a SameSite attribute will be treated as "Lax" by Chrome. This is really important to understand because put simply, it'll very likely break a bunch of stuff. [...] The fix is easy, all it needs is for everyone responsible for maintaining any system that uses cookies that might be passed from an external origin to understand what's going on. Can't be that hard, right? Hello? Oh...&lt;/p&gt;&lt;/blockquote&gt;
&lt;p class="cite"&gt;&amp;mdash; &lt;a href="https://www.troyhunt.com/promiscuous-cookies-and-their-impending-death-via-the-samesite-policy/"&gt;Troy Hunt&lt;/a&gt;&lt;/p&gt;

    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/csrf"&gt;csrf&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/cookies"&gt;cookies&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/samesite"&gt;samesite&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/troy-hunt"&gt;troy-hunt&lt;/a&gt;&lt;/p&gt;



</summary><category term="csrf"/><category term="cookies"/><category term="chrome"/><category term="samesite"/><category term="troy-hunt"/></entry><entry><title>BigInt: arbitrary-precision integers in JavaScript</title><link href="https://simonwillison.net/2018/Nov/28/bigint-javascript/#atom-tag" rel="alternate"/><published>2018-11-28T02:22:59+00:00</published><updated>2018-11-28T02:22:59+00:00</updated><id>https://simonwillison.net/2018/Nov/28/bigint-javascript/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://developers.google.com/web/updates/2018/05/bigint#polyfilling_and_transpiling_bigints"&gt;BigInt: arbitrary-precision integers in JavaScript&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
The BigInt specification is now supported in Chrome—but it hasn’t yet made it to other browsers. The Chrome team have a really interesting solution: they’ve released a JSBI library which you can use to do BigInt calculations in any browser today, and an accompanying Babel plugin which can rewrite calls to that library into BigInt syntax once browser support catches up. I’ve never seen a library that includes a tool for refactoring itself into oblivion before.

    &lt;p&gt;&lt;small&gt;&lt;/small&gt;Via &lt;a href="https://twitter.com/mathias/status/1067447959950934017"&gt;@mathias&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="javascript"/></entry><entry><title>What's New In DevTools (Chrome 62)</title><link href="https://simonwillison.net/2017/Oct/13/devtools/#atom-tag" rel="alternate"/><published>2017-10-13T22:59:26+00:00</published><updated>2017-10-13T22:59:26+00:00</updated><id>https://simonwillison.net/2017/Oct/13/devtools/#atom-tag</id><summary type="html">
    
&lt;p&gt;&lt;strong&gt;&lt;a href="https://developers.google.com/web/updates/2017/08/devtools-release-notes"&gt;What&amp;#x27;s New In DevTools (Chrome 62)&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
Some really neat stuff. Highlights include top-level “await” support in the console, the ability to take screenshots of specific HTML nodes, CSS grid highlighting and the ability to drop a .HAR file onto the network panel in order to view it as a waterfall.


    &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/css"&gt;css&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/javascript"&gt;javascript&lt;/a&gt;&lt;/p&gt;



</summary><category term="chrome"/><category term="css"/><category term="javascript"/></entry><entry><title>What data structures are used to implement the DOM tree?</title><link href="https://simonwillison.net/2013/Feb/17/what-data-structures-are/#atom-tag" rel="alternate"/><published>2013-02-17T13:31:00+00:00</published><updated>2013-02-17T13:31:00+00:00</updated><id>https://simonwillison.net/2013/Feb/17/what-data-structures-are/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/What-data-structures-are-used-to-implement-the-DOM-tree/answer/Simon-Willison"&gt;What data structures are used to implement the DOM tree?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You may enjoy this post from Hixie back in 2002 which illustrates how different browsers deal with incorrectly nested HTML. IE6 used to create a tree that wasn't actually a tree! &lt;span&gt;&lt;a href="http://ln.hixie.ch/?count=1&amp;amp;start=1037910467"&gt;http://ln.hixie.ch/?start=103791...&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/data-structures"&gt;data-structures&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firefox"&gt;firefox&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/html"&gt;html&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/internet-explorer"&gt;internet-explorer&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/opera"&gt;opera&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/webkit"&gt;webkit&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/firefoxos"&gt;firefoxos&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="chrome"/><category term="data-structures"/><category term="firefox"/><category term="html"/><category term="internet-explorer"/><category term="opera"/><category term="webkit"/><category term="quora"/><category term="firefoxos"/></entry><entry><title>Why doesn't Google Chrome use the Google Search Engine spellchecker?</title><link href="https://simonwillison.net/2012/Jan/14/why-doesnt-google-chrome/#atom-tag" rel="alternate"/><published>2012-01-14T10:37:00+00:00</published><updated>2012-01-14T10:37:00+00:00</updated><id>https://simonwillison.net/2012/Jan/14/why-doesnt-google-chrome/#atom-tag</id><summary type="html">
    &lt;p&gt;&lt;em&gt;My answer to &lt;a href="https://www.quora.com/Why-doesnt-Google-Chrome-use-the-Google-Search-Engine-spellchecker/answer/Simon-Willison"&gt;Why doesn&amp;#39;t Google Chrome use the Google Search Engine spellchecker?&lt;/a&gt; on Quora&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Because the Google search spellchecker runs in their data centers, while the Chrome spellchecker runs on your local machine. Sending everything you type in to your browser to Google's servers just to check spelling would be an unnecessary privacy violation, especially since local spell checking does a good-enough job.&lt;/p&gt;
    
        &lt;p&gt;Tags: &lt;a href="https://simonwillison.net/tags/chrome"&gt;chrome&lt;/a&gt;, &lt;a href="https://simonwillison.net/tags/quora"&gt;quora&lt;/a&gt;&lt;/p&gt;
    

</summary><category term="chrome"/><category term="quora"/></entry></feed>