From 84576fe4ec50ebe18a5ba353cff4dd8cf27f6de2 Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Mon, 23 Jan 2012 03:02:54 -0800 Subject: [PATCH 01/58] Bug 705640 - 'Implement DOMError as defined in DOM 4'. r=mounir. --- dom/base/DOMError.cpp | 73 +++++++++++++++++++ dom/base/DOMError.h | 48 ++++++++++++ dom/base/Makefile.in | 3 + dom/base/nsDOMClassInfo.cpp | 9 +++ dom/base/nsDOMClassInfoClasses.h | 2 + dom/base/nsIDOMDOMError.idl | 13 ++++ dom/interfaces/core/nsIDOMDOMError.idl | 62 ---------------- dom/interfaces/core/nsIDOMDOMErrorHandler.idl | 54 -------------- js/xpconnect/src/dom_quickstubs.qsconf | 2 + 9 files changed, 150 insertions(+), 116 deletions(-) create mode 100644 dom/base/DOMError.cpp create mode 100644 dom/base/DOMError.h create mode 100644 dom/base/nsIDOMDOMError.idl delete mode 100644 dom/interfaces/core/nsIDOMDOMError.idl delete mode 100644 dom/interfaces/core/nsIDOMDOMErrorHandler.idl diff --git a/dom/base/DOMError.cpp b/dom/base/DOMError.cpp new file mode 100644 index 000000000000..a72fab61c5d7 --- /dev/null +++ b/dom/base/DOMError.cpp @@ -0,0 +1,73 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DOMError.h" + +#include "mozilla/Util.h" +#include "nsDOMClassInfo.h" + +using mozilla::ArrayLength; +using mozilla::dom::DOMError; + +// static +already_AddRefed +DOMError::CreateForDOMExceptionCode(PRUint16 aDOMExceptionCode) +{ + struct NameMap { PRUint16 code; char* name; }; + + static const NameMap kNames[] = { + { 1, "IndexSizeError" }, + { 3, "HierarchyRequestError" }, + { 4, "WrongDocumentError" }, + { 5, "InvalidCharacterError" }, + { 7, "NoModificationAllowedError" }, + { 8, "NotFoundError" }, + { 9, "NotSupportedError" }, + { 11, "InvalidStateError" }, + { 12, "SyntaxError" }, + { 13, "InvalidModificationError" }, + { 14, "NamespaceError" }, + { 15, "InvalidAccessError" }, + { 17, "TypeMismatchError" }, + { 18, "SecurityError" }, + { 19, "NetworkError" }, + { 20, "AbortError" }, + { 21, "URLMismatchError" }, + { 22, "QuotaExceededError" }, + { 23, "TimeoutError" }, + { 24, "InvalidNodeTypeError" }, + { 25, "DataCloneError" }, + }; + + for (size_t index = 0; index < ArrayLength(kNames); index++) { + if (kNames[index].code == aDOMExceptionCode) { + nsString name; + name.AssignASCII(kNames[index].name); + return CreateWithName(name); + } + } + + NS_NOTREACHED("Unknown DOMException code!"); + return nsnull; +} + +NS_IMPL_ADDREF(DOMError) +NS_IMPL_RELEASE(DOMError) + +NS_INTERFACE_MAP_BEGIN(DOMError) + NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DOMError) + NS_INTERFACE_MAP_ENTRY(nsIDOMDOMError) + NS_INTERFACE_MAP_ENTRY(nsISupports) +NS_INTERFACE_MAP_END + +DOMCI_DATA(DOMError, DOMError) + +NS_IMETHODIMP +DOMError::GetName(nsAString& aName) +{ + aName = mName; + return NS_OK; +} diff --git a/dom/base/DOMError.h b/dom/base/DOMError.h new file mode 100644 index 000000000000..e4058b0daee9 --- /dev/null +++ b/dom/base/DOMError.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_domerror_h__ +#define mozilla_dom_domerror_h__ + +#include "nsIDOMDOMError.h" + +#include "nsCOMPtr.h" +#include "nsStringGlue.h" + +namespace mozilla { +namespace dom { + +class DOMError : public nsIDOMDOMError +{ + nsString mName; + +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDOMDOMERROR + + static already_AddRefed + CreateForDOMExceptionCode(PRUint16 aDOMExceptionCode); + + static already_AddRefed + CreateWithName(const nsAString& aName) + { + nsCOMPtr error = new DOMError(aName); + return error.forget(); + } + +protected: + DOMError(const nsAString& aName) + : mName(aName) + { } + + virtual ~DOMError() + { } +}; + +} // namespace dom +} // namespace mozilla + +#endif // mozilla_dom_domerror_h__ diff --git a/dom/base/Makefile.in b/dom/base/Makefile.in index b6fd3510bb44..74867bd35a45 100644 --- a/dom/base/Makefile.in +++ b/dom/base/Makefile.in @@ -66,6 +66,7 @@ EXTRA_JS_MODULES += Webapps.jsm \ endif XPIDLSRCS = \ + nsIDOMDOMError.idl \ nsIEntropyCollector.idl \ nsIScriptChannel.idl \ $(NULL) @@ -103,6 +104,7 @@ EXPORTS = \ EXPORTS_NAMESPACES = mozilla/dom EXPORTS_mozilla/dom = \ + DOMError.h \ StructuredCloneTags.h \ $(NULL) @@ -132,6 +134,7 @@ CPPSRCS = \ nsDOMNavigationTiming.cpp \ nsPerformance.cpp \ nsDOMMemoryReporter.cpp \ + DOMError.cpp \ Navigator.cpp \ $(NULL) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index bbb350f44253..ca54654c6f18 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -527,6 +527,8 @@ #include "CallEvent.h" #endif +#include "DOMError.h" + using namespace mozilla; using namespace mozilla::dom; @@ -1585,6 +1587,9 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(CallEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) #endif + + NS_DEFINE_CLASSINFO_DATA(DOMError, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) }; // Objects that should be constructable through |new Name();| @@ -4318,6 +4323,10 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_END #endif + DOM_CLASSINFO_MAP_BEGIN(DOMError, nsIDOMDOMError) + DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMError) + DOM_CLASSINFO_MAP_END + #ifdef NS_DEBUG { PRUint32 i = ArrayLength(sClassInfoData); diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index a7066b4770f5..d57053c2c4ed 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -537,3 +537,5 @@ DOMCI_CLASS(Telephony) DOMCI_CLASS(TelephonyCall) DOMCI_CLASS(CallEvent) #endif + +DOMCI_CLASS(DOMError) diff --git a/dom/base/nsIDOMDOMError.idl b/dom/base/nsIDOMDOMError.idl new file mode 100644 index 000000000000..06447b933b06 --- /dev/null +++ b/dom/base/nsIDOMDOMError.idl @@ -0,0 +1,13 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" + +[scriptable, builtinclass, uuid(e4e28307-d409-4cf7-93cd-6ea8e889f87a)] +interface nsIDOMDOMError : nsISupports +{ + readonly attribute DOMString name; +}; diff --git a/dom/interfaces/core/nsIDOMDOMError.idl b/dom/interfaces/core/nsIDOMDOMError.idl deleted file mode 100644 index 5059b646d3d7..000000000000 --- a/dom/interfaces/core/nsIDOMDOMError.idl +++ /dev/null @@ -1,62 +0,0 @@ -/* -*- Mode: IDL; 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 - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 2002 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Boris Zbarsky (original author) - * - * 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 "domstubs.idl" - -/** - * The nsIDOMDOMError interface is "An non-zero implementation - * dependent error code describing the error, or 0 if there is no - * error." [sic] - * - * For more information on this interface please see - * http://www.w3.org/TR/2002/WD-DOM-Level-3-Core-20020409/core.html - */ - -[scriptable, uuid(475790ce-d8fa-4e02-a167-e6308ba9b120)] -interface nsIDOMDOMError : nsISupports -{ - const unsigned short SEVERITY_WARNING = 0; - const unsigned short SEVERITY_ERROR = 1; - const unsigned short SEVERITY_FATAL_ERROR = 2; - readonly attribute unsigned short severity; - readonly attribute DOMString message; - // XXX This should be a DOMObject, not an nsISupports; do we need to - // do some magic to make that work? - readonly attribute nsISupports relatedException; - readonly attribute nsIDOMDOMLocator location; -}; diff --git a/dom/interfaces/core/nsIDOMDOMErrorHandler.idl b/dom/interfaces/core/nsIDOMDOMErrorHandler.idl deleted file mode 100644 index 33b64dc7a369..000000000000 --- a/dom/interfaces/core/nsIDOMDOMErrorHandler.idl +++ /dev/null @@ -1,54 +0,0 @@ -/* -*- Mode: IDL; 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 - * Netscape Communications Corporation. - * Portions created by the Initial Developer are Copyright (C) 2002 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Boris Zbarsky (original author) - * - * 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 "domstubs.idl" - -/** - * The nsIDOMDOMErrorHandler interface is a basic interface for DOM - * error handlers - * - * For more information on this interface please see - * http://www.w3.org/TR/2002/WD-DOM-Level-3-Core-20020409/core.html - */ - -[scriptable, uuid(2d958bdf-740d-43f4-9e5c-5d930f4f3876)] -interface nsIDOMDOMErrorHandler : nsISupports -{ - boolean handleError(in nsIDOMDOMError error); -}; - diff --git a/js/xpconnect/src/dom_quickstubs.qsconf b/js/xpconnect/src/dom_quickstubs.qsconf index addc9e7ae392..a5d3b4baa05e 100644 --- a/js/xpconnect/src/dom_quickstubs.qsconf +++ b/js/xpconnect/src/dom_quickstubs.qsconf @@ -467,6 +467,8 @@ members = [ 'nsIIDBVersionChangeEvent.*', 'nsIIndexedDatabaseUsageCallback.*', 'nsIIndexedDatabaseManager.*', + + 'nsIDOMDOMError.*', ] # Most interfaces can be found by searching the includePath; to find From c597ebf39da1c5682c4b92cefa30441c1db8e1ab Mon Sep 17 00:00:00 2001 From: Ben Turner Date: Mon, 23 Jan 2012 06:03:41 -0800 Subject: [PATCH 02/58] Bug 718132 - IndexedDB: Intermittent Failing to get JS wrapper in IDBRequest::NotifyHelperCompleted. r=khuey. --HG-- extra : transplant_source : 1j%98%F9%1D%7F%C8%13%8E%9D%B0%05%05%93%D4%60%89%3D%06%19 --- content/base/src/nsContentUtils.cpp | 5 +- dom/base/nsDOMClassInfo.cpp | 94 +++++++++- dom/indexedDB/AsyncConnectionHelper.cpp | 15 +- dom/indexedDB/IDBCursor.cpp | 18 +- dom/indexedDB/IDBCursor.h | 1 + dom/indexedDB/IDBDatabase.cpp | 84 +++------ dom/indexedDB/IDBDatabase.h | 31 +--- dom/indexedDB/IDBFactory.cpp | 75 ++++++-- dom/indexedDB/IDBFactory.h | 18 +- dom/indexedDB/IDBIndex.cpp | 10 +- dom/indexedDB/IDBIndex.h | 3 - dom/indexedDB/IDBObjectStore.cpp | 11 +- dom/indexedDB/IDBObjectStore.h | 6 +- dom/indexedDB/IDBRequest.cpp | 221 ++++++++++------------- dom/indexedDB/IDBRequest.h | 52 +++--- dom/indexedDB/IDBTransaction.cpp | 85 +++------ dom/indexedDB/IDBTransaction.h | 20 +- dom/indexedDB/IDBWrapperCache.cpp | 38 ++++ dom/indexedDB/IDBWrapperCache.h | 74 ++++++++ dom/indexedDB/IndexedDatabaseManager.cpp | 30 +-- dom/indexedDB/Makefile.in | 6 +- dom/indexedDB/OpenDatabaseHelper.cpp | 7 +- 22 files changed, 504 insertions(+), 400 deletions(-) create mode 100644 dom/indexedDB/IDBWrapperCache.cpp create mode 100644 dom/indexedDB/IDBWrapperCache.h diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 0c844510eef1..8e85b7dc290b 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -5454,8 +5454,9 @@ public: } NS_IMETHOD_(void) NoteScriptChild(PRUint32 langID, void* child) { - if (langID == nsIProgrammingLanguage::JAVASCRIPT) { - mFound = child == mWrapper; + if (langID == nsIProgrammingLanguage::JAVASCRIPT && + child == mWrapper) { + mFound = true; } } NS_IMETHOD_(void) NoteXPCOMChild(nsISupports *child) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index ca54654c6f18..6038457903d9 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -490,6 +490,7 @@ #include "DOMSVGPointList.h" #include "DOMSVGTransformList.h" +#include "mozilla/dom/indexedDB/IDBWrapperCache.h" #include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/dom/indexedDB/IDBRequest.h" #include "mozilla/dom/indexedDB/IDBDatabase.h" @@ -501,6 +502,8 @@ #include "mozilla/dom/indexedDB/IDBIndex.h" #include "nsIIDBDatabaseException.h" +using mozilla::dom::indexedDB::IDBWrapperCache; + #include "nsIDOMMediaQueryList.h" #include "nsDOMTouchEvent.h" @@ -595,6 +598,10 @@ static const char kDOMStringBundleURL[] = (DOM_DEFAULT_SCRIPTABLE_FLAGS | \ nsIXPCScriptable::WANT_ADDPROPERTY) +#define IDBEVENTTARGET_SCRIPTABLE_FLAGS \ + (EVENTTARGET_SCRIPTABLE_FLAGS | \ + nsIXPCScriptable::WANT_POSTCREATE) + #define DOMCLASSINFO_STANDARD_FLAGS \ (nsIClassInfo::MAIN_THREAD_ONLY | nsIClassInfo::DOM_OBJECT) @@ -656,6 +663,37 @@ DOMCI_DATA(DOMConstructor, void) NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA_WITH_NAME(_class, _class, _helper, \ _flags) +namespace { + +class IDBEventTargetSH : public nsEventTargetSH +{ +protected: + IDBEventTargetSH(nsDOMClassInfoData* aData) : nsEventTargetSH(aData) + { } + + virtual ~IDBEventTargetSH() + { } + +public: + NS_IMETHOD PreCreate(nsISupports *aNativeObj, JSContext *aCx, + JSObject *aGlobalObj, JSObject **aParentObj); + + NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *aQrapper, JSContext *aCx, + JSObject *aObj); + + NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *aWrapper, JSContext *aCx, + JSObject *aObj, jsid aId, jsval *aVp, bool *aRetval); + + virtual void PreserveWrapper(nsISupports *aNative); + + static nsIClassInfo *doCreate(nsDOMClassInfoData *aData) + { + return new IDBEventTargetSH(aData); + } +}; + +} // anonymous namespace + // This list of NS_DEFINE_CLASSINFO_DATA macros is what gives the DOM // classes their correct behavior when used through XPConnect. The // arguments that are passed to NS_DEFINE_CLASSINFO_DATA are @@ -1539,14 +1577,14 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(IDBFactory, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(IDBRequest, nsEventTargetSH, - EVENTTARGET_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(IDBDatabase, nsDOMGenericSH, - DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(IDBRequest, IDBEventTargetSH, + IDBEVENTTARGET_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(IDBDatabase, IDBEventTargetSH, + IDBEVENTTARGET_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(IDBObjectStore, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(IDBTransaction, nsDOMGenericSH, - DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(IDBTransaction, IDBEventTargetSH, + IDBEVENTTARGET_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(IDBCursor, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(IDBCursorWithValue, nsDOMGenericSH, @@ -1557,8 +1595,8 @@ static nsDOMClassInfoData sClassInfoData[] = { DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(IDBVersionChangeEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(IDBOpenDBRequest, nsDOMGenericSH, - DOM_DEFAULT_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(IDBOpenDBRequest, IDBEventTargetSH, + IDBEVENTTARGET_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(IDBDatabaseException, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -7621,6 +7659,46 @@ nsEventTargetSH::PreserveWrapper(nsISupports *aNative) nsContentUtils::PreserveWrapper(aNative, target); } +// IDBEventTarget helper + +NS_IMETHODIMP +IDBEventTargetSH::PreCreate(nsISupports *aNativeObj, JSContext *aCx, + JSObject *aGlobalObj, JSObject **aParentObj) +{ + IDBWrapperCache *target = IDBWrapperCache::FromSupports(aNativeObj); + JSObject *parent = target->GetParentObject(); + *aParentObj = parent ? parent : aGlobalObj; + return NS_OK; +} + +NS_IMETHODIMP +IDBEventTargetSH::PostCreate(nsIXPConnectWrappedNative *aWrapper, + JSContext *aCx, JSObject *aObj) +{ + IDBWrapperCache *target = IDBWrapperCache::FromSupports(aWrapper->Native()); + target->OnWrapperCreated(); + return NS_OK; +} + +NS_IMETHODIMP +IDBEventTargetSH::AddProperty(nsIXPConnectWrappedNative *aWrapper, + JSContext *aCx, JSObject *aObj, jsid aId, + jsval *aVp, bool *aRetval) +{ + if (aId == sAddEventListener_id) { + return nsEventTargetSH::AddProperty(aWrapper, aCx, aObj, aId, aVp, aRetval); + } + + IDBEventTargetSH::PreserveWrapper(GetNative(aWrapper, aObj)); + return NS_OK; +} + +void +IDBEventTargetSH::PreserveWrapper(nsISupports *aNative) +{ + IDBWrapperCache *target = IDBWrapperCache::FromSupports(aNative); + nsContentUtils::PreserveWrapper(aNative, target); +} // Element helper diff --git a/dom/indexedDB/AsyncConnectionHelper.cpp b/dom/indexedDB/AsyncConnectionHelper.cpp index 03713ace5bf8..26cfa17d4ffb 100644 --- a/dom/indexedDB/AsyncConnectionHelper.cpp +++ b/dom/indexedDB/AsyncConnectionHelper.cpp @@ -146,18 +146,11 @@ HelperBase::WrapNative(JSContext* aCx, NS_ASSERTION(aResult, "Null pointer!"); NS_ASSERTION(mRequest, "Null request!"); - JSObject* obj; - if (mRequest->ScriptContext()) { - obj = mRequest->ScriptContext()->GetNativeGlobal(); - } - else { - obj = mRequest->GetWrapper(); - } - - NS_ENSURE_TRUE(obj, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + JSObject* global = mRequest->GetParentObject(); + NS_ASSERTION(global, "This should never be null!"); nsresult rv = - nsContentUtils::WrapNative(aCx, obj, aNative, aResult); + nsContentUtils::WrapNative(aCx, global, aNative, aResult); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return NS_OK; @@ -288,7 +281,7 @@ AsyncConnectionHelper::Run() if (NS_SUCCEEDED(rv)) { bool hasSavepoint = false; if (mDatabase) { - IndexedDatabaseManager::SetCurrentWindow(mDatabase->Owner()); + IndexedDatabaseManager::SetCurrentWindow(mDatabase->GetOwner()); // Make the first savepoint. if (mTransaction) { diff --git a/dom/indexedDB/IDBCursor.cpp b/dom/indexedDB/IDBCursor.cpp index 0d3985d578da..c614edfbea73 100644 --- a/dom/indexedDB/IDBCursor.cpp +++ b/dom/indexedDB/IDBCursor.cpp @@ -64,8 +64,7 @@ GenerateRequest(IDBCursor* aCursor) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBDatabase* database = aCursor->Transaction()->Database(); - return IDBRequest::Create(aCursor, database->ScriptContext(), - database->Owner(), aCursor->Transaction()); + return IDBRequest::Create(aCursor, database, aCursor->Transaction()); } } // anonymous namespace @@ -262,11 +261,14 @@ IDBCursor::CreateCommon(IDBRequest* aRequest, nsRefPtr cursor = new IDBCursor(); + IDBDatabase* database = aTransaction->Database(); + cursor->mScriptContext = database->GetScriptContext(); + cursor->mOwner = database->GetOwner(); + cursor->mScriptOwner = database->GetScriptOwner(); + cursor->mRequest = aRequest; cursor->mTransaction = aTransaction; cursor->mObjectStore = aObjectStore; - cursor->mScriptContext = aTransaction->Database()->ScriptContext(); - cursor->mOwner = aTransaction->Database()->Owner(); cursor->mDirection = aDirection; cursor->mContinueQuery = aContinueQuery; cursor->mContinueToQuery = aContinueToQuery; @@ -276,7 +278,8 @@ IDBCursor::CreateCommon(IDBRequest* aRequest, } IDBCursor::IDBCursor() -: mType(OBJECTSTORE), +: mScriptOwner(nsnull), + mType(OBJECTSTORE), mDirection(nsIIDBCursor::NEXT), mCachedKey(JSVAL_VOID), mCachedPrimaryKey(JSVAL_VOID), @@ -377,6 +380,10 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor) "Should have a cached primary key"); NS_ASSERTION(tmp->mHaveCachedValue || JSVAL_IS_VOID(tmp->mCachedValue), "Should have a cached value"); + if (tmp->mScriptOwner) { + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->mScriptOwner, + "mScriptOwner") + } if (JSVAL_IS_GCTHING(tmp->mCachedKey)) { void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedKey); NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedKey") @@ -395,6 +402,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor) // Don't unlink mObjectStore, mIndex, or mTransaction! if (tmp->mRooted) { NS_DROP_JS_OBJECTS(tmp, IDBCursor); + tmp->mScriptOwner = nsnull; tmp->mCachedKey = JSVAL_VOID; tmp->mCachedPrimaryKey = JSVAL_VOID; tmp->mCachedValue = JSVAL_VOID; diff --git a/dom/indexedDB/IDBCursor.h b/dom/indexedDB/IDBCursor.h index 4cc6f74525e8..00a3fa4c7baf 100644 --- a/dom/indexedDB/IDBCursor.h +++ b/dom/indexedDB/IDBCursor.h @@ -154,6 +154,7 @@ protected: nsCOMPtr mScriptContext; nsCOMPtr mOwner; + JSObject* mScriptOwner; Type mType; PRUint16 mDirection; diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index d3ed92f9d36c..a97a0ea0aacc 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -148,8 +148,7 @@ private: // static already_AddRefed -IDBDatabase::Create(nsIScriptContext* aScriptContext, - nsPIDOMWindow* aOwner, +IDBDatabase::Create(IDBWrapperCache* aOwnerCache, already_AddRefed aDatabaseInfo, const nsACString& aASCIIOrigin, FileManager* aFileManager) @@ -162,8 +161,9 @@ IDBDatabase::Create(nsIScriptContext* aScriptContext, nsRefPtr db(new IDBDatabase()); - db->mScriptContext = aScriptContext; - db->mOwner = aOwner; + db->mScriptContext = aOwnerCache->GetScriptContext(); + db->mOwner = aOwnerCache->GetOwner(); + db->mScriptOwner = aOwnerCache->GetScriptOwner(); db->mDatabaseId = databaseInfo->id; db->mName = databaseInfo->name; @@ -205,6 +205,8 @@ IDBDatabase::~IDBDatabase() mgr->UnregisterDatabase(this); } } + + nsContentUtils::ReleaseWrapper(static_cast(this), this); } void @@ -218,7 +220,7 @@ IDBDatabase::Invalidate() // When the IndexedDatabaseManager needs to invalidate databases, all it has // is an origin, so we call back into the manager to cancel any prompts for // our owner. - IndexedDatabaseManager::CancelPromptsForWindow(Owner()); + IndexedDatabaseManager::CancelPromptsForWindow(GetOwner()); mInvalidated = true; } @@ -280,7 +282,8 @@ void IDBDatabase::OnUnlink() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!mOwner, "Should have been cleared already!"); + NS_ASSERTION(!GetOwner() && !GetScriptOwner(), + "Should have been cleared already!"); // We've been unlinked, at the very least we should be able to prevent further // transactions from starting and unblock any other SetVersion callers. @@ -298,18 +301,16 @@ IDBDatabase::OnUnlink() NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, - nsDOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnAbortListener) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnVersionChangeListener) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) + NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(abort) + NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error) + NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(versionchange) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, - nsDOMEventTargetHelper) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnAbortListener) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnVersionChangeListener) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) + NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(abort) + NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error) + NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(versionchange) // Do some cleanup. tmp->OnUnlink(); @@ -318,13 +319,17 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase) NS_INTERFACE_MAP_ENTRY(nsIIDBDatabase) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBDatabase) -NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper) +NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) -NS_IMPL_ADDREF_INHERITED(IDBDatabase, nsDOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(IDBDatabase, nsDOMEventTargetHelper) +NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache) +NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache) DOMCI_DATA(IDBDatabase, IDBDatabase) +NS_IMPL_EVENT_HANDLER(IDBDatabase, abort); +NS_IMPL_EVENT_HANDLER(IDBDatabase, error); +NS_IMPL_EVENT_HANDLER(IDBDatabase, versionchange); + NS_IMETHODIMP IDBDatabase::GetName(nsAString& aName) { @@ -668,47 +673,6 @@ IDBDatabase::Close() return NS_OK; } -NS_IMETHODIMP -IDBDatabase::SetOnabort(nsIDOMEventListener* aAbortListener) -{ - return RemoveAddEventListener(NS_LITERAL_STRING(ABORT_EVT_STR), - mOnAbortListener, aAbortListener); -} - -NS_IMETHODIMP -IDBDatabase::GetOnabort(nsIDOMEventListener** aAbortListener) -{ - return GetInnerEventListener(mOnAbortListener, aAbortListener); -} - -NS_IMETHODIMP -IDBDatabase::SetOnerror(nsIDOMEventListener* aErrorListener) -{ - return RemoveAddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR), - mOnErrorListener, aErrorListener); -} - -NS_IMETHODIMP -IDBDatabase::GetOnerror(nsIDOMEventListener** aErrorListener) -{ - return GetInnerEventListener(mOnErrorListener, aErrorListener); -} - -NS_IMETHODIMP -IDBDatabase::SetOnversionchange(nsIDOMEventListener* aVersionChangeListener) -{ - return RemoveAddEventListener(NS_LITERAL_STRING(VERSIONCHANGE_EVT_STR), - mOnVersionChangeListener, - aVersionChangeListener); -} - -NS_IMETHODIMP -IDBDatabase::GetOnversionchange(nsIDOMEventListener** aVersionChangeListener) -{ - return GetInnerEventListener(mOnVersionChangeListener, - aVersionChangeListener); -} - nsresult IDBDatabase::PostHandleEvent(nsEventChainPostVisitor& aVisitor) { diff --git a/dom/indexedDB/IDBDatabase.h b/dom/indexedDB/IDBDatabase.h index 574b4ff59ea4..dcc2253f17f5 100644 --- a/dom/indexedDB/IDBDatabase.h +++ b/dom/indexedDB/IDBDatabase.h @@ -41,13 +41,12 @@ #define mozilla_dom_indexeddb_idbdatabase_h__ #include "mozilla/dom/indexedDB/IndexedDatabase.h" -#include "mozilla/dom/indexedDB/FileManager.h" +#include "nsIDocument.h" #include "nsIIDBDatabase.h" -#include "nsCycleCollectionParticipant.h" -#include "nsDOMEventTargetHelper.h" -#include "nsIDocument.h" +#include "mozilla/dom/indexedDB/IDBWrapperCache.h" +#include "mozilla/dom/indexedDB/FileManager.h" class nsIScriptContext; class nsPIDOMWindow; @@ -61,7 +60,7 @@ class IDBObjectStore; class IDBTransaction; class IndexedDatabaseManager; -class IDBDatabase : public nsDOMEventTargetHelper, +class IDBDatabase : public IDBWrapperCache, public nsIIDBDatabase { friend class AsyncConnectionHelper; @@ -71,12 +70,10 @@ public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIIDBDATABASE - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, - nsDOMEventTargetHelper) + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, IDBWrapperCache) static already_AddRefed - Create(nsIScriptContext* aScriptContext, - nsPIDOMWindow* aOwner, + Create(IDBWrapperCache* aOwnerCache, already_AddRefed aDatabaseInfo, const nsACString& aASCIIOrigin, FileManager* aFileManager); @@ -104,16 +101,6 @@ public: return mFilePath; } - nsIScriptContext* ScriptContext() - { - return mScriptContext; - } - - nsPIDOMWindow* Owner() - { - return mOwner; - } - already_AddRefed GetOwnerDocument() { if (!mOwner) { @@ -168,9 +155,9 @@ private: nsRefPtr mFileManager; // Only touched on the main thread. - nsRefPtr mOnAbortListener; - nsRefPtr mOnErrorListener; - nsRefPtr mOnVersionChangeListener; + NS_DECL_EVENT_HANDLER(abort); + NS_DECL_EVENT_HANDLER(error); + NS_DECL_EVENT_HANDLER(versionchange); }; END_INDEXEDDB_NAMESPACE diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index 404db4076679..a545aac74beb 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -69,7 +69,6 @@ #include "IDBKeyRange.h" #include "IndexedDatabaseManager.h" #include "Key.h" -#include "nsIScriptSecurityManager.h" using namespace mozilla; @@ -91,28 +90,49 @@ struct ObjectStoreInfoMap } // anonymous namespace IDBFactory::IDBFactory() +: mOwningObject(nsnull) { IDBFactory::NoteUsedByProcessType(XRE_GetProcessType()); } +IDBFactory::~IDBFactory() +{ + if (mOwningObject) { + NS_DROP_JS_OBJECTS(this, IDBFactory); + } +} + // static already_AddRefed IDBFactory::Create(nsPIDOMWindow* aWindow) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - if (aWindow && aWindow->IsOuterWindow()) { + NS_ENSURE_TRUE(aWindow, nsnull); + + if (aWindow->IsOuterWindow()) { aWindow = aWindow->GetCurrentInnerWindow(); NS_ENSURE_TRUE(aWindow, nsnull); } nsRefPtr factory = new IDBFactory(); + factory->mWindow = aWindow; + return factory.forget(); +} - if (aWindow) { - factory->mWindow = do_GetWeakReference(aWindow); - NS_ENSURE_TRUE(factory->mWindow, nsnull); - } +// static +already_AddRefed +IDBFactory::Create(JSContext* aCx, + JSObject* aOwningObject) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(aCx, "Null context!"); + NS_ASSERTION(aOwningObject, "Null object!"); + NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject, + "Not a global object!"); + nsRefPtr factory = new IDBFactory(); + factory->mOwningObject = aOwningObject; return factory.forget(); } @@ -400,15 +420,36 @@ IDBFactory::SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo, return NS_OK; } -NS_IMPL_ADDREF(IDBFactory) -NS_IMPL_RELEASE(IDBFactory) +NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFactory) -NS_INTERFACE_MAP_BEGIN(IDBFactory) +NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBFactory) +NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBFactory) + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFactory) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_ENTRY(nsIIDBFactory) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBFactory) NS_INTERFACE_MAP_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBFactory) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindow) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBFactory) + if (tmp->mOwningObject) { + tmp->mOwningObject = nsnull; + } + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindow) +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory) + if (tmp->mOwningObject) { + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->mOwningObject, + "mOwningObject") + } +NS_IMPL_CYCLE_COLLECTION_TRACE_END + DOMCI_DATA(IDBFactory, IDBFactory) nsresult @@ -418,6 +459,7 @@ IDBFactory::OpenCommon(const nsAString& aName, nsIIDBOpenDBRequest** _retval) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!"); if (XRE_GetProcessType() == GeckoProcessType_Content) { // Force ContentChild to cache the path from the parent, so that @@ -430,16 +472,19 @@ IDBFactory::OpenCommon(const nsAString& aName, nsCOMPtr window; nsCOMPtr sgo; nsIScriptContext* context = nsnull; + JSObject* scriptOwner = nsnull; if (mWindow) { - window = do_QueryReferent(mWindow); - NS_ENSURE_TRUE(window, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - sgo = do_QueryInterface(window); + sgo = do_QueryInterface(mWindow); NS_ENSURE_TRUE(sgo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - + context = sgo->GetContext(); NS_ENSURE_TRUE(context, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + window = mWindow; + } + else { + scriptOwner = mOwningObject; } nsCString origin; @@ -448,7 +493,7 @@ IDBFactory::OpenCommon(const nsAString& aName, NS_ENSURE_SUCCESS(rv, rv); nsRefPtr request = - IDBOpenDBRequest::Create(context, window); + IDBOpenDBRequest::Create(context, window, scriptOwner); NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsRefPtr openHelper = diff --git a/dom/indexedDB/IDBFactory.h b/dom/indexedDB/IDBFactory.h index 8a1871fe180c..6144330692e7 100644 --- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.h @@ -45,11 +45,11 @@ #include "mozIStorageConnection.h" #include "nsIIDBFactory.h" -#include "nsIWeakReferenceUtils.h" +#include "nsCycleCollectionParticipant.h" #include "nsXULAppAPI.h" -class nsPIDOMWindow; class nsIAtom; +class nsPIDOMWindow; BEGIN_INDEXEDDB_NAMESPACE @@ -60,12 +60,17 @@ struct ObjectStoreInfo; class IDBFactory : public nsIIDBFactory { typedef nsTArray > ObjectStoreInfoArray; + public: - NS_DECL_ISUPPORTS + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBFactory) NS_DECL_NSIIDBFACTORY static already_AddRefed Create(nsPIDOMWindow* aWindow); + static already_AddRefed Create(JSContext* aCx, + JSObject* aOwningObject); + static already_AddRefed GetConnection(const nsAString& aDatabaseFilePath); @@ -96,7 +101,7 @@ public: private: IDBFactory(); - ~IDBFactory() { } + ~IDBFactory(); nsresult OpenCommon(const nsAString& aName, @@ -104,7 +109,10 @@ private: bool aDeleting, nsIIDBOpenDBRequest** _retval); - nsCOMPtr mWindow; + // If this factory lives on a window then mWindow must be non-null. Otherwise + // mOwningObject must be non-null. + nsCOMPtr mWindow; + JSObject* mOwningObject; }; END_INDEXEDDB_NAMESPACE diff --git a/dom/indexedDB/IDBIndex.cpp b/dom/indexedDB/IDBIndex.cpp index 73c93d7e8db7..52e89c4223c2 100644 --- a/dom/indexedDB/IDBIndex.cpp +++ b/dom/indexedDB/IDBIndex.cpp @@ -294,8 +294,7 @@ GenerateRequest(IDBIndex* aIndex) NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = aIndex->ObjectStore()->Transaction(); IDBDatabase* database = transaction->Database(); - return IDBRequest::Create(aIndex, database->ScriptContext(), - database->Owner(), transaction); + return IDBRequest::Create(aIndex, database, transaction); } } // anonymous namespace @@ -313,9 +312,6 @@ IDBIndex::Create(IDBObjectStore* aObjectStore, nsRefPtr index = new IDBIndex(); - index->mScriptContext = database->ScriptContext(); - index->mOwner = database->Owner(); - index->mObjectStore = aObjectStore; index->mId = aIndexInfo->id; index->mName = aIndexInfo->name; @@ -343,14 +339,10 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mObjectStore) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex) // Don't unlink mObjectStore! - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOwner) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptContext) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex) diff --git a/dom/indexedDB/IDBIndex.h b/dom/indexedDB/IDBIndex.h index 8f6b65a356cb..1f08de79a64c 100644 --- a/dom/indexedDB/IDBIndex.h +++ b/dom/indexedDB/IDBIndex.h @@ -113,9 +113,6 @@ private: nsRefPtr mObjectStore; - nsCOMPtr mScriptContext; - nsCOMPtr mOwner; - PRInt64 mId; nsString mName; nsString mKeyPath; diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 3c23eb83ec38..30f15d7d1b6f 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -507,8 +507,8 @@ GenerateRequest(IDBObjectStore* aObjectStore) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBDatabase* database = aObjectStore->Transaction()->Database(); - return IDBRequest::Create(aObjectStore, database->ScriptContext(), - database->Owner(), aObjectStore->Transaction()); + return IDBRequest::Create(aObjectStore, database, + aObjectStore->Transaction()); } JSClass gDummyPropClass = { @@ -532,9 +532,6 @@ IDBObjectStore::Create(IDBTransaction* aTransaction, nsRefPtr objectStore = new IDBObjectStore(); - objectStore->mScriptContext = aTransaction->Database()->ScriptContext(); - objectStore->mOwner = aTransaction->Database()->Owner(); - objectStore->mTransaction = aTransaction; objectStore->mName = aStoreInfo->name; objectStore->mId = aStoreInfo->id; @@ -1376,8 +1373,6 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mTransaction, nsIDOMEventTarget) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext) for (PRUint32 i = 0; i < tmp->mCreatedIndexes.Length(); i++) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedIndexes[i]"); @@ -1387,8 +1382,6 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore) // Don't unlink mTransaction! - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOwner) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptContext) tmp->mCreatedIndexes.Clear(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END diff --git a/dom/indexedDB/IDBObjectStore.h b/dom/indexedDB/IDBObjectStore.h index 861a5a306a81..0ddfef459e76 100644 --- a/dom/indexedDB/IDBObjectStore.h +++ b/dom/indexedDB/IDBObjectStore.h @@ -41,13 +41,14 @@ #define mozilla_dom_indexeddb_idbobjectstore_h__ #include "mozilla/dom/indexedDB/IndexedDatabase.h" -#include "mozilla/dom/indexedDB/IDBTransaction.h" #include "nsIIDBObjectStore.h" #include "nsIIDBTransaction.h" #include "nsCycleCollectionParticipant.h" +#include "mozilla/dom/indexedDB/IDBTransaction.h" + class nsIScriptContext; class nsPIDOMWindow; @@ -204,9 +205,6 @@ protected: private: nsRefPtr mTransaction; - nsCOMPtr mScriptContext; - nsCOMPtr mOwner; - PRInt64 mId; nsString mName; nsString mKeyPath; diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp index 1088844583e0..e04a1f1a2083 100644 --- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -62,8 +62,8 @@ USING_INDEXEDDB_NAMESPACE IDBRequest::IDBRequest() : mResultVal(JSVAL_VOID), mErrorCode(0), - mResultValRooted(false), - mHaveResultOrErrorCode(false) + mHaveResultOrErrorCode(false), + mManuallyRooted(false) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); } @@ -72,19 +72,13 @@ IDBRequest::~IDBRequest() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - if (mResultValRooted) { - // Calling a virtual from the destructor is bad... But we know that we won't - // call a subclass' implementation because mResultValRooted will be set to - // false. - UnrootResultVal(); - } + UnrootResultVal(); } // static already_AddRefed IDBRequest::Create(nsISupports* aSource, - nsIScriptContext* aScriptContext, - nsPIDOMWindow* aOwner, + IDBWrapperCache* aOwnerCache, IDBTransaction* aTransaction) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -92,8 +86,9 @@ IDBRequest::Create(nsISupports* aSource, request->mSource = aSource; request->mTransaction = aTransaction; - request->mScriptContext = aScriptContext; - request->mOwner = aOwner; + request->mScriptContext = aOwnerCache->GetScriptContext(); + request->mOwner = aOwnerCache->GetOwner(); + request->mScriptOwner = aOwnerCache->GetScriptOwner(); return request.forget(); } @@ -105,9 +100,7 @@ IDBRequest::Reset() mResultVal = JSVAL_VOID; mHaveResultOrErrorCode = false; mErrorCode = 0; - if (mResultValRooted) { - UnrootResultVal(); - } + UnrootResultVal(); } nsresult @@ -115,7 +108,7 @@ IDBRequest::NotifyHelperCompleted(HelperBase* aHelper) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!"); - NS_ASSERTION(!mResultValRooted, "Already rooted?!"); + NS_ASSERTION(!PreservingWrapper(), "Already rooted?!"); NS_ASSERTION(JSVAL_IS_VOID(mResultVal), "Should be undefined!"); // See if our window is still valid. If not then we're going to pretend that @@ -135,45 +128,40 @@ IDBRequest::NotifyHelperCompleted(HelperBase* aHelper) } // Otherwise we need to get the result from the helper. - JSContext* cx = nsnull; - JSObject* obj = nsnull; - if (mScriptContext) { - cx = mScriptContext->GetNativeContext(); - NS_ASSERTION(cx, "Failed to get a context!"); - - obj = mScriptContext->GetNativeGlobal(); - NS_ASSERTION(obj, "Failed to get global object!"); - } - else { + JSContext* cx; + if (mScriptOwner) { nsIThreadJSContextStack* cxStack = nsContentUtils::ThreadJSContextStack(); NS_ASSERTION(cxStack, "Failed to get thread context stack!"); - NS_ENSURE_SUCCESS(cxStack->GetSafeJSContext(&cx), - NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - obj = GetWrapper(); - NS_ENSURE_TRUE(obj, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + if (NS_FAILED(cxStack->GetSafeJSContext(&cx))) { + NS_WARNING("Failed to get safe JSContext!"); + rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + mErrorCode = NS_ERROR_GET_CODE(rv); + return rv; + } } + else { + cx = mScriptContext->GetNativeContext(); + NS_ASSERTION(cx, "Failed to get a context!"); + } + + JSObject* global = GetParentObject(); + NS_ASSERTION(global, "This should never be null!"); JSAutoRequest ar(cx); JSAutoEnterCompartment ac; - if (!ac.enter(cx, obj)) { - rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } - else { + if (ac.enter(cx, global)) { RootResultVal(); rv = aHelper->GetSuccessResult(cx, &mResultVal); - if (NS_SUCCEEDED(rv)) { - // Unroot if we don't really need to be rooted. - if (!JSVAL_IS_GCTHING(mResultVal)) { - UnrootResultVal(); - } - } - else { + if (NS_FAILED(rv)) { NS_WARNING("GetSuccessResult failed!"); } } + else { + NS_WARNING("Failed to enter correct compartment!"); + rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } if (NS_SUCCEEDED(rv)) { mErrorCode = 0; @@ -189,17 +177,47 @@ IDBRequest::NotifyHelperCompleted(HelperBase* aHelper) void IDBRequest::RootResultVal() { - NS_ASSERTION(!mResultValRooted, "This should be false!"); - NS_HOLD_JS_OBJECTS(this, IDBRequest); - mResultValRooted = true; + // We want to use the preserved wrapper to keep our result alive, but we don't + // always get a wrapper right away. Manually root if we don't have a wrapper. + if (!PreservingWrapper() && GetWrapperPreserveColor()) { + NS_ASSERTION(!mManuallyRooted, "Should not be manually rooted here!"); + nsContentUtils::PreserveWrapper(static_cast(this), + this); + } + else if (!mManuallyRooted) { + NS_HOLD_JS_OBJECTS(this, IDBRequest); + mManuallyRooted = true; + } } void IDBRequest::UnrootResultVal() { - NS_ASSERTION(mResultValRooted, "This should be true!"); - NS_DROP_JS_OBJECTS(this, IDBRequest); - mResultValRooted = false; + if (mManuallyRooted) { + NS_ASSERTION(!PreservingWrapper(), "Shouldn't have a wrapper here!"); + NS_DROP_JS_OBJECTS(this, IDBRequest); + mManuallyRooted = false; + } + else if (PreservingWrapper()) { + nsContentUtils::ReleaseWrapper(static_cast(this), this); + } +} + +void +IDBRequest::OnWrapperCreated() +{ + NS_ASSERTION(!PreservingWrapper(), + "Shouldn't have had a wrapper before now!"); + + // Update our rooting strategy to accommodate new wrapper if needed. We have + // to unroot the old way first. This is annoying because XPConnect uses a hash + // table to store these so we could be just fine adding an entry for a second + // time... However nsContentUtils keeps a counter that will get out of sync if + // we do. Safest to just remove the old holder and then add a new one. + if (mManuallyRooted) { + UnrootResultVal(); + RootResultVal(); + } } NS_IMETHODIMP @@ -262,66 +280,32 @@ IDBRequest::GetErrorCode(PRUint16* aErrorCode) return NS_OK; } -NS_IMETHODIMP -IDBRequest::SetOnsuccess(nsIDOMEventListener* aSuccessListener) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return RemoveAddEventListener(NS_LITERAL_STRING(SUCCESS_EVT_STR), - mOnSuccessListener, aSuccessListener); -} - -NS_IMETHODIMP -IDBRequest::GetOnsuccess(nsIDOMEventListener** aSuccessListener) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return GetInnerEventListener(mOnSuccessListener, aSuccessListener); -} - -NS_IMETHODIMP -IDBRequest::SetOnerror(nsIDOMEventListener* aErrorListener) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return RemoveAddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR), - mOnErrorListener, aErrorListener); -} - -NS_IMETHODIMP -IDBRequest::GetOnerror(nsIDOMEventListener** aErrorListener) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return GetInnerEventListener(mOnErrorListener, aErrorListener); -} - NS_IMPL_CYCLE_COLLECTION_CLASS(IDBRequest) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest, - nsDOMEventTargetWrapperCache) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnSuccessListener) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) + // Don't need NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS because + // nsDOMEventTargetHelper does it for us. + NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(success) + NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSource) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mTransaction, nsPIDOMEventTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest, - nsDOMEventTargetWrapperCache) - if (tmp->mResultValRooted) { - tmp->mResultVal = JSVAL_VOID; +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) + tmp->mResultVal = JSVAL_VOID; + if (tmp->mManuallyRooted) { tmp->UnrootResultVal(); } - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnSuccessListener) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener) + NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(success) + NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSource) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTransaction) NS_IMPL_CYCLE_COLLECTION_UNLINK_END -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, - nsDOMEventTargetWrapperCache) +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) + // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because + // nsDOMEventTargetHelper does it for us. if (JSVAL_IS_GCTHING(tmp->mResultVal)) { void *gcThing = JSVAL_TO_GCTHING(tmp->mResultVal); NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mResultVal") @@ -331,13 +315,16 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBRequest) NS_INTERFACE_MAP_ENTRY(nsIIDBRequest) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBRequest) -NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetWrapperCache) +NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) -NS_IMPL_ADDREF_INHERITED(IDBRequest, nsDOMEventTargetWrapperCache) -NS_IMPL_RELEASE_INHERITED(IDBRequest, nsDOMEventTargetWrapperCache) +NS_IMPL_ADDREF_INHERITED(IDBRequest, IDBWrapperCache) +NS_IMPL_RELEASE_INHERITED(IDBRequest, IDBWrapperCache) DOMCI_DATA(IDBRequest, IDBRequest) +NS_IMPL_EVENT_HANDLER(IDBRequest, success); +NS_IMPL_EVENT_HANDLER(IDBRequest, error); + nsresult IDBRequest::PreHandleEvent(nsEventChainPreVisitor& aVisitor) { @@ -352,21 +339,21 @@ IDBOpenDBRequest::~IDBOpenDBRequest() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - if (mResultValRooted) { - UnrootResultVal(); - } + UnrootResultVal(); } // static already_AddRefed IDBOpenDBRequest::Create(nsIScriptContext* aScriptContext, - nsPIDOMWindow* aOwner) + nsPIDOMWindow* aOwner, + JSObject* aScriptOwner) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); nsRefPtr request(new IDBOpenDBRequest()); request->mScriptContext = aScriptContext; request->mOwner = aOwner; + request->mScriptOwner = aScriptOwner; return request.forget(); } @@ -377,37 +364,18 @@ IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction) mTransaction = aTransaction; } -void -IDBOpenDBRequest::RootResultVal() -{ - NS_ASSERTION(!mResultValRooted, "This should be false!"); - NS_HOLD_JS_OBJECTS(this, IDBOpenDBRequest); - mResultValRooted = true; -} - -void -IDBOpenDBRequest::UnrootResultVal() -{ - NS_ASSERTION(mResultValRooted, "This should be true!"); - NS_DROP_JS_OBJECTS(this, IDBOpenDBRequest); - mResultValRooted = false; -} - -NS_IMPL_EVENT_HANDLER(IDBOpenDBRequest, blocked) -NS_IMPL_EVENT_HANDLER(IDBOpenDBRequest, upgradeneeded) - NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest, IDBRequest) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnupgradeneededListener) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnblockedListener) + NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(upgradeneeded) + NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(blocked) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBOpenDBRequest, IDBRequest) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnupgradeneededListener) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnblockedListener) + NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(upgradeneeded) + NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(blocked) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBOpenDBRequest) @@ -419,3 +387,6 @@ NS_IMPL_ADDREF_INHERITED(IDBOpenDBRequest, IDBRequest) NS_IMPL_RELEASE_INHERITED(IDBOpenDBRequest, IDBRequest) DOMCI_DATA(IDBOpenDBRequest, IDBOpenDBRequest) + +NS_IMPL_EVENT_HANDLER(IDBOpenDBRequest, blocked); +NS_IMPL_EVENT_HANDLER(IDBOpenDBRequest, upgradeneeded); diff --git a/dom/indexedDB/IDBRequest.h b/dom/indexedDB/IDBRequest.h index 9ab2fbaa4a19..3d5d960c5640 100644 --- a/dom/indexedDB/IDBRequest.h +++ b/dom/indexedDB/IDBRequest.h @@ -46,8 +46,7 @@ #include "nsIIDBRequest.h" #include "nsIIDBOpenDBRequest.h" -#include "nsDOMEventTargetWrapperCache.h" -#include "nsCycleCollectionParticipant.h" +#include "mozilla/dom/indexedDB/IDBWrapperCache.h" class nsIScriptContext; class nsPIDOMWindow; @@ -57,19 +56,18 @@ BEGIN_INDEXEDDB_NAMESPACE class HelperBase; class IDBTransaction; -class IDBRequest : public nsDOMEventTargetWrapperCache, +class IDBRequest : public IDBWrapperCache, public nsIIDBRequest { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIIDBREQUEST NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(IDBRequest, - nsDOMEventTargetWrapperCache) + IDBWrapperCache) static already_AddRefed Create(nsISupports* aSource, - nsIScriptContext* aScriptContext, - nsPIDOMWindow* aOwner, + IDBWrapperCache* aOwnerCache, IDBTransaction* aTransaction); // nsIDOMEventTarget @@ -92,18 +90,10 @@ public: mErrorCode = rv; } - nsIScriptContext* ScriptContext() - { - return mScriptContext; - } + void RootResultVal(); + void UnrootResultVal(); - nsPIDOMWindow* Owner() - { - return mOwner; - } - - virtual void RootResultVal(); - virtual void UnrootResultVal(); + virtual void OnWrapperCreated(); protected: IDBRequest(); @@ -112,14 +102,14 @@ protected: nsCOMPtr mSource; nsRefPtr mTransaction; - nsRefPtr mOnSuccessListener; - nsRefPtr mOnErrorListener; + NS_DECL_EVENT_HANDLER(success); + NS_DECL_EVENT_HANDLER(error); jsval mResultVal; PRUint16 mErrorCode; - bool mResultValRooted; bool mHaveResultOrErrorCode; + bool mManuallyRooted; }; class IDBOpenDBRequest : public IDBRequest, @@ -129,24 +119,30 @@ public: NS_DECL_ISUPPORTS_INHERITED NS_FORWARD_NSIIDBREQUEST(IDBRequest::) NS_DECL_NSIIDBOPENDBREQUEST - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBOpenDBRequest, - IDBRequest) + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBOpenDBRequest, IDBRequest) static already_AddRefed Create(nsIScriptContext* aScriptContext, - nsPIDOMWindow* aOwner); + nsPIDOMWindow* aOwner, + JSObject* aScriptOwner); + + static + already_AddRefed + Create(IDBWrapperCache* aOwnerCache) + { + return Create(aOwnerCache->GetScriptContext(), aOwnerCache->GetOwner(), + aOwnerCache->GetScriptOwner()); + } void SetTransaction(IDBTransaction* aTransaction); - virtual void RootResultVal(); - virtual void UnrootResultVal(); - protected: ~IDBOpenDBRequest(); - nsRefPtr mOnblockedListener; - nsRefPtr mOnupgradeneededListener; + // Only touched on the main thread. + NS_DECL_EVENT_HANDLER(blocked); + NS_DECL_EVENT_HANDLER(upgradeneeded); }; END_INDEXEDDB_NAMESPACE diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index ce3a0b5d17f1..7f506a3b6237 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -42,6 +42,7 @@ #include "nsIScriptContext.h" #include "mozilla/storage.h" +#include "nsContentUtils.h" #include "nsDOMClassInfoID.h" #include "nsDOMLists.h" #include "nsEventDispatcher.h" @@ -116,8 +117,9 @@ IDBTransaction::Create(IDBDatabase* aDatabase, nsRefPtr transaction = new IDBTransaction(); - transaction->mScriptContext = aDatabase->ScriptContext(); - transaction->mOwner = aDatabase->Owner(); + transaction->mScriptContext = aDatabase->GetScriptContext(); + transaction->mOwner = aDatabase->GetOwner(); + transaction->mScriptOwner = aDatabase->GetScriptOwner(); transaction->mDatabase = aDatabase; transaction->mMode = aMode; @@ -184,6 +186,8 @@ IDBTransaction::~IDBTransaction() NS_ASSERTION(!mConnection, "Should have called CommitOrRollback!"); NS_ASSERTION(!mCreating, "Should have been cleared already!"); NS_ASSERTION(mFiredCompleteOrAbort, "Should have fired event!"); + + nsContentUtils::ReleaseWrapper(static_cast(this), this); } void @@ -474,27 +478,25 @@ IDBTransaction::ClearCreatedFileInfos() NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransaction) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction, - nsDOMEventTargetHelper) + IDBWrapperCache) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mDatabase, nsIDOMEventTarget) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnCompleteListener) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnAbortListener) + NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error) + NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(complete) + NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(abort) for (PRUint32 i = 0; i < tmp->mCreatedObjectStores.Length(); i++) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedObjectStores[i]"); cb.NoteXPCOMChild(static_cast( tmp->mCreatedObjectStores[i].get())); } - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction, - nsDOMEventTargetHelper) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction, IDBWrapperCache) // Don't unlink mDatabase! - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnCompleteListener) - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnAbortListener) + NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error) + NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(complete) + NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(abort) tmp->mCreatedObjectStores.Clear(); @@ -504,13 +506,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBTransaction) NS_INTERFACE_MAP_ENTRY(nsIIDBTransaction) NS_INTERFACE_MAP_ENTRY(nsIThreadObserver) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBTransaction) -NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper) +NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) -NS_IMPL_ADDREF_INHERITED(IDBTransaction, nsDOMEventTargetHelper) -NS_IMPL_RELEASE_INHERITED(IDBTransaction, nsDOMEventTargetHelper) +NS_IMPL_ADDREF_INHERITED(IDBTransaction, IDBWrapperCache) +NS_IMPL_RELEASE_INHERITED(IDBTransaction, IDBWrapperCache) DOMCI_DATA(IDBTransaction, IDBTransaction) +NS_IMPL_EVENT_HANDLER(IDBTransaction, error); +NS_IMPL_EVENT_HANDLER(IDBTransaction, complete); +NS_IMPL_EVENT_HANDLER(IDBTransaction, abort); + NS_IMETHODIMP IDBTransaction::GetDb(nsIIDBDatabase** aDB) { @@ -626,53 +632,6 @@ IDBTransaction::Abort() return NS_OK; } -NS_IMETHODIMP -IDBTransaction::SetOnerror(nsIDOMEventListener* aErrorListener) -{ - return RemoveAddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR), - mOnErrorListener, aErrorListener); -} - -NS_IMETHODIMP -IDBTransaction::GetOnerror(nsIDOMEventListener** aErrorListener) -{ - return GetInnerEventListener(mOnErrorListener, aErrorListener); -} - -NS_IMETHODIMP -IDBTransaction::GetOncomplete(nsIDOMEventListener** aOncomplete) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return GetInnerEventListener(mOnCompleteListener, aOncomplete); -} - -NS_IMETHODIMP -IDBTransaction::SetOncomplete(nsIDOMEventListener* aOncomplete) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return RemoveAddEventListener(NS_LITERAL_STRING(COMPLETE_EVT_STR), - mOnCompleteListener, aOncomplete); -} - -NS_IMETHODIMP -IDBTransaction::GetOnabort(nsIDOMEventListener** aOnabort) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return GetInnerEventListener(mOnAbortListener, aOnabort); -} - -NS_IMETHODIMP -IDBTransaction::SetOnabort(nsIDOMEventListener* aOnabort) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - - return RemoveAddEventListener(NS_LITERAL_STRING(ABORT_EVT_STR), - mOnAbortListener, aOnabort); -} - nsresult IDBTransaction::PreHandleEvent(nsEventChainPreVisitor& aVisitor) { @@ -817,7 +776,7 @@ CommitHelper::Run() } if (mConnection) { - IndexedDatabaseManager::SetCurrentWindow(database->Owner()); + IndexedDatabaseManager::SetCurrentWindow(database->GetOwner()); if (!mAborted && mUpdateFileRefcountFunction && NS_FAILED(mUpdateFileRefcountFunction->UpdateDatabase(mConnection))) { diff --git a/dom/indexedDB/IDBTransaction.h b/dom/indexedDB/IDBTransaction.h index 4d6cdfff7250..f05f8b308887 100644 --- a/dom/indexedDB/IDBTransaction.h +++ b/dom/indexedDB/IDBTransaction.h @@ -41,8 +41,6 @@ #define mozilla_dom_indexeddb_idbtransaction_h__ #include "mozilla/dom/indexedDB/IndexedDatabase.h" -#include "mozilla/dom/indexedDB/IDBDatabase.h" -#include "mozilla/dom/indexedDB/FileInfo.h" #include "mozIStorageConnection.h" #include "mozIStorageStatement.h" @@ -51,14 +49,15 @@ #include "nsIRunnable.h" #include "nsIThreadInternal.h" -#include "nsDOMEventTargetHelper.h" -#include "nsCycleCollectionParticipant.h" - #include "nsAutoPtr.h" #include "nsClassHashtable.h" #include "nsHashKeys.h" #include "nsInterfaceHashtable.h" +#include "mozilla/dom/indexedDB/IDBDatabase.h" +#include "mozilla/dom/indexedDB/IDBWrapperCache.h" +#include "mozilla/dom/indexedDB/FileInfo.h" + class nsIThread; BEGIN_INDEXEDDB_NAMESPACE @@ -78,7 +77,7 @@ public: virtual nsresult NotifyTransactionComplete(IDBTransaction* aTransaction) = 0; }; -class IDBTransaction : public nsDOMEventTargetHelper, +class IDBTransaction : public IDBWrapperCache, public nsIIDBTransaction, public nsIThreadObserver { @@ -92,8 +91,7 @@ public: NS_DECL_NSIIDBTRANSACTION NS_DECL_NSITHREADOBSERVER - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBTransaction, - nsDOMEventTargetHelper) + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBTransaction, IDBWrapperCache) static already_AddRefed Create(IDBDatabase* aDatabase, @@ -180,9 +178,9 @@ private: PRUint32 mCreatedRecursionDepth; // Only touched on the main thread. - nsRefPtr mOnErrorListener; - nsRefPtr mOnCompleteListener; - nsRefPtr mOnAbortListener; + NS_DECL_EVENT_HANDLER(error); + NS_DECL_EVENT_HANDLER(complete); + NS_DECL_EVENT_HANDLER(abort); nsInterfaceHashtable mCachedStatements; diff --git a/dom/indexedDB/IDBWrapperCache.cpp b/dom/indexedDB/IDBWrapperCache.cpp new file mode 100644 index 000000000000..c09482fd7c30 --- /dev/null +++ b/dom/indexedDB/IDBWrapperCache.cpp @@ -0,0 +1,38 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "IDBWrapperCache.h" + +USING_INDEXEDDB_NAMESPACE + +NS_IMPL_CYCLE_COLLECTION_CLASS(IDBWrapperCache) + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBWrapperCache, + nsDOMEventTargetWrapperCache) + // Don't need NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS because + // nsDOMEventTargetHelper does it for us. +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBWrapperCache, + nsDOMEventTargetWrapperCache) + tmp->mScriptOwner = nsnull; +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBWrapperCache, + nsDOMEventTargetWrapperCache) + // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because + // nsDOMEventTargetHelper does it for us. + if (tmp->mScriptOwner) { + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->mScriptOwner, + "mScriptOwner") + } +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBWrapperCache) +NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetWrapperCache) + +NS_IMPL_ADDREF_INHERITED(IDBWrapperCache, nsDOMEventTargetWrapperCache) +NS_IMPL_RELEASE_INHERITED(IDBWrapperCache, nsDOMEventTargetWrapperCache) diff --git a/dom/indexedDB/IDBWrapperCache.h b/dom/indexedDB/IDBWrapperCache.h new file mode 100644 index 000000000000..8d4fdb55b0a5 --- /dev/null +++ b/dom/indexedDB/IDBWrapperCache.h @@ -0,0 +1,74 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_dom_indexeddb_idbwrappercache_h__ +#define mozilla_dom_indexeddb_idbwrappercache_h__ + +#include "mozilla/dom/indexedDB/IndexedDatabase.h" + +#include "nsDOMEventTargetWrapperCache.h" + +BEGIN_INDEXEDDB_NAMESPACE + +class IDBWrapperCache : public nsDOMEventTargetWrapperCache +{ +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED( + IDBWrapperCache, + nsDOMEventTargetWrapperCache) + + JSObject* GetScriptOwner() const + { + return mScriptOwner; + } + + nsIScriptContext* GetScriptContext() const + { + return mScriptContext; + } + + nsPIDOMWindow* GetOwner() const + { + return mOwner; + } + + JSObject* GetParentObject() + { + if (mScriptOwner) { + return mScriptOwner; + } + + // Do what nsEventTargetSH::PreCreate does. + nsCOMPtr parent; + nsDOMEventTargetWrapperCache::GetParentObject(getter_AddRefs(parent)); + + return parent ? parent->GetGlobalJSObject() : nsnull; + } + + virtual void OnWrapperCreated() + { } + + static IDBWrapperCache* FromSupports(nsISupports* aSupports) + { + return static_cast( + nsDOMEventTargetWrapperCache::FromSupports(aSupports)); + } + +protected: + IDBWrapperCache() + : mScriptOwner(nsnull) + { } + + virtual ~IDBWrapperCache() + { } + + JSObject* mScriptOwner; +}; + +END_INDEXEDDB_NAMESPACE + +#endif // mozilla_dom_indexeddb_idbwrappercache_h__ diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index 1f43d3bc73b6..cb9034f720e3 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -505,7 +505,7 @@ IndexedDatabaseManager::AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow) for (PRUint32 index = 0; index < liveDatabases.Length(); index++) { IDBDatabase*& database = liveDatabases[index]; - if (database->Owner() == aWindow) { + if (database->GetOwner() == aWindow) { if (NS_FAILED(database->Close())) { NS_WARNING("Failed to close database for dying window!"); } @@ -533,7 +533,7 @@ IndexedDatabaseManager::HasOpenTransactions(nsPIDOMWindow* aWindow) for (PRUint32 index = 0; index < liveDatabases.Length(); index++) { IDBDatabase*& database = liveDatabases[index]; - if (database->Owner() == aWindow && + if (database->GetOwner() == aWindow && pool->HasTransactionsForDatabase(database)) { return true; } @@ -1591,32 +1591,32 @@ NS_IMETHODIMP IndexedDatabaseManager::InitWindowless(const jsval& aObj, JSContext* aCx) { NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); - + NS_ENSURE_ARG(!JSVAL_IS_PRIMITIVE(aObj)); + // Instantiating this class will register exception providers so even - // in xpcshell we will get typed (dom) exceptions, instead of general exceptions. + // in xpcshell we will get typed (dom) exceptions, instead of general + // exceptions. nsCOMPtr sof(do_GetService(kDOMSOF_CID)); - // Defining IDBKeyrange static functions on the global. - if (JSVAL_IS_PRIMITIVE(aObj)) { - return NS_ERROR_INVALID_ARG; - } - - nsCOMPtr factory = IDBFactory::Create(nsnull); - NS_ASSERTION(factory, "IDBFactory should not be null."); - JSObject* obj = JSVAL_TO_OBJECT(aObj); + + JSObject* global = JS_GetGlobalForObject(aCx, obj); + + nsCOMPtr factory = IDBFactory::Create(aCx, global); + NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE); + jsval mozIndexedDBVal; nsresult rv = nsContentUtils::WrapNative(aCx, obj, factory, &mozIndexedDBVal); NS_ENSURE_SUCCESS(rv, rv); - if (!JS_DefineProperty(aCx, obj, "mozIndexedDB", mozIndexedDBVal, - nsnull, nsnull, JSPROP_ENUMERATE)) { + if (!JS_DefineProperty(aCx, obj, "mozIndexedDB", mozIndexedDBVal, nsnull, + nsnull, JSPROP_ENUMERATE)) { return NS_ERROR_FAILURE; } JSObject* keyrangeObj = JS_NewObject(aCx, nsnull, nsnull, nsnull); NS_ENSURE_TRUE(keyrangeObj, NS_ERROR_OUT_OF_MEMORY); - + if (!IDBKeyRange::DefineConstructors(aCx, keyrangeObj)) { return NS_ERROR_FAILURE; } diff --git a/dom/indexedDB/Makefile.in b/dom/indexedDB/Makefile.in index 36b7e0902198..2bf85c2db826 100644 --- a/dom/indexedDB/Makefile.in +++ b/dom/indexedDB/Makefile.in @@ -61,16 +61,17 @@ CPPSRCS = \ IDBCursor.cpp \ IDBDatabase.cpp \ IDBEvents.cpp \ + IDBFactory.cpp \ IDBIndex.cpp \ IDBKeyRange.cpp \ IDBObjectStore.cpp \ IDBRequest.cpp \ IDBTransaction.cpp \ - IDBFactory.cpp \ + IDBWrapperCache.cpp \ IndexedDatabaseManager.cpp \ + Key.cpp \ OpenDatabaseHelper.cpp \ TransactionThreadPool.cpp \ - Key.cpp \ $(NULL) EXPORTS_mozilla/dom/indexedDB = \ @@ -82,6 +83,7 @@ EXPORTS_mozilla/dom/indexedDB = \ IDBObjectStore.h \ IDBRequest.h \ IDBTransaction.h \ + IDBWrapperCache.h \ IndexedDatabase.h \ IndexedDatabaseManager.h \ IDBFactory.h \ diff --git a/dom/indexedDB/OpenDatabaseHelper.cpp b/dom/indexedDB/OpenDatabaseHelper.cpp index ab3e86a3ede3..4a145575478f 100644 --- a/dom/indexedDB/OpenDatabaseHelper.cpp +++ b/dom/indexedDB/OpenDatabaseHelper.cpp @@ -1611,7 +1611,9 @@ OpenDatabaseHelper::DoDatabaseWork() NS_ASSERTION(mOpenDBRequest, "This should never be null!"); - nsPIDOMWindow* window = mOpenDBRequest->Owner(); + // This will be null for non-window contexts. + nsPIDOMWindow* window = mOpenDBRequest->GetOwner(); + AutoEnterWindow autoWindow(window); nsCOMPtr dbDirectory; @@ -2111,8 +2113,7 @@ OpenDatabaseHelper::EnsureSuccessResult() dbInfo->nextIndexId = mLastIndexId + 1; nsRefPtr database = - IDBDatabase::Create(mOpenDBRequest->ScriptContext(), - mOpenDBRequest->Owner(), + IDBDatabase::Create(mOpenDBRequest, dbInfo.forget(), mASCIIOrigin, mFileManager); From 17cb57ffd452875c26a9b2326a79823997e1e1f3 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Mon, 23 Jan 2012 14:25:47 +0000 Subject: [PATCH 03/58] Backout 143f01714f2b (bug 705640) for build failures --- dom/base/DOMError.cpp | 73 ------------------- dom/base/DOMError.h | 48 ------------ dom/base/Makefile.in | 3 - dom/base/nsDOMClassInfo.cpp | 9 --- dom/base/nsDOMClassInfoClasses.h | 2 - dom/base/nsIDOMDOMError.idl | 13 ---- dom/interfaces/core/nsIDOMDOMError.idl | 62 ++++++++++++++++ dom/interfaces/core/nsIDOMDOMErrorHandler.idl | 54 ++++++++++++++ js/xpconnect/src/dom_quickstubs.qsconf | 2 - 9 files changed, 116 insertions(+), 150 deletions(-) delete mode 100644 dom/base/DOMError.cpp delete mode 100644 dom/base/DOMError.h delete mode 100644 dom/base/nsIDOMDOMError.idl create mode 100644 dom/interfaces/core/nsIDOMDOMError.idl create mode 100644 dom/interfaces/core/nsIDOMDOMErrorHandler.idl diff --git a/dom/base/DOMError.cpp b/dom/base/DOMError.cpp deleted file mode 100644 index a72fab61c5d7..000000000000 --- a/dom/base/DOMError.cpp +++ /dev/null @@ -1,73 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "DOMError.h" - -#include "mozilla/Util.h" -#include "nsDOMClassInfo.h" - -using mozilla::ArrayLength; -using mozilla::dom::DOMError; - -// static -already_AddRefed -DOMError::CreateForDOMExceptionCode(PRUint16 aDOMExceptionCode) -{ - struct NameMap { PRUint16 code; char* name; }; - - static const NameMap kNames[] = { - { 1, "IndexSizeError" }, - { 3, "HierarchyRequestError" }, - { 4, "WrongDocumentError" }, - { 5, "InvalidCharacterError" }, - { 7, "NoModificationAllowedError" }, - { 8, "NotFoundError" }, - { 9, "NotSupportedError" }, - { 11, "InvalidStateError" }, - { 12, "SyntaxError" }, - { 13, "InvalidModificationError" }, - { 14, "NamespaceError" }, - { 15, "InvalidAccessError" }, - { 17, "TypeMismatchError" }, - { 18, "SecurityError" }, - { 19, "NetworkError" }, - { 20, "AbortError" }, - { 21, "URLMismatchError" }, - { 22, "QuotaExceededError" }, - { 23, "TimeoutError" }, - { 24, "InvalidNodeTypeError" }, - { 25, "DataCloneError" }, - }; - - for (size_t index = 0; index < ArrayLength(kNames); index++) { - if (kNames[index].code == aDOMExceptionCode) { - nsString name; - name.AssignASCII(kNames[index].name); - return CreateWithName(name); - } - } - - NS_NOTREACHED("Unknown DOMException code!"); - return nsnull; -} - -NS_IMPL_ADDREF(DOMError) -NS_IMPL_RELEASE(DOMError) - -NS_INTERFACE_MAP_BEGIN(DOMError) - NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DOMError) - NS_INTERFACE_MAP_ENTRY(nsIDOMDOMError) - NS_INTERFACE_MAP_ENTRY(nsISupports) -NS_INTERFACE_MAP_END - -DOMCI_DATA(DOMError, DOMError) - -NS_IMETHODIMP -DOMError::GetName(nsAString& aName) -{ - aName = mName; - return NS_OK; -} diff --git a/dom/base/DOMError.h b/dom/base/DOMError.h deleted file mode 100644 index e4058b0daee9..000000000000 --- a/dom/base/DOMError.h +++ /dev/null @@ -1,48 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_domerror_h__ -#define mozilla_dom_domerror_h__ - -#include "nsIDOMDOMError.h" - -#include "nsCOMPtr.h" -#include "nsStringGlue.h" - -namespace mozilla { -namespace dom { - -class DOMError : public nsIDOMDOMError -{ - nsString mName; - -public: - NS_DECL_ISUPPORTS - NS_DECL_NSIDOMDOMERROR - - static already_AddRefed - CreateForDOMExceptionCode(PRUint16 aDOMExceptionCode); - - static already_AddRefed - CreateWithName(const nsAString& aName) - { - nsCOMPtr error = new DOMError(aName); - return error.forget(); - } - -protected: - DOMError(const nsAString& aName) - : mName(aName) - { } - - virtual ~DOMError() - { } -}; - -} // namespace dom -} // namespace mozilla - -#endif // mozilla_dom_domerror_h__ diff --git a/dom/base/Makefile.in b/dom/base/Makefile.in index 74867bd35a45..b6fd3510bb44 100644 --- a/dom/base/Makefile.in +++ b/dom/base/Makefile.in @@ -66,7 +66,6 @@ EXTRA_JS_MODULES += Webapps.jsm \ endif XPIDLSRCS = \ - nsIDOMDOMError.idl \ nsIEntropyCollector.idl \ nsIScriptChannel.idl \ $(NULL) @@ -104,7 +103,6 @@ EXPORTS = \ EXPORTS_NAMESPACES = mozilla/dom EXPORTS_mozilla/dom = \ - DOMError.h \ StructuredCloneTags.h \ $(NULL) @@ -134,7 +132,6 @@ CPPSRCS = \ nsDOMNavigationTiming.cpp \ nsPerformance.cpp \ nsDOMMemoryReporter.cpp \ - DOMError.cpp \ Navigator.cpp \ $(NULL) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index 6038457903d9..bf075d9a835c 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -530,8 +530,6 @@ using mozilla::dom::indexedDB::IDBWrapperCache; #include "CallEvent.h" #endif -#include "DOMError.h" - using namespace mozilla; using namespace mozilla::dom; @@ -1625,9 +1623,6 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(CallEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) #endif - - NS_DEFINE_CLASSINFO_DATA(DOMError, nsDOMGenericSH, - DOM_DEFAULT_SCRIPTABLE_FLAGS) }; // Objects that should be constructable through |new Name();| @@ -4361,10 +4356,6 @@ nsDOMClassInfo::Init() DOM_CLASSINFO_MAP_END #endif - DOM_CLASSINFO_MAP_BEGIN(DOMError, nsIDOMDOMError) - DOM_CLASSINFO_MAP_ENTRY(nsIDOMDOMError) - DOM_CLASSINFO_MAP_END - #ifdef NS_DEBUG { PRUint32 i = ArrayLength(sClassInfoData); diff --git a/dom/base/nsDOMClassInfoClasses.h b/dom/base/nsDOMClassInfoClasses.h index d57053c2c4ed..a7066b4770f5 100644 --- a/dom/base/nsDOMClassInfoClasses.h +++ b/dom/base/nsDOMClassInfoClasses.h @@ -537,5 +537,3 @@ DOMCI_CLASS(Telephony) DOMCI_CLASS(TelephonyCall) DOMCI_CLASS(CallEvent) #endif - -DOMCI_CLASS(DOMError) diff --git a/dom/base/nsIDOMDOMError.idl b/dom/base/nsIDOMDOMError.idl deleted file mode 100644 index 06447b933b06..000000000000 --- a/dom/base/nsIDOMDOMError.idl +++ /dev/null @@ -1,13 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsISupports.idl" - -[scriptable, builtinclass, uuid(e4e28307-d409-4cf7-93cd-6ea8e889f87a)] -interface nsIDOMDOMError : nsISupports -{ - readonly attribute DOMString name; -}; diff --git a/dom/interfaces/core/nsIDOMDOMError.idl b/dom/interfaces/core/nsIDOMDOMError.idl new file mode 100644 index 000000000000..5059b646d3d7 --- /dev/null +++ b/dom/interfaces/core/nsIDOMDOMError.idl @@ -0,0 +1,62 @@ +/* -*- Mode: IDL; 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 + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Boris Zbarsky (original author) + * + * 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 "domstubs.idl" + +/** + * The nsIDOMDOMError interface is "An non-zero implementation + * dependent error code describing the error, or 0 if there is no + * error." [sic] + * + * For more information on this interface please see + * http://www.w3.org/TR/2002/WD-DOM-Level-3-Core-20020409/core.html + */ + +[scriptable, uuid(475790ce-d8fa-4e02-a167-e6308ba9b120)] +interface nsIDOMDOMError : nsISupports +{ + const unsigned short SEVERITY_WARNING = 0; + const unsigned short SEVERITY_ERROR = 1; + const unsigned short SEVERITY_FATAL_ERROR = 2; + readonly attribute unsigned short severity; + readonly attribute DOMString message; + // XXX This should be a DOMObject, not an nsISupports; do we need to + // do some magic to make that work? + readonly attribute nsISupports relatedException; + readonly attribute nsIDOMDOMLocator location; +}; diff --git a/dom/interfaces/core/nsIDOMDOMErrorHandler.idl b/dom/interfaces/core/nsIDOMDOMErrorHandler.idl new file mode 100644 index 000000000000..33b64dc7a369 --- /dev/null +++ b/dom/interfaces/core/nsIDOMDOMErrorHandler.idl @@ -0,0 +1,54 @@ +/* -*- Mode: IDL; 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 + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 2002 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Boris Zbarsky (original author) + * + * 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 "domstubs.idl" + +/** + * The nsIDOMDOMErrorHandler interface is a basic interface for DOM + * error handlers + * + * For more information on this interface please see + * http://www.w3.org/TR/2002/WD-DOM-Level-3-Core-20020409/core.html + */ + +[scriptable, uuid(2d958bdf-740d-43f4-9e5c-5d930f4f3876)] +interface nsIDOMDOMErrorHandler : nsISupports +{ + boolean handleError(in nsIDOMDOMError error); +}; + diff --git a/js/xpconnect/src/dom_quickstubs.qsconf b/js/xpconnect/src/dom_quickstubs.qsconf index a5d3b4baa05e..addc9e7ae392 100644 --- a/js/xpconnect/src/dom_quickstubs.qsconf +++ b/js/xpconnect/src/dom_quickstubs.qsconf @@ -467,8 +467,6 @@ members = [ 'nsIIDBVersionChangeEvent.*', 'nsIIndexedDatabaseUsageCallback.*', 'nsIIndexedDatabaseManager.*', - - 'nsIDOMDOMError.*', ] # Most interfaces can be found by searching the includePath; to find From d61eeae4c7d291bdc1720cf53405aa3ab1ad0b26 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Mon, 23 Jan 2012 16:52:38 +0100 Subject: [PATCH 04/58] Bug 579535 - Remove AddScriptBlockerAndPreventAddingRunners() API and warn about script runner failures. r=bz --- content/base/public/nsContentUtils.h | 7 ------- content/base/src/nsContentUtils.cpp | 18 ------------------ content/base/src/nsDocument.cpp | 10 +++++++++- 3 files changed, 9 insertions(+), 26 deletions(-) diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index 04ba2a9e1bd0..62f80c181441 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -1451,13 +1451,6 @@ public: */ static void AddScriptBlocker(); - /** - * Increases the count of blockers preventing scripts from running. - * Also, while this script blocker is active, script runners must not be - * added --- we'll assert if one is, and ignore it. - */ - static void AddScriptBlockerAndPreventAddingRunners(); - /** * Decreases the count of blockers preventing scripts from running. * NOTE: You might want to use nsAutoScriptBlocker rather than calling diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 8e85b7dc290b..5d8d8dccb25e 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -260,7 +260,6 @@ PRUint32 nsContentUtils::sDOMNodeRemovedSuppressCount = 0; #endif nsTArray< nsCOMPtr >* nsContentUtils::sBlockedScriptRunners = nsnull; PRUint32 nsContentUtils::sRunnersCountAtFirstBlocker = 0; -PRUint32 nsContentUtils::sScriptBlockerCountWhereRunnersPrevented = 0; nsIInterfaceRequestor* nsContentUtils::sSameOriginChecker = nsnull; bool nsContentUtils::sIsHandlingKeyBoardEvent = false; @@ -4417,25 +4416,12 @@ nsContentUtils::AddScriptBlocker() ++sScriptBlockerCount; } -/* static */ -void -nsContentUtils::AddScriptBlockerAndPreventAddingRunners() -{ - AddScriptBlocker(); - if (sScriptBlockerCountWhereRunnersPrevented == 0) { - sScriptBlockerCountWhereRunnersPrevented = sScriptBlockerCount; - } -} - /* static */ void nsContentUtils::RemoveScriptBlocker() { NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers"); --sScriptBlockerCount; - if (sScriptBlockerCount < sScriptBlockerCountWhereRunnersPrevented) { - sScriptBlockerCountWhereRunnersPrevented = 0; - } if (sScriptBlockerCount) { return; } @@ -4469,10 +4455,6 @@ nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable) } if (sScriptBlockerCount) { - if (sScriptBlockerCountWhereRunnersPrevented > 0) { - NS_ERROR("Adding a script runner when that is prevented!"); - return false; - } return sBlockedScriptRunners->AppendElement(aRunnable) != nsnull; } diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index cee9d034fed5..acd8f89d66dd 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -7097,8 +7097,16 @@ nsDocument::BlockOnload() // block onload only when there are no script blockers. ++mAsyncOnloadBlockCount; if (mAsyncOnloadBlockCount == 1) { - nsContentUtils::AddScriptRunner( + bool success = nsContentUtils::AddScriptRunner( NS_NewRunnableMethod(this, &nsDocument::AsyncBlockOnload)); + + // The script runner shouldn't fail to add. But if somebody broke + // something and it does, we'll thrash at 100% cpu forever. The best + // response is just to ignore the onload blocking request. See bug 579535. + if (!success) { + NS_WARNING("Disaster! Onload blocking script runner failed to add - expect bad things!"); + mAsyncOnloadBlockCount = 0; + } } return; } From b39bbb8f95b60e404c538c8530b079cf281c3785 Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Mon, 23 Jan 2012 16:18:14 +0000 Subject: [PATCH 05/58] Backout 8886b027527a (bug 718132) for xpcshell failures --- content/base/src/nsContentUtils.cpp | 5 +- dom/base/nsDOMClassInfo.cpp | 94 +--------- dom/indexedDB/AsyncConnectionHelper.cpp | 15 +- dom/indexedDB/IDBCursor.cpp | 18 +- dom/indexedDB/IDBCursor.h | 1 - dom/indexedDB/IDBDatabase.cpp | 84 ++++++--- dom/indexedDB/IDBDatabase.h | 31 +++- dom/indexedDB/IDBFactory.cpp | 75 ++------ dom/indexedDB/IDBFactory.h | 18 +- dom/indexedDB/IDBIndex.cpp | 10 +- dom/indexedDB/IDBIndex.h | 3 + dom/indexedDB/IDBObjectStore.cpp | 11 +- dom/indexedDB/IDBObjectStore.h | 6 +- dom/indexedDB/IDBRequest.cpp | 221 +++++++++++++---------- dom/indexedDB/IDBRequest.h | 52 +++--- dom/indexedDB/IDBTransaction.cpp | 85 ++++++--- dom/indexedDB/IDBTransaction.h | 20 +- dom/indexedDB/IDBWrapperCache.cpp | 38 ---- dom/indexedDB/IDBWrapperCache.h | 74 -------- dom/indexedDB/IndexedDatabaseManager.cpp | 30 +-- dom/indexedDB/Makefile.in | 6 +- dom/indexedDB/OpenDatabaseHelper.cpp | 7 +- 22 files changed, 400 insertions(+), 504 deletions(-) delete mode 100644 dom/indexedDB/IDBWrapperCache.cpp delete mode 100644 dom/indexedDB/IDBWrapperCache.h diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 5d8d8dccb25e..4edc996b5da5 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -5436,9 +5436,8 @@ public: } NS_IMETHOD_(void) NoteScriptChild(PRUint32 langID, void* child) { - if (langID == nsIProgrammingLanguage::JAVASCRIPT && - child == mWrapper) { - mFound = true; + if (langID == nsIProgrammingLanguage::JAVASCRIPT) { + mFound = child == mWrapper; } } NS_IMETHOD_(void) NoteXPCOMChild(nsISupports *child) diff --git a/dom/base/nsDOMClassInfo.cpp b/dom/base/nsDOMClassInfo.cpp index bf075d9a835c..bbb350f44253 100644 --- a/dom/base/nsDOMClassInfo.cpp +++ b/dom/base/nsDOMClassInfo.cpp @@ -490,7 +490,6 @@ #include "DOMSVGPointList.h" #include "DOMSVGTransformList.h" -#include "mozilla/dom/indexedDB/IDBWrapperCache.h" #include "mozilla/dom/indexedDB/IDBFactory.h" #include "mozilla/dom/indexedDB/IDBRequest.h" #include "mozilla/dom/indexedDB/IDBDatabase.h" @@ -502,8 +501,6 @@ #include "mozilla/dom/indexedDB/IDBIndex.h" #include "nsIIDBDatabaseException.h" -using mozilla::dom::indexedDB::IDBWrapperCache; - #include "nsIDOMMediaQueryList.h" #include "nsDOMTouchEvent.h" @@ -596,10 +593,6 @@ static const char kDOMStringBundleURL[] = (DOM_DEFAULT_SCRIPTABLE_FLAGS | \ nsIXPCScriptable::WANT_ADDPROPERTY) -#define IDBEVENTTARGET_SCRIPTABLE_FLAGS \ - (EVENTTARGET_SCRIPTABLE_FLAGS | \ - nsIXPCScriptable::WANT_POSTCREATE) - #define DOMCLASSINFO_STANDARD_FLAGS \ (nsIClassInfo::MAIN_THREAD_ONLY | nsIClassInfo::DOM_OBJECT) @@ -661,37 +654,6 @@ DOMCI_DATA(DOMConstructor, void) NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA_WITH_NAME(_class, _class, _helper, \ _flags) -namespace { - -class IDBEventTargetSH : public nsEventTargetSH -{ -protected: - IDBEventTargetSH(nsDOMClassInfoData* aData) : nsEventTargetSH(aData) - { } - - virtual ~IDBEventTargetSH() - { } - -public: - NS_IMETHOD PreCreate(nsISupports *aNativeObj, JSContext *aCx, - JSObject *aGlobalObj, JSObject **aParentObj); - - NS_IMETHOD PostCreate(nsIXPConnectWrappedNative *aQrapper, JSContext *aCx, - JSObject *aObj); - - NS_IMETHOD AddProperty(nsIXPConnectWrappedNative *aWrapper, JSContext *aCx, - JSObject *aObj, jsid aId, jsval *aVp, bool *aRetval); - - virtual void PreserveWrapper(nsISupports *aNative); - - static nsIClassInfo *doCreate(nsDOMClassInfoData *aData) - { - return new IDBEventTargetSH(aData); - } -}; - -} // anonymous namespace - // This list of NS_DEFINE_CLASSINFO_DATA macros is what gives the DOM // classes their correct behavior when used through XPConnect. The // arguments that are passed to NS_DEFINE_CLASSINFO_DATA are @@ -1575,14 +1537,14 @@ static nsDOMClassInfoData sClassInfoData[] = { NS_DEFINE_CLASSINFO_DATA(IDBFactory, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(IDBRequest, IDBEventTargetSH, - IDBEVENTTARGET_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(IDBDatabase, IDBEventTargetSH, - IDBEVENTTARGET_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(IDBRequest, nsEventTargetSH, + EVENTTARGET_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(IDBDatabase, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(IDBObjectStore, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(IDBTransaction, IDBEventTargetSH, - IDBEVENTTARGET_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(IDBTransaction, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(IDBCursor, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(IDBCursorWithValue, nsDOMGenericSH, @@ -1593,8 +1555,8 @@ static nsDOMClassInfoData sClassInfoData[] = { DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(IDBVersionChangeEvent, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) - NS_DEFINE_CLASSINFO_DATA(IDBOpenDBRequest, IDBEventTargetSH, - IDBEVENTTARGET_SCRIPTABLE_FLAGS) + NS_DEFINE_CLASSINFO_DATA(IDBOpenDBRequest, nsDOMGenericSH, + DOM_DEFAULT_SCRIPTABLE_FLAGS) NS_DEFINE_CLASSINFO_DATA(IDBDatabaseException, nsDOMGenericSH, DOM_DEFAULT_SCRIPTABLE_FLAGS) @@ -7650,46 +7612,6 @@ nsEventTargetSH::PreserveWrapper(nsISupports *aNative) nsContentUtils::PreserveWrapper(aNative, target); } -// IDBEventTarget helper - -NS_IMETHODIMP -IDBEventTargetSH::PreCreate(nsISupports *aNativeObj, JSContext *aCx, - JSObject *aGlobalObj, JSObject **aParentObj) -{ - IDBWrapperCache *target = IDBWrapperCache::FromSupports(aNativeObj); - JSObject *parent = target->GetParentObject(); - *aParentObj = parent ? parent : aGlobalObj; - return NS_OK; -} - -NS_IMETHODIMP -IDBEventTargetSH::PostCreate(nsIXPConnectWrappedNative *aWrapper, - JSContext *aCx, JSObject *aObj) -{ - IDBWrapperCache *target = IDBWrapperCache::FromSupports(aWrapper->Native()); - target->OnWrapperCreated(); - return NS_OK; -} - -NS_IMETHODIMP -IDBEventTargetSH::AddProperty(nsIXPConnectWrappedNative *aWrapper, - JSContext *aCx, JSObject *aObj, jsid aId, - jsval *aVp, bool *aRetval) -{ - if (aId == sAddEventListener_id) { - return nsEventTargetSH::AddProperty(aWrapper, aCx, aObj, aId, aVp, aRetval); - } - - IDBEventTargetSH::PreserveWrapper(GetNative(aWrapper, aObj)); - return NS_OK; -} - -void -IDBEventTargetSH::PreserveWrapper(nsISupports *aNative) -{ - IDBWrapperCache *target = IDBWrapperCache::FromSupports(aNative); - nsContentUtils::PreserveWrapper(aNative, target); -} // Element helper diff --git a/dom/indexedDB/AsyncConnectionHelper.cpp b/dom/indexedDB/AsyncConnectionHelper.cpp index 26cfa17d4ffb..03713ace5bf8 100644 --- a/dom/indexedDB/AsyncConnectionHelper.cpp +++ b/dom/indexedDB/AsyncConnectionHelper.cpp @@ -146,11 +146,18 @@ HelperBase::WrapNative(JSContext* aCx, NS_ASSERTION(aResult, "Null pointer!"); NS_ASSERTION(mRequest, "Null request!"); - JSObject* global = mRequest->GetParentObject(); - NS_ASSERTION(global, "This should never be null!"); + JSObject* obj; + if (mRequest->ScriptContext()) { + obj = mRequest->ScriptContext()->GetNativeGlobal(); + } + else { + obj = mRequest->GetWrapper(); + } + + NS_ENSURE_TRUE(obj, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsresult rv = - nsContentUtils::WrapNative(aCx, global, aNative, aResult); + nsContentUtils::WrapNative(aCx, obj, aNative, aResult); NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); return NS_OK; @@ -281,7 +288,7 @@ AsyncConnectionHelper::Run() if (NS_SUCCEEDED(rv)) { bool hasSavepoint = false; if (mDatabase) { - IndexedDatabaseManager::SetCurrentWindow(mDatabase->GetOwner()); + IndexedDatabaseManager::SetCurrentWindow(mDatabase->Owner()); // Make the first savepoint. if (mTransaction) { diff --git a/dom/indexedDB/IDBCursor.cpp b/dom/indexedDB/IDBCursor.cpp index c614edfbea73..0d3985d578da 100644 --- a/dom/indexedDB/IDBCursor.cpp +++ b/dom/indexedDB/IDBCursor.cpp @@ -64,7 +64,8 @@ GenerateRequest(IDBCursor* aCursor) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBDatabase* database = aCursor->Transaction()->Database(); - return IDBRequest::Create(aCursor, database, aCursor->Transaction()); + return IDBRequest::Create(aCursor, database->ScriptContext(), + database->Owner(), aCursor->Transaction()); } } // anonymous namespace @@ -261,14 +262,11 @@ IDBCursor::CreateCommon(IDBRequest* aRequest, nsRefPtr cursor = new IDBCursor(); - IDBDatabase* database = aTransaction->Database(); - cursor->mScriptContext = database->GetScriptContext(); - cursor->mOwner = database->GetOwner(); - cursor->mScriptOwner = database->GetScriptOwner(); - cursor->mRequest = aRequest; cursor->mTransaction = aTransaction; cursor->mObjectStore = aObjectStore; + cursor->mScriptContext = aTransaction->Database()->ScriptContext(); + cursor->mOwner = aTransaction->Database()->Owner(); cursor->mDirection = aDirection; cursor->mContinueQuery = aContinueQuery; cursor->mContinueToQuery = aContinueToQuery; @@ -278,8 +276,7 @@ IDBCursor::CreateCommon(IDBRequest* aRequest, } IDBCursor::IDBCursor() -: mScriptOwner(nsnull), - mType(OBJECTSTORE), +: mType(OBJECTSTORE), mDirection(nsIIDBCursor::NEXT), mCachedKey(JSVAL_VOID), mCachedPrimaryKey(JSVAL_VOID), @@ -380,10 +377,6 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBCursor) "Should have a cached primary key"); NS_ASSERTION(tmp->mHaveCachedValue || JSVAL_IS_VOID(tmp->mCachedValue), "Should have a cached value"); - if (tmp->mScriptOwner) { - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->mScriptOwner, - "mScriptOwner") - } if (JSVAL_IS_GCTHING(tmp->mCachedKey)) { void *gcThing = JSVAL_TO_GCTHING(tmp->mCachedKey); NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mCachedKey") @@ -402,7 +395,6 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBCursor) // Don't unlink mObjectStore, mIndex, or mTransaction! if (tmp->mRooted) { NS_DROP_JS_OBJECTS(tmp, IDBCursor); - tmp->mScriptOwner = nsnull; tmp->mCachedKey = JSVAL_VOID; tmp->mCachedPrimaryKey = JSVAL_VOID; tmp->mCachedValue = JSVAL_VOID; diff --git a/dom/indexedDB/IDBCursor.h b/dom/indexedDB/IDBCursor.h index 00a3fa4c7baf..4cc6f74525e8 100644 --- a/dom/indexedDB/IDBCursor.h +++ b/dom/indexedDB/IDBCursor.h @@ -154,7 +154,6 @@ protected: nsCOMPtr mScriptContext; nsCOMPtr mOwner; - JSObject* mScriptOwner; Type mType; PRUint16 mDirection; diff --git a/dom/indexedDB/IDBDatabase.cpp b/dom/indexedDB/IDBDatabase.cpp index a97a0ea0aacc..d3ed92f9d36c 100644 --- a/dom/indexedDB/IDBDatabase.cpp +++ b/dom/indexedDB/IDBDatabase.cpp @@ -148,7 +148,8 @@ private: // static already_AddRefed -IDBDatabase::Create(IDBWrapperCache* aOwnerCache, +IDBDatabase::Create(nsIScriptContext* aScriptContext, + nsPIDOMWindow* aOwner, already_AddRefed aDatabaseInfo, const nsACString& aASCIIOrigin, FileManager* aFileManager) @@ -161,9 +162,8 @@ IDBDatabase::Create(IDBWrapperCache* aOwnerCache, nsRefPtr db(new IDBDatabase()); - db->mScriptContext = aOwnerCache->GetScriptContext(); - db->mOwner = aOwnerCache->GetOwner(); - db->mScriptOwner = aOwnerCache->GetScriptOwner(); + db->mScriptContext = aScriptContext; + db->mOwner = aOwner; db->mDatabaseId = databaseInfo->id; db->mName = databaseInfo->name; @@ -205,8 +205,6 @@ IDBDatabase::~IDBDatabase() mgr->UnregisterDatabase(this); } } - - nsContentUtils::ReleaseWrapper(static_cast(this), this); } void @@ -220,7 +218,7 @@ IDBDatabase::Invalidate() // When the IndexedDatabaseManager needs to invalidate databases, all it has // is an origin, so we call back into the manager to cancel any prompts for // our owner. - IndexedDatabaseManager::CancelPromptsForWindow(GetOwner()); + IndexedDatabaseManager::CancelPromptsForWindow(Owner()); mInvalidated = true; } @@ -282,8 +280,7 @@ void IDBDatabase::OnUnlink() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(!GetOwner() && !GetScriptOwner(), - "Should have been cleared already!"); + NS_ASSERTION(!mOwner, "Should have been cleared already!"); // We've been unlinked, at the very least we should be able to prevent further // transactions from starting and unblock any other SetVersion callers. @@ -301,16 +298,18 @@ IDBDatabase::OnUnlink() NS_IMPL_CYCLE_COLLECTION_CLASS(IDBDatabase) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) - NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(abort) - NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error) - NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(versionchange) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBDatabase, + nsDOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnAbortListener) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnVersionChangeListener) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, IDBWrapperCache) - NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(abort) - NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error) - NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(versionchange) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBDatabase, + nsDOMEventTargetHelper) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnAbortListener) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnVersionChangeListener) // Do some cleanup. tmp->OnUnlink(); @@ -319,17 +318,13 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBDatabase) NS_INTERFACE_MAP_ENTRY(nsIIDBDatabase) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBDatabase) -NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) +NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper) -NS_IMPL_ADDREF_INHERITED(IDBDatabase, IDBWrapperCache) -NS_IMPL_RELEASE_INHERITED(IDBDatabase, IDBWrapperCache) +NS_IMPL_ADDREF_INHERITED(IDBDatabase, nsDOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(IDBDatabase, nsDOMEventTargetHelper) DOMCI_DATA(IDBDatabase, IDBDatabase) -NS_IMPL_EVENT_HANDLER(IDBDatabase, abort); -NS_IMPL_EVENT_HANDLER(IDBDatabase, error); -NS_IMPL_EVENT_HANDLER(IDBDatabase, versionchange); - NS_IMETHODIMP IDBDatabase::GetName(nsAString& aName) { @@ -673,6 +668,47 @@ IDBDatabase::Close() return NS_OK; } +NS_IMETHODIMP +IDBDatabase::SetOnabort(nsIDOMEventListener* aAbortListener) +{ + return RemoveAddEventListener(NS_LITERAL_STRING(ABORT_EVT_STR), + mOnAbortListener, aAbortListener); +} + +NS_IMETHODIMP +IDBDatabase::GetOnabort(nsIDOMEventListener** aAbortListener) +{ + return GetInnerEventListener(mOnAbortListener, aAbortListener); +} + +NS_IMETHODIMP +IDBDatabase::SetOnerror(nsIDOMEventListener* aErrorListener) +{ + return RemoveAddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR), + mOnErrorListener, aErrorListener); +} + +NS_IMETHODIMP +IDBDatabase::GetOnerror(nsIDOMEventListener** aErrorListener) +{ + return GetInnerEventListener(mOnErrorListener, aErrorListener); +} + +NS_IMETHODIMP +IDBDatabase::SetOnversionchange(nsIDOMEventListener* aVersionChangeListener) +{ + return RemoveAddEventListener(NS_LITERAL_STRING(VERSIONCHANGE_EVT_STR), + mOnVersionChangeListener, + aVersionChangeListener); +} + +NS_IMETHODIMP +IDBDatabase::GetOnversionchange(nsIDOMEventListener** aVersionChangeListener) +{ + return GetInnerEventListener(mOnVersionChangeListener, + aVersionChangeListener); +} + nsresult IDBDatabase::PostHandleEvent(nsEventChainPostVisitor& aVisitor) { diff --git a/dom/indexedDB/IDBDatabase.h b/dom/indexedDB/IDBDatabase.h index dcc2253f17f5..574b4ff59ea4 100644 --- a/dom/indexedDB/IDBDatabase.h +++ b/dom/indexedDB/IDBDatabase.h @@ -41,12 +41,13 @@ #define mozilla_dom_indexeddb_idbdatabase_h__ #include "mozilla/dom/indexedDB/IndexedDatabase.h" +#include "mozilla/dom/indexedDB/FileManager.h" -#include "nsIDocument.h" #include "nsIIDBDatabase.h" -#include "mozilla/dom/indexedDB/IDBWrapperCache.h" -#include "mozilla/dom/indexedDB/FileManager.h" +#include "nsCycleCollectionParticipant.h" +#include "nsDOMEventTargetHelper.h" +#include "nsIDocument.h" class nsIScriptContext; class nsPIDOMWindow; @@ -60,7 +61,7 @@ class IDBObjectStore; class IDBTransaction; class IndexedDatabaseManager; -class IDBDatabase : public IDBWrapperCache, +class IDBDatabase : public nsDOMEventTargetHelper, public nsIIDBDatabase { friend class AsyncConnectionHelper; @@ -70,10 +71,12 @@ public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIIDBDATABASE - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, IDBWrapperCache) + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBDatabase, + nsDOMEventTargetHelper) static already_AddRefed - Create(IDBWrapperCache* aOwnerCache, + Create(nsIScriptContext* aScriptContext, + nsPIDOMWindow* aOwner, already_AddRefed aDatabaseInfo, const nsACString& aASCIIOrigin, FileManager* aFileManager); @@ -101,6 +104,16 @@ public: return mFilePath; } + nsIScriptContext* ScriptContext() + { + return mScriptContext; + } + + nsPIDOMWindow* Owner() + { + return mOwner; + } + already_AddRefed GetOwnerDocument() { if (!mOwner) { @@ -155,9 +168,9 @@ private: nsRefPtr mFileManager; // Only touched on the main thread. - NS_DECL_EVENT_HANDLER(abort); - NS_DECL_EVENT_HANDLER(error); - NS_DECL_EVENT_HANDLER(versionchange); + nsRefPtr mOnAbortListener; + nsRefPtr mOnErrorListener; + nsRefPtr mOnVersionChangeListener; }; END_INDEXEDDB_NAMESPACE diff --git a/dom/indexedDB/IDBFactory.cpp b/dom/indexedDB/IDBFactory.cpp index a545aac74beb..404db4076679 100644 --- a/dom/indexedDB/IDBFactory.cpp +++ b/dom/indexedDB/IDBFactory.cpp @@ -69,6 +69,7 @@ #include "IDBKeyRange.h" #include "IndexedDatabaseManager.h" #include "Key.h" +#include "nsIScriptSecurityManager.h" using namespace mozilla; @@ -90,49 +91,28 @@ struct ObjectStoreInfoMap } // anonymous namespace IDBFactory::IDBFactory() -: mOwningObject(nsnull) { IDBFactory::NoteUsedByProcessType(XRE_GetProcessType()); } -IDBFactory::~IDBFactory() -{ - if (mOwningObject) { - NS_DROP_JS_OBJECTS(this, IDBFactory); - } -} - // static already_AddRefed IDBFactory::Create(nsPIDOMWindow* aWindow) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ENSURE_TRUE(aWindow, nsnull); - - if (aWindow->IsOuterWindow()) { + if (aWindow && aWindow->IsOuterWindow()) { aWindow = aWindow->GetCurrentInnerWindow(); NS_ENSURE_TRUE(aWindow, nsnull); } nsRefPtr factory = new IDBFactory(); - factory->mWindow = aWindow; - return factory.forget(); -} -// static -already_AddRefed -IDBFactory::Create(JSContext* aCx, - JSObject* aOwningObject) -{ - NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(aCx, "Null context!"); - NS_ASSERTION(aOwningObject, "Null object!"); - NS_ASSERTION(JS_GetGlobalForObject(aCx, aOwningObject) == aOwningObject, - "Not a global object!"); + if (aWindow) { + factory->mWindow = do_GetWeakReference(aWindow); + NS_ENSURE_TRUE(factory->mWindow, nsnull); + } - nsRefPtr factory = new IDBFactory(); - factory->mOwningObject = aOwningObject; return factory.forget(); } @@ -420,36 +400,15 @@ IDBFactory::SetDatabaseMetadata(DatabaseInfo* aDatabaseInfo, return NS_OK; } -NS_IMPL_CYCLE_COLLECTION_CLASS(IDBFactory) +NS_IMPL_ADDREF(IDBFactory) +NS_IMPL_RELEASE(IDBFactory) -NS_IMPL_CYCLE_COLLECTING_ADDREF(IDBFactory) -NS_IMPL_CYCLE_COLLECTING_RELEASE(IDBFactory) - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBFactory) +NS_INTERFACE_MAP_BEGIN(IDBFactory) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_ENTRY(nsIIDBFactory) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBFactory) NS_INTERFACE_MAP_END -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBFactory) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS - NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mWindow) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBFactory) - if (tmp->mOwningObject) { - tmp->mOwningObject = nsnull; - } - NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mWindow) -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(IDBFactory) - if (tmp->mOwningObject) { - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->mOwningObject, - "mOwningObject") - } -NS_IMPL_CYCLE_COLLECTION_TRACE_END - DOMCI_DATA(IDBFactory, IDBFactory) nsresult @@ -459,7 +418,6 @@ IDBFactory::OpenCommon(const nsAString& aName, nsIIDBOpenDBRequest** _retval) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - NS_ASSERTION(mWindow || mOwningObject, "Must have one of these!"); if (XRE_GetProcessType() == GeckoProcessType_Content) { // Force ContentChild to cache the path from the parent, so that @@ -472,19 +430,16 @@ IDBFactory::OpenCommon(const nsAString& aName, nsCOMPtr window; nsCOMPtr sgo; nsIScriptContext* context = nsnull; - JSObject* scriptOwner = nsnull; if (mWindow) { - sgo = do_QueryInterface(mWindow); + window = do_QueryReferent(mWindow); + NS_ENSURE_TRUE(window, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + + sgo = do_QueryInterface(window); NS_ENSURE_TRUE(sgo, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - + context = sgo->GetContext(); NS_ENSURE_TRUE(context, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - - window = mWindow; - } - else { - scriptOwner = mOwningObject; } nsCString origin; @@ -493,7 +448,7 @@ IDBFactory::OpenCommon(const nsAString& aName, NS_ENSURE_SUCCESS(rv, rv); nsRefPtr request = - IDBOpenDBRequest::Create(context, window, scriptOwner); + IDBOpenDBRequest::Create(context, window); NS_ENSURE_TRUE(request, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); nsRefPtr openHelper = diff --git a/dom/indexedDB/IDBFactory.h b/dom/indexedDB/IDBFactory.h index 6144330692e7..8a1871fe180c 100644 --- a/dom/indexedDB/IDBFactory.h +++ b/dom/indexedDB/IDBFactory.h @@ -45,11 +45,11 @@ #include "mozIStorageConnection.h" #include "nsIIDBFactory.h" -#include "nsCycleCollectionParticipant.h" +#include "nsIWeakReferenceUtils.h" #include "nsXULAppAPI.h" -class nsIAtom; class nsPIDOMWindow; +class nsIAtom; BEGIN_INDEXEDDB_NAMESPACE @@ -60,17 +60,12 @@ struct ObjectStoreInfo; class IDBFactory : public nsIIDBFactory { typedef nsTArray > ObjectStoreInfoArray; - public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(IDBFactory) + NS_DECL_ISUPPORTS NS_DECL_NSIIDBFACTORY static already_AddRefed Create(nsPIDOMWindow* aWindow); - static already_AddRefed Create(JSContext* aCx, - JSObject* aOwningObject); - static already_AddRefed GetConnection(const nsAString& aDatabaseFilePath); @@ -101,7 +96,7 @@ public: private: IDBFactory(); - ~IDBFactory(); + ~IDBFactory() { } nsresult OpenCommon(const nsAString& aName, @@ -109,10 +104,7 @@ private: bool aDeleting, nsIIDBOpenDBRequest** _retval); - // If this factory lives on a window then mWindow must be non-null. Otherwise - // mOwningObject must be non-null. - nsCOMPtr mWindow; - JSObject* mOwningObject; + nsCOMPtr mWindow; }; END_INDEXEDDB_NAMESPACE diff --git a/dom/indexedDB/IDBIndex.cpp b/dom/indexedDB/IDBIndex.cpp index 52e89c4223c2..73c93d7e8db7 100644 --- a/dom/indexedDB/IDBIndex.cpp +++ b/dom/indexedDB/IDBIndex.cpp @@ -294,7 +294,8 @@ GenerateRequest(IDBIndex* aIndex) NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBTransaction* transaction = aIndex->ObjectStore()->Transaction(); IDBDatabase* database = transaction->Database(); - return IDBRequest::Create(aIndex, database, transaction); + return IDBRequest::Create(aIndex, database->ScriptContext(), + database->Owner(), transaction); } } // anonymous namespace @@ -312,6 +313,9 @@ IDBIndex::Create(IDBObjectStore* aObjectStore, nsRefPtr index = new IDBIndex(); + index->mScriptContext = database->ScriptContext(); + index->mOwner = database->Owner(); + index->mObjectStore = aObjectStore; index->mId = aIndexInfo->id; index->mName = aIndexInfo->name; @@ -339,10 +343,14 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(IDBIndex) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBIndex) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mObjectStore) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBIndex) // Don't unlink mObjectStore! + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOwner) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptContext) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(IDBIndex) diff --git a/dom/indexedDB/IDBIndex.h b/dom/indexedDB/IDBIndex.h index 1f08de79a64c..8f6b65a356cb 100644 --- a/dom/indexedDB/IDBIndex.h +++ b/dom/indexedDB/IDBIndex.h @@ -113,6 +113,9 @@ private: nsRefPtr mObjectStore; + nsCOMPtr mScriptContext; + nsCOMPtr mOwner; + PRInt64 mId; nsString mName; nsString mKeyPath; diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index 30f15d7d1b6f..3c23eb83ec38 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -507,8 +507,8 @@ GenerateRequest(IDBObjectStore* aObjectStore) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); IDBDatabase* database = aObjectStore->Transaction()->Database(); - return IDBRequest::Create(aObjectStore, database, - aObjectStore->Transaction()); + return IDBRequest::Create(aObjectStore, database->ScriptContext(), + database->Owner(), aObjectStore->Transaction()); } JSClass gDummyPropClass = { @@ -532,6 +532,9 @@ IDBObjectStore::Create(IDBTransaction* aTransaction, nsRefPtr objectStore = new IDBObjectStore(); + objectStore->mScriptContext = aTransaction->Database()->ScriptContext(); + objectStore->mOwner = aTransaction->Database()->Owner(); + objectStore->mTransaction = aTransaction; objectStore->mName = aStoreInfo->name; objectStore->mId = aStoreInfo->id; @@ -1373,6 +1376,8 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(IDBObjectStore) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(IDBObjectStore) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mTransaction, nsIDOMEventTarget) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOwner) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mScriptContext) for (PRUint32 i = 0; i < tmp->mCreatedIndexes.Length(); i++) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedIndexes[i]"); @@ -1382,6 +1387,8 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(IDBObjectStore) // Don't unlink mTransaction! + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOwner) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mScriptContext) tmp->mCreatedIndexes.Clear(); NS_IMPL_CYCLE_COLLECTION_UNLINK_END diff --git a/dom/indexedDB/IDBObjectStore.h b/dom/indexedDB/IDBObjectStore.h index 0ddfef459e76..861a5a306a81 100644 --- a/dom/indexedDB/IDBObjectStore.h +++ b/dom/indexedDB/IDBObjectStore.h @@ -41,14 +41,13 @@ #define mozilla_dom_indexeddb_idbobjectstore_h__ #include "mozilla/dom/indexedDB/IndexedDatabase.h" +#include "mozilla/dom/indexedDB/IDBTransaction.h" #include "nsIIDBObjectStore.h" #include "nsIIDBTransaction.h" #include "nsCycleCollectionParticipant.h" -#include "mozilla/dom/indexedDB/IDBTransaction.h" - class nsIScriptContext; class nsPIDOMWindow; @@ -205,6 +204,9 @@ protected: private: nsRefPtr mTransaction; + nsCOMPtr mScriptContext; + nsCOMPtr mOwner; + PRInt64 mId; nsString mName; nsString mKeyPath; diff --git a/dom/indexedDB/IDBRequest.cpp b/dom/indexedDB/IDBRequest.cpp index e04a1f1a2083..1088844583e0 100644 --- a/dom/indexedDB/IDBRequest.cpp +++ b/dom/indexedDB/IDBRequest.cpp @@ -62,8 +62,8 @@ USING_INDEXEDDB_NAMESPACE IDBRequest::IDBRequest() : mResultVal(JSVAL_VOID), mErrorCode(0), - mHaveResultOrErrorCode(false), - mManuallyRooted(false) + mResultValRooted(false), + mHaveResultOrErrorCode(false) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); } @@ -72,13 +72,19 @@ IDBRequest::~IDBRequest() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - UnrootResultVal(); + if (mResultValRooted) { + // Calling a virtual from the destructor is bad... But we know that we won't + // call a subclass' implementation because mResultValRooted will be set to + // false. + UnrootResultVal(); + } } // static already_AddRefed IDBRequest::Create(nsISupports* aSource, - IDBWrapperCache* aOwnerCache, + nsIScriptContext* aScriptContext, + nsPIDOMWindow* aOwner, IDBTransaction* aTransaction) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); @@ -86,9 +92,8 @@ IDBRequest::Create(nsISupports* aSource, request->mSource = aSource; request->mTransaction = aTransaction; - request->mScriptContext = aOwnerCache->GetScriptContext(); - request->mOwner = aOwnerCache->GetOwner(); - request->mScriptOwner = aOwnerCache->GetScriptOwner(); + request->mScriptContext = aScriptContext; + request->mOwner = aOwner; return request.forget(); } @@ -100,7 +105,9 @@ IDBRequest::Reset() mResultVal = JSVAL_VOID; mHaveResultOrErrorCode = false; mErrorCode = 0; - UnrootResultVal(); + if (mResultValRooted) { + UnrootResultVal(); + } } nsresult @@ -108,7 +115,7 @@ IDBRequest::NotifyHelperCompleted(HelperBase* aHelper) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); NS_ASSERTION(!mHaveResultOrErrorCode, "Already called!"); - NS_ASSERTION(!PreservingWrapper(), "Already rooted?!"); + NS_ASSERTION(!mResultValRooted, "Already rooted?!"); NS_ASSERTION(JSVAL_IS_VOID(mResultVal), "Should be undefined!"); // See if our window is still valid. If not then we're going to pretend that @@ -128,40 +135,45 @@ IDBRequest::NotifyHelperCompleted(HelperBase* aHelper) } // Otherwise we need to get the result from the helper. - JSContext* cx; - if (mScriptOwner) { + JSContext* cx = nsnull; + JSObject* obj = nsnull; + if (mScriptContext) { + cx = mScriptContext->GetNativeContext(); + NS_ASSERTION(cx, "Failed to get a context!"); + + obj = mScriptContext->GetNativeGlobal(); + NS_ASSERTION(obj, "Failed to get global object!"); + } + else { nsIThreadJSContextStack* cxStack = nsContentUtils::ThreadJSContextStack(); NS_ASSERTION(cxStack, "Failed to get thread context stack!"); - if (NS_FAILED(cxStack->GetSafeJSContext(&cx))) { - NS_WARNING("Failed to get safe JSContext!"); - rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - mErrorCode = NS_ERROR_GET_CODE(rv); - return rv; - } - } - else { - cx = mScriptContext->GetNativeContext(); - NS_ASSERTION(cx, "Failed to get a context!"); - } + NS_ENSURE_SUCCESS(cxStack->GetSafeJSContext(&cx), + NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); - JSObject* global = GetParentObject(); - NS_ASSERTION(global, "This should never be null!"); + obj = GetWrapper(); + NS_ENSURE_TRUE(obj, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR); + } JSAutoRequest ar(cx); JSAutoEnterCompartment ac; - if (ac.enter(cx, global)) { + if (!ac.enter(cx, obj)) { + rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; + } + else { RootResultVal(); rv = aHelper->GetSuccessResult(cx, &mResultVal); - if (NS_FAILED(rv)) { + if (NS_SUCCEEDED(rv)) { + // Unroot if we don't really need to be rooted. + if (!JSVAL_IS_GCTHING(mResultVal)) { + UnrootResultVal(); + } + } + else { NS_WARNING("GetSuccessResult failed!"); } } - else { - NS_WARNING("Failed to enter correct compartment!"); - rv = NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR; - } if (NS_SUCCEEDED(rv)) { mErrorCode = 0; @@ -177,47 +189,17 @@ IDBRequest::NotifyHelperCompleted(HelperBase* aHelper) void IDBRequest::RootResultVal() { - // We want to use the preserved wrapper to keep our result alive, but we don't - // always get a wrapper right away. Manually root if we don't have a wrapper. - if (!PreservingWrapper() && GetWrapperPreserveColor()) { - NS_ASSERTION(!mManuallyRooted, "Should not be manually rooted here!"); - nsContentUtils::PreserveWrapper(static_cast(this), - this); - } - else if (!mManuallyRooted) { - NS_HOLD_JS_OBJECTS(this, IDBRequest); - mManuallyRooted = true; - } + NS_ASSERTION(!mResultValRooted, "This should be false!"); + NS_HOLD_JS_OBJECTS(this, IDBRequest); + mResultValRooted = true; } void IDBRequest::UnrootResultVal() { - if (mManuallyRooted) { - NS_ASSERTION(!PreservingWrapper(), "Shouldn't have a wrapper here!"); - NS_DROP_JS_OBJECTS(this, IDBRequest); - mManuallyRooted = false; - } - else if (PreservingWrapper()) { - nsContentUtils::ReleaseWrapper(static_cast(this), this); - } -} - -void -IDBRequest::OnWrapperCreated() -{ - NS_ASSERTION(!PreservingWrapper(), - "Shouldn't have had a wrapper before now!"); - - // Update our rooting strategy to accommodate new wrapper if needed. We have - // to unroot the old way first. This is annoying because XPConnect uses a hash - // table to store these so we could be just fine adding an entry for a second - // time... However nsContentUtils keeps a counter that will get out of sync if - // we do. Safest to just remove the old holder and then add a new one. - if (mManuallyRooted) { - UnrootResultVal(); - RootResultVal(); - } + NS_ASSERTION(mResultValRooted, "This should be true!"); + NS_DROP_JS_OBJECTS(this, IDBRequest); + mResultValRooted = false; } NS_IMETHODIMP @@ -280,32 +262,66 @@ IDBRequest::GetErrorCode(PRUint16* aErrorCode) return NS_OK; } +NS_IMETHODIMP +IDBRequest::SetOnsuccess(nsIDOMEventListener* aSuccessListener) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return RemoveAddEventListener(NS_LITERAL_STRING(SUCCESS_EVT_STR), + mOnSuccessListener, aSuccessListener); +} + +NS_IMETHODIMP +IDBRequest::GetOnsuccess(nsIDOMEventListener** aSuccessListener) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return GetInnerEventListener(mOnSuccessListener, aSuccessListener); +} + +NS_IMETHODIMP +IDBRequest::SetOnerror(nsIDOMEventListener* aErrorListener) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return RemoveAddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR), + mOnErrorListener, aErrorListener); +} + +NS_IMETHODIMP +IDBRequest::GetOnerror(nsIDOMEventListener** aErrorListener) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return GetInnerEventListener(mOnErrorListener, aErrorListener); +} + NS_IMPL_CYCLE_COLLECTION_CLASS(IDBRequest) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) - // Don't need NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS because - // nsDOMEventTargetHelper does it for us. - NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(success) - NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBRequest, + nsDOMEventTargetWrapperCache) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnSuccessListener) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mSource) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mTransaction, nsPIDOMEventTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) - tmp->mResultVal = JSVAL_VOID; - if (tmp->mManuallyRooted) { +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBRequest, + nsDOMEventTargetWrapperCache) + if (tmp->mResultValRooted) { + tmp->mResultVal = JSVAL_VOID; tmp->UnrootResultVal(); } - NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(success) - NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnSuccessListener) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mSource) NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTransaction) NS_IMPL_CYCLE_COLLECTION_UNLINK_END -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, IDBWrapperCache) - // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because - // nsDOMEventTargetHelper does it for us. +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBRequest, + nsDOMEventTargetWrapperCache) if (JSVAL_IS_GCTHING(tmp->mResultVal)) { void *gcThing = JSVAL_TO_GCTHING(tmp->mResultVal); NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(gcThing, "mResultVal") @@ -315,16 +331,13 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBRequest) NS_INTERFACE_MAP_ENTRY(nsIIDBRequest) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBRequest) -NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) +NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetWrapperCache) -NS_IMPL_ADDREF_INHERITED(IDBRequest, IDBWrapperCache) -NS_IMPL_RELEASE_INHERITED(IDBRequest, IDBWrapperCache) +NS_IMPL_ADDREF_INHERITED(IDBRequest, nsDOMEventTargetWrapperCache) +NS_IMPL_RELEASE_INHERITED(IDBRequest, nsDOMEventTargetWrapperCache) DOMCI_DATA(IDBRequest, IDBRequest) -NS_IMPL_EVENT_HANDLER(IDBRequest, success); -NS_IMPL_EVENT_HANDLER(IDBRequest, error); - nsresult IDBRequest::PreHandleEvent(nsEventChainPreVisitor& aVisitor) { @@ -339,21 +352,21 @@ IDBOpenDBRequest::~IDBOpenDBRequest() { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); - UnrootResultVal(); + if (mResultValRooted) { + UnrootResultVal(); + } } // static already_AddRefed IDBOpenDBRequest::Create(nsIScriptContext* aScriptContext, - nsPIDOMWindow* aOwner, - JSObject* aScriptOwner) + nsPIDOMWindow* aOwner) { NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); nsRefPtr request(new IDBOpenDBRequest()); request->mScriptContext = aScriptContext; request->mOwner = aOwner; - request->mScriptOwner = aScriptOwner; return request.forget(); } @@ -364,18 +377,37 @@ IDBOpenDBRequest::SetTransaction(IDBTransaction* aTransaction) mTransaction = aTransaction; } +void +IDBOpenDBRequest::RootResultVal() +{ + NS_ASSERTION(!mResultValRooted, "This should be false!"); + NS_HOLD_JS_OBJECTS(this, IDBOpenDBRequest); + mResultValRooted = true; +} + +void +IDBOpenDBRequest::UnrootResultVal() +{ + NS_ASSERTION(mResultValRooted, "This should be true!"); + NS_DROP_JS_OBJECTS(this, IDBOpenDBRequest); + mResultValRooted = false; +} + +NS_IMPL_EVENT_HANDLER(IDBOpenDBRequest, blocked) +NS_IMPL_EVENT_HANDLER(IDBOpenDBRequest, upgradeneeded) + NS_IMPL_CYCLE_COLLECTION_CLASS(IDBOpenDBRequest) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBOpenDBRequest, IDBRequest) - NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(upgradeneeded) - NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(blocked) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnupgradeneededListener) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnblockedListener) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBOpenDBRequest, IDBRequest) - NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(upgradeneeded) - NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(blocked) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnupgradeneededListener) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnblockedListener) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBOpenDBRequest) @@ -387,6 +419,3 @@ NS_IMPL_ADDREF_INHERITED(IDBOpenDBRequest, IDBRequest) NS_IMPL_RELEASE_INHERITED(IDBOpenDBRequest, IDBRequest) DOMCI_DATA(IDBOpenDBRequest, IDBOpenDBRequest) - -NS_IMPL_EVENT_HANDLER(IDBOpenDBRequest, blocked); -NS_IMPL_EVENT_HANDLER(IDBOpenDBRequest, upgradeneeded); diff --git a/dom/indexedDB/IDBRequest.h b/dom/indexedDB/IDBRequest.h index 3d5d960c5640..9ab2fbaa4a19 100644 --- a/dom/indexedDB/IDBRequest.h +++ b/dom/indexedDB/IDBRequest.h @@ -46,7 +46,8 @@ #include "nsIIDBRequest.h" #include "nsIIDBOpenDBRequest.h" -#include "mozilla/dom/indexedDB/IDBWrapperCache.h" +#include "nsDOMEventTargetWrapperCache.h" +#include "nsCycleCollectionParticipant.h" class nsIScriptContext; class nsPIDOMWindow; @@ -56,18 +57,19 @@ BEGIN_INDEXEDDB_NAMESPACE class HelperBase; class IDBTransaction; -class IDBRequest : public IDBWrapperCache, +class IDBRequest : public nsDOMEventTargetWrapperCache, public nsIIDBRequest { public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_NSIIDBREQUEST NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(IDBRequest, - IDBWrapperCache) + nsDOMEventTargetWrapperCache) static already_AddRefed Create(nsISupports* aSource, - IDBWrapperCache* aOwnerCache, + nsIScriptContext* aScriptContext, + nsPIDOMWindow* aOwner, IDBTransaction* aTransaction); // nsIDOMEventTarget @@ -90,10 +92,18 @@ public: mErrorCode = rv; } - void RootResultVal(); - void UnrootResultVal(); + nsIScriptContext* ScriptContext() + { + return mScriptContext; + } - virtual void OnWrapperCreated(); + nsPIDOMWindow* Owner() + { + return mOwner; + } + + virtual void RootResultVal(); + virtual void UnrootResultVal(); protected: IDBRequest(); @@ -102,14 +112,14 @@ protected: nsCOMPtr mSource; nsRefPtr mTransaction; - NS_DECL_EVENT_HANDLER(success); - NS_DECL_EVENT_HANDLER(error); + nsRefPtr mOnSuccessListener; + nsRefPtr mOnErrorListener; jsval mResultVal; PRUint16 mErrorCode; + bool mResultValRooted; bool mHaveResultOrErrorCode; - bool mManuallyRooted; }; class IDBOpenDBRequest : public IDBRequest, @@ -119,30 +129,24 @@ public: NS_DECL_ISUPPORTS_INHERITED NS_FORWARD_NSIIDBREQUEST(IDBRequest::) NS_DECL_NSIIDBOPENDBREQUEST - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBOpenDBRequest, IDBRequest) + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBOpenDBRequest, + IDBRequest) static already_AddRefed Create(nsIScriptContext* aScriptContext, - nsPIDOMWindow* aOwner, - JSObject* aScriptOwner); - - static - already_AddRefed - Create(IDBWrapperCache* aOwnerCache) - { - return Create(aOwnerCache->GetScriptContext(), aOwnerCache->GetOwner(), - aOwnerCache->GetScriptOwner()); - } + nsPIDOMWindow* aOwner); void SetTransaction(IDBTransaction* aTransaction); + virtual void RootResultVal(); + virtual void UnrootResultVal(); + protected: ~IDBOpenDBRequest(); - // Only touched on the main thread. - NS_DECL_EVENT_HANDLER(blocked); - NS_DECL_EVENT_HANDLER(upgradeneeded); + nsRefPtr mOnblockedListener; + nsRefPtr mOnupgradeneededListener; }; END_INDEXEDDB_NAMESPACE diff --git a/dom/indexedDB/IDBTransaction.cpp b/dom/indexedDB/IDBTransaction.cpp index 7f506a3b6237..ce3a0b5d17f1 100644 --- a/dom/indexedDB/IDBTransaction.cpp +++ b/dom/indexedDB/IDBTransaction.cpp @@ -42,7 +42,6 @@ #include "nsIScriptContext.h" #include "mozilla/storage.h" -#include "nsContentUtils.h" #include "nsDOMClassInfoID.h" #include "nsDOMLists.h" #include "nsEventDispatcher.h" @@ -117,9 +116,8 @@ IDBTransaction::Create(IDBDatabase* aDatabase, nsRefPtr transaction = new IDBTransaction(); - transaction->mScriptContext = aDatabase->GetScriptContext(); - transaction->mOwner = aDatabase->GetOwner(); - transaction->mScriptOwner = aDatabase->GetScriptOwner(); + transaction->mScriptContext = aDatabase->ScriptContext(); + transaction->mOwner = aDatabase->Owner(); transaction->mDatabase = aDatabase; transaction->mMode = aMode; @@ -186,8 +184,6 @@ IDBTransaction::~IDBTransaction() NS_ASSERTION(!mConnection, "Should have called CommitOrRollback!"); NS_ASSERTION(!mCreating, "Should have been cleared already!"); NS_ASSERTION(mFiredCompleteOrAbort, "Should have fired event!"); - - nsContentUtils::ReleaseWrapper(static_cast(this), this); } void @@ -478,25 +474,27 @@ IDBTransaction::ClearCreatedFileInfos() NS_IMPL_CYCLE_COLLECTION_CLASS(IDBTransaction) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBTransaction, - IDBWrapperCache) + nsDOMEventTargetHelper) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR_AMBIGUOUS(mDatabase, nsIDOMEventTarget) - NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(error) - NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(complete) - NS_CYCLE_COLLECTION_TRAVERSE_EVENT_HANDLER(abort) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnErrorListener) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnCompleteListener) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mOnAbortListener) for (PRUint32 i = 0; i < tmp->mCreatedObjectStores.Length(); i++) { NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mCreatedObjectStores[i]"); cb.NoteXPCOMChild(static_cast( tmp->mCreatedObjectStores[i].get())); } + NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction, IDBWrapperCache) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBTransaction, + nsDOMEventTargetHelper) // Don't unlink mDatabase! - NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(error) - NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(complete) - NS_CYCLE_COLLECTION_UNLINK_EVENT_HANDLER(abort) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnErrorListener) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnCompleteListener) + NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mOnAbortListener) tmp->mCreatedObjectStores.Clear(); @@ -506,17 +504,13 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBTransaction) NS_INTERFACE_MAP_ENTRY(nsIIDBTransaction) NS_INTERFACE_MAP_ENTRY(nsIThreadObserver) NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(IDBTransaction) -NS_INTERFACE_MAP_END_INHERITING(IDBWrapperCache) +NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetHelper) -NS_IMPL_ADDREF_INHERITED(IDBTransaction, IDBWrapperCache) -NS_IMPL_RELEASE_INHERITED(IDBTransaction, IDBWrapperCache) +NS_IMPL_ADDREF_INHERITED(IDBTransaction, nsDOMEventTargetHelper) +NS_IMPL_RELEASE_INHERITED(IDBTransaction, nsDOMEventTargetHelper) DOMCI_DATA(IDBTransaction, IDBTransaction) -NS_IMPL_EVENT_HANDLER(IDBTransaction, error); -NS_IMPL_EVENT_HANDLER(IDBTransaction, complete); -NS_IMPL_EVENT_HANDLER(IDBTransaction, abort); - NS_IMETHODIMP IDBTransaction::GetDb(nsIIDBDatabase** aDB) { @@ -632,6 +626,53 @@ IDBTransaction::Abort() return NS_OK; } +NS_IMETHODIMP +IDBTransaction::SetOnerror(nsIDOMEventListener* aErrorListener) +{ + return RemoveAddEventListener(NS_LITERAL_STRING(ERROR_EVT_STR), + mOnErrorListener, aErrorListener); +} + +NS_IMETHODIMP +IDBTransaction::GetOnerror(nsIDOMEventListener** aErrorListener) +{ + return GetInnerEventListener(mOnErrorListener, aErrorListener); +} + +NS_IMETHODIMP +IDBTransaction::GetOncomplete(nsIDOMEventListener** aOncomplete) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return GetInnerEventListener(mOnCompleteListener, aOncomplete); +} + +NS_IMETHODIMP +IDBTransaction::SetOncomplete(nsIDOMEventListener* aOncomplete) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return RemoveAddEventListener(NS_LITERAL_STRING(COMPLETE_EVT_STR), + mOnCompleteListener, aOncomplete); +} + +NS_IMETHODIMP +IDBTransaction::GetOnabort(nsIDOMEventListener** aOnabort) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return GetInnerEventListener(mOnAbortListener, aOnabort); +} + +NS_IMETHODIMP +IDBTransaction::SetOnabort(nsIDOMEventListener* aOnabort) +{ + NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); + + return RemoveAddEventListener(NS_LITERAL_STRING(ABORT_EVT_STR), + mOnAbortListener, aOnabort); +} + nsresult IDBTransaction::PreHandleEvent(nsEventChainPreVisitor& aVisitor) { @@ -776,7 +817,7 @@ CommitHelper::Run() } if (mConnection) { - IndexedDatabaseManager::SetCurrentWindow(database->GetOwner()); + IndexedDatabaseManager::SetCurrentWindow(database->Owner()); if (!mAborted && mUpdateFileRefcountFunction && NS_FAILED(mUpdateFileRefcountFunction->UpdateDatabase(mConnection))) { diff --git a/dom/indexedDB/IDBTransaction.h b/dom/indexedDB/IDBTransaction.h index f05f8b308887..4d6cdfff7250 100644 --- a/dom/indexedDB/IDBTransaction.h +++ b/dom/indexedDB/IDBTransaction.h @@ -41,6 +41,8 @@ #define mozilla_dom_indexeddb_idbtransaction_h__ #include "mozilla/dom/indexedDB/IndexedDatabase.h" +#include "mozilla/dom/indexedDB/IDBDatabase.h" +#include "mozilla/dom/indexedDB/FileInfo.h" #include "mozIStorageConnection.h" #include "mozIStorageStatement.h" @@ -49,15 +51,14 @@ #include "nsIRunnable.h" #include "nsIThreadInternal.h" +#include "nsDOMEventTargetHelper.h" +#include "nsCycleCollectionParticipant.h" + #include "nsAutoPtr.h" #include "nsClassHashtable.h" #include "nsHashKeys.h" #include "nsInterfaceHashtable.h" -#include "mozilla/dom/indexedDB/IDBDatabase.h" -#include "mozilla/dom/indexedDB/IDBWrapperCache.h" -#include "mozilla/dom/indexedDB/FileInfo.h" - class nsIThread; BEGIN_INDEXEDDB_NAMESPACE @@ -77,7 +78,7 @@ public: virtual nsresult NotifyTransactionComplete(IDBTransaction* aTransaction) = 0; }; -class IDBTransaction : public IDBWrapperCache, +class IDBTransaction : public nsDOMEventTargetHelper, public nsIIDBTransaction, public nsIThreadObserver { @@ -91,7 +92,8 @@ public: NS_DECL_NSIIDBTRANSACTION NS_DECL_NSITHREADOBSERVER - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBTransaction, IDBWrapperCache) + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(IDBTransaction, + nsDOMEventTargetHelper) static already_AddRefed Create(IDBDatabase* aDatabase, @@ -178,9 +180,9 @@ private: PRUint32 mCreatedRecursionDepth; // Only touched on the main thread. - NS_DECL_EVENT_HANDLER(error); - NS_DECL_EVENT_HANDLER(complete); - NS_DECL_EVENT_HANDLER(abort); + nsRefPtr mOnErrorListener; + nsRefPtr mOnCompleteListener; + nsRefPtr mOnAbortListener; nsInterfaceHashtable mCachedStatements; diff --git a/dom/indexedDB/IDBWrapperCache.cpp b/dom/indexedDB/IDBWrapperCache.cpp deleted file mode 100644 index c09482fd7c30..000000000000 --- a/dom/indexedDB/IDBWrapperCache.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "IDBWrapperCache.h" - -USING_INDEXEDDB_NAMESPACE - -NS_IMPL_CYCLE_COLLECTION_CLASS(IDBWrapperCache) - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(IDBWrapperCache, - nsDOMEventTargetWrapperCache) - // Don't need NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS because - // nsDOMEventTargetHelper does it for us. -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(IDBWrapperCache, - nsDOMEventTargetWrapperCache) - tmp->mScriptOwner = nsnull; -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(IDBWrapperCache, - nsDOMEventTargetWrapperCache) - // Don't need NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER because - // nsDOMEventTargetHelper does it for us. - if (tmp->mScriptOwner) { - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_CALLBACK(tmp->mScriptOwner, - "mScriptOwner") - } -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(IDBWrapperCache) -NS_INTERFACE_MAP_END_INHERITING(nsDOMEventTargetWrapperCache) - -NS_IMPL_ADDREF_INHERITED(IDBWrapperCache, nsDOMEventTargetWrapperCache) -NS_IMPL_RELEASE_INHERITED(IDBWrapperCache, nsDOMEventTargetWrapperCache) diff --git a/dom/indexedDB/IDBWrapperCache.h b/dom/indexedDB/IDBWrapperCache.h deleted file mode 100644 index 8d4fdb55b0a5..000000000000 --- a/dom/indexedDB/IDBWrapperCache.h +++ /dev/null @@ -1,74 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef mozilla_dom_indexeddb_idbwrappercache_h__ -#define mozilla_dom_indexeddb_idbwrappercache_h__ - -#include "mozilla/dom/indexedDB/IndexedDatabase.h" - -#include "nsDOMEventTargetWrapperCache.h" - -BEGIN_INDEXEDDB_NAMESPACE - -class IDBWrapperCache : public nsDOMEventTargetWrapperCache -{ -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED( - IDBWrapperCache, - nsDOMEventTargetWrapperCache) - - JSObject* GetScriptOwner() const - { - return mScriptOwner; - } - - nsIScriptContext* GetScriptContext() const - { - return mScriptContext; - } - - nsPIDOMWindow* GetOwner() const - { - return mOwner; - } - - JSObject* GetParentObject() - { - if (mScriptOwner) { - return mScriptOwner; - } - - // Do what nsEventTargetSH::PreCreate does. - nsCOMPtr parent; - nsDOMEventTargetWrapperCache::GetParentObject(getter_AddRefs(parent)); - - return parent ? parent->GetGlobalJSObject() : nsnull; - } - - virtual void OnWrapperCreated() - { } - - static IDBWrapperCache* FromSupports(nsISupports* aSupports) - { - return static_cast( - nsDOMEventTargetWrapperCache::FromSupports(aSupports)); - } - -protected: - IDBWrapperCache() - : mScriptOwner(nsnull) - { } - - virtual ~IDBWrapperCache() - { } - - JSObject* mScriptOwner; -}; - -END_INDEXEDDB_NAMESPACE - -#endif // mozilla_dom_indexeddb_idbwrappercache_h__ diff --git a/dom/indexedDB/IndexedDatabaseManager.cpp b/dom/indexedDB/IndexedDatabaseManager.cpp index cb9034f720e3..1f43d3bc73b6 100644 --- a/dom/indexedDB/IndexedDatabaseManager.cpp +++ b/dom/indexedDB/IndexedDatabaseManager.cpp @@ -505,7 +505,7 @@ IndexedDatabaseManager::AbortCloseDatabasesForWindow(nsPIDOMWindow* aWindow) for (PRUint32 index = 0; index < liveDatabases.Length(); index++) { IDBDatabase*& database = liveDatabases[index]; - if (database->GetOwner() == aWindow) { + if (database->Owner() == aWindow) { if (NS_FAILED(database->Close())) { NS_WARNING("Failed to close database for dying window!"); } @@ -533,7 +533,7 @@ IndexedDatabaseManager::HasOpenTransactions(nsPIDOMWindow* aWindow) for (PRUint32 index = 0; index < liveDatabases.Length(); index++) { IDBDatabase*& database = liveDatabases[index]; - if (database->GetOwner() == aWindow && + if (database->Owner() == aWindow && pool->HasTransactionsForDatabase(database)) { return true; } @@ -1591,32 +1591,32 @@ NS_IMETHODIMP IndexedDatabaseManager::InitWindowless(const jsval& aObj, JSContext* aCx) { NS_ENSURE_TRUE(nsContentUtils::IsCallerChrome(), NS_ERROR_NOT_AVAILABLE); - NS_ENSURE_ARG(!JSVAL_IS_PRIMITIVE(aObj)); - + // Instantiating this class will register exception providers so even - // in xpcshell we will get typed (dom) exceptions, instead of general - // exceptions. + // in xpcshell we will get typed (dom) exceptions, instead of general exceptions. nsCOMPtr sof(do_GetService(kDOMSOF_CID)); + // Defining IDBKeyrange static functions on the global. + if (JSVAL_IS_PRIMITIVE(aObj)) { + return NS_ERROR_INVALID_ARG; + } + + nsCOMPtr factory = IDBFactory::Create(nsnull); + NS_ASSERTION(factory, "IDBFactory should not be null."); + JSObject* obj = JSVAL_TO_OBJECT(aObj); - - JSObject* global = JS_GetGlobalForObject(aCx, obj); - - nsCOMPtr factory = IDBFactory::Create(aCx, global); - NS_ENSURE_TRUE(factory, NS_ERROR_FAILURE); - jsval mozIndexedDBVal; nsresult rv = nsContentUtils::WrapNative(aCx, obj, factory, &mozIndexedDBVal); NS_ENSURE_SUCCESS(rv, rv); - if (!JS_DefineProperty(aCx, obj, "mozIndexedDB", mozIndexedDBVal, nsnull, - nsnull, JSPROP_ENUMERATE)) { + if (!JS_DefineProperty(aCx, obj, "mozIndexedDB", mozIndexedDBVal, + nsnull, nsnull, JSPROP_ENUMERATE)) { return NS_ERROR_FAILURE; } JSObject* keyrangeObj = JS_NewObject(aCx, nsnull, nsnull, nsnull); NS_ENSURE_TRUE(keyrangeObj, NS_ERROR_OUT_OF_MEMORY); - + if (!IDBKeyRange::DefineConstructors(aCx, keyrangeObj)) { return NS_ERROR_FAILURE; } diff --git a/dom/indexedDB/Makefile.in b/dom/indexedDB/Makefile.in index 2bf85c2db826..36b7e0902198 100644 --- a/dom/indexedDB/Makefile.in +++ b/dom/indexedDB/Makefile.in @@ -61,17 +61,16 @@ CPPSRCS = \ IDBCursor.cpp \ IDBDatabase.cpp \ IDBEvents.cpp \ - IDBFactory.cpp \ IDBIndex.cpp \ IDBKeyRange.cpp \ IDBObjectStore.cpp \ IDBRequest.cpp \ IDBTransaction.cpp \ - IDBWrapperCache.cpp \ + IDBFactory.cpp \ IndexedDatabaseManager.cpp \ - Key.cpp \ OpenDatabaseHelper.cpp \ TransactionThreadPool.cpp \ + Key.cpp \ $(NULL) EXPORTS_mozilla/dom/indexedDB = \ @@ -83,7 +82,6 @@ EXPORTS_mozilla/dom/indexedDB = \ IDBObjectStore.h \ IDBRequest.h \ IDBTransaction.h \ - IDBWrapperCache.h \ IndexedDatabase.h \ IndexedDatabaseManager.h \ IDBFactory.h \ diff --git a/dom/indexedDB/OpenDatabaseHelper.cpp b/dom/indexedDB/OpenDatabaseHelper.cpp index 4a145575478f..ab3e86a3ede3 100644 --- a/dom/indexedDB/OpenDatabaseHelper.cpp +++ b/dom/indexedDB/OpenDatabaseHelper.cpp @@ -1611,9 +1611,7 @@ OpenDatabaseHelper::DoDatabaseWork() NS_ASSERTION(mOpenDBRequest, "This should never be null!"); - // This will be null for non-window contexts. - nsPIDOMWindow* window = mOpenDBRequest->GetOwner(); - + nsPIDOMWindow* window = mOpenDBRequest->Owner(); AutoEnterWindow autoWindow(window); nsCOMPtr dbDirectory; @@ -2113,7 +2111,8 @@ OpenDatabaseHelper::EnsureSuccessResult() dbInfo->nextIndexId = mLastIndexId + 1; nsRefPtr database = - IDBDatabase::Create(mOpenDBRequest, + IDBDatabase::Create(mOpenDBRequest->ScriptContext(), + mOpenDBRequest->Owner(), dbInfo.forget(), mASCIIOrigin, mFileManager); From 26a57951095829c93bf27b70b83773cec69d0d3b Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Mon, 23 Jan 2012 16:58:52 +0000 Subject: [PATCH 06/58] Bug 719408 - Remove broken paths from Makefile.in files in layout. r=bernd.mielke. --- layout/build/Makefile.in | 2 -- layout/mathml/Makefile.in | 1 - 2 files changed, 3 deletions(-) diff --git a/layout/build/Makefile.in b/layout/build/Makefile.in index dc7a7a5c91f0..3801e21e39b1 100644 --- a/layout/build/Makefile.in +++ b/layout/build/Makefile.in @@ -235,14 +235,12 @@ LOCAL_INCLUDES += -I$(srcdir)/../base \ -I$(srcdir)/../forms \ -I$(srcdir)/../tables \ -I$(srcdir)/../style \ - -I$(srcdir)/../xul/content/src \ -I$(srcdir)/../xul/base/src \ -I$(srcdir)/../mathml \ -I$(topsrcdir)/content/base/src \ -I$(topsrcdir)/content/canvas/src \ -I$(topsrcdir)/content/html/content/src \ -I$(topsrcdir)/content/html/document/src \ - -I$(topsrcdir)/content/html/style/src \ -I$(topsrcdir)/content/xslt/src/base \ -I$(topsrcdir)/content/xslt/src/xml \ -I$(topsrcdir)/content/xslt/src/xpath \ diff --git a/layout/mathml/Makefile.in b/layout/mathml/Makefile.in index e12af85b58d7..9ef45fea178f 100644 --- a/layout/mathml/Makefile.in +++ b/layout/mathml/Makefile.in @@ -54,7 +54,6 @@ LOCAL_INCLUDES = \ -I$(srcdir)/../base \ -I$(srcdir)/../generic \ -I$(srcdir)/../tables \ - -I$(srcdir)/content/src \ -I$(topsrcdir)/content/base/src \ -I$(topsrcdir)/content/mathml/content/src \ -I$(srcdir)/../xul/base/src \ From 05d3f21653395ab40aed61cedd14e8530ed37fc0 Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Mon, 23 Jan 2012 17:42:32 +0000 Subject: [PATCH 07/58] Bug 647914 - Horizontal and vertical SVG paths are omitted from bbox calculations if they have siblings. r=jwatt --- content/svg/content/test/bbox-helper.svg | 8 ++++++++ content/svg/content/test/test_bbox.xhtml | 2 ++ layout/svg/base/src/nsSVGContainerFrame.cpp | 15 ++++++++++++--- layout/svg/base/src/nsSVGMarkerFrame.cpp | 17 ++++++++++++++--- 4 files changed, 36 insertions(+), 6 deletions(-) diff --git a/content/svg/content/test/bbox-helper.svg b/content/svg/content/test/bbox-helper.svg index b8378d0f8141..8c27c8ab1c16 100644 --- a/content/svg/content/test/bbox-helper.svg +++ b/content/svg/content/test/bbox-helper.svg @@ -6,4 +6,12 @@ b a y + + + + + + + + diff --git a/content/svg/content/test/test_bbox.xhtml b/content/svg/content/test/test_bbox.xhtml index cf5dc92a55bd..aaeee14816f9 100644 --- a/content/svg/content/test/test_bbox.xhtml +++ b/content/svg/content/test/test_bbox.xhtml @@ -42,6 +42,8 @@ function run() checkBBox("f", 0, 0, 100, 100); checkBBoxHeight("a", "b"); checkBBoxHeight("a", "y"); + checkBBox("v", 95, 45, 10, 155); + checkBBox("h", 195, 45, 105, 55); SimpleTest.finish(); } diff --git a/layout/svg/base/src/nsSVGContainerFrame.cpp b/layout/svg/base/src/nsSVGContainerFrame.cpp index cce065866002..547e2f66de95 100644 --- a/layout/svg/base/src/nsSVGContainerFrame.cpp +++ b/layout/svg/base/src/nsSVGContainerFrame.cpp @@ -265,7 +265,8 @@ nsSVGDisplayContainerFrame::GetBBoxContribution( const gfxMatrix &aToBBoxUserspace, PRUint32 aFlags) { - gfxRect bboxUnion(0.0, 0.0, 0.0, 0.0); + gfxRect bboxUnion; + bool firstChild = true; nsIFrame* kid = mFrames.FirstChild(); while (kid) { @@ -277,8 +278,16 @@ nsSVGDisplayContainerFrame::GetBBoxContribution( transform = static_cast(content)-> PrependLocalTransformTo(aToBBoxUserspace); } - bboxUnion = - bboxUnion.Union(svgKid->GetBBoxContribution(transform, aFlags)); + // We need to include zero width/height vertical/horizontal lines, so we have + // to use UnionEdges, but we must special case the first bbox so that we don't + // include the initial gfxRect(0,0,0,0). + gfxRect childBBox = svgKid->GetBBoxContribution(transform, aFlags); + if (firstChild) { + bboxUnion = childBBox; + firstChild = false; + continue; + } + bboxUnion = bboxUnion.UnionEdges(childBBox); } kid = kid->GetNextSibling(); } diff --git a/layout/svg/base/src/nsSVGMarkerFrame.cpp b/layout/svg/base/src/nsSVGMarkerFrame.cpp index 9235963467d7..df9dd60db4f3 100644 --- a/layout/svg/base/src/nsSVGMarkerFrame.cpp +++ b/layout/svg/base/src/nsSVGMarkerFrame.cpp @@ -203,14 +203,15 @@ nsSVGMarkerFrame::GetMarkBBoxContribution(const gfxMatrix &aToBBoxUserspace, mY = aMark->y; mAutoAngle = aMark->angle; - gfxRect bbox; - gfxMatrix markerTM = content->GetMarkerTransform(mStrokeWidth, mX, mY, mAutoAngle); gfxMatrix viewBoxTM = content->GetViewBoxTransform(); gfxMatrix tm = viewBoxTM * markerTM * aToBBoxUserspace; + gfxRect bbox; + bool firstChild = true; + for (nsIFrame* kid = mFrames.FirstChild(); kid; kid = kid->GetNextSibling()) { @@ -219,7 +220,17 @@ nsSVGMarkerFrame::GetMarkBBoxContribution(const gfxMatrix &aToBBoxUserspace, // When we're being called to obtain the invalidation area, we need to // pass down all the flags so that stroke is included. However, once DOM // getBBox() accepts flags, maybe we should strip some of those here? - bbox.UnionRect(bbox, child->GetBBoxContribution(tm, aFlags)); + + // We need to include zero width/height vertical/horizontal lines, so we have + // to use UnionEdges, but we must special case the first bbox so that we don't + // include the initial gfxRect(0,0,0,0). + gfxRect childBBox = child->GetBBoxContribution(tm, aFlags); + if (firstChild) { + bbox = childBBox; + firstChild = false; + continue; + } + bbox = bbox.UnionEdges(childBBox); } } From 0ceea254d2281148c13e6ca0d2455a7a29b2b118 Mon Sep 17 00:00:00 2001 From: David Bolter Date: Mon, 23 Jan 2012 13:24:13 -0500 Subject: [PATCH 08/58] Bug 717505 - Crash [@ nsAccessible::VisibilityState] when closing a tab. r=roc --- accessible/src/base/nsAccessible.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/accessible/src/base/nsAccessible.cpp b/accessible/src/base/nsAccessible.cpp index 08d455097a1e..b8d3316bc970 100644 --- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -616,6 +616,9 @@ nsAccessible::VisibilityState() } while (accessible = accessible->Parent()); nsIFrame* frame = GetFrame(); + if (!frame) + return vstates; + const nsCOMPtr shell(GetPresShell()); // We need to know if at least a kMinPixels around the object is visible, From fc39f2dd0a1f634d125b941e1f0e7280e66d86ae Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 23 Jan 2012 13:28:55 -0500 Subject: [PATCH 09/58] Bug 649564 - Make kill never take down the test, and add a missing callback. rs=ted --- netwerk/test/unit/test_socks.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/netwerk/test/unit/test_socks.js b/netwerk/test/unit/test_socks.js index bbaeffa443c8..ce0b9a6c1e2d 100644 --- a/netwerk/test/unit/test_socks.js +++ b/netwerk/test/unit/test_socks.js @@ -439,11 +439,21 @@ SocksTestServer.prototype = { var client = new SocksClient(this, input, output); this.client_connections.push(client); }, + + onStopListening: function(socket) + { + }, close: function() { if (this.client_subprocess) { - this.client_subprocess.kill(); + try { + this.client_subprocess.kill(); + } catch (x) { + do_print('Killing subprocess failed: ' + e); + if (x.stack) + _dump_exception_stack(x.stack); + } this.client_subprocess = null; } for each (var client in this.client_connections) From e715f2d2f24ba06486cb72ac98d17f3ec4be526e Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Tue, 13 Dec 2011 12:03:30 -0500 Subject: [PATCH 10/58] Bug 707320 - provide reflection for separate Histograms and SampleSets; r=taras --- toolkit/components/telemetry/Telemetry.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index 72633f1c6ff9..749b2a7a09f6 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -227,11 +227,9 @@ enum reflectStatus { }; enum reflectStatus -ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h) +ReflectHistogramAndSamples(JSContext *cx, JSObject *obj, Histogram *h, + const Histogram::SampleSet &ss) { - Histogram::SampleSet ss; - h->SnapshotSample(&ss); - // We don't want to reflect corrupt histograms. if (h->FindCorruption(ss) != Histogram::NO_INCONSISTENCIES) { return REFLECT_CORRUPT; @@ -260,6 +258,14 @@ ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h) return REFLECT_OK; } +enum reflectStatus +ReflectHistogramSnapshot(JSContext *cx, JSObject *obj, Histogram *h) +{ + Histogram::SampleSet ss; + h->SnapshotSample(&ss); + return ReflectHistogramAndSamples(cx, obj, h, ss); +} + JSBool JSHistogram_Add(JSContext *cx, uintN argc, jsval *vp) { From 9230c00ae78fa0bdea61e0a92d135564d086f2f0 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Fri, 9 Dec 2011 15:15:53 -0500 Subject: [PATCH 11/58] Bug 707320 - provide interface for loading and saving histograms; r=taras --- toolkit/components/telemetry/Telemetry.cpp | 331 ++++++++++++++++++ toolkit/components/telemetry/nsITelemetry.idl | 61 +++- 2 files changed, 391 insertions(+), 1 deletion(-) diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index 749b2a7a09f6..8c82f93b6c9f 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -38,6 +38,7 @@ * ***** END LICENSE BLOCK ***** */ #include "base/histogram.h" +#include "base/pickle.h" #include "nsIComponentManager.h" #include "nsIServiceManager.h" #include "nsCOMPtr.h" @@ -47,6 +48,8 @@ #include "jsapi.h" #include "nsStringGlue.h" #include "nsITelemetry.h" +#include "nsIFile.h" +#include "nsILocalFile.h" #include "Telemetry.h" #include "nsTHashtable.h" #include "nsHashKeys.h" @@ -54,6 +57,7 @@ #include "nsXULAppAPI.h" #include "nsThreadUtils.h" #include "mozilla/Mutex.h" +#include "mozilla/FileUtils.h" namespace { @@ -683,6 +687,333 @@ TelemetryImpl::GetHistogramById(const nsACString &name, JSContext *cx, jsval *re return WrapAndReturnHistogram(h, cx, ret); } +class TelemetrySessionData : public nsITelemetrySessionData +{ + NS_DECL_ISUPPORTS + NS_DECL_NSITELEMETRYSESSIONDATA + +public: + static nsresult LoadFromDisk(nsIFile *, TelemetrySessionData **ptr); + static nsresult SaveToDisk(nsIFile *, const nsACString &uuid); + + TelemetrySessionData(const char *uuid); + ~TelemetrySessionData(); + +private: + struct EnumeratorArgs { + JSContext *cx; + JSObject *snapshots; + }; + typedef nsBaseHashtableET EntryType; + typedef nsTHashtable SessionMapType; + static PLDHashOperator ReflectSamples(EntryType *entry, void *arg); + SessionMapType mSampleSetMap; + nsCString mUUID; + + bool DeserializeHistogramData(Pickle &pickle, void **iter); + static bool SerializeHistogramData(Pickle &pickle); + + // The file format version. Should be incremented whenever we change + // how individual SampleSets are stored in the file. + static const unsigned int sVersion = 1; +}; + +NS_IMPL_THREADSAFE_ISUPPORTS1(TelemetrySessionData, nsITelemetrySessionData) + +TelemetrySessionData::TelemetrySessionData(const char *uuid) + : mUUID(uuid) +{ + mSampleSetMap.Init(); +} + +TelemetrySessionData::~TelemetrySessionData() +{ + mSampleSetMap.Clear(); +} + +NS_IMETHODIMP +TelemetrySessionData::GetUuid(nsACString &uuid) +{ + uuid = mUUID; + return NS_OK; +} + +PLDHashOperator +TelemetrySessionData::ReflectSamples(EntryType *entry, void *arg) +{ + struct EnumeratorArgs *args = static_cast(arg); + // This has the undesirable effect of creating a histogram for the + // current session with the given ID. But there's no good way to + // compute the ranges and buckets from scratch. + Histogram *h = nsnull; + nsresult rv = GetHistogramByEnumId(Telemetry::ID(entry->GetKey()), &h); + if (NS_FAILED(rv)) { + return PL_DHASH_STOP; + } + + // Don't reflect histograms with no data associated with them. + if (entry->mData.sum() == 0) { + return PL_DHASH_NEXT; + } + + JSObject *snapshot = JS_NewObject(args->cx, NULL, NULL, NULL); + if (!(snapshot + && ReflectHistogramAndSamples(args->cx, snapshot, h, entry->mData) + && JS_DefineProperty(args->cx, args->snapshots, + h->histogram_name().c_str(), + OBJECT_TO_JSVAL(snapshot), NULL, NULL, + JSPROP_ENUMERATE))) { + return PL_DHASH_STOP; + } + + return PL_DHASH_NEXT; +} + +NS_IMETHODIMP +TelemetrySessionData::GetSnapshots(JSContext *cx, jsval *ret) +{ + JSObject *snapshots = JS_NewObject(cx, NULL, NULL, NULL); + if (!snapshots) { + return NS_ERROR_FAILURE; + } + + struct EnumeratorArgs args = { cx, snapshots }; + PRUint32 count = mSampleSetMap.EnumerateEntries(ReflectSamples, + static_cast(&args)); + if (count != mSampleSetMap.Count()) { + return NS_ERROR_FAILURE; + } + + *ret = OBJECT_TO_JSVAL(snapshots); + return NS_OK; +} + +bool +TelemetrySessionData::DeserializeHistogramData(Pickle &pickle, void **iter) +{ + PRUint32 count = 0; + if (!pickle.ReadUInt32(iter, &count)) { + return false; + } + + for (size_t i = 0; i < count; ++i) { + int stored_length; + const char *name; + if (!pickle.ReadData(iter, &name, &stored_length)) { + return false; + } + + Telemetry::ID id; + nsresult rv = TelemetryImpl::GetHistogramEnumId(name, &id); + if (NS_FAILED(rv)) { + // We serialized a non-static histogram. Just drop its data on + // the floor. If we can't deserialize the data, though, we're in + // trouble. + Histogram::SampleSet ss; + if (!ss.Deserialize(iter, pickle)) { + return false; + } + } + + EntryType *entry = mSampleSetMap.GetEntry(id); + if (!entry) { + entry = mSampleSetMap.PutEntry(id); + if (NS_UNLIKELY(!entry)) { + return false; + } + if (!entry->mData.Deserialize(iter, pickle)) { + return false; + } + } + } + + return true; +} + +nsresult +TelemetrySessionData::LoadFromDisk(nsIFile *file, TelemetrySessionData **ptr) +{ + *ptr = nsnull; + nsresult rv; + nsCOMPtr f(do_QueryInterface(file, &rv)); + if (NS_FAILED(rv)) { + return rv; + } + + AutoFDClose fd; + rv = f->OpenNSPRFileDesc(PR_RDONLY, 0, &fd); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + + PRInt32 size = PR_Available(fd); + if (size == -1) { + return NS_ERROR_FAILURE; + } + + nsAutoArrayPtr data(new char[size]); + PRInt32 amount = PR_Read(fd, data, size); + if (amount != size) { + return NS_ERROR_FAILURE; + } + + Pickle pickle(data, size); + void *iter = NULL; + + unsigned int storedVersion; + if (!(pickle.ReadUInt32(&iter, &storedVersion) + && storedVersion == sVersion)) { + return NS_ERROR_FAILURE; + } + + const char *uuid; + int uuidLength; + if (!pickle.ReadData(&iter, &uuid, &uuidLength)) { + return NS_ERROR_FAILURE; + } + + nsAutoPtr sessionData(new TelemetrySessionData(uuid)); + if (!sessionData->DeserializeHistogramData(pickle, &iter)) { + return NS_ERROR_FAILURE; + } + + *ptr = sessionData.forget(); + return NS_OK; +} + +bool +TelemetrySessionData::SerializeHistogramData(Pickle &pickle) +{ + StatisticsRecorder::Histograms hs; + StatisticsRecorder::GetHistograms(&hs); + + if (!pickle.WriteUInt32(hs.size())) { + return false; + } + + for (StatisticsRecorder::Histograms::const_iterator it = hs.begin(); + it != hs.end(); + ++it) { + const Histogram *h = *it; + const char *name = h->histogram_name().c_str(); + + Histogram::SampleSet ss; + h->SnapshotSample(&ss); + + if (!(pickle.WriteData(name, strlen(name)+1) + && ss.Serialize(&pickle))) { + return false; + } + } + + return true; +} + +nsresult +TelemetrySessionData::SaveToDisk(nsIFile *file, const nsACString &uuid) +{ + nsresult rv; + nsCOMPtr f(do_QueryInterface(file, &rv)); + if (NS_FAILED(rv)) { + return rv; + } + + AutoFDClose fd; + rv = f->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE | PR_TRUNCATE, 0600, &fd); + if (NS_FAILED(rv)) { + return rv; + } + + Pickle pickle; + if (!pickle.WriteUInt32(sVersion)) { + return NS_ERROR_FAILURE; + } + + // Include the trailing NULL for the UUID to make reading easier. + const char *data; + size_t length = uuid.GetData(&data); + if (!(pickle.WriteData(data, length+1) + && SerializeHistogramData(pickle))) { + return NS_ERROR_FAILURE; + } + + PRInt32 amount = PR_Write(fd, static_cast(pickle.data()), + pickle.size()); + if (amount != pickle.size()) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +class SaveHistogramEvent : public nsRunnable +{ +public: + SaveHistogramEvent(nsIFile *file, const nsACString &uuid, + nsITelemetrySaveSessionDataCallback *callback) + : mFile(file), mUUID(uuid), mCallback(callback) + {} + + NS_IMETHOD Run() + { + nsresult rv = TelemetrySessionData::SaveToDisk(mFile, mUUID); + mCallback->Handle(!!NS_SUCCEEDED(rv)); + return rv; + } + +private: + nsCOMPtr mFile; + nsCString mUUID; + nsCOMPtr mCallback; +}; + +NS_IMETHODIMP +TelemetryImpl::SaveHistograms(nsIFile *file, const nsACString &uuid, + nsITelemetrySaveSessionDataCallback *callback, + bool isSynchronous) +{ + nsCOMPtr event = new SaveHistogramEvent(file, uuid, callback); + if (isSynchronous) { + return event ? event->Run() : NS_ERROR_FAILURE; + } else { + return NS_DispatchToCurrentThread(event); + } +} + +class LoadHistogramEvent : public nsRunnable +{ +public: + LoadHistogramEvent(nsIFile *file, + nsITelemetryLoadSessionDataCallback *callback) + : mFile(file), mCallback(callback) + {} + + NS_IMETHOD Run() + { + TelemetrySessionData *sessionData = nsnull; + nsresult rv = TelemetrySessionData::LoadFromDisk(mFile, &sessionData); + if (NS_FAILED(rv)) { + mCallback->Handle(nsnull); + } else { + nsCOMPtr data(sessionData); + mCallback->Handle(data); + } + return rv; + } + +private: + nsCOMPtr mFile; + nsCOMPtr mCallback; +}; + +NS_IMETHODIMP +TelemetryImpl::LoadHistograms(nsIFile *file, + nsITelemetryLoadSessionDataCallback *callback) +{ + nsCOMPtr event = new LoadHistogramEvent(file, callback); + return NS_DispatchToCurrentThread(event); +} + NS_IMETHODIMP TelemetryImpl::GetCanRecord(bool *ret) { *ret = mCanRecord; diff --git a/toolkit/components/telemetry/nsITelemetry.idl b/toolkit/components/telemetry/nsITelemetry.idl index 5e82bf126f0a..b001b48ed8d9 100644 --- a/toolkit/components/telemetry/nsITelemetry.idl +++ b/toolkit/components/telemetry/nsITelemetry.idl @@ -38,8 +38,39 @@ * ***** END LICENSE BLOCK ***** */ #include "nsISupports.idl" +#include "nsIFile.idl" -[scriptable, uuid(db854295-478d-4de9-8211-d73ed7d81cd0)] +[scriptable, uuid(c177b6b0-5ef1-44f5-bc67-6bcf7d2518e5)] +interface nsITelemetrySessionData : nsISupports +{ + /** + * The UUID of our previous session. + */ + readonly attribute ACString uuid; + + /** + * An object containing a snapshot from all registered histograms that had + * data recorded in the previous session. + * { name1: data1, name2: data2, .... } + * where the individual dataN are as nsITelemetry.histogramSnapshots. + */ + [implicit_jscontext] + readonly attribute jsval snapshots; +}; + +[scriptable, function, uuid(aff36c9d-7e4c-41ab-a9b6-53773bbca0cd)] +interface nsITelemetryLoadSessionDataCallback : nsISupports +{ + void handle(in nsITelemetrySessionData data); +}; + +[scriptable, function, uuid(40065f26-afd2-4417-93de-c1de9adb1548)] +interface nsITelemetrySaveSessionDataCallback : nsISupports +{ + void handle(in bool success); +}; + +[scriptable, uuid(22fc825e-288f-457e-80d5-5bb35f06d37e)] interface nsITelemetry : nsISupports { /** @@ -127,6 +158,34 @@ interface nsITelemetry : nsISupports [implicit_jscontext] jsval getHistogramById(in ACString id); + /** + * Save persistent histograms to the given file. + * + * @param file - filename for saving + * @param uuid - UUID of this session + * @param callback - function to be caled when file writing is complete + * @param isSynchronous - whether the save is done synchronously. Defaults + * to asynchronous saving. + */ + void saveHistograms(in nsIFile file, in ACString uuid, + in nsITelemetrySaveSessionDataCallback callback, + [optional] in boolean isSynchronous); + + /* Reconstruct an nsITelemetryDataSession object containing histogram + * information from the given file; the file must have been produced + * via saveHistograms. The file is read asynchronously. + * + * This method does not modify the histogram information being + * collected in the current session. + * + * The reconstructed object is then passed to the given callback. + * + * @param file - the file to load histogram information from + * @param callback - function to process histogram information + */ + void loadHistograms(in nsIFile file, + in nsITelemetryLoadSessionDataCallback callback); + /** * Set this to false to disable gathering of telemetry statistics. */ From c3998d6b4ea72129c20d3854401be6ab84a551ff Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Fri, 23 Dec 2011 13:56:38 -0500 Subject: [PATCH 12/58] Bug 707320 - add load/save histograms to TelemetryPing; r=taras --- toolkit/components/telemetry/TelemetryPing.js | 107 ++++++++++++++---- 1 file changed, 88 insertions(+), 19 deletions(-) diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index 196316d4ab66..414f73dd8a57 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -42,6 +42,7 @@ const Cu = Components.utils; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/LightweightThemeManager.jsm"); +Cu.import("resource://gre/modules/ctypes.jsm"); // When modifying the payload in incompatible ways, please bump this version number const PAYLOAD_VERSION = 1; @@ -129,6 +130,10 @@ TelemetryPing.prototype = { _histograms: {}, _initialized: false, _prevValues: {}, + // Generate a unique id once per session so the server can cope with + // duplicate submissions. + _uuid: generateUUID(), + _prevSession: null, /** * Returns a set of histograms that can be converted into JSON @@ -137,8 +142,7 @@ TelemetryPing.prototype = { * histogram_type: <0 for exponential, 1 for linear>, bucketX:countX, ....} ...} * where bucket[XY], count[XY] are positive integers. */ - getHistograms: function getHistograms() { - let hls = Telemetry.histogramSnapshots; + getHistograms: function getHistograms(hls) { let info = Telemetry.registeredHistograms; let ret = {}; @@ -346,24 +350,48 @@ TelemetryPing.prototype = { send: function send(reason, server) { // populate histograms one last time this.gatherMemory(); + let data = this.getSessionPayloadAndSlug(reason); + + // Don't record a successful ping for previous session data. + this.doPing(server, data.slug, data.payload, !data.previous); + this._prevSession = null; + + // We were sending off data from before; now send the actual data + // we've collected this session. + if (data.previous) { + data = this.getSessionPayloadAndSlug(reason); + this.doPing(server, data.slug, data.payload, true); + } + }, + + getSessionPayloadAndSlug: function getSessionPayloadAndSlug(reason) { + // Use a deterministic url for testing. + let isTestPing = (reason == "test-ping"); + let havePreviousSession = !!this._prevSession; + let slug = (isTestPing + ? reason + : (havePreviousSession + ? this._prevSession.uuid + : this._uuid)); let payload = { ver: PAYLOAD_VERSION, - info: this.getMetadata(reason), - simpleMeasurements: getSimpleMeasurements(), - histograms: this.getHistograms(), - slowSQL: Telemetry.slowSQL + // Send a different reason string for previous session data. + info: this.getMetadata(havePreviousSession ? "saved-session" : reason), }; + if (havePreviousSession) { + payload.histograms = this.getHistograms(this._prevSession.snapshots); + } + else { + payload.simpleMeasurements = getSimpleMeasurements(); + payload.histograms = this.getHistograms(Telemetry.histogramSnapshots); + payload.slowSQL = Telemetry.slowSQL; + } + return { previous: !!havePreviousSession, slug: slug, payload: payload }; + }, - let isTestPing = (reason == "test-ping"); - // Generate a unique id once per session so the server can cope with duplicate submissions. - // Use a deterministic url for testing. - if (!this._path) - this._path = "/submit/telemetry/" + (isTestPing ? reason : generateUUID()); - - let hping = Telemetry.getHistogramById("TELEMETRY_PING"); - let hsuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); - - let url = server + this._path; + doPing: function doPing(server, slug, payload, recordSuccess) { + let submitPath = "/submit/telemetry/" + slug; + let url = server + submitPath; let request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"] .createInstance(Ci.nsIXMLHttpRequest); request.mozBackgroundRequest = true; @@ -372,6 +400,7 @@ TelemetryPing.prototype = { request.setRequestHeader("Content-Type", "application/json"); let startTime = new Date(); + let file = this.savedHistogramsFile(); function finishRequest(channel) { let success = false; @@ -379,9 +408,17 @@ TelemetryPing.prototype = { success = channel.QueryInterface(Ci.nsIHttpChannel).requestSucceeded; } catch(e) { } - hsuccess.add(success); - hping.add(new Date() - startTime); - if (isTestPing) + if (recordSuccess) { + let hping = Telemetry.getHistogramById("TELEMETRY_PING"); + let hsuccess = Telemetry.getHistogramById("TELEMETRY_SUCCESS"); + + hsuccess.add(success); + hping.add(new Date() - startTime); + } + if (success && file.exists()) { + file.remove(true); + } + if (slug == "test-ping") Services.obs.notifyObservers(null, "telemetry-test-xhr-complete", null); } request.addEventListener("error", function(aEvent) finishRequest(request.channel), false); @@ -408,6 +445,25 @@ TelemetryPing.prototype = { } }, + savedHistogramsFile: function savedHistogramsFile() { + let profileDirectory = Services.dirsvc.get("ProfD", Ci.nsILocalFile); + let profileFile = profileDirectory.clone(); + + // There's a bunch of binary data in the file, so we need to be + // sensitive to multiple machine types. Use ctypes to get some + // discriminating information. + let size = ctypes.voidptr_t.size; + // Hack to figure out endianness. + let uint32_array_t = ctypes.uint32_t.array(1); + let array = uint32_array_t([0xdeadbeef]); + let uint8_array_t = ctypes.uint8_t.array(4); + let array_as_bytes = ctypes.cast(array, uint8_array_t); + let endian = (array_as_bytes[0] === 0xde) ? "big" : "little" + let name = "sessionHistograms.dat." + size + endian; + profileFile.append(name); + return profileFile; + }, + /** * Initializes telemetry within a timer. If there is no PREF_SERVER set, don't turn on telemetry. */ @@ -428,6 +484,7 @@ TelemetryPing.prototype = { Services.obs.addObserver(this, "private-browsing", false); Services.obs.addObserver(this, "profile-before-change", false); Services.obs.addObserver(this, "sessionstore-windows-restored", false); + Services.obs.addObserver(this, "quit-application-granted", false); // Delay full telemetry initialization to give the browser time to // run various late initializers. Otherwise our gathered memory @@ -441,6 +498,12 @@ TelemetryPing.prototype = { delete self._timer } this._timer.initWithCallback(timerCallback, TELEMETRY_DELAY, Ci.nsITimer.TYPE_ONE_SHOT); + + // Load data from the previous session. + let loadCallback = function(data) { + self._prevSession = data; + } + Telemetry.loadHistograms(this.savedHistogramsFile(), loadCallback); }, /** @@ -451,6 +514,7 @@ TelemetryPing.prototype = { Services.obs.removeObserver(this, "sessionstore-windows-restored"); Services.obs.removeObserver(this, "profile-before-change"); Services.obs.removeObserver(this, "private-browsing"); + Services.obs.removeObserver(this, "quit-application-granted"); }, /** @@ -510,6 +574,11 @@ TelemetryPing.prototype = { } this.send(aTopic == "idle" ? "idle-daily" : aTopic, server); break; + case "quit-application-granted": + Telemetry.saveHistograms(this.savedHistogramsFile(), + this._uuid, function (success) success, + /*isSynchronous=*/true); + break; } }, From 75ffcc39865f4abfd3f5af86f1edb57a8edea4d5 Mon Sep 17 00:00:00 2001 From: Nathan Froyd Date: Fri, 23 Dec 2011 18:37:34 -0500 Subject: [PATCH 13/58] Bug 707320 - add tests for load/save functionality; r=taras --- .../telemetry/tests/unit/test_nsITelemetry.js | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js index e0711d7cedda..56dc92afbd81 100644 --- a/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js +++ b/toolkit/components/telemetry/tests/unit/test_nsITelemetry.js @@ -105,6 +105,45 @@ function test_privateMode() { do_check_neq(uneval(orig), uneval(h.snapshot())); } +function generateUUID() { + let str = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator).generateUUID().toString(); + // strip {} + return str.substring(1, str.length - 1); +} + +// Check that we do sane things when saving to disk. +function test_loadSave() +{ + let dirService = Cc["@mozilla.org/file/directory_service;1"] + .getService(Ci.nsIProperties); + let tmpDir = dirService.get("TmpD", Ci.nsILocalFile); + let tmpFile = tmpDir.clone(); + tmpFile.append("saved-histograms.dat"); + if (tmpFile.exists()) { + tmpFile.remove(true); + } + + let saveFinished = false; + let loadFinished = false; + let uuid = generateUUID(); + let loadCallback = function(data) { + do_check_true(data != null); + do_check_eq(uuid, data.uuid); + loadFinished = true; + do_test_finished(); + }; + let saveCallback = function(success) { + do_check_true(success); + Telemetry.loadHistograms(tmpFile, loadCallback); + saveFinished = true; + }; + do_test_pending(); + Telemetry.saveHistograms(tmpFile, uuid, saveCallback); + do_register_cleanup(function () do_check_true(saveFinished)); + do_register_cleanup(function () do_check_true(loadFinished)); + do_register_cleanup(function () tmpFile.exists() && tmpFile.remove(true)); +} + function run_test() { let kinds = [Telemetry.HISTOGRAM_EXPONENTIAL, Telemetry.HISTOGRAM_LINEAR] @@ -121,4 +160,6 @@ function run_test() test_getHistogramById(); test_getSlowSQL(); test_privateMode(); + test_loadSave(); + } From 33e1a67ecba647d13d6b51faab0800d8c245a142 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 20 Jan 2012 19:49:33 -0800 Subject: [PATCH 14/58] Bug 720067 - re-merge StackSpace.h with Stack.h, extract gc/Root.h (r=bhackett) --- js/src/gc/Root.h | 300 +++++++++++++++++++++++++++++++++ js/src/jscntxt.h | 263 +---------------------------- js/src/jsscope.h | 10 +- js/src/vm/Stack.h | 355 +++++++++++++++++++++++++++++++++++---- js/src/vm/StackSpace.h | 367 ----------------------------------------- 5 files changed, 635 insertions(+), 660 deletions(-) create mode 100644 js/src/gc/Root.h delete mode 100644 js/src/vm/StackSpace.h diff --git a/js/src/gc/Root.h b/js/src/gc/Root.h new file mode 100644 index 000000000000..79fab09c96ec --- /dev/null +++ b/js/src/gc/Root.h @@ -0,0 +1,300 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sw=4 et tw=78: + * + * ***** 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 SpiderMonkey global object code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * + * 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 ***** */ + +#ifndef jsgc_root_h__ +#define jsgc_root_h__ + +#include "jsapi.h" +#include "jsprvtd.h" + +namespace js { + +/* + * Moving GC Stack Rooting + * + * A moving GC may change the physical location of GC allocated things, even + * when they are rooted, updating all pointers to the thing to refer to its new + * location. The GC must therefore know about all live pointers to a thing, + * not just one of them, in order to behave correctly. + * + * The classes below are used to root stack locations whose value may be held + * live across a call that can trigger GC (i.e. a call which might allocate any + * GC things). For a code fragment such as: + * + * Foo(); + * ... = obj->lastProperty(); + * + * If Foo() can trigger a GC, the stack location of obj must be rooted to + * ensure that the GC does not move the JSObject referred to by obj without + * updating obj's location itself. This rooting must happen regardless of + * whether there are other roots which ensure that the object itself will not + * be collected. + * + * If Foo() cannot trigger a GC, and the same holds for all other calls made + * between obj's definitions and its last uses, then no rooting is required. + * + * Several classes are available for rooting stack locations. All are templated + * on the type T of the value being rooted, for which RootMethods must + * have an instantiation. + * + * - Root roots an existing stack allocated variable or other location of + * type T. This is typically used either when a variable only needs to be + * rooted on certain rare paths, or when a function takes a bare GC thing + * pointer as an argument and needs to root it. In the latter case a + * Handle is generally preferred, see below. + * + * - RootedVar declares a variable of type T, whose value is always rooted. + * + * - Handle is a const reference to a Root or RootedVar. Handles are + * coerced automatically from such a Root or RootedVar. Functions which + * take GC things or values as arguments and need to root those arguments + * should generally replace those arguments with handles and avoid any + * explicit rooting. This has two benefits. First, when several such + * functions call each other then redundant rooting of multiple copies of the + * GC thing can be avoided. Second, if the caller does not pass a rooted + * value a compile error will be generated, which is quicker and easier to + * fix than when relying on a separate rooting analysis. + */ + +template <> struct RootMethods +{ + static jsid initial() { return JSID_VOID; } + static ThingRootKind kind() { return THING_ROOT_ID; } + static bool poisoned(jsid id) { return IsPoisonedId(id); } +}; + +template <> struct RootMethods +{ + static jsid initial() { return JSID_VOID; } + static ThingRootKind kind() { return THING_ROOT_ID; } + static bool poisoned(jsid id) { return IsPoisonedId(id); } +}; + +template <> struct RootMethods +{ + static Value initial() { return UndefinedValue(); } + static ThingRootKind kind() { return THING_ROOT_VALUE; } + static bool poisoned(const Value &v) { return IsPoisonedValue(v); } +}; + +template <> struct RootMethods +{ + static Value initial() { return UndefinedValue(); } + static ThingRootKind kind() { return THING_ROOT_VALUE; } + static bool poisoned(const Value &v) { return IsPoisonedValue(v); } +}; + +template +struct RootMethods +{ + static T *initial() { return NULL; } + static ThingRootKind kind() { return T::rootKind(); } + static bool poisoned(T *v) { return IsPoisonedPtr(v); } +}; + +/* + * Root a stack location holding a GC thing. This takes a stack pointer + * and ensures that throughout its lifetime the referenced variable + * will remain pinned against a moving GC. + * + * It is important to ensure that the location referenced by a Root is + * initialized, as otherwise the GC may try to use the the uninitialized value. + * It is generally preferable to use either RootedVar for local variables, or + * Handle for arguments. + */ +template +class Root +{ + public: + Root(JSContext *cx, const T *ptr + JS_GUARD_OBJECT_NOTIFIER_PARAM) + { +#ifdef JSGC_ROOT_ANALYSIS + ThingRootKind kind = RootMethods::kind(); + this->stack = reinterpret_cast**>(&cx->thingGCRooters[kind]); + this->prev = *stack; + *stack = this; +#endif + + JS_ASSERT(!RootMethods::poisoned(*ptr)); + + this->ptr = ptr; + + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + + ~Root() + { +#ifdef JSGC_ROOT_ANALYSIS + JS_ASSERT(*stack == this); + *stack = prev; +#endif + } + +#ifdef JSGC_ROOT_ANALYSIS + Root *previous() { return prev; } +#endif + + const T *address() const { return ptr; } + + private: + +#ifdef JSGC_ROOT_ANALYSIS + Root **stack, *prev; +#endif + const T *ptr; + + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +template template +inline +Handle::Handle(const Root &root) +{ + testAssign(); + ptr = reinterpret_cast(root.address()); +} + +typedef Root RootObject; +typedef Root RootFunction; +typedef Root RootShape; +typedef Root RootBaseShape; +typedef Root RootTypeObject; +typedef Root RootString; +typedef Root RootAtom; +typedef Root RootId; +typedef Root RootValue; + +/* Mark a stack location as a root for a rooting analysis. */ +class CheckRoot +{ +#if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS) + + CheckRoot **stack, *prev; + const uint8_t *ptr; + + public: + template + CheckRoot(JSContext *cx, const T *ptr + JS_GUARD_OBJECT_NOTIFIER_PARAM) + { + this->stack = &cx->checkGCRooters; + this->prev = *stack; + *stack = this; + this->ptr = static_cast(ptr); + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + + ~CheckRoot() + { + JS_ASSERT(*stack == this); + *stack = prev; + } + + CheckRoot *previous() { return prev; } + + bool contains(const uint8_t *v, size_t len) { + return ptr >= v && ptr < v + len; + } + +#else /* DEBUG && JSGC_ROOT_ANALYSIS */ + + public: + template + CheckRoot(JSContext *cx, const T *ptr + JS_GUARD_OBJECT_NOTIFIER_PARAM) + { + JS_GUARD_OBJECT_NOTIFIER_INIT; + } + +#endif /* DEBUG && JSGC_ROOT_ANALYSIS */ + + JS_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +/* Make a local variable which stays rooted throughout its lifetime. */ +template +class RootedVar +{ + public: + RootedVar(JSContext *cx) + : ptr(RootMethods::initial()), root(cx, &ptr) + {} + + RootedVar(JSContext *cx, T initial) + : ptr(initial), root(cx, &ptr) + {} + + operator T () { return ptr; } + T operator ->() { return ptr; } + T * address() { return &ptr; } + const T * address() const { return &ptr; } + T raw() { return ptr; } + + T & operator =(T value) + { + JS_ASSERT(!RootMethods::poisoned(value)); + ptr = value; + return ptr; + } + + private: + T ptr; + Root root; +}; + +template template +inline +Handle::Handle(const RootedVar &root) +{ + ptr = reinterpret_cast(root.address()); +} + +typedef RootedVar RootedVarObject; +typedef RootedVar RootedVarFunction; +typedef RootedVar RootedVarShape; +typedef RootedVar RootedVarBaseShape; +typedef RootedVar RootedVarTypeObject; +typedef RootedVar RootedVarString; +typedef RootedVar RootedVarAtom; +typedef RootedVar RootedVarId; +typedef RootedVar RootedVarValue; + +} /* namespace js */ +#endif /* jsgc_root_h___ */ diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 0b6f4c2187fa..9f574fdf2a2a 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -64,7 +64,7 @@ #include "gc/Statistics.h" #include "js/HashTable.h" #include "js/Vector.h" -#include "vm/StackSpace.h" +#include "vm/Stack.h" #ifdef _MSC_VER #pragma warning(push) @@ -921,11 +921,11 @@ struct JSContext js::ContextStack stack; /* ContextStack convenience functions */ - inline bool hasfp() const; - inline js::StackFrame* fp() const; - inline js::StackFrame* maybefp() const; - inline js::FrameRegs& regs() const; - inline js::FrameRegs* maybeRegs() const; + inline bool hasfp() const { return stack.hasfp(); } + inline js::StackFrame* fp() const { return stack.fp(); } + inline js::StackFrame* maybefp() const { return stack.maybefp(); } + inline js::FrameRegs& regs() const { return stack.regs(); } + inline js::FrameRegs* maybeRegs() const { return stack.maybeRegs(); } /* Set cx->compartment based on the current scope chain. */ void resetCompartment(); @@ -1306,257 +1306,6 @@ struct AutoResolving { JS_DECL_USE_GUARD_OBJECT_NOTIFIER }; -/* - * Moving GC Stack Rooting - * - * A moving GC may change the physical location of GC allocated things, even - * when they are rooted, updating all pointers to the thing to refer to its new - * location. The GC must therefore know about all live pointers to a thing, - * not just one of them, in order to behave correctly. - * - * The classes below are used to root stack locations whose value may be held - * live across a call that can trigger GC (i.e. a call which might allocate any - * GC things). For a code fragment such as: - * - * Foo(); - * ... = obj->lastProperty(); - * - * If Foo() can trigger a GC, the stack location of obj must be rooted to - * ensure that the GC does not move the JSObject referred to by obj without - * updating obj's location itself. This rooting must happen regardless of - * whether there are other roots which ensure that the object itself will not - * be collected. - * - * If Foo() cannot trigger a GC, and the same holds for all other calls made - * between obj's definitions and its last uses, then no rooting is required. - * - * Several classes are available for rooting stack locations. All are templated - * on the type T of the value being rooted, for which RootMethods must - * have an instantiation. - * - * - Root roots an existing stack allocated variable or other location of - * type T. This is typically used either when a variable only needs to be - * rooted on certain rare paths, or when a function takes a bare GC thing - * pointer as an argument and needs to root it. In the latter case a - * Handle is generally preferred, see below. - * - * - RootedVar declares a variable of type T, whose value is always rooted. - * - * - Handle is a const reference to a Root or RootedVar. Handles are - * coerced automatically from such a Root or RootedVar. Functions which - * take GC things or values as arguments and need to root those arguments - * should generally replace those arguments with handles and avoid any - * explicit rooting. This has two benefits. First, when several such - * functions call each other then redundant rooting of multiple copies of the - * GC thing can be avoided. Second, if the caller does not pass a rooted - * value a compile error will be generated, which is quicker and easier to - * fix than when relying on a separate rooting analysis. - */ - -template <> struct RootMethods -{ - static jsid initial() { return JSID_VOID; } - static ThingRootKind kind() { return THING_ROOT_ID; } - static bool poisoned(jsid id) { return IsPoisonedId(id); } -}; - -template <> struct RootMethods -{ - static jsid initial() { return JSID_VOID; } - static ThingRootKind kind() { return THING_ROOT_ID; } - static bool poisoned(jsid id) { return IsPoisonedId(id); } -}; - -template <> struct RootMethods -{ - static Value initial() { return UndefinedValue(); } - static ThingRootKind kind() { return THING_ROOT_VALUE; } - static bool poisoned(const Value &v) { return IsPoisonedValue(v); } -}; - -template <> struct RootMethods -{ - static Value initial() { return UndefinedValue(); } - static ThingRootKind kind() { return THING_ROOT_VALUE; } - static bool poisoned(const Value &v) { return IsPoisonedValue(v); } -}; - -template -struct RootMethods -{ - static T *initial() { return NULL; } - static ThingRootKind kind() { return T::rootKind(); } - static bool poisoned(T *v) { return IsPoisonedPtr(v); } -}; - -/* - * Root a stack location holding a GC thing. This takes a stack pointer - * and ensures that throughout its lifetime the referenced variable - * will remain pinned against a moving GC. - * - * It is important to ensure that the location referenced by a Root is - * initialized, as otherwise the GC may try to use the the uninitialized value. - * It is generally preferable to use either RootedVar for local variables, or - * Handle for arguments. - */ -template -class Root -{ - public: - Root(JSContext *cx, const T *ptr - JS_GUARD_OBJECT_NOTIFIER_PARAM) - { -#ifdef JSGC_ROOT_ANALYSIS - ThingRootKind kind = RootMethods::kind(); - this->stack = reinterpret_cast**>(&cx->thingGCRooters[kind]); - this->prev = *stack; - *stack = this; -#endif - - JS_ASSERT(!RootMethods::poisoned(*ptr)); - - this->ptr = ptr; - - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - - ~Root() - { -#ifdef JSGC_ROOT_ANALYSIS - JS_ASSERT(*stack == this); - *stack = prev; -#endif - } - -#ifdef JSGC_ROOT_ANALYSIS - Root *previous() { return prev; } -#endif - - const T *address() const { return ptr; } - - private: - -#ifdef JSGC_ROOT_ANALYSIS - Root **stack, *prev; -#endif - const T *ptr; - - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -template template -inline -Handle::Handle(const Root &root) -{ - testAssign(); - ptr = reinterpret_cast(root.address()); -} - -typedef Root RootObject; -typedef Root RootFunction; -typedef Root RootShape; -typedef Root RootBaseShape; -typedef Root RootTypeObject; -typedef Root RootString; -typedef Root RootAtom; -typedef Root RootId; -typedef Root RootValue; - -/* Mark a stack location as a root for a rooting analysis. */ -class CheckRoot -{ -#if defined(DEBUG) && defined(JSGC_ROOT_ANALYSIS) - - CheckRoot **stack, *prev; - const uint8_t *ptr; - - public: - template - CheckRoot(JSContext *cx, const T *ptr - JS_GUARD_OBJECT_NOTIFIER_PARAM) - { - this->stack = &cx->checkGCRooters; - this->prev = *stack; - *stack = this; - this->ptr = static_cast(ptr); - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - - ~CheckRoot() - { - JS_ASSERT(*stack == this); - *stack = prev; - } - - CheckRoot *previous() { return prev; } - - bool contains(const uint8_t *v, size_t len) { - return ptr >= v && ptr < v + len; - } - -#else /* DEBUG && JSGC_ROOT_ANALYSIS */ - - public: - template - CheckRoot(JSContext *cx, const T *ptr - JS_GUARD_OBJECT_NOTIFIER_PARAM) - { - JS_GUARD_OBJECT_NOTIFIER_INIT; - } - -#endif /* DEBUG && JSGC_ROOT_ANALYSIS */ - - JS_DECL_USE_GUARD_OBJECT_NOTIFIER -}; - -/* Make a local variable which stays rooted throughout its lifetime. */ -template -class RootedVar -{ - public: - RootedVar(JSContext *cx) - : ptr(RootMethods::initial()), root(cx, &ptr) - {} - - RootedVar(JSContext *cx, T initial) - : ptr(initial), root(cx, &ptr) - {} - - operator T () { return ptr; } - T operator ->() { return ptr; } - T * address() { return &ptr; } - const T * address() const { return &ptr; } - T raw() { return ptr; } - - T & operator =(T value) - { - JS_ASSERT(!RootMethods::poisoned(value)); - ptr = value; - return ptr; - } - - private: - T ptr; - Root root; -}; - -template template -inline -Handle::Handle(const RootedVar &root) -{ - ptr = reinterpret_cast(root.address()); -} - -typedef RootedVar RootedVarObject; -typedef RootedVar RootedVarFunction; -typedef RootedVar RootedVarShape; -typedef RootedVar RootedVarBaseShape; -typedef RootedVar RootedVarTypeObject; -typedef RootedVar RootedVarString; -typedef RootedVar RootedVarAtom; -typedef RootedVar RootedVarId; -typedef RootedVar RootedVarValue; - #ifdef JS_HAS_XML_SUPPORT class AutoXMLRooter : private AutoGCRooter { public: diff --git a/js/src/jsscope.h b/js/src/jsscope.h index 8c02d4f87711..5b9bf4b7258c 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -48,15 +48,13 @@ #include #endif +#include "jsdhash.h" +#include "jsobj.h" +#include "jspropertytree.h" #include "jstypes.h" -#include "jscntxt.h" -#include "jsobj.h" -#include "jsprvtd.h" -#include "jspubtd.h" -#include "jspropertytree.h" - #include "js/HashTable.h" +#include "gc/Root.h" #include "mozilla/Attributes.h" #ifdef _MSC_VER diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 03bea68183d1..6c0667970469 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -46,6 +46,15 @@ struct JSContext; struct JSCompartment; +#ifdef JS_METHODJIT +namespace js { namespace mjit { struct CallSite; }} +typedef js::mjit::CallSite JSInlinedSite; +#else +struct JSInlinedSite {}; +#endif + +typedef /* js::mjit::RejoinState */ size_t JSRejoinState; + namespace js { class StackFrame; @@ -61,13 +70,6 @@ class ExecuteFrameGuard; class DummyFrameGuard; class GeneratorFrameGuard; -namespace mjit { - struct JITScript; - struct CallSite; - jsbytecode *NativeToPC(JITScript *jit, void *ncode, CallSite **pinline); -} -namespace detail { struct OOMCheck; } - class CallIter; class FrameRegsIter; class AllFramesIter; @@ -75,13 +77,15 @@ class AllFramesIter; class ArgumentsObject; class StaticBlockObject; -#ifdef JS_METHODJIT -typedef js::mjit::CallSite JSInlinedSite; -#else -struct JSInlinedSite {}; -#endif +namespace mjit { + struct JITScript; + jsbytecode *NativeToPC(JITScript *jit, void *ncode, CallSite **pinline); +} +namespace detail { + struct OOMCheck; +} -typedef /* js::mjit::RejoinState */ size_t JSRejoinState; +/*****************************************************************************/ /* * VM stack layout @@ -313,6 +317,22 @@ CallArgsListFromVp(uintN argc, Value *vp, CallArgsList *prev) /*****************************************************************************/ +/* Flags specified for a frame as it is constructed. */ +enum InitialFrameFlags { + INITIAL_NONE = 0, + INITIAL_CONSTRUCT = 0x80, /* == StackFrame::CONSTRUCTING, asserted in Stack.h */ + INITIAL_LOWERED = 0x800000 /* == StackFrame::LOWERED_CALL_APPLY, asserted in Stack.h */ +}; + +enum ExecuteType { + EXECUTE_GLOBAL = 0x1, /* == StackFrame::GLOBAL */ + EXECUTE_DIRECT_EVAL = 0x8, /* == StackFrame::EVAL */ + EXECUTE_INDIRECT_EVAL = 0x9, /* == StackFrame::GLOBAL | EVAL */ + EXECUTE_DEBUG = 0x18 /* == StackFrame::EVAL | DEBUGGER */ +}; + +/*****************************************************************************/ + class StackFrame { public: @@ -1397,27 +1417,302 @@ class StackSegment static const size_t VALUES_PER_STACK_SEGMENT = sizeof(StackSegment) / sizeof(Value); JS_STATIC_ASSERT(sizeof(StackSegment) % sizeof(Value) == 0); -inline Value * -StackSpace::firstUnused() const { return seg_ ? seg_->end() : base_; } +/*****************************************************************************/ -inline bool ContextStack::hasfp() const { return seg_ && seg_->maybeRegs(); } -inline FrameRegs * ContextStack::maybeRegs() const { return seg_ ? seg_->maybeRegs() : NULL; } -inline StackFrame * ContextStack::maybefp() const { return seg_ ? seg_->maybefp() : NULL; } -inline FrameRegs & ContextStack::regs() const { JS_ASSERT(hasfp()); return seg_->regs(); } -inline StackFrame * ContextStack::fp() const { JS_ASSERT(hasfp()); return seg_->fp(); } +class StackSpace +{ + StackSegment *seg_; + Value *base_; + mutable Value *conservativeEnd_; +#ifdef XP_WIN + mutable Value *commitEnd_; +#endif + Value *defaultEnd_; + Value *trustedEnd_; -} /* namespace js */ + void assertInvariants() const { + JS_ASSERT(base_ <= conservativeEnd_); +#ifdef XP_WIN + JS_ASSERT(conservativeEnd_ <= commitEnd_); + JS_ASSERT(commitEnd_ <= trustedEnd_); +#endif + JS_ASSERT(conservativeEnd_ <= defaultEnd_); + JS_ASSERT(defaultEnd_ <= trustedEnd_); + } -inline bool JSContext::hasfp() const { return stack.hasfp(); } -inline js::StackFrame* JSContext::fp() const { return stack.fp(); } -inline js::StackFrame* JSContext::maybefp() const { return stack.maybefp(); } -inline js::FrameRegs& JSContext::regs() const { return stack.regs(); } -inline js::FrameRegs* JSContext::maybeRegs() const { return stack.maybeRegs(); } + /* The total number of values/bytes reserved for the stack. */ + static const size_t CAPACITY_VALS = 512 * 1024; + static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value); -namespace js { + /* How much of the stack is initially committed. */ + static const size_t COMMIT_VALS = 16 * 1024; + static const size_t COMMIT_BYTES = COMMIT_VALS * sizeof(Value); -inline void -ContextStack::repointRegs(FrameRegs *regs) { JS_ASSERT(hasfp()); seg_->repointRegs(regs); } + /* How much space is reserved at the top of the stack for trusted JS. */ + static const size_t BUFFER_VALS = 16 * 1024; + static const size_t BUFFER_BYTES = BUFFER_VALS * sizeof(Value); + + static void staticAsserts() { + JS_STATIC_ASSERT(CAPACITY_VALS % COMMIT_VALS == 0); + } + + friend class AllFramesIter; + friend class ContextStack; + friend class StackFrame; + + /* + * Except when changing compartment (see pushDummyFrame), the 'dest' + * parameter of ensureSpace is cx->compartment. Ideally, we'd just pass + * this directly (and introduce a helper that supplies cx->compartment when + * no 'dest' is given). For some compilers, this really hurts performance, + * so, instead, a trivially sinkable magic constant is used to indicate + * that dest should be cx->compartment. + */ + static const size_t CX_COMPARTMENT = 0xc; + + inline bool ensureSpace(JSContext *cx, MaybeReportError report, + Value *from, ptrdiff_t nvals, + JSCompartment *dest = (JSCompartment *)CX_COMPARTMENT) const; + JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report, + Value *from, ptrdiff_t nvals, + JSCompartment *dest) const; + + StackSegment &findContainingSegment(const StackFrame *target) const; + + public: + StackSpace(); + bool init(); + ~StackSpace(); + + /* + * Maximum supported value of arguments.length. This bounds the maximum + * number of arguments that can be supplied to Function.prototype.apply. + * This value also bounds the number of elements parsed in an array + * initialiser. + * + * Since arguments are copied onto the stack, the stack size is the + * limiting factor for this constant. Use the max stack size (available to + * untrusted code) with an extra buffer so that, after such an apply, the + * callee can do a little work without OOMing. + */ + static const uintN ARGS_LENGTH_MAX = CAPACITY_VALS - (2 * BUFFER_VALS); + + /* See stack layout comment in Stack.h. */ + inline Value *firstUnused() const { return seg_ ? seg_->end() : base_; } + + StackSegment &containingSegment(const StackFrame *target) const; + + /* + * Extra space to reserve on the stack for method JIT frames, beyond the + * frame's nslots. This may be used for inlined stack frames, slots storing + * loop invariant code, or to reserve space for pushed callee frames. Note + * that this space should be reserved when pushing interpreter frames as + * well, so that we don't need to check the stack when entering the method + * JIT at loop heads or safe points. + */ + static const size_t STACK_JIT_EXTRA = (/*~VALUES_PER_STACK_FRAME*/ 8 + 18) * 10; + + /* + * Return a limit against which jit code can check for. This limit is not + * necessarily the end of the stack since we lazily commit stack memory on + * some platforms. Thus, when the stack limit is exceeded, the caller should + * use tryBumpLimit to attempt to increase the stack limit by committing + * more memory. If the stack is truly exhausted, tryBumpLimit will report an + * error and return NULL. + * + * An invariant of the methodjit is that there is always space to push a + * frame on top of the current frame's expression stack (which can be at + * most script->nslots deep). getStackLimit ensures that the returned limit + * does indeed have this required space and reports an error and returns + * NULL if this reserve space cannot be allocated. + */ + inline Value *getStackLimit(JSContext *cx, MaybeReportError report); + bool tryBumpLimit(JSContext *cx, Value *from, uintN nvals, Value **limit); + + /* Called during GC: mark segments, frames, and slots under firstUnused. */ + void mark(JSTracer *trc); + + /* We only report the committed size; uncommitted size is uninteresting. */ + JS_FRIEND_API(size_t) sizeOfCommitted(); +}; + +/*****************************************************************************/ + +class ContextStack +{ + StackSegment *seg_; + StackSpace *space_; + JSContext *cx_; + + /* + * Return whether this ContextStack is at the top of the contiguous stack. + * This is a precondition for extending the current segment by pushing + * stack frames or overrides etc. + * + * NB: Just because a stack is onTop() doesn't mean there is necessarily + * a frame pushed on the stack. For this, use hasfp(). + */ + bool onTop() const; + +#ifdef DEBUG + void assertSpaceInSync() const; +#else + void assertSpaceInSync() const {} +#endif + + /* Implementation details of push* public interface. */ + StackSegment *pushSegment(JSContext *cx); + enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false }; + Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars, + MaybeExtend extend, bool *pushedSeg, + JSCompartment *dest = (JSCompartment *)StackSpace::CX_COMPARTMENT); + + inline StackFrame * + getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args, + JSFunction *fun, JSScript *script, /*StackFrame::Flags*/ uint32_t *pflags) const; + + /* Make pop* functions private since only called by guard classes. */ + void popSegment(); + friend class InvokeArgsGuard; + void popInvokeArgs(const InvokeArgsGuard &iag); + friend class FrameGuard; + void popFrame(const FrameGuard &fg); + friend class GeneratorFrameGuard; + void popGeneratorFrame(const GeneratorFrameGuard &gfg); + + friend class StackIter; + + public: + ContextStack(JSContext *cx); + ~ContextStack(); + + /*** Stack accessors ***/ + + /* + * A context's stack is "empty" if there are no scripts or natives + * executing. Note that JS_SaveFrameChain does not factor into this definition. + */ + bool empty() const { return !seg_; } + + /* + * Return whether there has been at least one frame pushed since the most + * recent call to JS_SaveFrameChain. Note that natives do not have frames + * and dummy frames are frames that do not represent script execution hence + * this query has little semantic meaning past "you can call fp()". + */ + inline bool hasfp() const { return seg_ && seg_->maybeRegs(); } + + /* + * Return the most recent script activation's registers with the same + * caveat as hasfp regarding JS_SaveFrameChain. + */ + inline FrameRegs *maybeRegs() const { return seg_ ? seg_->maybeRegs() : NULL; } + inline StackFrame *maybefp() const { return seg_ ? seg_->maybefp() : NULL; } + + /* Faster alternatives to maybe* functions. */ + inline FrameRegs ®s() const { JS_ASSERT(hasfp()); return seg_->regs(); } + inline StackFrame *fp() const { JS_ASSERT(hasfp()); return seg_->fp(); } + + /* The StackSpace currently hosting this ContextStack. */ + StackSpace &space() const { return *space_; } + + /* Return whether the given frame is in this context's stack. */ + bool containsSlow(const StackFrame *target) const; + + /*** Stack manipulation ***/ + + /* + * pushInvokeArgs allocates |argc + 2| rooted values that will be passed as + * the arguments to Invoke. A single allocation can be used for multiple + * Invoke calls. The InvokeArgumentsGuard passed to Invoke must come from + * an immediately-enclosing (stack-wise) call to pushInvokeArgs. + */ + bool pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *ag); + + /* Called by Invoke for a scripted function call. */ + bool pushInvokeFrame(JSContext *cx, const CallArgs &args, + InitialFrameFlags initial, InvokeFrameGuard *ifg); + + /* Called by Execute for execution of eval or global code. */ + bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv, + JSObject &scopeChain, ExecuteType type, + StackFrame *evalInFrame, ExecuteFrameGuard *efg); + + /* + * Called by SendToGenerator to resume a yielded generator. In addition to + * pushing a frame onto the VM stack, this function copies over the + * floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor + * will copy the frame back to the floating frame. + */ + bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg); + + /* + * When changing the compartment of a cx, it is necessary to immediately + * change the scope chain to a global in the right compartment since any + * amount of general VM code can run before the first scripted frame is + * pushed (if at all). This is currently and hackily accomplished by + * pushing a "dummy frame" with the correct scope chain. On success, this + * function will change the compartment to 'scopeChain.compartment()' and + * push a dummy frame for 'scopeChain'. On failure, nothing is changed. + */ + bool pushDummyFrame(JSContext *cx, JSCompartment *dest, JSObject &scopeChain, DummyFrameGuard *dfg); + + /* + * An "inline frame" may only be pushed from within the top, active + * segment. This is the case for calls made inside mjit code and Interpret. + * The 'stackLimit' overload updates 'stackLimit' if it changes. + */ + bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args, + JSFunction &callee, JSScript *script, + InitialFrameFlags initial); + bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args, + JSFunction &callee, JSScript *script, + InitialFrameFlags initial, Value **stackLimit); + void popInlineFrame(FrameRegs ®s); + + /* Pop a partially-pushed frame after hitting the limit before throwing. */ + void popFrameAfterOverflow(); + + /* Get the topmost script and optional pc on the stack. */ + inline JSScript *currentScript(jsbytecode **pc = NULL) const; + + /* Get the scope chain for the topmost scripted call on the stack. */ + inline JSObject *currentScriptedScopeChain() const; + + /* + * Called by the methodjit for an arity mismatch. Arity mismatch can be + * hot, so getFixupFrame avoids doing call setup performed by jit code when + * FixupArity returns. In terms of work done: + * + * getFixupFrame = pushInlineFrame - + * (fp->initJitFrameLatePrologue + regs->prepareToRun) + */ + StackFrame *getFixupFrame(JSContext *cx, MaybeReportError report, + const CallArgs &args, JSFunction *fun, JSScript *script, + void *ncode, InitialFrameFlags initial, Value **stackLimit); + + bool saveFrameChain(); + void restoreFrameChain(); + + /* + * As an optimization, the interpreter/mjit can operate on a local + * FrameRegs instance repoint the ContextStack to this local instance. + */ + inline void repointRegs(FrameRegs *regs) { JS_ASSERT(hasfp()); seg_->repointRegs(regs); } + + /*** For JSContext: ***/ + + /* + * To avoid indirection, ContextSpace caches a pointer to the StackSpace. + * This must be kept coherent with cx->thread->data.space by calling + * 'threadReset' whenver cx->thread changes. + */ + void threadReset(); + + /*** For jit compiler: ***/ + + static size_t offsetOfSeg() { return offsetof(ContextStack, seg_); } +}; /*****************************************************************************/ @@ -1485,6 +1780,7 @@ class GeneratorFrameGuard : public FrameGuard * JS_ASSERT(i.isNativeCall()); * ... i.args(); * } + * } * * The SavedOption parameter additionally lets the iterator continue through * breaks in the callstack (from JS_SaveFrameChain). The default is to stop. @@ -1582,5 +1878,4 @@ class AllFramesIter }; } /* namespace js */ - #endif /* Stack_h__ */ diff --git a/js/src/vm/StackSpace.h b/js/src/vm/StackSpace.h deleted file mode 100644 index 1a55cf68e95c..000000000000 --- a/js/src/vm/StackSpace.h +++ /dev/null @@ -1,367 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- - * vim: set ts=4 sw=4 et tw=79 ft=cpp: - * - * ***** 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 SpiderMonkey JavaScript engine. - * - * The Initial Developer of the Original Code is - * Mozilla Corporation. - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Luke Wagner - * - * 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 StackSpace_h__ -#define StackSpace_h__ - -#include "jsprvtd.h" - -namespace js { - -/* Forward declarations. */ -class FrameGuard; -class DummyFrameGuard; -class ExecuteFrameGuard; -class GeneratorFrameGuard; - -/* Flags specified for a frame as it is constructed. */ -enum InitialFrameFlags { - INITIAL_NONE = 0, - INITIAL_CONSTRUCT = 0x80, /* == StackFrame::CONSTRUCTING, asserted in Stack.h */ - INITIAL_LOWERED = 0x800000 /* == StackFrame::LOWERED_CALL_APPLY, asserted in Stack.h */ -}; - -enum ExecuteType { - EXECUTE_GLOBAL = 0x1, /* == StackFrame::GLOBAL */ - EXECUTE_DIRECT_EVAL = 0x8, /* == StackFrame::EVAL */ - EXECUTE_INDIRECT_EVAL = 0x9, /* == StackFrame::GLOBAL | EVAL */ - EXECUTE_DEBUG = 0x18 /* == StackFrame::EVAL | DEBUGGER */ -}; - -/*****************************************************************************/ - -class StackSpace -{ - StackSegment *seg_; - Value *base_; - mutable Value *conservativeEnd_; -#ifdef XP_WIN - mutable Value *commitEnd_; -#endif - Value *defaultEnd_; - Value *trustedEnd_; - - void assertInvariants() const { - JS_ASSERT(base_ <= conservativeEnd_); -#ifdef XP_WIN - JS_ASSERT(conservativeEnd_ <= commitEnd_); - JS_ASSERT(commitEnd_ <= trustedEnd_); -#endif - JS_ASSERT(conservativeEnd_ <= defaultEnd_); - JS_ASSERT(defaultEnd_ <= trustedEnd_); - } - - /* The total number of values/bytes reserved for the stack. */ - static const size_t CAPACITY_VALS = 512 * 1024; - static const size_t CAPACITY_BYTES = CAPACITY_VALS * sizeof(Value); - - /* How much of the stack is initially committed. */ - static const size_t COMMIT_VALS = 16 * 1024; - static const size_t COMMIT_BYTES = COMMIT_VALS * sizeof(Value); - - /* How much space is reserved at the top of the stack for trusted JS. */ - static const size_t BUFFER_VALS = 16 * 1024; - static const size_t BUFFER_BYTES = BUFFER_VALS * sizeof(Value); - - static void staticAsserts() { - JS_STATIC_ASSERT(CAPACITY_VALS % COMMIT_VALS == 0); - } - - friend class AllFramesIter; - friend class ContextStack; - friend class StackFrame; - - /* - * Except when changing compartment (see pushDummyFrame), the 'dest' - * parameter of ensureSpace is cx->compartment. Ideally, we'd just pass - * this directly (and introduce a helper that supplies cx->compartment when - * no 'dest' is given). For some compilers, this really hurts performance, - * so, instead, a trivially sinkable magic constant is used to indicate - * that dest should be cx->compartment. - */ - static const size_t CX_COMPARTMENT = 0xc; - - inline bool ensureSpace(JSContext *cx, MaybeReportError report, - Value *from, ptrdiff_t nvals, - JSCompartment *dest = (JSCompartment *)CX_COMPARTMENT) const; - JS_FRIEND_API(bool) ensureSpaceSlow(JSContext *cx, MaybeReportError report, - Value *from, ptrdiff_t nvals, - JSCompartment *dest) const; - - StackSegment &findContainingSegment(const StackFrame *target) const; - - public: - StackSpace(); - bool init(); - ~StackSpace(); - - /* - * Maximum supported value of arguments.length. This bounds the maximum - * number of arguments that can be supplied to Function.prototype.apply. - * This value also bounds the number of elements parsed in an array - * initialiser. - * - * Since arguments are copied onto the stack, the stack size is the - * limiting factor for this constant. Use the max stack size (available to - * untrusted code) with an extra buffer so that, after such an apply, the - * callee can do a little work without OOMing. - */ - static const uintN ARGS_LENGTH_MAX = CAPACITY_VALS - (2 * BUFFER_VALS); - - /* See stack layout comment in Stack.h. */ - inline Value *firstUnused() const; - - StackSegment &containingSegment(const StackFrame *target) const; - - /* - * Extra space to reserve on the stack for method JIT frames, beyond the - * frame's nslots. This may be used for inlined stack frames, slots storing - * loop invariant code, or to reserve space for pushed callee frames. Note - * that this space should be reserved when pushing interpreter frames as - * well, so that we don't need to check the stack when entering the method - * JIT at loop heads or safe points. - */ - static const size_t STACK_JIT_EXTRA = (/*~VALUES_PER_STACK_FRAME*/ 8 + 18) * 10; - - /* - * Return a limit against which jit code can check for. This limit is not - * necessarily the end of the stack since we lazily commit stack memory on - * some platforms. Thus, when the stack limit is exceeded, the caller should - * use tryBumpLimit to attempt to increase the stack limit by committing - * more memory. If the stack is truly exhausted, tryBumpLimit will report an - * error and return NULL. - * - * An invariant of the methodjit is that there is always space to push a - * frame on top of the current frame's expression stack (which can be at - * most script->nslots deep). getStackLimit ensures that the returned limit - * does indeed have this required space and reports an error and returns - * NULL if this reserve space cannot be allocated. - */ - inline Value *getStackLimit(JSContext *cx, MaybeReportError report); - bool tryBumpLimit(JSContext *cx, Value *from, uintN nvals, Value **limit); - - /* Called during GC: mark segments, frames, and slots under firstUnused. */ - void mark(JSTracer *trc); - - /* We only report the committed size; uncommitted size is uninteresting. */ - JS_FRIEND_API(size_t) sizeOfCommitted(); -}; - -/*****************************************************************************/ - -class ContextStack -{ - StackSegment *seg_; - StackSpace *space_; - JSContext *cx_; - - /* - * Return whether this ContextStack is at the top of the contiguous stack. - * This is a precondition for extending the current segment by pushing - * stack frames or overrides etc. - * - * NB: Just because a stack is onTop() doesn't mean there is necessarily - * a frame pushed on the stack. For this, use hasfp(). - */ - bool onTop() const; - -#ifdef DEBUG - void assertSpaceInSync() const; -#else - void assertSpaceInSync() const {} -#endif - - /* Implementation details of push* public interface. */ - StackSegment *pushSegment(JSContext *cx); - enum MaybeExtend { CAN_EXTEND = true, CANT_EXTEND = false }; - Value *ensureOnTop(JSContext *cx, MaybeReportError report, uintN nvars, - MaybeExtend extend, bool *pushedSeg, - JSCompartment *dest = (JSCompartment *)StackSpace::CX_COMPARTMENT); - - inline StackFrame * - getCallFrame(JSContext *cx, MaybeReportError report, const CallArgs &args, - JSFunction *fun, JSScript *script, /*StackFrame::Flags*/ uint32_t *pflags) const; - - /* Make pop* functions private since only called by guard classes. */ - void popSegment(); - friend class InvokeArgsGuard; - void popInvokeArgs(const InvokeArgsGuard &iag); - friend class FrameGuard; - void popFrame(const FrameGuard &fg); - friend class GeneratorFrameGuard; - void popGeneratorFrame(const GeneratorFrameGuard &gfg); - - friend class StackIter; - - public: - ContextStack(JSContext *cx); - ~ContextStack(); - - /*** Stack accessors ***/ - - /* - * A context's stack is "empty" if there are no scripts or natives - * executing. Note that JS_SaveFrameChain does factor into this definition. - */ - bool empty() const { return !seg_; } - - /* - * Return whether there has been at least one frame pushed since the most - * recent call to JS_SaveFrameChain. Note that natives do not have frames - * and dummy frames are frames that do not represent script execution hence - * this query has little semantic meaning past "you can call fp()". - */ - inline bool hasfp() const; - - /* - * Return the most recent script activation's registers with the same - * caveat as hasfp regarding JS_SaveFrameChain. - */ - inline FrameRegs *maybeRegs() const; - inline StackFrame *maybefp() const; - - /* Faster alternatives to maybe* functions. */ - inline FrameRegs ®s() const; - inline StackFrame *fp() const; - - /* The StackSpace currently hosting this ContextStack. */ - StackSpace &space() const { assertSpaceInSync(); return *space_; } - - /* Return whether the given frame is in this context's stack. */ - bool containsSlow(const StackFrame *target) const; - - /*** Stack manipulation ***/ - - /* - * pushInvokeArgs allocates |argc + 2| rooted values that will be passed as - * the arguments to Invoke. A single allocation can be used for multiple - * Invoke calls. The InvokeArgumentsGuard passed to Invoke must come from - * an immediately-enclosing (stack-wise) call to pushInvokeArgs. - */ - bool pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *ag); - - /* Called by Invoke for a scripted function call. */ - bool pushInvokeFrame(JSContext *cx, const CallArgs &args, - InitialFrameFlags initial, InvokeFrameGuard *ifg); - - /* Called by Execute for execution of eval or global code. */ - bool pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv, - JSObject &scopeChain, ExecuteType type, - StackFrame *evalInFrame, ExecuteFrameGuard *efg); - - /* - * Called by SendToGenerator to resume a yielded generator. In addition to - * pushing a frame onto the VM stack, this function copies over the - * floating frame stored in 'gen'. When 'gfg' is destroyed, the destructor - * will copy the frame back to the floating frame. - */ - bool pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg); - - /* - * When changing the compartment of a cx, it is necessary to immediately - * change the scope chain to a global in the right compartment since any - * amount of general VM code can run before the first scripted frame is - * pushed (if at all). This is currently and hackily accomplished by - * pushing a "dummy frame" with the correct scope chain. On success, this - * function will change the compartment to 'scopeChain.compartment()' and - * push a dummy frame for 'scopeChain'. On failure, nothing is changed. - */ - bool pushDummyFrame(JSContext *cx, JSCompartment *dest, JSObject &scopeChain, DummyFrameGuard *dfg); - - /* - * An "inline frame" may only be pushed from within the top, active - * segment. This is the case for calls made inside mjit code and Interpret. - * The 'stackLimit' overload updates 'stackLimit' if it changes. - */ - bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args, - JSFunction &callee, JSScript *script, - InitialFrameFlags initial); - bool pushInlineFrame(JSContext *cx, FrameRegs ®s, const CallArgs &args, - JSFunction &callee, JSScript *script, - InitialFrameFlags initial, Value **stackLimit); - void popInlineFrame(FrameRegs ®s); - - /* Pop a partially-pushed frame after hitting the limit before throwing. */ - void popFrameAfterOverflow(); - - /* Get the topmost script and optional pc on the stack. */ - inline JSScript *currentScript(jsbytecode **pc = NULL) const; - - /* Get the scope chain for the topmost scripted call on the stack. */ - inline JSObject *currentScriptedScopeChain() const; - - /* - * Called by the methodjit for an arity mismatch. Arity mismatch can be - * hot, so getFixupFrame avoids doing call setup performed by jit code when - * FixupArity returns. In terms of work done: - * - * getFixupFrame = pushInlineFrame - - * (fp->initJitFrameLatePrologue + regs->prepareToRun) - */ - StackFrame *getFixupFrame(JSContext *cx, MaybeReportError report, - const CallArgs &args, JSFunction *fun, JSScript *script, - void *ncode, InitialFrameFlags initial, Value **stackLimit); - - bool saveFrameChain(); - void restoreFrameChain(); - - /* - * As an optimization, the interpreter/mjit can operate on a local - * FrameRegs instance repoint the ContextStack to this local instance. - */ - inline void repointRegs(FrameRegs *regs); - - /*** For JSContext: ***/ - - /* - * To avoid indirection, ContextSpace caches a pointer to the StackSpace. - * This must be kept coherent with cx->thread->data.space by calling - * 'threadReset' whenver cx->thread changes. - */ - void threadReset(); - - /*** For jit compiler: ***/ - - static size_t offsetOfSeg() { return offsetof(ContextStack, seg_); } -}; - -} /* namespace js */ - -#endif /* StackSpace_h__ */ From 61734cc666b8e7db52419b63b8f74bf7d05498f6 Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Mon, 23 Jan 2012 19:18:13 +0000 Subject: [PATCH 15/58] Bug 649114 - Text-anchor middle is not computed correctly for element scaled to 0. r=jwatt --- layout/reftests/svg/dynamic-text-08-ref.svg | 10 +++++++++ layout/reftests/svg/dynamic-text-08.svg | 24 +++++++++++++++++++++ layout/reftests/svg/reftest.list | 1 + layout/svg/base/src/nsSVGAFrame.cpp | 12 +++-------- layout/svg/base/src/nsSVGGFrame.cpp | 4 +--- layout/svg/base/src/nsSVGTextFrame.cpp | 24 +++++++++++++++------ 6 files changed, 57 insertions(+), 18 deletions(-) create mode 100644 layout/reftests/svg/dynamic-text-08-ref.svg create mode 100644 layout/reftests/svg/dynamic-text-08.svg diff --git a/layout/reftests/svg/dynamic-text-08-ref.svg b/layout/reftests/svg/dynamic-text-08-ref.svg new file mode 100644 index 000000000000..cb443bc66ccc --- /dev/null +++ b/layout/reftests/svg/dynamic-text-08-ref.svg @@ -0,0 +1,10 @@ + + + + Reference for scaling from zero + + ABC + diff --git a/layout/reftests/svg/dynamic-text-08.svg b/layout/reftests/svg/dynamic-text-08.svg new file mode 100644 index 000000000000..ff55f28198fd --- /dev/null +++ b/layout/reftests/svg/dynamic-text-08.svg @@ -0,0 +1,24 @@ + + + + Testcase to scaling from zero + + + ABC + + + diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index f66406417f29..1910c925e4d1 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -100,6 +100,7 @@ random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == dynamic-text-04.svg dyna == dynamic-text-05.svg pass.svg == dynamic-text-06.svg pass.svg == dynamic-text-07.svg dynamic-text-07-ref.svg +== dynamic-text-08.svg dynamic-text-08-ref.svg == dynamic-textPath-01.svg dynamic-textPath-01-ref.svg == dynamic-use-01.svg pass.svg == dynamic-use-02.svg pass.svg diff --git a/layout/svg/base/src/nsSVGAFrame.cpp b/layout/svg/base/src/nsSVGAFrame.cpp index febf623d7ae5..219783bdaf83 100644 --- a/layout/svg/base/src/nsSVGAFrame.cpp +++ b/layout/svg/base/src/nsSVGAFrame.cpp @@ -138,16 +138,10 @@ nsSVGAFrame::AttributeChanged(PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aModType) { - if (aNameSpaceID != kNameSpaceID_None) - return NS_OK; + if (aNameSpaceID == kNameSpaceID_None && + aAttribute == nsGkAtoms::transform) { - if (aAttribute == nsGkAtoms::transform) { - // transform has changed - - // make sure our cached transform matrix gets (lazily) updated - mCanvasTM = nsnull; - - nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED); + NotifySVGChanged(TRANSFORM_CHANGED); } return NS_OK; diff --git a/layout/svg/base/src/nsSVGGFrame.cpp b/layout/svg/base/src/nsSVGGFrame.cpp index 294e772741e3..ef949f535866 100644 --- a/layout/svg/base/src/nsSVGGFrame.cpp +++ b/layout/svg/base/src/nsSVGGFrame.cpp @@ -111,10 +111,8 @@ nsSVGGFrame::AttributeChanged(PRInt32 aNameSpaceID, { if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::transform) { - // make sure our cached transform matrix gets (lazily) updated - mCanvasTM = nsnull; - nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED); + NotifySVGChanged(TRANSFORM_CHANGED); } return NS_OK; diff --git a/layout/svg/base/src/nsSVGTextFrame.cpp b/layout/svg/base/src/nsSVGTextFrame.cpp index a96e66c49724..60f872a53f0c 100644 --- a/layout/svg/base/src/nsSVGTextFrame.cpp +++ b/layout/svg/base/src/nsSVGTextFrame.cpp @@ -89,12 +89,8 @@ nsSVGTextFrame::AttributeChanged(PRInt32 aNameSpaceID, return NS_OK; if (aAttribute == nsGkAtoms::transform) { - // transform has changed - // make sure our cached transform matrix gets (lazily) updated - mCanvasTM = nsnull; - - nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED); + NotifySVGChanged(TRANSFORM_CHANGED); } else if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y || @@ -185,14 +181,30 @@ nsSVGTextFrame::GetRotationOfChar(PRUint32 charnum, float *_retval) void nsSVGTextFrame::NotifySVGChanged(PRUint32 aFlags) { + bool updateGlyphMetrics = false; + + if (aFlags & COORD_CONTEXT_CHANGED) { + updateGlyphMetrics = true; + } + if (aFlags & TRANSFORM_CHANGED) { + if (mCanvasTM && mCanvasTM->IsSingular()) { + // We won't have calculated the glyph positions correctly + updateGlyphMetrics = true; + } // make sure our cached transform matrix gets (lazily) updated mCanvasTM = nsnull; } + if (updateGlyphMetrics) { + // NotifyGlyphMetricsChange will invalidate all children. + // No need for them to invalidate themselves + aFlags |= SUPPRESS_INVALIDATION; + } + nsSVGTextFrameBase::NotifySVGChanged(aFlags); - if (aFlags & COORD_CONTEXT_CHANGED) { + if (updateGlyphMetrics) { // If we are positioned using percentage values we need to update our // position whenever our viewport's dimensions change. From b9e612ab7c6d6bd447a57d5a28bd13f063ce3335 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 20 Jan 2012 15:09:56 -0800 Subject: [PATCH 16/58] Bug 688891: C++ power-armor for Sprinter. (r=cdleary) --- js/src/jsanalyze.cpp | 8 +- js/src/jsapi.cpp | 2 +- js/src/jsdbgapi.cpp | 16 +- js/src/jsopcode.cpp | 539 +++++++++++++++++++++------------- js/src/jsopcode.h | 92 ++++-- js/src/methodjit/Compiler.cpp | 7 +- js/src/shell/js.cpp | 71 +++-- 7 files changed, 461 insertions(+), 274 deletions(-) diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index 3fb7b5522ebc..dd0c0870ed05 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -57,11 +57,11 @@ void PrintBytecode(JSContext *cx, JSScript *script, jsbytecode *pc) { printf("#%u:", script->id()); - LifoAlloc lifoAlloc(1024); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0); + Sprinter sprinter(cx); + if (!sprinter.init()) + return; js_Disassemble1(cx, script, pc, pc - script->code, true, &sprinter); - fprintf(stdout, "%s", sprinter.base); + fprintf(stdout, "%s", sprinter.string()); } #endif diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 0b6947cffc4a..1117e7135b69 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -5600,7 +5600,7 @@ JS_NewStringCopyZ(JSContext *cx, const char *s) AssertNoGC(cx); CHECK_REQUEST(cx); - if (!s) + if (!s || !*s) return cx->runtime->emptyString; n = strlen(s); js = InflateString(cx, s, &n); diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp index ce6234a80d4d..9d55351ec83f 100644 --- a/js/src/jsdbgapi.cpp +++ b/js/src/jsdbgapi.cpp @@ -1620,13 +1620,13 @@ JS_PUBLIC_API(void) JS_DumpBytecode(JSContext *cx, JSScript *script) { #if defined(DEBUG) - LifoAlloc lifoAlloc(1024); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0); + Sprinter sprinter(cx); + if (!sprinter.init()) + return; fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno); js_Disassemble(cx, script, true, &sprinter); - fputs(sprinter.base, stdout); + fputs(sprinter.string(), stdout); fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno); #endif } @@ -1637,13 +1637,13 @@ JS_DumpPCCounts(JSContext *cx, JSScript *script) #if defined(DEBUG) JS_ASSERT(script->pcCounters); - LifoAlloc lifoAlloc(1024); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &lifoAlloc, 0); + Sprinter sprinter(cx); + if (!sprinter.init()) + return; fprintf(stdout, "--- SCRIPT %s:%d ---\n", script->filename, script->lineno); js_DumpPCCounts(cx, script, &sprinter); - fputs(sprinter.base, stdout); + fputs(sprinter.string(), stdout); fprintf(stdout, "--- END SCRIPT %s:%d ---\n", script->filename, script->lineno); #endif } diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 3602912a96a9..be175fa34112 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -423,22 +423,22 @@ js_Disassemble(JSContext *cx, JSScript *script, JSBool lines, Sprinter *sp) JS_FRIEND_API(JSBool) js_DumpPC(JSContext *cx) { - LifoAllocScope las(&cx->tempLifoAlloc()); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + Sprinter sprinter(cx); + if (!sprinter.init()) + return JS_FALSE; JSBool ok = js_DisassembleAtPC(cx, cx->fp()->script(), true, cx->regs().pc, &sprinter); - fprintf(stdout, "%s", sprinter.base); + fprintf(stdout, "%s", sprinter.string()); return ok; } JSBool js_DumpScript(JSContext *cx, JSScript *script) { - LifoAllocScope las(&cx->tempLifoAlloc()); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + Sprinter sprinter(cx); + if (!sprinter.init()) + return JS_FALSE; JSBool ok = js_Disassemble(cx, script, true, &sprinter); - fprintf(stdout, "%s", sprinter.base); + fprintf(stdout, "%s", sprinter.string()); return ok; } @@ -449,10 +449,9 @@ static bool ToDisassemblySource(JSContext *cx, jsval v, JSAutoByteString *bytes) { if (JSVAL_IS_STRING(v)) { - Sprinter sprinter; - LifoAlloc &tla = cx->tempLifoAlloc(); - LifoAllocScope las(&tla); - INIT_SPRINTER(cx, &sprinter, &tla, 0); + Sprinter sprinter(cx); + if (!sprinter.init()) + return false; char *nbytes = QuoteString(&sprinter, JSVAL_TO_STRING(v), '"'); if (!nbytes) return false; @@ -727,97 +726,237 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, /************************************************************************/ -#define OFF2STR(sp,off) ((sp)->base + (off)) -#define STR2OFF(sp,str) ((str) - (sp)->base) -#define RETRACT(sp,str) ((sp)->offset = STR2OFF(sp, str)) +const size_t Sprinter::DefaultSize = 64; -static JSBool -SprintEnsureBuffer(Sprinter *sp, size_t len) +bool +Sprinter::realloc_(size_t newSize) { - if (sp->offset + len < sp->size) - return JS_TRUE; - - ptrdiff_t nb = Max(Max((size_t)1024, sp->size * 2), sp->offset + len + 1); - char *base = sp->base; - if (!base) - base = static_cast(sp->pool->allocUnaligned(nb)); - else - base = static_cast(sp->pool->reallocUnaligned(base, sp->size, nb - sp->size)); - if (!base) { - js_ReportOutOfMemory(sp->context); - return JS_FALSE; - } - sp->base = base; - sp->size = nb; - return JS_TRUE; + JS_ASSERT(newSize > (size_t) offset); + char *newBuf = (char *) context->realloc_(base, newSize); + if (!newBuf) + return false; + base = newBuf; + size = newSize; + base[size - 1] = 0; + return true; } -namespace js { +Sprinter::Sprinter(JSContext *cx) + : context(cx), +#ifdef DEBUG + initialized(false), +#endif + base(NULL), size(0), offset(0) +{ } + +Sprinter::~Sprinter() +{ +#ifdef DEBUG + if (initialized) + checkInvariants(); +#endif + context->free_(base); +} + +bool +Sprinter::init() +{ + JS_ASSERT(!initialized); + base = (char *) context->malloc_(DefaultSize); + if (!base) + return false; +#ifdef DEBUG + initialized = true; +#endif + *base = 0; + size = DefaultSize; + base[size - 1] = 0; + return true; +} + +void +Sprinter::checkInvariants() const +{ + JS_ASSERT(initialized); + JS_ASSERT((size_t) offset < size); + JS_ASSERT(base[size - 1] == 0); +} + +const char * +Sprinter::string() const +{ + return base; +} + +const char * +Sprinter::stringEnd() const +{ + return base + offset; +} char * -SprintReserveAmount(Sprinter *sp, size_t len) +Sprinter::stringAt(ptrdiff_t off) const { - /* Allocate space for s, including the '\0' at the end. */ - if (!SprintEnsureBuffer(sp, len)) - return NULL; + JS_ASSERT(off >= 0 && (size_t) off < size); + return base + off; +} - /* Advance offset and return the previous offset for caller to fill. */ - ptrdiff_t offset = sp->offset; - sp->offset += len; - return sp->base + offset; +char & +Sprinter::operator[](size_t off) +{ + JS_ASSERT(off >= 0 && (size_t) off < size); + return *(base + off); +} + +bool +Sprinter::empty() const +{ + return *base == 0; +} + +char * +Sprinter::reserve(size_t len) +{ + InvariantChecker ic(this); + + while (len + 1 > size - offset) { /* Include trailing \0 */ + if (!realloc_(size * 2)) + return NULL; + } + + char *sb = base + offset; + offset += len; + return sb; +} + +char * +Sprinter::reserveAndClear(size_t len) +{ + char *sb = reserve(len); + if (sb) + memset(sb, 0, len); + return sb; } ptrdiff_t -SprintPut(Sprinter *sp, const char *s, size_t len) +Sprinter::put(const char *s, size_t len) { - ptrdiff_t offset = sp->size; /* save old size */ - char *bp = sp->base; /* save old base */ + InvariantChecker ic(this); - /* Allocate space for s, including the '\0' at the end. */ - if (!SprintEnsureBuffer(sp, len)) + const char *oldBase = base; + const char *oldEnd = base + size; + + ptrdiff_t oldOffset = offset; + char *bp = reserve(len); + if (!bp) return -1; - if (sp->base != bp && /* buffer was realloc'ed */ - s >= bp && s < bp + offset) { /* s was within the buffer */ - s = sp->base + (s - bp); /* this is where it lives now */ + /* s is within the buffer already */ + if (s >= oldBase && s < oldEnd) { + /* buffer was realloc'ed */ + if (base != oldBase) + s = stringAt(s - oldBase); /* this is where it lives now */ + memmove(bp, s, len); + } else { + JS_ASSERT(s < base || s >= base + size); + memcpy(bp, s, len); } - /* Advance offset and copy s into sp's buffer. */ - offset = sp->offset; - sp->offset += len; - bp = sp->base + offset; - memmove(bp, s, len); bp[len] = 0; + return oldOffset; +} + +ptrdiff_t +Sprinter::putString(JSString *s) +{ + InvariantChecker ic(this); + + size_t length = s->length(); + const jschar *chars = s->getChars(context); + if (!chars) + return -1; + + size_t size = GetDeflatedStringLength(context, chars, length); + if (size == (size_t) -1) + return -1; + + ptrdiff_t oldOffset = offset; + char *buffer = reserve(size); + if (!buffer) + return -1; + DeflateStringToBuffer(context, chars, length, buffer, &size); + buffer[size] = 0; + + return oldOffset; +} + +int +Sprinter::printf(const char *fmt, ...) +{ + InvariantChecker ic(this); + + do { + va_list va; + va_start(va, fmt); + int i = vsnprintf(base + offset, size - offset, fmt, va); + va_end(va); + + if (i > -1 && (size_t) i < size - offset) { + offset += i; + return i; + } + } while (realloc_(size * 2)); + + return -1; +} + +void +Sprinter::setOffset(const char *end) +{ + JS_ASSERT(end >= base && end < base + size); + offset = end - base; +} + +void +Sprinter::setOffset(ptrdiff_t off) +{ + JS_ASSERT(off >= 0 && (size_t) off < size); + offset = off; +} + +ptrdiff_t +Sprinter::getOffset() const +{ return offset; } ptrdiff_t -SprintCString(Sprinter *sp, const char *s) +Sprinter::getOffsetOf(const char *string) const +{ + JS_ASSERT(string >= base && string < base + size); + return string - base; +} + +ptrdiff_t +js::SprintPut(Sprinter *sp, const char *s, size_t len) +{ + return sp->put(s, len); +} + +ptrdiff_t +js::SprintCString(Sprinter *sp, const char *s) { return SprintPut(sp, s, strlen(s)); } ptrdiff_t -SprintString(Sprinter *sp, JSString *str) +js::SprintString(Sprinter *sp, JSString *str) { - size_t length = str->length(); - const jschar *chars = str->getChars(sp->context); - if (!chars) - return -1; - - size_t size = GetDeflatedStringLength(sp->context, chars, length); - if (size == (size_t)-1 || !SprintEnsureBuffer(sp, size)) - return -1; - - ptrdiff_t offset = sp->offset; - sp->offset += size; - DeflateStringToBuffer(sp->context, chars, length, sp->base + offset, &size); - sp->base[sp->offset] = 0; - return offset; + return sp->putString(str); } ptrdiff_t -Sprint(Sprinter *sp, const char *format, ...) +js::Sprint(Sprinter *sp, const char *format, ...) { va_list ap; char *bp; @@ -835,8 +974,6 @@ Sprint(Sprinter *sp, const char *format, ...) return offset; } -} // namespace js - const char js_EscapeMap[] = { '\b', 'b', '\f', 'f', @@ -858,7 +995,7 @@ QuoteString(Sprinter *sp, JSString *str, uint32_t quote) /* Sample off first for later return value pointer computation. */ JSBool dontEscape = (quote & DONT_ESCAPE) != 0; jschar qc = (jschar) quote; - ptrdiff_t off = sp->offset; + ptrdiff_t offset = sp->getOffset(); if (qc && Sprint(sp, "%c", (char)qc) < 0) return NULL; @@ -876,18 +1013,18 @@ QuoteString(Sprinter *sp, JSString *str, uint32_t quote) if (t == z) break; } - ptrdiff_t len = t - s; - /* Allocate space for s, including the '\0' at the end. */ - if (!SprintEnsureBuffer(sp, len)) - return NULL; + { + ptrdiff_t len = t - s; + ptrdiff_t base = sp->getOffset(); + char *bp = sp->reserve(len); + if (!bp) + return NULL; - /* Advance sp->offset and copy s into sp's buffer. */ - char *bp = sp->base + sp->offset; - sp->offset += len; - while (--len >= 0) - *bp++ = (char) *s++; - *bp = '\0'; + for (ptrdiff_t i = 0; i < len; ++i) + (*sp)[base + i] = (char) *s++; + (*sp)[base + len] = 0; + } if (t == z) break; @@ -917,19 +1054,20 @@ QuoteString(Sprinter *sp, JSString *str, uint32_t quote) /* * If we haven't Sprint'd anything yet, Sprint an empty string so that - * the OFF2STR below gives a valid result. + * the return below gives a valid result. */ - if (off == sp->offset && Sprint(sp, "") < 0) + if (offset == sp->getOffset() && Sprint(sp, "") < 0) return NULL; - return OFF2STR(sp, off); + + return sp->stringAt(offset); } JSString * js_QuoteString(JSContext *cx, JSString *str, jschar quote) { - LifoAllocScope las(&cx->tempLifoAlloc()); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + Sprinter sprinter(cx); + if (!sprinter.init()) + return NULL; char *bytes = QuoteString(&sprinter, str, quote); JSString *escstr = bytes ? JS_NewStringCopyZ(cx, bytes) : NULL; return escstr; @@ -996,7 +1134,9 @@ js_NewPrinter(JSContext *cx, const char *name, JSFunction *fun, JSPrinter *jp = (JSPrinter *) cx->malloc_(sizeof(JSPrinter)); if (!jp) return NULL; - INIT_SPRINTER(cx, &jp->sprinter, &jp->pool, 0); + new (&jp->sprinter) Sprinter(cx); + if (!jp->sprinter.init()) + return NULL; new (&jp->pool) LifoAlloc(1024); jp->indent = indent; jp->pretty = !!pretty; @@ -1030,9 +1170,7 @@ JSString * js_GetPrinterOutput(JSPrinter *jp) { JSContext *cx = jp->sprinter.context; - if (!jp->sprinter.base) - return cx->runtime->emptyString; - return JS_NewStringCopyZ(cx, jp->sprinter.base); + return JS_NewStringCopyZ(cx, jp->sprinter.string()); } /* Mark the parent and offset into the parent's text for a printed opcode. */ @@ -1133,6 +1271,12 @@ struct SprintStack uintN inArrayInit; /* array initialiser/comprehension level */ JSBool inGenExp; /* in generator expression */ JSPrinter *printer; /* permanent output goes here */ + + explicit SprintStack(JSContext *cx) + : sprinter(cx), offsets(NULL), + opcodes(NULL), bytecodes(NULL), top(0), inArrayInit(0), + inGenExp(JS_FALSE), printer(NULL) + { } }; /* @@ -1145,7 +1289,7 @@ UpdateDecompiledText(SprintStack *ss, jsbytecode *pc, ptrdiff_t todo) JSPrinter *jp = ss->printer; if (jp->decompiledOpcodes && jp->decompiled(pc).text == NULL) { - const char *text = OFF2STR(&ss->sprinter, todo); + const char *text = ss->sprinter.stringAt(todo); size_t len = strlen(text) + 1; char *ntext = ss->printer->pool.newArrayUninitialized(len); @@ -1181,7 +1325,8 @@ SprintDupeStr(SprintStack *ss, const char *str) static inline void SprintOpcodePermanent(JSPrinter *jp, const char *str, jsbytecode *pc) { - UpdateDecompiledParent(jp, pc, NULL, jp->sprinter.offset); + ptrdiff_t offset = jp->sprinter.getOffset(); + UpdateDecompiledParent(jp, pc, NULL, offset); js_printf(jp, "%s", str); } @@ -1198,7 +1343,8 @@ SprintOpcode(SprintStack *ss, const char *str, jsbytecode *pc, JS_ASSERT(ss->sprinter.context->isExceptionPending()); return; } - UpdateDecompiledParent(ss->printer, pc, parentpc, ss->sprinter.offset - startOffset); + ptrdiff_t offset = ss->sprinter.getOffset(); + UpdateDecompiledParent(ss->printer, pc, parentpc, offset - startOffset); SprintCString(&ss->sprinter, str); } @@ -1289,8 +1435,9 @@ GetOff(SprintStack *ss, uintN i) ss->sprinter.context->free_(bytes); return off; } - if (!ss->sprinter.base && SprintPut(&ss->sprinter, "", 0) >= 0) { - memset(ss->sprinter.base, 0, ss->sprinter.offset); + + if (!*ss->sprinter.string()) { + memset(ss->sprinter.stringAt(0), 0, ss->sprinter.getOffset()); ss->offsets[i] = -1; } } @@ -1300,14 +1447,8 @@ GetOff(SprintStack *ss, uintN i) static const char * GetStr(SprintStack *ss, uintN i) { - ptrdiff_t off; - - /* - * Must call GetOff before using ss->sprinter.base, since it may be null - * until bootstrapped by GetOff. - */ - off = GetOff(ss, i); - return OFF2STR(&ss->sprinter, off); + ptrdiff_t off = GetOff(ss, i); + return ss->sprinter.stringAt(off); } /* @@ -1319,11 +1460,10 @@ GetStr(SprintStack *ss, uintN i) /* Fake opcodes (see jsopcode.h) must not overflow unsigned 8-bit space. */ JS_STATIC_ASSERT(JSOP_FAKE_LIMIT <= 255); -static void +static inline void AddParenSlop(SprintStack *ss) { - memset(OFF2STR(&ss->sprinter, ss->sprinter.offset), 0, PAREN_SLOP); - ss->sprinter.offset += PAREN_SLOP; + ss->sprinter.reserveAndClear(PAREN_SLOP); } static JSBool @@ -1331,9 +1471,6 @@ PushOff(SprintStack *ss, ptrdiff_t off, JSOp op, jsbytecode *pc = NULL) { uintN top; - if (!SprintEnsureBuffer(&ss->sprinter, PAREN_SLOP)) - return JS_FALSE; - /* ss->top points to the next free slot; be paranoid about overflow. */ top = ss->top; JS_ASSERT(top < StackDepth(ss->printer->script)); @@ -1349,6 +1486,7 @@ PushOff(SprintStack *ss, ptrdiff_t off, JSOp op, jsbytecode *pc = NULL) : op); ss->bytecodes[top] = pc; ss->top = ++top; + AddParenSlop(ss); return JS_TRUE; } @@ -1387,12 +1525,13 @@ PopOffPrec(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL) *ppc = pc; if (topcs->prec != 0 && topcs->prec < prec) { - ss->sprinter.offset = ss->offsets[top] = off - 2; - off = Sprint(&ss->sprinter, "(%s)", OFF2STR(&ss->sprinter, off)); + ss->offsets[top] = off - 2; + ss->sprinter.setOffset(off - 2); + off = Sprint(&ss->sprinter, "(%s)", ss->sprinter.stringAt(off)); if (ss->printer->decompiledOpcodes && pc) ss->printer->decompiled(pc).parenthesized = true; } else { - ss->sprinter.offset = off; + ss->sprinter.setOffset(off); } return off; } @@ -1403,7 +1542,7 @@ PopStrPrec(SprintStack *ss, uint8_t prec, jsbytecode **ppc = NULL) ptrdiff_t off; off = PopOffPrec(ss, prec, ppc); - return OFF2STR(&ss->sprinter, off); + return ss->sprinter.stringAt(off); } /* @@ -1517,7 +1656,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, JSContext *cx; JSPrinter *jp; ptrdiff_t off, off2, diff, caseExprOff, todo; - char *rval; + const char *rval; uintN i; jsval key; JSString *str; @@ -1597,14 +1736,14 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, return JS_FALSE; } if (todo >= 0) { - rval = OFF2STR(&ss->sprinter, todo); + rval = ss->sprinter.stringAt(todo); } else { rval = QuoteString(&ss->sprinter, str, (jschar) (JSVAL_IS_STRING(key) ? '"' : 0)); if (!rval) return JS_FALSE; } - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); jp->indent += 2; js_printf(jp, "\tcase %s:\n", rval); } @@ -1683,7 +1822,7 @@ GetLocalInSlot(SprintStack *ss, jsint i, jsint slot, JSObject *obj) if (!rval) return NULL; - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); return rval; } } @@ -1696,7 +1835,7 @@ GetLocal(SprintStack *ss, jsint i) { ptrdiff_t off = ss->offsets[i]; if (off >= 0) - return OFF2STR(&ss->sprinter, off); + return ss->sprinter.stringAt(off); /* * We must be called from js_DecompileValueGenerator (via Decompile) when @@ -1922,8 +2061,8 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JS * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for * the nb parameter. */ - ptrdiff_t todo = ss->sprinter.offset; - ss->sprinter.offset = todo + PAREN_SLOP; + ptrdiff_t todo = ss->sprinter.getOffset(); + ss->sprinter.setOffset(todo + PAREN_SLOP); pc = Decompile(ss, pc, -((intN)ss->top)); if (!pc) return NULL; @@ -1933,7 +2072,7 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JS LOCAL_ASSERT(op == JSOP_ENUMELEM || op == JSOP_ENUMCONSTELEM); xval = PopStr(ss, JSOP_NOP); lval = PopStr(ss, JSOP_GETPROP); - ss->sprinter.offset = todo; + ss->sprinter.setOffset(todo); if (*lval == '\0') { /* lval is from JSOP_BINDNAME, so just print xval. */ todo = SprintCString(&ss->sprinter, xval); @@ -1985,9 +2124,9 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, ptrdiff_t head = SprintPut(&ss->sprinter, "[", 1); if (head < 0 || !PushOff(ss, head, JSOP_NOP)) return NULL; - ss->sprinter.offset -= PAREN_SLOP; - LOCAL_ASSERT(head == ss->sprinter.offset - 1); - LOCAL_ASSERT(*OFF2STR(&ss->sprinter, head) == '['); + ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP); + LOCAL_ASSERT(head == ss->sprinter.getOffset() - 1); + LOCAL_ASSERT(ss->sprinter[head] == '['); int lasti = -1; @@ -2034,7 +2173,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, /* Distinguish object from array by opcode or source note. */ if (sn && SN_TYPE(sn) == SRC_INITPROP) { - *OFF2STR(&ss->sprinter, head) = '{'; + ss->sprinter[head] = '{'; if (Sprint(&ss->sprinter, "%g: ", d) < 0) return NULL; } else { @@ -2055,9 +2194,9 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, { JSAtom *atom; LOAD_ATOM(0); - *OFF2STR(&ss->sprinter, head) = '{'; + ss->sprinter[head] = '{'; #if JS_HAS_DESTRUCTURING_SHORTHAND - nameoff = ss->sprinter.offset; + nameoff = ss->sprinter.getOffset(); #endif if (!QuoteString(&ss->sprinter, atom, IsIdentifier(atom) ? 0 : (jschar)'\'')) return NULL; @@ -2088,8 +2227,8 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, if (nameoff >= 0) { ptrdiff_t offset, initlen; - offset = ss->sprinter.offset; - LOCAL_ASSERT(*OFF2STR(&ss->sprinter, offset) == '\0'); + offset = ss->sprinter.getOffset(); + LOCAL_ASSERT(ss->sprinter[offset] == '\0'); initlen = offset - nameoff; LOCAL_ASSERT(initlen >= 4); @@ -2103,12 +2242,12 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, * like, and apply the shorthand if we can. */ namelen = (size_t)(initlen - 2) >> 1; - name = OFF2STR(&ss->sprinter, nameoff); + name = ss->sprinter.stringAt(nameoff); if (!strncmp(name + namelen, ": ", 2) && !strncmp(name, name + namelen + 2, namelen)) { offset -= namelen + 2; - *OFF2STR(&ss->sprinter, offset) = '\0'; - ss->sprinter.offset = offset; + ss->sprinter[offset] = '\0'; + ss->sprinter.setOffset(offset); } } } @@ -2140,7 +2279,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, } out: - const char *lval = OFF2STR(&ss->sprinter, head); + const char *lval = ss->sprinter.stringAt(head); if (SprintPut(&ss->sprinter, (*lval == '[') ? "]" : "}", 1) < 0) return NULL; return pc; @@ -2163,7 +2302,7 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, todo = Sprint(&ss->sprinter, "%s[", VarPrefix(sn)); if (todo < 0 || !PushOff(ss, todo, JSOP_NOP)) return NULL; - ss->sprinter.offset -= PAREN_SLOP; + ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP); for (;;) { pc += oplen; @@ -2198,7 +2337,7 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, if (SprintPut(&ss->sprinter, "]", 1) < 0) return NULL; - ss->sprinter.offset = ss->offsets[i]; + ss->sprinter.setOffset(ss->offsets[i]); ss->top = start; *todop = todo; return pc; @@ -2245,7 +2384,7 @@ PushBlockNames(JSContext *cx, SprintStack *ss, const AtomVector &atoms) { for (size_t i = 0; i < atoms.length(); i++) { const char *name = QuoteString(&ss->sprinter, atoms[i], 0); - if (!name || !PushOff(ss, STR2OFF(&ss->sprinter, name), JSOP_ENTERBLOCK)) + if (!name || !PushOff(ss, ss->sprinter.getOffsetOf(name), JSOP_ENTERBLOCK)) return false; } return true; @@ -2390,7 +2529,7 @@ SprintNormalFor(JSContext *cx, JSPrinter *jp, SprintStack *ss, const char *initP LOCAL_ASSERT(ss->top - saveTop <= 1U); jsbytecode *updatepc = NULL; const char *update = (ss->top == saveTop) - ? ss->sprinter.base + ss->sprinter.offset + ? ss->sprinter.stringEnd() : PopStr(ss, JSOP_NOP, &updatepc); js_printf(jp, " "); SprintOpcodePermanent(jp, update, updatepc); @@ -2416,7 +2555,9 @@ SprintNormalFor(JSContext *cx, JSPrinter *jp, SprintStack *ss, const char *initP static JSBool InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) { - INIT_SPRINTER(cx, &ss->sprinter, &cx->tempLifoAlloc(), PAREN_SLOP); + if (!ss->sprinter.init()) + return JS_FALSE; + ss->sprinter.setOffset(PAREN_SLOP); /* Allocate the parallel (to avoid padding) offset, opcode and bytecode stacks. */ size_t offsetsz = depth * sizeof(ptrdiff_t); @@ -2743,7 +2884,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } else if (!inXML) { rval = PopStrPrecDupe(ss, cs->prec + !!(cs->format & JOF_LEFTASSOC), &rvalpc); lval = PopStrPrec(ss, cs->prec + !(cs->format & JOF_LEFTASSOC), &lvalpc); - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); SprintOpcode(ss, lval, lvalpc, pc, todo); Sprint(&ss->sprinter, " %s ", token); SprintOpcode(ss, rval, rvalpc, pc, todo); @@ -2827,8 +2968,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (!jp2) return NULL; ok = js_DecompileFunction(jp2); - if (ok && jp2->sprinter.base) - js_puts(jp, jp2->sprinter.base); + if (ok && !jp2->sprinter.empty()) + js_puts(jp, jp2->sprinter.string()); js_DestroyPrinter(jp2); if (!ok) return NULL; @@ -2858,7 +2999,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = QuoteString(&ss->sprinter, atom, 0); if (!rval) return NULL; - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); js_printf(jp, "\t%s:\n", rval); jp->indent += 4; break; @@ -2868,7 +3009,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = QuoteString(&ss->sprinter, atom, 0); if (!rval) return NULL; - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); js_printf(jp, "\t%s: {\n", rval); jp->indent += 4; break; @@ -2948,7 +3089,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (todo < 0) return NULL; for (uintN i = newtop; i < oldtop; i++) { - rval = OFF2STR(&ss->sprinter, ss->offsets[i]); + rval = ss->sprinter.stringAt(ss->offsets[i]); if (Sprint(&ss->sprinter, ss_format, (i == newtop) ? "" : ", ", (i == oldtop - 1 && *rval == '\0') @@ -2966,14 +3107,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) * empty group assignment decompilation. */ if (newtop == oldtop) { - ss->sprinter.offset = todo; + ss->sprinter.setOffset(todo); } else { /* * Kill newtop before the end_groupassignment: label by * retracting/popping early. */ LOCAL_ASSERT(newtop < oldtop); - ss->sprinter.offset = GetOff(ss, newtop); + ss->sprinter.setOffset(GetOff(ss, newtop)); ss->top = newtop; } @@ -2991,7 +3132,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) * exactly how long rval lives, or else copies it down via * SprintCString. */ - rval = OFF2STR(&ss->sprinter, todo); + rval = ss->sprinter.stringAt(todo); rvalpc = NULL; todo = -2; pc2 = pc + oplen; @@ -3031,7 +3172,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) } #endif if (newtop < oldtop) { - ss->sprinter.offset = GetOff(ss, newtop); + ss->sprinter.setOffset(GetOff(ss, newtop)); ss->top = newtop; } break; @@ -3084,7 +3225,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) /* Pop Decompile result and print comma expression. */ rval = PopStrDupe(ss, op, &rvalpc); - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); SprintOpcode(ss, lval, lvalpc, pushpc, todo); SprintCString(&ss->sprinter, ", "); SprintOpcode(ss, rval, rvalpc, pushpc, todo); @@ -3293,7 +3434,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) LOCAL_ASSERT(top >= depth); top -= depth; ss->top = top; - ss->sprinter.offset = GetOff(ss, top); + ss->sprinter.setOffset(GetOff(ss, top)); if (op == JSOP_LEAVEBLOCKEXPR) todo = SprintCString(&ss->sprinter, rval); break; @@ -3324,14 +3465,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) Vector rhsExprs(cx); if (!rhsExprs.resize(atoms.length())) return NULL; - for (size_t i = 0; i < rhsExprs.length(); ++i) { - rhsExprs[i] = GetStr(ss, letDepth + i); - if (!rhsExprs[i]) - return NULL; - } + for (size_t i = 0; i < rhsExprs.length(); ++i) + rhsExprs[i] = SprintDupeStr(ss, GetStr(ss, letDepth + i)); /* Build the let head starting at headBegin. */ - ptrdiff_t headBegin = ss->sprinter.offset; + ptrdiff_t headBegin = ss->sprinter.getOffset(); /* * For group assignment, prepend the '[lhs-vars] = [' here, @@ -3384,7 +3522,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) /* Clone the let head chars before clobbering the stack. */ DupBuffer head(cx); - if (!Dup(OFF2STR(&ss->sprinter, headBegin), &head)) + if (!Dup(ss->sprinter.stringAt(headBegin), &head)) return NULL; if (!AssignBlockNamesToPushedSlots(cx, ss, atoms)) return NULL; @@ -3679,8 +3817,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) * (so has neutral stack balance). */ LOCAL_ASSERT(pos == 0); - xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]); - ss->sprinter.offset = PAREN_SLOP; + xval = ss->sprinter.stringAt(ss->offsets[forpos]); + ss->sprinter.setOffset(PAREN_SLOP); todo = Sprint(&ss->sprinter, ss_format, rval, xval); if (todo < 0) return NULL; @@ -3698,12 +3836,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT); ptrdiff_t start = ss->offsets[pos]; - LOCAL_ASSERT(ss->sprinter.base[start] == '[' || - ss->sprinter.base[start] == '#'); + LOCAL_ASSERT(ss->sprinter[start] == '[' || + ss->sprinter[start] == '#'); LOCAL_ASSERT(forpos < ss->top); - xval = OFF2STR(&ss->sprinter, ss->offsets[forpos]); - lval = OFF2STR(&ss->sprinter, start); - RETRACT(&ss->sprinter, lval); + xval = ss->sprinter.stringAt(ss->offsets[forpos]); + lval = ss->sprinter.stringAt(start); + ss->sprinter.setOffset(lval); todo = Sprint(&ss->sprinter, sss_format, lval, rval, xval); if (todo < 0) @@ -3798,7 +3936,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (ss->inArrayInit || ss->inGenExp) { rval = POP_STR(); if (ss->top >= 1 && ss->opcodes[ss->top - 1] == JSOP_FORLOCAL) { - ss->sprinter.offset = ss->offsets[ss->top] - PAREN_SLOP; + ss->sprinter.setOffset(ss->offsets[ss->top] - PAREN_SLOP); if (Sprint(&ss->sprinter, " %s (%s in %s)", foreach ? js_for_each_str : js_for_str, lval, rval) < 0) { @@ -3864,7 +4002,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = QuoteString(&ss->sprinter, atom, 0); if (!rval) return NULL; - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); js_printf(jp, "\tcontinue %s;\n", rval); break; @@ -3877,7 +4015,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = QuoteString(&ss->sprinter, atom, 0); if (!rval) return NULL; - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); js_printf(jp, "\tbreak %s;\n", rval); break; @@ -3905,7 +4043,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = PopCondStr(ss, &rvalpc); if (ss->inArrayInit || ss->inGenExp) { LOCAL_ASSERT(SN_TYPE(sn) == SRC_IF); - ss->sprinter.offset -= PAREN_SLOP; + ss->sprinter.setOffset(ss->sprinter.getOffset() - PAREN_SLOP); if (Sprint(&ss->sprinter, " if (%s)", rval) < 0) return NULL; AddParenSlop(ss); @@ -3971,7 +4109,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) len = GET_JUMP_OFFSET(pc); DECOMPILE_CODE(pc + oplen, len - oplen); rval = PopStrDupe(ss, op, &rvalpc); - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); SprintOpcode(ss, xval, xvalpc, pushpc, todo); SprintCString(&ss->sprinter, " ? "); SprintOpcode(ss, lval, lvalpc, pushpc, todo); @@ -4006,7 +4144,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = PopStrDupe(ss, op, &rvalpc); if (!rval) return NULL; - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); SprintOpcode(ss, lval, lvalpc, pushpc, todo); if (jp->pretty && jp->indent + 4 + strlen(lval) + 4 + strlen(rval) > 75) { @@ -4206,7 +4344,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) do_setlval: sn = js_GetSrcNote(jp->script, pc - 1); - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); if (sn && SN_TYPE(sn) == SRC_ASSIGNOP) { const char *token = GetTokenForAssignment(jp, sn, lastop, pc, rvalpc, @@ -4275,7 +4413,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) op = saveop; lval = "(", rval = ")"; - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); if (op == JSOP_NEW) { if (argc == 0) lval = rval = ""; @@ -4306,7 +4444,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) lval = QuoteString(&ss->sprinter, atom, 0); if (!lval) return NULL; - RETRACT(&ss->sprinter, lval); + ss->sprinter.setOffset(lval); do_delete_lval: todo = Sprint(&ss->sprinter, "%s %s", js_delete_str, lval); break; @@ -4348,7 +4486,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) { const char *prefix = (op == JSOP_VOID) ? js_void_str : js_typeof_str; rval = PopStrDupe(ss, op, &rvalpc); - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); Sprint(&ss->sprinter, "%s ", prefix); SprintOpcode(ss, rval, rvalpc, pc, todo); break; @@ -4369,7 +4507,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) lval = QuoteString(&ss->sprinter, atom, 0); if (!lval) return NULL; - RETRACT(&ss->sprinter, lval); + ss->sprinter.setOffset(lval); do_inclval: todo = Sprint(&ss->sprinter, ss_format, js_incop_strs[!(cs->format & JOF_INC)], lval); @@ -4429,7 +4567,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) lval = QuoteString(&ss->sprinter, atom, 0); if (!lval) return NULL; - RETRACT(&ss->sprinter, lval); + ss->sprinter.setOffset(lval); do_lvalinc: todo = Sprint(&ss->sprinter, ss_format, lval, js_incop_strs[!(cs->format & JOF_INC)]); @@ -4487,7 +4625,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) GET_QUOTE_AND_FMT("[%s]", ".%s", rval); PROPAGATE_CALLNESS(); lval = PopStr(ss, op, &lvalpc); - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); SprintOpcode(ss, lval, lvalpc, pc, todo); Sprint(&ss->sprinter, fmt, rval); break; @@ -4510,7 +4648,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) const char *token = GetTokenForAssignment(jp, sn, lastop, pc, rvalpc, &lastlvalpc, &lastrvalpc); - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); SprintOpcode(ss, lval, lvalpc, pc, todo); Sprint(&ss->sprinter, fmt, xval, token); SprintOpcode(ss, rval, rvalpc, pc, todo); @@ -4527,7 +4665,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) op = saveop; PROPAGATE_CALLNESS(); lval = PopStr(ss, op, &lvalpc); - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); SprintOpcode(ss, lval, lvalpc, pc, todo); if (*xval != '\0') { bool xml = (JOF_OPMODE(lastop) == JOF_XMLNAME); @@ -4553,7 +4691,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) const char *token = GetTokenForAssignment(jp, sn, lastop, pc, rvalpc, &lastlvalpc, &lastrvalpc); - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); SprintOpcode(ss, lval, lvalpc, pc, todo); SprintCString(&ss->sprinter, xml ? "." : "["); SprintOpcode(ss, xval, xvalpc, pc, todo); @@ -4591,7 +4729,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = QuoteString(&ss->sprinter, atom, inXML ? DONT_ESCAPE : 0); if (!rval) return NULL; - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); todo = Sprint(&ss->sprinter, sss_format, VarPrefix(sn), lval, rval); break; @@ -4628,7 +4766,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = QuoteString(&ss->sprinter, atom, inXML ? DONT_ESCAPE : '"'); if (!rval) return NULL; - todo = STR2OFF(&ss->sprinter, rval); + todo = ss->sprinter.getOffsetOf(rval); break; case JSOP_LAMBDA: @@ -4640,7 +4778,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) Vector *outerLocalNames; JSScript *inner, *outer; Vector *decompiledOpcodes; - SprintStack ss2; + SprintStack ss2(cx); JSFunction *outerfun; LOAD_FUNCTION(0); @@ -5013,7 +5151,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) i = pc[1]; LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object); - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); #if JS_HAS_SHARP_VARS op = (JSOp)pc[len]; if (op == JSOP_SHARPINIT) @@ -5042,7 +5180,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_NEWARRAY: { - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); ++ss->inArrayInit; if (SprintCString(&ss->sprinter, "[") < 0) return NULL; @@ -5051,7 +5189,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_NEWOBJECT: { - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); if (SprintCString(&ss->sprinter, "{") < 0) return NULL; break; @@ -5071,7 +5209,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) inArray = (*xval == '['); if (inArray) --ss->inArrayInit; - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); SprintOpcode(ss, rval, rvalpc, pc, todo); Sprint(&ss->sprinter, "%s%c", (sn && SN_TYPE(sn) == SRC_CONTINUE) ? ", " : "", @@ -5115,7 +5253,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) /* fall through */ do_initprop: - todo = ss->sprinter.offset; + todo = ss->sprinter.getOffset(); SprintOpcode(ss, lval, lvalpc, pc, todo); maybeComma = isFirst ? "" : ", "; if (lastop == JSOP_GETTER || lastop == JSOP_SETTER) { @@ -5197,7 +5335,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) rval = QuoteString(&ss->sprinter, atom, 0); if (!rval) return NULL; - RETRACT(&ss->sprinter, rval); + ss->sprinter.setOffset(rval); lval = POP_STR(); todo = Sprint(&ss->sprinter, "%s::%s", lval, rval); break; @@ -5381,7 +5519,7 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, /* Initialize a sprinter for use with the offset stack. */ LifoAllocScope las(&cx->tempLifoAlloc()); - SprintStack ss; + SprintStack ss(cx); if (!InitSprintStack(cx, &ss, jp, depth)) return false; @@ -5412,9 +5550,9 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, /* If the given code didn't empty the stack, do it now. */ if (ok && ss.top) { - char *last; + const char *last; do { - last = OFF2STR(&ss.sprinter, PopOff(&ss, JSOP_POP)); + last = ss.sprinter.stringAt(PopOff(&ss, JSOP_POP)); } while (ss.top > pcdepth); js_printf(jp, "%s", last); } @@ -5524,7 +5662,7 @@ js_DecompileFunction(JSPrinter *jp) } else { JSScript *script = fun->script(); #if JS_HAS_DESTRUCTURING - SprintStack ss; + SprintStack ss(jp->sprinter.context); #endif /* Print the parameters. */ @@ -5535,7 +5673,6 @@ js_DecompileFunction(JSPrinter *jp) #if JS_HAS_DESTRUCTURING ss.printer = NULL; jp->script = script; - LifoAllocScope las(&jp->sprinter.context->tempLifoAlloc()); #endif for (uintN i = 0; i < fun->nargs; i++) { @@ -5586,7 +5723,6 @@ js_DecompileFunction(JSPrinter *jp) #if JS_HAS_DESTRUCTURING jp->script = NULL; - las.releaseEarly(); #endif if (!ok) return JS_FALSE; @@ -5818,8 +5954,7 @@ DecompileExpression(JSContext *cx, JSScript *script, JSFunction *fun, if (!DecompileCode(g.printer, script, begin, (uintN) len, (uintN) pcdepth)) return NULL; - const char *name = (g.printer->sprinter.base) ? g.printer->sprinter.base : ""; - return JS_strdup(cx, name); + return JS_strdup(cx, g.printer->sprinter.string()); } uintN diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 7fb3ac27c568..ff55800fbbec 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -440,27 +440,81 @@ DecompileValueGenerator(JSContext *cx, intN spindex, const Value &v, /* * Sprintf, but with unlimited and automatically allocated buffering. */ -struct Sprinter { - JSContext *context; /* context executing the decompiler */ - LifoAlloc *pool; /* string allocation pool */ - char *base; /* base address of buffer in pool */ - size_t size; /* size of buffer allocated at base */ - ptrdiff_t offset; /* offset of next free char in buffer */ +class Sprinter +{ + public: + struct InvariantChecker + { + const Sprinter *parent; + + explicit InvariantChecker(const Sprinter *p) : parent(p) { + parent->checkInvariants(); + } + + ~InvariantChecker() { + parent->checkInvariants(); + } + }; + + JSContext *context; /* context executing the decompiler */ + + private: + static const size_t DefaultSize; +#ifdef DEBUG + bool initialized; /* true if this is initialized, use for debug builds */ +#endif + char *base; /* malloc'd buffer address */ + size_t size; /* size of buffer allocated at base */ + ptrdiff_t offset; /* offset of next free char in buffer */ + + bool realloc_(size_t newSize); + + public: + explicit Sprinter(JSContext *cx); + ~Sprinter(); + + /* Initialize this sprinter, returns false on error */ + bool init(); + + void checkInvariants() const; + + const char *string() const; + const char *stringEnd() const; + /* Returns the string at offset |off| */ + char *stringAt(ptrdiff_t off) const; + /* Returns the char at offset |off| */ + char &operator[](size_t off); + /* Test if this Sprinter is empty */ + bool empty() const; + + /* + * Attempt to reserve len + 1 space (for a trailing NULL byte). If the + * attempt succeeds, return a pointer to the start of that space and adjust the + * internal content. The caller *must* completely fill this space on success. + */ + char *reserve(size_t len); + /* Like reserve, but memory is initialized to 0 */ + char *reserveAndClear(size_t len); + + /* + * Puts |len| characters from |s| at the current position and return an offset to + * the beginning of this new data + */ + ptrdiff_t put(const char *s, size_t len); + ptrdiff_t putString(JSString *str); + + /* Prints a formatted string into the buffer */ + int printf(const char *fmt, ...); + + /* Change the offset */ + void setOffset(const char *end); + void setOffset(ptrdiff_t off); + + /* Get the offset */ + ptrdiff_t getOffset() const; + ptrdiff_t getOffsetOf(const char *string) const; }; -#define INIT_SPRINTER(cx, sp, ap, off) \ - ((sp)->context = cx, (sp)->pool = ap, (sp)->base = NULL, (sp)->size = 0, \ - (sp)->offset = off) - -/* - * Attempt to reserve len space in sp (including a trailing NULL byte). If the - * attempt succeeds, return a pointer to the start of that space and adjust the - * length of sp's contents. The caller *must* completely fill this space - * (including the space for the trailing NULL byte) on success. - */ -extern char * -SprintReserveAmount(Sprinter *sp, size_t len); - extern ptrdiff_t SprintPut(Sprinter *sp, const char *s, size_t len); diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index ae066faa6254..e8681692cbcb 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -1840,13 +1840,12 @@ mjit::Compiler::finishThisUp() #define SPEW_OPCODE() \ JS_BEGIN_MACRO \ if (IsJaegerSpewChannelActive(JSpew_JSOps)) { \ - LifoAllocScope las(&cx->tempLifoAlloc()); \ - Sprinter sprinter; \ - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); \ + Sprinter sprinter(cx); \ + sprinter.init(); \ js_Disassemble1(cx, script, PC, PC - script->code, \ JS_TRUE, &sprinter); \ JaegerSpew(JSpew_JSOps, " %2d %s", \ - frame.stackDepth(), sprinter.base); \ + frame.stackDepth(), sprinter.string()); \ } \ JS_END_MACRO; #else diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 8fed7382fa05..22ba64954f8b 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -1916,9 +1916,9 @@ SrcNotes(JSContext *cx, JSScript *script, Sprinter *sp) JSAtom *atom = script->getAtom(index); Sprint(sp, " atom %u (", index); size_t len = PutEscapedString(NULL, 0, atom, '\0'); - if (char *buf = SprintReserveAmount(sp, len)) { + if (char *buf = sp->reserve(len)) { PutEscapedString(buf, len, atom, 0); - buf[len] = '\0'; + buf[len] = 0; } Sprint(sp, ")"); break; @@ -1964,9 +1964,9 @@ SrcNotes(JSContext *cx, JSScript *script, Sprinter *sp) static JSBool Notes(JSContext *cx, uintN argc, jsval *vp) { - LifoAllocScope las(&cx->tempLifoAlloc()); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + Sprinter sprinter(cx); + if (!sprinter.init()) + return JS_FALSE; jsval *argv = JS_ARGV(cx, vp); for (uintN i = 0; i < argc; i++) { @@ -1977,7 +1977,7 @@ Notes(JSContext *cx, uintN argc, jsval *vp) SrcNotes(cx, script, &sprinter); } - JSString *str = JS_NewStringCopyZ(cx, sprinter.base); + JSString *str = JS_NewStringCopyZ(cx, sprinter.string()); if (!str) return JS_FALSE; JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str)); @@ -2117,19 +2117,18 @@ DisassembleToString(JSContext *cx, uintN argc, jsval *vp) if (!p.parse(cx)) return false; - LifoAllocScope las(&cx->tempLifoAlloc()); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); - Sprinter *sp = &sprinter; + Sprinter sprinter(cx); + if (!sprinter.init()) + return false; bool ok = true; if (p.argc == 0) { /* Without arguments, disassemble the current script. */ if (JSStackFrame *frame = JS_GetScriptedCaller(cx, NULL)) { JSScript *script = JS_GetFrameScript(cx, frame); - if (js_Disassemble(cx, script, p.lines, sp)) { - SrcNotes(cx, script, sp); - TryNotes(cx, script, sp); + if (js_Disassemble(cx, script, p.lines, &sprinter)) { + SrcNotes(cx, script, &sprinter); + TryNotes(cx, script, &sprinter); } else { ok = false; } @@ -2138,11 +2137,11 @@ DisassembleToString(JSContext *cx, uintN argc, jsval *vp) for (uintN i = 0; i < p.argc; i++) { JSFunction *fun; JSScript *script = ValueToScript(cx, p.argv[i], &fun); - ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, sp); + ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, &sprinter); } } - JSString *str = ok ? JS_NewStringCopyZ(cx, sprinter.base) : NULL; + JSString *str = ok ? JS_NewStringCopyZ(cx, sprinter.string()) : NULL; if (!str) return false; JS_SET_RVAL(cx, vp, STRING_TO_JSVAL(str)); @@ -2156,19 +2155,18 @@ Disassemble(JSContext *cx, uintN argc, jsval *vp) if (!p.parse(cx)) return false; - LifoAllocScope las(&cx->tempLifoAlloc()); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); - Sprinter *sp = &sprinter; + Sprinter sprinter(cx); + if (!sprinter.init()) + return false; bool ok = true; if (p.argc == 0) { /* Without arguments, disassemble the current script. */ if (JSStackFrame *frame = JS_GetScriptedCaller(cx, NULL)) { JSScript *script = JS_GetFrameScript(cx, frame); - if (js_Disassemble(cx, script, p.lines, sp)) { - SrcNotes(cx, script, sp); - TryNotes(cx, script, sp); + if (js_Disassemble(cx, script, p.lines, &sprinter)) { + SrcNotes(cx, script, &sprinter); + TryNotes(cx, script, &sprinter); } else { ok = false; } @@ -2177,12 +2175,12 @@ Disassemble(JSContext *cx, uintN argc, jsval *vp) for (uintN i = 0; i < p.argc; i++) { JSFunction *fun; JSScript *script = ValueToScript(cx, p.argv[i], &fun); - ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, sp); + ok = ok && script && DisassembleScript(cx, script, fun, p.lines, p.recursive, &sprinter); } } if (ok) - fprintf(stdout, "%s\n", sprinter.base); + fprintf(stdout, "%s\n", sprinter.string()); JS_SET_RVAL(cx, vp, JSVAL_VOID); return ok; } @@ -2218,12 +2216,12 @@ DisassFile(JSContext *cx, uintN argc, jsval *vp) if (!script) return false; - LifoAllocScope las(&cx->tempLifoAlloc()); - Sprinter sprinter; - INIT_SPRINTER(cx, &sprinter, &cx->tempLifoAlloc(), 0); + Sprinter sprinter(cx); + if (!sprinter.init()) + return false; bool ok = DisassembleScript(cx, script, NULL, p.lines, p.recursive, &sprinter); if (ok) - fprintf(stdout, "%s\n", sprinter.base); + fprintf(stdout, "%s\n", sprinter.string()); if (!ok) return false; @@ -2267,10 +2265,11 @@ DisassWithSrc(JSContext *cx, uintN argc, jsval *vp) pc = script->code; end = pc + script->length; - LifoAllocScope las(&cx->tempLifoAlloc()); - Sprinter sprinter; - Sprinter *sp = &sprinter; - INIT_SPRINTER(cx, sp, &cx->tempLifoAlloc(), 0); + Sprinter sprinter(cx); + if (!sprinter.init()) { + ok = JS_FALSE; + goto bail; + } /* burn the leading lines */ line2 = JS_PCToLineNumber(cx, script, pc); @@ -2290,11 +2289,11 @@ DisassWithSrc(JSContext *cx, uintN argc, jsval *vp) if (line2 < line1) { if (bupline != line2) { bupline = line2; - Sprint(sp, "%s %3u: BACKUP\n", sep, line2); + Sprint(&sprinter, "%s %3u: BACKUP\n", sep, line2); } } else { if (bupline && line1 == line2) - Sprint(sp, "%s %3u: RESTORE\n", sep, line2); + Sprint(&sprinter, "%s %3u: RESTORE\n", sep, line2); bupline = 0; while (line1 < line2) { if (!fgets(linebuf, LINE_BUF_LEN, file)) { @@ -2305,11 +2304,11 @@ DisassWithSrc(JSContext *cx, uintN argc, jsval *vp) goto bail; } line1++; - Sprint(sp, "%s %3u: %s", sep, line1, linebuf); + Sprint(&sprinter, "%s %3u: %s", sep, line1, linebuf); } } - len = js_Disassemble1(cx, script, pc, pc - script->code, JS_TRUE, sp); + len = js_Disassemble1(cx, script, pc, pc - script->code, JS_TRUE, &sprinter); if (!len) { ok = JS_FALSE; goto bail; From bada412c9a6a22f4d10d7a04db3924cdc7f5b6f8 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 18 Jan 2012 20:40:20 -0800 Subject: [PATCH 17/58] Bug 688891: Remove unaligned LifoAlloc API. (r=cdleary) --- js/src/ds/LifoAlloc.cpp | 57 ----------------------------------------- js/src/ds/LifoAlloc.h | 8 ------ 2 files changed, 65 deletions(-) diff --git a/js/src/ds/LifoAlloc.cpp b/js/src/ds/LifoAlloc.cpp index 384daae559d7..a6f43df0b77e 100644 --- a/js/src/ds/LifoAlloc.cpp +++ b/js/src/ds/LifoAlloc.cpp @@ -82,29 +82,6 @@ BumpChunk::canAlloc(size_t n) return bumped <= limit && bumped > headerBase(); } -bool -BumpChunk::canAllocUnaligned(size_t n) -{ - char *bumped = bump + n; - return bumped <= limit && bumped > headerBase(); -} - -void * -BumpChunk::tryAllocUnaligned(size_t n) -{ - char *oldBump = bump; - char *newBump = bump + n; - if (newBump > limit) - return NULL; - - if (JS_UNLIKELY(newBump < oldBump)) - return NULL; - - JS_ASSERT(canAllocUnaligned(n)); /* Ensure consistency between "can" and "try". */ - setBump(newBump); - return oldBump; -} - } /* namespace detail */ } /* namespace js */ @@ -195,37 +172,3 @@ LifoAlloc::getOrCreateChunk(size_t n) } return newChunk; } - -void * -LifoAlloc::allocUnaligned(size_t n) -{ - void *result; - if (latest && (result = latest->tryAllocUnaligned(n))) - return result; - - return alloc(n); -} - -void * -LifoAlloc::reallocUnaligned(void *origPtr, size_t origSize, size_t incr) -{ - JS_ASSERT(first && latest); - - /* - * Maybe we can grow the latest allocation in a BumpChunk. - * - * Note: we could also realloc the whole BumpChunk in the !canAlloc - * case, but this should not be a frequently hit case. - */ - if (latest - && origPtr == (char *) latest->mark() - origSize - && latest->canAllocUnaligned(incr)) { - JS_ALWAYS_TRUE(allocUnaligned(incr)); - return origPtr; - } - - /* Otherwise, memcpy. */ - size_t newSize = origSize + incr; - void *newPtr = allocUnaligned(newSize); - return newPtr ? js_memcpy(newPtr, origPtr, origSize) : NULL; -} diff --git a/js/src/ds/LifoAlloc.h b/js/src/ds/LifoAlloc.h index 3c5681361ccc..8f2d9bb51cf3 100644 --- a/js/src/ds/LifoAlloc.h +++ b/js/src/ds/LifoAlloc.h @@ -134,7 +134,6 @@ class BumpChunk } bool canAlloc(size_t n); - bool canAllocUnaligned(size_t n); /* Try to perform an allocation of size |n|, return null if not possible. */ JS_ALWAYS_INLINE @@ -154,8 +153,6 @@ class BumpChunk return aligned; } - void *tryAllocUnaligned(size_t n); - void *allocInfallible(size_t n) { void *result = tryAlloc(n); JS_ASSERT(result); @@ -320,11 +317,6 @@ class LifoAlloc } JS_DECLARE_NEW_METHODS(alloc, JS_ALWAYS_INLINE) - - /* Some legacy clients (ab)use LifoAlloc to act like a vector, see bug 688891. */ - - void *allocUnaligned(size_t n); - void *reallocUnaligned(void *origPtr, size_t origSize, size_t incr); }; class LifoAllocScope From eea04f58d3bd32005a989705dc7c5593638cb338 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 20 Jan 2012 19:00:55 -0800 Subject: [PATCH 18/58] Bug 719659 - Add -std=gnu99 to CFLAGS so that gcc and friends permit use of C99 constructs without warnings. This doesn't mean that everything in C99 is permitted now -- only the stuff supported by all the compilers we care about, including MSVC. r=espindola --HG-- extra : rebase_source : 0788763db92464426eaab05cc92ba9d4baea152c --- configure.in | 3 +++ js/src/configure.in | 3 +++ 2 files changed, 6 insertions(+) diff --git a/configure.in b/configure.in index 80d9544ef1c6..36ae7c123d9a 100644 --- a/configure.in +++ b/configure.in @@ -1827,6 +1827,9 @@ dnl ======================================================== dnl GNU specific defaults dnl ======================================================== if test "$GNU_CC"; then + # Per bug 719659 comment 2, some of the headers on ancient build machines + # require gnu89 inline semantics. But otherwise, we use C99. + CFLAGS="$CFLAGS -std=gnu99 -fgnu89-inline" # FIXME: Let us build with strict aliasing. bug 414641. CFLAGS="$CFLAGS -fno-strict-aliasing" MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' diff --git a/js/src/configure.in b/js/src/configure.in index 9ae067dc380e..e4d13ed4492d 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -1765,6 +1765,9 @@ dnl ======================================================== dnl GNU specific defaults dnl ======================================================== if test "$GNU_CC"; then + # Per bug 719659 comment 2, some of the headers on ancient build machines + # require gnu89 inline semantics. But otherwise, we use C99. + CFLAGS="$CFLAGS -std=gnu99 -fgnu89-inline" MKSHLIB='$(CXX) $(CXXFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' MKCSHLIB='$(CC) $(CFLAGS) $(DSO_PIC_CFLAGS) $(DSO_LDOPTS) -Wl,-h,$@ -o $@' DSO_LDOPTS='-shared' From fb111be4404be00eed68e502ce75d5b5a27cdcdc Mon Sep 17 00:00:00 2001 From: Robert Longson Date: Mon, 23 Jan 2012 20:40:09 +0000 Subject: [PATCH 19/58] Backout Bug 649114 - Reftest failures --- layout/reftests/svg/reftest.list | 1 - layout/svg/base/src/nsSVGAFrame.cpp | 12 +++++++++--- layout/svg/base/src/nsSVGGFrame.cpp | 4 +++- layout/svg/base/src/nsSVGTextFrame.cpp | 24 ++++++------------------ 4 files changed, 18 insertions(+), 23 deletions(-) diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index 1910c925e4d1..f66406417f29 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -100,7 +100,6 @@ random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == dynamic-text-04.svg dyna == dynamic-text-05.svg pass.svg == dynamic-text-06.svg pass.svg == dynamic-text-07.svg dynamic-text-07-ref.svg -== dynamic-text-08.svg dynamic-text-08-ref.svg == dynamic-textPath-01.svg dynamic-textPath-01-ref.svg == dynamic-use-01.svg pass.svg == dynamic-use-02.svg pass.svg diff --git a/layout/svg/base/src/nsSVGAFrame.cpp b/layout/svg/base/src/nsSVGAFrame.cpp index 219783bdaf83..febf623d7ae5 100644 --- a/layout/svg/base/src/nsSVGAFrame.cpp +++ b/layout/svg/base/src/nsSVGAFrame.cpp @@ -138,10 +138,16 @@ nsSVGAFrame::AttributeChanged(PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aModType) { - if (aNameSpaceID == kNameSpaceID_None && - aAttribute == nsGkAtoms::transform) { + if (aNameSpaceID != kNameSpaceID_None) + return NS_OK; - NotifySVGChanged(TRANSFORM_CHANGED); + if (aAttribute == nsGkAtoms::transform) { + // transform has changed + + // make sure our cached transform matrix gets (lazily) updated + mCanvasTM = nsnull; + + nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED); } return NS_OK; diff --git a/layout/svg/base/src/nsSVGGFrame.cpp b/layout/svg/base/src/nsSVGGFrame.cpp index ef949f535866..294e772741e3 100644 --- a/layout/svg/base/src/nsSVGGFrame.cpp +++ b/layout/svg/base/src/nsSVGGFrame.cpp @@ -111,8 +111,10 @@ nsSVGGFrame::AttributeChanged(PRInt32 aNameSpaceID, { if (aNameSpaceID == kNameSpaceID_None && aAttribute == nsGkAtoms::transform) { + // make sure our cached transform matrix gets (lazily) updated + mCanvasTM = nsnull; - NotifySVGChanged(TRANSFORM_CHANGED); + nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED); } return NS_OK; diff --git a/layout/svg/base/src/nsSVGTextFrame.cpp b/layout/svg/base/src/nsSVGTextFrame.cpp index 60f872a53f0c..a96e66c49724 100644 --- a/layout/svg/base/src/nsSVGTextFrame.cpp +++ b/layout/svg/base/src/nsSVGTextFrame.cpp @@ -89,8 +89,12 @@ nsSVGTextFrame::AttributeChanged(PRInt32 aNameSpaceID, return NS_OK; if (aAttribute == nsGkAtoms::transform) { + // transform has changed - NotifySVGChanged(TRANSFORM_CHANGED); + // make sure our cached transform matrix gets (lazily) updated + mCanvasTM = nsnull; + + nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED); } else if (aAttribute == nsGkAtoms::x || aAttribute == nsGkAtoms::y || @@ -181,30 +185,14 @@ nsSVGTextFrame::GetRotationOfChar(PRUint32 charnum, float *_retval) void nsSVGTextFrame::NotifySVGChanged(PRUint32 aFlags) { - bool updateGlyphMetrics = false; - - if (aFlags & COORD_CONTEXT_CHANGED) { - updateGlyphMetrics = true; - } - if (aFlags & TRANSFORM_CHANGED) { - if (mCanvasTM && mCanvasTM->IsSingular()) { - // We won't have calculated the glyph positions correctly - updateGlyphMetrics = true; - } // make sure our cached transform matrix gets (lazily) updated mCanvasTM = nsnull; } - if (updateGlyphMetrics) { - // NotifyGlyphMetricsChange will invalidate all children. - // No need for them to invalidate themselves - aFlags |= SUPPRESS_INVALIDATION; - } - nsSVGTextFrameBase::NotifySVGChanged(aFlags); - if (updateGlyphMetrics) { + if (aFlags & COORD_CONTEXT_CHANGED) { // If we are positioned using percentage values we need to update our // position whenever our viewport's dimensions change. From bd5d832b2faad843ec3db0d1cb3ca03a3aca537e Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 20 Jan 2012 23:39:32 -0800 Subject: [PATCH 20/58] Bug 720094 - Simplify the implementation of JSDOUBLE_IS_NaN a bit, consolidating two implementations (conditioned on the architecture) into one. r=dvander --- js/src/jsnum.h | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/js/src/jsnum.h b/js/src/jsnum.h index 735c02665d3b..b53627dd4ffa 100644 --- a/js/src/jsnum.h +++ b/js/src/jsnum.h @@ -92,11 +92,8 @@ JSDOUBLE_IS_NaN(jsdouble d) { jsdpun u; u.d = d; -#if defined(mips) || defined(__mips__) || defined(MIPS) || defined(_MIPS_) - return (u.u64 & ~JSDOUBLE_SIGNBIT) > JSDOUBLE_EXPMASK; -#else - return (u.s.hi & JSDOUBLE_HI32_NAN) == JSDOUBLE_HI32_NAN; -#endif + return (u.u64 & JSDOUBLE_EXPMASK) == JSDOUBLE_EXPMASK && + (u.u64 & JSDOUBLE_MANTMASK) != 0; } static inline int From f1d1ea0f85422fae0dc16e7ada0eab639bc150a0 Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Mon, 23 Jan 2012 13:32:12 -0800 Subject: [PATCH 21/58] Bug 717713 - Add -Qunused-arguments to default Clang compiler flags; r=ted --- configure.in | 11 +++++++++++ js/src/configure.in | 11 +++++++++++ 2 files changed, 22 insertions(+) diff --git a/configure.in b/configure.in index 36ae7c123d9a..b8b22dbad3c2 100644 --- a/configure.in +++ b/configure.in @@ -1823,6 +1823,17 @@ if test "$OS_TARGET" = "Android"; then LIBS="$LIBS $STLPORT_LIBS" fi +dnl ======================================================== +dnl Suppress Clang Argument Warnings +dnl ======================================================== +if test -n "$CLANG_CC"; then + _WARNINGS_CFLAGS="-Qunused-arguments ${_WARNINGS_CFLAGS}" + CPPFLAGS="-Qunused-arguments ${CPPFLAGS}" +fi +if test -n "$CLANG_CXX"; then + _WARNINGS_CXXFLAGS="-Qunused-arguments ${_WARNINGS_CXXFLAGS}" +fi + dnl ======================================================== dnl GNU specific defaults dnl ======================================================== diff --git a/js/src/configure.in b/js/src/configure.in index e4d13ed4492d..63e4507060bc 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -1761,6 +1761,17 @@ if test "$OS_TARGET" = "Android"; then ANDROID_LDFLAGS="$LDFLAGS" fi +dnl ======================================================== +dnl Suppress Clang Argument Warnings +dnl ======================================================== +if test -n "$CLANG_CC"; then + _WARNINGS_CFLAGS="-Qunused-arguments ${_WARNINGS_CFLAGS}" + CPPFLAGS="-Qunused-arguments ${CPPFLAGS}" +fi +if test -n "$CLANG_CXX"; then + _WARNINGS_CXXFLAGS="-Qunused-arguments ${_WARNINGS_CXXFLAGS}" +fi + dnl ======================================================== dnl GNU specific defaults dnl ======================================================== From 50d6f70243645104d2d9fe3b3d8d922879c132da Mon Sep 17 00:00:00 2001 From: Josh Matthews Date: Mon, 23 Jan 2012 16:38:00 -0500 Subject: [PATCH 22/58] Bug 649564 - Stupid typo in test_socks.js. rs=ted --- netwerk/test/unit/test_socks.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/netwerk/test/unit/test_socks.js b/netwerk/test/unit/test_socks.js index ce0b9a6c1e2d..2d35b3909854 100644 --- a/netwerk/test/unit/test_socks.js +++ b/netwerk/test/unit/test_socks.js @@ -450,9 +450,7 @@ SocksTestServer.prototype = { try { this.client_subprocess.kill(); } catch (x) { - do_print('Killing subprocess failed: ' + e); - if (x.stack) - _dump_exception_stack(x.stack); + do_note_exception(x, 'Killing subprocess failed'); } this.client_subprocess = null; } From 6580348f03b09cb969d94d3be3bd8e92b5916aee Mon Sep 17 00:00:00 2001 From: Brian Nicholson Date: Mon, 23 Jan 2012 13:40:07 -0800 Subject: [PATCH 23/58] Bug 719662 - Use nsIPrefLocalizedString for getComplexValue. r=mfinkle --- mobile/android/chrome/content/browser.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 29a458714a48..7ed4860b6d6d 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -621,7 +621,7 @@ var BrowserApp = { case Ci.nsIPrefBranch.PREF_STRING: default: pref.type = "string"; - pref.value = Services.prefs.getComplexValue(prefName, Ci.nsISupportsString).data; + pref.value = Services.prefs.getComplexValue(prefName, Ci.nsIPrefLocalizedString).data; break; } } catch (e) { From 7be6565932eb56301d71df49fcf6588fdb31622a Mon Sep 17 00:00:00 2001 From: Brian Nicholson Date: Mon, 23 Jan 2012 13:47:48 -0800 Subject: [PATCH 24/58] Bug 701824 - "Show character encoding" (Text encoding) preference should be hooked up. r=mfinkle --- mobile/android/base/GeckoApp.java | 55 +++++++++ mobile/android/base/GeckoPreferences.java | 11 ++ .../base/locales/en-US/android_strings.dtd | 5 +- .../base/resources/layout/gecko_menu.xml | 4 + .../android/base/resources/values/arrays.xml | 8 ++ .../base/resources/xml/preferences.xml | 4 +- mobile/android/base/strings.xml.in | 3 + mobile/android/chrome/content/browser.js | 112 ++++++++++++++++-- .../locales/en-US/chrome/browser.properties | 2 +- 9 files changed, 190 insertions(+), 14 deletions(-) diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index e5d9001d9cde..ac62ba35c472 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -440,6 +440,7 @@ abstract public class GeckoApp MenuItem share = aMenu.findItem(R.id.share); MenuItem saveAsPDF = aMenu.findItem(R.id.save_as_pdf); MenuItem downloads = aMenu.findItem(R.id.downloads); + MenuItem charEncoding = aMenu.findItem(R.id.char_encoding); if (tab == null) { bookmark.setEnabled(false); @@ -476,6 +477,8 @@ abstract public class GeckoApp if (Build.VERSION.SDK_INT < 12) downloads.setVisible(false); + charEncoding.setVisible(GeckoPreferences.getCharEncodingState()); + return true; } @@ -538,6 +541,9 @@ abstract public class GeckoApp intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); startActivity(intent); return true; + case R.id.char_encoding: + GeckoAppShell.sendEventToGecko(new GeckoEvent("CharEncoding:Get", null)); + return true; default: return super.onOptionsItemSelected(item); } @@ -985,6 +991,51 @@ abstract public class GeckoApp int size = message.getInt("size"); handleDownloadDone(displayName, path, mimeType, size); + } else if (event.equals("CharEncoding:Data")) { + final JSONArray charsets = message.getJSONArray("charsets"); + int selected = message.getInt("selected"); + + final int len = charsets.length(); + final String[] titleArray = new String[len]; + for (int i = 0; i < len; i++) { + JSONObject charset = charsets.getJSONObject(i); + titleArray[i] = charset.getString("title"); + } + + final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); + dialogBuilder.setSingleChoiceItems(titleArray, selected, new AlertDialog.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + try { + JSONObject charset = charsets.getJSONObject(which); + GeckoAppShell.sendEventToGecko(new GeckoEvent("CharEncoding:Set", charset.getString("code"))); + dialog.dismiss(); + } catch (JSONException e) { + Log.e(LOGTAG, "error parsing json", e); + } + } + }); + dialogBuilder.setNegativeButton(R.string.button_cancel, new AlertDialog.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + mMainHandler.post(new Runnable() { + public void run() { + dialogBuilder.show(); + } + }); + } else if (event.equals("CharEncoding:State")) { + final boolean visible = message.getString("visible").equals("true"); + GeckoPreferences.setCharEncodingState(visible); + if (sMenu != null) { + mMainHandler.post(new Runnable() { + public void run() { + sMenu.findItem(R.id.char_encoding).setVisible(visible); + } + }); + } } } catch (Exception e) { Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e); @@ -1598,6 +1649,8 @@ abstract public class GeckoApp GeckoAppShell.registerGeckoEventListener("FormAssist:AutoComplete", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("Permissions:Data", GeckoApp.mAppContext); GeckoAppShell.registerGeckoEventListener("Downloads:Done", GeckoApp.mAppContext); + GeckoAppShell.registerGeckoEventListener("CharEncoding:Data", GeckoApp.mAppContext); + GeckoAppShell.registerGeckoEventListener("CharEncoding:State", GeckoApp.mAppContext); mConnectivityFilter = new IntentFilter(); mConnectivityFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); @@ -1926,6 +1979,8 @@ abstract public class GeckoApp GeckoAppShell.unregisterGeckoEventListener("FormAssist:AutoComplete", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("Permissions:Data", GeckoApp.mAppContext); GeckoAppShell.unregisterGeckoEventListener("Downloads:Done", GeckoApp.mAppContext); + GeckoAppShell.unregisterGeckoEventListener("CharEncoding:Data", GeckoApp.mAppContext); + GeckoAppShell.unregisterGeckoEventListener("CharEncoding:State", GeckoApp.mAppContext); mFavicons.close(); diff --git a/mobile/android/base/GeckoPreferences.java b/mobile/android/base/GeckoPreferences.java index 731bb698783a..5da6e81e4357 100644 --- a/mobile/android/base/GeckoPreferences.java +++ b/mobile/android/base/GeckoPreferences.java @@ -73,6 +73,7 @@ public class GeckoPreferences private ArrayList mPreferencesList = new ArrayList(); private PreferenceScreen mPreferenceScreen; + private static boolean sIsCharEncodingEnabled = false; @Override protected void onCreate(Bundle savedInstanceState) { @@ -140,12 +141,22 @@ public class GeckoPreferences final private int DIALOG_CREATE_MASTER_PASSWORD = 0; final private int DIALOG_REMOVE_MASTER_PASSWORD = 1; + public static void setCharEncodingState(boolean enabled) { + sIsCharEncodingEnabled = enabled; + } + + public static boolean getCharEncodingState() { + return sIsCharEncodingEnabled; + } + @Override public boolean onPreferenceChange(Preference preference, Object newValue) { String prefName = preference.getKey(); if (prefName != null && prefName.equals("privacy.masterpassword.enabled")) { showDialog((Boolean)newValue ? DIALOG_CREATE_MASTER_PASSWORD : DIALOG_REMOVE_MASTER_PASSWORD); return false; + } else if (prefName != null && prefName.equals("browser.menu.showCharacterEncoding")) { + setCharEncodingState(((String) newValue).equals("true")); } setPreference(prefName, newValue); diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index 58acc9e92e30..f5dfeaf63901 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -51,7 +51,9 @@ - + + + @@ -72,6 +74,7 @@ + diff --git a/mobile/android/base/resources/layout/gecko_menu.xml b/mobile/android/base/resources/layout/gecko_menu.xml index 292fe7bd4f9a..2d0a0eb22c65 100644 --- a/mobile/android/base/resources/layout/gecko_menu.xml +++ b/mobile/android/base/resources/layout/gecko_menu.xml @@ -30,6 +30,10 @@ + + diff --git a/mobile/android/base/resources/values/arrays.xml b/mobile/android/base/resources/values/arrays.xml index 58b665e63518..75408d7bc1a3 100644 --- a/mobile/android/base/resources/values/arrays.xml +++ b/mobile/android/base/resources/values/arrays.xml @@ -25,4 +25,12 @@ 160 240 + + @string/pref_char_encoding_on + @string/pref_char_encoding_off + + + true + false + diff --git a/mobile/android/base/resources/xml/preferences.xml b/mobile/android/base/resources/xml/preferences.xml index 2bbce810e57e..384c01b4b996 100644 --- a/mobile/android/base/resources/xml/preferences.xml +++ b/mobile/android/base/resources/xml/preferences.xml @@ -15,8 +15,10 @@ - &pref_remember_signons; &pref_cookies; &pref_char_encoding; + &pref_char_encoding_on; + &pref_char_encoding_off; &pref_clear_history; &pref_clear_history_confirm; &pref_clear_private_data; @@ -78,6 +80,7 @@ &new_tab; &addons; &downloads; + &char_encoding; &site_settings_title; &site_settings_cancel; diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 7ed4860b6d6d..e915df9052c6 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -146,7 +146,8 @@ function resolveGeckoURI(aURI) { var Strings = {}; [ ["brand", "chrome://branding/locale/brand.properties"], - ["browser", "chrome://browser/locale/browser.properties"] + ["browser", "chrome://browser/locale/browser.properties"], + ["charset", "chrome://global/locale/charsetTitles.properties"] ].forEach(function (aStringBundle) { let [name, bundle] = aStringBundle; XPCOMUtils.defineLazyGetter(Strings, name, function() { @@ -252,6 +253,7 @@ var BrowserApp = { ConsoleAPI.init(); ClipboardHelper.init(); PermissionsHelper.init(); + CharacterEncoding.init(); // Init LoginManager Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager); @@ -357,6 +359,7 @@ var BrowserApp = { ViewportHandler.uninit(); XPInstallObserver.uninit(); ConsoleAPI.uninit(); + CharacterEncoding.uninit(); }, get tabs() { @@ -638,10 +641,6 @@ var BrowserApp = { pref.type = "bool"; pref.value = pref.value == 0; break; - case "browser.menu.showCharacterEncoding": - pref.type = "bool"; - pref.value = pref.value == "true"; - break; case "font.size.inflation.minTwips": pref.type = "string"; pref.value = pref.value.toString(); @@ -683,10 +682,6 @@ var BrowserApp = { json.type = "int"; json.value = (json.value ? 0 : 2); break; - case "browser.menu.showCharacterEncoding": - json.type = "string"; - json.value = (json.value ? "true" : "false"); - break; case "font.size.inflation.minTwips": json.type = "int"; json.value = parseInt(json.value); @@ -3819,7 +3814,7 @@ var PermissionsHelper = { Services.contentPrefs.removePref(aURI, aType + ".request.remember"); } } -} +}; var MasterPassword = { pref: "privacy.masterpassword.enabled", @@ -3899,4 +3894,99 @@ var MasterPassword = { } }); } -} +}; + +var CharacterEncoding = { + _charsets: [], + + init: function init() { + Services.obs.addObserver(this, "CharEncoding:Get", false); + Services.obs.addObserver(this, "CharEncoding:Set", false); + this.sendState(); + }, + + uninit: function uninit() { + Services.obs.removeObserver(this, "CharEncoding:Get", false); + Services.obs.removeObserver(this, "CharEncoding:Set", false); + }, + + observe: function observe(aSubject, aTopic, aData) { + switch (aTopic) { + case "CharEncoding:Get": + this.getEncoding(); + break; + case "CharEncoding:Set": + this.setEncoding(aData); + break; + } + }, + + sendState: function sendState() { + let showCharEncoding = "false"; + try { + showCharEncoding = Services.prefs.getComplexValue("browser.menu.showCharacterEncoding", Ci.nsIPrefLocalizedString).data; + } catch (e) { /* Optional */ } + + sendMessageToJava({ + gecko: { + type: "CharEncoding:State", + visible: showCharEncoding + } + }); + }, + + getEncoding: function getEncoding() { + function normalizeCharsetCode(charsetCode) { + return charsetCode.trim().toLowerCase(); + } + + function getTitle(charsetCode) { + let charsetTitle = charsetCode; + try { + charsetTitle = Strings.charset.GetStringFromName(charsetCode + ".title"); + } catch (e) { + dump("error: title not found for " + charsetCode); + } + return charsetTitle; + } + + if (!this._charsets.length) { + let charsets = Services.prefs.getComplexValue("intl.charsetmenu.browser.static", Ci.nsIPrefLocalizedString).data; + this._charsets = charsets.split(",").map(function (charset) { + return { + code: normalizeCharsetCode(charset), + title: getTitle(charset) + }; + }); + } + + // if document charset is not in charset options, add it + let docCharset = normalizeCharsetCode(BrowserApp.selectedBrowser.contentDocument.characterSet); + let selected = 0; + let charsetCount = this._charsets.length; + for (; selected < charsetCount && this._charsets[selected].code != docCharset; selected++); + if (selected == charsetCount) { + this._charsets.push({ + code: docCharset, + title: getTitle(docCharset) + }); + } + + sendMessageToJava({ + gecko: { + type: "CharEncoding:Data", + charsets: this._charsets, + selected: selected + } + }); + }, + + setEncoding: function setEncoding(aEncoding) { + let browser = BrowserApp.selectedBrowser; + let docCharset = browser.docShell.QueryInterface(Ci.nsIDocCharset); + docCharset.charset = aEncoding; + browser.reload(Ci.nsIWebNavigation.LOAD_FLAGS_CHARSET_CHANGE); + }, + +}; + diff --git a/mobile/android/locales/en-US/chrome/browser.properties b/mobile/android/locales/en-US/chrome/browser.properties index 6cb6d3321aac..78801141c6e5 100644 --- a/mobile/android/locales/en-US/chrome/browser.properties +++ b/mobile/android/locales/en-US/chrome/browser.properties @@ -172,7 +172,7 @@ browser.menu.showCharacterEncoding=false # LOCALIZATION NOTE (intl.charsetmenu.browser.static): Set to a series of comma separated # values for charsets that the user can select from in the Character Encoding menu. -intl.charsetmenu.browser.static=iso-8859-1,utf-8,x-gbk,big5,iso-2022-jp,shift_jis,euc-jp +intl.charsetmenu.browser.static=iso-8859-1,utf-8,big5,iso-2022-jp,shift_jis,euc-jp # Application Menu appMenu.more=More From 36e14bbce6697026e71050d1ce4078b12f8208c4 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 23 Jan 2012 13:51:01 -0800 Subject: [PATCH 25/58] Add JSOP_LOOPENTRY for target of initial loop jump, bug 720169. r=dvander --- js/src/frontend/BytecodeEmitter.cpp | 39 ++++++++++++++++++++++++----- js/src/jsanalyze.cpp | 4 ++- js/src/jsinfer.cpp | 1 + js/src/jsinterp.cpp | 1 + js/src/jsopcode.cpp | 2 ++ js/src/jsopcode.tbl | 3 +++ js/src/jsxdrapi.h | 2 +- js/src/methodjit/Compiler.cpp | 3 +++ js/src/methodjit/LoopState.cpp | 1 + 9 files changed, 48 insertions(+), 8 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 77710c2dd358..7028cbcf529c 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -539,7 +539,7 @@ UpdateLineNumberNotes(JSContext *cx, BytecodeEmitter *bce, uintN line) } static ptrdiff_t -EmitTraceOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn) +EmitLoopHead(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn) { if (nextpn) { /* @@ -557,6 +557,21 @@ EmitTraceOp(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn) return Emit1(cx, bce, JSOP_LOOPHEAD); } +static bool +EmitLoopEntry(JSContext *cx, BytecodeEmitter *bce, ParseNode *nextpn) +{ + if (nextpn) { + /* Update the line number, as for LOOPHEAD. */ + JS_ASSERT_IF(nextpn->isKind(PNK_STATEMENTLIST), nextpn->isArity(PN_LIST)); + if (nextpn->isKind(PNK_STATEMENTLIST) && nextpn->pn_head) + nextpn = nextpn->pn_head; + if (!UpdateLineNumberNotes(cx, bce, nextpn->pn_pos.begin.lineno)) + return false; + } + + return Emit1(cx, bce, JSOP_LOOPENTRY) >= 0; +} + /* * If op is JOF_TYPESET (see the type barriers comment in jsinfer.h), reserve * a type set to store its result. @@ -4831,7 +4846,7 @@ EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) top = bce->offset(); SET_STATEMENT_TOP(&stmtInfo, top); - if (EmitTraceOp(cx, bce, NULL) < 0) + if (EmitLoopHead(cx, bce, NULL) < 0) return false; #ifdef DEBUG @@ -4874,6 +4889,8 @@ EmitForIn(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) * Fixup the goto that starts the loop to jump down to JSOP_MOREITER. */ SetJumpOffsetAt(bce, jmp); + if (!EmitLoopEntry(cx, bce, NULL)) + return false; if (Emit1(cx, bce, JSOP_MOREITER) < 0) return false; ptrdiff_t beq = EmitJump(cx, bce, JSOP_IFNE, top - bce->offset()); @@ -4978,7 +4995,9 @@ EmitNormalFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) SET_STATEMENT_TOP(&stmtInfo, top); /* Emit code for the loop body. */ - if (EmitTraceOp(cx, bce, forBody) < 0) + if (EmitLoopHead(cx, bce, forBody) < 0) + return false; + if (jmp == -1 && !EmitLoopEntry(cx, bce, forBody)) return false; if (!EmitTree(cx, bce, forBody)) return false; @@ -5026,6 +5045,8 @@ EmitNormalFor(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) /* Fix up the goto from top to target the loop condition. */ JS_ASSERT(jmp >= 0); SetJumpOffsetAt(bce, jmp); + if (!EmitLoopEntry(cx, bce, forHead->pn_kid2)) + return false; if (!EmitTree(cx, bce, forHead->pn_kid2)) return false; @@ -5191,9 +5212,11 @@ EmitDo(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) return false; /* Compile the loop body. */ - ptrdiff_t top = EmitTraceOp(cx, bce, pn->pn_left); + ptrdiff_t top = EmitLoopHead(cx, bce, pn->pn_left); if (top < 0) return false; + if (!EmitLoopEntry(cx, bce, NULL)) + return false; StmtInfo stmtInfo; PushStatement(bce, &stmtInfo, STMT_DO_LOOP, top); @@ -5259,7 +5282,7 @@ EmitWhile(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) if (jmp < 0) return false; - top = EmitTraceOp(cx, bce, pn->pn_right); + top = EmitLoopHead(cx, bce, pn->pn_right); if (top < 0) return false; @@ -5267,6 +5290,8 @@ EmitWhile(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff_t top) return false; SetJumpOffsetAt(bce, jmp); + if (!EmitLoopEntry(cx, bce, pn->pn_left)) + return false; if (!EmitTree(cx, bce, pn->pn_left)) return false; @@ -6497,12 +6522,14 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) ptrdiff_t jmp = EmitJump(cx, bce, JSOP_FILTER, 0); if (jmp < 0) return JS_FALSE; - top = EmitTraceOp(cx, bce, pn->pn_right); + top = EmitLoopHead(cx, bce, pn->pn_right); if (top < 0) return JS_FALSE; if (!EmitTree(cx, bce, pn->pn_right)) return JS_FALSE; SetJumpOffsetAt(bce, jmp); + if (!EmitLoopEntry(cx, bce, NULL)) + return false; if (EmitJump(cx, bce, JSOP_ENDFILTER, top - bce->offset()) < 0) return JS_FALSE; break; diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index dd0c0870ed05..bfa7b0253378 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -815,7 +815,7 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) jsbytecode *entrypc = script->code + entry; - if (JSOp(*entrypc) == JSOP_GOTO) + if (JSOp(*entrypc) == JSOP_GOTO || JSOp(*entrypc) == JSOP_FILTER) loop->entry = entry + GET_JUMP_OFFSET(entrypc); else loop->entry = targetOffset; @@ -823,6 +823,8 @@ ScriptAnalysis::analyzeLifetimes(JSContext *cx) /* Do-while loop at the start of the script. */ loop->entry = targetOffset; } + JS_ASSERT(script->code[loop->entry] == JSOP_LOOPHEAD || + script->code[loop->entry] == JSOP_LOOPENTRY); } else { for (unsigned i = 0; i < savedCount; i++) { LifetimeVariable &var = *saved[i]; diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 3a2ab1de6173..52e9d8492e56 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -3430,6 +3430,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, case JSOP_POP: case JSOP_NOP: case JSOP_LOOPHEAD: + case JSOP_LOOPENTRY: case JSOP_GOTO: case JSOP_IFEQ: case JSOP_IFNE: diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 953d25625f6a..09d8dece7776 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1803,6 +1803,7 @@ ADD_EMPTY_CASE(JSOP_STARTXML) ADD_EMPTY_CASE(JSOP_STARTXMLEXPR) #endif ADD_EMPTY_CASE(JSOP_LOOPHEAD) +ADD_EMPTY_CASE(JSOP_LOOPENTRY) END_EMPTY_CASES BEGIN_CASE(JSOP_LABEL) diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index be175fa34112..1adb819a660d 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -3921,6 +3921,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) next = js_GetSrcNoteOffset(sn, 0); tail = js_GetSrcNoteOffset(sn, 1); JS_ASSERT(pc[next] == JSOP_POP); + JS_ASSERT(pc[cond] == JSOP_LOOPENTRY); + cond += JSOP_LOOPENTRY_LENGTH; JS_ASSERT(pc[cond] == JSOP_MOREITER); DECOMPILE_CODE(pc + oplen, next - oplen); lval = POP_STR(); diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 4bbe2ed3b60f..da30f0b71209 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -572,3 +572,6 @@ OPDEF(JSOP_TOID, 225, "toid", NULL, 1, 1, 1, 0, JOF_BYTE) /* Push the implicit 'this' value for calls to the associated name. */ OPDEF(JSOP_IMPLICITTHIS, 226, "implicitthis", "", 3, 0, 1, 0, JOF_ATOM) + +/* This opcode is the target of the entry jump for some loop. */ +OPDEF(JSOP_LOOPENTRY, 227, "loopentry", NULL, 1, 0, 0, 0, JOF_BYTE) diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index ea8b3651e720..d5155aa4b1aa 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -226,7 +226,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32_t id); * and saved versions. If deserialization fails, the data should be * invalidated if possible. */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 103) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 104) /* * Library-private functions. diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index e8681692cbcb..1b31ecdaeba4 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -3226,6 +3226,9 @@ mjit::Compiler::generateMethod() } END_CASE(JSOP_LOOPHEAD) + BEGIN_CASE(JSOP_LOOPENTRY) + END_CASE(JSOP_LOOPENTRY) + BEGIN_CASE(JSOP_DEBUGGER) { prepareStubCall(Uses(0)); diff --git a/js/src/methodjit/LoopState.cpp b/js/src/methodjit/LoopState.cpp index 0f08bb6f4cab..3e74ca4fbc65 100644 --- a/js/src/methodjit/LoopState.cpp +++ b/js/src/methodjit/LoopState.cpp @@ -1897,6 +1897,7 @@ LoopState::analyzeLoopBody(unsigned frame) break; case JSOP_LOOPHEAD: + case JSOP_LOOPENTRY: case JSOP_POP: case JSOP_ZERO: case JSOP_ONE: From 651ffa594bbbbbf4b37f1fa789376e1652072a91 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 23 Jan 2012 13:59:04 -0800 Subject: [PATCH 26/58] Add incremental GC barrier for generator frames associated with args/call objects, bug 716013. r=billm --- js/src/jit-test/tests/basic/bug716013.js | 4 ++++ js/src/vm/Stack.cpp | 11 +++++++++++ 2 files changed, 15 insertions(+) create mode 100644 js/src/jit-test/tests/basic/bug716013.js diff --git a/js/src/jit-test/tests/basic/bug716013.js b/js/src/jit-test/tests/basic/bug716013.js new file mode 100644 index 000000000000..b6ff2f991de1 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug716013.js @@ -0,0 +1,4 @@ +f = (function() { + for (x in [arguments, arguments]) yield(gczeal(4, function(){})) +}) +for (i in f()) {} diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 8473f2eb86cc..69770b66ce09 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -794,6 +794,17 @@ ContextStack::pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrame gfg->gen_ = gen; gfg->stackvp_ = stackvp; + /* + * Trigger incremental barrier on the floating frame's generator object. + * This is normally traced through only by associated arguments/call + * objects, but only when the generator is not actually on the stack. + * We don't need to worry about generational barriers as the generator + * object has a trace hook and cannot be nursery allocated. + */ + JSObject *genobj = js_FloatingFrameToGenerator(genfp)->obj; + JS_ASSERT(genobj->getClass()->trace); + JSObject::writeBarrierPre(genobj); + /* Copy from the generator's floating frame to the stack. */ stackfp->stealFrameAndSlots(stackvp, genfp, genvp, gen->regs.sp); stackfp->resetGeneratorPrev(cx); From f2bfc44f6565443c406c515da6a67061051ffc26 Mon Sep 17 00:00:00 2001 From: Steven Michaud Date: Mon, 23 Jan 2012 16:18:26 -0600 Subject: [PATCH 27/58] Bug 714951 - [Mac] Aurora and Nightly icons incorrectly positioned in mounted DMG image's finder window. r=gavin --- browser/branding/aurora/dsstore | Bin 15364 -> 15364 bytes browser/branding/nightly/dsstore | Bin 15364 -> 15364 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/browser/branding/aurora/dsstore b/browser/branding/aurora/dsstore index 85eb545601f66f0aada8d7d02144ae7627ef53aa..c09d6c1344f7b281220ca2e9c3a55b15e97c8472 100644 GIT binary patch delta 60 zcmZpvXsMXs&&aniU^hP_-)0^GQ7(Q#1~-OGh9ZVkhBStJhKkAWB$c_785kHA{Re}M Ng)3P%vn%|S1^_9T6)OM$ delta 28 kcmZpvXsMXs&&azmU^hP_?`9qWQLc?;&saCJEBuuP0FR;xu>b%7 diff --git a/browser/branding/nightly/dsstore b/browser/branding/nightly/dsstore index ea1c05db0dc9a467a75998c7673ce327a8b01971..360fd08066b0a0f695363cabafc776db28210f48 100644 GIT binary patch delta 63 zcmZpvXsMXs&&a Date: Mon, 23 Jan 2012 17:21:23 -0500 Subject: [PATCH 28/58] Bug 716590 - WalkStackMain64 shouldn't skip frames unless it's walking the thread that called NS_StackWalk. r=dbaron --- xpcom/base/nsStackWalk.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/xpcom/base/nsStackWalk.cpp b/xpcom/base/nsStackWalk.cpp index 7126180a0720..d31cfb23bd4e 100644 --- a/xpcom/base/nsStackWalk.cpp +++ b/xpcom/base/nsStackWalk.cpp @@ -338,6 +338,7 @@ BOOL SymGetModuleInfoEspecial(HANDLE aProcess, DWORD aAddr, PIMAGEHLP_MODULE aMo struct WalkStackData { PRUint32 skipFrames; HANDLE thread; + bool walkCallingThread; HANDLE process; HANDLE eventStart; HANDLE eventEnd; @@ -564,7 +565,8 @@ WalkStackMain64(struct WalkStackData* data) HANDLE myThread = data->thread; DWORD64 addr; STACKFRAME64 frame64; - int skip = 3 + data->skipFrames; // skip our own stack walking frames + // skip our own stack walking frames + int skip = (data->walkCallingThread ? 3 : 0) + data->skipFrames; BOOL ok; // Get a context for the specified thread. @@ -812,11 +814,13 @@ NS_StackWalk(NS_WalkStackCallback aCallback, PRUint32 aSkipFrames, if (!EnsureImageHlpInitialized()) return false; - HANDLE targetThread; + HANDLE targetThread = ::GetCurrentThread(); + data.walkCallingThread = true; if (aThread) { - targetThread = reinterpret_cast (aThread); - } else { - targetThread = ::GetCurrentThread(); + HANDLE threadToWalk = reinterpret_cast (aThread); + // walkCallingThread indicates whether we are walking the caller's stack + data.walkCallingThread = (threadToWalk == targetThread); + targetThread = threadToWalk; } // Have to duplicate handle to get a real handle. From e532ca2ebfc14e59de7b90c077a511152fcc380e Mon Sep 17 00:00:00 2001 From: Steven Michaud Date: Mon, 23 Jan 2012 16:37:40 -0600 Subject: [PATCH 29/58] Bug 720168 - Crash in nsHttpChannel::CallOnStartRequest @ objc_msgSend | nsOSHelperAppService::GetMIMEInfoFromOS. r=bgirard --- uriloader/exthandler/mac/nsOSHelperAppService.mm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/uriloader/exthandler/mac/nsOSHelperAppService.mm b/uriloader/exthandler/mac/nsOSHelperAppService.mm index 13e865d2c398..53f00865f16d 100644 --- a/uriloader/exthandler/mac/nsOSHelperAppService.mm +++ b/uriloader/exthandler/mac/nsOSHelperAppService.mm @@ -275,14 +275,15 @@ static CFArrayRef GetMIMETypesHandledByApp(FSRef *aAppRef) return NULL; } CFTypeRef cfObject = ::CFDictionaryGetValue(infoDict, CFSTR("CFBundleDocumentTypes")); - ::CFRelease(infoDict); if (!cfObject || (::CFGetTypeID(cfObject) != ::CFArrayGetTypeID())) { + ::CFRelease(infoDict); return NULL; } CFArrayRef docTypes = static_cast(cfObject); CFIndex docTypesCount = ::CFArrayGetCount(docTypes); if (docTypesCount == 0) { + ::CFRelease(infoDict); return NULL; } @@ -312,6 +313,7 @@ static CFArrayRef GetMIMETypesHandledByApp(FSRef *aAppRef) ::CFRangeMake(0, ::CFArrayGetCount(mimeTypeHolder))); } + ::CFRelease(infoDict); if (!::CFArrayGetCount(mimeTypes)) { ::CFRelease(mimeTypes); mimeTypes = NULL; From 6691a3ab2c94ee1f355c5298b149a0d952ce4f00 Mon Sep 17 00:00:00 2001 From: Brian Nicholson Date: Mon, 23 Jan 2012 14:39:04 -0800 Subject: [PATCH 30/58] Bug 716906 - Add "Firefox Sync" item to settings. r=dougt --- mobile/android/base/Makefile.in | 1 + mobile/android/base/SyncPreference.java.in | 81 +++++++++++++++++++ .../base/locales/en-US/android_strings.dtd | 1 + .../base/resources/xml/preferences.xml | 5 +- mobile/android/base/strings.xml.in | 1 + 5 files changed, 86 insertions(+), 3 deletions(-) create mode 100644 mobile/android/base/SyncPreference.java.in diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 328c0f5e421f..1fd37c12bf9a 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -141,6 +141,7 @@ FENNEC_PP_JAVA_FILES = \ db/BrowserContract.java \ db/BrowserProvider.java \ SmsManager.java \ + SyncPreference.java \ $(NULL) diff --git a/mobile/android/base/SyncPreference.java.in b/mobile/android/base/SyncPreference.java.in new file mode 100644 index 000000000000..e510c33b200e --- /dev/null +++ b/mobile/android/base/SyncPreference.java.in @@ -0,0 +1,81 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * ***** 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 Android code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009-2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Brian Nicholson + * + * 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 ***** */ + +#filter substitution +package org.mozilla.gecko; + +import android.accounts.Account; +import android.accounts.AccountManager; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.preference.Preference; +import android.util.AttributeSet; +import android.util.Log; + +class SyncPreference extends Preference { + private static final String FEEDS_PACKAGE_NAME = "com.android.providers.subscribedfeeds"; + private static final String ACCOUNT_SYNC_CLASS_NAME = "com.android.settings.AccountSyncSettings"; + private static final String ACCOUNT_KEY = "account"; + private static final String FENNEC_PACKAGE_NAME = "@ANDROID_PACKAGE_NAME@"; + private static final String FENNEC_SYNC_CLASS_NAME = "org.mozilla.gecko.sync.setup.activities.SetupSyncActivity"; + private static final String FENNEC_ACCOUNT_TYPE = "org.mozilla.firefox_sync"; + + private Context mContext; + + public SyncPreference(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + } + + @Override + protected void onClick() { + Intent intent = new Intent(Intent.ACTION_MAIN); + Account[] accounts = AccountManager.get(mContext).getAccountsByType(FENNEC_ACCOUNT_TYPE); + if (accounts.length > 0) { + // show sync account + // we assume there's exactly one sync account. see bugs 716906 and 710407. + intent.setComponent(new ComponentName(FEEDS_PACKAGE_NAME, ACCOUNT_SYNC_CLASS_NAME)); + Account account = accounts[0]; + intent.putExtra(ACCOUNT_KEY, account); + } else { + // show sync setup + intent.setComponent(new ComponentName(FENNEC_PACKAGE_NAME, FENNEC_SYNC_CLASS_NAME)); + } + mContext.startActivity(intent); + } +} diff --git a/mobile/android/base/locales/en-US/android_strings.dtd b/mobile/android/base/locales/en-US/android_strings.dtd index f5dfeaf63901..a4402ddb6012 100644 --- a/mobile/android/base/locales/en-US/android_strings.dtd +++ b/mobile/android/base/locales/en-US/android_strings.dtd @@ -69,6 +69,7 @@ + diff --git a/mobile/android/base/resources/xml/preferences.xml b/mobile/android/base/resources/xml/preferences.xml index 384c01b4b996..1f2ebe9a3384 100644 --- a/mobile/android/base/resources/xml/preferences.xml +++ b/mobile/android/base/resources/xml/preferences.xml @@ -7,9 +7,8 @@ - - - + diff --git a/mobile/android/base/strings.xml.in b/mobile/android/base/strings.xml.in index 05cd4671d237..5b2b7c90902d 100644 --- a/mobile/android/base/strings.xml.in +++ b/mobile/android/base/strings.xml.in @@ -74,6 +74,7 @@ &pref_font_size_medium; &pref_font_size_large; &pref_font_size_xlarge; + &pref_sync; &reload; &forward; From 78b91b84fcf2b772350c6aef1734f0a356172ee6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hub=20Figui=C3=A8re?= Date: Mon, 23 Jan 2012 14:37:21 -0800 Subject: [PATCH 31/58] Bug 712923 - Properly handle Heading elements to be recognizable by VoiceOver. r=tbsaunde,surkov,marcoz --HG-- extra : rebase_source : 111fa34bc3f12646f68d00a73d1717f822c196dd --- accessible/src/mac/Makefile.in | 1 + accessible/src/mac/mozHTMLAccessible.h | 45 ++++++++++++++++++++ accessible/src/mac/mozHTMLAccessible.mm | 56 +++++++++++++++++++++++++ accessible/src/mac/nsAccessibleWrap.mm | 7 +++- accessible/src/mac/nsRoleMap.h | 2 +- 5 files changed, 108 insertions(+), 3 deletions(-) create mode 100644 accessible/src/mac/mozHTMLAccessible.h create mode 100644 accessible/src/mac/mozHTMLAccessible.mm diff --git a/accessible/src/mac/Makefile.in b/accessible/src/mac/Makefile.in index 5ab96daec8c2..67dc22a18934 100644 --- a/accessible/src/mac/Makefile.in +++ b/accessible/src/mac/Makefile.in @@ -56,6 +56,7 @@ CMMSRCS = nsAccessNodeWrap.mm \ mozDocAccessible.mm \ mozActionElements.mm \ mozTextAccessible.mm \ + mozHTMLAccessible.mm \ $(NULL) diff --git a/accessible/src/mac/mozHTMLAccessible.h b/accessible/src/mac/mozHTMLAccessible.h new file mode 100644 index 000000000000..a4935e795124 --- /dev/null +++ b/accessible/src/mac/mozHTMLAccessible.h @@ -0,0 +1,45 @@ +/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=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 + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Original Author: Hub Figuière + * + * 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 ***** */ + +#import "mozAccessible.h" + +@interface mozHeadingAccessible : mozAccessible + +@end diff --git a/accessible/src/mac/mozHTMLAccessible.mm b/accessible/src/mac/mozHTMLAccessible.mm new file mode 100644 index 000000000000..12e8331ce9f2 --- /dev/null +++ b/accessible/src/mac/mozHTMLAccessible.mm @@ -0,0 +1,56 @@ +/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:expandtab:shiftwidth=2:tabstop=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 + * Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Original Author: Hub Figuière + * + * 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 ***** */ + +#import "mozHTMLAccessible.h" + +#import "nsHyperTextAccessible.h" + +@implementation mozHeadingAccessible + +- (id)value +{ + if (!mGeckoAccessible || !mGeckoAccessible->IsHyperText()) + return nil; + + PRUint32 level = mGeckoAccessible->AsHyperText()->GetLevelInternal(); + return [NSNumber numberWithInt:level]; +} + +@end diff --git a/accessible/src/mac/nsAccessibleWrap.mm b/accessible/src/mac/nsAccessibleWrap.mm index a88ced2a6250..ae3e1d717f12 100644 --- a/accessible/src/mac/nsAccessibleWrap.mm +++ b/accessible/src/mac/nsAccessibleWrap.mm @@ -45,6 +45,7 @@ #import "mozAccessible.h" #import "mozActionElements.h" +#import "mozHTMLAccessible.h" #import "mozTextAccessible.h" using namespace mozilla::a11y; @@ -108,10 +109,12 @@ nsAccessibleWrap::GetNativeType () case roles::AUTOCOMPLETE: return [mozComboboxAccessible class]; - + + case roles::HEADING: + return [mozHeadingAccessible class]; + case roles::ENTRY: case roles::STATICTEXT: - case roles::HEADING: case roles::LABEL: case roles::CAPTION: case roles::ACCEL_LABEL: diff --git a/accessible/src/mac/nsRoleMap.h b/accessible/src/mac/nsRoleMap.h index 34b2c2e315a2..9e83b94dbd01 100644 --- a/accessible/src/mac/nsRoleMap.h +++ b/accessible/src/mac/nsRoleMap.h @@ -147,7 +147,7 @@ static const NSString* AXRoles [] = { NSAccessibilityTextFieldRole, // ROLE_ENTRY NSAccessibilityStaticTextRole, // ROLE_CAPTION @"AXWebArea", // ROLE_DOCUMENT_FRAME - NSAccessibilityStaticTextRole, // ROLE_HEADING + @"AXHeading", // ROLE_HEADING NSAccessibilityGroupRole, // ROLE_PAGE NSAccessibilityGroupRole, // ROLE_SECTION NSAccessibilityUnknownRole, // ROLE_REDUNDANT_OBJECT From 821eb101a0fc2bd86d1f2f93402002725fdf6684 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Mon, 23 Jan 2012 15:25:53 -0800 Subject: [PATCH 32/58] Bug 720292 - add more cycle collector edge names. r=smaug --- content/xbl/src/nsXBLBinding.cpp | 2 ++ content/xbl/src/nsXBLDocumentInfo.cpp | 1 + content/xbl/src/nsXBLPrototypeBinding.cpp | 6 +++++- content/xul/content/src/nsXULElement.cpp | 7 ++++++- js/xpconnect/src/XPCVariant.cpp | 4 +++- js/xpconnect/src/XPCWrappedJS.cpp | 12 +++++++++--- js/xpconnect/src/nsXPConnect.cpp | 2 +- xpcom/ds/nsVariant.cpp | 5 ++++- 8 files changed, 31 insertions(+), 8 deletions(-) diff --git a/content/xbl/src/nsXBLBinding.cpp b/content/xbl/src/nsXBLBinding.cpp index 56cc1fc90237..3d86f4ab90fe 100644 --- a/content/xbl/src/nsXBLBinding.cpp +++ b/content/xbl/src/nsXBLBinding.cpp @@ -309,6 +309,8 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_NATIVE(nsXBLBinding) tmp->mInsertionPointTable = nsnull; NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXBLBinding) + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, + "mPrototypeBinding->XBLDocumentInfo()"); cb.NoteXPCOMChild(static_cast( tmp->mPrototypeBinding->XBLDocumentInfo())); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mContent) diff --git a/content/xbl/src/nsXBLDocumentInfo.cpp b/content/xbl/src/nsXBLDocumentInfo.cpp index d60ea8657952..7fc05f637a05 100644 --- a/content/xbl/src/nsXBLDocumentInfo.cpp +++ b/content/xbl/src/nsXBLDocumentInfo.cpp @@ -476,6 +476,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsXBLDocumentInfo) if (tmp->mBindingTable) { tmp->mBindingTable->Enumerate(TraverseProtos, &cb); } + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mGlobalObject"); cb.NoteXPCOMChild(static_cast(tmp->mGlobalObject)); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END diff --git a/content/xbl/src/nsXBLPrototypeBinding.cpp b/content/xbl/src/nsXBLPrototypeBinding.cpp index c7948e3bc247..eba7aa5473fb 100644 --- a/content/xbl/src/nsXBLPrototypeBinding.cpp +++ b/content/xbl/src/nsXBLPrototypeBinding.cpp @@ -385,6 +385,7 @@ TraverseBinding(nsHashKey *aKey, void *aData, void* aClosure) { nsCycleCollectionTraversalCallback *cb = static_cast(aClosure); + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME((*cb), "proto mInterfaceTable data"); cb->NoteXPCOMChild(static_cast(aData)); return kHashEnumerateNext; } @@ -392,9 +393,12 @@ TraverseBinding(nsHashKey *aKey, void *aData, void* aClosure) void nsXBLPrototypeBinding::Traverse(nsCycleCollectionTraversalCallback &cb) const { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mBinding"); cb.NoteXPCOMChild(mBinding); - if (mResources) + if (mResources) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "proto mResources mLoader"); cb.NoteXPCOMChild(mResources->mLoader); + } if (mInsertionPointTable) mInsertionPointTable->Enumerate(TraverseInsertionPoint, &cb); if (mInterfaceTable) diff --git a/content/xul/content/src/nsXULElement.cpp b/content/xul/content/src/nsXULElement.cpp index 59b7d3482c29..5d5a94fe253b 100644 --- a/content/xul/content/src/nsXULElement.cpp +++ b/content/xul/content/src/nsXULElement.cpp @@ -705,6 +705,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsScriptEventHandlerOwnerTearoff) tmp->mElement = nsnull; NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsScriptEventHandlerOwnerTearoff) + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mElement"); cb.NoteXPCOMChild(static_cast(tmp->mElement)); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END @@ -2545,12 +2546,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_BEGIN(nsXULPrototypeNode) if (tmp->mType == nsXULPrototypeNode::eType_Element) { nsXULPrototypeElement *elem = static_cast(tmp); + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mNodeInfo"); cb.NoteXPCOMChild(elem->mNodeInfo); PRUint32 i; for (i = 0; i < elem->mNumAttributes; ++i) { const nsAttrName& name = elem->mAttributes[i].mName; - if (!name.IsAtom()) + if (!name.IsAtom()) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, + "mAttributes[i].mName.NodeInfo()"); cb.NoteXPCOMChild(name.NodeInfo()); + } } for (i = 0; i < elem->mChildren.Length(); ++i) { NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NATIVE_PTR(elem->mChildren[i].get(), diff --git a/js/xpconnect/src/XPCVariant.cpp b/js/xpconnect/src/XPCVariant.cpp index 24d9c8c4149e..a72ec3e2c7d6 100644 --- a/js/xpconnect/src/XPCVariant.cpp +++ b/js/xpconnect/src/XPCVariant.cpp @@ -118,9 +118,11 @@ XPCTraceableVariant::PrintTraceName(JSTracer* trc, char *buf, size_t bufsize) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(XPCVariant) jsval val = tmp->GetJSValPreserveColor(); - if (JSVAL_IS_OBJECT(val)) + if (JSVAL_IS_OBJECT(val)) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSVal"); cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, JSVAL_TO_OBJECT(val)); + } nsVariant::Traverse(tmp->mData, cb); NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END diff --git a/js/xpconnect/src/XPCWrappedJS.cpp b/js/xpconnect/src/XPCWrappedJS.cpp index 2d396775ec3d..bccfa81d7f59 100644 --- a/js/xpconnect/src/XPCWrappedJS.cpp +++ b/js/xpconnect/src/XPCWrappedJS.cpp @@ -75,21 +75,27 @@ NS_CYCLE_COLLECTION_CLASSNAME(nsXPCWrappedJS)::Traverse // nsXPCWrappedJS keeps its own refcount artificially at or above 1, see the // comment above nsXPCWrappedJS::AddRef. + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "self"); cb.NoteXPCOMChild(s); - if (refcnt > 1) + if (refcnt > 1) { // nsXPCWrappedJS roots its mJSObj when its refcount is > 1, see // the comment above nsXPCWrappedJS::AddRef. + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mJSObj"); cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, tmp->GetJSObjectPreserveColor()); + } nsXPCWrappedJS* root = tmp->GetRootWrapper(); - if (root == tmp) + if (root == tmp) { // The root wrapper keeps the aggregated native object alive. + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "aggregated native"); cb.NoteXPCOMChild(tmp->GetAggregatedNativeObject()); - else + } else { // Non-root wrappers keep their root alive. + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "root"); cb.NoteXPCOMChild(static_cast(root)); + } return NS_OK; } diff --git a/js/xpconnect/src/nsXPConnect.cpp b/js/xpconnect/src/nsXPConnect.cpp index faccd7bccc66..324fba22cbce 100644 --- a/js/xpconnect/src/nsXPConnect.cpp +++ b/js/xpconnect/src/nsXPConnect.cpp @@ -1023,8 +1023,8 @@ public: // collected. unsigned refCount = nsXPConnect::GetXPConnect()->GetOutstandingRequests(cx) + 1; cb.DescribeRefCountedNode(refCount, js::SizeOfJSContext(), "JSContext"); - NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[global object]"); if (JSObject *global = JS_GetGlobalObject(cx)) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "[global object]"); cb.NoteScriptChild(nsIProgrammingLanguage::JAVASCRIPT, global); } diff --git a/xpcom/ds/nsVariant.cpp b/xpcom/ds/nsVariant.cpp index a87f139fd912..389848bf99d9 100644 --- a/xpcom/ds/nsVariant.cpp +++ b/xpcom/ds/nsVariant.cpp @@ -1668,6 +1668,7 @@ nsVariant::Traverse(const nsDiscriminatedUnion& data, { case nsIDataType::VTYPE_INTERFACE: case nsIDataType::VTYPE_INTERFACE_IS: + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mData"); cb.NoteXPCOMChild(data.u.iface.mInterfaceValue); break; case nsIDataType::VTYPE_ARRAY: @@ -1676,8 +1677,10 @@ nsVariant::Traverse(const nsDiscriminatedUnion& data, case nsIDataType::VTYPE_INTERFACE_IS: { nsISupports** p = (nsISupports**) data.u.array.mArrayValue; - for(PRUint32 i = data.u.array.mArrayCount; i > 0; p++, i--) + for(PRUint32 i = data.u.array.mArrayCount; i > 0; p++, i--) { + NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mData[i]"); cb.NoteXPCOMChild(*p); + } } default: break; From b8f1a573ae8dee5888c4282f1db59196b33787b4 Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Mon, 23 Jan 2012 18:28:58 -0500 Subject: [PATCH 33/58] Bug 719202 - Innocuous uninitialized BMP header value. r=joe --- image/decoders/nsBMPDecoder.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/image/decoders/nsBMPDecoder.cpp b/image/decoders/nsBMPDecoder.cpp index 81893d7a9b42..1e369d7e9da6 100644 --- a/image/decoders/nsBMPDecoder.cpp +++ b/image/decoders/nsBMPDecoder.cpp @@ -426,7 +426,8 @@ nsBMPDecoder::WriteInternal(const char* aBuffer, PRUint32 aCount) aBuffer += toCopy; aCount -= toCopy; } - if (mBIH.compression == BI_BITFIELDS && mPos == WIN_HEADER_LENGTH + BITFIELD_LENGTH) { + if (mPos == WIN_HEADER_LENGTH + BITFIELD_LENGTH && + mBIH.compression == BI_BITFIELDS) { mBitFields.red = LITTLE_TO_NATIVE32(*(PRUint32*)mRawBuf); mBitFields.green = LITTLE_TO_NATIVE32(*(PRUint32*)(mRawBuf + 4)); mBitFields.blue = LITTLE_TO_NATIVE32(*(PRUint32*)(mRawBuf + 8)); From b8a9c0671ad5e572ef998984b32c7436a1f9dd40 Mon Sep 17 00:00:00 2001 From: Steve Workman Date: Fri, 20 Jan 2012 15:14:46 -0800 Subject: [PATCH 34/58] Bug 622232: Cancel DNS prefetches for HTML Anchor Elems after a tab is closed; r=mcmanus sr=bz --- content/base/src/Link.h | 22 +++- .../html/content/src/nsHTMLAnchorElement.cpp | 52 ++++++++- .../html/content/src/nsHTMLDNSPrefetch.cpp | 101 +++++++++++++++--- content/html/content/src/nsHTMLDNSPrefetch.h | 17 ++- netwerk/dns/nsDNSService2.cpp | 54 ++++++++++ netwerk/dns/nsHostResolver.cpp | 44 ++++++++ netwerk/dns/nsHostResolver.h | 27 +++++ netwerk/dns/nsIDNSService.idl | 22 +++- netwerk/ipc/NeckoParent.cpp | 12 ++- netwerk/ipc/NeckoParent.h | 4 + netwerk/ipc/PNecko.ipdl | 1 + 11 files changed, 328 insertions(+), 28 deletions(-) diff --git a/content/base/src/Link.h b/content/base/src/Link.h index 67ed3c16a056..c193f2c88752 100644 --- a/content/base/src/Link.h +++ b/content/base/src/Link.h @@ -51,8 +51,8 @@ namespace mozilla { namespace dom { #define MOZILLA_DOM_LINK_IMPLEMENTATION_IID \ - { 0xa687a99c, 0x3893, 0x45c0, \ - {0x8e, 0xab, 0xb8, 0xf7, 0xd7, 0x9e, 0x9e, 0x7b } } + { 0x7EA57721, 0xE373, 0x458E, \ + {0x8F, 0x44, 0xF8, 0x96, 0x56, 0xB4, 0x14, 0xF5 } } class Link : public nsISupports { @@ -113,6 +113,24 @@ public: // This method nevers returns a null element. Element* GetElement() const { return mElement; } + /** + * DNS prefetch has been deferred until later, e.g. page load complete. + */ + virtual void OnDNSPrefetchDeferred() { /*do nothing*/ } + + /** + * DNS prefetch has been submitted to Host Resolver. + */ + virtual void OnDNSPrefetchRequested() { /*do nothing*/ } + + /** + * Checks if DNS Prefetching is ok + * + * @returns boolean + * Defaults to true; should be overridden for specialised cases + */ + virtual bool HasDeferredDNSPrefetchRequest() { return true; } + protected: virtual ~Link(); diff --git a/content/html/content/src/nsHTMLAnchorElement.cpp b/content/html/content/src/nsHTMLAnchorElement.cpp index af5fbca85742..e86473291113 100644 --- a/content/html/content/src/nsHTMLAnchorElement.cpp +++ b/content/html/content/src/nsHTMLAnchorElement.cpp @@ -140,14 +140,27 @@ public: virtual nsEventStates IntrinsicState() const; virtual nsXPCClassInfo* GetClassInfo(); + + virtual void OnDNSPrefetchDeferred(); + virtual void OnDNSPrefetchRequested(); + virtual bool HasDeferredDNSPrefetchRequest(); }; +// Indicates that a DNS Prefetch has been requested from this Anchor elem +#define HTML_ANCHOR_DNS_PREFETCH_REQUESTED \ + (1 << ELEMENT_TYPE_SPECIFIC_BITS_OFFSET) +// Indicates that a DNS Prefetch was added to the deferral queue +#define HTML_ANCHOR_DNS_PREFETCH_DEFERRED \ + (1 << ELEMENT_TYPE_SPECIFIC_BITS_OFFSET+1) + +// Make sure we have enough space for those bits +PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET+1 < 32); NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor) nsHTMLAnchorElement::nsHTMLAnchorElement(already_AddRefed aNodeInfo) - : nsGenericHTMLElement(aNodeInfo), - Link(this) + : nsGenericHTMLElement(aNodeInfo) + , Link(this) { } @@ -202,6 +215,26 @@ nsHTMLAnchorElement::GetDraggable(bool* aDraggable) return nsGenericHTMLElement::GetDraggable(aDraggable); } +void +nsHTMLAnchorElement::OnDNSPrefetchRequested() +{ + UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); + SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); +} + +void +nsHTMLAnchorElement::OnDNSPrefetchDeferred() +{ + UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); + SetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); +} + +bool +nsHTMLAnchorElement::HasDeferredDNSPrefetchRequest() +{ + return HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); +} + nsresult nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, @@ -228,6 +261,21 @@ nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, void nsHTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent) { + // Cancel any DNS prefetches + // Note: Must come before ResetLinkState. If called after, it will recreate + // mCachedURI based on data that is invalid - due to a call to GetHostname. + + // If prefetch was deferred, clear flag and move on + if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED)) + UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); + // Else if prefetch was requested, clear flag and send cancellation + else if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_REQUESTED)) { + UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); + // Possible that hostname could have changed since binding, but since this + // covers common cases, most DNS prefetch requests will be canceled + nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT); + } + // If this link is ever reinserted into a document, it might // be under a different xml:base, so forget the cached state now. Link::ResetLinkState(false); diff --git a/content/html/content/src/nsHTMLDNSPrefetch.cpp b/content/html/content/src/nsHTMLDNSPrefetch.cpp index 56148e91c630..7365f334c8fe 100644 --- a/content/html/content/src/nsHTMLDNSPrefetch.cpp +++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp @@ -165,7 +165,7 @@ nsHTMLDNSPrefetch::PrefetchHigh(Link *aElement) } nsresult -nsHTMLDNSPrefetch::Prefetch(nsAString &hostname, PRUint16 flags) +nsHTMLDNSPrefetch::Prefetch(const nsAString &hostname, PRUint16 flags) { if (IsNeckoChild()) { // We need to check IsEmpty() because net_IsValidHostName() @@ -181,28 +181,85 @@ nsHTMLDNSPrefetch::Prefetch(nsAString &hostname, PRUint16 flags) return NS_ERROR_NOT_AVAILABLE; nsCOMPtr tmpOutstanding; - return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), flags | nsIDNSService::RESOLVE_SPECULATE, - sDNSListener, nsnull, getter_AddRefs(tmpOutstanding)); + return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), + flags | nsIDNSService::RESOLVE_SPECULATE, + sDNSListener, nsnull, + getter_AddRefs(tmpOutstanding)); } nsresult -nsHTMLDNSPrefetch::PrefetchLow(nsAString &hostname) +nsHTMLDNSPrefetch::PrefetchLow(const nsAString &hostname) { return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW); } nsresult -nsHTMLDNSPrefetch::PrefetchMedium(nsAString &hostname) +nsHTMLDNSPrefetch::PrefetchMedium(const nsAString &hostname) { return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_MEDIUM); } nsresult -nsHTMLDNSPrefetch::PrefetchHigh(nsAString &hostname) +nsHTMLDNSPrefetch::PrefetchHigh(const nsAString &hostname) { return Prefetch(hostname, 0); } +nsresult +nsHTMLDNSPrefetch::CancelPrefetch(Link *aElement, + PRUint16 flags, + nsresult aReason) +{ + if (!(sInitialized && sPrefetches && sDNSService && sDNSListener)) + return NS_ERROR_NOT_AVAILABLE; + + nsAutoString hostname; + nsresult rv = aElement->GetHostname(hostname); + return CancelPrefetch(hostname, flags, aReason); +} + +nsresult +nsHTMLDNSPrefetch::CancelPrefetch(const nsAString &hostname, + PRUint16 flags, + nsresult aReason) +{ + // Forward this request to Necko Parent if we're a child process + if (IsNeckoChild()) { + // We need to check IsEmpty() because net_IsValidHostName() + // considers empty strings to be valid hostnames + if (!hostname.IsEmpty() && + net_IsValidHostName(NS_ConvertUTF16toUTF8(hostname))) { + gNeckoChild->SendCancelHTMLDNSPrefetch(nsString(hostname), flags, + aReason); + } + return NS_OK; + } + + if (!(sInitialized && sDNSService && sPrefetches && sDNSListener)) + return NS_ERROR_NOT_AVAILABLE; + + // Forward cancellation to DNS service + return sDNSService->CancelAsyncResolve(NS_ConvertUTF16toUTF8(hostname), + flags + | nsIDNSService::RESOLVE_SPECULATE, + sDNSListener, aReason); +} + +nsresult +nsHTMLDNSPrefetch::CancelPrefetchLow(Link *aElement, nsresult aReason) +{ + return CancelPrefetch(aElement, nsIDNSService::RESOLVE_PRIORITY_LOW, + aReason); +} + +nsresult +nsHTMLDNSPrefetch::CancelPrefetchLow(const nsAString &hostname, nsresult aReason) +{ + return CancelPrefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW, + aReason); +} + + ///////////////////////////////////////////////////////////////////////////////////////////////////////// NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTMLDNSPrefetch::nsListener, @@ -257,6 +314,8 @@ nsHTMLDNSPrefetch::nsDeferrals::Add(PRUint16 flags, Link *aElement) // The FIFO has no lock, so it can only be accessed on main thread NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::Add must be on main thread"); + aElement->OnDNSPrefetchDeferred(); + if (((mHead + 1) & sMaxDeferredMask) == mTail) return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; @@ -283,20 +342,28 @@ nsHTMLDNSPrefetch::nsDeferrals::SubmitQueue() nsCOMPtr content = do_QueryReferent(mEntries[mTail].mElement); if (content) { nsCOMPtr link = do_QueryInterface(content); - nsCOMPtr hrefURI(link ? link->GetURI() : nsnull); - if (hrefURI) - hrefURI->GetAsciiHost(hostName); + // Only prefetch here if request was deferred and deferral not cancelled + if (link && link->HasDeferredDNSPrefetchRequest()) { + nsCOMPtr hrefURI(link ? link->GetURI() : nsnull); + if (hrefURI) + hrefURI->GetAsciiHost(hostName); - if (!hostName.IsEmpty()) { - if (IsNeckoChild()) { - gNeckoChild->SendHTMLDNSPrefetch(NS_ConvertUTF8toUTF16(hostName), + if (!hostName.IsEmpty()) { + if (IsNeckoChild()) { + gNeckoChild->SendHTMLDNSPrefetch(NS_ConvertUTF8toUTF16(hostName), mEntries[mTail].mFlags); - } else { - nsCOMPtr tmpOutstanding; + } else { + nsCOMPtr tmpOutstanding; - sDNSService->AsyncResolve(hostName, - mEntries[mTail].mFlags | nsIDNSService::RESOLVE_SPECULATE, - sDNSListener, nsnull, getter_AddRefs(tmpOutstanding)); + nsresult rv = sDNSService->AsyncResolve(hostName, + mEntries[mTail].mFlags + | nsIDNSService::RESOLVE_SPECULATE, + sDNSListener, nsnull, + getter_AddRefs(tmpOutstanding)); + // Tell link that deferred prefetch was requested + if (NS_SUCCEEDED(rv)) + link->OnDNSPrefetchRequested(); + } } } } diff --git a/content/html/content/src/nsHTMLDNSPrefetch.h b/content/html/content/src/nsHTMLDNSPrefetch.h index 7883ff07a4dc..f25d8b6a77e3 100644 --- a/content/html/content/src/nsHTMLDNSPrefetch.h +++ b/content/html/content/src/nsHTMLDNSPrefetch.h @@ -84,13 +84,22 @@ public: static nsresult PrefetchHigh(mozilla::dom::Link *aElement); static nsresult PrefetchMedium(mozilla::dom::Link *aElement); static nsresult PrefetchLow(mozilla::dom::Link *aElement); - static nsresult PrefetchHigh(nsAString &host); - static nsresult PrefetchMedium(nsAString &host); - static nsresult PrefetchLow(nsAString &host); + static nsresult PrefetchHigh(const nsAString &host); + static nsresult PrefetchMedium(const nsAString &host); + static nsresult PrefetchLow(const nsAString &host); + static nsresult CancelPrefetchLow(const nsAString &host, nsresult aReason); + static nsresult CancelPrefetchLow(mozilla::dom::Link *aElement, + nsresult aReason); private: - static nsresult Prefetch(nsAString &host, PRUint16 flags); + static nsresult Prefetch(const nsAString &host, PRUint16 flags); static nsresult Prefetch(mozilla::dom::Link *aElement, PRUint16 flags); + static nsresult CancelPrefetch(const nsAString &hostname, + PRUint16 flags, + nsresult aReason); + static nsresult CancelPrefetch(mozilla::dom::Link *aElement, + PRUint16 flags, + nsresult aReason); public: class nsListener : public nsIDNSListener diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp index 4296bd772117..1bd5f38a6113 100644 --- a/netwerk/dns/nsDNSService2.cpp +++ b/netwerk/dns/nsDNSService2.cpp @@ -280,6 +280,10 @@ public: ~nsDNSAsyncRequest() {} void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult); + // Returns TRUE if the DNS listener arg is the same as the member listener + // Used in Cancellations to remove DNS requests associated with a + // particular hostname and nsIDNSListener + bool EqualsAsyncListener(nsIDNSListener *aListener); nsRefPtr mResolver; nsCString mHost; // hostname we're resolving @@ -312,6 +316,12 @@ nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver, NS_RELEASE_THIS(); } +bool +nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) +{ + return (aListener == mListener); +} + NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSAsyncRequest, nsICancelable) NS_IMETHODIMP @@ -334,6 +344,7 @@ public: virtual ~nsDNSSyncRequest() {} void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult); + bool EqualsAsyncListener(nsIDNSListener *aListener); bool mDone; nsresult mStatus; @@ -357,6 +368,13 @@ nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver, PR_ExitMonitor(mMonitor); } +bool +nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) +{ + // Sync request: no listener to compare + return false; +} + //----------------------------------------------------------------------------- nsDNSService::nsDNSService() @@ -603,6 +621,42 @@ nsDNSService::AsyncResolve(const nsACString &hostname, return rv; } +NS_IMETHODIMP +nsDNSService::CancelAsyncResolve(const nsACString &aHostname, + PRUint32 aFlags, + nsIDNSListener *aListener, + nsresult aReason) +{ + // grab reference to global host resolver and IDN service. beware + // simultaneous shutdown!! + nsRefPtr res; + nsCOMPtr idn; + { + MutexAutoLock lock(mLock); + + if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) + return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; + + res = mResolver; + idn = mIDN; + } + if (!res) + return NS_ERROR_OFFLINE; + + nsCString hostname(aHostname); + + nsCAutoString hostACE; + if (idn && !IsASCII(aHostname)) { + if (NS_SUCCEEDED(idn->ConvertUTF8toACE(aHostname, hostACE))) + hostname = hostACE; + } + + PRUint16 af = GetAFForLookup(hostname, aFlags); + + res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason); + return NS_OK; +} + NS_IMETHODIMP nsDNSService::Resolve(const nsACString &hostname, PRUint32 flags, diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp index 26752d82c75a..68b9375dd004 100644 --- a/netwerk/dns/nsHostResolver.cpp +++ b/netwerk/dns/nsHostResolver.cpp @@ -934,6 +934,50 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo NS_RELEASE(rec); } +void +nsHostResolver::CancelAsyncRequest(const char *host, + PRUint16 flags, + PRUint16 af, + nsIDNSListener *aListener, + nsresult status) + +{ + MutexAutoLock lock(mLock); + + // Lookup the host record associated with host, flags & address family + nsHostKey key = { host, flags, af }; + nsHostDBEnt *he = static_cast + (PL_DHashTableOperate(&mDB, &key, PL_DHASH_LOOKUP)); + if (he && he->rec) { + nsHostRecord* recPtr = NULL; + PRCList *node = he->rec->callbacks.next; + // Remove the first nsDNSAsyncRequest callback which matches the + // supplied listener object + while (node != &he->rec->callbacks) { + nsResolveHostCallback *callback + = static_cast(node); + if (callback && (callback->EqualsAsyncListener(aListener))) { + // Remove from the list of callbacks + PR_REMOVE_LINK(callback); + recPtr = he->rec; + callback->OnLookupComplete(this, recPtr, status); + break; + } + node = node->next; + } + + // If there are no more callbacks, remove the hash table entry + if (recPtr && PR_CLIST_IS_EMPTY(&recPtr->callbacks)) { + PL_DHashTableOperate(&mDB, (nsHostKey *)recPtr, PL_DHASH_REMOVE); + // If record is on a Queue, remove it and then deref it + if (recPtr->next != recPtr) { + PR_REMOVE_LINK(recPtr); + NS_RELEASE(recPtr); + } + } + } +} + //---------------------------------------------------------------------------- void diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h index cab878f79737..4ca2648e0bbe 100644 --- a/netwerk/dns/nsHostResolver.h +++ b/netwerk/dns/nsHostResolver.h @@ -46,6 +46,7 @@ #include "mozilla/CondVar.h" #include "mozilla/Mutex.h" #include "nsISupportsImpl.h" +#include "nsIDNSListener.h" #include "nsString.h" #include "nsTArray.h" @@ -181,6 +182,20 @@ public: virtual void OnLookupComplete(nsHostResolver *resolver, nsHostRecord *record, nsresult status) = 0; + /** + * EqualsAsyncListener + * + * Determines if the listener argument matches the listener member var. + * For subclasses not implementing a member listener, should return false. + * For subclasses having a member listener, the function should check if + * they are the same. Used for cases where a pointer to an object + * implementing nsResolveHostCallback is unknown, but a pointer to + * the original listener is known. + * + * @param aListener + * nsIDNSListener object associated with the original request + */ + virtual bool EqualsAsyncListener(nsIDNSListener *aListener) = 0; }; /** @@ -235,6 +250,18 @@ public: nsResolveHostCallback *callback, nsresult status); + /** + * Cancels an async request associated with the hostname, flags, + * address family and listener. Cancels first callback found which matches + * these criteria. These parameters should correspond to the parameters + * passed to ResolveHost. If this is the last callback associated with the + * host record, it is removed from any request queues it might be on. + */ + void CancelAsyncRequest(const char *host, + PRUint16 flags, + PRUint16 af, + nsIDNSListener *aListener, + nsresult status); /** * values for the flags parameter passed to ResolveHost and DetachCallback * that may be bitwise OR'd together. diff --git a/netwerk/dns/nsIDNSService.idl b/netwerk/dns/nsIDNSService.idl index 8a2a15c366f0..b34327c461aa 100644 --- a/netwerk/dns/nsIDNSService.idl +++ b/netwerk/dns/nsIDNSService.idl @@ -46,7 +46,7 @@ interface nsIDNSListener; /** * nsIDNSService */ -[scriptable, uuid(c1a56a45-8fa3-44e6-9f01-38c91c858cf9)] +[scriptable, uuid(F6E05CC3-8A13-463D-877F-D59B20B59724)] interface nsIDNSService : nsISupports { /** @@ -72,6 +72,26 @@ interface nsIDNSService : nsISupports in nsIDNSListener aListener, in nsIEventTarget aListenerTarget); + /** + * Attempts to cancel a previously requested async DNS lookup + * + * @param aHostName + * the hostname or IP-address-literal to resolve. + * @param aFlags + * a bitwise OR of the RESOLVE_ prefixed constants defined below. + * @param aListener + * the original listener which was to be notified about the host lookup + * result - used to match request information to requestor. + * @param aReason + * nsresult reason for the cancellation + * + * @return An object that can be used to cancel the host lookup. + */ + void cancelAsyncResolve(in AUTF8String aHostName, + in unsigned long aFlags, + in nsIDNSListener aListener, + in nsresult aReason); + /** * called to synchronously resolve a hostname. warning this method may * block the calling thread for a long period of time. it is extremely diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index 87309acecec5..59480b5f2668 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -145,8 +145,16 @@ bool NeckoParent::RecvHTMLDNSPrefetch(const nsString& hostname, const PRUint16& flags) { - nsAutoString h(hostname); - nsHTMLDNSPrefetch::Prefetch(h, flags); + nsHTMLDNSPrefetch::Prefetch(hostname, flags); + return true; +} + +bool +NeckoParent::RecvCancelHTMLDNSPrefetch(const nsString& hostname, + const PRUint16& flags, + const nsresult& reason) +{ + nsHTMLDNSPrefetch::CancelPrefetch(hostname, flags, reason); return true; } diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h index be133d3c47b4..edae5c41e126 100644 --- a/netwerk/ipc/NeckoParent.h +++ b/netwerk/ipc/NeckoParent.h @@ -68,6 +68,10 @@ protected: virtual bool DeallocPWebSocket(PWebSocketParent*); virtual bool RecvHTMLDNSPrefetch(const nsString& hostname, const PRUint16& flags); + virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname, + const PRUint16& flags, + const nsresult& reason); + }; } // namespace net diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl index a530c32dd683..d1a2ee77d15f 100644 --- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -69,6 +69,7 @@ parent: PWebSocket(PBrowser browser); HTMLDNSPrefetch(nsString hostname, PRUint16 flags); + CancelHTMLDNSPrefetch(nsString hostname, PRUint16 flags, nsresult reason); both: PHttpChannel(nullable PBrowser browser); From fb6ecb4459770b96ba76848f02bb032c5b1b9ad5 Mon Sep 17 00:00:00 2001 From: Santiago Gimeno Date: Mon, 23 Jan 2012 14:06:02 -0800 Subject: [PATCH 35/58] Bug 715265 - Optimize all-int array sort and avoid creating a JSString per element when there is no comparator (r=luke) --HG-- extra : rebase_source : 0a61ff4ac1cafa450b78fad1cf4adde3246729fc --- js/src/jsarray.cpp | 205 ++++++++++++++++++++++++++++++------------ js/src/jsstr.cpp | 12 +-- js/src/jsstrinlines.h | 19 ++++ 3 files changed, 170 insertions(+), 66 deletions(-) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index fb552b029c43..a1808e08fd3c 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1981,7 +1981,89 @@ CompareStringValues(JSContext *cx, const Value &a, const Value &b, bool *lessOrE return true; } -struct SortComparatorStrings { +static int32_t const powersOf10Int32[] = { + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 +}; + +static inline int +NumDigitsBase10(int32_t n) +{ + /* + * This is just floor_log10(n) + 1 + * Algorithm taken from + * http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog10 + */ + int32_t log2, t; + JS_CEILING_LOG2(log2, n); + t = log2 * 1233 >> 12; + return t - (n < powersOf10Int32[t]) + 1; +} + +inline bool +CompareLexicographicInt32(JSContext *cx, const Value &a, const Value &b, bool *lessOrEqualp) +{ + int32_t aint = a.toInt32(); + int32_t bint = b.toInt32(); + + /* + * If both numbers are equal ... trivial + * If only one of both is negative --> arithmetic comparison as char code + * of '-' is always less than any other digit + * If both numbers are negative convert them to positive and continue + * handling ... + */ + if (aint == bint) { + *lessOrEqualp = true; + } else if ((aint < 0) && (bint >= 0)) { + *lessOrEqualp = true; + } else if ((aint >= 0) && (bint < 0)) { + *lessOrEqualp = false; + } else if (bint == INT32_MIN) { /* a is negative too --> a <= b */ + *lessOrEqualp = true; + } else if (aint == INT32_MIN) { /* b is negative too but not INT32_MIN --> a > b */ + *lessOrEqualp = false; + } else { + if (aint < 0) { /* b is also negative */ + aint = -aint; + bint = -bint; + } + + /* + * ... get number of digits of both integers. + * If they have the same number of digits --> arithmetic comparison. + * If digits_a > digits_b: a < b*10e(digits_a - digits_b). + * If digits_b > digits_a: a*10e(digits_b - digits_a) <= b. + */ + int digitsa = NumDigitsBase10(aint); + int digitsb = NumDigitsBase10(bint); + if (digitsa == digitsb) + *lessOrEqualp = (aint <= bint); + else if (digitsa > digitsb) + *lessOrEqualp = (aint < bint*powersOf10Int32[digitsa - digitsb]); + else /* if (digitsb > digitsa) */ + *lessOrEqualp = (aint*powersOf10Int32[digitsb - digitsa] <= bint); + } + + return true; +} + +inline bool +CompareSubStringValues(JSContext *cx, const jschar *s1, size_t l1, + const jschar *s2, size_t l2, bool *lessOrEqualp) +{ + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + + int32_t result; + if (!s1 || !s2 || !CompareChars(s1, l1, s2, l2, &result)) + return false; + + *lessOrEqualp = (result <= 0); + return true; +} + +struct SortComparatorStrings +{ JSContext *const cx; SortComparatorStrings(JSContext *cx) @@ -1992,23 +2074,42 @@ struct SortComparatorStrings { } }; -struct StringValuePair { - Value str; - Value v; -}; - -struct SortComparatorStringValuePairs { +struct SortComparatorLexicographicInt32 +{ JSContext *const cx; - SortComparatorStringValuePairs(JSContext *cx) + SortComparatorLexicographicInt32(JSContext *cx) : cx(cx) {} - bool operator()(const StringValuePair &a, const StringValuePair &b, bool *lessOrEqualp) { - return CompareStringValues(cx, a.str, b.str, lessOrEqualp); + bool operator()(const Value &a, const Value &b, bool *lessOrEqualp) { + return CompareLexicographicInt32(cx, a, b, lessOrEqualp); } }; -struct SortComparatorFunction { +struct StringifiedElement +{ + size_t charsBegin; + size_t charsEnd; + size_t elementIndex; +}; + +struct SortComparatorStringifiedElements +{ + JSContext *const cx; + const StringBuffer &sb; + + SortComparatorStringifiedElements(JSContext *cx, const StringBuffer &sb) + : cx(cx), sb(sb) {} + + bool operator()(const StringifiedElement &a, const StringifiedElement &b, bool *lessOrEqualp) { + return CompareSubStringValues(cx, sb.begin() + a.charsBegin, a.charsEnd - a.charsBegin, + sb.begin() + b.charsBegin, b.charsEnd - b.charsBegin, + lessOrEqualp); + } +}; + +struct SortComparatorFunction +{ JSContext *const cx; const Value &fval; InvokeArgsGuard &ag; @@ -2123,6 +2224,7 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp) */ undefs = 0; bool allStrings = true; + bool allInts = true; for (jsuint i = 0; i < len; i++) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; @@ -2140,6 +2242,7 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp) } vec.infallibleAppend(v); allStrings = allStrings && v.isString(); + allInts = allInts && v.isInt32(); } n = vec.length(); @@ -2151,6 +2254,7 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp) JS_ALWAYS_TRUE(vec.resize(n * 2)); /* Here len == n + undefs + number_of_holes. */ + Value *result = vec.begin(); if (fval.isNull()) { /* * Sort using the default comparator converting all elements to @@ -2159,69 +2263,60 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp) if (allStrings) { if (!MergeSort(vec.begin(), n, vec.begin() + n, SortComparatorStrings(cx))) return false; + } else if (allInts) { + if (!MergeSort(vec.begin(), n, vec.begin() + n, + SortComparatorLexicographicInt32(cx))) { + return false; + } } else { /* - * To avoid string conversion on each compare we do it only once - * prior to sorting. But we also need the space for the original - * values to recover the sorting result. For that we move the - * original values to the odd indexes in vec, put the string - * conversion results in the even indexes and do the merge sort - * over resulting string-value pairs using an extra allocated - * scratch space. + * Convert all elements to a jschar array in StringBuffer. + * Store the index and length of each stringified element with + * the corresponding index of the element in the array. Sort + * the stringified elements and with this result order the + * original array. */ - size_t i = n; - do { - --i; + StringBuffer sb(cx); + Vector strElements(cx); + if (!strElements.reserve(2 * n)) + return false; + + int cursor = 0; + for (size_t i = 0; i < n; i++) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; - const Value &v = vec[i]; - JSString *str = ToString(cx, v); - if (!str) + + if (!ValueToStringBuffer(cx, vec[i], sb)) return false; - /* - * Copying v must come first, because the following line - * overwrites v when i == 0. - */ - vec[2 * i + 1] = v; - vec[2 * i].setString(str); - } while (i != 0); + StringifiedElement el = { cursor, sb.length(), i }; + strElements.infallibleAppend(el); + cursor = sb.length(); + } - AutoValueVector extraScratch(cx); - if (!extraScratch.resize(n * 2)) - return false; - if (!MergeSort(reinterpret_cast(vec.begin()), n, - reinterpret_cast(extraScratch.begin()), - SortComparatorStringValuePairs(cx))) { + /* Resize strElements so we can perform the sorting */ + JS_ALWAYS_TRUE(strElements.resize(2 * n)); + + if (!MergeSort(strElements.begin(), n, strElements.begin() + n, + SortComparatorStringifiedElements(cx, sb))) { return false; } - /* - * We want to unroot the cached results of toString calls - * before the operation callback has a chance to run the GC. - * So we do not call JS_CHECK_OPERATION_LIMIT in the loop. - */ - i = 0; - do { - vec[i] = vec[2 * i + 1]; - } while (++i != n); + /* Order vec[n:2n-1] using strElements.index */ + for (size_t i = 0; i < n; i ++) + vec[n + i] = vec[strElements[i].elementIndex]; + + result = vec.begin() + n; } } else { InvokeArgsGuard args; if (!MergeSort(vec.begin(), n, vec.begin() + n, - SortComparatorFunction(cx, fval, args))) - { + SortComparatorFunction(cx, fval, args))) { return false; } } - /* - * We no longer need to root the scratch space for the merge sort, so - * unroot it now to make the job of a potential GC under - * InitArrayElements easier. - */ - vec.resize(n); - if (!InitArrayElements(cx, obj, 0, jsuint(n), vec.begin(), DontUpdateTypes)) + if (!InitArrayElements(cx, obj, 0, jsuint(n), result, DontUpdateTypes)) return false; } diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 8c25720b5c5b..781cecdaec65 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -3430,25 +3430,15 @@ CompareStringsImpl(JSContext *cx, JSString *str1, JSString *str2, int32_t *resul return true; } - size_t l1 = str1->length(); const jschar *s1 = str1->getChars(cx); if (!s1) return false; - size_t l2 = str2->length(); const jschar *s2 = str2->getChars(cx); if (!s2) return false; - size_t n = JS_MIN(l1, l2); - for (size_t i = 0; i < n; i++) { - if (int32_t cmp = s1[i] - s2[i]) { - *result = cmp; - return true; - } - } - *result = (int32_t)(l1 - l2); - return true; + return CompareChars(s1, str1->length(), s2, str2->length(), result); } bool diff --git a/js/src/jsstrinlines.h b/js/src/jsstrinlines.h index 1f0b2979b4b9..de3c75c325b9 100644 --- a/js/src/jsstrinlines.h +++ b/js/src/jsstrinlines.h @@ -329,6 +329,25 @@ SkipSpace(const jschar *s, const jschar *end) return s; } +/* + * Return less than, equal to, or greater than zero depending on whether + * s1 is less than, equal to, or greater than s2. + */ +inline bool +CompareChars(const jschar *s1, size_t l1, const jschar *s2, size_t l2, int32_t *result) +{ + size_t n = JS_MIN(l1, l2); + for (size_t i = 0; i < n; i++) { + if (int32_t cmp = s1[i] - s2[i]) { + *result = cmp; + return true; + } + } + + *result = (int32_t)(l1 - l2); + return true; +} + } /* namespace js */ #endif /* jsstrinlines_h___ */ From ed1e18f9219c17437f7373619c81e1dc5031c126 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Mon, 23 Jan 2012 16:07:47 -0800 Subject: [PATCH 36/58] Backed out changeset a57b82b18bde (bug 622232) for build bustage --- content/base/src/Link.h | 22 +--- .../html/content/src/nsHTMLAnchorElement.cpp | 52 +-------- .../html/content/src/nsHTMLDNSPrefetch.cpp | 101 +++--------------- content/html/content/src/nsHTMLDNSPrefetch.h | 17 +-- netwerk/dns/nsDNSService2.cpp | 54 ---------- netwerk/dns/nsHostResolver.cpp | 44 -------- netwerk/dns/nsHostResolver.h | 27 ----- netwerk/dns/nsIDNSService.idl | 22 +--- netwerk/ipc/NeckoParent.cpp | 12 +-- netwerk/ipc/NeckoParent.h | 4 - netwerk/ipc/PNecko.ipdl | 1 - 11 files changed, 28 insertions(+), 328 deletions(-) diff --git a/content/base/src/Link.h b/content/base/src/Link.h index c193f2c88752..67ed3c16a056 100644 --- a/content/base/src/Link.h +++ b/content/base/src/Link.h @@ -51,8 +51,8 @@ namespace mozilla { namespace dom { #define MOZILLA_DOM_LINK_IMPLEMENTATION_IID \ - { 0x7EA57721, 0xE373, 0x458E, \ - {0x8F, 0x44, 0xF8, 0x96, 0x56, 0xB4, 0x14, 0xF5 } } + { 0xa687a99c, 0x3893, 0x45c0, \ + {0x8e, 0xab, 0xb8, 0xf7, 0xd7, 0x9e, 0x9e, 0x7b } } class Link : public nsISupports { @@ -113,24 +113,6 @@ public: // This method nevers returns a null element. Element* GetElement() const { return mElement; } - /** - * DNS prefetch has been deferred until later, e.g. page load complete. - */ - virtual void OnDNSPrefetchDeferred() { /*do nothing*/ } - - /** - * DNS prefetch has been submitted to Host Resolver. - */ - virtual void OnDNSPrefetchRequested() { /*do nothing*/ } - - /** - * Checks if DNS Prefetching is ok - * - * @returns boolean - * Defaults to true; should be overridden for specialised cases - */ - virtual bool HasDeferredDNSPrefetchRequest() { return true; } - protected: virtual ~Link(); diff --git a/content/html/content/src/nsHTMLAnchorElement.cpp b/content/html/content/src/nsHTMLAnchorElement.cpp index e86473291113..af5fbca85742 100644 --- a/content/html/content/src/nsHTMLAnchorElement.cpp +++ b/content/html/content/src/nsHTMLAnchorElement.cpp @@ -140,27 +140,14 @@ public: virtual nsEventStates IntrinsicState() const; virtual nsXPCClassInfo* GetClassInfo(); - - virtual void OnDNSPrefetchDeferred(); - virtual void OnDNSPrefetchRequested(); - virtual bool HasDeferredDNSPrefetchRequest(); }; -// Indicates that a DNS Prefetch has been requested from this Anchor elem -#define HTML_ANCHOR_DNS_PREFETCH_REQUESTED \ - (1 << ELEMENT_TYPE_SPECIFIC_BITS_OFFSET) -// Indicates that a DNS Prefetch was added to the deferral queue -#define HTML_ANCHOR_DNS_PREFETCH_DEFERRED \ - (1 << ELEMENT_TYPE_SPECIFIC_BITS_OFFSET+1) - -// Make sure we have enough space for those bits -PR_STATIC_ASSERT(ELEMENT_TYPE_SPECIFIC_BITS_OFFSET+1 < 32); NS_IMPL_NS_NEW_HTML_ELEMENT(Anchor) nsHTMLAnchorElement::nsHTMLAnchorElement(already_AddRefed aNodeInfo) - : nsGenericHTMLElement(aNodeInfo) - , Link(this) + : nsGenericHTMLElement(aNodeInfo), + Link(this) { } @@ -215,26 +202,6 @@ nsHTMLAnchorElement::GetDraggable(bool* aDraggable) return nsGenericHTMLElement::GetDraggable(aDraggable); } -void -nsHTMLAnchorElement::OnDNSPrefetchRequested() -{ - UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); - SetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); -} - -void -nsHTMLAnchorElement::OnDNSPrefetchDeferred() -{ - UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); - SetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); -} - -bool -nsHTMLAnchorElement::HasDeferredDNSPrefetchRequest() -{ - return HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); -} - nsresult nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, nsIContent* aBindingParent, @@ -261,21 +228,6 @@ nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent, void nsHTMLAnchorElement::UnbindFromTree(bool aDeep, bool aNullParent) { - // Cancel any DNS prefetches - // Note: Must come before ResetLinkState. If called after, it will recreate - // mCachedURI based on data that is invalid - due to a call to GetHostname. - - // If prefetch was deferred, clear flag and move on - if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_DEFERRED)) - UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_DEFERRED); - // Else if prefetch was requested, clear flag and send cancellation - else if (HasFlag(HTML_ANCHOR_DNS_PREFETCH_REQUESTED)) { - UnsetFlags(HTML_ANCHOR_DNS_PREFETCH_REQUESTED); - // Possible that hostname could have changed since binding, but since this - // covers common cases, most DNS prefetch requests will be canceled - nsHTMLDNSPrefetch::CancelPrefetchLow(this, NS_ERROR_ABORT); - } - // If this link is ever reinserted into a document, it might // be under a different xml:base, so forget the cached state now. Link::ResetLinkState(false); diff --git a/content/html/content/src/nsHTMLDNSPrefetch.cpp b/content/html/content/src/nsHTMLDNSPrefetch.cpp index 7365f334c8fe..56148e91c630 100644 --- a/content/html/content/src/nsHTMLDNSPrefetch.cpp +++ b/content/html/content/src/nsHTMLDNSPrefetch.cpp @@ -165,7 +165,7 @@ nsHTMLDNSPrefetch::PrefetchHigh(Link *aElement) } nsresult -nsHTMLDNSPrefetch::Prefetch(const nsAString &hostname, PRUint16 flags) +nsHTMLDNSPrefetch::Prefetch(nsAString &hostname, PRUint16 flags) { if (IsNeckoChild()) { // We need to check IsEmpty() because net_IsValidHostName() @@ -181,85 +181,28 @@ nsHTMLDNSPrefetch::Prefetch(const nsAString &hostname, PRUint16 flags) return NS_ERROR_NOT_AVAILABLE; nsCOMPtr tmpOutstanding; - return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), - flags | nsIDNSService::RESOLVE_SPECULATE, - sDNSListener, nsnull, - getter_AddRefs(tmpOutstanding)); + return sDNSService->AsyncResolve(NS_ConvertUTF16toUTF8(hostname), flags | nsIDNSService::RESOLVE_SPECULATE, + sDNSListener, nsnull, getter_AddRefs(tmpOutstanding)); } nsresult -nsHTMLDNSPrefetch::PrefetchLow(const nsAString &hostname) +nsHTMLDNSPrefetch::PrefetchLow(nsAString &hostname) { return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW); } nsresult -nsHTMLDNSPrefetch::PrefetchMedium(const nsAString &hostname) +nsHTMLDNSPrefetch::PrefetchMedium(nsAString &hostname) { return Prefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_MEDIUM); } nsresult -nsHTMLDNSPrefetch::PrefetchHigh(const nsAString &hostname) +nsHTMLDNSPrefetch::PrefetchHigh(nsAString &hostname) { return Prefetch(hostname, 0); } -nsresult -nsHTMLDNSPrefetch::CancelPrefetch(Link *aElement, - PRUint16 flags, - nsresult aReason) -{ - if (!(sInitialized && sPrefetches && sDNSService && sDNSListener)) - return NS_ERROR_NOT_AVAILABLE; - - nsAutoString hostname; - nsresult rv = aElement->GetHostname(hostname); - return CancelPrefetch(hostname, flags, aReason); -} - -nsresult -nsHTMLDNSPrefetch::CancelPrefetch(const nsAString &hostname, - PRUint16 flags, - nsresult aReason) -{ - // Forward this request to Necko Parent if we're a child process - if (IsNeckoChild()) { - // We need to check IsEmpty() because net_IsValidHostName() - // considers empty strings to be valid hostnames - if (!hostname.IsEmpty() && - net_IsValidHostName(NS_ConvertUTF16toUTF8(hostname))) { - gNeckoChild->SendCancelHTMLDNSPrefetch(nsString(hostname), flags, - aReason); - } - return NS_OK; - } - - if (!(sInitialized && sDNSService && sPrefetches && sDNSListener)) - return NS_ERROR_NOT_AVAILABLE; - - // Forward cancellation to DNS service - return sDNSService->CancelAsyncResolve(NS_ConvertUTF16toUTF8(hostname), - flags - | nsIDNSService::RESOLVE_SPECULATE, - sDNSListener, aReason); -} - -nsresult -nsHTMLDNSPrefetch::CancelPrefetchLow(Link *aElement, nsresult aReason) -{ - return CancelPrefetch(aElement, nsIDNSService::RESOLVE_PRIORITY_LOW, - aReason); -} - -nsresult -nsHTMLDNSPrefetch::CancelPrefetchLow(const nsAString &hostname, nsresult aReason) -{ - return CancelPrefetch(hostname, nsIDNSService::RESOLVE_PRIORITY_LOW, - aReason); -} - - ///////////////////////////////////////////////////////////////////////////////////////////////////////// NS_IMPL_THREADSAFE_ISUPPORTS1(nsHTMLDNSPrefetch::nsListener, @@ -314,8 +257,6 @@ nsHTMLDNSPrefetch::nsDeferrals::Add(PRUint16 flags, Link *aElement) // The FIFO has no lock, so it can only be accessed on main thread NS_ASSERTION(NS_IsMainThread(), "nsDeferrals::Add must be on main thread"); - aElement->OnDNSPrefetchDeferred(); - if (((mHead + 1) & sMaxDeferredMask) == mTail) return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; @@ -342,28 +283,20 @@ nsHTMLDNSPrefetch::nsDeferrals::SubmitQueue() nsCOMPtr content = do_QueryReferent(mEntries[mTail].mElement); if (content) { nsCOMPtr link = do_QueryInterface(content); - // Only prefetch here if request was deferred and deferral not cancelled - if (link && link->HasDeferredDNSPrefetchRequest()) { - nsCOMPtr hrefURI(link ? link->GetURI() : nsnull); - if (hrefURI) - hrefURI->GetAsciiHost(hostName); + nsCOMPtr hrefURI(link ? link->GetURI() : nsnull); + if (hrefURI) + hrefURI->GetAsciiHost(hostName); - if (!hostName.IsEmpty()) { - if (IsNeckoChild()) { - gNeckoChild->SendHTMLDNSPrefetch(NS_ConvertUTF8toUTF16(hostName), + if (!hostName.IsEmpty()) { + if (IsNeckoChild()) { + gNeckoChild->SendHTMLDNSPrefetch(NS_ConvertUTF8toUTF16(hostName), mEntries[mTail].mFlags); - } else { - nsCOMPtr tmpOutstanding; + } else { + nsCOMPtr tmpOutstanding; - nsresult rv = sDNSService->AsyncResolve(hostName, - mEntries[mTail].mFlags - | nsIDNSService::RESOLVE_SPECULATE, - sDNSListener, nsnull, - getter_AddRefs(tmpOutstanding)); - // Tell link that deferred prefetch was requested - if (NS_SUCCEEDED(rv)) - link->OnDNSPrefetchRequested(); - } + sDNSService->AsyncResolve(hostName, + mEntries[mTail].mFlags | nsIDNSService::RESOLVE_SPECULATE, + sDNSListener, nsnull, getter_AddRefs(tmpOutstanding)); } } } diff --git a/content/html/content/src/nsHTMLDNSPrefetch.h b/content/html/content/src/nsHTMLDNSPrefetch.h index f25d8b6a77e3..7883ff07a4dc 100644 --- a/content/html/content/src/nsHTMLDNSPrefetch.h +++ b/content/html/content/src/nsHTMLDNSPrefetch.h @@ -84,22 +84,13 @@ public: static nsresult PrefetchHigh(mozilla::dom::Link *aElement); static nsresult PrefetchMedium(mozilla::dom::Link *aElement); static nsresult PrefetchLow(mozilla::dom::Link *aElement); - static nsresult PrefetchHigh(const nsAString &host); - static nsresult PrefetchMedium(const nsAString &host); - static nsresult PrefetchLow(const nsAString &host); - static nsresult CancelPrefetchLow(const nsAString &host, nsresult aReason); - static nsresult CancelPrefetchLow(mozilla::dom::Link *aElement, - nsresult aReason); + static nsresult PrefetchHigh(nsAString &host); + static nsresult PrefetchMedium(nsAString &host); + static nsresult PrefetchLow(nsAString &host); private: - static nsresult Prefetch(const nsAString &host, PRUint16 flags); + static nsresult Prefetch(nsAString &host, PRUint16 flags); static nsresult Prefetch(mozilla::dom::Link *aElement, PRUint16 flags); - static nsresult CancelPrefetch(const nsAString &hostname, - PRUint16 flags, - nsresult aReason); - static nsresult CancelPrefetch(mozilla::dom::Link *aElement, - PRUint16 flags, - nsresult aReason); public: class nsListener : public nsIDNSListener diff --git a/netwerk/dns/nsDNSService2.cpp b/netwerk/dns/nsDNSService2.cpp index 1bd5f38a6113..4296bd772117 100644 --- a/netwerk/dns/nsDNSService2.cpp +++ b/netwerk/dns/nsDNSService2.cpp @@ -280,10 +280,6 @@ public: ~nsDNSAsyncRequest() {} void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult); - // Returns TRUE if the DNS listener arg is the same as the member listener - // Used in Cancellations to remove DNS requests associated with a - // particular hostname and nsIDNSListener - bool EqualsAsyncListener(nsIDNSListener *aListener); nsRefPtr mResolver; nsCString mHost; // hostname we're resolving @@ -316,12 +312,6 @@ nsDNSAsyncRequest::OnLookupComplete(nsHostResolver *resolver, NS_RELEASE_THIS(); } -bool -nsDNSAsyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) -{ - return (aListener == mListener); -} - NS_IMPL_THREADSAFE_ISUPPORTS1(nsDNSAsyncRequest, nsICancelable) NS_IMETHODIMP @@ -344,7 +334,6 @@ public: virtual ~nsDNSSyncRequest() {} void OnLookupComplete(nsHostResolver *, nsHostRecord *, nsresult); - bool EqualsAsyncListener(nsIDNSListener *aListener); bool mDone; nsresult mStatus; @@ -368,13 +357,6 @@ nsDNSSyncRequest::OnLookupComplete(nsHostResolver *resolver, PR_ExitMonitor(mMonitor); } -bool -nsDNSSyncRequest::EqualsAsyncListener(nsIDNSListener *aListener) -{ - // Sync request: no listener to compare - return false; -} - //----------------------------------------------------------------------------- nsDNSService::nsDNSService() @@ -621,42 +603,6 @@ nsDNSService::AsyncResolve(const nsACString &hostname, return rv; } -NS_IMETHODIMP -nsDNSService::CancelAsyncResolve(const nsACString &aHostname, - PRUint32 aFlags, - nsIDNSListener *aListener, - nsresult aReason) -{ - // grab reference to global host resolver and IDN service. beware - // simultaneous shutdown!! - nsRefPtr res; - nsCOMPtr idn; - { - MutexAutoLock lock(mLock); - - if (mDisablePrefetch && (aFlags & RESOLVE_SPECULATE)) - return NS_ERROR_DNS_LOOKUP_QUEUE_FULL; - - res = mResolver; - idn = mIDN; - } - if (!res) - return NS_ERROR_OFFLINE; - - nsCString hostname(aHostname); - - nsCAutoString hostACE; - if (idn && !IsASCII(aHostname)) { - if (NS_SUCCEEDED(idn->ConvertUTF8toACE(aHostname, hostACE))) - hostname = hostACE; - } - - PRUint16 af = GetAFForLookup(hostname, aFlags); - - res->CancelAsyncRequest(hostname.get(), aFlags, af, aListener, aReason); - return NS_OK; -} - NS_IMETHODIMP nsDNSService::Resolve(const nsACString &hostname, PRUint32 flags, diff --git a/netwerk/dns/nsHostResolver.cpp b/netwerk/dns/nsHostResolver.cpp index 68b9375dd004..26752d82c75a 100644 --- a/netwerk/dns/nsHostResolver.cpp +++ b/netwerk/dns/nsHostResolver.cpp @@ -934,50 +934,6 @@ nsHostResolver::OnLookupComplete(nsHostRecord *rec, nsresult status, PRAddrInfo NS_RELEASE(rec); } -void -nsHostResolver::CancelAsyncRequest(const char *host, - PRUint16 flags, - PRUint16 af, - nsIDNSListener *aListener, - nsresult status) - -{ - MutexAutoLock lock(mLock); - - // Lookup the host record associated with host, flags & address family - nsHostKey key = { host, flags, af }; - nsHostDBEnt *he = static_cast - (PL_DHashTableOperate(&mDB, &key, PL_DHASH_LOOKUP)); - if (he && he->rec) { - nsHostRecord* recPtr = NULL; - PRCList *node = he->rec->callbacks.next; - // Remove the first nsDNSAsyncRequest callback which matches the - // supplied listener object - while (node != &he->rec->callbacks) { - nsResolveHostCallback *callback - = static_cast(node); - if (callback && (callback->EqualsAsyncListener(aListener))) { - // Remove from the list of callbacks - PR_REMOVE_LINK(callback); - recPtr = he->rec; - callback->OnLookupComplete(this, recPtr, status); - break; - } - node = node->next; - } - - // If there are no more callbacks, remove the hash table entry - if (recPtr && PR_CLIST_IS_EMPTY(&recPtr->callbacks)) { - PL_DHashTableOperate(&mDB, (nsHostKey *)recPtr, PL_DHASH_REMOVE); - // If record is on a Queue, remove it and then deref it - if (recPtr->next != recPtr) { - PR_REMOVE_LINK(recPtr); - NS_RELEASE(recPtr); - } - } - } -} - //---------------------------------------------------------------------------- void diff --git a/netwerk/dns/nsHostResolver.h b/netwerk/dns/nsHostResolver.h index 4ca2648e0bbe..cab878f79737 100644 --- a/netwerk/dns/nsHostResolver.h +++ b/netwerk/dns/nsHostResolver.h @@ -46,7 +46,6 @@ #include "mozilla/CondVar.h" #include "mozilla/Mutex.h" #include "nsISupportsImpl.h" -#include "nsIDNSListener.h" #include "nsString.h" #include "nsTArray.h" @@ -182,20 +181,6 @@ public: virtual void OnLookupComplete(nsHostResolver *resolver, nsHostRecord *record, nsresult status) = 0; - /** - * EqualsAsyncListener - * - * Determines if the listener argument matches the listener member var. - * For subclasses not implementing a member listener, should return false. - * For subclasses having a member listener, the function should check if - * they are the same. Used for cases where a pointer to an object - * implementing nsResolveHostCallback is unknown, but a pointer to - * the original listener is known. - * - * @param aListener - * nsIDNSListener object associated with the original request - */ - virtual bool EqualsAsyncListener(nsIDNSListener *aListener) = 0; }; /** @@ -250,18 +235,6 @@ public: nsResolveHostCallback *callback, nsresult status); - /** - * Cancels an async request associated with the hostname, flags, - * address family and listener. Cancels first callback found which matches - * these criteria. These parameters should correspond to the parameters - * passed to ResolveHost. If this is the last callback associated with the - * host record, it is removed from any request queues it might be on. - */ - void CancelAsyncRequest(const char *host, - PRUint16 flags, - PRUint16 af, - nsIDNSListener *aListener, - nsresult status); /** * values for the flags parameter passed to ResolveHost and DetachCallback * that may be bitwise OR'd together. diff --git a/netwerk/dns/nsIDNSService.idl b/netwerk/dns/nsIDNSService.idl index b34327c461aa..8a2a15c366f0 100644 --- a/netwerk/dns/nsIDNSService.idl +++ b/netwerk/dns/nsIDNSService.idl @@ -46,7 +46,7 @@ interface nsIDNSListener; /** * nsIDNSService */ -[scriptable, uuid(F6E05CC3-8A13-463D-877F-D59B20B59724)] +[scriptable, uuid(c1a56a45-8fa3-44e6-9f01-38c91c858cf9)] interface nsIDNSService : nsISupports { /** @@ -72,26 +72,6 @@ interface nsIDNSService : nsISupports in nsIDNSListener aListener, in nsIEventTarget aListenerTarget); - /** - * Attempts to cancel a previously requested async DNS lookup - * - * @param aHostName - * the hostname or IP-address-literal to resolve. - * @param aFlags - * a bitwise OR of the RESOLVE_ prefixed constants defined below. - * @param aListener - * the original listener which was to be notified about the host lookup - * result - used to match request information to requestor. - * @param aReason - * nsresult reason for the cancellation - * - * @return An object that can be used to cancel the host lookup. - */ - void cancelAsyncResolve(in AUTF8String aHostName, - in unsigned long aFlags, - in nsIDNSListener aListener, - in nsresult aReason); - /** * called to synchronously resolve a hostname. warning this method may * block the calling thread for a long period of time. it is extremely diff --git a/netwerk/ipc/NeckoParent.cpp b/netwerk/ipc/NeckoParent.cpp index 59480b5f2668..87309acecec5 100644 --- a/netwerk/ipc/NeckoParent.cpp +++ b/netwerk/ipc/NeckoParent.cpp @@ -145,16 +145,8 @@ bool NeckoParent::RecvHTMLDNSPrefetch(const nsString& hostname, const PRUint16& flags) { - nsHTMLDNSPrefetch::Prefetch(hostname, flags); - return true; -} - -bool -NeckoParent::RecvCancelHTMLDNSPrefetch(const nsString& hostname, - const PRUint16& flags, - const nsresult& reason) -{ - nsHTMLDNSPrefetch::CancelPrefetch(hostname, flags, reason); + nsAutoString h(hostname); + nsHTMLDNSPrefetch::Prefetch(h, flags); return true; } diff --git a/netwerk/ipc/NeckoParent.h b/netwerk/ipc/NeckoParent.h index edae5c41e126..be133d3c47b4 100644 --- a/netwerk/ipc/NeckoParent.h +++ b/netwerk/ipc/NeckoParent.h @@ -68,10 +68,6 @@ protected: virtual bool DeallocPWebSocket(PWebSocketParent*); virtual bool RecvHTMLDNSPrefetch(const nsString& hostname, const PRUint16& flags); - virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname, - const PRUint16& flags, - const nsresult& reason); - }; } // namespace net diff --git a/netwerk/ipc/PNecko.ipdl b/netwerk/ipc/PNecko.ipdl index d1a2ee77d15f..a530c32dd683 100644 --- a/netwerk/ipc/PNecko.ipdl +++ b/netwerk/ipc/PNecko.ipdl @@ -69,7 +69,6 @@ parent: PWebSocket(PBrowser browser); HTMLDNSPrefetch(nsString hostname, PRUint16 flags); - CancelHTMLDNSPrefetch(nsString hostname, PRUint16 flags, nsresult reason); both: PHttpChannel(nullable PBrowser browser); From 321306d0cde9433fac7c55fa0750964394fd00b2 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Sat, 21 Jan 2012 04:05:53 -0800 Subject: [PATCH 37/58] Bug 720511 - Make sure indexed elements on prototypes of an array being joined show through in the final join-string. r=luke --HG-- extra : rebase_source : 9979fa265e18e347fb0406c39044bce6fa183aef --- js/src/jsarray.cpp | 33 +++++++++-- js/src/tests/ecma_5/Array/join-01.js | 76 ++++++++++++++++++++++++++ js/src/tests/ecma_5/Array/jstests.list | 1 + 3 files changed, 104 insertions(+), 6 deletions(-) create mode 100644 js/src/tests/ecma_5/Array/join-01.js diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index a1808e08fd3c..25b561237f99 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -1625,15 +1625,36 @@ array_toString_sub(JSContext *cx, JSObject *obj, JSBool locale, StringBuffer sb(cx); if (!locale && !seplen && obj->isDenseArray() && !js_PrototypeHasIndexedProperties(cx, obj)) { - /* Elements beyond the initialized length are 'undefined' and thus can be ignored. */ - const Value *beg = obj->getDenseArrayElements(); - const Value *end = beg + Min(length, obj->getDenseArrayInitializedLength()); - for (const Value *vp = beg; vp != end; ++vp) { + const Value *start = obj->getDenseArrayElements(); + const Value *end = start + obj->getDenseArrayInitializedLength(); + const Value *elem; + for (elem = start; elem < end; elem++) { if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; - if (!vp->isMagic(JS_ARRAY_HOLE) && !vp->isNullOrUndefined()) { - if (!ValueToStringBuffer(cx, *vp, sb)) + /* + * Object stringifying is slow; delegate it to a separate loop to + * keep this one tight. + */ + if (elem->isObject()) + break; + + if (!elem->isMagic(JS_ARRAY_HOLE) && !elem->isNullOrUndefined()) { + if (!ValueToStringBuffer(cx, *elem, sb)) + return false; + } + } + + for (uint32_t i = uint32_t(PointerRangeSize(start, elem)); i < length; i++) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + + JSBool hole; + Value v; + if (!GetElement(cx, obj, i, &hole, &v)) + return false; + if (!hole && !v.isNullOrUndefined()) { + if (!ValueToStringBuffer(cx, v, sb)) return false; } } diff --git a/js/src/tests/ecma_5/Array/join-01.js b/js/src/tests/ecma_5/Array/join-01.js new file mode 100644 index 000000000000..e4c15df17378 --- /dev/null +++ b/js/src/tests/ecma_5/Array/join-01.js @@ -0,0 +1,76 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + * Contributor: + * Jeff Walden + */ + +//----------------------------------------------------------------------------- +print("ES5: Array.prototype.join"); + +/************** + * BEGIN TEST * + **************/ + +var count; +var stringifyCounter = { toString: function() { count++; return "obj"; } }; + +var arr = [1, 2, 3, 4, 5]; +assertEq(arr.join(), "1,2,3,4,5"); +assertEq(arr.join(","), "1,2,3,4,5"); +assertEq(arr.join(undefined), "1,2,3,4,5"); +assertEq(arr.join(4), "142434445"); +assertEq(arr.join(""), "12345"); + +count = 0; +assertEq(arr.join(stringifyCounter), "1obj2obj3obj4obj5"); +assertEq(count, 1); + +var holey = [1, 2, , 4, 5]; +assertEq(holey.join(), "1,2,,4,5"); +assertEq(holey.join(","), "1,2,,4,5"); +assertEq(holey.join(undefined), "1,2,,4,5"); +assertEq(holey.join(4), "14244445"); + +count = 0; +assertEq(holey.join(stringifyCounter), "1obj2objobj4obj5"); +assertEq(count, 1); + +var nully = [1, 2, 3, null, 5]; +assertEq(nully.join(), "1,2,3,,5"); +assertEq(nully.join(","), "1,2,3,,5"); +assertEq(nully.join(undefined), "1,2,3,,5"); +assertEq(nully.join(4), "14243445"); + +count = 0; +assertEq(nully.join(stringifyCounter), "1obj2obj3objobj5"); +assertEq(count, 1); + +var undefiney = [1, undefined, 3, 4, 5]; +assertEq(undefiney.join(), "1,,3,4,5"); +assertEq(undefiney.join(","), "1,,3,4,5"); +assertEq(undefiney.join(undefined), "1,,3,4,5"); +assertEq(undefiney.join(4), "14434445"); + +count = 0; +assertEq(undefiney.join(stringifyCounter), "1objobj3obj4obj5"); +assertEq(count, 1); + +var funky = + { + toString: function() + { + Array.prototype[1] = "chorp"; + Object.prototype[3] = "fnord"; + return "funky"; + } + }; +var trailingHoles = [0, funky, /* 2 */, /* 3 */,]; +assertEq(trailingHoles.join(""), "0funkyfnord"); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/ecma_5/Array/jstests.list b/js/src/tests/ecma_5/Array/jstests.list index 9e0b5c419811..8212faf60505 100644 --- a/js/src/tests/ecma_5/Array/jstests.list +++ b/js/src/tests/ecma_5/Array/jstests.list @@ -1,4 +1,5 @@ url-prefix ../../jsreftest.html?test=ecma_5/Array/ +script join-01.js script length-01.js script length-set-object.js script regress-599159.js From cc5c77dbed4563ffbe8ef3b1db1e830fb6b02266 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 17 Jan 2012 17:55:27 -0800 Subject: [PATCH 38/58] Bug 720316 - Introduce GET_UINT8 and SET_UINT8 helpers for bytecode immediate examination/setting. r=luke --HG-- extra : rebase_source : 7413b96b2e1b048b78c37d6a7b0700d6b1d84874 --- js/src/frontend/BytecodeEmitter.cpp | 2 +- js/src/jsinfer.cpp | 6 +++--- js/src/jsinterp.cpp | 18 ++++++++---------- js/src/jsopcode.cpp | 6 +++--- js/src/jsopcode.h | 12 ++++++++++++ js/src/methodjit/Compiler.cpp | 10 +++++----- 6 files changed, 32 insertions(+), 22 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 7028cbcf529c..3530c8cdac52 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -3235,7 +3235,7 @@ EmitDestructuringOpsHelper(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JS_ASSERT((bce->stackDepth - bce->stackDepth) >= -1); uintN pickDistance = (uintN)((bce->stackDepth + 1) - depthBefore); if (pickDistance > 0) { - if (pickDistance > jsbytecode(-1)) { + if (pickDistance > UINT8_MAX) { ReportCompileErrorNumber(cx, bce->tokenStream(), pn3, JSREPORT_ERROR, JSMSG_TOO_MANY_LOCALS); return JS_FALSE; diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 52e9d8492e56..81fd91eceb25 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -3257,7 +3257,7 @@ GetInitializerType(JSContext *cx, JSScript *script, jsbytecode *pc) JSOp op = JSOp(*pc); JS_ASSERT(op == JSOP_NEWARRAY || op == JSOP_NEWOBJECT || op == JSOP_NEWINIT); - bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && pc[1] == JSProto_Array)); + bool isArray = (op == JSOP_NEWARRAY || (op == JSOP_NEWINIT && GET_UINT8(pc) == JSProto_Array)); return TypeScript::InitObject(cx, script, pc, isArray ? JSProto_Array : JSProto_Object); } @@ -3562,7 +3562,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, case JSOP_SWAP: case JSOP_PICK: { - unsigned pickedDepth = (op == JSOP_SWAP ? 1 : pc[1]); + unsigned pickedDepth = (op == JSOP_SWAP ? 1 : GET_UINT8(pc)); /* The last popped value is the last pushed. */ poppedTypes(pc, pickedDepth)->addSubset(cx, &pushed[pickedDepth]); for (unsigned i = 0; i < pickedDepth; i++) @@ -4025,7 +4025,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, return false; } - if (pc[1] & JSITER_FOREACH) + if (GET_UINT8(pc) & JSITER_FOREACH) state.forTypes->addType(cx, Type::UnknownType()); else state.forTypes->addType(cx, Type::StringType()); diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 09d8dece7776..e2ea1b33baa0 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -2047,7 +2047,7 @@ END_CASE(JSOP_AND) #define TRY_BRANCH_AFTER_COND(cond,spdec) \ JS_BEGIN_MACRO \ JS_ASSERT(js_CodeSpec[op].length == 1); \ - uintN diff_ = (uintN) regs.pc[1] - (uintN) JSOP_IFEQ; \ + uintN diff_ = (uintN) GET_UINT8(regs.pc) - (uintN) JSOP_IFEQ; \ if (diff_ <= 1) { \ regs.sp -= spdec; \ if (cond == (diff_ != 0)) { \ @@ -2084,7 +2084,7 @@ END_CASE(JSOP_IN) BEGIN_CASE(JSOP_ITER) { JS_ASSERT(regs.sp > regs.fp()->base()); - uintN flags = regs.pc[1]; + uint8_t flags = GET_UINT8(regs.pc); if (!js_ValueToIterator(cx, flags, ®s.sp[-1])) goto error; CHECK_INTERRUPT_HANDLER(); @@ -2155,10 +2155,10 @@ END_CASE(JSOP_SWAP) BEGIN_CASE(JSOP_PICK) { - jsint i = regs.pc[1]; - JS_ASSERT(regs.sp - (i+1) >= regs.fp()->base()); - Value lval = regs.sp[-(i+1)]; - memmove(regs.sp - (i+1), regs.sp - i, sizeof(Value)*i); + unsigned i = GET_UINT8(regs.pc); + JS_ASSERT(regs.sp - (i + 1) >= regs.fp()->base()); + Value lval = regs.sp[-int(i + 1)]; + memmove(regs.sp - (i + 1), regs.sp - i, sizeof(Value) * i); regs.sp[-1] = lval; } END_CASE(JSOP_PICK) @@ -3772,18 +3772,16 @@ END_CASE(JSOP_HOLE) BEGIN_CASE(JSOP_NEWINIT) { - jsint i = regs.pc[1]; - + uint8_t i = GET_UINT8(regs.pc); JS_ASSERT(i == JSProto_Array || i == JSProto_Object); - JSObject *obj; + JSObject *obj; if (i == JSProto_Array) { obj = NewDenseEmptyArray(cx); } else { gc::AllocKind kind = GuessObjectGCKind(0); obj = NewBuiltinClassInstance(cx, &ObjectClass, kind); } - if (!obj) goto error; diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 1adb819a660d..44d34d32dafa 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -695,7 +695,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, goto print_int; case JOF_UINT8: - i = pc[1]; + i = GET_UINT8(pc); goto print_int; case JOF_INT8: @@ -3868,7 +3868,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_ITER: - foreach = (pc[1] & (JSITER_FOREACH | JSITER_KEYVALUE)) == + foreach = (GET_UINT8(pc) & (JSITER_FOREACH | JSITER_KEYVALUE)) == JSITER_FOREACH; todo = -2; break; @@ -5150,7 +5150,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_NEWINIT: { - i = pc[1]; + i = GET_UINT8(pc); LOCAL_ASSERT(i == JSProto_Array || i == JSProto_Object); todo = ss->sprinter.getOffset(); diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index ff55800fbbec..7b29a17c62ac 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -151,6 +151,18 @@ typedef enum JSOp { * Immediate operand getters, setters, and bounds. */ +static JS_ALWAYS_INLINE uint8_t +GET_UINT8(jsbytecode *pc) +{ + return (uint8_t) pc[1]; +} + +static JS_ALWAYS_INLINE void +SET_UINT8(jsbytecode *pc, uint8_t u) +{ + pc[1] = (jsbytecode) u; +} + /* Common uint16_t immediate format helpers. */ #define UINT16_LEN 2 #define UINT16_HI(i) ((jsbytecode)((i) >> 8)) diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 1b31ecdaeba4..a74d0f70dc99 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -2259,12 +2259,12 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_PICK) { - int32_t amt = GET_INT8(PC); + uint32_t amt = GET_UINT8(PC); // Push -(amt + 1), say amt == 2 // Stack before: X3 X2 X1 // Stack after: X3 X2 X1 X3 - frame.dupAt(-(amt + 1)); + frame.dupAt(-int32_t(amt + 1)); // For each item X[i...1] push it then move it down. // The above would transition like so: @@ -2272,7 +2272,7 @@ mjit::Compiler::generateMethod() // X2 X2 X1 X3 (shift) // X2 X2 X1 X3 X1 (dupAt) // X2 X1 X1 X3 (shift) - for (int32_t i = -amt; i < 0; i++) { + for (int32_t i = -int32_t(amt); i < 0; i++) { frame.dupAt(i - 1); frame.shift(i - 2); } @@ -2731,7 +2731,7 @@ mjit::Compiler::generateMethod() END_CASE(JSOP_STRICTEQ) BEGIN_CASE(JSOP_ITER) - if (!iter(PC[1])) + if (!iter(GET_UINT8(PC))) return Compile_Error; END_CASE(JSOP_ITER) @@ -6777,7 +6777,7 @@ mjit::Compiler::jsop_newinit() JSObject *baseobj = NULL; switch (*PC) { case JSOP_NEWINIT: - isArray = (PC[1] == JSProto_Array); + isArray = (GET_UINT8(PC) == JSProto_Array); break; case JSOP_NEWARRAY: isArray = true; From ab65bc54f2595c24f1c41f0289a31018b3e6ce07 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Wed, 18 Jan 2012 16:55:59 -0800 Subject: [PATCH 39/58] Bug 720316 - Remove the entirely-unused JOF_SLOTATOM. r=luke --HG-- extra : rebase_source : 2f2222eebe863873d7d85323e9dd5c5484c72193 --- js/src/frontend/BytecodeCompiler.cpp | 6 +----- js/src/frontend/BytecodeEmitter.cpp | 3 +-- js/src/jsopcode.cpp | 10 +--------- js/src/jsopcode.h | 2 +- js/src/jsopcodeinlines.h | 3 +-- 5 files changed, 5 insertions(+), 19 deletions(-) diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp index d364592108af..847befaff441 100644 --- a/js/src/frontend/BytecodeCompiler.cpp +++ b/js/src/frontend/BytecodeCompiler.cpp @@ -334,11 +334,7 @@ frontend::CompileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF len = (cs->length > 0) ? (uintN) cs->length : js_GetVariableBytecodeLength(code); - if ((cs->format & JOF_SHARPSLOT) || - JOF_TYPE(cs->format) == JOF_LOCAL || - (JOF_TYPE(cs->format) == JOF_SLOTATOM)) { - JS_ASSERT_IF(!(cs->format & JOF_SHARPSLOT), - JOF_TYPE(cs->format) != JOF_SLOTATOM); + if ((cs->format & JOF_SHARPSLOT) || JOF_TYPE(cs->format) == JOF_LOCAL) { slot = GET_SLOTNO(code); if (!(cs->format & JOF_SHARPSLOT)) slot += bce.sharpSlots(); diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 3530c8cdac52..a4fbade1a20e 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1032,8 +1032,7 @@ EmitSlotIndexOp(JSContext *cx, JSOp op, uintN slot, uintN index, BytecodeEmitter ptrdiff_t off; jsbytecode *pc; - JS_ASSERT(JOF_OPTYPE(op) == JOF_SLOTATOM || - JOF_OPTYPE(op) == JOF_SLOTOBJECT); + JS_ASSERT(JOF_OPTYPE(op) == JOF_SLOTOBJECT); bigSuffix = EmitBigIndexPrefix(cx, bce, index); if (bigSuffix == JSOP_FALSE) return JS_FALSE; diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 44d34d32dafa..0aa8694bda8a 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -657,18 +657,10 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, Sprint(sp, " %u", GET_SLOTNO(pc)); break; - case JOF_SLOTATOM: case JOF_SLOTOBJECT: { Sprint(sp, " %u", GET_SLOTNO(pc)); uintN index = js_GetIndexFromBytecode(script, pc, SLOTNO_LEN); - jsval v; - if (type == JOF_SLOTATOM) { - JSAtom *atom = script->getAtom(index); - v = STRING_TO_JSVAL(atom); - } else { - v = OBJECT_TO_JSVAL(script->getObject(index)); - } - + jsval v = OBJECT_TO_JSVAL(script->getObject(index)); JSAutoByteString bytes; if (!ToDisassemblySource(cx, v, &bytes)) return 0; diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 7b29a17c62ac..08861ba3640b 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -82,7 +82,7 @@ typedef enum JSOp { #define JOF_LOOKUPSWITCH 5 /* lookup switch */ #define JOF_QARG 6 /* quickened get/set function argument ops */ #define JOF_LOCAL 7 /* var or block-local variable */ -#define JOF_SLOTATOM 8 /* uint16_t slot + constant index */ +/* 8 is unused */ #define JOF_UINT24 12 /* extended unsigned 24-bit literal (index) */ #define JOF_UINT8 13 /* uint8_t immediate, e.g. top 8 bits of 24-bit atom index */ diff --git a/js/src/jsopcodeinlines.h b/js/src/jsopcodeinlines.h index d2ab3f7de0ff..0178d73c938f 100644 --- a/js/src/jsopcodeinlines.h +++ b/js/src/jsopcodeinlines.h @@ -54,9 +54,8 @@ GetNameFromBytecode(JSContext *cx, jsbytecode *pc, JSOp op, const JSCodeSpec &cs return cx->runtime->atomState.classPrototypeAtom; JSScript *script = cx->stack.currentScript(); - ptrdiff_t pcoff = (JOF_TYPE(cs.format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0; PropertyName *name; - GET_NAME_FROM_BYTECODE(script, pc, pcoff, name); + GET_NAME_FROM_BYTECODE(script, pc, 0, name); return name; } From e2b7cdf1db022f0a52f086c1461406cf5d362b27 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Thu, 19 Jan 2012 17:15:24 -0800 Subject: [PATCH 40/58] Bug 720316 - Convert RegExp indexes into uint32_t. r=luke --HG-- extra : rebase_source : bc166b8b951b7142b27f423fdbb6aba7e3087d97 --- js/src/frontend/BytecodeEmitter.cpp | 25 ++++++++++++++++++++- js/src/jsinterp.cpp | 6 ++--- js/src/jsopcode.cpp | 35 ++++++++++++++++------------- js/src/jsopcode.h | 29 ++++++++++++++++-------- js/src/jsopcode.tbl | 2 +- js/src/jsxdrapi.h | 2 +- js/src/methodjit/Compiler.cpp | 2 +- 7 files changed, 69 insertions(+), 32 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index a4fbade1a20e..4f1f0c6a0eb3 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1015,6 +1015,29 @@ EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce) return EmitIndexOp(cx, op, bce->objectList.index(objbox), bce); } +static bool +EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce) +{ + const size_t len = 1 + UINT32_INDEX_LEN; + ptrdiff_t offset = EmitCheck(cx, bce, len); + if (offset < 0) + return false; + + jsbytecode *next = bce->next(); + next[0] = jsbytecode(op); + SET_UINT32_INDEX(next, index); + bce->current->next = next + len; + UpdateDepth(cx, bce, offset); + CheckTypeSet(cx, bce, op); + return true; +} + +static bool +EmitRegExp(JSContext *cx, uint32_t index, BytecodeEmitter *bce) +{ + return EmitIndex32(cx, JSOP_REGEXP, index, bce); +} + /* * What good are ARGNO_LEN and SLOTNO_LEN, you ask? The answer is that, apart * from EmitSlotIndexOp, they abstract out the detail that both are 2, and in @@ -6670,7 +6693,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) case PNK_REGEXP: JS_ASSERT(pn->isOp(JSOP_REGEXP)); - ok = EmitIndexOp(cx, JSOP_REGEXP, bce->regexpList.index(pn->pn_objbox), bce); + ok = EmitRegExp(cx, bce->regexpList.index(pn->pn_objbox), bce); break; #if JS_HAS_XML_SUPPORT diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index e2ea1b33baa0..91af33ba1555 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1603,8 +1603,8 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) * Initialize the index segment register used by LOAD_ATOM and * GET_FULL_INDEX macros below. As a register we use a pointer based on * the atom map to turn frequently executed LOAD_ATOM into simple array - * access. For less frequent object and regexp loads we have to recover - * the segment from atoms pointer first. + * access. For less frequent object loads we have to recover the segment + * from atoms pointer first. */ JSAtom **atoms = script->atoms; @@ -3085,7 +3085,7 @@ BEGIN_CASE(JSOP_REGEXP) * Push a regexp object cloned from the regexp literal object mapped by the * bytecode at pc. */ - jsatomid index = GET_FULL_INDEX(0); + uint32_t index = GET_UINT32_INDEX(regs.pc); JSObject *proto = regs.fp()->scopeChain().global().getOrCreateRegExpPrototype(cx); if (!proto) goto error; diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 0aa8694bda8a..fddf5702b99f 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -570,8 +570,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, } case JOF_ATOM: - case JOF_OBJECT: - case JOF_REGEXP: { + case JOF_OBJECT: { uintN index = js_GetIndexFromBytecode(script, pc, 0); jsval v; if (type == JOF_ATOM) { @@ -582,17 +581,15 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, v = STRING_TO_JSVAL(atom); } } else { - JSObject *obj; - if (type == JOF_OBJECT) { - /* Don't call obj.toSource if analysis/inference is active. */ - if (cx->compartment->activeAnalysis) { - Sprint(sp, " object"); - break; - } - obj = script->getObject(index); - } else { - obj = script->getRegExp(index); + JS_ASSERT(type == JOF_OBJECT); + + /* Don't call obj.toSource if analysis/inference is active. */ + if (cx->compartment->activeAnalysis) { + Sprint(sp, " object"); + break; } + + JSObject *obj = script->getObject(index); v = OBJECT_TO_JSVAL(obj); } { @@ -604,6 +601,15 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, break; } + case JOF_REGEXP: { + JSObject *obj = script->getRegExp(GET_UINT32_INDEX(pc)); + JSAutoByteString bytes; + if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes)) + return 0; + Sprint(sp, " %s", bytes.ptr()); + break; + } + case JOF_TABLESWITCH: { jsint i, low, high; @@ -2662,9 +2668,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) #define LOAD_FUNCTION(PCOFF) \ GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun) -#define LOAD_REGEXP(PCOFF) \ - GET_REGEXP_FROM_BYTECODE(jp->script, pc, PCOFF, obj) - #define GET_SOURCE_NOTE_ATOM(sn, atom) \ JS_BEGIN_MACRO \ jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \ @@ -4934,7 +4937,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) goto sprint_string; case JSOP_REGEXP: - GET_REGEXP_FROM_BYTECODE(jp->script, pc, 0, obj); + obj = jp->script->getRegExp(GET_UINT32_INDEX(pc)); str = obj->asRegExp().toString(cx); if (!str) return NULL; diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 08861ba3640b..64e5cfece2fa 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -89,7 +89,7 @@ typedef enum JSOp { #define JOF_INT32 14 /* int32_t immediate operand */ #define JOF_OBJECT 15 /* unsigned 16-bit object index */ #define JOF_SLOTOBJECT 16 /* uint16_t slot index + object index */ -#define JOF_REGEXP 17 /* unsigned 16-bit regexp index */ +#define JOF_REGEXP 17 /* unsigned 32-bit regexp index */ #define JOF_INT8 18 /* int8_t immediate operand */ #define JOF_ATOMOBJECT 19 /* uint16_t constant index + object index */ #define JOF_UINT16PAIR 20 /* pair of uint16_t immediates */ @@ -191,10 +191,27 @@ SET_JUMP_OFFSET(jsbytecode *pc, int32_t off) pc[4] = (jsbytecode)off; } +#define UINT32_INDEX_LEN 4 + +static JS_ALWAYS_INLINE uint32_t +GET_UINT32_INDEX(jsbytecode *pc) +{ + return (pc[1] << 24) | (pc[2] << 16) | (pc[3] << 8) | pc[4]; +} + +static JS_ALWAYS_INLINE void +SET_UINT32_INDEX(jsbytecode *pc, uint32_t index) +{ + pc[1] = (jsbytecode)(index >> 24); + pc[2] = (jsbytecode)(index >> 16); + pc[3] = (jsbytecode)(index >> 8); + pc[4] = (jsbytecode)index; +} + /* * A literal is indexed by a per-script atom or object maps. Most scripts - * have relatively few literals, so the standard JOF_ATOM, JOF_OBJECT and - * JOF_REGEXP formats specifies a fixed 16 bits of immediate operand index. + * have relatively few literals, so the standard JOF_ATOM and JOF_OBJECT + * format specifies a fixed 16 bits of immediate operand index. * A script with more than 64K literals must wrap the bytecode into * JSOP_INDEXBASE and JSOP_RESETBASE pair. */ @@ -357,12 +374,6 @@ js_GetIndexFromBytecode(JSScript *script, jsbytecode *pc, ptrdiff_t pcoff); fun = (script)->getFunction(index_); \ JS_END_MACRO -#define GET_REGEXP_FROM_BYTECODE(script, pc, pcoff, obj) \ - JS_BEGIN_MACRO \ - uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \ - obj = (script)->getRegExp(index_); \ - JS_END_MACRO - #ifdef __cplusplus namespace js { diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index da30f0b71209..e247e1fd4b43 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -411,7 +411,7 @@ OPDEF(JSOP_GNAMEINC, 158,"gnameinc", NULL, 4, 0, 1, 15, JOF_ATOM| OPDEF(JSOP_GNAMEDEC, 159,"gnamedec", NULL, 4, 0, 1, 15, JOF_ATOM|JOF_NAME|JOF_DEC|JOF_POST|JOF_TMPSLOT3|JOF_GNAME|JOF_DECOMPOSE) /* Regular expression literal requiring special "fork on exec" handling. */ -OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 3, 0, 1, 19, JOF_REGEXP) +OPDEF(JSOP_REGEXP, 160,"regexp", NULL, 5, 0, 1, 19, JOF_REGEXP) /* XML (ECMA-357, a.k.a. "E4X") support. */ OPDEF(JSOP_DEFXMLNS, 161,"defxmlns", NULL, 1, 1, 0, 0, JOF_BYTE) diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index d5155aa4b1aa..0665a7922c19 100644 --- a/js/src/jsxdrapi.h +++ b/js/src/jsxdrapi.h @@ -226,7 +226,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32_t id); * and saved versions. If deserialization fails, the data should be * invalidated if possible. */ -#define JSXDR_BYTECODE_VERSION (0xb973c0de - 104) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 105) /* * Library-private functions. diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index a74d0f70dc99..19ddac3600ba 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -6870,7 +6870,7 @@ mjit::Compiler::jsop_newinit() bool mjit::Compiler::jsop_regexp() { - JSObject *obj = script->getRegExp(fullAtomIndex(PC)); + JSObject *obj = script->getRegExp(GET_UINT32_INDEX(PC)); RegExpStatics *res = globalObj ? globalObj->getRegExpStatics() : NULL; if (!globalObj || From af40d3d15a5fccfeb6f487aca15fad684088ceee Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 23 Jan 2012 16:35:12 -0800 Subject: [PATCH 41/58] Don't leak JITScript->shimPool, bug 720359. r=dvander --- js/src/methodjit/MethodJIT.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/js/src/methodjit/MethodJIT.cpp b/js/src/methodjit/MethodJIT.cpp index 6ca42f6dc970..32befd5e4d10 100644 --- a/js/src/methodjit/MethodJIT.cpp +++ b/js/src/methodjit/MethodJIT.cpp @@ -1296,6 +1296,9 @@ JITScript::destroy(JSContext *cx) { for (unsigned i = 0; i < nchunks; i++) destroyChunk(cx, i); + + if (shimPool) + shimPool->release(); } void From 0822b0d7980f46f3037283b7eda764233b5973be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Tue, 24 Jan 2012 01:37:54 +0100 Subject: [PATCH 42/58] Bug 485237 - Use \!toolbar.visible instead of the chromehidden attribute existence to identify popups. r=gavin --HG-- extra : rebase_source : e5a18e5dca37e8ce3a0b5c832b376787d269b310 --- browser/base/content/browser.js | 2 +- browser/base/content/utilityOverlay.js | 4 ++-- browser/components/nsBrowserGlue.js | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 6b0662afb219..804381e1ecf9 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -5201,7 +5201,7 @@ nsBrowserAccess.prototype = { let win, needToFocusWin; // try the current window. if we're in a popup, fall back on the most recent browser window - if (!window.document.documentElement.getAttribute("chromehidden")) + if (window.toolbar.visible) win = window; else { win = Cc["@mozilla.org/browser/browserglue;1"] diff --git a/browser/base/content/utilityOverlay.js b/browser/base/content/utilityOverlay.js index 9a2146a0ef26..d98605b87818 100644 --- a/browser/base/content/utilityOverlay.js +++ b/browser/base/content/utilityOverlay.js @@ -56,7 +56,7 @@ function getTopWin(skipPopups) { // whether it's the frontmost window, since commands can be executed in // background windows (bug 626148). if (top.document.documentElement.getAttribute("windowtype") == "navigator:browser" && - (!skipPopups || !top.document.documentElement.getAttribute("chromehidden"))) + (!skipPopups || top.toolbar.visible)) return top; if (skipPopups) { @@ -206,7 +206,7 @@ function openLinkIn(url, where, params) { var w = getTopWin(); if ((where == "tab" || where == "tabshifted") && - w && w.document.documentElement.getAttribute("chromehidden")) { + w && !w.toolbar.visible) { w = getTopWin(true); aRelatedToCurrent = false; } diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 10d440f18b90..5bad463283bd 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -1442,7 +1442,7 @@ BrowserGlue.prototype = { getMostRecentBrowserWindow: function BG_getMostRecentBrowserWindow() { function isFullBrowserWindow(win) { return !win.closed && - !win.document.documentElement.getAttribute("chromehidden"); + win.toolbar.visible; } #ifdef BROKEN_WM_Z_ORDER From b5d3fefacde18a3e7e8082ba5971d379052005fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?D=C3=A3o=20Gottwald?= Date: Tue, 24 Jan 2012 01:39:49 +0100 Subject: [PATCH 43/58] Bug 714594 - Don't list app tabs in the all tabs menu. r=mak ui-r=boriss --HG-- extra : rebase_source : 462d500d1f44f637bb30eb7c29d0e444e221964f --- browser/base/content/tabbrowser.xml | 45 +++++++++-------------------- 1 file changed, 13 insertions(+), 32 deletions(-) diff --git a/browser/base/content/tabbrowser.xml b/browser/base/content/tabbrowser.xml index 0b79687b4197..4505ea772463 100644 --- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -3811,49 +3811,34 @@ - - - - - = tabstripBO.screenX && curTabBO.screenX + curTabBO.width <= tabstripBO.screenX + tabstripBO.width) this.childNodes[i].setAttribute("tabIsVisible", "true"); @@ -3896,14 +3879,10 @@ this._setMenuitemAttributes(menuItem, aTab); - // Keep some attributes of the menuitem in sync with its - // corresponding tab (e.g. the tab label) aTab.mCorrespondingMenuitem = menuItem; menuItem.tab = aTab; - menuItem.addEventListener("command", this, false); this.appendChild(menuItem); - return menuItem; ]]> @@ -3938,18 +3917,17 @@ @@ -3960,14 +3938,12 @@ for (let i = this.childNodes.length - 1; i > 0; i--) { let menuItem = this.childNodes[i]; if (menuItem.tab) { - menuItem.removeEventListener("command", this, false); menuItem.tab.mCorrespondingMenuitem = null; this.removeChild(menuItem); } } var tabcontainer = gBrowser.tabContainer; tabcontainer.mTabstrip.removeEventListener("scroll", this, false); - tabcontainer.removeEventListener("TabOpen", this, false); tabcontainer.removeEventListener("TabAttrModified", this, false); tabcontainer.removeEventListener("TabClose", this, false); ]]> @@ -3988,6 +3964,11 @@ XULBrowserWindow.setOverLink("", null); ]]> + + From afd6a8403b8348f9656b1d0472ea5a768dbe16b6 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 23 Jan 2012 16:42:21 -0800 Subject: [PATCH 44/58] Convert integers to doubles at join points immediately after a chunk boundary, bug 719918. r=dvander --- js/src/jit-test/tests/jaeger/bug719918.js | 19 +++++++++++++++++++ js/src/methodjit/Compiler.cpp | 2 ++ 2 files changed, 21 insertions(+) create mode 100644 js/src/jit-test/tests/jaeger/bug719918.js diff --git a/js/src/jit-test/tests/jaeger/bug719918.js b/js/src/jit-test/tests/jaeger/bug719918.js new file mode 100644 index 000000000000..9ea89a0be915 --- /dev/null +++ b/js/src/jit-test/tests/jaeger/bug719918.js @@ -0,0 +1,19 @@ +function test(m) { + do { + if (m = arr[0]) break; + m = 0; + } + while (0); + arr[1] = m; +} + +mjitChunkLimit(10); + +arr = new Float64Array(2); + +// run function a lot to trigger methodjit compile +for(var i=0; i<200; i++) + test(0); + +// should return 0, not NaN +assertEq(arr[1], 0) diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 19ddac3600ba..4f92c4bee623 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -1992,6 +1992,8 @@ mjit::Compiler::generateMethod() if (PC >= script->code + chunkEnd) { if (fallthrough) { + if (opinfo->jumpTarget) + fixDoubleTypes(PC); frame.syncAndForgetEverything(); jsbytecode *curPC = PC; do { From b600363fabfcabac2e8db0a525debd6a8e92177d Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Mon, 23 Jan 2012 16:43:12 -0800 Subject: [PATCH 45/58] Bug 716033 - Move write barriers from jsapi.h to jsfriendapi.h (r=luke,bholley) --- js/src/jsapi.cpp | 73 -------------------- js/src/jsapi.h | 99 --------------------------- js/src/jsfriendapi.cpp | 33 +++++++++ js/src/jsfriendapi.h | 50 ++++++++++++++ js/xpconnect/src/XPCWrappedNative.cpp | 18 ++--- js/xpconnect/src/xpcprivate.h | 15 ++-- 6 files changed, 101 insertions(+), 187 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 1117e7135b69..dd4741edb44d 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -6714,79 +6714,6 @@ js_GetCompartmentPrivate(JSCompartment *compartment) /************************************************************************/ -JS_PUBLIC_API(void) -JS_RegisterReference(void **ref) -{ -} - -JS_PUBLIC_API(void) -JS_ModifyReference(void **ref, void *newval) -{ - // XPConnect uses the lower bits of its JSObject refs for evil purposes, - // so we need to fix this. - void *thing = *ref; - *ref = newval; - thing = (void *)((uintptr_t)thing & ~7); - if (!thing) - return; - JS_ASSERT(!static_cast(thing)->compartment()->rt->gcRunning); - uint32_t kind = GetGCThingTraceKind(thing); - if (kind == JSTRACE_OBJECT) - JSObject::writeBarrierPre((JSObject *) thing); - else if (kind == JSTRACE_STRING) - JSString::writeBarrierPre((JSString *) thing); - else - JS_NOT_REACHED("invalid trace kind"); -} - -JS_PUBLIC_API(void) -JS_UnregisterReference(void **ref) -{ - // For now we just want to trigger a write barrier. - JS_ModifyReference(ref, NULL); -} - -JS_PUBLIC_API(void) -JS_UnregisterReferenceRT(JSRuntime *rt, void **ref) -{ - // For now we just want to trigger a write barrier. - if (!rt->gcRunning) - JS_ModifyReference(ref, NULL); -} - -JS_PUBLIC_API(void) -JS_RegisterValue(jsval *val) -{ -} - -JS_PUBLIC_API(void) -JS_ModifyValue(jsval *val, jsval newval) -{ - HeapValue::writeBarrierPre(*val); - *val = newval; -} - -JS_PUBLIC_API(void) -JS_UnregisterValue(jsval *val) -{ - JS_ModifyValue(val, JSVAL_VOID); -} - -JS_PUBLIC_API(void) -JS_UnregisterValueRT(JSRuntime *rt, jsval *val) -{ - if (!rt->gcRunning) - JS_ModifyValue(val, JSVAL_VOID); -} - -JS_PUBLIC_API(JSTracer *) -JS_GetIncrementalGCTracer(JSRuntime *rt) -{ - return rt->gcIncrementalTracer; -} - -/************************************************************************/ - #if !defined(STATIC_EXPORTABLE_JS_API) && !defined(STATIC_JS_API) && defined(XP_WIN) #include "jswin.h" diff --git a/js/src/jsapi.h b/js/src/jsapi.h index ae8e3bb24d07..2cff34f4d797 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3227,105 +3227,6 @@ JS_DumpHeap(JSContext *cx, FILE *fp, void* startThing, JSGCTraceKind kind, #endif -/* - * Write barrier API. - * - * This API is used to inform SpiderMonkey of pointers to JS GC things in the - * malloc heap. There is no need to use this API unless incremental GC is - * enabled. When they are, the requirements for using the API are as follows: - * - * All pointers to JS GC things from the malloc heap must be registered and - * unregistered with the API functions below. This is *in addition* to the - * normal rooting and tracing that must be done normally--these functions will - * not take care of rooting for you. - * - * Besides registration, the JS_ModifyReference function must be called to - * change the value of these references. You should not change them using - * assignment. - * - * Only the RT versions of these functions (which take a JSRuntime argument) - * should be called during GC. Without a JSRuntime, it is not possible to know - * if the object being barriered has already been finalized. - * - * To avoid the headache of using these API functions, the JSBarrieredObjectPtr - * C++ class is provided--simply replace your JSObject* with a - * JSBarrieredObjectPtr. It will take care of calling the registration and - * modification APIs. - * - * For more explanation, see the comment in gc/Barrier.h. - */ - -/* These functions are to be used for objects and strings. */ -extern JS_PUBLIC_API(void) -JS_RegisterReference(void **ref); - -extern JS_PUBLIC_API(void) -JS_ModifyReference(void **ref, void *newval); - -extern JS_PUBLIC_API(void) -JS_UnregisterReference(void **ref); - -extern JS_PUBLIC_API(void) -JS_UnregisterReferenceRT(JSRuntime *rt, void **ref); - -/* These functions are for values. */ -extern JS_PUBLIC_API(void) -JS_RegisterValue(jsval *val); - -extern JS_PUBLIC_API(void) -JS_ModifyValue(jsval *val, jsval newval); - -extern JS_PUBLIC_API(void) -JS_UnregisterValue(jsval *val); - -extern JS_PUBLIC_API(void) -JS_UnregisterValueRT(JSRuntime *rt, jsval *val); - -extern JS_PUBLIC_API(JSTracer *) -JS_GetIncrementalGCTracer(JSRuntime *rt); - -#ifdef __cplusplus -JS_END_EXTERN_C - -namespace JS { - -class HeapPtrObject -{ - JSObject *value; - - public: - HeapPtrObject() : value(NULL) { JS_RegisterReference((void **) &value); } - - HeapPtrObject(JSObject *obj) : value(obj) { JS_RegisterReference((void **) &value); } - - /* Always call finalize before the destructor. */ - ~HeapPtrObject() { JS_ASSERT(!value); } - - void finalize(JSRuntime *rt) { - JS_UnregisterReferenceRT(rt, (void **) &value); - value = NULL; - } - void finalize(JSContext *cx) { finalize(JS_GetRuntime(cx)); } - - void init(JSObject *obj) { value = obj; } - - JSObject *get() const { return value; } - - HeapPtrObject &operator=(JSObject *obj) { - JS_ModifyReference((void **) &value, obj); - return *this; - } - - JSObject &operator*() const { return *value; } - JSObject *operator->() const { return value; } - operator JSObject *() const { return value; } -}; - -} /* namespace JS */ - -JS_BEGIN_EXTERN_C -#endif - /* * Garbage collector API. */ diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 5de82cdaa034..eaf3cd50ad91 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -469,6 +469,39 @@ js::DumpHeapComplete(JSContext *cx, FILE *fp) namespace js { +JS_FRIEND_API(bool) +IsIncrementalBarrierNeeded(JSRuntime *rt) +{ + return !!rt->gcIncrementalTracer && !rt->gcRunning; +} + +JS_FRIEND_API(bool) +IsIncrementalBarrierNeeded(JSContext *cx) +{ + return IsIncrementalBarrierNeeded(cx->runtime); +} + +extern JS_FRIEND_API(void) +IncrementalReferenceBarrier(void *ptr) +{ + if (!ptr) + return; + JS_ASSERT(!static_cast(ptr)->compartment()->rt->gcRunning); + uint32_t kind = gc::GetGCThingTraceKind(ptr); + if (kind == JSTRACE_OBJECT) + JSObject::writeBarrierPre((JSObject *) ptr); + else if (kind == JSTRACE_STRING) + JSString::writeBarrierPre((JSString *) ptr); + else + JS_NOT_REACHED("invalid trace kind"); +} + +extern JS_FRIEND_API(void) +IncrementalValueBarrier(const Value &v) +{ + HeapValue::writeBarrierPre(v); +} + /* static */ void AutoLockGC::LockGC(JSRuntime *rt) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 4b0e7815fdf0..ab74e35d6532 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -565,6 +565,56 @@ GetRuntimeCompartments(JSRuntime *rt); extern JS_FRIEND_API(size_t) SizeOfJSContext(); +extern JS_FRIEND_API(bool) +IsIncrementalBarrierNeeded(JSRuntime *rt); + +extern JS_FRIEND_API(bool) +IsIncrementalBarrierNeeded(JSContext *cx); + +extern JS_FRIEND_API(void) +IncrementalReferenceBarrier(void *ptr); + +extern JS_FRIEND_API(void) +IncrementalValueBarrier(const Value &v); + +class ObjectPtr +{ + JSObject *value; + + public: + ObjectPtr() : value(NULL) {} + + ObjectPtr(JSObject *obj) : value(obj) {} + + /* Always call finalize before the destructor. */ + ~ObjectPtr() { JS_ASSERT(!value); } + + void finalize(JSRuntime *rt) { + if (IsIncrementalBarrierNeeded(rt)) + IncrementalReferenceBarrier(value); + value = NULL; + } + void finalize(JSContext *cx) { finalize(JS_GetRuntime(cx)); } + + void init(JSObject *obj) { value = obj; } + + JSObject *get() const { return value; } + + void writeBarrierPre(JSRuntime *rt) { + IncrementalReferenceBarrier(value); + } + + ObjectPtr &operator=(JSObject *obj) { + IncrementalReferenceBarrier(value); + value = obj; + return *this; + } + + JSObject &operator*() const { return *value; } + JSObject *operator->() const { return value; } + operator JSObject *() const { return value; } +}; + } /* namespace js */ /* diff --git a/js/xpconnect/src/XPCWrappedNative.cpp b/js/xpconnect/src/XPCWrappedNative.cpp index a829ad9fada4..d72d9416b930 100644 --- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -826,8 +826,6 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed aIdentity, NS_ASSERTION(mSet, "bad ctor param"); DEBUG_TrackNewWrapper(this); - - JS_RegisterReference((void **) &mWrapperWord); } // This ctor is used if this object will NOT have a proto. @@ -850,8 +848,6 @@ XPCWrappedNative::XPCWrappedNative(already_AddRefed aIdentity, NS_ASSERTION(aSet, "bad ctor param"); DEBUG_TrackNewWrapper(this); - - JS_RegisterReference((void **) &mWrapperWord); } XPCWrappedNative::~XPCWrappedNative() @@ -909,7 +905,8 @@ XPCWrappedNative::Destroy() * the first time because mWrapperWord isn't used afterwards. */ if (XPCJSRuntime *rt = GetRuntime()) { - JS_UnregisterReferenceRT(rt->GetJSRuntime(), (void **) &mWrapperWord); + if (js::IsIncrementalBarrierNeeded(rt->GetJSRuntime())) + js::IncrementalReferenceBarrier(GetWrapperPreserveColor()); mWrapperWord = WRAPPER_WORD_POISON; } else { MOZ_ASSERT(mWrapperWord == WRAPPER_WORD_POISON); @@ -925,7 +922,7 @@ XPCWrappedNative::UpdateScriptableInfo(XPCNativeScriptableInfo *si) // Write barrier for incremental GC. JSRuntime* rt = GetRuntime()->GetJSRuntime(); - if (JS_GetIncrementalGCTracer(rt)) + if (js::IsIncrementalBarrierNeeded(rt)) mScriptableInfo->Mark(); mScriptableInfo = si; @@ -936,12 +933,11 @@ XPCWrappedNative::SetProto(XPCWrappedNativeProto* p) { NS_ASSERTION(!IsWrapperExpired(), "bad ptr!"); + MOZ_ASSERT(HasProto()); + // Write barrier for incremental GC. - if (HasProto()) { - JSRuntime* rt = GetRuntime()->GetJSRuntime(); - if (JS_GetIncrementalGCTracer(rt)) - GetProto()->TraceJS(JS_GetIncrementalGCTracer(rt)); - } + JSRuntime* rt = GetRuntime()->GetJSRuntime(); + GetProto()->WriteBarrierPre(rt); mMaybeProto = p; } diff --git a/js/xpconnect/src/xpcprivate.h b/js/xpconnect/src/xpcprivate.h index fb97da25bef5..cf3882bef0cd 100644 --- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -1654,10 +1654,10 @@ private: // default parent for the XPCWrappedNatives that have us as the scope, // unless a PreCreate hook overrides it. Note that this _may_ be null (see // constructor). - JS::HeapPtrObject mGlobalJSObject; + js::ObjectPtr mGlobalJSObject; // Cached value of Object.prototype - JS::HeapPtrObject mPrototypeJSObject; + js::ObjectPtr mPrototypeJSObject; // Prototype to use for wrappers with no helper. JSObject* mPrototypeNoHelper; @@ -2306,6 +2306,12 @@ public: mScriptableInfo->Mark(); } + void WriteBarrierPre(JSRuntime* rt) + { + if (js::IsIncrementalBarrierNeeded(rt) && mJSProtoObject) + mJSProtoObject.writeBarrierPre(rt); + } + // NOP. This is just here to make the AutoMarkingPtr code compile. inline void AutoTrace(JSTracer* trc) {} @@ -2348,7 +2354,7 @@ private: } XPCWrappedNativeScope* mScope; - JS::HeapPtrObject mJSProtoObject; + js::ObjectPtr mJSProtoObject; nsCOMPtr mClassInfo; PRUint32 mClassInfoFlags; XPCNativeSet* mSet; @@ -2737,8 +2743,9 @@ public: } void SetWrapper(JSObject *obj) { + js::IncrementalReferenceBarrier(GetWrapperPreserveColor()); PRWord newval = PRWord(obj) | (mWrapperWord & FLAG_MASK); - JS_ModifyReference((void **)&mWrapperWord, (void *)newval); + mWrapperWord = newval; } void NoteTearoffs(nsCycleCollectionTraversalCallback& cb); From 6024b2a83d2fb095b7bb2387d373fc8c9cfb2d2d Mon Sep 17 00:00:00 2001 From: Bill McCloskey Date: Mon, 23 Jan 2012 16:43:19 -0800 Subject: [PATCH 46/58] Bug 716027 - Add write barrier to SetReservedSlot in jsfriendapi.h (r=bhackett) --- js/src/jsfriendapi.cpp | 6 ++++++ js/src/jsfriendapi.h | 9 ++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index eaf3cd50ad91..c428e2d1b4bc 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -323,6 +323,12 @@ js::SetFunctionNativeReserved(JSObject *fun, size_t which, const Value &val) fun->toFunction()->setExtendedSlot(which, val); } +JS_FRIEND_API(void) +js::SetReservedSlotWithBarrier(JSObject *obj, size_t slot, const js::Value &value) +{ + obj->setSlot(slot, value); +} + void js::SetPreserveWrapperCallback(JSRuntime *rt, PreserveWrapperCallback callback) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index ab74e35d6532..e47d52743e80 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -378,11 +378,18 @@ GetReservedSlot(const JSObject *obj, size_t slot) return reinterpret_cast(obj)->slotRef(slot); } +JS_FRIEND_API(void) +SetReservedSlotWithBarrier(JSObject *obj, size_t slot, const Value &value); + inline void SetReservedSlot(JSObject *obj, size_t slot, const Value &value) { JS_ASSERT(slot < JSCLASS_RESERVED_SLOTS(GetObjectClass(obj))); - reinterpret_cast(obj)->slotRef(slot) = value; + shadow::Object *sobj = reinterpret_cast(obj); + if (sobj->slotRef(slot).isMarkable()) + SetReservedSlotWithBarrier(obj, slot, value); + else + sobj->slotRef(slot) = value; } JS_FRIEND_API(uint32_t) From fd333c14764f76b3ebe1c09682eb684f3bd8ca60 Mon Sep 17 00:00:00 2001 From: Brian Hackett Date: Mon, 23 Jan 2012 16:50:23 -0800 Subject: [PATCH 47/58] Ensure code is discarded appropriately when kicking frames into the interpreter, bug 719674. r=dvander --- js/src/jsinfer.cpp | 6 ++--- js/src/jsscript.cpp | 2 +- js/src/methodjit/Compiler.cpp | 37 +++++++++++++++++++++++------- js/src/methodjit/InvokeHelpers.cpp | 5 ---- js/src/methodjit/MethodJIT.h | 3 --- js/src/methodjit/Retcon.cpp | 33 ++++++++++++++++++++++++++ js/src/methodjit/Retcon.h | 9 ++++++++ js/src/methodjit/StubCalls.cpp | 36 +++++++++++------------------ 8 files changed, 87 insertions(+), 44 deletions(-) diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 81fd91eceb25..6be599a533f0 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2153,10 +2153,8 @@ TypeCompartment::processPendingRecompiles(JSContext *cx) for (unsigned i = 0; i < pending->length(); i++) { const RecompileInfo &info = (*pending)[i]; mjit::JITScript *jit = info.script->getJIT(info.constructing); - if (jit && jit->chunkDescriptor(info.chunkIndex).chunk) { - mjit::Recompiler::clearStackReferences(cx, info.script); - jit->destroyChunk(cx, info.chunkIndex); - } + if (jit && jit->chunkDescriptor(info.chunkIndex).chunk) + mjit::Recompiler::clearStackReferencesAndChunk(cx, info.script, jit, info.chunkIndex); } #endif /* JS_METHODJIT */ diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index 8711a6c30dde..4503baf0dc89 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1746,7 +1746,7 @@ JSScript::recompileForStepMode(JSContext *cx) { #ifdef JS_METHODJIT if (jitNormal || jitCtor) { - mjit::ClearAllFrames(cx->compartment); + mjit::Recompiler::clearStackReferences(cx, this); mjit::ReleaseScriptCode(cx, this); } #endif diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index 4f92c4bee623..e9db461bbc30 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -732,10 +732,6 @@ MakeJITScript(JSContext *cx, JSScript *script, bool construct) } } - /* - * Watch for cross-chunk edges in a table switch. Don't handle - * lookup switches, as these are always stubbed. - */ if (op == JSOP_TABLESWITCH) { jsbytecode *pc2 = pc; unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); @@ -768,6 +764,31 @@ MakeJITScript(JSContext *cx, JSScript *script, bool construct) } } + if (op == JSOP_LOOKUPSWITCH) { + unsigned defaultOffset = offset + GET_JUMP_OFFSET(pc); + jsbytecode *pc2 = pc + JUMP_OFFSET_LEN; + unsigned npairs = GET_UINT16(pc2); + pc2 += UINT16_LEN; + + CrossChunkEdge edge; + edge.source = offset; + edge.target = defaultOffset; + if (!currentEdges.append(edge)) + return NULL; + + while (npairs) { + pc2 += INDEX_LEN; + unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2); + CrossChunkEdge edge; + edge.source = offset; + edge.target = targetOffset; + if (!currentEdges.append(edge)) + return NULL; + pc2 += JUMP_OFFSET_LEN; + npairs--; + } + } + if (unsigned(offset - chunkStart) > CHUNK_LIMIT) finishChunk = true; @@ -2683,7 +2704,7 @@ mjit::Compiler::generateMethod() masm.move(ImmPtr(PC), Registers::ArgReg1); /* prepareStubCall() is not needed due to syncAndForgetEverything() */ - INLINE_STUBCALL(stubs::TableSwitch, REJOIN_JUMP); + INLINE_STUBCALL(stubs::TableSwitch, REJOIN_NONE); frame.pop(); masm.jump(Registers::ReturnReg); @@ -2702,7 +2723,7 @@ mjit::Compiler::generateMethod() masm.move(ImmPtr(PC), Registers::ArgReg1); /* prepareStubCall() is not needed due to syncAndForgetEverything() */ - INLINE_STUBCALL(stubs::LookupSwitch, REJOIN_JUMP); + INLINE_STUBCALL(stubs::LookupSwitch, REJOIN_NONE); frame.pop(); masm.jump(Registers::ReturnReg); @@ -7358,7 +7379,7 @@ mjit::Compiler::jsop_tableswitch(jsbytecode *pc) masm.move(ImmPtr(originalPC), Registers::ArgReg1); /* prepareStubCall() is not needed due to forgetEverything() */ - INLINE_STUBCALL(stubs::TableSwitch, REJOIN_JUMP); + INLINE_STUBCALL(stubs::TableSwitch, REJOIN_NONE); frame.pop(); masm.jump(Registers::ReturnReg); return true; @@ -7405,7 +7426,7 @@ mjit::Compiler::jsop_tableswitch(jsbytecode *pc) stubcc.linkExitDirect(notInt.get(), stubcc.masm.label()); stubcc.leave(); stubcc.masm.move(ImmPtr(originalPC), Registers::ArgReg1); - OOL_STUBCALL(stubs::TableSwitch, REJOIN_JUMP); + OOL_STUBCALL(stubs::TableSwitch, REJOIN_NONE); stubcc.masm.jump(Registers::ReturnReg); } frame.pop(); diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index c52b0ff08c89..bb1083439fbf 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -872,11 +872,6 @@ js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VM f.regs.pc = nextpc; break; - case REJOIN_JUMP: - f.regs.pc = (jsbytecode *) returnReg; - JS_ASSERT(unsigned(f.regs.pc - script->code) < script->length); - break; - case REJOIN_NATIVE: case REJOIN_NATIVE_LOWERED: case REJOIN_NATIVE_GETTER: { diff --git a/js/src/methodjit/MethodJIT.h b/js/src/methodjit/MethodJIT.h index a457a7df558e..7871bb63093a 100644 --- a/js/src/methodjit/MethodJIT.h +++ b/js/src/methodjit/MethodJIT.h @@ -309,9 +309,6 @@ enum RejoinState { /* State is coherent for the start of the next (fallthrough) bytecode. */ REJOIN_FALLTHROUGH, - /* State is coherent for the start of the bytecode returned by the call. */ - REJOIN_JUMP, - /* * As for REJOIN_FALLTHROUGH, but holds a reference on the compartment's * orphaned native pools which needs to be reclaimed by InternalInterpret. diff --git a/js/src/methodjit/Retcon.cpp b/js/src/methodjit/Retcon.cpp index e63b2ef5b1f6..682c866df37d 100644 --- a/js/src/methodjit/Retcon.cpp +++ b/js/src/methodjit/Retcon.cpp @@ -456,6 +456,39 @@ Recompiler::clearStackReferences(JSContext *cx, JSScript *script) cx->compartment->types.recompilations++; } +void +Recompiler::clearStackReferencesAndChunk(JSContext *cx, JSScript *script, + JITScript *jit, size_t chunkIndex, + bool resetUses) +{ + Recompiler::clearStackReferences(cx, script); + + bool releaseChunk = true; + if (jit->nchunks > 1) { + // If we are in the middle of a native call from a native or getter IC, + // we need to make sure all JIT code for the script is purged, as + // otherwise we will have orphaned the native stub but pointers to it + // still exist in the containing chunk. + for (VMFrame *f = cx->compartment->jaegerCompartment()->activeFrame(); + f != NULL; + f = f->previous) { + if (f->fp()->script() == script) { + JS_ASSERT(f->stubRejoin != REJOIN_NATIVE && + f->stubRejoin != REJOIN_NATIVE_LOWERED && + f->stubRejoin != REJOIN_NATIVE_GETTER); + if (f->stubRejoin == REJOIN_NATIVE_PATCHED) { + mjit::ReleaseScriptCode(cx, script); + releaseChunk = false; + break; + } + } + } + } + + if (releaseChunk) + jit->destroyChunk(cx, chunkIndex, resetUses); +} + } /* namespace mjit */ } /* namespace js */ diff --git a/js/src/methodjit/Retcon.h b/js/src/methodjit/Retcon.h index dabc055ce2b6..222d1a9db827 100644 --- a/js/src/methodjit/Retcon.h +++ b/js/src/methodjit/Retcon.h @@ -65,9 +65,18 @@ namespace mjit { class Recompiler { public: + // Clear all uses of compiled code for script on the stack. This must be + // followed by destroying all JIT code for the script. static void clearStackReferences(JSContext *cx, JSScript *script); + // Clear all uses of compiled code for script on the stack, along with + // the specified compiled chunk. + static void + clearStackReferencesAndChunk(JSContext *cx, JSScript *script, + JITScript *jit, size_t chunkIndex, + bool resetUses = true); + static void expandInlineFrames(JSCompartment *compartment, StackFrame *fp, mjit::CallSite *inlined, StackFrame *next, VMFrame *f); diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index 16e715b518c4..d3dc8706fa0f 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -896,23 +896,8 @@ void JS_FASTCALL stubs::RecompileForInline(VMFrame &f) { ExpandInlineFrames(f.cx->compartment); - Recompiler::clearStackReferences(f.cx, f.script()); - - bool releaseChunk = true; - if (f.jit()->nchunks > 1) { - StackFrame *fp = f.fp(); - for (FrameRegsIter i(f.cx); !i.done(); ++i) { - StackFrame *xfp = i.fp(); - if (xfp->script() == fp->script() && xfp != fp) { - mjit::ReleaseScriptCode(f.cx, fp->script()); - releaseChunk = false; - break; - } - } - } - - if (releaseChunk) - f.jit()->destroyChunk(f.cx, f.chunkIndex(), /* resetUses = */ false); + Recompiler::clearStackReferencesAndChunk(f.cx, f.script(), f.jit(), f.chunkIndex(), + /* resetUses = */ false); } void JS_FASTCALL @@ -1501,13 +1486,18 @@ FindNativeCode(VMFrame &f, jsbytecode *target) if (native) return native; - CompileStatus status = CanMethodJIT(f.cx, f.script(), target, f.fp()->isConstructing(), - CompileRequest_Interpreter); - if (status == Compile_Error) - THROWV(NULL); + uint32_t sourceOffset = f.pc() - f.script()->code; + uint32_t targetOffset = target - f.script()->code; - mjit::ClearAllFrames(f.cx->compartment); - return target; + CrossChunkEdge *edges = f.jit()->edges(); + for (size_t i = 0; i < f.jit()->nedges; i++) { + const CrossChunkEdge &edge = edges[i]; + if (edge.source == sourceOffset && edge.target == targetOffset) + return edge.shimLabel; + } + + JS_NOT_REACHED("Missing edge"); + return NULL; } void * JS_FASTCALL From 0424de52a79d57132a35d450f6083df5a0cd48cf Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Mon, 23 Jan 2012 17:44:30 -0800 Subject: [PATCH 48/58] Bug 717417: Add 'terminate' shell primitive. r=jorendorff --- js/src/jit-test/tests/basic/terminate.js | 9 +++++++++ js/src/shell/js.cpp | 10 ++++++++++ 2 files changed, 19 insertions(+) create mode 100644 js/src/jit-test/tests/basic/terminate.js diff --git a/js/src/jit-test/tests/basic/terminate.js b/js/src/jit-test/tests/basic/terminate.js new file mode 100644 index 000000000000..05b63431de45 --- /dev/null +++ b/js/src/jit-test/tests/basic/terminate.js @@ -0,0 +1,9 @@ +try { + terminate(); + assertEq("execution continued", "execution should not continue"); +} catch (x) { + assertEq("caught exception", "uncatchable"); +} finally { + assertEq("'finally' clause ran", "'finally' clause should not run"); +} +assertEq("top-level execution continued", "top-level execution should not continue"); diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index 22ba64954f8b..ff9682b21a68 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -3958,6 +3958,13 @@ GetMaxArgs(JSContext *cx, uintN arg, jsval *vp) return JS_TRUE; } +static JSBool +Terminate(JSContext *cx, uintN arg, jsval *vp) +{ + JS_ClearPendingException(cx); + return JS_FALSE; +} + static JSFunctionSpec shell_functions[] = { JS_FN("version", Version, 0,0), JS_FN("revertVersion", RevertVersion, 0,0), @@ -4046,6 +4053,7 @@ static JSFunctionSpec shell_functions[] = { JS_FN("parseLegacyJSON",ParseLegacyJSON,1,0), JS_FN("enableStackWalkingAssertion",EnableStackWalkingAssertion,1,0), JS_FN("getMaxArgs", GetMaxArgs, 0,0), + JS_FN("terminate", Terminate, 0,0), JS_FS_END }; @@ -4200,6 +4208,8 @@ static const char *const shell_help_messages[] = { " assertion increases test duration by an order of magnitude, you shouldn't\n" " use this.", "getMaxArgs() Return the maximum number of supported args for a call.", +"terminate() Terminate JavaScript execution, as if we had run out of\n" +" memory or been terminated by the slow script dialog.", /* Keep these last: see the static assertion below. */ #ifdef MOZ_PROFILING From 663a6014a85b7a3c20d2976cc5ade8cd858ef50d Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Mon, 23 Jan 2012 17:44:32 -0800 Subject: [PATCH 49/58] Bug 717417: Don't skip ScriptDebugEpilogue when an onExceptionUnwind handler throws an uncaught exception or terminates the debuggee. r=jorendorff --- .../tests/debug/onExceptionUnwind-08.js | 18 ++++++++++++++++++ .../tests/debug/onExceptionUnwind-09.js | 15 +++++++++++++++ .../tests/debug/onExceptionUnwind-10.js | 16 ++++++++++++++++ js/src/methodjit/InvokeHelpers.cpp | 4 ++-- 4 files changed, 51 insertions(+), 2 deletions(-) create mode 100644 js/src/jit-test/tests/debug/onExceptionUnwind-08.js create mode 100644 js/src/jit-test/tests/debug/onExceptionUnwind-09.js create mode 100644 js/src/jit-test/tests/debug/onExceptionUnwind-10.js diff --git a/js/src/jit-test/tests/debug/onExceptionUnwind-08.js b/js/src/jit-test/tests/debug/onExceptionUnwind-08.js new file mode 100644 index 000000000000..9cea3e626e28 --- /dev/null +++ b/js/src/jit-test/tests/debug/onExceptionUnwind-08.js @@ -0,0 +1,18 @@ +// Ensure that ScriptDebugEpilogue gets called when onExceptionUnwind +// throws an uncaught exception. +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var frame; +dbg.onExceptionUnwind = function (f, x) { + frame = f; + assertEq(frame.live, true); + throw 'unhandled'; +}; +dbg.onDebuggerStatement = function(f) { + assertEq(f.eval('throw 42'), null); + assertEq(frame.live, false); +}; +g.eval('debugger'); + +// Don't fail just because we reported an uncaught exception. +quit(0); diff --git a/js/src/jit-test/tests/debug/onExceptionUnwind-09.js b/js/src/jit-test/tests/debug/onExceptionUnwind-09.js new file mode 100644 index 000000000000..41c364f5914b --- /dev/null +++ b/js/src/jit-test/tests/debug/onExceptionUnwind-09.js @@ -0,0 +1,15 @@ +// Ensure that ScriptDebugEpilogue gets called when onExceptionUnwind +// terminates execution. +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var frame; +dbg.onExceptionUnwind = function (f, x) { + frame = f; + assertEq(frame.live, true); + return null; +}; +dbg.onDebuggerStatement = function(f) { + assertEq(f.eval('throw 42'), null); + assertEq(frame.live, false); +}; +g.eval('debugger'); diff --git a/js/src/jit-test/tests/debug/onExceptionUnwind-10.js b/js/src/jit-test/tests/debug/onExceptionUnwind-10.js new file mode 100644 index 000000000000..719245c8373e --- /dev/null +++ b/js/src/jit-test/tests/debug/onExceptionUnwind-10.js @@ -0,0 +1,16 @@ +// Ensure that ScriptDebugEpilogue gets called when onExceptionUnwind +// terminates execution. +var g = newGlobal('new-compartment'); +var dbg = Debugger(g); +var frame; +dbg.onExceptionUnwind = function (f, x) { + frame = f; + assertEq(frame.type, 'eval'); + assertEq(frame.live, true); + terminate(); +}; +dbg.onDebuggerStatement = function(f) { + assertEq(f.eval('throw 42'), null); + assertEq(frame.live, false); +}; +g.eval('debugger'); diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index bb1083439fbf..ed7be6efb680 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -547,10 +547,10 @@ js_InternalThrow(VMFrame &f) switch (st) { case JSTRAP_ERROR: cx->clearPendingException(); - return NULL; + break; case JSTRAP_CONTINUE: - break; + break; case JSTRAP_RETURN: cx->clearPendingException(); From 8dc1c017f00328e202c2a214b32bd666ef8f0a4d Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 23 Jan 2012 19:18:24 -0800 Subject: [PATCH 50/58] Bug 706684 - Implement a simpler scale gesture listener on Android. r=Cwiiis --- mobile/android/base/Makefile.in | 1 + mobile/android/base/gfx/LayerController.java | 5 +- mobile/android/base/gfx/LayerView.java | 7 +- mobile/android/base/gfx/PointUtils.java | 6 + mobile/android/base/ui/PanZoomController.java | 9 +- .../base/ui/SimpleScaleGestureDetector.java | 327 ++++++++++++++++++ 6 files changed, 346 insertions(+), 9 deletions(-) create mode 100644 mobile/android/base/ui/SimpleScaleGestureDetector.java diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 1fd37c12bf9a..87dc508f3f56 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -125,6 +125,7 @@ FENNEC_JAVA_FILES = \ gfx/WidgetTileLayer.java \ ui/Axis.java \ ui/PanZoomController.java \ + ui/SimpleScaleGestureDetector.java \ ui/SubdocumentScrollHelper.java \ GeckoNetworkManager.java \ $(NULL) diff --git a/mobile/android/base/gfx/LayerController.java b/mobile/android/base/gfx/LayerController.java index d9ae22355ae4..ad6f5270f83c 100644 --- a/mobile/android/base/gfx/LayerController.java +++ b/mobile/android/base/gfx/LayerController.java @@ -43,6 +43,7 @@ import org.mozilla.gecko.gfx.Layer; import org.mozilla.gecko.gfx.LayerClient; import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.ui.PanZoomController; +import org.mozilla.gecko.ui.SimpleScaleGestureDetector; import org.mozilla.gecko.GeckoApp; import android.content.Context; import android.content.res.Resources; @@ -147,7 +148,9 @@ public class LayerController { public Bitmap getShadowPattern() { return getDrawable("shadow"); } public GestureDetector.OnGestureListener getGestureListener() { return mPanZoomController; } - public ScaleGestureDetector.OnScaleGestureListener getScaleGestureListener() { return mPanZoomController; } + public SimpleScaleGestureDetector.SimpleScaleGestureListener getScaleGestureListener() { + return mPanZoomController; + } public GestureDetector.OnDoubleTapListener getDoubleTapListener() { return mPanZoomController; } private Bitmap getDrawable(String name) { diff --git a/mobile/android/base/gfx/LayerView.java b/mobile/android/base/gfx/LayerView.java index 5a62fa61efb5..7856fd3e3400 100644 --- a/mobile/android/base/gfx/LayerView.java +++ b/mobile/android/base/gfx/LayerView.java @@ -40,6 +40,7 @@ package org.mozilla.gecko.gfx; import org.mozilla.gecko.gfx.FloatSize; import org.mozilla.gecko.gfx.InputConnectionHandler; import org.mozilla.gecko.gfx.LayerController; +import org.mozilla.gecko.ui.SimpleScaleGestureDetector; import android.content.Context; import android.opengl.GLSurfaceView; import android.view.GestureDetector; @@ -47,7 +48,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; -import android.view.ScaleGestureDetector; /** * A view rendered by the layer compositor. @@ -61,7 +61,7 @@ public class LayerView extends GLSurfaceView { private InputConnectionHandler mInputConnectionHandler; private LayerRenderer mRenderer; private GestureDetector mGestureDetector; - private ScaleGestureDetector mScaleGestureDetector; + private SimpleScaleGestureDetector mScaleGestureDetector; private long mRenderTime; private boolean mRenderTimeReset; @@ -74,7 +74,8 @@ public class LayerView extends GLSurfaceView { setRenderer(mRenderer); setRenderMode(RENDERMODE_WHEN_DIRTY); mGestureDetector = new GestureDetector(context, controller.getGestureListener()); - mScaleGestureDetector = new ScaleGestureDetector(context, controller.getScaleGestureListener()); + mScaleGestureDetector = + new SimpleScaleGestureDetector(controller.getScaleGestureListener()); mGestureDetector.setOnDoubleTapListener(controller.getDoubleTapListener()); mInputConnectionHandler = null; diff --git a/mobile/android/base/gfx/PointUtils.java b/mobile/android/base/gfx/PointUtils.java index d1a5438be210..5485f00b523c 100644 --- a/mobile/android/base/gfx/PointUtils.java +++ b/mobile/android/base/gfx/PointUtils.java @@ -39,6 +39,7 @@ package org.mozilla.gecko.gfx; import android.graphics.Point; import android.graphics.PointF; +import android.util.FloatMath; import org.json.JSONObject; import org.json.JSONException; @@ -77,6 +78,11 @@ public final class PointUtils { return (float)Math.sqrt(point.x * point.x + point.y * point.y); } + /** Computes the scalar distance between two points. */ + public static float distance(PointF one, PointF two) { + return PointF.length(one.x - two.x, one.y - two.y); + } + public static JSONObject toJSON(PointF point) throws JSONException { // Ensure we put ints, not longs, because Gecko message handlers call getInt(). int x = Math.round(point.x); diff --git a/mobile/android/base/ui/PanZoomController.java b/mobile/android/base/ui/PanZoomController.java index d7e0d0acc377..7b988e62d51c 100644 --- a/mobile/android/base/ui/PanZoomController.java +++ b/mobile/android/base/ui/PanZoomController.java @@ -55,7 +55,6 @@ import android.util.FloatMath; import android.util.Log; import android.view.GestureDetector; import android.view.MotionEvent; -import android.view.ScaleGestureDetector; import java.util.Timer; import java.util.TimerTask; @@ -67,7 +66,7 @@ import java.util.TimerTask; */ public class PanZoomController extends GestureDetector.SimpleOnGestureListener - implements ScaleGestureDetector.OnScaleGestureListener, GeckoEventListener + implements SimpleScaleGestureDetector.SimpleScaleGestureListener, GeckoEventListener { private static final String LOGTAG = "GeckoPanZoomController"; @@ -721,7 +720,7 @@ public class PanZoomController * Zooming */ @Override - public boolean onScaleBegin(ScaleGestureDetector detector) { + public boolean onScaleBegin(SimpleScaleGestureDetector detector) { Log.d(LOGTAG, "onScaleBegin in " + mState); if (mState == PanZoomState.ANIMATED_ZOOM) @@ -737,7 +736,7 @@ public class PanZoomController } @Override - public boolean onScale(ScaleGestureDetector detector) { + public boolean onScale(SimpleScaleGestureDetector detector) { Log.d(LOGTAG, "onScale in state " + mState); if (mState == PanZoomState.ANIMATED_ZOOM) @@ -784,7 +783,7 @@ public class PanZoomController } @Override - public void onScaleEnd(ScaleGestureDetector detector) { + public void onScaleEnd(SimpleScaleGestureDetector detector) { Log.d(LOGTAG, "onScaleEnd in " + mState); if (mState == PanZoomState.ANIMATED_ZOOM) diff --git a/mobile/android/base/ui/SimpleScaleGestureDetector.java b/mobile/android/base/ui/SimpleScaleGestureDetector.java new file mode 100644 index 000000000000..cefc3eaf1780 --- /dev/null +++ b/mobile/android/base/ui/SimpleScaleGestureDetector.java @@ -0,0 +1,327 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * ***** 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 Android code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Patrick Walton + * + * 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 ***** */ + +package org.mozilla.gecko.ui; + +import org.mozilla.gecko.gfx.PointUtils; +import org.json.JSONException; +import android.graphics.PointF; +import android.util.Log; +import android.view.MotionEvent; +import java.util.LinkedList; +import java.util.ListIterator; +import java.util.Stack; + +/** + * A less buggy, and smoother, replacement for the built-in Android ScaleGestureDetector. + * + * This gesture detector is more reliable than the built-in ScaleGestureDetector because: + * + * - It doesn't assume that pointer IDs are numbered 0 and 1. + * + * - It doesn't attempt to correct for "slop" when resting one's hand on the device. On some + * devices (e.g. the Droid X) this can cause the ScaleGestureDetector to lose track of how many + * pointers are down, with disastrous results (bug 706684). + * + * - Cancelling a zoom into a pan is handled correctly. + * + * - Starting with three or more fingers down, releasing fingers so that only two are down, and + * then performing a scale gesture is handled correctly. + * + * - It doesn't take pressure into account, which results in smoother scaling. + */ +public class SimpleScaleGestureDetector { + private static final String LOGTAG = "GeckoSimpleScaleGestureDetector"; + + private SimpleScaleGestureListener mListener; + private long mLastEventTime; + + /* Information about all pointers that are down. */ + private LinkedList mPointerInfo; + + /** Creates a new gesture detector with the given listener. */ + public SimpleScaleGestureDetector(SimpleScaleGestureListener listener) { + mListener = listener; + mPointerInfo = new LinkedList(); + } + + /** Forward touch events to this function. */ + public void onTouchEvent(MotionEvent event) { + switch (event.getAction() & event.ACTION_MASK) { + case MotionEvent.ACTION_DOWN: + case MotionEvent.ACTION_POINTER_DOWN: + onTouchStart(event); + break; + case MotionEvent.ACTION_MOVE: + onTouchMove(event); + break; + case MotionEvent.ACTION_POINTER_UP: + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + onTouchEnd(event); + break; + } + } + + private int getPointersDown() { + return mPointerInfo.size(); + } + + private void onTouchStart(MotionEvent event) { + mLastEventTime = event.getEventTime(); + mPointerInfo.push(PointerInfo.create(event, event.getActionIndex())); + if (getPointersDown() == 2) { + sendScaleGesture(EventType.BEGIN); + } + } + + private void onTouchMove(MotionEvent event) { + mLastEventTime = event.getEventTime(); + for (int i = 0; i < event.getPointerCount(); i++) { + PointerInfo pointerInfo = pointerInfoForEventIndex(event, i); + if (pointerInfo != null) { + pointerInfo.populate(event, i); + } + } + + if (getPointersDown() == 2) { + sendScaleGesture(EventType.CONTINUE); + } + } + + private void onTouchEnd(MotionEvent event) { + mLastEventTime = event.getEventTime(); + + int id = event.getPointerId(event.getActionIndex()); + ListIterator iterator = mPointerInfo.listIterator(); + while (iterator.hasNext()) { + PointerInfo pointerInfo = iterator.next(); + if (pointerInfo.getId() != id) { + continue; + } + + // One of the pointers we were tracking was lifted. Remove its info object from the + // list, recycle it to avoid GC pauses, and send an onScaleEnd() notification if this + // ended the gesture. + iterator.remove(); + pointerInfo.recycle(); + if (getPointersDown() == 1) { + sendScaleGesture(EventType.END); + } + } + } + + /** + * Returns the X coordinate of the focus location (the midpoint of the two fingers). If only + * one finger is down, returns the location of that finger. + */ + public float getFocusX() { + switch (getPointersDown()) { + case 1: + return mPointerInfo.getFirst().getCurrent().x; + case 2: + PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); + return (pointerA.getCurrent().x + pointerB.getCurrent().x) / 2.0f; + } + + Log.e(LOGTAG, "No gesture taking place in getFocusX()!"); + return 0.0f; + } + + /** + * Returns the Y coordinate of the focus location (the midpoint of the two fingers). If only + * one finger is down, returns the location of that finger. + */ + public float getFocusY() { + switch (getPointersDown()) { + case 1: + return mPointerInfo.getFirst().getCurrent().y; + case 2: + PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); + return (pointerA.getCurrent().y + pointerB.getCurrent().y) / 2.0f; + } + + Log.e(LOGTAG, "No gesture taking place in getFocusY()!"); + return 0.0f; + } + + /** Returns the most recent distance between the two pointers. */ + public float getCurrentSpan() { + if (getPointersDown() != 2) { + Log.e(LOGTAG, "No gesture taking place in getCurrentSpan()!"); + return 0.0f; + } + + PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); + return PointUtils.distance(pointerA.getCurrent(), pointerB.getCurrent()); + } + + /** Returns the second most recent distance between the two pointers. */ + public float getPreviousSpan() { + if (getPointersDown() != 2) { + Log.e(LOGTAG, "No gesture taking place in getPreviousSpan()!"); + return 0.0f; + } + + PointerInfo pointerA = mPointerInfo.getFirst(), pointerB = mPointerInfo.getLast(); + PointF a = pointerA.getPrevious(), b = pointerB.getPrevious(); + if (a == null || b == null) { + a = pointerA.getCurrent(); + b = pointerB.getCurrent(); + } + + return PointUtils.distance(a, b); + } + + /** Returns the time of the last event related to the gesture. */ + public long getEventTime() { + return mLastEventTime; + } + + /** Returns true if the scale gesture is in progress and false otherwise. */ + public boolean isInProgress() { + return getPointersDown() == 2; + } + + /* Sends the requested scale gesture notification to the listener. */ + private void sendScaleGesture(EventType eventType) { + switch (eventType) { + case BEGIN: mListener.onScaleBegin(this); break; + case CONTINUE: mListener.onScale(this); break; + case END: mListener.onScaleEnd(this); break; + } + } + + /* + * Returns the pointer info corresponding to the given pointer index, or null if the pointer + * isn't one that's being tracked. + */ + private PointerInfo pointerInfoForEventIndex(MotionEvent event, int index) { + int id = event.getPointerId(index); + for (PointerInfo pointerInfo : mPointerInfo) { + if (pointerInfo.getId() == id) { + return pointerInfo; + } + } + return null; + } + + private enum EventType { + BEGIN, + CONTINUE, + END, + } + + /* Encapsulates information about one of the two fingers involved in the gesture. */ + private static class PointerInfo { + /* A free list that recycles pointer info objects, to reduce GC pauses. */ + private static Stack sPointerInfoFreeList; + + private int mId; + private PointF mCurrent, mPrevious; + + private PointerInfo() { + // External users should use create() instead. + } + + /* Creates or recycles a new PointerInfo instance from an event and a pointer index. */ + public static PointerInfo create(MotionEvent event, int index) { + if (sPointerInfoFreeList == null) { + sPointerInfoFreeList = new Stack(); + } + + PointerInfo pointerInfo; + if (sPointerInfoFreeList.empty()) { + pointerInfo = new PointerInfo(); + } else { + pointerInfo = sPointerInfoFreeList.pop(); + } + + pointerInfo.populate(event, index); + return pointerInfo; + } + + /* + * Fills in the fields of this instance from the given motion event and pointer index + * within that event. + */ + public void populate(MotionEvent event, int index) { + mId = event.getPointerId(index); + mPrevious = mCurrent; + mCurrent = new PointF(event.getX(index), event.getY(index)); + } + + public void recycle() { + mId = -1; + mPrevious = mCurrent = null; + sPointerInfoFreeList.push(this); + } + + public int getId() { return mId; } + public PointF getCurrent() { return mCurrent; } + public PointF getPrevious() { return mPrevious; } + + @Override + public String toString() { + if (mId == -1) { + return "(up)"; + } + + try { + String prevString; + if (mPrevious == null) { + prevString = "n/a"; + } else { + prevString = PointUtils.toJSON(mPrevious).toString(); + } + + // The current position should always be non-null. + String currentString = PointUtils.toJSON(mCurrent).toString(); + return "id=" + mId + " cur=" + currentString + " prev=" + prevString; + } catch (JSONException e) { + throw new RuntimeException(e); + } + } + } + + public static interface SimpleScaleGestureListener { + public boolean onScale(SimpleScaleGestureDetector detector); + public boolean onScaleBegin(SimpleScaleGestureDetector detector); + public void onScaleEnd(SimpleScaleGestureDetector detector); + } +} + From f5853d353ae087043a5207ef4b027096a8c2b96d Mon Sep 17 00:00:00 2001 From: Patrick McManus Date: Mon, 23 Jan 2012 22:46:37 -0500 Subject: [PATCH 51/58] bug 720150 - spdy syn_reply div by zero on valid 0 len inflate buffer r=honzab --- netwerk/protocol/http/SpdySession.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/netwerk/protocol/http/SpdySession.cpp b/netwerk/protocol/http/SpdySession.cpp index 05675e750874..eeafa853ced5 100644 --- a/netwerk/protocol/http/SpdySession.cpp +++ b/netwerk/protocol/http/SpdySession.cpp @@ -891,9 +891,11 @@ SpdySession::HandleSynReply(SpdySession *self) Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_SIZE, self->mFrameDataSize - 6); - PRUint32 ratio = - (self->mFrameDataSize - 6) * 100 / self->mDecompressBufferUsed; - Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio); + if (self->mDecompressBufferUsed) { + PRUint32 ratio = + (self->mFrameDataSize - 6) * 100 / self->mDecompressBufferUsed; + Telemetry::Accumulate(Telemetry::SPDY_SYN_REPLY_RATIO, ratio); + } // status and version are required. nsDependentCSubstring status, version; From 062350623b5589afd34df9c2c60eccec825bb06f Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Mon, 23 Jan 2012 20:10:24 -0800 Subject: [PATCH 52/58] Bug 710533 - Tint the checkerboard with the background color of the page. r=Cwiiis --- mobile/android/base/Makefile.in | 2 +- .../android/base/gfx/CheckerboardImage.java | 150 ++++++++++++++++++ .../base/gfx/GeckoSoftwareLayerClient.java | 26 +++ mobile/android/base/gfx/LayerController.java | 19 ++- mobile/android/base/gfx/LayerRenderer.java | 24 ++- .../base/resources/drawable/checkerboard.png | Bin 199 -> 0 bytes mobile/android/chrome/content/browser.js | 13 +- 7 files changed, 226 insertions(+), 8 deletions(-) create mode 100644 mobile/android/base/gfx/CheckerboardImage.java delete mode 100644 mobile/android/base/resources/drawable/checkerboard.png diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 87dc508f3f56..44445ebe0fd4 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -101,6 +101,7 @@ FENNEC_JAVA_FILES = \ gfx/CairoGLInfo.java \ gfx/CairoImage.java \ gfx/CairoUtils.java \ + gfx/CheckerboardImage.java \ gfx/FloatSize.java \ gfx/GeckoSoftwareLayerClient.java \ gfx/InputConnectionHandler.java \ @@ -510,7 +511,6 @@ MOZ_ANDROID_DRAWABLES += \ mobile/android/base/resources/drawable/tabs_tray_close_button.xml \ mobile/android/base/resources/drawable/tabs_tray_list_divider.xml \ mobile/android/base/resources/drawable/tabs_tray_list_selector.xml \ - mobile/android/base/resources/drawable/checkerboard.png \ mobile/android/base/resources/drawable/shadow.png \ $(NULL) diff --git a/mobile/android/base/gfx/CheckerboardImage.java b/mobile/android/base/gfx/CheckerboardImage.java new file mode 100644 index 000000000000..101c9146d7d9 --- /dev/null +++ b/mobile/android/base/gfx/CheckerboardImage.java @@ -0,0 +1,150 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * ***** 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 Android code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Patrick Walton + * + * 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 ***** */ + +package org.mozilla.gecko.gfx; + +import org.mozilla.gecko.GeckoAppShell; +import android.graphics.Color; +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; +import java.util.Arrays; + +/** A Cairo image that displays a tinted checkerboard. */ +public class CheckerboardImage extends CairoImage { + // The width and height of the checkerboard tile. + private static final int SIZE = 16; + // The pixel format of the checkerboard tile. + private static final int FORMAT = CairoImage.FORMAT_RGB16_565; + // The color to mix in to tint the background color. + private static final int TINT_COLOR = Color.GRAY; + // The amount to mix in. + private static final float TINT_OPACITY = 0.4f; + + private ByteBuffer mBuffer; + private int mMainColor; + + /** Creates a new checkerboard image. */ + public CheckerboardImage() { + int bpp = CairoUtils.bitsPerPixelForCairoFormat(FORMAT); + mBuffer = GeckoAppShell.allocateDirectBuffer(SIZE * SIZE * bpp / 8); + setColor(Color.WHITE); + } + + /** Returns the current color of the checkerboard. */ + public int getColor() { + return mMainColor; + } + + /** Sets the color of the checkerboard image and regenerates it. */ + public void setColor(int color) { + if (mMainColor == color) { + return; + } + + mMainColor = color; + int tintColor = tint(mMainColor); + short mainColor16 = convertTo16Bit(mMainColor), tintColor16 = convertTo16Bit(tintColor); + + short[] mainPattern = new short[SIZE / 2], tintPattern = new short[SIZE / 2]; + Arrays.fill(mainPattern, mainColor16); + Arrays.fill(tintPattern, tintColor16); + + // The checkerboard pattern looks like this: + // + // +---+---+ + // | N | T | N = normal + // +---+---+ T = tinted + // | T | N | + // +---+---+ + + mBuffer.rewind(); + ShortBuffer shortBuffer = mBuffer.asShortBuffer(); + for (int i = 0; i < SIZE / 2; i++) { + shortBuffer.put(mainPattern); + shortBuffer.put(tintPattern); + } + for (int i = SIZE / 2; i < SIZE; i++) { + shortBuffer.put(tintPattern); + shortBuffer.put(mainPattern); + } + } + + // Tints the given color appropriately and returns the tinted color. + private int tint(int color) { + float negTintOpacity = 1.0f - TINT_OPACITY; + float r = Color.red(color) * negTintOpacity + Color.red(TINT_COLOR) * TINT_OPACITY; + float g = Color.green(color) * negTintOpacity + Color.green(TINT_COLOR) * TINT_OPACITY; + float b = Color.blue(color) * negTintOpacity + Color.blue(TINT_COLOR) * TINT_OPACITY; + return Color.rgb(Math.round(r), Math.round(g), Math.round(b)); + } + + // Converts a 32-bit ARGB color to 16-bit R5G6B5, truncating values and discarding the alpha + // channel. + private short convertTo16Bit(int color) { + int r = Color.red(color) >> 3, g = Color.green(color) >> 2, b = Color.blue(color) >> 3; + int c = ((r << 11) | (g << 5) | b); + // Swap endianness. + return (short)((c >> 8) | ((c & 0xff) << 8)); + } + + @Override + protected void finalize() throws Throwable { + try { + if (mBuffer != null) { + GeckoAppShell.freeDirectBuffer(mBuffer); + } + } finally { + super.finalize(); + } + } + + @Override + public ByteBuffer getBuffer() { + return mBuffer; + } + + @Override + public IntSize getSize() { + return new IntSize(SIZE, SIZE); + } + + @Override + public int getFormat() { + return FORMAT; + } +} + diff --git a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java index eb82d649c5ce..abcc7321a34b 100644 --- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java +++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java @@ -54,6 +54,7 @@ import org.mozilla.gecko.GeckoEventListener; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; @@ -63,6 +64,8 @@ import android.util.Log; import org.json.JSONException; import org.json.JSONObject; import java.nio.ByteBuffer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Transfers a software-rendered Gecko to an ImageLayer so that it can be rendered by our @@ -102,6 +105,8 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL // inside a transaction, so no synchronization is needed. private boolean mUpdateViewportOnEndDraw; + private static Pattern sColorPattern; + public GeckoSoftwareLayerClient(Context context) { mContext = context; @@ -227,6 +232,9 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL mTileLayer.setOrigin(PointUtils.round(displayportOrigin)); mTileLayer.setResolution(mGeckoViewport.getZoomFactor()); + int backgroundColor = parseColorFromGecko(viewportObject.getString("backgroundColor")); + controller.setCheckerboardColor(backgroundColor); + if (onlyUpdatePageSize) { // Don't adjust page size when zooming unless zoom levels are // approximately equal. @@ -454,5 +462,23 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL mUpdateViewportOnEndDraw = true; } } + + // Parses a color from an RGB triple of the form "rgb([0-9]+, [0-9]+, [0-9]+)". If the color + // cannot be parsed, returns white. + private static int parseColorFromGecko(String string) { + if (sColorPattern == null) { + sColorPattern = Pattern.compile("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)"); + } + + Matcher matcher = sColorPattern.matcher(string); + if (!matcher.matches()) { + return Color.WHITE; + } + + int r = Integer.parseInt(matcher.group(1)); + int g = Integer.parseInt(matcher.group(2)); + int b = Integer.parseInt(matcher.group(3)); + return Color.rgb(r, g, b); + } } diff --git a/mobile/android/base/gfx/LayerController.java b/mobile/android/base/gfx/LayerController.java index ad6f5270f83c..cc6d9d002214 100644 --- a/mobile/android/base/gfx/LayerController.java +++ b/mobile/android/base/gfx/LayerController.java @@ -81,8 +81,11 @@ public class LayerController { * updates our visible rect appropriately. */ - private OnTouchListener mOnTouchListener; /* The touch listener. */ - private LayerClient mLayerClient; /* The layer client. */ + private OnTouchListener mOnTouchListener; /* The touch listener. */ + private LayerClient mLayerClient; /* The layer client. */ + + /* The new color for the checkerboard. */ + private int mCheckerboardColor; private boolean mForceRedraw; @@ -144,7 +147,6 @@ public class LayerController { } public Bitmap getBackgroundPattern() { return getDrawable("background"); } - public Bitmap getCheckerboardPattern() { return getDrawable("checkerboard"); } public Bitmap getShadowPattern() { return getDrawable("shadow"); } public GestureDetector.OnGestureListener getGestureListener() { return mPanZoomController; } @@ -351,5 +353,16 @@ public class LayerController { return mOnTouchListener.onTouch(mView, event); return false; } + + /** Retrieves the color that the checkerboard should be. */ + public int getCheckerboardColor() { + return mCheckerboardColor; + } + + /** Sets a new color for the checkerboard. */ + public void setCheckerboardColor(int newColor) { + mCheckerboardColor = newColor; + mView.requestRender(); + } } diff --git a/mobile/android/base/gfx/LayerRenderer.java b/mobile/android/base/gfx/LayerRenderer.java index fe9cdb92b0ec..cd620a31c83c 100644 --- a/mobile/android/base/gfx/LayerRenderer.java +++ b/mobile/android/base/gfx/LayerRenderer.java @@ -80,6 +80,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private final LayerView mView; private final SingleTileLayer mBackgroundLayer; + private final CheckerboardImage mCheckerboardImage; private final SingleTileLayer mCheckerboardLayer; private final NinePatchTileLayer mShadowLayer; private final TextLayer mFrameRateLayer; @@ -102,8 +103,8 @@ public class LayerRenderer implements GLSurfaceView.Renderer { CairoImage backgroundImage = new BufferedCairoImage(controller.getBackgroundPattern()); mBackgroundLayer = new SingleTileLayer(true, backgroundImage); - CairoImage checkerboardImage = new BufferedCairoImage(controller.getCheckerboardPattern()); - mCheckerboardLayer = new SingleTileLayer(true, checkerboardImage); + mCheckerboardImage = new CheckerboardImage(); + mCheckerboardLayer = new SingleTileLayer(true, mCheckerboardImage); CairoImage shadowImage = new BufferedCairoImage(controller.getShadowPattern()); mShadowLayer = new NinePatchTileLayer(shadowImage); @@ -171,7 +172,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { if (rootLayer != null) updated &= rootLayer.update(gl, pageContext); updated &= mBackgroundLayer.update(gl, screenContext); updated &= mShadowLayer.update(gl, pageContext); - updated &= mCheckerboardLayer.update(gl, screenContext); + updateCheckerboardLayer(gl, screenContext); updated &= mFrameRateLayer.update(gl, screenContext); updated &= mVertScrollLayer.update(gl, pageContext); updated &= mHorizScrollLayer.update(gl, pageContext); @@ -334,6 +335,23 @@ public class LayerRenderer implements GLSurfaceView.Renderer { }).start(); } + private void updateCheckerboardLayer(GL10 gl, RenderContext renderContext) { + int newCheckerboardColor = mView.getController().getCheckerboardColor(); + if (newCheckerboardColor == mCheckerboardImage.getColor()) { + return; + } + + mCheckerboardLayer.beginTransaction(); + try { + mCheckerboardImage.setColor(newCheckerboardColor); + mCheckerboardLayer.invalidate(); + } finally { + mCheckerboardLayer.endTransaction(); + } + + mCheckerboardLayer.update(gl, renderContext); + } + class FadeRunnable implements Runnable { private boolean mStarted; private long mRunAt; diff --git a/mobile/android/base/resources/drawable/checkerboard.png b/mobile/android/base/resources/drawable/checkerboard.png deleted file mode 100644 index 57cfbe80fdcfaa6866ade23dd363dbb3f0f8c0c2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8pdfpRr>`sfZ7yL^X}!xfj385#N?apKobz*YQ}ap~oQqNuOHxx5$}>wc z6x=<10~GS}6cQDD6O-Fllsf}eYkRslhG?8mwkdwb!)%t2lhYGAr?b(;U{#~Ufgk_> l*RK&uKR-{FSM^sW1B266js})SVOgLy22WQ%mvv4FO#sOXHcbEk diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index e915df9052c6..7b6b76ad54b0 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -802,7 +802,18 @@ var BrowserApp = { }, getDrawMetadata: function getDrawMetadata() { - return JSON.stringify(this.selectedTab.viewport); + let viewport = this.selectedTab.viewport; + + // Sample the background color of the page and pass it along. (This is used to draw the + // checkerboard.) + let browser = this.selectedBrowser; + if (browser) { + let { contentDocument, contentWindow } = browser; + let computedStyle = contentWindow.getComputedStyle(contentDocument.body); + viewport.backgroundColor = computedStyle.backgroundColor; + } + + return JSON.stringify(viewport); }, observe: function(aSubject, aTopic, aData) { From 80f4fe24d32d0a73e2b0f829c758ce044a56a69f Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Mon, 23 Jan 2012 12:03:25 -0800 Subject: [PATCH 53/58] Bug 701002 part 1 - JavaStackTrace: consolidate reportJavaCrash() stack logging. r=dougt --- mobile/android/base/GeckoApp.java | 7 +------ mobile/android/base/GeckoAppShell.java | 13 ++++++++++++- mobile/android/base/GeckoThread.java | 7 +------ 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index ac62ba35c472..18153d0380b9 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -1610,12 +1610,7 @@ abstract public class GeckoApp try { Looper.loop(); } catch (Exception e) { - Log.e(LOGTAG, "top level exception", e); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - pw.flush(); - GeckoAppShell.reportJavaCrash(sw.toString()); + GeckoAppShell.reportJavaCrash(e); } // resetting this is kinda pointless, but oh well sTryCatchAttached = false; diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 38e55ac6fd48..a06b48c6e4bb 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -133,7 +133,18 @@ public class GeckoAppShell public static native void loadGeckoLibsNative(String apkName); public static native void loadSQLiteLibsNative(String apkName, boolean shouldExtract); public static native void onChangeNetworkLinkStatus(String status); - public static native void reportJavaCrash(String stack); + + public static void reportJavaCrash(Throwable e) { + Log.e(LOGTAG, "top level exception", e); + StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw); + e.printStackTrace(pw); + pw.flush(); + reportJavaCrash(sw.toString()); + } + + private static native void reportJavaCrash(String stackTrace); + public static void notifyUriVisited(String uri) { sendEventToGecko(new GeckoEvent(GeckoEvent.VISTITED, uri)); } diff --git a/mobile/android/base/GeckoThread.java b/mobile/android/base/GeckoThread.java index 2faff35c50a8..3276ed219aa0 100644 --- a/mobile/android/base/GeckoThread.java +++ b/mobile/android/base/GeckoThread.java @@ -106,12 +106,7 @@ public class GeckoThread extends Thread { mUri, mRestoreSession); } catch (Exception e) { - Log.e(LOGTAG, "top level exception", e); - StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw); - e.printStackTrace(pw); - pw.flush(); - GeckoAppShell.reportJavaCrash(sw.toString()); + GeckoAppShell.reportJavaCrash(e); } } } From 1258563513a9fcd109b280cbf95e2e535afa0c83 Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Mon, 23 Jan 2012 12:02:56 -0800 Subject: [PATCH 54/58] Bug 701002 part 2 - JavaStackTrace: send "JavaStackTrace" field to Socorro. r=dougt --- widget/android/AndroidJNI.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/widget/android/AndroidJNI.cpp b/widget/android/AndroidJNI.cpp index a4b0bc13935f..d9caba04f90e 100644 --- a/widget/android/AndroidJNI.cpp +++ b/widget/android/AndroidJNI.cpp @@ -206,12 +206,14 @@ Java_org_mozilla_gecko_GeckoAppShell_onChangeNetworkLinkStatus(JNIEnv *jenv, jcl } NS_EXPORT void JNICALL -Java_org_mozilla_gecko_GeckoAppShell_reportJavaCrash(JNIEnv *jenv, jclass, jstring stack) +Java_org_mozilla_gecko_GeckoAppShell_reportJavaCrash(JNIEnv *jenv, jclass, jstring jStackTrace) { #ifdef MOZ_CRASHREPORTER - nsJNIString javaStack(stack, jenv); - CrashReporter::AppendAppNotesToCrashReport(NS_ConvertUTF16toUTF8(javaStack)); -#endif + const nsJNIString stackTrace16(jStackTrace, jenv); + const NS_ConvertUTF16toUTF8 stackTrace8(stackTrace16); + CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("JavaStackTrace"), stackTrace8); +#endif // MOZ_CRASHREPORTER + abort(); } From 79e52670601fc1d92b7539e28c4f04998cca2157 Mon Sep 17 00:00:00 2001 From: Chris Peterson Date: Wed, 18 Jan 2012 13:56:56 -0800 Subject: [PATCH 55/58] Bug 701002 part 3 - JavaStackTrace: remove unused showErrorDialog() method. r=dougt --- mobile/android/base/GeckoApp.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 18153d0380b9..e8a898a97d09 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -197,22 +197,6 @@ abstract public class GeckoApp } } - void showErrorDialog(String message) - { - new AlertDialog.Builder(this) - .setMessage(message) - .setCancelable(false) - .setPositiveButton(R.string.exit_label, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int id) - { - GeckoApp.this.finish(); - System.exit(0); - } - }).show(); - } - public static final String PLUGIN_ACTION = "android.webkit.PLUGIN"; /** From 39424701c8732b89139063ca6c03d856125f44d8 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Mon, 23 Jan 2012 22:53:07 -0800 Subject: [PATCH 56/58] Backed out changeset b77c0c621163 (bug 710533) for native ts bustage --- mobile/android/base/Makefile.in | 2 +- .../android/base/gfx/CheckerboardImage.java | 150 ------------------ .../base/gfx/GeckoSoftwareLayerClient.java | 26 --- mobile/android/base/gfx/LayerController.java | 19 +-- mobile/android/base/gfx/LayerRenderer.java | 24 +-- mobile/android/chrome/content/browser.js | 13 +- 6 files changed, 8 insertions(+), 226 deletions(-) delete mode 100644 mobile/android/base/gfx/CheckerboardImage.java diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 44445ebe0fd4..87dc508f3f56 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -101,7 +101,6 @@ FENNEC_JAVA_FILES = \ gfx/CairoGLInfo.java \ gfx/CairoImage.java \ gfx/CairoUtils.java \ - gfx/CheckerboardImage.java \ gfx/FloatSize.java \ gfx/GeckoSoftwareLayerClient.java \ gfx/InputConnectionHandler.java \ @@ -511,6 +510,7 @@ MOZ_ANDROID_DRAWABLES += \ mobile/android/base/resources/drawable/tabs_tray_close_button.xml \ mobile/android/base/resources/drawable/tabs_tray_list_divider.xml \ mobile/android/base/resources/drawable/tabs_tray_list_selector.xml \ + mobile/android/base/resources/drawable/checkerboard.png \ mobile/android/base/resources/drawable/shadow.png \ $(NULL) diff --git a/mobile/android/base/gfx/CheckerboardImage.java b/mobile/android/base/gfx/CheckerboardImage.java deleted file mode 100644 index 101c9146d7d9..000000000000 --- a/mobile/android/base/gfx/CheckerboardImage.java +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** 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 Android code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2012 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Patrick Walton - * - * 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 ***** */ - -package org.mozilla.gecko.gfx; - -import org.mozilla.gecko.GeckoAppShell; -import android.graphics.Color; -import java.nio.ByteBuffer; -import java.nio.ShortBuffer; -import java.util.Arrays; - -/** A Cairo image that displays a tinted checkerboard. */ -public class CheckerboardImage extends CairoImage { - // The width and height of the checkerboard tile. - private static final int SIZE = 16; - // The pixel format of the checkerboard tile. - private static final int FORMAT = CairoImage.FORMAT_RGB16_565; - // The color to mix in to tint the background color. - private static final int TINT_COLOR = Color.GRAY; - // The amount to mix in. - private static final float TINT_OPACITY = 0.4f; - - private ByteBuffer mBuffer; - private int mMainColor; - - /** Creates a new checkerboard image. */ - public CheckerboardImage() { - int bpp = CairoUtils.bitsPerPixelForCairoFormat(FORMAT); - mBuffer = GeckoAppShell.allocateDirectBuffer(SIZE * SIZE * bpp / 8); - setColor(Color.WHITE); - } - - /** Returns the current color of the checkerboard. */ - public int getColor() { - return mMainColor; - } - - /** Sets the color of the checkerboard image and regenerates it. */ - public void setColor(int color) { - if (mMainColor == color) { - return; - } - - mMainColor = color; - int tintColor = tint(mMainColor); - short mainColor16 = convertTo16Bit(mMainColor), tintColor16 = convertTo16Bit(tintColor); - - short[] mainPattern = new short[SIZE / 2], tintPattern = new short[SIZE / 2]; - Arrays.fill(mainPattern, mainColor16); - Arrays.fill(tintPattern, tintColor16); - - // The checkerboard pattern looks like this: - // - // +---+---+ - // | N | T | N = normal - // +---+---+ T = tinted - // | T | N | - // +---+---+ - - mBuffer.rewind(); - ShortBuffer shortBuffer = mBuffer.asShortBuffer(); - for (int i = 0; i < SIZE / 2; i++) { - shortBuffer.put(mainPattern); - shortBuffer.put(tintPattern); - } - for (int i = SIZE / 2; i < SIZE; i++) { - shortBuffer.put(tintPattern); - shortBuffer.put(mainPattern); - } - } - - // Tints the given color appropriately and returns the tinted color. - private int tint(int color) { - float negTintOpacity = 1.0f - TINT_OPACITY; - float r = Color.red(color) * negTintOpacity + Color.red(TINT_COLOR) * TINT_OPACITY; - float g = Color.green(color) * negTintOpacity + Color.green(TINT_COLOR) * TINT_OPACITY; - float b = Color.blue(color) * negTintOpacity + Color.blue(TINT_COLOR) * TINT_OPACITY; - return Color.rgb(Math.round(r), Math.round(g), Math.round(b)); - } - - // Converts a 32-bit ARGB color to 16-bit R5G6B5, truncating values and discarding the alpha - // channel. - private short convertTo16Bit(int color) { - int r = Color.red(color) >> 3, g = Color.green(color) >> 2, b = Color.blue(color) >> 3; - int c = ((r << 11) | (g << 5) | b); - // Swap endianness. - return (short)((c >> 8) | ((c & 0xff) << 8)); - } - - @Override - protected void finalize() throws Throwable { - try { - if (mBuffer != null) { - GeckoAppShell.freeDirectBuffer(mBuffer); - } - } finally { - super.finalize(); - } - } - - @Override - public ByteBuffer getBuffer() { - return mBuffer; - } - - @Override - public IntSize getSize() { - return new IntSize(SIZE, SIZE); - } - - @Override - public int getFormat() { - return FORMAT; - } -} - diff --git a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java index abcc7321a34b..eb82d649c5ce 100644 --- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java +++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java @@ -54,7 +54,6 @@ import org.mozilla.gecko.GeckoEventListener; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; @@ -64,8 +63,6 @@ import android.util.Log; import org.json.JSONException; import org.json.JSONObject; import java.nio.ByteBuffer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Transfers a software-rendered Gecko to an ImageLayer so that it can be rendered by our @@ -105,8 +102,6 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL // inside a transaction, so no synchronization is needed. private boolean mUpdateViewportOnEndDraw; - private static Pattern sColorPattern; - public GeckoSoftwareLayerClient(Context context) { mContext = context; @@ -232,9 +227,6 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL mTileLayer.setOrigin(PointUtils.round(displayportOrigin)); mTileLayer.setResolution(mGeckoViewport.getZoomFactor()); - int backgroundColor = parseColorFromGecko(viewportObject.getString("backgroundColor")); - controller.setCheckerboardColor(backgroundColor); - if (onlyUpdatePageSize) { // Don't adjust page size when zooming unless zoom levels are // approximately equal. @@ -462,23 +454,5 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL mUpdateViewportOnEndDraw = true; } } - - // Parses a color from an RGB triple of the form "rgb([0-9]+, [0-9]+, [0-9]+)". If the color - // cannot be parsed, returns white. - private static int parseColorFromGecko(String string) { - if (sColorPattern == null) { - sColorPattern = Pattern.compile("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)"); - } - - Matcher matcher = sColorPattern.matcher(string); - if (!matcher.matches()) { - return Color.WHITE; - } - - int r = Integer.parseInt(matcher.group(1)); - int g = Integer.parseInt(matcher.group(2)); - int b = Integer.parseInt(matcher.group(3)); - return Color.rgb(r, g, b); - } } diff --git a/mobile/android/base/gfx/LayerController.java b/mobile/android/base/gfx/LayerController.java index cc6d9d002214..ad6f5270f83c 100644 --- a/mobile/android/base/gfx/LayerController.java +++ b/mobile/android/base/gfx/LayerController.java @@ -81,11 +81,8 @@ public class LayerController { * updates our visible rect appropriately. */ - private OnTouchListener mOnTouchListener; /* The touch listener. */ - private LayerClient mLayerClient; /* The layer client. */ - - /* The new color for the checkerboard. */ - private int mCheckerboardColor; + private OnTouchListener mOnTouchListener; /* The touch listener. */ + private LayerClient mLayerClient; /* The layer client. */ private boolean mForceRedraw; @@ -147,6 +144,7 @@ public class LayerController { } public Bitmap getBackgroundPattern() { return getDrawable("background"); } + public Bitmap getCheckerboardPattern() { return getDrawable("checkerboard"); } public Bitmap getShadowPattern() { return getDrawable("shadow"); } public GestureDetector.OnGestureListener getGestureListener() { return mPanZoomController; } @@ -353,16 +351,5 @@ public class LayerController { return mOnTouchListener.onTouch(mView, event); return false; } - - /** Retrieves the color that the checkerboard should be. */ - public int getCheckerboardColor() { - return mCheckerboardColor; - } - - /** Sets a new color for the checkerboard. */ - public void setCheckerboardColor(int newColor) { - mCheckerboardColor = newColor; - mView.requestRender(); - } } diff --git a/mobile/android/base/gfx/LayerRenderer.java b/mobile/android/base/gfx/LayerRenderer.java index cd620a31c83c..fe9cdb92b0ec 100644 --- a/mobile/android/base/gfx/LayerRenderer.java +++ b/mobile/android/base/gfx/LayerRenderer.java @@ -80,7 +80,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private final LayerView mView; private final SingleTileLayer mBackgroundLayer; - private final CheckerboardImage mCheckerboardImage; private final SingleTileLayer mCheckerboardLayer; private final NinePatchTileLayer mShadowLayer; private final TextLayer mFrameRateLayer; @@ -103,8 +102,8 @@ public class LayerRenderer implements GLSurfaceView.Renderer { CairoImage backgroundImage = new BufferedCairoImage(controller.getBackgroundPattern()); mBackgroundLayer = new SingleTileLayer(true, backgroundImage); - mCheckerboardImage = new CheckerboardImage(); - mCheckerboardLayer = new SingleTileLayer(true, mCheckerboardImage); + CairoImage checkerboardImage = new BufferedCairoImage(controller.getCheckerboardPattern()); + mCheckerboardLayer = new SingleTileLayer(true, checkerboardImage); CairoImage shadowImage = new BufferedCairoImage(controller.getShadowPattern()); mShadowLayer = new NinePatchTileLayer(shadowImage); @@ -172,7 +171,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { if (rootLayer != null) updated &= rootLayer.update(gl, pageContext); updated &= mBackgroundLayer.update(gl, screenContext); updated &= mShadowLayer.update(gl, pageContext); - updateCheckerboardLayer(gl, screenContext); + updated &= mCheckerboardLayer.update(gl, screenContext); updated &= mFrameRateLayer.update(gl, screenContext); updated &= mVertScrollLayer.update(gl, pageContext); updated &= mHorizScrollLayer.update(gl, pageContext); @@ -335,23 +334,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { }).start(); } - private void updateCheckerboardLayer(GL10 gl, RenderContext renderContext) { - int newCheckerboardColor = mView.getController().getCheckerboardColor(); - if (newCheckerboardColor == mCheckerboardImage.getColor()) { - return; - } - - mCheckerboardLayer.beginTransaction(); - try { - mCheckerboardImage.setColor(newCheckerboardColor); - mCheckerboardLayer.invalidate(); - } finally { - mCheckerboardLayer.endTransaction(); - } - - mCheckerboardLayer.update(gl, renderContext); - } - class FadeRunnable implements Runnable { private boolean mStarted; private long mRunAt; diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 7b6b76ad54b0..e915df9052c6 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -802,18 +802,7 @@ var BrowserApp = { }, getDrawMetadata: function getDrawMetadata() { - let viewport = this.selectedTab.viewport; - - // Sample the background color of the page and pass it along. (This is used to draw the - // checkerboard.) - let browser = this.selectedBrowser; - if (browser) { - let { contentDocument, contentWindow } = browser; - let computedStyle = contentWindow.getComputedStyle(contentDocument.body); - viewport.backgroundColor = computedStyle.backgroundColor; - } - - return JSON.stringify(viewport); + return JSON.stringify(this.selectedTab.viewport); }, observe: function(aSubject, aTopic, aData) { From 89f1879c06a1bea29b6a1c6a01b90b7af62e4777 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Mon, 23 Jan 2012 23:32:11 -0800 Subject: [PATCH 57/58] Back out 420d7b8ed59d for being a bad backout --- mobile/android/base/Makefile.in | 2 +- .../android/base/gfx/CheckerboardImage.java | 150 ++++++++++++++++++ .../base/gfx/GeckoSoftwareLayerClient.java | 26 +++ mobile/android/base/gfx/LayerController.java | 19 ++- mobile/android/base/gfx/LayerRenderer.java | 24 ++- mobile/android/chrome/content/browser.js | 13 +- 6 files changed, 226 insertions(+), 8 deletions(-) create mode 100644 mobile/android/base/gfx/CheckerboardImage.java diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 87dc508f3f56..44445ebe0fd4 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -101,6 +101,7 @@ FENNEC_JAVA_FILES = \ gfx/CairoGLInfo.java \ gfx/CairoImage.java \ gfx/CairoUtils.java \ + gfx/CheckerboardImage.java \ gfx/FloatSize.java \ gfx/GeckoSoftwareLayerClient.java \ gfx/InputConnectionHandler.java \ @@ -510,7 +511,6 @@ MOZ_ANDROID_DRAWABLES += \ mobile/android/base/resources/drawable/tabs_tray_close_button.xml \ mobile/android/base/resources/drawable/tabs_tray_list_divider.xml \ mobile/android/base/resources/drawable/tabs_tray_list_selector.xml \ - mobile/android/base/resources/drawable/checkerboard.png \ mobile/android/base/resources/drawable/shadow.png \ $(NULL) diff --git a/mobile/android/base/gfx/CheckerboardImage.java b/mobile/android/base/gfx/CheckerboardImage.java new file mode 100644 index 000000000000..101c9146d7d9 --- /dev/null +++ b/mobile/android/base/gfx/CheckerboardImage.java @@ -0,0 +1,150 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- + * ***** 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 Android code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2012 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Patrick Walton + * + * 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 ***** */ + +package org.mozilla.gecko.gfx; + +import org.mozilla.gecko.GeckoAppShell; +import android.graphics.Color; +import java.nio.ByteBuffer; +import java.nio.ShortBuffer; +import java.util.Arrays; + +/** A Cairo image that displays a tinted checkerboard. */ +public class CheckerboardImage extends CairoImage { + // The width and height of the checkerboard tile. + private static final int SIZE = 16; + // The pixel format of the checkerboard tile. + private static final int FORMAT = CairoImage.FORMAT_RGB16_565; + // The color to mix in to tint the background color. + private static final int TINT_COLOR = Color.GRAY; + // The amount to mix in. + private static final float TINT_OPACITY = 0.4f; + + private ByteBuffer mBuffer; + private int mMainColor; + + /** Creates a new checkerboard image. */ + public CheckerboardImage() { + int bpp = CairoUtils.bitsPerPixelForCairoFormat(FORMAT); + mBuffer = GeckoAppShell.allocateDirectBuffer(SIZE * SIZE * bpp / 8); + setColor(Color.WHITE); + } + + /** Returns the current color of the checkerboard. */ + public int getColor() { + return mMainColor; + } + + /** Sets the color of the checkerboard image and regenerates it. */ + public void setColor(int color) { + if (mMainColor == color) { + return; + } + + mMainColor = color; + int tintColor = tint(mMainColor); + short mainColor16 = convertTo16Bit(mMainColor), tintColor16 = convertTo16Bit(tintColor); + + short[] mainPattern = new short[SIZE / 2], tintPattern = new short[SIZE / 2]; + Arrays.fill(mainPattern, mainColor16); + Arrays.fill(tintPattern, tintColor16); + + // The checkerboard pattern looks like this: + // + // +---+---+ + // | N | T | N = normal + // +---+---+ T = tinted + // | T | N | + // +---+---+ + + mBuffer.rewind(); + ShortBuffer shortBuffer = mBuffer.asShortBuffer(); + for (int i = 0; i < SIZE / 2; i++) { + shortBuffer.put(mainPattern); + shortBuffer.put(tintPattern); + } + for (int i = SIZE / 2; i < SIZE; i++) { + shortBuffer.put(tintPattern); + shortBuffer.put(mainPattern); + } + } + + // Tints the given color appropriately and returns the tinted color. + private int tint(int color) { + float negTintOpacity = 1.0f - TINT_OPACITY; + float r = Color.red(color) * negTintOpacity + Color.red(TINT_COLOR) * TINT_OPACITY; + float g = Color.green(color) * negTintOpacity + Color.green(TINT_COLOR) * TINT_OPACITY; + float b = Color.blue(color) * negTintOpacity + Color.blue(TINT_COLOR) * TINT_OPACITY; + return Color.rgb(Math.round(r), Math.round(g), Math.round(b)); + } + + // Converts a 32-bit ARGB color to 16-bit R5G6B5, truncating values and discarding the alpha + // channel. + private short convertTo16Bit(int color) { + int r = Color.red(color) >> 3, g = Color.green(color) >> 2, b = Color.blue(color) >> 3; + int c = ((r << 11) | (g << 5) | b); + // Swap endianness. + return (short)((c >> 8) | ((c & 0xff) << 8)); + } + + @Override + protected void finalize() throws Throwable { + try { + if (mBuffer != null) { + GeckoAppShell.freeDirectBuffer(mBuffer); + } + } finally { + super.finalize(); + } + } + + @Override + public ByteBuffer getBuffer() { + return mBuffer; + } + + @Override + public IntSize getSize() { + return new IntSize(SIZE, SIZE); + } + + @Override + public int getFormat() { + return FORMAT; + } +} + diff --git a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java index eb82d649c5ce..abcc7321a34b 100644 --- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java +++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java @@ -54,6 +54,7 @@ import org.mozilla.gecko.GeckoEventListener; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; @@ -63,6 +64,8 @@ import android.util.Log; import org.json.JSONException; import org.json.JSONObject; import java.nio.ByteBuffer; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Transfers a software-rendered Gecko to an ImageLayer so that it can be rendered by our @@ -102,6 +105,8 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL // inside a transaction, so no synchronization is needed. private boolean mUpdateViewportOnEndDraw; + private static Pattern sColorPattern; + public GeckoSoftwareLayerClient(Context context) { mContext = context; @@ -227,6 +232,9 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL mTileLayer.setOrigin(PointUtils.round(displayportOrigin)); mTileLayer.setResolution(mGeckoViewport.getZoomFactor()); + int backgroundColor = parseColorFromGecko(viewportObject.getString("backgroundColor")); + controller.setCheckerboardColor(backgroundColor); + if (onlyUpdatePageSize) { // Don't adjust page size when zooming unless zoom levels are // approximately equal. @@ -454,5 +462,23 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL mUpdateViewportOnEndDraw = true; } } + + // Parses a color from an RGB triple of the form "rgb([0-9]+, [0-9]+, [0-9]+)". If the color + // cannot be parsed, returns white. + private static int parseColorFromGecko(String string) { + if (sColorPattern == null) { + sColorPattern = Pattern.compile("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)"); + } + + Matcher matcher = sColorPattern.matcher(string); + if (!matcher.matches()) { + return Color.WHITE; + } + + int r = Integer.parseInt(matcher.group(1)); + int g = Integer.parseInt(matcher.group(2)); + int b = Integer.parseInt(matcher.group(3)); + return Color.rgb(r, g, b); + } } diff --git a/mobile/android/base/gfx/LayerController.java b/mobile/android/base/gfx/LayerController.java index ad6f5270f83c..cc6d9d002214 100644 --- a/mobile/android/base/gfx/LayerController.java +++ b/mobile/android/base/gfx/LayerController.java @@ -81,8 +81,11 @@ public class LayerController { * updates our visible rect appropriately. */ - private OnTouchListener mOnTouchListener; /* The touch listener. */ - private LayerClient mLayerClient; /* The layer client. */ + private OnTouchListener mOnTouchListener; /* The touch listener. */ + private LayerClient mLayerClient; /* The layer client. */ + + /* The new color for the checkerboard. */ + private int mCheckerboardColor; private boolean mForceRedraw; @@ -144,7 +147,6 @@ public class LayerController { } public Bitmap getBackgroundPattern() { return getDrawable("background"); } - public Bitmap getCheckerboardPattern() { return getDrawable("checkerboard"); } public Bitmap getShadowPattern() { return getDrawable("shadow"); } public GestureDetector.OnGestureListener getGestureListener() { return mPanZoomController; } @@ -351,5 +353,16 @@ public class LayerController { return mOnTouchListener.onTouch(mView, event); return false; } + + /** Retrieves the color that the checkerboard should be. */ + public int getCheckerboardColor() { + return mCheckerboardColor; + } + + /** Sets a new color for the checkerboard. */ + public void setCheckerboardColor(int newColor) { + mCheckerboardColor = newColor; + mView.requestRender(); + } } diff --git a/mobile/android/base/gfx/LayerRenderer.java b/mobile/android/base/gfx/LayerRenderer.java index fe9cdb92b0ec..cd620a31c83c 100644 --- a/mobile/android/base/gfx/LayerRenderer.java +++ b/mobile/android/base/gfx/LayerRenderer.java @@ -80,6 +80,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private final LayerView mView; private final SingleTileLayer mBackgroundLayer; + private final CheckerboardImage mCheckerboardImage; private final SingleTileLayer mCheckerboardLayer; private final NinePatchTileLayer mShadowLayer; private final TextLayer mFrameRateLayer; @@ -102,8 +103,8 @@ public class LayerRenderer implements GLSurfaceView.Renderer { CairoImage backgroundImage = new BufferedCairoImage(controller.getBackgroundPattern()); mBackgroundLayer = new SingleTileLayer(true, backgroundImage); - CairoImage checkerboardImage = new BufferedCairoImage(controller.getCheckerboardPattern()); - mCheckerboardLayer = new SingleTileLayer(true, checkerboardImage); + mCheckerboardImage = new CheckerboardImage(); + mCheckerboardLayer = new SingleTileLayer(true, mCheckerboardImage); CairoImage shadowImage = new BufferedCairoImage(controller.getShadowPattern()); mShadowLayer = new NinePatchTileLayer(shadowImage); @@ -171,7 +172,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { if (rootLayer != null) updated &= rootLayer.update(gl, pageContext); updated &= mBackgroundLayer.update(gl, screenContext); updated &= mShadowLayer.update(gl, pageContext); - updated &= mCheckerboardLayer.update(gl, screenContext); + updateCheckerboardLayer(gl, screenContext); updated &= mFrameRateLayer.update(gl, screenContext); updated &= mVertScrollLayer.update(gl, pageContext); updated &= mHorizScrollLayer.update(gl, pageContext); @@ -334,6 +335,23 @@ public class LayerRenderer implements GLSurfaceView.Renderer { }).start(); } + private void updateCheckerboardLayer(GL10 gl, RenderContext renderContext) { + int newCheckerboardColor = mView.getController().getCheckerboardColor(); + if (newCheckerboardColor == mCheckerboardImage.getColor()) { + return; + } + + mCheckerboardLayer.beginTransaction(); + try { + mCheckerboardImage.setColor(newCheckerboardColor); + mCheckerboardLayer.invalidate(); + } finally { + mCheckerboardLayer.endTransaction(); + } + + mCheckerboardLayer.update(gl, renderContext); + } + class FadeRunnable implements Runnable { private boolean mStarted; private long mRunAt; diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index e915df9052c6..7b6b76ad54b0 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -802,7 +802,18 @@ var BrowserApp = { }, getDrawMetadata: function getDrawMetadata() { - return JSON.stringify(this.selectedTab.viewport); + let viewport = this.selectedTab.viewport; + + // Sample the background color of the page and pass it along. (This is used to draw the + // checkerboard.) + let browser = this.selectedBrowser; + if (browser) { + let { contentDocument, contentWindow } = browser; + let computedStyle = contentWindow.getComputedStyle(contentDocument.body); + viewport.backgroundColor = computedStyle.backgroundColor; + } + + return JSON.stringify(viewport); }, observe: function(aSubject, aTopic, aData) { From 34b242cb5e65e7d3ead7b25450e34c768e89ba22 Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Mon, 23 Jan 2012 23:33:05 -0800 Subject: [PATCH 58/58] Backed out changeset b77c0c621163 (bug 710533) for native ts bustage --- mobile/android/base/Makefile.in | 2 +- .../android/base/gfx/CheckerboardImage.java | 150 ------------------ .../base/gfx/GeckoSoftwareLayerClient.java | 26 --- mobile/android/base/gfx/LayerController.java | 19 +-- mobile/android/base/gfx/LayerRenderer.java | 24 +-- .../base/resources/drawable/checkerboard.png | Bin 0 -> 199 bytes mobile/android/chrome/content/browser.js | 13 +- 7 files changed, 8 insertions(+), 226 deletions(-) delete mode 100644 mobile/android/base/gfx/CheckerboardImage.java create mode 100644 mobile/android/base/resources/drawable/checkerboard.png diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 44445ebe0fd4..87dc508f3f56 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -101,7 +101,6 @@ FENNEC_JAVA_FILES = \ gfx/CairoGLInfo.java \ gfx/CairoImage.java \ gfx/CairoUtils.java \ - gfx/CheckerboardImage.java \ gfx/FloatSize.java \ gfx/GeckoSoftwareLayerClient.java \ gfx/InputConnectionHandler.java \ @@ -511,6 +510,7 @@ MOZ_ANDROID_DRAWABLES += \ mobile/android/base/resources/drawable/tabs_tray_close_button.xml \ mobile/android/base/resources/drawable/tabs_tray_list_divider.xml \ mobile/android/base/resources/drawable/tabs_tray_list_selector.xml \ + mobile/android/base/resources/drawable/checkerboard.png \ mobile/android/base/resources/drawable/shadow.png \ $(NULL) diff --git a/mobile/android/base/gfx/CheckerboardImage.java b/mobile/android/base/gfx/CheckerboardImage.java deleted file mode 100644 index 101c9146d7d9..000000000000 --- a/mobile/android/base/gfx/CheckerboardImage.java +++ /dev/null @@ -1,150 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- - * ***** 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 Android code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2012 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Patrick Walton - * - * 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 ***** */ - -package org.mozilla.gecko.gfx; - -import org.mozilla.gecko.GeckoAppShell; -import android.graphics.Color; -import java.nio.ByteBuffer; -import java.nio.ShortBuffer; -import java.util.Arrays; - -/** A Cairo image that displays a tinted checkerboard. */ -public class CheckerboardImage extends CairoImage { - // The width and height of the checkerboard tile. - private static final int SIZE = 16; - // The pixel format of the checkerboard tile. - private static final int FORMAT = CairoImage.FORMAT_RGB16_565; - // The color to mix in to tint the background color. - private static final int TINT_COLOR = Color.GRAY; - // The amount to mix in. - private static final float TINT_OPACITY = 0.4f; - - private ByteBuffer mBuffer; - private int mMainColor; - - /** Creates a new checkerboard image. */ - public CheckerboardImage() { - int bpp = CairoUtils.bitsPerPixelForCairoFormat(FORMAT); - mBuffer = GeckoAppShell.allocateDirectBuffer(SIZE * SIZE * bpp / 8); - setColor(Color.WHITE); - } - - /** Returns the current color of the checkerboard. */ - public int getColor() { - return mMainColor; - } - - /** Sets the color of the checkerboard image and regenerates it. */ - public void setColor(int color) { - if (mMainColor == color) { - return; - } - - mMainColor = color; - int tintColor = tint(mMainColor); - short mainColor16 = convertTo16Bit(mMainColor), tintColor16 = convertTo16Bit(tintColor); - - short[] mainPattern = new short[SIZE / 2], tintPattern = new short[SIZE / 2]; - Arrays.fill(mainPattern, mainColor16); - Arrays.fill(tintPattern, tintColor16); - - // The checkerboard pattern looks like this: - // - // +---+---+ - // | N | T | N = normal - // +---+---+ T = tinted - // | T | N | - // +---+---+ - - mBuffer.rewind(); - ShortBuffer shortBuffer = mBuffer.asShortBuffer(); - for (int i = 0; i < SIZE / 2; i++) { - shortBuffer.put(mainPattern); - shortBuffer.put(tintPattern); - } - for (int i = SIZE / 2; i < SIZE; i++) { - shortBuffer.put(tintPattern); - shortBuffer.put(mainPattern); - } - } - - // Tints the given color appropriately and returns the tinted color. - private int tint(int color) { - float negTintOpacity = 1.0f - TINT_OPACITY; - float r = Color.red(color) * negTintOpacity + Color.red(TINT_COLOR) * TINT_OPACITY; - float g = Color.green(color) * negTintOpacity + Color.green(TINT_COLOR) * TINT_OPACITY; - float b = Color.blue(color) * negTintOpacity + Color.blue(TINT_COLOR) * TINT_OPACITY; - return Color.rgb(Math.round(r), Math.round(g), Math.round(b)); - } - - // Converts a 32-bit ARGB color to 16-bit R5G6B5, truncating values and discarding the alpha - // channel. - private short convertTo16Bit(int color) { - int r = Color.red(color) >> 3, g = Color.green(color) >> 2, b = Color.blue(color) >> 3; - int c = ((r << 11) | (g << 5) | b); - // Swap endianness. - return (short)((c >> 8) | ((c & 0xff) << 8)); - } - - @Override - protected void finalize() throws Throwable { - try { - if (mBuffer != null) { - GeckoAppShell.freeDirectBuffer(mBuffer); - } - } finally { - super.finalize(); - } - } - - @Override - public ByteBuffer getBuffer() { - return mBuffer; - } - - @Override - public IntSize getSize() { - return new IntSize(SIZE, SIZE); - } - - @Override - public int getFormat() { - return FORMAT; - } -} - diff --git a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java index abcc7321a34b..eb82d649c5ce 100644 --- a/mobile/android/base/gfx/GeckoSoftwareLayerClient.java +++ b/mobile/android/base/gfx/GeckoSoftwareLayerClient.java @@ -54,7 +54,6 @@ import org.mozilla.gecko.GeckoEventListener; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; -import android.graphics.Color; import android.graphics.Point; import android.graphics.PointF; import android.graphics.Rect; @@ -64,8 +63,6 @@ import android.util.Log; import org.json.JSONException; import org.json.JSONObject; import java.nio.ByteBuffer; -import java.util.regex.Matcher; -import java.util.regex.Pattern; /** * Transfers a software-rendered Gecko to an ImageLayer so that it can be rendered by our @@ -105,8 +102,6 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL // inside a transaction, so no synchronization is needed. private boolean mUpdateViewportOnEndDraw; - private static Pattern sColorPattern; - public GeckoSoftwareLayerClient(Context context) { mContext = context; @@ -232,9 +227,6 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL mTileLayer.setOrigin(PointUtils.round(displayportOrigin)); mTileLayer.setResolution(mGeckoViewport.getZoomFactor()); - int backgroundColor = parseColorFromGecko(viewportObject.getString("backgroundColor")); - controller.setCheckerboardColor(backgroundColor); - if (onlyUpdatePageSize) { // Don't adjust page size when zooming unless zoom levels are // approximately equal. @@ -462,23 +454,5 @@ public class GeckoSoftwareLayerClient extends LayerClient implements GeckoEventL mUpdateViewportOnEndDraw = true; } } - - // Parses a color from an RGB triple of the form "rgb([0-9]+, [0-9]+, [0-9]+)". If the color - // cannot be parsed, returns white. - private static int parseColorFromGecko(String string) { - if (sColorPattern == null) { - sColorPattern = Pattern.compile("rgb\\((\\d+),\\s*(\\d+),\\s*(\\d+)\\)"); - } - - Matcher matcher = sColorPattern.matcher(string); - if (!matcher.matches()) { - return Color.WHITE; - } - - int r = Integer.parseInt(matcher.group(1)); - int g = Integer.parseInt(matcher.group(2)); - int b = Integer.parseInt(matcher.group(3)); - return Color.rgb(r, g, b); - } } diff --git a/mobile/android/base/gfx/LayerController.java b/mobile/android/base/gfx/LayerController.java index cc6d9d002214..ad6f5270f83c 100644 --- a/mobile/android/base/gfx/LayerController.java +++ b/mobile/android/base/gfx/LayerController.java @@ -81,11 +81,8 @@ public class LayerController { * updates our visible rect appropriately. */ - private OnTouchListener mOnTouchListener; /* The touch listener. */ - private LayerClient mLayerClient; /* The layer client. */ - - /* The new color for the checkerboard. */ - private int mCheckerboardColor; + private OnTouchListener mOnTouchListener; /* The touch listener. */ + private LayerClient mLayerClient; /* The layer client. */ private boolean mForceRedraw; @@ -147,6 +144,7 @@ public class LayerController { } public Bitmap getBackgroundPattern() { return getDrawable("background"); } + public Bitmap getCheckerboardPattern() { return getDrawable("checkerboard"); } public Bitmap getShadowPattern() { return getDrawable("shadow"); } public GestureDetector.OnGestureListener getGestureListener() { return mPanZoomController; } @@ -353,16 +351,5 @@ public class LayerController { return mOnTouchListener.onTouch(mView, event); return false; } - - /** Retrieves the color that the checkerboard should be. */ - public int getCheckerboardColor() { - return mCheckerboardColor; - } - - /** Sets a new color for the checkerboard. */ - public void setCheckerboardColor(int newColor) { - mCheckerboardColor = newColor; - mView.requestRender(); - } } diff --git a/mobile/android/base/gfx/LayerRenderer.java b/mobile/android/base/gfx/LayerRenderer.java index cd620a31c83c..fe9cdb92b0ec 100644 --- a/mobile/android/base/gfx/LayerRenderer.java +++ b/mobile/android/base/gfx/LayerRenderer.java @@ -80,7 +80,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private final LayerView mView; private final SingleTileLayer mBackgroundLayer; - private final CheckerboardImage mCheckerboardImage; private final SingleTileLayer mCheckerboardLayer; private final NinePatchTileLayer mShadowLayer; private final TextLayer mFrameRateLayer; @@ -103,8 +102,8 @@ public class LayerRenderer implements GLSurfaceView.Renderer { CairoImage backgroundImage = new BufferedCairoImage(controller.getBackgroundPattern()); mBackgroundLayer = new SingleTileLayer(true, backgroundImage); - mCheckerboardImage = new CheckerboardImage(); - mCheckerboardLayer = new SingleTileLayer(true, mCheckerboardImage); + CairoImage checkerboardImage = new BufferedCairoImage(controller.getCheckerboardPattern()); + mCheckerboardLayer = new SingleTileLayer(true, checkerboardImage); CairoImage shadowImage = new BufferedCairoImage(controller.getShadowPattern()); mShadowLayer = new NinePatchTileLayer(shadowImage); @@ -172,7 +171,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { if (rootLayer != null) updated &= rootLayer.update(gl, pageContext); updated &= mBackgroundLayer.update(gl, screenContext); updated &= mShadowLayer.update(gl, pageContext); - updateCheckerboardLayer(gl, screenContext); + updated &= mCheckerboardLayer.update(gl, screenContext); updated &= mFrameRateLayer.update(gl, screenContext); updated &= mVertScrollLayer.update(gl, pageContext); updated &= mHorizScrollLayer.update(gl, pageContext); @@ -335,23 +334,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { }).start(); } - private void updateCheckerboardLayer(GL10 gl, RenderContext renderContext) { - int newCheckerboardColor = mView.getController().getCheckerboardColor(); - if (newCheckerboardColor == mCheckerboardImage.getColor()) { - return; - } - - mCheckerboardLayer.beginTransaction(); - try { - mCheckerboardImage.setColor(newCheckerboardColor); - mCheckerboardLayer.invalidate(); - } finally { - mCheckerboardLayer.endTransaction(); - } - - mCheckerboardLayer.update(gl, renderContext); - } - class FadeRunnable implements Runnable { private boolean mStarted; private long mRunAt; diff --git a/mobile/android/base/resources/drawable/checkerboard.png b/mobile/android/base/resources/drawable/checkerboard.png new file mode 100644 index 0000000000000000000000000000000000000000..57cfbe80fdcfaa6866ade23dd363dbb3f0f8c0c2 GIT binary patch literal 199 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61SBU+%rFB|jKx9jP7LeL$-D$|I14-?iy0WW zg+Z8+Vb&Z8pdfpRr>`sfZ7yL^X}!xfj385#N?apKobz*YQ}ap~oQqNuOHxx5$}>wc z6x=<10~GS}6cQDD6O-Fllsf}eYkRslhG?8mwkdwb!)%t2lhYGAr?b(;U{#~Ufgk_> l*RK&uKR-{FSM^sW1B266js})SVOgLy22WQ%mvv4FO#sOXHcbEk literal 0 HcmV?d00001 diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 7b6b76ad54b0..e915df9052c6 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -802,18 +802,7 @@ var BrowserApp = { }, getDrawMetadata: function getDrawMetadata() { - let viewport = this.selectedTab.viewport; - - // Sample the background color of the page and pass it along. (This is used to draw the - // checkerboard.) - let browser = this.selectedBrowser; - if (browser) { - let { contentDocument, contentWindow } = browser; - let computedStyle = contentWindow.getComputedStyle(contentDocument.body); - viewport.backgroundColor = computedStyle.backgroundColor; - } - - return JSON.stringify(viewport); + return JSON.stringify(this.selectedTab.viewport); }, observe: function(aSubject, aTopic, aData) {