Auto merge of #16506 - cbrewster:about_chaos, r=asajeffrey

Make non-initial about:blank loads async

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

<!-- Either: -->
- [ ] There are tests for these changes OR
- [ ] These changes do not require tests because _____

<!-- Also, please make sure that "Allow edits from maintainers" checkbox is checked, so that we can help you if you get stuck somewhere along the way.-->

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

<!-- 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/16506)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-05-12 15:08:45 -05:00 committed by GitHub
commit dc8cf694ed
20 changed files with 336 additions and 200 deletions

View file

@ -903,7 +903,8 @@ impl<Window: WindowMethods> IOCompositor<Window> {
match ServoUrl::parse(&url_string) {
Ok(url) => {
let msg = match self.root_pipeline {
Some(ref pipeline) => ConstellationMsg::LoadUrl(pipeline.id, LoadData::new(url, None, None)),
Some(ref pipeline) =>
ConstellationMsg::LoadUrl(pipeline.id, LoadData::new(url, Some(pipeline.id), None, None)),
None => ConstellationMsg::InitLoadUrl(url)
};
if let Err(e) = self.constellation_chan.send(msg) {

View file

@ -101,7 +101,7 @@ use script_traits::{IFrameLoadInfo, IFrameLoadInfoWithData, IFrameSandboxState,
use script_traits::{LayoutMsg as FromLayoutMsg, ScriptMsg as FromScriptMsg, ScriptThreadFactory};
use script_traits::{LogEntry, ServiceWorkerMsg, webdriver_msg};
use script_traits::{MozBrowserErrorType, MozBrowserEvent, WebDriverCommandMsg, WindowSizeData};
use script_traits::{SWManagerMsg, ScopeThings, WindowSizeType};
use script_traits::{SWManagerMsg, ScopeThings, UpdatePipelineIdReason, WindowSizeType};
use serde::{Deserialize, Serialize};
use servo_config::opts;
use servo_config::prefs::PREFS;
@ -599,7 +599,11 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let (event_loop, host) = match sandbox {
IFrameSandboxState::IFrameSandboxed => (None, None),
IFrameSandboxState::IFrameUnsandboxed => match reg_host(&load_data.url) {
IFrameSandboxState::IFrameUnsandboxed => {
// If this is an about:blank load, it must share the creator's event loop.
// This must match the logic in the script thread when determining the proper origin.
if load_data.url.as_str() != "about:blank" {
match reg_host(&load_data.url) {
None => (None, None),
Some(host) => {
let event_loop = self.event_loops.get(&top_level_frame_id)
@ -610,6 +614,16 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
Some(event_loop) => (Some(event_loop.clone()), None),
}
},
}
} else if let Some(parent) = parent_info
.and_then(|(pipeline_id, _)| self.pipelines.get(&pipeline_id)) {
(Some(parent.event_loop.clone()), None)
} else if let Some(creator) = load_data.creator_pipeline_id
.and_then(|pipeline_id| self.pipelines.get(&pipeline_id)) {
(Some(creator.event_loop.clone()), None)
} else {
(None, None)
}
},
};
@ -735,7 +749,10 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
/// Create a new frame and update the internal bookkeeping.
fn new_frame(&mut self, frame_id: FrameId, pipeline_id: PipelineId, load_data: LoadData) {
fn new_frame(&mut self,
frame_id: FrameId,
pipeline_id: PipelineId,
load_data: LoadData) {
let frame = Frame::new(frame_id, pipeline_id, load_data);
self.frames.insert(frame_id, frame);
@ -915,11 +932,11 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
load_info.info.new_pipeline_id);
self.handle_script_loaded_url_in_iframe_msg(load_info);
}
FromScriptMsg::ScriptLoadedAboutBlankInIFrame(load_info, lc) => {
FromScriptMsg::ScriptNewIFrame(load_info, layout_sender) => {
debug!("constellation got loaded `about:blank` in iframe message {:?} {:?}",
load_info.parent_pipeline_id,
load_info.new_pipeline_id);
self.handle_script_loaded_about_blank_in_iframe_msg(load_info, lc);
self.handle_script_new_iframe(load_info, layout_sender);
}
FromScriptMsg::ChangeRunningAnimationsState(pipeline_id, animation_state) => {
self.handle_change_running_animations_state(pipeline_id, animation_state)
@ -1277,10 +1294,19 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// Notify the browser chrome that the pipeline has failed
self.trigger_mozbrowsererror(top_level_frame_id, reason, backtrace);
let pipeline_id = self.frames.get(&top_level_frame_id).map(|frame| frame.pipeline_id);
let pipeline_url = pipeline_id.and_then(|id| self.pipelines.get(&id).map(|pipeline| pipeline.url.clone()));
let parent_info = pipeline_id.and_then(|id| self.pipelines.get(&id).and_then(|pipeline| pipeline.parent_info));
let window_size = pipeline_id.and_then(|id| self.pipelines.get(&id).and_then(|pipeline| pipeline.size));
let (window_size, pipeline_id) = {
let frame = self.frames.get(&top_level_frame_id);
let window_size = frame.and_then(|frame| frame.size);
let pipeline_id = frame.map(|frame| frame.pipeline_id);
(window_size, pipeline_id)
};
let (pipeline_url, parent_info) = {
let pipeline = pipeline_id.and_then(|id| self.pipelines.get(&id));
let pipeline_url = pipeline.map(|pipeline| pipeline.url.clone());
let parent_info = pipeline.and_then(|pipeline| pipeline.parent_info);
(pipeline_url, parent_info)
};
self.close_frame_children(top_level_frame_id, DiscardBrowsingContext::No, ExitPipelineMode::Force);
@ -1295,7 +1321,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
warn!("creating replacement pipeline for about:failure");
let new_pipeline_id = PipelineId::new();
let load_data = LoadData::new(failure_url, None, None);
let load_data = LoadData::new(failure_url, None, None, None);
let sandbox = IFrameSandboxState::IFrameSandboxed;
self.new_pipeline(new_pipeline_id, top_level_frame_id, parent_info,
window_size, load_data.clone(), sandbox, false);
@ -1340,7 +1366,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let window_size = self.window_size.initial_viewport;
let root_pipeline_id = PipelineId::new();
let root_frame_id = self.root_frame_id;
let load_data = LoadData::new(url.clone(), None, None);
let load_data = LoadData::new(url.clone(), None, None, None);
let sandbox = IFrameSandboxState::IFrameUnsandboxed;
self.new_pipeline(root_pipeline_id, root_frame_id, None, Some(window_size), load_data.clone(), sandbox, false);
self.handle_load_start_msg(root_pipeline_id);
@ -1353,29 +1379,14 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
fn handle_frame_size_msg(&mut self,
iframe_sizes: Vec<(PipelineId, TypedSize2D<f32, CSSPixel>)>) {
for (pipeline_id, size) in iframe_sizes {
let result = {
let pipeline = match self.pipelines.get_mut(&pipeline_id) {
Some(pipeline) => pipeline,
None => continue,
};
if pipeline.size == Some(size) {
continue;
}
pipeline.size = Some(size);
let msg = ConstellationControlMsg::Resize(pipeline_id, WindowSizeData {
iframe_sizes: Vec<(FrameId, TypedSize2D<f32, CSSPixel>)>) {
for (frame_id, size) in iframe_sizes {
let window_size = WindowSizeData {
initial_viewport: size,
device_pixel_ratio: self.window_size.device_pixel_ratio,
}, WindowSizeType::Initial);
pipeline.event_loop.send(msg)
};
if let Err(e) = result {
self.handle_send_error(pipeline_id, e);
}
self.resize_frame(window_size, WindowSizeType::Initial, frame_id);
}
}
@ -1423,12 +1434,12 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
};
// TODO - loaddata here should have referrer info (not None, None)
LoadData::new(url, None, None)
LoadData::new(url, Some(source_pipeline.id), None, None)
});
let is_private = load_info.info.is_private || source_pipeline.is_private;
let window_size = old_pipeline.and_then(|old_pipeline| old_pipeline.size);
let window_size = self.frames.get(&load_info.info.frame_id).and_then(|frame| frame.size);
(load_data, window_size, is_private)
};
@ -1456,7 +1467,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
is_private);
}
fn handle_script_loaded_about_blank_in_iframe_msg(&mut self,
fn handle_script_new_iframe(&mut self,
load_info: IFrameLoadInfo,
layout_sender: IpcSender<LayoutControlMsg>) {
let IFrameLoadInfo {
@ -1486,12 +1497,11 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
self.compositor_proxy.clone_compositor_proxy(),
is_private || parent_pipeline.is_private,
url.clone(),
None,
parent_pipeline.visible)
};
// TODO: Referrer?
let load_data = LoadData::new(url, None, None);
let load_data = LoadData::new(url, Some(parent_pipeline_id), None, None);
let replace_instant = if replace {
self.frames.get(&frame_id).map(|frame| frame.instant)
@ -1641,7 +1651,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// changes would be overridden by changing the subframe associated with source_id.
// Create the new pipeline
let window_size = self.pipelines.get(&source_id).and_then(|source| source.size);
let window_size = self.frames.get(&root_frame_id).and_then(|frame| frame.size);
let new_pipeline_id = PipelineId::new();
let sandbox = IFrameSandboxState::IFrameUnsandboxed;
let replace_instant = if replace {
@ -1959,7 +1969,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
},
WebDriverCommandMsg::Refresh(pipeline_id, reply) => {
let load_data = match self.pipelines.get(&pipeline_id) {
Some(pipeline) => LoadData::new(pipeline.url.clone(), None, None),
Some(pipeline) => LoadData::new(pipeline.url.clone(), None, None, None),
None => return warn!("Pipeline {:?} Refresh after closure.", pipeline_id),
};
self.load_url_for_webdriver(pipeline_id, load_data, reply, true);
@ -2019,8 +2029,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
let load_data = entry.load_data;
let (parent_info, window_size, is_private) = match self.frames.get(&frame_id) {
Some(frame) => match self.pipelines.get(&frame.pipeline_id) {
Some(pipeline) => (pipeline.parent_info, pipeline.size, pipeline.is_private),
None => (None, None, false),
Some(pipeline) => (pipeline.parent_info, frame.size, pipeline.is_private),
None => (None, frame.size, false),
},
None => return warn!("no frame to traverse"),
};
@ -2101,7 +2111,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// Update the owning iframe to point to the new pipeline id.
// This makes things like contentDocument work correctly.
if let Some((parent_pipeline_id, _)) = parent_info {
let msg = ConstellationControlMsg::UpdatePipelineId(parent_pipeline_id, frame_id, pipeline_id);
let msg = ConstellationControlMsg::UpdatePipelineId(parent_pipeline_id,
frame_id, pipeline_id, UpdatePipelineIdReason::Traversal);
let result = match self.pipelines.get(&parent_pipeline_id) {
None => return warn!("Pipeline {:?} child traversed after closure.", parent_pipeline_id),
Some(pipeline) => pipeline.event_loop.send(msg),
@ -2245,7 +2256,9 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
}
if new_frame {
self.new_frame(frame_change.frame_id, frame_change.new_pipeline_id, frame_change.load_data);
self.new_frame(frame_change.frame_id,
frame_change.new_pipeline_id,
frame_change.load_data);
self.update_activity(frame_change.new_pipeline_id);
self.notify_history_changed(frame_change.new_pipeline_id);
};
@ -2275,7 +2288,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
if let Some(pipeline) = self.pipelines.get(&pipeline_id) {
if let Some((parent_pipeline_id, _)) = pipeline.parent_info {
if let Some(parent_pipeline) = self.pipelines.get(&parent_pipeline_id) {
let msg = ConstellationControlMsg::FramedContentChanged(parent_pipeline_id, pipeline.frame_id);
let msg = ConstellationControlMsg::UpdatePipelineId(parent_pipeline_id,
pipeline.frame_id, pipeline_id, UpdatePipelineIdReason::Navigation);
let _ = parent_pipeline.event_loop.send(msg);
}
}
@ -2298,45 +2312,8 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
fn handle_window_size_msg(&mut self, new_size: WindowSizeData, size_type: WindowSizeType) {
debug!("handle_window_size_msg: {:?}", new_size.initial_viewport.to_untyped());
if let Some(frame) = self.frames.get(&self.root_frame_id) {
// Send Resize (or ResizeInactive) messages to each
// pipeline in the frame tree.
let pipeline_id = frame.pipeline_id;
let pipeline = match self.pipelines.get(&pipeline_id) {
None => return warn!("Pipeline {:?} resized after closing.", pipeline_id),
Some(pipeline) => pipeline,
};
let _ = pipeline.event_loop.send(ConstellationControlMsg::Resize(
pipeline.id,
new_size,
size_type
));
let pipelines = frame.prev.iter().chain(frame.next.iter())
.filter_map(|entry| entry.pipeline_id)
.filter_map(|pipeline_id| self.pipelines.get(&pipeline_id));
for pipeline in pipelines {
let _ = pipeline.event_loop.send(ConstellationControlMsg::ResizeInactive(
pipeline.id,
new_size
));
}
}
// Send resize message to any pending pipelines that aren't loaded yet.
for pending_frame in &self.pending_frames {
let pipeline_id = pending_frame.new_pipeline_id;
let pipeline = match self.pipelines.get(&pipeline_id) {
None => { warn!("Pending pipeline {:?} is closed", pipeline_id); continue; }
Some(pipeline) => pipeline,
};
if pipeline.parent_info.is_none() {
let _ = pipeline.event_loop.send(ConstellationControlMsg::Resize(
pipeline.id,
new_size,
size_type
));
}
}
let frame_id = self.root_frame_id;
self.resize_frame(new_size, size_type, frame_id);
if let Some(resize_channel) = self.webdriver.resize_channel.take() {
let _ = resize_channel.send(new_size);
@ -2422,7 +2399,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
// size for the pipeline, then its painting should be up to date. If the constellation
// *hasn't* received a size, it could be that the layer was hidden by script before the
// compositor discovered it, so we just don't check the layer.
if let Some(size) = pipeline.size {
if let Some(size) = frame.size {
// If the rectangle for this pipeline is zero sized, it will
// never be painted. In this case, don't query the layout
// thread as it won't contribute to the final output image.
@ -2509,6 +2486,54 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
self.set_activity(pipeline_id, self.get_activity(pipeline_id));
}
/// Handle updating the size of a frame. This notifies every pipeline in the frame of the new
/// size.
fn resize_frame(&mut self, new_size: WindowSizeData, size_type: WindowSizeType, frame_id: FrameId) {
if let Some(frame) = self.frames.get_mut(&frame_id) {
frame.size = Some(new_size.initial_viewport);
}
if let Some(frame) = self.frames.get(&frame_id) {
// Send Resize (or ResizeInactive) messages to each
// pipeline in the frame tree.
let pipeline_id = frame.pipeline_id;
let pipeline = match self.pipelines.get(&pipeline_id) {
None => return warn!("Pipeline {:?} resized after closing.", pipeline_id),
Some(pipeline) => pipeline,
};
let _ = pipeline.event_loop.send(ConstellationControlMsg::Resize(
pipeline.id,
new_size,
size_type
));
let pipelines = frame.prev.iter().chain(frame.next.iter())
.filter_map(|entry| entry.pipeline_id)
.filter_map(|pipeline_id| self.pipelines.get(&pipeline_id));
for pipeline in pipelines {
let _ = pipeline.event_loop.send(ConstellationControlMsg::ResizeInactive(
pipeline.id,
new_size
));
}
}
// Send resize message to any pending pipelines that aren't loaded yet.
for pending_frame in &self.pending_frames {
let pipeline_id = pending_frame.new_pipeline_id;
let pipeline = match self.pipelines.get(&pipeline_id) {
None => { warn!("Pending pipeline {:?} is closed", pipeline_id); continue; }
Some(pipeline) => pipeline,
};
if pipeline.frame_id == frame_id {
let _ = pipeline.event_loop.send(ConstellationControlMsg::Resize(
pipeline.id,
new_size,
size_type
));
}
}
}
fn clear_joint_session_future(&mut self, frame_id: FrameId) {
let frame_ids: Vec<FrameId> = self.full_frame_tree_iter(frame_id)
.map(|frame| frame.id)
@ -2652,7 +2677,7 @@ impl<Message, LTF, STF> Constellation<Message, LTF, STF>
self.pipelines.get(&frame.pipeline_id).map(|pipeline: &Pipeline| {
let mut frame_tree = SendableFrameTree {
pipeline: pipeline.to_sendable(),
size: pipeline.size,
size: frame.size,
children: vec!(),
};

View file

@ -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 euclid::size::TypedSize2D;
use msg::constellation_msg::{FrameId, PipelineId};
use pipeline::Pipeline;
use script_traits::LoadData;
@ -9,6 +10,7 @@ use std::collections::HashMap;
use std::iter::once;
use std::mem::replace;
use std::time::Instant;
use style_traits::CSSPixel;
/// A frame in the frame tree.
/// Each frame is the constellation's view of a browsing context.
@ -22,13 +24,16 @@ pub struct Frame {
/// The frame id.
pub id: FrameId,
/// The timestamp for the current session history entry
/// The size of the frame.
pub size: Option<TypedSize2D<f32, CSSPixel>>,
/// The timestamp for the current session history entry.
pub instant: Instant,
/// The pipeline for the current session history entry
/// The pipeline for the current session history entry.
pub pipeline_id: PipelineId,
/// The load data for the current session history entry
/// The load data for the current session history entry.
pub load_data: LoadData,
/// The past session history, ordered chronologically.
@ -44,6 +49,7 @@ impl Frame {
pub fn new(id: FrameId, pipeline_id: PipelineId, load_data: LoadData) -> Frame {
Frame {
id: id,
size: None,
pipeline_id: pipeline_id,
instant: Instant::now(),
load_data: load_data,

View file

@ -76,10 +76,6 @@ pub struct Pipeline {
/// The title of the most recently-loaded page.
pub title: Option<String>,
/// The size of the frame.
/// TODO: move this field to `Frame`.
pub size: Option<TypedSize2D<f32, CSSPixel>>,
/// Whether this pipeline is currently running animations. Pipelines that are running
/// animations cause composites to be continually scheduled.
pub running_animations: bool,
@ -291,7 +287,6 @@ impl Pipeline {
state.compositor_proxy,
state.is_private,
url,
state.window_size,
state.prev_visibility.unwrap_or(true)))
}
@ -305,7 +300,6 @@ impl Pipeline {
compositor_proxy: Box<CompositorProxy + 'static + Send>,
is_private: bool,
url: ServoUrl,
size: Option<TypedSize2D<f32, CSSPixel>>,
visible: bool)
-> Pipeline {
let pipeline = Pipeline {
@ -318,7 +312,6 @@ impl Pipeline {
url: url,
title: None,
children: vec!(),
size: size,
running_animations: false,
visible: visible,
is_private: is_private,

View file

@ -34,7 +34,7 @@ use inline::{FIRST_FRAGMENT_OF_ELEMENT, InlineFlow, LAST_FRAGMENT_OF_ELEMENT};
use ipc_channel::ipc;
use list_item::ListItemFlow;
use model::{self, MaybeAuto, specified};
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::FrameId;
use net_traits::image::base::PixelFormat;
use net_traits::image_cache::UsePlaceholder;
use range::Range;
@ -173,7 +173,7 @@ pub struct DisplayListBuildState<'a> {
/// Vector containing iframe sizes, used to inform the constellation about
/// new iframe sizes
pub iframe_sizes: Vec<(PipelineId, TypedSize2D<f32, CSSPixel>)>,
pub iframe_sizes: Vec<(FrameId, TypedSize2D<f32, CSSPixel>)>,
/// A stack of clips used to cull display list entries that are outside the
/// rendered region.
@ -1809,7 +1809,7 @@ impl FragmentDisplayListBuilding for Fragment {
let size = Size2D::new(item.bounds().size.width.to_f32_px(),
item.bounds().size.height.to_f32_px());
state.iframe_sizes.push((fragment_info.pipeline_id, TypedSize2D::from_untyped(&size)));
state.iframe_sizes.push((fragment_info.frame_id, TypedSize2D::from_untyped(&size)));
state.add_display_item(item);
}

View file

@ -26,7 +26,7 @@ use ipc_channel::ipc::IpcSender;
use layout_debug;
use model::{self, IntrinsicISizes, IntrinsicISizesContribution, MaybeAuto, SizeConstraint};
use model::{style_length, ToGfxMatrix};
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::{FrameId, PipelineId};
use net_traits::image::base::{Image, ImageMetadata};
use net_traits::image_cache::{ImageOrMetadataAvailable, UsePlaceholder};
use range::*;
@ -467,19 +467,23 @@ impl ImageFragmentInfo {
}
}
/// A fragment that represents an inline frame (iframe). This stores the pipeline ID so that the
/// A fragment that represents an inline frame (iframe). This stores the frame ID so that the
/// size of this iframe can be communicated via the constellation to the iframe's own layout thread.
#[derive(Clone)]
pub struct IframeFragmentInfo {
/// The pipeline ID of this iframe.
/// The frame ID of this iframe.
pub frame_id: FrameId,
/// The pipelineID of this iframe.
pub pipeline_id: PipelineId,
}
impl IframeFragmentInfo {
/// Creates the information specific to an iframe fragment.
pub fn new<N: ThreadSafeLayoutNode>(node: &N) -> IframeFragmentInfo {
let frame_id = node.iframe_frame_id();
let pipeline_id = node.iframe_pipeline_id();
IframeFragmentInfo {
frame_id: frame_id,
pipeline_id: pipeline_id,
}
}

View file

@ -310,3 +310,4 @@ pub enum FrameType {
IFrame,
MozBrowserIFrame,
}

View file

@ -345,7 +345,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()));
let mut load_data = LoadData::new(action_components, None, doc.get_referrer_policy(), Some(doc.url()));
// Step 18
match (&*scheme, method) {

View file

@ -44,7 +44,7 @@ use msg::constellation_msg::{FrameType, FrameId, PipelineId, TraversalDirection}
use net_traits::response::HttpsState;
use script_layout_interface::message::ReflowQueryType;
use script_thread::{ScriptThread, Runnable};
use script_traits::{IFrameLoadInfo, IFrameLoadInfoWithData, LoadData};
use script_traits::{IFrameLoadInfo, IFrameLoadInfoWithData, LoadData, UpdatePipelineIdReason};
use script_traits::{MozBrowserEvent, NewLayoutInfo, ScriptMsg as ConstellationMsg};
use script_traits::IFrameSandboxState::{IFrameSandboxed, IFrameUnsandboxed};
use servo_atoms::Atom;
@ -69,6 +69,12 @@ bitflags! {
}
}
#[derive(PartialEq)]
pub enum NavigationType {
InitialAboutBlank,
Regular,
}
#[derive(PartialEq)]
enum ProcessingMode {
FirstTime,
@ -80,6 +86,7 @@ pub struct HTMLIFrameElement {
htmlelement: HTMLElement,
frame_id: FrameId,
pipeline_id: Cell<Option<PipelineId>>,
pending_pipeline_id: Cell<Option<PipelineId>>,
sandbox: MutNullableJS<DOMTokenList>,
sandbox_allowance: Cell<Option<SandboxAllowance>>,
load_blocker: DOMRefCell<Option<LoadBlocker>>,
@ -108,12 +115,14 @@ impl HTMLIFrameElement {
pub fn generate_new_pipeline_id(&self) -> (Option<PipelineId>, PipelineId) {
let old_pipeline_id = self.pipeline_id.get();
let new_pipeline_id = PipelineId::new();
self.pipeline_id.set(Some(new_pipeline_id));
debug!("Frame {} created pipeline {}.", self.frame_id, new_pipeline_id);
(old_pipeline_id, new_pipeline_id)
}
pub fn navigate_or_reload_child_browsing_context(&self, load_data: Option<LoadData>, replace: bool) {
pub fn navigate_or_reload_child_browsing_context(&self,
load_data: Option<LoadData>,
nav_type: NavigationType,
replace: bool) {
let sandboxed = if self.is_sandboxed() {
IFrameSandboxed
} else {
@ -136,6 +145,7 @@ impl HTMLIFrameElement {
let window = window_from_node(self);
let (old_pipeline_id, new_pipeline_id) = self.generate_new_pipeline_id();
self.pending_pipeline_id.set(Some(new_pipeline_id));
let private_iframe = self.privatebrowsing();
let frame_type = if self.Mozbrowser() { FrameType::MozBrowserIFrame } else { FrameType::IFrame };
@ -149,12 +159,13 @@ impl HTMLIFrameElement {
replace: replace,
};
if load_data.as_ref().map_or(false, |d| d.url.as_str() == "about:blank") {
match nav_type {
NavigationType::InitialAboutBlank => {
let (pipeline_sender, pipeline_receiver) = ipc::channel().unwrap();
global_scope
.constellation_chan()
.send(ConstellationMsg::ScriptLoadedAboutBlankInIFrame(load_info, pipeline_sender))
.send(ConstellationMsg::ScriptNewIFrame(load_info, pipeline_sender))
.unwrap();
let new_layout_info = NewLayoutInfo {
@ -168,8 +179,10 @@ impl HTMLIFrameElement {
layout_threads: PREFS.get("layout.threads").as_u64().expect("count") as usize,
};
self.pipeline_id.set(Some(new_pipeline_id));
ScriptThread::process_attach_layout(new_layout_info, document.origin().clone());
} else {
},
NavigationType::Regular => {
let load_info = IFrameLoadInfoWithData {
info: load_info,
load_data: load_data,
@ -181,6 +194,7 @@ impl HTMLIFrameElement {
.send(ConstellationMsg::ScriptLoadedURLInIFrame(load_info))
.unwrap();
}
}
if PREFS.is_mozbrowser_enabled() {
// https://developer.mozilla.org/en-US/docs/Web/Events/mozbrowserloadstart
@ -192,9 +206,10 @@ impl HTMLIFrameElement {
fn process_the_iframe_attributes(&self, mode: ProcessingMode) {
// TODO: srcdoc
let window = window_from_node(self);
// https://github.com/whatwg/html/issues/490
if mode == ProcessingMode::FirstTime && !self.upcast::<Element>().has_attribute(&local_name!("src")) {
let window = window_from_node(self);
let event_loop = window.dom_manipulation_task_source();
let _ = event_loop.queue(box IFrameLoadEventSteps::new(self),
window.upcast());
@ -205,9 +220,15 @@ impl HTMLIFrameElement {
// TODO: check ancestor browsing contexts for same URL
let creator_pipeline_id = if url.as_str() == "about:blank" {
Some(window.upcast::<GlobalScope>().pipeline_id())
} else {
None
};
let document = document_from_node(self);
self.navigate_or_reload_child_browsing_context(
Some(LoadData::new(url, document.get_referrer_policy(), Some(document.url()))), false);
let load_data = LoadData::new(url, creator_pipeline_id, document.get_referrer_policy(), Some(document.url()));
self.navigate_or_reload_child_browsing_context(Some(load_data), NavigationType::Regular, false);
}
#[allow(unsafe_code)]
@ -225,19 +246,30 @@ impl HTMLIFrameElement {
// Synchronously create a new context and navigate it to about:blank.
let url = ServoUrl::parse("about:blank").unwrap();
let document = document_from_node(self);
let load_data = LoadData::new(url,
document.get_referrer_policy(),
Some(document.url().clone()));
self.navigate_or_reload_child_browsing_context(Some(load_data), false);
let pipeline_id = Some(window_from_node(self).upcast::<GlobalScope>().pipeline_id());
let load_data = LoadData::new(url, pipeline_id, document.get_referrer_policy(), Some(document.url().clone()));
self.navigate_or_reload_child_browsing_context(Some(load_data), NavigationType::InitialAboutBlank, false);
}
pub fn update_pipeline_id(&self, new_pipeline_id: PipelineId, reason: UpdatePipelineIdReason) {
if self.pending_pipeline_id.get() != Some(new_pipeline_id) && reason == UpdatePipelineIdReason::Navigation {
return;
}
pub fn update_pipeline_id(&self, new_pipeline_id: PipelineId) {
self.pipeline_id.set(Some(new_pipeline_id));
// Only terminate the load blocker if the pipeline id was updated due to a traversal.
// The load blocker will be terminated for a navigation in iframe_load_event_steps.
if reason == UpdatePipelineIdReason::Traversal {
let mut blocker = self.load_blocker.borrow_mut();
LoadBlocker::terminate(&mut blocker);
}
self.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
let window = window_from_node(self);
window.reflow(ReflowGoal::ForDisplay,
ReflowQueryType::NoQuery,
ReflowReason::FramedContentChanged);
}
fn new_inherited(local_name: LocalName,
@ -247,6 +279,7 @@ impl HTMLIFrameElement {
htmlelement: HTMLElement::new_inherited(local_name, prefix, document),
frame_id: FrameId::new(),
pipeline_id: Cell::new(None),
pending_pipeline_id: Cell::new(None),
sandbox: Default::default(),
sandbox_allowance: Cell::new(None),
load_blocker: DOMRefCell::new(None),
@ -296,7 +329,7 @@ impl HTMLIFrameElement {
pub fn iframe_load_event_steps(&self, loaded_pipeline: PipelineId) {
// TODO(#9592): assert that the load blocker is present at all times when we
// can guarantee that it's created for the case of iframe.reload().
if Some(loaded_pipeline) != self.pipeline_id() { return; }
if Some(loaded_pipeline) != self.pending_pipeline_id.get() { return; }
// TODO A cross-origin child document would not be easily accessible
// from this script thread. It's unclear how to implement
@ -330,7 +363,8 @@ impl HTMLIFrameElement {
}
pub trait HTMLIFrameElementLayoutMethods {
fn pipeline_id(self) -> Option<PipelineId>;
fn pipeline_id(&self) -> Option<PipelineId>;
fn frame_id(&self) -> FrameId;
fn get_width(&self) -> LengthOrPercentageOrAuto;
fn get_height(&self) -> LengthOrPercentageOrAuto;
}
@ -338,12 +372,21 @@ pub trait HTMLIFrameElementLayoutMethods {
impl HTMLIFrameElementLayoutMethods for LayoutJS<HTMLIFrameElement> {
#[inline]
#[allow(unsafe_code)]
fn pipeline_id(self) -> Option<PipelineId> {
fn pipeline_id(&self) -> Option<PipelineId> {
unsafe {
(*self.unsafe_get()).pipeline_id.get()
}
}
#[inline]
#[allow(unsafe_code)]
fn frame_id(&self) -> FrameId {
unsafe {
(*self.unsafe_get()).frame_id
}
}
#[allow(unsafe_code)]
fn get_width(&self) -> LengthOrPercentageOrAuto {
unsafe {
@ -563,7 +606,7 @@ impl HTMLIFrameElementMethods for HTMLIFrameElement {
fn Reload(&self, _hard_reload: bool) -> ErrorResult {
if self.Mozbrowser() {
if self.upcast::<Node>().is_in_doc_with_browsing_context() {
self.navigate_or_reload_child_browsing_context(None, true);
self.navigate_or_reload_child_browsing_context(None, NavigationType::Regular, true);
}
Ok(())
} else {
@ -739,6 +782,7 @@ impl VirtualMethods for HTMLIFrameElement {
// a new iframe. Without this, the constellation gets very
// confused.
self.pipeline_id.set(None);
self.pending_pipeline_id.set(None);
}
}

View file

@ -61,7 +61,7 @@ use heapsize::{HeapSizeOf, heap_size_of};
use html5ever::{Prefix, Namespace, QualName};
use js::jsapi::{JSContext, JSObject, JSRuntime};
use libc::{self, c_void, uintptr_t};
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::{FrameId, PipelineId};
use ref_slice::ref_slice;
use script_layout_interface::{HTMLCanvasData, OpaqueStyleAndLayoutData, SVGSVGData};
use script_layout_interface::{LayoutElementType, LayoutNodeType, TrustedNodeAddress};
@ -970,6 +970,7 @@ pub trait LayoutNodeHelpers {
fn image_url(&self) -> Option<ServoUrl>;
fn canvas_data(&self) -> Option<HTMLCanvasData>;
fn svg_data(&self) -> Option<SVGSVGData>;
fn iframe_frame_id(&self) -> FrameId;
fn iframe_pipeline_id(&self) -> PipelineId;
fn opaque(&self) -> OpaqueNode;
}
@ -1120,6 +1121,12 @@ impl LayoutNodeHelpers for LayoutJS<Node> {
.map(|svg| svg.data())
}
fn iframe_frame_id(&self) -> FrameId {
let iframe_element = self.downcast::<HTMLIFrameElement>()
.expect("not an iframe element!");
iframe_element.frame_id()
}
fn iframe_pipeline_id(&self) -> PipelineId {
let iframe_element = self.downcast::<HTMLIFrameElement>()
.expect("not an iframe element!");

View file

@ -1537,10 +1537,10 @@ impl Window {
}
}
let pipeline_id = self.upcast::<GlobalScope>().pipeline_id();
self.main_thread_script_chan().send(
MainThreadScriptMsg::Navigate(self.upcast::<GlobalScope>().pipeline_id(),
LoadData::new(url, referrer_policy, Some(doc.url())),
replace)).unwrap();
MainThreadScriptMsg::Navigate(pipeline_id,
LoadData::new(url, Some(pipeline_id), referrer_policy, Some(doc.url())), replace)).unwrap();
}
pub fn handle_fire_timer(&self, timer_id: TimerEventId) {

View file

@ -44,7 +44,7 @@ use dom::node::{LayoutNodeHelpers, Node};
use dom::text::Text;
use gfx_traits::ByteIndex;
use html5ever::{LocalName, Namespace};
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::{FrameId, PipelineId};
use range::Range;
use script_layout_interface::{HTMLCanvasData, LayoutNodeType, SVGSVGData, TrustedNodeAddress};
use script_layout_interface::{OpaqueStyleAndLayoutData, PartialPersistentLayoutData};
@ -908,6 +908,11 @@ impl<'ln> ThreadSafeLayoutNode for ServoThreadSafeLayoutNode<'ln> {
this.svg_data()
}
fn iframe_frame_id(&self) -> FrameId {
let this = unsafe { self.get_jsmanaged() };
this.iframe_frame_id()
}
fn iframe_pipeline_id(&self) -> PipelineId {
let this = unsafe { self.get_jsmanaged() };
this.iframe_pipeline_id()

View file

@ -46,7 +46,7 @@ use dom::element::Element;
use dom::event::{Event, EventBubbles, EventCancelable};
use dom::globalscope::GlobalScope;
use dom::htmlanchorelement::HTMLAnchorElement;
use dom::htmliframeelement::HTMLIFrameElement;
use dom::htmliframeelement::{HTMLIFrameElement, NavigationType};
use dom::mutationobserver::MutationObserver;
use dom::node::{Node, NodeDamage, window_from_node};
use dom::serviceworker::TrustedServiceWorkerAddress;
@ -87,7 +87,7 @@ use script_runtime::{ScriptPort, StackRootTLS, get_reports, new_rt_and_cx};
use script_traits::{CompositorEvent, ConstellationControlMsg};
use script_traits::{DocumentActivity, DiscardBrowsingContext, EventResult};
use script_traits::{InitialScriptState, LayoutMsg, LoadData, MouseButton, MouseEventType, MozBrowserEvent};
use script_traits::{NewLayoutInfo, ScriptMsg as ConstellationMsg};
use script_traits::{NewLayoutInfo, ScriptMsg as ConstellationMsg, UpdatePipelineIdReason};
use script_traits::{ScriptThreadFactory, TimerEvent, TimerSchedulerMsg, TimerSource};
use script_traits::{TouchEventType, TouchId, UntrustedNodeAddress, WindowSizeData, WindowSizeType};
use script_traits::CompositorEvent::{KeyEvent, MouseButtonEvent, MouseMoveEvent, ResizeEvent};
@ -555,8 +555,8 @@ impl ScriptThreadFactory for ScriptThread {
let mut failsafe = ScriptMemoryFailsafe::new(&script_thread);
let origin = MutableOrigin::new(load_data.url.origin());
let new_load = InProgressLoad::new(id, frame_id, parent_info, layout_chan, window_size,
load_data.url.clone(), origin);
let new_load = InProgressLoad::new(id, frame_id, parent_info,
layout_chan, window_size, load_data.url.clone(), origin);
script_thread.start_page_load(new_load, load_data);
let reporter_name = format!("script-reporter-{}", id);
@ -827,7 +827,22 @@ impl ScriptThread {
FromConstellation(ConstellationControlMsg::AttachLayout(
new_layout_info)) => {
self.profile_event(ScriptThreadEventCategory::AttachLayout, || {
let origin = MutableOrigin::new(new_layout_info.load_data.url.origin());
// If this is an about:blank load, it must share the creator's origin.
// This must match the logic in the constellation when creating a new pipeline
let origin = if new_layout_info.load_data.url.as_str() != "about:blank" {
MutableOrigin::new(new_layout_info.load_data.url.origin())
} else if let Some(parent) = new_layout_info.parent_info
.and_then(|(pipeline_id, _)| self.documents.borrow()
.find_document(pipeline_id)) {
parent.origin().clone()
} else if let Some(creator) = new_layout_info.load_data.creator_pipeline_id
.and_then(|pipeline_id| self.documents.borrow()
.find_document(pipeline_id)) {
creator.origin().clone()
} else {
MutableOrigin::new(ImmutableOrigin::new_opaque())
};
self.handle_new_layout(new_layout_info, origin);
})
}
@ -1043,10 +1058,12 @@ impl ScriptThread {
event),
ConstellationControlMsg::UpdatePipelineId(parent_pipeline_id,
frame_id,
new_pipeline_id) =>
new_pipeline_id,
reason) =>
self.handle_update_pipeline_id(parent_pipeline_id,
frame_id,
new_pipeline_id),
new_pipeline_id,
reason),
ConstellationControlMsg::FocusIFrame(parent_pipeline_id, frame_id) =>
self.handle_focus_iframe_msg(parent_pipeline_id, frame_id),
ConstellationControlMsg::WebDriverScriptCommand(pipeline_id, msg) =>
@ -1062,8 +1079,6 @@ impl ScriptThread {
self.handle_frame_load_event(parent_id, frame_id, child_id),
ConstellationControlMsg::DispatchStorageEvent(pipeline_id, storage, url, key, old_value, new_value) =>
self.handle_storage_event(pipeline_id, storage, url, key, old_value, new_value),
ConstellationControlMsg::FramedContentChanged(parent_pipeline_id, frame_id) =>
self.handle_framed_content_changed(parent_pipeline_id, frame_id),
ConstellationControlMsg::ReportCSSError(pipeline_id, filename, line, column, msg) =>
self.handle_css_error_reporting(pipeline_id, filename, line, column, msg),
ConstellationControlMsg::Reload(pipeline_id) =>
@ -1399,20 +1414,6 @@ impl ScriptThread {
}
}
fn handle_framed_content_changed(&self,
parent_pipeline_id: PipelineId,
frame_id: FrameId) {
let doc = self.documents.borrow().find_document(parent_pipeline_id).unwrap();
let frame_element = doc.find_iframe(frame_id);
if let Some(ref frame_element) = frame_element {
frame_element.upcast::<Node>().dirty(NodeDamage::OtherNodeDamage);
let window = doc.window();
window.reflow(ReflowGoal::ForDisplay,
ReflowQueryType::NoQuery,
ReflowReason::FramedContentChanged);
}
}
fn handle_post_message_msg(&self, pipeline_id: PipelineId, origin: Option<ImmutableOrigin>, data: Vec<u8>) {
match { self.documents.borrow().find_window(pipeline_id) } {
None => return warn!("postMessage after pipeline {} closed.", pipeline_id),
@ -1443,10 +1444,11 @@ impl ScriptThread {
fn handle_update_pipeline_id(&self,
parent_pipeline_id: PipelineId,
frame_id: FrameId,
new_pipeline_id: PipelineId) {
new_pipeline_id: PipelineId,
reason: UpdatePipelineIdReason) {
let frame_element = self.documents.borrow().find_iframe(parent_pipeline_id, frame_id);
if let Some(frame_element) = frame_element {
frame_element.update_pipeline_id(new_pipeline_id);
frame_element.update_pipeline_id(new_pipeline_id, reason);
}
}
@ -2065,7 +2067,7 @@ impl ScriptThread {
Some(frame_id) => {
let iframe = self.documents.borrow().find_iframe(parent_pipeline_id, frame_id);
if let Some(iframe) = iframe {
iframe.navigate_or_reload_child_browsing_context(Some(load_data), replace);
iframe.navigate_or_reload_child_browsing_context(Some(load_data), NavigationType::Regular, replace);
}
}
None => {

View file

@ -11,7 +11,7 @@ use SVGSVGData;
use atomic_refcell::AtomicRefCell;
use gfx_traits::{ByteIndex, FragmentType, combine_id_with_fragment_type};
use html5ever::{Namespace, LocalName};
use msg::constellation_msg::PipelineId;
use msg::constellation_msg::{FrameId, PipelineId};
use range::Range;
use servo_url::ServoUrl;
use std::fmt::Debug;
@ -271,6 +271,10 @@ pub trait ThreadSafeLayoutNode: Clone + Copy + Debug + GetLayoutData + NodeInfo
fn svg_data(&self) -> Option<SVGSVGData>;
/// If this node is an iframe element, returns its frame ID. If this node is
/// not an iframe element, fails.
fn iframe_frame_id(&self) -> FrameId;
/// If this node is an iframe element, returns its pipeline ID. If this node is
/// not an iframe element, fails.
fn iframe_pipeline_id(&self) -> PipelineId;

View file

@ -134,6 +134,8 @@ pub enum LayoutControlMsg {
pub struct LoadData {
/// The URL.
pub url: ServoUrl,
/// The creator pipeline id if this is an about:blank load.
pub creator_pipeline_id: Option<PipelineId>,
/// The method.
#[serde(deserialize_with = "::hyper_serde::deserialize",
serialize_with = "::hyper_serde::serialize")]
@ -152,9 +154,14 @@ pub struct LoadData {
impl LoadData {
/// Create a new `LoadData` object.
pub fn new(url: ServoUrl, referrer_policy: Option<ReferrerPolicy>, referrer_url: Option<ServoUrl>) -> LoadData {
pub fn new(url: ServoUrl,
creator_pipeline_id: Option<PipelineId>,
referrer_policy: Option<ReferrerPolicy>,
referrer_url: Option<ServoUrl>)
-> LoadData {
LoadData {
url: url,
creator_pipeline_id: creator_pipeline_id,
method: Method::Get,
headers: Headers::new(),
data: None,
@ -211,6 +218,15 @@ pub enum DocumentActivity {
FullyActive,
}
/// The reason why the pipeline id of an iframe is being updated.
#[derive(Copy, Clone, PartialEq, Eq, Hash, HeapSizeOf, Debug, Deserialize, Serialize)]
pub enum UpdatePipelineIdReason {
/// The pipeline id is being updated due to a navigation.
Navigation,
/// The pipeline id is being updated due to a history traversal.
Traversal,
}
/// Messages sent from the constellation or layout to the script thread.
#[derive(Deserialize, Serialize)]
pub enum ConstellationControlMsg {
@ -249,7 +265,7 @@ pub enum ConstellationControlMsg {
MozBrowserEvent(PipelineId, Option<FrameId>, MozBrowserEvent),
/// Updates the current pipeline ID of a given iframe.
/// First PipelineId is for the parent, second is the new PipelineId for the frame.
UpdatePipelineId(PipelineId, FrameId, PipelineId),
UpdatePipelineId(PipelineId, FrameId, PipelineId, UpdatePipelineIdReason),
/// Set an iframe to be focused. Used when an element in an iframe gains focus.
/// PipelineId is for the parent, FrameId is for the actual frame.
FocusIFrame(PipelineId, FrameId),
@ -274,9 +290,6 @@ pub enum ConstellationControlMsg {
/// Cause a `storage` event to be dispatched at the appropriate window.
/// The strings are key, old value and new value.
DispatchStorageEvent(PipelineId, StorageType, ServoUrl, Option<String>, Option<String>, Option<String>),
/// Notifies a parent pipeline that one of its child frames is now active.
/// PipelineId is for the parent, FrameId is the child frame.
FramedContentChanged(PipelineId, FrameId),
/// Report an error from a CSS parser for the given pipeline
ReportCSSError(PipelineId, String, usize, usize, String),
/// Reload the given page.
@ -312,7 +325,6 @@ impl fmt::Debug for ConstellationControlMsg {
WebFontLoaded(..) => "WebFontLoaded",
DispatchFrameLoadEvent { .. } => "DispatchFrameLoadEvent",
DispatchStorageEvent(..) => "DispatchStorageEvent",
FramedContentChanged(..) => "FramedContentChanged",
ReportCSSError(..) => "ReportCSSError",
Reload(..) => "Reload",
WebVREvents(..) => "WebVREvents",

View file

@ -34,8 +34,8 @@ use webrender_traits::ClipId;
pub enum LayoutMsg {
/// Indicates whether this pipeline is currently running animations.
ChangeRunningAnimationsState(PipelineId, AnimationState),
/// Inform the constellation of the size of the pipeline's viewport.
FrameSizes(Vec<(PipelineId, TypedSize2D<f32, CSSPixel>)>),
/// Inform the constellation of the size of the frame's viewport.
FrameSizes(Vec<(FrameId, TypedSize2D<f32, CSSPixel>)>),
/// Requests that the constellation inform the compositor of the a cursor change.
SetCursor(Cursor),
/// Notifies the constellation that the viewport has been constrained in some manner
@ -120,8 +120,8 @@ pub enum ScriptMsg {
VisibilityChangeComplete(PipelineId, bool),
/// A load has been requested in an IFrame.
ScriptLoadedURLInIFrame(IFrameLoadInfoWithData),
/// A load of `about:blank` has been completed in an IFrame.
ScriptLoadedAboutBlankInIFrame(IFrameLoadInfo, IpcSender<LayoutControlMsg>),
/// A load of the initial `about:blank` has been completed in an IFrame.
ScriptNewIFrame(IFrameLoadInfo, IpcSender<LayoutControlMsg>),
/// Requests that the constellation set the contents of the clipboard
SetClipboardContents(String),
/// Mark a new document as active

View file

@ -357,7 +357,7 @@ impl Handler {
let (sender, receiver) = ipc::channel().unwrap();
let load_data = LoadData::new(url, None, None);
let load_data = LoadData::new(url, Some(pipeline_id), None, None);
let cmd_msg = WebDriverCommandMsg::LoadUrl(pipeline_id, load_data, sender.clone());
self.constellation_chan.send(ConstellationMsg::WebDriverCommand(cmd_msg)).unwrap();

View file

@ -331418,6 +331418,12 @@
{}
]
],
"html/semantics/embedded-content/the-iframe-element/content_document_changes_only_after_load_matures.html": [
[
"/html/semantics/embedded-content/the-iframe-element/content_document_changes_only_after_load_matures.html",
{}
]
],
"html/semantics/embedded-content/the-iframe-element/cross_origin_parentage.html": [
[
"/html/semantics/embedded-content/the-iframe-element/cross_origin_parentage.html",
@ -570683,6 +570689,10 @@
"56df0cb79a1af927a0209c0bbbb5edb25ccaee5f",
"testharness"
],
"html/semantics/embedded-content/the-iframe-element/content_document_changes_only_after_load_matures.html": [
"46708fc218e559fba7049a36888a7c8a24c22672",
"testharness"
],
"html/semantics/embedded-content/the-iframe-element/cross_origin_child.html": [
"a42082bb612b280eda5aa598ed750cfce3edd537",
"support"

View file

@ -1,4 +1,5 @@
[matchMedia.html]
disabled: true
type: testharness
[window.matchMedia exists]
expected: FAIL

View file

@ -0,0 +1,21 @@
<!doctype html>
<meta charset="utf-8">
<title>Iframe's contentDocument should only change after its pending load has matured.</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<body></body>
<script>
async_test(function(t) {
var iframe = document.createElement("iframe");
document.body.appendChild(iframe);
iframe.onload = t.step_func(function() {
assert_true(iframe.contentDocument.location.toString().includes("support/blank.htm"));
t.done();
});
assert_equals(iframe.contentDocument.location.toString(), "about:blank");
iframe.src = "support/blank.htm?pipe=trickle(d2)";
// The location of the contentDocument should not change until the new document has matured.
assert_equals(iframe.contentDocument.location.toString(), "about:blank");
}, "contentDocument should only change after a load matures.");
</script>