diff options
-rw-r--r-- | components/compositing/compositor.rs | 21 | ||||
-rw-r--r-- | components/compositing/lib.rs | 2 | ||||
-rw-r--r-- | components/constellation/constellation.rs | 150 | ||||
-rw-r--r-- | components/constellation/pipeline.rs | 7 | ||||
-rw-r--r-- | components/script/dom/history.rs | 16 | ||||
-rw-r--r-- | components/script/dom/htmliframeelement.rs | 16 | ||||
-rw-r--r-- | components/script/script_thread.rs | 4 | ||||
-rw-r--r-- | components/script/webdriver_handlers.rs | 15 | ||||
-rw-r--r-- | components/script_traits/lib.rs | 49 | ||||
-rw-r--r-- | components/script_traits/script_msg.rs | 6 | ||||
-rw-r--r-- | components/script_traits/webdriver_msg.rs | 4 | ||||
-rw-r--r-- | components/webdriver_server/lib.rs | 221 |
12 files changed, 265 insertions, 246 deletions
diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 52c617fb0bd..1614f28b741 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -754,10 +754,15 @@ impl<Window: WindowMethods> IOCompositor<Window> { let initial_viewport = self.window_rect.size.to_f32() / dppx; - let msg = ConstellationMsg::WindowSize(WindowSizeData { + let data = WindowSizeData { device_pixel_ratio: dppx, initial_viewport: initial_viewport, - }, size_type); + }; + let top_level_browsing_context_id = match self.root_pipeline { + Some(ref pipeline) => pipeline.top_level_browsing_context_id, + None => return warn!("Window resize without root pipeline."), + }; + let msg = ConstellationMsg::WindowSize(top_level_browsing_context_id, data, size_type); if let Err(e) = self.constellation_chan.send(msg) { warn!("Sending window resize to constellation failed ({}).", e); @@ -866,7 +871,11 @@ impl<Window: WindowMethods> IOCompositor<Window> { } WindowEvent::Reload => { - let msg = ConstellationMsg::Reload; + let top_level_browsing_context_id = match self.root_pipeline { + Some(ref pipeline) => pipeline.top_level_browsing_context_id, + None => return warn!("Window reload without root pipeline."), + }; + let msg = ConstellationMsg::Reload(top_level_browsing_context_id); if let Err(e) = self.constellation_chan.send(msg) { warn!("Sending reload to constellation failed ({}).", e); } @@ -1338,7 +1347,11 @@ impl<Window: WindowMethods> IOCompositor<Window> { windowing::WindowNavigateMsg::Forward => TraversalDirection::Forward(1), windowing::WindowNavigateMsg::Back => TraversalDirection::Back(1), }; - let msg = ConstellationMsg::TraverseHistory(None, direction); + let top_level_browsing_context_id = match self.root_pipeline { + Some(ref pipeline) => pipeline.top_level_browsing_context_id, + None => return warn!("Sending navigation to constellation with no root pipeline."), + }; + let msg = ConstellationMsg::TraverseHistory(top_level_browsing_context_id, direction); if let Err(e) = self.constellation_chan.send(msg) { warn!("Sending navigation to constellation failed ({}).", e); } diff --git a/components/compositing/lib.rs b/components/compositing/lib.rs index 9b3fb8d9cda..e4307ff04cc 100644 --- a/components/compositing/lib.rs +++ b/components/compositing/lib.rs @@ -29,6 +29,7 @@ pub use compositor::IOCompositor; use euclid::size::TypedSize2D; use ipc_channel::ipc::IpcSender; use msg::constellation_msg::PipelineId; +use msg::constellation_msg::TopLevelBrowsingContextId; use script_traits::{ConstellationControlMsg, LayoutControlMsg}; use style_traits::CSSPixel; @@ -48,6 +49,7 @@ pub struct SendableFrameTree { #[derive(Clone)] pub struct CompositionPipeline { pub id: PipelineId, + pub top_level_browsing_context_id: TopLevelBrowsingContextId, pub script_chan: IpcSender<ConstellationControlMsg>, pub layout_chan: IpcSender<LayoutControlMsg>, } diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index 7567b3d12cd..51d22e1129c 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -863,6 +863,13 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> debug!("constellation got get root pipeline message"); self.handle_get_pipeline(browsing_context_id, resp_chan); } + FromCompositorMsg::GetFocusTopLevelBrowsingContext(resp_chan) => { + debug!("constellation got get focus browsing context message"); + let focus_browsing_context = self.focus_pipeline_id + .and_then(|pipeline_id| self.pipelines.get(&pipeline_id)) + .map(|pipeline| pipeline.top_level_browsing_context_id); + let _ = resp_chan.send(focus_browsing_context); + } FromCompositorMsg::GetPipelineTitle(pipeline_id) => { debug!("constellation got get-pipeline-title message"); self.handle_get_pipeline_title_msg(pipeline_id); @@ -896,13 +903,14 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> self.handle_init_load(url); } // Handle a forward or back request - FromCompositorMsg::TraverseHistory(pipeline_id, direction) => { + // Handle a forward or back request + FromCompositorMsg::TraverseHistory(top_level_browsing_context_id, direction) => { debug!("constellation got traverse history message from compositor"); - self.handle_traverse_history_msg(pipeline_id, direction); + self.handle_traverse_history_msg(top_level_browsing_context_id, direction); } - FromCompositorMsg::WindowSize(new_size, size_type) => { + FromCompositorMsg::WindowSize(top_level_browsing_context_id, new_size, size_type) => { debug!("constellation got window resize message"); - self.handle_window_size_msg(new_size, size_type); + self.handle_window_size_msg(top_level_browsing_context_id, new_size, size_type); } FromCompositorMsg::TickAnimation(pipeline_id, tick_type) => { self.handle_tick_animation(pipeline_id, tick_type) @@ -911,9 +919,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> debug!("constellation got webdriver command message"); self.handle_webdriver_msg(command); } - FromCompositorMsg::Reload => { + FromCompositorMsg::Reload(top_level_browsing_context_id) => { debug!("constellation got reload message"); - self.handle_reload_msg(); + self.handle_reload_msg(top_level_browsing_context_id); } FromCompositorMsg::LogEntry(top_level_browsing_context_id, thread_name, entry) => { self.handle_log_entry(top_level_browsing_context_id, thread_name, entry); @@ -963,14 +971,14 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> self.handle_load_complete_msg(pipeline_id) } // Handle a forward or back request - FromScriptMsg::TraverseHistory(pipeline_id, direction) => { + FromScriptMsg::TraverseHistory(top_level_browsing_context_id, direction) => { debug!("constellation got traverse history message from script"); - self.handle_traverse_history_msg(pipeline_id, direction); + self.handle_traverse_history_msg(top_level_browsing_context_id, direction); } // Handle a joint session history length request. - FromScriptMsg::JointSessionHistoryLength(pipeline_id, sender) => { + FromScriptMsg::JointSessionHistoryLength(top_level_browsing_context_id, sender) => { debug!("constellation got joint session history length message from script"); - self.handle_joint_session_history_length(pipeline_id, sender); + self.handle_joint_session_history_length(top_level_browsing_context_id, sender); } // Notification that the new document is ready to become active FromScriptMsg::ActivateDocument(pipeline_id) => { @@ -1392,6 +1400,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> let window_size = self.window_size.initial_viewport; let root_pipeline_id = PipelineId::new(); let root_browsing_context_id = self.root_browsing_context_id; + self.focus_pipeline_id = Some(root_pipeline_id); let load_data = LoadData::new(url.clone(), None, None, None); let sandbox = IFrameSandboxState::IFrameUnsandboxed; self.new_pipeline(root_pipeline_id, @@ -1530,6 +1539,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> Pipeline::new(new_pipeline_id, browsing_context_id, + top_level_browsing_context_id, Some((parent_pipeline_id, frame_type)), script_sender, layout_sender, @@ -1747,14 +1757,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } fn handle_traverse_history_msg(&mut self, - pipeline_id: Option<PipelineId>, - direction: TraversalDirection) { - let top_level_browsing_context_id = pipeline_id - .and_then(|pipeline_id| self.pipelines.get(&pipeline_id)) - .and_then(|pipeline| self.browsing_contexts.get(&pipeline.browsing_context_id)) - .map(|browsing_context| browsing_context.top_level_id) - .unwrap_or(self.root_browsing_context_id); - + top_level_browsing_context_id: TopLevelBrowsingContextId, + direction: TraversalDirection) + { let mut size = 0; let mut table = HashMap::new(); @@ -1784,12 +1789,10 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } } - fn handle_joint_session_history_length(&self, pipeline_id: PipelineId, sender: IpcSender<u32>) { - let top_level_browsing_context_id = self.pipelines.get(&pipeline_id) - .and_then(|pipeline| self.browsing_contexts.get(&pipeline.browsing_context_id)) - .map(|browsing_context| browsing_context.top_level_id) - .unwrap_or(self.root_browsing_context_id); - + fn handle_joint_session_history_length(&self, + top_level_browsing_context_id: TopLevelBrowsingContextId, + sender: IpcSender<u32>) + { // Initialize length at 1 to count for the current active entry let mut length = 1; for browsing_context in self.all_browsing_contexts_iter(top_level_browsing_context_id) { @@ -1827,21 +1830,19 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } } - fn handle_reload_msg(&mut self) { - // Send Reload constellation msg to root script channel. - let root_browsing_context_id = BrowsingContextId::from(self.root_browsing_context_id); - let root_pipeline_id = self.browsing_contexts.get(&root_browsing_context_id) - .map(|root_browsing_context| root_browsing_context.pipeline_id); - - if let Some(pipeline_id) = root_pipeline_id { - let msg = ConstellationControlMsg::Reload(pipeline_id); - let result = match self.pipelines.get(&pipeline_id) { - Some(pipeline) => pipeline.event_loop.send(msg), - None => return debug!("Pipeline {:?} got reload event after closure.", pipeline_id), - }; - if let Err(e) = result { - self.handle_send_error(pipeline_id, e); - } + fn handle_reload_msg(&mut self, top_level_browsing_context_id: TopLevelBrowsingContextId) { + let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id); + let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { + Some(browsing_context) => browsing_context.pipeline_id, + None => return warn!("Browsing context {} got reload event after closure.", browsing_context_id), + }; + let msg = ConstellationControlMsg::Reload(pipeline_id); + let result = match self.pipelines.get(&pipeline_id) { + None => return warn!("Pipeline {} got reload event after closure.", pipeline_id), + Some(pipeline) => pipeline.event_loop.send(msg), + }; + if let Err(e) = result { + self.handle_send_error(pipeline_id, e); } } @@ -1894,10 +1895,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } fn handle_get_pipeline(&mut self, - browsing_context_id: Option<BrowsingContextId>, + browsing_context_id: BrowsingContextId, resp_chan: IpcSender<Option<PipelineId>>) { - let root_browsing_context_id = BrowsingContextId::from(self.root_browsing_context_id); - let browsing_context_id = browsing_context_id.unwrap_or(root_browsing_context_id); let current_pipeline_id = self.browsing_contexts.get(&browsing_context_id) .map(|browsing_context| browsing_context.pipeline_id); let pipeline_id_loaded = self.pending_changes.iter().rev() @@ -2033,17 +2032,22 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> self.webdriver.resize_channel = Some(reply); self.compositor_proxy.send(ToCompositorMsg::ResizeTo(size)); }, - WebDriverCommandMsg::LoadUrl(pipeline_id, load_data, reply) => { - self.load_url_for_webdriver(pipeline_id, load_data, reply, false); + WebDriverCommandMsg::LoadUrl(top_level_browsing_context_id, load_data, reply) => { + self.load_url_for_webdriver(top_level_browsing_context_id, load_data, reply, false); }, - WebDriverCommandMsg::Refresh(pipeline_id, reply) => { - let load_data = match self.pipelines.get(&pipeline_id) { - Some(pipeline) => LoadData::new(pipeline.url.clone(), None, None, None), - None => return warn!("Pipeline {:?} Refresh after closure.", pipeline_id), + WebDriverCommandMsg::Refresh(top_level_browsing_context_id, reply) => { + let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id); + let load_data = match self.browsing_contexts.get(&browsing_context_id) { + Some(browsing_context) => browsing_context.load_data.clone(), + None => return warn!("Browsing context {} Refresh after closure.", browsing_context_id), }; - self.load_url_for_webdriver(pipeline_id, load_data, reply, true); + self.load_url_for_webdriver(top_level_browsing_context_id, load_data, reply, true); } - WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd) => { + WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd) => { + let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { + Some(browsing_context) => browsing_context.pipeline_id, + None => return warn!("Browsing context {} ScriptCommand after closure.", browsing_context_id), + }; let control_msg = ConstellationControlMsg::WebDriverScriptCommand(pipeline_id, cmd); let result = match self.pipelines.get(&pipeline_id) { Some(pipeline) => pipeline.event_loop.send(control_msg), @@ -2053,10 +2057,14 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> self.handle_send_error(pipeline_id, e); } }, - WebDriverCommandMsg::SendKeys(pipeline_id, cmd) => { + WebDriverCommandMsg::SendKeys(browsing_context_id, cmd) => { + let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { + Some(browsing_context) => browsing_context.pipeline_id, + None => return warn!("Browsing context {} SendKeys after closure.", browsing_context_id), + }; let event_loop = match self.pipelines.get(&pipeline_id) { Some(pipeline) => pipeline.event_loop.clone(), - None => return warn!("Pipeline {:?} SendKeys after closure.", pipeline_id), + None => return warn!("Pipeline {} SendKeys after closure.", pipeline_id), }; for (key, mods, state) in cmd { let event = CompositorEvent::KeyEvent(None, key, state, mods); @@ -2066,17 +2074,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } } }, - WebDriverCommandMsg::TakeScreenshot(pipeline_id, reply) => { - let root_browsing_context_id = BrowsingContextId::from(self.root_browsing_context_id); - let current_pipeline_id = self.browsing_contexts.get(&root_browsing_context_id) - .map(|root_browsing_context| root_browsing_context.pipeline_id); - if Some(pipeline_id) == current_pipeline_id { - self.compositor_proxy.send(ToCompositorMsg::CreatePng(reply)); - } else { - if let Err(e) = reply.send(None) { - warn!("Screenshot reply failed ({})", e); - } - } + WebDriverCommandMsg::TakeScreenshot(_, reply) => { + self.compositor_proxy.send(ToCompositorMsg::CreatePng(reply)); }, } } @@ -2267,13 +2266,18 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } fn load_url_for_webdriver(&mut self, - pipeline_id: PipelineId, + top_level_browsing_context_id: TopLevelBrowsingContextId, load_data: LoadData, reply: IpcSender<webdriver_msg::LoadStatus>, - replace: bool) { - let new_pipeline_id = self.load_url(pipeline_id, load_data, replace); - if let Some(id) = new_pipeline_id { - self.webdriver.load_channel = Some((id, reply)); + replace: bool) + { + let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id); + let pipeline_id = match self.browsing_contexts.get(&browsing_context_id) { + Some(browsing_context) => browsing_context.pipeline_id, + None => return warn!("Webdriver load for closed browsing context {}.", browsing_context_id), + }; + if let Some(new_pipeline_id) = self.load_url(pipeline_id, load_data, replace) { + self.webdriver.load_channel = Some((new_pipeline_id, reply)); } } @@ -2369,10 +2373,14 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> } /// Called when the window is resized. - fn handle_window_size_msg(&mut self, new_size: WindowSizeData, size_type: WindowSizeType) { + fn handle_window_size_msg(&mut self, + top_level_browsing_context_id: TopLevelBrowsingContextId, + new_size: WindowSizeData, + size_type: WindowSizeType) + { debug!("handle_window_size_msg: {:?}", new_size.initial_viewport.to_untyped()); - let browsing_context_id = BrowsingContextId::from(self.root_browsing_context_id); + let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id); self.resize_browsing_context(new_size, size_type, browsing_context_id); if let Some(resize_channel) = self.webdriver.resize_channel.take() { @@ -2792,9 +2800,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF> let top_level_browsing_context_id = self.browsing_contexts.get(&pipeline.browsing_context_id) .map(|browsing_context| browsing_context.top_level_id) .unwrap_or(self.root_browsing_context_id); + let url = pipeline.url.clone(); let can_go_forward = !self.joint_session_future_is_empty(top_level_browsing_context_id); let can_go_back = !self.joint_session_past_is_empty(top_level_browsing_context_id); - let url = pipeline.url.to_string(); let event = MozBrowserEvent::LocationChange(url, can_go_back, can_go_forward); parent.trigger_mozbrowser_event(Some(top_level_browsing_context_id), event); }, diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 85fdc54289b..1fb385d551f 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -52,6 +52,9 @@ pub struct Pipeline { /// The ID of the browsing context that contains this Pipeline. pub browsing_context_id: BrowsingContextId, + /// The ID of the top-level browsing context that contains this Pipeline. + pub top_level_browsing_context_id: TopLevelBrowsingContextId, + /// The parent pipeline of this one. `None` if this is a root pipeline. /// Note that because of mozbrowser iframes, even top-level pipelines /// may have a parent (in which case the frame type will be @@ -282,6 +285,7 @@ impl Pipeline { Ok(Pipeline::new(state.id, state.browsing_context_id, + state.top_level_browsing_context_id, state.parent_info, script_chan, pipeline_chan, @@ -295,6 +299,7 @@ impl Pipeline { /// spawned. pub fn new(id: PipelineId, browsing_context_id: BrowsingContextId, + top_level_browsing_context_id: TopLevelBrowsingContextId, parent_info: Option<(PipelineId, FrameType)>, event_loop: Rc<EventLoop>, layout_chan: IpcSender<LayoutControlMsg>, @@ -306,6 +311,7 @@ impl Pipeline { let pipeline = Pipeline { id: id, browsing_context_id: browsing_context_id, + top_level_browsing_context_id: top_level_browsing_context_id, parent_info: parent_info, event_loop: event_loop, layout_chan: layout_chan, @@ -372,6 +378,7 @@ impl Pipeline { pub fn to_sendable(&self) -> CompositionPipeline { CompositionPipeline { id: self.id.clone(), + top_level_browsing_context_id: self.top_level_browsing_context_id.clone(), script_chan: self.event_loop.sender(), layout_chan: self.layout_chan.clone(), } diff --git a/components/script/dom/history.rs b/components/script/dom/history.rs index 5488d02d9bf..b9601d2ca1c 100644 --- a/components/script/dom/history.rs +++ b/components/script/dom/history.rs @@ -44,10 +44,9 @@ impl History { if !self.window.Document().is_fully_active() { return Err(Error::Security); } - let global_scope = self.window.upcast::<GlobalScope>(); - let pipeline = global_scope.pipeline_id(); - let msg = ConstellationMsg::TraverseHistory(Some(pipeline), direction); - let _ = global_scope.constellation_chan().send(msg); + let top_level_browsing_context_id = self.window.window_proxy().top_level_browsing_context_id(); + let msg = ConstellationMsg::TraverseHistory(top_level_browsing_context_id, direction); + let _ = self.window.upcast::<GlobalScope>().constellation_chan().send(msg); Ok(()) } } @@ -58,13 +57,12 @@ impl HistoryMethods for History { if !self.window.Document().is_fully_active() { return Err(Error::Security); } - let global_scope = self.window.upcast::<GlobalScope>(); - let pipeline = global_scope.pipeline_id(); + let top_level_browsing_context_id = self.window.window_proxy().top_level_browsing_context_id(); let (sender, recv) = ipc::channel().expect("Failed to create channel to send jsh length."); - let msg = ConstellationMsg::JointSessionHistoryLength(pipeline, sender); - let _ = global_scope.constellation_chan().send(msg); + let msg = ConstellationMsg::JointSessionHistoryLength(top_level_browsing_context_id, sender); + let _ = self.window.upcast::<GlobalScope>().constellation_chan().send(msg); Ok(recv.recv().unwrap()) - } +} // https://html.spec.whatwg.org/multipage/#dom-history-go fn Go(&self, delta: i32) -> ErrorResult { diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 00755213c75..3621bf3f4f9 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -498,7 +498,7 @@ unsafe fn build_mozbrowser_event_detail(event: MozBrowserEvent, } MozBrowserEvent::LocationChange(url, can_go_back, can_go_forward) => { BrowserElementLocationChangeEventDetail { - url: Some(DOMString::from(url)), + url: Some(DOMString::from(url.as_str())), canGoBack: Some(can_go_back), canGoForward: Some(can_go_forward), }.to_jsval(cx, rval); @@ -540,18 +540,16 @@ unsafe fn build_mozbrowser_event_detail(event: MozBrowserEvent, pub fn Navigate(iframe: &HTMLIFrameElement, direction: TraversalDirection) -> ErrorResult { if iframe.Mozbrowser() { - if iframe.upcast::<Node>().is_in_doc_with_browsing_context() { + if let Some(top_level_browsing_context_id) = iframe.top_level_browsing_context_id() { let window = window_from_node(iframe); - let msg = ConstellationMsg::TraverseHistory(iframe.pipeline_id(), direction); + let msg = ConstellationMsg::TraverseHistory(top_level_browsing_context_id, direction); window.upcast::<GlobalScope>().constellation_chan().send(msg).unwrap(); + return Ok(()); } - - Ok(()) - } else { - debug!(concat!("this frame is not mozbrowser: mozbrowser attribute missing, or not a top", - "level window, or mozbrowser preference not set (use --pref dom.mozbrowser.enabled)")); - Err(Error::NotSupported) } + debug!(concat!("this frame is not mozbrowser: mozbrowser attribute missing, or not a top", + "level window, or mozbrowser preference not set (use --pref dom.mozbrowser.enabled)")); + Err(Error::NotSupported) } impl HTMLIFrameElementMethods for HTMLIFrameElement { diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index d66baaeda81..405616fedec 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -1280,8 +1280,8 @@ impl ScriptThread { webdriver_handlers::handle_get_rect(&*documents, pipeline_id, node_id, reply), WebDriverScriptCommand::GetElementText(node_id, reply) => webdriver_handlers::handle_get_text(&*documents, pipeline_id, node_id, reply), - WebDriverScriptCommand::GetPipelineId(browsing_context_id, reply) => - webdriver_handlers::handle_get_pipeline_id(&*documents, pipeline_id, browsing_context_id, reply), + WebDriverScriptCommand::GetBrowsingContextId(webdriver_frame_id, reply) => + webdriver_handlers::handle_get_browsing_context_id(&*documents, pipeline_id, webdriver_frame_id, reply), WebDriverScriptCommand::GetUrl(reply) => webdriver_handlers::handle_get_url(&*documents, pipeline_id, reply), WebDriverScriptCommand::IsEnabled(element_id, reply) => diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 7bc31263c25..1aa2ad05dab 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -29,6 +29,7 @@ use hyper_serde::Serde; use ipc_channel::ipc::{self, IpcSender}; use js::jsapi::{HandleValue, JSContext}; use js::jsval::UndefinedValue; +use msg::constellation_msg::BrowsingContextId; use msg::constellation_msg::PipelineId; use net_traits::CookieSource::{HTTP, NonHTTP}; use net_traits::CoreResourceMsg::{GetCookiesDataForUrl, SetCookieForUrl}; @@ -109,23 +110,23 @@ pub fn handle_execute_async_script(documents: &Documents, window.upcast::<GlobalScope>().evaluate_js_on_global_with_result(&eval, rval.handle_mut()); } -pub fn handle_get_pipeline_id(documents: &Documents, - pipeline: PipelineId, - webdriver_frame_id: WebDriverFrameId, - reply: IpcSender<Result<Option<PipelineId>, ()>>) { +pub fn handle_get_browsing_context_id(documents: &Documents, + pipeline: PipelineId, + webdriver_frame_id: WebDriverFrameId, + reply: IpcSender<Result<BrowsingContextId, ()>>) { let result = match webdriver_frame_id { WebDriverFrameId::Short(_) => { // This isn't supported yet - Ok(None) + Err(()) }, WebDriverFrameId::Element(x) => { find_node_by_unique_id(documents, pipeline, x) - .and_then(|node| node.downcast::<HTMLIFrameElement>().map(|elem| elem.pipeline_id())) + .and_then(|node| node.downcast::<HTMLIFrameElement>().and_then(|elem| elem.browsing_context_id())) .ok_or(()) }, WebDriverFrameId::Parent => { documents.find_window(pipeline) - .map(|window| window.parent_info().map(|(parent_id, _)| parent_id)) + .and_then(|window| window.window_proxy().parent().map(|parent| parent.browsing_context_id())) .ok_or(()) } }; diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 98e66ab8044..c5e62b6ee81 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -602,7 +602,7 @@ pub enum MozBrowserEvent { /// Sent when the browser `<iframe>` starts to load a new page. LoadStart, /// Sent when a browser `<iframe>`'s location changes. - LocationChange(String, bool, bool), + LocationChange(ServoUrl, bool, bool), /// Sent when a new tab is opened within a browser `<iframe>` as a result of the user /// issuing a command to open a link target in a new tab (for example ctrl/cmd + click.) /// Includes the URL. @@ -715,21 +715,20 @@ pub enum WindowSizeType { #[derive(Deserialize, Serialize)] pub enum WebDriverCommandMsg { /// Get the window size. - GetWindowSize(PipelineId, IpcSender<WindowSizeData>), - /// Load a URL in the pipeline with the given ID. - LoadUrl(PipelineId, LoadData, IpcSender<LoadStatus>), - /// Refresh the pipeline with the given ID. - Refresh(PipelineId, IpcSender<LoadStatus>), - /// Pass a webdriver command to the script thread of the pipeline with the - /// given ID for execution. - ScriptCommand(PipelineId, WebDriverScriptCommand), - /// Act as if keys were pressed in the pipeline with the given ID. - SendKeys(PipelineId, Vec<(Key, KeyModifiers, KeyState)>), + GetWindowSize(TopLevelBrowsingContextId, IpcSender<WindowSizeData>), + /// Load a URL in the top-level browsing context with the given ID. + LoadUrl(TopLevelBrowsingContextId, LoadData, IpcSender<LoadStatus>), + /// Refresh the top-level browsing context with the given ID. + Refresh(TopLevelBrowsingContextId, IpcSender<LoadStatus>), + /// Pass a webdriver command to the script thread of the current pipeline + /// of a browsing context. + ScriptCommand(BrowsingContextId, WebDriverScriptCommand), + /// Act as if keys were pressed in the browsing context with the given ID. + SendKeys(BrowsingContextId, Vec<(Key, KeyModifiers, KeyState)>), /// Set the window size. - SetWindowSize(PipelineId, Size2D<u32>, IpcSender<WindowSizeData>), - /// Take a screenshot of the window, if the pipeline with the given ID is - /// the root pipeline. - TakeScreenshot(PipelineId, IpcSender<Option<Image>>), + SetWindowSize(TopLevelBrowsingContextId, Size2D<u32>, IpcSender<WindowSizeData>), + /// Take a screenshot of the window. + TakeScreenshot(TopLevelBrowsingContextId, IpcSender<Option<Image>>), } /// Messages to the constellation. @@ -740,10 +739,12 @@ pub enum ConstellationMsg { /// Request that the constellation send the BrowsingContextId corresponding to the document /// with the provided pipeline id GetBrowsingContext(PipelineId, IpcSender<Option<BrowsingContextId>>), - /// Request that the constellation send the current pipeline id for the provided frame - /// id, or for the root frame if this is None, over a provided channel. - /// Also returns a boolean saying whether the document has finished loading or not. - GetPipeline(Option<BrowsingContextId>, IpcSender<Option<PipelineId>>), + /// Request that the constellation send the current pipeline id for the provided + /// browsing context id, over a provided channel. + GetPipeline(BrowsingContextId, IpcSender<Option<PipelineId>>), + /// Request that the constellation send the current focused top-level browsing context id, + /// over a provided channel. + GetFocusTopLevelBrowsingContext(IpcSender<Option<TopLevelBrowsingContextId>>), /// Requests that the constellation inform the compositor of the title of the pipeline /// immediately. GetPipelineTitle(PipelineId), @@ -755,16 +756,16 @@ pub enum ConstellationMsg { KeyEvent(Option<char>, Key, KeyState, KeyModifiers), /// Request to load a page. LoadUrl(PipelineId, LoadData), - /// Request to traverse the joint session history. - TraverseHistory(Option<PipelineId>, TraversalDirection), + /// Request to traverse the joint session history of the provided browsing context. + TraverseHistory(TopLevelBrowsingContextId, TraversalDirection), /// Inform the constellation of a window being resized. - WindowSize(WindowSizeData, WindowSizeType), + WindowSize(TopLevelBrowsingContextId, WindowSizeData, WindowSizeType), /// Requests that the constellation instruct layout to begin a new tick of the animation. TickAnimation(PipelineId, AnimationTickType), /// Dispatch a webdriver command WebDriverCommand(WebDriverCommandMsg), - /// Reload the current page. - Reload, + /// Reload a top-level browsing context. + Reload(TopLevelBrowsingContextId), /// A log entry, with the top-level browsing context id and thread name LogEntry(Option<TopLevelBrowsingContextId>, Option<String>, LogEntry), /// Set the WebVR thread channel. diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index 1ebe6c17b5f..9bb7053daa0 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -86,7 +86,7 @@ pub enum ScriptMsg { ForwardEvent(PipelineId, CompositorEvent), /// Requests that the constellation retrieve the current contents of the clipboard GetClipboardContents(IpcSender<String>), - /// Get the frame id for a given pipeline. + /// Get the browsing context id for a given pipeline. GetBrowsingContextId(PipelineId, IpcSender<Option<BrowsingContextId>>), /// Get the parent info for a given pipeline. GetParentInfo(PipelineId, IpcSender<Option<(PipelineId, FrameType)>>), @@ -104,9 +104,9 @@ pub enum ScriptMsg { /// The first PipelineId is for the parent, the second is for the originating pipeline. MozBrowserEvent(PipelineId, PipelineId, MozBrowserEvent), /// HTMLIFrameElement Forward or Back traversal. - TraverseHistory(Option<PipelineId>, TraversalDirection), + TraverseHistory(TopLevelBrowsingContextId, TraversalDirection), /// Gets the length of the joint session history from the constellation. - JointSessionHistoryLength(PipelineId, IpcSender<u32>), + JointSessionHistoryLength(TopLevelBrowsingContextId, IpcSender<u32>), /// Favicon detected NewFavicon(ServoUrl), /// Status message to be displayed in the chrome, eg. a link URL on mouseover. diff --git a/components/script_traits/webdriver_msg.rs b/components/script_traits/webdriver_msg.rs index b92e32ff65c..ed7f2ea8f18 100644 --- a/components/script_traits/webdriver_msg.rs +++ b/components/script_traits/webdriver_msg.rs @@ -8,7 +8,7 @@ use cookie_rs::Cookie; use euclid::rect::Rect; use hyper_serde::Serde; use ipc_channel::ipc::IpcSender; -use msg::constellation_msg::PipelineId; +use msg::constellation_msg::BrowsingContextId; use rustc_serialize::json::{Json, ToJson}; use servo_url::ServoUrl; @@ -31,7 +31,7 @@ pub enum WebDriverScriptCommand { GetElementRect(String, IpcSender<Result<Rect<f64>, ()>>), GetElementTagName(String, IpcSender<Result<String, ()>>), GetElementText(String, IpcSender<Result<String, ()>>), - GetPipelineId(WebDriverFrameId, IpcSender<Result<Option<PipelineId>, ()>>), + GetBrowsingContextId(WebDriverFrameId, IpcSender<Result<BrowsingContextId, ()>>), GetUrl(IpcSender<ServoUrl>), IsEnabled(String, IpcSender<Result<bool, ()>>), IsSelected(String, IpcSender<Result<bool, ()>>), diff --git a/components/webdriver_server/lib.rs b/components/webdriver_server/lib.rs index 404f965bbb9..8f085e507ab 100644 --- a/components/webdriver_server/lib.rs +++ b/components/webdriver_server/lib.rs @@ -32,7 +32,7 @@ use hyper::method::Method::{self, Post}; use image::{DynamicImage, ImageFormat, RgbImage}; use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; use keys::keycodes_to_keys; -use msg::constellation_msg::{BrowsingContextId, PipelineId, TraversalDirection}; +use msg::constellation_msg::{BrowsingContextId, TopLevelBrowsingContextId, TraversalDirection}; use net_traits::image::base::PixelFormat; use regex::Captures; use rustc_serialize::json::{Json, ToJson}; @@ -102,7 +102,8 @@ pub fn start_server(port: u16, constellation_chan: Sender<ConstellationMsg>) { /// Represents the current WebDriver session and holds relevant session state. struct WebDriverSession { id: Uuid, - browsing_context_id: Option<BrowsingContextId>, + browsing_context_id: BrowsingContextId, + top_level_browsing_context_id: TopLevelBrowsingContextId, /// Time to wait for injected scripts to run before interrupting them. A [`None`] value /// specifies that the script should run indefinitely. @@ -117,10 +118,14 @@ struct WebDriverSession { } impl WebDriverSession { - pub fn new() -> WebDriverSession { + pub fn new(browsing_context_id: BrowsingContextId, + top_level_browsing_context_id: TopLevelBrowsingContextId) + -> WebDriverSession + { WebDriverSession { id: Uuid::new_v4(), - browsing_context_id: None, + browsing_context_id: browsing_context_id, + top_level_browsing_context_id: top_level_browsing_context_id, script_timeout: Some(30_000), load_timeout: Some(300_000), @@ -264,33 +269,28 @@ impl Handler { } } - fn pipeline_id(&self, frame_id: Option<BrowsingContextId>) -> WebDriverResult<PipelineId> { + fn focus_top_level_browsing_context_id(&self) -> WebDriverResult<TopLevelBrowsingContextId> { + debug!("Getting focused context."); let interval = 20; let iterations = 30_000 / interval; let (sender, receiver) = ipc::channel().unwrap(); for _ in 0..iterations { - let msg = ConstellationMsg::GetPipeline(frame_id, sender.clone()); + let msg = ConstellationMsg::GetFocusTopLevelBrowsingContext(sender.clone()); self.constellation_chan.send(msg).unwrap(); - // Wait until the document is ready before returning the pipeline id. + // Wait until the document is ready before returning the top-level browsing context id. if let Some(x) = receiver.recv().unwrap() { + debug!("Focused context is {}", x); return Ok(x); } thread::sleep(Duration::from_millis(interval)); } + debug!("Timed out getting focused context."); Err(WebDriverError::new(ErrorStatus::Timeout, "Failed to get window handle")) } - fn root_pipeline(&self) -> WebDriverResult<PipelineId> { - self.pipeline_id(None) - } - - fn frame_pipeline(&self) -> WebDriverResult<PipelineId> { - self.pipeline_id(self.session.as_ref().and_then(|session| session.browsing_context_id)) - } - fn session(&self) -> WebDriverResult<&WebDriverSession> { match self.session { Some(ref x) => Ok(x), @@ -299,31 +299,30 @@ impl Handler { } } - fn set_browsing_context_id(&mut self, browsing_context_id: Option<BrowsingContextId>) -> WebDriverResult<()> { + fn session_mut(&mut self) -> WebDriverResult<&mut WebDriverSession> { match self.session { - Some(ref mut x) => { - x.browsing_context_id = browsing_context_id; - Ok(()) - }, + Some(ref mut x) => Ok(x), None => Err(WebDriverError::new(ErrorStatus::SessionNotCreated, "Session not created")) } } fn handle_new_session(&mut self) -> WebDriverResult<WebDriverResponse> { + debug!("new session"); if self.session.is_none() { - let session = WebDriverSession::new(); + let top_level_browsing_context_id = self.focus_top_level_browsing_context_id()?; + let browsing_context_id = BrowsingContextId::from(top_level_browsing_context_id); + let session = WebDriverSession::new(browsing_context_id, top_level_browsing_context_id); let mut capabilities = BTreeMap::new(); capabilities.insert("browserName".to_owned(), "servo".to_json()); capabilities.insert("browserVersion".to_owned(), "0.0.1".to_json()); capabilities.insert("acceptInsecureCerts".to_owned(), false.to_json()); - let rv = Ok(WebDriverResponse::NewSession( - NewSessionResponse::new( - session.id.to_string(), - Json::Object(capabilities)))); + let response = NewSessionResponse::new(session.id.to_string(), Json::Object(capabilities)); + debug!("new session created {}.", session.id); self.session = Some(session); - rv + Ok(WebDriverResponse::NewSession(response)) } else { + debug!("new session failed."); Err(WebDriverError::new(ErrorStatus::UnknownError, "Session already created")) } @@ -334,16 +333,18 @@ impl Handler { Ok(WebDriverResponse::Void) } - #[inline] - fn frame_script_command(&self, cmd_msg: WebDriverScriptCommand) -> WebDriverResult<()> { - Ok(self.constellation_chan.send(ConstellationMsg::WebDriverCommand( - WebDriverCommandMsg::ScriptCommand(try!(self.frame_pipeline()), cmd_msg))).unwrap()) + fn browsing_context_script_command(&self, cmd_msg: WebDriverScriptCommand) -> WebDriverResult<()> { + let browsing_context_id = self.session()?.browsing_context_id; + let msg = ConstellationMsg::WebDriverCommand(WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd_msg)); + self.constellation_chan.send(msg).unwrap(); + Ok(()) } - #[inline] - fn root_script_command(&self, cmd_msg: WebDriverScriptCommand) -> WebDriverResult<()> { - Ok(self.constellation_chan.send(ConstellationMsg::WebDriverCommand( - WebDriverCommandMsg::ScriptCommand(try!(self.root_pipeline()), cmd_msg))).unwrap()) + fn top_level_script_command(&self, cmd_msg: WebDriverScriptCommand) -> WebDriverResult<()> { + let browsing_context_id = BrowsingContextId::from(self.session()?.top_level_browsing_context_id); + let msg = ConstellationMsg::WebDriverCommand(WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd_msg)); + self.constellation_chan.send(msg).unwrap(); + Ok(()) } fn handle_get(&self, parameters: &GetParameters) -> WebDriverResult<WebDriverResponse> { @@ -353,12 +354,12 @@ impl Handler { "Invalid URL")) }; - let pipeline_id = try!(self.root_pipeline()); + let top_level_browsing_context_id = self.session()?.top_level_browsing_context_id; let (sender, receiver) = ipc::channel().unwrap(); - let load_data = LoadData::new(url, Some(pipeline_id), None, None); - let cmd_msg = WebDriverCommandMsg::LoadUrl(pipeline_id, load_data, sender.clone()); + let load_data = LoadData::new(url, None, None, None); + let cmd_msg = WebDriverCommandMsg::LoadUrl(top_level_browsing_context_id, load_data, sender.clone()); self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); self.wait_for_load(sender, receiver) @@ -368,15 +369,10 @@ impl Handler { sender: IpcSender<LoadStatus>, receiver: IpcReceiver<LoadStatus>) -> WebDriverResult<WebDriverResponse> { - let session = try!(self.session - .as_ref() - .ok_or(WebDriverError::new(ErrorStatus::SessionNotCreated, ""))); - - let timeout = session.load_timeout; - let timeout_chan = sender; + let timeout = self.session()?.load_timeout; thread::spawn(move || { thread::sleep(Duration::from_millis(timeout.unwrap())); - let _ = timeout_chan.send(LoadStatus::LoadTimeout); + let _ = sender.send(LoadStatus::LoadTimeout); }); // wait to get a load event @@ -390,7 +386,7 @@ impl Handler { fn handle_current_url(&self) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.root_script_command(WebDriverScriptCommand::GetUrl(sender))); + self.top_level_script_command(WebDriverScriptCommand::GetUrl(sender))?; let url = receiver.recv().unwrap(); @@ -399,8 +395,8 @@ impl Handler { fn handle_window_size(&self) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - let pipeline_id = try!(self.root_pipeline()); - let cmd_msg = WebDriverCommandMsg::GetWindowSize(pipeline_id, sender); + let top_level_browsing_context_id = self.session()?.top_level_browsing_context_id; + let cmd_msg = WebDriverCommandMsg::GetWindowSize(top_level_browsing_context_id, sender); self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); @@ -413,8 +409,8 @@ impl Handler { fn handle_set_window_size(&self, params: &WindowSizeParameters) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); let size = Size2D::new(params.width as u32, params.height as u32); - let pipeline_id = try!(self.root_pipeline()); - let cmd_msg = WebDriverCommandMsg::SetWindowSize(pipeline_id, size, sender.clone()); + let top_level_browsing_context_id = self.session()?.top_level_browsing_context_id; + let cmd_msg = WebDriverCommandMsg::SetWindowSize(top_level_browsing_context_id, size, sender.clone()); self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); @@ -424,7 +420,7 @@ impl Handler { // On timeout, we send a GetWindowSize message to the constellation, // which will give the current window size. thread::sleep(Duration::from_millis(timeout as u64)); - let cmd_msg = WebDriverCommandMsg::GetWindowSize(pipeline_id, sender); + let cmd_msg = WebDriverCommandMsg::GetWindowSize(top_level_browsing_context_id, sender); constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); }); @@ -437,7 +433,7 @@ impl Handler { fn handle_is_enabled(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.root_script_command(WebDriverScriptCommand::IsEnabled(element.id.clone(), sender))); + self.top_level_script_command(WebDriverScriptCommand::IsEnabled(element.id.clone(), sender))?; match receiver.recv().unwrap() { Ok(is_enabled) => Ok(WebDriverResponse::Generic(ValueResponse::new(is_enabled.to_json()))), @@ -448,7 +444,7 @@ impl Handler { fn handle_is_selected(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.root_script_command(WebDriverScriptCommand::IsSelected(element.id.clone(), sender))); + self.top_level_script_command(WebDriverScriptCommand::IsSelected(element.id.clone(), sender))?; match receiver.recv().unwrap() { Ok(is_selected) => Ok(WebDriverResponse::Generic(ValueResponse::new(is_selected.to_json()))), @@ -457,21 +453,27 @@ impl Handler { } fn handle_go_back(&self) -> WebDriverResult<WebDriverResponse> { - self.constellation_chan.send(ConstellationMsg::TraverseHistory(None, TraversalDirection::Back(1))).unwrap(); + let top_level_browsing_context_id = self.session()?.top_level_browsing_context_id; + let direction = TraversalDirection::Back(1); + let msg = ConstellationMsg::TraverseHistory(top_level_browsing_context_id, direction); + self.constellation_chan.send(msg).unwrap(); Ok(WebDriverResponse::Void) } fn handle_go_forward(&self) -> WebDriverResult<WebDriverResponse> { - self.constellation_chan.send(ConstellationMsg::TraverseHistory(None, TraversalDirection::Forward(1))).unwrap(); + let top_level_browsing_context_id = self.session()?.top_level_browsing_context_id; + let direction = TraversalDirection::Forward(1); + let msg = ConstellationMsg::TraverseHistory(top_level_browsing_context_id, direction); + self.constellation_chan.send(msg).unwrap(); Ok(WebDriverResponse::Void) } fn handle_refresh(&self) -> WebDriverResult<WebDriverResponse> { - let pipeline_id = try!(self.root_pipeline()); + let top_level_browsing_context_id = self.session()?.top_level_browsing_context_id; let (sender, receiver) = ipc::channel().unwrap(); - let cmd_msg = WebDriverCommandMsg::Refresh(pipeline_id, sender.clone()); + let cmd_msg = WebDriverCommandMsg::Refresh(top_level_browsing_context_id, sender.clone()); self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); self.wait_for_load(sender, receiver) @@ -480,7 +482,7 @@ impl Handler { fn handle_title(&self) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.root_script_command(WebDriverScriptCommand::GetTitle(sender))); + self.top_level_script_command(WebDriverScriptCommand::GetTitle(sender))?; let value = receiver.recv().unwrap(); Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))) @@ -508,8 +510,8 @@ impl Handler { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.frame_script_command(WebDriverScriptCommand::FindElementCSS(parameters.value.clone(), - sender))); + let cmd = WebDriverScriptCommand::FindElementCSS(parameters.value.clone(), sender); + self.browsing_context_script_command(cmd)?; match receiver.recv().unwrap() { Ok(value) => { @@ -525,8 +527,9 @@ impl Handler { use webdriver::common::FrameId; let frame_id = match parameters.id { FrameId::Null => { - self.set_browsing_context_id(None).unwrap(); - return Ok(WebDriverResponse::Void) + let session = self.session_mut()?; + session.browsing_context_id = BrowsingContextId::from(session.top_level_browsing_context_id); + return Ok(WebDriverResponse::Void); }, FrameId::Short(ref x) => WebDriverFrameId::Short(*x), FrameId::Element(ref x) => WebDriverFrameId::Element(x.id.clone()) @@ -545,28 +548,15 @@ impl Handler { return Err(WebDriverError::new(ErrorStatus::UnsupportedOperation, "Selecting frame by id not supported")); } - let pipeline_id = try!(self.frame_pipeline()); + let (sender, receiver) = ipc::channel().unwrap(); - let cmd = WebDriverScriptCommand::GetPipelineId(frame_id, sender); - { - self.constellation_chan.send(ConstellationMsg::WebDriverCommand( - WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd))).unwrap(); - } + let cmd = WebDriverScriptCommand::GetBrowsingContextId(frame_id, sender); + self.browsing_context_script_command(cmd)?; - let context_id = match receiver.recv().unwrap() { - Ok(Some(pipeline_id)) => { - let (sender, receiver) = ipc::channel().unwrap(); - self.constellation_chan.send(ConstellationMsg::GetBrowsingContext(pipeline_id, sender)).unwrap(); - receiver.recv().unwrap() - }, - Ok(None) => None, - Err(_) => { - return Err(WebDriverError::new(ErrorStatus::NoSuchFrame, - "Frame does not exist")); - } - }; + let browsing_context_id = receiver.recv().unwrap() + .or(Err(WebDriverError::new(ErrorStatus::NoSuchFrame, "Frame does not exist")))?; - self.set_browsing_context_id(context_id).unwrap(); + self.session_mut()?.browsing_context_id = browsing_context_id; Ok(WebDriverResponse::Void) } @@ -578,8 +568,8 @@ impl Handler { } let (sender, receiver) = ipc::channel().unwrap(); - try!(self.frame_script_command(WebDriverScriptCommand::FindElementsCSS(parameters.value.clone(), - sender))); + let cmd = WebDriverScriptCommand::FindElementsCSS(parameters.value.clone(), sender); + self.browsing_context_script_command(cmd)?; match receiver.recv().unwrap() { Ok(value) => { let resp_value: Vec<Json> = value.into_iter().map( @@ -594,7 +584,8 @@ impl Handler { // https://w3c.github.io/webdriver/webdriver-spec.html#get-element-rect fn handle_element_rect(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.frame_script_command(WebDriverScriptCommand::GetElementRect(element.id.clone(), sender))); + let cmd = WebDriverScriptCommand::GetElementRect(element.id.clone(), sender); + self.browsing_context_script_command(cmd)?; match receiver.recv().unwrap() { Ok(rect) => { let response = ElementRectResponse::new(rect.origin.x, rect.origin.y, @@ -608,7 +599,8 @@ impl Handler { fn handle_element_text(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.frame_script_command(WebDriverScriptCommand::GetElementText(element.id.clone(), sender))); + let cmd = WebDriverScriptCommand::GetElementText(element.id.clone(), sender); + self.browsing_context_script_command(cmd)?; match receiver.recv().unwrap() { Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))), Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference, @@ -618,14 +610,16 @@ impl Handler { fn handle_active_element(&self) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.frame_script_command(WebDriverScriptCommand::GetActiveElement(sender))); + let cmd = WebDriverScriptCommand::GetActiveElement(sender); + self.browsing_context_script_command(cmd)?; let value = receiver.recv().unwrap().map(|x| WebElement::new(x).to_json()); Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))) } fn handle_element_tag_name(&self, element: &WebElement) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.frame_script_command(WebDriverScriptCommand::GetElementTagName(element.id.clone(), sender))); + let cmd = WebDriverScriptCommand::GetElementTagName(element.id.clone(), sender); + self.browsing_context_script_command(cmd)?; match receiver.recv().unwrap() { Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))), Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference, @@ -635,8 +629,8 @@ impl Handler { fn handle_element_attribute(&self, element: &WebElement, name: &str) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.frame_script_command(WebDriverScriptCommand::GetElementAttribute(element.id.clone(), name.to_owned(), - sender))); + let cmd = WebDriverScriptCommand::GetElementAttribute(element.id.clone(), name.to_owned(), sender); + self.browsing_context_script_command(cmd)?; match receiver.recv().unwrap() { Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))), Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference, @@ -646,8 +640,8 @@ impl Handler { fn handle_element_css(&self, element: &WebElement, name: &str) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.frame_script_command(WebDriverScriptCommand::GetElementCSS(element.id.clone(), name.to_owned(), - sender))); + let cmd = WebDriverScriptCommand::GetElementCSS(element.id.clone(), name.to_owned(), sender); + self.browsing_context_script_command(cmd)?; match receiver.recv().unwrap() { Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))), Err(_) => Err(WebDriverError::new(ErrorStatus::StaleElementReference, @@ -657,7 +651,8 @@ impl Handler { fn handle_get_cookies(&self) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.frame_script_command(WebDriverScriptCommand::GetCookies(sender))); + let cmd = WebDriverScriptCommand::GetCookies(sender); + self.browsing_context_script_command(cmd)?; let cookies = receiver.recv().unwrap(); let response = cookies.into_iter().map(|cookie| { cookie_msg_to_cookie(cookie.into_inner()) @@ -667,7 +662,8 @@ impl Handler { fn handle_get_cookie(&self, name: &str) -> WebDriverResult<WebDriverResponse> { let (sender, receiver) = ipc::channel().unwrap(); - try!(self.frame_script_command(WebDriverScriptCommand::GetCookie(name.to_owned(), sender))); + let cmd = WebDriverScriptCommand::GetCookie(name.to_owned(), sender); + self.browsing_context_script_command(cmd)?; let cookies = receiver.recv().unwrap(); let response = cookies.into_iter().map(|cookie| { cookie_msg_to_cookie(cookie.into_inner()) @@ -690,7 +686,8 @@ impl Handler { _ => cookie.finish(), }; - try!(self.frame_script_command(WebDriverScriptCommand::AddCookie(cookie, sender))); + let cmd = WebDriverScriptCommand::AddCookie(cookie, sender); + self.browsing_context_script_command(cmd)?; match receiver.recv().unwrap() { Ok(_) => Ok(WebDriverResponse::Void), Err(response) => match response { @@ -728,20 +725,18 @@ impl Handler { let (sender, receiver) = ipc::channel().unwrap(); let command = WebDriverScriptCommand::ExecuteScript(script, sender); - self.execute_script(command, receiver) + self.browsing_context_script_command(command)?; + let result = receiver.recv().unwrap(); + self.postprocess_js_result(result) } fn handle_execute_async_script(&self, parameters: &JavascriptCommandParameters) -> WebDriverResult<WebDriverResponse> { - let session = try!(self.session - .as_ref() - .ok_or(WebDriverError::new(ErrorStatus::SessionNotCreated, ""))); - let func_body = ¶meters.script; let args_string = "window.webdriverCallback"; - let script = match session.script_timeout { + let script = match self.session()?.script_timeout { Some(timeout) => { format!("setTimeout(webdriverTimeout, {}); (function(callback) {{ {} }})({})", timeout, @@ -753,19 +748,13 @@ impl Handler { let (sender, receiver) = ipc::channel().unwrap(); let command = WebDriverScriptCommand::ExecuteAsyncScript(script, sender); - self.execute_script(command, receiver) + self.browsing_context_script_command(command)?; + let result = receiver.recv().unwrap(); + self.postprocess_js_result(result) } - fn execute_script(&self, - command: WebDriverScriptCommand, - receiver: IpcReceiver<WebDriverJSResult>) - -> WebDriverResult<WebDriverResponse> { - let pipeline_id = try!(self.frame_pipeline()); - - let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, command); - self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); - - match receiver.recv().unwrap() { + fn postprocess_js_result(&self, result: WebDriverJSResult) -> WebDriverResult<WebDriverResponse> { + match result { Ok(value) => Ok(WebDriverResponse::Generic(ValueResponse::new(value.to_json()))), Err(WebDriverJSError::Timeout) => Err(WebDriverError::new(ErrorStatus::Timeout, "")), Err(WebDriverJSError::UnknownType) => Err(WebDriverError::new( @@ -778,12 +767,12 @@ impl Handler { fn handle_element_send_keys(&self, element: &WebElement, keys: &SendKeysParameters) -> WebDriverResult<WebDriverResponse> { - let pipeline_id = try!(self.frame_pipeline()); + let browsing_context_id = self.session()?.browsing_context_id; let (sender, receiver) = ipc::channel().unwrap(); let cmd = WebDriverScriptCommand::FocusElement(element.id.clone(), sender); - let cmd_msg = WebDriverCommandMsg::ScriptCommand(pipeline_id, cmd); + let cmd_msg = WebDriverCommandMsg::ScriptCommand(browsing_context_id, cmd); self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); // TODO: distinguish the not found and not focusable cases @@ -793,7 +782,9 @@ impl Handler { let keys = try!(keycodes_to_keys(&keys.value).or_else(|_| Err(WebDriverError::new(ErrorStatus::UnsupportedOperation, "Failed to convert keycodes")))); - let cmd_msg = WebDriverCommandMsg::SendKeys(pipeline_id, keys); + // TODO: there's a race condition caused by these being two separate messages, + // so the constellation may have changed state between them. + let cmd_msg = WebDriverCommandMsg::SendKeys(browsing_context_id, keys); self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); Ok(WebDriverResponse::Void) @@ -801,14 +792,14 @@ impl Handler { fn handle_take_screenshot(&self) -> WebDriverResult<WebDriverResponse> { let mut img = None; - let pipeline_id = try!(self.root_pipeline()); + let top_level_id = self.session()?.top_level_browsing_context_id; let interval = 1000; let iterations = 30_000 / interval; for _ in 0..iterations { let (sender, receiver) = ipc::channel().unwrap(); - let cmd_msg = WebDriverCommandMsg::TakeScreenshot(pipeline_id, sender); + let cmd_msg = WebDriverCommandMsg::TakeScreenshot(top_level_id, sender); self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap(); if let Some(x) = receiver.recv().unwrap() { |