diff --git a/components/constellation/constellation.rs b/components/constellation/constellation.rs index b262684d5d9f..c37727ff8e46 100644 --- a/components/constellation/constellation.rs +++ b/components/constellation/constellation.rs @@ -1043,14 +1043,14 @@ impl Constellation debug!("Panic handler for pipeline {:?}: {}.", pipeline_id, reason); + // Notify the browser chrome that the pipeline has failed + self.trigger_mozbrowsererror(pipeline_id, reason, backtrace); + if let Some(pipeline_id) = pipeline_id { let pipeline_url = self.pipelines.get(&pipeline_id).map(|pipeline| pipeline.url.clone()); let parent_info = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.parent_info); let window_size = self.pipelines.get(&pipeline_id).and_then(|pipeline| pipeline.size); - // Notify the browser chrome that the pipeline has failed - self.trigger_mozbrowsererror(pipeline_id, reason, backtrace); - self.close_pipeline(pipeline_id, ExitPipelineMode::Force); self.pipelines.remove(&pipeline_id); @@ -1082,12 +1082,10 @@ impl Constellation self.handled_panic = true; } - // TODO: trigger a mozbrowsererror even if there's no pipeline id fn handle_log_entry(&mut self, pipeline_id: Option, thread_name: Option, entry: LogEntry) { - match (pipeline_id, entry) { - (Some(pipeline_id), LogEntry::Panic(reason, backtrace)) => - self.trigger_mozbrowsererror(pipeline_id, reason, backtrace), - (None, LogEntry::Panic(reason, _)) | (_, LogEntry::Error(reason)) | (_, LogEntry::Warn(reason)) => { + match entry { + LogEntry::Panic(reason, backtrace) => self.trigger_mozbrowsererror(pipeline_id, reason, backtrace), + LogEntry::Error(reason) | LogEntry::Warn(reason) => { // VecDeque::truncate is unstable if WARNINGS_BUFFER_SIZE <= self.handled_warnings.len() { self.handled_warnings.pop_front(); @@ -1284,7 +1282,7 @@ impl Constellation // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowsershowmodalprompt let event = MozBrowserEvent::ShowModalPrompt("alert".to_owned(), "Alert".to_owned(), String::from(message), "".to_owned()); - root_pipeline.trigger_mozbrowser_event(subpage_id, event); + root_pipeline.trigger_mozbrowser_event(Some(subpage_id), event); } None => return warn!("Alert sent to Pipeline {:?} after closure.", root_pipeline_id), } @@ -1578,7 +1576,7 @@ impl Constellation fn handle_mozbrowser_event_msg(&mut self, containing_pipeline_id: PipelineId, - subpage_id: SubpageId, + subpage_id: Option, event: MozBrowserEvent) { assert!(PREFS.is_mozbrowser_enabled()); @@ -2271,7 +2269,7 @@ impl Constellation let can_go_backward = !frame.prev.is_empty(); let can_go_forward = !frame.next.is_empty(); let event = MozBrowserEvent::LocationChange(url, can_go_backward, can_go_forward); - parent_pipeline.trigger_mozbrowser_event(subpage_id, event); + parent_pipeline.trigger_mozbrowser_event(Some(subpage_id), event); } } } @@ -2280,35 +2278,43 @@ impl Constellation // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowsererror // Note that this does not require the pipeline to be an immediate child of the root - // TODO: allow the pipeline id to be optional, triggering the error on the root if it's not provided. - fn trigger_mozbrowsererror(&mut self, pipeline_id: PipelineId, reason: String, backtrace: String) { + fn trigger_mozbrowsererror(&mut self, pipeline_id: Option, reason: String, backtrace: String) { if !PREFS.is_mozbrowser_enabled() { return; } - let ancestor_info = self.get_mozbrowser_ancestor_info(pipeline_id); - - if let Some(ancestor_info) = ancestor_info { - match self.pipelines.get(&ancestor_info.0) { - Some(ancestor) => { - let mut report = String::new(); - for (thread_name, warning) in self.handled_warnings.drain(..) { - report.push_str("\nWARNING: "); - if let Some(thread_name) = thread_name { - report.push_str("<"); - report.push_str(&*thread_name); - report.push_str(">: "); - } - report.push_str(&*warning); - } - report.push_str("\nERROR: "); - report.push_str(&*reason); - report.push_str("\n\n"); - report.push_str(&*backtrace); - let event = MozBrowserEvent::Error(MozBrowserErrorType::Fatal, Some(reason), Some(report)); - ancestor.trigger_mozbrowser_event(ancestor_info.1, event); - }, - None => return warn!("Mozbrowsererror via closed pipeline {:?}.", ancestor_info.0), + let mut report = String::new(); + for (thread_name, warning) in self.handled_warnings.drain(..) { + report.push_str("\nWARNING: "); + if let Some(thread_name) = thread_name { + report.push_str("<"); + report.push_str(&*thread_name); + report.push_str(">: "); } + report.push_str(&*warning); } + report.push_str("\nERROR: "); + report.push_str(&*reason); + report.push_str("\n\n"); + report.push_str(&*backtrace); + + let event = MozBrowserEvent::Error(MozBrowserErrorType::Fatal, Some(reason), Some(report)); + + if let Some(pipeline_id) = pipeline_id { + if let Some((ancestor_id, subpage_id)) = self.get_mozbrowser_ancestor_info(pipeline_id) { + if let Some(ancestor) = self.pipelines.get(&ancestor_id) { + return ancestor.trigger_mozbrowser_event(Some(subpage_id), event); + } + } + } + + if let Some(root_frame_id) = self.root_frame_id { + if let Some(root_frame) = self.frames.get(&root_frame_id) { + if let Some(root_pipeline) = self.pipelines.get(&root_frame.current) { + return root_pipeline.trigger_mozbrowser_event(None, event); + } + } + } + + warn!("Mozbrowser error after root pipeline closed."); } fn focused_pipeline_in_tree(&self, frame_id: FrameId) -> bool { diff --git a/components/constellation/pipeline.rs b/components/constellation/pipeline.rs index c07891984ede..2fcb5d468724 100644 --- a/components/constellation/pipeline.rs +++ b/components/constellation/pipeline.rs @@ -376,7 +376,7 @@ impl Pipeline { } pub fn trigger_mozbrowser_event(&self, - subpage_id: SubpageId, + subpage_id: Option, event: MozBrowserEvent) { assert!(PREFS.is_mozbrowser_enabled()); diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 09d58c55e1e8..4708f8ee9bbe 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -1281,7 +1281,7 @@ impl Document { if PREFS.is_mozbrowser_enabled() { if let Some((containing_pipeline_id, subpage_id, _)) = self.window.parent_info() { let event = ConstellationMsg::MozBrowserEvent(containing_pipeline_id, - subpage_id, + Some(subpage_id), event); self.window.constellation_chan().send(event).unwrap(); } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 48947047e05e..1b2001c08df1 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -161,25 +161,11 @@ impl HTMLIFrameElement { #[allow(unsafe_code)] pub fn dispatch_mozbrowser_event(&self, event: MozBrowserEvent) { - // TODO(gw): Support mozbrowser event types that have detail which is not a string. - // See https://developer.mozilla.org/en-US/docs/Web/API/Using_the_Browser_API - // for a list of mozbrowser events. assert!(PREFS.is_mozbrowser_enabled()); if self.Mozbrowser() { let window = window_from_node(self); - let custom_event = unsafe { - let cx = window.get_cx(); - let _ac = JSAutoCompartment::new(cx, window.reflector().get_jsobject().get()); - rooted!(in(cx) let mut detail = UndefinedValue()); - let event_name = Atom::from(event.name()); - self.build_mozbrowser_event_detail(event, cx, detail.handle_mut()); - CustomEvent::new(GlobalRef::Window(window.r()), - event_name, - true, - true, - detail.handle()) - }; + let custom_event = build_mozbrowser_custom_event(&window, event); custom_event.upcast::().fire(self.upcast()); } } @@ -336,97 +322,104 @@ impl HTMLIFrameElementLayoutMethods for LayoutJS { } } -pub trait MozBrowserEventDetailBuilder { - #[allow(unsafe_code)] - unsafe fn build_mozbrowser_event_detail(&self, - event: MozBrowserEvent, - cx: *mut JSContext, - rval: MutableHandleValue); +#[allow(unsafe_code)] +pub fn build_mozbrowser_custom_event(window: &Window, event: MozBrowserEvent) -> Root { + // TODO(gw): Support mozbrowser event types that have detail which is not a string. + // See https://developer.mozilla.org/en-US/docs/Web/API/Using_the_Browser_API + // for a list of mozbrowser events. + let cx = window.get_cx(); + let _ac = JSAutoCompartment::new(cx, window.reflector().get_jsobject().get()); + rooted!(in(cx) let mut detail = UndefinedValue()); + let event_name = Atom::from(event.name()); + unsafe { build_mozbrowser_event_detail(event, cx, detail.handle_mut()); } + CustomEvent::new(GlobalRef::Window(window), + event_name, + true, + true, + detail.handle()) } -impl MozBrowserEventDetailBuilder for HTMLIFrameElement { - #[allow(unsafe_code)] - unsafe fn build_mozbrowser_event_detail(&self, - event: MozBrowserEvent, - cx: *mut JSContext, - rval: MutableHandleValue) { - match event { - MozBrowserEvent::AsyncScroll | MozBrowserEvent::Close | MozBrowserEvent::ContextMenu | - MozBrowserEvent::LoadEnd | MozBrowserEvent::LoadStart | - MozBrowserEvent::Connected | MozBrowserEvent::OpenSearch | - MozBrowserEvent::UsernameAndPasswordRequired => { - rval.set(NullValue()); - } - MozBrowserEvent::Error(error_type, description, report) => { - BrowserElementErrorEventDetail { - type_: Some(DOMString::from(error_type.name())), - description: description.map(DOMString::from), - report: report.map(DOMString::from), - version: Some(DOMString::from_string(servo_version().into())), - }.to_jsval(cx, rval); - }, - MozBrowserEvent::SecurityChange(https_state) => { - BrowserElementSecurityChangeDetail { - // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowsersecuritychange - state: Some(DOMString::from(match https_state { - HttpsState::Modern => "secure", - HttpsState::Deprecated => "broken", - HttpsState::None => "insecure", - }.to_owned())), - // FIXME - Not supported yet: - trackingContent: None, - mixedContent: None, - trackingState: None, - extendedValidation: None, - mixedState: None, - }.to_jsval(cx, rval); - } - MozBrowserEvent::TitleChange(ref string) => { - string.to_jsval(cx, rval); - } - MozBrowserEvent::LocationChange(url, can_go_back, can_go_forward) => { - BrowserElementLocationChangeEventDetail { - url: Some(DOMString::from(url)), - canGoBack: Some(can_go_back), - canGoForward: Some(can_go_forward), - }.to_jsval(cx, rval); - } - MozBrowserEvent::OpenTab(url) => { - BrowserElementOpenTabEventDetail { - url: Some(DOMString::from(url)), - }.to_jsval(cx, rval); - } - MozBrowserEvent::OpenWindow(url, target, features) => { - BrowserElementOpenWindowEventDetail { - url: Some(DOMString::from(url)), - target: target.map(DOMString::from), - features: features.map(DOMString::from), - }.to_jsval(cx, rval); - } - MozBrowserEvent::IconChange(rel, href, sizes) => { - BrowserElementIconChangeEventDetail { - rel: Some(DOMString::from(rel)), - href: Some(DOMString::from(href)), - sizes: Some(DOMString::from(sizes)), - }.to_jsval(cx, rval); - } - MozBrowserEvent::ShowModalPrompt(prompt_type, title, message, return_value) => { - BrowserShowModalPromptEventDetail { - promptType: Some(DOMString::from(prompt_type)), - title: Some(DOMString::from(title)), - message: Some(DOMString::from(message)), - returnValue: Some(DOMString::from(return_value)), - }.to_jsval(cx, rval) - } - MozBrowserEvent::VisibilityChange(visibility) => { - BrowserElementVisibilityChangeEventDetail { - visible: Some(visibility), - }.to_jsval(cx, rval); - } +#[allow(unsafe_code)] +unsafe fn build_mozbrowser_event_detail(event: MozBrowserEvent, + cx: *mut JSContext, + rval: MutableHandleValue) { + match event { + MozBrowserEvent::AsyncScroll | MozBrowserEvent::Close | MozBrowserEvent::ContextMenu | + MozBrowserEvent::LoadEnd | MozBrowserEvent::LoadStart | + MozBrowserEvent::Connected | MozBrowserEvent::OpenSearch | + MozBrowserEvent::UsernameAndPasswordRequired => { + rval.set(NullValue()); + } + MozBrowserEvent::Error(error_type, description, report) => { + BrowserElementErrorEventDetail { + type_: Some(DOMString::from(error_type.name())), + description: description.map(DOMString::from), + report: report.map(DOMString::from), + version: Some(DOMString::from_string(servo_version().into())), + }.to_jsval(cx, rval); + }, + MozBrowserEvent::SecurityChange(https_state) => { + BrowserElementSecurityChangeDetail { + // https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowsersecuritychange + state: Some(DOMString::from(match https_state { + HttpsState::Modern => "secure", + HttpsState::Deprecated => "broken", + HttpsState::None => "insecure", + }.to_owned())), + // FIXME - Not supported yet: + trackingContent: None, + mixedContent: None, + trackingState: None, + extendedValidation: None, + mixedState: None, + }.to_jsval(cx, rval); + } + MozBrowserEvent::TitleChange(ref string) => { + string.to_jsval(cx, rval); + } + MozBrowserEvent::LocationChange(url, can_go_back, can_go_forward) => { + BrowserElementLocationChangeEventDetail { + url: Some(DOMString::from(url)), + canGoBack: Some(can_go_back), + canGoForward: Some(can_go_forward), + }.to_jsval(cx, rval); + } + MozBrowserEvent::OpenTab(url) => { + BrowserElementOpenTabEventDetail { + url: Some(DOMString::from(url)), + }.to_jsval(cx, rval); + } + MozBrowserEvent::OpenWindow(url, target, features) => { + BrowserElementOpenWindowEventDetail { + url: Some(DOMString::from(url)), + target: target.map(DOMString::from), + features: features.map(DOMString::from), + }.to_jsval(cx, rval); + } + MozBrowserEvent::IconChange(rel, href, sizes) => { + BrowserElementIconChangeEventDetail { + rel: Some(DOMString::from(rel)), + href: Some(DOMString::from(href)), + sizes: Some(DOMString::from(sizes)), + }.to_jsval(cx, rval); + } + MozBrowserEvent::ShowModalPrompt(prompt_type, title, message, return_value) => { + BrowserShowModalPromptEventDetail { + promptType: Some(DOMString::from(prompt_type)), + title: Some(DOMString::from(title)), + message: Some(DOMString::from(message)), + returnValue: Some(DOMString::from(return_value)), + }.to_jsval(cx, rval) + } + MozBrowserEvent::VisibilityChange(visibility) => { + BrowserElementVisibilityChangeEventDetail { + visible: Some(visibility), + }.to_jsval(cx, rval); } } } + pub fn Navigate(iframe: &HTMLIFrameElement, direction: NavigationDirection) -> ErrorResult { if iframe.Mozbrowser() { if iframe.upcast::().is_in_doc() { diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index 30ab695f9b7b..a1e1ba11522a 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -28,7 +28,9 @@ use dom::crypto::Crypto; use dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration}; use dom::document::Document; use dom::element::Element; +use dom::event::Event; use dom::eventtarget::EventTarget; +use dom::htmliframeelement::build_mozbrowser_custom_event; use dom::location::Location; use dom::navigator::Navigator; use dom::node::{Node, from_untrusted_node_address, window_from_node}; @@ -63,7 +65,7 @@ use script_runtime::{ScriptChan, ScriptPort, maybe_take_panic_result}; use script_thread::SendableMainThreadScriptChan; use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, RunnableWrapper}; use script_traits::webdriver_msg::{WebDriverJSError, WebDriverJSResult}; -use script_traits::{ConstellationControlMsg, UntrustedNodeAddress}; +use script_traits::{ConstellationControlMsg, MozBrowserEvent, UntrustedNodeAddress}; use script_traits::{DocumentState, MsDuration, TimerEvent, TimerEventId}; use script_traits::{ScriptMsg as ConstellationMsg, TimerEventRequest, TimerSource, WindowSizeData}; use std::ascii::AsciiExt; @@ -1594,6 +1596,13 @@ impl Window { _ => false, } } + + #[allow(unsafe_code)] + pub fn dispatch_mozbrowser_event(&self, event: MozBrowserEvent) { + assert!(PREFS.is_mozbrowser_enabled()); + let custom_event = build_mozbrowser_custom_event(&self, event); + custom_event.upcast::().fire(self.upcast()); + } } impl Window { diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index 07779fbebd29..89a96863df49 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -1383,17 +1383,17 @@ impl ScriptThread { /// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserloadstart fn handle_mozbrowser_event_msg(&self, parent_pipeline_id: PipelineId, - subpage_id: SubpageId, + subpage_id: Option, event: MozBrowserEvent) { - let borrowed_context = self.root_browsing_context(); - - let frame_element = borrowed_context.find(parent_pipeline_id).and_then(|context| { - let doc = context.active_document(); - doc.find_iframe(subpage_id) - }); - - if let Some(ref frame_element) = frame_element { - frame_element.dispatch_mozbrowser_event(event); + match self.root_browsing_context().find(parent_pipeline_id) { + None => warn!("Mozbrowser event after pipeline {:?} closed.", parent_pipeline_id), + Some(context) => match subpage_id { + None => context.active_window().dispatch_mozbrowser_event(event), + Some(subpage_id) => match context.active_document().find_iframe(subpage_id) { + None => warn!("Mozbrowser event after iframe {:?}/{:?} closed.", parent_pipeline_id, subpage_id), + Some(frame_element) => frame_element.dispatch_mozbrowser_event(event), + }, + }, } } diff --git a/components/script_traits/lib.rs b/components/script_traits/lib.rs index 967303e63b88..a51a120d3c20 100644 --- a/components/script_traits/lib.rs +++ b/components/script_traits/lib.rs @@ -176,8 +176,9 @@ pub enum ConstellationControlMsg { 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 - MozBrowserEvent(PipelineId, SubpageId, MozBrowserEvent), + /// Requests the script thread forward a mozbrowser event to an iframe it owns, + /// or to the window if no subpage id is provided. + MozBrowserEvent(PipelineId, Option, MozBrowserEvent), /// Updates the current subpage and pipeline IDs of a given iframe UpdateSubpageId(PipelineId, SubpageId, SubpageId, PipelineId), /// Set an iframe to be focused. Used when an element in an iframe gains focus. diff --git a/components/script_traits/script_msg.rs b/components/script_traits/script_msg.rs index e510a74a9c12..ea2ee1b63524 100644 --- a/components/script_traits/script_msg.rs +++ b/components/script_traits/script_msg.rs @@ -84,8 +84,9 @@ pub enum ScriptMsg { LoadComplete(PipelineId), /// A new load has been requested. LoadUrl(PipelineId, LoadData), - /// Dispatch a mozbrowser event to a given iframe. Only available in experimental mode. - MozBrowserEvent(PipelineId, SubpageId, MozBrowserEvent), + /// Dispatch a mozbrowser event to a given iframe, + /// or to the window if no subpage id is provided. + MozBrowserEvent(PipelineId, Option, MozBrowserEvent), /// HTMLIFrameElement Forward or Back navigation. Navigate(Option<(PipelineId, SubpageId)>, NavigationDirection), /// Favicon detected