mirror of
https://github.com/servo/servo.git
synced 2025-08-03 04:30:10 +01:00
Implement non-visible pipeline and iframe visibility methods
This commit is contained in:
parent
ce88b8ed30
commit
2bff131535
14 changed files with 382 additions and 24 deletions
|
@ -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<Option<SubpageId>>,
|
||||
sandbox: Cell<Option<u8>>,
|
||||
load_blocker: DOMRefCell<Option<LoadBlocker>>,
|
||||
visibility: Cell<bool>,
|
||||
}
|
||||
|
||||
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<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)
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -139,6 +139,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,
|
||||
}
|
||||
|
@ -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()
|
||||
|
|
|
@ -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<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)]
|
||||
|
@ -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);
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue