diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 822666b7fc8..a74ee534588 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -734,19 +734,11 @@ pref("urlclassifier.gethashtables", "goog-phish-shavar,goog-malware-shavar"); // 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 -#ifdef RESTRICT_CACHEMAX -pref("urlclassifier.updatecachemax", 104857600); -#else -pref("urlclassifier.updatecachemax", -1); -#endif +pref("urlclassifier.updatecachemax", 41943040); + +// 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="); diff --git a/embedding/android/AndroidManifest.xml.in b/embedding/android/AndroidManifest.xml.in index 510c007e34e..3b7eb366196 100644 --- a/embedding/android/AndroidManifest.xml.in +++ b/embedding/android/AndroidManifest.xml.in @@ -10,7 +10,7 @@ #endif > + android:targetSdkVersion="11"/> diff --git a/embedding/android/resources/layout/notification_icon_text.xml b/embedding/android/resources/layout/notification_icon_text.xml index 2a33012460d..d420cbd354c 100644 --- a/embedding/android/resources/layout/notification_icon_text.xml +++ b/embedding/android/resources/layout/notification_icon_text.xml @@ -15,20 +15,20 @@ android:layout_height="25dp" android:scaleType="fitCenter" /> + /> + /> diff --git a/embedding/android/resources/layout/notification_progress.xml b/embedding/android/resources/layout/notification_progress.xml index 75cae9bd317..59633c3017a 100644 --- a/embedding/android/resources/layout/notification_progress.xml +++ b/embedding/android/resources/layout/notification_progress.xml @@ -16,16 +16,15 @@ android:layout_height="25dp" android:scaleType="fitCenter" /> + /> + /> + /> + /> diff --git a/ipc/chromium/src/base/histogram.cc b/ipc/chromium/src/base/histogram.cc index 06e264ae849..8831bcfea5e 100644 --- a/ipc/chromium/src/base/histogram.cc +++ b/ipc/chromium/src/base/histogram.cc @@ -130,6 +130,17 @@ void Histogram::Add(int value) { 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); } diff --git a/ipc/chromium/src/base/histogram.h b/ipc/chromium/src/base/histogram.h index 0b5e77e361e..0219dd510fc 100644 --- a/ipc/chromium/src/base/histogram.h +++ b/ipc/chromium/src/base/histogram.h @@ -381,6 +381,7 @@ class Histogram { Flags flags); void Add(int value); + void Subtract(int value); // This method is an interface, used only by BooleanHistogram. virtual void AddBoolean(bool value); diff --git a/js/src/Makefile.in b/js/src/Makefile.in index 3c022ffed53..70e585ca5fb 100644 --- a/js/src/Makefile.in +++ b/js/src/Makefile.in @@ -155,6 +155,7 @@ CPPSRCS = \ jsxml.cpp \ prmjtime.cpp \ sharkctl.cpp \ + CallObject.cpp \ Debugger.cpp \ GlobalObject.cpp \ Stack.cpp \ @@ -247,6 +248,7 @@ EXPORTS_NAMESPACES = vm EXPORTS_vm = \ ArgumentsObject.h \ + CallObject.h \ GlobalObject.h \ Stack.h \ String.h \ diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 4d053627e95..d6a2f6bf4df 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -72,6 +72,7 @@ #include "jsexn.h" #include "jsstaticcheck.h" #include "jstracer.h" +#include "vm/CallObject.h" #include "vm/Debugger.h" #if JS_HAS_GENERATORS @@ -91,7 +92,7 @@ #include "jsinferinlines.h" #include "jsobjinlines.h" #include "jsscriptinlines.h" - +#include "vm/CallObject-inl.h" #include "vm/ArgumentsObject-inl.h" #include "vm/Stack-inl.h" @@ -283,7 +284,7 @@ js_GetArgsObject(JSContext *cx, StackFrame *fp) if (argsobj->isStrictArguments()) fp->forEachCanonicalActualArg(PutArg(argsobj->data()->slots)); else - argsobj->setPrivate(fp); + argsobj->setStackFrame(fp); fp->setArgsObj(*argsobj); return argsobj; @@ -294,11 +295,11 @@ 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()); } } @@ -319,9 +320,9 @@ js_NewArgumentsOnTrace(JSContext *cx, uint32 argc, JSObject *callee) * 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; @@ -335,7 +336,7 @@ 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 @@ -349,7 +350,7 @@ js_PutArgumentsOnTrace(JSContext *cx, JSObject *obj, Value *argv) *dst = *src; } - argsobj->setPrivate(NULL); + argsobj->clearOnTrace(); return true; } JS_DEFINE_CALLINFO_3(extern, BOOL, js_PutArgumentsOnTrace, CONTEXT, OBJECT, VALUEPTR, 0, @@ -390,7 +391,7 @@ ArgGetter(JSContext *cx, JSObject *obj, jsid id, Value *vp) 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(argsobj->getPrivate())) + if (StackFrame *fp = argsobj->maybeStackFrame()) *vp = fp->canonicalActualArg(arg); else *vp = argsobj->element(arg); @@ -427,7 +428,7 @@ ArgSetter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) if (JSID_IS_INT(id)) { uintN arg = uintN(JSID_TO_INT(id)); if (arg < argsobj->initialLength()) { - if (StackFrame *fp = reinterpret_cast(argsobj->getPrivate())) { + if (StackFrame *fp = argsobj->maybeStackFrame()) { JSScript *script = fp->functionScript(); if (script->usesArguments) { if (arg < fp->numFormalArgs()) @@ -673,7 +674,7 @@ 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; } @@ -757,57 +758,6 @@ Class js::DeclEnvClass = { 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) { @@ -826,7 +776,7 @@ NewDeclEnvObject(JSContext *cx, StackFrame *fp) namespace js { -JSObject * +CallObject * CreateFunCallObject(JSContext *cx, StackFrame *fp) { JS_ASSERT(fp->isNonEvalFunctionFrame()); @@ -852,23 +802,23 @@ CreateFunCallObject(JSContext *cx, StackFrame *fp) } } - 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; } @@ -881,52 +831,44 @@ js_CreateCallObjectOnTrace(JSContext *cx, JSFunction *fun, JSObject *callee, JSO 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(); @@ -939,7 +881,7 @@ js_PutCallObject(StackFrame *fp) || 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 @@ -948,13 +890,13 @@ js_PutCallObject(StackFrame *fp) 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]); } } @@ -964,8 +906,8 @@ js_PutCallObject(StackFrame *fp) */ 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(); } } @@ -979,19 +921,19 @@ js_PutCallObject(StackFrame *fp) } } - 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; } @@ -1004,14 +946,16 @@ 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; } @@ -1019,37 +963,41 @@ GetCallArguments(JSContext *cx, JSObject *obj, jsid id, Value *vp) 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); + callobj.setArg(i, *vp); - JSFunction *fun = obj->getCallObjCalleeFunction(); + JSFunction *fun = callobj.getCalleeFunction(); JSScript *script = fun->script(); if (!script->ensureHasTypes(cx, fun)) return false; @@ -1062,40 +1010,43 @@ SetCallArg(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) 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); @@ -1115,12 +1066,12 @@ SetCallVar(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp) } #endif - if (StackFrame *fp = obj->maybeCallObjStackFrame()) + if (StackFrame *fp = callobj.maybeStackFrame()) fp->varSlot(i) = *vp; else - obj->setCallObjVar(i, *vp); + callobj.setVar(i, *vp); - JSFunction *fun = obj->getCallObjCalleeFunction(); + JSFunction *fun = callobj.getCalleeFunction(); JSScript *script = fun->script(); if (!script->ensureHasTypes(cx, fun)) return false; @@ -1153,16 +1104,14 @@ JS_DEFINE_CALLINFO_4(extern, BOOL, js_SetCallVar, CONTEXT, OBJECT, JSID, VALUE, #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(); @@ -1204,7 +1153,7 @@ call_trace(JSTracer *trc, JSObject *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 */ diff --git a/js/src/jsfun.h b/js/src/jsfun.h index 83e806045b4..a151de52a14 100644 --- a/js/src/jsfun.h +++ b/js/src/jsfun.h @@ -503,10 +503,10 @@ js_PutCallObjectOnTrace(JSObject *scopeChain, uint32 nargs, js::Value *argv, namespace js { -JSObject * +CallObject * CreateFunCallObject(JSContext *cx, StackFrame *fp); -JSObject * +CallObject * CreateEvalCallObject(JSContext *cx, StackFrame *fp); extern JSBool diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 57ac9b0f38e..4ffd69921e5 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -95,6 +95,7 @@ #include "jsobjinlines.h" #include "vm/String-inl.h" +#include "vm/CallObject-inl.h" #ifdef MOZ_VALGRIND # define JS_VALGRIND diff --git a/js/src/jsinfer.cpp b/js/src/jsinfer.cpp index c9503c8a47a..e070c1645ad 100644 --- a/js/src/jsinfer.cpp +++ b/js/src/jsinfer.cpp @@ -5055,8 +5055,8 @@ TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope) * 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; @@ -5089,11 +5089,13 @@ TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope) 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(); @@ -5121,8 +5123,8 @@ TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope) 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); @@ -5212,7 +5214,7 @@ CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script) 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) { diff --git a/js/src/jsinfer.h b/js/src/jsinfer.h index 4889a15f47e..81d95ca028b 100644 --- a/js/src/jsinfer.h +++ b/js/src/jsinfer.h @@ -55,7 +55,7 @@ namespace js { namespace analyze { class ScriptAnalysis; } - struct GlobalObject; + class GlobalObject; } namespace js { diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp index c27597d21b4..5b84020c0ae 100644 --- a/js/src/jsinterp.cpp +++ b/js/src/jsinterp.cpp @@ -3900,7 +3900,7 @@ BEGIN_CASE(JSOP_GETELEM) if (arg < argsobj->initialLength()) { copyFrom = &argsobj->element(arg); if (!copyFrom->isMagic(JS_ARGS_HOLE)) { - if (StackFrame *afp = reinterpret_cast(argsobj->getPrivate())) + if (StackFrame *afp = argsobj->maybeStackFrame()) copyFrom = &afp->canonicalActualArg(arg); goto end_getelem; } diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 0647561d34d..ce59ee432f6 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -4491,7 +4491,7 @@ JSObject::growSlots(JSContext *cx, size_t newcap) * 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 diff --git a/js/src/jsobj.h b/js/src/jsobj.h index fed22716823..ed164dc8d1d 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -66,6 +66,7 @@ namespace js { class AutoPropDescArrayRooter; class JSProxyHandler; class RegExp; +class CallObject; struct GCMarker; struct NativeIterator; @@ -733,6 +734,8 @@ struct JSObject : js::gc::Cell { private: inline js::Value* fixedSlots() const; + + protected: inline bool hasContiguousSlots(size_t start, size_t count) const; public: @@ -1065,58 +1068,10 @@ struct JSObject : js::gc::Cell { 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. */ @@ -1926,10 +1881,7 @@ 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; diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index 0fe99af49e3..95dea5f366b 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -540,112 +540,6 @@ JSObject::denseArrayHasInlineSlots() const 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(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 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 { /* diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp index d8205eceef9..6741c4bc220 100644 --- a/js/src/jsscript.cpp +++ b/js/src/jsscript.cpp @@ -120,7 +120,7 @@ Bindings::add(JSContext *cx, JSAtom *name, BindingKind kind) uint16 *indexp; PropertyOp getter; StrictPropertyOp setter; - uint32 slot = JSObject::CALL_RESERVED_SLOTS; + uint32 slot = CallObject::RESERVED_SLOTS; if (kind == ARGUMENT) { JS_ASSERT(nvars == 0); diff --git a/js/src/jstracer.cpp b/js/src/jstracer.cpp index d4c51c4f8a0..9303a7e65b4 100644 --- a/js/src/jstracer.cpp +++ b/js/src/jstracer.cpp @@ -93,6 +93,7 @@ #include "jsopcodeinlines.h" #include "jstypedarrayinlines.h" +#include "vm/CallObject-inl.h" #include "vm/Stack-inl.h" #ifdef JS_METHODJIT @@ -3286,9 +3287,9 @@ public: 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); @@ -3301,12 +3302,12 @@ public: } 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); } @@ -3539,7 +3540,7 @@ GetFromClosure(JSContext* cx, JSObject* call, const ClosureVarInfo* cv, double* // 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; @@ -3577,12 +3578,12 @@ struct ArgClosureTraits // 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: @@ -3608,12 +3609,12 @@ struct VarClosureTraits } 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: @@ -8149,9 +8150,10 @@ TraceRecorder::entryFrameIns() const * 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 +TraceRecorder::frameIfInRange(JSObject *obj, unsigned* depthp) const { - StackFrame* ofp = (StackFrame*) obj->getPrivate(); + 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) { @@ -8264,7 +8266,8 @@ TraceRecorder::callProp(JSObject* obj, JSProperty* prop, jsid id, const Value*& 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()); @@ -8281,7 +8284,7 @@ TraceRecorder::callProp(JSObject* obj, JSProperty* prop, jsid id, const Value*& // 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); @@ -12299,11 +12302,13 @@ TraceRecorder::setUpwardTrackedVar(Value* stackVp, const Value &v, LIns* v_ins) } 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()); @@ -12322,7 +12327,7 @@ TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, const Shape *sh 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 @@ -12330,11 +12335,11 @@ TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, const Shape *sh // 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"); } @@ -12345,7 +12350,7 @@ TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, const Shape *sh 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; } @@ -14997,7 +15002,7 @@ static inline bool IsFindableCallObj(JSObject *obj) { return obj->isCall() && - (obj->callIsForEval() || obj->getCallObjCalleeFunction()->isHeavyweight()); + (obj->asCall().isForEval() || obj->asCall().getCalleeFunction()->isHeavyweight()); } /* @@ -15134,8 +15139,7 @@ TraceRecorder::record_JSOP_BINDNAME() * 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"); } diff --git a/js/src/jstracer.h b/js/src/jstracer.h index 952ee653313..c7dd7706d37 100644 --- a/js/src/jstracer.h +++ b/js/src/jstracer.h @@ -1272,7 +1272,7 @@ class TraceRecorder 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); diff --git a/js/src/methodjit/PolyIC.cpp b/js/src/methodjit/PolyIC.cpp index cc4524bd08c..992205fd1c5 100644 --- a/js/src/methodjit/PolyIC.cpp +++ b/js/src/methodjit/PolyIC.cpp @@ -54,6 +54,8 @@ #include "jsinterpinlines.h" #include "jsautooplen.h" +#include "vm/CallObject-inl.h" + #if defined JS_POLYIC using namespace js; @@ -421,7 +423,7 @@ class SetPropCompiler : public PICStubCompiler // \\ 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. */ @@ -441,7 +443,7 @@ class SetPropCompiler : public PICStubCompiler 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); @@ -691,7 +693,7 @@ class SetPropCompiler : public PICStubCompiler * 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)) @@ -1504,7 +1506,7 @@ class ScopeNameCompiler : public PICStubCompiler /* 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; @@ -1525,7 +1527,7 @@ class ScopeNameCompiler : public PICStubCompiler 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. */ diff --git a/js/src/methodjit/StubCalls.cpp b/js/src/methodjit/StubCalls.cpp index fc304b7abb5..78989586e90 100644 --- a/js/src/methodjit/StubCalls.cpp +++ b/js/src/methodjit/StubCalls.cpp @@ -482,7 +482,7 @@ stubs::GetElem(VMFrame &f) 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; } diff --git a/js/src/vm/ArgumentsObject-inl.h b/js/src/vm/ArgumentsObject-inl.h index 3e55cc13679..9103151c6fa 100644 --- a/js/src/vm/ArgumentsObject-inl.h +++ b/js/src/vm/ArgumentsObject-inl.h @@ -110,6 +110,37 @@ ArgumentsObject::setElement(uint32 i, const js::Value &v) data()->slots[i] = v; } +inline js::StackFrame * +ArgumentsObject::maybeStackFrame() const +{ + return reinterpret_cast(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 { diff --git a/js/src/vm/ArgumentsObject.h b/js/src/vm/ArgumentsObject.h index 7e401db8f66..7b64716f1cb 100644 --- a/js/src/vm/ArgumentsObject.h +++ b/js/src/vm/ArgumentsObject.h @@ -47,8 +47,6 @@ class GetPropCompiler; #endif -#define JS_ARGUMENTS_OBJECT_ON_TRACE ((void *)0xa126) - #ifdef JS_TRACER namespace nanojit { class ValidateWriter; @@ -229,6 +227,14 @@ class ArgumentsObject : public ::JSObject 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(); }; /* diff --git a/js/src/vm/CallObject-inl.h b/js/src/vm/CallObject-inl.h new file mode 100644 index 00000000000..b869853fb5a --- /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 (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(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 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___ */ diff --git a/js/src/vm/CallObject.cpp b/js/src/vm/CallObject.cpp new file mode 100644 index 00000000000..63174325a08 --- /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 (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; +} + +} diff --git a/js/src/vm/CallObject.h b/js/src/vm/CallObject.h new file mode 100644 index 00000000000..d4c4e89fdc7 --- /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 (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(this); +} + +#endif /* CallObject_h___ */ diff --git a/js/src/vm/Stack-inl.h b/js/src/vm/Stack-inl.h index 15619de28d8..104425fdb4e 100644 --- a/js/src/vm/Stack-inl.h +++ b/js/src/vm/Stack-inl.h @@ -48,6 +48,8 @@ #include "jsscriptinlines.h" #include "ArgumentsObject-inl.h" +#include "CallObject-inl.h" + #include "methodjit/MethodJIT.h" namespace js { @@ -340,15 +342,15 @@ StackFrame::setScopeChainNoCallObj(JSObject &obj) } 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()); @@ -358,7 +360,7 @@ StackFrame::callObj() const JS_ASSERT(IsCacheableNonGlobalScope(pobj) || pobj->isWith()); pobj = pobj->getParent(); } - return *pobj; + return pobj->asCall(); } inline bool @@ -415,11 +417,11 @@ 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 @@ -687,8 +689,7 @@ ArgumentsObject::getElement(uint32 i, Value *vp) * 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(getPrivate()); - if (fp == JS_ARGUMENTS_OBJECT_ON_TRACE) + if (onTrace()) return false; /* @@ -696,6 +697,7 @@ ArgumentsObject::getElement(uint32 i, Value *vp) * 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); @@ -711,7 +713,7 @@ ArgumentsObject::getElements(uint32 start, uint32 count, Value *vp) if (start > length || start + count > length) return false; - StackFrame *fp = reinterpret_cast(getPrivate()); + StackFrame *fp = maybeStackFrame(); /* If there's no stack frame for this, argument values are in elements(). */ if (!fp) { @@ -727,7 +729,7 @@ ArgumentsObject::getElements(uint32 start, uint32 count, Value *vp) } /* 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. */ diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp index 32c0d8e542d..44d592fad65 100644 --- a/js/src/vm/Stack.cpp +++ b/js/src/vm/Stack.cpp @@ -159,9 +159,9 @@ StackFrame::stealFrameAndSlots(Value *vp, StackFrame *otherfp, 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; } } diff --git a/js/src/vm/Stack.h b/js/src/vm/Stack.h index 0412df61e6c..57bf06913d7 100644 --- a/js/src/vm/Stack.h +++ b/js/src/vm/Stack.h @@ -851,9 +851,9 @@ class StackFrame 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 diff --git a/mobile/chrome/content/AnimatedZoom.js b/mobile/chrome/content/AnimatedZoom.js index 9ea9a90ea90..95635653d48 100644 --- a/mobile/chrome/content/AnimatedZoom.js +++ b/mobile/chrome/content/AnimatedZoom.js @@ -76,6 +76,7 @@ const AnimatedZoom = { 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; @@ -84,14 +85,13 @@ const AnimatedZoom = { /** 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; diff --git a/mobile/chrome/content/Util.js b/mobile/chrome/content/Util.js index 72542987619..1b5b94fdf16 100644 --- a/mobile/chrome/content/Util.js +++ b/mobile/chrome/content/Util.js @@ -161,15 +161,29 @@ let Util = { 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() { diff --git a/mobile/chrome/content/browser-ui.js b/mobile/chrome/content/browser-ui.js index d7acdf7293e..6280f21ca7b 100644 --- a/mobile/chrome/content/browser-ui.js +++ b/mobile/chrome/content/browser-ui.js @@ -186,6 +186,8 @@ var BrowserUI = { }, lockToolbar: function lockToolbar() { + if (Util.isTablet()) + return; this._toolbarLocked++; document.getElementById("toolbar-moveable-container").top = "0"; if (this._toolbarLocked == 1) @@ -379,11 +381,6 @@ var BrowserUI = { return this._toolbarH; }, - get sidebarW() { - delete this._sidebarW; - return this._sidebarW = Elements.controls.getBoundingClientRect().width; - }, - sizeControls: function(windowW, windowH) { // tabs document.getElementById("tabs").resize(); @@ -552,11 +549,27 @@ var BrowserUI = { }, 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) { diff --git a/mobile/chrome/content/browser.js b/mobile/chrome/content/browser.js index 277a000f298..5d4157a8b6c 100644 --- a/mobile/chrome/content/browser.js +++ b/mobile/chrome/content/browser.js @@ -1038,7 +1038,7 @@ var Browser = { }, tryFloatToolbar: function tryFloatToolbar(dx, dy) { - if (this.floatedWhileDragging) + if (this.floatedWhileDragging || Util.isTablet()) return; let [leftvis, ritevis, leftw, ritew] = Browser.computeSidebarVisibility(dx, dy); @@ -1289,7 +1289,7 @@ Browser.MainDragger.prototype = { 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; @@ -1908,7 +1908,7 @@ const ContentTouchHandler = { // 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) { @@ -2899,7 +2899,6 @@ Tab.prototype = { 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; @@ -3155,15 +3154,18 @@ function rendererFactory(aBrowser, aCanvas) { */ 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; }, diff --git a/mobile/chrome/content/browser.xul b/mobile/chrome/content/browser.xul index 2efe83dcd27..06820f9641c 100644 --- a/mobile/chrome/content/browser.xul +++ b/mobile/chrome/content/browser.xul @@ -198,22 +198,22 @@ - + - - + - + - + diff --git a/mobile/chrome/content/input.js b/mobile/chrome/content/input.js index 73163ea4b2e..321fa769c1e 100644 --- a/mobile/chrome/content/input.js +++ b/mobile/chrome/content/input.js @@ -1209,6 +1209,7 @@ GestureModule.prototype = { 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; @@ -1223,13 +1224,13 @@ GestureModule.prototype = { 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; diff --git a/mobile/chrome/content/tabs.xml b/mobile/chrome/content/tabs.xml index 55e327ebdad..0081c2501ac 100644 --- a/mobile/chrome/content/tabs.xml +++ b/mobile/chrome/content/tabs.xml @@ -7,21 +7,61 @@ xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - + - - - - + + + + false - document.getAnonymousElementByAttribute(this, "anonid", "thumbnail"); - this.parentNode.parentNode; + document.getAnonymousElementByAttribute(this, "anonid", "thumbnail"); + document.getAnonymousElementByAttribute(this, "anonid", "reload"); + document.getAnonymousElementByAttribute(this, "anonid", "close-container"); + this.parentNode.parentNode; + + + + + + + + + + + + 0) @@ -118,7 +162,7 @@ - + @@ -241,7 +285,7 @@ // 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"; diff --git a/mobile/themes/core/browser.css b/mobile/themes/core/browser.css index 20766ee2142..a04f0c8398c 100644 --- a/mobile/themes/core/browser.css +++ b/mobile/themes/core/browser.css @@ -929,7 +929,7 @@ autocompleteresult.noresults > .autocomplete-item-container { -moz-box-align: start; } -#tabs > scrollbox { +#tabs > .tabs-scrollbox { max-width: 350px; } @@ -986,6 +986,7 @@ documenttab[selected="true"] > stack > .documenttab-thumbnail { .documenttab-close-container { position: relative; left: -16px; + -moz-margin-start: -16px; -moz-margin-end: 49px; } @@ -1575,46 +1576,6 @@ setting { 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, @@ -1640,4 +1601,6 @@ setting { #search-engines-popup { max-width: -moz-calc(@tablet_panel_minwidth@); } -} \ No newline at end of file +} + +%include tablet.css diff --git a/mobile/themes/core/gingerbread/browser.css b/mobile/themes/core/gingerbread/browser.css index 1b397d00699..45a8b4e78d3 100644 --- a/mobile/themes/core/gingerbread/browser.css +++ b/mobile/themes/core/gingerbread/browser.css @@ -879,7 +879,7 @@ autocompleteresult.noresults > .autocomplete-item-container { -moz-box-align: start; } -#tabs > scrollbox { +#tabs > .tabs-scrollbox { max-width: 350px; } @@ -935,6 +935,7 @@ documenttab[selected="true"] > stack > .documenttab-thumbnail { .documenttab-close-container { position: relative; left: -16px; + -moz-margin-start: -16px; -moz-margin-end: 49px; } @@ -1545,46 +1546,6 @@ setting { 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, @@ -1603,11 +1564,4 @@ setting { 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 diff --git a/mobile/themes/core/gingerbread/images/close-active-tablet-hdpi.png b/mobile/themes/core/gingerbread/images/close-active-tablet-hdpi.png new file mode 100644 index 00000000000..1e616b6698a Binary files /dev/null and b/mobile/themes/core/gingerbread/images/close-active-tablet-hdpi.png differ diff --git a/mobile/themes/core/gingerbread/images/close-background-hdpi.png b/mobile/themes/core/gingerbread/images/close-background-hdpi.png new file mode 100644 index 00000000000..1767f2db21d Binary files /dev/null and b/mobile/themes/core/gingerbread/images/close-background-hdpi.png differ diff --git a/mobile/themes/core/gingerbread/images/close-default-tablet-hdpi.png b/mobile/themes/core/gingerbread/images/close-default-tablet-hdpi.png new file mode 100644 index 00000000000..1e616b6698a Binary files /dev/null and b/mobile/themes/core/gingerbread/images/close-default-tablet-hdpi.png differ diff --git a/mobile/themes/core/gingerbread/images/close-inactive-tab-tablet-hdpi.png b/mobile/themes/core/gingerbread/images/close-inactive-tab-tablet-hdpi.png new file mode 100644 index 00000000000..fd8e46b0c15 Binary files /dev/null and b/mobile/themes/core/gingerbread/images/close-inactive-tab-tablet-hdpi.png differ diff --git a/mobile/themes/core/gingerbread/images/newtab-default-tablet-hdpi.png b/mobile/themes/core/gingerbread/images/newtab-default-tablet-hdpi.png new file mode 100644 index 00000000000..1d9d1ae2308 Binary files /dev/null and b/mobile/themes/core/gingerbread/images/newtab-default-tablet-hdpi.png differ diff --git a/mobile/themes/core/gingerbread/images/tab-reopen-tablet-hdpi.png b/mobile/themes/core/gingerbread/images/tab-reopen-tablet-hdpi.png new file mode 100644 index 00000000000..f8017dcd609 Binary files /dev/null and b/mobile/themes/core/gingerbread/images/tab-reopen-tablet-hdpi.png differ diff --git a/mobile/themes/core/honeycomb/browser.css b/mobile/themes/core/honeycomb/browser.css index 0568f036cb3..6a5943c175c 100644 --- a/mobile/themes/core/honeycomb/browser.css +++ b/mobile/themes/core/honeycomb/browser.css @@ -37,6 +37,7 @@ %filter substitution %include defines.inc +%define honeycomb 1 /* main toolbar (URL bar) -------------------------------------------------- */ #toolbar-main { @@ -962,7 +963,7 @@ autocompleteresult.noresults > .autocomplete-item-container { -moz-box-align: start; } -#tabs > scrollbox { +#tabs > .tabs-scrollbox { max-width: 350px; } @@ -1018,6 +1019,7 @@ documenttab[selected="true"] > stack > .documenttab-thumbnail { .documenttab-close-container { position: relative; left: -16px; + -moz-margin-start: -16px; -moz-margin-end: 49px; } @@ -1694,42 +1696,6 @@ setting { 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, @@ -1814,3 +1780,5 @@ setting { } } + +%include ../tablet.css diff --git a/mobile/themes/core/honeycomb/images/close-active-hdpi.png b/mobile/themes/core/honeycomb/images/close-active-hdpi.png index 935b7555e4d..1f19cd10638 100644 Binary files a/mobile/themes/core/honeycomb/images/close-active-hdpi.png and b/mobile/themes/core/honeycomb/images/close-active-hdpi.png differ diff --git a/mobile/themes/core/honeycomb/images/close-active-tablet-hdpi.png b/mobile/themes/core/honeycomb/images/close-active-tablet-hdpi.png new file mode 100644 index 00000000000..1e616b6698a Binary files /dev/null and b/mobile/themes/core/honeycomb/images/close-active-tablet-hdpi.png differ diff --git a/mobile/themes/core/honeycomb/images/close-background-hdpi.png b/mobile/themes/core/honeycomb/images/close-background-hdpi.png new file mode 100644 index 00000000000..1767f2db21d Binary files /dev/null and b/mobile/themes/core/honeycomb/images/close-background-hdpi.png differ diff --git a/mobile/themes/core/honeycomb/images/close-default-hdpi.png b/mobile/themes/core/honeycomb/images/close-default-hdpi.png index 3961ad1baaa..7e4b9f7d8d8 100644 Binary files a/mobile/themes/core/honeycomb/images/close-default-hdpi.png and b/mobile/themes/core/honeycomb/images/close-default-hdpi.png differ diff --git a/mobile/themes/core/honeycomb/images/close-default-tablet-hdpi.png b/mobile/themes/core/honeycomb/images/close-default-tablet-hdpi.png new file mode 100644 index 00000000000..1e616b6698a Binary files /dev/null and b/mobile/themes/core/honeycomb/images/close-default-tablet-hdpi.png differ diff --git a/mobile/themes/core/honeycomb/images/close-inactive-tab-hdpi.png b/mobile/themes/core/honeycomb/images/close-inactive-tab-hdpi.png index 105d7852edd..77d71107fa1 100644 Binary files a/mobile/themes/core/honeycomb/images/close-inactive-tab-hdpi.png and b/mobile/themes/core/honeycomb/images/close-inactive-tab-hdpi.png differ diff --git a/mobile/themes/core/honeycomb/images/close-inactive-tab-tablet-hdpi.png b/mobile/themes/core/honeycomb/images/close-inactive-tab-tablet-hdpi.png new file mode 100644 index 00000000000..fd8e46b0c15 Binary files /dev/null and b/mobile/themes/core/honeycomb/images/close-inactive-tab-tablet-hdpi.png differ diff --git a/mobile/themes/core/honeycomb/images/newtab-default-hdpi.png b/mobile/themes/core/honeycomb/images/newtab-default-hdpi.png index aacf3b927b0..e71e89cd79b 100644 Binary files a/mobile/themes/core/honeycomb/images/newtab-default-hdpi.png and b/mobile/themes/core/honeycomb/images/newtab-default-hdpi.png differ diff --git a/mobile/themes/core/honeycomb/images/newtab-default-tablet-hdpi.png b/mobile/themes/core/honeycomb/images/newtab-default-tablet-hdpi.png new file mode 100644 index 00000000000..1d9d1ae2308 Binary files /dev/null and b/mobile/themes/core/honeycomb/images/newtab-default-tablet-hdpi.png differ diff --git a/mobile/themes/core/honeycomb/images/tab-reopen-tablet-hdpi.png b/mobile/themes/core/honeycomb/images/tab-reopen-tablet-hdpi.png new file mode 100644 index 00000000000..f8017dcd609 Binary files /dev/null and b/mobile/themes/core/honeycomb/images/tab-reopen-tablet-hdpi.png differ diff --git a/mobile/themes/core/images/close-active-tablet-hdpi.png b/mobile/themes/core/images/close-active-tablet-hdpi.png new file mode 100644 index 00000000000..1e616b6698a Binary files /dev/null and b/mobile/themes/core/images/close-active-tablet-hdpi.png differ diff --git a/mobile/themes/core/images/close-background-hdpi.png b/mobile/themes/core/images/close-background-hdpi.png new file mode 100644 index 00000000000..1767f2db21d Binary files /dev/null and b/mobile/themes/core/images/close-background-hdpi.png differ diff --git a/mobile/themes/core/images/close-default-tablet-hdpi.png b/mobile/themes/core/images/close-default-tablet-hdpi.png new file mode 100644 index 00000000000..1e616b6698a Binary files /dev/null and b/mobile/themes/core/images/close-default-tablet-hdpi.png differ diff --git a/mobile/themes/core/images/close-inactive-tab-tablet-hdpi.png b/mobile/themes/core/images/close-inactive-tab-tablet-hdpi.png new file mode 100644 index 00000000000..fd8e46b0c15 Binary files /dev/null and b/mobile/themes/core/images/close-inactive-tab-tablet-hdpi.png differ diff --git a/mobile/themes/core/images/newtab-default-tablet-hdpi.png b/mobile/themes/core/images/newtab-default-tablet-hdpi.png new file mode 100644 index 00000000000..1d9d1ae2308 Binary files /dev/null and b/mobile/themes/core/images/newtab-default-tablet-hdpi.png differ diff --git a/mobile/themes/core/images/tab-reopen-tablet-hdpi.png b/mobile/themes/core/images/tab-reopen-tablet-hdpi.png new file mode 100644 index 00000000000..f8017dcd609 Binary files /dev/null and b/mobile/themes/core/images/tab-reopen-tablet-hdpi.png differ diff --git a/mobile/themes/core/jar.mn b/mobile/themes/core/jar.mn index eba240488e2..463145a1b29 100644 --- a/mobile/themes/core/jar.mn +++ b/mobile/themes/core/jar.mn @@ -90,10 +90,12 @@ chrome.jar: 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) @@ -110,8 +112,12 @@ chrome.jar: 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) @@ -214,10 +220,12 @@ chrome.jar: 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) @@ -233,8 +241,12 @@ chrome.jar: 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) @@ -339,10 +351,12 @@ chrome.jar: 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) @@ -359,8 +373,12 @@ chrome.jar: 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) diff --git a/mobile/themes/core/tablet.css b/mobile/themes/core/tablet.css new file mode 100644 index 00000000000..6158a78468c --- /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 diff --git a/modules/libpr0n/src/Makefile.in b/modules/libpr0n/src/Makefile.in index cf667f4cfd6..ff2c0d2560e 100644 --- a/modules/libpr0n/src/Makefile.in +++ b/modules/libpr0n/src/Makefile.in @@ -78,6 +78,7 @@ LOCAL_INCLUDES += \ 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) diff --git a/modules/libpr0n/src/RasterImage.cpp b/modules/libpr0n/src/RasterImage.cpp index d37fff50497..2c5f2429b99 100644 --- a/modules/libpr0n/src/RasterImage.cpp +++ b/modules/libpr0n/src/RasterImage.cpp @@ -42,6 +42,7 @@ * * ***** END LICENSE BLOCK ***** */ +#include "base/histogram.h" #include "nsComponentManagerUtils.h" #include "imgIContainerObserver.h" #include "ImageErrors.h" @@ -191,6 +192,7 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker) : mDecoder(nsnull), mWorker(nsnull), mBytesDecoded(0), + mDecodeCount(0), #ifdef DEBUG mFramesNotified(0), #endif @@ -208,6 +210,7 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker) : // 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++; @@ -2197,6 +2200,12 @@ RasterImage::InitDecoder(bool aDoSizeDecode) // 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; diff --git a/modules/libpr0n/src/RasterImage.h b/modules/libpr0n/src/RasterImage.h index da46e409e5c..fc28dde4c5b 100644 --- a/modules/libpr0n/src/RasterImage.h +++ b/modules/libpr0n/src/RasterImage.h @@ -503,6 +503,10 @@ private: // data nsRefPtr 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 diff --git a/netwerk/base/src/nsIOService.cpp b/netwerk/base/src/nsIOService.cpp index 9290472afbb..ed56a6086d9 100644 --- a/netwerk/base/src/nsIOService.cpp +++ b/netwerk/base/src/nsIOService.cpp @@ -234,7 +234,7 @@ nsIOService::Init() 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 @@ -969,7 +969,7 @@ nsIOService::Observe(nsISupports *subject, } } } - 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. diff --git a/toolkit/components/build/nsToolkitCompsCID.h b/toolkit/components/build/nsToolkitCompsCID.h index 4c6302bd2a0..da4e3168594 100644 --- a/toolkit/components/build/nsToolkitCompsCID.h +++ b/toolkit/components/build/nsToolkitCompsCID.h @@ -81,6 +81,9 @@ #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" @@ -156,7 +159,11 @@ // {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} } diff --git a/toolkit/components/build/nsToolkitCompsModule.cpp b/toolkit/components/build/nsToolkitCompsModule.cpp index 9c16dd1dfa7..4fd04ed0db8 100644 --- a/toolkit/components/build/nsToolkitCompsModule.cpp +++ b/toolkit/components/build/nsToolkitCompsModule.cpp @@ -63,6 +63,7 @@ #include "nsUrlClassifierDBService.h" #include "nsUrlClassifierStreamUpdater.h" #include "nsUrlClassifierUtils.h" +#include "nsUrlClassifierPrefixSet.h" #endif #ifdef MOZ_FEEDS @@ -94,6 +95,7 @@ NS_GENERIC_FACTORY_CONSTRUCTOR(nsDownloadProxy) 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) @@ -138,6 +140,7 @@ NS_DEFINE_NAMED_CID(NS_DOWNLOAD_CID); 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); @@ -164,6 +167,7 @@ static const mozilla::Module::CIDEntry kToolkitCIDs[] = { { &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 }, @@ -192,6 +196,7 @@ static const mozilla::Module::ContractIDEntry kToolkitContracts[] = { { 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 }, diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index dffcffa0707..9fa3b19deb1 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -149,7 +149,7 @@ GetHistogramByEnumId(Telemetry::ID id, Histogram **ret) 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; @@ -411,6 +411,14 @@ Accumulate(ID aHistogram, PRUint32 aSample) h->Add(aSample); } +base::Histogram* +GetHistogramById(ID id) +{ + Histogram *h = NULL; + GetHistogramByEnumId(id, &h); + return h; +} + } // namespace Telemetry } // namespace mozilla diff --git a/toolkit/components/telemetry/Telemetry.h b/toolkit/components/telemetry/Telemetry.h index 79231bacbb8..d4d099174bb 100644 --- a/toolkit/components/telemetry/Telemetry.h +++ b/toolkit/components/telemetry/Telemetry.h @@ -42,6 +42,10 @@ #include "mozilla/TimeStamp.h" #include "mozilla/AutoRestore.h" +namespace base { + class Histogram; +} + namespace mozilla { namespace Telemetry { @@ -62,6 +66,11 @@ HistogramCount */ 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 class AutoTimer { public: diff --git a/toolkit/components/telemetry/TelemetryHistograms.h b/toolkit/components/telemetry/TelemetryHistograms.h index d7187c05f62..3ec188e1c1b 100644 --- a/toolkit/components/telemetry/TelemetryHistograms.h +++ b/toolkit/components/telemetry/TelemetryHistograms.h @@ -97,6 +97,7 @@ HISTOGRAM(IMAGE_DECODE_LATENCY, 50, 5000000, 100, EXPONENTIAL, "Time spent deco 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 @@ -167,6 +168,15 @@ HISTOGRAM(NETWORK_DISK_CACHE_OPEN, 1, 10000, 10, EXPONENTIAL, "Time spent openin 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. */ diff --git a/toolkit/components/url-classifier/Makefile.in b/toolkit/components/url-classifier/Makefile.in index 9dfe0e86437..7d690233102 100644 --- a/toolkit/components/url-classifier/Makefile.in +++ b/toolkit/components/url-classifier/Makefile.in @@ -53,6 +53,7 @@ XPIDLSRCS = \ nsIUrlClassifierDBService.idl \ nsIUrlClassifierHashCompleter.idl \ nsIUrlClassifierStreamUpdater.idl \ + nsIUrlClassifierPrefixSet.idl \ nsIUrlClassifierUtils.idl \ nsIUrlListManager.idl \ $(NULL) @@ -61,6 +62,7 @@ CPPSRCS = \ nsUrlClassifierDBService.cpp \ nsUrlClassifierStreamUpdater.cpp \ nsUrlClassifierUtils.cpp \ + nsUrlClassifierPrefixSet.cpp \ nsUrlClassifierProxies.cpp \ $(NULL) diff --git a/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl b/toolkit/components/url-classifier/nsIUrlClassifierPrefixSet.idl new file mode 100644 index 00000000000..4a1fbf59d35 --- /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); +}; diff --git a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp index ec45accb8e6..3eba13eabbb 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp +++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.cpp @@ -24,6 +24,7 @@ * Brett Wilson * Dave Camp * David Dahl + * Gian-Carlo Pascutto * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -74,6 +75,7 @@ #include "nsThreadUtils.h" #include "nsXPCOMStrings.h" #include "mozilla/Mutex.h" +#include "mozilla/Telemetry.h" #include "prlog.h" #include "prprf.h" #include "prnetdb.h" @@ -137,6 +139,9 @@ static const PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull; // 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 @@ -161,9 +166,8 @@ static const PRLogModuleInfo *gUrlClassifierDbServiceLog = nsnull; #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 @@ -190,6 +194,7 @@ 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; @@ -273,6 +278,9 @@ struct nsUrlClassifierHash NS_ASSERTION(sHashSize >= PARTIAL_LENGTH, "nsUrlClassifierHash must be at least PARTIAL_LENGTH bytes long"); return memcmp(buf, hash.buf, PARTIAL_LENGTH) == 0; } + PRUint32 ToUint32() const { + return *(reinterpret_cast(buf)); + } }; typedef nsUrlClassifierHash nsUrlClassifierDomainHash; @@ -480,6 +488,9 @@ public: PRBool before, nsTArray &entries); + // Return an array with all Prefixes known + nsresult ReadPrefixes(nsTArray& array, PRUint32 aKey); + protected: nsresult ReadEntries(mozIStorageStatement *statement, nsTArray& entries); @@ -497,6 +508,8 @@ protected: nsCOMPtr mPartialEntriesAfterStatement; nsCOMPtr mLastPartialEntriesStatement; nsCOMPtr mPartialEntriesBeforeStatement; + + nsCOMPtr mAllPrefixStatement; }; nsresult @@ -553,6 +566,12 @@ nsUrlClassifierStore::Init(nsUrlClassifierDBServiceWorker *worker, 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; } @@ -571,6 +590,8 @@ nsUrlClassifierStore::Close() mPartialEntriesBeforeStatement = nsnull; mLastPartialEntriesStatement = nsnull; + mAllPrefixStatement = nsnull; + mConnection = nsnull; } @@ -993,6 +1014,39 @@ nsUrlClassifierSubStore::Close() mExpireAddChunkStatement = nsnull; } +// Similar to GetKey(), but if the domain contains three or more components, +// two keys will be returned: +// hostname.com/foo/bar -> [hostname.com] +// mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com] +// www.mail.hostname.com/foo/bar -> [hostname.com, mail.hostname.com] +static nsresult GetHostKeys(const nsACString &spec, + nsTArray &hostKeys); + +// take a lookup string (www.hostname.com/path/to/resource.html) and +// expand it into the set of fragments that should be searched for in an +// entry +static nsresult GetLookupFragments(const nsCSubstring& spec, + nsTArray& fragments); + +// Check for a canonicalized IP address. +static 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 @@ -1005,16 +1059,13 @@ public: NS_DECL_NSIURLCLASSIFIERDBSERVICEWORKER // Initialize, called in the main thread - nsresult Init(PRInt32 gethashNoise); + nsresult Init(PRInt32 gethashNoise, + nsRefPtr & 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(); @@ -1129,36 +1180,6 @@ private: // 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& 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 &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, @@ -1172,7 +1193,16 @@ private: PRInt32 count, nsTArray& results); + // Construct a Prefix Set with known prefixes + nsresult LoadPrefixSet(nsCOMPtr & aFile); + nsresult ConstructPrefixSet(); + + // Set the SQLite cache size + nsresult SetCacheSize(mozIStorageConnection * aConnection, + PRInt32 aCacheSize); + nsCOMPtr mDBFile; + nsCOMPtr mPSFile; nsCOMPtr mCryptoHash; @@ -1260,23 +1290,8 @@ private: // 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 mCachedEntries; + // Set of prefixes known to be in the database + nsRefPtr mPrefixSet; // Pending lookups are stored in a queue for processing. The queue // is protected by mPendingLookupLock. @@ -1315,7 +1330,7 @@ nsUrlClassifierDBServiceWorker::nsUrlClassifierDBServiceWorker() , mHaveCachedSubChunks(PR_FALSE) , mUpdateStartTime(0) , mGethashNoise(0) - , mCleanHostKeysLock("nsUrlClassifierDBServerWorker.mCleanHostKeysLock") + , mPrefixSet(0) , mPendingLookupLock("nsUrlClassifierDBServerWorker.mPendingLookupLock") { } @@ -1328,9 +1343,11 @@ nsUrlClassifierDBServiceWorker::~nsUrlClassifierDBServiceWorker() } nsresult -nsUrlClassifierDBServiceWorker::Init(PRInt32 gethashNoise) +nsUrlClassifierDBServiceWorker::Init(PRInt32 gethashNoise, + nsRefPtr & prefSet) { mGethashNoise = gethashNoise; + mPrefixSet = prefSet; // Compute database filename @@ -1346,14 +1363,14 @@ nsUrlClassifierDBServiceWorker::Init(PRInt32 gethashNoise) 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(); @@ -1378,29 +1395,114 @@ nsUrlClassifierDBServiceWorker::QueueLookup(const nsACString& spec, } nsresult -nsUrlClassifierDBServiceWorker::CheckCleanHost(const nsACString &spec, - PRBool *clean) +nsUrlClassifierDBService::CheckClean(const nsACString &spec, + PRBool *clean) { - nsAutoTArray lookupHosts; - nsresult rv = GetHostKeys(spec, lookupHosts); + Telemetry::AutoTimer timer; + + // Get the set of fragments to look up. + nsTArray fragments; + nsresult rv = GetLookupFragments(spec, fragments); NS_ENSURE_SUCCESS(rv, rv); - MutexAutoLock lock(mCleanHostKeysLock); + PRUint32 prefixkey; + rv = mPrefixSet->GetKey(&prefixkey); + NS_ENSURE_SUCCESS(rv, rv); - 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 &hostKeys) +{ + nsACString::const_iterator begin, end, iter; + spec.BeginReading(begin); + spec.EndReading(end); + + iter = begin; + if (!FindCharInReadable('/', iter, end)) { + return NS_OK; + } + + const nsCSubstring& host = Substring(begin, iter); + + if (IsCanonicalizedIP(host)) { + nsCString *key = hostKeys.AppendElement(); + if (!key) + return NS_ERROR_OUT_OF_MEMORY; + + key->Assign(host); + key->Append("/"); + return NS_OK; + } + + nsTArray hostComponents; + ParseString(PromiseFlatCString(host), '.', hostComponents); + + if (hostComponents.Length() < 2) { + // no host or toplevel host, this won't match anything in the db + return NS_OK; + } + + // First check with two domain components + PRInt32 last = PRInt32(hostComponents.Length()) - 1; + nsCString *lookupHost = hostKeys.AppendElement(); + if (!lookupHost) + return NS_ERROR_OUT_OF_MEMORY; + + lookupHost->Assign(hostComponents[last - 1]); + lookupHost->Append("."); + lookupHost->Append(hostComponents[last]); + lookupHost->Append("/"); + + // Now check with three domain components + if (hostComponents.Length() > 2) { + nsCString *lookupHost2 = hostKeys.AppendElement(); + if (!lookupHost2) + return NS_ERROR_OUT_OF_MEMORY; + lookupHost2->Assign(hostComponents[last - 2]); + lookupHost2->Append("."); + lookupHost2->Append(*lookupHost); + } + + return NS_OK; +} + nsresult -nsUrlClassifierDBServiceWorker::GetLookupFragments(const nsACString& spec, - nsTArray& fragments) +GetLookupFragments(const nsACString& spec, + nsTArray& fragments) { fragments.Clear(); @@ -1501,72 +1603,26 @@ nsUrlClassifierDBServiceWorker::GetLookupFragments(const nsACString& spec, return NS_OK; } -nsresult -nsUrlClassifierDBServiceWorker::CacheEntries(const nsACString& spec) -{ - nsAutoTArray 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& results) { - // Read any entries that might apply to this URI into mCachedEntries - nsresult rv = CacheEntries(spec); - NS_ENSURE_SUCCESS(rv, rv); + PRInt64 now = (PR_Now() / PR_USEC_PER_SEC); - if (mCachedEntries.Length() == 0) { - return NS_OK; + // Get list of host keys to look up + nsAutoTArray lookupHosts; + nsresult rv = GetHostKeys(spec, lookupHosts); + + nsTArray mCachedEntries; + + // Gather host's prefixes + for (PRUint32 i = 0; i < lookupHosts.Length(); i++) { + // Find the corresponding host key + nsUrlClassifierDomainHash hostKey; + nsresult rv = GetKey(lookupHosts[i], hostKey, mCryptoHash); + NS_ENSURE_SUCCESS(rv, rv); + // Read the entries for this fragments host from SQLite + mMainStore.ReadAddEntries(hostKey, mCachedEntries); } // Now get the set of fragments to look up. @@ -1574,18 +1630,11 @@ nsUrlClassifierDBServiceWorker::Check(const nsACString& spec, 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)) { @@ -1618,17 +1667,10 @@ nsUrlClassifierDBServiceWorker::Check(const nsACString& spec, // 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; @@ -1871,7 +1913,6 @@ nsUrlClassifierDBServiceWorker::GetTableName(PRUint32 tableId, 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); @@ -1983,8 +2024,8 @@ nsUrlClassifierStore::UpdateEntry(nsUrlClassifierEntry& entry) 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. @@ -1998,9 +2039,10 @@ nsUrlClassifierDBServiceWorker::IsCanonicalizedIP(const nsACString& host) 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); @@ -2017,7 +2059,7 @@ nsUrlClassifierDBServiceWorker::GetKey(const nsACString& spec, nsCAutoString key; key.Assign(host); key.Append("/"); - return hash.FromPlaintext(key, mCryptoHash); + return hash.FromPlaintext(key, aCryptoHash); } nsTArray hostComponents; @@ -2039,64 +2081,7 @@ nsUrlClassifierDBServiceWorker::GetKey(const nsACString& spec, lookupHost.Append(hostComponents[last]); lookupHost.Append("/"); - return hash.FromPlaintext(lookupHost, mCryptoHash); -} - -nsresult -nsUrlClassifierDBServiceWorker::GetHostKeys(const nsACString &spec, - nsTArray &hostKeys) -{ - nsACString::const_iterator begin, end, iter; - spec.BeginReading(begin); - spec.EndReading(end); - - iter = begin; - if (!FindCharInReadable('/', iter, end)) { - return NS_OK; - } - - const nsCSubstring& host = Substring(begin, iter); - - if (IsCanonicalizedIP(host)) { - nsCString *key = hostKeys.AppendElement(); - if (!key) - return NS_ERROR_OUT_OF_MEMORY; - - key->Assign(host); - key->Append("/"); - return NS_OK; - } - - nsTArray hostComponents; - ParseString(PromiseFlatCString(host), '.', hostComponents); - - if (hostComponents.Length() < 2) { - // no host or toplevel host, this won't match anything in the db - return NS_OK; - } - - // First check with two domain components - PRInt32 last = PRInt32(hostComponents.Length()) - 1; - nsCString *lookupHost = hostKeys.AppendElement(); - if (!lookupHost) - return NS_ERROR_OUT_OF_MEMORY; - - lookupHost->Assign(hostComponents[last - 1]); - lookupHost->Append("."); - lookupHost->Append(hostComponents[last]); - lookupHost->Append("/"); - - // Now check with three domain components - if (hostComponents.Length() > 2) { - nsCString *lookupHost2 = hostKeys.AppendElement(); - if (!lookupHost2) - return NS_ERROR_OUT_OF_MEMORY; - lookupHost2->Assign(hostComponents[last - 2]); - lookupHost2->Append("."); - lookupHost2->Append(*lookupHost); - } - - return NS_OK; + return hash.FromPlaintext(lookupHost, aCryptoHash); } nsresult @@ -2249,7 +2234,7 @@ nsUrlClassifierDBServiceWorker::GetChunkEntries(const nsACString& table, entryStr = lines[i]; } - rv = GetKey(entryStr, entry->mKey); + rv = GetKey(entryStr, entry->mKey, mCryptoHash); NS_ENSURE_SUCCESS(rv, rv); entry->mTableId = tableId; @@ -2862,18 +2847,6 @@ nsUrlClassifierDBServiceWorker::ResetUpdate() 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) @@ -3119,6 +3092,26 @@ nsUrlClassifierDBServiceWorker::FinishStream() 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() { @@ -3135,18 +3128,11 @@ nsUrlClassifierDBServiceWorker::SetupUpdate() 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; @@ -3169,15 +3155,16 @@ nsUrlClassifierDBServiceWorker::ApplyUpdate() } 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(); @@ -3249,12 +3236,15 @@ nsUrlClassifierDBServiceWorker::ResetDatabase() 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; } @@ -3332,11 +3322,6 @@ nsUrlClassifierDBServiceWorker::CacheCompletions(nsTArrayExecuteSimpleSQL(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); @@ -3452,16 +3446,225 @@ nsUrlClassifierDBServiceWorker::OpenDb() 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& 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(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(blobfull)); + } + } else { + prefixval = *(reinterpret_cast(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 timer; + + PRUint32 key; + nsresult rv = mPrefixSet->GetKey(&key); + NS_ENSURE_SUCCESS(rv, rv); + + nsTArray 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 & 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 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; } @@ -3694,6 +3897,9 @@ nsUrlClassifierLookupCallback::Completion(const nsACString& completeHash, // 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)); } } } @@ -3854,8 +4060,10 @@ nsUrlClassifierDBService::Init() NS_ENSURE_SUCCESS(rv, rv); // Force PSM to be loaded on the main thread. - nsCOMPtr 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? @@ -3894,6 +4102,9 @@ nsUrlClassifierDBService::Init() 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); @@ -3912,7 +4123,7 @@ nsUrlClassifierDBService::Init() 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; @@ -4001,10 +4212,10 @@ nsUrlClassifierDBService::LookupURI(nsIURI* uri, 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) { @@ -4200,6 +4411,10 @@ nsUrlClassifierDBService::Observe(nsISupports *aSubject, const char *aTopic, 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); @@ -4245,6 +4460,7 @@ nsUrlClassifierDBService::Shutdown() 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"); } diff --git a/toolkit/components/url-classifier/nsUrlClassifierDBService.h b/toolkit/components/url-classifier/nsUrlClassifierDBService.h index 55ed78898f8..3a6676b1c80 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierDBService.h +++ b/toolkit/components/url-classifier/nsUrlClassifierDBService.h @@ -45,10 +45,13 @@ #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 @@ -99,9 +102,13 @@ private: 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 mWorker; nsCOMPtr mWorkerProxy; @@ -124,6 +131,10 @@ private: // The list of tables that can use the default hash completer object. nsTArray mGethashWhitelist; + // Set of prefixes known to be in the database + nsRefPtr mPrefixSet; + nsCOMPtr mHash; + // Thread that we do the updates on. static nsIThread* gDbBackgroundThread; }; diff --git a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.cpp new file mode 100644 index 00000000000..3b0972a9621 --- /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 + * Mehdi Mulani + * + * 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 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 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 mNewIndexPrefixes(mIndexPrefixes); + nsTArray mNewIndexStarts(mIndexStarts); + nsTArray 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 mNewIndexPrefixes; + nsTArray mNewIndexStarts; + nsTArray 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 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 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); +} diff --git a/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h b/toolkit/components/url-classifier/nsUrlClassifierPrefixSet.h new file mode 100644 index 00000000000..865294b8d80 --- /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 + * Mehdi Mulani + * + * 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 mIndexPrefixes; + // the value corresponds to the beginning of the run + // (an index in |_deltas|) for the index + nsTArray mIndexStarts; + // array containing deltas from indices. + nsTArray mDeltas; +}; + +#endif diff --git a/toolkit/components/url-classifier/nsUrlClassifierUtils.h b/toolkit/components/url-classifier/nsUrlClassifierUtils.h index c5040667917..3658c4bfc54 100644 --- a/toolkit/components/url-classifier/nsUrlClassifierUtils.h +++ b/toolkit/components/url-classifier/nsUrlClassifierUtils.h @@ -124,125 +124,4 @@ private: nsAutoPtr 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 mEntryStorage; - - // Entry lookup by fragment. - nsDataHashtable mEntries; -}; - #endif // nsUrlClassifierUtils_h_ diff --git a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js index 7f82cbc8d78..c9cf375cd30 100644 --- a/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js +++ b/toolkit/components/url-classifier/tests/unit/head_urlclassifier.js @@ -1,3 +1,4 @@ +//* -*- Mode: Javascript; tab-width: 8; indent-tabs-mode: nil; js-indent-level: 2 -*- * function dumpn(s) { dump(s + "\n"); } @@ -277,6 +278,8 @@ function runNextTest() dbservice.setHashCompleter('test-phish-simple', null); dumpn("running " + gTests[gNextTest]); + dump("running " + gTests[gNextTest]); + gTests[gNextTest++](); } diff --git a/toolkit/components/url-classifier/tests/unit/test_cleankeycache.js b/toolkit/components/url-classifier/tests/unit/test_cleankeycache.js index 28edb17c6e1..d2e22d0c474 100644 --- a/toolkit/components/url-classifier/tests/unit/test_cleankeycache.js +++ b/toolkit/components/url-classifier/tests/unit/test_cleankeycache.js @@ -1,3 +1,4 @@ +//* -*- 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" ]; @@ -24,45 +25,12 @@ function testCleanHostKeys() { }); // 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); } @@ -71,109 +39,128 @@ 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... + doStreamUpdate(preUpdate, function() { + // First lookup won't happen... + var uri = ios.newURI("http://foo.com/a", null, null); + + // Use the nsIURIClassifier interface (the + // nsIUrlClassifierDBService will always queue a lookup, + // nsIURIClassifier won't if the host key is known to be clean. + var classifier = dbservice.QueryInterface(Ci.nsIURIClassifier); + var result = classifier.classify(uri, function(errorCode) { + // shouldn't arrive here do_check_eq(errorCode, Cr.NS_OK); - - // 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); + 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); - 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/" ]; + // 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 uris2 = []; + 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 runSecondLookup = function() { - if (uris2.length == 0) { - runNextTest(); - return; + var uris2 = []; + + var runSecondLookup = function() { + if (uris2.length == 0) { + runNextTest(); + return; + } + + var spec = uris2.pop(); + var uri = ios.newURI("http://" + spec, null, null); + + var result = classifier.classify(uri, function(errorCode) { + }); + runSecondLookup(); + // now look up a few more times. } - var 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() { @@ -200,7 +187,6 @@ function run_test() // directly after dbservice.resetDatabase(). testUpdate, testCleanHostKeys, - testDirtyHostKeys, testResetFullCache, testBug475436 ]); diff --git a/toolkit/components/url-classifier/tests/unit/test_partial.js b/toolkit/components/url-classifier/tests/unit/test_partial.js index 1a1ccd3aad2..13df8eca09a 100644 --- a/toolkit/components/url-classifier/tests/unit/test_partial.js +++ b/toolkit/components/url-classifier/tests/unit/test_partial.js @@ -87,12 +87,12 @@ compareQueries: function(fragments) 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); } }; @@ -421,15 +421,23 @@ function testInvalidHashSize() }], 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() diff --git a/toolkit/components/url-classifier/tests/unit/test_prefixset.js b/toolkit/components/url-classifier/tests/unit/test_prefixset.js new file mode 100644 index 00000000000..6200f4152d5 --- /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](); + } +} diff --git a/toolkit/components/url-classifier/tests/unit/test_streamupdater.js b/toolkit/components/url-classifier/tests/unit/test_streamupdater.js index 3786876c582..224cc871c2c 100644 --- a/toolkit/components/url-classifier/tests/unit/test_streamupdater.js +++ b/toolkit/components/url-classifier/tests/unit/test_streamupdater.js @@ -27,6 +27,25 @@ function doTest(updates, assertions, expectError, 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" ]; @@ -61,6 +80,8 @@ function testSimpleForward() { // Make sure that a nested forward (a forward within a forward) causes // the update to fail. function testNestedForward() { + testFillDb(); // Make sure the db isn't empty + var add1Urls = [ "foo.com/a", "bar.com/c" ]; var add2Urls = [ "foo.com/b" ]; @@ -182,6 +203,8 @@ function testValidMAC() { // Test a simple update with an invalid message authentication code. function testInvalidMAC() { + testFillDb(); // Make sure the db isn't empty + var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ]; var update = buildPhishingUpdate( [ @@ -201,6 +224,8 @@ function testInvalidMAC() { // Test a simple update without a message authentication code, when it is // expecting one. function testNoMAC() { + testFillDb(); // Make sure the db isn't empty + var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ]; var update = buildPhishingUpdate( [ @@ -257,6 +282,8 @@ function testValidForwardMAC() { // Test an update with a valid message authentication code, but with // invalid MACs on the forwards. function testInvalidForwardMAC() { + testFillDb(); // Make sure the db isn't empty + var add1Urls = [ "foo.com/a", "bar.com/c" ]; var add2Urls = [ "foo.com/b" ]; var add3Urls = [ "bar.com/d" ]; @@ -296,6 +323,8 @@ function testInvalidForwardMAC() { // Test an update with a valid message authentication code, but no MAC // specified for sub-urls. function testNoForwardMAC() { + testFillDb(); // Make sure the db isn't empty + var add1Urls = [ "foo.com/a", "bar.com/c" ]; var add2Urls = [ "foo.com/b" ]; var add3Urls = [ "bar.com/d" ]; @@ -362,6 +391,8 @@ gAssertions.gotRekey = function(data, cb) // Tests a rekey request. function testRekey() { + testFillDb(); + var addUrls = [ "foo.com/a", "foo.com/b", "bar.com/c" ]; var update = buildPhishingUpdate( [ diff --git a/toolkit/components/url-classifier/tests/unit/xpcshell.ini b/toolkit/components/url-classifier/tests/unit/xpcshell.ini index 21a23504565..8de09eda577 100644 --- a/toolkit/components/url-classifier/tests/unit/xpcshell.ini +++ b/toolkit/components/url-classifier/tests/unit/xpcshell.ini @@ -8,4 +8,5 @@ tail = tail_urlclassifier.js [test_dbservice.js] [test_hashcompleter.js] [test_partial.js] +[test_prefixset.js] [test_streamupdater.js] diff --git a/widget/src/android/nsAppShell.cpp b/widget/src/android/nsAppShell.cpp index de550131ada..910a7b3e67c 100644 --- a/widget/src/android/nsAppShell.cpp +++ b/widget/src/android/nsAppShell.cpp @@ -446,10 +446,6 @@ nsAppShell::PeekNextEvent() 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) {