@@ -1686,8 +1686,6 @@ public void loadJavascriptCharset() throws Exception {
16861686 * Tests the Sec-Fetch-* headers sent for an <img> request.
16871687 * Real browsers: Sec-Fetch-Mode: no-cors, Sec-Fetch-Dest: image, no Sec-Fetch-User
16881688 * (the image was not requested by direct user activation).
1689- * HtmlUnit currently hardcodes Sec-Fetch-Mode: navigate, Sec-Fetch-Dest: document
1690- * and always adds Sec-Fetch-User: ?1.
16911689 *
16921690 * @throws Exception if the test fails
16931691 */
@@ -2479,4 +2477,321 @@ public void anchorClickFromScript() throws Exception {
24792477 assertEquals (Arrays .asList (expectedHeaders ).toString (), Arrays .asList (headers ).toString ());
24802478 }
24812479 }
2480+
2481+ /**
2482+ * Tests a link with {@code rel="noreferrer"}. This suppresses the {@code Referer}
2483+ * header entirely, but must not affect {@code Sec-Fetch-Site}: that header reflects
2484+ * the true relationship between initiator and target regardless of referrer-policy
2485+ * stripping, so it is still sent as {@code same-origin} here.
2486+ *
2487+ * @throws Exception if the test fails
2488+ */
2489+ @ Test
2490+ @ Alerts (CHROME = {"GET /2.html HTTP/1.1" ,
2491+ "Host: localhost:§§PORT§§" ,
2492+ "Connection: keep-alive" ,
2493+ "sec-ch-ua: §§SEC_USER_AGENT§§" ,
2494+ "sec-ch-ua-mobile: ?0" ,
2495+ "sec-ch-ua-platform: \" Windows\" " ,
2496+ "Upgrade-Insecure-Requests: 1" ,
2497+ "User-Agent: §§USER_AGENT§§" ,
2498+ "Accept: §§ACCEPT§§" ,
2499+ "Sec-Fetch-Site: same-origin" ,
2500+ "Sec-Fetch-Mode: navigate" ,
2501+ "Sec-Fetch-User: ?1" ,
2502+ "Sec-Fetch-Dest: document" ,
2503+ "Accept-Encoding: gzip, deflate, br, zstd" ,
2504+ "Accept-Language: en-US,en;q=0.9" },
2505+ EDGE = {"GET /2.html HTTP/1.1" ,
2506+ "Host: localhost:§§PORT§§" ,
2507+ "Connection: keep-alive" ,
2508+ "sec-ch-ua: §§SEC_USER_AGENT§§" ,
2509+ "sec-ch-ua-mobile: ?0" ,
2510+ "sec-ch-ua-platform: \" Windows\" " ,
2511+ "Upgrade-Insecure-Requests: 1" ,
2512+ "User-Agent: §§USER_AGENT§§" ,
2513+ "Accept: §§ACCEPT§§" ,
2514+ "Sec-Fetch-Site: same-origin" ,
2515+ "Sec-Fetch-Mode: navigate" ,
2516+ "Sec-Fetch-User: ?1" ,
2517+ "Sec-Fetch-Dest: document" ,
2518+ "Accept-Encoding: gzip, deflate, br, zstd" ,
2519+ "Accept-Language: en-US,en;q=0.9" },
2520+ FF = {"GET /2.html HTTP/1.1" ,
2521+ "Host: localhost:§§PORT§§" ,
2522+ "User-Agent: §§USER_AGENT§§" ,
2523+ "Accept: §§ACCEPT§§" ,
2524+ "Accept-Language: en-US,en;q=0.9" ,
2525+ "Accept-Encoding: gzip, deflate, br, zstd" ,
2526+ "Connection: keep-alive" ,
2527+ "Upgrade-Insecure-Requests: 1" ,
2528+ "Sec-Fetch-Dest: document" ,
2529+ "Sec-Fetch-Mode: navigate" ,
2530+ "Sec-Fetch-Site: same-origin" ,
2531+ "Sec-Fetch-User: ?1" ,
2532+ "Priority: u=0, i" },
2533+ FF_ESR = {"GET /2.html HTTP/1.1" ,
2534+ "Host: localhost:§§PORT§§" ,
2535+ "User-Agent: §§USER_AGENT§§" ,
2536+ "Accept: §§ACCEPT§§" ,
2537+ "Accept-Language: en-US,en;q=0.5" ,
2538+ "Accept-Encoding: gzip, deflate, br, zstd" ,
2539+ "Connection: keep-alive" ,
2540+ "Upgrade-Insecure-Requests: 1" ,
2541+ "Sec-Fetch-Dest: document" ,
2542+ "Sec-Fetch-Mode: navigate" ,
2543+ "Sec-Fetch-Site: same-origin" ,
2544+ "Sec-Fetch-User: ?1" ,
2545+ "Priority: u=0, i" })
2546+ @ HtmlUnitNYI (
2547+ CHROME = {"GET /2.html HTTP/1.1" ,
2548+ "Host: localhost:§§PORT§§" ,
2549+ "Connection: keep-alive" ,
2550+ "sec-ch-ua: §§SEC_USER_AGENT§§" ,
2551+ "sec-ch-ua-mobile: ?0" ,
2552+ "sec-ch-ua-platform: \" Windows\" " ,
2553+ "Upgrade-Insecure-Requests: 1" ,
2554+ "User-Agent: §§USER_AGENT§§" ,
2555+ "Accept: §§ACCEPT§§" ,
2556+ "Sec-Fetch-Site: same-origin" ,
2557+ "Sec-Fetch-Mode: navigate" ,
2558+ "Sec-Fetch-User: ?1" ,
2559+ "Sec-Fetch-Dest: document" ,
2560+ "Accept-Encoding: gzip, deflate, br" ,
2561+ "Accept-Language: en-US,en;q=0.9" },
2562+ EDGE = {"GET /2.html HTTP/1.1" ,
2563+ "Host: localhost:§§PORT§§" ,
2564+ "Connection: keep-alive" ,
2565+ "sec-ch-ua: §§SEC_USER_AGENT§§" ,
2566+ "sec-ch-ua-mobile: ?0" ,
2567+ "sec-ch-ua-platform: \" Windows\" " ,
2568+ "Upgrade-Insecure-Requests: 1" ,
2569+ "User-Agent: §§USER_AGENT§§" ,
2570+ "Accept: §§ACCEPT§§" ,
2571+ "Sec-Fetch-Site: same-origin" ,
2572+ "Sec-Fetch-Mode: navigate" ,
2573+ "Sec-Fetch-User: ?1" ,
2574+ "Sec-Fetch-Dest: document" ,
2575+ "Accept-Encoding: gzip, deflate, br" ,
2576+ "Accept-Language: en-US,en;q=0.9" },
2577+ FF = {"GET /2.html HTTP/1.1" ,
2578+ "Host: localhost:§§PORT§§" ,
2579+ "User-Agent: §§USER_AGENT§§" ,
2580+ "Accept: §§ACCEPT§§" ,
2581+ "Accept-Language: en-US,en;q=0.9" ,
2582+ "Accept-Encoding: gzip, deflate, br" ,
2583+ "Connection: keep-alive" ,
2584+ "Upgrade-Insecure-Requests: 1" ,
2585+ "Sec-Fetch-Dest: document" ,
2586+ "Sec-Fetch-Mode: navigate" ,
2587+ "Sec-Fetch-Site: same-origin" ,
2588+ "Sec-Fetch-User: ?1" ,
2589+ "Priority: u=0, i" },
2590+ FF_ESR = {"GET /2.html HTTP/1.1" ,
2591+ "Host: localhost:§§PORT§§" ,
2592+ "User-Agent: §§USER_AGENT§§" ,
2593+ "Accept: §§ACCEPT§§" ,
2594+ "Accept-Language: en-US,en;q=0.5" ,
2595+ "Accept-Encoding: gzip, deflate, br" ,
2596+ "Connection: keep-alive" ,
2597+ "Upgrade-Insecure-Requests: 1" ,
2598+ "Sec-Fetch-Dest: document" ,
2599+ "Sec-Fetch-Mode: navigate" ,
2600+ "Sec-Fetch-Site: same-origin" ,
2601+ "Sec-Fetch-User: ?1" ,
2602+ "Priority: u=0, i" })
2603+ public void anchorRelNoreferrer () throws Exception {
2604+ String html = DOCTYPE_HTML
2605+ + "<html><body><a id='my' href='2.html' rel='noreferrer'>Click me</a></body></html>" ;
2606+ html = "HTTP/1.1 200 OK\r \n "
2607+ + "Content-Length: " + (html .length ()) + "\r \n "
2608+ + "Content-Type: text/html\r \n "
2609+ + "Connection: close\r \n "
2610+ + "\r \n "
2611+ + html ;
2612+ final String hi = "HTTP/1.1 200 OK\r \n "
2613+ + "Content-Length: 2\r \n "
2614+ + "Content-Type: text/plain\r \n "
2615+ + "Connection: close\r \n "
2616+ + "\r \n "
2617+ + "Hi" ;
2618+
2619+ shutDownAll ();
2620+ try (PrimitiveWebServer primitiveWebServer = new PrimitiveWebServer (null , html , hi )) {
2621+ final WebDriver driver = getWebDriver ();
2622+
2623+ driver .get ("http://localhost:" + primitiveWebServer .getPort ());
2624+ driver .findElement (By .id ("my" )).click ();
2625+
2626+ final String [] expectedHeaders = getExpectedAlerts ();
2627+ for (int i = 0 ; i < expectedHeaders .length ; i ++) {
2628+ expectedHeaders [i ] = expectedHeaders [i ].replaceAll ("§§PORT§§" , "" + primitiveWebServer .getPort ());
2629+ expectedHeaders [i ] = expectedHeaders [i ].replaceAll ("§§USER_AGENT§§" ,
2630+ getBrowserVersion ().getUserAgent ());
2631+ expectedHeaders [i ] = expectedHeaders [i ].replaceAll ("§§SEC_USER_AGENT§§" ,
2632+ getBrowserVersion ().getSecClientHintUserAgentHeader ());
2633+ expectedHeaders [i ] = expectedHeaders [i ].replaceAll ("§§ACCEPT§§" ,
2634+ getBrowserVersion ().getHtmlAcceptHeader ());
2635+ }
2636+ final String request = primitiveWebServer .getRequests ().get (1 );
2637+ final String [] headers = request .split ("\\ r\\ n" );
2638+ assertEquals (Arrays .asList (expectedHeaders ).toString (), Arrays .asList (headers ).toString ());
2639+ }
2640+ }
2641+
2642+ /**
2643+ * Tests the Sec-Fetch-* headers sent for an <img crossorigin> request. The
2644+ * presence of the {@code crossorigin} attribute (regardless of its value) forces
2645+ * CORS mode, unlike a plain <img> which uses no-cors (see {@link #image()}).
2646+ *
2647+ * @throws Exception if the test fails
2648+ */
2649+ @ Test
2650+ @ Alerts (CHROME = {"GET /image.png HTTP/1.1" ,
2651+ "Host: localhost:§§PORT§§" ,
2652+ "Connection: keep-alive" ,
2653+ "Origin: http://localhost:22225" ,
2654+ "sec-ch-ua-platform: \" Windows\" " ,
2655+ "User-Agent: §§USER_AGENT§§" ,
2656+ "sec-ch-ua: §§SEC_USER_AGENT§§" ,
2657+ "sec-ch-ua-mobile: ?0" ,
2658+ "Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8" ,
2659+ "Sec-Fetch-Site: same-origin" ,
2660+ "Sec-Fetch-Mode: cors" ,
2661+ "Sec-Fetch-Dest: image" ,
2662+ "Referer: http://localhost:§§PORT§§/" ,
2663+ "Accept-Encoding: gzip, deflate, br, zstd" ,
2664+ "Accept-Language: en-US,en;q=0.9" },
2665+ EDGE = {"GET /image.png HTTP/1.1" ,
2666+ "Host: localhost:§§PORT§§" ,
2667+ "Connection: keep-alive" ,
2668+ "Origin: http://localhost:22225" ,
2669+ "sec-ch-ua-platform: \" Windows\" " ,
2670+ "User-Agent: §§USER_AGENT§§" ,
2671+ "sec-ch-ua: §§SEC_USER_AGENT§§" ,
2672+ "sec-ch-ua-mobile: ?0" ,
2673+ "Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8" ,
2674+ "Sec-Fetch-Site: same-origin" ,
2675+ "Sec-Fetch-Mode: cors" ,
2676+ "Sec-Fetch-Dest: image" ,
2677+ "Referer: http://localhost:§§PORT§§/" ,
2678+ "Accept-Encoding: gzip, deflate, br, zstd" ,
2679+ "Accept-Language: en-US,en;q=0.9" },
2680+ FF = {"GET /image.png HTTP/1.1" ,
2681+ "Host: localhost:§§PORT§§" ,
2682+ "User-Agent: §§USER_AGENT§§" ,
2683+ "Accept: image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5" ,
2684+ "Accept-Language: en-US,en;q=0.9" ,
2685+ "Accept-Encoding: gzip, deflate, br, zstd" ,
2686+ "Connection: keep-alive" ,
2687+ "Referer: http://localhost:§§PORT§§/" ,
2688+ "Sec-Fetch-Dest: image" ,
2689+ "Sec-Fetch-Mode: cors" ,
2690+ "Sec-Fetch-Site: same-origin" ,
2691+ "Priority: u=4, i" },
2692+ FF_ESR = {"GET /image.png HTTP/1.1" ,
2693+ "Host: localhost:§§PORT§§" ,
2694+ "User-Agent: §§USER_AGENT§§" ,
2695+ "Accept: image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5" ,
2696+ "Accept-Language: en-US,en;q=0.5" ,
2697+ "Accept-Encoding: gzip, deflate, br, zstd" ,
2698+ "Connection: keep-alive" ,
2699+ "Referer: http://localhost:§§PORT§§/" ,
2700+ "Sec-Fetch-Dest: image" ,
2701+ "Sec-Fetch-Mode: cors" ,
2702+ "Sec-Fetch-Site: same-origin" ,
2703+ "Priority: u=4, i" })
2704+ @ HtmlUnitNYI (
2705+ CHROME = {"GET /image.png HTTP/1.1" ,
2706+ "Host: localhost:§§PORT§§" ,
2707+ "Connection: keep-alive" ,
2708+ "sec-ch-ua: §§SEC_USER_AGENT§§" ,
2709+ "sec-ch-ua-mobile: ?0" ,
2710+ "sec-ch-ua-platform: \" Windows\" " ,
2711+ "User-Agent: §§USER_AGENT§§" ,
2712+ "Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8" ,
2713+ "Sec-Fetch-Site: same-origin" ,
2714+ "Sec-Fetch-Mode: cors" ,
2715+ "Sec-Fetch-Dest: image" ,
2716+ "Referer: http://localhost:§§PORT§§/" ,
2717+ "Accept-Encoding: gzip, deflate, br" ,
2718+ "Accept-Language: en-US,en;q=0.9" ,
2719+ "Origin: http://localhost:22225" },
2720+ EDGE = {"GET /image.png HTTP/1.1" ,
2721+ "Host: localhost:§§PORT§§" ,
2722+ "Connection: keep-alive" ,
2723+ "sec-ch-ua: §§SEC_USER_AGENT§§" ,
2724+ "sec-ch-ua-mobile: ?0" ,
2725+ "sec-ch-ua-platform: \" Windows\" " ,
2726+ "User-Agent: §§USER_AGENT§§" ,
2727+ "Accept: image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8" ,
2728+ "Sec-Fetch-Site: same-origin" ,
2729+ "Sec-Fetch-Mode: cors" ,
2730+ "Sec-Fetch-Dest: image" ,
2731+ "Referer: http://localhost:§§PORT§§/" ,
2732+ "Accept-Encoding: gzip, deflate, br" ,
2733+ "Accept-Language: en-US,en;q=0.9" ,
2734+ "Origin: http://localhost:22225" },
2735+ FF = {"GET /image.png HTTP/1.1" ,
2736+ "Host: localhost:§§PORT§§" ,
2737+ "User-Agent: §§USER_AGENT§§" ,
2738+ "Accept: image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5" ,
2739+ "Accept-Language: en-US,en;q=0.9" ,
2740+ "Accept-Encoding: gzip, deflate, br" ,
2741+ "Connection: keep-alive" ,
2742+ "Referer: http://localhost:§§PORT§§/" ,
2743+ "Sec-Fetch-Dest: image" ,
2744+ "Sec-Fetch-Mode: cors" ,
2745+ "Sec-Fetch-Site: same-origin" ,
2746+ "Priority: u=0, i" },
2747+ FF_ESR = {"GET /image.png HTTP/1.1" ,
2748+ "Host: localhost:§§PORT§§" ,
2749+ "User-Agent: §§USER_AGENT§§" ,
2750+ "Accept: image/avif,image/webp,image/png,image/svg+xml,image/*;q=0.8,*/*;q=0.5" ,
2751+ "Accept-Language: en-US,en;q=0.5" ,
2752+ "Accept-Encoding: gzip, deflate, br" ,
2753+ "Connection: keep-alive" ,
2754+ "Referer: http://localhost:§§PORT§§/" ,
2755+ "Sec-Fetch-Dest: image" ,
2756+ "Sec-Fetch-Mode: cors" ,
2757+ "Sec-Fetch-Site: same-origin" ,
2758+ "Priority: u=0, i" })
2759+ public void imageCrossOrigin () throws Exception {
2760+ final String html = DOCTYPE_HTML
2761+ + "<html><head></head><body><img crossorigin='anonymous' src='image.png'></body></html>" ;
2762+ final String htmlResponse = "HTTP/1.1 200 OK\r \n "
2763+ + "Content-Length: " + html .length () + "\r \n "
2764+ + "Content-Type: text/html\r \n "
2765+ + "Connection: close\r \n "
2766+ + "\r \n "
2767+ + html ;
2768+ final String imageResponse = "HTTP/1.1 200 OK\r \n "
2769+ + "Content-Length: 2\r \n "
2770+ + "Content-Type: image/png\r \n "
2771+ + "Connection: close\r \n "
2772+ + "\r \n "
2773+ + "Hi" ;
2774+
2775+ shutDownAll ();
2776+ try (PrimitiveWebServer primitiveWebServer = new PrimitiveWebServer (null , htmlResponse , imageResponse )) {
2777+ final WebDriver driver = getWebDriver ();
2778+
2779+ driver .get ("http://localhost:" + primitiveWebServer .getPort ());
2780+
2781+ // force image download in htmlunit
2782+ driver .findElement (By .tagName ("img" )).getAttribute ("height" );
2783+
2784+ final String [] expectedHeaders = getExpectedAlerts ();
2785+ for (int i = 0 ; i < expectedHeaders .length ; i ++) {
2786+ expectedHeaders [i ] = expectedHeaders [i ].replaceAll ("§§PORT§§" , "" + primitiveWebServer .getPort ());
2787+ expectedHeaders [i ] = expectedHeaders [i ].replaceAll ("§§USER_AGENT§§" ,
2788+ getBrowserVersion ().getUserAgent ());
2789+ expectedHeaders [i ] = expectedHeaders [i ].replaceAll ("§§SEC_USER_AGENT§§" ,
2790+ getBrowserVersion ().getSecClientHintUserAgentHeader ());
2791+ }
2792+ final String request = primitiveWebServer .getRequests ().get (1 );
2793+ final String [] headers = request .split ("\\ r\\ n" );
2794+ assertEquals (Arrays .asList (expectedHeaders ).toString (), Arrays .asList (headers ).toString ());
2795+ }
2796+ }
24822797}
0 commit comments