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 XHR block document load.

  • Loading branch information
jdm committed Jan 10, 2015
commit 74be359646aa50be6963424456e598a048bd700e
@@ -110,8 +110,7 @@ impl Metadata {
/// the resource task to make a new request.
pub struct PendingAsyncLoad {
resource_task: ResourceTask,
url: Url,
input_chan: Sender<LoadResponse>,
pub load_data: LoadData,
input_port: Receiver<LoadResponse>,
}

@@ -120,8 +119,7 @@ impl PendingAsyncLoad {
let (tx, rx) = channel();
PendingAsyncLoad {
resource_task: resource_task,
url: url,
input_chan: tx,
load_data: LoadData::new(url, tx),
input_port: rx,
}
}
@@ -130,12 +128,15 @@ impl PendingAsyncLoad {
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));
pub fn load_with(mut self, cb: |load_data: &mut LoadData|) -> Receiver<LoadResponse> {
cb(&mut self.load_data);
self.resource_task.send(ControlMsg::Load(self.load_data));
self.input_port
}

pub fn url(&self) -> &Url {
&self.load_data.url
}
}

/// Message sent in response to `Load`. Contains metadata, and a port
@@ -19,13 +19,15 @@ pub enum LoadType {
Subframe(Url),
Stylesheet(Url),
PageSource(Url),
XHR(Url),
}

impl LoadType {
fn url(&self) -> &Url {
pub fn url(&self) -> &Url {
match *self {
LoadType::Image(ref url) | LoadType::Script(ref url) | LoadType::Subframe(ref url) |
LoadType::Stylesheet(ref url) | LoadType::PageSource(ref url) => url,
LoadType::Stylesheet(ref url) | LoadType::PageSource(ref url) |
LoadType::XHR(ref url) => url,
}
}
}
@@ -7,6 +7,7 @@
//! This module contains smart pointers to global scopes, to simplify writing
//! code that works in workers as well as window scopes.

use document_loader::LoadType;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::conversions::FromJSValConvertible;
use dom::bindings::js::{JS, JSRef, Root};
@@ -16,7 +17,8 @@ use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers};
use dom::window;
use script_task::ScriptChan;

use servo_net::resource_task::ResourceTask;
use servo_msg::constellation_msg::PipelineId;
use servo_net::resource_task::{ResourceTask, PendingAsyncLoad};

use js::{JSCLASS_IS_GLOBAL, JSCLASS_IS_DOMJSCLASS};
use js::glue::{GetGlobalForObjectCrossCompartment};
@@ -78,6 +80,28 @@ impl<'a> GlobalRef<'a> {
}
}

pub fn prep_async_load(&self, load: LoadType) -> PendingAsyncLoad {
match *self {
GlobalRef::Window(ref window) => {
let doc = window.Document().root();
doc.prep_async_load(load)
}
GlobalRef::Worker(ref worker) => {
worker.prep_async_load(load.url().clone())
}
}
}

pub fn finish_load(&self, load: LoadType) {
match *self {
GlobalRef::Window(ref window) => {
let doc = window.Document().root();
doc.finish_load(load)
}
GlobalRef::Worker(_) => {}
}
}

pub fn get_url(&self) -> Url {
match *self {
GlobalRef::Window(ref window) => window.get_url(),
@@ -93,6 +117,13 @@ impl<'a> GlobalRef<'a> {
GlobalRef::Worker(ref worker) => worker.script_chan(),
}
}

pub fn pipeline(&self) -> Option<PipelineId> {
match *self {
GlobalRef::Window(ref window) => Some(window.page().id),
GlobalRef::Worker(_) => None,
}
}
}

impl<'a> Reflectable for GlobalRef<'a> {
@@ -224,6 +224,9 @@ impl<'a> PrivateDedicatedWorkerGlobalScopeHelpers for JSRef<'a, DedicatedWorkerG
let scope: JSRef<WorkerGlobalScope> = WorkerGlobalScopeCast::from_ref(self);
scope.handle_fire_timer(timer_id);
}
ScriptMsg::PageResourceLoaded(..) => {
// workers don't track loads right now
}
_ => panic!("Unexpected message"),
}
}
@@ -19,7 +19,7 @@ use dom::window::{base64_atob, base64_btoa};
use script_task::{ScriptChan, TimerSource};
use timers::{IsInterval, TimerId, TimerManager, TimerCallback};

use servo_net::resource_task::{ResourceTask, load_whole_resource};
use servo_net::resource_task::{ResourceTask, load_whole_resource, PendingAsyncLoad};
use servo_util::str::DOMString;

use js::jsapi::JSContext;
@@ -75,7 +75,11 @@ impl WorkerGlobalScope {
}

pub fn resource_task<'a>(&'a self) -> &'a ResourceTask {
& self.resource_task
&self.resource_task
}

pub fn prep_async_load(&self, url: Url) -> PendingAsyncLoad {
PendingAsyncLoad::new(self.resource_task.clone(), url)
}

pub fn get_url<'a>(&'a self) -> &'a Url {
@@ -2,6 +2,7 @@
* 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::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull;
use dom::bindings::codegen::Bindings::XMLHttpRequestBinding;
@@ -13,7 +14,7 @@ use dom::bindings::conversions::ToJSValConvertible;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::error::Error::{InvalidState, InvalidAccess};
use dom::bindings::error::Error::{Network, Syntax, Security, Abort, Timeout};
use dom::bindings::global::{GlobalField, GlobalRef, GlobalRoot};
use dom::bindings::global::{GlobalField, GlobalRef, GlobalRoot, global_object_for_js_object};
use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, OptionalRootedRootable};
use dom::bindings::refcounted::Trusted;
use dom::bindings::str::ByteString;
@@ -42,10 +43,10 @@ use js::jsapi::{JS_ParseJSON, JSContext};
use js::jsapi::JS_ClearPendingException;
use js::jsval::{JSVal, NullValue, UndefinedValue};

use net::resource_task::{ResourceTask, ResourceCORSData, LoadData, LoadResponse};
use net::resource_task::ControlMsg::Load;
use net::resource_task::{PendingAsyncLoad, ResourceCORSData};
use net::resource_task::ProgressMsg::{Payload, Done};
use cors::{allow_cross_origin_request, CORSRequest, RequestMode};
use servo_msg::constellation_msg::PipelineId;
use servo_util::str::DOMString;
use servo_util::task::spawn_named;

@@ -119,7 +120,7 @@ impl XHRProgress {

enum SyncOrAsync<'a> {
Sync(JSRef<'a, XMLHttpRequest>),
Async(TrustedXHRAddress, Box<ScriptChan+Send>)
Async(TrustedXHRAddress, Box<ScriptChan+Send>, Option<PipelineId>)
}

enum TerminateReason {
@@ -207,22 +208,50 @@ impl XMLHttpRequest {
}

#[allow(unsafe_blocks)]
fn fetch(fetch_type: &SyncOrAsync, resource_task: ResourceTask,
mut load_data: LoadData, terminate_receiver: Receiver<TerminateReason>,
cors_request: Result<Option<CORSRequest>,()>, gen_id: GenerationId,
start_port: Receiver<LoadResponse>) -> ErrorResult {
fn fetch(fetch_type: &SyncOrAsync, mut pending: PendingAsyncLoad,
terminate_receiver: Receiver<TerminateReason>,
cors_request: Result<Option<CORSRequest>,()>, gen_id: GenerationId)
-> ErrorResult {

fn notify_partial_progress(fetch_type: &SyncOrAsync, msg: XHRProgress) {
match *fetch_type {
SyncOrAsync::Sync(xhr) => {
xhr.process_partial_response(msg);
},
SyncOrAsync::Async(ref addr, ref script_chan) => {
SyncOrAsync::Async(ref addr, ref script_chan, _) => {
script_chan.send(ScriptMsg::RunnableMsg(box XHRProgressHandler::new(addr.clone(), msg)));
}
}
}

enum AutoFinishLoad<'a> {
AsyncFinish(LoadType, Option<PipelineId>, Box<ScriptChan+Send>),
SyncFinish(JSRef<'a, XMLHttpRequest>, Url)
}
#[unsafe_destructor]
impl<'a> Drop for AutoFinishLoad<'a> {
fn drop(&mut self) {
match *self {
AutoFinishLoad::SyncFinish(ref xhr, ref url) => {
let reflector = xhr.reflector().get_jsobject();
global_object_for_js_object(reflector)
.root()
.r()
.finish_load(LoadType::XHR(url.clone()));
}
AutoFinishLoad::AsyncFinish(ref load, ref pipeline, ref chan) => {
chan.send(ScriptMsg::PageResourceLoaded(pipeline.clone(), load.clone()));
}
}
}
}
let _auto_finish = match fetch_type {
&SyncOrAsync::Sync(xhr) => AutoFinishLoad::SyncFinish(xhr, pending.url().clone()),
&SyncOrAsync::Async(_, ref script_chan, ref pipeline) =>
AutoFinishLoad::AsyncFinish(LoadType::XHR(pending.url().clone()),
pipeline.clone(), (*script_chan).clone())
};

macro_rules! notify_error_and_return(
($err:expr) => ({
notify_partial_progress(fetch_type, XHRProgress::Errored(gen_id, $err));
@@ -243,7 +272,6 @@ impl XMLHttpRequest {
);
)


match cors_request {
Err(_) => {
// Happens in case of cross-origin non-http URIs
@@ -265,7 +293,7 @@ impl XMLHttpRequest {
if response.network_error {
notify_error_and_return!(Network);
} else {
load_data.cors = Some(ResourceCORSData {
pending.load_data.cors = Some(ResourceCORSData {
preflight: req.preflight_flag,
origin: req.origin.clone()
});
@@ -278,8 +306,7 @@ impl XMLHttpRequest {
}

// Step 10, 13
resource_task.send(Load(load_data));

let start_port = pending.load();

let progress_port;
select! (
@@ -538,10 +565,6 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
}

let global = self.global.root();
let resource_task = global.r().resource_task();
let (start_chan, start_port) = channel();
let mut load_data = LoadData::new(self.request_url.borrow().clone().unwrap(), start_chan);
load_data.data = extracted;

// Default headers
{
@@ -576,20 +599,27 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {
}
} // drops the borrow_mut

load_data.headers = (*self.request_headers.borrow()).clone();
load_data.method = (*self.request_method.borrow()).clone();
let url = self.request_url.borrow().as_ref().unwrap().clone();
let mut pending = global.r().prep_async_load(LoadType::XHR(url));
pending.load_data.data = extracted;
pending.load_data.headers = (*self.request_headers.borrow()).clone();
pending.load_data.method = (*self.request_method.borrow()).clone();

let (terminate_sender, terminate_receiver) = channel();
*self.terminate_sender.borrow_mut() = Some(terminate_sender);

// CORS stuff
let referer_url = self.global.root().r().get_url();
let referer_url = global.r().get_url();
let mode = if self.upload_events.get() {
RequestMode::ForcedPreflight
} else {
RequestMode::CORS
};
let cors_request = CORSRequest::maybe_new(referer_url.clone(), load_data.url.clone(), mode,
load_data.method.clone(), load_data.headers.clone());
let cors_request = CORSRequest::maybe_new(referer_url.clone(),
pending.load_data.url.clone(),
mode,
pending.load_data.method.clone(),
pending.load_data.headers.clone());
match cors_request {
Ok(None) => {
let mut buf = String::new();
@@ -612,24 +642,22 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> {

let gen_id = self.generation_id.get();
if self.sync.get() {
return XMLHttpRequest::fetch(&mut SyncOrAsync::Sync(self), resource_task, load_data,
terminate_receiver, cors_request, gen_id, start_port);
return XMLHttpRequest::fetch(&mut SyncOrAsync::Sync(self), pending,
terminate_receiver, cors_request, gen_id);
} else {
self.fetch_time.set(time::now().to_timespec().sec);
let script_chan = global.r().script_chan();
// Pin the object before launching the fetch task. This is to ensure that
// the object will stay alive as long as there are (possibly cancelled)
// inflight events queued up in the script task's port.
let addr = Trusted::new(self.global.root().r().get_cx(), self,
script_chan.clone());
let addr = Trusted::new(global.r().get_cx(), self, script_chan.clone());
let pipeline = global.r().pipeline();
spawn_named("XHRTask", proc() {
let _ = XMLHttpRequest::fetch(&mut SyncOrAsync::Async(addr, script_chan),
resource_task,
load_data,
let _ = XMLHttpRequest::fetch(&mut SyncOrAsync::Async(addr, script_chan, pipeline),
pending,
terminate_receiver,
cors_request,
gen_id,
start_port);
gen_id);
});
let timeout = self.timeout.get();
if timeout > 0 {
@@ -127,6 +127,8 @@ pub enum ScriptMsg {
RefcountCleanup(*const libc::c_void),
/// Notify a document that all pending loads are complete.
DocumentLoadsComplete(PipelineId),
/// Notify a document that a pending load is complete.
PageResourceLoaded(Option<PipelineId>, LoadType),
}

/// A cloneable interface for communicating with an event loop.
@@ -614,6 +616,8 @@ impl ScriptTask {
LiveDOMReferences::cleanup(self.get_cx(), addr),
ScriptMsg::DocumentLoadsComplete(id) =>
self.handle_loads_complete(id),
ScriptMsg::PageResourceLoaded(id, load) =>
self.handle_resource_loaded(id.unwrap(), load),
}
}

ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.