aboutsummaryrefslogtreecommitdiffstats
path: root/components/script
diff options
context:
space:
mode:
Diffstat (limited to 'components/script')
-rw-r--r--components/script/dom/htmliframeelement.rs54
-rw-r--r--components/script/dom/webidls/BrowserElement.webidl22
-rw-r--r--components/script/dom/window.rs8
-rw-r--r--components/script/script_thread.rs60
-rw-r--r--components/script/timers.rs38
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);