mirror of
https://github.com/servo/servo.git
synced 2025-08-05 13:40:08 +01:00
Auto merge of #12444 - servo:fetch-script, r=jdm
Improve the readability of the script fetching code. <!-- 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/12444) <!-- Reviewable:end -->
This commit is contained in:
commit
86b2104f11
3 changed files with 108 additions and 69 deletions
|
@ -5,7 +5,6 @@
|
||||||
use document_loader::LoadType;
|
use document_loader::LoadType;
|
||||||
use dom::attr::Attr;
|
use dom::attr::Attr;
|
||||||
use dom::bindings::cell::DOMRefCell;
|
use dom::bindings::cell::DOMRefCell;
|
||||||
use dom::bindings::codegen::Bindings::AttrBinding::AttrMethods;
|
|
||||||
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
|
use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods;
|
||||||
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding;
|
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding;
|
||||||
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
|
use dom::bindings::codegen::Bindings::HTMLScriptElementBinding::HTMLScriptElementMethods;
|
||||||
|
@ -35,7 +34,6 @@ use net_traits::{AsyncResponseListener, AsyncResponseTarget, Metadata, NetworkEr
|
||||||
use network_listener::{NetworkListener, PreInvoke};
|
use network_listener::{NetworkListener, PreInvoke};
|
||||||
use std::ascii::AsciiExt;
|
use std::ascii::AsciiExt;
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::mem;
|
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use string_cache::Atom;
|
use string_cache::Atom;
|
||||||
use style::str::{HTML_SPACE_CHARACTERS, StaticStringVec};
|
use style::str::{HTML_SPACE_CHARACTERS, StaticStringVec};
|
||||||
|
@ -64,10 +62,6 @@ pub struct HTMLScriptElement {
|
||||||
|
|
||||||
/// The source this script was loaded from
|
/// The source this script was loaded from
|
||||||
load: DOMRefCell<Option<ScriptOrigin>>,
|
load: DOMRefCell<Option<ScriptOrigin>>,
|
||||||
|
|
||||||
#[ignore_heap_size_of = "Defined in rust-encoding"]
|
|
||||||
/// https://html.spec.whatwg.org/multipage/#concept-script-encoding
|
|
||||||
block_character_encoding: Cell<Option<EncodingRef>>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl HTMLScriptElement {
|
impl HTMLScriptElement {
|
||||||
|
@ -82,7 +76,6 @@ impl HTMLScriptElement {
|
||||||
ready_to_be_parser_executed: Cell::new(false),
|
ready_to_be_parser_executed: Cell::new(false),
|
||||||
parser_document: JS::from_ref(document),
|
parser_document: JS::from_ref(document),
|
||||||
load: DOMRefCell::new(None),
|
load: DOMRefCell::new(None),
|
||||||
block_character_encoding: Cell::new(None),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,13 +113,16 @@ static SCRIPT_JS_MIMES: StaticStringVec = &[
|
||||||
#[derive(HeapSizeOf, JSTraceable)]
|
#[derive(HeapSizeOf, JSTraceable)]
|
||||||
pub enum ScriptOrigin {
|
pub enum ScriptOrigin {
|
||||||
Internal(DOMString, Url),
|
Internal(DOMString, Url),
|
||||||
External(Result<(Metadata, Vec<u8>), NetworkError>),
|
External(Result<(String, Url), NetworkError>),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The context required for asynchronously loading an external script source.
|
/// The context required for asynchronously loading an external script source.
|
||||||
struct ScriptContext {
|
struct ScriptContext {
|
||||||
/// The element that initiated the request.
|
/// The element that initiated the request.
|
||||||
elem: Trusted<HTMLScriptElement>,
|
elem: Trusted<HTMLScriptElement>,
|
||||||
|
/// The (fallback) character encoding argument to the "fetch a classic
|
||||||
|
/// script" algorithm.
|
||||||
|
character_encoding: EncodingRef,
|
||||||
/// The response body received to date.
|
/// The response body received to date.
|
||||||
data: Vec<u8>,
|
data: Vec<u8>,
|
||||||
/// The response metadata received to date.
|
/// The response metadata received to date.
|
||||||
|
@ -162,14 +158,27 @@ impl AsyncResponseListener for ScriptContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// https://html.spec.whatwg.org/multipage/#fetch-a-classic-script
|
||||||
|
/// step 4-9
|
||||||
fn response_complete(&mut self, status: Result<(), NetworkError>) {
|
fn response_complete(&mut self, status: Result<(), NetworkError>) {
|
||||||
|
// Step 5.
|
||||||
let load = status.and(self.status.clone()).map(|_| {
|
let load = status.and(self.status.clone()).map(|_| {
|
||||||
let data = mem::replace(&mut self.data, vec!());
|
|
||||||
let metadata = self.metadata.take().unwrap();
|
let metadata = self.metadata.take().unwrap();
|
||||||
(metadata, data)
|
|
||||||
|
// Step 6.
|
||||||
|
let encoding = metadata.charset
|
||||||
|
.and_then(|encoding| encoding_from_whatwg_label(&encoding))
|
||||||
|
.unwrap_or(self.character_encoding);
|
||||||
|
|
||||||
|
// Step 7.
|
||||||
|
let source_text = encoding.decode(&self.data, DecoderTrap::Replace).unwrap();
|
||||||
|
(source_text, metadata.final_url)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Step 9.
|
||||||
|
// https://html.spec.whatwg.org/multipage/#prepare-a-script
|
||||||
|
// Step 18.6 (When the chosen algorithm asynchronously completes).
|
||||||
let elem = self.elem.root();
|
let elem = self.elem.root();
|
||||||
// TODO: maybe set this to None again after script execution to save memory.
|
|
||||||
*elem.load.borrow_mut() = Some(ScriptOrigin::External(load));
|
*elem.load.borrow_mut() = Some(ScriptOrigin::External(load));
|
||||||
elem.ready_to_be_parser_executed.set(true);
|
elem.ready_to_be_parser_executed.set(true);
|
||||||
|
|
||||||
|
@ -180,6 +189,38 @@ impl AsyncResponseListener for ScriptContext {
|
||||||
|
|
||||||
impl PreInvoke for ScriptContext {}
|
impl PreInvoke for ScriptContext {}
|
||||||
|
|
||||||
|
/// https://html.spec.whatwg.org/multipage/#fetch-a-classic-script
|
||||||
|
fn fetch_a_classic_script(script: &HTMLScriptElement,
|
||||||
|
url: Url,
|
||||||
|
character_encoding: EncodingRef) {
|
||||||
|
// TODO(#9186): use the fetch infrastructure.
|
||||||
|
let context = Arc::new(Mutex::new(ScriptContext {
|
||||||
|
elem: Trusted::new(script),
|
||||||
|
character_encoding: character_encoding,
|
||||||
|
data: vec!(),
|
||||||
|
metadata: None,
|
||||||
|
url: url.clone(),
|
||||||
|
status: Ok(())
|
||||||
|
}));
|
||||||
|
|
||||||
|
let doc = document_from_node(script);
|
||||||
|
|
||||||
|
let (action_sender, action_receiver) = ipc::channel().unwrap();
|
||||||
|
let listener = NetworkListener {
|
||||||
|
context: context,
|
||||||
|
script_chan: doc.window().networking_task_source(),
|
||||||
|
wrapper: Some(doc.window().get_runnable_wrapper()),
|
||||||
|
};
|
||||||
|
let response_target = AsyncResponseTarget {
|
||||||
|
sender: action_sender,
|
||||||
|
};
|
||||||
|
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
|
||||||
|
listener.notify_action(message.to().unwrap());
|
||||||
|
});
|
||||||
|
|
||||||
|
doc.load_async(LoadType::Script(url), response_target);
|
||||||
|
}
|
||||||
|
|
||||||
impl HTMLScriptElement {
|
impl HTMLScriptElement {
|
||||||
/// https://html.spec.whatwg.org/multipage/#prepare-a-script
|
/// https://html.spec.whatwg.org/multipage/#prepare-a-script
|
||||||
pub fn prepare(&self) -> NextParserState {
|
pub fn prepare(&self) -> NextParserState {
|
||||||
|
@ -227,13 +268,12 @@ impl HTMLScriptElement {
|
||||||
|
|
||||||
// Step 9.
|
// Step 9.
|
||||||
let doc = document_from_node(self);
|
let doc = document_from_node(self);
|
||||||
let document_from_node_ref = doc.r();
|
if self.parser_inserted.get() && &*self.parser_document != &*doc {
|
||||||
if self.parser_inserted.get() && &*self.parser_document != document_from_node_ref {
|
|
||||||
return NextParserState::Continue;
|
return NextParserState::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 10.
|
// Step 10.
|
||||||
if !document_from_node_ref.is_scripting_enabled() {
|
if !doc.is_scripting_enabled() {
|
||||||
return NextParserState::Continue;
|
return NextParserState::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -260,30 +300,32 @@ impl HTMLScriptElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 13.
|
// Step 13.
|
||||||
if let Some(ref charset) = element.get_attribute(&ns!(), &atom!("charset")) {
|
let encoding = element.get_attribute(&ns!(), &atom!("charset"))
|
||||||
if let Some(encodingRef) = encoding_from_whatwg_label(&charset.Value()) {
|
.and_then(|charset| encoding_from_whatwg_label(&charset.value()))
|
||||||
self.block_character_encoding.set(Some(encodingRef));
|
.unwrap_or_else(|| doc.encoding());
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Step 14: CORS.
|
// TODO: Step 14: CORS.
|
||||||
|
|
||||||
// TODO: Step 15: environment settings object.
|
// TODO: Step 15: Nonce.
|
||||||
|
|
||||||
|
// TODO: Step 16: Parser state.
|
||||||
|
|
||||||
|
// TODO: Step 17: environment settings object.
|
||||||
|
|
||||||
let base_url = doc.base_url();
|
let base_url = doc.base_url();
|
||||||
let is_external = match element.get_attribute(&ns!(), &atom!("src")) {
|
let is_external = match element.get_attribute(&ns!(), &atom!("src")) {
|
||||||
// Step 16.
|
// Step 18.
|
||||||
Some(ref src) => {
|
Some(ref src) => {
|
||||||
// Step 16.1.
|
// Step 18.1.
|
||||||
let src = src.value();
|
let src = src.value();
|
||||||
|
|
||||||
// Step 16.2.
|
// Step 18.2.
|
||||||
if src.is_empty() {
|
if src.is_empty() {
|
||||||
self.queue_error_event();
|
self.queue_error_event();
|
||||||
return NextParserState::Continue;
|
return NextParserState::Continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 16.4-16.5.
|
// Step 18.4-18.5.
|
||||||
let url = match base_url.join(&src) {
|
let url = match base_url.join(&src) {
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
error!("error parsing URL for script {}", &**src);
|
error!("error parsing URL for script {}", &**src);
|
||||||
|
@ -293,40 +335,16 @@ impl HTMLScriptElement {
|
||||||
Ok(url) => url,
|
Ok(url) => url,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 16.6.
|
// Step 18.6.
|
||||||
// TODO(#9186): use the fetch infrastructure.
|
fetch_a_classic_script(self, url, encoding);
|
||||||
let elem = Trusted::new(self);
|
|
||||||
|
|
||||||
let context = Arc::new(Mutex::new(ScriptContext {
|
|
||||||
elem: elem,
|
|
||||||
data: vec!(),
|
|
||||||
metadata: None,
|
|
||||||
url: url.clone(),
|
|
||||||
status: Ok(())
|
|
||||||
}));
|
|
||||||
|
|
||||||
let (action_sender, action_receiver) = ipc::channel().unwrap();
|
|
||||||
let listener = NetworkListener {
|
|
||||||
context: context,
|
|
||||||
script_chan: doc.window().networking_task_source(),
|
|
||||||
wrapper: Some(doc.window().get_runnable_wrapper()),
|
|
||||||
};
|
|
||||||
let response_target = AsyncResponseTarget {
|
|
||||||
sender: action_sender,
|
|
||||||
};
|
|
||||||
ROUTER.add_route(action_receiver.to_opaque(), box move |message| {
|
|
||||||
listener.notify_action(message.to().unwrap());
|
|
||||||
});
|
|
||||||
|
|
||||||
doc.load_async(LoadType::Script(url), response_target);
|
|
||||||
true
|
true
|
||||||
},
|
},
|
||||||
None => false,
|
None => false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Step 18.
|
// Step 20.
|
||||||
let deferred = element.has_attribute(&atom!("defer"));
|
let deferred = element.has_attribute(&atom!("defer"));
|
||||||
// Step 18.a: has src, has defer, was parser-inserted, is not async.
|
// Step 20.a: classic, has src, has defer, was parser-inserted, is not async.
|
||||||
if is_external &&
|
if is_external &&
|
||||||
deferred &&
|
deferred &&
|
||||||
was_parser_inserted &&
|
was_parser_inserted &&
|
||||||
|
@ -334,23 +352,23 @@ impl HTMLScriptElement {
|
||||||
doc.add_deferred_script(self);
|
doc.add_deferred_script(self);
|
||||||
// Second part implemented in Document::process_deferred_scripts.
|
// Second part implemented in Document::process_deferred_scripts.
|
||||||
return NextParserState::Continue;
|
return NextParserState::Continue;
|
||||||
// Step 18.b: has src, was parser-inserted, is not async.
|
// Step 20.b: classic, has src, was parser-inserted, is not async.
|
||||||
} else if is_external &&
|
} else if is_external &&
|
||||||
was_parser_inserted &&
|
was_parser_inserted &&
|
||||||
!async {
|
!async {
|
||||||
doc.set_pending_parsing_blocking_script(Some(self));
|
doc.set_pending_parsing_blocking_script(Some(self));
|
||||||
// Second part implemented in the load result handler.
|
// Second part implemented in the load result handler.
|
||||||
// Step 18.c: has src, isn't async, isn't non-blocking.
|
// Step 20.c: classic, has src, isn't async, isn't non-blocking.
|
||||||
} else if is_external &&
|
} else if is_external &&
|
||||||
!async &&
|
!async &&
|
||||||
!self.non_blocking.get() {
|
!self.non_blocking.get() {
|
||||||
doc.push_asap_in_order_script(self);
|
doc.push_asap_in_order_script(self);
|
||||||
// Second part implemented in Document::process_asap_scripts.
|
// Second part implemented in Document::process_asap_scripts.
|
||||||
// Step 18.d: has src.
|
// Step 20.d: classic, has src.
|
||||||
} else if is_external {
|
} else if is_external {
|
||||||
doc.add_asap_script(self);
|
doc.add_asap_script(self);
|
||||||
// Second part implemented in Document::process_asap_scripts.
|
// Second part implemented in Document::process_asap_scripts.
|
||||||
// Step 18.e: doesn't have src, was parser-inserted, is blocked on stylesheet.
|
// Step 20.e: doesn't have src, was parser-inserted, is blocked on stylesheet.
|
||||||
} else if !is_external &&
|
} else if !is_external &&
|
||||||
was_parser_inserted &&
|
was_parser_inserted &&
|
||||||
// TODO: check for script nesting levels.
|
// TODO: check for script nesting levels.
|
||||||
|
@ -358,7 +376,7 @@ impl HTMLScriptElement {
|
||||||
doc.set_pending_parsing_blocking_script(Some(self));
|
doc.set_pending_parsing_blocking_script(Some(self));
|
||||||
*self.load.borrow_mut() = Some(ScriptOrigin::Internal(text, base_url));
|
*self.load.borrow_mut() = Some(ScriptOrigin::Internal(text, base_url));
|
||||||
self.ready_to_be_parser_executed.set(true);
|
self.ready_to_be_parser_executed.set(true);
|
||||||
// Step 18.f: otherwise.
|
// Step 20.f: otherwise.
|
||||||
} else {
|
} else {
|
||||||
assert!(!text.is_empty());
|
assert!(!text.is_empty());
|
||||||
self.ready_to_be_parser_executed.set(true);
|
self.ready_to_be_parser_executed.set(true);
|
||||||
|
@ -402,17 +420,9 @@ impl HTMLScriptElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Step 2.b.1.a.
|
// Step 2.b.1.a.
|
||||||
ScriptOrigin::External(Ok((metadata, bytes))) => {
|
ScriptOrigin::External(Ok((text, url))) => {
|
||||||
debug!("loading external script, url = {}", metadata.final_url);
|
debug!("loading external script, url = {}", url);
|
||||||
|
(DOMString::from(text), true, url)
|
||||||
let encoding = metadata.charset
|
|
||||||
.and_then(|encoding| encoding_from_whatwg_label(&encoding))
|
|
||||||
.or_else(|| self.block_character_encoding.get())
|
|
||||||
.unwrap_or_else(|| self.parser_document.encoding());
|
|
||||||
|
|
||||||
(DOMString::from(encoding.decode(&*bytes, DecoderTrap::Replace).unwrap()),
|
|
||||||
true,
|
|
||||||
metadata.final_url)
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Step 2.b.1.c.
|
// Step 2.b.1.c.
|
||||||
|
|
|
@ -37203,7 +37203,16 @@
|
||||||
"local_changes": {
|
"local_changes": {
|
||||||
"deleted": [],
|
"deleted": [],
|
||||||
"deleted_reftests": {},
|
"deleted_reftests": {},
|
||||||
"items": {},
|
"items": {
|
||||||
|
"testharness": {
|
||||||
|
"html/semantics/scripting-1/the-script-element/script-charset-03.html": [
|
||||||
|
{
|
||||||
|
"path": "html/semantics/scripting-1/the-script-element/script-charset-03.html",
|
||||||
|
"url": "/html/semantics/scripting-1/the-script-element/script-charset-03.html"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
"reftest_nodes": {}
|
"reftest_nodes": {}
|
||||||
},
|
},
|
||||||
"reftest_nodes": {
|
"reftest_nodes": {
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Script changing @charset</title>
|
||||||
|
<link rel="help" href="https://html.spec.whatwg.org/multipage/#scriptingLanguages">
|
||||||
|
<script src="/resources/testharness.js"></script>
|
||||||
|
<script src="/resources/testharnessreport.js"></script>
|
||||||
|
<div id="log"></div>
|
||||||
|
<script>
|
||||||
|
async_test(function() {
|
||||||
|
var s = document.createElement("script");
|
||||||
|
s.src = "external-script-windows1250.js";
|
||||||
|
s.charset = "windows-1250";
|
||||||
|
document.body.appendChild(s);
|
||||||
|
s.charset = "utf-8";
|
||||||
|
window.onload = this.step_func_done(function() {
|
||||||
|
assert_equals(window.getSomeString(), "\u015b\u0107\u0105\u017c\u017a");
|
||||||
|
});
|
||||||
|
})
|
||||||
|
</script>
|
Loading…
Add table
Add a link
Reference in a new issue