diff options
Diffstat (limited to 'components/script')
-rw-r--r-- | components/script/dom/htmliframeelement.rs | 54 | ||||
-rw-r--r-- | components/script/dom/webidls/BrowserElement.webidl | 22 | ||||
-rw-r--r-- | components/script/dom/window.rs | 8 | ||||
-rw-r--r-- | components/script/script_thread.rs | 60 | ||||
-rw-r--r-- | components/script/timers.rs | 38 |
5 files changed, 168 insertions, 14 deletions
diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 57248c03e97..86146b8e915 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::{JS, MutNullableHeap, Root, LayoutJS}; @@ -68,6 +69,7 @@ pub struct HTMLIFrameElement { sandbox: MutNullableHeap<JS<DOMTokenList>>, sandbox_allowance: Cell<Option<u8>>, load_blocker: DOMRefCell<Option<LoadBlocker>>, + visibility: Cell<bool>, } impl HTMLIFrameElement { @@ -199,6 +201,7 @@ impl HTMLIFrameElement { sandbox: Default::default(), sandbox_allowance: Cell::new(None), load_blocker: DOMRefCell::new(None), + visibility: Cell::new(true), } } @@ -224,6 +227,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 @@ -390,6 +413,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); + } } } } @@ -496,6 +524,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<bool> { + 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.mozBrowserFramesEnabled", - // CheckAnyPermissions="browser embed-widgets"] - //DOMRequest getVisible(); + [Throws, + Pref="dom.mozbrowser.enabled", + CheckAnyPermissions="browser embed-widgets"] + void setVisible(boolean visible); + + [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 34f7c1acdb0..486ad45bde4 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -1509,6 +1509,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 4750a8d6390..3459e5ddcfa 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -140,6 +140,8 @@ struct InProgressLoad { clip_rect: Option<Rect<f32>>, /// 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, } @@ -158,6 +160,7 @@ impl InProgressLoad { window_size: window_size, clip_rect: None, is_frozen: false, + is_visible: true, url: url, } } @@ -919,6 +922,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) => @@ -1232,6 +1239,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() { @@ -1707,6 +1763,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 504dbb8aaaf..989382e80fe 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); @@ -212,6 +213,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()); @@ -290,6 +300,8 @@ pub struct JsTimers { active_timers: DOMRefCell<HashMap<JsTimerHandle, JsTimerEntry>>, /// The nesting level of the currently executing timer task or 0. nesting_level: Cell<u32>, + /// Used to introduce a minimum delay in event intervals + min_duration: Cell<Option<MsDuration>>, } #[derive(JSTraceable, HeapSizeOf)] @@ -344,6 +356,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), } } @@ -407,6 +420,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; @@ -415,13 +446,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); |