mirror of
https://github.com/servo/servo.git
synced 2025-07-16 03:43:38 +01:00
Auto merge of #14361 - nox:write, r=jdm
Implement document.write (fixes #3704) <!-- 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/14361) <!-- Reviewable:end -->
This commit is contained in:
commit
00f30d1b7e
61 changed files with 369 additions and 363 deletions
|
@ -142,12 +142,6 @@ pub enum IsHTMLDocument {
|
|||
NonHTMLDocument,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
enum ParserBlockedByScript {
|
||||
Blocked,
|
||||
Unblocked,
|
||||
}
|
||||
|
||||
#[derive(JSTraceable, HeapSizeOf)]
|
||||
#[must_root]
|
||||
pub struct StylesheetInDocument {
|
||||
|
@ -287,6 +281,8 @@ pub struct Document {
|
|||
/// https://w3c.github.io/uievents/#event-type-dblclick
|
||||
#[ignore_heap_size_of = "Defined in std"]
|
||||
last_click_info: DOMRefCell<Option<(Instant, Point2D<f32>)>>,
|
||||
/// https://html.spec.whatwg.org/multipage/#ignore-destructive-writes-counter
|
||||
ignore_destructive_writes_counter: Cell<u32>,
|
||||
}
|
||||
|
||||
#[derive(JSTraceable, HeapSizeOf)]
|
||||
|
@ -378,15 +374,16 @@ impl Document {
|
|||
self.trigger_mozbrowser_event(MozBrowserEvent::SecurityChange(https_state));
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#active-document
|
||||
pub fn is_active(&self) -> bool {
|
||||
self.browsing_context().map_or(false, |context| {
|
||||
self == &*context.active_document()
|
||||
})
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#fully-active
|
||||
pub fn is_fully_active(&self) -> bool {
|
||||
let browsing_context = match self.browsing_context() {
|
||||
Some(browsing_context) => browsing_context,
|
||||
None => return false,
|
||||
};
|
||||
let active_document = browsing_context.active_document();
|
||||
|
||||
if self != &*active_document {
|
||||
if !self.is_active() {
|
||||
return false;
|
||||
}
|
||||
// FIXME: It should also check whether the browser context is top-level or not
|
||||
|
@ -1545,15 +1542,13 @@ impl Document {
|
|||
self.process_asap_scripts();
|
||||
}
|
||||
|
||||
if self.maybe_execute_parser_blocking_script() == ParserBlockedByScript::Blocked {
|
||||
return;
|
||||
}
|
||||
|
||||
// A finished resource load can potentially unblock parsing. In that case, resume the
|
||||
// parser so its loop can find out.
|
||||
if let Some(parser) = self.get_current_parser() {
|
||||
if parser.is_suspended() {
|
||||
parser.resume();
|
||||
if let Some(script) = self.pending_parsing_blocking_script.get() {
|
||||
if self.script_blocking_stylesheets_count.get() > 0 || !script.is_ready_to_be_executed() {
|
||||
return;
|
||||
}
|
||||
self.pending_parsing_blocking_script.set(None);
|
||||
parser.resume_with_pending_parsing_blocking_script(&script);
|
||||
}
|
||||
} else if self.reflow_timeout.get().is_none() {
|
||||
// If we don't have a parser, and the reflow timer has been reset, explicitly
|
||||
|
@ -1576,23 +1571,6 @@ impl Document {
|
|||
}
|
||||
}
|
||||
|
||||
/// If document parsing is blocked on a script, and that script is ready to run,
|
||||
/// execute it.
|
||||
/// https://html.spec.whatwg.org/multipage/#ready-to-be-parser-executed
|
||||
fn maybe_execute_parser_blocking_script(&self) -> ParserBlockedByScript {
|
||||
let script = match self.pending_parsing_blocking_script.get() {
|
||||
None => return ParserBlockedByScript::Unblocked,
|
||||
Some(script) => script,
|
||||
};
|
||||
|
||||
if self.script_blocking_stylesheets_count.get() == 0 && script.is_ready_to_be_executed() {
|
||||
self.pending_parsing_blocking_script.set(None);
|
||||
script.execute();
|
||||
return ParserBlockedByScript::Unblocked;
|
||||
}
|
||||
ParserBlockedByScript::Blocked
|
||||
}
|
||||
|
||||
/// https://html.spec.whatwg.org/multipage/#the-end step 3
|
||||
pub fn process_deferred_scripts(&self) {
|
||||
if self.ready_state.get() != DocumentReadyState::Interactive {
|
||||
|
@ -1901,6 +1879,7 @@ impl Document {
|
|||
referrer_policy: Cell::new(referrer_policy),
|
||||
target_element: MutNullableHeap::new(None),
|
||||
last_click_info: DOMRefCell::new(None),
|
||||
ignore_destructive_writes_counter: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2077,6 +2056,16 @@ impl Document {
|
|||
ReflowQueryType::NoQuery,
|
||||
ReflowReason::ElementStateChanged);
|
||||
}
|
||||
|
||||
pub fn incr_ignore_destructive_writes_counter(&self) {
|
||||
self.ignore_destructive_writes_counter.set(
|
||||
self.ignore_destructive_writes_counter.get() + 1);
|
||||
}
|
||||
|
||||
pub fn decr_ignore_destructive_writes_counter(&self) {
|
||||
self.ignore_destructive_writes_counter.set(
|
||||
self.ignore_destructive_writes_counter.get() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -3043,6 +3032,55 @@ impl DocumentMethods for Document {
|
|||
elements
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-document-write
|
||||
fn Write(&self, text: Vec<DOMString>) -> ErrorResult {
|
||||
if !self.is_html_document() {
|
||||
// Step 1.
|
||||
return Err(Error::InvalidState);
|
||||
}
|
||||
|
||||
// Step 2.
|
||||
// TODO: handle throw-on-dynamic-markup-insertion counter.
|
||||
|
||||
if !self.is_active() {
|
||||
// Step 3.
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let parser = self.get_current_parser();
|
||||
let parser = match parser.as_ref() {
|
||||
Some(parser) if parser.script_nesting_level() > 0 => parser,
|
||||
_ => {
|
||||
// Either there is no parser, which means the parsing ended;
|
||||
// or script nesting level is 0, which means the method was
|
||||
// called from outside a parser-executed script.
|
||||
if self.ignore_destructive_writes_counter.get() > 0 {
|
||||
// Step 4.
|
||||
// TODO: handle ignore-opens-during-unload counter.
|
||||
return Ok(());
|
||||
}
|
||||
// Step 5.
|
||||
// TODO: call document.open().
|
||||
return Err(Error::InvalidState);
|
||||
}
|
||||
};
|
||||
|
||||
// Step 7.
|
||||
// TODO: handle reload override buffer.
|
||||
|
||||
// Steps 6-8.
|
||||
parser.write(text);
|
||||
|
||||
// Step 9.
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#dom-document-writeln
|
||||
fn Writeln(&self, mut text: Vec<DOMString>) -> ErrorResult {
|
||||
text.push("\n".into());
|
||||
self.Write(text)
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/#documentandelementeventhandlers
|
||||
document_and_element_event_handlers!();
|
||||
}
|
||||
|
|
|
@ -274,12 +274,10 @@ fn fetch_a_classic_script(script: &HTMLScriptElement,
|
|||
|
||||
impl HTMLScriptElement {
|
||||
/// https://html.spec.whatwg.org/multipage/#prepare-a-script
|
||||
///
|
||||
/// Returns true if tokenization should continue, false otherwise.
|
||||
pub fn prepare(&self) -> bool {
|
||||
pub fn prepare(&self) {
|
||||
// Step 1.
|
||||
if self.already_started.get() {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 2.
|
||||
|
@ -297,17 +295,17 @@ impl HTMLScriptElement {
|
|||
// Step 4.
|
||||
let text = self.Text();
|
||||
if text.is_empty() && !element.has_attribute(&local_name!("src")) {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 5.
|
||||
if !self.upcast::<Node>().is_in_doc() {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 6.
|
||||
if !self.is_javascript() {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 7.
|
||||
|
@ -322,12 +320,12 @@ impl HTMLScriptElement {
|
|||
// Step 9.
|
||||
let doc = document_from_node(self);
|
||||
if self.parser_inserted.get() && &*self.parser_document != &*doc {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 10.
|
||||
if !doc.is_scripting_enabled() {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(#4577): Step 11: CSP.
|
||||
|
@ -340,13 +338,13 @@ impl HTMLScriptElement {
|
|||
let for_value = for_attribute.value().to_ascii_lowercase();
|
||||
let for_value = for_value.trim_matches(HTML_SPACE_CHARACTERS);
|
||||
if for_value != "window" {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
let event_value = event_attribute.value().to_ascii_lowercase();
|
||||
let event_value = event_value.trim_matches(HTML_SPACE_CHARACTERS);
|
||||
if event_value != "onload" && event_value != "onload()" {
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
},
|
||||
(_, _) => (),
|
||||
|
@ -381,7 +379,7 @@ impl HTMLScriptElement {
|
|||
// Step 18.2.
|
||||
if src.is_empty() {
|
||||
self.queue_error_event();
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 18.4-18.5.
|
||||
|
@ -389,7 +387,7 @@ impl HTMLScriptElement {
|
|||
Err(_) => {
|
||||
warn!("error parsing URL for script {}", &**src);
|
||||
self.queue_error_event();
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
Ok(url) => url,
|
||||
};
|
||||
|
@ -412,7 +410,6 @@ impl HTMLScriptElement {
|
|||
!async {
|
||||
doc.add_deferred_script(self);
|
||||
// Second part implemented in Document::process_deferred_scripts.
|
||||
return true;
|
||||
// Step 20.b: classic, has src, was parser-inserted, is not async.
|
||||
} else if is_external &&
|
||||
was_parser_inserted &&
|
||||
|
@ -432,7 +429,7 @@ impl HTMLScriptElement {
|
|||
// Step 20.e: doesn't have src, was parser-inserted, is blocked on stylesheet.
|
||||
} else if !is_external &&
|
||||
was_parser_inserted &&
|
||||
// TODO: check for script nesting levels.
|
||||
doc.get_current_parser().map_or(false, |parser| parser.script_nesting_level() <= 1) &&
|
||||
doc.get_script_blocking_stylesheets_count() > 0 {
|
||||
doc.set_pending_parsing_blocking_script(Some(self));
|
||||
*self.load.borrow_mut() = Some(Ok(ScriptOrigin::internal(text, base_url)));
|
||||
|
@ -443,16 +440,7 @@ impl HTMLScriptElement {
|
|||
self.ready_to_be_parser_executed.set(true);
|
||||
*self.load.borrow_mut() = Some(Ok(ScriptOrigin::internal(text, base_url)));
|
||||
self.execute();
|
||||
return true;
|
||||
}
|
||||
|
||||
// TODO: make this suspension happen automatically.
|
||||
if was_parser_inserted {
|
||||
if let Some(parser) = doc.get_current_parser() {
|
||||
parser.suspend();
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn is_ready_to_be_executed(&self) -> bool {
|
||||
|
@ -481,19 +469,20 @@ impl HTMLScriptElement {
|
|||
Ok(script) => script,
|
||||
};
|
||||
|
||||
if script.external {
|
||||
debug!("loading external script, url = {}", script.url);
|
||||
}
|
||||
|
||||
// TODO(#12446): beforescriptexecute.
|
||||
if self.dispatch_before_script_execute_event() == EventStatus::Canceled {
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 3.
|
||||
// TODO: If the script is from an external file, then increment the
|
||||
// ignore-destructive-writes counter of the script element's node
|
||||
// document. Let neutralised doc be that Document.
|
||||
let neutralized_doc = if script.external {
|
||||
debug!("loading external script, url = {}", script.url);
|
||||
let doc = document_from_node(self);
|
||||
doc.incr_ignore_destructive_writes_counter();
|
||||
Some(doc)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// Step 4.
|
||||
let document = document_from_node(self);
|
||||
|
@ -512,8 +501,9 @@ impl HTMLScriptElement {
|
|||
document.set_current_script(old_script.r());
|
||||
|
||||
// Step 7.
|
||||
// TODO: Decrement the ignore-destructive-writes counter of neutralised
|
||||
// doc, if it was incremented in the earlier step.
|
||||
if let Some(doc) = neutralized_doc {
|
||||
doc.decr_ignore_destructive_writes_counter();
|
||||
}
|
||||
|
||||
// TODO(#12446): afterscriptexecute.
|
||||
self.dispatch_after_script_execute_event();
|
||||
|
|
|
@ -33,12 +33,25 @@ use profile_traits::time::{TimerMetadataReflowType, ProfilerCategory, profile};
|
|||
use script_thread::ScriptThread;
|
||||
use servo_url::ServoUrl;
|
||||
use std::cell::Cell;
|
||||
use std::mem;
|
||||
use util::resource_files::read_resource_file;
|
||||
|
||||
mod html;
|
||||
mod xml;
|
||||
|
||||
#[dom_struct]
|
||||
/// The parser maintains two input streams: one for input from script through
|
||||
/// document.write(), and one for input from network.
|
||||
///
|
||||
/// There is no concrete representation of the insertion point, instead it
|
||||
/// always points to just before the next character from the network input,
|
||||
/// with all of the script input before itself.
|
||||
///
|
||||
/// ```text
|
||||
/// ... script input ... | ... network input ...
|
||||
/// ^
|
||||
/// insertion point
|
||||
/// ```
|
||||
pub struct ServoParser {
|
||||
reflector: Reflector,
|
||||
/// The document associated with this parser.
|
||||
|
@ -46,15 +59,20 @@ pub struct ServoParser {
|
|||
/// The pipeline associated with this parse, unavailable if this parse
|
||||
/// does not correspond to a page load.
|
||||
pipeline: Option<PipelineId>,
|
||||
/// Input chunks received but not yet passed to the parser.
|
||||
/// Input received from network.
|
||||
#[ignore_heap_size_of = "Defined in html5ever"]
|
||||
pending_input: DOMRefCell<BufferQueue>,
|
||||
network_input: DOMRefCell<BufferQueue>,
|
||||
/// Input received from script. Used only to support document.write().
|
||||
#[ignore_heap_size_of = "Defined in html5ever"]
|
||||
script_input: DOMRefCell<BufferQueue>,
|
||||
/// The tokenizer of this parser.
|
||||
tokenizer: DOMRefCell<Tokenizer>,
|
||||
/// Whether to expect any further input from the associated network request.
|
||||
last_chunk_received: Cell<bool>,
|
||||
/// Whether this parser should avoid passing any further data to the tokenizer.
|
||||
suspended: Cell<bool>,
|
||||
/// https://html.spec.whatwg.org/multipage/#script-nesting-level
|
||||
script_nesting_level: Cell<usize>,
|
||||
}
|
||||
|
||||
#[derive(PartialEq)]
|
||||
|
@ -134,6 +152,84 @@ impl ServoParser {
|
|||
parser.parse_chunk(String::from(input));
|
||||
}
|
||||
|
||||
pub fn script_nesting_level(&self) -> usize {
|
||||
self.script_nesting_level.get()
|
||||
}
|
||||
|
||||
/// Corresponds to the latter part of the "Otherwise" branch of the 'An end
|
||||
/// tag whose tag name is "script"' of
|
||||
/// https://html.spec.whatwg.org/multipage/#parsing-main-incdata
|
||||
///
|
||||
/// This first moves everything from the script input to the beginning of
|
||||
/// the network input, effectively resetting the insertion point to just
|
||||
/// before the next character to be consumed.
|
||||
///
|
||||
///
|
||||
/// ```text
|
||||
/// | ... script input ... network input ...
|
||||
/// ^
|
||||
/// insertion point
|
||||
/// ```
|
||||
pub fn resume_with_pending_parsing_blocking_script(&self, script: &HTMLScriptElement) {
|
||||
assert!(self.suspended.get());
|
||||
self.suspended.set(false);
|
||||
|
||||
mem::swap(&mut *self.script_input.borrow_mut(), &mut *self.network_input.borrow_mut());
|
||||
while let Some(chunk) = self.script_input.borrow_mut().pop_front() {
|
||||
self.network_input.borrow_mut().push_back(chunk);
|
||||
}
|
||||
|
||||
let script_nesting_level = self.script_nesting_level.get();
|
||||
assert_eq!(script_nesting_level, 0);
|
||||
|
||||
self.script_nesting_level.set(script_nesting_level + 1);
|
||||
script.execute();
|
||||
self.script_nesting_level.set(script_nesting_level);
|
||||
|
||||
if !self.suspended.get() {
|
||||
self.parse_sync();
|
||||
}
|
||||
}
|
||||
|
||||
/// Steps 6-8 of https://html.spec.whatwg.org/multipage/#document.write()
|
||||
pub fn write(&self, text: Vec<DOMString>) {
|
||||
assert!(self.script_nesting_level.get() > 0);
|
||||
|
||||
if self.document.get_pending_parsing_blocking_script().is_some() {
|
||||
// There is already a pending parsing blocking script so the
|
||||
// parser is suspended, we just append everything to the
|
||||
// script input and abort these steps.
|
||||
for chunk in text {
|
||||
self.script_input.borrow_mut().push_back(String::from(chunk).into());
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// There is no pending parsing blocking script, so all previous calls
|
||||
// to document.write() should have seen their entire input tokenized
|
||||
// and process, with nothing pushed to the parser script input.
|
||||
assert!(self.script_input.borrow().is_empty());
|
||||
|
||||
let mut input = BufferQueue::new();
|
||||
for chunk in text {
|
||||
input.push_back(String::from(chunk).into());
|
||||
}
|
||||
|
||||
self.tokenize(|tokenizer| tokenizer.feed(&mut input));
|
||||
|
||||
if self.suspended.get() {
|
||||
// Parser got suspended, insert remaining input at end of
|
||||
// script input, following anything written by scripts executed
|
||||
// reentrantly during this call.
|
||||
while let Some(chunk) = input.pop_front() {
|
||||
self.script_input.borrow_mut().push_back(chunk);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
assert!(input.is_empty());
|
||||
}
|
||||
|
||||
#[allow(unrooted_must_root)]
|
||||
fn new_inherited(
|
||||
document: &Document,
|
||||
|
@ -145,10 +241,12 @@ impl ServoParser {
|
|||
reflector: Reflector::new(),
|
||||
document: JS::from_ref(document),
|
||||
pipeline: pipeline,
|
||||
pending_input: DOMRefCell::new(BufferQueue::new()),
|
||||
network_input: DOMRefCell::new(BufferQueue::new()),
|
||||
script_input: DOMRefCell::new(BufferQueue::new()),
|
||||
tokenizer: DOMRefCell::new(tokenizer),
|
||||
last_chunk_received: Cell::new(last_chunk_state == LastChunkState::Received),
|
||||
suspended: Default::default(),
|
||||
script_nesting_level: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,106 +263,87 @@ impl ServoParser {
|
|||
ServoParserBinding::Wrap)
|
||||
}
|
||||
|
||||
pub fn document(&self) -> &Document {
|
||||
&self.document
|
||||
}
|
||||
|
||||
pub fn pipeline(&self) -> Option<PipelineId> {
|
||||
self.pipeline
|
||||
}
|
||||
|
||||
fn has_pending_input(&self) -> bool {
|
||||
!self.pending_input.borrow().is_empty()
|
||||
}
|
||||
|
||||
fn push_input_chunk(&self, chunk: String) {
|
||||
self.pending_input.borrow_mut().push_back(chunk.into());
|
||||
}
|
||||
|
||||
fn last_chunk_received(&self) -> bool {
|
||||
self.last_chunk_received.get()
|
||||
}
|
||||
|
||||
fn mark_last_chunk_received(&self) {
|
||||
self.last_chunk_received.set(true)
|
||||
}
|
||||
|
||||
fn set_plaintext_state(&self) {
|
||||
self.tokenizer.borrow_mut().set_plaintext_state()
|
||||
}
|
||||
|
||||
pub fn suspend(&self) {
|
||||
assert!(!self.suspended.get());
|
||||
self.suspended.set(true);
|
||||
}
|
||||
|
||||
pub fn resume(&self) {
|
||||
assert!(self.suspended.get());
|
||||
self.suspended.set(false);
|
||||
self.parse_sync();
|
||||
}
|
||||
|
||||
pub fn is_suspended(&self) -> bool {
|
||||
self.suspended.get()
|
||||
self.network_input.borrow_mut().push_back(chunk.into());
|
||||
}
|
||||
|
||||
fn parse_sync(&self) {
|
||||
let metadata = TimerMetadata {
|
||||
url: self.document().url().as_str().into(),
|
||||
url: self.document.url().as_str().into(),
|
||||
iframe: TimerMetadataFrameType::RootWindow,
|
||||
incremental: TimerMetadataReflowType::FirstReflow,
|
||||
};
|
||||
let profiler_category = self.tokenizer.borrow().profiler_category();
|
||||
profile(profiler_category,
|
||||
Some(metadata),
|
||||
self.document().window().upcast::<GlobalScope>().time_profiler_chan().clone(),
|
||||
self.document.window().upcast::<GlobalScope>().time_profiler_chan().clone(),
|
||||
|| self.do_parse_sync())
|
||||
}
|
||||
|
||||
fn do_parse_sync(&self) {
|
||||
assert!(self.script_input.borrow().is_empty());
|
||||
|
||||
// This parser will continue to parse while there is either pending input or
|
||||
// the parser remains unsuspended.
|
||||
loop {
|
||||
self.document().reflow_if_reflow_timer_expired();
|
||||
if let Err(script) = self.tokenizer.borrow_mut().feed(&mut *self.pending_input.borrow_mut()) {
|
||||
if script.prepare() {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Document parsing is blocked on an external resource.
|
||||
if self.suspended.get() {
|
||||
return;
|
||||
}
|
||||
self.tokenize(|tokenizer| tokenizer.feed(&mut *self.network_input.borrow_mut()));
|
||||
|
||||
if !self.has_pending_input() {
|
||||
break;
|
||||
}
|
||||
if self.suspended.get() {
|
||||
return;
|
||||
}
|
||||
|
||||
if self.last_chunk_received() {
|
||||
assert!(self.network_input.borrow().is_empty());
|
||||
|
||||
if self.last_chunk_received.get() {
|
||||
self.finish();
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_chunk(&self, input: String) {
|
||||
self.document().set_current_parser(Some(self));
|
||||
self.document.set_current_parser(Some(self));
|
||||
self.push_input_chunk(input);
|
||||
if !self.is_suspended() {
|
||||
if !self.suspended.get() {
|
||||
self.parse_sync();
|
||||
}
|
||||
}
|
||||
|
||||
fn tokenize<F>(&self, mut feed: F)
|
||||
where F: FnMut(&mut Tokenizer) -> Result<(), Root<HTMLScriptElement>>
|
||||
{
|
||||
loop {
|
||||
assert!(!self.suspended.get());
|
||||
|
||||
self.document.reflow_if_reflow_timer_expired();
|
||||
let script = match feed(&mut *self.tokenizer.borrow_mut()) {
|
||||
Ok(()) => return,
|
||||
Err(script) => script,
|
||||
};
|
||||
|
||||
let script_nesting_level = self.script_nesting_level.get();
|
||||
|
||||
self.script_nesting_level.set(script_nesting_level + 1);
|
||||
script.prepare();
|
||||
self.script_nesting_level.set(script_nesting_level);
|
||||
|
||||
if self.document.get_pending_parsing_blocking_script().is_some() {
|
||||
self.suspended.set(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn finish(&self) {
|
||||
assert!(!self.suspended.get());
|
||||
assert!(!self.has_pending_input());
|
||||
assert!(self.last_chunk_received.get());
|
||||
assert!(self.script_input.borrow().is_empty());
|
||||
assert!(self.network_input.borrow().is_empty());
|
||||
|
||||
self.tokenizer.borrow_mut().end();
|
||||
debug!("finished parsing");
|
||||
|
||||
self.document().set_current_parser(None);
|
||||
self.document.set_current_parser(None);
|
||||
|
||||
if let Some(pipeline) = self.pipeline() {
|
||||
if let Some(pipeline) = self.pipeline {
|
||||
ScriptThread::parsing_complete(pipeline);
|
||||
}
|
||||
}
|
||||
|
@ -372,7 +451,7 @@ impl FetchResponseListener for ParserContext {
|
|||
parser.push_input_chunk(page);
|
||||
parser.parse_sync();
|
||||
|
||||
let doc = parser.document();
|
||||
let doc = &parser.document;
|
||||
let doc_body = Root::upcast::<Node>(doc.GetBody().unwrap());
|
||||
let img = HTMLImageElement::new(local_name!("img"), None, doc);
|
||||
img.SetSrc(DOMString::from(self.url.to_string()));
|
||||
|
@ -384,7 +463,7 @@ impl FetchResponseListener for ParserContext {
|
|||
let page = "<pre>\n".into();
|
||||
parser.push_input_chunk(page);
|
||||
parser.parse_sync();
|
||||
parser.set_plaintext_state();
|
||||
parser.tokenizer.borrow_mut().set_plaintext_state();
|
||||
},
|
||||
Some(ContentType(Mime(TopLevel::Text, SubLevel::Html, _))) => { // Handle text/html
|
||||
if let Some(reason) = ssl_error {
|
||||
|
@ -449,11 +528,11 @@ impl FetchResponseListener for ParserContext {
|
|||
debug!("Failed to load page URL {}, error: {:?}", self.url, err);
|
||||
}
|
||||
|
||||
parser.document()
|
||||
parser.document
|
||||
.finish_load(LoadType::PageSource(self.url.clone()));
|
||||
|
||||
parser.mark_last_chunk_received();
|
||||
if !parser.is_suspended() {
|
||||
parser.last_chunk_received.set(true);
|
||||
if !parser.suspended.get() {
|
||||
parser.parse_sync();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -114,8 +114,10 @@ partial /*sealed*/ interface Document {
|
|||
// Document open(optional DOMString type = "text/html", optional DOMString replace = "");
|
||||
// WindowProxy open(DOMString url, DOMString name, DOMString features, optional boolean replace = false);
|
||||
// void close();
|
||||
// void write(DOMString... text);
|
||||
// void writeln(DOMString... text);
|
||||
[Throws]
|
||||
void write(DOMString... text);
|
||||
[Throws]
|
||||
void writeln(DOMString... text);
|
||||
|
||||
// user interaction
|
||||
readonly attribute Window?/*Proxy?*/ defaultView;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue