Skip to content

Commit

Permalink
feat: Allow injecting JavaScript code into subframes
Browse files Browse the repository at this point in the history
Co-authored-by: Geometrically <[email protected]>
  • Loading branch information
Norbiros and Geometrically committed Sep 21, 2024
1 parent aa42ca4 commit 15957a9
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .changes/allow-injecting-into-subframes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wry": minor
---

Add option to injecting JavaScript code into subframes.
15 changes: 15 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ pub use error::*;
pub use http;
pub use proxy::{ProxyConfig, ProxyEndpoint};
pub use web_context::WebContext;
use webkit2gtk::UserContentInjectedFrames;

/// A rectangular region.
#[derive(Clone, Copy, Debug)]
Expand Down Expand Up @@ -503,6 +504,9 @@ pub struct WebViewAttributes {
/// This is only effective if the webview was created by [`WebView::new_as_child`] or [`WebViewBuilder::new_as_child`]
/// or on Linux, if was created by [`WebViewExtUnix::new_gtk`] or [`WebViewBuilderExtUnix::new_gtk`] with [`gtk::Fixed`].
pub bounds: Option<Rect>,

/// Whether JavaScript code should be injected into only main or all subframes
pub inject_into_subframes: UserContentInjectedFrames,
}

impl Default for WebViewAttributes {
Expand Down Expand Up @@ -541,6 +545,7 @@ impl Default for WebViewAttributes {
position: dpi::LogicalPosition::new(0, 0).into(),
size: dpi::LogicalSize::new(200, 200).into(),
}),
inject_into_subframes: UserContentInjectedFrames::TopFrame,
}
}
}
Expand Down Expand Up @@ -891,6 +896,16 @@ impl<'a> WebViewBuilder<'a> {
self
}

/// Whether JavaScript code should be injected to all subframes
pub fn with_inject_into_subframes(mut self, inject_into_subframes: bool) -> Self {
self.attrs.inject_into_subframes = if inject_into_subframes {
UserContentInjectedFrames::AllFrames
} else {
UserContentInjectedFrames::TopFrame
};
self
}

/// Set a navigation handler to decide if incoming url is allowed to navigate.
///
/// The closure take a `String` parameter as url and returns a `bool` to determine whether the navigation should happen.
Expand Down
9 changes: 4 additions & 5 deletions src/webkitgtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -292,11 +292,11 @@ impl InnerWebView {
};

// Initialize message handler
w.init("Object.defineProperty(window, 'ipc', { value: Object.freeze({ postMessage: function(x) { window.webkit.messageHandlers['ipc'].postMessage(x) } }) })")?;
w.init("Object.defineProperty(window, 'ipc', { value: Object.freeze({ postMessage: function(x) { window.webkit.messageHandlers['ipc'].postMessage(x) } }) })", attributes.inject_into_subframes)?;

// Initialize scripts
for js in attributes.initialization_scripts {
w.init(&js)?;
w.init(&js, attributes.inject_into_subframes)?;
}

// Run pending webview.eval() scripts once webview loads.
Expand Down Expand Up @@ -598,12 +598,11 @@ impl InnerWebView {
Ok(())
}

fn init(&self, js: &str) -> Result<()> {
fn init(&self, js: &str, injected_frames: UserContentInjectedFrames) -> Result<()> {
if let Some(manager) = self.webview.user_content_manager() {
let script = UserScript::new(
js,
// TODO: feature to allow injecting into subframes
UserContentInjectedFrames::TopFrame,
injected_frames,
UserScriptInjectionTime::Start,
&[],
&[],
Expand Down
6 changes: 6 additions & 0 deletions src/webview2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -578,6 +578,8 @@ impl InnerWebView {
if let Some(on_page_load_handler) = attributes.on_page_load_handler.take() {
let on_page_load_handler = Rc::new(on_page_load_handler);
let on_page_load_handler_ = on_page_load_handler.clone();
let scripts = attributes.initialization_scripts.clone();

webview.add_ContentLoading(
&ContentLoadingEventHandler::create(Box::new(move |webview, _| {
let Some(webview) = webview else {
Expand All @@ -586,6 +588,10 @@ impl InnerWebView {

on_page_load_handler_(PageLoadEvent::Started, Self::url_from_webview(&webview)?);

for script in &scripts {
Self::execute_script(&webview, script.clone(), |_| ())?;
}

Ok(())
})),
token,
Expand Down
18 changes: 11 additions & 7 deletions src/wkwebview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,14 +66,14 @@ use crate::{
Error, PageLoadEvent, Rect, RequestAsyncResponder, Result, WebContext, WebViewAttributes, RGBA,
};

use self::util::Counter;
use http::{
header::{CONTENT_LENGTH, CONTENT_TYPE},
status::StatusCode,
version::Version,
Request, Response as HttpResponse,
};

use self::util::Counter;
use webkit2gtk::UserContentInjectedFrames;

const IPC_MESSAGE_HANDLER_NAME: &str = "ipc";
#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -986,9 +986,10 @@ impl InnerWebView {
r#"Object.defineProperty(window, 'ipc', {
value: Object.freeze({postMessage: function(s) {window.webkit.messageHandlers.ipc.postMessage(s);}})
});"#,
attributes.inject_into_subframes
);
for js in attributes.initialization_scripts {
w.init(&js);
w.init(&js, attributes.inject_into_subframes);
}

// Set user agent
Expand Down Expand Up @@ -1109,15 +1110,18 @@ r#"Object.defineProperty(window, 'ipc', {
Ok(())
}

fn init(&self, js: &str) {
fn init(&self, js: &str, injected_frames: UserContentInjectedFrames) {
let for_main_frame_only = match injected_frames {
UserContentInjectedFrames::AllFrames => 0,
_ => 1,
};

// Safety: objc runtime calls are unsafe
// Equivalent Obj-C:
// [manager addUserScript:[[WKUserScript alloc] initWithSource:[NSString stringWithUTF8String:js.c_str()] injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES]]
unsafe {
let userscript: id = msg_send![class!(WKUserScript), alloc];
let script: id =
// TODO: feature to allow injecting into subframes
msg_send![userscript, initWithSource:NSString::new(js) injectionTime:0 forMainFrameOnly:1];
let script: id = msg_send![userscript, initWithSource:NSString::new(js) injectionTime:0 forMainFrameOnly:for_main_frame_only];
let _: () = msg_send![self.manager, addUserScript: script];
}
}
Expand Down

0 comments on commit 15957a9

Please sign in to comment.