[Problem/Bug]: WebResourceRequested is not raised for subresources of a cross-origin iframe in a NewWindow WebView2
Nessuno ha ancora preso questa issue.
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 54/100
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
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:
- Create the controller with the opener's environment.
AddWebResourceRequestedFilterWithRequestSourceKindsforhttp://app.localhost/*andhttp://frame.localhost/*(context ALL, source kinds ALL), thenadd_WebResourceRequested, which serves both hosts.- In
NewWindowRequested: take a deferral, create the popup WebView2 the same way (steps 1 and 2), setargs.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
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di MicrosoftEdge/WebView2Feedback
-
bug
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 72/100
MicrosoftEdge/WebView2Feedback#5749 · 1 commento ·
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 30/100
MicrosoftEdge/WebView2Feedback#5786 ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 36/100
MicrosoftEdge/WebView2Feedback#5785 ·
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
MicrosoftEdge/WebView2Feedback#5784 ·
-
[Problem/Bug]:Apertabug
Difficoltà 2/5 1-3 ore Idoneità per principianti 18/100
MicrosoftEdge/WebView2Feedback#5783 ·
Tutte le issue di MicrosoftEdge/WebView2Feedback
Issue simili
-
ai-driven-fuzzing bug claude Minor
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
linagora/cozy-client#1769 ·
I maintainer di solito rispondono entro 1 giorno
-
[BUG] micronaut-serde-oracle-jdbc-json brings a second OJDBC driver onto the runtime classpathForse già presa @ZARAYACH l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 72/100
micronaut-projects/micronaut-serialization#1494 · 4 commenti ·
I maintainer di solito rispondono entro 2 giorni
-
Admin member of a research group cannot see the HX2 allocation until they've been added to itAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 68/100
-
bug
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 78/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 73/100
I maintainer di solito rispondono entro 2 giorni