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

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

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