tauri_runtime/
webview.rs

1// Copyright 2019-2024 Tauri Programme within The Commons Conservancy
2// SPDX-License-Identifier: Apache-2.0
3// SPDX-License-Identifier: MIT
4
5//! A layer between raw [`Runtime`] webviews and Tauri.
6//!
7use crate::{window::is_label_valid, Rect, Runtime, UserEvent};
8
9use http::Request;
10use tauri_utils::config::{
11  BackgroundThrottlingPolicy, Color, WebviewUrl, WindowConfig, WindowEffectsConfig,
12};
13use url::Url;
14
15use std::{
16  borrow::Cow,
17  collections::HashMap,
18  hash::{Hash, Hasher},
19  path::PathBuf,
20  sync::Arc,
21};
22
23type UriSchemeProtocol = dyn Fn(&str, http::Request<Vec<u8>>, Box<dyn FnOnce(http::Response<Cow<'static, [u8]>>) + Send>)
24  + Send
25  + Sync
26  + 'static;
27
28type WebResourceRequestHandler =
29  dyn Fn(http::Request<Vec<u8>>, &mut http::Response<Cow<'static, [u8]>>) + Send + Sync;
30
31type NavigationHandler = dyn Fn(&Url) -> bool + Send;
32
33type OnPageLoadHandler = dyn Fn(Url, PageLoadEvent) + Send;
34
35type DownloadHandler = dyn Fn(DownloadEvent) -> bool + Send + Sync;
36
37#[cfg(target_os = "ios")]
38type InputAccessoryViewBuilderFn = dyn Fn(&objc2_ui_kit::UIView) -> Option<objc2::rc::Retained<objc2_ui_kit::UIView>>
39  + Send
40  + Sync
41  + 'static;
42
43/// Download event.
44pub enum DownloadEvent<'a> {
45  /// Download requested.
46  Requested {
47    /// The url being downloaded.
48    url: Url,
49    /// Represents where the file will be downloaded to.
50    /// Can be used to set the download location by assigning a new path to it.
51    /// The assigned path _must_ be absolute.
52    destination: &'a mut PathBuf,
53  },
54  /// Download finished.
55  Finished {
56    /// The URL of the original download request.
57    url: Url,
58    /// Potentially representing the filesystem path the file was downloaded to.
59    path: Option<PathBuf>,
60    /// Indicates if the download succeeded or not.
61    success: bool,
62  },
63}
64
65#[cfg(target_os = "android")]
66pub struct CreationContext<'a, 'b> {
67  pub env: &'a mut jni::JNIEnv<'b>,
68  pub activity: &'a jni::objects::JObject<'b>,
69  pub webview: &'a jni::objects::JObject<'b>,
70}
71
72/// Kind of event for the page load handler.
73#[derive(Debug, Clone, Copy, PartialEq, Eq)]
74pub enum PageLoadEvent {
75  /// Page started to load.
76  Started,
77  /// Page finished loading.
78  Finished,
79}
80
81/// A webview that has yet to be built.
82pub struct PendingWebview<T: UserEvent, R: Runtime<T>> {
83  /// The label that the webview will be named.
84  pub label: String,
85
86  /// The [`WebviewAttributes`] that the webview will be created with.
87  pub webview_attributes: WebviewAttributes,
88
89  pub uri_scheme_protocols: HashMap<String, Box<UriSchemeProtocol>>,
90
91  /// How to handle IPC calls on the webview.
92  pub ipc_handler: Option<WebviewIpcHandler<T, R>>,
93
94  /// A handler to decide if incoming url is allowed to navigate.
95  pub navigation_handler: Option<Box<NavigationHandler>>,
96
97  /// The resolved URL to load on the webview.
98  pub url: String,
99
100  #[cfg(target_os = "android")]
101  #[allow(clippy::type_complexity)]
102  pub on_webview_created:
103    Option<Box<dyn Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send>>,
104
105  pub web_resource_request_handler: Option<Box<WebResourceRequestHandler>>,
106
107  pub on_page_load_handler: Option<Box<OnPageLoadHandler>>,
108
109  pub download_handler: Option<Arc<DownloadHandler>>,
110}
111
112impl<T: UserEvent, R: Runtime<T>> PendingWebview<T, R> {
113  /// Create a new [`PendingWebview`] with a label from the given [`WebviewAttributes`].
114  pub fn new(
115    webview_attributes: WebviewAttributes,
116    label: impl Into<String>,
117  ) -> crate::Result<Self> {
118    let label = label.into();
119    if !is_label_valid(&label) {
120      Err(crate::Error::InvalidWindowLabel)
121    } else {
122      Ok(Self {
123        webview_attributes,
124        uri_scheme_protocols: Default::default(),
125        label,
126        ipc_handler: None,
127        navigation_handler: None,
128        url: "tauri://localhost".to_string(),
129        #[cfg(target_os = "android")]
130        on_webview_created: None,
131        web_resource_request_handler: None,
132        on_page_load_handler: None,
133        download_handler: None,
134      })
135    }
136  }
137
138  pub fn register_uri_scheme_protocol<
139    N: Into<String>,
140    H: Fn(&str, http::Request<Vec<u8>>, Box<dyn FnOnce(http::Response<Cow<'static, [u8]>>) + Send>)
141      + Send
142      + Sync
143      + 'static,
144  >(
145    &mut self,
146    uri_scheme: N,
147    protocol: H,
148  ) {
149    let uri_scheme = uri_scheme.into();
150    self
151      .uri_scheme_protocols
152      .insert(uri_scheme, Box::new(protocol));
153  }
154
155  #[cfg(target_os = "android")]
156  pub fn on_webview_created<
157    F: Fn(CreationContext<'_, '_>) -> Result<(), jni::errors::Error> + Send + 'static,
158  >(
159    mut self,
160    f: F,
161  ) -> Self {
162    self.on_webview_created.replace(Box::new(f));
163    self
164  }
165}
166
167/// A webview that is not yet managed by Tauri.
168#[derive(Debug)]
169pub struct DetachedWebview<T: UserEvent, R: Runtime<T>> {
170  /// Name of the window
171  pub label: String,
172
173  /// The [`crate::WebviewDispatch`] associated with the window.
174  pub dispatcher: R::WebviewDispatcher,
175}
176
177impl<T: UserEvent, R: Runtime<T>> Clone for DetachedWebview<T, R> {
178  fn clone(&self) -> Self {
179    Self {
180      label: self.label.clone(),
181      dispatcher: self.dispatcher.clone(),
182    }
183  }
184}
185
186impl<T: UserEvent, R: Runtime<T>> Hash for DetachedWebview<T, R> {
187  /// Only use the [`DetachedWebview`]'s label to represent its hash.
188  fn hash<H: Hasher>(&self, state: &mut H) {
189    self.label.hash(state)
190  }
191}
192
193impl<T: UserEvent, R: Runtime<T>> Eq for DetachedWebview<T, R> {}
194impl<T: UserEvent, R: Runtime<T>> PartialEq for DetachedWebview<T, R> {
195  /// Only use the [`DetachedWebview`]'s label to compare equality.
196  fn eq(&self, other: &Self) -> bool {
197    self.label.eq(&other.label)
198  }
199}
200
201/// The attributes used to create an webview.
202#[derive(Debug)]
203pub struct WebviewAttributes {
204  pub url: WebviewUrl,
205  pub user_agent: Option<String>,
206  /// A list of initialization javascript scripts to run when loading new pages.
207  /// When webview load a new page, this initialization code will be executed.
208  /// It is guaranteed that code is executed before `window.onload`.
209  ///
210  /// ## Platform-specific
211  ///
212  /// - **Windows:** scripts are always added to subframes.
213  /// - **Android:** When [addDocumentStartJavaScript] is not supported,
214  ///   we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
215  ///   For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
216  ///
217  /// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
218  /// [onPageStarted]: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
219  pub initialization_scripts: Vec<InitializationScript>,
220  pub data_directory: Option<PathBuf>,
221  pub drag_drop_handler_enabled: bool,
222  pub clipboard: bool,
223  pub accept_first_mouse: bool,
224  pub additional_browser_args: Option<String>,
225  pub window_effects: Option<WindowEffectsConfig>,
226  pub incognito: bool,
227  pub transparent: bool,
228  pub focus: bool,
229  pub bounds: Option<Rect>,
230  pub auto_resize: bool,
231  pub proxy_url: Option<Url>,
232  pub zoom_hotkeys_enabled: bool,
233  pub browser_extensions_enabled: bool,
234  pub extensions_path: Option<PathBuf>,
235  pub data_store_identifier: Option<[u8; 16]>,
236  pub use_https_scheme: bool,
237  pub devtools: Option<bool>,
238  pub background_color: Option<Color>,
239  pub traffic_light_position: Option<dpi::Position>,
240  pub background_throttling: Option<BackgroundThrottlingPolicy>,
241  pub javascript_disabled: bool,
242  /// on macOS and iOS there is a link preview on long pressing links, this is enabled by default.
243  /// see https://docs.rs/objc2-web-kit/latest/objc2_web_kit/struct.WKWebView.html#method.allowsLinkPreview
244  pub allow_link_preview: bool,
245  /// Allows overriding the the keyboard accessory view on iOS.
246  /// Returning `None` effectively removes the view.
247  ///
248  /// The closure parameter is the webview instance.
249  ///
250  /// The accessory view is the view that appears above the keyboard when a text input element is focused.
251  /// It usually displays a view with "Done", "Next" buttons.
252  ///
253  /// # Stability
254  ///
255  /// This relies on [`objc2_ui_kit`] which does not provide a stable API yet, so it can receive breaking changes in minor releases.
256  #[cfg(target_os = "ios")]
257  pub input_accessory_view_builder: Option<InputAccessoryViewBuilder>,
258}
259
260#[cfg(target_os = "ios")]
261#[non_exhaustive]
262pub struct InputAccessoryViewBuilder(pub Box<InputAccessoryViewBuilderFn>);
263
264#[cfg(target_os = "ios")]
265impl std::fmt::Debug for InputAccessoryViewBuilder {
266  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
267    f.debug_struct("InputAccessoryViewBuilder").finish()
268  }
269}
270
271#[cfg(target_os = "ios")]
272impl InputAccessoryViewBuilder {
273  pub fn new(builder: Box<InputAccessoryViewBuilderFn>) -> Self {
274    Self(builder)
275  }
276}
277
278impl From<&WindowConfig> for WebviewAttributes {
279  fn from(config: &WindowConfig) -> Self {
280    let mut builder = Self::new(config.url.clone())
281      .incognito(config.incognito)
282      .focused(config.focus)
283      .zoom_hotkeys_enabled(config.zoom_hotkeys_enabled)
284      .use_https_scheme(config.use_https_scheme)
285      .browser_extensions_enabled(config.browser_extensions_enabled)
286      .background_throttling(config.background_throttling.clone())
287      .devtools(config.devtools);
288
289    #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
290    {
291      builder = builder.transparent(config.transparent);
292    }
293    #[cfg(target_os = "macos")]
294    {
295      if let Some(position) = &config.traffic_light_position {
296        builder =
297          builder.traffic_light_position(dpi::LogicalPosition::new(position.x, position.y).into());
298      }
299    }
300    builder = builder.accept_first_mouse(config.accept_first_mouse);
301    if !config.drag_drop_enabled {
302      builder = builder.disable_drag_drop_handler();
303    }
304    if let Some(user_agent) = &config.user_agent {
305      builder = builder.user_agent(user_agent);
306    }
307    if let Some(additional_browser_args) = &config.additional_browser_args {
308      builder = builder.additional_browser_args(additional_browser_args);
309    }
310    if let Some(effects) = &config.window_effects {
311      builder = builder.window_effects(effects.clone());
312    }
313    if let Some(url) = &config.proxy_url {
314      builder = builder.proxy_url(url.to_owned());
315    }
316    if let Some(color) = config.background_color {
317      builder = builder.background_color(color);
318    }
319    builder.javascript_disabled = config.javascript_disabled;
320    builder.allow_link_preview = config.allow_link_preview;
321    #[cfg(target_os = "ios")]
322    if config.disable_input_accessory_view {
323      builder
324        .input_accessory_view_builder
325        .replace(InputAccessoryViewBuilder::new(Box::new(|_webview| None)));
326    }
327    builder
328  }
329}
330
331impl WebviewAttributes {
332  /// Initializes the default attributes for a webview.
333  pub fn new(url: WebviewUrl) -> Self {
334    Self {
335      url,
336      user_agent: None,
337      initialization_scripts: Vec::new(),
338      data_directory: None,
339      drag_drop_handler_enabled: true,
340      clipboard: false,
341      accept_first_mouse: false,
342      additional_browser_args: None,
343      window_effects: None,
344      incognito: false,
345      transparent: false,
346      focus: true,
347      bounds: None,
348      auto_resize: false,
349      proxy_url: None,
350      zoom_hotkeys_enabled: false,
351      browser_extensions_enabled: false,
352      data_store_identifier: None,
353      extensions_path: None,
354      use_https_scheme: false,
355      devtools: None,
356      background_color: None,
357      traffic_light_position: None,
358      background_throttling: None,
359      javascript_disabled: false,
360      allow_link_preview: true,
361      #[cfg(target_os = "ios")]
362      input_accessory_view_builder: None,
363    }
364  }
365
366  /// Sets the user agent
367  #[must_use]
368  pub fn user_agent(mut self, user_agent: &str) -> Self {
369    self.user_agent = Some(user_agent.to_string());
370    self
371  }
372
373  /// Adds an init script for the main frame.
374  ///
375  /// When webview load a new page, this initialization code will be executed.
376  /// It is guaranteed that code is executed before `window.onload`.
377  ///
378  /// This is executed only on the main frame.
379  /// If you only want to run it in all frames, use [Self::initialization_script_on_all_frames] instead.
380  ///
381  /// ## Platform-specific
382  ///
383  /// - **Android on Wry:** When [addDocumentStartJavaScript] is not supported,
384  ///   we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
385  ///   For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
386  ///
387  /// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
388  /// [onPageStarted]: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
389  #[must_use]
390  pub fn initialization_script(mut self, script: impl Into<String>) -> Self {
391    self.initialization_scripts.push(InitializationScript {
392      script: script.into(),
393      for_main_frame_only: true,
394    });
395    self
396  }
397
398  /// Adds an init script for all frames.
399  ///
400  /// When webview load a new page, this initialization code will be executed.
401  /// It is guaranteed that code is executed before `window.onload`.
402  ///
403  /// This is executed on all frames, main frame and also sub frames.
404  /// If you only want to run it in the main frame, use [Self::initialization_script] instead.
405  ///
406  /// ## Platform-specific
407  ///
408  /// - **Android on Wry:** When [addDocumentStartJavaScript] is not supported,
409  ///   we prepend initialization scripts to each HTML head (implementation only supported on custom protocol URLs).
410  ///   For remote URLs, we use [onPageStarted] which is not guaranteed to run before other scripts.
411  ///
412  /// [addDocumentStartJavaScript]: https://developer.android.com/reference/androidx/webkit/WebViewCompat#addDocumentStartJavaScript(android.webkit.WebView,java.lang.String,java.util.Set%3Cjava.lang.String%3E)
413  /// [onPageStarted]: https://developer.android.com/reference/android/webkit/WebViewClient#onPageStarted(android.webkit.WebView,%20java.lang.String,%20android.graphics.Bitmap)
414  #[must_use]
415  pub fn initialization_script_on_all_frames(mut self, script: impl Into<String>) -> Self {
416    self.initialization_scripts.push(InitializationScript {
417      script: script.into(),
418      for_main_frame_only: false,
419    });
420    self
421  }
422
423  /// Data directory for the webview.
424  #[must_use]
425  pub fn data_directory(mut self, data_directory: PathBuf) -> Self {
426    self.data_directory.replace(data_directory);
427    self
428  }
429
430  /// Disables the drag and drop handler. This is required to use HTML5 drag and drop APIs on the frontend on Windows.
431  #[must_use]
432  pub fn disable_drag_drop_handler(mut self) -> Self {
433    self.drag_drop_handler_enabled = false;
434    self
435  }
436
437  /// Enables clipboard access for the page rendered on **Linux** and **Windows**.
438  ///
439  /// **macOS** doesn't provide such method and is always enabled by default,
440  /// but you still need to add menu item accelerators to use shortcuts.
441  #[must_use]
442  pub fn enable_clipboard_access(mut self) -> Self {
443    self.clipboard = true;
444    self
445  }
446
447  /// Sets whether clicking an inactive window also clicks through to the webview.
448  #[must_use]
449  pub fn accept_first_mouse(mut self, accept: bool) -> Self {
450    self.accept_first_mouse = accept;
451    self
452  }
453
454  /// Sets additional browser arguments. **Windows Only**
455  #[must_use]
456  pub fn additional_browser_args(mut self, additional_args: &str) -> Self {
457    self.additional_browser_args = Some(additional_args.to_string());
458    self
459  }
460
461  /// Sets window effects
462  #[must_use]
463  pub fn window_effects(mut self, effects: WindowEffectsConfig) -> Self {
464    self.window_effects = Some(effects);
465    self
466  }
467
468  /// Enable or disable incognito mode for the WebView.
469  #[must_use]
470  pub fn incognito(mut self, incognito: bool) -> Self {
471    self.incognito = incognito;
472    self
473  }
474
475  /// Enable or disable transparency for the WebView.
476  #[cfg(any(not(target_os = "macos"), feature = "macos-private-api"))]
477  #[must_use]
478  pub fn transparent(mut self, transparent: bool) -> Self {
479    self.transparent = transparent;
480    self
481  }
482
483  /// Whether the webview should be focused or not.
484  #[must_use]
485  pub fn focused(mut self, focus: bool) -> Self {
486    self.focus = focus;
487    self
488  }
489
490  /// Sets the webview to automatically grow and shrink its size and position when the parent window resizes.
491  #[must_use]
492  pub fn auto_resize(mut self) -> Self {
493    self.auto_resize = true;
494    self
495  }
496
497  /// Enable proxy for the WebView
498  #[must_use]
499  pub fn proxy_url(mut self, url: Url) -> Self {
500    self.proxy_url = Some(url);
501    self
502  }
503
504  /// Whether page zooming by hotkeys is enabled
505  ///
506  /// ## Platform-specific:
507  ///
508  /// - **Windows**: Controls WebView2's [`IsZoomControlEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2settings?view=webview2-winrt-1.0.2420.47#iszoomcontrolenabled) setting.
509  /// - **MacOS / Linux**: Injects a polyfill that zooms in and out with `ctrl/command` + `-/=`,
510  ///   20% in each step, ranging from 20% to 1000%. Requires `webview:allow-set-webview-zoom` permission
511  ///
512  /// - **Android / iOS**: Unsupported.
513  #[must_use]
514  pub fn zoom_hotkeys_enabled(mut self, enabled: bool) -> Self {
515    self.zoom_hotkeys_enabled = enabled;
516    self
517  }
518
519  /// Whether browser extensions can be installed for the webview process
520  ///
521  /// ## Platform-specific:
522  ///
523  /// - **Windows**: Enables the WebView2 environment's [`AreBrowserExtensionsEnabled`](https://learn.microsoft.com/en-us/microsoft-edge/webview2/reference/winrt/microsoft_web_webview2_core/corewebview2environmentoptions?view=webview2-winrt-1.0.2739.15#arebrowserextensionsenabled)
524  /// - **MacOS / Linux / iOS / Android** - Unsupported.
525  #[must_use]
526  pub fn browser_extensions_enabled(mut self, enabled: bool) -> Self {
527    self.browser_extensions_enabled = enabled;
528    self
529  }
530
531  /// Sets whether the custom protocols should use `https://<scheme>.localhost` instead of the default `http://<scheme>.localhost` on Windows and Android. Defaults to `false`.
532  ///
533  /// ## Note
534  ///
535  /// Using a `https` scheme will NOT allow mixed content when trying to fetch `http` endpoints and therefore will not match the behavior of the `<scheme>://localhost` protocols used on macOS and Linux.
536  ///
537  /// ## Warning
538  ///
539  /// Changing this value between releases will change the IndexedDB, cookies and localstorage location and your app will not be able to access the old data.
540  #[must_use]
541  pub fn use_https_scheme(mut self, enabled: bool) -> Self {
542    self.use_https_scheme = enabled;
543    self
544  }
545
546  /// Whether web inspector, which is usually called browser devtools, is enabled or not. Enabled by default.
547  ///
548  /// This API works in **debug** builds, but requires `devtools` feature flag to enable it in **release** builds.
549  ///
550  /// ## Platform-specific
551  ///
552  /// - macOS: This will call private functions on **macOS**.
553  /// - Android: Open `chrome://inspect/#devices` in Chrome to get the devtools window. Wry's `WebView` devtools API isn't supported on Android.
554  /// - iOS: Open Safari > Develop > [Your Device Name] > [Your WebView] to get the devtools window.
555  #[must_use]
556  pub fn devtools(mut self, enabled: Option<bool>) -> Self {
557    self.devtools = enabled;
558    self
559  }
560
561  /// Set the window and webview background color.
562  /// ## Platform-specific:
563  ///
564  /// - **Windows**: On Windows 7, alpha channel is ignored for the webview layer.
565  /// - **Windows**: On Windows 8 and newer, if alpha channel is not `0`, it will be ignored.
566  #[must_use]
567  pub fn background_color(mut self, color: Color) -> Self {
568    self.background_color = Some(color);
569    self
570  }
571
572  /// Change the position of the window controls. Available on macOS only.
573  ///
574  /// Requires titleBarStyle: Overlay and decorations: true.
575  ///
576  /// ## Platform-specific
577  ///
578  /// - **Linux / Windows / iOS / Android:** Unsupported.
579  #[must_use]
580  pub fn traffic_light_position(mut self, position: dpi::Position) -> Self {
581    self.traffic_light_position = Some(position);
582    self
583  }
584
585  /// Whether to show a link preview when long pressing on links. Available on macOS and iOS only.
586  ///
587  /// Default is true.
588  ///
589  /// See https://docs.rs/objc2-web-kit/latest/objc2_web_kit/struct.WKWebView.html#method.allowsLinkPreview
590  ///
591  /// ## Platform-specific
592  ///
593  /// - **Linux / Windows / Android:** Unsupported.
594  #[must_use]
595  pub fn allow_link_preview(mut self, allow_link_preview: bool) -> Self {
596    self.allow_link_preview = allow_link_preview;
597    self
598  }
599
600  /// Change the default background throttling behavior.
601  ///
602  /// By default, browsers use a suspend policy that will throttle timers and even unload
603  /// the whole tab (view) to free resources after roughly 5 minutes when a view became
604  /// minimized or hidden. This will pause all tasks until the documents visibility state
605  /// changes back from hidden to visible by bringing the view back to the foreground.
606  ///
607  /// ## Platform-specific
608  ///
609  /// - **Linux / Windows / Android**: Unsupported. Workarounds like a pending WebLock transaction might suffice.
610  /// - **iOS**: Supported since version 17.0+.
611  /// - **macOS**: Supported since version 14.0+.
612  ///
613  /// see https://github.com/tauri-apps/tauri/issues/5250#issuecomment-2569380578
614  #[must_use]
615  pub fn background_throttling(mut self, policy: Option<BackgroundThrottlingPolicy>) -> Self {
616    self.background_throttling = policy;
617    self
618  }
619}
620
621/// IPC handler.
622pub type WebviewIpcHandler<T, R> = Box<dyn Fn(DetachedWebview<T, R>, Request<String>) + Send>;
623
624/// An initialization script
625#[derive(Debug, Clone)]
626pub struct InitializationScript {
627  /// The script to run
628  pub script: String,
629  /// Whether the script should be injected to main frame only
630  pub for_main_frame_only: bool,
631}