From b5b0bf9fbeeddfe063292bc4299ebf752b0a56d9 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 2 Jan 2012 18:18:26 -0800 Subject: [PATCH 01/90] Bug 671299 (part 1) - Add ns{Void,COM}Array::SizeOfExcludingThis(). r=bz. --HG-- extra : rebase_source : 170c6943e985a8bafc4c441918a0e23fd52e15f2 --- xpcom/glue/nsCOMArray.h | 24 ++++++++++++++++ xpcom/glue/nsVoidArray.cpp | 56 +++++++++++++++++++++++++++++++++++--- xpcom/glue/nsVoidArray.h | 14 ++++++++++ 3 files changed, 90 insertions(+), 4 deletions(-) diff --git a/xpcom/glue/nsCOMArray.h b/xpcom/glue/nsCOMArray.h index 35941c3a3097..50300a95b67b 100644 --- a/xpcom/glue/nsCOMArray.h +++ b/xpcom/glue/nsCOMArray.h @@ -121,6 +121,16 @@ public: : true; } + // Measures the size of the array's element storage, and if + // |aSizeOfElement| is non-NULL, measures the size of things pointed to by + // elements. + size_t SizeOfExcludingThis( + nsVoidArraySizeOfElementIncludingThisFunc aSizeOfElementIncludingThis, + nsMallocSizeOfFun aMallocSizeOf, void* aData = NULL) const { + return mArray.SizeOfExcludingThis(aSizeOfElementIncludingThis, + aMallocSizeOf, aData); + } + private: // the actual storage @@ -276,6 +286,20 @@ class nsCOMArray : public nsCOMArray_base return nsCOMArray_base::RemoveObjectsAt(aIndex, aCount); } + // Each element in an nsCOMArray is actually a T*, so this function is + // "IncludingThis" rather than "ExcludingThis" because it needs to measure + // the memory taken by the T itself as well as anything it points to. + typedef size_t (* nsCOMArraySizeOfElementIncludingThisFunc) + (T* aElement, nsMallocSizeOfFun aMallocSizeOf, void *aData); + + size_t SizeOfExcludingThis( + nsCOMArraySizeOfElementIncludingThisFunc aSizeOfElementIncludingThis, + nsMallocSizeOfFun aMallocSizeOf, void *aData = NULL) const { + return nsCOMArray_base::SizeOfExcludingThis( + nsVoidArraySizeOfElementIncludingThisFunc(aSizeOfElementIncludingThis), + aMallocSizeOf, aData); + } + private: // don't implement these! diff --git a/xpcom/glue/nsVoidArray.cpp b/xpcom/glue/nsVoidArray.cpp index f8904edfb286..0ae56d1d2ec5 100644 --- a/xpcom/glue/nsVoidArray.cpp +++ b/xpcom/glue/nsVoidArray.cpp @@ -717,10 +717,22 @@ bool nsVoidArray::EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData) PRInt32 index = -1; bool running = true; - if (mImpl) - { - while (running && (++index < mImpl->mCount)) - { + if (mImpl) { + while (running && (++index < mImpl->mCount)) { + running = (*aFunc)(mImpl->mArray[index], aData); + } + } + return running; +} + +bool nsVoidArray::EnumerateForwards(nsVoidArrayEnumFuncConst aFunc, + void* aData) const +{ + PRInt32 index = -1; + bool running = true; + + if (mImpl) { + while (running && (++index < mImpl->mCount)) { running = (*aFunc)(mImpl->mArray[index], aData); } } @@ -742,6 +754,42 @@ bool nsVoidArray::EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData) return running; } +struct SizeOfElementIncludingThisData +{ + size_t mSize; + nsVoidArraySizeOfElementIncludingThisFunc mSizeOfElementIncludingThis; + nsMallocSizeOfFun mMallocSizeOf; + void *mData; // the arg passed by the user +}; + +static bool +SizeOfElementIncludingThisEnumerator(const void *aElement, void *aData) +{ + SizeOfElementIncludingThisData *d = (SizeOfElementIncludingThisData *)aData; + d->mSize += d->mSizeOfElementIncludingThis(aElement, d->mMallocSizeOf, d->mData); + return true; +} + +size_t +nsVoidArray::SizeOfExcludingThis( + nsVoidArraySizeOfElementIncludingThisFunc aSizeOfElementIncludingThis, + nsMallocSizeOfFun aMallocSizeOf, void* aData) const +{ + size_t n = 0; + // Measure the element storage. + if (mImpl) { + n += aMallocSizeOf(mImpl); + } + // Measure things pointed to by the elements. + if (aSizeOfElementIncludingThis) { + SizeOfElementIncludingThisData data2 = + { 0, aSizeOfElementIncludingThis, aMallocSizeOf, aData }; + EnumerateForwards(SizeOfElementIncludingThisEnumerator, &data2); + n += data2.mSize; + } + return n; +} + //---------------------------------------------------------------- // nsAutoVoidArray diff --git a/xpcom/glue/nsVoidArray.h b/xpcom/glue/nsVoidArray.h index 36cc9ba0f518..9d3415b00843 100644 --- a/xpcom/glue/nsVoidArray.h +++ b/xpcom/glue/nsVoidArray.h @@ -47,6 +47,12 @@ typedef int (* nsVoidArrayComparatorFunc) // Enumerator callback function. Return false to stop typedef bool (* nsVoidArrayEnumFunc)(void* aElement, void *aData); +typedef bool (* nsVoidArrayEnumFuncConst)(const void* aElement, void *aData); + +// SizeOfExcludingThis callback function. +typedef size_t (* nsVoidArraySizeOfElementIncludingThisFunc)(const void* aElement, + nsMallocSizeOfFun aMallocSizeOf, + void *aData); /// A basic zero-based array of void*'s that manages its own memory class NS_COM_GLUE nsVoidArray { @@ -127,8 +133,16 @@ public: void Sort(nsVoidArrayComparatorFunc aFunc, void* aData); bool EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData); + bool EnumerateForwards(nsVoidArrayEnumFuncConst aFunc, void* aData) const; bool EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData); + // Measures the size of the array's element storage, and if + // |aSizeOfElementIncludingThis| is non-NULL, measures the size of things + // pointed to by elements. + size_t SizeOfExcludingThis( + nsVoidArraySizeOfElementIncludingThisFunc aSizeOfElementIncludingThis, + nsMallocSizeOfFun aMallocSizeOf, void* aData = NULL) const; + protected: bool GrowArrayBy(PRInt32 aGrowBy); From 8a9f231eaa3c98928c5c698dd41a6fded74c2202 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 2 Jan 2012 18:19:12 -0800 Subject: [PATCH 02/90] Bug 671299 (part 2) - Add nsStringBuffer::SizeOfIncludingThisMustBeUnshared. r=bz. --HG-- extra : rebase_source : 1702e32e19828ae061c89f017f27efc75dab6cff --- xpcom/string/public/nsStringBuffer.h | 6 ++++++ xpcom/string/src/nsSubstring.cpp | 10 +++++++++- 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/xpcom/string/public/nsStringBuffer.h b/xpcom/string/public/nsStringBuffer.h index 229ee050325c..0a4761d0a1e6 100644 --- a/xpcom/string/public/nsStringBuffer.h +++ b/xpcom/string/public/nsStringBuffer.h @@ -170,6 +170,12 @@ class nsStringBuffer void ToString(PRUint32 len, nsACString &str, bool aMoveOwnership = false); + /** + * This measures the size. It should only be used if the StringBuffer is + * unshared. This is checked. + */ + size_t SizeOfIncludingThisMustBeUnshared(nsMallocSizeOfFun aMallocSizeOf) const; + /** * This measures the size only if the StringBuffer is unshared. */ diff --git a/xpcom/string/src/nsSubstring.cpp b/xpcom/string/src/nsSubstring.cpp index 5df85437f57a..a9e9d5ea8ffc 100644 --- a/xpcom/string/src/nsSubstring.cpp +++ b/xpcom/string/src/nsSubstring.cpp @@ -307,12 +307,20 @@ nsStringBuffer::ToString(PRUint32 len, nsACString &str, accessor->set(data, len, flags); } +size_t +nsStringBuffer::SizeOfIncludingThisMustBeUnshared(nsMallocSizeOfFun aMallocSizeOf) const + { + NS_ASSERTION(!IsReadonly(), + "shared StringBuffer in SizeOfIncludingThisMustBeUnshared"); + return aMallocSizeOf(this); + } + size_t nsStringBuffer::SizeOfIncludingThisIfUnshared(nsMallocSizeOfFun aMallocSizeOf) const { if (!IsReadonly()) { - return aMallocSizeOf(this); + return SizeOfIncludingThisMustBeUnshared(aMallocSizeOf); } return 0; } From 0709e1673b28ba0fc4909000a0925dd6f1d2bcef Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 2 Jan 2012 18:19:14 -0800 Subject: [PATCH 03/90] Bug 671299 (part 3) - Add style sheet memory reporters. r=dbaron. --- content/base/public/nsIDocument.h | 2 + content/base/src/nsDocument.cpp | 15 ++ content/base/src/nsDocument.h | 2 + dom/base/nsDOMMemoryReporter.cpp | 105 ++++++--- dom/base/nsGlobalWindow.cpp | 10 + dom/base/nsGlobalWindow.h | 1 + dom/workers/WorkerPrivate.cpp | 6 +- js/xpconnect/src/XPCJSRuntime.cpp | 12 +- js/xpconnect/src/xpcpublic.h | 1 - layout/base/nsPresContext.h | 17 +- layout/style/AnimationCommon.cpp | 8 +- layout/style/Declaration.cpp | 10 + layout/style/Declaration.h | 2 + layout/style/GroupRule.h | 5 + layout/style/ImportRule.h | 2 + layout/style/NameSpaceRule.h | 3 + layout/style/Rule.h | 10 + layout/style/StyleRule.cpp | 99 ++++++++ layout/style/StyleRule.h | 10 + layout/style/nsAnimationManager.cpp | 7 +- layout/style/nsCSSDataBlock.cpp | 14 ++ layout/style/nsCSSDataBlock.h | 2 + layout/style/nsCSSRules.cpp | 116 ++++++++++ layout/style/nsCSSRules.h | 14 ++ layout/style/nsCSSStyleSheet.cpp | 46 ++++ layout/style/nsCSSStyleSheet.h | 4 + layout/style/nsCSSValue.cpp | 274 ++++++++++++++++++++++- layout/style/nsCSSValue.h | 38 +++- layout/style/nsIStyleSheet.h | 2 + layout/style/nsLayoutStylesheetCache.cpp | 58 +++++ layout/style/nsLayoutStylesheetCache.h | 10 +- xpcom/base/nsIMemoryReporter.idl | 4 +- 32 files changed, 853 insertions(+), 56 deletions(-) diff --git a/content/base/public/nsIDocument.h b/content/base/public/nsIDocument.h index b1f5a7565a56..d7a30aaa2fc6 100644 --- a/content/base/public/nsIDocument.h +++ b/content/base/public/nsIDocument.h @@ -1617,6 +1617,8 @@ public: } } + virtual size_t SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const = 0; + private: PRUint64 mWarnedAbout; diff --git a/content/base/src/nsDocument.cpp b/content/base/src/nsDocument.cpp index a507255baf28..d79ac50ae8a8 100644 --- a/content/base/src/nsDocument.cpp +++ b/content/base/src/nsDocument.cpp @@ -9134,3 +9134,18 @@ nsDocument::GetMozVisibilityState(nsAString& aState) aState.AssignASCII(states[mVisibilityState]); return NS_OK; } + +static size_t +SizeOfStyleSheetsElementIncludingThis(nsIStyleSheet* aStyleSheet, + nsMallocSizeOfFun aMallocSizeOf, + void* aData) +{ + return aStyleSheet->SizeOfIncludingThis(aMallocSizeOf); +} + +/* virtual */ size_t +nsDocument::SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const +{ + return mStyleSheets.SizeOfExcludingThis(SizeOfStyleSheetsElementIncludingThis, + aMallocSizeOf); +} diff --git a/content/base/src/nsDocument.h b/content/base/src/nsDocument.h index f88563c3aa6b..93751cb81efe 100644 --- a/content/base/src/nsDocument.h +++ b/content/base/src/nsDocument.h @@ -991,6 +991,8 @@ public: // Posts an event to call UpdateVisibilityState virtual void PostVisibilityUpdateEvent(); + virtual size_t SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const; + protected: friend class nsNodeUtils; diff --git a/dom/base/nsDOMMemoryReporter.cpp b/dom/base/nsDOMMemoryReporter.cpp index b9909d3529a1..ce30eb4e7208 100644 --- a/dom/base/nsDOMMemoryReporter.cpp +++ b/dom/base/nsDOMMemoryReporter.cpp @@ -88,14 +88,22 @@ AppendWindowURI(nsGlobalWindow *aWindow, nsACString& aStr) return true; } -static void -CollectWindowMemoryUsage(nsGlobalWindow *aWindow, - nsIMemoryMultiReporterCallback *aCb, - nsISupports *aClosure) +struct WindowTotals { - NS_NAMED_LITERAL_CSTRING(kWindowDesc, - "Memory used by a window and the DOM within it."); + WindowTotals() : mDom(0), mStyleSheets(0) {} + size_t mDom; + size_t mStyleSheets; +}; +NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(WindowStyleSheetsMallocSizeOf, + "window/style-sheets") + +static void +CollectWindowReports(nsGlobalWindow *aWindow, + WindowTotals *aWindowTotals, + nsIMemoryMultiReporterCallback *aCb, + nsISupports *aClosure) +{ // DOM window objects fall into one of three categories: // - "active" windows are currently either displayed in an active // tab, or a child of such a window. @@ -119,7 +127,7 @@ CollectWindowMemoryUsage(nsGlobalWindow *aWindow, // The path we give to the reporter callback for inner windows are // as follows: // - // explicit/dom/window-objects//top= (inner=)/inner-window(id=, uri=) + // explicit/dom+style/window-objects//top= (inner=)/inner-window(id=, uri=) // // Where: // - is active, cached, or other, as described above. @@ -138,62 +146,82 @@ CollectWindowMemoryUsage(nsGlobalWindow *aWindow, // // For outer windows we simply use: // - // explicit/dom/window-objects//outer-windows + // explicit/dom+style/window-objects//outer-windows // // Which gives us simple counts of how many outer windows (and their // combined sizes) per category. - nsCAutoString str("explicit/dom/window-objects/"); + nsCAutoString windowPath("explicit/dom+style/window-objects/"); nsIDocShell *docShell = aWindow->GetDocShell(); nsGlobalWindow *top = aWindow->GetTop(); - PRInt64 windowSize = aWindow->SizeOf(); + PRInt64 windowDOMSize = aWindow->SizeOf(); + PRInt64 styleSheetsSize = aWindow->SizeOfStyleSheets(WindowStyleSheetsMallocSizeOf); if (docShell && aWindow->IsFrozen()) { - str += NS_LITERAL_CSTRING("cached/"); + windowPath += NS_LITERAL_CSTRING("cached/"); } else if (docShell) { - str += NS_LITERAL_CSTRING("active/"); + windowPath += NS_LITERAL_CSTRING("active/"); } else { - str += NS_LITERAL_CSTRING("other/"); + windowPath += NS_LITERAL_CSTRING("other/"); } if (aWindow->IsInnerWindow()) { - str += NS_LITERAL_CSTRING("top="); + windowPath += NS_LITERAL_CSTRING("top="); if (top) { - str.AppendInt(top->WindowID()); + windowPath.AppendInt(top->WindowID()); nsGlobalWindow *topInner = top->GetCurrentInnerWindowInternal(); if (topInner) { - str += NS_LITERAL_CSTRING(" (inner="); - str.AppendInt(topInner->WindowID()); - str += NS_LITERAL_CSTRING(")"); + windowPath += NS_LITERAL_CSTRING(" (inner="); + windowPath.AppendInt(topInner->WindowID()); + windowPath += NS_LITERAL_CSTRING(")"); } } else { - str += NS_LITERAL_CSTRING("none"); + windowPath += NS_LITERAL_CSTRING("none"); } - str += NS_LITERAL_CSTRING("/inner-window(id="); - str.AppendInt(aWindow->WindowID()); - str += NS_LITERAL_CSTRING(", uri="); + windowPath += NS_LITERAL_CSTRING("/inner-window(id="); + windowPath.AppendInt(aWindow->WindowID()); + windowPath += NS_LITERAL_CSTRING(", uri="); - if (!AppendWindowURI(aWindow, str)) { - str += NS_LITERAL_CSTRING("[system]"); + if (!AppendWindowURI(aWindow, windowPath)) { + windowPath += NS_LITERAL_CSTRING("[system]"); } - str += NS_LITERAL_CSTRING(")"); + windowPath += NS_LITERAL_CSTRING(")"); } else { // Combine all outer windows per section (active/cached/other) as // they basically never contain anything of interest, and are // always pretty much the same size. - str += NS_LITERAL_CSTRING("outer-windows"); + windowPath += NS_LITERAL_CSTRING("outer-windows"); } - aCb->Callback(EmptyCString(), str, nsIMemoryReporter::KIND_HEAP, - nsIMemoryReporter::UNITS_BYTES, windowSize, kWindowDesc, - aClosure); + if (windowDOMSize > 0) { + nsCAutoString domPath(windowPath); + domPath += "/dom"; + NS_NAMED_LITERAL_CSTRING(kWindowDesc, + "Memory used by a window and the DOM within it."); + aCb->Callback(EmptyCString(), domPath, nsIMemoryReporter::KIND_HEAP, + nsIMemoryReporter::UNITS_BYTES, windowDOMSize, kWindowDesc, + aClosure); + aWindowTotals->mDom += windowDOMSize; + } + + if (styleSheetsSize > 0) { + nsCAutoString styleSheetsPath(windowPath); + styleSheetsPath += "/style-sheets"; + NS_NAMED_LITERAL_CSTRING(kStyleSheetsDesc, + "Memory used by style sheets within a window."); + aCb->Callback(EmptyCString(), styleSheetsPath, + nsIMemoryReporter::KIND_HEAP, + nsIMemoryReporter::UNITS_BYTES, styleSheetsSize, + kStyleSheetsDesc, aClosure); + aWindowTotals->mStyleSheets += styleSheetsSize; + } } typedef nsTArray< nsRefPtr > WindowArray; @@ -223,10 +251,27 @@ nsDOMMemoryMultiReporter::CollectReports(nsIMemoryMultiReporterCallback* aCb, // Collect window memory usage. nsRefPtr *w = windows.Elements(); nsRefPtr *end = w + windows.Length(); + WindowTotals windowTotals; for (; w != end; ++w) { - CollectWindowMemoryUsage(*w, aCb, aClosure); + CollectWindowReports(*w, &windowTotals, aCb, aClosure); } + NS_NAMED_LITERAL_CSTRING(kDomTotalWindowsDesc, + "Memory used for the DOM within windows. This is the sum of all windows' " + "'dom' numbers."); + aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING("dom-total-window"), + nsIMemoryReporter::KIND_OTHER, + nsIMemoryReporter::UNITS_BYTES, windowTotals.mDom, + kDomTotalWindowsDesc, aClosure); + + NS_NAMED_LITERAL_CSTRING(kLayoutTotalWindowStyleSheetsDesc, + "Memory used for style sheets within windows. This is the sum of all windows' " + "'style-sheets' numbers."); + aCb->Callback(EmptyCString(), NS_LITERAL_CSTRING("style-sheets-total-window"), + nsIMemoryReporter::KIND_OTHER, + nsIMemoryReporter::UNITS_BYTES, windowTotals.mStyleSheets, + kLayoutTotalWindowStyleSheetsDesc, aClosure); + return NS_OK; } diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 3bbb99d57d1e..5fb987163f35 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -10296,6 +10296,16 @@ nsGlobalWindow::SizeOf() const return size; } +size_t +nsGlobalWindow::SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = 0; + if (IsInnerWindow() && mDoc) { + n += mDoc->SizeOfStyleSheets(aMallocSizeOf); + } + return n; +} + // nsGlobalChromeWindow implementation NS_IMPL_CYCLE_COLLECTION_CLASS(nsGlobalChromeWindow) diff --git a/dom/base/nsGlobalWindow.h b/dom/base/nsGlobalWindow.h index 8f676e7f09c0..a145c86e87c7 100644 --- a/dom/base/nsGlobalWindow.h +++ b/dom/base/nsGlobalWindow.h @@ -576,6 +576,7 @@ public: } PRInt64 SizeOf() const; + size_t SizeOfStyleSheets(nsMallocSizeOfFun aMallocSizeOf) const; void UnmarkGrayTimers(); private: diff --git a/dom/workers/WorkerPrivate.cpp b/dom/workers/WorkerPrivate.cpp index 003734956f34..f0c9c69520af 100644 --- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -155,6 +155,8 @@ SwapToISupportsArray(SmartPtr& aSrc, dest->swap(rawSupports); } +NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsWorkerMallocSizeOf, "js-worker") + class WorkerMemoryReporter : public nsIMemoryMultiReporter { WorkerPrivate* mWorkerPrivate; @@ -232,7 +234,7 @@ public: { AssertIsOnMainThread(); - JS::RuntimeStats rtStats(xpc::JsMallocSizeOf, xpc::GetCompartmentName, + JS::RuntimeStats rtStats(JsWorkerMallocSizeOf, xpc::GetCompartmentName, xpc::DestroyCompartmentName); nsresult rv = CollectForRuntime(/* isQuick = */false, &rtStats); if (NS_FAILED(rv)) { @@ -1523,7 +1525,7 @@ public: JSAutoSuspendRequest asr(aCx); *mSucceeded = mIsQuick - ? JS::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), static_cast(mData), xpc::JsMallocSizeOf) + ? JS::GetExplicitNonHeapForRuntime(JS_GetRuntime(aCx), static_cast(mData), JsWorkerMallocSizeOf) : JS::CollectRuntimeStats(JS_GetRuntime(aCx), static_cast(mData)); { diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index 8a61d971b96f..ba8bcb5092ca 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1275,8 +1275,6 @@ DestroyCompartmentName(void *string) delete static_cast(string); } -NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js") - } // namespace xpc namespace { @@ -1725,6 +1723,8 @@ ReportJSRuntimeStats(const JS::RuntimeStats &rtStats, const nsACString &pathPref } // namespace xpconnect } // namespace mozilla +NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(JsMallocSizeOf, "js") + class XPConnectJSCompartmentsMultiReporter : public nsIMemoryMultiReporter { public: @@ -1740,7 +1740,7 @@ public: // the callback. Separating these steps is important because the // callback may be a JS function, and executing JS while getting these // stats seems like a bad idea. - JS::RuntimeStats rtStats(xpc::JsMallocSizeOf, xpc::GetCompartmentName, + JS::RuntimeStats rtStats(JsMallocSizeOf, xpc::GetCompartmentName, xpc::DestroyCompartmentName); if (!JS::CollectRuntimeStats(xpcrt->GetJSRuntime(), &rtStats)) return NS_ERROR_FAILURE; @@ -1748,8 +1748,8 @@ public: size_t xpconnect; { xpconnect = - xpcrt->SizeOfIncludingThis(xpc::JsMallocSizeOf) + - XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(xpc::JsMallocSizeOf); + xpcrt->SizeOfIncludingThis(JsMallocSizeOf) + + XPCWrappedNativeScope::SizeOfAllScopesIncludingThis(JsMallocSizeOf); } NS_NAMED_LITERAL_CSTRING(pathPrefix, "explicit/js/"); @@ -1855,7 +1855,7 @@ public: { JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->GetJSRuntime(); - if (!JS::GetExplicitNonHeapForRuntime(rt, reinterpret_cast(n), xpc::JsMallocSizeOf)) + if (!JS::GetExplicitNonHeapForRuntime(rt, reinterpret_cast(n), JsMallocSizeOf)) return NS_ERROR_FAILURE; return NS_OK; diff --git a/js/xpconnect/src/xpcpublic.h b/js/xpconnect/src/xpcpublic.h index e0066ee43ca2..487c0a1a580a 100644 --- a/js/xpconnect/src/xpcpublic.h +++ b/js/xpconnect/src/xpcpublic.h @@ -219,7 +219,6 @@ bool StringToJsval(JSContext *cx, nsString &str, JS::Value *rval); void *GetCompartmentName(JSContext *cx, JSCompartment *c); void DestroyCompartmentName(void *string); -size_t JsMallocSizeOf(const void *ptr); } // namespace xpc diff --git a/layout/base/nsPresContext.h b/layout/base/nsPresContext.h index 487869044bf2..a27309c2bdad 100644 --- a/layout/base/nsPresContext.h +++ b/layout/base/nsPresContext.h @@ -965,9 +965,10 @@ public: virtual NS_MUST_OVERRIDE size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const { - // XXX: lots of things hang off nsPresContext and should be included in - // this measurement. Bug 671299 may add them. return 0; + + // Measurement of other members may be added later if DMD finds it is + // worthwhile. } virtual NS_MUST_OVERRIDE size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const { @@ -1318,9 +1319,17 @@ public: virtual NS_MUST_OVERRIDE size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const MOZ_OVERRIDE { - // XXX: several things hang off an nsRootPresContext and should be included - // in this measurement. Bug 671299 may do this. return nsPresContext::SizeOfExcludingThis(aMallocSizeOf); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mNotifyDidPaintTimer + // - mRegisteredPlugins + // - mWillPaintObservers + // - mWillPaintFallbackEvent + // + // The following member are not measured: + // - mUpdatePluginGeometryForFrame, because it is non-owning } virtual NS_MUST_OVERRIDE size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const MOZ_OVERRIDE { diff --git a/layout/style/AnimationCommon.cpp b/layout/style/AnimationCommon.cpp index 38591d31c605..c2cc1bfdacc8 100644 --- a/layout/style/AnimationCommon.cpp +++ b/layout/style/AnimationCommon.cpp @@ -129,7 +129,13 @@ CommonAnimationManager::MediumFeaturesChanged(nsPresContext* aPresContext) /* virtual */ size_t CommonAnimationManager::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const { - // XXX: could measure mProperytValuePairs here. Bug 671299 may do this. + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mElementData + // + // The following members are not measured + // - mPresContext, because it's non-owning + return 0; } diff --git a/layout/style/Declaration.cpp b/layout/style/Declaration.cpp index d6ba485a02bc..3c89e3cf470d 100644 --- a/layout/style/Declaration.cpp +++ b/layout/style/Declaration.cpp @@ -994,5 +994,15 @@ Declaration::EnsureMutable() } } +size_t +Declaration::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += mOrder.SizeOfExcludingThis(aMallocSizeOf); + n += mData ? mData ->SizeOfIncludingThis(aMallocSizeOf) : 0; + n += mImportantData ? mImportantData->SizeOfIncludingThis(aMallocSizeOf) : 0; + return n; +} + } // namespace mozilla::css } // namespace mozilla diff --git a/layout/style/Declaration.h b/layout/style/Declaration.h index deee16d816f9..2bd9e47b29b9 100644 --- a/layout/style/Declaration.h +++ b/layout/style/Declaration.h @@ -257,6 +257,8 @@ public: return nsCSSProperty(mOrder.ElementAt(aValue)); } + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + private: nsAutoTArray mOrder; diff --git a/layout/style/GroupRule.h b/layout/style/GroupRule.h index dd05c0b77a5a..ea2afdf28c1e 100644 --- a/layout/style/GroupRule.h +++ b/layout/style/GroupRule.h @@ -96,6 +96,11 @@ public: virtual bool UseForPresentation(nsPresContext* aPresContext, nsMediaQueryResultCacheKey& aKey) = 0; + NS_MUST_OVERRIDE size_t // non-virtual -- it is only called by subclasses + SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + virtual size_t + SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const = 0; + protected: // to help implement nsIDOMCSSRule nsresult AppendRulesToCssText(nsAString& aCssText); diff --git a/layout/style/ImportRule.h b/layout/style/ImportRule.h index 4a24684ee3de..20e74d6342f4 100644 --- a/layout/style/ImportRule.h +++ b/layout/style/ImportRule.h @@ -81,6 +81,8 @@ public: void SetSheet(nsCSSStyleSheet*); + virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + // nsIDOMCSSRule interface NS_DECL_NSIDOMCSSRULE diff --git a/layout/style/NameSpaceRule.h b/layout/style/NameSpaceRule.h index 881ca739497e..2541a3fc22d0 100644 --- a/layout/style/NameSpaceRule.h +++ b/layout/style/NameSpaceRule.h @@ -84,6 +84,9 @@ public: void GetURLSpec(nsString& aURLSpec) const { aURLSpec = mURLSpec; } + virtual NS_MUST_OVERRIDE size_t + SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + // nsIDOMCSSRule interface NS_DECL_NSIDOMCSSRULE diff --git a/layout/style/Rule.h b/layout/style/Rule.h index 6add21ad9c35..7ac6733fe3ae 100644 --- a/layout/style/Rule.h +++ b/layout/style/Rule.h @@ -129,6 +129,16 @@ public: nsresult GetParentRule(nsIDOMCSSRule** aParentRule); nsresult GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet); + // This is pure virtual because all of Rule's data members are non-owning and + // thus measured elsewhere. + virtual NS_MUST_OVERRIDE size_t + SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const = 0; + + // This is used to measure nsCOMArrays. + static size_t SizeOfCOMArrayElementIncludingThis(css::Rule* aElement, + nsMallocSizeOfFun aMallocSizeOf, + void* aData); + protected: nsCSSStyleSheet* mSheet; GroupRule* mParentRule; diff --git a/layout/style/StyleRule.cpp b/layout/style/StyleRule.cpp index e12a8375eed9..ffd63bdd62f3 100644 --- a/layout/style/StyleRule.cpp +++ b/layout/style/StyleRule.cpp @@ -125,6 +125,22 @@ nsAtomList::Clone(bool aDeep) const return result; } +size_t +nsAtomList::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = 0; + const nsAtomList* a = this; + while (a) { + n += aMallocSizeOf(a); + + // The following members aren't measured: + // - a->mAtom, because it may be shared + + a = a->mNext; + } + return n; +} + nsAtomList::~nsAtomList(void) { MOZ_COUNT_DTOR(nsAtomList); @@ -204,6 +220,32 @@ nsPseudoClassList::Clone(bool aDeep) const return result; } +size_t +nsPseudoClassList::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = 0; + const nsPseudoClassList* p = this; + while (p) { + n += aMallocSizeOf(p); + if (!p->u.mMemory) { + // do nothing + + } else if (nsCSSPseudoClasses::HasStringArg(p->mType)) { + n += aMallocSizeOf(p->u.mString); + + } else if (nsCSSPseudoClasses::HasNthPairArg(p->mType)) { + n += aMallocSizeOf(p->u.mNumbers); + + } else { + NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(p->mType), + "unexpected pseudo-class"); + n += p->u.mSelectors->SizeOfIncludingThis(aMallocSizeOf); + } + p = p->mNext; + } + return n; +} + nsPseudoClassList::~nsPseudoClassList(void) { MOZ_COUNT_DTOR(nsPseudoClassList); @@ -810,6 +852,34 @@ nsCSSSelector::CanBeNamespaced(bool aIsNegated) const (!mIDList && !mClassList && !mPseudoClassList && !mAttrList); } +size_t +nsCSSSelector::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = 0; + const nsCSSSelector* s = this; + while (s) { + n += aMallocSizeOf(s); + + #define MEASURE(x) n += x ? x->SizeOfIncludingThis(aMallocSizeOf) : 0; + + MEASURE(s->mIDList); + MEASURE(s->mClassList); + MEASURE(s->mPseudoClassList); + MEASURE(s->mNegations); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - s->mAttrList + // + // The following members aren't measured: + // - s->mLowercaseTag, because it's an atom and therefore shared + // - s->mCasedTag, because it's an atom and therefore shared + + s = s->mNext; + } + return n; +} + // -- nsCSSSelectorList ------------------------------- nsCSSSelectorList::nsCSSSelectorList(void) @@ -872,6 +942,19 @@ nsCSSSelectorList::Clone(bool aDeep) const return result; } +size_t +nsCSSSelectorList::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = 0; + const nsCSSSelectorList* s = this; + while (s) { + n += aMallocSizeOf(s); + n += s->mSelectors ? s->mSelectors->SizeOfIncludingThis(aMallocSizeOf) : 0; + s = s->mNext; + } + return n; +} + // -- ImportantRule ---------------------------------- namespace mozilla { @@ -1422,5 +1505,21 @@ StyleRule::SetSelectorText(const nsAString& aSelectorText) // XXX and dirty sheet } +/* virtual */ size_t +StyleRule::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += mSelector ? mSelector->SizeOfIncludingThis(aMallocSizeOf) : 0; + n += mDeclaration ? mDeclaration->SizeOfIncludingThis(aMallocSizeOf) : 0; + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mImportantRule; + // - mDOMRule; + + return n; +} + + } // namespace css } // namespace mozilla diff --git a/layout/style/StyleRule.h b/layout/style/StyleRule.h index 114440ff38ff..ee7f3408a34b 100644 --- a/layout/style/StyleRule.h +++ b/layout/style/StyleRule.h @@ -70,6 +70,8 @@ public: /** Do a deep clone. Should be used only on the first in the linked list. */ nsAtomList* Clone() const { return Clone(true); } + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + nsCOMPtr mAtom; nsAtomList* mNext; private: @@ -91,6 +93,8 @@ public: /** Do a deep clone. Should be used only on the first in the linked list. */ nsPseudoClassList* Clone() const { return Clone(true); } + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + union { // For a given value of mType, we have either: // a. no value, which means mMemory is always null @@ -217,6 +221,8 @@ public: mPseudoType = static_cast(aType); } + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + // For case-sensitive documents, mLowercaseTag is the same as mCasedTag, // but in case-insensitive documents (HTML) mLowercaseTag is lowercase. // Also, for pseudo-elements mCasedTag will be null but mLowercaseTag @@ -271,6 +277,8 @@ struct nsCSSSelectorList { */ nsCSSSelectorList* Clone() const { return Clone(true); } + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + nsCSSSelector* mSelectors; PRInt32 mWeight; nsCSSSelectorList* mNext; @@ -380,6 +388,8 @@ public: virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const; #endif + virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + private: ~StyleRule(); diff --git a/layout/style/nsAnimationManager.cpp b/layout/style/nsAnimationManager.cpp index 9759f8c4d94b..b357ea0ca6bb 100644 --- a/layout/style/nsAnimationManager.cpp +++ b/layout/style/nsAnimationManager.cpp @@ -435,9 +435,12 @@ nsAnimationManager::RulesMatching(XULTreeRuleProcessorData* aData) /* virtual */ size_t nsAnimationManager::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const { - // XXX: various other members in nsAnimationManager could be measured here. - // Bug 671299 may do this. return CommonAnimationManager::SizeOfExcludingThis(aMallocSizeOf); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mKeyframesRules + // - mPendingEvents } /* virtual */ size_t diff --git a/layout/style/nsCSSDataBlock.cpp b/layout/style/nsCSSDataBlock.cpp index e9d1aefb0d47..c539aef48ff5 100644 --- a/layout/style/nsCSSDataBlock.cpp +++ b/layout/style/nsCSSDataBlock.cpp @@ -307,6 +307,20 @@ nsCSSCompressedDataBlock::CreateEmptyBlock() return result; } +size_t +nsCSSCompressedDataBlock::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + + const char* cursor = Block(); + const char* cursor_end = BlockEnd(); + while (cursor < cursor_end) { + n += ValueAtCursor(cursor)->SizeOfExcludingThis(aMallocSizeOf); + cursor += CDBValueStorage_advance; + } + return n; +} + /*****************************************************************************/ nsCSSExpandedDataBlock::nsCSSExpandedDataBlock() diff --git a/layout/style/nsCSSDataBlock.h b/layout/style/nsCSSDataBlock.h index 1f12d8712f5e..fcac919bf3ba 100644 --- a/layout/style/nsCSSDataBlock.h +++ b/layout/style/nsCSSDataBlock.h @@ -121,6 +121,8 @@ public: */ static nsCSSCompressedDataBlock* CreateEmptyBlock(); + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + private: void* operator new(size_t aBaseSize, size_t aDataSize) { NS_ABORT_IF_FALSE(aBaseSize == sizeof(nsCSSCompressedDataBlock), diff --git a/layout/style/nsCSSRules.cpp b/layout/style/nsCSSRules.cpp index e21b3f972831..49a0c26862d7 100644 --- a/layout/style/nsCSSRules.cpp +++ b/layout/style/nsCSSRules.cpp @@ -117,6 +117,13 @@ Rule::GetParentStyleSheet(nsIDOMCSSStyleSheet** aSheet) return NS_OK; } +size_t +Rule::SizeOfCOMArrayElementIncludingThis(css::Rule* aElement, + nsMallocSizeOfFun aMallocSizeOf, + void* aData) +{ + return aElement->SizeOfIncludingThis(aMallocSizeOf); +} // ------------------------------- // Style Rule List for group rules @@ -322,7 +329,15 @@ CharsetRule::GetParentRule(nsIDOMCSSRule** aParentRule) return Rule::GetParentRule(aParentRule); } +/* virtual */ size_t +CharsetRule::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + return aMallocSizeOf(this); + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mEncoding +} // ------------------------------------------- // ImportRule @@ -490,6 +505,20 @@ ImportRule::GetStyleSheet(nsIDOMCSSStyleSheet * *aStyleSheet) return NS_OK; } +/* virtual */ size_t +ImportRule::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + return aMallocSizeOf(this); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mURLSpec + // + // The following members are not measured: + // - mMedia, because it is measured via nsCSSStyleSheet::mMedia + // - mChildSheet, because it is measured via nsCSSStyleSheetInner::mSheets +} + } // namespace css } // namespace mozilla @@ -695,6 +724,17 @@ GroupRule::DeleteRule(PRUint32 aIndex) return mSheet->DeleteRuleFromGroup(this, aIndex); } +/* virtual */ size_t +GroupRule::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + return mRules.SizeOfExcludingThis(Rule::SizeOfCOMArrayElementIncludingThis, + aMallocSizeOf); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mRuleCollection +} + // ------------------------------------------- // nsICSSMediaRule @@ -866,6 +906,19 @@ MediaRule::UseForPresentation(nsPresContext* aPresContext, return true; } +/* virtual */ size_t +MediaRule::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += GroupRule::SizeOfExcludingThis(aMallocSizeOf); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mMedia + + return n; +} + } // namespace css } // namespace mozilla @@ -1075,6 +1128,19 @@ DocumentRule::URL::~URL() NS_CSS_DELETE_LIST_MEMBER(DocumentRule::URL, this, next); } +/* virtual */ size_t +DocumentRule::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += GroupRule::SizeOfExcludingThis(aMallocSizeOf); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mURLs + + return n; +} + } // namespace css } // namespace mozilla @@ -1199,6 +1265,18 @@ NameSpaceRule::GetParentRule(nsIDOMCSSRule** aParentRule) return Rule::GetParentRule(aParentRule); } +/* virtual */ size_t +NameSpaceRule::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + return aMallocSizeOf(this); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mPrefix + // - mURLSpec +} + + } // namespace css } // namespace mozilla @@ -1660,6 +1738,17 @@ nsCSSFontFaceRule::GetDesc(nsCSSFontDesc aDescID, nsCSSValue & aValue) aValue = mDecl.*nsCSSFontFaceStyleDecl::Fields[aDescID]; } +/* virtual */ size_t +nsCSSFontFaceRule::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + return aMallocSizeOf(this); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mDecl +} + + // ------------------------------------------- // nsCSSKeyframeStyleDeclaration // @@ -1882,6 +1971,19 @@ nsCSSKeyframeRule::ChangeDeclaration(css::Declaration* aDeclaration) } } +/* virtual */ size_t +nsCSSKeyframeRule::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + return aMallocSizeOf(this); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mKeys + // - mDeclaration + // - mDOMDeclaration +} + + // ------------------------------------------- // nsCSSKeyframesRule // @@ -2079,3 +2181,17 @@ nsCSSKeyframesRule::UseForPresentation(nsPresContext* aPresContext, return false; } +/* virtual */ size_t +nsCSSKeyframesRule::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += GroupRule::SizeOfExcludingThis(aMallocSizeOf); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mName + + return n; +} + + diff --git a/layout/style/nsCSSRules.h b/layout/style/nsCSSRules.h index a6c421d9c534..8aa1489cf7dc 100644 --- a/layout/style/nsCSSRules.h +++ b/layout/style/nsCSSRules.h @@ -111,6 +111,9 @@ public: // @media rule methods nsresult SetMedia(nsMediaList* aMedia); + virtual NS_MUST_OVERRIDE size_t + SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + protected: nsRefPtr mMedia; }; @@ -174,6 +177,9 @@ public: void SetURLs(URL *aURLs) { mURLs = aURLs; } + virtual NS_MUST_OVERRIDE size_t + SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + protected: nsAutoPtr mURLs; // linked list of |struct URL| above. }; @@ -241,6 +247,8 @@ public: void SetDesc(nsCSSFontDesc aDescID, nsCSSValue const & aValue); void GetDesc(nsCSSFontDesc aDescID, nsCSSValue & aValue); + virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + protected: friend class nsCSSFontFaceStyleDecl; nsCSSFontFaceStyleDecl mDecl; @@ -301,6 +309,8 @@ public: NS_IMETHOD GetEncoding(nsAString& aEncoding); NS_IMETHOD SetEncoding(const nsAString& aEncoding); + virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + private: nsString mEncoding; }; @@ -378,6 +388,8 @@ public: void ChangeDeclaration(mozilla::css::Declaration* aDeclaration); + virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + private: nsAutoTArray mKeys; nsAutoPtr mDeclaration; @@ -424,6 +436,8 @@ public: const nsString& GetName() { return mName; } + virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + private: PRUint32 FindRuleIndexForKey(const nsAString& aKey); diff --git a/layout/style/nsCSSStyleSheet.cpp b/layout/style/nsCSSStyleSheet.cpp index 1494652be46e..029dd75f1762 100644 --- a/layout/style/nsCSSStyleSheet.cpp +++ b/layout/style/nsCSSStyleSheet.cpp @@ -874,6 +874,30 @@ nsCSSStyleSheet::RebuildChildList(css::Rule* aRule, void* aBuilder) return true; } +size_t +nsCSSStyleSheet::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = 0; + const nsCSSStyleSheet* s = this; + while (s) { + n += aMallocSizeOf(s); + n += s->mInner->SizeOfIncludingThis(aMallocSizeOf); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - s->mTitle + // - s->mMedia + // - s->mRuleCollection + // - s->mRuleProcessors + // + // The following members are not measured: + // - s->mOwnerRule, because it's non-owning + + s = s->mNext; + } + return n; +} + nsCSSStyleSheetInner::nsCSSStyleSheetInner(nsCSSStyleSheetInner& aCopy, nsCSSStyleSheet* aPrimarySheet) : mSheets(), @@ -983,6 +1007,28 @@ nsCSSStyleSheetInner::CreateNamespaceMap() return NS_OK; } +size_t +nsCSSStyleSheetInner::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += mOrderedRules.SizeOfExcludingThis(css::Rule::SizeOfCOMArrayElementIncludingThis, + aMallocSizeOf); + n += mFirstChild ? mFirstChild->SizeOfIncludingThis(aMallocSizeOf) : 0; + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mSheetURI + // - mOriginalSheetURI + // - mBaseURI + // - mPrincipal + // - mNameSpaceMap + // + // The following members are not measured: + // - mSheets, because it's non-owning + + return n; +} + // ------------------------------- // CSS Style Sheet // diff --git a/layout/style/nsCSSStyleSheet.h b/layout/style/nsCSSStyleSheet.h index 6a1a680e8afb..fa602a9199f4 100644 --- a/layout/style/nsCSSStyleSheet.h +++ b/layout/style/nsCSSStyleSheet.h @@ -99,6 +99,8 @@ private: // Create a new namespace map nsresult CreateNamespaceMap(); + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + nsAutoTArray mSheets; nsCOMPtr mSheetURI; // for error reports, etc. nsCOMPtr mOriginalSheetURI; // for GetHref. Can be null. @@ -266,6 +268,8 @@ public: // list after we clone a unique inner for ourselves. static bool RebuildChildList(mozilla::css::Rule* aRule, void* aBuilder); + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + private: nsCSSStyleSheet(const nsCSSStyleSheet& aCopy, nsCSSStyleSheet* aParentToUse, diff --git a/layout/style/nsCSSValue.cpp b/layout/style/nsCSSValue.cpp index e435166b2ec9..ea3e010ded95 100644 --- a/layout/style/nsCSSValue.cpp +++ b/layout/style/nsCSSValue.cpp @@ -1114,6 +1114,141 @@ nsCSSValue::AppendToString(nsCSSProperty aProperty, nsAString& aResult) const } } +size_t +nsCSSValue::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = 0; + + switch (GetUnit()) { + // No value: nothing extra to measure. + case eCSSUnit_Null: + case eCSSUnit_Auto: + case eCSSUnit_Inherit: + case eCSSUnit_Initial: + case eCSSUnit_None: + case eCSSUnit_Normal: + case eCSSUnit_System_Font: + case eCSSUnit_All: + case eCSSUnit_Dummy: + case eCSSUnit_DummyInherit: + break; + + // String + case eCSSUnit_String: + case eCSSUnit_Ident: + case eCSSUnit_Families: + case eCSSUnit_Attr: + case eCSSUnit_Local_Font: + case eCSSUnit_Font_Format: + case eCSSUnit_Element: + n += mValue.mString->SizeOfIncludingThisIfUnshared(aMallocSizeOf); + break; + + // Array + case eCSSUnit_Array: + case eCSSUnit_Counter: + case eCSSUnit_Counters: + case eCSSUnit_Cubic_Bezier: + case eCSSUnit_Steps: + case eCSSUnit_Function: + case eCSSUnit_Calc: + case eCSSUnit_Calc_Plus: + case eCSSUnit_Calc_Minus: + case eCSSUnit_Calc_Times_L: + case eCSSUnit_Calc_Times_R: + case eCSSUnit_Calc_Divided: + break; + + // URL + case eCSSUnit_URL: + n += mValue.mURL->SizeOfIncludingThis(aMallocSizeOf); + break; + + // Image + case eCSSUnit_Image: + // Not yet measured. Measurement may be added later if DMD finds it + // worthwhile. + break; + + // Gradient + case eCSSUnit_Gradient: + n += mValue.mGradient->SizeOfIncludingThis(aMallocSizeOf); + break; + + // Pair + case eCSSUnit_Pair: + n += mValue.mPair->SizeOfIncludingThis(aMallocSizeOf); + break; + + // Triplet + case eCSSUnit_Triplet: + n += mValue.mTriplet->SizeOfIncludingThis(aMallocSizeOf); + break; + + // Rect + case eCSSUnit_Rect: + n += mValue.mRect->SizeOfIncludingThis(aMallocSizeOf); + break; + + // List + case eCSSUnit_List: + n += mValue.mList->SizeOfIncludingThis(aMallocSizeOf); + break; + + // ListDep: not measured because it's non-owning. + case eCSSUnit_ListDep: + break; + + // PairList + case eCSSUnit_PairList: + n += mValue.mPairList->SizeOfIncludingThis(aMallocSizeOf); + break; + + // PairListDep: not measured because it's non-owning. + case eCSSUnit_PairListDep: + break; + + // Int: nothing extra to measure. + case eCSSUnit_Integer: + case eCSSUnit_Enumerated: + case eCSSUnit_EnumColor: + break; + + // Color: nothing extra to measure. + case eCSSUnit_Color: + break; + + // Float: nothing extra to measure. + case eCSSUnit_Percent: + case eCSSUnit_Number: + case eCSSUnit_PhysicalMillimeter: + case eCSSUnit_EM: + case eCSSUnit_XHeight: + case eCSSUnit_Char: + case eCSSUnit_RootEM: + case eCSSUnit_Point: + case eCSSUnit_Inch: + case eCSSUnit_Millimeter: + case eCSSUnit_Centimeter: + case eCSSUnit_Pica: + case eCSSUnit_Pixel: + case eCSSUnit_Degree: + case eCSSUnit_Grad: + case eCSSUnit_Radian: + case eCSSUnit_Hertz: + case eCSSUnit_Kilohertz: + case eCSSUnit_Seconds: + case eCSSUnit_Milliseconds: + break; + + default: + NS_ABORT_IF_FALSE(false, "bad nsCSSUnit"); + break; + } + + return n; +} + // --- nsCSSValueList ----------------- nsCSSValueList::~nsCSSValueList() @@ -1175,6 +1310,28 @@ nsCSSValueList::operator==(const nsCSSValueList& aOther) const return !p1 && !p2; // true if same length, false otherwise } +size_t +nsCSSValueList::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = 0; + const nsCSSValueList* v = this; + while (v) { + n += aMallocSizeOf(v); + n += v->mValue.SizeOfExcludingThis(aMallocSizeOf); + v = v->mNext; + } + return n; +} + +size_t +nsCSSValueList_heap::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += mValue.SizeOfExcludingThis(aMallocSizeOf); + n += mNext ? mNext->SizeOfIncludingThis(aMallocSizeOf) : 0; + return n; +} + // --- nsCSSRect ----------------- nsCSSRect::nsCSSRect(void) @@ -1239,6 +1396,17 @@ void nsCSSRect::SetAllSidesTo(const nsCSSValue& aValue) mLeft = aValue; } +size_t +nsCSSRect_heap::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += mTop .SizeOfExcludingThis(aMallocSizeOf); + n += mRight .SizeOfExcludingThis(aMallocSizeOf); + n += mBottom.SizeOfExcludingThis(aMallocSizeOf); + n += mLeft .SizeOfExcludingThis(aMallocSizeOf); + return n; +} + PR_STATIC_ASSERT(NS_SIDE_TOP == 0 && NS_SIDE_RIGHT == 1 && NS_SIDE_BOTTOM == 2 && NS_SIDE_LEFT == 3); @@ -1262,7 +1430,25 @@ nsCSSValuePair::AppendToString(nsCSSProperty aProperty, } } -// --- nsCSSValueTriple ----------------- +size_t +nsCSSValuePair::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = 0; + n += mXValue.SizeOfExcludingThis(aMallocSizeOf); + n += mYValue.SizeOfExcludingThis(aMallocSizeOf); + return n; +} + +size_t +nsCSSValuePair_heap::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += mXValue.SizeOfExcludingThis(aMallocSizeOf); + n += mYValue.SizeOfExcludingThis(aMallocSizeOf); + return n; +} + +// --- nsCSSValueTriplet ----------------- void nsCSSValueTriplet::AppendToString(nsCSSProperty aProperty, @@ -1279,6 +1465,16 @@ nsCSSValueTriplet::AppendToString(nsCSSProperty aProperty, } } +size_t +nsCSSValueTriplet_heap::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += mXValue.SizeOfExcludingThis(aMallocSizeOf); + n += mYValue.SizeOfExcludingThis(aMallocSizeOf); + n += mZValue.SizeOfExcludingThis(aMallocSizeOf); + return n; +} + // --- nsCSSValuePairList ----------------- nsCSSValuePairList::~nsCSSValuePairList() @@ -1342,6 +1538,40 @@ nsCSSValuePairList::operator==(const nsCSSValuePairList& aOther) const return !p1 && !p2; // true if same length, false otherwise } +size_t +nsCSSValuePairList::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = 0; + const nsCSSValuePairList* v = this; + while (v) { + n += aMallocSizeOf(v); + n += v->mXValue.SizeOfExcludingThis(aMallocSizeOf); + n += v->mYValue.SizeOfExcludingThis(aMallocSizeOf); + v = v->mNext; + } + return n; +} + +size_t +nsCSSValuePairList_heap::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += mXValue.SizeOfExcludingThis(aMallocSizeOf); + n += mYValue.SizeOfExcludingThis(aMallocSizeOf); + n += mNext ? mNext->SizeOfIncludingThis(aMallocSizeOf) : 0; + return n; +} + +size_t +nsCSSValue::Array::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + for (size_t i = 0; i < mCount; i++) { + n += mArray[i].SizeOfExcludingThis(aMallocSizeOf); + } + return n; +} + nsCSSValue::URL::URL(nsIURI* aURI, nsStringBuffer* aString, nsIURI* aReferrer, nsIPrincipal* aOriginPrincipal) : mURI(aURI), @@ -1418,6 +1648,24 @@ nsCSSValue::URL::GetURI() const return mURI; } +size_t +nsCSSValue::URL::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + + // This string is unshared. + n += mString->SizeOfIncludingThisMustBeUnshared(aMallocSizeOf); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - mURI + // - mReferrer + // - mOriginPrincipal + + return n; +} + + nsCSSValue::Image::Image(nsIURI* aURI, nsStringBuffer* aString, nsIURI* aReferrer, nsIPrincipal* aOriginPrincipal, nsIDocument* aDocument) @@ -1458,6 +1706,15 @@ nsCSSValueGradientStop::~nsCSSValueGradientStop() MOZ_COUNT_DTOR(nsCSSValueGradientStop); } +size_t +nsCSSValueGradientStop::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = 0; + n += mLocation.SizeOfExcludingThis(aMallocSizeOf); + n += mColor .SizeOfExcludingThis(aMallocSizeOf); + return n; +} + nsCSSValueGradient::nsCSSValueGradient(bool aIsRadial, bool aIsRepeating) : mIsRadial(aIsRadial), @@ -1470,6 +1727,21 @@ nsCSSValueGradient::nsCSSValueGradient(bool aIsRadial, { } +size_t +nsCSSValueGradient::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + n += mBgPos .SizeOfExcludingThis(aMallocSizeOf); + n += mAngle .SizeOfExcludingThis(aMallocSizeOf); + n += mRadialShape.SizeOfExcludingThis(aMallocSizeOf); + n += mRadialSize .SizeOfExcludingThis(aMallocSizeOf); + n += mStops .SizeOfExcludingThis(aMallocSizeOf); + for (PRUint32 i = 0; i < mStops.Length(); i++) { + n += mStops[i].SizeOfExcludingThis(aMallocSizeOf); + } + return n; +} + // --- nsCSSCornerSizes ----------------- nsCSSCornerSizes::nsCSSCornerSizes(void) diff --git a/layout/style/nsCSSValue.h b/layout/style/nsCSSValue.h index d49cd9b37909..7baad7ba797a 100644 --- a/layout/style/nsCSSValue.h +++ b/layout/style/nsCSSValue.h @@ -454,6 +454,8 @@ public: static already_AddRefed BufferFromString(const nsString& aValue); + size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + struct URL { // Methods are not inline because using an nsIPrincipal means requiring // caps, which leads to REQUIRES hell, since this header is included all @@ -481,6 +483,8 @@ public: nsIURI* GetURI() const; + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + private: // If mURIResolved is false, mURI stores the base URI. // If mURIResolved is true, mURI stores the URI we resolve to; this may be @@ -544,7 +548,7 @@ protected: nsCSSValueList* mListDependent; nsCSSValuePairList_heap* mPairList; nsCSSValuePairList* mPairListDependent; - } mValue; + } mValue; }; struct nsCSSValue::Array { @@ -642,6 +646,8 @@ private: } } + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + #undef CSSVALUE_LIST_FOR_EXTRA_VALUES private: @@ -662,6 +668,8 @@ struct nsCSSValueList { bool operator!=(const nsCSSValueList& aOther) const { return !(*this == aOther); } + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + nsCSSValue mValue; nsCSSValueList* mNext; @@ -678,6 +686,8 @@ private: // it's an implementation detail of nsCSSValue. struct nsCSSValueList_heap : public nsCSSValueList { NS_INLINE_DECL_REFCOUNTING(nsCSSValueList_heap) + + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; }; // This has to be here so that the relationship between nsCSSValueList @@ -756,6 +766,8 @@ struct nsCSSRect { // it's an implementation detail of nsCSSValue. struct nsCSSRect_heap : public nsCSSRect { NS_INLINE_DECL_REFCOUNTING(nsCSSRect_heap) + + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; }; // This has to be here so that the relationship between nsCSSRect @@ -826,6 +838,8 @@ struct nsCSSValuePair { void AppendToString(nsCSSProperty aProperty, nsAString& aResult) const; + size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + nsCSSValue mXValue; nsCSSValue mYValue; }; @@ -834,12 +848,14 @@ struct nsCSSValuePair { // refcounted. It should not be necessary to use this class directly; // it's an implementation detail of nsCSSValue. struct nsCSSValuePair_heap : public nsCSSValuePair { - // forward constructor - nsCSSValuePair_heap(const nsCSSValue& aXValue, const nsCSSValue& aYValue) - : nsCSSValuePair(aXValue, aYValue) - {} + // forward constructor + nsCSSValuePair_heap(const nsCSSValue& aXValue, const nsCSSValue& aYValue) + : nsCSSValuePair(aXValue, aYValue) + {} - NS_INLINE_DECL_REFCOUNTING(nsCSSValuePair_heap) + NS_INLINE_DECL_REFCOUNTING(nsCSSValuePair_heap) + + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; }; struct nsCSSValueTriplet { @@ -916,6 +932,8 @@ struct nsCSSValueTriplet_heap : public nsCSSValueTriplet { {} NS_INLINE_DECL_REFCOUNTING(nsCSSValueTriplet_heap) + + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; }; // This has to be here so that the relationship between nsCSSValuePair @@ -960,6 +978,8 @@ struct nsCSSValuePairList { bool operator!=(const nsCSSValuePairList& aOther) const { return !(*this == aOther); } + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + nsCSSValue mXValue; nsCSSValue mYValue; nsCSSValuePairList* mNext; @@ -977,6 +997,8 @@ private: // it's an implementation detail of nsCSSValue. struct nsCSSValuePairList_heap : public nsCSSValuePairList { NS_INLINE_DECL_REFCOUNTING(nsCSSValuePairList_heap) + + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; }; // This has to be here so that the relationship between nsCSSValuePairList @@ -1024,6 +1046,8 @@ public: { return !(*this == aOther); } + + size_t SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const; }; struct nsCSSValueGradient { @@ -1072,6 +1096,8 @@ struct nsCSSValueGradient { NS_INLINE_DECL_REFCOUNTING(nsCSSValueGradient) + size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const; + private: nsCSSValueGradient(const nsCSSValueGradient& aOther) MOZ_DELETE; nsCSSValueGradient& operator=(const nsCSSValueGradient& aOther) MOZ_DELETE; diff --git a/layout/style/nsIStyleSheet.h b/layout/style/nsIStyleSheet.h index ca38f5d3b599..da12d37189fa 100644 --- a/layout/style/nsIStyleSheet.h +++ b/layout/style/nsIStyleSheet.h @@ -109,6 +109,8 @@ public: #ifdef DEBUG virtual void List(FILE* out = stdout, PRInt32 aIndent = 0) const = 0; #endif + + virtual size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) const = 0; }; NS_DEFINE_STATIC_IID_ACCESSOR(nsIStyleSheet, NS_ISTYLE_SHEET_IID) diff --git a/layout/style/nsLayoutStylesheetCache.cpp b/layout/style/nsLayoutStylesheetCache.cpp index 304b58abd390..db8a42e108c8 100644 --- a/layout/style/nsLayoutStylesheetCache.cpp +++ b/layout/style/nsLayoutStylesheetCache.cpp @@ -41,12 +41,30 @@ #include "mozilla/css/Loader.h" #include "nsIFile.h" #include "nsLayoutCID.h" +#include "nsIMemoryReporter.h" #include "nsNetUtil.h" #include "nsIObserverService.h" #include "nsServiceManagerUtils.h" #include "nsIXULRuntime.h" #include "nsCSSStyleSheet.h" +NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(LayoutStyleSheetCacheMallocSizeOf, + "layout/style-sheet-cache") + +static PRInt64 +GetStylesheetCacheSize() +{ + return nsLayoutStylesheetCache::SizeOfIncludingThis( + LayoutStyleSheetCacheMallocSizeOf); +} + +NS_MEMORY_REPORTER_IMPLEMENT(Sheets, + "explicit/layout/style-sheet-cache", + KIND_HEAP, + nsIMemoryReporter::UNITS_BYTES, + GetStylesheetCacheSize, + "Memory used for some built-in style sheets.") + NS_IMPL_ISUPPORTS1(nsLayoutStylesheetCache, nsIObserver) nsresult @@ -172,6 +190,38 @@ nsLayoutStylesheetCache::Shutdown() NS_IF_RELEASE(gStyleCache); } +size_t +nsLayoutStylesheetCache::SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf) +{ + if (nsLayoutStylesheetCache::gStyleCache) { + return nsLayoutStylesheetCache::gStyleCache-> + SizeOfIncludingThisHelper(aMallocSizeOf); + } + return 0; +} + +size_t +nsLayoutStylesheetCache::SizeOfIncludingThisHelper(nsMallocSizeOfFun aMallocSizeOf) const +{ + size_t n = aMallocSizeOf(this); + + #define MEASURE(s) n += s ? s->SizeOfIncludingThis(aMallocSizeOf) : 0; + + MEASURE(mScrollbarsSheet); + MEASURE(mFormsSheet); + MEASURE(mUserContentSheet); + MEASURE(mUserChromeSheet); + MEASURE(mUASheet); + MEASURE(mQuirkSheet); + MEASURE(mFullScreenOverrideSheet); + + // Measurement of the following members may be added later if DMD finds it is + // worthwhile: + // - gCSSLoader + + return n; +} + nsLayoutStylesheetCache::nsLayoutStylesheetCache() { nsCOMPtr obsSvc = @@ -208,6 +258,14 @@ nsLayoutStylesheetCache::nsLayoutStylesheetCache() } NS_ASSERTION(mFullScreenOverrideSheet, "Could not load full-screen-override.css"); + mSheetsReporter = new NS_MEMORY_REPORTER_NAME(Sheets); + (void)::NS_RegisterMemoryReporter(mSheetsReporter); +} + +nsLayoutStylesheetCache::~nsLayoutStylesheetCache() +{ + (void)::NS_UnregisterMemoryReporter(mSheetsReporter); + mSheetsReporter = nsnull; } void diff --git a/layout/style/nsLayoutStylesheetCache.h b/layout/style/nsLayoutStylesheetCache.h index ad45c52a3d5d..29e17d96ce0c 100644 --- a/layout/style/nsLayoutStylesheetCache.h +++ b/layout/style/nsLayoutStylesheetCache.h @@ -52,6 +52,8 @@ class Loader; } } +class nsIMemoryReporter; + class nsLayoutStylesheetCache : public nsIObserver { @@ -68,9 +70,11 @@ class nsLayoutStylesheetCache static void Shutdown(); + static size_t SizeOfIncludingThis(nsMallocSizeOfFun aMallocSizeOf); + private: nsLayoutStylesheetCache(); - ~nsLayoutStylesheetCache() {} + ~nsLayoutStylesheetCache(); static void EnsureGlobal(); void InitFromProfile(); @@ -78,6 +82,8 @@ private: static void LoadSheet(nsIURI* aURI, nsRefPtr &aSheet, bool aEnableUnsafeRules); + size_t SizeOfIncludingThisHelper(nsMallocSizeOfFun aMallocSizeOf) const; + static nsLayoutStylesheetCache* gStyleCache; static mozilla::css::Loader* gCSSLoader; nsRefPtr mScrollbarsSheet; @@ -87,6 +93,8 @@ private: nsRefPtr mUASheet; nsRefPtr mQuirkSheet; nsRefPtr mFullScreenOverrideSheet; + + nsIMemoryReporter* mSheetsReporter; }; #endif diff --git a/xpcom/base/nsIMemoryReporter.idl b/xpcom/base/nsIMemoryReporter.idl index c550fc2c97c1..83bf8e478c31 100644 --- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -356,7 +356,7 @@ namespace mozilla { * the relevant memory reporter(s). */ #define NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(fn, name) \ - size_t fn(const void *ptr) \ + static size_t fn(const void *ptr) \ { \ size_t usable = moz_malloc_size_of(ptr); \ VALGRIND_DMD_REPORT(ptr, usable, name); \ @@ -368,7 +368,7 @@ namespace mozilla { * "unreport" message to DMD. */ #define NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN_UN(fn) \ - size_t fn(const void *ptr) \ + static size_t fn(const void *ptr) \ { \ size_t usable = moz_malloc_size_of(ptr); \ VALGRIND_DMD_UNREPORT(ptr); \ From 8d27955326e5e79cc19619a6a8a537a8a3979f54 Mon Sep 17 00:00:00 2001 From: James Willcox Date: Tue, 31 Jan 2012 09:40:58 -0500 Subject: [PATCH 04/90] Bug 721741 - Add support for Flash on Android 4.0+ r=blassey --- dom/plugins/base/Makefile.in | 1 + dom/plugins/base/android/ANPAudio.cpp | 21 +- dom/plugins/base/android/ANPBase.h | 13 +- dom/plugins/base/android/ANPNativeWindow.cpp | 85 ++++++ dom/plugins/base/android/ANPOpenGL.cpp | 65 +++++ dom/plugins/base/android/ANPSystem.cpp | 25 ++ dom/plugins/base/android/ANPVideo.cpp | 109 ++++++++ dom/plugins/base/android/ANPWindow.cpp | 55 ++++ dom/plugins/base/android/Makefile.in | 5 + dom/plugins/base/android/android_npapi.h | 158 ++++++++++- dom/plugins/base/nsNPAPIPlugin.cpp | 71 ++++- dom/plugins/base/nsNPAPIPluginInstance.h | 2 + dom/plugins/base/nsPluginInstanceOwner.cpp | 123 ++++++--- dom/plugins/base/nsPluginInstanceOwner.h | 37 ++- layout/generic/nsObjectFrame.cpp | 1 - mobile/android/base/GeckoApp.java | 134 ++++++++- mobile/android/base/GeckoAppShell.java | 35 ++- mobile/android/base/Makefile.in | 2 + mobile/android/base/Tab.java | 21 ++ mobile/android/base/Tabs.java | 4 +- mobile/android/base/gfx/Layer.java | 5 +- mobile/android/base/gfx/LayerRenderer.java | 34 +++ mobile/android/base/gfx/LayerView.java | 8 + .../android/base/gfx/SurfaceTextureLayer.java | 259 ++++++++++++++++++ mobile/android/base/gfx/TextureGenerator.java | 73 +++++ mobile/android/base/gfx/TextureReaper.java | 6 +- mobile/android/base/ui/PanZoomController.java | 12 +- other-licenses/skia-npapi/Makefile.in | 1 + widget/android/AndroidBridge.cpp | 86 +++++- widget/android/AndroidBridge.h | 8 +- widget/android/AndroidMediaLayer.cpp | 155 +++++++++++ widget/android/AndroidMediaLayer.h | 96 +++++++ widget/android/Makefile.in | 1 + widget/android/nsWindow.cpp | 2 +- 34 files changed, 1640 insertions(+), 73 deletions(-) create mode 100644 dom/plugins/base/android/ANPNativeWindow.cpp create mode 100644 dom/plugins/base/android/ANPOpenGL.cpp create mode 100644 dom/plugins/base/android/ANPVideo.cpp create mode 100644 mobile/android/base/gfx/SurfaceTextureLayer.java create mode 100644 mobile/android/base/gfx/TextureGenerator.java create mode 100644 widget/android/AndroidMediaLayer.cpp create mode 100644 widget/android/AndroidMediaLayer.h diff --git a/dom/plugins/base/Makefile.in b/dom/plugins/base/Makefile.in index 1b67bfff57c5..d91f793ed684 100644 --- a/dom/plugins/base/Makefile.in +++ b/dom/plugins/base/Makefile.in @@ -138,6 +138,7 @@ endif LOCAL_INCLUDES += \ -DSK_BUILD_FOR_ANDROID_NDK \ + -I$(topsrcdir)/widget/android \ -I$(topsrcdir)/xpcom/base/ \ -I$(topsrcdir)/gfx/skia/include/core \ -I$(topsrcdir)/gfx/skia/include/config \ diff --git a/dom/plugins/base/android/ANPAudio.cpp b/dom/plugins/base/android/ANPAudio.cpp index 003616842680..c3398de9b8d8 100644 --- a/dom/plugins/base/android/ANPAudio.cpp +++ b/dom/plugins/base/android/ANPAudio.cpp @@ -306,7 +306,6 @@ anp_audio_start(ANPAudioTrack* s) if (s->keepGoing) { // we are already playing. Ignore. - LOG("anp_audio_start called twice!"); return; } @@ -359,7 +358,14 @@ anp_audio_isStopped(ANPAudioTrack* s) return s->isStopped; } -void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i) { +uint32_t +anp_audio_trackLatency(ANPAudioTrack* s) { + // Bug 721835 + NOT_IMPLEMENTED(); + return 1; +} + +void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i) { _assert(i->inSize == sizeof(*i)); ASSIGN(i, newTrack); ASSIGN(i, deleteTrack); @@ -368,3 +374,14 @@ void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i) { ASSIGN(i, stop); ASSIGN(i, isStopped); } + +void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, newTrack); + ASSIGN(i, deleteTrack); + ASSIGN(i, start); + ASSIGN(i, pause); + ASSIGN(i, stop); + ASSIGN(i, isStopped); + ASSIGN(i, trackLatency); +} diff --git a/dom/plugins/base/android/ANPBase.h b/dom/plugins/base/android/ANPBase.h index 729de6a5a342..1553e698ff60 100644 --- a/dom/plugins/base/android/ANPBase.h +++ b/dom/plugins/base/android/ANPBase.h @@ -36,8 +36,8 @@ * * ***** END LICENSE BLOCK ***** */ -#include "android_npapi.h" #include +#include "android_npapi.h" #include "nsAutoPtr.h" #include "nsISupportsImpl.h" @@ -53,7 +53,8 @@ "!!!!!!!!!!!!!! %s not implemented %s, %d", \ __PRETTY_FUNCTION__, __FILE__, __LINE__); \ -void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i); +void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i); +void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1* i); void InitBitmapInterface(ANPBitmapInterfaceV0 *i); void InitCanvasInterface(ANPCanvasInterfaceV0 *i); void InitEventInterface(ANPEventInterfaceV0 *i); @@ -63,5 +64,13 @@ void InitPaintInterface(ANPPaintInterfaceV0 *i); void InitPathInterface(ANPPathInterfaceV0 *i); void InitSurfaceInterface(ANPSurfaceInterfaceV0 *i); void InitSystemInterface(ANPSystemInterfaceV0 *i); +void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i); +void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i); void InitTypeFaceInterface(ANPTypefaceInterfaceV0 *i); void InitWindowInterface(ANPWindowInterfaceV0 *i); +void InitWindowInterfaceV1(ANPWindowInterfaceV1 *i); +void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i); +void InitVideoInterfaceV0(ANPVideoInterfaceV0 *i); +void InitVideoInterfaceV1(ANPVideoInterfaceV1 *i); +void InitOpenGLInterface(ANPOpenGLInterfaceV0 *i); +void InitNativeWindowInterface(ANPNativeWindowInterfaceV0 *i); diff --git a/dom/plugins/base/android/ANPNativeWindow.cpp b/dom/plugins/base/android/ANPNativeWindow.cpp new file mode 100644 index 000000000000..4359688a859d --- /dev/null +++ b/dom/plugins/base/android/ANPNativeWindow.cpp @@ -0,0 +1,85 @@ +/* -*- 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 Android NPAPI support code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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 ***** */ + +// must include config.h first for webkit to fiddle with new/delete +#include +#include "AndroidBridge.h" +#include "AndroidMediaLayer.h" +#include "ANPBase.h" +#include "nsIPluginInstanceOwner.h" +#include "nsPluginInstanceOwner.h" +#include "nsNPAPIPluginInstance.h" +#include "gfxRect.h" + +using namespace mozilla; +using namespace mozilla; + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) +#define ASSIGN(obj, name) (obj)->name = anp_native_window_##name + +static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + return NULL; + } + + + ANPNativeWindow window = owner->Layer()->GetNativeWindowForContent(); + owner->Invalidate(); + + return window; +} + +static void anp_native_window_invertPluginContent(NPP instance, bool isContentInverted) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + return; + } + + owner->Layer()->SetInverted(isContentInverted); +} + + +void InitNativeWindowInterface(ANPNativeWindowInterfaceV0* i) { + ASSIGN(i, acquireNativeWindow); + ASSIGN(i, invertPluginContent); +} diff --git a/dom/plugins/base/android/ANPOpenGL.cpp b/dom/plugins/base/android/ANPOpenGL.cpp new file mode 100644 index 000000000000..1c55fff40a65 --- /dev/null +++ b/dom/plugins/base/android/ANPOpenGL.cpp @@ -0,0 +1,65 @@ +/* The Original Code is Android NPAPI support code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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 +#include +#include "AndroidBridge.h" +#include "ANPBase.h" +#include "GLContextProvider.h" +#include "nsNPAPIPluginInstance.h" + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) +#define ASSIGN(obj, name) (obj)->name = anp_opengl_##name + +using namespace mozilla; +using namespace mozilla::gl; + +static ANPEGLContext anp_opengl_acquireContext(NPP inst) { + // Bug 687267 + NOT_IMPLEMENTED(); + return NULL; +} + +static ANPTextureInfo anp_opengl_lockTexture(NPP instance) { + ANPTextureInfo info = { 0, 0, 0, 0 }; + NOT_IMPLEMENTED(); + return info; +} + +static void anp_opengl_releaseTexture(NPP instance, const ANPTextureInfo* info) { + NOT_IMPLEMENTED(); +} + +static void anp_opengl_invertPluginContent(NPP instance, bool isContentInverted) { + NOT_IMPLEMENTED(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitOpenGLInterface(ANPOpenGLInterfaceV0* i) { + ASSIGN(i, acquireContext); + ASSIGN(i, lockTexture); + ASSIGN(i, releaseTexture); + ASSIGN(i, invertPluginContent); +} diff --git a/dom/plugins/base/android/ANPSystem.cpp b/dom/plugins/base/android/ANPSystem.cpp index 0fcd1530e330..876f9940ed4e 100644 --- a/dom/plugins/base/android/ANPSystem.cpp +++ b/dom/plugins/base/android/ANPSystem.cpp @@ -62,6 +62,12 @@ anp_system_getApplicationDataDirectory() return dir; } +const char* +anp_system_getApplicationDataDirectory(NPP instance) +{ + return anp_system_getApplicationDataDirectory(); +} + jclass anp_system_loadJavaClass(NPP instance, const char* className) { LOG("%s", __PRETTY_FUNCTION__); @@ -88,8 +94,27 @@ jclass anp_system_loadJavaClass(NPP instance, const char* className) return reinterpret_cast(obj); } +void anp_system_setPowerState(NPP instance, ANPPowerState powerState) +{ + NOT_IMPLEMENTED(); +} + void InitSystemInterface(ANPSystemInterfaceV0 *i) { _assert(i->inSize == sizeof(*i)); ASSIGN(i, getApplicationDataDirectory); ASSIGN(i, loadJavaClass); } + +void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, getApplicationDataDirectory); + ASSIGN(i, loadJavaClass); + ASSIGN(i, setPowerState); +} + +void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, getApplicationDataDirectory); + ASSIGN(i, loadJavaClass); + ASSIGN(i, setPowerState); +} diff --git a/dom/plugins/base/android/ANPVideo.cpp b/dom/plugins/base/android/ANPVideo.cpp new file mode 100644 index 000000000000..6b75425a046c --- /dev/null +++ b/dom/plugins/base/android/ANPVideo.cpp @@ -0,0 +1,109 @@ +/* The Original Code is Android NPAPI support code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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 +#include "ANPBase.h" +#include "AndroidMediaLayer.h" +#include "nsIPluginInstanceOwner.h" +#include "nsPluginInstanceOwner.h" +#include "nsNPAPIPluginInstance.h" +#include "gfxRect.h" + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) +#define ASSIGN(obj, name) (obj)->name = anp_video_##name + +using namespace mozilla; + +static AndroidMediaLayer* GetLayerForInstance(NPP instance) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + return NULL; + } + + return owner->Layer(); +} + +static void Invalidate(NPP instance) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) + return; + + owner->Invalidate(); +} + +static ANPNativeWindow anp_video_acquireNativeWindow(NPP instance) { + AndroidMediaLayer* layer = GetLayerForInstance(instance); + if (!layer) + return NULL; + + return layer->RequestNativeWindowForVideo(); +} + +static void anp_video_setWindowDimensions(NPP instance, const ANPNativeWindow window, + const ANPRectF* dimensions) { + AndroidMediaLayer* layer = GetLayerForInstance(instance); + if (!layer) + return; + + gfxRect rect(dimensions->left, dimensions->top, + dimensions->right - dimensions->left, + dimensions->bottom - dimensions->top); + + layer->SetNativeWindowDimensions(window, rect); + Invalidate(instance); +} + + +static void anp_video_releaseNativeWindow(NPP instance, ANPNativeWindow window) { + AndroidMediaLayer* layer = GetLayerForInstance(instance); + if (!layer) + return; + + layer->ReleaseNativeWindowForVideo(window); + Invalidate(instance); +} + +static void anp_video_setFramerateCallback(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc callback) { + // Bug 722682 + NOT_IMPLEMENTED(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitVideoInterfaceV0(ANPVideoInterfaceV0* i) { + ASSIGN(i, acquireNativeWindow); + ASSIGN(i, setWindowDimensions); + ASSIGN(i, releaseNativeWindow); +} + +void InitVideoInterfaceV1(ANPVideoInterfaceV1* i) { + ASSIGN(i, acquireNativeWindow); + ASSIGN(i, setWindowDimensions); + ASSIGN(i, releaseNativeWindow); + ASSIGN(i, setFramerateCallback); +} diff --git a/dom/plugins/base/android/ANPWindow.cpp b/dom/plugins/base/android/ANPWindow.cpp index c26790054b2c..75b643df386c 100644 --- a/dom/plugins/base/android/ANPWindow.cpp +++ b/dom/plugins/base/android/ANPWindow.cpp @@ -39,10 +39,16 @@ #include "assert.h" #include "ANPBase.h" #include +#include "AndroidBridge.h" +#include "nsNPAPIPluginInstance.h" +#include "nsIPluginInstanceOwner.h" +#include "nsPluginInstanceOwner.h" #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) #define ASSIGN(obj, name) (obj)->name = anp_window_##name +using namespace mozilla; + void anp_window_setVisibleRects(NPP instance, const ANPRectI rects[], int32_t count) { @@ -79,6 +85,32 @@ anp_window_requestCenterFitZoom(NPP instance) NOT_IMPLEMENTED(); } +ANPRectI +anp_window_visibleRect(NPP instance) +{ + ANPRectI rect = { 0, 0, 0, 0 }; + + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + return rect; + } + + nsIntRect visibleRect = owner->GetVisibleRect(); + rect.left = visibleRect.x; + rect.top = visibleRect.y; + rect.right = visibleRect.x + visibleRect.width; + rect.bottom = visibleRect.y + visibleRect.height; + + return rect; +} + +void anp_window_requestFullScreenOrientation(NPP instance, ANPScreenOrientation orientation) +{ + NOT_IMPLEMENTED(); +} + void InitWindowInterface(ANPWindowInterfaceV0 *i) { _assert(i->inSize == sizeof(*i)); ASSIGN(i, setVisibleRects); @@ -89,3 +121,26 @@ void InitWindowInterface(ANPWindowInterfaceV0 *i) { ASSIGN(i, requestCenterFitZoom); } +void InitWindowInterfaceV1(ANPWindowInterfaceV1 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, setVisibleRects); + ASSIGN(i, clearVisibleRects); + ASSIGN(i, showKeyboard); + ASSIGN(i, requestFullScreen); + ASSIGN(i, exitFullScreen); + ASSIGN(i, requestCenterFitZoom); + ASSIGN(i, visibleRect); +} + +void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, setVisibleRects); + ASSIGN(i, clearVisibleRects); + ASSIGN(i, showKeyboard); + ASSIGN(i, requestFullScreen); + ASSIGN(i, exitFullScreen); + ASSIGN(i, requestCenterFitZoom); + ASSIGN(i, visibleRect); + ASSIGN(i, requestFullScreenOrientation); +} + diff --git a/dom/plugins/base/android/Makefile.in b/dom/plugins/base/android/Makefile.in index 1b987e428de3..5f78039dc0fd 100644 --- a/dom/plugins/base/android/Makefile.in +++ b/dom/plugins/base/android/Makefile.in @@ -63,12 +63,17 @@ CPPSRCS += ANPAudio.cpp \ ANPWindow.cpp \ ANPBitmap.cpp \ ANPLog.cpp \ + ANPNativeWindow.cpp \ ANPSurface.cpp \ + ANPVideo.cpp \ + ANPOpenGL.cpp \ $(NULL) LOCAL_INCLUDES += \ + -I$(topsrcdir)/widget/android \ -I$(topsrcdir)/dom/plugins/base \ -I$(topsrcdir)/dom/plugins/base/android/include \ + -I$(topsrcdir)/gfx/gl \ $(MOZ_CAIRO_CFLAGS) \ $(NULL) diff --git a/dom/plugins/base/android/android_npapi.h b/dom/plugins/base/android/android_npapi.h index 27b0c0c9fab3..e903e2af0d37 100644 --- a/dom/plugins/base/android/android_npapi.h +++ b/dom/plugins/base/android/android_npapi.h @@ -36,8 +36,9 @@ #define android_npapi_H #include -#include "npapi.h" #include +#include "npapi.h" +#include "GLDefs.h" /////////////////////////////////////////////////////////////////////////////// // General types @@ -120,6 +121,16 @@ typedef uint32_t ANPMatrixFlag; #define kSystemInterfaceV0_ANPGetValue ((NPNVariable)1010) #define kEventInterfaceV0_ANPGetValue ((NPNVariable)1011) +#define kAudioTrackInterfaceV1_ANPGetValue ((NPNVariable)1012) +#define kOpenGLInterfaceV0_ANPGetValue ((NPNVariable)1013) +#define kWindowInterfaceV1_ANPGetValue ((NPNVariable)1014) +#define kVideoInterfaceV0_ANPGetValue ((NPNVariable)1015) +#define kSystemInterfaceV1_ANPGetValue ((NPNVariable)1016) +#define kSystemInterfaceV2_ANPGetValue ((NPNVariable)1017) +#define kWindowInterfaceV2_ANPGetValue ((NPNVariable)1018) +#define kNativeWindowInterfaceV0_ANPGetValue ((NPNVariable)1019) +#define kVideoInterfaceV1_ANPGetValue ((NPNVariable)1020) + /** queries for the drawing models supported on this device. NPN_GetValue(inst, kSupportedDrawingModel_ANPGetValue, uint32_t* bits) @@ -180,6 +191,7 @@ enum ANPDrawingModels { surface object. */ kSurface_ANPDrawingModel = 1 << 1, + kOpenGL_ANPDrawingModel = 1 << 2, }; typedef int32_t ANPDrawingModel; @@ -678,6 +690,25 @@ struct ANPWindowInterfaceV0 : ANPInterface { void (*requestCenterFitZoom)(NPP instance); }; +struct ANPWindowInterfaceV1 : ANPWindowInterfaceV0 { + /** Returns a rectangle representing the visible area of the plugin on + screen. The coordinates are relative to the size of the plugin in the + document and therefore will never be negative or exceed the plugin's size. + */ + ANPRectI (*visibleRect)(NPP instance); +}; + +typedef int32_t ANPScreenOrientation; + +struct ANPWindowInterfaceV2 : ANPWindowInterfaceV1 { + /** Called when the plugin wants to specify a particular screen orientation + when entering into full screen mode. The orientation must be set prior + to entering into full screen. After entering full screen any subsequent + changes will be updated the next time the plugin goes full screen. + */ + void (*requestFullScreenOrientation)(NPP instance, ANPScreenOrientation orientation); +}; + /////////////////////////////////////////////////////////////////////////////// enum ANPSampleFormats { @@ -762,6 +793,12 @@ struct ANPAudioTrackInterfaceV0 : ANPInterface { bool (*isStopped)(ANPAudioTrack*); }; +struct ANPAudioTrackInterfaceV1 : ANPAudioTrackInterfaceV0 { + /** Returns the track's latency in milliseconds. */ + uint32_t (*trackLatency)(ANPAudioTrack*); +}; + + /////////////////////////////////////////////////////////////////////////////// // DEFINITION OF VALUES PASSED THROUGH NPP_HandleEvent @@ -922,12 +959,16 @@ struct ANPEvent { // use based on the value in model union { ANPBitmap bitmap; + struct { + int32_t width; + int32_t height; + } surfaceSize; } data; } draw; - int32_t other[8]; } data; }; + struct ANPEventInterfaceV0 : ANPInterface { /** Post a copy of the specified event to the plugin. The event will be delivered to the plugin in its main thread (the thread that receives @@ -976,4 +1017,117 @@ typedef uint32_t uint32; typedef int16_t int16; typedef uint16_t uint16; +/** + * TODO should we not use EGL and GL data types for ABI safety? + */ +struct ANPTextureInfo { + GLuint textureId; + uint32_t width; + uint32_t height; + GLenum internalFormat; +}; + +typedef void* ANPEGLContext; + +struct ANPOpenGLInterfaceV0 : ANPInterface { + ANPEGLContext (*acquireContext)(NPP instance); + + ANPTextureInfo (*lockTexture)(NPP instance); + + void (*releaseTexture)(NPP instance, const ANPTextureInfo*); + + /** + * Invert the contents of the plugin on the y-axis. + * default is to not be inverted (i.e. use OpenGL coordinates) + */ + void (*invertPluginContent)(NPP instance, bool isContentInverted); +}; + +enum ANPPowerStates { + kDefault_ANPPowerState = 0, + kScreenOn_ANPPowerState = 1 +}; +typedef int32_t ANPPowerState; + +struct ANPSystemInterfaceV1 : ANPSystemInterfaceV0 { + void (*setPowerState)(NPP instance, ANPPowerState powerState); +}; + +struct ANPSystemInterfaceV2 : ANPInterface { + /** Return the path name for the current Application's plugin data directory, + or NULL if not supported. This directory will change depending on whether + or not the plugin is found within an incognito tab. + */ + const char* (*getApplicationDataDirectory)(NPP instance); + + // redeclaration of existing features + jclass (*loadJavaClass)(NPP instance, const char* className); + void (*setPowerState)(NPP instance, ANPPowerState powerState); +}; + +typedef void* ANPNativeWindow; + +struct ANPVideoInterfaceV0 : ANPInterface { + + /** + * Constructs a new native window to be used for rendering video content. + * + * Subsequent calls will produce new windows, but may also return NULL after + * n attempts if the browser has reached it's limit. Further, if the browser + * is unable to acquire the window quickly it may also return NULL in order + * to not prevent the plugin from executing. A subsequent call will then + * return the window if it is avaiable. + * + * NOTE: The hardware may fail if you try to decode more than the allowable + * number of videos supported on that device. + */ + ANPNativeWindow (*acquireNativeWindow)(NPP instance); + + /** + * Sets the rectangle that specifies where the video content is to be drawn. + * The dimensions are in document space. Further, if the rect is NULL the + * browser will not attempt to draw the window, therefore do not set the + * dimensions until you queue the first buffer in the window. + */ + void (*setWindowDimensions)(NPP instance, const ANPNativeWindow window, const ANPRectF* dimensions); + + /** + */ + void (*releaseNativeWindow)(NPP instance, ANPNativeWindow window); +}; + +/** Called to notify the plugin that a video frame has been composited by the +* browser for display. This will be called in a separate thread and as such +* you cannot call releaseNativeWindow from the callback. +* +* The timestamp is in nanoseconds, and is monotonically increasing. +*/ +typedef void (*ANPVideoFrameCallbackProc)(ANPNativeWindow* window, int64_t timestamp); + +struct ANPVideoInterfaceV1 : ANPVideoInterfaceV0 { + /** Set a callback to be notified when an ANPNativeWindow is composited by + * the browser. + */ + void (*setFramerateCallback)(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc); +}; + +struct ANPNativeWindowInterfaceV0 : ANPInterface { + /** + * Constructs a new native window to be used for rendering plugin content. + * + * Subsequent calls will return the original constructed window. Further, if + * the browser is unable to acquire the window quickly it may return NULL in + * order to not block the plugin indefinitely. A subsequent call will then + * return the window if it is available. + */ + ANPNativeWindow (*acquireNativeWindow)(NPP instance); + + /** + * Invert the contents of the plugin on the y-axis. + * default is to not be inverted (e.g. use OpenGL coordinates) + */ + void (*invertPluginContent)(NPP instance, bool isContentInverted); +}; + + #endif diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index 2d70d7f2fee3..109430b8ce85 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -132,9 +132,10 @@ using mozilla::plugins::PluginModuleParent; #endif #ifdef MOZ_WIDGET_ANDROID +#include +#include "android_npapi.h" #include "ANPBase.h" #include "AndroidBridge.h" -#include #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) #endif @@ -2348,7 +2349,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result) case kAudioTrackInterfaceV0_ANPGetValue: { LOG("get audio interface"); ANPAudioTrackInterfaceV0 *i = (ANPAudioTrackInterfaceV0 *) result; - InitAudioTrackInterface(i); + InitAudioTrackInterfaceV0(i); return NPERR_NO_ERROR; } @@ -2363,7 +2364,6 @@ _getvalue(NPP npp, NPNVariable variable, void *result) LOG("get system interface"); ANPSystemInterfaceV0* i = reinterpret_cast(result); InitSystemInterface(i); - LOG("done system interface"); return NPERR_NO_ERROR; } @@ -2395,6 +2395,71 @@ _getvalue(NPP npp, NPNVariable variable, void *result) *i = reinterpret_cast(ret); return NPERR_NO_ERROR; } + + case kAudioTrackInterfaceV1_ANPGetValue: { + LOG("get audio interface v1"); + ANPAudioTrackInterfaceV1 *i = (ANPAudioTrackInterfaceV1 *) result; + InitAudioTrackInterfaceV1(i); + return NPERR_NO_ERROR; + } + + case kNativeWindowInterfaceV0_ANPGetValue: { + LOG("get native window interface v0"); + ANPNativeWindowInterfaceV0* i = (ANPNativeWindowInterfaceV0 *) result; + InitNativeWindowInterface(i); + return NPERR_NO_ERROR; + } + + case kOpenGLInterfaceV0_ANPGetValue: { + LOG("get openGL interface"); + ANPOpenGLInterfaceV0 *i = (ANPOpenGLInterfaceV0*) result; + InitOpenGLInterface(i); + return NPERR_NO_ERROR; + } + + case kWindowInterfaceV1_ANPGetValue: { + LOG("get Window interface V1"); + ANPWindowInterfaceV1 *i = (ANPWindowInterfaceV1 *) result; + InitWindowInterfaceV1(i); + return NPERR_NO_ERROR; + } + + case kWindowInterfaceV2_ANPGetValue: { + LOG("get Window interface V2"); + ANPWindowInterfaceV2 *i = (ANPWindowInterfaceV2 *) result; + InitWindowInterfaceV2(i); + return NPERR_NO_ERROR; + } + + case kVideoInterfaceV0_ANPGetValue: { + LOG("get video interface"); + ANPVideoInterfaceV0 *i = (ANPVideoInterfaceV0*) result; + InitVideoInterfaceV0(i); + return NPERR_NO_ERROR; + } + + case kVideoInterfaceV1_ANPGetValue: { + LOG("get video interface"); + ANPVideoInterfaceV1 *i = (ANPVideoInterfaceV1*) result; + InitVideoInterfaceV1(i); + return NPERR_NO_ERROR; + } + + + case kSystemInterfaceV1_ANPGetValue: { + LOG("get system interface v1"); + ANPSystemInterfaceV1* i = reinterpret_cast(result); + InitSystemInterfaceV1(i); + return NPERR_NO_ERROR; + } + + case kSystemInterfaceV2_ANPGetValue: { + LOG("get system interface v2"); + ANPSystemInterfaceV2* i = reinterpret_cast(result); + InitSystemInterfaceV2(i); + return NPERR_NO_ERROR; + } + #endif // we no longer hand out any XPCOM objects diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h index ef4f8246ef8e..cf6e47d2ee68 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.h +++ b/dom/plugins/base/nsNPAPIPluginInstance.h @@ -152,6 +152,8 @@ public: #ifdef MOZ_WIDGET_ANDROID PRUint32 GetANPDrawingModel() { return mANPDrawingModel; } void SetANPDrawingModel(PRUint32 aModel); + + // This stuff is for kSurface_ANPDrawingModel void* GetJavaSurface(); void SetJavaSurface(void* aSurface); void RequestJavaSurface(); diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 5247a46d7667..90c5057338af 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -120,6 +120,7 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); #include "ANPBase.h" #include "android_npapi.h" #include "AndroidBridge.h" +#include "AndroidMediaLayer.h" using namespace mozilla::dom; #include @@ -323,8 +324,9 @@ nsPluginInstanceOwner::nsPluginInstanceOwner() mWaitingForPaint = false; #ifdef MOZ_WIDGET_ANDROID - mPluginViewAdded = false; - mLastPluginRect = gfxRect(0, 0, 0, 0); + mOnScreen = false; + mInverted = false; + mLayer = new AndroidMediaLayer(); #endif } @@ -380,6 +382,13 @@ nsPluginInstanceOwner::~nsPluginInstanceOwner() mPluginWindow = nsnull; } +#ifdef MOZ_WIDGET_ANDROID + if (mLayer) { + delete mLayer; + mLayer = nsnull; + } +#endif + if (mInstance) { mInstance->InvalidateOwner(); } @@ -1658,6 +1667,43 @@ void nsPluginInstanceOwner::ScrollPositionDidChange(nscoord aX, nscoord aY) } #ifdef MOZ_WIDGET_ANDROID + +void nsPluginInstanceOwner::SendSize(int width, int height) +{ + if (!mInstance) + return; + + PRInt32 model = mInstance->GetANPDrawingModel(); + + if (model != kOpenGL_ANPDrawingModel) + return; + + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = kDraw_ANPEventType; + event.data.draw.model = kOpenGL_ANPDrawingModel; + event.data.draw.data.surfaceSize.width = width; + event.data.draw.data.surfaceSize.height = height; + + mInstance->HandleEvent(&event, nsnull); +} + +void nsPluginInstanceOwner::SendOnScreenEvent(bool onScreen) +{ + if (!mInstance) + return; + + if ((onScreen && !mOnScreen) || (!onScreen && mOnScreen)) { + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = kLifecycle_ANPEventType; + event.data.lifecycle.action = onScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction; + mInstance->HandleEvent(&event, nsnull); + + mOnScreen = onScreen; + } +} + bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) { void* javaSurface = mInstance->GetJavaSurface(); @@ -1666,11 +1712,6 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) return false; } - if (aRect.IsEqualEdges(mLastPluginRect)) { - // Already added and in position, no work to do - return true; - } - JNIEnv* env = GetJNIForThread(); if (!env) return false; @@ -1713,26 +1754,16 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) aRect.height); #endif - if (!mPluginViewAdded) { - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kLifecycle_ANPEventType; - event.data.lifecycle.action = kOnScreen_ANPLifecycleAction; - mInstance->HandleEvent(&event, nsnull); - - mPluginViewAdded = true; - } + SendOnScreenEvent(true); return true; } void nsPluginInstanceOwner::RemovePluginView() { - if (!mInstance || !mObjectFrame | !mPluginViewAdded) + if (!mInstance || !mObjectFrame | !mOnScreen) return; - mPluginViewAdded = false; - void* surface = mInstance->GetJavaSurface(); if (!surface) return; @@ -1748,13 +1779,17 @@ void nsPluginInstanceOwner::RemovePluginView() "removePluginView", "(Landroid/view/View;)V"); env->CallStaticVoidMethod(cls, method, surface); - - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kLifecycle_ANPEventType; - event.data.lifecycle.action = kOffScreen_ANPLifecycleAction; - mInstance->HandleEvent(&event, nsnull); + SendOnScreenEvent(false); } + +void nsPluginInstanceOwner::Invalidate() { + NPRect rect; + rect.left = rect.top = 0; + rect.right = mPluginWindow->width; + rect.bottom = mPluginWindow->height; + InvalidateRect(&rect); +} + #endif nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent) @@ -2779,7 +2814,15 @@ nsPluginInstanceOwner::PrepareToStop(bool aDelayedStop) #endif #ifdef MOZ_WIDGET_ANDROID - RemovePluginView(); + + PRInt32 model = mInstance->GetANPDrawingModel(); + if (model == kSurface_ANPDrawingModel) { + RemovePluginView(); + } else if (model == kOpenGL_ANPDrawingModel && mLayer) { + delete mLayer; + mLayer = nsnull; + } + #endif // Unregister scroll position listeners @@ -2891,15 +2934,21 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext, if (model == kSurface_ANPDrawingModel) { if (!AddPluginView(aFrameRect)) { - NPRect rect; - rect.left = rect.top = 0; - rect.right = aFrameRect.width; - rect.bottom = aFrameRect.height; - InvalidateRect(&rect); + Invalidate(); } return; } + if (model == kOpenGL_ANPDrawingModel) { + // FIXME: this is gross + float zoomLevel = aFrameRect.width / (float)mPluginWindow->width; + mLayer->UpdatePosition(aFrameRect, zoomLevel); + + SendOnScreenEvent(true); + SendSize((int)aFrameRect.width, (int)aFrameRect.height); + return; + } + if (model != kBitmap_ANPDrawingModel) return; @@ -3590,8 +3639,16 @@ void nsPluginInstanceOwner::UpdateWindowPositionAndClipRect(bool aSetWindow) } else { mPluginWindow->clipRect.right = 0; mPluginWindow->clipRect.bottom = 0; -#ifdef MOZ_WIDGET_ANDROID - RemovePluginView(); +#if 0 //MOZ_WIDGET_ANDROID + if (mInstance) { + PRInt32 model = mInstance->GetANPDrawingModel(); + + if (model == kSurface_ANPDrawingModel) { + RemovePluginView(); + } else if (model == kOpenGL_ANPDrawingModel) { + HidePluginLayer(); + } + } #endif } diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h index a6acb483f741..a92f35e3ddbc 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.h +++ b/dom/plugins/base/nsPluginInstanceOwner.h @@ -94,6 +94,12 @@ class gfxXlibSurface; #include #endif +#ifdef MOZ_WIDGET_ANDROID +namespace mozilla { + class AndroidMediaLayer; +} +#endif + // X.h defines KeyPress #ifdef KeyPress #undef KeyPress @@ -297,6 +303,26 @@ public: void EndUpdateBackground(gfxContext* aContext, const nsIntRect& aRect); bool UseAsyncRendering(); + +#ifdef ANDROID + nsIntRect GetVisibleRect() { + return nsIntRect(0, 0, mPluginWindow->width, mPluginWindow->height); + } + + void SetInverted(bool aInverted) { + mInverted = aInverted; + } + + bool Inverted() { + return mInverted; + } + + mozilla::AndroidMediaLayer* Layer() { + return mLayer; + } + + void Invalidate(); +#endif private: @@ -310,10 +336,17 @@ private: void FixUpURLS(const nsString &name, nsAString &value); #ifdef ANDROID + void SendSize(int width, int height); + void SendOnScreenEvent(bool onScreen); + bool AddPluginView(const gfxRect& aRect); void RemovePluginView(); - bool mPluginViewAdded; - gfxRect mLastPluginRect; + + bool mOnScreen; + bool mInverted; + + // For kOpenGL_ANPDrawingModel + mozilla::AndroidMediaLayer *mLayer; #endif nsPluginNativeWindow *mPluginWindow; diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 102637134917..25c9616adc35 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -1695,7 +1695,6 @@ nsObjectFrame::PaintPlugin(nsDisplayListBuilder* aBuilder, // The matrix includes the frame's position, so we need to transform // from 0,0 to get the correct coordinates. frameGfxRect.MoveTo(0, 0); - matrix2d.NudgeToIntegers(); mInstanceOwner->Paint(ctx, matrix2d.Transform(frameGfxRect), dirtyGfxRect); return; diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index b35fa3478191..9e1efe4dc144 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -44,10 +44,12 @@ import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.gfx.FloatSize; import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient; import org.mozilla.gecko.gfx.IntSize; +import org.mozilla.gecko.gfx.Layer; import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.PlaceholderLayerClient; import org.mozilla.gecko.gfx.RectUtils; +import org.mozilla.gecko.gfx.SurfaceTextureLayer; import org.mozilla.gecko.gfx.ViewportMetrics; import org.mozilla.gecko.Tab.HistoryEntry; @@ -217,8 +219,9 @@ abstract public class GeckoApp public ArrayList mPackageInfoCache = new ArrayList(); String[] getPluginDirectories() { - // we don't support Honeycomb and later - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) + // we don't support Honeycomb + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && + Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) return new String[0]; Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - start of getPluginDirectories"); @@ -749,7 +752,7 @@ abstract public class GeckoApp mLayerController.setWaitForTouchListeners(false); if (tab != null) - hidePluginViews(tab); + hidePlugins(tab, true); } } }); @@ -1374,8 +1377,6 @@ abstract public class GeckoApp mMainHandler.post(new Runnable() { public void run() { PluginLayoutParams lp; - JSONObject viewportObject; - ViewportMetrics pluginViewport; Tabs tabs = Tabs.getInstance(); Tab tab = tabs.getSelectedTab(); @@ -1384,9 +1385,10 @@ abstract public class GeckoApp return; ViewportMetrics targetViewport = mLayerController.getViewportMetrics(); + ViewportMetrics pluginViewport; try { - viewportObject = new JSONObject(metadata); + JSONObject viewportObject = new JSONObject(metadata); pluginViewport = new ViewportMetrics(viewportObject); } catch (JSONException e) { Log.e(LOGTAG, "Bad viewport metadata: ", e); @@ -1441,28 +1443,128 @@ abstract public class GeckoApp }); } - public void hidePluginViews() { + public Surface createSurface() { + Tabs tabs = Tabs.getInstance(); + Tab tab = tabs.getSelectedTab(); + if (tab == null) + return null; + + SurfaceTextureLayer layer = SurfaceTextureLayer.create(); + if (layer == null) + return null; + + Surface surface = layer.getSurface(); + tab.addPluginLayer(surface, layer); + return surface; + } + + public void destroySurface(Surface surface) { + Tabs tabs = Tabs.getInstance(); + Tab tab = tabs.getSelectedTab(); + if (tab == null) + return; + + tab.removePluginLayer(surface); + } + + public void showSurface(Surface surface, int x, int y, + int w, int h, boolean inverted, boolean blend, + String metadata) { + Tabs tabs = Tabs.getInstance(); + Tab tab = tabs.getSelectedTab(); + if (tab == null) + return; + + ViewportMetrics metrics; + try { + metrics = new ViewportMetrics(new JSONObject(metadata)); + } catch (JSONException e) { + Log.e(LOGTAG, "Bad viewport metadata: ", e); + return; + } + + PointF origin = metrics.getDisplayportOrigin(); + x = x + (int)origin.x; + y = y + (int)origin.y; + + LayerView layerView = mLayerController.getView(); + SurfaceTextureLayer layer = (SurfaceTextureLayer)tab.getPluginLayer(surface); + if (layer == null) + return; + + layer.update(new Point(x, y), new IntSize(w, h), metrics.getZoomFactor(), inverted, blend); + layerView.addLayer(layer); + + // FIXME: shouldn't be necessary, layer will request + // one when it gets first frame + layerView.requestRender(); + } + + private void hidePluginLayer(Layer layer) { + LayerView layerView = mLayerController.getView(); + layerView.removeLayer(layer); + layerView.requestRender(); + } + + private void showPluginLayer(Layer layer) { + LayerView layerView = mLayerController.getView(); + layerView.addLayer(layer); + layerView.requestRender(); + } + + public void hideSurface(Surface surface) { + Tabs tabs = Tabs.getInstance(); + Tab tab = tabs.getSelectedTab(); + if (tab == null) + return; + + Layer layer = tab.getPluginLayer(surface); + if (layer == null) + return; + + hidePluginLayer(layer); + } + + public void requestRender() { + mLayerController.getView().requestRender(); + } + + public void hidePlugins(boolean hideLayers) { Tabs tabs = Tabs.getInstance(); Tab tab = tabs.getSelectedTab(); if (tab == null) return; - hidePluginViews(tab); + hidePlugins(tab, hideLayers); } - public void hidePluginViews(Tab tab) { + public void hidePlugins(Tab tab, boolean hideLayers) { for (View view : tab.getPluginViews()) { view.setVisibility(View.GONE); } + + if (hideLayers) { + for (Layer layer : tab.getPluginLayers()) { + hidePluginLayer(layer); + } + + requestRender(); + } } - public void showPluginViews() { + public void showPlugins() { repositionPluginViews(true); } - public void showPluginViews(Tab tab) { + public void showPlugins(Tab tab) { repositionPluginViews(tab, true); + + for (Layer layer : tab.getPluginLayers()) { + showPluginLayer(layer); + } + + requestRender(); } public void repositionPluginViews(boolean setVisible) { @@ -2619,8 +2721,10 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams } public void reset(int aX, int aY, int aWidth, int aHeight, ViewportMetrics aViewport) { - x = mOriginalX = aX; - y = mOriginalY = aY; + PointF origin = aViewport.getDisplayportOrigin(); + + x = mOriginalX = aX + (int)origin.x; + y = mOriginalY = aY + (int)origin.y; width = mOriginalWidth = aWidth; height = mOriginalHeight = aHeight; mOriginalViewport = aViewport; @@ -2651,4 +2755,8 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams reposition(offset, viewport.getZoomFactor()); } + + public float getLastResolution() { + return mLastResolution; + } } diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 56e7bbb77210..630cf8953cfd 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -226,6 +226,7 @@ public class GeckoAppShell try { sHandler = lt.mHandlerQueue.take(); } catch (InterruptedException ie) {} + } return sHandler; } @@ -567,7 +568,7 @@ public class GeckoAppShell while (sGeckoPendingAcks.getCount() != 0) { try { sGeckoPendingAcks.await(); - } catch (InterruptedException e) {} + } catch(InterruptedException e) {} } sGeckoPendingAcks = null; } @@ -1391,10 +1392,40 @@ public class GeckoAppShell } public static void removePluginView(View view) { - Log.i(LOGTAG, "remove view:" + view); + Log.i(LOGTAG, "removePluginView:" + view); GeckoApp.mAppContext.removePluginView(view); } + public static Surface createSurface() { + Log.i(LOGTAG, "createSurface"); + return GeckoApp.mAppContext.createSurface(); + } + + public static void showSurface(Surface surface, + int x, int y, + int w, int h, + boolean inverted, + boolean blend, + String metadata) + { + Log.i(LOGTAG, "showSurface:" + surface + " @ x:" + x + " y:" + y + " w:" + w + " h:" + h + " inverted: " + inverted + " blend: " + blend + " metadata: " + metadata); + try { + GeckoApp.mAppContext.showSurface(surface, x, y, w, h, inverted, blend, metadata); + } catch (Exception e) { + Log.i(LOGTAG, "Error in showSurface:", e); + } + } + + public static void hideSurface(Surface surface) { + Log.i(LOGTAG, "hideSurface:" + surface); + GeckoApp.mAppContext.hideSurface(surface); + } + + public static void destroySurface(Surface surface) { + Log.i(LOGTAG, "destroySurface:" + surface); + GeckoApp.mAppContext.destroySurface(surface); + } + public static Class loadPluginClass(String className, String libName) { Log.i(LOGTAG, "in loadPluginClass... attempting to access className, then libName....."); Log.i(LOGTAG, "className: " + className); diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 81999c349111..62b2971b50f4 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -124,7 +124,9 @@ FENNEC_JAVA_FILES = \ gfx/RectUtils.java \ gfx/ScrollbarLayer.java \ gfx/SingleTileLayer.java \ + gfx/SurfaceTextureLayer.java \ gfx/TextLayer.java \ + gfx/TextureGenerator.java \ gfx/TextureReaper.java \ gfx/TileLayer.java \ gfx/ViewportMetrics.java \ diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 8c35b25de93d..21672a08d2ea 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -44,13 +44,16 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.util.DisplayMetrics; import android.util.Log; +import android.view.Surface; import android.view.View; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.db.BrowserDB; +import org.mozilla.gecko.gfx.Layer; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -83,6 +86,7 @@ public final class Tab { private String mContentType; private boolean mHasTouchListeners; private ArrayList mPluginViews; + private HashMap mPluginLayers; private boolean mHasLoaded; public static final class HistoryEntry { @@ -117,6 +121,7 @@ public final class Tab { mDocumentURI = ""; mContentType = ""; mPluginViews = new ArrayList(); + mPluginLayers = new HashMap(); mHasLoaded = false; } @@ -568,4 +573,20 @@ public final class Tab { public View[] getPluginViews() { return mPluginViews.toArray(new View[mPluginViews.size()]); } + + public void addPluginLayer(Surface surface, Layer layer) { + mPluginLayers.put(surface, layer); + } + + public Layer getPluginLayer(Surface surface) { + return mPluginLayers.get(surface); + } + + public Collection getPluginLayers() { + return mPluginLayers.values(); + } + + public Layer removePluginLayer(Surface surface) { + return mPluginLayers.remove(surface); + } } diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index 6e466b9912d4..e22986d49afa 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -136,7 +136,7 @@ public class Tabs implements GeckoEventListener { GeckoApp.mBrowserToolbar.setShadowVisibility(!(tab.getURL().startsWith("about:"))); if (oldTab != null) - GeckoApp.mAppContext.hidePluginViews(oldTab); + GeckoApp.mAppContext.hidePlugins(oldTab, true); } } }); @@ -200,7 +200,7 @@ public class Tabs implements GeckoEventListener { GeckoApp.mAppContext.onTabsChanged(closedTab); GeckoApp.mBrowserToolbar.updateTabs(Tabs.getInstance().getCount()); GeckoApp.mDoorHangerPopup.updatePopup(); - GeckoApp.mAppContext.hidePluginViews(closedTab); + GeckoApp.mAppContext.hidePlugins(closedTab, true); } }); diff --git a/mobile/android/base/gfx/Layer.java b/mobile/android/base/gfx/Layer.java index 4c3328667542..46b81b7587f0 100644 --- a/mobile/android/base/gfx/Layer.java +++ b/mobile/android/base/gfx/Layer.java @@ -49,12 +49,13 @@ import org.mozilla.gecko.FloatUtils; public abstract class Layer { private final ReentrantLock mTransactionLock; private boolean mInTransaction; - private Point mOrigin; private Point mNewOrigin; - private float mResolution; private float mNewResolution; private LayerView mView; + protected Point mOrigin; + protected float mResolution; + public Layer() { mTransactionLock = new ReentrantLock(); mOrigin = new Point(0, 0); diff --git a/mobile/android/base/gfx/LayerRenderer.java b/mobile/android/base/gfx/LayerRenderer.java index 573e1884b838..7704e436ee23 100644 --- a/mobile/android/base/gfx/LayerRenderer.java +++ b/mobile/android/base/gfx/LayerRenderer.java @@ -46,6 +46,7 @@ import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.NinePatchTileLayer; import org.mozilla.gecko.gfx.SingleTileLayer; import org.mozilla.gecko.gfx.TextureReaper; +import org.mozilla.gecko.gfx.TextureGenerator; import org.mozilla.gecko.gfx.TextLayer; import org.mozilla.gecko.gfx.TileLayer; import android.content.Context; @@ -62,6 +63,7 @@ import android.view.WindowManager; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import java.nio.IntBuffer; +import java.util.ArrayList; /** * The layer renderer implements the rendering logic for a layer view. @@ -90,6 +92,8 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private RenderContext mLastPageContext; private int mMaxTextureSize; + private ArrayList mExtraLayers = new ArrayList(); + // Dropped frames display private int[] mFrameTimings; private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames; @@ -152,12 +156,34 @@ public class LayerRenderer implements GLSurfaceView.Renderer { int maxTextureSizeResult[] = new int[1]; gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0); mMaxTextureSize = maxTextureSizeResult[0]; + + TextureGenerator.get().fill(); } public int getMaxTextureSize() { return mMaxTextureSize; } + public void addLayer(Layer layer) { + LayerController controller = mView.getController(); + + synchronized (controller) { + if (mExtraLayers.contains(layer)) { + mExtraLayers.remove(layer); + } + + mExtraLayers.add(layer); + } + } + + public void removeLayer(Layer layer) { + LayerController controller = mView.getController(); + + synchronized (controller) { + mExtraLayers.remove(layer); + } + } + /** * Called whenever a new frame is about to be drawn. */ @@ -165,6 +191,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { long frameStartTime = SystemClock.uptimeMillis(); TextureReaper.get().reap(gl); + TextureGenerator.get().fill(); LayerController controller = mView.getController(); RenderContext screenContext = createScreenContext(); @@ -198,6 +225,9 @@ public class LayerRenderer implements GLSurfaceView.Renderer { updated &= mVertScrollLayer.update(gl, pageContext); updated &= mHorizScrollLayer.update(gl, pageContext); + for (Layer layer : mExtraLayers) + updated &= layer.update(gl, pageContext); + /* Draw the background. */ mBackgroundLayer.draw(screenContext); @@ -222,6 +252,10 @@ public class LayerRenderer implements GLSurfaceView.Renderer { gl.glDisable(GL10.GL_SCISSOR_TEST); + /* Draw any extra layers that were added (likely plugins) */ + for (Layer layer : mExtraLayers) + layer.draw(pageContext); + /* Draw the vertical scrollbar. */ IntSize screenSize = new IntSize(controller.getViewportSize()); if (pageRect.height() > screenSize.height) diff --git a/mobile/android/base/gfx/LayerView.java b/mobile/android/base/gfx/LayerView.java index 7781c1b97599..0e782daea9e6 100644 --- a/mobile/android/base/gfx/LayerView.java +++ b/mobile/android/base/gfx/LayerView.java @@ -224,6 +224,14 @@ public class LayerView extends GLSurfaceView } } + public void addLayer(Layer layer) { + mRenderer.addLayer(layer); + } + + public void removeLayer(Layer layer) { + mRenderer.removeLayer(layer); + } + /** * Returns the time elapsed between the first call of requestRender() after * the last call of getRenderTime(), in nanoseconds. diff --git a/mobile/android/base/gfx/SurfaceTextureLayer.java b/mobile/android/base/gfx/SurfaceTextureLayer.java new file mode 100644 index 000000000000..8f4799416dbe --- /dev/null +++ b/mobile/android/base/gfx/SurfaceTextureLayer.java @@ -0,0 +1,259 @@ +/* -*- 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) 2009-2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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.GeckoApp; + +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.SurfaceTexture; +import android.opengl.GLES11; +import android.opengl.GLES11Ext; +import android.opengl.Matrix; +import android.util.Log; +import android.view.Surface; +import javax.microedition.khronos.opengles.GL10; +import javax.microedition.khronos.opengles.GL11Ext; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import android.hardware.Camera; + +public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrameAvailableListener { + private static final String LOGTAG = "SurfaceTextureLayer"; + private static final int LOCAL_GL_TEXTURE_EXTERNAL_OES = 0x00008d65; // This is only defined in API level 15 for some reason (Android 4.0.3) + + private final SurfaceTexture mSurfaceTexture; + private final Surface mSurface; + private int mTextureId; + private boolean mHaveFrame; + + private IntSize mSize; + private IntSize mNewSize; + + private boolean mInverted; + private boolean mNewInverted; + private boolean mBlend; + private boolean mNewBlend; + + private FloatBuffer textureBuffer; + private FloatBuffer textureBufferInverted; + + public SurfaceTextureLayer(int textureId) { + mTextureId = textureId; + mHaveFrame = true; + mInverted = false; + + mSurfaceTexture = new SurfaceTexture(mTextureId); + mSurfaceTexture.setOnFrameAvailableListener(this); + + mSurface = new Surface(mSurfaceTexture); + + float textureMap[] = { + 0.0f, 1.0f, // top left + 0.0f, 0.0f, // bottom left + 1.0f, 1.0f, // top right + 1.0f, 0.0f, // bottom right + }; + + textureBuffer = createBuffer(textureMap); + + float textureMapInverted[] = { + 0.0f, 0.0f, // bottom left + 0.0f, 1.0f, // top left + 1.0f, 0.0f, // bottom right + 1.0f, 1.0f, // top right + }; + + textureBufferInverted = createBuffer(textureMapInverted); + } + + public static SurfaceTextureLayer create() { + int textureId = TextureGenerator.get().take(); + if (textureId == 0) + return null; + + return new SurfaceTextureLayer(textureId); + } + + // For SurfaceTexture.OnFrameAvailableListener + public void onFrameAvailable(SurfaceTexture texture) { + // FIXME: for some reason this doesn't get called + mHaveFrame = true; + GeckoApp.mAppContext.requestRender(); + } + + private FloatBuffer createBuffer(float[] input) { + // a float has 4 bytes so we allocate for each coordinate 4 bytes + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(input.length * 4); + byteBuffer.order(ByteOrder.nativeOrder()); + + FloatBuffer floatBuffer = byteBuffer.asFloatBuffer(); + floatBuffer.put(input); + floatBuffer.position(0); + + return floatBuffer; + } + + public void update(Point origin, IntSize size, float resolution, boolean inverted, boolean blend) { + beginTransaction(null); + + setOrigin(origin); + setResolution(resolution); + + mNewSize = size; + mNewInverted = inverted; + mNewBlend = blend; + + endTransaction(); + } + + @Override + public IntSize getSize() { return mSize; } + + @Override + protected void finalize() throws Throwable { + if (mSurfaceTexture != null) + mSurfaceTexture.release(); + + if (mTextureId > 0) + TextureReaper.get().add(mTextureId); + } + + @Override + protected boolean performUpdates(GL10 gl, RenderContext context) { + super.performUpdates(gl, context); + + if (mNewSize != null) { + mSize = mNewSize; + mNewSize = null; + } + + mInverted = mNewInverted; + mBlend = mNewBlend; + + gl.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES); + gl.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId); + mSurfaceTexture.updateTexImage(); + gl.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES); + + // FIXME: we should return true and rely on onFrameAvailable, but + // that isn't working for some reason + return false; + } + + private float mapToGLCoords(float input, float viewport, boolean flip) { + if (flip) input = viewport - input; + return ((input / viewport) * 2.0f) - 1.0f; + } + + @Override + public void draw(RenderContext context) { + + // Enable GL_TEXTURE_EXTERNAL_OES and bind our texture + GLES11.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES); + GLES11.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId); + + // Enable vertex and texture coordinate buffers + GLES11.glEnableClientState(GL10.GL_VERTEX_ARRAY); + GLES11.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + // Load whatever texture transform the SurfaceMatrix needs + float[] matrix = new float[16]; + mSurfaceTexture.getTransformMatrix(matrix); + GLES11.glMatrixMode(GLES11.GL_TEXTURE); + GLES11.glLoadMatrixf(matrix, 0); + + // Figure out vertices to put the texture in the right spot on the screen + IntSize size = getSize(); + RectF bounds = getBounds(context, new FloatSize(size)); + RectF viewport = context.viewport; + bounds.offset(-viewport.left, -viewport.top); + + float vertices[] = new float[8]; + + // Bottom left + vertices[0] = mapToGLCoords(bounds.left, viewport.width(), false); + vertices[1] = mapToGLCoords(bounds.bottom, viewport.height(), true); + + // Top left + vertices[2] = mapToGLCoords(bounds.left, viewport.width(), false); + vertices[3] = mapToGLCoords(bounds.top, viewport.height(), true); + + // Bottom right + vertices[4] = mapToGLCoords(bounds.right, viewport.width(), false); + vertices[5] = mapToGLCoords(bounds.bottom, viewport.height(), true); + + // Top right + vertices[6] = mapToGLCoords(bounds.right, viewport.width(), false); + vertices[7] = mapToGLCoords(bounds.top, viewport.height(), true); + + // Set texture and vertex buffers + GLES11.glVertexPointer(2, GL10.GL_FLOAT, 0, createBuffer(vertices)); + GLES11.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mInverted ? textureBufferInverted : textureBuffer); + + if (mBlend) { + GLES11.glEnable(GL10.GL_BLEND); + GLES11.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA); + } + + // Draw the vertices as triangle strip + GLES11.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 2); + + // Clean up + GLES11.glDisableClientState(GL10.GL_VERTEX_ARRAY); + GLES11.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + GLES11.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES); + GLES11.glLoadIdentity(); + + if (mBlend) { + GLES11.glDisable(GL10.GL_BLEND); + } + } + + public SurfaceTexture getSurfaceTexture() { + return mSurfaceTexture; + } + + public Surface getSurface() { + return mSurface; + } +} + diff --git a/mobile/android/base/gfx/TextureGenerator.java b/mobile/android/base/gfx/TextureGenerator.java new file mode 100644 index 000000000000..4392c55e1ae7 --- /dev/null +++ b/mobile/android/base/gfx/TextureGenerator.java @@ -0,0 +1,73 @@ +/* -*- 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) 2009-2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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 android.opengl.GLES10; +import java.util.Stack; + +public class TextureGenerator { + private static final int MIN_TEXTURES = 5; + + private static TextureGenerator sSharedInstance; + private Stack mTextureIds; + + private TextureGenerator() { mTextureIds = new Stack(); } + + public static TextureGenerator get() { + if (sSharedInstance == null) + sSharedInstance = new TextureGenerator(); + return sSharedInstance; + } + + public synchronized int take() { + if (mTextureIds.empty()) + return 0; + + return (int)mTextureIds.pop(); + } + + public synchronized void fill() { + int[] textures = new int[1]; + while (mTextureIds.size() < MIN_TEXTURES) { + GLES10.glGenTextures(1, textures, 0); + mTextureIds.push(textures[0]); + } + } +} + + diff --git a/mobile/android/base/gfx/TextureReaper.java b/mobile/android/base/gfx/TextureReaper.java index 0527ae73811d..047406234b44 100644 --- a/mobile/android/base/gfx/TextureReaper.java +++ b/mobile/android/base/gfx/TextureReaper.java @@ -55,7 +55,11 @@ public class TextureReaper { public void add(int[] textureIDs) { for (int textureID : textureIDs) - mDeadTextureIDs.add(textureID); + add(textureID); + } + + public void add(int textureID) { + mDeadTextureIDs.add(textureID); } public void reap(GL10 gl) { diff --git a/mobile/android/base/ui/PanZoomController.java b/mobile/android/base/ui/PanZoomController.java index 505fa2a11d20..b4fc39abf753 100644 --- a/mobile/android/base/ui/PanZoomController.java +++ b/mobile/android/base/ui/PanZoomController.java @@ -293,6 +293,7 @@ public class PanZoomController return false; } cancelTouch(); + GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */); // fall through case PANNING_HOLD_LOCKED: GeckoApp.mAppContext.mAutoCompletePopup.hide(); @@ -474,6 +475,8 @@ public class PanZoomController stopAnimationTimer(); } + GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */); + mAnimationTimer = new Timer("Animation Timer"); mAnimationRunnable = runnable; mAnimationTimer.scheduleAtFixedRate(new TimerTask() { @@ -492,6 +495,8 @@ public class PanZoomController mAnimationRunnable.terminate(); mAnimationRunnable = null; } + + GeckoApp.mAppContext.showPlugins(); } private float getVelocity() { @@ -581,7 +586,6 @@ public class PanZoomController finishBounce(); finishAnimation(); mState = PanZoomState.NOTHING; - GeckoApp.mAppContext.showPluginViews(); } /* Performs one frame of a bounce animation. */ @@ -661,6 +665,7 @@ public class PanZoomController stopAnimationTimer(); // Force a viewport synchronisation + GeckoApp.mAppContext.showPlugins(); mController.setForceRedraw(); mController.notifyLayerClientOfGeometryChange(); } @@ -741,7 +746,7 @@ public class PanZoomController mState = PanZoomState.PINCHING; mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY()); - GeckoApp.mAppContext.hidePluginViews(); + GeckoApp.mAppContext.hidePlugins(false /* don't hide layers, only views */); GeckoApp.mAppContext.mAutoCompletePopup.hide(); cancelTouch(); @@ -806,9 +811,9 @@ public class PanZoomController startTouch(detector.getFocusX(), detector.getFocusY(), detector.getEventTime()); // Force a viewport synchronisation + GeckoApp.mAppContext.showPlugins(); mController.setForceRedraw(); mController.notifyLayerClientOfGeometryChange(); - GeckoApp.mAppContext.showPluginViews(); } public boolean getRedrawHint() { @@ -862,7 +867,6 @@ public class PanZoomController } private boolean animatedZoomTo(RectF zoomToRect) { - GeckoApp.mAppContext.hidePluginViews(); GeckoApp.mAppContext.mAutoCompletePopup.hide(); mState = PanZoomState.ANIMATED_ZOOM; diff --git a/other-licenses/skia-npapi/Makefile.in b/other-licenses/skia-npapi/Makefile.in index c067c426077e..ba85042ea3cc 100644 --- a/other-licenses/skia-npapi/Makefile.in +++ b/other-licenses/skia-npapi/Makefile.in @@ -56,6 +56,7 @@ LOCAL_INCLUDES += \ -I$(topsrcdir)/dom/plugins/base/android \ -I$(topsrcdir)/gfx/skia/include/core \ -I$(topsrcdir)/gfx/skia/include/config \ + -I$(topsrcdir)/gfx/gl \ $(NULL) diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 2e33871497f4..d9b1c75af395 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -1709,9 +1709,9 @@ AndroidBridge::ReleaseNativeWindow(void *window) } bool -AndroidBridge::SetNativeWindowFormat(void *window, int format) +AndroidBridge::SetNativeWindowFormat(void *window, int width, int height, int format) { - return ANativeWindow_setBuffersGeometry(window, 0, 0, format) == 0; + return ANativeWindow_setBuffersGeometry(window, width, height, format) == 0; } bool @@ -1857,3 +1857,85 @@ extern "C" { return jEnv; } } + +jobject +AndroidBridge::CreateSurface() +{ +#ifndef MOZ_JAVA_COMPOSITOR + return NULL; +#else + AutoLocalJNIFrame frame(1); + + JNIEnv* env = GetJNIForThread(); + jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); + + jmethodID method = env->GetStaticMethodID(cls, + "createSurface", + "()Landroid/view/Surface;"); + + jobject surface = env->CallStaticObjectMethod(cls, method); + if (surface) + env->NewGlobalRef(surface); + + return surface; +#endif +} + +void +AndroidBridge::DestroySurface(jobject surface) +{ +#ifdef MOZ_JAVA_COMPOSITOR + AutoLocalJNIFrame frame(1); + + JNIEnv* env = GetJNIForThread(); + jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); + + jmethodID method = env->GetStaticMethodID(cls, + "destroySurface", + "(Landroid/view/Surface;)V"); + env->CallStaticVoidMethod(cls, method, surface); + env->DeleteGlobalRef(surface); +#endif +} + +void +AndroidBridge::ShowSurface(jobject surface, const gfxRect& aRect, bool aInverted, bool aBlend) +{ +#ifdef MOZ_JAVA_COMPOSITOR + AutoLocalJNIFrame frame; + + JNIEnv* env = GetJNIForThread(); + jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); + + nsAutoString metadata; + nsCOMPtr metadataProvider = GetDrawMetadataProvider(); + metadataProvider->GetDrawMetadata(metadata); + + jstring jMetadata = env->NewString(nsPromiseFlatString(metadata).get(), metadata.Length()); + + jmethodID method = env->GetStaticMethodID(cls, + "showSurface", + "(Landroid/view/Surface;IIIIZZLjava/lang/String;)V"); + + env->CallStaticVoidMethod(cls, method, surface, + (int)aRect.x, (int)aRect.y, + (int)aRect.width, (int)aRect.height, + aInverted, aBlend, jMetadata); +#endif +} + +void +AndroidBridge::HideSurface(jobject surface) +{ +#ifdef MOZ_JAVA_COMPOSITOR + AutoLocalJNIFrame frame(1); + + JNIEnv* env = GetJNIForThread(); + jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); + + jmethodID method = env->GetStaticMethodID(cls, + "hideSurface", + "(Landroid/view/Surface;)V"); + env->CallStaticVoidMethod(cls, method, surface); +#endif +} diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 388fccf8e4f0..9d0d2f164d5b 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -54,6 +54,7 @@ #include "nsIMutableArray.h" #include "nsIMIMEInfo.h" #include "nsColor.h" +#include "gfxRect.h" #include "nsIAndroidBridge.h" @@ -341,7 +342,7 @@ public: void *AcquireNativeWindow(jobject surface); void ReleaseNativeWindow(void *window); - bool SetNativeWindowFormat(void *window, int format); + bool SetNativeWindowFormat(void *window, int width, int height, int format); bool LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride); bool UnlockWindow(void *window); @@ -378,6 +379,11 @@ public: void EnableNetworkNotifications(); void DisableNetworkNotifications(); + jobject CreateSurface(); + void DestroySurface(jobject surface); + void ShowSurface(jobject surface, const gfxRect& aRect, bool aInverted, bool aBlend); + void HideSurface(jobject surface); + protected: static AndroidBridge *sBridge; diff --git a/widget/android/AndroidMediaLayer.cpp b/widget/android/AndroidMediaLayer.cpp new file mode 100644 index 000000000000..572dadcc46b0 --- /dev/null +++ b/widget/android/AndroidMediaLayer.cpp @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 20; 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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 +#include "AndroidMediaLayer.h" +#include "AndroidBridge.h" + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AndroidMediaLayer" , ## args) + + +namespace mozilla { + +AndroidMediaLayer::AndroidMediaLayer() + : mInverted(false) { +} + +AndroidMediaLayer::~AndroidMediaLayer() { + if (mContentData.window) { + AndroidBridge::Bridge()->ReleaseNativeWindow(mContentData.window); + mContentData.window = NULL; + } + + if (mContentData.surface) { + AndroidBridge::Bridge()->DestroySurface(mContentData.surface); + mContentData.surface = NULL; + } + + std::map::iterator it; + + for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) { + SurfaceData* data = it->second; + + AndroidBridge::Bridge()->ReleaseNativeWindow(data->window); + AndroidBridge::Bridge()->DestroySurface(data->surface); + delete data; + } + + mVideoSurfaces.clear(); +} + +bool AndroidMediaLayer::EnsureContentSurface() { + if (!mContentData.surface) { + mContentData.surface = AndroidBridge::Bridge()->CreateSurface(); + if (mContentData.surface) { + mContentData.window = AndroidBridge::Bridge()->AcquireNativeWindow(mContentData.surface); + AndroidBridge::Bridge()->SetNativeWindowFormat(mContentData.window, 0, 0, AndroidBridge::WINDOW_FORMAT_RGBA_8888); + } + } + + return mContentData.surface && mContentData.window; +} + +void* AndroidMediaLayer::GetNativeWindowForContent() { + if (!EnsureContentSurface()) + return NULL; + + return mContentData.window; +} + +void* AndroidMediaLayer::RequestNativeWindowForVideo() { + jobject surface = AndroidBridge::Bridge()->CreateSurface(); + if (surface) { + void* window = AndroidBridge::Bridge()->AcquireNativeWindow(surface); + if (window) { + AndroidBridge::Bridge()->SetNativeWindowFormat(window, 0, 0, AndroidBridge::WINDOW_FORMAT_RGBA_8888); + mVideoSurfaces[window] = new SurfaceData(surface, window); + return window; + } else { + LOG("Failed to create native window from surface"); + + // Cleanup + AndroidBridge::Bridge()->DestroySurface(surface); + } + } + + return NULL; +} + +void AndroidMediaLayer::ReleaseNativeWindowForVideo(void* aWindow) { + if (mVideoSurfaces.find(aWindow) == mVideoSurfaces.end()) + return; + + SurfaceData* data = mVideoSurfaces[aWindow]; + + AndroidBridge::Bridge()->ReleaseNativeWindow(data->window); + AndroidBridge::Bridge()->DestroySurface(data->surface); + + mVideoSurfaces.erase(aWindow); + delete data; +} + +void AndroidMediaLayer::SetNativeWindowDimensions(void* aWindow, const gfxRect& aDimensions) { + if (mVideoSurfaces.find(aWindow) == mVideoSurfaces.end()) + return; + + SurfaceData* data = mVideoSurfaces[aWindow]; + data->dimensions = aDimensions; +} + +void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) { + + std::map::iterator it; + + if (EnsureContentSurface()) + AndroidBridge::Bridge()->ShowSurface(mContentData.surface, aRect, mInverted, true); + + for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) { + SurfaceData* data = it->second; + + // The video window dimension we get is not adjusted by zoom factor (unlike the + // content window). Fix it up here. + gfxRect scaledDimensions = data->dimensions; + scaledDimensions.Scale(aZoomLevel); + + gfxRect videoRect(aRect.x + scaledDimensions.x, aRect.y + scaledDimensions.y, + scaledDimensions.width, scaledDimensions.height); + AndroidBridge::Bridge()->ShowSurface(data->surface, videoRect, mInverted, false); + } +} + +} /* mozilla */ diff --git a/widget/android/AndroidMediaLayer.h b/widget/android/AndroidMediaLayer.h new file mode 100644 index 000000000000..eb402743fc17 --- /dev/null +++ b/widget/android/AndroidMediaLayer.h @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 20; 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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 AndroidMediaLayer_h_ +#define AndroidMediaLayer_h_ + +#include +#include +#include "gfxRect.h" + +namespace mozilla { + +class AndroidMediaLayer +{ +public: + + AndroidMediaLayer(); + virtual ~AndroidMediaLayer(); + + void* GetNativeWindowForContent(); + + void* RequestNativeWindowForVideo(); + void ReleaseNativeWindowForVideo(void* aWindow); + + void SetNativeWindowDimensions(void* aWindow, const gfxRect& aDimensions); + + void UpdatePosition(const gfxRect& aRect, float aZoomLevel); + + bool Inverted() { + return mInverted; + } + + void SetInverted(bool aInverted) { + mInverted = aInverted; + } + +private: + bool mInverted; + + class SurfaceData { + public: + SurfaceData() : + surface(NULL), window(NULL) { + } + + SurfaceData(jobject aSurface, void* aWindow) : + surface(aSurface), window(aWindow) { + } + + jobject surface; + void* window; + gfxRect dimensions; + }; + + bool EnsureContentSurface(); + + SurfaceData mContentData; + std::map mVideoSurfaces; +}; + +} /* mozilla */ +#endif /* AndroidMediaLayer_h_ */ diff --git a/widget/android/Makefile.in b/widget/android/Makefile.in index 7413f0e57cc9..236837b8e200 100644 --- a/widget/android/Makefile.in +++ b/widget/android/Makefile.in @@ -68,6 +68,7 @@ CPPSRCS = \ AndroidDirectTexture.cpp \ AndroidGraphicBuffer.cpp \ AndroidJNI.cpp \ + AndroidMediaLayer.cpp \ nsWindow.cpp \ nsLookAndFeel.cpp \ nsScreenManagerAndroid.cpp \ diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 57606d898733..55a9ce3b7850 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1019,7 +1019,7 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) if (surface) { sNativeWindow = AndroidBridge::Bridge()->AcquireNativeWindow(surface); if (sNativeWindow) { - AndroidBridge::Bridge()->SetNativeWindowFormat(sNativeWindow, AndroidBridge::WINDOW_FORMAT_RGB_565); + AndroidBridge::Bridge()->SetNativeWindowFormat(sNativeWindow, 0, 0, AndroidBridge::WINDOW_FORMAT_RGB_565); } } } From 7244d2b180bc3f71cd0057a2525152c40c895309 Mon Sep 17 00:00:00 2001 From: Bas Schouten Date: Wed, 1 Feb 2012 03:18:30 +0100 Subject: [PATCH 05/90] Bug 715785: Make ImageContainers independent of LayerManagers. r=roc --- .../html/content/src/nsHTMLMediaElement.cpp | 10 +- dom/plugins/base/nsPluginInstanceOwner.cpp | 14 +- dom/plugins/ipc/PluginInstanceParent.cpp | 3 - gfx/layers/ImageLayers.cpp | 275 ++++++++ gfx/layers/ImageLayers.h | 310 ++++++--- gfx/layers/Layers.cpp | 56 +- gfx/layers/Layers.h | 5 +- gfx/layers/Makefile.in | 7 +- gfx/layers/basic/BasicImages.cpp | 274 ++------ gfx/layers/basic/BasicLayers.cpp | 2 + gfx/layers/basic/BasicLayers.h | 55 +- gfx/layers/d3d10/ImageLayerD3D10.cpp | 346 +++------- gfx/layers/d3d10/ImageLayerD3D10.h | 83 +-- gfx/layers/d3d10/LayerManagerD3D10.cpp | 7 - gfx/layers/d3d10/LayerManagerD3D10.h | 2 - gfx/layers/d3d9/ImageLayerD3D9.cpp | 591 +++++++----------- gfx/layers/d3d9/ImageLayerD3D9.h | 108 +--- gfx/layers/d3d9/LayerManagerD3D9.cpp | 7 - gfx/layers/d3d9/LayerManagerD3D9.h | 2 - gfx/layers/opengl/ImageLayerOGL.cpp | 481 +++++--------- gfx/layers/opengl/ImageLayerOGL.h | 125 +--- gfx/layers/opengl/LayerManagerOGL.cpp | 41 -- gfx/layers/opengl/LayerManagerOGL.h | 17 - gfx/layers/opengl/MacIOSurfaceImageOGL.h | 72 --- gfx/layers/opengl/MacIOSurfaceImageOGL.mm | 120 ---- gfx/thebes/Makefile.in | 1 + gfx/thebes/gfxASurface.h | 55 ++ gfx/thebes/gfxBlur.cpp | 14 +- gfx/thebes/nsCoreAnimationSupport.h | 30 +- gfx/thebes/nsCoreAnimationSupport.mm | 36 +- gfx/thebes/nsIOSurface.h | 79 +++ image/public/imgIContainer.idl | 6 +- image/src/RasterImage.cpp | 11 +- image/src/RasterImage.h | 2 +- image/src/VectorImage.h | 2 +- layout/base/FrameLayerBuilder.cpp | 8 +- layout/generic/nsImageFrame.cpp | 4 +- layout/generic/nsImageFrame.h | 2 +- layout/generic/nsObjectFrame.cpp | 46 +- layout/generic/nsObjectFrame.h | 2 +- layout/generic/nsVideoFrame.cpp | 54 +- 41 files changed, 1268 insertions(+), 2097 deletions(-) create mode 100644 gfx/layers/ImageLayers.cpp delete mode 100644 gfx/layers/opengl/MacIOSurfaceImageOGL.h delete mode 100644 gfx/layers/opengl/MacIOSurfaceImageOGL.mm create mode 100644 gfx/thebes/nsIOSurface.h diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp index b08e178cf4df..8346af12d2d6 100644 --- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -2490,15 +2490,7 @@ ImageContainer* nsHTMLMediaElement::GetImageContainer() if (!video) return nsnull; - nsRefPtr manager = - nsContentUtils::PersistentLayerManagerForDocument(OwnerDoc()); - if (!manager) - return nsnull; - - mImageContainer = manager->CreateImageContainer(); - if (manager->IsCompositingCheap()) { - mImageContainer->SetDelayedConversion(true); - } + mImageContainer = LayerManager::CreateImageContainer(); return mImageContainer; } diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 1edfc55e9f27..5247a46d7667 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -256,20 +256,12 @@ nsPluginInstanceOwner::EndUpdateBackground(gfxContext* aContext, bool nsPluginInstanceOwner::UseAsyncRendering() { -#ifdef XP_MACOSX - nsRefPtr container = mObjectFrame->GetImageContainer(); -#endif - bool useAsyncRendering; bool result = (mInstance && NS_SUCCEEDED(mInstance->UseAsyncPainting(&useAsyncRendering)) && - useAsyncRendering && -#ifdef XP_MACOSX - container && - container->GetBackendType() == - LayerManager::LAYERS_OPENGL -#else - (!mPluginWindow || + useAsyncRendering +#ifndef XP_MACOSX + && (!mPluginWindow || mPluginWindow->type == NPWindowTypeDrawable) #endif ); diff --git a/dom/plugins/ipc/PluginInstanceParent.cpp b/dom/plugins/ipc/PluginInstanceParent.cpp index a93b17b352e2..876c5742dbc0 100644 --- a/dom/plugins/ipc/PluginInstanceParent.cpp +++ b/dom/plugins/ipc/PluginInstanceParent.cpp @@ -647,9 +647,6 @@ PluginInstanceParent::GetImage(ImageContainer* aContainer, Image** aImage) #ifdef XP_MACOSX if (ioSurface) { format = Image::MAC_IO_SURFACE; - if (!aContainer->Manager()) { - return NS_ERROR_FAILURE; - } } #endif diff --git a/gfx/layers/ImageLayers.cpp b/gfx/layers/ImageLayers.cpp new file mode 100644 index 000000000000..d55f757a7116 --- /dev/null +++ b/gfx/layers/ImageLayers.cpp @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 20; 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Bas Schouten + * + * 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 "mozilla/ipc/Shmem.h" +#include "ImageLayers.h" +#include "gfxImageSurface.h" +#include "yuv_convert.h" + +#ifdef XP_MACOSX +#include "nsCoreAnimationSupport.h" +#endif + +using namespace mozilla::ipc; + +namespace mozilla { +namespace layers { + +already_AddRefed +ImageFactory::CreateImage(const Image::Format *aFormats, + PRUint32 aNumFormats, + const gfxIntSize &, + BufferRecycleBin *aRecycleBin) +{ + if (!aNumFormats) { + return nsnull; + } + nsRefPtr img; + if (FormatInList(aFormats, aNumFormats, Image::PLANAR_YCBCR)) { + img = new PlanarYCbCrImage(aRecycleBin); + } else if (FormatInList(aFormats, aNumFormats, Image::CAIRO_SURFACE)) { + img = new CairoImage(); +#ifdef XP_MACOSX + } else if (FormatInList(aFormats, aNumFormats, Image::MAC_IO_SURFACE)) { + img = new MacIOSurfaceImage(); +#endif + } + return img.forget(); +} + +BufferRecycleBin::BufferRecycleBin() + : mLock("mozilla.layers.BufferRecycleBin.mLock") +{ +} + +void +BufferRecycleBin::RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize) +{ + MutexAutoLock lock(mLock); + + if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) { + mRecycledBuffers.Clear(); + } + mRecycledBufferSize = aSize; + mRecycledBuffers.AppendElement(aBuffer); +} + +PRUint8* +BufferRecycleBin::GetBuffer(PRUint32 aSize) +{ + MutexAutoLock lock(mLock); + + if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize) + return new PRUint8[aSize]; + + PRUint32 last = mRecycledBuffers.Length() - 1; + PRUint8* result = mRecycledBuffers[last].forget(); + mRecycledBuffers.RemoveElementAt(last); + return result; +} + +ImageContainer::~ImageContainer() +{ +} + +already_AddRefed +ImageContainer::CreateImage(const Image::Format *aFormats, + PRUint32 aNumFormats) +{ + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + return mImageFactory->CreateImage(aFormats, aNumFormats, mScaleHint, mRecycleBin); +} + +void +ImageContainer::SetCurrentImage(Image *aImage) +{ + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + + mActiveImage = aImage; + CurrentImageChanged(); +} + +already_AddRefed +ImageContainer::GetCurrentAsSurface(gfxIntSize *aSize) +{ + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + + if (!mActiveImage) { + return nsnull; + } + + *aSize = mActiveImage->GetSize(); + return mActiveImage->GetAsSurface(); +} + +gfxIntSize +ImageContainer::GetCurrentSize() +{ + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + + if (!mActiveImage) { + return gfxIntSize(0,0); + } + + return mActiveImage->GetSize(); +} + +PlanarYCbCrImage::PlanarYCbCrImage(BufferRecycleBin *aRecycleBin) + : Image(nsnull, PLANAR_YCBCR) + , mBufferSize(0) + , mRecycleBin(aRecycleBin) +{ +} + +PlanarYCbCrImage::~PlanarYCbCrImage() +{ + if (mBuffer) { + mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize); + } +} + +PRUint8* +PlanarYCbCrImage::AllocateBuffer(PRUint32 aSize) +{ + return mRecycleBin->GetBuffer(aSize); +} + +void +PlanarYCbCrImage::CopyData(const Data& aData) +{ + mData = aData; + + mData.mYStride = mData.mYSize.width; + mData.mCbCrStride = mData.mCbCrSize.width; + + // update buffer size + mBufferSize = mData.mCbCrStride * mData.mCbCrSize.height * 2 + + mData.mYStride * mData.mYSize.height; + + // get new buffer + mBuffer = AllocateBuffer(mBufferSize); + if (!mBuffer) + return; + + mData.mYChannel = mBuffer; + mData.mCbChannel = mData.mYChannel + mData.mYStride * mData.mYSize.height; + mData.mCrChannel = mData.mCbChannel + mData.mCbCrStride * mData.mCbCrSize.height; + + for (int i = 0; i < mData.mYSize.height; i++) { + memcpy(mData.mYChannel + i * mData.mYStride, + aData.mYChannel + i * aData.mYStride, + mData.mYStride); + } + for (int i = 0; i < mData.mCbCrSize.height; i++) { + memcpy(mData.mCbChannel + i * mData.mCbCrStride, + aData.mCbChannel + i * aData.mCbCrStride, + mData.mCbCrStride); + memcpy(mData.mCrChannel + i * mData.mCbCrStride, + aData.mCrChannel + i * aData.mCbCrStride, + mData.mCbCrStride); + } + + mSize = aData.mPicSize; +} + +void +PlanarYCbCrImage::SetData(const Data &aData) +{ + CopyData(aData); +} + +already_AddRefed +PlanarYCbCrImage::GetAsSurface() +{ + if (mSurface) { + nsRefPtr result = mSurface.get(); + return result.forget(); + } + + nsRefPtr imageSurface = + new gfxImageSurface(mSize, gfxASurface::ImageFormatRGB24); + + gfx::YUVType type = + gfx::TypeFromSize(mData.mYSize.width, + mData.mYSize.height, + mData.mCbCrSize.width, + mData.mCbCrSize.height); + + // Convert from YCbCr to RGB now + gfx::ConvertYCbCrToRGB32(mData.mYChannel, + mData.mCbChannel, + mData.mCrChannel, + imageSurface->Data(), + mData.mPicX, + mData.mPicY, + mData.mPicSize.width, + mData.mPicSize.height, + mData.mYStride, + mData.mCbCrStride, + imageSurface->Stride(), + type); + + mSurface = imageSurface; + + return imageSurface.forget().get(); +} + +#ifdef XP_MACOSX +void +MacIOSurfaceImage::SetData(const Data& aData) +{ + mIOSurface = nsIOSurface::LookupSurface(aData.mIOSurface->GetIOSurfaceID()); + mSize = gfxIntSize(mIOSurface->GetWidth(), mIOSurface->GetHeight()); +} + +already_AddRefed +MacIOSurfaceImage::GetAsSurface() +{ + return mIOSurface->GetAsSurface(); +} + +void +MacIOSurfaceImage::Update(ImageContainer* aContainer) +{ + if (mUpdateCallback) { + mUpdateCallback(aContainer, mPluginInstanceOwner); + } +} +#endif + +} +} diff --git a/gfx/layers/ImageLayers.h b/gfx/layers/ImageLayers.h index 3aef7527b521..5ff7109d94e6 100644 --- a/gfx/layers/ImageLayers.h +++ b/gfx/layers/ImageLayers.h @@ -46,8 +46,12 @@ #include "mozilla/ReentrantMonitor.h" #include "mozilla/TimeStamp.h" #include "mozilla/mozalloc.h" +#include "mozilla/Mutex.h" +#include "gfxPlatform.h" -class nsIOSurface; +#ifdef XP_MACOSX +#include "nsIOSurface.h" +#endif namespace mozilla { namespace layers { @@ -60,6 +64,14 @@ enum StereoMode { STEREO_MODE_TOP_BOTTOM }; +struct ImageBackendData +{ + virtual ~ImageBackendData() {} + +protected: + ImageBackendData() {} +}; + /** * A class representing a buffer of pixel data. The data can be in one * of various formats including YCbCr. @@ -104,8 +116,7 @@ public: CAIRO_SURFACE, /** - * The MAC_IO_SURFACE format creates a MacIOSurfaceImage. This - * is only supported on Mac with OpenGL layers. + * The MAC_IO_SURFACE format creates a MacIOSurfaceImage. * * It wraps an IOSurface object and binds it directly to a GL texture. */ @@ -115,16 +126,106 @@ public: Format GetFormat() { return mFormat; } void* GetImplData() { return mImplData; } + virtual already_AddRefed GetAsSurface() = 0; + virtual gfxIntSize GetSize() = 0; + + ImageBackendData* GetBackendData(LayerManager::LayersBackend aBackend) + { return mBackendData[aBackend]; } + void SetBackendData(LayerManager::LayersBackend aBackend, ImageBackendData* aData) + { mBackendData[aBackend] = aData; } + protected: Image(void* aImplData, Format aFormat) : mImplData(aImplData), mFormat(aFormat) {} + nsAutoPtr mBackendData[LayerManager::LAYERS_LAST]; + void* mImplData; Format mFormat; }; +/** + * A RecycleBin is owned by an ImageContainer. We store buffers in it that we + * want to recycle from one image to the next.It's a separate object from + * ImageContainer because images need to store a strong ref to their RecycleBin + * and we must avoid creating a reference loop between an ImageContainer and + * its active image. + */ +class BufferRecycleBin { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecycleBin) + + typedef mozilla::gl::GLContext GLContext; + +public: + BufferRecycleBin(); + + void RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize); + // Returns a recycled buffer of the right size, or allocates a new buffer. + PRUint8* GetBuffer(PRUint32 aSize); + +private: + typedef mozilla::Mutex Mutex; + + // This protects mRecycledBuffers, mRecycledBufferSize, mRecycledTextures + // and mRecycledTextureSizes + Mutex mLock; + + // We should probably do something to prune this list on a timer so we don't + // eat excess memory while video is paused... + nsTArray > mRecycledBuffers; + // This is only valid if mRecycledBuffers is non-empty + PRUint32 mRecycledBufferSize; +}; + +/** + * Returns true if aFormat is in the given format array. + */ +static inline bool +FormatInList(const Image::Format* aFormats, PRUint32 aNumFormats, + Image::Format aFormat) +{ + for (PRUint32 i = 0; i < aNumFormats; ++i) { + if (aFormats[i] == aFormat) { + return true; + } + } + return false; +} + +/** + * A class that manages Image creation for a LayerManager. The only reason + * we need a separate class here is that LayerMananers aren't threadsafe + * (because layers can only be used on the main thread) and we want to + * be able to create images from any thread, to facilitate video playback + * without involving the main thread, for example. + * Different layer managers can implement child classes of this making it + * possible to create layer manager specific images. + * This class is not meant to be used directly but rather can be set on an + * image container. This is usually done by the layer system internally and + * not explicitly by users. For PlanarYCbCr or Cairo images the default + * implementation will creates images whose data lives in system memory, for + * MacIOSurfaces the default implementation will be a simple nsIOSurface + * wrapper. + */ + +class THEBES_API ImageFactory +{ + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ImageFactory) +protected: + friend class ImageContainer; + + ImageFactory() {} + virtual ~ImageFactory() {} + + virtual already_AddRefed CreateImage(const Image::Format* aFormats, + PRUint32 aNumFormats, + const gfxIntSize &aScaleHint, + BufferRecycleBin *aRecycleBin); + +}; + /** * A class that manages Images for an ImageLayer. The only reason * we need a separate class here is that ImageLayers aren't threadsafe @@ -139,10 +240,12 @@ public: ImageContainer() : mReentrantMonitor("ImageContainer.mReentrantMonitor"), mPaintCount(0), - mPreviousImagePainted(false) + mPreviousImagePainted(false), + mImageFactory(new ImageFactory()), + mRecycleBin(new BufferRecycleBin()) {} - virtual ~ImageContainer() {} + ~ImageContainer(); /** * Create an Image in one of the given formats. @@ -152,8 +255,8 @@ public: * Can be called on any thread. This method takes mReentrantMonitor * when accessing thread-shared state. */ - virtual already_AddRefed CreateImage(const Image::Format* aFormats, - PRUint32 aNumFormats) = 0; + already_AddRefed CreateImage(const Image::Format* aFormats, + PRUint32 aNumFormats); /** * Set an Image as the current image to display. The Image must have @@ -163,13 +266,7 @@ public: * * The Image data must not be modified after this method is called! */ - virtual void SetCurrentImage(Image* aImage) = 0; - - /** - * Ask any PlanarYCbCr images created by this container to delay - * YUV -> RGB conversion until draw time. See PlanarYCbCrImage::SetDelayedConversion. - */ - virtual void SetDelayedConversion(bool aDelayed) {} + void SetCurrentImage(Image* aImage); /** * Get the current Image. @@ -181,7 +278,13 @@ public: * Implementations must call CurrentImageChanged() while holding * mReentrantMonitor. */ - virtual already_AddRefed GetCurrentImage() = 0; + already_AddRefed GetCurrentImage() + { + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + + nsRefPtr retval = mActiveImage; + return retval.forget(); + } /** * Get the current image as a gfxASurface. This is useful for fallback @@ -198,32 +301,14 @@ public: * Can be called on any thread. This method takes mReentrantMonitor * when accessing thread-shared state. */ - virtual already_AddRefed GetCurrentAsSurface(gfxIntSize* aSizeResult) = 0; - - /** - * Returns the layer manager for this container. This can only - * be used on the main thread, since layer managers should only be - * accessed on the main thread. - */ - LayerManager* Manager() - { - NS_PRECONDITION(NS_IsMainThread(), "Must be called on main thread"); - return mManager; - } + already_AddRefed GetCurrentAsSurface(gfxIntSize* aSizeResult); /** * Returns the size of the image in pixels. * Can be called on any thread. This method takes mReentrantMonitor when accessing * thread-shared state. */ - virtual gfxIntSize GetCurrentSize() = 0; - - /** - * Set a new layer manager for this image container. It must be - * either of the same type as the container's current layer manager, - * or null. TRUE is returned on success. Main thread only. - */ - virtual bool SetLayerManager(LayerManager *aManager) = 0; + gfxIntSize GetCurrentSize(); /** * Sets a size that the image is expected to be rendered at. @@ -232,14 +317,14 @@ public: * Can be called on any thread. This method takes mReentrantMonitor * when accessing thread-shared state. */ - virtual void SetScaleHint(const gfxIntSize& /* aScaleHint */) { } + void SetScaleHint(const gfxIntSize& aScaleHint) + { mScaleHint = aScaleHint; } - /** - * Get the layer manager type this image container was created with, - * presumably its users might want to do something special if types do not - * match. Can be called on any thread. - */ - virtual LayerManager::LayersBackend GetBackendType() = 0; + void SetImageFactory(ImageFactory *aFactory) + { + ReentrantMonitorAutoEnter mon(mReentrantMonitor); + mImageFactory = aFactory ? aFactory : new ImageFactory(); + } /** * Returns the time at which the currently contained image was first @@ -285,19 +370,11 @@ public: protected: typedef mozilla::ReentrantMonitor ReentrantMonitor; - LayerManager* mManager; // ReentrantMonitor to protect thread safe access to the "current // image", and any other state which is shared between threads. ReentrantMonitor mReentrantMonitor; - ImageContainer(LayerManager* aManager) : - mManager(aManager), - mReentrantMonitor("ImageContainer.mReentrantMonitor"), - mPaintCount(0), - mPreviousImagePainted(false) - {} - // Performs necessary housekeeping to ensure the painted frame statistics // are accurate. Must be called by SetCurrentImage() implementations with // mReentrantMonitor held. @@ -307,6 +384,8 @@ protected: mPaintTime = TimeStamp(); } + nsRefPtr mActiveImage; + // Number of contained images that have been painted at least once. It's up // to the ImageContainer implementation to ensure accesses to this are // threadsafe. @@ -318,6 +397,15 @@ protected: // Denotes whether the previous image was painted. bool mPreviousImagePainted; + + // This is the image factory used by this container, layer managers using + // this container can set an alternative image factory that will be used to + // create images for this container. + nsRefPtr mImageFactory; + + gfxIntSize mScaleHint; + + nsRefPtr mRecycleBin; }; /** @@ -332,8 +420,6 @@ public: */ void SetContainer(ImageContainer* aContainer) { - NS_ASSERTION(!aContainer->Manager() || aContainer->Manager() == Manager(), - "ImageContainer must have the same manager as the ImageLayer"); mContainer = aContainer; } /** @@ -422,13 +508,13 @@ public: MAX_DIMENSION = 16384 }; + ~PlanarYCbCrImage(); + /** - * This makes a copy of the data buffers. - * XXX Eventually we will change this to not make a copy of the data, - * Right now it doesn't matter because the BasicLayer implementation - * does YCbCr conversion here anyway. + * This makes a copy of the data buffers, in order to support functioning + * in all different layer managers. */ - virtual void SetData(const Data& aData) = 0; + virtual void SetData(const Data& aData); /** * Ask this Image to not convert YUV to RGB during SetData, and make @@ -440,19 +526,14 @@ public: /** * Grab the original YUV data. This is optional. */ - virtual const Data* GetData() { return nsnull; } + virtual const Data* GetData() { return &mData; } /** - * Make a copy of the YCbCr data. + * Make a copy of the YCbCr data into local storage. * - * @param aDest Data object to store the plane data in. - * @param aDestSize Size of the Y plane that was copied. - * @param aDestBufferSize Number of bytes allocated for storage. * @param aData Input image data. - * @return Raw data pointer for the planes or nsnull on failure. */ - PRUint8 *CopyData(Data& aDest, gfxIntSize& aDestSize, - PRUint32& aDestBufferSize, const Data& aData); + void CopyData(const Data& aData); /** * Return a buffer to store image data in. @@ -464,15 +545,31 @@ public: /** * Return the number of bytes of heap memory used to store this image. */ - virtual PRUint32 GetDataSize() = 0; + virtual PRUint32 GetDataSize() { return mBufferSize; } -protected: - PlanarYCbCrImage(void* aImplData) : Image(aImplData, PLANAR_YCBCR) {} + already_AddRefed GetAsSurface(); + + virtual gfxIntSize GetSize() { return mSize; } + + void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; } + gfxImageFormat GetOffscreenFormat() { return mOffscreenFormat; } + + // XXX - not easy to protect these sadly. + nsAutoArrayPtr mBuffer; + PRUint32 mBufferSize; + Data mData; + gfxIntSize mSize; + gfxImageFormat mOffscreenFormat; + nsCountedRef mSurface; + nsRefPtr mRecycleBin; + + PlanarYCbCrImage(BufferRecycleBin *aRecycleBin); }; /** * Currently, the data in a CairoImage surface is treated as being in the - * device output color space. + * device output color space. This class is very simple as all backends + * have to know about how to deal with drawing a cairo image. */ class THEBES_API CairoImage : public Image { public: @@ -486,10 +583,26 @@ public: * to the surface (which will eventually be released on the main thread). * The surface must not be modified after this call!!! */ - virtual void SetData(const Data& aData) = 0; + void SetData(const Data& aData) + { + mSurface = aData.mSurface; + mSize = aData.mSize; + } -protected: - CairoImage(void* aImplData) : Image(aImplData, CAIRO_SURFACE) {} + + virtual already_AddRefed GetAsSurface() + { + NS_ASSERTION(NS_IsMainThread(), "Must be main thread"); + nsRefPtr surface = mSurface.get(); + return surface.forget(); + } + + gfxIntSize GetSize() { return mSize; } + + CairoImage() : Image(NULL, CAIRO_SURFACE) {} + + nsCountedRef mSurface; + gfxIntSize mSize; }; #ifdef XP_MACOSX @@ -499,12 +612,27 @@ public: nsIOSurface* mIOSurface; }; + MacIOSurfaceImage() + : Image(NULL, MAC_IO_SURFACE) + , mSize(0, 0) + , mPluginInstanceOwner(NULL) + , mUpdateCallback(NULL) + , mDestroyCallback(NULL) + {} + + virtual ~MacIOSurfaceImage() + { + if (mDestroyCallback) { + mDestroyCallback(mPluginInstanceOwner); + } + } + /** * This can only be called on the main thread. It may add a reference * to the surface (which will eventually be released on the main thread). * The surface must not be modified after this call!!! */ - virtual void SetData(const Data& aData) = 0; + virtual void SetData(const Data& aData); /** * Temporary hacks to force plugin drawing during an empty transaction. @@ -512,12 +640,38 @@ public: * when async plugin rendering is complete. */ typedef void (*UpdateSurfaceCallback)(ImageContainer* aContainer, void* aInstanceOwner); - virtual void SetUpdateCallback(UpdateSurfaceCallback aCallback, void* aInstanceOwner) = 0; - typedef void (*DestroyCallback)(void* aInstanceOwner); - virtual void SetDestroyCallback(DestroyCallback aCallback) = 0; + virtual void SetUpdateCallback(UpdateSurfaceCallback aCallback, void* aInstanceOwner) + { + mUpdateCallback = aCallback; + mPluginInstanceOwner = aInstanceOwner; + } -protected: - MacIOSurfaceImage(void* aImplData) : Image(aImplData, MAC_IO_SURFACE) {} + typedef void (*DestroyCallback)(void* aInstanceOwner); + virtual void SetDestroyCallback(DestroyCallback aCallback) + { + mDestroyCallback = aCallback; + } + + virtual gfxIntSize GetSize() + { + return mSize; + } + + nsIOSurface* GetIOSurface() + { + return mIOSurface; + } + + void Update(ImageContainer* aContainer); + + virtual already_AddRefed GetAsSurface(); + +private: + gfxIntSize mSize; + nsRefPtr mIOSurface; + void* mPluginInstanceOwner; + UpdateSurfaceCallback mUpdateCallback; + DestroyCallback mDestroyCallback; }; #endif diff --git a/gfx/layers/Layers.cpp b/gfx/layers/Layers.cpp index 913b18ff563a..58e24b4330f1 100644 --- a/gfx/layers/Layers.cpp +++ b/gfx/layers/Layers.cpp @@ -229,6 +229,13 @@ LayerManager::Mutated(Layer* aLayer) } #endif // DEBUG +already_AddRefed +LayerManager::CreateImageContainer() +{ + nsRefPtr container = new ImageContainer(); + return container.forget(); +} + //-------------------------------------------------- // Layer @@ -513,54 +520,7 @@ ContainerLayer::DidInsertChild(Layer* aLayer) mMayHaveReadbackChild = true; } } - -PRUint8* -PlanarYCbCrImage::AllocateBuffer(PRUint32 aSize) -{ - const fallible_t fallible = fallible_t(); - return new (fallible) PRUint8[aSize]; -} - -PRUint8* -PlanarYCbCrImage::CopyData(Data& aDest, gfxIntSize& aDestSize, - PRUint32& aDestBufferSize, const Data& aData) -{ - aDest = aData; - - aDest.mYStride = aDest.mYSize.width; - aDest.mCbCrStride = aDest.mCbCrSize.width; - - // update buffer size - aDestBufferSize = aDest.mCbCrStride * aDest.mCbCrSize.height * 2 + - aDest.mYStride * aDest.mYSize.height; - - // get new buffer - PRUint8* buffer = AllocateBuffer(aDestBufferSize); - if (!buffer) - return nsnull; - - aDest.mYChannel = buffer; - aDest.mCbChannel = aDest.mYChannel + aDest.mYStride * aDest.mYSize.height; - aDest.mCrChannel = aDest.mCbChannel + aDest.mCbCrStride * aDest.mCbCrSize.height; - - for (int i = 0; i < aDest.mYSize.height; i++) { - memcpy(aDest.mYChannel + i * aDest.mYStride, - aData.mYChannel + i * aData.mYStride, - aDest.mYStride); - } - for (int i = 0; i < aDest.mCbCrSize.height; i++) { - memcpy(aDest.mCbChannel + i * aDest.mCbCrStride, - aData.mCbChannel + i * aData.mCbCrStride, - aDest.mCbCrStride); - memcpy(aDest.mCrChannel + i * aDest.mCbCrStride, - aData.mCrChannel + i * aData.mCbCrStride, - aDest.mCbCrStride); - } - - aDestSize = aData.mPicSize; - return buffer; -} - + void LayerManager::StartFrameTimeRecording() { diff --git a/gfx/layers/Layers.h b/gfx/layers/Layers.h index 08e28851466b..299bb51b1514 100644 --- a/gfx/layers/Layers.h +++ b/gfx/layers/Layers.h @@ -49,6 +49,7 @@ #include "gfxColor.h" #include "gfxPattern.h" #include "nsTArray.h" +#include "nsThreadUtils.h" #include "mozilla/gfx/2D.h" #include "mozilla/TimeStamp.h" @@ -428,9 +429,9 @@ public: virtual already_AddRefed CreateReadbackLayer() { return nsnull; } /** - * Can be called anytime + * Can be called anytime, from any thread. */ - virtual already_AddRefed CreateImageContainer() = 0; + static already_AddRefed CreateImageContainer(); /** * Type of layer manager his is. This is to be used sparsely in order to diff --git a/gfx/layers/Makefile.in b/gfx/layers/Makefile.in index 497ac26a6a73..fdd80f656d8c 100644 --- a/gfx/layers/Makefile.in +++ b/gfx/layers/Makefile.in @@ -82,14 +82,9 @@ CPPSRCS = \ LayerManagerOGL.cpp \ ThebesLayerOGL.cpp \ LayerSorter.cpp \ + ImageLayers.cpp \ $(NULL) -ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa) -CMMSRCS = \ - MacIOSurfaceImageOGL.mm \ - $(NULL) -endif - ifeq ($(MOZ_WIDGET_TOOLKIT),windows) ifdef MOZ_ENABLE_D3D9_LAYER EXPORTS += \ diff --git a/gfx/layers/basic/BasicImages.cpp b/gfx/layers/basic/BasicImages.cpp index d0d2c56a6abb..5fd5942c9c08 100644 --- a/gfx/layers/basic/BasicImages.cpp +++ b/gfx/layers/basic/BasicImages.cpp @@ -56,106 +56,70 @@ using mozilla::ReentrantMonitor; namespace mozilla { namespace layers { -/** - * All our images can yield up a cairo surface and their size. - */ -class BasicImageImplData { +class BasicPlanarYCbCrImage : public PlanarYCbCrImage +{ public: - /** - * This must be called on the main thread. - */ - virtual already_AddRefed GetAsSurface() = 0; + BasicPlanarYCbCrImage(const gfxIntSize& aScaleHint, gfxImageFormat aOffscreenFormat, BufferRecycleBin *aRecycleBin) + : PlanarYCbCrImage(aRecycleBin) + , mScaleHint(aScaleHint) + , mOffscreenFormat(aOffscreenFormat) + {} - gfxIntSize GetSize() { return mSize; } - -protected: - gfxIntSize mSize; -}; - -/** - * Since BasicLayers only paint on the main thread, handling a CairoImage - * is extremely simple. We just hang on to a reference to the surface and - * return that surface when BasicImageLayer::Paint asks for it via - * BasicImageContainer::GetAsSurface. - */ -class BasicCairoImage : public CairoImage, public BasicImageImplData { -public: - BasicCairoImage() : CairoImage(static_cast(this)) {} - - virtual void SetData(const Data& aData) + ~BasicPlanarYCbCrImage() { - mSurface = aData.mSurface; - mSize = aData.mSize; + if (mDecodedBuffer) { + // Right now this only happens if the Image was never drawn, otherwise + // this will have been tossed away at surface destruction. + mRecycleBin->RecycleBuffer(mDecodedBuffer.forget(), mSize.height * mStride); + } } - virtual already_AddRefed GetAsSurface() - { - NS_ASSERTION(NS_IsMainThread(), "Must be main thread"); - nsRefPtr surface = mSurface.get(); - return surface.forget(); - } - -protected: - nsCountedRef mSurface; -}; - -/** - * We handle YCbCr by converting to RGB when the image is initialized - * (which should be done off the main thread). The RGB results are stored - * in a memory buffer and converted to a cairo surface lazily. - */ -class BasicPlanarYCbCrImage : public PlanarYCbCrImage, public BasicImageImplData { - typedef gfxASurface::gfxImageFormat gfxImageFormat; -public: - /** - * aScaleHint is a size that the image is expected to be rendered at. - * This is a hint for image backends to optimize scaling. - */ - BasicPlanarYCbCrImage(const gfxIntSize& aScaleHint) : - PlanarYCbCrImage(static_cast(this)), - mScaleHint(aScaleHint), - mOffscreenFormat(gfxASurface::ImageFormatUnknown), - mDelayedConversion(false) - {} - virtual void SetData(const Data& aData); - virtual void SetDelayedConversion(bool aDelayed) { mDelayedConversion = aDelayed; } + already_AddRefed GetAsSurface(); - virtual already_AddRefed GetAsSurface(); +private: + gfxIntSize mScaleHint; + gfxImageFormat mOffscreenFormat; + int mStride; + nsAutoArrayPtr mDecodedBuffer; +}; - const Data* GetData() { return &mData; } +class BasicImageFactory : public ImageFactory +{ +public: + BasicImageFactory() {} - void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; } - gfxImageFormat GetOffscreenFormat() { return mOffscreenFormat; } + virtual already_AddRefed CreateImage(const Image::Format* aFormats, + PRUint32 aNumFormats, + const gfxIntSize &aScaleHint, + BufferRecycleBin *aRecycleBin) + { + if (!aNumFormats) { + return nsnull; + } - PRUint32 GetDataSize() { return mBuffer ? mDelayedConversion ? mBufferSize : mSize.height * mStride : 0; } + nsRefPtr image; + if (aFormats[0] == Image::PLANAR_YCBCR) { + image = new BasicPlanarYCbCrImage(aScaleHint, gfxPlatform::GetPlatform()->GetOffscreenFormat(), aRecycleBin); + return image.forget(); + } -protected: - nsAutoArrayPtr mBuffer; - nsCountedRef mSurface; - gfxIntSize mScaleHint; - PRInt32 mStride; - gfxImageFormat mOffscreenFormat; - Data mData; - PRUint32 mBufferSize; - bool mDelayedConversion; + return ImageFactory::CreateImage(aFormats, aNumFormats, aScaleHint, aRecycleBin); + } }; void BasicPlanarYCbCrImage::SetData(const Data& aData) { + PlanarYCbCrImage::SetData(aData); + // Do some sanity checks to prevent integer overflow if (aData.mYSize.width > PlanarYCbCrImage::MAX_DIMENSION || aData.mYSize.height > PlanarYCbCrImage::MAX_DIMENSION) { NS_ERROR("Illegal image source width or height"); return; } - - if (mDelayedConversion) { - mBuffer = CopyData(mData, mSize, mBufferSize, aData); - return; - } - + gfxASurface::gfxImageFormat format = GetOffscreenFormat(); gfxIntSize size(mScaleHint); @@ -167,13 +131,13 @@ BasicPlanarYCbCrImage::SetData(const Data& aData) } mStride = gfxASurface::FormatStrideForWidth(format, size.width); - mBuffer = AllocateBuffer(size.height * mStride); - if (!mBuffer) { + mDecodedBuffer = AllocateBuffer(size.height * mStride); + if (!mDecodedBuffer) { // out of memory return; } - gfxUtils::ConvertYCbCrToRGB(aData, format, size, mBuffer, mStride); + gfxUtils::ConvertYCbCrToRGB(aData, format, size, mDecodedBuffer, mStride); SetOffscreenFormat(format); mSize = size; } @@ -196,22 +160,20 @@ BasicPlanarYCbCrImage::GetAsSurface() return result.forget(); } - // XXX: If we forced delayed conversion, are we ever going to hit this? - // We may need to implement the conversion here. - if (!mBuffer || mDelayedConversion) { - return nsnull; + if (!mDecodedBuffer) { + return PlanarYCbCrImage::GetAsSurface(); } gfxASurface::gfxImageFormat format = GetOffscreenFormat(); nsRefPtr imgSurface = - new gfxImageSurface(mBuffer, mSize, mStride, format); + new gfxImageSurface(mDecodedBuffer, mSize, mStride, format); if (!imgSurface || imgSurface->CairoStatus() != 0) { return nsnull; } // Pass ownership of the buffer to the surface - imgSurface->SetData(&imageSurfaceDataKey, mBuffer.forget(), DestroyBuffer); + imgSurface->SetData(&imageSurfaceDataKey, mDecodedBuffer.forget(), DestroyBuffer); nsRefPtr result = imgSurface.get(); #if defined(XP_MACOSX) @@ -221,144 +183,20 @@ BasicPlanarYCbCrImage::GetAsSurface() result = quartzSurface.forget(); } #endif - mSurface = result.get(); + + mSurface = result; return result.forget(); } -/** - * Our image container is very simple. It's really just a factory - * for the image objects. We use a ReentrantMonitor to synchronize access to - * mImage. - */ -class BasicImageContainer : public ImageContainer { -public: - typedef gfxASurface::gfxImageFormat gfxImageFormat; - - BasicImageContainer() : - ImageContainer(nsnull), - mScaleHint(-1, -1), - mOffscreenFormat(gfxASurface::ImageFormatUnknown), - mDelayed(false) - {} - virtual already_AddRefed CreateImage(const Image::Format* aFormats, - PRUint32 aNumFormats); - virtual void SetDelayedConversion(bool aDelayed) { mDelayed = aDelayed; } - virtual void SetCurrentImage(Image* aImage); - virtual already_AddRefed GetCurrentImage(); - virtual already_AddRefed GetCurrentAsSurface(gfxIntSize* aSize); - virtual gfxIntSize GetCurrentSize(); - virtual bool SetLayerManager(LayerManager *aManager); - virtual void SetScaleHint(const gfxIntSize& aScaleHint); - void SetOffscreenFormat(gfxImageFormat aFormat) { mOffscreenFormat = aFormat; } - virtual LayerManager::LayersBackend GetBackendType() { return LayerManager::LAYERS_BASIC; } - -protected: - nsRefPtr mImage; - gfxIntSize mScaleHint; - gfxImageFormat mOffscreenFormat; - bool mDelayed; -}; - -/** - * Returns true if aFormat is in the given format array. - */ -static bool -FormatInList(const Image::Format* aFormats, PRUint32 aNumFormats, - Image::Format aFormat) +ImageFactory* +BasicLayerManager::GetImageFactory() { - for (PRUint32 i = 0; i < aNumFormats; ++i) { - if (aFormats[i] == aFormat) { - return true; - } - } - return false; -} - -already_AddRefed -BasicImageContainer::CreateImage(const Image::Format* aFormats, - PRUint32 aNumFormats) -{ - nsRefPtr image; - // Prefer cairo surfaces because they're native for us - if (FormatInList(aFormats, aNumFormats, Image::CAIRO_SURFACE)) { - image = new BasicCairoImage(); - } else if (FormatInList(aFormats, aNumFormats, Image::PLANAR_YCBCR)) { - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - image = new BasicPlanarYCbCrImage(mScaleHint); - static_cast(image.get())->SetOffscreenFormat(mOffscreenFormat); - static_cast(image.get())->SetDelayedConversion(mDelayed); - } - return image.forget(); -} - -void -BasicImageContainer::SetCurrentImage(Image* aImage) -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - mImage = aImage; - CurrentImageChanged(); -} - -already_AddRefed -BasicImageContainer::GetCurrentImage() -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - nsRefPtr image = mImage; - return image.forget(); -} - -static BasicImageImplData* -ToImageData(Image* aImage) -{ - return static_cast(aImage->GetImplData()); -} - -already_AddRefed -BasicImageContainer::GetCurrentAsSurface(gfxIntSize* aSizeResult) -{ - NS_PRECONDITION(NS_IsMainThread(), "Must be called on main thread"); - - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - if (!mImage) { - return nsnull; - } - *aSizeResult = ToImageData(mImage)->GetSize(); - return ToImageData(mImage)->GetAsSurface(); -} - -gfxIntSize -BasicImageContainer::GetCurrentSize() -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - return !mImage ? gfxIntSize(0,0) : ToImageData(mImage)->GetSize(); -} - -void BasicImageContainer::SetScaleHint(const gfxIntSize& aScaleHint) -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - mScaleHint = aScaleHint; -} - -bool -BasicImageContainer::SetLayerManager(LayerManager *aManager) -{ - if (aManager && - aManager->GetBackendType() != LayerManager::LAYERS_BASIC) - { - return false; + if (!mFactory) { + mFactory = new BasicImageFactory(); } - return true; -} - -already_AddRefed -BasicLayerManager::CreateImageContainer() -{ - nsRefPtr container = new BasicImageContainer(); - static_cast(container.get())-> - SetOffscreenFormat(gfxPlatform::GetPlatform()->GetOffscreenFormat()); - return container.forget(); + return mFactory.get(); } } diff --git a/gfx/layers/basic/BasicLayers.cpp b/gfx/layers/basic/BasicLayers.cpp index f846aacc8560..7b61a298353b 100644 --- a/gfx/layers/basic/BasicLayers.cpp +++ b/gfx/layers/basic/BasicLayers.cpp @@ -899,6 +899,8 @@ BasicImageLayer::GetAndPaintCurrentImage(gfxContext* aContext, if (!mContainer) return nsnull; + mContainer->SetImageFactory(mManager->IsCompositingCheap() ? nsnull : BasicManager()->GetImageFactory()); + nsRefPtr image = mContainer->GetCurrentImage(); nsRefPtr surface = mContainer->GetCurrentAsSurface(&mSize); diff --git a/gfx/layers/basic/BasicLayers.h b/gfx/layers/basic/BasicLayers.h index c5406ccafaf5..330c0d3deef5 100644 --- a/gfx/layers/basic/BasicLayers.h +++ b/gfx/layers/basic/BasicLayers.h @@ -131,9 +131,10 @@ public: virtual already_AddRefed CreateContainerLayer(); virtual already_AddRefed CreateImageLayer(); virtual already_AddRefed CreateCanvasLayer(); - virtual already_AddRefed CreateImageContainer(); virtual already_AddRefed CreateColorLayer(); virtual already_AddRefed CreateReadbackLayer(); + virtual ImageFactory *GetImageFactory(); + virtual already_AddRefed CreateShadowThebesLayer() { return nsnull; } virtual already_AddRefed CreateShadowContainerLayer() @@ -207,6 +208,8 @@ protected: nsRefPtr mDefaultTarget; // The context to draw into. nsRefPtr mTarget; + // Image factory we use. + nsRefPtr mFactory; // Cached surface for double buffering gfxCachedTempSurface mCachedSurface; @@ -276,54 +279,4 @@ private: } } -/** - * We need to be able to hold a reference to a gfxASurface from Image - * subclasses. This is potentially a problem since Images can be addrefed - * or released off the main thread. We can ensure that we never AddRef - * a gfxASurface off the main thread, but we might want to Release due - * to an Image being destroyed off the main thread. - * - * We use nsCountedRef to reference the - * gfxASurface. When AddRefing, we assert that we're on the main thread. - * When Releasing, if we're not on the main thread, we post an event to - * the main thread to do the actual release. - */ -class nsMainThreadSurfaceRef; - -template <> -class nsAutoRefTraits { -public: - typedef gfxASurface* RawRef; - - /** - * The XPCOM event that will do the actual release on the main thread. - */ - class SurfaceReleaser : public nsRunnable { - public: - SurfaceReleaser(RawRef aRef) : mRef(aRef) {} - NS_IMETHOD Run() { - mRef->Release(); - return NS_OK; - } - RawRef mRef; - }; - - static RawRef Void() { return nsnull; } - static void Release(RawRef aRawRef) - { - if (NS_IsMainThread()) { - aRawRef->Release(); - return; - } - nsCOMPtr runnable = new SurfaceReleaser(aRawRef); - NS_DispatchToMainThread(runnable); - } - static void AddRef(RawRef aRawRef) - { - NS_ASSERTION(NS_IsMainThread(), - "Can only add a reference on the main thread"); - aRawRef->AddRef(); - } -}; - #endif /* GFX_BASICLAYERS_H */ diff --git a/gfx/layers/d3d10/ImageLayerD3D10.cpp b/gfx/layers/d3d10/ImageLayerD3D10.cpp index f598e1d969ea..8770c7fb1c16 100644 --- a/gfx/layers/d3d10/ImageLayerD3D10.cpp +++ b/gfx/layers/d3d10/ImageLayerD3D10.cpp @@ -100,102 +100,6 @@ SurfaceToTexture(ID3D10Device *aDevice, return texture.forget(); } -ImageContainerD3D10::ImageContainerD3D10(ID3D10Device1 *aDevice) - : ImageContainer(nsnull) - , mDevice(aDevice) -{ -} - -already_AddRefed -ImageContainerD3D10::CreateImage(const Image::Format *aFormats, - PRUint32 aNumFormats) -{ - if (!aNumFormats) { - return nsnull; - } - nsRefPtr img; - if (aFormats[0] == Image::PLANAR_YCBCR) { - img = new PlanarYCbCrImageD3D10(mDevice); - } else if (aFormats[0] == Image::CAIRO_SURFACE) { - img = new CairoImageD3D10(mDevice); - } - return img.forget(); -} - -void -ImageContainerD3D10::SetCurrentImage(Image *aImage) -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - - mActiveImage = aImage; - CurrentImageChanged(); -} - -already_AddRefed -ImageContainerD3D10::GetCurrentImage() -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - - nsRefPtr retval = mActiveImage; - return retval.forget(); -} - -already_AddRefed -ImageContainerD3D10::GetCurrentAsSurface(gfxIntSize *aSize) -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - if (!mActiveImage) { - return nsnull; - } - - if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) { - PlanarYCbCrImageD3D10 *yuvImage = - static_cast(mActiveImage.get()); - if (yuvImage->HasData()) { - *aSize = yuvImage->mSize; - } - } else if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) { - CairoImageD3D10 *cairoImage = - static_cast(mActiveImage.get()); - *aSize = cairoImage->mSize; - } - - return static_cast(mActiveImage->GetImplData())->GetAsSurface(); -} - -gfxIntSize -ImageContainerD3D10::GetCurrentSize() -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - if (!mActiveImage) { - return gfxIntSize(0,0); - } - if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) { - PlanarYCbCrImageD3D10 *yuvImage = - static_cast(mActiveImage.get()); - if (!yuvImage->HasData()) { - return gfxIntSize(0,0); - } - return yuvImage->mSize; - - } else if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) { - CairoImageD3D10 *cairoImage = - static_cast(mActiveImage.get()); - return cairoImage->mSize; - } - - return gfxIntSize(0,0); -} - -bool -ImageContainerD3D10::SetLayerManager(LayerManager *aManager) -{ - if (aManager->GetBackendType() == LayerManager::LAYERS_D3D10) { - return true; - } - return false; -} - Layer* ImageLayerD3D10::GetLayer() { @@ -218,51 +122,39 @@ ImageLayerD3D10::RenderLayer() ID3D10EffectTechnique *technique; - if (GetContainer()->GetBackendType() != LayerManager::LAYERS_D3D10 || - image->GetFormat() == Image::CAIRO_SURFACE) + if (image->GetFormat() == Image::CAIRO_SURFACE) { - gfxIntSize size; - bool hasAlpha; - nsRefPtr srView; + CairoImage *cairoImage = + static_cast(image.get()); - if (GetContainer()->GetBackendType() != LayerManager::LAYERS_D3D10) - { - nsRefPtr surf = GetContainer()->GetCurrentAsSurface(&size); - - nsRefPtr texture = SurfaceToTexture(device(), surf, size); - - if (!texture) { - NS_WARNING("Failed to create texture for surface."); - return; - } - - hasAlpha = surf->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA; - - device()->CreateShaderResourceView(texture, NULL, getter_AddRefs(srView)); - } else { - ImageContainerD3D10 *container = - static_cast(GetContainer()); - - if (container->device() != device()) { - container->SetDevice(device()); - } - - // image->GetFormat() == Image::CAIRO_SURFACE - CairoImageD3D10 *cairoImage = - static_cast(image.get()); - - if (cairoImage->mDevice != device()) { - // This shader resource view was for an old device! Can't draw that - // now. - return; - } - - srView = cairoImage->mSRView; - hasAlpha = cairoImage->mHasAlpha; - size = cairoImage->mSize; + if (!cairoImage->mSurface) { + return; } - if (hasAlpha) { + if (!cairoImage->GetBackendData(LayerManager::LAYERS_D3D10)) { + nsAutoPtr dat = new CairoD3D10BackendData(); + dat->mTexture = SurfaceToTexture(device(), cairoImage->mSurface, cairoImage->mSize); + + if (dat->mTexture) { + device()->CreateShaderResourceView(dat->mTexture, NULL, getter_AddRefs(dat->mSRView)); + cairoImage->SetBackendData(LayerManager::LAYERS_D3D10, dat.forget()); + } + } + + CairoD3D10BackendData *data = + static_cast(cairoImage->GetBackendData(LayerManager::LAYERS_D3D10)); + + if (!data) { + return; + } + + nsRefPtr dev; + data->mTexture->GetDevice(getter_AddRefs(dev)); + if (dev != device()) { + return; + } + + if (cairoImage->mSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA) { if (mFilter == gfxPattern::FILTER_NEAREST) { technique = effect()->GetTechniqueByName("RenderRGBALayerPremulPoint"); } else { @@ -276,29 +168,38 @@ ImageLayerD3D10::RenderLayer() } } - if (srView) { - effect()->GetVariableByName("tRGB")->AsShaderResource()->SetResource(srView); - } + effect()->GetVariableByName("tRGB")->AsShaderResource()->SetResource(data->mSRView); effect()->GetVariableByName("vLayerQuad")->AsVector()->SetFloatVector( ShaderConstantRectD3D10( (float)0, (float)0, - (float)size.width, - (float)size.height) + (float)cairoImage->mSize.width, + (float)cairoImage->mSize.height) ); } else if (image->GetFormat() == Image::PLANAR_YCBCR) { - PlanarYCbCrImageD3D10 *yuvImage = - static_cast(image.get()); + PlanarYCbCrImage *yuvImage = + static_cast(image.get()); - if (!yuvImage->HasData()) { + if (!yuvImage->mBufferSize) { return; } - if (yuvImage->mDevice != device()) { - // These shader resources were created for an old device! Can't draw - // that here. - return; + if (!yuvImage->GetBackendData(LayerManager::LAYERS_D3D10)) { + AllocateTexturesYCbCr(yuvImage); + } + + PlanarYCbCrD3D10BackendData *data = + static_cast(yuvImage->GetBackendData(LayerManager::LAYERS_D3D10)); + + if (!data) { + return; + } + + nsRefPtr dev; + data->mYTexture->GetDevice(getter_AddRefs(dev)); + if (dev != device()) { + return; } // TODO: At some point we should try to deal with mFilter here, you don't @@ -308,9 +209,9 @@ ImageLayerD3D10::RenderLayer() technique = effect()->GetTechniqueByName("RenderYCbCrLayer"); - effect()->GetVariableByName("tY")->AsShaderResource()->SetResource(yuvImage->mYView); - effect()->GetVariableByName("tCb")->AsShaderResource()->SetResource(yuvImage->mCbView); - effect()->GetVariableByName("tCr")->AsShaderResource()->SetResource(yuvImage->mCrView); + effect()->GetVariableByName("tY")->AsShaderResource()->SetResource(data->mYView); + effect()->GetVariableByName("tCb")->AsShaderResource()->SetResource(data->mCbView); + effect()->GetVariableByName("tCr")->AsShaderResource()->SetResource(data->mCrView); /* * Send 3d control data and metadata to NV3DVUtils @@ -341,7 +242,7 @@ ImageLayerD3D10::RenderLayer() if (yuvImage->mData.mStereoMode != STEREO_MODE_MONO) { // Dst resource is optional GetNv3DVUtils()->SendNv3DVMetaData((unsigned int)yuvImage->mSize.width, - (unsigned int)yuvImage->mSize.height, (HANDLE)(yuvImage->mYTexture), (HANDLE)(NULL)); + (unsigned int)yuvImage->mSize.height, (HANDLE)(data->mYTexture), (HANDLE)(NULL)); } } @@ -373,146 +274,49 @@ ImageLayerD3D10::RenderLayer() GetContainer()->NotifyPaintedImage(image); } -PlanarYCbCrImageD3D10::PlanarYCbCrImageD3D10(ID3D10Device1 *aDevice) - : PlanarYCbCrImage(static_cast(this)) - , mBufferSize(0) - , mDevice(aDevice) - , mHasData(false) +void ImageLayerD3D10::AllocateTexturesYCbCr(PlanarYCbCrImage *aImage) { -} + nsAutoPtr backendData = + new PlanarYCbCrD3D10BackendData; -void -PlanarYCbCrImageD3D10::SetData(const PlanarYCbCrImage::Data &aData) -{ - mBuffer = CopyData(mData, mSize, mBufferSize, aData); + PlanarYCbCrImage::Data &data = aImage->mData; - AllocateTextures(); - - mHasData = true; -} - -void -PlanarYCbCrImageD3D10::AllocateTextures() -{ D3D10_SUBRESOURCE_DATA dataY; D3D10_SUBRESOURCE_DATA dataCb; D3D10_SUBRESOURCE_DATA dataCr; CD3D10_TEXTURE2D_DESC descY(DXGI_FORMAT_R8_UNORM, - mData.mYSize.width, - mData.mYSize.height, 1, 1); + data.mYSize.width, + data.mYSize.height, 1, 1); CD3D10_TEXTURE2D_DESC descCbCr(DXGI_FORMAT_R8_UNORM, - mData.mCbCrSize.width, - mData.mCbCrSize.height, 1, 1); + data.mCbCrSize.width, + data.mCbCrSize.height, 1, 1); descY.Usage = descCbCr.Usage = D3D10_USAGE_IMMUTABLE; - dataY.pSysMem = mData.mYChannel; - dataY.SysMemPitch = mData.mYStride; - dataCb.pSysMem = mData.mCbChannel; - dataCb.SysMemPitch = mData.mCbCrStride; - dataCr.pSysMem = mData.mCrChannel; - dataCr.SysMemPitch = mData.mCbCrStride; + dataY.pSysMem = data.mYChannel; + dataY.SysMemPitch = data.mYStride; + dataCb.pSysMem = data.mCbChannel; + dataCb.SysMemPitch = data.mCbCrStride; + dataCr.pSysMem = data.mCrChannel; + dataCr.SysMemPitch = data.mCbCrStride; - HRESULT hr = mDevice->CreateTexture2D(&descY, &dataY, getter_AddRefs(mYTexture)); + HRESULT hr = device()->CreateTexture2D(&descY, &dataY, getter_AddRefs(backendData->mYTexture)); if (!FAILED(hr)) { - hr = mDevice->CreateTexture2D(&descCbCr, &dataCb, getter_AddRefs(mCbTexture)); + hr = device()->CreateTexture2D(&descCbCr, &dataCb, getter_AddRefs(backendData->mCbTexture)); } if (!FAILED(hr)) { - hr = mDevice->CreateTexture2D(&descCbCr, &dataCr, getter_AddRefs(mCrTexture)); + hr = device()->CreateTexture2D(&descCbCr, &dataCr, getter_AddRefs(backendData->mCrTexture)); } if (FAILED(hr)) { LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("PlanarYCbCrImageD3D10::AllocateTextures(): Failed to create texture"), hr); return; } - mDevice->CreateShaderResourceView(mYTexture, NULL, getter_AddRefs(mYView)); - mDevice->CreateShaderResourceView(mCbTexture, NULL, getter_AddRefs(mCbView)); - mDevice->CreateShaderResourceView(mCrTexture, NULL, getter_AddRefs(mCrView)); -} + device()->CreateShaderResourceView(backendData->mYTexture, NULL, getter_AddRefs(backendData->mYView)); + device()->CreateShaderResourceView(backendData->mCbTexture, NULL, getter_AddRefs(backendData->mCbView)); + device()->CreateShaderResourceView(backendData->mCrTexture, NULL, getter_AddRefs(backendData->mCrView)); -already_AddRefed -PlanarYCbCrImageD3D10::GetAsSurface() -{ - nsRefPtr imageSurface = - new gfxImageSurface(mSize, gfxASurface::ImageFormatRGB24); - - gfx::YUVType type = - gfx::TypeFromSize(mData.mYSize.width, - mData.mYSize.height, - mData.mCbCrSize.width, - mData.mCbCrSize.height); - - // Convert from YCbCr to RGB now - gfx::ConvertYCbCrToRGB32(mData.mYChannel, - mData.mCbChannel, - mData.mCrChannel, - imageSurface->Data(), - mData.mPicX, - mData.mPicY, - mData.mPicSize.width, - mData.mPicSize.height, - mData.mYStride, - mData.mCbCrStride, - imageSurface->Stride(), - type); - - return imageSurface.forget().get(); -} - -CairoImageD3D10::~CairoImageD3D10() -{ -} - -void -CairoImageD3D10::SetData(const CairoImage::Data &aData) -{ - mSize = aData.mSize; - NS_ASSERTION(aData.mSurface->GetContentType() != gfxASurface::CONTENT_ALPHA, - "Invalid content type passed to CairoImageD3D10."); - - mTexture = SurfaceToTexture(mDevice, aData.mSurface, mSize); - - if (!mTexture) { - NS_WARNING("Failed to create texture for CairoImage."); - return; - } - - if (aData.mSurface->GetContentType() == gfxASurface::CONTENT_COLOR) { - mHasAlpha = false; - } else { - mHasAlpha = true; - } - - mDevice->CreateShaderResourceView(mTexture, NULL, getter_AddRefs(mSRView)); -} - -already_AddRefed -CairoImageD3D10::GetAsSurface() -{ - nsRefPtr surfTexture; - - // Make a copy of the texture since our current texture is not suitable for - // drawing with Direct2D because it is immutable and cannot be bound as a - // render target. - D3D10_TEXTURE2D_DESC texDesc; - mTexture->GetDesc(&texDesc); - texDesc.Usage = D3D10_USAGE_DEFAULT; - texDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; - texDesc.MiscFlags = D3D10_RESOURCE_MISC_GDI_COMPATIBLE; - - HRESULT hr = mDevice->CreateTexture2D(&texDesc, NULL, getter_AddRefs(surfTexture)); - if (FAILED(hr)) { - LayerManagerD3D10::ReportFailure(NS_LITERAL_CSTRING("CairoImageD3D10::GetAsSurface(): Failed to create texture"), - hr); - return nsnull; - } - - mDevice->CopyResource(surfTexture, mTexture); - - nsRefPtr surf = - new gfxD2DSurface(surfTexture, mHasAlpha ? gfxASurface::CONTENT_COLOR_ALPHA : - gfxASurface::CONTENT_COLOR); - return surf.forget(); + aImage->SetBackendData(LayerManager::LAYERS_D3D10, backendData.forget()); } } /* layers */ diff --git a/gfx/layers/d3d10/ImageLayerD3D10.h b/gfx/layers/d3d10/ImageLayerD3D10.h index 7dda5e1c0140..9b6e2f3c6c9d 100644 --- a/gfx/layers/d3d10/ImageLayerD3D10.h +++ b/gfx/layers/d3d10/ImageLayerD3D10.h @@ -45,35 +45,6 @@ namespace mozilla { namespace layers { -class THEBES_API ImageContainerD3D10 : public ImageContainer -{ -public: - ImageContainerD3D10(ID3D10Device1 *aDevice); - virtual ~ImageContainerD3D10() {} - - virtual already_AddRefed CreateImage(const Image::Format* aFormats, - PRUint32 aNumFormats); - - virtual void SetCurrentImage(Image* aImage); - - virtual already_AddRefed GetCurrentImage(); - - virtual already_AddRefed GetCurrentAsSurface(gfxIntSize* aSize); - - virtual gfxIntSize GetCurrentSize(); - - virtual bool SetLayerManager(LayerManager *aManager); - - virtual LayerManager::LayersBackend GetBackendType() { return LayerManager::LAYERS_D3D10; } - - ID3D10Device1 *device() { return mDevice; } - void SetDevice(ID3D10Device1 *aDevice) { mDevice = aDevice; } - -private: - nsRefPtr mActiveImage; - nsRefPtr mDevice; -}; - class THEBES_API ImageLayerD3D10 : public ImageLayer, public LayerD3D10 { @@ -89,70 +60,24 @@ public: virtual Layer* GetLayer(); virtual void RenderLayer(); + + void AllocateTexturesYCbCr(PlanarYCbCrImage *aImage); }; -class THEBES_API ImageD3D10 +struct PlanarYCbCrD3D10BackendData : public ImageBackendData { -public: - virtual already_AddRefed GetAsSurface() = 0; -}; - -class THEBES_API PlanarYCbCrImageD3D10 : public PlanarYCbCrImage, - public ImageD3D10 -{ -public: - PlanarYCbCrImageD3D10(ID3D10Device1 *aDevice); - ~PlanarYCbCrImageD3D10() {} - - virtual void SetData(const Data &aData); - - /* - * Upload the data from out mData into our textures. For now we use this to - * make sure the textures are created and filled on the main thread. - */ - void AllocateTextures(); - - bool HasData() { return mHasData; } - - PRUint32 GetDataSize() { return mBuffer ? mBufferSize : 0; } - - virtual already_AddRefed GetAsSurface(); - - nsAutoArrayPtr mBuffer; - PRUint32 mBufferSize; - nsRefPtr mDevice; - Data mData; - gfxIntSize mSize; nsRefPtr mYTexture; nsRefPtr mCrTexture; nsRefPtr mCbTexture; nsRefPtr mYView; nsRefPtr mCbView; nsRefPtr mCrView; - bool mHasData; }; - -class THEBES_API CairoImageD3D10 : public CairoImage, - public ImageD3D10 +struct CairoD3D10BackendData : public ImageBackendData { -public: - CairoImageD3D10(ID3D10Device1 *aDevice) - : CairoImage(static_cast(this)) - , mDevice(aDevice) - , mHasAlpha(true) - { } - ~CairoImageD3D10(); - - virtual void SetData(const Data &aData); - - virtual already_AddRefed GetAsSurface(); - - nsRefPtr mDevice; nsRefPtr mTexture; nsRefPtr mSRView; - gfxIntSize mSize; - bool mHasAlpha; }; } /* layers */ diff --git a/gfx/layers/d3d10/LayerManagerD3D10.cpp b/gfx/layers/d3d10/LayerManagerD3D10.cpp index 42988d433ae1..32eb53180278 100644 --- a/gfx/layers/d3d10/LayerManagerD3D10.cpp +++ b/gfx/layers/d3d10/LayerManagerD3D10.cpp @@ -421,13 +421,6 @@ LayerManagerD3D10::CreateReadbackLayer() return layer.forget(); } -already_AddRefed -LayerManagerD3D10::CreateImageContainer() -{ - nsRefPtr layer = new ImageContainerD3D10(mDevice); - return layer.forget(); -} - static void ReleaseTexture(void *texture) { static_cast(texture)->Release(); diff --git a/gfx/layers/d3d10/LayerManagerD3D10.h b/gfx/layers/d3d10/LayerManagerD3D10.h index b097d483d08c..2b60f815d9ed 100644 --- a/gfx/layers/d3d10/LayerManagerD3D10.h +++ b/gfx/layers/d3d10/LayerManagerD3D10.h @@ -161,8 +161,6 @@ public: virtual already_AddRefed CreateReadbackLayer(); - virtual already_AddRefed CreateImageContainer(); - virtual already_AddRefed CreateOptimalSurface(const gfxIntSize &aSize, gfxASurface::gfxImageFormat imageFormat); diff --git a/gfx/layers/d3d9/ImageLayerD3D9.cpp b/gfx/layers/d3d9/ImageLayerD3D9.cpp index f5950b00a24e..75da703dad54 100644 --- a/gfx/layers/d3d9/ImageLayerD3D9.cpp +++ b/gfx/layers/d3d9/ImageLayerD3D9.cpp @@ -137,101 +137,160 @@ SurfaceToTexture(IDirect3DDevice9 *aDevice, return texture.forget(); } -ImageContainerD3D9::ImageContainerD3D9(IDirect3DDevice9 *aDevice) - : ImageContainer(nsnull) - , mDevice(aDevice) +static void AllocateTexturesYCbCr(PlanarYCbCrImage *aImage, + IDirect3DDevice9 *aDevice, + LayerManagerD3D9 *aManager) { -} + nsAutoPtr backendData = + new PlanarYCbCrD3D9BackendData; -already_AddRefed -ImageContainerD3D9::CreateImage(const Image::Format *aFormats, - PRUint32 aNumFormats) -{ - if (!aNumFormats) { - return nsnull; - } - nsRefPtr img; - if (aFormats[0] == Image::PLANAR_YCBCR) { - img = new PlanarYCbCrImageD3D9(); - } else if (aFormats[0] == Image::CAIRO_SURFACE) { - img = new CairoImageD3D9(mDevice); - } - return img.forget(); -} + PlanarYCbCrImage::Data &data = aImage->mData; -void -ImageContainerD3D9::SetCurrentImage(Image *aImage) -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); + D3DLOCKED_RECT lockrectY; + D3DLOCKED_RECT lockrectCb; + D3DLOCKED_RECT lockrectCr; + PRUint8* src; + PRUint8* dest; - mActiveImage = aImage; - CurrentImageChanged(); -} + nsRefPtr tmpSurfaceY; + nsRefPtr tmpSurfaceCb; + nsRefPtr tmpSurfaceCr; -already_AddRefed -ImageContainerD3D9::GetCurrentImage() -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); + nsRefPtr deviceEx; + aDevice->QueryInterface(IID_IDirect3DDevice9Ex, + getter_AddRefs(deviceEx)); - nsRefPtr retval = mActiveImage; - return retval.forget(); -} + bool isD3D9Ex = deviceEx; -already_AddRefed -ImageContainerD3D9::GetCurrentAsSurface(gfxIntSize *aSize) -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - if (!mActiveImage) { - return nsnull; - } + if (isD3D9Ex) { + nsRefPtr tmpYTexture; + nsRefPtr tmpCbTexture; + nsRefPtr tmpCrTexture; + // D3D9Ex does not support the managed pool, could use dynamic textures + // here. But since an Image is immutable static textures are probably a + // better idea. - if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) { - PlanarYCbCrImageD3D9 *yuvImage = - static_cast(mActiveImage.get()); - if (yuvImage->HasData()) { - *aSize = yuvImage->mSize; + HRESULT hr; + hr = aDevice->CreateTexture(data.mYSize.width, data.mYSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, + getter_AddRefs(backendData->mYTexture), NULL); + if (!FAILED(hr)) { + hr = aDevice->CreateTexture(data.mCbCrSize.width, data.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, + getter_AddRefs(backendData->mCbTexture), NULL); } - } else if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) { - CairoImageD3D9 *cairoImage = - static_cast(mActiveImage.get()); - *aSize = cairoImage->GetSize(); - } - - return static_cast(mActiveImage->GetImplData())->GetAsSurface(); -} - -gfxIntSize -ImageContainerD3D9::GetCurrentSize() -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - if (!mActiveImage) { - return gfxIntSize(0,0); - } - if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) { - PlanarYCbCrImageD3D9 *yuvImage = - static_cast(mActiveImage.get()); - if (!yuvImage->HasData()) { - return gfxIntSize(0,0); + if (!FAILED(hr)) { + hr = aDevice->CreateTexture(data.mCbCrSize.width, data.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, + getter_AddRefs(backendData->mCrTexture), NULL); + } + if (!FAILED(hr)) { + hr = aDevice->CreateTexture(data.mYSize.width, data.mYSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpYTexture), NULL); + } + if (!FAILED(hr)) { + hr = aDevice->CreateTexture(data.mCbCrSize.width, data.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpCbTexture), NULL); + } + if (!FAILED(hr)) { + hr = aDevice->CreateTexture(data.mCbCrSize.width, data.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM, + getter_AddRefs(tmpCrTexture), NULL); } - return yuvImage->mSize; - } else if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) { - CairoImageD3D9 *cairoImage = - static_cast(mActiveImage.get()); - return cairoImage->GetSize(); + if (FAILED(hr)) { + aManager->ReportFailure(NS_LITERAL_CSTRING("PlanarYCbCrImageD3D9::AllocateTextures(): Failed to create texture (isD3D9Ex)"), + hr); + return; + } + + tmpYTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceY)); + tmpCbTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceCb)); + tmpCrTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceCr)); + tmpSurfaceY->LockRect(&lockrectY, NULL, 0); + tmpSurfaceCb->LockRect(&lockrectCb, NULL, 0); + tmpSurfaceCr->LockRect(&lockrectCr, NULL, 0); + } else { + HRESULT hr; + hr = aDevice->CreateTexture(data.mYSize.width, data.mYSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, + getter_AddRefs(backendData->mYTexture), NULL); + if (!FAILED(hr)) { + aDevice->CreateTexture(data.mCbCrSize.width, data.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, + getter_AddRefs(backendData->mCbTexture), NULL); + } + if (!FAILED(hr)) { + aDevice->CreateTexture(data.mCbCrSize.width, data.mCbCrSize.height, + 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, + getter_AddRefs(backendData->mCrTexture), NULL); + } + + if (FAILED(hr)) { + aManager->ReportFailure(NS_LITERAL_CSTRING("PlanarYCbCrImageD3D9::AllocateTextures(): Failed to create texture (!isD3D9Ex)"), + hr); + return; + } + + /* lock the entire texture */ + backendData->mYTexture->LockRect(0, &lockrectY, NULL, 0); + backendData->mCbTexture->LockRect(0, &lockrectCb, NULL, 0); + backendData->mCrTexture->LockRect(0, &lockrectCr, NULL, 0); } - return gfxIntSize(0,0); -} + src = data.mYChannel; + //FIX cast + dest = (PRUint8*)lockrectY.pBits; -bool -ImageContainerD3D9::SetLayerManager(LayerManager *aManager) -{ - if (aManager->GetBackendType() == LayerManager::LAYERS_D3D9) { - return true; + // copy over data + for (int h=0; hUnlockRect(); + tmpSurfaceCb->UnlockRect(); + tmpSurfaceCr->UnlockRect(); + nsRefPtr dstSurface; + backendData->mYTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + aDevice->UpdateSurface(tmpSurfaceY, NULL, dstSurface, NULL); + backendData->mCbTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + aDevice->UpdateSurface(tmpSurfaceCb, NULL, dstSurface, NULL); + backendData->mCrTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); + aDevice->UpdateSurface(tmpSurfaceCr, NULL, dstSurface, NULL); + } else { + backendData->mYTexture->UnlockRect(0); + backendData->mCbTexture->UnlockRect(0); + backendData->mCrTexture->UnlockRect(0); + } + + aImage->SetBackendData(LayerManager::LAYERS_D3D9, backendData.forget()); } Layer* @@ -254,8 +313,37 @@ ImageLayerD3D9::RenderLayer() SetShaderTransformAndOpacity(); - if (GetContainer()->GetBackendType() != LayerManager::LAYERS_D3D9) + if (image->GetFormat() == Image::CAIRO_SURFACE) { + CairoImage *cairoImage = + static_cast(image.get()); + + if (!cairoImage->mSurface) { + return; + } + + if (!cairoImage->GetBackendData(LayerManager::LAYERS_D3D9)) { + nsAutoPtr dat = new CairoD3D9BackendData(); + dat->mTexture = SurfaceToTexture(device(), cairoImage->mSurface, cairoImage->mSize); + + if (dat->mTexture) { + cairoImage->SetBackendData(LayerManager::LAYERS_D3D9, dat.forget()); + } + } + + CairoD3D9BackendData *data = + static_cast(cairoImage->GetBackendData(LayerManager::LAYERS_D3D9)); + + if (!data) { + return; + } + + nsRefPtr dev; + data->mTexture->GetDevice(getter_AddRefs(dev)); + if (dev != device()) { + return; + } + gfxIntSize size; nsRefPtr surface = GetContainer()->GetCurrentAsSurface(&size); @@ -275,16 +363,40 @@ ImageLayerD3D9::RenderLayer() mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); } + if (mFilter == gfxPattern::FILTER_NEAREST) { + device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); + device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); + } device()->SetTexture(0, texture); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); - } else if (image->GetFormat() == Image::PLANAR_YCBCR) { - PlanarYCbCrImageD3D9 *yuvImage = - static_cast(image.get()); + if (mFilter == gfxPattern::FILTER_NEAREST) { + device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); + device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); + } + } else { + PlanarYCbCrImage *yuvImage = + static_cast(image.get()); - if (!yuvImage->HasData()) { + if (!yuvImage->mBufferSize) { + return; + } + + if (!yuvImage->GetBackendData(LayerManager::LAYERS_D3D9)) { + AllocateTexturesYCbCr(yuvImage, device(), mD3DManager); + } + + PlanarYCbCrD3D9BackendData *data = + static_cast(yuvImage->GetBackendData(LayerManager::LAYERS_D3D9)); + + if (!data) { + return; + } + + nsRefPtr dev; + data->mYTexture->GetDevice(getter_AddRefs(dev)); + if (dev != device()) { return; } - yuvImage->AllocateTextures(device()); device()->SetVertexShaderConstantF(CBvLayerQuad, ShaderConstantRect(0, @@ -336,300 +448,26 @@ ImageLayerD3D9::RenderLayer() nsRefPtr renderTarget; device()->GetRenderTarget(0, getter_AddRefs(renderTarget)); mD3DManager->GetNv3DVUtils()->SendNv3DVMetaData((unsigned int)yuvImage->mSize.width, - (unsigned int)yuvImage->mSize.height, (HANDLE)(yuvImage->mYTexture), (HANDLE)(renderTarget)); + (unsigned int)yuvImage->mSize.height, (HANDLE)(data->mYTexture), (HANDLE)(renderTarget)); } } // Linear scaling is default here, adhering to mFilter is difficult since // presumably even with point filtering we'll still want chroma upsampling // to be linear. In the current approach we can't. - device()->SetTexture(0, yuvImage->mYTexture); - device()->SetTexture(1, yuvImage->mCbTexture); - device()->SetTexture(2, yuvImage->mCrTexture); + device()->SetTexture(0, data->mYTexture); + device()->SetTexture(1, data->mCbTexture); + device()->SetTexture(2, data->mCrTexture); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); device()->SetVertexShaderConstantF(CBvTextureCoords, ShaderConstantRect(0, 0, 1.0f, 1.0f), 1); - - } else if (image->GetFormat() == Image::CAIRO_SURFACE) { - CairoImageD3D9 *cairoImage = - static_cast(image.get()); - ImageContainerD3D9 *container = - static_cast(GetContainer()); - - if (container->device() != device()) { - // Ensure future images get created with the right device. - container->SetDevice(device()); - } - - if (cairoImage->device() != device()) { - cairoImage->SetDevice(device()); - } - - device()->SetVertexShaderConstantF(CBvLayerQuad, - ShaderConstantRect(0, - 0, - cairoImage->GetSize().width, - cairoImage->GetSize().height), - 1); - - if (cairoImage->HasAlpha()) { - mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBALAYER); - } else { - mD3DManager->SetShaderMode(DeviceManagerD3D9::RGBLAYER); - } - - if (mFilter == gfxPattern::FILTER_NEAREST) { - device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_POINT); - device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_POINT); - } - device()->SetTexture(0, cairoImage->GetOrCreateTexture()); - device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); - if (mFilter == gfxPattern::FILTER_NEAREST) { - device()->SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); - device()->SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR); - } } GetContainer()->NotifyPaintedImage(image); } -PlanarYCbCrImageD3D9::PlanarYCbCrImageD3D9() - : PlanarYCbCrImage(static_cast(this)) - , mBufferSize(0) - , mHasData(false) -{ -} - -void -PlanarYCbCrImageD3D9::SetData(const PlanarYCbCrImage::Data &aData) -{ - mBuffer = CopyData(mData, mSize, mBufferSize, aData); - - mHasData = true; -} - -void -PlanarYCbCrImageD3D9::AllocateTextures(IDirect3DDevice9 *aDevice) -{ - D3DLOCKED_RECT lockrectY; - D3DLOCKED_RECT lockrectCb; - D3DLOCKED_RECT lockrectCr; - PRUint8* src; - PRUint8* dest; - - nsRefPtr tmpSurfaceY; - nsRefPtr tmpSurfaceCb; - nsRefPtr tmpSurfaceCr; - - nsRefPtr deviceEx; - aDevice->QueryInterface(IID_IDirect3DDevice9Ex, - getter_AddRefs(deviceEx)); - - bool isD3D9Ex = deviceEx; - - if (isD3D9Ex) { - nsRefPtr tmpYTexture; - nsRefPtr tmpCbTexture; - nsRefPtr tmpCrTexture; - // D3D9Ex does not support the managed pool, could use dynamic textures - // here. But since an Image is immutable static textures are probably a - // better idea. - - HRESULT hr; - hr = aDevice->CreateTexture(mData.mYSize.width, mData.mYSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, - getter_AddRefs(mYTexture), NULL); - if (!FAILED(hr)) { - hr = aDevice->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, - getter_AddRefs(mCbTexture), NULL); - } - if (!FAILED(hr)) { - hr = aDevice->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_DEFAULT, - getter_AddRefs(mCrTexture), NULL); - } - if (!FAILED(hr)) { - hr = aDevice->CreateTexture(mData.mYSize.width, mData.mYSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM, - getter_AddRefs(tmpYTexture), NULL); - } - if (!FAILED(hr)) { - hr = aDevice->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM, - getter_AddRefs(tmpCbTexture), NULL); - } - if (!FAILED(hr)) { - hr = aDevice->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_SYSTEMMEM, - getter_AddRefs(tmpCrTexture), NULL); - } - - if (FAILED(hr)) { - mManager->ReportFailure(NS_LITERAL_CSTRING("PlanarYCbCrImageD3D9::AllocateTextures(): Failed to create texture (isD3D9Ex)"), - hr); - return; - } - - tmpYTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceY)); - tmpCbTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceCb)); - tmpCrTexture->GetSurfaceLevel(0, getter_AddRefs(tmpSurfaceCr)); - tmpSurfaceY->LockRect(&lockrectY, NULL, 0); - tmpSurfaceCb->LockRect(&lockrectCb, NULL, 0); - tmpSurfaceCr->LockRect(&lockrectCr, NULL, 0); - } else { - HRESULT hr; - hr = aDevice->CreateTexture(mData.mYSize.width, mData.mYSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, - getter_AddRefs(mYTexture), NULL); - if (!FAILED(hr)) { - aDevice->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, - getter_AddRefs(mCbTexture), NULL); - } - if (!FAILED(hr)) { - aDevice->CreateTexture(mData.mCbCrSize.width, mData.mCbCrSize.height, - 1, 0, D3DFMT_L8, D3DPOOL_MANAGED, - getter_AddRefs(mCrTexture), NULL); - } - - if (FAILED(hr)) { - mManager->ReportFailure(NS_LITERAL_CSTRING("PlanarYCbCrImageD3D9::AllocateTextures(): Failed to create texture (!isD3D9Ex)"), - hr); - return; - } - - /* lock the entire texture */ - mYTexture->LockRect(0, &lockrectY, NULL, 0); - mCbTexture->LockRect(0, &lockrectCb, NULL, 0); - mCrTexture->LockRect(0, &lockrectCr, NULL, 0); - } - - src = mData.mYChannel; - //FIX cast - dest = (PRUint8*)lockrectY.pBits; - - // copy over data - for (int h=0; hUnlockRect(); - tmpSurfaceCb->UnlockRect(); - tmpSurfaceCr->UnlockRect(); - nsRefPtr dstSurface; - mYTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); - aDevice->UpdateSurface(tmpSurfaceY, NULL, dstSurface, NULL); - mCbTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); - aDevice->UpdateSurface(tmpSurfaceCb, NULL, dstSurface, NULL); - mCrTexture->GetSurfaceLevel(0, getter_AddRefs(dstSurface)); - aDevice->UpdateSurface(tmpSurfaceCr, NULL, dstSurface, NULL); - } else { - mYTexture->UnlockRect(0); - mCbTexture->UnlockRect(0); - mCrTexture->UnlockRect(0); - } -} - -void -PlanarYCbCrImageD3D9::FreeTextures() -{ -} - -already_AddRefed -PlanarYCbCrImageD3D9::GetAsSurface() -{ - nsRefPtr imageSurface = - new gfxImageSurface(mSize, gfxASurface::ImageFormatRGB24); - - gfx::YUVType type = - gfx::TypeFromSize(mData.mYSize.width, - mData.mYSize.height, - mData.mCbCrSize.width, - mData.mCbCrSize.height); - - // Convert from YCbCr to RGB now - gfx::ConvertYCbCrToRGB32(mData.mYChannel, - mData.mCbChannel, - mData.mCrChannel, - imageSurface->Data(), - mData.mPicX, - mData.mPicY, - mData.mPicSize.width, - mData.mPicSize.height, - mData.mYStride, - mData.mCbCrStride, - imageSurface->Stride(), - type); - - return imageSurface.forget().get(); -} - -CairoImageD3D9::~CairoImageD3D9() -{ -} - -void -CairoImageD3D9::SetDevice(IDirect3DDevice9 *aDevice) -{ - mTexture = NULL; - mDevice = aDevice; -} - -void -CairoImageD3D9::SetData(const CairoImage::Data &aData) -{ - mSize = aData.mSize; - mCachedSurface = aData.mSurface; - mTexture = NULL; -} - -IDirect3DTexture9* -CairoImageD3D9::GetOrCreateTexture() -{ - if (mTexture) - return mTexture; - - mTexture = SurfaceToTexture(mDevice, mCachedSurface, mSize); - - // We need to keep our cached surface around in case the device changes. - return mTexture; -} - -already_AddRefed -CairoImageD3D9::GetAsSurface() -{ - nsRefPtr surface = mCachedSurface; - return surface.forget(); -} - ShadowImageLayerD3D9::ShadowImageLayerD3D9(LayerManagerD3D9* aManager) : ShadowImageLayer(aManager, nsnull) , LayerD3D9(aManager) @@ -675,7 +513,7 @@ ShadowImageLayerD3D9::Swap(const SharedImage& aNewFront, data.mPicY = 0; if (!mYCbCrImage) { - mYCbCrImage = new PlanarYCbCrImageD3D9(); + mYCbCrImage = new PlanarYCbCrImage(new BufferRecycleBin()); } mYCbCrImage->SetData(data); @@ -710,12 +548,25 @@ ShadowImageLayerD3D9::RenderLayer() if (mBuffer) { mBuffer->RenderTo(mD3DManager, GetEffectiveVisibleRegion()); } else if (mYCbCrImage) { - if (!mYCbCrImage->HasData()) { + if (!mYCbCrImage->mBufferSize) { + return; + } + + if (!mYCbCrImage->GetBackendData(LayerManager::LAYERS_D3D9)) { + AllocateTexturesYCbCr(mYCbCrImage, device(), mD3DManager); + } + + PlanarYCbCrD3D9BackendData *data = + static_cast(mYCbCrImage->GetBackendData(LayerManager::LAYERS_D3D9)); + + if (!data) { + return; + } + + if (!mYCbCrImage->mBufferSize) { return; } - mYCbCrImage->AllocateTextures(device()); - SetShaderTransformAndOpacity(); device()->SetVertexShaderConstantF(CBvLayerQuad, @@ -737,9 +588,9 @@ ShadowImageLayerD3D9::RenderLayer() // Linear scaling is default here, adhering to mFilter is difficult since // presumably even with point filtering we'll still want chroma upsampling // to be linear. In the current approach we can't. - device()->SetTexture(0, mYCbCrImage->mYTexture); - device()->SetTexture(1, mYCbCrImage->mCbTexture); - device()->SetTexture(2, mYCbCrImage->mCrTexture); + device()->SetTexture(0, data->mYTexture); + device()->SetTexture(1, data->mCbTexture); + device()->SetTexture(2, data->mCrTexture); device()->DrawPrimitive(D3DPT_TRIANGLESTRIP, 0, 2); } else { diff --git a/gfx/layers/d3d9/ImageLayerD3D9.h b/gfx/layers/d3d9/ImageLayerD3D9.h index 4963181b294e..60bf4f547b9c 100644 --- a/gfx/layers/d3d9/ImageLayerD3D9.h +++ b/gfx/layers/d3d9/ImageLayerD3D9.h @@ -47,36 +47,6 @@ namespace layers { class ShadowBufferD3D9; -class THEBES_API ImageContainerD3D9 : public ImageContainer -{ -public: - ImageContainerD3D9(IDirect3DDevice9 *aDevice); - virtual ~ImageContainerD3D9() {} - - virtual already_AddRefed CreateImage(const Image::Format* aFormats, - PRUint32 aNumFormats); - - virtual void SetCurrentImage(Image* aImage); - - virtual already_AddRefed GetCurrentImage(); - - virtual already_AddRefed GetCurrentAsSurface(gfxIntSize* aSize); - - virtual gfxIntSize GetCurrentSize(); - - virtual bool SetLayerManager(LayerManager *aManager); - - virtual LayerManager::LayersBackend GetBackendType() { return LayerManager::LAYERS_D3D9; } - - IDirect3DDevice9 *device() { return mDevice; } - void SetDevice(IDirect3DDevice9 *aDevice) { mDevice = aDevice; } - -private: - nsRefPtr mActiveImage; - - nsRefPtr mDevice; -}; - class THEBES_API ImageLayerD3D9 : public ImageLayer, public LayerD3D9 { @@ -100,81 +70,17 @@ public: virtual already_AddRefed GetAsSurface() = 0; }; -class THEBES_API PlanarYCbCrImageD3D9 : public PlanarYCbCrImage, - public ImageD3D9 + +struct CairoD3D9BackendData : public ImageBackendData { -public: - PlanarYCbCrImageD3D9(); - ~PlanarYCbCrImageD3D9() {} + nsRefPtr mTexture; +}; - virtual void SetData(const Data &aData); - - /* - * Upload the data from out mData into our textures. For now we use this to - * make sure the textures are created and filled on the main thread. - */ - void AllocateTextures(IDirect3DDevice9 *aDevice); - /* - * XXX - * Free the textures, we call this from the main thread when we're done - * drawing this frame. We cannot free this from the constructor since it may - * be destroyed off the main-thread and might not be able to properly clean - * up its textures - */ - void FreeTextures(); - bool HasData() { return mHasData; } - - PRUint32 GetDataSize() { return mBuffer ? mBufferSize : 0; } - - virtual already_AddRefed GetAsSurface(); - - nsAutoArrayPtr mBuffer; - PRUint32 mBufferSize; - LayerManagerD3D9 *mManager; - Data mData; - gfxIntSize mSize; +struct PlanarYCbCrD3D9BackendData : public ImageBackendData +{ nsRefPtr mYTexture; nsRefPtr mCrTexture; nsRefPtr mCbTexture; - bool mHasData; -}; - - -class THEBES_API CairoImageD3D9 : public CairoImage, - public ImageD3D9 -{ -public: - CairoImageD3D9(IDirect3DDevice9 *aDevice) - : CairoImage(static_cast(this)) - , mDevice(aDevice) - { } - ~CairoImageD3D9(); - - virtual void SetData(const Data &aData); - - virtual already_AddRefed GetAsSurface(); - - IDirect3DDevice9 *device() { return mDevice; } - void SetDevice(IDirect3DDevice9 *aDevice); - - /** - * Uploading a texture may fail if the screen is locked. If this happens, - * we need to save the backing surface and retry when we are asked to paint. - */ - virtual IDirect3DTexture9* GetOrCreateTexture(); - const gfxIntSize& GetSize() { return mSize; } - - bool HasAlpha() { - return mCachedSurface->GetContentType() == - gfxASurface::CONTENT_COLOR_ALPHA; - } - -private: - gfxIntSize mSize; - nsRefPtr mCachedSurface; - nsRefPtr mDevice; - nsRefPtr mTexture; - LayerManagerD3D9 *mManager; }; class ShadowImageLayerD3D9 : public ShadowImageLayer, @@ -199,7 +105,7 @@ public: private: nsRefPtr mBuffer; - nsRefPtr mYCbCrImage; + nsRefPtr mYCbCrImage; }; } /* layers */ diff --git a/gfx/layers/d3d9/LayerManagerD3D9.cpp b/gfx/layers/d3d9/LayerManagerD3D9.cpp index 94099f10f5fa..c05e1ba3ebbe 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.cpp +++ b/gfx/layers/d3d9/LayerManagerD3D9.cpp @@ -239,13 +239,6 @@ LayerManagerD3D9::CreateReadbackLayer() return layer.forget(); } -already_AddRefed -LayerManagerD3D9::CreateImageContainer() -{ - nsRefPtr container = new ImageContainerD3D9(device()); - return container.forget(); -} - already_AddRefed LayerManagerD3D9::CreateShadowThebesLayer() { diff --git a/gfx/layers/d3d9/LayerManagerD3D9.h b/gfx/layers/d3d9/LayerManagerD3D9.h index 8d16255469eb..dd9ab588c100 100644 --- a/gfx/layers/d3d9/LayerManagerD3D9.h +++ b/gfx/layers/d3d9/LayerManagerD3D9.h @@ -165,8 +165,6 @@ public: virtual already_AddRefed CreateReadbackLayer(); - virtual already_AddRefed CreateImageContainer(); - virtual already_AddRefed CreateShadowThebesLayer(); virtual already_AddRefed CreateShadowContainerLayer(); virtual already_AddRefed CreateShadowImageLayer(); diff --git a/gfx/layers/opengl/ImageLayerOGL.cpp b/gfx/layers/opengl/ImageLayerOGL.cpp index 9370d230e006..fb9cc66d5d88 100644 --- a/gfx/layers/opengl/ImageLayerOGL.cpp +++ b/gfx/layers/opengl/ImageLayerOGL.cpp @@ -42,7 +42,6 @@ #include "gfxImageSurface.h" #include "yuv_convert.h" #include "GLContextProvider.h" -#include "MacIOSurfaceImageOGL.h" #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO) # include "GLXLibrary.h" # include "mozilla/X11Util.h" @@ -136,39 +135,13 @@ GLTexture::Release() mContext = nsnull; } -RecycleBin::RecycleBin() - : mLock("mozilla.layers.RecycleBin.mLock") +TextureRecycleBin::TextureRecycleBin() + : mLock("mozilla.layers.TextureRecycleBin.mLock") { } void -RecycleBin::RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize) -{ - MutexAutoLock lock(mLock); - - if (!mRecycledBuffers.IsEmpty() && aSize != mRecycledBufferSize) { - mRecycledBuffers.Clear(); - } - mRecycledBufferSize = aSize; - mRecycledBuffers.AppendElement(aBuffer); -} - -PRUint8* -RecycleBin::GetBuffer(PRUint32 aSize) -{ - MutexAutoLock lock(mLock); - - if (mRecycledBuffers.IsEmpty() || mRecycledBufferSize != aSize) - return new PRUint8[aSize]; - - PRUint32 last = mRecycledBuffers.Length() - 1; - PRUint8* result = mRecycledBuffers[last].forget(); - mRecycledBuffers.RemoveElementAt(last); - return result; -} - -void -RecycleBin::RecycleTexture(GLTexture *aTexture, TextureType aType, +TextureRecycleBin::RecycleTexture(GLTexture *aTexture, TextureType aType, const gfxIntSize& aSize) { MutexAutoLock lock(mLock); @@ -184,7 +157,7 @@ RecycleBin::RecycleTexture(GLTexture *aTexture, TextureType aType, } void -RecycleBin::GetTexture(TextureType aType, const gfxIntSize& aSize, +TextureRecycleBin::GetTexture(TextureType aType, const gfxIntSize& aSize, GLContext *aContext, GLTexture *aOutTexture) { MutexAutoLock lock(mLock); @@ -198,204 +171,40 @@ RecycleBin::GetTexture(TextureType aType, const gfxIntSize& aSize, mRecycledTextures[aType].RemoveElementAt(last); } -ImageContainerOGL::ImageContainerOGL(LayerManagerOGL *aManager) - : ImageContainer(aManager) - , mRecycleBin(new RecycleBin()) +struct THEBES_API MacIOSurfaceImageOGLBackendData : public ImageBackendData { -} + GLTexture mTexture; +}; -ImageContainerOGL::~ImageContainerOGL() -{ - if (mManager) { - NS_ASSERTION(mManager->GetBackendType() == LayerManager::LAYERS_OPENGL, "Wrong layer manager got assigned to ImageContainerOGL!"); - - static_cast(mManager)->ForgetImageContainer(this); - } -} - -already_AddRefed -ImageContainerOGL::CreateImage(const Image::Format *aFormats, - PRUint32 aNumFormats) -{ - if (!aNumFormats) { - return nsnull; - } - nsRefPtr img; - if (aFormats[0] == Image::PLANAR_YCBCR) { - img = new PlanarYCbCrImageOGL(static_cast(mManager), - mRecycleBin); - } else if (aFormats[0] == Image::CAIRO_SURFACE) { - img = new CairoImageOGL(static_cast(mManager)); - } #ifdef XP_MACOSX - else if (aFormats[0] == Image::MAC_IO_SURFACE) { - img = new MacIOSurfaceImageOGL(static_cast(mManager)); - } -#endif - - return img.forget(); -} - void -ImageContainerOGL::SetCurrentImage(Image *aImage) +AllocateTextureIOSurface(MacIOSurfaceImage *aIOImage, mozilla::gl::GLContext* aGL) { - nsRefPtr oldImage; + nsAutoPtr backendData( + new MacIOSurfaceImageOGLBackendData); - { - ReentrantMonitorAutoEnter mon(mReentrantMonitor); + backendData->mTexture.Allocate(aGL); + aGL->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, backendData->mTexture.GetTextureID()); + aGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, + LOCAL_GL_TEXTURE_MIN_FILTER, + LOCAL_GL_NEAREST); + aGL->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, + LOCAL_GL_TEXTURE_MAG_FILTER, + LOCAL_GL_NEAREST); - oldImage = mActiveImage.forget(); - mActiveImage = aImage; - CurrentImageChanged(); - } + void *nativeCtx = aGL->GetNativeData(GLContext::NativeGLContext); + NSOpenGLContext* nsCtx = (NSOpenGLContext*)nativeCtx; - // Make sure oldImage is released outside the lock, so it can take our - // lock in RecycleBuffer + aIOImage->GetIOSurface()->CGLTexImageIOSurface2D(nsCtx, + LOCAL_GL_RGBA, LOCAL_GL_BGRA, + LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV, 0); + + aGL->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0); + + aIOImage->SetBackendData(LayerManager::LAYERS_OPENGL, backendData.forget()); } - -already_AddRefed -ImageContainerOGL::GetCurrentImage() -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - - nsRefPtr retval = mActiveImage; - return retval.forget(); -} - -already_AddRefed -ImageContainerOGL::GetCurrentAsSurface(gfxIntSize *aSize) -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - - if (!mActiveImage) { - *aSize = gfxIntSize(0,0); - return nsnull; - } - - GLContext *gl = nsnull; - // tex1 will be RGBA or Y, tex2 will Cb, tex3 will be Cr - GLuint tex1 = 0; - gfxIntSize size; - - if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) { - PlanarYCbCrImageOGL *yuvImage = - static_cast(mActiveImage.get()); - if (!yuvImage->HasData()) { - *aSize = gfxIntSize(0, 0); - return nsnull; - } - - size = yuvImage->mData.mPicSize; - - nsRefPtr imageSurface = - new gfxImageSurface(size, gfxASurface::ImageFormatRGB24); - - gfx::YUVType type = - gfx::TypeFromSize(yuvImage->mData.mYSize.width, - yuvImage->mData.mYSize.height, - yuvImage->mData.mCbCrSize.width, - yuvImage->mData.mCbCrSize.height); - - gfx::ConvertYCbCrToRGB32(yuvImage->mData.mYChannel, - yuvImage->mData.mCbChannel, - yuvImage->mData.mCrChannel, - imageSurface->Data(), - yuvImage->mData.mPicX, - yuvImage->mData.mPicY, - size.width, - size.height, - yuvImage->mData.mYStride, - yuvImage->mData.mCbCrStride, - imageSurface->Stride(), - type); - - *aSize = size; - return imageSurface.forget().get(); - } - - if (mActiveImage->GetFormat() != Image::CAIRO_SURFACE) - { - *aSize = gfxIntSize(0, 0); - return nsnull; - } - - CairoImageOGL *cairoImage = - static_cast(mActiveImage.get()); - size = cairoImage->mSize; - gl = cairoImage->mTexture.GetGLContext(); - tex1 = cairoImage->mTexture.GetTextureID(); - - nsRefPtr s = gl->ReadTextureImage(tex1, size, LOCAL_GL_RGBA); - *aSize = size; - return s.forget(); -} - -gfxIntSize -ImageContainerOGL::GetCurrentSize() -{ - ReentrantMonitorAutoEnter mon(mReentrantMonitor); - if (!mActiveImage) { - return gfxIntSize(0,0); - } - - if (mActiveImage->GetFormat() == Image::PLANAR_YCBCR) { - PlanarYCbCrImageOGL *yuvImage = - static_cast(mActiveImage.get()); - if (!yuvImage->HasData()) { - return gfxIntSize(0,0); - } - return yuvImage->mSize; - } - - if (mActiveImage->GetFormat() == Image::CAIRO_SURFACE) { - CairoImageOGL *cairoImage = - static_cast(mActiveImage.get()); - return cairoImage->mSize; - } - -#ifdef XP_MACOSX - if (mActiveImage->GetFormat() == Image::MAC_IO_SURFACE) { - MacIOSurfaceImageOGL *ioImage = - static_cast(mActiveImage.get()); - return ioImage->mSize; - } #endif - return gfxIntSize(0,0); -} - -bool -ImageContainerOGL::SetLayerManager(LayerManager *aManager) -{ - if (!aManager) { - // the layer manager just entirely went away - - // XXX if we don't have context sharing, we should tell our images - // that their textures are no longer valid. - mManager = nsnull; - return true; - } - - if (aManager->GetBackendType() != LayerManager::LAYERS_OPENGL) { - return false; - } - - LayerManagerOGL* lmOld = static_cast(mManager); - LayerManagerOGL* lmNew = static_cast(aManager); - - if (lmOld) { - NS_ASSERTION(lmNew->glForResources() == lmOld->glForResources(), - "We require GL context sharing here!"); - lmOld->ForgetImageContainer(this); - } - - mManager = aManager; - - lmNew->RememberImageContainer(this); - - return true; -} - Layer* ImageLayerOGL::GetLayer() { @@ -417,27 +226,34 @@ ImageLayerOGL::RenderLayer(int, } if (image->GetFormat() == Image::PLANAR_YCBCR) { - PlanarYCbCrImageOGL *yuvImage = - static_cast(image.get()); + PlanarYCbCrImage *yuvImage = + static_cast(image.get()); - if (!yuvImage->HasData()) { + if (!yuvImage->mBufferSize) { return; } - - if (!yuvImage->HasTextures()) { - yuvImage->AllocateTextures(gl()); + + if (!yuvImage->GetBackendData(LayerManager::LAYERS_OPENGL)) { + AllocateTexturesYCbCr(yuvImage); } - yuvImage->UpdateTextures(gl()); + PlanarYCbCrOGLBackendData *data = + static_cast(yuvImage->GetBackendData(LayerManager::LAYERS_OPENGL)); + if (!data || data->mTextures->GetGLContext() != mOGLManager->glForResources()) { + // XXX - Can this ever happen? If so I need to fix this! + return; + } + + gl()->MakeCurrent(); gl()->fActiveTexture(LOCAL_GL_TEXTURE0); - gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, yuvImage->mTextures[0].GetTextureID()); + gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, data->mTextures[0].GetTextureID()); gl()->ApplyFilterToBoundTexture(mFilter); gl()->fActiveTexture(LOCAL_GL_TEXTURE1); - gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, yuvImage->mTextures[1].GetTextureID()); + gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, data->mTextures[1].GetTextureID()); gl()->ApplyFilterToBoundTexture(mFilter); gl()->fActiveTexture(LOCAL_GL_TEXTURE2); - gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, yuvImage->mTextures[2].GetTextureID()); + gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, data->mTextures[2].GetTextureID()); gl()->ApplyFilterToBoundTexture(mFilter); YCbCrTextureLayerProgram *program = mOGLManager->GetYCbCrLayerProgram(); @@ -460,16 +276,32 @@ ImageLayerOGL::RenderLayer(int, // someone else forgets. gl()->fActiveTexture(LOCAL_GL_TEXTURE0); } else if (image->GetFormat() == Image::CAIRO_SURFACE) { - CairoImageOGL *cairoImage = - static_cast(image.get()); + CairoImage *cairoImage = + static_cast(image.get()); - cairoImage->SetTiling(mUseTileSourceRect); + if (!cairoImage->mSurface) { + return; + } + + if (!cairoImage->GetBackendData(LayerManager::LAYERS_OPENGL)) { + AllocateTexturesCairo(cairoImage); + } + + CairoOGLBackendData *data = + static_cast(cairoImage->GetBackendData(LayerManager::LAYERS_OPENGL)); + + if (!data || data->mTexture.GetGLContext() != mOGLManager->glForResources()) { + // XXX - Can this ever happen? If so I need to fix this! + return; + } + + data->SetTiling(mUseTileSourceRect); gl()->MakeCurrent(); unsigned int iwidth = cairoImage->mSize.width; unsigned int iheight = cairoImage->mSize.height; gl()->fActiveTexture(LOCAL_GL_TEXTURE0); - gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, cairoImage->mTexture.GetTextureID()); + gl()->fBindTexture(LOCAL_GL_TEXTURE_2D, data->mTexture.GetTextureID()); #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO) GLXPixmap pixmap; @@ -484,7 +316,7 @@ ImageLayerOGL::RenderLayer(int, #endif ColorTextureLayerProgram *program = - mOGLManager->GetColorTextureLayerProgram(cairoImage->mLayerProgram); + mOGLManager->GetColorTextureLayerProgram(data->mLayerProgram); gl()->ApplyFilterToBoundTexture(mFilter); @@ -592,8 +424,8 @@ ImageLayerOGL::RenderLayer(int, #endif #ifdef XP_MACOSX } else if (image->GetFormat() == Image::MAC_IO_SURFACE) { - MacIOSurfaceImageOGL *ioImage = - static_cast(image.get()); + MacIOSurfaceImage *ioImage = + static_cast(image.get()); if (!mOGLManager->GetThebesLayerCallback()) { // If its an empty transaction we still need to update @@ -602,15 +434,24 @@ ImageLayerOGL::RenderLayer(int, ioImage->Update(GetContainer()); image = GetContainer()->GetCurrentImage(); gl()->MakeCurrent(); - ioImage = static_cast(image.get()); + ioImage = static_cast(image.get()); } if (!ioImage) { return; } - + gl()->fActiveTexture(LOCAL_GL_TEXTURE0); - gl()->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, ioImage->mTexture.GetTextureID()); + + if (!ioImage->GetBackendData(LayerManager::LAYERS_OPENGL)) { + AllocateTextureIOSurface(ioImage, gl()); + } + + MacIOSurfaceImageOGLBackendData *data = + static_cast(ioImage->GetBackendData(LayerManager::LAYERS_OPENGL)); + + gl()->fActiveTexture(LOCAL_GL_TEXTURE0); + gl()->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, data->mTexture.GetTextureID()); ColorTextureLayerProgram *program = mOGLManager->GetRGBARectLayerProgram(); @@ -618,7 +459,7 @@ ImageLayerOGL::RenderLayer(int, program->Activate(); if (program->GetTexCoordMultiplierUniformLocation() != -1) { // 2DRect case, get the multiplier right for a sampler2DRect - float f[] = { float(ioImage->mSize.width), float(ioImage->mSize.height) }; + float f[] = { float(ioImage->GetSize().width), float(ioImage->GetSize().height) }; program->SetUniform(program->GetTexCoordMultiplierUniformLocation(), 2, f); } else { @@ -626,8 +467,8 @@ ImageLayerOGL::RenderLayer(int, } program->SetLayerQuadRect(nsIntRect(0, 0, - ioImage->mSize.width, - ioImage->mSize.height)); + ioImage->GetSize().width, + ioImage->GetSize().height)); program->SetLayerTransform(GetEffectiveTransform()); program->SetLayerOpacity(GetEffectiveOpacity()); program->SetRenderOffset(aOffset); @@ -660,61 +501,6 @@ InitTexture(GLContext* aGL, GLuint aTexture, GLenum aFormat, const gfxIntSize& a NULL); } -PlanarYCbCrImageOGL::PlanarYCbCrImageOGL(LayerManagerOGL *aManager, - RecycleBin *aRecycleBin) - : PlanarYCbCrImage(nsnull), mRecycleBin(aRecycleBin), mHasData(false) -{ -#if 0 - // We really want to allocate this on the decode thread -- but to do that, - // we need to create a per-thread shared GL context, and it will only work - // if we have context sharing. For now, create the textures on the main - // thread the first time we render. - if (aManager) { - AllocateTextures(aManager->glForResources()); - } -#endif -} - -PlanarYCbCrImageOGL::~PlanarYCbCrImageOGL() -{ - if (mBuffer) { - mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize); - } - - if (HasTextures()) { - mRecycleBin->RecycleTexture(&mTextures[0], RecycleBin::TEXTURE_Y, mData.mYSize); - mRecycleBin->RecycleTexture(&mTextures[1], RecycleBin::TEXTURE_C, mData.mCbCrSize); - mRecycleBin->RecycleTexture(&mTextures[2], RecycleBin::TEXTURE_C, mData.mCbCrSize); - } -} - -void -PlanarYCbCrImageOGL::SetData(const PlanarYCbCrImage::Data &aData) -{ - // Recycle the previous image main-memory buffer now that we're about to get a new buffer - if (mBuffer) - mRecycleBin->RecycleBuffer(mBuffer.forget(), mBufferSize); - - mBuffer = CopyData(mData, mSize, mBufferSize, aData); - - mHasData = true; -} - -void -PlanarYCbCrImageOGL::AllocateTextures(mozilla::gl::GLContext *gl) -{ - gl->MakeCurrent(); - - mRecycleBin->GetTexture(RecycleBin::TEXTURE_Y, mData.mYSize, gl, &mTextures[0]); - InitTexture(gl, mTextures[0].GetTextureID(), LOCAL_GL_LUMINANCE, mData.mYSize); - - mRecycleBin->GetTexture(RecycleBin::TEXTURE_C, mData.mCbCrSize, gl, &mTextures[1]); - InitTexture(gl, mTextures[1].GetTextureID(), LOCAL_GL_LUMINANCE, mData.mCbCrSize); - - mRecycleBin->GetTexture(RecycleBin::TEXTURE_C, mData.mCbCrSize, gl, &mTextures[2]); - InitTexture(gl, mTextures[2].GetTextureID(), LOCAL_GL_LUMINANCE, mData.mCbCrSize); -} - static void UploadYUVToTexture(GLContext* gl, const PlanarYCbCrImage::Data& aData, GLTexture* aYTexture, @@ -745,62 +531,91 @@ UploadYUVToTexture(GLContext* gl, const PlanarYCbCrImage::Data& aData, gl->UploadSurfaceToTexture(surf, size, texture, true); } -void -PlanarYCbCrImageOGL::UpdateTextures(GLContext *gl) -{ - if (!mBuffer || !mHasData) - return; - - UploadYUVToTexture(gl, mData, &mTextures[0], &mTextures[1], &mTextures[2]); +ImageLayerOGL::ImageLayerOGL(LayerManagerOGL *aManager) + : ImageLayer(aManager, NULL) + , LayerOGL(aManager) + , mTextureRecycleBin(new TextureRecycleBin()) +{ + mImplData = static_cast(this); } -CairoImageOGL::CairoImageOGL(LayerManagerOGL *aManager) - : CairoImage(nsnull), mSize(0, 0), mTiling(false) +void +ImageLayerOGL::AllocateTexturesYCbCr(PlanarYCbCrImage *aImage) { - NS_ASSERTION(NS_IsMainThread(), "Should be on main thread to create a cairo image"); + if (!aImage->mBufferSize) + return; - if (aManager) { - // Allocate texture now to grab a reference to the GLContext - mTexture.Allocate(aManager->glForResources()); + nsAutoPtr backendData( + new PlanarYCbCrOGLBackendData); + + PlanarYCbCrImage::Data &data = aImage->mData; + + GLContext *gl = mOGLManager->glForResources(); + + gl->MakeCurrent(); + + mTextureRecycleBin->GetTexture(TextureRecycleBin::TEXTURE_Y, data.mYSize, gl, &backendData->mTextures[0]); + InitTexture(gl, backendData->mTextures[0].GetTextureID(), LOCAL_GL_LUMINANCE, data.mYSize); + + mTextureRecycleBin->GetTexture(TextureRecycleBin::TEXTURE_C, data.mCbCrSize, gl, &backendData->mTextures[1]); + InitTexture(gl, backendData->mTextures[1].GetTextureID(), LOCAL_GL_LUMINANCE, data.mCbCrSize); + + mTextureRecycleBin->GetTexture(TextureRecycleBin::TEXTURE_C, data.mCbCrSize, gl, &backendData->mTextures[2]); + InitTexture(gl, backendData->mTextures[2].GetTextureID(), LOCAL_GL_LUMINANCE, data.mCbCrSize); + + UploadYUVToTexture(gl, aImage->mData, + &backendData->mTextures[0], + &backendData->mTextures[1], + &backendData->mTextures[2]); + + backendData->mYSize = aImage->mData.mYSize; + backendData->mCbCrSize = aImage->mData.mCbCrSize; + backendData->mTextureRecycleBin = mTextureRecycleBin; + + aImage->SetBackendData(LayerManager::LAYERS_OPENGL, backendData.forget()); +} + +void +ImageLayerOGL::AllocateTexturesCairo(CairoImage *aImage) +{ + nsAutoPtr backendData( + new CairoOGLBackendData); + + GLTexture &texture = backendData->mTexture; + + texture.Allocate(mOGLManager->glForResources()); + + if (!texture.IsAllocated()) { + return; } -} -void -CairoImageOGL::SetData(const CairoImage::Data &aData) -{ -#if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO) - mSurface = nsnull; -#endif - - if (!mTexture.IsAllocated()) - return; - - mozilla::gl::GLContext *gl = mTexture.GetGLContext(); + mozilla::gl::GLContext *gl = texture.GetGLContext(); gl->MakeCurrent(); - GLuint tex = mTexture.GetTextureID(); + GLuint tex = texture.GetTextureID(); gl->fActiveTexture(LOCAL_GL_TEXTURE0); - mSize = aData.mSize; #if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO) - if (sGLXLibrary.SupportsTextureFromPixmap(aData.mSurface)) { - mSurface = aData.mSurface; - if (mSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA) { - mLayerProgram = gl::RGBALayerProgramType; + if (sGLXLibrary.SupportsTextureFromPixmap(aImage->mSurface)) { + if (aImage->mSurface->GetContentType() == gfxASurface::CONTENT_COLOR_ALPHA) { + backendData->mLayerProgram = gl::RGBALayerProgramType; } else { - mLayerProgram = gl::RGBXLayerProgramType; + backendData->mLayerProgram = gl::RGBXLayerProgramType; } + + aImage->SetBackendData(LayerManager::LAYERS_OPENGL, backendData.forget()); return; } #endif - - mLayerProgram = - gl->UploadSurfaceToTexture(aData.mSurface, - nsIntRect(0,0, mSize.width, mSize.height), + backendData->mLayerProgram = + gl->UploadSurfaceToTexture(aImage->mSurface, + nsIntRect(0,0, aImage->mSize.width, aImage->mSize.height), tex, true); + + aImage->SetBackendData(LayerManager::LAYERS_OPENGL, backendData.forget()); } -void CairoImageOGL::SetTiling(bool aTiling) +void CairoOGLBackendData::SetTiling(bool aTiling) { if (aTiling == mTiling) return; diff --git a/gfx/layers/opengl/ImageLayerOGL.h b/gfx/layers/opengl/ImageLayerOGL.h index 06fde6bc909a..682ae26e4224 100644 --- a/gfx/layers/opengl/ImageLayerOGL.h +++ b/gfx/layers/opengl/ImageLayerOGL.h @@ -90,23 +90,19 @@ private: }; /** - * A RecycleBin is owned by an ImageContainerOGL. We store buffers - * and textures in it that we want to recycle from one image to the next. - * It's a separate object from ImageContainerOGL because images need to store - * a strong ref to their RecycleBin and we must avoid creating a - * reference loop between an ImageContainerOGL and its active image. + * A RecycleBin is owned by an ImageLayer. We store textures in it that we + * want to recycle from one image to the next. It's a separate object from + * ImageContainer because images need to store a strong ref to their RecycleBin + * and we must avoid creating a reference loop between an ImageContainer and + * its active image. */ -class RecycleBin { - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(RecycleBin) +class TextureRecycleBin { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TextureRecycleBin) typedef mozilla::gl::GLContext GLContext; public: - RecycleBin(); - - void RecycleBuffer(PRUint8* aBuffer, PRUint32 aSize); - // Returns a recycled buffer of the right size, or allocates a new buffer. - PRUint8* GetBuffer(PRUint32 aSize); + TextureRecycleBin(); enum TextureType { TEXTURE_Y, @@ -125,53 +121,15 @@ private: // and mRecycledTextureSizes Mutex mLock; - // We should probably do something to prune this list on a timer so we don't - // eat excess memory while video is paused... - nsTArray > mRecycledBuffers; - // This is only valid if mRecycledBuffers is non-empty - PRUint32 mRecycledBufferSize; - nsTArray mRecycledTextures[2]; gfxIntSize mRecycledTextureSizes[2]; }; -class THEBES_API ImageContainerOGL : public ImageContainer -{ -public: - ImageContainerOGL(LayerManagerOGL *aManager); - virtual ~ImageContainerOGL(); - - virtual already_AddRefed CreateImage(const Image::Format* aFormats, - PRUint32 aNumFormats); - - virtual void SetCurrentImage(Image* aImage); - - virtual already_AddRefed GetCurrentImage(); - - virtual already_AddRefed GetCurrentAsSurface(gfxIntSize* aSize); - - virtual gfxIntSize GetCurrentSize(); - - virtual bool SetLayerManager(LayerManager *aManager); - - virtual LayerManager::LayersBackend GetBackendType() { return LayerManager::LAYERS_OPENGL; } - -private: - - nsRefPtr mRecycleBin; - nsRefPtr mActiveImage; -}; - class THEBES_API ImageLayerOGL : public ImageLayer, public LayerOGL { public: - ImageLayerOGL(LayerManagerOGL *aManager) - : ImageLayer(aManager, NULL) - , LayerOGL(aManager) - { - mImplData = static_cast(this); - } + ImageLayerOGL(LayerManagerOGL *aManager); ~ImageLayerOGL() { Destroy(); } // LayerOGL Implementation @@ -181,66 +139,43 @@ public: virtual void RenderLayer(int aPreviousFrameBuffer, const nsIntPoint& aOffset); virtual void CleanupResources() {} + + void AllocateTexturesYCbCr(PlanarYCbCrImage *aImage); + void AllocateTexturesCairo(CairoImage *aImage); + +protected: + nsRefPtr mTextureRecycleBin; }; -class THEBES_API PlanarYCbCrImageOGL : public PlanarYCbCrImage +struct THEBES_API PlanarYCbCrOGLBackendData : public ImageBackendData { - typedef mozilla::gl::GLContext GLContext; + ~PlanarYCbCrOGLBackendData() + { + if (HasTextures()) { + mTextureRecycleBin->RecycleTexture(&mTextures[0], TextureRecycleBin::TEXTURE_Y, mYSize); + mTextureRecycleBin->RecycleTexture(&mTextures[1], TextureRecycleBin::TEXTURE_C, mCbCrSize); + mTextureRecycleBin->RecycleTexture(&mTextures[2], TextureRecycleBin::TEXTURE_C, mCbCrSize); + } + } -public: - PlanarYCbCrImageOGL(LayerManagerOGL *aManager, - RecycleBin *aRecycleBin); - ~PlanarYCbCrImageOGL(); - - virtual void SetData(const Data &aData); - - /** - * Upload the data from out mData into our textures. For now we use this to - * make sure the textures are created and filled on the main thread. - */ - void AllocateTextures(GLContext *gl); - void UpdateTextures(GLContext *gl); - - bool HasData() { return mHasData; } bool HasTextures() { return mTextures[0].IsAllocated() && mTextures[1].IsAllocated() && mTextures[2].IsAllocated(); } - PRUint8* AllocateBuffer(PRUint32 aSize) { - return mRecycleBin->GetBuffer(aSize); - } - - PRUint32 GetDataSize() { return mBuffer ? mBufferSize : 0; } - - nsAutoArrayPtr mBuffer; - PRUint32 mBufferSize; - nsRefPtr mRecycleBin; GLTexture mTextures[3]; - Data mData; - gfxIntSize mSize; - bool mHasData; + gfxIntSize mYSize, mCbCrSize; + nsRefPtr mTextureRecycleBin; }; -class THEBES_API CairoImageOGL : public CairoImage +struct CairoOGLBackendData : public ImageBackendData { - typedef mozilla::gl::GLContext GLContext; - -public: - CairoImageOGL(LayerManagerOGL *aManager); - - void SetData(const Data &aData); - - GLTexture mTexture; - gfxIntSize mSize; - gl::ShaderProgramType mLayerProgram; -#if defined(MOZ_WIDGET_GTK2) && !defined(MOZ_PLATFORM_MAEMO) - nsRefPtr mSurface; -#endif + CairoOGLBackendData() : mLayerProgram(gl::RGBALayerProgramType), mTiling(false) {} void SetTiling(bool aTiling); -private: + GLTexture mTexture; + gl::ShaderProgramType mLayerProgram; bool mTiling; }; diff --git a/gfx/layers/opengl/LayerManagerOGL.cpp b/gfx/layers/opengl/LayerManagerOGL.cpp index ece7126019dd..41b6b319f842 100644 --- a/gfx/layers/opengl/LayerManagerOGL.cpp +++ b/gfx/layers/opengl/LayerManagerOGL.cpp @@ -102,14 +102,6 @@ LayerManagerOGL::Destroy() } mRoot = nsnull; - // Make a copy, since SetLayerManager will cause mImageContainers - // to get mutated. - nsTArray imageContainers(mImageContainers); - for (PRUint32 i = 0; i < imageContainers.Length(); ++i) { - ImageContainer *c = imageContainers[i]; - c->SetLayerManager(nsnull); - } - CleanupResources(); mDestroyed = true; @@ -477,19 +469,6 @@ LayerManagerOGL::CreateContainerLayer() return layer.forget(); } -already_AddRefed -LayerManagerOGL::CreateImageContainer() -{ - if (mDestroyed) { - NS_WARNING("Call on destroyed layer manager"); - return nsnull; - } - - nsRefPtr container = new ImageContainerOGL(this); - RememberImageContainer(container); - return container.forget(); -} - already_AddRefed LayerManagerOGL::CreateImageLayer() { @@ -526,26 +505,6 @@ LayerManagerOGL::CreateCanvasLayer() return layer.forget(); } -void -LayerManagerOGL::ForgetImageContainer(ImageContainer *aContainer) -{ - NS_ASSERTION(aContainer->Manager() == this, - "ForgetImageContainer called on non-owned container!"); - - if (!mImageContainers.RemoveElement(aContainer)) { - NS_WARNING("ForgetImageContainer couldn't find container it was supposed to forget!"); - return; - } -} - -void -LayerManagerOGL::RememberImageContainer(ImageContainer *aContainer) -{ - NS_ASSERTION(aContainer->Manager() == this, - "RememberImageContainer called on non-owned container!"); - mImageContainers.AppendElement(aContainer); -} - LayerOGL* LayerManagerOGL::RootLayer() const { diff --git a/gfx/layers/opengl/LayerManagerOGL.h b/gfx/layers/opengl/LayerManagerOGL.h index d5b6a7d33915..fda36d29f689 100644 --- a/gfx/layers/opengl/LayerManagerOGL.h +++ b/gfx/layers/opengl/LayerManagerOGL.h @@ -162,8 +162,6 @@ public: virtual already_AddRefed CreateCanvasLayer(); - virtual already_AddRefed CreateImageContainer(); - virtual already_AddRefed CreateShadowThebesLayer(); virtual already_AddRefed CreateShadowContainerLayer(); virtual already_AddRefed CreateShadowImageLayer(); @@ -173,16 +171,6 @@ public: virtual LayersBackend GetBackendType() { return LAYERS_OPENGL; } virtual void GetBackendName(nsAString& name) { name.AssignLiteral("OpenGL"); } - /** - * Image Container management. - */ - - /* Forget this image container. Should be called by ImageContainerOGL - * on its current layer manager before switching to a new one. - */ - void ForgetImageContainer(ImageContainer* aContainer); - void RememberImageContainer(ImageContainer* aContainer); - /** * Helper methods. */ @@ -423,11 +411,6 @@ private: already_AddRefed CreateContext(); - // The image containers that this layer manager has created. - // The destructor will tell the layer manager to remove - // it from the list. - nsTArray mImageContainers; - static ProgramType sLayerProgramTypes[]; /** Backbuffer */ diff --git a/gfx/layers/opengl/MacIOSurfaceImageOGL.h b/gfx/layers/opengl/MacIOSurfaceImageOGL.h deleted file mode 100644 index fac9ebc202a4..000000000000 --- a/gfx/layers/opengl/MacIOSurfaceImageOGL.h +++ /dev/null @@ -1,72 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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 Corporation code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Bas Schouten - * - * 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 GFX_MACIOSURFACEIMAGEOGL_H -#define GFX_MACIOSURFACEIMAGEOGL_H -#ifdef XP_MACOSX - -#include "nsCoreAnimationSupport.h" - -namespace mozilla { -namespace layers { - -class THEBES_API MacIOSurfaceImageOGL : public MacIOSurfaceImage -{ - typedef mozilla::gl::GLContext GLContext; - -public: - MacIOSurfaceImageOGL(LayerManagerOGL *aManager); - virtual ~MacIOSurfaceImageOGL(); - - void SetUpdateCallback(UpdateSurfaceCallback aCallback, void* aPluginInstanceOwner); - void SetDestroyCallback(DestroyCallback aCallback); - void Update(ImageContainer* aContainer); - - void SetData(const Data &aData); - - GLTexture mTexture; - gfxIntSize mSize; - nsRefPtr mIOSurface; - void* mPluginInstanceOwner; - UpdateSurfaceCallback mUpdateCallback; - DestroyCallback mDestroyCallback; -}; - -} /* layers */ -} /* mozilla */ -#endif /* XP_MACOSX */ -#endif /* GFX_MACIOSURFACEIMAGEOGL_H */ diff --git a/gfx/layers/opengl/MacIOSurfaceImageOGL.mm b/gfx/layers/opengl/MacIOSurfaceImageOGL.mm deleted file mode 100644 index 41c12097a7e9..000000000000 --- a/gfx/layers/opengl/MacIOSurfaceImageOGL.mm +++ /dev/null @@ -1,120 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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 Corporation code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Matt Woodrow - * - * 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 "ImageLayerOGL.h" -#include "MacIOSurfaceImageOGL.h" -#include -#include "OpenGL/OpenGL.h" - -using namespace mozilla::gl; - -namespace mozilla { -namespace layers { - -MacIOSurfaceImageOGL::MacIOSurfaceImageOGL(LayerManagerOGL *aManager) - : MacIOSurfaceImage(nsnull), mSize(0, 0), mPluginInstanceOwner(nsnull), - mUpdateCallback(nsnull), mDestroyCallback(nsnull) -{ - NS_ASSERTION(NS_IsMainThread(), "Should be on main thread to create a cairo image"); - - if (aManager) { - // Allocate texture now to grab a reference to the GLContext - GLContext *gl = aManager->glForResources(); - mTexture.Allocate(gl); - gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, mTexture.GetTextureID()); - gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, - LOCAL_GL_TEXTURE_MIN_FILTER, - LOCAL_GL_NEAREST); - gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, - LOCAL_GL_TEXTURE_MAG_FILTER, - LOCAL_GL_NEAREST); - gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0); - } -} - -MacIOSurfaceImageOGL::~MacIOSurfaceImageOGL() -{ - if (mDestroyCallback) { - mDestroyCallback(mPluginInstanceOwner); - } -} - -void -MacIOSurfaceImageOGL::SetData(const MacIOSurfaceImage::Data &aData) -{ - mIOSurface = nsIOSurface::LookupSurface(aData.mIOSurface->GetIOSurfaceID()); - mSize = gfxIntSize(mIOSurface->GetWidth(), mIOSurface->GetHeight()); - - GLContext *gl = mTexture.GetGLContext(); - gl->MakeCurrent(); - - gl->fActiveTexture(LOCAL_GL_TEXTURE0); - gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, mTexture.GetTextureID()); - - void *nativeCtx = gl->GetNativeData(GLContext::NativeGLContext); - NSOpenGLContext* nsCtx = (NSOpenGLContext*)nativeCtx; - - mIOSurface->CGLTexImageIOSurface2D((CGLContextObj)[nsCtx CGLContextObj], - LOCAL_GL_RGBA, LOCAL_GL_BGRA, - LOCAL_GL_UNSIGNED_INT_8_8_8_8_REV, 0); - - gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, 0); -} - -void -MacIOSurfaceImageOGL::SetUpdateCallback(UpdateSurfaceCallback aCallback, void* aPluginInstanceOwner) -{ - mUpdateCallback = aCallback; - mPluginInstanceOwner = aPluginInstanceOwner; -} - -void -MacIOSurfaceImageOGL::SetDestroyCallback(DestroyCallback aCallback) -{ - mDestroyCallback = aCallback; -} - -void -MacIOSurfaceImageOGL::Update(ImageContainer* aContainer) -{ - if (mUpdateCallback) { - mUpdateCallback(aContainer, mPluginInstanceOwner); - } -} - -} /* layers */ -} /* mozilla */ diff --git a/gfx/thebes/Makefile.in b/gfx/thebes/Makefile.in index ba9e4fcc3dc6..6a12281cf41f 100644 --- a/gfx/thebes/Makefile.in +++ b/gfx/thebes/Makefile.in @@ -79,6 +79,7 @@ EXPORTS = \ gfxUtils.h \ gfxUserFontSet.h \ nsCoreAnimationSupport.h \ + nsIOSurface.h \ gfxSharedImageSurface.h \ $(NULL) diff --git a/gfx/thebes/gfxASurface.h b/gfx/thebes/gfxASurface.h index 33fa4726fd7e..05895b090d06 100644 --- a/gfx/thebes/gfxASurface.h +++ b/gfx/thebes/gfxASurface.h @@ -41,6 +41,9 @@ #include "gfxTypes.h" #include "gfxRect.h" #include "nsAutoPtr.h" +#include "nsAutoRef.h" +#include "nsThreadUtils.h" + typedef struct _cairo_surface cairo_surface_t; typedef struct _cairo_user_data_key cairo_user_data_key_t; @@ -358,4 +361,56 @@ public: virtual ~gfxUnknownSurface() { } }; +#ifndef XPCOM_GLUE_AVOID_NSPR +/** + * We need to be able to hold a reference to a gfxASurface from Image + * subclasses. This is potentially a problem since Images can be addrefed + * or released off the main thread. We can ensure that we never AddRef + * a gfxASurface off the main thread, but we might want to Release due + * to an Image being destroyed off the main thread. + * + * We use nsCountedRef to reference the + * gfxASurface. When AddRefing, we assert that we're on the main thread. + * When Releasing, if we're not on the main thread, we post an event to + * the main thread to do the actual release. + */ +class nsMainThreadSurfaceRef; + +template <> +class nsAutoRefTraits { +public: + typedef gfxASurface* RawRef; + + /** + * The XPCOM event that will do the actual release on the main thread. + */ + class SurfaceReleaser : public nsRunnable { + public: + SurfaceReleaser(RawRef aRef) : mRef(aRef) {} + NS_IMETHOD Run() { + mRef->Release(); + return NS_OK; + } + RawRef mRef; + }; + + static RawRef Void() { return nsnull; } + static void Release(RawRef aRawRef) + { + if (NS_IsMainThread()) { + aRawRef->Release(); + return; + } + nsCOMPtr runnable = new SurfaceReleaser(aRawRef); + NS_DispatchToMainThread(runnable); + } + static void AddRef(RawRef aRawRef) + { + NS_ASSERTION(NS_IsMainThread(), + "Can only add a reference on the main thread"); + aRawRef->AddRef(); + } +}; + +#endif #endif /* GFX_ASURFACE_H */ diff --git a/gfx/thebes/gfxBlur.cpp b/gfx/thebes/gfxBlur.cpp index 963c294f4abe..f83013e8b194 100644 --- a/gfx/thebes/gfxBlur.cpp +++ b/gfx/thebes/gfxBlur.cpp @@ -58,16 +58,16 @@ gfxAlphaBoxBlur::Init(const gfxRect& aRect, const gfxRect* aDirtyRect, const gfxRect* aSkipRect) { - Rect rect(aRect.x, aRect.y, aRect.width, aRect.height); + mozilla::gfx::Rect rect(aRect.x, aRect.y, aRect.width, aRect.height); IntSize spreadRadius(aSpreadRadius.width, aSpreadRadius.height); IntSize blurRadius(aBlurRadius.width, aBlurRadius.height); - nsAutoPtr dirtyRect; + nsAutoPtr dirtyRect; if (aDirtyRect) { - dirtyRect = new Rect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height); + dirtyRect = new mozilla::gfx::Rect(aDirtyRect->x, aDirtyRect->y, aDirtyRect->width, aDirtyRect->height); } - nsAutoPtr skipRect; + nsAutoPtr skipRect; if (aSkipRect) { - skipRect = new Rect(aSkipRect->x, aSkipRect->y, aSkipRect->width, aSkipRect->height); + skipRect = new mozilla::gfx::Rect(aSkipRect->x, aSkipRect->y, aSkipRect->width, aSkipRect->height); } mBlur = new AlphaBoxBlur(rect, spreadRadius, blurRadius, dirtyRect, skipRect); @@ -106,7 +106,7 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset) mBlur->Blur(); - Rect* dirtyrect = mBlur->GetDirtyRect(); + mozilla::gfx::Rect* dirtyrect = mBlur->GetDirtyRect(); // Avoid a semi-expensive clip operation if we can, otherwise // clip to the dirty rect @@ -125,7 +125,7 @@ gfxAlphaBoxBlur::Paint(gfxContext* aDestinationCtx, const gfxPoint& offset) gfxIntSize gfxAlphaBoxBlur::CalculateBlurRadius(const gfxPoint& aStd) { - Point std(aStd.x, aStd.y); + mozilla::gfx::Point std(aStd.x, aStd.y); IntSize size = AlphaBoxBlur::CalculateBlurRadius(std); return gfxIntSize(size.width, size.height); } diff --git a/gfx/thebes/nsCoreAnimationSupport.h b/gfx/thebes/nsCoreAnimationSupport.h index aa467d2bb0e5..9be516505815 100644 --- a/gfx/thebes/nsCoreAnimationSupport.h +++ b/gfx/thebes/nsCoreAnimationSupport.h @@ -41,11 +41,12 @@ #define nsCoreAnimationSupport_h__ #ifdef XP_MACOSX -#import +#import #import "ApplicationServices/ApplicationServices.h" #include "nscore.h" #include "gfxTypes.h" #include "nsAutoPtr.h" +#include "nsIOSurface.h" // Get the system color space. CGColorSpaceRef THEBES_API CreateSystemColorSpace(); @@ -53,12 +54,9 @@ CGColorSpaceRef THEBES_API CreateSystemColorSpace(); // Manages a CARenderer struct _CGLPBufferObject; struct _CGLContextObject; -class nsIOSurface; enum AllowOfflineRendererEnum { ALLOW_OFFLINE_RENDERER, DISALLOW_OFFLINE_RENDERER }; -typedef uint32_t IOSurfaceID; - class THEBES_API nsCARenderer { NS_INLINE_DECL_REFCOUNTING(nsCARenderer) public: @@ -110,30 +108,6 @@ private: AllowOfflineRendererEnum mAllowOfflineRenderer; }; -class THEBES_API nsIOSurface { - NS_INLINE_DECL_REFCOUNTING(nsIOSurface) -public: - static already_AddRefed CreateIOSurface(int aWidth, int aHeight); - static void ReleaseIOSurface(nsIOSurface *aIOSurface); - static already_AddRefed LookupSurface(IOSurfaceID aSurfaceID); - - nsIOSurface(CFTypeRef aIOSurfacePtr) : mIOSurfacePtr(aIOSurfacePtr) {} - ~nsIOSurface() { CFRelease(mIOSurfacePtr); } - IOSurfaceID GetIOSurfaceID(); - void *GetBaseAddress(); - size_t GetWidth(); - size_t GetHeight(); - size_t GetBytesPerRow(); - void Lock(); - void Unlock(); - CGLError CGLTexImageIOSurface2D(CGLContextObj ctxt, - GLenum internalFormat, GLenum format, - GLenum type, GLuint plane); -private: - friend class nsCARenderer; - CFTypeRef mIOSurfacePtr; -}; - #endif // XP_MACOSX #endif // nsCoreAnimationSupport_h__ diff --git a/gfx/thebes/nsCoreAnimationSupport.mm b/gfx/thebes/nsCoreAnimationSupport.mm index f8fb76e497ee..ee635eeec7c3 100644 --- a/gfx/thebes/nsCoreAnimationSupport.mm +++ b/gfx/thebes/nsCoreAnimationSupport.mm @@ -202,7 +202,7 @@ CGLError nsIOSurfaceLib::CGLTexImageIOSurface2D(CGLContextObj ctxt, GLsizei width, GLsizei height, GLenum format, GLenum type, IOSurfacePtr ioSurface, GLuint plane) { - return sTexImage(ctxt, target, internalFormat, width, height, + return sTexImage(ctxt, target, internalFormat, width, height, format, type, ioSurface, plane); } @@ -267,6 +267,10 @@ void nsIOSurfaceLib::CloseLibrary() { sOpenGLFramework = nsnull; } +nsIOSurface::~nsIOSurface() { + CFRelease(mIOSurfacePtr); +} + already_AddRefed nsIOSurface::CreateIOSurface(int aWidth, int aHeight) { if (!nsIOSurfaceLib::isInit()) return nsnull; @@ -354,13 +358,37 @@ void nsIOSurface::Unlock() { nsIOSurfaceLib::IOSurfaceUnlock(mIOSurfacePtr, READ_ONLY, NULL); } +#include "gfxImageSurface.h" + +already_AddRefed +nsIOSurface::GetAsSurface() { + Lock(); + size_t bytesPerRow = GetBytesPerRow(); + size_t ioWidth = GetWidth(); + size_t ioHeight = GetHeight(); + + unsigned char* ioData = (unsigned char*)GetBaseAddress(); + + nsRefPtr imgSurface = + new gfxImageSurface(gfxIntSize(ioWidth, ioHeight), gfxASurface::ImageFormatARGB32); + + for (int i = 0; i < ioHeight; i++) { + memcpy(imgSurface->Data() + i * imgSurface->Stride(), + ioData + i * bytesPerRow, ioWidth * 4); + } + + Unlock(); + + return imgSurface.forget(); +} + CGLError -nsIOSurface::CGLTexImageIOSurface2D(CGLContextObj ctxt, +nsIOSurface::CGLTexImageIOSurface2D(NSOpenGLContext *ctxt, GLenum internalFormat, GLenum format, GLenum type, GLuint plane) { - return nsIOSurfaceLib::CGLTexImageIOSurface2D(ctxt, - GL_TEXTURE_RECTANGLE_ARB, + return nsIOSurfaceLib::CGLTexImageIOSurface2D((CGLContextObj)[ctxt CGLContextObj], + GL_TEXTURE_RECTANGLE_ARB, internalFormat, GetWidth(), GetHeight(), format, type, diff --git a/gfx/thebes/nsIOSurface.h b/gfx/thebes/nsIOSurface.h new file mode 100644 index 000000000000..93808915af74 --- /dev/null +++ b/gfx/thebes/nsIOSurface.h @@ -0,0 +1,79 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// vim:set ts=2 sts=2 sw=2 et cin: +/* ***** 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 Corporation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * bgirard + * + * 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 nsIOSurface_h__ +#define nsIOSurface_h__ +#ifdef XP_MACOSX + +#import + +class gfxASurface; +class _CGLContextObject; +class NSOpenGLContext; + +typedef _CGLContextObject* CGLContextObj; +typedef uint32_t IOSurfaceID; + +class THEBES_API nsIOSurface { + NS_INLINE_DECL_REFCOUNTING(nsIOSurface) +public: + static already_AddRefed CreateIOSurface(int aWidth, int aHeight); + static void ReleaseIOSurface(nsIOSurface *aIOSurface); + static already_AddRefed LookupSurface(IOSurfaceID aSurfaceID); + + nsIOSurface(const void *aIOSurfacePtr) : mIOSurfacePtr(aIOSurfacePtr) {} + ~nsIOSurface(); + IOSurfaceID GetIOSurfaceID(); + void *GetBaseAddress(); + size_t GetWidth(); + size_t GetHeight(); + size_t GetBytesPerRow(); + void Lock(); + void Unlock(); + CGLError CGLTexImageIOSurface2D(NSOpenGLContext *ctxt, + GLenum internalFormat, GLenum format, + GLenum type, GLuint plane); + already_AddRefed GetAsSurface(); +private: + friend class nsCARenderer; + const void* mIOSurfacePtr; +}; + +#endif +#endif diff --git a/image/public/imgIContainer.idl b/image/public/imgIContainer.idl index 43d41b3dd23a..5a632215103f 100644 --- a/image/public/imgIContainer.idl +++ b/image/public/imgIContainer.idl @@ -93,7 +93,7 @@ native gfxGraphicsFilter(gfxPattern::GraphicsFilter); * * Internally, imgIContainer also manages animation of images. */ -[scriptable, uuid(8c82b89f-f90c-4a31-a544-6e1f759673d4)] +[scriptable, uuid(2506249c-e0a1-4d8f-846c-2d478247f8d8)] interface imgIContainer : nsISupports { /** @@ -189,10 +189,8 @@ interface imgIContainer : nsISupports /** * Attempts to create an ImageContainer (and Image) containing the current * frame. Only valid for RASTER type images. - * - * @param aManager The layer manager to use to create the ImageContainer. */ - [noscript] ImageContainer getImageContainer(in LayerManager aManager); + [noscript] ImageContainer getImageContainer(); /** * Create and return a new copy of the given frame that you can write to diff --git a/image/src/RasterImage.cpp b/image/src/RasterImage.cpp index 37876a3e4a3f..2ed80f1aa781 100644 --- a/image/src/RasterImage.cpp +++ b/image/src/RasterImage.cpp @@ -945,13 +945,9 @@ RasterImage::GetFrame(PRUint32 aWhichFrame, NS_IMETHODIMP -RasterImage::GetImageContainer(LayerManager* aManager, - ImageContainer **_retval) +RasterImage::GetImageContainer(ImageContainer **_retval) { - if (mImageContainer && - (mImageContainer->Manager() == aManager || - (!mImageContainer->Manager() && - (mImageContainer->GetBackendType() == aManager->GetBackendType())))) { + if (mImageContainer) { *_retval = mImageContainer; NS_ADDREF(*_retval); return NS_OK; @@ -966,8 +962,7 @@ RasterImage::GetImageContainer(LayerManager* aManager, GetWidth(&cairoData.mSize.width); GetHeight(&cairoData.mSize.height); - mImageContainer = aManager->CreateImageContainer(); - NS_ASSERTION(mImageContainer, "Failed to create ImageContainer!"); + mImageContainer = LayerManager::CreateImageContainer(); // Now create a CairoImage to display the surface. layers::Image::Format cairoFormat = layers::Image::CAIRO_SURFACE; diff --git a/image/src/RasterImage.h b/image/src/RasterImage.h index f33bb8830c9a..282a1dc84a15 100644 --- a/image/src/RasterImage.h +++ b/image/src/RasterImage.h @@ -190,7 +190,7 @@ public: NS_SCRIPTABLE NS_IMETHOD GetAnimated(bool *aAnimated); NS_SCRIPTABLE NS_IMETHOD GetCurrentFrameIsOpaque(bool *aCurrentFrameIsOpaque); NS_IMETHOD GetFrame(PRUint32 aWhichFrame, PRUint32 aFlags, gfxASurface **_retval NS_OUTPARAM); - NS_IMETHOD GetImageContainer(mozilla::layers::LayerManager* aManager, mozilla::layers::ImageContainer **_retval NS_OUTPARAM); + NS_IMETHOD GetImageContainer(mozilla::layers::ImageContainer **_retval NS_OUTPARAM); NS_IMETHOD CopyFrame(PRUint32 aWhichFrame, PRUint32 aFlags, gfxImageSurface **_retval NS_OUTPARAM); NS_IMETHOD ExtractFrame(PRUint32 aWhichFrame, const nsIntRect & aRect, PRUint32 aFlags, imgIContainer **_retval NS_OUTPARAM); NS_IMETHOD Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter, const gfxMatrix & aUserSpaceToImageSpace, const gfxRect & aFill, const nsIntRect & aSubimage, const nsIntSize & aViewportSize, PRUint32 aFlags); diff --git a/image/src/VectorImage.h b/image/src/VectorImage.h index 7bf0aa94677d..3af8b314ae31 100644 --- a/image/src/VectorImage.h +++ b/image/src/VectorImage.h @@ -73,7 +73,7 @@ public: NS_SCRIPTABLE NS_IMETHOD GetAnimated(bool *aAnimated); NS_SCRIPTABLE NS_IMETHOD GetCurrentFrameIsOpaque(bool *aCurrentFrameIsOpaque); NS_IMETHOD GetFrame(PRUint32 aWhichFrame, PRUint32 aFlags, gfxASurface **_retval NS_OUTPARAM); - NS_IMETHOD GetImageContainer(mozilla::layers::LayerManager* aManager, mozilla::layers::ImageContainer **_retval NS_OUTPARAM) { *_retval = NULL; return NS_OK; } + NS_IMETHOD GetImageContainer(mozilla::layers::ImageContainer **_retval NS_OUTPARAM) { *_retval = NULL; return NS_OK; } NS_IMETHOD CopyFrame(PRUint32 aWhichFrame, PRUint32 aFlags, gfxImageSurface **_retval NS_OUTPARAM); NS_IMETHOD ExtractFrame(PRUint32 aWhichFrame, const nsIntRect & aRect, PRUint32 aFlags, imgIContainer **_retval NS_OUTPARAM); NS_IMETHOD Draw(gfxContext *aContext, gfxPattern::GraphicsFilter aFilter, const gfxMatrix & aUserSpaceToImageSpace, const gfxRect & aFill, const nsIntRect & aSubimage, const nsIntSize & aViewportSize, PRUint32 aFlags); diff --git a/layout/base/FrameLayerBuilder.cpp b/layout/base/FrameLayerBuilder.cpp index 5b60d01b18db..fac2737d7f0e 100644 --- a/layout/base/FrameLayerBuilder.cpp +++ b/layout/base/FrameLayerBuilder.cpp @@ -219,7 +219,7 @@ protected: * supports being optimized to an ImageLayer (TYPE_RASTER only) returns * an ImageContainer for the image. */ - already_AddRefed CanOptimizeImageLayer(LayerManager* aManager); + already_AddRefed CanOptimizeImageLayer(); /** * The region of visible content in the layer, relative to the @@ -969,13 +969,13 @@ ContainerState::FindOpaqueBackgroundColorFor(PRInt32 aThebesLayerIndex) } already_AddRefed -ContainerState::ThebesLayerData::CanOptimizeImageLayer(LayerManager* aManager) +ContainerState::ThebesLayerData::CanOptimizeImageLayer() { if (!mImage || !mImageClip.mRoundedClipRects.IsEmpty()) { return nsnull; } - return mImage->GetContainer(aManager); + return mImage->GetContainer(); } void @@ -987,7 +987,7 @@ ContainerState::PopThebesLayerData() ThebesLayerData* data = mThebesLayerDataStack[lastIndex]; nsRefPtr layer; - nsRefPtr imageContainer = data->CanOptimizeImageLayer(mManager); + nsRefPtr imageContainer = data->CanOptimizeImageLayer(); if (data->mIsSolidColorInVisibleRegion || imageContainer) { NS_ASSERTION(!(data->mIsSolidColorInVisibleRegion && imageContainer), diff --git a/layout/generic/nsImageFrame.cpp b/layout/generic/nsImageFrame.cpp index 6f64fbe02566..d3c0ef741742 100644 --- a/layout/generic/nsImageFrame.cpp +++ b/layout/generic/nsImageFrame.cpp @@ -1211,10 +1211,10 @@ nsDisplayImage::Paint(nsDisplayListBuilder* aBuilder, } already_AddRefed -nsDisplayImage::GetContainer(LayerManager* aManager) +nsDisplayImage::GetContainer() { nsRefPtr container; - nsresult rv = mImage->GetImageContainer(aManager, getter_AddRefs(container)); + nsresult rv = mImage->GetImageContainer(getter_AddRefs(container)); NS_ENSURE_SUCCESS(rv, nsnull); return container.forget(); } diff --git a/layout/generic/nsImageFrame.h b/layout/generic/nsImageFrame.h index 369468a796a1..ea38a5835db0 100644 --- a/layout/generic/nsImageFrame.h +++ b/layout/generic/nsImageFrame.h @@ -391,7 +391,7 @@ public: * Returns an ImageContainer for this image if the image type * supports it (TYPE_RASTER only). */ - already_AddRefed GetContainer(LayerManager* aManager); + already_AddRefed GetContainer(); /** * Configure an ImageLayer for this display item. diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 8ff81b06b8b9..102637134917 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -1495,40 +1495,15 @@ nsObjectFrame::PrintPlugin(nsRenderingContext& aRenderingContext, } already_AddRefed -nsObjectFrame::GetImageContainer(LayerManager* aManager) +nsObjectFrame::GetImageContainer() { - nsRefPtr manager = aManager; - bool retain = false; + nsRefPtr container = mImageContainer; - if (!manager) { - manager = nsContentUtils::LayerManagerForDocument(mContent->OwnerDoc(), &retain); - } - if (!manager) { - return nsnull; + if (container) { + return container.forget(); } - nsRefPtr container; - - // XXX - in the future image containers will be manager independent and - // we can remove the manager equals check and only check the backend type. - if (mImageContainer) { - if ((!mImageContainer->Manager() || mImageContainer->Manager() == manager) && - mImageContainer->GetBackendType() == manager->GetBackendType()) { - container = mImageContainer; - return container.forget(); - } - } - - container = manager->CreateImageContainer(); - - if (retain) { - // Clear current image before we reset mImageContainer. Only mImageContainer - // is allowed to contain the image for this plugin. - if (mImageContainer) { - mImageContainer->SetCurrentImage(nsnull); - } - mImageContainer = container; - } + container = mImageContainer = LayerManager::CreateImageContainer(); return container.forget(); } @@ -1573,13 +1548,6 @@ nsObjectFrame::GetLayerState(nsDisplayListBuilder* aBuilder, return LAYER_NONE; #ifdef XP_MACOSX - // Layer painting not supported without OpenGL - if (aManager && aManager->GetBackendType() != - LayerManager::LAYERS_OPENGL) { - return LAYER_NONE; - } - - // Synchronous painting, but with (gecko) layers. if (!mInstanceOwner->UseAsyncRendering() && mInstanceOwner->IsRemoteDrawingCoreAnimation() && mInstanceOwner->GetEventModel() == NPEventModelCocoa) { @@ -1611,9 +1579,7 @@ nsObjectFrame::BuildLayer(nsDisplayListBuilder* aBuilder, return nsnull; // Create image - nsRefPtr container = GetImageContainer(aManager); - if (!container) - return nsnull; + nsRefPtr container = GetImageContainer(); { nsRefPtr current = container->GetCurrentImage(); diff --git a/layout/generic/nsObjectFrame.h b/layout/generic/nsObjectFrame.h index c5adfce7f44d..0a01b2a853e6 100644 --- a/layout/generic/nsObjectFrame.h +++ b/layout/generic/nsObjectFrame.h @@ -186,7 +186,7 @@ public: virtual LayerState GetLayerState(nsDisplayListBuilder* aBuilder, LayerManager* aManager); - already_AddRefed GetImageContainer(LayerManager* aManager = nsnull); + already_AddRefed GetImageContainer(); /** * Get the rectangle (relative to this frame) which it will paint. Normally * the frame's content-box but may be smaller if the plugin is rendering diff --git a/layout/generic/nsVideoFrame.cpp b/layout/generic/nsVideoFrame.cpp index ee5746e34039..90253950c906 100644 --- a/layout/generic/nsVideoFrame.cpp +++ b/layout/generic/nsVideoFrame.cpp @@ -197,59 +197,7 @@ nsVideoFrame::BuildLayer(nsDisplayListBuilder* aBuilder, return nsnull; nsRefPtr container = element->GetImageContainer(); - // If we have a container with a different layer manager, try to hand - // off the container to the new one. - if (container && container->Manager() != aManager) { - // we don't care about the return type here -- if the set didn't take, it'll - // be handled when we next check the manager - container->SetLayerManager(aManager); - } - - // If we have a container of the correct type already, we don't need - // to do anything here. Otherwise we need to set up a temporary - // ImageContainer, capture the video data and store it in the temp - // container. For now we also check if the manager is equal since not all - // image containers are manager independent yet. - if (!container || - (container->Manager() && container->Manager() != aManager) || - container->GetBackendType() != aManager->GetBackendType()) - { - nsRefPtr tmpContainer = aManager->CreateImageContainer(); - if (!tmpContainer) - return nsnull; - - // We get a reference to the video data as a cairo surface. - CairoImage::Data cairoData; - nsRefPtr imageSurface; - if (container) { - // Get video from the existing container. It was created for a - // different layer manager, so we do fallback through cairo. - imageSurface = container->GetCurrentAsSurface(&cairoData.mSize); - if (!imageSurface) { - // we couldn't do fallback, so we've got nothing to do here - return nsnull; - } - cairoData.mSurface = imageSurface; - } else { - // We're probably printing. - cairoData.mSurface = element->GetPrintSurface(); - if (!cairoData.mSurface) - return nsnull; - cairoData.mSize = gfxIntSize(videoSize.width, videoSize.height); - } - - // Now create a CairoImage to display the surface. - Image::Format cairoFormat = Image::CAIRO_SURFACE; - nsRefPtr image = tmpContainer->CreateImage(&cairoFormat, 1); - if (!image) - return nsnull; - - NS_ASSERTION(image->GetFormat() == cairoFormat, "Wrong format"); - static_cast(image.get())->SetData(cairoData); - tmpContainer->SetCurrentImage(image); - container = tmpContainer.forget(); - } - + // Retrieve the size of the decoded video frame, before being scaled // by pixel aspect ratio. gfxIntSize frameSize = container->GetCurrentSize(); From 7a3abc1cf709c87aa5db1d45892488009a26cd83 Mon Sep 17 00:00:00 2001 From: James Willcox Date: Tue, 31 Jan 2012 15:47:42 -0500 Subject: [PATCH 06/90] Use Android SDK 14 in order to support some Ice Cream Sandwich features, no bug r=blassey --HG-- extra : rebase_source : 3f1ce0a852d2ffcd2a0570b597f08ac58bb11117 --- mobile/android/config/mozconfigs/android/debug | 4 ++-- mobile/android/config/mozconfigs/android/nightly | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/android/config/mozconfigs/android/debug b/mobile/android/config/mozconfigs/android/debug index 09933855668f..4bb953355c9c 100644 --- a/mobile/android/config/mozconfigs/android/debug +++ b/mobile/android/config/mozconfigs/android/debug @@ -9,8 +9,8 @@ ac_add_options --enable-application=mobile/android ac_add_options --target=arm-linux-androideabi ac_add_options --with-endian=little ac_add_options --with-android-ndk="/tools/android-ndk-r5c" -ac_add_options --with-android-sdk="/tools/android-sdk-r13/platforms/android-13" -ac_add_options --with-android-tools="/tools/android-sdk-r13/tools" +ac_add_options --with-android-sdk="/tools/android-sdk-r15/platforms/android-14" +ac_add_options --with-android-tools="/tools/android-sdk-r15/tools" ac_add_options --with-android-toolchain=/tools/android-ndk-r5c/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86 ac_add_options --with-android-platform=/tools/android-ndk-r5c/platforms/android-5/arch-arm ac_add_options --with-system-zlib diff --git a/mobile/android/config/mozconfigs/android/nightly b/mobile/android/config/mozconfigs/android/nightly index b766f04ba11a..e7e128c38b5d 100644 --- a/mobile/android/config/mozconfigs/android/nightly +++ b/mobile/android/config/mozconfigs/android/nightly @@ -8,8 +8,8 @@ ac_add_options --enable-application=mobile/android ac_add_options --target=arm-linux-androideabi ac_add_options --with-endian=little ac_add_options --with-android-ndk="/tools/android-ndk-r5c" -ac_add_options --with-android-sdk="/tools/android-sdk-r13/platforms/android-13" -ac_add_options --with-android-tools="/tools/android-sdk-r13/tools" +ac_add_options --with-android-sdk="/tools/android-sdk-r15/platforms/android-14" +ac_add_options --with-android-tools="/tools/android-sdk-r15/tools" ac_add_options --with-android-toolchain=/tools/android-ndk-r5c/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86 ac_add_options --with-android-platform=/tools/android-ndk-r5c/platforms/android-5/arch-arm ac_add_options --with-system-zlib From d51e04363001cde68ba515942e88b0eec95498af Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 31 Jan 2012 22:40:49 -0500 Subject: [PATCH 07/90] Bug 722729. Expose a public API for LinkConstructorAndPrototype. r=bhackett --- js/src/jsapi.cpp | 6 ++++++ js/src/jsapi.h | 7 +++++++ js/xpconnect/src/dombindings.cpp | 11 +++-------- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index a823b7179559..a58fc70f0176 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -3133,6 +3133,12 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, nargs, ps, fs, static_ps, static_fs); } +JS_PUBLIC_API(JSBool) +JS_LinkConstructorAndPrototype(JSContext *cx, JSObject *ctor, JSObject *proto) +{ + return LinkConstructorAndPrototype(cx, ctor, proto); +} + #ifdef JS_THREADSAFE JS_PUBLIC_API(JSClass *) JS_GetClass(JSContext *cx, JSObject *obj) diff --git a/js/src/jsapi.h b/js/src/jsapi.h index b01be90df713..2f0fb8b39169 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3645,6 +3645,13 @@ JS_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto, JSPropertySpec *ps, JSFunctionSpec *fs, JSPropertySpec *static_ps, JSFunctionSpec *static_fs); +/* + * Set up ctor.prototype = proto and proto.constructor = ctor with the + * right property flags. + */ +extern JS_PUBLIC_API(JSBool) +JS_LinkConstructorAndPrototype(JSContext *cx, JSObject *ctor, JSObject *proto); + #ifdef JS_THREADSAFE extern JS_PUBLIC_API(JSClass *) JS_GetClass(JSContext *cx, JSObject *obj); diff --git a/js/xpconnect/src/dombindings.cpp b/js/xpconnect/src/dombindings.cpp index 949e9db149dd..793a98258f97 100644 --- a/js/xpconnect/src/dombindings.cpp +++ b/js/xpconnect/src/dombindings.cpp @@ -59,7 +59,6 @@ namespace dom { namespace binding { -static jsid s_constructor_id = JSID_VOID; static jsid s_prototype_id = JSID_VOID; static jsid s_length_id = JSID_VOID; @@ -82,8 +81,7 @@ DefineStaticJSVals(JSContext *cx) { JSAutoRequest ar(cx); - return SET_JSID_TO_STRING(cx, constructor) && - SET_JSID_TO_STRING(cx, prototype) && + return SET_JSID_TO_STRING(cx, prototype) && SET_JSID_TO_STRING(cx, length) && DefinePropertyStaticJSVals(cx); } @@ -452,13 +450,10 @@ ListBase::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope) } JSObject *interface = JS_NewObject(cx, Jsvalify(&sInterfaceClass), NULL, global); - if (!interface || - !JS_DefinePropertyById(cx, interface, s_prototype_id, OBJECT_TO_JSVAL(interfacePrototype), - nsnull, nsnull, JSPROP_PERMANENT | JSPROP_READONLY)) + if (!interface) return NULL; - if (!JS_DefinePropertyById(cx, interfacePrototype, s_constructor_id, - OBJECT_TO_JSVAL(interface), nsnull, nsnull, 0)) + if (!JS_LinkConstructorAndPrototype(cx, interface, interfacePrototype)) return NULL; if (!JS_DefineProperty(cx, global, sInterfaceClass.name, OBJECT_TO_JSVAL(interface), NULL, From b79642be0c7eb097a9202607fbcc76149b38bf59 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 31 Jan 2012 23:32:40 -0500 Subject: [PATCH 08/90] Backed out changeset a3ea1a70905f (no bug) for breaking Android builds. --- mobile/android/config/mozconfigs/android/debug | 4 ++-- mobile/android/config/mozconfigs/android/nightly | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/android/config/mozconfigs/android/debug b/mobile/android/config/mozconfigs/android/debug index 4bb953355c9c..09933855668f 100644 --- a/mobile/android/config/mozconfigs/android/debug +++ b/mobile/android/config/mozconfigs/android/debug @@ -9,8 +9,8 @@ ac_add_options --enable-application=mobile/android ac_add_options --target=arm-linux-androideabi ac_add_options --with-endian=little ac_add_options --with-android-ndk="/tools/android-ndk-r5c" -ac_add_options --with-android-sdk="/tools/android-sdk-r15/platforms/android-14" -ac_add_options --with-android-tools="/tools/android-sdk-r15/tools" +ac_add_options --with-android-sdk="/tools/android-sdk-r13/platforms/android-13" +ac_add_options --with-android-tools="/tools/android-sdk-r13/tools" ac_add_options --with-android-toolchain=/tools/android-ndk-r5c/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86 ac_add_options --with-android-platform=/tools/android-ndk-r5c/platforms/android-5/arch-arm ac_add_options --with-system-zlib diff --git a/mobile/android/config/mozconfigs/android/nightly b/mobile/android/config/mozconfigs/android/nightly index e7e128c38b5d..b766f04ba11a 100644 --- a/mobile/android/config/mozconfigs/android/nightly +++ b/mobile/android/config/mozconfigs/android/nightly @@ -8,8 +8,8 @@ ac_add_options --enable-application=mobile/android ac_add_options --target=arm-linux-androideabi ac_add_options --with-endian=little ac_add_options --with-android-ndk="/tools/android-ndk-r5c" -ac_add_options --with-android-sdk="/tools/android-sdk-r15/platforms/android-14" -ac_add_options --with-android-tools="/tools/android-sdk-r15/tools" +ac_add_options --with-android-sdk="/tools/android-sdk-r13/platforms/android-13" +ac_add_options --with-android-tools="/tools/android-sdk-r13/tools" ac_add_options --with-android-toolchain=/tools/android-ndk-r5c/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86 ac_add_options --with-android-platform=/tools/android-ndk-r5c/platforms/android-5/arch-arm ac_add_options --with-system-zlib From 0b93e57f7a707b59dae837f14850b47d973c4926 Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 31 Jan 2012 23:36:47 -0500 Subject: [PATCH 09/90] Backed out changeset 614b573550d6, effectively relanding changeset a3ea1a70905f. --- mobile/android/config/mozconfigs/android/debug | 4 ++-- mobile/android/config/mozconfigs/android/nightly | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/android/config/mozconfigs/android/debug b/mobile/android/config/mozconfigs/android/debug index 09933855668f..4bb953355c9c 100644 --- a/mobile/android/config/mozconfigs/android/debug +++ b/mobile/android/config/mozconfigs/android/debug @@ -9,8 +9,8 @@ ac_add_options --enable-application=mobile/android ac_add_options --target=arm-linux-androideabi ac_add_options --with-endian=little ac_add_options --with-android-ndk="/tools/android-ndk-r5c" -ac_add_options --with-android-sdk="/tools/android-sdk-r13/platforms/android-13" -ac_add_options --with-android-tools="/tools/android-sdk-r13/tools" +ac_add_options --with-android-sdk="/tools/android-sdk-r15/platforms/android-14" +ac_add_options --with-android-tools="/tools/android-sdk-r15/tools" ac_add_options --with-android-toolchain=/tools/android-ndk-r5c/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86 ac_add_options --with-android-platform=/tools/android-ndk-r5c/platforms/android-5/arch-arm ac_add_options --with-system-zlib diff --git a/mobile/android/config/mozconfigs/android/nightly b/mobile/android/config/mozconfigs/android/nightly index b766f04ba11a..e7e128c38b5d 100644 --- a/mobile/android/config/mozconfigs/android/nightly +++ b/mobile/android/config/mozconfigs/android/nightly @@ -8,8 +8,8 @@ ac_add_options --enable-application=mobile/android ac_add_options --target=arm-linux-androideabi ac_add_options --with-endian=little ac_add_options --with-android-ndk="/tools/android-ndk-r5c" -ac_add_options --with-android-sdk="/tools/android-sdk-r13/platforms/android-13" -ac_add_options --with-android-tools="/tools/android-sdk-r13/tools" +ac_add_options --with-android-sdk="/tools/android-sdk-r15/platforms/android-14" +ac_add_options --with-android-tools="/tools/android-sdk-r15/tools" ac_add_options --with-android-toolchain=/tools/android-ndk-r5c/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86 ac_add_options --with-android-platform=/tools/android-ndk-r5c/platforms/android-5/arch-arm ac_add_options --with-system-zlib From 641cc99143123d10824e0b7c26eecf0dbcf8a31d Mon Sep 17 00:00:00 2001 From: Boris Zbarsky Date: Tue, 31 Jan 2012 23:38:35 -0500 Subject: [PATCH 10/90] Backed out changeset e57ddd9fdd5e (bug 721741) because it breaks Android builds. --- dom/plugins/base/Makefile.in | 1 - dom/plugins/base/android/ANPAudio.cpp | 21 +- dom/plugins/base/android/ANPBase.h | 13 +- dom/plugins/base/android/ANPNativeWindow.cpp | 85 ------ dom/plugins/base/android/ANPOpenGL.cpp | 65 ----- dom/plugins/base/android/ANPSystem.cpp | 25 -- dom/plugins/base/android/ANPVideo.cpp | 109 -------- dom/plugins/base/android/ANPWindow.cpp | 55 ---- dom/plugins/base/android/Makefile.in | 5 - dom/plugins/base/android/android_npapi.h | 158 +---------- dom/plugins/base/nsNPAPIPlugin.cpp | 71 +---- dom/plugins/base/nsNPAPIPluginInstance.h | 2 - dom/plugins/base/nsPluginInstanceOwner.cpp | 121 +++----- dom/plugins/base/nsPluginInstanceOwner.h | 37 +-- layout/generic/nsObjectFrame.cpp | 1 + mobile/android/base/GeckoApp.java | 134 +-------- mobile/android/base/GeckoAppShell.java | 35 +-- mobile/android/base/Makefile.in | 2 - mobile/android/base/Tab.java | 21 -- mobile/android/base/Tabs.java | 4 +- mobile/android/base/gfx/Layer.java | 5 +- mobile/android/base/gfx/LayerRenderer.java | 34 --- mobile/android/base/gfx/LayerView.java | 8 - .../android/base/gfx/SurfaceTextureLayer.java | 259 ------------------ mobile/android/base/gfx/TextureGenerator.java | 73 ----- mobile/android/base/gfx/TextureReaper.java | 6 +- mobile/android/base/ui/PanZoomController.java | 12 +- other-licenses/skia-npapi/Makefile.in | 1 - widget/android/AndroidBridge.cpp | 86 +----- widget/android/AndroidBridge.h | 8 +- widget/android/AndroidMediaLayer.cpp | 155 ----------- widget/android/AndroidMediaLayer.h | 96 ------- widget/android/Makefile.in | 1 - widget/android/nsWindow.cpp | 2 +- 34 files changed, 72 insertions(+), 1639 deletions(-) delete mode 100644 dom/plugins/base/android/ANPNativeWindow.cpp delete mode 100644 dom/plugins/base/android/ANPOpenGL.cpp delete mode 100644 dom/plugins/base/android/ANPVideo.cpp delete mode 100644 mobile/android/base/gfx/SurfaceTextureLayer.java delete mode 100644 mobile/android/base/gfx/TextureGenerator.java delete mode 100644 widget/android/AndroidMediaLayer.cpp delete mode 100644 widget/android/AndroidMediaLayer.h diff --git a/dom/plugins/base/Makefile.in b/dom/plugins/base/Makefile.in index d91f793ed684..1b67bfff57c5 100644 --- a/dom/plugins/base/Makefile.in +++ b/dom/plugins/base/Makefile.in @@ -138,7 +138,6 @@ endif LOCAL_INCLUDES += \ -DSK_BUILD_FOR_ANDROID_NDK \ - -I$(topsrcdir)/widget/android \ -I$(topsrcdir)/xpcom/base/ \ -I$(topsrcdir)/gfx/skia/include/core \ -I$(topsrcdir)/gfx/skia/include/config \ diff --git a/dom/plugins/base/android/ANPAudio.cpp b/dom/plugins/base/android/ANPAudio.cpp index c3398de9b8d8..003616842680 100644 --- a/dom/plugins/base/android/ANPAudio.cpp +++ b/dom/plugins/base/android/ANPAudio.cpp @@ -306,6 +306,7 @@ anp_audio_start(ANPAudioTrack* s) if (s->keepGoing) { // we are already playing. Ignore. + LOG("anp_audio_start called twice!"); return; } @@ -358,14 +359,7 @@ anp_audio_isStopped(ANPAudioTrack* s) return s->isStopped; } -uint32_t -anp_audio_trackLatency(ANPAudioTrack* s) { - // Bug 721835 - NOT_IMPLEMENTED(); - return 1; -} - -void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i) { +void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i) { _assert(i->inSize == sizeof(*i)); ASSIGN(i, newTrack); ASSIGN(i, deleteTrack); @@ -374,14 +368,3 @@ void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i) { ASSIGN(i, stop); ASSIGN(i, isStopped); } - -void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, newTrack); - ASSIGN(i, deleteTrack); - ASSIGN(i, start); - ASSIGN(i, pause); - ASSIGN(i, stop); - ASSIGN(i, isStopped); - ASSIGN(i, trackLatency); -} diff --git a/dom/plugins/base/android/ANPBase.h b/dom/plugins/base/android/ANPBase.h index 1553e698ff60..729de6a5a342 100644 --- a/dom/plugins/base/android/ANPBase.h +++ b/dom/plugins/base/android/ANPBase.h @@ -36,8 +36,8 @@ * * ***** END LICENSE BLOCK ***** */ -#include #include "android_npapi.h" +#include #include "nsAutoPtr.h" #include "nsISupportsImpl.h" @@ -53,8 +53,7 @@ "!!!!!!!!!!!!!! %s not implemented %s, %d", \ __PRETTY_FUNCTION__, __FILE__, __LINE__); \ -void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i); -void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1* i); +void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i); void InitBitmapInterface(ANPBitmapInterfaceV0 *i); void InitCanvasInterface(ANPCanvasInterfaceV0 *i); void InitEventInterface(ANPEventInterfaceV0 *i); @@ -64,13 +63,5 @@ void InitPaintInterface(ANPPaintInterfaceV0 *i); void InitPathInterface(ANPPathInterfaceV0 *i); void InitSurfaceInterface(ANPSurfaceInterfaceV0 *i); void InitSystemInterface(ANPSystemInterfaceV0 *i); -void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i); -void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i); void InitTypeFaceInterface(ANPTypefaceInterfaceV0 *i); void InitWindowInterface(ANPWindowInterfaceV0 *i); -void InitWindowInterfaceV1(ANPWindowInterfaceV1 *i); -void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i); -void InitVideoInterfaceV0(ANPVideoInterfaceV0 *i); -void InitVideoInterfaceV1(ANPVideoInterfaceV1 *i); -void InitOpenGLInterface(ANPOpenGLInterfaceV0 *i); -void InitNativeWindowInterface(ANPNativeWindowInterfaceV0 *i); diff --git a/dom/plugins/base/android/ANPNativeWindow.cpp b/dom/plugins/base/android/ANPNativeWindow.cpp deleted file mode 100644 index 4359688a859d..000000000000 --- a/dom/plugins/base/android/ANPNativeWindow.cpp +++ /dev/null @@ -1,85 +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 Android NPAPI support code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * James Willcox - * - * 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 ***** */ - -// must include config.h first for webkit to fiddle with new/delete -#include -#include "AndroidBridge.h" -#include "AndroidMediaLayer.h" -#include "ANPBase.h" -#include "nsIPluginInstanceOwner.h" -#include "nsPluginInstanceOwner.h" -#include "nsNPAPIPluginInstance.h" -#include "gfxRect.h" - -using namespace mozilla; -using namespace mozilla; - -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#define ASSIGN(obj, name) (obj)->name = anp_native_window_##name - -static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) { - nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); - - nsPluginInstanceOwner* owner; - if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { - return NULL; - } - - - ANPNativeWindow window = owner->Layer()->GetNativeWindowForContent(); - owner->Invalidate(); - - return window; -} - -static void anp_native_window_invertPluginContent(NPP instance, bool isContentInverted) { - nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); - - nsPluginInstanceOwner* owner; - if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { - return; - } - - owner->Layer()->SetInverted(isContentInverted); -} - - -void InitNativeWindowInterface(ANPNativeWindowInterfaceV0* i) { - ASSIGN(i, acquireNativeWindow); - ASSIGN(i, invertPluginContent); -} diff --git a/dom/plugins/base/android/ANPOpenGL.cpp b/dom/plugins/base/android/ANPOpenGL.cpp deleted file mode 100644 index 1c55fff40a65..000000000000 --- a/dom/plugins/base/android/ANPOpenGL.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* The Original Code is Android NPAPI support code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * James Willcox - * - * 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 -#include -#include "AndroidBridge.h" -#include "ANPBase.h" -#include "GLContextProvider.h" -#include "nsNPAPIPluginInstance.h" - -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#define ASSIGN(obj, name) (obj)->name = anp_opengl_##name - -using namespace mozilla; -using namespace mozilla::gl; - -static ANPEGLContext anp_opengl_acquireContext(NPP inst) { - // Bug 687267 - NOT_IMPLEMENTED(); - return NULL; -} - -static ANPTextureInfo anp_opengl_lockTexture(NPP instance) { - ANPTextureInfo info = { 0, 0, 0, 0 }; - NOT_IMPLEMENTED(); - return info; -} - -static void anp_opengl_releaseTexture(NPP instance, const ANPTextureInfo* info) { - NOT_IMPLEMENTED(); -} - -static void anp_opengl_invertPluginContent(NPP instance, bool isContentInverted) { - NOT_IMPLEMENTED(); -} - -/////////////////////////////////////////////////////////////////////////////// - -void InitOpenGLInterface(ANPOpenGLInterfaceV0* i) { - ASSIGN(i, acquireContext); - ASSIGN(i, lockTexture); - ASSIGN(i, releaseTexture); - ASSIGN(i, invertPluginContent); -} diff --git a/dom/plugins/base/android/ANPSystem.cpp b/dom/plugins/base/android/ANPSystem.cpp index 876f9940ed4e..0fcd1530e330 100644 --- a/dom/plugins/base/android/ANPSystem.cpp +++ b/dom/plugins/base/android/ANPSystem.cpp @@ -62,12 +62,6 @@ anp_system_getApplicationDataDirectory() return dir; } -const char* -anp_system_getApplicationDataDirectory(NPP instance) -{ - return anp_system_getApplicationDataDirectory(); -} - jclass anp_system_loadJavaClass(NPP instance, const char* className) { LOG("%s", __PRETTY_FUNCTION__); @@ -94,27 +88,8 @@ jclass anp_system_loadJavaClass(NPP instance, const char* className) return reinterpret_cast(obj); } -void anp_system_setPowerState(NPP instance, ANPPowerState powerState) -{ - NOT_IMPLEMENTED(); -} - void InitSystemInterface(ANPSystemInterfaceV0 *i) { _assert(i->inSize == sizeof(*i)); ASSIGN(i, getApplicationDataDirectory); ASSIGN(i, loadJavaClass); } - -void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, getApplicationDataDirectory); - ASSIGN(i, loadJavaClass); - ASSIGN(i, setPowerState); -} - -void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, getApplicationDataDirectory); - ASSIGN(i, loadJavaClass); - ASSIGN(i, setPowerState); -} diff --git a/dom/plugins/base/android/ANPVideo.cpp b/dom/plugins/base/android/ANPVideo.cpp deleted file mode 100644 index 6b75425a046c..000000000000 --- a/dom/plugins/base/android/ANPVideo.cpp +++ /dev/null @@ -1,109 +0,0 @@ -/* The Original Code is Android NPAPI support code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * James Willcox - * - * 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 -#include "ANPBase.h" -#include "AndroidMediaLayer.h" -#include "nsIPluginInstanceOwner.h" -#include "nsPluginInstanceOwner.h" -#include "nsNPAPIPluginInstance.h" -#include "gfxRect.h" - -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) -#define ASSIGN(obj, name) (obj)->name = anp_video_##name - -using namespace mozilla; - -static AndroidMediaLayer* GetLayerForInstance(NPP instance) { - nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); - - nsPluginInstanceOwner* owner; - if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { - return NULL; - } - - return owner->Layer(); -} - -static void Invalidate(NPP instance) { - nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); - - nsPluginInstanceOwner* owner; - if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) - return; - - owner->Invalidate(); -} - -static ANPNativeWindow anp_video_acquireNativeWindow(NPP instance) { - AndroidMediaLayer* layer = GetLayerForInstance(instance); - if (!layer) - return NULL; - - return layer->RequestNativeWindowForVideo(); -} - -static void anp_video_setWindowDimensions(NPP instance, const ANPNativeWindow window, - const ANPRectF* dimensions) { - AndroidMediaLayer* layer = GetLayerForInstance(instance); - if (!layer) - return; - - gfxRect rect(dimensions->left, dimensions->top, - dimensions->right - dimensions->left, - dimensions->bottom - dimensions->top); - - layer->SetNativeWindowDimensions(window, rect); - Invalidate(instance); -} - - -static void anp_video_releaseNativeWindow(NPP instance, ANPNativeWindow window) { - AndroidMediaLayer* layer = GetLayerForInstance(instance); - if (!layer) - return; - - layer->ReleaseNativeWindowForVideo(window); - Invalidate(instance); -} - -static void anp_video_setFramerateCallback(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc callback) { - // Bug 722682 - NOT_IMPLEMENTED(); -} - -/////////////////////////////////////////////////////////////////////////////// - -void InitVideoInterfaceV0(ANPVideoInterfaceV0* i) { - ASSIGN(i, acquireNativeWindow); - ASSIGN(i, setWindowDimensions); - ASSIGN(i, releaseNativeWindow); -} - -void InitVideoInterfaceV1(ANPVideoInterfaceV1* i) { - ASSIGN(i, acquireNativeWindow); - ASSIGN(i, setWindowDimensions); - ASSIGN(i, releaseNativeWindow); - ASSIGN(i, setFramerateCallback); -} diff --git a/dom/plugins/base/android/ANPWindow.cpp b/dom/plugins/base/android/ANPWindow.cpp index 75b643df386c..c26790054b2c 100644 --- a/dom/plugins/base/android/ANPWindow.cpp +++ b/dom/plugins/base/android/ANPWindow.cpp @@ -39,16 +39,10 @@ #include "assert.h" #include "ANPBase.h" #include -#include "AndroidBridge.h" -#include "nsNPAPIPluginInstance.h" -#include "nsIPluginInstanceOwner.h" -#include "nsPluginInstanceOwner.h" #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) #define ASSIGN(obj, name) (obj)->name = anp_window_##name -using namespace mozilla; - void anp_window_setVisibleRects(NPP instance, const ANPRectI rects[], int32_t count) { @@ -85,32 +79,6 @@ anp_window_requestCenterFitZoom(NPP instance) NOT_IMPLEMENTED(); } -ANPRectI -anp_window_visibleRect(NPP instance) -{ - ANPRectI rect = { 0, 0, 0, 0 }; - - nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); - - nsPluginInstanceOwner* owner; - if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { - return rect; - } - - nsIntRect visibleRect = owner->GetVisibleRect(); - rect.left = visibleRect.x; - rect.top = visibleRect.y; - rect.right = visibleRect.x + visibleRect.width; - rect.bottom = visibleRect.y + visibleRect.height; - - return rect; -} - -void anp_window_requestFullScreenOrientation(NPP instance, ANPScreenOrientation orientation) -{ - NOT_IMPLEMENTED(); -} - void InitWindowInterface(ANPWindowInterfaceV0 *i) { _assert(i->inSize == sizeof(*i)); ASSIGN(i, setVisibleRects); @@ -121,26 +89,3 @@ void InitWindowInterface(ANPWindowInterfaceV0 *i) { ASSIGN(i, requestCenterFitZoom); } -void InitWindowInterfaceV1(ANPWindowInterfaceV1 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, setVisibleRects); - ASSIGN(i, clearVisibleRects); - ASSIGN(i, showKeyboard); - ASSIGN(i, requestFullScreen); - ASSIGN(i, exitFullScreen); - ASSIGN(i, requestCenterFitZoom); - ASSIGN(i, visibleRect); -} - -void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i) { - _assert(i->inSize == sizeof(*i)); - ASSIGN(i, setVisibleRects); - ASSIGN(i, clearVisibleRects); - ASSIGN(i, showKeyboard); - ASSIGN(i, requestFullScreen); - ASSIGN(i, exitFullScreen); - ASSIGN(i, requestCenterFitZoom); - ASSIGN(i, visibleRect); - ASSIGN(i, requestFullScreenOrientation); -} - diff --git a/dom/plugins/base/android/Makefile.in b/dom/plugins/base/android/Makefile.in index 5f78039dc0fd..1b987e428de3 100644 --- a/dom/plugins/base/android/Makefile.in +++ b/dom/plugins/base/android/Makefile.in @@ -63,17 +63,12 @@ CPPSRCS += ANPAudio.cpp \ ANPWindow.cpp \ ANPBitmap.cpp \ ANPLog.cpp \ - ANPNativeWindow.cpp \ ANPSurface.cpp \ - ANPVideo.cpp \ - ANPOpenGL.cpp \ $(NULL) LOCAL_INCLUDES += \ - -I$(topsrcdir)/widget/android \ -I$(topsrcdir)/dom/plugins/base \ -I$(topsrcdir)/dom/plugins/base/android/include \ - -I$(topsrcdir)/gfx/gl \ $(MOZ_CAIRO_CFLAGS) \ $(NULL) diff --git a/dom/plugins/base/android/android_npapi.h b/dom/plugins/base/android/android_npapi.h index e903e2af0d37..27b0c0c9fab3 100644 --- a/dom/plugins/base/android/android_npapi.h +++ b/dom/plugins/base/android/android_npapi.h @@ -36,9 +36,8 @@ #define android_npapi_H #include -#include #include "npapi.h" -#include "GLDefs.h" +#include /////////////////////////////////////////////////////////////////////////////// // General types @@ -121,16 +120,6 @@ typedef uint32_t ANPMatrixFlag; #define kSystemInterfaceV0_ANPGetValue ((NPNVariable)1010) #define kEventInterfaceV0_ANPGetValue ((NPNVariable)1011) -#define kAudioTrackInterfaceV1_ANPGetValue ((NPNVariable)1012) -#define kOpenGLInterfaceV0_ANPGetValue ((NPNVariable)1013) -#define kWindowInterfaceV1_ANPGetValue ((NPNVariable)1014) -#define kVideoInterfaceV0_ANPGetValue ((NPNVariable)1015) -#define kSystemInterfaceV1_ANPGetValue ((NPNVariable)1016) -#define kSystemInterfaceV2_ANPGetValue ((NPNVariable)1017) -#define kWindowInterfaceV2_ANPGetValue ((NPNVariable)1018) -#define kNativeWindowInterfaceV0_ANPGetValue ((NPNVariable)1019) -#define kVideoInterfaceV1_ANPGetValue ((NPNVariable)1020) - /** queries for the drawing models supported on this device. NPN_GetValue(inst, kSupportedDrawingModel_ANPGetValue, uint32_t* bits) @@ -191,7 +180,6 @@ enum ANPDrawingModels { surface object. */ kSurface_ANPDrawingModel = 1 << 1, - kOpenGL_ANPDrawingModel = 1 << 2, }; typedef int32_t ANPDrawingModel; @@ -690,25 +678,6 @@ struct ANPWindowInterfaceV0 : ANPInterface { void (*requestCenterFitZoom)(NPP instance); }; -struct ANPWindowInterfaceV1 : ANPWindowInterfaceV0 { - /** Returns a rectangle representing the visible area of the plugin on - screen. The coordinates are relative to the size of the plugin in the - document and therefore will never be negative or exceed the plugin's size. - */ - ANPRectI (*visibleRect)(NPP instance); -}; - -typedef int32_t ANPScreenOrientation; - -struct ANPWindowInterfaceV2 : ANPWindowInterfaceV1 { - /** Called when the plugin wants to specify a particular screen orientation - when entering into full screen mode. The orientation must be set prior - to entering into full screen. After entering full screen any subsequent - changes will be updated the next time the plugin goes full screen. - */ - void (*requestFullScreenOrientation)(NPP instance, ANPScreenOrientation orientation); -}; - /////////////////////////////////////////////////////////////////////////////// enum ANPSampleFormats { @@ -793,12 +762,6 @@ struct ANPAudioTrackInterfaceV0 : ANPInterface { bool (*isStopped)(ANPAudioTrack*); }; -struct ANPAudioTrackInterfaceV1 : ANPAudioTrackInterfaceV0 { - /** Returns the track's latency in milliseconds. */ - uint32_t (*trackLatency)(ANPAudioTrack*); -}; - - /////////////////////////////////////////////////////////////////////////////// // DEFINITION OF VALUES PASSED THROUGH NPP_HandleEvent @@ -959,16 +922,12 @@ struct ANPEvent { // use based on the value in model union { ANPBitmap bitmap; - struct { - int32_t width; - int32_t height; - } surfaceSize; } data; } draw; + int32_t other[8]; } data; }; - struct ANPEventInterfaceV0 : ANPInterface { /** Post a copy of the specified event to the plugin. The event will be delivered to the plugin in its main thread (the thread that receives @@ -1017,117 +976,4 @@ typedef uint32_t uint32; typedef int16_t int16; typedef uint16_t uint16; -/** - * TODO should we not use EGL and GL data types for ABI safety? - */ -struct ANPTextureInfo { - GLuint textureId; - uint32_t width; - uint32_t height; - GLenum internalFormat; -}; - -typedef void* ANPEGLContext; - -struct ANPOpenGLInterfaceV0 : ANPInterface { - ANPEGLContext (*acquireContext)(NPP instance); - - ANPTextureInfo (*lockTexture)(NPP instance); - - void (*releaseTexture)(NPP instance, const ANPTextureInfo*); - - /** - * Invert the contents of the plugin on the y-axis. - * default is to not be inverted (i.e. use OpenGL coordinates) - */ - void (*invertPluginContent)(NPP instance, bool isContentInverted); -}; - -enum ANPPowerStates { - kDefault_ANPPowerState = 0, - kScreenOn_ANPPowerState = 1 -}; -typedef int32_t ANPPowerState; - -struct ANPSystemInterfaceV1 : ANPSystemInterfaceV0 { - void (*setPowerState)(NPP instance, ANPPowerState powerState); -}; - -struct ANPSystemInterfaceV2 : ANPInterface { - /** Return the path name for the current Application's plugin data directory, - or NULL if not supported. This directory will change depending on whether - or not the plugin is found within an incognito tab. - */ - const char* (*getApplicationDataDirectory)(NPP instance); - - // redeclaration of existing features - jclass (*loadJavaClass)(NPP instance, const char* className); - void (*setPowerState)(NPP instance, ANPPowerState powerState); -}; - -typedef void* ANPNativeWindow; - -struct ANPVideoInterfaceV0 : ANPInterface { - - /** - * Constructs a new native window to be used for rendering video content. - * - * Subsequent calls will produce new windows, but may also return NULL after - * n attempts if the browser has reached it's limit. Further, if the browser - * is unable to acquire the window quickly it may also return NULL in order - * to not prevent the plugin from executing. A subsequent call will then - * return the window if it is avaiable. - * - * NOTE: The hardware may fail if you try to decode more than the allowable - * number of videos supported on that device. - */ - ANPNativeWindow (*acquireNativeWindow)(NPP instance); - - /** - * Sets the rectangle that specifies where the video content is to be drawn. - * The dimensions are in document space. Further, if the rect is NULL the - * browser will not attempt to draw the window, therefore do not set the - * dimensions until you queue the first buffer in the window. - */ - void (*setWindowDimensions)(NPP instance, const ANPNativeWindow window, const ANPRectF* dimensions); - - /** - */ - void (*releaseNativeWindow)(NPP instance, ANPNativeWindow window); -}; - -/** Called to notify the plugin that a video frame has been composited by the -* browser for display. This will be called in a separate thread and as such -* you cannot call releaseNativeWindow from the callback. -* -* The timestamp is in nanoseconds, and is monotonically increasing. -*/ -typedef void (*ANPVideoFrameCallbackProc)(ANPNativeWindow* window, int64_t timestamp); - -struct ANPVideoInterfaceV1 : ANPVideoInterfaceV0 { - /** Set a callback to be notified when an ANPNativeWindow is composited by - * the browser. - */ - void (*setFramerateCallback)(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc); -}; - -struct ANPNativeWindowInterfaceV0 : ANPInterface { - /** - * Constructs a new native window to be used for rendering plugin content. - * - * Subsequent calls will return the original constructed window. Further, if - * the browser is unable to acquire the window quickly it may return NULL in - * order to not block the plugin indefinitely. A subsequent call will then - * return the window if it is available. - */ - ANPNativeWindow (*acquireNativeWindow)(NPP instance); - - /** - * Invert the contents of the plugin on the y-axis. - * default is to not be inverted (e.g. use OpenGL coordinates) - */ - void (*invertPluginContent)(NPP instance, bool isContentInverted); -}; - - #endif diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index 109430b8ce85..2d70d7f2fee3 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -132,10 +132,9 @@ using mozilla::plugins::PluginModuleParent; #endif #ifdef MOZ_WIDGET_ANDROID -#include -#include "android_npapi.h" #include "ANPBase.h" #include "AndroidBridge.h" +#include #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) #endif @@ -2349,7 +2348,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result) case kAudioTrackInterfaceV0_ANPGetValue: { LOG("get audio interface"); ANPAudioTrackInterfaceV0 *i = (ANPAudioTrackInterfaceV0 *) result; - InitAudioTrackInterfaceV0(i); + InitAudioTrackInterface(i); return NPERR_NO_ERROR; } @@ -2364,6 +2363,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result) LOG("get system interface"); ANPSystemInterfaceV0* i = reinterpret_cast(result); InitSystemInterface(i); + LOG("done system interface"); return NPERR_NO_ERROR; } @@ -2395,71 +2395,6 @@ _getvalue(NPP npp, NPNVariable variable, void *result) *i = reinterpret_cast(ret); return NPERR_NO_ERROR; } - - case kAudioTrackInterfaceV1_ANPGetValue: { - LOG("get audio interface v1"); - ANPAudioTrackInterfaceV1 *i = (ANPAudioTrackInterfaceV1 *) result; - InitAudioTrackInterfaceV1(i); - return NPERR_NO_ERROR; - } - - case kNativeWindowInterfaceV0_ANPGetValue: { - LOG("get native window interface v0"); - ANPNativeWindowInterfaceV0* i = (ANPNativeWindowInterfaceV0 *) result; - InitNativeWindowInterface(i); - return NPERR_NO_ERROR; - } - - case kOpenGLInterfaceV0_ANPGetValue: { - LOG("get openGL interface"); - ANPOpenGLInterfaceV0 *i = (ANPOpenGLInterfaceV0*) result; - InitOpenGLInterface(i); - return NPERR_NO_ERROR; - } - - case kWindowInterfaceV1_ANPGetValue: { - LOG("get Window interface V1"); - ANPWindowInterfaceV1 *i = (ANPWindowInterfaceV1 *) result; - InitWindowInterfaceV1(i); - return NPERR_NO_ERROR; - } - - case kWindowInterfaceV2_ANPGetValue: { - LOG("get Window interface V2"); - ANPWindowInterfaceV2 *i = (ANPWindowInterfaceV2 *) result; - InitWindowInterfaceV2(i); - return NPERR_NO_ERROR; - } - - case kVideoInterfaceV0_ANPGetValue: { - LOG("get video interface"); - ANPVideoInterfaceV0 *i = (ANPVideoInterfaceV0*) result; - InitVideoInterfaceV0(i); - return NPERR_NO_ERROR; - } - - case kVideoInterfaceV1_ANPGetValue: { - LOG("get video interface"); - ANPVideoInterfaceV1 *i = (ANPVideoInterfaceV1*) result; - InitVideoInterfaceV1(i); - return NPERR_NO_ERROR; - } - - - case kSystemInterfaceV1_ANPGetValue: { - LOG("get system interface v1"); - ANPSystemInterfaceV1* i = reinterpret_cast(result); - InitSystemInterfaceV1(i); - return NPERR_NO_ERROR; - } - - case kSystemInterfaceV2_ANPGetValue: { - LOG("get system interface v2"); - ANPSystemInterfaceV2* i = reinterpret_cast(result); - InitSystemInterfaceV2(i); - return NPERR_NO_ERROR; - } - #endif // we no longer hand out any XPCOM objects diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h index cf6e47d2ee68..ef4f8246ef8e 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.h +++ b/dom/plugins/base/nsNPAPIPluginInstance.h @@ -152,8 +152,6 @@ public: #ifdef MOZ_WIDGET_ANDROID PRUint32 GetANPDrawingModel() { return mANPDrawingModel; } void SetANPDrawingModel(PRUint32 aModel); - - // This stuff is for kSurface_ANPDrawingModel void* GetJavaSurface(); void SetJavaSurface(void* aSurface); void RequestJavaSurface(); diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 90c5057338af..5247a46d7667 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -120,7 +120,6 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); #include "ANPBase.h" #include "android_npapi.h" #include "AndroidBridge.h" -#include "AndroidMediaLayer.h" using namespace mozilla::dom; #include @@ -324,9 +323,8 @@ nsPluginInstanceOwner::nsPluginInstanceOwner() mWaitingForPaint = false; #ifdef MOZ_WIDGET_ANDROID - mOnScreen = false; - mInverted = false; - mLayer = new AndroidMediaLayer(); + mPluginViewAdded = false; + mLastPluginRect = gfxRect(0, 0, 0, 0); #endif } @@ -382,13 +380,6 @@ nsPluginInstanceOwner::~nsPluginInstanceOwner() mPluginWindow = nsnull; } -#ifdef MOZ_WIDGET_ANDROID - if (mLayer) { - delete mLayer; - mLayer = nsnull; - } -#endif - if (mInstance) { mInstance->InvalidateOwner(); } @@ -1667,43 +1658,6 @@ void nsPluginInstanceOwner::ScrollPositionDidChange(nscoord aX, nscoord aY) } #ifdef MOZ_WIDGET_ANDROID - -void nsPluginInstanceOwner::SendSize(int width, int height) -{ - if (!mInstance) - return; - - PRInt32 model = mInstance->GetANPDrawingModel(); - - if (model != kOpenGL_ANPDrawingModel) - return; - - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kDraw_ANPEventType; - event.data.draw.model = kOpenGL_ANPDrawingModel; - event.data.draw.data.surfaceSize.width = width; - event.data.draw.data.surfaceSize.height = height; - - mInstance->HandleEvent(&event, nsnull); -} - -void nsPluginInstanceOwner::SendOnScreenEvent(bool onScreen) -{ - if (!mInstance) - return; - - if ((onScreen && !mOnScreen) || (!onScreen && mOnScreen)) { - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kLifecycle_ANPEventType; - event.data.lifecycle.action = onScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction; - mInstance->HandleEvent(&event, nsnull); - - mOnScreen = onScreen; - } -} - bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) { void* javaSurface = mInstance->GetJavaSurface(); @@ -1712,6 +1666,11 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) return false; } + if (aRect.IsEqualEdges(mLastPluginRect)) { + // Already added and in position, no work to do + return true; + } + JNIEnv* env = GetJNIForThread(); if (!env) return false; @@ -1754,16 +1713,26 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) aRect.height); #endif - SendOnScreenEvent(true); + if (!mPluginViewAdded) { + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = kLifecycle_ANPEventType; + event.data.lifecycle.action = kOnScreen_ANPLifecycleAction; + mInstance->HandleEvent(&event, nsnull); + + mPluginViewAdded = true; + } return true; } void nsPluginInstanceOwner::RemovePluginView() { - if (!mInstance || !mObjectFrame | !mOnScreen) + if (!mInstance || !mObjectFrame | !mPluginViewAdded) return; + mPluginViewAdded = false; + void* surface = mInstance->GetJavaSurface(); if (!surface) return; @@ -1779,17 +1748,13 @@ void nsPluginInstanceOwner::RemovePluginView() "removePluginView", "(Landroid/view/View;)V"); env->CallStaticVoidMethod(cls, method, surface); - SendOnScreenEvent(false); -} -void nsPluginInstanceOwner::Invalidate() { - NPRect rect; - rect.left = rect.top = 0; - rect.right = mPluginWindow->width; - rect.bottom = mPluginWindow->height; - InvalidateRect(&rect); + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = kLifecycle_ANPEventType; + event.data.lifecycle.action = kOffScreen_ANPLifecycleAction; + mInstance->HandleEvent(&event, nsnull); } - #endif nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent) @@ -2814,15 +2779,7 @@ nsPluginInstanceOwner::PrepareToStop(bool aDelayedStop) #endif #ifdef MOZ_WIDGET_ANDROID - - PRInt32 model = mInstance->GetANPDrawingModel(); - if (model == kSurface_ANPDrawingModel) { - RemovePluginView(); - } else if (model == kOpenGL_ANPDrawingModel && mLayer) { - delete mLayer; - mLayer = nsnull; - } - + RemovePluginView(); #endif // Unregister scroll position listeners @@ -2934,21 +2891,15 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext, if (model == kSurface_ANPDrawingModel) { if (!AddPluginView(aFrameRect)) { - Invalidate(); + NPRect rect; + rect.left = rect.top = 0; + rect.right = aFrameRect.width; + rect.bottom = aFrameRect.height; + InvalidateRect(&rect); } return; } - if (model == kOpenGL_ANPDrawingModel) { - // FIXME: this is gross - float zoomLevel = aFrameRect.width / (float)mPluginWindow->width; - mLayer->UpdatePosition(aFrameRect, zoomLevel); - - SendOnScreenEvent(true); - SendSize((int)aFrameRect.width, (int)aFrameRect.height); - return; - } - if (model != kBitmap_ANPDrawingModel) return; @@ -3639,16 +3590,8 @@ void nsPluginInstanceOwner::UpdateWindowPositionAndClipRect(bool aSetWindow) } else { mPluginWindow->clipRect.right = 0; mPluginWindow->clipRect.bottom = 0; -#if 0 //MOZ_WIDGET_ANDROID - if (mInstance) { - PRInt32 model = mInstance->GetANPDrawingModel(); - - if (model == kSurface_ANPDrawingModel) { - RemovePluginView(); - } else if (model == kOpenGL_ANPDrawingModel) { - HidePluginLayer(); - } - } +#ifdef MOZ_WIDGET_ANDROID + RemovePluginView(); #endif } diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h index a92f35e3ddbc..a6acb483f741 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.h +++ b/dom/plugins/base/nsPluginInstanceOwner.h @@ -94,12 +94,6 @@ class gfxXlibSurface; #include #endif -#ifdef MOZ_WIDGET_ANDROID -namespace mozilla { - class AndroidMediaLayer; -} -#endif - // X.h defines KeyPress #ifdef KeyPress #undef KeyPress @@ -303,26 +297,6 @@ public: void EndUpdateBackground(gfxContext* aContext, const nsIntRect& aRect); bool UseAsyncRendering(); - -#ifdef ANDROID - nsIntRect GetVisibleRect() { - return nsIntRect(0, 0, mPluginWindow->width, mPluginWindow->height); - } - - void SetInverted(bool aInverted) { - mInverted = aInverted; - } - - bool Inverted() { - return mInverted; - } - - mozilla::AndroidMediaLayer* Layer() { - return mLayer; - } - - void Invalidate(); -#endif private: @@ -336,17 +310,10 @@ private: void FixUpURLS(const nsString &name, nsAString &value); #ifdef ANDROID - void SendSize(int width, int height); - void SendOnScreenEvent(bool onScreen); - bool AddPluginView(const gfxRect& aRect); void RemovePluginView(); - - bool mOnScreen; - bool mInverted; - - // For kOpenGL_ANPDrawingModel - mozilla::AndroidMediaLayer *mLayer; + bool mPluginViewAdded; + gfxRect mLastPluginRect; #endif nsPluginNativeWindow *mPluginWindow; diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 25c9616adc35..102637134917 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -1695,6 +1695,7 @@ nsObjectFrame::PaintPlugin(nsDisplayListBuilder* aBuilder, // The matrix includes the frame's position, so we need to transform // from 0,0 to get the correct coordinates. frameGfxRect.MoveTo(0, 0); + matrix2d.NudgeToIntegers(); mInstanceOwner->Paint(ctx, matrix2d.Transform(frameGfxRect), dirtyGfxRect); return; diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 9e1efe4dc144..b35fa3478191 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -44,12 +44,10 @@ import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.gfx.FloatSize; import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient; import org.mozilla.gecko.gfx.IntSize; -import org.mozilla.gecko.gfx.Layer; import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.PlaceholderLayerClient; import org.mozilla.gecko.gfx.RectUtils; -import org.mozilla.gecko.gfx.SurfaceTextureLayer; import org.mozilla.gecko.gfx.ViewportMetrics; import org.mozilla.gecko.Tab.HistoryEntry; @@ -219,9 +217,8 @@ abstract public class GeckoApp public ArrayList mPackageInfoCache = new ArrayList(); String[] getPluginDirectories() { - // we don't support Honeycomb - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && - Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) + // we don't support Honeycomb and later + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) return new String[0]; Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - start of getPluginDirectories"); @@ -752,7 +749,7 @@ abstract public class GeckoApp mLayerController.setWaitForTouchListeners(false); if (tab != null) - hidePlugins(tab, true); + hidePluginViews(tab); } } }); @@ -1377,6 +1374,8 @@ abstract public class GeckoApp mMainHandler.post(new Runnable() { public void run() { PluginLayoutParams lp; + JSONObject viewportObject; + ViewportMetrics pluginViewport; Tabs tabs = Tabs.getInstance(); Tab tab = tabs.getSelectedTab(); @@ -1385,10 +1384,9 @@ abstract public class GeckoApp return; ViewportMetrics targetViewport = mLayerController.getViewportMetrics(); - ViewportMetrics pluginViewport; try { - JSONObject viewportObject = new JSONObject(metadata); + viewportObject = new JSONObject(metadata); pluginViewport = new ViewportMetrics(viewportObject); } catch (JSONException e) { Log.e(LOGTAG, "Bad viewport metadata: ", e); @@ -1443,128 +1441,28 @@ abstract public class GeckoApp }); } - public Surface createSurface() { - Tabs tabs = Tabs.getInstance(); - Tab tab = tabs.getSelectedTab(); - if (tab == null) - return null; - - SurfaceTextureLayer layer = SurfaceTextureLayer.create(); - if (layer == null) - return null; - - Surface surface = layer.getSurface(); - tab.addPluginLayer(surface, layer); - return surface; - } - - public void destroySurface(Surface surface) { - Tabs tabs = Tabs.getInstance(); - Tab tab = tabs.getSelectedTab(); - if (tab == null) - return; - - tab.removePluginLayer(surface); - } - - public void showSurface(Surface surface, int x, int y, - int w, int h, boolean inverted, boolean blend, - String metadata) { - Tabs tabs = Tabs.getInstance(); - Tab tab = tabs.getSelectedTab(); - if (tab == null) - return; - - ViewportMetrics metrics; - try { - metrics = new ViewportMetrics(new JSONObject(metadata)); - } catch (JSONException e) { - Log.e(LOGTAG, "Bad viewport metadata: ", e); - return; - } - - PointF origin = metrics.getDisplayportOrigin(); - x = x + (int)origin.x; - y = y + (int)origin.y; - - LayerView layerView = mLayerController.getView(); - SurfaceTextureLayer layer = (SurfaceTextureLayer)tab.getPluginLayer(surface); - if (layer == null) - return; - - layer.update(new Point(x, y), new IntSize(w, h), metrics.getZoomFactor(), inverted, blend); - layerView.addLayer(layer); - - // FIXME: shouldn't be necessary, layer will request - // one when it gets first frame - layerView.requestRender(); - } - - private void hidePluginLayer(Layer layer) { - LayerView layerView = mLayerController.getView(); - layerView.removeLayer(layer); - layerView.requestRender(); - } - - private void showPluginLayer(Layer layer) { - LayerView layerView = mLayerController.getView(); - layerView.addLayer(layer); - layerView.requestRender(); - } - - public void hideSurface(Surface surface) { - Tabs tabs = Tabs.getInstance(); - Tab tab = tabs.getSelectedTab(); - if (tab == null) - return; - - Layer layer = tab.getPluginLayer(surface); - if (layer == null) - return; - - hidePluginLayer(layer); - } - - public void requestRender() { - mLayerController.getView().requestRender(); - } - - public void hidePlugins(boolean hideLayers) { + public void hidePluginViews() { Tabs tabs = Tabs.getInstance(); Tab tab = tabs.getSelectedTab(); if (tab == null) return; - hidePlugins(tab, hideLayers); + hidePluginViews(tab); } - public void hidePlugins(Tab tab, boolean hideLayers) { + public void hidePluginViews(Tab tab) { for (View view : tab.getPluginViews()) { view.setVisibility(View.GONE); } - - if (hideLayers) { - for (Layer layer : tab.getPluginLayers()) { - hidePluginLayer(layer); - } - - requestRender(); - } } - public void showPlugins() { + public void showPluginViews() { repositionPluginViews(true); } - public void showPlugins(Tab tab) { + public void showPluginViews(Tab tab) { repositionPluginViews(tab, true); - - for (Layer layer : tab.getPluginLayers()) { - showPluginLayer(layer); - } - - requestRender(); } public void repositionPluginViews(boolean setVisible) { @@ -2721,10 +2619,8 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams } public void reset(int aX, int aY, int aWidth, int aHeight, ViewportMetrics aViewport) { - PointF origin = aViewport.getDisplayportOrigin(); - - x = mOriginalX = aX + (int)origin.x; - y = mOriginalY = aY + (int)origin.y; + x = mOriginalX = aX; + y = mOriginalY = aY; width = mOriginalWidth = aWidth; height = mOriginalHeight = aHeight; mOriginalViewport = aViewport; @@ -2755,8 +2651,4 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams reposition(offset, viewport.getZoomFactor()); } - - public float getLastResolution() { - return mLastResolution; - } } diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 630cf8953cfd..56e7bbb77210 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -226,7 +226,6 @@ public class GeckoAppShell try { sHandler = lt.mHandlerQueue.take(); } catch (InterruptedException ie) {} - } return sHandler; } @@ -568,7 +567,7 @@ public class GeckoAppShell while (sGeckoPendingAcks.getCount() != 0) { try { sGeckoPendingAcks.await(); - } catch(InterruptedException e) {} + } catch (InterruptedException e) {} } sGeckoPendingAcks = null; } @@ -1392,40 +1391,10 @@ public class GeckoAppShell } public static void removePluginView(View view) { - Log.i(LOGTAG, "removePluginView:" + view); + Log.i(LOGTAG, "remove view:" + view); GeckoApp.mAppContext.removePluginView(view); } - public static Surface createSurface() { - Log.i(LOGTAG, "createSurface"); - return GeckoApp.mAppContext.createSurface(); - } - - public static void showSurface(Surface surface, - int x, int y, - int w, int h, - boolean inverted, - boolean blend, - String metadata) - { - Log.i(LOGTAG, "showSurface:" + surface + " @ x:" + x + " y:" + y + " w:" + w + " h:" + h + " inverted: " + inverted + " blend: " + blend + " metadata: " + metadata); - try { - GeckoApp.mAppContext.showSurface(surface, x, y, w, h, inverted, blend, metadata); - } catch (Exception e) { - Log.i(LOGTAG, "Error in showSurface:", e); - } - } - - public static void hideSurface(Surface surface) { - Log.i(LOGTAG, "hideSurface:" + surface); - GeckoApp.mAppContext.hideSurface(surface); - } - - public static void destroySurface(Surface surface) { - Log.i(LOGTAG, "destroySurface:" + surface); - GeckoApp.mAppContext.destroySurface(surface); - } - public static Class loadPluginClass(String className, String libName) { Log.i(LOGTAG, "in loadPluginClass... attempting to access className, then libName....."); Log.i(LOGTAG, "className: " + className); diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 62b2971b50f4..81999c349111 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -124,9 +124,7 @@ FENNEC_JAVA_FILES = \ gfx/RectUtils.java \ gfx/ScrollbarLayer.java \ gfx/SingleTileLayer.java \ - gfx/SurfaceTextureLayer.java \ gfx/TextLayer.java \ - gfx/TextureGenerator.java \ gfx/TextureReaper.java \ gfx/TileLayer.java \ gfx/ViewportMetrics.java \ diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 21672a08d2ea..8c35b25de93d 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -44,16 +44,13 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.util.DisplayMetrics; import android.util.Log; -import android.view.Surface; import android.view.View; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.db.BrowserDB; -import org.mozilla.gecko.gfx.Layer; import java.util.ArrayList; -import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -86,7 +83,6 @@ public final class Tab { private String mContentType; private boolean mHasTouchListeners; private ArrayList mPluginViews; - private HashMap mPluginLayers; private boolean mHasLoaded; public static final class HistoryEntry { @@ -121,7 +117,6 @@ public final class Tab { mDocumentURI = ""; mContentType = ""; mPluginViews = new ArrayList(); - mPluginLayers = new HashMap(); mHasLoaded = false; } @@ -573,20 +568,4 @@ public final class Tab { public View[] getPluginViews() { return mPluginViews.toArray(new View[mPluginViews.size()]); } - - public void addPluginLayer(Surface surface, Layer layer) { - mPluginLayers.put(surface, layer); - } - - public Layer getPluginLayer(Surface surface) { - return mPluginLayers.get(surface); - } - - public Collection getPluginLayers() { - return mPluginLayers.values(); - } - - public Layer removePluginLayer(Surface surface) { - return mPluginLayers.remove(surface); - } } diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index e22986d49afa..6e466b9912d4 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -136,7 +136,7 @@ public class Tabs implements GeckoEventListener { GeckoApp.mBrowserToolbar.setShadowVisibility(!(tab.getURL().startsWith("about:"))); if (oldTab != null) - GeckoApp.mAppContext.hidePlugins(oldTab, true); + GeckoApp.mAppContext.hidePluginViews(oldTab); } } }); @@ -200,7 +200,7 @@ public class Tabs implements GeckoEventListener { GeckoApp.mAppContext.onTabsChanged(closedTab); GeckoApp.mBrowserToolbar.updateTabs(Tabs.getInstance().getCount()); GeckoApp.mDoorHangerPopup.updatePopup(); - GeckoApp.mAppContext.hidePlugins(closedTab, true); + GeckoApp.mAppContext.hidePluginViews(closedTab); } }); diff --git a/mobile/android/base/gfx/Layer.java b/mobile/android/base/gfx/Layer.java index 46b81b7587f0..4c3328667542 100644 --- a/mobile/android/base/gfx/Layer.java +++ b/mobile/android/base/gfx/Layer.java @@ -49,13 +49,12 @@ import org.mozilla.gecko.FloatUtils; public abstract class Layer { private final ReentrantLock mTransactionLock; private boolean mInTransaction; + private Point mOrigin; private Point mNewOrigin; + private float mResolution; private float mNewResolution; private LayerView mView; - protected Point mOrigin; - protected float mResolution; - public Layer() { mTransactionLock = new ReentrantLock(); mOrigin = new Point(0, 0); diff --git a/mobile/android/base/gfx/LayerRenderer.java b/mobile/android/base/gfx/LayerRenderer.java index 7704e436ee23..573e1884b838 100644 --- a/mobile/android/base/gfx/LayerRenderer.java +++ b/mobile/android/base/gfx/LayerRenderer.java @@ -46,7 +46,6 @@ import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.NinePatchTileLayer; import org.mozilla.gecko.gfx.SingleTileLayer; import org.mozilla.gecko.gfx.TextureReaper; -import org.mozilla.gecko.gfx.TextureGenerator; import org.mozilla.gecko.gfx.TextLayer; import org.mozilla.gecko.gfx.TileLayer; import android.content.Context; @@ -63,7 +62,6 @@ import android.view.WindowManager; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import java.nio.IntBuffer; -import java.util.ArrayList; /** * The layer renderer implements the rendering logic for a layer view. @@ -92,8 +90,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private RenderContext mLastPageContext; private int mMaxTextureSize; - private ArrayList mExtraLayers = new ArrayList(); - // Dropped frames display private int[] mFrameTimings; private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames; @@ -156,34 +152,12 @@ public class LayerRenderer implements GLSurfaceView.Renderer { int maxTextureSizeResult[] = new int[1]; gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0); mMaxTextureSize = maxTextureSizeResult[0]; - - TextureGenerator.get().fill(); } public int getMaxTextureSize() { return mMaxTextureSize; } - public void addLayer(Layer layer) { - LayerController controller = mView.getController(); - - synchronized (controller) { - if (mExtraLayers.contains(layer)) { - mExtraLayers.remove(layer); - } - - mExtraLayers.add(layer); - } - } - - public void removeLayer(Layer layer) { - LayerController controller = mView.getController(); - - synchronized (controller) { - mExtraLayers.remove(layer); - } - } - /** * Called whenever a new frame is about to be drawn. */ @@ -191,7 +165,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { long frameStartTime = SystemClock.uptimeMillis(); TextureReaper.get().reap(gl); - TextureGenerator.get().fill(); LayerController controller = mView.getController(); RenderContext screenContext = createScreenContext(); @@ -225,9 +198,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { updated &= mVertScrollLayer.update(gl, pageContext); updated &= mHorizScrollLayer.update(gl, pageContext); - for (Layer layer : mExtraLayers) - updated &= layer.update(gl, pageContext); - /* Draw the background. */ mBackgroundLayer.draw(screenContext); @@ -252,10 +222,6 @@ public class LayerRenderer implements GLSurfaceView.Renderer { gl.glDisable(GL10.GL_SCISSOR_TEST); - /* Draw any extra layers that were added (likely plugins) */ - for (Layer layer : mExtraLayers) - layer.draw(pageContext); - /* Draw the vertical scrollbar. */ IntSize screenSize = new IntSize(controller.getViewportSize()); if (pageRect.height() > screenSize.height) diff --git a/mobile/android/base/gfx/LayerView.java b/mobile/android/base/gfx/LayerView.java index 0e782daea9e6..7781c1b97599 100644 --- a/mobile/android/base/gfx/LayerView.java +++ b/mobile/android/base/gfx/LayerView.java @@ -224,14 +224,6 @@ public class LayerView extends GLSurfaceView } } - public void addLayer(Layer layer) { - mRenderer.addLayer(layer); - } - - public void removeLayer(Layer layer) { - mRenderer.removeLayer(layer); - } - /** * Returns the time elapsed between the first call of requestRender() after * the last call of getRenderTime(), in nanoseconds. diff --git a/mobile/android/base/gfx/SurfaceTextureLayer.java b/mobile/android/base/gfx/SurfaceTextureLayer.java deleted file mode 100644 index 8f4799416dbe..000000000000 --- a/mobile/android/base/gfx/SurfaceTextureLayer.java +++ /dev/null @@ -1,259 +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) 2009-2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * James Willcox - * - * 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.GeckoApp; - -import android.graphics.Point; -import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.SurfaceTexture; -import android.opengl.GLES11; -import android.opengl.GLES11Ext; -import android.opengl.Matrix; -import android.util.Log; -import android.view.Surface; -import javax.microedition.khronos.opengles.GL10; -import javax.microedition.khronos.opengles.GL11Ext; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.FloatBuffer; -import android.hardware.Camera; - -public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrameAvailableListener { - private static final String LOGTAG = "SurfaceTextureLayer"; - private static final int LOCAL_GL_TEXTURE_EXTERNAL_OES = 0x00008d65; // This is only defined in API level 15 for some reason (Android 4.0.3) - - private final SurfaceTexture mSurfaceTexture; - private final Surface mSurface; - private int mTextureId; - private boolean mHaveFrame; - - private IntSize mSize; - private IntSize mNewSize; - - private boolean mInverted; - private boolean mNewInverted; - private boolean mBlend; - private boolean mNewBlend; - - private FloatBuffer textureBuffer; - private FloatBuffer textureBufferInverted; - - public SurfaceTextureLayer(int textureId) { - mTextureId = textureId; - mHaveFrame = true; - mInverted = false; - - mSurfaceTexture = new SurfaceTexture(mTextureId); - mSurfaceTexture.setOnFrameAvailableListener(this); - - mSurface = new Surface(mSurfaceTexture); - - float textureMap[] = { - 0.0f, 1.0f, // top left - 0.0f, 0.0f, // bottom left - 1.0f, 1.0f, // top right - 1.0f, 0.0f, // bottom right - }; - - textureBuffer = createBuffer(textureMap); - - float textureMapInverted[] = { - 0.0f, 0.0f, // bottom left - 0.0f, 1.0f, // top left - 1.0f, 0.0f, // bottom right - 1.0f, 1.0f, // top right - }; - - textureBufferInverted = createBuffer(textureMapInverted); - } - - public static SurfaceTextureLayer create() { - int textureId = TextureGenerator.get().take(); - if (textureId == 0) - return null; - - return new SurfaceTextureLayer(textureId); - } - - // For SurfaceTexture.OnFrameAvailableListener - public void onFrameAvailable(SurfaceTexture texture) { - // FIXME: for some reason this doesn't get called - mHaveFrame = true; - GeckoApp.mAppContext.requestRender(); - } - - private FloatBuffer createBuffer(float[] input) { - // a float has 4 bytes so we allocate for each coordinate 4 bytes - ByteBuffer byteBuffer = ByteBuffer.allocateDirect(input.length * 4); - byteBuffer.order(ByteOrder.nativeOrder()); - - FloatBuffer floatBuffer = byteBuffer.asFloatBuffer(); - floatBuffer.put(input); - floatBuffer.position(0); - - return floatBuffer; - } - - public void update(Point origin, IntSize size, float resolution, boolean inverted, boolean blend) { - beginTransaction(null); - - setOrigin(origin); - setResolution(resolution); - - mNewSize = size; - mNewInverted = inverted; - mNewBlend = blend; - - endTransaction(); - } - - @Override - public IntSize getSize() { return mSize; } - - @Override - protected void finalize() throws Throwable { - if (mSurfaceTexture != null) - mSurfaceTexture.release(); - - if (mTextureId > 0) - TextureReaper.get().add(mTextureId); - } - - @Override - protected boolean performUpdates(GL10 gl, RenderContext context) { - super.performUpdates(gl, context); - - if (mNewSize != null) { - mSize = mNewSize; - mNewSize = null; - } - - mInverted = mNewInverted; - mBlend = mNewBlend; - - gl.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES); - gl.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId); - mSurfaceTexture.updateTexImage(); - gl.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES); - - // FIXME: we should return true and rely on onFrameAvailable, but - // that isn't working for some reason - return false; - } - - private float mapToGLCoords(float input, float viewport, boolean flip) { - if (flip) input = viewport - input; - return ((input / viewport) * 2.0f) - 1.0f; - } - - @Override - public void draw(RenderContext context) { - - // Enable GL_TEXTURE_EXTERNAL_OES and bind our texture - GLES11.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES); - GLES11.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId); - - // Enable vertex and texture coordinate buffers - GLES11.glEnableClientState(GL10.GL_VERTEX_ARRAY); - GLES11.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); - - // Load whatever texture transform the SurfaceMatrix needs - float[] matrix = new float[16]; - mSurfaceTexture.getTransformMatrix(matrix); - GLES11.glMatrixMode(GLES11.GL_TEXTURE); - GLES11.glLoadMatrixf(matrix, 0); - - // Figure out vertices to put the texture in the right spot on the screen - IntSize size = getSize(); - RectF bounds = getBounds(context, new FloatSize(size)); - RectF viewport = context.viewport; - bounds.offset(-viewport.left, -viewport.top); - - float vertices[] = new float[8]; - - // Bottom left - vertices[0] = mapToGLCoords(bounds.left, viewport.width(), false); - vertices[1] = mapToGLCoords(bounds.bottom, viewport.height(), true); - - // Top left - vertices[2] = mapToGLCoords(bounds.left, viewport.width(), false); - vertices[3] = mapToGLCoords(bounds.top, viewport.height(), true); - - // Bottom right - vertices[4] = mapToGLCoords(bounds.right, viewport.width(), false); - vertices[5] = mapToGLCoords(bounds.bottom, viewport.height(), true); - - // Top right - vertices[6] = mapToGLCoords(bounds.right, viewport.width(), false); - vertices[7] = mapToGLCoords(bounds.top, viewport.height(), true); - - // Set texture and vertex buffers - GLES11.glVertexPointer(2, GL10.GL_FLOAT, 0, createBuffer(vertices)); - GLES11.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mInverted ? textureBufferInverted : textureBuffer); - - if (mBlend) { - GLES11.glEnable(GL10.GL_BLEND); - GLES11.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA); - } - - // Draw the vertices as triangle strip - GLES11.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 2); - - // Clean up - GLES11.glDisableClientState(GL10.GL_VERTEX_ARRAY); - GLES11.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); - GLES11.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES); - GLES11.glLoadIdentity(); - - if (mBlend) { - GLES11.glDisable(GL10.GL_BLEND); - } - } - - public SurfaceTexture getSurfaceTexture() { - return mSurfaceTexture; - } - - public Surface getSurface() { - return mSurface; - } -} - diff --git a/mobile/android/base/gfx/TextureGenerator.java b/mobile/android/base/gfx/TextureGenerator.java deleted file mode 100644 index 4392c55e1ae7..000000000000 --- a/mobile/android/base/gfx/TextureGenerator.java +++ /dev/null @@ -1,73 +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) 2009-2010 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * James Willcox - * - * 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 android.opengl.GLES10; -import java.util.Stack; - -public class TextureGenerator { - private static final int MIN_TEXTURES = 5; - - private static TextureGenerator sSharedInstance; - private Stack mTextureIds; - - private TextureGenerator() { mTextureIds = new Stack(); } - - public static TextureGenerator get() { - if (sSharedInstance == null) - sSharedInstance = new TextureGenerator(); - return sSharedInstance; - } - - public synchronized int take() { - if (mTextureIds.empty()) - return 0; - - return (int)mTextureIds.pop(); - } - - public synchronized void fill() { - int[] textures = new int[1]; - while (mTextureIds.size() < MIN_TEXTURES) { - GLES10.glGenTextures(1, textures, 0); - mTextureIds.push(textures[0]); - } - } -} - - diff --git a/mobile/android/base/gfx/TextureReaper.java b/mobile/android/base/gfx/TextureReaper.java index 047406234b44..0527ae73811d 100644 --- a/mobile/android/base/gfx/TextureReaper.java +++ b/mobile/android/base/gfx/TextureReaper.java @@ -55,11 +55,7 @@ public class TextureReaper { public void add(int[] textureIDs) { for (int textureID : textureIDs) - add(textureID); - } - - public void add(int textureID) { - mDeadTextureIDs.add(textureID); + mDeadTextureIDs.add(textureID); } public void reap(GL10 gl) { diff --git a/mobile/android/base/ui/PanZoomController.java b/mobile/android/base/ui/PanZoomController.java index b4fc39abf753..505fa2a11d20 100644 --- a/mobile/android/base/ui/PanZoomController.java +++ b/mobile/android/base/ui/PanZoomController.java @@ -293,7 +293,6 @@ public class PanZoomController return false; } cancelTouch(); - GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */); // fall through case PANNING_HOLD_LOCKED: GeckoApp.mAppContext.mAutoCompletePopup.hide(); @@ -475,8 +474,6 @@ public class PanZoomController stopAnimationTimer(); } - GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */); - mAnimationTimer = new Timer("Animation Timer"); mAnimationRunnable = runnable; mAnimationTimer.scheduleAtFixedRate(new TimerTask() { @@ -495,8 +492,6 @@ public class PanZoomController mAnimationRunnable.terminate(); mAnimationRunnable = null; } - - GeckoApp.mAppContext.showPlugins(); } private float getVelocity() { @@ -586,6 +581,7 @@ public class PanZoomController finishBounce(); finishAnimation(); mState = PanZoomState.NOTHING; + GeckoApp.mAppContext.showPluginViews(); } /* Performs one frame of a bounce animation. */ @@ -665,7 +661,6 @@ public class PanZoomController stopAnimationTimer(); // Force a viewport synchronisation - GeckoApp.mAppContext.showPlugins(); mController.setForceRedraw(); mController.notifyLayerClientOfGeometryChange(); } @@ -746,7 +741,7 @@ public class PanZoomController mState = PanZoomState.PINCHING; mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY()); - GeckoApp.mAppContext.hidePlugins(false /* don't hide layers, only views */); + GeckoApp.mAppContext.hidePluginViews(); GeckoApp.mAppContext.mAutoCompletePopup.hide(); cancelTouch(); @@ -811,9 +806,9 @@ public class PanZoomController startTouch(detector.getFocusX(), detector.getFocusY(), detector.getEventTime()); // Force a viewport synchronisation - GeckoApp.mAppContext.showPlugins(); mController.setForceRedraw(); mController.notifyLayerClientOfGeometryChange(); + GeckoApp.mAppContext.showPluginViews(); } public boolean getRedrawHint() { @@ -867,6 +862,7 @@ public class PanZoomController } private boolean animatedZoomTo(RectF zoomToRect) { + GeckoApp.mAppContext.hidePluginViews(); GeckoApp.mAppContext.mAutoCompletePopup.hide(); mState = PanZoomState.ANIMATED_ZOOM; diff --git a/other-licenses/skia-npapi/Makefile.in b/other-licenses/skia-npapi/Makefile.in index ba85042ea3cc..c067c426077e 100644 --- a/other-licenses/skia-npapi/Makefile.in +++ b/other-licenses/skia-npapi/Makefile.in @@ -56,7 +56,6 @@ LOCAL_INCLUDES += \ -I$(topsrcdir)/dom/plugins/base/android \ -I$(topsrcdir)/gfx/skia/include/core \ -I$(topsrcdir)/gfx/skia/include/config \ - -I$(topsrcdir)/gfx/gl \ $(NULL) diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index d9b1c75af395..2e33871497f4 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -1709,9 +1709,9 @@ AndroidBridge::ReleaseNativeWindow(void *window) } bool -AndroidBridge::SetNativeWindowFormat(void *window, int width, int height, int format) +AndroidBridge::SetNativeWindowFormat(void *window, int format) { - return ANativeWindow_setBuffersGeometry(window, width, height, format) == 0; + return ANativeWindow_setBuffersGeometry(window, 0, 0, format) == 0; } bool @@ -1857,85 +1857,3 @@ extern "C" { return jEnv; } } - -jobject -AndroidBridge::CreateSurface() -{ -#ifndef MOZ_JAVA_COMPOSITOR - return NULL; -#else - AutoLocalJNIFrame frame(1); - - JNIEnv* env = GetJNIForThread(); - jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); - - jmethodID method = env->GetStaticMethodID(cls, - "createSurface", - "()Landroid/view/Surface;"); - - jobject surface = env->CallStaticObjectMethod(cls, method); - if (surface) - env->NewGlobalRef(surface); - - return surface; -#endif -} - -void -AndroidBridge::DestroySurface(jobject surface) -{ -#ifdef MOZ_JAVA_COMPOSITOR - AutoLocalJNIFrame frame(1); - - JNIEnv* env = GetJNIForThread(); - jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); - - jmethodID method = env->GetStaticMethodID(cls, - "destroySurface", - "(Landroid/view/Surface;)V"); - env->CallStaticVoidMethod(cls, method, surface); - env->DeleteGlobalRef(surface); -#endif -} - -void -AndroidBridge::ShowSurface(jobject surface, const gfxRect& aRect, bool aInverted, bool aBlend) -{ -#ifdef MOZ_JAVA_COMPOSITOR - AutoLocalJNIFrame frame; - - JNIEnv* env = GetJNIForThread(); - jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); - - nsAutoString metadata; - nsCOMPtr metadataProvider = GetDrawMetadataProvider(); - metadataProvider->GetDrawMetadata(metadata); - - jstring jMetadata = env->NewString(nsPromiseFlatString(metadata).get(), metadata.Length()); - - jmethodID method = env->GetStaticMethodID(cls, - "showSurface", - "(Landroid/view/Surface;IIIIZZLjava/lang/String;)V"); - - env->CallStaticVoidMethod(cls, method, surface, - (int)aRect.x, (int)aRect.y, - (int)aRect.width, (int)aRect.height, - aInverted, aBlend, jMetadata); -#endif -} - -void -AndroidBridge::HideSurface(jobject surface) -{ -#ifdef MOZ_JAVA_COMPOSITOR - AutoLocalJNIFrame frame(1); - - JNIEnv* env = GetJNIForThread(); - jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); - - jmethodID method = env->GetStaticMethodID(cls, - "hideSurface", - "(Landroid/view/Surface;)V"); - env->CallStaticVoidMethod(cls, method, surface); -#endif -} diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 9d0d2f164d5b..388fccf8e4f0 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -54,7 +54,6 @@ #include "nsIMutableArray.h" #include "nsIMIMEInfo.h" #include "nsColor.h" -#include "gfxRect.h" #include "nsIAndroidBridge.h" @@ -342,7 +341,7 @@ public: void *AcquireNativeWindow(jobject surface); void ReleaseNativeWindow(void *window); - bool SetNativeWindowFormat(void *window, int width, int height, int format); + bool SetNativeWindowFormat(void *window, int format); bool LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride); bool UnlockWindow(void *window); @@ -379,11 +378,6 @@ public: void EnableNetworkNotifications(); void DisableNetworkNotifications(); - jobject CreateSurface(); - void DestroySurface(jobject surface); - void ShowSurface(jobject surface, const gfxRect& aRect, bool aInverted, bool aBlend); - void HideSurface(jobject surface); - protected: static AndroidBridge *sBridge; diff --git a/widget/android/AndroidMediaLayer.cpp b/widget/android/AndroidMediaLayer.cpp deleted file mode 100644 index 572dadcc46b0..000000000000 --- a/widget/android/AndroidMediaLayer.cpp +++ /dev/null @@ -1,155 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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 Corporation code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * James Willcox - * - * 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 -#include "AndroidMediaLayer.h" -#include "AndroidBridge.h" - -#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AndroidMediaLayer" , ## args) - - -namespace mozilla { - -AndroidMediaLayer::AndroidMediaLayer() - : mInverted(false) { -} - -AndroidMediaLayer::~AndroidMediaLayer() { - if (mContentData.window) { - AndroidBridge::Bridge()->ReleaseNativeWindow(mContentData.window); - mContentData.window = NULL; - } - - if (mContentData.surface) { - AndroidBridge::Bridge()->DestroySurface(mContentData.surface); - mContentData.surface = NULL; - } - - std::map::iterator it; - - for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) { - SurfaceData* data = it->second; - - AndroidBridge::Bridge()->ReleaseNativeWindow(data->window); - AndroidBridge::Bridge()->DestroySurface(data->surface); - delete data; - } - - mVideoSurfaces.clear(); -} - -bool AndroidMediaLayer::EnsureContentSurface() { - if (!mContentData.surface) { - mContentData.surface = AndroidBridge::Bridge()->CreateSurface(); - if (mContentData.surface) { - mContentData.window = AndroidBridge::Bridge()->AcquireNativeWindow(mContentData.surface); - AndroidBridge::Bridge()->SetNativeWindowFormat(mContentData.window, 0, 0, AndroidBridge::WINDOW_FORMAT_RGBA_8888); - } - } - - return mContentData.surface && mContentData.window; -} - -void* AndroidMediaLayer::GetNativeWindowForContent() { - if (!EnsureContentSurface()) - return NULL; - - return mContentData.window; -} - -void* AndroidMediaLayer::RequestNativeWindowForVideo() { - jobject surface = AndroidBridge::Bridge()->CreateSurface(); - if (surface) { - void* window = AndroidBridge::Bridge()->AcquireNativeWindow(surface); - if (window) { - AndroidBridge::Bridge()->SetNativeWindowFormat(window, 0, 0, AndroidBridge::WINDOW_FORMAT_RGBA_8888); - mVideoSurfaces[window] = new SurfaceData(surface, window); - return window; - } else { - LOG("Failed to create native window from surface"); - - // Cleanup - AndroidBridge::Bridge()->DestroySurface(surface); - } - } - - return NULL; -} - -void AndroidMediaLayer::ReleaseNativeWindowForVideo(void* aWindow) { - if (mVideoSurfaces.find(aWindow) == mVideoSurfaces.end()) - return; - - SurfaceData* data = mVideoSurfaces[aWindow]; - - AndroidBridge::Bridge()->ReleaseNativeWindow(data->window); - AndroidBridge::Bridge()->DestroySurface(data->surface); - - mVideoSurfaces.erase(aWindow); - delete data; -} - -void AndroidMediaLayer::SetNativeWindowDimensions(void* aWindow, const gfxRect& aDimensions) { - if (mVideoSurfaces.find(aWindow) == mVideoSurfaces.end()) - return; - - SurfaceData* data = mVideoSurfaces[aWindow]; - data->dimensions = aDimensions; -} - -void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) { - - std::map::iterator it; - - if (EnsureContentSurface()) - AndroidBridge::Bridge()->ShowSurface(mContentData.surface, aRect, mInverted, true); - - for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) { - SurfaceData* data = it->second; - - // The video window dimension we get is not adjusted by zoom factor (unlike the - // content window). Fix it up here. - gfxRect scaledDimensions = data->dimensions; - scaledDimensions.Scale(aZoomLevel); - - gfxRect videoRect(aRect.x + scaledDimensions.x, aRect.y + scaledDimensions.y, - scaledDimensions.width, scaledDimensions.height); - AndroidBridge::Bridge()->ShowSurface(data->surface, videoRect, mInverted, false); - } -} - -} /* mozilla */ diff --git a/widget/android/AndroidMediaLayer.h b/widget/android/AndroidMediaLayer.h deleted file mode 100644 index eb402743fc17..000000000000 --- a/widget/android/AndroidMediaLayer.h +++ /dev/null @@ -1,96 +0,0 @@ -/* -*- Mode: C++; tab-width: 20; 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 Corporation code. - * - * The Initial Developer of the Original Code is Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2009 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * James Willcox - * - * 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 AndroidMediaLayer_h_ -#define AndroidMediaLayer_h_ - -#include -#include -#include "gfxRect.h" - -namespace mozilla { - -class AndroidMediaLayer -{ -public: - - AndroidMediaLayer(); - virtual ~AndroidMediaLayer(); - - void* GetNativeWindowForContent(); - - void* RequestNativeWindowForVideo(); - void ReleaseNativeWindowForVideo(void* aWindow); - - void SetNativeWindowDimensions(void* aWindow, const gfxRect& aDimensions); - - void UpdatePosition(const gfxRect& aRect, float aZoomLevel); - - bool Inverted() { - return mInverted; - } - - void SetInverted(bool aInverted) { - mInverted = aInverted; - } - -private: - bool mInverted; - - class SurfaceData { - public: - SurfaceData() : - surface(NULL), window(NULL) { - } - - SurfaceData(jobject aSurface, void* aWindow) : - surface(aSurface), window(aWindow) { - } - - jobject surface; - void* window; - gfxRect dimensions; - }; - - bool EnsureContentSurface(); - - SurfaceData mContentData; - std::map mVideoSurfaces; -}; - -} /* mozilla */ -#endif /* AndroidMediaLayer_h_ */ diff --git a/widget/android/Makefile.in b/widget/android/Makefile.in index 236837b8e200..7413f0e57cc9 100644 --- a/widget/android/Makefile.in +++ b/widget/android/Makefile.in @@ -68,7 +68,6 @@ CPPSRCS = \ AndroidDirectTexture.cpp \ AndroidGraphicBuffer.cpp \ AndroidJNI.cpp \ - AndroidMediaLayer.cpp \ nsWindow.cpp \ nsLookAndFeel.cpp \ nsScreenManagerAndroid.cpp \ diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 55a9ce3b7850..57606d898733 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1019,7 +1019,7 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) if (surface) { sNativeWindow = AndroidBridge::Bridge()->AcquireNativeWindow(surface); if (sNativeWindow) { - AndroidBridge::Bridge()->SetNativeWindowFormat(sNativeWindow, 0, 0, AndroidBridge::WINDOW_FORMAT_RGB_565); + AndroidBridge::Bridge()->SetNativeWindowFormat(sNativeWindow, AndroidBridge::WINDOW_FORMAT_RGB_565); } } } From 254b4a460323f13e68fcb4103b928320d684df51 Mon Sep 17 00:00:00 2001 From: Jason Duell Date: Tue, 31 Jan 2012 20:41:43 -0800 Subject: [PATCH 11/90] Bug 718557: Clean up DOM Websocket close logic. r=smaug --- content/base/src/nsWebSocket.cpp | 76 ++++++++++--------- content/base/src/nsWebSocket.h | 17 +++-- .../websocket/nsIWebSocketChannel.idl | 4 + 3 files changed, 53 insertions(+), 44 deletions(-) diff --git a/content/base/src/nsWebSocket.cpp b/content/base/src/nsWebSocket.cpp index 8cc48cdc6a21..6723404ad57d 100644 --- a/content/base/src/nsWebSocket.cpp +++ b/content/base/src/nsWebSocket.cpp @@ -91,9 +91,9 @@ using namespace mozilla; #define TRUE_OR_FAIL_WEBSOCKET(x, ret) \ PR_BEGIN_MACRO \ if (NS_UNLIKELY(!(x))) { \ - NS_WARNING("ENSURE_TRUE_AND_FAIL_IF_FAILED(" #x ") failed"); \ - FailConnection(); \ - return ret; \ + NS_WARNING("ENSURE_TRUE_AND_FAIL_IF_FAILED(" #x ") failed"); \ + FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR); \ + return ret; \ } \ PR_END_MACRO @@ -102,7 +102,7 @@ using namespace mozilla; nsresult __rv = res; \ if (NS_FAILED(__rv)) { \ NS_ENSURE_SUCCESS_BODY(res, ret) \ - FailConnection(); \ + FailConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR); \ return ret; \ } \ PR_END_MACRO @@ -159,7 +159,8 @@ nsWebSocket::PrintErrorOnConsole(const char *aBundleURI, // when this is called the browser side wants no more part of it nsresult -nsWebSocket::CloseConnection() +nsWebSocket::CloseConnection(PRUint16 aReasonCode, + const nsACString& aReasonString) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); if (mDisconnected) @@ -172,7 +173,7 @@ nsWebSocket::CloseConnection() if (mReadyState == nsIWebSocket::CONNECTING) { SetReadyState(nsIWebSocket::CLOSED); if (mChannel) { - mChannel->Close(mClientReasonCode, mClientReason); + mChannel->Close(aReasonCode, aReasonString); } Disconnect(); return NS_OK; @@ -186,7 +187,7 @@ nsWebSocket::CloseConnection() return NS_OK; } - return mChannel->Close(mClientReasonCode, mClientReason); + return mChannel->Close(aReasonCode, aReasonString); } nsresult @@ -219,12 +220,13 @@ nsWebSocket::ConsoleError() nsresult -nsWebSocket::FailConnection() +nsWebSocket::FailConnection(PRUint16 aReasonCode, + const nsACString& aReasonString) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); ConsoleError(); - nsresult rv = CloseConnection(); + nsresult rv = CloseConnection(aReasonCode, aReasonString); NS_ENSURE_SUCCESS(rv, rv); rv = CreateAndDispatchSimpleEvent(NS_LITERAL_STRING("error")); @@ -322,7 +324,7 @@ nsWebSocket::OnStop(nsISupports *aContext, nsresult aStatusCode) if (mDisconnected) return NS_OK; - mClosedCleanly = NS_SUCCEEDED(aStatusCode); + mCloseEventWasClean = NS_SUCCEEDED(aStatusCode); if (aStatusCode == NS_BASE_STREAM_CLOSED && mReadyState >= nsIWebSocket::CLOSING) { @@ -360,10 +362,16 @@ nsWebSocket::OnServerClose(nsISupports *aContext, PRUint16 aCode, const nsACString &aReason) { NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); - mServerReasonCode = aCode; - CopyUTF8toUTF16(aReason, mServerReason); - CloseConnection(); /* reciprocate! */ + // store code/string for onclose DOM event + mCloseEventCode = aCode; + CopyUTF8toUTF16(aReason, mCloseEventReason); + + // Send reciprocal Close frame. + // 5.5.1: "When sending a Close frame in response, the endpoint typically + // echos the status code it received" + CloseConnection(aCode, aReason); + return NS_OK; } @@ -406,10 +414,9 @@ nsWebSocket::GetInterface(const nsIID &aIID, void **aResult) nsWebSocket::nsWebSocket() : mKeepingAlive(false), mCheckMustKeepAlive(true), mTriggeredCloseEvent(false), - mClosedCleanly(false), mDisconnected(false), - mClientReasonCode(0), - mServerReasonCode(nsIWebSocketChannel::CLOSE_ABNORMAL), + mCloseEventWasClean(false), + mCloseEventCode(nsIWebSocketChannel::CLOSE_ABNORMAL), mReadyState(nsIWebSocket::CONNECTING), mOutgoingBufferedAmount(0), mBinaryType(WS_BINARY_TYPE_BLOB), @@ -623,7 +630,7 @@ public: ~nsAutoCloseWS() { if (!mWebSocket->mChannel) { - mWebSocket->CloseConnection(); + mWebSocket->CloseConnection(nsIWebSocketChannel::CLOSE_INTERNAL_ERROR); } } private: @@ -906,9 +913,9 @@ nsWebSocket::SetReadyState(PRUint16 aNewReadyState) mReadyState = aNewReadyState; // The close event must be dispatched asynchronously. - rv = NS_DispatchToMainThread(new nsWSCloseEvent(this, mClosedCleanly, - mServerReasonCode, - mServerReason), + rv = NS_DispatchToMainThread(new nsWSCloseEvent(this, mCloseEventWasClean, + mCloseEventCode, + mCloseEventReason), NS_DISPATCH_NORMAL); if (NS_FAILED(rv)) { NS_WARNING("Failed to dispatch the close event"); @@ -1413,29 +1420,27 @@ nsWebSocket::Close(PRUint16 code, const nsAString & reason, PRUint8 argc) NS_ABORT_IF_FALSE(NS_IsMainThread(), "Not running on main thread"); // the reason code is optional, but if provided it must be in a specific range + PRUint16 closeCode = 0; if (argc >= 1) { - if (code != 1000 && (code < 3000 || code > 4999)) + if (code != 1000 && (code < 3000 || code > 4999)) { return NS_ERROR_DOM_INVALID_ACCESS_ERR; + } + closeCode = code; } - nsCAutoString utf8Reason; + nsCAutoString closeReason; if (argc >= 2) { - if (ContainsUnpairedSurrogates(reason)) + if (ContainsUnpairedSurrogates(reason)) { return NS_ERROR_DOM_SYNTAX_ERR; - - CopyUTF16toUTF8(reason, utf8Reason); + } + CopyUTF16toUTF8(reason, closeReason); // The API requires the UTF-8 string to be 123 or less bytes - if (utf8Reason.Length() > 123) + if (closeReason.Length() > 123) { return NS_ERROR_DOM_SYNTAX_ERR; + } } - // Format checks for reason and code both passed, they can now be assigned. - if (argc >= 1) - mClientReasonCode = code; - if (argc >= 2) - mClientReason = utf8Reason; - if (mReadyState == nsIWebSocket::CLOSING || mReadyState == nsIWebSocket::CLOSED) { return NS_OK; @@ -1446,12 +1451,12 @@ nsWebSocket::Close(PRUint16 code, const nsAString & reason, PRUint8 argc) // before calling it nsRefPtr kungfuDeathGrip = this; - FailConnection(); + FailConnection(closeCode, closeReason); return NS_OK; } // mReadyState == nsIWebSocket::OPEN - CloseConnection(); + CloseConnection(closeCode, closeReason); return NS_OK; } @@ -1594,8 +1599,7 @@ nsWebSocket::Cancel(nsresult aStatus) return NS_OK; ConsoleError(); - mClientReasonCode = nsIWebSocketChannel::CLOSE_GOING_AWAY; - return CloseConnection(); + return CloseConnection(nsIWebSocketChannel::CLOSE_GOING_AWAY); } NS_IMETHODIMP diff --git a/content/base/src/nsWebSocket.h b/content/base/src/nsWebSocket.h index 084bd8f6cef4..ebace902948b 100644 --- a/content/base/src/nsWebSocket.h +++ b/content/base/src/nsWebSocket.h @@ -111,9 +111,11 @@ protected: nsresult ParseURL(const nsString& aURL); nsresult EstablishConnection(); - // these three methods when called can release the WebSocket object - nsresult FailConnection(); - nsresult CloseConnection(); + // These methods when called can release the WebSocket object + nsresult FailConnection(PRUint16 reasonCode, + const nsACString& aReasonString = EmptyCString()); + nsresult CloseConnection(PRUint16 reasonCode, + const nsACString& aReasonString = EmptyCString()); nsresult Disconnect(); nsresult ConsoleError(); @@ -166,13 +168,12 @@ protected: bool mKeepingAlive; bool mCheckMustKeepAlive; bool mTriggeredCloseEvent; - bool mClosedCleanly; bool mDisconnected; - nsCString mClientReason; - nsString mServerReason; - PRUint16 mClientReasonCode; - PRUint16 mServerReasonCode; + // Set attributes of DOM 'onclose' message + bool mCloseEventWasClean; + nsString mCloseEventReason; + PRUint16 mCloseEventCode; nsCString mAsciiHost; // hostname PRUint32 mPort; diff --git a/netwerk/protocol/websocket/nsIWebSocketChannel.idl b/netwerk/protocol/websocket/nsIWebSocketChannel.idl index 090134160073..fa233f23eb4e 100644 --- a/netwerk/protocol/websocket/nsIWebSocketChannel.idl +++ b/netwerk/protocol/websocket/nsIWebSocketChannel.idl @@ -135,6 +135,10 @@ interface nsIWebSocketChannel : nsISupports const unsigned short CLOSE_TOO_LARGE = 1009; const unsigned short CLOSE_EXTENSION_MISSING = 1010; + // Websocket spec doesn't have equivalent of HTTP 500 code for internal + // errors: just use CLOSE_GOING_AWAY for now + const unsigned short CLOSE_INTERNAL_ERROR = CLOSE_GOING_AWAY; + /** * Use to send text message down the connection to WebSocket peer. * From 025ba418b8bcf3c242ffcd97be9fe321c725ec26 Mon Sep 17 00:00:00 2001 From: Jason Duell Date: Tue, 31 Jan 2012 20:41:47 -0800 Subject: [PATCH 12/90] Bug 701383: Websocket fails to delete mOutGoingMessage if closed multiple times. r=mcmanus --- netwerk/protocol/websocket/WebSocketChannel.cpp | 13 ++++++++++--- netwerk/protocol/websocket/WebSocketChannel.h | 1 + 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/netwerk/protocol/websocket/WebSocketChannel.cpp b/netwerk/protocol/websocket/WebSocketChannel.cpp index 22873d35f964..d3a3f18b2074 100644 --- a/netwerk/protocol/websocket/WebSocketChannel.cpp +++ b/netwerk/protocol/websocket/WebSocketChannel.cpp @@ -1347,6 +1347,7 @@ WebSocketChannel::PrimeNewOutgoingMessage() if (msgType == kMsgTypeFin) { // This is a demand to create a close message if (mClientClosed) { + DeleteCurrentOutGoingMessage(); PrimeNewOutgoingMessage(); return; } @@ -1515,6 +1516,14 @@ WebSocketChannel::PrimeNewOutgoingMessage() // compression process, } +void +WebSocketChannel::DeleteCurrentOutGoingMessage() +{ + delete mCurrentOut; + mCurrentOut = nsnull; + mCurrentOutSent = 0; +} + void WebSocketChannel::EnsureHdrOut(PRUint32 size) { @@ -2728,9 +2737,7 @@ WebSocketChannel::OnOutputStreamReady(nsIAsyncOutputStream *aStream) NS_DispatchToMainThread(new CallAcknowledge(this, mCurrentOut->Length())); } - delete mCurrentOut; - mCurrentOut = nsnull; - mCurrentOutSent = 0; + DeleteCurrentOutGoingMessage(); PrimeNewOutgoingMessage(); } else { mCurrentOutSent += amtSent; diff --git a/netwerk/protocol/websocket/WebSocketChannel.h b/netwerk/protocol/websocket/WebSocketChannel.h index c7891564b346..693e8010fbb1 100644 --- a/netwerk/protocol/websocket/WebSocketChannel.h +++ b/netwerk/protocol/websocket/WebSocketChannel.h @@ -147,6 +147,7 @@ private: void EnqueueOutgoingMessage(nsDeque &aQueue, OutboundMessage *aMsg); void PrimeNewOutgoingMessage(); + void DeleteCurrentOutGoingMessage(); void GeneratePong(PRUint8 *payload, PRUint32 len); void GeneratePing(); From 23e0bfe2228b14b8675fa1dd267f4fcc4fc40cb1 Mon Sep 17 00:00:00 2001 From: Alexander Surkov Date: Wed, 1 Feb 2012 14:17:42 +0900 Subject: [PATCH 13/90] Bug 719754 - rewrite events/test_scroll.xul, patch=marcoz, surkov, r=marcoz --- .../tests/mochitest/actions/test_anchors.html | 9 + accessible/tests/mochitest/browser.js | 16 ++ accessible/tests/mochitest/events/Makefile.in | 1 + .../tests/mochitest/events/test_scroll.xul | 157 ++++++------------ 4 files changed, 74 insertions(+), 109 deletions(-) diff --git a/accessible/tests/mochitest/actions/test_anchors.html b/accessible/tests/mochitest/actions/test_anchors.html index b452b9c547fc..5e8330683478 100644 --- a/accessible/tests/mochitest/actions/test_anchors.html +++ b/accessible/tests/mochitest/actions/test_anchors.html @@ -49,6 +49,15 @@ new scrollingChecker(getAccessible("bottom1")) ] }, + { // jump again (test for bug 437607) + ID: "anchor1", + actionName: "jump", + actionIndex: 0, + events: CLICK_EVENTS, + eventSeq: [ + new scrollingChecker(getAccessible("bottom1")) + ] + }, { ID: "anchor2", actionName: "jump", diff --git a/accessible/tests/mochitest/browser.js b/accessible/tests/mochitest/browser.js index 71c42b6a5687..078ce8a82011 100644 --- a/accessible/tests/mochitest/browser.js +++ b/accessible/tests/mochitest/browser.js @@ -49,6 +49,22 @@ function currentTabDocument() return currentBrowser().contentDocument; } +/** + * Return browser element of the tab at the given index. + */ +function browserAt(aIndex) +{ + return tabBrowser().getBrowserAtIndex(aIndex); +} + +/** + * Return DOM document of the tab at the given index. + */ +function tabDocumentAt(aIndex) +{ + return browserAt(aIndex).contentDocument; +} + /** * Return input element of address bar. */ diff --git a/accessible/tests/mochitest/events/Makefile.in b/accessible/tests/mochitest/events/Makefile.in index 4057726a4081..8d4a204bfd3f 100644 --- a/accessible/tests/mochitest/events/Makefile.in +++ b/accessible/tests/mochitest/events/Makefile.in @@ -82,6 +82,7 @@ _TEST_FILES =\ test_menu.xul \ test_mutation.html \ test_mutation.xhtml \ + test_scroll.xul \ test_selection_aria.html \ test_selection.html \ test_selection.xul \ diff --git a/accessible/tests/mochitest/events/test_scroll.xul b/accessible/tests/mochitest/events/test_scroll.xul index 29a01b092a36..b7872e8624ad 100644 --- a/accessible/tests/mochitest/events/test_scroll.xul +++ b/accessible/tests/mochitest/events/test_scroll.xul @@ -1,13 +1,6 @@ - - - - - @@ -16,7 +9,7 @@ - + - - Mozilla Bug 437607 -
- - Mozilla Bug 519303 - @@ -173,33 +139,6 @@ - - - - - - - - - - - - - - - - - - - - - - -
- + + From 45e92ed002d4cd9e9a451ac2af7eba53de82fe83 Mon Sep 17 00:00:00 2001 From: Mark Finkle Date: Wed, 1 Feb 2012 00:25:01 -0500 Subject: [PATCH 14/90] Bug 722627 - NativeWindow.contextmenus.remove creates a JS exception r=wesj --- 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 7f36d65ce910..f77e4e72b470 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -1149,7 +1149,7 @@ var NativeWindow = { }, remove: function(aId) { - this.items[aId] = null; + delete this.items[aId]; }, SelectorContext: function(aSelector) { From 6982c1b717fc84346810f175c30ed0a59e0078d9 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 1 Feb 2012 00:32:29 -0500 Subject: [PATCH 15/90] Bug 722765 - Use getLocationOnScreen to get the position of the gecko view. r=jmaher --- build/mobile/robocop/FennecNativeDriver.java.in | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/build/mobile/robocop/FennecNativeDriver.java.in b/build/mobile/robocop/FennecNativeDriver.java.in index dc40bb2ed8b3..aa14d2c86e70 100644 --- a/build/mobile/robocop/FennecNativeDriver.java.in +++ b/build/mobile/robocop/FennecNativeDriver.java.in @@ -134,11 +134,15 @@ public class FennecNativeDriver implements Driver { private void getGeckoInfo() { View geckoLayout = activity.findViewById(Integer.decode((String)locators.get("gecko_layout"))); if (geckoLayout != null) { - geckoTop = geckoLayout.getTop(); - geckoLeft = geckoLayout.getLeft(); + int[] pos = new int[2]; + geckoLayout.getLocationOnScreen(pos); + geckoTop = pos[1]; + geckoLeft = pos[0]; geckoWidth = geckoLayout.getWidth(); geckoHeight = geckoLayout.getHeight(); geckoInfo = true; + } else { + throw new RoboCopException("Unable to find view gecko_layout"); } } From de86b5b89d7c259169fbf29cc2cea4fe5976f88a Mon Sep 17 00:00:00 2001 From: James Willcox Date: Tue, 31 Jan 2012 09:40:58 -0500 Subject: [PATCH 16/90] Bug 721741 - Add support for Flash on Android 4.0+ r=blassey --- dom/plugins/base/Makefile.in | 1 + dom/plugins/base/android/ANPAudio.cpp | 21 +- dom/plugins/base/android/ANPBase.h | 13 +- dom/plugins/base/android/ANPNativeWindow.cpp | 85 ++++++ dom/plugins/base/android/ANPOpenGL.cpp | 65 +++++ dom/plugins/base/android/ANPSystem.cpp | 25 ++ dom/plugins/base/android/ANPVideo.cpp | 109 ++++++++ dom/plugins/base/android/ANPWindow.cpp | 55 ++++ dom/plugins/base/android/Makefile.in | 5 + dom/plugins/base/android/android_npapi.h | 158 ++++++++++- dom/plugins/base/nsNPAPIPlugin.cpp | 71 ++++- dom/plugins/base/nsNPAPIPluginInstance.h | 2 + dom/plugins/base/nsPluginInstanceOwner.cpp | 123 ++++++--- dom/plugins/base/nsPluginInstanceOwner.h | 37 ++- layout/generic/nsObjectFrame.cpp | 1 - mobile/android/base/GeckoApp.java | 134 ++++++++- mobile/android/base/GeckoAppShell.java | 35 ++- mobile/android/base/Makefile.in | 2 + mobile/android/base/Tab.java | 21 ++ mobile/android/base/Tabs.java | 4 +- mobile/android/base/gfx/Layer.java | 5 +- mobile/android/base/gfx/LayerRenderer.java | 34 +++ mobile/android/base/gfx/LayerView.java | 8 + .../android/base/gfx/SurfaceTextureLayer.java | 259 ++++++++++++++++++ mobile/android/base/gfx/TextureGenerator.java | 73 +++++ mobile/android/base/gfx/TextureReaper.java | 6 +- mobile/android/base/ui/PanZoomController.java | 12 +- other-licenses/skia-npapi/Makefile.in | 1 + widget/android/AndroidBridge.cpp | 86 +++++- widget/android/AndroidBridge.h | 8 +- widget/android/AndroidMediaLayer.cpp | 155 +++++++++++ widget/android/AndroidMediaLayer.h | 96 +++++++ widget/android/Makefile.in | 1 + widget/android/nsWindow.cpp | 2 +- 34 files changed, 1640 insertions(+), 73 deletions(-) create mode 100644 dom/plugins/base/android/ANPNativeWindow.cpp create mode 100644 dom/plugins/base/android/ANPOpenGL.cpp create mode 100644 dom/plugins/base/android/ANPVideo.cpp create mode 100644 mobile/android/base/gfx/SurfaceTextureLayer.java create mode 100644 mobile/android/base/gfx/TextureGenerator.java create mode 100644 widget/android/AndroidMediaLayer.cpp create mode 100644 widget/android/AndroidMediaLayer.h diff --git a/dom/plugins/base/Makefile.in b/dom/plugins/base/Makefile.in index 1b67bfff57c5..d91f793ed684 100644 --- a/dom/plugins/base/Makefile.in +++ b/dom/plugins/base/Makefile.in @@ -138,6 +138,7 @@ endif LOCAL_INCLUDES += \ -DSK_BUILD_FOR_ANDROID_NDK \ + -I$(topsrcdir)/widget/android \ -I$(topsrcdir)/xpcom/base/ \ -I$(topsrcdir)/gfx/skia/include/core \ -I$(topsrcdir)/gfx/skia/include/config \ diff --git a/dom/plugins/base/android/ANPAudio.cpp b/dom/plugins/base/android/ANPAudio.cpp index 003616842680..c3398de9b8d8 100644 --- a/dom/plugins/base/android/ANPAudio.cpp +++ b/dom/plugins/base/android/ANPAudio.cpp @@ -306,7 +306,6 @@ anp_audio_start(ANPAudioTrack* s) if (s->keepGoing) { // we are already playing. Ignore. - LOG("anp_audio_start called twice!"); return; } @@ -359,7 +358,14 @@ anp_audio_isStopped(ANPAudioTrack* s) return s->isStopped; } -void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i) { +uint32_t +anp_audio_trackLatency(ANPAudioTrack* s) { + // Bug 721835 + NOT_IMPLEMENTED(); + return 1; +} + +void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i) { _assert(i->inSize == sizeof(*i)); ASSIGN(i, newTrack); ASSIGN(i, deleteTrack); @@ -368,3 +374,14 @@ void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i) { ASSIGN(i, stop); ASSIGN(i, isStopped); } + +void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, newTrack); + ASSIGN(i, deleteTrack); + ASSIGN(i, start); + ASSIGN(i, pause); + ASSIGN(i, stop); + ASSIGN(i, isStopped); + ASSIGN(i, trackLatency); +} diff --git a/dom/plugins/base/android/ANPBase.h b/dom/plugins/base/android/ANPBase.h index 729de6a5a342..1553e698ff60 100644 --- a/dom/plugins/base/android/ANPBase.h +++ b/dom/plugins/base/android/ANPBase.h @@ -36,8 +36,8 @@ * * ***** END LICENSE BLOCK ***** */ -#include "android_npapi.h" #include +#include "android_npapi.h" #include "nsAutoPtr.h" #include "nsISupportsImpl.h" @@ -53,7 +53,8 @@ "!!!!!!!!!!!!!! %s not implemented %s, %d", \ __PRETTY_FUNCTION__, __FILE__, __LINE__); \ -void InitAudioTrackInterface(ANPAudioTrackInterfaceV0 *i); +void InitAudioTrackInterfaceV0(ANPAudioTrackInterfaceV0 *i); +void InitAudioTrackInterfaceV1(ANPAudioTrackInterfaceV1* i); void InitBitmapInterface(ANPBitmapInterfaceV0 *i); void InitCanvasInterface(ANPCanvasInterfaceV0 *i); void InitEventInterface(ANPEventInterfaceV0 *i); @@ -63,5 +64,13 @@ void InitPaintInterface(ANPPaintInterfaceV0 *i); void InitPathInterface(ANPPathInterfaceV0 *i); void InitSurfaceInterface(ANPSurfaceInterfaceV0 *i); void InitSystemInterface(ANPSystemInterfaceV0 *i); +void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i); +void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i); void InitTypeFaceInterface(ANPTypefaceInterfaceV0 *i); void InitWindowInterface(ANPWindowInterfaceV0 *i); +void InitWindowInterfaceV1(ANPWindowInterfaceV1 *i); +void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i); +void InitVideoInterfaceV0(ANPVideoInterfaceV0 *i); +void InitVideoInterfaceV1(ANPVideoInterfaceV1 *i); +void InitOpenGLInterface(ANPOpenGLInterfaceV0 *i); +void InitNativeWindowInterface(ANPNativeWindowInterfaceV0 *i); diff --git a/dom/plugins/base/android/ANPNativeWindow.cpp b/dom/plugins/base/android/ANPNativeWindow.cpp new file mode 100644 index 000000000000..4359688a859d --- /dev/null +++ b/dom/plugins/base/android/ANPNativeWindow.cpp @@ -0,0 +1,85 @@ +/* -*- 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 Android NPAPI support code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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 ***** */ + +// must include config.h first for webkit to fiddle with new/delete +#include +#include "AndroidBridge.h" +#include "AndroidMediaLayer.h" +#include "ANPBase.h" +#include "nsIPluginInstanceOwner.h" +#include "nsPluginInstanceOwner.h" +#include "nsNPAPIPluginInstance.h" +#include "gfxRect.h" + +using namespace mozilla; +using namespace mozilla; + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) +#define ASSIGN(obj, name) (obj)->name = anp_native_window_##name + +static ANPNativeWindow anp_native_window_acquireNativeWindow(NPP instance) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + return NULL; + } + + + ANPNativeWindow window = owner->Layer()->GetNativeWindowForContent(); + owner->Invalidate(); + + return window; +} + +static void anp_native_window_invertPluginContent(NPP instance, bool isContentInverted) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + return; + } + + owner->Layer()->SetInverted(isContentInverted); +} + + +void InitNativeWindowInterface(ANPNativeWindowInterfaceV0* i) { + ASSIGN(i, acquireNativeWindow); + ASSIGN(i, invertPluginContent); +} diff --git a/dom/plugins/base/android/ANPOpenGL.cpp b/dom/plugins/base/android/ANPOpenGL.cpp new file mode 100644 index 000000000000..1c55fff40a65 --- /dev/null +++ b/dom/plugins/base/android/ANPOpenGL.cpp @@ -0,0 +1,65 @@ +/* The Original Code is Android NPAPI support code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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 +#include +#include "AndroidBridge.h" +#include "ANPBase.h" +#include "GLContextProvider.h" +#include "nsNPAPIPluginInstance.h" + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) +#define ASSIGN(obj, name) (obj)->name = anp_opengl_##name + +using namespace mozilla; +using namespace mozilla::gl; + +static ANPEGLContext anp_opengl_acquireContext(NPP inst) { + // Bug 687267 + NOT_IMPLEMENTED(); + return NULL; +} + +static ANPTextureInfo anp_opengl_lockTexture(NPP instance) { + ANPTextureInfo info = { 0, 0, 0, 0 }; + NOT_IMPLEMENTED(); + return info; +} + +static void anp_opengl_releaseTexture(NPP instance, const ANPTextureInfo* info) { + NOT_IMPLEMENTED(); +} + +static void anp_opengl_invertPluginContent(NPP instance, bool isContentInverted) { + NOT_IMPLEMENTED(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitOpenGLInterface(ANPOpenGLInterfaceV0* i) { + ASSIGN(i, acquireContext); + ASSIGN(i, lockTexture); + ASSIGN(i, releaseTexture); + ASSIGN(i, invertPluginContent); +} diff --git a/dom/plugins/base/android/ANPSystem.cpp b/dom/plugins/base/android/ANPSystem.cpp index 0fcd1530e330..876f9940ed4e 100644 --- a/dom/plugins/base/android/ANPSystem.cpp +++ b/dom/plugins/base/android/ANPSystem.cpp @@ -62,6 +62,12 @@ anp_system_getApplicationDataDirectory() return dir; } +const char* +anp_system_getApplicationDataDirectory(NPP instance) +{ + return anp_system_getApplicationDataDirectory(); +} + jclass anp_system_loadJavaClass(NPP instance, const char* className) { LOG("%s", __PRETTY_FUNCTION__); @@ -88,8 +94,27 @@ jclass anp_system_loadJavaClass(NPP instance, const char* className) return reinterpret_cast(obj); } +void anp_system_setPowerState(NPP instance, ANPPowerState powerState) +{ + NOT_IMPLEMENTED(); +} + void InitSystemInterface(ANPSystemInterfaceV0 *i) { _assert(i->inSize == sizeof(*i)); ASSIGN(i, getApplicationDataDirectory); ASSIGN(i, loadJavaClass); } + +void InitSystemInterfaceV1(ANPSystemInterfaceV1 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, getApplicationDataDirectory); + ASSIGN(i, loadJavaClass); + ASSIGN(i, setPowerState); +} + +void InitSystemInterfaceV2(ANPSystemInterfaceV2 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, getApplicationDataDirectory); + ASSIGN(i, loadJavaClass); + ASSIGN(i, setPowerState); +} diff --git a/dom/plugins/base/android/ANPVideo.cpp b/dom/plugins/base/android/ANPVideo.cpp new file mode 100644 index 000000000000..6b75425a046c --- /dev/null +++ b/dom/plugins/base/android/ANPVideo.cpp @@ -0,0 +1,109 @@ +/* The Original Code is Android NPAPI support code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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 +#include "ANPBase.h" +#include "AndroidMediaLayer.h" +#include "nsIPluginInstanceOwner.h" +#include "nsPluginInstanceOwner.h" +#include "nsNPAPIPluginInstance.h" +#include "gfxRect.h" + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) +#define ASSIGN(obj, name) (obj)->name = anp_video_##name + +using namespace mozilla; + +static AndroidMediaLayer* GetLayerForInstance(NPP instance) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + return NULL; + } + + return owner->Layer(); +} + +static void Invalidate(NPP instance) { + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) + return; + + owner->Invalidate(); +} + +static ANPNativeWindow anp_video_acquireNativeWindow(NPP instance) { + AndroidMediaLayer* layer = GetLayerForInstance(instance); + if (!layer) + return NULL; + + return layer->RequestNativeWindowForVideo(); +} + +static void anp_video_setWindowDimensions(NPP instance, const ANPNativeWindow window, + const ANPRectF* dimensions) { + AndroidMediaLayer* layer = GetLayerForInstance(instance); + if (!layer) + return; + + gfxRect rect(dimensions->left, dimensions->top, + dimensions->right - dimensions->left, + dimensions->bottom - dimensions->top); + + layer->SetNativeWindowDimensions(window, rect); + Invalidate(instance); +} + + +static void anp_video_releaseNativeWindow(NPP instance, ANPNativeWindow window) { + AndroidMediaLayer* layer = GetLayerForInstance(instance); + if (!layer) + return; + + layer->ReleaseNativeWindowForVideo(window); + Invalidate(instance); +} + +static void anp_video_setFramerateCallback(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc callback) { + // Bug 722682 + NOT_IMPLEMENTED(); +} + +/////////////////////////////////////////////////////////////////////////////// + +void InitVideoInterfaceV0(ANPVideoInterfaceV0* i) { + ASSIGN(i, acquireNativeWindow); + ASSIGN(i, setWindowDimensions); + ASSIGN(i, releaseNativeWindow); +} + +void InitVideoInterfaceV1(ANPVideoInterfaceV1* i) { + ASSIGN(i, acquireNativeWindow); + ASSIGN(i, setWindowDimensions); + ASSIGN(i, releaseNativeWindow); + ASSIGN(i, setFramerateCallback); +} diff --git a/dom/plugins/base/android/ANPWindow.cpp b/dom/plugins/base/android/ANPWindow.cpp index c26790054b2c..75b643df386c 100644 --- a/dom/plugins/base/android/ANPWindow.cpp +++ b/dom/plugins/base/android/ANPWindow.cpp @@ -39,10 +39,16 @@ #include "assert.h" #include "ANPBase.h" #include +#include "AndroidBridge.h" +#include "nsNPAPIPluginInstance.h" +#include "nsIPluginInstanceOwner.h" +#include "nsPluginInstanceOwner.h" #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) #define ASSIGN(obj, name) (obj)->name = anp_window_##name +using namespace mozilla; + void anp_window_setVisibleRects(NPP instance, const ANPRectI rects[], int32_t count) { @@ -79,6 +85,32 @@ anp_window_requestCenterFitZoom(NPP instance) NOT_IMPLEMENTED(); } +ANPRectI +anp_window_visibleRect(NPP instance) +{ + ANPRectI rect = { 0, 0, 0, 0 }; + + nsNPAPIPluginInstance* pinst = static_cast(instance->ndata); + + nsPluginInstanceOwner* owner; + if (NS_FAILED(pinst->GetOwner((nsIPluginInstanceOwner**)&owner))) { + return rect; + } + + nsIntRect visibleRect = owner->GetVisibleRect(); + rect.left = visibleRect.x; + rect.top = visibleRect.y; + rect.right = visibleRect.x + visibleRect.width; + rect.bottom = visibleRect.y + visibleRect.height; + + return rect; +} + +void anp_window_requestFullScreenOrientation(NPP instance, ANPScreenOrientation orientation) +{ + NOT_IMPLEMENTED(); +} + void InitWindowInterface(ANPWindowInterfaceV0 *i) { _assert(i->inSize == sizeof(*i)); ASSIGN(i, setVisibleRects); @@ -89,3 +121,26 @@ void InitWindowInterface(ANPWindowInterfaceV0 *i) { ASSIGN(i, requestCenterFitZoom); } +void InitWindowInterfaceV1(ANPWindowInterfaceV1 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, setVisibleRects); + ASSIGN(i, clearVisibleRects); + ASSIGN(i, showKeyboard); + ASSIGN(i, requestFullScreen); + ASSIGN(i, exitFullScreen); + ASSIGN(i, requestCenterFitZoom); + ASSIGN(i, visibleRect); +} + +void InitWindowInterfaceV2(ANPWindowInterfaceV2 *i) { + _assert(i->inSize == sizeof(*i)); + ASSIGN(i, setVisibleRects); + ASSIGN(i, clearVisibleRects); + ASSIGN(i, showKeyboard); + ASSIGN(i, requestFullScreen); + ASSIGN(i, exitFullScreen); + ASSIGN(i, requestCenterFitZoom); + ASSIGN(i, visibleRect); + ASSIGN(i, requestFullScreenOrientation); +} + diff --git a/dom/plugins/base/android/Makefile.in b/dom/plugins/base/android/Makefile.in index 1b987e428de3..5f78039dc0fd 100644 --- a/dom/plugins/base/android/Makefile.in +++ b/dom/plugins/base/android/Makefile.in @@ -63,12 +63,17 @@ CPPSRCS += ANPAudio.cpp \ ANPWindow.cpp \ ANPBitmap.cpp \ ANPLog.cpp \ + ANPNativeWindow.cpp \ ANPSurface.cpp \ + ANPVideo.cpp \ + ANPOpenGL.cpp \ $(NULL) LOCAL_INCLUDES += \ + -I$(topsrcdir)/widget/android \ -I$(topsrcdir)/dom/plugins/base \ -I$(topsrcdir)/dom/plugins/base/android/include \ + -I$(topsrcdir)/gfx/gl \ $(MOZ_CAIRO_CFLAGS) \ $(NULL) diff --git a/dom/plugins/base/android/android_npapi.h b/dom/plugins/base/android/android_npapi.h index 27b0c0c9fab3..e903e2af0d37 100644 --- a/dom/plugins/base/android/android_npapi.h +++ b/dom/plugins/base/android/android_npapi.h @@ -36,8 +36,9 @@ #define android_npapi_H #include -#include "npapi.h" #include +#include "npapi.h" +#include "GLDefs.h" /////////////////////////////////////////////////////////////////////////////// // General types @@ -120,6 +121,16 @@ typedef uint32_t ANPMatrixFlag; #define kSystemInterfaceV0_ANPGetValue ((NPNVariable)1010) #define kEventInterfaceV0_ANPGetValue ((NPNVariable)1011) +#define kAudioTrackInterfaceV1_ANPGetValue ((NPNVariable)1012) +#define kOpenGLInterfaceV0_ANPGetValue ((NPNVariable)1013) +#define kWindowInterfaceV1_ANPGetValue ((NPNVariable)1014) +#define kVideoInterfaceV0_ANPGetValue ((NPNVariable)1015) +#define kSystemInterfaceV1_ANPGetValue ((NPNVariable)1016) +#define kSystemInterfaceV2_ANPGetValue ((NPNVariable)1017) +#define kWindowInterfaceV2_ANPGetValue ((NPNVariable)1018) +#define kNativeWindowInterfaceV0_ANPGetValue ((NPNVariable)1019) +#define kVideoInterfaceV1_ANPGetValue ((NPNVariable)1020) + /** queries for the drawing models supported on this device. NPN_GetValue(inst, kSupportedDrawingModel_ANPGetValue, uint32_t* bits) @@ -180,6 +191,7 @@ enum ANPDrawingModels { surface object. */ kSurface_ANPDrawingModel = 1 << 1, + kOpenGL_ANPDrawingModel = 1 << 2, }; typedef int32_t ANPDrawingModel; @@ -678,6 +690,25 @@ struct ANPWindowInterfaceV0 : ANPInterface { void (*requestCenterFitZoom)(NPP instance); }; +struct ANPWindowInterfaceV1 : ANPWindowInterfaceV0 { + /** Returns a rectangle representing the visible area of the plugin on + screen. The coordinates are relative to the size of the plugin in the + document and therefore will never be negative or exceed the plugin's size. + */ + ANPRectI (*visibleRect)(NPP instance); +}; + +typedef int32_t ANPScreenOrientation; + +struct ANPWindowInterfaceV2 : ANPWindowInterfaceV1 { + /** Called when the plugin wants to specify a particular screen orientation + when entering into full screen mode. The orientation must be set prior + to entering into full screen. After entering full screen any subsequent + changes will be updated the next time the plugin goes full screen. + */ + void (*requestFullScreenOrientation)(NPP instance, ANPScreenOrientation orientation); +}; + /////////////////////////////////////////////////////////////////////////////// enum ANPSampleFormats { @@ -762,6 +793,12 @@ struct ANPAudioTrackInterfaceV0 : ANPInterface { bool (*isStopped)(ANPAudioTrack*); }; +struct ANPAudioTrackInterfaceV1 : ANPAudioTrackInterfaceV0 { + /** Returns the track's latency in milliseconds. */ + uint32_t (*trackLatency)(ANPAudioTrack*); +}; + + /////////////////////////////////////////////////////////////////////////////// // DEFINITION OF VALUES PASSED THROUGH NPP_HandleEvent @@ -922,12 +959,16 @@ struct ANPEvent { // use based on the value in model union { ANPBitmap bitmap; + struct { + int32_t width; + int32_t height; + } surfaceSize; } data; } draw; - int32_t other[8]; } data; }; + struct ANPEventInterfaceV0 : ANPInterface { /** Post a copy of the specified event to the plugin. The event will be delivered to the plugin in its main thread (the thread that receives @@ -976,4 +1017,117 @@ typedef uint32_t uint32; typedef int16_t int16; typedef uint16_t uint16; +/** + * TODO should we not use EGL and GL data types for ABI safety? + */ +struct ANPTextureInfo { + GLuint textureId; + uint32_t width; + uint32_t height; + GLenum internalFormat; +}; + +typedef void* ANPEGLContext; + +struct ANPOpenGLInterfaceV0 : ANPInterface { + ANPEGLContext (*acquireContext)(NPP instance); + + ANPTextureInfo (*lockTexture)(NPP instance); + + void (*releaseTexture)(NPP instance, const ANPTextureInfo*); + + /** + * Invert the contents of the plugin on the y-axis. + * default is to not be inverted (i.e. use OpenGL coordinates) + */ + void (*invertPluginContent)(NPP instance, bool isContentInverted); +}; + +enum ANPPowerStates { + kDefault_ANPPowerState = 0, + kScreenOn_ANPPowerState = 1 +}; +typedef int32_t ANPPowerState; + +struct ANPSystemInterfaceV1 : ANPSystemInterfaceV0 { + void (*setPowerState)(NPP instance, ANPPowerState powerState); +}; + +struct ANPSystemInterfaceV2 : ANPInterface { + /** Return the path name for the current Application's plugin data directory, + or NULL if not supported. This directory will change depending on whether + or not the plugin is found within an incognito tab. + */ + const char* (*getApplicationDataDirectory)(NPP instance); + + // redeclaration of existing features + jclass (*loadJavaClass)(NPP instance, const char* className); + void (*setPowerState)(NPP instance, ANPPowerState powerState); +}; + +typedef void* ANPNativeWindow; + +struct ANPVideoInterfaceV0 : ANPInterface { + + /** + * Constructs a new native window to be used for rendering video content. + * + * Subsequent calls will produce new windows, but may also return NULL after + * n attempts if the browser has reached it's limit. Further, if the browser + * is unable to acquire the window quickly it may also return NULL in order + * to not prevent the plugin from executing. A subsequent call will then + * return the window if it is avaiable. + * + * NOTE: The hardware may fail if you try to decode more than the allowable + * number of videos supported on that device. + */ + ANPNativeWindow (*acquireNativeWindow)(NPP instance); + + /** + * Sets the rectangle that specifies where the video content is to be drawn. + * The dimensions are in document space. Further, if the rect is NULL the + * browser will not attempt to draw the window, therefore do not set the + * dimensions until you queue the first buffer in the window. + */ + void (*setWindowDimensions)(NPP instance, const ANPNativeWindow window, const ANPRectF* dimensions); + + /** + */ + void (*releaseNativeWindow)(NPP instance, ANPNativeWindow window); +}; + +/** Called to notify the plugin that a video frame has been composited by the +* browser for display. This will be called in a separate thread and as such +* you cannot call releaseNativeWindow from the callback. +* +* The timestamp is in nanoseconds, and is monotonically increasing. +*/ +typedef void (*ANPVideoFrameCallbackProc)(ANPNativeWindow* window, int64_t timestamp); + +struct ANPVideoInterfaceV1 : ANPVideoInterfaceV0 { + /** Set a callback to be notified when an ANPNativeWindow is composited by + * the browser. + */ + void (*setFramerateCallback)(NPP instance, const ANPNativeWindow window, ANPVideoFrameCallbackProc); +}; + +struct ANPNativeWindowInterfaceV0 : ANPInterface { + /** + * Constructs a new native window to be used for rendering plugin content. + * + * Subsequent calls will return the original constructed window. Further, if + * the browser is unable to acquire the window quickly it may return NULL in + * order to not block the plugin indefinitely. A subsequent call will then + * return the window if it is available. + */ + ANPNativeWindow (*acquireNativeWindow)(NPP instance); + + /** + * Invert the contents of the plugin on the y-axis. + * default is to not be inverted (e.g. use OpenGL coordinates) + */ + void (*invertPluginContent)(NPP instance, bool isContentInverted); +}; + + #endif diff --git a/dom/plugins/base/nsNPAPIPlugin.cpp b/dom/plugins/base/nsNPAPIPlugin.cpp index 2d70d7f2fee3..109430b8ce85 100644 --- a/dom/plugins/base/nsNPAPIPlugin.cpp +++ b/dom/plugins/base/nsNPAPIPlugin.cpp @@ -132,9 +132,10 @@ using mozilla::plugins::PluginModuleParent; #endif #ifdef MOZ_WIDGET_ANDROID +#include +#include "android_npapi.h" #include "ANPBase.h" #include "AndroidBridge.h" -#include #define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "GeckoPlugins" , ## args) #endif @@ -2348,7 +2349,7 @@ _getvalue(NPP npp, NPNVariable variable, void *result) case kAudioTrackInterfaceV0_ANPGetValue: { LOG("get audio interface"); ANPAudioTrackInterfaceV0 *i = (ANPAudioTrackInterfaceV0 *) result; - InitAudioTrackInterface(i); + InitAudioTrackInterfaceV0(i); return NPERR_NO_ERROR; } @@ -2363,7 +2364,6 @@ _getvalue(NPP npp, NPNVariable variable, void *result) LOG("get system interface"); ANPSystemInterfaceV0* i = reinterpret_cast(result); InitSystemInterface(i); - LOG("done system interface"); return NPERR_NO_ERROR; } @@ -2395,6 +2395,71 @@ _getvalue(NPP npp, NPNVariable variable, void *result) *i = reinterpret_cast(ret); return NPERR_NO_ERROR; } + + case kAudioTrackInterfaceV1_ANPGetValue: { + LOG("get audio interface v1"); + ANPAudioTrackInterfaceV1 *i = (ANPAudioTrackInterfaceV1 *) result; + InitAudioTrackInterfaceV1(i); + return NPERR_NO_ERROR; + } + + case kNativeWindowInterfaceV0_ANPGetValue: { + LOG("get native window interface v0"); + ANPNativeWindowInterfaceV0* i = (ANPNativeWindowInterfaceV0 *) result; + InitNativeWindowInterface(i); + return NPERR_NO_ERROR; + } + + case kOpenGLInterfaceV0_ANPGetValue: { + LOG("get openGL interface"); + ANPOpenGLInterfaceV0 *i = (ANPOpenGLInterfaceV0*) result; + InitOpenGLInterface(i); + return NPERR_NO_ERROR; + } + + case kWindowInterfaceV1_ANPGetValue: { + LOG("get Window interface V1"); + ANPWindowInterfaceV1 *i = (ANPWindowInterfaceV1 *) result; + InitWindowInterfaceV1(i); + return NPERR_NO_ERROR; + } + + case kWindowInterfaceV2_ANPGetValue: { + LOG("get Window interface V2"); + ANPWindowInterfaceV2 *i = (ANPWindowInterfaceV2 *) result; + InitWindowInterfaceV2(i); + return NPERR_NO_ERROR; + } + + case kVideoInterfaceV0_ANPGetValue: { + LOG("get video interface"); + ANPVideoInterfaceV0 *i = (ANPVideoInterfaceV0*) result; + InitVideoInterfaceV0(i); + return NPERR_NO_ERROR; + } + + case kVideoInterfaceV1_ANPGetValue: { + LOG("get video interface"); + ANPVideoInterfaceV1 *i = (ANPVideoInterfaceV1*) result; + InitVideoInterfaceV1(i); + return NPERR_NO_ERROR; + } + + + case kSystemInterfaceV1_ANPGetValue: { + LOG("get system interface v1"); + ANPSystemInterfaceV1* i = reinterpret_cast(result); + InitSystemInterfaceV1(i); + return NPERR_NO_ERROR; + } + + case kSystemInterfaceV2_ANPGetValue: { + LOG("get system interface v2"); + ANPSystemInterfaceV2* i = reinterpret_cast(result); + InitSystemInterfaceV2(i); + return NPERR_NO_ERROR; + } + #endif // we no longer hand out any XPCOM objects diff --git a/dom/plugins/base/nsNPAPIPluginInstance.h b/dom/plugins/base/nsNPAPIPluginInstance.h index ef4f8246ef8e..cf6e47d2ee68 100644 --- a/dom/plugins/base/nsNPAPIPluginInstance.h +++ b/dom/plugins/base/nsNPAPIPluginInstance.h @@ -152,6 +152,8 @@ public: #ifdef MOZ_WIDGET_ANDROID PRUint32 GetANPDrawingModel() { return mANPDrawingModel; } void SetANPDrawingModel(PRUint32 aModel); + + // This stuff is for kSurface_ANPDrawingModel void* GetJavaSurface(); void SetJavaSurface(void* aSurface); void RequestJavaSurface(); diff --git a/dom/plugins/base/nsPluginInstanceOwner.cpp b/dom/plugins/base/nsPluginInstanceOwner.cpp index 5247a46d7667..90c5057338af 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.cpp +++ b/dom/plugins/base/nsPluginInstanceOwner.cpp @@ -120,6 +120,7 @@ static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID); #include "ANPBase.h" #include "android_npapi.h" #include "AndroidBridge.h" +#include "AndroidMediaLayer.h" using namespace mozilla::dom; #include @@ -323,8 +324,9 @@ nsPluginInstanceOwner::nsPluginInstanceOwner() mWaitingForPaint = false; #ifdef MOZ_WIDGET_ANDROID - mPluginViewAdded = false; - mLastPluginRect = gfxRect(0, 0, 0, 0); + mOnScreen = false; + mInverted = false; + mLayer = new AndroidMediaLayer(); #endif } @@ -380,6 +382,13 @@ nsPluginInstanceOwner::~nsPluginInstanceOwner() mPluginWindow = nsnull; } +#ifdef MOZ_WIDGET_ANDROID + if (mLayer) { + delete mLayer; + mLayer = nsnull; + } +#endif + if (mInstance) { mInstance->InvalidateOwner(); } @@ -1658,6 +1667,43 @@ void nsPluginInstanceOwner::ScrollPositionDidChange(nscoord aX, nscoord aY) } #ifdef MOZ_WIDGET_ANDROID + +void nsPluginInstanceOwner::SendSize(int width, int height) +{ + if (!mInstance) + return; + + PRInt32 model = mInstance->GetANPDrawingModel(); + + if (model != kOpenGL_ANPDrawingModel) + return; + + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = kDraw_ANPEventType; + event.data.draw.model = kOpenGL_ANPDrawingModel; + event.data.draw.data.surfaceSize.width = width; + event.data.draw.data.surfaceSize.height = height; + + mInstance->HandleEvent(&event, nsnull); +} + +void nsPluginInstanceOwner::SendOnScreenEvent(bool onScreen) +{ + if (!mInstance) + return; + + if ((onScreen && !mOnScreen) || (!onScreen && mOnScreen)) { + ANPEvent event; + event.inSize = sizeof(ANPEvent); + event.eventType = kLifecycle_ANPEventType; + event.data.lifecycle.action = onScreen ? kOnScreen_ANPLifecycleAction : kOffScreen_ANPLifecycleAction; + mInstance->HandleEvent(&event, nsnull); + + mOnScreen = onScreen; + } +} + bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) { void* javaSurface = mInstance->GetJavaSurface(); @@ -1666,11 +1712,6 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) return false; } - if (aRect.IsEqualEdges(mLastPluginRect)) { - // Already added and in position, no work to do - return true; - } - JNIEnv* env = GetJNIForThread(); if (!env) return false; @@ -1713,26 +1754,16 @@ bool nsPluginInstanceOwner::AddPluginView(const gfxRect& aRect) aRect.height); #endif - if (!mPluginViewAdded) { - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kLifecycle_ANPEventType; - event.data.lifecycle.action = kOnScreen_ANPLifecycleAction; - mInstance->HandleEvent(&event, nsnull); - - mPluginViewAdded = true; - } + SendOnScreenEvent(true); return true; } void nsPluginInstanceOwner::RemovePluginView() { - if (!mInstance || !mObjectFrame | !mPluginViewAdded) + if (!mInstance || !mObjectFrame | !mOnScreen) return; - mPluginViewAdded = false; - void* surface = mInstance->GetJavaSurface(); if (!surface) return; @@ -1748,13 +1779,17 @@ void nsPluginInstanceOwner::RemovePluginView() "removePluginView", "(Landroid/view/View;)V"); env->CallStaticVoidMethod(cls, method, surface); - - ANPEvent event; - event.inSize = sizeof(ANPEvent); - event.eventType = kLifecycle_ANPEventType; - event.data.lifecycle.action = kOffScreen_ANPLifecycleAction; - mInstance->HandleEvent(&event, nsnull); + SendOnScreenEvent(false); } + +void nsPluginInstanceOwner::Invalidate() { + NPRect rect; + rect.left = rect.top = 0; + rect.right = mPluginWindow->width; + rect.bottom = mPluginWindow->height; + InvalidateRect(&rect); +} + #endif nsresult nsPluginInstanceOwner::DispatchFocusToPlugin(nsIDOMEvent* aFocusEvent) @@ -2779,7 +2814,15 @@ nsPluginInstanceOwner::PrepareToStop(bool aDelayedStop) #endif #ifdef MOZ_WIDGET_ANDROID - RemovePluginView(); + + PRInt32 model = mInstance->GetANPDrawingModel(); + if (model == kSurface_ANPDrawingModel) { + RemovePluginView(); + } else if (model == kOpenGL_ANPDrawingModel && mLayer) { + delete mLayer; + mLayer = nsnull; + } + #endif // Unregister scroll position listeners @@ -2891,15 +2934,21 @@ void nsPluginInstanceOwner::Paint(gfxContext* aContext, if (model == kSurface_ANPDrawingModel) { if (!AddPluginView(aFrameRect)) { - NPRect rect; - rect.left = rect.top = 0; - rect.right = aFrameRect.width; - rect.bottom = aFrameRect.height; - InvalidateRect(&rect); + Invalidate(); } return; } + if (model == kOpenGL_ANPDrawingModel) { + // FIXME: this is gross + float zoomLevel = aFrameRect.width / (float)mPluginWindow->width; + mLayer->UpdatePosition(aFrameRect, zoomLevel); + + SendOnScreenEvent(true); + SendSize((int)aFrameRect.width, (int)aFrameRect.height); + return; + } + if (model != kBitmap_ANPDrawingModel) return; @@ -3590,8 +3639,16 @@ void nsPluginInstanceOwner::UpdateWindowPositionAndClipRect(bool aSetWindow) } else { mPluginWindow->clipRect.right = 0; mPluginWindow->clipRect.bottom = 0; -#ifdef MOZ_WIDGET_ANDROID - RemovePluginView(); +#if 0 //MOZ_WIDGET_ANDROID + if (mInstance) { + PRInt32 model = mInstance->GetANPDrawingModel(); + + if (model == kSurface_ANPDrawingModel) { + RemovePluginView(); + } else if (model == kOpenGL_ANPDrawingModel) { + HidePluginLayer(); + } + } #endif } diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h index a6acb483f741..a92f35e3ddbc 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.h +++ b/dom/plugins/base/nsPluginInstanceOwner.h @@ -94,6 +94,12 @@ class gfxXlibSurface; #include #endif +#ifdef MOZ_WIDGET_ANDROID +namespace mozilla { + class AndroidMediaLayer; +} +#endif + // X.h defines KeyPress #ifdef KeyPress #undef KeyPress @@ -297,6 +303,26 @@ public: void EndUpdateBackground(gfxContext* aContext, const nsIntRect& aRect); bool UseAsyncRendering(); + +#ifdef ANDROID + nsIntRect GetVisibleRect() { + return nsIntRect(0, 0, mPluginWindow->width, mPluginWindow->height); + } + + void SetInverted(bool aInverted) { + mInverted = aInverted; + } + + bool Inverted() { + return mInverted; + } + + mozilla::AndroidMediaLayer* Layer() { + return mLayer; + } + + void Invalidate(); +#endif private: @@ -310,10 +336,17 @@ private: void FixUpURLS(const nsString &name, nsAString &value); #ifdef ANDROID + void SendSize(int width, int height); + void SendOnScreenEvent(bool onScreen); + bool AddPluginView(const gfxRect& aRect); void RemovePluginView(); - bool mPluginViewAdded; - gfxRect mLastPluginRect; + + bool mOnScreen; + bool mInverted; + + // For kOpenGL_ANPDrawingModel + mozilla::AndroidMediaLayer *mLayer; #endif nsPluginNativeWindow *mPluginWindow; diff --git a/layout/generic/nsObjectFrame.cpp b/layout/generic/nsObjectFrame.cpp index 102637134917..25c9616adc35 100644 --- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -1695,7 +1695,6 @@ nsObjectFrame::PaintPlugin(nsDisplayListBuilder* aBuilder, // The matrix includes the frame's position, so we need to transform // from 0,0 to get the correct coordinates. frameGfxRect.MoveTo(0, 0); - matrix2d.NudgeToIntegers(); mInstanceOwner->Paint(ctx, matrix2d.Transform(frameGfxRect), dirtyGfxRect); return; diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index b35fa3478191..9e1efe4dc144 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -44,10 +44,12 @@ import org.mozilla.gecko.db.BrowserDB; import org.mozilla.gecko.gfx.FloatSize; import org.mozilla.gecko.gfx.GeckoSoftwareLayerClient; import org.mozilla.gecko.gfx.IntSize; +import org.mozilla.gecko.gfx.Layer; import org.mozilla.gecko.gfx.LayerController; import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.PlaceholderLayerClient; import org.mozilla.gecko.gfx.RectUtils; +import org.mozilla.gecko.gfx.SurfaceTextureLayer; import org.mozilla.gecko.gfx.ViewportMetrics; import org.mozilla.gecko.Tab.HistoryEntry; @@ -217,8 +219,9 @@ abstract public class GeckoApp public ArrayList mPackageInfoCache = new ArrayList(); String[] getPluginDirectories() { - // we don't support Honeycomb and later - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) + // we don't support Honeycomb + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && + Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) return new String[0]; Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - start of getPluginDirectories"); @@ -749,7 +752,7 @@ abstract public class GeckoApp mLayerController.setWaitForTouchListeners(false); if (tab != null) - hidePluginViews(tab); + hidePlugins(tab, true); } } }); @@ -1374,8 +1377,6 @@ abstract public class GeckoApp mMainHandler.post(new Runnable() { public void run() { PluginLayoutParams lp; - JSONObject viewportObject; - ViewportMetrics pluginViewport; Tabs tabs = Tabs.getInstance(); Tab tab = tabs.getSelectedTab(); @@ -1384,9 +1385,10 @@ abstract public class GeckoApp return; ViewportMetrics targetViewport = mLayerController.getViewportMetrics(); + ViewportMetrics pluginViewport; try { - viewportObject = new JSONObject(metadata); + JSONObject viewportObject = new JSONObject(metadata); pluginViewport = new ViewportMetrics(viewportObject); } catch (JSONException e) { Log.e(LOGTAG, "Bad viewport metadata: ", e); @@ -1441,28 +1443,128 @@ abstract public class GeckoApp }); } - public void hidePluginViews() { + public Surface createSurface() { + Tabs tabs = Tabs.getInstance(); + Tab tab = tabs.getSelectedTab(); + if (tab == null) + return null; + + SurfaceTextureLayer layer = SurfaceTextureLayer.create(); + if (layer == null) + return null; + + Surface surface = layer.getSurface(); + tab.addPluginLayer(surface, layer); + return surface; + } + + public void destroySurface(Surface surface) { + Tabs tabs = Tabs.getInstance(); + Tab tab = tabs.getSelectedTab(); + if (tab == null) + return; + + tab.removePluginLayer(surface); + } + + public void showSurface(Surface surface, int x, int y, + int w, int h, boolean inverted, boolean blend, + String metadata) { + Tabs tabs = Tabs.getInstance(); + Tab tab = tabs.getSelectedTab(); + if (tab == null) + return; + + ViewportMetrics metrics; + try { + metrics = new ViewportMetrics(new JSONObject(metadata)); + } catch (JSONException e) { + Log.e(LOGTAG, "Bad viewport metadata: ", e); + return; + } + + PointF origin = metrics.getDisplayportOrigin(); + x = x + (int)origin.x; + y = y + (int)origin.y; + + LayerView layerView = mLayerController.getView(); + SurfaceTextureLayer layer = (SurfaceTextureLayer)tab.getPluginLayer(surface); + if (layer == null) + return; + + layer.update(new Point(x, y), new IntSize(w, h), metrics.getZoomFactor(), inverted, blend); + layerView.addLayer(layer); + + // FIXME: shouldn't be necessary, layer will request + // one when it gets first frame + layerView.requestRender(); + } + + private void hidePluginLayer(Layer layer) { + LayerView layerView = mLayerController.getView(); + layerView.removeLayer(layer); + layerView.requestRender(); + } + + private void showPluginLayer(Layer layer) { + LayerView layerView = mLayerController.getView(); + layerView.addLayer(layer); + layerView.requestRender(); + } + + public void hideSurface(Surface surface) { + Tabs tabs = Tabs.getInstance(); + Tab tab = tabs.getSelectedTab(); + if (tab == null) + return; + + Layer layer = tab.getPluginLayer(surface); + if (layer == null) + return; + + hidePluginLayer(layer); + } + + public void requestRender() { + mLayerController.getView().requestRender(); + } + + public void hidePlugins(boolean hideLayers) { Tabs tabs = Tabs.getInstance(); Tab tab = tabs.getSelectedTab(); if (tab == null) return; - hidePluginViews(tab); + hidePlugins(tab, hideLayers); } - public void hidePluginViews(Tab tab) { + public void hidePlugins(Tab tab, boolean hideLayers) { for (View view : tab.getPluginViews()) { view.setVisibility(View.GONE); } + + if (hideLayers) { + for (Layer layer : tab.getPluginLayers()) { + hidePluginLayer(layer); + } + + requestRender(); + } } - public void showPluginViews() { + public void showPlugins() { repositionPluginViews(true); } - public void showPluginViews(Tab tab) { + public void showPlugins(Tab tab) { repositionPluginViews(tab, true); + + for (Layer layer : tab.getPluginLayers()) { + showPluginLayer(layer); + } + + requestRender(); } public void repositionPluginViews(boolean setVisible) { @@ -2619,8 +2721,10 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams } public void reset(int aX, int aY, int aWidth, int aHeight, ViewportMetrics aViewport) { - x = mOriginalX = aX; - y = mOriginalY = aY; + PointF origin = aViewport.getDisplayportOrigin(); + + x = mOriginalX = aX + (int)origin.x; + y = mOriginalY = aY + (int)origin.y; width = mOriginalWidth = aWidth; height = mOriginalHeight = aHeight; mOriginalViewport = aViewport; @@ -2651,4 +2755,8 @@ class PluginLayoutParams extends AbsoluteLayout.LayoutParams reposition(offset, viewport.getZoomFactor()); } + + public float getLastResolution() { + return mLastResolution; + } } diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 56e7bbb77210..630cf8953cfd 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -226,6 +226,7 @@ public class GeckoAppShell try { sHandler = lt.mHandlerQueue.take(); } catch (InterruptedException ie) {} + } return sHandler; } @@ -567,7 +568,7 @@ public class GeckoAppShell while (sGeckoPendingAcks.getCount() != 0) { try { sGeckoPendingAcks.await(); - } catch (InterruptedException e) {} + } catch(InterruptedException e) {} } sGeckoPendingAcks = null; } @@ -1391,10 +1392,40 @@ public class GeckoAppShell } public static void removePluginView(View view) { - Log.i(LOGTAG, "remove view:" + view); + Log.i(LOGTAG, "removePluginView:" + view); GeckoApp.mAppContext.removePluginView(view); } + public static Surface createSurface() { + Log.i(LOGTAG, "createSurface"); + return GeckoApp.mAppContext.createSurface(); + } + + public static void showSurface(Surface surface, + int x, int y, + int w, int h, + boolean inverted, + boolean blend, + String metadata) + { + Log.i(LOGTAG, "showSurface:" + surface + " @ x:" + x + " y:" + y + " w:" + w + " h:" + h + " inverted: " + inverted + " blend: " + blend + " metadata: " + metadata); + try { + GeckoApp.mAppContext.showSurface(surface, x, y, w, h, inverted, blend, metadata); + } catch (Exception e) { + Log.i(LOGTAG, "Error in showSurface:", e); + } + } + + public static void hideSurface(Surface surface) { + Log.i(LOGTAG, "hideSurface:" + surface); + GeckoApp.mAppContext.hideSurface(surface); + } + + public static void destroySurface(Surface surface) { + Log.i(LOGTAG, "destroySurface:" + surface); + GeckoApp.mAppContext.destroySurface(surface); + } + public static Class loadPluginClass(String className, String libName) { Log.i(LOGTAG, "in loadPluginClass... attempting to access className, then libName....."); Log.i(LOGTAG, "className: " + className); diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 81999c349111..62b2971b50f4 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -124,7 +124,9 @@ FENNEC_JAVA_FILES = \ gfx/RectUtils.java \ gfx/ScrollbarLayer.java \ gfx/SingleTileLayer.java \ + gfx/SurfaceTextureLayer.java \ gfx/TextLayer.java \ + gfx/TextureGenerator.java \ gfx/TextureReaper.java \ gfx/TileLayer.java \ gfx/ViewportMetrics.java \ diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 8c35b25de93d..21672a08d2ea 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -44,13 +44,16 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.util.DisplayMetrics; import android.util.Log; +import android.view.Surface; import android.view.View; import org.json.JSONException; import org.json.JSONObject; import org.mozilla.gecko.db.BrowserDB; +import org.mozilla.gecko.gfx.Layer; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -83,6 +86,7 @@ public final class Tab { private String mContentType; private boolean mHasTouchListeners; private ArrayList mPluginViews; + private HashMap mPluginLayers; private boolean mHasLoaded; public static final class HistoryEntry { @@ -117,6 +121,7 @@ public final class Tab { mDocumentURI = ""; mContentType = ""; mPluginViews = new ArrayList(); + mPluginLayers = new HashMap(); mHasLoaded = false; } @@ -568,4 +573,20 @@ public final class Tab { public View[] getPluginViews() { return mPluginViews.toArray(new View[mPluginViews.size()]); } + + public void addPluginLayer(Surface surface, Layer layer) { + mPluginLayers.put(surface, layer); + } + + public Layer getPluginLayer(Surface surface) { + return mPluginLayers.get(surface); + } + + public Collection getPluginLayers() { + return mPluginLayers.values(); + } + + public Layer removePluginLayer(Surface surface) { + return mPluginLayers.remove(surface); + } } diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index 6e466b9912d4..e22986d49afa 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -136,7 +136,7 @@ public class Tabs implements GeckoEventListener { GeckoApp.mBrowserToolbar.setShadowVisibility(!(tab.getURL().startsWith("about:"))); if (oldTab != null) - GeckoApp.mAppContext.hidePluginViews(oldTab); + GeckoApp.mAppContext.hidePlugins(oldTab, true); } } }); @@ -200,7 +200,7 @@ public class Tabs implements GeckoEventListener { GeckoApp.mAppContext.onTabsChanged(closedTab); GeckoApp.mBrowserToolbar.updateTabs(Tabs.getInstance().getCount()); GeckoApp.mDoorHangerPopup.updatePopup(); - GeckoApp.mAppContext.hidePluginViews(closedTab); + GeckoApp.mAppContext.hidePlugins(closedTab, true); } }); diff --git a/mobile/android/base/gfx/Layer.java b/mobile/android/base/gfx/Layer.java index 4c3328667542..46b81b7587f0 100644 --- a/mobile/android/base/gfx/Layer.java +++ b/mobile/android/base/gfx/Layer.java @@ -49,12 +49,13 @@ import org.mozilla.gecko.FloatUtils; public abstract class Layer { private final ReentrantLock mTransactionLock; private boolean mInTransaction; - private Point mOrigin; private Point mNewOrigin; - private float mResolution; private float mNewResolution; private LayerView mView; + protected Point mOrigin; + protected float mResolution; + public Layer() { mTransactionLock = new ReentrantLock(); mOrigin = new Point(0, 0); diff --git a/mobile/android/base/gfx/LayerRenderer.java b/mobile/android/base/gfx/LayerRenderer.java index 573e1884b838..7704e436ee23 100644 --- a/mobile/android/base/gfx/LayerRenderer.java +++ b/mobile/android/base/gfx/LayerRenderer.java @@ -46,6 +46,7 @@ import org.mozilla.gecko.gfx.LayerView; import org.mozilla.gecko.gfx.NinePatchTileLayer; import org.mozilla.gecko.gfx.SingleTileLayer; import org.mozilla.gecko.gfx.TextureReaper; +import org.mozilla.gecko.gfx.TextureGenerator; import org.mozilla.gecko.gfx.TextLayer; import org.mozilla.gecko.gfx.TileLayer; import android.content.Context; @@ -62,6 +63,7 @@ import android.view.WindowManager; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import java.nio.IntBuffer; +import java.util.ArrayList; /** * The layer renderer implements the rendering logic for a layer view. @@ -90,6 +92,8 @@ public class LayerRenderer implements GLSurfaceView.Renderer { private RenderContext mLastPageContext; private int mMaxTextureSize; + private ArrayList mExtraLayers = new ArrayList(); + // Dropped frames display private int[] mFrameTimings; private int mCurrentFrame, mFrameTimingsSum, mDroppedFrames; @@ -152,12 +156,34 @@ public class LayerRenderer implements GLSurfaceView.Renderer { int maxTextureSizeResult[] = new int[1]; gl.glGetIntegerv(GL10.GL_MAX_TEXTURE_SIZE, maxTextureSizeResult, 0); mMaxTextureSize = maxTextureSizeResult[0]; + + TextureGenerator.get().fill(); } public int getMaxTextureSize() { return mMaxTextureSize; } + public void addLayer(Layer layer) { + LayerController controller = mView.getController(); + + synchronized (controller) { + if (mExtraLayers.contains(layer)) { + mExtraLayers.remove(layer); + } + + mExtraLayers.add(layer); + } + } + + public void removeLayer(Layer layer) { + LayerController controller = mView.getController(); + + synchronized (controller) { + mExtraLayers.remove(layer); + } + } + /** * Called whenever a new frame is about to be drawn. */ @@ -165,6 +191,7 @@ public class LayerRenderer implements GLSurfaceView.Renderer { long frameStartTime = SystemClock.uptimeMillis(); TextureReaper.get().reap(gl); + TextureGenerator.get().fill(); LayerController controller = mView.getController(); RenderContext screenContext = createScreenContext(); @@ -198,6 +225,9 @@ public class LayerRenderer implements GLSurfaceView.Renderer { updated &= mVertScrollLayer.update(gl, pageContext); updated &= mHorizScrollLayer.update(gl, pageContext); + for (Layer layer : mExtraLayers) + updated &= layer.update(gl, pageContext); + /* Draw the background. */ mBackgroundLayer.draw(screenContext); @@ -222,6 +252,10 @@ public class LayerRenderer implements GLSurfaceView.Renderer { gl.glDisable(GL10.GL_SCISSOR_TEST); + /* Draw any extra layers that were added (likely plugins) */ + for (Layer layer : mExtraLayers) + layer.draw(pageContext); + /* Draw the vertical scrollbar. */ IntSize screenSize = new IntSize(controller.getViewportSize()); if (pageRect.height() > screenSize.height) diff --git a/mobile/android/base/gfx/LayerView.java b/mobile/android/base/gfx/LayerView.java index 7781c1b97599..0e782daea9e6 100644 --- a/mobile/android/base/gfx/LayerView.java +++ b/mobile/android/base/gfx/LayerView.java @@ -224,6 +224,14 @@ public class LayerView extends GLSurfaceView } } + public void addLayer(Layer layer) { + mRenderer.addLayer(layer); + } + + public void removeLayer(Layer layer) { + mRenderer.removeLayer(layer); + } + /** * Returns the time elapsed between the first call of requestRender() after * the last call of getRenderTime(), in nanoseconds. diff --git a/mobile/android/base/gfx/SurfaceTextureLayer.java b/mobile/android/base/gfx/SurfaceTextureLayer.java new file mode 100644 index 000000000000..8f4799416dbe --- /dev/null +++ b/mobile/android/base/gfx/SurfaceTextureLayer.java @@ -0,0 +1,259 @@ +/* -*- 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) 2009-2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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.GeckoApp; + +import android.graphics.Point; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.SurfaceTexture; +import android.opengl.GLES11; +import android.opengl.GLES11Ext; +import android.opengl.Matrix; +import android.util.Log; +import android.view.Surface; +import javax.microedition.khronos.opengles.GL10; +import javax.microedition.khronos.opengles.GL11Ext; +import java.nio.Buffer; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.FloatBuffer; +import android.hardware.Camera; + +public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrameAvailableListener { + private static final String LOGTAG = "SurfaceTextureLayer"; + private static final int LOCAL_GL_TEXTURE_EXTERNAL_OES = 0x00008d65; // This is only defined in API level 15 for some reason (Android 4.0.3) + + private final SurfaceTexture mSurfaceTexture; + private final Surface mSurface; + private int mTextureId; + private boolean mHaveFrame; + + private IntSize mSize; + private IntSize mNewSize; + + private boolean mInverted; + private boolean mNewInverted; + private boolean mBlend; + private boolean mNewBlend; + + private FloatBuffer textureBuffer; + private FloatBuffer textureBufferInverted; + + public SurfaceTextureLayer(int textureId) { + mTextureId = textureId; + mHaveFrame = true; + mInverted = false; + + mSurfaceTexture = new SurfaceTexture(mTextureId); + mSurfaceTexture.setOnFrameAvailableListener(this); + + mSurface = new Surface(mSurfaceTexture); + + float textureMap[] = { + 0.0f, 1.0f, // top left + 0.0f, 0.0f, // bottom left + 1.0f, 1.0f, // top right + 1.0f, 0.0f, // bottom right + }; + + textureBuffer = createBuffer(textureMap); + + float textureMapInverted[] = { + 0.0f, 0.0f, // bottom left + 0.0f, 1.0f, // top left + 1.0f, 0.0f, // bottom right + 1.0f, 1.0f, // top right + }; + + textureBufferInverted = createBuffer(textureMapInverted); + } + + public static SurfaceTextureLayer create() { + int textureId = TextureGenerator.get().take(); + if (textureId == 0) + return null; + + return new SurfaceTextureLayer(textureId); + } + + // For SurfaceTexture.OnFrameAvailableListener + public void onFrameAvailable(SurfaceTexture texture) { + // FIXME: for some reason this doesn't get called + mHaveFrame = true; + GeckoApp.mAppContext.requestRender(); + } + + private FloatBuffer createBuffer(float[] input) { + // a float has 4 bytes so we allocate for each coordinate 4 bytes + ByteBuffer byteBuffer = ByteBuffer.allocateDirect(input.length * 4); + byteBuffer.order(ByteOrder.nativeOrder()); + + FloatBuffer floatBuffer = byteBuffer.asFloatBuffer(); + floatBuffer.put(input); + floatBuffer.position(0); + + return floatBuffer; + } + + public void update(Point origin, IntSize size, float resolution, boolean inverted, boolean blend) { + beginTransaction(null); + + setOrigin(origin); + setResolution(resolution); + + mNewSize = size; + mNewInverted = inverted; + mNewBlend = blend; + + endTransaction(); + } + + @Override + public IntSize getSize() { return mSize; } + + @Override + protected void finalize() throws Throwable { + if (mSurfaceTexture != null) + mSurfaceTexture.release(); + + if (mTextureId > 0) + TextureReaper.get().add(mTextureId); + } + + @Override + protected boolean performUpdates(GL10 gl, RenderContext context) { + super.performUpdates(gl, context); + + if (mNewSize != null) { + mSize = mNewSize; + mNewSize = null; + } + + mInverted = mNewInverted; + mBlend = mNewBlend; + + gl.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES); + gl.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId); + mSurfaceTexture.updateTexImage(); + gl.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES); + + // FIXME: we should return true and rely on onFrameAvailable, but + // that isn't working for some reason + return false; + } + + private float mapToGLCoords(float input, float viewport, boolean flip) { + if (flip) input = viewport - input; + return ((input / viewport) * 2.0f) - 1.0f; + } + + @Override + public void draw(RenderContext context) { + + // Enable GL_TEXTURE_EXTERNAL_OES and bind our texture + GLES11.glEnable(LOCAL_GL_TEXTURE_EXTERNAL_OES); + GLES11.glBindTexture(LOCAL_GL_TEXTURE_EXTERNAL_OES, mTextureId); + + // Enable vertex and texture coordinate buffers + GLES11.glEnableClientState(GL10.GL_VERTEX_ARRAY); + GLES11.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + + // Load whatever texture transform the SurfaceMatrix needs + float[] matrix = new float[16]; + mSurfaceTexture.getTransformMatrix(matrix); + GLES11.glMatrixMode(GLES11.GL_TEXTURE); + GLES11.glLoadMatrixf(matrix, 0); + + // Figure out vertices to put the texture in the right spot on the screen + IntSize size = getSize(); + RectF bounds = getBounds(context, new FloatSize(size)); + RectF viewport = context.viewport; + bounds.offset(-viewport.left, -viewport.top); + + float vertices[] = new float[8]; + + // Bottom left + vertices[0] = mapToGLCoords(bounds.left, viewport.width(), false); + vertices[1] = mapToGLCoords(bounds.bottom, viewport.height(), true); + + // Top left + vertices[2] = mapToGLCoords(bounds.left, viewport.width(), false); + vertices[3] = mapToGLCoords(bounds.top, viewport.height(), true); + + // Bottom right + vertices[4] = mapToGLCoords(bounds.right, viewport.width(), false); + vertices[5] = mapToGLCoords(bounds.bottom, viewport.height(), true); + + // Top right + vertices[6] = mapToGLCoords(bounds.right, viewport.width(), false); + vertices[7] = mapToGLCoords(bounds.top, viewport.height(), true); + + // Set texture and vertex buffers + GLES11.glVertexPointer(2, GL10.GL_FLOAT, 0, createBuffer(vertices)); + GLES11.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mInverted ? textureBufferInverted : textureBuffer); + + if (mBlend) { + GLES11.glEnable(GL10.GL_BLEND); + GLES11.glBlendFunc(GL10.GL_ONE, GL10.GL_ONE_MINUS_SRC_ALPHA); + } + + // Draw the vertices as triangle strip + GLES11.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 2); + + // Clean up + GLES11.glDisableClientState(GL10.GL_VERTEX_ARRAY); + GLES11.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); + GLES11.glDisable(LOCAL_GL_TEXTURE_EXTERNAL_OES); + GLES11.glLoadIdentity(); + + if (mBlend) { + GLES11.glDisable(GL10.GL_BLEND); + } + } + + public SurfaceTexture getSurfaceTexture() { + return mSurfaceTexture; + } + + public Surface getSurface() { + return mSurface; + } +} + diff --git a/mobile/android/base/gfx/TextureGenerator.java b/mobile/android/base/gfx/TextureGenerator.java new file mode 100644 index 000000000000..4392c55e1ae7 --- /dev/null +++ b/mobile/android/base/gfx/TextureGenerator.java @@ -0,0 +1,73 @@ +/* -*- 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) 2009-2010 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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 android.opengl.GLES10; +import java.util.Stack; + +public class TextureGenerator { + private static final int MIN_TEXTURES = 5; + + private static TextureGenerator sSharedInstance; + private Stack mTextureIds; + + private TextureGenerator() { mTextureIds = new Stack(); } + + public static TextureGenerator get() { + if (sSharedInstance == null) + sSharedInstance = new TextureGenerator(); + return sSharedInstance; + } + + public synchronized int take() { + if (mTextureIds.empty()) + return 0; + + return (int)mTextureIds.pop(); + } + + public synchronized void fill() { + int[] textures = new int[1]; + while (mTextureIds.size() < MIN_TEXTURES) { + GLES10.glGenTextures(1, textures, 0); + mTextureIds.push(textures[0]); + } + } +} + + diff --git a/mobile/android/base/gfx/TextureReaper.java b/mobile/android/base/gfx/TextureReaper.java index 0527ae73811d..047406234b44 100644 --- a/mobile/android/base/gfx/TextureReaper.java +++ b/mobile/android/base/gfx/TextureReaper.java @@ -55,7 +55,11 @@ public class TextureReaper { public void add(int[] textureIDs) { for (int textureID : textureIDs) - mDeadTextureIDs.add(textureID); + add(textureID); + } + + public void add(int textureID) { + mDeadTextureIDs.add(textureID); } public void reap(GL10 gl) { diff --git a/mobile/android/base/ui/PanZoomController.java b/mobile/android/base/ui/PanZoomController.java index 505fa2a11d20..b4fc39abf753 100644 --- a/mobile/android/base/ui/PanZoomController.java +++ b/mobile/android/base/ui/PanZoomController.java @@ -293,6 +293,7 @@ public class PanZoomController return false; } cancelTouch(); + GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */); // fall through case PANNING_HOLD_LOCKED: GeckoApp.mAppContext.mAutoCompletePopup.hide(); @@ -474,6 +475,8 @@ public class PanZoomController stopAnimationTimer(); } + GeckoApp.mAppContext.hidePlugins(false /* don't hide layers */); + mAnimationTimer = new Timer("Animation Timer"); mAnimationRunnable = runnable; mAnimationTimer.scheduleAtFixedRate(new TimerTask() { @@ -492,6 +495,8 @@ public class PanZoomController mAnimationRunnable.terminate(); mAnimationRunnable = null; } + + GeckoApp.mAppContext.showPlugins(); } private float getVelocity() { @@ -581,7 +586,6 @@ public class PanZoomController finishBounce(); finishAnimation(); mState = PanZoomState.NOTHING; - GeckoApp.mAppContext.showPluginViews(); } /* Performs one frame of a bounce animation. */ @@ -661,6 +665,7 @@ public class PanZoomController stopAnimationTimer(); // Force a viewport synchronisation + GeckoApp.mAppContext.showPlugins(); mController.setForceRedraw(); mController.notifyLayerClientOfGeometryChange(); } @@ -741,7 +746,7 @@ public class PanZoomController mState = PanZoomState.PINCHING; mLastZoomFocus = new PointF(detector.getFocusX(), detector.getFocusY()); - GeckoApp.mAppContext.hidePluginViews(); + GeckoApp.mAppContext.hidePlugins(false /* don't hide layers, only views */); GeckoApp.mAppContext.mAutoCompletePopup.hide(); cancelTouch(); @@ -806,9 +811,9 @@ public class PanZoomController startTouch(detector.getFocusX(), detector.getFocusY(), detector.getEventTime()); // Force a viewport synchronisation + GeckoApp.mAppContext.showPlugins(); mController.setForceRedraw(); mController.notifyLayerClientOfGeometryChange(); - GeckoApp.mAppContext.showPluginViews(); } public boolean getRedrawHint() { @@ -862,7 +867,6 @@ public class PanZoomController } private boolean animatedZoomTo(RectF zoomToRect) { - GeckoApp.mAppContext.hidePluginViews(); GeckoApp.mAppContext.mAutoCompletePopup.hide(); mState = PanZoomState.ANIMATED_ZOOM; diff --git a/other-licenses/skia-npapi/Makefile.in b/other-licenses/skia-npapi/Makefile.in index c067c426077e..ba85042ea3cc 100644 --- a/other-licenses/skia-npapi/Makefile.in +++ b/other-licenses/skia-npapi/Makefile.in @@ -56,6 +56,7 @@ LOCAL_INCLUDES += \ -I$(topsrcdir)/dom/plugins/base/android \ -I$(topsrcdir)/gfx/skia/include/core \ -I$(topsrcdir)/gfx/skia/include/config \ + -I$(topsrcdir)/gfx/gl \ $(NULL) diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 2e33871497f4..d9b1c75af395 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -1709,9 +1709,9 @@ AndroidBridge::ReleaseNativeWindow(void *window) } bool -AndroidBridge::SetNativeWindowFormat(void *window, int format) +AndroidBridge::SetNativeWindowFormat(void *window, int width, int height, int format) { - return ANativeWindow_setBuffersGeometry(window, 0, 0, format) == 0; + return ANativeWindow_setBuffersGeometry(window, width, height, format) == 0; } bool @@ -1857,3 +1857,85 @@ extern "C" { return jEnv; } } + +jobject +AndroidBridge::CreateSurface() +{ +#ifndef MOZ_JAVA_COMPOSITOR + return NULL; +#else + AutoLocalJNIFrame frame(1); + + JNIEnv* env = GetJNIForThread(); + jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); + + jmethodID method = env->GetStaticMethodID(cls, + "createSurface", + "()Landroid/view/Surface;"); + + jobject surface = env->CallStaticObjectMethod(cls, method); + if (surface) + env->NewGlobalRef(surface); + + return surface; +#endif +} + +void +AndroidBridge::DestroySurface(jobject surface) +{ +#ifdef MOZ_JAVA_COMPOSITOR + AutoLocalJNIFrame frame(1); + + JNIEnv* env = GetJNIForThread(); + jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); + + jmethodID method = env->GetStaticMethodID(cls, + "destroySurface", + "(Landroid/view/Surface;)V"); + env->CallStaticVoidMethod(cls, method, surface); + env->DeleteGlobalRef(surface); +#endif +} + +void +AndroidBridge::ShowSurface(jobject surface, const gfxRect& aRect, bool aInverted, bool aBlend) +{ +#ifdef MOZ_JAVA_COMPOSITOR + AutoLocalJNIFrame frame; + + JNIEnv* env = GetJNIForThread(); + jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); + + nsAutoString metadata; + nsCOMPtr metadataProvider = GetDrawMetadataProvider(); + metadataProvider->GetDrawMetadata(metadata); + + jstring jMetadata = env->NewString(nsPromiseFlatString(metadata).get(), metadata.Length()); + + jmethodID method = env->GetStaticMethodID(cls, + "showSurface", + "(Landroid/view/Surface;IIIIZZLjava/lang/String;)V"); + + env->CallStaticVoidMethod(cls, method, surface, + (int)aRect.x, (int)aRect.y, + (int)aRect.width, (int)aRect.height, + aInverted, aBlend, jMetadata); +#endif +} + +void +AndroidBridge::HideSurface(jobject surface) +{ +#ifdef MOZ_JAVA_COMPOSITOR + AutoLocalJNIFrame frame(1); + + JNIEnv* env = GetJNIForThread(); + jclass cls = env->FindClass("org/mozilla/gecko/GeckoAppShell"); + + jmethodID method = env->GetStaticMethodID(cls, + "hideSurface", + "(Landroid/view/Surface;)V"); + env->CallStaticVoidMethod(cls, method, surface); +#endif +} diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 388fccf8e4f0..9d0d2f164d5b 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -54,6 +54,7 @@ #include "nsIMutableArray.h" #include "nsIMIMEInfo.h" #include "nsColor.h" +#include "gfxRect.h" #include "nsIAndroidBridge.h" @@ -341,7 +342,7 @@ public: void *AcquireNativeWindow(jobject surface); void ReleaseNativeWindow(void *window); - bool SetNativeWindowFormat(void *window, int format); + bool SetNativeWindowFormat(void *window, int width, int height, int format); bool LockWindow(void *window, unsigned char **bits, int *width, int *height, int *format, int *stride); bool UnlockWindow(void *window); @@ -378,6 +379,11 @@ public: void EnableNetworkNotifications(); void DisableNetworkNotifications(); + jobject CreateSurface(); + void DestroySurface(jobject surface); + void ShowSurface(jobject surface, const gfxRect& aRect, bool aInverted, bool aBlend); + void HideSurface(jobject surface); + protected: static AndroidBridge *sBridge; diff --git a/widget/android/AndroidMediaLayer.cpp b/widget/android/AndroidMediaLayer.cpp new file mode 100644 index 000000000000..572dadcc46b0 --- /dev/null +++ b/widget/android/AndroidMediaLayer.cpp @@ -0,0 +1,155 @@ +/* -*- Mode: C++; tab-width: 20; 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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 +#include "AndroidMediaLayer.h" +#include "AndroidBridge.h" + +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "AndroidMediaLayer" , ## args) + + +namespace mozilla { + +AndroidMediaLayer::AndroidMediaLayer() + : mInverted(false) { +} + +AndroidMediaLayer::~AndroidMediaLayer() { + if (mContentData.window) { + AndroidBridge::Bridge()->ReleaseNativeWindow(mContentData.window); + mContentData.window = NULL; + } + + if (mContentData.surface) { + AndroidBridge::Bridge()->DestroySurface(mContentData.surface); + mContentData.surface = NULL; + } + + std::map::iterator it; + + for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) { + SurfaceData* data = it->second; + + AndroidBridge::Bridge()->ReleaseNativeWindow(data->window); + AndroidBridge::Bridge()->DestroySurface(data->surface); + delete data; + } + + mVideoSurfaces.clear(); +} + +bool AndroidMediaLayer::EnsureContentSurface() { + if (!mContentData.surface) { + mContentData.surface = AndroidBridge::Bridge()->CreateSurface(); + if (mContentData.surface) { + mContentData.window = AndroidBridge::Bridge()->AcquireNativeWindow(mContentData.surface); + AndroidBridge::Bridge()->SetNativeWindowFormat(mContentData.window, 0, 0, AndroidBridge::WINDOW_FORMAT_RGBA_8888); + } + } + + return mContentData.surface && mContentData.window; +} + +void* AndroidMediaLayer::GetNativeWindowForContent() { + if (!EnsureContentSurface()) + return NULL; + + return mContentData.window; +} + +void* AndroidMediaLayer::RequestNativeWindowForVideo() { + jobject surface = AndroidBridge::Bridge()->CreateSurface(); + if (surface) { + void* window = AndroidBridge::Bridge()->AcquireNativeWindow(surface); + if (window) { + AndroidBridge::Bridge()->SetNativeWindowFormat(window, 0, 0, AndroidBridge::WINDOW_FORMAT_RGBA_8888); + mVideoSurfaces[window] = new SurfaceData(surface, window); + return window; + } else { + LOG("Failed to create native window from surface"); + + // Cleanup + AndroidBridge::Bridge()->DestroySurface(surface); + } + } + + return NULL; +} + +void AndroidMediaLayer::ReleaseNativeWindowForVideo(void* aWindow) { + if (mVideoSurfaces.find(aWindow) == mVideoSurfaces.end()) + return; + + SurfaceData* data = mVideoSurfaces[aWindow]; + + AndroidBridge::Bridge()->ReleaseNativeWindow(data->window); + AndroidBridge::Bridge()->DestroySurface(data->surface); + + mVideoSurfaces.erase(aWindow); + delete data; +} + +void AndroidMediaLayer::SetNativeWindowDimensions(void* aWindow, const gfxRect& aDimensions) { + if (mVideoSurfaces.find(aWindow) == mVideoSurfaces.end()) + return; + + SurfaceData* data = mVideoSurfaces[aWindow]; + data->dimensions = aDimensions; +} + +void AndroidMediaLayer::UpdatePosition(const gfxRect& aRect, float aZoomLevel) { + + std::map::iterator it; + + if (EnsureContentSurface()) + AndroidBridge::Bridge()->ShowSurface(mContentData.surface, aRect, mInverted, true); + + for (it = mVideoSurfaces.begin(); it != mVideoSurfaces.end(); it++) { + SurfaceData* data = it->second; + + // The video window dimension we get is not adjusted by zoom factor (unlike the + // content window). Fix it up here. + gfxRect scaledDimensions = data->dimensions; + scaledDimensions.Scale(aZoomLevel); + + gfxRect videoRect(aRect.x + scaledDimensions.x, aRect.y + scaledDimensions.y, + scaledDimensions.width, scaledDimensions.height); + AndroidBridge::Bridge()->ShowSurface(data->surface, videoRect, mInverted, false); + } +} + +} /* mozilla */ diff --git a/widget/android/AndroidMediaLayer.h b/widget/android/AndroidMediaLayer.h new file mode 100644 index 000000000000..eb402743fc17 --- /dev/null +++ b/widget/android/AndroidMediaLayer.h @@ -0,0 +1,96 @@ +/* -*- Mode: C++; tab-width: 20; 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 Corporation code. + * + * The Initial Developer of the Original Code is Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2009 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * James Willcox + * + * 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 AndroidMediaLayer_h_ +#define AndroidMediaLayer_h_ + +#include +#include +#include "gfxRect.h" + +namespace mozilla { + +class AndroidMediaLayer +{ +public: + + AndroidMediaLayer(); + virtual ~AndroidMediaLayer(); + + void* GetNativeWindowForContent(); + + void* RequestNativeWindowForVideo(); + void ReleaseNativeWindowForVideo(void* aWindow); + + void SetNativeWindowDimensions(void* aWindow, const gfxRect& aDimensions); + + void UpdatePosition(const gfxRect& aRect, float aZoomLevel); + + bool Inverted() { + return mInverted; + } + + void SetInverted(bool aInverted) { + mInverted = aInverted; + } + +private: + bool mInverted; + + class SurfaceData { + public: + SurfaceData() : + surface(NULL), window(NULL) { + } + + SurfaceData(jobject aSurface, void* aWindow) : + surface(aSurface), window(aWindow) { + } + + jobject surface; + void* window; + gfxRect dimensions; + }; + + bool EnsureContentSurface(); + + SurfaceData mContentData; + std::map mVideoSurfaces; +}; + +} /* mozilla */ +#endif /* AndroidMediaLayer_h_ */ diff --git a/widget/android/Makefile.in b/widget/android/Makefile.in index 7413f0e57cc9..236837b8e200 100644 --- a/widget/android/Makefile.in +++ b/widget/android/Makefile.in @@ -68,6 +68,7 @@ CPPSRCS = \ AndroidDirectTexture.cpp \ AndroidGraphicBuffer.cpp \ AndroidJNI.cpp \ + AndroidMediaLayer.cpp \ nsWindow.cpp \ nsLookAndFeel.cpp \ nsScreenManagerAndroid.cpp \ diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 57606d898733..55a9ce3b7850 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1019,7 +1019,7 @@ nsWindow::OnGlobalAndroidEvent(AndroidGeckoEvent *ae) if (surface) { sNativeWindow = AndroidBridge::Bridge()->AcquireNativeWindow(surface); if (sNativeWindow) { - AndroidBridge::Bridge()->SetNativeWindowFormat(sNativeWindow, AndroidBridge::WINDOW_FORMAT_RGB_565); + AndroidBridge::Bridge()->SetNativeWindowFormat(sNativeWindow, 0, 0, AndroidBridge::WINDOW_FORMAT_RGB_565); } } } From 4bfbbfbe3a94c491876bf4c7a46f1cda4100507d Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Wed, 1 Feb 2012 01:29:09 -0500 Subject: [PATCH 17/90] bug 721741 - Add support for Flash on Android 4.0+, follow up to use reflection and not require ICS SDK r=dougt --- mobile/android/base/GeckoApp.java | 2 +- .../android/base/gfx/SurfaceTextureLayer.java | 23 +++++++++++++++---- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 9e1efe4dc144..59048d219901 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -221,7 +221,7 @@ abstract public class GeckoApp String[] getPluginDirectories() { // we don't support Honeycomb if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB && - Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) + Build.VERSION.SDK_INT < 14 /*Build.VERSION_CODES.ICE_CREAM_SANDWICH*/ ) return new String[0]; Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - start of getPluginDirectories"); diff --git a/mobile/android/base/gfx/SurfaceTextureLayer.java b/mobile/android/base/gfx/SurfaceTextureLayer.java index 8f4799416dbe..a100caebdf1d 100644 --- a/mobile/android/base/gfx/SurfaceTextureLayer.java +++ b/mobile/android/base/gfx/SurfaceTextureLayer.java @@ -84,7 +84,14 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame mSurfaceTexture = new SurfaceTexture(mTextureId); mSurfaceTexture.setOnFrameAvailableListener(this); - mSurface = new Surface(mSurfaceTexture); + Surface tmp = null; + try { + tmp = Surface.class.getConstructor(SurfaceTexture.class).newInstance(mSurfaceTexture); } + catch (Exception ie) { + Log.e(LOGTAG, "error constructing the surface", ie); + } + + mSurface = tmp; float textureMap[] = { 0.0f, 1.0f, // top left @@ -150,9 +157,17 @@ public class SurfaceTextureLayer extends Layer implements SurfaceTexture.OnFrame @Override protected void finalize() throws Throwable { - if (mSurfaceTexture != null) - mSurfaceTexture.release(); - + if (mSurfaceTexture != null) { + try { + SurfaceTexture.class.getDeclaredMethod("release").invoke(mSurfaceTexture); + } catch (NoSuchMethodException nsme) { + Log.e(LOGTAG, "error finding release method on mSurfaceTexture", nsme); + } catch (IllegalAccessException iae) { + Log.e(LOGTAG, "error invoking release method on mSurfaceTexture", iae); + } catch (Exception e) { + Log.e(LOGTAG, "some other exception while invoking release method on mSurfaceTexture", e); + } + } if (mTextureId > 0) TextureReaper.get().add(mTextureId); } From c884d3891b8421c18c2a164f15a38e7cf5b88fe0 Mon Sep 17 00:00:00 2001 From: Chris Lord Date: Wed, 1 Feb 2012 08:50:24 +0000 Subject: [PATCH 18/90] Bug 722325 - Revert bug 720987 for transformed frames. r=roc The fix checked in for bug 720987 caused a major rendering regression with native fennec. Revert it for transformed frames until the correct fix is found. --- layout/base/nsCSSFrameConstructor.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/layout/base/nsCSSFrameConstructor.cpp b/layout/base/nsCSSFrameConstructor.cpp index 33e2d403a79d..d982ca72a107 100644 --- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -8021,7 +8021,17 @@ nsCSSFrameConstructor::ProcessRestyledFrames(nsStyleChangeList& aChangeList) } if (hint & nsChangeHint_UpdateOverflow) { while (frame) { - frame->UpdateOverflow(); + nsOverflowAreas* pre = static_cast + (frame->Properties().Get(frame->PreTransformOverflowAreasProperty())); + if (pre) { + // FinishAndStoreOverflow will change the overflow areas passed in, + // so make a copy. + nsOverflowAreas overflowAreas = *pre; + frame->FinishAndStoreOverflow(overflowAreas, frame->GetSize()); + } else { + frame->UpdateOverflow(); + } + nsIFrame* next = nsLayoutUtils::GetNextContinuationOrSpecialSibling(frame); // Update the ancestors' overflow after we have updated the overflow From 38f7290b4392f7a08e752b5ef50e3dfbaaf520f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20=C3=81vila=20de=20Esp=C3=ADndola?= Date: Wed, 1 Feb 2012 11:16:24 +0100 Subject: [PATCH 19/90] Bug 714960 - Use asyncClose in toolkit/components/satchel/nsFormHistory.js. r=paul. --- toolkit/components/satchel/nsFormHistory.js | 29 ++++++++++++++------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/toolkit/components/satchel/nsFormHistory.js b/toolkit/components/satchel/nsFormHistory.js index bfa3a1d8ad1a..7da929520aa3 100644 --- a/toolkit/components/satchel/nsFormHistory.js +++ b/toolkit/components/satchel/nsFormHistory.js @@ -402,7 +402,7 @@ FormHistory.prototype = { this.expireOldEntries(); break; case "profile-before-change": - this._dbClose(); + this._dbClose(false); break; default: this.log("Oops! Unexpected notification: " + topic); @@ -868,18 +868,29 @@ FormHistory.prototype = { * _dbClose * * Finalize all statements and close the connection. + * + * @param aBlocking - Should we spin the loop waiting for the db to be + * closed. */ - _dbClose : function FH__dbClose() { + _dbClose : function FH__dbClose(aBlocking) { for each (let stmt in this.dbStmts) { stmt.finalize(); } this.dbStmts = {}; - if (this.dbConnection !== undefined) { - try { - this.dbConnection.close(); - } catch (e) { - Components.utils.reportError(e); - } + if (this.dbConnection === undefined) + return; + + let completed = false; + try { + this.dbConnection.asyncClose(function () { completed = true; }); + } catch (e) { + completed = true; + Components.utils.reportError(e); + } + + let thread = Services.tm.currentThread; + while (aBlocking && !completed) { + thread.processNextEvent(true); } }, @@ -898,7 +909,7 @@ FormHistory.prototype = { let backupFile = this.dbFile.leafName + ".corrupt"; storage.backupDatabaseFile(this.dbFile, backupFile); - this._dbClose(); + this._dbClose(true); this.dbFile.remove(false); } }; From c70d70f63447d03546517828a31655a761629959 Mon Sep 17 00:00:00 2001 From: Masatoshi Kimura Date: Wed, 1 Feb 2012 11:36:42 +0100 Subject: [PATCH 20/90] Bug 720208 - Implement DOM4 DOMException types and codes. r=ms2ger,smaug --- content/base/test/test_DOMException.html | 14 ++--- .../test_canvas2d_crossorigin.html | 2 +- .../test_webgl_crossorigin_textures.html | 2 +- content/canvas/test/test_canvas.html | 4 +- content/events/test/test_bug415498.xul | 4 +- content/events/test/test_dragstart.html | 24 ++++---- content/media/test/test_volume.html | 2 +- content/test/unit/test_range.js | 2 +- dom/base/domerr.msg | 55 +++++++++---------- dom/base/nsDOMError.h | 16 +++--- dom/base/nsDOMException.cpp | 4 +- dom/interfaces/core/nsIDOMDOMException.idl | 28 +++++----- dom/tests/mochitest/bugs/test_bug346659.html | 2 +- .../test_localStorageCookieSettings.html | 4 +- .../test_localStorageEnablePref.html | 2 +- .../viewsource/test/test_428653.xul | 4 +- 16 files changed, 83 insertions(+), 86 deletions(-) diff --git a/content/base/test/test_DOMException.html b/content/base/test/test_DOMException.html index 47c5b178312b..c17c25f5c1cb 100644 --- a/content/base/test/test_DOMException.html +++ b/content/base/test/test_DOMException.html @@ -30,13 +30,13 @@ var constants = [ "INVALID_ACCESS_ERR", "VALIDATION_ERR", "TYPE_MISMATCH_ERR", - null, - null, - null, - null, - null, - null, - null, + "SECURITY_ERR", + "NETWORK_ERR", + "ABORT_ERR", + "URL_MISMATCH_ERR", + "QUOTA_EXCEEDED_ERR", + "TIMEOUT_ERR", + "INVALID_NODE_TYPE_ERR", "DATA_CLONE_ERR" ]; for (var i = 0; i < constants.length; ++i) { diff --git a/content/canvas/test/crossorigin/test_canvas2d_crossorigin.html b/content/canvas/test/crossorigin/test_canvas2d_crossorigin.html index aa588ef75b82..c7b3b4daea73 100644 --- a/content/canvas/test/crossorigin/test_canvas2d_crossorigin.html +++ b/content/canvas/test/crossorigin/test_canvas2d_crossorigin.html @@ -21,7 +21,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=685518 SimpleTest.waitForExplicitFinish(); -const SECURITY_ERR = 0x805303e8; +const SECURITY_ERR = 0x80530012; const BAD_URI_ERR = 0x805303f4; const OK = 0; diff --git a/content/canvas/test/crossorigin/test_webgl_crossorigin_textures.html b/content/canvas/test/crossorigin/test_webgl_crossorigin_textures.html index b8a72728782a..e7d54c2da10d 100644 --- a/content/canvas/test/crossorigin/test_webgl_crossorigin_textures.html +++ b/content/canvas/test/crossorigin/test_webgl_crossorigin_textures.html @@ -11,7 +11,7 @@ SimpleTest.waitForExplicitFinish(); - const SECURITY_ERR = 0x805303e8; + const SECURITY_ERR = 0x80530012; const OK = 0; var gl; diff --git a/content/canvas/test/test_canvas.html b/content/canvas/test/test_canvas.html index 60db81adbf9e..e0c78ee46987 100644 --- a/content/canvas/test/test_canvas.html +++ b/content/canvas/test/test_canvas.html @@ -19702,7 +19702,7 @@ function test_bug397524() { try { $("canvas2").toDataURL("image/png"); gotData = true; - } catch (ex if (ex.code == 1000 && ex.name == "NS_ERROR_DOM_SECURITY_ERR")) { + } catch (ex if (ex.code == 18 && ex.name == "SecurityError")) { } is(gotData, false, "Shouldn't be able to read images cross-site!"); @@ -19711,7 +19711,7 @@ function test_bug397524() { try { $("canvas3").toDataURL("image/png"); gotData = true; - } catch (ex if (ex.code == 1000 && ex.name == "NS_ERROR_DOM_SECURITY_ERR")) { + } catch (ex if (ex.code == 18 && ex.name == "SecurityError")) { } is(gotData, false, "Shouldn't be able to read images redirected cross-site!"); diff --git a/content/events/test/test_bug415498.xul b/content/events/test/test_bug415498.xul index 3a46702d3c7a..bc6732c2531e 100644 --- a/content/events/test/test_bug415498.xul +++ b/content/events/test/test_bug415498.xul @@ -78,7 +78,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=415498 browser.loadURI(chromeDir + "bug415498-doc1.html"); yield; - ok(verifyErrorReceived("NS_ERROR_DOM_HIERARCHY_REQUEST_ERR"), + ok(verifyErrorReceived("HierarchyRequestError"), "Error message not reported in event listener callback!"); gMessages = []; @@ -87,7 +87,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=415498 browser.loadURI(chromeDir + "bug415498-doc2.html"); yield; - ok(verifyErrorReceived("NS_ERROR_DOM_HIERARCHY_REQUEST_ERR"), + ok(verifyErrorReceived("HierarchyRequestError"), "Error message not reported in window.onload!"); } diff --git a/content/events/test/test_dragstart.html b/content/events/test/test_dragstart.html index 487a575f7c46..3d518a1e04cd 100644 --- a/content/events/test/test_dragstart.html +++ b/content/events/test/test_dragstart.html @@ -38,17 +38,17 @@ function afterDragTests() checkTypes(gDataTransfer, [], 0, "after dragstart event"); expectError(function() gDataTransfer.setData("text/plain", "Some Text"), - "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "setData when read only"); + "NoModificationAllowedError", "setData when read only"); expectError(function() gDataTransfer.clearData("text/plain"), - "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "clearData when read only"); + "NoModificationAllowedError", "clearData when read only"); expectError(function() gDataTransfer.mozSetDataAt("text/plain", "Some Text", 0), - "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "setDataAt when read only"); + "NoModificationAllowedError", "setDataAt when read only"); expectError(function() gDataTransfer.mozClearDataAt("text/plain", 0), - "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "clearDataAt when read only"); + "NoModificationAllowedError", "clearDataAt when read only"); expectError(function() gDataTransfer.setDragImage(draggable, 10, 10), - "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "setDragImage when read only"); + "NoModificationAllowedError", "setDragImage when read only"); expectError(function() gDataTransfer.addElement(draggable), - "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "addElement when read only"); + "NoModificationAllowedError", "addElement when read only"); var evt = document.createEvent("dragevent"); ok(evt instanceof DragEvent, "synthetic dragevent class") @@ -154,7 +154,7 @@ function test_DataTransfer(dt) // calling setDataAt requires an index that is 0 <= index <= dt.itemCount expectError(function() dt.mozSetDataAt("text/plain", "Some Text", 1), - "NS_ERROR_DOM_INDEX_SIZE_ERR", "setDataAt index too high"); + "IndexSizeError", "setDataAt index too high"); is(dt.mozUserCancelled, false, "userCancelled"); @@ -172,9 +172,9 @@ function test_DataTransfer(dt) // similar with clearDataAt and getDataAt expectError(function() dt.mozGetDataAt("text/plain", 1), - "NS_ERROR_DOM_INDEX_SIZE_ERR", "getDataAt index too high"); + "IndexSizeError", "getDataAt index too high"); expectError(function() dt.mozClearDataAt("text/plain", 1), - "NS_ERROR_DOM_INDEX_SIZE_ERR", "clearDataAt index too high"); + "IndexSizeError", "clearDataAt index too high"); dt.setData("text/plain", "Sample Text"); is(dt.mozItemCount, 1, "added plaintext itemCount"); @@ -285,7 +285,7 @@ function test_DataTransfer(dt) dt.mozSetDataAt("text/plain", "First Item", 0); dt.mozSetDataAt("text/plain", "Second Item", 1); expectError(function() dt.mozSetDataAt("text/plain", "Some Text", 3), - "NS_ERROR_DOM_INDEX_SIZE_ERR", "setDataAt index too high with two items"); + "IndexSizeError", "setDataAt index too high with two items"); is(dt.mozItemCount, 2, "setDataAt item itemCount"); checkOneDataItem(dt, ["text/plain"], ["First Item"], 0, "setDataAt item at index 0"); checkOneDataItem(dt, ["text/plain"], ["Second Item"], 1, "setDataAt item at index 1"); @@ -329,7 +329,7 @@ function test_DataTransfer(dt) ["First Item", "Changed with setData"], 0, "clearDataAt last type at index 0"); checkOneDataItem(dt, ["application/-moz-node"], [draggable], 1, "clearDataAt last type item at index 2"); expectError(function() dt.mozGetDataAt("text/plain", 2), - "NS_ERROR_DOM_INDEX_SIZE_ERR", "getDataAt after item removed index too high"); + "IndexSizeError", "getDataAt after item removed index too high"); dt.mozSetDataAt("text/unknown", "Unknown type", 2); dt.mozSetDataAt("text/unknown", "Unknown type", 1); @@ -356,7 +356,7 @@ function test_DataTransfer(dt) ["Unknown type"], 1, "clearData type that does not exist item at index 1"); expectError(function() dt.mozClearDataAt("text/plain", 3), - "NS_ERROR_DOM_INDEX_SIZE_ERR", "clearData index too high with two items"); + "IndexSizeError", "clearData index too high with two items"); // ensure that clearData() removes all data associated with the first item dt.clearData(); diff --git a/content/media/test/test_volume.html b/content/media/test/test_volume.html index af9399c2febb..81eced620a1d 100644 --- a/content/media/test/test_volume.html +++ b/content/media/test/test_volume.html @@ -16,7 +16,7 @@ function test(element, value, shouldThrow) { var threw = false; try { element.volume = value; - } catch (err if err.name == "NS_ERROR_DOM_INDEX_SIZE_ERR") { + } catch (err if err.name == "IndexSizeError") { threw = true; } is(shouldThrow, threw, "Case: " +element.id+ " setVolume=" + value); diff --git a/content/test/unit/test_range.js b/content/test/unit/test_range.js index d20cf26c4191..912b22c838ff 100644 --- a/content/test/unit/test_range.js +++ b/content/test/unit/test_range.js @@ -43,7 +43,7 @@ const INVALID_STATE_ERR = 0x8053000b; // NS_ERROR_DOM_INVALID_STATE_ERR const INDEX_SIZE_ERR = 0x80530001; // NS_ERROR_DOM_INDEX_SIZE_ERR const INVALID_NODE_TYPE_ERR = 0x805c0002; // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR const NOT_OBJECT_ERR = 0x805303eb; // NS_ERROR_DOM_NOT_OBJECT_ERR -const SECURITY_ERR = 0x805303e8; // NS_ERROR_DOM_SECURITY_ERR +const SECURITY_ERR = 0x80530012; // NS_ERROR_DOM_SECURITY_ERR /** * Determine if the data node has only ignorable white-space. diff --git a/dom/base/domerr.msg b/dom/base/domerr.msg index 450e7d459585..15773986b24f 100644 --- a/dom/base/domerr.msg +++ b/dom/base/domerr.msg @@ -38,37 +38,39 @@ /* Error Message definitions. */ -/* DOM error codes from http://www.w3.org/TR/REC-DOM-Level-1/ */ +/* DOM error codes from http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#domexception */ -DOM_MSG_DEF(NS_ERROR_DOM_INDEX_SIZE_ERR, "Index or size is negative or greater than the allowed amount") -DOM_MSG_DEF(NS_ERROR_DOM_DOMSTRING_SIZE_ERR, "The specified range of text does not fit in a DOM string") -DOM_MSG_DEF(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR, "Node cannot be inserted at the specified point in the hierarchy") -DOM_MSG_DEF(NS_ERROR_DOM_WRONG_DOCUMENT_ERR, "Node cannot be used in a document other than the one in which it was created") -DOM_MSG_DEF(NS_ERROR_DOM_INVALID_CHARACTER_ERR, "String contains an invalid character") -DOM_MSG_DEF(NS_ERROR_DOM_NO_DATA_ALLOWED_ERR, "Node does not contain data") -DOM_MSG_DEF(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR, "Modifications are not allowed for this document") -DOM_MSG_DEF(NS_ERROR_DOM_NOT_FOUND_ERR, "Node was not found") -DOM_MSG_DEF(NS_ERROR_DOM_NOT_SUPPORTED_ERR, "Operation is not supported") -DOM_MSG_DEF(NS_ERROR_DOM_INUSE_ATTRIBUTE_ERR, "Attribute already in use") - -/* DOM error codes from http://www.w3.org/TR/DOM-Level-2/ */ - -DOM_MSG_DEF(NS_ERROR_DOM_INVALID_ACCESS_ERR, "A parameter or an operation is not supported by the underlying object") -DOM_MSG_DEF(NS_ERROR_DOM_INVALID_MODIFICATION_ERR, "An attempt was made to modify the type of the underlying objec") -DOM_MSG_DEF(NS_ERROR_DOM_INVALID_STATE_ERR, "An attempt was made to use an object that is not, or is no longer, usable") -DOM_MSG_DEF(NS_ERROR_DOM_NAMESPACE_ERR, "An attempt was made to create or change an object in a way which is incorrect with regard to namespaces") -DOM_MSG_DEF(NS_ERROR_DOM_SYNTAX_ERR, "An invalid or illegal string was specified") +DOM_MSG_DEF_(INDEX_SIZE_ERR, "IndexSizeError", "Index or size is negative or greater than the allowed amount") +DOM_MSG_DEF_(DOMSTRING_SIZE_ERR, "DOMStringSizeError", "The specified range of text does not fit in a DOM string") +DOM_MSG_DEF_(HIERARCHY_REQUEST_ERR, "HierarchyRequestError", "Node cannot be inserted at the specified point in the hierarchy") +DOM_MSG_DEF_(WRONG_DOCUMENT_ERR, "WrongDocumentError", "Node cannot be used in a document other than the one in which it was created") +DOM_MSG_DEF_(INVALID_CHARACTER_ERR, "InvalidCharacterError", "String contains an invalid character") +DOM_MSG_DEF_(NO_DATA_ALLOWED_ERR, "NoDataAllowedError", "Node does not contain data") +DOM_MSG_DEF_(NO_MODIFICATION_ALLOWED_ERR, "NoModificationAllowedError", "Modifications are not allowed for this document") +DOM_MSG_DEF_(NOT_FOUND_ERR, "NotFoundError", "Node was not found") +DOM_MSG_DEF_(NOT_SUPPORTED_ERR, "NotSupportedError", "Operation is not supported") +DOM_MSG_DEF_(INUSE_ATTRIBUTE_ERR, "InUseAttributeError", "Attribute already in use") +DOM_MSG_DEF_(INVALID_STATE_ERR, "InvalidStateError", "An attempt was made to use an object that is not, or is no longer, usable") +DOM_MSG_DEF_(SYNTAX_ERR, "SyntaxError", "An invalid or illegal string was specified") +DOM_MSG_DEF_(INVALID_MODIFICATION_ERR, "InvalidModificationError", "An attempt was made to modify the type of the underlying objec") +DOM_MSG_DEF_(NAMESPACE_ERR, "NamespaceError", "An attempt was made to create or change an object in a way which is incorrect with regard to namespaces") +DOM_MSG_DEF_(INVALID_ACCESS_ERR, "InvalidAccessError", "A parameter or an operation is not supported by the underlying object") +DOM_MSG_DEF_(VALIDATION_ERR, "ValidationError", "A call to a method would make the Node invalid with respect to \"partial validity\", so the operation was not done") +DOM_MSG_DEF_(TYPE_MISMATCH_ERR, "TypeMismatchError", "The type of an object is incompatible with the expected type of the parameter associated to the object") +DOM_MSG_DEF_(SECURITY_ERR, "SecurityError", "The operation is insecure.") +DOM_MSG_DEF_(NETWORK_ERR, "NetworkError", "A network error occurred.") +DOM_MSG_DEF_(ABORT_ERR, "AbortError", "The operation was aborted. ") +DOM_MSG_DEF_(URL_MISMATCH_ERR, "URLMismatchError", "The given URL does not match another URL.") +DOM_MSG_DEF_(QUOTA_EXCEEDED_ERR, "QuotaExceededError", "The quota has been exceeded.") +DOM_MSG_DEF_(TIMEOUT_ERR, "TimeoutError", "The operation timed out.") +DOM_MSG_DEF_(INVALID_NODE_TYPE_ERR, "InvalidNodeTypeError", "The supplied node is incorrect or has an incorrect ancestor for this operation.") +DOM_MSG_DEF_(DATA_CLONE_ERR, "DataCloneError", "The object could not be cloned.") /* DOM error codes from http://www.w3.org/TR/DOM-Level-2/range.html */ DOM_MSG_DEF(NS_ERROR_DOM_RANGE_BAD_BOUNDARYPOINTS_ERR, "The boundary-points of a range does not meet specific requirements.") DOM_MSG_DEF(NS_ERROR_DOM_RANGE_INVALID_NODE_TYPE_ERR, "The container of an boundary-point of a range is being set to either a node of an invalid type or a node with an ancestor of an invalid type.") -/* DOM error codes from http://www.w3.org/TR/DOM-Level-3/ */ - -DOM_MSG_DEF(NS_ERROR_DOM_VALIDATION_ERR, "A call to a method would make the Node invalid with respect to \"partial validity\", so the operation was not done") -DOM_MSG_DEF(NS_ERROR_DOM_TYPE_MISMATCH_ERR, "The type of an object is incompatible with the expected type of the parameter associated to the object") - /* SVG DOM error codes from http://www.w3.org/TR/SVG11/svgdom.html */ DOM_MSG_DEF(NS_ERROR_DOM_SVG_WRONG_TYPE_ERR, "Unknown or invalid type") @@ -80,10 +82,6 @@ DOM_MSG_DEF(NS_ERROR_DOM_SVG_MATRIX_NOT_INVERTABLE, "The matrix could not be com DOM_MSG_DEF(NS_ERROR_DOM_INVALID_EXPRESSION_ERR, "The expression is not a legal expression.") DOM_MSG_DEF(NS_ERROR_DOM_TYPE_ERR, "The expression cannot be converted to return the specified type.") -/* HTML5 error codes http://dev.w3.org/html5/spec/Overview.html */ - -DOM_MSG_DEF(NS_ERROR_DOM_DATA_CLONE_ERR, "The object could not be cloned.") - /* IndexedDB error codes http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html */ DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, "The operation failed for reasons unrelated to the database itself and not covered by any other error code.") @@ -105,7 +103,6 @@ DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_DEADLOCK_ERR, "The current transaction was au /* DOM error codes defined by us */ /* XXX string should be specified by norris */ -DOM_MSG_DEF(NS_ERROR_DOM_SECURITY_ERR, "Security error") DOM_MSG_DEF(NS_ERROR_DOM_SECMAN_ERR, "Unable to obtain security manager") DOM_MSG_DEF(NS_ERROR_DOM_WRONG_TYPE_ERR, "Object is of wrong type") DOM_MSG_DEF(NS_ERROR_DOM_NOT_OBJECT_ERR, "Parameter is not an object") diff --git a/dom/base/nsDOMError.h b/dom/base/nsDOMError.h index 2715b70dd4bb..24e99017002c 100644 --- a/dom/base/nsDOMError.h +++ b/dom/base/nsDOMError.h @@ -62,15 +62,14 @@ #define NS_ERROR_DOM_INVALID_ACCESS_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,15) #define NS_ERROR_DOM_VALIDATION_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,16) #define NS_ERROR_DOM_TYPE_MISMATCH_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,17) -/* 18: SECURITY_ERR */ -/* 19: NETWORK_ERR */ -/* 20: ABORT_ERR */ -/* 21: URL_MISMATCH_ERR */ -/* 22: QUOTA_EXCEEDED_ERR */ -/* 23: TIMEOUT_ERR */ -/* 24: NOT_READABLE_ERR */ +#define NS_ERROR_DOM_SECURITY_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,18) +#define NS_ERROR_DOM_NETWORK_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,19) +#define NS_ERROR_DOM_ABORT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,20) +#define NS_ERROR_DOM_URL_MISMATCH_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,21) +#define NS_ERROR_DOM_QUOTA_EXCEEDED_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,22) +#define NS_ERROR_DOM_TIMEOUT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,23) +#define NS_ERROR_DOM_INVALID_NODE_TYPE_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,24) #define NS_ERROR_DOM_DATA_CLONE_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,25) -/* 26: ENCODING_ERR */ /* DOM error codes from http://www.w3.org/TR/DOM-Level-2/range.html */ @@ -109,7 +108,6 @@ /* DOM error codes defined by us */ -#define NS_ERROR_DOM_SECURITY_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1000) #define NS_ERROR_DOM_SECMAN_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1001) #define NS_ERROR_DOM_WRONG_TYPE_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1002) #define NS_ERROR_DOM_NOT_OBJECT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1003) diff --git a/dom/base/nsDOMException.cpp b/dom/base/nsDOMException.cpp index 3424462843a1..da2a975b3e80 100644 --- a/dom/base/nsDOMException.cpp +++ b/dom/base/nsDOMException.cpp @@ -51,6 +51,7 @@ #include "prprf.h" #define DOM_MSG_DEF(val, message) {(val), #val, message}, +#define DOM_MSG_DEF_(val, name, message) {(NS_ERROR_DOM_##val), name, message}, #define IMPL_INTERNAL_DOM_EXCEPTION_HEAD(classname, ifname) \ class classname : public nsBaseDOMException, \ @@ -108,6 +109,7 @@ static struct ResultStruct }; #undef DOM_MSG_DEF +#undef DOM_MSG_DEF_ static void NSResultToNameAndMessage(nsresult aNSResult, @@ -157,7 +159,7 @@ IMPL_INTERNAL_DOM_EXCEPTION_TAIL(nsDOMException, nsIDOMDOMException, NSResultToNameAndMessage) NS_IMETHODIMP -nsDOMException::GetCode(PRUint32* aCode) +nsDOMException::GetCode(PRUint16* aCode) { NS_ENSURE_ARG_POINTER(aCode); nsresult result; diff --git a/dom/interfaces/core/nsIDOMDOMException.idl b/dom/interfaces/core/nsIDOMDOMException.idl index 8eeb44d3e952..828c6f7efa7f 100644 --- a/dom/interfaces/core/nsIDOMDOMException.idl +++ b/dom/interfaces/core/nsIDOMDOMException.idl @@ -48,38 +48,38 @@ * implementation has become unstable) * * For more information on this interface please see - * http://www.w3.org/TR/DOM-Level-3-Core/ + * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#domexception */ -[scriptable, uuid(a6cf910a-15b3-11d2-932e-00805f8add32)] +[scriptable, uuid(5bd766d3-57a9-4833-995d-dbe21da29595)] interface nsIDOMDOMException : nsISupports { const unsigned short INDEX_SIZE_ERR = 1; - const unsigned short DOMSTRING_SIZE_ERR = 2; + const unsigned short DOMSTRING_SIZE_ERR = 2; // historical const unsigned short HIERARCHY_REQUEST_ERR = 3; const unsigned short WRONG_DOCUMENT_ERR = 4; const unsigned short INVALID_CHARACTER_ERR = 5; - const unsigned short NO_DATA_ALLOWED_ERR = 6; + const unsigned short NO_DATA_ALLOWED_ERR = 6; // historical const unsigned short NO_MODIFICATION_ALLOWED_ERR = 7; const unsigned short NOT_FOUND_ERR = 8; const unsigned short NOT_SUPPORTED_ERR = 9; - const unsigned short INUSE_ATTRIBUTE_ERR = 10; - // Introduced in DOM Level 2: + const unsigned short INUSE_ATTRIBUTE_ERR = 10; // historical const unsigned short INVALID_STATE_ERR = 11; - // Introduced in DOM Level 2: const unsigned short SYNTAX_ERR = 12; - // Introduced in DOM Level 2: const unsigned short INVALID_MODIFICATION_ERR = 13; - // Introduced in DOM Level 2: const unsigned short NAMESPACE_ERR = 14; - // Introduced in DOM Level 2: const unsigned short INVALID_ACCESS_ERR = 15; - // Introduced in DOM Level 3: - const unsigned short VALIDATION_ERR = 16; - // Introduced in DOM Level 3: + const unsigned short VALIDATION_ERR = 16; // historical const unsigned short TYPE_MISMATCH_ERR = 17; + const unsigned short SECURITY_ERR = 18; + const unsigned short NETWORK_ERR = 19; + const unsigned short ABORT_ERR = 20; + const unsigned short URL_MISMATCH_ERR = 21; + const unsigned short QUOTA_EXCEEDED_ERR = 22; + const unsigned short TIMEOUT_ERR = 23; + const unsigned short INVALID_NODE_TYPE_ERR = 24; const unsigned short DATA_CLONE_ERR = 25; - readonly attribute unsigned long code; + readonly attribute unsigned short code; }; diff --git a/dom/tests/mochitest/bugs/test_bug346659.html b/dom/tests/mochitest/bugs/test_bug346659.html index d2cc7a499b0d..4a1871f93582 100644 --- a/dom/tests/mochitest/bugs/test_bug346659.html +++ b/dom/tests/mochitest/bugs/test_bug346659.html @@ -73,7 +73,7 @@ function handleCmd(evt) { } else { win.childWin.document.write(' diff --git a/dom/base/crashtests/693811-2.html b/dom/base/crashtests/693811-2.html new file mode 100644 index 000000000000..858e66f4f325 --- /dev/null +++ b/dom/base/crashtests/693811-2.html @@ -0,0 +1,16 @@ + + diff --git a/dom/base/crashtests/693811-3.html b/dom/base/crashtests/693811-3.html new file mode 100644 index 000000000000..6e5855e6100a --- /dev/null +++ b/dom/base/crashtests/693811-3.html @@ -0,0 +1,4 @@ + + diff --git a/dom/base/crashtests/crashtests.list b/dom/base/crashtests/crashtests.list index 0503d1f36480..ca11a8923817 100644 --- a/dom/base/crashtests/crashtests.list +++ b/dom/base/crashtests/crashtests.list @@ -30,6 +30,9 @@ load 637116.html load 666869.html load 675621-1.html load 693894.html +load 693811-1.html +load 693811-2.html +load 693811-3.html load 695867.html load 697643.html load 706283-1.html diff --git a/js/xpconnect/src/dombindings.cpp b/js/xpconnect/src/dombindings.cpp index 793a98258f97..f684b77b5609 100644 --- a/js/xpconnect/src/dombindings.cpp +++ b/js/xpconnect/src/dombindings.cpp @@ -222,6 +222,25 @@ Unwrap(JSContext *cx, jsval v, NoType **ppArg, nsISupports **ppArgRef, jsval *vp } +// Because we use proxies for wrapping DOM list objects we don't get the benefits of the property +// cache. To improve performance when using a property that lives on the prototype chain we +// implemented a cheap caching mechanism. Every DOM list proxy object stores a pointer to a shape +// in an extra slot. The first time we access a property on the object that lives on the prototype +// we check if all the DOM properties on the prototype chain are the real DOM properties and in +// that case we store a pointer to the shape of the object's prototype in the extra slot. From +// then on, every time we access a DOM property that lives on the prototype we check that the +// shape of the prototype is still identical to the cached shape and we do a fast lookup of the +// property. If the shape has changed, we recheck all the DOM properties on the prototype chain +// and we update the shape pointer if they are still the real DOM properties. This mechanism +// covers addition/removal of properties, changes in getters/setters, changes in the prototype +// chain, ... It does not cover changes in the values of the properties. For those we store an +// enum value in a reserved slot in every DOM prototype object. The value starts off as USE_CACHE. +// If a property of a DOM prototype object is set to a different value, we set the value to +// CHECK_CACHE. The next time we try to access the value of a property on that DOM prototype +// object we check if all the DOM properties on that DOM prototype object still match the real DOM +// properties. If they do we set the value to USE_CACHE again, if they're not we set the value to +// DONT_USE_CACHE. If the value is USE_CACHE we do the fast lookup. + template ListBase ListBase::instance; @@ -402,6 +421,43 @@ ListBase::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope, bool *en return getPrototype(cx, scope); } +enum { + USE_CACHE = 0, + CHECK_CACHE = 1, + DONT_USE_CACHE = 2 +}; + +static JSBool +InvalidateProtoShape_add(JSContext *cx, JSObject *obj, jsid id, jsval *vp); +static JSBool +InvalidateProtoShape_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp); + +js::Class sInterfacePrototypeClass = { + "Object", + JSCLASS_HAS_RESERVED_SLOTS(1), + InvalidateProtoShape_add, /* addProperty */ + JS_PropertyStub, /* delProperty */ + JS_PropertyStub, /* getProperty */ + InvalidateProtoShape_set, /* setProperty */ + JS_EnumerateStub, + JS_ResolveStub, + JS_ConvertStub +}; + +static JSBool +InvalidateProtoShape_add(JSContext *cx, JSObject *obj, jsid id, jsval *vp) +{ + if (JSID_IS_STRING(id) && JS_InstanceOf(cx, obj, Jsvalify(&sInterfacePrototypeClass), NULL)) + js::SetReservedSlot(obj, 0, PrivateUint32Value(CHECK_CACHE)); + return JS_TRUE; +} + +static JSBool +InvalidateProtoShape_set(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) +{ + return InvalidateProtoShape_add(cx, obj, id, vp); +} + template JSObject * ListBase::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope) @@ -422,7 +478,7 @@ ListBase::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope) return NULL; JSObject *global = scope->GetGlobalJSObject(); - interfacePrototype = JS_NewObject(cx, NULL, proto, global); + interfacePrototype = JS_NewObject(cx, Jsvalify(&sInterfacePrototypeClass), proto, global); if (!interfacePrototype) return NULL; @@ -460,6 +516,10 @@ ListBase::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope) NULL, 0)) return NULL; + // This needs to happen after we've set all our own properties on interfacePrototype, to + // overwrite the value set by InvalidateProtoShape_add when we set our own properties. + js::SetReservedSlot(interfacePrototype, 0, PrivateUint32Value(USE_CACHE)); + if (!cache.Put(sInterfaceClass.name, interfacePrototype)) return NULL; @@ -834,16 +894,18 @@ ListBase::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp) template bool -ListBase::shouldCacheProtoShape(JSContext *cx, JSObject *proto, bool *shouldCache) +ListBase::protoIsClean(JSContext *cx, JSObject *proto, bool *isClean) { JSPropertyDescriptor desc; for (size_t n = 0; n < ArrayLength(sProtoProperties); ++n) { jsid id = sProtoProperties[n].id; if (!JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, &desc)) return false; + JSStrictPropertyOp setter = + sProtoProperties[n].setter ? sProtoProperties[n].setter : InvalidateProtoShape_set; if (desc.obj != proto || desc.getter != sProtoProperties[n].getter || - desc.setter != sProtoProperties[n].setter) { - *shouldCache = false; + desc.setter != setter) { + *isClean = false; return true; } } @@ -853,13 +915,27 @@ ListBase::shouldCacheProtoShape(JSContext *cx, JSObject *proto, bool *should if (!JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, &desc)) return false; if (desc.obj != proto || desc.getter || JSVAL_IS_PRIMITIVE(desc.value) || - n >= js::GetObjectSlotSpan(proto) || js::GetObjectSlot(proto, n) != desc.value || + n >= js::GetObjectSlotSpan(proto) || js::GetObjectSlot(proto, n + 1) != desc.value || !JS_IsNativeFunction(JSVAL_TO_OBJECT(desc.value), sProtoMethods[n].native)) { - *shouldCache = false; + *isClean = false; return true; } } + *isClean = true; + return true; +} + +template +bool +ListBase::shouldCacheProtoShape(JSContext *cx, JSObject *proto, bool *shouldCache) +{ + bool ok = protoIsClean(cx, proto, shouldCache); + if (!ok || !*shouldCache) + return ok; + + js::SetReservedSlot(proto, 0, PrivateUint32Value(USE_CACHE)); + JSObject *protoProto = js::GetObjectProto(proto); if (!protoProto) { *shouldCache = false; @@ -910,10 +986,26 @@ template bool ListBase::nativeGet(JSContext *cx, JSObject *proxy, JSObject *proto, jsid id, bool *found, Value *vp) { + uint32_t cache = js::GetReservedSlot(proto, 0).toPrivateUint32(); + if (cache == CHECK_CACHE) { + bool isClean; + if (!protoIsClean(cx, proto, &isClean)) + return false; + if (!isClean) { + js::SetReservedSlot(proto, 0, PrivateUint32Value(DONT_USE_CACHE)); + return true; + } + js::SetReservedSlot(proto, 0, PrivateUint32Value(USE_CACHE)); + } + else if (cache == DONT_USE_CACHE) { + return true; + } + else { #ifdef DEBUG - bool shouldCache; - JS_ASSERT(shouldCacheProtoShape(cx, proto, &shouldCache) && shouldCache); + bool isClean; + JS_ASSERT(protoIsClean(cx, proto, &isClean) && isClean); #endif + } for (size_t n = 0; n < ArrayLength(sProtoProperties); ++n) { if (id == sProtoProperties[n].id) { @@ -930,7 +1022,7 @@ ListBase::nativeGet(JSContext *cx, JSObject *proxy, JSObject *proto, jsid id if (!vp) return true; - *vp = js::GetObjectSlot(proto, n); + *vp = js::GetObjectSlot(proto, n + 1); JS_ASSERT(JS_IsNativeFunction(&vp->toObject(), sProtoMethods[n].native)); return true; } diff --git a/js/xpconnect/src/dombindings.h b/js/xpconnect/src/dombindings.h index cdf802d482d2..7faed3b3abe9 100644 --- a/js/xpconnect/src/dombindings.h +++ b/js/xpconnect/src/dombindings.h @@ -252,6 +252,7 @@ public: static ListType *getListObject(JSObject *obj); static JSObject *getPrototype(JSContext *cx, XPCWrappedNativeScope *scope); + static inline bool protoIsClean(JSContext *cx, JSObject *proto, bool *isClean); static bool shouldCacheProtoShape(JSContext *cx, JSObject *proto, bool *shouldCache); static bool resolveNativeName(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc); From 4ecf3a4db37c67f0bb0ce82011e9ca9fd77c9b9a Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Fri, 14 Oct 2011 23:21:39 +0200 Subject: [PATCH 24/90] Fix for bug 716997 (Allow new DOM bindings with 0 methods or properties). r=jst. --HG-- extra : rebase_source : 2b16fa38254362cdedc136a8a4b07261ab2f6a76 --- js/xpconnect/src/dombindings.cpp | 32 ++++++++++++++++++++++-------- js/xpconnect/src/dombindings.h | 2 ++ js/xpconnect/src/dombindingsgen.py | 23 +++++++++++++++++---- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/js/xpconnect/src/dombindings.cpp b/js/xpconnect/src/dombindings.cpp index f684b77b5609..de1e129c2464 100644 --- a/js/xpconnect/src/dombindings.cpp +++ b/js/xpconnect/src/dombindings.cpp @@ -63,6 +63,8 @@ static jsid s_prototype_id = JSID_VOID; static jsid s_length_id = JSID_VOID; +static jsid s_VOID_id = JSID_VOID; + bool DefineStaticJSVal(JSContext *cx, jsid &id, const char *string) { @@ -241,6 +243,20 @@ Unwrap(JSContext *cx, jsval v, NoType **ppArg, nsISupports **ppArgRef, jsval *vp // properties. If they do we set the value to USE_CACHE again, if they're not we set the value to // DONT_USE_CACHE. If the value is USE_CACHE we do the fast lookup. +template +typename ListBase::Properties ListBase::sProtoProperties[] = { + { s_VOID_id, NULL, NULL } +}; +template +size_t ListBase::sProtoPropertiesCount = 0; + +template +typename ListBase::Methods ListBase::sProtoMethods[] = { + { s_VOID_id, NULL, 0 } +}; +template +size_t ListBase::sProtoMethodsCount = 0; + template ListBase ListBase::instance; @@ -482,7 +498,7 @@ ListBase::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope) if (!interfacePrototype) return NULL; - for (size_t n = 0; n < ArrayLength(sProtoProperties); ++n) { + for (size_t n = 0; n < sProtoPropertiesCount; ++n) { JS_ASSERT(sProtoProperties[n].getter); jsid id = sProtoProperties[n].id; uintN attrs = JSPROP_ENUMERATE | JSPROP_SHARED; @@ -493,7 +509,7 @@ ListBase::getPrototype(JSContext *cx, XPCWrappedNativeScope *scope) return NULL; } - for (size_t n = 0; n < ArrayLength(sProtoMethods); ++n) { + for (size_t n = 0; n < sProtoMethodsCount; ++n) { jsid id = sProtoMethods[n].id; JSFunction *fun = JS_NewFunctionById(cx, sProtoMethods[n].native, sProtoMethods[n].nargs, 0, js::GetObjectParent(interfacePrototype), id); @@ -897,7 +913,7 @@ bool ListBase::protoIsClean(JSContext *cx, JSObject *proto, bool *isClean) { JSPropertyDescriptor desc; - for (size_t n = 0; n < ArrayLength(sProtoProperties); ++n) { + for (size_t n = 0; n < sProtoPropertiesCount; ++n) { jsid id = sProtoProperties[n].id; if (!JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, &desc)) return false; @@ -910,7 +926,7 @@ ListBase::protoIsClean(JSContext *cx, JSObject *proto, bool *isClean) } } - for (size_t n = 0; n < ArrayLength(sProtoMethods); ++n) { + for (size_t n = 0; n < sProtoMethodsCount; ++n) { jsid id = sProtoMethods[n].id; if (!JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, &desc)) return false; @@ -951,7 +967,7 @@ ListBase::resolveNativeName(JSContext *cx, JSObject *proxy, jsid id, JSPrope { JS_ASSERT(xpc::WrapperFactory::IsXrayWrapper(proxy)); - for (size_t n = 0; n < ArrayLength(sProtoProperties); ++n) { + for (size_t n = 0; n < sProtoPropertiesCount; ++n) { if (id == sProtoProperties[n].id) { desc->attrs = JSPROP_ENUMERATE | JSPROP_SHARED; if (!sProtoProperties[n].setter) @@ -963,7 +979,7 @@ ListBase::resolveNativeName(JSContext *cx, JSObject *proxy, jsid id, JSPrope } } - for (size_t n = 0; n < ArrayLength(sProtoMethods); ++n) { + for (size_t n = 0; n < sProtoMethodsCount; ++n) { if (id == sProtoMethods[n].id) { JSFunction *fun = JS_NewFunctionById(cx, sProtoMethods[n].native, sProtoMethods[n].nargs, 0, proxy, id); @@ -1007,7 +1023,7 @@ ListBase::nativeGet(JSContext *cx, JSObject *proxy, JSObject *proto, jsid id #endif } - for (size_t n = 0; n < ArrayLength(sProtoProperties); ++n) { + for (size_t n = 0; n < sProtoPropertiesCount; ++n) { if (id == sProtoProperties[n].id) { *found = true; if (!vp) @@ -1016,7 +1032,7 @@ ListBase::nativeGet(JSContext *cx, JSObject *proxy, JSObject *proto, jsid id return sProtoProperties[n].getter(cx, proxy, id, vp); } } - for (size_t n = 0; n < ArrayLength(sProtoMethods); ++n) { + for (size_t n = 0; n < sProtoMethodsCount; ++n) { if (id == sProtoMethods[n].id) { *found = true; if (!vp) diff --git a/js/xpconnect/src/dombindings.h b/js/xpconnect/src/dombindings.h index 7faed3b3abe9..4b4786ea26a1 100644 --- a/js/xpconnect/src/dombindings.h +++ b/js/xpconnect/src/dombindings.h @@ -183,7 +183,9 @@ private: }; static Properties sProtoProperties[]; + static size_t sProtoPropertiesCount; static Methods sProtoMethods[]; + static size_t sProtoMethodsCount; static JSObject *ensureExpandoObject(JSContext *cx, JSObject *obj); diff --git a/js/xpconnect/src/dombindingsgen.py b/js/xpconnect/src/dombindingsgen.py index faa4fce97d52..8d84a2bce508 100644 --- a/js/xpconnect/src/dombindingsgen.py +++ b/js/xpconnect/src/dombindingsgen.py @@ -443,7 +443,7 @@ listTemplate = ( " JS_EnumerateStub,\n" " JS_ResolveStub,\n" " JS_ConvertStub,\n" -" NULL, /* finalize */\n" +" JS_FinalizeStub,\n" " NULL, /* reserved0 */\n" " NULL, /* checkAccess */\n" " NULL, /* call */\n" @@ -527,16 +527,27 @@ nameSetterTemplate = ( "}\n" "\n") -listTemplateFooter = ( +propertiesTemplate = ( "template<>\n" "${name}Wrapper::Properties ${name}Wrapper::sProtoProperties[] = {\n" "${properties}\n" "};\n" -"\n""template<>\n" +"\n" +"template<>\n" +"size_t ${name}Wrapper::sProtoPropertiesCount = ArrayLength(${name}Wrapper::sProtoProperties);\n" +"\n") + +methodsTemplate = ( +"template<>\n" "${name}Wrapper::Methods ${name}Wrapper::sProtoMethods[] = {\n" "${methods}\n" "};\n" "\n" +"template<>\n" +"size_t ${name}Wrapper::sProtoMethodsCount = ArrayLength(${name}Wrapper::sProtoMethods);\n" +"\n") + +listTemplateFooter = ( "template class ListBase<${name}Class>;\n" "\n" "JSObject*\n" @@ -718,7 +729,11 @@ def writeStubFile(filename, config, interfaces): else: propertiesList.append(writeAttrStubs(f, clazz.name, member)) - f.write(string.Template(listTemplateFooter).substitute(clazz, methods=",\n".join(methodsList), properties=",\n".join(propertiesList))) + if len(propertiesList) > 0: + f.write(string.Template(propertiesTemplate).substitute(clazz, properties=",\n".join(propertiesList))) + if len(methodsList) > 0: + f.write(string.Template(methodsTemplate).substitute(clazz, methods=",\n".join(methodsList))) + f.write(string.Template(listTemplateFooter).substitute(clazz)) f.write("// Register prototypes\n\n") From 848a63e2e684d1f8ab87cbe5341513973170853a Mon Sep 17 00:00:00 2001 From: Peter Van der Beken Date: Wed, 9 Nov 2011 11:37:47 +0100 Subject: [PATCH 25/90] Fix for bug 717009 (Inline ListBase::instanceIsListObject and ListBase::getListObject). r=bz. --HG-- extra : rebase_source : 368514fa942cb31751e5c6b4ff9d206c1bb609bc --- js/xpconnect/src/dombindings.cpp | 26 +++++++++++++++++--------- js/xpconnect/src/dombindings.h | 4 ++-- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/js/xpconnect/src/dombindings.cpp b/js/xpconnect/src/dombindings.cpp index de1e129c2464..232cc8408031 100644 --- a/js/xpconnect/src/dombindings.cpp +++ b/js/xpconnect/src/dombindings.cpp @@ -304,19 +304,27 @@ ListBase::setProtoShape(JSObject *obj, js::Shape *shape) js::SetProxyExtra(obj, JSPROXYSLOT_PROTOSHAPE, PrivateValue(shape)); } +static JSBool +UnwrapSecurityWrapper(JSContext *cx, JSObject *obj, JSObject *callee, JSObject **unwrapped) +{ + JS_ASSERT(XPCWrapper::IsSecurityWrapper(obj)); + + if (callee && JS_GetGlobalForObject(cx, obj) == JS_GetGlobalForObject(cx, callee)) { + *unwrapped = js::UnwrapObject(obj); + } else { + *unwrapped = XPCWrapper::Unwrap(cx, obj); + if (!*unwrapped) + return Throw(cx, NS_ERROR_XPC_SECURITY_MANAGER_VETO); + } + return true; +} + template bool ListBase::instanceIsListObject(JSContext *cx, JSObject *obj, JSObject *callee) { - if (XPCWrapper::IsSecurityWrapper(obj)) { - if (callee && JS_GetGlobalForObject(cx, obj) == JS_GetGlobalForObject(cx, callee)) { - obj = js::UnwrapObject(obj); - } else { - obj = XPCWrapper::Unwrap(cx, obj); - if (!obj) - return Throw(cx, NS_ERROR_XPC_SECURITY_MANAGER_VETO); - } - } + if (XPCWrapper::IsSecurityWrapper(obj) && !UnwrapSecurityWrapper(cx, obj, callee, &obj)) + return false; if (!objIsList(obj)) { // FIXME: Throw a proper DOM exception. diff --git a/js/xpconnect/src/dombindings.h b/js/xpconnect/src/dombindings.h index 4b4786ea26a1..078a7cba9c5a 100644 --- a/js/xpconnect/src/dombindings.h +++ b/js/xpconnect/src/dombindings.h @@ -246,12 +246,12 @@ public: static bool objIsList(JSObject *obj) { return js::IsProxy(obj) && proxyHandlerIsList(js::GetProxyHandler(obj)); } - static bool instanceIsListObject(JSContext *cx, JSObject *obj, JSObject *callee); + static inline bool instanceIsListObject(JSContext *cx, JSObject *obj, JSObject *callee); virtual bool isInstanceOf(JSObject *prototype) { return js::GetObjectClass(prototype) == &sInterfaceClass; } - static ListType *getListObject(JSObject *obj); + static inline ListType *getListObject(JSObject *obj); static JSObject *getPrototype(JSContext *cx, XPCWrappedNativeScope *scope); static inline bool protoIsClean(JSContext *cx, JSObject *proto, bool *isClean); From 0ce8f755313feb5f579ce1fa048bbe9d681808ae Mon Sep 17 00:00:00 2001 From: Ed Morley Date: Wed, 1 Feb 2012 12:58:07 +0000 Subject: [PATCH 26/90] Backout 0e6b1dfaa99c (bug 720208) for M1 orange --- content/base/test/test_DOMException.html | 14 ++--- .../test_canvas2d_crossorigin.html | 2 +- .../test_webgl_crossorigin_textures.html | 2 +- content/canvas/test/test_canvas.html | 4 +- content/events/test/test_bug415498.xul | 4 +- content/events/test/test_dragstart.html | 24 ++++---- content/media/test/test_volume.html | 2 +- content/test/unit/test_range.js | 2 +- dom/base/domerr.msg | 55 ++++++++++--------- dom/base/nsDOMError.h | 16 +++--- dom/base/nsDOMException.cpp | 4 +- dom/interfaces/core/nsIDOMDOMException.idl | 28 +++++----- dom/tests/mochitest/bugs/test_bug346659.html | 2 +- .../test_localStorageCookieSettings.html | 4 +- .../test_localStorageEnablePref.html | 2 +- .../viewsource/test/test_428653.xul | 4 +- 16 files changed, 86 insertions(+), 83 deletions(-) diff --git a/content/base/test/test_DOMException.html b/content/base/test/test_DOMException.html index c17c25f5c1cb..47c5b178312b 100644 --- a/content/base/test/test_DOMException.html +++ b/content/base/test/test_DOMException.html @@ -30,13 +30,13 @@ var constants = [ "INVALID_ACCESS_ERR", "VALIDATION_ERR", "TYPE_MISMATCH_ERR", - "SECURITY_ERR", - "NETWORK_ERR", - "ABORT_ERR", - "URL_MISMATCH_ERR", - "QUOTA_EXCEEDED_ERR", - "TIMEOUT_ERR", - "INVALID_NODE_TYPE_ERR", + null, + null, + null, + null, + null, + null, + null, "DATA_CLONE_ERR" ]; for (var i = 0; i < constants.length; ++i) { diff --git a/content/canvas/test/crossorigin/test_canvas2d_crossorigin.html b/content/canvas/test/crossorigin/test_canvas2d_crossorigin.html index c7b3b4daea73..aa588ef75b82 100644 --- a/content/canvas/test/crossorigin/test_canvas2d_crossorigin.html +++ b/content/canvas/test/crossorigin/test_canvas2d_crossorigin.html @@ -21,7 +21,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=685518 SimpleTest.waitForExplicitFinish(); -const SECURITY_ERR = 0x80530012; +const SECURITY_ERR = 0x805303e8; const BAD_URI_ERR = 0x805303f4; const OK = 0; diff --git a/content/canvas/test/crossorigin/test_webgl_crossorigin_textures.html b/content/canvas/test/crossorigin/test_webgl_crossorigin_textures.html index e7d54c2da10d..b8a72728782a 100644 --- a/content/canvas/test/crossorigin/test_webgl_crossorigin_textures.html +++ b/content/canvas/test/crossorigin/test_webgl_crossorigin_textures.html @@ -11,7 +11,7 @@ SimpleTest.waitForExplicitFinish(); - const SECURITY_ERR = 0x80530012; + const SECURITY_ERR = 0x805303e8; const OK = 0; var gl; diff --git a/content/canvas/test/test_canvas.html b/content/canvas/test/test_canvas.html index e0c78ee46987..60db81adbf9e 100644 --- a/content/canvas/test/test_canvas.html +++ b/content/canvas/test/test_canvas.html @@ -19702,7 +19702,7 @@ function test_bug397524() { try { $("canvas2").toDataURL("image/png"); gotData = true; - } catch (ex if (ex.code == 18 && ex.name == "SecurityError")) { + } catch (ex if (ex.code == 1000 && ex.name == "NS_ERROR_DOM_SECURITY_ERR")) { } is(gotData, false, "Shouldn't be able to read images cross-site!"); @@ -19711,7 +19711,7 @@ function test_bug397524() { try { $("canvas3").toDataURL("image/png"); gotData = true; - } catch (ex if (ex.code == 18 && ex.name == "SecurityError")) { + } catch (ex if (ex.code == 1000 && ex.name == "NS_ERROR_DOM_SECURITY_ERR")) { } is(gotData, false, "Shouldn't be able to read images redirected cross-site!"); diff --git a/content/events/test/test_bug415498.xul b/content/events/test/test_bug415498.xul index bc6732c2531e..3a46702d3c7a 100644 --- a/content/events/test/test_bug415498.xul +++ b/content/events/test/test_bug415498.xul @@ -78,7 +78,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=415498 browser.loadURI(chromeDir + "bug415498-doc1.html"); yield; - ok(verifyErrorReceived("HierarchyRequestError"), + ok(verifyErrorReceived("NS_ERROR_DOM_HIERARCHY_REQUEST_ERR"), "Error message not reported in event listener callback!"); gMessages = []; @@ -87,7 +87,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=415498 browser.loadURI(chromeDir + "bug415498-doc2.html"); yield; - ok(verifyErrorReceived("HierarchyRequestError"), + ok(verifyErrorReceived("NS_ERROR_DOM_HIERARCHY_REQUEST_ERR"), "Error message not reported in window.onload!"); } diff --git a/content/events/test/test_dragstart.html b/content/events/test/test_dragstart.html index 3d518a1e04cd..487a575f7c46 100644 --- a/content/events/test/test_dragstart.html +++ b/content/events/test/test_dragstart.html @@ -38,17 +38,17 @@ function afterDragTests() checkTypes(gDataTransfer, [], 0, "after dragstart event"); expectError(function() gDataTransfer.setData("text/plain", "Some Text"), - "NoModificationAllowedError", "setData when read only"); + "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "setData when read only"); expectError(function() gDataTransfer.clearData("text/plain"), - "NoModificationAllowedError", "clearData when read only"); + "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "clearData when read only"); expectError(function() gDataTransfer.mozSetDataAt("text/plain", "Some Text", 0), - "NoModificationAllowedError", "setDataAt when read only"); + "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "setDataAt when read only"); expectError(function() gDataTransfer.mozClearDataAt("text/plain", 0), - "NoModificationAllowedError", "clearDataAt when read only"); + "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "clearDataAt when read only"); expectError(function() gDataTransfer.setDragImage(draggable, 10, 10), - "NoModificationAllowedError", "setDragImage when read only"); + "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "setDragImage when read only"); expectError(function() gDataTransfer.addElement(draggable), - "NoModificationAllowedError", "addElement when read only"); + "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "addElement when read only"); var evt = document.createEvent("dragevent"); ok(evt instanceof DragEvent, "synthetic dragevent class") @@ -154,7 +154,7 @@ function test_DataTransfer(dt) // calling setDataAt requires an index that is 0 <= index <= dt.itemCount expectError(function() dt.mozSetDataAt("text/plain", "Some Text", 1), - "IndexSizeError", "setDataAt index too high"); + "NS_ERROR_DOM_INDEX_SIZE_ERR", "setDataAt index too high"); is(dt.mozUserCancelled, false, "userCancelled"); @@ -172,9 +172,9 @@ function test_DataTransfer(dt) // similar with clearDataAt and getDataAt expectError(function() dt.mozGetDataAt("text/plain", 1), - "IndexSizeError", "getDataAt index too high"); + "NS_ERROR_DOM_INDEX_SIZE_ERR", "getDataAt index too high"); expectError(function() dt.mozClearDataAt("text/plain", 1), - "IndexSizeError", "clearDataAt index too high"); + "NS_ERROR_DOM_INDEX_SIZE_ERR", "clearDataAt index too high"); dt.setData("text/plain", "Sample Text"); is(dt.mozItemCount, 1, "added plaintext itemCount"); @@ -285,7 +285,7 @@ function test_DataTransfer(dt) dt.mozSetDataAt("text/plain", "First Item", 0); dt.mozSetDataAt("text/plain", "Second Item", 1); expectError(function() dt.mozSetDataAt("text/plain", "Some Text", 3), - "IndexSizeError", "setDataAt index too high with two items"); + "NS_ERROR_DOM_INDEX_SIZE_ERR", "setDataAt index too high with two items"); is(dt.mozItemCount, 2, "setDataAt item itemCount"); checkOneDataItem(dt, ["text/plain"], ["First Item"], 0, "setDataAt item at index 0"); checkOneDataItem(dt, ["text/plain"], ["Second Item"], 1, "setDataAt item at index 1"); @@ -329,7 +329,7 @@ function test_DataTransfer(dt) ["First Item", "Changed with setData"], 0, "clearDataAt last type at index 0"); checkOneDataItem(dt, ["application/-moz-node"], [draggable], 1, "clearDataAt last type item at index 2"); expectError(function() dt.mozGetDataAt("text/plain", 2), - "IndexSizeError", "getDataAt after item removed index too high"); + "NS_ERROR_DOM_INDEX_SIZE_ERR", "getDataAt after item removed index too high"); dt.mozSetDataAt("text/unknown", "Unknown type", 2); dt.mozSetDataAt("text/unknown", "Unknown type", 1); @@ -356,7 +356,7 @@ function test_DataTransfer(dt) ["Unknown type"], 1, "clearData type that does not exist item at index 1"); expectError(function() dt.mozClearDataAt("text/plain", 3), - "IndexSizeError", "clearData index too high with two items"); + "NS_ERROR_DOM_INDEX_SIZE_ERR", "clearData index too high with two items"); // ensure that clearData() removes all data associated with the first item dt.clearData(); diff --git a/content/media/test/test_volume.html b/content/media/test/test_volume.html index 81eced620a1d..af9399c2febb 100644 --- a/content/media/test/test_volume.html +++ b/content/media/test/test_volume.html @@ -16,7 +16,7 @@ function test(element, value, shouldThrow) { var threw = false; try { element.volume = value; - } catch (err if err.name == "IndexSizeError") { + } catch (err if err.name == "NS_ERROR_DOM_INDEX_SIZE_ERR") { threw = true; } is(shouldThrow, threw, "Case: " +element.id+ " setVolume=" + value); diff --git a/content/test/unit/test_range.js b/content/test/unit/test_range.js index 912b22c838ff..d20cf26c4191 100644 --- a/content/test/unit/test_range.js +++ b/content/test/unit/test_range.js @@ -43,7 +43,7 @@ const INVALID_STATE_ERR = 0x8053000b; // NS_ERROR_DOM_INVALID_STATE_ERR const INDEX_SIZE_ERR = 0x80530001; // NS_ERROR_DOM_INDEX_SIZE_ERR const INVALID_NODE_TYPE_ERR = 0x805c0002; // NS_ERROR_DOM_INVALID_NODE_TYPE_ERR const NOT_OBJECT_ERR = 0x805303eb; // NS_ERROR_DOM_NOT_OBJECT_ERR -const SECURITY_ERR = 0x80530012; // NS_ERROR_DOM_SECURITY_ERR +const SECURITY_ERR = 0x805303e8; // NS_ERROR_DOM_SECURITY_ERR /** * Determine if the data node has only ignorable white-space. diff --git a/dom/base/domerr.msg b/dom/base/domerr.msg index 15773986b24f..450e7d459585 100644 --- a/dom/base/domerr.msg +++ b/dom/base/domerr.msg @@ -38,39 +38,37 @@ /* Error Message definitions. */ -/* DOM error codes from http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#domexception */ +/* DOM error codes from http://www.w3.org/TR/REC-DOM-Level-1/ */ -DOM_MSG_DEF_(INDEX_SIZE_ERR, "IndexSizeError", "Index or size is negative or greater than the allowed amount") -DOM_MSG_DEF_(DOMSTRING_SIZE_ERR, "DOMStringSizeError", "The specified range of text does not fit in a DOM string") -DOM_MSG_DEF_(HIERARCHY_REQUEST_ERR, "HierarchyRequestError", "Node cannot be inserted at the specified point in the hierarchy") -DOM_MSG_DEF_(WRONG_DOCUMENT_ERR, "WrongDocumentError", "Node cannot be used in a document other than the one in which it was created") -DOM_MSG_DEF_(INVALID_CHARACTER_ERR, "InvalidCharacterError", "String contains an invalid character") -DOM_MSG_DEF_(NO_DATA_ALLOWED_ERR, "NoDataAllowedError", "Node does not contain data") -DOM_MSG_DEF_(NO_MODIFICATION_ALLOWED_ERR, "NoModificationAllowedError", "Modifications are not allowed for this document") -DOM_MSG_DEF_(NOT_FOUND_ERR, "NotFoundError", "Node was not found") -DOM_MSG_DEF_(NOT_SUPPORTED_ERR, "NotSupportedError", "Operation is not supported") -DOM_MSG_DEF_(INUSE_ATTRIBUTE_ERR, "InUseAttributeError", "Attribute already in use") -DOM_MSG_DEF_(INVALID_STATE_ERR, "InvalidStateError", "An attempt was made to use an object that is not, or is no longer, usable") -DOM_MSG_DEF_(SYNTAX_ERR, "SyntaxError", "An invalid or illegal string was specified") -DOM_MSG_DEF_(INVALID_MODIFICATION_ERR, "InvalidModificationError", "An attempt was made to modify the type of the underlying objec") -DOM_MSG_DEF_(NAMESPACE_ERR, "NamespaceError", "An attempt was made to create or change an object in a way which is incorrect with regard to namespaces") -DOM_MSG_DEF_(INVALID_ACCESS_ERR, "InvalidAccessError", "A parameter or an operation is not supported by the underlying object") -DOM_MSG_DEF_(VALIDATION_ERR, "ValidationError", "A call to a method would make the Node invalid with respect to \"partial validity\", so the operation was not done") -DOM_MSG_DEF_(TYPE_MISMATCH_ERR, "TypeMismatchError", "The type of an object is incompatible with the expected type of the parameter associated to the object") -DOM_MSG_DEF_(SECURITY_ERR, "SecurityError", "The operation is insecure.") -DOM_MSG_DEF_(NETWORK_ERR, "NetworkError", "A network error occurred.") -DOM_MSG_DEF_(ABORT_ERR, "AbortError", "The operation was aborted. ") -DOM_MSG_DEF_(URL_MISMATCH_ERR, "URLMismatchError", "The given URL does not match another URL.") -DOM_MSG_DEF_(QUOTA_EXCEEDED_ERR, "QuotaExceededError", "The quota has been exceeded.") -DOM_MSG_DEF_(TIMEOUT_ERR, "TimeoutError", "The operation timed out.") -DOM_MSG_DEF_(INVALID_NODE_TYPE_ERR, "InvalidNodeTypeError", "The supplied node is incorrect or has an incorrect ancestor for this operation.") -DOM_MSG_DEF_(DATA_CLONE_ERR, "DataCloneError", "The object could not be cloned.") +DOM_MSG_DEF(NS_ERROR_DOM_INDEX_SIZE_ERR, "Index or size is negative or greater than the allowed amount") +DOM_MSG_DEF(NS_ERROR_DOM_DOMSTRING_SIZE_ERR, "The specified range of text does not fit in a DOM string") +DOM_MSG_DEF(NS_ERROR_DOM_HIERARCHY_REQUEST_ERR, "Node cannot be inserted at the specified point in the hierarchy") +DOM_MSG_DEF(NS_ERROR_DOM_WRONG_DOCUMENT_ERR, "Node cannot be used in a document other than the one in which it was created") +DOM_MSG_DEF(NS_ERROR_DOM_INVALID_CHARACTER_ERR, "String contains an invalid character") +DOM_MSG_DEF(NS_ERROR_DOM_NO_DATA_ALLOWED_ERR, "Node does not contain data") +DOM_MSG_DEF(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR, "Modifications are not allowed for this document") +DOM_MSG_DEF(NS_ERROR_DOM_NOT_FOUND_ERR, "Node was not found") +DOM_MSG_DEF(NS_ERROR_DOM_NOT_SUPPORTED_ERR, "Operation is not supported") +DOM_MSG_DEF(NS_ERROR_DOM_INUSE_ATTRIBUTE_ERR, "Attribute already in use") + +/* DOM error codes from http://www.w3.org/TR/DOM-Level-2/ */ + +DOM_MSG_DEF(NS_ERROR_DOM_INVALID_ACCESS_ERR, "A parameter or an operation is not supported by the underlying object") +DOM_MSG_DEF(NS_ERROR_DOM_INVALID_MODIFICATION_ERR, "An attempt was made to modify the type of the underlying objec") +DOM_MSG_DEF(NS_ERROR_DOM_INVALID_STATE_ERR, "An attempt was made to use an object that is not, or is no longer, usable") +DOM_MSG_DEF(NS_ERROR_DOM_NAMESPACE_ERR, "An attempt was made to create or change an object in a way which is incorrect with regard to namespaces") +DOM_MSG_DEF(NS_ERROR_DOM_SYNTAX_ERR, "An invalid or illegal string was specified") /* DOM error codes from http://www.w3.org/TR/DOM-Level-2/range.html */ DOM_MSG_DEF(NS_ERROR_DOM_RANGE_BAD_BOUNDARYPOINTS_ERR, "The boundary-points of a range does not meet specific requirements.") DOM_MSG_DEF(NS_ERROR_DOM_RANGE_INVALID_NODE_TYPE_ERR, "The container of an boundary-point of a range is being set to either a node of an invalid type or a node with an ancestor of an invalid type.") +/* DOM error codes from http://www.w3.org/TR/DOM-Level-3/ */ + +DOM_MSG_DEF(NS_ERROR_DOM_VALIDATION_ERR, "A call to a method would make the Node invalid with respect to \"partial validity\", so the operation was not done") +DOM_MSG_DEF(NS_ERROR_DOM_TYPE_MISMATCH_ERR, "The type of an object is incompatible with the expected type of the parameter associated to the object") + /* SVG DOM error codes from http://www.w3.org/TR/SVG11/svgdom.html */ DOM_MSG_DEF(NS_ERROR_DOM_SVG_WRONG_TYPE_ERR, "Unknown or invalid type") @@ -82,6 +80,10 @@ DOM_MSG_DEF(NS_ERROR_DOM_SVG_MATRIX_NOT_INVERTABLE, "The matrix could not be com DOM_MSG_DEF(NS_ERROR_DOM_INVALID_EXPRESSION_ERR, "The expression is not a legal expression.") DOM_MSG_DEF(NS_ERROR_DOM_TYPE_ERR, "The expression cannot be converted to return the specified type.") +/* HTML5 error codes http://dev.w3.org/html5/spec/Overview.html */ + +DOM_MSG_DEF(NS_ERROR_DOM_DATA_CLONE_ERR, "The object could not be cloned.") + /* IndexedDB error codes http://dvcs.w3.org/hg/IndexedDB/raw-file/tip/Overview.html */ DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR, "The operation failed for reasons unrelated to the database itself and not covered by any other error code.") @@ -103,6 +105,7 @@ DOM_MSG_DEF(NS_ERROR_DOM_INDEXEDDB_DEADLOCK_ERR, "The current transaction was au /* DOM error codes defined by us */ /* XXX string should be specified by norris */ +DOM_MSG_DEF(NS_ERROR_DOM_SECURITY_ERR, "Security error") DOM_MSG_DEF(NS_ERROR_DOM_SECMAN_ERR, "Unable to obtain security manager") DOM_MSG_DEF(NS_ERROR_DOM_WRONG_TYPE_ERR, "Object is of wrong type") DOM_MSG_DEF(NS_ERROR_DOM_NOT_OBJECT_ERR, "Parameter is not an object") diff --git a/dom/base/nsDOMError.h b/dom/base/nsDOMError.h index 24e99017002c..2715b70dd4bb 100644 --- a/dom/base/nsDOMError.h +++ b/dom/base/nsDOMError.h @@ -62,14 +62,15 @@ #define NS_ERROR_DOM_INVALID_ACCESS_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,15) #define NS_ERROR_DOM_VALIDATION_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,16) #define NS_ERROR_DOM_TYPE_MISMATCH_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,17) -#define NS_ERROR_DOM_SECURITY_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,18) -#define NS_ERROR_DOM_NETWORK_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,19) -#define NS_ERROR_DOM_ABORT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,20) -#define NS_ERROR_DOM_URL_MISMATCH_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,21) -#define NS_ERROR_DOM_QUOTA_EXCEEDED_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,22) -#define NS_ERROR_DOM_TIMEOUT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,23) -#define NS_ERROR_DOM_INVALID_NODE_TYPE_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,24) +/* 18: SECURITY_ERR */ +/* 19: NETWORK_ERR */ +/* 20: ABORT_ERR */ +/* 21: URL_MISMATCH_ERR */ +/* 22: QUOTA_EXCEEDED_ERR */ +/* 23: TIMEOUT_ERR */ +/* 24: NOT_READABLE_ERR */ #define NS_ERROR_DOM_DATA_CLONE_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,25) +/* 26: ENCODING_ERR */ /* DOM error codes from http://www.w3.org/TR/DOM-Level-2/range.html */ @@ -108,6 +109,7 @@ /* DOM error codes defined by us */ +#define NS_ERROR_DOM_SECURITY_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1000) #define NS_ERROR_DOM_SECMAN_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1001) #define NS_ERROR_DOM_WRONG_TYPE_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1002) #define NS_ERROR_DOM_NOT_OBJECT_ERR NS_ERROR_GENERATE_FAILURE(NS_ERROR_MODULE_DOM,1003) diff --git a/dom/base/nsDOMException.cpp b/dom/base/nsDOMException.cpp index da2a975b3e80..3424462843a1 100644 --- a/dom/base/nsDOMException.cpp +++ b/dom/base/nsDOMException.cpp @@ -51,7 +51,6 @@ #include "prprf.h" #define DOM_MSG_DEF(val, message) {(val), #val, message}, -#define DOM_MSG_DEF_(val, name, message) {(NS_ERROR_DOM_##val), name, message}, #define IMPL_INTERNAL_DOM_EXCEPTION_HEAD(classname, ifname) \ class classname : public nsBaseDOMException, \ @@ -109,7 +108,6 @@ static struct ResultStruct }; #undef DOM_MSG_DEF -#undef DOM_MSG_DEF_ static void NSResultToNameAndMessage(nsresult aNSResult, @@ -159,7 +157,7 @@ IMPL_INTERNAL_DOM_EXCEPTION_TAIL(nsDOMException, nsIDOMDOMException, NSResultToNameAndMessage) NS_IMETHODIMP -nsDOMException::GetCode(PRUint16* aCode) +nsDOMException::GetCode(PRUint32* aCode) { NS_ENSURE_ARG_POINTER(aCode); nsresult result; diff --git a/dom/interfaces/core/nsIDOMDOMException.idl b/dom/interfaces/core/nsIDOMDOMException.idl index 828c6f7efa7f..8eeb44d3e952 100644 --- a/dom/interfaces/core/nsIDOMDOMException.idl +++ b/dom/interfaces/core/nsIDOMDOMException.idl @@ -48,38 +48,38 @@ * implementation has become unstable) * * For more information on this interface please see - * http://dvcs.w3.org/hg/domcore/raw-file/tip/Overview.html#domexception + * http://www.w3.org/TR/DOM-Level-3-Core/ */ -[scriptable, uuid(5bd766d3-57a9-4833-995d-dbe21da29595)] +[scriptable, uuid(a6cf910a-15b3-11d2-932e-00805f8add32)] interface nsIDOMDOMException : nsISupports { const unsigned short INDEX_SIZE_ERR = 1; - const unsigned short DOMSTRING_SIZE_ERR = 2; // historical + const unsigned short DOMSTRING_SIZE_ERR = 2; const unsigned short HIERARCHY_REQUEST_ERR = 3; const unsigned short WRONG_DOCUMENT_ERR = 4; const unsigned short INVALID_CHARACTER_ERR = 5; - const unsigned short NO_DATA_ALLOWED_ERR = 6; // historical + const unsigned short NO_DATA_ALLOWED_ERR = 6; const unsigned short NO_MODIFICATION_ALLOWED_ERR = 7; const unsigned short NOT_FOUND_ERR = 8; const unsigned short NOT_SUPPORTED_ERR = 9; - const unsigned short INUSE_ATTRIBUTE_ERR = 10; // historical + const unsigned short INUSE_ATTRIBUTE_ERR = 10; + // Introduced in DOM Level 2: const unsigned short INVALID_STATE_ERR = 11; + // Introduced in DOM Level 2: const unsigned short SYNTAX_ERR = 12; + // Introduced in DOM Level 2: const unsigned short INVALID_MODIFICATION_ERR = 13; + // Introduced in DOM Level 2: const unsigned short NAMESPACE_ERR = 14; + // Introduced in DOM Level 2: const unsigned short INVALID_ACCESS_ERR = 15; - const unsigned short VALIDATION_ERR = 16; // historical + // Introduced in DOM Level 3: + const unsigned short VALIDATION_ERR = 16; + // Introduced in DOM Level 3: const unsigned short TYPE_MISMATCH_ERR = 17; - const unsigned short SECURITY_ERR = 18; - const unsigned short NETWORK_ERR = 19; - const unsigned short ABORT_ERR = 20; - const unsigned short URL_MISMATCH_ERR = 21; - const unsigned short QUOTA_EXCEEDED_ERR = 22; - const unsigned short TIMEOUT_ERR = 23; - const unsigned short INVALID_NODE_TYPE_ERR = 24; const unsigned short DATA_CLONE_ERR = 25; - readonly attribute unsigned short code; + readonly attribute unsigned long code; }; diff --git a/dom/tests/mochitest/bugs/test_bug346659.html b/dom/tests/mochitest/bugs/test_bug346659.html index 4a1871f93582..d2cc7a499b0d 100644 --- a/dom/tests/mochitest/bugs/test_bug346659.html +++ b/dom/tests/mochitest/bugs/test_bug346659.html @@ -73,7 +73,7 @@ function handleCmd(evt) { } else { win.childWin.document.write(' + diff --git a/layout/reftests/svg/reftest.list b/layout/reftests/svg/reftest.list index 1910c925e4d1..b692e13ad872 100644 --- a/layout/reftests/svg/reftest.list +++ b/layout/reftests/svg/reftest.list @@ -107,6 +107,7 @@ random-if(/^Windows\x20NT\x205\.1/.test(http.oscpu)) == dynamic-text-04.svg dyna == dynamic-use-03.svg pass.svg == dynamic-use-04.svg pass.svg == dynamic-use-05.svg pass.svg +== dynamic-use-06.svg pass.svg random == dynamic-use-nested-01.svg dynamic-use-nested-01-ref.svg # bug 467498 == dynamic-use-remove-width.svg dynamic-use-remove-width-ref.svg == linked-pattern-01.svg pass.svg diff --git a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp index b1c9f0dc7dc2..908906b89229 100644 --- a/layout/svg/base/src/nsSVGForeignObjectFrame.cpp +++ b/layout/svg/base/src/nsSVGForeignObjectFrame.cpp @@ -408,10 +408,16 @@ nsSVGForeignObjectFrame::NotifySVGChanged(PRUint32 aFlags) } } else if (aFlags & COORD_CONTEXT_CHANGED) { - // Our coordinate context's width/height has changed. If we have a - // percentage width/height our dimensions will change so we must reflow. nsSVGForeignObjectElement *fO = static_cast(mContent); + // Coordinate context changes affect mCanvasTM if we have a + // percentage 'x' or 'y' + if (fO->mLengthAttributes[nsSVGForeignObjectElement::X].IsPercentage() || + fO->mLengthAttributes[nsSVGForeignObjectElement::Y].IsPercentage()) { + mCanvasTM = nsnull; + } + // Our coordinate context's width/height has changed. If we have a + // percentage width/height our dimensions will change so we must reflow. if (fO->mLengthAttributes[nsSVGForeignObjectElement::WIDTH].IsPercentage() || fO->mLengthAttributes[nsSVGForeignObjectElement::HEIGHT].IsPercentage()) { reflow = true; diff --git a/layout/svg/base/src/nsSVGUseFrame.cpp b/layout/svg/base/src/nsSVGUseFrame.cpp index 96e3263a0b52..698aedd43ee5 100644 --- a/layout/svg/base/src/nsSVGUseFrame.cpp +++ b/layout/svg/base/src/nsSVGUseFrame.cpp @@ -87,6 +87,9 @@ public: } #endif + // nsISVGChildFrame interface: + virtual void NotifySVGChanged(PRUint32 aFlags); + // nsIAnonymousContentCreator virtual nsresult CreateAnonymousContent(nsTArray& aElements); virtual void AppendAnonymousContentTo(nsBaseContentList& aElements, @@ -177,6 +180,26 @@ nsSVGUseFrame::IsLeaf() const } +//---------------------------------------------------------------------- +// nsISVGChildFrame methods + +void +nsSVGUseFrame::NotifySVGChanged(PRUint32 aFlags) +{ + if (aFlags & COORD_CONTEXT_CHANGED && + !(aFlags & TRANSFORM_CHANGED)) { + // Coordinate context changes affect mCanvasTM if we have a + // percentage 'x' or 'y' + nsSVGUseElement *use = static_cast(mContent); + if (use->mLengthAttributes[nsSVGUseElement::X].IsPercentage() || + use->mLengthAttributes[nsSVGUseElement::Y].IsPercentage()) { + aFlags |= TRANSFORM_CHANGED; + } + } + + nsSVGUseFrameBase::NotifySVGChanged(aFlags); +} + //---------------------------------------------------------------------- // nsIAnonymousContentCreator methods: From def12c94cacf287b066477b0f546d3ff4fc6c546 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20=C3=81vila=20de=20Esp=C3=ADndola?= Date: Wed, 1 Feb 2012 17:29:30 +0100 Subject: [PATCH 32/90] Bug 721812 - WaitForConnectionClosed can create a TOPIC_PLACES_CONNECTION_CLOSED spinner too late. r=mak. --- toolkit/components/places/tests/cpp/places_test_harness.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/toolkit/components/places/tests/cpp/places_test_harness.h b/toolkit/components/places/tests/cpp/places_test_harness.h index 09a63a3c6593..96a797159f56 100644 --- a/toolkit/components/places/tests/cpp/places_test_harness.h +++ b/toolkit/components/places/tests/cpp/places_test_harness.h @@ -334,6 +334,7 @@ static const char TOPIC_PLACES_CONNECTION_CLOSED[] = "places-connection-closed"; class WaitForConnectionClosed : public nsIObserver { + nsRefPtr mSpinner; public: NS_DECL_ISUPPORTS @@ -345,6 +346,7 @@ public: if (os) { MOZ_ALWAYS_TRUE(NS_SUCCEEDED(os->AddObserver(this, TOPIC_PROFILE_CHANGE, false))); } + mSpinner = new WaitForTopicSpinner(TOPIC_PLACES_CONNECTION_CLOSED); } NS_IMETHOD Observe(nsISupports* aSubject, @@ -358,9 +360,7 @@ public: MOZ_ALWAYS_TRUE(NS_SUCCEEDED(os->RemoveObserver(this, aTopic))); } - nsRefPtr spinner = - new WaitForTopicSpinner(TOPIC_PLACES_CONNECTION_CLOSED); - spinner->Spin(); + mSpinner->Spin(); return NS_OK; } From e026e5fb10eddc7246a8041d26d8cb3c5934f1cd Mon Sep 17 00:00:00 2001 From: "Christian Holler (:decoder)" Date: Wed, 1 Feb 2012 11:43:15 -0500 Subject: [PATCH 33/90] Bug 720806 - Improve DeviceManagerADB actions when device is not rooted. r=gbrown --- build/mobile/devicemanagerADB.py | 90 ++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 10 deletions(-) diff --git a/build/mobile/devicemanagerADB.py b/build/mobile/devicemanagerADB.py index 9740ec0cbae3..f71b6469e896 100644 --- a/build/mobile/devicemanagerADB.py +++ b/build/mobile/devicemanagerADB.py @@ -14,8 +14,10 @@ class DeviceManagerADB(DeviceManager): self.retries = 0 self._sock = None self.useRunAs = False + self.haveRoot = False self.useZip = False self.packageName = None + self.tempDir = None if packageName == None: if os.getenv('USER'): packageName = 'org.mozilla.fennec_' + os.getenv('USER') @@ -36,18 +38,30 @@ class DeviceManagerADB(DeviceManager): self.verifyZip() except: self.useZip = False - try: + + def verifyRoot(): # a test to see if we have root privs files = self.listFiles("/data/data") if (len(files) == 1): if (files[0].find("Permission denied") != -1): print "NOT running as root" raise Exception("not running as root") + self.haveRoot = True + + try: + verifyRoot() except: try: self.checkCmd(["root"]) + # The root command does not fail even if ADB cannot get + # root rights (e.g. due to production builds), so we have + # to check again ourselves that we have root now. + verifyRoot() except: - print "restarting as root failed" + if (self.useRunAs): + print "restarting as root failed, but run-as available" + else: + print "restarting as root failed" # external function # returns: @@ -58,9 +72,12 @@ class DeviceManagerADB(DeviceManager): if (os.name == "nt"): destname = destname.replace('\\', '/') if (self.useRunAs): - remoteTmpFile = self.tmpDir + "/" + os.path.basename(localname) + remoteTmpFile = self.getTempDir() + "/" + os.path.basename(localname) self.checkCmd(["push", os.path.realpath(localname), remoteTmpFile]) - self.checkCmdAs(["shell", "cp", remoteTmpFile, destname]) + if self.useDDCopy: + self.checkCmdAs(["shell", "dd", "if=" + remoteTmpFile, "of=" + destname]) + else: + self.checkCmdAs(["shell", "cp", remoteTmpFile, destname]) self.checkCmd(["shell", "rm", remoteTmpFile]) else: self.checkCmd(["push", os.path.realpath(localname), destname]) @@ -329,7 +346,26 @@ class DeviceManagerADB(DeviceManager): # TODO: add debug flags and allow for printing stdout # self.runCmd(["pull", remoteFile, localFile]) try: - self.runCmd(["pull", remoteFile, localFile]).stdout.read() + + # First attempt to pull file regularly + outerr = self.runCmd(["pull", remoteFile, localFile]).communicate() + + # Now check stderr for errors + errl = outerr[1].splitlines() + if (len(errl) == 1): + if (((errl[0].find("Permission denied") != -1) + or (errl[0].find("does not exist") != -1)) + and self.useRunAs): + # If we lack permissions to read but have run-as, then we should try + # to copy the file to a world-readable location first before attempting + # to pull it again. + remoteTmpFile = self.getTempDir() + "/" + os.path.basename(remoteFile) + self.checkCmdAs(["shell", "dd", "if=" + remoteFile, "of=" + remoteTmpFile]) + self.checkCmdAs(["shell", "chmod", "777", remoteTmpFile]) + self.runCmd(["pull", remoteTmpFile, localFile]).stdout.read() + # Clean up temporary file + self.checkCmdAs(["shell", "rm", remoteTmpFile]) + f = open(localFile) ret = f.read() f.close() @@ -422,6 +458,23 @@ class DeviceManagerADB(DeviceManager): self.mkDir(testRoot) return testRoot + # Gets the temporary directory we are using on this device + # base on our device root, ensuring also that it exists. + # + # internal function + # returns: + # success: path for temporary directory + # failure: None + def getTempDir(self): + # Cache result to speed up operations depending + # on the temporary directory. + if self.tempDir == None: + self.tempDir = self.getDeviceRoot() + "/tmp" + if (not self.dirExists(self.tempDir)): + return self.mkDir(self.tempDir) + + return self.tempDir + # Either we will have /tests/fennec or /tests/firefox but we will never have # both. Return the one that exists # TODO: ensure we can support org.mozilla.firefox @@ -543,6 +596,11 @@ class DeviceManagerADB(DeviceManager): return ret def runCmd(self, args): + # If we are not root but have run-as, and we're trying to execute + # a shell command then using run-as is the best we can do + if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"): + args.insert(1, "run-as") + args.insert(2, self.packageName) args.insert(0, "adb") return subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE) @@ -553,6 +611,11 @@ class DeviceManagerADB(DeviceManager): return self.runCmd(args) def checkCmd(self, args): + # If we are not root but have run-as, and we're trying to execute + # a shell command then using run-as is the best we can do + if (not self.haveRoot and self.useRunAs and args[0] == "shell" and args[1] != "run-as"): + args.insert(1, "run-as") + args.insert(2, self.packageName) args.insert(0, "adb") return subprocess.check_call(args) @@ -591,6 +654,11 @@ class DeviceManagerADB(DeviceManager): if (re.search('Usage', data)): return True else: + data = self.runCmd(["shell", "dd", "-"]).stdout.read() + if (re.search('unknown operand', data)): + print "'cp' not found, but 'dd' was found as a replacement" + self.useDDCopy = True + return True print "unable to execute 'cp' on device; consider installing busybox from Android Market" return False @@ -605,12 +673,14 @@ class DeviceManagerADB(DeviceManager): self.useRunAs = False devroot = self.getDeviceRoot() if (packageName and self.isCpAvailable() and devroot): - self.tmpDir = devroot + "/tmp" - if (not self.dirExists(self.tmpDir)): - self.mkDir(self.tmpDir) + tmpDir = self.getTempDir() + self.checkCmd(["shell", "run-as", packageName, "mkdir", devroot + "/sanity"]) - self.checkCmd(["push", os.path.abspath(sys.argv[0]), self.tmpDir + "/tmpfile"]) - self.checkCmd(["shell", "run-as", packageName, "cp", self.tmpDir + "/tmpfile", devroot + "/sanity"]) + self.checkCmd(["push", os.path.abspath(sys.argv[0]), tmpDir + "/tmpfile"]) + if self.useDDCopy: + self.checkCmd(["shell", "run-as", packageName, "dd", "if=" + tmpDir + "/tmpfile", "of=" + devroot + "/sanity/tmpfile"]) + else: + self.checkCmd(["shell", "run-as", packageName, "cp", tmpDir + "/tmpfile", devroot + "/sanity"]) if (self.fileExists(devroot + "/sanity/tmpfile")): print "will execute commands via run-as " + packageName self.packageName = packageName From 00125c4e7af86dad445f74dc9027eb8c8aaef0b4 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Tue, 6 Dec 2011 19:03:05 +0100 Subject: [PATCH 34/90] Bug 673470 - Replace the SQLite SafeBrowsing store with an optimized store. r=dcamp --- storage/src/TelemetryVFS.cpp | 1 - toolkit/components/build/nsToolkitCompsCID.h | 6 +- .../telemetry/TelemetryHistograms.h | 9 +- .../components/url-classifier/ChunkSet.cpp | 136 + toolkit/components/url-classifier/ChunkSet.h | 90 + .../components/url-classifier/Classifier.cpp | 653 +++ .../components/url-classifier/Classifier.h | 128 + toolkit/components/url-classifier/Entries.h | 335 ++ .../components/url-classifier/HashStore.cpp | 950 +++++ toolkit/components/url-classifier/HashStore.h | 213 + .../components/url-classifier/LookupCache.cpp | 776 ++++ .../components/url-classifier/LookupCache.h | 186 + toolkit/components/url-classifier/Makefile.in | 6 + .../url-classifier/ProtocolParser.cpp | 777 ++++ .../url-classifier/ProtocolParser.h | 151 + .../url-classifier/content/listmanager.js | 2 +- .../nsCheckSummedOutputStream.cpp | 92 + .../nsCheckSummedOutputStream.h | 86 + .../nsIUrlClassifierDBService.idl | 16 +- .../nsIUrlClassifierPrefixSet.idl | 14 +- .../nsUrlClassifierDBService.cpp | 3729 ++--------------- .../url-classifier/nsUrlClassifierDBService.h | 9 +- .../nsUrlClassifierPrefixSet.cpp | 157 +- .../url-classifier/nsUrlClassifierPrefixSet.h | 16 +- .../url-classifier/nsUrlClassifierProxies.cpp | 19 +- .../url-classifier/nsUrlClassifierProxies.h | 27 +- .../nsUrlClassifierStreamUpdater.cpp | 12 +- .../tests/unit/head_urlclassifier.js | 25 +- .../url-classifier/tests/unit/test_addsub.js | 9 +- .../tests/unit/test_cleankeycache.js | 195 - .../url-classifier/tests/unit/test_partial.js | 59 +- .../tests/unit/test_prefixset.js | 39 +- .../tests/unit/test_streamupdater.js | 16 +- .../url-classifier/tests/unit/xpcshell.ini | 1 - 34 files changed, 5149 insertions(+), 3791 deletions(-) create mode 100644 toolkit/components/url-classifier/ChunkSet.cpp create mode 100644 toolkit/components/url-classifier/ChunkSet.h create mode 100644 toolkit/components/url-classifier/Classifier.cpp create mode 100644 toolkit/components/url-classifier/Classifier.h create mode 100644 toolkit/components/url-classifier/Entries.h create mode 100644 toolkit/components/url-classifier/HashStore.cpp create mode 100644 toolkit/components/url-classifier/HashStore.h create mode 100644 toolkit/components/url-classifier/LookupCache.cpp create mode 100644 toolkit/components/url-classifier/LookupCache.h create mode 100644 toolkit/components/url-classifier/ProtocolParser.cpp create mode 100644 toolkit/components/url-classifier/ProtocolParser.h create mode 100644 toolkit/components/url-classifier/nsCheckSummedOutputStream.cpp create mode 100644 toolkit/components/url-classifier/nsCheckSummedOutputStream.h delete mode 100644 toolkit/components/url-classifier/tests/unit/test_cleankeycache.js diff --git a/storage/src/TelemetryVFS.cpp b/storage/src/TelemetryVFS.cpp index 3d74dea45d28..6120ed17bd43 100644 --- a/storage/src/TelemetryVFS.cpp +++ b/storage/src/TelemetryVFS.cpp @@ -67,7 +67,6 @@ struct Histograms { Histograms gHistograms[] = { SQLITE_TELEMETRY("places.sqlite", PLACES), - SQLITE_TELEMETRY("urlclassifier3.sqlite", URLCLASSIFIER), SQLITE_TELEMETRY("cookies.sqlite", COOKIES), SQLITE_TELEMETRY("webappsstore.sqlite", WEBAPPS), SQLITE_TELEMETRY(NULL, OTHER) diff --git a/toolkit/components/build/nsToolkitCompsCID.h b/toolkit/components/build/nsToolkitCompsCID.h index 8f501ed13608..c24332cf7e23 100644 --- a/toolkit/components/build/nsToolkitCompsCID.h +++ b/toolkit/components/build/nsToolkitCompsCID.h @@ -163,9 +163,9 @@ #define NS_TYPEAHEADFIND_CID \ { 0xe7f70966, 0x9a37, 0x48d7, { 0x8a, 0xeb, 0x35, 0x99, 0x8f, 0x31, 0x09, 0x0e} } -// {15a892dd-cb0f-4a9f-a27f-8291d5e16653} -#define NS_URLCLASSIFIERPREFIXSET_CID \ -{ 0x15a892dd, 0xcb0f, 0x4a9f, { 0xa2, 0x7f, 0x82, 0x91, 0xd5, 0xe1, 0x66, 0x53} } +// {b21b0fa1-20d2-422a-b2cc-b289c9325811} + #define NS_URLCLASSIFIERPREFIXSET_CID \ +{ 0xb21b0fa1, 0x20d2, 0x422a, { 0xb2, 0xcc, 0xb2, 0x89, 0xc9, 0x32, 0x58, 0x11} } // {5eb7c3c1-ec1f-4007-87cc-eefb37d68ce6} #define NS_URLCLASSIFIERDBSERVICE_CID \ diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h index be77bc7b6d94..063ca844c5d5 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -225,7 +225,6 @@ HISTOGRAM(CHECK_JAVA_ENABLED, 1, 3000, 10, EXPONENTIAL, "Time spent checking if SQLITE_TIME_SPENT(OTHER_ ## NAME, DESC) \ SQLITE_TIME_SPENT(PLACES_ ## NAME, DESC) \ SQLITE_TIME_SPENT(COOKIES_ ## NAME, DESC) \ - SQLITE_TIME_SPENT(URLCLASSIFIER_ ## NAME, DESC) \ SQLITE_TIME_SPENT(WEBAPPS_ ## NAME, DESC) SQLITE_TIME_SPENT(OPEN, "Time spent on SQLite open() (ms)") @@ -238,11 +237,9 @@ SQLITE_TIME_PER_FILE(SYNC, "Time spent on SQLite fsync() (ms)") HISTOGRAM(MOZ_SQLITE_OTHER_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)") HISTOGRAM(MOZ_SQLITE_PLACES_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)") HISTOGRAM(MOZ_SQLITE_COOKIES_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)") -HISTOGRAM(MOZ_SQLITE_URLCLASSIFIER_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)") HISTOGRAM(MOZ_SQLITE_WEBAPPS_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)") HISTOGRAM(MOZ_SQLITE_PLACES_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)") HISTOGRAM(MOZ_SQLITE_COOKIES_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)") -HISTOGRAM(MOZ_SQLITE_URLCLASSIFIER_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)") HISTOGRAM(MOZ_SQLITE_WEBAPPS_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)") HISTOGRAM(MOZ_SQLITE_OTHER_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)") HISTOGRAM(MOZ_STORAGE_ASYNC_REQUESTS_MS, 1, 32768, 20, EXPONENTIAL, "mozStorage async requests completion (ms)") @@ -263,10 +260,14 @@ HISTOGRAM(NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_INTERNAL_MAIN_THREAD, 1, 10000, * Url-Classifier telemetry */ #ifdef MOZ_URL_CLASSIFIER +HISTOGRAM(URLCLASSIFIER_LOOKUP_TIME, 1, 500, 10, EXPONENTIAL, "Time spent per dbservice lookup (ms)") +HISTOGRAM(URLCLASSIFIER_CL_CHECK_TIME, 1, 500, 10, EXPONENTIAL, "Time spent per classifier lookup (ms)") +HISTOGRAM(URLCLASSIFIER_CL_UPDATE_TIME, 20, 15000, 15, EXPONENTIAL, "Time spent per classifier update (ms)") HISTOGRAM(URLCLASSIFIER_PS_FILELOAD_TIME, 1, 1000, 10, EXPONENTIAL, "Time spent loading PrefixSet from file (ms)") HISTOGRAM(URLCLASSIFIER_PS_FALLOCATE_TIME, 1, 1000, 10, EXPONENTIAL, "Time spent fallocating PrefixSet (ms)") HISTOGRAM(URLCLASSIFIER_PS_CONSTRUCT_TIME, 1, 5000, 15, EXPONENTIAL, "Time spent constructing PrefixSet from DB (ms)") -HISTOGRAM(URLCLASSIFIER_PS_LOOKUP_TIME, 1, 500, 10, EXPONENTIAL, "Time spent per PrefixSet lookup (ms)") +HISTOGRAM(URLCLASSIFIER_LC_PREFIXES, 1, 1500000, 15, LINEAR, "Size of the prefix cache in entries") +HISTOGRAM(URLCLASSIFIER_LC_COMPLETIONS, 1, 200, 10, EXPONENTIAL, "Size of the completion cache in entries") HISTOGRAM_BOOLEAN(URLCLASSIFIER_PS_OOM, "Did UrlClassifier run out of memory during PrefixSet construction?") #endif diff --git a/toolkit/components/url-classifier/ChunkSet.cpp b/toolkit/components/url-classifier/ChunkSet.cpp new file mode 100644 index 000000000000..43b1b2068ca0 --- /dev/null +++ b/toolkit/components/url-classifier/ChunkSet.cpp @@ -0,0 +1,136 @@ +//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * Gian-Carlo Pascutto + * + * 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 "ChunkSet.h" + +namespace mozilla { +namespace safebrowsing { + +nsresult +ChunkSet::Serialize(nsACString& aChunkStr) +{ + aChunkStr.Truncate(); + + PRUint32 i = 0; + while (i < mChunks.Length()) { + if (i != 0) { + aChunkStr.Append(','); + } + aChunkStr.AppendInt((PRInt32)mChunks[i]); + + PRUint32 first = i; + PRUint32 last = first; + i++; + while (i < mChunks.Length() && (mChunks[i] == mChunks[i - 1] + 1 || mChunks[i] == mChunks[i - 1])) { + last = i++; + } + + if (last != first) { + aChunkStr.Append('-'); + aChunkStr.AppendInt((PRInt32)mChunks[last]); + } + } + + return NS_OK; +} + +nsresult +ChunkSet::Set(PRUint32 aChunk) +{ + PRUint32 idx = mChunks.BinaryIndexOf(aChunk); + if (idx == nsTArray::NoIndex) { + mChunks.InsertElementSorted(aChunk); + } + return NS_OK; +} + +nsresult +ChunkSet::Unset(PRUint32 aChunk) +{ + mChunks.RemoveElementSorted(aChunk); + + return NS_OK; +} + +bool +ChunkSet::Has(PRUint32 aChunk) const +{ + return mChunks.BinaryIndexOf(aChunk) != nsTArray::NoIndex; +} + +nsresult +ChunkSet::Merge(const ChunkSet& aOther) +{ + const uint32 *dupIter = aOther.mChunks.Elements(); + const uint32 *end = aOther.mChunks.Elements() + aOther.mChunks.Length(); + + for (const uint32 *iter = dupIter; iter != end; iter++) { + nsresult rv = Set(*iter); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsresult +ChunkSet::Remove(const ChunkSet& aOther) +{ + uint32 *addIter = mChunks.Elements(); + uint32 *end = mChunks.Elements() + mChunks.Length(); + + for (uint32 *iter = addIter; iter != end; iter++) { + if (!aOther.Has(*iter)) { + *addIter = *iter; + addIter++; + } + } + + mChunks.SetLength(addIter - mChunks.Elements()); + + return NS_OK; +} + +void +ChunkSet::Clear() +{ + mChunks.Clear(); +} + +} +} diff --git a/toolkit/components/url-classifier/ChunkSet.h b/toolkit/components/url-classifier/ChunkSet.h new file mode 100644 index 000000000000..0a465060e508 --- /dev/null +++ b/toolkit/components/url-classifier/ChunkSet.h @@ -0,0 +1,90 @@ +//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * Gian-Carlo Pascutto + * + * 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 ChunkSet_h__ +#define ChunkSet_h__ + + +#include "Entries.h" +#include "nsString.h" +#include "nsTArray.h" + +namespace mozilla { +namespace safebrowsing { + +/** + * Store the chunks as an array of uint32. + * XXX: We should optimize this further to compress the + * many consecutive numbers. + */ +class ChunkSet { +public: + ChunkSet() {} + ~ChunkSet() {} + + nsresult Serialize(nsACString& aStr); + nsresult Set(PRUint32 aChunk); + nsresult Unset(PRUint32 aChunk); + void Clear(); + nsresult Merge(const ChunkSet& aOther); + nsresult Remove(const ChunkSet& aOther); + + bool Has(PRUint32 chunk) const; + + uint32 Length() const { return mChunks.Length(); } + + nsresult Write(nsIOutputStream* aOut) { + return WriteTArray(aOut, mChunks); + } + + nsresult Read(nsIInputStream* aIn, PRUint32 aNumElements) { + return ReadTArray(aIn, &mChunks, aNumElements); + } + + uint32 *Begin() { return mChunks.Elements(); } + uint32 *End() { return mChunks.Elements() + mChunks.Length(); } + +private: + nsTArray mChunks; +}; + +} +} + +#endif diff --git a/toolkit/components/url-classifier/Classifier.cpp b/toolkit/components/url-classifier/Classifier.cpp new file mode 100644 index 000000000000..0f613d307b80 --- /dev/null +++ b/toolkit/components/url-classifier/Classifier.cpp @@ -0,0 +1,653 @@ +//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * Gian-Carlo Pascutto + * + * 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 "Classifier.h" +#include "nsISimpleEnumerator.h" +#include "nsIRandomGenerator.h" +#include "nsIInputStream.h" +#include "nsISeekableStream.h" +#include "nsIFile.h" +#include "nsAutoPtr.h" +#include "mozilla/Telemetry.h" +#include "prlog.h" + +// NSPR_LOG_MODULES=UrlClassifierDbService:5 +extern PRLogModuleInfo *gUrlClassifierDbServiceLog; +#if defined(PR_LOGGING) +#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) +#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) +#else +#define LOG(args) +#define LOG_ENABLED() (PR_FALSE) +#endif + +namespace mozilla { +namespace safebrowsing { + +Classifier::Classifier() + : mFreshTime(45 * 60) +{ +} + +Classifier::~Classifier() +{ + Close(); +} + +/* + * Generate a unique 32-bit key for this user, which we will + * use to rehash all prefixes. This ensures that different users + * will get hash collisions on different prefixes, which in turn + * avoids that "unlucky" URLs get mysterious slowdowns, and that + * the servers get spammed if any such URL should get slashdotted. + * https://bugzilla.mozilla.org/show_bug.cgi?id=669407#c10 + */ +nsresult +Classifier::InitKey() +{ + nsCOMPtr storeFile; + nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = storeFile->AppendNative(NS_LITERAL_CSTRING("classifier.hashkey")); + NS_ENSURE_SUCCESS(rv, rv); + + bool exists; + rv = storeFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (!exists) { + // generate and store key + nsCOMPtr rg = + do_GetService("@mozilla.org/security/random-generator;1"); + NS_ENSURE_STATE(rg); + + PRUint8 *temp; + nsresult rv = rg->GenerateRandomBytes(sizeof(mHashKey), &temp); + NS_ENSURE_SUCCESS(rv, rv); + memcpy(&mHashKey, temp, sizeof(mHashKey)); + NS_Free(temp); + + nsCOMPtr out; + rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out), storeFile, + -1, -1, 0); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 written; + rv = out->Write(reinterpret_cast(&mHashKey), sizeof(PRUint32), &written); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr safeOut = do_QueryInterface(out); + rv = safeOut->Finish(); + NS_ENSURE_SUCCESS(rv, rv); + + LOG(("Initialized classifier, key = %X", mHashKey)); + } else { + // read key + nsCOMPtr inputStream; + rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), storeFile, + -1, -1, 0); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr seekable = do_QueryInterface(inputStream); + nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); + NS_ENSURE_SUCCESS(rv, rv); + + void *buffer = &mHashKey; + rv = NS_ReadInputStreamToBuffer(inputStream, + &buffer, + sizeof(PRUint32)); + NS_ENSURE_SUCCESS(rv, rv); + + LOG(("Loaded classifier key = %X", mHashKey)); + } + + return NS_OK; +} + +nsresult +Classifier::Open(nsIFile& aCacheDirectory) +{ + nsresult rv; + + mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // Ensure the safebrowsing directory exists. + rv = aCacheDirectory.Clone(getter_AddRefs(mStoreDirectory)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mStoreDirectory->AppendNative(NS_LITERAL_CSTRING("safebrowsing")); + NS_ENSURE_SUCCESS(rv, rv); + + bool storeExists; + rv = mStoreDirectory->Exists(&storeExists); + NS_ENSURE_SUCCESS(rv, rv); + + if (!storeExists) { + rv = mStoreDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); + NS_ENSURE_SUCCESS(rv, rv); + } else { + bool storeIsDir; + rv = mStoreDirectory->IsDirectory(&storeIsDir); + NS_ENSURE_SUCCESS(rv, rv); + if (!storeIsDir) + return NS_ERROR_FILE_DESTINATION_NOT_DIR; + } + + rv = InitKey(); + if (NS_FAILED(rv)) { + // Without a usable key the database is useless + Reset(); + return NS_ERROR_FAILURE; + } + + if (!mTableFreshness.Init()) { + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +Classifier::Close() +{ + DropStores(); + + return NS_OK; +} + +nsresult +Classifier::Reset() +{ + DropStores(); + + nsCOMPtr entries; + nsresult rv = mStoreDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasMore; + while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr file; + rv = entries->GetNext(getter_AddRefs(file)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = file->Remove(PR_FALSE); + NS_ENSURE_SUCCESS(rv, rv); + } + NS_ENSURE_SUCCESS(rv, rv); + + mTableFreshness.Clear(); + + return NS_OK; +} + +void +Classifier::TableRequest(nsACString& aResult) +{ + nsTArray tables; + ActiveTables(tables); + for (uint32 i = 0; i < tables.Length(); i++) { + nsAutoPtr store(new HashStore(tables[i], mStoreDirectory)); + if (!store) + continue; + + nsresult rv = store->Open(); + if (NS_FAILED(rv)) + continue; + + aResult.Append(store->TableName()); + aResult.Append(";"); + + ChunkSet &adds = store->AddChunks(); + ChunkSet &subs = store->SubChunks(); + + if (adds.Length() > 0) { + aResult.Append("a:"); + nsCAutoString addList; + adds.Serialize(addList); + aResult.Append(addList); + } + + if (subs.Length() > 0) { + if (adds.Length() > 0) + aResult.Append(':'); + aResult.Append("s:"); + nsCAutoString subList; + subs.Serialize(subList); + aResult.Append(subList); + } + + aResult.Append('\n'); + } +} + +nsresult +Classifier::Check(const nsACString& aSpec, LookupResultArray& aResults) +{ + Telemetry::AutoTimer timer; + + // Get the set of fragments to look up. + nsTArray fragments; + nsresult rv = LookupCache::GetLookupFragments(aSpec, &fragments); + NS_ENSURE_SUCCESS(rv, rv); + + nsTArray activeTables; + ActiveTables(activeTables); + + nsTArray cacheArray; + for (PRUint32 i = 0; i < activeTables.Length(); i++) { + LookupCache *cache = GetLookupCache(activeTables[i]); + if (cache) { + cacheArray.AppendElement(cache); + } else { + return NS_ERROR_FAILURE; + } + } + + // Now check each lookup fragment against the entries in the DB. + for (PRUint32 i = 0; i < fragments.Length(); i++) { + Completion lookupHash; + lookupHash.FromPlaintext(fragments[i], mCryptoHash); + + // Get list of host keys to look up + Completion hostKey; + rv = LookupCache::GetKey(fragments[i], &hostKey, mCryptoHash); + if (NS_FAILED(rv)) { + // Local host on the network + continue; + } + +#if DEBUG && defined(PR_LOGGING) + if (LOG_ENABLED()) { + nsCAutoString checking; + lookupHash.ToString(checking); + LOG(("Checking %s (%X)", checking.get(), lookupHash.ToUint32())); + } +#endif + for (PRUint32 i = 0; i < cacheArray.Length(); i++) { + LookupCache *cache = cacheArray[i]; + bool has, complete; + Prefix codedPrefix; + rv = cache->Has(lookupHash, hostKey, mHashKey, + &has, &complete, &codedPrefix); + NS_ENSURE_SUCCESS(rv, rv); + if (has) { + LookupResult *result = aResults.AppendElement(); + if (!result) + return NS_ERROR_OUT_OF_MEMORY; + + PRInt64 age; + bool found = mTableFreshness.Get(cache->TableName(), &age); + if (!found) { + age = 24 * 60 * 60; // just a large number + } else { + PRInt64 now = (PR_Now() / PR_USEC_PER_SEC); + age = now - age; + } + + LOG(("Found a result in %s: %s (Age: %Lds)", + cache->TableName().get(), + complete ? "complete." : "Not complete.", + age)); + + result->hash.complete = lookupHash; + result->mCodedPrefix = codedPrefix; + result->mComplete = complete; + result->mFresh = (age < mFreshTime); + result->mTableName.Assign(cache->TableName()); + } + } + + } + + return NS_OK; +} + +nsresult +Classifier::ApplyUpdates(nsTArray* aUpdates) +{ + Telemetry::AutoTimer timer; + +#if defined(PR_LOGGING) + PRIntervalTime clockStart = 0; + if (LOG_ENABLED() || true) { + clockStart = PR_IntervalNow(); + } +#endif + + LOG(("Applying table updates.")); + + nsresult rv; + + for (uint32 i = 0; i < aUpdates->Length(); i++) { + // Previous ApplyTableUpdates() may have consumed this update.. + if ((*aUpdates)[i]) { + // Run all updates for one table + rv = ApplyTableUpdates(aUpdates, aUpdates->ElementAt(i)->TableName()); + if (NS_FAILED(rv)) { + Reset(); + return rv; + } + } + } + aUpdates->Clear(); + LOG(("Done applying updates.")); + +#if defined(PR_LOGGING) + if (LOG_ENABLED() || true) { + PRIntervalTime clockEnd = PR_IntervalNow(); + LOG(("update took %dms\n", + PR_IntervalToMilliseconds(clockEnd - clockStart))); + } +#endif + + return NS_OK; +} + +nsresult +Classifier::MarkSpoiled(nsTArray& aTables) +{ + for (uint32 i = 0; i < aTables.Length(); i++) { + LOG(("Spoiling table: %s", aTables[i].get())); + // Spoil this table by marking it as no known freshness + mTableFreshness.Remove(aTables[i]); + } + return NS_OK; +} + +void +Classifier::DropStores() +{ + for (uint32 i = 0; i < mHashStores.Length(); i++) { + delete mHashStores[i]; + } + mHashStores.Clear(); + for (uint32 i = 0; i < mLookupCaches.Length(); i++) { + delete mLookupCaches[i]; + } + mLookupCaches.Clear(); +} + +nsresult +Classifier::ScanStoreDir(nsTArray& aTables) +{ + nsCOMPtr entries; + nsresult rv = mStoreDirectory->GetDirectoryEntries(getter_AddRefs(entries)); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasMore; + while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) { + nsCOMPtr file; + rv = entries->GetNext(getter_AddRefs(file)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString leafName; + rv = file->GetNativeLeafName(leafName); + NS_ENSURE_SUCCESS(rv, rv); + + nsCString suffix(NS_LITERAL_CSTRING(".sbstore")); + + PRInt32 dot = leafName.RFind(suffix, 0); + if (dot != -1) { + leafName.Cut(dot, suffix.Length()); + aTables.AppendElement(leafName); + } + } + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +Classifier::ActiveTables(nsTArray& aTables) +{ + aTables.Clear(); + + nsTArray foundTables; + ScanStoreDir(foundTables); + + for (uint32 i = 0; i < foundTables.Length(); i++) { + nsAutoPtr store(new HashStore(nsCString(foundTables[i]), mStoreDirectory)); + if (!store) + return NS_ERROR_OUT_OF_MEMORY; + + nsresult rv = store->Open(); + if (NS_FAILED(rv)) + continue; + + LookupCache *lookupCache = GetLookupCache(store->TableName()); + if (!lookupCache) { + continue; + } + + const ChunkSet &adds = store->AddChunks(); + const ChunkSet &subs = store->SubChunks(); + + if (adds.Length() == 0 && subs.Length() == 0) + continue; + + LOG(("Active table: %s", store->TableName().get())); + aTables.AppendElement(store->TableName()); + } + + return NS_OK; +} + +/* + * This will consume+delete updates from the passed nsTArray. +*/ +nsresult +Classifier::ApplyTableUpdates(nsTArray* aUpdates, + const nsACString& aTable) +{ + LOG(("Classifier::ApplyTableUpdates(%s)", + PromiseFlatCString(aTable).get())); + + nsAutoPtr store(new HashStore(aTable, mStoreDirectory)); + + if (!store) + return NS_ERROR_FAILURE; + + // take the quick exit if there is no valid update for us + // (common case) + uint32 validupdates = 0; + + for (uint32 i = 0; i < aUpdates->Length(); i++) { + TableUpdate *update = aUpdates->ElementAt(i); + if (!update || !update->TableName().Equals(store->TableName())) + continue; + if (update->Empty()) { + aUpdates->ElementAt(i) = nsnull; + delete update; + continue; + } + validupdates++; + } + + if (!validupdates) { + return NS_OK; + } + + nsresult rv = store->Open(); + NS_ENSURE_SUCCESS(rv, rv); + rv = store->BeginUpdate(); + NS_ENSURE_SUCCESS(rv, rv); + + // Read the part of the store that is (only) in the cache + LookupCache *prefixSet = GetLookupCache(store->TableName()); + if (!prefixSet) { + return NS_ERROR_FAILURE; + } + nsTArray AddPrefixHashes; + rv = prefixSet->GetPrefixes(&AddPrefixHashes); + NS_ENSURE_SUCCESS(rv, rv); + rv = store->AugmentAdds(AddPrefixHashes); + NS_ENSURE_SUCCESS(rv, rv); + + uint32 applied = 0; + bool updateFreshness = false; + + for (uint32 i = 0; i < aUpdates->Length(); i++) { + TableUpdate *update = aUpdates->ElementAt(i); + if (!update || !update->TableName().Equals(store->TableName())) + continue; + + rv = store->ApplyUpdate(*update); + NS_ENSURE_SUCCESS(rv, rv); + + applied++; + + LOG(("Applied update to table %s:", PromiseFlatCString(store->TableName()).get())); + LOG((" %d add chunks", update->AddChunks().Length())); + LOG((" %d add prefixes", update->AddPrefixes().Length())); + LOG((" %d add completions", update->AddCompletes().Length())); + LOG((" %d sub chunks", update->SubChunks().Length())); + LOG((" %d sub prefixes", update->SubPrefixes().Length())); + LOG((" %d sub completions", update->SubCompletes().Length())); + LOG((" %d add expirations", update->AddExpirations().Length())); + LOG((" %d sub expirations", update->SubExpirations().Length())); + + if (!update->IsLocalUpdate()) { + updateFreshness = true; + LOG(("Remote update, updating freshness")); + } + + aUpdates->ElementAt(i) = nsnull; + delete update; + } + + LOG(("Applied %d update(s) to %s.", applied, PromiseFlatCString(store->TableName()).get())); + + rv = store->Rebuild(); + NS_ENSURE_SUCCESS(rv, rv); + + LOG(("Table %s now has:", PromiseFlatCString(store->TableName()).get())); + LOG((" %d add chunks", store->AddChunks().Length())); + LOG((" %d add prefixes", store->AddPrefixes().Length())); + LOG((" %d add completions", store->AddCompletes().Length())); + LOG((" %d sub chunks", store->SubChunks().Length())); + LOG((" %d sub prefixes", store->SubPrefixes().Length())); + LOG((" %d sub completions", store->SubCompletes().Length())); + + rv = store->WriteFile(); + NS_ENSURE_SUCCESS(rv, rv); + + // At this point the store is updated and written out to disk, but + // the data is still in memory. Build our quick-lookup table here. + rv = prefixSet->Build(store->AddPrefixes(), store->AddCompletes()); + NS_ENSURE_SUCCESS(rv, rv); +#if defined(DEBUG) && defined(PR_LOGGING) + prefixSet->Dump(); +#endif + prefixSet->WriteFile(); + + // This will drop all the temporary storage used during the update. + rv = store->FinishUpdate(); + NS_ENSURE_SUCCESS(rv, rv); + + if (updateFreshness) { + PRInt64 now = (PR_Now() / PR_USEC_PER_SEC); + LOG(("Successfully updated %s", PromiseFlatCString(store->TableName()).get())); + rv = (mTableFreshness.Put(store->TableName(), now) ? NS_OK : NS_ERROR_FAILURE); + } + + return rv; +} + +LookupCache * +Classifier::GetLookupCache(const nsACString& aTable) +{ + for (uint32 i = 0; i < mLookupCaches.Length(); i++) { + if (mLookupCaches[i]->TableName().Equals(aTable)) { + return mLookupCaches[i]; + } + } + + LookupCache *cache = new LookupCache(aTable, mStoreDirectory); + nsresult rv = cache->Init(); + if (NS_FAILED(rv)) { + return nsnull; + } + rv = cache->Open(); + if (NS_FAILED(rv)) { + if (rv == NS_ERROR_FILE_CORRUPTED) { + Reset(); + } + return nsnull; + } + mLookupCaches.AppendElement(cache); + return cache; +} + +nsresult +Classifier::ReadNoiseEntries(const Prefix& aPrefix, + const nsACString& aTableName, + PRInt32 aCount, + PrefixArray* aNoiseEntries) +{ + LookupCache *cache = GetLookupCache(aTableName); + if (!cache) { + return NS_ERROR_FAILURE; + } + + nsTArray prefixes; + nsresult rv = cache->GetPrefixes(&prefixes); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 idx = prefixes.BinaryIndexOf(aPrefix.ToUint32()); + + if (idx == nsTArray::NoIndex) { + NS_WARNING("Could not find prefix in PrefixSet during noise lookup"); + return NS_ERROR_FAILURE; + } + + idx -= idx % aCount; + + for (PRInt32 i = 0; (i < aCount) && ((idx+i) < prefixes.Length()); i++) { + Prefix newPref; + newPref.FromUint32(prefixes[idx+i]); + aNoiseEntries->AppendElement(newPref); + } + + return NS_OK; +} + +} +} diff --git a/toolkit/components/url-classifier/Classifier.h b/toolkit/components/url-classifier/Classifier.h new file mode 100644 index 000000000000..b6eb00a73b67 --- /dev/null +++ b/toolkit/components/url-classifier/Classifier.h @@ -0,0 +1,128 @@ +//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * Gian-Carlo Pascutto + * + * 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 Classifier_h__ +#define Classifier_h__ + +#include "Entries.h" +#include "HashStore.h" +#include "ProtocolParser.h" +#include "LookupCache.h" +#include "nsCOMPtr.h" +#include "nsString.h" +#include "nsIFile.h" +#include "nsICryptoHash.h" +#include "nsDataHashtable.h" + +namespace mozilla { +namespace safebrowsing { + +/** + * Maintains the stores and LookupCaches for the url classifier. + */ +class Classifier { +public: + Classifier(); + ~Classifier(); + + nsresult Open(nsIFile& aCacheDirectory); + nsresult Close(); + nsresult Reset(); + + /** + * Get the list of active tables and their chunks in a format + * suitable for an update request. + */ + void TableRequest(nsACString& aResult); + + /* + * Get all tables that we know about. + */ + nsresult ActiveTables(nsTArray& aTables); + + /** + * Check a URL against the database. + */ + nsresult Check(const nsACString& aSpec, LookupResultArray& aResults); + + /** + * Apply the table updates in the array. Takes ownership of + * the updates in the array and clears it. Wacky! + */ + nsresult ApplyUpdates(nsTArray* aUpdates); + /** + * Failed update. Spoil the entries so we don't block hosts + * unnecessarily + */ + nsresult MarkSpoiled(nsTArray& aTables); + nsresult CacheCompletions(const CacheResultArray& aResults); + PRUint32 GetHashKey(void) { return mHashKey; }; + void SetFreshTime(PRUint32 aTime) { mFreshTime = aTime; }; + /* + * Get a bunch of extra prefixes to query for completion + * and mask the real entry being requested + */ + nsresult ReadNoiseEntries(const Prefix& aPrefix, + const nsACString& aTableName, + PRInt32 aCount, + PrefixArray* aNoiseEntries); +private: + void DropStores(); + nsresult ScanStoreDir(nsTArray& aTables); + + nsresult ApplyTableUpdates(nsTArray* aUpdates, + const nsACString& aTable); + + LookupCache *GetLookupCache(const nsACString& aTable); + nsresult InitKey(); + + nsCOMPtr mCryptoHash; + nsCOMPtr mStoreDirectory; + nsTArray mHashStores; + nsTArray mLookupCaches; + PRUint32 mHashKey; + // Stores the last time a given table was updated (seconds). + nsDataHashtable mTableFreshness; + PRUint32 mFreshTime; +}; + +} +} + +#endif diff --git a/toolkit/components/url-classifier/Entries.h b/toolkit/components/url-classifier/Entries.h new file mode 100644 index 000000000000..357b0ccec2ce --- /dev/null +++ b/toolkit/components/url-classifier/Entries.h @@ -0,0 +1,335 @@ +/* ***** 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 Url Classifier code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * Gian-Carlo Pascutto + * + * 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 ***** */ + +//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +#ifndef SBEntries_h__ +#define SBEntries_h__ + +#include "nsTArray.h" +#include "nsString.h" +#include "nsICryptoHash.h" +#include "nsNetUtil.h" +#include "prlog.h" + +extern PRLogModuleInfo *gUrlClassifierDbServiceLog; +#if defined(PR_LOGGING) +#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) +#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) +#else +#define LOG(args) +#define LOG_ENABLED() (PR_FALSE) +#endif + +#if DEBUG +#include "plbase64.h" +#endif + +namespace mozilla { +namespace safebrowsing { + +#define PREFIX_SIZE 4 +#define COMPLETE_SIZE 32 + +template +struct SafebrowsingHash +{ + static const uint32 sHashSize = S; + typedef SafebrowsingHash self_type; + uint8 buf[S]; + + nsresult FromPlaintext(const nsACString& aPlainText, nsICryptoHash* aHash) { + // From the protocol doc: + // Each entry in the chunk is composed + // of the SHA 256 hash of a suffix/prefix expression. + + nsresult rv = aHash->Init(nsICryptoHash::SHA256); + NS_ENSURE_SUCCESS(rv, rv); + + rv = aHash->Update + (reinterpret_cast(aPlainText.BeginReading()), + aPlainText.Length()); + NS_ENSURE_SUCCESS(rv, rv); + + nsCAutoString hashed; + rv = aHash->Finish(PR_FALSE, hashed); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(hashed.Length() >= sHashSize, + "not enough characters in the hash"); + + memcpy(buf, hashed.BeginReading(), sHashSize); + + return NS_OK; + } + + void Assign(const nsACString& aStr) { + NS_ASSERTION(aStr.Length() >= sHashSize, + "string must be at least sHashSize characters long"); + memcpy(buf, aStr.BeginReading(), sHashSize); + } + + int Compare(const self_type& aOther) const { + return Comparator::Compare(buf, aOther.buf); + } + + bool operator==(const self_type& aOther) const { + return Comparator::Compare(buf, aOther.buf) == 0; + } + + bool operator!=(const self_type& aOther) const { + return Comparator::Compare(buf, aOther.buf) != 0; + } + + bool operator<(const self_type& aOther) const { + return Comparator::Compare(buf, aOther.buf) < 0; + } + +#ifdef DEBUG + void ToString(nsACString& aStr) const { + uint32 len = ((sHashSize + 2) / 3) * 4; + aStr.SetCapacity(len + 1); + PL_Base64Encode((char*)buf, sHashSize, aStr.BeginWriting()); + aStr.BeginWriting()[len] = '\0'; + } +#endif + PRUint32 ToUint32() const { + PRUint32 res = 0; + memcpy(&res, buf, NS_MIN(4, S)); + return res; + } + void FromUint32(PRUint32 aHash) { + memcpy(buf, &aHash, NS_MIN(4, S)); + } +}; + +class PrefixComparator { +public: + static int Compare(const PRUint8* a, const PRUint8* b) { + return *((uint32*)a) - *((uint32*)b); + } +}; +typedef SafebrowsingHash Prefix; +typedef nsTArray PrefixArray; + +class CompletionComparator { +public: + static int Compare(const PRUint8* a, const PRUint8* b) { + return memcmp(a, b, COMPLETE_SIZE); + } +}; +typedef SafebrowsingHash Completion; +typedef nsTArray CompletionArray; + +struct AddPrefix { + Prefix prefix; + uint32 addChunk; + + AddPrefix() : addChunk(0) {} + + uint32 Chunk() const { return addChunk; } + const Prefix &PrefixHash() const { return prefix; } + + template + int Compare(const T& other) const { + int cmp = prefix.Compare(other.PrefixHash()); + if (cmp != 0) { + return cmp; + } + return addChunk - other.addChunk; + } +}; + +struct AddComplete { + union { + Prefix prefix; + Completion complete; + } hash; + uint32 addChunk; + + AddComplete() : addChunk(0) {} + + uint32 Chunk() const { return addChunk; } + const Prefix &PrefixHash() const { return hash.prefix; } + const Completion &CompleteHash() const { return hash.complete; } + + template + int Compare(const T& other) const { + int cmp = hash.complete.Compare(other.CompleteHash()); + if (cmp != 0) { + return cmp; + } + return addChunk - other.addChunk; + } +}; + +struct SubPrefix { + Prefix prefix; + uint32 addChunk; + uint32 subChunk; + + SubPrefix(): addChunk(0), subChunk(0) {} + + uint32 Chunk() const { return subChunk; } + uint32 AddChunk() const { return addChunk; } + const Prefix &PrefixHash() const { return prefix; } + + template + int Compare(const T& aOther) const { + int cmp = prefix.Compare(aOther.PrefixHash()); + if (cmp != 0) + return cmp; + if (addChunk != aOther.addChunk) + return addChunk - aOther.addChunk; + return subChunk - aOther.subChunk; + } + + template + int CompareAlt(const T& aOther) const { + int cmp = prefix.Compare(aOther.PrefixHash()); + if (cmp != 0) + return cmp; + return addChunk - aOther.addChunk; + } +}; + +struct SubComplete { + union { + Prefix prefix; + Completion complete; + } hash; + uint32 addChunk; + uint32 subChunk; + + SubComplete() : addChunk(0), subChunk(0) {} + + uint32 Chunk() const { return subChunk; } + uint32 AddChunk() const { return addChunk; } + const Prefix &PrefixHash() const { return hash.prefix; } + const Completion &CompleteHash() const { return hash.complete; } + + int Compare(const SubComplete& aOther) const { + int cmp = hash.complete.Compare(aOther.hash.complete); + if (cmp != 0) + return cmp; + if (addChunk != aOther.addChunk) + return addChunk - aOther.addChunk; + return subChunk - aOther.subChunk; + } +}; + +typedef nsTArray AddPrefixArray; +typedef nsTArray AddCompleteArray; +typedef nsTArray SubPrefixArray; +typedef nsTArray SubCompleteArray; + +/** + * Compares chunks by their add chunk, then their prefix. + */ +template +class EntryCompare { +public: + typedef T elem_type; + static int Compare(const void* e1, const void* e2, void* data) { + const elem_type* a = static_cast(e1); + const elem_type* b = static_cast(e2); + return a->Compare(*b); + } +}; + +template<> +class EntryCompare { +public: + typedef SubPrefix elem_type; + static int Compare(const void* e1, const void* e2, void* data) { + const elem_type* a = static_cast(e1); + const elem_type* b = static_cast(e2); + return a->Compare(*b); + } +}; + +template<> +class EntryCompare { +public: + typedef SubComplete elem_type; + static int Compare(const void* e1, const void* e2, void* data) { + const elem_type *a = static_cast(e1); + const elem_type *b = static_cast(e2); + return a->Compare(*b); + } +}; + +/** + * Sort an array of store entries. nsTArray::Sort uses Equal/LessThan + * to sort, this does a single Compare so it's a bit quicker over the + * large sorts we do. + */ +template +void +EntrySort(nsTArray& aArray) +{ + NS_QuickSort(aArray.Elements(), aArray.Length(), sizeof(T), + EntryCompare::Compare, 0); +} + +template +nsresult +ReadTArray(nsIInputStream* aStream, nsTArray* aArray, PRUint32 aNumElements) +{ + if (!aArray->SetLength(aNumElements)) + return NS_ERROR_OUT_OF_MEMORY; + + void *buffer = aArray->Elements(); + nsresult rv = NS_ReadInputStreamToBuffer(aStream, &buffer, + (aNumElements * sizeof(T))); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; +} + +template +nsresult +WriteTArray(nsIOutputStream* aStream, nsTArray& aArray) +{ + PRUint32 written; + return aStream->Write(reinterpret_cast(aArray.Elements()), + aArray.Length() * sizeof(T), + &written); +} + +} +} +#endif diff --git a/toolkit/components/url-classifier/HashStore.cpp b/toolkit/components/url-classifier/HashStore.cpp new file mode 100644 index 000000000000..234b4c12bc55 --- /dev/null +++ b/toolkit/components/url-classifier/HashStore.cpp @@ -0,0 +1,950 @@ +//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +// Originally based on Chrome sources: +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + +#include "HashStore.h" +#include "nsAutoPtr.h" +#include "nsICryptoHash.h" +#include "nsISeekableStream.h" +#include "nsIStreamConverterService.h" +#include "nsNetUtil.h" +#include "nsCheckSummedOutputStream.h" +#include "prlog.h" +#include "zlib.h" + +// Main store for SafeBrowsing protocol data. We store +// known add/sub chunks, prefixe and completions s in memory +// during an update, and serialize to disk. +// We do not store the add prefixes, those are retrieved by +// decompressing the PrefixSet cache whenever we need to apply +// an update. + +// Data format: +// uint32 magic +// uint32 version +// uint32 numAddChunks +// uint32 numSubChunks +// uint32 numAddPrefixes +// uint32 numSubPrefixes +// uint32 numAddCompletes +// uint32 numSubCompletes +// 0...numAddChunks uint32 addChunk +// 0...numSubChunks uint32 subChunk +// uint32 compressed-size +// compressed-size bytes zlib inflate data +// 0...numAddPrefixes uint32 addChunk +// uint32 compressed-size +// compressed-size bytes zlib inflate data +// 0...numSubPrefixes uint32 addChunk +// uint32 compressed-size +// compressed-size bytes zlib inflate data +// 0...numSubPrefixes uint32 subChunk +// 0...numSubPrefixes uint32 subPrefix +// 0...numAddCompletes 32-byte Completions +// 0...numSubCompletes 32-byte Completions +// 16-byte MD5 of all preceding data + +// NSPR_LOG_MODULES=UrlClassifierDbService:5 +extern PRLogModuleInfo *gUrlClassifierDbServiceLog; +#if defined(PR_LOGGING) +#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) +#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) +#else +#define LOG(args) +#define LOG_ENABLED() (PR_FALSE) +#endif + +namespace mozilla { +namespace safebrowsing { + +const uint32 STORE_MAGIC = 0x1231af3b; +const uint32 CURRENT_VERSION = 1; + +void +TableUpdate::NewAddPrefix(PRUint32 aAddChunk, const Prefix& aHash) +{ + AddPrefix *add = mAddPrefixes.AppendElement(); + add->addChunk = aAddChunk; + add->prefix = aHash; +} + +void +TableUpdate::NewSubPrefix(PRUint32 aAddChunk, const Prefix& aHash, PRUint32 aSubChunk) +{ + SubPrefix *sub = mSubPrefixes.AppendElement(); + sub->addChunk = aAddChunk; + sub->prefix = aHash; + sub->subChunk = aSubChunk; +} + +void +TableUpdate::NewAddComplete(PRUint32 aAddChunk, const Completion& aHash) +{ + AddComplete *add = mAddCompletes.AppendElement(); + add->addChunk = aAddChunk; + add->hash.complete = aHash; +} + +void +TableUpdate::NewSubComplete(PRUint32 aAddChunk, const Completion& aHash, PRUint32 aSubChunk) +{ + SubComplete *sub = mSubCompletes.AppendElement(); + sub->addChunk = aAddChunk; + sub->hash.complete = aHash; + sub->subChunk = aSubChunk; +} + + +HashStore::HashStore(const nsACString& aTableName, nsIFile* aStoreDir) + : mTableName(aTableName) + , mStoreDirectory(aStoreDir) + , mInUpdate(false) +{ +} + +HashStore::~HashStore() +{ +} + +nsresult +HashStore::Reset() +{ + LOG(("HashStore resetting")); + + nsCOMPtr storeFile; + nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = storeFile->Remove(PR_FALSE); + NS_ENSURE_SUCCESS(rv, rv); + + Clear(); + + return NS_OK; +} + +nsresult +HashStore::CheckChecksum(nsIFile* aStoreFile) +{ + // Check for file corruption by + // comparing the stored checksum to actual checksum of data + nsCAutoString hash; + nsCAutoString compareHash; + char *data; + PRUint32 read; + + PRInt64 fileSize; + nsresult rv = aStoreFile->GetFileSize(&fileSize); + NS_ENSURE_SUCCESS(rv, rv); + + if (fileSize < 0) { + return NS_ERROR_FAILURE; + } + + rv = CalculateChecksum(hash, true); + NS_ENSURE_SUCCESS(rv, rv); + + compareHash.GetMutableData(&data, hash.Length()); + + nsCOMPtr seekIn = do_QueryInterface(mInputStream); + rv = seekIn->Seek(nsISeekableStream::NS_SEEK_SET, fileSize-hash.Length()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mInputStream->Read(data, hash.Length(), &read); + NS_ENSURE_SUCCESS(rv, rv); + NS_ASSERTION(read == hash.Length(), "Could not read hash bytes"); + + if (!hash.Equals(compareHash)) { + NS_WARNING("Safebrowing file failed checksum."); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +HashStore::Open() +{ + nsCOMPtr storeFile; + nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore")); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr origStream; + rv = NS_NewLocalFileInputStream(getter_AddRefs(origStream), storeFile, + PR_RDONLY); + + if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) { + Reset(); + return rv; + } + + if (rv == NS_ERROR_FILE_NOT_FOUND) { + Clear(); + UpdateHeader(); + return NS_OK; + } + + rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream), origStream, + BUFFER_SIZE); + NS_ENSURE_SUCCESS(rv, rv); + + rv = CheckChecksum(storeFile); + if (NS_FAILED(rv)) { + Reset(); + return rv; + } + + rv = ReadHeader(); + if (NS_FAILED(rv)) { + Reset(); + return rv; + } + + rv = SanityCheck(storeFile); + if (NS_FAILED(rv)) { + NS_WARNING("Safebrowsing file failed sanity check. probably out of date."); + Reset(); + return rv; + } + + rv = ReadChunkNumbers(); + if (NS_FAILED(rv)) { + Reset(); + return rv; + } + + return NS_OK; +} + +void +HashStore::Clear() +{ + mAddChunks.Clear(); + mSubChunks.Clear(); + mAddExpirations.Clear(); + mSubExpirations.Clear(); + mAddPrefixes.Clear(); + mSubPrefixes.Clear(); + mAddCompletes.Clear(); + mSubCompletes.Clear(); +} + +nsresult +HashStore::ReadEntireStore() +{ + Clear(); + + nsresult rv = ReadHeader(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ReadChunkNumbers(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ReadHashes(); + if (NS_FAILED(rv)) { + // we are the only one reading this so it's up to us to detect corruption + Reset(); + } + + return rv; +} + +nsresult +HashStore::ReadHeader() +{ + if (!mInputStream) { + Clear(); + UpdateHeader(); + return NS_OK; + } + + nsCOMPtr seekable = do_QueryInterface(mInputStream); + nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); + NS_ENSURE_SUCCESS(rv, rv); + + void *buffer = &mHeader; + rv = NS_ReadInputStreamToBuffer(mInputStream, + &buffer, + sizeof(Header)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +HashStore::SanityCheck(nsIFile *storeFile) +{ + if (mHeader.magic != STORE_MAGIC || mHeader.version != CURRENT_VERSION) { + NS_WARNING("Unexpected header data in the store."); + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +HashStore::CalculateChecksum(nsCAutoString& aChecksum, bool aChecksumPresent) +{ + aChecksum.Truncate(); + + nsCOMPtr storeFile; + nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore")); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr hashStream; + + rv = NS_NewLocalFileInputStream(getter_AddRefs(hashStream), storeFile, + PR_RDONLY); + + if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) { + Reset(); + return rv; + } + + PRInt64 fileSize; + rv = storeFile->GetFileSize(&fileSize); + NS_ENSURE_SUCCESS(rv, rv); + + if (fileSize < 0) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr hash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // Size of MD5 hash in bytes + const uint32 CHECKSUM_SIZE = 16; + + rv = hash->Init(nsICryptoHash::MD5); + NS_ENSURE_SUCCESS(rv, rv); + + if (!aChecksumPresent) { + // Hash entire file + rv = hash->UpdateFromStream(hashStream, PR_UINT32_MAX); + } else { + // Hash everything but last checksum bytes + rv = hash->UpdateFromStream(hashStream, fileSize-CHECKSUM_SIZE); + } + NS_ENSURE_SUCCESS(rv, rv); + + rv = hash->Finish(PR_FALSE, aChecksum); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +void +HashStore::UpdateHeader() +{ + mHeader.magic = STORE_MAGIC; + mHeader.version = CURRENT_VERSION; + + mHeader.numAddChunks = mAddChunks.Length(); + mHeader.numSubChunks = mSubChunks.Length(); + mHeader.numAddPrefixes = mAddPrefixes.Length(); + mHeader.numSubPrefixes = mSubPrefixes.Length(); + mHeader.numAddCompletes = mAddCompletes.Length(); + mHeader.numSubCompletes = mSubCompletes.Length(); +} + +nsresult +HashStore::ReadChunkNumbers() +{ + if (!mInputStream) { + LOG(("Clearing.")); + Clear(); + return NS_OK; + } + + nsCOMPtr seekable = do_QueryInterface(mInputStream); + nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, + sizeof(Header)); + + rv = mAddChunks.Read(mInputStream, mHeader.numAddChunks); + NS_ENSURE_SUCCESS(rv, rv); + NS_ASSERTION(mAddChunks.Length() == mHeader.numAddChunks, "Read the right amount of add chunks."); + + rv = mSubChunks.Read(mInputStream, mHeader.numSubChunks); + NS_ENSURE_SUCCESS(rv, rv); + NS_ASSERTION(mSubChunks.Length() == mHeader.numSubChunks, "Read the right amount of sub chunks."); + + return NS_OK; +} + +nsresult +HashStore::ReadHashes() +{ + if (!mInputStream) { + return NS_OK; + } + + nsCOMPtr seekable = do_QueryInterface(mInputStream); + + uint32 offset = sizeof(Header); + offset += (mHeader.numAddChunks + mHeader.numSubChunks) * sizeof(uint32); + nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset); + + rv = ReadAddPrefixes(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ReadSubPrefixes(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ReadTArray(mInputStream, &mAddCompletes, mHeader.numAddCompletes); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ReadTArray(mInputStream, &mSubCompletes, mHeader.numSubCompletes); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +HashStore::BeginUpdate() +{ + mInUpdate = true; + + nsresult rv = ReadEntireStore(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +template +static nsresult +Merge(ChunkSet* aStoreChunks, + nsTArray* aStorePrefixes, + ChunkSet& aUpdateChunks, + nsTArray& aUpdatePrefixes) +{ + EntrySort(aUpdatePrefixes); + + T* updateIter = aUpdatePrefixes.Elements(); + T* updateEnd = aUpdatePrefixes.Elements() + aUpdatePrefixes.Length(); + + T* storeIter = aStorePrefixes->Elements(); + T* storeEnd = aStorePrefixes->Elements() + aStorePrefixes->Length(); + + // use a separate array so we can keep the iterators valid + // if the nsTArray grows + nsTArray adds; + + for (; updateIter != updateEnd; updateIter++) { + // XXX: binary search for insertion point might be faster in common + // case? + while (storeIter < storeEnd && (storeIter->Compare(*updateIter) < 0)) { + // skip forward to matching element (or not...) + storeIter++; + } + // no match, add + if (storeIter == storeEnd + || storeIter->Compare(*updateIter) != 0) { + if (!adds.AppendElement(*updateIter)) + return NS_ERROR_OUT_OF_MEMORY; + } + } + + // chunks can be empty, but we should still report we have them + // to make the chunkranges continuous + aStoreChunks->Merge(aUpdateChunks); + + aStorePrefixes->AppendElements(adds); + EntrySort(*aStorePrefixes); + + return NS_OK; +} + +nsresult +HashStore::ApplyUpdate(TableUpdate &update) +{ + nsresult rv = mAddExpirations.Merge(update.AddExpirations()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mSubExpirations.Merge(update.SubExpirations()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = Expire(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = Merge(&mAddChunks, &mAddPrefixes, + update.AddChunks(), update.AddPrefixes()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = Merge(&mAddChunks, &mAddCompletes, + update.AddChunks(), update.AddCompletes()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = Merge(&mSubChunks, &mSubPrefixes, + update.SubChunks(), update.SubPrefixes()); + NS_ENSURE_SUCCESS(rv, rv); + + rv = Merge(&mSubChunks, &mSubCompletes, + update.SubChunks(), update.SubCompletes()); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +HashStore::Rebuild() +{ + NS_ASSERTION(mInUpdate, "Must be in update to rebuild."); + + nsresult rv = ProcessSubs(); + NS_ENSURE_SUCCESS(rv, rv); + + UpdateHeader(); + + return NS_OK; +} + +template +static void +ExpireEntries(nsTArray* aEntries, ChunkSet& aExpirations) +{ + T* addIter = aEntries->Elements(); + T* end = aEntries->Elements() + aEntries->Length(); + + for (T *iter = addIter; iter != end; iter++) { + if (!aExpirations.Has(iter->Chunk())) { + *addIter = *iter; + addIter++; + } + } + + aEntries->SetLength(addIter - aEntries->Elements()); +} + +nsresult +HashStore::Expire() +{ + ExpireEntries(&mAddPrefixes, mAddExpirations); + ExpireEntries(&mAddCompletes, mAddExpirations); + ExpireEntries(&mSubPrefixes, mSubExpirations); + ExpireEntries(&mSubCompletes, mSubExpirations); + + mAddChunks.Remove(mAddExpirations); + mSubChunks.Remove(mSubExpirations); + + mAddExpirations.Clear(); + mSubExpirations.Clear(); + + return NS_OK; +} + +template +nsresult DeflateWriteTArray(nsIOutputStream* aStream, nsTArray& aIn) +{ + uLongf insize = aIn.Length() * sizeof(T); + uLongf outsize = compressBound(insize); + nsTArray outBuff; + outBuff.SetLength(outsize); + + int zerr = compress(reinterpret_cast(outBuff.Elements()), + &outsize, + reinterpret_cast(aIn.Elements()), + insize); + if (zerr != Z_OK) { + return NS_ERROR_FAILURE; + } + LOG(("DeflateWriteTArray: %d in %d out", insize, outsize)); + + outBuff.TruncateLength(outsize); + + // Length of compressed data stream + PRUint32 dataLen = outBuff.Length(); + PRUint32 written; + nsresult rv = aStream->Write(reinterpret_cast(&dataLen), sizeof(dataLen), &written); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(written == sizeof(dataLen), "Error writing deflate length"); + + // Store to stream + rv = WriteTArray(aStream, outBuff); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +template +nsresult InflateReadTArray(nsIInputStream* aStream, nsTArray* aOut, + PRUint32 aExpectedSize) +{ + + PRUint32 inLen; + PRUint32 read; + nsresult rv = aStream->Read(reinterpret_cast(&inLen), sizeof(inLen), &read); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(read == sizeof(inLen), "Error reading inflate length"); + + nsTArray inBuff; + inBuff.SetLength(inLen); + + rv = ReadTArray(aStream, &inBuff, inLen); + NS_ENSURE_SUCCESS(rv, rv); + + uLongf insize = inLen; + uLongf outsize = aExpectedSize * sizeof(T); + aOut->SetLength(aExpectedSize); + + int zerr = uncompress(reinterpret_cast(aOut->Elements()), + &outsize, + reinterpret_cast(inBuff.Elements()), + insize); + if (zerr != Z_OK) { + return NS_ERROR_FAILURE; + } + LOG(("InflateReadTArray: %d in %d out", insize, outsize)); + + NS_ASSERTION(outsize == aExpectedSize * sizeof(T), "Decompression size mismatch"); + + return NS_OK; +} + +nsresult +HashStore::ReadAddPrefixes() +{ + nsTArray chunks; + PRUint32 count = mHeader.numAddPrefixes; + + nsresult rv = InflateReadTArray(mInputStream, &chunks, count); + NS_ENSURE_SUCCESS(rv, rv); + + mAddPrefixes.SetCapacity(count); + for (uint32 i = 0; i < count; i++) { + AddPrefix *add = mAddPrefixes.AppendElement(); + add->prefix.FromUint32(0); + add->addChunk = chunks[i]; + } + + return NS_OK; +} + +nsresult +HashStore::ReadSubPrefixes() +{ + nsTArray addchunks; + nsTArray subchunks; + nsTArray prefixes; + PRUint32 count = mHeader.numSubPrefixes; + + nsresult rv = InflateReadTArray(mInputStream, &addchunks, count); + NS_ENSURE_SUCCESS(rv, rv); + + rv = InflateReadTArray(mInputStream, &subchunks, count); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ReadTArray(mInputStream, &prefixes, count); + NS_ENSURE_SUCCESS(rv, rv); + + mSubPrefixes.SetCapacity(count); + for (uint32 i = 0; i < count; i++) { + SubPrefix *sub = mSubPrefixes.AppendElement(); + sub->addChunk = addchunks[i]; + sub->prefix = prefixes[i]; + sub->subChunk = subchunks[i]; + } + + return NS_OK; +} + +// Split up PrefixArray back into the constituents +nsresult +HashStore::WriteAddPrefixes(nsIOutputStream* aOut) +{ + nsTArray chunks; + PRUint32 count = mAddPrefixes.Length(); + chunks.SetCapacity(count); + + for (uint32 i = 0; i < count; i++) { + chunks.AppendElement(mAddPrefixes[i].Chunk()); + } + + nsresult rv = DeflateWriteTArray(aOut, chunks); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +HashStore::WriteSubPrefixes(nsIOutputStream* aOut) +{ + nsTArray addchunks; + nsTArray subchunks; + nsTArray prefixes; + PRUint32 count = mSubPrefixes.Length(); + addchunks.SetCapacity(count); + subchunks.SetCapacity(count); + prefixes.SetCapacity(count); + + for (uint32 i = 0; i < count; i++) { + addchunks.AppendElement(mSubPrefixes[i].AddChunk()); + prefixes.AppendElement(mSubPrefixes[i].PrefixHash()); + subchunks.AppendElement(mSubPrefixes[i].Chunk()); + } + + nsresult rv = DeflateWriteTArray(aOut, addchunks); + NS_ENSURE_SUCCESS(rv, rv); + + rv = DeflateWriteTArray(aOut, subchunks); + NS_ENSURE_SUCCESS(rv, rv); + + // chunk-ordered prefixes are not compressible + rv = WriteTArray(aOut, prefixes); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +HashStore::WriteFile() +{ + nsCOMPtr storeFile; + nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore")); + NS_ENSURE_SUCCESS(rv, rv); + + // Need to close the inputstream here *before* rewriting its file. + // Windows will fail with an access violation if we don't. + if (mInputStream) { + rv = mInputStream->Close(); + NS_ENSURE_SUCCESS(rv, rv); + } + + nsCOMPtr out; + rv = NS_NewCheckSummedOutputStream(getter_AddRefs(out), storeFile, + PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 written; + rv = out->Write(reinterpret_cast(&mHeader), sizeof(mHeader), &written); + NS_ENSURE_SUCCESS(rv, rv); + + // Write chunk numbers... + rv = mAddChunks.Write(out); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mSubChunks.Write(out); + NS_ENSURE_SUCCESS(rv, rv); + + // Write hashes.. + rv = WriteAddPrefixes(out); + NS_ENSURE_SUCCESS(rv, rv); + + rv = WriteSubPrefixes(out); + NS_ENSURE_SUCCESS(rv, rv); + + rv = WriteTArray(out, mAddCompletes); + NS_ENSURE_SUCCESS(rv, rv); + + rv = WriteTArray(out, mSubCompletes); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr safeOut = do_QueryInterface(out, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = safeOut->Finish(); + NS_ENSURE_SUCCESS(rv, rv); + + // Reopen the file now that we've rewritten it. + nsCOMPtr origStream; + rv = NS_NewLocalFileInputStream(getter_AddRefs(origStream), storeFile, + PR_RDONLY); + NS_ENSURE_SUCCESS(rv, rv); + + rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream), origStream, + BUFFER_SIZE); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +HashStore::FinishUpdate() +{ + // Drop add/sub data, it's only used during updates. + mAddPrefixes.Clear(); + mSubPrefixes.Clear(); + mAddCompletes.Clear(); + mSubCompletes.Clear(); + + return NS_OK; +} + +template +static void +Erase(nsTArray* array, T* iterStart, T* iterEnd) +{ + uint32 start = iterStart - array->Elements(); + uint32 count = iterEnd - iterStart; + + if (count > 0) { + array->RemoveElementsAt(start, count); + } +} + +// Find items matching between |subs| and |adds|, and remove them, +// recording the item from |adds| in |adds_removed|. To minimize +// copies, the inputs are processing in parallel, so |subs| and |adds| +// should be compatibly ordered (either by SBAddPrefixLess or +// SBAddPrefixHashLess). +// +// |predAS| provides add < sub, |predSA| provides sub < add, for the +// tightest compare appropriate (see calls in SBProcessSubs). +template +static void +KnockoutSubs(nsTArray* aSubs, nsTArray* aAdds) +{ + // Keep a pair of output iterators for writing kept items. Due to + // deletions, these may lag the main iterators. Using erase() on + // individual items would result in O(N^2) copies. Using a list + // would work around that, at double or triple the memory cost. + TAdd* addOut = aAdds->Elements(); + TAdd* addIter = aAdds->Elements(); + + TSub* subOut = aSubs->Elements(); + TSub* subIter = aSubs->Elements(); + + TAdd* addEnd = addIter + aAdds->Length(); + TSub* subEnd = subIter + aSubs->Length(); + + while (addIter != addEnd && subIter != subEnd) { + // additer compare, so it compares on add chunk + int32 cmp = addIter->Compare(*subIter); + if (cmp > 0) { + // If |*sub_iter| < |*add_iter|, retain the sub. + *subOut = *subIter; + ++subOut; + ++subIter; + } else if (cmp < 0) { + // If |*add_iter| < |*sub_iter|, retain the add. + *addOut = *addIter; + ++addOut; + ++addIter; + } else { + // Drop equal items + ++addIter; + ++subIter; + } + } + + Erase(aAdds, addOut, addIter); + Erase(aSubs, subOut, subIter); +} + +// Remove items in |removes| from |fullHashes|. |fullHashes| and +// |removes| should be ordered by SBAddPrefix component. +template +static void +RemoveMatchingPrefixes(const SubPrefixArray& aSubs, nsTArray* aFullHashes) +{ + // Where to store kept items. + T* out = aFullHashes->Elements(); + T* hashIter = out; + T* hashEnd = aFullHashes->Elements() + aFullHashes->Length(); + + SubPrefix const * removeIter = aSubs.Elements(); + SubPrefix const * removeEnd = aSubs.Elements() + aSubs.Length(); + + while (hashIter != hashEnd && removeIter != removeEnd) { + int32 cmp = removeIter->CompareAlt(*hashIter); + if (cmp > 0) { + // Keep items less than |*removeIter|. + *out = *hashIter; + ++out; + ++hashIter; + } else if (cmp < 0) { + // No hit for |*removeIter|, bump it forward. + ++removeIter; + } else { + // Drop equal items, there may be multiple hits. + do { + ++hashIter; + } while (hashIter != hashEnd && + !(removeIter->CompareAlt(*hashIter) < 0)); + ++removeIter; + } + } + Erase(aFullHashes, out, hashIter); +} + +nsresult +HashStore::ProcessSubs() +{ + EntrySort(mAddPrefixes); + EntrySort(mSubPrefixes); + EntrySort(mAddCompletes); + EntrySort(mSubCompletes); + + KnockoutSubs(&mSubPrefixes, &mAddPrefixes); + + RemoveMatchingPrefixes(mSubPrefixes, &mAddCompletes); + RemoveMatchingPrefixes(mSubPrefixes, &mSubCompletes); + + KnockoutSubs(&mSubCompletes, &mAddCompletes); + + // Clean up temporary subs used for knocking out completes + ChunkSet dummyChunks; + dummyChunks.Set(0); + ExpireEntries(&mSubPrefixes, dummyChunks); + ExpireEntries(&mSubCompletes, dummyChunks); + mSubChunks.Remove(dummyChunks); + + return NS_OK; +} + +nsresult +HashStore::AugmentAdds(const nsTArray& aPrefixes) +{ + uint32 cnt = aPrefixes.Length(); + if (cnt != mAddPrefixes.Length()) { + LOG(("Amount of prefixes in cache not consistent with store (%d vs %d)", + aPrefixes.Length(), mAddPrefixes.Length())); + return NS_ERROR_FAILURE; + } + for (uint32 i = 0; i < cnt; i++) { + mAddPrefixes[i].prefix.FromUint32(aPrefixes[i]); + } + return NS_OK; +} + +} +} diff --git a/toolkit/components/url-classifier/HashStore.h b/toolkit/components/url-classifier/HashStore.h new file mode 100644 index 000000000000..bf76984eea0b --- /dev/null +++ b/toolkit/components/url-classifier/HashStore.h @@ -0,0 +1,213 @@ +/* ***** 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 Url Classifier code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * Gian-Carlo Pascutto + * + * 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 HashStore_h__ +#define HashStore_h__ + +#include "Entries.h" +#include "ChunkSet.h" + +#include "nsString.h" +#include "nsTArray.h" +#include "nsIFile.h" +#include "nsIFileStreams.h" +#include "nsCOMPtr.h" + +namespace mozilla { +namespace safebrowsing { + +class TableUpdate { +public: + TableUpdate(const nsACString& aTable) + : mTable(aTable), mLocalUpdate(false) {} + const nsCString& TableName() const { return mTable; } + + bool Empty() const { + return mAddChunks.Length() == 0 && + mSubChunks.Length() == 0 && + mAddExpirations.Length() == 0 && + mSubExpirations.Length() == 0 && + mAddPrefixes.Length() == 0 && + mSubPrefixes.Length() == 0 && + mAddCompletes.Length() == 0 && + mSubCompletes.Length() == 0; + } + + void NewAddChunk(PRUint32 aChunk) { mAddChunks.Set(aChunk); } + void NewSubChunk(PRUint32 aChunk) { mSubChunks.Set(aChunk); } + + void NewAddExpiration(PRUint32 aChunk) { mAddExpirations.Set(aChunk); } + void NewSubExpiration(PRUint32 aChunk) { mSubExpirations.Set(aChunk); } + + void NewAddPrefix(PRUint32 aAddChunk, const Prefix& aPrefix); + void NewSubPrefix(PRUint32 aAddChunk, const Prefix& aPprefix, PRUint32 aSubChunk); + void NewAddComplete(PRUint32 aChunk, const Completion& aCompletion); + void NewSubComplete(PRUint32 aAddChunk, const Completion& aCompletion, + PRUint32 aSubChunk); + void SetLocalUpdate(void) { mLocalUpdate = true; }; + bool IsLocalUpdate(void) { return mLocalUpdate; }; + + ChunkSet& AddChunks() { return mAddChunks; } + ChunkSet& SubChunks() { return mSubChunks; } + + ChunkSet& AddExpirations() { return mAddExpirations; } + ChunkSet& SubExpirations() { return mSubExpirations; } + + AddPrefixArray& AddPrefixes() { return mAddPrefixes; } + SubPrefixArray& SubPrefixes() { return mSubPrefixes; } + AddCompleteArray& AddCompletes() { return mAddCompletes; } + SubCompleteArray& SubCompletes() { return mSubCompletes; } + +private: + nsCString mTable; + // Update not from the remote server (no freshness) + bool mLocalUpdate; + + ChunkSet mAddChunks; + ChunkSet mSubChunks; + ChunkSet mAddExpirations; + ChunkSet mSubExpirations; + AddPrefixArray mAddPrefixes; + SubPrefixArray mSubPrefixes; + AddCompleteArray mAddCompletes; + SubCompleteArray mSubCompletes; +}; + +class HashStore { +public: + HashStore(const nsACString& aTableName, nsIFile* aStoreFile); + ~HashStore(); + + const nsCString& TableName() const { return mTableName; }; + + nsresult Open(); + nsresult AugmentAdds(const nsTArray& aPrefixes); + + ChunkSet& AddChunks() { return mAddChunks; } + ChunkSet& SubChunks() { return mSubChunks; } + const AddPrefixArray& AddPrefixes() const { return mAddPrefixes; } + const AddCompleteArray& AddCompletes() const { return mAddCompletes; } + const SubPrefixArray& SubPrefixes() const { return mSubPrefixes; } + const SubCompleteArray& SubCompletes() const { return mSubCompletes; } + + // ======= + // Updates + // ======= + // Begin the update process. Reads the store into memory. + nsresult BeginUpdate(); + + // Imports the data from a TableUpdate. + nsresult ApplyUpdate(TableUpdate &aUpdate); + + // Process expired chunks + nsresult Expire(); + + // Rebuild the store, Incorporating all the applied updates. + nsresult Rebuild(); + + // Write the current state of the store to disk. + // If you call between ApplyUpdate() and Rebuild(), you'll + // have a mess on your hands. + nsresult WriteFile(); + + // Drop memory used during the update process. + nsresult FinishUpdate(); + + // Force the entire store in memory + nsresult ReadEntireStore(); + +private: + static const int BUFFER_SIZE = 6 * 1024 * 1024; + + void Clear(); + nsresult Reset(); + + nsresult ReadHeader(); + nsresult SanityCheck(nsIFile* aStoreFile); + nsresult CalculateChecksum(nsCAutoString& aChecksum, bool aChecksumPresent); + nsresult CheckChecksum(nsIFile* aStoreFile); + void UpdateHeader(); + + nsresult EnsureChunkNumbers(); + nsresult ReadChunkNumbers(); + nsresult ReadHashes(); + nsresult ReadAddPrefixes(); + nsresult ReadSubPrefixes(); + + nsresult WriteAddPrefixes(nsIOutputStream* aOut); + nsresult WriteSubPrefixes(nsIOutputStream* aOut); + + nsresult ProcessSubs(); + + struct Header { + uint32 magic; + uint32 version; + uint32 numAddChunks; + uint32 numSubChunks; + uint32 numAddPrefixes; + uint32 numSubPrefixes; + uint32 numAddCompletes; + uint32 numSubCompletes; + }; + + Header mHeader; + + nsCString mTableName; + nsCOMPtr mStoreDirectory; + + bool mInUpdate; + + nsCOMPtr mInputStream; + + bool haveChunks; + ChunkSet mAddChunks; + ChunkSet mSubChunks; + + ChunkSet mAddExpirations; + ChunkSet mSubExpirations; + + AddPrefixArray mAddPrefixes; + AddCompleteArray mAddCompletes; + SubPrefixArray mSubPrefixes; + SubCompleteArray mSubCompletes; +}; + +} +} + +#endif diff --git a/toolkit/components/url-classifier/LookupCache.cpp b/toolkit/components/url-classifier/LookupCache.cpp new file mode 100644 index 000000000000..22ad00281922 --- /dev/null +++ b/toolkit/components/url-classifier/LookupCache.cpp @@ -0,0 +1,776 @@ +//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * Gian-Carlo Pascutto + * + * 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 "LookupCache.h" +#include "HashStore.h" +#include "nsISeekableStream.h" +#include "mozilla/Telemetry.h" +#include "prlog.h" +#include "prprf.h" + +// We act as the main entry point for all the real lookups, +// so note that those are not done to the actual HashStore. +// The latter solely exists to store the data needed to handle +// the updates from the protocol. + +// This module has its own store, which stores the Completions, +// mostly caching lookups that have happened over the net. +// The prefixes are cached/checked by looking them up in the +// PrefixSet. + +// Data format for the ".cache" files: +// uint32 magic Identify the file type +// uint32 version Version identifier for file format +// uint32 numCompletions Amount of completions stored +// 0...numCompletions 256-bit Completions + +// Name of the lookupcomplete cache +#define CACHE_SUFFIX ".cache" + +// Name of the persistent PrefixSet storage +#define PREFIXSET_SUFFIX ".pset" + +// NSPR_LOG_MODULES=UrlClassifierDbService:5 +extern PRLogModuleInfo *gUrlClassifierDbServiceLog; +#if defined(PR_LOGGING) +#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) +#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) +#else +#define LOG(args) +#define LOG_ENABLED() (false) +#endif + +namespace mozilla { +namespace safebrowsing { + +const uint32 LOOKUPCACHE_MAGIC = 0x1231af3e; +const uint32 CURRENT_VERSION = 1; + +LookupCache::LookupCache(const nsACString& aTableName, nsIFile* aStoreDir) + : mPrimed(false) + , mTableName(aTableName) + , mStoreDirectory(aStoreDir) +{ +} + +nsresult +LookupCache::Init() +{ + mPrefixSet = new nsUrlClassifierPrefixSet(); + nsresult rv = mPrefixSet->Init(mTableName); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +LookupCache::~LookupCache() +{ +} + +nsresult +LookupCache::Open() +{ + nsCOMPtr storeFile; + + nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = NS_NewLocalFileInputStream(getter_AddRefs(mInputStream), storeFile, + PR_RDONLY); + + if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) { + Reset(); + return rv; + } + + if (rv == NS_ERROR_FILE_NOT_FOUND) { + Clear(); + UpdateHeader(); + return NS_OK; + } + + rv = ReadHeader(); + NS_ENSURE_SUCCESS(rv, rv); + + LOG(("ReadCompletions")); + rv = ReadCompletions(); + NS_ENSURE_SUCCESS(rv, rv); + + LOG(("Loading PrefixSet")); + rv = LoadPrefixSet(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +LookupCache::Reset() +{ + LOG(("LookupCache resetting")); + + nsCOMPtr storeFile; + nsCOMPtr prefixsetFile; + nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = mStoreDirectory->Clone(getter_AddRefs(prefixsetFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX)); + NS_ENSURE_SUCCESS(rv, rv); + rv = prefixsetFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = storeFile->Remove(false); + NS_ENSURE_SUCCESS(rv, rv); + rv = prefixsetFile->Remove(false); + NS_ENSURE_SUCCESS(rv, rv); + + Clear(); + + return NS_OK; +} + + +nsresult +LookupCache::Build(const AddPrefixArray& aAddPrefixes, + const AddCompleteArray& aAddCompletes) +{ + mCompletions.Clear(); + mCompletions.SetCapacity(aAddCompletes.Length()); + for (uint32 i = 0; i < aAddCompletes.Length(); i++) { + mCompletions.AppendElement(aAddCompletes[i].CompleteHash()); + } + mCompletions.Sort(); + + Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_COMPLETIONS, + static_cast(mCompletions.Length())); + + nsresult rv = ConstructPrefixSet(aAddPrefixes); + NS_ENSURE_SUCCESS(rv, rv); + mPrimed = true; + + Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_PREFIXES, + static_cast(aAddPrefixes.Length())); + + return NS_OK; +} + +#if defined(DEBUG) && defined(PR_LOGGING) +void +LookupCache::Dump() +{ + if (!LOG_ENABLED()) + return; + + for (uint32 i = 0; i < mCompletions.Length(); i++) { + nsCAutoString str; + mCompletions[i].ToString(str); + LOG(("Completion: %s", str.get())); + } +} +#endif + +nsresult +LookupCache::Has(const Completion& aCompletion, + const Completion& aHostkey, + const PRUint32 aHashKey, + bool* aHas, bool* aComplete, + Prefix* aOrigPrefix) +{ + *aHas = *aComplete = false; + + // check completion store first + if (mCompletions.BinaryIndexOf(aCompletion) != nsTArray::NoIndex) { + LOG(("Complete in %s", mTableName.get())); + *aComplete = true; + *aHas = true; + return NS_OK; + } + + PRUint32 prefix = aCompletion.ToUint32(); + PRUint32 hostkey = aHostkey.ToUint32(); + PRUint32 codedkey; + nsresult rv = KeyedHash(prefix, hostkey, aHashKey, &codedkey); + NS_ENSURE_SUCCESS(rv, rv); + + Prefix codedPrefix; + codedPrefix.FromUint32(codedkey); + *aOrigPrefix = codedPrefix; + + bool ready = true; + bool found; + rv = mPrefixSet->Probe(codedkey, &ready, &found); + NS_ENSURE_SUCCESS(rv, rv); + + LOG(("Probe in %s: %X, ready: %d found %d", mTableName.get(), prefix, ready, found)); + + if (found) { + *aHas = true; + } + + return NS_OK; +} + +nsresult +LookupCache::WriteFile() +{ + nsCOMPtr storeFile; + nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX)); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr out; + rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out), storeFile, + PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE); + NS_ENSURE_SUCCESS(rv, rv); + + UpdateHeader(); + LOG(("Writing %d completions", mHeader.numCompletions)); + + PRUint32 written; + rv = out->Write(reinterpret_cast(&mHeader), sizeof(mHeader), &written); + NS_ENSURE_SUCCESS(rv, rv); + + rv = WriteTArray(out, mCompletions); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr safeOut = do_QueryInterface(out); + rv = safeOut->Finish(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = EnsureSizeConsistent(); + NS_ENSURE_SUCCESS(rv, rv); + + // Reopen the file now that we've rewritten it. + rv = NS_NewLocalFileInputStream(getter_AddRefs(mInputStream), storeFile, + PR_RDONLY); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr psFile; + rv = mStoreDirectory->Clone(getter_AddRefs(psFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mPrefixSet->StoreToFile(psFile); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store the prefixset"); + + return NS_OK; +} + +void +LookupCache::Clear() +{ + mCompletions.Clear(); + mPrefixSet->SetPrefixes(nsnull, 0); + mPrimed = false; +} + +void +LookupCache::UpdateHeader() +{ + mHeader.magic = LOOKUPCACHE_MAGIC; + mHeader.version = CURRENT_VERSION; + mHeader.numCompletions = mCompletions.Length(); +} + +nsresult +LookupCache::EnsureSizeConsistent() +{ + nsCOMPtr storeFile; + nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); + NS_ENSURE_SUCCESS(rv, rv); + rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX)); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt64 fileSize; + rv = storeFile->GetFileSize(&fileSize); + NS_ENSURE_SUCCESS(rv, rv); + + if (fileSize < 0) { + return NS_ERROR_FAILURE; + } + + PRInt64 expectedSize = sizeof(mHeader) + + mHeader.numCompletions*sizeof(Completion); + if (expectedSize != fileSize) { + NS_WARNING("File length does not match. Probably corrupted."); + Reset(); + return NS_ERROR_FILE_CORRUPTED; + } + + return NS_OK; +} + +nsresult +LookupCache::ReadHeader() +{ + if (!mInputStream) { + Clear(); + UpdateHeader(); + return NS_OK; + } + + nsCOMPtr seekable = do_QueryInterface(mInputStream); + nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); + NS_ENSURE_SUCCESS(rv, rv); + + void *buffer = &mHeader; + rv = NS_ReadInputStreamToBuffer(mInputStream, + &buffer, + sizeof(Header)); + NS_ENSURE_SUCCESS(rv, rv); + + if (mHeader.magic != LOOKUPCACHE_MAGIC || mHeader.version != CURRENT_VERSION) { + NS_WARNING("Unexpected header data in the store."); + Reset(); + return NS_ERROR_FILE_CORRUPTED; + } + LOG(("%d completions present", mHeader.numCompletions)); + + rv = EnsureSizeConsistent(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +LookupCache::ReadCompletions() +{ + if (!mHeader.numCompletions) { + mCompletions.Clear(); + return NS_OK; + } + + nsCOMPtr seekable = do_QueryInterface(mInputStream); + nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, sizeof(Header)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = ReadTArray(mInputStream, &mCompletions, mHeader.numCompletions); + NS_ENSURE_SUCCESS(rv, rv); + + LOG(("Read %d completions", mCompletions.Length())); + + return NS_OK; +} + +/* static */ bool +LookupCache::IsCanonicalizedIP(const nsACString& aHost) +{ + // The canonicalization process will have left IP addresses in dotted + // decimal with no surprises. + PRUint32 i1, i2, i3, i4; + char c; + if (PR_sscanf(PromiseFlatCString(aHost).get(), "%u.%u.%u.%u%c", + &i1, &i2, &i3, &i4, &c) == 4) { + return (i1 <= 0xFF && i2 <= 0xFF && i3 <= 0xFF && i4 <= 0xFF); + } + + return false; +} + +/* static */ nsresult +LookupCache::GetKey(const nsACString& aSpec, + Completion* aHash, + nsCOMPtr& aCryptoHash) +{ + nsACString::const_iterator begin, end, iter; + aSpec.BeginReading(begin); + aSpec.EndReading(end); + + iter = begin; + if (!FindCharInReadable('/', iter, end)) { + return NS_OK; + } + + const nsCSubstring& host = Substring(begin, iter); + + if (IsCanonicalizedIP(host)) { + nsCAutoString key; + key.Assign(host); + key.Append("/"); + return aHash->FromPlaintext(key, aCryptoHash); + } + + nsTArray hostComponents; + ParseString(PromiseFlatCString(host), '.', hostComponents); + + if (hostComponents.Length() < 2) + return NS_ERROR_FAILURE; + + PRInt32 last = PRInt32(hostComponents.Length()) - 1; + nsCAutoString lookupHost; + + if (hostComponents.Length() > 2) { + lookupHost.Append(hostComponents[last - 2]); + lookupHost.Append("."); + } + + lookupHost.Append(hostComponents[last - 1]); + lookupHost.Append("."); + lookupHost.Append(hostComponents[last]); + lookupHost.Append("/"); + + return aHash->FromPlaintext(lookupHost, aCryptoHash); +} + +/* static */ nsresult +LookupCache::GetLookupFragments(const nsACString& aSpec, + nsTArray* aFragments) + +{ + aFragments->Clear(); + + nsACString::const_iterator begin, end, iter; + aSpec.BeginReading(begin); + aSpec.EndReading(end); + + iter = begin; + if (!FindCharInReadable('/', iter, end)) { + return NS_OK; + } + + const nsCSubstring& host = Substring(begin, iter++); + nsCAutoString path; + path.Assign(Substring(iter, end)); + + /** + * From the protocol doc: + * For the hostname, the client will try at most 5 different strings. They + * are: + * a) The exact hostname of the url + * b) The 4 hostnames formed by starting with the last 5 components and + * successivly removing the leading component. The top-level component + * can be skipped. This is not done if the hostname is a numerical IP. + */ + nsTArray hosts; + hosts.AppendElement(host); + + if (!IsCanonicalizedIP(host)) { + host.BeginReading(begin); + host.EndReading(end); + int numHostComponents = 0; + while (RFindInReadable(NS_LITERAL_CSTRING("."), begin, end) && + numHostComponents < MAX_HOST_COMPONENTS) { + // don't bother checking toplevel domains + if (++numHostComponents >= 2) { + host.EndReading(iter); + hosts.AppendElement(Substring(end, iter)); + } + end = begin; + host.BeginReading(begin); + } + } + + /** + * From the protocol doc: + * For the path, the client will also try at most 6 different strings. + * They are: + * a) the exact path of the url, including query parameters + * b) the exact path of the url, without query parameters + * c) the 4 paths formed by starting at the root (/) and + * successively appending path components, including a trailing + * slash. This behavior should only extend up to the next-to-last + * path component, that is, a trailing slash should never be + * appended that was not present in the original url. + */ + nsTArray paths; + nsCAutoString pathToAdd; + + path.BeginReading(begin); + path.EndReading(end); + iter = begin; + if (FindCharInReadable('?', iter, end)) { + pathToAdd = Substring(begin, iter); + paths.AppendElement(pathToAdd); + end = iter; + } + + int numPathComponents = 1; + iter = begin; + while (FindCharInReadable('/', iter, end) && + numPathComponents < MAX_PATH_COMPONENTS) { + iter++; + pathToAdd.Assign(Substring(begin, iter)); + paths.AppendElement(pathToAdd); + numPathComponents++; + } + + // If we haven't already done so, add the full path + if (!pathToAdd.Equals(path)) { + paths.AppendElement(path); + } + // Check an empty path (for whole-domain blacklist entries) + paths.AppendElement(EmptyCString()); + + for (PRUint32 hostIndex = 0; hostIndex < hosts.Length(); hostIndex++) { + for (PRUint32 pathIndex = 0; pathIndex < paths.Length(); pathIndex++) { + nsCString key; + key.Assign(hosts[hostIndex]); + key.Append('/'); + key.Append(paths[pathIndex]); + LOG(("Chking %s", key.get())); + + aFragments->AppendElement(key); + } + } + + return NS_OK; +} + +/* static */ nsresult +LookupCache::GetHostKeys(const nsACString& aSpec, + nsTArray* aHostKeys) +{ + nsACString::const_iterator begin, end, iter; + aSpec.BeginReading(begin); + aSpec.EndReading(end); + + iter = begin; + if (!FindCharInReadable('/', iter, end)) { + return NS_OK; + } + + const nsCSubstring& host = Substring(begin, iter); + + if (IsCanonicalizedIP(host)) { + nsCString *key = aHostKeys->AppendElement(); + if (!key) + return NS_ERROR_OUT_OF_MEMORY; + + key->Assign(host); + key->Append("/"); + return NS_OK; + } + + nsTArray hostComponents; + ParseString(PromiseFlatCString(host), '.', hostComponents); + + if (hostComponents.Length() < 2) { + // no host or toplevel host, this won't match anything in the db + return NS_OK; + } + + // First check with two domain components + PRInt32 last = PRInt32(hostComponents.Length()) - 1; + nsCString *lookupHost = aHostKeys->AppendElement(); + if (!lookupHost) + return NS_ERROR_OUT_OF_MEMORY; + + lookupHost->Assign(hostComponents[last - 1]); + lookupHost->Append("."); + lookupHost->Append(hostComponents[last]); + lookupHost->Append("/"); + + // Now check with three domain components + if (hostComponents.Length() > 2) { + nsCString *lookupHost2 = aHostKeys->AppendElement(); + if (!lookupHost2) + return NS_ERROR_OUT_OF_MEMORY; + lookupHost2->Assign(hostComponents[last - 2]); + lookupHost2->Append("."); + lookupHost2->Append(*lookupHost); + } + + return NS_OK; +} + +/* We have both a prefix and a domain. Drop the domain, but + hash the domain, the prefix and a random value together, + ensuring any collisions happens at a different points for + different users. +*/ +/* static */ nsresult LookupCache::KeyedHash(PRUint32 aPref, PRUint32 aDomain, + PRUint32 aKey, PRUint32* aOut) +{ + /* This is a reimplementation of MurmurHash3 32-bit + based on the public domain C++ sources. + http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + for nblocks = 2 + */ + PRUint32 c1 = 0xCC9E2D51; + PRUint32 c2 = 0x1B873593; + PRUint32 c3 = 0xE6546B64; + PRUint32 c4 = 0x85EBCA6B; + PRUint32 c5 = 0xC2B2AE35; + PRUint32 h1 = aPref; // seed + PRUint32 k1; + PRUint32 karr[2]; + + karr[0] = aDomain; + karr[1] = aKey; + + for (PRUint32 i = 0; i < 2; i++) { + k1 = karr[i]; + k1 *= c1; + k1 = (k1 << 15) | (k1 >> (32-15)); + k1 *= c2; + + h1 ^= k1; + h1 = (h1 << 13) | (h1 >> (32-13)); + h1 *= 5; + h1 += c3; + } + + h1 ^= 2; // len + // fmix + h1 ^= h1 >> 16; + h1 *= c4; + h1 ^= h1 >> 13; + h1 *= c5; + h1 ^= h1 >> 16; + + *aOut = h1; + + return NS_OK; +} + +bool LookupCache::IsPrimed() +{ + return mPrimed; +} + +nsresult +LookupCache::ConstructPrefixSet(const AddPrefixArray& aAddPrefixes) +{ + Telemetry::AutoTimer timer; + + nsTArray array; + array.SetCapacity(aAddPrefixes.Length()); + + for (uint32 i = 0; i < aAddPrefixes.Length(); i++) { + array.AppendElement(aAddPrefixes[i].PrefixHash().ToUint32()); + } + + // clear old tree + if (array.IsEmpty()) { + // DB is empty, but put a sentinel to show that we looked + array.AppendElement(0); + } + // PrefixSet requires sorted order + array.Sort(); + + // construct new one, replace old entries + nsresult rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length()); + if (NS_FAILED(rv)) { + goto error_bailout; + } + +#ifdef DEBUG + PRUint32 size; + size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of); + LOG(("SB tree done, size = %d bytes\n", size)); +#endif + + mPrimed = true; + + return NS_OK; + + error_bailout: + // load an empty prefixset so the browser can work + nsAutoTArray sentinel; + sentinel.Clear(); + sentinel.AppendElement(0); + mPrefixSet->SetPrefixes(sentinel.Elements(), sentinel.Length()); + if (rv == NS_ERROR_OUT_OF_MEMORY) { + Telemetry::Accumulate(Telemetry::URLCLASSIFIER_PS_OOM, 1); + } + return rv; +} + +nsresult +LookupCache::LoadPrefixSet() +{ + nsCOMPtr psFile; + nsresult rv = mStoreDirectory->Clone(getter_AddRefs(psFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX)); + NS_ENSURE_SUCCESS(rv, rv); + + bool exists; + rv = psFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (exists) { + LOG(("stored PrefixSet exists, loading from disk")); + rv = mPrefixSet->LoadFromFile(psFile); + } + if (!exists || NS_FAILED(rv)) { + LOG(("no (usable) stored PrefixSet found")); + } else { + mPrimed = true; + } + +#ifdef DEBUG + if (mPrimed) { + PRUint32 size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of); + LOG(("SB tree done, size = %d bytes\n", size)); + } +#endif + + return NS_OK; +} + +nsresult +LookupCache::GetPrefixes(nsTArray* aAddPrefixes) +{ + if (!mPrimed) { + // This can happen if its a new table, so no error. + LOG(("GetPrefixes from empty LookupCache")); + return NS_OK; + } + PRUint32 cnt; + PRUint32 *arr; + nsresult rv = mPrefixSet->GetPrefixes(&cnt, &arr); + NS_ENSURE_SUCCESS(rv, rv); + if (!aAddPrefixes->AppendElements(arr, cnt)) + return NS_ERROR_FAILURE; + nsMemory::Free(arr); + return NS_OK; +} + + +} +} diff --git a/toolkit/components/url-classifier/LookupCache.h b/toolkit/components/url-classifier/LookupCache.h new file mode 100644 index 000000000000..5ba07d5fe099 --- /dev/null +++ b/toolkit/components/url-classifier/LookupCache.h @@ -0,0 +1,186 @@ +//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * Gian-Carlo Pascutto + * + * 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 LookupCache_h__ +#define LookupCache_h__ + +#include "Entries.h" +#include "nsString.h" +#include "nsTArray.h" +#include "nsAutoPtr.h" +#include "nsCOMPtr.h" +#include "nsIFile.h" +#include "nsUrlClassifierPrefixSet.h" +#include "prlog.h" + +namespace mozilla { +namespace safebrowsing { + +#define MAX_HOST_COMPONENTS 5 +#define MAX_PATH_COMPONENTS 4 + +class LookupResult { +public: + LookupResult() : mComplete(false), mNoise(false), mFresh(false), mProtocolConfirmed(false) {} + + // The fragment that matched in the LookupCache + union { + Prefix prefix; + Completion complete; + } hash; + + const Prefix &PrefixHash() { return hash.prefix; } + const Completion &CompleteHash() { return hash.complete; } + + bool Confirmed() const { return (mComplete && mFresh) || mProtocolConfirmed; } + bool Complete() const { return mComplete; } + + // True if we have a complete match for this hash in the table. + bool mComplete; + + // True if this is a noise entry, i.e. an extra entry + // that is inserted to mask the true URL we are requesting + bool mNoise; + + // Value of actual key looked up in the prefixset (coded with client key) + Prefix mCodedPrefix; + + // True if we've updated this table recently-enough. + bool mFresh; + + bool mProtocolConfirmed; + + nsCString mTableName; +}; + +typedef nsTArray LookupResultArray; + +struct CacheResult { + AddComplete entry; + nsCString table; +}; +typedef nsTArray CacheResultArray; + +class LookupCache { +public: + // Check for a canonicalized IP address. + static bool IsCanonicalizedIP(const nsACString& aHost); + + // take a lookup string (www.hostname.com/path/to/resource.html) and + // expand it into the set of fragments that should be searched for in an + // entry + static nsresult GetLookupFragments(const nsACString& aSpec, + nsTArray* aFragments); + // Similar to GetKey(), but if the domain contains three or more components, + // two keys will be returned: + // hostname.com/foo/bar -> [hostname.com] + // mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com] + // www.mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com] + static nsresult GetHostKeys(const nsACString& aSpec, + nsTArray* aHostKeys); + // Get the database key for a given URI. This is the top three + // domain components if they exist, otherwise the top two. + // hostname.com/foo/bar -> hostname.com + // mail.hostname.com/foo/bar -> mail.hostname.com + // www.mail.hostname.com/foo/bar -> mail.hostname.com + static nsresult GetKey(const nsACString& aSpec, Completion* aHash, + nsCOMPtr& aCryptoHash); + + /* We have both a prefix and a domain. Drop the domain, but + hash the domain, the prefix and a random value together, + ensuring any collisions happens at a different points for + different users. + */ + static nsresult KeyedHash(PRUint32 aPref, PRUint32 aDomain, + PRUint32 aKey, PRUint32* aOut); + + LookupCache(const nsACString& aTableName, nsIFile* aStoreFile); + ~LookupCache(); + + const nsCString &TableName() const { return mTableName; } + + nsresult Init(); + nsresult Open(); + nsresult Build(const AddPrefixArray& aAddPrefixes, + const AddCompleteArray& aAddCompletes); + nsresult GetPrefixes(nsTArray* aAddPrefixes); + +#if DEBUG && defined(PR_LOGGING) + void Dump(); +#endif + nsresult WriteFile(); + nsresult Has(const Completion& aCompletion, + const Completion& aHostkey, + PRUint32 aHashKey, + bool* aHas, bool* aComplete, + Prefix* aOrigPrefix); + bool IsPrimed(); + +private: + + void Clear(); + nsresult Reset(); + void UpdateHeader(); + nsresult ReadHeader(); + nsresult EnsureSizeConsistent(); + nsresult ReadCompletions(); + // Construct a Prefix Set with known prefixes + nsresult LoadPrefixSet(); + nsresult ConstructPrefixSet(const AddPrefixArray& aAddPrefixes); + + struct Header { + uint32 magic; + uint32 version; + uint32 numCompletions; + }; + Header mHeader; + + bool mPrimed; + nsCString mTableName; + nsCOMPtr mStoreDirectory; + nsCOMPtr mInputStream; + CompletionArray mCompletions; + // Set of prefixes known to be in the database + nsRefPtr mPrefixSet; +}; + +} +} + +#endif diff --git a/toolkit/components/url-classifier/Makefile.in b/toolkit/components/url-classifier/Makefile.in index 7d6902331023..2381ed8afa2a 100644 --- a/toolkit/components/url-classifier/Makefile.in +++ b/toolkit/components/url-classifier/Makefile.in @@ -59,11 +59,17 @@ XPIDLSRCS = \ $(NULL) CPPSRCS = \ + ChunkSet.cpp \ + Classifier.cpp \ + HashStore.cpp \ + ProtocolParser.cpp \ + LookupCache.cpp \ nsUrlClassifierDBService.cpp \ nsUrlClassifierStreamUpdater.cpp \ nsUrlClassifierUtils.cpp \ nsUrlClassifierPrefixSet.cpp \ nsUrlClassifierProxies.cpp \ + nsCheckSummedOutputStream.cpp \ $(NULL) LOCAL_INCLUDES = \ diff --git a/toolkit/components/url-classifier/ProtocolParser.cpp b/toolkit/components/url-classifier/ProtocolParser.cpp new file mode 100644 index 000000000000..52c47e5b35ba --- /dev/null +++ b/toolkit/components/url-classifier/ProtocolParser.cpp @@ -0,0 +1,777 @@ +//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * Gian-Carlo Pascutto + * + * 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 ***** */ + +//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +#include "ProtocolParser.h" +#include "LookupCache.h" +#include "nsIKeyModule.h" +#include "nsNetCID.h" +#include "prlog.h" +#include "prnetdb.h" +#include "prprf.h" + +#include "nsUrlClassifierUtils.h" + +// NSPR_LOG_MODULES=UrlClassifierDbService:5 +extern PRLogModuleInfo *gUrlClassifierDbServiceLog; +#if defined(PR_LOGGING) +#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) +#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) +#else +#define LOG(args) +#define LOG_ENABLED() (PR_FALSE) +#endif + +namespace mozilla { +namespace safebrowsing { + +// Updates will fail if fed chunks larger than this +const uint32 MAX_CHUNK_SIZE = (1024 * 1024); + +const uint32 DOMAIN_SIZE = 4; + +// Parse one stringified range of chunks of the form "n" or "n-m" from a +// comma-separated list of chunks. Upon return, 'begin' will point to the +// next range of chunks in the list of chunks. +static bool +ParseChunkRange(nsACString::const_iterator& aBegin, + const nsACString::const_iterator& aEnd, + PRUint32* aFirst, PRUint32* aLast) +{ + nsACString::const_iterator iter = aBegin; + FindCharInReadable(',', iter, aEnd); + + nsCAutoString element(Substring(aBegin, iter)); + aBegin = iter; + if (aBegin != aEnd) + aBegin++; + + PRUint32 numRead = PR_sscanf(element.get(), "%u-%u", aFirst, aLast); + if (numRead == 2) { + if (*aFirst > *aLast) { + PRUint32 tmp = *aFirst; + *aFirst = *aLast; + *aLast = tmp; + } + return true; + } + + if (numRead == 1) { + *aLast = *aFirst; + return true; + } + + return false; +} + +ProtocolParser::ProtocolParser(PRUint32 aHashKey) + : mState(PROTOCOL_STATE_CONTROL) + , mHashKey(aHashKey) + , mUpdateStatus(NS_OK) + , mUpdateWait(0) + , mResetRequested(false) + , mRekeyRequested(false) +{ +} + +ProtocolParser::~ProtocolParser() +{ + CleanupUpdates(); +} + +nsresult +ProtocolParser::Init(nsICryptoHash* aHasher) +{ + mCryptoHash = aHasher; + return NS_OK; +} + +/** + * Initialize HMAC for the stream. + * + * If serverMAC is empty, the update stream will need to provide a + * server MAC. + */ +nsresult +ProtocolParser::InitHMAC(const nsACString& aClientKey, + const nsACString& aServerMAC) +{ + mServerMAC = aServerMAC; + + nsresult rv; + nsCOMPtr keyObjectFactory( + do_GetService("@mozilla.org/security/keyobjectfactory;1", &rv)); + + if (NS_FAILED(rv)) { + NS_WARNING("Failed to get nsIKeyObjectFactory service"); + mUpdateStatus = rv; + return mUpdateStatus; + } + + nsCOMPtr keyObject; + rv = keyObjectFactory->KeyFromString(nsIKeyObject::HMAC, aClientKey, + getter_AddRefs(keyObject)); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to create key object, maybe not FIPS compliant?"); + mUpdateStatus = rv; + return mUpdateStatus; + } + + mHMAC = do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to create nsICryptoHMAC instance"); + mUpdateStatus = rv; + return mUpdateStatus; + } + + rv = mHMAC->Init(nsICryptoHMAC::SHA1, keyObject); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to initialize nsICryptoHMAC instance"); + mUpdateStatus = rv; + return mUpdateStatus; + } + return NS_OK; +} + +nsresult +ProtocolParser::FinishHMAC() +{ + if (NS_FAILED(mUpdateStatus)) { + return mUpdateStatus; + } + + if (mRekeyRequested) { + mUpdateStatus = NS_ERROR_FAILURE; + return mUpdateStatus; + } + + if (!mHMAC) { + return NS_OK; + } + + nsCAutoString clientMAC; + mHMAC->Finish(PR_TRUE, clientMAC); + + if (clientMAC != mServerMAC) { + NS_WARNING("Invalid update MAC!"); + LOG(("Invalid update MAC: expected %s, got %s", + clientMAC.get(), mServerMAC.get())); + mUpdateStatus = NS_ERROR_FAILURE; + } + return mUpdateStatus; +} + +void +ProtocolParser::SetCurrentTable(const nsACString& aTable) +{ + mTableUpdate = GetTableUpdate(aTable); +} + +nsresult +ProtocolParser::AppendStream(const nsACString& aData) +{ + if (NS_FAILED(mUpdateStatus)) + return mUpdateStatus; + + nsresult rv; + + // Digest the data if we have a server MAC. + if (mHMAC && !mServerMAC.IsEmpty()) { + rv = mHMAC->Update(reinterpret_cast(aData.BeginReading()), + aData.Length()); + if (NS_FAILED(rv)) { + mUpdateStatus = rv; + return rv; + } + } + + mPending.Append(aData); + + bool done = false; + while (!done) { + if (mState == PROTOCOL_STATE_CONTROL) { + rv = ProcessControl(&done); + } else if (mState == PROTOCOL_STATE_CHUNK) { + rv = ProcessChunk(&done); + } else { + NS_ERROR("Unexpected protocol state"); + rv = NS_ERROR_FAILURE; + } + if (NS_FAILED(rv)) { + mUpdateStatus = rv; + return rv; + } + } + return NS_OK; +} + +nsresult +ProtocolParser::ProcessControl(bool* aDone) +{ + nsresult rv; + + nsCAutoString line; + *aDone = true; + while (NextLine(line)) { + //LOG(("Processing %s\n", line.get())); + + if (line.EqualsLiteral("e:pleaserekey")) { + mRekeyRequested = true; + return NS_OK; + } else if (mHMAC && mServerMAC.IsEmpty()) { + rv = ProcessMAC(line); + NS_ENSURE_SUCCESS(rv, rv); + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) { + SetCurrentTable(Substring(line, 2)); + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) { + if (PR_sscanf(line.get(), "n:%d", &mUpdateWait) != 1) { + LOG(("Error parsing n: '%s' (%d)", line.get(), mUpdateWait)); + mUpdateWait = 0; + } + } else if (line.EqualsLiteral("r:pleasereset")) { + mResetRequested = true; + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("u:"))) { + rv = ProcessForward(line); + NS_ENSURE_SUCCESS(rv, rv); + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("a:")) || + StringBeginsWith(line, NS_LITERAL_CSTRING("s:"))) { + rv = ProcessChunkControl(line); + NS_ENSURE_SUCCESS(rv, rv); + *aDone = false; + return NS_OK; + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("ad:")) || + StringBeginsWith(line, NS_LITERAL_CSTRING("sd:"))) { + rv = ProcessExpirations(line); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + *aDone = true; + return NS_OK; +} + + +nsresult +ProtocolParser::ProcessMAC(const nsCString& aLine) +{ + nsresult rv; + + LOG(("line: %s", aLine.get())); + + if (StringBeginsWith(aLine, NS_LITERAL_CSTRING("m:"))) { + mServerMAC = Substring(aLine, 2); + nsUrlClassifierUtils::UnUrlsafeBase64(mServerMAC); + + // The remainder of the pending update wasn't digested, digest it now. + rv = mHMAC->Update(reinterpret_cast(mPending.BeginReading()), + mPending.Length()); + return rv; + } + + LOG(("No MAC specified!")); + return NS_ERROR_FAILURE; +} + +nsresult +ProtocolParser::ProcessExpirations(const nsCString& aLine) +{ + if (!mTableUpdate) { + NS_WARNING("Got an expiration without a table."); + return NS_ERROR_FAILURE; + } + const nsCSubstring &list = Substring(aLine, 3); + nsACString::const_iterator begin, end; + list.BeginReading(begin); + list.EndReading(end); + while (begin != end) { + PRUint32 first, last; + if (ParseChunkRange(begin, end, &first, &last)) { + for (PRUint32 num = first; num <= last; num++) { + if (aLine[0] == 'a') + mTableUpdate->NewAddExpiration(num); + else + mTableUpdate->NewSubExpiration(num); + } + } else { + return NS_ERROR_FAILURE; + } + } + return NS_OK; +} + +nsresult +ProtocolParser::ProcessChunkControl(const nsCString& aLine) +{ + if (!mTableUpdate) { + NS_WARNING("Got a chunk before getting a table."); + return NS_ERROR_FAILURE; + } + + mState = PROTOCOL_STATE_CHUNK; + char command; + + mChunkState.Clear(); + + if (PR_sscanf(aLine.get(), + "%c:%d:%d:%d", + &command, + &mChunkState.num, &mChunkState.hashSize, &mChunkState.length) + != 4) + { + return NS_ERROR_FAILURE; + } + + if (mChunkState.length > MAX_CHUNK_SIZE) { + return NS_ERROR_FAILURE; + } + + if (!(mChunkState.hashSize == PREFIX_SIZE || mChunkState.hashSize == COMPLETE_SIZE)) { + NS_WARNING("Invalid hash size specified in update."); + return NS_ERROR_FAILURE; + } + + mChunkState.type = (command == 'a') ? CHUNK_ADD : CHUNK_SUB; + + if (mChunkState.type == CHUNK_ADD) { + mTableUpdate->NewAddChunk(mChunkState.num); + } else { + mTableUpdate->NewSubChunk(mChunkState.num); + } + + return NS_OK; +} + +nsresult +ProtocolParser::ProcessForward(const nsCString& aLine) +{ + const nsCSubstring &forward = Substring(aLine, 2); + if (mHMAC) { + // We're expecting MACs alongside any url forwards. + nsCSubstring::const_iterator begin, end, sepBegin, sepEnd; + forward.BeginReading(begin); + sepBegin = begin; + + forward.EndReading(end); + sepEnd = end; + + if (!RFindInReadable(NS_LITERAL_CSTRING(","), sepBegin, sepEnd)) { + NS_WARNING("No MAC specified for a redirect in a request that expects a MAC"); + return NS_ERROR_FAILURE; + } + + nsCString serverMAC(Substring(sepEnd, end)); + nsUrlClassifierUtils::UnUrlsafeBase64(serverMAC); + return AddForward(Substring(begin, sepBegin), serverMAC); + } + return AddForward(forward, mServerMAC); +} + +nsresult +ProtocolParser::AddForward(const nsACString& aUrl, const nsACString& aMac) +{ + if (!mTableUpdate) { + NS_WARNING("Forward without a table name."); + return NS_ERROR_FAILURE; + } + + ForwardedUpdate *forward = mForwards.AppendElement(); + forward->table = mTableUpdate->TableName(); + forward->url.Assign(aUrl); + forward->mac.Assign(aMac); + + return NS_OK; +} + +nsresult +ProtocolParser::ProcessChunk(bool* aDone) +{ + if (!mTableUpdate) { + NS_WARNING("Processing chunk without an active table."); + return NS_ERROR_FAILURE; + } + + NS_ASSERTION(mChunkState.num != 0, "Must have a chunk number."); + + if (mPending.Length() < mChunkState.length) { + *aDone = true; + return NS_OK; + } + + // Pull the chunk out of the pending stream data. + nsCAutoString chunk; + chunk.Assign(Substring(mPending, 0, mChunkState.length)); + mPending = Substring(mPending, mChunkState.length); + + *aDone = false; + mState = PROTOCOL_STATE_CONTROL; + + //LOG(("Handling a %d-byte chunk", chunk.Length())); + if (StringEndsWith(mTableUpdate->TableName(), NS_LITERAL_CSTRING("-shavar"))) { + return ProcessShaChunk(chunk); + } else { + return ProcessPlaintextChunk(chunk); + } +} + +/** + * Process a plaintext chunk (currently only used in unit tests). + */ +nsresult +ProtocolParser::ProcessPlaintextChunk(const nsACString& aChunk) +{ + if (!mTableUpdate) { + NS_WARNING("Chunk received with no table."); + return NS_ERROR_FAILURE; + } + + nsresult rv; + nsTArray lines; + ParseString(PromiseFlatCString(aChunk), '\n', lines); + + // non-hashed tables need to be hashed + for (uint32 i = 0; i < lines.Length(); i++) { + nsCString& line = lines[i]; + + if (mChunkState.type == CHUNK_ADD) { + if (mChunkState.hashSize == COMPLETE_SIZE) { + Completion hash; + hash.FromPlaintext(line, mCryptoHash); + mTableUpdate->NewAddComplete(mChunkState.num, hash); + } else { + NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks."); + Completion hash; + Completion domHash; + Prefix newHash; + rv = LookupCache::GetKey(line, &domHash, mCryptoHash); + NS_ENSURE_SUCCESS(rv, rv); + hash.FromPlaintext(line, mCryptoHash); + PRUint32 codedHash; + rv = LookupCache::KeyedHash(hash.ToUint32(), domHash.ToUint32(), mHashKey, &codedHash); + NS_ENSURE_SUCCESS(rv, rv); + newHash.FromUint32(codedHash); + mTableUpdate->NewAddPrefix(mChunkState.num, newHash); + } + } else { + nsCString::const_iterator begin, iter, end; + line.BeginReading(begin); + line.EndReading(end); + iter = begin; + uint32 addChunk; + if (!FindCharInReadable(':', iter, end) || + PR_sscanf(lines[i].get(), "%d:", &addChunk) != 1) { + NS_WARNING("Received sub chunk without associated add chunk."); + return NS_ERROR_FAILURE; + } + iter++; + + if (mChunkState.hashSize == COMPLETE_SIZE) { + Completion hash; + hash.FromPlaintext(Substring(iter, end), mCryptoHash); + mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num); + } else { + NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks."); + Prefix hash; + Completion domHash; + Prefix newHash; + rv = LookupCache::GetKey(Substring(iter, end), &domHash, mCryptoHash); + NS_ENSURE_SUCCESS(rv, rv); + hash.FromPlaintext(Substring(iter, end), mCryptoHash); + PRUint32 codedHash; + rv = LookupCache::KeyedHash(hash.ToUint32(), domHash.ToUint32(), mHashKey, &codedHash); + NS_ENSURE_SUCCESS(rv, rv); + newHash.FromUint32(codedHash); + mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num); + // Needed to knock out completes + // Fake chunk nr, will cause it to be removed next update + mTableUpdate->NewSubPrefix(addChunk, hash, 0); + mTableUpdate->NewSubChunk(0); + } + } + } + + return NS_OK; +} + +nsresult +ProtocolParser::ProcessShaChunk(const nsACString& aChunk) +{ + PRUint32 start = 0; + while (start < aChunk.Length()) { + // First four bytes are the domain key. + Prefix domain; + domain.Assign(Substring(aChunk, start, DOMAIN_SIZE)); + start += DOMAIN_SIZE; + + // Then a count of entries. + uint8 numEntries = static_cast(aChunk[start]); + start++; + + nsresult rv; + if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == PREFIX_SIZE) { + rv = ProcessHostAdd(domain, numEntries, aChunk, &start); + } else if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == COMPLETE_SIZE) { + rv = ProcessHostAddComplete(numEntries, aChunk, &start); + } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == PREFIX_SIZE) { + rv = ProcessHostSub(domain, numEntries, aChunk, &start); + } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == COMPLETE_SIZE) { + rv = ProcessHostSubComplete(numEntries, aChunk, &start); + } else { + NS_WARNING("Unexpected chunk type/hash size!"); + LOG(("Got an unexpected chunk type/hash size: %s:%d", + mChunkState.type == CHUNK_ADD ? "add" : "sub", + mChunkState.hashSize)); + return NS_ERROR_FAILURE; + } + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +nsresult +ProtocolParser::ProcessHostAdd(const Prefix& aDomain, PRUint8 aNumEntries, + const nsACString& aChunk, PRUint32* aStart) +{ + NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE, + "ProcessHostAdd should only be called for prefix hashes."); + + PRUint32 codedHash; + PRUint32 domHash = aDomain.ToUint32(); + + if (aNumEntries == 0) { + nsresult rv = LookupCache::KeyedHash(domHash, domHash, mHashKey, &codedHash); + NS_ENSURE_SUCCESS(rv, rv); + Prefix newHash; + newHash.FromUint32(codedHash); + mTableUpdate->NewAddPrefix(mChunkState.num, newHash); + return NS_OK; + } + + if (*aStart + (PREFIX_SIZE * aNumEntries) > aChunk.Length()) { + NS_WARNING("Chunk is not long enough to contain the expected entries."); + return NS_ERROR_FAILURE; + } + + for (uint8 i = 0; i < aNumEntries; i++) { + Prefix hash; + hash.Assign(Substring(aChunk, *aStart, PREFIX_SIZE)); + nsresult rv = LookupCache::KeyedHash(domHash, hash.ToUint32(), mHashKey, &codedHash); + NS_ENSURE_SUCCESS(rv, rv); + Prefix newHash; + newHash.FromUint32(codedHash); + mTableUpdate->NewAddPrefix(mChunkState.num, newHash); + *aStart += PREFIX_SIZE; + } + + return NS_OK; +} + +nsresult +ProtocolParser::ProcessHostSub(const Prefix& aDomain, PRUint8 aNumEntries, + const nsACString& aChunk, PRUint32 *aStart) +{ + NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE, + "ProcessHostSub should only be called for prefix hashes."); + + PRUint32 codedHash; + PRUint32 domHash = aDomain.ToUint32(); + + if (aNumEntries == 0) { + if ((*aStart) + 4 > aChunk.Length()) { + NS_WARNING("Received a zero-entry sub chunk without an associated add."); + return NS_ERROR_FAILURE; + } + + const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4); + *aStart += 4; + + uint32 addChunk; + memcpy(&addChunk, addChunkStr.BeginReading(), 4); + addChunk = PR_ntohl(addChunk); + + nsresult rv = LookupCache::KeyedHash(domHash, domHash, mHashKey, &codedHash); + NS_ENSURE_SUCCESS(rv, rv); + Prefix newHash; + newHash.FromUint32(codedHash); + + mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num); + // Needed to knock out completes + // Fake chunk nr, will cause it to be removed next update + mTableUpdate->NewSubPrefix(addChunk, aDomain, 0); + mTableUpdate->NewSubChunk(0); + return NS_OK; + } + + if (*aStart + ((PREFIX_SIZE + 4) * aNumEntries) > aChunk.Length()) { + NS_WARNING("Chunk is not long enough to contain the expected entries."); + return NS_ERROR_FAILURE; + } + + for (uint8 i = 0; i < aNumEntries; i++) { + const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4); + *aStart += 4; + + uint32 addChunk; + memcpy(&addChunk, addChunkStr.BeginReading(), 4); + addChunk = PR_ntohl(addChunk); + + Prefix prefix; + prefix.Assign(Substring(aChunk, *aStart, PREFIX_SIZE)); + *aStart += PREFIX_SIZE; + + nsresult rv = LookupCache::KeyedHash(prefix.ToUint32(), domHash, mHashKey, &codedHash); + NS_ENSURE_SUCCESS(rv, rv); + Prefix newHash; + newHash.FromUint32(codedHash); + + mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num); + // Needed to knock out completes + // Fake chunk nr, will cause it to be removed next update + mTableUpdate->NewSubPrefix(addChunk, prefix, 0); + mTableUpdate->NewSubChunk(0); + } + + return NS_OK; +} + +nsresult +ProtocolParser::ProcessHostAddComplete(PRUint8 aNumEntries, + const nsACString& aChunk, PRUint32* aStart) +{ + NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE, + "ProcessHostAddComplete should only be called for complete hashes."); + + if (aNumEntries == 0) { + // this is totally comprehensible. + NS_WARNING("Expected > 0 entries for a 32-byte hash add."); + return NS_OK; + } + + if (*aStart + (COMPLETE_SIZE * aNumEntries) > aChunk.Length()) { + NS_WARNING("Chunk is not long enough to contain the expected entries."); + return NS_ERROR_FAILURE; + } + + for (uint8 i = 0; i < aNumEntries; i++) { + Completion hash; + hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE)); + mTableUpdate->NewAddComplete(mChunkState.num, hash); + *aStart += COMPLETE_SIZE; + } + + return NS_OK; +} + +nsresult +ProtocolParser::ProcessHostSubComplete(PRUint8 aNumEntries, + const nsACString& aChunk, PRUint32* aStart) +{ + NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE, + "ProcessHostSub should only be called for prefix hashes."); + + if (aNumEntries == 0) { + // this is totally comprehensible. + NS_WARNING("Expected > 0 entries for a 32-byte hash add."); + return NS_OK; + } + + if (*aStart + ((COMPLETE_SIZE + 4) * aNumEntries) > aChunk.Length()) { + NS_WARNING("Chunk is not long enough to contain the expected entries."); + return NS_ERROR_FAILURE; + } + + for (PRUint8 i = 0; i < aNumEntries; i++) { + Completion hash; + hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE)); + *aStart += COMPLETE_SIZE; + + const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4); + *aStart += 4; + + uint32 addChunk; + memcpy(&addChunk, addChunkStr.BeginReading(), 4); + addChunk = PR_ntohl(addChunk); + + mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num); + } + + return NS_OK; +} + +bool +ProtocolParser::NextLine(nsACString& line) +{ + int32 newline = mPending.FindChar('\n'); + if (newline == kNotFound) { + return false; + } + line.Assign(Substring(mPending, 0, newline)); + mPending = Substring(mPending, newline + 1); + return true; +} + +void +ProtocolParser::CleanupUpdates() +{ + for (uint32 i = 0; i < mTableUpdates.Length(); i++) { + delete mTableUpdates[i]; + } + mTableUpdates.Clear(); +} + +TableUpdate * +ProtocolParser::GetTableUpdate(const nsACString& aTable) +{ + for (uint32 i = 0; i < mTableUpdates.Length(); i++) { + if (aTable.Equals(mTableUpdates[i]->TableName())) { + return mTableUpdates[i]; + } + } + + // We free automatically on destruction, ownership of these + // updates can be transferred to DBServiceWorker, which passes + // them back to Classifier when doing the updates, and that + // will free them. + TableUpdate *update = new TableUpdate(aTable); + mTableUpdates.AppendElement(update); + return update; +} + +} +} diff --git a/toolkit/components/url-classifier/ProtocolParser.h b/toolkit/components/url-classifier/ProtocolParser.h new file mode 100644 index 000000000000..e263d4f43617 --- /dev/null +++ b/toolkit/components/url-classifier/ProtocolParser.h @@ -0,0 +1,151 @@ +//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dave Camp + * Gian-Carlo Pascutto + * + * 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 ProtocolParser_h__ +#define ProtocolParser_h__ + +#include "HashStore.h" +#include "nsICryptoHMAC.h" + +namespace mozilla { +namespace safebrowsing { + +/** + * Some helpers for parsing the safe + */ +class ProtocolParser { +public: + struct ForwardedUpdate { + nsCString table; + nsCString url; + nsCString mac; + }; + + ProtocolParser(PRUint32 aHashKey); + ~ProtocolParser(); + + nsresult Status() const { return mUpdateStatus; } + + nsresult Init(nsICryptoHash* aHasher); + + nsresult InitHMAC(const nsACString& aClientKey, + const nsACString& aServerMAC); + nsresult FinishHMAC(); + + void SetCurrentTable(const nsACString& aTable); + + nsresult Begin(); + nsresult AppendStream(const nsACString& aData); + + // Forget the table updates that were created by this pass. It + // becomes the caller's responsibility to free them. This is shitty. + TableUpdate *GetTableUpdate(const nsACString& aTable); + void ForgetTableUpdates() { mTableUpdates.Clear(); } + nsTArray &GetTableUpdates() { return mTableUpdates; } + + // Update information. + const nsTArray &Forwards() const { return mForwards; } + int32 UpdateWait() { return mUpdateWait; } + bool ResetRequested() { return mResetRequested; } + bool RekeyRequested() { return mRekeyRequested; } + +private: + nsresult ProcessControl(bool* aDone); + nsresult ProcessMAC(const nsCString& aLine); + nsresult ProcessExpirations(const nsCString& aLine); + nsresult ProcessChunkControl(const nsCString& aLine); + nsresult ProcessForward(const nsCString& aLine); + nsresult AddForward(const nsACString& aUrl, const nsACString& aMac); + nsresult ProcessChunk(bool* done); + nsresult ProcessPlaintextChunk(const nsACString& aChunk); + nsresult ProcessShaChunk(const nsACString& aChunk); + nsresult ProcessHostAdd(const Prefix& aDomain, PRUint8 aNumEntries, + const nsACString& aChunk, PRUint32* aStart); + nsresult ProcessHostSub(const Prefix& aDomain, PRUint8 aNumEntries, + const nsACString& aChunk, PRUint32* aStart); + nsresult ProcessHostAddComplete(PRUint8 aNumEntries, const nsACString& aChunk, + PRUint32 *aStart); + nsresult ProcessHostSubComplete(PRUint8 numEntries, const nsACString& aChunk, + PRUint32* start); + bool NextLine(nsACString& aLine); + + void CleanupUpdates(); + + enum ParserState { + PROTOCOL_STATE_CONTROL, + PROTOCOL_STATE_CHUNK + }; + ParserState mState; + + enum ChunkType { + CHUNK_ADD, + CHUNK_SUB + }; + + struct ChunkState { + ChunkType type; + uint32 num; + uint32 hashSize; + uint32 length; + void Clear() { num = 0; hashSize = 0; length = 0; } + }; + ChunkState mChunkState; + + PRUint32 mHashKey; + nsCOMPtr mCryptoHash; + + nsresult mUpdateStatus; + nsCString mPending; + + nsCOMPtr mHMAC; + nsCString mServerMAC; + + uint32 mUpdateWait; + bool mResetRequested; + bool mRekeyRequested; + + nsTArray mForwards; + nsTArray mTableUpdates; + TableUpdate *mTableUpdate; +}; + +} +} + +#endif diff --git a/toolkit/components/url-classifier/content/listmanager.js b/toolkit/components/url-classifier/content/listmanager.js index bed67db4a840..144a917bd5d9 100644 --- a/toolkit/components/url-classifier/content/listmanager.js +++ b/toolkit/components/url-classifier/content/listmanager.js @@ -304,7 +304,7 @@ PROT_ListManager.prototype.maybeToggleUpdateChecking = function() { */ PROT_ListManager.prototype.startUpdateChecker = function() { this.stopUpdateChecker(); - + // Schedule the first check for between 15 and 45 minutes. var repeatingUpdateDelay = this.updateInterval / 2; repeatingUpdateDelay += Math.floor(Math.random() * this.updateInterval); diff --git a/toolkit/components/url-classifier/nsCheckSummedOutputStream.cpp b/toolkit/components/url-classifier/nsCheckSummedOutputStream.cpp new file mode 100644 index 000000000000..90661f4d54d0 --- /dev/null +++ b/toolkit/components/url-classifier/nsCheckSummedOutputStream.cpp @@ -0,0 +1,92 @@ +//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Gian-Carlo Pascutto + * + * 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 "nsILocalFile.h" +#include "nsCRT.h" +#include "nsIFile.h" +#include "nsISupportsImpl.h" +#include "nsCheckSummedOutputStream.h" + +//////////////////////////////////////////////////////////////////////////////// +// nsCheckSummedOutputStream + +NS_IMPL_ISUPPORTS_INHERITED3(nsCheckSummedOutputStream, + nsSafeFileOutputStream, + nsISafeOutputStream, + nsIOutputStream, + nsIFileOutputStream) + +NS_IMETHODIMP +nsCheckSummedOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, + PRInt32 behaviorFlags) +{ + nsresult rv; + mHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mHash->Init(nsICryptoHash::MD5); + NS_ENSURE_SUCCESS(rv, rv); + + return nsSafeFileOutputStream::Init(file, ioFlags, perm, behaviorFlags); +} + +NS_IMETHODIMP +nsCheckSummedOutputStream::Finish() +{ + nsresult rv = mHash->Finish(false, mCheckSum); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 written; + rv = nsSafeFileOutputStream::Write(reinterpret_cast(mCheckSum.BeginReading()), + mCheckSum.Length(), &written); + NS_ASSERTION(written == mCheckSum.Length(), "Error writing stream checksum"); + NS_ENSURE_SUCCESS(rv, rv); + + return nsSafeFileOutputStream::Finish(); +} + +NS_IMETHODIMP +nsCheckSummedOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result) +{ + nsresult rv = mHash->Update(reinterpret_cast(buf), count); + NS_ENSURE_SUCCESS(rv, rv); + + return nsSafeFileOutputStream::Write(buf, count, result); +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/toolkit/components/url-classifier/nsCheckSummedOutputStream.h b/toolkit/components/url-classifier/nsCheckSummedOutputStream.h new file mode 100644 index 000000000000..d0f50f9b1ae7 --- /dev/null +++ b/toolkit/components/url-classifier/nsCheckSummedOutputStream.h @@ -0,0 +1,86 @@ +//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code. + * + * The Initial Developer of the Original Code is + * the Mozilla Foundation. + * Portions created by the Initial Developer are Copyright (C) 2011 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Gian-Carlo Pascutto + * + * 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 nsCheckSummedOutputStream_h__ +#define nsCheckSummedOutputStream_h__ + +#include "nsILocalFile.h" +#include "nsIFile.h" +#include "nsIOutputStream.h" +#include "nsICryptoHash.h" +#include "nsNetCID.h" +#include "nsString.h" +#include "../../../netwerk/base/src/nsFileStreams.h" +#include "nsToolkitCompsCID.h" + +class nsCheckSummedOutputStream : public nsSafeFileOutputStream +{ +public: + NS_DECL_ISUPPORTS_INHERITED + + // Size of MD5 hash in bytes + static const PRUint32 CHECKSUM_SIZE = 16; + + nsCheckSummedOutputStream() {} + virtual ~nsCheckSummedOutputStream() { nsSafeFileOutputStream::Close(); } + + NS_IMETHOD Finish(); + NS_IMETHOD Write(const char *buf, PRUint32 count, PRUint32 *result); + NS_IMETHOD Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, PRInt32 behaviorFlags); + +protected: + nsCOMPtr mHash; + nsCAutoString mCheckSum; +}; + +// returns a file output stream which can be QI'ed to nsIFileOutputStream. +inline nsresult +NS_NewCheckSummedOutputStream(nsIOutputStream **result, + nsIFile *file, + PRInt32 ioFlags = -1, + PRInt32 perm = -1, + PRInt32 behaviorFlags = 0) +{ + nsCOMPtr out = new nsCheckSummedOutputStream(); + nsresult rv = out->Init(file, ioFlags, perm, behaviorFlags); + if (NS_SUCCEEDED(rv)) + NS_ADDREF(*result = out); // cannot use nsCOMPtr::swap + return rv; +} + +#endif diff --git a/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl b/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl index e15323f11c56..be3a673619dc 100644 --- a/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl +++ b/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl @@ -40,10 +40,12 @@ %{C++ #include "nsTArray.h" -class nsUrlClassifierLookupResult; +#include "Entries.h" +#include "LookupCache.h" %} -[ptr] native ResultArray(nsTArray); - +[ptr] native ResultArray(nsTArray); +[ptr] native CacheCompletionArray(nsTArray); +[ptr] native PrefixArray(mozilla::safebrowsing::PrefixArray); interface nsIUrlClassifierHashCompleter; // Interface for JS function callbacks @@ -231,14 +233,14 @@ interface nsIUrlClassifierDBService : nsISupports * Interface for the actual worker thread. Implementations of this need not * be thread aware and just work on the database. */ -[scriptable, uuid(2af84c09-269e-4fc2-b28f-af56717db118)] +[scriptable, uuid(0445be75-b114-43ea-89dc-aa16af26e77e)] interface nsIUrlClassifierDBServiceWorker : nsIUrlClassifierDBService { // Provide a way to forcibly close the db connection. void closeDb(); - // Cache the results of a hash completion. - [noscript]void cacheCompletions(in ResultArray entries); + [noscript]void cacheCompletions(in CacheCompletionArray completions); + [noscript]void cacheMisses(in PrefixArray misses); }; /** @@ -247,7 +249,7 @@ interface nsIUrlClassifierDBServiceWorker : nsIUrlClassifierDBService * lookup to provide a set of possible results, which the main thread * may need to expand using an nsIUrlClassifierCompleter. */ -[uuid(f1dc83c6-ad43-4f0f-a809-fd43de7de8a4)] +[uuid(b903dc8f-dff1-42fe-894b-36e7a59bb801)] interface nsIUrlClassifierLookupCallback : nsISupports { /** diff --git a/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl b/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl index 0d6624cbfff7..1df5a7878412 100644 --- a/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl +++ b/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl @@ -39,28 +39,26 @@ #include "nsISupports.idl" #include "nsIFile.idl" -interface nsIArray; - // Note that the PrefixSet name is historical and we do properly support // duplicated values, so it's really a Prefix Trie. // All methods are thread-safe. - -[scriptable, uuid(519c8519-0f30-426b-bb7b-c400ba0318e2)] +[scriptable, uuid(b21b0fa1-20d2-422a-b2cc-b289c9325811)] interface nsIUrlClassifierPrefixSet : nsISupports { + // Initialize the PrefixSet. Give it a name for memory reporting. + void init(in ACString aName); // Fills the PrefixSet with the given array of prefixes. // Can send an empty Array to clear the tree. A truly "empty tree" // cannot be represented, so put a sentinel value if that is required // Requires array to be sorted. void setPrefixes([const, array, size_is(aLength)] in unsigned long aPrefixes, in unsigned long aLength); + void getPrefixes(out unsigned long aCount, + [array, size_is(aCount), retval] out unsigned long aPrefixes); // Do a lookup in the PrefixSet, return whether the value is present. // If aReady is set, we will block until there are any entries. // If not set, we will return in aReady whether we were ready or not. - boolean probe(in unsigned long aPrefix, in unsigned long aKey, - inout boolean aReady); - // Return the key that is used to randomize the collisions in the prefixes. - PRUint32 getKey(); + boolean probe(in unsigned long aPrefix, inout boolean aReady); boolean isEmpty(); void loadFromFile(in nsIFile aFile); void storeToFile(in nsIFile aFile); diff --git a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp index c135f358954c..f2870f8e405e 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp @@ -42,14 +42,8 @@ #include "nsAutoPtr.h" #include "nsCOMPtr.h" -#include "mozIStorageService.h" -#include "mozIStorageConnection.h" -#include "mozIStorageStatement.h" -#include "mozStorageHelper.h" -#include "mozStorageCID.h" #include "nsAppDirectoryServiceDefs.h" #include "nsCRT.h" -#include "nsDataHashtable.h" #include "nsICryptoHash.h" #include "nsICryptoHMAC.h" #include "nsIDirectoryService.h" @@ -73,48 +67,23 @@ #include "nsNetCID.h" #include "nsThreadUtils.h" #include "nsXPCOMStrings.h" +#include "nsProxyRelease.h" #include "mozilla/Mutex.h" +#include "mozilla/TimeStamp.h" #include "mozilla/Telemetry.h" #include "prlog.h" #include "prprf.h" #include "prnetdb.h" -#include "zlib.h" - -// Needed to interpert mozIStorageConnection::GetLastError -#include +#include "Entries.h" +#include "Classifier.h" +#include "ProtocolParser.h" using namespace mozilla; - -/** - * The DBServices stores a set of Fragments. A fragment is one URL - * fragment containing two or more domain components and some number - * of path components. - * - * Fragment examples: - * example.com/ - * www.example.com/foo/bar - * www.mail.example.com/mail - * - * Fragments are described in "Simplified Regular Expression Lookup" - * section of the protocol document at - * http://code.google.com/p/google-safe-browsing/wiki/Protocolv2Spec - * - * A fragment is associated with a domain. The domain for a given - * fragment is the three-host-component domain of the fragment (two - * host components for URLs with only two components) with a trailing - * slash. So for the fragments listed above, the domains are - * example.com/, www.example.com/ and mail.example.com/. - * - * Fragments and domains are hashed in the database. The hash is described - * in the protocol document, but it's basically a truncated SHA256 hash. - * - * A (table, chunk id, domain key, fragment) tuple is referred to as - * an Entry. - */ +using namespace mozilla::safebrowsing; // NSPR_LOG_MODULES=UrlClassifierDbService:5 #if defined(PR_LOGGING) -static const PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull; +PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull; #define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) #else @@ -122,31 +91,6 @@ static const PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull; #define LOG_ENABLED() (false) #endif -// Schema versioning: note that we don't bother to migrate between different -// versions of the schema, we just start fetching the data freshly with each -// migration. - -// The database filename is updated when there is an incompatible -// schema change and we expect both implementations to continue -// accessing the same database (such as between stable versions of the -// platform). -#define DATABASE_FILENAME "urlclassifier3.sqlite" - -// The implementation version is updated during development when we -// want to change schema, or to recover from updating bugs. When an -// implementation version change is detected, the database is scrapped -// and we start over. -#define IMPLEMENTATION_VERSION 7 - -// Name of the persistent PrefixSet storage -#define PREFIXSET_FILENAME "urlclassifier.pset" - -#define MAX_HOST_COMPONENTS 5 -#define MAX_PATH_COMPONENTS 4 - -// Updates will fail if fed chunks larger than this -#define MAX_CHUNK_SIZE (1024 * 1024) - // Prefs for implementing nsIURIClassifier to block page loads #define CHECK_MALWARE_PREF "browser.safebrowsing.malware.enabled" #define CHECK_MALWARE_DEFAULT false @@ -162,23 +106,6 @@ static const PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull; #define CONFIRM_AGE_PREF "urlclassifier.confirm-age" #define CONFIRM_AGE_DEFAULT_SEC (45 * 60) -#define UPDATE_CACHE_SIZE_PREF "urlclassifier.updatecachemax" -#define UPDATE_CACHE_SIZE_DEFAULT -1 - -#define LOOKUP_CACHE_SIZE_PREF "urlclassifier.lookupcachemax" -#define LOOKUP_CACHE_SIZE_DEFAULT -1 - -// Amount of time to spend updating before committing and delaying, in -// seconds. This is checked after each update stream, so the actual -// time spent can be higher than this, depending on update stream size. -#define UPDATE_WORKING_TIME "urlclassifier.workingtime" -#define UPDATE_WORKING_TIME_DEFAULT 5 - -// The amount of time to delay after hitting UPDATE_WORKING_TIME, in -// seconds. -#define UPDATE_DELAY_TIME "urlclassifier.updatetime" -#define UPDATE_DELAY_TIME_DEFAULT 60 - class nsUrlClassifierDBServiceWorker; // Singleton instance. @@ -192,12 +119,6 @@ static bool gShuttingDownThread = false; static PRInt32 gFreshnessGuarantee = CONFIRM_AGE_DEFAULT_SEC; -static PRInt32 gUpdateCacheSize = UPDATE_CACHE_SIZE_DEFAULT; -static PRInt32 gLookupCacheSize = LOOKUP_CACHE_SIZE_DEFAULT; - -static PRInt32 gWorkingTimeThreshold = UPDATE_WORKING_TIME_DEFAULT; -static PRInt32 gDelayTime = UPDATE_DELAY_TIME_DEFAULT; - static void SplitTables(const nsACString& str, nsTArray& tables) { @@ -216,868 +137,6 @@ SplitTables(const nsACString& str, nsTArray& tables) } } -// ------------------------------------------------------------------------- -// Hash class implementation - -// A convenience wrapper around the potentially-truncated hash for a -// domain or fragment. - -template -struct nsUrlClassifierHash -{ - static const PRUint32 sHashSize = S; - typedef nsUrlClassifierHash self_type; - PRUint8 buf[S]; - - nsresult FromPlaintext(const nsACString& plainText, nsICryptoHash *hash) { - // From the protocol doc: - // Each entry in the chunk is composed of the 128 most significant bits - // of the SHA 256 hash of a suffix/prefix expression. - - nsresult rv = hash->Init(nsICryptoHash::SHA256); - NS_ENSURE_SUCCESS(rv, rv); - - rv = hash->Update - (reinterpret_cast(plainText.BeginReading()), - plainText.Length()); - NS_ENSURE_SUCCESS(rv, rv); - - nsCAutoString hashed; - rv = hash->Finish(false, hashed); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(hashed.Length() >= sHashSize, - "not enough characters in the hash"); - - memcpy(buf, hashed.BeginReading(), sHashSize); - - return NS_OK; - } - - void Assign(const nsACString& str) { - NS_ASSERTION(str.Length() >= sHashSize, - "string must be at least sHashSize characters long"); - memcpy(buf, str.BeginReading(), sHashSize); - } - - void Clear() { - memset(buf, 0, sizeof(buf)); - } - - const bool operator==(const self_type& hash) const { - return (memcmp(buf, hash.buf, sizeof(buf)) == 0); - } - const bool operator!=(const self_type& hash) const { - return !(*this == hash); - } - const bool operator<(const self_type& hash) const { - return memcmp(buf, hash.buf, sizeof(self_type)) < 0; - } - const bool StartsWith(const nsUrlClassifierHash& hash) const { - NS_ASSERTION(sHashSize >= PARTIAL_LENGTH, "nsUrlClassifierHash must be at least PARTIAL_LENGTH bytes long"); - return memcmp(buf, hash.buf, PARTIAL_LENGTH) == 0; - } - PRUint32 ToUint32() const { - return *(reinterpret_cast(buf)); - } -}; - -typedef nsUrlClassifierHash nsUrlClassifierDomainHash; -typedef nsUrlClassifierHash nsUrlClassifierPartialHash; -typedef nsUrlClassifierHash nsUrlClassifierCompleteHash; - - -// ------------------------------------------------------------------------- -// Entry class implementation - -// This class represents one entry in the classifier database. It consists -// of a table id, a chunk id, a domain hash, and a partial or complete hash. -class nsUrlClassifierEntry -{ -public: - nsUrlClassifierEntry() - : mId(-1) - , mHavePartial(false) - , mHaveComplete(false) - , mTableId(0) - , mChunkId(0) - , mAddChunkId(0) - {} - ~nsUrlClassifierEntry() {} - - // Check that this entry could potentially match the complete hash. - bool Match(const nsUrlClassifierCompleteHash &hash); - - // Check that the sub entry should apply to this entry. - bool SubMatch(const nsUrlClassifierEntry& sub); - - // Clear out the entry structure - void Clear(); - - // Set the partial hash for this domain. - void SetHash(const nsUrlClassifierPartialHash &partialHash) { - mPartialHash = partialHash; - mHavePartial = true; - } - - // Set the complete hash for this domain. - void SetHash(const nsUrlClassifierCompleteHash &completeHash) { - mCompleteHash = completeHash; - mHaveComplete = true; - } - - bool operator== (const nsUrlClassifierEntry& entry) const { - return ! (mTableId != entry.mTableId || - mChunkId != entry.mChunkId || - mHavePartial != entry.mHavePartial || - (mHavePartial && mPartialHash != entry.mPartialHash) || - mHaveComplete != entry.mHaveComplete || - (mHaveComplete && mCompleteHash != entry.mCompleteHash)); - } - - bool operator< (const nsUrlClassifierEntry& entry) const { - return (mTableId < entry.mTableId || - mChunkId < entry.mChunkId || - (mHavePartial && !entry.mHavePartial) || - (mHavePartial && mPartialHash < entry.mPartialHash) || - (mHaveComplete && !entry.mHaveComplete) || - (mHaveComplete && mCompleteHash < entry.mCompleteHash)); - } - - PRInt64 mId; - - nsUrlClassifierDomainHash mKey; - - bool mHavePartial; - nsUrlClassifierPartialHash mPartialHash; - - bool mHaveComplete; - nsUrlClassifierCompleteHash mCompleteHash; - - PRUint32 mTableId; - PRUint32 mChunkId; - PRUint32 mAddChunkId; -}; - -bool -nsUrlClassifierEntry::Match(const nsUrlClassifierCompleteHash &hash) -{ - if (mHaveComplete) - return mCompleteHash == hash; - - if (mHavePartial) - return hash.StartsWith(mPartialHash); - - return false; -} - -bool -nsUrlClassifierEntry::SubMatch(const nsUrlClassifierEntry &subEntry) -{ - if ((mTableId != subEntry.mTableId) || (mChunkId != subEntry.mAddChunkId)) - return false; - - if (subEntry.mHaveComplete) - return mHaveComplete && mCompleteHash == subEntry.mCompleteHash; - - if (subEntry.mHavePartial) - return mHavePartial && mPartialHash == subEntry.mPartialHash; - - return false; -} - -void -nsUrlClassifierEntry::Clear() -{ - mId = -1; - mHavePartial = false; - mHaveComplete = false; -} - -// ------------------------------------------------------------------------- -// Lookup result class implementation - -// This helper class wraps a nsUrlClassifierEntry found during a lookup. -class nsUrlClassifierLookupResult -{ -public: - nsUrlClassifierLookupResult() : mConfirmed(false), mNoise(false) { - mLookupFragment.Clear(); - } - ~nsUrlClassifierLookupResult() {} - - bool operator==(const nsUrlClassifierLookupResult &result) const { - // Don't need to compare table name, it's contained by id in the entry. - return (mLookupFragment == result.mLookupFragment && - mConfirmed == result.mConfirmed && - mEntry == result.mEntry); - } - - bool operator<(const nsUrlClassifierLookupResult &result) const { - // Don't need to compare table name, it's contained by id in the entry. - return (mLookupFragment < result.mLookupFragment || - mConfirmed < result.mConfirmed || - mEntry < result.mEntry); - } - - // The hash that matched this entry. - nsUrlClassifierCompleteHash mLookupFragment; - - // The entry that was found during the lookup. - nsUrlClassifierEntry mEntry; - - // TRUE if the lookup matched a complete hash (not just a partial - // one). - bool mConfirmed; - - // TRUE if this lookup is gethash noise. Does not represent an actual - // result. - bool mNoise; - - // The table name associated with mEntry.mTableId. - nsCString mTableName; -}; - -// ------------------------------------------------------------------------- -// Store class implementation - -// This class mediates access to the classifier and chunk entry tables. -class nsUrlClassifierStore -{ -public: - nsUrlClassifierStore() {} - virtual ~nsUrlClassifierStore() {} - - // Initialize the statements for the store. - nsresult Init(nsUrlClassifierDBServiceWorker *worker, - mozIStorageConnection *connection, - const nsACString& entriesTableName); - // Shut down the store. - void Close(); - - // Read an entry from a database statement - virtual bool ReadStatement(mozIStorageStatement* statement, - nsUrlClassifierEntry& entry); - - // Prepare a statement to write this entry to the database - virtual nsresult BindStatement(const nsUrlClassifierEntry& entry, - mozIStorageStatement* statement); - - // Read the entry with a given ID from the database - nsresult ReadEntry(PRInt64 id, nsUrlClassifierEntry& entry, bool *exists); - - // Remove an entry from the database - nsresult DeleteEntry(nsUrlClassifierEntry& entry); - - // Write an entry to the database - nsresult WriteEntry(nsUrlClassifierEntry& entry); - - // Update an entry in the database. The entry must already exist in the - // database or this method will fail. - nsresult UpdateEntry(nsUrlClassifierEntry& entry); - - // Remove all entries for a given table/chunk pair from the database. - nsresult Expire(PRUint32 tableId, - PRUint32 chunkNum); - - // Read a certain number of rows adjacent to the requested rowid that - // don't have complete hash data. - nsresult ReadNoiseEntries(PRInt64 rowID, - PRUint32 numRequested, - bool before, - nsTArray &entries); - - // Ask the db for a random number. This is temporary, and should be - // replaced with nsIRandomGenerator when 419739 is fixed. - nsresult RandomNumber(PRInt64 *randomNum); - // Return an array with all Prefixes known - nsresult ReadPrefixes(FallibleTArray& array, PRUint32 aKey); - - -protected: - nsresult ReadEntries(mozIStorageStatement *statement, - nsTArray& entries); - nsUrlClassifierDBServiceWorker *mWorker; - nsCOMPtr mConnection; - - nsCOMPtr mLookupWithIDStatement; - - nsCOMPtr mInsertStatement; - nsCOMPtr mUpdateStatement; - nsCOMPtr mDeleteStatement; - nsCOMPtr mExpireStatement; - - nsCOMPtr mPartialEntriesStatement; - nsCOMPtr mPartialEntriesAfterStatement; - nsCOMPtr mLastPartialEntriesStatement; - nsCOMPtr mPartialEntriesBeforeStatement; - - nsCOMPtr mRandomStatement; - nsCOMPtr mAllPrefixGetStatement; - nsCOMPtr mAllPrefixCountStatement; -}; - -nsresult -nsUrlClassifierStore::Init(nsUrlClassifierDBServiceWorker *worker, - mozIStorageConnection *connection, - const nsACString& entriesName) -{ - mWorker = worker; - mConnection = connection; - - nsresult rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesName + - NS_LITERAL_CSTRING(" WHERE id=?1"), - getter_AddRefs(mLookupWithIDStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("DELETE FROM ") + entriesName + - NS_LITERAL_CSTRING(" WHERE id=?1"), - getter_AddRefs(mDeleteStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("DELETE FROM ") + entriesName + - NS_LITERAL_CSTRING(" WHERE table_id=?1 AND chunk_id=?2"), - getter_AddRefs(mExpireStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesName + - NS_LITERAL_CSTRING(" WHERE complete_data ISNULL" - " LIMIT ?1"), - getter_AddRefs(mPartialEntriesStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesName + - NS_LITERAL_CSTRING(" WHERE id > ?1 AND complete_data ISNULL" - " LIMIT ?2"), - getter_AddRefs(mPartialEntriesAfterStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesName + - NS_LITERAL_CSTRING(" WHERE complete_data ISNULL" - " ORDER BY id DESC LIMIT ?1"), - getter_AddRefs(mLastPartialEntriesStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesName + - NS_LITERAL_CSTRING(" WHERE id < ?1 AND complete_data ISNULL" - " ORDER BY id DESC LIMIT ?2"), - getter_AddRefs(mPartialEntriesBeforeStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("SELECT abs(random())"), - getter_AddRefs(mRandomStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement(NS_LITERAL_CSTRING("SELECT domain, partial_data, complete_data FROM ") - + entriesName, - getter_AddRefs(mAllPrefixGetStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement(NS_LITERAL_CSTRING("SELECT COUNT(1) FROM ") - + entriesName, - getter_AddRefs(mAllPrefixCountStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -void -nsUrlClassifierStore::Close() -{ - mLookupWithIDStatement = nsnull; - - mInsertStatement = nsnull; - mUpdateStatement = nsnull; - mDeleteStatement = nsnull; - mExpireStatement = nsnull; - - mPartialEntriesStatement = nsnull; - mPartialEntriesAfterStatement = nsnull; - mPartialEntriesBeforeStatement = nsnull; - mLastPartialEntriesStatement = nsnull; - mRandomStatement = nsnull; - - mAllPrefixGetStatement = nsnull; - mAllPrefixCountStatement = nsnull; - - mConnection = nsnull; -} - - -bool -nsUrlClassifierStore::ReadStatement(mozIStorageStatement* statement, - nsUrlClassifierEntry& entry) -{ - entry.mId = statement->AsInt64(0); - - PRUint32 size; - const PRUint8* blob = statement->AsSharedBlob(1, &size); - if (!blob || (size != DOMAIN_LENGTH)) - return false; - memcpy(entry.mKey.buf, blob, DOMAIN_LENGTH); - - blob = statement->AsSharedBlob(2, &size); - if (!blob || size == 0) { - entry.mHavePartial = false; - } else { - if (size != PARTIAL_LENGTH) - return false; - entry.mHavePartial = true; - memcpy(entry.mPartialHash.buf, blob, PARTIAL_LENGTH); - } - - blob = statement->AsSharedBlob(3, &size); - if (!blob || size == 0) { - entry.mHaveComplete = false; - } else { - if (size != COMPLETE_LENGTH) - return false; - entry.mHaveComplete = true; - memcpy(entry.mCompleteHash.buf, blob, COMPLETE_LENGTH); - } - - // If we only have a partial entry, and that partial entry matches the - // domain, we don't save the extra copy to the database. - if (!(entry.mHavePartial || entry.mHaveComplete)) { - entry.SetHash(entry.mKey); - } - - entry.mChunkId = statement->AsInt32(4); - entry.mTableId = statement->AsInt32(5); - - return true; -} - -nsresult -nsUrlClassifierStore::BindStatement(const nsUrlClassifierEntry &entry, - mozIStorageStatement* statement) -{ - nsresult rv; - - if (entry.mId == -1) - rv = statement->BindNullByIndex(0); - else - rv = statement->BindInt64ByIndex(0, entry.mId); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindBlobByIndex(1, entry.mKey.buf, DOMAIN_LENGTH); - NS_ENSURE_SUCCESS(rv, rv); - - if (entry.mHavePartial) { - // If we only have a partial entry and that entry matches the domain, - // we'll save some space by only storing the domain hash. - if (!entry.mHaveComplete && entry.mKey == entry.mPartialHash) { - rv = statement->BindNullByIndex(2); - } else { - rv = statement->BindBlobByIndex(2, entry.mPartialHash.buf, - PARTIAL_LENGTH); - } - } else { - rv = statement->BindNullByIndex(2); - } - NS_ENSURE_SUCCESS(rv, rv); - - if (entry.mHaveComplete) { - rv = statement->BindBlobByIndex(3, entry.mCompleteHash.buf, COMPLETE_LENGTH); - } else { - rv = statement->BindNullByIndex(3); - } - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindInt32ByIndex(4, entry.mChunkId); - NS_ENSURE_SUCCESS(rv, rv); - - rv = statement->BindInt32ByIndex(5, entry.mTableId); - NS_ENSURE_SUCCESS(rv, rv); - - return true; -} - -nsresult -nsUrlClassifierStore::ReadEntries(mozIStorageStatement *statement, - nsTArray& entries) -{ - bool exists; - nsresult rv = statement->ExecuteStep(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - while (exists) { - nsUrlClassifierEntry *entry = entries.AppendElement(); - if (!entry) { - return NS_ERROR_OUT_OF_MEMORY; - } - - if (!ReadStatement(statement, *entry)) - return NS_ERROR_FAILURE; - - statement->ExecuteStep(&exists); - } - - return NS_OK; -} - -nsresult -nsUrlClassifierStore::ReadEntry(PRInt64 id, - nsUrlClassifierEntry& entry, - bool *exists) -{ - entry.Clear(); - - mozStorageStatementScoper scoper(mLookupWithIDStatement); - - nsresult rv = mLookupWithIDStatement->BindInt64ByIndex(0, id); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mLookupWithIDStatement->ExecuteStep(exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (*exists) { - if (ReadStatement(mLookupWithIDStatement, entry)) - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - -nsresult -nsUrlClassifierStore::ReadNoiseEntries(PRInt64 rowID, - PRUint32 numRequested, - bool before, - nsTArray &entries) -{ - if (numRequested == 0) { - return NS_OK; - } - - mozIStorageStatement *statement = - before ? mPartialEntriesBeforeStatement : mPartialEntriesAfterStatement; - mozStorageStatementScoper scoper(statement); - - nsresult rv = statement->BindInt64ByIndex(0, rowID); - NS_ENSURE_SUCCESS(rv, rv); - - statement->BindInt32ByIndex(1, numRequested); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 length = entries.Length(); - rv = ReadEntries(statement, entries); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 numRead = entries.Length() - length; - - if (numRead >= numRequested) - return NS_OK; - - // If we didn't get enough entries, we need the search to wrap around from - // beginning to end (or vice-versa) - - mozIStorageStatement *wraparoundStatement = - before ? mPartialEntriesStatement : mLastPartialEntriesStatement; - mozStorageStatementScoper wraparoundScoper(wraparoundStatement); - - rv = wraparoundStatement->BindInt32ByIndex(0, numRequested - numRead); - NS_ENSURE_SUCCESS(rv, rv); - - return ReadEntries(wraparoundStatement, entries); -} - -nsresult -nsUrlClassifierStore::RandomNumber(PRInt64 *randomNum) -{ - mozStorageStatementScoper randScoper(mRandomStatement); - bool exists; - nsresult rv = mRandomStatement->ExecuteStep(&exists); - NS_ENSURE_SUCCESS(rv, rv); - if (!exists) - return NS_ERROR_NOT_AVAILABLE; - - *randomNum = mRandomStatement->AsInt64(0); - - return NS_OK; -} - -// ------------------------------------------------------------------------- -// nsUrlClassifierAddStore class implementation - -// This class accesses the moz_classifier table. -class nsUrlClassifierAddStore: public nsUrlClassifierStore -{ -public: - nsUrlClassifierAddStore() {}; - virtual ~nsUrlClassifierAddStore() {}; - - nsresult Init(nsUrlClassifierDBServiceWorker *worker, - mozIStorageConnection *connection, - const nsACString& entriesTableName); - - void Close(); - - // Read the entries for a given key/table/chunk from the database - nsresult ReadAddEntries(const nsUrlClassifierDomainHash& key, - PRUint32 tableId, - PRUint32 chunkId, - nsTArray& entry); - - // Read the entries for a given host key from the database. - nsresult ReadAddEntries(const nsUrlClassifierDomainHash& key, - nsTArray& entry); - -protected: - nsCOMPtr mLookupStatement; - nsCOMPtr mLookupWithChunkStatement; -}; - -nsresult -nsUrlClassifierAddStore::Init(nsUrlClassifierDBServiceWorker *worker, - mozIStorageConnection *connection, - const nsACString &entriesTableName) -{ - nsresult rv = nsUrlClassifierStore::Init(worker, connection, - entriesTableName); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("INSERT OR REPLACE INTO ") + entriesTableName + - NS_LITERAL_CSTRING(" VALUES (?1, ?2, ?3, ?4, ?5, ?6)"), - getter_AddRefs(mInsertStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("UPDATE ") + entriesTableName + - NS_LITERAL_CSTRING(" SET domain=?2, partial_data=?3, " - " complete_data=?4, chunk_id=?5, table_id=?6" - " WHERE id=?1"), - getter_AddRefs(mUpdateStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesTableName + - NS_LITERAL_CSTRING(" WHERE domain=?1"), - getter_AddRefs(mLookupStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesTableName + - NS_LITERAL_CSTRING(" WHERE domain=?1 AND table_id=?2 AND chunk_id=?3"), - getter_AddRefs(mLookupWithChunkStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -void -nsUrlClassifierAddStore::Close() -{ - nsUrlClassifierStore::Close(); - - mLookupStatement = nsnull; - mLookupWithChunkStatement = nsnull; -} - -nsresult -nsUrlClassifierAddStore::ReadAddEntries(const nsUrlClassifierDomainHash& hash, - PRUint32 tableId, - PRUint32 chunkId, - nsTArray& entries) -{ - mozStorageStatementScoper scoper(mLookupWithChunkStatement); - - nsresult rv = mLookupWithChunkStatement->BindBlobByIndex - (0, hash.buf, DOMAIN_LENGTH); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mLookupWithChunkStatement->BindInt32ByIndex(1, tableId); - NS_ENSURE_SUCCESS(rv, rv); - rv = mLookupWithChunkStatement->BindInt32ByIndex(2, chunkId); - NS_ENSURE_SUCCESS(rv, rv); - - return ReadEntries(mLookupWithChunkStatement, entries); -} - -nsresult -nsUrlClassifierAddStore::ReadAddEntries(const nsUrlClassifierDomainHash& hash, - nsTArray& entries) -{ - mozStorageStatementScoper scoper(mLookupStatement); - - nsresult rv = mLookupStatement->BindBlobByIndex - (0, hash.buf, DOMAIN_LENGTH); - NS_ENSURE_SUCCESS(rv, rv); - - return ReadEntries(mLookupStatement, entries); -} - -// ------------------------------------------------------------------------- -// nsUrlClassifierSubStore class implementation - -// This class accesses the moz_subs table. -class nsUrlClassifierSubStore : public nsUrlClassifierStore -{ -public: - nsUrlClassifierSubStore() {}; - virtual ~nsUrlClassifierSubStore() {}; - - nsresult Init(nsUrlClassifierDBServiceWorker *worker, - mozIStorageConnection *connection, - const nsACString& entriesTableName); - - void Close(); - - // Read an entry from a database statement - virtual bool ReadStatement(mozIStorageStatement* statement, - nsUrlClassifierEntry& entry); - - // Prepare a statement to write this entry to the database - virtual nsresult BindStatement(const nsUrlClassifierEntry& entry, - mozIStorageStatement* statement); - - // Read sub entries for a given add chunk - nsresult ReadSubEntries(PRUint32 tableId, PRUint32 chunkId, - nsTArray &subEntry); - - // Expire sub entries for a given add chunk - nsresult ExpireAddChunk(PRUint32 tableId, PRUint32 chunkId); - -protected: - nsCOMPtr mLookupWithAddChunkStatement; - nsCOMPtr mExpireAddChunkStatement; -}; - -nsresult -nsUrlClassifierSubStore::Init(nsUrlClassifierDBServiceWorker *worker, - mozIStorageConnection *connection, - const nsACString &entriesTableName) -{ - nsresult rv = nsUrlClassifierStore::Init(worker, connection, - entriesTableName); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("INSERT OR REPLACE INTO ") + entriesTableName + - NS_LITERAL_CSTRING(" VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), - getter_AddRefs(mInsertStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("UPDATE ") + entriesTableName + - NS_LITERAL_CSTRING(" SET domain=?2, partial_data=?3, complete_data=?4," - " chunk_id=?5, table_id=?6, add_chunk_id=?7" - " WHERE id=?1"), - getter_AddRefs(mUpdateStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesTableName + - NS_LITERAL_CSTRING(" WHERE table_id=?1 AND add_chunk_id=?2"), - getter_AddRefs(mLookupWithAddChunkStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mConnection->CreateStatement - (NS_LITERAL_CSTRING("DELETE FROM ") + entriesTableName + - NS_LITERAL_CSTRING(" WHERE table_id=?1 AND add_chunk_id=?2"), - getter_AddRefs(mExpireAddChunkStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -bool -nsUrlClassifierSubStore::ReadStatement(mozIStorageStatement* statement, - nsUrlClassifierEntry& entry) -{ - if (!nsUrlClassifierStore::ReadStatement(statement, entry)) - return false; - - entry.mAddChunkId = statement->AsInt32(6); - return true; -} - -nsresult -nsUrlClassifierSubStore::BindStatement(const nsUrlClassifierEntry& entry, - mozIStorageStatement* statement) -{ - nsresult rv = nsUrlClassifierStore::BindStatement(entry, statement); - NS_ENSURE_SUCCESS(rv, rv); - - return statement->BindInt32ByIndex(6, entry.mAddChunkId); -} - -nsresult -nsUrlClassifierSubStore::ReadSubEntries(PRUint32 tableId, PRUint32 addChunkId, - nsTArray& entries) -{ - mozStorageStatementScoper scoper(mLookupWithAddChunkStatement); - - nsresult rv = mLookupWithAddChunkStatement->BindInt32ByIndex(0, tableId); - NS_ENSURE_SUCCESS(rv, rv); - rv = mLookupWithAddChunkStatement->BindInt32ByIndex(1, addChunkId); - NS_ENSURE_SUCCESS(rv, rv); - - return ReadEntries(mLookupWithAddChunkStatement, entries); -} - -nsresult -nsUrlClassifierSubStore::ExpireAddChunk(PRUint32 tableId, PRUint32 addChunkId) -{ - mozStorageStatementScoper scoper(mExpireAddChunkStatement); - - nsresult rv = mExpireAddChunkStatement->BindInt32ByIndex(0, tableId); - NS_ENSURE_SUCCESS(rv, rv); - rv = mExpireAddChunkStatement->BindInt32ByIndex(1, addChunkId); - NS_ENSURE_SUCCESS(rv, rv); - - return mExpireAddChunkStatement->Execute(); -} - -void -nsUrlClassifierSubStore::Close() -{ - nsUrlClassifierStore::Close(); - mLookupWithAddChunkStatement = nsnull; - mExpireAddChunkStatement = nsnull; -} - -// Similar to GetKey(), but if the domain contains three or more components, -// two keys will be returned: -// hostname.com/foo/bar -> [hostname.com] -// mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com] -// www.mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com] -static nsresult GetHostKeys(const nsACString &spec, - nsTArray &hostKeys); - -// take a lookup string (www.hostname.com/path/to/resource.html) and -// expand it into the set of fragments that should be searched for in an -// entry -static nsresult GetLookupFragments(const nsCSubstring& spec, - nsTArray& fragments); - -// Check for a canonicalized IP address. -static bool IsCanonicalizedIP(const nsACString& host); - -// Get the database key for a given URI. This is the top three -// domain components if they exist, otherwise the top two. -// hostname.com/foo/bar -> hostname.com -// mail.hostname.com/foo/bar -> mail.hostname.com -// www.mail.hostname.com/foo/bar -> mail.hostname.com -static nsresult GetKey(const nsACString& spec, nsUrlClassifierDomainHash& hash, - nsICryptoHash * aCryptoHash); - -// We have both a prefix and a domain. Drop the domain, but -// hash the domain, the prefix and a random value together, -// ensuring any collisions happens at a different points for -// different users. -static nsresult KeyedHash(PRUint32 aPref, PRUint32 aDomain, - PRUint32 aKey, PRUint32 *aOut); - - // ------------------------------------------------------------------------- // Actual worker implemenatation class nsUrlClassifierDBServiceWorker : public nsIUrlClassifierDBServiceWorker @@ -1090,8 +149,7 @@ public: NS_DECL_NSIURLCLASSIFIERDBSERVICEWORKER // Initialize, called in the main thread - nsresult Init(PRInt32 gethashNoise, - nsRefPtr & prefSet); + nsresult Init(PRInt32 gethashNoise, nsCOMPtr aCacheDir); // Queue a lookup for the worker to perform, called in the main thread. nsresult QueueLookup(const nsACString& lookupKey, @@ -1101,12 +159,6 @@ public: // update operations to prevent lookups from blocking for too long. nsresult HandlePendingLookups(); - // Blocks the PrefixSet from being updated while the main thread is doing - // its lookups. LockPrefixSet will return whether the PrefixSet is in a - // usable state. If not, we should fall through to SQLite lookups. - bool LockPrefixSet(); - void UnlockPrefixSet(); - private: // No subclassing ~nsUrlClassifierDBServiceWorker(); @@ -1114,231 +166,66 @@ private: // Disallow copy constructor nsUrlClassifierDBServiceWorker(nsUrlClassifierDBServiceWorker&); - // Try to open the db, DATABASE_FILENAME. nsresult OpenDb(); - // Create table in the db if they don't exist. - nsresult MaybeCreateTables(mozIStorageConnection* connection); - - nsresult GetTableName(PRUint32 tableId, nsACString& table); - nsresult GetTableId(const nsACString& table, PRUint32* tableId); - - // Decompress a zlib'ed chunk (used for -exp tables) - nsresult InflateChunk(nsACString& chunk); - - // Expand shavar chunk into its individual entries - nsresult GetShaEntries(PRUint32 tableId, - PRUint32 chunkType, - PRUint32 chunkNum, - PRUint32 domainSize, - PRUint32 hashSize, - nsACString& chunk, - nsTArray& entries); - - // Expand a chunk into its individual entries - nsresult GetChunkEntries(const nsACString& table, - PRUint32 tableId, - PRUint32 chunkType, - PRUint32 chunkNum, - PRUint32 hashSize, - nsACString& chunk, - nsTArray& entries); - - // Parse one stringified range of chunks of the form "n" or "n-m" from a - // comma-separated list of chunks. Upon return, 'begin' will point to the - // next range of chunks in the list of chunks. - bool ParseChunkRange(nsACString::const_iterator &begin, - const nsACString::const_iterator &end, - PRUint32 *first, PRUint32 *last); - - // Expand a stringified chunk list into an array of ints. - nsresult ParseChunkList(const nsACString& chunkStr, - nsTArray& chunks); - - // Join an array of ints into a stringified chunk list. - nsresult JoinChunkList(nsTArray& chunks, nsCString& chunkStr); - - // List the add/subtract chunks that have been applied to a table - nsresult GetChunkLists(PRUint32 tableId, - nsACString& addChunks, - nsACString& subChunks); - - // Set the list of add/subtract chunks that have been applied to a table - nsresult SetChunkLists(PRUint32 tableId, - const nsACString& addChunks, - const nsACString& subChunks); - - // Cache the list of add/subtract chunks applied to the table, optionally - // parsing the add or sub lists. These lists are cached while updating - // tables to avoid excessive database reads/writes and parsing. - nsresult CacheChunkLists(PRUint32 tableId, - bool parseAdds, - bool parseSubs); - - // Clear the cached list of add/subtract chunks. - void ClearCachedChunkLists(); - - // Flush the cached add/subtract lists to the database. - nsresult FlushChunkLists(); - - // Inserts a chunk id into the list, sorted. Returns TRUE if the - // number was successfully added, FALSE if the chunk already exists. - bool InsertChunkId(nsTArray& chunks, PRUint32 chunkNum); - - // Add a list of entries to the database, merging with - // existing entries as necessary - nsresult AddChunk(PRUint32 tableId, PRUint32 chunkNum, - nsTArray& entries); - - // Expire an add chunk - nsresult ExpireAdd(PRUint32 tableId, PRUint32 chunkNum); - - // Subtract a list of entries from the database - nsresult SubChunk(PRUint32 tableId, PRUint32 chunkNum, - nsTArray& entries); - - // Expire a subtract chunk - nsresult ExpireSub(PRUint32 tableId, PRUint32 chunkNum); - - // Handle line-oriented control information from a stream update - nsresult ProcessResponseLines(bool* done); - // Handle chunk data from a stream update - nsresult ProcessChunk(bool* done); - - // Sets up a transaction and begins counting update time. - nsresult SetupUpdate(); - // Applies the current transaction and resets the update/working times. nsresult ApplyUpdate(); // Reset the in-progress update stream void ResetStream(); - // Reset the in-progress update - void ResetUpdate(); - - // Look for a given lookup string (www.hostname.com/path/to/resource.html) - // Returns a list of entries that match. - nsresult Check(const nsCSubstring& spec, - nsTArray& results); + // Reset the in-progress update + void ResetUpdate(); // Perform a classifier lookup for a given url. nsresult DoLookup(const nsACString& spec, nsIUrlClassifierLookupCallback* c); // Add entries to the results. - nsresult AddNoise(PRInt64 nearID, - PRInt32 count, - nsTArray& results); - - // Construct a Prefix Set with known prefixes - nsresult LoadPrefixSet(nsCOMPtr & aFile); - nsresult ConstructPrefixSet(); - - // Set the SQLite cache size - nsresult SetCacheSize(mozIStorageConnection * aConnection, - PRInt32 aCacheSize); - - nsCOMPtr mDBFile; - nsCOMPtr mPSFile; + nsresult AddNoise(const Prefix aPrefix, + const nsCString tableName, + PRInt32 aCount, + LookupResultArray& results); nsCOMPtr mCryptoHash; - // Holds a connection to the Db. We lazily initialize this because it has - // to be created in the background thread (currently mozStorageConnection - // isn't thread safe). - nsCOMPtr mConnection; + nsAutoPtr mClassifier; + nsAutoPtr mProtocolParser; - // The main collection of entries. This is the store that will be checked - // when classifying a URL. - nsUrlClassifierAddStore mMainStore; + // Directory where to store the SB databases. + nsCOMPtr mCacheDir; - // The collection of subs waiting for their accompanying add. - nsUrlClassifierSubStore mPendingSubStore; - - nsCOMPtr mGetChunkListsStatement; - nsCOMPtr mSetChunkListsStatement; - - nsCOMPtr mGetTablesStatement; - nsCOMPtr mGetTableIdStatement; - nsCOMPtr mGetTableNameStatement; - nsCOMPtr mInsertTableIdStatement; - nsCOMPtr mGetPageSizeStatement; - - // Stores the last time a given table was updated. - nsDataHashtable mTableFreshness; - - // We receive data in small chunks that may be broken in the middle of - // a line. So we save the last partial line here. - nsCString mPendingStreamUpdate; + // XXX: maybe an array of autoptrs. Or maybe a class specifically + // storing a series of updates. + nsTArray mTableUpdates; PRInt32 mUpdateWait; - bool mResetRequested; - bool mGrewCache; - - enum { - STATE_LINE, - STATE_CHUNK - } mState; - - enum { - CHUNK_ADD, - CHUNK_SUB - } mChunkType; - - PRUint32 mChunkNum; - PRUint32 mHashSize; - PRUint32 mChunkLen; - - // List of tables included in this update. - nsTArray mUpdateTables; - - nsCString mUpdateTable; - PRUint32 mUpdateTableId; + // Entries that cannot be completed. We expect them to die at + // the next update + PrefixArray mMissCache; nsresult mUpdateStatus; + nsTArray mUpdateTables; nsCOMPtr mUpdateObserver; bool mInStream; - bool mPrimaryStream; - - bool mHaveCachedLists; - PRUint32 mCachedListsTable; - nsCAutoString mCachedSubsStr; - nsCAutoString mCachedAddsStr; - - bool mHaveCachedAddChunks; - nsTArray mCachedAddChunks; - - bool mHaveCachedSubChunks; - nsTArray mCachedSubChunks; // The client key with which the data from the server will be MAC'ed. nsCString mUpdateClientKey; - // The MAC stated by the server. - nsCString mServerMAC; + // The client-specific hash key to rehash + PRUint32 mHashKey; - // Start time of the current update interval. This will be reset - // every time we apply the update. - PRIntervalTime mUpdateStartTime; - - nsCOMPtr mHMAC; // The number of noise entries to add to the set of lookup results. PRInt32 mGethashNoise; - // Set of prefixes known to be in the database - nsRefPtr mPrefixSet; - // Can we use the PrefixSet (low memory conditions) - bool mPrefixSetEnabled; - Mutex mPrefixSetEnabledLock; - // Pending lookups are stored in a queue for processing. The queue // is protected by mPendingLookupLock. Mutex mPendingLookupLock; class PendingLookup { public: + TimeStamp mStartTime; nsCString mKey; nsCOMPtr mCallback; }; @@ -1352,72 +239,28 @@ NS_IMPL_THREADSAFE_ISUPPORTS2(nsUrlClassifierDBServiceWorker, nsIUrlClassifierDBService) nsUrlClassifierDBServiceWorker::nsUrlClassifierDBServiceWorker() - : mUpdateWait(0) - , mResetRequested(false) - , mGrewCache(false) - , mState(STATE_LINE) - , mChunkType(CHUNK_ADD) - , mChunkNum(0) - , mHashSize(0) - , mChunkLen(0) - , mUpdateTableId(0) - , mUpdateStatus(NS_OK) - , mInStream(false) - , mPrimaryStream(false) - , mHaveCachedLists(false) - , mCachedListsTable(PR_UINT32_MAX) - , mHaveCachedAddChunks(false) - , mHaveCachedSubChunks(false) - , mUpdateStartTime(0) +: mInStream(false) , mGethashNoise(0) - , mPrefixSet(0) - , mPrefixSetEnabled(true) - , mPrefixSetEnabledLock("mPrefixSetEnabledLock") , mPendingLookupLock("nsUrlClassifierDBServerWorker.mPendingLookupLock") { } nsUrlClassifierDBServiceWorker::~nsUrlClassifierDBServiceWorker() { - NS_ASSERTION(!mConnection, + NS_ASSERTION(!mClassifier, "Db connection not closed, leaking memory! Call CloseDb " "to close the connection."); } nsresult nsUrlClassifierDBServiceWorker::Init(PRInt32 gethashNoise, - nsRefPtr & prefSet) + nsCOMPtr aCacheDir) { mGethashNoise = gethashNoise; - mPrefixSet = prefSet; - - // Compute database filename - - // Because we dump raw integers into the database, this database isn't - // portable between machine types, so store it in the local profile dir. - nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, - getter_AddRefs(mDBFile)); - - if (NS_FAILED(rv)) { - rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, - getter_AddRefs(mDBFile)); - } - - if (NS_FAILED(rv)) return NS_ERROR_NOT_AVAILABLE; - - rv = mDBFile->Clone(getter_AddRefs(mPSFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBFile->Append(NS_LITERAL_STRING(DATABASE_FILENAME)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mPSFile->Append(NS_LITERAL_STRING(PREFIXSET_FILENAME)); - NS_ENSURE_SUCCESS(rv, rv); + mCacheDir = aCacheDir; ResetUpdate(); - mTableFreshness.Init(); - return NS_OK; } @@ -1430,318 +273,13 @@ nsUrlClassifierDBServiceWorker::QueueLookup(const nsACString& spec, PendingLookup* lookup = mPendingLookups.AppendElement(); if (!lookup) return NS_ERROR_OUT_OF_MEMORY; + lookup->mStartTime = TimeStamp::Now(); lookup->mKey = spec; lookup->mCallback = callback; return NS_OK; } -nsresult -nsUrlClassifierDBService::CheckClean(const nsACString &spec, - bool *clean) -{ - Telemetry::AutoTimer timer; - - // Is the PrefixSet usable? - bool usePrefixSet = mWorker->LockPrefixSet(); - - // No, bail out and pretend the URL is not clean. We will do - // a database lookup and get the correct result. - if (!usePrefixSet) { - mWorker->UnlockPrefixSet(); - *clean = false; - return NS_OK; - } - - // Get the set of fragments to look up. - nsTArray fragments; - nsresult rv = GetLookupFragments(spec, fragments); - if (NS_FAILED(rv)) { - goto error_checkclean; - } - - PRUint32 prefixkey; - rv = mPrefixSet->GetKey(&prefixkey); - if (NS_FAILED(rv)) { - goto error_checkclean; - } - - *clean = true; - - for (PRUint32 i = 0; i < fragments.Length(); i++) { - nsUrlClassifierDomainHash fragmentKeyHash; - fragmentKeyHash.FromPlaintext(fragments[i], mHash); - - // Find the corresponding host key - nsUrlClassifierDomainHash hostkey; - rv = GetKey(fragments[i], hostkey, mHash); - if (NS_FAILED(rv)) { - /* This happens for hosts on the local network, - can't check these against the DB */ - continue; - } - - PRUint32 hostprefix = hostkey.ToUint32(); - PRUint32 fragkey = fragmentKeyHash.ToUint32(); - PRUint32 codedkey; - rv = KeyedHash(fragkey, hostprefix, prefixkey, &codedkey); - if (NS_FAILED(rv)) { - goto error_checkclean; - } - - bool found = false; - bool ready = false; /* opportunistic probe */ - rv = mPrefixSet->Probe(codedkey, prefixkey, &ready, &found); - if (NS_FAILED(rv)) { - goto error_checkclean; - } - LOG(("CheckClean Probed %X ready: %d found: %d ", - codedkey, ready, found)); - if (found || !ready) { - *clean = false; - } - } - - mWorker->UnlockPrefixSet(); - return NS_OK; - - error_checkclean: - mWorker->UnlockPrefixSet(); - return rv; -} - -static nsresult GetHostKeys(const nsACString &spec, - nsTArray &hostKeys) -{ - nsACString::const_iterator begin, end, iter; - spec.BeginReading(begin); - spec.EndReading(end); - - iter = begin; - if (!FindCharInReadable('/', iter, end)) { - return NS_OK; - } - - const nsCSubstring& host = Substring(begin, iter); - - if (IsCanonicalizedIP(host)) { - nsCString *key = hostKeys.AppendElement(); - if (!key) - return NS_ERROR_OUT_OF_MEMORY; - - key->Assign(host); - key->Append("/"); - return NS_OK; - } - - nsTArray hostComponents; - ParseString(PromiseFlatCString(host), '.', hostComponents); - - if (hostComponents.Length() < 2) { - // no host or toplevel host, this won't match anything in the db - return NS_OK; - } - - // First check with two domain components - PRInt32 last = PRInt32(hostComponents.Length()) - 1; - nsCString *lookupHost = hostKeys.AppendElement(); - if (!lookupHost) - return NS_ERROR_OUT_OF_MEMORY; - - lookupHost->Assign(hostComponents[last - 1]); - lookupHost->Append("."); - lookupHost->Append(hostComponents[last]); - lookupHost->Append("/"); - - // Now check with three domain components - if (hostComponents.Length() > 2) { - nsCString *lookupHost2 = hostKeys.AppendElement(); - if (!lookupHost2) - return NS_ERROR_OUT_OF_MEMORY; - lookupHost2->Assign(hostComponents[last - 2]); - lookupHost2->Append("."); - lookupHost2->Append(*lookupHost); - } - - return NS_OK; -} - -nsresult -GetLookupFragments(const nsACString& spec, - nsTArray& fragments) -{ - fragments.Clear(); - - nsACString::const_iterator begin, end, iter; - spec.BeginReading(begin); - spec.EndReading(end); - - iter = begin; - if (!FindCharInReadable('/', iter, end)) { - return NS_OK; - } - - const nsCSubstring& host = Substring(begin, iter++); - nsCAutoString path; - path.Assign(Substring(iter, end)); - - /** - * From the protocol doc: - * For the hostname, the client will try at most 5 different strings. They - * are: - * a) The exact hostname of the url - * b) The 4 hostnames formed by starting with the last 5 components and - * successivly removing the leading component. The top-level component - * can be skipped. This is not done if the hostname is a numerical IP. - */ - nsTArray hosts; - hosts.AppendElement(host); - - if (!IsCanonicalizedIP(host)) { - host.BeginReading(begin); - host.EndReading(end); - int numHostComponents = 0; - while (RFindInReadable(NS_LITERAL_CSTRING("."), begin, end) && - numHostComponents < MAX_HOST_COMPONENTS) { - // don't bother checking toplevel domains - if (++numHostComponents >= 2) { - host.EndReading(iter); - hosts.AppendElement(Substring(end, iter)); - } - end = begin; - host.BeginReading(begin); - } - } - - /** - * From the protocol doc: - * For the path, the client will also try at most 6 different strings. - * They are: - * a) the exact path of the url, including query parameters - * b) the exact path of the url, without query parameters - * c) the 4 paths formed by starting at the root (/) and - * successively appending path components, including a trailing - * slash. This behavior should only extend up to the next-to-last - * path component, that is, a trailing slash should never be - * appended that was not present in the original url. - */ - nsTArray paths; - nsCAutoString pathToAdd; - - path.BeginReading(begin); - path.EndReading(end); - iter = begin; - if (FindCharInReadable('?', iter, end)) { - pathToAdd = Substring(begin, iter); - paths.AppendElement(pathToAdd); - end = iter; - } - - int numPathComponents = 1; - iter = begin; - while (FindCharInReadable('/', iter, end) && - numPathComponents < MAX_PATH_COMPONENTS) { - iter++; - pathToAdd.Assign(Substring(begin, iter)); - paths.AppendElement(pathToAdd); - numPathComponents++; - } - - // If we haven't already done so, add the full path - if (!pathToAdd.Equals(path)) { - paths.AppendElement(path); - } - // Check an empty path (for whole-domain blacklist entries) - paths.AppendElement(EmptyCString()); - - for (PRUint32 hostIndex = 0; hostIndex < hosts.Length(); hostIndex++) { - for (PRUint32 pathIndex = 0; pathIndex < paths.Length(); pathIndex++) { - nsCString key; - key.Assign(hosts[hostIndex]); - key.Append('/'); - key.Append(paths[pathIndex]); - LOG(("Chking %s", key.get())); - - fragments.AppendElement(key); - } - } - - return NS_OK; -} - -nsresult -nsUrlClassifierDBServiceWorker::Check(const nsACString& spec, - nsTArray& results) -{ - PRInt64 now = (PR_Now() / PR_USEC_PER_SEC); - - // Get list of host keys to look up - nsAutoTArray lookupHosts; - nsresult rv = GetHostKeys(spec, lookupHosts); - - nsTArray mCachedEntries; - - // Gather host's prefixes - for (PRUint32 i = 0; i < lookupHosts.Length(); i++) { - // Find the corresponding host key - nsUrlClassifierDomainHash hostKey; - nsresult rv = GetKey(lookupHosts[i], hostKey, mCryptoHash); - NS_ENSURE_SUCCESS(rv, rv); - // Read the entries for this fragments host from SQLite - mMainStore.ReadAddEntries(hostKey, mCachedEntries); - } - - // Now get the set of fragments to look up. - nsTArray fragments; - rv = GetLookupFragments(spec, fragments); - NS_ENSURE_SUCCESS(rv, rv); - - // Now check each lookup fragment against the entries in the DB. - for (PRUint32 i = 0; i < fragments.Length(); i++) { - nsUrlClassifierCompleteHash lookupHash; - lookupHash.FromPlaintext(fragments[i], mCryptoHash); - - for (PRUint32 j = 0; j < mCachedEntries.Length(); j++) { - nsUrlClassifierEntry &entry = mCachedEntries[j]; - if (entry.Match(lookupHash)) { - // If the entry doesn't contain a complete hash, we need to - // save it here so that it can be compared against the - // complete hash. However, we don't set entry.mHaveComplete - // because it isn't a verified part of the entry yet. - nsUrlClassifierLookupResult *result = results.AppendElement(); - if (!result) - return NS_ERROR_OUT_OF_MEMORY; - - result->mLookupFragment = lookupHash; - result->mEntry = entry; - - // Fill in the table name. - GetTableName(entry.mTableId, result->mTableName); - - bool fresh; - PRInt64 tableUpdateTime; - if (mTableFreshness.Get(result->mTableName, &tableUpdateTime)) { - LOG(("tableUpdateTime: %lld, now: %lld, freshnessGuarantee: %d\n", - tableUpdateTime, now, gFreshnessGuarantee)); - fresh = ((now - tableUpdateTime) <= gFreshnessGuarantee); - } else { - LOG(("No expiration time for this table.\n")); - fresh = false; - } - - // This is a confirmed result if we match a complete fragment in - // an up-to-date table. - result->mConfirmed = entry.mHaveComplete && fresh; - - LOG(("Found a result. complete=%d, fresh=%d", - entry.mHaveComplete, fresh)); - } - } - } - - return NS_OK; -} - /** * Lookup up a key in the database is a two step process: * @@ -1775,8 +313,7 @@ nsUrlClassifierDBServiceWorker::DoLookup(const nsACString& spec, } #endif - nsAutoPtr > results; - results = new nsTArray(); + nsAutoPtr results(new LookupResultArray()); if (!results) { c->LookupComplete(nsnull); return NS_ERROR_OUT_OF_MEMORY; @@ -1784,7 +321,10 @@ nsUrlClassifierDBServiceWorker::DoLookup(const nsACString& spec, // we ignore failures from Check because we'd rather return the // results that were found than fail. - Check(spec, *results); + mClassifier->SetFreshTime(gFreshnessGuarantee); + mClassifier->Check(spec, *results); + + LOG(("Found %d results.", results->Length())); #if defined(PR_LOGGING) if (LOG_ENABLED()) { @@ -1794,16 +334,28 @@ nsUrlClassifierDBServiceWorker::DoLookup(const nsACString& spec, } #endif + nsAutoPtr completes(new LookupResultArray()); + for (PRUint32 i = 0; i < results->Length(); i++) { - if (!results->ElementAt(i).mConfirmed) { + if (!mMissCache.Contains(results->ElementAt(i).hash.prefix)) { + completes->AppendElement(results->ElementAt(i)); + } + } + + for (PRUint32 i = 0; i < completes->Length(); i++) { + if (!completes->ElementAt(i).Confirmed()) { // We're going to be doing a gethash request, add some extra entries. - AddNoise(results->ElementAt(i).mEntry.mId, mGethashNoise, *results); + // Note that we cannot pass the first two by reference, because we + // add to completes, whicah can cause completes to reallocate and move. + AddNoise(completes->ElementAt(i).mCodedPrefix, + completes->ElementAt(i).mTableName, + mGethashNoise, *completes); break; } } // At this point ownership of 'results' is handed to the callback. - c->LookupComplete(results.forget()); + c->LookupComplete(completes.forget()); return NS_OK; } @@ -1819,50 +371,42 @@ nsUrlClassifierDBServiceWorker::HandlePendingLookups() MutexAutoUnlock unlock(mPendingLookupLock); DoLookup(lookup.mKey, lookup.mCallback); } + double lookupTime = (TimeStamp::Now() - lookup.mStartTime).ToMilliseconds(); + Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LOOKUP_TIME, + static_cast(lookupTime)); } return NS_OK; } nsresult -nsUrlClassifierDBServiceWorker::AddNoise(PRInt64 nearID, - PRInt32 count, - nsTArray& results) +nsUrlClassifierDBServiceWorker::AddNoise(const Prefix aPrefix, + const nsCString tableName, + PRInt32 aCount, + LookupResultArray& results) { - if (count < 1) { + if (aCount < 1) { return NS_OK; } - PRInt64 randomNum; - nsresult rv = mMainStore.RandomNumber(&randomNum); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt32 numBefore = randomNum % count; - - nsTArray noiseEntries; - rv = mMainStore.ReadNoiseEntries(nearID, numBefore, true, noiseEntries); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mMainStore.ReadNoiseEntries(nearID, count - numBefore, false, noiseEntries); + PrefixArray noiseEntries; + nsresult rv = mClassifier->ReadNoiseEntries(aPrefix, tableName, + aCount, &noiseEntries); NS_ENSURE_SUCCESS(rv, rv); for (PRUint32 i = 0; i < noiseEntries.Length(); i++) { - nsUrlClassifierLookupResult *result = results.AppendElement(); + LookupResult *result = results.AppendElement(); if (!result) return NS_ERROR_OUT_OF_MEMORY; - result->mEntry = noiseEntries[i]; - result->mConfirmed = false; + result->hash.prefix = noiseEntries[i]; result->mNoise = true; - - // Fill in the table name. - GetTableName(noiseEntries[i].mTableId, result->mTableName); + result->mTableName.Assign(tableName); } return NS_OK; } - // Lookup a key in the db. NS_IMETHODIMP nsUrlClassifierDBServiceWorker::Lookup(const nsACString& spec, @@ -1883,1027 +427,31 @@ nsUrlClassifierDBServiceWorker::GetTables(nsIUrlClassifierCallback* c) return NS_ERROR_FAILURE; } - mozStorageStatementScoper scoper(mGetTablesStatement); + NS_ENSURE_SUCCESS(rv, rv); nsCAutoString response; - bool hasMore; - while (NS_SUCCEEDED(rv = mGetTablesStatement->ExecuteStep(&hasMore)) && - hasMore) { - nsCAutoString val; - mGetTablesStatement->GetUTF8String(0, val); - - if (val.IsEmpty()) { - continue; - } - - response.Append(val); - response.Append(';'); - - mGetTablesStatement->GetUTF8String(1, val); - - bool haveAdds = false; - if (!val.IsEmpty()) { - response.Append("a:"); - response.Append(val); - haveAdds = true; - } - - mGetTablesStatement->GetUTF8String(2, val); - if (!val.IsEmpty()) { - if (haveAdds) - response.Append(":"); - - response.Append("s:"); - response.Append(val); - } - - response.Append('\n'); - } - - if (NS_FAILED(rv)) { - response.Truncate(); - } - + mClassifier->TableRequest(response); c->HandleEvent(response); return rv; } -nsresult -nsUrlClassifierDBServiceWorker::GetTableId(const nsACString& table, - PRUint32* tableId) -{ - mozStorageStatementScoper findScoper(mGetTableIdStatement); - - nsresult rv = mGetTableIdStatement->BindUTF8StringByIndex(0, table); - NS_ENSURE_SUCCESS(rv, rv); - - bool exists; - rv = mGetTableIdStatement->ExecuteStep(&exists); - NS_ENSURE_SUCCESS(rv, rv); - if (exists) { - *tableId = mGetTableIdStatement->AsInt32(0); - return NS_OK; - } - - mozStorageStatementScoper insertScoper(mInsertTableIdStatement); - rv = mInsertTableIdStatement->BindUTF8StringByIndex(0, table); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mInsertTableIdStatement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt64 rowId; - rv = mConnection->GetLastInsertRowID(&rowId); - NS_ENSURE_SUCCESS(rv, rv); - - if (rowId > PR_UINT32_MAX) - return NS_ERROR_FAILURE; - - *tableId = rowId; - - return NS_OK; -} - -nsresult -nsUrlClassifierDBServiceWorker::GetTableName(PRUint32 tableId, - nsACString& tableName) -{ - mozStorageStatementScoper findScoper(mGetTableNameStatement); - nsresult rv = mGetTableNameStatement->BindInt32ByIndex(0, tableId); - NS_ENSURE_SUCCESS(rv, rv); - bool exists; - rv = mGetTableNameStatement->ExecuteStep(&exists); - NS_ENSURE_SUCCESS(rv, rv); - if (!exists) return NS_ERROR_FAILURE; - - return mGetTableNameStatement->GetUTF8String(0, tableName); -} - -nsresult -nsUrlClassifierDBServiceWorker::InflateChunk(nsACString& chunk) -{ - nsCAutoString inflated; - char buf[4096]; - - const nsPromiseFlatCString& flat = PromiseFlatCString(chunk); - - z_stream stream; - memset(&stream, 0, sizeof(stream)); - stream.next_in = (Bytef*)flat.get(); - stream.avail_in = flat.Length(); - - if (inflateInit(&stream) != Z_OK) { - return NS_ERROR_FAILURE; - } - - int code; - do { - stream.next_out = (Bytef*)buf; - stream.avail_out = sizeof(buf); - - code = inflate(&stream, Z_NO_FLUSH); - PRUint32 numRead = sizeof(buf) - stream.avail_out; - - if (code == Z_OK || code == Z_STREAM_END) { - inflated.Append(buf, numRead); - } - } while (code == Z_OK); - - inflateEnd(&stream); - - if (code != Z_STREAM_END) { - return NS_ERROR_FAILURE; - } - - chunk = inflated; - - return NS_OK; -} - -nsresult -nsUrlClassifierStore::DeleteEntry(nsUrlClassifierEntry& entry) -{ - if (entry.mId == -1) { - return NS_OK; - } - - mozStorageStatementScoper scoper(mDeleteStatement); - mDeleteStatement->BindInt64ByIndex(0, entry.mId); - nsresult rv = mDeleteStatement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - entry.mId = -1; - - return NS_OK; -} - -nsresult -nsUrlClassifierStore::WriteEntry(nsUrlClassifierEntry& entry) -{ - if (entry.mId != -1) { - // existing entry, just ignore it - return NS_OK; - } - - mozStorageStatementScoper scoper(mInsertStatement); - - nsresult rv = BindStatement(entry, mInsertStatement); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mInsertStatement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt64 rowId; - rv = mConnection->GetLastInsertRowID(&rowId); - NS_ENSURE_SUCCESS(rv, rv); - - if (rowId > PR_UINT32_MAX) { - return NS_ERROR_FAILURE; - } - - entry.mId = rowId; - - return NS_OK; -} - -nsresult -nsUrlClassifierStore::UpdateEntry(nsUrlClassifierEntry& entry) -{ - mozStorageStatementScoper scoper(mUpdateStatement); - - NS_ENSURE_ARG(entry.mId != -1); - - nsresult rv = BindStatement(entry, mUpdateStatement); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mUpdateStatement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -static bool -IsCanonicalizedIP(const nsACString& host) -{ - // The canonicalization process will have left IP addresses in dotted - // decimal with no surprises. - PRUint32 i1, i2, i3, i4; - char c; - if (PR_sscanf(PromiseFlatCString(host).get(), "%u.%u.%u.%u%c", - &i1, &i2, &i3, &i4, &c) == 4) { - return (i1 <= 0xFF && i2 <= 0xFF && i3 <= 0xFF && i4 <= 0xFF); - } - - return false; -} - -static nsresult -GetKey(const nsACString& spec, - nsUrlClassifierDomainHash& hash, - nsICryptoHash * aCryptoHash) -{ - nsACString::const_iterator begin, end, iter; - spec.BeginReading(begin); - spec.EndReading(end); - - iter = begin; - if (!FindCharInReadable('/', iter, end)) { - return NS_OK; - } - - const nsCSubstring& host = Substring(begin, iter); - - if (IsCanonicalizedIP(host)) { - nsCAutoString key; - key.Assign(host); - key.Append("/"); - return hash.FromPlaintext(key, aCryptoHash); - } - - nsTArray hostComponents; - ParseString(PromiseFlatCString(host), '.', hostComponents); - - if (hostComponents.Length() < 2) - return NS_ERROR_FAILURE; - - PRInt32 last = PRInt32(hostComponents.Length()) - 1; - nsCAutoString lookupHost; - - if (hostComponents.Length() > 2) { - lookupHost.Append(hostComponents[last - 2]); - lookupHost.Append("."); - } - - lookupHost.Append(hostComponents[last - 1]); - lookupHost.Append("."); - lookupHost.Append(hostComponents[last]); - lookupHost.Append("/"); - - return hash.FromPlaintext(lookupHost, aCryptoHash); -} - -nsresult -nsUrlClassifierDBServiceWorker::GetShaEntries(PRUint32 tableId, - PRUint32 chunkType, - PRUint32 chunkNum, - PRUint32 domainSize, - PRUint32 fragmentSize, - nsACString& chunk, - nsTArray& entries) -{ - PRUint32 start = 0; - while (start + domainSize + 1 <= chunk.Length()) { - nsUrlClassifierDomainHash domain; - domain.Assign(Substring(chunk, start, DOMAIN_LENGTH)); - start += domainSize; - - // then there is a one-byte count of fragments - PRUint8 numEntries = static_cast(chunk[start]); - start++; - - if (numEntries == 0) { - // if there are no fragments, the domain itself is treated as a - // fragment. This will only work if domainHashSize == hashSize - if (domainSize != fragmentSize) { - NS_WARNING("Received 0-fragment entry where domainSize != fragmentSize"); - return NS_ERROR_FAILURE; - } - - nsUrlClassifierEntry* entry = entries.AppendElement(); - if (!entry) return NS_ERROR_OUT_OF_MEMORY; - - entry->mKey = domain; - entry->mTableId = tableId; - entry->mChunkId = chunkNum; - entry->SetHash(domain); - - if (chunkType == CHUNK_SUB) { - if (start + 4 > chunk.Length()) { - // there isn't as much data as there should be. - NS_WARNING("Received a zero-entry sub chunk without an associated add."); - return NS_ERROR_FAILURE; - } - const nsCSubstring& str = Substring(chunk, start, 4); - PRUint32 p; - memcpy(&p, str.BeginReading(), 4); - entry->mAddChunkId = PR_ntohl(p); - if (entry->mAddChunkId == 0) { - NS_WARNING("Received invalid chunk number."); - return NS_ERROR_FAILURE; - } - start += 4; - } - } else { - PRUint32 entrySize = fragmentSize; - if (chunkType == CHUNK_SUB) { - entrySize += 4; - } - if (start + (numEntries * entrySize) > chunk.Length()) { - // there isn't as much data as they said there would be. - NS_WARNING("Received a chunk without enough data"); - return NS_ERROR_FAILURE; - } - - for (PRUint8 i = 0; i < numEntries; i++) { - nsUrlClassifierEntry* entry = entries.AppendElement(); - if (!entry) return NS_ERROR_OUT_OF_MEMORY; - - entry->mKey = domain; - entry->mTableId = tableId; - entry->mChunkId = chunkNum; - - if (chunkType == CHUNK_SUB) { - const nsCSubstring& str = Substring(chunk, start, 4); - PRUint32 p; - memcpy(&p, str.BeginReading(), 4); - entry->mAddChunkId = PR_ntohl(p); - if (entry->mAddChunkId == 0) { - NS_WARNING("Received invalid chunk number."); - return NS_ERROR_FAILURE; - } - start += 4; - } - - if (fragmentSize == PARTIAL_LENGTH) { - nsUrlClassifierPartialHash hash; - hash.Assign(Substring(chunk, start, PARTIAL_LENGTH)); - entry->SetHash(hash); - } else if (fragmentSize == COMPLETE_LENGTH) { - nsUrlClassifierCompleteHash hash; - hash.Assign(Substring(chunk, start, COMPLETE_LENGTH)); - entry->SetHash(hash); - } else { - NS_ASSERTION(false, "Invalid fragment size!"); - return NS_ERROR_FAILURE; - } - - start += fragmentSize; - } - } - } - - return NS_OK; -} - -nsresult -nsUrlClassifierDBServiceWorker::GetChunkEntries(const nsACString& table, - PRUint32 tableId, - PRUint32 chunkType, - PRUint32 chunkNum, - PRUint32 hashSize, - nsACString& chunk, - nsTArray& entries) -{ - nsresult rv; - if (StringEndsWith(table, NS_LITERAL_CSTRING("-exp"))) { - // regexp tables need to be ungzipped - rv = InflateChunk(chunk); - NS_ENSURE_SUCCESS(rv, rv); - } - - if (StringEndsWith(table, NS_LITERAL_CSTRING("-shavar"))) { - rv = GetShaEntries(tableId, chunkType, chunkNum, DOMAIN_LENGTH, hashSize, - chunk, entries); - NS_ENSURE_SUCCESS(rv, rv); - } else { - nsTArray lines; - ParseString(PromiseFlatCString(chunk), '\n', lines); - - // non-hashed tables need to be hashed - for (PRInt32 i = 0; i < PRInt32(lines.Length()); i++) { - nsUrlClassifierEntry *entry = entries.AppendElement(); - if (!entry) - return NS_ERROR_OUT_OF_MEMORY; - - nsCAutoString entryStr; - if (chunkType == CHUNK_SUB) { - nsCString::const_iterator begin, iter, end; - lines[i].BeginReading(begin); - lines[i].EndReading(end); - iter = begin; - if (!FindCharInReadable(':', iter, end) || - PR_sscanf(lines[i].get(), "%d:", &entry->mAddChunkId) != 1) { - NS_WARNING("Received sub chunk without associated add chunk."); - return NS_ERROR_FAILURE; - } - iter++; - entryStr = Substring(iter, end); - } else { - entryStr = lines[i]; - } - - rv = GetKey(entryStr, entry->mKey, mCryptoHash); - NS_ENSURE_SUCCESS(rv, rv); - - entry->mTableId = tableId; - entry->mChunkId = chunkNum; - if (hashSize == PARTIAL_LENGTH) { - nsUrlClassifierPartialHash hash; - hash.FromPlaintext(entryStr, mCryptoHash); - entry->SetHash(hash); - } else if (hashSize == COMPLETE_LENGTH) { - nsUrlClassifierCompleteHash hash; - hash.FromPlaintext(entryStr, mCryptoHash); - entry->SetHash(hash); - } else { - NS_ASSERTION(false, "Invalid fragment size!"); - return NS_ERROR_FAILURE; - } - } - } - - return NS_OK; -} - -bool -nsUrlClassifierDBServiceWorker::ParseChunkRange(nsACString::const_iterator &begin, - const nsACString::const_iterator &end, - PRUint32 *first, - PRUint32 *last) -{ - nsACString::const_iterator iter = begin; - FindCharInReadable(',', iter, end); - - nsCAutoString element(Substring(begin, iter)); - begin = iter; - if (begin != end) - begin++; - - PRUint32 numRead = PR_sscanf(element.get(), "%u-%u", first, last); - if (numRead == 2) { - if (*first > *last) { - PRUint32 tmp = *first; - *first = *last; - *last = tmp; - } - return true; - } - - if (numRead == 1) { - *last = *first; - return true; - } - - return false; -} - -nsresult -nsUrlClassifierDBServiceWorker::ParseChunkList(const nsACString& chunkStr, - nsTArray& chunks) -{ - LOG(("Parsing %s", PromiseFlatCString(chunkStr).get())); - - nsACString::const_iterator begin, end; - chunkStr.BeginReading(begin); - chunkStr.EndReading(end); - while (begin != end) { - PRUint32 first, last; - if (ParseChunkRange(begin, end, &first, &last)) { - for (PRUint32 num = first; num <= last; num++) { - chunks.AppendElement(num); - } - } - } - - LOG(("Got %d elements.", chunks.Length())); - - return NS_OK; -} - -nsresult -nsUrlClassifierDBServiceWorker::JoinChunkList(nsTArray& chunks, - nsCString& chunkStr) -{ - chunkStr.Truncate(); - chunks.Sort(); - - PRUint32 i = 0; - while (i < chunks.Length()) { - if (i != 0) { - chunkStr.Append(','); - } - chunkStr.AppendInt(chunks[i]); - - PRUint32 first = i; - PRUint32 last = first; - i++; - while (i < chunks.Length() && (chunks[i] == chunks[i - 1] + 1 || chunks[i] == chunks[i - 1])) { - last = i++; - } - - if (last != first) { - chunkStr.Append('-'); - chunkStr.AppendInt(chunks[last]); - } - } - - return NS_OK; -} - - -nsresult -nsUrlClassifierDBServiceWorker::GetChunkLists(PRUint32 tableId, - nsACString& addChunks, - nsACString& subChunks) -{ - addChunks.Truncate(); - subChunks.Truncate(); - - mozStorageStatementScoper scoper(mGetChunkListsStatement); - - nsresult rv = mGetChunkListsStatement->BindInt32ByIndex(0, tableId); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasMore = false; - rv = mGetChunkListsStatement->ExecuteStep(&hasMore); - NS_ENSURE_SUCCESS(rv, rv); - - if (!hasMore) { - LOG(("Getting chunks for %d, found nothing", tableId)); - return NS_OK; - } - - rv = mGetChunkListsStatement->GetUTF8String(0, addChunks); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mGetChunkListsStatement->GetUTF8String(1, subChunks); - NS_ENSURE_SUCCESS(rv, rv); - - LOG(("Getting chunks for %d, got %s/%s", - tableId, - PromiseFlatCString(addChunks).get(), - PromiseFlatCString(subChunks).get())); - - return NS_OK; -} - -nsresult -nsUrlClassifierDBServiceWorker::SetChunkLists(PRUint32 tableId, - const nsACString& addChunks, - const nsACString& subChunks) -{ - mozStorageStatementScoper scoper(mSetChunkListsStatement); - - mSetChunkListsStatement->BindUTF8StringByIndex(0, addChunks); - mSetChunkListsStatement->BindUTF8StringByIndex(1, subChunks); - mSetChunkListsStatement->BindInt32ByIndex(2, tableId); - nsresult rv = mSetChunkListsStatement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -nsUrlClassifierDBServiceWorker::CacheChunkLists(PRUint32 tableId, - bool parseAdds, - bool parseSubs) -{ - nsresult rv; - - if (mHaveCachedLists && mCachedListsTable != tableId) { - rv = FlushChunkLists(); - NS_ENSURE_SUCCESS(rv, rv); - } - - if (!mHaveCachedLists) { - rv = GetChunkLists(tableId, mCachedAddsStr, mCachedSubsStr); - NS_ENSURE_SUCCESS(rv, rv); - - mHaveCachedLists = true; - mCachedListsTable = tableId; - } - - if (parseAdds && !mHaveCachedAddChunks) { - ParseChunkList(mCachedAddsStr, mCachedAddChunks); - mHaveCachedAddChunks = true; - } - - if (parseSubs && !mHaveCachedSubChunks) { - ParseChunkList(mCachedSubsStr, mCachedSubChunks); - mHaveCachedSubChunks = true; - } - - return NS_OK; -} - -nsresult -nsUrlClassifierDBServiceWorker::FlushChunkLists() -{ - if (!mHaveCachedLists) { - return NS_OK; - } - - if (mHaveCachedAddChunks) { - JoinChunkList(mCachedAddChunks, mCachedAddsStr); - } - - if (mHaveCachedSubChunks) { - JoinChunkList(mCachedSubChunks, mCachedSubsStr); - } - - nsresult rv = SetChunkLists(mCachedListsTable, - mCachedAddsStr, mCachedSubsStr); - - // clear out the cache before checking/returning the error here. - ClearCachedChunkLists(); - - return rv; -} - -void -nsUrlClassifierDBServiceWorker::ClearCachedChunkLists() -{ - mCachedAddsStr.Truncate(); - mCachedSubsStr.Truncate(); - mCachedListsTable = PR_UINT32_MAX; - mHaveCachedLists = false; - - mCachedAddChunks.Clear(); - mHaveCachedAddChunks = false; - - mCachedSubChunks.Clear(); - mHaveCachedSubChunks = false; -} - -bool -nsUrlClassifierDBServiceWorker::InsertChunkId(nsTArray &chunks, - PRUint32 chunkNum) -{ - PRUint32 low = 0, high = chunks.Length(); - while (high > low) { - PRUint32 mid = (high + low) >> 1; - if (chunks[mid] == chunkNum) - return false; - if (chunks[mid] < chunkNum) - low = mid + 1; - else - high = mid; - } - - PRUint32 *item = chunks.InsertElementAt(low, chunkNum); - return (item != nsnull); -} - -nsresult -nsUrlClassifierDBServiceWorker::AddChunk(PRUint32 tableId, - PRUint32 chunkNum, - nsTArray& entries) -{ -#if defined(PR_LOGGING) - PRIntervalTime clockStart = 0; - if (LOG_ENABLED()) { - clockStart = PR_IntervalNow(); - } -#endif - - nsresult rv = CacheChunkLists(tableId, true, false); - NS_ENSURE_SUCCESS(rv, rv); - - if (!InsertChunkId(mCachedAddChunks, chunkNum)) { - LOG(("Ignoring duplicate add chunk %d in table %d", chunkNum, tableId)); - return NS_OK; - } - - LOG(("Adding %d entries to chunk %d in table %d", entries.Length(), chunkNum, tableId)); - - nsTArray entryIDs; - - nsAutoTArray subEntries; - rv = mPendingSubStore.ReadSubEntries(tableId, chunkNum, subEntries); - NS_ENSURE_SUCCESS(rv, rv); - - for (PRUint32 i = 0; i < entries.Length(); i++) { - nsUrlClassifierEntry& thisEntry = entries[i]; - - HandlePendingLookups(); - - bool writeEntry = true; - for (PRUint32 j = 0; j < subEntries.Length(); j++) { - if (thisEntry.SubMatch(subEntries[j])) { - subEntries.RemoveElementAt(j); - - writeEntry = false; - break; - } - } - - HandlePendingLookups(); - - if (writeEntry) { - rv = mMainStore.WriteEntry(thisEntry); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - rv = mPendingSubStore.ExpireAddChunk(tableId, chunkNum); - NS_ENSURE_SUCCESS(rv, rv); - -#if defined(PR_LOGGING) - if (LOG_ENABLED()) { - PRIntervalTime clockEnd = PR_IntervalNow(); - LOG(("adding chunk %d took %dms\n", chunkNum, - PR_IntervalToMilliseconds(clockEnd - clockStart))); - } -#endif - - return rv; -} - -nsresult -nsUrlClassifierStore::Expire(PRUint32 tableId, PRUint32 chunkNum) -{ - LOG(("Expiring chunk %d\n", chunkNum)); - - mozStorageStatementScoper expireScoper(mExpireStatement); - - nsresult rv = mExpireStatement->BindInt32ByIndex(0, tableId); - NS_ENSURE_SUCCESS(rv, rv); - rv = mExpireStatement->BindInt32ByIndex(1, chunkNum); - NS_ENSURE_SUCCESS(rv, rv); - - mWorker->HandlePendingLookups(); - - rv = mExpireStatement->Execute(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -nsUrlClassifierDBServiceWorker::ExpireAdd(PRUint32 tableId, - PRUint32 chunkNum) -{ - nsresult rv = CacheChunkLists(tableId, true, false); - NS_ENSURE_SUCCESS(rv, rv); - mCachedAddChunks.RemoveElement(chunkNum); - - return mMainStore.Expire(tableId, chunkNum); -} - -nsresult -nsUrlClassifierDBServiceWorker::SubChunk(PRUint32 tableId, - PRUint32 chunkNum, - nsTArray& entries) -{ - nsresult rv = CacheChunkLists(tableId, true, true); - - if (!InsertChunkId(mCachedSubChunks, chunkNum)) { - LOG(("Ignoring duplicate sub chunk %d in table %d", chunkNum, tableId)); - return NS_OK; - } - - LOG(("Subbing %d entries in chunk %d in table %d", entries.Length(), chunkNum, tableId)); - - for (PRUint32 i = 0; i < entries.Length(); i++) { - nsAutoTArray existingEntries; - nsUrlClassifierEntry& thisEntry = entries[i]; - - HandlePendingLookups(); - - // Check if we have the add chunk associated with the sub. - bool haveAdds = (mCachedAddChunks.BinaryIndexOf(thisEntry.mAddChunkId) != - mCachedAddChunks.NoIndex); - - if (haveAdds) { - rv = mMainStore.ReadAddEntries(thisEntry.mKey, thisEntry.mTableId, - thisEntry.mAddChunkId, existingEntries); - NS_ENSURE_SUCCESS(rv, rv); - } - - for (PRUint32 j = 0; j < existingEntries.Length(); j++) { - if (existingEntries[j].SubMatch(thisEntry)) { - rv = mMainStore.DeleteEntry(existingEntries[j]); - NS_ENSURE_SUCCESS(rv, rv); - existingEntries.RemoveElementAt(j); - break; - } - } - - if (!haveAdds) { - // Save this entry in the pending subtraction store. - rv = mPendingSubStore.WriteEntry(thisEntry); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - return NS_OK; -} - -nsresult -nsUrlClassifierDBServiceWorker::ExpireSub(PRUint32 tableId, PRUint32 chunkNum) -{ - nsresult rv = CacheChunkLists(tableId, false, true); - NS_ENSURE_SUCCESS(rv, rv); - mCachedSubChunks.RemoveElement(chunkNum); - - return mPendingSubStore.Expire(tableId, chunkNum); -} - -nsresult -nsUrlClassifierDBServiceWorker::ProcessChunk(bool* done) -{ - // wait until the chunk has been read - if (mPendingStreamUpdate.Length() < static_cast(mChunkLen)) { - *done = true; - return NS_OK; - } - - nsCAutoString chunk; - chunk.Assign(Substring(mPendingStreamUpdate, 0, mChunkLen)); - mPendingStreamUpdate = Substring(mPendingStreamUpdate, mChunkLen); - - LOG(("Handling a chunk sized %d", chunk.Length())); - - nsTArray entries; - nsresult rv = GetChunkEntries(mUpdateTable, mUpdateTableId, mChunkType, - mChunkNum, mHashSize, chunk, entries); - NS_ENSURE_SUCCESS(rv, rv); - - if (mChunkType == CHUNK_ADD) { - rv = AddChunk(mUpdateTableId, mChunkNum, entries); - } else { - rv = SubChunk(mUpdateTableId, mChunkNum, entries); - } - - mState = STATE_LINE; - *done = false; - - return rv; -} - -nsresult -nsUrlClassifierDBServiceWorker::ProcessResponseLines(bool* done) -{ - PRUint32 cur = 0; - PRInt32 next; - - nsresult rv; - // We will run to completion unless we find a chunk line - *done = true; - - nsACString& updateString = mPendingStreamUpdate; - - while(cur < updateString.Length() && - (next = updateString.FindChar('\n', cur)) != kNotFound) { - const nsCSubstring& line = Substring(updateString, cur, next - cur); - cur = next + 1; - - LOG(("Processing %s\n", PromiseFlatCString(line).get())); - - if (mHMAC && mServerMAC.IsEmpty()) { - // If we did not receive a server MAC during BeginStream(), we - // require the first line of the update to be either a MAC or - // a request to rekey. - if (StringBeginsWith(line, NS_LITERAL_CSTRING("m:"))) { - mServerMAC = Substring(line, 2); - nsUrlClassifierUtils::UnUrlsafeBase64(mServerMAC); - - // The remainder of the pending update needs to be digested. - const nsCSubstring &toDigest = Substring(updateString, cur); - rv = mHMAC->Update(reinterpret_cast(toDigest.BeginReading()), - toDigest.Length()); - NS_ENSURE_SUCCESS(rv, rv); - } else if (line.EqualsLiteral("e:pleaserekey")) { - mUpdateObserver->RekeyRequested(); - } else { - LOG(("No MAC specified!")); - return NS_ERROR_FAILURE; - } - } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) { - if (PR_sscanf(PromiseFlatCString(line).get(), "n:%d", - &mUpdateWait) != 1) { - LOG(("Error parsing n: field: %s", PromiseFlatCString(line).get())); - mUpdateWait = 0; - } - } else if (line.EqualsLiteral("r:pleasereset")) { - mResetRequested = true; - } else if (line.EqualsLiteral("e:pleaserekey")) { - mUpdateObserver->RekeyRequested(); - } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) { - mUpdateTable.Assign(Substring(line, 2)); - GetTableId(mUpdateTable, &mUpdateTableId); - LOG(("update table: '%s' (%d)", mUpdateTable.get(), mUpdateTableId)); - } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("u:"))) { - if (!mPrimaryStream) { - LOG(("Forwarded update tried to add its own forwarded update.")); - return NS_ERROR_FAILURE; - } - - const nsCSubstring& data = Substring(line, 2); - if (mHMAC) { - // We're expecting MACs alongside any url forwards. - nsCSubstring::const_iterator begin, end, sepBegin, sepEnd; - data.BeginReading(begin); - sepBegin = begin; - - data.EndReading(end); - sepEnd = end; - - if (!RFindInReadable(NS_LITERAL_CSTRING(","), sepBegin, sepEnd)) { - NS_WARNING("No MAC specified for a redirect in a request that expects a MAC"); - return NS_ERROR_FAILURE; - } - - nsCString serverMAC(Substring(sepEnd, end)); - nsUrlClassifierUtils::UnUrlsafeBase64(serverMAC); - mUpdateObserver->UpdateUrlRequested(Substring(begin, sepBegin), - mUpdateTable, - serverMAC); - } else { - // We didn't ask for a MAC, none should have been specified. - mUpdateObserver->UpdateUrlRequested(data, mUpdateTable, - NS_LITERAL_CSTRING("")); - } - } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("a:")) || - StringBeginsWith(line, NS_LITERAL_CSTRING("s:"))) { - mState = STATE_CHUNK; - char command; - if (PR_sscanf(PromiseFlatCString(line).get(), - "%c:%d:%d:%d", &command, &mChunkNum, &mHashSize, &mChunkLen) != 4) { - return NS_ERROR_FAILURE; - } - - if (mChunkLen > MAX_CHUNK_SIZE) { - return NS_ERROR_FAILURE; - } - - if (!(mHashSize == PARTIAL_LENGTH || mHashSize == COMPLETE_LENGTH)) { - NS_WARNING("Invalid hash size specified in update."); - return NS_ERROR_FAILURE; - } - - mChunkType = (command == 'a') ? CHUNK_ADD : CHUNK_SUB; - - // Done parsing lines, move to chunk state now - *done = false; - break; - } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("ad:"))) { - const nsCSubstring &list = Substring(line, 3); - nsACString::const_iterator begin, end; - list.BeginReading(begin); - list.EndReading(end); - while (begin != end) { - PRUint32 first, last; - if (ParseChunkRange(begin, end, &first, &last)) { - for (PRUint32 num = first; num <= last; num++) { - rv = ExpireAdd(mUpdateTableId, num); - NS_ENSURE_SUCCESS(rv, rv); - } - } else { - return NS_ERROR_FAILURE; - } - } - } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("sd:"))) { - const nsCSubstring &list = Substring(line, 3); - nsACString::const_iterator begin, end; - list.BeginReading(begin); - list.EndReading(end); - while (begin != end) { - PRUint32 first, last; - if (ParseChunkRange(begin, end, &first, &last)) { - for (PRUint32 num = first; num <= last; num++) { - rv = ExpireSub(mUpdateTableId, num); - NS_ENSURE_SUCCESS(rv, rv); - } - } else { - return NS_ERROR_FAILURE; - } - } - } else { - LOG(("ignoring unknown line: '%s'", PromiseFlatCString(line).get())); - } - } - - mPendingStreamUpdate = Substring(updateString, cur); - - return NS_OK; -} - void nsUrlClassifierDBServiceWorker::ResetStream() { - mState = STATE_LINE; - mChunkNum = 0; - mHashSize = 0; - mChunkLen = 0; + LOG(("ResetStream")); mInStream = false; - mPrimaryStream = false; - mUpdateTable.Truncate(); - mPendingStreamUpdate.Truncate(); - mServerMAC.Truncate(); - mHMAC = nsnull; + mProtocolParser = nsnull; } void nsUrlClassifierDBServiceWorker::ResetUpdate() { + LOG(("ResetUpdate")); mUpdateWait = 0; mUpdateStatus = NS_OK; mUpdateObserver = nsnull; mUpdateClientKey.Truncate(); - mResetRequested = false; - mUpdateTables.Clear(); } NS_IMETHODIMP @@ -2918,6 +466,8 @@ nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *obse const nsACString &tables, const nsACString &clientKey) { + LOG(("nsUrlClassifierDBServiceWorker::BeginUpdate [%s]", PromiseFlatCString(tables).get())); + if (gShuttingDownThread) return NS_ERROR_NOT_INITIALIZED; @@ -2929,38 +479,16 @@ nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *obse return NS_ERROR_FAILURE; } - bool transaction; - rv = mConnection->GetTransactionInProgress(&transaction); - if (NS_FAILED(rv)) { - mUpdateStatus = rv; - return rv; - } - - if (transaction) { - NS_WARNING("Transaction already in progress in nsUrlClassifierDBServiceWorker::BeginUpdate. Cancelling update."); - mUpdateStatus = NS_ERROR_FAILURE; - return rv; - } - - rv = SetupUpdate(); - if (NS_FAILED(rv)) { - mUpdateStatus = rv; - return rv; - } - + mUpdateStatus = NS_OK; mUpdateObserver = observer; + SplitTables(tables, mUpdateTables); if (!clientKey.IsEmpty()) { rv = nsUrlClassifierUtils::DecodeClientKey(clientKey, mUpdateClientKey); NS_ENSURE_SUCCESS(rv, rv); + LOG(("clientKey present, marking update key")); } - // The first stream in an update is the only stream that may request - // forwarded updates. - mPrimaryStream = true; - - SplitTables(tables, mUpdateTables); - return NS_OK; } @@ -2968,62 +496,37 @@ NS_IMETHODIMP nsUrlClassifierDBServiceWorker::BeginStream(const nsACString &table, const nsACString &serverMAC) { + LOG(("nsUrlClassifierDBServiceWorker::BeginStream")); + if (gShuttingDownThread) return NS_ERROR_NOT_INITIALIZED; NS_ENSURE_STATE(mUpdateObserver); NS_ENSURE_STATE(!mInStream); - // We may have committed the update in FinishStream, if so set it up - // again here. - nsresult rv = SetupUpdate(); - if (NS_FAILED(rv)) { - mUpdateStatus = rv; - return rv; - } - mInStream = true; + NS_ASSERTION(!mProtocolParser, "Should not have a protocol parser."); + + mProtocolParser = new ProtocolParser(mHashKey); + if (!mProtocolParser) + return NS_ERROR_OUT_OF_MEMORY; + + mProtocolParser->Init(mCryptoHash); + + nsresult rv; + // If we're expecting a MAC, create the nsICryptoHMAC component now. if (!mUpdateClientKey.IsEmpty()) { - nsCOMPtr keyObjectFactory(do_GetService( - "@mozilla.org/security/keyobjectfactory;1", &rv)); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to get nsIKeyObjectFactory service"); - mUpdateStatus = rv; - return mUpdateStatus; - } - - nsCOMPtr keyObject; - rv = keyObjectFactory->KeyFromString(nsIKeyObject::HMAC, mUpdateClientKey, - getter_AddRefs(keyObject)); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to create key object, maybe not FIPS compliant?"); - mUpdateStatus = rv; - return mUpdateStatus; - } - - mHMAC = do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to create nsICryptoHMAC instance"); - mUpdateStatus = rv; - return mUpdateStatus; - } - - rv = mHMAC->Init(nsICryptoHMAC::SHA1, keyObject); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to initialize nsICryptoHMAC instance"); - mUpdateStatus = rv; - return mUpdateStatus; - } + LOG(("Expecting MAC in this stream")); + rv = mProtocolParser->InitHMAC(mUpdateClientKey, serverMAC); + NS_ENSURE_SUCCESS(rv, rv); + } else { + LOG(("No MAC in this stream")); } - mServerMAC = serverMAC; - if (!table.IsEmpty()) { - mUpdateTable = table; - GetTableId(mUpdateTable, &mUpdateTableId); - LOG(("update table: '%s' (%d)", mUpdateTable.get(), mUpdateTableId)); + mProtocolParser->SetCurrentTable(table); } return NS_OK; @@ -3070,45 +573,7 @@ nsUrlClassifierDBServiceWorker::UpdateStream(const nsACString& chunk) HandlePendingLookups(); - LOG(("Update from Stream.")); - nsresult rv = OpenDb(); - if (NS_FAILED(rv)) { - NS_ERROR("Unable to open database"); - return NS_ERROR_FAILURE; - } - - // if something has gone wrong during this update, just throw it away - if (NS_FAILED(mUpdateStatus)) { - return mUpdateStatus; - } - - if (mHMAC && !mServerMAC.IsEmpty()) { - rv = mHMAC->Update(reinterpret_cast(chunk.BeginReading()), - chunk.Length()); - if (NS_FAILED(rv)) { - mUpdateStatus = rv; - return mUpdateStatus; - } - } - - LOG(("Got %s\n", PromiseFlatCString(chunk).get())); - - mPendingStreamUpdate.Append(chunk); - - bool done = false; - while (!done) { - if (mState == STATE_CHUNK) { - rv = ProcessChunk(&done); - } else { - rv = ProcessResponseLines(&done); - } - if (NS_FAILED(rv)) { - mUpdateStatus = rv; - return rv; - } - } - - return NS_OK; + return mProtocolParser->AppendStream(chunk); } NS_IMETHODIMP @@ -3120,85 +585,71 @@ nsUrlClassifierDBServiceWorker::FinishStream() NS_ENSURE_STATE(mInStream); NS_ENSURE_STATE(mUpdateObserver); - PRInt32 nextStreamDelay = 0; + mInStream = false; - if (NS_SUCCEEDED(mUpdateStatus) && mHMAC) { - nsCAutoString clientMAC; - mHMAC->Finish(true, clientMAC); + mProtocolParser->FinishHMAC(); - if (clientMAC != mServerMAC) { - NS_WARNING("Invalid update MAC!"); - LOG(("Invalid update MAC: expected %s, got %s", - mServerMAC.get(), clientMAC.get())); - mUpdateStatus = NS_ERROR_FAILURE; + if (NS_SUCCEEDED(mProtocolParser->Status())) { + if (mProtocolParser->UpdateWait()) { + mUpdateWait = mProtocolParser->UpdateWait(); } - PRIntervalTime updateTime = PR_IntervalNow() - mUpdateStartTime; - if (PR_IntervalToSeconds(updateTime) >= - static_cast(gWorkingTimeThreshold)) { - // We've spent long enough working that we should commit what we - // have and hold off for a bit. - nsresult rv = ApplyUpdate(); - if (NS_FAILED(rv)) { - if (rv == NS_ERROR_FILE_CORRUPTED) { - ResetDatabase(); - } - return rv; - } - nextStreamDelay = gDelayTime * 1000; + // XXX: Only allow forwards from the initial update? + const nsTArray &forwards = + mProtocolParser->Forwards(); + for (uint32 i = 0; i < forwards.Length(); i++) { + const ProtocolParser::ForwardedUpdate &forward = forwards[i]; + mUpdateObserver->UpdateUrlRequested(forward.url, forward.table, forward.mac); + } + // Hold on to any TableUpdate objects that were created by the + // parser. + mTableUpdates.AppendElements(mProtocolParser->GetTableUpdates()); + mProtocolParser->ForgetTableUpdates(); + } else { + mUpdateStatus = mProtocolParser->Status(); + } + mUpdateObserver->StreamFinished(mProtocolParser->Status(), 0); + + // Only reset if MAC was OK + if (NS_SUCCEEDED(mUpdateStatus)) { + if (mProtocolParser->ResetRequested()) { + mClassifier->Reset(); } } + // Rekey will cause update to fail (can't check MACs) + if (mProtocolParser->RekeyRequested()) { + mUpdateObserver->RekeyRequested(); + } - mUpdateObserver->StreamFinished(mUpdateStatus, - static_cast(nextStreamDelay)); - - ResetStream(); - + mProtocolParser = nsnull; return NS_OK; } -nsresult -nsUrlClassifierDBServiceWorker::SetCacheSize( - mozIStorageConnection * aConnection, PRInt32 aCacheSize) +NS_IMETHODIMP +nsUrlClassifierDBServiceWorker::FinishUpdate() { - mozStorageStatementScoper scoper(mGetPageSizeStatement); - bool hasResult; - nsresult rv = mGetPageSizeStatement->ExecuteStep(&hasResult); - NS_ENSURE_SUCCESS(rv, rv); + if (gShuttingDownThread) + return NS_ERROR_NOT_INITIALIZED; + NS_ENSURE_STATE(mUpdateObserver); - NS_ASSERTION(hasResult, "Should always be able to get page size from sqlite"); - PRUint32 pageSize = mGetPageSizeStatement->AsInt32(0); - PRUint32 cachePages = aCacheSize / pageSize; - nsCAutoString cacheSizePragma(MOZ_STORAGE_UNIQUIFY_QUERY_STR - "PRAGMA cache_size="); - cacheSizePragma.AppendInt(cachePages); - rv = aConnection->ExecuteSimpleSQL(cacheSizePragma); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -nsUrlClassifierDBServiceWorker::SetupUpdate() -{ - LOG(("nsUrlClassifierDBServiceWorker::SetupUpdate")); - bool inProgress; - nsresult rv = mConnection->GetTransactionInProgress(&inProgress); - if (inProgress) { - return NS_OK; + if (NS_SUCCEEDED(mUpdateStatus)) { + mUpdateStatus = ApplyUpdate(); } - mUpdateStartTime = PR_IntervalNow(); + mMissCache.Clear(); - rv = mConnection->BeginTransaction(); - NS_ENSURE_SUCCESS(rv, rv); - - if (gUpdateCacheSize > 0) { - rv = SetCacheSize(mConnection, gUpdateCacheSize); - NS_ENSURE_SUCCESS(rv, rv); - if (gUpdateCacheSize != gLookupCacheSize) { - mGrewCache = true; - } + if (NS_SUCCEEDED(mUpdateStatus)) { + LOG(("Notifying success: %d", mUpdateWait)); + mUpdateObserver->UpdateSuccess(mUpdateWait); + } else { + LOG(("Notifying error: %d", mUpdateStatus)); + mUpdateObserver->UpdateError(mUpdateStatus); + /* + * mark the tables as spoiled, we don't want to block hosts + * longer than normal because our update failed + */ + mClassifier->MarkSpoiled(mUpdateTables); } + mUpdateObserver = nsnull; return NS_OK; } @@ -3206,116 +657,20 @@ nsUrlClassifierDBServiceWorker::SetupUpdate() nsresult nsUrlClassifierDBServiceWorker::ApplyUpdate() { - LOG(("nsUrlClassifierDBServiceWorker::ApplyUpdate")); - - if (mConnection) { - if (NS_FAILED(mUpdateStatus)) { - mConnection->RollbackTransaction(); - } else { - mUpdateStatus = FlushChunkLists(); - if (NS_SUCCEEDED(mUpdateStatus)) { - mUpdateStatus = mConnection->CommitTransaction(); - } - } - } - - if (NS_SUCCEEDED(mUpdateStatus)) { - // Reconstruct the prefix tree from the DB - nsresult rv = ConstructPrefixSet(); - NS_ENSURE_SUCCESS(rv, rv); - } - - if (mGrewCache) { - // During the update we increased the page cache to bigger than we - // want to keep around. At the moment, the only reliable way to make - // sure that the page cache is freed is to reopen the connection. - LOG(("GrewCache true, reopening DB")); - mGrewCache = false; - CloseDb(); - OpenDb(); - } - - mUpdateStartTime = 0; - - return NS_OK; -} - -NS_IMETHODIMP -nsUrlClassifierDBServiceWorker::FinishUpdate() -{ - LOG(("nsUrlClassifierDBServiceWorker::FinishUpdate()")); - if (gShuttingDownThread) - return NS_ERROR_NOT_INITIALIZED; - - NS_ENSURE_STATE(!mInStream); - NS_ENSURE_STATE(mUpdateObserver); - - // We need to get the error code before ApplyUpdate, because it might - // close/open the connection. - PRInt32 errcode = SQLITE_OK; - if (mConnection) - mConnection->GetLastError(&errcode); - - nsresult rv = ApplyUpdate(); - if (NS_FAILED(rv)) { - if (rv == NS_ERROR_FILE_CORRUPTED) { - ResetDatabase(); - } - return rv; - } - - if (NS_SUCCEEDED(mUpdateStatus)) { - mUpdateObserver->UpdateSuccess(mUpdateWait); - } else { - mUpdateObserver->UpdateError(mUpdateStatus); - } - - // It's important that we only reset the database on an update - // command if the update was successful, otherwise unauthenticated - // updates could cause a database reset. - bool resetDB = (NS_SUCCEEDED(mUpdateStatus) && mResetRequested) || - errcode == SQLITE_CORRUPT; - - if (!resetDB) { - if (NS_SUCCEEDED(mUpdateStatus)) { - PRInt64 now = (PR_Now() / PR_USEC_PER_SEC); - for (PRUint32 i = 0; i < mUpdateTables.Length(); i++) { - LOG(("Successfully updated %s", mUpdateTables[i].get())); - mTableFreshness.Put(mUpdateTables[i], now); - } - } else { - for (PRUint32 i = 0; i < mUpdateTables.Length(); i++) { - LOG(("Failed updating %s", mUpdateTables[i].get())); - mTableFreshness.Remove(mUpdateTables[i]); - } - } - } - - ResetUpdate(); - - if (resetDB) { - ResetDatabase(); - } - - return NS_OK; + LOG(("nsUrlClassifierDBServiceWorker::ApplyUpdate()")); + return mClassifier->ApplyUpdates(&mTableUpdates); } NS_IMETHODIMP nsUrlClassifierDBServiceWorker::ResetDatabase() { - LOG(("nsUrlClassifierDBServiceWorker::ResetDatabase [%p]", this)); - ClearCachedChunkLists(); - - mTableFreshness.Clear(); - - nsresult rv = CloseDb(); + nsresult rv = OpenDb(); NS_ENSURE_SUCCESS(rv, rv); - rv = mPrefixSet->SetPrefixes(nsnull, 0); - NS_ENSURE_SUCCESS(rv, rv); + mClassifier->Reset(); - mDBFile->Remove(false); - mPSFile->Remove(false); + rv = CloseDb(); + NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } @@ -3323,22 +678,25 @@ nsUrlClassifierDBServiceWorker::ResetDatabase() NS_IMETHODIMP nsUrlClassifierDBServiceWorker::CancelUpdate() { - LOG(("CancelUpdate")); + LOG(("nsUrlClassifierDBServiceWorker::CancelUpdate")); if (mUpdateObserver) { + LOG(("UpdateObserver exists, cancelling")); + mUpdateStatus = NS_BINDING_ABORTED; - ClearCachedChunkLists(); - mConnection->RollbackTransaction(); mUpdateObserver->UpdateError(mUpdateStatus); - for (PRUint32 i = 0; i < mUpdateTables.Length(); i++) { - LOG(("Failed updating %s", mUpdateTables[i].get())); - mTableFreshness.Remove(mUpdateTables[i]); - } + /* + * mark the tables as spoiled, we don't want to block hosts + * longer than normal because our update failed + */ + mClassifier->MarkSpoiled(mUpdateTables); ResetStream(); ResetUpdate(); + } else { + LOG(("No UpdateObserver, nothing to cancel")); } return NS_OK; @@ -3351,48 +709,75 @@ nsUrlClassifierDBServiceWorker::CancelUpdate() NS_IMETHODIMP nsUrlClassifierDBServiceWorker::CloseDb() { - if (mConnection) { - mMainStore.Close(); - mPendingSubStore.Close(); - - mGetChunkListsStatement = nsnull; - mSetChunkListsStatement = nsnull; - - mGetTablesStatement = nsnull; - mGetTableIdStatement = nsnull; - mGetTableNameStatement = nsnull; - mInsertTableIdStatement = nsnull; - mGetPageSizeStatement = nsnull; - - mConnection = nsnull; - LOG(("urlclassifier db closed\n")); + if (mClassifier) { + mClassifier->Close(); + mClassifier = nsnull; } mCryptoHash = nsnull; + LOG(("urlclassifier db closed\n")); return NS_OK; } NS_IMETHODIMP -nsUrlClassifierDBServiceWorker::CacheCompletions(nsTArray *results) +nsUrlClassifierDBServiceWorker::CacheCompletions(CacheResultArray *results) { LOG(("nsUrlClassifierDBServiceWorker::CacheCompletions [%p]", this)); + if (!mClassifier) + return NS_OK; - nsAutoPtr > resultsPtr(results); + // Ownership is transferred in to us + nsAutoPtr resultsPtr(results); - // Start a new transaction. If a transaction is open for an update - // this will be a noop, and this cache will be included in the - // update's transaction. - mozStorageTransaction trans(mConnection, true); + nsAutoPtr pParse(new ProtocolParser(mHashKey)); + nsTArray updates; - for (PRUint32 i = 0; i < results->Length(); i++) { - nsUrlClassifierLookupResult& result = results->ElementAt(i); - // Failing to update here shouldn't be fatal (and might be common, - // if we're updating entries that were removed since they were - // returned after a lookup). - mMainStore.UpdateEntry(result.mEntry); - } + // Only cache results for tables that we have, don't take + // in tables we might accidentally have hit during a completion. + // This happens due to goog vs googpub lists existing. + nsTArray tables; + nsresult rv = mClassifier->ActiveTables(tables); + NS_ENSURE_SUCCESS(rv, rv); + for (PRUint32 i = 0; i < resultsPtr->Length(); i++) { + bool activeTable = false; + for (PRUint32 table = 0; table < tables.Length(); table++) { + if (tables[table].Equals(resultsPtr->ElementAt(i).table)) { + activeTable = true; + } + } + if (activeTable) { + TableUpdate * tu = pParse->GetTableUpdate(resultsPtr->ElementAt(i).table); + LOG(("CacheCompletion Addchunk %d hash %X", resultsPtr->ElementAt(i).entry.addChunk, + resultsPtr->ElementAt(i).entry.hash.prefix)); + tu->NewAddComplete(resultsPtr->ElementAt(i).entry.addChunk, + resultsPtr->ElementAt(i).entry.hash.complete); + tu->NewAddChunk(resultsPtr->ElementAt(i).entry.addChunk); + tu->SetLocalUpdate(); + updates.AppendElement(tu); + pParse->ForgetTableUpdates(); + } else { + LOG(("Completion received, but table is not active, so not caching.")); + } + } + + mClassifier->ApplyUpdates(&updates); + return NS_OK; +} + +NS_IMETHODIMP +nsUrlClassifierDBServiceWorker::CacheMisses(PrefixArray *results) +{ + LOG(("nsUrlClassifierDBServiceWorker::CacheMisses [%p] %d", + this, results->Length())); + + // Ownership is transferred in to us + nsAutoPtr resultsPtr(results); + + for (PRUint32 i = 0; i < resultsPtr->Length(); i++) { + mMissCache.AppendElement(resultsPtr->ElementAt(i)); + } return NS_OK; } @@ -3400,462 +785,33 @@ nsresult nsUrlClassifierDBServiceWorker::OpenDb() { // Connection already open, don't do anything. - if (mConnection) { + if (mClassifier) { return NS_OK; } - LOG(("Opening db\n")); + LOG(("Opening db")); - nsresult rv; - // open the connection - nsCOMPtr storageService = - do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - bool exists; - rv = mDBFile->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - bool newDB = !exists; - - nsCOMPtr connection; - rv = storageService->OpenDatabase(mDBFile, getter_AddRefs(connection)); - if (rv == NS_ERROR_FILE_CORRUPTED) { - // delete the db and try opening again - rv = mDBFile->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); - - newDB = true; - - rv = storageService->OpenDatabase(mDBFile, getter_AddRefs(connection)); - } - NS_ENSURE_SUCCESS(rv, rv); - - if (!newDB) { - PRInt32 databaseVersion; - rv = connection->GetSchemaVersion(&databaseVersion); - NS_ENSURE_SUCCESS(rv, rv); - - if (databaseVersion != IMPLEMENTATION_VERSION) { - LOG(("Incompatible database, removing.")); - - rv = connection->Close(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mDBFile->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); - - newDB = true; - - rv = storageService->OpenDatabase(mDBFile, getter_AddRefs(connection)); - NS_ENSURE_SUCCESS(rv, rv); - } + nsAutoPtr classifier(new Classifier()); + if (!classifier) { + return NS_ERROR_OUT_OF_MEMORY; } - connection->SetGrowthIncrement(5 * 1024 * 1024, EmptyCString()); - rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous=OFF")); - NS_ENSURE_SUCCESS(rv, rv); + classifier->SetFreshTime(gFreshnessGuarantee); - rv = connection->CreateStatement - (NS_LITERAL_CSTRING(MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size"), - getter_AddRefs(mGetPageSizeStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = SetCacheSize(connection, gLookupCacheSize); - NS_ENSURE_SUCCESS(rv, rv); - - if (newDB) { - rv = connection->SetSchemaVersion(IMPLEMENTATION_VERSION); - NS_ENSURE_SUCCESS(rv, rv); + nsresult rv = classifier->Open(*mCacheDir); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to open URL classifier."); } - // Create the table - rv = MaybeCreateTables(connection); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mMainStore.Init(this, connection, - NS_LITERAL_CSTRING("moz_classifier")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mPendingSubStore.Init(this, connection, - NS_LITERAL_CSTRING("moz_subs")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->CreateStatement - (NS_LITERAL_CSTRING("SELECT add_chunks, sub_chunks FROM moz_tables" - " WHERE id=?1"), - getter_AddRefs(mGetChunkListsStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->CreateStatement - (NS_LITERAL_CSTRING("UPDATE moz_tables" - " SET add_chunks=?1, sub_chunks=?2" - " WHERE id=?3"), - getter_AddRefs(mSetChunkListsStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->CreateStatement - (NS_LITERAL_CSTRING("SELECT name, add_chunks, sub_chunks" - " FROM moz_tables"), - getter_AddRefs(mGetTablesStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->CreateStatement - (NS_LITERAL_CSTRING("SELECT id FROM moz_tables" - " WHERE name = ?1"), - getter_AddRefs(mGetTableIdStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->CreateStatement - (NS_LITERAL_CSTRING("SELECT name FROM moz_tables" - " WHERE id = ?1"), - getter_AddRefs(mGetTableNameStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->CreateStatement - (NS_LITERAL_CSTRING("INSERT INTO moz_tables(id, name, add_chunks, sub_chunks)" - " VALUES (null, ?1, null, null)"), - getter_AddRefs(mInsertTableIdStatement)); - NS_ENSURE_SUCCESS(rv, rv); - - mConnection = connection; + mHashKey = classifier->GetHashKey(); + mClassifier = classifier; mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); - LOG(("loading Prefix Set\n")); - rv = LoadPrefixSet(mPSFile); - if (NS_FAILED(rv)) { - if (rv == NS_ERROR_FILE_CORRUPTED) { - ResetDatabase(); - } - return rv; - } - return NS_OK; } -// We have both a prefix and a domain. Drop the domain, but -// hash the domain, the prefix and a random value together, -// ensuring any collisions happens at a different points for -// different users. -// We need to calculate +- 500k hashes each update. -// The extensive initialization and finalization of normal -// cryptographic hashes, as well as fairly low speed, causes them -// to be prohibitively slow here, hence we can't use them. -// We use MurmurHash3 instead because it's reasonably well -// researched, trusted inside some other big projects, extremely -// fast and with a specific a 32-bit output version, and fairly -// compact. Upon testing with the actual prefix data, it does -// not appear to increase the number of collisions by any -// meaningful amount. -static nsresult KeyedHash(PRUint32 aPref, PRUint32 aDomain, - PRUint32 aKey, PRUint32 *aOut) -{ - // This is a reimplementation of MurmurHash3 32-bit - // based on the public domain C++ sources. - // http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp - // for nblocks = 2 - PRUint32 c1 = 0xCC9E2D51; - PRUint32 c2 = 0x1B873593; - PRUint32 c3 = 0xE6546B64; - PRUint32 c4 = 0x85EBCA6B; - PRUint32 c5 = 0xC2B2AE35; - PRUint32 h1 = aPref; // seed - PRUint32 k1; - PRUint32 karr[2]; - - karr[0] = aDomain; - karr[1] = aKey; - - for (PRUint32 i = 0; i < 2; i++) { - k1 = karr[i]; - k1 *= c1; - k1 = (k1 << 15) | (k1 >> (32-15)); - k1 *= c2; - - h1 ^= k1; - h1 = (h1 << 13) | (h1 >> (32-13)); - h1 *= 5; - h1 += c3; - } - - h1 ^= 2; // len - // fmix - h1 ^= h1 >> 16; - h1 *= c4; - h1 ^= h1 >> 13; - h1 *= c5; - h1 ^= h1 >> 16; - - *aOut = h1; - - return NS_OK; -} - -nsresult nsUrlClassifierStore::ReadPrefixes(FallibleTArray& array, - PRUint32 aKey) -{ - mozStorageStatementScoper scoper(mAllPrefixGetStatement); - mozStorageStatementScoper scoperToo(mAllPrefixCountStatement); - bool hasMoreData; - PRUint32 pcnt = 0; - PRUint32 fcnt = 0; - -#if defined(PR_LOGGING) - PRIntervalTime clockStart = 0; - if (LOG_ENABLED()) { - clockStart = PR_IntervalNow(); - } -#endif - - // Make sure we allocate no more than we really need, so first - // check how much entries there are - if (NS_SUCCEEDED(mAllPrefixCountStatement->ExecuteStep(&hasMoreData)) && hasMoreData) { - PRUint32 count = mAllPrefixCountStatement->AsInt32(0); - if (!array.SetCapacity(count)) { - return NS_ERROR_OUT_OF_MEMORY; - } - } else { - return NS_ERROR_FILE_CORRUPTED; - } - - while (NS_SUCCEEDED(mAllPrefixGetStatement->ExecuteStep(&hasMoreData)) && hasMoreData) { - PRUint32 prefixval; - PRUint32 domainval; - PRUint32 size; - - const PRUint8 *blobdomain = mAllPrefixGetStatement->AsSharedBlob(0, &size); - if (!blobdomain || (size != DOMAIN_LENGTH)) - return false; - - domainval = *(reinterpret_cast(blobdomain)); - - const PRUint8 *blobprefix = mAllPrefixGetStatement->AsSharedBlob(1, &size); - if (!blobprefix || (size != PARTIAL_LENGTH)) { - const PRUint8 *blobfull = mAllPrefixGetStatement->AsSharedBlob(2, &size); - if (!blobfull || (size != COMPLETE_LENGTH)) { - prefixval = domainval; - fcnt++; - } else { - prefixval = *(reinterpret_cast(blobfull)); - } - } else { - prefixval = *(reinterpret_cast(blobprefix)); - } - - PRUint32 keyedVal; - nsresult rv = KeyedHash(prefixval, domainval, aKey, &keyedVal); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 *res = array.AppendElement(keyedVal); - MOZ_ASSERT(res != nsnull); - pcnt++; - // Normal DB size is about 500k entries. If we are getting 10x - // as much, the database must be corrupted. - if (pcnt > 5000000) { - return NS_ERROR_FILE_CORRUPTED; - } - } - - LOG(("SB prefixes: %d fulldomain: %d\n", pcnt, fcnt)); - -#if defined(PR_LOGGING) - if (LOG_ENABLED()) { - PRIntervalTime clockEnd = PR_IntervalNow(); - LOG(("Gathering took %dms\n", - PR_IntervalToMilliseconds(clockEnd - clockStart))); - } -#endif - - return NS_OK; -} - -bool nsUrlClassifierDBServiceWorker::LockPrefixSet() -{ - mPrefixSetEnabledLock.Lock(); - return mPrefixSetEnabled; -} - -void nsUrlClassifierDBServiceWorker::UnlockPrefixSet() -{ - mPrefixSetEnabledLock.Unlock(); -} - -nsresult -nsUrlClassifierDBServiceWorker::ConstructPrefixSet() -{ - Telemetry::AutoTimer timer; - - PRUint32 key; - nsresult rv = mPrefixSet->GetKey(&key); - NS_ENSURE_SUCCESS(rv, rv); - - FallibleTArray array; - rv = mMainStore.ReadPrefixes(array, key); - if (NS_FAILED(rv)) { - goto error_bailout; - } - -#ifdef HASHFUNCTION_COLLISION_TEST - array.Sort(); - PRUint32 collisions = 0; - for (int i = 1; i < array.Length(); i++) { - if (array[i - 1] == array[i]) { - collisions++; - } - } - LOG(("%d collisions in the set", collisions)); -#endif - - if (array.IsEmpty()) { - // DB is empty, put a sentinel to show that we loaded it - if (!array.AppendElement(0)) { - goto error_bailout; - } - } - // SetPrefixes requires sorted arrays - array.Sort(); - - // construct new prefixset - rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length()); - if (NS_FAILED(rv)) { - goto error_bailout; - } - - // store the new tree to disk - rv = mPrefixSet->StoreToFile(mPSFile); - NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store the prefixset"); - - // re-enable prefixset usage if disabled earlier - mPrefixSetEnabled = true; - - return NS_OK; - - error_bailout: - // disable prefixset usage - MutexAutoLock lock(mPrefixSetEnabledLock); - mPrefixSetEnabled = false; - // load an empty prefixset - nsAutoTArray sentinel; - sentinel.Clear(); - sentinel.AppendElement(0); - mPrefixSet->SetPrefixes(sentinel.Elements(), sentinel.Length()); - if (rv == NS_ERROR_OUT_OF_MEMORY) { - Telemetry::Accumulate(Telemetry::URLCLASSIFIER_PS_OOM, 1); - } - return rv; -} - -nsresult -nsUrlClassifierDBServiceWorker::LoadPrefixSet(nsCOMPtr & aFile) -{ - bool empty; - nsresult rv = mPrefixSet->IsEmpty(&empty); - NS_ENSURE_SUCCESS(rv, rv); - - if (!empty) { - LOG(("PrefixSet already loaded, not loading again")); - return NS_OK; - } - - bool exists; - rv = aFile->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - -#if defined(PR_LOGGING) - PRIntervalTime clockStart = 0; - if (LOG_ENABLED()) { - clockStart = PR_IntervalNow(); - } -#endif - - if (exists) { - Telemetry::AutoTimer timer; - LOG(("stored PrefixSet exists, loading from disk")); - rv = mPrefixSet->LoadFromFile(aFile); - } - if (!exists || NS_FAILED(rv)) { - LOG(("no (usable) stored PrefixSet found, constructing from store")); - rv = ConstructPrefixSet(); - NS_ENSURE_SUCCESS(rv, rv); - } - -#ifdef DEBUG - LOG(("SB tree done, size = %d bytes\n", - mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of))); -#endif -#if defined(PR_LOGGING) - if (LOG_ENABLED()) { - PRIntervalTime clockEnd = PR_IntervalNow(); - LOG(("Loading took %dms\n", - PR_IntervalToMilliseconds(clockEnd - clockStart))); - } -#endif - - return NS_OK; -} - -nsresult -nsUrlClassifierDBServiceWorker::MaybeCreateTables(mozIStorageConnection* connection) -{ - LOG(("MaybeCreateTables\n")); - - nsresult rv = connection->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_classifier" - " (id INTEGER PRIMARY KEY," - " domain BLOB," - " partial_data BLOB," - " complete_data BLOB," - " chunk_id INTEGER," - " table_id INTEGER)")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS" - " moz_classifier_domain_index" - " ON moz_classifier(domain)")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS" - " moz_classifier_chunk_index" - " ON moz_classifier(chunk_id)")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_subs" - " (id INTEGER PRIMARY KEY," - " domain BLOB," - " partial_data BLOB," - " complete_data BLOB," - " chunk_id INTEGER," - " table_id INTEGER," - " add_chunk_id INTEGER)")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS" - " moz_subs_addchunk_index" - " ON moz_subs(add_chunk_id)")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS" - " moz_subs_chunk_index" - " ON moz_subs(chunk_id)")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = connection->ExecuteSimpleSQL( - NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_tables" - " (id INTEGER PRIMARY KEY," - " name TEXT," - " add_chunks TEXT," - " sub_chunks TEXT);")); - NS_ENSURE_SUCCESS(rv, rv); - - return rv; -} - // ------------------------------------------------------------------------- // nsUrlClassifierLookupCallback // @@ -3879,14 +835,16 @@ public: , mCallback(c) {} + ~nsUrlClassifierLookupCallback(); + private: nsresult HandleResults(); nsRefPtr mDBService; - nsAutoPtr > mResults; + nsAutoPtr mResults; // Completed results to send back to the worker for caching. - nsAutoPtr > mCacheResults; + nsAutoPtr mCacheResults; PRUint32 mPendingCompletions; nsCOMPtr mCallback; @@ -3896,8 +854,18 @@ NS_IMPL_THREADSAFE_ISUPPORTS2(nsUrlClassifierLookupCallback, nsIUrlClassifierLookupCallback, nsIUrlClassifierHashCompleterCallback) +nsUrlClassifierLookupCallback::~nsUrlClassifierLookupCallback() +{ + nsCOMPtr thread; + (void)NS_GetMainThread(getter_AddRefs(thread)); + + if (mCallback) { + (void)NS_ProxyRelease(thread, mCallback, false); + } +} + NS_IMETHODIMP -nsUrlClassifierLookupCallback::LookupComplete(nsTArray* results) +nsUrlClassifierLookupCallback::LookupComplete(nsTArray* results) { NS_ASSERTION(mResults == nsnull, "Should only get one set of results per nsUrlClassifierLookupCallback!"); @@ -3908,22 +876,19 @@ nsUrlClassifierLookupCallback::LookupComplete(nsTArraySort(); // Check the results entries that need to be completed. for (PRUint32 i = 0; i < results->Length(); i++) { - nsUrlClassifierLookupResult& result = results->ElementAt(i); + LookupResult& result = results->ElementAt(i); // We will complete partial matches and matches that are stale. - if (!result.mConfirmed) { + if (!result.Confirmed()) { nsCOMPtr completer; if (mDBService->GetCompleter(result.mTableName, getter_AddRefs(completer))) { nsCAutoString partialHash; - PRUint8 *buf = - result.mEntry.mHavePartial ? result.mEntry.mPartialHash.buf - : result.mEntry.mCompleteHash.buf; - partialHash.Assign(reinterpret_cast(buf), PARTIAL_LENGTH); + partialHash.Assign(reinterpret_cast(&result.hash.prefix), + PREFIX_SIZE); nsresult rv = completer->Complete(partialHash, this); if (NS_SUCCEEDED(rv)) { @@ -3931,12 +896,9 @@ nsUrlClassifierLookupCallback::LookupComplete(nsTArrayCacheCompletions(mCacheResults.forget()); - } } return NS_OK; @@ -3983,51 +939,33 @@ nsUrlClassifierLookupCallback::Completion(const nsACString& completeHash, { LOG(("nsUrlClassifierLookupCallback::Completion [%p, %s, %d, %d]", this, PromiseFlatCString(tableName).get(), chunkId, verified)); - nsUrlClassifierCompleteHash hash; + mozilla::safebrowsing::Completion hash; hash.Assign(completeHash); + // Send this completion to the store for caching. + if (!mCacheResults) { + mCacheResults = new CacheResultArray(); + if (!mCacheResults) + return NS_ERROR_OUT_OF_MEMORY; + } + + if (verified) { + CacheResult result; + result.entry.addChunk = chunkId; + result.entry.hash.complete = hash; + result.table = tableName; + + // OK if this fails, we just won't cache the item. + mCacheResults->AppendElement(result); + } + + // Check if this matched any of our results. for (PRUint32 i = 0; i < mResults->Length(); i++) { - nsUrlClassifierLookupResult& result = mResults->ElementAt(i); - - // First, see if this result can be used to update an entry. - if (verified && - !result.mEntry.mHaveComplete && - hash.StartsWith(result.mEntry.mPartialHash) && - result.mTableName == tableName && - result.mEntry.mChunkId == chunkId) { - // We have a completion for this entry. Fill it in... - result.mEntry.SetHash(hash); - - if (!mCacheResults) { - mCacheResults = new nsTArray(); - if (!mCacheResults) - return NS_ERROR_OUT_OF_MEMORY; - } - - mCacheResults->AppendElement(result); - } + LookupResult& result = mResults->ElementAt(i); // Now, see if it verifies a lookup - if (result.mLookupFragment == hash) { - result.mConfirmed = true; - - if (result.mTableName != tableName || - result.mEntry.mChunkId != chunkId) { - // The hash we got for this completion matches the hash we - // looked up, but doesn't match the table/chunk id. This could - // happen in rare cases where a given URL was moved between - // lists or added/removed/re-added to the list in the time since - // we've updated. - // - // Update the lookup result, but don't update the entry or try - // cache the results of this completion, as it might confuse - // things. - result.mTableName = tableName; - NS_WARNING("Accepting a gethash with an invalid table name or chunk id"); - LOG(("Tablename: %s ?= %s, ChunkId %d ?= %d", - result.mTableName.get(), PromiseFlatCString(tableName).get(), - result.mEntry.mChunkId, chunkId)); - } + if (result.CompleteHash() == hash && result.mTableName.Equals(tableName)) { + result.mProtocolConfirmed = true; } } @@ -4042,29 +980,56 @@ nsUrlClassifierLookupCallback::HandleResults() return mCallback->HandleEvent(NS_LITERAL_CSTRING("")); } + nsTArray tables; // Build a stringified list of result tables. - mResults->Sort(); - PRUint32 lastTableId = 0; - nsCAutoString tables; for (PRUint32 i = 0; i < mResults->Length(); i++) { - nsUrlClassifierLookupResult& result = mResults->ElementAt(i); + LookupResult& result = mResults->ElementAt(i); + // Leave out results that weren't confirmed, as their existence on // the list can't be verified. Also leave out randomly-generated // noise. - if (!result.mConfirmed || result.mNoise) + if (!result.Confirmed() || result.mNoise) { + LOG(("Skipping result from table %s", result.mTableName.get())); continue; - - if (tables.Length() > 0) { - if (lastTableId == result.mEntry.mTableId) - continue; - tables.Append(","); } - tables.Append(result.mTableName); - lastTableId = result.mEntry.mTableId; + LOG(("Confirmed result from table %s", result.mTableName.get())); + + if (tables.IndexOf(result.mTableName) == nsTArray::NoIndex) { + tables.AppendElement(result.mTableName); + } } - return mCallback->HandleEvent(tables); + // Some parts of this gethash request generated no hits at all. + // Prefixes must have been removed from the database since our last update. + // Save the prefixes we checked to prevent repeated requests + // until the next update. + nsAutoPtr cacheMisses(new PrefixArray()); + if (cacheMisses) { + for (uint32 i = 0; i < mResults->Length(); i++) { + LookupResult &result = mResults->ElementAt(i); + if (!result.Confirmed()) { + cacheMisses->AppendElement(result.PrefixHash()); + } + } + // Hands ownership of the miss array back to the worker thread. + mDBService->CacheMisses(cacheMisses.forget()); + } + + if (mCacheResults) { + // This hands ownership of the cache results array back to the worker + // thread. + mDBService->CacheCompletions(mCacheResults.forget()); + } + + nsCAutoString tableStr; + for (PRUint32 i = 0; i < tables.Length(); i++) { + if (i != 0) + tableStr.Append(','); + tableStr.Append(tables[i]); + } + + return mCallback->HandleEvent(tableStr); } @@ -4180,18 +1145,7 @@ nsUrlClassifierDBService::Init() gUrlClassifierDbServiceLog = PR_NewLogModule("UrlClassifierDbService"); #endif - // Force the storage service to be created on the main thread. nsresult rv; - nsCOMPtr storageService = - do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - // Force PSM to be loaded on the main thread. - mHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - mPrefixSet = new nsUrlClassifierPrefixSet(); - NS_ENSURE_SUCCESS(rv, rv); // Should we check document loads for malware URIs? nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); @@ -4225,20 +1179,19 @@ nsUrlClassifierDBService::Init() PR_ATOMIC_SET(&gFreshnessGuarantee, NS_SUCCEEDED(rv) ? tmpint : CONFIRM_AGE_DEFAULT_SEC); prefs->AddObserver(CONFIRM_AGE_PREF, this, false); + } - rv = prefs->GetIntPref(UPDATE_CACHE_SIZE_PREF, &tmpint); - PR_ATOMIC_SET(&gUpdateCacheSize, NS_SUCCEEDED(rv) ? tmpint : UPDATE_CACHE_SIZE_DEFAULT); + // Force PSM loading on main thread + nsCOMPtr acryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); - rv = prefs->GetIntPref(LOOKUP_CACHE_SIZE_PREF, &tmpint); - PR_ATOMIC_SET(&gLookupCacheSize, NS_SUCCEEDED(rv) ? tmpint : LOOKUP_CACHE_SIZE_DEFAULT); - - rv = prefs->GetIntPref(UPDATE_WORKING_TIME, &tmpint); - PR_ATOMIC_SET(&gWorkingTimeThreshold, - NS_SUCCEEDED(rv) ? tmpint : UPDATE_WORKING_TIME_DEFAULT); - - rv = prefs->GetIntPref(UPDATE_DELAY_TIME, &tmpint); - PR_ATOMIC_SET(&gDelayTime, - NS_SUCCEEDED(rv) ? tmpint : UPDATE_DELAY_TIME_DEFAULT); + // Directory providers must also be accessed on the main thread. + nsCOMPtr cacheDir; + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, + getter_AddRefs(cacheDir)); + if (NS_FAILED(rv)) { + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(cacheDir)); } // Start the background thread. @@ -4250,7 +1203,7 @@ nsUrlClassifierDBService::Init() if (!mWorker) return NS_ERROR_OUT_OF_MEMORY; - rv = mWorker->Init(gethashNoise, mPrefixSet); + rv = mWorker->Init(gethashNoise, cacheDir); if (NS_FAILED(rv)) { mWorker = nsnull; return rv; @@ -4339,11 +1292,7 @@ nsUrlClassifierDBService::LookupURI(nsIURI* uri, if (forceLookup) { *didLookup = true; } else { - // Check if the URI is clean. If so, we don't need to - // bother queueing up a lookup, we can just return.; - bool clean; - rv = CheckClean(key, &clean); - NS_ENSURE_SUCCESS(rv, rv); + bool clean = false; if (!clean) { nsCOMPtr permissionManager = @@ -4482,13 +1431,21 @@ nsUrlClassifierDBService::ResetDatabase() } nsresult -nsUrlClassifierDBService::CacheCompletions(nsTArray *results) +nsUrlClassifierDBService::CacheCompletions(CacheResultArray *results) { NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED); return mWorkerProxy->CacheCompletions(results); } +nsresult +nsUrlClassifierDBService::CacheMisses(PrefixArray *results) +{ + NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED); + + return mWorkerProxy->CacheMisses(results); +} + bool nsUrlClassifierDBService::GetCompleter(const nsACString &tableName, nsIUrlClassifierHashCompleter **completer) @@ -4531,24 +1488,6 @@ nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic, PRInt32 tmpint; rv = prefs->GetIntPref(CONFIRM_AGE_PREF, &tmpint); PR_ATOMIC_SET(&gFreshnessGuarantee, NS_SUCCEEDED(rv) ? tmpint : CONFIRM_AGE_DEFAULT_SEC); - } else if (NS_LITERAL_STRING(UPDATE_CACHE_SIZE_PREF).Equals(aData)) { - PRInt32 tmpint; - rv = prefs->GetIntPref(UPDATE_CACHE_SIZE_PREF, &tmpint); - PR_ATOMIC_SET(&gUpdateCacheSize, NS_SUCCEEDED(rv) ? tmpint : UPDATE_CACHE_SIZE_DEFAULT); - } else if (NS_LITERAL_STRING(LOOKUP_CACHE_SIZE_PREF).Equals(aData)) { - PRInt32 tmpint; - rv = prefs->GetIntPref(LOOKUP_CACHE_SIZE_PREF, &tmpint); - PR_ATOMIC_SET(&gLookupCacheSize, NS_SUCCEEDED(rv) ? tmpint : LOOKUP_CACHE_SIZE_DEFAULT); - } else if (NS_LITERAL_STRING(UPDATE_WORKING_TIME).Equals(aData)) { - PRInt32 tmpint; - rv = prefs->GetIntPref(UPDATE_WORKING_TIME, &tmpint); - PR_ATOMIC_SET(&gWorkingTimeThreshold, - NS_SUCCEEDED(rv) ? tmpint : UPDATE_WORKING_TIME_DEFAULT); - } else if (NS_LITERAL_STRING(UPDATE_DELAY_TIME).Equals(aData)) { - PRInt32 tmpint; - rv = prefs->GetIntPref(UPDATE_DELAY_TIME, &tmpint); - PR_ATOMIC_SET(&gDelayTime, - NS_SUCCEEDED(rv) ? tmpint : UPDATE_DELAY_TIME_DEFAULT); } } else if (!strcmp(aTopic, "profile-before-change") || !strcmp(aTopic, "xpcom-shutdown-threads")) { diff --git a/toolkit/components/url-classifier/nsUrlClassifierDBService.h b/toolkit/components/url-classifier/nsUrlClassifierDBService.h index b49303a41c2b..122102de87dc 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierDBService.h +++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h @@ -53,6 +53,8 @@ #include "nsICryptoHash.h" #include "nsICryptoHMAC.h" +#include "LookupCache.h" + // The hash length for a domain key. #define DOMAIN_LENGTH 4 @@ -88,7 +90,8 @@ public: bool GetCompleter(const nsACString& tableName, nsIUrlClassifierHashCompleter** completer); - nsresult CacheCompletions(nsTArray *results); + nsresult CacheCompletions(mozilla::safebrowsing::CacheResultArray *results); + nsresult CacheMisses(mozilla::safebrowsing::PrefixArray *results); static nsIThread* BackgroundThread(); @@ -131,10 +134,6 @@ private: // The list of tables that can use the default hash completer object. nsTArray mGethashWhitelist; - // Set of prefixes known to be in the database - nsRefPtr mPrefixSet; - nsCOMPtr mHash; - // Thread that we do the updates on. static nsIThread* gDbBackgroundThread; }; diff --git a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp index 9bc52e067833..dd528d13a8f9 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp @@ -71,7 +71,7 @@ static const PRLogModuleInfo *gUrlClassifierPrefixSetLog = nsnull; class nsPrefixSetReporter : public nsIMemoryReporter { public: - nsPrefixSetReporter(nsUrlClassifierPrefixSet * aParent, const nsACString & aName); + nsPrefixSetReporter(nsUrlClassifierPrefixSet* aParent, const nsACString& aName); virtual ~nsPrefixSetReporter() {}; NS_DECL_ISUPPORTS @@ -79,7 +79,7 @@ public: private: nsCString mPath; - nsUrlClassifierPrefixSet * mParent; + nsUrlClassifierPrefixSet* mParent; }; NS_IMPL_THREADSAFE_ISUPPORTS1(nsPrefixSetReporter, nsIMemoryReporter) @@ -87,8 +87,8 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsPrefixSetReporter, nsIMemoryReporter) NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(StoragePrefixSetMallocSizeOf, "storage/prefixset") -nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet * aParent, - const nsACString & aName) +nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet* aParent, + const nsACString& aName) : mParent(aParent) { mPath.Assign(NS_LITERAL_CSTRING("explicit/storage/prefixset")); @@ -99,42 +99,42 @@ nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet * aParent, } NS_IMETHODIMP -nsPrefixSetReporter::GetProcess(nsACString & aProcess) +nsPrefixSetReporter::GetProcess(nsACString& aProcess) { aProcess.Truncate(); return NS_OK; } NS_IMETHODIMP -nsPrefixSetReporter::GetPath(nsACString & aPath) +nsPrefixSetReporter::GetPath(nsACString& aPath) { aPath.Assign(mPath); return NS_OK; } NS_IMETHODIMP -nsPrefixSetReporter::GetKind(PRInt32 * aKind) +nsPrefixSetReporter::GetKind(PRInt32* aKind) { *aKind = nsIMemoryReporter::KIND_HEAP; return NS_OK; } NS_IMETHODIMP -nsPrefixSetReporter::GetUnits(PRInt32 * aUnits) +nsPrefixSetReporter::GetUnits(PRInt32* aUnits) { *aUnits = nsIMemoryReporter::UNITS_BYTES; return NS_OK; } NS_IMETHODIMP -nsPrefixSetReporter::GetAmount(PRInt64 * aAmount) +nsPrefixSetReporter::GetAmount(PRInt64* aAmount) { *aAmount = mParent->SizeOfIncludingThis(StoragePrefixSetMallocSizeOf); return NS_OK; } NS_IMETHODIMP -nsPrefixSetReporter::GetDescription(nsACString & aDescription) +nsPrefixSetReporter::GetDescription(nsACString& aDescription) { aDescription.Assign(NS_LITERAL_CSTRING("Memory used by a PrefixSet for " "UrlClassifier, in bytes.")); @@ -146,21 +146,21 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSe nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet() : mPrefixSetLock("mPrefixSetLock"), mSetIsReady(mPrefixSetLock, "mSetIsReady"), - mHasPrefixes(false), - mRandomKey(0) + mHasPrefixes(false) { #if defined(PR_LOGGING) if (!gUrlClassifierPrefixSetLog) gUrlClassifierPrefixSetLog = PR_NewLogModule("UrlClassifierPrefixSet"); #endif +} - nsresult rv = InitKey(); - if (NS_FAILED(rv)) { - LOG(("Failed to initialize PrefixSet")); - } - - mReporter = new nsPrefixSetReporter(this, NS_LITERAL_CSTRING("all")); +NS_IMETHODIMP +nsUrlClassifierPrefixSet::Init(const nsACString& aName) +{ + mReporter = new nsPrefixSetReporter(this, aName); NS_RegisterMemoryReporter(mReporter); + + return NS_OK; } nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet() @@ -168,26 +168,8 @@ nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet() NS_UnregisterMemoryReporter(mReporter); } -nsresult -nsUrlClassifierPrefixSet::InitKey() -{ - nsCOMPtr rg = - do_GetService("@mozilla.org/security/random-generator;1"); - NS_ENSURE_STATE(rg); - - PRUint8 *temp; - nsresult rv = rg->GenerateRandomBytes(sizeof(mRandomKey), &temp); - NS_ENSURE_SUCCESS(rv, rv); - memcpy(&mRandomKey, temp, sizeof(mRandomKey)); - NS_Free(temp); - - LOG(("Initialized PrefixSet, key = %X", mRandomKey)); - - return NS_OK; -} - NS_IMETHODIMP -nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32 * aArray, PRUint32 aLength) +nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32* aArray, PRUint32 aLength) { if (aLength <= 0) { MutexAutoLock lock(mPrefixSetLock); @@ -206,7 +188,7 @@ nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32 * aArray, PRUint32 aLength) } nsresult -nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32 * prefixes, PRUint32 aLength) +nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32* aPrefixes, PRUint32 aLength) { if (aLength == 0) { return NS_OK; @@ -214,7 +196,7 @@ nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32 * prefixes, PRUint32 aLen #ifdef DEBUG for (PRUint32 i = 1; i < aLength; i++) { - MOZ_ASSERT(prefixes[i] >= prefixes[i-1]); + MOZ_ASSERT(aPrefixes[i] >= aPrefixes[i-1]); } #endif @@ -222,7 +204,7 @@ nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32 * prefixes, PRUint32 aLen FallibleTArray newIndexStarts; FallibleTArray newDeltas; - if (!newIndexPrefixes.AppendElement(prefixes[0])) { + if (!newIndexPrefixes.AppendElement(aPrefixes[0])) { return NS_ERROR_OUT_OF_MEMORY; } if (!newIndexStarts.AppendElement(newDeltas.Length())) { @@ -230,25 +212,25 @@ nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32 * prefixes, PRUint32 aLen } PRUint32 numOfDeltas = 0; - PRUint32 currentItem = prefixes[0]; + PRUint32 currentItem = aPrefixes[0]; for (PRUint32 i = 1; i < aLength; i++) { if ((numOfDeltas >= DELTAS_LIMIT) || - (prefixes[i] - currentItem >= MAX_INDEX_DIFF)) { + (aPrefixes[i] - currentItem >= MAX_INDEX_DIFF)) { if (!newIndexStarts.AppendElement(newDeltas.Length())) { return NS_ERROR_OUT_OF_MEMORY; } - if (!newIndexPrefixes.AppendElement(prefixes[i])) { + if (!newIndexPrefixes.AppendElement(aPrefixes[i])) { return NS_ERROR_OUT_OF_MEMORY; } numOfDeltas = 0; } else { - PRUint16 delta = prefixes[i] - currentItem; + PRUint16 delta = aPrefixes[i] - currentItem; if (!newDeltas.AppendElement(delta)) { return NS_ERROR_OUT_OF_MEMORY; } numOfDeltas++; } - currentItem = prefixes[i]; + currentItem = aPrefixes[i]; } newIndexPrefixes.Compact(); @@ -271,6 +253,53 @@ nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32 * prefixes, PRUint32 aLen return NS_OK; } +NS_IMETHODIMP +nsUrlClassifierPrefixSet::GetPrefixes(PRUint32* aCount, + PRUint32** aPrefixes) +{ + NS_ENSURE_ARG_POINTER(aCount); + *aCount = 0; + NS_ENSURE_ARG_POINTER(aPrefixes); + *aPrefixes = nsnull; + + nsTArray aArray; + PRUint32 prefixLength = mIndexPrefixes.Length(); + + for (PRUint32 i = 0; i < prefixLength; i++) { + PRUint32 prefix = mIndexPrefixes[i]; + PRUint32 start = mIndexStarts[i]; + PRUint32 end = (i == (prefixLength - 1)) ? mDeltas.Length() + : mIndexStarts[i + 1]; + aArray.AppendElement(prefix); + for (PRUint32 j = start; j < end; j++) { + prefix += mDeltas[j]; + aArray.AppendElement(prefix); + } + } + + NS_ASSERTION(mIndexStarts.Length() + mDeltas.Length() == aArray.Length(), + "Lengths are inconsistent"); + + PRUint32 itemCount = aArray.Length(); + + if (itemCount == 1 && aArray[0] == 0) { + /* sentinel for empty set */ + aArray.Clear(); + itemCount = 0; + } + + PRUint32* retval = static_cast(nsMemory::Alloc(itemCount * sizeof(PRUint32))); + NS_ENSURE_TRUE(retval, NS_ERROR_OUT_OF_MEMORY); + for (PRUint32 i = 0; i < itemCount; i++) { + retval[i] = aArray[i]; + } + + *aCount = itemCount; + *aPrefixes = retval; + + return NS_OK; +} + PRUint32 nsUrlClassifierPrefixSet::BinSearch(PRUint32 start, PRUint32 end, PRUint32 target) @@ -290,7 +319,7 @@ PRUint32 nsUrlClassifierPrefixSet::BinSearch(PRUint32 start, } nsresult -nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, bool * aFound) +nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, bool* aFound) { mPrefixSetLock.AssertCurrentThreadOwns(); @@ -366,32 +395,13 @@ nsUrlClassifierPrefixSet::IsEmpty(bool * aEmpty) } NS_IMETHODIMP -nsUrlClassifierPrefixSet::GetKey(PRUint32 * aKey) - { - MutexAutoLock lock(mPrefixSetLock); - *aKey = mRandomKey; - return NS_OK; -} - -NS_IMETHODIMP -nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, PRUint32 aKey, +nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, bool* aReady, bool* aFound) { MutexAutoLock lock(mPrefixSetLock); *aFound = false; - // We might have raced here with a LoadPrefixSet call, - // loading a saved PrefixSet with another key than the one used to probe us. - // This must occur exactly between the GetKey call and the Probe call. - // This could cause a false negative immediately after browser start. - // Claim we are still busy loading instead. - if (aKey != mRandomKey) { - LOG(("Potential race condition detected, avoiding")); - *aReady = false; - return NS_OK; - } - // check whether we are opportunistically probing or should wait if (*aReady) { // we should block until we are ready @@ -415,7 +425,7 @@ nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, PRUint32 aKey, } nsresult -nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose & fileFd) +nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose& fileFd) { PRUint32 magic; PRInt32 read; @@ -427,8 +437,6 @@ nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose & fileFd) PRUint32 indexSize; PRUint32 deltaSize; - read = PR_Read(fileFd, &mRandomKey, sizeof(PRUint32)); - NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED); read = PR_Read(fileFd, &indexSize, sizeof(PRUint32)); NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED); read = PR_Read(fileFd, &deltaSize, sizeof(PRUint32)); @@ -481,8 +489,10 @@ nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose & fileFd) } NS_IMETHODIMP -nsUrlClassifierPrefixSet::LoadFromFile(nsIFile * aFile) +nsUrlClassifierPrefixSet::LoadFromFile(nsIFile* aFile) { + Telemetry::AutoTimer timer; + nsresult rv; nsCOMPtr file(do_QueryInterface(aFile, &rv)); NS_ENSURE_SUCCESS(rv, rv); @@ -495,7 +505,7 @@ nsUrlClassifierPrefixSet::LoadFromFile(nsIFile * aFile) } nsresult -nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose & fileFd) +nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose& fileFd) { { Telemetry::AutoTimer timer; @@ -511,9 +521,6 @@ nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose & fileFd) written = PR_Write(fileFd, &magic, sizeof(PRUint32)); NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE); - written = PR_Write(fileFd, &mRandomKey, sizeof(PRUint32)); - NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE); - PRUint32 indexSize = mIndexStarts.Length(); PRUint32 deltaSize = mDeltas.Length(); written = PR_Write(fileFd, &indexSize, sizeof(PRUint32)); @@ -536,7 +543,7 @@ nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose & fileFd) } NS_IMETHODIMP -nsUrlClassifierPrefixSet::StoreToFile(nsIFile * aFile) +nsUrlClassifierPrefixSet::StoreToFile(nsIFile* aFile) { if (!mHasPrefixes) { LOG(("Attempt to serialize empty PrefixSet")); diff --git a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h index deda71e0feba..35963462d182 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h +++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h @@ -44,6 +44,7 @@ #include "nsISupportsUtils.h" #include "nsID.h" #include "nsIFile.h" +#include "nsIMutableArray.h" #include "nsIUrlClassifierPrefixSet.h" #include "nsIMemoryReporter.h" #include "nsToolkitCompsCID.h" @@ -59,12 +60,13 @@ public: nsUrlClassifierPrefixSet(); virtual ~nsUrlClassifierPrefixSet(); + NS_IMETHOD Init(const nsACString& aName); NS_IMETHOD SetPrefixes(const PRUint32* aArray, PRUint32 aLength); - NS_IMETHOD Probe(PRUint32 aPrefix, PRUint32 aKey, bool* aReady, bool* aFound); - NS_IMETHOD IsEmpty(bool * aEmpty); + NS_IMETHOD GetPrefixes(PRUint32* aCount, PRUint32** aPrefixes); + NS_IMETHOD Probe(PRUint32 aPrefix, bool* aReady, bool* aFound); + NS_IMETHOD IsEmpty(bool* aEmpty); NS_IMETHOD LoadFromFile(nsIFile* aFile); NS_IMETHOD StoreToFile(nsIFile* aFile); - NS_IMETHOD GetKey(PRUint32* aKey); NS_DECL_ISUPPORTS @@ -84,15 +86,12 @@ protected: nsresult Contains(PRUint32 aPrefix, bool* aFound); nsresult MakePrefixSet(const PRUint32* aArray, PRUint32 aLength); PRUint32 BinSearch(PRUint32 start, PRUint32 end, PRUint32 target); - nsresult LoadFromFd(mozilla::AutoFDClose & fileFd); - nsresult StoreToFd(mozilla::AutoFDClose & fileFd); - nsresult InitKey(); + nsresult LoadFromFd(mozilla::AutoFDClose& fileFd); + nsresult StoreToFd(mozilla::AutoFDClose& fileFd); // boolean indicating whether |setPrefixes| has been // called with a non-empty array. bool mHasPrefixes; - // key used to randomize hash collisions - PRUint32 mRandomKey; // the prefix for each index. FallibleTArray mIndexPrefixes; // the value corresponds to the beginning of the run @@ -100,7 +99,6 @@ protected: FallibleTArray mIndexStarts; // array containing deltas from indices. FallibleTArray mDeltas; - }; #endif diff --git a/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp b/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp index dabcf59ac38e..6d9b2732eae3 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp @@ -183,7 +183,7 @@ UrlClassifierDBServiceWorkerProxy::CloseDb() } NS_IMETHODIMP -UrlClassifierDBServiceWorkerProxy::CacheCompletions(nsTArray* aEntries) +UrlClassifierDBServiceWorkerProxy::CacheCompletions(CacheResultArray * aEntries) { nsCOMPtr r = new CacheCompletionsRunnable(mTarget, aEntries); return DispatchToWorkerThread(r); @@ -196,12 +196,27 @@ UrlClassifierDBServiceWorkerProxy::CacheCompletionsRunnable::Run() return NS_OK; } +NS_IMETHODIMP +UrlClassifierDBServiceWorkerProxy::CacheMisses(PrefixArray * aEntries) +{ + nsCOMPtr r = new CacheMissesRunnable(mTarget, aEntries); + return DispatchToWorkerThread(r); +} + +NS_IMETHODIMP +UrlClassifierDBServiceWorkerProxy::CacheMissesRunnable::Run() +{ + mTarget->CacheMisses(mEntries); + return NS_OK; +} + + NS_IMPL_THREADSAFE_ISUPPORTS1(UrlClassifierLookupCallbackProxy, nsIUrlClassifierLookupCallback) NS_IMETHODIMP UrlClassifierLookupCallbackProxy::LookupComplete - (nsTArray* aResults) + (LookupResultArray * aResults) { nsCOMPtr r = new LookupCompleteRunnable(mTarget, aResults); return NS_DispatchToMainThread(r); diff --git a/toolkit/components/url-classifier/nsUrlClassifierProxies.h b/toolkit/components/url-classifier/nsUrlClassifierProxies.h index 843f43a2673d..9cb9303c97f1 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierProxies.h +++ b/toolkit/components/url-classifier/nsUrlClassifierProxies.h @@ -40,6 +40,9 @@ #include "nsIUrlClassifierDBService.h" #include "nsThreadUtils.h" +#include "LookupCache.h" + +using namespace mozilla::safebrowsing; /** * Thread proxy from the main thread to the worker thread. @@ -150,7 +153,7 @@ public: { public: CacheCompletionsRunnable(nsIUrlClassifierDBServiceWorker* aTarget, - nsTArray* aEntries) + CacheResultArray *aEntries) : mTarget(aTarget) , mEntries(aEntries) { } @@ -159,7 +162,23 @@ public: private: nsCOMPtr mTarget; - nsTArray* mEntries; + CacheResultArray *mEntries; + }; + + class CacheMissesRunnable : public nsRunnable + { + public: + CacheMissesRunnable(nsIUrlClassifierDBServiceWorker* aTarget, + PrefixArray *aEntries) + : mTarget(aTarget) + , mEntries(aEntries) + { } + + NS_DECL_NSIRUNNABLE + + private: + nsCOMPtr mTarget; + PrefixArray *mEntries; }; private: @@ -182,7 +201,7 @@ public: { public: LookupCompleteRunnable(nsIUrlClassifierLookupCallback* aTarget, - nsTArray* aResults) + LookupResultArray *aResults) : mTarget(aTarget) , mResults(aResults) { } @@ -191,7 +210,7 @@ public: private: nsCOMPtr mTarget; - nsTArray* mResults; + LookupResultArray * mResults; }; private: diff --git a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp index 9466a0e89b7d..9f2199e08f63 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp @@ -171,11 +171,16 @@ nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl, const nsACString & aStreamTable, const nsACString & aServerMAC) { + LOG(("(pre) Fetching update from %s\n", PromiseFlatCString(aUpdateUrl).get())); + nsCOMPtr uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), aUpdateUrl); NS_ENSURE_SUCCESS(rv, rv); - LOG(("Fetching update from %s\n", PromiseFlatCString(aUpdateUrl).get())); + nsCAutoString urlSpec; + uri->GetAsciiSpec(urlSpec); + + LOG(("(post) Fetching update from %s\n", urlSpec.get())); return FetchUpdate(uri, aRequestBody, aStreamTable, aServerMAC); } @@ -240,6 +245,11 @@ nsUrlClassifierStreamUpdater::DownloadUpdates( mIsUpdating = true; *_retval = true; + nsCAutoString urlSpec; + mUpdateUrl->GetAsciiSpec(urlSpec); + + LOG(("FetchUpdate: %s", urlSpec.get())); + //LOG(("requestBody: %s", aRequestBody.get())); return FetchUpdate(mUpdateUrl, aRequestBody, EmptyCString(), EmptyCString()); } diff --git a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js index c9cf375cd30d..56e59389d9db 100644 --- a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js +++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js @@ -24,14 +24,26 @@ prefBranch.setIntPref("urlclassifier.gethashnoise", 0); prefBranch.setBoolPref("browser.safebrowsing.malware.enabled", true); prefBranch.setBoolPref("browser.safebrowsing.enabled", true); -function cleanUp() { +function delFile(name) { try { // Delete a previously created sqlite file var file = dirSvc.get('ProfLD', Ci.nsIFile); - file.append("urlclassifier3.sqlite"); + file.append(name); if (file.exists()) file.remove(false); - } catch (e) {} + } catch(e) { + } +} + +function cleanUp() { + delFile("classifier.hashkey"); + delFile("urlclassifier3.sqlite"); + delFile("safebrowsing/test-phish-simple.sbstore"); + delFile("safebrowsing/test-malware-simple.sbstore"); + delFile("safebrowsing/test-phish-simple.cache"); + delFile("safebrowsing/test-malware-simple.cache"); + delFile("safebrowsing/test-phish-simple.pset"); + delFile("safebrowsing/test-malware-simple.pset"); } var dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIUrlClassifierDBService); @@ -276,11 +288,10 @@ function runNextTest() dbservice.resetDatabase(); dbservice.setHashCompleter('test-phish-simple', null); - dumpn("running " + gTests[gNextTest]); - dump("running " + gTests[gNextTest]); - - gTests[gNextTest++](); + let test = gTests[gNextTest++]; + dump("running " + test.name + "\n"); + test(); } function runTests(tests) diff --git a/toolkit/components/url-classifier/tests/unit/test_addsub.js b/toolkit/components/url-classifier/tests/unit/test_addsub.js index 1303bd399ba4..1ed65c7baad5 100644 --- a/toolkit/components/url-classifier/tests/unit/test_addsub.js +++ b/toolkit/components/url-classifier/tests/unit/test_addsub.js @@ -55,6 +55,7 @@ function testSimpleSub() "chunkType" : "s", "urls": subUrls }]); + var assertions = { "tableData" : "test-phish-simple;a:1:s:50", "urlsExist" : [ "bar.com/b" ], @@ -361,7 +362,8 @@ function testExpireLists() { { "chunkType" : "sd:1-3,5" }]); var assertions = { - "tableData" : "test-phish-simple;" + // "tableData" : "test-phish-simple;" + "tableData": "" }; doTest([addUpdate, subUpdate, expireUpdate], assertions); @@ -479,10 +481,7 @@ function run_test() testSubPartiallyMatches2, testSubsDifferentChunks, testSubsDifferentChunksSameHostId, - testExpireLists, - testDuplicateAddChunks, - testExpireWholeSub, - testPreventWholeSub, + testExpireLists ]); } diff --git a/toolkit/components/url-classifier/tests/unit/test_cleankeycache.js b/toolkit/components/url-classifier/tests/unit/test_cleankeycache.js deleted file mode 100644 index d2e22d0c4749..000000000000 --- a/toolkit/components/url-classifier/tests/unit/test_cleankeycache.js +++ /dev/null @@ -1,195 +0,0 @@ -//* -*- Mode: Javascript; tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*- * -// Test an add of two urls to a fresh database -function testCleanHostKeys() { - var addUrls = [ "foo.com/a" ]; - var update = buildPhishingUpdate( - [ - { "chunkNum" : 1, - "urls" : addUrls - }]); - - doStreamUpdate(update, function() { - var ios = Components.classes["@mozilla.org/network/io-service;1"]. - getService(Components.interfaces.nsIIOService); - - // Check with a clean host key - var uri = ios.newURI("http://bar.com/a", null, null); - - // Use the nsIURIClassifier interface (the - // nsIUrlClassifierDBService will always queue a lookup, - // nsIURIClassifier won't if the host key is known to be clean. - var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier); - var result = classifier.classify(uri, function(errorCode) { - var result2 = classifier.classify(uri, function() { - do_throw("shouldn't get a callback"); - }); - // second call shouldn't result in a callback. - do_check_eq(result2, false); - do_throw("shouldn't get a callback"); - }); - - // The first classifier call will not result in a callback - do_check_eq(result, false); - runNextTest(); - }, updateError); -} - -// Make sure that an update properly clears the host key cache -function testUpdate() { - var ios = Components.classes["@mozilla.org/network/io-service;1"]. - getService(Components.interfaces.nsIIOService); - - // Must put something in the PrefixSet - var preUrls = [ "foo.com/b" ]; - var preUpdate = buildPhishingUpdate( - [ - { "chunkNum" : 1, - "urls" : preUrls - }]); - - doStreamUpdate(preUpdate, function() { - // First lookup won't happen... - var uri = ios.newURI("http://foo.com/a", null, null); - - // Use the nsIURIClassifier interface (the - // nsIUrlClassifierDBService will always queue a lookup, - // nsIURIClassifier won't if the host key is known to be clean. - var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier); - var result = classifier.classify(uri, function(errorCode) { - // shouldn't arrive here - do_check_eq(errorCode, Cr.NS_OK); - do_throw("shouldn't get a callback"); - }); - do_check_eq(result, false); - - // Now add the url to the db... - var addUrls = [ "foo.com/a" ]; - var update = buildPhishingUpdate( - [ - { "chunkNum" : 2, - "urls" : addUrls - }]); - doStreamUpdate(update, function() { - var result2 = classifier.classify(uri, function(errorCode) { - do_check_neq(errorCode, Cr.NS_OK); - runNextTest(); - }); - // second call should result in a callback. - do_check_eq(result2, true); - }, updateError); - }, updateError); -} - -function testResetFullCache() { - // Must put something in the PrefixSet - var preUrls = [ "zaz.com/b" ]; - var preUpdate = buildPhishingUpdate( - [ - { "chunkNum" : 1, - "urls" : preUrls - }]); - - doStreamUpdate(preUpdate, function() { - // First do enough queries to fill up the clean hostkey cache - var ios = Components.classes["@mozilla.org/network/io-service;1"]. - getService(Components.interfaces.nsIIOService); - - // Use the nsIURIClassifier interface (the - // nsIUrlClassifierDBService will always queue a lookup, - // nsIURIClassifier won't if the host key is known to be clean. - var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier); - - var uris1 = [ - "www.foo.com/", - "www.bar.com/", - "www.blah.com/", - "www.site.com/", - "www.example.com/", - "www.test.com/", - "www.malware.com/", - "www.phishing.com/", - "www.clean.com/" ]; - - var uris2 = []; - - var runSecondLookup = function() { - if (uris2.length == 0) { - runNextTest(); - return; - } - - var spec = uris2.pop(); - var uri = ios.newURI("http://" + spec, null, null); - - var result = classifier.classify(uri, function(errorCode) { - }); - runSecondLookup(); - // now look up a few more times. - } - - var runInitialLookup = function() { - if (uris1.length == 0) { - // We're done filling up the cache. Run an update to flush it, - // then start lookup up again. - var addUrls = [ "notgoingtocheck.com/a" ]; - var update = buildPhishingUpdate( - [ - { "chunkNum" : 1, - "urls" : addUrls - }]); - doStreamUpdate(update, function() { - runSecondLookup(); - }, updateError); - return; - } - var spec = uris1.pop(); - - uris2.push(spec); - var uri = ios.newURI("http://" + spec, null, null); - var result = classifier.classify(uri, function(errorCode) { - }); - runInitialLookup(); - // None of these will generate a callback - do_check_eq(result, false); - if (!result) { - doNextTest(); - } - } - - // XXX bug 457790: dbservice.resetDatabase() doesn't have a way to - // wait to make sure it has been applied. Until this is added, we'll - // just use a timeout. - var t = new Timer(3000, runInitialLookup); - }, updateError); -} - -function testBug475436() { - var addUrls = [ "foo.com/a", "www.foo.com/" ]; - var update = buildPhishingUpdate( - [ - { "chunkNum" : 1, - "urls" : addUrls - }]); - - var assertions = { - "tableData" : "test-phish-simple;a:1", - "urlsExist" : ["foo.com/a", "foo.com/a" ] - }; - - doUpdateTest([update], assertions, runNextTest, updateError); -} - -function run_test() -{ - runTests([ - // XXX: We need to run testUpdate first, because of a - // race condition (bug 457790) calling dbservice.classify() - // directly after dbservice.resetDatabase(). - testUpdate, - testCleanHostKeys, - testResetFullCache, - testBug475436 - ]); -} - -do_test_pending(); diff --git a/toolkit/components/url-classifier/tests/unit/test_partial.js b/toolkit/components/url-classifier/tests/unit/test_partial.js index 13df8eca09a8..2e997bc36d78 100644 --- a/toolkit/components/url-classifier/tests/unit/test_partial.js +++ b/toolkit/components/url-classifier/tests/unit/test_partial.js @@ -461,7 +461,8 @@ function testWrongTable() "tableData" : "test-phish-simple;a:1", // The urls were added as phishing urls, but the completer is claiming // that they are malware urls, and we trust the completer in this case. - "malwareUrlsExist" : addUrls, + // The result will be discarded, so we can only check for non-existence. + "urlsDontExist" : addUrls, // Make sure the completer was actually queried. "completerQueried" : [completer, addUrls] }; @@ -470,57 +471,14 @@ function testWrongTable() function() { // Give the dbservice a chance to (not) cache the result. var timer = new Timer(3000, function() { - // The dbservice shouldn't have cached this result, - // so this completer should be queried. - var newCompleter = installCompleter('test-malware-simple', [[1, addUrls]], []); - - // The above installCompleter installs the - // completer for test-malware-simple, we want it - // to be used for test-phish-simple too. - dbservice.setHashCompleter("test-phish-simple", + // The miss earlier will have caused a miss to be cached. + // Resetting the completer does not count as an update, + // so we will not be probed again. + var newCompleter = installCompleter('test-malware-simple', [[1, addUrls]], []); dbservice.setHashCompleter("test-phish-simple", newCompleter); - var assertions = { - "malwareUrlsExist" : addUrls, - "completerQueried" : [newCompleter, addUrls] - }; - checkAssertions(assertions, runNextTest); - }); - }, updateError); -} - -function testWrongChunk() -{ - var addUrls = [ "foo.com/a" ]; - var update = buildPhishingUpdate( - [ - { "chunkNum" : 1, - "urls" : addUrls - }], - 4); - var completer = installCompleter('test-phish-simple', - [[2, // wrong chunk number - addUrls]], []); - - var assertions = { - "tableData" : "test-phish-simple;a:1", - "urlsExist" : addUrls, - // Make sure the completer was actually queried. - "completerQueried" : [completer, addUrls] - }; - - doUpdateTest([update], assertions, - function() { - // Give the dbservice a chance to (not) cache the result. - var timer = new Timer(3000, function() { - // The dbservice shouldn't have cached this result, - // so this completer should be queried. - var newCompleter = installCompleter('test-phish-simple', [[2, addUrls]], []); - - var assertions = { - "urlsExist" : addUrls, - "completerQueried" : [newCompleter, addUrls] + "urlsDontExist" : addUrls }; checkAssertions(assertions, runNextTest); }); @@ -818,7 +776,6 @@ function run_test() testMixedSizesDifferentDomains, testInvalidHashSize, testWrongTable, - testWrongChunk, testCachedResults, testCachedResultsWithSub, testCachedResultsWithExpire, @@ -826,7 +783,7 @@ function run_test() testStaleList, testStaleListEmpty, testErrorList, - testErrorListIndependent, + testErrorListIndependent ]); } diff --git a/toolkit/components/url-classifier/tests/unit/test_prefixset.js b/toolkit/components/url-classifier/tests/unit/test_prefixset.js index b71d3ccd66f2..72927a194d13 100644 --- a/toolkit/components/url-classifier/tests/unit/test_prefixset.js +++ b/toolkit/components/url-classifier/tests/unit/test_prefixset.js @@ -1,7 +1,9 @@ // newPset: returns an empty nsIUrlClassifierPrefixSet. function newPset() { - return Cc["@mozilla.org/url-classifier/prefixset;1"] - .createInstance(Ci.nsIUrlClassifierPrefixSet); + let pset = Cc["@mozilla.org/url-classifier/prefixset;1"] + .createInstance(Ci.nsIUrlClassifierPrefixSet); + pset.init("all"); + return pset; } // arrContains: returns true if |arr| contains the element |target|. Uses binary @@ -28,10 +30,22 @@ function arrContains(arr, target) { return (!(i < 0 || i >= arr.length) && arr[i] == target); } +// checkContents: Check whether the PrefixSet pset contains +// the prefixes in the passed array. +function checkContents(pset, prefixes) { + var outcount = {}, outset = {}; + outset = pset.getPrefixes(outcount); + let inset = prefixes; + do_check_eq(inset.length, outset.length); + inset.sort(function(x,y) x - y); + for (let i = 0; i < inset.length; i++) { + do_check_eq(inset[i], outset[i]); + } +} + function wrappedProbe(pset, prefix) { - let key = pset.getKey(); let dummy = {}; - return pset.probe(prefix, key, dummy); + return pset.probe(prefix, dummy); }; // doRandomLookups: we use this to test for false membership with random input @@ -74,6 +88,9 @@ function testBasicPset() { do_check_true(wrappedProbe(pset, 1593203)); do_check_false(wrappedProbe(pset, 999)); do_check_false(wrappedProbe(pset, 0)); + + + checkContents(pset, prefixes); } function testDuplicates() { @@ -88,6 +105,9 @@ function testDuplicates() { do_check_true(wrappedProbe(pset, 9)); do_check_false(wrappedProbe(pset, 4)); do_check_false(wrappedProbe(pset, 8)); + + + checkContents(pset, prefixes); } function testSimplePset() { @@ -97,6 +117,9 @@ function testSimplePset() { doRandomLookups(pset, prefixes, 100); doExpectedLookups(pset, prefixes, 1); + + + checkContents(pset, prefixes); } function testReSetPrefixes() { @@ -113,6 +136,9 @@ function testReSetPrefixes() { for (let i = 0; i < prefixes.length; i++) { do_check_false(wrappedProbe(pset, prefixes[i])); } + + + checkContents(pset, secondPrefixes); } function testLargeSet() { @@ -131,6 +157,9 @@ function testLargeSet() { doExpectedLookups(pset, arr, 1); doRandomLookups(pset, arr, 1000); + + + checkContents(pset, arr); } function testTinySet() { @@ -141,10 +170,12 @@ function testTinySet() { do_check_true(wrappedProbe(pset, 1)); do_check_false(wrappedProbe(pset, 100000)); + checkContents(pset, prefixes); prefixes = []; pset.setPrefixes(prefixes, prefixes.length); do_check_false(wrappedProbe(pset, 1)); + checkContents(pset, prefixes); } let tests = [testBasicPset, diff --git a/toolkit/components/url-classifier/tests/unit/test_streamupdater.js b/toolkit/components/url-classifier/tests/unit/test_streamupdater.js index 224cc871c2c3..c934890dba0e 100644 --- a/toolkit/components/url-classifier/tests/unit/test_streamupdater.js +++ b/toolkit/components/url-classifier/tests/unit/test_streamupdater.js @@ -80,8 +80,6 @@ function testSimpleForward() { // Make sure that a nested forward (a forward within a forward) causes // the update to fail. function testNestedForward() { - testFillDb(); // Make sure the db isn't empty - var add1Urls = [ "foo.com/a", "bar.com/c" ]; var add2Urls = [ "foo.com/b" ]; @@ -203,8 +201,6 @@ function testValidMAC() { // Test a simple update with an invalid message authentication code. function testInvalidMAC() { - testFillDb(); // Make sure the db isn't empty - var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ]; var update = buildPhishingUpdate( [ @@ -224,8 +220,6 @@ function testInvalidMAC() { // Test a simple update without a message authentication code, when it is // expecting one. function testNoMAC() { - testFillDb(); // Make sure the db isn't empty - var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ]; var update = buildPhishingUpdate( [ @@ -282,8 +276,6 @@ function testValidForwardMAC() { // Test an update with a valid message authentication code, but with // invalid MACs on the forwards. function testInvalidForwardMAC() { - testFillDb(); // Make sure the db isn't empty - var add1Urls = [ "foo.com/a", "bar.com/c" ]; var add2Urls = [ "foo.com/b" ]; var add3Urls = [ "bar.com/d" ]; @@ -323,8 +315,6 @@ function testInvalidForwardMAC() { // Test an update with a valid message authentication code, but no MAC // specified for sub-urls. function testNoForwardMAC() { - testFillDb(); // Make sure the db isn't empty - var add1Urls = [ "foo.com/a", "bar.com/c" ]; var add2Urls = [ "foo.com/b" ]; var add3Urls = [ "bar.com/d" ]; @@ -391,8 +381,6 @@ gAssertions.gotRekey = function(data, cb) // Tests a rekey request. function testRekey() { - testFillDb(); - var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ]; var update = buildPhishingUpdate( [ @@ -457,6 +445,9 @@ function run_test() testInvalidUrlForward, testErrorUrlForward, testMultipleTables, + testReset, + // XXX: we're currently "once MAC, always MAC", + // so any test not using a MAC must go above testValidMAC, testInvalidMAC, testNoMAC, @@ -464,7 +455,6 @@ function run_test() testInvalidForwardMAC, testNoForwardMAC, testRekey, - testReset, ]); } diff --git a/toolkit/components/url-classifier/tests/unit/xpcshell.ini b/toolkit/components/url-classifier/tests/unit/xpcshell.ini index 8de09eda577c..e80ef1e69613 100644 --- a/toolkit/components/url-classifier/tests/unit/xpcshell.ini +++ b/toolkit/components/url-classifier/tests/unit/xpcshell.ini @@ -4,7 +4,6 @@ tail = tail_urlclassifier.js [test_addsub.js] [test_backoff.js] -[test_cleankeycache.js] [test_dbservice.js] [test_hashcompleter.js] [test_partial.js] From 55b1ebcf0ab5593169eb42bd3977e2885d97bb59 Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Wed, 1 Feb 2012 11:55:19 -0500 Subject: [PATCH 35/90] Bug 722155 - crash in nsBaseWidget::SetZIndex from uninitialized window variable. r=roc --- view/src/nsView.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/view/src/nsView.cpp b/view/src/nsView.cpp index 6bf7ca5200bf..f6c81e61be74 100644 --- a/view/src/nsView.cpp +++ b/view/src/nsView.cpp @@ -208,6 +208,8 @@ nsView::nsView(nsViewManager* aViewManager, nsViewVisibility aVisibility) mDeletionObserver = nsnull; mHaveInvalidationDimensions = false; mWidgetIsTopLevel = false; + mParent = nsnull; + mWindow = nsnull; } void nsView::DropMouseGrabbing() From 9936de5b97aeccce048d01c5d91858c6af581990 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 1 Feb 2012 12:19:46 -0500 Subject: [PATCH 36/90] Bug 720144 - Add a new panning correctness test. r=jmaher --- .../base/tests/MotionEventHelper.java.in | 95 +++++++++++++++++++ mobile/android/base/tests/robocop.ini | 1 + .../base/tests/testPanCorrectness.java.in | 54 +++++++++++ 3 files changed, 150 insertions(+) create mode 100644 mobile/android/base/tests/MotionEventHelper.java.in create mode 100644 mobile/android/base/tests/testPanCorrectness.java.in diff --git a/mobile/android/base/tests/MotionEventHelper.java.in b/mobile/android/base/tests/MotionEventHelper.java.in new file mode 100644 index 000000000000..c1146f1654fe --- /dev/null +++ b/mobile/android/base/tests/MotionEventHelper.java.in @@ -0,0 +1,95 @@ +#filter substitution +package @ANDROID_PACKAGE_NAME@.tests; + +import @ANDROID_PACKAGE_NAME@.*; + +import android.app.Instrumentation; +import android.os.SystemClock; +import android.util.Log; +import android.view.MotionEvent; + +class MotionEventHelper { + private static final String LOGTAG = "RobocopMotionEventHelper"; + + private static final long DRAG_EVENTS_PER_SECOND = 20; // 20 move events per second when doing a drag + + private final Instrumentation mInstrumentation; + private final int mSurfaceOffsetX; + private final int mSurfaceOffsetY; + + public MotionEventHelper(Instrumentation inst, int surfaceOffsetX, int surfaceOffsetY) { + mInstrumentation = inst; + mSurfaceOffsetX = surfaceOffsetX; + mSurfaceOffsetY = surfaceOffsetY; + Log.i(LOGTAG, "Initialized using offset (" + mSurfaceOffsetX + "," + mSurfaceOffsetY + ")"); + } + + public long down(float x, float y) { + Log.d(LOGTAG, "Triggering down at (" + x + "," + y + ")"); + long downTime = SystemClock.uptimeMillis(); + MotionEvent event = MotionEvent.obtain(downTime, downTime, MotionEvent.ACTION_DOWN, mSurfaceOffsetX + x, mSurfaceOffsetY + y, 0); + mInstrumentation.sendPointerSync(event); + return downTime; + } + + public long move(long downTime, float x, float y) { + Log.d(LOGTAG, "Triggering move to (" + x + "," + y + ")"); + MotionEvent event = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_MOVE, mSurfaceOffsetX + x, mSurfaceOffsetY + y, 0); + mInstrumentation.sendPointerSync(event); + return downTime; + } + + public long up(long downTime, float x, float y) { + Log.d(LOGTAG, "Triggering up at (" + x + "," + y + ")"); + MotionEvent event = MotionEvent.obtain(downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, mSurfaceOffsetX + x, mSurfaceOffsetY + y, 0); + mInstrumentation.sendPointerSync(event); + return -1L; + } + + public Thread dragAsync(final float startX, final float startY, final float endX, final float endY, final long durationMillis) { + Thread t = new Thread() { + public void run() { + int numEvents = (int)(durationMillis * DRAG_EVENTS_PER_SECOND / 1000); + float eventDx = (endX - startX) / numEvents; + float eventDy = (endY - startY) / numEvents; + long downTime = down(startX, startY); + for (int i = 0; i < numEvents - 1; i++) { + downTime = move(downTime, startX + (eventDx * i), startY + (eventDy * i)); + try { + Thread.sleep(1000L / DRAG_EVENTS_PER_SECOND); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + // do the last one using endX/endY directly to avoid rounding errors + downTime = move(downTime, endX, endY); + downTime = up(downTime, endX, endY); + } + }; + t.start(); + return t; + } + + public void dragSync(float startX, float startY, float endX, float endY, long durationMillis) { + try { + dragAsync(startX, startY, endX, endY, durationMillis).join(); + mInstrumentation.waitForIdleSync(); + } catch (InterruptedException ie) { + ie.printStackTrace(); + } + } + + public void dragSync(float startX, float startY, float endX, float endY) { + dragSync(startX, startY, endX, endY, 1000); + } + + public void tap(float x, float y) { + long downTime = down(x, y); + downTime = up(downTime, x, y); + } + + public void doubleTap(float x, float y) { + tap(x, y); + tap(x, y); + } +} diff --git a/mobile/android/base/tests/robocop.ini b/mobile/android/base/tests/robocop.ini index cd6f3d29855d..f01213af29d7 100644 --- a/mobile/android/base/tests/robocop.ini +++ b/mobile/android/base/tests/robocop.ini @@ -2,6 +2,7 @@ [testBookmark] [testLoad] [testNewTab] +[testPanCorrectness] # Used for Talos, please don't use in mochitest #[testPan] diff --git a/mobile/android/base/tests/testPanCorrectness.java.in b/mobile/android/base/tests/testPanCorrectness.java.in new file mode 100644 index 000000000000..11e871b2256a --- /dev/null +++ b/mobile/android/base/tests/testPanCorrectness.java.in @@ -0,0 +1,54 @@ +#filter substitution +package @ANDROID_PACKAGE_NAME@.tests; + +import @ANDROID_PACKAGE_NAME@.*; +import android.app.Instrumentation; + +public class testPanCorrectness extends BaseTest { + private static final String URL = "http://mochi.test:8888/tests/robocop/robocop_boxes.html"; + + private void checkPixels(int[][] colors) { + mActions.expectPaint().blockForEvent(); + getInstrumentation().waitForIdleSync(); + + int[][] painted = mDriver.getPaintedSurface(); + mAsserter.ispixel(painted[0][0], colors[0][0], colors[0][1], colors[0][2], "Pixel at 0, 0"); + mAsserter.ispixel(painted[0][100], colors[1][0], colors[1][1], colors[1][2], "Pixel at 100, 0"); + mAsserter.ispixel(painted[100][0], colors[2][0], colors[2][1], colors[2][2], "Pixel at 0, 100"); + mAsserter.ispixel(painted[100][100], colors[3][0], colors[3][1], colors[3][2], "Pixel at 100, 100"); + } + + public void testPanCorrectness() { + loadUrl(URL); + + MotionEventHelper meh = new MotionEventHelper(getInstrumentation(), mDriver.getGeckoLeft(), mDriver.getGeckoTop()); + + checkPixels(new int[][] { + /* at 0, 0 */ /* at 100, 0 */ + { 0, 255, 255 }, { 100, 255, 245 }, + + /* at 0, 100 */ /* at 100, 100 */ + { 100, 245, 255 }, { 200, 245, 245 }, + }); + + // drag page upwards by 100 pixels + meh.dragSync(10, 150, 10, 50); + checkPixels(new int[][] { + /* 0, 100 now at 0, 0 */ /* 100, 100 now at 100, 0 */ + { 100, 245, 255 }, { 200, 245, 245 }, + + /* 0, 200 now at 0, 100 */ /* 100, 200 now at 100, 100 */ + { 200, 235, 255 }, { 45, 235, 245 }, + }); + + // drag page leftwards by 100 pixels + meh.dragSync(150, 10, 50, 10); + checkPixels(new int[][] { + /* 100, 100 now at 0, 0 */ /* 200, 100 now at 100, 0 */ + { 200, 245, 245 }, { 45, 245, 235 }, + + /* 100, 200 now at 0, 100 */ /* 200, 200 now at 100, 100 */ + { 45, 235, 245 }, { 145, 235, 235 }, + }); + } +} From ddbc4e3a61605231d7a2c654147edbf039c60cfb Mon Sep 17 00:00:00 2001 From: Blake Kaplan Date: Thu, 26 Jan 2012 14:55:27 +0100 Subject: [PATCH 37/90] Bug 723111 - Add an option to js::UnwrapObject so that it doesn't stop at outer windows when unwrapping. This is useful for cases where we are trying to find the wrapped native for a security wrapper. r=bzbarsky --HG-- extra : rebase_source : 51216cf21b63554e50633c89f393c5888241dee2 --- js/src/jscompartment.cpp | 2 +- js/src/jswrapper.cpp | 4 ++-- js/src/jswrapper.h | 9 ++++++++- js/xpconnect/src/XPCConvert.cpp | 2 +- js/xpconnect/src/XPCQuickStubs.cpp | 2 +- js/xpconnect/src/XPCWrapper.cpp | 4 ++-- js/xpconnect/src/XPCWrapper.h | 2 +- js/xpconnect/wrappers/AccessCheck.cpp | 12 +++++++++++- js/xpconnect/wrappers/WrapperFactory.h | 2 +- 9 files changed, 28 insertions(+), 11 deletions(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index d22ef17f6136..8ab058472f7d 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -216,7 +216,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp) /* Don't unwrap an outer window proxy. */ if (!obj->getClass()->ext.innerObject) { - obj = UnwrapObject(&vp->toObject(), &flags); + obj = UnwrapObject(&vp->toObject(), true, &flags); vp->setObject(*obj); if (obj->compartment() == this) return true; diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 567d004900c2..5d2c7156eb30 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -76,13 +76,13 @@ js::IsWrapper(const JSObject *wrapper) } JS_FRIEND_API(JSObject *) -js::UnwrapObject(JSObject *wrapped, uintN *flagsp) +js::UnwrapObject(JSObject *wrapped, bool stopAtOuter, uintN *flagsp) { uintN flags = 0; while (wrapped->isWrapper()) { flags |= static_cast(GetProxyHandler(wrapped))->flags(); wrapped = GetProxyPrivate(wrapped).toObjectOrNull(); - if (wrapped->getClass()->ext.innerObject) + if (stopAtOuter && wrapped->getClass()->ext.innerObject) break; } if (flagsp) diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index a86af286b9ba..e9ddd8fc7011 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -206,7 +206,14 @@ TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, J uintN flags); JS_FRIEND_API(bool) IsWrapper(const JSObject *obj); -JS_FRIEND_API(JSObject *) UnwrapObject(JSObject *obj, uintN *flagsp = NULL); + +// Given a JSObject, returns that object stripped of wrappers. If +// stopAtOuter is true, then this returns the outer window if it was +// previously wrapped. Otherwise, this returns the first object for +// which JSObject::isWrapper returns false. +JS_FRIEND_API(JSObject *) UnwrapObject(JSObject *obj, bool stopAtOuter = true, + uintN *flagsp = NULL); + bool IsCrossCompartmentWrapper(const JSObject *obj); } /* namespace js */ diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index a105034d6a21..59b4d51c684f 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -1220,7 +1220,7 @@ XPCConvert::JSObject2NativeInterface(XPCCallContext& ccx, // we aren't, throw an exception eagerly. JSObject* inner = nsnull; if (XPCWrapper::IsSecurityWrapper(src)) { - inner = XPCWrapper::Unwrap(cx, src); + inner = XPCWrapper::Unwrap(cx, src, false); if (!inner) { if (pErr) *pErr = NS_ERROR_XPC_SECURITY_MANAGER_VETO; diff --git a/js/xpconnect/src/XPCQuickStubs.cpp b/js/xpconnect/src/XPCQuickStubs.cpp index 4a651dca8cc7..2b79e145e1cd 100644 --- a/js/xpconnect/src/XPCQuickStubs.cpp +++ b/js/xpconnect/src/XPCQuickStubs.cpp @@ -788,7 +788,7 @@ getWrapper(JSContext *cx, XPCWrappedNativeTearOff **tearoff) { if (XPCWrapper::IsSecurityWrapper(obj) && - !(obj = XPCWrapper::Unwrap(cx, obj))) { + !(obj = XPCWrapper::Unwrap(cx, obj, false))) { return NS_ERROR_XPC_SECURITY_MANAGER_VETO; } diff --git a/js/xpconnect/src/XPCWrapper.cpp b/js/xpconnect/src/XPCWrapper.cpp index 9901c1b354d1..f66ed2e899be 100644 --- a/js/xpconnect/src/XPCWrapper.cpp +++ b/js/xpconnect/src/XPCWrapper.cpp @@ -123,12 +123,12 @@ AttachNewConstructorObject(XPCCallContext &ccx, JSObject *aGlobalObject) namespace XPCWrapper { JSObject * -Unwrap(JSContext *cx, JSObject *wrapper) +Unwrap(JSContext *cx, JSObject *wrapper, bool stopAtOuter) { if (js::IsWrapper(wrapper)) { if (xpc::AccessCheck::isScriptAccessOnly(cx, wrapper)) return nsnull; - return js::UnwrapObject(wrapper); + return js::UnwrapObject(wrapper, stopAtOuter); } return nsnull; diff --git a/js/xpconnect/src/XPCWrapper.h b/js/xpconnect/src/XPCWrapper.h index 8918e4466681..36c7205ae06a 100644 --- a/js/xpconnect/src/XPCWrapper.h +++ b/js/xpconnect/src/XPCWrapper.h @@ -93,7 +93,7 @@ IsSecurityWrapper(JSObject *wrapper) * exception on |cx|. */ JSObject * -Unwrap(JSContext *cx, JSObject *wrapper); +Unwrap(JSContext *cx, JSObject *wrapper, bool stopAtOuter = true); JSObject * UnsafeUnwrapSecurityWrapper(JSObject *obj); diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index c3a831b39503..540c3cf9a0ff 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -90,12 +90,22 @@ AccessCheck::isSameOrigin(JSCompartment *a, JSCompartment *b) bool AccessCheck::isLocationObjectSameOrigin(JSContext *cx, JSObject *wrapper) { + // Location objects are parented to the outer window for which they + // were created. This gives us an easy way to determine whether our + // object is same origin with the current inner window: + + // Grab the outer window... JSObject *obj = js::GetObjectParent(js::UnwrapObject(wrapper)); if (!js::GetObjectClass(obj)->ext.innerObject) { + // ...which might be wrapped in a security wrapper. obj = js::UnwrapObject(obj); JS_ASSERT(js::GetObjectClass(obj)->ext.innerObject); } + + // Now innerize it to find the *current* inner window for our outer. obj = JS_ObjectToInnerObject(cx, obj); + + // Which lets us compare the current compartment against the old one. return obj && (isSameOrigin(js::GetObjectCompartment(wrapper), js::GetObjectCompartment(obj)) || @@ -384,7 +394,7 @@ AccessCheck::isScriptAccessOnly(JSContext *cx, JSObject *wrapper) JS_ASSERT(js::IsWrapper(wrapper)); uintN flags; - JSObject *obj = js::UnwrapObject(wrapper, &flags); + JSObject *obj = js::UnwrapObject(wrapper, true, &flags); // If the wrapper indicates script-only access, we are done. if (flags & WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG) { diff --git a/js/xpconnect/wrappers/WrapperFactory.h b/js/xpconnect/wrappers/WrapperFactory.h index e33abca9f83c..1bda2340053d 100644 --- a/js/xpconnect/wrappers/WrapperFactory.h +++ b/js/xpconnect/wrappers/WrapperFactory.h @@ -53,7 +53,7 @@ class WrapperFactory { // Return true if any of any of the nested wrappers have the flag set. static bool HasWrapperFlag(JSObject *wrapper, uintN flag) { uintN flags = 0; - js::UnwrapObject(wrapper, &flags); + js::UnwrapObject(wrapper, true, &flags); return !!(flags & flag); } From c57a4567418d225890f6e3430665e34131e2eaf4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Wed, 1 Feb 2012 09:28:57 -0800 Subject: [PATCH 38/90] Bug 723148 - b2g build broken since Bug 721741 landed [r=snorp] --- dom/plugins/base/nsPluginInstanceOwner.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dom/plugins/base/nsPluginInstanceOwner.h b/dom/plugins/base/nsPluginInstanceOwner.h index a92f35e3ddbc..6ad8208102a8 100644 --- a/dom/plugins/base/nsPluginInstanceOwner.h +++ b/dom/plugins/base/nsPluginInstanceOwner.h @@ -304,7 +304,7 @@ public: bool UseAsyncRendering(); -#ifdef ANDROID +#ifdef MOZ_WIDGET_ANDROID nsIntRect GetVisibleRect() { return nsIntRect(0, 0, mPluginWindow->width, mPluginWindow->height); } @@ -335,7 +335,7 @@ private: } void FixUpURLS(const nsString &name, nsAString &value); -#ifdef ANDROID +#ifdef MOZ_WIDGET_ANDROID void SendSize(int width, int height); void SendOnScreenEvent(bool onScreen); From 19ee81d3dd8356a571323f32f6cfac6776f02e0e Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Wed, 1 Feb 2012 12:56:04 -0500 Subject: [PATCH 39/90] Bug 722155 - Backout 253a5461dec1, fix will not help. r=mats --- view/src/nsView.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/view/src/nsView.cpp b/view/src/nsView.cpp index f6c81e61be74..6bf7ca5200bf 100644 --- a/view/src/nsView.cpp +++ b/view/src/nsView.cpp @@ -208,8 +208,6 @@ nsView::nsView(nsViewManager* aViewManager, nsViewVisibility aVisibility) mDeletionObserver = nsnull; mHaveInvalidationDimensions = false; mWidgetIsTopLevel = false; - mParent = nsnull; - mWindow = nsnull; } void nsView::DropMouseGrabbing() From 5a0b40ee93db70087027a1be59c68d311e34971b Mon Sep 17 00:00:00 2001 From: Phil Ringnalda Date: Wed, 1 Feb 2012 10:25:04 -0800 Subject: [PATCH 40/90] Back out 9aac714775b9 (bug 723111) for make check orange --- js/src/jscompartment.cpp | 2 +- js/src/jswrapper.cpp | 4 ++-- js/src/jswrapper.h | 9 +-------- js/xpconnect/src/XPCConvert.cpp | 2 +- js/xpconnect/src/XPCQuickStubs.cpp | 2 +- js/xpconnect/src/XPCWrapper.cpp | 4 ++-- js/xpconnect/src/XPCWrapper.h | 2 +- js/xpconnect/wrappers/AccessCheck.cpp | 12 +----------- js/xpconnect/wrappers/WrapperFactory.h | 2 +- 9 files changed, 11 insertions(+), 28 deletions(-) diff --git a/js/src/jscompartment.cpp b/js/src/jscompartment.cpp index 8ab058472f7d..d22ef17f6136 100644 --- a/js/src/jscompartment.cpp +++ b/js/src/jscompartment.cpp @@ -216,7 +216,7 @@ JSCompartment::wrap(JSContext *cx, Value *vp) /* Don't unwrap an outer window proxy. */ if (!obj->getClass()->ext.innerObject) { - obj = UnwrapObject(&vp->toObject(), true, &flags); + obj = UnwrapObject(&vp->toObject(), &flags); vp->setObject(*obj); if (obj->compartment() == this) return true; diff --git a/js/src/jswrapper.cpp b/js/src/jswrapper.cpp index 5d2c7156eb30..567d004900c2 100644 --- a/js/src/jswrapper.cpp +++ b/js/src/jswrapper.cpp @@ -76,13 +76,13 @@ js::IsWrapper(const JSObject *wrapper) } JS_FRIEND_API(JSObject *) -js::UnwrapObject(JSObject *wrapped, bool stopAtOuter, uintN *flagsp) +js::UnwrapObject(JSObject *wrapped, uintN *flagsp) { uintN flags = 0; while (wrapped->isWrapper()) { flags |= static_cast(GetProxyHandler(wrapped))->flags(); wrapped = GetProxyPrivate(wrapped).toObjectOrNull(); - if (stopAtOuter && wrapped->getClass()->ext.innerObject) + if (wrapped->getClass()->ext.innerObject) break; } if (flagsp) diff --git a/js/src/jswrapper.h b/js/src/jswrapper.h index e9ddd8fc7011..a86af286b9ba 100644 --- a/js/src/jswrapper.h +++ b/js/src/jswrapper.h @@ -206,14 +206,7 @@ TransparentObjectWrapper(JSContext *cx, JSObject *obj, JSObject *wrappedProto, J uintN flags); JS_FRIEND_API(bool) IsWrapper(const JSObject *obj); - -// Given a JSObject, returns that object stripped of wrappers. If -// stopAtOuter is true, then this returns the outer window if it was -// previously wrapped. Otherwise, this returns the first object for -// which JSObject::isWrapper returns false. -JS_FRIEND_API(JSObject *) UnwrapObject(JSObject *obj, bool stopAtOuter = true, - uintN *flagsp = NULL); - +JS_FRIEND_API(JSObject *) UnwrapObject(JSObject *obj, uintN *flagsp = NULL); bool IsCrossCompartmentWrapper(const JSObject *obj); } /* namespace js */ diff --git a/js/xpconnect/src/XPCConvert.cpp b/js/xpconnect/src/XPCConvert.cpp index 59b4d51c684f..a105034d6a21 100644 --- a/js/xpconnect/src/XPCConvert.cpp +++ b/js/xpconnect/src/XPCConvert.cpp @@ -1220,7 +1220,7 @@ XPCConvert::JSObject2NativeInterface(XPCCallContext& ccx, // we aren't, throw an exception eagerly. JSObject* inner = nsnull; if (XPCWrapper::IsSecurityWrapper(src)) { - inner = XPCWrapper::Unwrap(cx, src, false); + inner = XPCWrapper::Unwrap(cx, src); if (!inner) { if (pErr) *pErr = NS_ERROR_XPC_SECURITY_MANAGER_VETO; diff --git a/js/xpconnect/src/XPCQuickStubs.cpp b/js/xpconnect/src/XPCQuickStubs.cpp index 2b79e145e1cd..4a651dca8cc7 100644 --- a/js/xpconnect/src/XPCQuickStubs.cpp +++ b/js/xpconnect/src/XPCQuickStubs.cpp @@ -788,7 +788,7 @@ getWrapper(JSContext *cx, XPCWrappedNativeTearOff **tearoff) { if (XPCWrapper::IsSecurityWrapper(obj) && - !(obj = XPCWrapper::Unwrap(cx, obj, false))) { + !(obj = XPCWrapper::Unwrap(cx, obj))) { return NS_ERROR_XPC_SECURITY_MANAGER_VETO; } diff --git a/js/xpconnect/src/XPCWrapper.cpp b/js/xpconnect/src/XPCWrapper.cpp index f66ed2e899be..9901c1b354d1 100644 --- a/js/xpconnect/src/XPCWrapper.cpp +++ b/js/xpconnect/src/XPCWrapper.cpp @@ -123,12 +123,12 @@ AttachNewConstructorObject(XPCCallContext &ccx, JSObject *aGlobalObject) namespace XPCWrapper { JSObject * -Unwrap(JSContext *cx, JSObject *wrapper, bool stopAtOuter) +Unwrap(JSContext *cx, JSObject *wrapper) { if (js::IsWrapper(wrapper)) { if (xpc::AccessCheck::isScriptAccessOnly(cx, wrapper)) return nsnull; - return js::UnwrapObject(wrapper, stopAtOuter); + return js::UnwrapObject(wrapper); } return nsnull; diff --git a/js/xpconnect/src/XPCWrapper.h b/js/xpconnect/src/XPCWrapper.h index 36c7205ae06a..8918e4466681 100644 --- a/js/xpconnect/src/XPCWrapper.h +++ b/js/xpconnect/src/XPCWrapper.h @@ -93,7 +93,7 @@ IsSecurityWrapper(JSObject *wrapper) * exception on |cx|. */ JSObject * -Unwrap(JSContext *cx, JSObject *wrapper, bool stopAtOuter = true); +Unwrap(JSContext *cx, JSObject *wrapper); JSObject * UnsafeUnwrapSecurityWrapper(JSObject *obj); diff --git a/js/xpconnect/wrappers/AccessCheck.cpp b/js/xpconnect/wrappers/AccessCheck.cpp index 540c3cf9a0ff..c3a831b39503 100644 --- a/js/xpconnect/wrappers/AccessCheck.cpp +++ b/js/xpconnect/wrappers/AccessCheck.cpp @@ -90,22 +90,12 @@ AccessCheck::isSameOrigin(JSCompartment *a, JSCompartment *b) bool AccessCheck::isLocationObjectSameOrigin(JSContext *cx, JSObject *wrapper) { - // Location objects are parented to the outer window for which they - // were created. This gives us an easy way to determine whether our - // object is same origin with the current inner window: - - // Grab the outer window... JSObject *obj = js::GetObjectParent(js::UnwrapObject(wrapper)); if (!js::GetObjectClass(obj)->ext.innerObject) { - // ...which might be wrapped in a security wrapper. obj = js::UnwrapObject(obj); JS_ASSERT(js::GetObjectClass(obj)->ext.innerObject); } - - // Now innerize it to find the *current* inner window for our outer. obj = JS_ObjectToInnerObject(cx, obj); - - // Which lets us compare the current compartment against the old one. return obj && (isSameOrigin(js::GetObjectCompartment(wrapper), js::GetObjectCompartment(obj)) || @@ -394,7 +384,7 @@ AccessCheck::isScriptAccessOnly(JSContext *cx, JSObject *wrapper) JS_ASSERT(js::IsWrapper(wrapper)); uintN flags; - JSObject *obj = js::UnwrapObject(wrapper, true, &flags); + JSObject *obj = js::UnwrapObject(wrapper, &flags); // If the wrapper indicates script-only access, we are done. if (flags & WrapperFactory::SCRIPT_ACCESS_ONLY_FLAG) { diff --git a/js/xpconnect/wrappers/WrapperFactory.h b/js/xpconnect/wrappers/WrapperFactory.h index 1bda2340053d..e33abca9f83c 100644 --- a/js/xpconnect/wrappers/WrapperFactory.h +++ b/js/xpconnect/wrappers/WrapperFactory.h @@ -53,7 +53,7 @@ class WrapperFactory { // Return true if any of any of the nested wrappers have the flag set. static bool HasWrapperFlag(JSObject *wrapper, uintN flag) { uintN flags = 0; - js::UnwrapObject(wrapper, true, &flags); + js::UnwrapObject(wrapper, &flags); return !!(flags & flag); } From a566382d1650738342b13292b5adf0a49328cde7 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Wed, 1 Feb 2012 10:32:00 -0800 Subject: [PATCH 41/90] Bug 721076 - Return correct prevent default value if there are no changed touches. r=smaug --- layout/base/nsPresShell.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/layout/base/nsPresShell.cpp b/layout/base/nsPresShell.cpp index 783a529b7060..b03bd89c9782 100644 --- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -6568,6 +6568,9 @@ PresShell::HandleEventInternal(nsEvent* aEvent, nsEventStatus* aStatus) } // is nothing has changed, we should just return if (!haveChanged) { + if (gPreventMouseEvents) { + *aStatus = nsEventStatus_eConsumeNoDefault; + } return NS_OK; } break; From 1312b9c6e7fcd73deb62c1b1cffa2e4cd9e98028 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Wed, 1 Feb 2012 10:33:33 -0800 Subject: [PATCH 42/90] Bug 721076 - Set correct prevent panning mode on touchdown. r=kats --- mobile/android/base/gfx/LayerController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/gfx/LayerController.java b/mobile/android/base/gfx/LayerController.java index eddd72a37e3d..950ec217d3cb 100644 --- a/mobile/android/base/gfx/LayerController.java +++ b/mobile/android/base/gfx/LayerController.java @@ -384,11 +384,13 @@ public class LayerController { public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); PointF point = new PointF(event.getX(), event.getY()); + if ((action & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_DOWN) { + mView.clearEventQueue(); initialTouchLocation = point; + allowDefaultActions = !mWaitForTouchListeners; post(new Runnable() { public void run() { - mView.clearEventQueue(); preventPanning(mWaitForTouchListeners); } }); From 9dd8dab90144700f4f8c15f40950bbe1115af558 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Wed, 1 Feb 2012 10:33:40 -0800 Subject: [PATCH 43/90] Bug 721080 - Pages with touch event listeners that don't call preventDefault should not hold up panning. r=dougt --- mobile/android/base/GeckoAppShell.java | 4 ++-- mobile/android/base/gfx/LayerController.java | 16 +++++++++------- widget/android/AndroidBridge.cpp | 6 +++--- widget/android/AndroidBridge.h | 4 ++-- widget/android/nsWindow.cpp | 5 +++-- 5 files changed, 19 insertions(+), 16 deletions(-) diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 630cf8953cfd..2f3157f73820 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -1121,11 +1121,11 @@ public class GeckoAppShell }); } - public static void preventPanning() { + public static void setPreventPanning(final boolean aPreventPanning) { getMainHandler().post(new Runnable() { public void run() { LayerController layerController = GeckoApp.mAppContext.getLayerController(); - layerController.preventPanning(true); + layerController.preventPanning(aPreventPanning); } }); } diff --git a/mobile/android/base/gfx/LayerController.java b/mobile/android/base/gfx/LayerController.java index 950ec217d3cb..37b02d7d5f8a 100644 --- a/mobile/android/base/gfx/LayerController.java +++ b/mobile/android/base/gfx/LayerController.java @@ -441,13 +441,15 @@ public class LayerController { allowDefaultTimer.purge(); allowDefaultTimer = null; } - allowDefaultActions = !aValue; - - if (aValue) { - mView.clearEventQueue(); - mPanZoomController.cancelTouch(); - } else { - mView.processEventQueue(); + if (aValue == allowDefaultActions) { + allowDefaultActions = !aValue; + + if (aValue) { + mView.clearEventQueue(); + mPanZoomController.cancelTouch(); + } else { + mView.processEventQueue(); + } } } diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index d9b1c75af395..64127c3d7d20 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -125,7 +125,7 @@ AndroidBridge::Init(JNIEnv *jEnv, jGetDpi = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getDpi", "()I"); jSetFullScreen = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setFullScreen", "(Z)V"); jShowInputMethodPicker = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showInputMethodPicker", "()V"); - jPreventPanning = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "preventPanning", "()V"); + jSetPreventPanning = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setPreventPanning", "(Z)V"); jHideProgressDialog = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "hideProgressDialog", "()V"); jPerformHapticFeedback = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "performHapticFeedback", "(Z)V"); jVibrate1 = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "vibrate", "(J)V"); @@ -1806,13 +1806,13 @@ NS_IMETHODIMP nsAndroidBridge::SetDrawMetadataProvider(nsIAndroidDrawMetadataPro } void -AndroidBridge::PreventPanning() { +AndroidBridge::SetPreventPanning(bool aPreventPanning) { ALOG_BRIDGE("AndroidBridge::PreventPanning"); JNIEnv *env = GetJNIEnv(); if (!env) return; - env->CallStaticVoidMethod(mGeckoAppShellClass, jPreventPanning); + env->CallStaticVoidMethod(mGeckoAppShellClass, jSetPreventPanning, (jboolean)aPreventPanning); } diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 9d0d2f164d5b..a236649fa003 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -230,7 +230,7 @@ public: void ShowInputMethodPicker(); - void PreventPanning(); + void SetPreventPanning(bool aPreventPanning); void HideProgressDialogOnce(); @@ -439,7 +439,7 @@ protected: jmethodID jGetDpi; jmethodID jSetFullScreen; jmethodID jShowInputMethodPicker; - jmethodID jPreventPanning; + jmethodID jSetPreventPanning; jmethodID jHideProgressDialog; jmethodID jPerformHapticFeedback; jmethodID jVibrate1; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 55a9ce3b7850..5f417a5e4ce4 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1579,8 +1579,9 @@ nsWindow::DispatchMultitouchEvent(nsTouchEvent &event, AndroidGeckoEvent *ae) nsEventStatus status; DispatchEvent(&event, status); - if (status == nsEventStatus_eConsumeNoDefault) { - AndroidBridge::Bridge()->PreventPanning(); + bool preventPanning = (status == nsEventStatus_eConsumeNoDefault); + if (preventPanning || action == AndroidMotionEvent::ACTION_MOVE) { + AndroidBridge::Bridge()->SetPreventPanning(preventPanning); return true; } return false; From eddc1669f9e10b398d3d41d679e07c6d3aa0298d Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 1 Feb 2012 13:54:18 -0500 Subject: [PATCH 44/90] Bug 718684 - Cleanup: remove trailing whitespace and unnecessary variables. r=sriram --- mobile/android/chrome/content/browser.js | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index f77e4e72b470..303b3c5820a8 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2654,20 +2654,21 @@ const ElementTouchHelper = { } return result; }, + getBoundingContentRect: function(aElement) { if (!aElement) return {x: 0, y: 0, w: 0, h: 0}; - + let document = aElement.ownerDocument; while (document.defaultView.frameElement) document = document.defaultView.frameElement.ownerDocument; - + let cwu = document.defaultView.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); let scrollX = {}, scrollY = {}; cwu.getScrollXY(false, scrollX, scrollY); - + let r = aElement.getBoundingClientRect(); - + // step out of iframes and frames, offsetting scroll values for (let frame = aElement.ownerDocument.defaultView; frame.frameElement && frame != content; frame = frame.parent) { // adjust client coordinates' origin to be top left of iframe viewport @@ -2678,14 +2679,10 @@ const ElementTouchHelper = { scrollY.value += rect.top + parseInt(top); } - var x = r.left + scrollX.value; - var y = r.top + scrollY.value; - var x2 = x + r.width; - var y2 = y + r.height; - return {x: x, - y: y, - w: x2 - x, - h: y2 - y}; + return {x: r.left + scrollX.value, + y: r.top + scrollY.value, + w: r.width, + h: r.height }; } }; From 0c79f4d2af25a2b7b5838a9b7c606394147753d9 Mon Sep 17 00:00:00 2001 From: Kartikaya Gupta Date: Wed, 1 Feb 2012 13:54:42 -0500 Subject: [PATCH 45/90] Bug 718684 - Fix rect calculation so that the form assist works within iframes too. r=sriram --- mobile/android/chrome/content/browser.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 303b3c5820a8..8e111990bf66 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -2774,15 +2774,15 @@ var FormAssistant = { this._currentInputElement = currentElement; let suggestions = this._getAutocompleteSuggestions(currentElement.value, currentElement); - let rect = currentElement.getBoundingClientRect(); - let zoom = BrowserApp.selectedTab.viewport.zoom; + let rect = ElementTouchHelper.getBoundingContentRect(currentElement); + let viewport = BrowserApp.selectedTab.viewport; sendMessageToJava({ gecko: { type: "FormAssist:AutoComplete", suggestions: suggestions, - rect: [rect.left, rect.top, rect.width, rect.height], - zoom: zoom + rect: [rect.x - (viewport.x / viewport.zoom), rect.y - (viewport.y / viewport.zoom), rect.w, rect.h], + zoom: viewport.zoom } }); } From 024b8e70d22d37f0e905ab14bd6d8a417ec1cec6 Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Mon, 16 Jan 2012 17:02:15 -0800 Subject: [PATCH 46/90] Bug 717104 - unreachable debuggee globals should not keep their debuggers alive. r=jorendorff --HG-- extra : rebase_source : e1b4bc44adea1fa0bb94188ccaccc156c194dfbb --- js/src/vm/Debugger.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index 48c832d7f9a9..f15a859df550 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -1159,6 +1159,8 @@ Debugger::markAllIteratively(GCMarker *trc) const GlobalObjectSet &debuggees = dc->getDebuggees(); for (GlobalObjectSet::Range r = debuggees.all(); !r.empty(); r.popFront()) { GlobalObject *global = r.front(); + if (IsAboutToBeFinalized(cx, global)) + continue; /* * Every debuggee has at least one debugger, so in this case From 01ef79496dc8ad85b297c9677a9bf15570b39c6c Mon Sep 17 00:00:00 2001 From: Steve Fink Date: Thu, 26 Jan 2012 11:55:39 -0800 Subject: [PATCH 47/90] Bug 717104 - test that an unreachable global does not keep its Debuggers alive. r=woof! --HG-- extra : rebase_source : 6249095c7501ba1e1647ea01998de685fe0a278e --- js/src/jit-test/tests/debug/gc-09.2.js | 16 ++++++++++++++++ js/src/jit-test/tests/debug/gc-09.js | 15 +++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 js/src/jit-test/tests/debug/gc-09.2.js create mode 100644 js/src/jit-test/tests/debug/gc-09.js diff --git a/js/src/jit-test/tests/debug/gc-09.2.js b/js/src/jit-test/tests/debug/gc-09.2.js new file mode 100644 index 000000000000..740b802efa98 --- /dev/null +++ b/js/src/jit-test/tests/debug/gc-09.2.js @@ -0,0 +1,16 @@ +// Bug 717104 - Unreachable debuggee globals should not keep their debuggers +// alive. The loop is to defeat conservative stack scanning; if the same stack +// locations are used each time through the loop, at least three of the +// debuggers should be collected. +// +// This is a slight modification of gc-09.js, which contains a cycle. + +for (var i = 0; i < 4; i++) { + var g = newGlobal('new-compartment'); + var dbg = new Debugger(g); + dbg.onDebuggerStatement = function () { throw "FAIL"; }; + dbg.o = makeFinalizeObserver(); +} + +gc(); +assertEq(finalizeCount() > 0, true); diff --git a/js/src/jit-test/tests/debug/gc-09.js b/js/src/jit-test/tests/debug/gc-09.js new file mode 100644 index 000000000000..20f27fa88fe6 --- /dev/null +++ b/js/src/jit-test/tests/debug/gc-09.js @@ -0,0 +1,15 @@ +// Bug 717104 - Unreachable debuggee globals should not keep their debuggers +// alive. The loop is to defeat conservative stack scanning; if the same stack +// locations are used each time through the loop, at least three of the +// debuggers should be collected. + +for (var i = 0; i < 4; i++) { + var g = newGlobal('new-compartment'); + var dbg = new Debugger(g); + dbg.onDebuggerStatement = function () { throw "FAIL"; }; + dbg.o = makeFinalizeObserver(); + dbg.loop = g; // make a cycle of strong references with dbg and g +} + +gc(); +assertEq(finalizeCount() > 0, true); From 99a251d9ae9a04c72dfb0b803f9d8ca5a0922c31 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Wed, 1 Feb 2012 20:10:02 +0100 Subject: [PATCH 48/90] Backout 44a0dc4fb9ff (Bug 673470) for Talos regression. --- storage/src/TelemetryVFS.cpp | 1 + toolkit/components/build/nsToolkitCompsCID.h | 6 +- .../telemetry/TelemetryHistograms.h | 9 +- .../components/url-classifier/ChunkSet.cpp | 136 - toolkit/components/url-classifier/ChunkSet.h | 90 - .../components/url-classifier/Classifier.cpp | 653 --- .../components/url-classifier/Classifier.h | 128 - toolkit/components/url-classifier/Entries.h | 335 -- .../components/url-classifier/HashStore.cpp | 950 ----- toolkit/components/url-classifier/HashStore.h | 213 - .../components/url-classifier/LookupCache.cpp | 776 ---- .../components/url-classifier/LookupCache.h | 186 - toolkit/components/url-classifier/Makefile.in | 6 - .../url-classifier/ProtocolParser.cpp | 777 ---- .../url-classifier/ProtocolParser.h | 151 - .../url-classifier/content/listmanager.js | 2 +- .../nsCheckSummedOutputStream.cpp | 92 - .../nsCheckSummedOutputStream.h | 86 - .../nsIUrlClassifierDBService.idl | 16 +- .../nsIUrlClassifierPrefixSet.idl | 14 +- .../nsUrlClassifierDBService.cpp | 3729 +++++++++++++++-- .../url-classifier/nsUrlClassifierDBService.h | 9 +- .../nsUrlClassifierPrefixSet.cpp | 157 +- .../url-classifier/nsUrlClassifierPrefixSet.h | 16 +- .../url-classifier/nsUrlClassifierProxies.cpp | 19 +- .../url-classifier/nsUrlClassifierProxies.h | 27 +- .../nsUrlClassifierStreamUpdater.cpp | 12 +- .../tests/unit/head_urlclassifier.js | 25 +- .../url-classifier/tests/unit/test_addsub.js | 9 +- .../tests/unit/test_cleankeycache.js | 195 + .../url-classifier/tests/unit/test_partial.js | 59 +- .../tests/unit/test_prefixset.js | 39 +- .../tests/unit/test_streamupdater.js | 16 +- .../url-classifier/tests/unit/xpcshell.ini | 1 + 34 files changed, 3791 insertions(+), 5149 deletions(-) delete mode 100644 toolkit/components/url-classifier/ChunkSet.cpp delete mode 100644 toolkit/components/url-classifier/ChunkSet.h delete mode 100644 toolkit/components/url-classifier/Classifier.cpp delete mode 100644 toolkit/components/url-classifier/Classifier.h delete mode 100644 toolkit/components/url-classifier/Entries.h delete mode 100644 toolkit/components/url-classifier/HashStore.cpp delete mode 100644 toolkit/components/url-classifier/HashStore.h delete mode 100644 toolkit/components/url-classifier/LookupCache.cpp delete mode 100644 toolkit/components/url-classifier/LookupCache.h delete mode 100644 toolkit/components/url-classifier/ProtocolParser.cpp delete mode 100644 toolkit/components/url-classifier/ProtocolParser.h delete mode 100644 toolkit/components/url-classifier/nsCheckSummedOutputStream.cpp delete mode 100644 toolkit/components/url-classifier/nsCheckSummedOutputStream.h create mode 100644 toolkit/components/url-classifier/tests/unit/test_cleankeycache.js diff --git a/storage/src/TelemetryVFS.cpp b/storage/src/TelemetryVFS.cpp index 6120ed17bd43..3d74dea45d28 100644 --- a/storage/src/TelemetryVFS.cpp +++ b/storage/src/TelemetryVFS.cpp @@ -67,6 +67,7 @@ struct Histograms { Histograms gHistograms[] = { SQLITE_TELEMETRY("places.sqlite", PLACES), + SQLITE_TELEMETRY("urlclassifier3.sqlite", URLCLASSIFIER), SQLITE_TELEMETRY("cookies.sqlite", COOKIES), SQLITE_TELEMETRY("webappsstore.sqlite", WEBAPPS), SQLITE_TELEMETRY(NULL, OTHER) diff --git a/toolkit/components/build/nsToolkitCompsCID.h b/toolkit/components/build/nsToolkitCompsCID.h index c24332cf7e23..8f501ed13608 100644 --- a/toolkit/components/build/nsToolkitCompsCID.h +++ b/toolkit/components/build/nsToolkitCompsCID.h @@ -163,9 +163,9 @@ #define NS_TYPEAHEADFIND_CID \ { 0xe7f70966, 0x9a37, 0x48d7, { 0x8a, 0xeb, 0x35, 0x99, 0x8f, 0x31, 0x09, 0x0e} } -// {b21b0fa1-20d2-422a-b2cc-b289c9325811} - #define NS_URLCLASSIFIERPREFIXSET_CID \ -{ 0xb21b0fa1, 0x20d2, 0x422a, { 0xb2, 0xcc, 0xb2, 0x89, 0xc9, 0x32, 0x58, 0x11} } +// {15a892dd-cb0f-4a9f-a27f-8291d5e16653} +#define NS_URLCLASSIFIERPREFIXSET_CID \ +{ 0x15a892dd, 0xcb0f, 0x4a9f, { 0xa2, 0x7f, 0x82, 0x91, 0xd5, 0xe1, 0x66, 0x53} } // {5eb7c3c1-ec1f-4007-87cc-eefb37d68ce6} #define NS_URLCLASSIFIERDBSERVICE_CID \ diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h index 063ca844c5d5..be77bc7b6d94 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -225,6 +225,7 @@ HISTOGRAM(CHECK_JAVA_ENABLED, 1, 3000, 10, EXPONENTIAL, "Time spent checking if SQLITE_TIME_SPENT(OTHER_ ## NAME, DESC) \ SQLITE_TIME_SPENT(PLACES_ ## NAME, DESC) \ SQLITE_TIME_SPENT(COOKIES_ ## NAME, DESC) \ + SQLITE_TIME_SPENT(URLCLASSIFIER_ ## NAME, DESC) \ SQLITE_TIME_SPENT(WEBAPPS_ ## NAME, DESC) SQLITE_TIME_SPENT(OPEN, "Time spent on SQLite open() (ms)") @@ -237,9 +238,11 @@ SQLITE_TIME_PER_FILE(SYNC, "Time spent on SQLite fsync() (ms)") HISTOGRAM(MOZ_SQLITE_OTHER_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)") HISTOGRAM(MOZ_SQLITE_PLACES_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)") HISTOGRAM(MOZ_SQLITE_COOKIES_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)") +HISTOGRAM(MOZ_SQLITE_URLCLASSIFIER_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)") HISTOGRAM(MOZ_SQLITE_WEBAPPS_READ_B, 1, 32768, 3, LINEAR, "SQLite read() (bytes)") HISTOGRAM(MOZ_SQLITE_PLACES_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)") HISTOGRAM(MOZ_SQLITE_COOKIES_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)") +HISTOGRAM(MOZ_SQLITE_URLCLASSIFIER_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)") HISTOGRAM(MOZ_SQLITE_WEBAPPS_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)") HISTOGRAM(MOZ_SQLITE_OTHER_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)") HISTOGRAM(MOZ_STORAGE_ASYNC_REQUESTS_MS, 1, 32768, 20, EXPONENTIAL, "mozStorage async requests completion (ms)") @@ -260,14 +263,10 @@ HISTOGRAM(NETWORK_DISK_CACHE_OUTPUT_STREAM_CLOSE_INTERNAL_MAIN_THREAD, 1, 10000, * Url-Classifier telemetry */ #ifdef MOZ_URL_CLASSIFIER -HISTOGRAM(URLCLASSIFIER_LOOKUP_TIME, 1, 500, 10, EXPONENTIAL, "Time spent per dbservice lookup (ms)") -HISTOGRAM(URLCLASSIFIER_CL_CHECK_TIME, 1, 500, 10, EXPONENTIAL, "Time spent per classifier lookup (ms)") -HISTOGRAM(URLCLASSIFIER_CL_UPDATE_TIME, 20, 15000, 15, EXPONENTIAL, "Time spent per classifier update (ms)") HISTOGRAM(URLCLASSIFIER_PS_FILELOAD_TIME, 1, 1000, 10, EXPONENTIAL, "Time spent loading PrefixSet from file (ms)") HISTOGRAM(URLCLASSIFIER_PS_FALLOCATE_TIME, 1, 1000, 10, EXPONENTIAL, "Time spent fallocating PrefixSet (ms)") HISTOGRAM(URLCLASSIFIER_PS_CONSTRUCT_TIME, 1, 5000, 15, EXPONENTIAL, "Time spent constructing PrefixSet from DB (ms)") -HISTOGRAM(URLCLASSIFIER_LC_PREFIXES, 1, 1500000, 15, LINEAR, "Size of the prefix cache in entries") -HISTOGRAM(URLCLASSIFIER_LC_COMPLETIONS, 1, 200, 10, EXPONENTIAL, "Size of the completion cache in entries") +HISTOGRAM(URLCLASSIFIER_PS_LOOKUP_TIME, 1, 500, 10, EXPONENTIAL, "Time spent per PrefixSet lookup (ms)") HISTOGRAM_BOOLEAN(URLCLASSIFIER_PS_OOM, "Did UrlClassifier run out of memory during PrefixSet construction?") #endif diff --git a/toolkit/components/url-classifier/ChunkSet.cpp b/toolkit/components/url-classifier/ChunkSet.cpp deleted file mode 100644 index 43b1b2068ca0..000000000000 --- a/toolkit/components/url-classifier/ChunkSet.cpp +++ /dev/null @@ -1,136 +0,0 @@ -//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dave Camp - * Gian-Carlo Pascutto - * - * 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 "ChunkSet.h" - -namespace mozilla { -namespace safebrowsing { - -nsresult -ChunkSet::Serialize(nsACString& aChunkStr) -{ - aChunkStr.Truncate(); - - PRUint32 i = 0; - while (i < mChunks.Length()) { - if (i != 0) { - aChunkStr.Append(','); - } - aChunkStr.AppendInt((PRInt32)mChunks[i]); - - PRUint32 first = i; - PRUint32 last = first; - i++; - while (i < mChunks.Length() && (mChunks[i] == mChunks[i - 1] + 1 || mChunks[i] == mChunks[i - 1])) { - last = i++; - } - - if (last != first) { - aChunkStr.Append('-'); - aChunkStr.AppendInt((PRInt32)mChunks[last]); - } - } - - return NS_OK; -} - -nsresult -ChunkSet::Set(PRUint32 aChunk) -{ - PRUint32 idx = mChunks.BinaryIndexOf(aChunk); - if (idx == nsTArray::NoIndex) { - mChunks.InsertElementSorted(aChunk); - } - return NS_OK; -} - -nsresult -ChunkSet::Unset(PRUint32 aChunk) -{ - mChunks.RemoveElementSorted(aChunk); - - return NS_OK; -} - -bool -ChunkSet::Has(PRUint32 aChunk) const -{ - return mChunks.BinaryIndexOf(aChunk) != nsTArray::NoIndex; -} - -nsresult -ChunkSet::Merge(const ChunkSet& aOther) -{ - const uint32 *dupIter = aOther.mChunks.Elements(); - const uint32 *end = aOther.mChunks.Elements() + aOther.mChunks.Length(); - - for (const uint32 *iter = dupIter; iter != end; iter++) { - nsresult rv = Set(*iter); - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -nsresult -ChunkSet::Remove(const ChunkSet& aOther) -{ - uint32 *addIter = mChunks.Elements(); - uint32 *end = mChunks.Elements() + mChunks.Length(); - - for (uint32 *iter = addIter; iter != end; iter++) { - if (!aOther.Has(*iter)) { - *addIter = *iter; - addIter++; - } - } - - mChunks.SetLength(addIter - mChunks.Elements()); - - return NS_OK; -} - -void -ChunkSet::Clear() -{ - mChunks.Clear(); -} - -} -} diff --git a/toolkit/components/url-classifier/ChunkSet.h b/toolkit/components/url-classifier/ChunkSet.h deleted file mode 100644 index 0a465060e508..000000000000 --- a/toolkit/components/url-classifier/ChunkSet.h +++ /dev/null @@ -1,90 +0,0 @@ -//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dave Camp - * Gian-Carlo Pascutto - * - * 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 ChunkSet_h__ -#define ChunkSet_h__ - - -#include "Entries.h" -#include "nsString.h" -#include "nsTArray.h" - -namespace mozilla { -namespace safebrowsing { - -/** - * Store the chunks as an array of uint32. - * XXX: We should optimize this further to compress the - * many consecutive numbers. - */ -class ChunkSet { -public: - ChunkSet() {} - ~ChunkSet() {} - - nsresult Serialize(nsACString& aStr); - nsresult Set(PRUint32 aChunk); - nsresult Unset(PRUint32 aChunk); - void Clear(); - nsresult Merge(const ChunkSet& aOther); - nsresult Remove(const ChunkSet& aOther); - - bool Has(PRUint32 chunk) const; - - uint32 Length() const { return mChunks.Length(); } - - nsresult Write(nsIOutputStream* aOut) { - return WriteTArray(aOut, mChunks); - } - - nsresult Read(nsIInputStream* aIn, PRUint32 aNumElements) { - return ReadTArray(aIn, &mChunks, aNumElements); - } - - uint32 *Begin() { return mChunks.Elements(); } - uint32 *End() { return mChunks.Elements() + mChunks.Length(); } - -private: - nsTArray mChunks; -}; - -} -} - -#endif diff --git a/toolkit/components/url-classifier/Classifier.cpp b/toolkit/components/url-classifier/Classifier.cpp deleted file mode 100644 index 0f613d307b80..000000000000 --- a/toolkit/components/url-classifier/Classifier.cpp +++ /dev/null @@ -1,653 +0,0 @@ -//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dave Camp - * Gian-Carlo Pascutto - * - * 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 "Classifier.h" -#include "nsISimpleEnumerator.h" -#include "nsIRandomGenerator.h" -#include "nsIInputStream.h" -#include "nsISeekableStream.h" -#include "nsIFile.h" -#include "nsAutoPtr.h" -#include "mozilla/Telemetry.h" -#include "prlog.h" - -// NSPR_LOG_MODULES=UrlClassifierDbService:5 -extern PRLogModuleInfo *gUrlClassifierDbServiceLog; -#if defined(PR_LOGGING) -#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) -#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) -#else -#define LOG(args) -#define LOG_ENABLED() (PR_FALSE) -#endif - -namespace mozilla { -namespace safebrowsing { - -Classifier::Classifier() - : mFreshTime(45 * 60) -{ -} - -Classifier::~Classifier() -{ - Close(); -} - -/* - * Generate a unique 32-bit key for this user, which we will - * use to rehash all prefixes. This ensures that different users - * will get hash collisions on different prefixes, which in turn - * avoids that "unlucky" URLs get mysterious slowdowns, and that - * the servers get spammed if any such URL should get slashdotted. - * https://bugzilla.mozilla.org/show_bug.cgi?id=669407#c10 - */ -nsresult -Classifier::InitKey() -{ - nsCOMPtr storeFile; - nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = storeFile->AppendNative(NS_LITERAL_CSTRING("classifier.hashkey")); - NS_ENSURE_SUCCESS(rv, rv); - - bool exists; - rv = storeFile->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (!exists) { - // generate and store key - nsCOMPtr rg = - do_GetService("@mozilla.org/security/random-generator;1"); - NS_ENSURE_STATE(rg); - - PRUint8 *temp; - nsresult rv = rg->GenerateRandomBytes(sizeof(mHashKey), &temp); - NS_ENSURE_SUCCESS(rv, rv); - memcpy(&mHashKey, temp, sizeof(mHashKey)); - NS_Free(temp); - - nsCOMPtr out; - rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out), storeFile, - -1, -1, 0); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 written; - rv = out->Write(reinterpret_cast(&mHashKey), sizeof(PRUint32), &written); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr safeOut = do_QueryInterface(out); - rv = safeOut->Finish(); - NS_ENSURE_SUCCESS(rv, rv); - - LOG(("Initialized classifier, key = %X", mHashKey)); - } else { - // read key - nsCOMPtr inputStream; - rv = NS_NewLocalFileInputStream(getter_AddRefs(inputStream), storeFile, - -1, -1, 0); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr seekable = do_QueryInterface(inputStream); - nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); - NS_ENSURE_SUCCESS(rv, rv); - - void *buffer = &mHashKey; - rv = NS_ReadInputStreamToBuffer(inputStream, - &buffer, - sizeof(PRUint32)); - NS_ENSURE_SUCCESS(rv, rv); - - LOG(("Loaded classifier key = %X", mHashKey)); - } - - return NS_OK; -} - -nsresult -Classifier::Open(nsIFile& aCacheDirectory) -{ - nsresult rv; - - mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - // Ensure the safebrowsing directory exists. - rv = aCacheDirectory.Clone(getter_AddRefs(mStoreDirectory)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mStoreDirectory->AppendNative(NS_LITERAL_CSTRING("safebrowsing")); - NS_ENSURE_SUCCESS(rv, rv); - - bool storeExists; - rv = mStoreDirectory->Exists(&storeExists); - NS_ENSURE_SUCCESS(rv, rv); - - if (!storeExists) { - rv = mStoreDirectory->Create(nsIFile::DIRECTORY_TYPE, 0755); - NS_ENSURE_SUCCESS(rv, rv); - } else { - bool storeIsDir; - rv = mStoreDirectory->IsDirectory(&storeIsDir); - NS_ENSURE_SUCCESS(rv, rv); - if (!storeIsDir) - return NS_ERROR_FILE_DESTINATION_NOT_DIR; - } - - rv = InitKey(); - if (NS_FAILED(rv)) { - // Without a usable key the database is useless - Reset(); - return NS_ERROR_FAILURE; - } - - if (!mTableFreshness.Init()) { - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - -nsresult -Classifier::Close() -{ - DropStores(); - - return NS_OK; -} - -nsresult -Classifier::Reset() -{ - DropStores(); - - nsCOMPtr entries; - nsresult rv = mStoreDirectory->GetDirectoryEntries(getter_AddRefs(entries)); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasMore; - while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) { - nsCOMPtr file; - rv = entries->GetNext(getter_AddRefs(file)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = file->Remove(PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - } - NS_ENSURE_SUCCESS(rv, rv); - - mTableFreshness.Clear(); - - return NS_OK; -} - -void -Classifier::TableRequest(nsACString& aResult) -{ - nsTArray tables; - ActiveTables(tables); - for (uint32 i = 0; i < tables.Length(); i++) { - nsAutoPtr store(new HashStore(tables[i], mStoreDirectory)); - if (!store) - continue; - - nsresult rv = store->Open(); - if (NS_FAILED(rv)) - continue; - - aResult.Append(store->TableName()); - aResult.Append(";"); - - ChunkSet &adds = store->AddChunks(); - ChunkSet &subs = store->SubChunks(); - - if (adds.Length() > 0) { - aResult.Append("a:"); - nsCAutoString addList; - adds.Serialize(addList); - aResult.Append(addList); - } - - if (subs.Length() > 0) { - if (adds.Length() > 0) - aResult.Append(':'); - aResult.Append("s:"); - nsCAutoString subList; - subs.Serialize(subList); - aResult.Append(subList); - } - - aResult.Append('\n'); - } -} - -nsresult -Classifier::Check(const nsACString& aSpec, LookupResultArray& aResults) -{ - Telemetry::AutoTimer timer; - - // Get the set of fragments to look up. - nsTArray fragments; - nsresult rv = LookupCache::GetLookupFragments(aSpec, &fragments); - NS_ENSURE_SUCCESS(rv, rv); - - nsTArray activeTables; - ActiveTables(activeTables); - - nsTArray cacheArray; - for (PRUint32 i = 0; i < activeTables.Length(); i++) { - LookupCache *cache = GetLookupCache(activeTables[i]); - if (cache) { - cacheArray.AppendElement(cache); - } else { - return NS_ERROR_FAILURE; - } - } - - // Now check each lookup fragment against the entries in the DB. - for (PRUint32 i = 0; i < fragments.Length(); i++) { - Completion lookupHash; - lookupHash.FromPlaintext(fragments[i], mCryptoHash); - - // Get list of host keys to look up - Completion hostKey; - rv = LookupCache::GetKey(fragments[i], &hostKey, mCryptoHash); - if (NS_FAILED(rv)) { - // Local host on the network - continue; - } - -#if DEBUG && defined(PR_LOGGING) - if (LOG_ENABLED()) { - nsCAutoString checking; - lookupHash.ToString(checking); - LOG(("Checking %s (%X)", checking.get(), lookupHash.ToUint32())); - } -#endif - for (PRUint32 i = 0; i < cacheArray.Length(); i++) { - LookupCache *cache = cacheArray[i]; - bool has, complete; - Prefix codedPrefix; - rv = cache->Has(lookupHash, hostKey, mHashKey, - &has, &complete, &codedPrefix); - NS_ENSURE_SUCCESS(rv, rv); - if (has) { - LookupResult *result = aResults.AppendElement(); - if (!result) - return NS_ERROR_OUT_OF_MEMORY; - - PRInt64 age; - bool found = mTableFreshness.Get(cache->TableName(), &age); - if (!found) { - age = 24 * 60 * 60; // just a large number - } else { - PRInt64 now = (PR_Now() / PR_USEC_PER_SEC); - age = now - age; - } - - LOG(("Found a result in %s: %s (Age: %Lds)", - cache->TableName().get(), - complete ? "complete." : "Not complete.", - age)); - - result->hash.complete = lookupHash; - result->mCodedPrefix = codedPrefix; - result->mComplete = complete; - result->mFresh = (age < mFreshTime); - result->mTableName.Assign(cache->TableName()); - } - } - - } - - return NS_OK; -} - -nsresult -Classifier::ApplyUpdates(nsTArray* aUpdates) -{ - Telemetry::AutoTimer timer; - -#if defined(PR_LOGGING) - PRIntervalTime clockStart = 0; - if (LOG_ENABLED() || true) { - clockStart = PR_IntervalNow(); - } -#endif - - LOG(("Applying table updates.")); - - nsresult rv; - - for (uint32 i = 0; i < aUpdates->Length(); i++) { - // Previous ApplyTableUpdates() may have consumed this update.. - if ((*aUpdates)[i]) { - // Run all updates for one table - rv = ApplyTableUpdates(aUpdates, aUpdates->ElementAt(i)->TableName()); - if (NS_FAILED(rv)) { - Reset(); - return rv; - } - } - } - aUpdates->Clear(); - LOG(("Done applying updates.")); - -#if defined(PR_LOGGING) - if (LOG_ENABLED() || true) { - PRIntervalTime clockEnd = PR_IntervalNow(); - LOG(("update took %dms\n", - PR_IntervalToMilliseconds(clockEnd - clockStart))); - } -#endif - - return NS_OK; -} - -nsresult -Classifier::MarkSpoiled(nsTArray& aTables) -{ - for (uint32 i = 0; i < aTables.Length(); i++) { - LOG(("Spoiling table: %s", aTables[i].get())); - // Spoil this table by marking it as no known freshness - mTableFreshness.Remove(aTables[i]); - } - return NS_OK; -} - -void -Classifier::DropStores() -{ - for (uint32 i = 0; i < mHashStores.Length(); i++) { - delete mHashStores[i]; - } - mHashStores.Clear(); - for (uint32 i = 0; i < mLookupCaches.Length(); i++) { - delete mLookupCaches[i]; - } - mLookupCaches.Clear(); -} - -nsresult -Classifier::ScanStoreDir(nsTArray& aTables) -{ - nsCOMPtr entries; - nsresult rv = mStoreDirectory->GetDirectoryEntries(getter_AddRefs(entries)); - NS_ENSURE_SUCCESS(rv, rv); - - bool hasMore; - while (NS_SUCCEEDED(rv = entries->HasMoreElements(&hasMore)) && hasMore) { - nsCOMPtr file; - rv = entries->GetNext(getter_AddRefs(file)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCString leafName; - rv = file->GetNativeLeafName(leafName); - NS_ENSURE_SUCCESS(rv, rv); - - nsCString suffix(NS_LITERAL_CSTRING(".sbstore")); - - PRInt32 dot = leafName.RFind(suffix, 0); - if (dot != -1) { - leafName.Cut(dot, suffix.Length()); - aTables.AppendElement(leafName); - } - } - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -Classifier::ActiveTables(nsTArray& aTables) -{ - aTables.Clear(); - - nsTArray foundTables; - ScanStoreDir(foundTables); - - for (uint32 i = 0; i < foundTables.Length(); i++) { - nsAutoPtr store(new HashStore(nsCString(foundTables[i]), mStoreDirectory)); - if (!store) - return NS_ERROR_OUT_OF_MEMORY; - - nsresult rv = store->Open(); - if (NS_FAILED(rv)) - continue; - - LookupCache *lookupCache = GetLookupCache(store->TableName()); - if (!lookupCache) { - continue; - } - - const ChunkSet &adds = store->AddChunks(); - const ChunkSet &subs = store->SubChunks(); - - if (adds.Length() == 0 && subs.Length() == 0) - continue; - - LOG(("Active table: %s", store->TableName().get())); - aTables.AppendElement(store->TableName()); - } - - return NS_OK; -} - -/* - * This will consume+delete updates from the passed nsTArray. -*/ -nsresult -Classifier::ApplyTableUpdates(nsTArray* aUpdates, - const nsACString& aTable) -{ - LOG(("Classifier::ApplyTableUpdates(%s)", - PromiseFlatCString(aTable).get())); - - nsAutoPtr store(new HashStore(aTable, mStoreDirectory)); - - if (!store) - return NS_ERROR_FAILURE; - - // take the quick exit if there is no valid update for us - // (common case) - uint32 validupdates = 0; - - for (uint32 i = 0; i < aUpdates->Length(); i++) { - TableUpdate *update = aUpdates->ElementAt(i); - if (!update || !update->TableName().Equals(store->TableName())) - continue; - if (update->Empty()) { - aUpdates->ElementAt(i) = nsnull; - delete update; - continue; - } - validupdates++; - } - - if (!validupdates) { - return NS_OK; - } - - nsresult rv = store->Open(); - NS_ENSURE_SUCCESS(rv, rv); - rv = store->BeginUpdate(); - NS_ENSURE_SUCCESS(rv, rv); - - // Read the part of the store that is (only) in the cache - LookupCache *prefixSet = GetLookupCache(store->TableName()); - if (!prefixSet) { - return NS_ERROR_FAILURE; - } - nsTArray AddPrefixHashes; - rv = prefixSet->GetPrefixes(&AddPrefixHashes); - NS_ENSURE_SUCCESS(rv, rv); - rv = store->AugmentAdds(AddPrefixHashes); - NS_ENSURE_SUCCESS(rv, rv); - - uint32 applied = 0; - bool updateFreshness = false; - - for (uint32 i = 0; i < aUpdates->Length(); i++) { - TableUpdate *update = aUpdates->ElementAt(i); - if (!update || !update->TableName().Equals(store->TableName())) - continue; - - rv = store->ApplyUpdate(*update); - NS_ENSURE_SUCCESS(rv, rv); - - applied++; - - LOG(("Applied update to table %s:", PromiseFlatCString(store->TableName()).get())); - LOG((" %d add chunks", update->AddChunks().Length())); - LOG((" %d add prefixes", update->AddPrefixes().Length())); - LOG((" %d add completions", update->AddCompletes().Length())); - LOG((" %d sub chunks", update->SubChunks().Length())); - LOG((" %d sub prefixes", update->SubPrefixes().Length())); - LOG((" %d sub completions", update->SubCompletes().Length())); - LOG((" %d add expirations", update->AddExpirations().Length())); - LOG((" %d sub expirations", update->SubExpirations().Length())); - - if (!update->IsLocalUpdate()) { - updateFreshness = true; - LOG(("Remote update, updating freshness")); - } - - aUpdates->ElementAt(i) = nsnull; - delete update; - } - - LOG(("Applied %d update(s) to %s.", applied, PromiseFlatCString(store->TableName()).get())); - - rv = store->Rebuild(); - NS_ENSURE_SUCCESS(rv, rv); - - LOG(("Table %s now has:", PromiseFlatCString(store->TableName()).get())); - LOG((" %d add chunks", store->AddChunks().Length())); - LOG((" %d add prefixes", store->AddPrefixes().Length())); - LOG((" %d add completions", store->AddCompletes().Length())); - LOG((" %d sub chunks", store->SubChunks().Length())); - LOG((" %d sub prefixes", store->SubPrefixes().Length())); - LOG((" %d sub completions", store->SubCompletes().Length())); - - rv = store->WriteFile(); - NS_ENSURE_SUCCESS(rv, rv); - - // At this point the store is updated and written out to disk, but - // the data is still in memory. Build our quick-lookup table here. - rv = prefixSet->Build(store->AddPrefixes(), store->AddCompletes()); - NS_ENSURE_SUCCESS(rv, rv); -#if defined(DEBUG) && defined(PR_LOGGING) - prefixSet->Dump(); -#endif - prefixSet->WriteFile(); - - // This will drop all the temporary storage used during the update. - rv = store->FinishUpdate(); - NS_ENSURE_SUCCESS(rv, rv); - - if (updateFreshness) { - PRInt64 now = (PR_Now() / PR_USEC_PER_SEC); - LOG(("Successfully updated %s", PromiseFlatCString(store->TableName()).get())); - rv = (mTableFreshness.Put(store->TableName(), now) ? NS_OK : NS_ERROR_FAILURE); - } - - return rv; -} - -LookupCache * -Classifier::GetLookupCache(const nsACString& aTable) -{ - for (uint32 i = 0; i < mLookupCaches.Length(); i++) { - if (mLookupCaches[i]->TableName().Equals(aTable)) { - return mLookupCaches[i]; - } - } - - LookupCache *cache = new LookupCache(aTable, mStoreDirectory); - nsresult rv = cache->Init(); - if (NS_FAILED(rv)) { - return nsnull; - } - rv = cache->Open(); - if (NS_FAILED(rv)) { - if (rv == NS_ERROR_FILE_CORRUPTED) { - Reset(); - } - return nsnull; - } - mLookupCaches.AppendElement(cache); - return cache; -} - -nsresult -Classifier::ReadNoiseEntries(const Prefix& aPrefix, - const nsACString& aTableName, - PRInt32 aCount, - PrefixArray* aNoiseEntries) -{ - LookupCache *cache = GetLookupCache(aTableName); - if (!cache) { - return NS_ERROR_FAILURE; - } - - nsTArray prefixes; - nsresult rv = cache->GetPrefixes(&prefixes); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt32 idx = prefixes.BinaryIndexOf(aPrefix.ToUint32()); - - if (idx == nsTArray::NoIndex) { - NS_WARNING("Could not find prefix in PrefixSet during noise lookup"); - return NS_ERROR_FAILURE; - } - - idx -= idx % aCount; - - for (PRInt32 i = 0; (i < aCount) && ((idx+i) < prefixes.Length()); i++) { - Prefix newPref; - newPref.FromUint32(prefixes[idx+i]); - aNoiseEntries->AppendElement(newPref); - } - - return NS_OK; -} - -} -} diff --git a/toolkit/components/url-classifier/Classifier.h b/toolkit/components/url-classifier/Classifier.h deleted file mode 100644 index b6eb00a73b67..000000000000 --- a/toolkit/components/url-classifier/Classifier.h +++ /dev/null @@ -1,128 +0,0 @@ -//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dave Camp - * Gian-Carlo Pascutto - * - * 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 Classifier_h__ -#define Classifier_h__ - -#include "Entries.h" -#include "HashStore.h" -#include "ProtocolParser.h" -#include "LookupCache.h" -#include "nsCOMPtr.h" -#include "nsString.h" -#include "nsIFile.h" -#include "nsICryptoHash.h" -#include "nsDataHashtable.h" - -namespace mozilla { -namespace safebrowsing { - -/** - * Maintains the stores and LookupCaches for the url classifier. - */ -class Classifier { -public: - Classifier(); - ~Classifier(); - - nsresult Open(nsIFile& aCacheDirectory); - nsresult Close(); - nsresult Reset(); - - /** - * Get the list of active tables and their chunks in a format - * suitable for an update request. - */ - void TableRequest(nsACString& aResult); - - /* - * Get all tables that we know about. - */ - nsresult ActiveTables(nsTArray& aTables); - - /** - * Check a URL against the database. - */ - nsresult Check(const nsACString& aSpec, LookupResultArray& aResults); - - /** - * Apply the table updates in the array. Takes ownership of - * the updates in the array and clears it. Wacky! - */ - nsresult ApplyUpdates(nsTArray* aUpdates); - /** - * Failed update. Spoil the entries so we don't block hosts - * unnecessarily - */ - nsresult MarkSpoiled(nsTArray& aTables); - nsresult CacheCompletions(const CacheResultArray& aResults); - PRUint32 GetHashKey(void) { return mHashKey; }; - void SetFreshTime(PRUint32 aTime) { mFreshTime = aTime; }; - /* - * Get a bunch of extra prefixes to query for completion - * and mask the real entry being requested - */ - nsresult ReadNoiseEntries(const Prefix& aPrefix, - const nsACString& aTableName, - PRInt32 aCount, - PrefixArray* aNoiseEntries); -private: - void DropStores(); - nsresult ScanStoreDir(nsTArray& aTables); - - nsresult ApplyTableUpdates(nsTArray* aUpdates, - const nsACString& aTable); - - LookupCache *GetLookupCache(const nsACString& aTable); - nsresult InitKey(); - - nsCOMPtr mCryptoHash; - nsCOMPtr mStoreDirectory; - nsTArray mHashStores; - nsTArray mLookupCaches; - PRUint32 mHashKey; - // Stores the last time a given table was updated (seconds). - nsDataHashtable mTableFreshness; - PRUint32 mFreshTime; -}; - -} -} - -#endif diff --git a/toolkit/components/url-classifier/Entries.h b/toolkit/components/url-classifier/Entries.h deleted file mode 100644 index 357b0ccec2ce..000000000000 --- a/toolkit/components/url-classifier/Entries.h +++ /dev/null @@ -1,335 +0,0 @@ -/* ***** 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 Url Classifier code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dave Camp - * Gian-Carlo Pascutto - * - * 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 ***** */ - -//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -#ifndef SBEntries_h__ -#define SBEntries_h__ - -#include "nsTArray.h" -#include "nsString.h" -#include "nsICryptoHash.h" -#include "nsNetUtil.h" -#include "prlog.h" - -extern PRLogModuleInfo *gUrlClassifierDbServiceLog; -#if defined(PR_LOGGING) -#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) -#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) -#else -#define LOG(args) -#define LOG_ENABLED() (PR_FALSE) -#endif - -#if DEBUG -#include "plbase64.h" -#endif - -namespace mozilla { -namespace safebrowsing { - -#define PREFIX_SIZE 4 -#define COMPLETE_SIZE 32 - -template -struct SafebrowsingHash -{ - static const uint32 sHashSize = S; - typedef SafebrowsingHash self_type; - uint8 buf[S]; - - nsresult FromPlaintext(const nsACString& aPlainText, nsICryptoHash* aHash) { - // From the protocol doc: - // Each entry in the chunk is composed - // of the SHA 256 hash of a suffix/prefix expression. - - nsresult rv = aHash->Init(nsICryptoHash::SHA256); - NS_ENSURE_SUCCESS(rv, rv); - - rv = aHash->Update - (reinterpret_cast(aPlainText.BeginReading()), - aPlainText.Length()); - NS_ENSURE_SUCCESS(rv, rv); - - nsCAutoString hashed; - rv = aHash->Finish(PR_FALSE, hashed); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(hashed.Length() >= sHashSize, - "not enough characters in the hash"); - - memcpy(buf, hashed.BeginReading(), sHashSize); - - return NS_OK; - } - - void Assign(const nsACString& aStr) { - NS_ASSERTION(aStr.Length() >= sHashSize, - "string must be at least sHashSize characters long"); - memcpy(buf, aStr.BeginReading(), sHashSize); - } - - int Compare(const self_type& aOther) const { - return Comparator::Compare(buf, aOther.buf); - } - - bool operator==(const self_type& aOther) const { - return Comparator::Compare(buf, aOther.buf) == 0; - } - - bool operator!=(const self_type& aOther) const { - return Comparator::Compare(buf, aOther.buf) != 0; - } - - bool operator<(const self_type& aOther) const { - return Comparator::Compare(buf, aOther.buf) < 0; - } - -#ifdef DEBUG - void ToString(nsACString& aStr) const { - uint32 len = ((sHashSize + 2) / 3) * 4; - aStr.SetCapacity(len + 1); - PL_Base64Encode((char*)buf, sHashSize, aStr.BeginWriting()); - aStr.BeginWriting()[len] = '\0'; - } -#endif - PRUint32 ToUint32() const { - PRUint32 res = 0; - memcpy(&res, buf, NS_MIN(4, S)); - return res; - } - void FromUint32(PRUint32 aHash) { - memcpy(buf, &aHash, NS_MIN(4, S)); - } -}; - -class PrefixComparator { -public: - static int Compare(const PRUint8* a, const PRUint8* b) { - return *((uint32*)a) - *((uint32*)b); - } -}; -typedef SafebrowsingHash Prefix; -typedef nsTArray PrefixArray; - -class CompletionComparator { -public: - static int Compare(const PRUint8* a, const PRUint8* b) { - return memcmp(a, b, COMPLETE_SIZE); - } -}; -typedef SafebrowsingHash Completion; -typedef nsTArray CompletionArray; - -struct AddPrefix { - Prefix prefix; - uint32 addChunk; - - AddPrefix() : addChunk(0) {} - - uint32 Chunk() const { return addChunk; } - const Prefix &PrefixHash() const { return prefix; } - - template - int Compare(const T& other) const { - int cmp = prefix.Compare(other.PrefixHash()); - if (cmp != 0) { - return cmp; - } - return addChunk - other.addChunk; - } -}; - -struct AddComplete { - union { - Prefix prefix; - Completion complete; - } hash; - uint32 addChunk; - - AddComplete() : addChunk(0) {} - - uint32 Chunk() const { return addChunk; } - const Prefix &PrefixHash() const { return hash.prefix; } - const Completion &CompleteHash() const { return hash.complete; } - - template - int Compare(const T& other) const { - int cmp = hash.complete.Compare(other.CompleteHash()); - if (cmp != 0) { - return cmp; - } - return addChunk - other.addChunk; - } -}; - -struct SubPrefix { - Prefix prefix; - uint32 addChunk; - uint32 subChunk; - - SubPrefix(): addChunk(0), subChunk(0) {} - - uint32 Chunk() const { return subChunk; } - uint32 AddChunk() const { return addChunk; } - const Prefix &PrefixHash() const { return prefix; } - - template - int Compare(const T& aOther) const { - int cmp = prefix.Compare(aOther.PrefixHash()); - if (cmp != 0) - return cmp; - if (addChunk != aOther.addChunk) - return addChunk - aOther.addChunk; - return subChunk - aOther.subChunk; - } - - template - int CompareAlt(const T& aOther) const { - int cmp = prefix.Compare(aOther.PrefixHash()); - if (cmp != 0) - return cmp; - return addChunk - aOther.addChunk; - } -}; - -struct SubComplete { - union { - Prefix prefix; - Completion complete; - } hash; - uint32 addChunk; - uint32 subChunk; - - SubComplete() : addChunk(0), subChunk(0) {} - - uint32 Chunk() const { return subChunk; } - uint32 AddChunk() const { return addChunk; } - const Prefix &PrefixHash() const { return hash.prefix; } - const Completion &CompleteHash() const { return hash.complete; } - - int Compare(const SubComplete& aOther) const { - int cmp = hash.complete.Compare(aOther.hash.complete); - if (cmp != 0) - return cmp; - if (addChunk != aOther.addChunk) - return addChunk - aOther.addChunk; - return subChunk - aOther.subChunk; - } -}; - -typedef nsTArray AddPrefixArray; -typedef nsTArray AddCompleteArray; -typedef nsTArray SubPrefixArray; -typedef nsTArray SubCompleteArray; - -/** - * Compares chunks by their add chunk, then their prefix. - */ -template -class EntryCompare { -public: - typedef T elem_type; - static int Compare(const void* e1, const void* e2, void* data) { - const elem_type* a = static_cast(e1); - const elem_type* b = static_cast(e2); - return a->Compare(*b); - } -}; - -template<> -class EntryCompare { -public: - typedef SubPrefix elem_type; - static int Compare(const void* e1, const void* e2, void* data) { - const elem_type* a = static_cast(e1); - const elem_type* b = static_cast(e2); - return a->Compare(*b); - } -}; - -template<> -class EntryCompare { -public: - typedef SubComplete elem_type; - static int Compare(const void* e1, const void* e2, void* data) { - const elem_type *a = static_cast(e1); - const elem_type *b = static_cast(e2); - return a->Compare(*b); - } -}; - -/** - * Sort an array of store entries. nsTArray::Sort uses Equal/LessThan - * to sort, this does a single Compare so it's a bit quicker over the - * large sorts we do. - */ -template -void -EntrySort(nsTArray& aArray) -{ - NS_QuickSort(aArray.Elements(), aArray.Length(), sizeof(T), - EntryCompare::Compare, 0); -} - -template -nsresult -ReadTArray(nsIInputStream* aStream, nsTArray* aArray, PRUint32 aNumElements) -{ - if (!aArray->SetLength(aNumElements)) - return NS_ERROR_OUT_OF_MEMORY; - - void *buffer = aArray->Elements(); - nsresult rv = NS_ReadInputStreamToBuffer(aStream, &buffer, - (aNumElements * sizeof(T))); - NS_ENSURE_SUCCESS(rv, rv); - return NS_OK; -} - -template -nsresult -WriteTArray(nsIOutputStream* aStream, nsTArray& aArray) -{ - PRUint32 written; - return aStream->Write(reinterpret_cast(aArray.Elements()), - aArray.Length() * sizeof(T), - &written); -} - -} -} -#endif diff --git a/toolkit/components/url-classifier/HashStore.cpp b/toolkit/components/url-classifier/HashStore.cpp deleted file mode 100644 index 234b4c12bc55..000000000000 --- a/toolkit/components/url-classifier/HashStore.cpp +++ /dev/null @@ -1,950 +0,0 @@ -//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -// Originally based on Chrome sources: -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -#include "HashStore.h" -#include "nsAutoPtr.h" -#include "nsICryptoHash.h" -#include "nsISeekableStream.h" -#include "nsIStreamConverterService.h" -#include "nsNetUtil.h" -#include "nsCheckSummedOutputStream.h" -#include "prlog.h" -#include "zlib.h" - -// Main store for SafeBrowsing protocol data. We store -// known add/sub chunks, prefixe and completions s in memory -// during an update, and serialize to disk. -// We do not store the add prefixes, those are retrieved by -// decompressing the PrefixSet cache whenever we need to apply -// an update. - -// Data format: -// uint32 magic -// uint32 version -// uint32 numAddChunks -// uint32 numSubChunks -// uint32 numAddPrefixes -// uint32 numSubPrefixes -// uint32 numAddCompletes -// uint32 numSubCompletes -// 0...numAddChunks uint32 addChunk -// 0...numSubChunks uint32 subChunk -// uint32 compressed-size -// compressed-size bytes zlib inflate data -// 0...numAddPrefixes uint32 addChunk -// uint32 compressed-size -// compressed-size bytes zlib inflate data -// 0...numSubPrefixes uint32 addChunk -// uint32 compressed-size -// compressed-size bytes zlib inflate data -// 0...numSubPrefixes uint32 subChunk -// 0...numSubPrefixes uint32 subPrefix -// 0...numAddCompletes 32-byte Completions -// 0...numSubCompletes 32-byte Completions -// 16-byte MD5 of all preceding data - -// NSPR_LOG_MODULES=UrlClassifierDbService:5 -extern PRLogModuleInfo *gUrlClassifierDbServiceLog; -#if defined(PR_LOGGING) -#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) -#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) -#else -#define LOG(args) -#define LOG_ENABLED() (PR_FALSE) -#endif - -namespace mozilla { -namespace safebrowsing { - -const uint32 STORE_MAGIC = 0x1231af3b; -const uint32 CURRENT_VERSION = 1; - -void -TableUpdate::NewAddPrefix(PRUint32 aAddChunk, const Prefix& aHash) -{ - AddPrefix *add = mAddPrefixes.AppendElement(); - add->addChunk = aAddChunk; - add->prefix = aHash; -} - -void -TableUpdate::NewSubPrefix(PRUint32 aAddChunk, const Prefix& aHash, PRUint32 aSubChunk) -{ - SubPrefix *sub = mSubPrefixes.AppendElement(); - sub->addChunk = aAddChunk; - sub->prefix = aHash; - sub->subChunk = aSubChunk; -} - -void -TableUpdate::NewAddComplete(PRUint32 aAddChunk, const Completion& aHash) -{ - AddComplete *add = mAddCompletes.AppendElement(); - add->addChunk = aAddChunk; - add->hash.complete = aHash; -} - -void -TableUpdate::NewSubComplete(PRUint32 aAddChunk, const Completion& aHash, PRUint32 aSubChunk) -{ - SubComplete *sub = mSubCompletes.AppendElement(); - sub->addChunk = aAddChunk; - sub->hash.complete = aHash; - sub->subChunk = aSubChunk; -} - - -HashStore::HashStore(const nsACString& aTableName, nsIFile* aStoreDir) - : mTableName(aTableName) - , mStoreDirectory(aStoreDir) - , mInUpdate(false) -{ -} - -HashStore::~HashStore() -{ -} - -nsresult -HashStore::Reset() -{ - LOG(("HashStore resetting")); - - nsCOMPtr storeFile; - nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = storeFile->Remove(PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - - Clear(); - - return NS_OK; -} - -nsresult -HashStore::CheckChecksum(nsIFile* aStoreFile) -{ - // Check for file corruption by - // comparing the stored checksum to actual checksum of data - nsCAutoString hash; - nsCAutoString compareHash; - char *data; - PRUint32 read; - - PRInt64 fileSize; - nsresult rv = aStoreFile->GetFileSize(&fileSize); - NS_ENSURE_SUCCESS(rv, rv); - - if (fileSize < 0) { - return NS_ERROR_FAILURE; - } - - rv = CalculateChecksum(hash, true); - NS_ENSURE_SUCCESS(rv, rv); - - compareHash.GetMutableData(&data, hash.Length()); - - nsCOMPtr seekIn = do_QueryInterface(mInputStream); - rv = seekIn->Seek(nsISeekableStream::NS_SEEK_SET, fileSize-hash.Length()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mInputStream->Read(data, hash.Length(), &read); - NS_ENSURE_SUCCESS(rv, rv); - NS_ASSERTION(read == hash.Length(), "Could not read hash bytes"); - - if (!hash.Equals(compareHash)) { - NS_WARNING("Safebrowing file failed checksum."); - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - -nsresult -HashStore::Open() -{ - nsCOMPtr storeFile; - nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore")); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr origStream; - rv = NS_NewLocalFileInputStream(getter_AddRefs(origStream), storeFile, - PR_RDONLY); - - if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) { - Reset(); - return rv; - } - - if (rv == NS_ERROR_FILE_NOT_FOUND) { - Clear(); - UpdateHeader(); - return NS_OK; - } - - rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream), origStream, - BUFFER_SIZE); - NS_ENSURE_SUCCESS(rv, rv); - - rv = CheckChecksum(storeFile); - if (NS_FAILED(rv)) { - Reset(); - return rv; - } - - rv = ReadHeader(); - if (NS_FAILED(rv)) { - Reset(); - return rv; - } - - rv = SanityCheck(storeFile); - if (NS_FAILED(rv)) { - NS_WARNING("Safebrowsing file failed sanity check. probably out of date."); - Reset(); - return rv; - } - - rv = ReadChunkNumbers(); - if (NS_FAILED(rv)) { - Reset(); - return rv; - } - - return NS_OK; -} - -void -HashStore::Clear() -{ - mAddChunks.Clear(); - mSubChunks.Clear(); - mAddExpirations.Clear(); - mSubExpirations.Clear(); - mAddPrefixes.Clear(); - mSubPrefixes.Clear(); - mAddCompletes.Clear(); - mSubCompletes.Clear(); -} - -nsresult -HashStore::ReadEntireStore() -{ - Clear(); - - nsresult rv = ReadHeader(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = ReadChunkNumbers(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = ReadHashes(); - if (NS_FAILED(rv)) { - // we are the only one reading this so it's up to us to detect corruption - Reset(); - } - - return rv; -} - -nsresult -HashStore::ReadHeader() -{ - if (!mInputStream) { - Clear(); - UpdateHeader(); - return NS_OK; - } - - nsCOMPtr seekable = do_QueryInterface(mInputStream); - nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); - NS_ENSURE_SUCCESS(rv, rv); - - void *buffer = &mHeader; - rv = NS_ReadInputStreamToBuffer(mInputStream, - &buffer, - sizeof(Header)); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -HashStore::SanityCheck(nsIFile *storeFile) -{ - if (mHeader.magic != STORE_MAGIC || mHeader.version != CURRENT_VERSION) { - NS_WARNING("Unexpected header data in the store."); - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - -nsresult -HashStore::CalculateChecksum(nsCAutoString& aChecksum, bool aChecksumPresent) -{ - aChecksum.Truncate(); - - nsCOMPtr storeFile; - nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore")); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr hashStream; - - rv = NS_NewLocalFileInputStream(getter_AddRefs(hashStream), storeFile, - PR_RDONLY); - - if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) { - Reset(); - return rv; - } - - PRInt64 fileSize; - rv = storeFile->GetFileSize(&fileSize); - NS_ENSURE_SUCCESS(rv, rv); - - if (fileSize < 0) { - return NS_ERROR_FAILURE; - } - - nsCOMPtr hash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - // Size of MD5 hash in bytes - const uint32 CHECKSUM_SIZE = 16; - - rv = hash->Init(nsICryptoHash::MD5); - NS_ENSURE_SUCCESS(rv, rv); - - if (!aChecksumPresent) { - // Hash entire file - rv = hash->UpdateFromStream(hashStream, PR_UINT32_MAX); - } else { - // Hash everything but last checksum bytes - rv = hash->UpdateFromStream(hashStream, fileSize-CHECKSUM_SIZE); - } - NS_ENSURE_SUCCESS(rv, rv); - - rv = hash->Finish(PR_FALSE, aChecksum); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -void -HashStore::UpdateHeader() -{ - mHeader.magic = STORE_MAGIC; - mHeader.version = CURRENT_VERSION; - - mHeader.numAddChunks = mAddChunks.Length(); - mHeader.numSubChunks = mSubChunks.Length(); - mHeader.numAddPrefixes = mAddPrefixes.Length(); - mHeader.numSubPrefixes = mSubPrefixes.Length(); - mHeader.numAddCompletes = mAddCompletes.Length(); - mHeader.numSubCompletes = mSubCompletes.Length(); -} - -nsresult -HashStore::ReadChunkNumbers() -{ - if (!mInputStream) { - LOG(("Clearing.")); - Clear(); - return NS_OK; - } - - nsCOMPtr seekable = do_QueryInterface(mInputStream); - nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, - sizeof(Header)); - - rv = mAddChunks.Read(mInputStream, mHeader.numAddChunks); - NS_ENSURE_SUCCESS(rv, rv); - NS_ASSERTION(mAddChunks.Length() == mHeader.numAddChunks, "Read the right amount of add chunks."); - - rv = mSubChunks.Read(mInputStream, mHeader.numSubChunks); - NS_ENSURE_SUCCESS(rv, rv); - NS_ASSERTION(mSubChunks.Length() == mHeader.numSubChunks, "Read the right amount of sub chunks."); - - return NS_OK; -} - -nsresult -HashStore::ReadHashes() -{ - if (!mInputStream) { - return NS_OK; - } - - nsCOMPtr seekable = do_QueryInterface(mInputStream); - - uint32 offset = sizeof(Header); - offset += (mHeader.numAddChunks + mHeader.numSubChunks) * sizeof(uint32); - nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, offset); - - rv = ReadAddPrefixes(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = ReadSubPrefixes(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = ReadTArray(mInputStream, &mAddCompletes, mHeader.numAddCompletes); - NS_ENSURE_SUCCESS(rv, rv); - - rv = ReadTArray(mInputStream, &mSubCompletes, mHeader.numSubCompletes); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -HashStore::BeginUpdate() -{ - mInUpdate = true; - - nsresult rv = ReadEntireStore(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -template -static nsresult -Merge(ChunkSet* aStoreChunks, - nsTArray* aStorePrefixes, - ChunkSet& aUpdateChunks, - nsTArray& aUpdatePrefixes) -{ - EntrySort(aUpdatePrefixes); - - T* updateIter = aUpdatePrefixes.Elements(); - T* updateEnd = aUpdatePrefixes.Elements() + aUpdatePrefixes.Length(); - - T* storeIter = aStorePrefixes->Elements(); - T* storeEnd = aStorePrefixes->Elements() + aStorePrefixes->Length(); - - // use a separate array so we can keep the iterators valid - // if the nsTArray grows - nsTArray adds; - - for (; updateIter != updateEnd; updateIter++) { - // XXX: binary search for insertion point might be faster in common - // case? - while (storeIter < storeEnd && (storeIter->Compare(*updateIter) < 0)) { - // skip forward to matching element (or not...) - storeIter++; - } - // no match, add - if (storeIter == storeEnd - || storeIter->Compare(*updateIter) != 0) { - if (!adds.AppendElement(*updateIter)) - return NS_ERROR_OUT_OF_MEMORY; - } - } - - // chunks can be empty, but we should still report we have them - // to make the chunkranges continuous - aStoreChunks->Merge(aUpdateChunks); - - aStorePrefixes->AppendElements(adds); - EntrySort(*aStorePrefixes); - - return NS_OK; -} - -nsresult -HashStore::ApplyUpdate(TableUpdate &update) -{ - nsresult rv = mAddExpirations.Merge(update.AddExpirations()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mSubExpirations.Merge(update.SubExpirations()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = Expire(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = Merge(&mAddChunks, &mAddPrefixes, - update.AddChunks(), update.AddPrefixes()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = Merge(&mAddChunks, &mAddCompletes, - update.AddChunks(), update.AddCompletes()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = Merge(&mSubChunks, &mSubPrefixes, - update.SubChunks(), update.SubPrefixes()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = Merge(&mSubChunks, &mSubCompletes, - update.SubChunks(), update.SubCompletes()); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -HashStore::Rebuild() -{ - NS_ASSERTION(mInUpdate, "Must be in update to rebuild."); - - nsresult rv = ProcessSubs(); - NS_ENSURE_SUCCESS(rv, rv); - - UpdateHeader(); - - return NS_OK; -} - -template -static void -ExpireEntries(nsTArray* aEntries, ChunkSet& aExpirations) -{ - T* addIter = aEntries->Elements(); - T* end = aEntries->Elements() + aEntries->Length(); - - for (T *iter = addIter; iter != end; iter++) { - if (!aExpirations.Has(iter->Chunk())) { - *addIter = *iter; - addIter++; - } - } - - aEntries->SetLength(addIter - aEntries->Elements()); -} - -nsresult -HashStore::Expire() -{ - ExpireEntries(&mAddPrefixes, mAddExpirations); - ExpireEntries(&mAddCompletes, mAddExpirations); - ExpireEntries(&mSubPrefixes, mSubExpirations); - ExpireEntries(&mSubCompletes, mSubExpirations); - - mAddChunks.Remove(mAddExpirations); - mSubChunks.Remove(mSubExpirations); - - mAddExpirations.Clear(); - mSubExpirations.Clear(); - - return NS_OK; -} - -template -nsresult DeflateWriteTArray(nsIOutputStream* aStream, nsTArray& aIn) -{ - uLongf insize = aIn.Length() * sizeof(T); - uLongf outsize = compressBound(insize); - nsTArray outBuff; - outBuff.SetLength(outsize); - - int zerr = compress(reinterpret_cast(outBuff.Elements()), - &outsize, - reinterpret_cast(aIn.Elements()), - insize); - if (zerr != Z_OK) { - return NS_ERROR_FAILURE; - } - LOG(("DeflateWriteTArray: %d in %d out", insize, outsize)); - - outBuff.TruncateLength(outsize); - - // Length of compressed data stream - PRUint32 dataLen = outBuff.Length(); - PRUint32 written; - nsresult rv = aStream->Write(reinterpret_cast(&dataLen), sizeof(dataLen), &written); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(written == sizeof(dataLen), "Error writing deflate length"); - - // Store to stream - rv = WriteTArray(aStream, outBuff); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -template -nsresult InflateReadTArray(nsIInputStream* aStream, nsTArray* aOut, - PRUint32 aExpectedSize) -{ - - PRUint32 inLen; - PRUint32 read; - nsresult rv = aStream->Read(reinterpret_cast(&inLen), sizeof(inLen), &read); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(read == sizeof(inLen), "Error reading inflate length"); - - nsTArray inBuff; - inBuff.SetLength(inLen); - - rv = ReadTArray(aStream, &inBuff, inLen); - NS_ENSURE_SUCCESS(rv, rv); - - uLongf insize = inLen; - uLongf outsize = aExpectedSize * sizeof(T); - aOut->SetLength(aExpectedSize); - - int zerr = uncompress(reinterpret_cast(aOut->Elements()), - &outsize, - reinterpret_cast(inBuff.Elements()), - insize); - if (zerr != Z_OK) { - return NS_ERROR_FAILURE; - } - LOG(("InflateReadTArray: %d in %d out", insize, outsize)); - - NS_ASSERTION(outsize == aExpectedSize * sizeof(T), "Decompression size mismatch"); - - return NS_OK; -} - -nsresult -HashStore::ReadAddPrefixes() -{ - nsTArray chunks; - PRUint32 count = mHeader.numAddPrefixes; - - nsresult rv = InflateReadTArray(mInputStream, &chunks, count); - NS_ENSURE_SUCCESS(rv, rv); - - mAddPrefixes.SetCapacity(count); - for (uint32 i = 0; i < count; i++) { - AddPrefix *add = mAddPrefixes.AppendElement(); - add->prefix.FromUint32(0); - add->addChunk = chunks[i]; - } - - return NS_OK; -} - -nsresult -HashStore::ReadSubPrefixes() -{ - nsTArray addchunks; - nsTArray subchunks; - nsTArray prefixes; - PRUint32 count = mHeader.numSubPrefixes; - - nsresult rv = InflateReadTArray(mInputStream, &addchunks, count); - NS_ENSURE_SUCCESS(rv, rv); - - rv = InflateReadTArray(mInputStream, &subchunks, count); - NS_ENSURE_SUCCESS(rv, rv); - - rv = ReadTArray(mInputStream, &prefixes, count); - NS_ENSURE_SUCCESS(rv, rv); - - mSubPrefixes.SetCapacity(count); - for (uint32 i = 0; i < count; i++) { - SubPrefix *sub = mSubPrefixes.AppendElement(); - sub->addChunk = addchunks[i]; - sub->prefix = prefixes[i]; - sub->subChunk = subchunks[i]; - } - - return NS_OK; -} - -// Split up PrefixArray back into the constituents -nsresult -HashStore::WriteAddPrefixes(nsIOutputStream* aOut) -{ - nsTArray chunks; - PRUint32 count = mAddPrefixes.Length(); - chunks.SetCapacity(count); - - for (uint32 i = 0; i < count; i++) { - chunks.AppendElement(mAddPrefixes[i].Chunk()); - } - - nsresult rv = DeflateWriteTArray(aOut, chunks); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -HashStore::WriteSubPrefixes(nsIOutputStream* aOut) -{ - nsTArray addchunks; - nsTArray subchunks; - nsTArray prefixes; - PRUint32 count = mSubPrefixes.Length(); - addchunks.SetCapacity(count); - subchunks.SetCapacity(count); - prefixes.SetCapacity(count); - - for (uint32 i = 0; i < count; i++) { - addchunks.AppendElement(mSubPrefixes[i].AddChunk()); - prefixes.AppendElement(mSubPrefixes[i].PrefixHash()); - subchunks.AppendElement(mSubPrefixes[i].Chunk()); - } - - nsresult rv = DeflateWriteTArray(aOut, addchunks); - NS_ENSURE_SUCCESS(rv, rv); - - rv = DeflateWriteTArray(aOut, subchunks); - NS_ENSURE_SUCCESS(rv, rv); - - // chunk-ordered prefixes are not compressible - rv = WriteTArray(aOut, prefixes); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -HashStore::WriteFile() -{ - nsCOMPtr storeFile; - nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); - NS_ENSURE_SUCCESS(rv, rv); - rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(".sbstore")); - NS_ENSURE_SUCCESS(rv, rv); - - // Need to close the inputstream here *before* rewriting its file. - // Windows will fail with an access violation if we don't. - if (mInputStream) { - rv = mInputStream->Close(); - NS_ENSURE_SUCCESS(rv, rv); - } - - nsCOMPtr out; - rv = NS_NewCheckSummedOutputStream(getter_AddRefs(out), storeFile, - PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 written; - rv = out->Write(reinterpret_cast(&mHeader), sizeof(mHeader), &written); - NS_ENSURE_SUCCESS(rv, rv); - - // Write chunk numbers... - rv = mAddChunks.Write(out); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mSubChunks.Write(out); - NS_ENSURE_SUCCESS(rv, rv); - - // Write hashes.. - rv = WriteAddPrefixes(out); - NS_ENSURE_SUCCESS(rv, rv); - - rv = WriteSubPrefixes(out); - NS_ENSURE_SUCCESS(rv, rv); - - rv = WriteTArray(out, mAddCompletes); - NS_ENSURE_SUCCESS(rv, rv); - - rv = WriteTArray(out, mSubCompletes); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr safeOut = do_QueryInterface(out, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = safeOut->Finish(); - NS_ENSURE_SUCCESS(rv, rv); - - // Reopen the file now that we've rewritten it. - nsCOMPtr origStream; - rv = NS_NewLocalFileInputStream(getter_AddRefs(origStream), storeFile, - PR_RDONLY); - NS_ENSURE_SUCCESS(rv, rv); - - rv = NS_NewBufferedInputStream(getter_AddRefs(mInputStream), origStream, - BUFFER_SIZE); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -HashStore::FinishUpdate() -{ - // Drop add/sub data, it's only used during updates. - mAddPrefixes.Clear(); - mSubPrefixes.Clear(); - mAddCompletes.Clear(); - mSubCompletes.Clear(); - - return NS_OK; -} - -template -static void -Erase(nsTArray* array, T* iterStart, T* iterEnd) -{ - uint32 start = iterStart - array->Elements(); - uint32 count = iterEnd - iterStart; - - if (count > 0) { - array->RemoveElementsAt(start, count); - } -} - -// Find items matching between |subs| and |adds|, and remove them, -// recording the item from |adds| in |adds_removed|. To minimize -// copies, the inputs are processing in parallel, so |subs| and |adds| -// should be compatibly ordered (either by SBAddPrefixLess or -// SBAddPrefixHashLess). -// -// |predAS| provides add < sub, |predSA| provides sub < add, for the -// tightest compare appropriate (see calls in SBProcessSubs). -template -static void -KnockoutSubs(nsTArray* aSubs, nsTArray* aAdds) -{ - // Keep a pair of output iterators for writing kept items. Due to - // deletions, these may lag the main iterators. Using erase() on - // individual items would result in O(N^2) copies. Using a list - // would work around that, at double or triple the memory cost. - TAdd* addOut = aAdds->Elements(); - TAdd* addIter = aAdds->Elements(); - - TSub* subOut = aSubs->Elements(); - TSub* subIter = aSubs->Elements(); - - TAdd* addEnd = addIter + aAdds->Length(); - TSub* subEnd = subIter + aSubs->Length(); - - while (addIter != addEnd && subIter != subEnd) { - // additer compare, so it compares on add chunk - int32 cmp = addIter->Compare(*subIter); - if (cmp > 0) { - // If |*sub_iter| < |*add_iter|, retain the sub. - *subOut = *subIter; - ++subOut; - ++subIter; - } else if (cmp < 0) { - // If |*add_iter| < |*sub_iter|, retain the add. - *addOut = *addIter; - ++addOut; - ++addIter; - } else { - // Drop equal items - ++addIter; - ++subIter; - } - } - - Erase(aAdds, addOut, addIter); - Erase(aSubs, subOut, subIter); -} - -// Remove items in |removes| from |fullHashes|. |fullHashes| and -// |removes| should be ordered by SBAddPrefix component. -template -static void -RemoveMatchingPrefixes(const SubPrefixArray& aSubs, nsTArray* aFullHashes) -{ - // Where to store kept items. - T* out = aFullHashes->Elements(); - T* hashIter = out; - T* hashEnd = aFullHashes->Elements() + aFullHashes->Length(); - - SubPrefix const * removeIter = aSubs.Elements(); - SubPrefix const * removeEnd = aSubs.Elements() + aSubs.Length(); - - while (hashIter != hashEnd && removeIter != removeEnd) { - int32 cmp = removeIter->CompareAlt(*hashIter); - if (cmp > 0) { - // Keep items less than |*removeIter|. - *out = *hashIter; - ++out; - ++hashIter; - } else if (cmp < 0) { - // No hit for |*removeIter|, bump it forward. - ++removeIter; - } else { - // Drop equal items, there may be multiple hits. - do { - ++hashIter; - } while (hashIter != hashEnd && - !(removeIter->CompareAlt(*hashIter) < 0)); - ++removeIter; - } - } - Erase(aFullHashes, out, hashIter); -} - -nsresult -HashStore::ProcessSubs() -{ - EntrySort(mAddPrefixes); - EntrySort(mSubPrefixes); - EntrySort(mAddCompletes); - EntrySort(mSubCompletes); - - KnockoutSubs(&mSubPrefixes, &mAddPrefixes); - - RemoveMatchingPrefixes(mSubPrefixes, &mAddCompletes); - RemoveMatchingPrefixes(mSubPrefixes, &mSubCompletes); - - KnockoutSubs(&mSubCompletes, &mAddCompletes); - - // Clean up temporary subs used for knocking out completes - ChunkSet dummyChunks; - dummyChunks.Set(0); - ExpireEntries(&mSubPrefixes, dummyChunks); - ExpireEntries(&mSubCompletes, dummyChunks); - mSubChunks.Remove(dummyChunks); - - return NS_OK; -} - -nsresult -HashStore::AugmentAdds(const nsTArray& aPrefixes) -{ - uint32 cnt = aPrefixes.Length(); - if (cnt != mAddPrefixes.Length()) { - LOG(("Amount of prefixes in cache not consistent with store (%d vs %d)", - aPrefixes.Length(), mAddPrefixes.Length())); - return NS_ERROR_FAILURE; - } - for (uint32 i = 0; i < cnt; i++) { - mAddPrefixes[i].prefix.FromUint32(aPrefixes[i]); - } - return NS_OK; -} - -} -} diff --git a/toolkit/components/url-classifier/HashStore.h b/toolkit/components/url-classifier/HashStore.h deleted file mode 100644 index bf76984eea0b..000000000000 --- a/toolkit/components/url-classifier/HashStore.h +++ /dev/null @@ -1,213 +0,0 @@ -/* ***** 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 Url Classifier code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dave Camp - * Gian-Carlo Pascutto - * - * 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 HashStore_h__ -#define HashStore_h__ - -#include "Entries.h" -#include "ChunkSet.h" - -#include "nsString.h" -#include "nsTArray.h" -#include "nsIFile.h" -#include "nsIFileStreams.h" -#include "nsCOMPtr.h" - -namespace mozilla { -namespace safebrowsing { - -class TableUpdate { -public: - TableUpdate(const nsACString& aTable) - : mTable(aTable), mLocalUpdate(false) {} - const nsCString& TableName() const { return mTable; } - - bool Empty() const { - return mAddChunks.Length() == 0 && - mSubChunks.Length() == 0 && - mAddExpirations.Length() == 0 && - mSubExpirations.Length() == 0 && - mAddPrefixes.Length() == 0 && - mSubPrefixes.Length() == 0 && - mAddCompletes.Length() == 0 && - mSubCompletes.Length() == 0; - } - - void NewAddChunk(PRUint32 aChunk) { mAddChunks.Set(aChunk); } - void NewSubChunk(PRUint32 aChunk) { mSubChunks.Set(aChunk); } - - void NewAddExpiration(PRUint32 aChunk) { mAddExpirations.Set(aChunk); } - void NewSubExpiration(PRUint32 aChunk) { mSubExpirations.Set(aChunk); } - - void NewAddPrefix(PRUint32 aAddChunk, const Prefix& aPrefix); - void NewSubPrefix(PRUint32 aAddChunk, const Prefix& aPprefix, PRUint32 aSubChunk); - void NewAddComplete(PRUint32 aChunk, const Completion& aCompletion); - void NewSubComplete(PRUint32 aAddChunk, const Completion& aCompletion, - PRUint32 aSubChunk); - void SetLocalUpdate(void) { mLocalUpdate = true; }; - bool IsLocalUpdate(void) { return mLocalUpdate; }; - - ChunkSet& AddChunks() { return mAddChunks; } - ChunkSet& SubChunks() { return mSubChunks; } - - ChunkSet& AddExpirations() { return mAddExpirations; } - ChunkSet& SubExpirations() { return mSubExpirations; } - - AddPrefixArray& AddPrefixes() { return mAddPrefixes; } - SubPrefixArray& SubPrefixes() { return mSubPrefixes; } - AddCompleteArray& AddCompletes() { return mAddCompletes; } - SubCompleteArray& SubCompletes() { return mSubCompletes; } - -private: - nsCString mTable; - // Update not from the remote server (no freshness) - bool mLocalUpdate; - - ChunkSet mAddChunks; - ChunkSet mSubChunks; - ChunkSet mAddExpirations; - ChunkSet mSubExpirations; - AddPrefixArray mAddPrefixes; - SubPrefixArray mSubPrefixes; - AddCompleteArray mAddCompletes; - SubCompleteArray mSubCompletes; -}; - -class HashStore { -public: - HashStore(const nsACString& aTableName, nsIFile* aStoreFile); - ~HashStore(); - - const nsCString& TableName() const { return mTableName; }; - - nsresult Open(); - nsresult AugmentAdds(const nsTArray& aPrefixes); - - ChunkSet& AddChunks() { return mAddChunks; } - ChunkSet& SubChunks() { return mSubChunks; } - const AddPrefixArray& AddPrefixes() const { return mAddPrefixes; } - const AddCompleteArray& AddCompletes() const { return mAddCompletes; } - const SubPrefixArray& SubPrefixes() const { return mSubPrefixes; } - const SubCompleteArray& SubCompletes() const { return mSubCompletes; } - - // ======= - // Updates - // ======= - // Begin the update process. Reads the store into memory. - nsresult BeginUpdate(); - - // Imports the data from a TableUpdate. - nsresult ApplyUpdate(TableUpdate &aUpdate); - - // Process expired chunks - nsresult Expire(); - - // Rebuild the store, Incorporating all the applied updates. - nsresult Rebuild(); - - // Write the current state of the store to disk. - // If you call between ApplyUpdate() and Rebuild(), you'll - // have a mess on your hands. - nsresult WriteFile(); - - // Drop memory used during the update process. - nsresult FinishUpdate(); - - // Force the entire store in memory - nsresult ReadEntireStore(); - -private: - static const int BUFFER_SIZE = 6 * 1024 * 1024; - - void Clear(); - nsresult Reset(); - - nsresult ReadHeader(); - nsresult SanityCheck(nsIFile* aStoreFile); - nsresult CalculateChecksum(nsCAutoString& aChecksum, bool aChecksumPresent); - nsresult CheckChecksum(nsIFile* aStoreFile); - void UpdateHeader(); - - nsresult EnsureChunkNumbers(); - nsresult ReadChunkNumbers(); - nsresult ReadHashes(); - nsresult ReadAddPrefixes(); - nsresult ReadSubPrefixes(); - - nsresult WriteAddPrefixes(nsIOutputStream* aOut); - nsresult WriteSubPrefixes(nsIOutputStream* aOut); - - nsresult ProcessSubs(); - - struct Header { - uint32 magic; - uint32 version; - uint32 numAddChunks; - uint32 numSubChunks; - uint32 numAddPrefixes; - uint32 numSubPrefixes; - uint32 numAddCompletes; - uint32 numSubCompletes; - }; - - Header mHeader; - - nsCString mTableName; - nsCOMPtr mStoreDirectory; - - bool mInUpdate; - - nsCOMPtr mInputStream; - - bool haveChunks; - ChunkSet mAddChunks; - ChunkSet mSubChunks; - - ChunkSet mAddExpirations; - ChunkSet mSubExpirations; - - AddPrefixArray mAddPrefixes; - AddCompleteArray mAddCompletes; - SubPrefixArray mSubPrefixes; - SubCompleteArray mSubCompletes; -}; - -} -} - -#endif diff --git a/toolkit/components/url-classifier/LookupCache.cpp b/toolkit/components/url-classifier/LookupCache.cpp deleted file mode 100644 index 22ad00281922..000000000000 --- a/toolkit/components/url-classifier/LookupCache.cpp +++ /dev/null @@ -1,776 +0,0 @@ -//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dave Camp - * Gian-Carlo Pascutto - * - * 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 "LookupCache.h" -#include "HashStore.h" -#include "nsISeekableStream.h" -#include "mozilla/Telemetry.h" -#include "prlog.h" -#include "prprf.h" - -// We act as the main entry point for all the real lookups, -// so note that those are not done to the actual HashStore. -// The latter solely exists to store the data needed to handle -// the updates from the protocol. - -// This module has its own store, which stores the Completions, -// mostly caching lookups that have happened over the net. -// The prefixes are cached/checked by looking them up in the -// PrefixSet. - -// Data format for the ".cache" files: -// uint32 magic Identify the file type -// uint32 version Version identifier for file format -// uint32 numCompletions Amount of completions stored -// 0...numCompletions 256-bit Completions - -// Name of the lookupcomplete cache -#define CACHE_SUFFIX ".cache" - -// Name of the persistent PrefixSet storage -#define PREFIXSET_SUFFIX ".pset" - -// NSPR_LOG_MODULES=UrlClassifierDbService:5 -extern PRLogModuleInfo *gUrlClassifierDbServiceLog; -#if defined(PR_LOGGING) -#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) -#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) -#else -#define LOG(args) -#define LOG_ENABLED() (false) -#endif - -namespace mozilla { -namespace safebrowsing { - -const uint32 LOOKUPCACHE_MAGIC = 0x1231af3e; -const uint32 CURRENT_VERSION = 1; - -LookupCache::LookupCache(const nsACString& aTableName, nsIFile* aStoreDir) - : mPrimed(false) - , mTableName(aTableName) - , mStoreDirectory(aStoreDir) -{ -} - -nsresult -LookupCache::Init() -{ - mPrefixSet = new nsUrlClassifierPrefixSet(); - nsresult rv = mPrefixSet->Init(mTableName); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -LookupCache::~LookupCache() -{ -} - -nsresult -LookupCache::Open() -{ - nsCOMPtr storeFile; - - nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = NS_NewLocalFileInputStream(getter_AddRefs(mInputStream), storeFile, - PR_RDONLY); - - if (NS_FAILED(rv) && rv != NS_ERROR_FILE_NOT_FOUND) { - Reset(); - return rv; - } - - if (rv == NS_ERROR_FILE_NOT_FOUND) { - Clear(); - UpdateHeader(); - return NS_OK; - } - - rv = ReadHeader(); - NS_ENSURE_SUCCESS(rv, rv); - - LOG(("ReadCompletions")); - rv = ReadCompletions(); - NS_ENSURE_SUCCESS(rv, rv); - - LOG(("Loading PrefixSet")); - rv = LoadPrefixSet(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -LookupCache::Reset() -{ - LOG(("LookupCache resetting")); - - nsCOMPtr storeFile; - nsCOMPtr prefixsetFile; - nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); - NS_ENSURE_SUCCESS(rv, rv); - rv = mStoreDirectory->Clone(getter_AddRefs(prefixsetFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX)); - NS_ENSURE_SUCCESS(rv, rv); - rv = prefixsetFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = storeFile->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); - rv = prefixsetFile->Remove(false); - NS_ENSURE_SUCCESS(rv, rv); - - Clear(); - - return NS_OK; -} - - -nsresult -LookupCache::Build(const AddPrefixArray& aAddPrefixes, - const AddCompleteArray& aAddCompletes) -{ - mCompletions.Clear(); - mCompletions.SetCapacity(aAddCompletes.Length()); - for (uint32 i = 0; i < aAddCompletes.Length(); i++) { - mCompletions.AppendElement(aAddCompletes[i].CompleteHash()); - } - mCompletions.Sort(); - - Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_COMPLETIONS, - static_cast(mCompletions.Length())); - - nsresult rv = ConstructPrefixSet(aAddPrefixes); - NS_ENSURE_SUCCESS(rv, rv); - mPrimed = true; - - Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LC_PREFIXES, - static_cast(aAddPrefixes.Length())); - - return NS_OK; -} - -#if defined(DEBUG) && defined(PR_LOGGING) -void -LookupCache::Dump() -{ - if (!LOG_ENABLED()) - return; - - for (uint32 i = 0; i < mCompletions.Length(); i++) { - nsCAutoString str; - mCompletions[i].ToString(str); - LOG(("Completion: %s", str.get())); - } -} -#endif - -nsresult -LookupCache::Has(const Completion& aCompletion, - const Completion& aHostkey, - const PRUint32 aHashKey, - bool* aHas, bool* aComplete, - Prefix* aOrigPrefix) -{ - *aHas = *aComplete = false; - - // check completion store first - if (mCompletions.BinaryIndexOf(aCompletion) != nsTArray::NoIndex) { - LOG(("Complete in %s", mTableName.get())); - *aComplete = true; - *aHas = true; - return NS_OK; - } - - PRUint32 prefix = aCompletion.ToUint32(); - PRUint32 hostkey = aHostkey.ToUint32(); - PRUint32 codedkey; - nsresult rv = KeyedHash(prefix, hostkey, aHashKey, &codedkey); - NS_ENSURE_SUCCESS(rv, rv); - - Prefix codedPrefix; - codedPrefix.FromUint32(codedkey); - *aOrigPrefix = codedPrefix; - - bool ready = true; - bool found; - rv = mPrefixSet->Probe(codedkey, &ready, &found); - NS_ENSURE_SUCCESS(rv, rv); - - LOG(("Probe in %s: %X, ready: %d found %d", mTableName.get(), prefix, ready, found)); - - if (found) { - *aHas = true; - } - - return NS_OK; -} - -nsresult -LookupCache::WriteFile() -{ - nsCOMPtr storeFile; - nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); - NS_ENSURE_SUCCESS(rv, rv); - rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX)); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr out; - rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(out), storeFile, - PR_WRONLY | PR_TRUNCATE | PR_CREATE_FILE); - NS_ENSURE_SUCCESS(rv, rv); - - UpdateHeader(); - LOG(("Writing %d completions", mHeader.numCompletions)); - - PRUint32 written; - rv = out->Write(reinterpret_cast(&mHeader), sizeof(mHeader), &written); - NS_ENSURE_SUCCESS(rv, rv); - - rv = WriteTArray(out, mCompletions); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr safeOut = do_QueryInterface(out); - rv = safeOut->Finish(); - NS_ENSURE_SUCCESS(rv, rv); - - rv = EnsureSizeConsistent(); - NS_ENSURE_SUCCESS(rv, rv); - - // Reopen the file now that we've rewritten it. - rv = NS_NewLocalFileInputStream(getter_AddRefs(mInputStream), storeFile, - PR_RDONLY); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr psFile; - rv = mStoreDirectory->Clone(getter_AddRefs(psFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mPrefixSet->StoreToFile(psFile); - NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store the prefixset"); - - return NS_OK; -} - -void -LookupCache::Clear() -{ - mCompletions.Clear(); - mPrefixSet->SetPrefixes(nsnull, 0); - mPrimed = false; -} - -void -LookupCache::UpdateHeader() -{ - mHeader.magic = LOOKUPCACHE_MAGIC; - mHeader.version = CURRENT_VERSION; - mHeader.numCompletions = mCompletions.Length(); -} - -nsresult -LookupCache::EnsureSizeConsistent() -{ - nsCOMPtr storeFile; - nsresult rv = mStoreDirectory->Clone(getter_AddRefs(storeFile)); - NS_ENSURE_SUCCESS(rv, rv); - rv = storeFile->AppendNative(mTableName + NS_LITERAL_CSTRING(CACHE_SUFFIX)); - NS_ENSURE_SUCCESS(rv, rv); - - PRInt64 fileSize; - rv = storeFile->GetFileSize(&fileSize); - NS_ENSURE_SUCCESS(rv, rv); - - if (fileSize < 0) { - return NS_ERROR_FAILURE; - } - - PRInt64 expectedSize = sizeof(mHeader) - + mHeader.numCompletions*sizeof(Completion); - if (expectedSize != fileSize) { - NS_WARNING("File length does not match. Probably corrupted."); - Reset(); - return NS_ERROR_FILE_CORRUPTED; - } - - return NS_OK; -} - -nsresult -LookupCache::ReadHeader() -{ - if (!mInputStream) { - Clear(); - UpdateHeader(); - return NS_OK; - } - - nsCOMPtr seekable = do_QueryInterface(mInputStream); - nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0); - NS_ENSURE_SUCCESS(rv, rv); - - void *buffer = &mHeader; - rv = NS_ReadInputStreamToBuffer(mInputStream, - &buffer, - sizeof(Header)); - NS_ENSURE_SUCCESS(rv, rv); - - if (mHeader.magic != LOOKUPCACHE_MAGIC || mHeader.version != CURRENT_VERSION) { - NS_WARNING("Unexpected header data in the store."); - Reset(); - return NS_ERROR_FILE_CORRUPTED; - } - LOG(("%d completions present", mHeader.numCompletions)); - - rv = EnsureSizeConsistent(); - NS_ENSURE_SUCCESS(rv, rv); - - return NS_OK; -} - -nsresult -LookupCache::ReadCompletions() -{ - if (!mHeader.numCompletions) { - mCompletions.Clear(); - return NS_OK; - } - - nsCOMPtr seekable = do_QueryInterface(mInputStream); - nsresult rv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, sizeof(Header)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = ReadTArray(mInputStream, &mCompletions, mHeader.numCompletions); - NS_ENSURE_SUCCESS(rv, rv); - - LOG(("Read %d completions", mCompletions.Length())); - - return NS_OK; -} - -/* static */ bool -LookupCache::IsCanonicalizedIP(const nsACString& aHost) -{ - // The canonicalization process will have left IP addresses in dotted - // decimal with no surprises. - PRUint32 i1, i2, i3, i4; - char c; - if (PR_sscanf(PromiseFlatCString(aHost).get(), "%u.%u.%u.%u%c", - &i1, &i2, &i3, &i4, &c) == 4) { - return (i1 <= 0xFF && i2 <= 0xFF && i3 <= 0xFF && i4 <= 0xFF); - } - - return false; -} - -/* static */ nsresult -LookupCache::GetKey(const nsACString& aSpec, - Completion* aHash, - nsCOMPtr& aCryptoHash) -{ - nsACString::const_iterator begin, end, iter; - aSpec.BeginReading(begin); - aSpec.EndReading(end); - - iter = begin; - if (!FindCharInReadable('/', iter, end)) { - return NS_OK; - } - - const nsCSubstring& host = Substring(begin, iter); - - if (IsCanonicalizedIP(host)) { - nsCAutoString key; - key.Assign(host); - key.Append("/"); - return aHash->FromPlaintext(key, aCryptoHash); - } - - nsTArray hostComponents; - ParseString(PromiseFlatCString(host), '.', hostComponents); - - if (hostComponents.Length() < 2) - return NS_ERROR_FAILURE; - - PRInt32 last = PRInt32(hostComponents.Length()) - 1; - nsCAutoString lookupHost; - - if (hostComponents.Length() > 2) { - lookupHost.Append(hostComponents[last - 2]); - lookupHost.Append("."); - } - - lookupHost.Append(hostComponents[last - 1]); - lookupHost.Append("."); - lookupHost.Append(hostComponents[last]); - lookupHost.Append("/"); - - return aHash->FromPlaintext(lookupHost, aCryptoHash); -} - -/* static */ nsresult -LookupCache::GetLookupFragments(const nsACString& aSpec, - nsTArray* aFragments) - -{ - aFragments->Clear(); - - nsACString::const_iterator begin, end, iter; - aSpec.BeginReading(begin); - aSpec.EndReading(end); - - iter = begin; - if (!FindCharInReadable('/', iter, end)) { - return NS_OK; - } - - const nsCSubstring& host = Substring(begin, iter++); - nsCAutoString path; - path.Assign(Substring(iter, end)); - - /** - * From the protocol doc: - * For the hostname, the client will try at most 5 different strings. They - * are: - * a) The exact hostname of the url - * b) The 4 hostnames formed by starting with the last 5 components and - * successivly removing the leading component. The top-level component - * can be skipped. This is not done if the hostname is a numerical IP. - */ - nsTArray hosts; - hosts.AppendElement(host); - - if (!IsCanonicalizedIP(host)) { - host.BeginReading(begin); - host.EndReading(end); - int numHostComponents = 0; - while (RFindInReadable(NS_LITERAL_CSTRING("."), begin, end) && - numHostComponents < MAX_HOST_COMPONENTS) { - // don't bother checking toplevel domains - if (++numHostComponents >= 2) { - host.EndReading(iter); - hosts.AppendElement(Substring(end, iter)); - } - end = begin; - host.BeginReading(begin); - } - } - - /** - * From the protocol doc: - * For the path, the client will also try at most 6 different strings. - * They are: - * a) the exact path of the url, including query parameters - * b) the exact path of the url, without query parameters - * c) the 4 paths formed by starting at the root (/) and - * successively appending path components, including a trailing - * slash. This behavior should only extend up to the next-to-last - * path component, that is, a trailing slash should never be - * appended that was not present in the original url. - */ - nsTArray paths; - nsCAutoString pathToAdd; - - path.BeginReading(begin); - path.EndReading(end); - iter = begin; - if (FindCharInReadable('?', iter, end)) { - pathToAdd = Substring(begin, iter); - paths.AppendElement(pathToAdd); - end = iter; - } - - int numPathComponents = 1; - iter = begin; - while (FindCharInReadable('/', iter, end) && - numPathComponents < MAX_PATH_COMPONENTS) { - iter++; - pathToAdd.Assign(Substring(begin, iter)); - paths.AppendElement(pathToAdd); - numPathComponents++; - } - - // If we haven't already done so, add the full path - if (!pathToAdd.Equals(path)) { - paths.AppendElement(path); - } - // Check an empty path (for whole-domain blacklist entries) - paths.AppendElement(EmptyCString()); - - for (PRUint32 hostIndex = 0; hostIndex < hosts.Length(); hostIndex++) { - for (PRUint32 pathIndex = 0; pathIndex < paths.Length(); pathIndex++) { - nsCString key; - key.Assign(hosts[hostIndex]); - key.Append('/'); - key.Append(paths[pathIndex]); - LOG(("Chking %s", key.get())); - - aFragments->AppendElement(key); - } - } - - return NS_OK; -} - -/* static */ nsresult -LookupCache::GetHostKeys(const nsACString& aSpec, - nsTArray* aHostKeys) -{ - nsACString::const_iterator begin, end, iter; - aSpec.BeginReading(begin); - aSpec.EndReading(end); - - iter = begin; - if (!FindCharInReadable('/', iter, end)) { - return NS_OK; - } - - const nsCSubstring& host = Substring(begin, iter); - - if (IsCanonicalizedIP(host)) { - nsCString *key = aHostKeys->AppendElement(); - if (!key) - return NS_ERROR_OUT_OF_MEMORY; - - key->Assign(host); - key->Append("/"); - return NS_OK; - } - - nsTArray hostComponents; - ParseString(PromiseFlatCString(host), '.', hostComponents); - - if (hostComponents.Length() < 2) { - // no host or toplevel host, this won't match anything in the db - return NS_OK; - } - - // First check with two domain components - PRInt32 last = PRInt32(hostComponents.Length()) - 1; - nsCString *lookupHost = aHostKeys->AppendElement(); - if (!lookupHost) - return NS_ERROR_OUT_OF_MEMORY; - - lookupHost->Assign(hostComponents[last - 1]); - lookupHost->Append("."); - lookupHost->Append(hostComponents[last]); - lookupHost->Append("/"); - - // Now check with three domain components - if (hostComponents.Length() > 2) { - nsCString *lookupHost2 = aHostKeys->AppendElement(); - if (!lookupHost2) - return NS_ERROR_OUT_OF_MEMORY; - lookupHost2->Assign(hostComponents[last - 2]); - lookupHost2->Append("."); - lookupHost2->Append(*lookupHost); - } - - return NS_OK; -} - -/* We have both a prefix and a domain. Drop the domain, but - hash the domain, the prefix and a random value together, - ensuring any collisions happens at a different points for - different users. -*/ -/* static */ nsresult LookupCache::KeyedHash(PRUint32 aPref, PRUint32 aDomain, - PRUint32 aKey, PRUint32* aOut) -{ - /* This is a reimplementation of MurmurHash3 32-bit - based on the public domain C++ sources. - http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp - for nblocks = 2 - */ - PRUint32 c1 = 0xCC9E2D51; - PRUint32 c2 = 0x1B873593; - PRUint32 c3 = 0xE6546B64; - PRUint32 c4 = 0x85EBCA6B; - PRUint32 c5 = 0xC2B2AE35; - PRUint32 h1 = aPref; // seed - PRUint32 k1; - PRUint32 karr[2]; - - karr[0] = aDomain; - karr[1] = aKey; - - for (PRUint32 i = 0; i < 2; i++) { - k1 = karr[i]; - k1 *= c1; - k1 = (k1 << 15) | (k1 >> (32-15)); - k1 *= c2; - - h1 ^= k1; - h1 = (h1 << 13) | (h1 >> (32-13)); - h1 *= 5; - h1 += c3; - } - - h1 ^= 2; // len - // fmix - h1 ^= h1 >> 16; - h1 *= c4; - h1 ^= h1 >> 13; - h1 *= c5; - h1 ^= h1 >> 16; - - *aOut = h1; - - return NS_OK; -} - -bool LookupCache::IsPrimed() -{ - return mPrimed; -} - -nsresult -LookupCache::ConstructPrefixSet(const AddPrefixArray& aAddPrefixes) -{ - Telemetry::AutoTimer timer; - - nsTArray array; - array.SetCapacity(aAddPrefixes.Length()); - - for (uint32 i = 0; i < aAddPrefixes.Length(); i++) { - array.AppendElement(aAddPrefixes[i].PrefixHash().ToUint32()); - } - - // clear old tree - if (array.IsEmpty()) { - // DB is empty, but put a sentinel to show that we looked - array.AppendElement(0); - } - // PrefixSet requires sorted order - array.Sort(); - - // construct new one, replace old entries - nsresult rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length()); - if (NS_FAILED(rv)) { - goto error_bailout; - } - -#ifdef DEBUG - PRUint32 size; - size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of); - LOG(("SB tree done, size = %d bytes\n", size)); -#endif - - mPrimed = true; - - return NS_OK; - - error_bailout: - // load an empty prefixset so the browser can work - nsAutoTArray sentinel; - sentinel.Clear(); - sentinel.AppendElement(0); - mPrefixSet->SetPrefixes(sentinel.Elements(), sentinel.Length()); - if (rv == NS_ERROR_OUT_OF_MEMORY) { - Telemetry::Accumulate(Telemetry::URLCLASSIFIER_PS_OOM, 1); - } - return rv; -} - -nsresult -LookupCache::LoadPrefixSet() -{ - nsCOMPtr psFile; - nsresult rv = mStoreDirectory->Clone(getter_AddRefs(psFile)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = psFile->AppendNative(mTableName + NS_LITERAL_CSTRING(PREFIXSET_SUFFIX)); - NS_ENSURE_SUCCESS(rv, rv); - - bool exists; - rv = psFile->Exists(&exists); - NS_ENSURE_SUCCESS(rv, rv); - - if (exists) { - LOG(("stored PrefixSet exists, loading from disk")); - rv = mPrefixSet->LoadFromFile(psFile); - } - if (!exists || NS_FAILED(rv)) { - LOG(("no (usable) stored PrefixSet found")); - } else { - mPrimed = true; - } - -#ifdef DEBUG - if (mPrimed) { - PRUint32 size = mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of); - LOG(("SB tree done, size = %d bytes\n", size)); - } -#endif - - return NS_OK; -} - -nsresult -LookupCache::GetPrefixes(nsTArray* aAddPrefixes) -{ - if (!mPrimed) { - // This can happen if its a new table, so no error. - LOG(("GetPrefixes from empty LookupCache")); - return NS_OK; - } - PRUint32 cnt; - PRUint32 *arr; - nsresult rv = mPrefixSet->GetPrefixes(&cnt, &arr); - NS_ENSURE_SUCCESS(rv, rv); - if (!aAddPrefixes->AppendElements(arr, cnt)) - return NS_ERROR_FAILURE; - nsMemory::Free(arr); - return NS_OK; -} - - -} -} diff --git a/toolkit/components/url-classifier/LookupCache.h b/toolkit/components/url-classifier/LookupCache.h deleted file mode 100644 index 5ba07d5fe099..000000000000 --- a/toolkit/components/url-classifier/LookupCache.h +++ /dev/null @@ -1,186 +0,0 @@ -//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dave Camp - * Gian-Carlo Pascutto - * - * 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 LookupCache_h__ -#define LookupCache_h__ - -#include "Entries.h" -#include "nsString.h" -#include "nsTArray.h" -#include "nsAutoPtr.h" -#include "nsCOMPtr.h" -#include "nsIFile.h" -#include "nsUrlClassifierPrefixSet.h" -#include "prlog.h" - -namespace mozilla { -namespace safebrowsing { - -#define MAX_HOST_COMPONENTS 5 -#define MAX_PATH_COMPONENTS 4 - -class LookupResult { -public: - LookupResult() : mComplete(false), mNoise(false), mFresh(false), mProtocolConfirmed(false) {} - - // The fragment that matched in the LookupCache - union { - Prefix prefix; - Completion complete; - } hash; - - const Prefix &PrefixHash() { return hash.prefix; } - const Completion &CompleteHash() { return hash.complete; } - - bool Confirmed() const { return (mComplete && mFresh) || mProtocolConfirmed; } - bool Complete() const { return mComplete; } - - // True if we have a complete match for this hash in the table. - bool mComplete; - - // True if this is a noise entry, i.e. an extra entry - // that is inserted to mask the true URL we are requesting - bool mNoise; - - // Value of actual key looked up in the prefixset (coded with client key) - Prefix mCodedPrefix; - - // True if we've updated this table recently-enough. - bool mFresh; - - bool mProtocolConfirmed; - - nsCString mTableName; -}; - -typedef nsTArray LookupResultArray; - -struct CacheResult { - AddComplete entry; - nsCString table; -}; -typedef nsTArray CacheResultArray; - -class LookupCache { -public: - // Check for a canonicalized IP address. - static bool IsCanonicalizedIP(const nsACString& aHost); - - // take a lookup string (www.hostname.com/path/to/resource.html) and - // expand it into the set of fragments that should be searched for in an - // entry - static nsresult GetLookupFragments(const nsACString& aSpec, - nsTArray* aFragments); - // Similar to GetKey(), but if the domain contains three or more components, - // two keys will be returned: - // hostname.com/foo/bar -> [hostname.com] - // mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com] - // www.mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com] - static nsresult GetHostKeys(const nsACString& aSpec, - nsTArray* aHostKeys); - // Get the database key for a given URI. This is the top three - // domain components if they exist, otherwise the top two. - // hostname.com/foo/bar -> hostname.com - // mail.hostname.com/foo/bar -> mail.hostname.com - // www.mail.hostname.com/foo/bar -> mail.hostname.com - static nsresult GetKey(const nsACString& aSpec, Completion* aHash, - nsCOMPtr& aCryptoHash); - - /* We have both a prefix and a domain. Drop the domain, but - hash the domain, the prefix and a random value together, - ensuring any collisions happens at a different points for - different users. - */ - static nsresult KeyedHash(PRUint32 aPref, PRUint32 aDomain, - PRUint32 aKey, PRUint32* aOut); - - LookupCache(const nsACString& aTableName, nsIFile* aStoreFile); - ~LookupCache(); - - const nsCString &TableName() const { return mTableName; } - - nsresult Init(); - nsresult Open(); - nsresult Build(const AddPrefixArray& aAddPrefixes, - const AddCompleteArray& aAddCompletes); - nsresult GetPrefixes(nsTArray* aAddPrefixes); - -#if DEBUG && defined(PR_LOGGING) - void Dump(); -#endif - nsresult WriteFile(); - nsresult Has(const Completion& aCompletion, - const Completion& aHostkey, - PRUint32 aHashKey, - bool* aHas, bool* aComplete, - Prefix* aOrigPrefix); - bool IsPrimed(); - -private: - - void Clear(); - nsresult Reset(); - void UpdateHeader(); - nsresult ReadHeader(); - nsresult EnsureSizeConsistent(); - nsresult ReadCompletions(); - // Construct a Prefix Set with known prefixes - nsresult LoadPrefixSet(); - nsresult ConstructPrefixSet(const AddPrefixArray& aAddPrefixes); - - struct Header { - uint32 magic; - uint32 version; - uint32 numCompletions; - }; - Header mHeader; - - bool mPrimed; - nsCString mTableName; - nsCOMPtr mStoreDirectory; - nsCOMPtr mInputStream; - CompletionArray mCompletions; - // Set of prefixes known to be in the database - nsRefPtr mPrefixSet; -}; - -} -} - -#endif diff --git a/toolkit/components/url-classifier/Makefile.in b/toolkit/components/url-classifier/Makefile.in index 2381ed8afa2a..7d6902331023 100644 --- a/toolkit/components/url-classifier/Makefile.in +++ b/toolkit/components/url-classifier/Makefile.in @@ -59,17 +59,11 @@ XPIDLSRCS = \ $(NULL) CPPSRCS = \ - ChunkSet.cpp \ - Classifier.cpp \ - HashStore.cpp \ - ProtocolParser.cpp \ - LookupCache.cpp \ nsUrlClassifierDBService.cpp \ nsUrlClassifierStreamUpdater.cpp \ nsUrlClassifierUtils.cpp \ nsUrlClassifierPrefixSet.cpp \ nsUrlClassifierProxies.cpp \ - nsCheckSummedOutputStream.cpp \ $(NULL) LOCAL_INCLUDES = \ diff --git a/toolkit/components/url-classifier/ProtocolParser.cpp b/toolkit/components/url-classifier/ProtocolParser.cpp deleted file mode 100644 index 52c47e5b35ba..000000000000 --- a/toolkit/components/url-classifier/ProtocolParser.cpp +++ /dev/null @@ -1,777 +0,0 @@ -//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dave Camp - * Gian-Carlo Pascutto - * - * 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 ***** */ - -//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -#include "ProtocolParser.h" -#include "LookupCache.h" -#include "nsIKeyModule.h" -#include "nsNetCID.h" -#include "prlog.h" -#include "prnetdb.h" -#include "prprf.h" - -#include "nsUrlClassifierUtils.h" - -// NSPR_LOG_MODULES=UrlClassifierDbService:5 -extern PRLogModuleInfo *gUrlClassifierDbServiceLog; -#if defined(PR_LOGGING) -#define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) -#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) -#else -#define LOG(args) -#define LOG_ENABLED() (PR_FALSE) -#endif - -namespace mozilla { -namespace safebrowsing { - -// Updates will fail if fed chunks larger than this -const uint32 MAX_CHUNK_SIZE = (1024 * 1024); - -const uint32 DOMAIN_SIZE = 4; - -// Parse one stringified range of chunks of the form "n" or "n-m" from a -// comma-separated list of chunks. Upon return, 'begin' will point to the -// next range of chunks in the list of chunks. -static bool -ParseChunkRange(nsACString::const_iterator& aBegin, - const nsACString::const_iterator& aEnd, - PRUint32* aFirst, PRUint32* aLast) -{ - nsACString::const_iterator iter = aBegin; - FindCharInReadable(',', iter, aEnd); - - nsCAutoString element(Substring(aBegin, iter)); - aBegin = iter; - if (aBegin != aEnd) - aBegin++; - - PRUint32 numRead = PR_sscanf(element.get(), "%u-%u", aFirst, aLast); - if (numRead == 2) { - if (*aFirst > *aLast) { - PRUint32 tmp = *aFirst; - *aFirst = *aLast; - *aLast = tmp; - } - return true; - } - - if (numRead == 1) { - *aLast = *aFirst; - return true; - } - - return false; -} - -ProtocolParser::ProtocolParser(PRUint32 aHashKey) - : mState(PROTOCOL_STATE_CONTROL) - , mHashKey(aHashKey) - , mUpdateStatus(NS_OK) - , mUpdateWait(0) - , mResetRequested(false) - , mRekeyRequested(false) -{ -} - -ProtocolParser::~ProtocolParser() -{ - CleanupUpdates(); -} - -nsresult -ProtocolParser::Init(nsICryptoHash* aHasher) -{ - mCryptoHash = aHasher; - return NS_OK; -} - -/** - * Initialize HMAC for the stream. - * - * If serverMAC is empty, the update stream will need to provide a - * server MAC. - */ -nsresult -ProtocolParser::InitHMAC(const nsACString& aClientKey, - const nsACString& aServerMAC) -{ - mServerMAC = aServerMAC; - - nsresult rv; - nsCOMPtr keyObjectFactory( - do_GetService("@mozilla.org/security/keyobjectfactory;1", &rv)); - - if (NS_FAILED(rv)) { - NS_WARNING("Failed to get nsIKeyObjectFactory service"); - mUpdateStatus = rv; - return mUpdateStatus; - } - - nsCOMPtr keyObject; - rv = keyObjectFactory->KeyFromString(nsIKeyObject::HMAC, aClientKey, - getter_AddRefs(keyObject)); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to create key object, maybe not FIPS compliant?"); - mUpdateStatus = rv; - return mUpdateStatus; - } - - mHMAC = do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to create nsICryptoHMAC instance"); - mUpdateStatus = rv; - return mUpdateStatus; - } - - rv = mHMAC->Init(nsICryptoHMAC::SHA1, keyObject); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to initialize nsICryptoHMAC instance"); - mUpdateStatus = rv; - return mUpdateStatus; - } - return NS_OK; -} - -nsresult -ProtocolParser::FinishHMAC() -{ - if (NS_FAILED(mUpdateStatus)) { - return mUpdateStatus; - } - - if (mRekeyRequested) { - mUpdateStatus = NS_ERROR_FAILURE; - return mUpdateStatus; - } - - if (!mHMAC) { - return NS_OK; - } - - nsCAutoString clientMAC; - mHMAC->Finish(PR_TRUE, clientMAC); - - if (clientMAC != mServerMAC) { - NS_WARNING("Invalid update MAC!"); - LOG(("Invalid update MAC: expected %s, got %s", - clientMAC.get(), mServerMAC.get())); - mUpdateStatus = NS_ERROR_FAILURE; - } - return mUpdateStatus; -} - -void -ProtocolParser::SetCurrentTable(const nsACString& aTable) -{ - mTableUpdate = GetTableUpdate(aTable); -} - -nsresult -ProtocolParser::AppendStream(const nsACString& aData) -{ - if (NS_FAILED(mUpdateStatus)) - return mUpdateStatus; - - nsresult rv; - - // Digest the data if we have a server MAC. - if (mHMAC && !mServerMAC.IsEmpty()) { - rv = mHMAC->Update(reinterpret_cast(aData.BeginReading()), - aData.Length()); - if (NS_FAILED(rv)) { - mUpdateStatus = rv; - return rv; - } - } - - mPending.Append(aData); - - bool done = false; - while (!done) { - if (mState == PROTOCOL_STATE_CONTROL) { - rv = ProcessControl(&done); - } else if (mState == PROTOCOL_STATE_CHUNK) { - rv = ProcessChunk(&done); - } else { - NS_ERROR("Unexpected protocol state"); - rv = NS_ERROR_FAILURE; - } - if (NS_FAILED(rv)) { - mUpdateStatus = rv; - return rv; - } - } - return NS_OK; -} - -nsresult -ProtocolParser::ProcessControl(bool* aDone) -{ - nsresult rv; - - nsCAutoString line; - *aDone = true; - while (NextLine(line)) { - //LOG(("Processing %s\n", line.get())); - - if (line.EqualsLiteral("e:pleaserekey")) { - mRekeyRequested = true; - return NS_OK; - } else if (mHMAC && mServerMAC.IsEmpty()) { - rv = ProcessMAC(line); - NS_ENSURE_SUCCESS(rv, rv); - } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) { - SetCurrentTable(Substring(line, 2)); - } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) { - if (PR_sscanf(line.get(), "n:%d", &mUpdateWait) != 1) { - LOG(("Error parsing n: '%s' (%d)", line.get(), mUpdateWait)); - mUpdateWait = 0; - } - } else if (line.EqualsLiteral("r:pleasereset")) { - mResetRequested = true; - } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("u:"))) { - rv = ProcessForward(line); - NS_ENSURE_SUCCESS(rv, rv); - } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("a:")) || - StringBeginsWith(line, NS_LITERAL_CSTRING("s:"))) { - rv = ProcessChunkControl(line); - NS_ENSURE_SUCCESS(rv, rv); - *aDone = false; - return NS_OK; - } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("ad:")) || - StringBeginsWith(line, NS_LITERAL_CSTRING("sd:"))) { - rv = ProcessExpirations(line); - NS_ENSURE_SUCCESS(rv, rv); - } - } - - *aDone = true; - return NS_OK; -} - - -nsresult -ProtocolParser::ProcessMAC(const nsCString& aLine) -{ - nsresult rv; - - LOG(("line: %s", aLine.get())); - - if (StringBeginsWith(aLine, NS_LITERAL_CSTRING("m:"))) { - mServerMAC = Substring(aLine, 2); - nsUrlClassifierUtils::UnUrlsafeBase64(mServerMAC); - - // The remainder of the pending update wasn't digested, digest it now. - rv = mHMAC->Update(reinterpret_cast(mPending.BeginReading()), - mPending.Length()); - return rv; - } - - LOG(("No MAC specified!")); - return NS_ERROR_FAILURE; -} - -nsresult -ProtocolParser::ProcessExpirations(const nsCString& aLine) -{ - if (!mTableUpdate) { - NS_WARNING("Got an expiration without a table."); - return NS_ERROR_FAILURE; - } - const nsCSubstring &list = Substring(aLine, 3); - nsACString::const_iterator begin, end; - list.BeginReading(begin); - list.EndReading(end); - while (begin != end) { - PRUint32 first, last; - if (ParseChunkRange(begin, end, &first, &last)) { - for (PRUint32 num = first; num <= last; num++) { - if (aLine[0] == 'a') - mTableUpdate->NewAddExpiration(num); - else - mTableUpdate->NewSubExpiration(num); - } - } else { - return NS_ERROR_FAILURE; - } - } - return NS_OK; -} - -nsresult -ProtocolParser::ProcessChunkControl(const nsCString& aLine) -{ - if (!mTableUpdate) { - NS_WARNING("Got a chunk before getting a table."); - return NS_ERROR_FAILURE; - } - - mState = PROTOCOL_STATE_CHUNK; - char command; - - mChunkState.Clear(); - - if (PR_sscanf(aLine.get(), - "%c:%d:%d:%d", - &command, - &mChunkState.num, &mChunkState.hashSize, &mChunkState.length) - != 4) - { - return NS_ERROR_FAILURE; - } - - if (mChunkState.length > MAX_CHUNK_SIZE) { - return NS_ERROR_FAILURE; - } - - if (!(mChunkState.hashSize == PREFIX_SIZE || mChunkState.hashSize == COMPLETE_SIZE)) { - NS_WARNING("Invalid hash size specified in update."); - return NS_ERROR_FAILURE; - } - - mChunkState.type = (command == 'a') ? CHUNK_ADD : CHUNK_SUB; - - if (mChunkState.type == CHUNK_ADD) { - mTableUpdate->NewAddChunk(mChunkState.num); - } else { - mTableUpdate->NewSubChunk(mChunkState.num); - } - - return NS_OK; -} - -nsresult -ProtocolParser::ProcessForward(const nsCString& aLine) -{ - const nsCSubstring &forward = Substring(aLine, 2); - if (mHMAC) { - // We're expecting MACs alongside any url forwards. - nsCSubstring::const_iterator begin, end, sepBegin, sepEnd; - forward.BeginReading(begin); - sepBegin = begin; - - forward.EndReading(end); - sepEnd = end; - - if (!RFindInReadable(NS_LITERAL_CSTRING(","), sepBegin, sepEnd)) { - NS_WARNING("No MAC specified for a redirect in a request that expects a MAC"); - return NS_ERROR_FAILURE; - } - - nsCString serverMAC(Substring(sepEnd, end)); - nsUrlClassifierUtils::UnUrlsafeBase64(serverMAC); - return AddForward(Substring(begin, sepBegin), serverMAC); - } - return AddForward(forward, mServerMAC); -} - -nsresult -ProtocolParser::AddForward(const nsACString& aUrl, const nsACString& aMac) -{ - if (!mTableUpdate) { - NS_WARNING("Forward without a table name."); - return NS_ERROR_FAILURE; - } - - ForwardedUpdate *forward = mForwards.AppendElement(); - forward->table = mTableUpdate->TableName(); - forward->url.Assign(aUrl); - forward->mac.Assign(aMac); - - return NS_OK; -} - -nsresult -ProtocolParser::ProcessChunk(bool* aDone) -{ - if (!mTableUpdate) { - NS_WARNING("Processing chunk without an active table."); - return NS_ERROR_FAILURE; - } - - NS_ASSERTION(mChunkState.num != 0, "Must have a chunk number."); - - if (mPending.Length() < mChunkState.length) { - *aDone = true; - return NS_OK; - } - - // Pull the chunk out of the pending stream data. - nsCAutoString chunk; - chunk.Assign(Substring(mPending, 0, mChunkState.length)); - mPending = Substring(mPending, mChunkState.length); - - *aDone = false; - mState = PROTOCOL_STATE_CONTROL; - - //LOG(("Handling a %d-byte chunk", chunk.Length())); - if (StringEndsWith(mTableUpdate->TableName(), NS_LITERAL_CSTRING("-shavar"))) { - return ProcessShaChunk(chunk); - } else { - return ProcessPlaintextChunk(chunk); - } -} - -/** - * Process a plaintext chunk (currently only used in unit tests). - */ -nsresult -ProtocolParser::ProcessPlaintextChunk(const nsACString& aChunk) -{ - if (!mTableUpdate) { - NS_WARNING("Chunk received with no table."); - return NS_ERROR_FAILURE; - } - - nsresult rv; - nsTArray lines; - ParseString(PromiseFlatCString(aChunk), '\n', lines); - - // non-hashed tables need to be hashed - for (uint32 i = 0; i < lines.Length(); i++) { - nsCString& line = lines[i]; - - if (mChunkState.type == CHUNK_ADD) { - if (mChunkState.hashSize == COMPLETE_SIZE) { - Completion hash; - hash.FromPlaintext(line, mCryptoHash); - mTableUpdate->NewAddComplete(mChunkState.num, hash); - } else { - NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks."); - Completion hash; - Completion domHash; - Prefix newHash; - rv = LookupCache::GetKey(line, &domHash, mCryptoHash); - NS_ENSURE_SUCCESS(rv, rv); - hash.FromPlaintext(line, mCryptoHash); - PRUint32 codedHash; - rv = LookupCache::KeyedHash(hash.ToUint32(), domHash.ToUint32(), mHashKey, &codedHash); - NS_ENSURE_SUCCESS(rv, rv); - newHash.FromUint32(codedHash); - mTableUpdate->NewAddPrefix(mChunkState.num, newHash); - } - } else { - nsCString::const_iterator begin, iter, end; - line.BeginReading(begin); - line.EndReading(end); - iter = begin; - uint32 addChunk; - if (!FindCharInReadable(':', iter, end) || - PR_sscanf(lines[i].get(), "%d:", &addChunk) != 1) { - NS_WARNING("Received sub chunk without associated add chunk."); - return NS_ERROR_FAILURE; - } - iter++; - - if (mChunkState.hashSize == COMPLETE_SIZE) { - Completion hash; - hash.FromPlaintext(Substring(iter, end), mCryptoHash); - mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num); - } else { - NS_ASSERTION(mChunkState.hashSize == 4, "Only 32- or 4-byte hashes can be used for add chunks."); - Prefix hash; - Completion domHash; - Prefix newHash; - rv = LookupCache::GetKey(Substring(iter, end), &domHash, mCryptoHash); - NS_ENSURE_SUCCESS(rv, rv); - hash.FromPlaintext(Substring(iter, end), mCryptoHash); - PRUint32 codedHash; - rv = LookupCache::KeyedHash(hash.ToUint32(), domHash.ToUint32(), mHashKey, &codedHash); - NS_ENSURE_SUCCESS(rv, rv); - newHash.FromUint32(codedHash); - mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num); - // Needed to knock out completes - // Fake chunk nr, will cause it to be removed next update - mTableUpdate->NewSubPrefix(addChunk, hash, 0); - mTableUpdate->NewSubChunk(0); - } - } - } - - return NS_OK; -} - -nsresult -ProtocolParser::ProcessShaChunk(const nsACString& aChunk) -{ - PRUint32 start = 0; - while (start < aChunk.Length()) { - // First four bytes are the domain key. - Prefix domain; - domain.Assign(Substring(aChunk, start, DOMAIN_SIZE)); - start += DOMAIN_SIZE; - - // Then a count of entries. - uint8 numEntries = static_cast(aChunk[start]); - start++; - - nsresult rv; - if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == PREFIX_SIZE) { - rv = ProcessHostAdd(domain, numEntries, aChunk, &start); - } else if (mChunkState.type == CHUNK_ADD && mChunkState.hashSize == COMPLETE_SIZE) { - rv = ProcessHostAddComplete(numEntries, aChunk, &start); - } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == PREFIX_SIZE) { - rv = ProcessHostSub(domain, numEntries, aChunk, &start); - } else if (mChunkState.type == CHUNK_SUB && mChunkState.hashSize == COMPLETE_SIZE) { - rv = ProcessHostSubComplete(numEntries, aChunk, &start); - } else { - NS_WARNING("Unexpected chunk type/hash size!"); - LOG(("Got an unexpected chunk type/hash size: %s:%d", - mChunkState.type == CHUNK_ADD ? "add" : "sub", - mChunkState.hashSize)); - return NS_ERROR_FAILURE; - } - NS_ENSURE_SUCCESS(rv, rv); - } - - return NS_OK; -} - -nsresult -ProtocolParser::ProcessHostAdd(const Prefix& aDomain, PRUint8 aNumEntries, - const nsACString& aChunk, PRUint32* aStart) -{ - NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE, - "ProcessHostAdd should only be called for prefix hashes."); - - PRUint32 codedHash; - PRUint32 domHash = aDomain.ToUint32(); - - if (aNumEntries == 0) { - nsresult rv = LookupCache::KeyedHash(domHash, domHash, mHashKey, &codedHash); - NS_ENSURE_SUCCESS(rv, rv); - Prefix newHash; - newHash.FromUint32(codedHash); - mTableUpdate->NewAddPrefix(mChunkState.num, newHash); - return NS_OK; - } - - if (*aStart + (PREFIX_SIZE * aNumEntries) > aChunk.Length()) { - NS_WARNING("Chunk is not long enough to contain the expected entries."); - return NS_ERROR_FAILURE; - } - - for (uint8 i = 0; i < aNumEntries; i++) { - Prefix hash; - hash.Assign(Substring(aChunk, *aStart, PREFIX_SIZE)); - nsresult rv = LookupCache::KeyedHash(domHash, hash.ToUint32(), mHashKey, &codedHash); - NS_ENSURE_SUCCESS(rv, rv); - Prefix newHash; - newHash.FromUint32(codedHash); - mTableUpdate->NewAddPrefix(mChunkState.num, newHash); - *aStart += PREFIX_SIZE; - } - - return NS_OK; -} - -nsresult -ProtocolParser::ProcessHostSub(const Prefix& aDomain, PRUint8 aNumEntries, - const nsACString& aChunk, PRUint32 *aStart) -{ - NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE, - "ProcessHostSub should only be called for prefix hashes."); - - PRUint32 codedHash; - PRUint32 domHash = aDomain.ToUint32(); - - if (aNumEntries == 0) { - if ((*aStart) + 4 > aChunk.Length()) { - NS_WARNING("Received a zero-entry sub chunk without an associated add."); - return NS_ERROR_FAILURE; - } - - const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4); - *aStart += 4; - - uint32 addChunk; - memcpy(&addChunk, addChunkStr.BeginReading(), 4); - addChunk = PR_ntohl(addChunk); - - nsresult rv = LookupCache::KeyedHash(domHash, domHash, mHashKey, &codedHash); - NS_ENSURE_SUCCESS(rv, rv); - Prefix newHash; - newHash.FromUint32(codedHash); - - mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num); - // Needed to knock out completes - // Fake chunk nr, will cause it to be removed next update - mTableUpdate->NewSubPrefix(addChunk, aDomain, 0); - mTableUpdate->NewSubChunk(0); - return NS_OK; - } - - if (*aStart + ((PREFIX_SIZE + 4) * aNumEntries) > aChunk.Length()) { - NS_WARNING("Chunk is not long enough to contain the expected entries."); - return NS_ERROR_FAILURE; - } - - for (uint8 i = 0; i < aNumEntries; i++) { - const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4); - *aStart += 4; - - uint32 addChunk; - memcpy(&addChunk, addChunkStr.BeginReading(), 4); - addChunk = PR_ntohl(addChunk); - - Prefix prefix; - prefix.Assign(Substring(aChunk, *aStart, PREFIX_SIZE)); - *aStart += PREFIX_SIZE; - - nsresult rv = LookupCache::KeyedHash(prefix.ToUint32(), domHash, mHashKey, &codedHash); - NS_ENSURE_SUCCESS(rv, rv); - Prefix newHash; - newHash.FromUint32(codedHash); - - mTableUpdate->NewSubPrefix(addChunk, newHash, mChunkState.num); - // Needed to knock out completes - // Fake chunk nr, will cause it to be removed next update - mTableUpdate->NewSubPrefix(addChunk, prefix, 0); - mTableUpdate->NewSubChunk(0); - } - - return NS_OK; -} - -nsresult -ProtocolParser::ProcessHostAddComplete(PRUint8 aNumEntries, - const nsACString& aChunk, PRUint32* aStart) -{ - NS_ASSERTION(mChunkState.hashSize == COMPLETE_SIZE, - "ProcessHostAddComplete should only be called for complete hashes."); - - if (aNumEntries == 0) { - // this is totally comprehensible. - NS_WARNING("Expected > 0 entries for a 32-byte hash add."); - return NS_OK; - } - - if (*aStart + (COMPLETE_SIZE * aNumEntries) > aChunk.Length()) { - NS_WARNING("Chunk is not long enough to contain the expected entries."); - return NS_ERROR_FAILURE; - } - - for (uint8 i = 0; i < aNumEntries; i++) { - Completion hash; - hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE)); - mTableUpdate->NewAddComplete(mChunkState.num, hash); - *aStart += COMPLETE_SIZE; - } - - return NS_OK; -} - -nsresult -ProtocolParser::ProcessHostSubComplete(PRUint8 aNumEntries, - const nsACString& aChunk, PRUint32* aStart) -{ - NS_ASSERTION(mChunkState.hashSize == PREFIX_SIZE, - "ProcessHostSub should only be called for prefix hashes."); - - if (aNumEntries == 0) { - // this is totally comprehensible. - NS_WARNING("Expected > 0 entries for a 32-byte hash add."); - return NS_OK; - } - - if (*aStart + ((COMPLETE_SIZE + 4) * aNumEntries) > aChunk.Length()) { - NS_WARNING("Chunk is not long enough to contain the expected entries."); - return NS_ERROR_FAILURE; - } - - for (PRUint8 i = 0; i < aNumEntries; i++) { - Completion hash; - hash.Assign(Substring(aChunk, *aStart, COMPLETE_SIZE)); - *aStart += COMPLETE_SIZE; - - const nsCSubstring& addChunkStr = Substring(aChunk, *aStart, 4); - *aStart += 4; - - uint32 addChunk; - memcpy(&addChunk, addChunkStr.BeginReading(), 4); - addChunk = PR_ntohl(addChunk); - - mTableUpdate->NewSubComplete(addChunk, hash, mChunkState.num); - } - - return NS_OK; -} - -bool -ProtocolParser::NextLine(nsACString& line) -{ - int32 newline = mPending.FindChar('\n'); - if (newline == kNotFound) { - return false; - } - line.Assign(Substring(mPending, 0, newline)); - mPending = Substring(mPending, newline + 1); - return true; -} - -void -ProtocolParser::CleanupUpdates() -{ - for (uint32 i = 0; i < mTableUpdates.Length(); i++) { - delete mTableUpdates[i]; - } - mTableUpdates.Clear(); -} - -TableUpdate * -ProtocolParser::GetTableUpdate(const nsACString& aTable) -{ - for (uint32 i = 0; i < mTableUpdates.Length(); i++) { - if (aTable.Equals(mTableUpdates[i]->TableName())) { - return mTableUpdates[i]; - } - } - - // We free automatically on destruction, ownership of these - // updates can be transferred to DBServiceWorker, which passes - // them back to Classifier when doing the updates, and that - // will free them. - TableUpdate *update = new TableUpdate(aTable); - mTableUpdates.AppendElement(update); - return update; -} - -} -} diff --git a/toolkit/components/url-classifier/ProtocolParser.h b/toolkit/components/url-classifier/ProtocolParser.h deleted file mode 100644 index e263d4f43617..000000000000 --- a/toolkit/components/url-classifier/ProtocolParser.h +++ /dev/null @@ -1,151 +0,0 @@ -//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Dave Camp - * Gian-Carlo Pascutto - * - * 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 ProtocolParser_h__ -#define ProtocolParser_h__ - -#include "HashStore.h" -#include "nsICryptoHMAC.h" - -namespace mozilla { -namespace safebrowsing { - -/** - * Some helpers for parsing the safe - */ -class ProtocolParser { -public: - struct ForwardedUpdate { - nsCString table; - nsCString url; - nsCString mac; - }; - - ProtocolParser(PRUint32 aHashKey); - ~ProtocolParser(); - - nsresult Status() const { return mUpdateStatus; } - - nsresult Init(nsICryptoHash* aHasher); - - nsresult InitHMAC(const nsACString& aClientKey, - const nsACString& aServerMAC); - nsresult FinishHMAC(); - - void SetCurrentTable(const nsACString& aTable); - - nsresult Begin(); - nsresult AppendStream(const nsACString& aData); - - // Forget the table updates that were created by this pass. It - // becomes the caller's responsibility to free them. This is shitty. - TableUpdate *GetTableUpdate(const nsACString& aTable); - void ForgetTableUpdates() { mTableUpdates.Clear(); } - nsTArray &GetTableUpdates() { return mTableUpdates; } - - // Update information. - const nsTArray &Forwards() const { return mForwards; } - int32 UpdateWait() { return mUpdateWait; } - bool ResetRequested() { return mResetRequested; } - bool RekeyRequested() { return mRekeyRequested; } - -private: - nsresult ProcessControl(bool* aDone); - nsresult ProcessMAC(const nsCString& aLine); - nsresult ProcessExpirations(const nsCString& aLine); - nsresult ProcessChunkControl(const nsCString& aLine); - nsresult ProcessForward(const nsCString& aLine); - nsresult AddForward(const nsACString& aUrl, const nsACString& aMac); - nsresult ProcessChunk(bool* done); - nsresult ProcessPlaintextChunk(const nsACString& aChunk); - nsresult ProcessShaChunk(const nsACString& aChunk); - nsresult ProcessHostAdd(const Prefix& aDomain, PRUint8 aNumEntries, - const nsACString& aChunk, PRUint32* aStart); - nsresult ProcessHostSub(const Prefix& aDomain, PRUint8 aNumEntries, - const nsACString& aChunk, PRUint32* aStart); - nsresult ProcessHostAddComplete(PRUint8 aNumEntries, const nsACString& aChunk, - PRUint32 *aStart); - nsresult ProcessHostSubComplete(PRUint8 numEntries, const nsACString& aChunk, - PRUint32* start); - bool NextLine(nsACString& aLine); - - void CleanupUpdates(); - - enum ParserState { - PROTOCOL_STATE_CONTROL, - PROTOCOL_STATE_CHUNK - }; - ParserState mState; - - enum ChunkType { - CHUNK_ADD, - CHUNK_SUB - }; - - struct ChunkState { - ChunkType type; - uint32 num; - uint32 hashSize; - uint32 length; - void Clear() { num = 0; hashSize = 0; length = 0; } - }; - ChunkState mChunkState; - - PRUint32 mHashKey; - nsCOMPtr mCryptoHash; - - nsresult mUpdateStatus; - nsCString mPending; - - nsCOMPtr mHMAC; - nsCString mServerMAC; - - uint32 mUpdateWait; - bool mResetRequested; - bool mRekeyRequested; - - nsTArray mForwards; - nsTArray mTableUpdates; - TableUpdate *mTableUpdate; -}; - -} -} - -#endif diff --git a/toolkit/components/url-classifier/content/listmanager.js b/toolkit/components/url-classifier/content/listmanager.js index 144a917bd5d9..bed67db4a840 100644 --- a/toolkit/components/url-classifier/content/listmanager.js +++ b/toolkit/components/url-classifier/content/listmanager.js @@ -304,7 +304,7 @@ PROT_ListManager.prototype.maybeToggleUpdateChecking = function() { */ PROT_ListManager.prototype.startUpdateChecker = function() { this.stopUpdateChecker(); - + // Schedule the first check for between 15 and 45 minutes. var repeatingUpdateDelay = this.updateInterval / 2; repeatingUpdateDelay += Math.floor(Math.random() * this.updateInterval); diff --git a/toolkit/components/url-classifier/nsCheckSummedOutputStream.cpp b/toolkit/components/url-classifier/nsCheckSummedOutputStream.cpp deleted file mode 100644 index 90661f4d54d0..000000000000 --- a/toolkit/components/url-classifier/nsCheckSummedOutputStream.cpp +++ /dev/null @@ -1,92 +0,0 @@ -//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Gian-Carlo Pascutto - * - * 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 "nsILocalFile.h" -#include "nsCRT.h" -#include "nsIFile.h" -#include "nsISupportsImpl.h" -#include "nsCheckSummedOutputStream.h" - -//////////////////////////////////////////////////////////////////////////////// -// nsCheckSummedOutputStream - -NS_IMPL_ISUPPORTS_INHERITED3(nsCheckSummedOutputStream, - nsSafeFileOutputStream, - nsISafeOutputStream, - nsIOutputStream, - nsIFileOutputStream) - -NS_IMETHODIMP -nsCheckSummedOutputStream::Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, - PRInt32 behaviorFlags) -{ - nsresult rv; - mHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - rv = mHash->Init(nsICryptoHash::MD5); - NS_ENSURE_SUCCESS(rv, rv); - - return nsSafeFileOutputStream::Init(file, ioFlags, perm, behaviorFlags); -} - -NS_IMETHODIMP -nsCheckSummedOutputStream::Finish() -{ - nsresult rv = mHash->Finish(false, mCheckSum); - NS_ENSURE_SUCCESS(rv, rv); - - PRUint32 written; - rv = nsSafeFileOutputStream::Write(reinterpret_cast(mCheckSum.BeginReading()), - mCheckSum.Length(), &written); - NS_ASSERTION(written == mCheckSum.Length(), "Error writing stream checksum"); - NS_ENSURE_SUCCESS(rv, rv); - - return nsSafeFileOutputStream::Finish(); -} - -NS_IMETHODIMP -nsCheckSummedOutputStream::Write(const char *buf, PRUint32 count, PRUint32 *result) -{ - nsresult rv = mHash->Update(reinterpret_cast(buf), count); - NS_ENSURE_SUCCESS(rv, rv); - - return nsSafeFileOutputStream::Write(buf, count, result); -} - -//////////////////////////////////////////////////////////////////////////////// diff --git a/toolkit/components/url-classifier/nsCheckSummedOutputStream.h b/toolkit/components/url-classifier/nsCheckSummedOutputStream.h deleted file mode 100644 index d0f50f9b1ae7..000000000000 --- a/toolkit/components/url-classifier/nsCheckSummedOutputStream.h +++ /dev/null @@ -1,86 +0,0 @@ -//* -*- Mode: C++; tab-width: 8; 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 Url Classifier code. - * - * The Initial Developer of the Original Code is - * the Mozilla Foundation. - * Portions created by the Initial Developer are Copyright (C) 2011 - * the Initial Developer. All Rights Reserved. - * - * Contributor(s): - * Gian-Carlo Pascutto - * - * 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 nsCheckSummedOutputStream_h__ -#define nsCheckSummedOutputStream_h__ - -#include "nsILocalFile.h" -#include "nsIFile.h" -#include "nsIOutputStream.h" -#include "nsICryptoHash.h" -#include "nsNetCID.h" -#include "nsString.h" -#include "../../../netwerk/base/src/nsFileStreams.h" -#include "nsToolkitCompsCID.h" - -class nsCheckSummedOutputStream : public nsSafeFileOutputStream -{ -public: - NS_DECL_ISUPPORTS_INHERITED - - // Size of MD5 hash in bytes - static const PRUint32 CHECKSUM_SIZE = 16; - - nsCheckSummedOutputStream() {} - virtual ~nsCheckSummedOutputStream() { nsSafeFileOutputStream::Close(); } - - NS_IMETHOD Finish(); - NS_IMETHOD Write(const char *buf, PRUint32 count, PRUint32 *result); - NS_IMETHOD Init(nsIFile* file, PRInt32 ioFlags, PRInt32 perm, PRInt32 behaviorFlags); - -protected: - nsCOMPtr mHash; - nsCAutoString mCheckSum; -}; - -// returns a file output stream which can be QI'ed to nsIFileOutputStream. -inline nsresult -NS_NewCheckSummedOutputStream(nsIOutputStream **result, - nsIFile *file, - PRInt32 ioFlags = -1, - PRInt32 perm = -1, - PRInt32 behaviorFlags = 0) -{ - nsCOMPtr out = new nsCheckSummedOutputStream(); - nsresult rv = out->Init(file, ioFlags, perm, behaviorFlags); - if (NS_SUCCEEDED(rv)) - NS_ADDREF(*result = out); // cannot use nsCOMPtr::swap - return rv; -} - -#endif diff --git a/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl b/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl index be3a673619dc..e15323f11c56 100644 --- a/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl +++ b/toolkit/components/url-classifier/nsIUrlClassifierDBService.idl @@ -40,12 +40,10 @@ %{C++ #include "nsTArray.h" -#include "Entries.h" -#include "LookupCache.h" +class nsUrlClassifierLookupResult; %} -[ptr] native ResultArray(nsTArray); -[ptr] native CacheCompletionArray(nsTArray); -[ptr] native PrefixArray(mozilla::safebrowsing::PrefixArray); +[ptr] native ResultArray(nsTArray); + interface nsIUrlClassifierHashCompleter; // Interface for JS function callbacks @@ -233,14 +231,14 @@ interface nsIUrlClassifierDBService : nsISupports * Interface for the actual worker thread. Implementations of this need not * be thread aware and just work on the database. */ -[scriptable, uuid(0445be75-b114-43ea-89dc-aa16af26e77e)] +[scriptable, uuid(2af84c09-269e-4fc2-b28f-af56717db118)] interface nsIUrlClassifierDBServiceWorker : nsIUrlClassifierDBService { // Provide a way to forcibly close the db connection. void closeDb(); - [noscript]void cacheCompletions(in CacheCompletionArray completions); - [noscript]void cacheMisses(in PrefixArray misses); + // Cache the results of a hash completion. + [noscript]void cacheCompletions(in ResultArray entries); }; /** @@ -249,7 +247,7 @@ interface nsIUrlClassifierDBServiceWorker : nsIUrlClassifierDBService * lookup to provide a set of possible results, which the main thread * may need to expand using an nsIUrlClassifierCompleter. */ -[uuid(b903dc8f-dff1-42fe-894b-36e7a59bb801)] +[uuid(f1dc83c6-ad43-4f0f-a809-fd43de7de8a4)] interface nsIUrlClassifierLookupCallback : nsISupports { /** diff --git a/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl b/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl index 1df5a7878412..0d6624cbfff7 100644 --- a/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl +++ b/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl @@ -39,26 +39,28 @@ #include "nsISupports.idl" #include "nsIFile.idl" +interface nsIArray; + // Note that the PrefixSet name is historical and we do properly support // duplicated values, so it's really a Prefix Trie. // All methods are thread-safe. -[scriptable, uuid(b21b0fa1-20d2-422a-b2cc-b289c9325811)] + +[scriptable, uuid(519c8519-0f30-426b-bb7b-c400ba0318e2)] interface nsIUrlClassifierPrefixSet : nsISupports { - // Initialize the PrefixSet. Give it a name for memory reporting. - void init(in ACString aName); // Fills the PrefixSet with the given array of prefixes. // Can send an empty Array to clear the tree. A truly "empty tree" // cannot be represented, so put a sentinel value if that is required // Requires array to be sorted. void setPrefixes([const, array, size_is(aLength)] in unsigned long aPrefixes, in unsigned long aLength); - void getPrefixes(out unsigned long aCount, - [array, size_is(aCount), retval] out unsigned long aPrefixes); // Do a lookup in the PrefixSet, return whether the value is present. // If aReady is set, we will block until there are any entries. // If not set, we will return in aReady whether we were ready or not. - boolean probe(in unsigned long aPrefix, inout boolean aReady); + boolean probe(in unsigned long aPrefix, in unsigned long aKey, + inout boolean aReady); + // Return the key that is used to randomize the collisions in the prefixes. + PRUint32 getKey(); boolean isEmpty(); void loadFromFile(in nsIFile aFile); void storeToFile(in nsIFile aFile); diff --git a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp index f2870f8e405e..c135f358954c 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp @@ -42,8 +42,14 @@ #include "nsAutoPtr.h" #include "nsCOMPtr.h" +#include "mozIStorageService.h" +#include "mozIStorageConnection.h" +#include "mozIStorageStatement.h" +#include "mozStorageHelper.h" +#include "mozStorageCID.h" #include "nsAppDirectoryServiceDefs.h" #include "nsCRT.h" +#include "nsDataHashtable.h" #include "nsICryptoHash.h" #include "nsICryptoHMAC.h" #include "nsIDirectoryService.h" @@ -67,23 +73,48 @@ #include "nsNetCID.h" #include "nsThreadUtils.h" #include "nsXPCOMStrings.h" -#include "nsProxyRelease.h" #include "mozilla/Mutex.h" -#include "mozilla/TimeStamp.h" #include "mozilla/Telemetry.h" #include "prlog.h" #include "prprf.h" #include "prnetdb.h" -#include "Entries.h" -#include "Classifier.h" -#include "ProtocolParser.h" +#include "zlib.h" + +// Needed to interpert mozIStorageConnection::GetLastError +#include using namespace mozilla; -using namespace mozilla::safebrowsing; + +/** + * The DBServices stores a set of Fragments. A fragment is one URL + * fragment containing two or more domain components and some number + * of path components. + * + * Fragment examples: + * example.com/ + * www.example.com/foo/bar + * www.mail.example.com/mail + * + * Fragments are described in "Simplified Regular Expression Lookup" + * section of the protocol document at + * http://code.google.com/p/google-safe-browsing/wiki/Protocolv2Spec + * + * A fragment is associated with a domain. The domain for a given + * fragment is the three-host-component domain of the fragment (two + * host components for URLs with only two components) with a trailing + * slash. So for the fragments listed above, the domains are + * example.com/, www.example.com/ and mail.example.com/. + * + * Fragments and domains are hashed in the database. The hash is described + * in the protocol document, but it's basically a truncated SHA256 hash. + * + * A (table, chunk id, domain key, fragment) tuple is referred to as + * an Entry. + */ // NSPR_LOG_MODULES=UrlClassifierDbService:5 #if defined(PR_LOGGING) -PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull; +static const PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull; #define LOG(args) PR_LOG(gUrlClassifierDbServiceLog, PR_LOG_DEBUG, args) #define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierDbServiceLog, 4) #else @@ -91,6 +122,31 @@ PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull; #define LOG_ENABLED() (false) #endif +// Schema versioning: note that we don't bother to migrate between different +// versions of the schema, we just start fetching the data freshly with each +// migration. + +// The database filename is updated when there is an incompatible +// schema change and we expect both implementations to continue +// accessing the same database (such as between stable versions of the +// platform). +#define DATABASE_FILENAME "urlclassifier3.sqlite" + +// The implementation version is updated during development when we +// want to change schema, or to recover from updating bugs. When an +// implementation version change is detected, the database is scrapped +// and we start over. +#define IMPLEMENTATION_VERSION 7 + +// Name of the persistent PrefixSet storage +#define PREFIXSET_FILENAME "urlclassifier.pset" + +#define MAX_HOST_COMPONENTS 5 +#define MAX_PATH_COMPONENTS 4 + +// Updates will fail if fed chunks larger than this +#define MAX_CHUNK_SIZE (1024 * 1024) + // Prefs for implementing nsIURIClassifier to block page loads #define CHECK_MALWARE_PREF "browser.safebrowsing.malware.enabled" #define CHECK_MALWARE_DEFAULT false @@ -106,6 +162,23 @@ PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull; #define CONFIRM_AGE_PREF "urlclassifier.confirm-age" #define CONFIRM_AGE_DEFAULT_SEC (45 * 60) +#define UPDATE_CACHE_SIZE_PREF "urlclassifier.updatecachemax" +#define UPDATE_CACHE_SIZE_DEFAULT -1 + +#define LOOKUP_CACHE_SIZE_PREF "urlclassifier.lookupcachemax" +#define LOOKUP_CACHE_SIZE_DEFAULT -1 + +// Amount of time to spend updating before committing and delaying, in +// seconds. This is checked after each update stream, so the actual +// time spent can be higher than this, depending on update stream size. +#define UPDATE_WORKING_TIME "urlclassifier.workingtime" +#define UPDATE_WORKING_TIME_DEFAULT 5 + +// The amount of time to delay after hitting UPDATE_WORKING_TIME, in +// seconds. +#define UPDATE_DELAY_TIME "urlclassifier.updatetime" +#define UPDATE_DELAY_TIME_DEFAULT 60 + class nsUrlClassifierDBServiceWorker; // Singleton instance. @@ -119,6 +192,12 @@ static bool gShuttingDownThread = false; static PRInt32 gFreshnessGuarantee = CONFIRM_AGE_DEFAULT_SEC; +static PRInt32 gUpdateCacheSize = UPDATE_CACHE_SIZE_DEFAULT; +static PRInt32 gLookupCacheSize = LOOKUP_CACHE_SIZE_DEFAULT; + +static PRInt32 gWorkingTimeThreshold = UPDATE_WORKING_TIME_DEFAULT; +static PRInt32 gDelayTime = UPDATE_DELAY_TIME_DEFAULT; + static void SplitTables(const nsACString& str, nsTArray& tables) { @@ -137,6 +216,868 @@ SplitTables(const nsACString& str, nsTArray& tables) } } +// ------------------------------------------------------------------------- +// Hash class implementation + +// A convenience wrapper around the potentially-truncated hash for a +// domain or fragment. + +template +struct nsUrlClassifierHash +{ + static const PRUint32 sHashSize = S; + typedef nsUrlClassifierHash self_type; + PRUint8 buf[S]; + + nsresult FromPlaintext(const nsACString& plainText, nsICryptoHash *hash) { + // From the protocol doc: + // Each entry in the chunk is composed of the 128 most significant bits + // of the SHA 256 hash of a suffix/prefix expression. + + nsresult rv = hash->Init(nsICryptoHash::SHA256); + NS_ENSURE_SUCCESS(rv, rv); + + rv = hash->Update + (reinterpret_cast(plainText.BeginReading()), + plainText.Length()); + NS_ENSURE_SUCCESS(rv, rv); + + nsCAutoString hashed; + rv = hash->Finish(false, hashed); + NS_ENSURE_SUCCESS(rv, rv); + + NS_ASSERTION(hashed.Length() >= sHashSize, + "not enough characters in the hash"); + + memcpy(buf, hashed.BeginReading(), sHashSize); + + return NS_OK; + } + + void Assign(const nsACString& str) { + NS_ASSERTION(str.Length() >= sHashSize, + "string must be at least sHashSize characters long"); + memcpy(buf, str.BeginReading(), sHashSize); + } + + void Clear() { + memset(buf, 0, sizeof(buf)); + } + + const bool operator==(const self_type& hash) const { + return (memcmp(buf, hash.buf, sizeof(buf)) == 0); + } + const bool operator!=(const self_type& hash) const { + return !(*this == hash); + } + const bool operator<(const self_type& hash) const { + return memcmp(buf, hash.buf, sizeof(self_type)) < 0; + } + const bool StartsWith(const nsUrlClassifierHash& hash) const { + NS_ASSERTION(sHashSize >= PARTIAL_LENGTH, "nsUrlClassifierHash must be at least PARTIAL_LENGTH bytes long"); + return memcmp(buf, hash.buf, PARTIAL_LENGTH) == 0; + } + PRUint32 ToUint32() const { + return *(reinterpret_cast(buf)); + } +}; + +typedef nsUrlClassifierHash nsUrlClassifierDomainHash; +typedef nsUrlClassifierHash nsUrlClassifierPartialHash; +typedef nsUrlClassifierHash nsUrlClassifierCompleteHash; + + +// ------------------------------------------------------------------------- +// Entry class implementation + +// This class represents one entry in the classifier database. It consists +// of a table id, a chunk id, a domain hash, and a partial or complete hash. +class nsUrlClassifierEntry +{ +public: + nsUrlClassifierEntry() + : mId(-1) + , mHavePartial(false) + , mHaveComplete(false) + , mTableId(0) + , mChunkId(0) + , mAddChunkId(0) + {} + ~nsUrlClassifierEntry() {} + + // Check that this entry could potentially match the complete hash. + bool Match(const nsUrlClassifierCompleteHash &hash); + + // Check that the sub entry should apply to this entry. + bool SubMatch(const nsUrlClassifierEntry& sub); + + // Clear out the entry structure + void Clear(); + + // Set the partial hash for this domain. + void SetHash(const nsUrlClassifierPartialHash &partialHash) { + mPartialHash = partialHash; + mHavePartial = true; + } + + // Set the complete hash for this domain. + void SetHash(const nsUrlClassifierCompleteHash &completeHash) { + mCompleteHash = completeHash; + mHaveComplete = true; + } + + bool operator== (const nsUrlClassifierEntry& entry) const { + return ! (mTableId != entry.mTableId || + mChunkId != entry.mChunkId || + mHavePartial != entry.mHavePartial || + (mHavePartial && mPartialHash != entry.mPartialHash) || + mHaveComplete != entry.mHaveComplete || + (mHaveComplete && mCompleteHash != entry.mCompleteHash)); + } + + bool operator< (const nsUrlClassifierEntry& entry) const { + return (mTableId < entry.mTableId || + mChunkId < entry.mChunkId || + (mHavePartial && !entry.mHavePartial) || + (mHavePartial && mPartialHash < entry.mPartialHash) || + (mHaveComplete && !entry.mHaveComplete) || + (mHaveComplete && mCompleteHash < entry.mCompleteHash)); + } + + PRInt64 mId; + + nsUrlClassifierDomainHash mKey; + + bool mHavePartial; + nsUrlClassifierPartialHash mPartialHash; + + bool mHaveComplete; + nsUrlClassifierCompleteHash mCompleteHash; + + PRUint32 mTableId; + PRUint32 mChunkId; + PRUint32 mAddChunkId; +}; + +bool +nsUrlClassifierEntry::Match(const nsUrlClassifierCompleteHash &hash) +{ + if (mHaveComplete) + return mCompleteHash == hash; + + if (mHavePartial) + return hash.StartsWith(mPartialHash); + + return false; +} + +bool +nsUrlClassifierEntry::SubMatch(const nsUrlClassifierEntry &subEntry) +{ + if ((mTableId != subEntry.mTableId) || (mChunkId != subEntry.mAddChunkId)) + return false; + + if (subEntry.mHaveComplete) + return mHaveComplete && mCompleteHash == subEntry.mCompleteHash; + + if (subEntry.mHavePartial) + return mHavePartial && mPartialHash == subEntry.mPartialHash; + + return false; +} + +void +nsUrlClassifierEntry::Clear() +{ + mId = -1; + mHavePartial = false; + mHaveComplete = false; +} + +// ------------------------------------------------------------------------- +// Lookup result class implementation + +// This helper class wraps a nsUrlClassifierEntry found during a lookup. +class nsUrlClassifierLookupResult +{ +public: + nsUrlClassifierLookupResult() : mConfirmed(false), mNoise(false) { + mLookupFragment.Clear(); + } + ~nsUrlClassifierLookupResult() {} + + bool operator==(const nsUrlClassifierLookupResult &result) const { + // Don't need to compare table name, it's contained by id in the entry. + return (mLookupFragment == result.mLookupFragment && + mConfirmed == result.mConfirmed && + mEntry == result.mEntry); + } + + bool operator<(const nsUrlClassifierLookupResult &result) const { + // Don't need to compare table name, it's contained by id in the entry. + return (mLookupFragment < result.mLookupFragment || + mConfirmed < result.mConfirmed || + mEntry < result.mEntry); + } + + // The hash that matched this entry. + nsUrlClassifierCompleteHash mLookupFragment; + + // The entry that was found during the lookup. + nsUrlClassifierEntry mEntry; + + // TRUE if the lookup matched a complete hash (not just a partial + // one). + bool mConfirmed; + + // TRUE if this lookup is gethash noise. Does not represent an actual + // result. + bool mNoise; + + // The table name associated with mEntry.mTableId. + nsCString mTableName; +}; + +// ------------------------------------------------------------------------- +// Store class implementation + +// This class mediates access to the classifier and chunk entry tables. +class nsUrlClassifierStore +{ +public: + nsUrlClassifierStore() {} + virtual ~nsUrlClassifierStore() {} + + // Initialize the statements for the store. + nsresult Init(nsUrlClassifierDBServiceWorker *worker, + mozIStorageConnection *connection, + const nsACString& entriesTableName); + // Shut down the store. + void Close(); + + // Read an entry from a database statement + virtual bool ReadStatement(mozIStorageStatement* statement, + nsUrlClassifierEntry& entry); + + // Prepare a statement to write this entry to the database + virtual nsresult BindStatement(const nsUrlClassifierEntry& entry, + mozIStorageStatement* statement); + + // Read the entry with a given ID from the database + nsresult ReadEntry(PRInt64 id, nsUrlClassifierEntry& entry, bool *exists); + + // Remove an entry from the database + nsresult DeleteEntry(nsUrlClassifierEntry& entry); + + // Write an entry to the database + nsresult WriteEntry(nsUrlClassifierEntry& entry); + + // Update an entry in the database. The entry must already exist in the + // database or this method will fail. + nsresult UpdateEntry(nsUrlClassifierEntry& entry); + + // Remove all entries for a given table/chunk pair from the database. + nsresult Expire(PRUint32 tableId, + PRUint32 chunkNum); + + // Read a certain number of rows adjacent to the requested rowid that + // don't have complete hash data. + nsresult ReadNoiseEntries(PRInt64 rowID, + PRUint32 numRequested, + bool before, + nsTArray &entries); + + // Ask the db for a random number. This is temporary, and should be + // replaced with nsIRandomGenerator when 419739 is fixed. + nsresult RandomNumber(PRInt64 *randomNum); + // Return an array with all Prefixes known + nsresult ReadPrefixes(FallibleTArray& array, PRUint32 aKey); + + +protected: + nsresult ReadEntries(mozIStorageStatement *statement, + nsTArray& entries); + nsUrlClassifierDBServiceWorker *mWorker; + nsCOMPtr mConnection; + + nsCOMPtr mLookupWithIDStatement; + + nsCOMPtr mInsertStatement; + nsCOMPtr mUpdateStatement; + nsCOMPtr mDeleteStatement; + nsCOMPtr mExpireStatement; + + nsCOMPtr mPartialEntriesStatement; + nsCOMPtr mPartialEntriesAfterStatement; + nsCOMPtr mLastPartialEntriesStatement; + nsCOMPtr mPartialEntriesBeforeStatement; + + nsCOMPtr mRandomStatement; + nsCOMPtr mAllPrefixGetStatement; + nsCOMPtr mAllPrefixCountStatement; +}; + +nsresult +nsUrlClassifierStore::Init(nsUrlClassifierDBServiceWorker *worker, + mozIStorageConnection *connection, + const nsACString& entriesName) +{ + mWorker = worker; + mConnection = connection; + + nsresult rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesName + + NS_LITERAL_CSTRING(" WHERE id=?1"), + getter_AddRefs(mLookupWithIDStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("DELETE FROM ") + entriesName + + NS_LITERAL_CSTRING(" WHERE id=?1"), + getter_AddRefs(mDeleteStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("DELETE FROM ") + entriesName + + NS_LITERAL_CSTRING(" WHERE table_id=?1 AND chunk_id=?2"), + getter_AddRefs(mExpireStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesName + + NS_LITERAL_CSTRING(" WHERE complete_data ISNULL" + " LIMIT ?1"), + getter_AddRefs(mPartialEntriesStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesName + + NS_LITERAL_CSTRING(" WHERE id > ?1 AND complete_data ISNULL" + " LIMIT ?2"), + getter_AddRefs(mPartialEntriesAfterStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesName + + NS_LITERAL_CSTRING(" WHERE complete_data ISNULL" + " ORDER BY id DESC LIMIT ?1"), + getter_AddRefs(mLastPartialEntriesStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesName + + NS_LITERAL_CSTRING(" WHERE id < ?1 AND complete_data ISNULL" + " ORDER BY id DESC LIMIT ?2"), + getter_AddRefs(mPartialEntriesBeforeStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("SELECT abs(random())"), + getter_AddRefs(mRandomStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING("SELECT domain, partial_data, complete_data FROM ") + + entriesName, + getter_AddRefs(mAllPrefixGetStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement(NS_LITERAL_CSTRING("SELECT COUNT(1) FROM ") + + entriesName, + getter_AddRefs(mAllPrefixCountStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +void +nsUrlClassifierStore::Close() +{ + mLookupWithIDStatement = nsnull; + + mInsertStatement = nsnull; + mUpdateStatement = nsnull; + mDeleteStatement = nsnull; + mExpireStatement = nsnull; + + mPartialEntriesStatement = nsnull; + mPartialEntriesAfterStatement = nsnull; + mPartialEntriesBeforeStatement = nsnull; + mLastPartialEntriesStatement = nsnull; + mRandomStatement = nsnull; + + mAllPrefixGetStatement = nsnull; + mAllPrefixCountStatement = nsnull; + + mConnection = nsnull; +} + + +bool +nsUrlClassifierStore::ReadStatement(mozIStorageStatement* statement, + nsUrlClassifierEntry& entry) +{ + entry.mId = statement->AsInt64(0); + + PRUint32 size; + const PRUint8* blob = statement->AsSharedBlob(1, &size); + if (!blob || (size != DOMAIN_LENGTH)) + return false; + memcpy(entry.mKey.buf, blob, DOMAIN_LENGTH); + + blob = statement->AsSharedBlob(2, &size); + if (!blob || size == 0) { + entry.mHavePartial = false; + } else { + if (size != PARTIAL_LENGTH) + return false; + entry.mHavePartial = true; + memcpy(entry.mPartialHash.buf, blob, PARTIAL_LENGTH); + } + + blob = statement->AsSharedBlob(3, &size); + if (!blob || size == 0) { + entry.mHaveComplete = false; + } else { + if (size != COMPLETE_LENGTH) + return false; + entry.mHaveComplete = true; + memcpy(entry.mCompleteHash.buf, blob, COMPLETE_LENGTH); + } + + // If we only have a partial entry, and that partial entry matches the + // domain, we don't save the extra copy to the database. + if (!(entry.mHavePartial || entry.mHaveComplete)) { + entry.SetHash(entry.mKey); + } + + entry.mChunkId = statement->AsInt32(4); + entry.mTableId = statement->AsInt32(5); + + return true; +} + +nsresult +nsUrlClassifierStore::BindStatement(const nsUrlClassifierEntry &entry, + mozIStorageStatement* statement) +{ + nsresult rv; + + if (entry.mId == -1) + rv = statement->BindNullByIndex(0); + else + rv = statement->BindInt64ByIndex(0, entry.mId); + NS_ENSURE_SUCCESS(rv, rv); + + rv = statement->BindBlobByIndex(1, entry.mKey.buf, DOMAIN_LENGTH); + NS_ENSURE_SUCCESS(rv, rv); + + if (entry.mHavePartial) { + // If we only have a partial entry and that entry matches the domain, + // we'll save some space by only storing the domain hash. + if (!entry.mHaveComplete && entry.mKey == entry.mPartialHash) { + rv = statement->BindNullByIndex(2); + } else { + rv = statement->BindBlobByIndex(2, entry.mPartialHash.buf, + PARTIAL_LENGTH); + } + } else { + rv = statement->BindNullByIndex(2); + } + NS_ENSURE_SUCCESS(rv, rv); + + if (entry.mHaveComplete) { + rv = statement->BindBlobByIndex(3, entry.mCompleteHash.buf, COMPLETE_LENGTH); + } else { + rv = statement->BindNullByIndex(3); + } + NS_ENSURE_SUCCESS(rv, rv); + + rv = statement->BindInt32ByIndex(4, entry.mChunkId); + NS_ENSURE_SUCCESS(rv, rv); + + rv = statement->BindInt32ByIndex(5, entry.mTableId); + NS_ENSURE_SUCCESS(rv, rv); + + return true; +} + +nsresult +nsUrlClassifierStore::ReadEntries(mozIStorageStatement *statement, + nsTArray& entries) +{ + bool exists; + nsresult rv = statement->ExecuteStep(&exists); + NS_ENSURE_SUCCESS(rv, rv); + + while (exists) { + nsUrlClassifierEntry *entry = entries.AppendElement(); + if (!entry) { + return NS_ERROR_OUT_OF_MEMORY; + } + + if (!ReadStatement(statement, *entry)) + return NS_ERROR_FAILURE; + + statement->ExecuteStep(&exists); + } + + return NS_OK; +} + +nsresult +nsUrlClassifierStore::ReadEntry(PRInt64 id, + nsUrlClassifierEntry& entry, + bool *exists) +{ + entry.Clear(); + + mozStorageStatementScoper scoper(mLookupWithIDStatement); + + nsresult rv = mLookupWithIDStatement->BindInt64ByIndex(0, id); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mLookupWithIDStatement->ExecuteStep(exists); + NS_ENSURE_SUCCESS(rv, rv); + + if (*exists) { + if (ReadStatement(mLookupWithIDStatement, entry)) + return NS_ERROR_FAILURE; + } + + return NS_OK; +} + +nsresult +nsUrlClassifierStore::ReadNoiseEntries(PRInt64 rowID, + PRUint32 numRequested, + bool before, + nsTArray &entries) +{ + if (numRequested == 0) { + return NS_OK; + } + + mozIStorageStatement *statement = + before ? mPartialEntriesBeforeStatement : mPartialEntriesAfterStatement; + mozStorageStatementScoper scoper(statement); + + nsresult rv = statement->BindInt64ByIndex(0, rowID); + NS_ENSURE_SUCCESS(rv, rv); + + statement->BindInt32ByIndex(1, numRequested); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 length = entries.Length(); + rv = ReadEntries(statement, entries); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 numRead = entries.Length() - length; + + if (numRead >= numRequested) + return NS_OK; + + // If we didn't get enough entries, we need the search to wrap around from + // beginning to end (or vice-versa) + + mozIStorageStatement *wraparoundStatement = + before ? mPartialEntriesStatement : mLastPartialEntriesStatement; + mozStorageStatementScoper wraparoundScoper(wraparoundStatement); + + rv = wraparoundStatement->BindInt32ByIndex(0, numRequested - numRead); + NS_ENSURE_SUCCESS(rv, rv); + + return ReadEntries(wraparoundStatement, entries); +} + +nsresult +nsUrlClassifierStore::RandomNumber(PRInt64 *randomNum) +{ + mozStorageStatementScoper randScoper(mRandomStatement); + bool exists; + nsresult rv = mRandomStatement->ExecuteStep(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (!exists) + return NS_ERROR_NOT_AVAILABLE; + + *randomNum = mRandomStatement->AsInt64(0); + + return NS_OK; +} + +// ------------------------------------------------------------------------- +// nsUrlClassifierAddStore class implementation + +// This class accesses the moz_classifier table. +class nsUrlClassifierAddStore: public nsUrlClassifierStore +{ +public: + nsUrlClassifierAddStore() {}; + virtual ~nsUrlClassifierAddStore() {}; + + nsresult Init(nsUrlClassifierDBServiceWorker *worker, + mozIStorageConnection *connection, + const nsACString& entriesTableName); + + void Close(); + + // Read the entries for a given key/table/chunk from the database + nsresult ReadAddEntries(const nsUrlClassifierDomainHash& key, + PRUint32 tableId, + PRUint32 chunkId, + nsTArray& entry); + + // Read the entries for a given host key from the database. + nsresult ReadAddEntries(const nsUrlClassifierDomainHash& key, + nsTArray& entry); + +protected: + nsCOMPtr mLookupStatement; + nsCOMPtr mLookupWithChunkStatement; +}; + +nsresult +nsUrlClassifierAddStore::Init(nsUrlClassifierDBServiceWorker *worker, + mozIStorageConnection *connection, + const nsACString &entriesTableName) +{ + nsresult rv = nsUrlClassifierStore::Init(worker, connection, + entriesTableName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("INSERT OR REPLACE INTO ") + entriesTableName + + NS_LITERAL_CSTRING(" VALUES (?1, ?2, ?3, ?4, ?5, ?6)"), + getter_AddRefs(mInsertStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("UPDATE ") + entriesTableName + + NS_LITERAL_CSTRING(" SET domain=?2, partial_data=?3, " + " complete_data=?4, chunk_id=?5, table_id=?6" + " WHERE id=?1"), + getter_AddRefs(mUpdateStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesTableName + + NS_LITERAL_CSTRING(" WHERE domain=?1"), + getter_AddRefs(mLookupStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesTableName + + NS_LITERAL_CSTRING(" WHERE domain=?1 AND table_id=?2 AND chunk_id=?3"), + getter_AddRefs(mLookupWithChunkStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +void +nsUrlClassifierAddStore::Close() +{ + nsUrlClassifierStore::Close(); + + mLookupStatement = nsnull; + mLookupWithChunkStatement = nsnull; +} + +nsresult +nsUrlClassifierAddStore::ReadAddEntries(const nsUrlClassifierDomainHash& hash, + PRUint32 tableId, + PRUint32 chunkId, + nsTArray& entries) +{ + mozStorageStatementScoper scoper(mLookupWithChunkStatement); + + nsresult rv = mLookupWithChunkStatement->BindBlobByIndex + (0, hash.buf, DOMAIN_LENGTH); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mLookupWithChunkStatement->BindInt32ByIndex(1, tableId); + NS_ENSURE_SUCCESS(rv, rv); + rv = mLookupWithChunkStatement->BindInt32ByIndex(2, chunkId); + NS_ENSURE_SUCCESS(rv, rv); + + return ReadEntries(mLookupWithChunkStatement, entries); +} + +nsresult +nsUrlClassifierAddStore::ReadAddEntries(const nsUrlClassifierDomainHash& hash, + nsTArray& entries) +{ + mozStorageStatementScoper scoper(mLookupStatement); + + nsresult rv = mLookupStatement->BindBlobByIndex + (0, hash.buf, DOMAIN_LENGTH); + NS_ENSURE_SUCCESS(rv, rv); + + return ReadEntries(mLookupStatement, entries); +} + +// ------------------------------------------------------------------------- +// nsUrlClassifierSubStore class implementation + +// This class accesses the moz_subs table. +class nsUrlClassifierSubStore : public nsUrlClassifierStore +{ +public: + nsUrlClassifierSubStore() {}; + virtual ~nsUrlClassifierSubStore() {}; + + nsresult Init(nsUrlClassifierDBServiceWorker *worker, + mozIStorageConnection *connection, + const nsACString& entriesTableName); + + void Close(); + + // Read an entry from a database statement + virtual bool ReadStatement(mozIStorageStatement* statement, + nsUrlClassifierEntry& entry); + + // Prepare a statement to write this entry to the database + virtual nsresult BindStatement(const nsUrlClassifierEntry& entry, + mozIStorageStatement* statement); + + // Read sub entries for a given add chunk + nsresult ReadSubEntries(PRUint32 tableId, PRUint32 chunkId, + nsTArray &subEntry); + + // Expire sub entries for a given add chunk + nsresult ExpireAddChunk(PRUint32 tableId, PRUint32 chunkId); + +protected: + nsCOMPtr mLookupWithAddChunkStatement; + nsCOMPtr mExpireAddChunkStatement; +}; + +nsresult +nsUrlClassifierSubStore::Init(nsUrlClassifierDBServiceWorker *worker, + mozIStorageConnection *connection, + const nsACString &entriesTableName) +{ + nsresult rv = nsUrlClassifierStore::Init(worker, connection, + entriesTableName); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("INSERT OR REPLACE INTO ") + entriesTableName + + NS_LITERAL_CSTRING(" VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7)"), + getter_AddRefs(mInsertStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("UPDATE ") + entriesTableName + + NS_LITERAL_CSTRING(" SET domain=?2, partial_data=?3, complete_data=?4," + " chunk_id=?5, table_id=?6, add_chunk_id=?7" + " WHERE id=?1"), + getter_AddRefs(mUpdateStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("SELECT * FROM ") + entriesTableName + + NS_LITERAL_CSTRING(" WHERE table_id=?1 AND add_chunk_id=?2"), + getter_AddRefs(mLookupWithAddChunkStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mConnection->CreateStatement + (NS_LITERAL_CSTRING("DELETE FROM ") + entriesTableName + + NS_LITERAL_CSTRING(" WHERE table_id=?1 AND add_chunk_id=?2"), + getter_AddRefs(mExpireAddChunkStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +bool +nsUrlClassifierSubStore::ReadStatement(mozIStorageStatement* statement, + nsUrlClassifierEntry& entry) +{ + if (!nsUrlClassifierStore::ReadStatement(statement, entry)) + return false; + + entry.mAddChunkId = statement->AsInt32(6); + return true; +} + +nsresult +nsUrlClassifierSubStore::BindStatement(const nsUrlClassifierEntry& entry, + mozIStorageStatement* statement) +{ + nsresult rv = nsUrlClassifierStore::BindStatement(entry, statement); + NS_ENSURE_SUCCESS(rv, rv); + + return statement->BindInt32ByIndex(6, entry.mAddChunkId); +} + +nsresult +nsUrlClassifierSubStore::ReadSubEntries(PRUint32 tableId, PRUint32 addChunkId, + nsTArray& entries) +{ + mozStorageStatementScoper scoper(mLookupWithAddChunkStatement); + + nsresult rv = mLookupWithAddChunkStatement->BindInt32ByIndex(0, tableId); + NS_ENSURE_SUCCESS(rv, rv); + rv = mLookupWithAddChunkStatement->BindInt32ByIndex(1, addChunkId); + NS_ENSURE_SUCCESS(rv, rv); + + return ReadEntries(mLookupWithAddChunkStatement, entries); +} + +nsresult +nsUrlClassifierSubStore::ExpireAddChunk(PRUint32 tableId, PRUint32 addChunkId) +{ + mozStorageStatementScoper scoper(mExpireAddChunkStatement); + + nsresult rv = mExpireAddChunkStatement->BindInt32ByIndex(0, tableId); + NS_ENSURE_SUCCESS(rv, rv); + rv = mExpireAddChunkStatement->BindInt32ByIndex(1, addChunkId); + NS_ENSURE_SUCCESS(rv, rv); + + return mExpireAddChunkStatement->Execute(); +} + +void +nsUrlClassifierSubStore::Close() +{ + nsUrlClassifierStore::Close(); + mLookupWithAddChunkStatement = nsnull; + mExpireAddChunkStatement = nsnull; +} + +// Similar to GetKey(), but if the domain contains three or more components, +// two keys will be returned: +// hostname.com/foo/bar -> [hostname.com] +// mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com] +// www.mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com] +static nsresult GetHostKeys(const nsACString &spec, + nsTArray &hostKeys); + +// take a lookup string (www.hostname.com/path/to/resource.html) and +// expand it into the set of fragments that should be searched for in an +// entry +static nsresult GetLookupFragments(const nsCSubstring& spec, + nsTArray& fragments); + +// Check for a canonicalized IP address. +static bool IsCanonicalizedIP(const nsACString& host); + +// Get the database key for a given URI. This is the top three +// domain components if they exist, otherwise the top two. +// hostname.com/foo/bar -> hostname.com +// mail.hostname.com/foo/bar -> mail.hostname.com +// www.mail.hostname.com/foo/bar -> mail.hostname.com +static nsresult GetKey(const nsACString& spec, nsUrlClassifierDomainHash& hash, + nsICryptoHash * aCryptoHash); + +// We have both a prefix and a domain. Drop the domain, but +// hash the domain, the prefix and a random value together, +// ensuring any collisions happens at a different points for +// different users. +static nsresult KeyedHash(PRUint32 aPref, PRUint32 aDomain, + PRUint32 aKey, PRUint32 *aOut); + + // ------------------------------------------------------------------------- // Actual worker implemenatation class nsUrlClassifierDBServiceWorker : public nsIUrlClassifierDBServiceWorker @@ -149,7 +1090,8 @@ public: NS_DECL_NSIURLCLASSIFIERDBSERVICEWORKER // Initialize, called in the main thread - nsresult Init(PRInt32 gethashNoise, nsCOMPtr aCacheDir); + nsresult Init(PRInt32 gethashNoise, + nsRefPtr & prefSet); // Queue a lookup for the worker to perform, called in the main thread. nsresult QueueLookup(const nsACString& lookupKey, @@ -159,6 +1101,12 @@ public: // update operations to prevent lookups from blocking for too long. nsresult HandlePendingLookups(); + // Blocks the PrefixSet from being updated while the main thread is doing + // its lookups. LockPrefixSet will return whether the PrefixSet is in a + // usable state. If not, we should fall through to SQLite lookups. + bool LockPrefixSet(); + void UnlockPrefixSet(); + private: // No subclassing ~nsUrlClassifierDBServiceWorker(); @@ -166,66 +1114,231 @@ private: // Disallow copy constructor nsUrlClassifierDBServiceWorker(nsUrlClassifierDBServiceWorker&); + // Try to open the db, DATABASE_FILENAME. nsresult OpenDb(); + // Create table in the db if they don't exist. + nsresult MaybeCreateTables(mozIStorageConnection* connection); + + nsresult GetTableName(PRUint32 tableId, nsACString& table); + nsresult GetTableId(const nsACString& table, PRUint32* tableId); + + // Decompress a zlib'ed chunk (used for -exp tables) + nsresult InflateChunk(nsACString& chunk); + + // Expand shavar chunk into its individual entries + nsresult GetShaEntries(PRUint32 tableId, + PRUint32 chunkType, + PRUint32 chunkNum, + PRUint32 domainSize, + PRUint32 hashSize, + nsACString& chunk, + nsTArray& entries); + + // Expand a chunk into its individual entries + nsresult GetChunkEntries(const nsACString& table, + PRUint32 tableId, + PRUint32 chunkType, + PRUint32 chunkNum, + PRUint32 hashSize, + nsACString& chunk, + nsTArray& entries); + + // Parse one stringified range of chunks of the form "n" or "n-m" from a + // comma-separated list of chunks. Upon return, 'begin' will point to the + // next range of chunks in the list of chunks. + bool ParseChunkRange(nsACString::const_iterator &begin, + const nsACString::const_iterator &end, + PRUint32 *first, PRUint32 *last); + + // Expand a stringified chunk list into an array of ints. + nsresult ParseChunkList(const nsACString& chunkStr, + nsTArray& chunks); + + // Join an array of ints into a stringified chunk list. + nsresult JoinChunkList(nsTArray& chunks, nsCString& chunkStr); + + // List the add/subtract chunks that have been applied to a table + nsresult GetChunkLists(PRUint32 tableId, + nsACString& addChunks, + nsACString& subChunks); + + // Set the list of add/subtract chunks that have been applied to a table + nsresult SetChunkLists(PRUint32 tableId, + const nsACString& addChunks, + const nsACString& subChunks); + + // Cache the list of add/subtract chunks applied to the table, optionally + // parsing the add or sub lists. These lists are cached while updating + // tables to avoid excessive database reads/writes and parsing. + nsresult CacheChunkLists(PRUint32 tableId, + bool parseAdds, + bool parseSubs); + + // Clear the cached list of add/subtract chunks. + void ClearCachedChunkLists(); + + // Flush the cached add/subtract lists to the database. + nsresult FlushChunkLists(); + + // Inserts a chunk id into the list, sorted. Returns TRUE if the + // number was successfully added, FALSE if the chunk already exists. + bool InsertChunkId(nsTArray& chunks, PRUint32 chunkNum); + + // Add a list of entries to the database, merging with + // existing entries as necessary + nsresult AddChunk(PRUint32 tableId, PRUint32 chunkNum, + nsTArray& entries); + + // Expire an add chunk + nsresult ExpireAdd(PRUint32 tableId, PRUint32 chunkNum); + + // Subtract a list of entries from the database + nsresult SubChunk(PRUint32 tableId, PRUint32 chunkNum, + nsTArray& entries); + + // Expire a subtract chunk + nsresult ExpireSub(PRUint32 tableId, PRUint32 chunkNum); + + // Handle line-oriented control information from a stream update + nsresult ProcessResponseLines(bool* done); + // Handle chunk data from a stream update + nsresult ProcessChunk(bool* done); + + // Sets up a transaction and begins counting update time. + nsresult SetupUpdate(); + // Applies the current transaction and resets the update/working times. nsresult ApplyUpdate(); // Reset the in-progress update stream void ResetStream(); - // Reset the in-progress update - void ResetUpdate(); + // Reset the in-progress update + void ResetUpdate(); + + // Look for a given lookup string (www.hostname.com/path/to/resource.html) + // Returns a list of entries that match. + nsresult Check(const nsCSubstring& spec, + nsTArray& results); // Perform a classifier lookup for a given url. nsresult DoLookup(const nsACString& spec, nsIUrlClassifierLookupCallback* c); // Add entries to the results. - nsresult AddNoise(const Prefix aPrefix, - const nsCString tableName, - PRInt32 aCount, - LookupResultArray& results); + nsresult AddNoise(PRInt64 nearID, + PRInt32 count, + nsTArray& results); + + // Construct a Prefix Set with known prefixes + nsresult LoadPrefixSet(nsCOMPtr & aFile); + nsresult ConstructPrefixSet(); + + // Set the SQLite cache size + nsresult SetCacheSize(mozIStorageConnection * aConnection, + PRInt32 aCacheSize); + + nsCOMPtr mDBFile; + nsCOMPtr mPSFile; nsCOMPtr mCryptoHash; - nsAutoPtr mClassifier; - nsAutoPtr mProtocolParser; + // Holds a connection to the Db. We lazily initialize this because it has + // to be created in the background thread (currently mozStorageConnection + // isn't thread safe). + nsCOMPtr mConnection; - // Directory where to store the SB databases. - nsCOMPtr mCacheDir; + // The main collection of entries. This is the store that will be checked + // when classifying a URL. + nsUrlClassifierAddStore mMainStore; - // XXX: maybe an array of autoptrs. Or maybe a class specifically - // storing a series of updates. - nsTArray mTableUpdates; + // The collection of subs waiting for their accompanying add. + nsUrlClassifierSubStore mPendingSubStore; + + nsCOMPtr mGetChunkListsStatement; + nsCOMPtr mSetChunkListsStatement; + + nsCOMPtr mGetTablesStatement; + nsCOMPtr mGetTableIdStatement; + nsCOMPtr mGetTableNameStatement; + nsCOMPtr mInsertTableIdStatement; + nsCOMPtr mGetPageSizeStatement; + + // Stores the last time a given table was updated. + nsDataHashtable mTableFreshness; + + // We receive data in small chunks that may be broken in the middle of + // a line. So we save the last partial line here. + nsCString mPendingStreamUpdate; PRInt32 mUpdateWait; - // Entries that cannot be completed. We expect them to die at - // the next update - PrefixArray mMissCache; + bool mResetRequested; + bool mGrewCache; + + enum { + STATE_LINE, + STATE_CHUNK + } mState; + + enum { + CHUNK_ADD, + CHUNK_SUB + } mChunkType; + + PRUint32 mChunkNum; + PRUint32 mHashSize; + PRUint32 mChunkLen; + + // List of tables included in this update. + nsTArray mUpdateTables; + + nsCString mUpdateTable; + PRUint32 mUpdateTableId; nsresult mUpdateStatus; - nsTArray mUpdateTables; nsCOMPtr mUpdateObserver; bool mInStream; + bool mPrimaryStream; + + bool mHaveCachedLists; + PRUint32 mCachedListsTable; + nsCAutoString mCachedSubsStr; + nsCAutoString mCachedAddsStr; + + bool mHaveCachedAddChunks; + nsTArray mCachedAddChunks; + + bool mHaveCachedSubChunks; + nsTArray mCachedSubChunks; // The client key with which the data from the server will be MAC'ed. nsCString mUpdateClientKey; - // The client-specific hash key to rehash - PRUint32 mHashKey; + // The MAC stated by the server. + nsCString mServerMAC; + // Start time of the current update interval. This will be reset + // every time we apply the update. + PRIntervalTime mUpdateStartTime; + + nsCOMPtr mHMAC; // The number of noise entries to add to the set of lookup results. PRInt32 mGethashNoise; + // Set of prefixes known to be in the database + nsRefPtr mPrefixSet; + // Can we use the PrefixSet (low memory conditions) + bool mPrefixSetEnabled; + Mutex mPrefixSetEnabledLock; + // Pending lookups are stored in a queue for processing. The queue // is protected by mPendingLookupLock. Mutex mPendingLookupLock; class PendingLookup { public: - TimeStamp mStartTime; nsCString mKey; nsCOMPtr mCallback; }; @@ -239,28 +1352,72 @@ NS_IMPL_THREADSAFE_ISUPPORTS2(nsUrlClassifierDBServiceWorker, nsIUrlClassifierDBService) nsUrlClassifierDBServiceWorker::nsUrlClassifierDBServiceWorker() -: mInStream(false) + : mUpdateWait(0) + , mResetRequested(false) + , mGrewCache(false) + , mState(STATE_LINE) + , mChunkType(CHUNK_ADD) + , mChunkNum(0) + , mHashSize(0) + , mChunkLen(0) + , mUpdateTableId(0) + , mUpdateStatus(NS_OK) + , mInStream(false) + , mPrimaryStream(false) + , mHaveCachedLists(false) + , mCachedListsTable(PR_UINT32_MAX) + , mHaveCachedAddChunks(false) + , mHaveCachedSubChunks(false) + , mUpdateStartTime(0) , mGethashNoise(0) + , mPrefixSet(0) + , mPrefixSetEnabled(true) + , mPrefixSetEnabledLock("mPrefixSetEnabledLock") , mPendingLookupLock("nsUrlClassifierDBServerWorker.mPendingLookupLock") { } nsUrlClassifierDBServiceWorker::~nsUrlClassifierDBServiceWorker() { - NS_ASSERTION(!mClassifier, + NS_ASSERTION(!mConnection, "Db connection not closed, leaking memory! Call CloseDb " "to close the connection."); } nsresult nsUrlClassifierDBServiceWorker::Init(PRInt32 gethashNoise, - nsCOMPtr aCacheDir) + nsRefPtr & prefSet) { mGethashNoise = gethashNoise; - mCacheDir = aCacheDir; + mPrefixSet = prefSet; + + // Compute database filename + + // Because we dump raw integers into the database, this database isn't + // portable between machine types, so store it in the local profile dir. + nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, + getter_AddRefs(mDBFile)); + + if (NS_FAILED(rv)) { + rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, + getter_AddRefs(mDBFile)); + } + + if (NS_FAILED(rv)) return NS_ERROR_NOT_AVAILABLE; + + rv = mDBFile->Clone(getter_AddRefs(mPSFile)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mDBFile->Append(NS_LITERAL_STRING(DATABASE_FILENAME)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mPSFile->Append(NS_LITERAL_STRING(PREFIXSET_FILENAME)); + NS_ENSURE_SUCCESS(rv, rv); ResetUpdate(); + mTableFreshness.Init(); + return NS_OK; } @@ -273,13 +1430,318 @@ nsUrlClassifierDBServiceWorker::QueueLookup(const nsACString& spec, PendingLookup* lookup = mPendingLookups.AppendElement(); if (!lookup) return NS_ERROR_OUT_OF_MEMORY; - lookup->mStartTime = TimeStamp::Now(); lookup->mKey = spec; lookup->mCallback = callback; return NS_OK; } +nsresult +nsUrlClassifierDBService::CheckClean(const nsACString &spec, + bool *clean) +{ + Telemetry::AutoTimer timer; + + // Is the PrefixSet usable? + bool usePrefixSet = mWorker->LockPrefixSet(); + + // No, bail out and pretend the URL is not clean. We will do + // a database lookup and get the correct result. + if (!usePrefixSet) { + mWorker->UnlockPrefixSet(); + *clean = false; + return NS_OK; + } + + // Get the set of fragments to look up. + nsTArray fragments; + nsresult rv = GetLookupFragments(spec, fragments); + if (NS_FAILED(rv)) { + goto error_checkclean; + } + + PRUint32 prefixkey; + rv = mPrefixSet->GetKey(&prefixkey); + if (NS_FAILED(rv)) { + goto error_checkclean; + } + + *clean = true; + + for (PRUint32 i = 0; i < fragments.Length(); i++) { + nsUrlClassifierDomainHash fragmentKeyHash; + fragmentKeyHash.FromPlaintext(fragments[i], mHash); + + // Find the corresponding host key + nsUrlClassifierDomainHash hostkey; + rv = GetKey(fragments[i], hostkey, mHash); + if (NS_FAILED(rv)) { + /* This happens for hosts on the local network, + can't check these against the DB */ + continue; + } + + PRUint32 hostprefix = hostkey.ToUint32(); + PRUint32 fragkey = fragmentKeyHash.ToUint32(); + PRUint32 codedkey; + rv = KeyedHash(fragkey, hostprefix, prefixkey, &codedkey); + if (NS_FAILED(rv)) { + goto error_checkclean; + } + + bool found = false; + bool ready = false; /* opportunistic probe */ + rv = mPrefixSet->Probe(codedkey, prefixkey, &ready, &found); + if (NS_FAILED(rv)) { + goto error_checkclean; + } + LOG(("CheckClean Probed %X ready: %d found: %d ", + codedkey, ready, found)); + if (found || !ready) { + *clean = false; + } + } + + mWorker->UnlockPrefixSet(); + return NS_OK; + + error_checkclean: + mWorker->UnlockPrefixSet(); + return rv; +} + +static nsresult GetHostKeys(const nsACString &spec, + nsTArray &hostKeys) +{ + nsACString::const_iterator begin, end, iter; + spec.BeginReading(begin); + spec.EndReading(end); + + iter = begin; + if (!FindCharInReadable('/', iter, end)) { + return NS_OK; + } + + const nsCSubstring& host = Substring(begin, iter); + + if (IsCanonicalizedIP(host)) { + nsCString *key = hostKeys.AppendElement(); + if (!key) + return NS_ERROR_OUT_OF_MEMORY; + + key->Assign(host); + key->Append("/"); + return NS_OK; + } + + nsTArray hostComponents; + ParseString(PromiseFlatCString(host), '.', hostComponents); + + if (hostComponents.Length() < 2) { + // no host or toplevel host, this won't match anything in the db + return NS_OK; + } + + // First check with two domain components + PRInt32 last = PRInt32(hostComponents.Length()) - 1; + nsCString *lookupHost = hostKeys.AppendElement(); + if (!lookupHost) + return NS_ERROR_OUT_OF_MEMORY; + + lookupHost->Assign(hostComponents[last - 1]); + lookupHost->Append("."); + lookupHost->Append(hostComponents[last]); + lookupHost->Append("/"); + + // Now check with three domain components + if (hostComponents.Length() > 2) { + nsCString *lookupHost2 = hostKeys.AppendElement(); + if (!lookupHost2) + return NS_ERROR_OUT_OF_MEMORY; + lookupHost2->Assign(hostComponents[last - 2]); + lookupHost2->Append("."); + lookupHost2->Append(*lookupHost); + } + + return NS_OK; +} + +nsresult +GetLookupFragments(const nsACString& spec, + nsTArray& fragments) +{ + fragments.Clear(); + + nsACString::const_iterator begin, end, iter; + spec.BeginReading(begin); + spec.EndReading(end); + + iter = begin; + if (!FindCharInReadable('/', iter, end)) { + return NS_OK; + } + + const nsCSubstring& host = Substring(begin, iter++); + nsCAutoString path; + path.Assign(Substring(iter, end)); + + /** + * From the protocol doc: + * For the hostname, the client will try at most 5 different strings. They + * are: + * a) The exact hostname of the url + * b) The 4 hostnames formed by starting with the last 5 components and + * successivly removing the leading component. The top-level component + * can be skipped. This is not done if the hostname is a numerical IP. + */ + nsTArray hosts; + hosts.AppendElement(host); + + if (!IsCanonicalizedIP(host)) { + host.BeginReading(begin); + host.EndReading(end); + int numHostComponents = 0; + while (RFindInReadable(NS_LITERAL_CSTRING("."), begin, end) && + numHostComponents < MAX_HOST_COMPONENTS) { + // don't bother checking toplevel domains + if (++numHostComponents >= 2) { + host.EndReading(iter); + hosts.AppendElement(Substring(end, iter)); + } + end = begin; + host.BeginReading(begin); + } + } + + /** + * From the protocol doc: + * For the path, the client will also try at most 6 different strings. + * They are: + * a) the exact path of the url, including query parameters + * b) the exact path of the url, without query parameters + * c) the 4 paths formed by starting at the root (/) and + * successively appending path components, including a trailing + * slash. This behavior should only extend up to the next-to-last + * path component, that is, a trailing slash should never be + * appended that was not present in the original url. + */ + nsTArray paths; + nsCAutoString pathToAdd; + + path.BeginReading(begin); + path.EndReading(end); + iter = begin; + if (FindCharInReadable('?', iter, end)) { + pathToAdd = Substring(begin, iter); + paths.AppendElement(pathToAdd); + end = iter; + } + + int numPathComponents = 1; + iter = begin; + while (FindCharInReadable('/', iter, end) && + numPathComponents < MAX_PATH_COMPONENTS) { + iter++; + pathToAdd.Assign(Substring(begin, iter)); + paths.AppendElement(pathToAdd); + numPathComponents++; + } + + // If we haven't already done so, add the full path + if (!pathToAdd.Equals(path)) { + paths.AppendElement(path); + } + // Check an empty path (for whole-domain blacklist entries) + paths.AppendElement(EmptyCString()); + + for (PRUint32 hostIndex = 0; hostIndex < hosts.Length(); hostIndex++) { + for (PRUint32 pathIndex = 0; pathIndex < paths.Length(); pathIndex++) { + nsCString key; + key.Assign(hosts[hostIndex]); + key.Append('/'); + key.Append(paths[pathIndex]); + LOG(("Chking %s", key.get())); + + fragments.AppendElement(key); + } + } + + return NS_OK; +} + +nsresult +nsUrlClassifierDBServiceWorker::Check(const nsACString& spec, + nsTArray& results) +{ + PRInt64 now = (PR_Now() / PR_USEC_PER_SEC); + + // Get list of host keys to look up + nsAutoTArray lookupHosts; + nsresult rv = GetHostKeys(spec, lookupHosts); + + nsTArray mCachedEntries; + + // Gather host's prefixes + for (PRUint32 i = 0; i < lookupHosts.Length(); i++) { + // Find the corresponding host key + nsUrlClassifierDomainHash hostKey; + nsresult rv = GetKey(lookupHosts[i], hostKey, mCryptoHash); + NS_ENSURE_SUCCESS(rv, rv); + // Read the entries for this fragments host from SQLite + mMainStore.ReadAddEntries(hostKey, mCachedEntries); + } + + // Now get the set of fragments to look up. + nsTArray fragments; + rv = GetLookupFragments(spec, fragments); + NS_ENSURE_SUCCESS(rv, rv); + + // Now check each lookup fragment against the entries in the DB. + for (PRUint32 i = 0; i < fragments.Length(); i++) { + nsUrlClassifierCompleteHash lookupHash; + lookupHash.FromPlaintext(fragments[i], mCryptoHash); + + for (PRUint32 j = 0; j < mCachedEntries.Length(); j++) { + nsUrlClassifierEntry &entry = mCachedEntries[j]; + if (entry.Match(lookupHash)) { + // If the entry doesn't contain a complete hash, we need to + // save it here so that it can be compared against the + // complete hash. However, we don't set entry.mHaveComplete + // because it isn't a verified part of the entry yet. + nsUrlClassifierLookupResult *result = results.AppendElement(); + if (!result) + return NS_ERROR_OUT_OF_MEMORY; + + result->mLookupFragment = lookupHash; + result->mEntry = entry; + + // Fill in the table name. + GetTableName(entry.mTableId, result->mTableName); + + bool fresh; + PRInt64 tableUpdateTime; + if (mTableFreshness.Get(result->mTableName, &tableUpdateTime)) { + LOG(("tableUpdateTime: %lld, now: %lld, freshnessGuarantee: %d\n", + tableUpdateTime, now, gFreshnessGuarantee)); + fresh = ((now - tableUpdateTime) <= gFreshnessGuarantee); + } else { + LOG(("No expiration time for this table.\n")); + fresh = false; + } + + // This is a confirmed result if we match a complete fragment in + // an up-to-date table. + result->mConfirmed = entry.mHaveComplete && fresh; + + LOG(("Found a result. complete=%d, fresh=%d", + entry.mHaveComplete, fresh)); + } + } + } + + return NS_OK; +} + /** * Lookup up a key in the database is a two step process: * @@ -313,7 +1775,8 @@ nsUrlClassifierDBServiceWorker::DoLookup(const nsACString& spec, } #endif - nsAutoPtr results(new LookupResultArray()); + nsAutoPtr > results; + results = new nsTArray(); if (!results) { c->LookupComplete(nsnull); return NS_ERROR_OUT_OF_MEMORY; @@ -321,10 +1784,7 @@ nsUrlClassifierDBServiceWorker::DoLookup(const nsACString& spec, // we ignore failures from Check because we'd rather return the // results that were found than fail. - mClassifier->SetFreshTime(gFreshnessGuarantee); - mClassifier->Check(spec, *results); - - LOG(("Found %d results.", results->Length())); + Check(spec, *results); #if defined(PR_LOGGING) if (LOG_ENABLED()) { @@ -334,28 +1794,16 @@ nsUrlClassifierDBServiceWorker::DoLookup(const nsACString& spec, } #endif - nsAutoPtr completes(new LookupResultArray()); - for (PRUint32 i = 0; i < results->Length(); i++) { - if (!mMissCache.Contains(results->ElementAt(i).hash.prefix)) { - completes->AppendElement(results->ElementAt(i)); - } - } - - for (PRUint32 i = 0; i < completes->Length(); i++) { - if (!completes->ElementAt(i).Confirmed()) { + if (!results->ElementAt(i).mConfirmed) { // We're going to be doing a gethash request, add some extra entries. - // Note that we cannot pass the first two by reference, because we - // add to completes, whicah can cause completes to reallocate and move. - AddNoise(completes->ElementAt(i).mCodedPrefix, - completes->ElementAt(i).mTableName, - mGethashNoise, *completes); + AddNoise(results->ElementAt(i).mEntry.mId, mGethashNoise, *results); break; } } // At this point ownership of 'results' is handed to the callback. - c->LookupComplete(completes.forget()); + c->LookupComplete(results.forget()); return NS_OK; } @@ -371,42 +1819,50 @@ nsUrlClassifierDBServiceWorker::HandlePendingLookups() MutexAutoUnlock unlock(mPendingLookupLock); DoLookup(lookup.mKey, lookup.mCallback); } - double lookupTime = (TimeStamp::Now() - lookup.mStartTime).ToMilliseconds(); - Telemetry::Accumulate(Telemetry::URLCLASSIFIER_LOOKUP_TIME, - static_cast(lookupTime)); } return NS_OK; } nsresult -nsUrlClassifierDBServiceWorker::AddNoise(const Prefix aPrefix, - const nsCString tableName, - PRInt32 aCount, - LookupResultArray& results) +nsUrlClassifierDBServiceWorker::AddNoise(PRInt64 nearID, + PRInt32 count, + nsTArray& results) { - if (aCount < 1) { + if (count < 1) { return NS_OK; } - PrefixArray noiseEntries; - nsresult rv = mClassifier->ReadNoiseEntries(aPrefix, tableName, - aCount, &noiseEntries); + PRInt64 randomNum; + nsresult rv = mMainStore.RandomNumber(&randomNum); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt32 numBefore = randomNum % count; + + nsTArray noiseEntries; + rv = mMainStore.ReadNoiseEntries(nearID, numBefore, true, noiseEntries); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mMainStore.ReadNoiseEntries(nearID, count - numBefore, false, noiseEntries); NS_ENSURE_SUCCESS(rv, rv); for (PRUint32 i = 0; i < noiseEntries.Length(); i++) { - LookupResult *result = results.AppendElement(); + nsUrlClassifierLookupResult *result = results.AppendElement(); if (!result) return NS_ERROR_OUT_OF_MEMORY; - result->hash.prefix = noiseEntries[i]; + result->mEntry = noiseEntries[i]; + result->mConfirmed = false; result->mNoise = true; - result->mTableName.Assign(tableName); + + // Fill in the table name. + GetTableName(noiseEntries[i].mTableId, result->mTableName); } return NS_OK; } + // Lookup a key in the db. NS_IMETHODIMP nsUrlClassifierDBServiceWorker::Lookup(const nsACString& spec, @@ -427,31 +1883,1027 @@ nsUrlClassifierDBServiceWorker::GetTables(nsIUrlClassifierCallback* c) return NS_ERROR_FAILURE; } - NS_ENSURE_SUCCESS(rv, rv); + mozStorageStatementScoper scoper(mGetTablesStatement); nsCAutoString response; - mClassifier->TableRequest(response); + bool hasMore; + while (NS_SUCCEEDED(rv = mGetTablesStatement->ExecuteStep(&hasMore)) && + hasMore) { + nsCAutoString val; + mGetTablesStatement->GetUTF8String(0, val); + + if (val.IsEmpty()) { + continue; + } + + response.Append(val); + response.Append(';'); + + mGetTablesStatement->GetUTF8String(1, val); + + bool haveAdds = false; + if (!val.IsEmpty()) { + response.Append("a:"); + response.Append(val); + haveAdds = true; + } + + mGetTablesStatement->GetUTF8String(2, val); + if (!val.IsEmpty()) { + if (haveAdds) + response.Append(":"); + + response.Append("s:"); + response.Append(val); + } + + response.Append('\n'); + } + + if (NS_FAILED(rv)) { + response.Truncate(); + } + c->HandleEvent(response); return rv; } +nsresult +nsUrlClassifierDBServiceWorker::GetTableId(const nsACString& table, + PRUint32* tableId) +{ + mozStorageStatementScoper findScoper(mGetTableIdStatement); + + nsresult rv = mGetTableIdStatement->BindUTF8StringByIndex(0, table); + NS_ENSURE_SUCCESS(rv, rv); + + bool exists; + rv = mGetTableIdStatement->ExecuteStep(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (exists) { + *tableId = mGetTableIdStatement->AsInt32(0); + return NS_OK; + } + + mozStorageStatementScoper insertScoper(mInsertTableIdStatement); + rv = mInsertTableIdStatement->BindUTF8StringByIndex(0, table); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mInsertTableIdStatement->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt64 rowId; + rv = mConnection->GetLastInsertRowID(&rowId); + NS_ENSURE_SUCCESS(rv, rv); + + if (rowId > PR_UINT32_MAX) + return NS_ERROR_FAILURE; + + *tableId = rowId; + + return NS_OK; +} + +nsresult +nsUrlClassifierDBServiceWorker::GetTableName(PRUint32 tableId, + nsACString& tableName) +{ + mozStorageStatementScoper findScoper(mGetTableNameStatement); + nsresult rv = mGetTableNameStatement->BindInt32ByIndex(0, tableId); + NS_ENSURE_SUCCESS(rv, rv); + bool exists; + rv = mGetTableNameStatement->ExecuteStep(&exists); + NS_ENSURE_SUCCESS(rv, rv); + if (!exists) return NS_ERROR_FAILURE; + + return mGetTableNameStatement->GetUTF8String(0, tableName); +} + +nsresult +nsUrlClassifierDBServiceWorker::InflateChunk(nsACString& chunk) +{ + nsCAutoString inflated; + char buf[4096]; + + const nsPromiseFlatCString& flat = PromiseFlatCString(chunk); + + z_stream stream; + memset(&stream, 0, sizeof(stream)); + stream.next_in = (Bytef*)flat.get(); + stream.avail_in = flat.Length(); + + if (inflateInit(&stream) != Z_OK) { + return NS_ERROR_FAILURE; + } + + int code; + do { + stream.next_out = (Bytef*)buf; + stream.avail_out = sizeof(buf); + + code = inflate(&stream, Z_NO_FLUSH); + PRUint32 numRead = sizeof(buf) - stream.avail_out; + + if (code == Z_OK || code == Z_STREAM_END) { + inflated.Append(buf, numRead); + } + } while (code == Z_OK); + + inflateEnd(&stream); + + if (code != Z_STREAM_END) { + return NS_ERROR_FAILURE; + } + + chunk = inflated; + + return NS_OK; +} + +nsresult +nsUrlClassifierStore::DeleteEntry(nsUrlClassifierEntry& entry) +{ + if (entry.mId == -1) { + return NS_OK; + } + + mozStorageStatementScoper scoper(mDeleteStatement); + mDeleteStatement->BindInt64ByIndex(0, entry.mId); + nsresult rv = mDeleteStatement->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + + entry.mId = -1; + + return NS_OK; +} + +nsresult +nsUrlClassifierStore::WriteEntry(nsUrlClassifierEntry& entry) +{ + if (entry.mId != -1) { + // existing entry, just ignore it + return NS_OK; + } + + mozStorageStatementScoper scoper(mInsertStatement); + + nsresult rv = BindStatement(entry, mInsertStatement); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mInsertStatement->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + + PRInt64 rowId; + rv = mConnection->GetLastInsertRowID(&rowId); + NS_ENSURE_SUCCESS(rv, rv); + + if (rowId > PR_UINT32_MAX) { + return NS_ERROR_FAILURE; + } + + entry.mId = rowId; + + return NS_OK; +} + +nsresult +nsUrlClassifierStore::UpdateEntry(nsUrlClassifierEntry& entry) +{ + mozStorageStatementScoper scoper(mUpdateStatement); + + NS_ENSURE_ARG(entry.mId != -1); + + nsresult rv = BindStatement(entry, mUpdateStatement); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mUpdateStatement->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +static bool +IsCanonicalizedIP(const nsACString& host) +{ + // The canonicalization process will have left IP addresses in dotted + // decimal with no surprises. + PRUint32 i1, i2, i3, i4; + char c; + if (PR_sscanf(PromiseFlatCString(host).get(), "%u.%u.%u.%u%c", + &i1, &i2, &i3, &i4, &c) == 4) { + return (i1 <= 0xFF && i2 <= 0xFF && i3 <= 0xFF && i4 <= 0xFF); + } + + return false; +} + +static nsresult +GetKey(const nsACString& spec, + nsUrlClassifierDomainHash& hash, + nsICryptoHash * aCryptoHash) +{ + nsACString::const_iterator begin, end, iter; + spec.BeginReading(begin); + spec.EndReading(end); + + iter = begin; + if (!FindCharInReadable('/', iter, end)) { + return NS_OK; + } + + const nsCSubstring& host = Substring(begin, iter); + + if (IsCanonicalizedIP(host)) { + nsCAutoString key; + key.Assign(host); + key.Append("/"); + return hash.FromPlaintext(key, aCryptoHash); + } + + nsTArray hostComponents; + ParseString(PromiseFlatCString(host), '.', hostComponents); + + if (hostComponents.Length() < 2) + return NS_ERROR_FAILURE; + + PRInt32 last = PRInt32(hostComponents.Length()) - 1; + nsCAutoString lookupHost; + + if (hostComponents.Length() > 2) { + lookupHost.Append(hostComponents[last - 2]); + lookupHost.Append("."); + } + + lookupHost.Append(hostComponents[last - 1]); + lookupHost.Append("."); + lookupHost.Append(hostComponents[last]); + lookupHost.Append("/"); + + return hash.FromPlaintext(lookupHost, aCryptoHash); +} + +nsresult +nsUrlClassifierDBServiceWorker::GetShaEntries(PRUint32 tableId, + PRUint32 chunkType, + PRUint32 chunkNum, + PRUint32 domainSize, + PRUint32 fragmentSize, + nsACString& chunk, + nsTArray& entries) +{ + PRUint32 start = 0; + while (start + domainSize + 1 <= chunk.Length()) { + nsUrlClassifierDomainHash domain; + domain.Assign(Substring(chunk, start, DOMAIN_LENGTH)); + start += domainSize; + + // then there is a one-byte count of fragments + PRUint8 numEntries = static_cast(chunk[start]); + start++; + + if (numEntries == 0) { + // if there are no fragments, the domain itself is treated as a + // fragment. This will only work if domainHashSize == hashSize + if (domainSize != fragmentSize) { + NS_WARNING("Received 0-fragment entry where domainSize != fragmentSize"); + return NS_ERROR_FAILURE; + } + + nsUrlClassifierEntry* entry = entries.AppendElement(); + if (!entry) return NS_ERROR_OUT_OF_MEMORY; + + entry->mKey = domain; + entry->mTableId = tableId; + entry->mChunkId = chunkNum; + entry->SetHash(domain); + + if (chunkType == CHUNK_SUB) { + if (start + 4 > chunk.Length()) { + // there isn't as much data as there should be. + NS_WARNING("Received a zero-entry sub chunk without an associated add."); + return NS_ERROR_FAILURE; + } + const nsCSubstring& str = Substring(chunk, start, 4); + PRUint32 p; + memcpy(&p, str.BeginReading(), 4); + entry->mAddChunkId = PR_ntohl(p); + if (entry->mAddChunkId == 0) { + NS_WARNING("Received invalid chunk number."); + return NS_ERROR_FAILURE; + } + start += 4; + } + } else { + PRUint32 entrySize = fragmentSize; + if (chunkType == CHUNK_SUB) { + entrySize += 4; + } + if (start + (numEntries * entrySize) > chunk.Length()) { + // there isn't as much data as they said there would be. + NS_WARNING("Received a chunk without enough data"); + return NS_ERROR_FAILURE; + } + + for (PRUint8 i = 0; i < numEntries; i++) { + nsUrlClassifierEntry* entry = entries.AppendElement(); + if (!entry) return NS_ERROR_OUT_OF_MEMORY; + + entry->mKey = domain; + entry->mTableId = tableId; + entry->mChunkId = chunkNum; + + if (chunkType == CHUNK_SUB) { + const nsCSubstring& str = Substring(chunk, start, 4); + PRUint32 p; + memcpy(&p, str.BeginReading(), 4); + entry->mAddChunkId = PR_ntohl(p); + if (entry->mAddChunkId == 0) { + NS_WARNING("Received invalid chunk number."); + return NS_ERROR_FAILURE; + } + start += 4; + } + + if (fragmentSize == PARTIAL_LENGTH) { + nsUrlClassifierPartialHash hash; + hash.Assign(Substring(chunk, start, PARTIAL_LENGTH)); + entry->SetHash(hash); + } else if (fragmentSize == COMPLETE_LENGTH) { + nsUrlClassifierCompleteHash hash; + hash.Assign(Substring(chunk, start, COMPLETE_LENGTH)); + entry->SetHash(hash); + } else { + NS_ASSERTION(false, "Invalid fragment size!"); + return NS_ERROR_FAILURE; + } + + start += fragmentSize; + } + } + } + + return NS_OK; +} + +nsresult +nsUrlClassifierDBServiceWorker::GetChunkEntries(const nsACString& table, + PRUint32 tableId, + PRUint32 chunkType, + PRUint32 chunkNum, + PRUint32 hashSize, + nsACString& chunk, + nsTArray& entries) +{ + nsresult rv; + if (StringEndsWith(table, NS_LITERAL_CSTRING("-exp"))) { + // regexp tables need to be ungzipped + rv = InflateChunk(chunk); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (StringEndsWith(table, NS_LITERAL_CSTRING("-shavar"))) { + rv = GetShaEntries(tableId, chunkType, chunkNum, DOMAIN_LENGTH, hashSize, + chunk, entries); + NS_ENSURE_SUCCESS(rv, rv); + } else { + nsTArray lines; + ParseString(PromiseFlatCString(chunk), '\n', lines); + + // non-hashed tables need to be hashed + for (PRInt32 i = 0; i < PRInt32(lines.Length()); i++) { + nsUrlClassifierEntry *entry = entries.AppendElement(); + if (!entry) + return NS_ERROR_OUT_OF_MEMORY; + + nsCAutoString entryStr; + if (chunkType == CHUNK_SUB) { + nsCString::const_iterator begin, iter, end; + lines[i].BeginReading(begin); + lines[i].EndReading(end); + iter = begin; + if (!FindCharInReadable(':', iter, end) || + PR_sscanf(lines[i].get(), "%d:", &entry->mAddChunkId) != 1) { + NS_WARNING("Received sub chunk without associated add chunk."); + return NS_ERROR_FAILURE; + } + iter++; + entryStr = Substring(iter, end); + } else { + entryStr = lines[i]; + } + + rv = GetKey(entryStr, entry->mKey, mCryptoHash); + NS_ENSURE_SUCCESS(rv, rv); + + entry->mTableId = tableId; + entry->mChunkId = chunkNum; + if (hashSize == PARTIAL_LENGTH) { + nsUrlClassifierPartialHash hash; + hash.FromPlaintext(entryStr, mCryptoHash); + entry->SetHash(hash); + } else if (hashSize == COMPLETE_LENGTH) { + nsUrlClassifierCompleteHash hash; + hash.FromPlaintext(entryStr, mCryptoHash); + entry->SetHash(hash); + } else { + NS_ASSERTION(false, "Invalid fragment size!"); + return NS_ERROR_FAILURE; + } + } + } + + return NS_OK; +} + +bool +nsUrlClassifierDBServiceWorker::ParseChunkRange(nsACString::const_iterator &begin, + const nsACString::const_iterator &end, + PRUint32 *first, + PRUint32 *last) +{ + nsACString::const_iterator iter = begin; + FindCharInReadable(',', iter, end); + + nsCAutoString element(Substring(begin, iter)); + begin = iter; + if (begin != end) + begin++; + + PRUint32 numRead = PR_sscanf(element.get(), "%u-%u", first, last); + if (numRead == 2) { + if (*first > *last) { + PRUint32 tmp = *first; + *first = *last; + *last = tmp; + } + return true; + } + + if (numRead == 1) { + *last = *first; + return true; + } + + return false; +} + +nsresult +nsUrlClassifierDBServiceWorker::ParseChunkList(const nsACString& chunkStr, + nsTArray& chunks) +{ + LOG(("Parsing %s", PromiseFlatCString(chunkStr).get())); + + nsACString::const_iterator begin, end; + chunkStr.BeginReading(begin); + chunkStr.EndReading(end); + while (begin != end) { + PRUint32 first, last; + if (ParseChunkRange(begin, end, &first, &last)) { + for (PRUint32 num = first; num <= last; num++) { + chunks.AppendElement(num); + } + } + } + + LOG(("Got %d elements.", chunks.Length())); + + return NS_OK; +} + +nsresult +nsUrlClassifierDBServiceWorker::JoinChunkList(nsTArray& chunks, + nsCString& chunkStr) +{ + chunkStr.Truncate(); + chunks.Sort(); + + PRUint32 i = 0; + while (i < chunks.Length()) { + if (i != 0) { + chunkStr.Append(','); + } + chunkStr.AppendInt(chunks[i]); + + PRUint32 first = i; + PRUint32 last = first; + i++; + while (i < chunks.Length() && (chunks[i] == chunks[i - 1] + 1 || chunks[i] == chunks[i - 1])) { + last = i++; + } + + if (last != first) { + chunkStr.Append('-'); + chunkStr.AppendInt(chunks[last]); + } + } + + return NS_OK; +} + + +nsresult +nsUrlClassifierDBServiceWorker::GetChunkLists(PRUint32 tableId, + nsACString& addChunks, + nsACString& subChunks) +{ + addChunks.Truncate(); + subChunks.Truncate(); + + mozStorageStatementScoper scoper(mGetChunkListsStatement); + + nsresult rv = mGetChunkListsStatement->BindInt32ByIndex(0, tableId); + NS_ENSURE_SUCCESS(rv, rv); + + bool hasMore = false; + rv = mGetChunkListsStatement->ExecuteStep(&hasMore); + NS_ENSURE_SUCCESS(rv, rv); + + if (!hasMore) { + LOG(("Getting chunks for %d, found nothing", tableId)); + return NS_OK; + } + + rv = mGetChunkListsStatement->GetUTF8String(0, addChunks); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mGetChunkListsStatement->GetUTF8String(1, subChunks); + NS_ENSURE_SUCCESS(rv, rv); + + LOG(("Getting chunks for %d, got %s/%s", + tableId, + PromiseFlatCString(addChunks).get(), + PromiseFlatCString(subChunks).get())); + + return NS_OK; +} + +nsresult +nsUrlClassifierDBServiceWorker::SetChunkLists(PRUint32 tableId, + const nsACString& addChunks, + const nsACString& subChunks) +{ + mozStorageStatementScoper scoper(mSetChunkListsStatement); + + mSetChunkListsStatement->BindUTF8StringByIndex(0, addChunks); + mSetChunkListsStatement->BindUTF8StringByIndex(1, subChunks); + mSetChunkListsStatement->BindInt32ByIndex(2, tableId); + nsresult rv = mSetChunkListsStatement->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +nsUrlClassifierDBServiceWorker::CacheChunkLists(PRUint32 tableId, + bool parseAdds, + bool parseSubs) +{ + nsresult rv; + + if (mHaveCachedLists && mCachedListsTable != tableId) { + rv = FlushChunkLists(); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (!mHaveCachedLists) { + rv = GetChunkLists(tableId, mCachedAddsStr, mCachedSubsStr); + NS_ENSURE_SUCCESS(rv, rv); + + mHaveCachedLists = true; + mCachedListsTable = tableId; + } + + if (parseAdds && !mHaveCachedAddChunks) { + ParseChunkList(mCachedAddsStr, mCachedAddChunks); + mHaveCachedAddChunks = true; + } + + if (parseSubs && !mHaveCachedSubChunks) { + ParseChunkList(mCachedSubsStr, mCachedSubChunks); + mHaveCachedSubChunks = true; + } + + return NS_OK; +} + +nsresult +nsUrlClassifierDBServiceWorker::FlushChunkLists() +{ + if (!mHaveCachedLists) { + return NS_OK; + } + + if (mHaveCachedAddChunks) { + JoinChunkList(mCachedAddChunks, mCachedAddsStr); + } + + if (mHaveCachedSubChunks) { + JoinChunkList(mCachedSubChunks, mCachedSubsStr); + } + + nsresult rv = SetChunkLists(mCachedListsTable, + mCachedAddsStr, mCachedSubsStr); + + // clear out the cache before checking/returning the error here. + ClearCachedChunkLists(); + + return rv; +} + +void +nsUrlClassifierDBServiceWorker::ClearCachedChunkLists() +{ + mCachedAddsStr.Truncate(); + mCachedSubsStr.Truncate(); + mCachedListsTable = PR_UINT32_MAX; + mHaveCachedLists = false; + + mCachedAddChunks.Clear(); + mHaveCachedAddChunks = false; + + mCachedSubChunks.Clear(); + mHaveCachedSubChunks = false; +} + +bool +nsUrlClassifierDBServiceWorker::InsertChunkId(nsTArray &chunks, + PRUint32 chunkNum) +{ + PRUint32 low = 0, high = chunks.Length(); + while (high > low) { + PRUint32 mid = (high + low) >> 1; + if (chunks[mid] == chunkNum) + return false; + if (chunks[mid] < chunkNum) + low = mid + 1; + else + high = mid; + } + + PRUint32 *item = chunks.InsertElementAt(low, chunkNum); + return (item != nsnull); +} + +nsresult +nsUrlClassifierDBServiceWorker::AddChunk(PRUint32 tableId, + PRUint32 chunkNum, + nsTArray& entries) +{ +#if defined(PR_LOGGING) + PRIntervalTime clockStart = 0; + if (LOG_ENABLED()) { + clockStart = PR_IntervalNow(); + } +#endif + + nsresult rv = CacheChunkLists(tableId, true, false); + NS_ENSURE_SUCCESS(rv, rv); + + if (!InsertChunkId(mCachedAddChunks, chunkNum)) { + LOG(("Ignoring duplicate add chunk %d in table %d", chunkNum, tableId)); + return NS_OK; + } + + LOG(("Adding %d entries to chunk %d in table %d", entries.Length(), chunkNum, tableId)); + + nsTArray entryIDs; + + nsAutoTArray subEntries; + rv = mPendingSubStore.ReadSubEntries(tableId, chunkNum, subEntries); + NS_ENSURE_SUCCESS(rv, rv); + + for (PRUint32 i = 0; i < entries.Length(); i++) { + nsUrlClassifierEntry& thisEntry = entries[i]; + + HandlePendingLookups(); + + bool writeEntry = true; + for (PRUint32 j = 0; j < subEntries.Length(); j++) { + if (thisEntry.SubMatch(subEntries[j])) { + subEntries.RemoveElementAt(j); + + writeEntry = false; + break; + } + } + + HandlePendingLookups(); + + if (writeEntry) { + rv = mMainStore.WriteEntry(thisEntry); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + rv = mPendingSubStore.ExpireAddChunk(tableId, chunkNum); + NS_ENSURE_SUCCESS(rv, rv); + +#if defined(PR_LOGGING) + if (LOG_ENABLED()) { + PRIntervalTime clockEnd = PR_IntervalNow(); + LOG(("adding chunk %d took %dms\n", chunkNum, + PR_IntervalToMilliseconds(clockEnd - clockStart))); + } +#endif + + return rv; +} + +nsresult +nsUrlClassifierStore::Expire(PRUint32 tableId, PRUint32 chunkNum) +{ + LOG(("Expiring chunk %d\n", chunkNum)); + + mozStorageStatementScoper expireScoper(mExpireStatement); + + nsresult rv = mExpireStatement->BindInt32ByIndex(0, tableId); + NS_ENSURE_SUCCESS(rv, rv); + rv = mExpireStatement->BindInt32ByIndex(1, chunkNum); + NS_ENSURE_SUCCESS(rv, rv); + + mWorker->HandlePendingLookups(); + + rv = mExpireStatement->Execute(); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +nsUrlClassifierDBServiceWorker::ExpireAdd(PRUint32 tableId, + PRUint32 chunkNum) +{ + nsresult rv = CacheChunkLists(tableId, true, false); + NS_ENSURE_SUCCESS(rv, rv); + mCachedAddChunks.RemoveElement(chunkNum); + + return mMainStore.Expire(tableId, chunkNum); +} + +nsresult +nsUrlClassifierDBServiceWorker::SubChunk(PRUint32 tableId, + PRUint32 chunkNum, + nsTArray& entries) +{ + nsresult rv = CacheChunkLists(tableId, true, true); + + if (!InsertChunkId(mCachedSubChunks, chunkNum)) { + LOG(("Ignoring duplicate sub chunk %d in table %d", chunkNum, tableId)); + return NS_OK; + } + + LOG(("Subbing %d entries in chunk %d in table %d", entries.Length(), chunkNum, tableId)); + + for (PRUint32 i = 0; i < entries.Length(); i++) { + nsAutoTArray existingEntries; + nsUrlClassifierEntry& thisEntry = entries[i]; + + HandlePendingLookups(); + + // Check if we have the add chunk associated with the sub. + bool haveAdds = (mCachedAddChunks.BinaryIndexOf(thisEntry.mAddChunkId) != + mCachedAddChunks.NoIndex); + + if (haveAdds) { + rv = mMainStore.ReadAddEntries(thisEntry.mKey, thisEntry.mTableId, + thisEntry.mAddChunkId, existingEntries); + NS_ENSURE_SUCCESS(rv, rv); + } + + for (PRUint32 j = 0; j < existingEntries.Length(); j++) { + if (existingEntries[j].SubMatch(thisEntry)) { + rv = mMainStore.DeleteEntry(existingEntries[j]); + NS_ENSURE_SUCCESS(rv, rv); + existingEntries.RemoveElementAt(j); + break; + } + } + + if (!haveAdds) { + // Save this entry in the pending subtraction store. + rv = mPendingSubStore.WriteEntry(thisEntry); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + return NS_OK; +} + +nsresult +nsUrlClassifierDBServiceWorker::ExpireSub(PRUint32 tableId, PRUint32 chunkNum) +{ + nsresult rv = CacheChunkLists(tableId, false, true); + NS_ENSURE_SUCCESS(rv, rv); + mCachedSubChunks.RemoveElement(chunkNum); + + return mPendingSubStore.Expire(tableId, chunkNum); +} + +nsresult +nsUrlClassifierDBServiceWorker::ProcessChunk(bool* done) +{ + // wait until the chunk has been read + if (mPendingStreamUpdate.Length() < static_cast(mChunkLen)) { + *done = true; + return NS_OK; + } + + nsCAutoString chunk; + chunk.Assign(Substring(mPendingStreamUpdate, 0, mChunkLen)); + mPendingStreamUpdate = Substring(mPendingStreamUpdate, mChunkLen); + + LOG(("Handling a chunk sized %d", chunk.Length())); + + nsTArray entries; + nsresult rv = GetChunkEntries(mUpdateTable, mUpdateTableId, mChunkType, + mChunkNum, mHashSize, chunk, entries); + NS_ENSURE_SUCCESS(rv, rv); + + if (mChunkType == CHUNK_ADD) { + rv = AddChunk(mUpdateTableId, mChunkNum, entries); + } else { + rv = SubChunk(mUpdateTableId, mChunkNum, entries); + } + + mState = STATE_LINE; + *done = false; + + return rv; +} + +nsresult +nsUrlClassifierDBServiceWorker::ProcessResponseLines(bool* done) +{ + PRUint32 cur = 0; + PRInt32 next; + + nsresult rv; + // We will run to completion unless we find a chunk line + *done = true; + + nsACString& updateString = mPendingStreamUpdate; + + while(cur < updateString.Length() && + (next = updateString.FindChar('\n', cur)) != kNotFound) { + const nsCSubstring& line = Substring(updateString, cur, next - cur); + cur = next + 1; + + LOG(("Processing %s\n", PromiseFlatCString(line).get())); + + if (mHMAC && mServerMAC.IsEmpty()) { + // If we did not receive a server MAC during BeginStream(), we + // require the first line of the update to be either a MAC or + // a request to rekey. + if (StringBeginsWith(line, NS_LITERAL_CSTRING("m:"))) { + mServerMAC = Substring(line, 2); + nsUrlClassifierUtils::UnUrlsafeBase64(mServerMAC); + + // The remainder of the pending update needs to be digested. + const nsCSubstring &toDigest = Substring(updateString, cur); + rv = mHMAC->Update(reinterpret_cast(toDigest.BeginReading()), + toDigest.Length()); + NS_ENSURE_SUCCESS(rv, rv); + } else if (line.EqualsLiteral("e:pleaserekey")) { + mUpdateObserver->RekeyRequested(); + } else { + LOG(("No MAC specified!")); + return NS_ERROR_FAILURE; + } + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("n:"))) { + if (PR_sscanf(PromiseFlatCString(line).get(), "n:%d", + &mUpdateWait) != 1) { + LOG(("Error parsing n: field: %s", PromiseFlatCString(line).get())); + mUpdateWait = 0; + } + } else if (line.EqualsLiteral("r:pleasereset")) { + mResetRequested = true; + } else if (line.EqualsLiteral("e:pleaserekey")) { + mUpdateObserver->RekeyRequested(); + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("i:"))) { + mUpdateTable.Assign(Substring(line, 2)); + GetTableId(mUpdateTable, &mUpdateTableId); + LOG(("update table: '%s' (%d)", mUpdateTable.get(), mUpdateTableId)); + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("u:"))) { + if (!mPrimaryStream) { + LOG(("Forwarded update tried to add its own forwarded update.")); + return NS_ERROR_FAILURE; + } + + const nsCSubstring& data = Substring(line, 2); + if (mHMAC) { + // We're expecting MACs alongside any url forwards. + nsCSubstring::const_iterator begin, end, sepBegin, sepEnd; + data.BeginReading(begin); + sepBegin = begin; + + data.EndReading(end); + sepEnd = end; + + if (!RFindInReadable(NS_LITERAL_CSTRING(","), sepBegin, sepEnd)) { + NS_WARNING("No MAC specified for a redirect in a request that expects a MAC"); + return NS_ERROR_FAILURE; + } + + nsCString serverMAC(Substring(sepEnd, end)); + nsUrlClassifierUtils::UnUrlsafeBase64(serverMAC); + mUpdateObserver->UpdateUrlRequested(Substring(begin, sepBegin), + mUpdateTable, + serverMAC); + } else { + // We didn't ask for a MAC, none should have been specified. + mUpdateObserver->UpdateUrlRequested(data, mUpdateTable, + NS_LITERAL_CSTRING("")); + } + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("a:")) || + StringBeginsWith(line, NS_LITERAL_CSTRING("s:"))) { + mState = STATE_CHUNK; + char command; + if (PR_sscanf(PromiseFlatCString(line).get(), + "%c:%d:%d:%d", &command, &mChunkNum, &mHashSize, &mChunkLen) != 4) { + return NS_ERROR_FAILURE; + } + + if (mChunkLen > MAX_CHUNK_SIZE) { + return NS_ERROR_FAILURE; + } + + if (!(mHashSize == PARTIAL_LENGTH || mHashSize == COMPLETE_LENGTH)) { + NS_WARNING("Invalid hash size specified in update."); + return NS_ERROR_FAILURE; + } + + mChunkType = (command == 'a') ? CHUNK_ADD : CHUNK_SUB; + + // Done parsing lines, move to chunk state now + *done = false; + break; + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("ad:"))) { + const nsCSubstring &list = Substring(line, 3); + nsACString::const_iterator begin, end; + list.BeginReading(begin); + list.EndReading(end); + while (begin != end) { + PRUint32 first, last; + if (ParseChunkRange(begin, end, &first, &last)) { + for (PRUint32 num = first; num <= last; num++) { + rv = ExpireAdd(mUpdateTableId, num); + NS_ENSURE_SUCCESS(rv, rv); + } + } else { + return NS_ERROR_FAILURE; + } + } + } else if (StringBeginsWith(line, NS_LITERAL_CSTRING("sd:"))) { + const nsCSubstring &list = Substring(line, 3); + nsACString::const_iterator begin, end; + list.BeginReading(begin); + list.EndReading(end); + while (begin != end) { + PRUint32 first, last; + if (ParseChunkRange(begin, end, &first, &last)) { + for (PRUint32 num = first; num <= last; num++) { + rv = ExpireSub(mUpdateTableId, num); + NS_ENSURE_SUCCESS(rv, rv); + } + } else { + return NS_ERROR_FAILURE; + } + } + } else { + LOG(("ignoring unknown line: '%s'", PromiseFlatCString(line).get())); + } + } + + mPendingStreamUpdate = Substring(updateString, cur); + + return NS_OK; +} + void nsUrlClassifierDBServiceWorker::ResetStream() { - LOG(("ResetStream")); + mState = STATE_LINE; + mChunkNum = 0; + mHashSize = 0; + mChunkLen = 0; mInStream = false; - mProtocolParser = nsnull; + mPrimaryStream = false; + mUpdateTable.Truncate(); + mPendingStreamUpdate.Truncate(); + mServerMAC.Truncate(); + mHMAC = nsnull; } void nsUrlClassifierDBServiceWorker::ResetUpdate() { - LOG(("ResetUpdate")); mUpdateWait = 0; mUpdateStatus = NS_OK; mUpdateObserver = nsnull; mUpdateClientKey.Truncate(); + mResetRequested = false; + mUpdateTables.Clear(); } NS_IMETHODIMP @@ -466,8 +2918,6 @@ nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *obse const nsACString &tables, const nsACString &clientKey) { - LOG(("nsUrlClassifierDBServiceWorker::BeginUpdate [%s]", PromiseFlatCString(tables).get())); - if (gShuttingDownThread) return NS_ERROR_NOT_INITIALIZED; @@ -479,16 +2929,38 @@ nsUrlClassifierDBServiceWorker::BeginUpdate(nsIUrlClassifierUpdateObserver *obse return NS_ERROR_FAILURE; } - mUpdateStatus = NS_OK; + bool transaction; + rv = mConnection->GetTransactionInProgress(&transaction); + if (NS_FAILED(rv)) { + mUpdateStatus = rv; + return rv; + } + + if (transaction) { + NS_WARNING("Transaction already in progress in nsUrlClassifierDBServiceWorker::BeginUpdate. Cancelling update."); + mUpdateStatus = NS_ERROR_FAILURE; + return rv; + } + + rv = SetupUpdate(); + if (NS_FAILED(rv)) { + mUpdateStatus = rv; + return rv; + } + mUpdateObserver = observer; - SplitTables(tables, mUpdateTables); if (!clientKey.IsEmpty()) { rv = nsUrlClassifierUtils::DecodeClientKey(clientKey, mUpdateClientKey); NS_ENSURE_SUCCESS(rv, rv); - LOG(("clientKey present, marking update key")); } + // The first stream in an update is the only stream that may request + // forwarded updates. + mPrimaryStream = true; + + SplitTables(tables, mUpdateTables); + return NS_OK; } @@ -496,37 +2968,62 @@ NS_IMETHODIMP nsUrlClassifierDBServiceWorker::BeginStream(const nsACString &table, const nsACString &serverMAC) { - LOG(("nsUrlClassifierDBServiceWorker::BeginStream")); - if (gShuttingDownThread) return NS_ERROR_NOT_INITIALIZED; NS_ENSURE_STATE(mUpdateObserver); NS_ENSURE_STATE(!mInStream); + // We may have committed the update in FinishStream, if so set it up + // again here. + nsresult rv = SetupUpdate(); + if (NS_FAILED(rv)) { + mUpdateStatus = rv; + return rv; + } + mInStream = true; - NS_ASSERTION(!mProtocolParser, "Should not have a protocol parser."); - - mProtocolParser = new ProtocolParser(mHashKey); - if (!mProtocolParser) - return NS_ERROR_OUT_OF_MEMORY; - - mProtocolParser->Init(mCryptoHash); - - nsresult rv; - // If we're expecting a MAC, create the nsICryptoHMAC component now. if (!mUpdateClientKey.IsEmpty()) { - LOG(("Expecting MAC in this stream")); - rv = mProtocolParser->InitHMAC(mUpdateClientKey, serverMAC); - NS_ENSURE_SUCCESS(rv, rv); - } else { - LOG(("No MAC in this stream")); + nsCOMPtr keyObjectFactory(do_GetService( + "@mozilla.org/security/keyobjectfactory;1", &rv)); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to get nsIKeyObjectFactory service"); + mUpdateStatus = rv; + return mUpdateStatus; + } + + nsCOMPtr keyObject; + rv = keyObjectFactory->KeyFromString(nsIKeyObject::HMAC, mUpdateClientKey, + getter_AddRefs(keyObject)); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to create key object, maybe not FIPS compliant?"); + mUpdateStatus = rv; + return mUpdateStatus; + } + + mHMAC = do_CreateInstance(NS_CRYPTO_HMAC_CONTRACTID, &rv); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to create nsICryptoHMAC instance"); + mUpdateStatus = rv; + return mUpdateStatus; + } + + rv = mHMAC->Init(nsICryptoHMAC::SHA1, keyObject); + if (NS_FAILED(rv)) { + NS_WARNING("Failed to initialize nsICryptoHMAC instance"); + mUpdateStatus = rv; + return mUpdateStatus; + } } + mServerMAC = serverMAC; + if (!table.IsEmpty()) { - mProtocolParser->SetCurrentTable(table); + mUpdateTable = table; + GetTableId(mUpdateTable, &mUpdateTableId); + LOG(("update table: '%s' (%d)", mUpdateTable.get(), mUpdateTableId)); } return NS_OK; @@ -573,7 +3070,45 @@ nsUrlClassifierDBServiceWorker::UpdateStream(const nsACString& chunk) HandlePendingLookups(); - return mProtocolParser->AppendStream(chunk); + LOG(("Update from Stream.")); + nsresult rv = OpenDb(); + if (NS_FAILED(rv)) { + NS_ERROR("Unable to open database"); + return NS_ERROR_FAILURE; + } + + // if something has gone wrong during this update, just throw it away + if (NS_FAILED(mUpdateStatus)) { + return mUpdateStatus; + } + + if (mHMAC && !mServerMAC.IsEmpty()) { + rv = mHMAC->Update(reinterpret_cast(chunk.BeginReading()), + chunk.Length()); + if (NS_FAILED(rv)) { + mUpdateStatus = rv; + return mUpdateStatus; + } + } + + LOG(("Got %s\n", PromiseFlatCString(chunk).get())); + + mPendingStreamUpdate.Append(chunk); + + bool done = false; + while (!done) { + if (mState == STATE_CHUNK) { + rv = ProcessChunk(&done); + } else { + rv = ProcessResponseLines(&done); + } + if (NS_FAILED(rv)) { + mUpdateStatus = rv; + return rv; + } + } + + return NS_OK; } NS_IMETHODIMP @@ -585,71 +3120,85 @@ nsUrlClassifierDBServiceWorker::FinishStream() NS_ENSURE_STATE(mInStream); NS_ENSURE_STATE(mUpdateObserver); - mInStream = false; + PRInt32 nextStreamDelay = 0; - mProtocolParser->FinishHMAC(); + if (NS_SUCCEEDED(mUpdateStatus) && mHMAC) { + nsCAutoString clientMAC; + mHMAC->Finish(true, clientMAC); - if (NS_SUCCEEDED(mProtocolParser->Status())) { - if (mProtocolParser->UpdateWait()) { - mUpdateWait = mProtocolParser->UpdateWait(); + if (clientMAC != mServerMAC) { + NS_WARNING("Invalid update MAC!"); + LOG(("Invalid update MAC: expected %s, got %s", + mServerMAC.get(), clientMAC.get())); + mUpdateStatus = NS_ERROR_FAILURE; } - // XXX: Only allow forwards from the initial update? - const nsTArray &forwards = - mProtocolParser->Forwards(); - for (uint32 i = 0; i < forwards.Length(); i++) { - const ProtocolParser::ForwardedUpdate &forward = forwards[i]; - mUpdateObserver->UpdateUrlRequested(forward.url, forward.table, forward.mac); - } - // Hold on to any TableUpdate objects that were created by the - // parser. - mTableUpdates.AppendElements(mProtocolParser->GetTableUpdates()); - mProtocolParser->ForgetTableUpdates(); - } else { - mUpdateStatus = mProtocolParser->Status(); - } - mUpdateObserver->StreamFinished(mProtocolParser->Status(), 0); - - // Only reset if MAC was OK - if (NS_SUCCEEDED(mUpdateStatus)) { - if (mProtocolParser->ResetRequested()) { - mClassifier->Reset(); + PRIntervalTime updateTime = PR_IntervalNow() - mUpdateStartTime; + if (PR_IntervalToSeconds(updateTime) >= + static_cast(gWorkingTimeThreshold)) { + // We've spent long enough working that we should commit what we + // have and hold off for a bit. + nsresult rv = ApplyUpdate(); + if (NS_FAILED(rv)) { + if (rv == NS_ERROR_FILE_CORRUPTED) { + ResetDatabase(); + } + return rv; + } + nextStreamDelay = gDelayTime * 1000; } } - // Rekey will cause update to fail (can't check MACs) - if (mProtocolParser->RekeyRequested()) { - mUpdateObserver->RekeyRequested(); - } - mProtocolParser = nsnull; + mUpdateObserver->StreamFinished(mUpdateStatus, + static_cast(nextStreamDelay)); + + ResetStream(); + return NS_OK; } -NS_IMETHODIMP -nsUrlClassifierDBServiceWorker::FinishUpdate() +nsresult +nsUrlClassifierDBServiceWorker::SetCacheSize( + mozIStorageConnection * aConnection, PRInt32 aCacheSize) { - if (gShuttingDownThread) - return NS_ERROR_NOT_INITIALIZED; - NS_ENSURE_STATE(mUpdateObserver); + mozStorageStatementScoper scoper(mGetPageSizeStatement); + bool hasResult; + nsresult rv = mGetPageSizeStatement->ExecuteStep(&hasResult); + NS_ENSURE_SUCCESS(rv, rv); - if (NS_SUCCEEDED(mUpdateStatus)) { - mUpdateStatus = ApplyUpdate(); + NS_ASSERTION(hasResult, "Should always be able to get page size from sqlite"); + PRUint32 pageSize = mGetPageSizeStatement->AsInt32(0); + PRUint32 cachePages = aCacheSize / pageSize; + nsCAutoString cacheSizePragma(MOZ_STORAGE_UNIQUIFY_QUERY_STR + "PRAGMA cache_size="); + cacheSizePragma.AppendInt(cachePages); + rv = aConnection->ExecuteSimpleSQL(cacheSizePragma); + NS_ENSURE_SUCCESS(rv, rv); + + return NS_OK; +} + +nsresult +nsUrlClassifierDBServiceWorker::SetupUpdate() +{ + LOG(("nsUrlClassifierDBServiceWorker::SetupUpdate")); + bool inProgress; + nsresult rv = mConnection->GetTransactionInProgress(&inProgress); + if (inProgress) { + return NS_OK; } - mMissCache.Clear(); + mUpdateStartTime = PR_IntervalNow(); - if (NS_SUCCEEDED(mUpdateStatus)) { - LOG(("Notifying success: %d", mUpdateWait)); - mUpdateObserver->UpdateSuccess(mUpdateWait); - } else { - LOG(("Notifying error: %d", mUpdateStatus)); - mUpdateObserver->UpdateError(mUpdateStatus); - /* - * mark the tables as spoiled, we don't want to block hosts - * longer than normal because our update failed - */ - mClassifier->MarkSpoiled(mUpdateTables); + rv = mConnection->BeginTransaction(); + NS_ENSURE_SUCCESS(rv, rv); + + if (gUpdateCacheSize > 0) { + rv = SetCacheSize(mConnection, gUpdateCacheSize); + NS_ENSURE_SUCCESS(rv, rv); + if (gUpdateCacheSize != gLookupCacheSize) { + mGrewCache = true; + } } - mUpdateObserver = nsnull; return NS_OK; } @@ -657,46 +3206,139 @@ nsUrlClassifierDBServiceWorker::FinishUpdate() nsresult nsUrlClassifierDBServiceWorker::ApplyUpdate() { - LOG(("nsUrlClassifierDBServiceWorker::ApplyUpdate()")); - return mClassifier->ApplyUpdates(&mTableUpdates); + LOG(("nsUrlClassifierDBServiceWorker::ApplyUpdate")); + + if (mConnection) { + if (NS_FAILED(mUpdateStatus)) { + mConnection->RollbackTransaction(); + } else { + mUpdateStatus = FlushChunkLists(); + if (NS_SUCCEEDED(mUpdateStatus)) { + mUpdateStatus = mConnection->CommitTransaction(); + } + } + } + + if (NS_SUCCEEDED(mUpdateStatus)) { + // Reconstruct the prefix tree from the DB + nsresult rv = ConstructPrefixSet(); + NS_ENSURE_SUCCESS(rv, rv); + } + + if (mGrewCache) { + // During the update we increased the page cache to bigger than we + // want to keep around. At the moment, the only reliable way to make + // sure that the page cache is freed is to reopen the connection. + LOG(("GrewCache true, reopening DB")); + mGrewCache = false; + CloseDb(); + OpenDb(); + } + + mUpdateStartTime = 0; + + return NS_OK; +} + +NS_IMETHODIMP +nsUrlClassifierDBServiceWorker::FinishUpdate() +{ + LOG(("nsUrlClassifierDBServiceWorker::FinishUpdate()")); + if (gShuttingDownThread) + return NS_ERROR_NOT_INITIALIZED; + + NS_ENSURE_STATE(!mInStream); + NS_ENSURE_STATE(mUpdateObserver); + + // We need to get the error code before ApplyUpdate, because it might + // close/open the connection. + PRInt32 errcode = SQLITE_OK; + if (mConnection) + mConnection->GetLastError(&errcode); + + nsresult rv = ApplyUpdate(); + if (NS_FAILED(rv)) { + if (rv == NS_ERROR_FILE_CORRUPTED) { + ResetDatabase(); + } + return rv; + } + + if (NS_SUCCEEDED(mUpdateStatus)) { + mUpdateObserver->UpdateSuccess(mUpdateWait); + } else { + mUpdateObserver->UpdateError(mUpdateStatus); + } + + // It's important that we only reset the database on an update + // command if the update was successful, otherwise unauthenticated + // updates could cause a database reset. + bool resetDB = (NS_SUCCEEDED(mUpdateStatus) && mResetRequested) || + errcode == SQLITE_CORRUPT; + + if (!resetDB) { + if (NS_SUCCEEDED(mUpdateStatus)) { + PRInt64 now = (PR_Now() / PR_USEC_PER_SEC); + for (PRUint32 i = 0; i < mUpdateTables.Length(); i++) { + LOG(("Successfully updated %s", mUpdateTables[i].get())); + mTableFreshness.Put(mUpdateTables[i], now); + } + } else { + for (PRUint32 i = 0; i < mUpdateTables.Length(); i++) { + LOG(("Failed updating %s", mUpdateTables[i].get())); + mTableFreshness.Remove(mUpdateTables[i]); + } + } + } + + ResetUpdate(); + + if (resetDB) { + ResetDatabase(); + } + + return NS_OK; } NS_IMETHODIMP nsUrlClassifierDBServiceWorker::ResetDatabase() { - nsresult rv = OpenDb(); + LOG(("nsUrlClassifierDBServiceWorker::ResetDatabase [%p]", this)); + ClearCachedChunkLists(); + + mTableFreshness.Clear(); + + nsresult rv = CloseDb(); NS_ENSURE_SUCCESS(rv, rv); - mClassifier->Reset(); - - rv = CloseDb(); + rv = mPrefixSet->SetPrefixes(nsnull, 0); NS_ENSURE_SUCCESS(rv, rv); + mDBFile->Remove(false); + mPSFile->Remove(false); + return NS_OK; } NS_IMETHODIMP nsUrlClassifierDBServiceWorker::CancelUpdate() { - LOG(("nsUrlClassifierDBServiceWorker::CancelUpdate")); + LOG(("CancelUpdate")); if (mUpdateObserver) { - LOG(("UpdateObserver exists, cancelling")); - mUpdateStatus = NS_BINDING_ABORTED; + ClearCachedChunkLists(); + mConnection->RollbackTransaction(); mUpdateObserver->UpdateError(mUpdateStatus); - /* - * mark the tables as spoiled, we don't want to block hosts - * longer than normal because our update failed - */ - mClassifier->MarkSpoiled(mUpdateTables); + for (PRUint32 i = 0; i < mUpdateTables.Length(); i++) { + LOG(("Failed updating %s", mUpdateTables[i].get())); + mTableFreshness.Remove(mUpdateTables[i]); + } ResetStream(); ResetUpdate(); - } else { - LOG(("No UpdateObserver, nothing to cancel")); } return NS_OK; @@ -709,75 +3351,48 @@ nsUrlClassifierDBServiceWorker::CancelUpdate() NS_IMETHODIMP nsUrlClassifierDBServiceWorker::CloseDb() { - if (mClassifier) { - mClassifier->Close(); - mClassifier = nsnull; + if (mConnection) { + mMainStore.Close(); + mPendingSubStore.Close(); + + mGetChunkListsStatement = nsnull; + mSetChunkListsStatement = nsnull; + + mGetTablesStatement = nsnull; + mGetTableIdStatement = nsnull; + mGetTableNameStatement = nsnull; + mInsertTableIdStatement = nsnull; + mGetPageSizeStatement = nsnull; + + mConnection = nsnull; + LOG(("urlclassifier db closed\n")); } mCryptoHash = nsnull; - LOG(("urlclassifier db closed\n")); return NS_OK; } NS_IMETHODIMP -nsUrlClassifierDBServiceWorker::CacheCompletions(CacheResultArray *results) +nsUrlClassifierDBServiceWorker::CacheCompletions(nsTArray *results) { LOG(("nsUrlClassifierDBServiceWorker::CacheCompletions [%p]", this)); - if (!mClassifier) - return NS_OK; - // Ownership is transferred in to us - nsAutoPtr resultsPtr(results); + nsAutoPtr > resultsPtr(results); - nsAutoPtr pParse(new ProtocolParser(mHashKey)); - nsTArray updates; + // Start a new transaction. If a transaction is open for an update + // this will be a noop, and this cache will be included in the + // update's transaction. + mozStorageTransaction trans(mConnection, true); - // Only cache results for tables that we have, don't take - // in tables we might accidentally have hit during a completion. - // This happens due to goog vs googpub lists existing. - nsTArray tables; - nsresult rv = mClassifier->ActiveTables(tables); - NS_ENSURE_SUCCESS(rv, rv); + for (PRUint32 i = 0; i < results->Length(); i++) { + nsUrlClassifierLookupResult& result = results->ElementAt(i); + // Failing to update here shouldn't be fatal (and might be common, + // if we're updating entries that were removed since they were + // returned after a lookup). + mMainStore.UpdateEntry(result.mEntry); + } - for (PRUint32 i = 0; i < resultsPtr->Length(); i++) { - bool activeTable = false; - for (PRUint32 table = 0; table < tables.Length(); table++) { - if (tables[table].Equals(resultsPtr->ElementAt(i).table)) { - activeTable = true; - } - } - if (activeTable) { - TableUpdate * tu = pParse->GetTableUpdate(resultsPtr->ElementAt(i).table); - LOG(("CacheCompletion Addchunk %d hash %X", resultsPtr->ElementAt(i).entry.addChunk, - resultsPtr->ElementAt(i).entry.hash.prefix)); - tu->NewAddComplete(resultsPtr->ElementAt(i).entry.addChunk, - resultsPtr->ElementAt(i).entry.hash.complete); - tu->NewAddChunk(resultsPtr->ElementAt(i).entry.addChunk); - tu->SetLocalUpdate(); - updates.AppendElement(tu); - pParse->ForgetTableUpdates(); - } else { - LOG(("Completion received, but table is not active, so not caching.")); - } - } - - mClassifier->ApplyUpdates(&updates); - return NS_OK; -} - -NS_IMETHODIMP -nsUrlClassifierDBServiceWorker::CacheMisses(PrefixArray *results) -{ - LOG(("nsUrlClassifierDBServiceWorker::CacheMisses [%p] %d", - this, results->Length())); - - // Ownership is transferred in to us - nsAutoPtr resultsPtr(results); - - for (PRUint32 i = 0; i < resultsPtr->Length(); i++) { - mMissCache.AppendElement(resultsPtr->ElementAt(i)); - } return NS_OK; } @@ -785,33 +3400,462 @@ nsresult nsUrlClassifierDBServiceWorker::OpenDb() { // Connection already open, don't do anything. - if (mClassifier) { + if (mConnection) { return NS_OK; } - LOG(("Opening db")); + LOG(("Opening db\n")); - nsAutoPtr classifier(new Classifier()); - if (!classifier) { - return NS_ERROR_OUT_OF_MEMORY; + nsresult rv; + // open the connection + nsCOMPtr storageService = + do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + bool exists; + rv = mDBFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + bool newDB = !exists; + + nsCOMPtr connection; + rv = storageService->OpenDatabase(mDBFile, getter_AddRefs(connection)); + if (rv == NS_ERROR_FILE_CORRUPTED) { + // delete the db and try opening again + rv = mDBFile->Remove(false); + NS_ENSURE_SUCCESS(rv, rv); + + newDB = true; + + rv = storageService->OpenDatabase(mDBFile, getter_AddRefs(connection)); + } + NS_ENSURE_SUCCESS(rv, rv); + + if (!newDB) { + PRInt32 databaseVersion; + rv = connection->GetSchemaVersion(&databaseVersion); + NS_ENSURE_SUCCESS(rv, rv); + + if (databaseVersion != IMPLEMENTATION_VERSION) { + LOG(("Incompatible database, removing.")); + + rv = connection->Close(); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mDBFile->Remove(false); + NS_ENSURE_SUCCESS(rv, rv); + + newDB = true; + + rv = storageService->OpenDatabase(mDBFile, getter_AddRefs(connection)); + NS_ENSURE_SUCCESS(rv, rv); + } } - classifier->SetFreshTime(gFreshnessGuarantee); + connection->SetGrowthIncrement(5 * 1024 * 1024, EmptyCString()); + rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous=OFF")); + NS_ENSURE_SUCCESS(rv, rv); - nsresult rv = classifier->Open(*mCacheDir); - if (NS_FAILED(rv)) { - NS_WARNING("Failed to open URL classifier."); + rv = connection->CreateStatement + (NS_LITERAL_CSTRING(MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size"), + getter_AddRefs(mGetPageSizeStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = SetCacheSize(connection, gLookupCacheSize); + NS_ENSURE_SUCCESS(rv, rv); + + if (newDB) { + rv = connection->SetSchemaVersion(IMPLEMENTATION_VERSION); + NS_ENSURE_SUCCESS(rv, rv); } - mHashKey = classifier->GetHashKey(); - mClassifier = classifier; + // Create the table + rv = MaybeCreateTables(connection); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mMainStore.Init(this, connection, + NS_LITERAL_CSTRING("moz_classifier")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = mPendingSubStore.Init(this, connection, + NS_LITERAL_CSTRING("moz_subs")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->CreateStatement + (NS_LITERAL_CSTRING("SELECT add_chunks, sub_chunks FROM moz_tables" + " WHERE id=?1"), + getter_AddRefs(mGetChunkListsStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->CreateStatement + (NS_LITERAL_CSTRING("UPDATE moz_tables" + " SET add_chunks=?1, sub_chunks=?2" + " WHERE id=?3"), + getter_AddRefs(mSetChunkListsStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->CreateStatement + (NS_LITERAL_CSTRING("SELECT name, add_chunks, sub_chunks" + " FROM moz_tables"), + getter_AddRefs(mGetTablesStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->CreateStatement + (NS_LITERAL_CSTRING("SELECT id FROM moz_tables" + " WHERE name = ?1"), + getter_AddRefs(mGetTableIdStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->CreateStatement + (NS_LITERAL_CSTRING("SELECT name FROM moz_tables" + " WHERE id = ?1"), + getter_AddRefs(mGetTableNameStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->CreateStatement + (NS_LITERAL_CSTRING("INSERT INTO moz_tables(id, name, add_chunks, sub_chunks)" + " VALUES (null, ?1, null, null)"), + getter_AddRefs(mInsertTableIdStatement)); + NS_ENSURE_SUCCESS(rv, rv); + + mConnection = connection; mCryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); + LOG(("loading Prefix Set\n")); + rv = LoadPrefixSet(mPSFile); + if (NS_FAILED(rv)) { + if (rv == NS_ERROR_FILE_CORRUPTED) { + ResetDatabase(); + } + return rv; + } + return NS_OK; } +// We have both a prefix and a domain. Drop the domain, but +// hash the domain, the prefix and a random value together, +// ensuring any collisions happens at a different points for +// different users. +// We need to calculate +- 500k hashes each update. +// The extensive initialization and finalization of normal +// cryptographic hashes, as well as fairly low speed, causes them +// to be prohibitively slow here, hence we can't use them. +// We use MurmurHash3 instead because it's reasonably well +// researched, trusted inside some other big projects, extremely +// fast and with a specific a 32-bit output version, and fairly +// compact. Upon testing with the actual prefix data, it does +// not appear to increase the number of collisions by any +// meaningful amount. +static nsresult KeyedHash(PRUint32 aPref, PRUint32 aDomain, + PRUint32 aKey, PRUint32 *aOut) +{ + // This is a reimplementation of MurmurHash3 32-bit + // based on the public domain C++ sources. + // http://code.google.com/p/smhasher/source/browse/trunk/MurmurHash3.cpp + // for nblocks = 2 + PRUint32 c1 = 0xCC9E2D51; + PRUint32 c2 = 0x1B873593; + PRUint32 c3 = 0xE6546B64; + PRUint32 c4 = 0x85EBCA6B; + PRUint32 c5 = 0xC2B2AE35; + PRUint32 h1 = aPref; // seed + PRUint32 k1; + PRUint32 karr[2]; + + karr[0] = aDomain; + karr[1] = aKey; + + for (PRUint32 i = 0; i < 2; i++) { + k1 = karr[i]; + k1 *= c1; + k1 = (k1 << 15) | (k1 >> (32-15)); + k1 *= c2; + + h1 ^= k1; + h1 = (h1 << 13) | (h1 >> (32-13)); + h1 *= 5; + h1 += c3; + } + + h1 ^= 2; // len + // fmix + h1 ^= h1 >> 16; + h1 *= c4; + h1 ^= h1 >> 13; + h1 *= c5; + h1 ^= h1 >> 16; + + *aOut = h1; + + return NS_OK; +} + +nsresult nsUrlClassifierStore::ReadPrefixes(FallibleTArray& array, + PRUint32 aKey) +{ + mozStorageStatementScoper scoper(mAllPrefixGetStatement); + mozStorageStatementScoper scoperToo(mAllPrefixCountStatement); + bool hasMoreData; + PRUint32 pcnt = 0; + PRUint32 fcnt = 0; + +#if defined(PR_LOGGING) + PRIntervalTime clockStart = 0; + if (LOG_ENABLED()) { + clockStart = PR_IntervalNow(); + } +#endif + + // Make sure we allocate no more than we really need, so first + // check how much entries there are + if (NS_SUCCEEDED(mAllPrefixCountStatement->ExecuteStep(&hasMoreData)) && hasMoreData) { + PRUint32 count = mAllPrefixCountStatement->AsInt32(0); + if (!array.SetCapacity(count)) { + return NS_ERROR_OUT_OF_MEMORY; + } + } else { + return NS_ERROR_FILE_CORRUPTED; + } + + while (NS_SUCCEEDED(mAllPrefixGetStatement->ExecuteStep(&hasMoreData)) && hasMoreData) { + PRUint32 prefixval; + PRUint32 domainval; + PRUint32 size; + + const PRUint8 *blobdomain = mAllPrefixGetStatement->AsSharedBlob(0, &size); + if (!blobdomain || (size != DOMAIN_LENGTH)) + return false; + + domainval = *(reinterpret_cast(blobdomain)); + + const PRUint8 *blobprefix = mAllPrefixGetStatement->AsSharedBlob(1, &size); + if (!blobprefix || (size != PARTIAL_LENGTH)) { + const PRUint8 *blobfull = mAllPrefixGetStatement->AsSharedBlob(2, &size); + if (!blobfull || (size != COMPLETE_LENGTH)) { + prefixval = domainval; + fcnt++; + } else { + prefixval = *(reinterpret_cast(blobfull)); + } + } else { + prefixval = *(reinterpret_cast(blobprefix)); + } + + PRUint32 keyedVal; + nsresult rv = KeyedHash(prefixval, domainval, aKey, &keyedVal); + NS_ENSURE_SUCCESS(rv, rv); + + PRUint32 *res = array.AppendElement(keyedVal); + MOZ_ASSERT(res != nsnull); + pcnt++; + // Normal DB size is about 500k entries. If we are getting 10x + // as much, the database must be corrupted. + if (pcnt > 5000000) { + return NS_ERROR_FILE_CORRUPTED; + } + } + + LOG(("SB prefixes: %d fulldomain: %d\n", pcnt, fcnt)); + +#if defined(PR_LOGGING) + if (LOG_ENABLED()) { + PRIntervalTime clockEnd = PR_IntervalNow(); + LOG(("Gathering took %dms\n", + PR_IntervalToMilliseconds(clockEnd - clockStart))); + } +#endif + + return NS_OK; +} + +bool nsUrlClassifierDBServiceWorker::LockPrefixSet() +{ + mPrefixSetEnabledLock.Lock(); + return mPrefixSetEnabled; +} + +void nsUrlClassifierDBServiceWorker::UnlockPrefixSet() +{ + mPrefixSetEnabledLock.Unlock(); +} + +nsresult +nsUrlClassifierDBServiceWorker::ConstructPrefixSet() +{ + Telemetry::AutoTimer timer; + + PRUint32 key; + nsresult rv = mPrefixSet->GetKey(&key); + NS_ENSURE_SUCCESS(rv, rv); + + FallibleTArray array; + rv = mMainStore.ReadPrefixes(array, key); + if (NS_FAILED(rv)) { + goto error_bailout; + } + +#ifdef HASHFUNCTION_COLLISION_TEST + array.Sort(); + PRUint32 collisions = 0; + for (int i = 1; i < array.Length(); i++) { + if (array[i - 1] == array[i]) { + collisions++; + } + } + LOG(("%d collisions in the set", collisions)); +#endif + + if (array.IsEmpty()) { + // DB is empty, put a sentinel to show that we loaded it + if (!array.AppendElement(0)) { + goto error_bailout; + } + } + // SetPrefixes requires sorted arrays + array.Sort(); + + // construct new prefixset + rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length()); + if (NS_FAILED(rv)) { + goto error_bailout; + } + + // store the new tree to disk + rv = mPrefixSet->StoreToFile(mPSFile); + NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store the prefixset"); + + // re-enable prefixset usage if disabled earlier + mPrefixSetEnabled = true; + + return NS_OK; + + error_bailout: + // disable prefixset usage + MutexAutoLock lock(mPrefixSetEnabledLock); + mPrefixSetEnabled = false; + // load an empty prefixset + nsAutoTArray sentinel; + sentinel.Clear(); + sentinel.AppendElement(0); + mPrefixSet->SetPrefixes(sentinel.Elements(), sentinel.Length()); + if (rv == NS_ERROR_OUT_OF_MEMORY) { + Telemetry::Accumulate(Telemetry::URLCLASSIFIER_PS_OOM, 1); + } + return rv; +} + +nsresult +nsUrlClassifierDBServiceWorker::LoadPrefixSet(nsCOMPtr & aFile) +{ + bool empty; + nsresult rv = mPrefixSet->IsEmpty(&empty); + NS_ENSURE_SUCCESS(rv, rv); + + if (!empty) { + LOG(("PrefixSet already loaded, not loading again")); + return NS_OK; + } + + bool exists; + rv = aFile->Exists(&exists); + NS_ENSURE_SUCCESS(rv, rv); + +#if defined(PR_LOGGING) + PRIntervalTime clockStart = 0; + if (LOG_ENABLED()) { + clockStart = PR_IntervalNow(); + } +#endif + + if (exists) { + Telemetry::AutoTimer timer; + LOG(("stored PrefixSet exists, loading from disk")); + rv = mPrefixSet->LoadFromFile(aFile); + } + if (!exists || NS_FAILED(rv)) { + LOG(("no (usable) stored PrefixSet found, constructing from store")); + rv = ConstructPrefixSet(); + NS_ENSURE_SUCCESS(rv, rv); + } + +#ifdef DEBUG + LOG(("SB tree done, size = %d bytes\n", + mPrefixSet->SizeOfIncludingThis(moz_malloc_size_of))); +#endif +#if defined(PR_LOGGING) + if (LOG_ENABLED()) { + PRIntervalTime clockEnd = PR_IntervalNow(); + LOG(("Loading took %dms\n", + PR_IntervalToMilliseconds(clockEnd - clockStart))); + } +#endif + + return NS_OK; +} + +nsresult +nsUrlClassifierDBServiceWorker::MaybeCreateTables(mozIStorageConnection* connection) +{ + LOG(("MaybeCreateTables\n")); + + nsresult rv = connection->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_classifier" + " (id INTEGER PRIMARY KEY," + " domain BLOB," + " partial_data BLOB," + " complete_data BLOB," + " chunk_id INTEGER," + " table_id INTEGER)")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS" + " moz_classifier_domain_index" + " ON moz_classifier(domain)")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS" + " moz_classifier_chunk_index" + " ON moz_classifier(chunk_id)")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_subs" + " (id INTEGER PRIMARY KEY," + " domain BLOB," + " partial_data BLOB," + " complete_data BLOB," + " chunk_id INTEGER," + " table_id INTEGER," + " add_chunk_id INTEGER)")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS" + " moz_subs_addchunk_index" + " ON moz_subs(add_chunk_id)")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE INDEX IF NOT EXISTS" + " moz_subs_chunk_index" + " ON moz_subs(chunk_id)")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = connection->ExecuteSimpleSQL( + NS_LITERAL_CSTRING("CREATE TABLE IF NOT EXISTS moz_tables" + " (id INTEGER PRIMARY KEY," + " name TEXT," + " add_chunks TEXT," + " sub_chunks TEXT);")); + NS_ENSURE_SUCCESS(rv, rv); + + return rv; +} + // ------------------------------------------------------------------------- // nsUrlClassifierLookupCallback // @@ -835,16 +3879,14 @@ public: , mCallback(c) {} - ~nsUrlClassifierLookupCallback(); - private: nsresult HandleResults(); nsRefPtr mDBService; - nsAutoPtr mResults; + nsAutoPtr > mResults; // Completed results to send back to the worker for caching. - nsAutoPtr mCacheResults; + nsAutoPtr > mCacheResults; PRUint32 mPendingCompletions; nsCOMPtr mCallback; @@ -854,18 +3896,8 @@ NS_IMPL_THREADSAFE_ISUPPORTS2(nsUrlClassifierLookupCallback, nsIUrlClassifierLookupCallback, nsIUrlClassifierHashCompleterCallback) -nsUrlClassifierLookupCallback::~nsUrlClassifierLookupCallback() -{ - nsCOMPtr thread; - (void)NS_GetMainThread(getter_AddRefs(thread)); - - if (mCallback) { - (void)NS_ProxyRelease(thread, mCallback, false); - } -} - NS_IMETHODIMP -nsUrlClassifierLookupCallback::LookupComplete(nsTArray* results) +nsUrlClassifierLookupCallback::LookupComplete(nsTArray* results) { NS_ASSERTION(mResults == nsnull, "Should only get one set of results per nsUrlClassifierLookupCallback!"); @@ -876,19 +3908,22 @@ nsUrlClassifierLookupCallback::LookupComplete(nsTArray* results) } mResults = results; + mResults->Sort(); // Check the results entries that need to be completed. for (PRUint32 i = 0; i < results->Length(); i++) { - LookupResult& result = results->ElementAt(i); + nsUrlClassifierLookupResult& result = results->ElementAt(i); // We will complete partial matches and matches that are stale. - if (!result.Confirmed()) { + if (!result.mConfirmed) { nsCOMPtr completer; if (mDBService->GetCompleter(result.mTableName, getter_AddRefs(completer))) { nsCAutoString partialHash; - partialHash.Assign(reinterpret_cast(&result.hash.prefix), - PREFIX_SIZE); + PRUint8 *buf = + result.mEntry.mHavePartial ? result.mEntry.mPartialHash.buf + : result.mEntry.mCompleteHash.buf; + partialHash.Assign(reinterpret_cast(buf), PARTIAL_LENGTH); nsresult rv = completer->Complete(partialHash, this); if (NS_SUCCEEDED(rv)) { @@ -896,9 +3931,12 @@ nsUrlClassifierLookupCallback::LookupComplete(nsTArray* results) } } else { // For tables with no hash completer, a complete hash match is - // good enough, we'll consider it fresh. - if (result.Complete()) { - result.mFresh = true; + // good enough, it doesn't need to be fresh. (we need the + // mLookupFragment comparison to weed out noise entries, which + // should never be confirmed). + if (result.mEntry.mHaveComplete + && (result.mLookupFragment == result.mEntry.mCompleteHash)) { + result.mConfirmed = true; } else { NS_WARNING("Partial match in a table without a valid completer, ignoring partial match."); } @@ -926,6 +3964,12 @@ nsUrlClassifierLookupCallback::CompletionFinished(nsresult status) mPendingCompletions--; if (mPendingCompletions == 0) { HandleResults(); + + if (mCacheResults) { + // This hands ownership of the cache results array back to the worker + // thread. + mDBService->CacheCompletions(mCacheResults.forget()); + } } return NS_OK; @@ -939,33 +3983,51 @@ nsUrlClassifierLookupCallback::Completion(const nsACString& completeHash, { LOG(("nsUrlClassifierLookupCallback::Completion [%p, %s, %d, %d]", this, PromiseFlatCString(tableName).get(), chunkId, verified)); - mozilla::safebrowsing::Completion hash; + nsUrlClassifierCompleteHash hash; hash.Assign(completeHash); - // Send this completion to the store for caching. - if (!mCacheResults) { - mCacheResults = new CacheResultArray(); - if (!mCacheResults) - return NS_ERROR_OUT_OF_MEMORY; - } - - if (verified) { - CacheResult result; - result.entry.addChunk = chunkId; - result.entry.hash.complete = hash; - result.table = tableName; - - // OK if this fails, we just won't cache the item. - mCacheResults->AppendElement(result); - } - - // Check if this matched any of our results. for (PRUint32 i = 0; i < mResults->Length(); i++) { - LookupResult& result = mResults->ElementAt(i); + nsUrlClassifierLookupResult& result = mResults->ElementAt(i); + + // First, see if this result can be used to update an entry. + if (verified && + !result.mEntry.mHaveComplete && + hash.StartsWith(result.mEntry.mPartialHash) && + result.mTableName == tableName && + result.mEntry.mChunkId == chunkId) { + // We have a completion for this entry. Fill it in... + result.mEntry.SetHash(hash); + + if (!mCacheResults) { + mCacheResults = new nsTArray(); + if (!mCacheResults) + return NS_ERROR_OUT_OF_MEMORY; + } + + mCacheResults->AppendElement(result); + } // Now, see if it verifies a lookup - if (result.CompleteHash() == hash && result.mTableName.Equals(tableName)) { - result.mProtocolConfirmed = true; + if (result.mLookupFragment == hash) { + result.mConfirmed = true; + + if (result.mTableName != tableName || + result.mEntry.mChunkId != chunkId) { + // The hash we got for this completion matches the hash we + // looked up, but doesn't match the table/chunk id. This could + // happen in rare cases where a given URL was moved between + // lists or added/removed/re-added to the list in the time since + // we've updated. + // + // Update the lookup result, but don't update the entry or try + // cache the results of this completion, as it might confuse + // things. + result.mTableName = tableName; + NS_WARNING("Accepting a gethash with an invalid table name or chunk id"); + LOG(("Tablename: %s ?= %s, ChunkId %d ?= %d", + result.mTableName.get(), PromiseFlatCString(tableName).get(), + result.mEntry.mChunkId, chunkId)); + } } } @@ -980,56 +4042,29 @@ nsUrlClassifierLookupCallback::HandleResults() return mCallback->HandleEvent(NS_LITERAL_CSTRING("")); } - nsTArray tables; // Build a stringified list of result tables. + mResults->Sort(); + PRUint32 lastTableId = 0; + nsCAutoString tables; for (PRUint32 i = 0; i < mResults->Length(); i++) { - LookupResult& result = mResults->ElementAt(i); - + nsUrlClassifierLookupResult& result = mResults->ElementAt(i); // Leave out results that weren't confirmed, as their existence on // the list can't be verified. Also leave out randomly-generated // noise. - if (!result.Confirmed() || result.mNoise) { - LOG(("Skipping result from table %s", result.mTableName.get())); + if (!result.mConfirmed || result.mNoise) continue; + + if (tables.Length() > 0) { + if (lastTableId == result.mEntry.mTableId) + continue; + tables.Append(","); } - LOG(("Confirmed result from table %s", result.mTableName.get())); - - if (tables.IndexOf(result.mTableName) == nsTArray::NoIndex) { - tables.AppendElement(result.mTableName); - } + tables.Append(result.mTableName); + lastTableId = result.mEntry.mTableId; } - // Some parts of this gethash request generated no hits at all. - // Prefixes must have been removed from the database since our last update. - // Save the prefixes we checked to prevent repeated requests - // until the next update. - nsAutoPtr cacheMisses(new PrefixArray()); - if (cacheMisses) { - for (uint32 i = 0; i < mResults->Length(); i++) { - LookupResult &result = mResults->ElementAt(i); - if (!result.Confirmed()) { - cacheMisses->AppendElement(result.PrefixHash()); - } - } - // Hands ownership of the miss array back to the worker thread. - mDBService->CacheMisses(cacheMisses.forget()); - } - - if (mCacheResults) { - // This hands ownership of the cache results array back to the worker - // thread. - mDBService->CacheCompletions(mCacheResults.forget()); - } - - nsCAutoString tableStr; - for (PRUint32 i = 0; i < tables.Length(); i++) { - if (i != 0) - tableStr.Append(','); - tableStr.Append(tables[i]); - } - - return mCallback->HandleEvent(tableStr); + return mCallback->HandleEvent(tables); } @@ -1145,7 +4180,18 @@ nsUrlClassifierDBService::Init() gUrlClassifierDbServiceLog = PR_NewLogModule("UrlClassifierDbService"); #endif + // Force the storage service to be created on the main thread. nsresult rv; + nsCOMPtr storageService = + do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + // Force PSM to be loaded on the main thread. + mHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + mPrefixSet = new nsUrlClassifierPrefixSet(); + NS_ENSURE_SUCCESS(rv, rv); // Should we check document loads for malware URIs? nsCOMPtr prefs = do_GetService(NS_PREFSERVICE_CONTRACTID); @@ -1179,19 +4225,20 @@ nsUrlClassifierDBService::Init() PR_ATOMIC_SET(&gFreshnessGuarantee, NS_SUCCEEDED(rv) ? tmpint : CONFIRM_AGE_DEFAULT_SEC); prefs->AddObserver(CONFIRM_AGE_PREF, this, false); - } - // Force PSM loading on main thread - nsCOMPtr acryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); + rv = prefs->GetIntPref(UPDATE_CACHE_SIZE_PREF, &tmpint); + PR_ATOMIC_SET(&gUpdateCacheSize, NS_SUCCEEDED(rv) ? tmpint : UPDATE_CACHE_SIZE_DEFAULT); - // Directory providers must also be accessed on the main thread. - nsCOMPtr cacheDir; - rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_LOCAL_50_DIR, - getter_AddRefs(cacheDir)); - if (NS_FAILED(rv)) { - rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR, - getter_AddRefs(cacheDir)); + rv = prefs->GetIntPref(LOOKUP_CACHE_SIZE_PREF, &tmpint); + PR_ATOMIC_SET(&gLookupCacheSize, NS_SUCCEEDED(rv) ? tmpint : LOOKUP_CACHE_SIZE_DEFAULT); + + rv = prefs->GetIntPref(UPDATE_WORKING_TIME, &tmpint); + PR_ATOMIC_SET(&gWorkingTimeThreshold, + NS_SUCCEEDED(rv) ? tmpint : UPDATE_WORKING_TIME_DEFAULT); + + rv = prefs->GetIntPref(UPDATE_DELAY_TIME, &tmpint); + PR_ATOMIC_SET(&gDelayTime, + NS_SUCCEEDED(rv) ? tmpint : UPDATE_DELAY_TIME_DEFAULT); } // Start the background thread. @@ -1203,7 +4250,7 @@ nsUrlClassifierDBService::Init() if (!mWorker) return NS_ERROR_OUT_OF_MEMORY; - rv = mWorker->Init(gethashNoise, cacheDir); + rv = mWorker->Init(gethashNoise, mPrefixSet); if (NS_FAILED(rv)) { mWorker = nsnull; return rv; @@ -1292,7 +4339,11 @@ nsUrlClassifierDBService::LookupURI(nsIURI* uri, if (forceLookup) { *didLookup = true; } else { - bool clean = false; + // Check if the URI is clean. If so, we don't need to + // bother queueing up a lookup, we can just return.; + bool clean; + rv = CheckClean(key, &clean); + NS_ENSURE_SUCCESS(rv, rv); if (!clean) { nsCOMPtr permissionManager = @@ -1431,21 +4482,13 @@ nsUrlClassifierDBService::ResetDatabase() } nsresult -nsUrlClassifierDBService::CacheCompletions(CacheResultArray *results) +nsUrlClassifierDBService::CacheCompletions(nsTArray *results) { NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED); return mWorkerProxy->CacheCompletions(results); } -nsresult -nsUrlClassifierDBService::CacheMisses(PrefixArray *results) -{ - NS_ENSURE_TRUE(gDbBackgroundThread, NS_ERROR_NOT_INITIALIZED); - - return mWorkerProxy->CacheMisses(results); -} - bool nsUrlClassifierDBService::GetCompleter(const nsACString &tableName, nsIUrlClassifierHashCompleter **completer) @@ -1488,6 +4531,24 @@ nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic, PRInt32 tmpint; rv = prefs->GetIntPref(CONFIRM_AGE_PREF, &tmpint); PR_ATOMIC_SET(&gFreshnessGuarantee, NS_SUCCEEDED(rv) ? tmpint : CONFIRM_AGE_DEFAULT_SEC); + } else if (NS_LITERAL_STRING(UPDATE_CACHE_SIZE_PREF).Equals(aData)) { + PRInt32 tmpint; + rv = prefs->GetIntPref(UPDATE_CACHE_SIZE_PREF, &tmpint); + PR_ATOMIC_SET(&gUpdateCacheSize, NS_SUCCEEDED(rv) ? tmpint : UPDATE_CACHE_SIZE_DEFAULT); + } else if (NS_LITERAL_STRING(LOOKUP_CACHE_SIZE_PREF).Equals(aData)) { + PRInt32 tmpint; + rv = prefs->GetIntPref(LOOKUP_CACHE_SIZE_PREF, &tmpint); + PR_ATOMIC_SET(&gLookupCacheSize, NS_SUCCEEDED(rv) ? tmpint : LOOKUP_CACHE_SIZE_DEFAULT); + } else if (NS_LITERAL_STRING(UPDATE_WORKING_TIME).Equals(aData)) { + PRInt32 tmpint; + rv = prefs->GetIntPref(UPDATE_WORKING_TIME, &tmpint); + PR_ATOMIC_SET(&gWorkingTimeThreshold, + NS_SUCCEEDED(rv) ? tmpint : UPDATE_WORKING_TIME_DEFAULT); + } else if (NS_LITERAL_STRING(UPDATE_DELAY_TIME).Equals(aData)) { + PRInt32 tmpint; + rv = prefs->GetIntPref(UPDATE_DELAY_TIME, &tmpint); + PR_ATOMIC_SET(&gDelayTime, + NS_SUCCEEDED(rv) ? tmpint : UPDATE_DELAY_TIME_DEFAULT); } } else if (!strcmp(aTopic, "profile-before-change") || !strcmp(aTopic, "xpcom-shutdown-threads")) { diff --git a/toolkit/components/url-classifier/nsUrlClassifierDBService.h b/toolkit/components/url-classifier/nsUrlClassifierDBService.h index 122102de87dc..b49303a41c2b 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierDBService.h +++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h @@ -53,8 +53,6 @@ #include "nsICryptoHash.h" #include "nsICryptoHMAC.h" -#include "LookupCache.h" - // The hash length for a domain key. #define DOMAIN_LENGTH 4 @@ -90,8 +88,7 @@ public: bool GetCompleter(const nsACString& tableName, nsIUrlClassifierHashCompleter** completer); - nsresult CacheCompletions(mozilla::safebrowsing::CacheResultArray *results); - nsresult CacheMisses(mozilla::safebrowsing::PrefixArray *results); + nsresult CacheCompletions(nsTArray *results); static nsIThread* BackgroundThread(); @@ -134,6 +131,10 @@ private: // The list of tables that can use the default hash completer object. nsTArray mGethashWhitelist; + // Set of prefixes known to be in the database + nsRefPtr mPrefixSet; + nsCOMPtr mHash; + // Thread that we do the updates on. static nsIThread* gDbBackgroundThread; }; diff --git a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp index dd528d13a8f9..9bc52e067833 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp @@ -71,7 +71,7 @@ static const PRLogModuleInfo *gUrlClassifierPrefixSetLog = nsnull; class nsPrefixSetReporter : public nsIMemoryReporter { public: - nsPrefixSetReporter(nsUrlClassifierPrefixSet* aParent, const nsACString& aName); + nsPrefixSetReporter(nsUrlClassifierPrefixSet * aParent, const nsACString & aName); virtual ~nsPrefixSetReporter() {}; NS_DECL_ISUPPORTS @@ -79,7 +79,7 @@ public: private: nsCString mPath; - nsUrlClassifierPrefixSet* mParent; + nsUrlClassifierPrefixSet * mParent; }; NS_IMPL_THREADSAFE_ISUPPORTS1(nsPrefixSetReporter, nsIMemoryReporter) @@ -87,8 +87,8 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsPrefixSetReporter, nsIMemoryReporter) NS_MEMORY_REPORTER_MALLOC_SIZEOF_FUN(StoragePrefixSetMallocSizeOf, "storage/prefixset") -nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet* aParent, - const nsACString& aName) +nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet * aParent, + const nsACString & aName) : mParent(aParent) { mPath.Assign(NS_LITERAL_CSTRING("explicit/storage/prefixset")); @@ -99,42 +99,42 @@ nsPrefixSetReporter::nsPrefixSetReporter(nsUrlClassifierPrefixSet* aParent, } NS_IMETHODIMP -nsPrefixSetReporter::GetProcess(nsACString& aProcess) +nsPrefixSetReporter::GetProcess(nsACString & aProcess) { aProcess.Truncate(); return NS_OK; } NS_IMETHODIMP -nsPrefixSetReporter::GetPath(nsACString& aPath) +nsPrefixSetReporter::GetPath(nsACString & aPath) { aPath.Assign(mPath); return NS_OK; } NS_IMETHODIMP -nsPrefixSetReporter::GetKind(PRInt32* aKind) +nsPrefixSetReporter::GetKind(PRInt32 * aKind) { *aKind = nsIMemoryReporter::KIND_HEAP; return NS_OK; } NS_IMETHODIMP -nsPrefixSetReporter::GetUnits(PRInt32* aUnits) +nsPrefixSetReporter::GetUnits(PRInt32 * aUnits) { *aUnits = nsIMemoryReporter::UNITS_BYTES; return NS_OK; } NS_IMETHODIMP -nsPrefixSetReporter::GetAmount(PRInt64* aAmount) +nsPrefixSetReporter::GetAmount(PRInt64 * aAmount) { *aAmount = mParent->SizeOfIncludingThis(StoragePrefixSetMallocSizeOf); return NS_OK; } NS_IMETHODIMP -nsPrefixSetReporter::GetDescription(nsACString& aDescription) +nsPrefixSetReporter::GetDescription(nsACString & aDescription) { aDescription.Assign(NS_LITERAL_CSTRING("Memory used by a PrefixSet for " "UrlClassifier, in bytes.")); @@ -146,21 +146,21 @@ NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSe nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet() : mPrefixSetLock("mPrefixSetLock"), mSetIsReady(mPrefixSetLock, "mSetIsReady"), - mHasPrefixes(false) + mHasPrefixes(false), + mRandomKey(0) { #if defined(PR_LOGGING) if (!gUrlClassifierPrefixSetLog) gUrlClassifierPrefixSetLog = PR_NewLogModule("UrlClassifierPrefixSet"); #endif -} -NS_IMETHODIMP -nsUrlClassifierPrefixSet::Init(const nsACString& aName) -{ - mReporter = new nsPrefixSetReporter(this, aName); + nsresult rv = InitKey(); + if (NS_FAILED(rv)) { + LOG(("Failed to initialize PrefixSet")); + } + + mReporter = new nsPrefixSetReporter(this, NS_LITERAL_CSTRING("all")); NS_RegisterMemoryReporter(mReporter); - - return NS_OK; } nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet() @@ -168,8 +168,26 @@ nsUrlClassifierPrefixSet::~nsUrlClassifierPrefixSet() NS_UnregisterMemoryReporter(mReporter); } +nsresult +nsUrlClassifierPrefixSet::InitKey() +{ + nsCOMPtr rg = + do_GetService("@mozilla.org/security/random-generator;1"); + NS_ENSURE_STATE(rg); + + PRUint8 *temp; + nsresult rv = rg->GenerateRandomBytes(sizeof(mRandomKey), &temp); + NS_ENSURE_SUCCESS(rv, rv); + memcpy(&mRandomKey, temp, sizeof(mRandomKey)); + NS_Free(temp); + + LOG(("Initialized PrefixSet, key = %X", mRandomKey)); + + return NS_OK; +} + NS_IMETHODIMP -nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32* aArray, PRUint32 aLength) +nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32 * aArray, PRUint32 aLength) { if (aLength <= 0) { MutexAutoLock lock(mPrefixSetLock); @@ -188,7 +206,7 @@ nsUrlClassifierPrefixSet::SetPrefixes(const PRUint32* aArray, PRUint32 aLength) } nsresult -nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32* aPrefixes, PRUint32 aLength) +nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32 * prefixes, PRUint32 aLength) { if (aLength == 0) { return NS_OK; @@ -196,7 +214,7 @@ nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32* aPrefixes, PRUint32 aLen #ifdef DEBUG for (PRUint32 i = 1; i < aLength; i++) { - MOZ_ASSERT(aPrefixes[i] >= aPrefixes[i-1]); + MOZ_ASSERT(prefixes[i] >= prefixes[i-1]); } #endif @@ -204,7 +222,7 @@ nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32* aPrefixes, PRUint32 aLen FallibleTArray newIndexStarts; FallibleTArray newDeltas; - if (!newIndexPrefixes.AppendElement(aPrefixes[0])) { + if (!newIndexPrefixes.AppendElement(prefixes[0])) { return NS_ERROR_OUT_OF_MEMORY; } if (!newIndexStarts.AppendElement(newDeltas.Length())) { @@ -212,25 +230,25 @@ nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32* aPrefixes, PRUint32 aLen } PRUint32 numOfDeltas = 0; - PRUint32 currentItem = aPrefixes[0]; + PRUint32 currentItem = prefixes[0]; for (PRUint32 i = 1; i < aLength; i++) { if ((numOfDeltas >= DELTAS_LIMIT) || - (aPrefixes[i] - currentItem >= MAX_INDEX_DIFF)) { + (prefixes[i] - currentItem >= MAX_INDEX_DIFF)) { if (!newIndexStarts.AppendElement(newDeltas.Length())) { return NS_ERROR_OUT_OF_MEMORY; } - if (!newIndexPrefixes.AppendElement(aPrefixes[i])) { + if (!newIndexPrefixes.AppendElement(prefixes[i])) { return NS_ERROR_OUT_OF_MEMORY; } numOfDeltas = 0; } else { - PRUint16 delta = aPrefixes[i] - currentItem; + PRUint16 delta = prefixes[i] - currentItem; if (!newDeltas.AppendElement(delta)) { return NS_ERROR_OUT_OF_MEMORY; } numOfDeltas++; } - currentItem = aPrefixes[i]; + currentItem = prefixes[i]; } newIndexPrefixes.Compact(); @@ -253,53 +271,6 @@ nsUrlClassifierPrefixSet::MakePrefixSet(const PRUint32* aPrefixes, PRUint32 aLen return NS_OK; } -NS_IMETHODIMP -nsUrlClassifierPrefixSet::GetPrefixes(PRUint32* aCount, - PRUint32** aPrefixes) -{ - NS_ENSURE_ARG_POINTER(aCount); - *aCount = 0; - NS_ENSURE_ARG_POINTER(aPrefixes); - *aPrefixes = nsnull; - - nsTArray aArray; - PRUint32 prefixLength = mIndexPrefixes.Length(); - - for (PRUint32 i = 0; i < prefixLength; i++) { - PRUint32 prefix = mIndexPrefixes[i]; - PRUint32 start = mIndexStarts[i]; - PRUint32 end = (i == (prefixLength - 1)) ? mDeltas.Length() - : mIndexStarts[i + 1]; - aArray.AppendElement(prefix); - for (PRUint32 j = start; j < end; j++) { - prefix += mDeltas[j]; - aArray.AppendElement(prefix); - } - } - - NS_ASSERTION(mIndexStarts.Length() + mDeltas.Length() == aArray.Length(), - "Lengths are inconsistent"); - - PRUint32 itemCount = aArray.Length(); - - if (itemCount == 1 && aArray[0] == 0) { - /* sentinel for empty set */ - aArray.Clear(); - itemCount = 0; - } - - PRUint32* retval = static_cast(nsMemory::Alloc(itemCount * sizeof(PRUint32))); - NS_ENSURE_TRUE(retval, NS_ERROR_OUT_OF_MEMORY); - for (PRUint32 i = 0; i < itemCount; i++) { - retval[i] = aArray[i]; - } - - *aCount = itemCount; - *aPrefixes = retval; - - return NS_OK; -} - PRUint32 nsUrlClassifierPrefixSet::BinSearch(PRUint32 start, PRUint32 end, PRUint32 target) @@ -319,7 +290,7 @@ PRUint32 nsUrlClassifierPrefixSet::BinSearch(PRUint32 start, } nsresult -nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, bool* aFound) +nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, bool * aFound) { mPrefixSetLock.AssertCurrentThreadOwns(); @@ -395,13 +366,32 @@ nsUrlClassifierPrefixSet::IsEmpty(bool * aEmpty) } NS_IMETHODIMP -nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, +nsUrlClassifierPrefixSet::GetKey(PRUint32 * aKey) + { + MutexAutoLock lock(mPrefixSetLock); + *aKey = mRandomKey; + return NS_OK; +} + +NS_IMETHODIMP +nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, PRUint32 aKey, bool* aReady, bool* aFound) { MutexAutoLock lock(mPrefixSetLock); *aFound = false; + // We might have raced here with a LoadPrefixSet call, + // loading a saved PrefixSet with another key than the one used to probe us. + // This must occur exactly between the GetKey call and the Probe call. + // This could cause a false negative immediately after browser start. + // Claim we are still busy loading instead. + if (aKey != mRandomKey) { + LOG(("Potential race condition detected, avoiding")); + *aReady = false; + return NS_OK; + } + // check whether we are opportunistically probing or should wait if (*aReady) { // we should block until we are ready @@ -425,7 +415,7 @@ nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, } nsresult -nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose& fileFd) +nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose & fileFd) { PRUint32 magic; PRInt32 read; @@ -437,6 +427,8 @@ nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose& fileFd) PRUint32 indexSize; PRUint32 deltaSize; + read = PR_Read(fileFd, &mRandomKey, sizeof(PRUint32)); + NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED); read = PR_Read(fileFd, &indexSize, sizeof(PRUint32)); NS_ENSURE_TRUE(read == sizeof(PRUint32), NS_ERROR_FILE_CORRUPTED); read = PR_Read(fileFd, &deltaSize, sizeof(PRUint32)); @@ -489,10 +481,8 @@ nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose& fileFd) } NS_IMETHODIMP -nsUrlClassifierPrefixSet::LoadFromFile(nsIFile* aFile) +nsUrlClassifierPrefixSet::LoadFromFile(nsIFile * aFile) { - Telemetry::AutoTimer timer; - nsresult rv; nsCOMPtr file(do_QueryInterface(aFile, &rv)); NS_ENSURE_SUCCESS(rv, rv); @@ -505,7 +495,7 @@ nsUrlClassifierPrefixSet::LoadFromFile(nsIFile* aFile) } nsresult -nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose& fileFd) +nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose & fileFd) { { Telemetry::AutoTimer timer; @@ -521,6 +511,9 @@ nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose& fileFd) written = PR_Write(fileFd, &magic, sizeof(PRUint32)); NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE); + written = PR_Write(fileFd, &mRandomKey, sizeof(PRUint32)); + NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE); + PRUint32 indexSize = mIndexStarts.Length(); PRUint32 deltaSize = mDeltas.Length(); written = PR_Write(fileFd, &indexSize, sizeof(PRUint32)); @@ -543,7 +536,7 @@ nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose& fileFd) } NS_IMETHODIMP -nsUrlClassifierPrefixSet::StoreToFile(nsIFile* aFile) +nsUrlClassifierPrefixSet::StoreToFile(nsIFile * aFile) { if (!mHasPrefixes) { LOG(("Attempt to serialize empty PrefixSet")); diff --git a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h index 35963462d182..deda71e0feba 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h +++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h @@ -44,7 +44,6 @@ #include "nsISupportsUtils.h" #include "nsID.h" #include "nsIFile.h" -#include "nsIMutableArray.h" #include "nsIUrlClassifierPrefixSet.h" #include "nsIMemoryReporter.h" #include "nsToolkitCompsCID.h" @@ -60,13 +59,12 @@ public: nsUrlClassifierPrefixSet(); virtual ~nsUrlClassifierPrefixSet(); - NS_IMETHOD Init(const nsACString& aName); NS_IMETHOD SetPrefixes(const PRUint32* aArray, PRUint32 aLength); - NS_IMETHOD GetPrefixes(PRUint32* aCount, PRUint32** aPrefixes); - NS_IMETHOD Probe(PRUint32 aPrefix, bool* aReady, bool* aFound); - NS_IMETHOD IsEmpty(bool* aEmpty); + NS_IMETHOD Probe(PRUint32 aPrefix, PRUint32 aKey, bool* aReady, bool* aFound); + NS_IMETHOD IsEmpty(bool * aEmpty); NS_IMETHOD LoadFromFile(nsIFile* aFile); NS_IMETHOD StoreToFile(nsIFile* aFile); + NS_IMETHOD GetKey(PRUint32* aKey); NS_DECL_ISUPPORTS @@ -86,12 +84,15 @@ protected: nsresult Contains(PRUint32 aPrefix, bool* aFound); nsresult MakePrefixSet(const PRUint32* aArray, PRUint32 aLength); PRUint32 BinSearch(PRUint32 start, PRUint32 end, PRUint32 target); - nsresult LoadFromFd(mozilla::AutoFDClose& fileFd); - nsresult StoreToFd(mozilla::AutoFDClose& fileFd); + nsresult LoadFromFd(mozilla::AutoFDClose & fileFd); + nsresult StoreToFd(mozilla::AutoFDClose & fileFd); + nsresult InitKey(); // boolean indicating whether |setPrefixes| has been // called with a non-empty array. bool mHasPrefixes; + // key used to randomize hash collisions + PRUint32 mRandomKey; // the prefix for each index. FallibleTArray mIndexPrefixes; // the value corresponds to the beginning of the run @@ -99,6 +100,7 @@ protected: FallibleTArray mIndexStarts; // array containing deltas from indices. FallibleTArray mDeltas; + }; #endif diff --git a/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp b/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp index 6d9b2732eae3..dabcf59ac38e 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierProxies.cpp @@ -183,7 +183,7 @@ UrlClassifierDBServiceWorkerProxy::CloseDb() } NS_IMETHODIMP -UrlClassifierDBServiceWorkerProxy::CacheCompletions(CacheResultArray * aEntries) +UrlClassifierDBServiceWorkerProxy::CacheCompletions(nsTArray* aEntries) { nsCOMPtr r = new CacheCompletionsRunnable(mTarget, aEntries); return DispatchToWorkerThread(r); @@ -196,27 +196,12 @@ UrlClassifierDBServiceWorkerProxy::CacheCompletionsRunnable::Run() return NS_OK; } -NS_IMETHODIMP -UrlClassifierDBServiceWorkerProxy::CacheMisses(PrefixArray * aEntries) -{ - nsCOMPtr r = new CacheMissesRunnable(mTarget, aEntries); - return DispatchToWorkerThread(r); -} - -NS_IMETHODIMP -UrlClassifierDBServiceWorkerProxy::CacheMissesRunnable::Run() -{ - mTarget->CacheMisses(mEntries); - return NS_OK; -} - - NS_IMPL_THREADSAFE_ISUPPORTS1(UrlClassifierLookupCallbackProxy, nsIUrlClassifierLookupCallback) NS_IMETHODIMP UrlClassifierLookupCallbackProxy::LookupComplete - (LookupResultArray * aResults) + (nsTArray* aResults) { nsCOMPtr r = new LookupCompleteRunnable(mTarget, aResults); return NS_DispatchToMainThread(r); diff --git a/toolkit/components/url-classifier/nsUrlClassifierProxies.h b/toolkit/components/url-classifier/nsUrlClassifierProxies.h index 9cb9303c97f1..843f43a2673d 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierProxies.h +++ b/toolkit/components/url-classifier/nsUrlClassifierProxies.h @@ -40,9 +40,6 @@ #include "nsIUrlClassifierDBService.h" #include "nsThreadUtils.h" -#include "LookupCache.h" - -using namespace mozilla::safebrowsing; /** * Thread proxy from the main thread to the worker thread. @@ -153,7 +150,7 @@ public: { public: CacheCompletionsRunnable(nsIUrlClassifierDBServiceWorker* aTarget, - CacheResultArray *aEntries) + nsTArray* aEntries) : mTarget(aTarget) , mEntries(aEntries) { } @@ -162,23 +159,7 @@ public: private: nsCOMPtr mTarget; - CacheResultArray *mEntries; - }; - - class CacheMissesRunnable : public nsRunnable - { - public: - CacheMissesRunnable(nsIUrlClassifierDBServiceWorker* aTarget, - PrefixArray *aEntries) - : mTarget(aTarget) - , mEntries(aEntries) - { } - - NS_DECL_NSIRUNNABLE - - private: - nsCOMPtr mTarget; - PrefixArray *mEntries; + nsTArray* mEntries; }; private: @@ -201,7 +182,7 @@ public: { public: LookupCompleteRunnable(nsIUrlClassifierLookupCallback* aTarget, - LookupResultArray *aResults) + nsTArray* aResults) : mTarget(aTarget) , mResults(aResults) { } @@ -210,7 +191,7 @@ public: private: nsCOMPtr mTarget; - LookupResultArray * mResults; + nsTArray* mResults; }; private: diff --git a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp index 9f2199e08f63..9466a0e89b7d 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierStreamUpdater.cpp @@ -171,16 +171,11 @@ nsUrlClassifierStreamUpdater::FetchUpdate(const nsACString & aUpdateUrl, const nsACString & aStreamTable, const nsACString & aServerMAC) { - LOG(("(pre) Fetching update from %s\n", PromiseFlatCString(aUpdateUrl).get())); - nsCOMPtr uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), aUpdateUrl); NS_ENSURE_SUCCESS(rv, rv); - nsCAutoString urlSpec; - uri->GetAsciiSpec(urlSpec); - - LOG(("(post) Fetching update from %s\n", urlSpec.get())); + LOG(("Fetching update from %s\n", PromiseFlatCString(aUpdateUrl).get())); return FetchUpdate(uri, aRequestBody, aStreamTable, aServerMAC); } @@ -245,11 +240,6 @@ nsUrlClassifierStreamUpdater::DownloadUpdates( mIsUpdating = true; *_retval = true; - nsCAutoString urlSpec; - mUpdateUrl->GetAsciiSpec(urlSpec); - - LOG(("FetchUpdate: %s", urlSpec.get())); - //LOG(("requestBody: %s", aRequestBody.get())); return FetchUpdate(mUpdateUrl, aRequestBody, EmptyCString(), EmptyCString()); } diff --git a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js index 56e59389d9db..c9cf375cd30d 100644 --- a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js +++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js @@ -24,26 +24,14 @@ prefBranch.setIntPref("urlclassifier.gethashnoise", 0); prefBranch.setBoolPref("browser.safebrowsing.malware.enabled", true); prefBranch.setBoolPref("browser.safebrowsing.enabled", true); -function delFile(name) { +function cleanUp() { try { // Delete a previously created sqlite file var file = dirSvc.get('ProfLD', Ci.nsIFile); - file.append(name); + file.append("urlclassifier3.sqlite"); if (file.exists()) file.remove(false); - } catch(e) { - } -} - -function cleanUp() { - delFile("classifier.hashkey"); - delFile("urlclassifier3.sqlite"); - delFile("safebrowsing/test-phish-simple.sbstore"); - delFile("safebrowsing/test-malware-simple.sbstore"); - delFile("safebrowsing/test-phish-simple.cache"); - delFile("safebrowsing/test-malware-simple.cache"); - delFile("safebrowsing/test-phish-simple.pset"); - delFile("safebrowsing/test-malware-simple.pset"); + } catch (e) {} } var dbservice = Cc["@mozilla.org/url-classifier/dbservice;1"].getService(Ci.nsIUrlClassifierDBService); @@ -288,10 +276,11 @@ function runNextTest() dbservice.resetDatabase(); dbservice.setHashCompleter('test-phish-simple', null); + dumpn("running " + gTests[gNextTest]); - let test = gTests[gNextTest++]; - dump("running " + test.name + "\n"); - test(); + dump("running " + gTests[gNextTest]); + + gTests[gNextTest++](); } function runTests(tests) diff --git a/toolkit/components/url-classifier/tests/unit/test_addsub.js b/toolkit/components/url-classifier/tests/unit/test_addsub.js index 1ed65c7baad5..1303bd399ba4 100644 --- a/toolkit/components/url-classifier/tests/unit/test_addsub.js +++ b/toolkit/components/url-classifier/tests/unit/test_addsub.js @@ -55,7 +55,6 @@ function testSimpleSub() "chunkType" : "s", "urls": subUrls }]); - var assertions = { "tableData" : "test-phish-simple;a:1:s:50", "urlsExist" : [ "bar.com/b" ], @@ -362,8 +361,7 @@ function testExpireLists() { { "chunkType" : "sd:1-3,5" }]); var assertions = { - // "tableData" : "test-phish-simple;" - "tableData": "" + "tableData" : "test-phish-simple;" }; doTest([addUpdate, subUpdate, expireUpdate], assertions); @@ -481,7 +479,10 @@ function run_test() testSubPartiallyMatches2, testSubsDifferentChunks, testSubsDifferentChunksSameHostId, - testExpireLists + testExpireLists, + testDuplicateAddChunks, + testExpireWholeSub, + testPreventWholeSub, ]); } diff --git a/toolkit/components/url-classifier/tests/unit/test_cleankeycache.js b/toolkit/components/url-classifier/tests/unit/test_cleankeycache.js new file mode 100644 index 000000000000..d2e22d0c4749 --- /dev/null +++ b/toolkit/components/url-classifier/tests/unit/test_cleankeycache.js @@ -0,0 +1,195 @@ +//* -*- Mode: Javascript; tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*- * +// Test an add of two urls to a fresh database +function testCleanHostKeys() { + var addUrls = [ "foo.com/a" ]; + var update = buildPhishingUpdate( + [ + { "chunkNum" : 1, + "urls" : addUrls + }]); + + doStreamUpdate(update, function() { + var ios = Components.classes["@mozilla.org/network/io-service;1"]. + getService(Components.interfaces.nsIIOService); + + // Check with a clean host key + var uri = ios.newURI("http://bar.com/a", null, null); + + // Use the nsIURIClassifier interface (the + // nsIUrlClassifierDBService will always queue a lookup, + // nsIURIClassifier won't if the host key is known to be clean. + var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier); + var result = classifier.classify(uri, function(errorCode) { + var result2 = classifier.classify(uri, function() { + do_throw("shouldn't get a callback"); + }); + // second call shouldn't result in a callback. + do_check_eq(result2, false); + do_throw("shouldn't get a callback"); + }); + + // The first classifier call will not result in a callback + do_check_eq(result, false); + runNextTest(); + }, updateError); +} + +// Make sure that an update properly clears the host key cache +function testUpdate() { + var ios = Components.classes["@mozilla.org/network/io-service;1"]. + getService(Components.interfaces.nsIIOService); + + // Must put something in the PrefixSet + var preUrls = [ "foo.com/b" ]; + var preUpdate = buildPhishingUpdate( + [ + { "chunkNum" : 1, + "urls" : preUrls + }]); + + doStreamUpdate(preUpdate, function() { + // First lookup won't happen... + var uri = ios.newURI("http://foo.com/a", null, null); + + // Use the nsIURIClassifier interface (the + // nsIUrlClassifierDBService will always queue a lookup, + // nsIURIClassifier won't if the host key is known to be clean. + var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier); + var result = classifier.classify(uri, function(errorCode) { + // shouldn't arrive here + do_check_eq(errorCode, Cr.NS_OK); + do_throw("shouldn't get a callback"); + }); + do_check_eq(result, false); + + // Now add the url to the db... + var addUrls = [ "foo.com/a" ]; + var update = buildPhishingUpdate( + [ + { "chunkNum" : 2, + "urls" : addUrls + }]); + doStreamUpdate(update, function() { + var result2 = classifier.classify(uri, function(errorCode) { + do_check_neq(errorCode, Cr.NS_OK); + runNextTest(); + }); + // second call should result in a callback. + do_check_eq(result2, true); + }, updateError); + }, updateError); +} + +function testResetFullCache() { + // Must put something in the PrefixSet + var preUrls = [ "zaz.com/b" ]; + var preUpdate = buildPhishingUpdate( + [ + { "chunkNum" : 1, + "urls" : preUrls + }]); + + doStreamUpdate(preUpdate, function() { + // First do enough queries to fill up the clean hostkey cache + var ios = Components.classes["@mozilla.org/network/io-service;1"]. + getService(Components.interfaces.nsIIOService); + + // Use the nsIURIClassifier interface (the + // nsIUrlClassifierDBService will always queue a lookup, + // nsIURIClassifier won't if the host key is known to be clean. + var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier); + + var uris1 = [ + "www.foo.com/", + "www.bar.com/", + "www.blah.com/", + "www.site.com/", + "www.example.com/", + "www.test.com/", + "www.malware.com/", + "www.phishing.com/", + "www.clean.com/" ]; + + var uris2 = []; + + var runSecondLookup = function() { + if (uris2.length == 0) { + runNextTest(); + return; + } + + var spec = uris2.pop(); + var uri = ios.newURI("http://" + spec, null, null); + + var result = classifier.classify(uri, function(errorCode) { + }); + runSecondLookup(); + // now look up a few more times. + } + + var runInitialLookup = function() { + if (uris1.length == 0) { + // We're done filling up the cache. Run an update to flush it, + // then start lookup up again. + var addUrls = [ "notgoingtocheck.com/a" ]; + var update = buildPhishingUpdate( + [ + { "chunkNum" : 1, + "urls" : addUrls + }]); + doStreamUpdate(update, function() { + runSecondLookup(); + }, updateError); + return; + } + var spec = uris1.pop(); + + uris2.push(spec); + var uri = ios.newURI("http://" + spec, null, null); + var result = classifier.classify(uri, function(errorCode) { + }); + runInitialLookup(); + // None of these will generate a callback + do_check_eq(result, false); + if (!result) { + doNextTest(); + } + } + + // XXX bug 457790: dbservice.resetDatabase() doesn't have a way to + // wait to make sure it has been applied. Until this is added, we'll + // just use a timeout. + var t = new Timer(3000, runInitialLookup); + }, updateError); +} + +function testBug475436() { + var addUrls = [ "foo.com/a", "www.foo.com/" ]; + var update = buildPhishingUpdate( + [ + { "chunkNum" : 1, + "urls" : addUrls + }]); + + var assertions = { + "tableData" : "test-phish-simple;a:1", + "urlsExist" : ["foo.com/a", "foo.com/a" ] + }; + + doUpdateTest([update], assertions, runNextTest, updateError); +} + +function run_test() +{ + runTests([ + // XXX: We need to run testUpdate first, because of a + // race condition (bug 457790) calling dbservice.classify() + // directly after dbservice.resetDatabase(). + testUpdate, + testCleanHostKeys, + testResetFullCache, + testBug475436 + ]); +} + +do_test_pending(); diff --git a/toolkit/components/url-classifier/tests/unit/test_partial.js b/toolkit/components/url-classifier/tests/unit/test_partial.js index 2e997bc36d78..13df8eca09a8 100644 --- a/toolkit/components/url-classifier/tests/unit/test_partial.js +++ b/toolkit/components/url-classifier/tests/unit/test_partial.js @@ -461,8 +461,7 @@ function testWrongTable() "tableData" : "test-phish-simple;a:1", // The urls were added as phishing urls, but the completer is claiming // that they are malware urls, and we trust the completer in this case. - // The result will be discarded, so we can only check for non-existence. - "urlsDontExist" : addUrls, + "malwareUrlsExist" : addUrls, // Make sure the completer was actually queried. "completerQueried" : [completer, addUrls] }; @@ -471,14 +470,57 @@ function testWrongTable() function() { // Give the dbservice a chance to (not) cache the result. var timer = new Timer(3000, function() { - // The miss earlier will have caused a miss to be cached. - // Resetting the completer does not count as an update, - // so we will not be probed again. - var newCompleter = installCompleter('test-malware-simple', [[1, addUrls]], []); dbservice.setHashCompleter("test-phish-simple", + // The dbservice shouldn't have cached this result, + // so this completer should be queried. + var newCompleter = installCompleter('test-malware-simple', [[1, addUrls]], []); + + // The above installCompleter installs the + // completer for test-malware-simple, we want it + // to be used for test-phish-simple too. + dbservice.setHashCompleter("test-phish-simple", newCompleter); + var assertions = { - "urlsDontExist" : addUrls + "malwareUrlsExist" : addUrls, + "completerQueried" : [newCompleter, addUrls] + }; + checkAssertions(assertions, runNextTest); + }); + }, updateError); +} + +function testWrongChunk() +{ + var addUrls = [ "foo.com/a" ]; + var update = buildPhishingUpdate( + [ + { "chunkNum" : 1, + "urls" : addUrls + }], + 4); + var completer = installCompleter('test-phish-simple', + [[2, // wrong chunk number + addUrls]], []); + + var assertions = { + "tableData" : "test-phish-simple;a:1", + "urlsExist" : addUrls, + // Make sure the completer was actually queried. + "completerQueried" : [completer, addUrls] + }; + + doUpdateTest([update], assertions, + function() { + // Give the dbservice a chance to (not) cache the result. + var timer = new Timer(3000, function() { + // The dbservice shouldn't have cached this result, + // so this completer should be queried. + var newCompleter = installCompleter('test-phish-simple', [[2, addUrls]], []); + + var assertions = { + "urlsExist" : addUrls, + "completerQueried" : [newCompleter, addUrls] }; checkAssertions(assertions, runNextTest); }); @@ -776,6 +818,7 @@ function run_test() testMixedSizesDifferentDomains, testInvalidHashSize, testWrongTable, + testWrongChunk, testCachedResults, testCachedResultsWithSub, testCachedResultsWithExpire, @@ -783,7 +826,7 @@ function run_test() testStaleList, testStaleListEmpty, testErrorList, - testErrorListIndependent + testErrorListIndependent, ]); } diff --git a/toolkit/components/url-classifier/tests/unit/test_prefixset.js b/toolkit/components/url-classifier/tests/unit/test_prefixset.js index 72927a194d13..b71d3ccd66f2 100644 --- a/toolkit/components/url-classifier/tests/unit/test_prefixset.js +++ b/toolkit/components/url-classifier/tests/unit/test_prefixset.js @@ -1,9 +1,7 @@ // newPset: returns an empty nsIUrlClassifierPrefixSet. function newPset() { - let pset = Cc["@mozilla.org/url-classifier/prefixset;1"] - .createInstance(Ci.nsIUrlClassifierPrefixSet); - pset.init("all"); - return pset; + return Cc["@mozilla.org/url-classifier/prefixset;1"] + .createInstance(Ci.nsIUrlClassifierPrefixSet); } // arrContains: returns true if |arr| contains the element |target|. Uses binary @@ -30,22 +28,10 @@ function arrContains(arr, target) { return (!(i < 0 || i >= arr.length) && arr[i] == target); } -// checkContents: Check whether the PrefixSet pset contains -// the prefixes in the passed array. -function checkContents(pset, prefixes) { - var outcount = {}, outset = {}; - outset = pset.getPrefixes(outcount); - let inset = prefixes; - do_check_eq(inset.length, outset.length); - inset.sort(function(x,y) x - y); - for (let i = 0; i < inset.length; i++) { - do_check_eq(inset[i], outset[i]); - } -} - function wrappedProbe(pset, prefix) { + let key = pset.getKey(); let dummy = {}; - return pset.probe(prefix, dummy); + return pset.probe(prefix, key, dummy); }; // doRandomLookups: we use this to test for false membership with random input @@ -88,9 +74,6 @@ function testBasicPset() { do_check_true(wrappedProbe(pset, 1593203)); do_check_false(wrappedProbe(pset, 999)); do_check_false(wrappedProbe(pset, 0)); - - - checkContents(pset, prefixes); } function testDuplicates() { @@ -105,9 +88,6 @@ function testDuplicates() { do_check_true(wrappedProbe(pset, 9)); do_check_false(wrappedProbe(pset, 4)); do_check_false(wrappedProbe(pset, 8)); - - - checkContents(pset, prefixes); } function testSimplePset() { @@ -117,9 +97,6 @@ function testSimplePset() { doRandomLookups(pset, prefixes, 100); doExpectedLookups(pset, prefixes, 1); - - - checkContents(pset, prefixes); } function testReSetPrefixes() { @@ -136,9 +113,6 @@ function testReSetPrefixes() { for (let i = 0; i < prefixes.length; i++) { do_check_false(wrappedProbe(pset, prefixes[i])); } - - - checkContents(pset, secondPrefixes); } function testLargeSet() { @@ -157,9 +131,6 @@ function testLargeSet() { doExpectedLookups(pset, arr, 1); doRandomLookups(pset, arr, 1000); - - - checkContents(pset, arr); } function testTinySet() { @@ -170,12 +141,10 @@ function testTinySet() { do_check_true(wrappedProbe(pset, 1)); do_check_false(wrappedProbe(pset, 100000)); - checkContents(pset, prefixes); prefixes = []; pset.setPrefixes(prefixes, prefixes.length); do_check_false(wrappedProbe(pset, 1)); - checkContents(pset, prefixes); } let tests = [testBasicPset, diff --git a/toolkit/components/url-classifier/tests/unit/test_streamupdater.js b/toolkit/components/url-classifier/tests/unit/test_streamupdater.js index c934890dba0e..224cc871c2c3 100644 --- a/toolkit/components/url-classifier/tests/unit/test_streamupdater.js +++ b/toolkit/components/url-classifier/tests/unit/test_streamupdater.js @@ -80,6 +80,8 @@ function testSimpleForward() { // Make sure that a nested forward (a forward within a forward) causes // the update to fail. function testNestedForward() { + testFillDb(); // Make sure the db isn't empty + var add1Urls = [ "foo.com/a", "bar.com/c" ]; var add2Urls = [ "foo.com/b" ]; @@ -201,6 +203,8 @@ function testValidMAC() { // Test a simple update with an invalid message authentication code. function testInvalidMAC() { + testFillDb(); // Make sure the db isn't empty + var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ]; var update = buildPhishingUpdate( [ @@ -220,6 +224,8 @@ function testInvalidMAC() { // Test a simple update without a message authentication code, when it is // expecting one. function testNoMAC() { + testFillDb(); // Make sure the db isn't empty + var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ]; var update = buildPhishingUpdate( [ @@ -276,6 +282,8 @@ function testValidForwardMAC() { // Test an update with a valid message authentication code, but with // invalid MACs on the forwards. function testInvalidForwardMAC() { + testFillDb(); // Make sure the db isn't empty + var add1Urls = [ "foo.com/a", "bar.com/c" ]; var add2Urls = [ "foo.com/b" ]; var add3Urls = [ "bar.com/d" ]; @@ -315,6 +323,8 @@ function testInvalidForwardMAC() { // Test an update with a valid message authentication code, but no MAC // specified for sub-urls. function testNoForwardMAC() { + testFillDb(); // Make sure the db isn't empty + var add1Urls = [ "foo.com/a", "bar.com/c" ]; var add2Urls = [ "foo.com/b" ]; var add3Urls = [ "bar.com/d" ]; @@ -381,6 +391,8 @@ gAssertions.gotRekey = function(data, cb) // Tests a rekey request. function testRekey() { + testFillDb(); + var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ]; var update = buildPhishingUpdate( [ @@ -445,9 +457,6 @@ function run_test() testInvalidUrlForward, testErrorUrlForward, testMultipleTables, - testReset, - // XXX: we're currently "once MAC, always MAC", - // so any test not using a MAC must go above testValidMAC, testInvalidMAC, testNoMAC, @@ -455,6 +464,7 @@ function run_test() testInvalidForwardMAC, testNoForwardMAC, testRekey, + testReset, ]); } diff --git a/toolkit/components/url-classifier/tests/unit/xpcshell.ini b/toolkit/components/url-classifier/tests/unit/xpcshell.ini index e80ef1e69613..8de09eda577c 100644 --- a/toolkit/components/url-classifier/tests/unit/xpcshell.ini +++ b/toolkit/components/url-classifier/tests/unit/xpcshell.ini @@ -4,6 +4,7 @@ tail = tail_urlclassifier.js [test_addsub.js] [test_backoff.js] +[test_cleankeycache.js] [test_dbservice.js] [test_hashcompleter.js] [test_partial.js] From ab18fc8ad0febd850edfbb3307a403644c32392b Mon Sep 17 00:00:00 2001 From: Jonathan Watt Date: Wed, 1 Feb 2012 19:15:49 +0000 Subject: [PATCH 49/90] Bug 119061 - Make SVG elements respond to :hover when script/animation moves them under the mouse pointer. r=roc. --- layout/svg/base/src/nsSVGOuterSVGFrame.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp index cffcff39d8bf..0d88e56e1d5a 100644 --- a/layout/svg/base/src/nsSVGOuterSVGFrame.cpp +++ b/layout/svg/base/src/nsSVGOuterSVGFrame.cpp @@ -687,6 +687,10 @@ nsSVGOuterSVGFrame::GetType() const void nsSVGOuterSVGFrame::InvalidateCoveredRegion(nsIFrame *aFrame) { + // Make sure elements styled by :hover get updated if script/animation moves + // them under or out from under the pointer: + PresContext()->PresShell()->SynthesizeMouseMove(false); + nsISVGChildFrame *svgFrame = do_QueryFrame(aFrame); if (!svgFrame) return; @@ -698,6 +702,10 @@ nsSVGOuterSVGFrame::InvalidateCoveredRegion(nsIFrame *aFrame) bool nsSVGOuterSVGFrame::UpdateAndInvalidateCoveredRegion(nsIFrame *aFrame) { + // Make sure elements styled by :hover get updated if script/animation moves + // them under or out from under the pointer: + PresContext()->PresShell()->SynthesizeMouseMove(false); + nsISVGChildFrame *svgFrame = do_QueryFrame(aFrame); if (!svgFrame) return false; From ea987f7685bcbc907017c892fa9e1e078e4f58af Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Thu, 19 Jan 2012 17:15:33 -0800 Subject: [PATCH 50/90] Bug 720316 - Convert the various object and function indexes into uint32_t. r=jorendorff --- js/src/frontend/BytecodeEmitter.cpp | 102 ++++++++++-------- .../basic/adjacent-trycatch-second-nested.js | 9 ++ .../jaeger/bug563000/trap-from-add-inline.js | 2 +- .../jaeger/bug563000/trap-from-add-ool.js | 2 +- js/src/jsanalyze.cpp | 2 +- js/src/jsinfer.cpp | 13 +-- js/src/jsinterp.cpp | 36 ++----- js/src/jsopcode.cpp | 83 +++++++------- js/src/jsopcode.h | 21 +--- js/src/jsopcode.tbl | 24 ++--- js/src/jsscript.cpp | 7 +- js/src/jsxdrapi.h | 2 +- js/src/methodjit/Compiler.cpp | 21 ++-- js/src/methodjit/Compiler.h | 2 +- js/src/methodjit/InvokeHelpers.cpp | 2 +- 15 files changed, 152 insertions(+), 176 deletions(-) create mode 100644 js/src/jit-test/tests/basic/adjacent-trycatch-second-nested.js diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 7b6f7400687f..bdd171ee1a9b 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -565,7 +565,7 @@ CheckTypeSet(JSContext *cx, BytecodeEmitter *bce, JSOp op) ptrdiff_t off_ = EmitN(cx, bce, op, 2 * UINT16_LEN); \ if (off_ < 0) \ return JS_FALSE; \ - jsbytecode *pc_ = bce->code(off_); \ + jsbytecode *pc_ = bce->code(off_); \ SET_UINT16(pc_, i); \ pc_ += UINT16_LEN; \ SET_UINT16(pc_, j); \ @@ -573,9 +573,18 @@ CheckTypeSet(JSContext *cx, BytecodeEmitter *bce, JSOp op) #define EMIT_UINT16_IN_PLACE(offset, op, i) \ JS_BEGIN_MACRO \ - bce->code(offset)[0] = op; \ - bce->code(offset)[1] = UINT16_HI(i); \ - bce->code(offset)[2] = UINT16_LO(i); \ + bce->code(offset)[0] = op; \ + bce->code(offset)[1] = UINT16_HI(i); \ + bce->code(offset)[2] = UINT16_LO(i); \ + JS_END_MACRO + +#define EMIT_UINT32_IN_PLACE(offset, op, i) \ + JS_BEGIN_MACRO \ + bce->code(offset)[0] = op; \ + bce->code(offset)[1] = jsbytecode(i >> 24); \ + bce->code(offset)[2] = jsbytecode(i >> 16); \ + bce->code(offset)[3] = jsbytecode(i >> 8); \ + bce->code(offset)[4] = jsbytecode(i); \ JS_END_MACRO static JSBool @@ -970,17 +979,11 @@ EmitAtomOp(JSContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *bce, JSOp *ps return EmitAtomOp(cx, pn->pn_atom, op, bce, psuffix); } -static JSBool -EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce) -{ - JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT); - 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; + JS_ASSERT(js_CodeSpec[op].length == len); ptrdiff_t offset = EmitCheck(cx, bce, len); if (offset < 0) return false; @@ -994,43 +997,39 @@ EmitIndex32(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce) return true; } +static bool +EmitFunctionOp(JSContext *cx, JSOp op, uint32_t index, BytecodeEmitter *bce) +{ + return EmitIndex32(cx, op, index, bce); +} + +static bool +EmitObjectOp(JSContext *cx, ObjectBox *objbox, JSOp op, BytecodeEmitter *bce) +{ + JS_ASSERT(JOF_OPTYPE(op) == JOF_OBJECT); + return EmitIndex32(cx, op, bce->objectList.index(objbox), bce); +} + 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 - * other parts of the code there's no necessary relationship between the two. - * The abstraction cracks here in order to share EmitSlotIndexOp code among - * the JSOP_DEFLOCALFUN and JSOP_GET{ARG,VAR,LOCAL}PROP cases. - */ -JS_STATIC_ASSERT(ARGNO_LEN == 2); -JS_STATIC_ASSERT(SLOTNO_LEN == 2); - -static JSBool -EmitSlotIndexOp(JSContext *cx, JSOp op, uintN slot, uintN index, BytecodeEmitter *bce) +static bool +EmitSlotObjectOp(JSContext *cx, JSOp op, uintN slot, uint32_t index, BytecodeEmitter *bce) { - JSOp bigSuffix; - ptrdiff_t off; - jsbytecode *pc; - JS_ASSERT(JOF_OPTYPE(op) == JOF_SLOTOBJECT); - bigSuffix = EmitBigIndexPrefix(cx, bce, index); - if (bigSuffix == JSOP_FALSE) - return JS_FALSE; - /* Emit [op, slot, index]. */ - off = EmitN(cx, bce, op, 2 + INDEX_LEN); + ptrdiff_t off = EmitN(cx, bce, op, SLOTNO_LEN + UINT32_INDEX_LEN); if (off < 0) - return JS_FALSE; - pc = bce->code(off); + return false; + + jsbytecode *pc = bce->code(off); SET_UINT16(pc, slot); - pc += 2; - SET_INDEX(pc, index); - return bigSuffix == JSOP_NOP || Emit1(cx, bce, bigSuffix) >= 0; + pc += SLOTNO_LEN; + SET_UINT32_INDEX(pc, index); + return true; } bool @@ -3910,8 +3909,19 @@ EmitFunctionDefNop(JSContext *cx, BytecodeEmitter *bce, uintN index) static bool EmitNewInit(JSContext *cx, BytecodeEmitter *bce, JSProtoKey key, ParseNode *pn) { - if (Emit3(cx, bce, JSOP_NEWINIT, (jsbytecode) key, 0) < 0) + 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] = JSOP_NEWINIT; + next[1] = jsbytecode(key); + next[2] = 0; + next[3] = 0; + next[4] = 0; + bce->current->next = next + len; + UpdateDepth(cx, bce, offset); CheckTypeSet(cx, bce, JSOP_NEWINIT); return true; } @@ -5109,8 +5119,8 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { return false; } - EMIT_INDEX_OP(pn->getOp(), index); - return true; + + return EmitFunctionOp(cx, pn->getOp(), index, bce); } /* @@ -5129,7 +5139,8 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (pn->pn_cookie.isFree()) { bce->switchToProlog(); JSOp op = fun->isFlatClosure() ? JSOP_DEFFUN_FC : JSOP_DEFFUN; - EMIT_INDEX_OP(op, index); + if (!EmitFunctionOp(cx, op, index, bce)) + return false; bce->switchToMain(); } @@ -5149,7 +5160,7 @@ EmitFunc(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { return false; } - return EmitSlotIndexOp(cx, op, slot, index, bce); + return EmitSlotObjectOp(cx, op, slot, index, bce); } return true; @@ -6046,8 +6057,11 @@ EmitObject(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) if (!objbox) return false; uintN index = bce->objectList.index(objbox); - if (FitsWithoutBigIndex(index)) - EMIT_UINT16_IN_PLACE(offset, JSOP_NEWOBJECT, uint16_t(index)); + if (FitsWithoutBigIndex(index)) { + MOZ_STATIC_ASSERT(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH, + "newinit and newobject must have equal length to edit in-place"); + EMIT_UINT32_IN_PLACE(offset, JSOP_NEWOBJECT, uint32_t(index)); + } } return true; diff --git a/js/src/jit-test/tests/basic/adjacent-trycatch-second-nested.js b/js/src/jit-test/tests/basic/adjacent-trycatch-second-nested.js new file mode 100644 index 000000000000..35767eb4022a --- /dev/null +++ b/js/src/jit-test/tests/basic/adjacent-trycatch-second-nested.js @@ -0,0 +1,9 @@ +try { } +catch (e) { } + +try { throw 2; } +catch (e) +{ + try { throw 3; } + catch (e2) { } +} diff --git a/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js b/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js index 59da2a43ea37..9234f1637653 100644 --- a/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js +++ b/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-inline.js @@ -3,7 +3,7 @@ setDebug(true); x = "notset"; function main() { /* The JSOP_STOP in main. */ - a = { valueOf: function () { trap(main, 34, "success()"); } }; + a = { valueOf: function () { trap(main, 39, "success()"); } }; a + ""; x = "failure"; } diff --git a/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js b/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js index 5d38997d979f..3eaec1353b50 100644 --- a/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js +++ b/js/src/jit-test/tests/jaeger/bug563000/trap-from-add-ool.js @@ -3,7 +3,7 @@ setDebug(true); x = "notset"; function main() { /* The JSOP_STOP in main. */ - a = { valueOf: function () { trap(main, 55, "success()"); } }; + a = { valueOf: function () { trap(main, 60, "success()"); } }; b = ""; eval(); a + b; diff --git a/js/src/jsanalyze.cpp b/js/src/jsanalyze.cpp index bfa7b0253378..cb2e1f685bd2 100644 --- a/js/src/jsanalyze.cpp +++ b/js/src/jsanalyze.cpp @@ -119,7 +119,7 @@ ScriptAnalysis::checkAliasedName(JSContext *cx, jsbytecode *pc) JSAtom *atom; if (JSOp(*pc) == JSOP_DEFFUN) { - JSFunction *fun = script->getFunction(js_GetIndexFromBytecode(script, pc, 0)); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(pc)); atom = fun->atom; } else { JS_ASSERT(JOF_TYPE(js_CodeSpec[*pc].format) == JOF_ATOM); diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index 8206f95925db..57de63c895df 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -2026,7 +2026,7 @@ TypeCompartment::newAllocationSiteTypeObject(JSContext *cx, const AllocationSite * observed by other code before all properties have been added. Mark * all the properties as definite properties of the object. */ - JSObject *baseobj = key.script->getObject(GET_SLOTNO(pc)); + JSObject *baseobj = key.script->getObject(GET_UINT32_INDEX(pc)); if (!res->addDefiniteProperties(cx, baseobj)) return NULL; @@ -2047,13 +2047,6 @@ GetAtomId(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset return MakeTypeId(cx, ATOM_TO_JSID(script->getAtom(index))); } -static inline JSObject * -GetScriptObject(JSContext *cx, JSScript *script, const jsbytecode *pc, unsigned offset) -{ - unsigned index = js_GetIndexFromBytecode(script, (jsbytecode*) pc, offset); - return script->getObject(index); -} - static inline const Value & GetScriptConst(JSContext *cx, JSScript *script, const jsbytecode *pc) { @@ -3528,7 +3521,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, break; case JSOP_OBJECT: { - JSObject *obj = GetScriptObject(cx, script, pc, 0); + JSObject *obj = script->getObject(GET_UINT32_INDEX(pc)); pushed[0].addType(cx, Type::ObjectType(obj)); break; } @@ -3833,7 +3826,7 @@ ScriptAnalysis::analyzeTypesBytecode(JSContext *cx, unsigned offset, case JSOP_DEFLOCALFUN: case JSOP_DEFLOCALFUN_FC: { unsigned off = (op == JSOP_DEFLOCALFUN || op == JSOP_DEFLOCALFUN_FC) ? SLOTNO_LEN : 0; - JSObject *obj = GetScriptObject(cx, script, pc, off); + JSObject *obj = script->getObject(GET_UINT32_INDEX(pc + off)); TypeSet *res = NULL; if (op == JSOP_LAMBDA || op == JSOP_LAMBDA_FC) { diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index 58405ec38ad3..52a9582ef7fc 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -1410,6 +1410,7 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) # define END_CASE_LEN3 len = 3; goto advance_pc; # define END_CASE_LEN4 len = 4; goto advance_pc; # define END_CASE_LEN5 len = 5; goto advance_pc; +# define END_CASE_LEN7 len = 7; goto advance_pc; # define END_VARLEN_CASE goto advance_pc; # define ADD_EMPTY_CASE(OP) BEGIN_CASE(OP) # define END_EMPTY_CASES goto advance_pc_by_one; @@ -1435,12 +1436,6 @@ js::Interpret(JSContext *cx, StackFrame *entryFrame, InterpMode interpMode) #define GET_FULL_INDEX(PCOFF) \ (atoms - script->atoms + GET_INDEX(regs.pc + (PCOFF))) -#define LOAD_OBJECT(PCOFF, obj) \ - (obj = script->getObject(GET_FULL_INDEX(PCOFF))) - -#define LOAD_FUNCTION(PCOFF) \ - (fun = script->getFunction(GET_FULL_INDEX(PCOFF))) - #define LOAD_DOUBLE(PCOFF, dbl) \ (dbl = script->getConst(GET_FULL_INDEX(PCOFF)).toDouble()) @@ -2947,9 +2942,7 @@ END_CASE(JSOP_STRING) BEGIN_CASE(JSOP_OBJECT) { - JSObject *obj; - LOAD_OBJECT(0, obj); - PUSH_OBJECT(*obj); + PUSH_OBJECT(*script->getObject(GET_UINT32_INDEX(regs.pc))); } END_CASE(JSOP_OBJECT) @@ -3228,8 +3221,7 @@ BEGIN_CASE(JSOP_DEFFUN) * a compound statement (not at the top statement level of global code, or * at the top level of a function body). */ - JSFunction *fun; - LOAD_FUNCTION(0); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc)); JSObject *obj = fun; JSObject *obj2; @@ -3338,8 +3330,7 @@ END_CASE(JSOP_DEFFUN) BEGIN_CASE(JSOP_DEFFUN_FC) { - JSFunction *fun; - LOAD_FUNCTION(0); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc)); JSObject *obj = js_NewFlatClosure(cx, fun); if (!obj) @@ -3375,8 +3366,7 @@ BEGIN_CASE(JSOP_DEFLOCALFUN) * JSOP_DEFFUN that avoids requiring a call object for the outer function's * activation. */ - JSFunction *fun; - LOAD_FUNCTION(SLOTNO_LEN); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc + SLOTNO_LEN)); JS_ASSERT(fun->isInterpreted()); JS_ASSERT(!fun->isFlatClosure()); @@ -3401,8 +3391,7 @@ END_CASE(JSOP_DEFLOCALFUN) BEGIN_CASE(JSOP_DEFLOCALFUN_FC) { - JSFunction *fun; - LOAD_FUNCTION(SLOTNO_LEN); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc + SLOTNO_LEN)); JSObject *obj = js_NewFlatClosure(cx, fun); if (!obj) @@ -3416,8 +3405,7 @@ END_CASE(JSOP_DEFLOCALFUN_FC) BEGIN_CASE(JSOP_LAMBDA) { /* Load the specified function object literal. */ - JSFunction *fun; - LOAD_FUNCTION(0); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc)); JSObject *obj = fun; /* do-while(0) so we can break instead of using a goto. */ @@ -3510,8 +3498,7 @@ END_CASE(JSOP_LAMBDA) BEGIN_CASE(JSOP_LAMBDA_FC) { - JSFunction *fun; - LOAD_FUNCTION(0); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(regs.pc)); JSObject *obj = js_NewFlatClosure(cx, fun); if (!obj) @@ -3688,8 +3675,7 @@ END_CASE(JSOP_NEWARRAY) BEGIN_CASE(JSOP_NEWOBJECT) { - JSObject *baseobj; - LOAD_OBJECT(0, baseobj); + JSObject *baseobj = script->getObject(GET_UINT32_INDEX(regs.pc)); TypeObject *type = TypeScript::InitObject(cx, script, regs.pc, JSProto_Object); if (!type) @@ -4235,9 +4221,7 @@ BEGIN_CASE(JSOP_ENTERBLOCK) BEGIN_CASE(JSOP_ENTERLET0) BEGIN_CASE(JSOP_ENTERLET1) { - JSObject *obj; - LOAD_OBJECT(0, obj); - StaticBlockObject &blockObj = obj->asStaticBlock(); + StaticBlockObject &blockObj = script->getObject(GET_UINT32_INDEX(regs.pc))->asStaticBlock(); JS_ASSERT(regs.fp()->maybeBlockChain() == blockObj.enclosingBlock()); if (op == JSOP_ENTERBLOCK) { diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 43c6f08611d3..9cbb9bccda48 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -149,6 +149,7 @@ js_GetIndexFromBytecode(JSScript *script, jsbytecode *pc, ptrdiff_t pcoff) { JSOp op = JSOp(*pc); JS_ASSERT(js_CodeSpec[op].length >= 1 + pcoff + UINT16_LEN); + JS_ASSERT(js_CodeSpec[op].format & JOF_ATOM); /* * We need to detect index base prefix. It presents when resetbase @@ -204,9 +205,7 @@ NumBlockSlots(JSScript *script, jsbytecode *pc) JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET0_LENGTH); JS_STATIC_ASSERT(JSOP_ENTERBLOCK_LENGTH == JSOP_ENTERLET1_LENGTH); - JSObject *obj = NULL; - GET_OBJECT_FROM_BYTECODE(script, pc, 0, obj); - return obj->asStaticBlock().slotCount(); + return script->getObject(GET_UINT32_INDEX(pc))->asStaticBlock().slotCount(); } uintN @@ -541,7 +540,7 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, Sprint(sp, "%4u", JS_PCToLineNumber(cx, script, pc)); Sprint(sp, " %s", js_CodeName[op]); - switch (uint32_t type = JOF_TYPE(cs->format)) { + switch (JOF_TYPE(cs->format)) { case JOF_BYTE: // Scan the trynotes to find the associated catch block // and make the try opcode look like a jump instruction @@ -568,28 +567,14 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, break; } - case JOF_ATOM: - case JOF_OBJECT: { + case JOF_ATOM: { uintN index = js_GetIndexFromBytecode(script, pc, 0); jsval v; - if (type == JOF_ATOM) { - if (op == JSOP_DOUBLE) { - v = script->getConst(index); - } else { - JSAtom *atom = script->getAtom(index); - v = STRING_TO_JSVAL(atom); - } + if (op == JSOP_DOUBLE) { + v = script->getConst(index); } else { - 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); + JSAtom *atom = script->getAtom(index); + v = STRING_TO_JSVAL(atom); } { JSAutoByteString bytes; @@ -600,6 +585,23 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, break; } + case JOF_OBJECT: { + /* Don't call obj.toSource if analysis/inference is active. */ + if (cx->compartment->activeAnalysis) { + Sprint(sp, " object"); + break; + } + + JSObject *obj = script->getObject(GET_UINT32_INDEX(pc)); + { + JSAutoByteString bytes; + if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes)) + return 0; + Sprint(sp, " %s", bytes.ptr()); + } + break; + } + case JOF_REGEXP: { JSObject *obj = script->getRegExp(GET_UINT32_INDEX(pc)); JSAutoByteString bytes; @@ -664,10 +666,9 @@ js_Disassemble1(JSContext *cx, JSScript *script, jsbytecode *pc, case JOF_SLOTOBJECT: { Sprint(sp, " %u", GET_SLOTNO(pc)); - uintN index = js_GetIndexFromBytecode(script, pc, SLOTNO_LEN); - jsval v = OBJECT_TO_JSVAL(script->getObject(index)); + JSObject *obj = script->getObject(GET_UINT32_INDEX(pc + SLOTNO_LEN)); JSAutoByteString bytes; - if (!ToDisassemblySource(cx, v, &bytes)) + if (!ToDisassemblySource(cx, ObjectValue(*obj), &bytes)) return 0; Sprint(sp, " %s", bytes.ptr()); break; @@ -1857,8 +1858,7 @@ GetLocal(SprintStack *ss, jsint i) JS_ASSERT(pc < (ss->printer->script->code + ss->printer->script->length)); if (JSOP_ENTERBLOCK == (JSOp)*pc) { - jsatomid j = js_GetIndexFromBytecode(ss->printer->script, pc, 0); - JSObject *obj = script->getObject(j); + JSObject *obj = script->getObject(GET_UINT32_INDEX(pc)); if (obj->isBlock()) { uint32_t depth = obj->asBlock().stackDepth(); @@ -2660,12 +2660,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) return NULL; \ JS_END_MACRO -#define LOAD_OBJECT(PCOFF) \ - GET_OBJECT_FROM_BYTECODE(jp->script, pc, PCOFF, obj) - -#define LOAD_FUNCTION(PCOFF) \ - GET_FUNCTION_FROM_BYTECODE(jp->script, pc, PCOFF, fun) - #define GET_SOURCE_NOTE_ATOM(sn, atom) \ JS_BEGIN_MACRO \ jsatomid atomIndex_ = (jsatomid) js_GetSrcNoteOffset((sn), 0); \ @@ -2736,9 +2730,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) if (cs->format & JOF_INDEXBASE) { /* * The decompiler uses js_GetIndexFromBytecode to get atoms and - * objects and ignores these suffix/prefix bytecodes, thus - * simplifying code that must process JSOP_GETTER/JSOP_SETTER - * prefixes. + * ignores these suffix/prefix bytecodes, thus simplifying code + * that must process JSOP_GETTER/JSOP_SETTER prefixes. */ pc += cs->length; if (pc >= endpc) @@ -3292,7 +3285,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_ENTERBLOCK: { - LOAD_OBJECT(0); + obj = jp->script->getObject(GET_UINT32_INDEX(pc)); AtomVector atoms(cx); StaticBlockObject &blockObj = obj->asStaticBlock(); @@ -3435,7 +3428,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_ENTERLET0: { - LOAD_OBJECT(0); + obj = jp->script->getObject(GET_UINT32_INDEX(pc)); StaticBlockObject &blockObj = obj->asStaticBlock(); AtomVector atoms(cx); @@ -3554,7 +3547,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) */ case JSOP_ENTERLET1: { - LOAD_OBJECT(0); + obj = jp->script->getObject(GET_UINT32_INDEX(pc)); StaticBlockObject &blockObj = obj->asStaticBlock(); AtomVector atoms(cx); @@ -4229,7 +4222,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) ptrdiff_t offsetToLet = js_GetSrcNoteOffset(sn, 0); LOCAL_ASSERT(*(pc + offsetToLet) == JSOP_ENTERLET0); - GET_OBJECT_FROM_BYTECODE(jp->script, pc + offsetToLet, 0, obj); + obj = jp->script->getObject(GET_UINT32_INDEX(pc + offsetToLet)); StaticBlockObject &blockObj = obj->asStaticBlock(); uint32_t blockDepth = blockObj.stackDepth(); @@ -4776,7 +4769,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) SprintStack ss2(cx); JSFunction *outerfun; - LOAD_FUNCTION(0); + fun = jp->script->getFunction(GET_UINT32_INDEX(pc)); /* * All allocation when decompiling is LIFO, using malloc or, @@ -4902,7 +4895,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) #endif /* JS_HAS_GENERATOR_EXPRS */ /* FALL THROUGH */ - LOAD_FUNCTION(0); + fun = jp->script->getFunction(GET_UINT32_INDEX(pc)); { /* * Always parenthesize expression closures. We can't force @@ -4928,7 +4921,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case JSOP_OBJECT: - LOAD_OBJECT(0); + obj = jp->script->getObject(GET_UINT32_INDEX(pc)); str = js_ValueToSource(cx, ObjectValue(*obj)); if (!str) return NULL; @@ -5132,7 +5125,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_DEFFUN: case JSOP_DEFFUN_FC: - LOAD_FUNCTION(0); + fun = jp->script->getFunction(GET_UINT32_INDEX(pc)); todo = -2; goto do_function; break; diff --git a/js/src/jsopcode.h b/js/src/jsopcode.h index 495b23a8d081..03b0e12d081b 100644 --- a/js/src/jsopcode.h +++ b/js/src/jsopcode.h @@ -207,11 +207,10 @@ SET_UINT32_INDEX(jsbytecode *pc, uint32_t index) } /* - * A literal is indexed by a per-script atom or object maps. Most scripts - * 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. + * A literal is indexed by a per-script atom map. Most scripts have relatively + * few literals, so the standard JOF_ATOM format specifies a fixed 16 bits of + * immediate operand index. A script with more than 64K literals must wrap the + * bytecode into an JSOP_INDEXBASE and JSOP_RESETBASE pair. */ #define INDEX_LEN 2 #define INDEX_HI(i) ((jsbytecode)((i) >> 8)) @@ -360,18 +359,6 @@ js_GetIndexFromBytecode(JSScript *script, jsbytecode *pc, ptrdiff_t pcoff); (dbl) = (script)->getConst(index_).toDouble(); \ JS_END_MACRO -#define GET_OBJECT_FROM_BYTECODE(script, pc, pcoff, obj) \ - JS_BEGIN_MACRO \ - uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \ - obj = (script)->getObject(index_); \ - JS_END_MACRO - -#define GET_FUNCTION_FROM_BYTECODE(script, pc, pcoff, fun) \ - JS_BEGIN_MACRO \ - uintN index_ = js_GetIndexFromBytecode((script), (pc), (pcoff)); \ - fun = (script)->getFunction(index_); \ - JS_END_MACRO - #ifdef __cplusplus namespace js { diff --git a/js/src/jsopcode.tbl b/js/src/jsopcode.tbl index 3a3b48fac24e..6e24ac30408b 100644 --- a/js/src/jsopcode.tbl +++ b/js/src/jsopcode.tbl @@ -224,7 +224,7 @@ OPDEF(JSOP_ENDITER, 78, "enditer", NULL, 1, 1, 0, 0, JOF_BYTE) OPDEF(JSOP_FUNAPPLY, 79, "funapply", NULL, 3, -1, 1, 18, JOF_UINT16|JOF_INVOKE|JOF_TYPESET) /* Push object literal: either an XML object or initialiser object. */ -OPDEF(JSOP_OBJECT, 80, "object", NULL, 3, 0, 1, 19, JOF_OBJECT) +OPDEF(JSOP_OBJECT, 80, "object", NULL, 5, 0, 1, 19, JOF_OBJECT) /* Pop value and discard it. */ OPDEF(JSOP_POP, 81, "pop", NULL, 1, 1, 0, 2, JOF_BYTE) @@ -251,9 +251,9 @@ OPDEF(JSOP_UINT16, 88, "uint16", NULL, 3, 0, 1, 16, JOF_UINT16 * shape, which can be set at the start and slots then filled in directly. * NEWINIT has an extra byte so it can be exchanged with NEWOBJECT during emit. */ -OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 3, 0, 1, 19, JOF_UINT8|JOF_TYPESET) +OPDEF(JSOP_NEWINIT, 89, "newinit", NULL, 5, 0, 1, 19, JOF_UINT8|JOF_TYPESET) OPDEF(JSOP_NEWARRAY, 90, "newarray", NULL, 4, 0, 1, 19, JOF_UINT24|JOF_TYPESET) -OPDEF(JSOP_NEWOBJECT, 91, "newobject", NULL, 3, 0, 1, 19, JOF_OBJECT|JOF_TYPESET) +OPDEF(JSOP_NEWOBJECT, 91, "newobject", NULL, 5, 0, 1, 19, JOF_OBJECT|JOF_TYPESET) OPDEF(JSOP_ENDINIT, 92, "endinit", NULL, 1, 0, 0, 19, JOF_BYTE) OPDEF(JSOP_INITPROP, 93, "initprop", NULL, 3, 2, 1, 3, JOF_ATOM|JOF_PROP|JOF_SET|JOF_DETECTING) OPDEF(JSOP_INITELEM, 94, "initelem", NULL, 1, 3, 1, 3, JOF_BYTE|JOF_ELEM|JOF_SET|JOF_DETECTING) @@ -338,12 +338,12 @@ OPDEF(JSOP_SETTER, 126,js_setter_str,NULL, 1, 0, 0, 0, JOF_BYTE) /* * Prolog bytecodes for defining function, var, and const names. */ -OPDEF(JSOP_DEFFUN, 127,"deffun", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING) +OPDEF(JSOP_DEFFUN, 127,"deffun", NULL, 5, 0, 0, 0, JOF_OBJECT|JOF_DECLARING) OPDEF(JSOP_DEFCONST, 128,"defconst", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING) OPDEF(JSOP_DEFVAR, 129,"defvar", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING) /* Push a closure for a named or anonymous function expression. */ -OPDEF(JSOP_LAMBDA, 130, "lambda", NULL, 3, 0, 1, 19, JOF_OBJECT) +OPDEF(JSOP_LAMBDA, 130, "lambda", NULL, 5, 0, 1, 19, JOF_OBJECT) /* Used for named function expression self-naming, if lightweight. */ OPDEF(JSOP_CALLEE, 131, "callee", NULL, 1, 0, 1, 19, JOF_BYTE) @@ -377,7 +377,7 @@ OPDEF(JSOP_CALLFCSLOT, 137,"callfcslot", NULL, 3, 0, 1, 19, JOF_UINT16 * The local variable's slot number is the first immediate two-byte operand. * The function object's atom index is the second immediate operand. */ -OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT) +OPDEF(JSOP_DEFLOCALFUN, 138,"deflocalfun",NULL, 7, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT) /* Extended jumps. */ OPDEF(JSOP_UNUSED4, 139,"unused4", NULL, 1, 0, 0, 0, JOF_BYTE) @@ -441,10 +441,10 @@ OPDEF(JSOP_DELDESC, 183,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|J OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 3, 1, 1, 18, JOF_ATOM|JOF_PROP|JOF_TYPESET|JOF_TMPSLOT3) /* Enter a let block/expr whose slots are at the top of the stack. */ -OPDEF(JSOP_ENTERLET0, 185,"enterlet0", NULL, 3, -1, -1, 0, JOF_OBJECT) +OPDEF(JSOP_ENTERLET0, 185,"enterlet0", NULL, 5, -1, -1, 0, JOF_OBJECT) /* Enter a let block/expr whose slots are 1 below the top of the stack. */ -OPDEF(JSOP_ENTERLET1, 186,"enterlet1", NULL, 3, -1, -1, 0, JOF_OBJECT) +OPDEF(JSOP_ENTERLET1, 186,"enterlet1", NULL, 5, -1, -1, 0, JOF_OBJECT) /* * Opcode to hold 24-bit immediate integer operands. @@ -491,7 +491,7 @@ OPDEF(JSOP_TYPEOFEXPR, 197,"typeofexpr", NULL, 1, 1, 1, 15, JOF_BYTE|J /* * Block-local scope support. */ -OPDEF(JSOP_ENTERBLOCK, 198,"enterblock", NULL, 3, 0, -1, 0, JOF_OBJECT) +OPDEF(JSOP_ENTERBLOCK, 198,"enterblock", NULL, 5, 0, -1, 0, JOF_OBJECT) OPDEF(JSOP_LEAVEBLOCK, 199,"leaveblock", NULL, 3, -1, 0, 0, JOF_UINT16) @@ -555,9 +555,9 @@ OPDEF(JSOP_HOLE, 218, "hole", NULL, 1, 0, 1, 0, JOF_BYTE) /* * Variants of JSOP_{DEF{,LOCAL}FUN,LAMBDA} optimized for the flat closure case. */ -OPDEF(JSOP_DEFFUN_FC, 219,"deffun_fc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING) -OPDEF(JSOP_DEFLOCALFUN_FC,220,"deflocalfun_fc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT) -OPDEF(JSOP_LAMBDA_FC, 221,"lambda_fc", NULL, 3, 0, 1, 19, JOF_OBJECT) +OPDEF(JSOP_DEFFUN_FC, 219,"deffun_fc", NULL, 5, 0, 0, 0, JOF_OBJECT|JOF_DECLARING) +OPDEF(JSOP_DEFLOCALFUN_FC,220,"deflocalfun_fc",NULL, 7, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING|JOF_TMPSLOT) +OPDEF(JSOP_LAMBDA_FC, 221,"lambda_fc", NULL, 5, 0, 1, 19, JOF_OBJECT) /* * Joined function object as method optimization support. diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index b7d6ba963ecc..cf56e2f3e5ab 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -1459,11 +1459,8 @@ js_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc) JSOp op = JSOp(*pc); if (js_CodeSpec[op].format & JOF_INDEXBASE) pc += js_CodeSpec[op].length; - if (*pc == JSOP_DEFFUN) { - JSFunction *fun; - GET_FUNCTION_FROM_BYTECODE(script, pc, 0, fun); - return fun->script()->lineno; - } + if (*pc == JSOP_DEFFUN) + return script->getFunction(GET_UINT32_INDEX(pc))->script()->lineno; /* * General case: walk through source notes accumulating their deltas, diff --git a/js/src/jsxdrapi.h b/js/src/jsxdrapi.h index 0665a7922c19..26d92beb1f00 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 - 105) +#define JSXDR_BYTECODE_VERSION (0xb973c0de - 106) /* * Library-private functions. diff --git a/js/src/methodjit/Compiler.cpp b/js/src/methodjit/Compiler.cpp index b95a86c8ccf2..891de3d7f033 100644 --- a/js/src/methodjit/Compiler.cpp +++ b/js/src/methodjit/Compiler.cpp @@ -3019,8 +3019,7 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_DEFFUN) { - uint32_t index = fullAtomIndex(PC); - JSFunction *innerFun = script->getFunction(index); + JSFunction *innerFun = script->getFunction(GET_UINT32_INDEX(PC)); prepareStubCall(Uses(0)); masm.move(ImmPtr(innerFun), Registers::ArgReg1); @@ -3054,7 +3053,7 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_DEFLOCALFUN_FC) { uint32_t slot = GET_SLOTNO(PC); - JSFunction *fun = script->getFunction(fullAtomIndex(&PC[SLOTNO_LEN])); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(PC + SLOTNO_LEN)); prepareStubCall(Uses(frame.frameSlots())); masm.move(ImmPtr(fun), Registers::ArgReg1); INLINE_STUBCALL(stubs::DefLocalFun_FC, REJOIN_DEFLOCALFUN); @@ -3068,7 +3067,7 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_LAMBDA) { - JSFunction *fun = script->getFunction(fullAtomIndex(PC)); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(PC)); JSObjStubFun stub = stubs::Lambda; uint32_t uses = 0; @@ -3134,7 +3133,7 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_DEFLOCALFUN) { uint32_t slot = GET_SLOTNO(PC); - JSFunction *fun = script->getFunction(fullAtomIndex(&PC[SLOTNO_LEN])); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(PC + SLOTNO_LEN)); prepareStubCall(Uses(0)); masm.move(ImmPtr(fun), Registers::ArgReg1); INLINE_STUBCALL(stubs::DefLocalFun, REJOIN_DEFLOCALFUN); @@ -3175,7 +3174,7 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_OBJECT) { - JSObject *object = script->getObject(fullAtomIndex(PC)); + JSObject *object = script->getObject(GET_UINT32_INDEX(PC)); RegisterID reg = frame.allocReg(); masm.move(ImmPtr(object), reg); frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg); @@ -3201,7 +3200,7 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_ENTERBLOCK) BEGIN_CASE(JSOP_ENTERLET0) BEGIN_CASE(JSOP_ENTERLET1) - enterBlock(script->getObject(fullAtomIndex(PC))); + enterBlock(&script->getObject(GET_UINT32_INDEX(PC))->asStaticBlock()); END_CASE(JSOP_ENTERBLOCK); BEGIN_CASE(JSOP_LEAVEBLOCK) @@ -3222,7 +3221,7 @@ mjit::Compiler::generateMethod() BEGIN_CASE(JSOP_LAMBDA_FC) { - JSFunction *fun = script->getFunction(fullAtomIndex(PC)); + JSFunction *fun = script->getFunction(GET_UINT32_INDEX(PC)); prepareStubCall(Uses(frame.frameSlots())); masm.move(ImmPtr(fun), Registers::ArgReg1); INLINE_STUBCALL(stubs::FlatLambda, REJOIN_PUSH_OBJECT); @@ -6798,7 +6797,7 @@ mjit::Compiler::jsop_newinit() * actually a global object). This should only happen in chrome code. */ isArray = false; - baseobj = globalObj ? script->getObject(fullAtomIndex(PC)) : NULL; + baseobj = globalObj ? script->getObject(GET_UINT32_INDEX(PC)) : NULL; break; default: JS_NOT_REACHED("Bad op"); @@ -7215,11 +7214,11 @@ mjit::Compiler::jumpAndRun(Jump j, jsbytecode *target, Jump *slow, bool *trampol } void -mjit::Compiler::enterBlock(JSObject *obj) +mjit::Compiler::enterBlock(StaticBlockObject *block) { /* For now, don't bother doing anything for this opcode. */ frame.syncAndForgetEverything(); - masm.move(ImmPtr(obj), Registers::ArgReg1); + masm.move(ImmPtr(block), Registers::ArgReg1); INLINE_STUBCALL(stubs::EnterBlock, REJOIN_NONE); if (*PC == JSOP_ENTERBLOCK) frame.enterBlock(StackDefs(script, PC)); diff --git a/js/src/methodjit/Compiler.h b/js/src/methodjit/Compiler.h index c2e3319d9f54..6cb7caa1aeac 100644 --- a/js/src/methodjit/Compiler.h +++ b/js/src/methodjit/Compiler.h @@ -683,7 +683,7 @@ private: bool jsop_instanceof(); void jsop_name(PropertyName *name, JSValueType type); bool jsop_xname(PropertyName *name); - void enterBlock(JSObject *obj); + void enterBlock(StaticBlockObject *block); void leaveBlock(); void emitEval(uint32_t argc); void jsop_arguments(RejoinState rejoin); diff --git a/js/src/methodjit/InvokeHelpers.cpp b/js/src/methodjit/InvokeHelpers.cpp index ed7be6efb680..437e9de23860 100644 --- a/js/src/methodjit/InvokeHelpers.cpp +++ b/js/src/methodjit/InvokeHelpers.cpp @@ -626,7 +626,7 @@ js_InternalThrow(VMFrame &f) */ if (cx->isExceptionPending()) { JS_ASSERT(JSOp(*pc) == JSOP_ENTERBLOCK); - StaticBlockObject &blockObj = script->getObject(GET_SLOTNO(pc))->asStaticBlock(); + StaticBlockObject &blockObj = script->getObject(GET_UINT32_INDEX(pc))->asStaticBlock(); Value *vp = cx->regs().sp + blockObj.slotCount(); SetValueRangeToUndefined(cx->regs().sp, vp); cx->regs().sp = vp; From 29b6689cf73d63c41b9418a765f29cf7e6f271f2 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Tue, 31 Jan 2012 16:49:27 -0800 Subject: [PATCH 51/90] Bug 722260 - Properly handle NaN values as keys in Map objects. r=luke --- js/src/builtin/MapObject.cpp | 10 ++--- js/src/tests/Makefile.in | 1 + js/src/tests/ecma_6/Map/NaN-as-key.js | 55 ++++++++++++++++++++++++++ js/src/tests/ecma_6/Map/browser.js | 0 js/src/tests/ecma_6/Map/jstests.list | 2 + js/src/tests/ecma_6/Map/shell.js | 0 js/src/tests/ecma_6/Set/NaN-as-key.js | 56 +++++++++++++++++++++++++++ js/src/tests/ecma_6/Set/browser.js | 0 js/src/tests/ecma_6/Set/jstests.list | 2 + js/src/tests/ecma_6/Set/shell.js | 0 js/src/tests/ecma_6/browser.js | 0 js/src/tests/ecma_6/jstests.list | 2 + js/src/tests/ecma_6/shell.js | 0 js/src/tests/jstests.list | 3 ++ 14 files changed, 124 insertions(+), 7 deletions(-) create mode 100644 js/src/tests/ecma_6/Map/NaN-as-key.js create mode 100644 js/src/tests/ecma_6/Map/browser.js create mode 100644 js/src/tests/ecma_6/Map/jstests.list create mode 100644 js/src/tests/ecma_6/Map/shell.js create mode 100644 js/src/tests/ecma_6/Set/NaN-as-key.js create mode 100644 js/src/tests/ecma_6/Set/browser.js create mode 100644 js/src/tests/ecma_6/Set/jstests.list create mode 100644 js/src/tests/ecma_6/Set/shell.js create mode 100644 js/src/tests/ecma_6/browser.js create mode 100644 js/src/tests/ecma_6/jstests.list create mode 100644 js/src/tests/ecma_6/shell.js diff --git a/js/src/builtin/MapObject.cpp b/js/src/builtin/MapObject.cpp index e923d9f3b064..c7cdd46e3b89 100644 --- a/js/src/builtin/MapObject.cpp +++ b/js/src/builtin/MapObject.cpp @@ -90,14 +90,10 @@ HashableValue::setValue(JSContext *cx, const Value &v) if (JSDOUBLE_IS_INT32(d, &i)) { /* Normalize int32-valued doubles to int32 for faster hashing and testing. */ value = Int32Value(i); + } else if (JSDOUBLE_IS_NaN(d)) { + /* NaNs with different bits must hash and test identically. */ + value = DoubleValue(js_NaN); } else { -#ifdef DEBUG - /* All NaN values are the same. The bit-pattern must reflect this. */ - jsval_layout a, b; - a.asDouble = d; - b.asDouble = JS_CANONICALIZE_NAN(d); - JS_ASSERT(a.asBits == b.asBits); -#endif value = v; } } else { diff --git a/js/src/tests/Makefile.in b/js/src/tests/Makefile.in index fcd479fb76e2..7d2515dc8afb 100644 --- a/js/src/tests/Makefile.in +++ b/js/src/tests/Makefile.in @@ -59,6 +59,7 @@ TEST_FILES = \ ecma_3/ \ ecma_3_1/ \ ecma_5/ \ + ecma_6/ \ js1_1/ \ js1_2/ \ js1_3/ \ diff --git a/js/src/tests/ecma_6/Map/NaN-as-key.js b/js/src/tests/ecma_6/Map/NaN-as-key.js new file mode 100644 index 000000000000..6fa5ee67e9f8 --- /dev/null +++ b/js/src/tests/ecma_6/Map/NaN-as-key.js @@ -0,0 +1,55 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 722260; +var summary = 'All NaNs must be treated as identical keys for Map'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +/* Avoid constant-folding that would happen were |undefined| to be used. */ +var key = -/a/g.missingProperty; + +var m = new Map(); +m.set(key, 17); +assertEq(m.get(key), 17); +assertEq(m.get(-key), 17); +assertEq(m.get(NaN), 17); + +m.delete(-key); +assertEq(m.has(key), false); +assertEq(m.has(-key), false); +assertEq(m.has(NaN), false); + +m.set(-key, 17); +assertEq(m.get(key), 17); +assertEq(m.get(-key), 17); +assertEq(m.get(NaN), 17); + +m.delete(NaN); +assertEq(m.has(key), false); +assertEq(m.has(-key), false); +assertEq(m.has(NaN), false); + +m.set(NaN, 17); +assertEq(m.get(key), 17); +assertEq(m.get(-key), 17); +assertEq(m.get(NaN), 17); + +m.delete(key); +assertEq(m.has(key), false); +assertEq(m.has(-key), false); +assertEq(m.has(NaN), false); + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/ecma_6/Map/browser.js b/js/src/tests/ecma_6/Map/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_6/Map/jstests.list b/js/src/tests/ecma_6/Map/jstests.list new file mode 100644 index 000000000000..bb68abf7d584 --- /dev/null +++ b/js/src/tests/ecma_6/Map/jstests.list @@ -0,0 +1,2 @@ +url-prefix ../../jsreftest.html?test=ecma_6/Map/ +script NaN-as-key.js diff --git a/js/src/tests/ecma_6/Map/shell.js b/js/src/tests/ecma_6/Map/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_6/Set/NaN-as-key.js b/js/src/tests/ecma_6/Set/NaN-as-key.js new file mode 100644 index 000000000000..8c9207e8867d --- /dev/null +++ b/js/src/tests/ecma_6/Set/NaN-as-key.js @@ -0,0 +1,56 @@ +/* + * Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/licenses/publicdomain/ + */ + +//----------------------------------------------------------------------------- +var BUGNUMBER = 722260; +var summary = 'All NaNs must be treated as identical keys for Set'; + +print(BUGNUMBER + ": " + summary); + +/************** + * BEGIN TEST * + **************/ + +/* Avoid constant-folding that would happen were |undefined| to be used. */ +var key = -/a/g.missingProperty; + +var s = new Set(); +s.add(key, 17); +assertEq(s.has(key), true); +assertEq(s.has(-key), true); +assertEq(s.has(NaN), true); + +s.delete(-key); +assertEq(s.has(key), false); +assertEq(s.has(-key), false); +assertEq(s.has(NaN), false); + +s.add(-key, 17); +assertEq(s.has(key), true); +assertEq(s.has(-key), true); +assertEq(s.has(NaN), true); + +s.delete(NaN); +assertEq(s.has(key), false); +assertEq(s.has(-key), false); +assertEq(s.has(NaN), false); + +s.add(NaN, 17); +assertEq(s.has(key), true); +assertEq(s.has(-key), true); +assertEq(s.has(NaN), true); + +s.delete(key); +assertEq(s.has(key), false); +assertEq(s.has(-key), false); +assertEq(s.has(NaN), false); + + +/******************************************************************************/ + +if (typeof reportCompare === "function") + reportCompare(true, true); + +print("Tests complete"); diff --git a/js/src/tests/ecma_6/Set/browser.js b/js/src/tests/ecma_6/Set/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_6/Set/jstests.list b/js/src/tests/ecma_6/Set/jstests.list new file mode 100644 index 000000000000..3c3be4cb98e1 --- /dev/null +++ b/js/src/tests/ecma_6/Set/jstests.list @@ -0,0 +1,2 @@ +url-prefix ../../jsreftest.html?test=ecma_6/Set/ +script NaN-as-key.js diff --git a/js/src/tests/ecma_6/Set/shell.js b/js/src/tests/ecma_6/Set/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_6/browser.js b/js/src/tests/ecma_6/browser.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/ecma_6/jstests.list b/js/src/tests/ecma_6/jstests.list new file mode 100644 index 000000000000..bbf6a436a934 --- /dev/null +++ b/js/src/tests/ecma_6/jstests.list @@ -0,0 +1,2 @@ +include Map/jstests.list +include Set/jstests.list diff --git a/js/src/tests/ecma_6/shell.js b/js/src/tests/ecma_6/shell.js new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/js/src/tests/jstests.list b/js/src/tests/jstests.list index 93253d9a8a9e..823640af51c0 100644 --- a/js/src/tests/jstests.list +++ b/js/src/tests/jstests.list @@ -1,9 +1,12 @@ +# If you add a new test subdirectory here, you *must* add it to Makefile.in as +# well, or tinderbox will go up in flames. YOU HAVE BEEN WARNED. include e4x/jstests.list include ecma/jstests.list include ecma_2/jstests.list include ecma_3/jstests.list include ecma_3_1/jstests.list include ecma_5/jstests.list +include ecma_6/jstests.list include js1_1/jstests.list include js1_2/jstests.list include js1_3/jstests.list From e3c73e0a403b4cb4d744c6c46d9b1495baf80eba Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Wed, 1 Feb 2012 11:27:33 -0800 Subject: [PATCH 52/90] Bug 714129: Add partial unit tests for dwarf2reader::CompilationUnit. r=ted This is really incomplete --- it's just what's needed to get started testing support for the DWARF 4 attribute forms. --- .../common/dwarf/dwarf2reader_die_unittest.cc | 377 ++++++++++++++++++ .../common/dwarf/dwarf2reader_test_common.h | 149 +++++++ 2 files changed, 526 insertions(+) create mode 100644 toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc create mode 100644 toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_test_common.h diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc new file mode 100644 index 000000000000..50c01dd62db1 --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc @@ -0,0 +1,377 @@ +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf2reader_die_unittest.cc: Unit tests for dwarf2reader::CompilationUnit + +#include + +#include +#include +#include + +#include "breakpad_googletest_includes.h" +#include "common/dwarf/bytereader-inl.h" +#include "common/dwarf/dwarf2reader_test_common.h" +#include "common/dwarf/dwarf2reader.h" +#include "google_breakpad/common/breakpad_types.h" + +using google_breakpad::test_assembler::Endianness; +using google_breakpad::test_assembler::Label; +using google_breakpad::test_assembler::Section; +using google_breakpad::test_assembler::kBigEndian; +using google_breakpad::test_assembler::kLittleEndian; + +using dwarf2reader::AttributeList; +using dwarf2reader::ByteReader; +using dwarf2reader::CompilationUnit; +using dwarf2reader::Dwarf2Handler; +using dwarf2reader::DwarfAttribute; +using dwarf2reader::DwarfForm; +using dwarf2reader::DwarfHasChild; +using dwarf2reader::DwarfTag; +using dwarf2reader::ENDIANNESS_BIG; +using dwarf2reader::ENDIANNESS_LITTLE; +using dwarf2reader::SectionMap; + +using std::string; +using std::vector; +using testing::InSequence; +using testing::Pointee; +using testing::Return; +using testing::Sequence; +using testing::Test; +using testing::TestWithParam; +using testing::_; + +class MockDwarf2Handler: public Dwarf2Handler { + public: + MOCK_METHOD5(StartCompilationUnit, bool(uint64 offset, uint8 address_size, + uint8 offset_size, uint64 cu_length, + uint8 dwarf_version)); + MOCK_METHOD3(StartDIE, bool(uint64 offset, enum DwarfTag tag, + const AttributeList& attrs)); + MOCK_METHOD4(ProcessAttributeUnsigned, void(uint64 offset, + DwarfAttribute attr, + enum DwarfForm form, + uint64 data)); + MOCK_METHOD4(ProcessAttributeSigned, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + int64 data)); + MOCK_METHOD4(ProcessAttributeReference, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 data)); + MOCK_METHOD5(ProcessAttributeBuffer, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const char* data, + uint64 len)); + MOCK_METHOD4(ProcessAttributeString, void(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + const std::string& data)); + MOCK_METHOD1(EndDIE, void(uint64 offset)); +}; + +struct DIEFixture { + + DIEFixture() { + // Fix the initial offset of the .debug_info and .debug_abbrev sections. + info.start() = 0; + abbrevs.start() = 0; + + // Default expectations for the data handler. + EXPECT_CALL(handler, StartCompilationUnit(_, _, _, _, _)).Times(0); + EXPECT_CALL(handler, StartDIE(_, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeSigned(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeReference(_, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, _, _, _, _)).Times(0); + EXPECT_CALL(handler, ProcessAttributeString(_, _, _, _)).Times(0); + EXPECT_CALL(handler, EndDIE(_)).Times(0); + } + + // Return a reference to a section map whose .debug_info section refers + // to |info|, and whose .debug_abbrev section refers to |abbrevs|. This + // function returns a reference to the same SectionMap each time; new + // calls wipe out maps established by earlier calls. + const SectionMap &MakeSectionMap() { + // Copy the sections' contents into strings that will live as long as + // the map itself. + assert(info.GetContents(&info_contents)); + assert(abbrevs.GetContents(&abbrevs_contents)); + section_map.clear(); + section_map[".debug_info"].first = info_contents.data(); + section_map[".debug_info"].second = info_contents.size(); + section_map[".debug_abbrev"].first = abbrevs_contents.data(); + section_map[".debug_abbrev"].second = abbrevs_contents.size(); + return section_map; + } + + TestCompilationUnit info; + TestAbbrevTable abbrevs; + MockDwarf2Handler handler; + string abbrevs_contents, info_contents; + SectionMap section_map; +}; + +struct DwarfHeaderParams { + DwarfHeaderParams(Endianness endianness, size_t format_size, + int version, size_t address_size) + : endianness(endianness), format_size(format_size), + version(version), address_size(address_size) { } + Endianness endianness; + size_t format_size; // 4-byte or 8-byte DWARF offsets + int version; + size_t address_size; +}; + +class DwarfHeader: public DIEFixture, + public TestWithParam { }; + +TEST_P(DwarfHeader, Header) { + Label abbrev_table = abbrevs.Here(); + abbrevs.Abbrev(1, dwarf2reader::DW_TAG_compile_unit, + dwarf2reader::DW_children_yes) + .Attribute(dwarf2reader::DW_AT_name, dwarf2reader::DW_FORM_string) + .EndAbbrev() + .EndTable(); + + info.set_format_size(GetParam().format_size); + info.set_endianness(GetParam().endianness); + + info.Header(GetParam().version, abbrev_table, GetParam().address_size) + .ULEB128(1) // DW_TAG_compile_unit, with children + .AppendCString("sam") // DW_AT_name, DW_FORM_string + .D8(0); // end of children + info.Finish(); + + { + InSequence s; + EXPECT_CALL(handler, + StartCompilationUnit(0, GetParam().address_size, + GetParam().format_size, _, + GetParam().version)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, StartDIE(_, dwarf2reader::DW_TAG_compile_unit, _)) + .WillOnce(Return(true)); + EXPECT_CALL(handler, ProcessAttributeString(_, dwarf2reader::DW_AT_name, + dwarf2reader::DW_FORM_string, + "sam")) + .WillOnce(Return()); + EXPECT_CALL(handler, EndDIE(_)) + .WillOnce(Return()); + } + + ByteReader byte_reader(GetParam().endianness == kLittleEndian ? + ENDIANNESS_LITTLE : ENDIANNESS_BIG); + CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler); + EXPECT_EQ(parser.Start(), info_contents.size()); +} + +INSTANTIATE_TEST_CASE_P( + HeaderVariants, DwarfHeader, + ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), + DwarfHeaderParams(kLittleEndian, 4, 2, 8), + DwarfHeaderParams(kLittleEndian, 4, 3, 4), + DwarfHeaderParams(kLittleEndian, 4, 3, 8), + DwarfHeaderParams(kLittleEndian, 4, 4, 4), + DwarfHeaderParams(kLittleEndian, 4, 4, 8), + DwarfHeaderParams(kLittleEndian, 8, 2, 4), + DwarfHeaderParams(kLittleEndian, 8, 2, 8), + DwarfHeaderParams(kLittleEndian, 8, 3, 4), + DwarfHeaderParams(kLittleEndian, 8, 3, 8), + DwarfHeaderParams(kLittleEndian, 8, 4, 4), + DwarfHeaderParams(kLittleEndian, 8, 4, 8), + DwarfHeaderParams(kBigEndian, 4, 2, 4), + DwarfHeaderParams(kBigEndian, 4, 2, 8), + DwarfHeaderParams(kBigEndian, 4, 3, 4), + DwarfHeaderParams(kBigEndian, 4, 3, 8), + DwarfHeaderParams(kBigEndian, 4, 4, 4), + DwarfHeaderParams(kBigEndian, 4, 4, 8), + DwarfHeaderParams(kBigEndian, 8, 2, 4), + DwarfHeaderParams(kBigEndian, 8, 2, 8), + DwarfHeaderParams(kBigEndian, 8, 3, 4), + DwarfHeaderParams(kBigEndian, 8, 3, 8), + DwarfHeaderParams(kBigEndian, 8, 4, 4), + DwarfHeaderParams(kBigEndian, 8, 4, 8))); + +struct DwarfFormsFixture: public DIEFixture { + // Start a compilation unit, as directed by |params|, containing one + // childless DIE of the given tag, with one attribute of the given name + // and form. The 'info' fixture member is left just after the abbrev + // code, waiting for the attribute value to be appended. + void StartSingleAttributeDIE(const DwarfHeaderParams ¶ms, + DwarfTag tag, DwarfAttribute name, + DwarfForm form) { + // Create the abbreviation table. + Label abbrev_table = abbrevs.Here(); + abbrevs.Abbrev(1, tag, dwarf2reader::DW_children_no) + .Attribute(name, form) + .EndAbbrev() + .EndTable(); + + // Create the compilation unit, up to the attribute value. + info.set_format_size(params.format_size); + info.set_endianness(params.endianness); + info.Header(params.version, abbrev_table, params.address_size) + .ULEB128(1); // abbrev code + } + + // Set up handler to expect a compilation unit matching |params|, + // containing one childless DIE of the given tag, in the sequence s. Stop + // just before the expectations. + void ExpectBeginCompilationUnit(const DwarfHeaderParams ¶ms, + DwarfTag tag) { + EXPECT_CALL(handler, + StartCompilationUnit(0, params.address_size, + params.format_size, _, + params.version)) + .InSequence(s) + .WillOnce(Return(true)); + EXPECT_CALL(handler, StartDIE(_, tag, _)) + .InSequence(s) + .WillOnce(Return(true)); + } + + void ExpectEndCompilationUnit() { + EXPECT_CALL(handler, EndDIE(_)) + .InSequence(s) + .WillOnce(Return()); + } + + void ParseCompilationUnit(const DwarfHeaderParams ¶ms) { + ByteReader byte_reader(params.endianness == kLittleEndian ? + ENDIANNESS_LITTLE : ENDIANNESS_BIG); + CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler); + EXPECT_EQ(parser.Start(), info_contents.size()); + } + + // The sequence to which the fixture's methods append expectations. + Sequence s; +}; + +struct DwarfForms: public DwarfFormsFixture, + public TestWithParam { }; + +TEST_P(DwarfForms, addr) { + StartSingleAttributeDIE(GetParam(), dwarf2reader::DW_TAG_compile_unit, + dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr); + u_int64_t value; + if (GetParam().address_size == 4) { + value = 0xc8e9ffcc; + info.D32(value); + } else { + value = 0xe942517fc2768564ULL; + info.D64(value); + } + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), dwarf2reader::DW_TAG_compile_unit); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, dwarf2reader::DW_AT_low_pc, + dwarf2reader::DW_FORM_addr, + value)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, block2_empty) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, + (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2); + info.D16(0); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2, + _, 0)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, block2) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x16e4d2f7, + (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2); + unsigned char data[258]; + memset(data, '*', sizeof(data)); + info.D16(sizeof(data)) + .Append(data, sizeof(data)); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x16e4d2f7); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xe52c4463, + dwarf2reader::DW_FORM_block2, + Pointee('*'), 258)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +// Tests for the other attribute forms could go here. + +INSTANTIATE_TEST_CASE_P( + HeaderVariants, DwarfForms, + ::testing::Values(DwarfHeaderParams(kLittleEndian, 4, 2, 4), + DwarfHeaderParams(kLittleEndian, 4, 2, 8), + DwarfHeaderParams(kLittleEndian, 4, 3, 4), + DwarfHeaderParams(kLittleEndian, 4, 3, 8), + DwarfHeaderParams(kLittleEndian, 4, 4, 4), + DwarfHeaderParams(kLittleEndian, 4, 4, 8), + DwarfHeaderParams(kLittleEndian, 8, 2, 4), + DwarfHeaderParams(kLittleEndian, 8, 2, 8), + DwarfHeaderParams(kLittleEndian, 8, 3, 4), + DwarfHeaderParams(kLittleEndian, 8, 3, 8), + DwarfHeaderParams(kLittleEndian, 8, 4, 4), + DwarfHeaderParams(kLittleEndian, 8, 4, 8), + DwarfHeaderParams(kBigEndian, 4, 2, 4), + DwarfHeaderParams(kBigEndian, 4, 2, 8), + DwarfHeaderParams(kBigEndian, 4, 3, 4), + DwarfHeaderParams(kBigEndian, 4, 3, 8), + DwarfHeaderParams(kBigEndian, 4, 4, 4), + DwarfHeaderParams(kBigEndian, 4, 4, 8), + DwarfHeaderParams(kBigEndian, 8, 2, 4), + DwarfHeaderParams(kBigEndian, 8, 2, 8), + DwarfHeaderParams(kBigEndian, 8, 3, 4), + DwarfHeaderParams(kBigEndian, 8, 3, 8), + DwarfHeaderParams(kBigEndian, 8, 4, 4), + DwarfHeaderParams(kBigEndian, 8, 4, 8))); diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_test_common.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_test_common.h new file mode 100644 index 000000000000..e46931a4e03a --- /dev/null +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_test_common.h @@ -0,0 +1,149 @@ +// -*- mode: c++ -*- + +// Copyright (c) 2012, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Original author: Jim Blandy + +// dwarf2reader_test_common.h: Define TestCompilationUnit and +// TestAbbrevTable, classes for creating properly (and improperly) +// formatted DWARF compilation unit data for unit tests. + +#ifndef COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ +#define COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ + +#include "common/test_assembler.h" +#include "common/dwarf/dwarf2enums.h" + +// A subclass of test_assembler::Section, specialized for constructing +// DWARF compilation units. +class TestCompilationUnit: public google_breakpad::test_assembler::Section { + public: + typedef dwarf2reader::DwarfTag DwarfTag; + typedef dwarf2reader::DwarfAttribute DwarfAttribute; + typedef dwarf2reader::DwarfForm DwarfForm; + typedef google_breakpad::test_assembler::Label Label; + + // Set the section's DWARF format size (the 32-bit DWARF format or the + // 64-bit DWARF format, for lengths and section offsets --- not the + // address size) to format_size. + void set_format_size(size_t format_size) { + assert(format_size == 4 || format_size == 8); + format_size_ = format_size; + } + + // Append a DWARF section offset value, of the appropriate size for this + // compilation unit. + template + void SectionOffset(T offset) { + if (format_size_ == 4) + D32(offset); + else + D64(offset); + } + + // Append a DWARF compilation unit header to the section, with the given + // DWARF version, abbrev table offset, and address size. + TestCompilationUnit &Header(int version, const Label &abbrev_offset, + size_t address_size) { + if (format_size_ == 4) { + D32(length_); + } else { + D32(0xffffffff); + D64(length_); + } + post_length_offset_ = Size(); + D16(version); + SectionOffset(abbrev_offset); + D8(address_size); + return *this; + } + + // Mark the end of this header's DIEs. + TestCompilationUnit &Finish() { + length_ = Size() - post_length_offset_; + return *this; + } + + private: + // The DWARF format size for this compilation unit. + size_t format_size_; + + // The offset of the point in the compilation unit header immediately + // after the initial length field. + u_int64_t post_length_offset_; + + // The length of the compilation unit, not including the initial length field. + Label length_; +}; + +// A subclass of test_assembler::Section specialized for constructing DWARF +// abbreviation tables. +class TestAbbrevTable: public google_breakpad::test_assembler::Section { + public: + typedef dwarf2reader::DwarfTag DwarfTag; + typedef dwarf2reader::DwarfAttribute DwarfAttribute; + typedef dwarf2reader::DwarfForm DwarfForm; + typedef dwarf2reader::DwarfHasChild DwarfHasChild; + typedef google_breakpad::test_assembler::Label Label; + + // Start a new abbreviation table entry for abbreviation code |code|, + // encoding a DIE whose tag is |tag|, and which has children if and only + // if |has_children| is true. + TestAbbrevTable &Abbrev(int code, DwarfTag tag, DwarfHasChild has_children) { + assert(code != 0); + ULEB128(code); + ULEB128(static_cast(tag)); + D8(static_cast(has_children)); + return *this; + }; + + // Add an attribute to the current abbreviation code whose name is |name| + // and whose form is |form|. + TestAbbrevTable &Attribute(DwarfAttribute name, DwarfForm form) { + ULEB128(static_cast(name)); + ULEB128(static_cast(form)); + return *this; + } + + // Finish the current abbreviation code. + TestAbbrevTable &EndAbbrev() { + ULEB128(0); + ULEB128(0); + return *this; + } + + // Finish the current abbreviation table. + TestAbbrevTable &EndTable() { + ULEB128(0); + return *this; + } +}; + +#endif // COMMON_DWARF_DWARF2READER_TEST_COMMON_H__ From 869bbdab6a427f3e2e9b34115c1f3621bb845c79 Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Wed, 1 Feb 2012 11:27:33 -0800 Subject: [PATCH 53/90] Bug 714129: Breakpad DWARF support: Remove extraneous breaks following returns in dwarf2reader.cc. r=ted --- .../src/common/dwarf/dwarf2reader.cc | 50 +++---------------- 1 file changed, 6 insertions(+), 44 deletions(-) diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc index 23f7ffa842f8..2898042fa317 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc @@ -151,41 +151,32 @@ const char* CompilationUnit::SkipAttribute(const char* start, &len)); start += len; return SkipAttribute(start, form); - break; case DW_FORM_data1: case DW_FORM_flag: case DW_FORM_ref1: return start + 1; - break; case DW_FORM_ref2: case DW_FORM_data2: return start + 2; - break; case DW_FORM_ref4: case DW_FORM_data4: return start + 4; - break; case DW_FORM_ref8: case DW_FORM_data8: return start + 8; - break; case DW_FORM_string: return start + strlen(start) + 1; - break; case DW_FORM_udata: case DW_FORM_ref_udata: reader_->ReadUnsignedLEB128(start, &len); return start + len; - break; case DW_FORM_sdata: reader_->ReadSignedLEB128(start, &len); return start + len; - break; case DW_FORM_addr: return start + reader_->AddressSize(); - break; case DW_FORM_ref_addr: // DWARF2 and 3 differ on whether ref_addr is address size or // offset size. @@ -195,27 +186,19 @@ const char* CompilationUnit::SkipAttribute(const char* start, } else if (header_.version == 3) { return start + reader_->OffsetSize(); } - break; case DW_FORM_block1: return start + 1 + reader_->ReadOneByte(start); - break; case DW_FORM_block2: return start + 2 + reader_->ReadTwoBytes(start); - break; case DW_FORM_block4: return start + 4 + reader_->ReadFourBytes(start); - break; case DW_FORM_block: { uint64 size = reader_->ReadUnsignedLEB128(start, &len); return start + size + len; } - break; case DW_FORM_strp: - return start + reader_->OffsetSize(); - break; - default: - fprintf(stderr,"Unhandled form type"); + return start + reader_->OffsetSize(); } fprintf(stderr,"Unhandled form type"); return NULL; @@ -327,85 +310,71 @@ const char* CompilationUnit::ProcessAttribute( &len)); start += len; return ProcessAttribute(dieoffset, start, attr, form); - break; case DW_FORM_data1: case DW_FORM_flag: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadOneByte(start)); return start + 1; - break; case DW_FORM_data2: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadTwoBytes(start)); return start + 2; - break; case DW_FORM_data4: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadFourBytes(start)); return start + 4; - break; case DW_FORM_data8: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadEightBytes(start)); return start + 8; - break; case DW_FORM_string: { const char* str = start; handler_->ProcessAttributeString(dieoffset, attr, form, str); return start + strlen(str) + 1; } - break; case DW_FORM_udata: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadUnsignedLEB128(start, &len)); return start + len; - break; case DW_FORM_sdata: handler_->ProcessAttributeSigned(dieoffset, attr, form, reader_->ReadSignedLEB128(start, &len)); return start + len; - break; case DW_FORM_addr: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadAddress(start)); return start + reader_->AddressSize(); - break; case DW_FORM_ref1: handler_->ProcessAttributeReference(dieoffset, attr, form, reader_->ReadOneByte(start) + offset_from_section_start_); return start + 1; - break; case DW_FORM_ref2: handler_->ProcessAttributeReference(dieoffset, attr, form, reader_->ReadTwoBytes(start) + offset_from_section_start_); return start + 2; - break; case DW_FORM_ref4: handler_->ProcessAttributeReference(dieoffset, attr, form, reader_->ReadFourBytes(start) + offset_from_section_start_); return start + 4; - break; case DW_FORM_ref8: handler_->ProcessAttributeReference(dieoffset, attr, form, reader_->ReadEightBytes(start) + offset_from_section_start_); return start + 8; - break; case DW_FORM_ref_udata: handler_->ProcessAttributeReference(dieoffset, attr, form, reader_->ReadUnsignedLEB128(start, &len) + offset_from_section_start_); return start + len; - break; case DW_FORM_ref_addr: // DWARF2 and 3 differ on whether ref_addr is address size or // offset size. @@ -424,31 +393,27 @@ const char* CompilationUnit::ProcessAttribute( case DW_FORM_block1: { uint64 datalen = reader_->ReadOneByte(start); handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 1, - datalen); + datalen); return start + 1 + datalen; } - break; case DW_FORM_block2: { uint64 datalen = reader_->ReadTwoBytes(start); handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 2, - datalen); + datalen); return start + 2 + datalen; } - break; case DW_FORM_block4: { uint64 datalen = reader_->ReadFourBytes(start); handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + 4, - datalen); + datalen); return start + 4 + datalen; } - break; case DW_FORM_block: { uint64 datalen = reader_->ReadUnsignedLEB128(start, &len); handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + len, - datalen); + datalen); return start + datalen + len; } - break; case DW_FORM_strp: { assert(string_buffer_ != NULL); @@ -460,11 +425,8 @@ const char* CompilationUnit::ProcessAttribute( str); return start + reader_->OffsetSize(); } - break; - default: - fprintf(stderr, "Unhandled form type"); } - fprintf(stderr, "Unhandled form type"); + fprintf(stderr, "Unhandled form type\n"); return NULL; } From 4fe8a051b27a309e5d1aab64d3052b1c80a7a04f Mon Sep 17 00:00:00 2001 From: Jim Blandy Date: Wed, 1 Feb 2012 11:27:33 -0800 Subject: [PATCH 54/90] Bug 714129: Breakpad DWARF: Add support for DWARF 4 attribute forms. r=ted This patch allows Breakpad's DWARF reader to at least read or skip attributes using the new forms defined in version 4 of the DWARF specification, instead of crashing. Attributes encoded using DW_FORM_flag_present, DW_FORM_sec_offset, and DW_FORM_exprloc should work fine now. However, compilation units using DW_FORM_ref_sig8 to refer to types in .debug_types will need further work to support. (GCC 4.6.2 does not emit .debug_types sections.) Specifically: - dwarf2reader::DwarfForm gets new values. - dwarf2reader::Dwarf2Handler and dwarf2reader::DIEHandler get new handler methods, named ProcessAttributeSignature, for DW_FORM_ref_sig8 attributes. - dwarf2reader::CompilationUnit reads DW_FORM_ref_sig8 attributes, and passes them to ProcessAttributeSignature. It also gets support for DW_FORM_sec_offset, DW_FORM_exprloc, and DW_FORM_flag_present, using the existing appropriate ProcessAttribute* methods. - dwarf2reader::DIEDispatcher passes through ProcessAttributeSignature attributes to its DIEHandler. - Unit tests are updated. --- .../src/common/dwarf/dwarf2diehandler.cc | 10 ++ .../src/common/dwarf/dwarf2diehandler.h | 7 ++ .../common/dwarf/dwarf2diehandler_unittest.cc | 13 ++ .../src/common/dwarf/dwarf2enums.h | 8 +- .../src/common/dwarf/dwarf2reader.cc | 21 +++- .../src/common/dwarf/dwarf2reader.h | 9 ++ .../common/dwarf/dwarf2reader_die_unittest.cc | 119 +++++++++++++++++- 7 files changed, 179 insertions(+), 8 deletions(-) diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.cc index 5d01929354ee..86ca060f7ca3 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.cc @@ -183,4 +183,14 @@ void DIEDispatcher::ProcessAttributeString(uint64 offset, current.handler_->ProcessAttributeString(attr, form, data); } +void DIEDispatcher::ProcessAttributeSignature(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signature) { + HandlerStack ¤t = die_handlers_.top(); + // This had better be an attribute of the DIE we were meant to handle. + assert(offset == current.offset_); + current.handler_->ProcessAttributeSignature(attr, form, signature); +} + } // namespace dwarf2reader diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.h index 4c9d7536a856..af2a705dd87c 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.h +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler.h @@ -209,6 +209,9 @@ class DIEHandler { virtual void ProcessAttributeString(enum DwarfAttribute attr, enum DwarfForm form, const string& data) { } + virtual void ProcessAttributeSignature(enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signture) { } // Once we have reported all the DIE's attributes' values, we call // this member function. If it returns false, we skip all the DIE's @@ -314,6 +317,10 @@ class DIEDispatcher: public Dwarf2Handler { enum DwarfAttribute attr, enum DwarfForm form, const string &data); + void ProcessAttributeSignature(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signature); void EndDIE(uint64 offset); private: diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc index 67ccb95f88d9..70a277980b0a 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2diehandler_unittest.cc @@ -65,6 +65,8 @@ class MockDIEHandler: public DIEHandler { void(DwarfAttribute, DwarfForm, const char *, uint64)); MOCK_METHOD3(ProcessAttributeString, void(DwarfAttribute, DwarfForm, const string &)); + MOCK_METHOD3(ProcessAttributeSignature, + void(DwarfAttribute, DwarfForm, uint64)); MOCK_METHOD0(EndAttributes, bool()); MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag, const AttributeList &)); @@ -83,6 +85,8 @@ class MockRootDIEHandler: public RootDIEHandler { void(DwarfAttribute, DwarfForm, const char *, uint64)); MOCK_METHOD3(ProcessAttributeString, void(DwarfAttribute, DwarfForm, const string &)); + MOCK_METHOD3(ProcessAttributeSignature, + void(DwarfAttribute, DwarfForm, uint64)); MOCK_METHOD0(EndAttributes, bool()); MOCK_METHOD3(FindChildHandler, DIEHandler *(uint64, DwarfTag, const AttributeList &)); @@ -238,6 +242,11 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) { (DwarfForm) 0x15762fec, StrEq(str))) .WillOnce(Return()); + EXPECT_CALL(mock_root_handler, + ProcessAttributeSignature((DwarfAttribute) 0x58790d72, + (DwarfForm) 0x4159f138, + 0x94682463613e6a5fULL)) + .WillOnce(Return()); EXPECT_CALL(mock_root_handler, EndAttributes()) .WillOnce(Return(true)); EXPECT_CALL(mock_root_handler, FindChildHandler(_, _, _)) @@ -279,6 +288,10 @@ TEST(Dwarf2DIEHandler, PassAttributeValues) { (DwarfAttribute) 0x310ed065, (DwarfForm) 0x15762fec, str); + die_dispatcher.ProcessAttributeSignature(0xe2222da01e29f2a9LL, + (DwarfAttribute) 0x58790d72, + (DwarfForm) 0x4159f138, + 0x94682463613e6a5fULL); // Finish the root DIE (and thus the CU). die_dispatcher.EndDIE(0xe2222da01e29f2a9LL); diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2enums.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2enums.h index 832a17ca2448..5565d66e12d8 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2enums.h +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2enums.h @@ -143,7 +143,13 @@ enum DwarfForm { DW_FORM_ref4 = 0x13, DW_FORM_ref8 = 0x14, DW_FORM_ref_udata = 0x15, - DW_FORM_indirect = 0x16 + DW_FORM_indirect = 0x16, + + // Added in DWARF 4: + DW_FORM_sec_offset = 0x17, + DW_FORM_exprloc = 0x18, + DW_FORM_flag_present = 0x19, + DW_FORM_ref_sig8 = 0x20 }; // Attribute names and codes diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc index 2898042fa317..9c1141f9f244 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.cc @@ -152,6 +152,8 @@ const char* CompilationUnit::SkipAttribute(const char* start, start += len; return SkipAttribute(start, form); + case DW_FORM_flag_present: + return start; case DW_FORM_data1: case DW_FORM_flag: case DW_FORM_ref1: @@ -164,6 +166,7 @@ const char* CompilationUnit::SkipAttribute(const char* start, return start + 4; case DW_FORM_ref8: case DW_FORM_data8: + case DW_FORM_ref_sig8: return start + 8; case DW_FORM_string: return start + strlen(start) + 1; @@ -193,11 +196,13 @@ const char* CompilationUnit::SkipAttribute(const char* start, return start + 2 + reader_->ReadTwoBytes(start); case DW_FORM_block4: return start + 4 + reader_->ReadFourBytes(start); - case DW_FORM_block: { + case DW_FORM_block: + case DW_FORM_exprloc: { uint64 size = reader_->ReadUnsignedLEB128(start, &len); return start + size + len; } case DW_FORM_strp: + case DW_FORM_sec_offset: return start + reader_->OffsetSize(); } fprintf(stderr,"Unhandled form type"); @@ -311,6 +316,9 @@ const char* CompilationUnit::ProcessAttribute( start += len; return ProcessAttribute(dieoffset, start, attr, form); + case DW_FORM_flag_present: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, 1); + return start; case DW_FORM_data1: case DW_FORM_flag: handler_->ProcessAttributeUnsigned(dieoffset, attr, form, @@ -348,6 +356,10 @@ const char* CompilationUnit::ProcessAttribute( handler_->ProcessAttributeUnsigned(dieoffset, attr, form, reader_->ReadAddress(start)); return start + reader_->AddressSize(); + case DW_FORM_sec_offset: + handler_->ProcessAttributeUnsigned(dieoffset, attr, form, + reader_->ReadOffset(start)); + return start + reader_->OffsetSize(); case DW_FORM_ref1: handler_->ProcessAttributeReference(dieoffset, attr, form, @@ -389,6 +401,10 @@ const char* CompilationUnit::ProcessAttribute( return start + reader_->OffsetSize(); } break; + case DW_FORM_ref_sig8: + handler_->ProcessAttributeSignature(dieoffset, attr, form, + reader_->ReadEightBytes(start)); + return start + 8; case DW_FORM_block1: { uint64 datalen = reader_->ReadOneByte(start); @@ -408,7 +424,8 @@ const char* CompilationUnit::ProcessAttribute( datalen); return start + 4 + datalen; } - case DW_FORM_block: { + case DW_FORM_block: + case DW_FORM_exprloc: { uint64 datalen = reader_->ReadUnsignedLEB128(start, &len); handler_->ProcessAttributeBuffer(dieoffset, attr, form, start + len, datalen); diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.h b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.h index 5a255238f115..b7ba48539836 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.h +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader.h @@ -394,6 +394,15 @@ class Dwarf2Handler { enum DwarfForm form, const string& data) { } + // Called when we have an attribute whose value is the 64-bit signature + // of a type unit in the .debug_types section. OFFSET is the offset of + // the DIE whose attribute we're reporting. ATTR and FORM are the + // attribute's name and form. SIGNATURE is the type unit's signature. + virtual void ProcessAttributeSignature(uint64 offset, + enum DwarfAttribute attr, + enum DwarfForm form, + uint64 signature) { } + // Called when finished processing the DIE at OFFSET. // Because DWARF2/3 specifies a tree of DIEs, you may get starts // before ends of the previous DIE, as we process children before diff --git a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc index 50c01dd62db1..e76fcae77616 100644 --- a/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc +++ b/toolkit/crashreporter/google-breakpad/src/common/dwarf/dwarf2reader_die_unittest.cc @@ -99,6 +99,10 @@ class MockDwarf2Handler: public Dwarf2Handler { enum DwarfAttribute attr, enum DwarfForm form, const std::string& data)); + MOCK_METHOD4(ProcessAttributeSignature, void(uint64 offset, + DwarfAttribute attr, + enum DwarfForm form, + uint64 signature)); MOCK_METHOD1(EndDIE, void(uint64 offset)); }; @@ -251,9 +255,9 @@ struct DwarfFormsFixture: public DIEFixture { // containing one childless DIE of the given tag, in the sequence s. Stop // just before the expectations. void ExpectBeginCompilationUnit(const DwarfHeaderParams ¶ms, - DwarfTag tag) { + DwarfTag tag, uint64 offset=0) { EXPECT_CALL(handler, - StartCompilationUnit(0, params.address_size, + StartCompilationUnit(offset, params.address_size, params.format_size, _, params.version)) .InSequence(s) @@ -269,11 +273,11 @@ struct DwarfFormsFixture: public DIEFixture { .WillOnce(Return()); } - void ParseCompilationUnit(const DwarfHeaderParams ¶ms) { + void ParseCompilationUnit(const DwarfHeaderParams ¶ms, uint64 offset=0) { ByteReader byte_reader(params.endianness == kLittleEndian ? ENDIANNESS_LITTLE : ENDIANNESS_BIG); - CompilationUnit parser(MakeSectionMap(), 0, &byte_reader, &handler); - EXPECT_EQ(parser.Start(), info_contents.size()); + CompilationUnit parser(MakeSectionMap(), offset, &byte_reader, &handler); + EXPECT_EQ(offset + parser.Start(), info_contents.size()); } // The sequence to which the fixture's methods append expectations. @@ -347,6 +351,111 @@ TEST_P(DwarfForms, block2) { ParseCompilationUnit(GetParam()); } +TEST_P(DwarfForms, flag_present) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x3e449ac2, + (DwarfAttribute) 0x359d1972, + dwarf2reader::DW_FORM_flag_present); + // DW_FORM_flag_present occupies no space in the DIE. + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x3e449ac2); + EXPECT_CALL(handler, + ProcessAttributeUnsigned(_, (DwarfAttribute) 0x359d1972, + dwarf2reader::DW_FORM_flag_present, + 1)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, sec_offset) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x1d971689, + (DwarfAttribute) 0xa060bfd1, + dwarf2reader::DW_FORM_sec_offset); + u_int64_t value; + if (GetParam().format_size == 4) { + value = 0xacc9c388; + info.D32(value); + } else { + value = 0xcffe5696ffe3ed0aULL; + info.D64(value); + } + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x1d971689); + EXPECT_CALL(handler, ProcessAttributeUnsigned(_, (DwarfAttribute) 0xa060bfd1, + dwarf2reader::DW_FORM_sec_offset, + value)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, exprloc) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0xb6d167bb, + (DwarfAttribute) 0xba3ae5cb, + dwarf2reader::DW_FORM_exprloc); + info.ULEB128(29) + .Append(29, 173); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0xb6d167bb); + EXPECT_CALL(handler, ProcessAttributeBuffer(_, (DwarfAttribute) 0xba3ae5cb, + dwarf2reader::DW_FORM_exprloc, + Pointee(173), 29)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +TEST_P(DwarfForms, ref_sig8) { + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, + (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8); + info.D64(0xf72fa0cb6ddcf9d6ULL); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b); + EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8, + 0xf72fa0cb6ddcf9d6ULL)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam()); +} + +// A value passed to ProcessAttributeSignature is just an absolute number, +// not an offset within the compilation unit as most of the other +// DW_FORM_ref forms are. Check that the reader doesn't try to apply any +// offset to the signature, by reading it from a compilation unit that does +// not start at the beginning of the section. +TEST_P(DwarfForms, ref_sig8_not_first) { + info.Append(98, '*'); + StartSingleAttributeDIE(GetParam(), (DwarfTag) 0x253e7b2b, + (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8); + info.D64(0xf72fa0cb6ddcf9d6ULL); + info.Finish(); + + ExpectBeginCompilationUnit(GetParam(), (DwarfTag) 0x253e7b2b, 98); + EXPECT_CALL(handler, ProcessAttributeSignature(_, (DwarfAttribute) 0xd708d908, + dwarf2reader::DW_FORM_ref_sig8, + 0xf72fa0cb6ddcf9d6ULL)) + .InSequence(s) + .WillOnce(Return()); + ExpectEndCompilationUnit(); + + ParseCompilationUnit(GetParam(), 98); +} + // Tests for the other attribute forms could go here. INSTANTIATE_TEST_CASE_P( From 9a89878e6dd9d909d191cfc761339d224042f387 Mon Sep 17 00:00:00 2001 From: Sriram Ramasubramanian Date: Wed, 1 Feb 2012 11:42:42 -0800 Subject: [PATCH 55/90] Bug 715260: Web apps support should be hidden as it is not supported now. [r=mfinkle] --- mobile/android/base/AndroidManifest.xml.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/AndroidManifest.xml.in b/mobile/android/base/AndroidManifest.xml.in index d54f87018d66..b1de6a7b0ed8 100644 --- a/mobile/android/base/AndroidManifest.xml.in +++ b/mobile/android/base/AndroidManifest.xml.in @@ -134,15 +134,16 @@ #endif + + --> Date: Wed, 1 Feb 2012 12:10:53 -0800 Subject: [PATCH 56/90] Back out b35af982b085 (bug 721080) for android-xul bustage --- mobile/android/base/GeckoAppShell.java | 4 ++-- mobile/android/base/gfx/LayerController.java | 16 +++++++--------- widget/android/AndroidBridge.cpp | 6 +++--- widget/android/AndroidBridge.h | 4 ++-- widget/android/nsWindow.cpp | 5 ++--- 5 files changed, 16 insertions(+), 19 deletions(-) diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 2f3157f73820..630cf8953cfd 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -1121,11 +1121,11 @@ public class GeckoAppShell }); } - public static void setPreventPanning(final boolean aPreventPanning) { + public static void preventPanning() { getMainHandler().post(new Runnable() { public void run() { LayerController layerController = GeckoApp.mAppContext.getLayerController(); - layerController.preventPanning(aPreventPanning); + layerController.preventPanning(true); } }); } diff --git a/mobile/android/base/gfx/LayerController.java b/mobile/android/base/gfx/LayerController.java index 37b02d7d5f8a..950ec217d3cb 100644 --- a/mobile/android/base/gfx/LayerController.java +++ b/mobile/android/base/gfx/LayerController.java @@ -441,15 +441,13 @@ public class LayerController { allowDefaultTimer.purge(); allowDefaultTimer = null; } - if (aValue == allowDefaultActions) { - allowDefaultActions = !aValue; - - if (aValue) { - mView.clearEventQueue(); - mPanZoomController.cancelTouch(); - } else { - mView.processEventQueue(); - } + allowDefaultActions = !aValue; + + if (aValue) { + mView.clearEventQueue(); + mPanZoomController.cancelTouch(); + } else { + mView.processEventQueue(); } } diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index 64127c3d7d20..d9b1c75af395 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -125,7 +125,7 @@ AndroidBridge::Init(JNIEnv *jEnv, jGetDpi = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getDpi", "()I"); jSetFullScreen = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setFullScreen", "(Z)V"); jShowInputMethodPicker = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showInputMethodPicker", "()V"); - jSetPreventPanning = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setPreventPanning", "(Z)V"); + jPreventPanning = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "preventPanning", "()V"); jHideProgressDialog = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "hideProgressDialog", "()V"); jPerformHapticFeedback = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "performHapticFeedback", "(Z)V"); jVibrate1 = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "vibrate", "(J)V"); @@ -1806,13 +1806,13 @@ NS_IMETHODIMP nsAndroidBridge::SetDrawMetadataProvider(nsIAndroidDrawMetadataPro } void -AndroidBridge::SetPreventPanning(bool aPreventPanning) { +AndroidBridge::PreventPanning() { ALOG_BRIDGE("AndroidBridge::PreventPanning"); JNIEnv *env = GetJNIEnv(); if (!env) return; - env->CallStaticVoidMethod(mGeckoAppShellClass, jSetPreventPanning, (jboolean)aPreventPanning); + env->CallStaticVoidMethod(mGeckoAppShellClass, jPreventPanning); } diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index a236649fa003..9d0d2f164d5b 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -230,7 +230,7 @@ public: void ShowInputMethodPicker(); - void SetPreventPanning(bool aPreventPanning); + void PreventPanning(); void HideProgressDialogOnce(); @@ -439,7 +439,7 @@ protected: jmethodID jGetDpi; jmethodID jSetFullScreen; jmethodID jShowInputMethodPicker; - jmethodID jSetPreventPanning; + jmethodID jPreventPanning; jmethodID jHideProgressDialog; jmethodID jPerformHapticFeedback; jmethodID jVibrate1; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 5f417a5e4ce4..55a9ce3b7850 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1579,9 +1579,8 @@ nsWindow::DispatchMultitouchEvent(nsTouchEvent &event, AndroidGeckoEvent *ae) nsEventStatus status; DispatchEvent(&event, status); - bool preventPanning = (status == nsEventStatus_eConsumeNoDefault); - if (preventPanning || action == AndroidMotionEvent::ACTION_MOVE) { - AndroidBridge::Bridge()->SetPreventPanning(preventPanning); + if (status == nsEventStatus_eConsumeNoDefault) { + AndroidBridge::Bridge()->PreventPanning(); return true; } return false; From 791306c8796cd246dbea3204667598308392aeca Mon Sep 17 00:00:00 2001 From: Jeff Gilbert Date: Wed, 1 Feb 2012 12:46:07 -0800 Subject: [PATCH 57/90] Bug 711642 - Deguarantee that ResizeOffscreenFBO sets viewport the first time it's run - r=bjacob --- gfx/gl/GLContext.cpp | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/gfx/gl/GLContext.cpp b/gfx/gl/GLContext.cpp index 7c4b88319a55..88f03542d6be 100644 --- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -1108,8 +1108,6 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize, const bool aUseReadFBO, c return true; } - const bool firstTime = (mOffscreenDrawFBO == 0 && mOffscreenReadFBO == 0); - GLuint curBoundFramebufferDraw = 0; GLuint curBoundFramebufferRead = 0; GLuint curBoundRenderbuffer = 0; @@ -1412,8 +1410,7 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize, const bool aUseReadFBO, c #ifdef DEBUG if (DebugMode()) { - printf_stderr("%s %dx%d offscreen FBO: r: %d g: %d b: %d a: %d depth: %d stencil: %d samples: %d\n", - firstTime ? "Created" : "Resized", + printf_stderr("Resized %dx%d offscreen FBO: r: %d g: %d b: %d a: %d depth: %d stencil: %d samples: %d\n", mOffscreenActualSize.width, mOffscreenActualSize.height, mActualFormat.red, mActualFormat.green, mActualFormat.blue, mActualFormat.alpha, mActualFormat.depth, mActualFormat.stencil, mActualFormat.samples); @@ -1429,13 +1426,7 @@ GLContext::ResizeOffscreenFBO(const gfxIntSize& aSize, const bool aUseReadFBO, c BindReadFBO(curBoundFramebufferRead); fBindTexture(LOCAL_GL_TEXTURE_2D, curBoundTexture); fBindRenderbuffer(LOCAL_GL_RENDERBUFFER, curBoundRenderbuffer); - - // -don't- restore the viewport the first time through this, since - // the previous one isn't valid. - if (firstTime) - fViewport(0, 0, aSize.width, aSize.height); // XXX This is coming out in 711642 - else - fViewport(viewport[0], viewport[1], viewport[2], viewport[3]); + fViewport(viewport[0], viewport[1], viewport[2], viewport[3]); return true; } From ac75a17316a5045d4e8b7d1afd653cb133f1757e Mon Sep 17 00:00:00 2001 From: Igor Bukanov Date: Tue, 31 Jan 2012 23:28:22 +0100 Subject: [PATCH 58/90] bug 723021 - moving native stack limits into runtime. r=luke --- content/base/src/nsFrameMessageManager.cpp | 2 - dom/workers/RuntimeService.cpp | 4 +- js/src/jsapi-tests/tests.h | 40 ++++++++------- js/src/jsapi.cpp | 52 +++++++++++--------- js/src/jsapi.h | 8 +-- js/src/jscntxt.cpp | 9 +--- js/src/jscntxt.h | 29 ++++------- js/src/jsfriendapi.cpp | 6 --- js/src/jsfriendapi.h | 45 +++++++++++++++-- js/src/jsgc.cpp | 4 +- js/src/jsnativestack.h | 8 +-- js/src/jsxml.cpp | 2 +- js/src/shell/js.cpp | 3 +- js/xpconnect/loader/mozJSComponentLoader.cpp | 3 -- js/xpconnect/src/XPCJSRuntime.cpp | 3 +- 15 files changed, 116 insertions(+), 102 deletions(-) diff --git a/content/base/src/nsFrameMessageManager.cpp b/content/base/src/nsFrameMessageManager.cpp index 4e9884107679..d43aa1fe21f0 100644 --- a/content/base/src/nsFrameMessageManager.cpp +++ b/content/base/src/nsFrameMessageManager.cpp @@ -866,8 +866,6 @@ nsFrameScriptExecutor::InitTabChildGlobalInternal(nsISupports* aScope) nsContentUtils::GetSecurityManager()->GetSystemPrincipal(getter_AddRefs(mPrincipal)); - JS_SetNativeStackQuota(cx, 128 * sizeof(size_t) * 1024); - JS_SetOptions(cx, JS_GetOptions(cx) | JSOPTION_PRIVATE_IS_NSISUPPORTS); JS_SetVersion(cx, JSVERSION_LATEST); JS_SetErrorReporter(cx, ContentScriptErrorReporter); diff --git a/dom/workers/RuntimeService.cpp b/dom/workers/RuntimeService.cpp index bf140fa78e76..8fc13c5f4ecc 100644 --- a/dom/workers/RuntimeService.cpp +++ b/dom/workers/RuntimeService.cpp @@ -293,6 +293,8 @@ CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate) JS_SetGCParameter(runtime, JSGC_MAX_BYTES, aWorkerPrivate->GetJSRuntimeHeapSize()); + JS_SetNativeStackQuota(runtime, WORKER_CONTEXT_NATIVE_STACK_LIMIT); + JSContext* workerCx = JS_NewContext(runtime, 0); if (!workerCx) { JS_DestroyRuntime(runtime); @@ -306,8 +308,6 @@ CreateJSContextForWorker(WorkerPrivate* aWorkerPrivate) JS_SetOperationCallback(workerCx, OperationCallback); - JS_SetNativeStackQuota(workerCx, WORKER_CONTEXT_NATIVE_STACK_LIMIT); - NS_ASSERTION((aWorkerPrivate->GetJSContextOptions() & kRequiredJSContextOptions) == kRequiredJSContextOptions, "Somehow we lost our required options!"); diff --git a/js/src/jsapi-tests/tests.h b/js/src/jsapi-tests/tests.h index 867816151b11..5868b0831528 100644 --- a/js/src/jsapi-tests/tests.h +++ b/js/src/jsapi-tests/tests.h @@ -338,7 +338,25 @@ class JSAPITest } virtual JSRuntime * createRuntime() { - return JS_NewRuntime(8L * 1024 * 1024); + JSRuntime *rt = JS_NewRuntime(8L * 1024 * 1024); + if (!rt) + return NULL; + + const size_t MAX_STACK_SIZE = +/* Assume we can't use more than 5e5 bytes of C stack by default. */ +#if (defined(DEBUG) && defined(__SUNPRO_CC)) || defined(JS_CPU_SPARC) + /* + * Sun compiler uses a larger stack space for js::Interpret() with + * debug. Use a bigger gMaxStackSize to make "make check" happy. + */ + 5000000 +#else + 500000 +#endif + ; + + JS_SetNativeStackQuota(rt, MAX_STACK_SIZE); + return rt; } virtual void destroyRuntime() { @@ -358,22 +376,6 @@ class JSAPITest JSContext *cx = JS_NewContext(rt, 8192); if (!cx) return NULL; - - const size_t MAX_STACK_SIZE = -/* Assume we can't use more than 5e5 bytes of C stack by default. */ -#if (defined(DEBUG) && defined(__SUNPRO_CC)) || defined(JS_CPU_SPARC) - /* - * Sun compiler uses a larger stack space for js::Interpret() with - * debug. Use a bigger gMaxStackSize to make "make check" happy. - */ - 5000000 -#else - 500000 -#endif - ; - - JS_SetNativeStackQuota(cx, MAX_STACK_SIZE); - JS_SetOptions(cx, JSOPTION_VAROBJFIX); JS_SetVersion(cx, JSVERSION_LATEST); JS_SetErrorReporter(cx, &reportError); @@ -432,7 +434,7 @@ class JSAPITest /* * A class for creating and managing one temporary file. - * + * * We could use the ISO C temporary file functions here, but those try to * create files in the root directory on Windows, which fails for users * without Administrator privileges. @@ -463,7 +465,7 @@ class TempFile { fprintf(stderr, "error opening temporary file '%s': %s\n", fileName, strerror(errno)); exit(1); - } + } name = fileName; return stream; } diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index a58fc70f0176..70764190d052 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -694,8 +694,7 @@ JS_IsBuiltinFunctionConstructor(JSFunction *fun) static JSBool js_NewRuntimeWasCalled = JS_FALSE; JSRuntime::JSRuntime() - : interrupt(0), - atomsCompartment(NULL), + : atomsCompartment(NULL), #ifdef JS_THREADSAFE ownerThread_(NULL), #endif @@ -703,6 +702,8 @@ JSRuntime::JSRuntime() execAlloc_(NULL), bumpAlloc_(NULL), repCache_(NULL), + nativeStackBase(0), + nativeStackQuota(0), interpreterFrames(NULL), cxCallback(NULL), compartmentCallback(NULL), @@ -795,6 +796,10 @@ JSRuntime::JSRuntime() PodZero(&globalDebugHooks); PodZero(&atomState); + +#if JS_STACK_GROWTH_DIRECTION > 0 + nativeStackLimit = UINTPTR_MAX; +#endif } bool @@ -834,7 +839,7 @@ JSRuntime::init(uint32_t maxbytes) if (!stackSpace.init()) return false; - conservativeGC.nativeStackBase = GetNativeStackBase(); + nativeStackBase = GetNativeStackBase(); return true; } @@ -883,7 +888,9 @@ JSRuntime::setOwnerThread() JS_ASSERT(ownerThread_ == (void *)0xc1ea12); /* "clear" */ JS_ASSERT(requestDepth == 0); ownerThread_ = PR_GetCurrentThread(); - conservativeGC.nativeStackBase = GetNativeStackBase(); + nativeStackBase = GetNativeStackBase(); + if (nativeStackQuota) + JS_SetNativeStackQuota(this, nativeStackQuota); } void @@ -892,7 +899,12 @@ JSRuntime::clearOwnerThread() JS_ASSERT(onOwnerThread()); JS_ASSERT(requestDepth == 0); ownerThread_ = (void *)0xc1ea12; /* "clear" */ - conservativeGC.nativeStackBase = 0; + nativeStackBase = 0; +#if JS_STACK_GROWTH_DIRECTION > 0 + nativeStackLimit = UINTPTR_MAX; +#else + nativeStackLimit = 0; +#endif } JS_FRIEND_API(bool) @@ -3002,33 +3014,25 @@ JS_GetExternalStringClosure(JSContext *cx, JSString *str) } JS_PUBLIC_API(void) -JS_SetThreadStackLimit(JSContext *cx, uintptr_t limitAddr) -{ -#if JS_STACK_GROWTH_DIRECTION > 0 - if (limitAddr == 0) - limitAddr = UINTPTR_MAX; -#endif - cx->stackLimit = limitAddr; -} - -JS_PUBLIC_API(void) -JS_SetNativeStackQuota(JSContext *cx, size_t stackSize) +JS_SetNativeStackQuota(JSRuntime *rt, size_t stackSize) { + rt->nativeStackQuota = stackSize; + if (!rt->nativeStackBase) + return; + #if JS_STACK_GROWTH_DIRECTION > 0 if (stackSize == 0) { - cx->stackLimit = UINTPTR_MAX; + rt->nativeStackLimit = UINTPTR_MAX; } else { - uintptr_t stackBase = cx->runtime->nativeStackBase; - JS_ASSERT(stackBase <= size_t(-1) - stackSize); - cx->stackLimit = stackBase + stackSize - 1; + JS_ASSERT(rt->nativeStackBase <= size_t(-1) - stackSize); + rt->nativeStackLimit = rt->nativeStackBase + stackSize - 1; } #else if (stackSize == 0) { - cx->stackLimit = 0; + rt->nativeStackLimit = 0; } else { - uintptr_t stackBase = uintptr_t(cx->runtime->conservativeGC.nativeStackBase); - JS_ASSERT(stackBase >= stackSize); - cx->stackLimit = stackBase - (stackSize - 1); + JS_ASSERT(rt->nativeStackBase >= stackSize); + rt->nativeStackLimit = rt->nativeStackBase - (stackSize - 1); } #endif } diff --git a/js/src/jsapi.h b/js/src/jsapi.h index 2f0fb8b39169..3ebf7295e523 100644 --- a/js/src/jsapi.h +++ b/js/src/jsapi.h @@ -3370,18 +3370,12 @@ JS_IsExternalString(JSContext *cx, JSString *str); extern JS_PUBLIC_API(void *) JS_GetExternalStringClosure(JSContext *cx, JSString *str); -/* - * Deprecated. Use JS_SetNativeStackQuoata instead. - */ -extern JS_PUBLIC_API(void) -JS_SetThreadStackLimit(JSContext *cx, uintptr_t limitAddr); - /* * Set the size of the native stack that should not be exceed. To disable * stack size checking pass 0. */ extern JS_PUBLIC_API(void) -JS_SetNativeStackQuota(JSContext *cx, size_t stackSize); +JS_SetNativeStackQuota(JSRuntime *cx, size_t stackSize); /************************************************************************/ diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 648fd49f7eb9..f62fe9bdd20d 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -963,7 +963,8 @@ DSTOffsetCache::DSTOffsetCache() } JSContext::JSContext(JSRuntime *rt) - : defaultVersion(JSVERSION_DEFAULT), + : ContextFriendFields(rt), + defaultVersion(JSVERSION_DEFAULT), hasVersionOverride(false), throwing(false), exception(UndefinedValue()), @@ -972,12 +973,6 @@ JSContext::JSContext(JSRuntime *rt) localeCallbacks(NULL), resolvingList(NULL), generatingError(false), -#if JS_STACK_GROWTH_DIRECTION > 0 - stackLimit(UINTPTR_MAX), -#else - stackLimit(0), -#endif - runtime(rt), compartment(NULL), stack(thisDuringConstruction()), /* depends on cx->thread_ */ parseMapPool_(NULL), diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 3c18606fe5db..b1e88be38bd2 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -127,9 +127,6 @@ typedef Vector ScriptOpcodeCountsV struct ConservativeGCData { - /* Base address of the native stack for the current thread. */ - uintptr_t *nativeStackBase; - /* * The GC scans conservatively between ThreadData::nativeStackBase and * nativeStackTop unless the latter is NULL. @@ -149,7 +146,7 @@ struct ConservativeGCData unsigned requestThreshold; ConservativeGCData() - : nativeStackBase(NULL), nativeStackTop(NULL), requestThreshold(0) + : nativeStackTop(NULL), requestThreshold(0) {} ~ConservativeGCData() { @@ -180,14 +177,8 @@ struct ConservativeGCData } /* namespace js */ -struct JSRuntime +struct JSRuntime : js::RuntimeFriendFields { - /* - * If non-zero, we were been asked to call the operation callback as soon - * as possible. - */ - volatile int32_t interrupt; - /* Default compartment. */ JSCompartment *atomsCompartment; @@ -243,6 +234,12 @@ struct JSRuntime return repCache_ ? repCache_ : createRegExpPrivateCache(cx); } + /* Base address of the native stack for the current thread. */ + uintptr_t nativeStackBase; + + /* The native stack size limit that runtime should not exceed. */ + size_t nativeStackQuota; + /* * Frames currently running in js::Interpret. See InterpreterFrames for * details. @@ -428,7 +425,7 @@ struct JSRuntime bool hasContexts() const { return !JS_CLIST_IS_EMPTY(&contextList); } - + /* Per runtime debug hooks -- see jsprvtd.h and jsdbgapi.h. */ JSDebugHooks globalDebugHooks; @@ -764,7 +761,7 @@ typedef HashSetcompartments; } -JS_FRIEND_API(uintptr_t) -GetContextStackLimit(const JSContext *cx) -{ - return cx->stackLimit; -} - JS_FRIEND_API(size_t) SizeOfJSContext() { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index f178d6f207ab..93d5eaf26e5e 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -156,6 +156,42 @@ struct PRLock; namespace js { +struct ContextFriendFields { + JSRuntime *const runtime; + + ContextFriendFields(JSRuntime *rt) + : runtime(rt) { } + + static const ContextFriendFields *get(const JSContext *cx) { + return reinterpret_cast(cx); + } +}; + +struct RuntimeFriendFields { + /* + * If non-zero, we were been asked to call the operation callback as soon + * as possible. + */ + volatile int32_t interrupt; + + /* Limit pointer for checking native stack consumption. */ + uintptr_t nativeStackLimit; + + RuntimeFriendFields() + : interrupt(0), + nativeStackLimit(0) { } + + static const RuntimeFriendFields *get(const JSRuntime *rt) { + return reinterpret_cast(rt); + } +}; + +inline JSRuntime * +GetRuntime(const JSContext *cx) +{ + return ContextFriendFields::get(cx)->runtime; +} + typedef bool (* PreserveWrapperCallback)(JSContext *cx, JSObject *obj); @@ -228,7 +264,7 @@ struct WeakMapTracer { JSContext *context; WeakMapTraceCallback callback; - WeakMapTracer(JSContext *cx, WeakMapTraceCallback cb) + WeakMapTracer(JSContext *cx, WeakMapTraceCallback cb) : context(cx), callback(cb) {} }; @@ -440,8 +476,11 @@ IsObjectInContextCompartment(const JSObject *obj, const JSContext *cx); #define JSITER_OWNONLY 0x8 /* iterate over obj's own properties only */ #define JSITER_HIDDEN 0x10 /* also enumerate non-enumerable properties */ -JS_FRIEND_API(uintptr_t) -GetContextStackLimit(const JSContext *cx); +inline uintptr_t +GetContextStackLimit(const JSContext *cx) +{ + return RuntimeFriendFields::get(GetRuntime(cx))->nativeStackLimit; +} #define JS_CHECK_RECURSION(cx, onerror) \ JS_BEGIN_MACRO \ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 90fdff5fdfba..24dbf4a17efd 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -1092,11 +1092,11 @@ MarkConservativeStackRoots(JSTracer *trc, JSRuntime *rt) uintptr_t *stackMin, *stackEnd; #if JS_STACK_GROWTH_DIRECTION > 0 - stackMin = rt->conservativeGC.nativeStackBase; + stackMin = rt->nativeStackBase; stackEnd = cgcd->nativeStackTop; #else stackMin = cgcd->nativeStackTop + 1; - stackEnd = rt->conservativeGC.nativeStackBase; + stackEnd = reinterpret_cast(rt->nativeStackBase); #endif JS_ASSERT(stackMin <= stackEnd); diff --git a/js/src/jsnativestack.h b/js/src/jsnativestack.h index a576960faf5d..2f41b6434309 100644 --- a/js/src/jsnativestack.h +++ b/js/src/jsnativestack.h @@ -47,12 +47,12 @@ namespace js { extern void * GetNativeStackBaseImpl(); -inline uintptr_t * +inline uintptr_t GetNativeStackBase() { - void *stackBase = GetNativeStackBaseImpl(); - JS_ASSERT(reinterpret_cast(stackBase) % sizeof(void *) == 0); - return static_cast(stackBase); + uintptr_t stackBase = reinterpret_cast(GetNativeStackBaseImpl()); + JS_ASSERT(stackBase % sizeof(void *) == 0); + return stackBase; } } /* namespace js */ diff --git a/js/src/jsxml.cpp b/js/src/jsxml.cpp index cdfe38ab2bba..6871fe004895 100644 --- a/js/src/jsxml.cpp +++ b/js/src/jsxml.cpp @@ -1332,7 +1332,7 @@ ParseNodeToXML(Parser *parser, ParseNode *pn, JSXMLClass xml_class; int stackDummy; - if (!JS_CHECK_STACK_SIZE(cx->stackLimit, &stackDummy)) { + if (!JS_CHECK_STACK_SIZE(cx->runtime->nativeStackLimit, &stackDummy)) { ReportCompileErrorNumber(cx, &parser->tokenStream, pn, JSREPORT_ERROR, JSMSG_OVER_RECURSED); return NULL; diff --git a/js/src/shell/js.cpp b/js/src/shell/js.cpp index a0fef3142b3d..638bca0fb9c2 100644 --- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -388,7 +388,6 @@ ShellOperationCallback(JSContext *cx) static void SetContextOptions(JSContext *cx) { - JS_SetNativeStackQuota(cx, gMaxStackSize); JS_SetOperationCallback(cx, ShellOperationCallback); } @@ -5445,6 +5444,8 @@ main(int argc, char **argv, char **envp) JS_SetTrustedPrincipals(rt, &shellTrustedPrincipals); JS_SetRuntimeSecurityCallbacks(rt, &securityCallbacks); + JS_SetNativeStackQuota(rt, gMaxStackSize); + if (!InitWatchdog(rt)) return 1; diff --git a/js/xpconnect/loader/mozJSComponentLoader.cpp b/js/xpconnect/loader/mozJSComponentLoader.cpp index 151905c08e16..86dbd533fc6c 100644 --- a/js/xpconnect/loader/mozJSComponentLoader.cpp +++ b/js/xpconnect/loader/mozJSComponentLoader.cpp @@ -446,9 +446,6 @@ mozJSComponentLoader::ReallyInit() // Always use the latest js version JS_SetVersion(mContext, JSVERSION_LATEST); - // Limit C stack consumption to a reasonable 512K - JS_SetNativeStackQuota(mContext, 512 * 1024); - nsCOMPtr secman = do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID); if (!secman) diff --git a/js/xpconnect/src/XPCJSRuntime.cpp b/js/xpconnect/src/XPCJSRuntime.cpp index ba8bcb5092ca..f62686922f28 100644 --- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -1966,6 +1966,7 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* aXPConnect) // to cause period, and we hope hygienic, last-ditch GCs from within // the GC's allocator. JS_SetGCParameter(mJSRuntime, JSGC_MAX_BYTES, 0xffffffff); + JS_SetNativeStackQuota(mJSRuntime, 128 * sizeof(size_t) * 1024); JS_SetContextCallback(mJSRuntime, ContextCallback); JS_SetCompartmentCallback(mJSRuntime, CompartmentCallback); JS_SetGCCallbackRT(mJSRuntime, GCCallback); @@ -2090,8 +2091,6 @@ XPCJSRuntime::OnJSContextNew(JSContext *cx) if (!xpc) return false; - JS_SetNativeStackQuota(cx, 128 * sizeof(size_t) * 1024); - // we want to mark the global object ourselves since we use a different color JS_ToggleOptions(cx, JSOPTION_UNROOTED_GLOBAL); From a22d53f4242d4693a7811265a416a4a4e41779f7 Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Wed, 1 Feb 2012 14:32:25 -0500 Subject: [PATCH 59/90] bug 723151 - No drawable resources defined for eclair r=mfinkle f=sriram --HG-- rename : mobile/android/base/resources/drawable-hdpi-v8/abouthome_icon.png => mobile/android/base/resources/drawable-hdpi/abouthome_icon.png rename : mobile/android/base/resources/drawable-hdpi-v8/abouthome_logo.png => mobile/android/base/resources/drawable-hdpi/abouthome_logo.png rename : mobile/android/base/resources/drawable-hdpi-v8/abouthome_separator.9.png => mobile/android/base/resources/drawable-hdpi/abouthome_separator.9.png rename : mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_bg.9.png => mobile/android/base/resources/drawable-hdpi/abouthome_sync_bg.9.png rename : mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_logo.png => mobile/android/base/resources/drawable-hdpi/abouthome_sync_logo.png rename : mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_pressed_bg.9.png => mobile/android/base/resources/drawable-hdpi/abouthome_sync_pressed_bg.9.png rename : mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_placeholder.png => mobile/android/base/resources/drawable-hdpi/abouthome_topsite_placeholder.png rename : mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_shadow.9.png => mobile/android/base/resources/drawable-hdpi/abouthome_topsite_shadow.9.png rename : mobile/android/base/resources/drawable-hdpi-v8/address_bar_texture.png => mobile/android/base/resources/drawable-hdpi/address_bar_texture.png rename : mobile/android/base/resources/drawable-hdpi-v8/address_bar_url_bg.9.png => mobile/android/base/resources/drawable-hdpi/address_bar_url_bg.9.png rename : mobile/android/base/resources/drawable-hdpi-v8/address_bar_url_outline.9.png => mobile/android/base/resources/drawable-hdpi/address_bar_url_outline.9.png rename : mobile/android/base/resources/drawable-hdpi-v8/awesomebar_tab.9.png => mobile/android/base/resources/drawable-hdpi/awesomebar_tab.9.png rename : mobile/android/base/resources/drawable-hdpi-v8/awesomebar_tab_pressed.9.png => mobile/android/base/resources/drawable-hdpi/awesomebar_tab_pressed.9.png rename : mobile/android/base/resources/drawable-hdpi-v8/doorhanger_arrow.png => mobile/android/base/resources/drawable-hdpi/doorhanger_arrow.png rename : mobile/android/base/resources/drawable-hdpi-v8/doorhanger_bg.9.png => mobile/android/base/resources/drawable-hdpi/doorhanger_bg.9.png rename : mobile/android/base/resources/drawable-hdpi-v8/doorhanger_popup_bg.9.png => mobile/android/base/resources/drawable-hdpi/doorhanger_popup_bg.9.png rename : mobile/android/base/resources/drawable-hdpi-v8/doorhanger_shadow_bg.9.png => mobile/android/base/resources/drawable-hdpi/doorhanger_shadow_bg.9.png rename : mobile/android/base/resources/drawable-hdpi-v8/favicon.png => mobile/android/base/resources/drawable-hdpi/favicon.png rename : mobile/android/base/resources/drawable-hdpi-v8/home_bg.png => mobile/android/base/resources/drawable-hdpi/home_bg.png rename : mobile/android/base/resources/drawable-hdpi-v8/home_star.png => mobile/android/base/resources/drawable-hdpi/home_star.png rename : mobile/android/base/resources/drawable-hdpi-v8/ic_addons_empty.png => mobile/android/base/resources/drawable-hdpi/ic_addons_empty.png rename : mobile/android/base/resources/drawable-hdpi-v8/ic_awesomebar_go.png => mobile/android/base/resources/drawable-hdpi/ic_awesomebar_go.png rename : mobile/android/base/resources/drawable-hdpi-v8/ic_awesomebar_search.png => mobile/android/base/resources/drawable-hdpi/ic_awesomebar_search.png rename : mobile/android/base/resources/drawable-hdpi-v8/ic_menu_bookmark_add.png => mobile/android/base/resources/drawable-hdpi/ic_menu_bookmark_add.png rename : mobile/android/base/resources/drawable-hdpi-v8/ic_menu_bookmark_remove.png => mobile/android/base/resources/drawable-hdpi/ic_menu_bookmark_remove.png rename : mobile/android/base/resources/drawable-hdpi-v8/ic_menu_find_in_page.png => mobile/android/base/resources/drawable-hdpi/ic_menu_find_in_page.png rename : mobile/android/base/resources/drawable-hdpi-v8/ic_menu_forward.png => mobile/android/base/resources/drawable-hdpi/ic_menu_forward.png rename : mobile/android/base/resources/drawable-hdpi-v8/ic_menu_reload.png => mobile/android/base/resources/drawable-hdpi/ic_menu_reload.png rename : mobile/android/base/resources/drawable-hdpi-v8/ic_menu_save_as_pdf.png => mobile/android/base/resources/drawable-hdpi/ic_menu_save_as_pdf.png rename : mobile/android/base/resources/drawable-hdpi-v8/ic_menu_share.png => mobile/android/base/resources/drawable-hdpi/ic_menu_share.png rename : mobile/android/base/resources/drawable-hdpi-v8/site_security_identified.png => mobile/android/base/resources/drawable-hdpi/site_security_identified.png rename : mobile/android/base/resources/drawable-hdpi-v8/site_security_verified.png => mobile/android/base/resources/drawable-hdpi/site_security_verified.png rename : mobile/android/base/resources/drawable-hdpi-v8/tab_close.png => mobile/android/base/resources/drawable-hdpi/tab_close.png rename : mobile/android/base/resources/drawable-hdpi-v8/tab_new.png => mobile/android/base/resources/drawable-hdpi/tab_new.png rename : mobile/android/base/resources/drawable-hdpi-v8/tab_selected.png => mobile/android/base/resources/drawable-hdpi/tab_selected.png rename : mobile/android/base/resources/drawable-hdpi-v8/tab_thumbnail_default.png => mobile/android/base/resources/drawable-hdpi/tab_thumbnail_default.png rename : mobile/android/base/resources/drawable-hdpi-v8/tab_thumbnail_shadow.png => mobile/android/base/resources/drawable-hdpi/tab_thumbnail_shadow.png rename : mobile/android/base/resources/drawable-hdpi-v8/tabs_more.png => mobile/android/base/resources/drawable-hdpi/tabs_more.png rename : mobile/android/base/resources/drawable-hdpi-v8/tabs_normal.png => mobile/android/base/resources/drawable-hdpi/tabs_normal.png rename : mobile/android/base/resources/drawable-hdpi-v8/tabs_plus.png => mobile/android/base/resources/drawable-hdpi/tabs_plus.png rename : mobile/android/base/resources/drawable-hdpi-v8/tabs_pressed.png => mobile/android/base/resources/drawable-hdpi/tabs_pressed.png rename : mobile/android/base/resources/drawable-hdpi-v8/urlbar_stop.png => mobile/android/base/resources/drawable-hdpi/urlbar_stop.png rename : mobile/android/base/resources/drawable-mdpi-v8/abouthome_icon.png => mobile/android/base/resources/drawable/abouthome_icon.png rename : mobile/android/base/resources/drawable-mdpi-v8/abouthome_logo.png => mobile/android/base/resources/drawable/abouthome_logo.png rename : mobile/android/base/resources/drawable-mdpi-v8/abouthome_separator.9.png => mobile/android/base/resources/drawable/abouthome_separator.9.png rename : mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_bg.9.png => mobile/android/base/resources/drawable/abouthome_sync_bg.9.png rename : mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_logo.png => mobile/android/base/resources/drawable/abouthome_sync_logo.png rename : mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_pressed_bg.9.png => mobile/android/base/resources/drawable/abouthome_sync_pressed_bg.9.png rename : mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_placeholder.png => mobile/android/base/resources/drawable/abouthome_topsite_placeholder.png rename : mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_shadow.9.png => mobile/android/base/resources/drawable/abouthome_topsite_shadow.9.png rename : mobile/android/base/resources/drawable-mdpi-v8/address_bar_texture.png => mobile/android/base/resources/drawable/address_bar_texture.png rename : mobile/android/base/resources/drawable-mdpi-v8/address_bar_url_bg.9.png => mobile/android/base/resources/drawable/address_bar_url_bg.9.png rename : mobile/android/base/resources/drawable-mdpi-v8/address_bar_url_outline.9.png => mobile/android/base/resources/drawable/address_bar_url_outline.9.png rename : mobile/android/base/resources/drawable-mdpi-v8/awesomebar_tab.9.png => mobile/android/base/resources/drawable/awesomebar_tab.9.png rename : mobile/android/base/resources/drawable-mdpi-v8/awesomebar_tab_pressed.9.png => mobile/android/base/resources/drawable/awesomebar_tab_pressed.9.png rename : mobile/android/base/resources/drawable-mdpi-v8/doorhanger_arrow.png => mobile/android/base/resources/drawable/doorhanger_arrow.png rename : mobile/android/base/resources/drawable-mdpi-v8/doorhanger_bg.9.png => mobile/android/base/resources/drawable/doorhanger_bg.9.png rename : mobile/android/base/resources/drawable-mdpi-v8/doorhanger_popup_bg.9.png => mobile/android/base/resources/drawable/doorhanger_popup_bg.9.png rename : mobile/android/base/resources/drawable-mdpi-v8/doorhanger_shadow_bg.9.png => mobile/android/base/resources/drawable/doorhanger_shadow_bg.9.png rename : mobile/android/base/resources/drawable-mdpi-v8/favicon.png => mobile/android/base/resources/drawable/favicon.png rename : mobile/android/base/resources/drawable-mdpi-v8/ic_addons_empty.png => mobile/android/base/resources/drawable/ic_addons_empty.png rename : mobile/android/base/resources/drawable-mdpi-v8/ic_awesomebar_go.png => mobile/android/base/resources/drawable/ic_awesomebar_go.png rename : mobile/android/base/resources/drawable-mdpi-v8/ic_awesomebar_search.png => mobile/android/base/resources/drawable/ic_awesomebar_search.png rename : mobile/android/base/resources/drawable-mdpi-v8/ic_menu_bookmark_add.png => mobile/android/base/resources/drawable/ic_menu_bookmark_add.png rename : mobile/android/base/resources/drawable-mdpi-v8/ic_menu_bookmark_remove.png => mobile/android/base/resources/drawable/ic_menu_bookmark_remove.png rename : mobile/android/base/resources/drawable-mdpi-v8/ic_menu_find_in_page.png => mobile/android/base/resources/drawable/ic_menu_find_in_page.png rename : mobile/android/base/resources/drawable-mdpi-v8/ic_menu_forward.png => mobile/android/base/resources/drawable/ic_menu_forward.png rename : mobile/android/base/resources/drawable-mdpi-v8/ic_menu_reload.png => mobile/android/base/resources/drawable/ic_menu_reload.png rename : mobile/android/base/resources/drawable-mdpi-v8/ic_menu_save_as_pdf.png => mobile/android/base/resources/drawable/ic_menu_save_as_pdf.png rename : mobile/android/base/resources/drawable-mdpi-v8/ic_menu_share.png => mobile/android/base/resources/drawable/ic_menu_share.png rename : mobile/android/base/resources/drawable-mdpi-v8/site_security_identified.png => mobile/android/base/resources/drawable/site_security_identified.png rename : mobile/android/base/resources/drawable-mdpi-v8/site_security_verified.png => mobile/android/base/resources/drawable/site_security_verified.png rename : mobile/android/base/resources/drawable-mdpi-v8/tab_close.png => mobile/android/base/resources/drawable/tab_close.png rename : mobile/android/base/resources/drawable-mdpi-v8/tab_new.png => mobile/android/base/resources/drawable/tab_new.png rename : mobile/android/base/resources/drawable-mdpi-v8/tab_selected.png => mobile/android/base/resources/drawable/tab_selected.png rename : mobile/android/base/resources/drawable-mdpi-v8/tab_thumbnail_default.png => mobile/android/base/resources/drawable/tab_thumbnail_default.png rename : mobile/android/base/resources/drawable-mdpi-v8/tab_thumbnail_shadow.png => mobile/android/base/resources/drawable/tab_thumbnail_shadow.png rename : mobile/android/base/resources/drawable-mdpi-v8/tabs_more.png => mobile/android/base/resources/drawable/tabs_more.png rename : mobile/android/base/resources/drawable-mdpi-v8/tabs_normal.png => mobile/android/base/resources/drawable/tabs_normal.png rename : mobile/android/base/resources/drawable-mdpi-v8/tabs_plus.png => mobile/android/base/resources/drawable/tabs_plus.png rename : mobile/android/base/resources/drawable-mdpi-v8/tabs_pressed.png => mobile/android/base/resources/drawable/tabs_pressed.png rename : mobile/android/base/resources/drawable-mdpi-v8/urlbar_stop.png => mobile/android/base/resources/drawable/urlbar_stop.png --- mobile/android/base/Makefile.in | 194 +++++++++--------- .../abouthome_icon.png | Bin .../abouthome_logo.png | Bin .../abouthome_separator.9.png | Bin .../abouthome_sync_bg.9.png | Bin .../abouthome_sync_logo.png | Bin .../abouthome_sync_pressed_bg.9.png | Bin .../abouthome_topsite_placeholder.png | Bin .../abouthome_topsite_shadow.9.png | Bin .../address_bar_texture.png | Bin .../address_bar_url_bg.9.png | Bin .../address_bar_url_outline.9.png | Bin .../awesomebar_tab.9.png | Bin .../awesomebar_tab_pressed.9.png | Bin .../doorhanger_arrow.png | Bin .../doorhanger_bg.9.png | Bin .../doorhanger_popup_bg.9.png | Bin .../doorhanger_shadow_bg.9.png | Bin .../favicon.png | Bin .../home_bg.png | Bin .../home_star.png | Bin .../ic_addons_empty.png | Bin .../ic_awesomebar_go.png | Bin .../ic_awesomebar_search.png | Bin .../ic_menu_bookmark_add.png | Bin .../ic_menu_bookmark_remove.png | Bin .../ic_menu_find_in_page.png | Bin .../ic_menu_forward.png | Bin .../ic_menu_reload.png | Bin .../ic_menu_save_as_pdf.png | Bin .../ic_menu_share.png | Bin .../site_security_identified.png | Bin .../site_security_verified.png | Bin .../tab_close.png | Bin .../tab_new.png | Bin .../tab_selected.png | Bin .../tab_thumbnail_default.png | Bin .../tab_thumbnail_shadow.png | Bin .../tabs_more.png | Bin .../tabs_normal.png | Bin .../tabs_plus.png | Bin .../tabs_pressed.png | Bin .../urlbar_stop.png | Bin .../abouthome_icon.png | Bin .../abouthome_logo.png | Bin .../abouthome_separator.9.png | Bin .../abouthome_sync_bg.9.png | Bin .../abouthome_sync_logo.png | Bin .../abouthome_sync_pressed_bg.9.png | Bin .../abouthome_topsite_placeholder.png | Bin .../abouthome_topsite_shadow.9.png | Bin .../address_bar_texture.png | Bin .../address_bar_url_bg.9.png | Bin .../address_bar_url_outline.9.png | Bin .../awesomebar_tab.9.png | Bin .../awesomebar_tab_pressed.9.png | Bin .../doorhanger_arrow.png | Bin .../doorhanger_bg.9.png | Bin .../doorhanger_popup_bg.9.png | Bin .../doorhanger_shadow_bg.9.png | Bin .../favicon.png | Bin .../ic_addons_empty.png | Bin .../ic_awesomebar_go.png | Bin .../ic_awesomebar_search.png | Bin .../ic_menu_bookmark_add.png | Bin .../ic_menu_bookmark_remove.png | Bin .../ic_menu_find_in_page.png | Bin .../ic_menu_forward.png | Bin .../ic_menu_reload.png | Bin .../ic_menu_save_as_pdf.png | Bin .../ic_menu_share.png | Bin .../site_security_identified.png | Bin .../site_security_verified.png | Bin .../tab_close.png | Bin .../tab_new.png | Bin .../tab_selected.png | Bin .../tab_thumbnail_default.png | Bin .../tab_thumbnail_shadow.png | Bin .../tabs_more.png | Bin .../tabs_normal.png | Bin .../tabs_plus.png | Bin .../tabs_pressed.png | Bin .../urlbar_stop.png | Bin 83 files changed, 92 insertions(+), 102 deletions(-) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/abouthome_icon.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/abouthome_logo.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/abouthome_separator.9.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/abouthome_sync_bg.9.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/abouthome_sync_logo.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/abouthome_sync_pressed_bg.9.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/abouthome_topsite_placeholder.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/abouthome_topsite_shadow.9.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/address_bar_texture.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/address_bar_url_bg.9.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/address_bar_url_outline.9.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/awesomebar_tab.9.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/awesomebar_tab_pressed.9.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/doorhanger_arrow.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/doorhanger_bg.9.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/doorhanger_popup_bg.9.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/doorhanger_shadow_bg.9.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/favicon.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/home_bg.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/home_star.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/ic_addons_empty.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/ic_awesomebar_go.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/ic_awesomebar_search.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/ic_menu_bookmark_add.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/ic_menu_bookmark_remove.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/ic_menu_find_in_page.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/ic_menu_forward.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/ic_menu_reload.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/ic_menu_save_as_pdf.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/ic_menu_share.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/site_security_identified.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/site_security_verified.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/tab_close.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/tab_new.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/tab_selected.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/tab_thumbnail_default.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/tab_thumbnail_shadow.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/tabs_more.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/tabs_normal.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/tabs_plus.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/tabs_pressed.png (100%) rename mobile/android/base/resources/{drawable-hdpi-v8 => drawable-hdpi}/urlbar_stop.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/abouthome_icon.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/abouthome_logo.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/abouthome_separator.9.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/abouthome_sync_bg.9.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/abouthome_sync_logo.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/abouthome_sync_pressed_bg.9.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/abouthome_topsite_placeholder.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/abouthome_topsite_shadow.9.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/address_bar_texture.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/address_bar_url_bg.9.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/address_bar_url_outline.9.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/awesomebar_tab.9.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/awesomebar_tab_pressed.9.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/doorhanger_arrow.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/doorhanger_bg.9.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/doorhanger_popup_bg.9.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/doorhanger_shadow_bg.9.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/favicon.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/ic_addons_empty.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/ic_awesomebar_go.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/ic_awesomebar_search.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/ic_menu_bookmark_add.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/ic_menu_bookmark_remove.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/ic_menu_find_in_page.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/ic_menu_forward.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/ic_menu_reload.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/ic_menu_save_as_pdf.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/ic_menu_share.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/site_security_identified.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/site_security_verified.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/tab_close.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/tab_new.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/tab_selected.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/tab_thumbnail_default.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/tab_thumbnail_shadow.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/tabs_more.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/tabs_normal.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/tabs_plus.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/tabs_pressed.png (100%) rename mobile/android/base/resources/{drawable-mdpi-v8 => drawable}/urlbar_stop.png (100%) diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 62b2971b50f4..ba6e706926b3 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -285,92 +285,98 @@ RES_DRAWABLE_NODPI = \ res/drawable-nodpi/tabs_tray_pressed_bg.png \ $(NULL) -RES_DRAWABLE_MDPI_V8 = \ - res/drawable-mdpi-v8/favicon.png \ - res/drawable-mdpi-v8/abouthome_icon.png \ - res/drawable-mdpi-v8/abouthome_logo.png \ - res/drawable-mdpi-v8/abouthome_separator.9.png \ - res/drawable-mdpi-v8/abouthome_sync_logo.png \ - res/drawable-mdpi-v8/abouthome_sync_bg.9.png \ - res/drawable-mdpi-v8/abouthome_sync_pressed_bg.9.png \ - res/drawable-mdpi-v8/abouthome_topsite_placeholder.png \ - res/drawable-mdpi-v8/abouthome_topsite_shadow.9.png \ - res/drawable-mdpi-v8/awesomebar_tab.9.png \ - res/drawable-mdpi-v8/awesomebar_tab_pressed.9.png \ - res/drawable-mdpi-v8/ic_addons_empty.png \ - res/drawable-mdpi-v8/ic_awesomebar_go.png \ - res/drawable-mdpi-v8/ic_awesomebar_search.png \ - res/drawable-mdpi-v8/ic_menu_bookmark_add.png \ - res/drawable-mdpi-v8/ic_menu_bookmark_remove.png \ - res/drawable-mdpi-v8/ic_menu_find_in_page.png \ - res/drawable-mdpi-v8/ic_menu_reload.png \ - res/drawable-mdpi-v8/ic_menu_save_as_pdf.png \ - res/drawable-mdpi-v8/ic_menu_share.png \ - res/drawable-mdpi-v8/ic_menu_forward.png \ - res/drawable-mdpi-v8/tab_new.png \ - res/drawable-mdpi-v8/tab_close.png \ - res/drawable-mdpi-v8/tab_thumbnail_default.png \ - res/drawable-mdpi-v8/tab_thumbnail_shadow.png \ - res/drawable-mdpi-v8/tab_selected.png \ - res/drawable-mdpi-v8/tabs_normal.png \ - res/drawable-mdpi-v8/tabs_pressed.png \ - res/drawable-mdpi-v8/tabs_more.png \ - res/drawable-mdpi-v8/tabs_plus.png \ - res/drawable-mdpi-v8/address_bar_texture.png \ - res/drawable-mdpi-v8/address_bar_url_bg.9.png \ - res/drawable-mdpi-v8/address_bar_url_outline.9.png \ - res/drawable-mdpi-v8/doorhanger_arrow.png \ - res/drawable-mdpi-v8/doorhanger_bg.9.png \ - res/drawable-mdpi-v8/doorhanger_shadow_bg.9.png \ - res/drawable-mdpi-v8/doorhanger_popup_bg.9.png \ - res/drawable-mdpi-v8/site_security_identified.png \ - res/drawable-mdpi-v8/site_security_verified.png \ - res/drawable-mdpi-v8/urlbar_stop.png \ +RES_DRAWABLE_BASE = \ + res/drawable/favicon.png \ + res/drawable/abouthome_icon.png \ + res/drawable/abouthome_logo.png \ + res/drawable/abouthome_separator.9.png \ + res/drawable/abouthome_sync_logo.png \ + res/drawable/abouthome_sync_bg.9.png \ + res/drawable/abouthome_sync_pressed_bg.9.png \ + res/drawable/abouthome_topsite_placeholder.png \ + res/drawable/abouthome_topsite_shadow.9.png \ + res/drawable/awesomebar_tab.9.png \ + res/drawable/awesomebar_tab_pressed.9.png \ + res/drawable/ic_addons_empty.png \ + res/drawable/ic_awesomebar_go.png \ + res/drawable/ic_awesomebar_search.png \ + res/drawable/ic_menu_bookmark_add.png \ + res/drawable/ic_menu_bookmark_remove.png \ + res/drawable/ic_menu_find_in_page.png \ + res/drawable/ic_menu_reload.png \ + res/drawable/ic_menu_save_as_pdf.png \ + res/drawable/ic_menu_share.png \ + res/drawable/ic_menu_forward.png \ + res/drawable/tab_new.png \ + res/drawable/tab_close.png \ + res/drawable/tab_thumbnail_default.png \ + res/drawable/tab_thumbnail_shadow.png \ + res/drawable/tab_selected.png \ + res/drawable/tabs_normal.png \ + res/drawable/tabs_pressed.png \ + res/drawable/tabs_more.png \ + res/drawable/tabs_plus.png \ + res/drawable/address_bar_texture.png \ + res/drawable/address_bar_url_bg.9.png \ + res/drawable/address_bar_url_outline.9.png \ + res/drawable/doorhanger_arrow.png \ + res/drawable/doorhanger_bg.9.png \ + res/drawable/doorhanger_shadow_bg.9.png \ + res/drawable/doorhanger_popup_bg.9.png \ + res/drawable/site_security_identified.png \ + res/drawable/site_security_verified.png \ + res/drawable/urlbar_stop.png \ + $(addprefix res/drawable-mdpi/,$(notdir $(SYNC_RES_DRAWABLE_MDPI))) \ $(NULL) -RES_DRAWABLE_HDPI_V8 = \ - res/drawable-hdpi-v8/favicon.png \ - res/drawable-hdpi-v8/home_bg.png \ - res/drawable-hdpi-v8/home_star.png \ - res/drawable-hdpi-v8/abouthome_icon.png \ - res/drawable-hdpi-v8/abouthome_logo.png \ - res/drawable-hdpi-v8/abouthome_separator.9.png \ - res/drawable-hdpi-v8/abouthome_sync_logo.png \ - res/drawable-hdpi-v8/abouthome_sync_bg.9.png \ - res/drawable-hdpi-v8/abouthome_sync_pressed_bg.9.png \ - res/drawable-hdpi-v8/abouthome_topsite_placeholder.png \ - res/drawable-hdpi-v8/abouthome_topsite_shadow.9.png \ - res/drawable-hdpi-v8/awesomebar_tab.9.png \ - res/drawable-hdpi-v8/awesomebar_tab_pressed.9.png \ - res/drawable-hdpi-v8/ic_addons_empty.png \ - res/drawable-hdpi-v8/ic_awesomebar_go.png \ - res/drawable-hdpi-v8/ic_awesomebar_search.png \ - res/drawable-hdpi-v8/ic_menu_bookmark_add.png \ - res/drawable-hdpi-v8/ic_menu_bookmark_remove.png \ - res/drawable-hdpi-v8/ic_menu_find_in_page.png \ - res/drawable-hdpi-v8/ic_menu_reload.png \ - res/drawable-hdpi-v8/ic_menu_save_as_pdf.png \ - res/drawable-hdpi-v8/ic_menu_share.png \ - res/drawable-hdpi-v8/ic_menu_forward.png \ - res/drawable-hdpi-v8/tab_new.png \ - res/drawable-hdpi-v8/tab_close.png \ - res/drawable-hdpi-v8/tab_thumbnail_default.png \ - res/drawable-hdpi-v8/tab_thumbnail_shadow.png \ - res/drawable-hdpi-v8/tab_selected.png \ - res/drawable-hdpi-v8/tabs_normal.png \ - res/drawable-hdpi-v8/tabs_pressed.png \ - res/drawable-hdpi-v8/tabs_more.png \ - res/drawable-hdpi-v8/tabs_plus.png \ - res/drawable-hdpi-v8/address_bar_texture.png \ - res/drawable-hdpi-v8/address_bar_url_bg.9.png \ - res/drawable-hdpi-v8/address_bar_url_outline.9.png \ - res/drawable-hdpi-v8/doorhanger_arrow.png \ - res/drawable-hdpi-v8/doorhanger_bg.9.png \ - res/drawable-hdpi-v8/doorhanger_shadow_bg.9.png \ - res/drawable-hdpi-v8/doorhanger_popup_bg.9.png \ - res/drawable-hdpi-v8/site_security_identified.png \ - res/drawable-hdpi-v8/site_security_verified.png \ - res/drawable-hdpi-v8/urlbar_stop.png \ +RES_DRAWABLE_LDPI = \ + $(addprefix res/drawable-ldpi/,$(notdir $(SYNC_RES_DRAWABLE_LDPI))) \ + $(NULL) + +RES_DRAWABLE_HDPI = \ + res/drawable-hdpi/favicon.png \ + res/drawable-hdpi/home_bg.png \ + res/drawable-hdpi/home_star.png \ + res/drawable-hdpi/abouthome_icon.png \ + res/drawable-hdpi/abouthome_logo.png \ + res/drawable-hdpi/abouthome_separator.9.png \ + res/drawable-hdpi/abouthome_sync_logo.png \ + res/drawable-hdpi/abouthome_sync_bg.9.png \ + res/drawable-hdpi/abouthome_sync_pressed_bg.9.png \ + res/drawable-hdpi/abouthome_topsite_placeholder.png \ + res/drawable-hdpi/abouthome_topsite_shadow.9.png \ + res/drawable-hdpi/awesomebar_tab.9.png \ + res/drawable-hdpi/awesomebar_tab_pressed.9.png \ + res/drawable-hdpi/ic_addons_empty.png \ + res/drawable-hdpi/ic_awesomebar_go.png \ + res/drawable-hdpi/ic_awesomebar_search.png \ + res/drawable-hdpi/ic_menu_bookmark_add.png \ + res/drawable-hdpi/ic_menu_bookmark_remove.png \ + res/drawable-hdpi/ic_menu_find_in_page.png \ + res/drawable-hdpi/ic_menu_reload.png \ + res/drawable-hdpi/ic_menu_save_as_pdf.png \ + res/drawable-hdpi/ic_menu_share.png \ + res/drawable-hdpi/ic_menu_forward.png \ + res/drawable-hdpi/tab_new.png \ + res/drawable-hdpi/tab_close.png \ + res/drawable-hdpi/tab_thumbnail_default.png \ + res/drawable-hdpi/tab_thumbnail_shadow.png \ + res/drawable-hdpi/tab_selected.png \ + res/drawable-hdpi/tabs_normal.png \ + res/drawable-hdpi/tabs_pressed.png \ + res/drawable-hdpi/tabs_more.png \ + res/drawable-hdpi/tabs_plus.png \ + res/drawable-hdpi/address_bar_texture.png \ + res/drawable-hdpi/address_bar_url_bg.9.png \ + res/drawable-hdpi/address_bar_url_outline.9.png \ + res/drawable-hdpi/doorhanger_arrow.png \ + res/drawable-hdpi/doorhanger_bg.9.png \ + res/drawable-hdpi/doorhanger_shadow_bg.9.png \ + res/drawable-hdpi/doorhanger_popup_bg.9.png \ + res/drawable-hdpi/site_security_identified.png \ + res/drawable-hdpi/site_security_verified.png \ + res/drawable-hdpi/urlbar_stop.png \ + $(addprefix res/drawable-hdpi/,$(notdir $(SYNC_RES_DRAWABLE_HDPI))) \ $(NULL) RES_DRAWABLE_MDPI_V11 = \ @@ -545,7 +551,7 @@ MOZ_ANDROID_DRAWABLES += \ MOZ_ANDROID_DRAWABLES += $(shell if test -e $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn; then cat $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/android-resources.mn | tr '\n' ' '; fi) -RESOURCES=$(RES_LAYOUT) $(RES_LAYOUT_V11) $(RES_LAYOUT_LAND_V14) $(RES_VALUES) $(RES_VALUES_V11) $(RES_XML) $(RES_ANIM) $(RES_DRAWABLE_NODPI) $(RES_DRAWABLE_MDPI_V8) $(RES_DRAWABLE_HDPI_V8) $(RES_DRAWABLE_MDPI_V11) $(RES_DRAWABLE_HDPI_V11) $(RES_DRAWABLE_XHDPI_V11) $(RES_DRAWABLE_LAND_MDPI_V14) $(RES_DRAWABLE_LAND_HDPI_V14) $(RES_DRAWABLE_LAND_XHDPI_V14) $(RES_COLOR) $(RES_MENU) +RESOURCES=$(RES_LAYOUT) $(RES_LAYOUT_V11) $(RES_LAYOUT_LAND_V14) $(RES_VALUES) $(RES_VALUES_V11) $(RES_XML) $(RES_ANIM) $(RES_DRAWABLE_NODPI) $(RES_DRAWABLE_BASE) $(RES_DRAWABLE_LDPI) $(RES_DRAWABLE_HDPI) $(RES_DRAWABLE_MDPI_V11) $(RES_DRAWABLE_HDPI_V11) $(RES_DRAWABLE_XHDPI_V11) $(RES_DRAWABLE_LAND_MDPI_V14) $(RES_DRAWABLE_LAND_HDPI_V14) $(RES_DRAWABLE_LAND_XHDPI_V14) $(RES_COLOR) $(RES_MENU) RES_DIRS= \ res/layout \ @@ -559,8 +565,7 @@ RES_DIRS= \ res/drawable-ldpi \ res/drawable-mdpi \ res/drawable-hdpi \ - res/drawable-mdpi-v8 \ - res/drawable-hdpi-v8 \ + res/drawable \ res/drawable-mdpi-v9 \ res/drawable-hdpi-v9 \ res/drawable-mdpi-v11 \ @@ -617,21 +622,6 @@ $(RES_DRAWABLE): $(addprefix $(topsrcdir)/,$(MOZ_ANDROID_DRAWABLES)) $(NSINSTALL) -D res/drawable $(NSINSTALL) $^ res/drawable/ -RES_DRAWABLE_LDPI = $(addprefix res/drawable-ldpi/,$(notdir $(SYNC_RES_DRAWABLE_LDPI))) -$(RES_DRAWABLE_LDPI): $(addprefix $(topsrcdir)/,$(SYNC_RES_DRAWABLE_LDPI)) - $(NSINSTALL) -D res/drawable-ldpi - $(NSINSTALL) $^ res/drawable-ldpi/ - -RES_DRAWABLE_MDPI = $(addprefix res/drawable-mdpi/,$(notdir $(SYNC_RES_DRAWABLE_MDPI))) -$(RES_DRAWABLE_MDPI): $(addprefix $(topsrcdir)/,$(SYNC_RES_DRAWABLE_MDPI)) - $(NSINSTALL) -D res/drawable-mdpi - $(NSINSTALL) $^ res/drawable-mdpi/ - -RES_DRAWABLE_HDPI = $(addprefix res/drawable-hdpi/,$(notdir $(SYNC_RES_DRAWABLE_HDPI))) -$(RES_DRAWABLE_HDPI): $(addprefix $(topsrcdir)/,$(SYNC_RES_DRAWABLE_HDPI)) - $(NSINSTALL) -D res/drawable-hdpi - $(NSINSTALL) $^ res/drawable-hdpi/ - res/values/defaults.xml: $(topsrcdir)/$(MOZ_BRANDING_DIRECTORY)/res/values/defaults.xml $(NSINSTALL) -D res/values $(NSINSTALL) $^ res/values diff --git a/mobile/android/base/resources/drawable-hdpi-v8/abouthome_icon.png b/mobile/android/base/resources/drawable-hdpi/abouthome_icon.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/abouthome_icon.png rename to mobile/android/base/resources/drawable-hdpi/abouthome_icon.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/abouthome_logo.png b/mobile/android/base/resources/drawable-hdpi/abouthome_logo.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/abouthome_logo.png rename to mobile/android/base/resources/drawable-hdpi/abouthome_logo.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/abouthome_separator.9.png b/mobile/android/base/resources/drawable-hdpi/abouthome_separator.9.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/abouthome_separator.9.png rename to mobile/android/base/resources/drawable-hdpi/abouthome_separator.9.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_bg.9.png b/mobile/android/base/resources/drawable-hdpi/abouthome_sync_bg.9.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_bg.9.png rename to mobile/android/base/resources/drawable-hdpi/abouthome_sync_bg.9.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_logo.png b/mobile/android/base/resources/drawable-hdpi/abouthome_sync_logo.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_logo.png rename to mobile/android/base/resources/drawable-hdpi/abouthome_sync_logo.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_pressed_bg.9.png b/mobile/android/base/resources/drawable-hdpi/abouthome_sync_pressed_bg.9.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/abouthome_sync_pressed_bg.9.png rename to mobile/android/base/resources/drawable-hdpi/abouthome_sync_pressed_bg.9.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_placeholder.png b/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_placeholder.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_placeholder.png rename to mobile/android/base/resources/drawable-hdpi/abouthome_topsite_placeholder.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_shadow.9.png b/mobile/android/base/resources/drawable-hdpi/abouthome_topsite_shadow.9.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/abouthome_topsite_shadow.9.png rename to mobile/android/base/resources/drawable-hdpi/abouthome_topsite_shadow.9.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/address_bar_texture.png b/mobile/android/base/resources/drawable-hdpi/address_bar_texture.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/address_bar_texture.png rename to mobile/android/base/resources/drawable-hdpi/address_bar_texture.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/address_bar_url_bg.9.png b/mobile/android/base/resources/drawable-hdpi/address_bar_url_bg.9.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/address_bar_url_bg.9.png rename to mobile/android/base/resources/drawable-hdpi/address_bar_url_bg.9.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/address_bar_url_outline.9.png b/mobile/android/base/resources/drawable-hdpi/address_bar_url_outline.9.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/address_bar_url_outline.9.png rename to mobile/android/base/resources/drawable-hdpi/address_bar_url_outline.9.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/awesomebar_tab.9.png b/mobile/android/base/resources/drawable-hdpi/awesomebar_tab.9.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/awesomebar_tab.9.png rename to mobile/android/base/resources/drawable-hdpi/awesomebar_tab.9.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/awesomebar_tab_pressed.9.png b/mobile/android/base/resources/drawable-hdpi/awesomebar_tab_pressed.9.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/awesomebar_tab_pressed.9.png rename to mobile/android/base/resources/drawable-hdpi/awesomebar_tab_pressed.9.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/doorhanger_arrow.png b/mobile/android/base/resources/drawable-hdpi/doorhanger_arrow.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/doorhanger_arrow.png rename to mobile/android/base/resources/drawable-hdpi/doorhanger_arrow.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/doorhanger_bg.9.png b/mobile/android/base/resources/drawable-hdpi/doorhanger_bg.9.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/doorhanger_bg.9.png rename to mobile/android/base/resources/drawable-hdpi/doorhanger_bg.9.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/doorhanger_popup_bg.9.png b/mobile/android/base/resources/drawable-hdpi/doorhanger_popup_bg.9.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/doorhanger_popup_bg.9.png rename to mobile/android/base/resources/drawable-hdpi/doorhanger_popup_bg.9.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/doorhanger_shadow_bg.9.png b/mobile/android/base/resources/drawable-hdpi/doorhanger_shadow_bg.9.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/doorhanger_shadow_bg.9.png rename to mobile/android/base/resources/drawable-hdpi/doorhanger_shadow_bg.9.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/favicon.png b/mobile/android/base/resources/drawable-hdpi/favicon.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/favicon.png rename to mobile/android/base/resources/drawable-hdpi/favicon.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/home_bg.png b/mobile/android/base/resources/drawable-hdpi/home_bg.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/home_bg.png rename to mobile/android/base/resources/drawable-hdpi/home_bg.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/home_star.png b/mobile/android/base/resources/drawable-hdpi/home_star.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/home_star.png rename to mobile/android/base/resources/drawable-hdpi/home_star.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/ic_addons_empty.png b/mobile/android/base/resources/drawable-hdpi/ic_addons_empty.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/ic_addons_empty.png rename to mobile/android/base/resources/drawable-hdpi/ic_addons_empty.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/ic_awesomebar_go.png b/mobile/android/base/resources/drawable-hdpi/ic_awesomebar_go.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/ic_awesomebar_go.png rename to mobile/android/base/resources/drawable-hdpi/ic_awesomebar_go.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/ic_awesomebar_search.png b/mobile/android/base/resources/drawable-hdpi/ic_awesomebar_search.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/ic_awesomebar_search.png rename to mobile/android/base/resources/drawable-hdpi/ic_awesomebar_search.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/ic_menu_bookmark_add.png b/mobile/android/base/resources/drawable-hdpi/ic_menu_bookmark_add.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/ic_menu_bookmark_add.png rename to mobile/android/base/resources/drawable-hdpi/ic_menu_bookmark_add.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/ic_menu_bookmark_remove.png b/mobile/android/base/resources/drawable-hdpi/ic_menu_bookmark_remove.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/ic_menu_bookmark_remove.png rename to mobile/android/base/resources/drawable-hdpi/ic_menu_bookmark_remove.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/ic_menu_find_in_page.png b/mobile/android/base/resources/drawable-hdpi/ic_menu_find_in_page.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/ic_menu_find_in_page.png rename to mobile/android/base/resources/drawable-hdpi/ic_menu_find_in_page.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/ic_menu_forward.png b/mobile/android/base/resources/drawable-hdpi/ic_menu_forward.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/ic_menu_forward.png rename to mobile/android/base/resources/drawable-hdpi/ic_menu_forward.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/ic_menu_reload.png b/mobile/android/base/resources/drawable-hdpi/ic_menu_reload.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/ic_menu_reload.png rename to mobile/android/base/resources/drawable-hdpi/ic_menu_reload.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/ic_menu_save_as_pdf.png b/mobile/android/base/resources/drawable-hdpi/ic_menu_save_as_pdf.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/ic_menu_save_as_pdf.png rename to mobile/android/base/resources/drawable-hdpi/ic_menu_save_as_pdf.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/ic_menu_share.png b/mobile/android/base/resources/drawable-hdpi/ic_menu_share.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/ic_menu_share.png rename to mobile/android/base/resources/drawable-hdpi/ic_menu_share.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/site_security_identified.png b/mobile/android/base/resources/drawable-hdpi/site_security_identified.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/site_security_identified.png rename to mobile/android/base/resources/drawable-hdpi/site_security_identified.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/site_security_verified.png b/mobile/android/base/resources/drawable-hdpi/site_security_verified.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/site_security_verified.png rename to mobile/android/base/resources/drawable-hdpi/site_security_verified.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/tab_close.png b/mobile/android/base/resources/drawable-hdpi/tab_close.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/tab_close.png rename to mobile/android/base/resources/drawable-hdpi/tab_close.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/tab_new.png b/mobile/android/base/resources/drawable-hdpi/tab_new.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/tab_new.png rename to mobile/android/base/resources/drawable-hdpi/tab_new.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/tab_selected.png b/mobile/android/base/resources/drawable-hdpi/tab_selected.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/tab_selected.png rename to mobile/android/base/resources/drawable-hdpi/tab_selected.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/tab_thumbnail_default.png b/mobile/android/base/resources/drawable-hdpi/tab_thumbnail_default.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/tab_thumbnail_default.png rename to mobile/android/base/resources/drawable-hdpi/tab_thumbnail_default.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/tab_thumbnail_shadow.png b/mobile/android/base/resources/drawable-hdpi/tab_thumbnail_shadow.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/tab_thumbnail_shadow.png rename to mobile/android/base/resources/drawable-hdpi/tab_thumbnail_shadow.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/tabs_more.png b/mobile/android/base/resources/drawable-hdpi/tabs_more.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/tabs_more.png rename to mobile/android/base/resources/drawable-hdpi/tabs_more.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/tabs_normal.png b/mobile/android/base/resources/drawable-hdpi/tabs_normal.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/tabs_normal.png rename to mobile/android/base/resources/drawable-hdpi/tabs_normal.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/tabs_plus.png b/mobile/android/base/resources/drawable-hdpi/tabs_plus.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/tabs_plus.png rename to mobile/android/base/resources/drawable-hdpi/tabs_plus.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/tabs_pressed.png b/mobile/android/base/resources/drawable-hdpi/tabs_pressed.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/tabs_pressed.png rename to mobile/android/base/resources/drawable-hdpi/tabs_pressed.png diff --git a/mobile/android/base/resources/drawable-hdpi-v8/urlbar_stop.png b/mobile/android/base/resources/drawable-hdpi/urlbar_stop.png similarity index 100% rename from mobile/android/base/resources/drawable-hdpi-v8/urlbar_stop.png rename to mobile/android/base/resources/drawable-hdpi/urlbar_stop.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/abouthome_icon.png b/mobile/android/base/resources/drawable/abouthome_icon.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/abouthome_icon.png rename to mobile/android/base/resources/drawable/abouthome_icon.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/abouthome_logo.png b/mobile/android/base/resources/drawable/abouthome_logo.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/abouthome_logo.png rename to mobile/android/base/resources/drawable/abouthome_logo.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/abouthome_separator.9.png b/mobile/android/base/resources/drawable/abouthome_separator.9.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/abouthome_separator.9.png rename to mobile/android/base/resources/drawable/abouthome_separator.9.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_bg.9.png b/mobile/android/base/resources/drawable/abouthome_sync_bg.9.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_bg.9.png rename to mobile/android/base/resources/drawable/abouthome_sync_bg.9.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_logo.png b/mobile/android/base/resources/drawable/abouthome_sync_logo.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_logo.png rename to mobile/android/base/resources/drawable/abouthome_sync_logo.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_pressed_bg.9.png b/mobile/android/base/resources/drawable/abouthome_sync_pressed_bg.9.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/abouthome_sync_pressed_bg.9.png rename to mobile/android/base/resources/drawable/abouthome_sync_pressed_bg.9.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_placeholder.png b/mobile/android/base/resources/drawable/abouthome_topsite_placeholder.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_placeholder.png rename to mobile/android/base/resources/drawable/abouthome_topsite_placeholder.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_shadow.9.png b/mobile/android/base/resources/drawable/abouthome_topsite_shadow.9.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/abouthome_topsite_shadow.9.png rename to mobile/android/base/resources/drawable/abouthome_topsite_shadow.9.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/address_bar_texture.png b/mobile/android/base/resources/drawable/address_bar_texture.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/address_bar_texture.png rename to mobile/android/base/resources/drawable/address_bar_texture.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/address_bar_url_bg.9.png b/mobile/android/base/resources/drawable/address_bar_url_bg.9.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/address_bar_url_bg.9.png rename to mobile/android/base/resources/drawable/address_bar_url_bg.9.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/address_bar_url_outline.9.png b/mobile/android/base/resources/drawable/address_bar_url_outline.9.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/address_bar_url_outline.9.png rename to mobile/android/base/resources/drawable/address_bar_url_outline.9.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/awesomebar_tab.9.png b/mobile/android/base/resources/drawable/awesomebar_tab.9.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/awesomebar_tab.9.png rename to mobile/android/base/resources/drawable/awesomebar_tab.9.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/awesomebar_tab_pressed.9.png b/mobile/android/base/resources/drawable/awesomebar_tab_pressed.9.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/awesomebar_tab_pressed.9.png rename to mobile/android/base/resources/drawable/awesomebar_tab_pressed.9.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/doorhanger_arrow.png b/mobile/android/base/resources/drawable/doorhanger_arrow.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/doorhanger_arrow.png rename to mobile/android/base/resources/drawable/doorhanger_arrow.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/doorhanger_bg.9.png b/mobile/android/base/resources/drawable/doorhanger_bg.9.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/doorhanger_bg.9.png rename to mobile/android/base/resources/drawable/doorhanger_bg.9.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/doorhanger_popup_bg.9.png b/mobile/android/base/resources/drawable/doorhanger_popup_bg.9.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/doorhanger_popup_bg.9.png rename to mobile/android/base/resources/drawable/doorhanger_popup_bg.9.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/doorhanger_shadow_bg.9.png b/mobile/android/base/resources/drawable/doorhanger_shadow_bg.9.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/doorhanger_shadow_bg.9.png rename to mobile/android/base/resources/drawable/doorhanger_shadow_bg.9.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/favicon.png b/mobile/android/base/resources/drawable/favicon.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/favicon.png rename to mobile/android/base/resources/drawable/favicon.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/ic_addons_empty.png b/mobile/android/base/resources/drawable/ic_addons_empty.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/ic_addons_empty.png rename to mobile/android/base/resources/drawable/ic_addons_empty.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/ic_awesomebar_go.png b/mobile/android/base/resources/drawable/ic_awesomebar_go.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/ic_awesomebar_go.png rename to mobile/android/base/resources/drawable/ic_awesomebar_go.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/ic_awesomebar_search.png b/mobile/android/base/resources/drawable/ic_awesomebar_search.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/ic_awesomebar_search.png rename to mobile/android/base/resources/drawable/ic_awesomebar_search.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/ic_menu_bookmark_add.png b/mobile/android/base/resources/drawable/ic_menu_bookmark_add.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/ic_menu_bookmark_add.png rename to mobile/android/base/resources/drawable/ic_menu_bookmark_add.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/ic_menu_bookmark_remove.png b/mobile/android/base/resources/drawable/ic_menu_bookmark_remove.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/ic_menu_bookmark_remove.png rename to mobile/android/base/resources/drawable/ic_menu_bookmark_remove.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/ic_menu_find_in_page.png b/mobile/android/base/resources/drawable/ic_menu_find_in_page.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/ic_menu_find_in_page.png rename to mobile/android/base/resources/drawable/ic_menu_find_in_page.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/ic_menu_forward.png b/mobile/android/base/resources/drawable/ic_menu_forward.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/ic_menu_forward.png rename to mobile/android/base/resources/drawable/ic_menu_forward.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/ic_menu_reload.png b/mobile/android/base/resources/drawable/ic_menu_reload.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/ic_menu_reload.png rename to mobile/android/base/resources/drawable/ic_menu_reload.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/ic_menu_save_as_pdf.png b/mobile/android/base/resources/drawable/ic_menu_save_as_pdf.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/ic_menu_save_as_pdf.png rename to mobile/android/base/resources/drawable/ic_menu_save_as_pdf.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/ic_menu_share.png b/mobile/android/base/resources/drawable/ic_menu_share.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/ic_menu_share.png rename to mobile/android/base/resources/drawable/ic_menu_share.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/site_security_identified.png b/mobile/android/base/resources/drawable/site_security_identified.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/site_security_identified.png rename to mobile/android/base/resources/drawable/site_security_identified.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/site_security_verified.png b/mobile/android/base/resources/drawable/site_security_verified.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/site_security_verified.png rename to mobile/android/base/resources/drawable/site_security_verified.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/tab_close.png b/mobile/android/base/resources/drawable/tab_close.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/tab_close.png rename to mobile/android/base/resources/drawable/tab_close.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/tab_new.png b/mobile/android/base/resources/drawable/tab_new.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/tab_new.png rename to mobile/android/base/resources/drawable/tab_new.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/tab_selected.png b/mobile/android/base/resources/drawable/tab_selected.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/tab_selected.png rename to mobile/android/base/resources/drawable/tab_selected.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/tab_thumbnail_default.png b/mobile/android/base/resources/drawable/tab_thumbnail_default.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/tab_thumbnail_default.png rename to mobile/android/base/resources/drawable/tab_thumbnail_default.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/tab_thumbnail_shadow.png b/mobile/android/base/resources/drawable/tab_thumbnail_shadow.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/tab_thumbnail_shadow.png rename to mobile/android/base/resources/drawable/tab_thumbnail_shadow.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/tabs_more.png b/mobile/android/base/resources/drawable/tabs_more.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/tabs_more.png rename to mobile/android/base/resources/drawable/tabs_more.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/tabs_normal.png b/mobile/android/base/resources/drawable/tabs_normal.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/tabs_normal.png rename to mobile/android/base/resources/drawable/tabs_normal.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/tabs_plus.png b/mobile/android/base/resources/drawable/tabs_plus.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/tabs_plus.png rename to mobile/android/base/resources/drawable/tabs_plus.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/tabs_pressed.png b/mobile/android/base/resources/drawable/tabs_pressed.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/tabs_pressed.png rename to mobile/android/base/resources/drawable/tabs_pressed.png diff --git a/mobile/android/base/resources/drawable-mdpi-v8/urlbar_stop.png b/mobile/android/base/resources/drawable/urlbar_stop.png similarity index 100% rename from mobile/android/base/resources/drawable-mdpi-v8/urlbar_stop.png rename to mobile/android/base/resources/drawable/urlbar_stop.png From cb4401d7dd05ce8f898e7fa0654c7af37561671e Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Wed, 1 Feb 2012 15:03:46 -0500 Subject: [PATCH 60/90] bug 723243 - SimpleScaleGestureDetector uses getActionIndex(), which is not available in eclair r=wesj --- mobile/android/base/ui/SimpleScaleGestureDetector.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/ui/SimpleScaleGestureDetector.java b/mobile/android/base/ui/SimpleScaleGestureDetector.java index 7f4aee8a54bd..5184bd18a462 100644 --- a/mobile/android/base/ui/SimpleScaleGestureDetector.java +++ b/mobile/android/base/ui/SimpleScaleGestureDetector.java @@ -101,9 +101,14 @@ public class SimpleScaleGestureDetector { return mPointerInfo.size(); } + private int getActionIndex(MotionEvent event) { + return (event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK) + >> MotionEvent.ACTION_POINTER_INDEX_SHIFT; + } + private void onTouchStart(MotionEvent event) { mLastEventTime = event.getEventTime(); - mPointerInfo.addFirst(PointerInfo.create(event, event.getActionIndex())); + mPointerInfo.addFirst(PointerInfo.create(event, getActionIndex(event))); if (getPointersDown() == 2) { sendScaleGesture(EventType.BEGIN); } @@ -126,7 +131,7 @@ public class SimpleScaleGestureDetector { private void onTouchEnd(MotionEvent event) { mLastEventTime = event.getEventTime(); - int id = event.getPointerId(event.getActionIndex()); + int id = event.getPointerId(getActionIndex(event)); ListIterator iterator = mPointerInfo.listIterator(); while (iterator.hasNext()) { PointerInfo pointerInfo = iterator.next(); From 9fcbfba852e6ee926a4220ba77a3bbfdd79b3e77 Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Wed, 1 Feb 2012 14:37:03 -0500 Subject: [PATCH 61/90] bug 723159 - Base64.decode not available on eclair r=mfinkle --- mobile/android/base/AwesomeBarTabs.java | 3 +- mobile/android/base/GeckoApp.java | 2 +- mobile/android/base/GeckoAppShell.java | 102 ++++++++++++++++++ mobile/android/base/Tabs.java | 3 +- .../android/base/db/BrowserProvider.java.in | 49 +-------- 5 files changed, 106 insertions(+), 53 deletions(-) diff --git a/mobile/android/base/AwesomeBarTabs.java b/mobile/android/base/AwesomeBarTabs.java index b2854934e66e..3f32823dc000 100644 --- a/mobile/android/base/AwesomeBarTabs.java +++ b/mobile/android/base/AwesomeBarTabs.java @@ -49,7 +49,6 @@ import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.SystemClock; import android.util.AttributeSet; -import android.util.Base64; import android.util.Log; import android.util.Pair; import android.view.LayoutInflater; @@ -475,7 +474,7 @@ public class AwesomeBarTabs extends TabHost { String base64 = dataURI.substring(dataURI.indexOf(',') + 1); Drawable drawable = null; try { - byte[] bytes = Base64.decode(base64, Base64.DEFAULT); + byte[] bytes = GeckoAppShell.decodeBase64(base64); ByteArrayInputStream stream = new ByteArrayInputStream(bytes); drawable = Drawable.createFromStream(stream, "src"); stream.close(); diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 59048d219901..97cacfb3530b 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -399,7 +399,7 @@ abstract public class GeckoApp final MenuItem mi = aMenu.add(Menu.NONE, item.id, Menu.NONE, item.label); if (item.icon != null) { if (item.icon.startsWith("data")) { - byte[] raw = Base64.decode(item.icon.substring(22), Base64.DEFAULT); + byte[] raw = GeckoAppShell.decodeBase64(item.icon.substring(22)); Bitmap bitmap = BitmapFactory.decodeByteArray(raw, 0, raw.length); BitmapDrawable drawable = new BitmapDrawable(bitmap); mi.setIcon(drawable); diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 630cf8953cfd..8ba90288c10f 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -1833,4 +1833,106 @@ public class GeckoAppShell public static void disableNetworkNotifications() { GeckoNetworkManager.getInstance().disableNotifications(); } + + private static final int GUID_ENCODE_FLAGS = Base64.URL_SAFE | Base64.NO_WRAP; + + /** + * taken from http://www.source-code.biz/base64coder/java/Base64Coder.java.txt and modified (MIT License) + */ + // Mapping table from 6-bit nibbles to Base64 characters. + private static final byte[] map1 = new byte[64]; + static { + int i=0; + for (byte c='A'; c<='Z'; c++) map1[i++] = c; + for (byte c='a'; c<='z'; c++) map1[i++] = c; + for (byte c='0'; c<='9'; c++) map1[i++] = c; + map1[i++] = '-'; map1[i++] = '_'; + } + + // Mapping table from Base64 characters to 6-bit nibbles. + private static final byte[] map2 = new byte[128]; + static { + for (int i=0; i 127 || i1 > 127 || i2 > 127 || i3 > 127) + throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); + int b0 = map2[i0]; + int b1 = map2[i1]; + int b2 = map2[i2]; + int b3 = map2[i3]; + if (b0 < 0 || b1 < 0 || b2 < 0 || b3 < 0) + throw new IllegalArgumentException ("Illegal character in Base64 encoded data."); + int o0 = ( b0 <<2) | (b1>>>4); + int o1 = ((b1 & 0xf)<<4) | (b2>>>2); + int o2 = ((b2 & 3)<<6) | b3; + out[op++] = (byte)o0; + if (op= 32 && s[i] < 127) + fputc(s[i], stderr); + else if (s[i] <= 255) + fprintf(stderr, "\\x%02x", (unsigned int) s[i]); + else + fprintf(stderr, "\\u%04x", (unsigned int) s[i]); + } + fputc('"', stderr); +} + +JS_FRIEND_API(void) +js_DumpChars(const jschar *s, size_t n) +{ + fprintf(stderr, "jschar * (%p) = ", (void *) s); + DumpChars(s, n); + fputc('\n', stderr); +} + +JS_FRIEND_API(void) +js_DumpObject(JSObject *obj) +{ + obj->dump(); +} struct DumpingChildInfo { void *node; diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 93d5eaf26e5e..33697bb69280 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -135,6 +135,27 @@ js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, JSBool strict, JS_FRIEND_API(void) js_ReportOverRecursed(JSContext *maybecx); +#ifdef DEBUG + +/* + * Routines to print out values during debugging. These are FRIEND_API to help + * the debugger find them and to support temporarily hacking js_Dump* calls + * into other code. + */ + +extern JS_FRIEND_API(void) +js_DumpString(JSString *str); + +extern JS_FRIEND_API(void) +js_DumpAtom(JSAtom *atom); + +extern JS_FRIEND_API(void) +js_DumpObject(JSObject *obj); + +extern JS_FRIEND_API(void) +js_DumpChars(const jschar *s, size_t n); +#endif + #ifdef __cplusplus extern JS_FRIEND_API(bool) diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 3a36ef4af611..a7e80064ab2b 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -6534,68 +6534,6 @@ js::HandleNonGenericMethodClassMismatch(JSContext *cx, CallArgs args, Native nat * into other code. */ -void -dumpChars(const jschar *s, size_t n) -{ - size_t i; - - if (n == (size_t) -1) { - while (s[++n]) ; - } - - fputc('"', stderr); - for (i = 0; i < n; i++) { - if (s[i] == '\n') - fprintf(stderr, "\\n"); - else if (s[i] == '\t') - fprintf(stderr, "\\t"); - else if (s[i] >= 32 && s[i] < 127) - fputc(s[i], stderr); - else if (s[i] <= 255) - fprintf(stderr, "\\x%02x", (unsigned int) s[i]); - else - fprintf(stderr, "\\u%04x", (unsigned int) s[i]); - } - fputc('"', stderr); -} - -JS_FRIEND_API(void) -js_DumpChars(const jschar *s, size_t n) -{ - fprintf(stderr, "jschar * (%p) = ", (void *) s); - dumpChars(s, n); - fputc('\n', stderr); -} - -void -dumpString(JSString *str) -{ - if (const jschar *chars = str->getChars(NULL)) - dumpChars(chars, str->length()); - else - fprintf(stderr, "(oom in dumpString)"); -} - -JS_FRIEND_API(void) -js_DumpString(JSString *str) -{ - if (const jschar *chars = str->getChars(NULL)) { - fprintf(stderr, "JSString* (%p) = jschar * (%p) = ", - (void *) str, (void *) chars); - dumpString(str); - } else { - fprintf(stderr, "(oom in JS_DumpString)"); - } - fputc('\n', stderr); -} - -JS_FRIEND_API(void) -js_DumpAtom(JSAtom *atom) -{ - fprintf(stderr, "JSAtom* (%p) = ", (void *) atom); - js_DumpString(atom); -} - void dumpValue(const Value &v) { @@ -6608,7 +6546,7 @@ dumpValue(const Value &v) else if (v.isDouble()) fprintf(stderr, "%g", v.toDouble()); else if (v.isString()) - dumpString(v.toString()); + v.toString()->dump(); else if (v.isObject() && v.toObject().isFunction()) { JSFunction *fun = v.toObject().toFunction(); if (fun->atom) { @@ -6692,7 +6630,7 @@ DumpProperty(JSObject *obj, const Shape &shape) fprintf(stderr, "setterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.setterOp())); if (JSID_IS_ATOM(id)) - dumpString(JSID_TO_STRING(id)); + JSID_TO_STRING(id)->dump(); else if (JSID_IS_INT(id)) fprintf(stderr, "%d", (int) JSID_TO_INT(id)); else @@ -6709,9 +6647,10 @@ DumpProperty(JSObject *obj, const Shape &shape) fprintf(stderr, "\n"); } -JS_FRIEND_API(void) -js_DumpObject(JSObject *obj) +void +JSObject::dump() { + JSObject *obj = this; fprintf(stderr, "object %p\n", (void *) obj); Class *clasp = obj->getClass(); fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name); diff --git a/js/src/jsobj.h b/js/src/jsobj.h index 92e148de4f45..7e30094c1aef 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1464,6 +1464,10 @@ struct JSObject : js::gc::Cell static inline js::ThingRootKind rootKind() { return js::THING_ROOT_OBJECT; } +#ifdef DEBUG + void dump(); +#endif + private: static void staticAsserts() { /* Check alignment for any fixed slots allocated after the object. */ diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp index 362fd0c28322..ef0852942fcd 100644 --- a/js/src/vm/String.cpp +++ b/js/src/vm/String.cpp @@ -114,6 +114,40 @@ JSString::sizeOfExcludingThis(JSMallocSizeOfFun mallocSizeOf) return mallocSizeOf(fixed.chars()); } +#ifdef DEBUG +void +JSString::dump() +{ + if (const jschar *chars = getChars(NULL)) { + fprintf(stderr, "JSString* (%p) = jschar * (%p) = ", + (void *) this, (void *) chars); + + extern void DumpChars(const jschar *s, size_t n); + DumpChars(chars, length()); + } else { + fprintf(stderr, "(oom in JSString::dump)"); + } + fputc('\n', stderr); +} + +bool +JSString::equals(const char *s) +{ + const jschar *c = getChars(NULL); + if (!c) { + fprintf(stderr, "OOM in JSString::equals!\n"); + return false; + } + while (*c && *s) { + if (*c != *s) + return false; + c++; + s++; + } + return *c == *s; +} +#endif /* DEBUG */ + static JS_ALWAYS_INLINE bool AllocChars(JSContext *maybecx, size_t length, jschar **chars, size_t *capacity) { @@ -508,3 +542,12 @@ StaticStrings::isStatic(JSAtom *atom) return false; } } + +#ifdef DEBUG +void +JSAtom::dump() +{ + fprintf(stderr, "JSAtom* (%p) = ", (void *) this); + this->JSString::dump(); +} +#endif /* DEBUG */ diff --git a/js/src/vm/String.h b/js/src/vm/String.h index 73d3c5c8693b..ec3d69c7fc07 100644 --- a/js/src/vm/String.h +++ b/js/src/vm/String.h @@ -425,6 +425,11 @@ class JSString : public js::gc::Cell static inline void readBarrier(JSString *str); static inline js::ThingRootKind rootKind() { return js::THING_ROOT_STRING; } + +#ifdef DEBUG + void dump(); + bool equals(const char *s); +#endif }; class JSRope : public JSString @@ -698,6 +703,10 @@ class JSAtom : public JSFixedString inline js::PropertyName *asPropertyName(); inline void finalize(JSRuntime *rt); + +#ifdef DEBUG + void dump(); +#endif }; JS_STATIC_ASSERT(sizeof(JSAtom) == sizeof(JSString)); From cf1cfb8e56f111a808bf435f32670d6bdf607217 Mon Sep 17 00:00:00 2001 From: Mark Capella Date: Wed, 1 Feb 2012 16:23:22 +0900 Subject: [PATCH 63/90] Bug 589784 - Remove role='label' aria processing: no longer in spec, r=davidb, surkov --- accessible/src/base/nsARIAMap.cpp | 9 --------- .../tests/mochitest/test_aria_roles.html | 2 +- .../tests/mochitest/test_childAtPoint.html | 18 +++++++++--------- 3 files changed, 10 insertions(+), 19 deletions(-) diff --git a/accessible/src/base/nsARIAMap.cpp b/accessible/src/base/nsARIAMap.cpp index 20b7db297333..da4484422f48 100644 --- a/accessible/src/base/nsARIAMap.cpp +++ b/accessible/src/base/nsARIAMap.cpp @@ -219,15 +219,6 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = eNoLiveAttr, kNoReqStates }, - { - "label", - roles::LABEL, - kUseMapRole, - eNoValue, - eNoAction, - eNoLiveAttr, - kNoReqStates - }, { "link", roles::LINK, diff --git a/accessible/tests/mochitest/test_aria_roles.html b/accessible/tests/mochitest/test_aria_roles.html index 037e223a1604..dbeacbe86731 100644 --- a/accessible/tests/mochitest/test_aria_roles.html +++ b/accessible/tests/mochitest/test_aria_roles.html @@ -131,7 +131,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=529289 hi - +
diff --git a/accessible/tests/mochitest/test_childAtPoint.html b/accessible/tests/mochitest/test_childAtPoint.html index a7047a7efddc..7d4363afff19 100644 --- a/accessible/tests/mochitest/test_childAtPoint.html +++ b/accessible/tests/mochitest/test_childAtPoint.html @@ -37,15 +37,15 @@ testChildAtPoint(txt, -10000, 10000, false, null); testChildAtPoint(txt, -10000, 10000, true, null); - // Not specific case, point is inside of label accessible. - var label = getAccessible("label"); - var labelText = label.firstChild; - testChildAtPoint(label, 1, 1, false, labelText); - testChildAtPoint(label, 1, 1, true, labelText); + // Not specific case, point is inside of btn accessible. + var btn = getAccessible("btn"); + var btnText = btn.firstChild; + testChildAtPoint(btn, 1, 1, false, btnText); + testChildAtPoint(btn, 1, 1, true, btnText); - // Not specific case, point is outside of label accessible. - testChildAtPoint(label, -1, 1, false, null); - testChildAtPoint(label, -1, 1, true, null); + // Not specific case, point is outside of btn accessible. + testChildAtPoint(btn, -1, 1, false, null); + testChildAtPoint(btn, -1, 1, true, null); // Out of flow accessible testing, do not return out of flow accessible // because it's not a child of the accessible even visually it is. @@ -78,7 +78,7 @@
imgitem
- label1label2 + button1button2textbox1textbox2 From 16c435842a028bfe0e29ba7cf5a82c2df990730b Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Wed, 1 Feb 2012 15:01:47 -0800 Subject: [PATCH 64/90] Bug 721080 - Pages with touch event listeners that don't call preventDefault should not hold up panning. r=dougt --- embedding/android/GeckoAppShell.java | 2 +- mobile/android/base/GeckoAppShell.java | 4 ++-- mobile/android/base/gfx/LayerController.java | 16 +++++++++------- widget/android/AndroidBridge.cpp | 6 +++--- widget/android/AndroidBridge.h | 4 ++-- widget/android/nsWindow.cpp | 5 +++-- 6 files changed, 20 insertions(+), 17 deletions(-) diff --git a/embedding/android/GeckoAppShell.java b/embedding/android/GeckoAppShell.java index 663df023e81f..19204cd8fb89 100644 --- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -1792,5 +1792,5 @@ public class GeckoAppShell } // This is only used in Native Fennec. - public static void preventPanning() { } + public static void setPreventPanning(final boolean aPreventPanning) { } } diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index 8ba90288c10f..9c8a53608f6a 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -1121,11 +1121,11 @@ public class GeckoAppShell }); } - public static void preventPanning() { + public static void setPreventPanning(final boolean aPreventPanning) { getMainHandler().post(new Runnable() { public void run() { LayerController layerController = GeckoApp.mAppContext.getLayerController(); - layerController.preventPanning(true); + layerController.preventPanning(aPreventPanning); } }); } diff --git a/mobile/android/base/gfx/LayerController.java b/mobile/android/base/gfx/LayerController.java index 950ec217d3cb..37b02d7d5f8a 100644 --- a/mobile/android/base/gfx/LayerController.java +++ b/mobile/android/base/gfx/LayerController.java @@ -441,13 +441,15 @@ public class LayerController { allowDefaultTimer.purge(); allowDefaultTimer = null; } - allowDefaultActions = !aValue; - - if (aValue) { - mView.clearEventQueue(); - mPanZoomController.cancelTouch(); - } else { - mView.processEventQueue(); + if (aValue == allowDefaultActions) { + allowDefaultActions = !aValue; + + if (aValue) { + mView.clearEventQueue(); + mPanZoomController.cancelTouch(); + } else { + mView.processEventQueue(); + } } } diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index d9b1c75af395..64127c3d7d20 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -125,7 +125,7 @@ AndroidBridge::Init(JNIEnv *jEnv, jGetDpi = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getDpi", "()I"); jSetFullScreen = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setFullScreen", "(Z)V"); jShowInputMethodPicker = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showInputMethodPicker", "()V"); - jPreventPanning = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "preventPanning", "()V"); + jSetPreventPanning = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setPreventPanning", "(Z)V"); jHideProgressDialog = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "hideProgressDialog", "()V"); jPerformHapticFeedback = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "performHapticFeedback", "(Z)V"); jVibrate1 = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "vibrate", "(J)V"); @@ -1806,13 +1806,13 @@ NS_IMETHODIMP nsAndroidBridge::SetDrawMetadataProvider(nsIAndroidDrawMetadataPro } void -AndroidBridge::PreventPanning() { +AndroidBridge::SetPreventPanning(bool aPreventPanning) { ALOG_BRIDGE("AndroidBridge::PreventPanning"); JNIEnv *env = GetJNIEnv(); if (!env) return; - env->CallStaticVoidMethod(mGeckoAppShellClass, jPreventPanning); + env->CallStaticVoidMethod(mGeckoAppShellClass, jSetPreventPanning, (jboolean)aPreventPanning); } diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index 9d0d2f164d5b..a236649fa003 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -230,7 +230,7 @@ public: void ShowInputMethodPicker(); - void PreventPanning(); + void SetPreventPanning(bool aPreventPanning); void HideProgressDialogOnce(); @@ -439,7 +439,7 @@ protected: jmethodID jGetDpi; jmethodID jSetFullScreen; jmethodID jShowInputMethodPicker; - jmethodID jPreventPanning; + jmethodID jSetPreventPanning; jmethodID jHideProgressDialog; jmethodID jPerformHapticFeedback; jmethodID jVibrate1; diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index 55a9ce3b7850..5f417a5e4ce4 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -1579,8 +1579,9 @@ nsWindow::DispatchMultitouchEvent(nsTouchEvent &event, AndroidGeckoEvent *ae) nsEventStatus status; DispatchEvent(&event, status); - if (status == nsEventStatus_eConsumeNoDefault) { - AndroidBridge::Bridge()->PreventPanning(); + bool preventPanning = (status == nsEventStatus_eConsumeNoDefault); + if (preventPanning || action == AndroidMotionEvent::ACTION_MOVE) { + AndroidBridge::Bridge()->SetPreventPanning(preventPanning); return true; } return false; From b5e8d84620029aabb314ab63008afffe4ca937ac Mon Sep 17 00:00:00 2001 From: Brian Birtles Date: Thu, 2 Feb 2012 08:58:58 +0900 Subject: [PATCH 65/90] Bug 720103 - Detect and break create-delete cycles in SMIL timegraphs; r=dholbert --- content/smil/nsSMILTimedElement.cpp | 24 ++++++++++++++++++++++-- content/smil/nsSMILTimedElement.h | 10 +++++----- 2 files changed, 27 insertions(+), 7 deletions(-) diff --git a/content/smil/nsSMILTimedElement.cpp b/content/smil/nsSMILTimedElement.cpp index dbfa2904a738..b138a0040233 100644 --- a/content/smil/nsSMILTimedElement.cpp +++ b/content/smil/nsSMILTimedElement.cpp @@ -233,7 +233,7 @@ const PRUint8 nsSMILTimedElement::sMaxNumInstanceTimes = 100; // Detect if we arrive in some sort of undetected recursive syncbase dependency // relationship -const PRUint16 nsSMILTimedElement::sMaxUpdateIntervalRecursionDepth = 20; +const PRUint8 nsSMILTimedElement::sMaxUpdateIntervalRecursionDepth = 20; //---------------------------------------------------------------------- // Ctor, dtor @@ -252,6 +252,7 @@ nsSMILTimedElement::nsSMILTimedElement() mSeekState(SEEK_NOT_SEEKING), mDeferIntervalUpdates(false), mDoDeferredUpdate(false), + mDeleteCount(0), mUpdateIntervalRecursionDepth(0) { mSimpleDur.SetIndefinite(); @@ -1960,12 +1961,29 @@ nsSMILTimedElement::UpdateCurrentInterval(bool aForceChangeNotice) if (mElementState == STATE_STARTUP) return; + // Although SMIL gives rules for detecting cycles in change notifications, + // some configurations can lead to create-delete-create-delete-etc. cycles + // which SMIL does not consider. + // + // In order to provide consistent behavior in such cases, we detect two + // deletes in a row and then refuse to create any further intervals. That is, + // we say the configuration is invalid. + if (mDeleteCount > 1) { + // When we update the delete count we also set the state to post active, so + // if we're not post active here then something other than + // UpdateCurrentInterval has updated the element state in between and all + // bets are off. + NS_ABORT_IF_FALSE(mElementState == STATE_POSTACTIVE, + "Expected to be in post-active state after performing double delete"); + return; + } + // Check that we aren't stuck in infinite recursion updating some syncbase // dependencies. Generally such situations should be detected in advance and // the chain broken in a sensible and predictable manner, so if we're hitting // this assertion we need to work out how to detect the case that's causing // it. In release builds, just bail out before we overflow the stack. - AutoRestore depthRestorer(mUpdateIntervalRecursionDepth); + AutoRestore depthRestorer(mUpdateIntervalRecursionDepth); if (++mUpdateIntervalRecursionDepth > sMaxUpdateIntervalRecursionDepth) { NS_ABORT_IF_FALSE(false, "Update current interval recursion depth exceeded threshold"); @@ -2026,6 +2044,8 @@ nsSMILTimedElement::UpdateCurrentInterval(bool aForceChangeNotice) // sample (along with firing end events, clearing intervals etc.) RegisterMilestone(); } else if (mElementState == STATE_WAITING) { + AutoRestore deleteCountRestorer(mDeleteCount); + ++mDeleteCount; mElementState = STATE_POSTACTIVE; ResetCurrentInterval(); } diff --git a/content/smil/nsSMILTimedElement.h b/content/smil/nsSMILTimedElement.h index 59e4d041594f..5213180d01ce 100644 --- a/content/smil/nsSMILTimedElement.h +++ b/content/smil/nsSMILTimedElement.h @@ -625,13 +625,13 @@ protected: // Used to batch updates to the timing model class AutoIntervalUpdateBatcher; bool mDeferIntervalUpdates; - bool mDoDeferredUpdate; // Set if an update to the current interval - // was requested while mDeferIntervalUpdates - // was set + bool mDoDeferredUpdate; // Set if an update to the current interval was + // requested while mDeferIntervalUpdates was set // Recursion depth checking - PRUint16 mUpdateIntervalRecursionDepth; - static const PRUint16 sMaxUpdateIntervalRecursionDepth; + PRUint8 mDeleteCount; + PRUint8 mUpdateIntervalRecursionDepth; + static const PRUint8 sMaxUpdateIntervalRecursionDepth; }; #endif // NS_SMILTIMEDELEMENT_H_ From 268d4fdda4c78e8799e595edd0212b1a1ecafa61 Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Wed, 1 Feb 2012 19:05:06 -0500 Subject: [PATCH 66/90] bug 723305 - Tabs menu uses smoothScrollToPosition(), which doesn't exist in eclair r=mfinkle --- mobile/android/base/TabsTray.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/mobile/android/base/TabsTray.java b/mobile/android/base/TabsTray.java index 9b675bd8da5b..1db699eb2a76 100644 --- a/mobile/android/base/TabsTray.java +++ b/mobile/android/base/TabsTray.java @@ -119,8 +119,13 @@ public class TabsTray extends Activity implements GeckoApp.OnTabsChangedListener // Scrolling to the selected tab can happen here if (hasFocus) { int position = mTabsAdapter.getPositionForTab(Tabs.getInstance().getSelectedTab()); - if (position != -1) + if (position == -1) + return; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { mList.smoothScrollToPosition(position); + } else { + /* To Do: Find a way to scroll with Eclair's APIs */ + } } } From 3eceafc02cd576e3f10bf873a1bd541e794151de Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Wed, 1 Feb 2012 16:20:13 -0800 Subject: [PATCH 67/90] Bug 723084 - Remove observers when tabs are destroyed [r=bnicholson] --- mobile/android/chrome/content/browser.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 8e111990bf66..5df8947c827b 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -1508,6 +1508,8 @@ Tab.prototype = { this.browser.removeEventListener("pagehide", this, true); this.browser.removeEventListener("pageshow", this, true); + Services.obs.removeObserver(this, "document-shown"); + // Make sure the previously selected panel remains selected. The selected panel of a deck is // not stable when panels are removed. let selectedPanel = BrowserApp.deck.selectedPanel; From eabddd5e285a84935217016c15a81c1bd698ae10 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Wed, 1 Feb 2012 16:25:08 -0800 Subject: [PATCH 68/90] Bug 720932 - Clean up default search engine code [r=wesj] --- mobile/android/base/AwesomeBar.java | 15 ++++----------- mobile/android/chrome/content/browser.js | 16 +++------------- 2 files changed, 7 insertions(+), 24 deletions(-) diff --git a/mobile/android/base/AwesomeBar.java b/mobile/android/base/AwesomeBar.java index 77953cc8ade3..4ad6cc83e5f4 100644 --- a/mobile/android/base/AwesomeBar.java +++ b/mobile/android/base/AwesomeBar.java @@ -124,7 +124,7 @@ public class AwesomeBar extends Activity implements GeckoEventListener { mAwesomeTabs = (AwesomeBarTabs) findViewById(R.id.awesomebar_tabs); mAwesomeTabs.setOnUrlOpenListener(new AwesomeBarTabs.OnUrlOpenListener() { public void onUrlOpen(String url) { - submitAndFinish(url); + openUrlAndFinish(url); } public void onSearch(String engine) { @@ -134,7 +134,7 @@ public class AwesomeBar extends Activity implements GeckoEventListener { mGoButton.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { - submitAndFinish(mText.getText().toString()); + openUrlAndFinish(mText.getText().toString()); } }); @@ -168,7 +168,7 @@ public class AwesomeBar extends Activity implements GeckoEventListener { (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); if (keyCode == KeyEvent.KEYCODE_ENTER && event.getAction() == KeyEvent.ACTION_DOWN) { - submitAndFinish(mText.getText().toString()); + openUrlAndFinish(mText.getText().toString()); return true; } @@ -208,7 +208,7 @@ public class AwesomeBar extends Activity implements GeckoEventListener { if (event.getAction() != KeyEvent.ACTION_DOWN) return true; - submitAndFinish(mText.getText().toString()); + openUrlAndFinish(mText.getText().toString()); return true; } else { return false; @@ -306,13 +306,6 @@ public class AwesomeBar extends Activity implements GeckoEventListener { } } - private void submitAndFinish(String url) { - if (isSearchUrl(url)) - openSearchAndFinish(url, "__default__"); - else - openUrlAndFinish(url); - } - private void cancelAndFinish() { setResult(Activity.RESULT_CANCELED); finish(); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 5df8947c827b..5006d720bfc1 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -51,9 +51,6 @@ XPCOMUtils.defineLazyGetter(this, "PluralForm", function() { return PluralForm; }); -XPCOMUtils.defineLazyServiceGetter(this, "URIFixup", - "@mozilla.org/docshell/urifixup;1", "nsIURIFixup"); - XPCOMUtils.defineLazyServiceGetter(this, "Haptic", "@mozilla.org/widget/hapticfeedback;1", "nsIHapticFeedback"); @@ -792,19 +789,12 @@ var BrowserApp = { }); }, - getSearchOrFixupURI: function(aParams) { + getSearchOrURI: function getSearchOrURI(aParams) { let uri; if (aParams.engine) { - let engine; - // If the default engine was requested, we just pass the URL through - // and let the third-party fixup send it to the default search. - if (aParams.engine != "__default__") - engine = Services.search.getEngineByName(aParams.engine); - + let engine = Services.search.getEngineByName(aParams.engine); if (engine) uri = engine.getSubmission(aParams.url).uri; - } else { - uri = URIFixup.createFixupURI(aParams.url, Ci.nsIURIFixup.FIXUP_FLAG_ALLOW_KEYWORD_LOOKUP); } return uri ? uri.spec : aParams.url; }, @@ -921,7 +911,7 @@ var BrowserApp = { | Ci.nsIWebNavigation.LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP }; - let url = this.getSearchOrFixupURI(data); + let url = this.getSearchOrURI(data); // Don't show progress throbber for about:home if (url == "about:home") From 455a19d6267b4d5d94cd442af9d8994cfc70d0b0 Mon Sep 17 00:00:00 2001 From: Brian Nicholson Date: Wed, 1 Feb 2012 14:25:50 -0800 Subject: [PATCH 69/90] Bug 719479 - Don't animate tab counter during session restore. r=mfinkle --- mobile/android/base/BrowserToolbar.java | 14 ++++++--- mobile/android/base/GeckoApp.java | 36 +++++++++++++----------- mobile/android/base/Tabs.java | 26 +++++++++++++---- mobile/android/chrome/content/browser.js | 12 ++++++++ 4 files changed, 62 insertions(+), 26 deletions(-) diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index b7577d3a5e37..c9122885e579 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -151,7 +151,7 @@ public class BrowserToolbar extends LinearLayout { addTab(); } }); - mTabs.setImageLevel(1); + mTabs.setImageLevel(0); mCounterColor = 0xFFC7D1DB; @@ -224,8 +224,8 @@ public class BrowserToolbar extends LinearLayout { public int getHighlightColor() { return mColor; } - - public void updateTabs(int count) { + + public void updateTabCountAndAnimate(int count) { if (mCount > count) { mTabsCount.setInAnimation(mSlideDownIn); mTabsCount.setOutAnimation(mSlideDownOut); @@ -267,6 +267,12 @@ public class BrowserToolbar extends LinearLayout { }, 2 * mDuration); } + public void updateTabCount(int count) { + mTabsCount.setCurrentText(String.valueOf(count)); + mTabs.setImageLevel(count); + mTabsCount.setVisibility(count > 1 ? View.VISIBLE : View.INVISIBLE); + } + public void setProgressVisibility(boolean visible) { if (visible) { mFavicon.setImageDrawable(mProgressSpinner); @@ -326,7 +332,7 @@ public class BrowserToolbar extends LinearLayout { setSecurityMode(tab.getSecurityMode()); setProgressVisibility(tab.isLoading()); setShadowVisibility(!(tab.getURL().startsWith("about:"))); - updateTabs(Tabs.getInstance().getCount()); + updateTabCountAndAnimate(Tabs.getInstance().getCount()); } } } diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 97cacfb3530b..70e69bf727ff 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -1659,6 +1659,20 @@ abstract public class GeckoApp checkAndLaunchUpdate(); } + super.onCreate(savedInstanceState); + + setContentView(R.layout.gecko_app); + + mOrientation = getResources().getConfiguration().orientation; + + if (Build.VERSION.SDK_INT >= 11) { + refreshActionBar(); + } else { + mBrowserToolbar = (BrowserToolbar) findViewById(R.id.browser_toolbar); + } + + mBrowserToolbar.setTitle(mLastTitle); + String passedUri = null; String uri = getURIFromIntent(intent); if (uri != null && uri.length() > 0) @@ -1672,8 +1686,12 @@ abstract public class GeckoApp if (profileDir != null) sessionExists = new File(profileDir, "sessionstore.bak").exists(); Log.w(LOGTAG, "zerdatime " + SystemClock.uptimeMillis() + " - finish check sessionstore.bak exists"); - if (!sessionExists) + if (!sessionExists) { + mBrowserToolbar.updateTabCount(1); showAboutHome(); + } + } else { + mBrowserToolbar.updateTabCount(1); } if (sGREDir == null) @@ -1698,20 +1716,6 @@ abstract public class GeckoApp checkAndSetLaunchState(LaunchState.Launching, LaunchState.Launched)) sGeckoThread.start(); - super.onCreate(savedInstanceState); - - setContentView(R.layout.gecko_app); - - mOrientation = getResources().getConfiguration().orientation; - - if (Build.VERSION.SDK_INT >= 11) { - refreshActionBar(); - } else { - mBrowserToolbar = (BrowserToolbar) findViewById(R.id.browser_toolbar); - } - - mBrowserToolbar.setTitle(mLastTitle); - mFavicons = new Favicons(this); // setup gecko layout @@ -1727,7 +1731,7 @@ abstract public class GeckoApp mBrowserToolbar.setTitle(tab.getDisplayTitle()); mBrowserToolbar.setFavicon(tab.getFavicon()); mBrowserToolbar.setProgressVisibility(tab.isLoading()); - mBrowserToolbar.updateTabs(Tabs.getInstance().getCount()); + mBrowserToolbar.updateTabCountAndAnimate(Tabs.getInstance().getCount()); } tabs.setContentResolver(getContentResolver()); diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index 93981efdb33d..012e98bfb55f 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -55,6 +55,7 @@ public class Tabs implements GeckoEventListener { private HashMap tabs; private ArrayList order; private ContentResolver resolver; + private boolean mRestoringSession = false; private Tabs() { tabs = new HashMap(); @@ -68,6 +69,8 @@ public class Tabs implements GeckoEventListener { GeckoAppShell.registerGeckoEventListener("Tab:Close", this); GeckoAppShell.registerGeckoEventListener("Tab:Select", this); GeckoAppShell.registerGeckoEventListener("Tab:ScreenshotData", this); + GeckoAppShell.registerGeckoEventListener("Session:RestoreBegin", this); + GeckoAppShell.registerGeckoEventListener("Session:RestoreEnd", this); } public int getCount() { @@ -88,11 +91,13 @@ public class Tabs implements GeckoEventListener { tabs.put(id, tab); order.add(tab); - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { - public void run() { - GeckoApp.mBrowserToolbar.updateTabs(getCount()); - } - }); + if (!mRestoringSession) { + GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + public void run() { + GeckoApp.mBrowserToolbar.updateTabCountAndAnimate(getCount()); + } + }); + } Log.i(LOGTAG, "Added a tab with id: " + id + ", url: " + url); return tab; @@ -197,7 +202,7 @@ public class Tabs implements GeckoEventListener { GeckoApp.mAppContext.mMainHandler.post(new Runnable() { public void run() { GeckoApp.mAppContext.onTabsChanged(closedTab); - GeckoApp.mBrowserToolbar.updateTabs(Tabs.getInstance().getCount()); + GeckoApp.mBrowserToolbar.updateTabCountAndAnimate(Tabs.getInstance().getCount()); GeckoApp.mDoorHangerPopup.updatePopup(); GeckoApp.mAppContext.hidePlugins(closedTab, true); } @@ -290,6 +295,15 @@ public class Tabs implements GeckoEventListener { return; byte[] compressed = GeckoAppShell.decodeBase64(data.substring(22)); GeckoApp.mAppContext.processThumbnail(tab, null, compressed); + } else if (event.equals("Session:RestoreBegin")) { + mRestoringSession = true; + } else if (event.equals("Session:RestoreEnd")) { + mRestoringSession = false; + GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + public void run() { + GeckoApp.mBrowserToolbar.updateTabCount(getCount()); + } + }); } } catch (Exception e) { Log.i(LOGTAG, "handleMessage throws " + e + " for message: " + event); diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 5006d720bfc1..645f18c5f58b 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -292,6 +292,12 @@ var BrowserApp = { // A restored tab should not be active if we are loading a URL let restoreToFront = false; + sendMessageToJava({ + gecko: { + type: "Session:RestoreBegin" + } + }); + // Open any commandline URLs, except the homepage if (url && url != "about:home") { this.addTab(url); @@ -308,6 +314,12 @@ var BrowserApp = { let params = { selected: restoreToFront }; BrowserApp.addTab("about:home", { showProgress: false }); } + + sendMessageToJava({ + gecko: { + type: "Session:RestoreEnd" + } + }); } }; Services.obs.addObserver(restoreCleanup, "sessionstore-windows-restored", false); From 8a58ee5bb8ef7076e3e137881b7ccaccac3b1886 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Wed, 1 Feb 2012 17:08:04 -0800 Subject: [PATCH 70/90] Bug 723200 - Enable multitouch by default on Android. r=mbrubeck --- mobile/android/app/mobile.js | 2 +- mobile/android/base/gfx/LayerView.java | 39 ++------------------------ 2 files changed, 4 insertions(+), 37 deletions(-) diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index 036411ba5fe4..4d09a5fefb57 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -617,7 +617,7 @@ pref("content.image.allow_locking", false); pref("image.mem.min_discard_timeout_ms", 10000); // enable touch events interfaces -pref("dom.w3c_touch_events.enabled", false); +pref("dom.w3c_touch_events.enabled", true); #ifdef MOZ_SAFE_BROWSING // Safe browsing does nothing unless this pref is set diff --git a/mobile/android/base/gfx/LayerView.java b/mobile/android/base/gfx/LayerView.java index 0e782daea9e6..1e9ae0df3e70 100644 --- a/mobile/android/base/gfx/LayerView.java +++ b/mobile/android/base/gfx/LayerView.java @@ -37,9 +37,6 @@ package org.mozilla.gecko.gfx; -import org.mozilla.gecko.GeckoAppShell; -import org.mozilla.gecko.GeckoEvent; -import org.mozilla.gecko.GeckoEventListener; import org.mozilla.gecko.gfx.FloatSize; import org.mozilla.gecko.gfx.InputConnectionHandler; import org.mozilla.gecko.gfx.LayerController; @@ -55,18 +52,13 @@ import android.util.Log; import java.nio.IntBuffer; import java.util.LinkedList; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - /** * A view rendered by the layer compositor. * * This view delegates to LayerRenderer to actually do the drawing. Its role is largely that of a * mediator between the LayerRenderer and the LayerController. */ -public class LayerView extends GLSurfaceView - implements GeckoEventListener { +public class LayerView extends GLSurfaceView { private Context mContext; private LayerController mController; private InputConnectionHandler mInputConnectionHandler; @@ -78,8 +70,7 @@ public class LayerView extends GLSurfaceView private static String LOGTAG = "GeckoLayerView"; /* List of events to be processed if the page does not prevent them. Should only be touched on the main thread */ private LinkedList mEventQueue = new LinkedList(); - private boolean touchEventsEnabled = false; - private String touchEventsPrefName = "dom.w3c_touch_events.enabled"; + public LayerView(Context context, LayerController controller) { super(context); @@ -97,30 +88,6 @@ public class LayerView extends GLSurfaceView setFocusable(true); setFocusableInTouchMode(true); - - GeckoAppShell.registerGeckoEventListener("Preferences:Data", this); - JSONArray jsonPrefs = new JSONArray(); - jsonPrefs.put(touchEventsPrefName); - GeckoEvent event = new GeckoEvent("Preferences:Get", jsonPrefs.toString()); - GeckoAppShell.sendEventToGecko(event); - } - - public void handleMessage(String event, JSONObject message) { - if (event.equals("Preferences:Data")) { - try { - JSONArray jsonPrefs = message.getJSONArray("preferences"); - for (int i = 0; i < jsonPrefs.length(); i++) { - JSONObject jPref = jsonPrefs.getJSONObject(i); - final String prefName = jPref.getString("name"); - if (prefName.equals(touchEventsPrefName)) { - touchEventsEnabled = jPref.getBoolean("value"); - GeckoAppShell.unregisterGeckoEventListener("Preferences:Data", this); - } - } - } catch(JSONException ex) { - Log.e(LOGTAG, "Error decoding JSON", ex); - } - } } private void addToEventQueue(MotionEvent event) { @@ -142,7 +109,7 @@ public class LayerView extends GLSurfaceView @Override public boolean onTouchEvent(MotionEvent event) { - if (touchEventsEnabled && mController.onTouchEvent(event)) { + if (mController.onTouchEvent(event)) { addToEventQueue(event); return true; } From 217269b30319958ba974482c6f836e328e95224e Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Thu, 2 Feb 2012 01:46:49 +0100 Subject: [PATCH 71/90] Bug 721004 - Load webapi+apps.js inside as a frame script instead of having to copy it to all applications r=cjones --- b2g/chrome/content/shell.js | 16 +- b2g/chrome/content/webapi.js | 1523 ++++++++++++++++++++++++++++++++++ b2g/chrome/jar.mn | 1 + dom/telephony/Telephony.cpp | 12 +- 4 files changed, 1540 insertions(+), 12 deletions(-) create mode 100644 b2g/chrome/content/webapi.js diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index d6fbe1862119..bc015af91700 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -76,7 +76,7 @@ function startupHttpd(baseDir, port) { // FIXME Bug 707625 // until we have a proper security model, add some rights to -// the pre-installed web applications +// the pre-installed web applications function addPermissions(urls) { let permissions = [ 'indexedDB', 'indexedDB-unlimited', 'webapps-manage', 'offline-app' @@ -84,7 +84,7 @@ function addPermissions(urls) { urls.forEach(function(url) { let uri = Services.io.newURI(url, null, null); let allow = Ci.nsIPermissionManager.ALLOW_ACTION; - + permissions.forEach(function(permission) { Services.perms.add(uri, permission, allow); }); @@ -156,6 +156,14 @@ var shell = { return alert(msg); } + // Load webapi+apps.js as a frame script + let frameScriptUrl = 'chrome://browser/content/webapi.js'; + try { + messageManager.loadFrameScript(frameScriptUrl, true); + } catch (e) { + dump('Error when loading ' + frameScriptUrl + ' as a frame script: ' + e + '\n'); + } + let browser = this.home; browser.homePage = homeURL; browser.goHome(); @@ -229,7 +237,7 @@ var shell = { if (!documentElement) return; - let manifest = documentElement.getAttribute("manifest"); + let manifest = documentElement.getAttribute('manifest'); if (!manifest) return; @@ -273,7 +281,7 @@ var shell = { turnScreenOn: function shell_turnScreenOn() { screen.mozEnabled = true; screen.mozBrightness = this.preferredScreenBrightness; - }, + } }; (function VirtualKeyboardManager() { diff --git a/b2g/chrome/content/webapi.js b/b2g/chrome/content/webapi.js new file mode 100644 index 000000000000..9ee5832ea535 --- /dev/null +++ b/b2g/chrome/content/webapi.js @@ -0,0 +1,1523 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +dump('======================= webapi+apps.js ======================= \n'); + +'use strict'; + +(function() { + let { interfaces: Ci, utils: Cu } = Components; + + Cu.import('resource://gre/modules/XPCOMUtils.jsm'); + function generateAPI(window) { + let navigator = window.navigator; + XPCOMUtils.defineLazyGetter(navigator, 'mozSettings', function() { + return new Settings(); + }); + + XPCOMUtils.defineLazyGetter(navigator, 'mozContacts', function() { + return ContactsManager; + }); + + XPCOMUtils.defineLazyGetter(navigator, 'mozApps', function() { + let mozApps = { + enumerate: function mozAppsEnumerate(callback) { + callback(webapps); + } + }; + return mozApps; + }); + + TouchHandler.start(window); + updateApplicationCache(window); + }; + + let progressListener = { + onStateChange: function onStateChange(progress, request, + flags, status) { + }, + + onProgressChange: function onProgressChange(progress, request, + curSelf, maxSelf, + curTotal, maxTotal) { + }, + + onLocationChange: function onLocationChange(progress, request, + locationURI, flags) { + if (locationURI.spec.indexOf('/apps/') == -1) + return; + + content.addEventListener('appwillopen', function(evt) { + let appManager = content.wrappedJSObject.Gaia.AppManager; + let topWindow = appManager.foregroundWindow.contentWindow; + generateAPI(topWindow); + }); + + generateAPI(content.wrappedJSObject); + }, + + onStatusChange: function onStatusChange(progress, request, + status, message) { + }, + + onSecurityChange: function onSecurityChange(progress, request, + state) { + }, + + QueryInterface: function QueryInterface(aIID) { + if (aIID.equals(Ci.nsIWebProgressListener) || + aIID.equals(Ci.nsISupportsWeakReference) || + aIID.equals(Ci.nsISupports)) { + return this; + } + + throw Components.results.NS_ERROR_NO_INTERFACE; + } + }; + + let flags = Ci.nsIWebProgress.NOTIFY_LOCATION; + let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebProgress); + flags = Ci.nsIWebProgress.NOTIFY_ALL; + webProgress.addProgressListener(progressListener, flags); +})(); + + +// WebApps - Application Cache +function updateApplicationCache(window) { + try { + var cache = window.applicationCache; + cache.update(); + + cache.addEventListener('updateready', function updateReady(evt) { + // XXX Add a nice UI when an update is ready asking if the user + // want to reload the application now. + cache.swapCache(); + window.document.location.reload(); + }); + } catch (e) {} +} + + +// MozSettings - Bug 678695 +function Settings() { + content.addEventListener('message', this); +} + +Settings.prototype = { + ERROR_SETTING_UNKNOWN: 0x0001, + + _requests: [], + handleEvent: function settings_handleEvent(event) { + var data = event.data; + if (typeof data !== 'string') + return; + + var cmd = data.split(':'); + if (cmd.length < 4 || cmd[0] != 'settings') + return; + + var method = cmd[1], id = cmd[2], key = cmd[3], value = cmd[4]; + var request = this._requests[id]; + switch (method) { + case 'error': + request.error = this.ERROR_SETTING_UNKNOWN; + this._dispatchEvent(request, request.TYPE_ERROR); + break; + + case 'success': + request.result = new SettingsMessage(key, value); + this._dispatchEvent(request, request.TYPE_SUCCESS); + break; + } + }, + + get: function settings_get(key) { + var request = new SettingsRequest(); + var id = this._requests.length; + this._requests.push(request); + + var msg = 'settings:get:' + id + ':' + key; + content.top.wrappedJSObject.postMessage(msg, '*'); + return request; + }, + + set: function settings_set(key, value) { + var request = new SettingsRequest(); + var id = this._requests.length; + this._requests.push(request); + + var msg = 'settings:set:' + id + ':' + key + ':' + value; + content.top.wrappedJSObject.postMessage(msg, '*'); + return request; + }, + + _dispatchEvent: function(target, type) { + var evt = content.document.createEvent('CustomEvent'); + evt.initCustomEvent(type, true, false, null); + target.dispatchEvent(evt); + } +}; + + +/* ========== nsIDOMMozSettingsRequest ========== */ +function SettingsRequest() { + this.readyState = this.STATE_PROCESSING; + + this.error = null; + this.onerror = null; + // XXX should be an array + this._errorCallback = null; + + this.result = null; + this.onsuccess = null; + // XXX should be an array + this._successCallback = null; +} + +SettingsRequest.prototype = { + // States of the request + STATE_PROCESSING: 'processing', + STATE_DONE: 'done', + + // Types of events + TYPE_SUCCESS: 'success', + TYPE_ERROR: 'error', + + addEventListener: function sr_addEventListener(type, callback) { + switch (type) { + case this.TYPE_SUCCESS: + this._successCallback = callback; + break; + case this.TYPE_ERROR: + this._errorCallback = callback; + break; + } + }, + + removeEventListener: function sr_removeEventListener(type, callback) { + switch (type) { + case this.TYPE_SUCCESS: + this._successCallback = null; + break; + case this.TYPE_ERROR: + this._errorCallback = null; + break; + } + }, + + dispatchEvent: function sr_dispatchEvent(event) { + this.readyState = this.STATE_DONE; + + switch (event.type) { + case this.TYPE_SUCCESS: + if (this._successCallback) + this._successCallback(event); + + if (this.onsuccess) + this.onsuccess(event); + break; + case this.TYPE_ERROR: + if (this._errorCallback) + this._errorCallback(event); + + if (this.onerror) + this.onerror(event); + break; + } + } +}; + +/* ========== nsIDOMMozSettingsMessage ========== */ +function SettingsMessage(name, value) { + this.name = name; + this.value = value; +} + + +// MozApps - Bug 709015 +var webapps = [ + { // browser + installOrigin: 'http://gaiamobile.org:8888', + origin: '../browser', + receipt: null, + installTime: 1323339869000, + manifest: { + 'name': 'Browser', + 'description': 'Gaia Web Browser', + 'launch_path': '/browser.html', + 'developer': { + 'name': 'The Gaia Team', + 'url': 'https://github.com/andreasgal/gaia' + }, + 'icons': { + '120': '/style/icons/Browser.png' + } + } + }, + { // camera + 'installOrigin': 'http://gaiamobile.org:8888', + 'origin': '../camera', + 'receipt': null, + 'installTime': 1323339869000, + manifest: { + 'name': 'Camera', + 'description': 'Gaia Camera', + 'launch_path': '/camera.html', + 'developer': { + 'name': 'The Gaia Team', + 'url': 'https://github.com/andreasgal/gaia' + }, + 'icons': { + '120': '/style/icons/Camera.png' + } + } + }, + { // dialer + 'installOrigin': 'http://gaiamobile.org:8888', + 'origin': '../dialer', + 'receipt': null, + 'installTime': 1323339869000, + manifest: { + 'name': 'Dialer', + 'description': 'Gaia Dialer', + 'launch_path': '/dialer.html', + 'developer': { + 'name': 'The Gaia Team', + 'url': 'https://github.com/andreasgal/gaia' + }, + 'icons': { + '120': '/style/icons/Phone.png' + } + } + }, + { // gallery + 'installOrigin': 'http://gaiamobile.org:8888', + 'origin': '../gallery', + 'receipt': null, + 'installTime': 1323339869000, + manifest: { + 'name': 'Gallery', + 'description': 'Gaia Gallery', + 'launch_path': '/gallery.html', + 'developer': { + 'name': 'The Gaia Team', + 'url': 'https://github.com/andreasgal/gaia' + }, + 'icons': { + '120': '/style/icons/Gallery.png' + } + } + }, + { // music + 'installOrigin': 'http://gaiamobile.org:8888', + 'origin': '../music', + 'receipt': null, + 'installTime': 1323339869000, + manifest: { + 'name': 'Music', + 'description': 'Gaia Music', + 'launch_path': '/music.html', + 'developer': { + 'name': 'The Gaia Team', + 'url': 'https://github.com/andreasgal/gaia' + }, + 'icons': { + '120': '/style/icons/Music.png' + } + } + }, + { // market + 'installOrigin': 'http://gaiamobile.org:8888', + 'origin': '../market', + 'receipt': null, + 'installTime': 1323339869000, + manifest: { + 'name': 'Market', + 'description': 'Market for downloading and installing apps', + 'launch_path': '/market.html', + 'developer': { + 'name': 'The Gaia Team', + 'url': 'https://github.com/andreasgal/gaia' + }, + 'icons': { + '120': '/style/icons/Market.png' + } + } + }, + { // settings + 'installOrigin': 'http://gaiamobile.org:8888', + 'origin': '../settings', + 'receipt': null, + 'installTime': 1323339869000, + manifest: { + 'name': 'Settings', + 'description': 'Gaia Settings', + 'launch_path': '/settings.html', + 'developer': { + 'name': 'The Gaia Team', + 'url': 'https://github.com/andreasgal/gaia' + }, + 'icons': { + '120': '/style/icons/Settings.png' + } + } + }, + { // sms + 'installOrigin': 'http://gaiamobile.org:8888', + 'origin': '../sms', + 'receipt': null, + 'installTime': 1323339869000, + manifest: { + 'name': 'Messages', + 'description': 'Gaia Messages', + 'launch_path': '/sms.html', + 'developer': { + 'name': 'The Gaia Team', + 'url': 'https://github.com/andreasgal/gaia' + }, + 'icons': { + '120': '/style/icons/Messages.png' + } + } + } +]; + + +// Kinetic panning +var TouchHandler = { + touchState: { active: false, startX: 0, startY: 0 }, + events: ['touchstart', 'touchmove', 'touchend', + 'contextmenu'], + start: function th_start(window) { + this._window = window; + this.events.forEach((function(evt) { + window.addEventListener(evt, this); + }).bind(this)); + }, + onTouchStart: function th_touchStart(evt) { + var touchState = this.touchState; + touchState.active = true; + touchState.startTime = evt.timeStamp; + + this.startX = this.lastX = evt.pageX; + this.startY = this.lastY = evt.pageY; + }, + onTouchEnd: function th_touchEnd(evt) { + var touchState = this.touchState; + if (!touchState.active) + return; + touchState.active = false; + + this.startX = this.startY = 0; + this.lastX = this.lastY = 0; + }, + onTouchMove: function th_touchMove(evt) { + var offsetX = this.lastX - evt.pageX; + var offsetY = this.lastY - evt.pageY; + + var element = this.target; + element.scrollLeft += offsetX; + element.scrollTop += offsetY; + + this.lastX = evt.pageX; + this.lastY = evt.pageY; + }, + isPan: function isPan(x1, y1, x2, y2) { + var kRadius = 10; + return Math.abs(x1 - x2) > kRadius || Math.abs(y1 - y2) > kRadius; + }, + getPannableTarget: function isPannable(target) { + // TODO Check if the target is pannable + var targetNode = target; + try { + while (targetNode) { + var computedStyle = this._window.getComputedStyle(targetNode, null); + if (computedStyle && + computedStyle.getPropertyValue('overflow') == 'scroll') { + + var targetHeight = targetNode.getBoundingClientRect().height; + if (targetNode.scrollHeight > targetHeight) + return targetNode; + } + targetNode = targetNode.parentNode; + } + } catch (e) { + } + return null; + }, + handleEvent: function th_handleEvent(evt) { + if (evt.getPreventDefault()) + return; + + switch (evt.type) { + case 'touchstart': + var pannableTarget = this.getPannableTarget(evt.originalTarget); + if (!pannableTarget) + return; + + this.target = pannableTarget; + this.onTouchStart(evt.touches ? evt.touches[0] : evt); + break; + + case 'touchmove': + if (!this.target) + break; + + var touchEvent = evt.touches ? evt.touches[0] : evt; + if (!this.panning) { + var pan = this.isPan(evt.pageX, evt.pageY, this.startX, this.startY); + if (pan) { + this.panning = true; + this.startX = this.lastX = touchEvent.pageX; + this.startY = this.lastY = touchEvent.pageY; + this.target.setAttribute('panning', true); + this.target.setCapture(false); + } + } + this.onTouchMove(touchEvent); + break; + + case 'touchend': + if (!this.target) + return; + + if (this.panning) { + document.releaseCapture(); + this.target.removeAttribute('panning'); + this.panning = null; + } + this.onTouchEnd(evt.touches ? evt.touches[0] : evt); + this.target = null; + break; + } + } +}; + + +// Bug 674720 - webContacts +var contacts = [ + { + id: '3', + displayName: 'Coby Newman', + name: { + familyName: ['Coby'], + givenName: ['Newman'] + }, + phones: ['1-823-949-7735'], + emails: ['posuere.at@hendreritaarcu.com'] + }, + { + id: '6', + displayName: 'Caesar Velasquez', + name: { + familyName: ['Caesar'], + givenName: ['Velasquez'] + }, + phones: ['1-355-185-5419'], + emails: ['fames@Duis.org'] + }, + { + id: '9', + displayName: 'Hamilton Farrell', + name: { + familyName: ['Hamilton'], + givenName: ['Farrell'] + }, + phones: ['1-682-456-9186'], + emails: ['sem@Uttinciduntvehicula.com'] + }, + { + id: '12', + displayName: 'Emery Livingston', + name: { + familyName: ['Emery'], + givenName: ['Livingston'] + }, + phones: ['1-510-151-9801'], + emails: ['orci.luctus.et@massaInteger.com'] + }, + { + id: '15', + displayName: 'Griffith Heath', + name: { + familyName: ['Griffith'], + givenName: ['Heath'] + }, + phones: ['1-800-719-3201'], + emails: ['dapibus@Inlorem.ca'] + }, + { + id: '18', + displayName: 'Luke Stuart', + name: { + familyName: ['Luke'], + givenName: ['Stuart'] + }, + phones: ['1-120-910-1976'], + emails: ['congue@nibh.ca'] + }, + { + id: '21', + displayName: 'Brennan Love', + name: { + familyName: ['Brennan'], + givenName: ['Love'] + }, + phones: ['1-724-155-2807'], + emails: ['interdum.libero.dui@cursusvestibulum.edu'] + }, + { + id: '24', + displayName: 'Lamar Meadows', + name: { + familyName: ['Lamar'], + givenName: ['Meadows'] + }, + phones: ['1-976-164-8769'], + emails: ['tincidunt@non.com'] + }, + { + id: '27', + displayName: 'Erasmus Flynn', + name: { + familyName: ['Erasmus'], + givenName: ['Flynn'] + }, + phones: ['1-488-678-3487'], + emails: ['lorem.ut.aliquam@eu.ca'] + }, + { + id: '30', + displayName: 'Aladdin Ellison', + name: { + familyName: ['Aladdin'], + givenName: ['Ellison'] + }, + phones: ['1-977-743-6797'], + emails: ['sociosqu.ad@sollicitudin.org'] + }, + { + id: '33', + displayName: 'Valentine Rasmussen', + name: { + familyName: ['Valentine'], + givenName: ['Rasmussen'] + }, + phones: ['1-265-504-2025'], + emails: ['ultrices.iaculis@acsem.edu'] + }, + { + id: '36', + displayName: 'Deacon Murphy', + name: { + familyName: ['Deacon'], + givenName: ['Murphy'] + }, + phones: ['1-770-450-1221'], + emails: ['varius@erat.edu'] + }, + { + id: '39', + displayName: 'Paul Kennedy', + name: { + familyName: ['Paul'], + givenName: ['Kennedy'] + }, + phones: ['1-689-891-3529'], + emails: ['ac.arcu@vitae.edu'] + }, + { + id: '42', + displayName: 'Aaron Chase', + name: { + familyName: ['Aaron'], + givenName: ['Chase'] + }, + phones: ['1-451-574-7937'], + emails: ['tempor.bibendum.Donec@pharetraQuisque.edu'] + }, + { + id: '45', + displayName: 'Geoffrey Dunn', + name: { + familyName: ['Geoffrey'], + givenName: ['Dunn'] + }, + phones: ['1-924-387-2395'], + emails: ['a.malesuada@tellusPhasellus.com'] + }, + { + id: '48', + displayName: 'Ashton Russo', + name: { + familyName: ['Ashton'], + givenName: ['Russo'] + }, + phones: ['1-182-776-5600'], + emails: ['Aliquam.vulputate.ullamcorper@faucibusorci.edu'] + }, + { + id: '51', + displayName: 'Owen Noble', + name: { + familyName: ['Owen'], + givenName: ['Noble'] + }, + phones: ['1-463-693-1336'], + emails: ['et@vulputateveliteu.ca'] + }, + { + id: '54', + displayName: 'Kamal Blake', + name: { + familyName: ['Kamal'], + givenName: ['Blake'] + }, + phones: ['1-636-197-1985'], + emails: ['tempor@malesuada.edu'] + }, + { + id: '57', + displayName: 'Tyrone Delaney', + name: { + familyName: ['Tyrone'], + givenName: ['Delaney'] + }, + phones: ['1-886-920-6283'], + emails: ['est@aliquetsemut.com'] + }, + { + id: '60', + displayName: 'Ciaran Sellers', + name: { + familyName: ['Ciaran'], + givenName: ['Sellers'] + }, + phones: ['1-315-414-0323'], + emails: ['Etiam@Nulla.com'] + }, + { + id: '63', + displayName: 'Bernard Alford', + name: { + familyName: ['Bernard'], + givenName: ['Alford'] + }, + phones: ['1-430-958-2651'], + emails: ['elementum.lorem.ut@sociisnatoque.edu'] + }, + { + id: '66', + displayName: 'Kamal Cote', + name: { + familyName: ['Kamal'], + givenName: ['Cote'] + }, + phones: ['1-666-609-9141'], + emails: ['eleifend.egestas@cursus.edu'] + }, + { + id: '69', + displayName: 'Lucius Mckee', + name: { + familyName: ['Lucius'], + givenName: ['Mckee'] + }, + phones: ['1-224-590-6780'], + emails: ['Fusce.dolor@tellusnon.org'] + }, + { + id: '72', + displayName: 'Dale Coleman', + name: { + familyName: ['Dale'], + givenName: ['Coleman'] + }, + phones: ['1-320-245-3036'], + emails: ['dapibus.rutrum@ametlorem.org'] + }, + { + id: '75', + displayName: 'Kermit Nguyen', + name: { + familyName: ['Kermit'], + givenName: ['Nguyen'] + }, + phones: ['1-247-825-8563'], + emails: ['per@risusMorbi.org'] + }, + { + id: '78', + displayName: 'Timon Horton', + name: { + familyName: ['Timon'], + givenName: ['Horton'] + }, + phones: ['1-739-233-8981'], + emails: ['Etiam@nonummyultriciesornare.ca'] + }, + { + id: '81', + displayName: 'Dale Lamb', + name: { + familyName: ['Dale'], + givenName: ['Lamb'] + }, + phones: ['1-640-507-8295'], + emails: ['dapibus.id@pedeac.edu'] + }, + { + id: '84', + displayName: 'Owen Acevedo', + name: { + familyName: ['Owen'], + givenName: ['Acevedo'] + }, + phones: ['1-403-201-3170'], + emails: ['porttitor.tellus.non@dolorFusce.edu'] + }, + { + id: '87', + displayName: 'Richard Mckee', + name: { + familyName: ['Richard'], + givenName: ['Mckee'] + }, + phones: ['1-783-513-0684'], + emails: ['senectus.et.netus@Vestibulum.com'] + }, + { + id: '90', + displayName: 'Elijah Bass', + name: { + familyName: ['Elijah'], + givenName: ['Bass'] + }, + phones: ['1-632-950-0553'], + emails: ['erat@sapien.com'] + }, + { + id: '93', + displayName: 'Barrett Wells', + name: { + familyName: ['Barrett'], + givenName: ['Wells'] + }, + phones: ['1-112-180-5617'], + emails: ['interdum.ligula@varius.edu'] + }, + { + id: '96', + displayName: 'Herman Meyer', + name: { + familyName: ['Herman'], + givenName: ['Meyer'] + }, + phones: ['1-296-252-5507'], + emails: ['urna@vitaealiquameros.org'] + }, + { + id: '99', + displayName: 'Ashton Hinton', + name: { + familyName: ['Ashton'], + givenName: ['Hinton'] + }, + phones: ['1-695-256-8929'], + emails: ['lorem@mattisornare.org'] + }, + { + id: '102', + displayName: 'Harrison Marsh', + name: { + familyName: ['Harrison'], + givenName: ['Marsh'] + }, + phones: ['1-897-458-1730'], + emails: ['pharetra.felis.eget@auctor.com'] + }, + { + id: '105', + displayName: 'Benedict Santana', + name: { + familyName: ['Benedict'], + givenName: ['Santana'] + }, + phones: ['1-565-457-4828'], + emails: ['amet.metus.Aliquam@Maecenas.org'] + }, + { + id: '108', + displayName: 'David Church', + name: { + familyName: ['David'], + givenName: ['Church'] + }, + phones: ['1-179-353-3314'], + emails: ['Nullam.enim@Utsagittis.edu'] + }, + { + id: '111', + displayName: 'Colt Wolfe', + name: { + familyName: ['Colt'], + givenName: ['Wolfe'] + }, + phones: ['1-587-970-8581'], + emails: ['hendrerit.Donec.porttitor@tinciduntaliquam.org'] + }, + { + id: '114', + displayName: 'Carlos Bishop', + name: { + familyName: ['Carlos'], + givenName: ['Bishop'] + }, + phones: ['1-963-305-6702'], + emails: ['Nam@cursusNunc.org'] + }, + { + id: '117', + displayName: 'Dominic Ware', + name: { + familyName: ['Dominic'], + givenName: ['Ware'] + }, + phones: ['1-609-458-5449'], + emails: ['Fusce.aliquet@Etiam.ca'] + }, + { + id: '120', + displayName: 'Phillip Whitley', + name: { + familyName: ['Phillip'], + givenName: ['Whitley'] + }, + phones: ['1-284-955-1766'], + emails: ['per.inceptos.hymenaeos@nequesedsem.ca'] + }, + { + id: '123', + displayName: 'Valentine Sargent', + name: { + familyName: ['Valentine'], + givenName: ['Sargent'] + }, + phones: ['1-346-890-6417'], + emails: ['nec@dolorFusce.com'] + }, + { + id: '126', + displayName: 'Gabriel Huber', + name: { + familyName: ['Gabriel'], + givenName: ['Huber'] + }, + phones: ['1-399-465-0589'], + emails: ['pretium.neque@nislsemconsequat.ca'] + }, + { + id: '129', + displayName: 'George Tyler', + name: { + familyName: ['George'], + givenName: ['Tyler'] + }, + phones: ['1-739-571-2737'], + emails: ['blandit.viverra.Donec@dictum.ca'] + }, + { + id: '132', + displayName: 'Asher Carey', + name: { + familyName: ['Asher'], + givenName: ['Carey'] + }, + phones: ['1-477-425-4723'], + emails: ['torquent.per.conubia@blanditNamnulla.edu'] + }, + { + id: '135', + displayName: 'Anthony Solomon', + name: { + familyName: ['Anthony'], + givenName: ['Solomon'] + }, + phones: ['1-570-753-4296'], + emails: ['risus.Nunc@hendreritconsectetuercursus.com'] + }, + { + id: '138', + displayName: 'Griffith Fuller', + name: { + familyName: ['Griffith'], + givenName: ['Fuller'] + }, + phones: ['1-779-242-5342'], + emails: ['Suspendisse@aliquam.ca'] + }, + { + id: '141', + displayName: 'Beau Brewer', + name: { + familyName: ['Beau'], + givenName: ['Brewer'] + }, + phones: ['1-664-184-7334'], + emails: ['magna.tellus.faucibus@ultricesposuerecubilia.com'] + }, + { + id: '144', + displayName: 'Jordan Campbell', + name: { + familyName: ['Jordan'], + givenName: ['Campbell'] + }, + phones: ['1-593-938-2525'], + emails: ['Curae;.Phasellus@Morbiquis.ca'] + }, + { + id: '147', + displayName: 'Cyrus Cabrera', + name: { + familyName: ['Cyrus'], + givenName: ['Cabrera'] + }, + phones: ['1-915-748-1349'], + emails: ['lorem.tristique@acmetus.edu'] + }, + { + id: '150', + displayName: 'Hamilton Boone', + name: { + familyName: ['Hamilton'], + givenName: ['Boone'] + }, + phones: ['1-278-421-9845'], + emails: ['non.sapien@quamdignissimpharetra.edu'] + }, + { + id: '153', + displayName: 'Wallace Donovan', + name: { + familyName: ['Wallace'], + givenName: ['Donovan'] + }, + phones: ['1-940-175-9334'], + emails: ['justo@lacusMaurisnon.org'] + }, + { + id: '156', + displayName: 'Kirk Buckley', + name: { + familyName: ['Kirk'], + givenName: ['Buckley'] + }, + phones: ['1-283-177-6304'], + emails: ['Cras@Morbinon.edu'] + }, + { + id: '159', + displayName: 'Simon Hall', + name: { + familyName: ['Simon'], + givenName: ['Hall'] + }, + phones: ['1-269-202-5174'], + emails: ['mus.Proin@dolor.org'] + }, + { + id: '162', + displayName: 'Trevor Rush', + name: { + familyName: ['Trevor'], + givenName: ['Rush'] + }, + phones: ['1-865-595-9074'], + emails: ['Fusce@Donec.edu'] + }, + { + id: '165', + displayName: 'Todd Mccormick', + name: { + familyName: ['Todd'], + givenName: ['Mccormick'] + }, + phones: ['1-398-916-3514'], + emails: ['at@ornareelit.org'] + }, + { + id: '168', + displayName: 'Yuli Gay', + name: { + familyName: ['Yuli'], + givenName: ['Gay'] + }, + phones: ['1-198-196-4256'], + emails: ['Sed.congue.elit@Inornare.edu'] + }, + { + id: '171', + displayName: 'Joseph Frazier', + name: { + familyName: ['Joseph'], + givenName: ['Frazier'] + }, + phones: ['1-969-410-7180'], + emails: ['faucibus.ut.nulla@massa.org'] + }, + { + id: '174', + displayName: 'Ali Chase', + name: { + familyName: ['Ali'], + givenName: ['Chase'] + }, + phones: ['1-598-924-6112'], + emails: ['eu.elit@necanteMaecenas.edu'] + }, + { + id: '177', + displayName: 'Guy Simpson', + name: { + familyName: ['Guy'], + givenName: ['Simpson'] + }, + phones: ['1-558-377-3714'], + emails: ['in@mauriselit.edu'] + }, + { + id: '180', + displayName: 'Ivan Wynn', + name: { + familyName: ['Ivan'], + givenName: ['Wynn'] + }, + phones: ['1-274-885-0477'], + emails: ['lobortis.quis@Sed.com'] + }, + { + id: '183', + displayName: 'Preston Carpenter', + name: { + familyName: ['Preston'], + givenName: ['Carpenter'] + }, + phones: ['1-758-120-5270'], + emails: ['elit.Curabitur@vehiculaaliquet.edu'] + }, + { + id: '186', + displayName: 'Demetrius Santos', + name: { + familyName: ['Demetrius'], + givenName: ['Santos'] + }, + phones: ['1-913-961-7009'], + emails: ['id@magnaPhasellusdolor.com'] + }, + { + id: '189', + displayName: 'Dale Franklin', + name: { + familyName: ['Dale'], + givenName: ['Franklin'] + }, + phones: ['1-443-971-0116'], + emails: ['velit.Pellentesque@IntegerurnaVivamus.com'] + }, + { + id: '192', + displayName: 'Abraham Randolph', + name: { + familyName: ['Abraham'], + givenName: ['Randolph'] + }, + phones: ['1-368-169-0957'], + emails: ['egestas@maurisidsapien.com'] + }, + { + id: '195', + displayName: 'Hu Avila', + name: { + familyName: ['Hu'], + givenName: ['Avila'] + }, + phones: ['1-311-333-8877'], + emails: ['metus@adipiscinglacusUt.com'] + }, + { + id: '198', + displayName: 'Garth Trujillo', + name: { + familyName: ['Garth'], + givenName: ['Trujillo'] + }, + phones: ['1-409-494-1231'], + emails: ['commodo.hendrerit.Donec@etnunc.ca'] + }, + { + id: '201', + displayName: 'Quamar Buchanan', + name: { + familyName: ['Quamar'], + givenName: ['Buchanan'] + }, + phones: ['1-114-992-7225'], + emails: ['tellus@consequatpurusMaecenas.ca'] + }, + { + id: '204', + displayName: 'Ulysses Bishop', + name: { + familyName: ['Ulysses'], + givenName: ['Bishop'] + }, + phones: ['1-485-518-5941'], + emails: ['fermentum.fermentum.arcu@amalesuadaid.com'] + }, + { + id: '207', + displayName: 'Avram Knapp', + name: { + familyName: ['Avram'], + givenName: ['Knapp'] + }, + phones: ['1-307-139-5554'], + emails: ['est.ac.mattis@ultricesmauris.ca'] + }, + { + id: '210', + displayName: 'Conan Grant', + name: { + familyName: ['Conan'], + givenName: ['Grant'] + }, + phones: ['1-331-936-0280'], + emails: ['turpis@odio.com'] + }, + { + id: '213', + displayName: 'Chester Kemp', + name: { + familyName: ['Chester'], + givenName: ['Kemp'] + }, + phones: ['1-554-119-4848'], + emails: ['Aenean.gravida.nunc@eu.org'] + }, + { + id: '216', + displayName: 'Hedley Dudley', + name: { + familyName: ['Hedley'], + givenName: ['Dudley'] + }, + phones: ['1-578-607-6287'], + emails: ['Nunc@dignissimtemporarcu.ca'] + }, + { + id: '219', + displayName: 'Jermaine Avila', + name: { + familyName: ['Jermaine'], + givenName: ['Avila'] + }, + phones: ['1-860-455-2283'], + emails: ['accumsan@ametdapibusid.ca'] + }, + { + id: '222', + displayName: 'Kamal Hamilton', + name: { + familyName: ['Kamal'], + givenName: ['Hamilton'] + }, + phones: ['1-650-389-0920'], + emails: ['Fusce.dolor@nuncsed.ca'] + }, + { + id: '225', + displayName: 'Castor Maxwell', + name: { + familyName: ['Castor'], + givenName: ['Maxwell'] + }, + phones: ['1-260-489-7135'], + emails: ['diam.lorem@a.ca'] + }, + { + id: '228', + displayName: 'Lyle Burris', + name: { + familyName: ['Lyle'], + givenName: ['Burris'] + }, + phones: ['1-250-343-2038'], + emails: ['eget.lacus@tempordiamdictum.com'] + }, + { + id: '231', + displayName: 'Merrill Dalton', + name: { + familyName: ['Merrill'], + givenName: ['Dalton'] + }, + phones: ['1-851-675-1381'], + emails: ['eu.tempor@blanditmattisCras.edu'] + }, + { + id: '234', + displayName: 'Ezekiel Medina', + name: { + familyName: ['Ezekiel'], + givenName: ['Medina'] + }, + phones: ['1-389-582-3443'], + emails: ['lectus.sit@interdum.ca'] + }, + { + id: '237', + displayName: 'Len Tran', + name: { + familyName: ['Len'], + givenName: ['Tran'] + }, + phones: ['1-434-573-6114'], + emails: ['turpis.Aliquam.adipiscing@montesnasceturridiculus.com'] + }, + { + id: '240', + displayName: 'Len Dominguez', + name: { + familyName: ['Len'], + givenName: ['Dominguez'] + }, + phones: ['1-144-489-7487'], + emails: ['augue@Innec.ca'] + }, + { + id: '243', + displayName: 'Paul Lane', + name: { + familyName: ['Paul'], + givenName: ['Lane'] + }, + phones: ['1-448-169-4312'], + emails: ['lectus.Cum.sociis@dolornonummyac.org'] + }, + { + id: '246', + displayName: 'Eric Horne', + name: { + familyName: ['Eric'], + givenName: ['Horne'] + }, + phones: ['1-124-862-6890'], + emails: ['commodo.tincidunt.nibh@eleifendnuncrisus.com'] + }, + { + id: '249', + displayName: 'Elton Ellis', + name: { + familyName: ['Elton'], + givenName: ['Ellis'] + }, + phones: ['1-492-834-0019'], + emails: ['lorem.eu.metus@felis.ca'] + }, + { + id: '252', + displayName: 'Jameson Snyder', + name: { + familyName: ['Jameson'], + givenName: ['Snyder'] + }, + phones: ['1-811-590-5893'], + emails: ['fermentum@Nuncmaurissapien.org'] + }, + { + id: '255', + displayName: 'Micah Shelton', + name: { + familyName: ['Micah'], + givenName: ['Shelton'] + }, + phones: ['1-402-504-4026'], + emails: ['Nunc.mauris@malesuada.ca'] + }, + { + id: '258', + displayName: 'Evan Lester', + name: { + familyName: ['Evan'], + givenName: ['Lester'] + }, + phones: ['1-535-915-3570'], + emails: ['libero@adipiscingfringillaporttitor.org'] + }, + { + id: '261', + displayName: 'Reuben Dalton', + name: { + familyName: ['Reuben'], + givenName: ['Dalton'] + }, + phones: ['1-296-598-2504'], + emails: ['tincidunt.vehicula.risus@Craseutellus.com'] + }, + { + id: '264', + displayName: 'Beau Baird', + name: { + familyName: ['Beau'], + givenName: ['Baird'] + }, + phones: ['1-525-882-9957'], + emails: ['urna.suscipit.nonummy@facilisisvitae.com'] + }, + { + id: '267', + displayName: 'Hedley Olsen', + name: { + familyName: ['Hedley'], + givenName: ['Olsen'] + }, + phones: ['1-945-295-5863'], + emails: ['vulputate.ullamcorper@Vivamusnisi.org'] + }, + { + id: '270', + displayName: 'Oliver Todd', + name: { + familyName: ['Oliver'], + givenName: ['Todd'] + }, + phones: ['1-551-447-1296'], + emails: ['Donec.egestas@rutrum.edu'] + }, + { + id: '273', + displayName: 'Keegan Mayo', + name: { + familyName: ['Keegan'], + givenName: ['Mayo'] + }, + phones: ['1-351-848-2796'], + emails: ['ridiculus@Nuncsed.ca'] + }, + { + id: '276', + displayName: 'Wang Cote', + name: { + familyName: ['Wang'], + givenName: ['Cote'] + }, + phones: ['1-439-568-2013'], + emails: ['Morbi@tinciduntduiaugue.org'] + }, + { + id: '279', + displayName: 'Hyatt Rowe', + name: { + familyName: ['Hyatt'], + givenName: ['Rowe'] + }, + phones: ['1-596-765-3807'], + emails: ['eu.erat.semper@enimnonnisi.com'] + }, + { + id: '282', + displayName: 'Cade Wyatt', + name: { + familyName: ['Cade'], + givenName: ['Wyatt'] + }, + phones: ['1-988-289-5924'], + emails: ['erat.nonummy@sedpedeCum.com'] + }, + { + id: '285', + displayName: 'Stephen Vincent', + name: { + familyName: ['Stephen'], + givenName: ['Vincent'] + }, + phones: ['1-954-435-1259'], + emails: ['nec.euismod@ultricies.ca'] + }, + { + id: '288', + displayName: 'Tobias Cherry', + name: { + familyName: ['Tobias'], + givenName: ['Cherry'] + }, + phones: ['1-270-763-1111'], + emails: ['Nulla.aliquet@sit.com'] + }, + { + id: '291', + displayName: 'Keane Trevino', + name: { + familyName: ['Keane'], + givenName: ['Trevino'] + }, + phones: ['1-794-929-8599'], + emails: ['sem.semper.erat@Aliquamnecenim.edu'] + }, + { + id: '294', + displayName: 'Kennedy Cooley', + name: { + familyName: ['Kennedy'], + givenName: ['Cooley'] + }, + phones: ['1-725-946-1901'], + emails: ['urna.justo@Duismienim.edu'] + }, + { + id: '297', + displayName: 'Lucian Pope', + name: { + familyName: ['Lucian'], + givenName: ['Pope'] + }, + phones: ['1-186-946-8356'], + emails: ['justo.Proin@dis.com'] + }, + { + id: '300', + displayName: 'Hu Combs', + name: { + familyName: ['Hu'], + givenName: ['Combs'] + }, + phones: ['1-398-488-5222'], + emails: ['faucibus.lectus@nuncsedpede.com'] + } +]; + +var ContactsManager = { + contacts: contacts, + find: function contactsManager(fields, successCallback, errorCallback) { + var contacts = this.contacts.slice(); + successCallback(contacts); + }, + create: function contactsCreate(successCallback, errorCallback, contact) { + this.contacts.push(contact); + successCallback(); + }, + delete: function contactsDelete(successCallback, errorCallback, id) { + var count = contacts.length; + for (var i = 0; i < count; i++) { + if (contacts[i].id != id) + continue; + var oldContact = contacts.slice(i, 1); + successCallback(oldContact); + return; + } + errorCallback(); + } +}; + diff --git a/b2g/chrome/jar.mn b/b2g/chrome/jar.mn index 3b5ca8a7cbdf..fe8b2eaa3762 100644 --- a/b2g/chrome/jar.mn +++ b/b2g/chrome/jar.mn @@ -9,6 +9,7 @@ chrome.jar: content/touch.js (content/touch.js) content/commandUtil.js (content/commandUtil.js) content/httpd.js (content/httpd.js) + content/webapi.js (content/webapi.js) content/content.css (content/content.css) % override chrome://global/content/netError.xhtml chrome://browser/content/netError.xhtml diff --git a/dom/telephony/Telephony.cpp b/dom/telephony/Telephony.cpp index 8dce5ac482be..392f7a2a544b 100644 --- a/dom/telephony/Telephony.cpp +++ b/dom/telephony/Telephony.cpp @@ -57,7 +57,7 @@ USING_TELEPHONY_NAMESPACE using mozilla::Preferences; -#define DOM_TELEPHONY_APP_PHONE_URL_PREF "dom.telephony.app.phone.url" +#define DOM_TELEPHONY_WHITELIST "dom.telephony.whitelist" namespace { @@ -495,13 +495,9 @@ NS_NewTelephony(nsPIDOMWindow* aWindow, nsIDOMTelephony** aTelephony) rv = documentURI->GetSpec(documentURL); NS_ENSURE_SUCCESS(rv, rv); - // The pref may not exist but in that case we deny access just as we do if - // the url doesn't match. - nsCString phoneAppURL; - if (NS_FAILED(Preferences::GetCString(DOM_TELEPHONY_APP_PHONE_URL_PREF, - &phoneAppURL)) || - !phoneAppURL.Equals(documentURL, - nsCaseInsensitiveCStringComparator())) { + // Fail if the node principal isn't trusted. + if (!nsContentUtils::URIIsChromeOrInPref(documentURI, + DOM_TELEPHONY_WHITELIST)) { *aTelephony = nsnull; return NS_OK; } From 70c7d0c9cb59142354ec790b6cb78f6746917d3c Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Thu, 2 Feb 2012 01:46:49 +0100 Subject: [PATCH 72/90] Bug 721777 - B2G should open new windows as new frames in Gaia r=bz,fabrice --- b2g/app/b2g.js | 10 ++++++++++ b2g/chrome/content/shell.js | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index b24ba90d1e24..6b9e24273f22 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -394,6 +394,15 @@ pref("layers.acceleration.force-enabled", true); pref("dom.screenEnabledProperty.enabled", true); pref("dom.screenBrightnessProperty.enabled", true); +// handle links targeting new windows +// 1=current window/tab, 2=new window, 3=new tab in most recent window +pref("browser.link.open_newwindow", 3); + +// 0: no restrictions - divert everything +// 1: don't divert window.open at all +// 2: don't divert window.open with features +pref("browser.link.open_newwindow.restriction", 0); + // Enable browser frame pref("dom.mozBrowserFramesEnabled", true); pref("dom.mozBrowserFramesWhitelist", "http://localhost:6666"); @@ -401,6 +410,7 @@ pref("dom.mozBrowserFramesWhitelist", "http://localhost:6666"); // Temporary permission hack for WebSMS pref("dom.sms.enabled", true); pref("dom.sms.whitelist", "file://,http://localhost:6666"); + // Ignore X-Frame-Options headers. pref("b2g.ignoreXFrameOptions", true); diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index bc015af91700..94bed7b9ae6d 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -225,6 +225,10 @@ var shell = { case 'load': this.home.removeEventListener('load', this, true); this.turnScreenOn(); + + let chromeWindow = window.QueryInterface(Ci.nsIDOMChromeWindow); + chromeWindow.browserDOMWindow = new nsBrowserAccess(); + this.sendEvent(window, 'ContentStart'); break; case 'MozApplicationManifest': @@ -356,3 +360,34 @@ MozKeyboard.prototype = { } }; + +function nsBrowserAccess() { +} + +nsBrowserAccess.prototype = { + QueryInterface: XPCOMUtils.generateQI([Ci.nsIBrowserDOMWindow]), + + openURI: function openURI(uri, opener, where, context) { + // TODO This should be replaced by an 'open-browser-window' intent + let contentWindow = content.wrappedJSObject; + if (!('getApplicationManager' in contentWindow)) + return null; + + let applicationManager = contentWindow.getApplicationManager(); + if (!applicationManager) + return null; + + let url = uri ? uri.spec : 'about:blank'; + let window = applicationManager.launch(url, where); + return window.contentWindow; + }, + + openURIInFrame: function openURIInFrame(uri, opener, where, context) { + throw new Error('Not Implemented'); + }, + + isTabContentWindow: function isTabContentWindow(contentWindow) { + return contentWindow == window; + } +}; + From 207557d18cc6c9424bb5cc0f8215c5e45ec8b0d2 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Thu, 2 Feb 2012 01:46:50 +0100 Subject: [PATCH 73/90] Bug 722031 - Move mozKeyboard from shell.js to webapi.js r=cjones --- b2g/chrome/content/shell.js | 77 +------------------------------ b2g/chrome/content/webapi.js | 89 ++++++++++++++++++++++++++++++++++-- 2 files changed, 88 insertions(+), 78 deletions(-) diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index 94bed7b9ae6d..08d15692aaf8 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -47,18 +47,16 @@ const LocalFile = CC('@mozilla.org/file/local;1', Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); + XPCOMUtils.defineLazyGetter(Services, 'env', function() { return Cc['@mozilla.org/process/environment;1'] .getService(Ci.nsIEnvironment); }); + XPCOMUtils.defineLazyGetter(Services, 'ss', function() { return Cc['@mozilla.org/content/style-sheet-service;1'] .getService(Ci.nsIStyleSheetService); }); -XPCOMUtils.defineLazyGetter(Services, 'fm', function() { - return Cc['@mozilla.org/focus-manager;1'] - .getService(Ci.nsIFocusManager); -}); // In order to use http:// scheme instead of file:// scheme // (that is much more restricted) the following code kick-off @@ -288,77 +286,6 @@ var shell = { } }; -(function VirtualKeyboardManager() { - let activeElement = null; - let isKeyboardOpened = false; - - let constructor = { - handleEvent: function vkm_handleEvent(evt) { - let contentWindow = shell.home.contentWindow.wrappedJSObject; - - switch (evt.type) { - case 'ContentStart': - contentWindow.navigator.mozKeyboard = new MozKeyboard(); - break; - case 'keypress': - if (evt.keyCode != evt.DOM_VK_ESCAPE || !isKeyboardOpened) - return; - - shell.sendEvent(contentWindow, 'hideime'); - isKeyboardOpened = false; - - evt.preventDefault(); - evt.stopPropagation(); - break; - case 'mousedown': - if (evt.target != activeElement || isKeyboardOpened) - return; - - let type = activeElement.type; - shell.sendEvent(contentWindow, 'showime', { type: type }); - isKeyboardOpened = true; - break; - } - }, - observe: function vkm_observe(subject, topic, data) { - let contentWindow = shell.home.contentWindow; - - let shouldOpen = parseInt(data); - if (shouldOpen && !isKeyboardOpened) { - activeElement = Services.fm.focusedElement; - if (!activeElement) - return; - - let type = activeElement.type; - shell.sendEvent(contentWindow, 'showime', { type: type }); - } else if (!shouldOpen && isKeyboardOpened) { - shell.sendEvent(contentWindow, 'hideime'); - } - isKeyboardOpened = shouldOpen; - } - }; - - Services.obs.addObserver(constructor, 'ime-enabled-state-changed', false); - ['ContentStart', 'keypress', 'mousedown'].forEach(function vkm_events(type) { - window.addEventListener(type, constructor, true); - }); -})(); - - -function MozKeyboard() { -} - -MozKeyboard.prototype = { - sendKey: function mozKeyboardSendKey(keyCode, charCode) { - charCode = (charCode == undefined) ? keyCode : charCode; - - var utils = window.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindowUtils); - ['keydown', 'keypress', 'keyup'].forEach(function sendKeyEvents(type) { - utils.sendKeyEvent(type, keyCode, charCode, null); - }); - } -}; function nsBrowserAccess() { diff --git a/b2g/chrome/content/webapi.js b/b2g/chrome/content/webapi.js index 9ee5832ea535..30448183729a 100644 --- a/b2g/chrome/content/webapi.js +++ b/b2g/chrome/content/webapi.js @@ -4,10 +4,17 @@ dump('======================= webapi+apps.js ======================= \n'); 'use strict'; -(function() { - let { interfaces: Ci, utils: Cu } = Components; +let { classes: Cc, interfaces: Ci, utils: Cu } = Components; +Cu.import('resource://gre/modules/XPCOMUtils.jsm'); +Cu.import('resource://gre/modules/Services.jsm'); + +XPCOMUtils.defineLazyGetter(Services, 'fm', function() { + return Cc['@mozilla.org/focus-manager;1'] + .getService(Ci.nsIFocusManager); +}); + +(function() { - Cu.import('resource://gre/modules/XPCOMUtils.jsm'); function generateAPI(window) { let navigator = window.navigator; XPCOMUtils.defineLazyGetter(navigator, 'mozSettings', function() { @@ -27,6 +34,10 @@ dump('======================= webapi+apps.js ======================= \n'); return mozApps; }); + XPCOMUtils.defineLazyGetter(navigator, 'mozKeyboard', function() { + return new MozKeyboard(); + }); + TouchHandler.start(window); updateApplicationCache(window); }; @@ -97,6 +108,78 @@ function updateApplicationCache(window) { } catch (e) {} } +// MozKeyboard +(function VirtualKeyboardManager() { + let activeElement = null; + let isKeyboardOpened = false; + + function fireEvent(type, details) { + let event = content.document.createEvent('CustomEvent'); + event.initCustomEvent(type, true, true, details ? details : {}); + content.dispatchEvent(event); + } + + let constructor = { + handleEvent: function vkm_handleEvent(evt) { + switch (evt.type) { + case 'keypress': + if (evt.keyCode != evt.DOM_VK_ESCAPE || !isKeyboardOpened) + return; + + fireEvent('hideime'); + isKeyboardOpened = false; + + evt.preventDefault(); + evt.stopPropagation(); + break; + + case 'mousedown': + if (evt.target != activeElement || isKeyboardOpened) + return; + + let type = activeElement.type; + fireEvent('showime', { type: type }); + isKeyboardOpened = true; + break; + } + }, + observe: function vkm_observe(subject, topic, data) { + let shouldOpen = parseInt(data); + if (shouldOpen && !isKeyboardOpened) { + activeElement = Services.fm.focusedElement; + if (!activeElement) + return; + + let type = activeElement.type; + fireEvent('showime', { type: type }); + } else if (!shouldOpen && isKeyboardOpened) { + fireEvent('hideime'); + } + isKeyboardOpened = shouldOpen; + } + }; + + Services.obs.addObserver(constructor, 'ime-enabled-state-changed', false); + ['keypress', 'mousedown'].forEach(function vkm_events(type) { + addEventListener(type, constructor, true); + }); +})(); + + +function MozKeyboard() { +} + +MozKeyboard.prototype = { + sendKey: function mozKeyboardSendKey(keyCode, charCode) { + charCode = (charCode == undefined) ? keyCode : charCode; + + let utils = content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils); + ['keydown', 'keypress', 'keyup'].forEach(function sendKey(type) { + utils.sendKeyEvent(type, keyCode, charCode, null); + }); + } +}; // MozSettings - Bug 678695 function Settings() { From 325e43ed9c754988a08dd99ff27574991c862b41 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Thu, 2 Feb 2012 01:46:50 +0100 Subject: [PATCH 74/90] Bug 722035 - Use content instead of shell.home.contentWindow r=fabrice --- b2g/chrome/content/shell.js | 18 +++++++++--------- b2g/chrome/content/touch.js | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index 08d15692aaf8..6d07cfccbc7a 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -94,9 +94,9 @@ var shell = { // FIXME/bug 678695: this should be a system setting preferredScreenBrightness: 1.0, - get home() { - delete this.home; - return this.home = document.getElementById('homescreen'); + get contentBrowser() { + delete this.contentBrowser; + return this.contentBrowser = document.getElementById('homescreen'); }, get homeURL() { @@ -129,7 +129,7 @@ var shell = { window.controllers.appendController(this); window.addEventListener('keypress', this); window.addEventListener('MozApplicationManifest', this); - this.home.addEventListener('load', this, true); + this.contentBrowser.addEventListener('load', this, true); try { Services.io.offline = false; @@ -162,7 +162,7 @@ var shell = { dump('Error when loading ' + frameScriptUrl + ' as a frame script: ' + e + '\n'); } - let browser = this.home; + let browser = this.contentBrowser; browser.homePage = homeURL; browser.goHome(); }, @@ -193,7 +193,7 @@ var shell = { doCommand: function shell_doCommand(cmd) { switch (cmd) { case 'cmd_close': - this.home.contentWindow.postMessage('appclose', '*'); + content.postMessage('appclose', '*'); break; } }, @@ -203,7 +203,7 @@ var shell = { case 'keypress': switch (evt.keyCode) { case evt.DOM_VK_HOME: - this.sendEvent(this.home.contentWindow, 'home'); + this.sendEvent(content, 'home'); break; case evt.DOM_VK_SLEEP: this.toggleScreen(); @@ -211,7 +211,7 @@ var shell = { let details = { 'enabled': screen.mozEnabled }; - this.sendEvent(this.home.contentWindow, 'sleep', details); + this.sendEvent(content, 'sleep', details); break; case evt.DOM_VK_ESCAPE: if (evt.defaultPrevented) @@ -221,7 +221,7 @@ var shell = { } break; case 'load': - this.home.removeEventListener('load', this, true); + this.contentBrowser.removeEventListener('load', this, true); this.turnScreenOn(); let chromeWindow = window.QueryInterface(Ci.nsIDOMChromeWindow); diff --git a/b2g/chrome/content/touch.js b/b2g/chrome/content/touch.js index 5bb141777709..207cb7bba585 100644 --- a/b2g/chrome/content/touch.js +++ b/b2g/chrome/content/touch.js @@ -66,12 +66,12 @@ events: ['mousedown', 'mousemove', 'mouseup', 'click', 'unload'], start: function teh_start() { this.events.forEach((function(evt) { - shell.home.addEventListener(evt, this, true); + shell.contentBrowser.addEventListener(evt, this, true); }).bind(this)); }, stop: function teh_stop() { this.events.forEach((function(evt) { - shell.home.removeEventListener(evt, this, true); + shell.contentBrowser.removeEventListener(evt, this, true); }).bind(this)); }, handleEvent: function teh_handleEvent(evt) { From 02829eb74d4d85cc6ea67b9160a7331ffd80ee6b Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Thu, 2 Feb 2012 01:46:50 +0100 Subject: [PATCH 75/90] Bug 717844 - Add kinetic scrolling support to B2G r=cjones --- b2g/chrome/content/touch.js | 7 +- b2g/chrome/content/webapi.js | 411 +++++++++++++++++++++++++---------- 2 files changed, 299 insertions(+), 119 deletions(-) diff --git a/b2g/chrome/content/touch.js b/b2g/chrome/content/touch.js index 207cb7bba585..eb9fd4995ebf 100644 --- a/b2g/chrome/content/touch.js +++ b/b2g/chrome/content/touch.js @@ -139,12 +139,7 @@ return; case 'click': - if (!isNewTouchAction) { - debug('click: cancel'); - - evt.preventDefault(); - evt.stopPropagation(); - } else { + if (isNewTouchAction) { // Mouse events has been cancelled so dispatch a sequence // of events to where touchend has been fired if (preventMouseEvents) { diff --git a/b2g/chrome/content/webapi.js b/b2g/chrome/content/webapi.js index 30448183729a..71d7e7e4bc75 100644 --- a/b2g/chrome/content/webapi.js +++ b/b2g/chrome/content/webapi.js @@ -1,5 +1,9 @@ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- / /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +/* 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/. */ + dump('======================= webapi+apps.js ======================= \n'); 'use strict'; @@ -14,7 +18,6 @@ XPCOMUtils.defineLazyGetter(Services, 'fm', function() { }); (function() { - function generateAPI(window) { let navigator = window.navigator; XPCOMUtils.defineLazyGetter(navigator, 'mozSettings', function() { @@ -38,7 +41,6 @@ XPCOMUtils.defineLazyGetter(Services, 'fm', function() { return new MozKeyboard(); }); - TouchHandler.start(window); updateApplicationCache(window); }; @@ -466,117 +468,6 @@ var webapps = [ ]; -// Kinetic panning -var TouchHandler = { - touchState: { active: false, startX: 0, startY: 0 }, - events: ['touchstart', 'touchmove', 'touchend', - 'contextmenu'], - start: function th_start(window) { - this._window = window; - this.events.forEach((function(evt) { - window.addEventListener(evt, this); - }).bind(this)); - }, - onTouchStart: function th_touchStart(evt) { - var touchState = this.touchState; - touchState.active = true; - touchState.startTime = evt.timeStamp; - - this.startX = this.lastX = evt.pageX; - this.startY = this.lastY = evt.pageY; - }, - onTouchEnd: function th_touchEnd(evt) { - var touchState = this.touchState; - if (!touchState.active) - return; - touchState.active = false; - - this.startX = this.startY = 0; - this.lastX = this.lastY = 0; - }, - onTouchMove: function th_touchMove(evt) { - var offsetX = this.lastX - evt.pageX; - var offsetY = this.lastY - evt.pageY; - - var element = this.target; - element.scrollLeft += offsetX; - element.scrollTop += offsetY; - - this.lastX = evt.pageX; - this.lastY = evt.pageY; - }, - isPan: function isPan(x1, y1, x2, y2) { - var kRadius = 10; - return Math.abs(x1 - x2) > kRadius || Math.abs(y1 - y2) > kRadius; - }, - getPannableTarget: function isPannable(target) { - // TODO Check if the target is pannable - var targetNode = target; - try { - while (targetNode) { - var computedStyle = this._window.getComputedStyle(targetNode, null); - if (computedStyle && - computedStyle.getPropertyValue('overflow') == 'scroll') { - - var targetHeight = targetNode.getBoundingClientRect().height; - if (targetNode.scrollHeight > targetHeight) - return targetNode; - } - targetNode = targetNode.parentNode; - } - } catch (e) { - } - return null; - }, - handleEvent: function th_handleEvent(evt) { - if (evt.getPreventDefault()) - return; - - switch (evt.type) { - case 'touchstart': - var pannableTarget = this.getPannableTarget(evt.originalTarget); - if (!pannableTarget) - return; - - this.target = pannableTarget; - this.onTouchStart(evt.touches ? evt.touches[0] : evt); - break; - - case 'touchmove': - if (!this.target) - break; - - var touchEvent = evt.touches ? evt.touches[0] : evt; - if (!this.panning) { - var pan = this.isPan(evt.pageX, evt.pageY, this.startX, this.startY); - if (pan) { - this.panning = true; - this.startX = this.lastX = touchEvent.pageX; - this.startY = this.lastY = touchEvent.pageY; - this.target.setAttribute('panning', true); - this.target.setCapture(false); - } - } - this.onTouchMove(touchEvent); - break; - - case 'touchend': - if (!this.target) - return; - - if (this.panning) { - document.releaseCapture(); - this.target.removeAttribute('panning'); - this.panning = null; - } - this.onTouchEnd(evt.touches ? evt.touches[0] : evt); - this.target = null; - break; - } - } -}; - - // Bug 674720 - webContacts var contacts = [ { @@ -1604,3 +1495,297 @@ var ContactsManager = { } }; +let { classes: Cc, interfaces: Ci, utils: Cu } = Components; + +Cu.import('resource://gre/modules/Geometry.jsm'); +Cu.import('resource://gre/modules/Services.jsm'); + +const ContentPanning = { + init: function cp_init() { + ['mousedown', 'mouseup', 'mousemove'].forEach(function(type) { + addEventListener(type, ContentPanning, true); + }); + }, + + handleEvent: function cp_handleEvent(evt) { + switch (evt.type) { + case 'mousedown': + this.onTouchStart(evt); + break; + case 'mousemove': + this.onTouchMove(evt); + break; + case 'mouseup': + this.onTouchEnd(evt); + break; + case 'click': + evt.stopPropagation(); + evt.preventDefault(); + evt.target.removeEventListener('click', this, true); + break; + } + }, + + position: { + origin: new Point(0, 0), + current: new Point(0 , 0) + }, + + onTouchStart: function cp_onTouchStart(evt) { + this.dragging = true; + KineticPanning.stop(); + + this.scrollCallback = this.getPannable(evt.originalTarget); + this.position.origin.set(evt.screenX, evt.screenY); + this.position.current.set(evt.screenX, evt.screenY); + KineticPanning.record(new Point(0, 0)); + }, + + onTouchEnd: function cp_onTouchEnd(evt) { + if (!this.dragging) + return; + this.dragging = false; + + if (this.isPan()) { + if (evt.detail) // The event will generate a click + evt.target.addEventListener('click', this, true); + + KineticPanning.start(this); + } + }, + + onTouchMove: function cp_onTouchMove(evt) { + if (!this.dragging || !this.scrollCallback) + return; + + let current = this.position.current; + let delta = new Point(evt.screenX - current.x, evt.screenY - current.y); + current.set(evt.screenX, evt.screenY); + + if (this.isPan()) { + KineticPanning.record(delta); + this.scrollCallback(delta.scale(-1)); + } + }, + + + onKineticBegin: function cp_onKineticBegin(evt) { + }, + + onKineticPan: function cp_onKineticPan(delta) { + return !this.scrollCallback(delta); + }, + + onKineticEnd: function cp_onKineticEnd() { + if (!this.dragging) + this.scrollCallback = null; + }, + + isPan: function cp_isPan() { + let dpi = content.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindowUtils) + .displayDPI; + + let threshold = Services.prefs.getIntPref('ui.dragThresholdX') / 240 * dpi; + + let deltaX = this.position.origin.x - this.position.current.x; + let deltaY = this.position.origin.y - this.position.current.y; + return (Math.abs(deltaX) > threshold || Math.abs(deltaY) > threshold); + }, + + getPannable: function cp_getPannable(node) { + if (!(node instanceof Ci.nsIDOMHTMLElement) || node.tagName == 'HTML') + return null; + + let content = node.ownerDocument.defaultView; + + while (!(node instanceof Ci.nsIDOMHTMLBodyElement)) { + let style = content.getComputedStyle(node, null); + + let overflow = [style.getPropertyValue('overflow'), + style.getPropertyValue('overflow-x'), + style.getPropertyValue('overflow-y')]; + + let rect = node.getBoundingClientRect(); + let isAuto = (overflow.indexOf('auto') != -1 && + (rect.height < node.scrollHeight || + rect.width < node.scrollWidth)); + + let isScroll = (overflow.indexOf('scroll') != -1); + if (isScroll || isAuto) + return this._generateCallback(node); + + node = node.parentNode; + } + + return this._generateCallback(content); + }, + + _generateCallback: function cp_generateCallback(content) { + function scroll(delta) { + if (content instanceof Ci.nsIDOMHTMLElement) { + let oldX = content.scrollLeft, oldY = content.scrollTop; + content.scrollLeft += delta.x; + content.scrollTop += delta.y; + let newX = content.scrollLeft, newY = content.scrollTop; + return (newX != oldX) || (newY != oldY); + } else { + let oldX = content.scrollX, oldY = content.scrollY; + content.scrollBy(delta.x, delta.y); + let newX = content.scrollX, newY = content.scrollY; + return (newX != oldX) || (newY != oldY); + } + } + return scroll; + } +}; + +ContentPanning.init(); + + +// Min/max velocity of kinetic panning. This is in pixels/millisecond. +const kMinVelocity = 0.4; +const kMaxVelocity = 6; + +// Constants that affect the "friction" of the scroll pane. +const kExponentialC = 1400; +const kPolynomialC = 100 / 1000000; + +// How often do we change the position of the scroll pane? +// Too often and panning may jerk near the end. +// Too little and panning will be choppy. In milliseconds. +const kUpdateInterval = 16; + +const KineticPanning = { + _position: new Point(0, 0), + _velocity: new Point(0, 0), + _acceleration: new Point(0, 0), + + _target: null, + start: function kp_start(target) { + this.target = target; + + // Calculate the initial velocity of the movement based on user input + let momentums = this.momentums; + + let distance = new Point(0, 0); + momentums.forEach(function(momentum) { + distance.add(momentum.dx, momentum.dy); + }); + + let elapsed = momentums[momentums.length - 1].time - momentums[0].time; + + function clampFromZero(x, min, max) { + if (x >= 0) + return Math.max(min, Math.min(max, x)); + return Math.min(-min, Math.max(-max, x)); + } + + let velocityX = clampFromZero(distance.x / elapsed, 0, kMaxVelocity); + let velocityY = clampFromZero(distance.y / elapsed, 0, kMaxVelocity); + + let velocity = this._velocity; + velocity.set(Math.abs(velocityX) < kMinVelocity ? 0 : velocityX, + Math.abs(velocityY) < kMinVelocity ? 0 : velocityY); + + // Set acceleration vector to opposite signs of velocity + function sign(x) { + return x ? (x > 0 ? 1 : -1) : 0; + } + + this._acceleration.set(velocity.clone().map(sign).scale(-kPolynomialC)); + + // Reset the position + this._position.set(0, 0); + + this._startAnimation(); + + this.target.onKineticBegin(); + }, + + stop: function kp_stop() { + if (!this.target) + return; + + this.momentums.splice(0); + + this.target.onKineticEnd(); + this.target = null; + }, + + momentums: [], + record: function kp_record(delta) { + // If the panning direction has changed, stop the current activity. + if (this.target && ((delta.x * this._velocity.x < 0) || + (delta.y * this._velocity.y < 0))) + this.stop(); + + this.momentums.push({ 'time': Date.now(), 'dx' : delta.x, 'dy' : delta.y }); + }, + + _startAnimation: function kp_startAnimation() { + let c = kExponentialC; + function getNextPosition(position, v, a, t) { + // Important traits for this function: + // p(t=0) is 0 + // p'(t=0) is v0 + // + // We use exponential to get a smoother stop, but by itself exponential + // is too smooth at the end. Adding a polynomial with the appropriate + // weight helps to balance + position.set(v.x * Math.exp(-t / c) * -c + a.x * t * t + v.x * c, + v.y * Math.exp(-t / c) * -c + a.y * t * t + v.y * c); + } + + let startTime = content.mozAnimationStartTime; + let elapsedTime = 0, targetedTime = 0, averageTime = 0; + + let velocity = this._velocity; + let acceleration = this._acceleration; + + let position = this._position; + let nextPosition = new Point(0, 0); + let delta = new Point(0, 0); + + let callback = (function(timestamp) { + if (!this.target) + return; + + // To make animation end fast enough but to keep smoothness, average the + // ideal time frame (smooth animation) with the actual time lapse + // (end fast enough). + // Animation will never take longer than 2 times the ideal length of time. + elapsedTime = timestamp - startTime; + targetedTime += kUpdateInterval; + averageTime = (targetedTime + elapsedTime) / 2; + + // Calculate new position. + getNextPosition(nextPosition, velocity, acceleration, averageTime); + delta.set(Math.round(nextPosition.x - position.x), + Math.round(nextPosition.y - position.y)); + + // Test to see if movement is finished for each component. + if (delta.x * acceleration.x > 0) + delta.x = position.x = velocity.x = acceleration.x = 0; + + if (delta.y * acceleration.y > 0) + delta.y = position.y = velocity.y = acceleration.y = 0; + + if (velocity.equals(0, 0) || delta.equals(0, 0)) { + this.stop(); + return; + } + + position.add(delta); + if (this.target.onKineticPan(delta.scale(-1))) { + this.stop(); + return; + } + + content.mozRequestAnimationFrame(callback); + }).bind(this); + + content.mozRequestAnimationFrame(callback); + } +}; + From 75ebf6e8cce3326ba0e1a828c377acf719f405d1 Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Thu, 19 Jan 2012 16:52:44 -0500 Subject: [PATCH 76/90] bug 719560 - Can't publish split native and xul builds under the same product on android market r=mbrubeck --- configure.in | 4 ++++ embedding/android/AndroidManifest.xml.in | 8 ++++++++ embedding/android/Makefile.in | 8 +++++++- mobile/xul/branding/aurora/configure.sh | 1 + mobile/xul/branding/beta/configure.sh | 1 + mobile/xul/branding/nightly/configure.sh | 1 + mobile/xul/branding/official/configure.sh | 1 + mobile/xul/branding/unofficial/configure.sh | 1 + 8 files changed, 24 insertions(+), 1 deletion(-) diff --git a/configure.in b/configure.in index 29864c5845aa..8d6e1c31ca16 100644 --- a/configure.in +++ b/configure.in @@ -460,6 +460,9 @@ case "$target" in if test -z "$ANDROID_PACKAGE_NAME" ; then ANDROID_PACKAGE_NAME='org.mozilla.$(MOZ_APP_NAME)' fi + if test -z "$MOZ_MOBILE_COMPAT" ; then + MOZ_MOBILE_COMPAT='All' + fi AC_DEFINE(ANDROID) AC_DEFINE_UNQUOTED(ANDROID_VERSION, $android_version) @@ -481,6 +484,7 @@ AC_SUBST(ANDROID_PLATFORM) AC_SUBST(ANDROID_SDK) AC_SUBST(ANDROID_PLATFORM_TOOLS) AC_SUBST(ANDROID_PACKAGE_NAME) +AC_SUBST(MOZ_MOBILE_COMPAT) AC_SUBST(OBJCOPY) dnl ======================================================== diff --git a/embedding/android/AndroidManifest.xml.in b/embedding/android/AndroidManifest.xml.in index 32e003631ccc..ec386218c192 100644 --- a/embedding/android/AndroidManifest.xml.in +++ b/embedding/android/AndroidManifest.xml.in @@ -9,9 +9,17 @@ android:sharedUserId="@MOZ_ANDROID_SHARED_ID@" #endif > + +#ifdef MOZ_TABLETS_ONLY + +#endif + diff --git a/embedding/android/Makefile.in b/embedding/android/Makefile.in index 1889e16f934c..27dd67bbfd07 100644 --- a/embedding/android/Makefile.in +++ b/embedding/android/Makefile.in @@ -79,7 +79,8 @@ MIN_CPU_VERSION=5 endif ifeq (,$(ANDROID_VERSION_CODE)) -ANDROID_VERSION_CODE=$(shell $(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10) +# increment the version code by 1 so xul fennec will win any compatability ties +ANDROID_VERSION_CODE=$(shell echo `$(PYTHON) $(topsrcdir)/toolkit/xre/make-platformini.py --print-buildid | cut -c1-10` + 1 | bc) endif DEFINES += \ @@ -94,6 +95,11 @@ DEFINES += \ -DMOZILLA_OFFICIAL=$(MOZILLA_OFFICIAL) \ $(NULL) +MOZ_MOBILE_COMPAT = @MOZ_MOBILE_COMPAT@ +ifeq (Tablets,$(MOZ_MOBILE_COMPAT)) +DEFINES += -DMOZ_TABLETS_ONLY=1 +endif + GARBAGE += \ AndroidManifest.xml \ classes.dex \ diff --git a/mobile/xul/branding/aurora/configure.sh b/mobile/xul/branding/aurora/configure.sh index d39212ccf388..3eb7fa3bcbd8 100644 --- a/mobile/xul/branding/aurora/configure.sh +++ b/mobile/xul/branding/aurora/configure.sh @@ -1,3 +1,4 @@ MOZ_APP_DISPLAYNAME=Aurora ANDROID_PACKAGE_NAME=org.mozilla.fennec_aurora MOZ_UPDATER=1 +MOZ_MOBILE_COMPAT=Tablets diff --git a/mobile/xul/branding/beta/configure.sh b/mobile/xul/branding/beta/configure.sh index 9d89bf17bb98..0971c3875e83 100644 --- a/mobile/xul/branding/beta/configure.sh +++ b/mobile/xul/branding/beta/configure.sh @@ -1,3 +1,4 @@ MOZ_APP_DISPLAYNAME="Firefox Beta" ANDROID_PACKAGE_NAME=org.mozilla.firefox_beta MOZ_UPDATER= +MOZ_MOBILE_COMPAT=Tablets diff --git a/mobile/xul/branding/nightly/configure.sh b/mobile/xul/branding/nightly/configure.sh index 0917293c83de..95852821752a 100644 --- a/mobile/xul/branding/nightly/configure.sh +++ b/mobile/xul/branding/nightly/configure.sh @@ -1,2 +1,3 @@ MOZ_APP_DISPLAYNAME=Nightly MOZ_UPDATER=1 +MOZ_MOBILE_COMPAT=All diff --git a/mobile/xul/branding/official/configure.sh b/mobile/xul/branding/official/configure.sh index 5078894daebc..e936558db59d 100644 --- a/mobile/xul/branding/official/configure.sh +++ b/mobile/xul/branding/official/configure.sh @@ -1,3 +1,4 @@ MOZ_APP_DISPLAYNAME=Firefox ANDROID_PACKAGE_NAME=org.mozilla.firefox MOZ_UPDATER= +MOZ_MOBILE_COMPAT=Tablets diff --git a/mobile/xul/branding/unofficial/configure.sh b/mobile/xul/branding/unofficial/configure.sh index 0145020011ca..96541eafe973 100644 --- a/mobile/xul/branding/unofficial/configure.sh +++ b/mobile/xul/branding/unofficial/configure.sh @@ -1,3 +1,4 @@ ANDROID_PACKAGE_NAME=org.mozilla.fennec_`echo $USER` MOZ_APP_DISPLAYNAME=Fennec MOZ_UPDATER= +MOZ_MOBILE_COMPAT=All From 484b586d4ed482ac681c8b22002b34fdf6cfba90 Mon Sep 17 00:00:00 2001 From: Brian Nicholson Date: Wed, 1 Feb 2012 17:51:41 -0800 Subject: [PATCH 77/90] Bug 723349 - Fix params in addTab on session restore failure. r=mfinkle --- mobile/android/chrome/content/browser.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mobile/android/chrome/content/browser.js b/mobile/android/chrome/content/browser.js index 645f18c5f58b..7e233d3602d9 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -311,8 +311,10 @@ var BrowserApp = { observe: function(aSubject, aTopic, aData) { Services.obs.removeObserver(restoreCleanup, "sessionstore-windows-restored"); if (aData == "fail") { - let params = { selected: restoreToFront }; - BrowserApp.addTab("about:home", { showProgress: false }); + BrowserApp.addTab("about:home", { + showProgress: false, + selected: restoreToFront + }); } sendMessageToJava({ From 4ed39163d3d63060b24044f201926a1b3a429de8 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Wed, 1 Feb 2012 17:55:05 -0800 Subject: [PATCH 78/90] (no bug, comment-only) Fix broken link to feDiffuseLighting chunk of SVG spec. DONTBUILD --- content/svg/content/src/nsSVGFilters.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/content/svg/content/src/nsSVGFilters.cpp b/content/svg/content/src/nsSVGFilters.cpp index 1b7411fdcd30..bff464a8aa3b 100644 --- a/content/svg/content/src/nsSVGFilters.cpp +++ b/content/svg/content/src/nsSVGFilters.cpp @@ -4927,7 +4927,7 @@ GenerateNormal(float *N, const PRUint8 *data, PRInt32 stride, PRInt32 x, PRInt32 y, float surfaceScale) { // See this for source of constants: - // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLighting + // http://www.w3.org/TR/SVG11/filters.html#feDiffuseLightingElement static const PRInt8 Kx[3][3][3][3] = { { { { 0, 0, 0}, { 0, -2, 2}, { 0, -1, 1} }, { { 0, 0, 0}, {-2, 0, 2}, {-1, 0, 1} }, From e9dd77e37e3f258c17387776e03a3db0e2e387f2 Mon Sep 17 00:00:00 2001 From: Andrew McCreight Date: Wed, 1 Feb 2012 17:55:09 -0800 Subject: [PATCH 79/90] Bug 721933 - add way to do compartment GC with a reason. r=billm --- js/src/jsfriendapi.cpp | 9 +++++++++ js/src/jsfriendapi.h | 3 +++ 2 files changed, 12 insertions(+) diff --git a/js/src/jsfriendapi.cpp b/js/src/jsfriendapi.cpp index 7133db5d75ba..04cc74093018 100644 --- a/js/src/jsfriendapi.cpp +++ b/js/src/jsfriendapi.cpp @@ -135,6 +135,15 @@ js::GCForReason(JSContext *cx, gcreason::Reason reason) js_GC(cx, NULL, GC_NORMAL, reason); } +JS_FRIEND_API(void) +js::CompartmentGCForReason(JSContext *cx, JSCompartment *comp, gcreason::Reason reason) +{ + /* We cannot GC the atoms compartment alone; use a full GC instead. */ + JS_ASSERT(comp != cx->runtime->atomsCompartment); + + js_GC(cx, comp, GC_NORMAL, reason); +} + JS_FRIEND_API(void) js::ShrinkingGC(JSContext *cx, gcreason::Reason reason) { diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 33697bb69280..724b37d59e1f 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -675,6 +675,9 @@ enum Reason { extern JS_FRIEND_API(void) GCForReason(JSContext *cx, gcreason::Reason reason); +extern JS_FRIEND_API(void) +CompartmentGCForReason(JSContext *cx, JSCompartment *comp, gcreason::Reason reason); + extern JS_FRIEND_API(void) ShrinkingGC(JSContext *cx, gcreason::Reason reason); From 8f45182bfa7ab917a6cad1afc653cccd7ec68986 Mon Sep 17 00:00:00 2001 From: Murali Date: Thu, 2 Feb 2012 10:13:09 +0900 Subject: [PATCH 80/90] Bug 720148 - ARIA button should expose press action, r=surkov --- accessible/src/base/nsARIAMap.cpp | 2 +- accessible/src/base/nsARIAMap.h | 1 + accessible/src/base/nsAccessible.cpp | 4 ++++ accessible/tests/mochitest/actions/test_aria.html | 2 +- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/accessible/src/base/nsARIAMap.cpp b/accessible/src/base/nsARIAMap.cpp index da4484422f48..f308c40298eb 100644 --- a/accessible/src/base/nsARIAMap.cpp +++ b/accessible/src/base/nsARIAMap.cpp @@ -105,7 +105,7 @@ nsRoleMapEntry nsARIAMap::gWAIRoleMap[] = roles::PUSHBUTTON, kUseMapRole, eNoValue, - eClickAction, + ePressAction, eNoLiveAttr, kNoReqStates, eARIAPressed diff --git a/accessible/src/base/nsARIAMap.h b/accessible/src/base/nsARIAMap.h index 87d2165f5921..9efa8cd1ecd8 100644 --- a/accessible/src/base/nsARIAMap.h +++ b/accessible/src/base/nsARIAMap.h @@ -78,6 +78,7 @@ enum EActionRule eNoAction, eActivateAction, eClickAction, + ePressAction, eCheckUncheckAction, eExpandAction, eJumpAction, diff --git a/accessible/src/base/nsAccessible.cpp b/accessible/src/base/nsAccessible.cpp index a85043c2c55b..02a7cc06e4c2 100644 --- a/accessible/src/base/nsAccessible.cpp +++ b/accessible/src/base/nsAccessible.cpp @@ -1828,6 +1828,10 @@ nsAccessible::GetActionName(PRUint8 aIndex, nsAString& aName) aName.AssignLiteral("click"); return NS_OK; + case ePressAction: + aName.AssignLiteral("press"); + return NS_OK; + case eCheckUncheckAction: if (states & states::CHECKED) aName.AssignLiteral("uncheck"); diff --git a/accessible/tests/mochitest/actions/test_aria.html b/accessible/tests/mochitest/actions/test_aria.html index 43679b6d34e0..5a5f4259e067 100644 --- a/accessible/tests/mochitest/actions/test_aria.html +++ b/accessible/tests/mochitest/actions/test_aria.html @@ -27,7 +27,7 @@ }, { ID: "button", - actionName: "click", + actionName: "press", events: CLICK_EVENTS }, { From 8d881ee1d42387cf15019de4edaf9dd507746cac Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Fri, 13 Jan 2012 10:20:36 +1300 Subject: [PATCH 81/90] Bug 677138 - Integrate libcubeb into a buffered nsAudioStream API. r=cpearce --- content/media/nsAudioStream.cpp | 490 ++++++++++++++++++++++++++++++-- 1 file changed, 460 insertions(+), 30 deletions(-) diff --git a/content/media/nsAudioStream.cpp b/content/media/nsAudioStream.cpp index ad3d518b8ce8..85b095380787 100644 --- a/content/media/nsAudioStream.cpp +++ b/content/media/nsAudioStream.cpp @@ -60,6 +60,11 @@ extern "C" { #include "nsThreadUtils.h" #include "mozilla/Preferences.h" +#if defined(MOZ_CUBEB) +#include "nsAutoRef.h" +#include "cubeb/cubeb.h" +#endif + using namespace mozilla; #if defined(XP_MACOSX) @@ -78,6 +83,10 @@ using mozilla::TimeStamp; PRLogModuleInfo* gAudioStreamLog = nsnull; #endif +#if defined(MOZ_CUBEB) +static cubeb* gCubebContext; +#endif + static const PRUint32 FAKE_BUFFER_SIZE = 176400; // Number of milliseconds per second. @@ -170,9 +179,9 @@ class AudioInitEvent : public nsRunnable { ContentChild * cpc = ContentChild::GetSingleton(); NS_ASSERTION(cpc, "Content Protocol is NULL!"); - mOwner->mAudioChild = static_cast (cpc->SendPAudioConstructor(mOwner->mChannels, - mOwner->mRate, - mOwner->mFormat)); + mOwner->mAudioChild = static_cast(cpc->SendPAudioConstructor(mOwner->mChannels, + mOwner->mRate, + mOwner->mFormat)); return NS_OK; } @@ -316,42 +325,78 @@ class AudioShutdownEvent : public nsRunnable }; #endif -static mozilla::Mutex* gVolumeScaleLock = nsnull; +#define PREF_VOLUME_SCALE "media.volume_scale" +#define PREF_USE_CUBEB "media.use_cubeb" +static mozilla::Mutex* gAudioPrefsLock = nsnull; static double gVolumeScale = 1.0; +static bool gUseCubeb = false; -static int VolumeScaleChanged(const char* aPref, void *aClosure) { - nsAdoptingString value = Preferences::GetString("media.volume_scale"); - mozilla::MutexAutoLock lock(*gVolumeScaleLock); - if (value.IsEmpty()) { - gVolumeScale = 1.0; - } else { - NS_ConvertUTF16toUTF8 utf8(value); - gVolumeScale = NS_MAX(0, PR_strtod(utf8.get(), nsnull)); +static int PrefChanged(const char* aPref, void* aClosure) +{ + if (strcmp(aPref, PREF_VOLUME_SCALE) == 0) { + nsAdoptingString value = Preferences::GetString(aPref); + mozilla::MutexAutoLock lock(*gAudioPrefsLock); + if (value.IsEmpty()) { + gVolumeScale = 1.0; + } else { + NS_ConvertUTF16toUTF8 utf8(value); + gVolumeScale = NS_MAX(0, PR_strtod(utf8.get(), nsnull)); + } + } else if (strcmp(aPref, PREF_USE_CUBEB) == 0) { + bool value = Preferences::GetBool(aPref, true); + mozilla::MutexAutoLock lock(*gAudioPrefsLock); + gUseCubeb = value; } return 0; } -static double GetVolumeScale() { - mozilla::MutexAutoLock lock(*gVolumeScaleLock); +static double GetVolumeScale() +{ + mozilla::MutexAutoLock lock(*gAudioPrefsLock); return gVolumeScale; } +#if defined(MOZ_CUBEB) +static bool GetUseCubeb() +{ + mozilla::MutexAutoLock lock(*gAudioPrefsLock); + return gUseCubeb; +} +#endif + void nsAudioStream::InitLibrary() { #ifdef PR_LOGGING gAudioStreamLog = PR_NewLogModule("nsAudioStream"); #endif - gVolumeScaleLock = new mozilla::Mutex("nsAudioStream::gVolumeScaleLock"); - VolumeScaleChanged(nsnull, nsnull); - Preferences::RegisterCallback(VolumeScaleChanged, "media.volume_scale"); + gAudioPrefsLock = new mozilla::Mutex("nsAudioStream::gAudioPrefsLock"); + PrefChanged(PREF_VOLUME_SCALE, nsnull); + Preferences::RegisterCallback(PrefChanged, PREF_VOLUME_SCALE); +#if defined(MOZ_CUBEB) + PrefChanged(PREF_USE_CUBEB, nsnull); + Preferences::RegisterCallback(PrefChanged, PREF_USE_CUBEB); + if (cubeb_init(&gCubebContext, "nsAudioStream") != 0) { + NS_WARNING("cubeb_init failed"); + } +#endif } void nsAudioStream::ShutdownLibrary() { - Preferences::UnregisterCallback(VolumeScaleChanged, "media.volume_scale"); - delete gVolumeScaleLock; - gVolumeScaleLock = nsnull; + Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE); +#if defined(MOZ_CUBEB) + Preferences::UnregisterCallback(PrefChanged, PREF_USE_CUBEB); +#endif + delete gAudioPrefsLock; + gAudioPrefsLock = nsnull; + +#if defined(MOZ_CUBEB) + if (gCubebContext) { + cubeb_destroy(gCubebContext); + gCubebContext = nsnull; + } +#endif } nsIThread * @@ -365,16 +410,6 @@ nsAudioStream::GetThread() return mAudioPlaybackThread; } -nsAudioStream* nsAudioStream::AllocateStream() -{ -#if defined(REMOTE_AUDIO) - if (XRE_GetProcessType() == GeckoProcessType_Content) { - return new nsRemotedAudioStream(); - } -#endif - return new nsNativeAudioStream(); -} - class AsyncShutdownPlaybackThread : public nsRunnable { public: @@ -770,3 +805,398 @@ nsRemotedAudioStream::IsPaused() } #endif +#if defined(MOZ_CUBEB) +template <> +class nsAutoRefTraits : public nsPointerRefTraits +{ +public: + static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); } +}; + +class nsBufferedAudioStream : public nsAudioStream +{ + public: + NS_DECL_ISUPPORTS + + nsBufferedAudioStream(); + ~nsBufferedAudioStream(); + + nsresult Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat); + void Shutdown(); + nsresult Write(const void* aBuf, PRUint32 aFrames); + PRUint32 Available(); + void SetVolume(double aVolume); + void Drain(); + void Pause(); + void Resume(); + PRInt64 GetPosition(); + PRInt64 GetPositionInFrames(); + bool IsPaused(); + PRInt32 GetMinWriteSize(); + +private: + static long DataCallback_S(cubeb_stream*, void* aThis, void* aBuffer, long aFrames) + { + return static_cast(aThis)->DataCallback(aBuffer, aFrames); + } + + static int StateCallback_S(cubeb_stream*, void* aThis, cubeb_state aState) + { + return static_cast(aThis)->StateCallback(aState); + } + + long DataCallback(void* aBuffer, long aFrames); + int StateCallback(cubeb_state aState); + + // Shared implementation of underflow adjusted position calculation. + // Caller must own the monitor. + PRInt64 GetPositionInFramesUnlocked(); + + // The monitor is held to protect all access to member variables. Write() + // waits while mBuffer is full; DataCallback() notifies as it consumes + // data from mBuffer. Drain() waits while mState is DRAINING; + // StateCallback() notifies when mState is DRAINED. + Monitor mMonitor; + + // Sum of silent frames written when DataCallback requests more frames + // than are available in mBuffer. + PRUint64 mLostFrames; + + // Temporary audio buffer. Filled by Write() and consumed by + // DataCallback(). Once mBufferLimit is reached, Write() blocks until + // sufficient space becomes available in mBuffer. The buffer and buffer + // limit deal in bytes, not frames. + nsTArray mBuffer; + PRUint32 mBufferLimit; + + // Software volume level. Applied during the servicing of DataCallback(). + double mVolume; + + // Owning reference to a cubeb_stream. cubeb_stream_destroy is called by + // nsAutoRef's destructor. + nsAutoRef mCubebStream; + + PRInt32 mRate; + PRInt32 mChannels; + SampleFormat mFormat; + PRUint32 mBytesPerFrame; + + enum StreamState { + INITIALIZED, // Initialized, playback has not begun. + STARTED, // Started by a call to Write() (iff INITIALIZED) or Resume(). + STOPPED, // Stopped by a call to Pause(). + DRAINING, // Drain requested. DataCallback will indicate end of stream + // once the remaining contents of mBuffer are requested by + // cubeb, after which StateCallback will indicate drain + // completion. + DRAINED // StateCallback has indicated that the drain is complete. + }; + + StreamState mState; + + // Arbitrary default stream latency. The higher this value, the longer stream + // volume changes will take to become audible. + static const unsigned int DEFAULT_LATENCY_MS = 100; +}; +#endif + +nsAudioStream* nsAudioStream::AllocateStream() +{ +#if defined(REMOTE_AUDIO) + if (XRE_GetProcessType() == GeckoProcessType_Content) { + return new nsRemotedAudioStream(); + } +#endif +#if defined(MOZ_CUBEB) + if (GetUseCubeb()) { + return new nsBufferedAudioStream(); + } +#endif + return new nsNativeAudioStream(); +} + +#if defined(MOZ_CUBEB) +nsBufferedAudioStream::nsBufferedAudioStream() + : mMonitor("nsBufferedAudioStream"), mLostFrames(0), mVolume(1.0), mRate(0), mChannels(0), + mBytesPerFrame(0), mState(INITIALIZED) +{ +} + +nsBufferedAudioStream::~nsBufferedAudioStream() +{ + Shutdown(); +} + +NS_IMPL_THREADSAFE_ISUPPORTS0(nsBufferedAudioStream) + +nsresult +nsBufferedAudioStream::Init(PRInt32 aNumChannels, PRInt32 aRate, SampleFormat aFormat) +{ + if (!gCubebContext || aNumChannels < 0 || aRate < 0) { + return NS_ERROR_FAILURE; + } + + mRate = aRate; + mChannels = aNumChannels; + mFormat = aFormat; + + cubeb_stream_params params; + params.rate = aRate; + params.channels = aNumChannels; + switch (aFormat) { + case FORMAT_S16_LE: + params.format = CUBEB_SAMPLE_S16LE; + mBytesPerFrame = sizeof(short) * aNumChannels; + break; + case FORMAT_FLOAT32: + params.format = CUBEB_SAMPLE_FLOAT32LE; + mBytesPerFrame = sizeof(float) * aNumChannels; + break; + default: + return NS_ERROR_FAILURE; + } + + { + cubeb_stream* stream; + if (cubeb_stream_init(gCubebContext, &stream, "nsBufferedAudioStream", params, + DEFAULT_LATENCY_MS, DataCallback_S, StateCallback_S, this) == CUBEB_OK) { + mCubebStream.own(stream); + } + } + + if (!mCubebStream) { + return NS_ERROR_FAILURE; + } + + // Limit mBuffer to one second of audio. This value is arbitrary, and was + // selected based on the observed behaviour of the existing nsAudioStream + // implementations. + mBufferLimit = aRate * mBytesPerFrame; + NS_ABORT_IF_FALSE(mBufferLimit % mBytesPerFrame == 0, "Must buffer complete frames"); + + // Pre-allocate the buffer. nsTArray::RemoveElementsAt shrinks the buffer + // only if its length reaches zero, so allocator thrashing should be + // minimal. + mBuffer.SetCapacity(mBufferLimit); + + return NS_OK; +} + +void +nsBufferedAudioStream::Shutdown() +{ + if (mCubebStream) { + cubeb_stream_stop(mCubebStream); + mCubebStream.reset(); + } +} + +nsresult +nsBufferedAudioStream::Write(const void* aBuf, PRUint32 aFrames) +{ + MonitorAutoLock mon(mMonitor); + if (!mCubebStream) { + return NS_ERROR_FAILURE; + } + NS_ASSERTION(mState == INITIALIZED || mState == STARTED, "Stream write in unexpected state."); + + const PRUint8* src = static_cast(aBuf); + PRUint32 bytesToCopy = aFrames * mBytesPerFrame; + + while (bytesToCopy > 0) { + NS_ABORT_IF_FALSE(mBuffer.Length() <= mBufferLimit, "Buffer invariant violated."); + + PRUint32 available = NS_MIN(bytesToCopy, mBufferLimit - mBuffer.Length()); + NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames."); + + mBuffer.AppendElements(src, available); + src += available; + bytesToCopy -= available; + + if (mState != STARTED && cubeb_stream_start(mCubebStream) == CUBEB_OK) { + mState = STARTED; + } + + if (bytesToCopy > 0) { + mon.Wait(); + } + } + + return NS_OK; +} + +PRUint32 +nsBufferedAudioStream::Available() +{ + MonitorAutoLock mon(mMonitor); + NS_ABORT_IF_FALSE(mBuffer.Length() <= mBufferLimit, "Buffer invariant violated."); + NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Buffer invariant violated."); + return (mBufferLimit - mBuffer.Length()) / mBytesPerFrame; +} + +PRInt32 nsBufferedAudioStream::GetMinWriteSize() +{ + return 1; +} + +void +nsBufferedAudioStream::SetVolume(double aVolume) +{ + MonitorAutoLock mon(mMonitor); + NS_ABORT_IF_FALSE(aVolume >= 0.0 && aVolume <= 1.0, "Invalid volume"); + mVolume = aVolume; +} + +void +nsBufferedAudioStream::Drain() +{ + MonitorAutoLock mon(mMonitor); + if (mState != STARTED) { + return; + } + mState = DRAINING; + while (mState != DRAINED) { + mon.Wait(); + } +} + +void +nsBufferedAudioStream::Pause() +{ + MonitorAutoLock mon(mMonitor); + if (!mCubebStream || mState != STARTED) { + return; + } + + if (cubeb_stream_stop(mCubebStream) == CUBEB_OK) { + mState = STOPPED; + } +} + +void +nsBufferedAudioStream::Resume() +{ + MonitorAutoLock mon(mMonitor); + if (!mCubebStream || mState != STOPPED) { + return; + } + + if (cubeb_stream_start(mCubebStream) == CUBEB_OK) { + mState = STARTED; + } +} + +PRInt64 nsBufferedAudioStream::GetPosition() +{ + MonitorAutoLock mon(mMonitor); + PRInt64 frames = GetPositionInFramesUnlocked(); + if (frames >= 0) { + return USECS_PER_S * frames / mRate; + } + return -1; +} + +PRInt64 +nsBufferedAudioStream::GetPositionInFrames() +{ + MonitorAutoLock mon(mMonitor); + return GetPositionInFramesUnlocked(); +} + +PRInt64 +nsBufferedAudioStream::GetPositionInFramesUnlocked() +{ + mMonitor.AssertCurrentThreadOwns(); + + if (!mCubebStream) { + return -1; + } + + uint64_t position = 0; + if (cubeb_stream_get_position(mCubebStream, &position) != CUBEB_OK) { + return -1; + } + + // Adjust the reported position by the number of silent frames written + // during stream underruns. + PRInt64 adjustedPosition = 0; + if (position >= mLostFrames) { + adjustedPosition = position - mLostFrames; + } + return adjustedPosition; +} + +bool +nsBufferedAudioStream::IsPaused() +{ + MonitorAutoLock mon(mMonitor); + return mState == STOPPED; +} + +template +void +SampleCopy(void* aDst, const PRUint8* aSrc, PRUint32 aSamples, double aVolume) +{ + const T* src = reinterpret_cast(aSrc); + double scaled_volume = GetVolumeScale() * aVolume; + T* dst = static_cast(aDst); + for (PRUint32 i = 0; i < aSamples; ++i) { + dst[i] = T(src[i] * scaled_volume); + } +} + +long +nsBufferedAudioStream::DataCallback(void* aBuffer, long aFrames) +{ + MonitorAutoLock mon(mMonitor); + PRUint32 bytesWanted = aFrames * mBytesPerFrame; + + // Adjust bytesWanted to fit what is available in mBuffer. + PRUint32 available = NS_MIN(bytesWanted, mBuffer.Length()); + NS_ABORT_IF_FALSE(available % mBytesPerFrame == 0, "Must copy complete frames"); + + // Copy each sample from mBuffer to aBuffer, adjusting the volume during the copy. + PRUint32 samplesToCopy = available / mBytesPerFrame * mChannels; + switch (mFormat) { + case FORMAT_S16_LE: + SampleCopy(aBuffer, mBuffer.Elements(), samplesToCopy, mVolume); + break; + case FORMAT_FLOAT32: + SampleCopy(aBuffer, mBuffer.Elements(), samplesToCopy, mVolume); + break; + default: + return -1; + } + + // Remove copied data from the temporary audio buffer. + mBuffer.RemoveElementsAt(0, available); + NS_ABORT_IF_FALSE(mBuffer.Length() % mBytesPerFrame == 0, "Must copy complete frames"); + + // Notify any blocked Write() call that more space is available in mBuffer. + mon.NotifyAll(); + + // Calculate remaining bytes requested by caller. If the stream is not + // draining an underrun has occurred, so fill the remaining buffer with + // silence. + bytesWanted -= available; + if (mState != DRAINING) { + memset(static_cast(aBuffer) + available, 0, bytesWanted); + mLostFrames += bytesWanted / mBytesPerFrame; + bytesWanted = 0; + } + + return aFrames - (bytesWanted / mBytesPerFrame); +} + +int +nsBufferedAudioStream::StateCallback(cubeb_state aState) +{ + if (aState == CUBEB_STATE_DRAINED) { + MonitorAutoLock mon(mMonitor); + mState = DRAINED; + mon.NotifyAll(); + } + return CUBEB_OK; +} +#endif + From 576ad01988874c9615520f0e003a03ae5ef0eeb4 Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Fri, 13 Jan 2012 14:38:39 +1300 Subject: [PATCH 82/90] Bug 623444 - Import Windows libcubeb implementation. r=doublec, r=khuey --- config/autoconf.mk.in | 1 + config/system-headers | 1 + configure.in | 18 +- js/src/config/system-headers | 1 + layout/build/Makefile.in | 4 - layout/media/Makefile.in | 10 + layout/media/symbols.def.in | 9 + media/libcubeb/AUTHORS | 1 + media/libcubeb/LICENSE | 13 + media/libcubeb/Makefile.in | 52 ++++ media/libcubeb/README | 3 + media/libcubeb/README_MOZILLA | 8 + media/libcubeb/include/Makefile.in | 48 +++ media/libcubeb/include/cubeb-stdint.h | 1 + media/libcubeb/include/cubeb.h | 208 +++++++++++++ media/libcubeb/src/Makefile.in | 58 ++++ media/libcubeb/src/cubeb_winmm.c | 433 ++++++++++++++++++++++++++ media/libcubeb/update.sh | 27 ++ toolkit/content/license.html | 25 ++ toolkit/toolkit-makefiles.sh | 8 + toolkit/toolkit-tiers.mk | 6 + 21 files changed, 930 insertions(+), 5 deletions(-) create mode 100644 media/libcubeb/AUTHORS create mode 100644 media/libcubeb/LICENSE create mode 100644 media/libcubeb/Makefile.in create mode 100644 media/libcubeb/README create mode 100644 media/libcubeb/README_MOZILLA create mode 100644 media/libcubeb/include/Makefile.in create mode 100644 media/libcubeb/include/cubeb-stdint.h create mode 100644 media/libcubeb/include/cubeb.h create mode 100644 media/libcubeb/src/Makefile.in create mode 100644 media/libcubeb/src/cubeb_winmm.c create mode 100644 media/libcubeb/update.sh diff --git a/config/autoconf.mk.in b/config/autoconf.mk.in index d7bb9421a58f..57cca0db44bf 100644 --- a/config/autoconf.mk.in +++ b/config/autoconf.mk.in @@ -160,6 +160,7 @@ MOZ_ZIPWRITER = @MOZ_ZIPWRITER@ MOZ_OGG = @MOZ_OGG@ MOZ_RAW = @MOZ_RAW@ MOZ_SYDNEYAUDIO = @MOZ_SYDNEYAUDIO@ +MOZ_CUBEB = @MOZ_CUBEB@ MOZ_WAVE = @MOZ_WAVE@ MOZ_MEDIA = @MOZ_MEDIA@ MOZ_VORBIS = @MOZ_VORBIS@ diff --git a/config/system-headers b/config/system-headers index 160816a0ddf8..5bddb3649656 100644 --- a/config/system-headers +++ b/config/system-headers @@ -1050,4 +1050,5 @@ tremor/ivorbiscodec.h ogg/ogg.h ogg/os_types.h nestegg/nestegg.h +cubeb/cubeb.h #endif diff --git a/configure.in b/configure.in index 8d6e1c31ca16..1847f11232a3 100644 --- a/configure.in +++ b/configure.in @@ -4586,6 +4586,7 @@ MOZ_AUTH_EXTENSION=1 MOZ_OGG=1 MOZ_RAW= MOZ_SYDNEYAUDIO= +MOZ_CUBEB= MOZ_VORBIS= MOZ_TREMOR= MOZ_WAVE=1 @@ -5596,6 +5597,7 @@ MOZ_ARG_DISABLE_BOOL(ogg, if test -n "$MOZ_OGG"; then AC_DEFINE(MOZ_OGG) MOZ_SYDNEYAUDIO=1 + MOZ_CUBEB=1 MOZ_MEDIA=1 case "$target_cpu" in arm*) @@ -5713,6 +5715,7 @@ AC_SUBST(MOZ_LIBVPX_LIBS) if test -n "$MOZ_WEBM" -a -z "$MOZ_NATIVE_LIBVPX"; then MOZ_SYDNEYAUDIO=1 + MOZ_CUBEB=1 MOZ_MEDIA=1 case "$target_cpu" in arm*) @@ -5824,17 +5827,29 @@ MOZ_ARG_DISABLE_BOOL(wave, if test -n "$MOZ_WAVE"; then AC_DEFINE(MOZ_WAVE) MOZ_SYDNEYAUDIO=1 + MOZ_CUBEB=1 MOZ_MEDIA=1 fi dnl ======================================================== -dnl = Handle dependent SYDNEYAUDIO and MEDIA defines +dnl = Handle dependent SYDNEYAUDIO, CUBEB, and MEDIA defines dnl ======================================================== if test -n "$MOZ_SYDNEYAUDIO"; then AC_DEFINE(MOZ_SYDNEYAUDIO) fi +if test -n "$MOZ_CUBEB"; then + case "$target" in + *-mingw*) + AC_DEFINE(MOZ_CUBEB) + ;; + *) + dnl Other targets will be enabled soon. + ;; + esac +fi + if test -n "$MOZ_MEDIA"; then AC_DEFINE(MOZ_MEDIA) fi @@ -8696,6 +8711,7 @@ AC_SUBST(MOZ_APP_EXTRA_LIBS) AC_SUBST(MOZ_MEDIA) AC_SUBST(MOZ_SYDNEYAUDIO) +AC_SUBST(MOZ_CUBEB) AC_SUBST(MOZ_WAVE) AC_SUBST(MOZ_VORBIS) AC_SUBST(MOZ_TREMOR) diff --git a/js/src/config/system-headers b/js/src/config/system-headers index 160816a0ddf8..5bddb3649656 100644 --- a/js/src/config/system-headers +++ b/js/src/config/system-headers @@ -1050,4 +1050,5 @@ tremor/ivorbiscodec.h ogg/ogg.h ogg/os_types.h nestegg/nestegg.h +cubeb/cubeb.h #endif diff --git a/layout/build/Makefile.in b/layout/build/Makefile.in index b2609b888ff8..4c42e8bd4d40 100644 --- a/layout/build/Makefile.in +++ b/layout/build/Makefile.in @@ -182,10 +182,6 @@ SHARED_LIBRARY_LIBS += \ $(NULL) endif -ifdef MOZ_SYDNEYAUDIO -LOCAL_INCLUDES += -I$(DEPTH)/content/html/content/src -endif - ifdef NS_PRINTING SHARED_LIBRARY_LIBS += \ ../printing/$(LIB_PREFIX)gkprinting_s.$(LIB_SUFFIX) \ diff --git a/layout/media/Makefile.in b/layout/media/Makefile.in index 7943211ea5ec..6988a80d389f 100644 --- a/layout/media/Makefile.in +++ b/layout/media/Makefile.in @@ -86,6 +86,12 @@ SHARED_LIBRARY_LIBS += \ $(NULL) endif +ifdef MOZ_CUBEB +SHARED_LIBRARY_LIBS += \ + $(DEPTH)/media/libcubeb/src/$(LIB_PREFIX)cubeb.$(LIB_SUFFIX) \ + $(NULL) +endif + SHARED_LIBRARY_LIBS += \ $(DEPTH)/gfx/angle/$(LIB_PREFIX)angle.$(LIB_SUFFIX) \ $(NULL) @@ -100,6 +106,10 @@ else EXTRA_DSO_LDOPTS += $(MOZ_ZLIB_LIBS) endif +ifdef MOZ_CUBEB +EXTRA_DSO_LDOPTS += $(call EXPAND_LIBNAME, winmm ksguid) +endif + DEFFILE = symbols.def endif diff --git a/layout/media/symbols.def.in b/layout/media/symbols.def.in index b09e08a19e88..f76c639a972f 100644 --- a/layout/media/symbols.def.in +++ b/layout/media/symbols.def.in @@ -73,6 +73,15 @@ sa_stream_pause sa_stream_resume sa_stream_write #endif +#ifdef MOZ_CUBEB +cubeb_destroy +cubeb_init +cubeb_stream_destroy +cubeb_stream_get_position +cubeb_stream_init +cubeb_stream_start +cubeb_stream_stop +#endif #ifdef MOZ_OGG th_comment_clear th_comment_init diff --git a/media/libcubeb/AUTHORS b/media/libcubeb/AUTHORS new file mode 100644 index 000000000000..8204f40f474d --- /dev/null +++ b/media/libcubeb/AUTHORS @@ -0,0 +1 @@ +Matthew Gregan diff --git a/media/libcubeb/LICENSE b/media/libcubeb/LICENSE new file mode 100644 index 000000000000..fffc9dc40536 --- /dev/null +++ b/media/libcubeb/LICENSE @@ -0,0 +1,13 @@ +Copyright © 2011 Mozilla Foundation + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/media/libcubeb/Makefile.in b/media/libcubeb/Makefile.in new file mode 100644 index 000000000000..9f5ac0e369d5 --- /dev/null +++ b/media/libcubeb/Makefile.in @@ -0,0 +1,52 @@ +# ***** 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) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Matthew Gregan +# +# 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 ***** + +DEPTH = ../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = cubeb + +DIRS = \ + include \ + src \ + $(NULL) + +include $(topsrcdir)/config/rules.mk diff --git a/media/libcubeb/README b/media/libcubeb/README new file mode 100644 index 000000000000..feb141ecde2c --- /dev/null +++ b/media/libcubeb/README @@ -0,0 +1,3 @@ +See INSTALL for build instructions. + +Licensed under an ISC-style license. See LICENSE for details. diff --git a/media/libcubeb/README_MOZILLA b/media/libcubeb/README_MOZILLA new file mode 100644 index 000000000000..f236dc0d1bc6 --- /dev/null +++ b/media/libcubeb/README_MOZILLA @@ -0,0 +1,8 @@ +The source from this directory was copied from the cubeb +git repository using the update.sh script. The only changes +made were those applied by update.sh and the addition of +Makefile.in build files for the Mozilla build system. + +The cubeb git repository is: git://github.com/kinetiknz/cubeb.git + +The git commit ID used was ddfaaf39c1a15cfb1a04ce62d6bd253737fc764a-dirty. diff --git a/media/libcubeb/include/Makefile.in b/media/libcubeb/include/Makefile.in new file mode 100644 index 000000000000..cd837d38e0a7 --- /dev/null +++ b/media/libcubeb/include/Makefile.in @@ -0,0 +1,48 @@ +# ***** 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) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Matthew Gregan +# +# 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 ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +EXPORTS_NAMESPACES = cubeb +EXPORTS_cubeb = cubeb.h cubeb-stdint.h + +include $(topsrcdir)/config/rules.mk diff --git a/media/libcubeb/include/cubeb-stdint.h b/media/libcubeb/include/cubeb-stdint.h new file mode 100644 index 000000000000..13c8060f7351 --- /dev/null +++ b/media/libcubeb/include/cubeb-stdint.h @@ -0,0 +1 @@ +#include "mozilla/StdInt.h" diff --git a/media/libcubeb/include/cubeb.h b/media/libcubeb/include/cubeb.h new file mode 100644 index 000000000000..25dde0e0b04e --- /dev/null +++ b/media/libcubeb/include/cubeb.h @@ -0,0 +1,208 @@ +/* + * Copyright © 2011 Mozilla Foundation + * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ +#ifndef CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 +#define CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @mainpage + + @section intro Introduction + + This is the documentation for the libcubeb C API. + libcubeb is a callback-based audio API library allowing the + authoring of portable multiplatform audio playback. + + @section example Example code + + @code + cubeb * app_ctx; + cubeb_init(&app_ctx, "Example Application"); + + cubeb_stream_params params; + params.format = CUBEB_SAMPLE_S16NE; + params.rate = 48000; + params.channels = 2; + + unsigned int latency_ms = 250; + + cubeb_stream * stm; + cubeb_stream_init(app_ctx, &stm, "Example Stream 1", params, + latency_ms, data_cb, state_cb, NULL); + + cubeb_stream_start(stm); + for (;;) { + cubeb_get_time(stm, &ts); + printf("time=%lu\n", ts); + sleep(1); + } + cubeb_stream_stop(stm); + + cubeb_stream_destroy(stm); + cubeb_destroy(app_ctx); + @endcode + + @code + long data_cb(cubeb_stream * stm, void * user, void * buffer, long nframes) + { + short * buf = buffer; + for (i = 0; i < nframes; ++i) { + for (c = 0; c < params.channels; ++c) { + buf[i][c] = 0; + } + } + return nframes; + } + @endcode + + @code + int state_cb(cubeb_stream * stm, void * user, cubeb_state state) + { + printf("state=%d\n", state); + return CUBEB_OK; + } + @endcode +*/ + + +/** @file + The libcubeb C API. */ + +typedef struct cubeb cubeb; /**< Opaque handle referencing the application state. */ +typedef struct cubeb_stream cubeb_stream; /**< Opaque handle referencing the stream state. */ + +/** Sample format enumeration. */ +typedef enum { + /**< Little endian 16-bit signed PCM. */ + CUBEB_SAMPLE_S16LE, + /**< Big endian 16-bit signed PCM. */ + CUBEB_SAMPLE_S16BE, + /**< Little endian 32-bit IEEE floating point PCM. */ + CUBEB_SAMPLE_FLOAT32LE, + /**< Big endian 32-bit IEEE floating point PCM. */ + CUBEB_SAMPLE_FLOAT32BE, +#ifdef WORDS_BIGENDIAN + /**< Native endian 16-bit signed PCM. */ + CUBEB_SAMPLE_S16NE = CUBEB_SAMPLE_S16BE, + /**< Native endian 32-bit IEEE floating point PCM. */ + CUBEB_SAMPLE_FLOAT32NE = CUBEB_SAMPLE_FLOAT32BE +#else + /**< Native endian 16-bit signed PCM. */ + CUBEB_SAMPLE_S16NE = CUBEB_SAMPLE_S16LE, + /**< Native endian 32-bit IEEE floating point PCM. */ + CUBEB_SAMPLE_FLOAT32NE = CUBEB_SAMPLE_FLOAT32LE +#endif +} cubeb_sample_format; + +/** Stream format initialization parameters. */ +typedef struct { + cubeb_sample_format format; /**< Requested sample format. One of + #cubeb_sample_format. */ + unsigned int rate; /**< Requested sample rate. Valid range is [1, 192000]. */ + unsigned int channels; /**< Requested channel count. Valid range is [1, 32]. */ +} cubeb_stream_params; + +/** Stream states signaled via state_callback. */ +typedef enum { + CUBEB_STATE_STARTED, /**< Stream started. */ + CUBEB_STATE_STOPPED, /**< Stream stopped. */ + CUBEB_STATE_DRAINED /**< Stream drained. */ +} cubeb_state; + +/** Result code enumeration. */ +enum { + CUBEB_OK = 0, /**< Success. */ + CUBEB_ERROR = -1, /**< Unclassified error. */ + CUBEB_ERROR_INVALID_FORMAT /**< Unsupported #cubeb_stream_params requested. */ +}; + +/** User supplied data callback. + @param stream + @param user_ptr + @param buffer + @param nframes + @retval Number of frames written to buffer, which must equal nframes except at end of stream. + @retval CUBEB_ERROR on error, in which case the data callback will stop + and the stream will enter a shutdown state. */ +typedef long (* cubeb_data_callback)(cubeb_stream * stream, + void * user_ptr, + void * buffer, + long nframes); + +/** User supplied state callback. + @param stream + @param user_ptr + @param state + @retval CUBEB_OK + @retval CUBEB_ERROR */ +typedef int (* cubeb_state_callback)(cubeb_stream * stream, + void * user_ptr, + cubeb_state state); + +/** Initialize an application context. This will perform any library or + application scoped initialization. + @param context + @param context_name + @retval CUBEB_OK + @retval CUBEB_ERROR */ +int cubeb_init(cubeb ** context, char const * context_name); + +/** Destroy an application context. + @param context */ +void cubeb_destroy(cubeb * context); + +/** Initialize a stream associated with the supplied application context. + @param context + @param stream + @param stream_name + @param stream_params + @param latency Approximate stream latency in milliseconds. Valid range is [1, 2000]. + @param data_callback Will be called to preroll data before playback is + started by cubeb_stream_start. + @param state_callback + @param user_ptr + @retval CUBEB_OK + @retval CUBEB_ERROR + @retval CUBEB_ERROR_INVALID_FORMAT */ +int cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, + cubeb_stream_params stream_params, unsigned int latency, + cubeb_data_callback data_callback, + cubeb_state_callback state_callback, + void * user_ptr); + +/** Destroy a stream. + @param stream */ +void cubeb_stream_destroy(cubeb_stream * stream); + +/** Start playback. + @param stream + @retval CUBEB_OK + @retval CUBEB_ERROR */ +int cubeb_stream_start(cubeb_stream * stream); + +/** Stop playback. + @param stream + @retval CUBEB_OK + @retval CUBEB_ERROR */ +int cubeb_stream_stop(cubeb_stream * stream); + +/** Get the current stream playback position. + @param stream + @param position Playback position in frames. + @retval CUBEB_OK + @retval CUBEB_ERROR */ +int cubeb_stream_get_position(cubeb_stream * stream, uint64_t * position); + +#ifdef __cplusplus +} +#endif + +#endif /* CUBEB_c2f983e9_c96f_e71c_72c3_bbf62992a382 */ diff --git a/media/libcubeb/src/Makefile.in b/media/libcubeb/src/Makefile.in new file mode 100644 index 000000000000..781df2d82fca --- /dev/null +++ b/media/libcubeb/src/Makefile.in @@ -0,0 +1,58 @@ +# ***** 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) 2011 +# the Initial Developer. All Rights Reserved. +# +# Contributor(s): +# Matthew Gregan +# +# 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 ***** + +DEPTH = ../../.. +topsrcdir = @top_srcdir@ +srcdir = @srcdir@ +VPATH = @srcdir@ + +include $(DEPTH)/config/autoconf.mk + +MODULE = cubeb +LIBRARY_NAME = cubeb +FORCE_STATIC_LIB= 1 +ifeq (WINNT,$(OS_TARGET)) +VISIBILITY_FLAGS = +endif + +ifeq (WINNT,$(OS_TARGET)) +CSRCS = \ + cubeb_winmm.c \ + $(NULL) +endif + +include $(topsrcdir)/config/rules.mk diff --git a/media/libcubeb/src/cubeb_winmm.c b/media/libcubeb/src/cubeb_winmm.c new file mode 100644 index 000000000000..1a0b98317f6a --- /dev/null +++ b/media/libcubeb/src/cubeb_winmm.c @@ -0,0 +1,433 @@ +/* + * Copyright  2011 Mozilla Foundation + * + * This program is made available under an ISC-style license. See the + * accompanying file LICENSE for details. + */ +#undef NDEBUG +#include +#include +#include +#include +#include +#include +#include "cubeb/cubeb.h" + +#include + +#define NBUFS 4 + +struct cubeb_stream_item { + SLIST_ENTRY head; + cubeb_stream * stream; +}; + +struct cubeb { + HANDLE event; + HANDLE thread; + int shutdown; + PSLIST_HEADER work; +}; + +struct cubeb_stream { + cubeb * context; + cubeb_stream_params params; + cubeb_data_callback data_callback; + cubeb_state_callback state_callback; + void * user_ptr; + WAVEHDR buffers[NBUFS]; + int next_buffer; + int free_buffers; + int shutdown; + int draining; + HANDLE event; + HWAVEOUT waveout; + CRITICAL_SECTION lock; +}; + +static size_t +bytes_per_frame(cubeb_stream_params params) +{ + size_t bytes; + + switch (params.format) { + case CUBEB_SAMPLE_S16LE: + bytes = sizeof(signed int); + break; + case CUBEB_SAMPLE_FLOAT32LE: + bytes = sizeof(float); + break; + default: + assert(0); + } + + return bytes * params.channels; +} + +static WAVEHDR * +cubeb_get_next_buffer(cubeb_stream * stm) +{ + WAVEHDR * hdr = NULL; + + assert(stm->free_buffers > 0 && stm->free_buffers <= NBUFS); + hdr = &stm->buffers[stm->next_buffer]; + assert(hdr->dwFlags == 0 || + (hdr->dwFlags & WHDR_DONE && !(hdr->dwFlags & WHDR_INQUEUE))); + stm->next_buffer = (stm->next_buffer + 1) % NBUFS; + stm->free_buffers -= 1; + + return hdr; +} + +static void +cubeb_submit_buffer(cubeb_stream * stm, WAVEHDR * hdr) +{ + long got; + MMRESULT r; + + got = stm->data_callback(stm, stm->user_ptr, hdr->lpData, + hdr->dwBufferLength / bytes_per_frame(stm->params)); + if (got < 0) { + /* XXX handle this case */ + assert(0); + return; + } else if ((DWORD) got < hdr->dwBufferLength / bytes_per_frame(stm->params)) { + r = waveOutUnprepareHeader(stm->waveout, hdr, sizeof(*hdr)); + assert(r == MMSYSERR_NOERROR); + + hdr->dwBufferLength = got * bytes_per_frame(stm->params); + + r = waveOutPrepareHeader(stm->waveout, hdr, sizeof(*hdr)); + assert(r == MMSYSERR_NOERROR); + + stm->draining = 1; + } + + assert(hdr->dwFlags & WHDR_PREPARED); + + r = waveOutWrite(stm->waveout, hdr, sizeof(*hdr)); + assert(r == MMSYSERR_NOERROR); +} + +static unsigned __stdcall +cubeb_buffer_thread(void * user_ptr) +{ + cubeb * ctx = (cubeb *) user_ptr; + assert(ctx); + + for (;;) { + DWORD rv; + struct cubeb_stream_item * item; + + rv = WaitForSingleObject(ctx->event, INFINITE); + assert(rv == WAIT_OBJECT_0); + + item = (struct cubeb_stream_item *) InterlockedPopEntrySList(ctx->work); + while (item) { + cubeb_stream * stm = item->stream; + + EnterCriticalSection(&stm->lock); + stm->free_buffers += 1; + assert(stm->free_buffers > 0 && stm->free_buffers <= NBUFS); + + if (stm->draining || stm->shutdown) { + if (stm->free_buffers == NBUFS) { + if (stm->draining) { + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED); + } + SetEvent(stm->event); + } + } else { + cubeb_submit_buffer(stm, cubeb_get_next_buffer(stm)); + } + LeaveCriticalSection(&stm->lock); + + _aligned_free(item); + + item = (struct cubeb_stream_item *) InterlockedPopEntrySList(ctx->work); + } + + if (ctx->shutdown) { + break; + } + } + + return 0; +} + +static void CALLBACK +cubeb_buffer_callback(HWAVEOUT waveout, UINT msg, DWORD_PTR user_ptr, DWORD_PTR p1, DWORD_PTR p2) +{ + cubeb_stream * stm = (cubeb_stream *) user_ptr; + struct cubeb_stream_item * item; + + if (msg != WOM_DONE) { + return; + } + + item = _aligned_malloc(sizeof(struct cubeb_stream_item), MEMORY_ALLOCATION_ALIGNMENT); + assert(item); + item->stream = stm; + InterlockedPushEntrySList(stm->context->work, &item->head); + + SetEvent(stm->context->event); +} + +int +cubeb_init(cubeb ** context, char const * context_name) +{ + cubeb * ctx; + + ctx = calloc(1, sizeof(*ctx)); + assert(ctx); + + ctx->work = _aligned_malloc(sizeof(*ctx->work), MEMORY_ALLOCATION_ALIGNMENT); + assert(ctx->work); + InitializeSListHead(ctx->work); + + ctx->event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!ctx->event) { + cubeb_destroy(ctx); + return CUBEB_ERROR; + } + + ctx->thread = (HANDLE) _beginthreadex(NULL, 64 * 1024, cubeb_buffer_thread, ctx, 0, NULL); + if (!ctx->thread) { + cubeb_destroy(ctx); + return CUBEB_ERROR; + } + + *context = ctx; + + return CUBEB_OK; +} + +void +cubeb_destroy(cubeb * ctx) +{ + DWORD rv; + + assert(!InterlockedPopEntrySList(ctx->work)); + + if (ctx->thread) { + ctx->shutdown = 1; + SetEvent(ctx->event); + rv = WaitForSingleObject(ctx->thread, INFINITE); + assert(rv == WAIT_OBJECT_0); + CloseHandle(ctx->thread); + } + + if (ctx->event) { + CloseHandle(ctx->event); + } + + _aligned_free(ctx->work); + + free(ctx); +} + +int +cubeb_stream_init(cubeb * context, cubeb_stream ** stream, char const * stream_name, + cubeb_stream_params stream_params, unsigned int latency, + cubeb_data_callback data_callback, + cubeb_state_callback state_callback, + void * user_ptr) +{ + MMRESULT r; + WAVEFORMATEXTENSIBLE wfx; + cubeb_stream * stm; + int i; + size_t bufsz; + + if (stream_params.rate < 1 || stream_params.rate > 192000 || + stream_params.channels < 1 || stream_params.channels > 32 || + latency < 1 || latency > 2000) { + return CUBEB_ERROR_INVALID_FORMAT; + } + + memset(&wfx, 0, sizeof(wfx)); + if (stream_params.channels > 2) { + wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE; + wfx.Format.cbSize = sizeof(wfx) - sizeof(wfx.Format); + } else { + wfx.Format.wFormatTag = WAVE_FORMAT_PCM; + if (stream_params.format == CUBEB_SAMPLE_FLOAT32LE) { + wfx.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT; + } + wfx.Format.cbSize = 0; + } + wfx.Format.nChannels = stream_params.channels; + wfx.Format.nSamplesPerSec = stream_params.rate; + + /* XXX fix channel mappings */ + wfx.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; + + switch (stream_params.format) { + case CUBEB_SAMPLE_S16LE: + wfx.Format.wBitsPerSample = 16; + wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM; + break; + case CUBEB_SAMPLE_FLOAT32LE: + wfx.Format.wBitsPerSample = 32; + wfx.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + break; + default: + return CUBEB_ERROR_INVALID_FORMAT; + } + + wfx.Format.nBlockAlign = (wfx.Format.wBitsPerSample * wfx.Format.nChannels) / 8; + wfx.Format.nAvgBytesPerSec = wfx.Format.nSamplesPerSec * wfx.Format.nBlockAlign; + wfx.Samples.wValidBitsPerSample = 0; + wfx.Samples.wSamplesPerBlock = 0; + wfx.Samples.wReserved = 0; + + stm = calloc(1, sizeof(*stm)); + assert(stm); + + stm->context = context; + + stm->params = stream_params; + + stm->data_callback = data_callback; + stm->state_callback = state_callback; + stm->user_ptr = user_ptr; + + bufsz = (size_t) (stm->params.rate / 1000.0 * latency * bytes_per_frame(stm->params) / NBUFS); + if (bufsz % bytes_per_frame(stm->params) != 0) { + bufsz += bytes_per_frame(stm->params) - (bufsz % bytes_per_frame(stm->params)); + } + assert(bufsz % bytes_per_frame(stm->params) == 0); + + for (i = 0; i < NBUFS; ++i) { + stm->buffers[i].lpData = calloc(1, bufsz); + assert(stm->buffers[i].lpData); + stm->buffers[i].dwBufferLength = bufsz; + stm->buffers[i].dwFlags = 0; + } + + InitializeCriticalSection(&stm->lock); + + stm->event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!stm->event) { + cubeb_stream_destroy(stm); + return CUBEB_ERROR; + } + + stm->free_buffers = NBUFS; + + /* cubeb_buffer_callback will be called during waveOutOpen, so all + other initialization must be complete before calling it. */ + r = waveOutOpen(&stm->waveout, WAVE_MAPPER, &wfx.Format, + (DWORD_PTR) cubeb_buffer_callback, (DWORD_PTR) stm, + CALLBACK_FUNCTION); + if (r != MMSYSERR_NOERROR) { + cubeb_stream_destroy(stm); + return CUBEB_ERROR; + } + assert(r == MMSYSERR_NOERROR); + + r = waveOutPause(stm->waveout); + assert(r == MMSYSERR_NOERROR); + + for (i = 0; i < NBUFS; ++i) { + WAVEHDR * hdr = cubeb_get_next_buffer(stm); + + r = waveOutPrepareHeader(stm->waveout, hdr, sizeof(*hdr)); + assert(r == MMSYSERR_NOERROR); + + cubeb_submit_buffer(stm, hdr); + } + + *stream = stm; + + return CUBEB_OK; +} + +void +cubeb_stream_destroy(cubeb_stream * stm) +{ + MMRESULT r; + DWORD rv; + int i; + int enqueued; + + if (stm->waveout) { + EnterCriticalSection(&stm->lock); + stm->shutdown = 1; + + r = waveOutReset(stm->waveout); + assert(r == MMSYSERR_NOERROR); + + enqueued = NBUFS - stm->free_buffers; + LeaveCriticalSection(&stm->lock); + + /* wait for all blocks to complete */ + if (enqueued > 0) { + rv = WaitForSingleObject(stm->event, INFINITE); + assert(rv == WAIT_OBJECT_0); + } + + for (i = 0; i < NBUFS; ++i) { + r = waveOutUnprepareHeader(stm->waveout, &stm->buffers[i], sizeof(stm->buffers[i])); + assert(r == MMSYSERR_NOERROR); + } + + r = waveOutClose(stm->waveout); + assert(r == MMSYSERR_NOERROR); + } + + if (stm->event) { + CloseHandle(stm->event); + } + + DeleteCriticalSection(&stm->lock); + + for (i = 0; i < NBUFS; ++i) { + free(stm->buffers[i].lpData); + } + + free(stm); +} + +int +cubeb_stream_start(cubeb_stream * stm) +{ + MMRESULT r; + + r = waveOutRestart(stm->waveout); + assert(r == MMSYSERR_NOERROR); + + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STARTED); + + return CUBEB_OK; +} + +int +cubeb_stream_stop(cubeb_stream * stm) +{ + MMRESULT r; + + r = waveOutPause(stm->waveout); + assert(r == MMSYSERR_NOERROR); + + stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_STOPPED); + + return CUBEB_OK; +} + +int +cubeb_stream_get_position(cubeb_stream * stm, uint64_t * position) +{ + MMRESULT r; + MMTIME time; + + time.wType = TIME_SAMPLES; + r = waveOutGetPosition(stm->waveout, &time, sizeof(time)); + assert(r == MMSYSERR_NOERROR); + assert(time.wType == TIME_SAMPLES); + + *position = time.u.sample; + + return CUBEB_OK; +} + diff --git a/media/libcubeb/update.sh b/media/libcubeb/update.sh new file mode 100644 index 000000000000..a5d9addcdca4 --- /dev/null +++ b/media/libcubeb/update.sh @@ -0,0 +1,27 @@ +# Usage: sh update.sh +set -e + +cp $1/include/cubeb/cubeb.h include +cp $1/src/cubeb_alsa.c src +cp $1/src/cubeb_winmm.c src +cp $1/src/cubeb_audiounit.c src +cp $1/LICENSE . +cp $1/README . +cp $1/AUTHORS . +if [ -d $1/.git ]; then + rev=$(cd $1 && git rev-parse --verify HEAD) + dirty=$(cd $1 && git diff-index --name-only HEAD) +fi + +if [ -n "$rev" ]; then + version=$rev + if [ -n "$dirty" ]; then + version=$version-dirty + echo "WARNING: updating from a dirty git repository." + fi + sed -i.bak -e "/The git commit ID used was/ s/[0-9a-f]\+\(-dirty\)\?\./$version./" README_MOZILLA + rm README_MOZILLA.bak +else + echo "Remember to update README_MOZILLA with the version details." +fi + diff --git a/toolkit/content/license.html b/toolkit/content/license.html index 342921aec3ab..9e75f74251d7 100644 --- a/toolkit/content/license.html +++ b/toolkit/content/license.html @@ -132,6 +132,7 @@
  • Japan Network Information Center License
  • jemalloc License
  • jQuery License
  • +
  • libcubeb License
  • libevent License
  • libffi License
  • libnestegg License
  • @@ -2936,6 +2937,30 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    +

    libcubeb License

    + +

    This license applies to files in the directory + media/libcubeb. +

    + +
    +Copyright © 2011 Mozilla Foundation
    +
    +Permission to use, copy, modify, and distribute this software for any
    +purpose with or without fee is hereby granted, provided that the above
    +copyright notice and this permission notice appear in all copies.
    +
    +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
    +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
    +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
    +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
    +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
    +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
    +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
    +
    + +
    +

    libevent License

    This license applies to files in the directory diff --git a/toolkit/toolkit-makefiles.sh b/toolkit/toolkit-makefiles.sh index 1c9ba46e2d91..1817f48ad5e8 100644 --- a/toolkit/toolkit-makefiles.sh +++ b/toolkit/toolkit-makefiles.sh @@ -1584,6 +1584,14 @@ if [ "$MOZ_WAVE" ]; then " fi +if [ "$MOZ_CUBEB" ]; then + add_makefiles " + media/libcubeb/Makefile + media/libcubeb/include/Makefile + media/libcubeb/src/Makefile + " +fi + if [ "$MOZ_SYDNEYAUDIO" ]; then add_makefiles " media/libsydneyaudio/Makefile diff --git a/toolkit/toolkit-tiers.mk b/toolkit/toolkit-tiers.mk index 72fe7dcba3c4..89163d1d3152 100644 --- a/toolkit/toolkit-tiers.mk +++ b/toolkit/toolkit-tiers.mk @@ -154,6 +154,12 @@ tier_platform_dirs += \ $(NULL) endif +ifdef MOZ_CUBEB +tier_platform_dirs += \ + media/libcubeb \ + $(NULL) +endif + ifndef MOZ_NATIVE_PNG tier_platform_dirs += media/libpng endif From eb099720cf03d9d2fed87f915912e67dfb7d1c96 Mon Sep 17 00:00:00 2001 From: Matthew Gregan Date: Thu, 2 Feb 2012 15:28:12 +1300 Subject: [PATCH 83/90] Bug 687927 - Remove vestigial logging. r=roc --- content/html/content/src/nsHTMLMediaElement.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp index 8346af12d2d6..d01f812b6d5f 100644 --- a/content/html/content/src/nsHTMLMediaElement.cpp +++ b/content/html/content/src/nsHTMLMediaElement.cpp @@ -2087,10 +2087,6 @@ nsresult nsHTMLMediaElement::FinishDecoderSetup(nsMediaDecoder* aDecoder) { NS_ASSERTION(mLoadingSrc, "mLoadingSrc set up"); - nsCAutoString src; - GetCurrentSpec(src); - printf("*** nsHTMLElement::FinishDecoderSetup() mDecoder=%p stream=%p src=%s\n", - aDecoder, aDecoder->GetStream(), src.get()); mDecoder = aDecoder; AddMediaElementToURITable(); From c2a3b4c2acd06f3ab3a1d9c04962f4ddb87df7bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Wed, 1 Feb 2012 18:51:54 -0800 Subject: [PATCH 84/90] Bug XXXXXX - port Bug 671634 to b2g --- b2g/app/b2g.js | 1 - b2g/confvars.sh | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 6b9e24273f22..68031fc00ac4 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -38,7 +38,6 @@ #filter substitution pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.xul"); -pref("general.useragent.compatMode.firefox", true); pref("browser.chromeURL", "chrome://browser/content/"); #ifdef MOZ_OFFICIAL_BRANDING pref("browser.homescreenURL", "file:///system/home/homescreen.html"); diff --git a/b2g/confvars.sh b/b2g/confvars.sh index e1e259b877d0..86e1bee3d8de 100644 --- a/b2g/confvars.sh +++ b/b2g/confvars.sh @@ -38,7 +38,8 @@ MOZ_APP_BASENAME=B2G MOZ_APP_VENDOR=Mozilla -MOZ_APP_VERSION=11.0a1 +MOZ_APP_VERSION=13.0a1 +MOZ_APP_UA_NAME=Firefox MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official From b7ee99591bdcec33f0e1c7cdd88beac7d18dbf04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Wed, 1 Feb 2012 19:04:31 -0800 Subject: [PATCH 85/90] Backed out changeset 61352cba1254 --- b2g/app/b2g.js | 1 + b2g/confvars.sh | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 68031fc00ac4..6b9e24273f22 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -38,6 +38,7 @@ #filter substitution pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.xul"); +pref("general.useragent.compatMode.firefox", true); pref("browser.chromeURL", "chrome://browser/content/"); #ifdef MOZ_OFFICIAL_BRANDING pref("browser.homescreenURL", "file:///system/home/homescreen.html"); diff --git a/b2g/confvars.sh b/b2g/confvars.sh index 86e1bee3d8de..e1e259b877d0 100644 --- a/b2g/confvars.sh +++ b/b2g/confvars.sh @@ -38,8 +38,7 @@ MOZ_APP_BASENAME=B2G MOZ_APP_VENDOR=Mozilla -MOZ_APP_VERSION=13.0a1 -MOZ_APP_UA_NAME=Firefox +MOZ_APP_VERSION=11.0a1 MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official From 4ff1b0b66bea13a2ec0f04b7d795a3d1e896d094 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Wed, 1 Feb 2012 19:13:33 -0800 Subject: [PATCH 86/90] Bug 721004 - Revert changes to dom/telephony/nsTelephony.cpp [a=backout] --- dom/telephony/Telephony.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/dom/telephony/Telephony.cpp b/dom/telephony/Telephony.cpp index 392f7a2a544b..8dce5ac482be 100644 --- a/dom/telephony/Telephony.cpp +++ b/dom/telephony/Telephony.cpp @@ -57,7 +57,7 @@ USING_TELEPHONY_NAMESPACE using mozilla::Preferences; -#define DOM_TELEPHONY_WHITELIST "dom.telephony.whitelist" +#define DOM_TELEPHONY_APP_PHONE_URL_PREF "dom.telephony.app.phone.url" namespace { @@ -495,9 +495,13 @@ NS_NewTelephony(nsPIDOMWindow* aWindow, nsIDOMTelephony** aTelephony) rv = documentURI->GetSpec(documentURL); NS_ENSURE_SUCCESS(rv, rv); - // Fail if the node principal isn't trusted. - if (!nsContentUtils::URIIsChromeOrInPref(documentURI, - DOM_TELEPHONY_WHITELIST)) { + // The pref may not exist but in that case we deny access just as we do if + // the url doesn't match. + nsCString phoneAppURL; + if (NS_FAILED(Preferences::GetCString(DOM_TELEPHONY_APP_PHONE_URL_PREF, + &phoneAppURL)) || + !phoneAppURL.Equals(documentURL, + nsCaseInsensitiveCStringComparator())) { *aTelephony = nsnull; return NS_OK; } From 0a1475455aacfbb0818e16d8cf2a25594461d285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Wed, 1 Feb 2012 19:13:36 -0800 Subject: [PATCH 87/90] Bug 723169 - port Bug 671634 to b2g [r=cjones] --- b2g/app/b2g.js | 1 - b2g/confvars.sh | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/b2g/app/b2g.js b/b2g/app/b2g.js index 6b9e24273f22..68031fc00ac4 100644 --- a/b2g/app/b2g.js +++ b/b2g/app/b2g.js @@ -38,7 +38,6 @@ #filter substitution pref("toolkit.defaultChromeURI", "chrome://browser/content/shell.xul"); -pref("general.useragent.compatMode.firefox", true); pref("browser.chromeURL", "chrome://browser/content/"); #ifdef MOZ_OFFICIAL_BRANDING pref("browser.homescreenURL", "file:///system/home/homescreen.html"); diff --git a/b2g/confvars.sh b/b2g/confvars.sh index e1e259b877d0..86e1bee3d8de 100644 --- a/b2g/confvars.sh +++ b/b2g/confvars.sh @@ -38,7 +38,8 @@ MOZ_APP_BASENAME=B2G MOZ_APP_VENDOR=Mozilla -MOZ_APP_VERSION=11.0a1 +MOZ_APP_VERSION=13.0a1 +MOZ_APP_UA_NAME=Firefox MOZ_BRANDING_DIRECTORY=b2g/branding/unofficial MOZ_OFFICIAL_BRANDING_DIRECTORY=b2g/branding/official From 853d196cb5f8ebf60545c7dfad9617a10e40f265 Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Fri, 6 Jan 2012 17:05:33 -0800 Subject: [PATCH 88/90] Bug 716136 - Add a configure flag to enable Generational GC; r=billm These are already in the tree in several places to protect (empty) post write barriers. This will make it easier to test this functionality on more platforms going forward. --HG-- extra : rebase_source : d9c7f4f46392abbd608da94d02a7bc45f9fe8613 --- js/src/configure.in | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/js/src/configure.in b/js/src/configure.in index 89fe61707b32..84390cf6f2cb 100644 --- a/js/src/configure.in +++ b/js/src/configure.in @@ -4348,6 +4348,17 @@ if test -n "$JSGC_INCREMENTAL"; then AC_DEFINE(JSGC_INCREMENTAL) fi +dnl ======================================================== +dnl = Use generational GC +dnl ======================================================== +MOZ_ARG_ENABLE_BOOL(gcgenerational, +[ --enable-gcgenerational Enable generational GC], + JSGC_GENERATIONAL=1, + JSGC_GENERATIONAL= ) +if test -n "$JSGC_GENERATIONAL"; then + AC_DEFINE(JSGC_GENERATIONAL) +fi + dnl ======================================================== dnl = Perform moving GC stack rooting analysis dnl ======================================================== From 383d9f000dc550ef86ff093c42cef6acc90c4a1c Mon Sep 17 00:00:00 2001 From: Doug Turner Date: Wed, 1 Feb 2012 22:44:18 -0500 Subject: [PATCH 89/90] Bug 721006 - Crash when setting a very large document.title. 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 7e233d3602d9..8b01e930bf66 100644 --- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -1785,7 +1785,7 @@ Tab.prototype = { gecko: { type: "DOMTitleChanged", tabID: this.id, - title: aEvent.target.title + title: aEvent.target.title.substring(0, 255) } }); break; From 562b1a7223c470e172abe67e334f52d511fbad4b Mon Sep 17 00:00:00 2001 From: Gregory Szorc Date: Wed, 1 Feb 2012 19:52:45 -0800 Subject: [PATCH 90/90] Bug 723323 - Add extensions.getWithPerformance.url pref; r=Unfocused This is a follow-up from bug 716736. --- mobile/android/app/mobile.js | 3 ++- mobile/xul/app/mobile.js | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/mobile/android/app/mobile.js b/mobile/android/app/mobile.js index c8c42ad998c6..30c5a54bd38b 100644 --- a/mobile/android/app/mobile.js +++ b/mobile/android/app/mobile.js @@ -230,7 +230,8 @@ pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.or pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/mobile/search?q=%TERMS%"); pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%/%COMPATIBILITY_MODE%"); pref("extensions.getAddons.browseAddons", "https://addons.mozilla.org/%LOCALE%/mobile/"); -pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/guid:%IDS%?src=mobile&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%"); +pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/guid:%IDS%?src=mobile&appOS=%OS%&appVersion=%VERSION%"); +pref("extensions.getAddons.getWithPerformance.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/guid:%IDS%?src=mobile&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%"); /* preference for the locale picker */ pref("extensions.getLocales.get.url", ""); diff --git a/mobile/xul/app/mobile.js b/mobile/xul/app/mobile.js index d0fce54c2a28..4ca4909031d8 100644 --- a/mobile/xul/app/mobile.js +++ b/mobile/xul/app/mobile.js @@ -225,7 +225,8 @@ pref("extensions.getAddons.recommended.url", "https://services.addons.mozilla.or pref("extensions.getAddons.search.browseURL", "https://addons.mozilla.org/%LOCALE%/mobile/search?q=%TERMS%"); pref("extensions.getAddons.search.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/%TERMS%/all/%MAX_RESULTS%/%OS%/%VERSION%/%COMPATIBILITY_MODE%"); pref("extensions.getAddons.browseAddons", "https://addons.mozilla.org/%LOCALE%/mobile/"); -pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/guid:%IDS%?src=mobile&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%"); +pref("extensions.getAddons.get.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/guid:%IDS%?src=mobile&appOS=%OS%&appVersion=%VERSION%"); +pref("extensions.getAddons.getWithPerformance.url", "https://services.addons.mozilla.org/%LOCALE%/mobile/api/%API_VERSION%/search/guid:%IDS%?src=mobile&appOS=%OS%&appVersion=%VERSION%&tMain=%TIME_MAIN%&tFirstPaint=%TIME_FIRST_PAINT%&tSessionRestored=%TIME_SESSION_RESTORED%"); /* preference for the locale picker */ pref("extensions.getLocales.get.url", "");

    cell