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

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

Issue 2855843002: Reland "Sync requestFullscreen() and exitFullscreen() algorithms with the spec" (Closed)
Patch Set: Reland "Sync requestFullscreen() and exitFullscreen() algorithms with the spec" Created 3 years, 6 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 144 matching lines...) Expand 10 before | Expand all | Expand 10 after
155 // |element|'s node document's browsing context's browsing context container, 155 // |element|'s node document's browsing context's browsing context container,
156 // or it has no browsing context container. 156 // or it has no browsing context container.
157 if (const Element* owner = element.GetDocument().LocalOwner()) { 157 if (const Element* owner = element.GetDocument().LocalOwner()) {
158 if (!FullscreenElementReady(*owner)) 158 if (!FullscreenElementReady(*owner))
159 return false; 159 return false;
160 } 160 }
161 161
162 return true; 162 return true;
163 } 163 }
164 164
165 bool IsPrefixed(const AtomicString& type) { 165 // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen step 4:
166 return type == EventTypeNames::webkitfullscreenchange || 166 bool RequestFullscreenConditionsMet(Element& pending, Document& document) {
167 type == EventTypeNames::webkitfullscreenerror; 167 // |pending|'s namespace is the HTML namespace or |pending| is an SVG svg or
168 } 168 // MathML math element. Note: MathML is not supported.
169 if (!pending.IsHTMLElement() && !isSVGSVGElement(pending))
170 return false;
169 171
170 Event* CreateEvent(const AtomicString& type, EventTarget& target) { 172 // The fullscreen element ready check for |pending| returns false.
171 EventInit initializer; 173 if (!FullscreenElementReady(pending))
172 initializer.setBubbles(IsPrefixed(type)); 174 return false;
173 Event* event = Event::Create(type, initializer); 175
174 event->SetTarget(&target); 176 // Fullscreen is supported.
175 return event; 177 if (!FullscreenIsSupported(document))
178 return false;
179
180 // This algorithm is allowed to request fullscreen.
181 if (!AllowedToRequestFullscreen(document))
182 return false;
183
184 return true;
176 } 185 }
177 186
178 // Walks the frame tree and returns the first local ancestor frame, if any. 187 // Walks the frame tree and returns the first local ancestor frame, if any.
179 LocalFrame* NextLocalAncestor(Frame& frame) { 188 LocalFrame* NextLocalAncestor(Frame& frame) {
180 Frame* parent = frame.Tree().Parent(); 189 Frame* parent = frame.Tree().Parent();
181 if (!parent) 190 if (!parent)
182 return nullptr; 191 return nullptr;
183 if (parent->IsLocalFrame()) 192 if (parent->IsLocalFrame())
184 return ToLocalFrame(parent); 193 return ToLocalFrame(parent);
185 return NextLocalAncestor(*parent); 194 return NextLocalAncestor(*parent);
186 } 195 }
187 196
188 // Walks the document's frame tree and returns the document of the first local 197 // Walks the document's frame tree and returns the document of the first local
189 // ancestor frame, if any. 198 // ancestor frame, if any.
190 Document* NextLocalAncestor(Document& document) { 199 Document* NextLocalAncestor(Document& document) {
191 LocalFrame* frame = document.GetFrame(); 200 LocalFrame* frame = document.GetFrame();
192 if (!frame) 201 if (!frame)
193 return nullptr; 202 return nullptr;
194 LocalFrame* next = NextLocalAncestor(*document.GetFrame()); 203 LocalFrame* next = NextLocalAncestor(*frame);
195 if (!next) 204 if (!next)
196 return nullptr; 205 return nullptr;
197 DCHECK(next->GetDocument()); 206 DCHECK(next->GetDocument());
198 return next->GetDocument(); 207 return next->GetDocument();
199 } 208 }
200 209
201 // Helper to walk the ancestor chain and return the Document of the topmost 210 // Helper to walk the ancestor chain and return the Document of the topmost
202 // local ancestor frame. Note that this is not the same as the topmost frame's 211 // local ancestor frame. Note that this is not the same as the topmost frame's
203 // Document, which might be unavailable in OOPIF scenarios. For example, with 212 // Document, which might be unavailable in OOPIF scenarios. For example, with
204 // OOPIFs, when called on the bottom frame's Document in a A-B-C-B hierarchy in 213 // OOPIFs, when called on the bottom frame's Document in a A-B-C-B hierarchy in
205 // process B, this will skip remote frame C and return this frame: A-[B]-C-B. 214 // process B, this will skip remote frame C and return this frame: A-[B]-C-B.
206 Document& TopmostLocalAncestor(Document& document) { 215 Document& TopmostLocalAncestor(Document& document) {
207 if (Document* next = NextLocalAncestor(document)) 216 if (Document* next = NextLocalAncestor(document))
208 return TopmostLocalAncestor(*next); 217 return TopmostLocalAncestor(*next);
209 return document; 218 return document;
210 } 219 }
211 220
212 // Helper to find the browsing context container in |doc| that embeds the 221 // https://fullscreen.spec.whatwg.org/#collect-documents-to-unfullscreen
213 // |descendant| Document, possibly through multiple levels of nesting. This 222 HeapVector<Member<Document>> CollectDocumentsToUnfullscreen(
214 // works even in OOPIF scenarios like A-B-A, where there may be remote frames 223 Document& doc,
215 // in between |doc| and |descendant|. 224 Fullscreen::ExitType exit_type) {
216 HTMLFrameOwnerElement* FindContainerForDescendant(const Document& doc, 225 DCHECK(Fullscreen::FullscreenElementFrom(doc));
217 const Document& descendant) { 226
218 Frame* frame = descendant.GetFrame(); 227 // 1. If |doc|'s top layer consists of more than a single element that has
219 while (frame->Tree().Parent() != doc.GetFrame()) 228 // its fullscreen flag set, return the empty set.
220 frame = frame->Tree().Parent(); 229 // TODO(foolip): See TODO in |fullyExitFullscreen()|.
221 return ToHTMLFrameOwnerElement(frame->Owner()); 230 if (exit_type != Fullscreen::ExitType::kFully &&
231 Fullscreen::FullscreenElementStackSizeFrom(doc) > 1)
232 return HeapVector<Member<Document>>();
233
234 // 2. Let |docs| be an ordered set consisting of |doc|.
235 HeapVector<Member<Document>> docs;
236 docs.push_back(&doc);
237
238 // 3. While |docs|'s last document ...
239 //
240 // OOPIF: Skip over remote frames, assuming that they have exactly one element
241 // in their fullscreen element stacks, thereby erring on the side of exiting
242 // fullscreen. TODO(alexmos): Deal with nested fullscreen cases, see
243 // https://crbug.com/617369.
244 for (Document* document = NextLocalAncestor(doc); document;
245 document = NextLocalAncestor(*document)) {
246 // ... has a browsing context container whose node document's top layer
247 // consists of a single element that has its fullscreen flag set and does
248 // not have its iframe fullscreen flag set (if any), append that node
249 // document to |docs|.
250 // TODO(foolip): Support the iframe fullscreen flag.
251 // https://crbug.com/644695
252 if (Fullscreen::FullscreenElementStackSizeFrom(*document) == 1)
253 docs.push_back(document);
254 else
255 break;
256 }
257
258 // 4. Return |docs|.
259 return docs;
222 } 260 }
223 261
224 // Fullscreen status affects scroll paint properties through 262 // Creates a non-bubbling event with |document| as its target.
225 // LocalFrameView::userInputScrollable(). 263 Event* CreateEvent(const AtomicString& type, Document& document) {
226 void SetNeedsPaintPropertyUpdate(Document* document) { 264 DCHECK(type == EventTypeNames::fullscreenchange ||
227 if (!RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() || 265 type == EventTypeNames::fullscreenerror);
228 RuntimeEnabledFeatures::rootLayerScrollingEnabled())
229 return;
230 266
231 if (!document) 267 Event* event = Event::Create(type);
232 return; 268 event->SetTarget(&document);
269 return event;
270 }
233 271
234 LocalFrame* frame = document->GetFrame(); 272 // Creates a bubbling event with |element| as its target. If |element| is not
235 if (!frame) 273 // connected to |document|, then |document| is used as the target instead.
236 return; 274 Event* CreatePrefixedEvent(const AtomicString& type,
275 Element& element,
276 Document& document) {
277 DCHECK(type == EventTypeNames::webkitfullscreenchange ||
278 type == EventTypeNames::webkitfullscreenerror);
237 279
238 if (LocalFrameView* frame_view = frame->View()) 280 Event* event = Event::CreateBubble(type);
239 frame_view->SetNeedsPaintPropertyUpdate(); 281 if (element.isConnected() && element.GetDocument() == document)
282 event->SetTarget(&element);
283 else
284 event->SetTarget(&document);
285 return event;
286 }
287
288 Event* CreateChangeEvent(Document& document,
289 Element& element,
290 Fullscreen::RequestType request_type) {
291 if (request_type == Fullscreen::RequestType::kUnprefixed)
292 return CreateEvent(EventTypeNames::fullscreenchange, document);
293 return CreatePrefixedEvent(EventTypeNames::webkitfullscreenchange, element,
294 document);
295 }
296
297 Event* CreateErrorEvent(Document& document,
298 Element& element,
299 Fullscreen::RequestType request_type) {
300 if (request_type == Fullscreen::RequestType::kUnprefixed)
301 return CreateEvent(EventTypeNames::fullscreenerror, document);
302 return CreatePrefixedEvent(EventTypeNames::webkitfullscreenerror, element,
303 document);
304 }
305
306 void DispatchEvents(const HeapVector<Member<Event>>& events) {
307 for (Event* event : events)
308 event->target()->DispatchEvent(event);
240 } 309 }
241 310
242 } // anonymous namespace 311 } // anonymous namespace
243 312
244 const char* Fullscreen::SupplementName() { 313 const char* Fullscreen::SupplementName() {
245 return "Fullscreen"; 314 return "Fullscreen";
246 } 315 }
247 316
248 Fullscreen& Fullscreen::From(Document& document) { 317 Fullscreen& Fullscreen::From(Document& document) {
249 Fullscreen* fullscreen = FromIfExists(document); 318 Fullscreen* fullscreen = FromIfExists(document);
250 if (!fullscreen) { 319 if (!fullscreen) {
251 fullscreen = new Fullscreen(document); 320 fullscreen = new Fullscreen(document);
252 Supplement<Document>::ProvideTo(document, SupplementName(), fullscreen); 321 Supplement<Document>::ProvideTo(document, SupplementName(), fullscreen);
253 } 322 }
254 323
255 return *fullscreen; 324 return *fullscreen;
256 } 325 }
257 326
258 Fullscreen* Fullscreen::FromIfExistsSlow(Document& document) { 327 Fullscreen* Fullscreen::FromIfExistsSlow(Document& document) {
259 return static_cast<Fullscreen*>( 328 return static_cast<Fullscreen*>(
260 Supplement<Document>::From(document, SupplementName())); 329 Supplement<Document>::From(document, SupplementName()));
261 } 330 }
262 331
263 Element* Fullscreen::FullscreenElementFrom(Document& document) { 332 Element* Fullscreen::FullscreenElementFrom(Document& document) {
264 if (Fullscreen* found = FromIfExists(document)) 333 if (Fullscreen* found = FromIfExists(document))
265 return found->FullscreenElement(); 334 return found->FullscreenElement();
266 return nullptr; 335 return nullptr;
267 } 336 }
268 337
338 size_t Fullscreen::FullscreenElementStackSizeFrom(Document& document) {
339 if (Fullscreen* found = FromIfExists(document))
340 return found->fullscreen_element_stack_.size();
341 return 0;
342 }
343
269 Element* Fullscreen::FullscreenElementForBindingFrom(TreeScope& scope) { 344 Element* Fullscreen::FullscreenElementForBindingFrom(TreeScope& scope) {
270 Element* element = FullscreenElementFrom(scope.GetDocument()); 345 Element* element = FullscreenElementFrom(scope.GetDocument());
271 if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled()) 346 if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled())
272 return element; 347 return element;
273 348
274 // TODO(kochi): Once V0 code is removed, we can use the same logic for 349 // TODO(kochi): Once V0 code is removed, we can use the same logic for
275 // Document and ShadowRoot. 350 // Document and ShadowRoot.
276 if (!scope.RootNode().IsShadowRoot()) { 351 if (!scope.RootNode().IsShadowRoot()) {
277 // For Shadow DOM V0 compatibility: We allow returning an element in V0 352 // For Shadow DOM V0 compatibility: We allow returning an element in V0
278 // shadow tree, even though it leaks the Shadow DOM. 353 // shadow tree, even though it leaks the Shadow DOM.
279 if (element->IsInV0ShadowTree()) { 354 if (element->IsInV0ShadowTree()) {
280 UseCounter::Count(scope.GetDocument(), 355 UseCounter::Count(scope.GetDocument(),
281 UseCounter::kDocumentFullscreenElementInV0Shadow); 356 UseCounter::kDocumentFullscreenElementInV0Shadow);
282 return element; 357 return element;
283 } 358 }
284 } else if (!ToShadowRoot(scope.RootNode()).IsV1()) { 359 } else if (!ToShadowRoot(scope.RootNode()).IsV1()) {
285 return nullptr; 360 return nullptr;
286 } 361 }
287 return scope.AdjustedElement(*element); 362 return scope.AdjustedElement(*element);
288 } 363 }
289 364
290 Element* Fullscreen::CurrentFullScreenElementFrom(Document& document) {
291 if (Fullscreen* found = FromIfExists(document))
292 return found->CurrentFullScreenElement();
293 return nullptr;
294 }
295
296 Element* Fullscreen::CurrentFullScreenElementForBindingFrom(
297 Document& document) {
298 Element* element = CurrentFullScreenElementFrom(document);
299 if (!element || !RuntimeEnabledFeatures::fullscreenUnprefixedEnabled())
300 return element;
301
302 // For Shadow DOM V0 compatibility: We allow returning an element in V0 shadow
303 // tree, even though it leaks the Shadow DOM.
304 if (element->IsInV0ShadowTree()) {
305 UseCounter::Count(document,
306 UseCounter::kDocumentFullscreenElementInV0Shadow);
307 return element;
308 }
309 return document.AdjustedElement(*element);
310 }
311
312 bool Fullscreen::IsInFullscreenElementStack(const Element& element) { 365 bool Fullscreen::IsInFullscreenElementStack(const Element& element) {
313 const Fullscreen* found = FromIfExists(element.GetDocument()); 366 const Fullscreen* found = FromIfExists(element.GetDocument());
314 if (!found) 367 if (!found)
315 return false; 368 return false;
316 for (size_t i = 0; i < found->fullscreen_element_stack_.size(); ++i) { 369 for (size_t i = 0; i < found->fullscreen_element_stack_.size(); ++i) {
317 if (found->fullscreen_element_stack_[i].first.Get() == &element) 370 if (found->fullscreen_element_stack_[i].first.Get() == &element)
318 return true; 371 return true;
319 } 372 }
320 return false; 373 return false;
321 } 374 }
322 375
323 Fullscreen::Fullscreen(Document& document) 376 Fullscreen::Fullscreen(Document& document)
324 : Supplement<Document>(document), 377 : Supplement<Document>(document),
325 ContextLifecycleObserver(&document), 378 ContextLifecycleObserver(&document),
326 full_screen_layout_object_(nullptr), 379 full_screen_layout_object_(nullptr) {
327 event_queue_timer_(
328 TaskRunnerHelper::Get(TaskType::kUnthrottled, &document),
329 this,
330 &Fullscreen::EventQueueTimerFired),
331 for_cross_process_descendant_(false) {
332 document.SetHasFullscreenSupplement(); 380 document.SetHasFullscreenSupplement();
333 } 381 }
334 382
335 Fullscreen::~Fullscreen() {} 383 Fullscreen::~Fullscreen() {}
336 384
337 inline Document* Fullscreen::GetDocument() { 385 Document* Fullscreen::GetDocument() {
338 return ToDocument(LifecycleContext()); 386 return ToDocument(LifecycleContext());
339 } 387 }
340 388
341 void Fullscreen::ContextDestroyed(ExecutionContext*) { 389 void Fullscreen::ContextDestroyed(ExecutionContext*) {
342 event_queue_.clear();
343
344 if (full_screen_layout_object_) 390 if (full_screen_layout_object_)
345 full_screen_layout_object_->Destroy(); 391 full_screen_layout_object_->Destroy();
346 392
347 current_full_screen_element_ = nullptr; 393 pending_requests_.clear();
348 fullscreen_element_stack_.clear(); 394 fullscreen_element_stack_.clear();
349 } 395 }
350 396
351 // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen 397 // https://fullscreen.spec.whatwg.org/#dom-element-requestfullscreen
352 void Fullscreen::RequestFullscreen(Element& element) { 398 void Fullscreen::RequestFullscreen(Element& pending) {
353 // TODO(foolip): Make RequestType::Unprefixed the default when the unprefixed 399 // TODO(foolip): Make RequestType::Unprefixed the default when the unprefixed
354 // API is enabled. https://crbug.com/383813 400 // API is enabled. https://crbug.com/383813
355 RequestFullscreen(element, RequestType::kPrefixed, false); 401 RequestFullscreen(pending, RequestType::kPrefixed);
356 } 402 }
357 403
358 void Fullscreen::RequestFullscreen(Element& element, 404 void Fullscreen::RequestFullscreen(Element& pending, RequestType request_type) {
359 RequestType request_type, 405 Document& document = pending.GetDocument();
360 bool for_cross_process_descendant) { 406
361 Document& document = element.GetDocument(); 407 // Ignore this call if the document is not in a live frame.
408 if (!document.IsActive() || !document.GetFrame())
409 return;
410
411 bool for_cross_process_descendant =
412 request_type == RequestType::kPrefixedForCrossProcessDescendant;
362 413
363 // Use counters only need to be incremented in the process of the actual 414 // Use counters only need to be incremented in the process of the actual
364 // fullscreen element. 415 // fullscreen element.
365 if (!for_cross_process_descendant) { 416 if (!for_cross_process_descendant) {
366 if (document.IsSecureContext()) { 417 if (document.IsSecureContext()) {
367 UseCounter::Count(document, UseCounter::kFullscreenSecureOrigin); 418 UseCounter::Count(document, UseCounter::kFullscreenSecureOrigin);
368 } else { 419 } else {
369 UseCounter::Count(document, UseCounter::kFullscreenInsecureOrigin); 420 UseCounter::Count(document, UseCounter::kFullscreenInsecureOrigin);
370 HostsUsingFeatures::CountAnyWorld( 421 HostsUsingFeatures::CountAnyWorld(
371 document, HostsUsingFeatures::Feature::kFullscreenInsecureHost); 422 document, HostsUsingFeatures::Feature::kFullscreenInsecureHost);
372 } 423 }
373 } 424 }
374 425
375 // Ignore this call if the document is not in a live frame. 426 // 1. Let |pending| be the context object.
376 if (!document.IsActive() || !document.GetFrame()) 427
377 return; 428 // 2. Let |error| be false.
378 429 bool error = false;
379 // If |element| is on top of |doc|'s fullscreen element stack, terminate these 430
380 // substeps. 431 // 3. Let |promise| be a new promise.
381 if (&element == FullscreenElementFrom(document)) 432 // TODO(foolip): Promises. https://crbug.com/644637
382 return; 433
383 434 // 4. If any of the following conditions are false, then set |error| to true:
384 do { 435 //
385 // 1. If any of the following conditions are false, then terminate these 436 // OOPIF: If |requestFullscreen()| was already called in a descendant frame
386 // steps and queue a task to fire an event named fullscreenerror with its 437 // and passed the checks, do not check again here.
387 // bubbles attribute set to true on the context object's node document: 438 if (!for_cross_process_descendant &&
388 439 !RequestFullscreenConditionsMet(pending, document))
389 // |element|'s namespace is the HTML namespace or |element| is an SVG 440 error = true;
390 // svg or MathML math element. 441
391 // Note: MathML is not supported. 442 // 5. Return |promise|, and run the remaining steps in parallel.
392 if (!element.IsHTMLElement() && !isSVGSVGElement(element)) 443 // TODO(foolip): Promises. https://crbug.com/644637
393 break; 444
445 // 6. If |error| is false: Resize pending's top-level browsing context's
446 // document's viewport's dimensions to match the dimensions of the screen of
447 // the output device. Optionally display a message how the end user can
448 // revert this.
449 if (!error) {
450 if (From(document).pending_requests_.size()) {
451 UseCounter::Count(document,
452 UseCounter::kFullscreenRequestWithPendingElement);
453 }
394 454
395 // TODO(foolip): In order to reinstate the hierarchy restrictions in the 455 // TODO(foolip): In order to reinstate the hierarchy restrictions in the
396 // spec, something has to prevent dialog elements from moving within top 456 // spec, something has to prevent dialog elements from moving within top
397 // layer. Either disallowing fullscreen for dialog elements entirely or just 457 // layer. Either disallowing fullscreen for dialog elements entirely or just
398 // preventing dialog elements from simultaneously being fullscreen and modal 458 // preventing dialog elements from simultaneously being fullscreen and modal
399 // are good candidates. See https://github.com/whatwg/fullscreen/pull/91 459 // are good candidates. See https://github.com/whatwg/fullscreen/pull/91
400 if (isHTMLDialogElement(element)) { 460 if (isHTMLDialogElement(pending)) {
401 UseCounter::Count(document, 461 UseCounter::Count(document,
402 UseCounter::kRequestFullscreenForDialogElement); 462 UseCounter::kRequestFullscreenForDialogElement);
403 if (element.IsInTopLayer()) { 463 if (pending.IsInTopLayer()) {
404 UseCounter::Count( 464 UseCounter::Count(
405 document, UseCounter::kRequestFullscreenForDialogElementInTopLayer); 465 document, UseCounter::kRequestFullscreenForDialogElementInTopLayer);
406 } 466 }
407 } 467 }
408 468
409 // The fullscreen element ready check for |element| returns true. 469 From(document).pending_requests_.push_back(
410 if (!FullscreenElementReady(element)) 470 std::make_pair(&pending, request_type));
411 break; 471 LocalFrame& frame = *document.GetFrame();
412 472 frame.GetChromeClient().EnterFullscreen(frame);
413 // Fullscreen is supported. 473 } else {
414 if (!FullscreenIsSupported(document)) 474 EnqueueTaskForRequest(document, pending, request_type, true);
415 break; 475 }
416 476 }
417 // This algorithm is allowed to request fullscreen. 477
418 // OOPIF: If |forCrossProcessDescendant| is true, requestFullscreen was 478 void Fullscreen::DidEnterFullscreen() {
419 // already called on a descendant element in another process, and 479 if (!GetDocument())
420 // getting here means that it was already allowed to request fullscreen. 480 return;
421 if (!for_cross_process_descendant && !AllowedToRequestFullscreen(document)) 481
422 break; 482 ElementStack requests;
423 483 requests.swap(pending_requests_);
424 // 2. Let doc be element's node document. (i.e. "this") 484 for (const ElementStackEntry& request : requests) {
425 485 EnqueueTaskForRequest(*GetDocument(), *request.first, request.second,
426 // 3. Let docs be all doc's ancestor browsing context's documents (if any) 486 false);
427 // and doc. 487 }
428 // 488 }
429 // For OOPIF scenarios, |docs| will only contain documents for local 489
430 // ancestors, and remote ancestors will be processed in their 490 void Fullscreen::EnqueueTaskForRequest(Document& document,
431 // respective processes. This preserves the spec's event firing order 491 Element& pending,
432 // for local ancestors, but not for remote ancestors. However, that 492 RequestType request_type,
433 // difference shouldn't be observable in practice: a fullscreenchange 493 bool error) {
434 // event handler would need to postMessage a frame in another renderer 494 // 7. As part of the next animation frame task, run these substeps:
435 // process, where the message should be queued up and processed after 495 document.EnqueueAnimationFrameTask(
436 // the IPC that dispatches fullscreenchange. 496 WTF::Bind(&RunTaskForRequest, WrapPersistent(&document),
437 HeapDeque<Member<Document>> docs; 497 WrapPersistent(&pending), request_type, error));
438 for (Document* doc = &document; doc; doc = NextLocalAncestor(*doc)) 498 }
439 docs.push_front(doc); 499
440 500 void Fullscreen::RunTaskForRequest(Document* document,
441 // 4. For each document in docs, run these substeps: 501 Element* element,
442 HeapDeque<Member<Document>>::iterator current = docs.begin(), 502 RequestType request_type,
443 following = docs.begin(); 503 bool error) {
444 504 DCHECK(document);
445 do { 505 DCHECK(document->IsActive());
446 ++following; 506 DCHECK(document->GetFrame());
447 507 DCHECK(element);
448 // 1. Let following document be the document after document in docs, or 508
449 // null if there is no such document. 509 Document& pending_doc = *document;
450 Document* current_doc = *current; 510 Element& pending = *element;
451 Document* following_doc = following != docs.end() ? *following : nullptr; 511
452 512 // TODO(foolip): Spec something like: If |pending|'s node document is not
453 // 2. If following document is null, push context object on document's 513 // |pendingDoc|, then set |error| to true.
454 // fullscreen element stack, and queue a task to fire an event named 514 // https://github.com/whatwg/fullscreen/issues/33
455 // fullscreenchange with its bubbles attribute set to true on the 515 if (pending.GetDocument() != pending_doc)
456 // document. 516 error = true;
457 if (!following_doc) { 517
458 From(*current_doc).PushFullscreenElementStack(element, request_type); 518 // 7.1. If either |error| is true or the fullscreen element ready check for
459 From(document).EnqueueChangeEvent(*current_doc, request_type); 519 // |pending| returns false, fire an event named fullscreenerror on
460 continue; 520 // |pending|'s node document, reject |promise| with a TypeError exception,
461 } 521 // and terminate these steps.
462 522 // TODO(foolip): Promises. https://crbug.com/644637
463 // 3. Otherwise, if document's fullscreen element stack is either empty or 523 if (error || !FullscreenElementReady(pending)) {
464 // its top element is not following document's browsing context container, 524 Event* event = CreateErrorEvent(pending_doc, pending, request_type);
465 Element* top_element = FullscreenElementFrom(*current_doc); 525 event->target()->DispatchEvent(event);
466 HTMLFrameOwnerElement* following_owner = 526 return;
467 FindContainerForDescendant(*current_doc, *following_doc); 527 }
468 if (!top_element || top_element != following_owner) { 528
469 // ...push following document's browsing context container on document's 529 // 7.2. Let |fullscreenElements| be an ordered set initially consisting of
470 // fullscreen element stack, and queue a task to fire an event named 530 // |pending|.
471 // fullscreenchange with its bubbles attribute set to true on document. 531 HeapDeque<Member<Element>> fullscreen_elements;
472 From(*current_doc) 532 fullscreen_elements.push_back(pending);
473 .PushFullscreenElementStack(*following_owner, request_type); 533
474 From(document).EnqueueChangeEvent(*current_doc, request_type); 534 // 7.3. While the first element in |fullscreenElements| is in a nested
475 continue; 535 // browsing context, prepend its browsing context container to
476 } 536 // |fullscreenElements|.
477 537 //
478 // 4. Otherwise, do nothing for this document. It stays the same. 538 // OOPIF: |fullscreenElements| will only contain elements for local ancestors,
479 } while (++current != docs.end()); 539 // and remote ancestors will be processed in their respective processes. This
480 540 // preserves the spec's event firing order for local ancestors, but not for
481 From(document).for_cross_process_descendant_ = for_cross_process_descendant; 541 // remote ancestors. However, that difference shouldn't be observable in
482 542 // practice: a fullscreenchange event handler would need to postMessage a
483 // 5. Return, and run the remaining steps asynchronously. 543 // frame in another renderer process, where the message should be queued up
484 // 6. Optionally, perform some animation. 544 // and processed after the IPC that dispatches fullscreenchange.
485 if (From(document).pending_fullscreen_element_) { 545 for (Frame* frame = pending.GetDocument().GetFrame(); frame;
486 UseCounter::Count(document, 546 frame = frame->Tree().Parent()) {
487 UseCounter::kFullscreenRequestWithPendingElement); 547 if (!frame->Owner() || !frame->Owner()->IsLocal())
488 } 548 continue;
489 From(document).pending_fullscreen_element_ = &element; 549 Element* element = ToHTMLFrameOwnerElement(frame->Owner());
490 document.GetFrame()->GetChromeClient().EnterFullscreen( 550 fullscreen_elements.push_front(element);
491 *document.GetFrame()); 551 }
492 552
493 // 7. Optionally, display a message indicating how the user can exit 553 // 7.4. Let |eventDocs| be an empty list.
494 // displaying the context object fullscreen. 554 // Note: For prefixed requests, the event target is an element, so instead
495 return; 555 // let |events| be a list of events to dispatch.
496 } while (false); 556 HeapVector<Member<Event>> events;
497 557
498 From(document).EnqueueErrorEvent(element, request_type); 558 // 7.5. For each |element| in |fullscreenElements|, in order, run these
559 // subsubsteps:
560 for (Element* element : fullscreen_elements) {
561 // 7.5.1. Let |doc| be |element|'s node document.
562 Document& doc = element->GetDocument();
563
564 // 7.5.2. If |element| is |doc|'s fullscreen element, terminate these
565 // subsubsteps.
566 if (element == FullscreenElementFrom(doc))
567 continue;
568
569 // 7.5.3. Otherwise, append |doc| to |eventDocs|.
570 events.push_back(CreateChangeEvent(doc, *element, request_type));
571
572 // 7.5.4. If |element| is |pending| and |pending| is an iframe element,
573 // set |element|'s iframe fullscreen flag.
574 // TODO(foolip): Support the iframe fullscreen flag.
575 // https://crbug.com/644695
576
577 // 7.5.5. Fullscreen |element| within |doc|.
578 // TODO(foolip): Merge fullscreen element stack into top layer.
579 // https://crbug.com/627790
580 From(doc).PushFullscreenElementStack(*element, request_type);
581 }
582
583 // 7.6. For each |doc| in |eventDocs|, in order, fire an event named
584 // fullscreenchange on |doc|.
585 DispatchEvents(events);
586
587 // 7.7. Fulfill |promise| with undefined.
588 // TODO(foolip): Promises. https://crbug.com/644637
499 } 589 }
500 590
501 // https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen 591 // https://fullscreen.spec.whatwg.org/#fully-exit-fullscreen
502 void Fullscreen::FullyExitFullscreen(Document& document) { 592 void Fullscreen::FullyExitFullscreen(Document& document) {
503 // To fully exit fullscreen, run these steps: 593 // 1. If |document|'s fullscreen element is null, terminate these steps.
504 594
505 // 1. Let |doc| be the top-level browsing context's document. 595 // 2. Unfullscreen elements whose fullscreen flag is set, within
596 // |document|'s top layer, except for |document|'s fullscreen element.
597
598 // 3. Exit fullscreen |document|.
599
600 // TODO(foolip): Change the spec. To remove elements from |document|'s top
601 // layer as in step 2 could leave descendant frames in fullscreen. It may work
602 // to give the "exit fullscreen" algorithm a |fully| flag that's used in the
603 // animation frame task after exit. Here, retain the old behavior of fully
604 // exiting fullscreen for the topmost local ancestor:
605 ExitFullscreen(TopmostLocalAncestor(document), ExitType::kFully);
606 }
607
608 // https://fullscreen.spec.whatwg.org/#exit-fullscreen
609 void Fullscreen::ExitFullscreen(Document& doc, ExitType exit_type) {
610 if (!doc.IsActive() || !doc.GetFrame())
611 return;
612
613 // 1. Let |promise| be a new promise.
614 // 2. If |doc|'s fullscreen element is null, reject |promise| with a
615 // TypeError exception, and return |promise|.
616 // TODO(foolip): Promises. https://crbug.com/644637
617 if (!FullscreenElementFrom(doc))
618 return;
619
620 // 3. Let |resize| be false.
621 bool resize = false;
622
623 // 4. Let |docs| be the result of collecting documents to unfullscreen given
624 // |doc|.
625 HeapVector<Member<Document>> docs =
626 CollectDocumentsToUnfullscreen(doc, exit_type);
627
628 // 5. Let |topLevelDoc| be |doc|'s top-level browsing context's document.
506 // 629 //
507 // Since the top-level browsing context's document might be unavailable in 630 // OOPIF: Let |topLevelDoc| be the topmost local ancestor instead. If the main
508 // OOPIF scenarios (i.e., when the top frame is remote), this actually uses 631 // frame is in another process, we will still fully exit fullscreen even
509 // the Document of the topmost local ancestor frame. Without OOPIF, this 632 // though that's wrong if the main frame was in nested fullscreen.
510 // will be the top frame's document. With OOPIF, each renderer process for 633 // TODO(alexmos): Deal with nested fullscreen cases, see
511 // the current page will separately call fullyExitFullscreen to cover all 634 // https://crbug.com/617369.
512 // local frames in each process. 635 Document& top_level_doc = TopmostLocalAncestor(doc);
513 Document& doc = TopmostLocalAncestor(document); 636
514 637 // 6. If |topLevelDoc| is in |docs|, set |resize| to true.
515 // 2. If |doc|'s fullscreen element stack is empty, terminate these steps. 638 if (!docs.IsEmpty() && docs.back() == &top_level_doc)
639 resize = true;
640
641 // 7. Return |promise|, and run the remaining steps in parallel.
642 // TODO(foolip): Promises. https://crbug.com/644637
643
644 // Note: |ExitType::Fully| is only used together with the topmost local
645 // ancestor in |fullyExitFullscreen()|, and so implies that |resize| is true.
646 // This would change if matching the spec for "fully exit fullscreen".
647 if (exit_type == ExitType::kFully)
648 DCHECK(resize);
649
650 // 8. If |resize| is true, resize |topLevelDoc|'s viewport to its "normal"
651 // dimensions.
652 if (resize) {
653 LocalFrame& frame = *doc.GetFrame();
654 frame.GetChromeClient().ExitFullscreen(frame);
655 } else {
656 EnqueueTaskForExit(doc, exit_type);
657 }
658 }
659
660 void Fullscreen::DidExitFullscreen() {
661 if (!GetDocument())
662 return;
663
664 DCHECK_EQ(GetDocument(), &TopmostLocalAncestor(*GetDocument()));
665
666 EnqueueTaskForExit(*GetDocument(), ExitType::kFully);
667 }
668
669 void Fullscreen::EnqueueTaskForExit(Document& document, ExitType exit_type) {
670 // 9. As part of the next animation frame task, run these substeps:
671 document.EnqueueAnimationFrameTask(
672 WTF::Bind(&RunTaskForExit, WrapPersistent(&document), exit_type));
673 }
674
675 void Fullscreen::RunTaskForExit(Document* document, ExitType exit_type) {
676 DCHECK(document);
677 DCHECK(document->IsActive());
678 DCHECK(document->GetFrame());
679
680 Document& doc = *document;
681
516 if (!FullscreenElementFrom(doc)) 682 if (!FullscreenElementFrom(doc))
517 return; 683 return;
518 684
519 // 3. Remove elements from |doc|'s fullscreen element stack until only the top 685 // 9.1. Let |exitDocs| be the result of collecting documents to unfullscreen
520 // element is left. 686 // given |doc|.
521 size_t stack_size = From(doc).fullscreen_element_stack_.size(); 687
522 From(doc).fullscreen_element_stack_.erase(0, stack_size - 1); 688 // 9.2. If |resize| is true and |topLevelDoc| is not in |exitDocs|, fully
523 DCHECK_EQ(From(doc).fullscreen_element_stack_.size(), 1u); 689 // exit fullscreen |topLevelDoc|, reject promise with a TypeError exception,
524 690 // and terminate these steps.
525 // 4. Act as if the exitFullscreen() method was invoked on |doc|. 691
526 ExitFullscreen(doc); 692 // TODO(foolip): See TODO in |fullyExitFullscreen()|. Instead of using "fully
527 } 693 // exit fullscreen" in step 9.2 (which is async), give "exit fullscreen" a
528 694 // |fully| flag which is always true if |resize| was true.
529 // https://fullscreen.spec.whatwg.org/#exit-fullscreen 695
530 void Fullscreen::ExitFullscreen(Document& document) { 696 HeapVector<Member<Document>> exit_docs =
531 // The exitFullscreen() method must run these steps: 697 CollectDocumentsToUnfullscreen(doc, exit_type);
532 698
533 // Ignore this call if the document is not in a live frame. 699 // 9.3. If |exitDocs| is the empty set, append |doc| to |exitDocs|.
534 if (!document.IsActive() || !document.GetFrame()) 700 if (exit_docs.IsEmpty())
535 return; 701 exit_docs.push_back(&doc);
536 702
537 // 1. Let doc be the context object. (i.e. "this") 703 // 9.4. If |exitDocs|'s last document has a browsing context container,
538 // 2. If doc's fullscreen element stack is empty, terminate these steps. 704 // append that browsing context container's node document to |exitDocs|.
539 if (!FullscreenElementFrom(document)) 705 //
540 return; 706 // OOPIF: Skip over remote frames, assuming that they have exactly one element
541 707 // in their fullscreen element stacks, thereby erring on the side of exiting
542 // 3. Let descendants be all the doc's descendant browsing context's documents 708 // fullscreen. TODO(alexmos): Deal with nested fullscreen cases, see
543 // with a non-empty fullscreen element stack (if any), ordered so that the 709 // https://crbug.com/617369.
544 // child of the doc is last and the document furthest away from the doc is 710 if (Document* document = NextLocalAncestor(*exit_docs.back()))
545 // first. 711 exit_docs.push_back(document);
546 HeapDeque<Member<Document>> descendants; 712
547 for (Frame* descendant = document.GetFrame()->Tree().TraverseNext(); 713 // 9.5. Let |descendantDocs| be an ordered set consisting of |doc|'s
548 descendant; descendant = descendant->Tree().TraverseNext()) { 714 // descendant browsing contexts' documents whose fullscreen element is
715 // non-null, if any, in *reverse* tree order.
716 HeapDeque<Member<Document>> descendant_docs;
717 for (Frame* descendant = doc.GetFrame()->Tree().FirstChild(); descendant;
718 descendant = descendant->Tree().TraverseNext(doc.GetFrame())) {
549 if (!descendant->IsLocalFrame()) 719 if (!descendant->IsLocalFrame())
550 continue; 720 continue;
551 DCHECK(ToLocalFrame(descendant)->GetDocument()); 721 DCHECK(ToLocalFrame(descendant)->GetDocument());
552 if (FullscreenElementFrom(*ToLocalFrame(descendant)->GetDocument())) 722 if (FullscreenElementFrom(*ToLocalFrame(descendant)->GetDocument()))
553 descendants.push_front(ToLocalFrame(descendant)->GetDocument()); 723 descendant_docs.push_front(ToLocalFrame(descendant)->GetDocument());
554 } 724 }
555 725
556 // 4. For each descendant in descendants, empty descendant's fullscreen 726 // Note: For prefixed requests, the event target is an element, so let
557 // element stack, and queue a task to fire an event named fullscreenchange 727 // |events| be a list of events to dispatch.
558 // with its bubbles attribute set to true on descendant. 728 HeapVector<Member<Event>> events;
559 for (auto& descendant : descendants) { 729
560 DCHECK(descendant); 730 // 9.6. For each |descendantDoc| in |descendantDocs|, in order, unfullscreen
561 RequestType request_type = 731 // |descendantDoc|.
562 From(*descendant).fullscreen_element_stack_.back().second; 732 for (Document* descendant_doc : descendant_docs) {
563 From(*descendant).ClearFullscreenElementStack(); 733 Fullscreen& fullscreen = From(*descendant_doc);
564 From(document).EnqueueChangeEvent(*descendant, request_type); 734 ElementStack& stack = fullscreen.fullscreen_element_stack_;
565 } 735 DCHECK(!stack.IsEmpty());
566 736 events.push_back(CreateChangeEvent(*descendant_doc, *stack.back().first,
567 // 5. While doc is not null, run these substeps: 737 stack.back().second));
568 Element* new_top = nullptr; 738 while (!stack.IsEmpty())
569 for (Document* current_doc = &document; current_doc;) { 739 fullscreen.PopFullscreenElementStack();
570 RequestType request_type = 740 }
571 From(*current_doc).fullscreen_element_stack_.back().second; 741
572 742 // 9.7. For each |exitDoc| in |exitDocs|, in order, unfullscreen |exitDoc|'s
573 // 1. Pop the top element of doc's fullscreen element stack. 743 // fullscreen element.
574 From(*current_doc).PopFullscreenElementStack(); 744 for (Document* exit_doc : exit_docs) {
575 745 Fullscreen& fullscreen = From(*exit_doc);
576 // If doc's fullscreen element stack is non-empty and the element now at 746 ElementStack& stack = fullscreen.fullscreen_element_stack_;
577 // the top is either not in a document or its node document is not doc, 747 DCHECK(!stack.IsEmpty());
578 // repeat this substep. 748 events.push_back(
579 new_top = FullscreenElementFrom(*current_doc); 749 CreateChangeEvent(*exit_doc, *stack.back().first, stack.back().second));
580 if (new_top && 750 fullscreen.PopFullscreenElementStack();
581 (!new_top->isConnected() || new_top->GetDocument() != current_doc)) 751
582 continue; 752 // TODO(foolip): See TODO in |fullyExitFullscreen()|.
583 753 if (exit_doc == &doc && exit_type == ExitType::kFully) {
584 // 2. Queue a task to fire an event named fullscreenchange with its bubbles 754 while (!stack.IsEmpty())
585 // attribute set to true on doc. 755 fullscreen.PopFullscreenElementStack();
586 From(document).EnqueueChangeEvent(*current_doc, request_type);
587
588 // 3. If doc's fullscreen element stack is empty and doc's browsing context
589 // has a browsing context container, set doc to that browsing context
590 // container's node document.
591 //
592 // OOPIF: If browsing context container's document is in another
593 // process, keep moving up the ancestor chain and looking for a
594 // browsing context container with a local document.
595 // TODO(alexmos): Deal with nested fullscreen cases, see
596 // https://crbug.com/617369.
597 if (!new_top) {
598 current_doc = NextLocalAncestor(*current_doc);
599 continue;
600 } 756 }
601 757 }
602 // 4. Otherwise, set doc to null. 758
603 current_doc = nullptr; 759 // 9.8. For each |descendantDoc| in |descendantDocs|, in order, fire an
604 } 760 // event named fullscreenchange on |descendantDoc|.
605 761 // 9.9. For each |exitDoc| in |exitDocs|, in order, fire an event named
606 // 6. Return, and run the remaining steps asynchronously. 762 // fullscreenchange on |exitDoc|.
607 // 7. Optionally, perform some animation. 763 DispatchEvents(events);
608 764
609 // Only exit fullscreen mode if the fullscreen element stack is empty. 765 // 9.10. Fulfill |promise| with undefined.
610 if (!new_top) { 766 // TODO(foolip): Promises. https://crbug.com/644637
611 document.GetFrame()->GetChromeClient().ExitFullscreen(*document.GetFrame());
612 return;
613 }
614
615 // Otherwise, enter fullscreen for the fullscreen element stack's top element.
616 From(document).pending_fullscreen_element_ = new_top;
617 From(document).DidEnterFullscreen();
618 } 767 }
619 768
620 // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled 769 // https://fullscreen.spec.whatwg.org/#dom-document-fullscreenenabled
621 bool Fullscreen::FullscreenEnabled(Document& document) { 770 bool Fullscreen::FullscreenEnabled(Document& document) {
622 // The fullscreenEnabled attribute's getter must return true if the context 771 // The fullscreenEnabled attribute's getter must return true if the context
623 // object is allowed to use the feature indicated by attribute name 772 // object is allowed to use the feature indicated by attribute name
624 // allowfullscreen and fullscreen is supported, and false otherwise. 773 // allowfullscreen and fullscreen is supported, and false otherwise.
625 return AllowedToUseFullscreen(document.GetFrame()) && 774 return AllowedToUseFullscreen(document.GetFrame()) &&
626 FullscreenIsSupported(document); 775 FullscreenIsSupported(document);
627 } 776 }
628 777
629 void Fullscreen::DidEnterFullscreen() {
630 if (!GetDocument()->IsActive() || !GetDocument()->GetFrame())
631 return;
632
633 // Start the timer for events enqueued by |requestFullscreen()|. The hover
634 // state update is scheduled first so that it's done when the events fire.
635 GetDocument()->GetFrame()->GetEventHandler().ScheduleHoverStateUpdate();
636 event_queue_timer_.StartOneShot(0, BLINK_FROM_HERE);
637
638 Element* element = pending_fullscreen_element_.Release();
639 if (!element)
640 return;
641
642 if (current_full_screen_element_ == element)
643 return;
644
645 if (!element->isConnected() || &element->GetDocument() != GetDocument()) {
646 // The element was removed or has moved to another document since the
647 // |requestFullscreen()| call. Exit fullscreen again to recover.
648 // TODO(foolip): Fire a fullscreenerror event. This is currently difficult
649 // because the fullscreenchange event has already been enqueued and possibly
650 // even fired. https://crbug.com/402376
651 LocalFrame& frame = *GetDocument()->GetFrame();
652 frame.GetChromeClient().ExitFullscreen(frame);
653 return;
654 }
655
656 if (full_screen_layout_object_)
657 full_screen_layout_object_->UnwrapLayoutObject();
658
659 Element* previous_element = current_full_screen_element_;
660 current_full_screen_element_ = element;
661
662 // Create a placeholder block for a the full-screen element, to keep the page
663 // from reflowing when the element is removed from the normal flow. Only do
664 // this for a LayoutBox, as only a box will have a frameRect. The placeholder
665 // will be created in setFullScreenLayoutObject() during layout.
666 LayoutObject* layout_object = current_full_screen_element_->GetLayoutObject();
667 bool should_create_placeholder = layout_object && layout_object->IsBox();
668 if (should_create_placeholder) {
669 saved_placeholder_frame_rect_ = ToLayoutBox(layout_object)->FrameRect();
670 saved_placeholder_computed_style_ =
671 ComputedStyle::Clone(layout_object->StyleRef());
672 }
673
674 // TODO(alexmos): When |m_forCrossProcessDescendant| is true, some of
675 // this layout work has already been done in another process, so it should
676 // not be necessary to repeat it here.
677 if (current_full_screen_element_ != GetDocument()->documentElement()) {
678 LayoutFullScreen::WrapLayoutObject(
679 layout_object, layout_object ? layout_object->Parent() : 0,
680 GetDocument());
681 }
682
683 // When |m_forCrossProcessDescendant| is true, m_currentFullScreenElement
684 // corresponds to the HTMLFrameOwnerElement for the out-of-process iframe
685 // that contains the actual fullscreen element. Hence, it must also set
686 // the ContainsFullScreenElement flag (so that it gains the
687 // -webkit-full-screen-ancestor style).
688 if (for_cross_process_descendant_) {
689 DCHECK(current_full_screen_element_->IsFrameOwnerElement());
690 DCHECK(ToHTMLFrameOwnerElement(current_full_screen_element_)
691 ->ContentFrame()
692 ->IsRemoteFrame());
693 current_full_screen_element_->SetContainsFullScreenElement(true);
694 }
695
696 current_full_screen_element_
697 ->SetContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(true);
698
699 GetDocument()->GetStyleEngine().EnsureUAStyleForFullscreen();
700 current_full_screen_element_->PseudoStateChanged(
701 CSSSelector::kPseudoFullScreen);
702
703 // FIXME: This should not call updateStyleAndLayoutTree.
704 GetDocument()->UpdateStyleAndLayoutTree();
705
706 GetDocument()->GetFrame()->GetChromeClient().FullscreenElementChanged(
707 previous_element, element);
708 }
709
710 void Fullscreen::DidExitFullscreen() {
711 if (!GetDocument()->IsActive() || !GetDocument()->GetFrame())
712 return;
713
714 // Start the timer for events enqueued by |exitFullscreen()|. The hover state
715 // update is scheduled first so that it's done when the events fire.
716 GetDocument()->GetFrame()->GetEventHandler().ScheduleHoverStateUpdate();
717 event_queue_timer_.StartOneShot(0, BLINK_FROM_HERE);
718
719 // If fullscreen was canceled by the browser, e.g. if the user pressed Esc,
720 // then |exitFullscreen()| was never called. Let |fullyExitFullscreen()| clear
721 // the fullscreen element stack and fire any events as necessary.
722 // TODO(foolip): Remove this when state changes and events are synchronized
723 // with animation frames. https://crbug.com/402376
724 FullyExitFullscreen(*GetDocument());
725
726 if (!current_full_screen_element_)
727 return;
728
729 if (for_cross_process_descendant_)
730 current_full_screen_element_->SetContainsFullScreenElement(false);
731
732 current_full_screen_element_
733 ->SetContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
734
735 if (full_screen_layout_object_)
736 LayoutFullScreenItem(full_screen_layout_object_).UnwrapLayoutObject();
737
738 GetDocument()->GetStyleEngine().EnsureUAStyleForFullscreen();
739 current_full_screen_element_->PseudoStateChanged(
740 CSSSelector::kPseudoFullScreen);
741 Element* previous_element = current_full_screen_element_;
742 current_full_screen_element_ = nullptr;
743
744 for_cross_process_descendant_ = false;
745
746 GetDocument()->GetFrame()->GetChromeClient().FullscreenElementChanged(
747 previous_element, nullptr);
748 }
749
750 void Fullscreen::SetFullScreenLayoutObject(LayoutFullScreen* layout_object) { 778 void Fullscreen::SetFullScreenLayoutObject(LayoutFullScreen* layout_object) {
751 if (layout_object == full_screen_layout_object_) 779 if (layout_object == full_screen_layout_object_)
752 return; 780 return;
753 781
754 if (layout_object && saved_placeholder_computed_style_) { 782 if (layout_object && saved_placeholder_computed_style_) {
755 layout_object->CreatePlaceholder( 783 layout_object->CreatePlaceholder(
756 std::move(saved_placeholder_computed_style_), 784 std::move(saved_placeholder_computed_style_),
757 saved_placeholder_frame_rect_); 785 saved_placeholder_frame_rect_);
758 } else if (layout_object && full_screen_layout_object_ && 786 } else if (layout_object && full_screen_layout_object_ &&
759 full_screen_layout_object_->Placeholder()) { 787 full_screen_layout_object_->Placeholder()) {
760 LayoutBlockFlow* placeholder = full_screen_layout_object_->Placeholder(); 788 LayoutBlockFlow* placeholder = full_screen_layout_object_->Placeholder();
761 layout_object->CreatePlaceholder( 789 layout_object->CreatePlaceholder(
762 ComputedStyle::Clone(placeholder->StyleRef()), 790 ComputedStyle::Clone(placeholder->StyleRef()),
763 placeholder->FrameRect()); 791 placeholder->FrameRect());
764 } 792 }
765 793
766 if (full_screen_layout_object_) 794 if (full_screen_layout_object_)
767 full_screen_layout_object_->UnwrapLayoutObject(); 795 full_screen_layout_object_->UnwrapLayoutObject();
768 DCHECK(!full_screen_layout_object_); 796 DCHECK(!full_screen_layout_object_);
769 797
770 full_screen_layout_object_ = layout_object; 798 full_screen_layout_object_ = layout_object;
771 } 799 }
772 800
773 void Fullscreen::FullScreenLayoutObjectDestroyed() { 801 void Fullscreen::FullScreenLayoutObjectDestroyed() {
774 full_screen_layout_object_ = nullptr; 802 full_screen_layout_object_ = nullptr;
775 } 803 }
776 804
777 void Fullscreen::EnqueueChangeEvent(Document& document, 805 void Fullscreen::ElementRemoved(Element& node) {
778 RequestType request_type) { 806 DCHECK_EQ(GetDocument(), &node.GetDocument());
779 Event* event;
780 if (request_type == RequestType::kUnprefixed) {
781 event = CreateEvent(EventTypeNames::fullscreenchange, document);
782 } else {
783 DCHECK(document.HasFullscreenSupplement());
784 Fullscreen& fullscreen = From(document);
785 EventTarget* target = fullscreen.FullscreenElement();
786 if (!target)
787 target = fullscreen.CurrentFullScreenElement();
788 if (!target)
789 target = &document;
790 event = CreateEvent(EventTypeNames::webkitfullscreenchange, *target);
791 }
792 event_queue_.push_back(event);
793 // NOTE: The timer is started in didEnterFullscreen/didExitFullscreen.
794 }
795 807
796 void Fullscreen::EnqueueErrorEvent(Element& element, RequestType request_type) {
797 Event* event;
798 if (request_type == RequestType::kUnprefixed)
799 event = CreateEvent(EventTypeNames::fullscreenerror, element.GetDocument());
800 else
801 event = CreateEvent(EventTypeNames::webkitfullscreenerror, element);
802 event_queue_.push_back(event);
803 event_queue_timer_.StartOneShot(0, BLINK_FROM_HERE);
804 }
805
806 void Fullscreen::EventQueueTimerFired(TimerBase*) {
807 HeapDeque<Member<Event>> event_queue;
808 event_queue_.Swap(event_queue);
809
810 while (!event_queue.IsEmpty()) {
811 Event* event = event_queue.TakeFirst();
812 Node* target = event->target()->ToNode();
813
814 // If the element was removed from our tree, also message the
815 // documentElement.
816 if (!target->isConnected() && GetDocument()->documentElement()) {
817 DCHECK(IsPrefixed(event->type()));
818 event_queue.push_back(
819 CreateEvent(event->type(), *GetDocument()->documentElement()));
820 }
821
822 target->DispatchEvent(event);
823 }
824 }
825
826 void Fullscreen::ElementRemoved(Element& node) {
827 // |Fullscreen::ElementRemoved| is called for each removed element, so only 808 // |Fullscreen::ElementRemoved| is called for each removed element, so only
828 // the body of the spec "removing steps" loop appears here: 809 // the body of the spec "removing steps" loop appears here:
829 810
830 // 2.1. If |node| is its node document's fullscreen element, exit fullscreen 811 // 2.1. If |node| is its node document's fullscreen element, exit fullscreen
831 // that document. 812 // that document.
832 if (FullscreenElement() == &node) { 813 if (FullscreenElement() == &node) {
833 ExitFullscreen(node.GetDocument()); 814 ExitFullscreen(node.GetDocument());
834 return; 815 return;
835 } 816 }
836 817
837 // 2.2. Otherwise, unfullscreen |node| within its node document. 818 // 2.2. Otherwise, unfullscreen |node| within its node document.
838 for (size_t i = 0; i < fullscreen_element_stack_.size(); ++i) { 819 for (size_t i = 0; i < fullscreen_element_stack_.size(); ++i) {
839 if (fullscreen_element_stack_[i].first.Get() == &node) { 820 if (fullscreen_element_stack_[i].first.Get() == &node) {
840 fullscreen_element_stack_.erase(i); 821 fullscreen_element_stack_.erase(i);
841 return; 822 return;
842 } 823 }
843 } 824 }
844 825
845 // Note: |node| was not in the fullscreen element stack. 826 // Note: |node| was not in the fullscreen element stack.
846 } 827 }
847 828
848 void Fullscreen::ClearFullscreenElementStack() { 829 void Fullscreen::PopFullscreenElementStack() {
849 if (fullscreen_element_stack_.IsEmpty()) 830 DCHECK(!fullscreen_element_stack_.IsEmpty());
850 return;
851 831
852 fullscreen_element_stack_.clear(); 832 Element* previous_element = FullscreenElement();
853
854 SetNeedsPaintPropertyUpdate(GetDocument());
855 }
856
857 void Fullscreen::PopFullscreenElementStack() {
858 if (fullscreen_element_stack_.IsEmpty())
859 return;
860
861 fullscreen_element_stack_.pop_back(); 833 fullscreen_element_stack_.pop_back();
862 834
863 SetNeedsPaintPropertyUpdate(GetDocument()); 835 // Note: |requestType| is only used if |fullscreenElement()| is non-null.
836 RequestType request_type = fullscreen_element_stack_.IsEmpty()
837 ? RequestType::kUnprefixed
838 : fullscreen_element_stack_.back().second;
839 FullscreenElementChanged(previous_element, FullscreenElement(), request_type);
864 } 840 }
865 841
866 void Fullscreen::PushFullscreenElementStack(Element& element, 842 void Fullscreen::PushFullscreenElementStack(Element& element,
867 RequestType request_type) { 843 RequestType request_type) {
844 Element* previous_element = FullscreenElement();
868 fullscreen_element_stack_.push_back(std::make_pair(&element, request_type)); 845 fullscreen_element_stack_.push_back(std::make_pair(&element, request_type));
869 846
870 SetNeedsPaintPropertyUpdate(GetDocument()); 847 FullscreenElementChanged(previous_element, &element, request_type);
848 }
849
850 void Fullscreen::FullscreenElementChanged(Element* from_element,
851 Element* to_element,
852 RequestType to_request_type) {
853 DCHECK_NE(from_element, to_element);
854
855 if (!GetDocument())
856 return;
857
858 GetDocument()->GetStyleEngine().EnsureUAStyleForFullscreen();
859
860 if (full_screen_layout_object_)
861 full_screen_layout_object_->UnwrapLayoutObject();
862 DCHECK(!full_screen_layout_object_);
863
864 if (from_element) {
865 DCHECK_NE(from_element, FullscreenElement());
866
867 from_element->PseudoStateChanged(CSSSelector::kPseudoFullScreen);
868
869 from_element->SetContainsFullScreenElement(false);
870 from_element
871 ->SetContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(false);
872 }
873
874 if (to_element) {
875 DCHECK_EQ(to_element, FullscreenElement());
876
877 to_element->PseudoStateChanged(CSSSelector::kPseudoFullScreen);
878
879 // OOPIF: For RequestType::PrefixedForCrossProcessDescendant, |toElement| is
880 // the iframe element for the out-of-process frame that contains the
881 // fullscreen element. Hence, it must match :-webkit-full-screen-ancestor.
882 if (to_request_type == RequestType::kPrefixedForCrossProcessDescendant) {
883 DCHECK(isHTMLIFrameElement(to_element));
884 to_element->SetContainsFullScreenElement(true);
885 }
886 to_element->SetContainsFullScreenElementOnAncestorsCrossingFrameBoundaries(
887 true);
888
889 // Create a placeholder block for the fullscreen element, to keep the page
890 // from reflowing when the element is removed from the normal flow. Only do
891 // this for a LayoutBox, as only a box will have a frameRect. The
892 // placeholder will be created in setFullScreenLayoutObject() during layout.
893 LayoutObject* layout_object = to_element->GetLayoutObject();
894 bool should_create_placeholder = layout_object && layout_object->IsBox();
895 if (should_create_placeholder) {
896 saved_placeholder_frame_rect_ = ToLayoutBox(layout_object)->FrameRect();
897 saved_placeholder_computed_style_ =
898 ComputedStyle::Clone(layout_object->StyleRef());
899 }
900
901 if (to_element != GetDocument()->documentElement()) {
902 LayoutFullScreen::WrapLayoutObject(
903 layout_object, layout_object ? layout_object->Parent() : 0,
904 GetDocument());
905 }
906 }
907
908 if (LocalFrame* frame = GetDocument()->GetFrame()) {
909 // TODO(foolip): Synchronize hover state changes with animation frames.
910 // https://crbug.com/668758
911 frame->GetEventHandler().ScheduleHoverStateUpdate();
912 frame->GetChromeClient().FullscreenElementChanged(from_element, to_element);
913
914 if (RuntimeEnabledFeatures::slimmingPaintInvalidationEnabled() &&
915 !RuntimeEnabledFeatures::rootLayerScrollingEnabled()) {
916 // Fullscreen status affects scroll paint properties through
917 // LocalFrameView::UserInputScrollable().
918 if (LocalFrameView* frame_view = frame->View())
919 frame_view->SetNeedsPaintPropertyUpdate();
920 }
921 }
922
923 // TODO(foolip): This should not call updateStyleAndLayoutTree.
924 GetDocument()->UpdateStyleAndLayoutTree();
871 } 925 }
872 926
873 DEFINE_TRACE(Fullscreen) { 927 DEFINE_TRACE(Fullscreen) {
874 visitor->Trace(pending_fullscreen_element_); 928 visitor->Trace(pending_requests_);
875 visitor->Trace(fullscreen_element_stack_); 929 visitor->Trace(fullscreen_element_stack_);
876 visitor->Trace(current_full_screen_element_);
877 visitor->Trace(event_queue_);
878 Supplement<Document>::Trace(visitor); 930 Supplement<Document>::Trace(visitor);
879 ContextLifecycleObserver::Trace(visitor); 931 ContextLifecycleObserver::Trace(visitor);
880 } 932 }
881 933
882 } // namespace blink 934 } // 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