mirror of
https://github.com/servo/servo.git
synced 2025-07-22 23:03:42 +01:00
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 -->
This commit is contained in:
commit
5f4030d028
3 changed files with 106 additions and 48 deletions
|
@ -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
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
[open-features-tokenization-noopener.html]
|
||||
[feature `name` should be converted to ASCII lowercase]
|
||||
expected: FAIL
|
||||
|
||||
[invalid feature names should not tokenize as "noopener"]
|
||||
expected: FAIL
|
||||
|
||||
[Integer value of 0 should not activate the feature]
|
||||
expected: FAIL
|
||||
|
||||
[Invalid feature names should not tokenize as "noopener"]
|
||||
expected: FAIL
|
||||
|
||||
[Integer value of 0 should not activate "noopener"]
|
||||
expected: FAIL
|
||||
|
||||
[Feature "noopener" should be converted to ASCII lowercase]
|
||||
expected: FAIL
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
[open-features-tokenization-noreferrer.html]
|
||||
[Tokenizing "noreferrer" should ignore window feature separators except "," after initial "=" and before value]
|
||||
expected: FAIL
|
||||
|
||||
[Tokenizing "noreferrer" should read characters until first window feature separator as `value`]
|
||||
expected: FAIL
|
||||
|
||||
[After "noreferrer", tokenization should skip window features separators that are not "=" or ","]
|
||||
expected: FAIL
|
||||
|
||||
[Integer values other than 0 should activate the feature]
|
||||
expected: FAIL
|
||||
|
||||
[Tokenization of "noreferrer" should skip window features separators before feature]
|
||||
expected: FAIL
|
||||
|
||||
[Feature "noreferrer" should be converted to ASCII lowercase]
|
||||
expected: FAIL
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue