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 163 matching lines...) Loading... | |
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...) Loading... | |
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. | |
alexmos
2016/12/15 07:58:53
Does this CL help with problematic nested cases in
foolip
2016/12/19 17:36:57
The iframe fullscreen flag is unrelated, it's mere
| |
247 | |
248 for (Document* document = nextLocalAncestor(doc); document; | |
249 document = nextLocalAncestor(*document)) { | |
250 if (Fullscreen::fullscreenElementStackSizeFrom(*document) == 1) | |
eae
2016/12/14 00:19:24
This is a little hard to follow, could you move do
mlamouri (slow - plz ping)
2016/12/14 16:24:12
+1
foolip
2016/12/19 17:36:57
Done.
| |
251 docs.append(document); | |
252 else | |
253 break; | |
254 } | |
255 | |
256 // 4. Return |docs|. | |
eae
2016/12/14 00:19:24
Unnecessary comment.
mlamouri (slow - plz ping)
2016/12/14 16:24:12
I guess the idea is to keep track of the spec step
foolip
2016/12/19 17:36:57
Yeah, admittedly it gets a bit silly at points, bu
| |
257 return docs; | |
258 } | |
259 | |
260 // Creates a non-bubbling event with |document| as its target. | |
mlamouri (slow - plz ping)
2016/12/14 16:24:12
Does it have to be cancellable? The one you create
foolip
2016/12/19 17:36:56
The spec uses https://dom.spec.whatwg.org/#concept
| |
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. | |
mlamouri (slow - plz ping)
2016/12/14 16:24:12
ditto
| |
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 } | |
mlamouri (slow - plz ping)
2016/12/14 16:24:12
Doesn't seem very useful.
foolip
2016/12/19 17:36:57
It made the error event case a bit more compact, b
| |
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 |
eae
2016/12/14 00:19:24
Comment doesn't match the logic anymore (it's been
foolip
2016/12/19 17:36:57
It's the forCrossProcessDescendant case that makes
| |
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(); |
mlamouri (slow - plz ping)
2016/12/14 16:24:12
nit: Maybe you could move this all the way up beca
foolip
2016/12/19 17:36:57
This is indeed something to worry about, at variou
| |
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()) { | |
mlamouri (slow - plz ping)
2016/12/14 16:24:12
early break for readability:
```
if (!frame->owner
foolip
2016/12/19 17:36:57
Done.
| |
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); |
alexmos
2016/12/15 07:58:53
Seems a bit inefficient to collect all these docs
foolip
2016/12/19 17:36:57
Yeah. The spec uses the same primitive both before
| |
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 |
alexmos
2016/12/15 07:58:53
nit: s/oppposed/opposed/
foolip
2016/12/19 17:36:57
Done.
| |
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()) | |
alexmos
2016/12/15 07:58:53
I wonder if instead of isLocalRoot() this should b
foolip
2016/12/19 17:36:57
Done, this was a not-well-thought-out change compa
| |
621 resize = true; | |
622 | |
623 // 7. Return |promise|, and run the remaining steps in parallel. | |
624 // TODO(foolip): Promises. https://crbug.com/644637 | |
625 | |
alexmos
2016/12/15 07:58:53
Is it worth DCHECKing somewhere that if exitType i
foolip
2016/12/19 17:36:57
Yes, this is currently true because Fullscreen::fu
| |
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 | |
alexmos
2016/12/15 07:58:53
One thing we could do for these nested fullscreen
foolip
2016/12/19 17:36:57
Yeah, I think that is the obvious next thing to tr
alexmos
2016/12/19 22:53:26
sgtm. I'm planning to be there.
| |
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) { |
mlamouri (slow - plz ping)
2016/12/14 16:24:12
Maybe an early return? I know, I'm obsessed :)
foolip
2016/12/19 17:36:57
This one I think would be less clear and actually
| |
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()); |
mlamouri (slow - plz ping)
2016/12/14 16:24:12
DCHECK_EQ?
foolip
2016/12/19 17:36:57
Done, and verified no other missed opportunities t
| |
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(); | |
eae
2016/12/14 00:19:24
Is document() guaranteed to be non-null here?
mlamouri (slow - plz ping)
2016/12/14 16:24:12
Would it make sense to return Document& if you alw
foolip
2016/12/19 17:36:57
We get here from pushFullscreenElementStack or pop
| |
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(); | |
mlamouri (slow - plz ping)
2016/12/14 16:24:12
Is this variable added just for readability? You c
foolip
2016/12/19 17:36:56
This is moved code and getting rid of it is the ne
| |
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 |
OLD | NEW |