diff options
author | bors-servo <lbergstrom+bors@mozilla.com> | 2019-03-24 05:31:24 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-03-24 05:31:24 -0400 |
commit | 5f4030d028f5f5cceb67caba608f4de4031df780 (patch) | |
tree | ccc5d0b3df0050db50f2677686493c313e5ab3ef /components/script/dom/windowproxy.rs | |
parent | fb1123495fa2f3bd2b1808648ef5e1f9659b207f (diff) | |
parent | f261799e64feb2a9d75af63abd217938d7c3995d (diff) | |
download | servo-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.rs | 116 |
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 |