Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

[Problem/Bug]: WebResourceRequested is not raised for subresources of a cross-origin iframe in a NewWindow WebView2

Aperta
#5,787 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
4/5
Tempo stimato
3-5 giorni
Idoneità per principianti
54/100
Tipo di issue
Bug
Chiarezza
Specificata chiaramente
Stato di attività
Attiva
Stack tecnologico
rust
Ambito
backend, web-dev

Direzione di ricerca

Start by reproducing the issue with the Rust/wry sample in the issue, especially the NewWindowRequested setup and WebResourceRequested filter registration. Compare the logged requests for same-origin and cross-origin iframe subresources in the popup, including with REREGISTER=1. Done means the popup's cross-origin iframe scripts and fetch requests are raised through WebResourceRequested and served by the custom protocol.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

bug
What happened?

In a WebView2 that is passed to CoreWebView2NewWindowRequestedEventArgs.NewWindow (a window.open() popup), WebResourceRequested is not raised for the subresource requests of a cross-origin iframe. The request for the iframe document is raised, but its scripts and fetch() calls are not. When the content is served from WebResourceRequested (a custom host such as http://frame.localhost/), those requests go to the network and fail with net::ERR_CONNECTION_REFUSED.

Every WebView2 gets the filter
AddWebResourceRequestedFilterWithRequestSourceKinds(L"http://frame.localhost/*", COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL, COREWEBVIEW2_WEB_RESOURCE_REQUEST_SOURCE_KINDS_ALL).

Request WebResourceRequested
Cross-origin iframe in a normally created WebView2: document and subresources raised
Same-origin iframe in the popup: document and subresources raised
Cross-origin script loaded by the popup's top-level document raised
Cross-origin iframe in the popup: document raised
Cross-origin iframe in the popup: subresources not raised

The last row is the same for an about:blank popup filled by the opener and for a popup that navigates to its own page.

Adding the filters and a second WebResourceRequested handler again after NewWindow is set, as suggested in #4689, does not change it: the second handler logs the iframe document but none of its subresources.

It looks like #2341 still applies to WebView2s created through NewWindowRequested. Related: #4689, #4181.

Found through Tauri / wry, which implement custom protocols this way, so a cross-origin iframe in any window.open() window of such an app cannot load its resources.

Importance

Moderate. My app's user experience is affected, but still usable.

Runtime Channel

Stable release (WebView2 Runtime)

Runtime Version

156.0.4314.50

SDK Version

1.0.3650.58

Framework

Win32

Operating System

Windows 11

OS Version

10.0.26300.9711

Repro steps

Rust, wry 0.55.1 with webview2-com 0.38.2 (SDK 1.0.3650.58). Sequence per WebView2, as wry does it:

  1. Create the controller with the opener's environment.
  2. AddWebResourceRequestedFilterWithRequestSourceKinds for http://app.localhost/* and http://frame.localhost/* (context ALL, source kinds ALL), then add_WebResourceRequested, which serves both hosts.
  3. In NewWindowRequested: take a deferral, create the popup WebView2 the same way (steps 1 and 2), set args.NewWindow, complete the deferral.

The main page (http://app.localhost/) opens three popups. Each iframe reports whether its script loaded and whether its fetch() succeeded.

cargo run:

WebView2 156.0.4314.50
1-cross-origin-iframe-in-main-window: frame.js loaded, fetch: ok
3-cross-origin-iframe-in-about-blank-popup: frame.js FAILED, fetch: error Failed to fetch
2-same-origin-iframe-in-about-blank-popup: frame.js loaded, fetch: ok
4-cross-origin-iframe-in-about-blank-popup-after-1s: frame.js FAILED, fetch: error Failed to fetch
5-popup-page-itself: cross-origin frame.js loaded
6-cross-origin-iframe-in-popup-page: frame.js FAILED, fetch: error Failed to fetch

With REREGISTER=1, the filters and a second handler that only logs are added again after NewWindow is set. It sees the documents of the cross-origin iframes and the subresources of the same-origin iframe, but never frame.localhost/frame.js or frame.localhost/data.txt from inside an iframe:

WebView2 156.0.4314.50
1-cross-origin-iframe-in-main-window: frame.js loaded, fetch: ok
  seen by the handler added after SetNewWindow: http://app.localhost/frame.html?2-same-origin-iframe-in-about-blank-popup
  seen by the handler added after SetNewWindow: http://frame.localhost/frame.html?3-cross-origin-iframe-in-about-blank-popup
  seen by the handler added after SetNewWindow: http://app.localhost/frame.js
  seen by the handler added after SetNewWindow: http://app.localhost/data.txt
3-cross-origin-iframe-in-about-blank-popup: frame.js FAILED, fetch: error Failed to fetch
2-same-origin-iframe-in-about-blank-popup: frame.js loaded, fetch: ok
  seen by the handler added after SetNewWindow: http://frame.localhost/frame.html?4-cross-origin-iframe-in-about-blank-popup-after-1s
4-cross-origin-iframe-in-about-blank-popup-after-1s: frame.js FAILED, fetch: error Failed to fetch
  seen by the handler added after SetNewWindow: http://app.localhost/popup.html
  seen by the handler added after SetNewWindow: http://frame.localhost/frame.js
5-popup-page-itself: cross-origin frame.js loaded
  seen by the handler added after SetNewWindow: http://frame.localhost/frame.html?6-cross-origin-iframe-in-popup-page
6-cross-origin-iframe-in-popup-page: frame.js FAILED, fetch: error Failed to fetch
  seen by the handler added after SetNewWindow: http://app.localhost/favicon.ico

(http://frame.localhost/frame.js in this log is the script of the popup's top-level page, case 5.)

Cargo.toml

[package]
name = "popup-iframe-protocol"
version = "0.1.0"
edition = "2021"

[dependencies]
wry = "=0.55.1"
tao = "0.35"
webview2-com = "=0.38.2"
windows-core = "0.61"

src/main.rs

use std::{borrow::Cow, cell::{Cell, RefCell}, rc::Rc, time::Duration};
use tao::{
    event::Event,
    event_loop::{ControlFlow, EventLoopBuilder},
    window::WindowBuilder,
};
use webview2_com::{take_pwstr, Microsoft::Web::WebView2::Win32::*, WebResourceRequestedEventHandler};
use windows_core::{Interface, HSTRING, PWSTR};
use wry::{
    http::{header::CONTENT_TYPE, Request, Response},
    NewWindowResponse, WebViewBuilder, WebViewBuilderExtWindows, WebViewExtWindows, WebViewId,
};

enum Ev {
    Exit,
    AfterSetNewWindow,
}

// REREGISTER=1: once wry has called SetNewWindow, add the filters again and a second handler that only logs.
unsafe fn reregister(webview: &ICoreWebView2) {
    let w22: ICoreWebView2_22 = webview.cast().unwrap();
    for f in ["http://app.localhost/*", "http://frame.localhost/*"] {
        w22.AddWebResourceRequestedFilterWithRequestSourceKinds(
            &HSTRING::from(f),
            COREWEBVIEW2_WEB_RESOURCE_CONTEXT_ALL,
            COREWEBVIEW2_WEB_RESOURCE_REQUEST_SOURCE_KINDS_ALL,
        )
        .unwrap();
    }
    let mut token = 0i64;
    webview
        .add_WebResourceRequested(
            &WebResourceRequestedEventHandler::create(Box::new(|_, args| {
                if let Some(args) = args {
                    let mut uri = PWSTR::null();
                    args.Request()?.Uri(&mut uri)?;
                    println!("  seen by the handler added after SetNewWindow: {}", take_pwstr(uri));
                }
                Ok(())
            })),
            &mut token,
        )
        .unwrap();
}

// The page is served by the `app` protocol (http://app.localhost/), the iframes by `frame`
// (http://frame.localhost/, cross-origin) or by `app` (same-origin). Both protocols serve the same files.
const MAIN: &str = r#"<!doctype html><title>main</title>
<iframe src="http://frame.localhost/frame.html?1-cross-origin-iframe-in-main-window"></iframe>
<script>
addEventListener('message', (e) => window.ipc.postMessage(String(e.data)));
const frame = (doc, origin, name) => {
  const f = doc.createElement('iframe');
  f.src = origin + '/frame.html?' + name;
  doc.body.append(f);
};
setTimeout(() => {
  // about:blank popup, filled by the opener right after window.open()
  const w = window.open('about:blank', 'a', 'popup,width=500,height=300');
  w.document.open(); w.document.write('<!doctype html><title>a</title>'); w.document.close();
  frame(w.document, 'http://app.localhost', '2-same-origin-iframe-in-about-blank-popup');
  frame(w.document, 'http://frame.localhost', '3-cross-origin-iframe-in-about-blank-popup');
}, 500);
setTimeout(() => {
  const w = window.open('about:blank', 'b', 'popup,width=500,height=300');
  setTimeout(() => frame(w.document, 'http://frame.localhost', '4-cross-origin-iframe-in-about-blank-popup-after-1s'), 1000);
}, 1500);
setTimeout(() => window.open('popup.html', 'c', 'popup,width=500,height=300'), 3500);
</script>"#;

const POPUP: &str = r#"<!doctype html><title>c</title>
<script src="http://frame.localhost/frame.js"></script>
<script>opener.postMessage('5-popup-page-itself: cross-origin frame.js ' + (window.frameJs ? 'loaded' : 'FAILED'), '*');</script>
<iframe src="http://frame.localhost/frame.html?6-cross-origin-iframe-in-popup-page"></iframe>"#;

const FRAME: &str = r#"<!doctype html>
<script src="frame.js"></script>
<script>
fetch('data.txt').then((r) => r.text(), (e) => 'error ' + e.message).then((f) =>
  (top.opener || top).postMessage(location.search.slice(1) + ': frame.js ' +
    (window.frameJs ? 'loaded' : 'FAILED') + ', fetch: ' + f, '*'));
</script>"#;

fn serve(_id: WebViewId, req: Request<Vec<u8>>) -> Response<Cow<'static, [u8]>> {
    let (mime, body) = match req.uri().path() {
        "/" => ("text/html", MAIN),
        "/popup.html" => ("text/html", POPUP),
        "/frame.html" => ("text/html", FRAME),
        "/frame.js" => ("text/javascript", "window.frameJs = true;"),
        "/data.txt" => ("text/plain", "ok"),
        _ => ("text/plain", ""),
    };
    Response::builder()
        .header(CONTENT_TYPE, mime)
        .header("Access-Control-Allow-Origin", "*")
        .body(Cow::Borrowed(body.as_bytes()))
        .unwrap()
}

fn main() -> wry::Result<()> {
    println!("WebView2 {}", wry::webview_version()?);
    let event_loop = EventLoopBuilder::<Ev>::with_user_event().build();
    let proxy = event_loop.create_proxy();
    let timeout = proxy.clone();
    let after = proxy.clone();
    std::thread::spawn(move || {
        std::thread::sleep(Duration::from_secs(10));
        let _ = timeout.send_event(Ev::Exit);
    });

    // Windows for the popups, created up front and shown when the popup is requested.
    let spare = Rc::new(RefCell::new(
        (0..3)
            .map(|_| WindowBuilder::new().with_visible(false).build(&event_loop).unwrap())
            .collect::<Vec<_>>(),
    ));
    let keep = Rc::new(RefCell::new(Vec::new()));
    let reports = Rc::new(Cell::new(0));

    let main_window = WindowBuilder::new().with_title("main").build(&event_loop).unwrap();
    let keep_ = keep.clone();
    let _main = WebViewBuilder::new()
        .with_custom_protocol("app".into(), serve)
        .with_custom_protocol("frame".into(), serve)
        .with_url("http://app.localhost/")
        .with_ipc_handler(move |req: Request<String>| {
            println!("{}", req.body());
            reports.set(reports.get() + 1);
            if reports.get() == 6 {
                let _ = proxy.send_event(Ev::Exit);
            }
        })
        .with_new_window_req_handler(move |_url, features| {
            let Some(window) = spare.borrow_mut().pop() else {
                return NewWindowResponse::Deny;
            };
            let webview = WebViewBuilder::new()
                .with_environment(features.opener.environment.clone())
                .with_custom_protocol("app".into(), serve)
                .with_custom_protocol("frame".into(), serve)
                .build(&window)
                .unwrap();
            window.set_visible(true);
            let core = webview.webview();
            keep_.borrow_mut().push((window, webview));
            if std::env::var_os("REREGISTER").is_some() {
                let _ = after.send_event(Ev::AfterSetNewWindow);
            }
            NewWindowResponse::Create { webview: core }
        })
        .build(&main_window)?;

    event_loop.run(move |event, _, control_flow| {
        *control_flow = ControlFlow::Wait;
        match event {
            Event::UserEvent(Ev::Exit) => *control_flow = ControlFlow::Exit,
            Event::UserEvent(Ev::AfterSetNewWindow) => {
                if let Some((_, webview)) = keep.borrow().last() {
                    unsafe { reregister(&webview.webview()) };
                }
            }
            _ => {}
        }
    });
}
Repros in Edge Browser

No, issue does not reproduce in the corresponding Edge version

Regression

Don't know

Last working version (if regression)

No response

Lingua principale
PowerShell
Stelle
526
Fork
68
Merge medio
15g 5h
PR unite (30g)
1

Preparare l'ambiente

Questo progetto non fornisce container di sviluppo, Dockerfile né guida per i contributori, quindi l'ambiente è a tuo carico: parti dal suo README e consulta la nostra guida al primo contributo per i passaggi generali.

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di MicrosoftEdge/WebView2Feedback

Tutte le issue di MicrosoftEdge/WebView2Feedback

Issue simili

Altre issue su Backend & API Design

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.