Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add find-in-page, closes #585 #593

Open
wants to merge 5 commits into
base: dev
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changes/add-find-in-page.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"wry": minor
---

Add `WebView::find_in_page`.
75 changes: 75 additions & 0 deletions examples/find_in_page.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT

use wry::webview::FindInPageOption;

fn main() -> wry::Result<()> {
use wry::{
application::{
event::{Event, StartCause, WindowEvent},
event_loop::{ControlFlow, EventLoop},
window::WindowBuilder,
},
webview::WebViewBuilder,
};

#[derive(Debug)]
enum UserEvent {
FindInPage(String),
}

let event_loop: EventLoop<UserEvent> = EventLoop::with_user_event();
let proxy = event_loop.create_proxy();
let window = WindowBuilder::new()
.with_title("Hello World")
.build(&event_loop)?;
let webview = WebViewBuilder::new(window)?
.with_html(
r#"
<input placeholder="Find text"/>
<button>find</button>
<p>Tauri is a toolkit that helps developers make applications for the major desktop platforms - using virtually any frontend framework in existence. The core is built with Rust, and the CLI leverages Node.js making Tauri a genuinely polyglot approach to creating and maintaining great apps. If you want to know more about the technical details, then please visit the Introduction. If you want to know more about this project's philosophy - then keep reading.</p>
<script>
document.querySelector("button").addEventListener("click", () => {
const text = document.querySelector("input").value;
window.ipc.postMessage(text);
});
</script>
"#,
)?
.with_ipc_handler(move |_, text: String| {
proxy
.send_event(UserEvent::FindInPage(text.clone()))
.unwrap();
});

#[cfg(debug_assertions)]
let webview = webview.with_devtools(true);

let webview = webview.build()?;

event_loop.run(move |event, _, control_flow| {
*control_flow = ControlFlow::Wait;

match event {
Event::NewEvents(StartCause::Init) => println!("Wry has started!"),
Event::WindowEvent {
event: WindowEvent::CloseRequested,
..
} => *control_flow = ControlFlow::Exit,
Event::UserEvent(UserEvent::FindInPage(text)) => {
webview.find_in_page(
text,
FindInPageOption {
case_sensitive: true,
max_match_count: 100,
..FindInPageOption::default()
},
|found| println!("Is found: {}", found),
);
}
_ => (),
}
});
}
8 changes: 7 additions & 1 deletion src/webview/android/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{collections::HashSet, ffi::c_void, ptr::null_mut, rc::Rc, sync::RwLock};

use crate::{application::window::Window, Result};
use crate::{application::window::Window, webview::FindInPageOption, Result};

use super::{WebContext, WebViewAttributes};

Expand Down Expand Up @@ -120,6 +120,12 @@ impl InnerWebView {
}

pub fn zoom(&self, scale_factor: f64) {}

pub fn find_in_page<F>(&self, _string: String, _option: FindInPageOption, _f: F)
where
F: Fn(bool) + 'static,
{
}
}

pub struct UnsafeIpc(*mut c_void);
Expand Down
31 changes: 31 additions & 0 deletions src/webview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -517,6 +517,20 @@ impl WebView {
self.webview.zoom(scale_factor);
}

/// Searches for the specified string in the WebView content.
///
/// ## Platform-specific:
///
/// - **Windows / Android**: Unsupported.
/// - **macOS**: available on macOS 10.15.4+ only.
/// - **iOS**: available on iOS 13.4+ only.
pub fn find_in_page<F>(&self, string: String, option: FindInPageOption, f: F)
where
F: Fn(bool) + 'static,
{
self.webview.find_in_page(string, option, f);
}

#[cfg(target_os = "android")]
pub fn run(self, env: JNIEnv, jclass: JClass, jobject: JObject) -> jobject {
self.webview.run(env, jclass, jobject).unwrap()
Expand All @@ -528,6 +542,23 @@ impl WebView {
}
}

/// A configuration for [`WebView::find_in_page`].
#[derive(Default)]
pub struct FindInPageOption {
/// Indicate the search direction.
pub backwards: bool,
/// Match the search string in a case-sensitive.
pub case_sensitive: bool,
/// Wrap around to the other side of the page.
pub wraps: bool,
/// Maximum number of search strings to highlight.
///
/// ## Platform-specific:
///
/// **Windows / macOS / Android / iOS**: Unsupported.
pub max_match_count: u32,
}

/// An event enumeration sent to [`FileDropHandler`].
#[non_exhaustive]
#[derive(Debug, Serialize, Clone)]
Expand Down
43 changes: 41 additions & 2 deletions src/webview/webkitgtk/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ use gio::Cancellable;
use glib::signal::Inhibit;
use gtk::prelude::*;
use webkit2gtk::{
traits::*, NavigationPolicyDecision, PolicyDecisionType, UserContentInjectedFrames, UserScript,
traits::*, FindController, FindControllerBuilder, FindControllerExt, FindOptions,
NavigationPolicyDecision, PolicyDecisionType, UserContentInjectedFrames, UserScript,
UserScriptInjectionTime, WebView, WebViewBuilder,
};
use webkit2gtk_sys::{
Expand All @@ -31,7 +32,7 @@ pub use web_context::WebContextImpl;

use crate::{
application::{platform::unix::*, window::Window},
webview::{web_context::WebContext, WebViewAttributes},
webview::{web_context::WebContext, FindInPageOption, WebViewAttributes},
Error, Result,
};

Expand All @@ -42,6 +43,7 @@ pub struct InnerWebView {
pub(crate) webview: Rc<WebView>,
#[cfg(any(debug_assertions, feature = "devtools"))]
is_inspector_open: Arc<AtomicBool>,
find_controller: Rc<FindController>,
}

impl InnerWebView {
Expand Down Expand Up @@ -294,10 +296,17 @@ impl InnerWebView {
is_inspector_open
};

let find_controller = {
let builder = FindControllerBuilder::new();
let controller = builder.web_view(&*webview).build();
Rc::new(controller)
};

let w = Self {
webview,
#[cfg(any(debug_assertions, feature = "devtools"))]
is_inspector_open,
find_controller,
};

// Initialize message handler
Expand Down Expand Up @@ -390,6 +399,36 @@ impl InnerWebView {
pub fn zoom(&self, scale_factor: f64) {
WebViewExt::set_zoom_level(&*self.webview, scale_factor);
}

pub fn find_in_page<F>(&self, string: String, option: FindInPageOption, f: F)
where
F: Fn(bool) + 'static,
{
let mut flags = FindOptions::NONE;
if option.backwards {
flags |= FindOptions::BACKWARDS;
}
if !option.case_sensitive {
flags |= FindOptions::CASE_INSENSITIVE;
}
if option.wraps {
flags |= FindOptions::WRAP_AROUND;
}

self
.find_controller
.search(&string, flags.bits(), option.max_match_count);

let handler = Rc::new(Box::new(f));
let found = handler.clone();

self
.find_controller
.connect_failed_to_find_text(move |_| (*handler)(false));
self
.find_controller
.connect_found_text(move |_, _| (*found)(true));
}
}

pub fn platform_webview_version() -> Result<String> {
Expand Down
8 changes: 7 additions & 1 deletion src/webview/webview2/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
mod file_drop;

use crate::{
webview::{WebContext, WebViewAttributes},
webview::{FindInPageOption, WebContext, WebViewAttributes},
Error, Result,
};

Expand Down Expand Up @@ -621,6 +621,12 @@ window.addEventListener('mousemove', (e) => window.chrome.webview.postMessage('_
pub fn zoom(&self, scale_factor: f64) {
let _ = unsafe { self.controller.SetZoomFactor(scale_factor) };
}

pub fn find_in_page<F>(&self, _string: String, _option: FindInPageOption, _f: F)
where
F: Fn(bool) + 'static,
{
}
}

pub fn platform_webview_version() -> Result<String> {
Expand Down
19 changes: 18 additions & 1 deletion src/webview/wkwebview/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
mod file_drop;
mod web_context;

use block::ConcreteBlock;
pub use web_context::WebContextImpl;

#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -43,7 +44,7 @@ use crate::{
dpi::{LogicalSize, PhysicalSize},
window::Window,
},
webview::{FileDropEvent, WebContext, WebViewAttributes},
webview::{FileDropEvent, FindInPageOption, WebContext, WebViewAttributes},
Result,
};

Expand Down Expand Up @@ -608,6 +609,22 @@ r#"Object.defineProperty(window, 'ipc', {
let _: () = msg_send![self.webview, setPageZoom: scale_factor];
}
}

pub fn find_in_page<F>(&self, string: String, option: FindInPageOption, f: F)
where
F: Fn(bool) + 'static,
{
unsafe {
let config: id = msg_send![class!(WKFindConfiguration), new];
let _: () = msg_send![config, setBackwards: option.backwards];
let _: () = msg_send![config, setCaseSensitive: option.case_sensitive];
let _: () = msg_send![config, setWraps: option.wraps];
let _: () = msg_send![self.webview, findString: NSString::new(&string) withConfiguration: config completionHandler:ConcreteBlock::new(|result: id| {
let match_found: BOOL = msg_send![result, matchFound];
f(match_found == YES);
})];
}
}
}

pub fn platform_webview_version() -> Result<String> {
Expand Down