зеркало из https://github.com/mozilla/pjs.git
Merge mozilla-inbound to mozilla-central
This commit is contained in:
Коммит
0a636f7669
|
@ -1920,9 +1920,7 @@ SessionStoreService.prototype = {
|
||||||
catch (ex) { debug(ex); }
|
catch (ex) { debug(ex); }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aEntry.docIdentifier) {
|
entry.docIdentifier = aEntry.BFCacheEntry.ID;
|
||||||
entry.docIdentifier = aEntry.docIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aEntry.stateData != null) {
|
if (aEntry.stateData != null) {
|
||||||
entry.structuredCloneState = aEntry.stateData.getDataAsBase64();
|
entry.structuredCloneState = aEntry.stateData.getDataAsBase64();
|
||||||
|
@ -3024,16 +3022,11 @@ SessionStoreService.prototype = {
|
||||||
browser.webNavigation.setCurrentURI(this._getURIFromString("about:blank"));
|
browser.webNavigation.setCurrentURI(this._getURIFromString("about:blank"));
|
||||||
// Attach data that will be restored on "load" event, after tab is restored.
|
// Attach data that will be restored on "load" event, after tab is restored.
|
||||||
if (activeIndex > -1) {
|
if (activeIndex > -1) {
|
||||||
let curSHEntry = browser.webNavigation.sessionHistory.
|
|
||||||
getEntryAtIndex(activeIndex, false).
|
|
||||||
QueryInterface(Ci.nsISHEntry);
|
|
||||||
|
|
||||||
// restore those aspects of the currently active documents which are not
|
// restore those aspects of the currently active documents which are not
|
||||||
// preserved in the plain history entries (mainly scroll state and text data)
|
// preserved in the plain history entries (mainly scroll state and text data)
|
||||||
browser.__SS_restore_data = tabData.entries[activeIndex] || {};
|
browser.__SS_restore_data = tabData.entries[activeIndex] || {};
|
||||||
browser.__SS_restore_pageStyle = tabData.pageStyle || "";
|
browser.__SS_restore_pageStyle = tabData.pageStyle || "";
|
||||||
browser.__SS_restore_tab = aTab;
|
browser.__SS_restore_tab = aTab;
|
||||||
browser.__SS_restore_docIdentifier = curSHEntry.docIdentifier;
|
|
||||||
didStartLoad = true;
|
didStartLoad = true;
|
||||||
try {
|
try {
|
||||||
// In order to work around certain issues in session history, we need to
|
// In order to work around certain issues in session history, we need to
|
||||||
|
@ -3188,24 +3181,16 @@ SessionStoreService.prototype = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aEntry.docIdentifier) {
|
if (aEntry.docIdentifier) {
|
||||||
// Get a new document identifier for this entry to ensure that history
|
// If we have a serialized document identifier, try to find an SHEntry
|
||||||
// entries after a session restore are considered to have different
|
// which matches that doc identifier and adopt that SHEntry's
|
||||||
// documents from the history entries before the session restore.
|
// BFCacheEntry. If we don't find a match, insert shEntry as the match
|
||||||
// Document identifiers are 64-bit ints, so JS will loose precision and
|
// for the document identifier.
|
||||||
// start assigning all entries the same doc identifier if these ever get
|
let matchingEntry = aDocIdentMap[aEntry.docIdentifier];
|
||||||
// large enough.
|
if (!matchingEntry) {
|
||||||
//
|
aDocIdentMap[aEntry.docIdentifier] = shEntry;
|
||||||
// It's a potential security issue if document identifiers aren't
|
|
||||||
// globally unique, but shEntry.setUniqueDocIdentifier() below guarantees
|
|
||||||
// that we won't re-use a doc identifier within a given instance of the
|
|
||||||
// application.
|
|
||||||
let ident = aDocIdentMap[aEntry.docIdentifier];
|
|
||||||
if (!ident) {
|
|
||||||
shEntry.setUniqueDocIdentifier();
|
|
||||||
aDocIdentMap[aEntry.docIdentifier] = shEntry.docIdentifier;
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
shEntry.docIdentifier = ident;
|
shEntry.adoptBFCacheEntry(matchingEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3341,19 +3326,12 @@ SessionStoreService.prototype = {
|
||||||
aBrowser.markupDocumentViewer.authorStyleDisabled = selectedPageStyle == "_nostyle";
|
aBrowser.markupDocumentViewer.authorStyleDisabled = selectedPageStyle == "_nostyle";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aBrowser.__SS_restore_docIdentifier) {
|
|
||||||
let sh = aBrowser.webNavigation.sessionHistory;
|
|
||||||
sh.getEntryAtIndex(sh.index, false).QueryInterface(Ci.nsISHEntry).
|
|
||||||
docIdentifier = aBrowser.__SS_restore_docIdentifier;
|
|
||||||
}
|
|
||||||
|
|
||||||
// notify the tabbrowser that this document has been completely restored
|
// notify the tabbrowser that this document has been completely restored
|
||||||
this._sendTabRestoredNotification(aBrowser.__SS_restore_tab);
|
this._sendTabRestoredNotification(aBrowser.__SS_restore_tab);
|
||||||
|
|
||||||
delete aBrowser.__SS_restore_data;
|
delete aBrowser.__SS_restore_data;
|
||||||
delete aBrowser.__SS_restore_pageStyle;
|
delete aBrowser.__SS_restore_pageStyle;
|
||||||
delete aBrowser.__SS_restore_tab;
|
delete aBrowser.__SS_restore_tab;
|
||||||
delete aBrowser.__SS_restore_docIdentifier;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -116,13 +116,13 @@ function test() {
|
||||||
|
|
||||||
// After these push/replaceState calls, the window should have three
|
// After these push/replaceState calls, the window should have three
|
||||||
// history entries:
|
// history entries:
|
||||||
// testURL (state object: null) <-- oldest
|
// testURL (state object: null) <-- oldest
|
||||||
// testURL (state object: {obj1:1})
|
// testURL (state object: {obj1:1})
|
||||||
// page2 (state object: {obj3:/^a$/}) <-- newest
|
// testURL?page2 (state object: {obj3:/^a$/}) <-- newest
|
||||||
let contentWindow = tab.linkedBrowser.contentWindow;
|
let contentWindow = tab.linkedBrowser.contentWindow;
|
||||||
let history = contentWindow.history;
|
let history = contentWindow.history;
|
||||||
history.pushState({obj1:1}, "title-obj1");
|
history.pushState({obj1:1}, "title-obj1");
|
||||||
history.pushState({obj2:2}, "title-obj2", "page2");
|
history.pushState({obj2:2}, "title-obj2", "?page2");
|
||||||
history.replaceState({obj3:/^a$/}, "title-obj3");
|
history.replaceState({obj3:/^a$/}, "title-obj3");
|
||||||
|
|
||||||
let state = ss.getTabState(tab);
|
let state = ss.getTabState(tab);
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
%filter substitution
|
%filter substitution
|
||||||
%define toolbarHighlight rgba(255,255,255,.5)
|
%define toolbarHighlight rgba(255,255,255,.5)
|
||||||
%define navbarTextboxCustomBorder border-color: rgba(0,0,0,.32);
|
|
||||||
%define customToolbarColor hsl(214,44%,87%)
|
%define customToolbarColor hsl(214,44%,87%)
|
||||||
|
|
||||||
#placesView {
|
#placesView {
|
||||||
|
@ -71,21 +70,10 @@
|
||||||
|
|
||||||
#searchFilter {
|
#searchFilter {
|
||||||
-moz-appearance: none;
|
-moz-appearance: none;
|
||||||
background-color: rgba(255,255,255,.725);
|
|
||||||
color: black;
|
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
-moz-padding-start: 4px;
|
-moz-padding-start: 4px;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
border: 1px solid ThreeDDarkShadow;
|
border: 1px solid rgba(0,0,0,.32);
|
||||||
border-radius: 2.5px;
|
border-radius: 2.5px;
|
||||||
@navbarTextboxCustomBorder@
|
|
||||||
}
|
|
||||||
|
|
||||||
#searchFilter:hover {
|
|
||||||
background-color: rgba(255,255,255,.898);
|
|
||||||
}
|
|
||||||
|
|
||||||
#searchFilter[focused] {
|
|
||||||
background-color: white;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -312,7 +312,6 @@ MOZ_OPTIMIZE_LDFLAGS = @MOZ_OPTIMIZE_LDFLAGS@
|
||||||
MOZ_OPTIMIZE_SIZE_TWEAK = @MOZ_OPTIMIZE_SIZE_TWEAK@
|
MOZ_OPTIMIZE_SIZE_TWEAK = @MOZ_OPTIMIZE_SIZE_TWEAK@
|
||||||
|
|
||||||
MOZ_RTTI_FLAGS_ON = @_MOZ_RTTI_FLAGS_ON@
|
MOZ_RTTI_FLAGS_ON = @_MOZ_RTTI_FLAGS_ON@
|
||||||
MOZ_EXCEPTIONS_FLAGS_ON = @_MOZ_EXCEPTIONS_FLAGS_ON@
|
|
||||||
|
|
||||||
PROFILE_GEN_CFLAGS = @PROFILE_GEN_CFLAGS@
|
PROFILE_GEN_CFLAGS = @PROFILE_GEN_CFLAGS@
|
||||||
PROFILE_GEN_LDFLAGS = @PROFILE_GEN_LDFLAGS@
|
PROFILE_GEN_LDFLAGS = @PROFILE_GEN_LDFLAGS@
|
||||||
|
|
|
@ -362,9 +362,6 @@ endif
|
||||||
|
|
||||||
endif # !GNU_CC
|
endif # !GNU_CC
|
||||||
|
|
||||||
ifdef ENABLE_CXX_EXCEPTIONS
|
|
||||||
CXXFLAGS += $(MOZ_EXCEPTIONS_FLAGS_ON) -DMOZ_CPP_EXCEPTIONS=1
|
|
||||||
endif # ENABLE_CXX_EXCEPTIONS
|
|
||||||
endif # WINNT
|
endif # WINNT
|
||||||
|
|
||||||
ifeq ($(SOLARIS_SUNPRO_CXX),1)
|
ifeq ($(SOLARIS_SUNPRO_CXX),1)
|
||||||
|
|
29
configure.in
29
configure.in
|
@ -755,8 +755,6 @@ case "$target" in
|
||||||
|
|
||||||
_MOZ_RTTI_FLAGS_ON='-GR'
|
_MOZ_RTTI_FLAGS_ON='-GR'
|
||||||
_MOZ_RTTI_FLAGS_OFF='-GR-'
|
_MOZ_RTTI_FLAGS_OFF='-GR-'
|
||||||
_MOZ_EXCEPTIONS_FLAGS_ON='-EHsc'
|
|
||||||
_MOZ_EXCEPTIONS_FLAGS_OFF=''
|
|
||||||
AC_DEFINE(HAVE_SEH_EXCEPTIONS)
|
AC_DEFINE(HAVE_SEH_EXCEPTIONS)
|
||||||
|
|
||||||
if test -n "$WIN32_REDIST_DIR"; then
|
if test -n "$WIN32_REDIST_DIR"; then
|
||||||
|
@ -1545,8 +1543,6 @@ if test "$GNU_CC"; then
|
||||||
ASFLAGS="$ASFLAGS -fPIC"
|
ASFLAGS="$ASFLAGS -fPIC"
|
||||||
_MOZ_RTTI_FLAGS_ON=${_COMPILER_PREFIX}-frtti
|
_MOZ_RTTI_FLAGS_ON=${_COMPILER_PREFIX}-frtti
|
||||||
_MOZ_RTTI_FLAGS_OFF=${_COMPILER_PREFIX}-fno-rtti
|
_MOZ_RTTI_FLAGS_OFF=${_COMPILER_PREFIX}-fno-rtti
|
||||||
_MOZ_EXCEPTIONS_FLAGS_ON='-fexceptions'
|
|
||||||
_MOZ_EXCEPTIONS_FLAGS_OFF='-fno-exceptions'
|
|
||||||
|
|
||||||
# Turn on GNU specific features
|
# Turn on GNU specific features
|
||||||
# -Wall - turn on all warnings
|
# -Wall - turn on all warnings
|
||||||
|
@ -1604,7 +1600,7 @@ fi
|
||||||
|
|
||||||
if test "$GNU_CXX"; then
|
if test "$GNU_CXX"; then
|
||||||
# FIXME: Let us build with strict aliasing. bug 414641.
|
# FIXME: Let us build with strict aliasing. bug 414641.
|
||||||
CXXFLAGS="$CXXFLAGS -fno-strict-aliasing"
|
CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-strict-aliasing"
|
||||||
# Turn on GNU specific features
|
# Turn on GNU specific features
|
||||||
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wsynth -Wno-ctor-dtor-privacy -Wno-non-virtual-dtor"
|
_WARNINGS_CXXFLAGS="${_WARNINGS_CXXFLAGS} -Wall -Wpointer-arith -Woverloaded-virtual -Wsynth -Wno-ctor-dtor-privacy -Wno-non-virtual-dtor"
|
||||||
if test -z "$INTEL_CXX" -a -z "$CLANG_CXX"; then
|
if test -z "$INTEL_CXX" -a -z "$CLANG_CXX"; then
|
||||||
|
@ -2490,9 +2486,7 @@ ia64*-hpux*)
|
||||||
LIBS="-lCrun -lCstd -lc $LIBS"
|
LIBS="-lCrun -lCstd -lc $LIBS"
|
||||||
AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES)
|
AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES)
|
||||||
CFLAGS="$CFLAGS -xlibmieee -xstrconst -xbuiltin=%all -D__FUNCTION__=__func__"
|
CFLAGS="$CFLAGS -xlibmieee -xstrconst -xbuiltin=%all -D__FUNCTION__=__func__"
|
||||||
CXXFLAGS="$CXXFLAGS -xlibmieee -xbuiltin=%all -features=tmplife,tmplrefstatic,extensions -norunpath -D__FUNCTION__=__func__ -template=no%extdef"
|
CXXFLAGS="$CXXFLAGS -xlibmieee -xbuiltin=%all -features=tmplife,tmplrefstatic,extensions,no%except -norunpath -D__FUNCTION__=__func__ -template=no%extdef"
|
||||||
_MOZ_EXCEPTIONS_FLAGS_ON='-features=except'
|
|
||||||
_MOZ_EXCEPTIONS_FLAGS_OFF='-features=no%except'
|
|
||||||
LDFLAGS="-xildoff $LDFLAGS"
|
LDFLAGS="-xildoff $LDFLAGS"
|
||||||
if test -z "$CROSS_COMPILE" -a -f /usr/lib/ld/map.noexstk; then
|
if test -z "$CROSS_COMPILE" -a -f /usr/lib/ld/map.noexstk; then
|
||||||
_SAVE_LDFLAGS=$LDFLAGS
|
_SAVE_LDFLAGS=$LDFLAGS
|
||||||
|
@ -7747,24 +7741,6 @@ if test "$ac_nscap_nonconst_opeq_bug" = "yes" ; then
|
||||||
fi
|
fi
|
||||||
fi # ! SKIP_COMPILER_CHECKS
|
fi # ! SKIP_COMPILER_CHECKS
|
||||||
|
|
||||||
dnl ========================================================
|
|
||||||
dnl C++ exceptions (g++/VC/Sun only - for now)
|
|
||||||
dnl Should be smarter and check that the compiler does indeed have exceptions
|
|
||||||
dnl ========================================================
|
|
||||||
MOZ_ARG_ENABLE_BOOL(cpp-exceptions,
|
|
||||||
[ --enable-cpp-exceptions Enable C++ exceptions ],
|
|
||||||
[ _MOZ_CPP_EXCEPTIONS=1 ],
|
|
||||||
[ _MOZ_CPP_EXCEPTIONS= ])
|
|
||||||
|
|
||||||
if test "$_MOZ_CPP_EXCEPTIONS"; then
|
|
||||||
_MOZ_EXCEPTIONS_FLAGS=$_MOZ_EXCEPTIONS_FLAGS_ON
|
|
||||||
AC_DEFINE(MOZ_CPP_EXCEPTIONS)
|
|
||||||
else
|
|
||||||
_MOZ_EXCEPTIONS_FLAGS=$_MOZ_EXCEPTIONS_FLAGS_OFF
|
|
||||||
fi
|
|
||||||
|
|
||||||
AC_SUBST(_MOZ_EXCEPTIONS_FLAGS_ON)
|
|
||||||
|
|
||||||
AC_DEFINE(CPP_THROW_NEW, [throw()])
|
AC_DEFINE(CPP_THROW_NEW, [throw()])
|
||||||
AC_LANG_C
|
AC_LANG_C
|
||||||
|
|
||||||
|
@ -8421,7 +8397,6 @@ CFLAGS=`echo \
|
||||||
|
|
||||||
CXXFLAGS=`echo \
|
CXXFLAGS=`echo \
|
||||||
$_MOZ_RTTI_FLAGS \
|
$_MOZ_RTTI_FLAGS \
|
||||||
$_MOZ_EXCEPTIONS_FLAGS \
|
|
||||||
$_WARNINGS_CXXFLAGS \
|
$_WARNINGS_CXXFLAGS \
|
||||||
$CXXFLAGS`
|
$CXXFLAGS`
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,7 @@
|
||||||
#include "nsIAnimationFrameListener.h"
|
#include "nsIAnimationFrameListener.h"
|
||||||
#include "nsEventStates.h"
|
#include "nsEventStates.h"
|
||||||
#include "nsIStructuredCloneContainer.h"
|
#include "nsIStructuredCloneContainer.h"
|
||||||
|
#include "nsIBFCacheEntry.h"
|
||||||
#include "nsDOMMemoryReporter.h"
|
#include "nsDOMMemoryReporter.h"
|
||||||
|
|
||||||
class nsIContent;
|
class nsIContent;
|
||||||
|
@ -125,8 +126,8 @@ class Element;
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|
||||||
#define NS_IDOCUMENT_IID \
|
#define NS_IDOCUMENT_IID \
|
||||||
{ 0x4114a7c7, 0xb2f4, 0x4dea, \
|
{ 0x448c396a, 0x013c, 0x47b8, \
|
||||||
{ 0xac, 0x78, 0x20, 0xab, 0xda, 0x6f, 0xb2, 0xaf } }
|
{ 0x95, 0xf4, 0x56, 0x68, 0x0f, 0x5f, 0x12, 0xf8 } }
|
||||||
|
|
||||||
// Flag for AddStyleSheet().
|
// Flag for AddStyleSheet().
|
||||||
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
|
#define NS_STYLESHEET_FROM_CATALOG (1 << 0)
|
||||||
|
@ -479,11 +480,15 @@ public:
|
||||||
return GetBFCacheEntry() ? nsnull : mPresShell;
|
return GetBFCacheEntry() ? nsnull : mPresShell;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetBFCacheEntry(nsISHEntry* aSHEntry) {
|
void SetBFCacheEntry(nsIBFCacheEntry* aEntry)
|
||||||
mSHEntry = aSHEntry;
|
{
|
||||||
|
mBFCacheEntry = aEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsISHEntry* GetBFCacheEntry() const { return mSHEntry; }
|
nsIBFCacheEntry* GetBFCacheEntry() const
|
||||||
|
{
|
||||||
|
return mBFCacheEntry;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the parent document of this document. Will return null
|
* Return the parent document of this document. Will return null
|
||||||
|
@ -1786,9 +1791,9 @@ protected:
|
||||||
|
|
||||||
AnimationListenerList mAnimationFrameListeners;
|
AnimationListenerList mAnimationFrameListeners;
|
||||||
|
|
||||||
// The session history entry in which we're currently bf-cached. Non-null
|
// This object allows us to evict ourself from the back/forward cache. The
|
||||||
// if and only if we're currently in the bfcache.
|
// pointer is non-null iff we're currently in the bfcache.
|
||||||
nsISHEntry* mSHEntry;
|
nsIBFCacheEntry *mBFCacheEntry;
|
||||||
|
|
||||||
// Our base target.
|
// Our base target.
|
||||||
nsString mBaseTarget;
|
nsString mBaseTarget;
|
||||||
|
|
|
@ -4126,10 +4126,10 @@ nsDocShell::LoadErrorPage(nsIURI *aURI, const PRUnichar *aURL,
|
||||||
mFailedLoadType = mLoadType;
|
mFailedLoadType = mLoadType;
|
||||||
|
|
||||||
if (mLSHE) {
|
if (mLSHE) {
|
||||||
// If we don't give mLSHE a new doc identifier here, when we go back or
|
// Abandon mLSHE's BFCache entry and create a new one. This way, if
|
||||||
// forward to another SHEntry with the same doc identifier, the error
|
// we go back or forward to another SHEntry with the same doc
|
||||||
// page will persist.
|
// identifier, the error page won't persist.
|
||||||
mLSHE->SetUniqueDocIdentifier();
|
mLSHE->AbandonBFCacheEntry();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsCAutoString url;
|
nsCAutoString url;
|
||||||
|
@ -4402,10 +4402,10 @@ nsDocShell::LoadPage(nsISupports *aPageDescriptor, PRUint32 aDisplayType)
|
||||||
nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
|
nsresult rv = shEntryIn->Clone(getter_AddRefs(shEntry));
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
// Give our cloned shEntry a new document identifier so this load is
|
// Give our cloned shEntry a new bfcache entry so this load is independent
|
||||||
// independent of all other loads. (This is important, in particular,
|
// of all other loads. (This is important, in particular, for bugs 582795
|
||||||
// for bugs 582795 and 585298.)
|
// and 585298.)
|
||||||
rv = shEntry->SetUniqueDocIdentifier();
|
rv = shEntry->AbandonBFCacheEntry();
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
//
|
//
|
||||||
|
@ -8280,17 +8280,15 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||||
NS_SUCCEEDED(splitRv2) &&
|
NS_SUCCEEDED(splitRv2) &&
|
||||||
curBeforeHash.Equals(newBeforeHash);
|
curBeforeHash.Equals(newBeforeHash);
|
||||||
|
|
||||||
bool sameDocIdent = false;
|
// XXX rename
|
||||||
|
bool sameDocument = false;
|
||||||
if (mOSHE && aSHEntry) {
|
if (mOSHE && aSHEntry) {
|
||||||
// We're doing a history load.
|
// We're doing a history load.
|
||||||
|
|
||||||
PRUint64 ourDocIdent, otherDocIdent;
|
mOSHE->SharesDocumentWith(aSHEntry, &sameDocument);
|
||||||
mOSHE->GetDocIdentifier(&ourDocIdent);
|
|
||||||
aSHEntry->GetDocIdentifier(&otherDocIdent);
|
|
||||||
sameDocIdent = (ourDocIdent == otherDocIdent);
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
if (sameDocIdent) {
|
if (sameDocument) {
|
||||||
nsCOMPtr<nsIInputStream> currentPostData;
|
nsCOMPtr<nsIInputStream> currentPostData;
|
||||||
mOSHE->GetPostData(getter_AddRefs(currentPostData));
|
mOSHE->GetPostData(getter_AddRefs(currentPostData));
|
||||||
NS_ASSERTION(currentPostData == aPostData,
|
NS_ASSERTION(currentPostData == aPostData,
|
||||||
|
@ -8313,7 +8311,7 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||||
// The restriction tha the SHEntries in (a) must be different ensures
|
// The restriction tha the SHEntries in (a) must be different ensures
|
||||||
// that history.go(0) and the like trigger full refreshes, rather than
|
// that history.go(0) and the like trigger full refreshes, rather than
|
||||||
// short-circuited loads.
|
// short-circuited loads.
|
||||||
bool doShortCircuitedLoad = (sameDocIdent && mOSHE != aSHEntry) ||
|
bool doShortCircuitedLoad = (sameDocument && mOSHE != aSHEntry) ||
|
||||||
(!aSHEntry && aPostData == nsnull &&
|
(!aSHEntry && aPostData == nsnull &&
|
||||||
sameExceptHashes && !newHash.IsEmpty());
|
sameExceptHashes && !newHash.IsEmpty());
|
||||||
|
|
||||||
|
@ -8364,7 +8362,6 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||||
OnNewURI(aURI, nsnull, owner, mLoadType, true, true, true);
|
OnNewURI(aURI, nsnull, owner, mLoadType, true, true, true);
|
||||||
|
|
||||||
nsCOMPtr<nsIInputStream> postData;
|
nsCOMPtr<nsIInputStream> postData;
|
||||||
PRUint64 docIdent = PRUint64(-1);
|
|
||||||
nsCOMPtr<nsISupports> cacheKey;
|
nsCOMPtr<nsISupports> cacheKey;
|
||||||
|
|
||||||
if (mOSHE) {
|
if (mOSHE) {
|
||||||
|
@ -8378,8 +8375,13 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||||
// wouldn't want here.
|
// wouldn't want here.
|
||||||
if (aLoadType & LOAD_CMD_NORMAL) {
|
if (aLoadType & LOAD_CMD_NORMAL) {
|
||||||
mOSHE->GetPostData(getter_AddRefs(postData));
|
mOSHE->GetPostData(getter_AddRefs(postData));
|
||||||
mOSHE->GetDocIdentifier(&docIdent);
|
|
||||||
mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
|
mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
|
||||||
|
|
||||||
|
// Link our new SHEntry to the old SHEntry's back/forward
|
||||||
|
// cache data, since the two SHEntries correspond to the
|
||||||
|
// same document.
|
||||||
|
if (mLSHE)
|
||||||
|
mLSHE->AdoptBFCacheEntry(mOSHE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8399,11 +8401,6 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||||
// cache first
|
// cache first
|
||||||
if (cacheKey)
|
if (cacheKey)
|
||||||
mOSHE->SetCacheKey(cacheKey);
|
mOSHE->SetCacheKey(cacheKey);
|
||||||
|
|
||||||
// Propagate our document identifier to the new mOSHE so that
|
|
||||||
// we'll know it's related by an anchor navigation or pushState.
|
|
||||||
if (docIdent != PRUint64(-1))
|
|
||||||
mOSHE->SetDocIdentifier(docIdent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* restore previous position of scroller(s), if we're moving
|
/* restore previous position of scroller(s), if we're moving
|
||||||
|
@ -8447,7 +8444,7 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sameDocIdent) {
|
if (sameDocument) {
|
||||||
// Set the doc's URI according to the new history entry's URI
|
// Set the doc's URI according to the new history entry's URI
|
||||||
nsCOMPtr<nsIURI> newURI;
|
nsCOMPtr<nsIURI> newURI;
|
||||||
mOSHE->GetURI(getter_AddRefs(newURI));
|
mOSHE->GetURI(getter_AddRefs(newURI));
|
||||||
|
@ -8464,10 +8461,10 @@ nsDocShell::InternalLoad(nsIURI * aURI,
|
||||||
// Dispatch the popstate and hashchange events, as appropriate.
|
// Dispatch the popstate and hashchange events, as appropriate.
|
||||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobal);
|
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mScriptGlobal);
|
||||||
if (window) {
|
if (window) {
|
||||||
// Need the doHashchange check here since sameDocIdent is
|
// Need the doHashchange check here since sameDocument is
|
||||||
// false if we're navigating to a new shentry (i.e. a aSHEntry
|
// false if we're navigating to a new shentry (i.e. a aSHEntry
|
||||||
// is null), such as when clicking a <a href="#foo">.
|
// is null), such as when clicking a <a href="#foo">.
|
||||||
if (sameDocIdent || doHashchange) {
|
if (sameDocument || doHashchange) {
|
||||||
window->DispatchSyncPopState();
|
window->DispatchSyncPopState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9250,11 +9247,11 @@ nsDocShell::OnNewURI(nsIURI * aURI, nsIChannel * aChannel, nsISupports* aOwner,
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the response status indicates an error, unlink this session
|
// If the response status indicates an error, unlink this session
|
||||||
// history entry from any entries sharing its doc ident.
|
// history entry from any entries sharing its document.
|
||||||
PRUint32 responseStatus;
|
PRUint32 responseStatus;
|
||||||
nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
|
nsresult rv = httpChannel->GetResponseStatus(&responseStatus);
|
||||||
if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
|
if (mLSHE && NS_SUCCEEDED(rv) && responseStatus >= 400) {
|
||||||
mLSHE->SetUniqueDocIdentifier();
|
mLSHE->AbandonBFCacheEntry();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9483,9 +9480,9 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
|
||||||
// It's important that this function not run arbitrary scripts after step 1
|
// It's important that this function not run arbitrary scripts after step 1
|
||||||
// and before completing step 5. For example, if a script called
|
// and before completing step 5. For example, if a script called
|
||||||
// history.back() before we completed step 5, bfcache might destroy an
|
// history.back() before we completed step 5, bfcache might destroy an
|
||||||
// active content viewer. Since EvictContentViewers at the end of step 5
|
// active content viewer. Since EvictOutOfRangeContentViewers at the end of
|
||||||
// might run script, we can't just put a script blocker around the critical
|
// step 5 might run script, we can't just put a script blocker around the
|
||||||
// section.
|
// critical section.
|
||||||
//
|
//
|
||||||
// Note that we completely ignore the aTitle parameter.
|
// Note that we completely ignore the aTitle parameter.
|
||||||
|
|
||||||
|
@ -9665,11 +9662,9 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
|
||||||
|
|
||||||
NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
|
NS_ENSURE_TRUE(newSHEntry, NS_ERROR_FAILURE);
|
||||||
|
|
||||||
// Set the new SHEntry's document identifier, if we can.
|
// Link the new SHEntry to the old SHEntry's BFCache entry, since the
|
||||||
PRUint64 ourDocIdent;
|
// two entries correspond to the same document.
|
||||||
NS_ENSURE_SUCCESS(oldOSHE->GetDocIdentifier(&ourDocIdent),
|
NS_ENSURE_SUCCESS(newSHEntry->AdoptBFCacheEntry(oldOSHE),
|
||||||
NS_ERROR_FAILURE);
|
|
||||||
NS_ENSURE_SUCCESS(newSHEntry->SetDocIdentifier(ourDocIdent),
|
|
||||||
NS_ERROR_FAILURE);
|
NS_ERROR_FAILURE);
|
||||||
|
|
||||||
// Set the new SHEntry's title (bug 655273).
|
// Set the new SHEntry's title (bug 655273).
|
||||||
|
@ -9715,7 +9710,7 @@ nsDocShell::AddState(nsIVariant *aData, const nsAString& aTitle,
|
||||||
PRInt32 curIndex = -1;
|
PRInt32 curIndex = -1;
|
||||||
rv = rootSH->GetIndex(&curIndex);
|
rv = rootSH->GetIndex(&curIndex);
|
||||||
if (NS_SUCCEEDED(rv) && curIndex > -1) {
|
if (NS_SUCCEEDED(rv) && curIndex > -1) {
|
||||||
internalSH->EvictContentViewers(curIndex - 1, curIndex);
|
internalSH->EvictOutOfRangeContentViewers(curIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -66,6 +66,7 @@
|
||||||
|
|
||||||
// session history
|
// session history
|
||||||
#include "nsSHEntry.h"
|
#include "nsSHEntry.h"
|
||||||
|
#include "nsSHEntryShared.h"
|
||||||
#include "nsSHistory.h"
|
#include "nsSHistory.h"
|
||||||
#include "nsSHTransaction.h"
|
#include "nsSHTransaction.h"
|
||||||
|
|
||||||
|
@ -87,15 +88,15 @@ Initialize()
|
||||||
nsresult rv = nsSHistory::Startup();
|
nsresult rv = nsSHistory::Startup();
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
rv = nsSHEntry::Startup();
|
nsSHEntryShared::Startup();
|
||||||
return rv;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
Shutdown()
|
Shutdown()
|
||||||
{
|
{
|
||||||
nsSHistory::Shutdown();
|
nsSHistory::Shutdown();
|
||||||
nsSHEntry::Shutdown();
|
nsSHEntryShared::Shutdown();
|
||||||
gInitialized = false;
|
gInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -56,6 +56,7 @@ XPIDLSRCS = \
|
||||||
nsISHContainer.idl \
|
nsISHContainer.idl \
|
||||||
nsISHTransaction.idl \
|
nsISHTransaction.idl \
|
||||||
nsISHistoryInternal.idl \
|
nsISHistoryInternal.idl \
|
||||||
|
nsIBFCacheEntry.idl \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
include $(topsrcdir)/config/rules.mk
|
include $(topsrcdir)/config/rules.mk
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is mozilla.org code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
||||||
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#include "nsISupports.idl"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface lets you evict a document from the back/forward cache.
|
||||||
|
*/
|
||||||
|
[scriptable, uuid(a576060e-c7df-4d81-aa8c-5b52bd6ad25d)]
|
||||||
|
interface nsIBFCacheEntry : nsISupports
|
||||||
|
{
|
||||||
|
void RemoveFromBFCacheSync();
|
||||||
|
void RemoveFromBFCacheAsync();
|
||||||
|
|
||||||
|
readonly attribute unsigned long long ID;
|
||||||
|
};
|
|
@ -51,15 +51,18 @@ interface nsIInputStream;
|
||||||
interface nsIDocShellTreeItem;
|
interface nsIDocShellTreeItem;
|
||||||
interface nsISupportsArray;
|
interface nsISupportsArray;
|
||||||
interface nsIStructuredCloneContainer;
|
interface nsIStructuredCloneContainer;
|
||||||
|
interface nsIBFCacheEntry;
|
||||||
|
|
||||||
%{C++
|
%{C++
|
||||||
struct nsIntRect;
|
struct nsIntRect;
|
||||||
class nsDocShellEditorData;
|
class nsDocShellEditorData;
|
||||||
|
class nsSHEntryShared;
|
||||||
%}
|
%}
|
||||||
[ref] native nsIntRect(nsIntRect);
|
[ref] native nsIntRect(nsIntRect);
|
||||||
[ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
|
[ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
|
||||||
|
[ptr] native nsSHEntryShared(nsSHEntryShared);
|
||||||
|
|
||||||
|
[scriptable, uuid(6443FD72-A50F-4B8B-BB82-BB1FA04CB15D)]
|
||||||
[scriptable, uuid(b92d403e-f5ec-4b81-b0e3-6e6c241cef2d)]
|
|
||||||
interface nsISHEntry : nsIHistoryEntry
|
interface nsISHEntry : nsIHistoryEntry
|
||||||
{
|
{
|
||||||
/** URI for the document */
|
/** URI for the document */
|
||||||
|
@ -140,21 +143,6 @@ interface nsISHEntry : nsIHistoryEntry
|
||||||
*/
|
*/
|
||||||
attribute unsigned long ID;
|
attribute unsigned long ID;
|
||||||
|
|
||||||
/**
|
|
||||||
* docIdentifier is an integer that should be the same for two entries
|
|
||||||
* attached to the same docshell if and only if the two entries are entries
|
|
||||||
* for the same document. In practice, two entries A and B will have the
|
|
||||||
* same docIdentifier if we arrived at B by clicking an anchor link in A or
|
|
||||||
* if B was created by A's calling history.pushState().
|
|
||||||
*/
|
|
||||||
attribute unsigned long long docIdentifier;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes this entry's doc identifier to a new value which is unique
|
|
||||||
* among those of all other entries.
|
|
||||||
*/
|
|
||||||
void setUniqueDocIdentifier();
|
|
||||||
|
|
||||||
/** attribute to set and get the cache key for the entry */
|
/** attribute to set and get the cache key for the entry */
|
||||||
attribute nsISupports cacheKey;
|
attribute nsISupports cacheKey;
|
||||||
|
|
||||||
|
@ -252,6 +240,36 @@ interface nsISHEntry : nsIHistoryEntry
|
||||||
* The history ID of the docshell.
|
* The history ID of the docshell.
|
||||||
*/
|
*/
|
||||||
attribute unsigned long long docshellID;
|
attribute unsigned long long docshellID;
|
||||||
|
|
||||||
|
readonly attribute nsIBFCacheEntry BFCacheEntry;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this SHEntry point to the given BFCache entry? If so, evicting
|
||||||
|
* the BFCache entry will evict the SHEntry, since the two entries
|
||||||
|
* correspond to the same document.
|
||||||
|
*/
|
||||||
|
[notxpcom, noscript]
|
||||||
|
boolean hasBFCacheEntry(in nsIBFCacheEntry aEntry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adopt aEntry's BFCacheEntry, so now both this and aEntry point to
|
||||||
|
* aEntry's BFCacheEntry.
|
||||||
|
*/
|
||||||
|
void adoptBFCacheEntry(in nsISHEntry aEntry);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new BFCache entry and drop our reference to our old one. This
|
||||||
|
* call unlinks this SHEntry from any other SHEntries for its document.
|
||||||
|
*/
|
||||||
|
void abandonBFCacheEntry();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Does this SHEntry correspond to the same document as aEntry? This is
|
||||||
|
* true iff the two SHEntries have the same BFCacheEntry. So in
|
||||||
|
* particular, sharesDocumentWith(aEntry) is guaranteed to return true if
|
||||||
|
* it's preceeded by a call to adoptBFCacheEntry(aEntry).
|
||||||
|
*/
|
||||||
|
boolean sharesDocumentWith(in nsISHEntry aEntry);
|
||||||
};
|
};
|
||||||
|
|
||||||
[scriptable, uuid(bb66ac35-253b-471f-a317-3ece940f04c5)]
|
[scriptable, uuid(bb66ac35-253b-471f-a317-3ece940f04c5)]
|
||||||
|
@ -264,6 +282,17 @@ interface nsISHEntryInternal : nsISupports
|
||||||
* A number that is assigned by the sHistory when the entry is activated
|
* A number that is assigned by the sHistory when the entry is activated
|
||||||
*/
|
*/
|
||||||
attribute unsigned long lastTouched;
|
attribute unsigned long lastTouched;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some state, particularly that related to the back/forward cache, is
|
||||||
|
* shared between SHEntries which correspond to the same document. This
|
||||||
|
* method gets a pointer to that shared state.
|
||||||
|
*
|
||||||
|
* This shared state is the SHEntry's BFCacheEntry. So
|
||||||
|
* hasBFCacheEntry(getSharedState()) is guaranteed to return true.
|
||||||
|
*/
|
||||||
|
[noscript, notxpcom]
|
||||||
|
nsSHEntryShared getSharedState();
|
||||||
};
|
};
|
||||||
|
|
||||||
%{ C++
|
%{ C++
|
||||||
|
|
|
@ -57,7 +57,7 @@ struct nsTArrayDefaultAllocator;
|
||||||
|
|
||||||
[ref] native nsDocshellIDArray(nsTArray<PRUint64, nsTArrayDefaultAllocator>);
|
[ref] native nsDocshellIDArray(nsTArray<PRUint64, nsTArrayDefaultAllocator>);
|
||||||
|
|
||||||
[scriptable, uuid(2dede933-25e1-47a3-8f61-0127c785ea01)]
|
[scriptable, uuid(e27cf38e-c19f-4294-bd31-d7e0916e7fa2)]
|
||||||
interface nsISHistoryInternal: nsISupports
|
interface nsISHistoryInternal: nsISupports
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -96,20 +96,25 @@ interface nsISHistoryInternal: nsISupports
|
||||||
*/
|
*/
|
||||||
readonly attribute nsISHistoryListener listener;
|
readonly attribute nsISHistoryListener listener;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evict content viewers until the number of content viewers per tab
|
* Evict content viewers which don't lie in the "safe" range around aIndex.
|
||||||
* is no more than gHistoryMaxViewers. Also, count
|
* In practice, this should leave us with no more than gHistoryMaxViewers
|
||||||
* total number of content viewers globally and evict one if we are over
|
* viewers associated with this SHistory object.
|
||||||
* our total max. This is always called in Show(), after we destroy
|
*
|
||||||
* the previous viewer.
|
* Also make sure that the total number of content viewers in all windows is
|
||||||
*/
|
* not greater than our global max; if it is, evict viewers as appropriate.
|
||||||
void evictContentViewers(in long previousIndex, in long index);
|
*
|
||||||
|
* @param aIndex - The index around which the "safe" range is centered. In
|
||||||
|
* general, if you just navigated the history, aIndex should be the index
|
||||||
|
* history was navigated to.
|
||||||
|
*/
|
||||||
|
void evictOutOfRangeContentViewers(in long aIndex);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evict the content viewer associated with a session history entry
|
* Evict the content viewer associated with a bfcache entry
|
||||||
* that has timed out.
|
* that has timed out.
|
||||||
*/
|
*/
|
||||||
void evictExpiredContentViewerForEntry(in nsISHEntry aEntry);
|
void evictExpiredContentViewerForEntry(in nsIBFCacheEntry aEntry);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evict all the content viewers in this session history
|
* Evict all the content viewers in this session history
|
||||||
|
|
|
@ -48,10 +48,13 @@ LIBRARY_NAME = shistory_s
|
||||||
FORCE_STATIC_LIB = 1
|
FORCE_STATIC_LIB = 1
|
||||||
LIBXUL_LIBRARY = 1
|
LIBXUL_LIBRARY = 1
|
||||||
|
|
||||||
|
EXPORTS = nsSHEntryShared.h \
|
||||||
|
$(NULL)
|
||||||
|
|
||||||
CPPSRCS = nsSHEntry.cpp \
|
CPPSRCS = nsSHEntry.cpp \
|
||||||
nsSHTransaction.cpp \
|
nsSHTransaction.cpp \
|
||||||
nsSHistory.cpp \
|
nsSHistory.cpp \
|
||||||
|
nsSHEntryShared.cpp \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
||||||
include $(topsrcdir)/config/rules.mk
|
include $(topsrcdir)/config/rules.mk
|
||||||
|
|
|
@ -36,10 +36,6 @@
|
||||||
*
|
*
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
#ifdef DEBUG_bryner
|
|
||||||
#define DEBUG_PAGE_CACHE
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Local Includes
|
// Local Includes
|
||||||
#include "nsSHEntry.h"
|
#include "nsSHEntry.h"
|
||||||
#include "nsXPIDLString.h"
|
#include "nsXPIDLString.h"
|
||||||
|
@ -48,103 +44,44 @@
|
||||||
#include "nsIDocShellTreeItem.h"
|
#include "nsIDocShellTreeItem.h"
|
||||||
#include "nsIDocument.h"
|
#include "nsIDocument.h"
|
||||||
#include "nsIDOMDocument.h"
|
#include "nsIDOMDocument.h"
|
||||||
#include "nsAutoPtr.h"
|
|
||||||
#include "nsThreadUtils.h"
|
|
||||||
#include "nsIWebNavigation.h"
|
|
||||||
#include "nsISHistory.h"
|
#include "nsISHistory.h"
|
||||||
#include "nsISHistoryInternal.h"
|
#include "nsISHistoryInternal.h"
|
||||||
#include "nsDocShellEditorData.h"
|
#include "nsDocShellEditorData.h"
|
||||||
#include "nsIDocShell.h"
|
#include "nsSHEntryShared.h"
|
||||||
|
#include "nsILayoutHistoryState.h"
|
||||||
|
#include "nsIContentViewer.h"
|
||||||
|
#include "nsISupportsArray.h"
|
||||||
|
|
||||||
namespace dom = mozilla::dom;
|
namespace dom = mozilla::dom;
|
||||||
|
|
||||||
// Hardcode this to time out unused content viewers after 30 minutes
|
|
||||||
#define CONTENT_VIEWER_TIMEOUT_SECONDS 30*60
|
|
||||||
|
|
||||||
typedef nsExpirationTracker<nsSHEntry,3> HistoryTrackerBase;
|
|
||||||
class HistoryTracker : public HistoryTrackerBase {
|
|
||||||
public:
|
|
||||||
// Expire cached contentviewers after 20-30 minutes in the cache.
|
|
||||||
HistoryTracker() : HistoryTrackerBase((CONTENT_VIEWER_TIMEOUT_SECONDS/2)*1000) {}
|
|
||||||
|
|
||||||
protected:
|
|
||||||
virtual void NotifyExpired(nsSHEntry* aObj) {
|
|
||||||
RemoveObject(aObj);
|
|
||||||
aObj->Expire();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static HistoryTracker *gHistoryTracker = nsnull;
|
|
||||||
static PRUint32 gEntryID = 0;
|
static PRUint32 gEntryID = 0;
|
||||||
static PRUint64 gEntryDocIdentifier = 0;
|
|
||||||
|
|
||||||
nsresult nsSHEntry::Startup()
|
|
||||||
{
|
|
||||||
gHistoryTracker = new HistoryTracker();
|
|
||||||
return gHistoryTracker ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
|
|
||||||
}
|
|
||||||
|
|
||||||
void nsSHEntry::Shutdown()
|
|
||||||
{
|
|
||||||
delete gHistoryTracker;
|
|
||||||
gHistoryTracker = nsnull;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void StopTrackingEntry(nsSHEntry *aEntry)
|
|
||||||
{
|
|
||||||
if (aEntry->GetExpirationState()->IsTracked()) {
|
|
||||||
gHistoryTracker->RemoveObject(aEntry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//*****************************************************************************
|
//*****************************************************************************
|
||||||
//*** nsSHEntry: Object Management
|
//*** nsSHEntry: Object Management
|
||||||
//*****************************************************************************
|
//*****************************************************************************
|
||||||
|
|
||||||
|
|
||||||
nsSHEntry::nsSHEntry()
|
nsSHEntry::nsSHEntry()
|
||||||
: mLoadType(0)
|
: mLoadType(0)
|
||||||
, mID(gEntryID++)
|
, mID(gEntryID++)
|
||||||
, mDocIdentifier(gEntryDocIdentifier++)
|
|
||||||
, mScrollPositionX(0)
|
, mScrollPositionX(0)
|
||||||
, mScrollPositionY(0)
|
, mScrollPositionY(0)
|
||||||
, mURIWasModified(false)
|
, mURIWasModified(false)
|
||||||
, mIsFrameNavigation(false)
|
|
||||||
, mSaveLayoutState(true)
|
|
||||||
, mExpired(false)
|
|
||||||
, mSticky(true)
|
|
||||||
, mDynamicallyCreated(false)
|
|
||||||
, mParent(nsnull)
|
|
||||||
, mViewerBounds(0, 0, 0, 0)
|
|
||||||
, mDocShellID(0)
|
|
||||||
, mLastTouched(0)
|
|
||||||
{
|
{
|
||||||
|
mShared = new nsSHEntryShared();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsSHEntry::nsSHEntry(const nsSHEntry &other)
|
nsSHEntry::nsSHEntry(const nsSHEntry &other)
|
||||||
: mURI(other.mURI)
|
: mShared(other.mShared)
|
||||||
|
, mURI(other.mURI)
|
||||||
, mReferrerURI(other.mReferrerURI)
|
, mReferrerURI(other.mReferrerURI)
|
||||||
// XXX why not copy mDocument?
|
|
||||||
, mTitle(other.mTitle)
|
, mTitle(other.mTitle)
|
||||||
, mPostData(other.mPostData)
|
, mPostData(other.mPostData)
|
||||||
, mLayoutHistoryState(other.mLayoutHistoryState)
|
|
||||||
, mLoadType(0) // XXX why not copy?
|
, mLoadType(0) // XXX why not copy?
|
||||||
, mID(other.mID)
|
, mID(other.mID)
|
||||||
, mDocIdentifier(other.mDocIdentifier)
|
|
||||||
, mScrollPositionX(0) // XXX why not copy?
|
, mScrollPositionX(0) // XXX why not copy?
|
||||||
, mScrollPositionY(0) // XXX why not copy?
|
, mScrollPositionY(0) // XXX why not copy?
|
||||||
, mURIWasModified(other.mURIWasModified)
|
, mURIWasModified(other.mURIWasModified)
|
||||||
, mIsFrameNavigation(other.mIsFrameNavigation)
|
|
||||||
, mSaveLayoutState(other.mSaveLayoutState)
|
|
||||||
, mExpired(other.mExpired)
|
|
||||||
, mSticky(true)
|
|
||||||
, mDynamicallyCreated(other.mDynamicallyCreated)
|
|
||||||
// XXX why not copy mContentType?
|
|
||||||
, mCacheKey(other.mCacheKey)
|
|
||||||
, mParent(other.mParent)
|
|
||||||
, mViewerBounds(0, 0, 0, 0)
|
|
||||||
, mOwner(other.mOwner)
|
|
||||||
, mDocShellID(other.mDocShellID)
|
|
||||||
, mStateData(other.mStateData)
|
, mStateData(other.mStateData)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -160,37 +97,16 @@ ClearParentPtr(nsISHEntry* aEntry, void* /* aData */)
|
||||||
|
|
||||||
nsSHEntry::~nsSHEntry()
|
nsSHEntry::~nsSHEntry()
|
||||||
{
|
{
|
||||||
StopTrackingEntry(this);
|
// Null out the mParent pointers on all our kids.
|
||||||
|
|
||||||
// Since we never really remove kids from SHEntrys, we need to null
|
|
||||||
// out the mParent pointers on all our kids.
|
|
||||||
mChildren.EnumerateForwards(ClearParentPtr, nsnull);
|
mChildren.EnumerateForwards(ClearParentPtr, nsnull);
|
||||||
mChildren.Clear();
|
|
||||||
|
|
||||||
if (mContentViewer) {
|
|
||||||
// RemoveFromBFCacheSync is virtual, so call the nsSHEntry version
|
|
||||||
// explicitly
|
|
||||||
nsSHEntry::RemoveFromBFCacheSync();
|
|
||||||
}
|
|
||||||
|
|
||||||
mEditorData = nsnull;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
// This is not happening as far as I can tell from breakpad as of early November 2007
|
|
||||||
nsExpirationTracker<nsSHEntry,3>::Iterator iterator(gHistoryTracker);
|
|
||||||
nsSHEntry* elem;
|
|
||||||
while ((elem = iterator.Next()) != nsnull) {
|
|
||||||
NS_ASSERTION(elem != this, "Found dead entry still in the tracker!");
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//*****************************************************************************
|
//*****************************************************************************
|
||||||
// nsSHEntry: nsISupports
|
// nsSHEntry: nsISupports
|
||||||
//*****************************************************************************
|
//*****************************************************************************
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS5(nsSHEntry, nsISHContainer, nsISHEntry, nsIHistoryEntry,
|
NS_IMPL_ISUPPORTS4(nsSHEntry, nsISHContainer, nsISHEntry, nsIHistoryEntry,
|
||||||
nsIMutationObserver, nsISHEntryInternal)
|
nsISHEntryInternal)
|
||||||
|
|
||||||
//*****************************************************************************
|
//*****************************************************************************
|
||||||
// nsSHEntry: nsISHEntry
|
// nsSHEntry: nsISHEntry
|
||||||
|
@ -251,35 +167,13 @@ NS_IMETHODIMP nsSHEntry::SetReferrerURI(nsIURI *aReferrerURI)
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::SetContentViewer(nsIContentViewer *aViewer)
|
nsSHEntry::SetContentViewer(nsIContentViewer *aViewer)
|
||||||
{
|
{
|
||||||
NS_PRECONDITION(!aViewer || !mContentViewer, "SHEntry already contains viewer");
|
return mShared->SetContentViewer(aViewer);
|
||||||
|
|
||||||
if (mContentViewer || !aViewer) {
|
|
||||||
DropPresentationState();
|
|
||||||
}
|
|
||||||
|
|
||||||
mContentViewer = aViewer;
|
|
||||||
|
|
||||||
if (mContentViewer) {
|
|
||||||
gHistoryTracker->AddObject(this);
|
|
||||||
|
|
||||||
nsCOMPtr<nsIDOMDocument> domDoc;
|
|
||||||
mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
|
|
||||||
// Store observed document in strong pointer in case it is removed from
|
|
||||||
// the contentviewer
|
|
||||||
mDocument = do_QueryInterface(domDoc);
|
|
||||||
if (mDocument) {
|
|
||||||
mDocument->SetBFCacheEntry(this);
|
|
||||||
mDocument->AddMutationObserver(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::GetContentViewer(nsIContentViewer **aResult)
|
nsSHEntry::GetContentViewer(nsIContentViewer **aResult)
|
||||||
{
|
{
|
||||||
*aResult = mContentViewer;
|
*aResult = mShared->mContentViewer;
|
||||||
NS_IF_ADDREF(*aResult);
|
NS_IF_ADDREF(*aResult);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -319,14 +213,14 @@ nsSHEntry::GetAnyContentViewer(nsISHEntry **aOwnerEntry,
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::SetSticky(bool aSticky)
|
nsSHEntry::SetSticky(bool aSticky)
|
||||||
{
|
{
|
||||||
mSticky = aSticky;
|
mShared->mSticky = aSticky;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::GetSticky(bool *aSticky)
|
nsSHEntry::GetSticky(bool *aSticky)
|
||||||
{
|
{
|
||||||
*aSticky = mSticky;
|
*aSticky = mShared->mSticky;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -365,16 +259,18 @@ NS_IMETHODIMP nsSHEntry::SetPostData(nsIInputStream* aPostData)
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::GetLayoutHistoryState(nsILayoutHistoryState** aResult)
|
NS_IMETHODIMP nsSHEntry::GetLayoutHistoryState(nsILayoutHistoryState** aResult)
|
||||||
{
|
{
|
||||||
*aResult = mLayoutHistoryState;
|
*aResult = mShared->mLayoutHistoryState;
|
||||||
NS_IF_ADDREF(*aResult);
|
NS_IF_ADDREF(*aResult);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::SetLayoutHistoryState(nsILayoutHistoryState* aState)
|
NS_IMETHODIMP nsSHEntry::SetLayoutHistoryState(nsILayoutHistoryState* aState)
|
||||||
{
|
{
|
||||||
mLayoutHistoryState = aState;
|
mShared->mLayoutHistoryState = aState;
|
||||||
if (mLayoutHistoryState)
|
if (mShared->mLayoutHistoryState) {
|
||||||
mLayoutHistoryState->SetScrollPositionOnly(!mSaveLayoutState);
|
mShared->mLayoutHistoryState->
|
||||||
|
SetScrollPositionOnly(!mShared->mSaveLayoutState);
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -403,91 +299,73 @@ NS_IMETHODIMP nsSHEntry::SetID(PRUint32 aID)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::GetDocIdentifier(PRUint64 * aResult)
|
nsSHEntryShared* nsSHEntry::GetSharedState()
|
||||||
{
|
{
|
||||||
*aResult = mDocIdentifier;
|
return mShared;
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::SetDocIdentifier(PRUint64 aDocIdentifier)
|
|
||||||
{
|
|
||||||
// This ensures that after a session restore, gEntryDocIdentifier is greater
|
|
||||||
// than all SHEntries' docIdentifiers, which ensures that we'll never repeat
|
|
||||||
// a doc identifier.
|
|
||||||
if (aDocIdentifier >= gEntryDocIdentifier)
|
|
||||||
gEntryDocIdentifier = aDocIdentifier + 1;
|
|
||||||
|
|
||||||
mDocIdentifier = aDocIdentifier;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::SetUniqueDocIdentifier()
|
|
||||||
{
|
|
||||||
mDocIdentifier = gEntryDocIdentifier++;
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::GetIsSubFrame(bool * aFlag)
|
NS_IMETHODIMP nsSHEntry::GetIsSubFrame(bool * aFlag)
|
||||||
{
|
{
|
||||||
*aFlag = mIsFrameNavigation;
|
*aFlag = mShared->mIsFrameNavigation;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::SetIsSubFrame(bool aFlag)
|
NS_IMETHODIMP nsSHEntry::SetIsSubFrame(bool aFlag)
|
||||||
{
|
{
|
||||||
mIsFrameNavigation = aFlag;
|
mShared->mIsFrameNavigation = aFlag;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::GetCacheKey(nsISupports** aResult)
|
NS_IMETHODIMP nsSHEntry::GetCacheKey(nsISupports** aResult)
|
||||||
{
|
{
|
||||||
*aResult = mCacheKey;
|
*aResult = mShared->mCacheKey;
|
||||||
NS_IF_ADDREF(*aResult);
|
NS_IF_ADDREF(*aResult);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::SetCacheKey(nsISupports* aCacheKey)
|
NS_IMETHODIMP nsSHEntry::SetCacheKey(nsISupports* aCacheKey)
|
||||||
{
|
{
|
||||||
mCacheKey = aCacheKey;
|
mShared->mCacheKey = aCacheKey;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::GetSaveLayoutStateFlag(bool * aFlag)
|
NS_IMETHODIMP nsSHEntry::GetSaveLayoutStateFlag(bool * aFlag)
|
||||||
{
|
{
|
||||||
*aFlag = mSaveLayoutState;
|
*aFlag = mShared->mSaveLayoutState;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::SetSaveLayoutStateFlag(bool aFlag)
|
NS_IMETHODIMP nsSHEntry::SetSaveLayoutStateFlag(bool aFlag)
|
||||||
{
|
{
|
||||||
mSaveLayoutState = aFlag;
|
mShared->mSaveLayoutState = aFlag;
|
||||||
if (mLayoutHistoryState)
|
if (mShared->mLayoutHistoryState) {
|
||||||
mLayoutHistoryState->SetScrollPositionOnly(!aFlag);
|
mShared->mLayoutHistoryState->SetScrollPositionOnly(!aFlag);
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::GetExpirationStatus(bool * aFlag)
|
NS_IMETHODIMP nsSHEntry::GetExpirationStatus(bool * aFlag)
|
||||||
{
|
{
|
||||||
*aFlag = mExpired;
|
*aFlag = mShared->mExpired;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::SetExpirationStatus(bool aFlag)
|
NS_IMETHODIMP nsSHEntry::SetExpirationStatus(bool aFlag)
|
||||||
{
|
{
|
||||||
mExpired = aFlag;
|
mShared->mExpired = aFlag;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::GetContentType(nsACString& aContentType)
|
NS_IMETHODIMP nsSHEntry::GetContentType(nsACString& aContentType)
|
||||||
{
|
{
|
||||||
aContentType = mContentType;
|
aContentType = mShared->mContentType;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP nsSHEntry::SetContentType(const nsACString& aContentType)
|
NS_IMETHODIMP nsSHEntry::SetContentType(const nsACString& aContentType)
|
||||||
{
|
{
|
||||||
mContentType = aContentType;
|
mShared->mContentType = aContentType;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -502,26 +380,27 @@ nsSHEntry::Create(nsIURI * aURI, const nsAString &aTitle,
|
||||||
mURI = aURI;
|
mURI = aURI;
|
||||||
mTitle = aTitle;
|
mTitle = aTitle;
|
||||||
mPostData = aInputStream;
|
mPostData = aInputStream;
|
||||||
mCacheKey = aCacheKey;
|
|
||||||
mContentType = aContentType;
|
|
||||||
mOwner = aOwner;
|
|
||||||
mDocShellID = aDocShellID;
|
|
||||||
mDynamicallyCreated = aDynamicCreation;
|
|
||||||
|
|
||||||
// Set the LoadType by default to loadHistory during creation
|
// Set the LoadType by default to loadHistory during creation
|
||||||
mLoadType = (PRUint32) nsIDocShellLoadInfo::loadHistory;
|
mLoadType = (PRUint32) nsIDocShellLoadInfo::loadHistory;
|
||||||
|
|
||||||
|
mShared->mCacheKey = aCacheKey;
|
||||||
|
mShared->mContentType = aContentType;
|
||||||
|
mShared->mOwner = aOwner;
|
||||||
|
mShared->mDocShellID = aDocShellID;
|
||||||
|
mShared->mDynamicallyCreated = aDynamicCreation;
|
||||||
|
|
||||||
// By default all entries are set false for subframe flag.
|
// By default all entries are set false for subframe flag.
|
||||||
// nsDocShell::CloneAndReplace() which creates entries for
|
// nsDocShell::CloneAndReplace() which creates entries for
|
||||||
// all subframe navigations, sets the flag to true.
|
// all subframe navigations, sets the flag to true.
|
||||||
mIsFrameNavigation = false;
|
mShared->mIsFrameNavigation = false;
|
||||||
|
|
||||||
// By default we save LayoutHistoryState
|
// By default we save LayoutHistoryState
|
||||||
mSaveLayoutState = true;
|
mShared->mSaveLayoutState = true;
|
||||||
mLayoutHistoryState = aLayoutHistoryState;
|
mShared->mLayoutHistoryState = aLayoutHistoryState;
|
||||||
|
|
||||||
//By default the page is not expired
|
//By default the page is not expired
|
||||||
mExpired = false;
|
mShared->mExpired = false;
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -530,8 +409,6 @@ NS_IMETHODIMP
|
||||||
nsSHEntry::Clone(nsISHEntry ** aResult)
|
nsSHEntry::Clone(nsISHEntry ** aResult)
|
||||||
{
|
{
|
||||||
*aResult = new nsSHEntry(*this);
|
*aResult = new nsSHEntry(*this);
|
||||||
if (!*aResult)
|
|
||||||
return NS_ERROR_OUT_OF_MEMORY;
|
|
||||||
NS_ADDREF(*aResult);
|
NS_ADDREF(*aResult);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -540,7 +417,7 @@ NS_IMETHODIMP
|
||||||
nsSHEntry::GetParent(nsISHEntry ** aResult)
|
nsSHEntry::GetParent(nsISHEntry ** aResult)
|
||||||
{
|
{
|
||||||
NS_ENSURE_ARG_POINTER(aResult);
|
NS_ENSURE_ARG_POINTER(aResult);
|
||||||
*aResult = mParent;
|
*aResult = mShared->mParent;
|
||||||
NS_IF_ADDREF(*aResult);
|
NS_IF_ADDREF(*aResult);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
@ -553,49 +430,95 @@ nsSHEntry::SetParent(nsISHEntry * aParent)
|
||||||
*
|
*
|
||||||
* XXX this method should not be scriptable if this is the case!!
|
* XXX this method should not be scriptable if this is the case!!
|
||||||
*/
|
*/
|
||||||
mParent = aParent;
|
mShared->mParent = aParent;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::SetWindowState(nsISupports *aState)
|
nsSHEntry::SetWindowState(nsISupports *aState)
|
||||||
{
|
{
|
||||||
mWindowState = aState;
|
mShared->mWindowState = aState;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::GetWindowState(nsISupports **aState)
|
nsSHEntry::GetWindowState(nsISupports **aState)
|
||||||
{
|
{
|
||||||
NS_IF_ADDREF(*aState = mWindowState);
|
NS_IF_ADDREF(*aState = mShared->mWindowState);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::SetViewerBounds(const nsIntRect &aBounds)
|
nsSHEntry::SetViewerBounds(const nsIntRect &aBounds)
|
||||||
{
|
{
|
||||||
mViewerBounds = aBounds;
|
mShared->mViewerBounds = aBounds;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::GetViewerBounds(nsIntRect &aBounds)
|
nsSHEntry::GetViewerBounds(nsIntRect &aBounds)
|
||||||
{
|
{
|
||||||
aBounds = mViewerBounds;
|
aBounds = mShared->mViewerBounds;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::GetOwner(nsISupports **aOwner)
|
nsSHEntry::GetOwner(nsISupports **aOwner)
|
||||||
{
|
{
|
||||||
NS_IF_ADDREF(*aOwner = mOwner);
|
NS_IF_ADDREF(*aOwner = mShared->mOwner);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::SetOwner(nsISupports *aOwner)
|
nsSHEntry::SetOwner(nsISupports *aOwner)
|
||||||
{
|
{
|
||||||
mOwner = aOwner;
|
mShared->mOwner = aOwner;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsSHEntry::GetBFCacheEntry(nsIBFCacheEntry **aEntry)
|
||||||
|
{
|
||||||
|
NS_ENSURE_ARG_POINTER(aEntry);
|
||||||
|
NS_IF_ADDREF(*aEntry = mShared);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
nsSHEntry::HasBFCacheEntry(nsIBFCacheEntry *aEntry)
|
||||||
|
{
|
||||||
|
return static_cast<nsIBFCacheEntry*>(mShared) == aEntry;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsSHEntry::AdoptBFCacheEntry(nsISHEntry *aEntry)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsISHEntryInternal> shEntry = do_QueryInterface(aEntry);
|
||||||
|
NS_ENSURE_STATE(shEntry);
|
||||||
|
|
||||||
|
nsSHEntryShared *shared = shEntry->GetSharedState();
|
||||||
|
NS_ENSURE_STATE(shared);
|
||||||
|
|
||||||
|
mShared = shared;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsSHEntry::SharesDocumentWith(nsISHEntry *aEntry, bool *aOut)
|
||||||
|
{
|
||||||
|
NS_ENSURE_ARG_POINTER(aOut);
|
||||||
|
|
||||||
|
nsCOMPtr<nsISHEntryInternal> internal = do_QueryInterface(aEntry);
|
||||||
|
NS_ENSURE_STATE(internal);
|
||||||
|
|
||||||
|
*aOut = mShared == internal->GetSharedState();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
nsSHEntry::AbandonBFCacheEntry()
|
||||||
|
{
|
||||||
|
mShared = nsSHEntryShared::Duplicate(mShared);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -753,256 +676,77 @@ NS_IMETHODIMP
|
||||||
nsSHEntry::AddChildShell(nsIDocShellTreeItem *aShell)
|
nsSHEntry::AddChildShell(nsIDocShellTreeItem *aShell)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(aShell, "Null child shell added to history entry");
|
NS_ASSERTION(aShell, "Null child shell added to history entry");
|
||||||
mChildShells.AppendObject(aShell);
|
mShared->mChildShells.AppendObject(aShell);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::ChildShellAt(PRInt32 aIndex, nsIDocShellTreeItem **aShell)
|
nsSHEntry::ChildShellAt(PRInt32 aIndex, nsIDocShellTreeItem **aShell)
|
||||||
{
|
{
|
||||||
NS_IF_ADDREF(*aShell = mChildShells.SafeObjectAt(aIndex));
|
NS_IF_ADDREF(*aShell = mShared->mChildShells.SafeObjectAt(aIndex));
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::ClearChildShells()
|
nsSHEntry::ClearChildShells()
|
||||||
{
|
{
|
||||||
mChildShells.Clear();
|
mShared->mChildShells.Clear();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::GetRefreshURIList(nsISupportsArray **aList)
|
nsSHEntry::GetRefreshURIList(nsISupportsArray **aList)
|
||||||
{
|
{
|
||||||
NS_IF_ADDREF(*aList = mRefreshURIList);
|
NS_IF_ADDREF(*aList = mShared->mRefreshURIList);
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::SetRefreshURIList(nsISupportsArray *aList)
|
nsSHEntry::SetRefreshURIList(nsISupportsArray *aList)
|
||||||
{
|
{
|
||||||
mRefreshURIList = aList;
|
mShared->mRefreshURIList = aList;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::SyncPresentationState()
|
nsSHEntry::SyncPresentationState()
|
||||||
{
|
{
|
||||||
if (mContentViewer && mWindowState) {
|
return mShared->SyncPresentationState();
|
||||||
// If we have a content viewer and a window state, we should be ok.
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
DropPresentationState();
|
|
||||||
|
|
||||||
return NS_OK;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
nsSHEntry::DropPresentationState()
|
|
||||||
{
|
|
||||||
nsRefPtr<nsSHEntry> kungFuDeathGrip = this;
|
|
||||||
|
|
||||||
if (mDocument) {
|
|
||||||
mDocument->SetBFCacheEntry(nsnull);
|
|
||||||
mDocument->RemoveMutationObserver(this);
|
|
||||||
mDocument = nsnull;
|
|
||||||
}
|
|
||||||
if (mContentViewer)
|
|
||||||
mContentViewer->ClearHistoryEntry();
|
|
||||||
|
|
||||||
StopTrackingEntry(this);
|
|
||||||
mContentViewer = nsnull;
|
|
||||||
mSticky = true;
|
|
||||||
mWindowState = nsnull;
|
|
||||||
mViewerBounds.SetRect(0, 0, 0, 0);
|
|
||||||
mChildShells.Clear();
|
|
||||||
mRefreshURIList = nsnull;
|
|
||||||
mEditorData = nsnull;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
nsSHEntry::Expire()
|
|
||||||
{
|
|
||||||
// This entry has timed out. If we still have a content viewer, we need to
|
|
||||||
// get it evicted.
|
|
||||||
if (!mContentViewer)
|
|
||||||
return;
|
|
||||||
nsCOMPtr<nsISupports> container;
|
|
||||||
mContentViewer->GetContainer(getter_AddRefs(container));
|
|
||||||
nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container);
|
|
||||||
if (!treeItem)
|
|
||||||
return;
|
|
||||||
// We need to find the root DocShell since only that object has an
|
|
||||||
// SHistory and we need the SHistory to evict content viewers
|
|
||||||
nsCOMPtr<nsIDocShellTreeItem> root;
|
|
||||||
treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
|
|
||||||
nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
|
|
||||||
nsCOMPtr<nsISHistory> history;
|
|
||||||
webNav->GetSessionHistory(getter_AddRefs(history));
|
|
||||||
nsCOMPtr<nsISHistoryInternal> historyInt = do_QueryInterface(history);
|
|
||||||
if (!historyInt)
|
|
||||||
return;
|
|
||||||
historyInt->EvictExpiredContentViewerForEntry(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
//*****************************************************************************
|
|
||||||
// nsSHEntry: nsIMutationObserver
|
|
||||||
//*****************************************************************************
|
|
||||||
|
|
||||||
void
|
|
||||||
nsSHEntry::NodeWillBeDestroyed(const nsINode* aNode)
|
|
||||||
{
|
|
||||||
NS_NOTREACHED("Document destroyed while we're holding a strong ref to it");
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
nsSHEntry::CharacterDataWillChange(nsIDocument* aDocument,
|
|
||||||
nsIContent* aContent,
|
|
||||||
CharacterDataChangeInfo* aInfo)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
nsSHEntry::CharacterDataChanged(nsIDocument* aDocument,
|
|
||||||
nsIContent* aContent,
|
|
||||||
CharacterDataChangeInfo* aInfo)
|
|
||||||
{
|
|
||||||
RemoveFromBFCacheAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
nsSHEntry::AttributeWillChange(nsIDocument* aDocument,
|
|
||||||
dom::Element* aContent,
|
|
||||||
PRInt32 aNameSpaceID,
|
|
||||||
nsIAtom* aAttribute,
|
|
||||||
PRInt32 aModType)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
nsSHEntry::AttributeChanged(nsIDocument* aDocument,
|
|
||||||
dom::Element* aElement,
|
|
||||||
PRInt32 aNameSpaceID,
|
|
||||||
nsIAtom* aAttribute,
|
|
||||||
PRInt32 aModType)
|
|
||||||
{
|
|
||||||
RemoveFromBFCacheAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
nsSHEntry::ContentAppended(nsIDocument* aDocument,
|
|
||||||
nsIContent* aContainer,
|
|
||||||
nsIContent* aFirstNewContent,
|
|
||||||
PRInt32 /* unused */)
|
|
||||||
{
|
|
||||||
RemoveFromBFCacheAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
nsSHEntry::ContentInserted(nsIDocument* aDocument,
|
|
||||||
nsIContent* aContainer,
|
|
||||||
nsIContent* aChild,
|
|
||||||
PRInt32 /* unused */)
|
|
||||||
{
|
|
||||||
RemoveFromBFCacheAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
nsSHEntry::ContentRemoved(nsIDocument* aDocument,
|
|
||||||
nsIContent* aContainer,
|
|
||||||
nsIContent* aChild,
|
|
||||||
PRInt32 aIndexInContainer,
|
|
||||||
nsIContent* aPreviousSibling)
|
|
||||||
{
|
|
||||||
RemoveFromBFCacheAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
nsSHEntry::ParentChainChanged(nsIContent *aContent)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
class DestroyViewerEvent : public nsRunnable
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
DestroyViewerEvent(nsIContentViewer* aViewer, nsIDocument* aDocument)
|
|
||||||
: mViewer(aViewer),
|
|
||||||
mDocument(aDocument)
|
|
||||||
{}
|
|
||||||
|
|
||||||
NS_IMETHOD Run()
|
|
||||||
{
|
|
||||||
if (mViewer)
|
|
||||||
mViewer->Destroy();
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIContentViewer> mViewer;
|
|
||||||
nsCOMPtr<nsIDocument> mDocument;
|
|
||||||
};
|
|
||||||
|
|
||||||
void
|
void
|
||||||
nsSHEntry::RemoveFromBFCacheSync()
|
nsSHEntry::RemoveFromBFCacheSync()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mContentViewer && mDocument,
|
mShared->RemoveFromBFCacheSync();
|
||||||
"we're not in the bfcache!");
|
|
||||||
|
|
||||||
nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
|
|
||||||
DropPresentationState();
|
|
||||||
|
|
||||||
// Warning! The call to DropPresentationState could have dropped the last
|
|
||||||
// reference to this nsSHEntry, so no accessing members beyond here.
|
|
||||||
|
|
||||||
if (viewer) {
|
|
||||||
viewer->Destroy();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsSHEntry::RemoveFromBFCacheAsync()
|
nsSHEntry::RemoveFromBFCacheAsync()
|
||||||
{
|
{
|
||||||
NS_ASSERTION(mContentViewer && mDocument,
|
mShared->RemoveFromBFCacheAsync();
|
||||||
"we're not in the bfcache!");
|
|
||||||
|
|
||||||
// Release the reference to the contentviewer asynchronously so that the
|
|
||||||
// document doesn't get nuked mid-mutation.
|
|
||||||
|
|
||||||
nsCOMPtr<nsIRunnable> evt =
|
|
||||||
new DestroyViewerEvent(mContentViewer, mDocument);
|
|
||||||
nsresult rv = NS_DispatchToCurrentThread(evt);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
NS_WARNING("failed to dispatch DestroyViewerEvent");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Drop presentation. Also ensures that we don't post more then one
|
|
||||||
// PLEvent. Only do this if we succeeded in posting the event since
|
|
||||||
// otherwise the document could be torn down mid mutation causing crashes.
|
|
||||||
DropPresentationState();
|
|
||||||
}
|
|
||||||
// Warning! The call to DropPresentationState could have dropped the last
|
|
||||||
// reference to this nsSHEntry, so no accessing members beyond here.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsDocShellEditorData*
|
nsDocShellEditorData*
|
||||||
nsSHEntry::ForgetEditorData()
|
nsSHEntry::ForgetEditorData()
|
||||||
{
|
{
|
||||||
return mEditorData.forget();
|
// XXX jlebar Check how this is used.
|
||||||
|
return mShared->mEditorData.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsSHEntry::SetEditorData(nsDocShellEditorData* aData)
|
nsSHEntry::SetEditorData(nsDocShellEditorData* aData)
|
||||||
{
|
{
|
||||||
NS_ASSERTION(!(aData && mEditorData),
|
NS_ASSERTION(!(aData && mShared->mEditorData),
|
||||||
"We're going to overwrite an owning ref!");
|
"We're going to overwrite an owning ref!");
|
||||||
if (mEditorData != aData)
|
if (mShared->mEditorData != aData) {
|
||||||
mEditorData = aData;
|
mShared->mEditorData = aData;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
nsSHEntry::HasDetachedEditor()
|
nsSHEntry::HasDetachedEditor()
|
||||||
{
|
{
|
||||||
return mEditorData != nsnull;
|
return mShared->mEditorData != nsnull;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
|
@ -1023,7 +767,7 @@ nsSHEntry::SetStateData(nsIStructuredCloneContainer *aContainer)
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::IsDynamicallyAdded(bool* aAdded)
|
nsSHEntry::IsDynamicallyAdded(bool* aAdded)
|
||||||
{
|
{
|
||||||
*aAdded = mDynamicallyCreated;
|
*aAdded = mShared->mDynamicallyCreated;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1046,14 +790,14 @@ nsSHEntry::HasDynamicallyAddedChild(bool* aAdded)
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::GetDocshellID(PRUint64* aID)
|
nsSHEntry::GetDocshellID(PRUint64* aID)
|
||||||
{
|
{
|
||||||
*aID = mDocShellID;
|
*aID = mShared->mDocShellID;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::SetDocshellID(PRUint64 aID)
|
nsSHEntry::SetDocshellID(PRUint64 aID)
|
||||||
{
|
{
|
||||||
mDocShellID = aID;
|
mShared->mDocShellID = aID;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1061,14 +805,13 @@ nsSHEntry::SetDocshellID(PRUint64 aID)
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::GetLastTouched(PRUint32 *aLastTouched)
|
nsSHEntry::GetLastTouched(PRUint32 *aLastTouched)
|
||||||
{
|
{
|
||||||
*aLastTouched = mLastTouched;
|
*aLastTouched = mShared->mLastTouched;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHEntry::SetLastTouched(PRUint32 aLastTouched)
|
nsSHEntry::SetLastTouched(PRUint32 aLastTouched)
|
||||||
{
|
{
|
||||||
mLastTouched = aLastTouched;
|
mShared->mLastTouched = aLastTouched;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,28 +42,21 @@
|
||||||
|
|
||||||
// Helper Classes
|
// Helper Classes
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
|
#include "nsAutoPtr.h"
|
||||||
#include "nsCOMArray.h"
|
#include "nsCOMArray.h"
|
||||||
#include "nsString.h"
|
#include "nsString.h"
|
||||||
#include "nsAutoPtr.h"
|
|
||||||
|
|
||||||
// Interfaces needed
|
// Interfaces needed
|
||||||
#include "nsIContentViewer.h"
|
|
||||||
#include "nsIInputStream.h"
|
#include "nsIInputStream.h"
|
||||||
#include "nsILayoutHistoryState.h"
|
|
||||||
#include "nsISHEntry.h"
|
#include "nsISHEntry.h"
|
||||||
#include "nsISHContainer.h"
|
#include "nsISHContainer.h"
|
||||||
#include "nsIURI.h"
|
#include "nsIURI.h"
|
||||||
#include "nsIEnumerator.h"
|
|
||||||
#include "nsIHistoryEntry.h"
|
#include "nsIHistoryEntry.h"
|
||||||
#include "nsRect.h"
|
|
||||||
#include "nsISupportsArray.h"
|
class nsSHEntryShared;
|
||||||
#include "nsIMutationObserver.h"
|
|
||||||
#include "nsExpirationTracker.h"
|
|
||||||
#include "nsDocShellEditorData.h"
|
|
||||||
|
|
||||||
class nsSHEntry : public nsISHEntry,
|
class nsSHEntry : public nsISHEntry,
|
||||||
public nsISHContainer,
|
public nsISHContainer,
|
||||||
public nsIMutationObserver,
|
|
||||||
public nsISHEntryInternal
|
public nsISHEntryInternal
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -75,51 +68,30 @@ public:
|
||||||
NS_DECL_NSISHENTRY
|
NS_DECL_NSISHENTRY
|
||||||
NS_DECL_NSISHENTRYINTERNAL
|
NS_DECL_NSISHENTRYINTERNAL
|
||||||
NS_DECL_NSISHCONTAINER
|
NS_DECL_NSISHCONTAINER
|
||||||
NS_DECL_NSIMUTATIONOBSERVER
|
|
||||||
|
|
||||||
void DropPresentationState();
|
void DropPresentationState();
|
||||||
|
|
||||||
void Expire();
|
|
||||||
|
|
||||||
nsExpirationState *GetExpirationState() { return &mExpirationState; }
|
|
||||||
|
|
||||||
static nsresult Startup();
|
static nsresult Startup();
|
||||||
static void Shutdown();
|
static void Shutdown();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
~nsSHEntry();
|
~nsSHEntry();
|
||||||
|
|
||||||
nsCOMPtr<nsIURI> mURI;
|
// We share the state in here with other SHEntries which correspond to the
|
||||||
nsCOMPtr<nsIURI> mReferrerURI;
|
// same document.
|
||||||
nsCOMPtr<nsIContentViewer> mContentViewer;
|
nsRefPtr<nsSHEntryShared> mShared;
|
||||||
nsCOMPtr<nsIDocument> mDocument; // document currently being observed
|
|
||||||
nsString mTitle;
|
// See nsSHEntry.idl for comments on these members.
|
||||||
nsCOMPtr<nsIInputStream> mPostData;
|
nsCOMPtr<nsIURI> mURI;
|
||||||
nsCOMPtr<nsILayoutHistoryState> mLayoutHistoryState;
|
nsCOMPtr<nsIURI> mReferrerURI;
|
||||||
nsCOMArray<nsISHEntry> mChildren;
|
nsString mTitle;
|
||||||
PRUint32 mLoadType;
|
nsCOMPtr<nsIInputStream> mPostData;
|
||||||
PRUint32 mID;
|
PRUint32 mLoadType;
|
||||||
PRInt64 mDocIdentifier;
|
PRUint32 mID;
|
||||||
PRInt32 mScrollPositionX;
|
PRInt32 mScrollPositionX;
|
||||||
PRInt32 mScrollPositionY;
|
PRInt32 mScrollPositionY;
|
||||||
PRPackedBool mURIWasModified;
|
nsCOMArray<nsISHEntry> mChildren;
|
||||||
PRPackedBool mIsFrameNavigation;
|
bool mURIWasModified;
|
||||||
PRPackedBool mSaveLayoutState;
|
|
||||||
PRPackedBool mExpired;
|
|
||||||
PRPackedBool mSticky;
|
|
||||||
PRPackedBool mDynamicallyCreated;
|
|
||||||
nsCString mContentType;
|
|
||||||
nsCOMPtr<nsISupports> mCacheKey;
|
|
||||||
nsISHEntry * mParent; // weak reference
|
|
||||||
nsCOMPtr<nsISupports> mWindowState;
|
|
||||||
nsIntRect mViewerBounds;
|
|
||||||
nsCOMArray<nsIDocShellTreeItem> mChildShells;
|
|
||||||
nsCOMPtr<nsISupportsArray> mRefreshURIList;
|
|
||||||
nsCOMPtr<nsISupports> mOwner;
|
|
||||||
nsExpirationState mExpirationState;
|
|
||||||
nsAutoPtr<nsDocShellEditorData> mEditorData;
|
|
||||||
PRUint64 mDocShellID;
|
|
||||||
PRUint32 mLastTouched;
|
|
||||||
nsCOMPtr<nsIStructuredCloneContainer> mStateData;
|
nsCOMPtr<nsIStructuredCloneContainer> mStateData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,400 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla.org code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||||
|
*
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Justin Lebar <justin.lebar@gmail.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#include "nsSHEntryShared.h"
|
||||||
|
#include "nsISHistory.h"
|
||||||
|
#include "nsISHistoryInternal.h"
|
||||||
|
#include "nsIDocument.h"
|
||||||
|
#include "nsIWebNavigation.h"
|
||||||
|
#include "nsIContentViewer.h"
|
||||||
|
#include "nsIDocShellTreeItem.h"
|
||||||
|
#include "nsISupportsArray.h"
|
||||||
|
#include "nsDocShellEditorData.h"
|
||||||
|
#include "nsThreadUtils.h"
|
||||||
|
#include "nsILayoutHistoryState.h"
|
||||||
|
#include "prprf.h"
|
||||||
|
|
||||||
|
namespace dom = mozilla::dom;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
PRUint64 gSHEntrySharedID = 0;
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
// Hardcode this to time out unused content viewers after 30 minutes
|
||||||
|
// XXX jlebar shouldn't this be a pref?
|
||||||
|
#define CONTENT_VIEWER_TIMEOUT_SECONDS (30*60)
|
||||||
|
|
||||||
|
typedef nsExpirationTracker<nsSHEntryShared, 3> HistoryTrackerBase;
|
||||||
|
class HistoryTracker : public HistoryTrackerBase {
|
||||||
|
public:
|
||||||
|
// Expire cached contentviewers after 20-30 minutes in the cache.
|
||||||
|
HistoryTracker()
|
||||||
|
: HistoryTrackerBase(1000 * CONTENT_VIEWER_TIMEOUT_SECONDS / 2)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void NotifyExpired(nsSHEntryShared *aObj) {
|
||||||
|
RemoveObject(aObj);
|
||||||
|
aObj->Expire();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static HistoryTracker *gHistoryTracker = nsnull;
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::Startup()
|
||||||
|
{
|
||||||
|
gHistoryTracker = new HistoryTracker();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::Shutdown()
|
||||||
|
{
|
||||||
|
delete gHistoryTracker;
|
||||||
|
gHistoryTracker = nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsSHEntryShared::nsSHEntryShared()
|
||||||
|
: mDocShellID(0)
|
||||||
|
, mParent(nsnull)
|
||||||
|
, mIsFrameNavigation(false)
|
||||||
|
, mSaveLayoutState(true)
|
||||||
|
, mSticky(true)
|
||||||
|
, mDynamicallyCreated(false)
|
||||||
|
, mLastTouched(0)
|
||||||
|
, mID(gSHEntrySharedID++)
|
||||||
|
, mExpired(false)
|
||||||
|
, mViewerBounds(0, 0, 0, 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
nsSHEntryShared::~nsSHEntryShared()
|
||||||
|
{
|
||||||
|
RemoveFromExpirationTracker();
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
// Check that we're not still on track to expire. We shouldn't be, because
|
||||||
|
// we just removed ourselves!
|
||||||
|
nsExpirationTracker<nsSHEntryShared, 3>::Iterator
|
||||||
|
iterator(gHistoryTracker);
|
||||||
|
|
||||||
|
nsSHEntryShared *elem;
|
||||||
|
while ((elem = iterator.Next()) != nsnull) {
|
||||||
|
NS_ASSERTION(elem != this, "Found dead entry still in the tracker!");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (mContentViewer) {
|
||||||
|
RemoveFromBFCacheSync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS2(nsSHEntryShared, nsIBFCacheEntry, nsIMutationObserver)
|
||||||
|
|
||||||
|
already_AddRefed<nsSHEntryShared>
|
||||||
|
nsSHEntryShared::Duplicate(nsSHEntryShared *aEntry)
|
||||||
|
{
|
||||||
|
nsRefPtr<nsSHEntryShared> newEntry = new nsSHEntryShared();
|
||||||
|
|
||||||
|
newEntry->mDocShellID = aEntry->mDocShellID;
|
||||||
|
newEntry->mChildShells.AppendObjects(aEntry->mChildShells);
|
||||||
|
newEntry->mOwner = aEntry->mOwner;
|
||||||
|
newEntry->mParent = aEntry->mParent;
|
||||||
|
newEntry->mContentType.Assign(aEntry->mContentType);
|
||||||
|
newEntry->mIsFrameNavigation = aEntry->mIsFrameNavigation;
|
||||||
|
newEntry->mSaveLayoutState = aEntry->mSaveLayoutState;
|
||||||
|
newEntry->mSticky = aEntry->mSticky;
|
||||||
|
newEntry->mDynamicallyCreated = aEntry->mDynamicallyCreated;
|
||||||
|
newEntry->mCacheKey = aEntry->mCacheKey;
|
||||||
|
newEntry->mLastTouched = aEntry->mLastTouched;
|
||||||
|
|
||||||
|
return newEntry.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void nsSHEntryShared::RemoveFromExpirationTracker()
|
||||||
|
{
|
||||||
|
if (GetExpirationState()->IsTracked()) {
|
||||||
|
gHistoryTracker->RemoveObject(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsSHEntryShared::SyncPresentationState()
|
||||||
|
{
|
||||||
|
if (mContentViewer && mWindowState) {
|
||||||
|
// If we have a content viewer and a window state, we should be ok.
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
DropPresentationState();
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::DropPresentationState()
|
||||||
|
{
|
||||||
|
nsRefPtr<nsSHEntryShared> kungFuDeathGrip = this;
|
||||||
|
|
||||||
|
if (mDocument) {
|
||||||
|
mDocument->SetBFCacheEntry(nsnull);
|
||||||
|
mDocument->RemoveMutationObserver(this);
|
||||||
|
mDocument = nsnull;
|
||||||
|
}
|
||||||
|
if (mContentViewer) {
|
||||||
|
mContentViewer->ClearHistoryEntry();
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoveFromExpirationTracker();
|
||||||
|
mContentViewer = nsnull;
|
||||||
|
mSticky = true;
|
||||||
|
mWindowState = nsnull;
|
||||||
|
mViewerBounds.SetRect(0, 0, 0, 0);
|
||||||
|
mChildShells.Clear();
|
||||||
|
mRefreshURIList = nsnull;
|
||||||
|
mEditorData = nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::Expire()
|
||||||
|
{
|
||||||
|
// This entry has timed out. If we still have a content viewer, we need to
|
||||||
|
// evict it.
|
||||||
|
if (!mContentViewer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
nsCOMPtr<nsISupports> container;
|
||||||
|
mContentViewer->GetContainer(getter_AddRefs(container));
|
||||||
|
nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container);
|
||||||
|
if (!treeItem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// We need to find the root DocShell since only that object has an
|
||||||
|
// SHistory and we need the SHistory to evict content viewers
|
||||||
|
nsCOMPtr<nsIDocShellTreeItem> root;
|
||||||
|
treeItem->GetSameTypeRootTreeItem(getter_AddRefs(root));
|
||||||
|
nsCOMPtr<nsIWebNavigation> webNav = do_QueryInterface(root);
|
||||||
|
nsCOMPtr<nsISHistory> history;
|
||||||
|
webNav->GetSessionHistory(getter_AddRefs(history));
|
||||||
|
nsCOMPtr<nsISHistoryInternal> historyInt = do_QueryInterface(history);
|
||||||
|
if (!historyInt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
historyInt->EvictExpiredContentViewerForEntry(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsSHEntryShared::SetContentViewer(nsIContentViewer *aViewer)
|
||||||
|
{
|
||||||
|
NS_PRECONDITION(!aViewer || !mContentViewer,
|
||||||
|
"SHEntryShared already contains viewer");
|
||||||
|
|
||||||
|
if (mContentViewer || !aViewer) {
|
||||||
|
DropPresentationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
mContentViewer = aViewer;
|
||||||
|
|
||||||
|
if (mContentViewer) {
|
||||||
|
gHistoryTracker->AddObject(this);
|
||||||
|
|
||||||
|
nsCOMPtr<nsIDOMDocument> domDoc;
|
||||||
|
mContentViewer->GetDOMDocument(getter_AddRefs(domDoc));
|
||||||
|
// Store observed document in strong pointer in case it is removed from
|
||||||
|
// the contentviewer
|
||||||
|
mDocument = do_QueryInterface(domDoc);
|
||||||
|
if (mDocument) {
|
||||||
|
mDocument->SetBFCacheEntry(this);
|
||||||
|
mDocument->AddMutationObserver(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsSHEntryShared::RemoveFromBFCacheSync()
|
||||||
|
{
|
||||||
|
NS_ASSERTION(mContentViewer && mDocument,
|
||||||
|
"we're not in the bfcache!");
|
||||||
|
|
||||||
|
nsCOMPtr<nsIContentViewer> viewer = mContentViewer;
|
||||||
|
DropPresentationState();
|
||||||
|
|
||||||
|
// Warning! The call to DropPresentationState could have dropped the last
|
||||||
|
// reference to this object, so don't access members beyond here.
|
||||||
|
|
||||||
|
if (viewer) {
|
||||||
|
viewer->Destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
class DestroyViewerEvent : public nsRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DestroyViewerEvent(nsIContentViewer* aViewer, nsIDocument* aDocument)
|
||||||
|
: mViewer(aViewer),
|
||||||
|
mDocument(aDocument)
|
||||||
|
{}
|
||||||
|
|
||||||
|
NS_IMETHOD Run()
|
||||||
|
{
|
||||||
|
if (mViewer) {
|
||||||
|
mViewer->Destroy();
|
||||||
|
}
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsIContentViewer> mViewer;
|
||||||
|
nsCOMPtr<nsIDocument> mDocument;
|
||||||
|
};
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsSHEntryShared::RemoveFromBFCacheAsync()
|
||||||
|
{
|
||||||
|
NS_ASSERTION(mContentViewer && mDocument,
|
||||||
|
"we're not in the bfcache!");
|
||||||
|
|
||||||
|
// Release the reference to the contentviewer asynchronously so that the
|
||||||
|
// document doesn't get nuked mid-mutation.
|
||||||
|
|
||||||
|
nsCOMPtr<nsIRunnable> evt =
|
||||||
|
new DestroyViewerEvent(mContentViewer, mDocument);
|
||||||
|
nsresult rv = NS_DispatchToCurrentThread(evt);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
NS_WARNING("failed to dispatch DestroyViewerEvent");
|
||||||
|
} else {
|
||||||
|
// Drop presentation. Only do this if we succeeded in posting the event
|
||||||
|
// since otherwise the document could be torn down mid-mutation, causing
|
||||||
|
// crashes.
|
||||||
|
DropPresentationState();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Careful! The call to DropPresentationState could have dropped the last
|
||||||
|
// reference to this nsSHEntryShared, so don't access members beyond here.
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
nsSHEntryShared::GetID(PRUint64 *aID)
|
||||||
|
{
|
||||||
|
*aID = mID;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
//*****************************************************************************
|
||||||
|
// nsSHEntryShared: nsIMutationObserver
|
||||||
|
//*****************************************************************************
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::NodeWillBeDestroyed(const nsINode* aNode)
|
||||||
|
{
|
||||||
|
NS_NOTREACHED("Document destroyed while we're holding a strong ref to it");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::CharacterDataWillChange(nsIDocument* aDocument,
|
||||||
|
nsIContent* aContent,
|
||||||
|
CharacterDataChangeInfo* aInfo)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::CharacterDataChanged(nsIDocument* aDocument,
|
||||||
|
nsIContent* aContent,
|
||||||
|
CharacterDataChangeInfo* aInfo)
|
||||||
|
{
|
||||||
|
RemoveFromBFCacheAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::AttributeWillChange(nsIDocument* aDocument,
|
||||||
|
dom::Element* aContent,
|
||||||
|
PRInt32 aNameSpaceID,
|
||||||
|
nsIAtom* aAttribute,
|
||||||
|
PRInt32 aModType)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::AttributeChanged(nsIDocument* aDocument,
|
||||||
|
dom::Element* aElement,
|
||||||
|
PRInt32 aNameSpaceID,
|
||||||
|
nsIAtom* aAttribute,
|
||||||
|
PRInt32 aModType)
|
||||||
|
{
|
||||||
|
RemoveFromBFCacheAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::ContentAppended(nsIDocument* aDocument,
|
||||||
|
nsIContent* aContainer,
|
||||||
|
nsIContent* aFirstNewContent,
|
||||||
|
PRInt32 /* unused */)
|
||||||
|
{
|
||||||
|
RemoveFromBFCacheAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::ContentInserted(nsIDocument* aDocument,
|
||||||
|
nsIContent* aContainer,
|
||||||
|
nsIContent* aChild,
|
||||||
|
PRInt32 /* unused */)
|
||||||
|
{
|
||||||
|
RemoveFromBFCacheAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::ContentRemoved(nsIDocument* aDocument,
|
||||||
|
nsIContent* aContainer,
|
||||||
|
nsIContent* aChild,
|
||||||
|
PRInt32 aIndexInContainer,
|
||||||
|
nsIContent* aPreviousSibling)
|
||||||
|
{
|
||||||
|
RemoveFromBFCacheAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
nsSHEntryShared::ParentChainChanged(nsIContent *aContent)
|
||||||
|
{
|
||||||
|
}
|
|
@ -0,0 +1,124 @@
|
||||||
|
/* ***** BEGIN LICENSE BLOCK *****
|
||||||
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the Mozilla Public License Version
|
||||||
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||||
|
* the License. You may obtain a copy of the License at
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*
|
||||||
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||||
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||||
|
* for the specific language governing rights and limitations under the
|
||||||
|
* License.
|
||||||
|
*
|
||||||
|
* The Original Code is Mozilla.org code.
|
||||||
|
*
|
||||||
|
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
||||||
|
*
|
||||||
|
* Portions created by the Initial Developer are Copyright (C) 2011
|
||||||
|
* the Initial Developer. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Contributor(s):
|
||||||
|
* Justin Lebar <justin.lebar@gmail.com>
|
||||||
|
*
|
||||||
|
* Alternatively, the contents of this file may be used under the terms of
|
||||||
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||||
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||||
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||||
|
* of those above. If you wish to allow use of your version of this file only
|
||||||
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||||
|
* use your version of this file under the terms of the MPL, indicate your
|
||||||
|
* decision by deleting the provisions above and replace them with the notice
|
||||||
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||||
|
* the provisions above, a recipient may use your version of this file under
|
||||||
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||||
|
*
|
||||||
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#ifndef nsSHEntryShared_h__
|
||||||
|
#define nsSHEntryShared_h__
|
||||||
|
|
||||||
|
#include "nsCOMPtr.h"
|
||||||
|
#include "nsAutoPtr.h"
|
||||||
|
#include "nsCOMArray.h"
|
||||||
|
#include "nsIBFCacheEntry.h"
|
||||||
|
#include "nsIMutationObserver.h"
|
||||||
|
#include "nsExpirationTracker.h"
|
||||||
|
#include "nsRect.h"
|
||||||
|
|
||||||
|
class nsSHEntry;
|
||||||
|
class nsISHEntry;
|
||||||
|
class nsIDocument;
|
||||||
|
class nsIContentViewer;
|
||||||
|
class nsIDocShellTreeItem;
|
||||||
|
class nsILayoutHistoryState;
|
||||||
|
class nsISupportsArray;
|
||||||
|
class nsDocShellEditorData;
|
||||||
|
|
||||||
|
// A document may have multiple SHEntries, either due to hash navigations or
|
||||||
|
// calls to history.pushState. SHEntries corresponding to the same document
|
||||||
|
// share many members; in particular, they share state related to the
|
||||||
|
// back/forward cache.
|
||||||
|
//
|
||||||
|
// nsSHEntryShared is the vehicle for this sharing.
|
||||||
|
class nsSHEntryShared : public nsIBFCacheEntry,
|
||||||
|
public nsIMutationObserver
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static void Startup();
|
||||||
|
static void Shutdown();
|
||||||
|
|
||||||
|
nsSHEntryShared();
|
||||||
|
~nsSHEntryShared();
|
||||||
|
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIMUTATIONOBSERVER
|
||||||
|
NS_DECL_NSIBFCACHEENTRY
|
||||||
|
|
||||||
|
private:
|
||||||
|
friend class nsSHEntry;
|
||||||
|
|
||||||
|
friend class HistoryTracker;
|
||||||
|
friend class nsExpirationTracker<nsSHEntryShared, 3>;
|
||||||
|
nsExpirationState *GetExpirationState() { return &mExpirationState; }
|
||||||
|
|
||||||
|
static already_AddRefed<nsSHEntryShared> Duplicate(nsSHEntryShared *aEntry);
|
||||||
|
|
||||||
|
void RemoveFromExpirationTracker();
|
||||||
|
void Expire();
|
||||||
|
nsresult SyncPresentationState();
|
||||||
|
void DropPresentationState();
|
||||||
|
|
||||||
|
nsresult SetContentViewer(nsIContentViewer *aViewer);
|
||||||
|
|
||||||
|
// See nsISHEntry.idl for an explanation of these members.
|
||||||
|
|
||||||
|
// These members are copied by nsSHEntryShared::Duplicate(). If you add a
|
||||||
|
// member here, be sure to update the Duplicate() implementation.
|
||||||
|
PRUint64 mDocShellID;
|
||||||
|
nsCOMArray<nsIDocShellTreeItem> mChildShells;
|
||||||
|
nsCOMPtr<nsISupports> mOwner;
|
||||||
|
nsISHEntry* mParent;
|
||||||
|
nsCString mContentType;
|
||||||
|
bool mIsFrameNavigation;
|
||||||
|
bool mSaveLayoutState;
|
||||||
|
bool mSticky;
|
||||||
|
bool mDynamicallyCreated;
|
||||||
|
nsCOMPtr<nsISupports> mCacheKey;
|
||||||
|
PRUint32 mLastTouched;
|
||||||
|
|
||||||
|
// These members aren't copied by nsSHEntryShared::Duplicate() because
|
||||||
|
// they're specific to a particular content viewer.
|
||||||
|
PRUint64 mID;
|
||||||
|
nsCOMPtr<nsIContentViewer> mContentViewer;
|
||||||
|
nsCOMPtr<nsIDocument> mDocument;
|
||||||
|
nsCOMPtr<nsILayoutHistoryState> mLayoutHistoryState;
|
||||||
|
bool mExpired;
|
||||||
|
nsCOMPtr<nsISupports> mWindowState;
|
||||||
|
nsIntRect mViewerBounds;
|
||||||
|
nsCOMPtr<nsISupportsArray> mRefreshURIList;
|
||||||
|
nsExpirationState mExpirationState;
|
||||||
|
nsAutoPtr<nsDocShellEditorData> mEditorData;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -72,12 +72,10 @@ using namespace mozilla;
|
||||||
|
|
||||||
#define PREF_SHISTORY_SIZE "browser.sessionhistory.max_entries"
|
#define PREF_SHISTORY_SIZE "browser.sessionhistory.max_entries"
|
||||||
#define PREF_SHISTORY_MAX_TOTAL_VIEWERS "browser.sessionhistory.max_total_viewers"
|
#define PREF_SHISTORY_MAX_TOTAL_VIEWERS "browser.sessionhistory.max_total_viewers"
|
||||||
#define PREF_SHISTORY_OPTIMIZE_EVICTION "browser.sessionhistory.optimize_eviction"
|
|
||||||
|
|
||||||
static const char* kObservedPrefs[] = {
|
static const char* kObservedPrefs[] = {
|
||||||
PREF_SHISTORY_SIZE,
|
PREF_SHISTORY_SIZE,
|
||||||
PREF_SHISTORY_MAX_TOTAL_VIEWERS,
|
PREF_SHISTORY_MAX_TOTAL_VIEWERS,
|
||||||
PREF_SHISTORY_OPTIMIZE_EVICTION,
|
|
||||||
nsnull
|
nsnull
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -90,16 +88,46 @@ static PRCList gSHistoryList;
|
||||||
// means we will calculate how many viewers to cache based on total memory
|
// means we will calculate how many viewers to cache based on total memory
|
||||||
PRInt32 nsSHistory::sHistoryMaxTotalViewers = -1;
|
PRInt32 nsSHistory::sHistoryMaxTotalViewers = -1;
|
||||||
|
|
||||||
// Whether we should optimize the search for which entry to evict,
|
|
||||||
// by evicting older entries first. See entryLastTouched in
|
|
||||||
// nsSHistory::EvictGlobalContentViewer().
|
|
||||||
// NB: After 4.0, we should remove this option and the corresponding
|
|
||||||
// pref - optimization should always be used
|
|
||||||
static bool gOptimizeEviction = false;
|
|
||||||
// A counter that is used to be able to know the order in which
|
// A counter that is used to be able to know the order in which
|
||||||
// entries were touched, so that we can evict older entries first.
|
// entries were touched, so that we can evict older entries first.
|
||||||
static PRUint32 gTouchCounter = 0;
|
static PRUint32 gTouchCounter = 0;
|
||||||
|
|
||||||
|
static PRLogModuleInfo* gLogModule = PR_LOG_DEFINE("nsSHistory");
|
||||||
|
#define LOG(format) PR_LOG(gLogModule, PR_LOG_DEBUG, format)
|
||||||
|
|
||||||
|
// This macro makes it easier to print a log message which includes a URI's
|
||||||
|
// spec. Example use:
|
||||||
|
//
|
||||||
|
// nsIURI *uri = [...];
|
||||||
|
// LOG_SPEC(("The URI is %s.", _spec), uri);
|
||||||
|
//
|
||||||
|
#define LOG_SPEC(format, uri) \
|
||||||
|
PR_BEGIN_MACRO \
|
||||||
|
if (PR_LOG_TEST(gLogModule, PR_LOG_DEBUG)) { \
|
||||||
|
nsCAutoString _specStr(NS_LITERAL_CSTRING("(null)"));\
|
||||||
|
if (uri) { \
|
||||||
|
uri->GetSpec(_specStr); \
|
||||||
|
} \
|
||||||
|
const char* _spec = _specStr.get(); \
|
||||||
|
LOG(format); \
|
||||||
|
} \
|
||||||
|
PR_END_MACRO
|
||||||
|
|
||||||
|
// This macro makes it easy to log a message including an SHEntry's URI.
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// nsCOMPtr<nsISHEntry> shentry = [...];
|
||||||
|
// LOG_SHENTRY_SPEC(("shentry %p has uri %s.", shentry.get(), _spec), shentry);
|
||||||
|
//
|
||||||
|
#define LOG_SHENTRY_SPEC(format, shentry) \
|
||||||
|
PR_BEGIN_MACRO \
|
||||||
|
if (PR_LOG_TEST(gLogModule, PR_LOG_DEBUG)) { \
|
||||||
|
nsCOMPtr<nsIURI> uri; \
|
||||||
|
shentry->GetURI(getter_AddRefs(uri)); \
|
||||||
|
LOG_SPEC(format, uri); \
|
||||||
|
} \
|
||||||
|
PR_END_MACRO
|
||||||
|
|
||||||
enum HistCmd{
|
enum HistCmd{
|
||||||
HIST_CMD_BACK,
|
HIST_CMD_BACK,
|
||||||
HIST_CMD_FORWARD,
|
HIST_CMD_FORWARD,
|
||||||
|
@ -134,15 +162,60 @@ nsSHistoryObserver::Observe(nsISupports *aSubject, const char *aTopic,
|
||||||
{
|
{
|
||||||
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
if (!strcmp(aTopic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) {
|
||||||
nsSHistory::UpdatePrefs();
|
nsSHistory::UpdatePrefs();
|
||||||
nsSHistory::EvictGlobalContentViewer();
|
nsSHistory::GloballyEvictContentViewers();
|
||||||
} else if (!strcmp(aTopic, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID) ||
|
} else if (!strcmp(aTopic, NS_CACHESERVICE_EMPTYCACHE_TOPIC_ID) ||
|
||||||
!strcmp(aTopic, "memory-pressure")) {
|
!strcmp(aTopic, "memory-pressure")) {
|
||||||
nsSHistory::EvictAllContentViewersGlobally();
|
nsSHistory::GloballyEvictAllContentViewers();
|
||||||
}
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
already_AddRefed<nsIContentViewer>
|
||||||
|
GetContentViewerForTransaction(nsISHTransaction *aTrans)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsISHEntry> entry;
|
||||||
|
aTrans->GetSHEntry(getter_AddRefs(entry));
|
||||||
|
if (!entry) {
|
||||||
|
return nsnull;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsISHEntry> ownerEntry;
|
||||||
|
nsCOMPtr<nsIContentViewer> viewer;
|
||||||
|
entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
|
||||||
|
getter_AddRefs(viewer));
|
||||||
|
return viewer.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
EvictContentViewerForTransaction(nsISHTransaction *aTrans)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsISHEntry> entry;
|
||||||
|
aTrans->GetSHEntry(getter_AddRefs(entry));
|
||||||
|
nsCOMPtr<nsIContentViewer> viewer;
|
||||||
|
nsCOMPtr<nsISHEntry> ownerEntry;
|
||||||
|
entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
|
||||||
|
getter_AddRefs(viewer));
|
||||||
|
if (viewer) {
|
||||||
|
NS_ASSERTION(ownerEntry,
|
||||||
|
"Content viewer exists but its SHEntry is null");
|
||||||
|
|
||||||
|
LOG_SHENTRY_SPEC(("Evicting content viewer 0x%p for "
|
||||||
|
"owning SHEntry 0x%p at %s.",
|
||||||
|
viewer.get(), ownerEntry.get(), _spec), ownerEntry);
|
||||||
|
|
||||||
|
// Drop the presentation state before destroying the viewer, so that
|
||||||
|
// document teardown is able to correctly persist the state.
|
||||||
|
ownerEntry->SetContentViewer(nsnull);
|
||||||
|
ownerEntry->SyncPresentationState();
|
||||||
|
viewer->Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
//*****************************************************************************
|
//*****************************************************************************
|
||||||
//*** nsSHistory: Object Management
|
//*** nsSHistory: Object Management
|
||||||
//*****************************************************************************
|
//*****************************************************************************
|
||||||
|
@ -240,7 +313,6 @@ nsSHistory::UpdatePrefs()
|
||||||
Preferences::GetInt(PREF_SHISTORY_SIZE, &gHistoryMaxSize);
|
Preferences::GetInt(PREF_SHISTORY_SIZE, &gHistoryMaxSize);
|
||||||
Preferences::GetInt(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
|
Preferences::GetInt(PREF_SHISTORY_MAX_TOTAL_VIEWERS,
|
||||||
&sHistoryMaxTotalViewers);
|
&sHistoryMaxTotalViewers);
|
||||||
Preferences::GetBool(PREF_SHISTORY_OPTIMIZE_EVICTION, &gOptimizeEviction);
|
|
||||||
// If the pref is negative, that means we calculate how many viewers
|
// If the pref is negative, that means we calculate how many viewers
|
||||||
// we think we should cache, based on total memory
|
// we think we should cache, based on total memory
|
||||||
if (sHistoryMaxTotalViewers < 0) {
|
if (sHistoryMaxTotalViewers < 0) {
|
||||||
|
@ -689,12 +761,12 @@ nsSHistory::GetListener(nsISHistoryListener ** aListener)
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
NS_IMETHODIMP
|
||||||
nsSHistory::EvictContentViewers(PRInt32 aPreviousIndex, PRInt32 aIndex)
|
nsSHistory::EvictOutOfRangeContentViewers(PRInt32 aIndex)
|
||||||
{
|
{
|
||||||
// Check our per SHistory object limit in the currently navigated SHistory
|
// Check our per SHistory object limit in the currently navigated SHistory
|
||||||
EvictWindowContentViewers(aPreviousIndex, aIndex);
|
EvictOutOfRangeWindowContentViewers(aIndex);
|
||||||
// Check our total limit across all SHistory objects
|
// Check our total limit across all SHistory objects
|
||||||
EvictGlobalContentViewer();
|
GloballyEvictContentViewers();
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -703,7 +775,14 @@ nsSHistory::EvictAllContentViewers()
|
||||||
{
|
{
|
||||||
// XXXbz we don't actually do a good job of evicting things as we should, so
|
// XXXbz we don't actually do a good job of evicting things as we should, so
|
||||||
// we might have viewers quite far from mIndex. So just evict everything.
|
// we might have viewers quite far from mIndex. So just evict everything.
|
||||||
EvictContentViewersInRange(0, mLength);
|
nsCOMPtr<nsISHTransaction> trans = mListRoot;
|
||||||
|
while (trans) {
|
||||||
|
EvictContentViewerForTransaction(trans);
|
||||||
|
|
||||||
|
nsISHTransaction *temp = trans;
|
||||||
|
temp->GetNext(getter_AddRefs(trans));
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -835,103 +914,78 @@ nsSHistory::ReloadCurrentEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
nsSHistory::EvictWindowContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex)
|
nsSHistory::EvictOutOfRangeWindowContentViewers(PRInt32 aIndex)
|
||||||
{
|
{
|
||||||
// To enforce the per SHistory object limit on cached content viewers, we
|
// XXX rename method to EvictContentViewersExceptAroundIndex, or something.
|
||||||
// need to release all of the content viewers that are no longer in the
|
|
||||||
// "window" that now ends/begins at aToIndex. Existing content viewers
|
// We need to release all content viewers that are no longer in the range
|
||||||
// should be in the window from
|
|
||||||
// aFromIndex - gHistoryMaxViewers to aFromIndex + gHistoryMaxViewers
|
|
||||||
//
|
//
|
||||||
// We make the assumption that entries outside this range have no viewers so
|
// aIndex - gHistoryMaxViewers to aIndex + gHistoryMaxViewers
|
||||||
// that we don't have to walk the whole entire session history checking for
|
//
|
||||||
// content viewers.
|
// to ensure that this SHistory object isn't responsible for more than
|
||||||
|
// gHistoryMaxViewers content viewers. But our job is complicated by the
|
||||||
|
// fact that two transactions which are related by either hash navigations or
|
||||||
|
// history.pushState will have the same content viewer.
|
||||||
|
//
|
||||||
|
// To illustrate the issue, suppose gHistoryMaxViewers = 3 and we have four
|
||||||
|
// linked transactions in our history. Suppose we then add a new content
|
||||||
|
// viewer and call into this function. So the history looks like:
|
||||||
|
//
|
||||||
|
// A A A A B
|
||||||
|
// + *
|
||||||
|
//
|
||||||
|
// where the letters are content viewers and + and * denote the beginning and
|
||||||
|
// end of the range aIndex +/- gHistoryMaxViewers.
|
||||||
|
//
|
||||||
|
// Although one copy of the content viewer A exists outside the range, we
|
||||||
|
// don't want to evict A, because it has other copies in range!
|
||||||
|
//
|
||||||
|
// We therefore adjust our eviction strategy to read:
|
||||||
|
//
|
||||||
|
// Evict each content viewer outside the range aIndex -/+
|
||||||
|
// gHistoryMaxViewers, unless that content viewer also appears within the
|
||||||
|
// range.
|
||||||
|
//
|
||||||
|
// (Note that it's entirely legal to have two copies of one content viewer
|
||||||
|
// separated by a different content viewer -- call pushState twice, go back
|
||||||
|
// once, and refresh -- so we can't rely on identical viewers only appearing
|
||||||
|
// adjacent to one another.)
|
||||||
|
|
||||||
// This can happen on the first load of a page in a particular window
|
if (aIndex < 0) {
|
||||||
if (aFromIndex < 0 || aToIndex < 0) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
NS_ASSERTION(aFromIndex < mLength, "aFromIndex is out of range");
|
NS_ASSERTION(aIndex < mLength, "aIndex is out of range");
|
||||||
NS_ASSERTION(aToIndex < mLength, "aToIndex is out of range");
|
if (aIndex >= mLength) {
|
||||||
if (aFromIndex >= mLength || aToIndex >= mLength) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// These indices give the range of SHEntries whose content viewers will be
|
// Calculate the range that's safe from eviction.
|
||||||
// evicted
|
PRInt32 startSafeIndex = PR_MAX(0, aIndex - gHistoryMaxViewers);
|
||||||
PRInt32 startIndex, endIndex;
|
PRInt32 endSafeIndex = PR_MIN(mLength, aIndex + gHistoryMaxViewers);
|
||||||
if (aToIndex > aFromIndex) { // going forward
|
|
||||||
endIndex = aToIndex - gHistoryMaxViewers;
|
|
||||||
if (endIndex <= 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
startIndex = NS_MAX(0, aFromIndex - gHistoryMaxViewers);
|
|
||||||
} else { // going backward
|
|
||||||
startIndex = aToIndex + gHistoryMaxViewers + 1;
|
|
||||||
if (startIndex >= mLength) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
endIndex = NS_MIN(mLength, aFromIndex + gHistoryMaxViewers + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
LOG(("EvictOutOfRangeWindowContentViewers(index=%d), "
|
||||||
|
"mLength=%d. Safe range [%d, %d]",
|
||||||
|
aIndex, mLength, startSafeIndex, endSafeIndex));
|
||||||
|
|
||||||
|
// The content viewers in range aIndex -/+ gHistoryMaxViewers will not be
|
||||||
|
// evicted. Collect a set of them so we don't accidentally evict one of them
|
||||||
|
// if it appears outside this range.
|
||||||
|
nsCOMArray<nsIContentViewer> safeViewers;
|
||||||
nsCOMPtr<nsISHTransaction> trans;
|
nsCOMPtr<nsISHTransaction> trans;
|
||||||
|
GetTransactionAtIndex(startSafeIndex, getter_AddRefs(trans));
|
||||||
|
for (PRUint32 i = startSafeIndex; trans && i <= endSafeIndex; i++) {
|
||||||
|
nsCOMPtr<nsIContentViewer> viewer = GetContentViewerForTransaction(trans);
|
||||||
|
safeViewers.AppendObject(viewer);
|
||||||
|
nsISHTransaction *temp = trans;
|
||||||
|
temp->GetNext(getter_AddRefs(trans));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Walk the SHistory list and evict any content viewers that aren't safe.
|
||||||
GetTransactionAtIndex(0, getter_AddRefs(trans));
|
GetTransactionAtIndex(0, getter_AddRefs(trans));
|
||||||
|
while (trans) {
|
||||||
// Walk the full session history and check that entries outside the window
|
nsCOMPtr<nsIContentViewer> viewer = GetContentViewerForTransaction(trans);
|
||||||
// around aFromIndex have no content viewers
|
if (safeViewers.IndexOf(viewer) == -1) {
|
||||||
for (PRInt32 i = 0; trans && i < mLength; ++i) {
|
EvictContentViewerForTransaction(trans);
|
||||||
if (i < aFromIndex - gHistoryMaxViewers ||
|
|
||||||
i > aFromIndex + gHistoryMaxViewers) {
|
|
||||||
nsCOMPtr<nsISHEntry> entry;
|
|
||||||
trans->GetSHEntry(getter_AddRefs(entry));
|
|
||||||
nsCOMPtr<nsIContentViewer> viewer;
|
|
||||||
nsCOMPtr<nsISHEntry> ownerEntry;
|
|
||||||
entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
|
|
||||||
getter_AddRefs(viewer));
|
|
||||||
NS_WARN_IF_FALSE(!viewer,
|
|
||||||
"ContentViewer exists outside gHistoryMaxViewer range");
|
|
||||||
}
|
|
||||||
|
|
||||||
nsISHTransaction *temp = trans;
|
|
||||||
temp->GetNext(getter_AddRefs(trans));
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
EvictContentViewersInRange(startIndex, endIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
nsSHistory::EvictContentViewersInRange(PRInt32 aStart, PRInt32 aEnd)
|
|
||||||
{
|
|
||||||
nsCOMPtr<nsISHTransaction> trans;
|
|
||||||
GetTransactionAtIndex(aStart, getter_AddRefs(trans));
|
|
||||||
|
|
||||||
for (PRInt32 i = aStart; trans && i < aEnd; ++i) {
|
|
||||||
nsCOMPtr<nsISHEntry> entry;
|
|
||||||
trans->GetSHEntry(getter_AddRefs(entry));
|
|
||||||
nsCOMPtr<nsIContentViewer> viewer;
|
|
||||||
nsCOMPtr<nsISHEntry> ownerEntry;
|
|
||||||
entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
|
|
||||||
getter_AddRefs(viewer));
|
|
||||||
if (viewer) {
|
|
||||||
NS_ASSERTION(ownerEntry,
|
|
||||||
"ContentViewer exists but its SHEntry is null");
|
|
||||||
#ifdef DEBUG_PAGE_CACHE
|
|
||||||
nsCOMPtr<nsIURI> uri;
|
|
||||||
ownerEntry->GetURI(getter_AddRefs(uri));
|
|
||||||
nsCAutoString spec;
|
|
||||||
if (uri)
|
|
||||||
uri->GetSpec(spec);
|
|
||||||
|
|
||||||
printf("per SHistory limit: evicting content viewer: %s\n", spec.get());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Drop the presentation state before destroying the viewer, so that
|
|
||||||
// document teardown is able to correctly persist the state.
|
|
||||||
ownerEntry->SetContentViewer(nsnull);
|
|
||||||
ownerEntry->SyncPresentationState();
|
|
||||||
viewer->Destroy();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsISHTransaction *temp = trans;
|
nsISHTransaction *temp = trans;
|
||||||
|
@ -939,138 +993,153 @@ nsSHistory::EvictContentViewersInRange(PRInt32 aStart, PRInt32 aEnd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
namespace {
|
||||||
void
|
|
||||||
nsSHistory::EvictGlobalContentViewer()
|
class TransactionAndDistance
|
||||||
{
|
{
|
||||||
// true until the total number of content viewers is <= total max
|
public:
|
||||||
// The usual case is that we only need to evict one content viewer.
|
TransactionAndDistance(nsISHTransaction *aTrans, PRUint32 aDist)
|
||||||
// However, if somebody resets the pref value, we might occasionally
|
: mTransaction(aTrans)
|
||||||
// need to evict more than one.
|
, mDistance(aDist)
|
||||||
bool shouldTryEviction = true;
|
{
|
||||||
while (shouldTryEviction) {
|
mViewer = GetContentViewerForTransaction(aTrans);
|
||||||
// Walk through our list of SHistory objects, looking for content
|
NS_ASSERTION(mViewer, "Transaction should have a content viewer");
|
||||||
// viewers in the possible active window of all of the SHEntry objects.
|
|
||||||
// Keep track of the SHEntry object that has a ContentViewer and is
|
|
||||||
// farthest from the current focus in any SHistory object. The
|
|
||||||
// ContentViewer associated with that SHEntry will be evicted
|
|
||||||
PRInt32 distanceFromFocus = 0;
|
|
||||||
PRUint32 candidateLastTouched = 0;
|
|
||||||
nsCOMPtr<nsISHEntry> evictFromSHE;
|
|
||||||
nsCOMPtr<nsIContentViewer> evictViewer;
|
|
||||||
PRInt32 totalContentViewers = 0;
|
|
||||||
nsSHistory* shist = static_cast<nsSHistory*>
|
|
||||||
(PR_LIST_HEAD(&gSHistoryList));
|
|
||||||
while (shist != &gSHistoryList) {
|
|
||||||
// Calculate the window of SHEntries that could possibly have a content
|
|
||||||
// viewer. There could be up to gHistoryMaxViewers content viewers,
|
|
||||||
// but we don't know whether they are before or after the mIndex position
|
|
||||||
// in the SHEntry list. Just check both sides, to be safe.
|
|
||||||
PRInt32 startIndex = NS_MAX(0, shist->mIndex - gHistoryMaxViewers);
|
|
||||||
PRInt32 endIndex = NS_MIN(shist->mLength - 1,
|
|
||||||
shist->mIndex + gHistoryMaxViewers);
|
|
||||||
nsCOMPtr<nsISHTransaction> trans;
|
|
||||||
shist->GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
|
|
||||||
|
|
||||||
for (PRInt32 i = startIndex; trans && i <= endIndex; ++i) {
|
nsCOMPtr<nsISHEntry> shentry;
|
||||||
nsCOMPtr<nsISHEntry> entry;
|
mTransaction->GetSHEntry(getter_AddRefs(shentry));
|
||||||
trans->GetSHEntry(getter_AddRefs(entry));
|
|
||||||
nsCOMPtr<nsIContentViewer> viewer;
|
|
||||||
nsCOMPtr<nsISHEntry> ownerEntry;
|
|
||||||
entry->GetAnyContentViewer(getter_AddRefs(ownerEntry),
|
|
||||||
getter_AddRefs(viewer));
|
|
||||||
|
|
||||||
PRUint32 entryLastTouched = 0;
|
nsCOMPtr<nsISHEntryInternal> shentryInternal = do_QueryInterface(shentry);
|
||||||
if (gOptimizeEviction) {
|
if (shentryInternal) {
|
||||||
nsCOMPtr<nsISHEntryInternal> entryInternal = do_QueryInterface(entry);
|
shentryInternal->GetLastTouched(&mLastTouched);
|
||||||
if (entryInternal) {
|
|
||||||
// Find when this entry was last activated
|
|
||||||
entryInternal->GetLastTouched(&entryLastTouched);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_PAGE_CACHE
|
|
||||||
nsCOMPtr<nsIURI> uri;
|
|
||||||
if (ownerEntry) {
|
|
||||||
ownerEntry->GetURI(getter_AddRefs(uri));
|
|
||||||
} else {
|
|
||||||
entry->GetURI(getter_AddRefs(uri));
|
|
||||||
}
|
|
||||||
nsCAutoString spec;
|
|
||||||
if (uri) {
|
|
||||||
uri->GetSpec(spec);
|
|
||||||
printf("Considering for eviction: %s\n", spec.get());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// This SHEntry has a ContentViewer, so check how far away it is from
|
|
||||||
// the currently used SHEntry within this SHistory object
|
|
||||||
if (viewer) {
|
|
||||||
PRInt32 distance = NS_ABS(shist->mIndex - i);
|
|
||||||
|
|
||||||
#ifdef DEBUG_PAGE_CACHE
|
|
||||||
printf("Has a cached content viewer: %s\n", spec.get());
|
|
||||||
printf("mIndex: %d i: %d\n", shist->mIndex, i);
|
|
||||||
#endif
|
|
||||||
totalContentViewers++;
|
|
||||||
|
|
||||||
// If this entry is further away from focus than any previously found
|
|
||||||
// or at the same distance but it is longer time since it was activated
|
|
||||||
// then take this entry as the new candiate for eviction
|
|
||||||
if (distance > distanceFromFocus || (distance == distanceFromFocus && candidateLastTouched > entryLastTouched)) {
|
|
||||||
|
|
||||||
#ifdef DEBUG_PAGE_CACHE
|
|
||||||
printf("Choosing as new eviction candidate: %s\n", spec.get());
|
|
||||||
#endif
|
|
||||||
candidateLastTouched = entryLastTouched;
|
|
||||||
distanceFromFocus = distance;
|
|
||||||
evictFromSHE = ownerEntry;
|
|
||||||
evictViewer = viewer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nsISHTransaction* temp = trans;
|
|
||||||
temp->GetNext(getter_AddRefs(trans));
|
|
||||||
}
|
|
||||||
shist = static_cast<nsSHistory*>(PR_NEXT_LINK(shist));
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef DEBUG_PAGE_CACHE
|
|
||||||
printf("Distance from focus: %d\n", distanceFromFocus);
|
|
||||||
printf("Total max viewers: %d\n", sHistoryMaxTotalViewers);
|
|
||||||
printf("Total number of viewers: %d\n", totalContentViewers);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (totalContentViewers > sHistoryMaxTotalViewers && evictViewer) {
|
|
||||||
#ifdef DEBUG_PAGE_CACHE
|
|
||||||
nsCOMPtr<nsIURI> uri;
|
|
||||||
evictFromSHE->GetURI(getter_AddRefs(uri));
|
|
||||||
nsCAutoString spec;
|
|
||||||
if (uri) {
|
|
||||||
uri->GetSpec(spec);
|
|
||||||
printf("Evicting content viewer: %s\n", spec.get());
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
// Drop the presentation state before destroying the viewer, so that
|
|
||||||
// document teardown is able to correctly persist the state.
|
|
||||||
evictFromSHE->SetContentViewer(nsnull);
|
|
||||||
evictFromSHE->SyncPresentationState();
|
|
||||||
evictViewer->Destroy();
|
|
||||||
|
|
||||||
// If we only needed to evict one content viewer, then we are done.
|
|
||||||
// Otherwise, continue evicting until we reach the max total limit.
|
|
||||||
if (totalContentViewers - sHistoryMaxTotalViewers == 1) {
|
|
||||||
shouldTryEviction = false;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// couldn't find a content viewer to evict, so we are done
|
NS_WARNING("Can't cast to nsISHEntryInternal?");
|
||||||
shouldTryEviction = false;
|
mLastTouched = 0;
|
||||||
}
|
}
|
||||||
} // while shouldTryEviction
|
}
|
||||||
|
|
||||||
|
bool operator<(const TransactionAndDistance &aOther) const
|
||||||
|
{
|
||||||
|
// Compare distances first, and fall back to last-accessed times.
|
||||||
|
if (aOther.mDistance != this->mDistance) {
|
||||||
|
return this->mDistance < aOther.mDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this->mLastTouched < aOther.mLastTouched;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const TransactionAndDistance &aOther) const
|
||||||
|
{
|
||||||
|
// This is a little silly; we need == so the default comaprator can be
|
||||||
|
// instantiated, but this function is never actually called when we sort
|
||||||
|
// the list of TransactionAndDistance objects.
|
||||||
|
return aOther.mDistance == this->mDistance &&
|
||||||
|
aOther.mLastTouched == this->mLastTouched;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsISHTransaction> mTransaction;
|
||||||
|
nsCOMPtr<nsIContentViewer> mViewer;
|
||||||
|
PRUint32 mLastTouched;
|
||||||
|
PRInt32 mDistance;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // anonymous namespace
|
||||||
|
|
||||||
|
//static
|
||||||
|
void
|
||||||
|
nsSHistory::GloballyEvictContentViewers()
|
||||||
|
{
|
||||||
|
// First, collect from each SHistory object the transactions which have a
|
||||||
|
// cached content viewer. Associate with each transaction its distance from
|
||||||
|
// its SHistory's current index.
|
||||||
|
|
||||||
|
nsTArray<TransactionAndDistance> transactions;
|
||||||
|
|
||||||
|
nsSHistory *shist = static_cast<nsSHistory*>(PR_LIST_HEAD(&gSHistoryList));
|
||||||
|
while (shist != &gSHistoryList) {
|
||||||
|
|
||||||
|
// Maintain a list of the transactions which have viewers and belong to
|
||||||
|
// this particular shist object. We'll add this list to the global list,
|
||||||
|
// |transactions|, eventually.
|
||||||
|
nsTArray<TransactionAndDistance> shTransactions;
|
||||||
|
|
||||||
|
// Content viewers are likely to exist only within shist->mIndex -/+
|
||||||
|
// gHistoryMaxViewers, so only search within that range.
|
||||||
|
//
|
||||||
|
// A content viewer might exist outside that range due to either:
|
||||||
|
//
|
||||||
|
// * history.pushState or hash navigations, in which case a copy of the
|
||||||
|
// content viewer should exist within the range, or
|
||||||
|
//
|
||||||
|
// * bugs which cause us not to call nsSHistory::EvictContentViewers()
|
||||||
|
// often enough. Once we do call EvictContentViewers() for the
|
||||||
|
// SHistory object in question, we'll do a full search of its history
|
||||||
|
// and evict the out-of-range content viewers, so we don't bother here.
|
||||||
|
//
|
||||||
|
PRInt32 startIndex = NS_MAX(0, shist->mIndex - gHistoryMaxViewers);
|
||||||
|
PRInt32 endIndex = NS_MIN(shist->mLength - 1,
|
||||||
|
shist->mIndex + gHistoryMaxViewers);
|
||||||
|
nsCOMPtr<nsISHTransaction> trans;
|
||||||
|
shist->GetTransactionAtIndex(startIndex, getter_AddRefs(trans));
|
||||||
|
for (PRInt32 i = startIndex; trans && i <= endIndex; i++) {
|
||||||
|
nsCOMPtr<nsIContentViewer> contentViewer =
|
||||||
|
GetContentViewerForTransaction(trans);
|
||||||
|
|
||||||
|
if (contentViewer) {
|
||||||
|
// Because one content viewer might belong to multiple SHEntries, we
|
||||||
|
// have to search through shTransactions to see if we already know
|
||||||
|
// about this content viewer. If we find the viewer, update its
|
||||||
|
// distance from the SHistory's index and continue.
|
||||||
|
bool found = false;
|
||||||
|
for (PRUint32 j = 0; j < shTransactions.Length(); j++) {
|
||||||
|
TransactionAndDistance &container = shTransactions[j];
|
||||||
|
if (container.mViewer == contentViewer) {
|
||||||
|
container.mDistance = PR_MIN(container.mDistance,
|
||||||
|
PR_ABS(i - shist->mIndex));
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we didn't find a TransactionAndDistance for this content viewer, make a new
|
||||||
|
// one.
|
||||||
|
if (!found) {
|
||||||
|
TransactionAndDistance container(trans, PR_ABS(i - shist->mIndex));
|
||||||
|
shTransactions.AppendElement(container);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsISHTransaction *temp = trans;
|
||||||
|
temp->GetNext(getter_AddRefs(trans));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We've found all the transactions belonging to shist which have viewers.
|
||||||
|
// Add those transactions to our global list and move on.
|
||||||
|
transactions.AppendElements(shTransactions);
|
||||||
|
shist = static_cast<nsSHistory*>(PR_NEXT_LINK(shist));
|
||||||
|
}
|
||||||
|
|
||||||
|
// We now have collected all cached content viewers. First check that we
|
||||||
|
// have enough that we actually need to evict some.
|
||||||
|
if ((PRInt32)transactions.Length() <= sHistoryMaxTotalViewers) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we need to evict, sort our list of transactions and evict the largest
|
||||||
|
// ones. (We could of course get better algorithmic complexity here by using
|
||||||
|
// a heap or something more clever. But sHistoryMaxTotalViewers isn't large,
|
||||||
|
// so let's not worry about it.)
|
||||||
|
transactions.Sort();
|
||||||
|
|
||||||
|
for (PRInt32 i = transactions.Length() - 1;
|
||||||
|
i >= sHistoryMaxTotalViewers; --i) {
|
||||||
|
|
||||||
|
EvictContentViewerForTransaction(transactions[i].mTransaction);
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
nsresult
|
||||||
nsSHistory::EvictExpiredContentViewerForEntry(nsISHEntry *aEntry)
|
nsSHistory::EvictExpiredContentViewerForEntry(nsIBFCacheEntry *aEntry)
|
||||||
{
|
{
|
||||||
PRInt32 startIndex = NS_MAX(0, mIndex - gHistoryMaxViewers);
|
PRInt32 startIndex = NS_MAX(0, mIndex - gHistoryMaxViewers);
|
||||||
PRInt32 endIndex = NS_MIN(mLength - 1,
|
PRInt32 endIndex = NS_MIN(mLength - 1,
|
||||||
|
@ -1082,8 +1151,11 @@ nsSHistory::EvictExpiredContentViewerForEntry(nsISHEntry *aEntry)
|
||||||
for (i = startIndex; trans && i <= endIndex; ++i) {
|
for (i = startIndex; trans && i <= endIndex; ++i) {
|
||||||
nsCOMPtr<nsISHEntry> entry;
|
nsCOMPtr<nsISHEntry> entry;
|
||||||
trans->GetSHEntry(getter_AddRefs(entry));
|
trans->GetSHEntry(getter_AddRefs(entry));
|
||||||
if (entry == aEntry)
|
|
||||||
|
// Does entry have the same BFCacheEntry as the argument to this method?
|
||||||
|
if (entry->HasBFCacheEntry(aEntry)) {
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
nsISHTransaction *temp = trans;
|
nsISHTransaction *temp = trans;
|
||||||
temp->GetNext(getter_AddRefs(trans));
|
temp->GetNext(getter_AddRefs(trans));
|
||||||
|
@ -1091,21 +1163,13 @@ nsSHistory::EvictExpiredContentViewerForEntry(nsISHEntry *aEntry)
|
||||||
if (i > endIndex)
|
if (i > endIndex)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
||||||
NS_ASSERTION(i != mIndex, "How did the current session entry expire?");
|
if (i == mIndex) {
|
||||||
if (i == mIndex)
|
NS_WARNING("How did the current SHEntry expire?");
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
||||||
// We evict content viewers for the expired entry and any other entries that
|
|
||||||
// we would have to go through the expired entry to get to (i.e. the entries
|
|
||||||
// that have the expired entry between them and the current entry). Those
|
|
||||||
// other entries should have timed out already, actually, but this is just
|
|
||||||
// to be on the safe side.
|
|
||||||
if (i < mIndex) {
|
|
||||||
EvictContentViewersInRange(startIndex, i + 1);
|
|
||||||
} else {
|
|
||||||
EvictContentViewersInRange(i, endIndex + 1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EvictContentViewerForTransaction(trans);
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1116,11 +1180,11 @@ nsSHistory::EvictExpiredContentViewerForEntry(nsISHEntry *aEntry)
|
||||||
|
|
||||||
//static
|
//static
|
||||||
void
|
void
|
||||||
nsSHistory::EvictAllContentViewersGlobally()
|
nsSHistory::GloballyEvictAllContentViewers()
|
||||||
{
|
{
|
||||||
PRInt32 maxViewers = sHistoryMaxTotalViewers;
|
PRInt32 maxViewers = sHistoryMaxTotalViewers;
|
||||||
sHistoryMaxTotalViewers = 0;
|
sHistoryMaxTotalViewers = 0;
|
||||||
EvictGlobalContentViewer();
|
GloballyEvictContentViewers();
|
||||||
sHistoryMaxTotalViewers = maxViewers;
|
sHistoryMaxTotalViewers = maxViewers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,12 +101,11 @@ protected:
|
||||||
nsresult PrintHistory();
|
nsresult PrintHistory();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Evict the viewers at indices between aStartIndex and aEndIndex,
|
// Evict content viewers in this window which don't lie in the "safe" range
|
||||||
// including aStartIndex but not aEndIndex.
|
// around aIndex.
|
||||||
void EvictContentViewersInRange(PRInt32 aStartIndex, PRInt32 aEndIndex);
|
void EvictOutOfRangeWindowContentViewers(PRInt32 aIndex);
|
||||||
void EvictWindowContentViewers(PRInt32 aFromIndex, PRInt32 aToIndex);
|
static void GloballyEvictContentViewers();
|
||||||
static void EvictGlobalContentViewer();
|
static void GloballyEvictAllContentViewers();
|
||||||
static void EvictAllContentViewersGlobally();
|
|
||||||
|
|
||||||
// Calculates a max number of total
|
// Calculates a max number of total
|
||||||
// content viewers to cache, based on amount of total memory
|
// content viewers to cache, based on amount of total memory
|
||||||
|
|
|
@ -119,6 +119,7 @@ _TEST_FILES = \
|
||||||
test_bug669671.html \
|
test_bug669671.html \
|
||||||
file_bug669671.sjs \
|
file_bug669671.sjs \
|
||||||
test_bug675587.html \
|
test_bug675587.html \
|
||||||
|
test_bfcache_plus_hash.html \
|
||||||
test_bug680257.html \
|
test_bug680257.html \
|
||||||
file_bug680257.html \
|
file_bug680257.html \
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
|
|
@ -50,6 +50,9 @@
|
||||||
|
|
||||||
const LISTEN_EVENTS = ["pageshow"];
|
const LISTEN_EVENTS = ["pageshow"];
|
||||||
|
|
||||||
|
const Cc = Components.classes;
|
||||||
|
const Ci = Components.interfaces;
|
||||||
|
|
||||||
var gBrowser;
|
var gBrowser;
|
||||||
var gTestCount = 0;
|
var gTestCount = 0;
|
||||||
var gTestsIterator;
|
var gTestsIterator;
|
||||||
|
@ -96,6 +99,23 @@
|
||||||
QueryInterface(Components.interfaces.nsISHEntry);
|
QueryInterface(Components.interfaces.nsISHEntry);
|
||||||
is(!!shEntry.contentViewer, gExpected[i], "content viewer "+i+", test "+gTestCount);
|
is(!!shEntry.contentViewer, gExpected[i], "content viewer "+i+", test "+gTestCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure none of the SHEntries share bfcache entries with one
|
||||||
|
// another.
|
||||||
|
for (var i = 0; i < history.count; i++) {
|
||||||
|
for (var j = 0; j < history.count; j++) {
|
||||||
|
if (j == i)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
let shentry1 = history.getEntryAtIndex(i, false)
|
||||||
|
.QueryInterface(Ci.nsISHEntry);
|
||||||
|
let shentry2 = history.getEntryAtIndex(j, false)
|
||||||
|
.QueryInterface(Ci.nsISHEntry);
|
||||||
|
ok(!shentry1.sharesDocumentWith(shentry2),
|
||||||
|
'Test ' + gTestCount + ': shentry[' + i + "] shouldn't " +
|
||||||
|
"share document with shentry[" + j + ']');
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
is(history.count, gExpected.length, "Wrong history length in test "+gTestCount);
|
is(history.count, gExpected.length, "Wrong history length in test "+gTestCount);
|
||||||
|
|
|
@ -0,0 +1,120 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=646641
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<title>Test for Bug 646641</title>
|
||||||
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<script type="text/javascript" src="/tests/SimpleTest/WindowSnapshot.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=646641">Mozilla Bug 646641</a>
|
||||||
|
<p id="display"></p>
|
||||||
|
<div id="content" style="display: none">
|
||||||
|
|
||||||
|
</div>
|
||||||
|
<pre id="test">
|
||||||
|
<script type="application/javascript;version=1.7">
|
||||||
|
|
||||||
|
/** Test for Bug 646641 **/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In a popup (because navigating the main frame confuses Mochitest), do the
|
||||||
|
* following:
|
||||||
|
*
|
||||||
|
* * Call history.pushState().
|
||||||
|
* * Navigate to a new page.
|
||||||
|
* * Go back two history entries.
|
||||||
|
*
|
||||||
|
* Check that we go back, we retrieve the document from bfcache.
|
||||||
|
*/
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
|
function debug(msg) {
|
||||||
|
// Wrap dump so we can turn debug messages on and off easily.
|
||||||
|
dump(msg + '\n');
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedLoadNum = -1;
|
||||||
|
function childLoad(n) {
|
||||||
|
if (n == expectedLoadNum) {
|
||||||
|
debug('Got load ' + n);
|
||||||
|
expectedLoadNum = -1;
|
||||||
|
|
||||||
|
// Spin the event loop before calling gGen.next() so the generator runs
|
||||||
|
// outside the onload handler. This prevents us from encountering all
|
||||||
|
// sorts of docshell quirks.
|
||||||
|
//
|
||||||
|
// (I don't know why I need to wrap gGen.next() in a function, but it
|
||||||
|
// throws an error otherwise.)
|
||||||
|
setTimeout(function() { gGen.next() }, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
debug('Got unexpected load ' + n);
|
||||||
|
ok(false, 'Got unexpected load ' + n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var expectedPageshowNum = -1;
|
||||||
|
function childPageshow(n) {
|
||||||
|
if (n == expectedPageshowNum) {
|
||||||
|
debug('Got expected pageshow ' + n);
|
||||||
|
expectedPageshowNum = -1;
|
||||||
|
ok(true, 'Got expected pageshow ' + n);
|
||||||
|
setTimeout(function() { gGen.next() }, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
debug('Got pageshow ' + n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Since a pageshow comes along with an onload, don't fail the test if we get
|
||||||
|
// an unexpected pageshow.
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForLoad(n) {
|
||||||
|
debug('Waiting for load ' + n);
|
||||||
|
expectedLoadNum = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function waitForShow(n) {
|
||||||
|
debug('Waiting for show ' + n);
|
||||||
|
expectedPageshowNum = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
var popup = window.open('data:text/html,' +
|
||||||
|
'<html><body onload="opener.childLoad(1)" ' +
|
||||||
|
'onpageshow="opener.childPageshow(1)">' +
|
||||||
|
'Popup 1' +
|
||||||
|
'</body></html>');
|
||||||
|
waitForLoad(1);
|
||||||
|
yield;
|
||||||
|
|
||||||
|
popup.history.pushState('', '', '');
|
||||||
|
|
||||||
|
popup.location = 'data:text/html,<html><body onload="opener.childLoad(2)">Popup 2</body></html>';
|
||||||
|
waitForLoad(2);
|
||||||
|
yield;
|
||||||
|
|
||||||
|
// Now go back 2. The first page should be retrieved from bfcache.
|
||||||
|
popup.history.go(-2);
|
||||||
|
waitForShow(1);
|
||||||
|
yield;
|
||||||
|
|
||||||
|
popup.close();
|
||||||
|
SimpleTest.finish();
|
||||||
|
|
||||||
|
// Yield once more so we don't throw a StopIteration exception.
|
||||||
|
yield;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gGen = test();
|
||||||
|
gGen.next();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
</pre>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -228,12 +228,9 @@ public:
|
||||||
|
|
||||||
// First check if the document the IDBDatabase is part of is bfcached
|
// First check if the document the IDBDatabase is part of is bfcached
|
||||||
nsCOMPtr<nsIDocument> ownerDoc = database->GetOwnerDocument();
|
nsCOMPtr<nsIDocument> ownerDoc = database->GetOwnerDocument();
|
||||||
nsISHEntry* shEntry;
|
nsIBFCacheEntry* bfCacheEntry;
|
||||||
if (ownerDoc && (shEntry = ownerDoc->GetBFCacheEntry())) {
|
if (ownerDoc && (bfCacheEntry = ownerDoc->GetBFCacheEntry())) {
|
||||||
nsCOMPtr<nsISHEntryInternal> sheInternal = do_QueryInterface(shEntry);
|
bfCacheEntry->RemoveFromBFCacheSync();
|
||||||
if (sheInternal) {
|
|
||||||
sheInternal->RemoveFromBFCacheSync();
|
|
||||||
}
|
|
||||||
NS_ASSERTION(database->IsClosed(),
|
NS_ASSERTION(database->IsClosed(),
|
||||||
"Kicking doc out of bfcache should have closed database");
|
"Kicking doc out of bfcache should have closed database");
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -63,6 +63,7 @@ using nsMouseScrollEvent;
|
||||||
using nsKeyEvent;
|
using nsKeyEvent;
|
||||||
using RemoteDOMEvent;
|
using RemoteDOMEvent;
|
||||||
using mozilla::WindowsHandle;
|
using mozilla::WindowsHandle;
|
||||||
|
using nscolor;
|
||||||
|
|
||||||
namespace mozilla {
|
namespace mozilla {
|
||||||
namespace dom {
|
namespace dom {
|
||||||
|
@ -190,6 +191,7 @@ parent:
|
||||||
sync GetWidgetNativeData() returns (WindowsHandle value);
|
sync GetWidgetNativeData() returns (WindowsHandle value);
|
||||||
|
|
||||||
SetCursor(PRUint32 value);
|
SetCursor(PRUint32 value);
|
||||||
|
SetBackgroundColor(nscolor color);
|
||||||
|
|
||||||
PContentPermissionRequest(nsCString aType, URI uri);
|
PContentPermissionRequest(nsCString aType, URI uri);
|
||||||
|
|
||||||
|
|
|
@ -123,6 +123,7 @@ TabChild::TabChild(PRUint32 aChromeFlags)
|
||||||
, mTabChildGlobal(nsnull)
|
, mTabChildGlobal(nsnull)
|
||||||
, mChromeFlags(aChromeFlags)
|
, mChromeFlags(aChromeFlags)
|
||||||
, mOuterRect(0, 0, 0, 0)
|
, mOuterRect(0, 0, 0, 0)
|
||||||
|
, mLastBackgroundColor(NS_RGB(255, 255, 255))
|
||||||
{
|
{
|
||||||
printf("creating %d!\n", NS_IsMainThread());
|
printf("creating %d!\n", NS_IsMainThread());
|
||||||
}
|
}
|
||||||
|
@ -1000,6 +1001,15 @@ TabChild::InitWidget(const nsIntSize& size)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TabChild::SetBackgroundColor(const nscolor& aColor)
|
||||||
|
{
|
||||||
|
if (mLastBackgroundColor != aColor) {
|
||||||
|
mLastBackgroundColor = aColor;
|
||||||
|
SendSetBackgroundColor(mLastBackgroundColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
SendSyncMessageToParent(void* aCallbackData,
|
SendSyncMessageToParent(void* aCallbackData,
|
||||||
const nsAString& aMessage,
|
const nsAString& aMessage,
|
||||||
|
@ -1121,4 +1131,3 @@ TabChildGlobal::GetPrincipal()
|
||||||
return nsnull;
|
return nsnull;
|
||||||
return mTabChild->GetPrincipal();
|
return mTabChild->GetPrincipal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -255,6 +255,7 @@ public:
|
||||||
|
|
||||||
nsIPrincipal* GetPrincipal() { return mPrincipal; }
|
nsIPrincipal* GetPrincipal() { return mPrincipal; }
|
||||||
|
|
||||||
|
void SetBackgroundColor(const nscolor& aColor);
|
||||||
protected:
|
protected:
|
||||||
NS_OVERRIDE
|
NS_OVERRIDE
|
||||||
virtual PRenderFrameChild* AllocPRenderFrame();
|
virtual PRenderFrameChild* AllocPRenderFrame();
|
||||||
|
@ -278,6 +279,7 @@ private:
|
||||||
nsRefPtr<TabChildGlobal> mTabChildGlobal;
|
nsRefPtr<TabChildGlobal> mTabChildGlobal;
|
||||||
PRUint32 mChromeFlags;
|
PRUint32 mChromeFlags;
|
||||||
nsIntRect mOuterRect;
|
nsIntRect mOuterRect;
|
||||||
|
nscolor mLastBackgroundColor;
|
||||||
|
|
||||||
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
|
DISALLOW_EVIL_CONSTRUCTORS(TabChild);
|
||||||
};
|
};
|
||||||
|
|
|
@ -341,6 +341,17 @@ TabParent::RecvSetCursor(const PRUint32& aCursor)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
TabParent::RecvSetBackgroundColor(const nscolor& aColor)
|
||||||
|
{
|
||||||
|
if (nsRefPtr<nsFrameLoader> frameLoader = GetFrameLoader()) {
|
||||||
|
if (RenderFrameParent* frame = frameLoader->GetCurrentRemoteFrame()) {
|
||||||
|
frame->SetBackgroundColor(aColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
TabParent::RecvNotifyIMEFocus(const bool& aFocus,
|
TabParent::RecvNotifyIMEFocus(const bool& aFocus,
|
||||||
nsIMEUpdatePreference* aPreference,
|
nsIMEUpdatePreference* aPreference,
|
||||||
|
|
|
@ -108,6 +108,7 @@ public:
|
||||||
virtual bool RecvGetIMEOpenState(bool* aValue);
|
virtual bool RecvGetIMEOpenState(bool* aValue);
|
||||||
virtual bool RecvSetIMEOpenState(const bool& aValue);
|
virtual bool RecvSetIMEOpenState(const bool& aValue);
|
||||||
virtual bool RecvSetCursor(const PRUint32& aValue);
|
virtual bool RecvSetCursor(const PRUint32& aValue);
|
||||||
|
virtual bool RecvSetBackgroundColor(const nscolor& aValue);
|
||||||
virtual bool RecvGetDPI(float* aValue);
|
virtual bool RecvGetDPI(float* aValue);
|
||||||
virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue);
|
virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue);
|
||||||
virtual PContentDialogParent* AllocPContentDialog(const PRUint32& aType,
|
virtual PContentDialogParent* AllocPContentDialog(const PRUint32& aType,
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
*
|
*
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#include "mozilla/Util.h"
|
||||||
|
|
||||||
#include "Events.h"
|
#include "Events.h"
|
||||||
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
|
@ -57,6 +59,7 @@
|
||||||
#define CONSTANT_FLAGS \
|
#define CONSTANT_FLAGS \
|
||||||
JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY
|
JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
USING_WORKERS_NAMESPACE
|
USING_WORKERS_NAMESPACE
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -1126,7 +1129,7 @@ DispatchEventToTarget(JSContext* aCx, JSObject* aTarget, JSObject* aEvent,
|
||||||
if (hasProperty) {
|
if (hasProperty) {
|
||||||
jsval argv[] = { OBJECT_TO_JSVAL(aEvent) };
|
jsval argv[] = { OBJECT_TO_JSVAL(aEvent) };
|
||||||
jsval rval = JSVAL_VOID;
|
jsval rval = JSVAL_VOID;
|
||||||
if (!JS_CallFunctionName(aCx, aTarget, kFunctionName, JS_ARRAY_LENGTH(argv),
|
if (!JS_CallFunctionName(aCx, aTarget, kFunctionName, ArrayLength(argv),
|
||||||
argv, &rval) ||
|
argv, &rval) ||
|
||||||
!JS_ValueToBoolean(aCx, rval, &preventDefaultCalled)) {
|
!JS_ValueToBoolean(aCx, rval, &preventDefaultCalled)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
*
|
*
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#include "mozilla/Util.h"
|
||||||
|
|
||||||
#include "Exceptions.h"
|
#include "Exceptions.h"
|
||||||
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
|
@ -54,6 +56,7 @@
|
||||||
#define CONSTANT_FLAGS \
|
#define CONSTANT_FLAGS \
|
||||||
JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY
|
JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_PERMANENT | JSPROP_READONLY
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
USING_WORKERS_NAMESPACE
|
USING_WORKERS_NAMESPACE
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -247,9 +250,7 @@ DOMException::Create(JSContext* aCx, intN aCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t foundIndex = size_t(-1);
|
size_t foundIndex = size_t(-1);
|
||||||
for (size_t index = 0;
|
for (size_t index = 0; index < ArrayLength(sStaticProperties) - 1; index++) {
|
||||||
index < JS_ARRAY_LENGTH(sStaticProperties) - 1;
|
|
||||||
index++) {
|
|
||||||
if (sStaticProperties[index].tinyid == aCode) {
|
if (sStaticProperties[index].tinyid == aCode) {
|
||||||
foundIndex = index;
|
foundIndex = index;
|
||||||
break;
|
break;
|
||||||
|
@ -397,9 +398,7 @@ FileException::Create(JSContext* aCx, intN aCode)
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t foundIndex = size_t(-1);
|
size_t foundIndex = size_t(-1);
|
||||||
for (size_t index = 0;
|
for (size_t index = 0; index < ArrayLength(sStaticProperties) - 1; index++) {
|
||||||
index < JS_ARRAY_LENGTH(sStaticProperties) - 1;
|
|
||||||
index++) {
|
|
||||||
if (sStaticProperties[index].tinyid == aCode) {
|
if (sStaticProperties[index].tinyid == aCode) {
|
||||||
foundIndex = index;
|
foundIndex = index;
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -36,6 +36,8 @@
|
||||||
*
|
*
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#include "mozilla/Util.h"
|
||||||
|
|
||||||
#include "ListenerManager.h"
|
#include "ListenerManager.h"
|
||||||
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
|
@ -44,7 +46,8 @@
|
||||||
|
|
||||||
#include "Events.h"
|
#include "Events.h"
|
||||||
|
|
||||||
using mozilla::dom::workers::events::ListenerManager;
|
using namespace mozilla;
|
||||||
|
using dom::workers::events::ListenerManager;
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
@ -426,7 +429,7 @@ ListenerManager::DispatchEvent(JSContext* aCx, JSObject* aTarget,
|
||||||
|
|
||||||
jsval argv[] = { OBJECT_TO_JSVAL(aEvent) };
|
jsval argv[] = { OBJECT_TO_JSVAL(aEvent) };
|
||||||
jsval rval = JSVAL_VOID;
|
jsval rval = JSVAL_VOID;
|
||||||
if (!JS_CallFunctionValue(aCx, aTarget, listenerVal, JS_ARRAY_LENGTH(argv),
|
if (!JS_CallFunctionValue(aCx, aTarget, listenerVal, ArrayLength(argv),
|
||||||
argv, &rval)) {
|
argv, &rval)) {
|
||||||
if (!JS_ReportPendingException(aCx)) {
|
if (!JS_ReportPendingException(aCx)) {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -37,6 +37,8 @@
|
||||||
*
|
*
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#include "mozilla/Util.h"
|
||||||
|
|
||||||
#include "WorkerScope.h"
|
#include "WorkerScope.h"
|
||||||
|
|
||||||
#include "jsapi.h"
|
#include "jsapi.h"
|
||||||
|
@ -68,6 +70,7 @@
|
||||||
#define FUNCTION_FLAGS \
|
#define FUNCTION_FLAGS \
|
||||||
JSPROP_ENUMERATE
|
JSPROP_ENUMERATE
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
USING_WORKERS_NAMESPACE
|
USING_WORKERS_NAMESPACE
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -287,7 +290,7 @@ private:
|
||||||
|
|
||||||
jsval rval = JSVAL_VOID;
|
jsval rval = JSVAL_VOID;
|
||||||
if (!JS_CallFunctionValue(aCx, JSVAL_TO_OBJECT(scope), listener,
|
if (!JS_CallFunctionValue(aCx, JSVAL_TO_OBJECT(scope), listener,
|
||||||
JS_ARRAY_LENGTH(argv), argv, &rval)) {
|
ArrayLength(argv), argv, &rval)) {
|
||||||
JS_ReportPendingException(aCx);
|
JS_ReportPendingException(aCx);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -150,7 +150,6 @@ MOZ_OPTIMIZE_LDFLAGS = @MOZ_OPTIMIZE_LDFLAGS@
|
||||||
MOZ_OPTIMIZE_SIZE_TWEAK = @MOZ_OPTIMIZE_SIZE_TWEAK@
|
MOZ_OPTIMIZE_SIZE_TWEAK = @MOZ_OPTIMIZE_SIZE_TWEAK@
|
||||||
|
|
||||||
MOZ_RTTI_FLAGS_ON = @_MOZ_RTTI_FLAGS_ON@
|
MOZ_RTTI_FLAGS_ON = @_MOZ_RTTI_FLAGS_ON@
|
||||||
MOZ_EXCEPTIONS_FLAGS_ON = @_MOZ_EXCEPTIONS_FLAGS_ON@
|
|
||||||
|
|
||||||
PROFILE_GEN_CFLAGS = @PROFILE_GEN_CFLAGS@
|
PROFILE_GEN_CFLAGS = @PROFILE_GEN_CFLAGS@
|
||||||
PROFILE_GEN_LDFLAGS = @PROFILE_GEN_LDFLAGS@
|
PROFILE_GEN_LDFLAGS = @PROFILE_GEN_LDFLAGS@
|
||||||
|
|
|
@ -362,9 +362,6 @@ endif
|
||||||
|
|
||||||
endif # !GNU_CC
|
endif # !GNU_CC
|
||||||
|
|
||||||
ifdef ENABLE_CXX_EXCEPTIONS
|
|
||||||
CXXFLAGS += $(MOZ_EXCEPTIONS_FLAGS_ON) -DMOZ_CPP_EXCEPTIONS=1
|
|
||||||
endif # ENABLE_CXX_EXCEPTIONS
|
|
||||||
endif # WINNT
|
endif # WINNT
|
||||||
|
|
||||||
ifeq ($(SOLARIS_SUNPRO_CXX),1)
|
ifeq ($(SOLARIS_SUNPRO_CXX),1)
|
||||||
|
|
|
@ -752,8 +752,6 @@ case "$target" in
|
||||||
|
|
||||||
_MOZ_RTTI_FLAGS_ON='-GR'
|
_MOZ_RTTI_FLAGS_ON='-GR'
|
||||||
_MOZ_RTTI_FLAGS_OFF='-GR-'
|
_MOZ_RTTI_FLAGS_OFF='-GR-'
|
||||||
_MOZ_EXCEPTIONS_FLAGS_ON='-EHsc'
|
|
||||||
_MOZ_EXCEPTIONS_FLAGS_OFF=''
|
|
||||||
|
|
||||||
dnl Ensure that mt.exe is 'Microsoft (R) Manifest Tool',
|
dnl Ensure that mt.exe is 'Microsoft (R) Manifest Tool',
|
||||||
dnl not something else like "magnetic tape manipulation utility".
|
dnl not something else like "magnetic tape manipulation utility".
|
||||||
|
@ -1491,8 +1489,6 @@ if test "$GNU_CC"; then
|
||||||
ASFLAGS="$ASFLAGS -fPIC"
|
ASFLAGS="$ASFLAGS -fPIC"
|
||||||
_MOZ_RTTI_FLAGS_ON=${_COMPILER_PREFIX}-frtti
|
_MOZ_RTTI_FLAGS_ON=${_COMPILER_PREFIX}-frtti
|
||||||
_MOZ_RTTI_FLAGS_OFF=${_COMPILER_PREFIX}-fno-rtti
|
_MOZ_RTTI_FLAGS_OFF=${_COMPILER_PREFIX}-fno-rtti
|
||||||
_MOZ_EXCEPTIONS_FLAGS_ON='-fexceptions'
|
|
||||||
_MOZ_EXCEPTIONS_FLAGS_OFF='-fno-exceptions'
|
|
||||||
|
|
||||||
# Turn on GNU specific features
|
# Turn on GNU specific features
|
||||||
# -Wall - turn on all warnings
|
# -Wall - turn on all warnings
|
||||||
|
@ -2336,9 +2332,7 @@ ia64*-hpux*)
|
||||||
LIBS="-lCrun -lCstd -lc $LIBS"
|
LIBS="-lCrun -lCstd -lc $LIBS"
|
||||||
AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES)
|
AC_DEFINE(NSCAP_DISABLE_DEBUG_PTR_TYPES)
|
||||||
CFLAGS="$CFLAGS -xlibmieee -xstrconst -xbuiltin=%all -D__FUNCTION__=__func__"
|
CFLAGS="$CFLAGS -xlibmieee -xstrconst -xbuiltin=%all -D__FUNCTION__=__func__"
|
||||||
CXXFLAGS="$CXXFLAGS -xlibmieee -xbuiltin=%all -features=tmplife,tmplrefstatic,extensions -norunpath -D__FUNCTION__=__func__ -template=no%extdef"
|
CXXFLAGS="$CXXFLAGS -xlibmieee -xbuiltin=%all -features=tmplife,tmplrefstatic,extensions,no%except -norunpath -D__FUNCTION__=__func__ -template=no%extdef"
|
||||||
_MOZ_EXCEPTIONS_FLAGS_ON='-features=except'
|
|
||||||
_MOZ_EXCEPTIONS_FLAGS_OFF='-features=no%except'
|
|
||||||
LDFLAGS="-xildoff $LDFLAGS"
|
LDFLAGS="-xildoff $LDFLAGS"
|
||||||
if test -z "$CROSS_COMPILE" -a -f /usr/lib/ld/map.noexstk; then
|
if test -z "$CROSS_COMPILE" -a -f /usr/lib/ld/map.noexstk; then
|
||||||
_SAVE_LDFLAGS=$LDFLAGS
|
_SAVE_LDFLAGS=$LDFLAGS
|
||||||
|
@ -4856,24 +4850,6 @@ fi
|
||||||
|
|
||||||
AC_SUBST(_MOZ_RTTI_FLAGS_ON)
|
AC_SUBST(_MOZ_RTTI_FLAGS_ON)
|
||||||
|
|
||||||
dnl ========================================================
|
|
||||||
dnl C++ exceptions (g++/VC/Sun only - for now)
|
|
||||||
dnl Should be smarter and check that the compiler does indeed have exceptions
|
|
||||||
dnl ========================================================
|
|
||||||
MOZ_ARG_ENABLE_BOOL(cpp-exceptions,
|
|
||||||
[ --enable-cpp-exceptions Enable C++ exceptions ],
|
|
||||||
[ _MOZ_CPP_EXCEPTIONS=1 ],
|
|
||||||
[ _MOZ_CPP_EXCEPTIONS= ])
|
|
||||||
|
|
||||||
if test "$_MOZ_CPP_EXCEPTIONS"; then
|
|
||||||
_MOZ_EXCEPTIONS_FLAGS=$_MOZ_EXCEPTIONS_FLAGS_ON
|
|
||||||
AC_DEFINE(MOZ_CPP_EXCEPTIONS)
|
|
||||||
else
|
|
||||||
_MOZ_EXCEPTIONS_FLAGS=$_MOZ_EXCEPTIONS_FLAGS_OFF
|
|
||||||
fi
|
|
||||||
|
|
||||||
AC_SUBST(_MOZ_EXCEPTIONS_FLAGS_ON)
|
|
||||||
|
|
||||||
AC_DEFINE(CPP_THROW_NEW, [throw()])
|
AC_DEFINE(CPP_THROW_NEW, [throw()])
|
||||||
AC_LANG_C
|
AC_LANG_C
|
||||||
|
|
||||||
|
@ -5138,7 +5114,6 @@ CFLAGS=`echo \
|
||||||
|
|
||||||
CXXFLAGS=`echo \
|
CXXFLAGS=`echo \
|
||||||
$_MOZ_RTTI_FLAGS \
|
$_MOZ_RTTI_FLAGS \
|
||||||
$_MOZ_EXCEPTIONS_FLAGS \
|
|
||||||
$_WARNINGS_CXXFLAGS \
|
$_WARNINGS_CXXFLAGS \
|
||||||
$CXXFLAGS`
|
$CXXFLAGS`
|
||||||
|
|
||||||
|
|
|
@ -425,7 +425,7 @@ BytecodeCompiler::compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipa
|
||||||
if (nargs) {
|
if (nargs) {
|
||||||
/*
|
/*
|
||||||
* NB: do not use AutoLocalNameArray because it will release space
|
* NB: do not use AutoLocalNameArray because it will release space
|
||||||
* allocated from cx->tempPool by DefineArg.
|
* allocated from cx->tempLifoAlloc by DefineArg.
|
||||||
*/
|
*/
|
||||||
Vector<JSAtom *> names(cx);
|
Vector<JSAtom *> names(cx);
|
||||||
if (!funcg.bindings.getLocalNameArray(cx, &names)) {
|
if (!funcg.bindings.getLocalNameArray(cx, &names)) {
|
||||||
|
|
|
@ -5556,13 +5556,301 @@ EmitWith(JSContext *cx, CodeGenerator *cg, ParseNode *pn, JSBool &ok)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
EmitForIn(JSContext *cx, CodeGenerator *cg, ParseNode *pn, ptrdiff_t top)
|
||||||
|
{
|
||||||
|
StmtInfo stmtInfo;
|
||||||
|
PushStatement(cg, &stmtInfo, STMT_FOR_IN_LOOP, top);
|
||||||
|
|
||||||
|
ParseNode *forHead = pn->pn_left;
|
||||||
|
ParseNode *forBody = pn->pn_right;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the left part is 'var x', emit code to define x if necessary
|
||||||
|
* using a prolog opcode, but do not emit a pop. If the left part
|
||||||
|
* was originally 'var x = i', the parser will have rewritten it;
|
||||||
|
* see Parser::forStatement. 'for (let x = i in o)' is mercifully
|
||||||
|
* banned.
|
||||||
|
*/
|
||||||
|
bool forLet = false;
|
||||||
|
if (ParseNode *decl = forHead->pn_kid1) {
|
||||||
|
JS_ASSERT(TokenKindIsDecl(decl->getKind()));
|
||||||
|
forLet = decl->isKind(TOK_LET);
|
||||||
|
cg->flags |= TCF_IN_FOR_INIT;
|
||||||
|
if (!EmitTree(cx, cg, decl))
|
||||||
|
return false;
|
||||||
|
cg->flags &= ~TCF_IN_FOR_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Compile the object expression to the right of 'in'. */
|
||||||
|
{
|
||||||
|
TempPopScope tps;
|
||||||
|
if (forLet && !tps.popBlock(cx, cg))
|
||||||
|
return false;
|
||||||
|
if (!EmitTree(cx, cg, forHead->pn_kid3))
|
||||||
|
return false;
|
||||||
|
if (forLet && !tps.repushBlock(cx, cg))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emit a bytecode to convert top of stack value to the iterator
|
||||||
|
* object depending on the loop variant (for-in, for-each-in, or
|
||||||
|
* destructuring for-in).
|
||||||
|
*/
|
||||||
|
JS_ASSERT(pn->isOp(JSOP_ITER));
|
||||||
|
if (Emit2(cx, cg, JSOP_ITER, (uint8) pn->pn_iflags) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Annotate so the decompiler can find the loop-closing jump. */
|
||||||
|
intN noteIndex = NewSrcNote(cx, cg, SRC_FOR_IN);
|
||||||
|
if (noteIndex < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Jump down to the loop condition to minimize overhead assuming at
|
||||||
|
* least one iteration, as the other loop forms do.
|
||||||
|
*/
|
||||||
|
ptrdiff_t jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
|
||||||
|
if (jmp < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
intN noteIndex2 = NewSrcNote(cx, cg, SRC_TRACE);
|
||||||
|
if (noteIndex2 < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
top = CG_OFFSET(cg);
|
||||||
|
SET_STATEMENT_TOP(&stmtInfo, top);
|
||||||
|
if (EmitTraceOp(cx, cg, NULL) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
intN loopDepth = cg->stackDepth;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Emit code to get the next enumeration value and assign it to the
|
||||||
|
* left hand side. The JSOP_POP after this assignment is annotated
|
||||||
|
* so that the decompiler can distinguish 'for (x in y)' from
|
||||||
|
* 'for (var x in y)'.
|
||||||
|
*/
|
||||||
|
if (!EmitAssignment(cx, cg, forHead->pn_kid2, JSOP_NOP, NULL))
|
||||||
|
return false;
|
||||||
|
ptrdiff_t tmp2 = CG_OFFSET(cg);
|
||||||
|
if (forHead->pn_kid1 && NewSrcNote2(cx, cg, SRC_DECL,
|
||||||
|
(forHead->pn_kid1->isOp(JSOP_DEFVAR))
|
||||||
|
? SRC_DECL_VAR
|
||||||
|
: SRC_DECL_LET) < 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (Emit1(cx, cg, JSOP_POP) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* The stack should be balanced around the assignment opcode sequence. */
|
||||||
|
JS_ASSERT(cg->stackDepth == loopDepth);
|
||||||
|
|
||||||
|
/* Emit code for the loop body. */
|
||||||
|
if (!EmitTree(cx, cg, forBody))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Set loop and enclosing "update" offsets, for continue. */
|
||||||
|
StmtInfo *stmt = &stmtInfo;
|
||||||
|
do {
|
||||||
|
stmt->update = CG_OFFSET(cg);
|
||||||
|
} while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Fixup the goto that starts the loop to jump down to JSOP_MOREITER.
|
||||||
|
*/
|
||||||
|
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
|
||||||
|
if (Emit1(cx, cg, JSOP_MOREITER) < 0)
|
||||||
|
return false;
|
||||||
|
ptrdiff_t beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg));
|
||||||
|
if (beq < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
|
||||||
|
* note gets bigger.
|
||||||
|
*/
|
||||||
|
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex2, 0, beq - top))
|
||||||
|
return false;
|
||||||
|
/* Set the first srcnote offset so we can find the start of the loop body. */
|
||||||
|
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp2 - jmp))
|
||||||
|
return false;
|
||||||
|
/* Set the second srcnote offset so we can find the closing jump. */
|
||||||
|
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, beq - jmp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Now fixup all breaks and continues (before the JSOP_ENDITER). */
|
||||||
|
if (!PopStatementCG(cx, cg))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!NewTryNote(cx, cg, JSTRY_ITER, cg->stackDepth, top, CG_OFFSET(cg)))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return Emit1(cx, cg, JSOP_ENDITER) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
EmitNormalFor(JSContext *cx, CodeGenerator *cg, ParseNode *pn, ptrdiff_t top)
|
||||||
|
{
|
||||||
|
StmtInfo stmtInfo;
|
||||||
|
PushStatement(cg, &stmtInfo, STMT_FOR_LOOP, top);
|
||||||
|
|
||||||
|
ParseNode *forHead = pn->pn_left;
|
||||||
|
ParseNode *forBody = pn->pn_right;
|
||||||
|
|
||||||
|
/* C-style for (init; cond; update) ... loop. */
|
||||||
|
JSOp op = JSOP_POP;
|
||||||
|
ParseNode *pn3 = forHead->pn_kid1;
|
||||||
|
if (!pn3) {
|
||||||
|
/* No initializer: emit an annotated nop for the decompiler. */
|
||||||
|
op = JSOP_NOP;
|
||||||
|
} else {
|
||||||
|
cg->flags |= TCF_IN_FOR_INIT;
|
||||||
|
#if JS_HAS_DESTRUCTURING
|
||||||
|
if (pn3->isKind(TOK_ASSIGN) &&
|
||||||
|
!MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (op == JSOP_POP) {
|
||||||
|
if (!EmitTree(cx, cg, pn3))
|
||||||
|
return false;
|
||||||
|
if (TokenKindIsDecl(pn3->getKind())) {
|
||||||
|
/*
|
||||||
|
* Check whether a destructuring-initialized var decl
|
||||||
|
* was optimized to a group assignment. If so, we do
|
||||||
|
* not need to emit a pop below, so switch to a nop,
|
||||||
|
* just for the decompiler.
|
||||||
|
*/
|
||||||
|
JS_ASSERT(pn3->isArity(PN_LIST));
|
||||||
|
if (pn3->pn_xflags & PNX_GROUPINIT)
|
||||||
|
op = JSOP_NOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cg->flags &= ~TCF_IN_FOR_INIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH).
|
||||||
|
* Use tmp to hold the biased srcnote "top" offset, which differs
|
||||||
|
* from the top local variable by the length of the JSOP_GOTO{,X}
|
||||||
|
* emitted in between tmp and top if this loop has a condition.
|
||||||
|
*/
|
||||||
|
intN noteIndex = NewSrcNote(cx, cg, SRC_FOR);
|
||||||
|
if (noteIndex < 0 || Emit1(cx, cg, op) < 0)
|
||||||
|
return false;
|
||||||
|
ptrdiff_t tmp = CG_OFFSET(cg);
|
||||||
|
|
||||||
|
ptrdiff_t jmp = -1;
|
||||||
|
if (forHead->pn_kid2) {
|
||||||
|
/* Goto the loop condition, which branches back to iterate. */
|
||||||
|
jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
|
||||||
|
if (jmp < 0)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
top = CG_OFFSET(cg);
|
||||||
|
SET_STATEMENT_TOP(&stmtInfo, top);
|
||||||
|
|
||||||
|
intN noteIndex2 = NewSrcNote(cx, cg, SRC_TRACE);
|
||||||
|
if (noteIndex2 < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Emit code for the loop body. */
|
||||||
|
if (EmitTraceOp(cx, cg, forBody) < 0)
|
||||||
|
return false;
|
||||||
|
if (!EmitTree(cx, cg, forBody))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Set the second note offset so we can find the update part. */
|
||||||
|
JS_ASSERT(noteIndex != -1);
|
||||||
|
ptrdiff_t tmp2 = CG_OFFSET(cg);
|
||||||
|
|
||||||
|
/* Set loop and enclosing "update" offsets, for continue. */
|
||||||
|
StmtInfo *stmt = &stmtInfo;
|
||||||
|
do {
|
||||||
|
stmt->update = CG_OFFSET(cg);
|
||||||
|
} while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
|
||||||
|
|
||||||
|
/* Check for update code to do before the condition (if any). */
|
||||||
|
pn3 = forHead->pn_kid3;
|
||||||
|
if (pn3) {
|
||||||
|
op = JSOP_POP;
|
||||||
|
#if JS_HAS_DESTRUCTURING
|
||||||
|
if (pn3->isKind(TOK_ASSIGN) &&
|
||||||
|
!MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (op == JSOP_POP && !EmitTree(cx, cg, pn3))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Always emit the POP or NOP, to help the decompiler. */
|
||||||
|
if (Emit1(cx, cg, op) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Restore the absolute line number for source note readers. */
|
||||||
|
ptrdiff_t lineno = pn->pn_pos.end.lineno;
|
||||||
|
if (CG_CURRENT_LINE(cg) != (uintN) lineno) {
|
||||||
|
if (NewSrcNote2(cx, cg, SRC_SETLINE, lineno) < 0)
|
||||||
|
return false;
|
||||||
|
CG_CURRENT_LINE(cg) = (uintN) lineno;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrdiff_t tmp3 = CG_OFFSET(cg);
|
||||||
|
|
||||||
|
if (forHead->pn_kid2) {
|
||||||
|
/* Fix up the goto from top to target the loop condition. */
|
||||||
|
JS_ASSERT(jmp >= 0);
|
||||||
|
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
|
||||||
|
|
||||||
|
if (!EmitTree(cx, cg, forHead->pn_kid2))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
|
||||||
|
* note gets bigger.
|
||||||
|
*/
|
||||||
|
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex2, 0, CG_OFFSET(cg) - top))
|
||||||
|
return false;
|
||||||
|
/* Set the first note offset so we can find the loop condition. */
|
||||||
|
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp3 - tmp))
|
||||||
|
return false;
|
||||||
|
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, tmp2 - tmp))
|
||||||
|
return false;
|
||||||
|
/* The third note offset helps us find the loop-closing jump. */
|
||||||
|
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2, CG_OFFSET(cg) - tmp))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* If no loop condition, just emit a loop-closing jump. */
|
||||||
|
op = forHead->pn_kid2 ? JSOP_IFNE : JSOP_GOTO;
|
||||||
|
if (EmitJump(cx, cg, op, top - CG_OFFSET(cg)) < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
/* Now fixup all breaks and continues. */
|
||||||
|
return PopStatementCG(cx, cg);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
EmitFor(JSContext *cx, CodeGenerator *cg, ParseNode *pn, ptrdiff_t top)
|
||||||
|
{
|
||||||
|
return pn->pn_left->isKind(TOK_IN)
|
||||||
|
? EmitForIn(cx, cg, pn, top)
|
||||||
|
: EmitNormalFor(cx, cg, pn, top);
|
||||||
|
}
|
||||||
|
|
||||||
JSBool
|
JSBool
|
||||||
EmitTree(JSContext *cx, CodeGenerator *cg, ParseNode *pn)
|
EmitTree(JSContext *cx, CodeGenerator *cg, ParseNode *pn)
|
||||||
{
|
{
|
||||||
JSBool useful, wantval;
|
JSBool useful, wantval;
|
||||||
StmtInfo stmtInfo;
|
StmtInfo stmtInfo;
|
||||||
StmtInfo *stmt;
|
StmtInfo *stmt;
|
||||||
ptrdiff_t top, off, tmp, beq, jmp, tmp2, tmp3;
|
ptrdiff_t top, off, tmp, beq, jmp;
|
||||||
ParseNode *pn2, *pn3;
|
ParseNode *pn2, *pn3;
|
||||||
JSAtom *atom;
|
JSAtom *atom;
|
||||||
jsatomid atomIndex;
|
jsatomid atomIndex;
|
||||||
|
@ -5860,280 +6148,7 @@ EmitTree(JSContext *cx, CodeGenerator *cg, ParseNode *pn)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOK_FOR:
|
case TOK_FOR:
|
||||||
beq = 0; /* suppress gcc warnings */
|
ok = EmitFor(cx, cg, pn, top);
|
||||||
jmp = -1;
|
|
||||||
pn2 = pn->pn_left;
|
|
||||||
PushStatement(cg, &stmtInfo, STMT_FOR_LOOP, top);
|
|
||||||
|
|
||||||
if (pn2->isKind(TOK_IN)) {
|
|
||||||
/* Set stmtInfo type for later testing. */
|
|
||||||
stmtInfo.type = STMT_FOR_IN_LOOP;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* If the left part is 'var x', emit code to define x if necessary
|
|
||||||
* using a prolog opcode, but do not emit a pop. If the left part
|
|
||||||
* was originally 'var x = i', the parser will have rewritten it;
|
|
||||||
* see Parser::forStatement. 'for (let x = i in o)' is mercifully
|
|
||||||
* banned.
|
|
||||||
*/
|
|
||||||
bool forLet = false;
|
|
||||||
if (ParseNode *decl = pn2->pn_kid1) {
|
|
||||||
JS_ASSERT(TokenKindIsDecl(decl->getKind()));
|
|
||||||
forLet = decl->isKind(TOK_LET);
|
|
||||||
cg->flags |= TCF_IN_FOR_INIT;
|
|
||||||
if (!EmitTree(cx, cg, decl))
|
|
||||||
return JS_FALSE;
|
|
||||||
cg->flags &= ~TCF_IN_FOR_INIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Compile the object expression to the right of 'in'. */
|
|
||||||
{
|
|
||||||
TempPopScope tps;
|
|
||||||
if (forLet && !tps.popBlock(cx, cg))
|
|
||||||
return JS_FALSE;
|
|
||||||
if (!EmitTree(cx, cg, pn2->pn_kid3))
|
|
||||||
return JS_FALSE;
|
|
||||||
if (forLet && !tps.repushBlock(cx, cg))
|
|
||||||
return JS_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Emit a bytecode to convert top of stack value to the iterator
|
|
||||||
* object depending on the loop variant (for-in, for-each-in, or
|
|
||||||
* destructuring for-in).
|
|
||||||
*/
|
|
||||||
JS_ASSERT(pn->isOp(JSOP_ITER));
|
|
||||||
if (Emit2(cx, cg, JSOP_ITER, (uint8) pn->pn_iflags) < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
/* Annotate so the decompiler can find the loop-closing jump. */
|
|
||||||
noteIndex = NewSrcNote(cx, cg, SRC_FOR_IN);
|
|
||||||
if (noteIndex < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Jump down to the loop condition to minimize overhead assuming at
|
|
||||||
* least one iteration, as the other loop forms do.
|
|
||||||
*/
|
|
||||||
jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
|
|
||||||
if (jmp < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
noteIndex2 = NewSrcNote(cx, cg, SRC_TRACE);
|
|
||||||
if (noteIndex2 < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
top = CG_OFFSET(cg);
|
|
||||||
SET_STATEMENT_TOP(&stmtInfo, top);
|
|
||||||
if (EmitTraceOp(cx, cg, NULL) < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
#ifdef DEBUG
|
|
||||||
intN loopDepth = cg->stackDepth;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Emit code to get the next enumeration value and assign it to the
|
|
||||||
* left hand side. The JSOP_POP after this assignment is annotated
|
|
||||||
* so that the decompiler can distinguish 'for (x in y)' from
|
|
||||||
* 'for (var x in y)'.
|
|
||||||
*/
|
|
||||||
if (!EmitAssignment(cx, cg, pn2->pn_kid2, JSOP_NOP, NULL))
|
|
||||||
return false;
|
|
||||||
tmp2 = CG_OFFSET(cg);
|
|
||||||
if (pn2->pn_kid1 && NewSrcNote2(cx, cg, SRC_DECL, (pn2->pn_kid1->isOp(JSOP_DEFVAR))
|
|
||||||
? SRC_DECL_VAR
|
|
||||||
: SRC_DECL_LET) < 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (Emit1(cx, cg, JSOP_POP) < 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
/* The stack should be balanced around the assignment opcode sequence. */
|
|
||||||
JS_ASSERT(cg->stackDepth == loopDepth);
|
|
||||||
|
|
||||||
/* Emit code for the loop body. */
|
|
||||||
if (!EmitTree(cx, cg, pn->pn_right))
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
/* Set loop and enclosing "update" offsets, for continue. */
|
|
||||||
stmt = &stmtInfo;
|
|
||||||
do {
|
|
||||||
stmt->update = CG_OFFSET(cg);
|
|
||||||
} while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Fixup the goto that starts the loop to jump down to JSOP_MOREITER.
|
|
||||||
*/
|
|
||||||
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
|
|
||||||
if (Emit1(cx, cg, JSOP_MOREITER) < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg));
|
|
||||||
if (beq < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
|
|
||||||
* note gets bigger.
|
|
||||||
*/
|
|
||||||
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex2, 0, beq - top))
|
|
||||||
return JS_FALSE;
|
|
||||||
/* Set the first srcnote offset so we can find the start of the loop body. */
|
|
||||||
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp2 - jmp))
|
|
||||||
return JS_FALSE;
|
|
||||||
/* Set the second srcnote offset so we can find the closing jump. */
|
|
||||||
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, beq - jmp))
|
|
||||||
return JS_FALSE;
|
|
||||||
} else {
|
|
||||||
/* C-style for (init; cond; update) ... loop. */
|
|
||||||
op = JSOP_POP;
|
|
||||||
pn3 = pn2->pn_kid1;
|
|
||||||
if (!pn3) {
|
|
||||||
/* No initializer: emit an annotated nop for the decompiler. */
|
|
||||||
op = JSOP_NOP;
|
|
||||||
} else {
|
|
||||||
cg->flags |= TCF_IN_FOR_INIT;
|
|
||||||
#if JS_HAS_DESTRUCTURING
|
|
||||||
if (pn3->isKind(TOK_ASSIGN) &&
|
|
||||||
!MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
|
|
||||||
return JS_FALSE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (op == JSOP_POP) {
|
|
||||||
if (!EmitTree(cx, cg, pn3))
|
|
||||||
return JS_FALSE;
|
|
||||||
if (TokenKindIsDecl(pn3->getKind())) {
|
|
||||||
/*
|
|
||||||
* Check whether a destructuring-initialized var decl
|
|
||||||
* was optimized to a group assignment. If so, we do
|
|
||||||
* not need to emit a pop below, so switch to a nop,
|
|
||||||
* just for the decompiler.
|
|
||||||
*/
|
|
||||||
JS_ASSERT(pn3->isArity(PN_LIST));
|
|
||||||
if (pn3->pn_xflags & PNX_GROUPINIT)
|
|
||||||
op = JSOP_NOP;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
cg->flags &= ~TCF_IN_FOR_INIT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* NB: the SRC_FOR note has offsetBias 1 (JSOP_{NOP,POP}_LENGTH).
|
|
||||||
* Use tmp to hold the biased srcnote "top" offset, which differs
|
|
||||||
* from the top local variable by the length of the JSOP_GOTO{,X}
|
|
||||||
* emitted in between tmp and top if this loop has a condition.
|
|
||||||
*/
|
|
||||||
noteIndex = NewSrcNote(cx, cg, SRC_FOR);
|
|
||||||
if (noteIndex < 0 || Emit1(cx, cg, op) < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
tmp = CG_OFFSET(cg);
|
|
||||||
|
|
||||||
if (pn2->pn_kid2) {
|
|
||||||
/* Goto the loop condition, which branches back to iterate. */
|
|
||||||
jmp = EmitJump(cx, cg, JSOP_GOTO, 0);
|
|
||||||
if (jmp < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
top = CG_OFFSET(cg);
|
|
||||||
SET_STATEMENT_TOP(&stmtInfo, top);
|
|
||||||
|
|
||||||
noteIndex2 = NewSrcNote(cx, cg, SRC_TRACE);
|
|
||||||
if (noteIndex2 < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
/* Emit code for the loop body. */
|
|
||||||
if (EmitTraceOp(cx, cg, pn->pn_right) < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
if (!EmitTree(cx, cg, pn->pn_right))
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
/* Set the second note offset so we can find the update part. */
|
|
||||||
JS_ASSERT(noteIndex != -1);
|
|
||||||
tmp2 = CG_OFFSET(cg);
|
|
||||||
|
|
||||||
/* Set loop and enclosing "update" offsets, for continue. */
|
|
||||||
stmt = &stmtInfo;
|
|
||||||
do {
|
|
||||||
stmt->update = CG_OFFSET(cg);
|
|
||||||
} while ((stmt = stmt->down) != NULL && stmt->type == STMT_LABEL);
|
|
||||||
|
|
||||||
/* Check for update code to do before the condition (if any). */
|
|
||||||
pn3 = pn2->pn_kid3;
|
|
||||||
if (pn3) {
|
|
||||||
op = JSOP_POP;
|
|
||||||
#if JS_HAS_DESTRUCTURING
|
|
||||||
if (pn3->isKind(TOK_ASSIGN) &&
|
|
||||||
!MaybeEmitGroupAssignment(cx, cg, op, pn3, &op)) {
|
|
||||||
return JS_FALSE;
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
if (op == JSOP_POP && !EmitTree(cx, cg, pn3))
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
/* Always emit the POP or NOP, to help the decompiler. */
|
|
||||||
if (Emit1(cx, cg, op) < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
/* Restore the absolute line number for source note readers. */
|
|
||||||
off = (ptrdiff_t) pn->pn_pos.end.lineno;
|
|
||||||
if (CG_CURRENT_LINE(cg) != (uintN) off) {
|
|
||||||
if (NewSrcNote2(cx, cg, SRC_SETLINE, off) < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
CG_CURRENT_LINE(cg) = (uintN) off;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tmp3 = CG_OFFSET(cg);
|
|
||||||
|
|
||||||
if (pn2->pn_kid2) {
|
|
||||||
/* Fix up the goto from top to target the loop condition. */
|
|
||||||
JS_ASSERT(jmp >= 0);
|
|
||||||
CHECK_AND_SET_JUMP_OFFSET_AT(cx, cg, jmp);
|
|
||||||
|
|
||||||
if (!EmitTree(cx, cg, pn2->pn_kid2))
|
|
||||||
return JS_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Be careful: We must set noteIndex2 before noteIndex in case the noteIndex
|
|
||||||
* note gets bigger.
|
|
||||||
*/
|
|
||||||
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex2, 0, CG_OFFSET(cg) - top))
|
|
||||||
return JS_FALSE;
|
|
||||||
/* Set the first note offset so we can find the loop condition. */
|
|
||||||
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 0, tmp3 - tmp))
|
|
||||||
return JS_FALSE;
|
|
||||||
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 1, tmp2 - tmp))
|
|
||||||
return JS_FALSE;
|
|
||||||
/* The third note offset helps us find the loop-closing jump. */
|
|
||||||
if (!SetSrcNoteOffset(cx, cg, (uintN)noteIndex, 2, CG_OFFSET(cg) - tmp))
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
if (pn2->pn_kid2) {
|
|
||||||
beq = EmitJump(cx, cg, JSOP_IFNE, top - CG_OFFSET(cg));
|
|
||||||
if (beq < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
} else {
|
|
||||||
/* No loop condition -- emit the loop-closing jump. */
|
|
||||||
jmp = EmitJump(cx, cg, JSOP_GOTO, top - CG_OFFSET(cg));
|
|
||||||
if (jmp < 0)
|
|
||||||
return JS_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Now fixup all breaks and continues (before for/in's JSOP_ENDITER). */
|
|
||||||
if (!PopStatementCG(cx, cg))
|
|
||||||
return JS_FALSE;
|
|
||||||
|
|
||||||
if (pn2->isKind(TOK_IN)) {
|
|
||||||
if (!NewTryNote(cx, cg, JSTRY_ITER, cg->stackDepth, top, CG_OFFSET(cg)) ||
|
|
||||||
Emit1(cx, cg, JSOP_ENDITER) < 0)
|
|
||||||
{
|
|
||||||
return JS_FALSE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TOK_BREAK: {
|
case TOK_BREAK: {
|
||||||
|
|
|
@ -678,9 +678,10 @@ struct CodeGenerator : public TreeContext
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Note that cgs are magic: they own the arena "top-of-stack" space above
|
* Note that cgs are magic: they own the arena "top-of-stack" space
|
||||||
* their tempMark points. This means that you cannot alloc from tempPool
|
* above their tempMark points. This means that you cannot alloc from
|
||||||
* and save the pointer beyond the next CodeGenerator destructor call.
|
* tempLifoAlloc and save the pointer beyond the next CodeGenerator
|
||||||
|
* destructor call.
|
||||||
*/
|
*/
|
||||||
~CodeGenerator();
|
~CodeGenerator();
|
||||||
|
|
||||||
|
|
|
@ -176,10 +176,11 @@ Parser::newObjectBox(JSObject *obj)
|
||||||
JS_ASSERT(obj);
|
JS_ASSERT(obj);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We use JSContext.tempPool to allocate parsed objects and place them on
|
* We use JSContext.tempLifoAlloc to allocate parsed objects and place them
|
||||||
* a list in this Parser to ensure GC safety. Thus the tempPool arenas
|
* on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
|
||||||
* containing the entries must be alive until we are done with scanning,
|
* arenas containing the entries must be alive until we are done with
|
||||||
* parsing and code generation for the whole script or top-level function.
|
* scanning, parsing and code generation for the whole script or top-level
|
||||||
|
* function.
|
||||||
*/
|
*/
|
||||||
ObjectBox *objbox = context->tempLifoAlloc().new_<ObjectBox>();
|
ObjectBox *objbox = context->tempLifoAlloc().new_<ObjectBox>();
|
||||||
if (!objbox) {
|
if (!objbox) {
|
||||||
|
@ -201,10 +202,11 @@ Parser::newFunctionBox(JSObject *obj, ParseNode *fn, TreeContext *tc)
|
||||||
JS_ASSERT(obj->isFunction());
|
JS_ASSERT(obj->isFunction());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We use JSContext.tempPool to allocate parsed objects and place them on
|
* We use JSContext.tempLifoAlloc to allocate parsed objects and place them
|
||||||
* a list in this Parser to ensure GC safety. Thus the tempPool arenas
|
* on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
|
||||||
* containing the entries must be alive until we are done with scanning,
|
* arenas containing the entries must be alive until we are done with
|
||||||
* parsing and code generation for the whole script or top-level function.
|
* scanning, parsing and code generation for the whole script or top-level
|
||||||
|
* function.
|
||||||
*/
|
*/
|
||||||
FunctionBox *funbox = context->tempLifoAlloc().newPod<FunctionBox>();
|
FunctionBox *funbox = context->tempLifoAlloc().newPod<FunctionBox>();
|
||||||
if (!funbox) {
|
if (!funbox) {
|
||||||
|
|
|
@ -102,7 +102,7 @@ struct Parser : private AutoGCRooter
|
||||||
JSContext *const context; /* FIXME Bug 551291: use AutoGCRooter::context? */
|
JSContext *const context; /* FIXME Bug 551291: use AutoGCRooter::context? */
|
||||||
void *tempFreeList[NUM_TEMP_FREELISTS];
|
void *tempFreeList[NUM_TEMP_FREELISTS];
|
||||||
TokenStream tokenStream;
|
TokenStream tokenStream;
|
||||||
void *tempPoolMark; /* initial JSContext.tempPool mark */
|
void *tempPoolMark; /* initial JSContext.tempLifoAlloc mark */
|
||||||
JSPrincipals *principals; /* principals associated with source */
|
JSPrincipals *principals; /* principals associated with source */
|
||||||
StackFrame *const callerFrame; /* scripted caller frame for eval and dbgapi */
|
StackFrame *const callerFrame; /* scripted caller frame for eval and dbgapi */
|
||||||
JSObject *const callerVarObj; /* callerFrame's varObj */
|
JSObject *const callerVarObj; /* callerFrame's varObj */
|
||||||
|
@ -125,10 +125,11 @@ struct Parser : private AutoGCRooter
|
||||||
friend struct BytecodeCompiler;
|
friend struct BytecodeCompiler;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Initialize a parser. Parameters are passed on to init tokenStream.
|
* Initialize a parser. Parameters are passed on to init tokenStream. The
|
||||||
* The compiler owns the arena pool "tops-of-stack" space above the current
|
* compiler owns the arena pool "tops-of-stack" space above the current
|
||||||
* JSContext.tempPool mark. This means you cannot allocate from tempPool
|
* JSContext.tempLifoAlloc mark. This means you cannot allocate from
|
||||||
* and save the pointer beyond the next Parser destructor invocation.
|
* tempLifoAlloc and save the pointer beyond the next Parser destructor
|
||||||
|
* invocation.
|
||||||
*/
|
*/
|
||||||
bool init(const jschar *base, size_t length, const char *filename, uintN lineno,
|
bool init(const jschar *base, size_t length, const char *filename, uintN lineno,
|
||||||
JSVersion version);
|
JSVersion version);
|
||||||
|
@ -150,7 +151,8 @@ struct Parser : private AutoGCRooter
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate a new parsed object or function container from cx->tempPool.
|
* Allocate a new parsed object or function container from
|
||||||
|
* cx->tempLifoAlloc.
|
||||||
*/
|
*/
|
||||||
ObjectBox *newObjectBox(JSObject *obj);
|
ObjectBox *newObjectBox(JSObject *obj);
|
||||||
|
|
||||||
|
|
|
@ -384,7 +384,7 @@ class TokenStream
|
||||||
* first call |close| then call the destructor. If |init| fails, do not call
|
* first call |close| then call the destructor. If |init| fails, do not call
|
||||||
* |close|.
|
* |close|.
|
||||||
*
|
*
|
||||||
* This class uses JSContext.tempPool to allocate internal buffers. The
|
* This class uses JSContext.tempLifoAlloc to allocate internal buffers. The
|
||||||
* caller should JS_ARENA_MARK before calling |init| and JS_ARENA_RELEASE
|
* caller should JS_ARENA_MARK before calling |init| and JS_ARENA_RELEASE
|
||||||
* after calling |close|.
|
* after calling |close|.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -2181,8 +2181,8 @@ Function(JSContext *cx, uintN argc, Value *vp)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate a string to hold the concatenated arguments, including room
|
* Allocate a string to hold the concatenated arguments, including room
|
||||||
* for a terminating 0. Mark cx->tempPool for later release, to free
|
* for a terminating 0. Mark cx->tempLifeAlloc for later release, to
|
||||||
* collected_args and its tokenstream in one swoop.
|
* free collected_args and its tokenstream in one swoop.
|
||||||
*/
|
*/
|
||||||
LifoAllocScope las(&cx->tempLifoAlloc());
|
LifoAllocScope las(&cx->tempLifoAlloc());
|
||||||
jschar *cp = cx->tempLifoAlloc().newArray<jschar>(args_length + 1);
|
jschar *cp = cx->tempLifoAlloc().newArray<jschar>(args_length + 1);
|
||||||
|
|
|
@ -4067,10 +4067,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||||
LOAD_FUNCTION(0);
|
LOAD_FUNCTION(0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* All allocation when decompiling is LIFO, using malloc
|
* All allocation when decompiling is LIFO, using malloc or,
|
||||||
* or, more commonly, arena-allocating from cx->tempPool.
|
* more commonly, arena-allocating from cx->tempLifoAlloc
|
||||||
* Therefore after InitSprintStack succeeds, we must
|
* Therefore after InitSprintStack succeeds, we must release
|
||||||
* release to mark before returning.
|
* to mark before returning.
|
||||||
*/
|
*/
|
||||||
LifoAllocScope las(&cx->tempLifoAlloc());
|
LifoAllocScope las(&cx->tempLifoAlloc());
|
||||||
if (fun->script()->bindings.hasLocalNames()) {
|
if (fun->script()->bindings.hasLocalNames()) {
|
||||||
|
@ -4179,9 +4179,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Alas, we have to malloc a copy of the result left on
|
* Alas, we have to malloc a copy of the result left on the
|
||||||
* the top of ss2 because both ss and ss2 arena-allocate
|
* top of ss2 because both ss and ss2 arena-allocate from
|
||||||
* from cx's tempPool.
|
* cx's tempLifoAlloc
|
||||||
*/
|
*/
|
||||||
rval = JS_strdup(cx, PopStr(&ss2, op));
|
rval = JS_strdup(cx, PopStr(&ss2, op));
|
||||||
las.releaseEarly();
|
las.releaseEarly();
|
||||||
|
|
|
@ -648,8 +648,9 @@ js_XDRAtom(JSXDRState *xdr, JSAtom **atomp)
|
||||||
chars = stackChars;
|
chars = stackChars;
|
||||||
} else {
|
} else {
|
||||||
/*
|
/*
|
||||||
* This is very uncommon. Don't use the tempPool arena for this as
|
* This is very uncommon. Don't use the tempLifoAlloc arena for this as
|
||||||
* most allocations here will be bigger than tempPool's arenasize.
|
* most allocations here will be bigger than tempLifoAlloc's default
|
||||||
|
* chunk size.
|
||||||
*/
|
*/
|
||||||
chars = (jschar *) cx->malloc_(nchars * sizeof(jschar));
|
chars = (jschar *) cx->malloc_(nchars * sizeof(jschar));
|
||||||
if (!chars)
|
if (!chars)
|
||||||
|
|
|
@ -1959,7 +1959,7 @@ DocumentViewerImpl::Show(void)
|
||||||
printf("About to evict content viewers: prev=%d, loaded=%d\n",
|
printf("About to evict content viewers: prev=%d, loaded=%d\n",
|
||||||
prevIndex, loadedIndex);
|
prevIndex, loadedIndex);
|
||||||
#endif
|
#endif
|
||||||
historyInt->EvictContentViewers(prevIndex, loadedIndex);
|
historyInt->EvictOutOfRangeContentViewers(loadedIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,8 @@
|
||||||
|
|
||||||
/* a presentation of a document, part 2 */
|
/* a presentation of a document, part 2 */
|
||||||
|
|
||||||
|
#include "mozilla/dom/PBrowserChild.h"
|
||||||
|
#include "mozilla/dom/TabChild.h"
|
||||||
#include "mozilla/Util.h"
|
#include "mozilla/Util.h"
|
||||||
|
|
||||||
#include "nsPresShell.h"
|
#include "nsPresShell.h"
|
||||||
|
@ -5092,6 +5094,11 @@ void PresShell::UpdateCanvasBackground()
|
||||||
if (!FrameConstructor()->GetRootElementFrame()) {
|
if (!FrameConstructor()->GetRootElementFrame()) {
|
||||||
mCanvasBackgroundColor = GetDefaultBackgroundColorToDraw();
|
mCanvasBackgroundColor = GetDefaultBackgroundColorToDraw();
|
||||||
}
|
}
|
||||||
|
if (XRE_GetProcessType() == GeckoProcessType_Content) {
|
||||||
|
if (TabChild* tabChild = GetTabChildFrom(this)) {
|
||||||
|
tabChild->SetBackgroundColor(mCanvasBackgroundColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
nscolor PresShell::ComputeBackstopColor(nsIView* aDisplayRoot)
|
nscolor PresShell::ComputeBackstopColor(nsIView* aDisplayRoot)
|
||||||
|
|
|
@ -114,7 +114,7 @@ private:
|
||||||
|
|
||||||
nsRect
|
nsRect
|
||||||
nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder) {
|
nsDisplayButtonBoxShadowOuter::GetBounds(nsDisplayListBuilder* aBuilder) {
|
||||||
return mFrame->GetVisualOverflowRect() + ToReferenceFrame();
|
return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -153,7 +153,7 @@ public:
|
||||||
// override bounds because the list item focus ring may extend outside
|
// override bounds because the list item focus ring may extend outside
|
||||||
// the nsSelectsAreaFrame
|
// the nsSelectsAreaFrame
|
||||||
nsListControlFrame* listFrame = GetEnclosingListFrame(GetUnderlyingFrame());
|
nsListControlFrame* listFrame = GetEnclosingListFrame(GetUnderlyingFrame());
|
||||||
return listFrame->GetVisualOverflowRect() +
|
return listFrame->GetVisualOverflowRectRelativeToSelf() +
|
||||||
aBuilder->ToReferenceFrame(listFrame);
|
aBuilder->ToReferenceFrame(listFrame);
|
||||||
}
|
}
|
||||||
virtual void Paint(nsDisplayListBuilder* aBuilder,
|
virtual void Paint(nsDisplayListBuilder* aBuilder,
|
||||||
|
|
|
@ -226,7 +226,7 @@ public:
|
||||||
|
|
||||||
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder)
|
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder)
|
||||||
{
|
{
|
||||||
return mFrame->GetVisualOverflowRect() + ToReferenceFrame();
|
return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
|
||||||
}
|
}
|
||||||
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
|
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
|
||||||
HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
|
HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
|
||||||
|
|
|
@ -163,11 +163,15 @@ nsHTMLReflowState::nsHTMLReflowState(nsPresContext* aPresContext,
|
||||||
mLineLayout = aParentReflowState.mLineLayout;
|
mLineLayout = aParentReflowState.mLineLayout;
|
||||||
else
|
else
|
||||||
mLineLayout = nsnull;
|
mLineLayout = nsnull;
|
||||||
mFlags.mIsTopOfPage = aParentReflowState.mFlags.mIsTopOfPage;
|
|
||||||
|
// Note: mFlags was initialized as a copy of aParentReflowState.mFlags up in
|
||||||
|
// this constructor's init list, so the only flags that we need to explicitly
|
||||||
|
// initialize here are those that may need a value other than our parent's.
|
||||||
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
|
mFlags.mNextInFlowUntouched = aParentReflowState.mFlags.mNextInFlowUntouched &&
|
||||||
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
|
CheckNextInFlowParenthood(aFrame, aParentReflowState.frame);
|
||||||
mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = false;
|
mFlags.mAssumingHScrollbar = mFlags.mAssumingVScrollbar = false;
|
||||||
mFlags.mHasClearance = false;
|
mFlags.mHasClearance = false;
|
||||||
|
|
||||||
mDiscoveredClearance = nsnull;
|
mDiscoveredClearance = nsnull;
|
||||||
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
|
mPercentHeightObserver = (aParentReflowState.mPercentHeightObserver &&
|
||||||
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
|
aParentReflowState.mPercentHeightObserver->NeedsToObserve(*this))
|
||||||
|
|
|
@ -4137,7 +4137,7 @@ public:
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
|
virtual nsRect GetBounds(nsDisplayListBuilder* aBuilder) {
|
||||||
return mFrame->GetVisualOverflowRect() + ToReferenceFrame();
|
return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
|
||||||
}
|
}
|
||||||
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
|
virtual void HitTest(nsDisplayListBuilder* aBuilder, const nsRect& aRect,
|
||||||
HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
|
HitTestState* aState, nsTArray<nsIFrame*> *aOutFrames) {
|
||||||
|
|
|
@ -62,28 +62,6 @@ namespace layout {
|
||||||
typedef FrameMetrics::ViewID ViewID;
|
typedef FrameMetrics::ViewID ViewID;
|
||||||
typedef RenderFrameParent::ViewMap ViewMap;
|
typedef RenderFrameParent::ViewMap ViewMap;
|
||||||
|
|
||||||
nsRefPtr<ImageContainer> sCheckerboard = nsnull;
|
|
||||||
|
|
||||||
class CheckerBoardPatternDeleter : public nsIObserver
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_NSIOBSERVER
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_IMPL_ISUPPORTS1(CheckerBoardPatternDeleter, nsIObserver)
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
CheckerBoardPatternDeleter::Observe(nsISupports* aSubject,
|
|
||||||
const char* aTopic,
|
|
||||||
const PRUnichar* aData)
|
|
||||||
{
|
|
||||||
if (!strcmp(aTopic, "xpcom-shutdown")) {
|
|
||||||
sCheckerboard = nsnull;
|
|
||||||
}
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Represents (affine) transforms that are calculated from a content view.
|
// Represents (affine) transforms that are calculated from a content view.
|
||||||
struct ViewTransform {
|
struct ViewTransform {
|
||||||
ViewTransform(nsIntPoint aTranslation = nsIntPoint(0, 0), float aXScale = 1, float aYScale = 1)
|
ViewTransform(nsIntPoint aTranslation = nsIntPoint(0, 0), float aXScale = 1, float aYScale = 1)
|
||||||
|
@ -424,61 +402,14 @@ BuildViewMap(ViewMap& oldContentViews, ViewMap& newContentViews,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#define BOARDSIZE 32
|
|
||||||
#define CHECKERSIZE 16
|
|
||||||
already_AddRefed<gfxASurface>
|
|
||||||
GetBackgroundImage()
|
|
||||||
{
|
|
||||||
static unsigned int data[BOARDSIZE * BOARDSIZE];
|
|
||||||
static bool initialized = false;
|
|
||||||
if (!initialized) {
|
|
||||||
initialized = true;
|
|
||||||
for (unsigned int y = 0; y < BOARDSIZE; y++) {
|
|
||||||
for (unsigned int x = 0; x < BOARDSIZE; x++) {
|
|
||||||
bool col_odd = (x / CHECKERSIZE) & 1;
|
|
||||||
bool row_odd = (y / CHECKERSIZE) & 1;
|
|
||||||
if (col_odd ^ row_odd) { // xor
|
|
||||||
data[y * BOARDSIZE + x] = 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
data[y * BOARDSIZE + x] = 0xFFDDDDDD;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsRefPtr<gfxASurface> s =
|
|
||||||
new gfxImageSurface((unsigned char*) data,
|
|
||||||
gfxIntSize(BOARDSIZE, BOARDSIZE),
|
|
||||||
BOARDSIZE * sizeof(unsigned int),
|
|
||||||
gfxASurface::ImageFormatARGB32);
|
|
||||||
return s.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
BuildBackgroundPatternFor(ContainerLayer* aContainer,
|
BuildBackgroundPatternFor(ContainerLayer* aContainer,
|
||||||
ContainerLayer* aShadowRoot,
|
ContainerLayer* aShadowRoot,
|
||||||
const FrameMetrics& aMetrics,
|
|
||||||
const ViewConfig& aConfig,
|
const ViewConfig& aConfig,
|
||||||
|
const gfxRGBA& aColor,
|
||||||
LayerManager* aManager,
|
LayerManager* aManager,
|
||||||
nsIFrame* aFrame,
|
nsIFrame* aFrame)
|
||||||
nsDisplayListBuilder* aBuilder)
|
|
||||||
{
|
{
|
||||||
// We tile a visible region that is the frame's area \setminus the
|
|
||||||
// rect in our frame onto which valid pixels from remote content
|
|
||||||
// will be drawn. It's just a waste of CPU cycles to draw a
|
|
||||||
// checkerboard behind that content.
|
|
||||||
//
|
|
||||||
// We want to give the background the illusion of moving while the
|
|
||||||
// user pans, so we nudge the tiling area a bit based on the
|
|
||||||
// "desired" scroll offset.
|
|
||||||
//
|
|
||||||
// The background-image layer is added to the layer tree "behind"
|
|
||||||
// the shadow tree. It doesn't matter in theory which is behind/in
|
|
||||||
// front, except that having the background in front of content
|
|
||||||
// means we have to be more careful about snapping boundaries,
|
|
||||||
// whereas having it behind allows us to trade off simplicity for
|
|
||||||
// "wasted" drawing of a few extra pixels.
|
|
||||||
ShadowLayer* shadowRoot = aShadowRoot->AsShadowLayer();
|
ShadowLayer* shadowRoot = aShadowRoot->AsShadowLayer();
|
||||||
gfxMatrix t;
|
gfxMatrix t;
|
||||||
if (!shadowRoot->GetShadowTransform().Is2D(&t)) {
|
if (!shadowRoot->GetShadowTransform().Is2D(&t)) {
|
||||||
|
@ -505,57 +436,14 @@ BuildBackgroundPatternFor(ContainerLayer* aContainer,
|
||||||
if (localIntContentVis.Contains(frameRect)) {
|
if (localIntContentVis.Contains(frameRect)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
nsRefPtr<ColorLayer> layer = aManager->CreateColorLayer();
|
||||||
nsRefPtr<gfxASurface> bgImage = GetBackgroundImage();
|
layer->SetColor(aColor);
|
||||||
gfxIntSize bgImageSize = bgImage->GetSize();
|
|
||||||
|
|
||||||
// Set up goop needed to get a cairo image into its own layer
|
|
||||||
if (!sCheckerboard) {
|
|
||||||
sCheckerboard = aManager->CreateImageContainer().get();
|
|
||||||
const Image::Format fmts[] = { Image::CAIRO_SURFACE };
|
|
||||||
nsRefPtr<Image> img = sCheckerboard->CreateImage(fmts, 1);
|
|
||||||
CairoImage::Data data = { bgImage.get(), bgImageSize };
|
|
||||||
static_cast<CairoImage*>(img.get())->SetData(data);
|
|
||||||
sCheckerboard->SetCurrentImage(img);
|
|
||||||
nsCOMPtr<nsIObserverService> observerService =
|
|
||||||
mozilla::services::GetObserverService();
|
|
||||||
if (!observerService) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
nsresult rv = observerService->AddObserver(new CheckerBoardPatternDeleter, "xpcom-shutdown", false);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
nsRefPtr<ImageLayer> layer = aManager->CreateImageLayer();
|
|
||||||
layer->SetContainer(sCheckerboard);
|
|
||||||
|
|
||||||
// The tile source is the entire background image
|
|
||||||
nsIntRect tileSource(0, 0, bgImageSize.width, bgImageSize.height);
|
|
||||||
layer->SetTileSourceRect(&tileSource);
|
|
||||||
|
|
||||||
// The origin of the tiling plane, top-left of the tile source rect,
|
|
||||||
// is at layer-space point <0,0>. Set up a translation from that
|
|
||||||
// origin to the frame top-left, with the little nudge included.
|
|
||||||
nsIntPoint translation = frameRect.TopLeft();
|
|
||||||
nsIntPoint panNudge = aConfig.mScrollOffset.ToNearestPixels(auPerDevPixel);
|
|
||||||
// This offset must be positive to ensure that the tiling rect
|
|
||||||
// contains the frame's visible rect. The "desired" scroll offset
|
|
||||||
// is allowed to be negative, however, so we fix that up here.
|
|
||||||
panNudge.x = (panNudge.x % bgImageSize.width);
|
|
||||||
if (panNudge.x < 0) panNudge.x += bgImageSize.width;
|
|
||||||
panNudge.y = (panNudge.y % bgImageSize.height);
|
|
||||||
if (panNudge.y < 0) panNudge.y += bgImageSize.height;
|
|
||||||
|
|
||||||
translation -= panNudge;
|
|
||||||
layer->SetTransform(gfx3DMatrix::Translation(translation.x, translation.y, 0));
|
|
||||||
|
|
||||||
// The visible area of the background is the frame's area minus the
|
// The visible area of the background is the frame's area minus the
|
||||||
// content area
|
// content area
|
||||||
nsIntRegion bgRgn(frameRect);
|
nsIntRegion bgRgn(frameRect);
|
||||||
bgRgn.Sub(bgRgn, localIntContentVis);
|
bgRgn.Sub(bgRgn, localIntContentVis);
|
||||||
bgRgn.MoveBy(-translation);
|
bgRgn.MoveBy(-frameRect.TopLeft());
|
||||||
layer->SetVisibleRegion(bgRgn);
|
layer->SetVisibleRegion(bgRgn);
|
||||||
|
|
||||||
aContainer->InsertAfter(layer, nsnull);
|
aContainer->InsertAfter(layer, nsnull);
|
||||||
|
@ -564,6 +452,7 @@ BuildBackgroundPatternFor(ContainerLayer* aContainer,
|
||||||
RenderFrameParent::RenderFrameParent(nsFrameLoader* aFrameLoader)
|
RenderFrameParent::RenderFrameParent(nsFrameLoader* aFrameLoader)
|
||||||
: mFrameLoader(aFrameLoader)
|
: mFrameLoader(aFrameLoader)
|
||||||
, mFrameLoaderDestroyed(false)
|
, mFrameLoaderDestroyed(false)
|
||||||
|
, mBackgroundColor(gfxRGBA(1, 1, 1))
|
||||||
{
|
{
|
||||||
if (aFrameLoader) {
|
if (aFrameLoader) {
|
||||||
mContentViews[FrameMetrics::ROOT_SCROLL_ID] =
|
mContentViews[FrameMetrics::ROOT_SCROLL_ID] =
|
||||||
|
@ -691,9 +580,9 @@ RenderFrameParent::BuildLayer(nsDisplayListBuilder* aBuilder,
|
||||||
const nsContentView* view = GetContentView(FrameMetrics::ROOT_SCROLL_ID);
|
const nsContentView* view = GetContentView(FrameMetrics::ROOT_SCROLL_ID);
|
||||||
BuildBackgroundPatternFor(mContainer,
|
BuildBackgroundPatternFor(mContainer,
|
||||||
shadowRoot,
|
shadowRoot,
|
||||||
shadowRoot->GetFrameMetrics(),
|
|
||||||
view->GetViewConfig(),
|
view->GetViewConfig(),
|
||||||
aManager, aFrame, aBuilder);
|
mBackgroundColor,
|
||||||
|
aManager, aFrame);
|
||||||
}
|
}
|
||||||
mContainer->SetVisibleRegion(aVisibleRect);
|
mContainer->SetVisibleRegion(aVisibleRect);
|
||||||
|
|
||||||
|
|
|
@ -98,6 +98,8 @@ public:
|
||||||
|
|
||||||
void OwnerContentChanged(nsIContent* aContent);
|
void OwnerContentChanged(nsIContent* aContent);
|
||||||
|
|
||||||
|
void SetBackgroundColor(nscolor aColor) { mBackgroundColor = gfxRGBA(aColor); };
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
NS_OVERRIDE void ActorDestroy(ActorDestroyReason why);
|
NS_OVERRIDE void ActorDestroy(ActorDestroyReason why);
|
||||||
|
|
||||||
|
@ -133,6 +135,8 @@ private:
|
||||||
// It's possible for mFrameLoader==null and
|
// It's possible for mFrameLoader==null and
|
||||||
// mFrameLoaderDestroyed==false.
|
// mFrameLoaderDestroyed==false.
|
||||||
bool mFrameLoaderDestroyed;
|
bool mFrameLoaderDestroyed;
|
||||||
|
// this is gfxRGBA because that's what ColorLayer wants.
|
||||||
|
gfxRGBA mBackgroundColor;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace layout
|
} // namespace layout
|
||||||
|
|
|
@ -91,6 +91,7 @@ fails-if(!browserIsRemote) == test-displayport.html test-displayport-ref.html #
|
||||||
skip-if(!browserIsRemote) != test-displayport-2.html test-displayport-ref.html # bug 593168
|
skip-if(!browserIsRemote) != test-displayport-2.html test-displayport-ref.html # bug 593168
|
||||||
skip-if(!browserIsRemote) == 647192-1.html 647192-1-ref.html
|
skip-if(!browserIsRemote) == 647192-1.html 647192-1-ref.html
|
||||||
skip-if(!browserIsRemote) == 656041-1.html 656041-1-ref.html
|
skip-if(!browserIsRemote) == 656041-1.html 656041-1-ref.html
|
||||||
|
skip-if(!browserIsRemote) == test-displayport-bg.html test-displayport-ref.html # bug 694706
|
||||||
|
|
||||||
# IPC Position-fixed frames/layers test
|
# IPC Position-fixed frames/layers test
|
||||||
# Fixed layers are temporarily disabled (bug 656167).
|
# Fixed layers are temporarily disabled (bug 656167).
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html reftest-viewport-w="100" reftest-viewport-h="100"
|
||||||
|
reftest-displayport-w="400" reftest-displayport-h="500"
|
||||||
|
reftest-async-scroll=true>
|
||||||
|
<body bgcolor=green style="position: absolute; left:0px; top:0px;width:800px; height:1000px;"></body>
|
||||||
|
</html>
|
||||||
|
|
|
@ -49,7 +49,7 @@ const CSS_TYPE_SHORTHAND_AND_LONGHAND = 2;
|
||||||
|
|
||||||
// Each property has the following fields:
|
// Each property has the following fields:
|
||||||
// domProp: The name of the relevant member of nsIDOM[NS]CSS2Properties
|
// domProp: The name of the relevant member of nsIDOM[NS]CSS2Properties
|
||||||
// inherited: Whether the property is inherited by default (stated as
|
// inherited: Whether the property is inherited by default (stated as
|
||||||
// yes or no in the property header in all CSS specs)
|
// yes or no in the property header in all CSS specs)
|
||||||
// type: see above
|
// type: see above
|
||||||
// get_computed: if present, the property's computed value shows up on
|
// get_computed: if present, the property's computed value shows up on
|
||||||
|
@ -1833,7 +1833,7 @@ var gCSSProperties = {
|
||||||
inherited: true,
|
inherited: true,
|
||||||
type: CSS_TYPE_LONGHAND,
|
type: CSS_TYPE_LONGHAND,
|
||||||
initial_values: [ "show" ],
|
initial_values: [ "show" ],
|
||||||
other_values: [ "hide" ],
|
other_values: [ "hide", "-moz-show-background" ],
|
||||||
invalid_values: []
|
invalid_values: []
|
||||||
},
|
},
|
||||||
"float": {
|
"float": {
|
||||||
|
@ -1874,7 +1874,7 @@ var gCSSProperties = {
|
||||||
inherited: true,
|
inherited: true,
|
||||||
type: CSS_TYPE_LONGHAND,
|
type: CSS_TYPE_LONGHAND,
|
||||||
initial_values: [ "normal" ],
|
initial_values: [ "normal" ],
|
||||||
other_values: [ "'TRK'", "\"TRK\"", "'N\\'Ko'" ],
|
other_values: [ "'ENG'", "'TRK'", "\"TRK\"", "'N\\'Ko'" ],
|
||||||
invalid_values: [ "TRK" ]
|
invalid_values: [ "TRK" ]
|
||||||
},
|
},
|
||||||
"font-size": {
|
"font-size": {
|
||||||
|
@ -1935,7 +1935,7 @@ var gCSSProperties = {
|
||||||
type: CSS_TYPE_LONGHAND,
|
type: CSS_TYPE_LONGHAND,
|
||||||
initial_values: [ "normal", "400" ],
|
initial_values: [ "normal", "400" ],
|
||||||
other_values: [ "bold", "100", "200", "300", "500", "600", "700", "800", "900", "bolder", "lighter" ],
|
other_values: [ "bold", "100", "200", "300", "500", "600", "700", "800", "900", "bolder", "lighter" ],
|
||||||
invalid_values: [ "107", "399", "401", "699", "710" ]
|
invalid_values: [ "0", "100.0", "107", "399", "401", "699", "710", "1000" ]
|
||||||
},
|
},
|
||||||
"height": {
|
"height": {
|
||||||
domProp: "height",
|
domProp: "height",
|
||||||
|
|
|
@ -257,6 +257,10 @@ function setupDisplayport(contentRootElement) {
|
||||||
LogInfo("Setting displayport to <x="+ dpx +", y="+ dpy +", w="+ dpw +", h="+ dph +">");
|
LogInfo("Setting displayport to <x="+ dpx +", y="+ dpy +", w="+ dpw +", h="+ dph +">");
|
||||||
windowUtils().setDisplayPortForElement(dpx, dpy, dpw, dph, content.document.documentElement);
|
windowUtils().setDisplayPortForElement(dpx, dpy, dpw, dph, content.document.documentElement);
|
||||||
}
|
}
|
||||||
|
var asyncScroll = attrOrDefault("reftest-async-scroll", false);
|
||||||
|
if (asyncScroll) {
|
||||||
|
SendEnableAsyncScroll();
|
||||||
|
}
|
||||||
|
|
||||||
// XXX support resolution when needed
|
// XXX support resolution when needed
|
||||||
|
|
||||||
|
@ -751,6 +755,11 @@ function SendFailedLoad(why)
|
||||||
sendAsyncMessage("reftest:FailedLoad", { why: why });
|
sendAsyncMessage("reftest:FailedLoad", { why: why });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SendEnableAsyncScroll()
|
||||||
|
{
|
||||||
|
sendAsyncMessage("reftest:EnableAsyncScroll");
|
||||||
|
}
|
||||||
|
|
||||||
// Return true if a snapshot was taken.
|
// Return true if a snapshot was taken.
|
||||||
function SendInitCanvasWithSnapshot()
|
function SendInitCanvasWithSnapshot()
|
||||||
{
|
{
|
||||||
|
|
|
@ -1346,6 +1346,7 @@ function FinishTestItem()
|
||||||
gDumpLog("REFTEST INFO | Loading a blank page\n");
|
gDumpLog("REFTEST INFO | Loading a blank page\n");
|
||||||
// After clearing, content will notify us of the assertion count
|
// After clearing, content will notify us of the assertion count
|
||||||
// and tests will continue.
|
// and tests will continue.
|
||||||
|
SetAsyncScroll(false);
|
||||||
SendClear();
|
SendClear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1442,10 +1443,21 @@ function RegisterMessageListenersAndLoadContentScript()
|
||||||
"reftest:ExpectProcessCrash",
|
"reftest:ExpectProcessCrash",
|
||||||
function (m) { RecvExpectProcessCrash(); }
|
function (m) { RecvExpectProcessCrash(); }
|
||||||
);
|
);
|
||||||
|
gBrowserMessageManager.addMessageListener(
|
||||||
|
"reftest:EnableAsyncScroll",
|
||||||
|
function (m) { SetAsyncScroll(true); }
|
||||||
|
);
|
||||||
|
|
||||||
gBrowserMessageManager.loadFrameScript("chrome://reftest/content/reftest-content.js", true);
|
gBrowserMessageManager.loadFrameScript("chrome://reftest/content/reftest-content.js", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function SetAsyncScroll(enabled)
|
||||||
|
{
|
||||||
|
gBrowser.QueryInterface(CI.nsIFrameLoaderOwner).frameLoader.renderMode =
|
||||||
|
enabled ? CI.nsIFrameLoader.RENDER_MODE_ASYNC_SCROLL :
|
||||||
|
CI.nsIFrameLoader.RENDER_MODE_DEFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
function RecvAssertionCount(count)
|
function RecvAssertionCount(count)
|
||||||
{
|
{
|
||||||
DoAssertionCheck(count);
|
DoAssertionCheck(count);
|
||||||
|
|
|
@ -379,7 +379,7 @@ nsDisplayXULTextBox::PaintTextToContext(nsRenderingContext* aCtx,
|
||||||
|
|
||||||
nsRect
|
nsRect
|
||||||
nsDisplayXULTextBox::GetBounds(nsDisplayListBuilder* aBuilder) {
|
nsDisplayXULTextBox::GetBounds(nsDisplayListBuilder* aBuilder) {
|
||||||
return mFrame->GetVisualOverflowRect() + ToReferenceFrame();
|
return mFrame->GetVisualOverflowRectRelativeToSelf() + ToReferenceFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsRect
|
nsRect
|
||||||
|
|
|
@ -140,7 +140,6 @@ pref("browser.display.remotetabs.timeout", 10);
|
||||||
/* session history */
|
/* session history */
|
||||||
pref("browser.sessionhistory.max_total_viewers", 1);
|
pref("browser.sessionhistory.max_total_viewers", 1);
|
||||||
pref("browser.sessionhistory.max_entries", 50);
|
pref("browser.sessionhistory.max_entries", 50);
|
||||||
pref("browser.sessionhistory.optimize_eviction", true);
|
|
||||||
|
|
||||||
/* session store */
|
/* session store */
|
||||||
pref("browser.sessionstore.resume_session_once", false);
|
pref("browser.sessionstore.resume_session_once", false);
|
||||||
|
|
|
@ -268,8 +268,13 @@ let WebNavigation = {
|
||||||
if (aEntry.docshellID)
|
if (aEntry.docshellID)
|
||||||
shEntry.docshellID = aEntry.docshellID;
|
shEntry.docshellID = aEntry.docshellID;
|
||||||
|
|
||||||
if (aEntry.stateData)
|
if (aEntry.structuredCloneState && aEntry.structuredCloneVersion) {
|
||||||
shEntry.stateData = aEntry.stateData;
|
shEntry.stateData =
|
||||||
|
Cc["@mozilla.org/docshell/structured-clone-container;1"].
|
||||||
|
createInstance(Ci.nsIStructuredCloneContainer);
|
||||||
|
|
||||||
|
shEntry.stateData.initFromBase64(aEntry.structuredCloneState, aEntry.structuredCloneVersion);
|
||||||
|
}
|
||||||
|
|
||||||
if (aEntry.scroll) {
|
if (aEntry.scroll) {
|
||||||
let scrollPos = aEntry.scroll.split(",");
|
let scrollPos = aEntry.scroll.split(",");
|
||||||
|
@ -278,23 +283,15 @@ let WebNavigation = {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aEntry.docIdentifier) {
|
if (aEntry.docIdentifier) {
|
||||||
// Get a new document identifier for this entry to ensure that history
|
// If we have a serialized document identifier, try to find an SHEntry
|
||||||
// entries after a session restore are considered to have different
|
// which matches that doc identifier and adopt that SHEntry's
|
||||||
// documents from the history entries before the session restore.
|
// BFCacheEntry. If we don't find a match, insert shEntry as the match
|
||||||
// Document identifiers are 64-bit ints, so JS will loose precision and
|
// for the document identifier.
|
||||||
// start assigning all entries the same doc identifier if these ever get
|
let matchingEntry = aDocIdentMap[aEntry.docIdentifier];
|
||||||
// large enough.
|
if (!matchingEntry) {
|
||||||
//
|
aDocIdentMap[aEntry.docIdentifier] = shEntry;
|
||||||
// It's a potential security issue if document identifiers aren't
|
|
||||||
// globally unique, but shEntry.setUniqueDocIdentifier() below guarantees
|
|
||||||
// that we won't re-use a doc identifier within a given instance of the
|
|
||||||
// application.
|
|
||||||
let ident = aDocIdentMap[aEntry.docIdentifier];
|
|
||||||
if (!ident) {
|
|
||||||
shEntry.setUniqueDocIdentifier();
|
|
||||||
aDocIdentMap[aEntry.docIdentifier] = shEntry.docIdentifier;
|
|
||||||
} else {
|
} else {
|
||||||
shEntry.docIdentifier = ident;
|
shEntry.adoptBFCacheEntry(matchingEntry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -379,11 +376,12 @@ let WebNavigation = {
|
||||||
} catch (e) { dump(e); }
|
} catch (e) { dump(e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aEntry.docIdentifier)
|
entry.docIdentifier = aEntry.BFCacheEntry.ID;
|
||||||
entry.docIdentifier = aEntry.docIdentifier;
|
|
||||||
|
|
||||||
if (aEntry.stateData)
|
if (aEntry.stateData != null) {
|
||||||
entry.stateData = aEntry.stateData;
|
entry.structuredCloneState = aEntry.stateData.getDataAsBase64();
|
||||||
|
entry.structuredCloneVersion = aEntry.stateData.formatVersion;
|
||||||
|
}
|
||||||
|
|
||||||
if (!(aEntry instanceof Ci.nsISHContainer))
|
if (!(aEntry instanceof Ci.nsISHContainer))
|
||||||
return entry;
|
return entry;
|
||||||
|
|
|
@ -109,8 +109,6 @@ pref("dom.enable_performance", true);
|
||||||
// of content viewers to cache based on the amount of available memory.
|
// of content viewers to cache based on the amount of available memory.
|
||||||
pref("browser.sessionhistory.max_total_viewers", -1);
|
pref("browser.sessionhistory.max_total_viewers", -1);
|
||||||
|
|
||||||
pref("browser.sessionhistory.optimize_eviction", true);
|
|
||||||
|
|
||||||
pref("ui.use_native_colors", true);
|
pref("ui.use_native_colors", true);
|
||||||
pref("ui.click_hold_context_menus", false);
|
pref("ui.click_hold_context_menus", false);
|
||||||
pref("browser.display.use_document_fonts", 1); // 0 = never, 1 = quick, 2 = always
|
pref("browser.display.use_document_fonts", 1); // 0 = never, 1 = quick, 2 = always
|
||||||
|
|
|
@ -52,15 +52,13 @@
|
||||||
#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager"
|
#define NM_DBUS_SERVICE "org.freedesktop.NetworkManager"
|
||||||
#define NM_DBUS_PATH "/org/freedesktop/NetworkManager"
|
#define NM_DBUS_PATH "/org/freedesktop/NetworkManager"
|
||||||
#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager"
|
#define NM_DBUS_INTERFACE "org.freedesktop.NetworkManager"
|
||||||
#define NM_DBUS_SIGNAL_STATE_CHANGE "StateChange"
|
#define NM_DBUS_SIGNAL_STATE_CHANGE "StateChange" /* Deprecated in 0.7.x */
|
||||||
typedef enum NMState
|
#define NM_DBUS_SIGNAL_STATE_CHANGED "StateChanged"
|
||||||
{
|
|
||||||
NM_STATE_UNKNOWN = 0,
|
#define NM_STATE_CONNECTED_OLD 3 /* Before NM 0.9.0 */
|
||||||
NM_STATE_ASLEEP,
|
#define NM_STATE_CONNECTED_LOCAL 50
|
||||||
NM_STATE_CONNECTING,
|
#define NM_STATE_CONNECTED_SITE 60
|
||||||
NM_STATE_CONNECTED,
|
#define NM_STATE_CONNECTED_GLOBAL 70
|
||||||
NM_STATE_DISCONNECTED
|
|
||||||
} NMState;
|
|
||||||
|
|
||||||
nsNetworkManagerListener::nsNetworkManagerListener() :
|
nsNetworkManagerListener::nsNetworkManagerListener() :
|
||||||
mLinkUp(true), mNetworkManagerActive(false),
|
mLinkUp(true), mNetworkManagerActive(false),
|
||||||
|
@ -185,7 +183,9 @@ nsNetworkManagerListener::UnregisterWithConnection(DBusConnection* connection) {
|
||||||
bool
|
bool
|
||||||
nsNetworkManagerListener::HandleMessage(DBusMessage* message) {
|
nsNetworkManagerListener::HandleMessage(DBusMessage* message) {
|
||||||
if (dbus_message_is_signal(message, NM_DBUS_INTERFACE,
|
if (dbus_message_is_signal(message, NM_DBUS_INTERFACE,
|
||||||
NM_DBUS_SIGNAL_STATE_CHANGE)) {
|
NM_DBUS_SIGNAL_STATE_CHANGE) ||
|
||||||
|
dbus_message_is_signal(message, NM_DBUS_INTERFACE,
|
||||||
|
NM_DBUS_SIGNAL_STATE_CHANGED)) {
|
||||||
UpdateNetworkStatus(message);
|
UpdateNetworkStatus(message);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -202,7 +202,10 @@ nsNetworkManagerListener::UpdateNetworkStatus(DBusMessage* msg) {
|
||||||
mNetworkManagerActive = true;
|
mNetworkManagerActive = true;
|
||||||
|
|
||||||
bool wasUp = mLinkUp;
|
bool wasUp = mLinkUp;
|
||||||
mLinkUp = result == NM_STATE_CONNECTED;
|
mLinkUp = result == NM_STATE_CONNECTED_OLD ||
|
||||||
|
result == NM_STATE_CONNECTED_LOCAL ||
|
||||||
|
result == NM_STATE_CONNECTED_SITE ||
|
||||||
|
result == NM_STATE_CONNECTED_GLOBAL;
|
||||||
if (wasUp == mLinkUp)
|
if (wasUp == mLinkUp)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -2817,7 +2817,7 @@ nsLocalFile::RevealUsingShell()
|
||||||
}
|
}
|
||||||
|
|
||||||
const ITEMIDLIST* selection[] = { dir };
|
const ITEMIDLIST* selection[] = { dir };
|
||||||
UINT count = PR_ARRAY_SIZE(selection);
|
UINT count = ArrayLength(selection);
|
||||||
|
|
||||||
//Perform the open of the directory.
|
//Perform the open of the directory.
|
||||||
hr = sSHOpenFolderAndSelectItems(dir, count, selection, 0);
|
hr = sSHOpenFolderAndSelectItems(dir, count, selection, 0);
|
||||||
|
@ -2846,7 +2846,7 @@ nsLocalFile::RevealUsingShell()
|
||||||
}
|
}
|
||||||
|
|
||||||
const ITEMIDLIST* selection[] = { item };
|
const ITEMIDLIST* selection[] = { item };
|
||||||
UINT count = PR_ARRAY_SIZE(selection);
|
UINT count = ArrayLength(selection);
|
||||||
|
|
||||||
//Perform the selection of the file.
|
//Perform the selection of the file.
|
||||||
hr = sSHOpenFolderAndSelectItems(dir, count, selection, 0);
|
hr = sSHOpenFolderAndSelectItems(dir, count, selection, 0);
|
||||||
|
|
|
@ -38,6 +38,8 @@
|
||||||
*
|
*
|
||||||
* ***** END LICENSE BLOCK ***** */
|
* ***** END LICENSE BLOCK ***** */
|
||||||
|
|
||||||
|
#include "mozilla/Util.h"
|
||||||
|
|
||||||
#include "jscntxt.h"
|
#include "jscntxt.h"
|
||||||
#include "nscore.h"
|
#include "nscore.h"
|
||||||
#include "plstr.h"
|
#include "plstr.h"
|
||||||
|
@ -52,6 +54,8 @@
|
||||||
#include "nsICryptoHash.h"
|
#include "nsICryptoHash.h"
|
||||||
#include "nsIX509Cert.h"
|
#include "nsIX509Cert.h"
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
|
|
||||||
//
|
//
|
||||||
// nsXPITriggerItem
|
// nsXPITriggerItem
|
||||||
//
|
//
|
||||||
|
@ -246,7 +250,7 @@ XPITriggerEvent::Run()
|
||||||
|
|
||||||
// Build arguments into rooted jsval array
|
// Build arguments into rooted jsval array
|
||||||
jsval args[2] = { JSVAL_NULL, JSVAL_NULL };
|
jsval args[2] = { JSVAL_NULL, JSVAL_NULL };
|
||||||
js::AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(args), args);
|
js::AutoArrayRooter tvr(cx, ArrayLength(args), args);
|
||||||
|
|
||||||
// args[0] is the URL
|
// args[0] is the URL
|
||||||
JSString *str = JS_NewUCStringCopyZ(cx, reinterpret_cast<const jschar*>(URL.get()));
|
JSString *str = JS_NewUCStringCopyZ(cx, reinterpret_cast<const jschar*>(URL.get()));
|
||||||
|
|
Загрузка…
Ссылка в новой задаче