Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement document load tracking. #3714

Closed
wants to merge 4 commits into from
Closed
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

Make stylesheets block page load.

  • Loading branch information
jdm committed Jan 10, 2015
commit ab2334b389b2268fa8663425a10e06bb44919f19
@@ -121,7 +121,6 @@ impl Pipeline {
failure,
script_chan.clone(),
paint_chan.clone(),
resource_task,
image_cache_task,
font_cache_task,
time_profiler_chan,
@@ -48,7 +48,7 @@ use servo_msg::constellation_msg::Msg as ConstellationMsg;
use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineExitType, PipelineId};
use servo_net::image_cache_task::{ImageCacheTask, ImageResponseMsg};
use servo_net::local_image_cache::{ImageResponder, LocalImageCache};
use servo_net::resource_task::{ResourceTask, load_bytes_iter};
use servo_net::resource_task::{PendingAsyncLoad, load_bytes_iter};
use servo_util::cursor::Cursor;
use servo_util::geometry::Au;
use servo_util::logical_geometry::LogicalPoint;
@@ -127,9 +127,6 @@ pub struct LayoutTask {
/// The channel on which messages can be sent to the time profiler.
pub time_profiler_chan: TimeProfilerChan,

/// The channel on which messages can be sent to the resource task.
pub resource_task: ResourceTask,

/// The channel on which messages can be sent to the image cache.
pub image_cache_task: ImageCacheTask,

@@ -178,7 +175,6 @@ impl LayoutTaskFactory for LayoutTask {
failure_msg: Failure,
script_chan: ScriptControlChan,
paint_chan: PaintChan,
resource_task: ResourceTask,
img_cache_task: ImageCacheTask,
font_cache_task: FontCacheTask,
time_profiler_chan: TimeProfilerChan,
@@ -196,7 +192,6 @@ impl LayoutTaskFactory for LayoutTask {
constellation_chan,
script_chan,
paint_chan,
resource_task,
img_cache_task,
font_cache_task,
time_profiler_chan);
@@ -245,7 +240,6 @@ impl LayoutTask {
constellation_chan: ConstellationChan,
script_chan: ScriptControlChan,
paint_chan: PaintChan,
resource_task: ResourceTask,
image_cache_task: ImageCacheTask,
font_cache_task: FontCacheTask,
time_profiler_chan: TimeProfilerChan)
@@ -269,7 +263,6 @@ impl LayoutTask {
script_chan: script_chan,
paint_chan: paint_chan,
time_profiler_chan: time_profiler_chan,
resource_task: resource_task,
image_cache_task: image_cache_task.clone(),
font_cache_task: font_cache_task,
first_reflow: Cell::new(true),
@@ -393,7 +386,7 @@ impl LayoutTask {
-> bool {
match request {
Msg::AddStylesheet(sheet) => self.handle_add_stylesheet(sheet, possibly_locked_rw_data),
Msg::LoadStylesheet(url) => self.handle_load_stylesheet(url, possibly_locked_rw_data),
Msg::LoadStylesheet(url, pending) => self.handle_load_stylesheet(url, pending, possibly_locked_rw_data),
Msg::SetQuirksMode => self.handle_set_quirks_mode(possibly_locked_rw_data),
Msg::GetRPC(response_chan) => {
response_chan.send(box LayoutRPCImpl(self.rw_data.clone()) as
@@ -474,12 +467,13 @@ impl LayoutTask {

fn handle_load_stylesheet<'a>(&'a self,
url: Url,
pending: PendingAsyncLoad,
possibly_locked_rw_data:
&mut Option<MutexGuard<'a, LayoutTaskData>>) {
// TODO: Get the actual value. http://dev.w3.org/csswg/css-syntax/#environment-encoding
let environment_encoding = UTF_8 as EncodingRef;

let (metadata, iter) = load_bytes_iter(&self.resource_task, url);
let (metadata, iter) = load_bytes_iter(pending);
let protocol_encoding_label = metadata.charset.as_ref().map(|s| s.as_slice());
let final_url = metadata.final_url;

@@ -488,6 +482,11 @@ impl LayoutTask {
protocol_encoding_label,
Some(environment_encoding),
StylesheetOrigin::Author);

//TODO: mark critical subresources as blocking load as well
let ScriptControlChan(ref chan) = self.script_chan;
chan.send(ConstellationControlMsg::StylesheetLoadComplete(self.id, url));

self.handle_add_stylesheet(sheet, possibly_locked_rw_data);
}

@@ -38,6 +38,17 @@ extern crate encoding;
extern crate libc;
extern crate url;

#[allow(non_snake_case)]
mod ICE_workaround {
use super::script::layout_interface::Msg;

#[allow(dead_code)]
fn ice_workaround() {
let (tx, _rx) = channel();
tx.send(Msg::SetQuirksMode);
}
}

// Listed first because of macro definitions
pub mod layout_debug;

@@ -21,7 +21,6 @@ use gfx::font_cache_task::FontCacheTask;
use gfx::paint_task::PaintChan;
use servo_msg::constellation_msg::{ConstellationChan, Failure, PipelineId, PipelineExitType};
use servo_net::image_cache_task::ImageCacheTask;
use servo_net::resource_task::ResourceTask;
use servo_util::time::TimeProfilerChan;
use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel};
use std::comm::Sender;
@@ -46,7 +45,6 @@ pub trait LayoutTaskFactory {
failure_msg: Failure,
script_chan: ScriptControlChan,
paint_chan: PaintChan,
resource_task: ResourceTask,
img_cache_task: ImageCacheTask,
font_cache_task: FontCacheTask,
time_profiler_chan: TimeProfilerChan,
@@ -106,6 +106,38 @@ impl Metadata {
}
}

/// Initialized but unsent request. Encapsulates everything necessary to instruct
/// the resource task to make a new request.
pub struct PendingAsyncLoad {
resource_task: ResourceTask,
url: Url,
input_chan: Sender<LoadResponse>,
input_port: Receiver<LoadResponse>,
}

impl PendingAsyncLoad {
pub fn new(resource_task: ResourceTask, url: Url) -> PendingAsyncLoad {
let (tx, rx) = channel();
PendingAsyncLoad {
resource_task: resource_task,
url: url,
input_chan: tx,
input_port: rx,
}
}

pub fn load(self) -> Receiver<LoadResponse> {
self.load_with(|_| {})
}

pub fn load_with(self, cb: |load_data: &mut LoadData|) -> Receiver<LoadResponse> {
let mut load_data = LoadData::new(self.url, self.input_chan);
cb(&mut load_data);
self.resource_task.send(ControlMsg::Load(load_data));
self.input_port
}
}

/// Message sent in response to `Load`. Contains metadata, and a port
/// for receiving the data.
///
@@ -247,10 +279,8 @@ impl ResourceManager {
}

/// Load a URL asynchronously and iterate over chunks of bytes from the response.
pub fn load_bytes_iter(resource_task: &ResourceTask, url: Url) -> (Metadata, ProgressMsgPortIterator) {
let (input_chan, input_port) = channel();
resource_task.send(ControlMsg::Load(LoadData::new(url, input_chan)));

pub fn load_bytes_iter(pending: PendingAsyncLoad) -> (Metadata, ProgressMsgPortIterator) {
let input_port = pending.load();
let response = input_port.recv();
let iter = ProgressMsgPortIterator { progress_port: response.progress_port };
(response.metadata, iter)
@@ -8,7 +8,7 @@
use script_task::{ScriptMsg, ScriptChan};
use servo_msg::constellation_msg::{PipelineId};
use servo_net::resource_task::{LoadResponse, Metadata, load_whole_resource, ResourceTask};
use servo_net::resource_task::{ControlMsg, LoadData};
use servo_net::resource_task::{LoadData, PendingAsyncLoad};
use url::Url;

#[jstraceable]
@@ -57,17 +57,19 @@ impl DocumentLoader {
}
}

pub fn load_async(&mut self, load: LoadType) -> Receiver<LoadResponse> {
self.load_async_with(load, |_| {})
pub fn load_async_with(&mut self, load: LoadType, cb: |load_data: &mut LoadData|) -> Receiver<LoadResponse> {
self.blocking_loads.push(load.clone());
let pending = self.prep_async_load(load);
pending.load_with(cb)
}

pub fn load_async_with(&mut self, load: LoadType, cb: |load_data: &mut LoadData|) -> Receiver<LoadResponse> {
let (tx, rx) = channel();
pub fn prep_async_load(&mut self, load: LoadType) -> PendingAsyncLoad {
self.blocking_loads.push(load.clone());

This comment has been minimized.

Copy link
@hsivonen

hsivonen Nov 24, 2014

Contributor

What's the relationship of a DocumentLoader to the browsing context concept and the concept of a document? Why are blocking loads tracked on a DocumentLoader object and not on a Document object? If the user navigates away from a document before the load event for the document has fired, what happens to the blocking_loads vector on the DocumentLoader.

This comment has been minimized.

Copy link
@jdm

jdm Dec 8, 2014

Author Member

There's a 1:1 relationship between a document and a document loader. I split it out into a separate object because document.rs is big and it was easier to read this way. If a navigation occurs before the load event has fired, the loads will continue to be tracked as long as the document is sitting in the bfcache, as the script task event queue will continue receiving network response completion messages.

This comment has been minimized.

Copy link
@hsivonen

hsivonen Dec 19, 2014

Contributor

OK. I'm slightly worried about 1:1 objects, because when working on Gecko, there have been occasions when I've hoped nsScriptLoader was part of nsDocument and, more importantly, have hoped that nsDocShell and nsDocLoader were just one class.

That is, considering the level of intertwingularity that's expected here, I'm not convinced that it's worthwhile to have a constellation of 1:1 classes instead of just 1big class for each thing that counts as an indivisible thing in the Web Platform.

let mut load_data = LoadData::new(load.url().clone(), tx);
cb(&mut load_data);
self.resource_task.send(ControlMsg::Load(load_data));
rx
PendingAsyncLoad::new(self.resource_task.clone(), load.url().clone())
}

pub fn load_async(&mut self, load: LoadType) -> Receiver<LoadResponse> {
self.load_async_with(load, |_| {})
}

pub fn load_sync(&mut self, load: LoadType) -> Result<(Metadata, Vec<u8>), String> {
@@ -56,7 +56,7 @@ use dom::range::Range;
use dom::treewalker::TreeWalker;
use dom::uievent::UIEvent;
use dom::window::{Window, WindowHelpers};
use servo_net::resource_task::{LoadResponse, Metadata, LoadData};
use servo_net::resource_task::{LoadResponse, Metadata, LoadData, PendingAsyncLoad};
use servo_util::namespace;
use servo_util::str::{DOMString, split_html_space_chars};

@@ -194,6 +194,7 @@ pub trait DocumentHelpers<'a> {
fn commit_focus_transaction(self);
fn send_title_to_compositor(self);
fn dirty_all_nodes(self);
fn prep_async_load(self, load: LoadType) -> PendingAsyncLoad;
fn load_async(self, load: LoadType) -> Receiver<LoadResponse>;
fn load_async_with(self, load: LoadType, cb: |&mut LoadData|) -> Receiver<LoadResponse>;
fn load_sync(self, load: LoadType) -> Result<(Metadata, Vec<u8>), String>;
@@ -403,6 +404,10 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> {
}
}

fn prep_async_load(self, load: LoadType) -> PendingAsyncLoad {
self.loader.borrow_mut().prep_async_load(load)
}

fn load_async(self, load: LoadType) -> Receiver<LoadResponse> {
self.load_async_with(load, |_| {})
}
@@ -2,20 +2,21 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use document_loader::LoadType;
use dom::attr::{Attr, AttrValue};
use dom::attr::AttrHelpers;
use dom::bindings::codegen::Bindings::HTMLLinkElementBinding;
use dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods;
use dom::bindings::codegen::InheritTypes::HTMLLinkElementDerived;
use dom::bindings::codegen::InheritTypes::{ElementCast, HTMLElementCast};
use dom::bindings::js::{MutNullableJS, JSRef, Temporary, OptionalRootable};
use dom::document::Document;
use dom::document::{Document, DocumentHelpers};
use dom::domtokenlist::DOMTokenList;
use dom::element::{AttributeHandlers, Element};
use dom::eventtarget::{EventTarget, EventTargetTypeId};
use dom::element::ElementTypeId;
use dom::htmlelement::{HTMLElement, HTMLElementTypeId};
use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node};
use dom::node::{Node, NodeHelpers, NodeTypeId, window_from_node, document_from_node};
use dom::virtualmethods::VirtualMethods;
use layout_interface::{LayoutChan, Msg};
use servo_util::str::{DOMString, HTML_SPACE_CHARACTERS};
@@ -131,8 +132,10 @@ impl<'a> PrivateHTMLLinkElementHelpers for JSRef<'a, HTMLLinkElement> {
let window = window.r();
match UrlParser::new().base_url(&window.page().get_url()).parse(href) {
Ok(url) => {
let doc = document_from_node(self).root();
let pending = doc.prep_async_load(LoadType::Stylesheet(url.clone()));
let LayoutChan(ref layout_chan) = window.page().layout_chan;
layout_chan.send(Msg::LoadStylesheet(url));
layout_chan.send(Msg::LoadStylesheet(url, pending));
}
Err(e) => debug!("Parsing url {} failed: {}", href, e)
}
@@ -12,6 +12,7 @@ use geom::point::Point2D;
use geom::rect::Rect;
use script_traits::{ScriptControlChan, OpaqueScriptLayoutChannel, UntrustedNodeAddress};
use servo_msg::constellation_msg::{PipelineExitType, WindowSizeData};
use servo_net::resource_task::PendingAsyncLoad;
use servo_util::geometry::Au;
use std::any::{Any, AnyRefExt};
use std::comm::{channel, Receiver, Sender};
@@ -27,7 +28,7 @@ pub enum Msg {
AddStylesheet(Stylesheet),

/// Adds the given stylesheet to the document.
LoadStylesheet(Url),
LoadStylesheet(Url, PendingAsyncLoad),

/// Puts a document into quirks mode, causing the quirks mode stylesheet to be loaded.
SetQuirksMode,
@@ -43,6 +43,8 @@ extern crate string_cache;
#[phase(plugin)]
extern crate string_cache_macros;

pub mod layout_interface;

pub mod cors;
pub mod document_loader;

@@ -221,7 +223,6 @@ pub mod dom {

pub mod parse;

pub mod layout_interface;
pub mod page;
pub mod script_task;
mod timers;
@@ -587,6 +587,8 @@ impl ScriptTask {
panic!("should have handled ExitPipeline already"),
ConstellationControlMsg::GetTitle(pipeline_id) =>
self.handle_get_title_msg(pipeline_id),
ConstellationControlMsg::StylesheetLoadComplete(id, url) =>
self.handle_resource_loaded(id, LoadType::Stylesheet(url)),
}
}

@@ -632,6 +634,13 @@ impl ScriptTask {
}
}

fn handle_resource_loaded(&self, pipeline: PipelineId, load: LoadType) {
let page = get_page(&*self.page.borrow(), pipeline);
let frame = page.frame();
let doc = frame.as_ref().unwrap().document.root();
doc.finish_load(load);
}

fn handle_new_layout(&self, new_layout_info: NewLayoutInfo) {
let NewLayoutInfo {
old_pipeline_id,
@@ -31,6 +31,7 @@ use servo_net::resource_task::ResourceTask;
use servo_net::storage_task::StorageTask;
use servo_util::smallvec::SmallVec1;
use std::any::Any;
use url::Url;

use geom::point::Point2D;
use geom::rect::Rect;
@@ -68,6 +69,8 @@ pub enum ConstellationControlMsg {
Viewport(PipelineId, Rect<f32>),
/// Requests that the script task immediately send the constellation the title of a pipeline.
GetTitle(PipelineId),
/// Notifies script that a stylesheet has finished loading.
StylesheetLoadComplete(PipelineId, Url),
}

/// Events from the compositor that the script task needs to know about
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.