aboutsummaryrefslogtreecommitdiffstats
path: root/components/script/dom/windowproxy.rs
diff options
context:
space:
mode:
authorbors-servo <lbergstrom+bors@mozilla.com>2019-03-24 05:31:24 -0400
committerGitHub <noreply@github.com>2019-03-24 05:31:24 -0400
commit5f4030d028f5f5cceb67caba608f4de4031df780 (patch)
treeccc5d0b3df0050db50f2677686493c313e5ab3ef /components/script/dom/windowproxy.rs
parentfb1123495fa2f3bd2b1808648ef5e1f9659b207f (diff)
parentf261799e64feb2a9d75af63abd217938d7c3995d (diff)
downloadservo-5f4030d028f5f5cceb67caba608f4de4031df780.tar.gz
servo-5f4030d028f5f5cceb67caba608f4de4031df780.zip
Auto merge of #23011 - dboyan:open-feature-tokenize, r=gterzian,cybai
Add tokenizer for features in window.open <!-- Please describe your changes on the following line: --> This is a prototype implementation of feature tokenizer ~~and "noreferrer" feature~~ for window.open. I'm not very sure where the tokenizer code should be placed. Building now generates the following warning ``` warning: unused attribute --> components/script/dom/bindings/conversions.rs:74:5 | 74 | rustc_on_unimplemented(message = "The IDL interface `{Self}` is not derived from `{T}`.") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: #[warn(unused_attributes)] on by default ``` --- <!-- Thank you for contributing to Servo! Please replace each `[ ]` by `[X]` when the step is complete, and replace `___` with appropriate data: --> - [X] `./mach build -d` does not report any errors - [X] `./mach test-tidy` does not report any errors - [X] These changes _partially_ fix #22869 (GitHub issue number if applicable) <!-- Either: --> - [X] There are tests for these changes OR - [ ] These changes do not require tests because ___ <!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.--> <!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. --> <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/23011) <!-- Reviewable:end -->
Diffstat (limited to 'components/script/dom/windowproxy.rs')
-rw-r--r--components/script/dom/windowproxy.rs116
1 files changed, 106 insertions, 10 deletions
diff --git a/components/script/dom/windowproxy.rs b/components/script/dom/windowproxy.rs
index d7c1f9da73d..8a78b661a76 100644
--- a/components/script/dom/windowproxy.rs
+++ b/components/script/dom/windowproxy.rs
@@ -20,6 +20,7 @@ use crate::dom::window::Window;
use crate::script_thread::ScriptThread;
use dom_struct::dom_struct;
use embedder_traits::EmbedderMsg;
+use indexmap::map::IndexMap;
use ipc_channel::ipc;
use js::glue::{CreateWrapperProxyHandler, ProxyTraps};
use js::glue::{GetProxyPrivate, GetProxyReservedSlot, SetProxyReservedSlot};
@@ -48,6 +49,7 @@ use script_traits::{AuxiliaryBrowsingContextLoadInfo, LoadData, NewLayoutInfo, S
use servo_url::ServoUrl;
use std::cell::Cell;
use std::ptr;
+use style::attr::parse_integer;
#[dom_struct]
// NOTE: the browsing context for a window is managed in two places:
@@ -388,26 +390,32 @@ impl WindowProxy {
target: DOMString,
features: DOMString,
) -> Option<DomRoot<WindowProxy>> {
- // Step 3.
+ // Step 4.
let non_empty_target = match target.as_ref() {
"" => DOMString::from("_blank"),
_ => target,
};
- // TODO Step 4, properly tokenize features.
// Step 5
- let noopener = features.contains("noopener");
- // Step 6, 7
+ let tokenized_features = tokenize_open_features(features);
+ // Step 7-9
+ let noreferrer = parse_open_feature_boolean(&tokenized_features, "noreferrer");
+ let noopener = if noreferrer {
+ true
+ } else {
+ parse_open_feature_boolean(&tokenized_features, "noopener")
+ };
+ // Step 10, 11
let (chosen, new) = match self.choose_browsing_context(non_empty_target, noopener) {
(Some(chosen), new) => (chosen, new),
(None, _) => return None,
};
- // TODO Step 8, set up browsing context features.
+ // TODO Step 12, set up browsing context features.
let target_document = match chosen.document() {
Some(target_document) => target_document,
None => return None,
};
let target_window = target_document.window();
- // Step 9, and 10.2, will have happened elsewhere,
+ // Step 13, and 14.4, will have happened elsewhere,
// since we've created a new browsing context and loaded it with about:blank.
if !url.is_empty() {
let existing_document = self
@@ -415,19 +423,20 @@ impl WindowProxy {
.get()
.and_then(|id| ScriptThread::find_document(id))
.unwrap();
- // Step 10.1
+ // Step 14.1
let url = match existing_document.url().join(&url) {
Ok(url) => url,
Err(_) => return None, // TODO: throw a "SyntaxError" DOMException.
};
- // Step 10.3
+ // TODO Step 14.3, handle noreferrer flag
+ // Step 14.5
target_window.load_url(url, new, false, target_document.get_referrer_policy());
}
if noopener {
- // Step 11 (Dis-owning has been done in create_auxiliary_browsing_context).
+ // Step 15 (Dis-owning has been done in create_auxiliary_browsing_context).
return None;
}
- // Step 12.
+ // Step 17.
return target_document.browsing_context();
}
@@ -590,6 +599,93 @@ impl WindowProxy {
}
}
+// https://html.spec.whatwg.org/multipage/#concept-window-open-features-tokenize
+fn tokenize_open_features(features: DOMString) -> IndexMap<String, String> {
+ let is_feature_sep = |c: char| c.is_ascii_whitespace() || ['=', ','].contains(&c);
+ // Step 1
+ let mut tokenized_features = IndexMap::new();
+ // Step 2
+ let mut iter = features.chars();
+ let mut cur = iter.next();
+
+ // Step 3
+ while cur != None {
+ // Step 3.1 & 3.2
+ let mut name = String::new();
+ let mut value = String::new();
+ // Step 3.3
+ while let Some(cur_char) = cur {
+ if !is_feature_sep(cur_char) {
+ break;
+ }
+ cur = iter.next();
+ }
+ // Step 3.4
+ while let Some(cur_char) = cur {
+ if is_feature_sep(cur_char) {
+ break;
+ }
+ name.push(cur_char.to_ascii_lowercase());
+ cur = iter.next();
+ }
+ // Step 3.5
+ let normalized_name = String::from(match name.as_ref() {
+ "screenx" => "left",
+ "screeny" => "top",
+ "innerwidth" => "width",
+ "innerheight" => "height",
+ _ => name.as_ref(),
+ });
+ // Step 3.6
+ while let Some(cur_char) = cur {
+ if cur_char == '=' || cur_char == ',' || !is_feature_sep(cur_char) {
+ break;
+ }
+ cur = iter.next();
+ }
+ // Step 3.7
+ if cur.is_some() && is_feature_sep(cur.unwrap()) {
+ // Step 3.7.1
+ while let Some(cur_char) = cur {
+ if !is_feature_sep(cur_char) || cur_char == ',' {
+ break;
+ }
+ cur = iter.next();
+ }
+ // Step 3.7.2
+ while let Some(cur_char) = cur {
+ if is_feature_sep(cur_char) {
+ break;
+ }
+ value.push(cur_char.to_ascii_lowercase());
+ cur = iter.next();
+ }
+ }
+ // Step 3.8
+ if !name.is_empty() {
+ tokenized_features.insert(normalized_name, value);
+ }
+ }
+ // Step 4
+ tokenized_features
+}
+
+// https://html.spec.whatwg.org/multipage/#concept-window-open-features-parse-boolean
+fn parse_open_feature_boolean(tokenized_features: &IndexMap<String, String>, name: &str) -> bool {
+ if let Some(value) = tokenized_features.get(name) {
+ // Step 1 & 2
+ if value == "" || value == "yes" {
+ return true;
+ }
+ // Step 3 & 4
+ if let Ok(int) = parse_integer(value.chars()) {
+ return int != 0;
+ }
+ }
+ // Step 5
+ return false;
+}
+
// This is only called from extern functions,
// there's no use using the lifetimed handles here.
// https://html.spec.whatwg.org/multipage/#accessing-other-browsing-contexts