servo: Merge #13489 - Add support for fullscreen #10102 (from farodin91:fullscreen); r=jdm
authorJansen Jan <farodin91@sek-server.de>
Fri, 09 Dec 2016 09:52:34 -0800
changeset 389282 088b220230d754d962ef695873042c7cf8ed3f43
parent 389281 e9324af352ad6a215543c5d0735ea615fa00c554
child 389283 fc94a472712b69aea05a8a164c0078949b848687
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdm
servo: Merge #13489 - Add support for fullscreen #10102 (from farodin91:fullscreen); r=jdm <!-- Please describe your changes on the following line: --> I'm start working on fullscreen support. @jdm Should be the entry_point in ScriptReflow a Option if fullscreen is enabled or point on the entry_node? For example the RootNode. --- <!-- 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 #10102 (github issue number if applicable). <!-- Either: --> - [x] There are tests for these changes OR - [ ] These changes do not require tests because _____ <!-- 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: 8b69e73594647319e95bd0fd36c2addabcee1e5d
servo/Cargo.lock
servo/components/atoms/static_atoms.txt
servo/components/compositing/compositor.rs
servo/components/compositing/compositor_thread.rs
servo/components/compositing/windowing.rs
servo/components/constellation/constellation.rs
servo/components/profile/time.rs
servo/components/profile_traits/time.rs
servo/components/script/dom/document.rs
servo/components/script/dom/element.rs
servo/components/script/dom/htmliframeelement.rs
servo/components/script/dom/webidls/Document.webidl
servo/components/script/dom/webidls/Element.webidl
servo/components/script/dom/webidls/HTMLIFrameElement.webidl
servo/components/script/layout_wrapper.rs
servo/components/script/script_runtime.rs
servo/components/script/script_thread.rs
servo/components/script_traits/script_msg.rs
servo/components/style/element_state.rs
servo/components/style/gecko/selector_parser.rs
servo/components/style/gecko/wrapper.rs
servo/components/style/servo/selector_parser.rs
servo/ports/cef/window.rs
servo/ports/glutin/window.rs
servo/resources/user-agent.css
--- a/servo/Cargo.lock
+++ b/servo/Cargo.lock
@@ -1070,30 +1070,30 @@ dependencies = [
 
 [[package]]
 name = "html5ever"
 version = "0.10.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "quote 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "syn 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "tendril 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "html5ever-atoms"
-version = "0.1.1"
+version = "0.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
  "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "string_cache_codegen 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
@@ -1267,17 +1267,17 @@ dependencies = [
  "canvas_traits 0.0.1",
  "cssparser 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "gfx 0.0.1",
  "gfx_traits 0.0.1",
  "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
  "net_traits 0.0.1",
  "ordered-float 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "plugins 0.0.1",
@@ -2184,17 +2184,17 @@ dependencies = [
  "devtools_traits 0.0.1",
  "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "gfx_traits 0.0.1",
  "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "html5ever 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)",
  "hyper_serde 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "image 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "js 0.1.3 (git+https://github.com/servo/rust-mozjs)",
  "jstraceable_derive 0.0.1",
  "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2242,17 +2242,17 @@ dependencies = [
  "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "canvas_traits 0.0.1",
  "cssparser 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "gfx_traits 0.0.1",
  "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "ipc-channel 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "libc 0.2.17 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "msg 0.0.1",
  "net_traits 0.0.1",
  "plugins 0.0.1",
  "profile_traits 0.0.1",
  "range 0.0.1",
@@ -2593,17 +2593,17 @@ dependencies = [
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cfg-if 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "fnv 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)",
  "heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
- "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "matches 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "nsstring_vendor 0.1.0",
  "num-integer 0.1.32 (registry+https://github.com/rust-lang/crates.io-index)",
  "num-traits 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
  "num_cpus 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
@@ -2630,17 +2630,17 @@ dependencies = [
 
 [[package]]
 name = "style_tests"
 version = "0.0.1"
 dependencies = [
  "app_units 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "cssparser 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)",
  "euclid 0.10.2 (registry+https://github.com/rust-lang/crates.io-index)",
- "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "owning_ref 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "parking_lot 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "selectors 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "servo_atoms 0.0.1",
  "servo_url 0.0.1",
  "style 0.0.1",
  "style_traits 0.0.1",
@@ -3187,17 +3187,17 @@ dependencies = [
  "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
 
 [[package]]
 name = "xml5ever"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 dependencies = [
- "html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
+ "html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
  "mac 0.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "phf_codegen 0.7.20 (registry+https://github.com/rust-lang/crates.io-index)",
  "rustc-serialize 0.3.19 (registry+https://github.com/rust-lang/crates.io-index)",
  "tendril 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)",
  "time 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)",
 ]
@@ -3285,17 +3285,17 @@ dependencies = [
 "checksum glx 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b280007fa9c7442cfd1e0b1addb8d1a59240267110e8705f8f7e2c7bfb7e2f72"
 "checksum harfbuzz-sys 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "6b76113246f5c089dcf272cf89c3f61168a4d77b50ec5b2c1fab8c628c9ea762"
 "checksum heapsize 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8c80e194758495a9109566134dc06e42ea0423987d6ceca016edaa90381b3549"
 "checksum heapsize_derive 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "4b6876925b6c3de6f9073f016f425de0076ab68cf30522107fa586ae6524abfe"
 "checksum heartbeats-simple 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "78c0810722eacd0bdd3f1f691524bd9900bf8fed1947f6b883c10ddecd2560b1"
 "checksum heartbeats-simple-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "53c4b67617665d7f4172f381f9843c1bec6a4fccc9a9226529e5b1be40dc1301"
 "checksum hpack 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3d2da7d3a34cf6406d9d700111b8eafafe9a251de41ae71d8052748259343b58"
 "checksum html5ever 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "45815593feb142cf01121b9f413d8630c9902192d160e494a579c50628eef498"
-"checksum html5ever-atoms 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "daefa106438c66af58309c84842b5db1df2733fe35849f39adde6fdf63583d40"
+"checksum html5ever-atoms 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "fd3fc831590ee7fcf693c673e4e3cbe14fbda44dc0f26d9bdc79cfc9f551dc05"
 "checksum httparse 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "46534074dbb80b070d60a5cb8ecadd8963a00a438ae1a95268850a7ef73b67ae"
 "checksum hyper 0.9.11 (registry+https://github.com/rust-lang/crates.io-index)" = "edd47c66782933e546a32ae89ca3c49263b2ba9bc29f3a0d5c52fff48e0ac67c"
 "checksum hyper_serde 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "572d2168173019de312a050a24f2ad33ac2ac7895a2139fbf21ee6b6f470a24e"
 "checksum idna 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1053236e00ce4f668aeca4a769a09b3bf5a682d802abd6f3cb39374f6b162c11"
 "checksum image 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)" = "559d5ebbe9ec73111799e49c07717944b244f8accf5de33a8a8128bc3ecd2e00"
 "checksum immeta 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "3e76ecb1d64979a91c7fc5b7c0495ef1467e3cbff759044f2b88878a5a845ef7"
 "checksum inflate 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e7e0062d2dc2f17d2f13750d95316ae8a2ff909af0fda957084f5defd87c43bb"
 "checksum io-surface 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3c93eb4952ee5b903c4193391779f90209e1b75ba55911097fa494f35e975846"
--- a/servo/components/atoms/static_atoms.txt
+++ b/servo/components/atoms/static_atoms.txt
@@ -73,8 +73,10 @@ afterscriptexecute
 invalid
 change
 open
 toggle
 statechange
 controllerchange
 fetch
 characteristicvaluechanged
+fullscreenchange
+fullscreenerror
--- a/servo/components/compositing/compositor.rs
+++ b/servo/components/compositing/compositor.rs
@@ -628,16 +628,20 @@ impl<Window: WindowMethods> IOCompositor
             }
 
             (Msg::Dispatch(func), ShutdownState::NotShuttingDown) => {
                 // The functions sent here right now are really dumb, so they can't panic.
                 // But if we start running more complex code here, we should really catch panic here.
                 func();
             }
 
+            (Msg::SetFullscreenState(state), ShutdownState::NotShuttingDown) => {
+                self.window.set_fullscreen_state(state);
+            }
+
             // When we are shutting_down, we need to avoid performing operations
             // such as Paint that may crash because we have begun tearing down
             // the rest of our resources.
             (_, ShutdownState::ShuttingDown) => { }
         }
 
         true
     }
--- a/servo/components/compositing/compositor_thread.rs
+++ b/servo/components/compositing/compositor_thread.rs
@@ -123,17 +123,19 @@ pub enum Msg {
     // This message acts as a synchronization point between the constellation,
     // when it shuts down a pipeline, to the compositor; when the compositor
     // sends a reply on the IpcSender, the constellation knows it's safe to
     // tear down the other threads associated with this pipeline.
     PipelineExited(PipelineId, IpcSender<()>),
     /// Runs a closure in the compositor thread.
     /// It's used to dispatch functions from webrender to the main thread's event loop.
     /// Required to allow WGL GLContext sharing in Windows.
-    Dispatch(Box<Fn() + Send>)
+    Dispatch(Box<Fn() + Send>),
+    /// Enter or exit fullscreen
+    SetFullscreenState(bool),
 }
 
 impl Debug for Msg {
     fn fmt(&self, f: &mut Formatter) -> Result<(), Error> {
         match *self {
             Msg::Exit => write!(f, "Exit"),
             Msg::ShutdownComplete => write!(f, "ShutdownComplete"),
             Msg::ScrollFragmentPoint(..) => write!(f, "ScrollFragmentPoint"),
@@ -156,16 +158,17 @@ impl Debug for Msg {
             Msg::Status(..) => write!(f, "Status"),
             Msg::GetClientWindow(..) => write!(f, "GetClientWindow"),
             Msg::MoveTo(..) => write!(f, "MoveTo"),
             Msg::ResizeTo(..) => write!(f, "ResizeTo"),
             Msg::PipelineVisibilityChanged(..) => write!(f, "PipelineVisibilityChanged"),
             Msg::PipelineExited(..) => write!(f, "PipelineExited"),
             Msg::NewScrollFrameReady(..) => write!(f, "NewScrollFrameReady"),
             Msg::Dispatch(..) => write!(f, "Dispatch"),
+            Msg::SetFullscreenState(..) => write!(f, "SetFullscreenState"),
         }
     }
 }
 
 /// Data used to construct a compositor.
 pub struct InitialCompositorState {
     /// A channel to the compositor.
     pub sender: Box<CompositorProxy + Send>,
--- a/servo/components/compositing/windowing.rs
+++ b/servo/components/compositing/windowing.rs
@@ -114,16 +114,18 @@ pub trait WindowMethods {
     fn present(&self);
 
     /// Return the size of the window with head and borders and position of the window values
     fn client_window(&self) -> (Size2D<u32>, Point2D<i32>);
     /// Set the size inside of borders and head
     fn set_inner_size(&self, size: Size2D<u32>);
     /// Set the window position
     fn set_position(&self, point: Point2D<i32>);
+    /// Set fullscreen state
+    fn set_fullscreen_state(&self, state: bool);
 
     /// Sets the page title for the current page.
     fn set_page_title(&self, title: Option<String>);
     /// Sets the load data for the current page.
     fn set_page_url(&self, url: ServoUrl);
     /// Called when the browser chrome should display a status message.
     fn status(&self, Option<String>);
     /// Called when the browser has started loading a frame.
--- a/servo/components/constellation/constellation.rs
+++ b/servo/components/constellation/constellation.rs
@@ -1085,16 +1085,19 @@ impl<Message, LTF, STF> Constellation<Me
                     let _ = mgr.send(ServiceWorkerMsg::ForwardDOMMessage(msg_vec, scope_url));
                 } else {
                     warn!("Unable to forward DOMMessage for postMessage call");
                 }
             }
             FromScriptMsg::BroadcastStorageEvent(pipeline_id, storage, url, key, old_value, new_value) => {
                 self.handle_broadcast_storage_event(pipeline_id, storage, url, key, old_value, new_value);
             }
+            FromScriptMsg::SetFullscreenState(state) => {
+                self.compositor_proxy.send(ToCompositorMsg::SetFullscreenState(state));
+            }
         }
     }
 
     fn handle_request_from_layout(&mut self, message: FromLayoutMsg) {
         match message {
             FromLayoutMsg::ChangeRunningAnimationsState(pipeline_id, animation_state) => {
                 self.handle_change_running_animations_state(pipeline_id, animation_state)
             }
--- a/servo/components/profile/time.rs
+++ b/servo/components/profile/time.rs
@@ -145,16 +145,18 @@ impl Formattable for ProfilerCategory {
             ProfilerCategory::ScriptUpdateReplacedElement => "Script Update Replaced Element",
             ProfilerCategory::ScriptSetScrollState => "Script Set Scroll State",
             ProfilerCategory::ScriptSetViewport => "Script Set Viewport",
             ProfilerCategory::ScriptTimerEvent => "Script Timer Event",
             ProfilerCategory::ScriptStylesheetLoad => "Script Stylesheet Load",
             ProfilerCategory::ScriptWebSocketEvent => "Script Web Socket Event",
             ProfilerCategory::ScriptWorkerEvent => "Script Worker Event",
             ProfilerCategory::ScriptServiceWorkerEvent => "Script Service Worker Event",
+            ProfilerCategory::ScriptEnterFullscreen => "Script Enter Fullscreen",
+            ProfilerCategory::ScriptExitFullscreen => "Script Exit Fullscreen",
             ProfilerCategory::ApplicationHeartbeat => "Application Heartbeat",
         };
         format!("{}{}", padding, name)
     }
 }
 
 type ProfilerBuckets = BTreeMap<(ProfilerCategory, Option<TimerMetadata>), Vec<f64>>;
 
--- a/servo/components/profile_traits/time.rs
+++ b/servo/components/profile_traits/time.rs
@@ -81,16 +81,18 @@ pub enum ProfilerCategory {
     ScriptSetViewport = 0x6f,
     ScriptTimerEvent = 0x70,
     ScriptStylesheetLoad = 0x71,
     ScriptUpdateReplacedElement = 0x72,
     ScriptWebSocketEvent = 0x73,
     ScriptWorkerEvent = 0x74,
     ScriptServiceWorkerEvent = 0x75,
     ScriptParseXML = 0x76,
+    ScriptEnterFullscreen = 0x77,
+    ScriptExitFullscreen = 0x78,
     ApplicationHeartbeat = 0x90,
 }
 
 #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Deserialize, Serialize)]
 pub enum TimerMetadataFrameType {
     RootWindow,
     IFrame,
 }
--- a/servo/components/script/dom/document.rs
+++ b/servo/components/script/dom/document.rs
@@ -22,29 +22,29 @@ use dom::bindings::codegen::Bindings::Pe
 use dom::bindings::codegen::Bindings::TouchBinding::TouchMethods;
 use dom::bindings::codegen::Bindings::WindowBinding::{FrameRequestCallback, ScrollBehavior, WindowMethods};
 use dom::bindings::codegen::UnionTypes::NodeOrString;
 use dom::bindings::error::{Error, ErrorResult, Fallible};
 use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
 use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root};
 use dom::bindings::js::RootedReference;
 use dom::bindings::num::Finite;
-use dom::bindings::refcounted::Trusted;
+use dom::bindings::refcounted::{Trusted, TrustedPromise};
 use dom::bindings::reflector::{DomObject, reflect_dom_object};
 use dom::bindings::str::{DOMString, USVString};
 use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml_name_type};
 use dom::bindings::xmlname::XMLName::InvalidXMLName;
 use dom::browsingcontext::BrowsingContext;
 use dom::closeevent::CloseEvent;
 use dom::comment::Comment;
 use dom::customevent::CustomEvent;
 use dom::documentfragment::DocumentFragment;
 use dom::documenttype::DocumentType;
 use dom::domimplementation::DOMImplementation;
-use dom::element::{Element, ElementCreator};
+use dom::element::{Element, ElementCreator, ElementPerformFullscreenEnter, ElementPerformFullscreenExit};
 use dom::errorevent::ErrorEvent;
 use dom::event::{Event, EventBubbles, EventCancelable};
 use dom::eventdispatcher::EventStatus;
 use dom::eventtarget::EventTarget;
 use dom::focusevent::FocusEvent;
 use dom::forcetouchevent::ForceTouchEvent;
 use dom::globalscope::GlobalScope;
 use dom::hashchangeevent::HashChangeEvent;
@@ -69,16 +69,17 @@ use dom::messageevent::MessageEvent;
 use dom::mouseevent::MouseEvent;
 use dom::node::{self, CloneChildrenFlag, Node, NodeDamage, window_from_node};
 use dom::nodeiterator::NodeIterator;
 use dom::nodelist::NodeList;
 use dom::pagetransitionevent::PageTransitionEvent;
 use dom::popstateevent::PopStateEvent;
 use dom::processinginstruction::ProcessingInstruction;
 use dom::progressevent::ProgressEvent;
+use dom::promise::Promise;
 use dom::range::Range;
 use dom::servoparser::ServoParser;
 use dom::storageevent::StorageEvent;
 use dom::stylesheetlist::StyleSheetList;
 use dom::text::Text;
 use dom::touch::Touch;
 use dom::touchevent::TouchEvent;
 use dom::touchlist::TouchList;
@@ -100,16 +101,17 @@ use msg::constellation_msg::{FrameId, Ke
 use net_traits::{FetchResponseMsg, IpcSend, ReferrerPolicy};
 use net_traits::CookieSource::NonHTTP;
 use net_traits::CoreResourceMsg::{GetCookiesForUrl, SetCookiesForUrl};
 use net_traits::request::RequestInit;
 use net_traits::response::HttpsState;
 use num_traits::ToPrimitive;
 use origin::Origin;
 use script_layout_interface::message::{Msg, ReflowQueryType};
+use script_runtime::{CommonScriptMsg, ScriptThreadEventCategory};
 use script_thread::{MainThreadScriptMsg, Runnable};
 use script_traits::{AnimationState, CompositorEvent, MouseButton, MouseEventType, MozBrowserEvent};
 use script_traits::{ScriptMsg as ConstellationMsg, TouchpadPressurePhase};
 use script_traits::{TouchEventType, TouchId};
 use script_traits::UntrustedNodeAddress;
 use servo_atoms::Atom;
 use servo_url::ServoUrl;
 use std::ascii::AsciiExt;
@@ -285,16 +287,18 @@ pub struct Document {
     /// https://html.spec.whatwg.org/multipage/#ignore-destructive-writes-counter
     ignore_destructive_writes_counter: Cell<u32>,
     /// Track the total number of elements in this DOM's tree.
     /// This is sent to the layout thread every time a reflow is done;
     /// layout uses this to determine if the gains from parallel layout will be worth the overhead.
     ///
     /// See also: https://github.com/servo/servo/issues/10110
     dom_count: Cell<u32>,
+    /// Entry node for fullscreen.
+    fullscreen_element: MutNullableHeap<JS<Element>>,
 }
 
 #[derive(JSTraceable, HeapSizeOf)]
 struct ImagesFilter;
 impl CollectionFilter for ImagesFilter {
     fn filter(&self, elem: &Element, _root: &Node) -> bool {
         elem.is::<HTMLImageElement>()
     }
@@ -1902,16 +1906,17 @@ impl Document {
             touchpad_pressure_phase: Cell::new(TouchpadPressurePhase::BeforeClick),
             origin: origin,
             referrer: referrer,
             referrer_policy: Cell::new(referrer_policy),
             target_element: MutNullableHeap::new(None),
             last_click_info: DOMRefCell::new(None),
             ignore_destructive_writes_counter: Default::default(),
             dom_count: Cell::new(1),
+            fullscreen_element: MutNullableHeap::new(None),
         }
     }
 
     // https://dom.spec.whatwg.org/#dom-document
     pub fn Constructor(window: &Window) -> Fallible<Root<Document>> {
         let doc = window.Document();
         let docloader = DocumentLoader::new(&*doc.loader());
         Ok(Document::new(window,
@@ -2087,16 +2092,120 @@ impl Document {
         self.ignore_destructive_writes_counter.set(
             self.ignore_destructive_writes_counter.get() + 1);
     }
 
     pub fn decr_ignore_destructive_writes_counter(&self) {
         self.ignore_destructive_writes_counter.set(
             self.ignore_destructive_writes_counter.get() - 1);
     }
+
+    // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen
+    #[allow(unrooted_must_root)]
+    pub fn enter_fullscreen(&self, pending: &Element) -> Rc<Promise> {
+        // Step 1
+        let promise = Promise::new(self.global().r());
+        let mut error = false;
+
+        // Step 4
+        // check namespace
+        match *pending.namespace() {
+            ns!(mathml) => {
+                if pending.local_name().as_ref() != "math" {
+                    error = true;
+                }
+            }
+            ns!(svg) => {
+                if pending.local_name().as_ref() != "svg" {
+                    error = true;
+                }
+            }
+            ns!(html) => (),
+            _ => error = true,
+        }
+        // fullscreen element ready check
+        if !pending.fullscreen_element_ready_check() {
+            error = true;
+        }
+        // TODO fullscreen is supported
+        // TODO This algorithm is allowed to request fullscreen.
+
+        // Step 5 Parallel start
+
+        let window = self.window();
+        // Step 6
+        if !error {
+            let event = ConstellationMsg::SetFullscreenState(true);
+            window.upcast::<GlobalScope>().constellation_chan().send(event).unwrap();
+        }
+
+        // Step 7
+        let trusted_pending = Trusted::new(pending);
+        let trusted_promise = TrustedPromise::new(promise.clone());
+        let handler = ElementPerformFullscreenEnter::new(trusted_pending, trusted_promise, error);
+        let script_msg = CommonScriptMsg::RunnableMsg(ScriptThreadEventCategory::EnterFullscreen, handler);
+        let msg = MainThreadScriptMsg::Common(script_msg);
+        window.main_thread_script_chan().send(msg).unwrap();
+
+        promise
+    }
+
+    // https://fullscreen.spec.whatwg.org/#exit-fullscreen
+    #[allow(unrooted_must_root)]
+    pub fn exit_fullscreen(&self) -> Rc<Promise> {
+        let global = self.global();
+        // Step 1
+        let promise = Promise::new(global.r());
+        // Step 2
+        if self.fullscreen_element.get().is_none() {
+            promise.reject_error(global.get_cx(), Error::Type(String::from("fullscreen is null")));
+            return promise
+        }
+        // TODO Step 3-6
+        let element = self.fullscreen_element.get().unwrap();
+
+        // Step 7 Parallel start
+
+        let window = self.window();
+        // Step 8
+        let event = ConstellationMsg::SetFullscreenState(false);
+        window.upcast::<GlobalScope>().constellation_chan().send(event).unwrap();
+
+        // Step 9
+        let trusted_element = Trusted::new(element.r());
+        let trusted_promise = TrustedPromise::new(promise.clone());
+        let handler = ElementPerformFullscreenExit::new(trusted_element, trusted_promise);
+        let script_msg = CommonScriptMsg::RunnableMsg(ScriptThreadEventCategory::ExitFullscreen, handler);
+        let msg = MainThreadScriptMsg::Common(script_msg);
+        window.main_thread_script_chan().send(msg).unwrap();
+
+        promise
+    }
+
+    pub fn set_fullscreen_element(&self, element: Option<&Element>) {
+        self.fullscreen_element.set(element);
+    }
+
+    pub fn get_allow_fullscreen(&self) -> bool {
+        // https://html.spec.whatwg.org/multipage/#allowed-to-use
+        match self.browsing_context() {
+            // Step 1
+            None => false,
+            Some(_) => {
+                // Step 2
+                let window = self.window();
+                if window.is_top_level() {
+                    true
+                } else {
+                    // Step 3
+                    window.GetFrameElement().map_or(false, |el| el.has_attribute(&local_name!("allowfullscreen")))
+                }
+            }
+        }
+    }
 }
 
 
 impl Element {
     fn click_event_filter_by_disabled_state(&self) -> bool {
         let node = self.upcast::<Node>();
         match node.type_id() {
             NodeTypeId::Element(ElementTypeId::HTMLElement(HTMLElementTypeId::HTMLButtonElement)) |
@@ -3104,16 +3213,44 @@ impl DocumentMethods for Document {
     // https://html.spec.whatwg.org/multipage/#dom-document-writeln
     fn Writeln(&self, mut text: Vec<DOMString>) -> ErrorResult {
         text.push("\n".into());
         self.Write(text)
     }
 
     // https://html.spec.whatwg.org/multipage/#documentandelementeventhandlers
     document_and_element_event_handlers!();
+
+    // https://fullscreen.spec.whatwg.org/#handler-document-onfullscreenerror
+    event_handler!(fullscreenerror, GetOnfullscreenerror, SetOnfullscreenerror);
+
+    // https://fullscreen.spec.whatwg.org/#handler-document-onfullscreenchange
+    event_handler!(fullscreenchange, GetOnfullscreenchange, SetOnfullscreenchange);
+
+    // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled
+    fn FullscreenEnabled(&self) -> bool {
+        self.get_allow_fullscreen()
+    }
+
+    // https://fullscreen.spec.whatwg.org/#dom-document-fullscreen
+    fn Fullscreen(&self) -> bool {
+        self.fullscreen_element.get().is_some()
+    }
+
+    // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenelement
+    fn GetFullscreenElement(&self) -> Option<Root<Element>> {
+        // TODO ShadowRoot
+        self.fullscreen_element.get()
+    }
+
+    #[allow(unrooted_must_root)]
+    // https://fullscreen.spec.whatwg.org/#dom-document-exitfullscreen
+    fn ExitFullscreen(&self) -> Rc<Promise> {
+        self.exit_fullscreen()
+    }
 }
 
 fn update_with_current_time_ms(marker: &Cell<u64>) {
     if marker.get() == Default::default() {
         let time = time::get_time();
         let current_time_ms = time.sec * 1000 + time.nsec as i64 / 1000000;
         marker.set(current_time_ms as u64);
     }
--- a/servo/components/script/dom/element.rs
+++ b/servo/components/script/dom/element.rs
@@ -19,27 +19,30 @@ use dom::bindings::codegen::Bindings::HT
 use dom::bindings::codegen::Bindings::NodeBinding::NodeMethods;
 use dom::bindings::codegen::Bindings::WindowBinding::{ScrollBehavior, ScrollToOptions};
 use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
 use dom::bindings::codegen::UnionTypes::NodeOrString;
 use dom::bindings::error::{Error, ErrorResult, Fallible};
 use dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId};
 use dom::bindings::js::{JS, LayoutJS, MutNullableHeap};
 use dom::bindings::js::{Root, RootedReference};
+use dom::bindings::refcounted::{Trusted, TrustedPromise};
+use dom::bindings::reflector::DomObject;
 use dom::bindings::str::DOMString;
 use dom::bindings::xmlname::{namespace_from_domstring, validate_and_extract, xml_name_type};
 use dom::bindings::xmlname::XMLName::InvalidXMLName;
 use dom::characterdata::CharacterData;
 use dom::create::create_element;
 use dom::document::{Document, LayoutDocumentHelpers};
 use dom::documentfragment::DocumentFragment;
 use dom::domrect::DOMRect;
 use dom::domrectlist::DOMRectList;
 use dom::domtokenlist::DOMTokenList;
 use dom::event::Event;
+use dom::eventtarget::EventTarget;
 use dom::htmlanchorelement::HTMLAnchorElement;
 use dom::htmlbodyelement::{HTMLBodyElement, HTMLBodyElementLayoutHelpers};
 use dom::htmlbuttonelement::HTMLButtonElement;
 use dom::htmlcollection::HTMLCollection;
 use dom::htmlelement::HTMLElement;
 use dom::htmlfieldsetelement::HTMLFieldSetElement;
 use dom::htmlfontelement::{HTMLFontElement, HTMLFontElementLayoutHelpers};
 use dom::htmlhrelement::{HTMLHRElement, HTMLHRLayoutHelpers};
@@ -57,41 +60,48 @@ use dom::htmltablerowelement::{HTMLTable
 use dom::htmltablesectionelement::{HTMLTableSectionElement, HTMLTableSectionElementLayoutHelpers};
 use dom::htmltemplateelement::HTMLTemplateElement;
 use dom::htmltextareaelement::{HTMLTextAreaElement, LayoutHTMLTextAreaElementHelpers};
 use dom::namednodemap::NamedNodeMap;
 use dom::node::{CLICK_IN_PROGRESS, ChildrenMutation, LayoutNodeHelpers, Node};
 use dom::node::{NodeDamage, SEQUENTIALLY_FOCUSABLE, UnbindContext};
 use dom::node::{document_from_node, window_from_node};
 use dom::nodelist::NodeList;
+use dom::promise::Promise;
 use dom::servoparser::ServoParser;
 use dom::text::Text;
 use dom::validation::Validatable;
 use dom::virtualmethods::{VirtualMethods, vtable_for};
+use dom::window::ReflowReason;
 use html5ever::serialize;
 use html5ever::serialize::SerializeOpts;
 use html5ever::serialize::TraversalScope;
 use html5ever::serialize::TraversalScope::{ChildrenOnly, IncludeNode};
 use html5ever::tree_builder::{LimitedQuirks, NoQuirks, Quirks};
 use html5ever_atoms::{Prefix, LocalName, Namespace, QualName};
+use js::jsapi::{HandleValue, JSAutoCompartment};
 use parking_lot::RwLock;
 use ref_filter_map::ref_filter_map;
+use script_layout_interface::message::ReflowQueryType;
+use script_thread::Runnable;
 use selectors::matching::{ElementFlags, MatchingReason, matches};
 use selectors::matching::{HAS_EDGE_CHILD_SELECTOR, HAS_SLOW_SELECTOR, HAS_SLOW_SELECTOR_LATER_SIBLINGS};
 use selectors::parser::{AttrSelector, NamespaceConstraint};
 use servo_atoms::Atom;
 use std::ascii::AsciiExt;
 use std::borrow::Cow;
 use std::cell::{Cell, Ref};
 use std::convert::TryFrom;
 use std::default::Default;
 use std::fmt;
+use std::rc::Rc;
 use std::sync::Arc;
 use std::sync::atomic::{AtomicUsize, Ordering};
 use style::attr::{AttrValue, LengthOrPercentageOrAuto};
+use style::context::ReflowGoal;
 use style::dom::TRestyleDamage;
 use style::element_state::*;
 use style::matching::{common_style_affecting_attributes, rare_style_affecting_attributes};
 use style::parser::ParserContextExtraData;
 use style::properties::{DeclaredValue, Importance};
 use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, parse_style_attribute};
 use style::properties::longhands::{background_image, border_spacing, font_family, font_size, overflow_x};
 use style::restyle_hints::RESTYLE_SELF;
@@ -1295,16 +1305,25 @@ impl Element {
             Some(elem) if elem.local_name() != &local_name!("html") || !elem.html_element_in_html_document() => {
                 Root::from_ref(elem)
             },
             _ => {
                 Root::upcast(HTMLBodyElement::new(local_name!("body"), None, owner_doc))
             }
         }
     }
+
+    // https://fullscreen.spec.whatwg.org/#fullscreen-element-ready-check
+    pub fn fullscreen_element_ready_check(&self) -> bool {
+        if !self.is_connected() {
+            return false
+        }
+        let document = document_from_node(self);
+        document.get_allow_fullscreen()
+    }
 }
 
 impl ElementMethods for Element {
     // https://dom.spec.whatwg.org/#dom-element-namespaceuri
     fn GetNamespaceURI(&self) -> Option<DOMString> {
         Node::namespace_to_string(self.namespace.clone())
     }
 
@@ -2051,16 +2070,23 @@ impl ElementMethods for Element {
         match self.as_maybe_activatable() {
             Some(a) => {
                 a.exit_formal_activation_state();
                 return Ok(());
             },
             None => return Err(Error::NotSupported)
         }
     }
+
+    // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen
+    #[allow(unrooted_must_root)]
+    fn RequestFullscreen(&self) -> Rc<Promise> {
+        let doc = document_from_node(self);
+        doc.enter_fullscreen(self)
+    }
 }
 
 pub fn fragment_affecting_attributes() -> [LocalName; 3] {
     [local_name!("width"), local_name!("height"), local_name!("src")]
 }
 
 impl VirtualMethods for Element {
     fn super_type(&self) -> Option<&VirtualMethods> {
@@ -2162,16 +2188,20 @@ impl VirtualMethods for Element {
     fn unbind_from_tree(&self, context: &UnbindContext) {
         self.super_type().unwrap().unbind_from_tree(context);
 
         if !context.tree_in_doc {
             return;
         }
 
         let doc = document_from_node(self);
+        let fullscreen = doc.GetFullscreenElement();
+        if fullscreen.r() == Some(self) {
+            doc.exit_fullscreen();
+        }
         if let Some(ref value) = *self.id_attribute.borrow() {
             doc.unregister_named_element(self, value.clone());
         }
         // This is used for layout optimization.
         doc.decrement_dom_count();
     }
 
     fn children_changed(&self, mutation: &ChildrenMutation) {
@@ -2302,16 +2332,17 @@ impl<'a> ::selectors::Element for Root<E
                 }
             },
 
             NonTSPseudoClass::ReadOnly =>
                 !Element::state(self).contains(pseudo_class.state_flag()),
 
             NonTSPseudoClass::Active |
             NonTSPseudoClass::Focus |
+            NonTSPseudoClass::Fullscreen |
             NonTSPseudoClass::Hover |
             NonTSPseudoClass::Enabled |
             NonTSPseudoClass::Disabled |
             NonTSPseudoClass::Checked |
             NonTSPseudoClass::Indeterminate |
             NonTSPseudoClass::ReadWrite |
             NonTSPseudoClass::PlaceholderShown |
             NonTSPseudoClass::Target =>
@@ -2577,17 +2608,32 @@ impl Element {
         }
     }
 
     pub fn target_state(&self) -> bool {
         self.state.get().contains(IN_TARGET_STATE)
     }
 
     pub fn set_target_state(&self, value: bool) {
-       self.set_state(IN_TARGET_STATE, value)
+        self.set_state(IN_TARGET_STATE, value)
+    }
+
+    pub fn fullscreen_state(&self) -> bool {
+        self.state.get().contains(IN_FULLSCREEN_STATE)
+    }
+
+    pub fn set_fullscreen_state(&self, value: bool) {
+        self.set_state(IN_FULLSCREEN_STATE, value)
+    }
+
+    /// https://dom.spec.whatwg.org/#connected
+    pub fn is_connected(&self) -> bool {
+        let node = self.upcast::<Node>();
+        let root = node.GetRootNode();
+        root.is::<Document>()
     }
 }
 
 impl Element {
     pub fn check_ancestors_disabled_state_for_form_control(&self) {
         let node = self.upcast::<Node>();
         if self.disabled_state() {
             return;
@@ -2708,8 +2754,109 @@ impl TagName {
     }
 
     /// Clear the cached tag name, so that it will be re-calculated the
     /// next time that `or_init()` is called.
     fn clear(&self) {
         *self.ptr.borrow_mut() = None;
     }
 }
+
+pub struct ElementPerformFullscreenEnter {
+    element: Trusted<Element>,
+    promise: TrustedPromise,
+    error: bool,
+}
+
+impl ElementPerformFullscreenEnter {
+    pub fn new(element: Trusted<Element>, promise: TrustedPromise, error: bool) -> Box<ElementPerformFullscreenEnter> {
+        box ElementPerformFullscreenEnter {
+            element: element,
+            promise: promise,
+            error: error,
+        }
+    }
+}
+
+impl Runnable for ElementPerformFullscreenEnter {
+    fn name(&self) -> &'static str { "ElementPerformFullscreenEnter" }
+
+    #[allow(unrooted_must_root)]
+    fn handler(self: Box<ElementPerformFullscreenEnter>) {
+        let element = self.element.root();
+        let document = document_from_node(element.r());
+
+        // Step 7.1
+        if self.error || !element.fullscreen_element_ready_check() {
+            // JSAutoCompartment needs to be manually made.
+            // Otherwise, Servo will crash.
+            let promise = self.promise.root();
+            let promise_cx = promise.global().get_cx();
+            let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get());
+            document.upcast::<EventTarget>().fire_event(atom!("fullscreenerror"));
+            promise.reject_error(promise.global().get_cx(), Error::Type(String::from("fullscreen is not connected")));
+            return
+        }
+
+        // TODO Step 7.2-4
+        // Step 7.5
+        element.set_fullscreen_state(true);
+        document.set_fullscreen_element(Some(&element));
+        document.window().reflow(ReflowGoal::ForDisplay,
+                                 ReflowQueryType::NoQuery,
+                                 ReflowReason::ElementStateChanged);
+
+        // Step 7.6
+        document.upcast::<EventTarget>().fire_event(atom!("fullscreenchange"));
+
+        // Step 7.7
+        // JSAutoCompartment needs to be manually made.
+        // Otherwise, Servo will crash.
+        let promise = self.promise.root();
+        let promise_cx = promise.global().get_cx();
+        let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get());
+        promise.resolve(promise.global().get_cx(), HandleValue::undefined());
+    }
+}
+
+pub struct ElementPerformFullscreenExit {
+    element: Trusted<Element>,
+    promise: TrustedPromise,
+}
+
+impl ElementPerformFullscreenExit {
+    pub fn new(element: Trusted<Element>, promise: TrustedPromise) -> Box<ElementPerformFullscreenExit> {
+        box ElementPerformFullscreenExit {
+            element: element,
+            promise: promise,
+        }
+    }
+}
+
+impl Runnable for ElementPerformFullscreenExit {
+    fn name(&self) -> &'static str { "ElementPerformFullscreenExit" }
+
+    #[allow(unrooted_must_root)]
+    fn handler(self: Box<ElementPerformFullscreenExit>) {
+        let element = self.element.root();
+        let document = document_from_node(element.r());
+        // TODO Step 9.1-5
+        // Step 9.6
+        element.set_fullscreen_state(false);
+
+        document.window().reflow(ReflowGoal::ForDisplay,
+                                 ReflowQueryType::NoQuery,
+                                 ReflowReason::ElementStateChanged);
+
+        document.set_fullscreen_element(None);
+
+        // Step 9.8
+        document.upcast::<EventTarget>().fire_event(atom!("fullscreenchange"));
+
+        // Step 9.10
+        let promise = self.promise.root();
+        // JSAutoCompartment needs to be manually made.
+        // Otherwise, Servo will crash.
+        let promise_cx = promise.global().get_cx();
+        let _ac = JSAutoCompartment::new(promise_cx, promise.reflector().get_jsobject().get());
+        promise.resolve(promise.global().get_cx(), HandleValue::undefined());
+    }
+}
--- a/servo/components/script/dom/htmliframeelement.rs
+++ b/servo/components/script/dom/htmliframeelement.rs
@@ -10,17 +10,17 @@ use dom::bindings::codegen::Bindings::Br
 use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementLocationChangeEventDetail;
 use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementOpenTabEventDetail;
 use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementOpenWindowEventDetail;
 use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementSecurityChangeDetail;
 use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserElementVisibilityChangeEventDetail;
 use dom::bindings::codegen::Bindings::BrowserElementBinding::BrowserShowModalPromptEventDetail;
 use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding;
 use dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods;
-use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods;
+use dom::bindings::codegen::Bindings::WindowBinding::WindowBinding::WindowMethods;
 use dom::bindings::conversions::ToJSValConvertible;
 use dom::bindings::error::{Error, ErrorResult, Fallible};
 use dom::bindings::inheritance::Castable;
 use dom::bindings::js::{JS, LayoutJS, MutNullableHeap, Root};
 use dom::bindings::refcounted::Trusted;
 use dom::bindings::reflector::DomObject;
 use dom::bindings::str::DOMString;
 use dom::browsingcontext::BrowsingContext;
@@ -539,16 +539,21 @@ impl HTMLIFrameElementMethods for HTMLIF
     }
 
     // https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-mozbrowser
     fn SetMozbrowser(&self, value: bool) {
         let element = self.upcast::<Element>();
         element.set_bool_attribute(&local_name!("mozbrowser"), value);
     }
 
+    // https://html.spec.whatwg.org/multipage/#attr-iframe-allowfullscreen
+    make_bool_getter!(AllowFullscreen, "allowfullscreen");
+    // https://html.spec.whatwg.org/multipage/#attr-iframe-allowfullscreen
+    make_bool_setter!(SetAllowFullscreen, "allowfullscreen");
+
     // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/goBack
     fn GoBack(&self) -> ErrorResult {
         Navigate(self, TraversalDirection::Back(1))
     }
 
     // https://developer.mozilla.org/en-US/docs/Web/API/HTMLIFrameElement/goForward
     fn GoForward(&self) -> ErrorResult {
         Navigate(self, TraversalDirection::Forward(1))
--- a/servo/components/script/dom/webidls/Document.webidl
+++ b/servo/components/script/dom/webidls/Document.webidl
@@ -185,8 +185,20 @@ partial interface Document {
   Element? elementFromPoint(double x, double y);
   sequence<Element> elementsFromPoint(double x, double y);
 };
 
 // https://drafts.csswg.org/cssom/#extensions-to-the-document-interface
 partial interface Document {
   [SameObject] readonly attribute StyleSheetList styleSheets;
 };
+
+// https://fullscreen.spec.whatwg.org/#api
+partial interface Document {
+  [LenientSetter] readonly attribute boolean fullscreenEnabled;
+  [LenientSetter] readonly attribute Element? fullscreenElement;
+  [LenientSetter] readonly attribute boolean fullscreen; // historical
+
+  Promise<void> exitFullscreen();
+
+  attribute EventHandler onfullscreenchange;
+  attribute EventHandler onfullscreenerror;
+};
--- a/servo/components/script/dom/webidls/Element.webidl
+++ b/servo/components/script/dom/webidls/Element.webidl
@@ -105,12 +105,17 @@ partial interface Element {
 // https://domparsing.spec.whatwg.org/#extensions-to-the-element-interface
 partial interface Element {
   [Throws,TreatNullAs=EmptyString]
   attribute DOMString innerHTML;
   [Throws,TreatNullAs=EmptyString]
   attribute DOMString outerHTML;
 };
 
+// https://fullscreen.spec.whatwg.org/#api
+partial interface Element {
+  Promise<void> requestFullscreen();
+};
+
 Element implements ChildNode;
 Element implements NonDocumentTypeChildNode;
 Element implements ParentNode;
 Element implements ActivatableElement;
--- a/servo/components/script/dom/webidls/HTMLIFrameElement.webidl
+++ b/servo/components/script/dom/webidls/HTMLIFrameElement.webidl
@@ -8,17 +8,17 @@ interface HTMLIFrameElement : HTMLElemen
   //         attribute DOMString srcdoc;
 
   // https://github.com/servo/servo/issues/14453
   // attribute DOMString name;
 
            [SameObject, PutForwards=value]
            readonly attribute DOMTokenList sandbox;
   //         attribute boolean seamless;
-  //         attribute boolean allowFullscreen;
+           attribute boolean allowFullscreen;
            attribute DOMString width;
            attribute DOMString height;
   readonly attribute Document? contentDocument;
   readonly attribute WindowProxy? contentWindow;
 
   // also has obsolete members
 };
 
--- a/servo/components/script/layout_wrapper.rs
+++ b/servo/components/script/layout_wrapper.rs
@@ -661,16 +661,17 @@ impl<'le> ::selectors::Element for Servo
                 }
             },
 
             NonTSPseudoClass::ReadOnly =>
                 !self.element.get_state_for_layout().contains(pseudo_class.state_flag()),
 
             NonTSPseudoClass::Active |
             NonTSPseudoClass::Focus |
+            NonTSPseudoClass::Fullscreen |
             NonTSPseudoClass::Hover |
             NonTSPseudoClass::Enabled |
             NonTSPseudoClass::Disabled |
             NonTSPseudoClass::Checked |
             NonTSPseudoClass::Indeterminate |
             NonTSPseudoClass::ReadWrite |
             NonTSPseudoClass::PlaceholderShown |
             NonTSPseudoClass::Target =>
--- a/servo/components/script/script_runtime.rs
+++ b/servo/components/script/script_runtime.rs
@@ -71,17 +71,19 @@ pub enum ScriptThreadEventCategory {
     ScriptEvent,
     SetScrollState,
     SetViewport,
     StylesheetLoad,
     TimerEvent,
     UpdateReplacedElement,
     WebSocketEvent,
     WorkerEvent,
-    ServiceWorkerEvent
+    ServiceWorkerEvent,
+    EnterFullscreen,
+    ExitFullscreen,
 }
 
 /// An interface for receiving ScriptMsg values in an event loop. Used for synchronous DOM
 /// APIs that need to abstract over multiple kinds of event loops (worker/main thread) with
 /// different Receiver interfaces.
 pub trait ScriptPort {
     fn recv(&self) -> Result<CommonScriptMsg, ()>;
 }
--- a/servo/components/script/script_thread.rs
+++ b/servo/components/script/script_thread.rs
@@ -941,17 +941,19 @@ impl ScriptThread {
                 ScriptThreadEventCategory::UpdateReplacedElement => {
                     ProfilerCategory::ScriptUpdateReplacedElement
                 }
                 ScriptThreadEventCategory::StylesheetLoad => ProfilerCategory::ScriptStylesheetLoad,
                 ScriptThreadEventCategory::SetViewport => ProfilerCategory::ScriptSetViewport,
                 ScriptThreadEventCategory::TimerEvent => ProfilerCategory::ScriptTimerEvent,
                 ScriptThreadEventCategory::WebSocketEvent => ProfilerCategory::ScriptWebSocketEvent,
                 ScriptThreadEventCategory::WorkerEvent => ProfilerCategory::ScriptWorkerEvent,
-                ScriptThreadEventCategory::ServiceWorkerEvent => ProfilerCategory::ScriptServiceWorkerEvent
+                ScriptThreadEventCategory::ServiceWorkerEvent => ProfilerCategory::ScriptServiceWorkerEvent,
+                ScriptThreadEventCategory::EnterFullscreen => ProfilerCategory::ScriptEnterFullscreen,
+                ScriptThreadEventCategory::ExitFullscreen => ProfilerCategory::ScriptExitFullscreen,
             };
             profile(profiler_cat, None, self.time_profiler_chan.clone(), f)
         } else {
             f()
         }
     }
 
     fn handle_msg_from_constellation(&self, msg: ConstellationControlMsg) {
--- a/servo/components/script_traits/script_msg.rs
+++ b/servo/components/script_traits/script_msg.rs
@@ -140,16 +140,18 @@ pub enum ScriptMsg {
     LogEntry(Option<FrameId>, Option<String>, LogEntry),
     /// Notifies the constellation that this pipeline has exited.
     PipelineExited(PipelineId),
     /// Send messages from postMessage calls from serviceworker
     /// to constellation for storing in service worker manager
     ForwardDOMMessage(DOMMessage, ServoUrl),
     /// Store the data required to activate a service worker for the given scope
     RegisterServiceWorker(ScopeThings, ServoUrl),
+    /// Enter or exit fullscreen
+    SetFullscreenState(bool),
     /// Requests that the compositor shut down.
     Exit
 }
 
 /// Entities required to spawn service workers
 #[derive(Deserialize, Serialize, Clone)]
 pub struct ScopeThings {
     /// script resource url
--- a/servo/components/style/element_state.rs
+++ b/servo/components/style/element_state.rs
@@ -30,10 +30,12 @@ bitflags! {
         #[doc = "https://html.spec.whatwg.org/multipage/#selector-indeterminate"]
         const IN_INDETERMINATE_STATE = 0x40,
         #[doc = "https://html.spec.whatwg.org/multipage/#selector-read-write"]
         const IN_READ_WRITE_STATE = 0x80,
         #[doc = "https://html.spec.whatwg.org/multipage/#selector-placeholder-shown"]
         const IN_PLACEHOLDER_SHOWN_STATE = 0x0100,
         #[doc = "https://html.spec.whatwg.org/multipage/#selector-target"]
         const IN_TARGET_STATE = 0x0200,
+        #[doc = "https://fullscreen.spec.whatwg.org/#%3Afullscreen-pseudo-class"]
+        const IN_FULLSCREEN_STATE = 0x0400,
     }
 }
--- a/servo/components/style/gecko/selector_parser.rs
+++ b/servo/components/style/gecko/selector_parser.rs
@@ -104,16 +104,17 @@ impl ToCss for PseudoElement {
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 pub enum NonTSPseudoClass {
     AnyLink,
     Link,
     Visited,
     Active,
     Focus,
+    Fullscreen,
     Hover,
     Enabled,
     Disabled,
     Checked,
     Indeterminate,
     ReadWrite,
     ReadOnly,
 }
@@ -122,16 +123,17 @@ impl ToCss for NonTSPseudoClass {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         use self::NonTSPseudoClass::*;
         dest.write_str(match *self {
             AnyLink => ":any-link",
             Link => ":link",
             Visited => ":visited",
             Active => ":active",
             Focus => ":focus",
+            Fullscreen => ":fullscreen",
             Hover => ":hover",
             Enabled => ":enabled",
             Disabled => ":disabled",
             Checked => ":checked",
             Indeterminate => ":indeterminate",
             ReadWrite => ":read-write",
             ReadOnly => ":read-only",
         })
@@ -140,16 +142,17 @@ impl ToCss for NonTSPseudoClass {
 
 impl NonTSPseudoClass {
     pub fn state_flag(&self) -> ElementState {
         use element_state::*;
         use self::NonTSPseudoClass::*;
         match *self {
             Active => IN_ACTIVE_STATE,
             Focus => IN_FOCUS_STATE,
+            Fullscreen => IN_FULLSCREEN_STATE,
             Hover => IN_HOVER_STATE,
             Enabled => IN_ENABLED_STATE,
             Disabled => IN_DISABLED_STATE,
             Checked => IN_CHECKED_STATE,
             Indeterminate => IN_INDETERMINATE_STATE,
             ReadOnly | ReadWrite => IN_READ_WRITE_STATE,
 
             AnyLink |
@@ -191,16 +194,17 @@ impl<'a> ::selectors::Parser for Selecto
     fn parse_non_ts_pseudo_class(&self, name: Cow<str>) -> Result<NonTSPseudoClass, ()> {
         use self::NonTSPseudoClass::*;
         let pseudo_class = match_ignore_ascii_case! { &name,
             "any-link" => AnyLink,
             "link" => Link,
             "visited" => Visited,
             "active" => Active,
             "focus" => Focus,
+            "fullscreen" => Fullscreen,
             "hover" => Hover,
             "enabled" => Enabled,
             "disabled" => Disabled,
             "checked" => Checked,
             "indeterminate" => Indeterminate,
             "read-write" => ReadWrite,
             "read-only" => ReadOnly,
             _ => return Err(())
--- a/servo/components/style/gecko/wrapper.rs
+++ b/servo/components/style/gecko/wrapper.rs
@@ -476,16 +476,17 @@ impl<'le> ::selectors::Element for Gecko
             NonTSPseudoClass::Visited => unsafe { Gecko_IsVisitedLink(self.0) },
             NonTSPseudoClass::Active |
             NonTSPseudoClass::Focus |
             NonTSPseudoClass::Hover |
             NonTSPseudoClass::Enabled |
             NonTSPseudoClass::Disabled |
             NonTSPseudoClass::Checked |
             NonTSPseudoClass::ReadWrite |
+            NonTSPseudoClass::Fullscreen |
             NonTSPseudoClass::Indeterminate => {
                 self.get_state().contains(pseudo_class.state_flag())
             },
             NonTSPseudoClass::ReadOnly => {
                 !self.get_state().contains(pseudo_class.state_flag())
             }
         }
     }
--- a/servo/components/style/servo/selector_parser.rs
+++ b/servo/components/style/servo/selector_parser.rs
@@ -85,16 +85,17 @@ impl PseudoElement {
 #[derive(Clone, Debug, PartialEq, Eq, Hash)]
 #[cfg_attr(feature = "servo", derive(HeapSizeOf))]
 pub enum NonTSPseudoClass {
     AnyLink,
     Link,
     Visited,
     Active,
     Focus,
+    Fullscreen,
     Hover,
     Enabled,
     Disabled,
     Checked,
     Indeterminate,
     ServoNonZeroBorder,
     ReadWrite,
     ReadOnly,
@@ -106,16 +107,17 @@ impl ToCss for NonTSPseudoClass {
     fn to_css<W>(&self, dest: &mut W) -> fmt::Result where W: fmt::Write {
         use self::NonTSPseudoClass::*;
         dest.write_str(match *self {
             AnyLink => ":any-link",
             Link => ":link",
             Visited => ":visited",
             Active => ":active",
             Focus => ":focus",
+            Fullscreen => ":fullscreen",
             Hover => ":hover",
             Enabled => ":enabled",
             Disabled => ":disabled",
             Checked => ":checked",
             Indeterminate => ":indeterminate",
             ReadWrite => ":read-write",
             ReadOnly => ":read-only",
             PlaceholderShown => ":placeholder-shown",
@@ -127,16 +129,17 @@ impl ToCss for NonTSPseudoClass {
 
 impl NonTSPseudoClass {
     pub fn state_flag(&self) -> ElementState {
         use element_state::*;
         use self::NonTSPseudoClass::*;
         match *self {
             Active => IN_ACTIVE_STATE,
             Focus => IN_FOCUS_STATE,
+            Fullscreen => IN_FULLSCREEN_STATE,
             Hover => IN_HOVER_STATE,
             Enabled => IN_ENABLED_STATE,
             Disabled => IN_DISABLED_STATE,
             Checked => IN_CHECKED_STATE,
             Indeterminate => IN_INDETERMINATE_STATE,
             ReadOnly | ReadWrite => IN_READ_WRITE_STATE,
             PlaceholderShown => IN_PLACEHOLDER_SHOWN_STATE,
             Target => IN_TARGET_STATE,
@@ -182,16 +185,17 @@ impl<'a> ::selectors::Parser for Selecto
     fn parse_non_ts_pseudo_class(&self, name: Cow<str>) -> Result<NonTSPseudoClass, ()> {
         use self::NonTSPseudoClass::*;
         let pseudo_class = match_ignore_ascii_case! { &name,
             "any-link" => AnyLink,
             "link" => Link,
             "visited" => Visited,
             "active" => Active,
             "focus" => Focus,
+            "fullscreen" => Fullscreen,
             "hover" => Hover,
             "enabled" => Enabled,
             "disabled" => Disabled,
             "checked" => Checked,
             "indeterminate" => Indeterminate,
             "read-write" => ReadWrite,
             "read-only" => ReadOnly,
             "placeholder-shown" => PlaceholderShown,
--- a/servo/ports/cef/window.rs
+++ b/servo/ports/cef/window.rs
@@ -233,16 +233,19 @@ impl WindowMethods for Window {
     fn set_inner_size(&self, _size: Size2D<u32>) {
 
     }
 
     fn set_position(&self, _point: Point2D<i32>) {
 
     }
 
+    fn set_fullscreen_state(&self, _state: bool) {
+    }
+
     fn present(&self) {
         let browser = self.cef_browser.borrow();
         match *browser {
             None => {}
             Some(ref browser) => {
                 if check_ptr_exist!(browser.get_host().get_client(), get_render_handler) &&
                    check_ptr_exist!(browser.get_host().get_client().get_render_handler(), on_present) {
                     browser.get_host().get_client().get_render_handler().on_present(browser.clone());
--- a/servo/ports/glutin/window.rs
+++ b/servo/ports/glutin/window.rs
@@ -836,16 +836,25 @@ impl WindowMethods for Window {
         match self.kind {
             WindowKind::Window(ref window) => {
                 window.set_position(point.x, point.y)
             }
             WindowKind::Headless(..) => {}
         }
     }
 
+    fn set_fullscreen_state(&self, _state: bool) {
+        match self.kind {
+            WindowKind::Window(..) => {
+                warn!("Fullscreen is not implemented!")
+            },
+            WindowKind::Headless(..) => {}
+        }
+    }
+
     fn present(&self) {
         match self.kind {
             WindowKind::Window(ref window) => {
                 if let Err(err) = window.swap_buffers() {
                     warn!("Failed to swap window buffers ({}).", err);
                 }
             }
             WindowKind::Headless(..) => {}
--- a/servo/resources/user-agent.css
+++ b/servo/resources/user-agent.css
@@ -281,8 +281,30 @@ legend {
 }
 
 iframe:not([seamless]) { border: 2px inset; }
 iframe[seamless] { display: block; }
 video { object-fit: contain; }
 
 
 textarea { white-space: pre-wrap; }
+
+*|*:not(:root):fullscreen {
+  position:fixed !important;
+  top:0 !important; right:0 !important; bottom:0 !important; left:0 !important;
+  margin:0 !important;
+  box-sizing:border-box !important;
+  min-width:0 !important;
+  max-width:none !important;
+  min-height:0 !important;
+  max-height:none !important;
+  width:100% !important;
+  height:100% !important;
+  transform:none !important;
+
+  /* intentionally not !important */
+  object-fit:contain;
+}
+
+iframe:fullscreen {
+  border:none !important;
+  padding:0 !important;
+}