Bug 704039 part 2 - Implement full-screen mode rollback stack. r=bz

This commit is contained in:
Chris Pearce 2011-12-07 10:59:39 +13:00
parent 1279d53503
commit c2405549e1
20 changed files with 571 additions and 223 deletions

View File

@ -134,6 +134,15 @@ public:
*/
void UpdateLinkState(nsEventStates aState);
/**
* Returns true if this element is either a full-screen element or an
* ancestor of the full-screen element.
*/
bool IsFullScreenAncestor() const {
return mState.HasAtLeastOneOfStates(NS_EVENT_STATE_FULL_SCREEN_ANCESTOR |
NS_EVENT_STATE_FULL_SCREEN);
}
protected:
/**
* Method to get the _intrinsic_ content state of this element. This is the

View File

@ -124,8 +124,9 @@ class Element;
} // namespace mozilla
#define NS_IDOCUMENT_IID \
{ 0x3b78f6, 0x6dc5, 0x44c6, \
{ 0xbc, 0x28, 0x60, 0x2a, 0xb2, 0x4f, 0xfb, 0x7b } }
{ 0x283ec27d, 0x5b23, 0x49b2, \
{ 0x94, 0xd9, 0x9, 0xb5, 0xdb, 0x45, 0x30, 0x73 } }
// Flag for AddStyleSheet().
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
@ -752,21 +753,31 @@ public:
/**
* Asynchronously requests that the document make aElement the full-screen
* element, and move into full-screen mode.
* element, and move into full-screen mode. The current full-screen element
* (if any) is pushed onto the full-screen element stack, and it can be
* returned to full-screen status by calling RestorePreviousFullScreenState().
*/
virtual void AsyncRequestFullScreen(Element* aElement) = 0;
/**
* Requests that the document, and all documents in its hierarchy exit
* from DOM full-screen mode.
* Restores the previous full-screen element to full-screen status. If there
* is no former full-screen element, this exits full-screen, moving the
* top-level browser window out of full-screen mode.
*/
virtual void CancelFullScreen() = 0;
virtual void RestorePreviousFullScreenState() = 0;
/**
* Returns true if this document is in full-screen mode.
*/
virtual bool IsFullScreenDoc() = 0;
/**
* Exits all documents from DOM full-screen mode, and moves the top-level
* browser window out of full-screen mode. If aRunAsync is true, this runs
* asynchronously.
*/
static void ExitFullScreen(bool aRunAsync);
//----------------------------------------------------------------------
// Document notification API's

View File

@ -1872,7 +1872,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
nsIDOMNodeList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOriginalDocument)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mCachedEncoder)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mFullScreenElement)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mStateObjectCached)
// Traverse all our nsCOMArrays.
@ -1927,7 +1926,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mImageMaps)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOriginalDocument)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mCachedEncoder)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mFullScreenElement)
tmp->mParentDocument = nsnull;
@ -7303,21 +7301,6 @@ NotifyPageHide(nsIDocument* aDocument, void* aData)
return true;
}
static bool
SetFullScreenElement(nsIDocument* aDoc, Element* aElement);
static void
SetWindowFullScreen(nsIDocument* aDoc, bool aValue);
static bool
ResetFullScreen(nsIDocument* aDocument, void* aData) {
if (aDocument->IsFullScreenDoc()) {
::SetFullScreenElement(aDocument, nsnull);
aDocument->EnumerateSubDocuments(ResetFullScreen, nsnull);
}
return true;
}
void
nsDocument::OnPageHide(bool aPersisted,
nsIDOMEventTarget* aDispatchStartTarget)
@ -7372,23 +7355,15 @@ nsDocument::OnPageHide(bool aPersisted,
// A full-screen doc has been hidden. We need to ensure we exit
// full-screen, i.e. remove full-screen state from all full-screen
// documents, and exit the top-level window from full-screen mode.
// Unfortunately by the time a doc is hidden, it has been removed
// from the doc tree, so we can't just call CancelFullScreen()...
// By the time a doc is hidden, it has been removed from the doc tree,
// so nsIDocument::ExitFullScreen() won't be able to traverse to this
// document to reset its state, so reset full-screen state in *this*
// document. OnPageHide() is called in every hidden document, so doing
// this ensures all hidden documents have their full-screen state reset.
ClearFullScreenStack();
// So firstly reset full-screen state in *this* document. OnPageHide()
// is called in every hidden document, so doing this ensures all hidden
// documents have their state reset.
::SetFullScreenElement(this, nsnull);
// Next walk the document tree of still visible documents, and reset
// their full-screen state. We then move the top-level window out
// of full-screen mode.
nsCOMPtr<nsIDocument> fullScreenRoot(do_QueryReferent(sFullScreenRootDoc));
if (fullScreenRoot) {
fullScreenRoot->EnumerateSubDocuments(ResetFullScreen, nsnull);
SetWindowFullScreen(fullScreenRoot, false);
sFullScreenRootDoc = nsnull;
}
// Next reset full-screen state in all visible documents in the doctree.
nsIDocument::ExitFullScreen(false);
}
}
@ -8444,40 +8419,13 @@ DispatchFullScreenChange(nsIDocument* aTarget)
e->PostDOMEvent();
}
bool
nsDocument::SetFullScreenElement(Element* aElement)
{
if (mFullScreenElement == aElement) {
return false;
}
if (mFullScreenElement) {
// Reset the ancestor and full-screen styles on the outgoing full-screen
// element in the current document.
nsEventStateManager::SetFullScreenState(mFullScreenElement, false);
}
bool stateChange = (mFullScreenElement != nsnull) != (aElement != nsnull);
mFullScreenElement = aElement;
if (aElement) {
nsEventStateManager::SetFullScreenState(aElement, true);
}
return stateChange;
}
// Wrapper for the nsIDocument -> nsDocument cast required to call
// nsDocument::SetFullScreenElement().
static bool
SetFullScreenElement(nsIDocument* aDoc, Element* aElement)
{
return static_cast<nsDocument*>(aDoc)->SetFullScreenElement(aElement);
}
NS_IMETHODIMP
nsDocument::MozCancelFullScreen()
{
if (!nsContentUtils::IsRequestFullScreenAllowed()) {
return NS_OK;
}
CancelFullScreen();
RestorePreviousFullScreenState();
return NS_OK;
}
@ -8510,8 +8458,80 @@ SetWindowFullScreen(nsIDocument* aDoc, bool aValue)
nsContentUtils::AddScriptRunner(new nsSetWindowFullScreen(aDoc, aValue));
}
class nsCallExitFullScreen : public nsRunnable {
public:
NS_IMETHOD Run()
{
nsDocument::ExitFullScreen();
return NS_OK;
}
};
/* static */
void
nsDocument::CancelFullScreen()
nsIDocument::ExitFullScreen(bool aRunAsync)
{
if (aRunAsync) {
NS_DispatchToCurrentThread(new nsCallExitFullScreen());
return;
}
nsDocument::ExitFullScreen();
}
static bool
ResetFullScreen(nsIDocument* aDocument, void* aData) {
if (aDocument->IsFullScreenDoc()) {
static_cast<nsDocument*>(aDocument)->ClearFullScreenStack();
NS_ASSERTION(!aDocument->IsFullScreenDoc(), "Should reset full-screen");
nsTArray<nsIDocument*>* changed = reinterpret_cast<nsTArray<nsIDocument*>*>(aData);
changed->AppendElement(aDocument);
aDocument->EnumerateSubDocuments(ResetFullScreen, aData);
}
return true;
}
/* static */
void
nsDocument::ExitFullScreen()
{
// Clear full-screen stacks in all descendant documents.
nsCOMPtr<nsIDocument> root(do_QueryReferent(sFullScreenRootDoc));
if (!root) {
// Not in full-screen mode.
return;
}
NS_ASSERTION(root->IsFullScreenDoc(),
"Full-screen root should be a full-screen doc...");
// Stores a list of documents to which we must dispatch "mozfullscreenchange".
// We're required by the spec to dispatch the events in leaf-to-root
// order when exiting full-screen, but we traverse the doctree in a
// root-to-leaf order, so we save references to the documents we must
// dispatch to so that we dispatch in the specified order.
nsAutoTArray<nsIDocument*, 8> changed;
// Walk the tree of full-screen documents, and reset their full-screen state.
ResetFullScreen(root, static_cast<void*>(&changed));
// Dispatch "mozfullscreenchange" events. Note this loop is in reverse
// order so that the events for the leaf document arrives before the root
// document, as required by the spec.
for (PRUint32 i = 0; i < changed.Length(); ++i) {
DispatchFullScreenChange(changed[changed.Length() - i - 1]);
}
// Reset global state. Do this before we move the window out of full-screen
// mode, as that calls nsGlobalWindow::SetFullScreen() which calls back into
// nsIDocument::ExitFullScreen().
sFullScreenRootDoc = nsnull;
sFullScreenDoc = nsnull;
// Move the top-level window out of full-screen mode.
SetWindowFullScreen(root, false);
}
void
nsDocument::RestorePreviousFullScreenState()
{
NS_ASSERTION(!IsFullScreenDoc() || sFullScreenDoc != nsnull,
"Should have a full-screen doc when full-screen!");
@ -8520,32 +8540,57 @@ nsDocument::CancelFullScreen()
return;
}
// Reset full-screen state in all full-screen documents.
nsCOMPtr<nsIDocument> doc(do_QueryReferent(sFullScreenDoc));
while (doc != nsnull) {
if (::SetFullScreenElement(doc, nsnull)) {
DispatchFullScreenChange(doc);
}
// Clear full-screen stacks in all descendant documents, bottom up.
nsCOMPtr<nsIDocument> fullScreenDoc(do_QueryReferent(sFullScreenDoc));
nsIDocument* doc = fullScreenDoc;
while (doc != this) {
NS_ASSERTION(doc->IsFullScreenDoc(), "Should be full-screen doc");
static_cast<nsDocument*>(doc)->ClearFullScreenStack();
DispatchFullScreenChange(doc);
doc = doc->GetParentDocument();
}
sFullScreenDoc = nsnull;
sFullScreenRootDoc = nsnull;
// Move the window out of full-screen mode.
SetWindowFullScreen(this, false);
// Roll-back full-screen state to previous full-screen element.
NS_ASSERTION(doc == this, "Must have reached this doc.");
while (doc != nsnull) {
static_cast<nsDocument*>(doc)->FullScreenStackPop();
DispatchFullScreenChange(doc);
if (static_cast<nsDocument*>(doc)->mFullScreenStack.IsEmpty()) {
// Full-screen stack in document is empty. Go back up to the parent
// document. We'll pop the containing element off its stack, and use
// its next full-screen element as the full-screen element.
doc = doc->GetParentDocument();
} else {
// Else we popped the top of the stack, and there's still another
// element in there, so that will become the full-screen element.
sFullScreenDoc = do_GetWeakReference(doc);
break;
}
}
return;
if (doc == nsnull) {
// We moved all documents out of full-screen mode, reset global full-screen
// state and move the top-level window out of full-screen mode.
DebugOnly< nsCOMPtr<nsIDocument> > root(do_QueryReferent(sFullScreenRootDoc));
NS_ASSERTION(!root->IsFullScreenDoc(), "Should have cleared all docs' stacks");
sFullScreenDoc = nsnull;
sFullScreenRootDoc = nsnull;
SetWindowFullScreen(this, false);
}
}
bool
nsDocument::IsFullScreenDoc()
{
return mFullScreenElement != nsnull;
return GetFullScreenElement() != nsnull;
}
static nsIDocument*
GetCommonAncestor(nsIDocument* aDoc1, nsIDocument* aDoc2)
{
if (!aDoc1 || !aDoc2) {
return nsnull;
}
nsIDocument* doc1 = aDoc1;
nsIDocument* doc2 = aDoc2;
@ -8629,12 +8674,99 @@ LogFullScreenDenied(bool aLogFailure, const char* aMessage, nsIDocument* aDoc)
"DOM", aDoc);
}
void
nsDocument::ClearFullScreenStack()
{
if (mFullScreenStack.IsEmpty()) {
return;
}
// The top element in the full-screen stack will have full-screen
// style bits set on it and its ancestors. Remove the style bits.
// Note the non-top elements won't have the style bits set.
Element* top = FullScreenStackTop();
NS_ASSERTION(top, "Should have a top when full-screen stack isn't empty");
if (top) {
nsEventStateManager::SetFullScreenState(top, false);
}
mFullScreenStack.Clear();
}
bool
nsDocument::FullScreenStackPush(Element* aElement)
{
NS_ASSERTION(aElement, "Must pass non-null to FullScreenStackPush()");
Element* top = FullScreenStackTop();
if (top == aElement || !aElement) {
return false;
}
if (top) {
// We're pushing a new element onto the full-screen stack, so we must
// remove the ancestor and full-screen styles from the former top of the
// stack.
nsEventStateManager::SetFullScreenState(top, false);
}
nsEventStateManager::SetFullScreenState(aElement, true);
mFullScreenStack.AppendElement(do_GetWeakReference(aElement));
NS_ASSERTION(GetFullScreenElement() == aElement, "Should match");
return true;
}
void
nsDocument::FullScreenStackPop()
{
if (mFullScreenStack.IsEmpty()) {
return;
}
// Remove styles from existing top element.
Element* top = FullScreenStackTop();
nsEventStateManager::SetFullScreenState(top, false);
// Remove top element. Note the remaining top element in the stack
// will not have full-screen style bits set, so we will need to restore
// them on the new top element before returning.
PRUint32 last = mFullScreenStack.Length() - 1;
mFullScreenStack.RemoveElementAt(last);
// Pop from the stack null elements (references to elements which have
// been GC'd since they were added to the stack) and elements which are
// no longer in this document.
while (!mFullScreenStack.IsEmpty()) {
Element* element = FullScreenStackTop();
if (!element || !element->IsInDoc() || element->OwnerDoc() != this) {
NS_ASSERTION(!element->IsFullScreenAncestor(),
"Should have already removed full-screen styles");
PRUint32 last = mFullScreenStack.Length() - 1;
mFullScreenStack.RemoveElementAt(last);
} else {
// The top element of the stack is now an in-doc element. Apply the
// full-screen styles and return.
nsEventStateManager::SetFullScreenState(element, true);
break;
}
}
}
Element*
nsDocument::FullScreenStackTop()
{
if (mFullScreenStack.IsEmpty()) {
return nsnull;
}
PRUint32 last = mFullScreenStack.Length() - 1;
nsCOMPtr<Element> element(do_QueryReferent(mFullScreenStack[last]));
NS_ASSERTION(element, "Should have full-screen element!");
NS_ASSERTION(element->IsInDoc(), "Full-screen element should be in doc");
NS_ASSERTION(element->OwnerDoc() == this, "Full-screen element should be in this doc");
return element;
}
void
nsDocument::RequestFullScreen(Element* aElement, bool aWasCallerChrome)
{
NS_ASSERTION(aElement,
"Must pass non-null element to nsDocument::RequestFullScreen");
if (!aElement) {
if (!aElement || aElement == GetFullScreenElement()) {
return;
}
if (!aElement->IsInDoc()) {
@ -8653,28 +8785,29 @@ nsDocument::RequestFullScreen(Element* aElement, bool aWasCallerChrome)
// IsFullScreenEnabled calls LogFullScreenDenied, no need to log.
return;
}
if (GetFullScreenElement() &&
!nsContentUtils::ContentIsDescendantOf(aElement, GetFullScreenElement())) {
// If this document is full-screen, only grant full-screen requests from
// a descendent of the current full-screen element.
LogFullScreenDenied(true, "FullScreenDeniedNotDescendant", this);
return;
}
// Turn off full-screen state in all documents which were previously
// full-screen but which shouldn't be after this request is granted.
// Note commonAncestor will be null when in a separate browser window
// to the requesting document.
nsIDocument* commonAncestor = nsnull;
// Stores a list of documents which we must dispatch "mozfullscreenchange"
// too. We're required by the spec to dispatch the events in root-to-leaf
// order, but we traverse the doctree in a leaf-to-root order, so we save
// references to the documents we must dispatch to so that we get the order
// as specified.
nsAutoTArray<nsIDocument*, 8> changed;
// If another top-level window is full-screen. Exit it from full-screen.
nsCOMPtr<nsIDocument> fullScreenDoc(do_QueryReferent(sFullScreenDoc));
if (fullScreenDoc) {
commonAncestor = GetCommonAncestor(fullScreenDoc, this);
}
nsIDocument* doc = fullScreenDoc;
while (doc != commonAncestor) {
if (::SetFullScreenElement(doc, nsnull)) {
DispatchFullScreenChange(doc);
}
doc = doc->GetParentDocument();
}
if (!commonAncestor && fullScreenDoc) {
// Other doc is in another browser window. Move it out of full-screen.
// Note that nsGlobalWindow::SetFullScreen() proxies to the root window
// in its hierarchy, and does not operate on the a per-nsIDOMWindow basis.
SetWindowFullScreen(fullScreenDoc, false);
nsIDocument* commonAncestor = GetCommonAncestor(fullScreenDoc, this);
if (fullScreenDoc && !commonAncestor) {
// A document which doesn't have a common ancestor is full-screen, this
// must be in a separate browser window. Fully exit full-screen, to move
// the other browser window/doctree out of full-screen.
nsIDocument::ExitFullScreen(false);
}
// Remember the root document, so that if a full-screen document is hidden
@ -8684,10 +8817,10 @@ nsDocument::RequestFullScreen(Element* aElement, bool aWasCallerChrome)
// Set the full-screen element. This sets the full-screen style on the
// element, and the full-screen-ancestor styles on ancestors of the element
// in this document.
if (SetFullScreenElement(aElement)) {
DispatchFullScreenChange(aElement->OwnerDoc());
}
DebugOnly<bool> x = FullScreenStackPush(aElement);
NS_ASSERTION(x, "Full-screen state of requesting doc should always change!");
changed.AppendElement(this);
// Propagate up the document hierarchy, setting the full-screen element as
// the element's container in ancestor documents. This also sets the
// appropriate css styles as well. Note we don't propagate down the
@ -8697,21 +8830,49 @@ nsDocument::RequestFullScreen(Element* aElement, bool aWasCallerChrome)
nsIDocument* parent;
while ((parent = child->GetParentDocument())) {
Element* element = parent->FindContentForSubDocument(child)->AsElement();
if (::SetFullScreenElement(parent, element)) {
DispatchFullScreenChange(element->OwnerDoc());
if (static_cast<nsDocument*>(parent)->FullScreenStackPush(element)) {
changed.AppendElement(parent);
child = parent;
} else {
NS_ASSERTION(!commonAncestor || child == commonAncestor,
"Should finish loop at common ancestor (or null)");
// We've reached either the root, or a point in the doctree where the
// new full-screen element container is the same as the previous
// full-screen element's container. No more changes need to be made
// to the full-screen stacks of documents further up the tree.
break;
}
child = parent;
}
// Dispatch "mozfullscreenchange" events. Note this loop is in reverse
// order so that the events for the root document arrives before the leaf
// document, as required by the spec.
for (PRUint32 i = 0; i < changed.Length(); ++i) {
DispatchFullScreenChange(changed[changed.Length() - i - 1]);
}
// Remember this is the requesting full-screen document.
sFullScreenDoc = do_GetWeakReference(static_cast<nsIDocument*>(this));
// Make the window full-screen. Note we must make the state changes above
// before making the window full-screen, as then the document reports as
// being in full-screen mode when the chrome "fullscreen" event fires,
// enabling chrome to distinguish between browser and dom full-screen
// modes.
// modes. Also note that nsGlobalWindow::SetFullScreen() (which
// SetWindowFullScreen() calls) proxies to the root window in its hierarchy,
// and does not operate on the a per-nsIDOMWindow basis.
SetWindowFullScreen(this, true);
// Remember this is the requesting full-screen document.
sFullScreenDoc = do_GetWeakReference(static_cast<nsIDocument*>(this));
#ifdef DEBUG
NS_ASSERTION(GetFullScreenElement() == aElement,
"Full-screen element should be the requested element!");
NS_ASSERTION(IsFullScreenDoc(), "Should be full-screen doc");
nsCOMPtr<nsIDOMHTMLElement> fse;
GetMozFullScreenElement(getter_AddRefs(fse));
nsCOMPtr<nsIContent> c(do_QueryInterface(fse));
NS_ASSERTION(c->AsElement() == aElement,
"GetMozFullScreenElement should match GetFullScreenElement()");
#endif
}
NS_IMETHODIMP
@ -8730,7 +8891,10 @@ nsDocument::GetMozFullScreenElement(nsIDOMHTMLElement **aFullScreenElement)
Element*
nsDocument::GetFullScreenElement()
{
return mFullScreenElement;
Element* element = FullScreenStackTop();
NS_ASSERTION(!element || element->IsFullScreenAncestor(),
"Should have full-screen styles applied!");
return element;
}
NS_IMETHODIMP
@ -8749,6 +8913,26 @@ nsDocument::GetMozFullScreenEnabled(bool *aFullScreen)
return NS_OK;
}
static bool
HasFullScreenSubDocument(nsIDocument* aDoc, void* aData)
{
if (aDoc->IsFullScreenDoc()) {
// This subdocument is full-screen. Set result and return false to
// stop iteration.
*static_cast<bool*>(aData) = true;
return false;
}
return true;
}
static bool
HasFullScreenSubDocument(nsIDocument* aDoc)
{
bool result = false;
aDoc->EnumerateSubDocuments(&HasFullScreenSubDocument, static_cast<void*>(&result));
return result;
}
bool
nsDocument::IsFullScreenEnabled(bool aCallerIsChrome, bool aLogFailure)
{
@ -8772,6 +8956,10 @@ nsDocument::IsFullScreenEnabled(bool aCallerIsChrome, bool aLogFailure)
LogFullScreenDenied(aLogFailure, "FullScreenDeniedHidden", this);
return false;
}
if (HasFullScreenSubDocument(this)) {
LogFullScreenDenied(aLogFailure, "FullScreenDeniedSubDocFullScreen", this);
return false;
}
// Ensure that all ancestor <iframe> elements have the mozallowfullscreen
// boolean attribute set.

View File

@ -958,8 +958,9 @@ public:
virtual Element* GetFullScreenElement();
virtual void AsyncRequestFullScreen(Element* aElement);
virtual void CancelFullScreen();
virtual void RestorePreviousFullScreenState();
virtual bool IsFullScreenDoc();
static void ExitFullScreen();
// This is called asynchronously by nsIDocument::AsyncRequestFullScreen()
// to move document into full-screen mode if allowed. aWasCallerChrome
@ -967,12 +968,23 @@ public:
// by chrome code.
void RequestFullScreen(Element* aElement, bool aWasCallerChrome);
// Sets the full-screen element to aElement, applying appropriate styles to
// aElement, and removing them from the old full-screen element. Returns true
// if making this change results in a change in the full-screen state of this
// document.
bool SetFullScreenElement(Element* aElement);
// Removes all elements from the full-screen stack, removing full-scren
// styles from the top element in the stack.
void ClearFullScreenStack();
// Pushes aElement onto the full-screen stack, and removes full-screen styles
// from the former full-screen stack top, and its ancestors, and applies the
// styles to aElement. aElement becomes the new "full-screen element".
bool FullScreenStackPush(Element* aElement);
// Remove the top element from the full-screen stack. Removes the full-screen
// styles from the former top element, and applies them to the new top
// element, if there is one.
void FullScreenStackPop();
// Returns the top element from the full-screen stack.
Element* FullScreenStackTop();
// This method may fire a DOM event; if it does so it will happen
// synchronously.
void UpdateVisibilityState();
@ -1112,6 +1124,11 @@ protected:
// document is hidden/navigation occurs.
static nsWeakPtr sFullScreenRootDoc;
// Stack of full-screen elements. When we request full-screen we push the
// full-screen element onto this stack, and when we cancel full-screen we
// pop one off this stack, restoring the previous full-screen state
nsTArray<nsWeakPtr> mFullScreenStack;
nsRefPtr<nsEventListenerManager> mListenerManager;
nsCOMPtr<nsIDOMStyleSheetList> mDOMStyleSheets;
nsRefPtr<nsDOMStyleSheetSetList> mStyleSheetSetList;
@ -1131,9 +1148,6 @@ protected:
// Recorded time of change to 'loading' state.
mozilla::TimeStamp mLoadingTimeStamp;
// The current full-screen element of this document.
nsCOMPtr<Element> mFullScreenElement;
// True if the document has been detached from its content viewer.
bool mIsGoingAway:1;
// True if the document is being destroyed.

View File

@ -3067,14 +3067,6 @@ nsGenericElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
return NS_OK;
}
static bool
IsFullScreenAncestor(Element* aElement)
{
nsEventStates state = aElement->State();
return state.HasAtLeastOneOfStates(NS_EVENT_STATE_FULL_SCREEN_ANCESTOR |
NS_EVENT_STATE_FULL_SCREEN);
}
void
nsGenericElement::UnbindFromTree(bool aDeep, bool aNullParent)
{
@ -3086,7 +3078,7 @@ nsGenericElement::UnbindFromTree(bool aDeep, bool aNullParent)
HasFlag(NODE_FORCE_XBL_BINDINGS) ? OwnerDoc() : GetCurrentDoc();
if (aNullParent) {
if (IsFullScreenAncestor(this)) {
if (IsFullScreenAncestor()) {
// The element being removed is an ancestor of the full-screen element,
// exit full-screen state.
nsContentUtils::ReportToConsole(nsContentUtils::eDOM_PROPERTIES,
@ -3094,8 +3086,9 @@ nsGenericElement::UnbindFromTree(bool aDeep, bool aNullParent)
nsnull, 0, nsnull,
EmptyString(), 0, 0,
nsIScriptError::warningFlag,
"DOM", OwnerDoc());
OwnerDoc()->CancelFullScreen();
"DOM", OwnerDoc());
// Fully exit full-screen.
nsIDocument::ExitFullScreen(false);
}
if (GetParent()) {
NS_RELEASE(mParent);

View File

@ -297,8 +297,7 @@ nsHTMLSharedObjectElement::BindToTree(nsIDocument *aDocument,
// This content contains a windowed plugin for which we don't control
// event dispatch, and we're in full-screen mode. Exit full-screen mode
// to prevent phishing attacks.
NS_DispatchToCurrentThread(
NS_NewRunnableMethod(aDocument, &nsIDocument::CancelFullScreen));
nsIDocument::ExitFullScreen(true);
nsContentUtils::ReportToConsole(nsContentUtils::eDOM_PROPERTIES,
"AddedWindowedPluginWhileFullScreen",
nsnull, 0, nsnull,

View File

@ -292,6 +292,7 @@ _TEST_FILES = \
file_fullscreen-navigation.html \
file_fullscreen-esc-exit.html \
file_fullscreen-esc-exit-inner.html \
file_fullscreen-rollback.html \
test_li_attributes_reflection.html \
test_ol_attributes_reflection.html \
test_bug651956.html \

View File

@ -28,11 +28,11 @@ var keyList = [
];
function ok(condition, msg) {
opener.ok(condition, msg);
opener.ok(condition, "[keys] " + msg);
}
function is(a, b, msg) {
opener.is(a, b, msg);
opener.is(a, b, "[keys] " + msg);
}
var gKeyTestIndex = 0;

View File

@ -22,11 +22,11 @@ Test DOM full-screen API.
/** Test for Bug 545812 **/
function ok(condition, msg) {
opener.ok(condition, msg);
opener.ok(condition, "[fullscreen] " + msg);
}
function is(a, b, msg) {
opener.is(a, b, msg);
opener.is(a, b, "[fullscreen] " + msg);
}
/*
@ -64,27 +64,23 @@ function fullScreenElement() {
function fullScreenChange(event) {
switch (fullScreenChangeCount) {
case 0: {
ok(document.mozFullScreen, "Should be in full-screen mode (first time)");
is(event.target, document, "Event target should be full-screen document #1");
is(document.mozFullScreenElement, fullScreenElement(),
"Full-screen element should be div element.");
ok(document.mozFullScreenElement.mozMatchesSelector(":-moz-full-screen"),
"FSE should match :-moz-full-screen");
ok(document.mozFullScreen, "1. Should be in full-screen mode (first time)");
is(event.target, document, "2. Event target should be full-screen document #1");
is(document.mozFullScreenElement, fullScreenElement(), "3. Full-screen element should be div element.");
ok(document.mozFullScreenElement.mozMatchesSelector(":-moz-full-screen"), "4. FSE should match :-moz-full-screen");
var fse = fullScreenElement();
fse.parentNode.removeChild(fse);
is(document.mozFullScreenElement, null,
"Full-screen element should be null after removing.");
ok(!document.mozFullScreen, "Should have left full-screen mode when we remove full-screen element");
is(document.mozFullScreenElement, null, "5. Full-screen element should be null after removing.");
ok(!document.mozFullScreen, "6. Should have left full-screen mode when we remove full-screen element");
document.body.appendChild(fse);
ok(!document.mozFullScreen, "Should not return to full-screen mode when re-adding former FSE");
is(document.mozFullScreenElement, null,
"Full-screen element should still be null after re-adding former FSE.");
ok(!document.mozFullScreen, "7. Should not return to full-screen mode when re-adding former FSE");
is(document.mozFullScreenElement, null, "8. Full-screen element should still be null after re-adding former FSE.");
break;
}
case 1: {
ok(!document.mozFullScreen, "Should have left full-screen mode (first time)");
is(event.target, document, "Event target should be full-screen document #2");
is(document.mozFullScreenElement, null, "Full-screen element should be null.");
ok(!document.mozFullScreen, "9. Should have left full-screen mode (first time)");
is(event.target, document, "10. Event target should be full-screen document #2");
is(document.mozFullScreenElement, null, "11. Full-screen element should be null.");
iframe = document.createElement("iframe");
iframe.mozAllowFullScreen = true;
document.body.appendChild(iframe);
@ -92,41 +88,48 @@ function fullScreenChange(event) {
break;
}
case 2: {
ok(document.mozFullScreen, "Should be back in full-screen mode (second time)");
is(event.target, document, "Event target should be full-screen document #3");
is(document.mozFullScreenElement, iframe,
"Full-screen element should be iframe element.");
SimpleTest.waitForFocus(function() {
ok(document.mozFullScreen, "Should still be full-screen mode before focus.");
SpecialPowers.focus(window);
ok(document.mozFullScreen, "Should still be full-screen mode after focus.");
var fse = fullScreenElement();
fse.mozRequestFullScreen();
// RequestFullScreen() is async, continue after it completes.
setTimeout(function() {
ok(document.mozFullScreen, "Should still be full-screen mode after re-requesting.");
is(document.mozFullScreenElement, fse, "Full-screen element should have switched to requestee.");
var _innerFrame = iframe.contentDocument.getElementById("inner-frame");
_innerFrame.contentDocument.body.appendChild(fse);
ok(!document.mozFullScreen, "Should exit full-screen after transplanting FSE");
is(document.mozFullScreenElement, null, "Full-screen element transplanted, should be null.");
is(iframe.contentDocument.mozFullScreenElement, null, "Full-screen element in outer frame should be null.");
is(_innerFrame.contentDocument.mozFullScreenElement, null, "Full-screen element in inner frame should be null.");
ok(!iframe.contentDocument.mozFullScreen, "Outer frame should not acquire full-screen status.");
ok(!_innerFrame.contentDocument.mozFullScreen, "Inner frame should not acquire full-screen status.");
document.body.appendChild(fse);
}, 0);
}, 0);
ok(document.mozFullScreen, "12. Should be back in full-screen mode (second time)");
is(event.target, document, "13. Event target should be full-screen document #3");
is(document.mozFullScreenElement, iframe, "14. Full-screen element should be iframe element.");
is(iframe.contentDocument.mozFullScreenElement, iframe.contentDocument.body, "15. Full-screen element in subframe should be body");
// The iframe's body is full-screen. Cancel full-screen in the subdocument to return
// the full-screen element to the previous full-screen element. This causes
// a fullscreenchange event.
document.mozCancelFullScreen();
break;
}
case 3: {
ok(!document.mozFullScreen, "Should be back in non-full-screen mode (second time)");
is(event.target, document, "Event target should be full-screen document #4");
is(document.mozFullScreenElement, null, "Full-screen element should be null.");
ok(!document.mozFullScreen, "16. Should have left full-screen when removing FSE ancestor.");
is(document.mozFullScreenElement, null, "17. Full-screen element should have rolled back.");
is(iframe.contentDocument.mozFullScreenElement, null, "18. Full-screen element in subframe should be null");
fullScreenElement().mozRequestFullScreen();
break;
}
case 4: {
ok(document.mozFullScreen, "19. Should be back in full-screen mode (second time)");
is(event.target, document, "20. Event target should be full-screen document #3");
is(document.mozFullScreenElement, fullScreenElement(), "21. Full-screen element should be div.");
// Transplant the FSE into subdoc. Should exit full-screen.
var _innerFrame = iframe.contentDocument.getElementById("inner-frame");
var fse = fullScreenElement();
_innerFrame.contentDocument.body.appendChild(fse);
ok(!document.mozFullScreen, "22. Should exit full-screen after transplanting FSE");
is(document.mozFullScreenElement, null, "23. Full-screen element transplanted, should be null.");
is(iframe.contentDocument.mozFullScreenElement, null, "24. Full-screen element in outer frame should be null.");
is(_innerFrame.contentDocument.mozFullScreenElement, null, "25. Full-screen element in inner frame should be null.");
ok(!iframe.contentDocument.mozFullScreen, "26. Outer frame should not acquire full-screen status.");
ok(!_innerFrame.contentDocument.mozFullScreen, "27. Inner frame should not acquire full-screen status.");
document.body.appendChild(fse);
break;
}
case 5: {
ok(!document.mozFullScreen, "28. Should be back in non-full-screen mode (second time)");
is(event.target, document, "29. Event target should be full-screen document #4");
is(document.mozFullScreenElement, null, "30. Full-screen element should be null.");
document.body.removeChild(iframe);
iframe = null;
@ -136,7 +139,7 @@ function fullScreenChange(event) {
var f =
function(e) {
document.removeEventListener("mozfullscreenerror", f, false);
ok(!document.mozFullScreen, "Requests for full-screen from not-in-doc elements should fail.");
ok(!document.mozFullScreen, "31. Requests for full-screen from not-in-doc elements should fail.");
fullScreenErrorRun = true;
container = document.createElement("div");
@ -151,12 +154,11 @@ function fullScreenChange(event) {
break;
}
case 4: {
ok(document.mozFullScreen, "Should still be in full-screen mode (third time)");
is(event.target, document, "Event target should be full-screen document #5");
ok(fullScreenErrorRun, "Should have run fullscreenerror handler from previous case.");
is(document.mozFullScreenElement, inDocElement,
"FSE should be inDocElement.");
case 6: {
ok(document.mozFullScreen, "32. Should still be in full-screen mode (third time)");
is(event.target, document, "33. Event target should be full-screen document #5");
ok(fullScreenErrorRun, "34. Should have run fullscreenerror handler from previous case.");
is(document.mozFullScreenElement, inDocElement, "35. FSE should be inDocElement.");
var n = container;
do {
@ -166,14 +168,12 @@ function fullScreenChange(event) {
// Remove full-screen ancestor element from document, verify it stops being reported as current FSE.
container.parentNode.removeChild(container);
ok(!document.mozFullScreen,
"Should exit full-screen mode after removing full-screen element ancestor from document");
is(document.mozFullScreenElement, null,
"Should not have a full-screen element again.");
ok(!document.mozFullScreen, "36. Should exit full-screen mode after removing full-screen element ancestor from document");
is(document.mozFullScreenElement, null, "37. Should not have a full-screen element again.");
break;
}
case 5: {
ok(!document.mozFullScreen, "Should be back in non-full-screen mode (third time)");
case 7: {
ok(!document.mozFullScreen, "38. Should be back in non-full-screen mode (third time)");
setRequireTrustedContext(true);
fullscreendenied = false;
fullScreenElement().mozRequestFullScreen();
@ -189,7 +189,7 @@ function fullScreenChange(event) {
}, 0);
break;
}
case 6: {
case 8: {
ok(document.mozFullScreen, "Moved to full-screen after mouse click");
document.mozCancelFullScreen();
ok(document.mozFullScreen, "Should still be in full-screen mode, because calling context isn't trusted.");
@ -198,7 +198,7 @@ function fullScreenChange(event) {
ok(!document.mozFullScreen, "Should have left full-screen mode.");
break;
}
case 7: {
case 9: {
ok(!document.mozFullScreen, "Should have left full-screen mode (last time).");
SpecialPowers.setBoolPref("full-screen-api.enabled", false);

View File

@ -22,11 +22,11 @@ Test DOM full-screen API.
/** Test for Bug 545812 **/
function ok(condition, msg) {
opener.ok(condition, msg);
opener.ok(condition, "[denied] " + msg);
}
function is(a, b, msg) {
opener.is(a, b, msg);
opener.is(a, b, "[denied] " + msg);
}
var fullscreendenied = false;

View File

@ -21,11 +21,11 @@ exit DOM full-screen mode.
<script type="application/javascript">
function ok(condition, msg) {
opener.ok(condition, msg);
opener.ok(condition, "[esc-exit] " + msg);
}
function is(a, b, msg) {
opener.is(a, b, msg);
opener.is(a, b, "[esc-exit] " + msg);
}
function finish() {
@ -34,6 +34,7 @@ function finish() {
function fullscreenchange1(event) {
ok(document.mozFullScreen, "Should have entered full-screen mode");
is(document.mozFullScreenElement, document.body, "FSE should be doc");
document.removeEventListener("mozfullscreenchange", fullscreenchange1, false);
document.addEventListener("mozfullscreenchange", fullscreenchange2, false);
ok(!document.getElementById("subdoc").contentWindow.escKeySent, "Should not yet have sent ESC key press.");

View File

@ -34,17 +34,18 @@ function boom()
function b2()
{
try { e1.mozRequestFullScreen(); } catch(e) { opener.ok(false, "Should not enter full-screen"); }
try { e1.mozRequestFullScreen(); } catch(e) { opener.ok(false, "[hidden] Should not enter full-screen"); }
setTimeout(done, 0);
}
function done() {
opener.ok(!document.mozFullScreen, "Should not have entered full-screen mode in hidden document.");
opener.ok(!e1.ownerDocument.mozFullScreen, "Requesting owner should not have entered full-screen mode.");
opener.ok(!document.mozFullScreen, "[hidden] Should not have entered full-screen mode in hidden document.");
opener.ok(!e1.ownerDocument.mozFullScreen, "[hidden] Requesting owner should not have entered full-screen mode.");
opener.nextTest();
}
</script>
</pre>
</body>
</html>

View File

@ -34,14 +34,14 @@ function boom()
e1.mozRequestFullScreen();
setTimeout(
function() {
opener.ok(document.mozFullScreen, "Request should be granted");
opener.ok(document.mozFullScreen, "[navigation] Request should be granted");
frameWin.location = "data:text/html,<body text=blue onload='parent.b2()'>2";
}, 0);
}
function b2()
{
opener.ok(!document.mozFullScreen, "Should have left full-screen due to navigation.");
opener.ok(!document.mozFullScreen, "[navigation] Should have left full-screen due to navigation.");
opener.nextTest();
}

View File

@ -43,11 +43,11 @@ Test plugins with DOM full-screen API:
/** Test for Bug 545812 **/
function ok(condition, msg) {
opener.ok(condition, msg);
opener.ok(condition, "[plugins] " + msg);
}
function is(a, b, msg) {
opener.is(a, b, msg);
opener.is(a, b, "[plugins] " + msg);
}
const isMacOs = navigator.appVersion.indexOf("Macintosh") != -1;
@ -144,6 +144,7 @@ function fullScreenChange(event) {
switch (fullScreenChangeCount) {
case 0: {
ok(document.mozFullScreen, "Request for full-screen with document containing windowless plugin should be granted");
is(document.mozFullScreenElement, document.body, "FSE should be body.");
// Add windowed plugin to document, should cause full-screen mode to exit.
document.body.appendChild(windowedPlugin);
break;

View File

@ -0,0 +1,130 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=700764
Verifies that cancelFullScreen() rolls back to have the previous full-screen
element full-screen.
Tests:
* Request full-screen in doc.
* Request full-screen in doc on element not descended from full-screen element. Request should be denied.
* Request full-screen in subdoc.
* Cancel full-screen in subdoc, doc should be full-screen.
* Request full-screen in subdoc.
* Removing FSE should fully-exit full-screen.
-->
<head>
<title>Test for Bug 700764</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
</head>
<body onload="start();">
<div id="fse">
<div id="fse-inner">
<iframe id="subdoc" mozallowfullscreen src="data:text/html,<html><body bgcolor='black'></body></html>"></iframe>
</div>
</div>
<div id="non-fse"></div>
<script type="application/javascript">
/** Test for Bug 700764 **/
function ok(condition, msg) {
opener.ok(condition, "[rollback] " + msg);
}
function is(a, b, msg) {
opener.is(a, b, "[rollback] " + msg);
}
function addListener(type, f) {
document.addEventListener("mozfullscreen" + type, f, false);
}
function removeListener(type, f) {
document.removeEventListener("mozfullscreen" + type, f, false);
}
function e(id) {
return document.getElementById(id);
}
function start() {
SimpleTest.waitForFocus(
function() {
addListener("change", change1);
e("fse").mozRequestFullScreen();
}
);
}
function change1() {
removeListener("change", change1);
addListener("error", error1);
is(document.mozFullScreenElement, e("fse"), "Body should be FSE");
// Request full-screen from element not descendent from current FSE.
e("non-fse").mozRequestFullScreen();
}
function error1() {
removeListener("error", error1);
addListener("change", change2);
is(document.mozFullScreenElement, e("fse"), "FSE should not change");
var iframe = e("subdoc");
iframe.contentDocument.body.mozRequestFullScreen();
}
function change2() {
removeListener("change", change2);
var iframe = e("subdoc");
is(document.mozFullScreenElement, iframe, "Subdoc container should be FSE.");
is(iframe.contentDocument.mozFullScreenElement, iframe.contentDocument.body, "Subdoc body should be FSE in subdoc");
addListener("change", change3);
iframe.contentDocument.mozCancelFullScreen();
}
function change3() {
removeListener("change", change3);
is(document.mozFullScreenElement, e("fse"), "FSE should rollback to FSE.");
addListener("change", change4);
document.mozCancelFullScreen();
}
function change4() {
removeListener("change", change4);
is(document.mozFullScreenElement, null, "Should have left full-screen entirely");
addListener("change", change5);
e("fse").mozRequestFullScreen();
}
function change5() {
removeListener("change", change5);
addListener("change", change6);
is(document.mozFullScreenElement, e("fse"), "FSE should be e('fse')");
e("fse-inner").mozRequestFullScreen();
}
function change6() {
removeListener("change", change6);
addListener("change", change7);
var element = e('fse-inner');
is(document.mozFullScreenElement, element, "FSE should be e('fse-inner')");
element.parentNode.removeChild(element);
}
function change7() {
removeListener("change", change7);
is(document.mozFullScreenElement, null, "Should have fully exited full-screen mode when removed FSE from doc");
opener.nextTest();
}
</script>
</pre>
</body>
</html>

View File

@ -36,6 +36,7 @@ SpecialPowers.setBoolPref("full-screen-api.allow-trusted-requests-only", false);
// run in an iframe, which by default will not have the mozallowfullscreen
// attribute set, so full-screen won't work.
var gTestWindows = [
"file_fullscreen-rollback.html",
"file_fullscreen-esc-exit.html",
"file_fullscreen-denied.html",
"file_fullscreen-api.html",

View File

@ -4424,12 +4424,12 @@ nsGlobalWindow::SetFullScreen(bool aFullScreen)
if (widget)
widget->MakeFullScreen(aFullScreen);
if (!mFullScreen && mDocument) {
// Notify the document that we've left full-screen mode. This is so that
// if we're in full-screen mode and the user exits full-screen mode with
// the browser full-screen mode toggle keyboard-shortcut, we detect that
// and leave DOM API full-screen mode too.
mDocument->MozCancelFullScreen();
if (!mFullScreen) {
// Force exit from DOM full-screen mode. This is so that if we're in
// DOM full-screen mode and the user exits full-screen mode with
// the browser full-screen mode toggle keyboard-shortcut, we'll detect
// that and leave DOM API full-screen mode too.
nsIDocument::ExitFullScreen(false);
}
return NS_OK;

View File

@ -123,6 +123,8 @@ FullScreenDeniedNotInputDriven=Request for full-screen was denied because Elemen
FullScreenDeniedNotInDocument=Request for full-screen was denied because requesting element is no longer in its document.
FullScreenDeniedMovedDocument=Request for full-screen was denied because requesting element has moved document.
FullScreenDeniedLostWindow=Request for full-screen was denied because we no longer have a window.
FullScreenDeniedSubDocFullScreen=Request for full-screen was denied because a subdocument of the document requesting full-screen is already full-screen.
FullScreenDeniedNotDescendant=Request for full-screen was denied because requesting element is not a descendant of the current full-screen element.
RemovedFullScreenElement=Exited full-screen because full-screen element was removed from document.
AddedWindowedPluginWhileFullScreen=Exited full-screen because windowed plugin was added to document.
HTMLMultipartXHRWarning=HTML parsing in XMLHttpRequest is not supported for multipart responses.

View File

@ -3313,8 +3313,7 @@ NS_IMETHODIMP nsPluginInstanceOwner::CreateWidget(void)
nsIDocument *doc = mContent ? mContent->OwnerDoc() : nsnull;
#ifndef XP_MACOSX
if (!windowless && doc && doc->IsFullScreenDoc()) {
NS_DispatchToCurrentThread(
NS_NewRunnableMethod(doc, &nsIDocument::CancelFullScreen));
nsIDocument::ExitFullScreen(true);
}
#endif
// always create widgets in Twips, not pixels

View File

@ -6337,9 +6337,7 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus)
if (aEvent->message == NS_KEY_UP) {
// ESC key released while in DOM full-screen mode.
// Exit full-screen mode.
NS_DispatchToCurrentThread(
NS_NewRunnableMethod(root,
&nsIDocument::CancelFullScreen));
nsIDocument::ExitFullScreen(true);
}
} else if (IsFullScreenAndRestrictedKeyEvent(mCurrentEventContent, aEvent)) {
// Restricted key press while in DOM full-screen mode. Dispatch