diff --git a/resources/components/panel.html b/resources/components/panel.html
index 14377ebe..9cb1596c 100644
--- a/resources/components/panel.html
+++ b/resources/components/panel.html
@@ -4,10 +4,8 @@
-
-
+
+
diff --git a/src/compositor.rs b/src/compositor.rs
index e2498f85..7e029a19 100644
--- a/src/compositor.rs
+++ b/src/compositor.rs
@@ -1228,14 +1228,16 @@ impl IOCompositor {
}
let rect = DeviceIntRect::from_size(size);
- let content_size = window.get_content_size(rect);
- if let Some(w) = &mut window.webview {
- w.set_size(content_size);
- self.on_resize_webview_event(w.webview_id, w.rect);
- }
- if let Some(prompt) = &mut window.prompt {
- prompt.resize(content_size);
- self.on_resize_webview_event(prompt.webview().webview_id, rect);
+ let show_tab_bar = window.tab_manager.count() > 1;
+ let content_size = window.get_content_size(rect, show_tab_bar);
+ if let Some(tab_id) = window.tab_manager.current_tab_id() {
+ let (tab_id, prompt_id) = window.tab_manager.set_size(tab_id, content_size);
+ if let Some(tab_id) = tab_id {
+ self.on_resize_webview_event(tab_id, content_size);
+ }
+ if let Some(prompt_id) = prompt_id {
+ self.on_resize_webview_event(prompt_id, content_size);
+ }
}
self.send_root_pipeline_display_list(window);
diff --git a/src/lib.rs b/src/lib.rs
index 73c34cb0..aab96b83 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -29,3 +29,5 @@ pub use errors::{Error, Result};
pub use verso::Verso;
/// Re-exporting Winit for the sake of convenience.
pub use winit;
+/// Window tabs manager
+pub mod tab;
diff --git a/src/tab.rs b/src/tab.rs
new file mode 100644
index 00000000..0e4cf1c8
--- /dev/null
+++ b/src/tab.rs
@@ -0,0 +1,295 @@
+use std::collections::HashMap;
+
+use crate::webview::{prompt::PromptDialog, WebView};
+use base::id::WebViewId;
+use serde::{Deserialize, Serialize};
+use servo_url::ServoUrl;
+use webrender_api::units::DeviceIntRect;
+
+/// Tab state
+pub struct Tab {
+ /// Tab WebView id
+ id: WebViewId,
+ /// Tab WebView
+ webview: WebView,
+ /// History
+ history: TabHistory,
+ /// Prompt
+ prompt: Option,
+}
+
+impl Tab {
+ /// Create a new tab state.
+ pub fn new(webview: WebView) -> Self {
+ Self {
+ id: webview.webview_id,
+ webview,
+ history: TabHistory {
+ list: Vec::new(),
+ current_idx: 0,
+ },
+ prompt: None,
+ }
+ }
+
+ /// Get tab WebView id.
+ pub fn id(&self) -> WebViewId {
+ self.id
+ }
+
+ /// Get tab WebView.
+ pub fn webview(&self) -> &WebView {
+ &self.webview
+ }
+
+ /// Set tab WebView size.
+ pub fn set_webview_size(&mut self, rect: DeviceIntRect) {
+ self.webview.set_size(rect);
+ }
+
+ /// Get tab history.
+ pub fn history(&self) -> &TabHistory {
+ &self.history
+ }
+
+ /// Set tab history.
+ pub fn set_history(&mut self, list: Vec, current_idx: usize) {
+ self.history = TabHistory { list, current_idx };
+ }
+
+ /// Get tab prompt dialog.
+ pub fn prompt(&self) -> Option<&PromptDialog> {
+ self.prompt.as_ref()
+ }
+
+ /// Get tab prompt id.
+ pub fn prompt_id(&self) -> Option {
+ self.prompt.as_ref().map(|p| p.id())
+ }
+
+ /// Set tab prompt dialog.
+ pub fn set_prompt(&mut self, prompt: PromptDialog) {
+ self.prompt = Some(prompt);
+ }
+
+ /// Remove tab prompt dialog.
+ pub fn remove_prompt(&mut self) -> Option {
+ self.prompt.take()
+ }
+
+ /// Check if there is a prompt dialog.
+ pub fn has_prompt(&self) -> bool {
+ self.prompt.is_some()
+ }
+
+ /// Set prompt webview size.
+ pub fn set_prompt_size(&mut self, rect: DeviceIntRect) {
+ if let Some(prompt) = self.prompt.as_mut() {
+ prompt.set_size(rect);
+ }
+ }
+}
+
+/// Tab manager to handle multiple tab in a window.
+pub struct TabManager {
+ /// Current active tab id
+ active_tab_id: Option,
+ /// Tab webview id -> Tab webview
+ tab_map: HashMap,
+ /// Prompt webview id -> Parent tab webview id
+ prompt_tab_map: HashMap,
+}
+
+impl TabManager {
+ /// Create a new tab manager.
+ pub fn new() -> Self {
+ Self {
+ active_tab_id: None,
+ tab_map: HashMap::new(),
+ prompt_tab_map: HashMap::new(),
+ }
+ }
+ /// Get tab count.
+ pub fn count(&self) -> usize {
+ self.tab_map.len()
+ }
+ /// Get current actvie tab id.
+ pub fn current_tab_id(&self) -> Option {
+ self.active_tab_id
+ }
+ /// Get current active tab.
+ pub fn current_tab(&self) -> Option<&Tab> {
+ if let Some(tab_id) = self.active_tab_id {
+ self.tab_map.get(&tab_id)
+ } else {
+ None
+ }
+ }
+ /// Get all tab id.
+ pub fn tab_ids(&self) -> Vec {
+ self.tab_map.keys().cloned().collect()
+ }
+ /// Activate the tab by tab id.
+ pub fn activate_tab(&mut self, tab_id: WebViewId) -> Option<&Tab> {
+ if let Some(tab) = self.tab_map.get(&tab_id) {
+ self.active_tab_id = Some(tab_id);
+ Some(tab)
+ } else {
+ self.active_tab_id = None;
+ None
+ }
+ }
+ /// Get tab by tab id.
+ pub fn tab(&self, id: WebViewId) -> Option<&Tab> {
+ self.tab_map.get(&id)
+ }
+ /// Append a tab.
+ pub fn append_tab(&mut self, webview: WebView, active: bool) {
+ let id = webview.webview_id;
+ let tab = Tab::new(webview);
+ self.tab_map.insert(id, tab);
+ if active {
+ self.active_tab_id = Some(id);
+ }
+ }
+ /// Close a tab.
+ pub fn close_tab(&mut self, id: WebViewId) -> Result {
+ match self.tab_map.remove(&id) {
+ Some(tab) => Ok(tab),
+ None => Err(TabManagerErr::WebViewIdNotFound),
+ }
+ }
+ /// Set tab size. Will also set prompt dialog size if it exists.
+ /// - Returns the tab and prompt WebViewId if they exist.
+ pub fn set_size(
+ &mut self,
+ tab_id: WebViewId,
+ rect: DeviceIntRect,
+ ) -> (Option, Option) {
+ if let Some(tab) = self.tab_map.get_mut(&tab_id) {
+ tab.set_webview_size(rect);
+
+ if let Some(prompt_id) = tab.prompt_id() {
+ tab.set_prompt_size(rect);
+ (Some(tab_id), Some(prompt_id))
+ } else {
+ (Some(tab_id), None)
+ }
+ } else {
+ (None, None)
+ }
+ }
+
+ /* History */
+
+ /// Get tab history.
+ pub fn history(&self, tab_id: WebViewId) -> Option<&TabHistory> {
+ self.tab_map.get(&tab_id).map(|tab| tab.history())
+ }
+ /// Set tab history.
+ pub fn set_history(&mut self, tab_id: WebViewId, list: Vec, current_idx: usize) {
+ if let Some(tab) = self.tab_map.get_mut(&tab_id) {
+ tab.set_history(list, current_idx);
+ };
+ }
+
+ /* Prompt */
+
+ /// Get prompt dialog by tab id.
+ pub fn prompt_by_tab_id(&self, tab_id: WebViewId) -> Option<&PromptDialog> {
+ self.tab_map.get(&tab_id).and_then(|tab| tab.prompt())
+ }
+ /// Get prompt dialog by tab id.
+ pub fn prompt_by_prompt_id(&self, prompt_id: WebViewId) -> Option<&PromptDialog> {
+ if let Some(tab_id) = self.prompt_tab_map.get(&prompt_id) {
+ self.prompt_by_tab_id(*tab_id)
+ } else {
+ None
+ }
+ }
+ /// Get current tabw prompt dialog.
+ pub fn current_prompt(&self) -> Option<&PromptDialog> {
+ if let Some(tab_id) = self.active_tab_id {
+ self.prompt_by_tab_id(tab_id)
+ } else {
+ None
+ }
+ }
+ /// Set tab prompt dialog.
+ pub fn set_prompt(&mut self, tab_id: WebViewId, prompt: PromptDialog) {
+ if let Some(tab) = self.tab_map.get_mut(&tab_id) {
+ self.prompt_tab_map.insert(prompt.id(), tab_id);
+ tab.set_prompt(prompt);
+ }
+ }
+ /// Remove prompt by tab webview ID.
+ pub fn remove_prompt_by_tab_id(&mut self, tab_id: WebViewId) -> Option {
+ if let Some(tab) = self.tab_map.get_mut(&tab_id) {
+ if let Some(prompt) = tab.remove_prompt() {
+ self.prompt_tab_map.remove(&prompt.id());
+ return Some(prompt);
+ }
+ }
+ None
+ }
+ /// Remove prompt by prompt webview ID.
+ pub fn remove_prompt_by_prompt_id(&mut self, prompt_id: WebViewId) -> Option {
+ if let Some(tab_id) = self.prompt_tab_map.remove(&prompt_id) {
+ self.remove_prompt_by_tab_id(tab_id)
+ } else {
+ None
+ }
+ }
+ /// Check if there is a prompt dialog by prompt webview ID.
+ pub fn has_prompt(&self, prompt_id: WebViewId) -> bool {
+ self.prompt_tab_map.contains_key(&prompt_id)
+ }
+}
+
+/// Tab history
+pub struct TabHistory {
+ /// History list
+ pub list: Vec,
+ /// Current index
+ pub current_idx: usize,
+}
+
+/// Tab manager errors.
+pub enum TabManagerErr {
+ /// Index out of bounds.
+ IndexOutOfBounds,
+ /// WebView WebViewId not found.
+ WebViewIdNotFound,
+ /// Remove last WebView.
+ RemoveLastWebView,
+}
+
+/// Response to UI that the tab was created.
+#[derive(Debug, Clone, Serialize)]
+pub struct TabCreateResponse {
+ /// Tab creation success
+ pub success: bool,
+ /// Tab WebView id
+ pub id: WebViewId,
+}
+
+impl TabCreateResponse {
+ /// Create a new TabCreatedResult json string.
+ pub fn to_json(&self) -> String {
+ serde_json::to_string(self).unwrap()
+ }
+}
+
+/// Activate the tab request from UI.
+#[derive(Debug, Clone, Deserialize)]
+pub struct TabActivateRequest {
+ /// Tab WebView id
+ pub id: WebViewId,
+}
+
+/// Activate the tab request from UI.
+#[derive(Debug, Clone, Deserialize)]
+pub struct TabCloseRequest {
+ /// Tab WebView id
+ pub id: WebViewId,
+}
diff --git a/src/verso.rs b/src/verso.rs
index 75e5cb95..50dea602 100644
--- a/src/verso.rs
+++ b/src/verso.rs
@@ -388,7 +388,7 @@ impl Verso {
if with_panel {
window.create_panel(&constellation_sender, initial_url);
} else if let Some(initial_url) = initial_url {
- window.create_webview(&constellation_sender, initial_url.into());
+ window.create_tab(&constellation_sender, initial_url.into());
}
let mut windows = HashMap::new();
@@ -587,9 +587,11 @@ impl Verso {
pub fn handle_incoming_webview_message(&self, message: ControllerMessage) {
match message {
ControllerMessage::NavigateTo(to_url) => {
- if let Some(webview_id) = self.windows.values().next().and_then(|(window, _)| {
- window.webview.as_ref().map(|webview| webview.webview_id)
- }) {
+ if let Some(webview_id) =
+ self.windows.values().next().and_then(|(window, _)| {
+ window.tab_manager.current_tab().map(|tab| tab.id())
+ })
+ {
send_to_constellation(
&self.constellation_sender,
ConstellationMsg::LoadUrl(webview_id, ServoUrl::from_url(to_url)),
diff --git a/src/webview/prompt.rs b/src/webview/prompt.rs
index 80b150a0..60d76b99 100644
--- a/src/webview/prompt.rs
+++ b/src/webview/prompt.rs
@@ -79,6 +79,10 @@ impl PromptDialog {
pub fn webview(&self) -> &WebView {
&self.webview
}
+ /// Get prompt webview ID
+ pub fn id(&self) -> WebViewId {
+ self.webview.webview_id
+ }
/// Get prompt sender. Send user interaction result back to caller.
pub fn sender(&self) -> Option {
@@ -91,9 +95,9 @@ impl PromptDialog {
/// ```rust
/// let rect = window.webview.as_ref().unwrap().rect;
/// let content_size = window.get_content_size(rect);
- /// prompt.resize(content_size);
+ /// prompt.set_size(content_size);
/// ```
- pub fn resize(&mut self, rect: DeviceIntRect) {
+ pub fn set_size(&mut self, rect: DeviceIntRect) {
self.webview.set_size(rect);
}
diff --git a/src/webview/webview.rs b/src/webview/webview.rs
index 37735ad4..2ed0ef8f 100644
--- a/src/webview/webview.rs
+++ b/src/webview/webview.rs
@@ -17,6 +17,7 @@ use webrender_api::units::DeviceIntRect;
use crate::{
compositor::IOCompositor,
+ tab::{TabActivateRequest, TabCloseRequest, TabCreateResponse},
verso::send_to_constellation,
webview::prompt::{PromptDialog, PromptInputResult, PromptSender},
window::Window,
@@ -95,6 +96,31 @@ impl Window {
self.window.request_redraw();
send_to_constellation(sender, ConstellationMsg::FocusWebView(webview_id));
}
+ EmbedderMsg::ChangePageTitle(title) => {
+ if let Some(panel) = self.panel.as_ref() {
+ let title = if let Some(title) = title {
+ format!("'{title}'")
+ } else {
+ "null".to_string()
+ };
+
+ let script = format!(
+ "window.navbar.setTabTitle('{}', {})",
+ serde_json::to_string(&webview_id).unwrap(),
+ title.as_str()
+ );
+
+ let (tx, rx) = ipc::channel::().unwrap();
+ send_to_constellation(
+ sender,
+ ConstellationMsg::WebDriverCommand(WebDriverCommandMsg::ScriptCommand(
+ BrowsingContextId::from(panel.webview.webview_id),
+ WebDriverScriptCommand::ExecuteScript(script, tx),
+ )),
+ );
+ let _ = rx.recv();
+ }
+ }
EmbedderMsg::AllowNavigationRequest(id, _url) => {
// TODO should provide a API for users to check url
send_to_constellation(sender, ConstellationMsg::AllowNavigationResponse(id, true));
@@ -129,8 +155,9 @@ impl Window {
}
}
EmbedderMsg::HistoryChanged(list, index) => {
- self.close_prompt_dialog();
- self.update_history(&list, index);
+ self.close_prompt_dialog(webview_id);
+ self.tab_manager
+ .set_history(webview_id, list.clone(), index);
let url = list.get(index).unwrap();
if let Some(panel) = self.panel.as_ref() {
let (tx, rx) = ipc::channel::().unwrap();
@@ -156,59 +183,64 @@ impl Window {
// TODO: Implement context menu
}
EmbedderMsg::Prompt(prompt_type, _origin) => {
- let mut prompt = PromptDialog::new();
- let rect = self.webview.as_ref().unwrap().rect;
-
- match prompt_type {
- PromptDefinition::Alert(message, prompt_sender) => {
- prompt.alert(sender, rect, message, prompt_sender);
- }
- PromptDefinition::OkCancel(message, prompt_sender) => {
- prompt.ok_cancel(sender, rect, message, prompt_sender);
- }
- PromptDefinition::YesNo(message, prompt_sender) => {
- prompt.yes_no(
- sender,
- rect,
- message,
- PromptSender::ConfirmSender(prompt_sender),
- );
- }
- PromptDefinition::Input(message, default_value, prompt_sender) => {
- prompt.input(sender, rect, message, Some(default_value), prompt_sender);
+ if let Some(tab) = self.tab_manager.tab(webview_id) {
+ let mut prompt = PromptDialog::new();
+ let rect = tab.webview().rect;
+ match prompt_type {
+ PromptDefinition::Alert(message, prompt_sender) => {
+ prompt.alert(sender, rect, message, prompt_sender);
+ }
+ PromptDefinition::OkCancel(message, prompt_sender) => {
+ prompt.ok_cancel(sender, rect, message, prompt_sender);
+ }
+ PromptDefinition::YesNo(message, prompt_sender) => {
+ prompt.yes_no(
+ sender,
+ rect,
+ message,
+ PromptSender::ConfirmSender(prompt_sender),
+ );
+ }
+ PromptDefinition::Input(message, default_value, prompt_sender) => {
+ prompt.input(sender, rect, message, Some(default_value), prompt_sender);
+ }
}
- }
- // save prompt in window to keep prompt_sender alive
- // so that we can send the result back to the prompt after user clicked the button
- self.prompt = Some(prompt);
+ // save prompt in window to keep prompt_sender alive
+ // so that we can send the result back to the prompt after user clicked the button
+ self.tab_manager.set_prompt(webview_id, prompt);
+ } else {
+ log::error!("Failed to get WebView {webview_id:?} in this window.");
+ }
}
EmbedderMsg::PromptPermission(prompt, prompt_sender) => {
- let message = match prompt {
- PermissionPrompt::Request(permission_name) => {
- format!(
- "This website would like to request permission for {:?}.",
- permission_name
- )
- }
- PermissionPrompt::Insecure(permission_name) => {
- format!(
- "This website would like to request permission for {:?}. However current connection is not secure. Do you want to proceed?",
- permission_name
- )
- }
- };
-
- let mut prompt = PromptDialog::new();
- let rect = self.webview.as_ref().unwrap().rect;
- prompt.yes_no(
- sender,
- rect,
- message,
- PromptSender::PermissionSender(prompt_sender),
- );
+ if let Some(tab) = self.tab_manager.tab(webview_id) {
+ let message = match prompt {
+ PermissionPrompt::Request(permission_name) => {
+ format!(
+ "This website would like to request permission for {:?}.",
+ permission_name
+ )
+ }
+ PermissionPrompt::Insecure(permission_name) => {
+ format!(
+ "This website would like to request permission for {:?}. However current connection is not secure. Do you want to proceed?",
+ permission_name
+ )
+ }
+ };
- self.prompt = Some(prompt);
+ let mut prompt = PromptDialog::new();
+ prompt.yes_no(
+ sender,
+ tab.webview().rect,
+ message,
+ PromptSender::PermissionSender(prompt_sender),
+ );
+ self.tab_manager.set_prompt(webview_id, prompt);
+ } else {
+ log::error!("Failed to get WebView {webview_id:?} in this window.");
+ }
}
e => {
log::trace!("Verso WebView isn't supporting this message yet: {e:?}")
@@ -223,7 +255,7 @@ impl Window {
message: EmbedderMsg,
sender: &Sender,
clipboard: Option<&mut Clipboard>,
- _compositor: &mut IOCompositor,
+ compositor: &mut IOCompositor,
) -> bool {
log::trace!("Verso Panel {panel_id:?} is handling Embedder message: {message:?}",);
match message {
@@ -246,7 +278,7 @@ impl Window {
self.window.request_redraw();
send_to_constellation(sender, ConstellationMsg::FocusWebView(panel_id));
- self.create_webview(sender, self.panel.as_ref().unwrap().initial_url.clone());
+ self.create_tab(sender, self.panel.as_ref().unwrap().initial_url.clone());
}
EmbedderMsg::AllowNavigationRequest(id, _url) => {
// The panel shouldn't navigate to other pages.
@@ -258,10 +290,86 @@ impl Window {
EmbedderMsg::Prompt(definition, _origin) => {
match definition {
PromptDefinition::Input(msg, _, prompt_sender) => {
+ /* Tab */
+ if msg.starts_with("CLOSE_TAB:") {
+ let request_str = msg.strip_prefix("CLOSE_TAB:").unwrap();
+ let request: TabCloseRequest = serde_json::from_str(request_str)
+ .expect("Failed to parse TabCloseRequest");
+
+ // close the tab
+ if let Some(_) = self.tab_manager.tab(request.id) {
+ send_to_constellation(
+ sender,
+ ConstellationMsg::CloseWebView(request.id),
+ );
+ }
+
+ let _ = prompt_sender.send(None);
+ return false;
+ } else if msg.starts_with("ACTIVATE_TAB:") {
+ let request_str = msg.strip_prefix("ACTIVATE_TAB:").unwrap();
+ let request: TabActivateRequest = serde_json::from_str(request_str)
+ .expect("Failed to parse TabActivateRequest");
+
+ let tab_id = request.id;
+
+ // FIXME: set dirty flag, and only resize when flag is set
+ self.activate_tab(compositor, tab_id, self.tab_manager.count() > 1);
+
+ let _ = prompt_sender.send(None);
+ return false;
+ } else if msg == "NEW_TAB" {
+ let webview_id = WebViewId::new();
+ let size = self.size();
+ let rect = DeviceIntRect::from_size(size);
+ let content_size = self.get_content_size(rect, true);
+ let mut webview = WebView::new(webview_id, rect);
+ webview.set_size(content_size);
+
+ self.tab_manager.append_tab(webview, true);
+
+ send_to_constellation(
+ sender,
+ ConstellationMsg::NewWebView(
+ ServoUrl::parse("https://example.com").unwrap(),
+ webview_id,
+ ),
+ );
+ let result = TabCreateResponse {
+ success: true,
+ id: webview_id,
+ };
+ let _ = prompt_sender.send(Some(result.to_json()));
+ return false;
+ }
+
let _ = prompt_sender.send(None);
- if let Some(webview) = &self.webview {
- let id = webview.webview_id;
+ /* Window */
+ match msg.as_str() {
+ "NEW_WINDOW" => {
+ let _ = prompt_sender.send(None);
+ return true;
+ }
+ "MINIMIZE" => {
+ self.window.set_minimized(true);
+ return false;
+ }
+ "MAXIMIZE" | "DBCLICK_PANEL" => {
+ let is_maximized = self.window.is_maximized();
+ self.window.set_maximized(!is_maximized);
+ return false;
+ }
+ "DRAG_WINDOW" => {
+ let _ = self.window.drag_window();
+ return false;
+ }
+ _ => {}
+ }
+
+ /* Main WebView */
+ if let Some(tab) = self.tab_manager.current_tab() {
+ let id = tab.id();
if msg.starts_with("NAVIGATE_TO:") {
let unparsed_url = msg.strip_prefix("NAVIGATE_TO:").unwrap();
let url = match Url::parse(unparsed_url) {
@@ -305,19 +413,6 @@ impl Window {
"REFRESH" => {
send_to_constellation(sender, ConstellationMsg::Reload(id));
}
- "NEW_WINDOW" => {
- return true;
- }
- "MINIMIZE" => {
- self.window.set_minimized(true);
- }
- "MAXIMIZE" | "DBCLICK_PANEL" => {
- let is_maximized = self.window.is_maximized();
- self.window.set_maximized(!is_maximized);
- }
- "DRAG_WINDOW" => {
- let _ = self.window.drag_window();
- }
e => log::trace!(
"Verso Panel isn't supporting this prompt message yet: {e}"
),
@@ -405,7 +500,12 @@ impl Window {
match message {
EmbedderMsg::Prompt(prompt, _origin) => match prompt {
PromptDefinition::Alert(msg, ignored_prompt_sender) => {
- let prompt = self.prompt.as_ref().unwrap();
+ let prompt = self.tab_manager.prompt_by_prompt_id(webview_id);
+ if prompt.is_none() {
+ log::error!("Prompt not found for WebView {webview_id:?}");
+ return false;
+ }
+ let prompt = prompt.unwrap();
let prompt_sender = prompt.sender().unwrap();
match prompt_sender {
diff --git a/src/window.rs b/src/window.rs
index 0ae1fdeb..24a5245b 100644
--- a/src/window.rs
+++ b/src/window.rs
@@ -10,11 +10,16 @@ use glutin::{
surface::{Surface, WindowSurface},
};
use glutin_winit::DisplayBuilder;
+use ipc_channel::ipc;
+use keyboard_types::{Code, KeyState, KeyboardEvent, Modifiers};
#[cfg(any(target_os = "macos", target_os = "windows"))]
use muda::{Menu as MudaMenu, MenuEvent, MenuEventReceiver, MenuItem};
#[cfg(any(target_os = "macos", target_os = "windows"))]
use raw_window_handle::HasWindowHandle;
-use script_traits::TraversalDirection;
+use script_traits::{
+ webdriver_msg::{WebDriverJSResult, WebDriverJSValue, WebDriverScriptCommand},
+ TraversalDirection, WebDriverCommandMsg,
+};
use script_traits::{TouchEventType, WheelDelta, WheelMode};
use servo_url::ServoUrl;
use webrender_api::{
@@ -35,16 +40,21 @@ use crate::{
compositor::{IOCompositor, MouseWindowEvent},
keyboard::keyboard_event_from_winit,
rendering::{gl_config_picker, RenderingContext},
+ tab::TabManager,
verso::send_to_constellation,
webview::{
context_menu::{ContextMenu, Menu},
- prompt::{PromptDialog, PromptSender},
+ prompt::PromptSender,
Panel, WebView,
},
};
use arboard::Clipboard;
+const PANEL_HEIGHT: f64 = 50.0;
+const TAB_HEIGHT: f64 = 30.0;
+const PANEL_PADDING: f64 = 4.0;
+
/// A Verso window is a Winit window containing several web views.
pub struct Window {
/// Access to Winit window
@@ -54,15 +64,11 @@ pub struct Window {
/// The main panel of this window.
pub(crate) panel: Option,
/// The WebView of this window.
- pub(crate) webview: Option,
+ // pub(crate) webview: Option,
/// The mouse physical position in the web view.
mouse_position: Cell