servo: Merge #13418 - Make document url mutable and implement location.replace() (from stshine:location-replace); r=KiChjang

<!-- Please describe your changes on the following line: -->

---

<!-- 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 fix #13413 (github issue number if applicable).

<!-- Either: -->
- [X] There are tests for these changes OR

<!-- Pull requests that do not address these steps are welcome, but they will require additional verification as part of the review process. -->

Source-Repo: https://github.com/servo/servo
Source-Revision: 72e4c6dc21c132d49cf0a5f68a3ba45d16cc8322
This commit is contained in:
Pu Xingyu 2016-11-20 02:27:54 -06:00
Родитель f241c1d75a
Коммит 8f506067e0
17 изменённых файлов: 115 добавлений и 115 удалений

Просмотреть файл

@ -18,7 +18,7 @@ use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
use dom::bindings::codegen::Bindings::NodeFilterBinding::NodeFilter;
use dom::bindings::codegen::Bindings::PerformanceBinding::PerformanceMethods;
use dom::bindings::codegen::Bindings::TouchBinding::TouchMethods;
use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, WindowMethods};
use dom::bindings::codegen::UnionTypes::NodeOrString;
use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
@ -192,7 +192,7 @@ pub struct Document {
last_modified: Option<String>,
encoding: Cell<EncodingRef>,
is_html_document: bool,
url: ServoUrl,
url: DOMRefCell<ServoUrl>,
quirks_mode: Cell<QuirksMode>,
/// Caches for the getElement methods
id_map: DOMRefCell<HashMap<Atom, Vec<JS<Element>>>>,
@ -398,8 +398,12 @@ impl Document {
}
// https://dom.spec.whatwg.org/#concept-document-url
pub fn url(&self) -> &ServoUrl {
&self.url
pub fn url(&self) -> ServoUrl {
self.url.borrow().clone()
}
pub fn set_url(&self, url: ServoUrl) {
*self.url.borrow_mut() = url;
}
// https://html.spec.whatwg.org/multipage/#fallback-base-url
@ -407,7 +411,7 @@ impl Document {
// Step 1: iframe srcdoc (#4767).
// Step 2: about:blank with a creator browsing context.
// Step 3.
self.url().clone()
self.url()
}
// https://html.spec.whatwg.org/multipage/#document-base-url
@ -587,7 +591,7 @@ impl Document {
// Step 6
.or_else(|| self.get_anchor_by_name(fragid))
// Step 7
.or_else(|| if fragid.to_lowercase() == "top" {
.or_else(|| if fragid.eq_ignore_ascii_case("top") {
self.GetDocumentElement()
} else {
// Step 8
@ -596,6 +600,45 @@ impl Document {
}
}
/// https://html.spec.whatwg.org/multipage/#scroll-to-the-fragment-identifier
pub fn check_and_scroll_fragment(&self, fragment: &str) {
let target = self.find_fragment_node(fragment);
// Step 1
self.set_target_element(target.r());
let point = if fragment.is_empty() || fragment.eq_ignore_ascii_case("top") {
// FIXME(stshine): this should be the origin of the stacking context space,
// which may differ under the influence of writing mode.
Some((0.0, 0.0))
} else {
target.r().map(|element| {
// FIXME(#8275, pcwalton): This is pretty bogus when multiple layers
// are involved. Really what needs to happen is that this needs to go
// through layout to ask which layer the element belongs to, and have
// it send the scroll message to the compositor.
let rect = element.upcast::<Node>().bounding_content_box();
// In order to align with element edges, we snap to unscaled pixel
// boundaries, since the paint thread currently does the same for
// drawing elements. This is important for pages that require pixel
// perfect scroll positioning for proper display (like Acid2). Since
// we don't have the device pixel ratio here, this might not be
// accurate, but should work as long as the ratio is a whole number.
// Once #8275 is fixed this should actually take into account the
// real device pixel ratio.
(rect.origin.x.to_nearest_px() as f32,
rect.origin.y.to_nearest_px() as f32)
})
};
if let Some((x, y)) = point {
// Step 3
self.window.perform_a_scroll(x, y, ScrollBehavior::Instant,
target.r());
}
}
fn get_anchor_by_name(&self, name: &str) -> Option<Root<Element>> {
let check_anchor = |node: &HTMLAnchorElement| {
let elem = node.upcast::<Element>();
@ -1709,7 +1752,7 @@ impl Document {
/// https://html.spec.whatwg.org/multipage/#cookie-averse-document-object
pub fn is_cookie_averse(&self) -> bool {
self.browsing_context.is_none() || !url_has_network_scheme(&self.url)
self.browsing_context.is_none() || !url_has_network_scheme(&self.url())
}
pub fn nodes_from_point(&self, client_point: &Point2D<f32>) -> Vec<UntrustedNodeAddress> {
@ -1814,7 +1857,7 @@ impl Document {
}),
},
last_modified: last_modified,
url: url,
url: DOMRefCell::new(url),
// https://dom.spec.whatwg.org/#concept-document-quirks
quirks_mode: Cell::new(NoQuirks),
// https://dom.spec.whatwg.org/#concept-document-encoding
@ -2787,7 +2830,7 @@ impl DocumentMethods for Document {
let _ = self.window
.upcast::<GlobalScope>()
.resource_threads()
.send(GetCookiesForUrl((*url).clone(), tx, NonHTTP));
.send(GetCookiesForUrl(url, tx, NonHTTP));
let cookies = rx.recv().unwrap();
Ok(cookies.map_or(DOMString::new(), DOMString::from))
}
@ -2806,7 +2849,7 @@ impl DocumentMethods for Document {
let _ = self.window
.upcast::<GlobalScope>()
.resource_threads()
.send(SetCookiesForUrl((*url).clone(), String::from(cookie), NonHTTP));
.send(SetCookiesForUrl(url, String::from(cookie), NonHTTP));
Ok(())
}

Просмотреть файл

@ -615,5 +615,5 @@ fn follow_hyperlink(subject: &Element, hyperlink_suffix: Option<String>, referre
debug!("following hyperlink to {}", url);
let window = document.window();
window.load_url(url, false, referrer_policy);
window.load_url(url, false, false, referrer_policy);
}

Просмотреть файл

@ -341,7 +341,7 @@ impl HTMLFormElement {
let _target = submitter.target();
// TODO: Handle browsing contexts, partially loaded documents (step 16-17)
let mut load_data = LoadData::new(action_components, doc.get_referrer_policy(), Some(doc.url().clone()));
let mut load_data = LoadData::new(action_components, doc.get_referrer_policy(), Some(doc.url()));
// Step 18
match (&*scheme, method) {

Просмотреть файл

@ -157,7 +157,7 @@ impl HTMLIFrameElement {
let document = document_from_node(self);
self.navigate_or_reload_child_browsing_context(
Some(LoadData::new(url, document.get_referrer_policy(), Some(document.url().clone()))), false);
Some(LoadData::new(url, document.get_referrer_policy(), Some(document.url()))), false);
}
#[allow(unsafe_code)]

Просмотреть файл

@ -280,9 +280,9 @@ impl HTMLLinkElement {
destination: Destination::Style,
credentials_mode: CredentialsMode::Include,
use_url_credentials: true,
origin: document.url().clone(),
origin: document.url(),
pipeline_id: Some(self.global().pipeline_id()),
referrer_url: Some(document.url().clone()),
referrer_url: Some(document.url()),
referrer_policy: referrer_policy,
.. RequestInit::default()
};

Просмотреть файл

@ -548,9 +548,9 @@ impl HTMLMediaElement {
destination: Destination::Media,
credentials_mode: CredentialsMode::Include,
use_url_credentials: true,
origin: document.url().clone(),
origin: document.url(),
pipeline_id: Some(self.global().pipeline_id()),
referrer_url: Some(document.url().clone()),
referrer_url: Some(document.url()),
referrer_policy: document.get_referrer_policy(),
.. RequestInit::default()
};

Просмотреть файл

@ -241,9 +241,9 @@ fn fetch_a_classic_script(script: &HTMLScriptElement,
Some(CorsSettings::Anonymous) => CredentialsMode::CredentialsSameOrigin,
_ => CredentialsMode::Include,
},
origin: doc.url().clone(),
origin: doc.url(),
pipeline_id: Some(script.global().pipeline_id()),
referrer_url: Some(doc.url().clone()),
referrer_url: Some(doc.url()),
referrer_policy: doc.get_referrer_policy(),
.. RequestInit::default()
};

Просмотреть файл

@ -40,7 +40,7 @@ impl Location {
setter: fn(&mut ServoUrl, USVString)) {
let mut url = self.window.get_url();
setter(&mut url, value);
self.window.load_url(url, false, None);
self.window.load_url(url, false, false, None);
}
}
@ -51,7 +51,7 @@ impl LocationMethods for Location {
// _entry settings object_.
let base_url = self.window.get_url();
if let Ok(url) = base_url.join(&url.0) {
self.window.load_url(url, false, None);
self.window.load_url(url, false, false, None);
Ok(())
} else {
Err(Error::Syntax)
@ -60,7 +60,20 @@ impl LocationMethods for Location {
// https://html.spec.whatwg.org/multipage/#dom-location-reload
fn Reload(&self) {
self.window.load_url(self.get_url(), true, None);
self.window.load_url(self.get_url(), true, true, None);
}
// https://html.spec.whatwg.org/multipage/#dom-location-replace
fn Replace(&self, url: USVString) -> ErrorResult {
// TODO: per spec, we should use the _API base URL_ specified by the
// _entry settings object_.
let base_url = self.window.get_url();
if let Ok(url) = base_url.join(&url.0) {
self.window.load_url(url, true, false, None);
Ok(())
} else {
Err(Error::Syntax)
}
}
// https://html.spec.whatwg.org/multipage/#dom-location-hash
@ -109,7 +122,7 @@ impl LocationMethods for Location {
// https://html.spec.whatwg.org/multipage/#dom-location-href
fn SetHref(&self, value: USVString) {
if let Ok(url) = self.window.get_url().join(&value.0) {
self.window.load_url(url, false, None);
self.window.load_url(url, false, false, None);
}
}

Просмотреть файл

@ -135,7 +135,7 @@ macro_rules! make_string_or_document_url_getter(
if val.is_empty() {
let doc = document_from_node(self);
DOMString::from(doc.url().clone().into_string())
DOMString::from(doc.url().into_string())
} else {
val
}

Просмотреть файл

@ -1751,7 +1751,7 @@ impl Node {
let window = document.window();
let loader = DocumentLoader::new(&*document.loader());
let document = Document::new(window, None,
Some((*document.url()).clone()),
Some(document.url()),
is_html_doc, None,
None, DocumentSource::NotFromParser, loader,
None, None);

Просмотреть файл

@ -16,7 +16,8 @@
[Throws]
void assign(USVString url);
//void replace(USVString url);
[Throws]
void replace(USVString url);
void reload();
//[SameObject] readonly attribute USVString[] ancestorOrigins;

Просмотреть файл

@ -103,6 +103,7 @@ use time;
use timers::{IsInterval, TimerCallback};
#[cfg(any(target_os = "macos", target_os = "linux", target_os = "windows"))]
use tinyfiledialogs::{self, MessageBoxIcon};
use url::Position;
use util::geometry::{self, max_rect};
use util::opts;
use util::prefs::PREFS;
@ -205,9 +206,6 @@ pub struct Window {
#[ignore_heap_size_of = "channels are hard"]
bluetooth_thread: IpcSender<BluetoothRequest>,
/// Pending scroll to fragment event, if any
fragment_name: DOMRefCell<Option<String>>,
/// An enlarged rectangle around the page contents visible in the viewport, used
/// to prevent creating display list items for content that is far away from the viewport.
page_clip_rect: Cell<Rect<Au>>,
@ -1331,13 +1329,25 @@ impl Window {
}
/// Commence a new URL load which will either replace this window or scroll to a fragment.
pub fn load_url(&self, url: ServoUrl, replace: bool, referrer_policy: Option<ReferrerPolicy>) {
pub fn load_url(&self, url: ServoUrl, replace: bool, force_reload: bool,
referrer_policy: Option<ReferrerPolicy>) {
let doc = self.Document();
let referrer_policy = referrer_policy.or(doc.get_referrer_policy());
// https://html.spec.whatwg.org/multipage/#navigating-across-documents
if !force_reload && url.as_url().unwrap()[..Position::AfterQuery] ==
doc.url().as_url().unwrap()[..Position::AfterQuery] {
// Step 5
if let Some(fragment) = url.fragment() {
doc.check_and_scroll_fragment(fragment);
doc.set_url(url.clone());
return
}
}
self.main_thread_script_chan().send(
MainThreadScriptMsg::Navigate(self.upcast::<GlobalScope>().pipeline_id(),
LoadData::new(url, referrer_policy, Some(doc.url().clone())),
LoadData::new(url, referrer_policy, Some(doc.url())),
replace)).unwrap();
}
@ -1348,14 +1358,6 @@ impl Window {
ReflowReason::Timer);
}
pub fn set_fragment_name(&self, fragment: Option<String>) {
*self.fragment_name.borrow_mut() = fragment;
}
pub fn steal_fragment_name(&self) -> Option<String> {
self.fragment_name.borrow_mut().take()
}
pub fn set_window_size(&self, size: WindowSizeData) {
self.window_size.set(Some(size));
}
@ -1365,7 +1367,7 @@ impl Window {
}
pub fn get_url(&self) -> ServoUrl {
(*self.Document().url()).clone()
self.Document().url()
}
pub fn layout_chan(&self) -> &Sender<Msg> {
@ -1588,7 +1590,6 @@ impl Window {
js_runtime: DOMRefCell::new(Some(runtime.clone())),
bluetooth_thread: bluetooth_thread,
page_clip_rect: Cell::new(max_rect()),
fragment_name: DOMRefCell::new(None),
resize_event: Cell::new(None),
layout_chan: layout_chan,
layout_rpc: layout_rpc,

Просмотреть файл

@ -159,7 +159,7 @@ impl XMLHttpRequest {
//TODO - update this when referrer policy implemented for workers
let (referrer_url, referrer_policy) = if let Some(window) = global.downcast::<Window>() {
let document = window.Document();
(Some(document.url().clone()), document.get_referrer_policy())
(Some(document.url()), document.get_referrer_policy())
} else {
(None, None)
};

Просмотреть файл

@ -59,7 +59,6 @@ use euclid::Rect;
use euclid::point::Point2D;
use hyper::header::{ContentType, HttpDate, LastModified};
use hyper::header::ReferrerPolicy as ReferrerPolicyHeader;
use hyper::method::Method;
use hyper::mime::{Mime, SubLevel, TopLevel};
use hyper_serde::Serde;
use ipc_channel::ipc::{self, IpcSender};
@ -91,7 +90,6 @@ use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent,
use script_traits::CompositorEvent::{TouchEvent, TouchpadPressureEvent};
use script_traits::webdriver_msg::WebDriverScriptCommand;
use servo_url::ServoUrl;
use std::borrow::ToOwned;
use std::cell::Cell;
use std::collections::{hash_map, HashMap, HashSet};
use std::option::Option;
@ -1231,20 +1229,8 @@ impl ScriptThread {
self.dom_manipulation_task_source.queue(handler, doc.window().upcast()).unwrap();
if let Some(fragment) = doc.url().fragment() {
self.check_and_scroll_fragment(fragment, pipeline, &doc);
}
}
fn check_and_scroll_fragment(&self, fragment: &str, pipeline_id: PipelineId, doc: &Document) {
match doc.find_fragment_node(fragment) {
Some(ref node) => {
doc.set_target_element(Some(&node));
self.scroll_fragment_point(pipeline_id, &node);
}
None => {
doc.set_target_element(None);
}
}
doc.check_and_scroll_fragment(fragment);
};
}
fn collect_reports(&self, reports_chan: ReportsChan) {
@ -1253,7 +1239,7 @@ impl ScriptThread {
let mut reports = vec![];
for (_, document) in self.documents.borrow().iter() {
let current_url = document.url().as_str();
let current_url = document.url();
for child in document.upcast::<Node>().traverse_preorder() {
dom_tree_size += heap_size_of_self_and_children(&*child);
@ -1263,10 +1249,10 @@ impl ScriptThread {
if reports.len() > 0 {
path_seg.push_str(", ");
}
path_seg.push_str(current_url);
path_seg.push_str(current_url.as_str());
reports.push(Report {
path: path![format!("url({})", current_url), "dom-tree"],
path: path![format!("url({})", current_url.as_str()), "dom-tree"],
kind: ReportKind::ExplicitJemallocHeapSize,
size: dom_tree_size,
});
@ -1826,26 +1812,6 @@ impl ScriptThread {
}
}
fn scroll_fragment_point(&self, pipeline_id: PipelineId, element: &Element) {
// FIXME(#8275, pcwalton): This is pretty bogus when multiple layers are involved.
// Really what needs to happen is that this needs to go through layout to ask which
// layer the element belongs to, and have it send the scroll message to the
// compositor.
let rect = element.upcast::<Node>().bounding_content_box();
// In order to align with element edges, we snap to unscaled pixel boundaries, since the
// paint thread currently does the same for drawing elements. This is important for pages
// that require pixel perfect scroll positioning for proper display (like Acid2). Since we
// don't have the device pixel ratio here, this might not be accurate, but should work as
// long as the ratio is a whole number. Once #8275 is fixed this should actually take into
// account the real device pixel ratio.
let point = Point2D::new(rect.origin.x.to_nearest_px() as f32,
rect.origin.y.to_nearest_px() as f32);
let message = ConstellationMsg::ScrollFragmentPoint(pipeline_id, point, false);
self.constellation_chan.send(message).unwrap();
}
/// Reflows non-incrementally, rebuilding the entire layout tree in the process.
fn rebuild_and_force_reflow(&self, document: &Document, reason: ReflowReason) {
let window = window_from_node(&*document);
@ -1987,25 +1953,6 @@ impl ScriptThread {
frame_id: Option<FrameId>,
load_data: LoadData,
replace: bool) {
// Step 7.
{
let nurl = &load_data.url;
if let Some(fragment) = nurl.fragment() {
let document = match self.documents.borrow().find_document(parent_pipeline_id) {
Some(document) => document,
None => return warn!("Message sent to closed pipeline {}.", parent_pipeline_id),
};
let nurl = nurl.as_url().unwrap();
if let Some(url) = document.url().as_url() {
if &url[..Position::AfterQuery] == &nurl[..Position::AfterQuery] &&
load_data.method == Method::Get {
self.check_and_scroll_fragment(fragment, parent_pipeline_id, &document);
return;
}
}
}
}
match frame_id {
Some(frame_id) => {
if let Some(iframe) = self.documents.borrow().find_iframe(parent_pipeline_id, frame_id) {
@ -2032,13 +1979,6 @@ impl ScriptThread {
ReflowQueryType::NoQuery,
ReflowReason::WindowResize);
let fragment_node = window.steal_fragment_name()
.and_then(|name| document.find_fragment_node(&*name));
match fragment_node {
Some(ref node) => self.scroll_fragment_point(pipeline_id, &node),
None => {}
}
// http://dev.w3.org/csswg/cssom-view/#resizing-viewports
if size_type == WindowSizeType::Resize {
let uievent = UIEvent::new(&window,
@ -2118,8 +2058,6 @@ impl ScriptThread {
// https://html.spec.whatwg.org/multipage/#the-end steps 3-4.
document.process_deferred_scripts();
window.set_fragment_name(final_url.fragment().map(str::to_owned));
}
fn handle_css_error_reporting(&self, pipeline_id: PipelineId, filename: String,

Просмотреть файл

@ -195,7 +195,7 @@ pub fn handle_get_cookies(documents: &Documents,
let url = document.url();
let (sender, receiver) = ipc::channel().unwrap();
let _ = document.window().upcast::<GlobalScope>().resource_threads().send(
GetCookiesDataForUrl(url.clone(), sender, NonHTTP)
GetCookiesDataForUrl(url, sender, NonHTTP)
);
receiver.recv().unwrap()
},
@ -215,7 +215,7 @@ pub fn handle_get_cookie(documents: &Documents,
let url = document.url();
let (sender, receiver) = ipc::channel().unwrap();
let _ = document.window().upcast::<GlobalScope>().resource_threads().send(
GetCookiesDataForUrl(url.clone(), sender, NonHTTP)
GetCookiesDataForUrl(url, sender, NonHTTP)
);
receiver.recv().unwrap()
},
@ -243,13 +243,13 @@ pub fn handle_add_cookie(documents: &Documents,
(true, _) => Err(WebDriverCookieError::InvalidDomain),
(false, Some(ref domain)) if url.host_str().map(|x| { x == &**domain }).unwrap_or(false) => {
let _ = document.window().upcast::<GlobalScope>().resource_threads().send(
SetCookiesForUrlWithData(url.clone(), cookie, method)
SetCookiesForUrlWithData(url, cookie, method)
);
Ok(())
},
(false, None) => {
let _ = document.window().upcast::<GlobalScope>().resource_threads().send(
SetCookiesForUrlWithData(url.clone(), cookie, method)
SetCookiesForUrlWithData(url, cookie, method)
);
Ok(())
},
@ -364,7 +364,7 @@ pub fn handle_get_url(documents: &Documents,
reply: IpcSender<ServoUrl>) {
// TODO: Return an error if the pipeline doesn't exist.
let url = documents.find_document(pipeline)
.map(|document| document.url().clone())
.map(|document| document.url())
.unwrap_or_else(|| ServoUrl::parse("about:blank").expect("infallible"));
reply.send(url).unwrap();
}

Просмотреть файл

@ -208,7 +208,7 @@ impl AttrValue {
AttrValue::Atom(value)
}
pub fn from_url(base: &ServoUrl, url: String) -> AttrValue {
pub fn from_url(base: ServoUrl, url: String) -> AttrValue {
let joined = base.join(&url).ok();
AttrValue::Url(url, joined)
}

Просмотреть файл

@ -98,6 +98,10 @@ impl ServoUrl {
Arc::make_mut(&mut self.0).set_password(pass)
}
pub fn set_fragment(&mut self, fragment: Option<&str>) {
Arc::make_mut(&mut self.0).set_fragment(fragment)
}
pub fn username(&self) -> &str {
self.0.username()
}