Merge mozilla-inbound and mozilla-central
authorEhsan Akhgari <ehsan@mozilla.com>
Thu, 08 Sep 2011 16:57:20 -0400
changeset 78094 a38ad346d7cba3d4c13eab623dfcf62b2459cf58
parent 78051 817c2b9dc11d7d39d91cd1f376e673cb87dc9b77 (current diff)
parent 78093 d3709196278923f59818d7eb8d0f8bb73834d4ed (diff)
child 78095 aea08d37a22b31c294f6be561d2c8599b57b334b
push id78
push userclegnitto@mozilla.com
push dateFri, 16 Dec 2011 17:32:24 +0000
treeherdermozilla-release@79d24e644fdd [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone9.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-inbound and mozilla-central
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -729,29 +729,21 @@ pref("urlclassifier.gethashnoise", 4);
 // The list of tables that use the gethash request to confirm partial results.
 pref("urlclassifier.gethashtables", "goog-phish-shavar,goog-malware-shavar");
 
 // If an urlclassifier table has not been updated in this number of seconds,
 // a gethash request will be forced to check that the result is still in
 // the database.
 pref("urlclassifier.confirm-age", 2700);
 
-#ifdef MOZ_WIDGET_GTK2
-#define RESTRICT_CACHEMAX
-#endif
-#ifdef XP_OS2
-#define RESTRICT_CACHEMAX
-#endif
+// Maximum size of the sqlite3 cache during an update, in bytes
+pref("urlclassifier.updatecachemax", 41943040);
 
-// Maximum size of the sqlite3 cache during an update, in bytes
-#ifdef RESTRICT_CACHEMAX
-pref("urlclassifier.updatecachemax", 104857600);
-#else
-pref("urlclassifier.updatecachemax", -1);
-#endif
+// Maximum size of the sqlite3 cache for lookups, in bytes
+pref("urlclassifier.lookupcachemax", 1048576);
 
 // URL for checking the reason for a malware warning.
 pref("browser.safebrowsing.malware.reportURL", "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?client=%NAME%&hl=%LOCALE%&site=");
 
 #endif
 
 pref("browser.EULA.version", 3);
 pref("browser.rights.version", 3);
--- a/embedding/android/AndroidManifest.xml.in
+++ b/embedding/android/AndroidManifest.xml.in
@@ -5,17 +5,17 @@
       android:installLocation="auto"
       android:versionCode="@ANDROID_VERSION_CODE@"
       android:versionName="@MOZ_APP_VERSION@"
 #ifdef MOZ_ANDROID_SHARED_ID
       android:sharedUserId="@MOZ_ANDROID_SHARED_ID@"
 #endif
       >
     <uses-sdk android:minSdkVersion="5"
-              android:targetSdkVersion="5"/>
+              android:targetSdkVersion="11"/>
 
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
     <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
     <uses-permission android:name="android.permission.INTERNET"/>
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
     <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
     <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
 
--- a/embedding/android/resources/layout/notification_icon_text.xml
+++ b/embedding/android/resources/layout/notification_icon_text.xml
@@ -10,25 +10,25 @@
         android:layout_height="wrap_content"
         android:orientation="horizontal"
         >
         <ImageView android:id="@+id/notificationImage"
             android:layout_width="25dp"
             android:layout_height="25dp"
             android:scaleType="fitCenter" />
         <TextView android:id="@+id/notificationTitle"
+            android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:singleLine="true"
             android:ellipsize="marquee"
             android:fadingEdge="horizontal"
-            android:textStyle="bold"
-            android:textSize="18sp"
             android:paddingLeft="4dp"
-            android:textColor="#ff000000" />
+            />
     </LinearLayout>
     <TextView android:id="@+id/notificationText"
+          android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent"
 	      android:layout_width="fill_parent"
 	      android:layout_height="wrap_content"
 	      android:paddingLeft="4dp"
-	      android:textColor="#ff000000" />
+	      />
 </LinearLayout>
--- a/embedding/android/resources/layout/notification_progress.xml
+++ b/embedding/android/resources/layout/notification_progress.xml
@@ -11,38 +11,38 @@
         android:layout_height="wrap_content"
         android:orientation="horizontal"
         >
         <ImageView android:id="@+id/notificationImage"
             android:layout_width="25dp"
             android:layout_height="25dp"
             android:scaleType="fitCenter" />
         <TextView android:id="@+id/notificationTitle"
+            android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:singleLine="true"
             android:ellipsize="marquee"
             android:fadingEdge="horizontal"
-            android:textStyle="bold"
-            android:textSize="18sp"
             android:paddingLeft="10dp"
-            android:textColor="#ff000000" />
+            />
     </LinearLayout>
 
     <LinearLayout
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:orientation="horizontal"
         >
         <TextView android:id="@+id/notificationText"
+            android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent"
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:paddingLeft="3dp"
-            android:textColor="#ff000000" />
+            />
 
         <ProgressBar android:id="@+id/notificationProgressbar"
             style="?android:attr/progressBarStyleHorizontal"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_marginTop="1dip"
             android:layout_marginBottom="1dip"
             android:layout_marginLeft="4dip"
--- a/embedding/android/resources/layout/notification_progress_text.xml
+++ b/embedding/android/resources/layout/notification_progress_text.xml
@@ -10,37 +10,37 @@
         android:layout_height="wrap_content"
         android:orientation="horizontal"
         >
         <ImageView android:id="@+id/notificationImage"
             android:layout_width="25dp"
             android:layout_height="25dp"
             android:scaleType="fitCenter" />
         <TextView android:id="@+id/notificationTitle"
+            android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
             android:layout_width="fill_parent"
             android:layout_height="wrap_content"
             android:layout_weight="1"
             android:singleLine="true"
             android:ellipsize="marquee"
             android:fadingEdge="horizontal"
-            android:textStyle="bold"
-            android:textSize="18sp"
             android:paddingLeft="4dp"
-            android:textColor="#ff000000" />
+            />
     </LinearLayout>
 
     <ProgressBar android:id="@+id/notificationProgressbar"
         style="?android:attr/progressBarStyleHorizontal"
         android:layout_width="fill_parent"
         android:layout_height="16dip"
         android:layout_marginTop="1dip"
         android:layout_marginBottom="1dip"
         android:layout_marginLeft="10dip"
         android:layout_marginRight="10dip"
         android:layout_centerHorizontal="true" />
 
     <TextView android:id="@+id/notificationText"
+        android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:paddingLeft="4dp"
-        android:textColor="#ff000000" />
+        />
 
 </LinearLayout>
--- a/ipc/chromium/src/base/histogram.cc
+++ b/ipc/chromium/src/base/histogram.cc
@@ -125,16 +125,27 @@ void Histogram::Add(int value) {
   if (value < 0)
     value = 0;
   size_t index = BucketIndex(value);
   DCHECK_GE(value, ranges(index));
   DCHECK_LT(value, ranges(index + 1));
   Accumulate(value, 1, index);
 }
 
+void Histogram::Subtract(int value) {
+  if (value > kSampleType_MAX - 1)
+    value = kSampleType_MAX - 1;
+  if (value < 0)
+    value = 0;
+  size_t index = BucketIndex(value);
+  DCHECK_GE(value, ranges(index));
+  DCHECK_LT(value, ranges(index + 1));
+  Accumulate(value, -1, index);
+}
+
 void Histogram::AddBoolean(bool value) {
   DCHECK(false);
 }
 
 void Histogram::AddSampleSet(const SampleSet& sample) {
   sample_.Add(sample);
 }
 
--- a/ipc/chromium/src/base/histogram.h
+++ b/ipc/chromium/src/base/histogram.h
@@ -376,16 +376,17 @@ class Histogram {
                                Flags flags);
   static Histogram* FactoryTimeGet(const std::string& name,
                                    base::TimeDelta minimum,
                                    base::TimeDelta maximum,
                                    size_t bucket_count,
                                    Flags flags);
 
   void Add(int value);
+  void Subtract(int value);
 
   // This method is an interface, used only by BooleanHistogram.
   virtual void AddBoolean(bool value);
 
   // Accept a TimeDelta to increment.
   void AddTime(TimeDelta time) {
     Add(static_cast<int>(time.InMilliseconds()));
   }
--- a/js/src/Makefile.in
+++ b/js/src/Makefile.in
@@ -150,16 +150,17 @@ CPPSRCS		= \
 		jsutil.cpp \
 		jswatchpoint.cpp \
 		jsweakmap.cpp \
 		jswrapper.cpp \
 		jsxdrapi.cpp \
 		jsxml.cpp \
 		prmjtime.cpp \
 		sharkctl.cpp \
+		CallObject.cpp \
 		Debugger.cpp \
 		GlobalObject.cpp \
 		Stack.cpp \
 		String.cpp \
 		ParseMaps.cpp \
 		Unicode.cpp \
 		$(NULL)
 
@@ -242,16 +243,17 @@ VPATH		+= \
 		$(srcdir)/vm \
 		$(srcdir)/frontend \
 		$(NULL)
 
 EXPORTS_NAMESPACES = vm
 
 EXPORTS_vm = \
 		ArgumentsObject.h \
+		CallObject.h \
 		GlobalObject.h \
 		Stack.h \
 		String.h \
 		StringObject.h \
 		Unicode.h  \
 		$(NULL)
 
 ###############################################
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -67,16 +67,17 @@
 #include "jsproxy.h"
 #include "jsscan.h"
 #include "jsscope.h"
 #include "jsscript.h"
 #include "jsstr.h"
 #include "jsexn.h"
 #include "jsstaticcheck.h"
 #include "jstracer.h"
+#include "vm/CallObject.h"
 #include "vm/Debugger.h"
 
 #if JS_HAS_GENERATORS
 # include "jsiter.h"
 #endif
 
 #if JS_HAS_XDR
 # include "jsxdrapi.h"
@@ -86,17 +87,17 @@
 #include "methodjit/MethodJIT.h"
 #endif
 
 #include "jsatominlines.h"
 #include "jsfuninlines.h"
 #include "jsinferinlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
-
+#include "vm/CallObject-inl.h"
 #include "vm/ArgumentsObject-inl.h"
 #include "vm/Stack-inl.h"
 
 using namespace js;
 using namespace js::gc;
 using namespace js::types;
 
 inline JSObject *
@@ -278,32 +279,32 @@ js_GetArgsObject(JSContext *cx, StackFra
      * ensures this by synthesizing an arguments access at the start of any
      * strict mode function that contains an assignment to a parameter, or
      * that calls eval.)  Non-strict mode arguments use the frame pointer to
      * retrieve up-to-date parameter values.
      */
     if (argsobj->isStrictArguments())
         fp->forEachCanonicalActualArg(PutArg(argsobj->data()->slots));
     else
-        argsobj->setPrivate(fp);
+        argsobj->setStackFrame(fp);
 
     fp->setArgsObj(*argsobj);
     return argsobj;
 }
 
 void
 js_PutArgsObject(StackFrame *fp)
 {
     ArgumentsObject &argsobj = fp->argsObj();
     if (argsobj.isNormalArguments()) {
-        JS_ASSERT(argsobj.getPrivate() == fp);
+        JS_ASSERT(argsobj.maybeStackFrame() == fp);
         fp->forEachCanonicalActualArg(PutArg(argsobj.data()->slots));
-        argsobj.setPrivate(NULL);
+        argsobj.setStackFrame(NULL);
     } else {
-        JS_ASSERT(!argsobj.getPrivate());
+        JS_ASSERT(!argsobj.maybeStackFrame());
     }
 }
 
 #ifdef JS_TRACER
 
 /*
  * Traced versions of js_GetArgsObject and js_PutArgsObject.
  */
@@ -314,47 +315,47 @@ js_NewArgumentsOnTrace(JSContext *cx, ui
     if (!argsobj)
         return NULL;
 
     if (argsobj->isStrictArguments()) {
         /*
          * Strict mode callers must copy arguments into the created arguments
          * object. The trace-JITting code is in TraceRecorder::newArguments.
          */
-        JS_ASSERT(!argsobj->getPrivate());
+        JS_ASSERT(!argsobj->maybeStackFrame());
     } else {
-        argsobj->setPrivate(JS_ARGUMENTS_OBJECT_ON_TRACE);
+        argsobj->setOnTrace();
     }
 
     return argsobj;
 }
 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_NewArgumentsOnTrace, CONTEXT, UINT32, OBJECT,
                      0, nanojit::ACCSET_STORE_ANY)
 
 /* FIXME change the return type to void. */
 JSBool JS_FASTCALL
 js_PutArgumentsOnTrace(JSContext *cx, JSObject *obj, Value *argv)
 {
     NormalArgumentsObject *argsobj = obj->asNormalArguments();
 
-    JS_ASSERT(argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE);
+    JS_ASSERT(argsobj->onTrace());
 
     /*
      * TraceRecorder::putActivationObjects builds a single, contiguous array of
      * the arguments, regardless of whether #actuals > #formals so there is no
      * need to worry about actual vs. formal arguments.
      */
     Value *srcend = argv + argsobj->initialLength();
     Value *dst = argsobj->data()->slots;
     for (Value *src = argv; src < srcend; ++src, ++dst) {
         if (!dst->isMagic(JS_ARGS_HOLE))
             *dst = *src;
     }
 
-    argsobj->setPrivate(NULL);
+    argsobj->clearOnTrace();
     return true;
 }
 JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArgumentsOnTrace, CONTEXT, OBJECT, VALUEPTR, 0,
                      nanojit::ACCSET_STORE_ANY)
 
 #endif /* JS_TRACER */
 
 static JSBool
@@ -385,17 +386,17 @@ ArgGetter(JSContext *cx, JSObject *obj, 
     if (JSID_IS_INT(id)) {
         /*
          * arg can exceed the number of arguments if a script changed the
          * prototype to point to another Arguments object with a bigger argc.
          */
         uintN arg = uintN(JSID_TO_INT(id));
         if (arg < argsobj->initialLength()) {
             JS_ASSERT(!argsobj->element(arg).isMagic(JS_ARGS_HOLE));
-            if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate()))
+            if (StackFrame *fp = argsobj->maybeStackFrame())
                 *vp = fp->canonicalActualArg(arg);
             else
                 *vp = argsobj->element(arg);
         }
     } else if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
         if (!argsobj->hasOverriddenLength())
             vp->setInt32(argsobj->initialLength());
     } else {
@@ -422,17 +423,17 @@ ArgSetter(JSContext *cx, JSObject *obj, 
     if (!obj->isNormalArguments())
         return true;
 
     NormalArgumentsObject *argsobj = obj->asNormalArguments();
 
     if (JSID_IS_INT(id)) {
         uintN arg = uintN(JSID_TO_INT(id));
         if (arg < argsobj->initialLength()) {
-            if (StackFrame *fp = reinterpret_cast<StackFrame *>(argsobj->getPrivate())) {
+            if (StackFrame *fp = argsobj->maybeStackFrame()) {
                 JSScript *script = fp->functionScript();
                 if (script->usesArguments) {
                     if (arg < fp->numFormalArgs())
                         TypeScript::SetArgument(cx, script, arg, *vp);
                     fp->canonicalActualArg(arg) = *vp;
                 }
                 return true;
             }
@@ -668,17 +669,17 @@ MaybeMarkGenerator(JSTracer *trc, JSObje
     }
 #endif
 }
 
 static void
 args_trace(JSTracer *trc, JSObject *obj)
 {
     ArgumentsObject *argsobj = obj->asArguments();
-    if (argsobj->getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE) {
+    if (argsobj->onTrace()) {
         JS_ASSERT(!argsobj->isStrictArguments());
         return;
     }
 
     ArgumentsData *data = argsobj->data();
     if (data->callee.isObject())
         MarkObject(trc, data->callee.toObject(), js_callee_str);
     MarkValueRange(trc, argsobj->initialLength(), data->slots, js_arguments_str);
@@ -752,67 +753,16 @@ Class js::DeclEnvClass = {
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
     ConvertStub
 };
 
-/*
- * Construct a call object for the given bindings.  If this is a call object
- * for a function invocation, callee should be the function being called.
- * Otherwise it must be a call object for eval of strict mode code, and callee
- * must be null.
- */
-static JSObject *
-NewCallObject(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee)
-{
-    Bindings &bindings = script->bindings;
-    size_t argsVars = bindings.countArgsAndVars();
-    size_t slots = JSObject::CALL_RESERVED_SLOTS + argsVars;
-    gc::AllocKind kind = gc::GetGCObjectKind(slots);
-
-    /*
-     * Make sure that the arguments and variables in the call object all end up
-     * in a contiguous range of slots. We need this to be able to embed the
-     * args/vars arrays in the TypeScriptNesting for the function, after the
-     * call object's frame has finished.
-     */
-    if (cx->typeInferenceEnabled() && gc::GetGCKindSlots(kind) < slots) {
-        kind = gc::GetGCObjectKind(JSObject::CALL_RESERVED_SLOTS);
-        JS_ASSERT(gc::GetGCKindSlots(kind) == JSObject::CALL_RESERVED_SLOTS);
-    }
-
-    JSObject *callobj = js_NewGCObject(cx, kind);
-    if (!callobj)
-        return NULL;
-
-    /* Init immediately to avoid GC seeing a half-init'ed object. */
-    callobj->initCall(cx, bindings, &scopeChain);
-    callobj->makeVarObj();
-
-    /* This must come after callobj->lastProp has been set. */
-    if (!callobj->ensureInstanceReservedSlots(cx, argsVars))
-        return NULL;
-
-#ifdef DEBUG
-    for (Shape::Range r = callobj->lastProp; !r.empty(); r.popFront()) {
-        const Shape &s = r.front();
-        if (s.slot != SHAPE_INVALID_SLOT) {
-            JS_ASSERT(s.slot + 1 == callobj->slotSpan());
-            break;
-        }
-    }
-#endif
-
-    callobj->setCallObjCallee(callee);
-    return callobj;
-}
-
 static inline JSObject *
 NewDeclEnvObject(JSContext *cx, StackFrame *fp)
 {
     JSObject *envobj = js_NewGCObject(cx, FINALIZE_OBJECT2);
     if (!envobj)
         return NULL;
 
     EmptyShape *emptyDeclEnvShape = EmptyShape::getEmptyDeclEnvShape(cx);
@@ -821,17 +771,17 @@ NewDeclEnvObject(JSContext *cx, StackFra
     envobj->init(cx, &DeclEnvClass, &emptyTypeObject, &fp->scopeChain(), fp, false);
     envobj->setMap(emptyDeclEnvShape);
 
     return envobj;
 }
 
 namespace js {
 
-JSObject *
+CallObject *
 CreateFunCallObject(JSContext *cx, StackFrame *fp)
 {
     JS_ASSERT(fp->isNonEvalFunctionFrame());
     JS_ASSERT(!fp->hasCallObj());
 
     JSObject *scopeChain = &fp->scopeChain();
     JS_ASSERT_IF(scopeChain->isWith() || scopeChain->isBlock() || scopeChain->isCall(),
                  scopeChain->getPrivate() != fp);
@@ -847,260 +797,261 @@ CreateFunCallObject(JSContext *cx, Stack
 
         if (!DefineNativeProperty(cx, scopeChain, ATOM_TO_JSID(lambdaName),
                                   ObjectValue(fp->callee()), NULL, NULL,
                                   JSPROP_PERMANENT | JSPROP_READONLY, 0, 0)) {
             return NULL;
         }
     }
 
-    JSObject *callobj = NewCallObject(cx, fp->script(), *scopeChain, &fp->callee());
+    CallObject *callobj = CallObject::create(cx, fp->script(), *scopeChain, &fp->callee());
     if (!callobj)
         return NULL;
 
-    callobj->setPrivate(fp);
+    callobj->setStackFrame(fp);
     fp->setScopeChainWithOwnCallObj(*callobj);
     return callobj;
 }
 
-JSObject *
+CallObject *
 CreateEvalCallObject(JSContext *cx, StackFrame *fp)
 {
-    JSObject *callobj = NewCallObject(cx, fp->script(), fp->scopeChain(), NULL);
+    CallObject *callobj = CallObject::create(cx, fp->script(), fp->scopeChain(), NULL);
     if (!callobj)
         return NULL;
 
-    callobj->setPrivate(fp);
+    callobj->setStackFrame(fp);
     fp->setScopeChainWithOwnCallObj(*callobj);
     return callobj;
 }
 
 } // namespace js
 
 JSObject * JS_FASTCALL
 js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSObject *scopeChain)
 {
     JS_ASSERT(!js_IsNamedLambda(fun));
     JS_ASSERT(scopeChain);
     JS_ASSERT(callee);
-    return NewCallObject(cx, fun->script(), *scopeChain, callee);
+    return CallObject::create(cx, fun->script(), *scopeChain, callee);
 }
 
 JS_DEFINE_CALLINFO_4(extern, OBJECT, js_CreateCallObjectOnTrace, CONTEXT, FUNCTION, OBJECT, OBJECT,
                      0, nanojit::ACCSET_STORE_ANY)
 
-inline static void
-CopyValuesToCallObject(JSObject &callobj, uintN nargs, Value *argv, uintN nvars, Value *slots)
-{
-    JS_ASSERT(callobj.numSlots() >= JSObject::CALL_RESERVED_SLOTS + nargs + nvars);
-    callobj.copySlotRange(JSObject::CALL_RESERVED_SLOTS, argv, nargs);
-    callobj.copySlotRange(JSObject::CALL_RESERVED_SLOTS + nargs, slots, nvars);
-}
-
 void
 js_PutCallObject(StackFrame *fp)
 {
-    JSObject &callobj = fp->callObj();
-    JS_ASSERT(callobj.getPrivate() == fp);
+    CallObject &callobj = fp->callObj().asCall();
+    JS_ASSERT(callobj.maybeStackFrame() == fp);
     JS_ASSERT_IF(fp->isEvalFrame(), fp->isStrictEvalFrame());
-    JS_ASSERT(fp->isEvalFrame() == callobj.callIsForEval());
+    JS_ASSERT(fp->isEvalFrame() == callobj.isForEval());
 
     /* Get the arguments object to snapshot fp's actual argument values. */
     if (fp->hasArgsObj()) {
         if (!fp->hasOverriddenArgs())
-            callobj.setCallObjArguments(ObjectValue(fp->argsObj()));
+            callobj.setArguments(ObjectValue(fp->argsObj()));
         js_PutArgsObject(fp);
     }
 
     JSScript *script = fp->script();
     Bindings &bindings = script->bindings;
 
-    if (callobj.callIsForEval()) {
+    if (callobj.isForEval()) {
         JS_ASSERT(script->strictModeCode);
         JS_ASSERT(bindings.countArgs() == 0);
 
         /* This could be optimized as below, but keep it simple for now. */
-        CopyValuesToCallObject(callobj, 0, NULL, bindings.countVars(), fp->slots());
+        callobj.copyValues(0, NULL, bindings.countVars(), fp->slots());
     } else {
         JSFunction *fun = fp->fun();
-        JS_ASSERT(fun == callobj.getCallObjCalleeFunction());
+        JS_ASSERT(fun == callobj.getCalleeFunction());
         JS_ASSERT(script == fun->script());
 
         uintN n = bindings.countArgsAndVars();
         if (n > 0) {
-            JS_ASSERT(JSObject::CALL_RESERVED_SLOTS + n <= callobj.numSlots());
+            JS_ASSERT(CallObject::RESERVED_SLOTS + n <= callobj.numSlots());
 
             uint32 nvars = bindings.countVars();
             uint32 nargs = bindings.countArgs();
             JS_ASSERT(fun->nargs == nargs);
             JS_ASSERT(nvars + nargs == n);
 
             JSScript *script = fun->script();
             if (script->usesEval
 #ifdef JS_METHODJIT
                 || script->debugMode
 #endif
                 ) {
-                CopyValuesToCallObject(callobj, nargs, fp->formalArgs(), nvars, fp->slots());
+                callobj.copyValues(nargs, fp->formalArgs(), nvars, fp->slots());
             } else {
                 /*
                  * For each arg & var that is closed over, copy it from the stack
                  * into the call object.
                  */
                 uint32 nclosed = script->nClosedArgs;
                 for (uint32 i = 0; i < nclosed; i++) {
                     uint32 e = script->getClosedArg(i);
-                    callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + e, fp->formalArg(e));
+                    callobj.setArg(e, fp->formalArg(e));
                 }
 
                 nclosed = script->nClosedVars;
                 for (uint32 i = 0; i < nclosed; i++) {
                     uint32 e = script->getClosedVar(i);
-                    callobj.setSlot(JSObject::CALL_RESERVED_SLOTS + nargs + e, fp->slots()[e]);
+                    callobj.setVar(e, fp->slots()[e]);
                 }
             }
 
             /*
              * Update the args and vars for the active call if this is an outer
              * function in a script nesting.
              */
             types::TypeScriptNesting *nesting = script->nesting();
             if (nesting && script->isOuterFunction) {
-                nesting->argArray = callobj.callObjArgArray();
-                nesting->varArray = callobj.callObjVarArray();
+                nesting->argArray = callobj.argArray();
+                nesting->varArray = callobj.varArray();
             }
         }
 
         /* Clear private pointers to fp, which is about to go away. */
         if (js_IsNamedLambda(fun)) {
             JSObject *env = callobj.getParent();
 
             JS_ASSERT(env->isDeclEnv());
             JS_ASSERT(env->getPrivate() == fp);
             env->setPrivate(NULL);
         }
     }
 
-    callobj.setPrivate(NULL);
+    callobj.setStackFrame(NULL);
 }
 
 JSBool JS_FASTCALL
-js_PutCallObjectOnTrace(JSObject *callobj, uint32 nargs, Value *argv,
+js_PutCallObjectOnTrace(JSObject *obj, uint32 nargs, Value *argv,
                         uint32 nvars, Value *slots)
 {
-    JS_ASSERT(callobj->isCall());
-    JS_ASSERT(!callobj->getPrivate());
+    CallObject &callobj = obj->asCall();
+    JS_ASSERT(!callobj.maybeStackFrame());
 
     uintN n = nargs + nvars;
     if (n != 0)
-        CopyValuesToCallObject(*callobj, nargs, argv, nvars, slots);
+        callobj.copyValues(nargs, argv, nvars, slots);
 
     return true;
 }
 
 JS_DEFINE_CALLINFO_5(extern, BOOL, js_PutCallObjectOnTrace, OBJECT, UINT32, VALUEPTR,
                      UINT32, VALUEPTR, 0, nanojit::ACCSET_STORE_ANY)
 
 namespace js {
 
 static JSBool
 GetCallArguments(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
-    StackFrame *fp = obj->maybeCallObjStackFrame();
+    CallObject &callobj = obj->asCall();
+
+    StackFrame *fp = callobj.maybeStackFrame();
     if (fp && !fp->hasOverriddenArgs()) {
         JSObject *argsobj = js_GetArgsObject(cx, fp);
         if (!argsobj)
             return false;
         vp->setObject(*argsobj);
     } else {
-        *vp = obj->getCallObjArguments();
+        *vp = callobj.getArguments();
     }
     return true;
 }
 
 static JSBool
 SetCallArguments(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
 {
-    if (StackFrame *fp = obj->maybeCallObjStackFrame())
+    CallObject &callobj = obj->asCall();
+
+    if (StackFrame *fp = callobj.maybeStackFrame())
         fp->setOverriddenArgs();
-    obj->setCallObjArguments(*vp);
+    callobj.setArguments(*vp);
     return true;
 }
 
 JSBool
 GetCallArg(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
+    CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    if (StackFrame *fp = obj->maybeCallObjStackFrame())
+    if (StackFrame *fp = callobj.maybeStackFrame())
         *vp = fp->formalArg(i);
     else
-        *vp = obj->callObjArg(i);
+        *vp = callobj.arg(i);
     return true;
 }
 
 JSBool
 SetCallArg(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
 {
+    CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    if (StackFrame *fp = obj->maybeCallObjStackFrame())
+    if (StackFrame *fp = callobj.maybeStackFrame())
         fp->formalArg(i) = *vp;
     else
-        obj->setCallObjArg(i, *vp);
-
-    JSFunction *fun = obj->getCallObjCalleeFunction();
+        callobj.setArg(i, *vp);
+
+    JSFunction *fun = callobj.getCalleeFunction();
     JSScript *script = fun->script();
     if (!script->ensureHasTypes(cx, fun))
         return false;
 
     TypeScript::SetArgument(cx, script, i, *vp);
 
     return true;
 }
 
 JSBool
 GetCallUpvar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
+    CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    *vp = obj->getCallObjCallee()->getFlatClosureUpvar(i);
+    *vp = callobj.getCallee()->getFlatClosureUpvar(i);
     return true;
 }
 
 JSBool
 SetCallUpvar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
 {
+    CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    obj->getCallObjCallee()->setFlatClosureUpvar(i, *vp);
+    callobj.getCallee()->setFlatClosureUpvar(i, *vp);
     return true;
 }
 
 JSBool
 GetCallVar(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
+    CallObject &callobj = obj->asCall();
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
-    if (StackFrame *fp = obj->maybeCallObjStackFrame())
+    if (StackFrame *fp = callobj.maybeStackFrame())
         *vp = fp->varSlot(i);
     else
-        *vp = obj->callObjVar(i);
+        *vp = callobj.var(i);
     return true;
 }
 
 JSBool
 SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
 {
-    JS_ASSERT(obj->isCall());
+    CallObject &callobj = obj->asCall();
 
     JS_ASSERT((int16) JSID_TO_INT(id) == JSID_TO_INT(id));
     uintN i = (uint16) JSID_TO_INT(id);
 
     /*
      * As documented in TraceRecorder::attemptTreeCall(), when recording an
      * inner tree call, the recorder assumes the inner tree does not mutate
      * any tracked upvars. The abort here is a pessimistic precaution against
@@ -1110,22 +1061,22 @@ SetCallVar(JSContext *cx, JSObject *obj,
 #ifdef JS_TRACER
     if (JS_ON_TRACE(cx)) {
         TraceMonitor *tm = JS_TRACE_MONITOR_ON_TRACE(cx);
         if (tm->recorder && tm->tracecx)
             AbortRecording(cx, "upvar write in nested tree");
     }
 #endif
 
-    if (StackFrame *fp = obj->maybeCallObjStackFrame())
+    if (StackFrame *fp = callobj.maybeStackFrame())
         fp->varSlot(i) = *vp;
     else
-        obj->setCallObjVar(i, *vp);
-
-    JSFunction *fun = obj->getCallObjCalleeFunction();
+        callobj.setVar(i, *vp);
+
+    JSFunction *fun = callobj.getCalleeFunction();
     JSScript *script = fun->script();
     if (!script->ensureHasTypes(cx, fun))
         return false;
 
     TypeScript::SetLocal(cx, script, i, *vp);
 
     return true;
 }
@@ -1148,26 +1099,24 @@ js_SetCallVar(JSContext *cx, JSObject *o
     Value argcopy = ValueArgToConstRef(arg);
     return SetCallVar(cx, obj, slotid, false /* STRICT DUMMY */, &argcopy);
 }
 JS_DEFINE_CALLINFO_4(extern, BOOL, js_SetCallVar, CONTEXT, OBJECT, JSID, VALUE, 0,
                      nanojit::ACCSET_STORE_ANY)
 #endif
 
 static JSBool
-call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags,
-             JSObject **objp)
+call_resolve(JSContext *cx, JSObject *obj, jsid id, uintN flags, JSObject **objp)
 {
-    JS_ASSERT(obj->isCall());
     JS_ASSERT(!obj->getProto());
 
     if (!JSID_IS_ATOM(id))
         return true;
 
-    JSObject *callee = obj->getCallObjCallee();
+    JSObject *callee = obj->asCall().getCallee();
 #ifdef DEBUG
     if (callee) {
         JSScript *script = callee->getFunctionPrivate()->script();
         JS_ASSERT(!script->bindings.hasBinding(cx, JSID_TO_ATOM(id)));
     }
 #endif
 
     /*
@@ -1199,17 +1148,17 @@ call_trace(JSTracer *trc, JSObject *obj)
     JS_ASSERT(obj->isCall());
 
     MaybeMarkGenerator(trc, obj);
 }
 
 JS_PUBLIC_DATA(Class) js::CallClass = {
     "Call",
     JSCLASS_HAS_PRIVATE |
-    JSCLASS_HAS_RESERVED_SLOTS(JSObject::CALL_RESERVED_SLOTS) |
+    JSCLASS_HAS_RESERVED_SLOTS(CallObject::RESERVED_SLOTS) |
     JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS,
     PropertyStub,         /* addProperty */
     PropertyStub,         /* delProperty */
     PropertyStub,         /* getProperty */
     StrictPropertyStub,   /* setProperty */
     JS_EnumerateStub,
     (JSResolveOp)call_resolve,
     NULL,                 /* convert: Leave it NULL so we notice if calls ever escape */
--- a/js/src/jsfun.h
+++ b/js/src/jsfun.h
@@ -498,20 +498,20 @@ extern void
 js_PutCallObject(js::StackFrame *fp);
 
 extern JSBool JS_FASTCALL
 js_PutCallObjectOnTrace(JSObject *scopeChain, uint32 nargs, js::Value *argv,
                         uint32 nvars, js::Value *slots);
 
 namespace js {
 
-JSObject *
+CallObject *
 CreateFunCallObject(JSContext *cx, StackFrame *fp);
 
-JSObject *
+CallObject *
 CreateEvalCallObject(JSContext *cx, StackFrame *fp);
 
 extern JSBool
 GetCallArg(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
 
 extern JSBool
 GetCallVar(JSContext *cx, JSObject *obj, jsid id, js::Value *vp);
 
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -90,16 +90,17 @@
 
 #include "methodjit/MethodJIT.h"
 #include "vm/String.h"
 #include "vm/Debugger.h"
 
 #include "jsobjinlines.h"
 
 #include "vm/String-inl.h"
+#include "vm/CallObject-inl.h"
 
 #ifdef MOZ_VALGRIND
 # define JS_VALGRIND
 #endif
 #ifdef JS_VALGRIND
 # include <valgrind/memcheck.h>
 #endif
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -5050,18 +5050,18 @@ TypeScript::SetScope(JSContext *cx, JSSc
     JS_ASSERT(script->hasFunction == (fun != NULL));
     JS_ASSERT_IF(!fun, !script->isOuterFunction && !script->isInnerFunction);
     JS_ASSERT_IF(!scope, fun && !script->isInnerFunction);
 
     /*
      * The scope object must be the initial one for the script, before any call
      * object has been created in the heavyweight case.
      */
-    JS_ASSERT_IF(scope && scope->isCall() && !scope->callIsForEval(),
-                 scope->getCallObjCalleeFunction() != fun);
+    JS_ASSERT_IF(scope && scope->isCall() && !scope->asCall().isForEval(),
+                 scope->asCall().getCalleeFunction() != fun);
 
     if (!script->compileAndGo) {
         script->types->global = NULL;
         return true;
     }
 
     JS_ASSERT_IF(fun && scope, fun->getGlobal() == scope->getGlobal());
     script->types->global = fun ? fun->getGlobal() : scope->getGlobal();
@@ -5084,21 +5084,23 @@ TypeScript::SetScope(JSContext *cx, JSSc
 
     /*
      * Walk the scope chain to the next call object, which will be the function
      * the script is nested inside.
      */
     while (!scope->isCall())
         scope = scope->getParent();
 
+    CallObject &call = scope->asCall();
+
     /* The isInnerFunction test ensures there is no intervening strict eval call object. */
-    JS_ASSERT(!scope->callIsForEval());
+    JS_ASSERT(!call.isForEval());
 
     /* Don't track non-heavyweight parents, NAME ops won't reach into them. */
-    JSFunction *parentFun = scope->getCallObjCalleeFunction();
+    JSFunction *parentFun = call.getCalleeFunction();
     if (!parentFun || !parentFun->isHeavyweight())
         return true;
     JSScript *parent = parentFun->script();
     JS_ASSERT(parent->isOuterFunction);
 
     /*
      * We only need the nesting in the child if it has NAME accesses going
      * into the parent. We won't know for sure whether this is the case until
@@ -5116,18 +5118,18 @@ TypeScript::SetScope(JSContext *cx, JSSc
      * marked as reentrant.
      */
     if (!parent->ensureHasTypes(cx, parentFun))
         return false;
     if (!parent->types->hasScope()) {
         if (!SetScope(cx, parent, scope->getParent()))
             return false;
         parent->nesting()->activeCall = scope;
-        parent->nesting()->argArray = scope->callObjArgArray();
-        parent->nesting()->varArray = scope->callObjVarArray();
+        parent->nesting()->argArray = call.argArray();
+        parent->nesting()->varArray = call.varArray();
     }
 
     JS_ASSERT(!script->types->nesting);
 
     /* Construct and link nesting information for the two functions. */
 
     script->types->nesting = cx->new_<TypeScriptNesting>();
     if (!script->types->nesting)
@@ -5207,17 +5209,17 @@ ClearActiveNesting(JSScript *start)
  */
 static void
 CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
 {
   restart:
     JSScript *parent = script->nesting()->parent;
     JS_ASSERT(parent);
 
-    while (!scope->isCall() || scope->getCallObjCalleeFunction()->script() != parent)
+    while (!scope->isCall() || scope->asCall().getCalleeFunction()->script() != parent)
         scope = scope->getParent();
 
     if (scope != parent->nesting()->activeCall) {
         parent->reentrantOuterFunction = true;
         MarkTypeObjectFlags(cx, parent->function(), OBJECT_FLAG_REENTRANT_FUNCTION);
 
         /*
          * Continue checking parents to see if this is reentrant for them too.
--- a/js/src/jsinfer.h
+++ b/js/src/jsinfer.h
@@ -50,17 +50,17 @@
 #include "jsvalue.h"
 #include "jshashtable.h"
 
 namespace js {
     class CallArgs;
     namespace analyze {
         class ScriptAnalysis;
     }
-    struct GlobalObject;
+    class GlobalObject;
 }
 
 namespace js {
 namespace types {
 
 /* Forward declarations. */
 class TypeSet;
 struct TypeCallsite;
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -3895,17 +3895,17 @@ BEGIN_CASE(JSOP_GETELEM)
             }
         } else if (obj->isArguments()) {
             uint32 arg = uint32(i);
             ArgumentsObject *argsobj = obj->asArguments();
 
             if (arg < argsobj->initialLength()) {
                 copyFrom = &argsobj->element(arg);
                 if (!copyFrom->isMagic(JS_ARGS_HOLE)) {
-                    if (StackFrame *afp = reinterpret_cast<StackFrame *>(argsobj->getPrivate()))
+                    if (StackFrame *afp = argsobj->maybeStackFrame())
                         copyFrom = &afp->canonicalActualArg(arg);
                     goto end_getelem;
                 }
             }
         }
         if (JS_LIKELY(INT_FITS_IN_JSID(i)))
             id = INT_TO_JSID(i);
         else
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4486,17 +4486,17 @@ bool
 JSObject::growSlots(JSContext *cx, size_t newcap)
 {
     /*
      * Slots are only allocated for call objects when new properties are
      * added to them, which can only happen while the call is still on the
      * stack (and an eval, DEFFUN, etc. happens). We thus do not need to
      * worry about updating any active outer function args/vars.
      */
-    JS_ASSERT_IF(isCall(), maybeCallObjStackFrame() != NULL);
+    JS_ASSERT_IF(isCall(), asCall().maybeStackFrame() != NULL);
 
     /*
      * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
      * grow, double its capacity, to add N elements in amortized O(N) time.
      *
      * Above this limit, grow by 12.5% each time. Speed is still amortized
      * O(N), with a higher constant factor, and we waste less space.
      */
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -61,16 +61,17 @@
 
 namespace nanojit { class ValidateWriter; }
 
 namespace js {
 
 class AutoPropDescArrayRooter;
 class JSProxyHandler;
 class RegExp;
+class CallObject;
 struct GCMarker;
 struct NativeIterator;
 
 namespace mjit { class Compiler; }
 
 static inline PropertyOp
 CastAsPropertyOp(JSObject *object)
 {
@@ -728,16 +729,18 @@ struct JSObject : js::gc::Cell {
     /* Whether this object has any dynamic slots at all. */
     inline bool hasSlotsArray() const;
 
     /* Get the number of dynamic slots required for a given capacity. */
     inline size_t numDynamicSlots(size_t capacity) const;
 
   private:
     inline js::Value* fixedSlots() const;
+
+  protected:
     inline bool hasContiguousSlots(size_t start, size_t count) const;
 
   public:
     /* Minimum size for dynamically allocated slots. */
     static const uint32 SLOT_CAPACITY_MIN = 8;
 
     bool allocSlots(JSContext *cx, size_t nslots);
     bool growSlots(JSContext *cx, size_t nslots);
@@ -1060,68 +1063,20 @@ struct JSObject : js::gc::Cell {
     inline uint32 arrayBufferByteLength();
     inline uint8 * arrayBufferDataOffset();
 
   public:
     inline js::ArgumentsObject *asArguments();
     inline js::NormalArgumentsObject *asNormalArguments();
     inline js::StrictArgumentsObject *asStrictArguments();
 
-  private:
-    /*
-     * Reserved slot structure for Call objects:
-     *
-     * private               - the stack frame corresponding to the Call object
-     *                         until js_PutCallObject or its on-trace analog
-     *                         is called, null thereafter
-     * JSSLOT_CALL_CALLEE    - callee function for the stack frame, or null if
-     *                         the stack frame is for strict mode eval code
-     * JSSLOT_CALL_ARGUMENTS - arguments object for non-strict mode eval stack
-     *                         frames (not valid for strict mode eval frames)
-     */
-    static const uint32 JSSLOT_CALL_CALLEE = 0;
-    static const uint32 JSSLOT_CALL_ARGUMENTS = 1;
+  public:
+    inline js::CallObject &asCall();
 
   public:
-    /* Number of reserved slots. */
-    static const uint32 CALL_RESERVED_SLOTS = 2;
-
-    /* True if this is for a strict mode eval frame or for a function call. */
-    inline bool callIsForEval() const;
-
-    /* The stack frame for this Call object, if the frame is still active. */
-    inline js::StackFrame *maybeCallObjStackFrame() const;
-
-    /*
-     * The callee function if this Call object was created for a function
-     * invocation, or null if it was created for a strict mode eval frame.
-     */
-    inline JSObject *getCallObjCallee() const;
-    inline JSFunction *getCallObjCalleeFunction() const; 
-    inline void setCallObjCallee(JSObject *callee);
-
-    inline const js::Value &getCallObjArguments() const;
-    inline void setCallObjArguments(const js::Value &v);
-
-    /* Returns the formal argument at the given index. */
-    inline const js::Value &callObjArg(uintN i) const;
-    inline void setCallObjArg(uintN i, const js::Value &v);
-
-    /* Returns the variable at the given index. */
-    inline const js::Value &callObjVar(uintN i) const;
-    inline void setCallObjVar(uintN i, const js::Value &v);
-
-    /*
-     * Get the actual arrays of arguments and variables. Only call if type
-     * inference is enabled, where we ensure that call object variables are in
-     * contiguous slots (see NewCallObject).
-     */
-    inline js::Value *callObjArgArray();
-    inline js::Value *callObjVarArray();
-
     /*
      * Date-specific getters and setters.
      */
 
     static const uint32 JSSLOT_DATE_UTC_TIME = 0;
 
     /*
      * Cached slots holding local properties of the date.
@@ -1921,20 +1876,17 @@ static const uintN RESOLVE_INFER = 0xfff
  * non-global objects without prototype or with prototype that never mutates,
  * see bug 462734 and bug 487039.
  */
 static inline bool
 IsCacheableNonGlobalScope(JSObject *obj)
 {
     JS_ASSERT(obj->getParent());
 
-    js::Class *clasp = obj->getClass();
-    bool cacheable = (clasp == &CallClass ||
-                      clasp == &BlockClass ||
-                      clasp == &DeclEnvClass);
+    bool cacheable = (obj->isCall() || obj->isBlock() || obj->isDeclEnv());
 
     JS_ASSERT_IF(cacheable, !obj->getOps()->lookupProperty);
     return cacheable;
 }
 
 }
 
 /*
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -535,122 +535,16 @@ JSObject::shrinkDenseArrayElements(JSCon
 
 inline bool
 JSObject::denseArrayHasInlineSlots() const
 {
     JS_ASSERT(isDenseArray() && slots);
     return slots == fixedSlots();
 }
 
-inline bool
-JSObject::callIsForEval() const
-{
-    JS_ASSERT(isCall());
-    JS_ASSERT(getFixedSlot(JSSLOT_CALL_CALLEE).isObjectOrNull());
-    JS_ASSERT_IF(getFixedSlot(JSSLOT_CALL_CALLEE).isObject(),
-                 getFixedSlot(JSSLOT_CALL_CALLEE).toObject().isFunction());
-    return getFixedSlot(JSSLOT_CALL_CALLEE).isNull();
-}
-
-inline js::StackFrame *
-JSObject::maybeCallObjStackFrame() const
-{
-    JS_ASSERT(isCall());
-    return reinterpret_cast<js::StackFrame *>(getPrivate());
-}
-
-inline void
-JSObject::setCallObjCallee(JSObject *callee)
-{
-    JS_ASSERT(isCall());
-    JS_ASSERT_IF(callee, callee->isFunction());
-    setFixedSlot(JSSLOT_CALL_CALLEE, js::ObjectOrNullValue(callee));
-}
-
-inline JSObject *
-JSObject::getCallObjCallee() const
-{
-    JS_ASSERT(isCall());
-    return getFixedSlot(JSSLOT_CALL_CALLEE).toObjectOrNull();
-}
-
-inline JSFunction *
-JSObject::getCallObjCalleeFunction() const
-{
-    JS_ASSERT(isCall());
-    return getFixedSlot(JSSLOT_CALL_CALLEE).toObject().getFunctionPrivate();
-}
-
-inline const js::Value &
-JSObject::getCallObjArguments() const
-{
-    JS_ASSERT(isCall());
-    JS_ASSERT(!callIsForEval());
-    return getFixedSlot(JSSLOT_CALL_ARGUMENTS);
-}
-
-inline void
-JSObject::setCallObjArguments(const js::Value &v)
-{
-    JS_ASSERT(isCall());
-    JS_ASSERT(!callIsForEval());
-    setFixedSlot(JSSLOT_CALL_ARGUMENTS, v);
-}
-
-inline const js::Value &
-JSObject::callObjArg(uintN i) const
-{
-    JS_ASSERT(isCall());
-    JS_ASSERT(i < getCallObjCalleeFunction()->nargs);
-    return getSlot(JSObject::CALL_RESERVED_SLOTS + i);
-}
-
-inline void
-JSObject::setCallObjArg(uintN i, const js::Value &v)
-{
-    JS_ASSERT(isCall());
-    JS_ASSERT(i < getCallObjCalleeFunction()->nargs);
-    setSlot(JSObject::CALL_RESERVED_SLOTS + i, v);
-}
-
-inline js::Value *
-JSObject::callObjArgArray()
-{
-    js::DebugOnly<JSFunction*> fun = getCallObjCalleeFunction();
-    JS_ASSERT(hasContiguousSlots(JSObject::CALL_RESERVED_SLOTS, fun->nargs));
-    return getSlotAddress(JSObject::CALL_RESERVED_SLOTS);
-}
-
-inline const js::Value &
-JSObject::callObjVar(uintN i) const
-{
-    JSFunction *fun = getCallObjCalleeFunction();
-    JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
-    JS_ASSERT(i < fun->script()->bindings.countVars());
-    return getSlot(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i);
-}
-
-inline void
-JSObject::setCallObjVar(uintN i, const js::Value &v)
-{
-    JSFunction *fun = getCallObjCalleeFunction();
-    JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
-    JS_ASSERT(i < fun->script()->bindings.countVars());
-    setSlot(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i, v);
-}
-
-inline js::Value *
-JSObject::callObjVarArray()
-{
-    JSFunction *fun = getCallObjCalleeFunction();
-    JS_ASSERT(hasContiguousSlots(JSObject::CALL_RESERVED_SLOTS + fun->nargs,
-                                 fun->script()->bindings.countVars()));
-    return getSlotAddress(JSObject::CALL_RESERVED_SLOTS + fun->nargs);
-}
-
 namespace js {
 
 /*
  * Any name atom for a function which will be added as a DeclEnv object to the
  * scope chain above call objects for fun.
  */
 static inline JSAtom *
 CallObjectLambdaName(JSFunction *fun)
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -115,17 +115,17 @@ Bindings::add(JSContext *cx, JSAtom *nam
      * of the Call objects enumerable. ES5 reformulated all of its Clause 10 to
      * avoid objects as activations, something we should do too.
      */
     uintN attrs = JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED;
 
     uint16 *indexp;
     PropertyOp getter;
     StrictPropertyOp setter;
-    uint32 slot = JSObject::CALL_RESERVED_SLOTS;
+    uint32 slot = CallObject::RESERVED_SLOTS;
 
     if (kind == ARGUMENT) {
         JS_ASSERT(nvars == 0);
         JS_ASSERT(nupvars == 0);
         indexp = &nargs;
         getter = GetCallArg;
         setter = SetCallArg;
         slot += nargs;
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -88,16 +88,17 @@
 #include "jspropertycacheinlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jsscriptinlines.h"
 #include "jscntxtinlines.h"
 #include "jsopcodeinlines.h"
 #include "jstypedarrayinlines.h"
 
+#include "vm/CallObject-inl.h"
 #include "vm/Stack-inl.h"
 
 #ifdef JS_METHODJIT
 #include "methodjit/MethodJIT.h"
 #endif
 
 #include "tracejit/Writer-inl.h"
 
@@ -3281,37 +3282,37 @@ public:
         debug_only_printf(LC_TMTracer, "%s%u=", stackSlotKind(), 0);
         JSObject *frameobj = *(JSObject **)mStack;
         JS_ASSERT((frameobj == NULL) == (*mTypeMap == JSVAL_TYPE_NULL));
         if (p == fp->addressOfArgs()) {
             if (frameobj) {
                 JS_ASSERT_IF(fp->hasArgsObj(), frameobj == &fp->argsObj());
                 fp->setArgsObj(*frameobj->asArguments());
                 if (frameobj->isNormalArguments())
-                    frameobj->setPrivate(fp);
+                    frameobj->asArguments()->setStackFrame(fp);
                 else
-                    JS_ASSERT(!frameobj->getPrivate());
+                    JS_ASSERT(!frameobj->asArguments()->maybeStackFrame());
                 debug_only_printf(LC_TMTracer,
                                   "argsobj<%p> ",
                                   (void *)frameobj);
             } else {
                 JS_ASSERT(!fp->hasArgsObj());
                 debug_only_print0(LC_TMTracer,
                                   "argsobj<null> ");
             }
             /* else, SynthesizeFrame has initialized fp->args.nactual */
         } else {
             JS_ASSERT(p == fp->addressOfScopeChain());
             if (frameobj->isCall() &&
-                !frameobj->getPrivate() &&
-                fp->maybeCalleev().toObjectOrNull() == frameobj->getCallObjCallee())
+                !frameobj->asCall().maybeStackFrame() &&
+                fp->maybeCalleev().toObjectOrNull() == frameobj->asCall().getCallee())
             {
                 JS_ASSERT(&fp->scopeChain() == StackFrame::sInvalidScopeChain);
-                frameobj->setPrivate(fp);
-                fp->setScopeChainWithOwnCallObj(*frameobj);
+                frameobj->asCall().setStackFrame(fp);
+                fp->setScopeChainWithOwnCallObj(frameobj->asCall());
             } else {
                 fp->setScopeChainNoCallObj(*frameobj);
             }
             debug_only_printf(LC_TMTracer,
                               "scopechain<%p> ",
                               (void *)frameobj);
         }
 #ifdef DEBUG
@@ -3534,17 +3535,17 @@ GetFromClosure(JSContext* cx, JSObject* 
             //   is different.
             JS_NOT_REACHED("JSOP_NAME variable found in outer trace");
         }
     }
 #endif
 
     // We already guarded on trace that we aren't touching an outer tree's entry frame
     VOUCH_DOES_NOT_REQUIRE_STACK();
-    StackFrame* fp = (StackFrame*) call->getPrivate();
+    StackFrame* fp = call->asCall().maybeStackFrame();
     JS_ASSERT(fp != cx->fp());
 
     Value v;
     if (fp) {
         v = T::get_slot(fp, cv->slot);
     } else {
         /*
          * Get the value from the object. We know we have a Call object, and
@@ -3572,22 +3573,22 @@ struct ArgClosureTraits
 
     // Get the right object slots to use our slot index with.
     static inline Value get_slot(JSObject* obj, unsigned slot) {
         return obj->getSlot(slot_offset(obj) + slot);
     }
 
     // Get the offset of our object slots from the object's slots pointer.
     static inline uint32 slot_offset(JSObject* obj) {
-        return JSObject::CALL_RESERVED_SLOTS;
+        return CallObject::RESERVED_SLOTS;
     }
 
     // Get the maximum slot index of this type that should be allowed
     static inline uint16 slot_count(JSObject* obj) {
-        return obj->getCallObjCalleeFunction()->nargs;
+        return obj->asCall().getCalleeFunction()->nargs;
     }
 
 private:
     ArgClosureTraits();
 };
 
 uint32 JS_FASTCALL
 GetClosureArg(JSContext* cx, JSObject* callee, const ClosureVarInfo* cv, double* result)
@@ -3603,22 +3604,22 @@ struct VarClosureTraits
         return fp->slots()[slot];
     }
 
     static inline Value get_slot(JSObject* obj, unsigned slot) {
         return obj->getSlot(slot_offset(obj) + slot);
     }
 
     static inline uint32 slot_offset(JSObject* obj) {
-        return JSObject::CALL_RESERVED_SLOTS +
-               obj->getCallObjCalleeFunction()->nargs;
+        return CallObject::RESERVED_SLOTS +
+               obj->asCall().getCalleeFunction()->nargs;
     }
 
     static inline uint16 slot_count(JSObject* obj) {
-        return obj->getCallObjCalleeFunction()->script()->bindings.countVars();
+        return obj->asCall().getCalleeFunction()->script()->bindings.countVars();
     }
 
 private:
     VarClosureTraits();
 };
 
 uint32 JS_FASTCALL
 GetClosureVar(JSContext* cx, JSObject* callee, const ClosureVarInfo* cv, double* result)
@@ -8144,19 +8145,20 @@ TraceRecorder::entryFrameIns() const
 }
 
 /*
  * Return the frame of a call object if that frame is part of the current
  * trace. |depthp| is an optional outparam: if it is non-null, it will be
  * filled in with the depth of the call object's frame relevant to cx->fp().
  */
 JS_REQUIRES_STACK StackFrame*
-TraceRecorder::frameIfInRange(JSObject* obj, unsigned* depthp) const
-{
-    StackFrame* ofp = (StackFrame*) obj->getPrivate();
+TraceRecorder::frameIfInRange(JSObject *obj, unsigned* depthp) const
+{
+    JS_ASSERT(obj->isCall() || obj->isArguments());
+    StackFrame* ofp = (StackFrame *) obj->getPrivate();
     StackFrame* fp = cx->fp();
     for (unsigned depth = 0; depth <= callDepth; ++depth) {
         if (fp == ofp) {
             if (depthp)
                 *depthp = depth;
             return ofp;
         }
         if (!(fp = fp->prev()))
@@ -8259,34 +8261,35 @@ TraceRecorder::callProp(JSObject* obj, J
     JSOp op = JSOp(*cx->regs().pc);
     uint32 setflags = (js_CodeSpec[op].format & (JOF_SET | JOF_INCDEC | JOF_FOR));
     if (setflags && !shape->writable())
         RETURN_STOP("writing to a read-only property");
 
     uintN slot = uint16(shape->shortid);
 
     vp = NULL;
-    StackFrame* cfp = (StackFrame*) obj->getPrivate();
+    CallObject &callobj = obj->asCall();
+    StackFrame* cfp = callobj.maybeStackFrame();
     if (cfp) {
         if (shape->getterOp() == GetCallArg) {
             JS_ASSERT(slot < cfp->numFormalArgs());
             vp = &cfp->formalArg(slot);
             nr.v = *vp;
         } else if (shape->getterOp() == GetCallVar) {
             JS_ASSERT(slot < cfp->numSlots());
             vp = &cfp->slots()[slot];
             nr.v = *vp;
         } else {
             RETURN_STOP("dynamic property of Call object");
         }
 
         // Now assert that our use of shape->shortid was in fact kosher.
         JS_ASSERT(shape->hasShortID());
 
-        if (frameIfInRange(obj)) {
+        if (frameIfInRange(&callobj)) {
             // At this point we are guaranteed to be looking at an active call oject
             // whose properties are stored in the corresponding StackFrame.
             ins = get(vp);
             nr.tracked = true;
             return RECORD_CONTINUE;
         }
     } else {
         // Call objects do not yet have shape->isMethod() properties, but they
@@ -12294,21 +12297,23 @@ TraceRecorder::setUpwardTrackedVar(Value
     }
 
     set(stackVp, v_ins, promote);
 
     return RECORD_CONTINUE;
 }
 
 JS_REQUIRES_STACK RecordingStatus
-TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, const Shape *shape,
+TraceRecorder::setCallProp(JSObject *obj, LIns *callobj_ins, const Shape *shape,
                            LIns *v_ins, const Value &v)
 {
+    CallObject &callobj = obj->asCall();
+
     // Set variables in on-trace-stack call objects by updating the tracker.
-    StackFrame *fp = frameIfInRange(callobj);
+    StackFrame *fp = frameIfInRange(&callobj);
     if (fp) {
         if (shape->setterOp() == SetCallArg) {
             JS_ASSERT(shape->hasShortID());
             uintN slot = uint16(shape->shortid);
             Value *vp2 = &fp->formalArg(slot);
             CHECK_STATUS(setUpwardTrackedVar(vp2, v, v_ins));
             return RECORD_CONTINUE;
         }
@@ -12317,40 +12322,40 @@ TraceRecorder::setCallProp(JSObject *cal
             uintN slot = uint16(shape->shortid);
             Value *vp2 = &fp->slots()[slot];
             CHECK_STATUS(setUpwardTrackedVar(vp2, v, v_ins));
             return RECORD_CONTINUE;
         }
         RETURN_STOP("can't trace special CallClass setter");
     }
 
-    if (!callobj->getPrivate()) {
+    if (!callobj.maybeStackFrame()) {
         // Because the parent guard in guardCallee ensures this Call object
         // will be the same object now and on trace, and because once a Call
         // object loses its frame it never regains one, on trace we will also
         // have a null private in the Call object. So all we need to do is
         // write the value to the Call object's slot.
         intN slot = uint16(shape->shortid);
         if (shape->setterOp() == SetCallArg) {
-            JS_ASSERT(slot < ArgClosureTraits::slot_count(callobj));
-            slot += ArgClosureTraits::slot_offset(callobj);
+            JS_ASSERT(slot < ArgClosureTraits::slot_count(&callobj));
+            slot += ArgClosureTraits::slot_offset(obj);
         } else if (shape->setterOp() == SetCallVar) {
-            JS_ASSERT(slot < VarClosureTraits::slot_count(callobj));
-            slot += VarClosureTraits::slot_offset(callobj);
+            JS_ASSERT(slot < VarClosureTraits::slot_count(&callobj));
+            slot += VarClosureTraits::slot_offset(obj);
         } else {
             RETURN_STOP("can't trace special CallClass setter");
         }
 
         // Now assert that the shortid get we did above was ok. Have to do it
         // after the RETURN_STOP above, since in that case we may in fact not
         // have a valid shortid; but we don't use it in that case anyway.
         JS_ASSERT(shape->hasShortID());
 
         LIns* slots_ins = NULL;
-        stobj_set_slot(callobj, callobj_ins, slot, slots_ins, v, v_ins);
+        stobj_set_slot(&callobj, callobj_ins, slot, slots_ins, v, v_ins);
         return RECORD_CONTINUE;
     }
 
     // This is the hard case: we have a StackFrame private, but it's not in
     // range.  During trace execution we may or may not have a StackFrame
     // anymore.  Call the standard builtins, which handle that situation.
 
     // Set variables in off-trace-stack call objects by calling standard builtins.
@@ -14992,17 +14997,17 @@ TraceRecorder::record_JSOP_POPN()
 {
     return ARECORD_CONTINUE;
 }
 
 static inline bool
 IsFindableCallObj(JSObject *obj)
 {
     return obj->isCall() &&
-           (obj->callIsForEval() || obj->getCallObjCalleeFunction()->isHeavyweight());
+           (obj->asCall().isForEval() || obj->asCall().getCalleeFunction()->isHeavyweight());
 }
 
 /*
  * Generate LIR to reach |obj2| from |obj| by traversing the scope chain. The
  * generated code also ensures that any call objects found have not changed shape.
  *
  *      obj               starting object
  *      obj_ins           LIR instruction representing obj
@@ -15129,18 +15134,17 @@ TraceRecorder::record_JSOP_BINDNAME()
             JS_ASSERT(obj);
         }
 
         /*
          * If this is a strict mode eval frame, we will have a Call object for
          * it. For now just don't trace this case.
          */
         if (obj != globalObj) {
-            JS_ASSERT(obj->isCall());
-            JS_ASSERT(obj->callIsForEval());
+            JS_ASSERT(obj->asCall().isForEval());
             RETURN_STOP_A("BINDNAME within strict eval code");
         }
 
         /*
          * The trace is specialized to this global object. Furthermore, we know
          * it is the sole 'global' object on the scope chain: we set globalObj
          * to the scope chain element with no parent, and we reached it
          * starting from the function closure or the current scopeChain, so
--- a/js/src/jstracer.h
+++ b/js/src/jstracer.h
@@ -1267,17 +1267,17 @@ class TraceRecorder
         JSObject         *obj;           // Call object where name was found
         nanojit::LIns    *obj_ins;       // LIR value for obj
         js::Shape        *shape;         // shape name was resolved to
     };
 
     JS_REQUIRES_STACK nanojit::LIns* scopeChain();
     JS_REQUIRES_STACK nanojit::LIns* entryScopeChain() const;
     JS_REQUIRES_STACK nanojit::LIns* entryFrameIns() const;
-    JS_REQUIRES_STACK StackFrame* frameIfInRange(JSObject* obj, unsigned* depthp = NULL) const;
+    JS_REQUIRES_STACK StackFrame* frameIfInRange(JSObject *obj, unsigned* depthp = NULL) const;
     JS_REQUIRES_STACK RecordingStatus traverseScopeChain(JSObject *obj, nanojit::LIns *obj_ins, JSObject *obj2, nanojit::LIns *&obj2_ins);
     JS_REQUIRES_STACK AbortableRecordingStatus scopeChainProp(JSObject* obj, const Value*& vp, nanojit::LIns*& ins, NameResult& nr, JSObject **scopeObjp = NULL);
     JS_REQUIRES_STACK RecordingStatus callProp(JSObject* obj, JSProperty* shape, jsid id, const Value*& vp, nanojit::LIns*& ins, NameResult& nr);
 
     JS_REQUIRES_STACK nanojit::LIns* arg(unsigned n);
     JS_REQUIRES_STACK void arg(unsigned n, nanojit::LIns* i);
     JS_REQUIRES_STACK nanojit::LIns* var(unsigned n);
     JS_REQUIRES_STACK void var(unsigned n, nanojit::LIns* i);
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -49,16 +49,18 @@
 #include "jsatominlines.h"
 #include "jsobjinlines.h"
 #include "jsscopeinlines.h"
 #include "jspropertycache.h"
 #include "jspropertycacheinlines.h"
 #include "jsinterpinlines.h"
 #include "jsautooplen.h"
 
+#include "vm/CallObject-inl.h"
+
 #if defined JS_POLYIC
 
 using namespace js;
 using namespace js::mjit;
 using namespace js::mjit::ic;
 
 typedef JSC::FunctionPtr FunctionPtr;
 typedef JSC::MacroAssembler::RegisterID RegisterID;
@@ -416,17 +418,17 @@ class SetPropCompiler : public PICStubCo
         } else {
             //   \ /        In general, two function objects with different JSFunctions
             //    #         can have the same shape, thus we must not rely on the identity
             // >--+--<      of 'fun' remaining the same. However, since:
             //   |||         1. the shape includes all arguments and locals and their setters
             //    \\     V     and getters, and
             //      \===/    2. arguments and locals have different getters
             //              then we can rely on fun->nargs remaining invariant.
-            JSFunction *fun = obj->getCallObjCalleeFunction();
+            JSFunction *fun = obj->asCall().getCalleeFunction();
             uint16 slot = uint16(shape->shortid);
 
             /* Guard that the call object has a frame. */
             masm.loadObjPrivate(pic.objReg, pic.shapeReg);
             Jump escapedFrame = masm.branchTestPtr(Assembler::Zero, pic.shapeReg, pic.shapeReg);
 
             {
                 Address addr(pic.shapeReg, shape->setterOp() == SetCallArg
@@ -436,17 +438,17 @@ class SetPropCompiler : public PICStubCo
                 skipOver = masm.jump();
             }
 
             escapedFrame.linkTo(masm.label(), &masm);
             {
                 if (shape->setterOp() == SetCallVar)
                     slot += fun->nargs;
 
-                slot += JSObject::CALL_RESERVED_SLOTS;
+                slot += CallObject::RESERVED_SLOTS;
                 Address address = masm.objPropAddress(obj, pic.objReg, slot);
 
                 masm.storeValue(pic.u.vr, address);
             }
 
             pic.shapeRegHasBaseShape = false;
         }
 
@@ -686,17 +688,17 @@ class SetPropCompiler : public PICStubCo
                  * shape guards we have performed do not by themselves
                  * guarantee that future call objects hit will be for the same
                  * script. We also depend on the fact that the scope chains hit
                  * at the same bytecode are all isomorphic: the same scripts,
                  * in the same order (though the properties on their call
                  * objects may differ due to eval(), DEFFUN, etc.).
                  */
                 RecompilationMonitor monitor(cx);
-                JSFunction *fun = obj->getCallObjCalleeFunction();
+                JSFunction *fun = obj->asCall().getCalleeFunction();
                 JSScript *script = fun->script();
                 uint16 slot = uint16(shape->shortid);
                 if (!script->ensureHasTypes(cx, fun))
                     return error();
                 {
                     types::AutoEnterTypeInference enter(cx);
                     if (shape->setterOp() == SetCallArg)
                         pic.rhsTypes->addSubset(cx, types::TypeScript::ArgTypes(script, slot));
@@ -1499,17 +1501,17 @@ class ScopeNameCompiler : public PICStub
             Value *thisVp = &cx->regs().sp[1];
             Address thisSlot(JSFrameReg, StackFrame::offsetOfFixed(thisVp - cx->fp()->slots()));
             masm.storeValue(UndefinedValue(), thisSlot);
         }
 
         /* Get callobj's stack frame. */
         masm.loadObjPrivate(pic.objReg, pic.shapeReg);
 
-        JSFunction *fun = getprop.holder->getCallObjCalleeFunction();
+        JSFunction *fun = getprop.holder->asCall().getCalleeFunction();
         uint16 slot = uint16(shape->shortid);
 
         Jump skipOver;
         Jump escapedFrame = masm.branchTestPtr(Assembler::Zero, pic.shapeReg, pic.shapeReg);
 
         /* Not-escaped case. */
         {
             Address addr(pic.shapeReg, kind == ARG ? StackFrame::offsetOfFormalArg(fun, slot)
@@ -1520,17 +1522,17 @@ class ScopeNameCompiler : public PICStub
         }
 
         escapedFrame.linkTo(masm.label(), &masm);
 
         {
             if (kind == VAR)
                 slot += fun->nargs;
 
-            slot += JSObject::CALL_RESERVED_SLOTS;
+            slot += CallObject::RESERVED_SLOTS;
             Address address = masm.objPropAddress(obj, pic.objReg, slot);
 
             /* Safe because type is loaded first. */
             masm.loadValueAsComponents(address, pic.shapeReg, pic.objReg);
         }
 
         skipOver.linkTo(masm.label(), &masm);
         Jump done = masm.jump();
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -477,17 +477,17 @@ stubs::GetElem(VMFrame &f)
             }
         } else if (obj->isArguments()) {
             uint32 arg = uint32(i);
             ArgumentsObject *argsobj = obj->asArguments();
 
             if (arg < argsobj->initialLength()) {
                 copyFrom = &argsobj->element(arg);
                 if (!copyFrom->isMagic()) {
-                    if (StackFrame *afp = (StackFrame *) argsobj->getPrivate())
+                    if (StackFrame *afp = argsobj->maybeStackFrame())
                         copyFrom = &afp->canonicalActualArg(arg);
                     goto end_getelem;
                 }
             }
         }
         if (JS_LIKELY(INT_FITS_IN_JSID(i)))
             id = INT_TO_JSID(i);
         else
--- a/js/src/vm/ArgumentsObject-inl.h
+++ b/js/src/vm/ArgumentsObject-inl.h
@@ -105,16 +105,47 @@ ArgumentsObject::elements() const
 
 inline void
 ArgumentsObject::setElement(uint32 i, const js::Value &v)
 {
     JS_ASSERT(i < initialLength());
     data()->slots[i] = v;
 }
 
+inline js::StackFrame *
+ArgumentsObject::maybeStackFrame() const
+{
+    return reinterpret_cast<js::StackFrame *>(getPrivate());
+}
+
+inline void
+ArgumentsObject::setStackFrame(StackFrame *frame)
+{
+    return setPrivate(frame);
+}
+
+#define JS_ARGUMENTS_OBJECT_ON_TRACE ((void *)0xa126)
+inline bool
+ArgumentsObject::onTrace() const
+{
+    return getPrivate() == JS_ARGUMENTS_OBJECT_ON_TRACE;
+}
+
+inline void
+ArgumentsObject::setOnTrace()
+{
+    return setPrivate(JS_ARGUMENTS_OBJECT_ON_TRACE);
+}
+
+inline void
+ArgumentsObject::clearOnTrace()
+{
+    return setPrivate(NULL);
+}
+
 inline const js::Value &
 NormalArgumentsObject::callee() const
 {
     return data()->callee;
 }
 
 inline void
 NormalArgumentsObject::clearCallee()
--- a/js/src/vm/ArgumentsObject.h
+++ b/js/src/vm/ArgumentsObject.h
@@ -42,18 +42,16 @@
 #define ArgumentsObject_h___
 
 #include "jsfun.h"
 
 #ifdef JS_POLYIC
 class GetPropCompiler;
 #endif
 
-#define JS_ARGUMENTS_OBJECT_ON_TRACE ((void *)0xa126)
-
 #ifdef JS_TRACER
 namespace nanojit {
 class ValidateWriter;
 }
 #endif
 
 namespace js {
 
@@ -224,16 +222,24 @@ class ArgumentsObject : public ::JSObjec
      */
     inline bool getElements(uint32 start, uint32 count, js::Value *vp);
 
     inline js::ArgumentsData *data() const;
 
     inline const js::Value &element(uint32 i) const;
     inline const js::Value *elements() const;
     inline void setElement(uint32 i, const js::Value &v);
+
+    /* The stack frame for this ArgumentsObject, if the frame is still active. */
+    inline js::StackFrame *maybeStackFrame() const;
+    inline void setStackFrame(js::StackFrame *frame);
+
+    inline bool onTrace() const;
+    inline void setOnTrace();
+    inline void clearOnTrace();
 };
 
 /*
  * Non-strict arguments have a private: the function's stack frame until the
  * function returns, when it is replaced with null.  When an arguments object
  * is created on-trace its private is JS_ARGUMENTS_OBJECT_ON_TRACE, and when
  * the trace exits its private is replaced with the stack frame or null, as
  * appropriate.
new file mode 100644
--- /dev/null
+++ b/js/src/vm/CallObject-inl.h
@@ -0,0 +1,161 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is SpiderMonkey call object 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):
+ *   Paul Biggar <pbiggar@mozilla.com> (original author)
+ *
+ * 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 CallObject_inl_h___
+#define CallObject_inl_h___
+
+#include "CallObject.h"
+
+namespace js {
+
+inline bool
+CallObject::isForEval() const
+{
+    JS_ASSERT(getFixedSlot(CALLEE_SLOT).isObjectOrNull());
+    JS_ASSERT_IF(getFixedSlot(CALLEE_SLOT).isObject(),
+                 getFixedSlot(CALLEE_SLOT).toObject().isFunction());
+    return getFixedSlot(CALLEE_SLOT).isNull();
+}
+
+inline js::StackFrame *
+CallObject::maybeStackFrame() const
+{
+    return reinterpret_cast<js::StackFrame *>(getPrivate());
+}
+
+inline void
+CallObject::setStackFrame(StackFrame *frame)
+{
+    return setPrivate(frame);
+}
+
+inline void
+CallObject::setCallee(JSObject *callee)
+{
+    JS_ASSERT_IF(callee, callee->isFunction());
+    setFixedSlot(CALLEE_SLOT, js::ObjectOrNullValue(callee));
+}
+
+inline JSObject *
+CallObject::getCallee() const
+{
+    return getFixedSlot(CALLEE_SLOT).toObjectOrNull();
+}
+
+inline JSFunction *
+CallObject::getCalleeFunction() const
+{
+    return getFixedSlot(CALLEE_SLOT).toObject().getFunctionPrivate();
+}
+
+inline const js::Value &
+CallObject::getArguments() const
+{
+    JS_ASSERT(!isForEval());
+    return getFixedSlot(ARGUMENTS_SLOT);
+}
+
+inline void
+CallObject::setArguments(const js::Value &v)
+{
+    JS_ASSERT(!isForEval());
+    setFixedSlot(ARGUMENTS_SLOT, v);
+}
+
+inline const js::Value &
+CallObject::arg(uintN i) const
+{
+    JS_ASSERT(i < getCalleeFunction()->nargs);
+    return getSlot(RESERVED_SLOTS + i);
+}
+
+inline void
+CallObject::setArg(uintN i, const js::Value &v)
+{
+    JS_ASSERT(i < getCalleeFunction()->nargs);
+    setSlot(RESERVED_SLOTS + i, v);
+}
+
+inline const js::Value &
+CallObject::var(uintN i) const
+{
+    JSFunction *fun = getCalleeFunction();
+    JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
+    JS_ASSERT(i < fun->script()->bindings.countVars());
+    return getSlot(RESERVED_SLOTS + fun->nargs + i);
+}
+
+inline void
+CallObject::setVar(uintN i, const js::Value &v)
+{
+    JSFunction *fun = getCalleeFunction();
+    JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
+    JS_ASSERT(i < fun->script()->bindings.countVars());
+    setSlot(RESERVED_SLOTS + fun->nargs + i, v);
+}
+
+inline void
+CallObject::copyValues(uintN nargs, Value *argv, uintN nvars, Value *slots)
+{
+    JS_ASSERT(numSlots() >= RESERVED_SLOTS + nargs + nvars);
+    copySlotRange(RESERVED_SLOTS, argv, nargs);
+    copySlotRange(RESERVED_SLOTS + nargs, slots, nvars);
+}
+
+inline js::Value *
+CallObject::argArray()
+{
+    js::DebugOnly<JSFunction*> fun = getCalleeFunction();
+    JS_ASSERT(hasContiguousSlots(RESERVED_SLOTS, fun->nargs));
+    return getSlotAddress(RESERVED_SLOTS);
+}
+
+inline js::Value *
+CallObject::varArray()
+{
+    JSFunction *fun = getCalleeFunction();
+    JS_ASSERT(hasContiguousSlots(RESERVED_SLOTS + fun->nargs,
+                                 fun->script()->bindings.countVars()));
+    return getSlotAddress(RESERVED_SLOTS + fun->nargs);
+}
+
+}
+
+#endif /* CallObject_inl_h___ */
new file mode 100644
--- /dev/null
+++ b/js/src/vm/CallObject.cpp
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is SpiderMonkey call object 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):
+ *   Paul Biggar <pbiggar@mozilla.com> (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "jsobjinlines.h"
+#include "CallObject.h"
+
+#include "CallObject-inl.h"
+
+namespace js {
+
+/*
+ * Construct a call object for the given bindings.  If this is a call object
+ * for a function invocation, callee should be the function being called.
+ * Otherwise it must be a call object for eval of strict mode code, and callee
+ * must be null.
+ */
+CallObject *
+CallObject::create(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee)
+{
+    Bindings &bindings = script->bindings;
+    size_t argsVars = bindings.countArgsAndVars();
+    size_t slots = RESERVED_SLOTS + argsVars;
+    gc::AllocKind kind = gc::GetGCObjectKind(slots);
+
+    /*
+     * Make sure that the arguments and variables in the call object all end up
+     * in a contiguous range of slots. We need this to be able to embed the
+     * args/vars arrays in the TypeScriptNesting for the function, after the
+     * call object's frame has finished.
+     */
+    if (cx->typeInferenceEnabled() && gc::GetGCKindSlots(kind) < slots) {
+        kind = gc::GetGCObjectKind(RESERVED_SLOTS);
+        JS_ASSERT(gc::GetGCKindSlots(kind) == RESERVED_SLOTS);
+    }
+
+    JSObject *obj = js_NewGCObject(cx, kind);
+    if (!obj)
+        return NULL;
+
+    /* Init immediately to avoid GC seeing a half-init'ed object. */
+    obj->initCall(cx, bindings, &scopeChain);
+    obj->makeVarObj();
+
+    /* This must come after callobj->lastProp has been set. */
+    if (!obj->ensureInstanceReservedSlots(cx, argsVars))
+        return NULL;
+
+#ifdef DEBUG
+    for (Shape::Range r = obj->lastProp; !r.empty(); r.popFront()) {
+        const Shape &s = r.front();
+        if (s.slot != SHAPE_INVALID_SLOT) {
+            JS_ASSERT(s.slot + 1 == obj->slotSpan());
+            break;
+        }
+    }
+#endif
+
+    CallObject &callobj = obj->asCall();
+    callobj.setCallee(callee);
+    return &callobj;
+}
+
+}
new file mode 100644
--- /dev/null
+++ b/js/src/vm/CallObject.h
@@ -0,0 +1,116 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=78:
+ *
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is SpiderMonkey call object 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):
+ *   Paul Biggar <pbiggar@mozilla.com> (original author)
+ *
+ * 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 CallObject_h___
+#define CallObject_h___
+
+namespace js {
+
+class CallObject : public ::JSObject
+{
+    /*
+     * Reserved slot structure for Call objects:
+     *
+     * private               - the stack frame corresponding to the Call object
+     *                         until js_PutCallObject or its on-trace analog
+     *                         is called, null thereafter
+     * JSSLOT_CALL_CALLEE    - callee function for the stack frame, or null if
+     *                         the stack frame is for strict mode eval code
+     * JSSLOT_CALL_ARGUMENTS - arguments object for non-strict mode eval stack
+     *                         frames (not valid for strict mode eval frames)
+     */
+    static const uint32 CALLEE_SLOT = 0;
+    static const uint32 ARGUMENTS_SLOT = 1;
+
+public:
+    /* Create a CallObject for the given callee function. */
+    static CallObject *
+    create(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee);
+
+    static const uint32 RESERVED_SLOTS = 2;
+
+    /* True if this is for a strict mode eval frame or for a function call. */
+    inline bool isForEval() const;
+
+    /* The stack frame for this CallObject, if the frame is still active. */
+    inline js::StackFrame *maybeStackFrame() const;
+    inline void setStackFrame(js::StackFrame *frame);
+
+    /*
+     * The callee function if this CallObject was created for a function
+     * invocation, or null if it was created for a strict mode eval frame.
+     */
+    inline JSObject *getCallee() const;
+    inline JSFunction *getCalleeFunction() const; 
+    inline void setCallee(JSObject *callee);
+
+    /* Returns the callee's arguments object. */
+    inline const js::Value &getArguments() const;
+    inline void setArguments(const js::Value &v);
+
+    /* Returns the formal argument at the given index. */
+    inline const js::Value &arg(uintN i) const;
+    inline void setArg(uintN i, const js::Value &v);
+
+    /* Returns the variable at the given index. */
+    inline const js::Value &var(uintN i) const;
+    inline void setVar(uintN i, const js::Value &v);
+
+    /*
+     * Get the actual arrays of arguments and variables. Only call if type
+     * inference is enabled, where we ensure that call object variables are in
+     * contiguous slots (see NewCallObject).
+     */
+    inline js::Value *argArray();
+    inline js::Value *varArray();
+
+    inline void copyValues(uintN nargs, Value *argv, uintN nvars, Value *slots);
+};
+
+}
+
+js::CallObject &
+JSObject::asCall()
+{
+    JS_ASSERT(isCall());
+    return *reinterpret_cast<js::CallObject *>(this);
+}
+
+#endif /* CallObject_h___ */
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -43,16 +43,18 @@
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "Stack.h"
 
 #include "jsscriptinlines.h"
 #include "ArgumentsObject-inl.h"
+#include "CallObject-inl.h"
+
 #include "methodjit/MethodJIT.h"
 
 namespace js {
 
 inline void
 StackFrame::initPrev(JSContext *cx)
 {
     JS_ASSERT(flags_ & HAS_PREVPC);
@@ -335,35 +337,35 @@ StackFrame::setScopeChainNoCallObj(JSObj
         }
     }
 #endif
     scopeChain_ = &obj;
     flags_ |= HAS_SCOPECHAIN;
 }
 
 inline void
-StackFrame::setScopeChainWithOwnCallObj(JSObject &obj)
+StackFrame::setScopeChainWithOwnCallObj(CallObject &obj)
 {
     JS_ASSERT(&obj != NULL);
-    JS_ASSERT(!hasCallObj() && obj.isCall() && obj.getPrivate() == this);
+    JS_ASSERT(!hasCallObj() && obj.maybeStackFrame() == this);
     scopeChain_ = &obj;
     flags_ |= HAS_SCOPECHAIN | HAS_CALL_OBJ;
 }
 
-inline JSObject &
+inline CallObject &
 StackFrame::callObj() const
 {
     JS_ASSERT_IF(isNonEvalFunctionFrame() || isStrictEvalFrame(), hasCallObj());
 
     JSObject *pobj = &scopeChain();
     while (JS_UNLIKELY(!pobj->isCall())) {
         JS_ASSERT(IsCacheableNonGlobalScope(pobj) || pobj->isWith());
         pobj = pobj->getParent();
     }
-    return *pobj;
+    return pobj->asCall();
 }
 
 inline bool
 StackFrame::maintainNestingState() const
 {
     /*
      * Whether to invoke the nesting epilogue/prologue to maintain active
      * frame counts and check for reentrant outer functions.
@@ -410,21 +412,21 @@ StackFrame::functionEpilogue(bool object
     if (!objectsOnly && maintainNestingState())
         types::NestingEpilogue(this);
 }
 
 inline void
 StackFrame::markFunctionEpilogueDone(bool activationOnly)
 {
     if (flags_ & (HAS_ARGS_OBJ | HAS_CALL_OBJ)) {
-        if (hasArgsObj() && !argsObj().getPrivate()) {
+        if (hasArgsObj() && !argsObj().maybeStackFrame()) {
             args.nactual = args.obj->initialLength();
             flags_ &= ~HAS_ARGS_OBJ;
         }
-        if (hasCallObj() && !callObj().getPrivate()) {
+        if (hasCallObj() && !callObj().maybeStackFrame()) {
             /*
              * For function frames, the call object may or may not have have an
              * enclosing DeclEnv object, so we use the callee's parent, since
              * it was the initial scope chain. For global (strict) eval frames,
              * there is no calle, but the call object's parent is the initial
              * scope chain.
              */
             scopeChain_ = isFunctionFrame()
@@ -682,57 +684,57 @@ ArgumentsObject::getElement(uint32 i, Va
      */
     if (vp->isMagic(JS_ARGS_HOLE))
         return false;
 
     /*
      * If this arguments object was created on trace the actual argument value
      * could be in a register or something, so we can't optimize.
      */
-    StackFrame *fp = reinterpret_cast<StackFrame *>(getPrivate());
-    if (fp == JS_ARGUMENTS_OBJECT_ON_TRACE)
+    if (onTrace())
         return false;
 
     /*
      * If this arguments object has an associated stack frame, that contains
      * the canonical argument value.  Note that strict arguments objects do not
      * alias named arguments and never have a stack frame.
      */
+    StackFrame *fp = maybeStackFrame();
     JS_ASSERT_IF(isStrictArguments(), !fp);
     if (fp)
         *vp = fp->canonicalActualArg(i);
     return true;
 }
 
 inline bool
 ArgumentsObject::getElements(uint32 start, uint32 count, Value *vp)
 {
     JS_ASSERT(start + count >= start);
 
     uint32 length = initialLength();
     if (start > length || start + count > length)
         return false;
 
-    StackFrame *fp = reinterpret_cast<StackFrame *>(getPrivate());
+    StackFrame *fp = maybeStackFrame();
 
     /* If there's no stack frame for this, argument values are in elements(). */
     if (!fp) {
         const Value *srcbeg = elements() + start;
         const Value *srcend = srcbeg + count;
         const Value *src = srcbeg;
         for (Value *dst = vp; src < srcend; ++dst, ++src) {
             if (src->isMagic(JS_ARGS_HOLE))
                 return false;
             *dst = *src;
         }
         return true;
     }
 
     /* If we're on trace, there's no canonical location for elements: fail. */
-    if (fp == JS_ARGUMENTS_OBJECT_ON_TRACE)
+    if (onTrace())
         return false;
 
     /* Otherwise, element values are on the stack. */
     JS_ASSERT(fp->numActualArgs() <= StackSpace::ARGS_LENGTH_MAX);
     return fp->forEachCanonicalActualArg(detail::CopyNonHoleArgsTo(this, vp), start, count);
 }
 
 } /* namespace js */
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -154,19 +154,19 @@ StackFrame::stealFrameAndSlots(Value *vp
             JSObject *env = obj.getParent();
             JS_ASSERT(env->isDeclEnv());
             env->setPrivate(this);
         }
     }
     if (hasArgsObj()) {
         ArgumentsObject &argsobj = argsObj();
         if (argsobj.isNormalArguments())
-            argsobj.setPrivate(this);
+            argsobj.setStackFrame(this);
         else
-            JS_ASSERT(!argsobj.getPrivate());
+            JS_ASSERT(!argsobj.maybeStackFrame());
         otherfp->flags_ &= ~HAS_ARGS_OBJ;
     }
 }
 
 #ifdef DEBUG
 JSObject *const StackFrame::sInvalidScopeChain = (JSObject *)0xbeef;
 #endif
 
--- a/js/src/vm/Stack.h
+++ b/js/src/vm/Stack.h
@@ -846,19 +846,19 @@ class StackFrame
     }
 
     bool hasCallObj() const {
         bool ret = !!(flags_ & HAS_CALL_OBJ);
         JS_ASSERT_IF(ret, !isNonStrictEvalFrame());
         return ret;
     }
 
-    inline JSObject &callObj() const;
+    inline CallObject &callObj() const;
     inline void setScopeChainNoCallObj(JSObject &obj);
-    inline void setScopeChainWithOwnCallObj(JSObject &obj);
+    inline void setScopeChainWithOwnCallObj(CallObject &obj);
 
     /*
      * Prologue for function frames: make a call object for heavyweight
      * functions, and maintain type nesting invariants.
      */
     inline bool functionPrologue(JSContext *cx);
 
     /*
--- a/mobile/chrome/content/AnimatedZoom.js
+++ b/mobile/chrome/content/AnimatedZoom.js
@@ -71,32 +71,32 @@ const AnimatedZoom = {
       event.initEvent("AnimatedZoomBegin", true, true);
       window.dispatchEvent(event);
     }
   },
 
   start: function start() {
     this.tab = Browser.selectedTab;
     this.browser = this.tab.browser;
+    this.bcr = this.browser.getBoundingClientRect();
     this.zoomFrom = this.zoomRect || this.getStartRect();
     this.startScale = this.browser.scale;
     this.beginTime = mozAnimationStartTime;
   },
 
   /** Get the visible rect, in device pixels relative to the content origin. */
   getStartRect: function getStartRect() {
     let browser = this.browser;
-    let bcr = browser.getBoundingClientRect();
     let scroll = browser.getRootView().getPosition();
-    return new Rect(scroll.x, scroll.y, bcr.width, bcr.height);
+    return new Rect(scroll.x, scroll.y, this.bcr.width, this.bcr.height);
   },
 
   /** Update the visible rect, in device pixels relative to the content origin. */
   updateTo: function(nextRect) {
-    let zoomRatio = window.innerWidth / nextRect.width;
+    let zoomRatio = this.bcr.width / nextRect.width;
     let scale = this.startScale * zoomRatio;
     let scrollX = nextRect.left * zoomRatio;
     let scrollY = nextRect.top * zoomRatio;
 
     this.browser.fuzzyZoom(scale, scrollX, scrollY);
 
     this.zoomRect = nextRect;
   },
--- a/mobile/chrome/content/Util.js
+++ b/mobile/chrome/content/Util.js
@@ -156,25 +156,39 @@ let Util = {
     Services.io.offline = false;
   },
 
   isParentProcess: function isInParentProcess() {
     let appInfo = Cc["@mozilla.org/xre/app-info;1"];
     return (!appInfo || appInfo.getService(Ci.nsIXULRuntime).processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT);
   },
 
-  isTablet: function isTablet() {
+  isTablet: function isTablet(options) {
+    let forceUpdate = options && 'forceUpdate' in options && options.forceUpdate;
+
+    if ('_isTablet' in this && !forceUpdate)
+      return this._isTablet;
+
+    let tabletPref = Services.prefs.getIntPref("browser.ui.layout.tablet");
+
+    // Act according to user prefs if tablet mode has been
+    // explicitly disabled or enabled.
+    if (tabletPref == 0)
+      return this._isTablet = false;
+    else if (tabletPref == 1)
+      return this._isTablet = true;
+
     let dpi = this.displayDPI;
     if (dpi <= 96)
-      return (window.innerWidth > 1024);
+      return this._isTablet = (window.innerWidth > 1024);
 
     // See the tablet_panel_minwidth from mobile/themes/core/defines.inc
     let tablet_panel_minwidth = 124;
     let dpmm = 25.4 * window.innerWidth / dpi;
-    return (dpmm >= tablet_panel_minwidth);
+    return this._isTablet = (dpmm >= tablet_panel_minwidth);
   },
 
   isPortrait: function isPortrait() {
 #ifdef MOZ_PLATFORM_MAEMO
     return (screen.width <= screen.height);
 #elifdef ANDROID
     return (screen.width <= screen.height);
 #else
--- a/mobile/chrome/content/browser-ui.js
+++ b/mobile/chrome/content/browser-ui.js
@@ -181,16 +181,18 @@ var BrowserUI = {
 
   _toolbarLocked: 0,
 
   isToolbarLocked: function isToolbarLocked() {
     return this._toolbarLocked;
   },
 
   lockToolbar: function lockToolbar() {
+    if (Util.isTablet())
+      return;
     this._toolbarLocked++;
     document.getElementById("toolbar-moveable-container").top = "0";
     if (this._toolbarLocked == 1)
       Browser.forceChromeReflow();
   },
 
   unlockToolbar: function unlockToolbar() {
     if (!this._toolbarLocked)
@@ -374,21 +376,16 @@ var BrowserUI = {
   get toolbarH() {
     if (!this._toolbarH) {
       let toolbar = document.getElementById("toolbar-main");
       this._toolbarH = toolbar.boxObject.height;
     }
     return this._toolbarH;
   },
 
-  get sidebarW() {
-    delete this._sidebarW;
-    return this._sidebarW = Elements.controls.getBoundingClientRect().width;
-  },
-
   sizeControls: function(windowW, windowH) {
     // tabs
     document.getElementById("tabs").resize();
     AwesomeScreen.doResize(windowW, windowH);
 
     // content navigator helper
     document.getElementById("content-navigator").contentHasChanged();
   },
@@ -547,21 +544,37 @@ var BrowserUI = {
   },
 
   observe: function observe(aSubject, aTopic, aData) {
     if (aTopic == "nsPref:changed" && aData == "browser.ui.layout.tablet")
       this.updateTabletLayout();
   },
 
   updateTabletLayout: function updateTabletLayout() {
-    let tabletPref = Services.prefs.getIntPref("browser.ui.layout.tablet");
-    if (tabletPref == 1 || (tabletPref == -1 && Util.isTablet()))
+    if (Util.isTablet({ forceUpdate: true })) {
+      this.unlockToolbar();
       Elements.urlbarState.setAttribute("tablet", "true");
-    else
+    } else {
       Elements.urlbarState.removeAttribute("tablet");
+    }
+
+    // Tablet mode changes the size of the thumbnails
+    // in the tabs container. Hence we have to force a
+    // thumbnail update on all tabs.
+    setTimeout(function(self) {
+      self._updateAllTabThumbnails();
+    }, 0, this);
+  },
+
+  _updateAllTabThumbnails: function() {
+    let tabs = Browser.tabs;
+
+    tabs.forEach(function(tab) {
+      tab.updateThumbnail({ force: true });
+    });
   },
 
   update: function(aState) {
     let browser = Browser.selectedBrowser;
 
     switch (aState) {
       case TOOLBARSTATE_LOADED:
         this._updateToolbar();
--- a/mobile/chrome/content/browser.js
+++ b/mobile/chrome/content/browser.js
@@ -1033,17 +1033,17 @@ var Browser = {
         snappedX = -ritevis * ritew * dirVal;
       }
     }
 
     return Math.round(snappedX);
   },
 
   tryFloatToolbar: function tryFloatToolbar(dx, dy) {
-    if (this.floatedWhileDragging)
+    if (this.floatedWhileDragging || Util.isTablet())
       return;
 
     let [leftvis, ritevis, leftw, ritew] = Browser.computeSidebarVisibility(dx, dy);
     if (leftvis > 0 || ritevis > 0) {
       BrowserUI.lockToolbar();
       this.floatedWhileDragging = true;
     }
   },
@@ -1284,17 +1284,17 @@ Browser.MainDragger.prototype = {
     return { x: true, y: true };
   },
 
   dragStart: function dragStart(clientX, clientY, target, scroller) {
     let browser = getBrowser();
     let bcr = browser.getBoundingClientRect();
     this._contentView = browser.getViewAt(clientX - bcr.left, clientY - bcr.top);
     this._stopAtSidebar = 0;
-    this._panToolbars = !Elements.urlbarState.getAttribute("tablet");
+    this._panToolbars = !Util.isTablet();
     if (this._sidebarTimeout) {
       clearTimeout(this._sidebarTimeout);
       this._sidebarTimeout = null;
     }
   },
 
   dragStop: function dragStop(dx, dy, scroller) {
     if (this._contentView && this._contentView._updateCacheViewport)
@@ -1903,17 +1903,17 @@ const ContentTouchHandler = {
     let bcr = browser.getBoundingClientRect();
     let rect = new Rect(0, 0, window.innerWidth, window.innerHeight);
     rect.restrictTo(Rect.fromRect(bcr));
 
     // Check if the user touched near to one of the edges of the browser area
     // or if the urlbar is showing
     this.canCancelPan = (aX >= rect.left + kSafetyX) && (aX <= rect.right - kSafetyX) &&
                         (aY >= rect.top  + kSafetyY) &&
-                        (bcr.top == 0 || Elements.urlbarState.getAttribute("tablet"));
+                        (bcr.top == 0 || Util.isTablet());
   },
 
   tapDown: function tapDown(aX, aY) {
     let browser = getBrowser();
     browser.focus();
 
     // if the page might capture touch events, we give it the option
     this.updateCanCancel(aX, aY);
@@ -2894,17 +2894,16 @@ Tab.prototype = {
   _createBrowser: function _createBrowser(aURI, aInsertBefore) {
     if (this._browser)
       throw "Browser already exists";
 
     // Create a notification box around the browser
     let notification = this._notification = document.createElement("notificationbox");
     notification.classList.add("inputHandler");
 
-    // Create the browser using the current width the dynamically size the height
     let browser = this._browser = document.createElement("browser");
     browser.setAttribute("class", "viewable-width viewable-height");
     this._chromeTab.linkedBrowser = browser;
 
     browser.setAttribute("type", "content");
 
     let useRemote = Services.prefs.getBoolPref("browser.tabs.remote");
     let useLocal = Util.isLocalScheme(aURI);
@@ -3150,25 +3149,28 @@ function rendererFactory(aBrowser, aCanv
 
 /* ViewableAreaObserver is an helper object where width/height represents the
  * size of the currently viewable area in pixels. This is use instead of
  * window.innerHeight/innerWidth because some keyboards does not resize the
  * window but floats over it.
  */
 var ViewableAreaObserver = {
   get width() {
-    return this._width || window.innerWidth;
+    let width = this._width || window.innerWidth;
+    if (Util.isTablet()) {
+      let sidebarWidth = Math.round(Elements.tabs.getBoundingClientRect().width);
+      width -= sidebarWidth;
+    }
+    return width;
   },
 
   get height() {
     let height = (this._height || window.innerHeight);
-    if (Elements.urlbarState.getAttribute("tablet")) {
-      let toolbarHeight = Math.round(document.getElementById("toolbar-main").getBoundingClientRect().height);
-      height -= toolbarHeight;
-    }
+    if (Util.isTablet())
+      height -= BrowserUI.toolbarH;
     return height;
   },
 
   _isKeyboardOpened: true,
   get isKeyboardOpened() {
     return this._isKeyboardOpened;
   },
 
--- a/mobile/chrome/content/browser.xul
+++ b/mobile/chrome/content/browser.xul
@@ -193,32 +193,32 @@
     <key id="key_newTab" key="t" modifiers="accel" command="cmd_newTab"/>
     <key id="key_closeTab" key="w" modifiers="accel" command="cmd_closeTab"/>
     <key id="key_undoCloseTab" key="t" modifiers="accel,shift" command="cmd_undoCloseTab"/>
   </keyset>
 
   <stack flex="1" id="stack">
     <scrollbox id="controls-scrollbox" style="overflow: hidden; -moz-box-orient: horizontal; position: relative;" flex="1" observes="bcast_urlbarState">
       <vbox id="tabs-sidebar" class="sidebar" observes="bcast_uidiscovery">
-        <spacer class="toolbar-height"/>
+        <spacer class="toolbar-height" id="tabs-spacer" observes="bcast_urlbarState"/>
         <!-- Left toolbar -->
-        <vbox id="tabs-container" class="panel-dark" flex="1">
-          <vbox id="tabs" flex="1"
+        <vbox id="tabs-container" class="panel-dark" flex="1" observes="bcast_urlbarState">
+          <vbox id="tabs" flex="1" observes="bcast_urlbarState"
                 onselect="BrowserUI.selectTab(this);"
                 onreloadtab="BrowserUI.undoCloseTab()"
                 onclosetab="BrowserUI.closeTab(this)"
                 onclosereloadtab="this._container.removeTab(this)"/>
           <hbox id="tabs-controls">
-            <toolbarbutton id="newtab-button" class="button-control" command="cmd_newTab"/>
+            <toolbarbutton id="newtab-button" class="button-control" command="cmd_newTab" observes="bcast_urlbarState"/>
           </hbox>
         </vbox>
       </vbox>
 
       <!-- Page Area -->
-      <stack>
+      <stack id="page-stack" observes="bcast_urlbarState">
         <scrollbox id="page-scrollbox">
           <vbox>
             <!-- Main Toolbar -->
             <box id="toolbar-container" class="panel-dark toolbar-height">
               <box id="toolbar-moveable-container" observes="bcast_uidiscovery">
                 <toolbar id="toolbar-main" class="panel-dark viewable-width" observes="bcast_urlbarState">
                   <toolbarbutton id="tool-back2" class="tool-back button-actionbar" command="cmd_back"/>
                   <toolbarbutton id="tool-forward2" class="tool-forward button-actionbar" command="cmd_forward"/>
--- a/mobile/chrome/content/input.js
+++ b/mobile/chrome/content/input.js
@@ -1204,37 +1204,38 @@ GestureModule.prototype = {
 
   _pinchUpdate: function _pinchUpdate(aEvent) {
     if (!AnimatedZoom.isZooming() || !aEvent.delta)
       return;
 
     let delta = 0;
     let browser = AnimatedZoom.browser;
     let oldScale = browser.scale;
+    let bcr = this._browserBCR;
 
     // Accumulate pinch delta. Small changes are just jitter.
     this._pinchDelta += aEvent.delta;
     if (Math.abs(this._pinchDelta) >= oldScale) {
       delta = this._pinchDelta;
       this._pinchDelta = 0;
     }
 
     // decrease the pinchDelta min/max values to limit zooming out/in speed
     delta = Util.clamp(delta, -this._maxShrink, this._maxGrowth);
 
     let newScale = Browser.selectedTab.clampZoomLevel(oldScale * (1 + delta / this._scalingFactor));
     let startScale = AnimatedZoom.startScale;
     let scaleRatio = startScale / newScale;
-    let cX = aEvent.clientX - this._browserBCR.left;
-    let cY = aEvent.clientY - this._browserBCR.top;
+    let cX = aEvent.clientX - bcr.left;
+    let cY = aEvent.clientY - bcr.top;
 
     // Calculate the new zoom rect.
     let rect = AnimatedZoom.zoomFrom.clone();
-    rect.translate(this._pinchStartX - cX + (1-scaleRatio) * cX * rect.width / window.innerWidth,
-                   this._pinchStartY - cY + (1-scaleRatio) * cY * rect.height / window.innerHeight);
+    rect.translate(this._pinchStartX - cX + (1-scaleRatio) * cX * rect.width / bcr.width,
+                   this._pinchStartY - cY + (1-scaleRatio) * cY * rect.height / bcr.height);
 
     rect.width *= scaleRatio;
     rect.height *= scaleRatio;
 
     this.translateInside(rect, new Rect(0, 0, browser.contentDocumentWidth * startScale,
                                               browser.contentDocumentHeight * startScale));
 
     // redraw zoom canvas according to new zoom rect
--- a/mobile/chrome/content/tabs.xml
+++ b/mobile/chrome/content/tabs.xml
@@ -2,31 +2,71 @@
 
 <bindings
     xmlns="http://www.mozilla.org/xbl"
     xmlns:xbl="http://www.mozilla.org/xbl"
     xmlns:html="http://www.w3.org/1999/xhtml"
     xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
 
   <binding id="documenttab">
-    <content>
+    <content observes="bcast_urlbarState">
       <xul:stack anonid="page" class="documenttab-container" flex="1">
-        <html:canvas anonid="thumbnail" class="documenttab-thumbnail" left="0" width="104" height="65" moz-opaque="true" empty="true"
-          onclick="document.getBindingParent(this)._onClick()"/>
-        <xul:hbox class="documenttab-reload" left="0" top="0" width="104" height="65" onclick="document.getBindingParent(this)._onUndo();"/>
-        <xul:hbox class="documenttab-close-container" start="-16" top="0" height="65" width="55" align="center" onclick="document.getBindingParent(this)._onClose()">
-          <xul:image anonid="close" class="documenttab-close" mousethrough="always"/>
+        <html:canvas anonid="thumbnail" class="documenttab-thumbnail" left="0" moz-opaque="true" empty="true"
+          onclick="document.getBindingParent(this)._onClick()" observes="bcast_urlbarState"/>
+        <xul:hbox anonid="reload" class="documenttab-reload" left="0" top="0" onclick="document.getBindingParent(this)._onUndo();" observes="bcast_urlbarState"/>
+        <xul:hbox anonid="close-container" class="documenttab-close-container" top="0" align="center" onclick="document.getBindingParent(this)._onClose()" observes="bcast_urlbarState">
+          <xul:image anonid="close" class="documenttab-close" mousethrough="always" observes="bcast_urlbarState"/>
         </xul:hbox>
       </xul:stack>
     </content>
 
     <implementation>
       <field name="ignoreUndo">false</field>
-      <field name="thumbnail">document.getAnonymousElementByAttribute(this, "anonid", "thumbnail");</field>
-      <field name="_container">this.parentNode.parentNode;</field>
+      <field name="thumbnail" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "thumbnail");</field>
+      <field name="_reload" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "reload");</field>
+      <field name="_closeContainer" readonly="true">document.getAnonymousElementByAttribute(this, "anonid", "close-container");</field>
+      <field name="_container" readonly="true">this.parentNode.parentNode;</field>
+
+      <constructor>
+        <![CDATA[
+          this.updateTabletLayout(this.thumbnail);
+        ]]>
+      </constructor>
+
+      <method name="updateTabletLayout">
+        <parameter name="thumbnail"/>
+        <body>
+          <![CDATA[
+            let tabWidth, tabHeight, closeWidth, closeHeight;
+
+            if (Util.isTablet()) {
+              tabWidth = 176;
+              tabHeight = 110;
+              closeWidth = 41;
+              closeHeight = 117;
+            } else {
+              tabWidth = 104;
+              tabHeight = 65;
+              closeWidth = 55;
+              closeHeight = 65;
+            }
+
+            if (tabWidth != thumbnail.width) {
+              let reload = this._reload;
+              let closeContainer = this._closeContainer;
+
+              thumbnail.width = reload.width = tabWidth;
+              thumbnail.height = reload.height = tabHeight;
+              closeContainer.width = closeWidth;
+              closeContainer.height = closeHeight;
+            }
+          ]]>
+        </body>
+      </method>
+
       <method name="_onClick">
         <body>
           <![CDATA[
             this._container.selectedTab = this;
 
             let selectFn = new Function("event", this._container.parentNode.getAttribute("onselect"));
             selectFn.call(this);
           ]]>
@@ -56,24 +96,28 @@
 
       <method name="updateThumbnail">
         <parameter name="browser"/>
         <parameter name="width"/>
         <parameter name="height"/>
         <body>
           <![CDATA[
             let thumbnail = this.thumbnail;
+            // Ensure the thumbnail will have the correct
+            // dimensions for tablet and phone modes
+            this.updateTabletLayout(thumbnail);
+
             if (browser.currentURI.spec == "about:blank") {
               thumbnail.setAttribute("empty", "true");
               return;
             }
             thumbnail.removeAttribute("empty");
 
-            const tabWidth = 104;
-            const tabHeight = 65;
+            const tabWidth = thumbnail.width;
+            const tabHeight = thumbnail.height;
 
             let ratio = tabHeight / tabWidth;
             if (browser.contentDocumentWidth > 0)
               width = Math.min(width, browser.contentDocumentWidth);
 
             if (browser.contentDocumentHeight > 0)
               height = Math.min(height, browser.contentDocumentHeight);
 
@@ -113,17 +157,17 @@
           ]]>
         </body>
       </method>
     </implementation>
   </binding>
 
   <binding id="tablist">
     <content>
-      <xul:scrollbox anonid="tabs-scrollbox" flex="1">
+      <xul:scrollbox anonid="tabs-scrollbox" class="tabs-scrollbox" flex="1">
         <xul:vbox class="tabs-list" anonid="tabs-children" />
       </xul:scrollbox>
       <xul:box class="tabs-list" anonid="tabs-undo"/>
     </content>
     <implementation>
       <field name="children">document.getAnonymousElementByAttribute(this, "anonid", "tabs-children");</field>
       <field name="_scrollbox">document.getAnonymousElementByAttribute(this, "anonid", "tabs-scrollbox");</field>
       <field name="_tabsUndo">document.getAnonymousElementByAttribute(this, "anonid", "tabs-undo");</field>
@@ -236,17 +280,17 @@
             // XXX we can do better than using a constant
             const COLUMN_MARGIN = 20;
             let firstBox = this.children.firstChild.getBoundingClientRect();
             let lastBox = this.children.lastChild.getBoundingClientRect();
 
             // We can't rely on getBoundingClientRect() for this.children height
             // it is not synced (sometimes, especially during resize) with the 
             // style.height rule
-            let columnsCount = Math.ceil(this.children.childNodes.length / Math.floor(parseInt(this.children.style.height) / (firstBox.height + 4)));
+            let columnsCount = Util.isTablet() ? 1 : Math.ceil(this.children.childNodes.length / Math.floor(parseInt(this.children.style.height) / (firstBox.height + 4)));
             if (this._columnsCount != columnsCount && window.innerWidth > 1) { // > 1 to ignore column resizing while the main window is loading
               let width = columnsCount * (COLUMN_MARGIN + firstBox.width);
               this.children.style.width = width + "px";
 
               // Clamp the sidebar width so it won't overflow the window. Only clamp
               // the scrollbox. The children need to be the full width.
               if (width > window.innerWidth - firstBox.width)
                 width = window.innerWidth - firstBox.width;
--- a/mobile/themes/core/browser.css
+++ b/mobile/themes/core/browser.css
@@ -924,17 +924,17 @@ autocompleteresult.noresults > .autocomp
   -moz-border-end: @border_width_large@ solid #262629;
   background: #5e6166;
 }
 
 #tabs:-moz-locale-dir(rtl) {
   -moz-box-align: start;
 }
 
-#tabs > scrollbox {
+#tabs > .tabs-scrollbox {
   max-width: 350px;
 }
 
 #tabs .tabs-list {
   display: block;
   -moz-column-width: 121px;
   -moz-column-gap: 0;
   -moz-user-focus: ignore;
@@ -981,16 +981,17 @@ documenttab[selected="true"] {
 documenttab:hover:active > stack > .documenttab-thumbnail,
 documenttab[selected="true"] > stack > .documenttab-thumbnail {
   opacity: 1.0;
 }
 
 .documenttab-close-container {
   position: relative;
   left: -16px;
+  -moz-margin-start: -16px;
   -moz-margin-end: 49px;
 }
 
 .documenttab-close {
   width: 40px;
   height: 40px;
   list-style-image: url("chrome://browser/skin/images/close-inactive-tab-hdpi.png");
 }
@@ -1570,56 +1571,16 @@ setting {
   from { -moz-transform: translateX(0); }
   10% { -moz-transform: translateX(-moz-calc(-121px - @border_width_large@ - 2*@padding_normal@)); }
   45% { -moz-transform: translateX(-moz-calc(-121px - @border_width_large@ - 2*@padding_normal@)); }
   55% { -moz-transform: translateX(@sidebar_width_minimum@); }
   90% { -moz-transform: translateX(@sidebar_width_minimum@); }
   to { -moz-transform: translateX(0); }
 }
 
-/* Tablet mode */
-
-.spacer-actionbar,
-.button-actionbar {
-  visibility: collapse;
-}
-
-.button-actionbar[disabled="true"] {
-  opacity: 0.5;
-}
-
-.button-actionbar:hover:active {
-  background-color: #8db8d8;
-}
-
-#toolbar-main[tablet="true"] > .spacer-actionbar,
-#toolbar-main[tablet="true"] > .button-actionbar {
-  visibility: visible;
-}
-
-#controls-scrollbox[tablet="true"] > #controls-sidebar {
-  visibility: collapse;
-}
-
-#controls-scrollbox[tablet="true"] > #tabs-sidebar {
-  border: none;
-  position: fixed;
-  top: -moz-calc(@touch_button_xlarge@ + @margin_normal@);
-  visibility: collapse;
-}
-#controls-scrollbox[tablet="true"] > #tabs-sidebar:-moz-locale-dir(ltr) {
-  right: 0;
-}
-#controls-scrollbox[tablet="true"] > #tabs-sidebar:-moz-locale-dir(rtl) {
-  left: 0;
-}
-#controls-scrollbox[tablet="true"] > #tabs-sidebar[open] {
-  visibility: visible;
-}
-
 /* Text selection handles */
 
 #selectionhandle-start,
 #selectionhandle-end {
   min-width: 35px !important;
   width: 35px !important;
   padding: 0 !important;
   margin: 0 !important;
@@ -1635,9 +1596,11 @@ setting {
 
 @media (min-width: @tablet_panel_minwidth@) {
   #awesome-panels {
     -moz-box-shadow: 0px 0px @shadow_width_small@ black;
   }
   #search-engines-popup {
     max-width: -moz-calc(@tablet_panel_minwidth@);
   }
-}
\ No newline at end of file
+}
+
+%include tablet.css
--- a/mobile/themes/core/gingerbread/browser.css
+++ b/mobile/themes/core/gingerbread/browser.css
@@ -874,17 +874,17 @@ autocompleteresult.noresults > .autocomp
   -moz-border-end: @border_width_large@ solid #262629;
   background: @color_background_default@;
 }
 
 #tabs:-moz-locale-dir(rtl) {
   -moz-box-align: start;
 }
 
-#tabs > scrollbox {
+#tabs > .tabs-scrollbox {
   max-width: 350px;
 }
 
 #tabs .tabs-list {
   display: block;
   -moz-column-width: 121px;
   -moz-column-gap: 0;
   -moz-user-focus: ignore;
@@ -930,16 +930,17 @@ documenttab[selected="true"] {
 documenttab:hover:active > stack > .documenttab-thumbnail,
 documenttab[selected="true"] > stack > .documenttab-thumbnail {
   opacity: 1.0;
 }
 
 .documenttab-close-container {
   position: relative;
   left: -16px;
+  -moz-margin-start: -16px;
   -moz-margin-end: 49px;
 }
 
 .documenttab-close {
   width: 40px;
   height: 40px;
   list-style-image: url("chrome://browser/skin/images/close-inactive-tab-hdpi.png");
 }
@@ -1540,56 +1541,16 @@ setting {
   from { -moz-transform: translateX(0); }
   10% { -moz-transform: translateX(-moz-calc(-121px - @border_width_large@ - 2*@padding_normal@)); }
   45% { -moz-transform: translateX(-moz-calc(-121px - @border_width_large@ - 2*@padding_normal@)); }
   55% { -moz-transform: translateX(@sidebar_width_minimum@); }
   90% { -moz-transform: translateX(@sidebar_width_minimum@); }
   to { -moz-transform: translateX(0); }
 }
 
-/* Tablet mode */
-
-.spacer-actionbar,
-.button-actionbar {
-  visibility: collapse;
-}
-
-.button-actionbar[disabled="true"] {
-  opacity: 0.5;
-}
-
-.button-actionbar:hover:active {
-  background-color: #8db8d8;
-}
-
-#toolbar-main[tablet="true"] > .spacer-actionbar,
-#toolbar-main[tablet="true"] > .button-actionbar {
-  visibility: visible;
-}
-
-#controls-scrollbox[tablet="true"] > #controls-sidebar {
-  visibility: collapse;
-}
-
-#controls-scrollbox[tablet="true"] > #tabs-sidebar {
-  border: none;
-  position: fixed;
-  top: -moz-calc(@touch_button_xlarge@ + @margin_normal@);
-  visibility: collapse;
-}
-#controls-scrollbox[tablet="true"] > #tabs-sidebar:-moz-locale-dir(ltr) {
-  right: 0;
-}
-#controls-scrollbox[tablet="true"] > #tabs-sidebar:-moz-locale-dir(rtl) {
-  left: 0;
-}
-#controls-scrollbox[tablet="true"] > #tabs-sidebar[open] {
-  visibility: visible;
-}
-
 /* Text selection handles */
 
 #selectionhandle-start,
 #selectionhandle-end {
   min-width: 35px !important;
   width: 35px !important;
   padding: 0 !important;
   margin: 0 !important;
@@ -1598,16 +1559,9 @@ setting {
 #selectionhandle-start {
   list-style-image: url("chrome://browser/skin/images/handle-start.png");
 }
 
 #selectionhandle-end {
   list-style-image: url("chrome://browser/skin/images/handle-end.png");
 }
 
-@media (min-width: @tablet_panel_minwidth@) {
-  #awesome-panels {
-    -moz-box-shadow: 0px 0px @shadow_width_small@ black;
-  }
-  #search-engines-popup {
-    max-width: -moz-calc(@tablet_panel_minwidth@);
-  }
-}
+%include ../tablet.css
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1e616b6698a5fc77fbf709cf62af43e7aeb949df
GIT binary patch
literal 626
zc$@)p0*(ENP)<h;3K|Lk000e1NJLTq0015U001lq1^@s6a)Jm10000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!6iGxuRCwC#mcdKHa1_V2Hj5-78pD={
zu!D4vAS08gLxiB-Iz}L<;3fSB^c>|Su(NjQU+`YtyJ_KZ5D^sw#lq6C_u&0tjNF!k
z?C|@+2Y%bWd*8kHd%tb&e!uS;#TavqXmDe0%#FD*H~#;|f(^|$FiG<c@MWPBNcZ8y
z7_TdUkB(n~7vRmH^8kL*iKM$TGq)<W1jyxbc@Kg491sAe4W{^Te$?c0xgyMGiNOPZ
z1WZ@}wnL$i77PaaiA3TOj-$X5FbzzQX01>tR8fP#*a3oOv8^)3wR(-m<25{Jx7%CE
zWU@jf4G?m%Sln+k8U@s(QmIoab;n7@BFm^$Djoiic{H2Np0P|ZYt+&`9<$hP8~<Fa
zapZcvzFw(Rj=SCN6g<Qgs{+fkTCJYL^bxoRKAmLD5C8>Ot<`D=Ec0P9wY$b_^k@rd
znM~$dQIuPD!Rz%t=kxgs+B)x+nf~(4B~c^!pt+7zRgH+E_>O3rCZ*HqlUA#>NZU=e
z712gnWMCQC#HPa1VyUs@NH`pR!gG;GL~S;kyQGz^;#!%`0AUP>f%T@i-oT3fn!#Lh
zVmt?I#A30VA50Zi4Wl_$2M9PZ#+x@W7-BFL^h?3a@!{}oM;$)&KE|e+H+&?XXs`Bk
zI++^F_>p)R6xhxSL^6C9pDlEPPS<l{%zu5jF*oMM+?X5x?f4d804qkI*d{1O6951J
M07*qoM6N<$f_zRRW&i*H
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1767f2db21d9fdcfda14aa9e474b8dea4be0a17f
GIT binary patch
literal 1589
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1e616b6698a5fc77fbf709cf62af43e7aeb949df
GIT binary patch
literal 626
zc$@)p0*(ENP)<h;3K|Lk000e1NJLTq0015U001lq1^@s6a)Jm10000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!6iGxuRCwC#mcdKHa1_V2Hj5-78pD={
zu!D4vAS08gLxiB-Iz}L<;3fSB^c>|Su(NjQU+`YtyJ_KZ5D^sw#lq6C_u&0tjNF!k
z?C|@+2Y%bWd*8kHd%tb&e!uS;#TavqXmDe0%#FD*H~#;|f(^|$FiG<c@MWPBNcZ8y
z7_TdUkB(n~7vRmH^8kL*iKM$TGq)<W1jyxbc@Kg491sAe4W{^Te$?c0xgyMGiNOPZ
z1WZ@}wnL$i77PaaiA3TOj-$X5FbzzQX01>tR8fP#*a3oOv8^)3wR(-m<25{Jx7%CE
zWU@jf4G?m%Sln+k8U@s(QmIoab;n7@BFm^$Djoiic{H2Np0P|ZYt+&`9<$hP8~<Fa
zapZcvzFw(Rj=SCN6g<Qgs{+fkTCJYL^bxoRKAmLD5C8>Ot<`D=Ec0P9wY$b_^k@rd
znM~$dQIuPD!Rz%t=kxgs+B)x+nf~(4B~c^!pt+7zRgH+E_>O3rCZ*HqlUA#>NZU=e
z712gnWMCQC#HPa1VyUs@NH`pR!gG;GL~S;kyQGz^;#!%`0AUP>f%T@i-oT3fn!#Lh
zVmt?I#A30VA50Zi4Wl_$2M9PZ#+x@W7-BFL^h?3a@!{}oM;$)&KE|e+H+&?XXs`Bk
zI++^F_>p)R6xhxSL^6C9pDlEPPS<l{%zu5jF*oMM+?X5x?f4d804qkI*d{1O6951J
M07*qoM6N<$f_zRRW&i*H
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fd8e46b0c15e533f5c6360a659fbddc5a386b7fa
GIT binary patch
literal 546
zc$@(u0^R+IP)<h;3K|Lk000e1NJLTq0015U001lq1^@s6a)Jm10000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz#7RU!RCwC#mN84iKp2Kwn^sX2gb-}(
zAUZjSprV54ApQX9>`$nJlm9@}MW}<5xcCSB2kPYFYA1(^sI&#a(pn`F-@`X}grvDP
zU>)w@fnM*;_ws%B-6cuabrZc9eTg0oZLE#8u{PGm|KHe9)TO`xf49I_gwCMuBaX4q
z*kFY7H}DGTK3xWkQJ1FfG0a@W*aQ~HbAADa7_=^#+SF}=EI0x0;3+Vw_z;L-0<43E
z`!lY<5|{?Vpr4vWP;`IC2AB+stI}L8mhF`DHE_t1=7`923v5$!%x7AmQ57;yOBwA2
zWG*<9GA&Xop2>v8O)7qKd5t47TLuSU5L`=D@G?CI74Ge4oQypTV1orvkTUNm(@yr-
z8xFg;0d7G_TC0Ie9-U^}OuM+bOg55es(mm|lP1@Wz!Z;?6&X#j%odmht86Nlr7n;v
z|0{z$wN|^BDyE}gh5^eyQ?cK3zBSq0jANVyE8s4`)RwB@Hy72(#c8e-dqG^24`3JE
z@VHdSGq?vk)R?@lL$73&b1Z_4`(6@N8ak5BVq^JV5-;5vd*<?Mu@<2-B%Kq-SbzFx
kV{NRBwXrt-yYVf+01?=dN6%R1uK)l507*qoM6N<$f(;q;0ssI2
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1d9d1ae2308f8adcb70fddb823b04e25b12d456a
GIT binary patch
literal 263
zc%17D@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%k|nMYCBgY=CFO}lsSJ)O`AMk?
zp1FzXsX?iUDV2pMQ*A(MJzX3_DsH_AG3076;8`Pd<mAbVC-;B3`tRt)lP6d1xDwIi
zvGPmHB~5mB&)Z)(GS_{)CoLdbobpY``q?>~i77@Fc@sM=J!($unkx3nHD%(BeXC9{
zGM&1>Nh>72LrD0)c=0nE3l3GaeM?R}R+uuQLgdBY2MVFpPnOvH-YoRx-`{P@8>^V>
z+IFaGPxxHZCcNd>`eW-~@n|i1$E#d6!TDPk_fft_an+GAOgs#;BNY2GFZ%8RI-0@L
L)z4*}Q$iB}x3+FW
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f8017dcd6090938e4da76f3ec888446c70060718
GIT binary patch
literal 4600
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
G`TqcGR=q0#
--- a/mobile/themes/core/honeycomb/browser.css
+++ b/mobile/themes/core/honeycomb/browser.css
@@ -32,16 +32,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 %filter substitution
 %include defines.inc
+%define honeycomb 1
 
 /* main toolbar (URL bar) -------------------------------------------------- */
 #toolbar-main {
   -moz-appearance: none;
   -moz-box-align: center;
   padding: 0;
   background: @color_toolbar_background@;
   border: none;
@@ -957,17 +958,17 @@ autocompleteresult.noresults > .autocomp
   -moz-border-end: @border_width_large@ solid #262629;
   background: @color_toolbar_background@;
 }
 
 #tabs:-moz-locale-dir(rtl) {
   -moz-box-align: start;
 }
 
-#tabs > scrollbox {
+#tabs > .tabs-scrollbox {
   max-width: 350px;
 }
 
 #tabs .tabs-list {
   display: block;
   -moz-column-width: 121px;
   -moz-column-gap: 0;
   -moz-user-focus: ignore;
@@ -1013,16 +1014,17 @@ documenttab[selected="true"] {
 documenttab:hover:active > stack > .documenttab-thumbnail,
 documenttab[selected="true"] > stack > .documenttab-thumbnail {
   opacity: 1.0;
 }
 
 .documenttab-close-container {
   position: relative;
   left: -16px;
+  -moz-margin-start: -16px;
   -moz-margin-end: 49px;
 }
 
 .documenttab-close {
   width: 40px;
   height: 40px;
   list-style-image: url("chrome://browser/skin/images/close-inactive-tab-hdpi.png");
 }
@@ -1689,52 +1691,16 @@ setting {
   from { -moz-transform: translateX(0); }
   10% { -moz-transform: translateX(-moz-calc(-121px - @border_width_large@ - 2*@padding_normal@)); }
   45% { -moz-transform: translateX(-moz-calc(-121px - @border_width_large@ - 2*@padding_normal@)); }
   55% { -moz-transform: translateX(@sidebar_width_minimum@); }
   90% { -moz-transform: translateX(@sidebar_width_minimum@); }
   to { -moz-transform: translateX(0); }
 }
 
-/* Tablet mode */
-
-.spacer-actionbar,
-.button-actionbar {
-  visibility: collapse;
-}
-
-.button-actionbar[disabled="true"] {
-  opacity: 0.5;
-}
-
-#toolbar-main[tablet="true"] > .spacer-actionbar,
-#toolbar-main[tablet="true"] > .button-actionbar {
-  visibility: visible;
-}
-
-#controls-scrollbox[tablet="true"] > #controls-sidebar {
-  visibility: collapse;
-}
-
-#controls-scrollbox[tablet="true"] > #tabs-sidebar {
-  border: none;
-  position: fixed;
-  top: @touch_button_xlarge@;
-  visibility: collapse;
-}
-#controls-scrollbox[tablet="true"] > #tabs-sidebar:-moz-locale-dir(ltr) {
-  left: 0;
-}
-#controls-scrollbox[tablet="true"] > #tabs-sidebar:-moz-locale-dir(rtl) {
-  right: 0;
-}
-#controls-scrollbox[tablet="true"] > #tabs-sidebar[open] {
-  visibility: visible;
-}
-
 /* Text selection handles */
 
 #selectionhandle-start,
 #selectionhandle-end {
   min-width: 35px !important;
   width: 35px !important;
   padding: 0 !important;
   margin: 0 !important;
@@ -1809,8 +1775,10 @@ setting {
   }
   
   #search-engines-list > .action-button > .button-box > .button-text {
     text-align: start;
     font-size: @font_tiny@ !important;
   }
 
 }
+
+%include ../tablet.css
index 935b7555e4d8aceb67a15f9eb137b6ddade69522..1f19cd10638b54797e21db629ef041bcedc54f17
GIT binary patch
literal 1202
zc$@*U1Wo&iP)<h;3K|Lk000e1NJLTq001Ze001Zm1^@s6jQ+T70000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$R7pfZRCwCVm`P|9K@^5-dX~(h&V;BW
z;t~Y)pu~6;k?28i0moYu!5jnyAvx;BC3?}5C<qFI5k$Sk;07Kvh=?bNq8A01-~thy
znIyAszQ3&+I^A8pO$>QZ=_K9n{a?STdev3FdcDr(vngzz+q?%1;Kx><ubCL#)DqNs
zbpIcvmZmo5HSlv9egO}DfJsu@%dav!t@-^PwGn<zyAktYlJqTDYWv`)?5|EPn&gnG
zg_HpEQxAmwp&8E?swGyX*ri%U`|mV|99$h+^2JVbXnMj~1_|yAT?PQA5KPS9!lHqY
zdX-u*+W0kFEwWstglnQ!ulM04Ddr`DJkwMLDF`w(jPVul`B`fyszNCq$1K6Au&Gjx
zRq9p7$LBTzP0KQ<SY!=@|1;oPLov2I(#jSF!k*#Ol0m+Rfmn#iC)j7M<-E*rhi4Qp
z(GgCtR_ZRLxtVSNMNEJ~16ndjMb@n}_!BE5Z7kvs{<j*32?h;lL6d9SX7C0%bS|UJ
z&1TlDvI#9{LfaCM-XKX1$@cJ~If2tca!t(cq*VqlE@@fFFk@x5vTFm|a<JDm1bpBt
zUD8Ms+F&p&bv%20R6;tfY8wPjJRWAzcwGJa#jU%J;SOKD%yO9wJHK-u8-M%G^f_pQ
z0W4H{hV&U6XlsdCifG{==p>T@4hG+|FP~caafX2zU5sUn>QlE>*P6XGgoU`79k_7L
za35KeWl(fASoqS~$Y4)A7_rO*f}(pMAj^;*e#fyR1~{uO7RFh68h}B?zZo^j&Jg(2
z=IsUnJ)~Lpix;$m6ZH{HjkS1JF#Kj|%c3r)GZJG7<552=#iWtJ>9cQT6UE85kIIFO
zD&XEw>;tWhYuuC#8Kl#pVP~mQ_Txw?yH@SufZ~t1t!qxklUFz`Q&W2q2w&cRNO~Xl
zyucY&l1i<b_u`Ch?!R+WdLQ>h-E?b$Nd>I3`g<>Mc4JV#^0+a<qyiSJmC@ON!z|V>
ze_kO4(?7skc|^Clk=o)_E2MDOkM^rg25C3rg@FZ34M6O$rYePTJ4Xm$EtSCuYZekL
zV%OK0#hG&cG~dX$8c%vSE##9c*IbWVFo4DIJ&4bnKc(!1Ro9w9n869Vef*^9eJemP
zh)*p;9h|Ndk6>G9X0Pu*o>6qoy3x$E85YG|<_B#sSni9z=6*lu2*-EJJ|6J)Xih&?
zwI38%S!62@y7qfM{cu&ZZI5<eYyia0Dy#%wwTI*D=4xMp4gJqT`bV)=S+Cv1n{M3k
zK%rLdC5Nfn92{<PxG93$ZFa(7x)#%eW8|=PJpVhBua^HC9O@l3pao4=2bgf6x9g<0
z8%>pSY&NrrScv&8pHeksaJFAFNIA7_^ldiyJ)fGS0@@TPd=Ue&5R+nWk{uj*J|Cos
zvyZlhaogiJUHRb0ZyACdc5^=H7*Kb1(>P&uc!1eBVYSPcDd2oQqy8(v04{5M+MdAo
QYybcN07*qoM6N<$f?N<W0ssI2
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1e616b6698a5fc77fbf709cf62af43e7aeb949df
GIT binary patch
literal 626
zc$@)p0*(ENP)<h;3K|Lk000e1NJLTq0015U001lq1^@s6a)Jm10000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!6iGxuRCwC#mcdKHa1_V2Hj5-78pD={
zu!D4vAS08gLxiB-Iz}L<;3fSB^c>|Su(NjQU+`YtyJ_KZ5D^sw#lq6C_u&0tjNF!k
z?C|@+2Y%bWd*8kHd%tb&e!uS;#TavqXmDe0%#FD*H~#;|f(^|$FiG<c@MWPBNcZ8y
z7_TdUkB(n~7vRmH^8kL*iKM$TGq)<W1jyxbc@Kg491sAe4W{^Te$?c0xgyMGiNOPZ
z1WZ@}wnL$i77PaaiA3TOj-$X5FbzzQX01>tR8fP#*a3oOv8^)3wR(-m<25{Jx7%CE
zWU@jf4G?m%Sln+k8U@s(QmIoab;n7@BFm^$Djoiic{H2Np0P|ZYt+&`9<$hP8~<Fa
zapZcvzFw(Rj=SCN6g<Qgs{+fkTCJYL^bxoRKAmLD5C8>Ot<`D=Ec0P9wY$b_^k@rd
znM~$dQIuPD!Rz%t=kxgs+B)x+nf~(4B~c^!pt+7zRgH+E_>O3rCZ*HqlUA#>NZU=e
z712gnWMCQC#HPa1VyUs@NH`pR!gG;GL~S;kyQGz^;#!%`0AUP>f%T@i-oT3fn!#Lh
zVmt?I#A30VA50Zi4Wl_$2M9PZ#+x@W7-BFL^h?3a@!{}oM;$)&KE|e+H+&?XXs`Bk
zI++^F_>p)R6xhxSL^6C9pDlEPPS<l{%zu5jF*oMM+?X5x?f4d804qkI*d{1O6951J
M07*qoM6N<$f_zRRW&i*H
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1767f2db21d9fdcfda14aa9e474b8dea4be0a17f
GIT binary patch
literal 1589
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
index 3961ad1baaa1e26175cdf58f16e54b1801996206..7e4b9f7d8d82e74551c48a938a16606d2d041901
GIT binary patch
literal 1087
zc$@)01i<@=P)<h;3K|Lk000e1NJLTq001Ze001Zm1^@s6jQ+T70000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#;Ymb6RCwCFnN3JlK@`X5`IJCJ-zFE8
z@S--A;0s!0P+058C$$J#WUFYCxM~rh5iMGYZK9R72wD_sMJ)<5f)o;bg4xDUup)Hx
zeE9jkZu7sMvGIP)ojdP64-BKuJ?H%HoVjOajuFeU*xSaVtOQB$7UQkW@%J&_(!8Zi
z4PRWEW7P(FfJyS!PQ9P>NIwtrHbQ+mjI*i`&&GKh;BB7zWC-1)LpNS-z+T>_c<ZDY
zG?f_Hm?E1c1H#}Z3Rfv$UUqv3G-e8zppdk&%8Bri2n(2?nTxT?i-=M}7Z3_VdjuEg
z(+e=vS5`(5Swabz3`!sM&Bs{N)ytv1LkGGV>1daKXQJ*4f@>Xm&T1O!?cWz)ywZki
zdHjgwRx)h7;~LA%{7^mKM+x0EgPp3-!ifwAhdMJU;2_XFH~J<>FHS5jz%G^6%FL|m
zZ*VM}d&!9hcU|L{MK4<p>2d<bRk;X)upm5e;5S1~_`^4ExZpHGEYe(D2GmN%F#P4h
zC6@yyq*wUG3u?eInMr&kExKCki@$6~Sda2oDFgYzIcGmWY^Dg#gCmVc#-l89I*c|J
zyc06a(g>0QvnJY?ezw{n&RSOJT|;L*nZn6THelXjbtb7Fjwi3*@*T&R37pKd4ah7b
zK!o|!vnRge7!!FjG$1imF+<T=gU+4DL~OuJvA9|eIBb2+?avfaFxh}B#ez0qZPOuN
zxar$>?B)llaaj1ja}kpb__vtXx@p0|+0oa&a6-xfDVRivC(^}2Ae>jIoNzMa{L=xW
z+zA#i$sft|Ts|8ZS}zDMI3eHfJ@g!JXg;p=MJ&@Y;MhVw$JVT(|CGP~uq)|$h0#mo
z85YT1r3VJEP-S9!t3&DI<$7l7K(sn>W?yY!s4)Y&hgErh#xX1Cu<&(jo&71~Q&(#1
zPdZB<{d{16H^xDg+gFGa$*!3U68J^~i|fG3i3fSN@GZN+f<Bl^2to_rU?3LHOH+4_
zhkP=-$<XhWz}NWJhVa?#d#mH*xU39g20!z)uosZ<0bdlgVPa6;i*Zg*LE+uzDqG%O
z6BQBRgWwj(xBBAF-yls7rp4RM=C;Z@o3I6&A2-*8UHI%E-@&yR{vfKN=S8tm7(;$4
z1r)ll0b8&sCKA5Lr%NKL5-`t=qGqylsj7)Y*g>9#4n%8|>ByZva_l$n9WR(7IM1^H
z@?b=#M$JKc>8|R!8pNuiHuQep%d*VZ6<~Z)7J&LWn6iku1K3&Ds9W6Wq1uUQ<tt5P
zjr;UbC}nVUDHNbwD$^;V%VJbSHwE;n0#xMR5CHrqzyS6nJ3M6*QC$E4002ovPDHLk
FV1hM@1Hb?P
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1e616b6698a5fc77fbf709cf62af43e7aeb949df
GIT binary patch
literal 626
zc$@)p0*(ENP)<h;3K|Lk000e1NJLTq0015U001lq1^@s6a)Jm10000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!6iGxuRCwC#mcdKHa1_V2Hj5-78pD={
zu!D4vAS08gLxiB-Iz}L<;3fSB^c>|Su(NjQU+`YtyJ_KZ5D^sw#lq6C_u&0tjNF!k
z?C|@+2Y%bWd*8kHd%tb&e!uS;#TavqXmDe0%#FD*H~#;|f(^|$FiG<c@MWPBNcZ8y
z7_TdUkB(n~7vRmH^8kL*iKM$TGq)<W1jyxbc@Kg491sAe4W{^Te$?c0xgyMGiNOPZ
z1WZ@}wnL$i77PaaiA3TOj-$X5FbzzQX01>tR8fP#*a3oOv8^)3wR(-m<25{Jx7%CE
zWU@jf4G?m%Sln+k8U@s(QmIoab;n7@BFm^$Djoiic{H2Np0P|ZYt+&`9<$hP8~<Fa
zapZcvzFw(Rj=SCN6g<Qgs{+fkTCJYL^bxoRKAmLD5C8>Ot<`D=Ec0P9wY$b_^k@rd
znM~$dQIuPD!Rz%t=kxgs+B)x+nf~(4B~c^!pt+7zRgH+E_>O3rCZ*HqlUA#>NZU=e
z712gnWMCQC#HPa1VyUs@NH`pR!gG;GL~S;kyQGz^;#!%`0AUP>f%T@i-oT3fn!#Lh
zVmt?I#A30VA50Zi4Wl_$2M9PZ#+x@W7-BFL^h?3a@!{}oM;$)&KE|e+H+&?XXs`Bk
zI++^F_>p)R6xhxSL^6C9pDlEPPS<l{%zu5jF*oMM+?X5x?f4d804qkI*d{1O6951J
M07*qoM6N<$f_zRRW&i*H
index 105d7852eddc5783d5936d836efcc94d08a38450..77d71107fa1bc1016d6119bcbea054e9765fd36b
GIT binary patch
literal 1150
zc$@)#1cCdBP)<h;3K|Lk000e1NJLTq001Ze001Zm1^@s6jQ+T70000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU$AW1|)RCwCFnN4UDK@`VllQd1*DygCs
zFQ$6*U@Crs2a5+Uq7gq%wp2wyFQw>BF|FuLDZS`H)S{k5F^U%tejEhr2c`uJo-~SR
zt6-yTt=2a2{ns~PlkCUrCh3D=m)&{q{dQjF&6|mq%jLvrO%r8Ej9P@+=A!3MsAZ@P
zdNq8C*a9B(028OSgRfSSFx)4p-Q{b>jWeH%zENsdslDfG+=XtuNH<z(z;0?OY8^a+
zUM0ddQf#wqKp1??;VK8r%x;1~BVGYx9FonVdLn(q+ycgU<RYT_BBK;j1%$$#If8Ta
zX$I(OmQ_$>maqkkJEe~$%T|imu6^!*Y@`ESg>+Qlzc^9%I>EKSekwu>C=$DAiMV}3
z8E*Tn>*DwKjJURXlNk9hY}dV)6MD%AcG!g$PV#UhaHukp5)K00!|69pdQoO60qnHX
z8hMo}c?QSZFI=|N*?Q(&9!?5I(actnbQS|f?Q)S0NMS*E;GkfHjvO$B#IyA$j+MYE
zgjlAzs1Z;p8N=|U=t?pAGGz;idpt7=bQIx)kx5RHRjOKJ<DbQ~b(=){i>IP#ZA$^9
z*?k_4bf&~FHxplH(ohffXufBf$Zc?=9Ts&ZkY?f$PUM6(kjP~>>LH%hG}o&J9KvyN
zxGMo?WYPh1n%3YY^~3SaE!!;L<DC~cBa;r872W_5=DiQ^TfWCT^zkB+$tOID%5M<w
z(8nue9&p+hn5YEY;KeI?s|2KE3b+MqoVQdDof+J{qX$h>jfA|rr%gEzN+x{-fBAz-
zJ1rCohtk82aJV-eNm|PlDjH#A(gFYa`~yVn2xlx7tl*50&(EGOcm`b+NU(s(I7x<n
z)P(Iz-WAQI5WCw<;iNn`d0g*P^08~|lRL8v83CV+2SQ>-^IIyteRRK|Sb$@ddJugi
zq%0Qf^uPcXKAHHx)ZyskUHzg|{p_Du)9;^-p~eWP7OTd(5M`FrBpD6O7nA<l!R3?R
zT8fW8`sl?~YJxy*D;gISNx?`u34Ei0rKDgb;(ofMztzqYP7|gYg3!V@7?{O$)6|uv
zaDSza+44@j+%0S5YlK3}KD#V;bwrNMN)jXZITV=_Ncey+j#`o#Z1=_c@sokV$M8Zi
z<*%t4ob<tPY4&Zuai=fHaK{Y3?hAe0SWR6;M-#SS^HaD{+J(=Z?A!TlhC1R@v{fE9
zjs@#=W5`eCfI=5GU<)=`;G>m&s-JGLLbFVma+V<^xN<sB67)6d8WsfIhZUUJ$%0w9
zTPcZm^IkO~M}f1w2xe!5pAu?lcDitj=?|i8w@O*C+eh{t4>9$Xq@!W6>fug;&rZCa
zd}a7pqlYJjt@KZPbyZQzS=P+gqzm0-k#1U5G2n3&`nkzdfU5ir0r@Au0H<ui;K+$Q
QAOHXW07*qoM6N<$f&xb#MF0Q*
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fd8e46b0c15e533f5c6360a659fbddc5a386b7fa
GIT binary patch
literal 546
zc$@(u0^R+IP)<h;3K|Lk000e1NJLTq0015U001lq1^@s6a)Jm10000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz#7RU!RCwC#mN84iKp2Kwn^sX2gb-}(
zAUZjSprV54ApQX9>`$nJlm9@}MW}<5xcCSB2kPYFYA1(^sI&#a(pn`F-@`X}grvDP
zU>)w@fnM*;_ws%B-6cuabrZc9eTg0oZLE#8u{PGm|KHe9)TO`xf49I_gwCMuBaX4q
z*kFY7H}DGTK3xWkQJ1FfG0a@W*aQ~HbAADa7_=^#+SF}=EI0x0;3+Vw_z;L-0<43E
z`!lY<5|{?Vpr4vWP;`IC2AB+stI}L8mhF`DHE_t1=7`923v5$!%x7AmQ57;yOBwA2
zWG*<9GA&Xop2>v8O)7qKd5t47TLuSU5L`=D@G?CI74Ge4oQypTV1orvkTUNm(@yr-
z8xFg;0d7G_TC0Ie9-U^}OuM+bOg55es(mm|lP1@Wz!Z;?6&X#j%odmht86Nlr7n;v
z|0{z$wN|^BDyE}gh5^eyQ?cK3zBSq0jANVyE8s4`)RwB@Hy72(#c8e-dqG^24`3JE
z@VHdSGq?vk)R?@lL$73&b1Z_4`(6@N8ak5BVq^JV5-;5vd*<?Mu@<2-B%Kq-SbzFx
kV{NRBwXrt-yYVf+01?=dN6%R1uK)l507*qoM6N<$f(;q;0ssI2
index aacf3b927b0da779ef18c830863e3109604c3b9f..e71e89cd79b51c9a0ba31ecafd73760dff9adc8f
GIT binary patch
literal 208
zc%17D@N?(olHy`uVBq!ia0vp^8X(NU1|)m_?Z^dEk|nMYCBgY=CFO}lsSJ)O`AMk?
zp1FzXsX?iUDV2pMQ*D5XiacE$Ln>~)x#q}qK!L|4FxX~w`=?Eh_ccvOO3kRh<|wCT
z*tX=6Ot>L`b%O7+{WtkPc1q`T-|^#TGMVv6U0BnLr{Jpg$@5J{(Fw;Tzh00!eduxd
z`JRe2hFNQ^0+SjT7@1f&1QZVZF4x}S*ud~yf{BOW?mvN0wrr-$K-(ESUHx3vIVCg!
E0J9BAjsO4v
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1d9d1ae2308f8adcb70fddb823b04e25b12d456a
GIT binary patch
literal 263
zc%17D@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%k|nMYCBgY=CFO}lsSJ)O`AMk?
zp1FzXsX?iUDV2pMQ*A(MJzX3_DsH_AG3076;8`Pd<mAbVC-;B3`tRt)lP6d1xDwIi
zvGPmHB~5mB&)Z)(GS_{)CoLdbobpY``q?>~i77@Fc@sM=J!($unkx3nHD%(BeXC9{
zGM&1>Nh>72LrD0)c=0nE3l3GaeM?R}R+uuQLgdBY2MVFpPnOvH-YoRx-`{P@8>^V>
z+IFaGPxxHZCcNd>`eW-~@n|i1$E#d6!TDPk_fft_an+GAOgs#;BNY2GFZ%8RI-0@L
L)z4*}Q$iB}x3+FW
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f8017dcd6090938e4da76f3ec888446c70060718
GIT binary patch
literal 4600
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
G`TqcGR=q0#
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1e616b6698a5fc77fbf709cf62af43e7aeb949df
GIT binary patch
literal 626
zc$@)p0*(ENP)<h;3K|Lk000e1NJLTq0015U001lq1^@s6a)Jm10000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!6iGxuRCwC#mcdKHa1_V2Hj5-78pD={
zu!D4vAS08gLxiB-Iz}L<;3fSB^c>|Su(NjQU+`YtyJ_KZ5D^sw#lq6C_u&0tjNF!k
z?C|@+2Y%bWd*8kHd%tb&e!uS;#TavqXmDe0%#FD*H~#;|f(^|$FiG<c@MWPBNcZ8y
z7_TdUkB(n~7vRmH^8kL*iKM$TGq)<W1jyxbc@Kg491sAe4W{^Te$?c0xgyMGiNOPZ
z1WZ@}wnL$i77PaaiA3TOj-$X5FbzzQX01>tR8fP#*a3oOv8^)3wR(-m<25{Jx7%CE
zWU@jf4G?m%Sln+k8U@s(QmIoab;n7@BFm^$Djoiic{H2Np0P|ZYt+&`9<$hP8~<Fa
zapZcvzFw(Rj=SCN6g<Qgs{+fkTCJYL^bxoRKAmLD5C8>Ot<`D=Ec0P9wY$b_^k@rd
znM~$dQIuPD!Rz%t=kxgs+B)x+nf~(4B~c^!pt+7zRgH+E_>O3rCZ*HqlUA#>NZU=e
z712gnWMCQC#HPa1VyUs@NH`pR!gG;GL~S;kyQGz^;#!%`0AUP>f%T@i-oT3fn!#Lh
zVmt?I#A30VA50Zi4Wl_$2M9PZ#+x@W7-BFL^h?3a@!{}oM;$)&KE|e+H+&?XXs`Bk
zI++^F_>p)R6xhxSL^6C9pDlEPPS<l{%zu5jF*oMM+?X5x?f4d804qkI*d{1O6951J
M07*qoM6N<$f_zRRW&i*H
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1767f2db21d9fdcfda14aa9e474b8dea4be0a17f
GIT binary patch
literal 1589
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
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1e616b6698a5fc77fbf709cf62af43e7aeb949df
GIT binary patch
literal 626
zc$@)p0*(ENP)<h;3K|Lk000e1NJLTq0015U001lq1^@s6a)Jm10000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!6iGxuRCwC#mcdKHa1_V2Hj5-78pD={
zu!D4vAS08gLxiB-Iz}L<;3fSB^c>|Su(NjQU+`YtyJ_KZ5D^sw#lq6C_u&0tjNF!k
z?C|@+2Y%bWd*8kHd%tb&e!uS;#TavqXmDe0%#FD*H~#;|f(^|$FiG<c@MWPBNcZ8y
z7_TdUkB(n~7vRmH^8kL*iKM$TGq)<W1jyxbc@Kg491sAe4W{^Te$?c0xgyMGiNOPZ
z1WZ@}wnL$i77PaaiA3TOj-$X5FbzzQX01>tR8fP#*a3oOv8^)3wR(-m<25{Jx7%CE
zWU@jf4G?m%Sln+k8U@s(QmIoab;n7@BFm^$Djoiic{H2Np0P|ZYt+&`9<$hP8~<Fa
zapZcvzFw(Rj=SCN6g<Qgs{+fkTCJYL^bxoRKAmLD5C8>Ot<`D=Ec0P9wY$b_^k@rd
znM~$dQIuPD!Rz%t=kxgs+B)x+nf~(4B~c^!pt+7zRgH+E_>O3rCZ*HqlUA#>NZU=e
z712gnWMCQC#HPa1VyUs@NH`pR!gG;GL~S;kyQGz^;#!%`0AUP>f%T@i-oT3fn!#Lh
zVmt?I#A30VA50Zi4Wl_$2M9PZ#+x@W7-BFL^h?3a@!{}oM;$)&KE|e+H+&?XXs`Bk
zI++^F_>p)R6xhxSL^6C9pDlEPPS<l{%zu5jF*oMM+?X5x?f4d804qkI*d{1O6951J
M07*qoM6N<$f_zRRW&i*H
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..fd8e46b0c15e533f5c6360a659fbddc5a386b7fa
GIT binary patch
literal 546
zc$@(u0^R+IP)<h;3K|Lk000e1NJLTq0015U001lq1^@s6a)Jm10000PbVXQnQ*UN;
zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBUz#7RU!RCwC#mN84iKp2Kwn^sX2gb-}(
zAUZjSprV54ApQX9>`$nJlm9@}MW}<5xcCSB2kPYFYA1(^sI&#a(pn`F-@`X}grvDP
zU>)w@fnM*;_ws%B-6cuabrZc9eTg0oZLE#8u{PGm|KHe9)TO`xf49I_gwCMuBaX4q
z*kFY7H}DGTK3xWkQJ1FfG0a@W*aQ~HbAADa7_=^#+SF}=EI0x0;3+Vw_z;L-0<43E
z`!lY<5|{?Vpr4vWP;`IC2AB+stI}L8mhF`DHE_t1=7`923v5$!%x7AmQ57;yOBwA2
zWG*<9GA&Xop2>v8O)7qKd5t47TLuSU5L`=D@G?CI74Ge4oQypTV1orvkTUNm(@yr-
z8xFg;0d7G_TC0Ie9-U^}OuM+bOg55es(mm|lP1@Wz!Z;?6&X#j%odmht86Nlr7n;v
z|0{z$wN|^BDyE}gh5^eyQ?cK3zBSq0jANVyE8s4`)RwB@Hy72(#c8e-dqG^24`3JE
z@VHdSGq?vk)R?@lL$73&b1Z_4`(6@N8ak5BVq^JV5-;5vd*<?Mu@<2-B%Kq-SbzFx
kV{NRBwXrt-yYVf+01?=dN6%R1uK)l507*qoM6N<$f(;q;0ssI2
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..1d9d1ae2308f8adcb70fddb823b04e25b12d456a
GIT binary patch
literal 263
zc%17D@N?(olHy`uVBq!ia0vp^QXtI11|(N{`J4k%k|nMYCBgY=CFO}lsSJ)O`AMk?
zp1FzXsX?iUDV2pMQ*A(MJzX3_DsH_AG3076;8`Pd<mAbVC-;B3`tRt)lP6d1xDwIi
zvGPmHB~5mB&)Z)(GS_{)CoLdbobpY``q?>~i77@Fc@sM=J!($unkx3nHD%(BeXC9{
zGM&1>Nh>72LrD0)c=0nE3l3GaeM?R}R+uuQLgdBY2MVFpPnOvH-YoRx-`{P@8>^V>
z+IFaGPxxHZCcNd>`eW-~@n|i1$E#d6!TDPk_fft_an+GAOgs#;BNY2GFZ%8RI-0@L
L)z4*}Q$iB}x3+FW
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..f8017dcd6090938e4da76f3ec888446c70060718
GIT binary patch
literal 4600
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
G`TqcGR=q0#
--- a/mobile/themes/core/jar.mn
+++ b/mobile/themes/core/jar.mn
@@ -85,38 +85,44 @@ chrome.jar:
   skin/images/panelrow-selected-hdpi.png    (images/panelrow-selected-hdpi.png)
   skin/images/forward-default-hdpi.png        (images/forward-default-hdpi.png)
   skin/images/downloads-default-hdpi.png      (images/downloads-default-hdpi.png)
   skin/images/settings-default-hdpi.png       (images/settings-default-hdpi.png)
   skin/images/preferences-default-hdpi.png    (images/preferences-default-hdpi.png)
   skin/images/row-header-bg.png             (images/row-header-bg.png)
   skin/images/console-default-hdpi.png        (images/console-default-hdpi.png)
   skin/images/newtab-default-hdpi.png         (images/newtab-default-hdpi.png)
+  skin/images/newtab-default-tablet-hdpi.png (images/newtab-default-tablet-hdpi.png)
   skin/images/tab-active-hdpi.png           (images/tab-active-hdpi.png)
   skin/images/tab-inactive-hdpi.png         (images/tab-inactive-hdpi.png)
   skin/images/tab-closed-hdpi.png           (images/tab-closed-hdpi.png)
   skin/images/tab-reopen-hdpi.png           (images/tab-reopen-hdpi.png)
+  skin/images/tab-reopen-tablet-hdpi.png    (images/tab-reopen-tablet-hdpi.png)
   skin/images/remotetabs-48.png             (images/remotetabs-48.png)
   skin/images/remotetabs-32.png             (images/remotetabs-32.png)
   skin/images/mozilla-32.png                (images/mozilla-32.png)
   skin/images/toggle-on.png                 (images/toggle-on.png)
   skin/images/toggle-off.png                (images/toggle-off.png)
   skin/images/sidebarbutton-active-hdpi.png (images/sidebarbutton-active-hdpi.png)
   skin/images/previous-hdpi.png             (images/previous-hdpi.png)
   skin/images/previous-disabled-hdpi.png    (images/previous-disabled-hdpi.png)
   skin/images/next-hdpi.png                 (images/next-hdpi.png)
   skin/images/next-disabled-hdpi.png        (images/next-disabled-hdpi.png)
   skin/images/identity-default-hdpi.png     (images/identity-default-hdpi.png)
   skin/images/identity-ssl-hdpi.png         (images/identity-ssl-hdpi.png)
   skin/images/identity-ev-hdpi.png          (images/identity-ev-hdpi.png)
   skin/images/unlocked-hdpi.png             (images/unlocked-hdpi.png)
   skin/images/locked-hdpi.png               (images/locked-hdpi.png)
   skin/images/close-default-hdpi.png        (images/close-default-hdpi.png)
+  skin/images/close-default-tablet-hdpi.png (images/close-default-tablet-hdpi.png)
   skin/images/close-active-hdpi.png         (images/close-active-hdpi.png)
+  skin/images/close-active-tablet-hdpi.png  (images/close-active-tablet-hdpi.png)
   skin/images/close-inactive-tab-hdpi.png   (images/close-inactive-tab-hdpi.png)
+  skin/images/close-inactive-tab-tablet-hdpi.png   (images/close-inactive-tab-tablet-hdpi.png)
+  skin/images/close-background-hdpi.png     (images/close-background-hdpi.png)
   skin/images/check-30.png                  (images/check-30.png)
   skin/images/search-glass-30.png           (images/search-glass-30.png)
   skin/images/search-clear-30.png           (images/search-clear-30.png)
   skin/images/section-expanded-16.png       (images/section-expanded-16.png)
   skin/images/section-collapsed-16.png      (images/section-collapsed-16.png)
   skin/images/task-switch-hdpi.png          (images/task-switch-hdpi.png)
   skin/images/task-close-hdpi.png           (images/task-close-hdpi.png)
   skin/images/task-back-hdpi.png            (images/task-back-hdpi.png)
@@ -209,37 +215,43 @@ chrome.jar:
   skin/gingerbread/images/bookmark-starred-hdpi.png     (gingerbread/images/bookmark-starred-hdpi.png)
   skin/gingerbread/images/forward-default-hdpi.png        (gingerbread/images/forward-default-hdpi.png)
   skin/gingerbread/images/downloads-default-hdpi.png      (gingerbread/images/downloads-default-hdpi.png)
   skin/gingerbread/images/settings-default-hdpi.png       (gingerbread/images/settings-default-hdpi.png)
   skin/gingerbread/images/preferences-default-hdpi.png    (gingerbread/images/preferences-default-hdpi.png)
   skin/gingerbread/images/row-header-bg.png             (gingerbread/images/row-header-bg.png)
   skin/gingerbread/images/console-default-hdpi.png        (gingerbread/images/console-default-hdpi.png)
   skin/gingerbread/images/newtab-default-hdpi.png         (gingerbread/images/newtab-default-hdpi.png)
+  skin/gingerbread/images/newtab-default-tablet-hdpi.png (gingerbread/images/newtab-default-tablet-hdpi.png)
   skin/gingerbread/images/tab-active-hdpi.png           (gingerbread/images/tab-active-hdpi.png)
   skin/gingerbread/images/tab-inactive-hdpi.png         (gingerbread/images/tab-inactive-hdpi.png)
   skin/gingerbread/images/tab-closed-hdpi.png           (gingerbread/images/tab-closed-hdpi.png)
   skin/gingerbread/images/tab-reopen-hdpi.png           (gingerbread/images/tab-reopen-hdpi.png)
+  skin/gingerbread/images/tab-reopen-tablet-hdpi.png    (gingerbread/images/tab-reopen-tablet-hdpi.png)
   skin/gingerbread/images/remotetabs-48.png             (gingerbread/images/remotetabs-48.png)
   skin/gingerbread/images/remotetabs-32.png             (gingerbread/images/remotetabs-32.png)
   skin/gingerbread/images/mozilla-32.png                (images/mozilla-32.png)
   skin/gingerbread/images/toggle-on.png                 (gingerbread/images/toggle-on.png)
   skin/gingerbread/images/toggle-off.png                (gingerbread/images/toggle-off.png)
   skin/gingerbread/images/previous-hdpi.png             (gingerbread/images/previous-hdpi.png)
   skin/gingerbread/images/previous-disabled-hdpi.png    (gingerbread/images/previous-disabled-hdpi.png)
   skin/gingerbread/images/next-hdpi.png                 (gingerbread/images/next-hdpi.png)
   skin/gingerbread/images/next-disabled-hdpi.png        (gingerbread/images/next-disabled-hdpi.png)
   skin/gingerbread/images/identity-default-hdpi.png     (gingerbread/images/identity-default-hdpi.png)
   skin/gingerbread/images/identity-ssl-hdpi.png         (gingerbread/images/identity-ssl-hdpi.png)
   skin/gingerbread/images/identity-ev-hdpi.png          (gingerbread/images/identity-ev-hdpi.png)
   skin/gingerbread/images/unlocked-hdpi.png             (gingerbread/images/unlocked-hdpi.png)
   skin/gingerbread/images/locked-hdpi.png               (gingerbread/images/locked-hdpi.png)
   skin/gingerbread/images/close-default-hdpi.png        (gingerbread/images/close-default-hdpi.png)
+  skin/gingerbread/images/close-default-tablet-hdpi.png (gingerbread/images/close-default-tablet-hdpi.png)
   skin/gingerbread/images/close-active-hdpi.png         (gingerbread/images/close-active-hdpi.png)
+  skin/gingerbread/images/close-active-tablet-hdpi.png  (gingerbread/images/close-active-tablet-hdpi.png)
+  skin/gingerbread/images/close-background-hdpi.png     (gingerbread/images/close-background-hdpi.png)
   skin/gingerbread/images/close-inactive-tab-hdpi.png   (gingerbread/images/close-inactive-tab-hdpi.png)
+  skin/gingerbread/images/close-inactive-tab-tablet-hdpi.png   (gingerbread/images/close-inactive-tab-tablet-hdpi.png)
   skin/gingerbread/images/check-30.png                  (gingerbread/images/check-30.png)
   skin/gingerbread/images/search-glass-30.png           (gingerbread/images/search-glass-30.png)
   skin/gingerbread/images/search-clear-30.png           (gingerbread/images/search-clear-30.png)
   skin/gingerbread/images/section-expanded-16.png       (images/section-expanded-16.png)
   skin/gingerbread/images/section-collapsed-16.png      (images/section-collapsed-16.png)
   skin/gingerbread/images/task-switch-hdpi.png          (gingerbread/images/task-switch-hdpi.png)
   skin/gingerbread/images/task-close-hdpi.png           (gingerbread/images/task-close-hdpi.png)
   skin/gingerbread/images/task-back-hdpi.png            (gingerbread/images/task-back-hdpi.png)
@@ -334,38 +346,44 @@ chrome.jar:
   skin/honeycomb/images/panelrow-selected-hdpi.png    (honeycomb/images/panelrow-selected-hdpi.png)
   skin/honeycomb/images/forward-default-hdpi.png        (honeycomb/images/forward-default-hdpi.png)
   skin/honeycomb/images/downloads-default-hdpi.png      (honeycomb/images/downloads-default-hdpi.png)
   skin/honeycomb/images/settings-default-hdpi.png       (honeycomb/images/settings-default-hdpi.png)
   skin/honeycomb/images/preferences-default-hdpi.png    (honeycomb/images/preferences-default-hdpi.png)
   skin/honeycomb/images/row-header-bg.png             (honeycomb/images/row-header-bg.png)
   skin/honeycomb/images/console-default-hdpi.png        (honeycomb/images/console-default-hdpi.png)
   skin/honeycomb/images/newtab-default-hdpi.png         (honeycomb/images/newtab-default-hdpi.png)
+  skin/honeycomb/images/newtab-default-tablet-hdpi.png (honeycomb/images/newtab-default-tablet-hdpi.png)
   skin/honeycomb/images/tab-active-hdpi.png           (honeycomb/images/tab-active-hdpi.png)
   skin/honeycomb/images/tab-inactive-hdpi.png         (honeycomb/images/tab-inactive-hdpi.png)
   skin/honeycomb/images/tab-closed-hdpi.png           (honeycomb/images/tab-closed-hdpi.png)
   skin/honeycomb/images/tab-reopen-hdpi.png           (honeycomb/images/tab-reopen-hdpi.png)
+  skin/honeycomb/images/tab-reopen-tablet-hdpi.png    (honeycomb/images/tab-reopen-tablet-hdpi.png)
   skin/honeycomb/images/remotetabs-48.png             (honeycomb/images/remotetabs-48.png)
   skin/honeycomb/images/remotetabs-32.png             (honeycomb/images/remotetabs-32.png)
   skin/honeycomb/images/mozilla-32.png                (images/mozilla-32.png)
   skin/honeycomb/images/toggle-on.png                 (honeycomb/images/toggle-on.png)
   skin/honeycomb/images/toggle-off.png                (honeycomb/images/toggle-off.png)
   skin/honeycomb/images/sidebarbutton-active-hdpi.png (honeycomb/images/sidebarbutton-active-hdpi.png)
   skin/honeycomb/images/previous-hdpi.png             (honeycomb/images/previous-hdpi.png)
   skin/honeycomb/images/previous-disabled-hdpi.png    (honeycomb/images/previous-disabled-hdpi.png)
   skin/honeycomb/images/next-hdpi.png                 (honeycomb/images/next-hdpi.png)
   skin/honeycomb/images/next-disabled-hdpi.png        (honeycomb/images/next-disabled-hdpi.png)
   skin/honeycomb/images/identity-default-hdpi.png     (honeycomb/images/identity-default-hdpi.png)
   skin/honeycomb/images/identity-ssl-hdpi.png         (honeycomb/images/identity-ssl-hdpi.png)
   skin/honeycomb/images/identity-ev-hdpi.png          (honeycomb/images/identity-ev-hdpi.png)
   skin/honeycomb/images/unlocked-hdpi.png             (honeycomb/images/unlocked-hdpi.png)
   skin/honeycomb/images/locked-hdpi.png               (honeycomb/images/locked-hdpi.png)
   skin/honeycomb/images/close-default-hdpi.png        (honeycomb/images/close-default-hdpi.png)
+  skin/honeycomb/images/close-default-tablet-hdpi.png (honeycomb/images/close-default-tablet-hdpi.png)
   skin/honeycomb/images/close-active-hdpi.png         (honeycomb/images/close-active-hdpi.png)
+  skin/honeycomb/images/close-active-tablet-hdpi.png  (honeycomb/images/close-active-tablet-hdpi.png)
+  skin/honeycomb/images/close-background-hdpi.png     (honeycomb/images/close-background-hdpi.png)
   skin/honeycomb/images/close-inactive-tab-hdpi.png   (honeycomb/images/close-inactive-tab-hdpi.png)
+  skin/honeycomb/images/close-inactive-tab-tablet-hdpi.png   (honeycomb/images/close-inactive-tab-tablet-hdpi.png)
   skin/honeycomb/images/check-30.png                  (images/check-30.png)
   skin/honeycomb/images/check-selected-hdpi.png       (honeycomb/images/check-selected-hdpi.png)
   skin/honeycomb/images/check-unselected-hdpi.png     (honeycomb/images/check-unselected-hdpi.png)
   skin/honeycomb/images/search-glass-30.png           (honeycomb/images/search-glass-30.png)
   skin/honeycomb/images/search-clear-30.png           (honeycomb/images/search-clear-30.png)
   skin/honeycomb/images/section-expanded-16.png       (images/section-expanded-16.png)
   skin/honeycomb/images/section-collapsed-16.png      (images/section-collapsed-16.png)
   skin/honeycomb/images/task-switch-hdpi.png          (honeycomb/images/task-switch-hdpi.png)
new file mode 100644
--- /dev/null
+++ b/mobile/themes/core/tablet.css
@@ -0,0 +1,144 @@
+
+.spacer-actionbar,
+.button-actionbar {
+  visibility: collapse;
+}
+
+.button-actionbar[disabled="true"] {
+  opacity: 0.5;
+}
+
+%ifndef honeycomb
+.button-actionbar:hover:active {
+  background-color: #8db8d8;
+}
+%endif
+
+#toolbar-main[tablet="true"] > .spacer-actionbar,
+#toolbar-main[tablet="true"] > .button-actionbar {
+  visibility: visible;
+}
+#toolbar-main[tablet="true"] > #tool-tabs {
+  visibility: collapse;
+}
+
+#controls-scrollbox[tablet="true"] > #controls-sidebar {
+  visibility: collapse;
+}
+
+#tabs-spacer[tablet="true"]  {
+  visibility: collapse;
+}
+
+#tabs-container[tablet="true"] {
+  -moz-border-end: 0px;
+  background: #000;
+}
+
+#tabs[tablet="true"] > .tabs-scrollbox {
+  max-width: 200px;
+  -moz-box-orient: vertical;
+}
+
+#tabs[tablet="true"] .tabs-list {
+}
+
+#tabs[tablet="true"] .tabs-list {
+  width: 200px;
+  -moz-column-width: auto;
+  -moz-padding-end: 0px;
+  -moz-padding-start: 0px;
+}
+
+documenttab[tablet="true"] {
+  /* Use px instead of mozmm because tab bar items have hard-coded pixel sizes */
+  width: 200px;
+  padding: 12px;
+  -moz-margin-start: 0px;
+  background: none;
+}
+
+documenttab[tablet="true"][selected="true"] {
+  background: none;
+}
+
+.documenttab-thumbnail[tablet="true"] {
+  border-radius: @border_radius_tiny@;
+}
+
+.documenttab-close-container[tablet="true"] {
+  left: 0px;
+  -moz-margin-end: 0px;
+  -moz-margin-start: 135px;
+  -moz-box-pack: end;
+  -moz-box-align: center;
+}
+
+documenttab[selected="true"] > stack > .documenttab-close-container[tablet="true"] {
+  background: url("chrome://browser/skin/images/close-background-hdpi.png");
+  background-repeat: no-repeat;
+  background-position: right;
+  -moz-margin-end: -12px;
+  padding-right: 2px;
+}
+
+.documenttab-close[tablet="true"] {
+  width: 30px;
+  height: 44px;
+  list-style-image: url("chrome://browser/skin/images/close-inactive-tab-tablet-hdpi.png");
+}
+
+documenttab[selected="true"] > stack > hbox > .documenttab-close[tablet="true"] {
+  list-style-image: url("chrome://browser/skin/images/close-default-tablet-hdpi.png");
+}
+
+.documenttab-close-container:hover:active > .documenttab-close[tablet="true"] {
+  list-style-image: url("chrome://browser/skin/images/close-active-tablet-hdpi.png");
+}
+
+.documenttab-reload[tablet="true"] {
+  background: url("chrome://browser/skin/images/tab-reopen-tablet-hdpi.png");
+}
+
+#newtab-button[tablet="true"] {
+  list-style-image: url("images/newtab-default-tablet-hdpi.png");
+}
+
+@media (@orientation@: portrait) {
+  #toolbar-main[tablet="true"] > #tool-tabs {
+    visibility: visible;
+  }
+
+  #controls-scrollbox[tablet="true"] > #tabs-sidebar {
+    border: none;
+%ifdef honeycomb
+    top: @touch_button_xlarge@;
+%else
+    top: -moz-calc(@touch_button_xlarge@ + @margin_normal@);
+%endif
+    visibility: collapse;
+  }
+
+  #controls-scrollbox[tablet="true"] > #tabs-sidebar:-moz-locale-dir(ltr) {
+    left: 0;
+  }
+  #controls-scrollbox[tablet="true"] > #tabs-sidebar:-moz-locale-dir(rtl) {
+    right: 0;
+  }
+
+  #controls-scrollbox[tablet="true"] > #tabs-sidebar[open] {
+    position: fixed;
+    visibility: visible;
+  }
+}
+
+%ifndef honeycomb
+@media (min-width: @tablet_panel_minwidth@) {
+  #awesome-panels {
+    -moz-box-shadow: 0px 0px @shadow_width_small@ black;
+  }
+  #search-engines-popup {
+    max-width: -moz-calc(@tablet_panel_minwidth@);
+  }
+}
+%endif
--- a/modules/libpr0n/src/Makefile.in
+++ b/modules/libpr0n/src/Makefile.in
@@ -73,13 +73,14 @@ LOCAL_INCLUDES += \
 			-I$(topsrcdir)/content/svg/content/src \
 			-I$(topsrcdir)/content/base/src \
 			$(NULL)
 
 # Because VectorImage.cpp includes nsSVGUtils.h and nsSVGEffects.h
 LOCAL_INCLUDES += -I$(topsrcdir)/layout/svg/base/src
 
 include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
 # Because imgFrame.cpp includes "cairo.h"
 CXXFLAGS += $(MOZ_CAIRO_CFLAGS)
 
 
--- a/modules/libpr0n/src/RasterImage.cpp
+++ b/modules/libpr0n/src/RasterImage.cpp
@@ -37,16 +37,17 @@
  * 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 "base/histogram.h"
 #include "nsComponentManagerUtils.h"
 #include "imgIContainerObserver.h"
 #include "ImageErrors.h"
 #include "Decoder.h"
 #include "imgIDecoderObserver.h"
 #include "RasterImage.h"
 #include "nsIInterfaceRequestor.h"
 #include "nsIInterfaceRequestorUtils.h"
@@ -186,16 +187,17 @@ RasterImage::RasterImage(imgStatusTracke
   mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
   mAnim(nsnull),
   mLoopCount(-1),
   mObserver(nsnull),
   mLockCount(0),
   mDecoder(nsnull),
   mWorker(nsnull),
   mBytesDecoded(0),
+  mDecodeCount(0),
 #ifdef DEBUG
   mFramesNotified(0),
 #endif
   mHasSize(PR_FALSE),
   mDecodeOnDraw(PR_FALSE),
   mMultipart(PR_FALSE),
   mDiscardable(PR_FALSE),
   mHasSourceData(PR_FALSE),
@@ -203,16 +205,17 @@ RasterImage::RasterImage(imgStatusTracke
   mHasBeenDecoded(PR_FALSE),
   mWorkerPending(PR_FALSE),
   mInDecoder(PR_FALSE),
   mAnimationFinished(PR_FALSE)
 {
   // Set up the discard tracker node.
   mDiscardTrackerNode.curr = this;
   mDiscardTrackerNode.prev = mDiscardTrackerNode.next = nsnull;
+  Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(0);
 
   // Statistics
   num_containers++;
 }
 
 //******************************************************************************
 RasterImage::~RasterImage()
 {
@@ -2192,16 +2195,22 @@ RasterImage::InitDecoder(bool aDoSizeDec
   nsCOMPtr<imgIDecoderObserver> observer(do_QueryReferent(mObserver));
   mDecoder->SetSizeDecode(aDoSizeDecode);
   mDecoder->SetDecodeFlags(mFrameDecodeFlags);
   mDecoder->Init(this, observer);
   CONTAINER_ENSURE_SUCCESS(mDecoder->GetDecoderError());
 
   // Create a decode worker
   mWorker = new imgDecodeWorker(this);
+
+  if (!aDoSizeDecode) {
+    Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Subtract(mDecodeCount);
+    mDecodeCount++;
+    Telemetry::GetHistogramById(Telemetry::IMAGE_DECODE_COUNT)->Add(mDecodeCount);
+  }
   CONTAINER_ENSURE_TRUE(mWorker, NS_ERROR_OUT_OF_MEMORY);
 
   return NS_OK;
 }
 
 // Flushes, closes, and nulls-out a decoder. Cleans up any related decoding
 // state. It is an error to call this function when there is no initialized
 // decoder.
--- a/modules/libpr0n/src/RasterImage.h
+++ b/modules/libpr0n/src/RasterImage.h
@@ -498,16 +498,20 @@ private: // data
   friend class imgDecodeWorker;
   friend class DiscardTracker;
 
   // Decoder and friends
   nsRefPtr<Decoder>              mDecoder;
   nsRefPtr<imgDecodeWorker>      mWorker;
   PRUint32                       mBytesDecoded;
 
+  // How many times we've decoded this image.
+  // This is currently only used for statistics
+  PRInt32                        mDecodeCount;
+
 #ifdef DEBUG
   PRUint32                       mFramesNotified;
 #endif
 
   // Boolean flags (clustered together to conserve space):
   PRPackedBool               mHasSize:1;       // Has SetSize() been called?
   PRPackedBool               mDecodeOnDraw:1;  // Decoding on draw?
   PRPackedBool               mMultipart:1;     // Multipart?
--- a/netwerk/base/src/nsIOService.cpp
+++ b/netwerk/base/src/nsIOService.cpp
@@ -229,17 +229,17 @@ nsIOService::Init()
     }
     
     // Register for profile change notifications
     nsCOMPtr<nsIObserverService> observerService =
         mozilla::services::GetObserverService();
     if (observerService) {
         observerService->AddObserver(this, kProfileChangeNetTeardownTopic, PR_TRUE);
         observerService->AddObserver(this, kProfileChangeNetRestoreTopic, PR_TRUE);
-        observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, PR_TRUE);
+        observerService->AddObserver(this, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID, PR_TRUE);
         observerService->AddObserver(this, NS_NETWORK_LINK_TOPIC, PR_TRUE);
     }
     else
         NS_WARNING("failed to get observer service");
         
     NS_TIME_FUNCTION_MARK("Registered observers");
 
     // Get the allocator ready
@@ -964,17 +964,17 @@ nsIOService::Observe(nsISupports *subjec
         if (mOfflineForProfileChange) {
             mOfflineForProfileChange = PR_FALSE;
             if (!mManageOfflineStatus ||
                 NS_FAILED(TrackNetworkLinkStatusForOffline())) {
                 SetOffline(PR_FALSE);
             }
         } 
     }
-    else if (!strcmp(topic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
+    else if (!strcmp(topic, NS_XPCOM_WILL_SHUTDOWN_OBSERVER_ID)) {
         // Remember we passed XPCOM shutdown notification to prevent any
         // changes of the offline status from now. We must not allow going
         // online after this point.
         mShutdown = PR_TRUE;
 
         SetOffline(PR_TRUE);
 
         // Break circular reference.
--- a/toolkit/components/build/nsToolkitCompsCID.h
+++ b/toolkit/components/build/nsToolkitCompsCID.h
@@ -76,16 +76,19 @@
     "@mozilla.org/autocomplete/search;1?name=history"
 
 #define NS_TYPEAHEADFIND_CONTRACTID \
     "@mozilla.org/typeaheadfind;1"
 
 #define NS_PARENTALCONTROLSSERVICE_CONTRACTID \
     "@mozilla.org/parental-controls-service;1"
 
+#define NS_URLCLASSIFIERPREFIXSET_CONTRACTID \
+  "@mozilla.org/url-classifier/prefixset;1"
+
 #define NS_URLCLASSIFIERDBSERVICE_CONTRACTID \
     "@mozilla.org/url-classifier/dbservice;1"
 
 #define NS_URLCLASSIFIERSTREAMUPDATER_CONTRACTID \
     "@mozilla.org/url-classifier/streamupdater;1"
 
 #define NS_URLCLASSIFIERUTILS_CONTRACTID \
     "@mozilla.org/url-classifier/utils;1"
@@ -151,17 +154,21 @@
 
 // {59648a91-5a60-4122-8ff2-54b839c84aed}
 #define NS_PARENTALCONTROLSSERVICE_CID \
 { 0x580530e5, 0x118c, 0x4bc7, { 0xab, 0x88, 0xbc, 0x2c, 0xd2, 0xb9, 0x72, 0x23 } }
 
 // {e7f70966-9a37-48d7-8aeb-35998f31090e}
 #define NS_TYPEAHEADFIND_CID \
 { 0xe7f70966, 0x9a37, 0x48d7, { 0x8a, 0xeb, 0x35, 0x99, 0x8f, 0x31, 0x09, 0x0e} }
-  
+
+// {42ef1d52-3351-4973-98f8-d18f089bccfa}
+#define NS_URLCLASSIFIERPREFIXSET_CID \
+{ 0x42ef1d52, 0x3351, 0x4973, { 0x98, 0xf8, 0xd1, 0x8f, 0x08, 0x9b, 0xcc, 0xfa} }
+
 // {5eb7c3c1-ec1f-4007-87cc-eefb37d68ce6}
 #define NS_URLCLASSIFIERDBSERVICE_CID \
 { 0x5eb7c3c1, 0xec1f, 0x4007, { 0x87, 0xcc, 0xee, 0xfb, 0x37, 0xd6, 0x8c, 0xe6} }
 
 // {c2be6dc0-ef1e-4abd-86a2-4f864ddc57f6}
 #define NS_URLCLASSIFIERSTREAMUPDATER_CID \
 { 0xc2be6dc0, 0xef1e, 0x4abd, { 0x86, 0xa2, 0x4f, 0x86, 0x4d, 0xdc, 0x57, 0xf6} }
 
--- a/toolkit/components/build/nsToolkitCompsModule.cpp
+++ b/toolkit/components/build/nsToolkitCompsModule.cpp
@@ -58,16 +58,17 @@
 #endif
 
 #include "nsTypeAheadFind.h"
 
 #ifdef MOZ_URL_CLASSIFIER
 #include "nsUrlClassifierDBService.h"
 #include "nsUrlClassifierStreamUpdater.h"
 #include "nsUrlClassifierUtils.h"
+#include "nsUrlClassifierPrefixSet.h"
 #endif
 
 #ifdef MOZ_FEEDS
 #include "nsScriptableUnescapeHTML.h"
 #endif
 
 #include "nsBrowserStatusFilter.h"
 
@@ -89,16 +90,17 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsAlertsS
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsDownloadManager,
                                          nsDownloadManager::GetSingleton) 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsDownloadProxy)
 #endif
 
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsTypeAheadFind)
 
 #ifdef MOZ_URL_CLASSIFIER
+NS_GENERIC_FACTORY_CONSTRUCTOR(nsUrlClassifierPrefixSet)
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsUrlClassifierStreamUpdater)
 NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsUrlClassifierUtils, Init)
 
 static nsresult
 nsUrlClassifierDBServiceConstructor(nsISupports *aOuter, REFNSIID aIID,
                                     void **aResult)
 {
     nsresult rv;
@@ -133,16 +135,17 @@ NS_DEFINE_NAMED_CID(NS_PARENTALCONTROLSS
 #endif
 #ifdef MOZ_RDF
 NS_DEFINE_NAMED_CID(NS_DOWNLOADMANAGER_CID);
 NS_DEFINE_NAMED_CID(NS_DOWNLOAD_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_FIND_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_TYPEAHEADFIND_CID);
 #ifdef MOZ_URL_CLASSIFIER
+NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERPREFIXSET_CID);
 NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERDBSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERSTREAMUPDATER_CID);
 NS_DEFINE_NAMED_CID(NS_URLCLASSIFIERUTILS_CID);
 #endif
 #ifdef MOZ_FEEDS
 NS_DEFINE_NAMED_CID(NS_SCRIPTABLEUNESCAPEHTML_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_BROWSERSTATUSFILTER_CID);
@@ -159,16 +162,17 @@ static const mozilla::Module::CIDEntry k
 #endif
 #ifdef MOZ_RDF
   { &kNS_DOWNLOADMANAGER_CID, false, NULL, nsDownloadManagerConstructor },
   { &kNS_DOWNLOAD_CID, false, NULL, nsDownloadProxyConstructor },
 #endif
   { &kNS_FIND_SERVICE_CID, false, NULL, nsFindServiceConstructor },
   { &kNS_TYPEAHEADFIND_CID, false, NULL, nsTypeAheadFindConstructor },
 #ifdef MOZ_URL_CLASSIFIER
+  { &kNS_URLCLASSIFIERPREFIXSET_CID, false, NULL, nsUrlClassifierPrefixSetConstructor },
   { &kNS_URLCLASSIFIERDBSERVICE_CID, false, NULL, nsUrlClassifierDBServiceConstructor },
   { &kNS_URLCLASSIFIERSTREAMUPDATER_CID, false, NULL, nsUrlClassifierStreamUpdaterConstructor },
   { &kNS_URLCLASSIFIERUTILS_CID, false, NULL, nsUrlClassifierUtilsConstructor },
 #endif
 #ifdef MOZ_FEEDS
   { &kNS_SCRIPTABLEUNESCAPEHTML_CID, false, NULL, nsScriptableUnescapeHTMLConstructor },
 #endif
   { &kNS_BROWSERSTATUSFILTER_CID, false, NULL, nsBrowserStatusFilterConstructor },
@@ -187,16 +191,17 @@ static const mozilla::Module::ContractID
 #endif
 #ifdef MOZ_RDF
   { NS_DOWNLOADMANAGER_CONTRACTID, &kNS_DOWNLOADMANAGER_CID },
   { NS_TRANSFER_CONTRACTID, &kNS_DOWNLOAD_CID },
 #endif
   { NS_FIND_SERVICE_CONTRACTID, &kNS_FIND_SERVICE_CID },
   { NS_TYPEAHEADFIND_CONTRACTID, &kNS_TYPEAHEADFIND_CID },
 #ifdef MOZ_URL_CLASSIFIER
+  { NS_URLCLASSIFIERPREFIXSET_CONTRACTID, &kNS_URLCLASSIFIERPREFIXSET_CID },
   { NS_URLCLASSIFIERDBSERVICE_CONTRACTID, &kNS_URLCLASSIFIERDBSERVICE_CID },
   { NS_URICLASSIFIERSERVICE_CONTRACTID, &kNS_URLCLASSIFIERDBSERVICE_CID },
   { NS_URLCLASSIFIERSTREAMUPDATER_CONTRACTID, &kNS_URLCLASSIFIERSTREAMUPDATER_CID },
   { NS_URLCLASSIFIERUTILS_CONTRACTID, &kNS_URLCLASSIFIERUTILS_CID },
 #endif
 #ifdef MOZ_FEEDS
   { NS_SCRIPTABLEUNESCAPEHTML_CONTRACTID, &kNS_SCRIPTABLEUNESCAPEHTML_CID },
 #endif
--- a/toolkit/components/telemetry/Telemetry.cpp
+++ b/toolkit/components/telemetry/Telemetry.cpp
@@ -144,17 +144,17 @@ GetHistogramByEnumId(Telemetry::ID id, H
   if (h) {
     *ret = h;
     return NS_OK;
   }
 
   const TelemetryHistogram &p = gHistograms[id];
   nsresult rv = HistogramGet(p.id, p.min, p.max, p.bucketCount, p.histogramType, &h);
   if (NS_FAILED(rv))
-    return NS_ERROR_FAILURE;
+    return rv;
 
   *ret = knownHistograms[id] = h;
   return NS_OK;
 }
 
 bool
 FillRanges(JSContext *cx, JSObject *array, Histogram *h)
 {
@@ -406,16 +406,24 @@ Accumulate(ID aHistogram, PRUint32 aSamp
     return;
   }
   Histogram *h;
   nsresult rv = GetHistogramByEnumId(aHistogram, &h);
   if (NS_SUCCEEDED(rv))
     h->Add(aSample);
 }
 
+base::Histogram*
+GetHistogramById(ID id)
+{
+  Histogram *h = NULL;
+  GetHistogramByEnumId(id, &h);
+  return h;
+}
+
 } // namespace Telemetry
 } // namespace mozilla
 
 NSMODULE_DEFN(nsTelemetryModule) = &kTelemetryModule;
 
 /**
  * The XRE_TelemetryAdd function is to be used by embedding applications
  * that can't use mozilla::Telemetry::Accumulate() directly.
--- a/toolkit/components/telemetry/Telemetry.h
+++ b/toolkit/components/telemetry/Telemetry.h
@@ -37,16 +37,20 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef Telemetry_h__
 #define Telemetry_h__
 
 #include "mozilla/TimeStamp.h"
 #include "mozilla/AutoRestore.h"
 
+namespace base {
+  class Histogram;
+}
+
 namespace mozilla {
 namespace Telemetry {
 
 enum ID {
 #define HISTOGRAM(name, a, b, c, d, e) name,
 
 #include "TelemetryHistograms.h"
 
@@ -57,16 +61,21 @@ HistogramCount
 /**
  * Adds sample to a histogram defined in TelemetryHistograms.h
  *
  * @param id - histogram id
  * @param sample - value to record.
  */
 void Accumulate(ID id, PRUint32 sample);
 
+/**
+ * Return a raw Histogram for direct manipulation for users who can not use Accumulate().
+ */
+base::Histogram* GetHistogramById(ID id);
+
 template<ID id>
 class AutoTimer {
 public:
   AutoTimer(MOZILLA_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
     : start(TimeStamp::Now())
   {
     MOZILLA_GUARD_OBJECT_NOTIFIER_INIT;
   }
--- a/toolkit/components/telemetry/TelemetryHistograms.h
+++ b/toolkit/components/telemetry/TelemetryHistograms.h
@@ -92,16 +92,17 @@ HISTOGRAM(GDI_INITFONTLIST_TOTAL, 1, 300
 HISTOGRAM(MAC_INITFONTLIST_TOTAL, 1, 30000, 10, EXPONENTIAL, "gfxMacPlatformFontList::InitFontList Total (ms)")
 #endif
 HISTOGRAM(SHUTDOWN_OK, 0, 1, 2, BOOLEAN, "Did the browser start after a successful shutdown")
 
 HISTOGRAM(IMAGE_DECODE_LATENCY, 50,  5000000, 100, EXPONENTIAL, "Time spent decoding an image chunk (us)")
 HISTOGRAM(IMAGE_DECODE_TIME,    50, 50000000, 100, EXPONENTIAL, "Time spent decoding an image (us)")
 HISTOGRAM(IMAGE_DECODE_ON_DRAW_LATENCY,  50, 50000000, 100, EXPONENTIAL, "Time from starting a decode to it showing up on the screen (us)")
 HISTOGRAM(IMAGE_DECODE_CHUNKS, 1,  500, 50, EXPONENTIAL, "Number of chunks per decode attempt")
+HISTOGRAM(IMAGE_DECODE_COUNT, 1,  500, 50, EXPONENTIAL, "Decode count")
 
 /**
  * Networking telemetry
  */
 HISTOGRAM(TOTAL_CONTENT_PAGE_LOAD_TIME, 100, 10000, 100, EXPONENTIAL, "HTTP: Total page load time (ms)")
 HISTOGRAM(HTTP_SUBITEM_OPEN_LATENCY_TIME, 1, 30000, 50, EXPONENTIAL, "HTTP subitem: Page start -> subitem open() (ms)")
 HISTOGRAM(HTTP_SUBITEM_FIRST_BYTE_LATENCY_TIME, 1, 30000, 50, EXPONENTIAL, "HTTP subitem: Page start -> first byte received for subitem reply (ms)")
 HISTOGRAM(HTTP_REQUEST_PER_PAGE, 1, 1000, 50, EXPONENTIAL, "HTTP: Requests per page (count)")
@@ -163,16 +164,25 @@ HISTOGRAM(MOZ_SQLITE_COOKIES_WRITE_B, 1,
 HISTOGRAM(MOZ_SQLITE_URLCLASSIFIER_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
 HISTOGRAM(MOZ_SQLITE_OTHER_WRITE_B, 1, 32768, 3, LINEAR, "SQLite write (bytes)")
 HISTOGRAM(STARTUP_MEASUREMENT_ERRORS, 1, 3, 4, LINEAR, "Flags errors in startup calculation()")
 HISTOGRAM(NETWORK_DISK_CACHE_OPEN, 1, 10000, 10, EXPONENTIAL, "Time spent opening disk cache (ms)")
 HISTOGRAM(NETWORK_DISK_CACHE_TRASHRENAME, 1, 10000, 10, EXPONENTIAL, "Time spent renaming bad Cache to Cache.Trash (ms)")
 HISTOGRAM(NETWORK_DISK_CACHE_DELETEDIR, 1, 10000, 10, EXPONENTIAL, "Time spent deleting disk cache (ms)")
 
 /**
+ * Url-Classifier telemetry
+ */
+#ifdef MOZ_URL_CLASSIFIER
+HISTOGRAM(URLCLASSIFIER_PS_FILELOAD_TIME, 1, 1000, 10, EXPONENTIAL, "Time spent loading PrefixSet from file (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)")
+#endif
+
+/**
  * Places telemetry.
  */
 HISTOGRAM(PLACES_PAGES_COUNT, 1000, 150000, 20, EXPONENTIAL, "PLACES: Number of unique pages")
 HISTOGRAM(PLACES_BOOKMARKS_COUNT, 100, 8000, 15, EXPONENTIAL, "PLACES: Number of bookmarks")
 HISTOGRAM(PLACES_TAGS_COUNT, 1, 200, 10, EXPONENTIAL, "PLACES: Number of tags")
 HISTOGRAM(PLACES_FOLDERS_COUNT, 1, 200, 10, EXPONENTIAL, "PLACES: Number of folders")
 HISTOGRAM(PLACES_KEYWORDS_COUNT, 1, 200, 10, EXPONENTIAL, "PLACES: Number of keywords")
 HISTOGRAM(PLACES_SORTED_BOOKMARKS_PERC, 1, 100, 10, LINEAR, "PLACES: Percentage of bookmarks organized in folders")
--- a/toolkit/components/url-classifier/Makefile.in
+++ b/toolkit/components/url-classifier/Makefile.in
@@ -48,24 +48,26 @@ LIBRARY_NAME = urlclassifier_s
 XPIDL_MODULE = url-classifier
 LIBXUL_LIBRARY = 1
 FORCE_STATIC_LIB = 1
 
 XPIDLSRCS = \
   nsIUrlClassifierDBService.idl \
   nsIUrlClassifierHashCompleter.idl \
   nsIUrlClassifierStreamUpdater.idl \
+  nsIUrlClassifierPrefixSet.idl \
   nsIUrlClassifierUtils.idl \
   nsIUrlListManager.idl \
   $(NULL)
 
 CPPSRCS = \
   nsUrlClassifierDBService.cpp \
   nsUrlClassifierStreamUpdater.cpp \
   nsUrlClassifierUtils.cpp \
+  nsUrlClassifierPrefixSet.cpp \
   nsUrlClassifierProxies.cpp \
   $(NULL)
 
 LOCAL_INCLUDES = \
   -I$(srcdir)/../build \
   $(SQLITE_CFLAGS) \
   $(NULL)
 
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl
@@ -0,0 +1,21 @@
+#include "nsISupports.idl"
+#include "nsIFile.idl"
+
+interface nsIArray;
+
+[scriptable, uuid(42ef1d52-3351-4973-98f8-d18f089bccfa)]
+interface nsIUrlClassifierPrefixSet : nsISupports
+{
+  void setPrefixes([const, array, size_is(aLength)] in unsigned long aPrefixes,
+                   in unsigned long aLength);
+  void addPrefixes([const, array, size_is(aLength)] in unsigned long aPrefixes,
+                   in unsigned long aLength);
+  boolean contains(in unsigned long aPrefix);
+  boolean probe(in unsigned long aPrefix, in unsigned long aKey,
+                inout boolean aReady);
+  PRUint32 estimateSize();
+  PRUint32 getKey();
+  boolean isEmpty();
+  void loadFromFile(in nsIFile aFile);
+  void storeToFile(in nsIFile aFile);
+};
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp
@@ -19,16 +19,17 @@
  * Portions created by the Initial Developer are Copyright (C) 2006
  * the Initial Developer. All Rights Reserved.
  *
  * Contributor(s):
  *   Tony Chang <tony@ponderer.org> (original author)
  *   Brett Wilson <brettw@gmail.com>
  *   Dave Camp <dcamp@mozilla.com>
  *   David Dahl <ddahl@mozilla.com>
+ *   Gian-Carlo Pascutto <gpascutto@mozilla.com>
  *
  * Alternatively, the contents of this file may be used under the terms of
  * either the GNU General Public License Version 2 or later (the "GPL"), or
  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  * in which case the provisions of the GPL or the LGPL are applicable instead
  * of those above. If you wish to allow use of your version of this file only
  * under the terms of either the GPL or the LGPL, and not to allow others to
  * use your version of this file under the terms of the MPL, indicate your
@@ -69,16 +70,17 @@
 #include "nsString.h"
 #include "nsReadableUtils.h"
 #include "nsTArray.h"
 #include "nsNetUtil.h"
 #include "nsNetCID.h"
 #include "nsThreadUtils.h"
 #include "nsXPCOMStrings.h"
 #include "mozilla/Mutex.h"
+#include "mozilla/Telemetry.h"
 #include "prlog.h"
 #include "prprf.h"
 #include "prnetdb.h"
 #include "zlib.h"
 
 // Needed to interpert mozIStorageConnection::GetLastError
 #include <sqlite3.h>
 
@@ -132,16 +134,19 @@ static const PRLogModuleInfo *gUrlClassi
 #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"
@@ -156,19 +161,18 @@ static const PRLogModuleInfo *gUrlClassi
 #define GETHASH_TABLES_PREF     "urlclassifier.gethashtables"
 
 #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
 
-// MRU cache sizes for remembering clean lookups
-#define CLEAN_HOST_KEYS_SIZE 16
-#define CLEAN_FRAGMENTS_SIZE 32
+#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
@@ -185,16 +189,17 @@ nsIThread* nsUrlClassifierDBService::gDb
 
 // Once we've committed to shutting down, don't do work in the background
 // thread.
 static PRBool gShuttingDownThread = PR_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<nsCString>& tables)
 {
   tables.Clear();
@@ -268,16 +273,19 @@ struct nsUrlClassifierHash
   }
   const PRBool operator<(const self_type& hash) const {
     return memcmp(buf, hash.buf, sizeof(self_type)) < 0;
   }
   const PRBool StartsWith(const nsUrlClassifierHash<PARTIAL_LENGTH>& 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<const PRUint32*>(buf));
+  }
 };
 
 typedef nsUrlClassifierHash<DOMAIN_LENGTH> nsUrlClassifierDomainHash;
 typedef nsUrlClassifierHash<PARTIAL_LENGTH> nsUrlClassifierPartialHash;
 typedef nsUrlClassifierHash<COMPLETE_LENGTH> nsUrlClassifierCompleteHash;
 
 
 // -------------------------------------------------------------------------
@@ -475,16 +483,19 @@ public:
 
   // Read a certain number of rows adjacent to the requested rowid that
   // don't have complete hash data.
   nsresult ReadNoiseEntries(PRInt64 rowID,
                             PRUint32 numRequested,
                             PRBool before,
                             nsTArray<nsUrlClassifierEntry> &entries);
 
+  // Return an array with all Prefixes known
+  nsresult ReadPrefixes(nsTArray<PRUint32>& array, PRUint32 aKey);
+
 protected:
   nsresult ReadEntries(mozIStorageStatement *statement,
                        nsTArray<nsUrlClassifierEntry>& entries);
   nsUrlClassifierDBServiceWorker *mWorker;
   nsCOMPtr<mozIStorageConnection> mConnection;
 
   nsCOMPtr<mozIStorageStatement> mLookupWithIDStatement;
 
@@ -492,16 +503,18 @@ protected:
   nsCOMPtr<mozIStorageStatement> mUpdateStatement;
   nsCOMPtr<mozIStorageStatement> mDeleteStatement;
   nsCOMPtr<mozIStorageStatement> mExpireStatement;
 
   nsCOMPtr<mozIStorageStatement> mPartialEntriesStatement;
   nsCOMPtr<mozIStorageStatement> mPartialEntriesAfterStatement;
   nsCOMPtr<mozIStorageStatement> mLastPartialEntriesStatement;
   nsCOMPtr<mozIStorageStatement> mPartialEntriesBeforeStatement;
+
+  nsCOMPtr<mozIStorageStatement> mAllPrefixStatement;
 };
 
 nsresult
 nsUrlClassifierStore::Init(nsUrlClassifierDBServiceWorker *worker,
                            mozIStorageConnection *connection,
                            const nsACString& entriesName)
 {
   mWorker = worker;
@@ -548,16 +561,22 @@ nsUrlClassifierStore::Init(nsUrlClassifi
 
   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 domain, partial_data, complete_data FROM ")
+     + entriesName,
+     getter_AddRefs(mAllPrefixStatement));
+  NS_ENSURE_SUCCESS(rv, rv);
+
   return NS_OK;
 }
 
 void
 nsUrlClassifierStore::Close()
 {
   mLookupWithIDStatement = nsnull;
 
@@ -566,16 +585,18 @@ nsUrlClassifierStore::Close()
   mDeleteStatement = nsnull;
   mExpireStatement = nsnull;
 
   mPartialEntriesStatement = nsnull;
   mPartialEntriesAfterStatement = nsnull;
   mPartialEntriesBeforeStatement = nsnull;
   mLastPartialEntriesStatement = nsnull;
 
+  mAllPrefixStatement = nsnull;
+
   mConnection = nsnull;
 }
 
 
 PRBool
 nsUrlClassifierStore::ReadStatement(mozIStorageStatement* statement,
                                     nsUrlClassifierEntry& entry)
 {
@@ -988,38 +1009,68 @@ nsUrlClassifierSubStore::ExpireAddChunk(
 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<nsCString> &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<nsCString>& fragments);
+
+// Check for a canonicalized IP address.
+static PRBool 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
 {
 public:
   nsUrlClassifierDBServiceWorker();
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIURLCLASSIFIERDBSERVICE
   NS_DECL_NSIURLCLASSIFIERDBSERVICEWORKER
 
   // Initialize, called in the main thread
-  nsresult Init(PRInt32 gethashNoise);
+  nsresult Init(PRInt32 gethashNoise,
+                nsRefPtr<nsUrlClassifierPrefixSet> & prefSet);
 
   // Queue a lookup for the worker to perform, called in the main thread.
   nsresult QueueLookup(const nsACString& lookupKey,
                        nsIUrlClassifierLookupCallback* callback);
 
-  // Check if the key is on a known-clean host.
-  nsresult CheckCleanHost(const nsACString &lookupKey,
-                          PRBool *clean);
-
   // Handle any queued-up lookups.  We call this function during long-running
   // update operations to prevent lookups from blocking for too long.
   nsresult HandlePendingLookups();
 
 private:
   // No subclassing
   ~nsUrlClassifierDBServiceWorker();
 
@@ -1124,60 +1175,39 @@ private:
   nsresult ApplyUpdate();
 
   // Reset the in-progress update stream
   void ResetStream();
 
   // Reset the in-progress update
   void ResetUpdate();
 
-  // Reset the set of clean host keys and cached lookups.
-  void ResetLookupCache();
-
-  // 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
-  nsresult GetLookupFragments(const nsCSubstring& spec,
-                              nsTArray<nsCString>& fragments);
-
-  // Check for a canonicalized IP address.
-  PRBool 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
-  nsresult GetKey(const nsACString& spec, nsUrlClassifierDomainHash& hash);
-
-  // 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]
-  nsresult GetHostKeys(const nsACString &spec,
-                       nsTArray<nsCString> &hostKeys);
-
-// Read all relevant entries for the given URI into mCachedEntries.
-  nsresult CacheEntries(const nsCSubstring& spec);
-
   // 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<nsUrlClassifierLookupResult>& results);
 
   // 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<nsUrlClassifierLookupResult>& results);
 
+  // Construct a Prefix Set with known prefixes
+  nsresult LoadPrefixSet(nsCOMPtr<nsIFile> & aFile);
+  nsresult ConstructPrefixSet();
+
+  // Set the SQLite cache size
+  nsresult SetCacheSize(mozIStorageConnection * aConnection,
+                        PRInt32 aCacheSize);
+
   nsCOMPtr<nsIFile> mDBFile;
+  nsCOMPtr<nsIFile> mPSFile;
 
   nsCOMPtr<nsICryptoHash> 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<mozIStorageConnection> mConnection;
 
@@ -1255,33 +1285,18 @@ private:
   // Start time of the current update interval.  This will be reset
   // every time we apply the update.
   PRIntervalTime mUpdateStartTime;
 
   nsCOMPtr<nsICryptoHMAC> mHMAC;
   // The number of noise entries to add to the set of lookup results.
   PRInt32 mGethashNoise;
 
-  // We maintain an MRU cache of clean host keys (host keys with no
-  // entry in the db).
-  nsUrlClassifierFragmentSet mCleanHostKeys;
-
-  // The clean-host-key cache is updated in the worker thread, but
-  // checked in the main thread (to avoid posting lookup requests if
-  // not necessary).
-  Mutex mCleanHostKeysLock;
-
-  // We maintain an MRU cache of clean fragments (fragments with no
-  // entry in the db).
-  nsUrlClassifierFragmentSet mCleanFragments;
-
-  // The host keys from the last host to be checked for malware are
-  // cached for quicker lookup next time through.
-  nsCString mCachedHostKey;
-  nsTArray<nsUrlClassifierEntry> mCachedEntries;
+  // Set of prefixes known to be in the database
+  nsRefPtr<nsUrlClassifierPrefixSet> mPrefixSet;
 
   // Pending lookups are stored in a queue for processing.  The queue
   // is protected by mPendingLookupLock.
   Mutex mPendingLookupLock;
 
   class PendingLookup {
   public:
     nsCString mKey;
@@ -1310,55 +1325,57 @@ nsUrlClassifierDBServiceWorker::nsUrlCla
   , mInStream(PR_FALSE)
   , mPrimaryStream(PR_FALSE)
   , mHaveCachedLists(PR_FALSE)
   , mCachedListsTable(PR_UINT32_MAX)
   , mHaveCachedAddChunks(PR_FALSE)
   , mHaveCachedSubChunks(PR_FALSE)
   , mUpdateStartTime(0)
   , mGethashNoise(0)
-  , mCleanHostKeysLock("nsUrlClassifierDBServerWorker.mCleanHostKeysLock")
+  , mPrefixSet(0)
   , mPendingLookupLock("nsUrlClassifierDBServerWorker.mPendingLookupLock")
 {
 }
 
 nsUrlClassifierDBServiceWorker::~nsUrlClassifierDBServiceWorker()
 {
   NS_ASSERTION(!mConnection,
                "Db connection not closed, leaking memory!  Call CloseDb "
                "to close the connection.");
 }
 
 nsresult
-nsUrlClassifierDBServiceWorker::Init(PRInt32 gethashNoise)
+nsUrlClassifierDBServiceWorker::Init(PRInt32 gethashNoise,
+                                     nsRefPtr<nsUrlClassifierPrefixSet> & prefSet)
 {
   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);
 
-  if (!mCleanHostKeys.Init(CLEAN_HOST_KEYS_SIZE))
-    return NS_ERROR_OUT_OF_MEMORY;
-
-  if (!mCleanFragments.Init(CLEAN_FRAGMENTS_SIZE))
-    return NS_ERROR_OUT_OF_MEMORY;
+  rv = mPSFile->Append(NS_LITERAL_STRING(PREFIXSET_FILENAME));
+  NS_ENSURE_SUCCESS(rv, rv);
 
   ResetUpdate();
 
   mTableFreshness.Init();
 
   return NS_OK;
 }
 
@@ -1373,39 +1390,124 @@ nsUrlClassifierDBServiceWorker::QueueLoo
 
   lookup->mKey = spec;
   lookup->mCallback = callback;
 
   return NS_OK;
 }
 
 nsresult
-nsUrlClassifierDBServiceWorker::CheckCleanHost(const nsACString &spec,
-                                               PRBool *clean)
+nsUrlClassifierDBService::CheckClean(const nsACString &spec,
+                                     PRBool *clean)
 {
-  nsAutoTArray<nsCString, 2> lookupHosts;
-  nsresult rv = GetHostKeys(spec, lookupHosts);
+  Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_LOOKUP_TIME> timer;
+
+  // Get the set of fragments to look up.
+  nsTArray<nsCString> fragments;
+  nsresult rv = GetLookupFragments(spec, fragments);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  PRUint32 prefixkey;
+  rv = mPrefixSet->GetKey(&prefixkey);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  MutexAutoLock lock(mCleanHostKeysLock);
-
-  for (PRUint32 i = 0; i < lookupHosts.Length(); i++) {
-    if (!mCleanHostKeys.Has(lookupHosts[i])) {
+  *clean = PR_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);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    PRBool found;
+    PRBool ready = PR_FALSE;  /* opportunistic probe */
+    rv = mPrefixSet->Probe(codedkey, prefixkey, &ready, &found);
+    NS_ENSURE_SUCCESS(rv, rv);
+    LOG(("CheckClean Probed %X ready: %d found: %d ",
+         codedkey, ready, found));
+    if (found || !ready) {
       *clean = PR_FALSE;
-      return NS_OK;
     }
   }
 
-  *clean = PR_TRUE;
   return NS_OK;
 }
 
+static nsresult GetHostKeys(const nsACString &spec,
+                            nsTArray<nsCString> &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<nsCString> 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
-nsUrlClassifierDBServiceWorker::GetLookupFragments(const nsACString& spec,
-                                                   nsTArray<nsCString>& fragments)
+GetLookupFragments(const nsACString& spec,
+                   nsTArray<nsCString>& fragments)
 {
   fragments.Clear();
 
   nsACString::const_iterator begin, end, iter;
   spec.BeginReading(begin);
   spec.EndReading(end);
 
   iter = begin;
@@ -1497,100 +1599,47 @@ nsUrlClassifierDBServiceWorker::GetLooku
       fragments.AppendElement(key);
     }
   }
 
   return NS_OK;
 }
 
 nsresult
-nsUrlClassifierDBServiceWorker::CacheEntries(const nsACString& spec)
-{
-  nsAutoTArray<nsCString, 2> lookupHosts;
-  nsresult rv = GetHostKeys(spec, lookupHosts);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  // Build a unique string for this set of lookup hosts.
-  nsCAutoString hostKey;
-  for (PRUint32 i = 0; i < lookupHosts.Length(); i++) {
-    hostKey.Append(lookupHosts[i]);
-    hostKey.Append("|");
-  }
-
-  if (hostKey == mCachedHostKey) {
-    // mCachedHostKeys is valid for this set of lookup hosts.
-    return NS_OK;
-  }
-
-  mCachedEntries.Clear();
-  mCachedHostKey.Truncate();
-
-  PRUint32 prevLength = 0;
-  for (PRUint32 i = 0; i < lookupHosts.Length(); i++) {
-    // First, if this key has been checked since our last update and
-    // had no entries, we don't need to check the DB here.  We also do
-    // this check before posting the lookup to this thread, but in
-    // case multiple lookups are queued at the same time, it's worth
-    // checking again here.
-    {
-      MutexAutoLock lock(mCleanHostKeysLock);
-      if (mCleanHostKeys.Has(lookupHosts[i]))
-        continue;
-    }
-
-    // Read the entries for this lookup houst
-    nsUrlClassifierDomainHash hostKeyHash;
-    hostKeyHash.FromPlaintext(lookupHosts[i], mCryptoHash);
-    mMainStore.ReadAddEntries(hostKeyHash, mCachedEntries);
-
-    if (mCachedEntries.Length() == prevLength) {
-      // There were no entries in the db for this host key.  Go
-      // ahead and mark the host key as clean to help short-circuit
-      // future lookups.
-      MutexAutoLock lock(mCleanHostKeysLock);
-      mCleanHostKeys.Put(lookupHosts[i]);
-    } else {
-      prevLength = mCachedEntries.Length();
-    }
-  }
-
-  mCachedHostKey = hostKey;
-
-  return NS_OK;
-}
-
-nsresult
 nsUrlClassifierDBServiceWorker::Check(const nsACString& spec,
                                       nsTArray<nsUrlClassifierLookupResult>& results)
 {
-  // Read any entries that might apply to this URI into mCachedEntries
-  nsresult  rv = CacheEntries(spec);
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (mCachedEntries.Length() == 0) {
-    return NS_OK;
+  PRInt64 now = (PR_Now() / PR_USEC_PER_SEC);
+
+  // Get list of host keys to look up
+  nsAutoTArray<nsCString, 2> lookupHosts;
+  nsresult rv = GetHostKeys(spec, lookupHosts);
+
+  nsTArray<nsUrlClassifierEntry> 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<nsCString> fragments;
   rv = GetLookupFragments(spec, fragments);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  PRInt64 now = (PR_Now() / PR_USEC_PER_SEC);
-
   // Now check each lookup fragment against the entries in the DB.
   for (PRUint32 i = 0; i < fragments.Length(); i++) {
-    // If this fragment has been previously checked, ignore it.
-    if (mCleanFragments.Has(fragments[i]))
-      continue;
-
     nsUrlClassifierCompleteHash lookupHash;
     lookupHash.FromPlaintext(fragments[i], mCryptoHash);
 
-    PRBool foundMatch = PR_FALSE;
     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();
@@ -1613,27 +1662,20 @@ nsUrlClassifierDBServiceWorker::Check(co
           LOG(("No expiration time for this table.\n"));
           fresh = PR_FALSE;
         }
 
         // This is a confirmed result if we match a complete fragment in
         // an up-to-date table.
         result->mConfirmed = entry.mHaveComplete && fresh;
 
-        foundMatch = PR_TRUE;
         LOG(("Found a result.  complete=%d, fresh=%d",
              entry.mHaveComplete, fresh));
       }
     }
-
-    if (!foundMatch) {
-      // This fragment is clean, we don't need to bother checking it
-      // again until the next update.
-      mCleanFragments.Put(fragments[i]);
-    }
   }
 
   return NS_OK;
 }
 
 /**
  * Lookup up a key in the database is a two step process:
  *
@@ -1866,17 +1908,16 @@ nsUrlClassifierDBServiceWorker::GetTable
 
 nsresult
 nsUrlClassifierDBServiceWorker::GetTableName(PRUint32 tableId,
                                              nsACString& tableName)
 {
   mozStorageStatementScoper findScoper(mGetTableNameStatement);
   nsresult rv = mGetTableNameStatement->BindInt32ByIndex(0, tableId);
   NS_ENSURE_SUCCESS(rv, rv);
-
   PRBool exists;
   rv = mGetTableNameStatement->ExecuteStep(&exists);
   NS_ENSURE_SUCCESS(rv, rv);
   if (!exists) return NS_ERROR_FAILURE;
 
   return mGetTableNameStatement->GetUTF8String(0, tableName);
 }
 
@@ -1978,51 +2019,52 @@ nsUrlClassifierStore::UpdateEntry(nsUrlC
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = mUpdateStatement->Execute();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
-PRBool
-nsUrlClassifierDBServiceWorker::IsCanonicalizedIP(const nsACString& host)
+static PRBool
+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 PR_FALSE;
 }
 
-nsresult
-nsUrlClassifierDBServiceWorker::GetKey(const nsACString& spec,
-                                       nsUrlClassifierDomainHash& hash)
+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, mCryptoHash);
+    return hash.FromPlaintext(key, aCryptoHash);
   }
 
   nsTArray<nsCString> hostComponents;
   ParseString(PromiseFlatCString(host), '.', hostComponents);
 
   if (hostComponents.Length() < 2)
     return NS_ERROR_FAILURE;
 
@@ -2034,74 +2076,17 @@ nsUrlClassifierDBServiceWorker::GetKey(c
     lookupHost.Append(".");
   }
 
   lookupHost.Append(hostComponents[last - 1]);
   lookupHost.Append(".");
   lookupHost.Append(hostComponents[last]);
   lookupHost.Append("/");
 
-  return hash.FromPlaintext(lookupHost, mCryptoHash);
-}
-
-nsresult
-nsUrlClassifierDBServiceWorker::GetHostKeys(const nsACString &spec,
-                                            nsTArray<nsCString> &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<nsCString> 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;
+  return hash.FromPlaintext(lookupHost, aCryptoHash);
 }
 
 nsresult
 nsUrlClassifierDBServiceWorker::GetShaEntries(PRUint32 tableId,
                                               PRUint32 chunkType,
                                               PRUint32 chunkNum,
                                               PRUint32 domainSize,
                                               PRUint32 fragmentSize,
@@ -2244,17 +2229,17 @@ nsUrlClassifierDBServiceWorker::GetChunk
           return NS_ERROR_FAILURE;
         }
         iter++;
         entryStr = Substring(iter, end);
       } else {
         entryStr = lines[i];
       }
 
-      rv = GetKey(entryStr, entry->mKey);
+      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);
@@ -2857,28 +2842,16 @@ nsUrlClassifierDBServiceWorker::ResetUpd
   mUpdateWait = 0;
   mUpdateStatus = NS_OK;
   mUpdateObserver = nsnull;
   mUpdateClientKey.Truncate();
   mResetRequested = PR_FALSE;
   mUpdateTables.Clear();
 }
 
-void
-nsUrlClassifierDBServiceWorker::ResetLookupCache()
-{
-    mCachedHostKey.Truncate();
-    mCachedEntries.Clear();
-
-    mCleanFragments.Clear();
-
-    MutexAutoLock lock(mCleanHostKeysLock);
-    mCleanHostKeys.Clear();
-}
-
 NS_IMETHODIMP
 nsUrlClassifierDBServiceWorker::SetHashCompleter(const nsACString &tableName,
                                                  nsIUrlClassifierHashCompleter *completer)
 {
   return NS_ERROR_NOT_IMPLEMENTED;
 }
 
 NS_IMETHODIMP
@@ -3115,43 +3088,56 @@ nsUrlClassifierDBServiceWorker::FinishSt
                                   static_cast<PRUint32>(nextStreamDelay));
 
   ResetStream();
 
   return NS_OK;
 }
 
 nsresult
+nsUrlClassifierDBServiceWorker::SetCacheSize(
+  mozIStorageConnection * aConnection, PRInt32 aCacheSize)
+{
+  mozStorageStatementScoper scoper(mGetPageSizeStatement);
+  PRBool hasResult;
+  nsresult rv = mGetPageSizeStatement->ExecuteStep(&hasResult);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_ASSERTION(hasResult, "Should always be able to get page size from sqlite");
+  PRUint32 pageSize = mGetPageSizeStatement->AsInt32(0);
+  PRUint32 cachePages = aCacheSize / pageSize;
+  nsCAutoString cacheSizePragma("PRAGMA cache_size=");
+  cacheSizePragma.AppendInt(cachePages);
+  rv = aConnection->ExecuteSimpleSQL(cacheSizePragma);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
 nsUrlClassifierDBServiceWorker::SetupUpdate()
 {
   LOG(("nsUrlClassifierDBServiceWorker::SetupUpdate"));
   PRBool inProgress;
   nsresult rv = mConnection->GetTransactionInProgress(&inProgress);
   if (inProgress) {
     return NS_OK;
   }
 
   mUpdateStartTime = PR_IntervalNow();
 
   rv = mConnection->BeginTransaction();
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (gUpdateCacheSize > 0) {
-    PRBool hasResult;
-    rv = mGetPageSizeStatement->ExecuteStep(&hasResult);
+    rv = SetCacheSize(mConnection, gUpdateCacheSize);
     NS_ENSURE_SUCCESS(rv, rv);
-
-    NS_ASSERTION(hasResult, "Should always be able to get page size from sqlite");
-    PRUint32 pageSize = mGetPageSizeStatement->AsInt32(0);
-    PRUint32 cachePages = gUpdateCacheSize / pageSize;
-    nsCAutoString cacheSizePragma("PRAGMA cache_size=");
-    cacheSizePragma.AppendInt(cachePages);
-    rv = mConnection->ExecuteSimpleSQL(cacheSizePragma);
-    NS_ENSURE_SUCCESS(rv, rv);
-    mGrewCache = PR_TRUE;
+    if (gUpdateCacheSize != gLookupCacheSize) {
+      mGrewCache = PR_TRUE;
+    }
   }
 
   return NS_OK;
 }
 
 nsresult
 nsUrlClassifierDBServiceWorker::ApplyUpdate()
 {
@@ -3164,25 +3150,26 @@ nsUrlClassifierDBServiceWorker::ApplyUpd
       mUpdateStatus = FlushChunkLists();
       if (NS_SUCCEEDED(mUpdateStatus)) {
         mUpdateStatus = mConnection->CommitTransaction();
       }
     }
   }
 
   if (NS_SUCCEEDED(mUpdateStatus)) {
-    // We have modified the db, we can't trust the set of clean
-    // fragments or domains anymore.
-    ResetLookupCache();
+    // 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 = PR_FALSE;
     CloseDb();
     OpenDb();
   }
 
   mUpdateStartTime = 0;
 
   return NS_OK;
@@ -3244,22 +3231,25 @@ nsUrlClassifierDBServiceWorker::FinishUp
 
 NS_IMETHODIMP
 nsUrlClassifierDBServiceWorker::ResetDatabase()
 {
   LOG(("nsUrlClassifierDBServiceWorker::ResetDatabase [%p]", this));
   ClearCachedChunkLists();
 
   mTableFreshness.Clear();
-  ResetLookupCache();
 
   nsresult rv = CloseDb();
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = mPrefixSet->SetPrefixes(nsnull, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
+
   mDBFile->Remove(PR_FALSE);
+  mPSFile->Remove(PR_FALSE);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsUrlClassifierDBServiceWorker::CancelUpdate()
 {
   LOG(("CancelUpdate"));
@@ -3327,30 +3317,26 @@ nsUrlClassifierDBServiceWorker::CacheCom
   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);
   }
 
-  // Completions change entries in the DB, the cached set of entries is
-  // no longer valid.
-  mCachedHostKey.Truncate();
-  mCachedEntries.Clear();
-
   return NS_OK;
 }
 
 nsresult
 nsUrlClassifierDBServiceWorker::OpenDb()
 {
   // Connection already open, don't do anything.
-  if (mConnection)
+  if (mConnection) {
     return NS_OK;
+  }
 
   LOG(("Opening db\n"));
 
   nsresult rv;
   // open the connection
   nsCOMPtr<mozIStorageService> storageService =
     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -3393,16 +3379,24 @@ nsUrlClassifierDBServiceWorker::OpenDb()
       NS_ENSURE_SUCCESS(rv, rv);
     }
   }
 
   connection->SetGrowthIncrement(5 * 1024 * 1024, EmptyCString());
   rv = connection->ExecuteSimpleSQL(NS_LITERAL_CSTRING("PRAGMA synchronous=OFF"));
   NS_ENSURE_SUCCESS(rv, rv);
 
+  rv = connection->CreateStatement
+    (NS_LITERAL_CSTRING("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);
   }
 
   // Create the table
   rv = MaybeCreateTables(connection);
   NS_ENSURE_SUCCESS(rv, rv);
@@ -3447,26 +3441,235 @@ nsUrlClassifierDBServiceWorker::OpenDb()
   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);
 
-  rv = connection->CreateStatement
-    (NS_LITERAL_CSTRING("PRAGMA page_size"),
-     getter_AddRefs(mGetPageSizeStatement));
-  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);
+  NS_ENSURE_SUCCESS(rv, 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(nsTArray<PRUint32>& array,
+                                            PRUint32 aKey)
+{
+  mozStorageStatementScoper scoper(mAllPrefixStatement);
+  PRBool hasMoreData;
+  PRUint32 pcnt = 0;
+  PRUint32 fcnt = 0;
+
+#if defined(PR_LOGGING)
+  PRIntervalTime clockStart = 0;
+  if (LOG_ENABLED()) {
+    clockStart = PR_IntervalNow();
+  }
+#endif
+
+  while (NS_SUCCEEDED(mAllPrefixStatement->ExecuteStep(&hasMoreData)) && hasMoreData) {
+    PRUint32 prefixval;
+    PRUint32 domainval;
+    PRUint32 size;
+
+    const PRUint8 *blobdomain = mAllPrefixStatement->AsSharedBlob(0, &size);
+    if (!blobdomain || (size != DOMAIN_LENGTH))
+      return PR_FALSE;
+
+    domainval = *(reinterpret_cast<const PRUint32*>(blobdomain));
+
+    const PRUint8 *blobprefix = mAllPrefixStatement->AsSharedBlob(1, &size);
+    if (!blobprefix || (size != PARTIAL_LENGTH)) {
+      const PRUint8 *blobfull = mAllPrefixStatement->AsSharedBlob(2, &size);
+      if (!blobfull || (size != COMPLETE_LENGTH)) {
+        prefixval = domainval;
+        fcnt++;
+      } else {
+        prefixval = *(reinterpret_cast<const PRUint32*>(blobfull));
+      }
+    } else {
+      prefixval = *(reinterpret_cast<const PRUint32*>(blobprefix));
+    }
+
+    PRUint32 keyedVal;
+    nsresult rv = KeyedHash(prefixval, domainval, aKey, &keyedVal);
+    NS_ENSURE_SUCCESS(rv, rv);
+
+    array.AppendElement(keyedVal);
+    pcnt++;
+  }
+
+  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;
+}
+
+nsresult
+nsUrlClassifierDBServiceWorker::ConstructPrefixSet()
+{
+  Telemetry::AutoTimer<Telemetry::URLCLASSIFIER_PS_CONSTRUCT_TIME> timer;
+
+  PRUint32 key;
+  nsresult rv = mPrefixSet->GetKey(&key);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsTArray<PRUint32> array;
+  rv = mMainStore.ReadPrefixes(array, key);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+#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
+
+  // clear old tree
+  rv = mPrefixSet->SetPrefixes(nsnull, 0);
+  NS_ENSURE_SUCCESS(rv, rv);
+  if (array.IsEmpty()) {
+    // DB is empty, but put a sentinel to show that we looked
+    array.AppendElement(0);
+  }
+  // construct new one
+  rv = mPrefixSet->SetPrefixes(array.Elements(), array.Length());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  // store the new tree to disk
+  rv = mPrefixSet->StoreToFile(mPSFile);
+  NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store the prefixset");
+
+  return NS_OK;
+}
+
+nsresult
+nsUrlClassifierDBServiceWorker::LoadPrefixSet(nsCOMPtr<nsIFile> & aFile)
+{
+  PRBool empty;
+  nsresult rv = mPrefixSet->IsEmpty(&empty);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (!empty) {
+    LOG(("PrefixSet already loaded, not loading again"));
+    return NS_OK;
+  }
+
+  PRBool 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<Telemetry::URLCLASSIFIER_PS_FILELOAD_TIME> 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"));
+    ConstructPrefixSet();
+  }
+
+#ifdef DEBUG
+  PRUint32 size = 0;
+  rv = mPrefixSet->EstimateSize(&size);
+  LOG(("SB tree done, size = %d bytes\n", size));
+  NS_ENSURE_SUCCESS(rv, rv);
+#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"));
 
@@ -3689,16 +3892,19 @@ nsUrlClassifierLookupCallback::Completio
         // 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));
       }
     }
   }
 
   return NS_OK;
 }
 
 nsresult
@@ -3849,18 +4055,20 @@ nsUrlClassifierDBService::Init()
 
   // Force the storage service to be created on the main thread.
   nsresult rv;
   nsCOMPtr<mozIStorageService> storageService =
     do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID, &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
   // Force PSM to be loaded on the main thread.
-  nsCOMPtr<nsICryptoHash> hash =
-    do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+  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<nsIPrefBranch2> prefs = do_GetService(NS_PREFSERVICE_CONTRACTID);
 
   PRInt32 gethashNoise = 0;
   if (prefs) {
     PRBool tmpbool;
@@ -3889,16 +4097,19 @@ nsUrlClassifierDBService::Init()
     rv = prefs->GetIntPref(CONFIRM_AGE_PREF, &tmpint);
     PR_ATOMIC_SET(&gFreshnessGuarantee, NS_SUCCEEDED(rv) ? tmpint : CONFIRM_AGE_DEFAULT_SEC);
 
     prefs->AddObserver(CONFIRM_AGE_PREF, this, PR_FALSE);
 
     rv = prefs->GetIntPref(UPDATE_CACHE_SIZE_PREF, &tmpint);
     PR_ATOMIC_SET(&gUpdateCacheSize, NS_SUCCEEDED(rv) ? tmpint : UPDATE_CACHE_SIZE_DEFAULT);
 
+    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);
   }
@@ -3907,17 +4118,17 @@ nsUrlClassifierDBService::Init()
   rv = NS_NewThread(&gDbBackgroundThread);
   if (NS_FAILED(rv))
     return rv;
 
   mWorker = new nsUrlClassifierDBServiceWorker();
   if (!mWorker)
     return NS_ERROR_OUT_OF_MEMORY;
 
-  rv = mWorker->Init(gethashNoise);
+  rv = mWorker->Init(gethashNoise, mPrefixSet);
   if (NS_FAILED(rv)) {
     mWorker = nsnull;
     return rv;
   }
 
   // Proxy for calling the worker on the background thread
   mWorkerProxy = new UrlClassifierDBServiceWorkerProxy(mWorker);
 
@@ -3996,20 +4207,20 @@ nsUrlClassifierDBService::LookupURI(nsIU
     do_GetService(NS_URLCLASSIFIERUTILS_CONTRACTID);
   nsresult rv = utilsService->GetKeyForURI(uri, key);
   if (NS_FAILED(rv))
     return rv;
 
   if (forceLookup) {
     *didLookup = PR_TRUE;
   } else {
-    // Check if the URI is on a clean host.  If so, we don't need to
-    // bother queueing up a lookup, we can just return.
+    // Check if the URI is clean.  If so, we don't need to
+    // bother queueing up a lookup, we can just return.;
     PRBool clean;
-    rv = mWorker->CheckCleanHost(key, &clean);
+    rv = CheckClean(key, &clean);
     NS_ENSURE_SUCCESS(rv, rv);
 
     if (!clean) {
       nsCOMPtr<nsIPermissionManager> permissionManager =
         do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
 
       if (permissionManager) {
         PRUint32 perm;
@@ -4195,16 +4406,20 @@ nsUrlClassifierDBService::Observe(nsISup
     } else if (NS_LITERAL_STRING(CONFIRM_AGE_PREF).Equals(aData)) {
       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);
@@ -4240,16 +4455,17 @@ nsUrlClassifierDBService::Shutdown()
     prefs->RemoveObserver(CONFIRM_AGE_PREF, this);
   }
 
   nsresult rv;
   // First close the db connection.
   if (mWorker) {
     rv = mWorkerProxy->CancelUpdate();
     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post cancel update event");
+
     rv = mWorkerProxy->CloseDb();
     NS_ASSERTION(NS_SUCCEEDED(rv), "failed to post close db event");
   }
 
   mWorkerProxy = nsnull;
 
   LOG(("joining background thread"));
 
--- a/toolkit/components/url-classifier/nsUrlClassifierDBService.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h
@@ -40,20 +40,23 @@
 #ifndef nsUrlClassifierDBService_h_
 #define nsUrlClassifierDBService_h_
 
 #include <nsISupportsUtils.h>
 
 #include "nsID.h"
 #include "nsInterfaceHashtable.h"
 #include "nsIObserver.h"
+#include "nsUrlClassifierPrefixSet.h"
 #include "nsIUrlClassifierHashCompleter.h"
 #include "nsIUrlClassifierDBService.h"
 #include "nsIURIClassifier.h"
 #include "nsToolkitCompsCID.h"
+#include "nsICryptoHash.h"
+#include "nsICryptoHMAC.h"
 
 // The hash length for a domain key.
 #define DOMAIN_LENGTH 4
 
 // The hash length of a partial hash entry.
 #define PARTIAL_LENGTH 4
 
 // The hash length of a complete hash entry.
@@ -94,19 +97,23 @@ private:
   ~nsUrlClassifierDBService();
 
   // Disallow copy constructor
   nsUrlClassifierDBService(nsUrlClassifierDBService&);
 
   nsresult LookupURI(nsIURI* uri, nsIUrlClassifierCallback* c,
                      PRBool forceCheck, PRBool *didCheck);
 
-  // Close db connection and join the background thread if it exists. 
+  // Close db connection and join the background thread if it exists.
   nsresult Shutdown();
-  
+
+  // Check if the key is on a known-clean host.
+  nsresult CheckClean(const nsACString &lookupKey,
+                      PRBool *clean);
+
   nsCOMPtr<nsUrlClassifierDBServiceWorker> mWorker;
   nsCOMPtr<nsIUrlClassifierDBServiceWorker> mWorkerProxy;
 
   nsInterfaceHashtable<nsCStringHashKey, nsIUrlClassifierHashCompleter> mCompleters;
 
   // TRUE if the nsURIClassifier implementation should check for malware
   // uris on document loads.
   PRBool mCheckMalware;
@@ -119,15 +126,19 @@ private:
   // CancelUpdate()/FinishUpdate().  This is used to prevent competing
   // updates, not to determine whether an update is still being
   // processed.
   PRBool mInUpdate;
 
   // The list of tables that can use the default hash completer object.
   nsTArray<nsCString> mGethashWhitelist;
 
+  // Set of prefixes known to be in the database
+  nsRefPtr<nsUrlClassifierPrefixSet> mPrefixSet;
+  nsCOMPtr<nsICryptoHash> mHash;
+
   // Thread that we do the updates on.
   static nsIThread* gDbBackgroundThread;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsUrlClassifierDBService, NS_URLCLASSIFIERDBSERVICE_CID)
 
 #endif // nsUrlClassifierDBService_h_
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp
@@ -0,0 +1,439 @@
+//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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 <gpascutto@mozilla.com>
+ *   Mehdi Mulani <mars.martian+bugmail@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsAutoPtr.h"
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsTArray.h"
+#include "nsUrlClassifierPrefixSet.h"
+#include "nsIUrlClassifierPrefixSet.h"
+#include "nsIRandomGenerator.h"
+#include "nsIFile.h"
+#include "nsILocalFile.h"
+#include "nsToolkitCompsCID.h"
+#include "nsTArray.h"
+#include "nsThreadUtils.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/FileUtils.h"
+#include "prlog.h"
+
+using namespace mozilla;
+
+// NSPR_LOG_MODULES=UrlClassifierPrefixSet:5
+#if defined(PR_LOGGING)
+static const PRLogModuleInfo *gUrlClassifierPrefixSetLog = nsnull;
+#define LOG(args) PR_LOG(gUrlClassifierPrefixSetLog, PR_LOG_DEBUG, args)
+#define LOG_ENABLED() PR_LOG_TEST(gUrlClassifierPrefixSetLog, 4)
+#else
+#define LOG(args)
+#define LOG_ENABLED() (PR_FALSE)
+#endif
+
+NS_IMPL_THREADSAFE_ISUPPORTS1(nsUrlClassifierPrefixSet, nsIUrlClassifierPrefixSet)
+
+nsUrlClassifierPrefixSet::nsUrlClassifierPrefixSet()
+  : mPrefixSetLock("mPrefixSetLock"),
+    mSetIsReady(mPrefixSetLock, "mSetIsReady"),
+    mHasPrefixes(PR_FALSE),
+    mRandomKey(0)
+{
+#if defined(PR_LOGGING)
+  if (!gUrlClassifierPrefixSetLog)
+    gUrlClassifierPrefixSetLog = PR_NewLogModule("UrlClassifierPrefixSet");
+#endif
+
+  nsresult rv = InitKey();
+  if (NS_FAILED(rv)) {
+    LOG(("Failed to initialize PrefixSet"));
+  }
+}
+
+nsresult
+nsUrlClassifierPrefixSet::InitKey()
+{
+  nsCOMPtr<nsIRandomGenerator> 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)
+{
+  {
+    MutexAutoLock lock(mPrefixSetLock);
+    if (mHasPrefixes) {
+      LOG(("Clearing PrefixSet"));
+      mDeltas.Clear();
+      mIndexPrefixes.Clear();
+      mIndexStarts.Clear();
+      mHasPrefixes = PR_FALSE;
+    }
+  }
+  if (aLength > 0) {
+    // Ensure they are sorted before adding
+    nsTArray<PRUint32> prefixes;
+    prefixes.AppendElements(aArray, aLength);
+    prefixes.Sort();
+    AddPrefixes(prefixes.Elements(), prefixes.Length());
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::AddPrefixes(const PRUint32 * prefixes, PRUint32 aLength)
+{
+  if (aLength == 0) {
+    return NS_OK;
+  }
+
+  nsTArray<PRUint32> mNewIndexPrefixes(mIndexPrefixes);
+  nsTArray<PRUint32> mNewIndexStarts(mIndexStarts);
+  nsTArray<PRUint16> mNewDeltas(mDeltas);
+
+  mNewIndexPrefixes.AppendElement(prefixes[0]);
+  mNewIndexStarts.AppendElement(mNewDeltas.Length());
+
+  PRUint32 numOfDeltas = 0;
+  PRUint32 currentItem = prefixes[0];
+  for (PRUint32 i = 1; i < aLength; i++) {
+    if ((numOfDeltas >= DELTAS_LIMIT) ||
+          (prefixes[i] - currentItem >= MAX_INDEX_DIFF)) {
+      mNewIndexStarts.AppendElement(mNewDeltas.Length());
+      mNewIndexPrefixes.AppendElement(prefixes[i]);
+      numOfDeltas = 0;
+    } else {
+      PRUint16 delta = prefixes[i] - currentItem;
+      mNewDeltas.AppendElement(delta);
+      numOfDeltas++;
+    }
+    currentItem = prefixes[i];
+  }
+
+  mNewIndexPrefixes.Compact();
+  mNewIndexStarts.Compact();
+  mNewDeltas.Compact();
+
+  LOG(("Total number of indices: %d", mNewIndexPrefixes.Length()));
+  LOG(("Total number of deltas: %d", mNewDeltas.Length()));
+
+  MutexAutoLock lock(mPrefixSetLock);
+
+  // This just swaps some pointers
+  mIndexPrefixes.SwapElements(mNewIndexPrefixes);
+  mIndexStarts.SwapElements(mNewIndexStarts);
+  mDeltas.SwapElements(mNewDeltas);
+
+  mHasPrefixes = PR_TRUE;
+  mSetIsReady.NotifyAll();
+
+  return NS_OK;
+}
+
+PRUint32 nsUrlClassifierPrefixSet::BinSearch(PRUint32 start,
+                                             PRUint32 end,
+                                             PRUint32 target)
+{
+  while (start != end && end >= start) {
+    PRUint32 i = start + ((end - start) >> 1);
+    PRUint32 value = mIndexPrefixes[i];
+    if (value < target) {
+      start = i + 1;
+    } else if (value > target) {
+      end = i - 1;
+    } else {
+      return i;
+    }
+  }
+  return end;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::Contains(PRUint32 aPrefix, PRBool * aFound)
+{
+  *aFound = PR_FALSE;
+
+  if (!mHasPrefixes) {
+    return NS_OK;
+  }
+
+  PRUint32 target = aPrefix;
+
+  // We want to do a "Price is Right" binary search, that is, we want to find
+  // the index of the value either equal to the target or the closest value
+  // that is less than the target.
+  //
+  if (target < mIndexPrefixes[0]) {
+    return NS_OK;
+  }
+
+  // |binsearch| does not necessarily return the correct index (when the
+  // target is not found) but rather it returns an index at least one away
+  // from the correct index.
+  // Because of this, we need to check if the target lies before the beginning
+  // of the indices.
+
+  PRUint32 i = BinSearch(0, mIndexPrefixes.Length() - 1, target);
+  if (mIndexPrefixes[i] > target && i > 0) {
+    i--;
+  }
+
+  // Now search through the deltas for the target.
+  PRUint32 diff = target - mIndexPrefixes[i];
+  PRUint32 deltaIndex = mIndexStarts[i];
+  PRUint32 end = (i + 1 < mIndexStarts.Length()) ? mIndexStarts[i+1]
+                                                 : mDeltas.Length();
+  while (diff > 0 && deltaIndex < end) {
+    diff -= mDeltas[deltaIndex];
+    deltaIndex++;
+  }
+
+  if (diff == 0) {
+    *aFound = PR_TRUE;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::EstimateSize(PRUint32 * aSize)
+{
+  MutexAutoLock lock(mPrefixSetLock);
+  *aSize = sizeof(PRBool);
+  if (mHasPrefixes) {
+    *aSize += sizeof(PRUint16) * mDeltas.Length();
+    *aSize += sizeof(PRUint32) * mIndexPrefixes.Length();
+    *aSize += sizeof(PRUint32) * mIndexStarts.Length();
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::IsEmpty(PRBool * aEmpty)
+{
+  MutexAutoLock lock(mPrefixSetLock);
+  *aEmpty = !mHasPrefixes;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::GetKey(PRUint32 * aKey)
+ {
+   MutexAutoLock lock(mPrefixSetLock);
+   *aKey = mRandomKey;
+   return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::Probe(PRUint32 aPrefix, PRUint32 aKey,
+                                PRBool* aReady, PRBool* aFound)
+{
+  MutexAutoLock lock(mPrefixSetLock);
+
+  // 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 = PR_FALSE;
+    return NS_OK;
+  }
+
+  // check whether we are opportunistically probing or should wait
+  if (*aReady) {
+    // we should block until we are ready
+    while (!mHasPrefixes) {
+      LOG(("Set is empty, probe must wait"));
+      mSetIsReady.Wait();
+    }
+  } else {
+    // opportunistic probe -> check if set is loaded
+    if (mHasPrefixes) {
+      *aReady = PR_TRUE;
+    } else {
+      return NS_OK;
+    }
+  }
+
+  nsresult rv = Contains(aPrefix, aFound);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
+}
+
+nsresult
+nsUrlClassifierPrefixSet::LoadFromFd(AutoFDClose & fileFd)
+{
+  PRUint32 magic;
+  PRInt32 read;
+
+  read = PR_Read(fileFd, &magic, sizeof(PRUint32));
+  NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+
+  if (magic == PREFIXSET_VERSION_MAGIC) {
+    PRUint32 indexSize;
+    PRUint32 deltaSize;
+
+    read = PR_Read(fileFd, &mRandomKey, sizeof(PRUint32));
+    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    read = PR_Read(fileFd, &indexSize, sizeof(PRUint32));
+    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    read = PR_Read(fileFd, &deltaSize, sizeof(PRUint32));
+    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+
+    if (indexSize == 0) {
+      LOG(("stored PrefixSet is empty!"));
+      return NS_ERROR_FAILURE;
+    }
+
+    nsTArray<PRUint32> mNewIndexPrefixes;
+    nsTArray<PRUint32> mNewIndexStarts;
+    nsTArray<PRUint16> mNewDeltas;
+
+    mNewIndexStarts.SetLength(indexSize);
+    mNewIndexPrefixes.SetLength(indexSize);
+    mNewDeltas.SetLength(deltaSize);
+
+    read = PR_Read(fileFd, mNewIndexPrefixes.Elements(), indexSize*sizeof(PRUint32));
+    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    read = PR_Read(fileFd, mNewIndexStarts.Elements(), indexSize*sizeof(PRUint32));
+    NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    if (deltaSize > 0) {
+      read = PR_Read(fileFd, mNewDeltas.Elements(), deltaSize*sizeof(PRUint16));
+      NS_ENSURE_TRUE(read > 0, NS_ERROR_FAILURE);
+    }
+
+    MutexAutoLock lock(mPrefixSetLock);
+
+    mIndexPrefixes.SwapElements(mNewIndexPrefixes);
+    mIndexStarts.SwapElements(mNewIndexStarts);
+    mDeltas.SwapElements(mNewDeltas);
+
+    mHasPrefixes = PR_TRUE;
+    mSetIsReady.NotifyAll();
+  } else {
+    LOG(("Version magic mismatch, not loading"));
+    return NS_ERROR_FAILURE;
+  }
+
+  LOG(("Loading PrefixSet successful"));
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::LoadFromFile(nsIFile * aFile)
+{
+  nsresult rv;
+  nsCOMPtr<nsILocalFile> file(do_QueryInterface(aFile, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AutoFDClose fileFd;
+  rv = file->OpenNSPRFileDesc(PR_RDONLY, 0, &fileFd);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return LoadFromFd(fileFd);
+}
+
+nsresult
+nsUrlClassifierPrefixSet::StoreToFd(AutoFDClose & fileFd)
+{
+  PRInt32 written;
+  PRUint32 magic = PREFIXSET_VERSION_MAGIC;
+  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));
+  NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
+  written = PR_Write(fileFd, &deltaSize, sizeof(PRUint32));
+  NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
+
+  written = PR_Write(fileFd, mIndexPrefixes.Elements(), indexSize * sizeof(PRUint32));
+  NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
+  written = PR_Write(fileFd, mIndexStarts.Elements(), indexSize * sizeof(PRUint32));
+  NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
+  if (deltaSize > 0) {
+    written = PR_Write(fileFd, mDeltas.Elements(), deltaSize * sizeof(PRUint16));
+    NS_ENSURE_TRUE(written > 0, NS_ERROR_FAILURE);
+  }
+
+  LOG(("Saving PrefixSet successful\n"));
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUrlClassifierPrefixSet::StoreToFile(nsIFile * aFile)
+{
+  if (!mHasPrefixes) {
+    LOG(("Attempt to serialize empty PrefixSet"));
+    return NS_ERROR_FAILURE;
+  }
+
+  nsresult rv;
+  nsCOMPtr<nsILocalFile> file(do_QueryInterface(aFile, &rv));
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  AutoFDClose fileFd;
+  rv = file->OpenNSPRFileDesc(PR_RDWR | PR_TRUNCATE | PR_CREATE_FILE,
+                              0644, &fileFd);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  MutexAutoLock lock(mPrefixSetLock);
+
+  return StoreToFd(fileFd);
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h
@@ -0,0 +1,108 @@
+//* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=2 et sw=2 tw=80: */
+/* ***** 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 <gpascutto@mozilla.com>
+ *   Mehdi Mulani <mars.martian+bugmail@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsUrlClassifierPrefixSet_h_
+#define nsUrlClassifierPrefixSet_h_
+
+#include "nsISupportsUtils.h"
+#include "nsID.h"
+#include "nsIFile.h"
+#include "nsIUrlClassifierPrefixSet.h"
+#include "nsToolkitCompsCID.h"
+#include "mozilla/Mutex.h"
+#include "mozilla/CondVar.h"
+#include "mozilla/FileUtils.h"
+
+class nsUrlClassifierPrefixSet : public nsIUrlClassifierPrefixSet
+{
+public:
+  nsUrlClassifierPrefixSet();
+  virtual ~nsUrlClassifierPrefixSet() {};
+
+  // Can send an empty Array to clean the tree
+  NS_IMETHOD SetPrefixes(const PRUint32* aArray, PRUint32 aLength);
+  // Given prefixes must be in sorted order and bigger than
+  // anything currently in the Prefix Set
+  NS_IMETHOD AddPrefixes(const PRUint32* aArray, PRUint32 aLength);
+  // Does the PrefixSet contain this prefix? not thread-safe
+  NS_IMETHOD Contains(PRUint32 aPrefix, PRBool* aFound);
+  // Do a lookup in the PrefixSet
+  // 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
+  NS_IMETHOD Probe(PRUint32 aPrefix, PRUint32 aKey, PRBool* aReady, PRBool* aFound);
+  // Return the estimated size of the set on disk and in memory,
+  // in bytes
+  NS_IMETHOD EstimateSize(PRUint32* aSize);
+  NS_IMETHOD IsEmpty(PRBool * aEmpty);
+  NS_IMETHOD LoadFromFile(nsIFile* aFile);
+  NS_IMETHOD StoreToFile(nsIFile* aFile);
+  // Return a key that is used to randomize the collisions in the prefixes
+  NS_IMETHOD GetKey(PRUint32* aKey);
+
+  NS_DECL_ISUPPORTS
+
+protected:
+  static const PRUint32 DELTAS_LIMIT = 100;
+  static const PRUint32 MAX_INDEX_DIFF = (1 << 16);
+  static const PRUint32 PREFIXSET_VERSION_MAGIC = 1;
+
+  mozilla::Mutex mPrefixSetLock;
+  mozilla::CondVar mSetIsReady;
+
+  PRUint32 BinSearch(PRUint32 start, PRUint32 end, PRUint32 target);
+  nsresult LoadFromFd(mozilla::AutoFDClose & fileFd);
+  nsresult StoreToFd(mozilla::AutoFDClose & fileFd);
+  nsresult InitKey();
+
+  // boolean indicating whether |setPrefixes| has been
+  // called with a non-empty array.
+  PRBool mHasPrefixes;
+  // key used to randomize hash collisions
+  PRUint32 mRandomKey;
+  // the prefix for each index.
+  nsTArray<PRUint32> mIndexPrefixes;
+  // the value corresponds to the beginning of the run
+  // (an index in |_deltas|) for the index
+  nsTArray<PRUint32> mIndexStarts;
+  // array containing deltas from indices.
+  nsTArray<PRUint16> mDeltas;
+};
+
+#endif
--- a/toolkit/components/url-classifier/nsUrlClassifierUtils.h
+++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.h
@@ -119,130 +119,9 @@ private:
   // Function to tell if we should encode a character.
   PRBool ShouldURLEscape(const unsigned char c) const;
 
   void CleanupHostname(const nsACString & host, nsACString & _retval);
 
   nsAutoPtr<Charmap> mEscapeCharmap;
 };
 
-// An MRU list of fragments.  This is used by the DB service to
-// keep a set of known-clean fragments that don't need a database
-// lookup.
-class nsUrlClassifierFragmentSet
-{
-public:
-  nsUrlClassifierFragmentSet() : mFirst(nsnull), mLast(nsnull), mCapacity(16) {}
-
-  PRBool Init(PRUint32 maxEntries) {
-    mCapacity = maxEntries;
-    if (!mEntryStorage.SetCapacity(mCapacity))
-      return PR_FALSE;
-
-    if (!mEntries.Init())
-      return PR_FALSE;
-
-    return PR_TRUE;
-  }
-
-  PRBool Put(const nsACString &fragment) {
-    Entry *entry = nsnull;
-    if (mEntries.Get(fragment, &entry)) {
-      // Remove this entry from the list, we'll add it back
-      // to the front.
-      UnlinkEntry(entry);
-    } else {
-      if (mEntryStorage.Length() < mEntryStorage.Capacity()) {
-        entry = mEntryStorage.AppendElement();
-        if (!entry)
-          return PR_FALSE;
-      } else {
-        // Reuse the oldest entry.
-        entry = mLast;
-        UnlinkEntry(entry);
-        mEntries.Remove(entry->mFragment);
-      }
-      entry->mFragment = fragment;
-      mEntries.Put(fragment, entry);
-    }
-
-    LinkEntry(entry);
-
-    return PR_TRUE;
-  }
-
-  PRBool Has(const nsACString &fragment, PRBool update = PR_TRUE) {
-    Entry *entry = nsnull;
-    PRBool exists = mEntries.Get(fragment, &entry);
-    // Move this entry to the front of the list (if it isn't already there)
-    if (update && exists && entry != mFirst) {
-      UnlinkEntry(entry);
-      LinkEntry(entry);
-    }
-
-    return exists;
-  }
-
-  void Clear() {
-    mFirst = mLast = nsnull;
-    mEntries.Clear();
-    mEntryStorage.Clear();
-    mEntryStorage.SetCapacity(mCapacity);
-  }
-
-private:
-  // One entry in the set.  We maintain a doubly-linked list, with
-  // the most recently used entry at the front.
-  class Entry {
-  public:
-    Entry() : mNext(nsnull), mPrev(nsnull) {};
-    ~Entry() { }
-
-    Entry *mNext;
-    Entry *mPrev;
-    nsCString mFragment;
-  };
-
-  void LinkEntry(Entry *entry)
-  {
-    // Add the entry to the front of the list
-    entry->mPrev = nsnull;
-    entry->mNext = mFirst;
-    if (mFirst) {
-      mFirst->mPrev = entry;
-    }
-    mFirst = entry;
-    if (!mLast) {
-      mLast = entry;
-    }
-  }
-
-  void UnlinkEntry(Entry *entry)
-  {
-    if (entry->mPrev)
-      entry->mPrev->mNext = entry->mNext;
-    else
-      mFirst = entry->mNext;
-
-    if (entry->mNext)
-      entry->mNext->mPrev = entry->mPrev;
-    else
-      mLast = entry->mPrev;
-
-    entry->mPrev = entry->mNext = nsnull;
-  }
-
-  // The newest entry in the cache.
-  Entry *mFirst;
-  // The oldest entry in the cache.
-  Entry *mLast;
-
-  // Max entries in the cache.
-  PRUint32 mCapacity;
-
-  // Storage for the entries in this set.
-  nsTArray<Entry> mEntryStorage;
-
-  // Entry lookup by fragment.
-  nsDataHashtable<nsCStringHashKey, Entry*> mEntries;
-};
-
 #endif // nsUrlClassifierUtils_h_
--- a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
+++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js
@@ -1,8 +1,9 @@
+//* -*- Mode: Javascript; tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*- *
 function dumpn(s) {
   dump(s + "\n");
 }
 
 const NS_APP_USER_PROFILE_50_DIR = "ProfD";
 const NS_APP_USER_PROFILE_LOCAL_50_DIR = "ProfLD";
 
 // This loads Ci, Cc, Cr and Cu.
@@ -272,16 +273,18 @@ function runNextTest()
     do_test_finished();
     return;
   }
 
   dbservice.resetDatabase();
   dbservice.setHashCompleter('test-phish-simple', null);
   dumpn("running " + gTests[gNextTest]);
 
+  dump("running " + gTests[gNextTest]);
+
   gTests[gNextTest++]();
 }
 
 function runTests(tests)
 {
   gTests = tests;
   runNextTest();
 }
--- a/toolkit/components/url-classifier/tests/unit/test_cleankeycache.js
+++ b/toolkit/components/url-classifier/tests/unit/test_cleankeycache.js
@@ -1,8 +1,9 @@
+//* -*- 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
           }]);
@@ -19,166 +20,152 @@ function testCleanHostKeys() {
       // 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);
-
-          runNextTest();
+        do_throw("shouldn't get a callback");
         });
 
-      // The first classifier call should result in a callback.
-      do_check_eq(result, true);
-    }, updateError);
-}
-
-// Test an add of two urls to a fresh database
-function testDirtyHostKeys() {
-  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 dirty host key - both checks should happen.
-      var uri = ios.newURI("http://foo.com/b", 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() {
-              runNextTest();
-            });
-          // second call should result in a callback.
-          do_check_eq(result2, true);
-        });
-
-      // The first classifier call should result in a callback.
-      do_check_eq(result, true);
+      // 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);
 
-  // First lookup should happen...
-  var uri = ios.newURI("http://foo.com/a", null, null);
+  // Must put something in the PrefixSet
+  var preUrls = [ "foo.com/b" ];
+  var preUpdate = buildPhishingUpdate(
+    [
+      { "chunkNum" : 1,
+        "urls" : preUrls
+      }]);
 
-  // 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) {
-      // This check will succeed, which will cause the key to
-      // be put in the clean host key cache...
-      do_check_eq(errorCode, Cr.NS_OK);
+  doStreamUpdate(preUpdate, function() {
+    // First lookup won't happen...
+    var uri = ios.newURI("http://foo.com/a", null, null);
 
-      // Now add the url to the db...
-      var addUrls = [ "foo.com/a" ];
-      var update = buildPhishingUpdate(
-        [
-          { "chunkNum" : 1,
-            "urls" : addUrls
-          }]);
-      doStreamUpdate(update, function() {
-          // The clean key cache should be blown now that we've
-          // added, and this callback should execute.
-          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);
+    // 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() {
-  // 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);
+  // Must put something in the PrefixSet
+  var preUrls = [ "zaz.com/b" ];
+  var preUpdate = buildPhishingUpdate(
+    [
+      { "chunkNum" : 1,
+        "urls" : preUrls
+      }]);
 
-  // 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);
+  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 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 uris2 = [];
+    var runSecondLookup = function() {
+      if (uris2.length == 0) {
+        runNextTest();
+        return;
+      }
 
-  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 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() {
+    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();
+        return;
+      }
+      var spec = uris1.pop();
 
-    uris2.push(spec);
-    var uri = ios.newURI("http://" + spec, null, null);
-    var result = classifier.classify(uri, function(errorCode) {
-        runInitialLookup();
+      uris2.push(spec);
+      var uri = ios.newURI("http://" + spec, null, null);
+      var result = classifier.classify(uri, function(errorCode) {
       });
-    // All of these classifications should succeed.
-    do_check_eq(result, true);
-    if (result) {
-      doNextTest();
+      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);
+    // 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
@@ -195,15 +182,14 @@ function testBug475436() {
 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,
-             testDirtyHostKeys,
              testResetFullCache,
              testBug475436
   ]);
 }
 
 do_test_pending();
--- a/toolkit/components/url-classifier/tests/unit/test_partial.js
+++ b/toolkit/components/url-classifier/tests/unit/test_partial.js
@@ -82,22 +82,22 @@ addHash: function(chunkId, hash)
 },
 
 compareQueries: function(fragments)
 {
   var expectedQueries = [];
   for (var i = 0; i < fragments.length; i++) {
     expectedQueries.push(this.getHash(fragments[i]).slice(0, 4));
   }
+  do_check_eq(this.queries.length, expectedQueries.length);
   expectedQueries.sort();
   this.queries.sort();
   for (var i = 0; i < this.queries.length; i++) {
     do_check_eq(this.queries[i], expectedQueries[i]);
   }
-  do_check_eq(this.queries.length, expectedQueries.length);
 }
 };
 
 function setupCompleter(table, hits, conflicts)
 {
   var completer = new DummyCompleter();
   completer.tableName = table;
   for (var i = 0; i < hits.length; i++) {
@@ -416,25 +416,33 @@ function testInvalidHashSize()
   var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ];
   var update = buildPhishingUpdate(
         [
           { "chunkNum" : 1,
             "urls" : addUrls
           }],
         12); // only 4 and 32 are legal hash sizes
 
+  var addUrls2 = [ "zaz.com/a", "xyz.com/b" ];
+  var update2 = buildPhishingUpdate(
+        [
+          { "chunkNum" : 2,
+            "urls" : addUrls2
+          }],
+        4);
+
   var completer = installCompleter('test-phish-simple', [[1, addUrls]], []);
 
   var assertions = {
-    "tableData" : "",
+    "tableData" : "test-phish-simple;a:2",
     "urlsDontExist" : addUrls
   };
 
   // A successful update will trigger an error
-  doUpdateTest([update], assertions, updateError, runNextTest);
+  doUpdateTest([update2, update], assertions, updateError, runNextTest);
 }
 
 function testWrongTable()
 {
   var addUrls = [ "foo.com/a" ];
   var update = buildPhishingUpdate(
         [
           { "chunkNum" : 1,
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/unit/test_prefixset.js
@@ -0,0 +1,168 @@
+// newPset: returns an empty nsIUrlClassifierPrefixSet.
+function newPset() {
+  return Cc["@mozilla.org/url-classifier/prefixset;1"]
+           .createInstance(Ci.nsIUrlClassifierPrefixSet);
+}
+
+// arrContains: returns true if |arr| contains the element |target|. Uses binary
+// search and requires |arr| to be sorted.
+function arrContains(arr, target) {
+  let start = 0;
+  let end = arr.length - 1;
+  let i = 0;
+
+  while (end > start) {
+    i = start + (end - start >> 1);
+    let value = arr[i];
+
+    if (value < target)
+      start = i+1;
+    else if (value > target)
+      end = i-1;
+    else
+      break;
+  }
+  if (start == end)
+    i = start;
+
+  return (!(i < 0 || i >= arr.length) && arr[i] == target);
+}
+
+// doRandomLookups: we use this to test for false membership with random input
+// over the range of prefixes (unsigned 32-bits integers).
+//    pset: a nsIUrlClassifierPrefixSet to test.
+//    prefixes: an array of prefixes supposed to make up the prefix set.
+//    N: number of random lookups to make.
+function doRandomLookups(pset, prefixes, N) {
+  for (let i = 0; i < N; i++) {
+    let randInt = prefixes[0];
+    while (arrContains(prefixes, randInt))
+      randInt = Math.floor(Math.random() * Math.pow(2, 32));
+
+    do_check_false(pset.contains(randInt));
+  }
+}
+
+// doExpectedLookups: we use this to test expected membership.
+//    pset: a nsIUrlClassifierPrefixSet to test.
+//    prefixes:
+function doExpectedLookups(pset, prefixes, N) {
+  for (let i = 0; i < N; i++) {
+    prefixes.forEach(function (x) {
+      dump("Checking " + x + "\n");
+      do_check_true(pset.contains(x));
+    });
+  }
+}
+
+// testBasicPset: A very basic test of the prefix set to make sure that it
+// exists and to give a basic example of its use.
+function testBasicPset() {
+  let pset = Cc["@mozilla.org/url-classifier/prefixset;1"]
+               .createInstance(Ci.nsIUrlClassifierPrefixSet);
+  let prefixes = [2,100,50,2000,78000,1593203];
+  pset.setPrefixes(prefixes, prefixes.length);
+
+  do_check_true(pset.contains(100));
+  do_check_false(pset.contains(100000));
+  do_check_true(pset.contains(1593203));
+  do_check_false(pset.contains(999));
+  do_check_false(pset.contains(0));
+}
+
+function testDuplicates() {
+  let pset = Cc["@mozilla.org/url-classifier/prefixset;1"]
+               .createInstance(Ci.nsIUrlClassifierPrefixSet);
+  let prefixes = [1,1,2,2,2,3,3,3,3,3,3,5,6,6,7,7,9,9,9];
+  pset.setPrefixes(prefixes, prefixes.length);
+
+  do_check_true(pset.contains(1));
+  do_check_true(pset.contains(2));
+  do_check_true(pset.contains(5));
+  do_check_true(pset.contains(9));
+  do_check_false(pset.contains(4));
+  do_check_false(pset.contains(8));
+}
+
+function testSimplePset() {
+  let pset = newPset();
+  let prefixes = [1,2,100,400,123456789];
+  pset.setPrefixes(prefixes, prefixes.length);
+
+  doRandomLookups(pset, prefixes, 100);
+  doExpectedLookups(pset, prefixes, 1);
+}
+
+function testUnsortedPset() {
+  let pset = newPset();
+  let prefixes = [5,1,20,100,200000,100000];
+  pset.setPrefixes(prefixes, prefixes.length);
+
+  doRandomLookups(pset, prefixes, 100);
+  doExpectedLookups(pset, prefixes, 1);
+}
+
+function testReSetPrefixes() {
+  let pset = newPset();
+  let prefixes = [1, 5, 100, 1000, 150000];
+  pset.setPrefixes(prefixes, prefixes.length);
+
+  doExpectedLookups(pset, prefixes, 1);
+
+  let secondPrefixes = [12, 50, 300, 2000, 5000, 200000];
+  pset.setPrefixes(secondPrefixes, secondPrefixes.length);
+
+  doExpectedLookups(pset, secondPrefixes, 1);
+  for (let i = 0; i < prefixes.length; i++) {
+    do_check_false(pset.contains(prefixes[i]));
+  }
+}
+
+function testLargeSet() {
+  let N = 1000;
+  let arr = [];
+
+  for (let i = 0; i < N; i++) {
+    let randInt = Math.floor(Math.random() * Math.pow(2, 32));
+    arr.push(randInt);
+  }
+
+  arr.sort(function(x,y) x - y);
+
+  let pset = newPset();
+  pset.setPrefixes(arr, arr.length);
+
+  doExpectedLookups(pset, arr, 1);
+  doRandomLookups(pset, arr, 1000);
+}
+
+function testTinySet() {
+  let pset = Cc["@mozilla.org/url-classifier/prefixset;1"]
+               .createInstance(Ci.nsIUrlClassifierPrefixSet);
+  let prefixes = [1];
+  pset.setPrefixes(prefixes, prefixes.length);
+
+  do_check_true(pset.contains(1));
+  do_check_false(pset.contains(100000));
+
+  prefixes = [];
+  pset.setPrefixes(prefixes, prefixes.length);
+  do_check_false(pset.contains(1));
+}
+
+let tests = [testBasicPset,
+             testSimplePset,
+             testUnsortedPset,
+             testReSetPrefixes,
+             testLargeSet,
+             testDuplicates,
+             testTinySet];
+
+function run_test() {
+  // None of the tests use |executeSoon| or any sort of callbacks, so we can
+  // just run them in succession.
+  for (let i = 0; i < tests.length; i++) {
+    dump("Running " + tests[i].name + "\n");
+    tests[i]();
+  }
+}
--- a/toolkit/components/url-classifier/tests/unit/test_streamupdater.js
+++ b/toolkit/components/url-classifier/tests/unit/test_streamupdater.js
@@ -22,16 +22,35 @@ function doTest(updates, assertions, exp
 {
   if (expectError) {
     doUpdateTest(updates, assertions, updateError, runNextTest, clientKey);
   } else {
     doUpdateTest(updates, assertions, runNextTest, updateError, clientKey);
   }
 }
 
+function testFillDb() {
+  var add1Urls = [ "zaz.com/a", "yxz.com/c" ];
+
+  var update = "n:1000\n";
+  update += "i:test-phish-simple\n";
+
+  var update1 = buildBareUpdate(
+    [{ "chunkNum" : 1,
+       "urls" : add1Urls }]);
+  update += "u:data:," + encodeURIComponent(update1) + "\n";
+
+  var assertions = {
+    "tableData" : "test-phish-simple;a:1",
+    "urlsExist" : add1Urls
+  };
+
+  doTest([update], assertions, false);
+}
+
 function testSimpleForward() {
   var add1Urls = [ "foo.com/a", "bar.com/c" ];
   var add2Urls = [ "foo.com/b" ];
   var add3Urls = [ "bar.com/d" ];
 
   var update = "n:1000\n";
   update += "i:test-phish-simple\n";
 
@@ -56,16 +75,18 @@ function testSimpleForward() {
   };
 
   doTest([update], assertions, false);
 }
 
 // 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" ];
 
   var update = "n:1000\n";
   update += "i:test-phish-simple\n";
 
   var update1 = buildBareUpdate(
     [{ "chunkNum" : 1,
@@ -177,16 +198,18 @@ function testValidMAC() {
     "urlsExist" : addUrls
   };
 
   doTest([update], assertions, false, gClientKey);
 }
 
 // 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(
         [
           { "chunkNum" : 1,
             "urls" : addUrls
           }]);
   update = "m:INVALIDMAC\n" + update;
 
@@ -196,16 +219,18 @@ function testInvalidMAC() {
   };
 
   doTest([update], assertions, true, gClientKey);
 }
 
 // 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(
         [
           { "chunkNum" : 1,
             "urls" : addUrls
           }]);
 
   var assertions = {
@@ -252,16 +277,18 @@ function testValidForwardMAC() {
   update = "m:" + MAC(update, gClientKeyRaw) + "\n" + update;
 
   doTest([update], assertions, false, gClientKey);
 }
 
 // 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" ];
 
   var update = "n:1000\n";
   update += "i:test-phish-simple\n";
 
   var update1 = buildBareUpdate(
@@ -291,16 +318,18 @@ function testInvalidForwardMAC() {
   update = "m:" + MAC(update, gClientKeyRaw) + "\n" + update;
 
   doTest([update], assertions, true, gClientKey);
 }
 
 // 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" ];
 
   var update = "n:1000\n";
   update += "i:test-phish-simple\n";
 
   // XXX : This test presents invalid data: urls as forwards.  A valid
@@ -357,16 +386,18 @@ var gGotRekey;
 gAssertions.gotRekey = function(data, cb)
 {
   do_check_eq(gGotRekey, data);
   cb();
 }
 
 // Tests a rekey request.
 function testRekey() {
+  testFillDb();
+
   var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ];
   var update = buildPhishingUpdate(
         [
           { "chunkNum" : 1,
             "urls" : addUrls
           }]);
   update = "e:pleaserekey\n" + update;
 
--- a/toolkit/components/url-classifier/tests/unit/xpcshell.ini
+++ b/toolkit/components/url-classifier/tests/unit/xpcshell.ini
@@ -3,9 +3,10 @@ head = head_urlclassifier.js
 tail = tail_urlclassifier.js
 
 [test_addsub.js]
 [test_backoff.js]
 [test_cleankeycache.js]
 [test_dbservice.js]
 [test_hashcompleter.js]
 [test_partial.js]
+[test_prefixset.js]
 [test_streamupdater.js]
--- a/widget/src/android/nsAppShell.cpp
+++ b/widget/src/android/nsAppShell.cpp
@@ -441,20 +441,16 @@ nsAppShell::PeekNextEvent()
     }
 
     return ae;
 }
 
 void
 nsAppShell::PostEvent(AndroidGeckoEvent *ae)
 {
-    if (ae->Type() == AndroidGeckoEvent::ACTIVITY_STOPPING) {
-        PostEvent(new AndroidGeckoEvent(AndroidGeckoEvent::SURFACE_DESTROYED));
-    }
-
     {
         MutexAutoLock lock(mQueueLock);
         if (ae->Type() == AndroidGeckoEvent::SURFACE_DESTROYED) {
             // Give priority to this event, and discard any pending
             // SURFACE_CREATED events.
             mEventQueue.InsertElementAt(0, ae);
             AndroidGeckoEvent *event;
             for (int i = mEventQueue.Length()-1; i >=1; i--) {