OLD | NEW |
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 Loading... |
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 "core/dom/Document.h" | 32 #include "core/dom/Document.h" |
33 #include "core/dom/ElementTraversal.h" | 33 #include "core/dom/ElementTraversal.h" |
34 #include "core/dom/StyleEngine.h" | 34 #include "core/dom/StyleEngine.h" |
35 #include "core/events/Event.h" | 35 #include "core/events/Event.h" |
36 #include "core/frame/FrameHost.h" | |
37 #include "core/frame/HostsUsingFeatures.h" | 36 #include "core/frame/HostsUsingFeatures.h" |
38 #include "core/frame/LocalFrame.h" | 37 #include "core/frame/LocalFrame.h" |
39 #include "core/frame/Settings.h" | 38 #include "core/frame/Settings.h" |
40 #include "core/frame/UseCounter.h" | 39 #include "core/frame/UseCounter.h" |
41 #include "core/html/HTMLIFrameElement.h" | 40 #include "core/html/HTMLIFrameElement.h" |
42 #include "core/input/EventHandler.h" | 41 #include "core/input/EventHandler.h" |
43 #include "core/inspector/ConsoleMessage.h" | 42 #include "core/inspector/ConsoleMessage.h" |
44 #include "core/layout/LayoutBlockFlow.h" | 43 #include "core/layout/LayoutBlockFlow.h" |
45 #include "core/layout/LayoutFullScreen.h" | 44 #include "core/layout/LayoutFullScreen.h" |
46 #include "core/layout/api/LayoutFullScreenItem.h" | 45 #include "core/layout/api/LayoutFullScreenItem.h" |
(...skipping 244 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
291 if (!forCrossProcessDescendant) { | 290 if (!forCrossProcessDescendant) { |
292 if (document.isSecureContext()) { | 291 if (document.isSecureContext()) { |
293 UseCounter::count(document, UseCounter::FullscreenSecureOrigin); | 292 UseCounter::count(document, UseCounter::FullscreenSecureOrigin); |
294 } else { | 293 } else { |
295 UseCounter::count(document, UseCounter::FullscreenInsecureOrigin); | 294 UseCounter::count(document, UseCounter::FullscreenInsecureOrigin); |
296 HostsUsingFeatures::countAnyWorld( | 295 HostsUsingFeatures::countAnyWorld( |
297 document, HostsUsingFeatures::Feature::FullscreenInsecureHost); | 296 document, HostsUsingFeatures::Feature::FullscreenInsecureHost); |
298 } | 297 } |
299 } | 298 } |
300 | 299 |
301 // Ignore this request if the document is not in a live frame. | 300 // Ignore this call if the document is not in a live frame. |
302 if (!document.isActive()) | 301 if (!document.isActive() || !document.frame()) |
303 return; | 302 return; |
304 | 303 |
305 // If |element| is on top of |doc|'s fullscreen element stack, terminate these | 304 // If |element| is on top of |doc|'s fullscreen element stack, terminate these |
306 // substeps. | 305 // substeps. |
307 if (&element == fullscreenElementFrom(document)) | 306 if (&element == fullscreenElementFrom(document)) |
308 return; | 307 return; |
309 | 308 |
310 do { | 309 do { |
311 // 1. If any of the following conditions are false, then terminate these | 310 // 1. If any of the following conditions are false, then terminate these |
312 // steps and queue a task to fire an event named fullscreenerror with its | 311 // steps and queue a task to fire an event named fullscreenerror with its |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
392 continue; | 391 continue; |
393 } | 392 } |
394 | 393 |
395 // 4. Otherwise, do nothing for this document. It stays the same. | 394 // 4. Otherwise, do nothing for this document. It stays the same. |
396 } while (++current != docs.end()); | 395 } while (++current != docs.end()); |
397 | 396 |
398 from(document).m_forCrossProcessDescendant = forCrossProcessDescendant; | 397 from(document).m_forCrossProcessDescendant = forCrossProcessDescendant; |
399 | 398 |
400 // 5. Return, and run the remaining steps asynchronously. | 399 // 5. Return, and run the remaining steps asynchronously. |
401 // 6. Optionally, perform some animation. | 400 // 6. Optionally, perform some animation. |
402 document.frameHost()->chromeClient().enterFullscreenForElement(&element); | 401 from(document).m_pendingFullscreenElement = &element; |
| 402 document.frame()->chromeClient().enterFullscreen(*document.frame()); |
403 | 403 |
404 // 7. Optionally, display a message indicating how the user can exit | 404 // 7. Optionally, display a message indicating how the user can exit |
405 // displaying the context object fullscreen. | 405 // displaying the context object fullscreen. |
406 return; | 406 return; |
407 } while (false); | 407 } while (false); |
408 | 408 |
409 from(document).enqueueErrorEvent(element, requestType); | 409 from(document).enqueueErrorEvent(element, requestType); |
410 } | 410 } |
411 | 411 |
412 // https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen | 412 // https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen |
(...skipping 21 matching lines...) Expand all Loading... |
434 DCHECK_EQ(from(doc).m_fullscreenElementStack.size(), 1u); | 434 DCHECK_EQ(from(doc).m_fullscreenElementStack.size(), 1u); |
435 | 435 |
436 // 4. Act as if the exitFullscreen() method was invoked on |doc|. | 436 // 4. Act as if the exitFullscreen() method was invoked on |doc|. |
437 exitFullscreen(doc); | 437 exitFullscreen(doc); |
438 } | 438 } |
439 | 439 |
440 // https://fullscreen.spec.whatwg.org/#exit-fullscreen | 440 // https://fullscreen.spec.whatwg.org/#exit-fullscreen |
441 void Fullscreen::exitFullscreen(Document& document) { | 441 void Fullscreen::exitFullscreen(Document& document) { |
442 // The exitFullscreen() method must run these steps: | 442 // The exitFullscreen() method must run these steps: |
443 | 443 |
444 // 1. Let doc be the context object. (i.e. "this") | 444 // Ignore this call if the document is not in a live frame. |
445 if (!document.isActive()) | 445 if (!document.isActive() || !document.frame()) |
446 return; | 446 return; |
447 | 447 |
| 448 // 1. Let doc be the context object. (i.e. "this") |
448 // 2. If doc's fullscreen element stack is empty, terminate these steps. | 449 // 2. If doc's fullscreen element stack is empty, terminate these steps. |
449 if (!fullscreenElementFrom(document)) | 450 if (!fullscreenElementFrom(document)) |
450 return; | 451 return; |
451 | 452 |
452 // 3. Let descendants be all the doc's descendant browsing context's documents | 453 // 3. Let descendants be all the doc's descendant browsing context's documents |
453 // with a non-empty fullscreen element stack (if any), ordered so that the | 454 // with a non-empty fullscreen element stack (if any), ordered so that the |
454 // child of the doc is last and the document furthest away from the doc is | 455 // child of the doc is last and the document furthest away from the doc is |
455 // first. | 456 // first. |
456 HeapDeque<Member<Document>> descendants; | 457 HeapDeque<Member<Document>> descendants; |
457 for (Frame* descendant = | 458 for (Frame* descendant = document.frame()->tree().traverseNext(); descendant; |
458 document.frame() ? document.frame()->tree().traverseNext() : nullptr; | 459 descendant = descendant->tree().traverseNext()) { |
459 descendant; descendant = descendant->tree().traverseNext()) { | |
460 if (!descendant->isLocalFrame()) | 460 if (!descendant->isLocalFrame()) |
461 continue; | 461 continue; |
462 DCHECK(toLocalFrame(descendant)->document()); | 462 DCHECK(toLocalFrame(descendant)->document()); |
463 if (fullscreenElementFrom(*toLocalFrame(descendant)->document())) | 463 if (fullscreenElementFrom(*toLocalFrame(descendant)->document())) |
464 descendants.prepend(toLocalFrame(descendant)->document()); | 464 descendants.prepend(toLocalFrame(descendant)->document()); |
465 } | 465 } |
466 | 466 |
467 // 4. For each descendant in descendants, empty descendant's fullscreen | 467 // 4. For each descendant in descendants, empty descendant's fullscreen |
468 // element stack, and queue a task to fire an event named fullscreenchange | 468 // element stack, and queue a task to fire an event named fullscreenchange |
469 // with its bubbles attribute set to true on descendant. | 469 // with its bubbles attribute set to true on descendant. |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
514 } | 514 } |
515 } | 515 } |
516 | 516 |
517 // 4. Otherwise, set doc to null. | 517 // 4. Otherwise, set doc to null. |
518 currentDoc = nullptr; | 518 currentDoc = nullptr; |
519 } | 519 } |
520 | 520 |
521 // 6. Return, and run the remaining steps asynchronously. | 521 // 6. Return, and run the remaining steps asynchronously. |
522 // 7. Optionally, perform some animation. | 522 // 7. Optionally, perform some animation. |
523 | 523 |
524 FrameHost* host = document.frameHost(); | 524 // Only exit fullscreen mode if the fullscreen element stack is empty. |
525 | |
526 // Speculative fix for engaget.com/videos per crbug.com/336239. | |
527 // FIXME: This check is wrong. We DCHECK(document->isActive()) above | |
528 // so this should be redundant and should be removed! | |
529 if (!host) | |
530 return; | |
531 | |
532 // Only exit out of full screen window mode if there are no remaining elements | |
533 // in the full screen stack. | |
534 if (!newTop) { | 525 if (!newTop) { |
535 host->chromeClient().exitFullscreen(document.frame()); | 526 document.frame()->chromeClient().exitFullscreen(*document.frame()); |
536 return; | 527 return; |
537 } | 528 } |
538 | 529 |
539 // Otherwise, notify the chrome of the new full screen element. | 530 // Otherwise, enter fullscreen for the fullscreen element stack's top element. |
540 host->chromeClient().enterFullscreenForElement(newTop); | 531 from(document).m_pendingFullscreenElement = newTop; |
| 532 from(document).didEnterFullscreen(); |
541 } | 533 } |
542 | 534 |
543 // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled | 535 // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled |
544 bool Fullscreen::fullscreenEnabled(Document& document) { | 536 bool Fullscreen::fullscreenEnabled(Document& document) { |
545 // The fullscreenEnabled attribute's getter must return true if the context | 537 // The fullscreenEnabled attribute's getter must return true if the context |
546 // object is allowed to use the feature indicated by attribute name | 538 // object is allowed to use the feature indicated by attribute name |
547 // allowfullscreen and fullscreen is supported, and false otherwise. | 539 // allowfullscreen and fullscreen is supported, and false otherwise. |
548 return allowedToUseFullscreen(document.frame()) && | 540 return allowedToUseFullscreen(document.frame()) && |
549 fullscreenIsSupported(document); | 541 fullscreenIsSupported(document); |
550 } | 542 } |
551 | 543 |
552 void Fullscreen::didEnterFullscreenForElement(Element* element) { | 544 void Fullscreen::didEnterFullscreen() { |
553 DCHECK(element); | |
554 if (!document()->isActive() || !document()->frame()) | 545 if (!document()->isActive() || !document()->frame()) |
555 return; | 546 return; |
556 | 547 |
| 548 // Start the timer for events enqueued by |requestFullscreen()|. The hover |
| 549 // state update is scheduled first so that it's done when the events fire. |
| 550 document()->frame()->eventHandler().scheduleHoverStateUpdate(); |
| 551 m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE); |
| 552 |
| 553 Element* element = m_pendingFullscreenElement.release(); |
| 554 if (!element) |
| 555 return; |
| 556 |
557 if (m_currentFullScreenElement == element) | 557 if (m_currentFullScreenElement == element) |
558 return; | 558 return; |
559 | 559 |
| 560 if (!element->isConnected() || &element->document() != document()) { |
| 561 // The element was removed or has moved to another document since the |
| 562 // |requestFullscreen()| call. Exit fullscreen again to recover. |
| 563 // TODO(foolip): Fire a fullscreenerror event. This is currently difficult |
| 564 // because the fullscreenchange event has already been enqueued and possibly |
| 565 // even fired. https://crbug.com/402376 |
| 566 LocalFrame& frame = *document()->frame(); |
| 567 frame.chromeClient().exitFullscreen(frame); |
| 568 return; |
| 569 } |
| 570 |
560 if (m_fullScreenLayoutObject) | 571 if (m_fullScreenLayoutObject) |
561 m_fullScreenLayoutObject->unwrapLayoutObject(); | 572 m_fullScreenLayoutObject->unwrapLayoutObject(); |
562 | 573 |
563 Element* previousElement = m_currentFullScreenElement; | 574 Element* previousElement = m_currentFullScreenElement; |
564 m_currentFullScreenElement = element; | 575 m_currentFullScreenElement = element; |
565 | 576 |
566 // Create a placeholder block for a the full-screen element, to keep the page | 577 // Create a placeholder block for a the full-screen element, to keep the page |
567 // from reflowing when the element is removed from the normal flow. Only do | 578 // from reflowing when the element is removed from the normal flow. Only do |
568 // this for a LayoutBox, as only a box will have a frameRect. The placeholder | 579 // this for a LayoutBox, as only a box will have a frameRect. The placeholder |
569 // will be created in setFullScreenLayoutObject() during layout. | 580 // will be created in setFullScreenLayoutObject() during layout. |
(...skipping 27 matching lines...) Expand all Loading... |
597 | 608 |
598 m_currentFullScreenElement | 609 m_currentFullScreenElement |
599 ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true); | 610 ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true); |
600 | 611 |
601 document()->styleEngine().ensureUAStyleForFullscreen(); | 612 document()->styleEngine().ensureUAStyleForFullscreen(); |
602 m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen); | 613 m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen); |
603 | 614 |
604 // FIXME: This should not call updateStyleAndLayoutTree. | 615 // FIXME: This should not call updateStyleAndLayoutTree. |
605 document()->updateStyleAndLayoutTree(); | 616 document()->updateStyleAndLayoutTree(); |
606 | 617 |
607 document()->frame()->eventHandler().scheduleHoverStateUpdate(); | |
608 | |
609 m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE); | |
610 | |
611 document()->frame()->chromeClient().fullscreenElementChanged(previousElement, | 618 document()->frame()->chromeClient().fullscreenElementChanged(previousElement, |
612 element); | 619 element); |
613 } | 620 } |
614 | 621 |
615 void Fullscreen::didExitFullscreen() { | 622 void Fullscreen::didExitFullscreen() { |
616 if (!document()->isActive() || !document()->frame()) | 623 if (!document()->isActive() || !document()->frame()) |
617 return; | 624 return; |
618 | 625 |
| 626 // Start the timer for events enqueued by |exitFullscreen()|. The hover state |
| 627 // 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 // If fullscreen was canceled by the browser, e.g. if the user pressed Esc, |
| 632 // then |exitFullscreen()| was never called. Let |fullyExitFullscreen()| clear |
| 633 // the fullscreen element stack and fire any events as necessary. |
| 634 // TODO(foolip): Remove this when state changes and events are synchronized |
| 635 // with animation frames. https://crbug.com/402376 |
| 636 fullyExitFullscreen(*document()); |
| 637 |
619 if (!m_currentFullScreenElement) | 638 if (!m_currentFullScreenElement) |
620 return; | 639 return; |
621 | 640 |
622 if (m_forCrossProcessDescendant) | 641 if (m_forCrossProcessDescendant) |
623 m_currentFullScreenElement->setContainsFullScreenElement(false); | 642 m_currentFullScreenElement->setContainsFullScreenElement(false); |
624 | 643 |
625 m_currentFullScreenElement | 644 m_currentFullScreenElement |
626 ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false); | 645 ->setContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false); |
627 | 646 |
628 if (m_fullScreenLayoutObject) | 647 if (m_fullScreenLayoutObject) |
629 LayoutFullScreenItem(m_fullScreenLayoutObject).unwrapLayoutObject(); | 648 LayoutFullScreenItem(m_fullScreenLayoutObject).unwrapLayoutObject(); |
630 | 649 |
631 document()->styleEngine().ensureUAStyleForFullscreen(); | 650 document()->styleEngine().ensureUAStyleForFullscreen(); |
632 m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen); | 651 m_currentFullScreenElement->pseudoStateChanged(CSSSelector::PseudoFullScreen); |
633 Element* previousElement = m_currentFullScreenElement; | 652 Element* previousElement = m_currentFullScreenElement; |
634 m_currentFullScreenElement = nullptr; | 653 m_currentFullScreenElement = nullptr; |
635 | 654 |
636 document()->frame()->eventHandler().scheduleHoverStateUpdate(); | |
637 | |
638 // When fullyExitFullscreen is called, we call exitFullscreen on the | |
639 // topDocument(). That means that the events will be queued there. So if we | |
640 // have no events here, start the timer on the exiting document. | |
641 Document* exitingDocument = document(); | |
642 if (m_eventQueue.isEmpty()) | |
643 exitingDocument = &topmostLocalAncestor(*document()); | |
644 DCHECK(exitingDocument); | |
645 from(*exitingDocument).m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE); | |
646 | |
647 m_forCrossProcessDescendant = false; | 655 m_forCrossProcessDescendant = false; |
648 | 656 |
649 document()->frame()->chromeClient().fullscreenElementChanged(previousElement, | 657 document()->frame()->chromeClient().fullscreenElementChanged(previousElement, |
650 nullptr); | 658 nullptr); |
651 } | 659 } |
652 | 660 |
653 void Fullscreen::setFullScreenLayoutObject(LayoutFullScreen* layoutObject) { | 661 void Fullscreen::setFullScreenLayoutObject(LayoutFullScreen* layoutObject) { |
654 if (layoutObject == m_fullScreenLayoutObject) | 662 if (layoutObject == m_fullScreenLayoutObject) |
655 return; | 663 return; |
656 | 664 |
(...skipping 28 matching lines...) Expand all Loading... |
685 DCHECK(document.hasFullscreenSupplement()); | 693 DCHECK(document.hasFullscreenSupplement()); |
686 Fullscreen& fullscreen = from(document); | 694 Fullscreen& fullscreen = from(document); |
687 EventTarget* target = fullscreen.fullscreenElement(); | 695 EventTarget* target = fullscreen.fullscreenElement(); |
688 if (!target) | 696 if (!target) |
689 target = fullscreen.currentFullScreenElement(); | 697 target = fullscreen.currentFullScreenElement(); |
690 if (!target) | 698 if (!target) |
691 target = &document; | 699 target = &document; |
692 event = createEvent(EventTypeNames::webkitfullscreenchange, *target); | 700 event = createEvent(EventTypeNames::webkitfullscreenchange, *target); |
693 } | 701 } |
694 m_eventQueue.append(event); | 702 m_eventQueue.append(event); |
695 // NOTE: The timer is started in | 703 // NOTE: The timer is started in didEnterFullscreen/didExitFullscreen. |
696 // didEnterFullscreenForElement/didExitFullscreen. | |
697 } | 704 } |
698 | 705 |
699 void Fullscreen::enqueueErrorEvent(Element& element, RequestType requestType) { | 706 void Fullscreen::enqueueErrorEvent(Element& element, RequestType requestType) { |
700 Event* event; | 707 Event* event; |
701 if (requestType == UnprefixedRequest) | 708 if (requestType == UnprefixedRequest) |
702 event = createEvent(EventTypeNames::fullscreenerror, element.document()); | 709 event = createEvent(EventTypeNames::fullscreenerror, element.document()); |
703 else | 710 else |
704 event = createEvent(EventTypeNames::webkitfullscreenerror, element); | 711 event = createEvent(EventTypeNames::webkitfullscreenerror, element); |
705 m_eventQueue.append(event); | 712 m_eventQueue.append(event); |
706 m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE); | 713 m_eventQueueTimer.startOneShot(0, BLINK_FROM_HERE); |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
759 | 766 |
760 m_fullscreenElementStack.pop_back(); | 767 m_fullscreenElementStack.pop_back(); |
761 } | 768 } |
762 | 769 |
763 void Fullscreen::pushFullscreenElementStack(Element& element, | 770 void Fullscreen::pushFullscreenElementStack(Element& element, |
764 RequestType requestType) { | 771 RequestType requestType) { |
765 m_fullscreenElementStack.append(std::make_pair(&element, requestType)); | 772 m_fullscreenElementStack.append(std::make_pair(&element, requestType)); |
766 } | 773 } |
767 | 774 |
768 DEFINE_TRACE(Fullscreen) { | 775 DEFINE_TRACE(Fullscreen) { |
| 776 visitor->trace(m_pendingFullscreenElement); |
| 777 visitor->trace(m_fullscreenElementStack); |
769 visitor->trace(m_currentFullScreenElement); | 778 visitor->trace(m_currentFullScreenElement); |
770 visitor->trace(m_fullscreenElementStack); | |
771 visitor->trace(m_eventQueue); | 779 visitor->trace(m_eventQueue); |
772 Supplement<Document>::trace(visitor); | 780 Supplement<Document>::trace(visitor); |
773 ContextLifecycleObserver::trace(visitor); | 781 ContextLifecycleObserver::trace(visitor); |
774 } | 782 } |
775 | 783 |
776 } // namespace blink | 784 } // namespace blink |
OLD | NEW |