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

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

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