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

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

Issue 2667913002: Revert of Sync requestFullscreen() and exitFullscreen() algorithms with the spec (patchset #3 id:40… (Closed)
Patch Set: Created 3 years, 10 months 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 15 matching lines...) Expand all
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/ConditionalFeatures.h" 32 #include "bindings/core/v8/ConditionalFeatures.h"
33 #include "core/dom/Document.h" 33 #include "core/dom/Document.h"
34 #include "core/dom/ElementTraversal.h" 34 #include "core/dom/ElementTraversal.h"
35 #include "core/dom/StyleEngine.h" 35 #include "core/dom/StyleEngine.h"
36 #include "core/dom/TaskRunnerHelper.h"
36 #include "core/events/Event.h" 37 #include "core/events/Event.h"
37 #include "core/frame/FrameView.h" 38 #include "core/frame/FrameView.h"
38 #include "core/frame/HostsUsingFeatures.h" 39 #include "core/frame/HostsUsingFeatures.h"
39 #include "core/frame/LocalFrame.h" 40 #include "core/frame/LocalFrame.h"
40 #include "core/frame/Settings.h" 41 #include "core/frame/Settings.h"
41 #include "core/frame/UseCounter.h" 42 #include "core/frame/UseCounter.h"
42 #include "core/html/HTMLIFrameElement.h" 43 #include "core/html/HTMLIFrameElement.h"
43 #include "core/input/EventHandler.h" 44 #include "core/input/EventHandler.h"
44 #include "core/inspector/ConsoleMessage.h" 45 #include "core/inspector/ConsoleMessage.h"
45 #include "core/layout/LayoutBlockFlow.h" 46 #include "core/layout/LayoutBlockFlow.h"
(...skipping 133 matching lines...) Expand 10 before | Expand all | Expand 10 after
179 // |element|'s node document's browsing context's browsing context container, 180 // |element|'s node document's browsing context's browsing context container,
180 // or it has no browsing context container. 181 // or it has no browsing context container.
181 if (const Element* owner = element.document().localOwner()) { 182 if (const Element* owner = element.document().localOwner()) {
182 if (!fullscreenElementReady(*owner)) 183 if (!fullscreenElementReady(*owner))
183 return false; 184 return false;
184 } 185 }
185 186
186 return true; 187 return true;
187 } 188 }
188 189
189 // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen step 4: 190 bool isPrefixed(const AtomicString& type) {
190 bool requestFullscreenConditionsMet(Element& pending, Document& document) { 191 return type == EventTypeNames::webkitfullscreenchange ||
191 // |pending|'s namespace is the HTML namespace or |pending| is an SVG svg or 192 type == EventTypeNames::webkitfullscreenerror;
192 // MathML math element. Note: MathML is not supported. 193 }
193 if (!pending.isHTMLElement() && !isSVGSVGElement(pending))
194 return false;
195 194
196 // The fullscreen element ready check for |pending| returns false. 195 Event* createEvent(const AtomicString& type, EventTarget& target) {
197 if (!fullscreenElementReady(pending)) 196 EventInit initializer;
198 return false; 197 initializer.setBubbles(isPrefixed(type));
199 198 Event* event = Event::create(type, initializer);
200 // Fullscreen is supported. 199 event->setTarget(&target);
201 if (!fullscreenIsSupported(document)) 200 return event;
202 return false;
203
204 // This algorithm is allowed to request fullscreen.
205 if (!allowedToRequestFullscreen(document))
206 return false;
207
208 return true;
209 } 201 }
210 202
211 // Walks the frame tree and returns the first local ancestor frame, if any. 203 // Walks the frame tree and returns the first local ancestor frame, if any.
212 LocalFrame* nextLocalAncestor(Frame& frame) { 204 LocalFrame* nextLocalAncestor(Frame& frame) {
213 Frame* parent = frame.tree().parent(); 205 Frame* parent = frame.tree().parent();
214 if (!parent) 206 if (!parent)
215 return nullptr; 207 return nullptr;
216 if (parent->isLocalFrame()) 208 if (parent->isLocalFrame())
217 return toLocalFrame(parent); 209 return toLocalFrame(parent);
218 return nextLocalAncestor(*parent); 210 return nextLocalAncestor(*parent);
219 } 211 }
220 212
221 // Walks the document's frame tree and returns the document of the first local 213 // Walks the document's frame tree and returns the document of the first local
222 // ancestor frame, if any. 214 // ancestor frame, if any.
223 Document* nextLocalAncestor(Document& document) { 215 Document* nextLocalAncestor(Document& document) {
224 LocalFrame* frame = document.frame(); 216 LocalFrame* frame = document.frame();
225 if (!frame) 217 if (!frame)
226 return nullptr; 218 return nullptr;
227 LocalFrame* next = nextLocalAncestor(*frame); 219 LocalFrame* next = nextLocalAncestor(*document.frame());
228 if (!next) 220 if (!next)
229 return nullptr; 221 return nullptr;
230 DCHECK(next->document()); 222 DCHECK(next->document());
231 return next->document(); 223 return next->document();
232 } 224 }
233 225
234 // Helper to walk the ancestor chain and return the Document of the topmost 226 // Helper to walk the ancestor chain and return the Document of the topmost
235 // local ancestor frame. Note that this is not the same as the topmost frame's 227 // local ancestor frame. Note that this is not the same as the topmost frame's
236 // Document, which might be unavailable in OOPIF scenarios. For example, with 228 // Document, which might be unavailable in OOPIF scenarios. For example, with
237 // OOPIFs, when called on the bottom frame's Document in a A-B-C-B hierarchy in 229 // OOPIFs, when called on the bottom frame's Document in a A-B-C-B hierarchy in
238 // process B, this will skip remote frame C and return this frame: A-[B]-C-B. 230 // process B, this will skip remote frame C and return this frame: A-[B]-C-B.
239 Document& topmostLocalAncestor(Document& document) { 231 Document& topmostLocalAncestor(Document& document) {
240 if (Document* next = nextLocalAncestor(document)) 232 if (Document* next = nextLocalAncestor(document))
241 return topmostLocalAncestor(*next); 233 return topmostLocalAncestor(*next);
242 return document; 234 return document;
243 } 235 }
244 236
245 // https://fullscreen.spec.whatwg.org/#collect-documents-to-unfullscreen 237 // Helper to find the browsing context container in |doc| that embeds the
246 HeapVector<Member<Document>> collectDocumentsToUnfullscreen( 238 // |descendant| Document, possibly through multiple levels of nesting. This
247 Document& doc, 239 // works even in OOPIF scenarios like A-B-A, where there may be remote frames
248 Fullscreen::ExitType exitType) { 240 // in between |doc| and |descendant|.
249 DCHECK(Fullscreen::fullscreenElementFrom(doc)); 241 HTMLFrameOwnerElement* findContainerForDescendant(const Document& doc,
250 242 const Document& descendant) {
251 // 1. If |doc|'s top layer consists of more than a single element that has 243 Frame* frame = descendant.frame();
252 // its fullscreen flag set, return the empty set. 244 while (frame->tree().parent() != doc.frame())
253 // TODO(foolip): See TODO in |fullyExitFullscreen()|. 245 frame = frame->tree().parent();
254 if (exitType != Fullscreen::ExitType::Fully && 246 return toHTMLFrameOwnerElement(frame->owner());
255 Fullscreen::fullscreenElementStackSizeFrom(doc) > 1)
256 return HeapVector<Member<Document>>();
257
258 // 2. Let |docs| be an ordered set consisting of |doc|.
259 HeapVector<Member<Document>> docs;
260 docs.push_back(&doc);
261
262 // 3. While |docs|'s last document ...
263 //
264 // OOPIF: Skip over remote frames, assuming that they have exactly one element
265 // in their fullscreen element stacks, thereby erring on the side of exiting
266 // fullscreen. TODO(alexmos): Deal with nested fullscreen cases, see
267 // https://crbug.com/617369.
268 for (Document* document = nextLocalAncestor(doc); document;
269 document = nextLocalAncestor(*document)) {
270 // ... has a browsing context container whose node document's top layer
271 // consists of a single element that has its fullscreen flag set and does
272 // not have its iframe fullscreen flag set (if any), append that node
273 // document to |docs|.
274 // TODO(foolip): Support the iframe fullscreen flag.
275 // https://crbug.com/644695
276 if (Fullscreen::fullscreenElementStackSizeFrom(*document) == 1)
277 docs.push_back(document);
278 else
279 break;
280 }
281
282 // 4. Return |docs|.
283 return docs;
284 } 247 }
285 248
286 // Creates a non-bubbling event with |document| as its target. 249 // Fullscreen status affects scroll paint properties through
287 Event* createEvent(const AtomicString& type, Document& document) { 250 // FrameView::userInputScrollable().
288 DCHECK(type == EventTypeNames::fullscreenchange || 251 void setNeedsPaintPropertyUpdate(Document* document) {
289 type == EventTypeNames::fullscreenerror); 252 if (!RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() ||
253 RuntimeEnabledFeatures::rootLayerScrollingEnabled())
254 return;
290 255
291 Event* event = Event::create(type); 256 if (!document)
292 event->setTarget(&document); 257 return;
293 return event;
294 }
295 258
296 // Creates a bubbling event with |element| as its target. If |element| is not 259 LocalFrame* frame = document->frame();
297 // connected to |document|, then |document| is used as the target instead. 260 if (!frame)
298 Event* createPrefixedEvent(const AtomicString& type, 261 return;
299 Element& element,
300 Document& document) {
301 DCHECK(type == EventTypeNames::webkitfullscreenchange ||
302 type == EventTypeNames::webkitfullscreenerror);
303 262
304 Event* event = Event::createBubble(type); 263 if (FrameView* frameView = frame->view())
305 if (element.isConnected() && element.document() == document) 264 frameView->setNeedsPaintPropertyUpdate();
306 event->setTarget(&element);
307 else
308 event->setTarget(&document);
309 return event;
310 }
311
312 Event* createChangeEvent(Document& document,
313 Element& element,
314 Fullscreen::RequestType requestType) {
315 if (requestType == Fullscreen::RequestType::Unprefixed)
316 return createEvent(EventTypeNames::fullscreenchange, document);
317 return createPrefixedEvent(EventTypeNames::webkitfullscreenchange, element,
318 document);
319 }
320
321 Event* createErrorEvent(Document& document,
322 Element& element,
323 Fullscreen::RequestType requestType) {
324 if (requestType == Fullscreen::RequestType::Unprefixed)
325 return createEvent(EventTypeNames::fullscreenerror, document);
326 return createPrefixedEvent(EventTypeNames::webkitfullscreenerror, element,
327 document);
328 }
329
330 void dispatchEvents(const HeapVector<Member<Event>>& events) {
331 for (Event* event : events)
332 event->target()->dispatchEvent(event);
333 } 265 }
334 266
335 } // anonymous namespace 267 } // anonymous namespace
336 268
337 const char* Fullscreen::supplementName() { 269 const char* Fullscreen::supplementName() {
338 return "Fullscreen"; 270 return "Fullscreen";
339 } 271 }
340 272
341 Fullscreen& Fullscreen::from(Document& document) { 273 Fullscreen& Fullscreen::from(Document& document) {
342 Fullscreen* fullscreen = fromIfExists(document); 274 Fullscreen* fullscreen = fromIfExists(document);
343 if (!fullscreen) { 275 if (!fullscreen) {
344 fullscreen = new Fullscreen(document); 276 fullscreen = new Fullscreen(document);
345 Supplement<Document>::provideTo(document, supplementName(), fullscreen); 277 Supplement<Document>::provideTo(document, supplementName(), fullscreen);
346 } 278 }
347 279
348 return *fullscreen; 280 return *fullscreen;
349 } 281 }
350 282
351 Fullscreen* Fullscreen::fromIfExistsSlow(Document& document) { 283 Fullscreen* Fullscreen::fromIfExistsSlow(Document& document) {
352 return static_cast<Fullscreen*>( 284 return static_cast<Fullscreen*>(
353 Supplement<Document>::from(document, supplementName())); 285 Supplement<Document>::from(document, supplementName()));
354 } 286 }
355 287
356 Element* Fullscreen::fullscreenElementFrom(Document& document) { 288 Element* Fullscreen::fullscreenElementFrom(Document& document) {
357 if (Fullscreen* found = fromIfExists(document)) 289 if (Fullscreen* found = fromIfExists(document))
358 return found->fullscreenElement(); 290 return found->fullscreenElement();
359 return nullptr; 291 return nullptr;
360 } 292 }
361 293
362 size_t Fullscreen::fullscreenElementStackSizeFrom(Document& document) {
363 if (Fullscreen* found = fromIfExists(document))
364 return found->m_fullscreenElementStack.size();
365 return 0;
366 }
367
368 Element* Fullscreen::fullscreenElementForBindingFrom(TreeScope& scope) { 294 Element* Fullscreen::fullscreenElementForBindingFrom(TreeScope& scope) {
369 Element* element = fullscreenElementFrom(scope.document()); 295 Element* element = fullscreenElementFrom(scope.document());
370 if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled()) 296 if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled())
371 return element; 297 return element;
372 298
373 // TODO(kochi): Once V0 code is removed, we can use the same logic for 299 // TODO(kochi): Once V0 code is removed, we can use the same logic for
374 // Document and ShadowRoot. 300 // Document and ShadowRoot.
375 if (!scope.rootNode().isShadowRoot()) { 301 if (!scope.rootNode().isShadowRoot()) {
376 // For Shadow DOM V0 compatibility: We allow returning an element in V0 302 // For Shadow DOM V0 compatibility: We allow returning an element in V0
377 // shadow tree, even though it leaks the Shadow DOM. 303 // shadow tree, even though it leaks the Shadow DOM.
378 if (element->isInV0ShadowTree()) { 304 if (element->isInV0ShadowTree()) {
379 UseCounter::count(scope.document(), 305 UseCounter::count(scope.document(),
380 UseCounter::DocumentFullscreenElementInV0Shadow); 306 UseCounter::DocumentFullscreenElementInV0Shadow);
381 return element; 307 return element;
382 } 308 }
383 } else if (!toShadowRoot(scope.rootNode()).isV1()) { 309 } else if (!toShadowRoot(scope.rootNode()).isV1()) {
384 return nullptr; 310 return nullptr;
385 } 311 }
386 return scope.adjustedElement(*element); 312 return scope.adjustedElement(*element);
387 } 313 }
388 314
315 Element* Fullscreen::currentFullScreenElementFrom(Document& document) {
316 if (Fullscreen* found = fromIfExists(document))
317 return found->currentFullScreenElement();
318 return nullptr;
319 }
320
321 Element* Fullscreen::currentFullScreenElementForBindingFrom(
322 Document& document) {
323 Element* element = currentFullScreenElementFrom(document);
324 if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled())
325 return element;
326
327 // For Shadow DOM V0 compatibility: We allow returning an element in V0 shadow
328 // tree, even though it leaks the Shadow DOM.
329 if (element->isInV0ShadowTree()) {
330 UseCounter::count(document,
331 UseCounter::DocumentFullscreenElementInV0Shadow);
332 return element;
333 }
334 return document.adjustedElement(*element);
335 }
336
389 Fullscreen::Fullscreen(Document& document) 337 Fullscreen::Fullscreen(Document& document)
390 : Supplement<Document>(document), 338 : Supplement<Document>(document),
391 ContextLifecycleObserver(&document), 339 ContextLifecycleObserver(&document),
392 m_fullScreenLayoutObject(nullptr) { 340 m_fullScreenLayoutObject(nullptr),
341 m_eventQueueTimer(TaskRunnerHelper::get(TaskType::Unthrottled, &document),
342 this,
343 &Fullscreen::eventQueueTimerFired),
344 m_forCrossProcessDescendant(false) {
393 document.setHasFullscreenSupplement(); 345 document.setHasFullscreenSupplement();
394 } 346 }
395 347
396 Fullscreen::~Fullscreen() {} 348 Fullscreen::~Fullscreen() {}
397 349
398 Document* Fullscreen::document() { 350 inline Document* Fullscreen::document() {
399 return toDocument(lifecycleContext()); 351 return toDocument(lifecycleContext());
400 } 352 }
401 353
402 void Fullscreen::contextDestroyed(ExecutionContext*) { 354 void Fullscreen::contextDestroyed(ExecutionContext*) {
355 m_eventQueue.clear();
356
403 if (m_fullScreenLayoutObject) 357 if (m_fullScreenLayoutObject)
404 m_fullScreenLayoutObject->destroy(); 358 m_fullScreenLayoutObject->destroy();
405 359
406 m_pendingRequests.clear(); 360 m_currentFullScreenElement = nullptr;
407 m_fullscreenElementStack.clear(); 361 m_fullscreenElementStack.clear();
408 } 362 }
409 363
410 // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen 364 // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen
411 void Fullscreen::requestFullscreen(Element& pending) { 365 void Fullscreen::requestFullscreen(Element& element) {
412 // TODO(foolip): Make RequestType::Unprefixed the default when the unprefixed 366 // TODO(foolip): Make RequestType::Unprefixed the default when the unprefixed
413 // API is enabled. https://crbug.com/383813 367 // API is enabled. https://crbug.com/383813
414 requestFullscreen(pending, RequestType::Prefixed); 368 requestFullscreen(element, RequestType::Prefixed, false);
415 } 369 }
416 370
417 void Fullscreen::requestFullscreen(Element& pending, RequestType requestType) { 371 void Fullscreen::requestFullscreen(Element& element,
418 Document& document = pending.document(); 372 RequestType requestType,
419 373 bool forCrossProcessDescendant) {
420 // Ignore this call if the document is not in a live frame. 374 Document& document = element.document();
421 if (!document.isActive() || !document.frame())
422 return;
423
424 bool forCrossProcessDescendant =
425 requestType == RequestType::PrefixedForCrossProcessDescendant;
426 375
427 // Use counters only need to be incremented in the process of the actual 376 // Use counters only need to be incremented in the process of the actual
428 // fullscreen element. 377 // fullscreen element.
429 if (!forCrossProcessDescendant) { 378 if (!forCrossProcessDescendant) {
430 if (document.isSecureContext()) { 379 if (document.isSecureContext()) {
431 UseCounter::count(document, UseCounter::FullscreenSecureOrigin); 380 UseCounter::count(document, UseCounter::FullscreenSecureOrigin);
432 } else { 381 } else {
433 UseCounter::count(document, UseCounter::FullscreenInsecureOrigin); 382 UseCounter::count(document, UseCounter::FullscreenInsecureOrigin);
434 HostsUsingFeatures::countAnyWorld( 383 HostsUsingFeatures::countAnyWorld(
435 document, HostsUsingFeatures::Feature::FullscreenInsecureHost); 384 document, HostsUsingFeatures::Feature::FullscreenInsecureHost);
436 } 385 }
437 } 386 }
438 387
439 // 1. Let |pending| be the context object. 388 // Ignore this call if the document is not in a live frame.
440 389 if (!document.isActive() || !document.frame())
441 // 2. Let |error| be false. 390 return;
442 bool error = false; 391
443 392 // If |element| is on top of |doc|'s fullscreen element stack, terminate these
444 // 3. Let |promise| be a new promise. 393 // substeps.
445 // TODO(foolip): Promises. https://crbug.com/644637 394 if (&element == fullscreenElementFrom(document))
446 395 return;
447 // 4. If any of the following conditions are false, then set |error| to true: 396
448 // 397 do {
449 // OOPIF: If |requestFullscreen()| was already called in a descendant frame 398 // 1. If any of the following conditions are false, then terminate these
450 // and passed the checks, do not check again here. 399 // steps and queue a task to fire an event named fullscreenerror with its
451 if (!forCrossProcessDescendant && 400 // bubbles attribute set to true on the context object's node document:
452 !requestFullscreenConditionsMet(pending, document)) 401
453 error = true; 402 // |element|'s namespace is the HTML namespace or |element| is an SVG
454 403 // svg or MathML math element.
455 // 5. Return |promise|, and run the remaining steps in parallel. 404 // Note: MathML is not supported.
456 // TODO(foolip): Promises. https://crbug.com/644637 405 if (!element.isHTMLElement() && !isSVGSVGElement(element))
457 406 break;
458 // 6. If |error| is false: Resize pending's top-level browsing context's 407
459 // document's viewport's dimensions to match the dimensions of the screen of 408 // The fullscreen element ready check for |element| returns true.
460 // the output device. Optionally display a message how the end user can 409 if (!fullscreenElementReady(element))
461 // revert this. 410 break;
462 if (!error) { 411
463 from(document).m_pendingRequests.push_back( 412 // Fullscreen is supported.
464 std::make_pair(&pending, requestType)); 413 if (!fullscreenIsSupported(document))
465 LocalFrame& frame = *document.frame(); 414 break;
466 frame.chromeClient().enterFullscreen(frame); 415
467 } else { 416 // This algorithm is allowed to request fullscreen.
468 enqueueTaskForRequest(document, pending, requestType, true); 417 // OOPIF: If |forCrossProcessDescendant| is true, requestFullscreen was
469 } 418 // already called on a descendant element in another process, and
470 } 419 // getting here means that it was already allowed to request fullscreen.
471 420 if (!forCrossProcessDescendant && !allowedToRequestFullscreen(document))
472 void Fullscreen::didEnterFullscreen() { 421 break;
473 if (!document()) 422
474 return; 423 // 2. Let doc be element's node document. (i.e. "this")
475 424
476 ElementStack requests; 425 // 3. Let docs be all doc's ancestor browsing context's documents (if any)
477 requests.swap(m_pendingRequests); 426 // and doc.
478 for (const ElementStackEntry& request : requests) 427 //
479 enqueueTaskForRequest(*document(), *request.first, request.second, false); 428 // For OOPIF scenarios, |docs| will only contain documents for local
480 } 429 // ancestors, and remote ancestors will be processed in their
481 430 // respective processes. This preserves the spec's event firing order
482 void Fullscreen::enqueueTaskForRequest(Document& document, 431 // for local ancestors, but not for remote ancestors. However, that
483 Element& pending, 432 // difference shouldn't be observable in practice: a fullscreenchange
484 RequestType requestType, 433 // event handler would need to postMessage a frame in another renderer
485 bool error) { 434 // process, where the message should be queued up and processed after
486 // 7. As part of the next animation frame task, run these substeps: 435 // the IPC that dispatches fullscreenchange.
487 document.enqueueAnimationFrameTask( 436 HeapDeque<Member<Document>> docs;
488 WTF::bind(&runTaskForRequest, wrapPersistent(&document), 437 for (Document* doc = &document; doc; doc = nextLocalAncestor(*doc))
489 wrapPersistent(&pending), requestType, error)); 438 docs.prepend(doc);
490 } 439
491 440 // 4. For each document in docs, run these substeps:
492 void Fullscreen::runTaskForRequest(Document* document, 441 HeapDeque<Member<Document>>::iterator current = docs.begin(),
493 Element* element, 442 following = docs.begin();
494 RequestType requestType, 443
495 bool error) { 444 do {
496 DCHECK(document); 445 ++following;
497 DCHECK(document->isActive()); 446
498 DCHECK(document->frame()); 447 // 1. Let following document be the document after document in docs, or
499 DCHECK(element); 448 // null if there is no such document.
500 449 Document* currentDoc = *current;
501 Document& pendingDoc = *document; 450 Document* followingDoc = following != docs.end() ? *following : nullptr;
502 Element& pending = *element; 451
503 452 // 2. If following document is null, push context object on document's
504 // TODO(foolip): Spec something like: If |pending|'s node document is not 453 // fullscreen element stack, and queue a task to fire an event named
505 // |pendingDoc|, then set |error| to true. 454 // fullscreenchange with its bubbles attribute set to true on the
506 // https://github.com/whatwg/fullscreen/issues/33 455 // document.
507 if (pending.document() != pendingDoc) 456 if (!followingDoc) {
508 error = true; 457 from(*currentDoc).pushFullscreenElementStack(element, requestType);
509 458 from(document).enqueueChangeEvent(*currentDoc, requestType);
510 // 7.1. If either |error| is true or the fullscreen element ready check for 459 continue;
511 // |pending| returns false, fire an event named fullscreenerror on 460 }
512 // |pending|'s node document, reject |promise| with a TypeError exception, 461
513 // and terminate these steps. 462 // 3. Otherwise, if document's fullscreen element stack is either empty or
514 // TODO(foolip): Promises. https://crbug.com/644637 463 // its top element is not following document's browsing context container,
515 if (error || !fullscreenElementReady(pending)) { 464 Element* topElement = fullscreenElementFrom(*currentDoc);
516 Event* event = createErrorEvent(pendingDoc, pending, requestType); 465 HTMLFrameOwnerElement* followingOwner =
517 event->target()->dispatchEvent(event); 466 findContainerForDescendant(*currentDoc, *followingDoc);
518 return; 467 if (!topElement || topElement != followingOwner) {
519 } 468 // ...push following document's browsing context container on document's
520 469 // fullscreen element stack, and queue a task to fire an event named
521 // 7.2. Let |fullscreenElements| be an ordered set initially consisting of 470 // fullscreenchange with its bubbles attribute set to true on document.
522 // |pending|. 471 from(*currentDoc)
523 HeapDeque<Member<Element>> fullscreenElements; 472 .pushFullscreenElementStack(*followingOwner, requestType);
524 fullscreenElements.append(pending); 473 from(document).enqueueChangeEvent(*currentDoc, requestType);
525 474 continue;
526 // 7.3. While the first element in |fullscreenElements| is in a nested 475 }
527 // browsing context, prepend its browsing context container to 476
528 // |fullscreenElements|. 477 // 4. Otherwise, do nothing for this document. It stays the same.
529 // 478 } while (++current != docs.end());
530 // OOPIF: |fullscreenElements| will only contain elements for local ancestors, 479
531 // and remote ancestors will be processed in their respective processes. This 480 from(document).m_forCrossProcessDescendant = forCrossProcessDescendant;
532 // preserves the spec's event firing order for local ancestors, but not for 481
533 // remote ancestors. However, that difference shouldn't be observable in 482 // 5. Return, and run the remaining steps asynchronously.
534 // practice: a fullscreenchange event handler would need to postMessage a 483 // 6. Optionally, perform some animation.
535 // frame in another renderer process, where the message should be queued up 484 from(document).m_pendingFullscreenElement = &element;
536 // and processed after the IPC that dispatches fullscreenchange. 485 document.frame()->chromeClient().enterFullscreen(*document.frame());
537 for (Frame* frame = pending.document().frame(); frame; 486
538 frame = frame->tree().parent()) { 487 // 7. Optionally, display a message indicating how the user can exit
539 if (!frame->owner() || !frame->owner()->isLocal()) 488 // displaying the context object fullscreen.
540 continue; 489 return;
541 Element* element = toHTMLFrameOwnerElement(frame->owner()); 490 } while (false);
542 fullscreenElements.prepend(element); 491
543 } 492 from(document).enqueueErrorEvent(element, requestType);
544
545 // 7.4. Let |eventDocs| be an empty list.
546 // Note: For prefixed requests, the event target is an element, so instead
547 // let |events| be a list of events to dispatch.
548 HeapVector<Member<Event>> events;
549
550 // 7.5. For each |element| in |fullscreenElements|, in order, run these
551 // subsubsteps:
552 for (Element* element : fullscreenElements) {
553 // 7.5.1. Let |doc| be |element|'s node document.
554 Document& doc = element->document();
555
556 // 7.5.2. If |element| is |doc|'s fullscreen element, terminate these
557 // subsubsteps.
558 if (element == fullscreenElementFrom(doc))
559 continue;
560
561 // 7.5.3. Otherwise, append |doc| to |eventDocs|.
562 events.push_back(createChangeEvent(doc, *element, requestType));
563
564 // 7.5.4. If |element| is |pending| and |pending| is an iframe element,
565 // set |element|'s iframe fullscreen flag.
566 // TODO(foolip): Support the iframe fullscreen flag.
567 // https://crbug.com/644695
568
569 // 7.5.5. Fullscreen |element| within |doc|.
570 // TODO(foolip): Merge fullscreen element stack into top layer.
571 // https://crbug.com/627790
572 from(doc).pushFullscreenElementStack(*element, requestType);
573 }
574
575 // 7.6. For each |doc| in |eventDocs|, in order, fire an event named
576 // fullscreenchange on |doc|.
577 dispatchEvents(events);
578
579 // 7.7. Fulfill |promise| with undefined.
580 // TODO(foolip): Promises. https://crbug.com/644637
581 } 493 }
582 494
583 // https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen 495 // https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen
584 void Fullscreen::fullyExitFullscreen(Document& document) { 496 void Fullscreen::fullyExitFullscreen(Document& document) {
585 // 1. If |document|'s fullscreen element is null, terminate these steps. 497 // To fully exit fullscreen, run these steps:
586 498
587 // 2. Unfullscreen elements whose fullscreen flag is set, within 499 // 1. Let |doc| be the top-level browsing context's document.
588 // |document|'s top layer, except for |document|'s fullscreen element. 500 //
589 501 // Since the top-level browsing context's document might be unavailable in
590 // 3. Exit fullscreen |document|. 502 // OOPIF scenarios (i.e., when the top frame is remote), this actually uses
591 503 // the Document of the topmost local ancestor frame. Without OOPIF, this
592 // TODO(foolip): Change the spec. To remove elements from |document|'s top 504 // will be the top frame's document. With OOPIF, each renderer process for
593 // layer as in step 2 could leave descendant frames in fullscreen. It may work 505 // the current page will separately call fullyExitFullscreen to cover all
594 // to give the "exit fullscreen" algorithm a |fully| flag that's used in the 506 // local frames in each process.
595 // animation frame task after exit. Here, retain the old behavior of fully 507 Document& doc = topmostLocalAncestor(document);
596 // exiting fullscreen for the topmost local ancestor: 508
597 exitFullscreen(topmostLocalAncestor(document), ExitType::Fully); 509 // 2. If |doc|'s fullscreen element stack is empty, terminate these steps.
510 if (!fullscreenElementFrom(doc))
511 return;
512
513 // 3. Remove elements from |doc|'s fullscreen element stack until only the top
514 // element is left.
515 size_t stackSize = from(doc).m_fullscreenElementStack.size();
516 from(doc).m_fullscreenElementStack.remove(0, stackSize - 1);
517 DCHECK_EQ(from(doc).m_fullscreenElementStack.size(), 1u);
518
519 // 4. Act as if the exitFullscreen() method was invoked on |doc|.
520 exitFullscreen(doc);
598 } 521 }
599 522
600 // https://fullscreen.spec.whatwg.org/#exit-fullscreen 523 // https://fullscreen.spec.whatwg.org/#exit-fullscreen
601 void Fullscreen::exitFullscreen(Document& doc, ExitType exitType) { 524 void Fullscreen::exitFullscreen(Document& document) {
602 if (!doc.isActive() || !doc.frame()) 525 // The exitFullscreen() method must run these steps:
603 return; 526
604 527 // Ignore this call if the document is not in a live frame.
605 // 1. Let |promise| be a new promise. 528 if (!document.isActive() || !document.frame())
606 // 2. If |doc|'s fullscreen element is null, reject |promise| with a 529 return;
607 // TypeError exception, and return |promise|. 530
608 // TODO(foolip): Promises. https://crbug.com/644637 531 // 1. Let doc be the context object. (i.e. "this")
609 if (!fullscreenElementFrom(doc)) 532 // 2. If doc's fullscreen element stack is empty, terminate these steps.
610 return; 533 if (!fullscreenElementFrom(document))
611 534 return;
612 // 3. Let |resize| be false. 535
613 bool resize = false; 536 // 3. Let descendants be all the doc's descendant browsing context's documents
614 537 // with a non-empty fullscreen element stack (if any), ordered so that the
615 // 4. Let |docs| be the result of collecting documents to unfullscreen given 538 // child of the doc is last and the document furthest away from the doc is
616 // |doc|. 539 // first.
617 HeapVector<Member<Document>> docs = 540 HeapDeque<Member<Document>> descendants;
618 collectDocumentsToUnfullscreen(doc, exitType); 541 for (Frame* descendant = document.frame()->tree().traverseNext(); descendant;
619 542 descendant = descendant->tree().traverseNext()) {
620 // 5. Let |topLevelDoc| be |doc|'s top-level browsing context's document.
621 //
622 // OOPIF: Let |topLevelDoc| be the topmost local ancestor instead. If the main
623 // frame is in another process, we will still fully exit fullscreen even
624 // though that's wrong if the main frame was in nested fullscreen.
625 // TODO(alexmos): Deal with nested fullscreen cases, see
626 // https://crbug.com/617369.
627 Document& topLevelDoc = topmostLocalAncestor(doc);
628
629 // 6. If |topLevelDoc| is in |docs|, set |resize| to true.
630 if (!docs.isEmpty() && docs.back() == &topLevelDoc)
631 resize = true;
632
633 // 7. Return |promise|, and run the remaining steps in parallel.
634 // TODO(foolip): Promises. https://crbug.com/644637
635
636 // Note: |ExitType::Fully| is only used together with the topmost local
637 // ancestor in |fullyExitFullscreen()|, and so implies that |resize| is true.
638 // This would change if matching the spec for "fully exit fullscreen".
639 if (exitType == ExitType::Fully)
640 DCHECK(resize);
641
642 // 8. If |resize| is true, resize |topLevelDoc|'s viewport to its "normal"
643 // dimensions.
644 if (resize) {
645 LocalFrame& frame = *doc.frame();
646 frame.chromeClient().exitFullscreen(frame);
647 } else {
648 enqueueTaskForExit(doc, exitType);
649 }
650 }
651
652 void Fullscreen::didExitFullscreen() {
653 if (!document())
654 return;
655
656 DCHECK_EQ(document(), &topmostLocalAncestor(*document()));
657
658 enqueueTaskForExit(*document(), ExitType::Fully);
659 }
660
661 void Fullscreen::enqueueTaskForExit(Document& document, ExitType exitType) {
662 // 9. As part of the next animation frame task, run these substeps:
663 document.enqueueAnimationFrameTask(
664 WTF::bind(&runTaskForExit, wrapPersistent(&document), exitType));
665 }
666
667 void Fullscreen::runTaskForExit(Document* document, ExitType exitType) {
668 DCHECK(document);
669 DCHECK(document->isActive());
670 DCHECK(document->frame());
671
672 Document& doc = *document;
673
674 if (!fullscreenElementFrom(doc))
675 return;
676
677 // 9.1. Let |exitDocs| be the result of collecting documents to unfullscreen
678 // given |doc|.
679
680 // 9.2. If |resize| is true and |topLevelDoc| is not in |exitDocs|, fully
681 // exit fullscreen |topLevelDoc|, reject promise with a TypeError exception,
682 // and terminate these steps.
683
684 // TODO(foolip): See TODO in |fullyExitFullscreen()|. Instead of using "fully
685 // exit fullscreen" in step 9.2 (which is async), give "exit fullscreen" a
686 // |fully| flag which is always true if |resize| was true.
687
688 HeapVector<Member<Document>> exitDocs =
689 collectDocumentsToUnfullscreen(doc, exitType);
690
691 // 9.3. If |exitDocs| is the empty set, append |doc| to |exitDocs|.
692 if (exitDocs.isEmpty())
693 exitDocs.push_back(&doc);
694
695 // 9.4. If |exitDocs|'s last document has a browsing context container,
696 // append that browsing context container's node document to |exitDocs|.
697 //
698 // OOPIF: Skip over remote frames, assuming that they have exactly one element
699 // in their fullscreen element stacks, thereby erring on the side of exiting
700 // fullscreen. TODO(alexmos): Deal with nested fullscreen cases, see
701 // https://crbug.com/617369.
702 if (Document* document = nextLocalAncestor(*exitDocs.back()))
703 exitDocs.push_back(document);
704
705 // 9.5. Let |descendantDocs| be an ordered set consisting of |doc|'s
706 // descendant browsing contexts' documents whose fullscreen element is
707 // non-null, if any, in *reverse* tree order.
708 HeapDeque<Member<Document>> descendantDocs;
709 for (Frame* descendant = doc.frame()->tree().firstChild(); descendant;
710 descendant = descendant->tree().traverseNext(doc.frame())) {
711 if (!descendant->isLocalFrame()) 543 if (!descendant->isLocalFrame())
712 continue; 544 continue;
713 DCHECK(toLocalFrame(descendant)->document()); 545 DCHECK(toLocalFrame(descendant)->document());
714 if (fullscreenElementFrom(*toLocalFrame(descendant)->document())) 546 if (fullscreenElementFrom(*toLocalFrame(descendant)->document()))
715 descendantDocs.prepend(toLocalFrame(descendant)->document()); 547 descendants.prepend(toLocalFrame(descendant)->document());
716 } 548 }
717 549
718 // Note: For prefixed requests, the event target is an element, so let 550 // 4. For each descendant in descendants, empty descendant's fullscreen
719 // |events| be a list of events to dispatch. 551 // element stack, and queue a task to fire an event named fullscreenchange
720 HeapVector<Member<Event>> events; 552 // with its bubbles attribute set to true on descendant.
721 553 for (auto& descendant : descendants) {
722 // 9.6. For each |descendantDoc| in |descendantDocs|, in order, unfullscreen 554 DCHECK(descendant);
723 // |descendantDoc|. 555 RequestType requestType =
724 for (Document* descendantDoc : descendantDocs) { 556 from(*descendant).m_fullscreenElementStack.back().second;
725 Fullscreen& fullscreen = from(*descendantDoc); 557 from(*descendant).clearFullscreenElementStack();
726 ElementStack& stack = fullscreen.m_fullscreenElementStack; 558 from(document).enqueueChangeEvent(*descendant, requestType);
727 DCHECK(!stack.isEmpty()); 559 }
728 events.push_back(createChangeEvent(*descendantDoc, *stack.back().first, 560
729 stack.back().second)); 561 // 5. While doc is not null, run these substeps:
730 while (!stack.isEmpty()) 562 Element* newTop = nullptr;
731 fullscreen.popFullscreenElementStack(); 563 for (Document* currentDoc = &document; currentDoc;) {
732 } 564 RequestType requestType =
733 565 from(*currentDoc).m_fullscreenElementStack.back().second;
734 // 9.7. For each |exitDoc| in |exitDocs|, in order, unfullscreen |exitDoc|'s 566
735 // fullscreen element. 567 // 1. Pop the top element of doc's fullscreen element stack.
736 for (Document* exitDoc : exitDocs) { 568 from(*currentDoc).popFullscreenElementStack();
737 Fullscreen& fullscreen = from(*exitDoc); 569
738 ElementStack& stack = fullscreen.m_fullscreenElementStack; 570 // If doc's fullscreen element stack is non-empty and the element now at
739 DCHECK(!stack.isEmpty()); 571 // the top is either not in a document or its node document is not doc,
740 events.push_back( 572 // repeat this substep.
741 createChangeEvent(*exitDoc, *stack.back().first, stack.back().second)); 573 newTop = fullscreenElementFrom(*currentDoc);
742 fullscreen.popFullscreenElementStack(); 574 if (newTop && (!newTop->isConnected() || newTop->document() != currentDoc))
743 575 continue;
744 // TODO(foolip): See TODO in |fullyExitFullscreen()|. 576
745 if (exitDoc == &doc && exitType == ExitType::Fully) { 577 // 2. Queue a task to fire an event named fullscreenchange with its bubbles
746 while (!stack.isEmpty()) 578 // attribute set to true on doc.
747 fullscreen.popFullscreenElementStack(); 579 from(document).enqueueChangeEvent(*currentDoc, requestType);
580
581 // 3. If doc's fullscreen element stack is empty and doc's browsing context
582 // has a browsing context container, set doc to that browsing context
583 // container's node document.
584 //
585 // OOPIF: If browsing context container's document is in another
586 // process, keep moving up the ancestor chain and looking for a
587 // browsing context container with a local document.
588 // TODO(alexmos): Deal with nested fullscreen cases, see
589 // https://crbug.com/617369.
590 if (!newTop) {
591 currentDoc = nextLocalAncestor(*currentDoc);
592 continue;
748 } 593 }
749 } 594
750 595 // 4. Otherwise, set doc to null.
751 // 9.8. For each |descendantDoc| in |descendantDocs|, in order, fire an 596 currentDoc = nullptr;
752 // event named fullscreenchange on |descendantDoc|. 597 }
753 // 9.9. For each |exitDoc| in |exitDocs|, in order, fire an event named 598
754 // fullscreenchange on |exitDoc|. 599 // 6. Return, and run the remaining steps asynchronously.
755 dispatchEvents(events); 600 // 7. Optionally, perform some animation.
756 601
757 // 9.10. Fulfill |promise| with undefined. 602 // Only exit fullscreen mode if the fullscreen element stack is empty.
758 // TODO(foolip): Promises. https://crbug.com/644637 603 if (!newTop) {
604 document.frame()->chromeClient().exitFullscreen(*document.frame());
605 return;
606 }
607
608 // Otherwise, enter fullscreen for the fullscreen element stack's top element.
609 from(document).m_pendingFullscreenElement = newTop;
610 from(document).didEnterFullscreen();
759 } 611 }
760 612
761 // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled 613 // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled
762 bool Fullscreen::fullscreenEnabled(Document& document) { 614 bool Fullscreen::fullscreenEnabled(Document& document) {
763 // The fullscreenEnabled attribute's getter must return true if the context 615 // The fullscreenEnabled attribute's getter must return true if the context
764 // object is allowed to use the feature indicated by attribute name 616 // object is allowed to use the feature indicated by attribute name
765 // allowfullscreen and fullscreen is supported, and false otherwise. 617 // allowfullscreen and fullscreen is supported, and false otherwise.
766 return allowedToUseFullscreen(document.frame()) && 618 return allowedToUseFullscreen(document.frame()) &&
767 fullscreenIsSupported(document); 619 fullscreenIsSupported(document);
768 } 620 }
769 621
622 void Fullscreen::didEnterFullscreen() {
623 if (!document()->isActive() || !document()->frame())
624 return;
625
626 // Start the timer for events enqueued by |requestFullscreen()|. The hover
627 // state update is scheduled first so that it's done when the events fire.
628 document()->frame()->eventHandler().scheduleHoverStateUpdate();
629 m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE);
630
631 Element* element = m_pendingFullscreenElement.release();
632 if (!element)
633 return;
634
635 if (m_currentFullScreenElement == element)
636 return;
637
638 if (!element->isConnected() || &element->document() != document()) {
639 // The element was removed or has moved to another document since the
640 // |requestFullscreen()| call. Exit fullscreen again to recover.
641 // TODO(foolip): Fire a fullscreenerror event. This is currently difficult
642 // because the fullscreenchange event has already been enqueued and possibly
643 // even fired. https://crbug.com/402376
644 LocalFrame& frame = *document()->frame();
645 frame.chromeClient().exitFullscreen(frame);
646 return;
647 }
648
649 if (m_fullScreenLayoutObject)
650 m_fullScreenLayoutObject->unwrapLayoutObject();
651
652 Element* previousElement = m_currentFullScreenElement;
653 m_currentFullScreenElement = element;
654
655 // Create a placeholder block for a the full-screen element, to keep the page
656 // from reflowing when the element is removed from the normal flow. Only do
657 // this for a LayoutBox, as only a box will have a frameRect. The placeholder
658 // will be created in setFullScreenLayoutObject() during layout.
659 LayoutObject* layoutObject = m_currentFullScreenElement->layoutObject();
660 bool shouldCreatePlaceholder = layoutObject && layoutObject->isBox();
661 if (shouldCreatePlaceholder) {
662 m_savedPlaceholderFrameRect = toLayoutBox(layoutObject)->frameRect();
663 m_savedPlaceholderComputedStyle =
664 ComputedStyle::clone(layoutObject->styleRef());
665 }
666
667 // TODO(alexmos): When |m_forCrossProcessDescendant| is true, some of
668 // this layout work has already been done in another process, so it should
669 // not be necessary to repeat it here.
670 if (m_currentFullScreenElement != document()->documentElement()) {
671 LayoutFullScreen::wrapLayoutObject(
672 layoutObject, layoutObject ? layoutObject->parent() : 0, document());
673 }
674
675 // When |m_forCrossProcessDescendant| is true, m_currentFullScreenElement
676 // corresponds to the HTMLFrameOwnerElement for the out-of-process iframe
677 // that contains the actual fullscreen element. Hence, it must also set
678 // the ContainsFullScreenElement flag (so that it gains the
679 // -webkit-full-screen-ancestor style).
680 if (m_forCrossProcessDescendant) {
681 DCHECK(m_currentFullScreenElement->isFrameOwnerElement());
682 DCHECK(toHTMLFrameOwnerElement(m_currentFullScreenElement)
683 ->contentFrame()
684 ->isRemoteFrame());
685 m_currentFullScreenElement->setContainsFullScreenElement(true);
686 }
687
688 m_currentFullScreenElement
689 ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
690
691 document()->styleEngine().ensureUAStyleForFullscreen();
692 m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen);
693
694 // FIXME: This should not call updateStyleAndLayoutTree.
695 document()->updateStyleAndLayoutTree();
696
697 document()->frame()->chromeClient().fullscreenElementChanged(previousElement,
698 element);
699 }
700
701 void Fullscreen::didExitFullscreen() {
702 if (!document()->isActive() || !document()->frame())
703 return;
704
705 // Start the timer for events enqueued by |exitFullscreen()|. The hover state
706 // update is scheduled first so that it's done when the events fire.
707 document()->frame()->eventHandler().scheduleHoverStateUpdate();
708 m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE);
709
710 // If fullscreen was canceled by the browser, e.g. if the user pressed Esc,
711 // then |exitFullscreen()| was never called. Let |fullyExitFullscreen()| clear
712 // the fullscreen element stack and fire any events as necessary.
713 // TODO(foolip): Remove this when state changes and events are synchronized
714 // with animation frames. https://crbug.com/402376
715 fullyExitFullscreen(*document());
716
717 if (!m_currentFullScreenElement)
718 return;
719
720 if (m_forCrossProcessDescendant)
721 m_currentFullScreenElement->setContainsFullScreenElement(false);
722
723 m_currentFullScreenElement
724 ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
725
726 if (m_fullScreenLayoutObject)
727 LayoutFullScreenItem(m_fullScreenLayoutObject).unwrapLayoutObject();
728
729 document()->styleEngine().ensureUAStyleForFullscreen();
730 m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen);
731 Element* previousElement = m_currentFullScreenElement;
732 m_currentFullScreenElement = nullptr;
733
734 m_forCrossProcessDescendant = false;
735
736 document()->frame()->chromeClient().fullscreenElementChanged(previousElement,
737 nullptr);
738 }
739
770 void Fullscreen::setFullScreenLayoutObject(LayoutFullScreen* layoutObject) { 740 void Fullscreen::setFullScreenLayoutObject(LayoutFullScreen* layoutObject) {
771 if (layoutObject == m_fullScreenLayoutObject) 741 if (layoutObject == m_fullScreenLayoutObject)
772 return; 742 return;
773 743
774 if (layoutObject && m_savedPlaceholderComputedStyle) { 744 if (layoutObject && m_savedPlaceholderComputedStyle) {
775 layoutObject->createPlaceholder(std::move(m_savedPlaceholderComputedStyle), 745 layoutObject->createPlaceholder(std::move(m_savedPlaceholderComputedStyle),
776 m_savedPlaceholderFrameRect); 746 m_savedPlaceholderFrameRect);
777 } else if (layoutObject && m_fullScreenLayoutObject && 747 } else if (layoutObject && m_fullScreenLayoutObject &&
778 m_fullScreenLayoutObject->placeholder()) { 748 m_fullScreenLayoutObject->placeholder()) {
779 LayoutBlockFlow* placeholder = m_fullScreenLayoutObject->placeholder(); 749 LayoutBlockFlow* placeholder = m_fullScreenLayoutObject->placeholder();
780 layoutObject->createPlaceholder( 750 layoutObject->createPlaceholder(
781 ComputedStyle::clone(placeholder->styleRef()), 751 ComputedStyle::clone(placeholder->styleRef()),
782 placeholder->frameRect()); 752 placeholder->frameRect());
783 } 753 }
784 754
785 if (m_fullScreenLayoutObject) 755 if (m_fullScreenLayoutObject)
786 m_fullScreenLayoutObject->unwrapLayoutObject(); 756 m_fullScreenLayoutObject->unwrapLayoutObject();
787 DCHECK(!m_fullScreenLayoutObject); 757 DCHECK(!m_fullScreenLayoutObject);
788 758
789 m_fullScreenLayoutObject = layoutObject; 759 m_fullScreenLayoutObject = layoutObject;
790 } 760 }
791 761
792 void Fullscreen::fullScreenLayoutObjectDestroyed() { 762 void Fullscreen::fullScreenLayoutObjectDestroyed() {
793 m_fullScreenLayoutObject = nullptr; 763 m_fullScreenLayoutObject = nullptr;
794 } 764 }
795 765
766 void Fullscreen::enqueueChangeEvent(Document& document,
767 RequestType requestType) {
768 Event* event;
769 if (requestType == RequestType::Unprefixed) {
770 event = createEvent(EventTypeNames::fullscreenchange, document);
771 } else {
772 DCHECK(document.hasFullscreenSupplement());
773 Fullscreen& fullscreen = from(document);
774 EventTarget* target = fullscreen.fullscreenElement();
775 if (!target)
776 target = fullscreen.currentFullScreenElement();
777 if (!target)
778 target = &document;
779 event = createEvent(EventTypeNames::webkitfullscreenchange, *target);
780 }
781 m_eventQueue.append(event);
782 // NOTE: The timer is started in didEnterFullscreen/didExitFullscreen.
783 }
784
785 void Fullscreen::enqueueErrorEvent(Element& element, RequestType requestType) {
786 Event* event;
787 if (requestType == RequestType::Unprefixed)
788 event = createEvent(EventTypeNames::fullscreenerror, element.document());
789 else
790 event = createEvent(EventTypeNames::webkitfullscreenerror, element);
791 m_eventQueue.append(event);
792 m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE);
793 }
794
795 void Fullscreen::eventQueueTimerFired(TimerBase*) {
796 HeapDeque<Member<Event>> eventQueue;
797 m_eventQueue.swap(eventQueue);
798
799 while (!eventQueue.isEmpty()) {
800 Event* event = eventQueue.takeFirst();
801 Node* target = event->target()->toNode();
802
803 // If the element was removed from our tree, also message the
804 // documentElement.
805 if (!target->isConnected() && document()->documentElement()) {
806 DCHECK(isPrefixed(event->type()));
807 eventQueue.append(
808 createEvent(event->type(), *document()->documentElement()));
809 }
810
811 target->dispatchEvent(event);
812 }
813 }
814
796 void Fullscreen::elementRemoved(Element& oldNode) { 815 void Fullscreen::elementRemoved(Element& oldNode) {
797 DCHECK_EQ(document(), &oldNode.document());
798
799 // Whenever the removing steps run with an |oldNode| and |oldNode| is in its 816 // Whenever the removing steps run with an |oldNode| and |oldNode| is in its
800 // node document's fullscreen element stack, run these steps: 817 // node document's fullscreen element stack, run these steps:
801 818
802 // 1. If |oldNode| is at the top of its node document's fullscreen element 819 // 1. If |oldNode| is at the top of its node document's fullscreen element
803 // stack, act as if the exitFullscreen() method was invoked on that document. 820 // stack, act as if the exitFullscreen() method was invoked on that document.
804 if (fullscreenElement() == &oldNode) { 821 if (fullscreenElement() == &oldNode) {
805 exitFullscreen(oldNode.document()); 822 exitFullscreen(oldNode.document());
806 return; 823 return;
807 } 824 }
808 825
809 // 2. Otherwise, remove |oldNode| from its node document's fullscreen element 826 // 2. Otherwise, remove |oldNode| from its node document's fullscreen element
810 // stack. 827 // stack.
811 for (size_t i = 0; i < m_fullscreenElementStack.size(); ++i) { 828 for (size_t i = 0; i < m_fullscreenElementStack.size(); ++i) {
812 if (m_fullscreenElementStack[i].first.get() == &oldNode) { 829 if (m_fullscreenElementStack[i].first.get() == &oldNode) {
813 m_fullscreenElementStack.remove(i); 830 m_fullscreenElementStack.remove(i);
814 return; 831 return;
815 } 832 }
816 } 833 }
817 834
818 // NOTE: |oldNode| was not in the fullscreen element stack. 835 // NOTE: |oldNode| was not in the fullscreen element stack.
819 } 836 }
820 837
838 void Fullscreen::clearFullscreenElementStack() {
839 if (m_fullscreenElementStack.isEmpty())
840 return;
841
842 m_fullscreenElementStack.clear();
843
844 setNeedsPaintPropertyUpdate(document());
845 }
846
821 void Fullscreen::popFullscreenElementStack() { 847 void Fullscreen::popFullscreenElementStack() {
822 DCHECK(!m_fullscreenElementStack.isEmpty()); 848 if (m_fullscreenElementStack.isEmpty())
849 return;
823 850
824 Element* previousElement = fullscreenElement();
825 m_fullscreenElementStack.pop_back(); 851 m_fullscreenElementStack.pop_back();
826 852
827 // Note: |requestType| is only used if |fullscreenElement()| is non-null. 853 setNeedsPaintPropertyUpdate(document());
828 RequestType requestType = m_fullscreenElementStack.isEmpty()
829 ? RequestType::Unprefixed
830 : m_fullscreenElementStack.back().second;
831 fullscreenElementChanged(previousElement, fullscreenElement(), requestType);
832 } 854 }
833 855
834 void Fullscreen::pushFullscreenElementStack(Element& element, 856 void Fullscreen::pushFullscreenElementStack(Element& element,
835 RequestType requestType) { 857 RequestType requestType) {
836 Element* previousElement = fullscreenElement();
837 m_fullscreenElementStack.push_back(std::make_pair(&element, requestType)); 858 m_fullscreenElementStack.push_back(std::make_pair(&element, requestType));
838 859
839 fullscreenElementChanged(previousElement, &element, requestType); 860 setNeedsPaintPropertyUpdate(document());
840 }
841
842 void Fullscreen::fullscreenElementChanged(Element* fromElement,
843 Element* toElement,
844 RequestType toRequestType) {
845 DCHECK_NE(fromElement, toElement);
846
847 if (!document())
848 return;
849
850 document()->styleEngine().ensureUAStyleForFullscreen();
851
852 if (m_fullScreenLayoutObject)
853 m_fullScreenLayoutObject->unwrapLayoutObject();
854 DCHECK(!m_fullScreenLayoutObject);
855
856 if (fromElement) {
857 DCHECK_NE(fromElement, fullscreenElement());
858
859 fromElement->pseudoStateChanged(CSSSelector::PseudoFullScreen);
860
861 fromElement->setContainsFullScreenElement(false);
862 fromElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(
863 false);
864 }
865
866 if (toElement) {
867 DCHECK_EQ(toElement, fullscreenElement());
868
869 toElement->pseudoStateChanged(CSSSelector::PseudoFullScreen);
870
871 // OOPIF: For RequestType::PrefixedForCrossProcessDescendant, |toElement| is
872 // the iframe element for the out-of-process frame that contains the
873 // fullscreen element. Hence, it must match :-webkit-full-screen-ancestor.
874 if (toRequestType == RequestType::PrefixedForCrossProcessDescendant) {
875 DCHECK(isHTMLIFrameElement(toElement));
876 toElement->setContainsFullScreenElement(true);
877 }
878 toElement->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(
879 true);
880
881 // Create a placeholder block for the fullscreen element, to keep the page
882 // from reflowing when the element is removed from the normal flow. Only do
883 // this for a LayoutBox, as only a box will have a frameRect. The
884 // placeholder will be created in setFullScreenLayoutObject() during layout.
885 LayoutObject* layoutObject = toElement->layoutObject();
886 bool shouldCreatePlaceholder = layoutObject && layoutObject->isBox();
887 if (shouldCreatePlaceholder) {
888 m_savedPlaceholderFrameRect = toLayoutBox(layoutObject)->frameRect();
889 m_savedPlaceholderComputedStyle =
890 ComputedStyle::clone(layoutObject->styleRef());
891 }
892
893 if (toElement != document()->documentElement()) {
894 LayoutFullScreen::wrapLayoutObject(
895 layoutObject, layoutObject ? layoutObject->parent() : 0, document());
896 }
897 }
898
899 if (LocalFrame* frame = document()->frame()) {
900 // TODO(foolip): Synchronize hover state changes with animation frames.
901 // https://crbug.com/668758
902 frame->eventHandler().scheduleHoverStateUpdate();
903 frame->chromeClient().fullscreenElementChanged(fromElement, toElement);
904
905 if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() &&
906 !RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
907 // Fullscreen status affects scroll paint properties through
908 // FrameView::userInputScrollable().
909 if (FrameView* frameView = frame->view())
910 frameView->setNeedsPaintPropertyUpdate();
911 }
912 }
913
914 // TODO(foolip): This should not call updateStyleAndLayoutTree.
915 document()->updateStyleAndLayoutTree();
916 } 861 }
917 862
918 DEFINE_TRACE(Fullscreen) { 863 DEFINE_TRACE(Fullscreen) {
919 visitor->trace(m_pendingRequests); 864 visitor->trace(m_pendingFullscreenElement);
920 visitor->trace(m_fullscreenElementStack); 865 visitor->trace(m_fullscreenElementStack);
866 visitor->trace(m_currentFullScreenElement);
867 visitor->trace(m_eventQueue);
921 Supplement<Document>::trace(visitor); 868 Supplement<Document>::trace(visitor);
922 ContextLifecycleObserver::trace(visitor); 869 ContextLifecycleObserver::trace(visitor);
923 } 870 }
924 871
925 } // namespace blink 872 } // 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