Bug 1453788 - Allow top level HTML windows to have persistent window state. r=smaug

Move tracking of persistent window state into nsXULWindow. Also, move
special handling of the width/height of the window into nsXULWindow.

MozReview-Commit-ID: LOmHGyYeNSU

--HG--
extra : rebase_source : bcea16eb6209ff789948644a64968a7325cea4ef
This commit is contained in:
Brendan Dahl 2018-04-26 17:53:54 -07:00
parent 65d34faa5e
commit 295e2f4aca
4 changed files with 210 additions and 83 deletions

View File

@ -1161,41 +1161,6 @@ XULDocument::Persist(const nsAString& aID,
aRv = Persist(element, nameSpaceID, tag);
}
enum class ConversionDirection {
InnerToOuter,
OuterToInner,
};
static void
ConvertWindowSize(nsIXULWindow* aWin,
nsAtom* aAttr,
ConversionDirection aDirection,
nsAString& aInOutString)
{
MOZ_ASSERT(aWin);
MOZ_ASSERT(aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height);
nsresult rv;
int32_t size = aInOutString.ToInteger(&rv);
if (NS_FAILED(rv)) {
return;
}
int32_t sizeDiff = aAttr == nsGkAtoms::width
? aWin->GetOuterToInnerWidthDifferenceInCSSPixels()
: aWin->GetOuterToInnerHeightDifferenceInCSSPixels();
if (!sizeDiff) {
return;
}
int32_t multiplier =
aDirection == ConversionDirection::InnerToOuter ? 1 : - 1;
CopyASCIItoUTF16(nsPrintfCString("%d", size + multiplier * sizeDiff),
aInOutString);
}
nsresult
XULDocument::Persist(Element* aElement, int32_t aNameSpaceID,
nsAtom* aAttribute)
@ -1236,16 +1201,9 @@ XULDocument::Persist(Element* aElement, int32_t aNameSpaceID,
return mLocalStore->RemoveValue(uri, id, attrstr);
}
// Make sure we store the <window> attributes as outer window size, see
// bug 1444525 & co.
if (aElement->IsXULElement(nsGkAtoms::window) &&
(aAttribute == nsGkAtoms::width || aAttribute == nsGkAtoms::height)) {
if (nsCOMPtr<nsIXULWindow> win = GetXULWindowIfToplevelChrome()) {
ConvertWindowSize(win,
aAttribute,
ConversionDirection::InnerToOuter,
valuestr);
}
// Persisting attributes to windows is handled by nsXULWindow.
if (aElement->IsXULElement(nsGkAtoms::window)) {
return NS_OK;
}
return mLocalStore->SetValue(uri, id, attrstr, valuestr);
@ -1837,21 +1795,9 @@ XULDocument::ApplyPersistentAttributesToElements(const nsAString &aID,
continue;
}
// Convert attributes from outer size to inner size for top-level
// windows, see bug 1444525 & co.
if (element->IsXULElement(nsGkAtoms::window) &&
(attr == nsGkAtoms::width || attr == nsGkAtoms::height)) {
if (nsCOMPtr<nsIXULWindow> win = GetXULWindowIfToplevelChrome()) {
nsAutoString maybeConvertedValue = value;
ConvertWindowSize(win,
attr,
ConversionDirection::OuterToInner,
maybeConvertedValue);
Unused <<
element->SetAttr(kNameSpaceID_None, attr, maybeConvertedValue, true);
continue;
}
// Applying persistent attributes to windows is handled by nsXULWindow.
if (element->IsXULElement(nsGkAtoms::window)) {
continue;
}
Unused << element->SetAttr(kNameSpaceID_None, attr, value, true);

View File

@ -1453,6 +1453,7 @@ GK_ATOM(xulcontentsgenerated, "xulcontentsgenerated")
GK_ATOM(yes, "yes")
GK_ATOM(z_index, "z-index")
GK_ATOM(zeroDigit, "zero-digit")
GK_ATOM(zlevel, "zlevel")
GK_ATOM(percentage, "%")

View File

@ -92,6 +92,7 @@ nsXULWindow::nsXULWindow(uint32_t aChromeFlags)
mContinueModalLoop(false),
mDebuting(false),
mChromeLoaded(false),
mPersistentWindowStateLoaded(false),
mSizingShellFromXUL(false),
mShowAfterLoad(false),
mIntrinsicallySized(false),
@ -1123,6 +1124,7 @@ void nsXULWindow::OnChromeLoaded()
if (NS_SUCCEEDED(rv)) {
mChromeLoaded = true;
ApplyChromeFlags();
LoadPersistentWindowState();
SyncAttributesToWidget();
SizeShell();
@ -1598,6 +1600,147 @@ void nsXULWindow::SyncAttributesToWidget()
}
}
enum class ConversionDirection {
InnerToOuter,
OuterToInner,
};
static void
ConvertWindowSize(nsIXULWindow* aWin,
const nsAtom* aAttr,
ConversionDirection aDirection,
nsAString& aInOutString)
{
MOZ_ASSERT(aWin);
MOZ_ASSERT(aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height);
nsresult rv;
int32_t size = aInOutString.ToInteger(&rv);
if (NS_FAILED(rv)) {
return;
}
int32_t sizeDiff = aAttr == nsGkAtoms::width
? aWin->GetOuterToInnerWidthDifferenceInCSSPixels()
: aWin->GetOuterToInnerHeightDifferenceInCSSPixels();
if (!sizeDiff) {
return;
}
int32_t multiplier =
aDirection == ConversionDirection::InnerToOuter ? 1 : - 1;
CopyASCIItoUTF16(nsPrintfCString("%d", size + multiplier * sizeDiff),
aInOutString);
}
nsresult
nsXULWindow::GetPersistentValue(const nsAtom* aAttr,
nsAString& aValue)
{
nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
if (!docShellElement) {
return NS_ERROR_FAILURE;
}
nsAutoString windowElementId;
docShellElement->GetId(windowElementId);
// Elements must have an ID to be persisted.
if (windowElementId.IsEmpty()) {
return NS_OK;
}
nsCOMPtr<nsIDocument> ownerDoc = docShellElement->OwnerDoc();
nsIURI* docURI = ownerDoc->GetDocumentURI();
if (!docURI) {
return NS_ERROR_FAILURE;
}
nsAutoCString utf8uri;
nsresult rv = docURI->GetSpec(utf8uri);
NS_ENSURE_SUCCESS(rv, rv);
NS_ConvertUTF8toUTF16 uri(utf8uri);
if (!mLocalStore) {
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if (NS_WARN_IF(!mLocalStore)) {
return NS_ERROR_NOT_INITIALIZED;
}
}
rv = mLocalStore->GetValue(uri,
windowElementId,
nsDependentAtomString(aAttr),
aValue);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
// Convert attributes from outer size to inner size for top-level
// windows, see bug 1444525 & co.
ConvertWindowSize(this,
aAttr,
ConversionDirection::OuterToInner,
aValue);
}
return NS_OK;
}
nsresult
nsXULWindow::SetPersistentValue(const nsAtom* aAttr,
const nsAString& aValue)
{
nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
if (!docShellElement) {
return NS_ERROR_FAILURE;
}
nsAutoString windowElementId;
docShellElement->GetId(windowElementId);
// Match the behavior of XULDocument and only persist values if the element
// has an ID.
if (windowElementId.IsEmpty()) {
return NS_OK;
}
nsCOMPtr<nsIDocument> ownerDoc = docShellElement->OwnerDoc();
nsIURI* docURI = ownerDoc->GetDocumentURI();
if (!docURI) {
return NS_ERROR_FAILURE;
}
nsAutoCString utf8uri;
nsresult rv = docURI->GetSpec(utf8uri);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
NS_ConvertUTF8toUTF16 uri(utf8uri);
nsAutoString maybeConvertedValue(aValue);
if (aAttr == nsGkAtoms::width || aAttr == nsGkAtoms::height) {
// Make sure we store the <window> attributes as outer window size, see
// bug 1444525 & co.
ConvertWindowSize(this,
aAttr,
ConversionDirection::InnerToOuter,
maybeConvertedValue);
}
if (!mLocalStore) {
mLocalStore = do_GetService("@mozilla.org/xul/xulstore;1");
if (NS_WARN_IF(!mLocalStore)) {
return NS_ERROR_NOT_INITIALIZED;
}
}
return mLocalStore->SetValue(uri,
windowElementId,
nsDependentAtomString(aAttr),
maybeConvertedValue);
}
NS_IMETHODIMP nsXULWindow::SavePersistentAttributes()
{
// can happen when the persistence timer fires at an inopportune time
@ -1639,17 +1782,7 @@ NS_IMETHODIMP nsXULWindow::SavePersistentAttributes()
}
nsAutoString sizeString;
nsAutoString windowElementId;
// fetch docShellElement's ID and XUL owner document
RefPtr<dom::XULDocument> ownerXULDoc =
docShellElement->OwnerDoc()->IsXULDocument()
? docShellElement->OwnerDoc()->AsXULDocument() : nullptr;
if (docShellElement->IsXULElement()) {
docShellElement->GetId(windowElementId);
}
bool shouldPersist = !isFullscreen && ownerXULDoc;
bool shouldPersist = !isFullscreen;
ErrorResult rv;
// (only for size elements which are persisted)
if ((mPersistentAttributesDirty & PAD_POSITION) && gotRestoredBounds) {
@ -1658,8 +1791,7 @@ NS_IMETHODIMP nsXULWindow::SavePersistentAttributes()
sizeString.AppendInt(NSToIntRound(rect.X() / posScale.scale));
docShellElement->SetAttribute(SCREENX_ATTRIBUTE, sizeString, rv);
if (shouldPersist) {
IgnoredErrorResult err;
ownerXULDoc->Persist(windowElementId, SCREENX_ATTRIBUTE, err);
Unused << SetPersistentValue(nsGkAtoms::screenX, sizeString);
}
}
if (persistString.Find("screenY") >= 0) {
@ -1667,8 +1799,7 @@ NS_IMETHODIMP nsXULWindow::SavePersistentAttributes()
sizeString.AppendInt(NSToIntRound(rect.Y() / posScale.scale));
docShellElement->SetAttribute(SCREENY_ATTRIBUTE, sizeString, rv);
if (shouldPersist) {
IgnoredErrorResult err;
ownerXULDoc->Persist(windowElementId, SCREENY_ATTRIBUTE, err);
Unused << SetPersistentValue(nsGkAtoms::screenY, sizeString);
}
}
}
@ -1680,8 +1811,7 @@ NS_IMETHODIMP nsXULWindow::SavePersistentAttributes()
sizeString.AppendInt(NSToIntRound(innerRect.Width() / sizeScale.scale));
docShellElement->SetAttribute(WIDTH_ATTRIBUTE, sizeString, rv);
if (shouldPersist) {
IgnoredErrorResult err;
ownerXULDoc->Persist(windowElementId, WIDTH_ATTRIBUTE, err);
Unused << SetPersistentValue(nsGkAtoms::width, sizeString);
}
}
if (persistString.Find("height") >= 0) {
@ -1689,8 +1819,7 @@ NS_IMETHODIMP nsXULWindow::SavePersistentAttributes()
sizeString.AppendInt(NSToIntRound(innerRect.Height() / sizeScale.scale));
docShellElement->SetAttribute(HEIGHT_ATTRIBUTE, sizeString, rv);
if (shouldPersist) {
IgnoredErrorResult err;
ownerXULDoc->Persist(windowElementId, HEIGHT_ATTRIBUTE, err);
Unused << SetPersistentValue(nsGkAtoms::height, sizeString);
}
}
}
@ -1707,8 +1836,7 @@ NS_IMETHODIMP nsXULWindow::SavePersistentAttributes()
sizeString.Assign(SIZEMODE_NORMAL);
docShellElement->SetAttribute(MODE_ATTRIBUTE, sizeString, rv);
if (shouldPersist && persistString.Find("sizemode") >= 0) {
IgnoredErrorResult err;
ownerXULDoc->Persist(windowElementId, MODE_ATTRIBUTE, err);
Unused << SetPersistentValue(nsGkAtoms::sizemode, sizeString);
}
}
if (persistString.Find("zlevel") >= 0) {
@ -1720,8 +1848,7 @@ NS_IMETHODIMP nsXULWindow::SavePersistentAttributes()
sizeString.AppendInt(zLevel);
docShellElement->SetAttribute(ZLEVEL_ATTRIBUTE, sizeString, rv);
if (shouldPersist) {
IgnoredErrorResult err;
ownerXULDoc->Persist(windowElementId, ZLEVEL_ATTRIBUTE, err);
Unused << SetPersistentValue(nsGkAtoms::zlevel, sizeString);
}
}
}
@ -2280,11 +2407,53 @@ NS_IMETHODIMP
nsXULWindow::BeforeStartLayout()
{
ApplyChromeFlags();
LoadPersistentWindowState();
SyncAttributesToWidget();
SizeShell();
return NS_OK;
}
void
nsXULWindow::LoadPersistentWindowState()
{
// Only apply the persisted state once.
if (mPersistentWindowStateLoaded) {
return;
}
mPersistentWindowStateLoaded = true;
nsCOMPtr<dom::Element> docShellElement = GetWindowDOMElement();
if (!docShellElement) {
return;
}
// Check if the window wants to persist anything.
nsAutoString persist;
docShellElement->GetAttr(kNameSpaceID_None, nsGkAtoms::persist, persist);
if (persist.IsEmpty()) {
return;
}
auto loadValue = [&] (const nsAtom* aAttr) {
nsDependentAtomString attrString(aAttr);
if (persist.Find(attrString) >= 0) {
nsAutoString value;
nsresult rv = GetPersistentValue(aAttr, value);
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to get persistent state.");
if (NS_SUCCEEDED(rv) && !value.IsEmpty()) {
IgnoredErrorResult err;
docShellElement->SetAttribute(attrString, value, err);
}
}
};
loadValue(nsGkAtoms::screenX);
loadValue(nsGkAtoms::screenY);
loadValue(nsGkAtoms::width);
loadValue(nsGkAtoms::height);
loadValue(nsGkAtoms::sizemode);
}
void
nsXULWindow::SizeShell()
{

View File

@ -34,6 +34,7 @@
#include "nsIWeakReference.h"
#include "nsIWidgetListener.h"
#include "nsITabParent.h"
#include "nsIXULStore.h"
namespace mozilla {
namespace dom {
@ -41,6 +42,8 @@ class Element;
} // namespace dom
} // namespace mozilla
class nsAtom;
// nsXULWindow
#define NS_XULWINDOW_IMPL_CID \
@ -142,6 +145,12 @@ protected:
void PersistentAttributesDirty(uint32_t aDirtyFlags);
nsresult GetTabCount(uint32_t* aResult);
void LoadPersistentWindowState();
nsresult GetPersistentValue(const nsAtom* aAttr,
nsAString& aValue);
nsresult SetPersistentValue(const nsAtom* aAttr,
const nsAString& aValue);
nsChromeTreeOwner* mChromeTreeOwner;
nsContentTreeOwner* mContentTreeOwner;
nsContentTreeOwner* mPrimaryContentTreeOwner;
@ -157,6 +166,7 @@ protected:
bool mContinueModalLoop;
bool mDebuting; // being made visible right now
bool mChromeLoaded; // True when chrome has loaded
bool mPersistentWindowStateLoaded;
bool mSizingShellFromXUL; // true when in SizeShell()
bool mShowAfterLoad;
bool mIntrinsicallySized;
@ -186,6 +196,7 @@ private:
MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult GetPrimaryTabParentSize(int32_t* aWidth, int32_t* aHeight);
nsresult GetPrimaryContentShellSize(int32_t* aWidth, int32_t* aHeight);
nsresult SetPrimaryTabParentSize(int32_t aWidth, int32_t aHeight);
nsCOMPtr<nsIXULStore> mLocalStore;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsXULWindow, NS_XULWINDOW_IMPL_CID)