Skip to content

Commit

Permalink
Implement alert dialogs
Browse files Browse the repository at this point in the history
Fixed conflict

Fixed merge issue

Finished implementation

Disable tinyfiledialogs on Windows

addressed comments

Use ancestor's SubpageId
Move display alert from method to function
Add extra test for nested iframes

Addressed comments

Updated tinyfiledialogs
  • Loading branch information
cbrewster committed May 4, 2016
1 parent 3f2ceef commit dc85be4
Show file tree
Hide file tree
Showing 10 changed files with 122 additions and 19 deletions.
83 changes: 72 additions & 11 deletions components/compositing/constellation.rs
Expand Up @@ -797,6 +797,10 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
debug!("constellation got SetDocumentState message");
self.document_states.insert(pipeline_id, state);
}
Request::Script(FromScriptMsg::Alert(pipeline_id, message, sender)) => {
debug!("constellation got Alert message");
self.handle_alert(pipeline_id, message, sender);
}


// Messages from layout thread
Expand Down Expand Up @@ -1069,6 +1073,44 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
}
}

fn handle_alert(&mut self, pipeline_id: PipelineId, message: String, sender: IpcSender<bool>) {
let display_alert_dialog = if prefs::get_pref("dom.mozbrowser.enabled").as_boolean().unwrap_or(false) {
let parent_pipeline_info = self.pipelines.get(&pipeline_id).and_then(|source| source.parent_info);
if let Some(_) = parent_pipeline_info {
let root_pipeline_id = self.root_frame_id
.and_then(|root_frame_id| self.frames.get(&root_frame_id))
.map(|root_frame| root_frame.current);

let ancestor_info = self.get_root_pipeline_and_containing_parent(&pipeline_id);
if let Some(ancestor_info) = ancestor_info {
if root_pipeline_id == Some(ancestor_info.0) {
match root_pipeline_id.and_then(|pipeline_id| self.pipelines.get(&pipeline_id)) {
Some(root_pipeline) => {
// 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(ancestor_info.1, event);
}
None => return warn!("Alert sent to Pipeline {:?} after closure.", root_pipeline_id),
}
} else {
warn!("A non-current frame is trying to show an alert.")
}
}
false
} else {
true
}
} else {
true
};

let result = sender.send(display_alert_dialog);
if let Err(e) = result {
self.handle_send_error(pipeline_id, e);
}
}

fn handle_load_url_msg(&mut self, source_id: PipelineId, load_data: LoadData) {
self.load_url(source_id, load_data);
}
Expand Down Expand Up @@ -1945,6 +1987,29 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
}
}

/// For a given pipeline, determine the iframe in the root pipeline that transitively contains
/// it. There could be arbitrary levels of nested iframes in between them.
fn get_root_pipeline_and_containing_parent(&self, pipeline_id: &PipelineId) -> Option<(PipelineId, SubpageId)> {
if let Some(pipeline) = self.pipelines.get(pipeline_id) {
if let Some(mut ancestor_info) = pipeline.parent_info {
if let Some(mut ancestor) = self.pipelines.get(&ancestor_info.0) {
while let Some(next_info) = ancestor.parent_info {
ancestor_info = next_info;
ancestor = match self.pipelines.get(&ancestor_info.0) {
Some(ancestor) => ancestor,
None => {
warn!("Get parent pipeline before root via closed pipeline {:?}.", ancestor_info.0);
return None;
},
};
}
return Some(ancestor_info);
}
}
}
None
}

// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserlocationchange
// Note that this is a no-op if the pipeline is not an immediate child iframe of the root
fn trigger_mozbrowserlocationchange(&self, pipeline_id: PipelineId) {
Expand Down Expand Up @@ -1976,19 +2041,15 @@ impl<LTF: LayoutThreadFactory, STF: ScriptThreadFactory> Constellation<LTF, STF>
fn trigger_mozbrowsererror(&self, pipeline_id: PipelineId, reason: String, backtrace: String) {
if !prefs::get_pref("dom.mozbrowser.enabled").as_boolean().unwrap_or(false) { return; }

if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
if let Some(mut ancestor_info) = pipeline.parent_info {
if let Some(mut ancestor) = self.pipelines.get(&ancestor_info.0) {
while let Some(next_info) = ancestor.parent_info {
ancestor_info = next_info;
ancestor = match self.pipelines.get(&ancestor_info.0) {
Some(ancestor) => ancestor,
None => return warn!("Mozbrowsererror via closed pipeline {:?}.", ancestor_info.0),
};
}
let ancestor_info = self.get_root_pipeline_and_containing_parent(&pipeline_id);

if let Some(ancestor_info) = ancestor_info {
match self.pipelines.get(&ancestor_info.0) {
Some(ancestor) => {
let event = MozBrowserEvent::Error(MozBrowserErrorType::Fatal, Some(reason), Some(backtrace));
ancestor.trigger_mozbrowser_event(ancestor_info.1, event);
}
},
None => return warn!("Mozbrowsererror via closed pipeline {:?}.", ancestor_info.0),
}
}
}
Expand Down
3 changes: 3 additions & 0 deletions components/script/Cargo.toml
Expand Up @@ -13,6 +13,9 @@ path = "lib.rs"
[features]
debugmozjs = ['js/debugmozjs']

[target.'cfg(any(target_os = "macos", target_os = "linux"))'.dependencies]
tinyfiledialogs = {git = "https://github.com/jdm/tinyfiledialogs"}

[dependencies]
plugins = {path = "../plugins"}
util = {path = "../util"}
Expand Down
25 changes: 20 additions & 5 deletions components/script/dom/window.rs
Expand Up @@ -59,7 +59,7 @@ use script_thread::SendableMainThreadScriptChan;
use script_thread::{MainThreadScriptChan, MainThreadScriptMsg, RunnableWrapper};
use script_traits::{ConstellationControlMsg, UntrustedNodeAddress};
use script_traits::{DocumentState, MsDuration, ScriptToCompositorMsg, TimerEvent, TimerEventId};
use script_traits::{MozBrowserEvent, ScriptMsg as ConstellationMsg, TimerEventRequest, TimerSource};
use script_traits::{ScriptMsg as ConstellationMsg, TimerEventRequest, TimerSource};
use std::ascii::AsciiExt;
use std::borrow::ToOwned;
use std::cell::Cell;
Expand All @@ -85,6 +85,8 @@ use task_source::networking::NetworkingTaskSource;
use task_source::user_interaction::UserInteractionTaskSource;
use time;
use timers::{IsInterval, OneshotTimerCallback, OneshotTimerHandle, OneshotTimers, TimerCallback};
#[cfg(any(target_os = "macos", target_os = "linux"))]
use tinyfiledialogs::{self, MessageBoxIcon};
use url::Url;
use util::geometry::{self, MAX_RECT};
use util::str::{DOMString, HTML_SPACE_CHARACTERS};
Expand Down Expand Up @@ -343,6 +345,16 @@ impl Window {
}
}

#[cfg(any(target_os = "macos", target_os = "linux"))]
fn display_alert_dialog(message: &str) {
tinyfiledialogs::message_box_ok("Alert!", message, MessageBoxIcon::Warning);
}

#[cfg(not(any(target_os = "macos", target_os = "linux")))]
fn display_alert_dialog(_message: &str) {
// tinyfiledialogs not supported on Windows
}

// https://html.spec.whatwg.org/multipage/#atob
pub fn base64_btoa(input: DOMString) -> Fallible<DOMString> {
// "The btoa() method must throw an InvalidCharacterError exception if
Expand Down Expand Up @@ -425,10 +437,13 @@ impl WindowMethods for Window {
stdout.flush().unwrap();
stderr.flush().unwrap();

// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowsershowmodalprompt
let event = MozBrowserEvent::ShowModalPrompt("alert".to_owned(), "Alert".to_owned(),
String::from(s), "".to_owned());
self.Document().trigger_mozbrowser_event(event);
let (sender, receiver) = ipc::channel().unwrap();
self.constellation_chan().0.send(ConstellationMsg::Alert(self.pipeline(), s.to_string(), sender)).unwrap();

let should_display_alert_dialog = receiver.recv().unwrap();
if should_display_alert_dialog {
display_alert_dialog(&s);
}
}

// https://html.spec.whatwg.org/multipage/#dom-window-close
Expand Down
2 changes: 2 additions & 0 deletions components/script/lib.rs
Expand Up @@ -73,6 +73,8 @@ extern crate smallvec;
#[macro_use]
extern crate style;
extern crate time;
#[cfg(any(target_os = "macos", target_os = "linux"))]
extern crate tinyfiledialogs;
extern crate unicase;
extern crate url;
#[macro_use]
Expand Down
2 changes: 2 additions & 0 deletions components/script_traits/script_msg.rs
Expand Up @@ -81,4 +81,6 @@ pub enum ScriptMsg {
SetDocumentState(PipelineId, DocumentState),
/// Update the pipeline Url, which can change after redirections.
SetFinalUrl(PipelineId, Url),
/// Check if an alert dialog box should be presented
Alert(PipelineId, String, IpcSender<bool>),
}
3 changes: 2 additions & 1 deletion components/servo/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion ports/cef/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion ports/gonk/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Expand Up @@ -16,4 +16,16 @@
}));
document.body.appendChild(iframe);
});

async_test(function(t) {
var iframe = document.createElement("iframe");
iframe.mozbrowser = "true";
iframe.src = "mozbrowsershowmodalprompt_event_nested_iframe.html";
iframe.addEventListener("mozbrowsershowmodalprompt", t.step_func(e => {
assert_equals(e.detail.promptType, "alert");
assert_equals(e.detail.message, "my alert message");
t.done();
}));
document.body.appendChild(iframe);
}, "mozbrowsershowmodalprompt event from nested iframes triggering an alert");
</script>
@@ -0,0 +1,5 @@
<html>
<body>
<iframe src="mozbrowsershowmodalprompt_event_iframe.html" />
</body>
</html>

0 comments on commit dc85be4

Please sign in to comment.