From 2bff131535987da0330737f78fb23291593db30d Mon Sep 17 00:00:00 2001 From: jmr0 Date: Wed, 9 Mar 2016 01:24:46 -0500 Subject: [PATCH] Implement non-visible pipeline and iframe visibility methods --- components/compositing/compositor.rs | 29 ++++-- components/compositing/compositor_thread.rs | 3 + components/constellation/constellation.rs | 47 +++++++++- components/constellation/pipeline.rs | 30 +++++- components/script/dom/htmliframeelement.rs | 54 ++++++++++- .../script/dom/webidls/BrowserElement.webidl | 20 ++-- components/script/dom/window.rs | 8 ++ components/script/script_thread.rs | 60 ++++++++++++ components/script/timers.rs | 38 +++++++- components/script_traits/lib.rs | 9 +- components/script_traits/script_msg.rs | 4 + tests/wpt/mozilla/meta/MANIFEST.json | 6 ++ .../tests/mozilla/mozbrowser/helper.html | 6 ++ .../mozilla/mozbrowser/iframe_visibility.html | 92 +++++++++++++++++++ 14 files changed, 382 insertions(+), 24 deletions(-) create mode 100644 tests/wpt/mozilla/tests/mozilla/mozbrowser/helper.html create mode 100644 tests/wpt/mozilla/tests/mozilla/mozbrowser/iframe_visibility.html diff --git a/components/compositing/compositor.rs b/components/compositing/compositor.rs index 6bfa6c992e4..d5cbefa4e3a 100644 --- a/components/compositing/compositor.rs +++ b/components/compositing/compositor.rs @@ -290,6 +290,9 @@ struct PipelineDetails { /// Whether there are animation callbacks animation_callbacks_running: bool, + + /// Whether this pipeline is visible + visible: bool, } impl PipelineDetails { @@ -299,6 +302,7 @@ impl PipelineDetails { current_epoch: Epoch(0), animations_running: false, animation_callbacks_running: false, + visible: true, } } } @@ -760,6 +764,13 @@ impl IOCompositor { reports_chan.send(reports); } + (Msg::PipelineVisibilityChanged(pipeline_id, visible), ShutdownState::NotShuttingDown) => { + self.pipeline_details(pipeline_id).visible = visible; + if visible { + self.process_animations(); + } + } + (Msg::PipelineExited(pipeline_id, sender), _) => { debug!("Compositor got pipeline exited: {:?}", pipeline_id); self.pending_subpages.remove(&pipeline_id); @@ -795,13 +806,16 @@ impl IOCompositor { animation_state: AnimationState) { match animation_state { AnimationState::AnimationsPresent => { + let visible = self.pipeline_details(pipeline_id).visible; self.pipeline_details(pipeline_id).animations_running = true; - self.composite_if_necessary(CompositingReason::Animation); + if visible { + self.composite_if_necessary(CompositingReason::Animation); + } } AnimationState::AnimationCallbacksPresent => { - if !self.pipeline_details(pipeline_id).animation_callbacks_running { - self.pipeline_details(pipeline_id).animation_callbacks_running = - true; + let visible = self.pipeline_details(pipeline_id).visible; + self.pipeline_details(pipeline_id).animation_callbacks_running = true; + if visible { self.tick_animations_for_pipeline(pipeline_id); } } @@ -1712,9 +1726,10 @@ impl IOCompositor { fn process_animations(&mut self) { let mut pipeline_ids = vec![]; for (pipeline_id, pipeline_details) in &self.pipeline_details { - if pipeline_details.animations_running || - pipeline_details.animation_callbacks_running { - pipeline_ids.push(*pipeline_id); + if (pipeline_details.animations_running || + pipeline_details.animation_callbacks_running) && + pipeline_details.visible { + pipeline_ids.push(*pipeline_id); } } for pipeline_id in &pipeline_ids { diff --git a/components/compositing/compositor_thread.rs b/components/compositing/compositor_thread.rs index 325cdc3d104..d29235858b8 100644 --- a/components/compositing/compositor_thread.rs +++ b/components/compositing/compositor_thread.rs @@ -183,6 +183,8 @@ pub enum Msg { ResizeTo(Size2D), /// Get scroll offset of a layer GetScrollOffset(PipelineId, LayerId, IpcSender>), + /// Pipeline visibility changed + PipelineVisibilityChanged(PipelineId, bool), /// A pipeline was shut down. // This message acts as a synchronization point between the constellation, // when it shuts down a pipeline, to the compositor; when the compositor @@ -223,6 +225,7 @@ impl Debug for Msg { Msg::GetClientWindow(..) => write!(f, "GetClientWindow"), Msg::MoveTo(..) => write!(f, "MoveTo"), Msg::ResizeTo(..) => write!(f, "ResizeTo"), + Msg::PipelineVisibilityChanged(..) => write!(f, "PipelineVisibilityChanged"), Msg::PipelineExited(..) => write!(f, "PipelineExited"), Msg::GetScrollOffset(..) => write!(f, "GetScrollOffset"), } diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index cdf66a006fb..5e4e5ee2ba7 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -407,6 +407,12 @@ impl Constellation load_data: LoadData) { if self.shutting_down { return; } + let parent_visibility = if let Some((parent_pipeline_id, _, _)) = parent_info { + self.pipelines.get(&parent_pipeline_id).map(|pipeline| pipeline.visible) + } else { + None + }; + let result = Pipeline::spawn::(InitialPipelineState { id: pipeline_id, parent_info: parent_info, @@ -427,6 +433,7 @@ impl Constellation load_data: load_data, device_pixel_ratio: self.window_size.device_pixel_ratio, pipeline_namespace_id: self.next_pipeline_namespace_id(), + parent_visibility: parent_visibility, webrender_api_sender: self.webrender_api_sender.clone(), }); @@ -710,6 +717,14 @@ impl Constellation } } } + FromScriptMsg::SetVisible(pipeline_id, visible) => { + debug!("constellation got set visible messsage"); + self.handle_set_visible_msg(pipeline_id, visible); + } + FromScriptMsg::VisibilityChangeComplete(pipeline_id, visible) => { + debug!("constellation got set visibility change complete message"); + self.handle_visibility_change_complete(pipeline_id, visible); + } FromScriptMsg::RemoveIFrame(pipeline_id, sender) => { debug!("constellation got remove iframe message"); self.handle_remove_iframe_msg(pipeline_id); @@ -943,7 +958,8 @@ impl Constellation let window_size = self.window_size.visible_viewport; let root_pipeline_id = PipelineId::new(); debug_assert!(PipelineId::fake_root_pipeline_id() == root_pipeline_id); - self.new_pipeline(root_pipeline_id, None, Some(window_size), None, LoadData::new(url.clone(), None, None)); + self.new_pipeline(root_pipeline_id, None, Some(window_size), None, + LoadData::new(url.clone(), None, None)); self.handle_load_start_msg(&root_pipeline_id); self.push_pending_frame(root_pipeline_id, None); self.compositor_proxy.send(ToCompositorMsg::ChangePageUrl(root_pipeline_id, url)); @@ -1482,6 +1498,35 @@ impl Constellation } } + fn handle_set_visible_msg(&mut self, pipeline_id: PipelineId, visible: bool) { + let frame_id = self.pipeline_to_frame_map.get(&pipeline_id).map(|frame_id| *frame_id); + let child_pipeline_ids: Vec = self.current_frame_tree_iter(frame_id) + .map(|frame| frame.current) + .collect(); + for id in child_pipeline_ids { + if let Some(pipeline) = self.pipelines.get_mut(&id) { + pipeline.change_visibility(visible); + } + } + } + + fn handle_visibility_change_complete(&mut self, pipeline_id: PipelineId, visibility: bool) { + let parent_pipeline_info = self.pipelines.get(&pipeline_id).and_then(|source| source.parent_info); + if let Some((parent_pipeline_id, _, _)) = parent_pipeline_info { + let visibility_msg = ConstellationControlMsg::NotifyVisibilityChange(parent_pipeline_id, + pipeline_id, + visibility); + let result = match self.pipelines.get(&parent_pipeline_id) { + None => return warn!("Parent pipeline {:?} closed", parent_pipeline_id), + Some(parent_pipeline) => parent_pipeline.script_chan.send(visibility_msg), + }; + + if let Err(e) = result { + self.handle_send_error(parent_pipeline_id, e); + } + } + } + fn handle_create_canvas_paint_thread_msg( &mut self, size: &Size2D, diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index 578ccae0927..8a48108ca81 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -65,6 +65,9 @@ pub struct Pipeline { pub running_animations: bool, pub children: Vec, pub is_private: bool, + /// Whether this pipeline should be treated as visible for the purposes of scheduling and + /// resource management. + pub visible: bool, } /// Initial setup data needed to construct a pipeline. @@ -112,6 +115,8 @@ pub struct InitialPipelineState { pub load_data: LoadData, /// The ID of the pipeline namespace for this script thread. pub pipeline_namespace_id: PipelineNamespaceId, + /// Pipeline visibility is inherited from parent + pub parent_visibility: Option, /// Optional webrender api (if enabled). pub webrender_api_sender: Option, } @@ -250,7 +255,10 @@ impl Pipeline { state.compositor_proxy, chrome_to_paint_chan, state.load_data.url, - state.window_size); + state.window_size, + state.parent_visibility.unwrap_or(true)); + + pipeline.notify_visibility(); Ok((pipeline, child_process)) } @@ -262,7 +270,8 @@ impl Pipeline { compositor_proxy: Box, chrome_to_paint_chan: Sender, url: Url, - size: Option>) + size: Option>, + visible: bool) -> Pipeline { Pipeline { id: id, @@ -277,6 +286,7 @@ impl Pipeline { size: size, running_animations: false, is_private: false, + visible: visible, } } @@ -367,6 +377,22 @@ impl Pipeline { warn!("Sending mozbrowser event to script failed ({}).", e); } } + + fn notify_visibility(&self) { + self.script_chan.send(ConstellationControlMsg::ChangeFrameVisibilityStatus(self.id, self.visible)) + .expect("Pipeline script chan"); + + self.compositor_proxy.send(CompositorMsg::PipelineVisibilityChanged(self.id, self.visible)); + } + + pub fn change_visibility(&mut self, visible: bool) { + if visible == self.visible { + return; + } + self.visible = visible; + self.notify_visibility(); + } + } #[derive(Deserialize, Serialize)] diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 44122f07fe4..54712f44cad 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -11,12 +11,13 @@ use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementLocat use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementOpenTabEventDetail; use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementOpenWindowEventDetail; use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementSecurityChangeDetail; +use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementVisibilityChangeEventDetail; use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserShowModalPromptEventDetail; use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding; use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods; use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::conversions::ToJSValConvertible; -use dom::bindings::error::{Error, ErrorResult}; +use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::global::GlobalRef; use dom::bindings::inheritance::Castable; use dom::bindings::js::{Root, LayoutJS}; @@ -66,6 +67,7 @@ pub struct HTMLIFrameElement { subpage_id: Cell>, sandbox: Cell>, load_blocker: DOMRefCell>, + visibility: Cell, } impl HTMLIFrameElement { @@ -196,6 +198,7 @@ impl HTMLIFrameElement { subpage_id: Cell::new(None), sandbox: Cell::new(None), load_blocker: DOMRefCell::new(None), + visibility: Cell::new(true), } } @@ -221,6 +224,26 @@ impl HTMLIFrameElement { self.pipeline_id.get() } + pub fn change_visibility_status(&self, visibility: bool) { + if self.visibility.get() != visibility { + self.visibility.set(visibility); + + // Visibility changes are only exposed to Mozbrowser iframes + if self.Mozbrowser() { + self.dispatch_mozbrowser_event(MozBrowserEvent::VisibilityChange(visibility)); + } + } + } + + pub fn set_visible(&self, visible: bool) { + if let Some(pipeline_id) = self.pipeline_id.get() { + let window = window_from_node(self); + let window = window.r(); + let msg = ConstellationMsg::SetVisible(pipeline_id, visible); + window.constellation_chan().send(msg).unwrap(); + } + } + /// https://html.spec.whatwg.org/multipage/#iframe-load-event-steps steps 1-4 pub fn iframe_load_event_steps(&self, loaded_pipeline: PipelineId) { // TODO(#9592): assert that the load blocker is present at all times when we @@ -387,6 +410,11 @@ impl MozBrowserEventDetailBuilder for HTMLIFrameElement { returnValue: Some(DOMString::from(return_value)), }.to_jsval(cx, rval) } + MozBrowserEvent::VisibilityChange(visibility) => { + BrowserElementVisibilityChangeEventDetail { + visible: Some(visibility), + }.to_jsval(cx, rval); + } } } } @@ -498,6 +526,30 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement { } } + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/setVisible + fn SetVisible(&self, visible: bool) -> ErrorResult { + if self.Mozbrowser() { + self.set_visible(visible); + Ok(()) + } else { + debug!("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) + } + } + + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/getVisible + fn GetVisible(&self) -> Fallible { + if self.Mozbrowser() { + Ok(self.visibility.get()) + } else { + debug!("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) + } + } + + // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/stop fn Stop(&self) -> ErrorResult { Err(Error::NotSupported) diff --git a/components/script/dom/webidls/BrowserElement.webidl b/components/script/dom/webidls/BrowserElement.webidl index 9351cc9377a..f183b5cf813 100644 --- a/components/script/dom/webidls/BrowserElement.webidl +++ b/components/script/dom/webidls/BrowserElement.webidl @@ -96,20 +96,24 @@ dictionary BrowserElementOpenWindowEventDetail { // Element frameElement; }; +dictionary BrowserElementVisibilityChangeEventDetail { + boolean visible; +}; + BrowserElement implements BrowserElementCommon; BrowserElement implements BrowserElementPrivileged; [NoInterfaceObject] interface BrowserElementCommon { - //[Throws, - // Pref="dom.mozBrowserFramesEnabled", - // CheckAnyPermissions="browser embed-widgets"] - //void setVisible(boolean visible); + [Throws, + Pref="dom.mozbrowser.enabled", + CheckAnyPermissions="browser embed-widgets"] + void setVisible(boolean visible); - //[Throws, - // Pref="dom.mozBrowserFramesEnabled", - // CheckAnyPermissions="browser embed-widgets"] - //DOMRequest getVisible(); + [Throws, + Pref="dom.mozbrowser.enabled", + CheckAnyPermissions="browser embed-widgets"] + boolean getVisible(); //[Throws, // Pref="dom.mozBrowserFramesEnabled", diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 4bfbb1993d1..b778b37abd7 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -1477,6 +1477,14 @@ impl Window { self.timers.suspend(); } + pub fn slow_down_timers(&self) { + self.timers.slow_down(); + } + + pub fn speed_up_timers(&self) { + self.timers.speed_up(); + } + pub fn need_emit_timeline_marker(&self, timeline_type: TimelineMarkerType) -> bool { let markers = self.devtools_markers.borrow(); markers.contains(&timeline_type) diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 51460cd4bb9..c17ea8b5bfc 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -139,6 +139,8 @@ struct InProgressLoad { clip_rect: Option>, /// Window is frozen (navigated away while loading for example). is_frozen: bool, + /// Window is visible. + is_visible: bool, /// The requested URL of the load. url: Url, } @@ -157,6 +159,7 @@ impl InProgressLoad { window_size: window_size, clip_rect: None, is_frozen: false, + is_visible: true, url: url, } } @@ -918,6 +921,10 @@ impl ScriptThread { self.handle_freeze_msg(pipeline_id), ConstellationControlMsg::Thaw(pipeline_id) => self.handle_thaw_msg(pipeline_id), + ConstellationControlMsg::ChangeFrameVisibilityStatus(pipeline_id, visible) => + self.handle_visibility_change_msg(pipeline_id, visible), + ConstellationControlMsg::NotifyVisibilityChange(containing_id, pipeline_id, visible) => + self.handle_visibility_change_complete_msg(containing_id, pipeline_id, visible), ConstellationControlMsg::MozBrowserEvent(parent_pipeline_id, subpage_id, event) => @@ -1217,6 +1224,55 @@ impl ScriptThread { reports_chan.send(reports); } + /// To slow/speed up timers and manage any other script thread resource based on visibility. + /// Returns true if successful. + fn alter_resource_utilization(&self, id: PipelineId, visible: bool) -> bool { + if let Some(root_context) = self.browsing_context.get() { + if let Some(ref inner_context) = root_context.find(id) { + let window = inner_context.active_window(); + if visible { + window.speed_up_timers(); + } else { + window.slow_down_timers(); + } + return true; + } + } + false + } + + /// Updates iframe element after a change in visibility + fn handle_visibility_change_complete_msg(&self, containing_id: PipelineId, id: PipelineId, visible: bool) { + if let Some(root_context) = self.browsing_context.get() { + if let Some(ref inner_context) = root_context.find(containing_id) { + if let Some(iframe) = inner_context.active_document().find_iframe_by_pipeline(id) { + iframe.change_visibility_status(visible); + } + } + } + } + + /// Handle visibility change message + fn handle_visibility_change_msg(&self, id: PipelineId, visible: bool) { + let resources_altered = self.alter_resource_utilization(id, visible); + + // Separate message sent since parent script thread could be different (Iframe of different + // domain) + self.constellation_chan.send(ConstellationMsg::VisibilityChangeComplete(id, visible)).unwrap(); + + if !resources_altered { + let mut loads = self.incomplete_loads.borrow_mut(); + if let Some(ref mut load) = loads.iter_mut().find(|load| load.pipeline_id == id) { + load.is_visible = visible; + return; + } + } else { + return; + } + + warn!("change visibility message sent to nonexistent pipeline"); + } + /// Handles freeze message fn handle_freeze_msg(&self, id: PipelineId) { if let Some(root_context) = self.browsing_context.get() { @@ -1692,6 +1748,10 @@ impl ScriptThread { window.freeze(); } + if !incomplete.is_visible { + self.alter_resource_utilization(browsing_context.pipeline(), false); + } + context_remover.neuter(); document.get_current_parser().unwrap() diff --git a/components/script/timers.rs b/components/script/timers.rs index 53113e06682..709ee9b967a 100644 --- a/components/script/timers.rs +++ b/components/script/timers.rs @@ -22,6 +22,7 @@ use std::cmp::{self, Ord, Ordering}; use std::collections::HashMap; use std::default::Default; use std::rc::Rc; +use util::prefs::get_pref; #[derive(JSTraceable, PartialEq, Eq, Copy, Clone, HeapSizeOf, Hash, PartialOrd, Ord, Debug)] pub struct OneshotTimerHandle(i32); @@ -209,6 +210,15 @@ impl OneshotTimers { } } + pub fn slow_down(&self) { + let duration = get_pref("js.timers.minimum_duration").as_u64().unwrap_or(1000); + self.js_timers.set_min_duration(MsDuration::new(duration)); + } + + pub fn speed_up(&self) { + self.js_timers.remove_min_duration(); + } + pub fn suspend(&self) { assert!(self.suspended_since.get().is_none()); @@ -287,6 +297,8 @@ pub struct JsTimers { active_timers: DOMRefCell>, /// The nesting level of the currently executing timer task or 0. nesting_level: Cell, + /// Used to introduce a minimum delay in event intervals + min_duration: Cell>, } #[derive(JSTraceable, HeapSizeOf)] @@ -341,6 +353,7 @@ impl JsTimers { next_timer_handle: Cell::new(JsTimerHandle(1)), active_timers: DOMRefCell::new(HashMap::new()), nesting_level: Cell::new(0), + min_duration: Cell::new(None), } } @@ -404,6 +417,24 @@ impl JsTimers { } } + pub fn set_min_duration(&self, duration: MsDuration) { + self.min_duration.set(Some(duration)); + } + + pub fn remove_min_duration(&self) { + self.min_duration.set(None); + } + + // see step 13 of https://html.spec.whatwg.org/multipage/#timer-initialisation-steps + fn user_agent_pad(&self, current_duration: MsDuration) -> MsDuration { + match self.min_duration.get() { + Some(min_duration) => { + cmp::max(min_duration, current_duration) + }, + None => current_duration + } + } + // see https://html.spec.whatwg.org/multipage/#timer-initialisation-steps fn initialize_and_schedule(&self, global: GlobalRef, mut task: JsTimerTask) { let handle = task.handle; @@ -412,13 +443,12 @@ impl JsTimers { // step 6 let nesting_level = self.nesting_level.get(); - // step 7 - let duration = clamp_duration(nesting_level, task.duration); - + // step 7, 13 + let duration = self.user_agent_pad(clamp_duration(nesting_level, task.duration)); // step 8, 9 task.nesting_level = nesting_level + 1; - // essentially step 11-14 + // essentially step 11, 12, and 14 let callback = OneshotTimerCallback::JsTimer(task); let oneshot_handle = global.schedule_callback(callback, duration); diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 82795e4a43c..cd7a96df537 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -133,6 +133,10 @@ pub enum ConstellationControlMsg { Freeze(PipelineId), /// Notifies script thread to resume all its timers Thaw(PipelineId), + /// Notifies script thread whether frame is visible + ChangeFrameVisibilityStatus(PipelineId, bool), + /// Notifies script thread that frame visibility change is complete + NotifyVisibilityChange(PipelineId, PipelineId, bool), /// Notifies script thread that a url should be loaded in this iframe. Navigate(PipelineId, SubpageId, LoadData), /// Requests the script thread forward a mozbrowser event to an iframe it owns @@ -421,6 +425,8 @@ pub enum MozBrowserEvent { UsernameAndPasswordRequired, /// Sent when a link to a search engine is found. OpenSearch, + /// Sent when visibility state changes. + VisibilityChange(bool), } impl MozBrowserEvent { @@ -442,7 +448,8 @@ impl MozBrowserEvent { MozBrowserEvent::ShowModalPrompt(_, _, _, _) => "mozbrowsershowmodalprompt", MozBrowserEvent::TitleChange(_) => "mozbrowsertitlechange", MozBrowserEvent::UsernameAndPasswordRequired => "mozbrowserusernameandpasswordrequired", - MozBrowserEvent::OpenSearch => "mozbrowseropensearch" + MozBrowserEvent::OpenSearch => "mozbrowseropensearch", + MozBrowserEvent::VisibilityChange(_) => "mozbrowservisibilitychange", } } } diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index 241833bc13f..7fc871eb4cc 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -81,6 +81,10 @@ pub enum ScriptMsg { NodeStatus(Option), /// Notification that this iframe should be removed. RemoveIFrame(PipelineId, Option>), + /// Change pipeline visibility + SetVisible(PipelineId, bool), + /// Notifies constellation that an iframe's visibility has been changed. + VisibilityChangeComplete(PipelineId, bool), /// A load has been requested in an IFrame. ScriptLoadedURLInIFrame(IFrameLoadInfo), /// Requests that the constellation set the contents of the clipboard diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 24af8aa6f41..a66bd46c4f6 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -6538,6 +6538,12 @@ "url": "/_mozilla/mozilla/mozbrowser/iframe_reload_twice.html" } ], + "mozilla/mozbrowser/iframe_visibility.html": [ + { + "path": "mozilla/mozbrowser/iframe_visibility.html", + "url": "/_mozilla/mozilla/mozbrowser/iframe_visibility.html" + } + ], "mozilla/mozbrowser/mozbrowser_click_fires_openwindow.html": [ { "path": "mozilla/mozbrowser/mozbrowser_click_fires_openwindow.html", diff --git a/tests/wpt/mozilla/tests/mozilla/mozbrowser/helper.html b/tests/wpt/mozilla/tests/mozilla/mozbrowser/helper.html new file mode 100644 index 00000000000..dee1ea597e5 --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/mozbrowser/helper.html @@ -0,0 +1,6 @@ + + + +

test

+ + diff --git a/tests/wpt/mozilla/tests/mozilla/mozbrowser/iframe_visibility.html b/tests/wpt/mozilla/tests/mozilla/mozbrowser/iframe_visibility.html new file mode 100644 index 00000000000..4681f5db25a --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/mozbrowser/iframe_visibility.html @@ -0,0 +1,92 @@ + + + +Iframe visibility tests + + + + + +