Bug 1375319 part 3 - Try dispatching fullscreen events to element first rather than document. r=smaug

Some steps in file_fullscreen-api.html are adjusted in order to test
the behavior that the event is correctly dispatched to the document
when element is disconnected.

Depends on D5415

Differential Revision: https://phabricator.services.mozilla.com/D5416

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Xidorn Quan 2018-09-14 00:06:16 +00:00
Родитель 6239a69055
Коммит abdaa90858
9 изменённых файлов: 72 добавлений и 68 удалений

Просмотреть файл

@ -3580,7 +3580,7 @@ Element::RequestFullscreen(CallerType aCallerType, ErrorResult& aError)
// Note that requests for fullscreen inside a web app's origin are exempt
// from this restriction.
if (const char* error = GetFullscreenError(aCallerType)) {
OwnerDoc()->DispatchFullscreenError(error);
OwnerDoc()->DispatchFullscreenError(error, this);
return;
}

Просмотреть файл

@ -8562,11 +8562,11 @@ NotifyPageHide(nsIDocument* aDocument, void* aData)
}
static void
DispatchFullscreenChange(nsIDocument* aTarget)
DispatchFullscreenChange(nsIDocument* aDocument, nsINode* aTarget)
{
if (nsPresContext* presContext = aTarget->GetPresContext()) {
auto pendingEvent =
MakeUnique<PendingFullscreenEvent>(FullscreenEventType::Change, aTarget);
if (nsPresContext* presContext = aDocument->GetPresContext()) {
auto pendingEvent = MakeUnique<PendingFullscreenEvent>(
FullscreenEventType::Change, aDocument, aTarget);
presContext->RefreshDriver()->
ScheduleFullscreenEvent(std::move(pendingEvent));
}
@ -10716,13 +10716,13 @@ GetFullscreenLeaf(nsIDocument* aDoc)
static bool
ResetFullscreen(nsIDocument* aDocument, void* aData)
{
if (aDocument->FullscreenStackTop()) {
if (Element* fsElement = aDocument->FullscreenStackTop()) {
NS_ASSERTION(CountFullscreenSubDocuments(aDocument) <= 1,
"Should have at most 1 fullscreen subdocument.");
aDocument->CleanupFullscreenState();
NS_ASSERTION(!aDocument->FullscreenStackTop(),
"Should reset fullscreen");
DispatchFullscreenChange(aDocument);
DispatchFullscreenChange(aDocument, fsElement);
aDocument->EnumerateSubDocuments(ResetFullscreen, nullptr);
}
return true;
@ -10823,35 +10823,38 @@ nsIDocument::RestorePreviousFullscreenState()
}
nsCOMPtr<nsIDocument> fullScreenDoc = GetFullscreenLeaf(this);
AutoTArray<nsIDocument*, 8> exitDocs;
AutoTArray<Element*, 8> exitElements;
nsIDocument* doc = fullScreenDoc;
// Collect all subdocuments.
for (; doc != this; doc = doc->GetParentDocument()) {
exitDocs.AppendElement(doc);
Element* fsElement = doc->FullscreenStackTop();
MOZ_ASSERT(fsElement, "Parent document of "
"a fullscreen document without fullscreen element?");
exitElements.AppendElement(fsElement);
}
MOZ_ASSERT(doc == this, "Must have reached this doc");
// Collect all ancestor documents which we are going to change.
for (; doc; doc = doc->GetParentDocument()) {
MOZ_ASSERT(!doc->mFullscreenStack.IsEmpty(),
"Ancestor of fullscreen document must also be in fullscreen");
Element* fsElement = doc->FullscreenStackTop();
if (doc != this) {
Element* top = doc->FullscreenStackTop();
if (top->IsHTMLElement(nsGkAtoms::iframe)) {
if (static_cast<HTMLIFrameElement*>(top)->FullscreenFlag()) {
if (auto* iframe = HTMLIFrameElement::FromNode(fsElement)) {
if (iframe->FullscreenFlag()) {
// If this is an iframe, and it explicitly requested
// fullscreen, don't rollback it automatically.
break;
}
}
}
exitDocs.AppendElement(doc);
exitElements.AppendElement(fsElement);
if (doc->mFullscreenStack.Length() > 1) {
break;
}
}
nsIDocument* lastDoc = exitDocs.LastElement();
nsIDocument* lastDoc = exitElements.LastElement()->OwnerDoc();
if (!lastDoc->GetParentDocument() &&
lastDoc->mFullscreenStack.Length() == 1) {
// If we are fully exiting fullscreen, don't touch anything here,
@ -10864,8 +10867,8 @@ nsIDocument::RestorePreviousFullscreenState()
UnlockPointer();
// All documents listed in the array except the last one are going to
// completely exit from the fullscreen state.
for (auto i : IntegerRange(exitDocs.Length() - 1)) {
exitDocs[i]->CleanupFullscreenState();
for (auto i : IntegerRange(exitElements.Length() - 1)) {
exitElements[i]->OwnerDoc()->CleanupFullscreenState();
}
// The last document will either rollback one fullscreen element, or
// completely exit from the fullscreen state as well.
@ -10880,8 +10883,8 @@ nsIDocument::RestorePreviousFullscreenState()
// Dispatch the fullscreenchange event to all document listed. Note
// that the loop order is reversed so that events are dispatched in
// the tree order as indicated in the spec.
for (nsIDocument* d : Reversed(exitDocs)) {
DispatchFullscreenChange(d);
for (Element* e : Reversed(exitElements)) {
DispatchFullscreenChange(e->OwnerDoc(), e);
}
MOZ_ASSERT(newFullscreenDoc, "If we were going to exit from fullscreen on "
@ -10932,11 +10935,11 @@ nsIDocument::AsyncRequestFullscreen(UniquePtr<FullscreenRequest> aRequest)
}
void
nsIDocument::DispatchFullscreenError(const char* aMessage)
nsIDocument::DispatchFullscreenError(const char* aMessage, nsINode* aTarget)
{
if (nsPresContext* presContext = GetPresContext()) {
auto pendingEvent =
MakeUnique<PendingFullscreenEvent>(FullscreenEventType::Error, this);
auto pendingEvent = MakeUnique<PendingFullscreenEvent>(
FullscreenEventType::Error, this, aTarget);
presContext->RefreshDriver()->
ScheduleFullscreenEvent(std::move(pendingEvent));
}
@ -11171,27 +11174,27 @@ nsIDocument::FullscreenElementReadyCheck(Element* aElement,
return false;
}
if (!aElement->IsInComposedDoc()) {
DispatchFullscreenError("FullscreenDeniedNotInDocument");
DispatchFullscreenError("FullscreenDeniedNotInDocument", aElement);
return false;
}
if (aElement->OwnerDoc() != this) {
DispatchFullscreenError("FullscreenDeniedMovedDocument");
DispatchFullscreenError("FullscreenDeniedMovedDocument", aElement);
return false;
}
if (!GetWindow()) {
DispatchFullscreenError("FullscreenDeniedLostWindow");
DispatchFullscreenError("FullscreenDeniedLostWindow", aElement);
return false;
}
if (const char* msg = GetFullscreenError(this, aCallerType)) {
DispatchFullscreenError(msg);
DispatchFullscreenError(msg, aElement);
return false;
}
if (!IsVisible()) {
DispatchFullscreenError("FullscreenDeniedHidden");
DispatchFullscreenError("FullscreenDeniedHidden", aElement);
return false;
}
if (HasFullscreenSubDocument(this)) {
DispatchFullscreenError("FullscreenDeniedSubDocFullScreen");
DispatchFullscreenError("FullscreenDeniedSubDocFullScreen", aElement);
return false;
}
//XXXsmaug Note, we don't follow the latest fullscreen spec here.
@ -11201,11 +11204,11 @@ nsIDocument::FullscreenElementReadyCheck(Element* aElement,
FullscreenStackTop())) {
// If this document is fullscreen, only grant fullscreen requests from
// a descendant of the current fullscreen element.
DispatchFullscreenError("FullscreenDeniedNotDescendant");
DispatchFullscreenError("FullscreenDeniedNotDescendant", aElement);
return false;
}
if (!nsContentUtils::IsChromeDoc(this) && !IsInActiveTab(this)) {
DispatchFullscreenError("FullscreenDeniedNotFocusedTab");
DispatchFullscreenError("FullscreenDeniedNotFocusedTab", aElement);
return false;
}
// Deny requests when a windowed plugin is focused.
@ -11215,7 +11218,7 @@ nsIDocument::FullscreenElementReadyCheck(Element* aElement,
return false;
}
if (nsContentUtils::HasPluginWithUncontrolledEventDispatch(fm->GetFocusedElement())) {
DispatchFullscreenError("FullscreenDeniedFocusedPlugin");
DispatchFullscreenError("FullscreenDeniedFocusedPlugin", aElement);
return false;
}
return true;
@ -11395,7 +11398,7 @@ nsIDocument::RequestFullscreen(UniquePtr<FullscreenRequest> aRequest)
if (!elem->IsHTMLElement() && !elem->IsXULElement() &&
!elem->IsSVGElement(nsGkAtoms::svg) &&
!elem->IsMathMLElement(nsGkAtoms::math)) {
DispatchFullscreenError("FullscreenDeniedNotHTMLSVGOrMathML");
DispatchFullscreenError("FullscreenDeniedNotHTMLSVGOrMathML", elem);
return;
}
@ -11538,7 +11541,7 @@ nsIDocument::ApplyFullscreen(const FullscreenRequest& aRequest)
// reversed so that events are dispatched in the tree order as
// indicated in the spec.
for (nsIDocument* d : Reversed(changed)) {
DispatchFullscreenChange(d);
DispatchFullscreenChange(d, d->FullscreenStackTop());
}
return true;
}

Просмотреть файл

@ -1858,7 +1858,7 @@ public:
* Dispatch fullscreenerror event and report the failure message to
* the console.
*/
void DispatchFullscreenError(const char* aMessage);
void DispatchFullscreenError(const char* aMessage, nsINode* aTarget);
void RequestPointerLock(Element* aElement, mozilla::dom::CallerType);
bool SetPointerLock(Element* aElement, int aCursorStyle);

Просмотреть файл

@ -26,11 +26,15 @@ enum class FullscreenEventType
class PendingFullscreenEvent
{
public:
PendingFullscreenEvent(FullscreenEventType aType, nsIDocument* aDoc)
: mDocument(aDoc)
PendingFullscreenEvent(FullscreenEventType aType,
nsIDocument* aDocument,
nsINode* aTarget)
: mDocument(aDocument)
, mTarget(aTarget)
, mType(aType)
{
MOZ_ASSERT(aDoc);
MOZ_ASSERT(aDocument);
MOZ_ASSERT(aTarget);
}
nsIDocument* Document() const { return mDocument; }
@ -50,13 +54,16 @@ public:
name = NS_LITERAL_STRING("fullscreenerror");
break;
}
nsINode* target =
mTarget->GetComposedDoc() == mDocument ? mTarget : mDocument;
Unused << nsContentUtils::DispatchTrustedEvent(
mDocument, mDocument, name,
mDocument, target, name,
CanBubble::eYes, Cancelable::eNo, Composed::eYes);
}
private:
nsCOMPtr<nsIDocument> mDocument;
nsCOMPtr<nsINode> mTarget;
FullscreenEventType mType;
#ifdef DEBUG
bool mDispatched = false;

Просмотреть файл

@ -54,7 +54,8 @@ function sendMouseClick(element) {
const FULLSCREEN_ELEMENT = document.getElementById("fullscreen-element");
function enter1(event) {
is(event.target, document, "Event target should be full-screen document #1");
is(event.target, FULLSCREEN_ELEMENT,
"Event target should be the fullscreen element #1");
ok(document.fullscreen, "Document should be in fullscreen");
is(document.fullscreenElement, FULLSCREEN_ELEMENT,
"Full-screen element should be div element.");
@ -64,13 +65,13 @@ function enter1(event) {
FULLSCREEN_ELEMENT.remove();
is(document.fullscreenElement, null,
"Full-screen element should be null after removing.");
document.body.appendChild(FULLSCREEN_ELEMENT);
is(document.fullscreenElement, null,
"Full-screen element should still be null after re-adding former FSE.");
}
function exit1(event) {
is(event.target, document, "Event target should be full-screen document #2");
document.body.appendChild(FULLSCREEN_ELEMENT);
is(document.fullscreenElement, null,
"Full-screen element should still be null after re-adding former FSE.");
is(event.target, document, "Event target should be the document #2");
ok(!document.fullscreen, "Document should not be in fullscreen");
is(document.fullscreenElement, null, "Full-screen element should be null.");
iframe = document.createElement("iframe");
@ -81,7 +82,8 @@ function exit1(event) {
}
function enter2(event) {
is(event.target, document, "Event target should be full-screen document #3");
is(event.target, iframe,
"Event target should be the fullscreen iframe #3");
is(document.fullscreenElement, iframe,
"Full-screen element should be iframe element.");
is(iframe.contentDocument.fullscreenElement, iframe.contentDocument.body,
@ -105,7 +107,8 @@ function exit2(event) {
}
function enter3(event) {
is(event.target, document, "Event target should be full-screen document #3");
is(event.target, FULLSCREEN_ELEMENT,
"Event target should be the fullscreen element #3");
is(document.fullscreenElement, FULLSCREEN_ELEMENT,
"Full-screen element should be div.");
@ -119,12 +122,11 @@ function enter3(event) {
"Full-screen element in outer frame should be null.");
is(_innerFrame.contentDocument.fullscreenElement, null,
"Full-screen element in inner frame should be null.");
document.body.appendChild(FULLSCREEN_ELEMENT);
}
function exit3(event) {
is(event.target, document, "Event target should be full-screen document #4");
document.body.appendChild(FULLSCREEN_ELEMENT);
is(event.target, document, "Event target should be the document #3");
is(document.fullscreenElement, null, "Full-screen element should be null.");
document.body.removeChild(iframe);
iframe = null;
@ -149,7 +151,8 @@ function error1(event) {
}
function enter4(event) {
is(event.target, document, "Event target should be full-screen document #5");
is(event.target, inDocElement,
"Event target should be the fullscreen element #4");
is(document.fullscreenElement, inDocElement, "FSE should be inDocElement.");
// Remove full-screen ancestor element from document, verify it stops being reported as current FSE.

Просмотреть файл

@ -12,10 +12,11 @@ function is(a, b, msg) {
}
let fullscreenEvents = [];
let iframeDoc;
let iframe, iframeDoc;
function begin() {
iframeDoc = document.querySelector("iframe").contentDocument;
iframe = document.querySelector("iframe");
iframeDoc = iframe.contentDocument;
document.addEventListener("fullscreenchange", evt => {
fullscreenEvents.push(evt);
});
@ -29,9 +30,9 @@ function begin() {
function assertFullscreenEvents(action) {
is(fullscreenEvents.length, 2,
"Two documents should have event dispatched for " + action);
is(fullscreenEvents[0].target, document,
is(fullscreenEvents[0].target, iframe,
"Root document should have the event dispatched first after " + action);
is(fullscreenEvents[1].target, iframeDoc,
is(fullscreenEvents[1].target, iframeDoc.body,
"Inner document should have the event dispatched second after " + action);
}

Просмотреть файл

@ -265,6 +265,12 @@ partial interface Element {
void requestFullscreen();
[Throws, BinaryName="requestFullscreen", NeedsCallerType]
void mozRequestFullScreen();
// Events handlers
[Func="nsDocument::IsUnprefixedFullscreenEnabled"]
attribute EventHandler onfullscreenchange;
[Func="nsDocument::IsUnprefixedFullscreenEnabled"]
attribute EventHandler onfullscreenerror;
};
// https://w3c.github.io/pointerlock/#extensions-to-the-element-interface

Просмотреть файл

@ -1,4 +0,0 @@
[element-request-fullscreen-not-allowed.html]
[Element#requestFullscreen() when not allowed to request fullscreen]
expected: FAIL

Просмотреть файл

@ -5,15 +5,3 @@
[Document interface: operation exitFullscreen()]
expected: FAIL
[Element interface: document.createElementNS(null, "test") must inherit property "onfullscreenerror" with the proper type]
expected: FAIL
[Element interface: attribute onfullscreenerror]
expected: FAIL
[Element interface: document.createElementNS(null, "test") must inherit property "onfullscreenchange" with the proper type]
expected: FAIL
[Element interface: attribute onfullscreenchange]
expected: FAIL