Add a backend for controlling the enclosing web browser.
This commit is contained in:
parent
2d2b473fe1
commit
0f9db1744b
|
@ -22,6 +22,7 @@ pub struct ActionContext<'a, 'gc, 'gc_context> {
|
||||||
pub start_clip: DisplayNode<'gc>,
|
pub start_clip: DisplayNode<'gc>,
|
||||||
pub active_clip: DisplayNode<'gc>,
|
pub active_clip: DisplayNode<'gc>,
|
||||||
pub audio: &'a mut dyn crate::backend::audio::AudioBackend,
|
pub audio: &'a mut dyn crate::backend::audio::AudioBackend,
|
||||||
|
pub navigator: &'a mut dyn crate::backend::navigator::NavigatorBackend
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Avm1<'gc> {
|
pub struct Avm1<'gc> {
|
||||||
|
|
|
@ -74,6 +74,7 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::avm1::Error;
|
use crate::avm1::Error;
|
||||||
use crate::backend::audio::NullAudioBackend;
|
use crate::backend::audio::NullAudioBackend;
|
||||||
|
use crate::backend::navigator::NullNavigatorBackend;
|
||||||
use crate::display_object::DisplayObject;
|
use crate::display_object::DisplayObject;
|
||||||
use crate::movie_clip::MovieClip;
|
use crate::movie_clip::MovieClip;
|
||||||
use gc_arena::rootless_arena;
|
use gc_arena::rootless_arena;
|
||||||
|
@ -110,6 +111,7 @@ mod tests {
|
||||||
start_clip: root,
|
start_clip: root,
|
||||||
active_clip: root,
|
active_clip: root,
|
||||||
audio: &mut NullAudioBackend::new(),
|
audio: &mut NullAudioBackend::new(),
|
||||||
|
navigator: &mut NullNavigatorBackend::new()
|
||||||
};
|
};
|
||||||
test(&mut context)
|
test(&mut context)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod audio;
|
pub mod audio;
|
||||||
pub mod render;
|
pub mod render;
|
||||||
|
pub mod navigator;
|
|
@ -0,0 +1,63 @@
|
||||||
|
//! Browser-related platform functions
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
/// Enumerates all possible navigation methods.
|
||||||
|
pub enum NavigationMethod {
|
||||||
|
/// Indicates that navigation should generate a GET request.
|
||||||
|
GET,
|
||||||
|
|
||||||
|
/// Indicates that navigation should generate a POST request.
|
||||||
|
POST
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A backend interacting with a browser environment.
|
||||||
|
pub trait NavigatorBackend {
|
||||||
|
/// Cause a browser navigation to a given URL.
|
||||||
|
///
|
||||||
|
/// The URL given may be any URL scheme a browser can support. This may not
|
||||||
|
/// be meaningful for all environments: for example, `javascript:` URLs may
|
||||||
|
/// not be executable in a desktop context.
|
||||||
|
///
|
||||||
|
/// The `window` parameter, if provided, should be treated identically to
|
||||||
|
/// the `window` parameter on an HTML `<a>nchor` tag.
|
||||||
|
///
|
||||||
|
/// This function may be used to send variables to an eligible target. If
|
||||||
|
/// desired, the `vars_method` will be specified with a suitable
|
||||||
|
/// `NavigationMethod` and a key-value representation of the variables to
|
||||||
|
/// be sent. What the backend needs to do depends on the `NavigationMethod`:
|
||||||
|
///
|
||||||
|
/// * `GET` - Variables are appended onto the query parameters of the given
|
||||||
|
/// URL.
|
||||||
|
/// * `POST` - Variables are sent as form data in a POST request, as if the
|
||||||
|
/// user had filled out and submitted an HTML form.
|
||||||
|
///
|
||||||
|
/// Flash Player implemented sandboxing to prevent certain kinds of XSS
|
||||||
|
/// attacks. The `NavigatorBackend` is not responsible for enforcing this
|
||||||
|
/// sandbox.
|
||||||
|
fn navigate_to_url(&self, url: String, window: Option<String>, vars_method: Option<(NavigationMethod, HashMap<String, String>)>);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A null implementation for platforms that do not live in a web browser.
|
||||||
|
pub struct NullNavigatorBackend {
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NullNavigatorBackend {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
NullNavigatorBackend {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for NullNavigatorBackend {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavigatorBackend for NullNavigatorBackend {
|
||||||
|
fn navigate_to_url(&self, _url: String, _window: Option<String>, _vars_method: Option<(NavigationMethod, HashMap<String, String>)>) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::avm1::Avm1;
|
use crate::avm1::Avm1;
|
||||||
use crate::backend::{audio::AudioBackend, render::Letterbox, render::RenderBackend};
|
use crate::backend::{audio::AudioBackend, render::Letterbox, render::RenderBackend, navigator::NavigatorBackend};
|
||||||
use crate::events::{ButtonEvent, PlayerEvent};
|
use crate::events::{ButtonEvent, PlayerEvent};
|
||||||
use crate::library::Library;
|
use crate::library::Library;
|
||||||
use crate::movie_clip::MovieClip;
|
use crate::movie_clip::MovieClip;
|
||||||
|
@ -20,7 +20,7 @@ struct GcRoot<'gc> {
|
||||||
|
|
||||||
make_arena!(GcArena, GcRoot);
|
make_arena!(GcArena, GcRoot);
|
||||||
|
|
||||||
pub struct Player<Audio: AudioBackend, Renderer: RenderBackend> {
|
pub struct Player<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend> {
|
||||||
swf_data: Arc<Vec<u8>>,
|
swf_data: Arc<Vec<u8>>,
|
||||||
swf_version: u8,
|
swf_version: u8,
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ pub struct Player<Audio: AudioBackend, Renderer: RenderBackend> {
|
||||||
|
|
||||||
audio: Audio,
|
audio: Audio,
|
||||||
renderer: Renderer,
|
renderer: Renderer,
|
||||||
|
navigator: Navigator,
|
||||||
transform_stack: TransformStack,
|
transform_stack: TransformStack,
|
||||||
view_matrix: Matrix,
|
view_matrix: Matrix,
|
||||||
inverse_view_matrix: Matrix,
|
inverse_view_matrix: Matrix,
|
||||||
|
@ -49,10 +50,11 @@ pub struct Player<Audio: AudioBackend, Renderer: RenderBackend> {
|
||||||
is_mouse_down: bool,
|
is_mouse_down: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
impl<Audio: AudioBackend, Renderer: RenderBackend, Navigator: NavigatorBackend> Player<Audio, Renderer, Navigator> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
renderer: Renderer,
|
renderer: Renderer,
|
||||||
audio: Audio,
|
audio: Audio,
|
||||||
|
navigator: Navigator,
|
||||||
swf_data: Vec<u8>,
|
swf_data: Vec<u8>,
|
||||||
) -> Result<Self, Box<dyn std::error::Error>> {
|
) -> Result<Self, Box<dyn std::error::Error>> {
|
||||||
let (header, mut reader) = swf::read::read_swf_header(&swf_data[..]).unwrap();
|
let (header, mut reader) = swf::read::read_swf_header(&swf_data[..]).unwrap();
|
||||||
|
@ -74,6 +76,7 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
||||||
|
|
||||||
renderer,
|
renderer,
|
||||||
audio,
|
audio,
|
||||||
|
navigator,
|
||||||
|
|
||||||
background_color: Color {
|
background_color: Color {
|
||||||
r: 255,
|
r: 255,
|
||||||
|
@ -202,13 +205,14 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (global_time, swf_data, swf_version, background_color, renderer, audio, is_mouse_down) = (
|
let (global_time, swf_data, swf_version, background_color, renderer, audio, navigator, is_mouse_down) = (
|
||||||
self.global_time,
|
self.global_time,
|
||||||
&mut self.swf_data,
|
&mut self.swf_data,
|
||||||
self.swf_version,
|
self.swf_version,
|
||||||
&mut self.background_color,
|
&mut self.background_color,
|
||||||
&mut self.renderer,
|
&mut self.renderer,
|
||||||
&mut self.audio,
|
&mut self.audio,
|
||||||
|
&mut self.navigator,
|
||||||
&mut self.is_mouse_down,
|
&mut self.is_mouse_down,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -222,6 +226,7 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
||||||
avm: gc_root.avm.write(gc_context),
|
avm: gc_root.avm.write(gc_context),
|
||||||
renderer,
|
renderer,
|
||||||
audio,
|
audio,
|
||||||
|
navigator,
|
||||||
actions: vec![],
|
actions: vec![],
|
||||||
gc_context,
|
gc_context,
|
||||||
active_clip: gc_root.root,
|
active_clip: gc_root.root,
|
||||||
|
@ -264,13 +269,14 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (global_time, swf_data, swf_version, background_color, renderer, audio) = (
|
let (global_time, swf_data, swf_version, background_color, renderer, audio, navigator) = (
|
||||||
self.global_time,
|
self.global_time,
|
||||||
&mut self.swf_data,
|
&mut self.swf_data,
|
||||||
self.swf_version,
|
self.swf_version,
|
||||||
&mut self.background_color,
|
&mut self.background_color,
|
||||||
&mut self.renderer,
|
&mut self.renderer,
|
||||||
&mut self.audio,
|
&mut self.audio,
|
||||||
|
&mut self.navigator
|
||||||
);
|
);
|
||||||
|
|
||||||
let mouse_pos = &self.mouse_pos;
|
let mouse_pos = &self.mouse_pos;
|
||||||
|
@ -291,6 +297,7 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
||||||
avm: gc_root.avm.write(gc_context),
|
avm: gc_root.avm.write(gc_context),
|
||||||
renderer,
|
renderer,
|
||||||
audio,
|
audio,
|
||||||
|
navigator,
|
||||||
actions: vec![],
|
actions: vec![],
|
||||||
gc_context,
|
gc_context,
|
||||||
active_clip: gc_root.root,
|
active_clip: gc_root.root,
|
||||||
|
@ -323,13 +330,14 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn preload(&mut self) {
|
fn preload(&mut self) {
|
||||||
let (global_time, swf_data, swf_version, background_color, renderer, audio) = (
|
let (global_time, swf_data, swf_version, background_color, renderer, audio, navigator) = (
|
||||||
self.global_time,
|
self.global_time,
|
||||||
&mut self.swf_data,
|
&mut self.swf_data,
|
||||||
self.swf_version,
|
self.swf_version,
|
||||||
&mut self.background_color,
|
&mut self.background_color,
|
||||||
&mut self.renderer,
|
&mut self.renderer,
|
||||||
&mut self.audio,
|
&mut self.audio,
|
||||||
|
&mut self.navigator,
|
||||||
);
|
);
|
||||||
|
|
||||||
self.gc_arena.mutate(|gc_context, gc_root| {
|
self.gc_arena.mutate(|gc_context, gc_root| {
|
||||||
|
@ -342,6 +350,7 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
||||||
avm: gc_root.avm.write(gc_context),
|
avm: gc_root.avm.write(gc_context),
|
||||||
renderer,
|
renderer,
|
||||||
audio,
|
audio,
|
||||||
|
navigator,
|
||||||
actions: vec![],
|
actions: vec![],
|
||||||
gc_context,
|
gc_context,
|
||||||
active_clip: gc_root.root,
|
active_clip: gc_root.root,
|
||||||
|
@ -367,13 +376,14 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run_frame(&mut self) {
|
pub fn run_frame(&mut self) {
|
||||||
let (global_time, swf_data, swf_version, background_color, renderer, audio) = (
|
let (global_time, swf_data, swf_version, background_color, renderer, audio, navigator) = (
|
||||||
self.global_time,
|
self.global_time,
|
||||||
&mut self.swf_data,
|
&mut self.swf_data,
|
||||||
self.swf_version,
|
self.swf_version,
|
||||||
&mut self.background_color,
|
&mut self.background_color,
|
||||||
&mut self.renderer,
|
&mut self.renderer,
|
||||||
&mut self.audio,
|
&mut self.audio,
|
||||||
|
&mut self.navigator
|
||||||
);
|
);
|
||||||
|
|
||||||
self.gc_arena.mutate(|gc_context, gc_root| {
|
self.gc_arena.mutate(|gc_context, gc_root| {
|
||||||
|
@ -386,6 +396,7 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
||||||
avm: gc_root.avm.write(gc_context),
|
avm: gc_root.avm.write(gc_context),
|
||||||
renderer,
|
renderer,
|
||||||
audio,
|
audio,
|
||||||
|
navigator,
|
||||||
actions: vec![],
|
actions: vec![],
|
||||||
gc_context,
|
gc_context,
|
||||||
active_clip: gc_root.root,
|
active_clip: gc_root.root,
|
||||||
|
@ -468,6 +479,7 @@ impl<Audio: AudioBackend, Renderer: RenderBackend> Player<Audio, Renderer> {
|
||||||
start_clip: root,
|
start_clip: root,
|
||||||
active_clip: root,
|
active_clip: root,
|
||||||
audio: update_context.audio,
|
audio: update_context.audio,
|
||||||
|
navigator: update_context.navigator,
|
||||||
};
|
};
|
||||||
for (active_clip, action) in actions {
|
for (active_clip, action) in actions {
|
||||||
action_context.start_clip = active_clip;
|
action_context.start_clip = active_clip;
|
||||||
|
@ -535,6 +547,7 @@ pub struct UpdateContext<'a, 'gc, 'gc_context> {
|
||||||
pub avm: std::cell::RefMut<'a, Avm1<'gc>>,
|
pub avm: std::cell::RefMut<'a, Avm1<'gc>>,
|
||||||
pub renderer: &'a mut dyn RenderBackend,
|
pub renderer: &'a mut dyn RenderBackend,
|
||||||
pub audio: &'a mut dyn AudioBackend,
|
pub audio: &'a mut dyn AudioBackend,
|
||||||
|
pub navigator: &'a mut dyn NavigatorBackend,
|
||||||
pub actions: Vec<(DisplayNode<'gc>, crate::tag_utils::SwfSlice)>,
|
pub actions: Vec<(DisplayNode<'gc>, crate::tag_utils::SwfSlice)>,
|
||||||
pub active_clip: DisplayNode<'gc>,
|
pub active_clip: DisplayNode<'gc>,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
//! Trace output can be compared with correct output from the official Flash Payer.
|
//! Trace output can be compared with correct output from the official Flash Payer.
|
||||||
|
|
||||||
use log::{Metadata, Record};
|
use log::{Metadata, Record};
|
||||||
use ruffle_core::backend::{audio::NullAudioBackend, render::NullRenderer};
|
use ruffle_core::backend::{audio::NullAudioBackend, render::NullRenderer, navigator::NullNavigatorBackend};
|
||||||
use ruffle_core::Player;
|
use ruffle_core::Player;
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ fn test_swf(swf_path: &str, num_frames: u32, expected_output_path: &str) -> Resu
|
||||||
let expected_output = std::fs::read_to_string(expected_output_path)?.replace("\r\n", "\n");
|
let expected_output = std::fs::read_to_string(expected_output_path)?.replace("\r\n", "\n");
|
||||||
|
|
||||||
let swf_data = std::fs::read(swf_path)?;
|
let swf_data = std::fs::read(swf_path)?;
|
||||||
let mut player = Player::new(NullRenderer, NullAudioBackend::new(), swf_data)?;
|
let mut player = Player::new(NullRenderer, NullAudioBackend::new(), NullNavigatorBackend::new(), swf_data)?;
|
||||||
|
|
||||||
for _ in 0..num_frames {
|
for _ in 0..num_frames {
|
||||||
player.run_frame();
|
player.run_frame();
|
||||||
|
|
|
@ -6,7 +6,7 @@ use glutin::{
|
||||||
dpi::{LogicalSize, PhysicalPosition},
|
dpi::{LogicalSize, PhysicalPosition},
|
||||||
ContextBuilder, ElementState, EventsLoop, MouseButton, WindowBuilder, WindowEvent,
|
ContextBuilder, ElementState, EventsLoop, MouseButton, WindowBuilder, WindowEvent,
|
||||||
};
|
};
|
||||||
use ruffle_core::{backend::render::RenderBackend, Player};
|
use ruffle_core::{backend::render::RenderBackend, Player, backend::navigator::NullNavigatorBackend};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
use structopt::StructOpt;
|
use structopt::StructOpt;
|
||||||
|
@ -44,8 +44,9 @@ fn run_player(input_path: PathBuf) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
.build_windowed(window_builder, &events_loop)?;
|
.build_windowed(window_builder, &events_loop)?;
|
||||||
let audio = audio::RodioAudioBackend::new()?;
|
let audio = audio::RodioAudioBackend::new()?;
|
||||||
let renderer = GliumRenderBackend::new(windowed_context)?;
|
let renderer = GliumRenderBackend::new(windowed_context)?;
|
||||||
|
let navigator = NullNavigatorBackend::new(); //TODO: actually implement this backend type
|
||||||
let display = renderer.display().clone();
|
let display = renderer.display().clone();
|
||||||
let mut player = Player::new(renderer, audio, swf_data)?;
|
let mut player = Player::new(renderer, audio, navigator, swf_data)?;
|
||||||
player.set_is_playing(true); // Desktop player will auto-play.
|
player.set_is_playing(true); // Desktop player will auto-play.
|
||||||
|
|
||||||
let logical_size: LogicalSize = (player.movie_width(), player.movie_height()).into();
|
let logical_size: LogicalSize = (player.movie_width(), player.movie_height()).into();
|
||||||
|
|
|
@ -38,7 +38,8 @@ version = "0.3.25"
|
||||||
features = [
|
features = [
|
||||||
"AudioBuffer", "AudioBufferSourceNode", "AudioProcessingEvent", "AudioContext", "AudioDestinationNode", "AudioNode",
|
"AudioBuffer", "AudioBufferSourceNode", "AudioProcessingEvent", "AudioContext", "AudioDestinationNode", "AudioNode",
|
||||||
"CanvasRenderingContext2d", "CssStyleDeclaration", "Document", "Element", "Event", "EventTarget", "HtmlCanvasElement",
|
"CanvasRenderingContext2d", "CssStyleDeclaration", "Document", "Element", "Event", "EventTarget", "HtmlCanvasElement",
|
||||||
"HtmlElement", "HtmlImageElement", "MouseEvent", "Navigator", "Node", "Performance", "ScriptProcessorNode", "Window"]
|
"HtmlElement", "HtmlImageElement", "MouseEvent", "Navigator", "Node", "Performance", "ScriptProcessorNode", "Window",
|
||||||
|
"Location"]
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
wasm-bindgen-test = "0.2.48"
|
wasm-bindgen-test = "0.2.48"
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
//! Ruffle web frontend.
|
//! Ruffle web frontend.
|
||||||
mod audio;
|
mod audio;
|
||||||
mod render;
|
mod render;
|
||||||
|
mod navigator;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use crate::{audio::WebAudioBackend, render::WebCanvasRenderBackend};
|
use crate::{audio::WebAudioBackend, render::WebCanvasRenderBackend, navigator::WebNavigatorBackend};
|
||||||
use generational_arena::{Arena, Index};
|
use generational_arena::{Arena, Index};
|
||||||
use js_sys::Uint8Array;
|
use js_sys::Uint8Array;
|
||||||
use ruffle_core::{backend::render::RenderBackend, PlayerEvent};
|
use ruffle_core::{backend::render::RenderBackend, PlayerEvent};
|
||||||
|
@ -21,7 +22,7 @@ thread_local! {
|
||||||
type AnimationHandler = Closure<dyn FnMut(f64)>;
|
type AnimationHandler = Closure<dyn FnMut(f64)>;
|
||||||
|
|
||||||
struct RuffleInstance {
|
struct RuffleInstance {
|
||||||
core: ruffle_core::Player<WebAudioBackend, WebCanvasRenderBackend>,
|
core: ruffle_core::Player<WebAudioBackend, WebCanvasRenderBackend, WebNavigatorBackend>,
|
||||||
canvas: HtmlCanvasElement,
|
canvas: HtmlCanvasElement,
|
||||||
canvas_width: i32,
|
canvas_width: i32,
|
||||||
canvas_height: i32,
|
canvas_height: i32,
|
||||||
|
@ -82,8 +83,9 @@ impl Ruffle {
|
||||||
let window = web_sys::window().ok_or_else(|| "Expected window")?;
|
let window = web_sys::window().ok_or_else(|| "Expected window")?;
|
||||||
let renderer = WebCanvasRenderBackend::new(&canvas)?;
|
let renderer = WebCanvasRenderBackend::new(&canvas)?;
|
||||||
let audio = WebAudioBackend::new()?;
|
let audio = WebAudioBackend::new()?;
|
||||||
|
let navigator = WebNavigatorBackend::new();
|
||||||
|
|
||||||
let core = ruffle_core::Player::new(renderer, audio, data)?;
|
let core = ruffle_core::Player::new(renderer, audio, navigator, data)?;
|
||||||
|
|
||||||
// Create instance.
|
// Create instance.
|
||||||
let instance = RuffleInstance {
|
let instance = RuffleInstance {
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
//! Navigator backend for web
|
||||||
|
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use web_sys::window;
|
||||||
|
use ruffle_core::backend::navigator::{NavigatorBackend, NavigationMethod};
|
||||||
|
|
||||||
|
pub struct WebNavigatorBackend {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WebNavigatorBackend {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
WebNavigatorBackend {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NavigatorBackend for WebNavigatorBackend {
|
||||||
|
fn navigate_to_url(&self, url: String, _window_spec: Option<String>, _vars_method: Option<(NavigationMethod, HashMap<String, String>)>) {
|
||||||
|
if let Some(window) = window() {
|
||||||
|
//TODO: Support `window`
|
||||||
|
//TODO: Support `vars_method`
|
||||||
|
window.location().assign(&url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue