Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(591)

Side by Side Diff: third_party/WebKit/Source/core/dom/Fullscreen.cpp

Issue 2202493002: NOT FOR REVIEW: Fullscreen WIP (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org) 2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org) 3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org) 4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * (C) 2006 Alexey Proskuryakov (ap@webkit.org) 5 * (C) 2006 Alexey Proskuryakov (ap@webkit.org)
6 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All 6 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012 Apple Inc. All
7 * rights reserved. 7 * rights reserved.
8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved. 8 * Copyright (C) 2008, 2009 Torch Mobile Inc. All rights reserved.
9 * (http://www.torchmobile.com/) 9 * (http://www.torchmobile.com/)
10 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies) 10 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies)
(...skipping 11 matching lines...) Expand all
22 * 22 *
23 * You should have received a copy of the GNU Library General Public License 23 * You should have received a copy of the GNU Library General Public License
24 * along with this library; see the file COPYING.LIB. If not, write to 24 * along with this library; see the file COPYING.LIB. If not, write to
25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
26 * Boston, MA 02110-1301, USA. 26 * Boston, MA 02110-1301, USA.
27 * 27 *
28 */ 28 */
29 29
30 #include "core/dom/Fullscreen.h" 30 #include "core/dom/Fullscreen.h"
31 31
32 #include "bindings/core/v8/ExceptionMessages.h"
32 #include "core/dom/Document.h" 33 #include "core/dom/Document.h"
33 #include "core/dom/ElementTraversal.h" 34 #include "core/dom/StyleChangeReason.h"
34 #include "core/dom/StyleEngine.h" 35 #include "core/dom/StyleEngine.h"
35 #include "core/events/Event.h" 36 #include "core/events/Event.h"
36 #include "core/frame/FrameHost.h" 37 #include "core/frame/FrameHost.h"
37 #include "core/frame/HostsUsingFeatures.h" 38 #include "core/frame/HostsUsingFeatures.h"
38 #include "core/frame/LocalFrame.h" 39 #include "core/frame/LocalFrame.h"
39 #include "core/frame/Settings.h" 40 #include "core/frame/Settings.h"
40 #include "core/frame/UseCounter.h" 41 #include "core/frame/UseCounter.h"
41 #include "core/html/HTMLIFrameElement.h" 42 #include "core/html/HTMLFrameOwnerElement.h"
42 #include "core/input/EventHandler.h" 43 #include "core/input/EventHandler.h"
43 #include "core/inspector/ConsoleMessage.h" 44 #include "core/inspector/ConsoleMessage.h"
44 #include "core/layout/LayoutBlockFlow.h"
45 #include "core/layout/LayoutFullScreen.h"
46 #include "core/layout/api/LayoutFullScreenItem.h"
47 #include "core/page/ChromeClient.h" 45 #include "core/page/ChromeClient.h"
48 #include "core/svg/SVGSVGElement.h" 46 #include "core/svg/SVGSVGElement.h"
49 #include "platform/ScopedOrientationChangeIndicator.h" 47 #include "platform/ScopedOrientationChangeIndicator.h"
50 #include "platform/UserGestureIndicator.h" 48 #include "platform/UserGestureIndicator.h"
51 49
52 namespace blink { 50 namespace blink {
53 51
54 namespace { 52 namespace {
55 53
54 // https://fullscreen.spec.whatwg.org/#fullscreen-an-element
55 void fullscreen(Element& element, Fullscreen::RequestType requestType) {
56 // To fullscreen an |element| within a |document|, set the |element|'s
57 // fullscreen flag and add it to |document|'s top layer.
58 DCHECK(!element.isFullscreen());
59 // DCHECK(!element.isIFrameFullscreen());
60 DCHECK(!element.isInTopLayer());
61 element.setIsFullscreen(true);
62 element.document().addToTopLayer(&element);
63 }
64
65 // https://fullscreen.spec.whatwg.org/#unfullscreen-an-element
66 void unfullscreen(Element& element) {
67 // To unfullscreen an |element| within a |document|, unset the element's
68 // fullscreen flag and iframe fullscreen flag (if any), and remove it from
69 // |document|'s top layer.
70 DCHECK(element.isFullscreen());
71 DCHECK(element.isInTopLayer());
72 element.setIsFullscreen(true);
73 // element.setIsIFrameFullscreen(false);
74 element.document().removeFromTopLayer(&element);
75 }
76
77 // https://fullscreen.spec.whatwg.org/#unfullscreen-a-document
78 void unfullscreen(Document& document) {
79 // To unfullscreen a |document|, unfullscreen all elements, within
80 // |document|'s top layer, whose fullscreen flag is set.
81 for (Element* element : document.topLayerElements()) {
82 if (element->isFullscreen())
83 unfullscreen(*element);
84 }
85 }
86
56 // https://html.spec.whatwg.org/multipage/embedded-content.html#allowed-to-use 87 // https://html.spec.whatwg.org/multipage/embedded-content.html#allowed-to-use
57 bool allowedToUseFullscreen(const Frame* frame) { 88 bool allowedToUseFullscreen(const Frame* frame) {
58 // To determine whether a Document object |document| is allowed to use the 89 // To determine whether a Document object |document| is allowed to use the
59 // feature indicated by attribute name |allowattribute|, run these steps: 90 // feature indicated by attribute name |allowattribute|, run these steps:
60 91
61 // 1. If |document| has no browsing context, then return false. 92 // 1. If |document| has no browsing context, then return false.
62 if (!frame) 93 if (!frame)
63 return false; 94 return false;
64 95
65 // 2. If |document|'s browsing context is a top-level browsing context, then 96 // 2. If |document|'s browsing context is a top-level browsing context, then
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
113 144
114 // |element| is in a document. 145 // |element| is in a document.
115 if (!element.isConnected()) 146 if (!element.isConnected())
116 return false; 147 return false;
117 148
118 // |element|'s node document is allowed to use the feature indicated by 149 // |element|'s node document is allowed to use the feature indicated by
119 // attribute name allowfullscreen. 150 // attribute name allowfullscreen.
120 if (!allowedToUseFullscreen(element.document().frame())) 151 if (!allowedToUseFullscreen(element.document().frame()))
121 return false; 152 return false;
122 153
123 // |element|'s node document's fullscreen element stack is either empty or its
124 // top element is an inclusive ancestor of |element|.
125 if (const Element* topElement =
126 Fullscreen::fullscreenElementFrom(element.document())) {
127 if (!topElement->contains(&element))
128 return false;
129 }
130
131 // |element| has no ancestor element whose local name is iframe and namespace
132 // is the HTML namespace.
133 if (Traversal<HTMLIFrameElement>::firstAncestor(element))
134 return false;
135
136 // |element|'s node document's browsing context either has a browsing context 154 // |element|'s node document's browsing context either has a browsing context
137 // container and the fullscreen element ready check returns true for 155 // container and the fullscreen element ready check returns true for
138 // |element|'s node document's browsing context's browsing context container, 156 // |element|'s node document's browsing context's browsing context container,
139 // or it has no browsing context container. 157 // or it has no browsing context container.
140 if (const Element* owner = element.document().localOwner()) { 158 if (const Element* owner = element.document().localOwner()) {
141 if (!fullscreenElementReady(*owner)) 159 if (!fullscreenElementReady(*owner))
142 return false; 160 return false;
143 } 161 }
144 162
145 return true; 163 return true;
146 } 164 }
147 165
148 bool isPrefixed(const AtomicString& type) { 166 size_t countFullscreenInTopLayer(const Document& document) {
149 return type == EventTypeNames::webkitfullscreenchange || 167 size_t count = 0;
150 type == EventTypeNames::webkitfullscreenerror; 168 for (Element* element : document.topLayerElements()) {
169 if (element->isFullscreen())
170 ++count;
171 }
172 return count;
151 } 173 }
152 174
153 Event* createEvent(const AtomicString& type, EventTarget& target) { 175 // https://fullscreen.spec.whatwg.org/#collect-documents-to-unfullscreen
154 EventInit initializer; 176 HeapVector<Member<Document>> collectDocumentsToUnfullscreen(Document& doc) {
155 initializer.setBubbles(isPrefixed(type)); 177 // 1. If |doc|'s top layer consists of more than a single element that has
156 Event* event = Event::create(type, initializer); 178 // its fullscreen flag set, return the empty set.
157 event->setTarget(&target); 179
158 return event; 180 // 2. Let |docs| be an ordered set consisting of |doc|.
181
182 // 3. While |docs|'s last document has a browsing context container whose
183 // node document's top layer consists of a single element that has its
184 // fullscreen flag set and does not have its iframe fullscreen flag set (if
185 // any), append that node document to |docs|.
186 // TODO(foolip): Support the iframe fullscreen flag.
187 // https://crbug.com/644695
188
189 // OOPIF: Skip over remote frames, assuming that they have exactly one
190 // element in their fullscreen element stacks, thereby erring on the side of
191 // exiting fullscreen.
192 // TODO(alexmos): Deal with nested fullscreen cases, see
193 // https://crbug.com/617369.
194
195 HeapVector<Member<Document>> docs;
196 for (Frame* frame = doc.frame(); frame; frame = frame->tree().parent()) {
197 if (frame->isRemoteFrame())
198 continue;
199 if (Document* document = toLocalFrame(frame)->document()) {
200 // TODO(foolip): Fix the spec to count number of "fullscreen flag"
201 // elements in top layer, to avoid dialogs messing up the logic.
202 if (countFullscreenInTopLayer(*document) == 1)
203 docs.append(document);
204 else
205 break;
206 }
207 }
208
209 // 4. Return |docs|.
210 return docs;
159 } 211 }
160 212
161 // Helper to walk the ancestor chain and return the Document of the topmost 213 // Helper to walk the ancestor chain and return the Document of the topmost
162 // local ancestor frame. Note that this is not the same as the topmost frame's 214 // local ancestor frame. Note that this is not the same as the topmost frame's
163 // Document, which might be unavailable in OOPIF scenarios. For example, with 215 // Document, which might be unavailable in OOPIF scenarios. For example, with
164 // OOPIFs, when called on the bottom frame's Document in a A-B-C-B hierarchy in 216 // OOPIFs, when called on the bottom frame's Document in a A-B-C-B hierarchy in
165 // process B, this will skip remote frame C and return this frame: A-[B]-C-B. 217 // process B, this will skip remote frame C and return this frame: A-[B]-C-B.
166 Document& topmostLocalAncestor(Document& document) { 218 Document& topmostLocalAncestor(Document& document) {
167 Document* topmost = &document; 219 Document* topmost = &document;
168 Frame* frame = document.frame(); 220 Frame* frame = document.frame();
169 while (frame) { 221 while (frame) {
170 frame = frame->tree().parent(); 222 frame = frame->tree().parent();
171 if (frame && frame->isLocalFrame()) 223 if (frame && frame->isLocalFrame())
172 topmost = toLocalFrame(frame)->document(); 224 topmost = toLocalFrame(frame)->document();
173 } 225 }
174 return *topmost; 226 return *topmost;
175 } 227 }
176 228
177 // Helper to find the browsing context container in |doc| that embeds the 229 void fireChangeEvent(Element& element, Fullscreen::RequestType requestType) {
178 // |descendant| Document, possibly through multiple levels of nesting. This 230 if (requestType == Fullscreen::UnprefixedRequest) {
179 // works even in OOPIF scenarios like A-B-A, where there may be remote frames 231 Event* event = Event::create(EventTypeNames::fullscreenchange);
180 // in between |doc| and |descendant|. 232 element.document().dispatchEvent(event);
181 HTMLFrameOwnerElement* findContainerForDescendant(const Document& doc, 233 } else {
182 const Document& descendant) { 234 bool wasConnected = element.isConnected();
183 Frame* frame = descendant.frame(); 235
184 while (frame->tree().parent() != doc.frame()) 236 Event* event = Event::createBubble(EventTypeNames::webkitfullscreenchange);
185 frame = frame->tree().parent(); 237 element.dispatchEvent(event);
186 return toHTMLFrameOwnerElement(frame->owner()); 238
239 // TODO(foolip): Talk to Edge and WebKit teams about what to do if the
240 // element isn't connected.
241 if (!wasConnected) {
242 if (Element* documentElement = element.document().documentElement()) {
243 Event* documentEvent =
244 Event::createBubble(EventTypeNames::webkitfullscreenchange);
245 documentElement->dispatchEvent(documentEvent);
246 }
247 }
248 }
249 }
250
251 void fireErrorEvent(Element& element, Fullscreen::RequestType requestType) {
252 if (requestType == Fullscreen::UnprefixedRequest) {
253 Event* event = Event::create(EventTypeNames::fullscreenerror);
254 element.document().dispatchEvent(event);
255 } else {
256 Event* event = Event::createBubble(EventTypeNames::webkitfullscreenerror);
257 element.dispatchEvent(event);
258 }
187 } 259 }
188 260
189 } // anonymous namespace 261 } // anonymous namespace
190 262
191 const char* Fullscreen::supplementName() { 263 Element* Fullscreen::fullscreenElement(Document& document) {
192 return "Fullscreen"; 264 // The fullscreen element is the topmost element in the document's top layer
193 } 265 // whose fullscreen flag is set, if any, and null otherwise.
194 266
195 Fullscreen& Fullscreen::from(Document& document) { 267 const auto& elements = document.topLayerElements();
196 Fullscreen* fullscreen = fromIfExists(document); 268 for (auto it = elements.rbegin(); it != elements.rend(); ++it) {
197 if (!fullscreen) { 269 Element* element = (*it).get();
198 fullscreen = new Fullscreen(document); 270 if (element->isFullscreen())
199 Supplement<Document>::provideTo(document, supplementName(), fullscreen); 271 return element;
200 } 272 }
201 273
202 return *fullscreen;
203 }
204
205 Fullscreen* Fullscreen::fromIfExistsSlow(Document& document) {
206 return static_cast<Fullscreen*>(
207 Supplement<Document>::from(document, supplementName()));
208 }
209
210 Element* Fullscreen::fullscreenElementFrom(Document& document) {
211 if (Fullscreen* found = fromIfExists(document))
212 return found->fullscreenElement();
213 return nullptr; 274 return nullptr;
214 } 275 }
215 276
216 Element* Fullscreen::fullscreenElementForBindingFrom(TreeScope& scope) { 277 // https://fullscreen.spec.whatwg.org/#fullscreen-element
217 Element* element = fullscreenElementFrom(scope.document()); 278 Element* Fullscreen::fullscreenElementForBinding(TreeScope& scope) {
279 Element* element = fullscreenElement(scope.document());
218 if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled()) 280 if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled())
219 return element; 281 return element;
220 282
221 // TODO(kochi): Once V0 code is removed, we can use the same logic for 283 // TODO(kochi): Once V0 code is removed, we can use the same logic for
222 // Document and ShadowRoot. 284 // Document and ShadowRoot.
223 if (!scope.rootNode().isShadowRoot()) { 285 if (!scope.rootNode().isShadowRoot()) {
224 // For Shadow DOM V0 compatibility: We allow returning an element in V0 286 // For Shadow DOM V0 compatibility: We allow returning an element in V0
225 // shadow tree, even though it leaks the Shadow DOM. 287 // shadow tree, even though it leaks the Shadow DOM.
226 if (element->isInV0ShadowTree()) { 288 if (element->isInV0ShadowTree()) {
227 UseCounter::count(scope.document(), 289 UseCounter::count(scope.document(),
228 UseCounter::DocumentFullscreenElementInV0Shadow); 290 UseCounter::DocumentFullscreenElementInV0Shadow);
229 return element; 291 return element;
230 } 292 }
231 } else if (!toShadowRoot(scope.rootNode()).isV1()) { 293 } else if (!toShadowRoot(scope.rootNode()).isV1()) {
232 return nullptr; 294 return nullptr;
233 } 295 }
234 return scope.adjustedElement(*element); 296 return scope.adjustedElement(*element);
235 } 297 }
236 298
237 Element* Fullscreen::currentFullScreenElementFrom(Document& document) {
238 if (Fullscreen* found = fromIfExists(document))
239 return found->currentFullScreenElement();
240 return nullptr;
241 }
242
243 Element* Fullscreen::currentFullScreenElementForBindingFrom(
244 Document& document) {
245 Element* element = currentFullScreenElementFrom(document);
246 if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled())
247 return element;
248
249 // For Shadow DOM V0 compatibility: We allow returning an element in V0 shadow
250 // tree, even though it leaks the Shadow DOM.
251 if (element->isInV0ShadowTree()) {
252 UseCounter::count(document,
253 UseCounter::DocumentFullscreenElementInV0Shadow);
254 return element;
255 }
256 return document.adjustedElement(*element);
257 }
258
259 Fullscreen::Fullscreen(Document& document)
260 : ContextLifecycleObserver(&document),
261 m_fullScreenLayoutObject(nullptr),
262 m_eventQueueTimer(this, &Fullscreen::eventQueueTimerFired),
263 m_forCrossProcessDescendant(false) {
264 document.setHasFullscreenSupplement();
265 }
266
267 Fullscreen::~Fullscreen() {}
268
269 inline Document* Fullscreen::document() {
270 return toDocument(lifecycleContext());
271 }
272
273 void Fullscreen::contextDestroyed() {
274 m_eventQueue.clear();
275
276 if (m_fullScreenLayoutObject)
277 m_fullScreenLayoutObject->destroy();
278
279 m_currentFullScreenElement = nullptr;
280 m_fullscreenElementStack.clear();
281 }
282
283 // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen 299 // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen
284 void Fullscreen::requestFullscreen(Element& element, 300 void Fullscreen::requestFullscreen(Element& pending, RequestType requestType) {
285 RequestType requestType, 301 Document& document = pending.document();
286 bool forCrossProcessDescendant) {
287 Document& document = element.document();
288
289 // Use counters only need to be incremented in the process of the actual
290 // fullscreen element.
291 if (!forCrossProcessDescendant) {
292 if (document.isSecureContext()) {
293 UseCounter::count(document, UseCounter::FullscreenSecureOrigin);
294 } else {
295 UseCounter::count(document, UseCounter::FullscreenInsecureOrigin);
296 HostsUsingFeatures::countAnyWorld(
297 document, HostsUsingFeatures::Feature::FullscreenInsecureHost);
298 }
299 }
300 302
301 // Ignore this request if the document is not in a live frame. 303 // Ignore this request if the document is not in a live frame.
304 // TODO(foolip): This would make sense as part of fullscreenIsSupported.
302 if (!document.isActive()) 305 if (!document.isActive())
303 return; 306 return;
304 307
305 // If |element| is on top of |doc|'s fullscreen element stack, terminate these 308 if (document.isSecureContext()) {
306 // substeps. 309 UseCounter::count(document, UseCounter::FullscreenSecureOrigin);
307 if (&element == fullscreenElementFrom(document)) 310 } else {
308 return; 311 UseCounter::count(document, UseCounter::FullscreenInsecureOrigin);
312 HostsUsingFeatures::countAnyWorld(
313 document, HostsUsingFeatures::Feature::FullscreenInsecureHost);
314 }
309 315
316 // 1. Let |pending| be the context object.
317
318 // 2. Let |error| be false.
319
320 // 3. Let |promise| be a new promise.
321 // TODO(foolip): Promises. https://crbug.com/644637
322
323 // 4. If any of the following conditions are false, then set |error| to
324 // true:
325 bool error = true;
310 do { 326 do {
311 // 1. If any of the following conditions are false, then terminate these 327 // |pending|'s namespace is the HTML namespace or |pending| is an SVG
312 // steps and queue a task to fire an event named fullscreenerror with its
313 // bubbles attribute set to true on the context object's node document:
314
315 // |element|'s namespace is the HTML namespace or |element| is an SVG
316 // svg or MathML math element. 328 // svg or MathML math element.
317 // Note: MathML is not supported. 329 // Note: MathML is not supported.
318 if (!element.isHTMLElement() && !isSVGSVGElement(element)) 330 if (!pending.isHTMLElement() && !isSVGSVGElement(pending))
319 break; 331 break;
320 332
321 // The fullscreen element ready check for |element| returns true. 333 // The fullscreen element ready check for |pending| returns false.
322 if (!fullscreenElementReady(element)) 334 if (!fullscreenElementReady(pending))
323 break; 335 break;
324 336
325 // Fullscreen is supported. 337 // Fullscreen is supported.
326 if (!fullscreenIsSupported(document)) 338 if (!fullscreenIsSupported(document))
327 break; 339 break;
328 340
329 // This algorithm is allowed to request fullscreen. 341 // This algorithm is allowed to request fullscreen.
330 // OOPIF: If |forCrossProcessDescendant| is true, requestFullscreen was 342 if (!allowedToRequestFullscreen(document))
331 // already called on a descendant element in another process, and
332 // getting here means that it was already allowed to request fullscreen.
333 if (!forCrossProcessDescendant && !allowedToRequestFullscreen(document))
334 break; 343 break;
335 344
336 // 2. Let doc be element's node document. (i.e. "this") 345 error = false;
337 346 } while (false);
338 // 3. Let docs be all doc's ancestor browsing context's documents (if any) 347
339 // and doc. 348 // 5. Return |promise|, and run the remaining steps in parallel.
340 // 349 // TODO(foolip): Promises. https://crbug.com/644637
341 // For OOPIF scenarios, |docs| will only contain documents for local 350
342 // ancestors, and remote ancestors will be processed in their 351 // 6. If |error| is false: Resize pending's top-level browsing context's
343 // respective processes. This preserves the spec's event firing order 352 // document's viewport's dimensions to match the dimensions of the screen of
344 // for local ancestors, but not for remote ancestors. However, that 353 // the output device. Optionally display a message how the end user can
345 // difference shouldn't be observable in practice: a fullscreenchange 354 // revert this.
346 // event handler would need to postMessage a frame in another renderer 355 if (!error) {
347 // process, where the message should be queued up and processed after 356 document.frameHost()->chromeClient().enterFullscreenForElement(&pending);
348 // the IPC that dispatches fullscreenchange. 357 } else {
349 HeapDeque<Member<Document>> docs; 358 document.enqueueAnimationFrameTask(WTF::bind(&animationFrameTaskAfterEnter,
350 359 wrapPersistent(&pending),
351 docs.prepend(&document); 360 requestType, true));
352 for (Frame* frame = document.frame()->tree().parent(); frame; 361 }
353 frame = frame->tree().parent()) { 362 }
354 if (frame->isLocalFrame()) 363
355 docs.prepend(toLocalFrame(frame)->document()); 364 void Fullscreen::didEnterFullscreenForElement(Element& pending,
365 RequestType requestType) {
366 // 7. As part of the next animation frame task, run these substeps:
367 pending.document().enqueueAnimationFrameTask(
368 WTF::bind(&animationFrameTaskAfterEnter, wrapPersistent(&pending),
369 requestType, false));
370 }
371
372 void Fullscreen::animationFrameTaskAfterEnter(Element* element,
373 RequestType requestType,
374 bool error) {
375 DCHECK(element);
376 Element& pending = *element;
377
378 // 7.1. If either |error| is true or the fullscreen element ready check for
379 // |pending| returns false, fire an event named fullscreenerror on
380 // |pending|'s node document, reject |promise| with a TypeError exception,
381 // and terminate these steps.
382 // TODO(foolip): Promises. https://crbug.com/644637
383 if (error || !fullscreenElementReady(pending)) {
384 // TODO(foolip): What if we just entered fullscreen but the ready check
385 // no longer passes, e.g. if the allowfullscreen attribute was just
386 // removed? We need to exit fullscreen again, or at least maybe.
387 fireErrorEvent(pending, requestType);
388 return;
389 }
390
391 // 7.2. Let |fullscreenElements| be an ordered set initially consisting of
392 // |pending|.
393 HeapDeque<Member<Element>> fullscreenElements;
394 fullscreenElements.append(pending);
395
396 // 7.3. While the first element in |fullscreenElements| is in a nested
397 // browsing context, prepend its browsing context container to
398 // |fullscreenElements|.
399 //
400 // OOPIF: |fullscreenElements| will only contain elements for local
401 // ancestors, and remote ancestors will be processed in their respective
402 // processes. This preserves the spec's event firing order for local
403 // ancestors, but not for remote ancestors. However, that difference
404 // shouldn't be observable in practice: a fullscreenchange event handler
405 // would need to postMessage a frame in another renderer process, where the
406 // message should be queued up and processed after the IPC that dispatches
407 // fullscreenchange.
408 for (Frame* frame = pending.document().frame(); frame;
409 frame = frame->tree().parent()) {
410 if (frame->owner() && frame->owner()->isLocal()) {
411 Element* element = toHTMLFrameOwnerElement(frame->owner());
412 if (!isFullscreenElement(*element))
413 fullscreenElements.prepend(element);
356 } 414 }
357 415 }
358 // 4. For each document in docs, run these substeps: 416
359 HeapDeque<Member<Document>>::iterator current = docs.begin(), 417 // 7.4. Let |eventDocs| be an empty list.
360 following = docs.begin(); 418 // Note: Rather than creating a separate |eventDocs| list with a subset of
361 419 // the |fullscreenElements|' elements' documents, those elements are not
362 do { 420 // added to |fullscreenElements| at all, which simplifies the following.
363 ++following; 421
364 422 // 7.5. For each |element| in |fullscreenElements|, in order, run these
365 // 1. Let following document be the document after document in docs, or 423 // subsubsteps:
366 // null if there is no such document. 424 for (Element* element : fullscreenElements) {
367 Document* currentDoc = *current; 425 // 7.5.1. Let |doc| be |element|'s node document.
368 Document* followingDoc = following != docs.end() ? *following : nullptr; 426
369 427 // 7.5.2. If |element| is |doc|'s fullscreen element, terminate these
370 // 2. If following document is null, push context object on document's 428 // subsubsteps.
371 // fullscreen element stack, and queue a task to fire an event named 429
372 // fullscreenchange with its bubbles attribute set to true on the 430 // 7.5.3. Otherwise, append |doc| to |eventDocs|.
373 // document. 431
374 if (!followingDoc) { 432 // 7.5.4. If |element| is |pending| and |pending| is an iframe element,
375 from(*currentDoc).pushFullscreenElementStack(element, requestType); 433 // set |element|'s iframe fullscreen flag.
376 from(document).enqueueChangeEvent(*currentDoc, requestType); 434 // TODO(foolip): Support the iframe fullscreen flag.
377 continue; 435 // https://crbug.com/644695
378 } 436
379 437 // 7.5.5. Fullscreen |element| within |doc|.
380 // 3. Otherwise, if document's fullscreen element stack is either empty or 438 fullscreen(*element, requestType);
381 // its top element is not following document's browsing context container, 439 }
382 Element* topElement = fullscreenElementFrom(*currentDoc); 440
383 HTMLFrameOwnerElement* followingOwner = 441 // 7.6. For each |doc| in |eventDocs|, in order, fire an event named
384 findContainerForDescendant(*currentDoc, *followingDoc); 442 // fullscreenchange on |doc|.
385 if (!topElement || topElement != followingOwner) { 443 for (Element* element : fullscreenElements)
386 // ...push following document's browsing context container on document's 444 fireChangeEvent(*element, requestType);
387 // fullscreen element stack, and queue a task to fire an event named 445
388 // fullscreenchange with its bubbles attribute set to true on document. 446 // 7.7. Fulfill |promise| with undefined.
389 from(*currentDoc) 447 // TODO(foolip): Promises. https://crbug.com/644637
390 .pushFullscreenElementStack(*followingOwner, requestType);
391 from(document).enqueueChangeEvent(*currentDoc, requestType);
392 continue;
393 }
394
395 // 4. Otherwise, do nothing for this document. It stays the same.
396 } while (++current != docs.end());
397
398 from(document).m_forCrossProcessDescendant = forCrossProcessDescendant;
399
400 // 5. Return, and run the remaining steps asynchronously.
401 // 6. Optionally, perform some animation.
402 document.frameHost()->chromeClient().enterFullscreenForElement(&element);
403
404 // 7. Optionally, display a message indicating how the user can exit
405 // displaying the context object fullscreen.
406 return;
407 } while (false);
408
409 from(document).enqueueErrorEvent(element, requestType);
410 } 448 }
411 449
412 // https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen 450 // https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen
413 void Fullscreen::fullyExitFullscreen(Document& document) { 451 void Fullscreen::fullyExitFullscreen(Document& document) {
414 // To fully exit fullscreen, run these steps: 452 // 1. If |document|'s fullscreen element is null, terminate these steps.
415 453 Element* fullscreenElement = Fullscreen::fullscreenElement(document);
416 // 1. Let |doc| be the top-level browsing context's document. 454 if (!fullscreenElement)
417 // 455 return;
418 // Since the top-level browsing context's document might be unavailable in 456
419 // OOPIF scenarios (i.e., when the top frame is remote), this actually uses 457 // 2. Unfullscreen elements whose fullscreen flag is set, within
420 // the Document of the topmost local ancestor frame. Without OOPIF, this 458 // |document|'s top layer, except for |document|'s fullscreen element.
421 // will be the top frame's document. With OOPIF, each renderer process for 459 for (Element* element : document.topLayerElements()) {
422 // the current page will separately call fullyExitFullscreen to cover all 460 if (element->isFullscreen() && element != fullscreenElement)
423 // local frames in each process. 461 unfullscreen(*element);
424 Document& doc = topmostLocalAncestor(document); 462 }
425 463
426 // 2. If |doc|'s fullscreen element stack is empty, terminate these steps. 464 // 3. Exit fullscreen |document|.
427 if (!fullscreenElementFrom(doc)) 465 exitFullscreen(document);
428 return;
429
430 // 3. Remove elements from |doc|'s fullscreen element stack until only the top
431 // element is left.
432 size_t stackSize = from(doc).m_fullscreenElementStack.size();
433 from(doc).m_fullscreenElementStack.remove(0, stackSize - 1);
434 DCHECK_EQ(from(doc).m_fullscreenElementStack.size(), 1u);
435
436 // 4. Act as if the exitFullscreen() method was invoked on |doc|.
437 exitFullscreen(doc);
438 } 466 }
439 467
440 // https://fullscreen.spec.whatwg.org/#exit-fullscreen 468 // https://fullscreen.spec.whatwg.org/#exit-fullscreen
441 void Fullscreen::exitFullscreen(Document& document) { 469 void Fullscreen::exitFullscreen(Document& doc) {
442 // The exitFullscreen() method must run these steps: 470 if (!doc.isActive())
443 471 return;
444 // 1. Let doc be the context object. (i.e. "this") 472
445 if (!document.isActive()) 473 // 1. Let |promise| be a new promise.
446 return; 474 // 2. If |doc|'s fullscreen element is null, reject |promise| with a
447 475 // TypeError exception, and return |promise|.
448 // 2. If doc's fullscreen element stack is empty, terminate these steps. 476 // TODO(foolip): Promises. https://crbug.com/644637
449 if (!fullscreenElementFrom(document)) 477 if (!fullscreenElement(doc))
450 return; 478 return;
451 479
452 // 3. Let descendants be all the doc's descendant browsing context's documents 480 // 3. Let |resize| be false.
453 // with a non-empty fullscreen element stack (if any), ordered so that the 481 bool resize = false;
454 // child of the doc is last and the document furthest away from the doc is 482
455 // first. 483 // 4. Let |docs| be the result of collecting documents to unfullscreen given
456 HeapDeque<Member<Document>> descendants; 484 // |doc|.
457 for (Frame* descendant = 485 HeapVector<Member<Document>> docs = collectDocumentsToUnfullscreen(doc);
458 document.frame() ? document.frame()->tree().traverseNext() : nullptr; 486
459 descendant; descendant = descendant->tree().traverseNext()) { 487 // 5. Let |topLevelDoc| be |doc|'s top-level browsing context's document.
488 Document& topLevelDoc = topmostLocalAncestor(doc);
489
490 // 6. If |topLevelDoc| is in |docs|, set |resize| to true.
491 // OOPIF: Use |isLocalRoot| as oppposed to |isMainFrame|, so that if the
492 // main frame is in another process, we will still fully exit fullscreen
493 // even though that's wrong if the main frame was in nested fullscreen.
494 // TODO(alexmos): Deal with nested fullscreen cases, see
495 // https://crbug.com/617369.
496 if (!docs.isEmpty() && docs.last()->frame()->isLocalRoot())
497 resize = true;
498
499 // 7. Return |promise|, and run the remaining steps in parallel.
500 // TODO(foolip): Promises. https://crbug.com/644637
501
502 // 8. If |resize| is true, resize |topLevelDoc|'s viewport to its "normal"
503 // dimensions.
504 if (resize) {
505 topLevelDoc.frameHost()->chromeClient().exitFullscreenForElement(
506 fullscreenElement(topLevelDoc));
507 } else {
508 topLevelDoc.enqueueAnimationFrameTask(
509 WTF::bind(&animationFrameTaskAfterExit, wrapPersistent(&topLevelDoc)));
510 }
511 }
512
513 void Fullscreen::didExitFullscreen(Document& doc) {
514 // 9. As part of the next animation frame task, run these substeps:
515 doc.enqueueAnimationFrameTask(
516 WTF::bind(&animationFrameTaskAfterExit, wrapPersistent(&doc)));
517 }
518
519 void Fullscreen::animationFrameTaskAfterExit(Document* document) {
520 DCHECK(document);
521 Document& doc = *document;
522
523 // 9.1. Let |exitDocs| be the result of collecting documents to unfullscreen
524 // given |doc|.
525 HeapVector<Member<Document>> exitDocs = collectDocumentsToUnfullscreen(doc);
526
527 // 9.2. If |resize| is true and |topLevelDoc| is not in |exitDocs|, fully
528 // exit fullscreen |topLevelDoc|, reject promise with a TypeError exception,
529 // and terminate these steps.
530 // TODO(foolip): Promises. https://crbug.com/644637
531 if (exitDocs.isEmpty() || !exitDocs.last()->frame()->isLocalRoot()) {
532 return;
533 }
534
535 // 9.3. If |exitDocs| is the empty set, append |doc| to |exitDocs|.
536 if (exitDocs.isEmpty())
537 exitDocs.append(&doc);
538
539 // 9.4. If |exitDocs|'s last document has a browsing context container,
540 // append that browsing context container's node document to |exitDocs|.
541 // OOPIF: Skip over remote frames, assuming that they have exactly one
542 // element in their fullscreen element stacks, thereby erring on the side of
543 // exiting fullscreen.
544 // TODO(alexmos): Deal with nested fullscreen cases, see
545 // https://crbug.com/617369.
546 for (Frame* frame = exitDocs.last()->frame()->tree().parent(); frame;
547 frame = frame->tree().parent()) {
548 if (frame->isLocalFrame()) {
549 if (Document* document = toLocalFrame(frame)->document()) {
550 exitDocs.append(document);
551 break;
552 }
553 }
554 }
555
556 // 9.5. Let |descendantDocs| be an ordered set consisting of |doc|'s
557 // descendant browsing contexts' documents whose fullscreen element is
558 // non-null, if any, in *reverse* tree order.
559 HeapDeque<Member<Document>> descendantDocs;
560 for (Frame* descendant = doc.frame()->tree().firstChild(); descendant;
561 descendant = descendant->tree().traverseNext(doc.frame())) {
460 if (!descendant->isLocalFrame()) 562 if (!descendant->isLocalFrame())
461 continue; 563 continue;
462 DCHECK(toLocalFrame(descendant)->document()); 564 DCHECK(toLocalFrame(descendant)->document());
463 if (fullscreenElementFrom(*toLocalFrame(descendant)->document())) 565 if (fullscreenElement(*toLocalFrame(descendant)->document()))
464 descendants.prepend(toLocalFrame(descendant)->document()); 566 descendantDocs.prepend(toLocalFrame(descendant)->document());
465 } 567 }
466 568
467 // 4. For each descendant in descendants, empty descendant's fullscreen 569 HeapVector<Member<Element>> eventElements;
468 // element stack, and queue a task to fire an event named fullscreenchange 570
469 // with its bubbles attribute set to true on descendant. 571 // 9.6. For each |descendantDoc| in |descendantDocs|, in order, unfullscreen
470 for (auto& descendant : descendants) { 572 // |descendantDoc|.
471 DCHECK(descendant); 573 for (Document* descendantDoc : descendantDocs) {
472 RequestType requestType = 574 eventElements.append(fullscreenElement(*descendantDoc));
473 from(*descendant).m_fullscreenElementStack.last().second; 575 unfullscreen(*descendantDoc);
474 from(*descendant).clearFullscreenElementStack(); 576 }
475 from(document).enqueueChangeEvent(*descendant, requestType); 577
476 } 578 // 9.7. For each |exitDoc| in |exitDocs|, in order, unfullscreen |exitDoc|'s
477 579 // fullscreen element.
478 // 5. While doc is not null, run these substeps: 580 for (Document* exitDoc : exitDocs) {
479 Element* newTop = nullptr; 581 eventElements.append(fullscreenElement(*exitDoc));
480 for (Document* currentDoc = &document; currentDoc;) { 582 unfullscreen(*fullscreenElement(*exitDoc));
481 RequestType requestType = 583 }
482 from(*currentDoc).m_fullscreenElementStack.last().second; 584
483 585 // 9.8. For each |descendantDoc| in |descendantDocs|, in order, fire an
484 // 1. Pop the top element of doc's fullscreen element stack. 586 // event named fullscreenchange on |descendantDoc|.
485 from(*currentDoc).popFullscreenElementStack(); 587 // 9.9. For each |exitDoc| in |exitDocs|, in order, fire an event named
486 588 // fullscreenchange on |exitDoc|.
487 // If doc's fullscreen element stack is non-empty and the element now at 589 // TODO(foolip): Fire the unprefixed event.
488 // the top is either not in a document or its node document is not doc, 590 for (Element* element : eventElements)
489 // repeat this substep. 591 fireChangeEvent(*element, PrefixedRequest);
490 newTop = fullscreenElementFrom(*currentDoc); 592
491 if (newTop && (!newTop->isConnected() || newTop->document() != currentDoc)) 593 // 9.10. Fulfill |promise| with undefined.
492 continue; 594 // TODO(foolip): Promises. https://crbug.com/644637
493
494 // 2. Queue a task to fire an event named fullscreenchange with its bubbles
495 // attribute set to true on doc.
496 from(document).enqueueChangeEvent(*currentDoc, requestType);
497
498 // 3. If doc's fullscreen element stack is empty and doc's browsing context
499 // has a browsing context container, set doc to that browsing context
500 // container's node document.
501 //
502 // OOPIF: If browsing context container's document is in another
503 // process, keep moving up the ancestor chain and looking for a
504 // browsing context container with a local document.
505 // TODO(alexmos): Deal with nested fullscreen cases, see
506 // https://crbug.com/617369.
507 if (!newTop) {
508 Frame* frame = currentDoc->frame()->tree().parent();
509 while (frame && frame->isRemoteFrame())
510 frame = frame->tree().parent();
511 if (frame) {
512 currentDoc = toLocalFrame(frame)->document();
513 continue;
514 }
515 }
516
517 // 4. Otherwise, set doc to null.
518 currentDoc = nullptr;
519 }
520
521 // 6. Return, and run the remaining steps asynchronously.
522 // 7. Optionally, perform some animation.
523
524 FrameHost* host = document.frameHost();
525
526 // Speculative fix for engaget.com/videos per crbug.com/336239.
527 // FIXME: This check is wrong. We DCHECK(document->isActive()) above
528 // so this should be redundant and should be removed!
529 if (!host)
530 return;
531
532 // Only exit out of full screen window mode if there are no remaining elements
533 // in the full screen stack.
534 if (!newTop) {
535 // FIXME: if the frame exiting fullscreen is not the frame that entered
536 // fullscreen (but a parent frame for example),
537 // m_currentFullScreenElement might be null. We want to pass an element
538 // that is part of the document so we will pass the documentElement in
539 // that case. This should be fix by exiting fullscreen for a frame
540 // instead of an element, see https://crbug.com/441259
541 Element* currentFullScreenElement = currentFullScreenElementFrom(document);
542 host->chromeClient().exitFullscreenForElement(
543 currentFullScreenElement ? currentFullScreenElement
544 : document.documentElement());
545 return;
546 }
547
548 // Otherwise, notify the chrome of the new full screen element.
549 host->chromeClient().enterFullscreenForElement(newTop);
550 } 595 }
551 596
552 // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled 597 // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled
553 bool Fullscreen::fullscreenEnabled(Document& document) { 598 bool Fullscreen::fullscreenEnabled(Document& document) {
554 // The fullscreenEnabled attribute's getter must return true if the context 599 // The fullscreenEnabled attribute's getter must return true if the context
555 // object is allowed to use the feature indicated by attribute name 600 // object is allowed to use the feature indicated by attribute name
556 // allowfullscreen and fullscreen is supported, and false otherwise. 601 // allowfullscreen and fullscreen is supported, and false otherwise.
557 return allowedToUseFullscreen(document.frame()) && 602 return allowedToUseFullscreen(document.frame()) &&
558 fullscreenIsSupported(document); 603 fullscreenIsSupported(document);
559 } 604 }
560 605
561 void Fullscreen::didEnterFullscreenForElement(Element* element) { 606 void Fullscreen::didUpdateSize(Element& element) {
562 DCHECK(element); 607 // StyleAdjuster will set the size so we need to do a style recalc.
563 if (!document()->isActive()) 608 // Normally changing size means layout so just doing a style recalc is a
564 return; 609 // bit surprising.
565 610 element.setNeedsStyleRecalc(
566 if (m_fullScreenLayoutObject) 611 LocalStyleChange,
567 m_fullScreenLayoutObject->unwrapLayoutObject(); 612 StyleChangeReasonForTracing::create(StyleChangeReason::Fullscreen));
568
569 m_currentFullScreenElement = element;
570
571 // Create a placeholder block for a the full-screen element, to keep the page
572 // from reflowing when the element is removed from the normal flow. Only do
573 // this for a LayoutBox, as only a box will have a frameRect. The placeholder
574 // will be created in setFullScreenLayoutObject() during layout.
575 LayoutObject* layoutObject = m_currentFullScreenElement->layoutObject();
576 bool shouldCreatePlaceholder = layoutObject && layoutObject->isBox();
577 if (shouldCreatePlaceholder) {
578 m_savedPlaceholderFrameRect = toLayoutBox(layoutObject)->frameRect();
579 m_savedPlaceholderComputedStyle =
580 ComputedStyle::clone(layoutObject->styleRef());
581 }
582
583 // TODO(alexmos): When |m_forCrossProcessDescendant| is true, some of
584 // this layout work has already been done in another process, so it should
585 // not be necessary to repeat it here.
586 if (m_currentFullScreenElement != document()->documentElement())
587 LayoutFullScreen::wrapLayoutObject(
588 layoutObject, layoutObject ? layoutObject->parent() : 0, document());
589
590 // When |m_forCrossProcessDescendant| is true, m_currentFullScreenElement
591 // corresponds to the HTMLFrameOwnerElement for the out-of-process iframe
592 // that contains the actual fullscreen element. Hence, it must also set
593 // the ContainsFullScreenElement flag (so that it gains the
594 // -webkit-full-screen-ancestor style).
595 if (m_forCrossProcessDescendant) {
596 DCHECK(m_currentFullScreenElement->isFrameOwnerElement());
597 DCHECK(toHTMLFrameOwnerElement(m_currentFullScreenElement)
598 ->contentFrame()
599 ->isRemoteFrame());
600 m_currentFullScreenElement->setContainsFullScreenElement(true);
601 }
602
603 m_currentFullScreenElement
604 ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
605
606 document()->styleEngine().ensureUAStyleForFullscreen();
607 m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen);
608
609 // FIXME: This should not call updateStyleAndLayoutTree.
610 document()->updateStyleAndLayoutTree();
611
612 m_currentFullScreenElement->didBecomeFullscreenElement();
613
614 if (document()->frame())
615 document()->frame()->eventHandler().scheduleHoverStateUpdate();
616
617 m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE);
618 } 613 }
619 614
620 void Fullscreen::didExitFullscreen() { 615 void Fullscreen::elementRemoved(Element& node) {
621 if (!m_currentFullScreenElement) 616 // Whenever the removing steps run with an oldNode, run these steps:
622 return;
623 617
624 if (!document()->isActive()) 618 // 1. Let |nodes| be |oldNode|'s inclusive descendants that have their
625 return; 619 // fullscreen flag set, in tree order.
626 620
627 m_currentFullScreenElement->willStopBeingFullscreenElement(); 621 // 2. For each |node| in |nodes|, run these substeps:
628 622
629 if (m_forCrossProcessDescendant) 623 // Note: The iteration of descendants is done in
630 m_currentFullScreenElement->setContainsFullScreenElement(false); 624 // ContainerNode::notifyNodeRemoved, and Element::removedFrom calls
625 // Fullscreen::elementRemoved only for fullscreen elements.
626 DCHECK(node.isInTopLayer());
627 DCHECK(node.isFullscreen());
631 628
632 m_currentFullScreenElement 629 // 2.1. If |node| is its node document's fullscreen element, exit fullscreen
633 ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false); 630 // that document.
634 631 if (&node == fullscreenElement(node.document())) {
635 if (m_fullScreenLayoutObject) 632 exitFullscreen(node.document());
636 LayoutFullScreenItem(m_fullScreenLayoutObject).unwrapLayoutObject();
637
638 document()->styleEngine().ensureUAStyleForFullscreen();
639 m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen);
640 m_currentFullScreenElement = nullptr;
641
642 if (document()->frame())
643 document()->frame()->eventHandler().scheduleHoverStateUpdate();
644
645 // When fullyExitFullscreen is called, we call exitFullscreen on the
646 // topDocument(). That means that the events will be queued there. So if we
647 // have no events here, start the timer on the exiting document.
648 Document* exitingDocument = document();
649 if (m_eventQueue.isEmpty())
650 exitingDocument = &topmostLocalAncestor(*document());
651 DCHECK(exitingDocument);
652 from(*exitingDocument).m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE);
653
654 m_forCrossProcessDescendant = false;
655 }
656
657 void Fullscreen::setFullScreenLayoutObject(LayoutFullScreen* layoutObject) {
658 if (layoutObject == m_fullScreenLayoutObject)
659 return;
660
661 if (layoutObject && m_savedPlaceholderComputedStyle) {
662 layoutObject->createPlaceholder(m_savedPlaceholderComputedStyle.release(),
663 m_savedPlaceholderFrameRect);
664 } else if (layoutObject && m_fullScreenLayoutObject &&
665 m_fullScreenLayoutObject->placeholder()) {
666 LayoutBlockFlow* placeholder = m_fullScreenLayoutObject->placeholder();
667 layoutObject->createPlaceholder(
668 ComputedStyle::clone(placeholder->styleRef()),
669 placeholder->frameRect());
670 }
671
672 if (m_fullScreenLayoutObject)
673 m_fullScreenLayoutObject->unwrapLayoutObject();
674 DCHECK(!m_fullScreenLayoutObject);
675
676 m_fullScreenLayoutObject = layoutObject;
677 }
678
679 void Fullscreen::fullScreenLayoutObjectDestroyed() {
680 m_fullScreenLayoutObject = nullptr;
681 }
682
683 void Fullscreen::enqueueChangeEvent(Document& document,
684 RequestType requestType) {
685 Event* event;
686 if (requestType == UnprefixedRequest) {
687 event = createEvent(EventTypeNames::fullscreenchange, document);
688 } else {
689 DCHECK(document.hasFullscreenSupplement());
690 Fullscreen& fullscreen = from(document);
691 EventTarget* target = fullscreen.fullscreenElement();
692 if (!target)
693 target = fullscreen.currentFullScreenElement();
694 if (!target)
695 target = &document;
696 event = createEvent(EventTypeNames::webkitfullscreenchange, *target);
697 }
698 m_eventQueue.append(event);
699 // NOTE: The timer is started in
700 // didEnterFullscreenForElement/didExitFullscreen.
701 }
702
703 void Fullscreen::enqueueErrorEvent(Element& element, RequestType requestType) {
704 Event* event;
705 if (requestType == UnprefixedRequest)
706 event = createEvent(EventTypeNames::fullscreenerror, element.document());
707 else
708 event = createEvent(EventTypeNames::webkitfullscreenerror, element);
709 m_eventQueue.append(event);
710 m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE);
711 }
712
713 void Fullscreen::eventQueueTimerFired(TimerBase*) {
714 HeapDeque<Member<Event>> eventQueue;
715 m_eventQueue.swap(eventQueue);
716
717 while (!eventQueue.isEmpty()) {
718 Event* event = eventQueue.takeFirst();
719 Node* target = event->target()->toNode();
720
721 // If the element was removed from our tree, also message the
722 // documentElement.
723 if (!target->isConnected() && document()->documentElement()) {
724 DCHECK(isPrefixed(event->type()));
725 eventQueue.append(
726 createEvent(event->type(), *document()->documentElement()));
727 }
728
729 target->dispatchEvent(event);
730 }
731 }
732
733 void Fullscreen::elementRemoved(Element& oldNode) {
734 // Whenever the removing steps run with an |oldNode| and |oldNode| is in its
735 // node document's fullscreen element stack, run these steps:
736
737 // 1. If |oldNode| is at the top of its node document's fullscreen element
738 // stack, act as if the exitFullscreen() method was invoked on that document.
739 if (fullscreenElement() == &oldNode) {
740 exitFullscreen(oldNode.document());
741 return; 633 return;
742 } 634 }
743 635
744 // 2. Otherwise, remove |oldNode| from its node document's fullscreen element 636 // 2.2. Otherwise, unfullscreen |node| within its node document.
745 // stack. 637 unfullscreen(node);
746 for (size_t i = 0; i < m_fullscreenElementStack.size(); ++i) {
747 if (m_fullscreenElementStack[i].first.get() == &oldNode) {
748 m_fullscreenElementStack.remove(i);
749 return;
750 }
751 }
752
753 // NOTE: |oldNode| was not in the fullscreen element stack.
754 }
755
756 void Fullscreen::clearFullscreenElementStack() {
757 m_fullscreenElementStack.clear();
758 }
759
760 void Fullscreen::popFullscreenElementStack() {
761 if (m_fullscreenElementStack.isEmpty())
762 return;
763
764 m_fullscreenElementStack.removeLast();
765 }
766
767 void Fullscreen::pushFullscreenElementStack(Element& element,
768 RequestType requestType) {
769 m_fullscreenElementStack.append(std::make_pair(&element, requestType));
770 }
771
772 DEFINE_TRACE(Fullscreen) {
773 visitor->trace(m_currentFullScreenElement);
774 visitor->trace(m_fullscreenElementStack);
775 visitor->trace(m_eventQueue);
776 Supplement<Document>::trace(visitor);
777 ContextLifecycleObserver::trace(visitor);
778 } 638 }
779 639
780 } // namespace blink 640 } // namespace blink
OLDNEW
« no previous file with comments | « third_party/WebKit/Source/core/dom/Fullscreen.h ('k') | third_party/WebKit/Source/core/dom/LayoutTreeBuilder.cpp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698