Sat, 03 Jan 2015 20:18:00 +0100
Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.
michael@0 | 1 | /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
michael@0 | 2 | * vim: set ts=8 sts=4 et sw=4 tw=99: |
michael@0 | 3 | */ |
michael@0 | 4 | /* This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 5 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 6 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 7 | |
michael@0 | 8 | #include "jscntxt.h" |
michael@0 | 9 | |
michael@0 | 10 | #include "js/OldDebugAPI.h" |
michael@0 | 11 | #include "jsapi-tests/tests.h" |
michael@0 | 12 | |
michael@0 | 13 | using namespace js; |
michael@0 | 14 | |
michael@0 | 15 | static int callCounts[2] = {0, 0}; |
michael@0 | 16 | |
michael@0 | 17 | static void * |
michael@0 | 18 | callCountHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, bool before, |
michael@0 | 19 | bool *ok, void *closure) |
michael@0 | 20 | { |
michael@0 | 21 | callCounts[before]++; |
michael@0 | 22 | |
michael@0 | 23 | JS::RootedValue thisv(cx); |
michael@0 | 24 | frame.getThisValue(cx, &thisv); // assert if fp is incomplete |
michael@0 | 25 | |
michael@0 | 26 | return cx; // any non-null value causes the hook to be called again after |
michael@0 | 27 | } |
michael@0 | 28 | |
michael@0 | 29 | BEGIN_TEST(testDebugger_bug519719) |
michael@0 | 30 | { |
michael@0 | 31 | CHECK(JS_SetDebugMode(cx, true)); |
michael@0 | 32 | JS_SetCallHook(rt, callCountHook, nullptr); |
michael@0 | 33 | EXEC("function call(fn) { fn(0); }\n" |
michael@0 | 34 | "function f(g) { for (var i = 0; i < 9; i++) call(g); }\n" |
michael@0 | 35 | "f(Math.sin);\n" // record loop, starting in f |
michael@0 | 36 | "f(Math.cos);\n"); // side exit in f -> call |
michael@0 | 37 | CHECK_EQUAL(callCounts[0], 20); |
michael@0 | 38 | CHECK_EQUAL(callCounts[1], 20); |
michael@0 | 39 | return true; |
michael@0 | 40 | } |
michael@0 | 41 | END_TEST(testDebugger_bug519719) |
michael@0 | 42 | |
michael@0 | 43 | static void * |
michael@0 | 44 | nonStrictThisHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, bool before, |
michael@0 | 45 | bool *ok, void *closure) |
michael@0 | 46 | { |
michael@0 | 47 | if (before) { |
michael@0 | 48 | bool *allWrapped = (bool *) closure; |
michael@0 | 49 | JS::RootedValue thisv(cx); |
michael@0 | 50 | frame.getThisValue(cx, &thisv); |
michael@0 | 51 | *allWrapped = *allWrapped && !JSVAL_IS_PRIMITIVE(thisv); |
michael@0 | 52 | } |
michael@0 | 53 | return nullptr; |
michael@0 | 54 | } |
michael@0 | 55 | |
michael@0 | 56 | BEGIN_TEST(testDebugger_getThisNonStrict) |
michael@0 | 57 | { |
michael@0 | 58 | bool allWrapped = true; |
michael@0 | 59 | CHECK(JS_SetDebugMode(cx, true)); |
michael@0 | 60 | JS_SetCallHook(rt, nonStrictThisHook, (void *) &allWrapped); |
michael@0 | 61 | EXEC("function nonstrict() { }\n" |
michael@0 | 62 | "Boolean.prototype.nonstrict = nonstrict;\n" |
michael@0 | 63 | "String.prototype.nonstrict = nonstrict;\n" |
michael@0 | 64 | "Number.prototype.nonstrict = nonstrict;\n" |
michael@0 | 65 | "Object.prototype.nonstrict = nonstrict;\n" |
michael@0 | 66 | "nonstrict.call(true);\n" |
michael@0 | 67 | "true.nonstrict();\n" |
michael@0 | 68 | "nonstrict.call('');\n" |
michael@0 | 69 | "''.nonstrict();\n" |
michael@0 | 70 | "nonstrict.call(42);\n" |
michael@0 | 71 | "(42).nonstrict();\n" |
michael@0 | 72 | // The below don't really get 'wrapped', but it's okay. |
michael@0 | 73 | "nonstrict.call(undefined);\n" |
michael@0 | 74 | "nonstrict.call(null);\n" |
michael@0 | 75 | "nonstrict.call({});\n" |
michael@0 | 76 | "({}).nonstrict();\n"); |
michael@0 | 77 | CHECK(allWrapped); |
michael@0 | 78 | return true; |
michael@0 | 79 | } |
michael@0 | 80 | END_TEST(testDebugger_getThisNonStrict) |
michael@0 | 81 | |
michael@0 | 82 | static void * |
michael@0 | 83 | strictThisHook(JSContext *cx, JSAbstractFramePtr frame, bool isConstructing, bool before, |
michael@0 | 84 | bool *ok, void *closure) |
michael@0 | 85 | { |
michael@0 | 86 | if (before) { |
michael@0 | 87 | bool *anyWrapped = (bool *) closure; |
michael@0 | 88 | JS::RootedValue thisv(cx); |
michael@0 | 89 | frame.getThisValue(cx, &thisv); |
michael@0 | 90 | *anyWrapped = *anyWrapped || !JSVAL_IS_PRIMITIVE(thisv); |
michael@0 | 91 | } |
michael@0 | 92 | return nullptr; |
michael@0 | 93 | } |
michael@0 | 94 | |
michael@0 | 95 | BEGIN_TEST(testDebugger_getThisStrict) |
michael@0 | 96 | { |
michael@0 | 97 | bool anyWrapped = false; |
michael@0 | 98 | CHECK(JS_SetDebugMode(cx, true)); |
michael@0 | 99 | JS_SetCallHook(rt, strictThisHook, (void *) &anyWrapped); |
michael@0 | 100 | EXEC("function strict() { 'use strict'; }\n" |
michael@0 | 101 | "Boolean.prototype.strict = strict;\n" |
michael@0 | 102 | "String.prototype.strict = strict;\n" |
michael@0 | 103 | "Number.prototype.strict = strict;\n" |
michael@0 | 104 | "strict.call(true);\n" |
michael@0 | 105 | "true.strict();\n" |
michael@0 | 106 | "strict.call('');\n" |
michael@0 | 107 | "''.strict();\n" |
michael@0 | 108 | "strict.call(42);\n" |
michael@0 | 109 | "(42).strict();\n" |
michael@0 | 110 | "strict.call(undefined);\n" |
michael@0 | 111 | "strict.call(null);\n"); |
michael@0 | 112 | CHECK(!anyWrapped); |
michael@0 | 113 | return true; |
michael@0 | 114 | } |
michael@0 | 115 | END_TEST(testDebugger_getThisStrict) |
michael@0 | 116 | |
michael@0 | 117 | static bool calledThrowHook = false; |
michael@0 | 118 | |
michael@0 | 119 | static JSTrapStatus |
michael@0 | 120 | ThrowHook(JSContext *cx, JSScript *, jsbytecode *, jsval *rval, void *closure) |
michael@0 | 121 | { |
michael@0 | 122 | JS_ASSERT(!closure); |
michael@0 | 123 | calledThrowHook = true; |
michael@0 | 124 | |
michael@0 | 125 | JS::RootedObject global(cx, JS::CurrentGlobalOrNull(cx)); |
michael@0 | 126 | |
michael@0 | 127 | char text[] = "new Error()"; |
michael@0 | 128 | JS::RootedValue _(cx); |
michael@0 | 129 | JS_EvaluateScript(cx, global, text, strlen(text), "", 0, &_); |
michael@0 | 130 | |
michael@0 | 131 | return JSTRAP_CONTINUE; |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | BEGIN_TEST(testDebugger_throwHook) |
michael@0 | 135 | { |
michael@0 | 136 | CHECK(JS_SetDebugMode(cx, true)); |
michael@0 | 137 | CHECK(JS_SetThrowHook(rt, ThrowHook, nullptr)); |
michael@0 | 138 | EXEC("function foo() { throw 3 };\n" |
michael@0 | 139 | "for (var i = 0; i < 10; ++i) { \n" |
michael@0 | 140 | " var x = {}\n" |
michael@0 | 141 | " try {\n" |
michael@0 | 142 | " foo(); \n" |
michael@0 | 143 | " } catch(e) {}\n" |
michael@0 | 144 | "}\n"); |
michael@0 | 145 | CHECK(calledThrowHook); |
michael@0 | 146 | CHECK(JS_SetThrowHook(rt, nullptr, nullptr)); |
michael@0 | 147 | return true; |
michael@0 | 148 | } |
michael@0 | 149 | END_TEST(testDebugger_throwHook) |
michael@0 | 150 | |
michael@0 | 151 | BEGIN_TEST(testDebugger_debuggerObjectVsDebugMode) |
michael@0 | 152 | { |
michael@0 | 153 | CHECK(JS_DefineDebuggerObject(cx, global)); |
michael@0 | 154 | JS::RootedObject debuggee(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook)); |
michael@0 | 155 | CHECK(debuggee); |
michael@0 | 156 | |
michael@0 | 157 | { |
michael@0 | 158 | JSAutoCompartment ae(cx, debuggee); |
michael@0 | 159 | CHECK(JS_SetDebugMode(cx, true)); |
michael@0 | 160 | CHECK(JS_InitStandardClasses(cx, debuggee)); |
michael@0 | 161 | } |
michael@0 | 162 | |
michael@0 | 163 | JS::RootedObject debuggeeWrapper(cx, debuggee); |
michael@0 | 164 | CHECK(JS_WrapObject(cx, &debuggeeWrapper)); |
michael@0 | 165 | JS::RootedValue v(cx, JS::ObjectValue(*debuggeeWrapper)); |
michael@0 | 166 | CHECK(JS_SetProperty(cx, global, "debuggee", v)); |
michael@0 | 167 | |
michael@0 | 168 | EVAL("var dbg = new Debugger(debuggee);\n" |
michael@0 | 169 | "var hits = 0;\n" |
michael@0 | 170 | "dbg.onDebuggerStatement = function () { hits++; };\n" |
michael@0 | 171 | "debuggee.eval('debugger;');\n" |
michael@0 | 172 | "hits;\n", |
michael@0 | 173 | &v); |
michael@0 | 174 | CHECK_SAME(v, JSVAL_ONE); |
michael@0 | 175 | |
michael@0 | 176 | { |
michael@0 | 177 | JSAutoCompartment ae(cx, debuggee); |
michael@0 | 178 | CHECK(JS_SetDebugMode(cx, false)); |
michael@0 | 179 | } |
michael@0 | 180 | |
michael@0 | 181 | EVAL("debuggee.eval('debugger; debugger; debugger;');\n" |
michael@0 | 182 | "hits;\n", |
michael@0 | 183 | &v); |
michael@0 | 184 | CHECK_SAME(v, INT_TO_JSVAL(4)); |
michael@0 | 185 | |
michael@0 | 186 | return true; |
michael@0 | 187 | } |
michael@0 | 188 | END_TEST(testDebugger_debuggerObjectVsDebugMode) |
michael@0 | 189 | |
michael@0 | 190 | BEGIN_TEST(testDebugger_newScriptHook) |
michael@0 | 191 | { |
michael@0 | 192 | // Test that top-level indirect eval fires the newScript hook. |
michael@0 | 193 | CHECK(JS_DefineDebuggerObject(cx, global)); |
michael@0 | 194 | JS::RootedObject g(cx, JS_NewGlobalObject(cx, getGlobalClass(), nullptr, JS::FireOnNewGlobalHook)); |
michael@0 | 195 | CHECK(g); |
michael@0 | 196 | { |
michael@0 | 197 | JSAutoCompartment ae(cx, g); |
michael@0 | 198 | CHECK(JS_InitStandardClasses(cx, g)); |
michael@0 | 199 | } |
michael@0 | 200 | |
michael@0 | 201 | JS::RootedObject gWrapper(cx, g); |
michael@0 | 202 | CHECK(JS_WrapObject(cx, &gWrapper)); |
michael@0 | 203 | JS::RootedValue v(cx, JS::ObjectValue(*gWrapper)); |
michael@0 | 204 | CHECK(JS_SetProperty(cx, global, "g", v)); |
michael@0 | 205 | |
michael@0 | 206 | EXEC("var dbg = Debugger(g);\n" |
michael@0 | 207 | "var hits = 0;\n" |
michael@0 | 208 | "dbg.onNewScript = function (s) {\n" |
michael@0 | 209 | " hits += Number(s instanceof Debugger.Script);\n" |
michael@0 | 210 | "};\n"); |
michael@0 | 211 | |
michael@0 | 212 | // Since g is a debuggee, g.eval should trigger newScript, regardless of |
michael@0 | 213 | // what scope object we use to enter the compartment. |
michael@0 | 214 | // |
michael@0 | 215 | // Scripts are associated with the global where they're compiled, so we |
michael@0 | 216 | // deliver them only to debuggers that are watching that particular global. |
michael@0 | 217 | // |
michael@0 | 218 | return testIndirectEval(g, "Math.abs(0)"); |
michael@0 | 219 | } |
michael@0 | 220 | |
michael@0 | 221 | bool testIndirectEval(JS::HandleObject scope, const char *code) |
michael@0 | 222 | { |
michael@0 | 223 | EXEC("hits = 0;"); |
michael@0 | 224 | |
michael@0 | 225 | { |
michael@0 | 226 | JSAutoCompartment ae(cx, scope); |
michael@0 | 227 | JSString *codestr = JS_NewStringCopyZ(cx, code); |
michael@0 | 228 | CHECK(codestr); |
michael@0 | 229 | JS::RootedValue arg(cx, JS::StringValue(codestr)); |
michael@0 | 230 | JS::RootedValue v(cx); |
michael@0 | 231 | CHECK(JS_CallFunctionName(cx, scope, "eval", arg, &v)); |
michael@0 | 232 | } |
michael@0 | 233 | |
michael@0 | 234 | JS::RootedValue hitsv(cx); |
michael@0 | 235 | EVAL("hits", &hitsv); |
michael@0 | 236 | CHECK_SAME(hitsv, INT_TO_JSVAL(1)); |
michael@0 | 237 | return true; |
michael@0 | 238 | } |
michael@0 | 239 | END_TEST(testDebugger_newScriptHook) |
michael@0 | 240 | |
michael@0 | 241 | BEGIN_TEST(testDebugger_singleStepThrow) |
michael@0 | 242 | { |
michael@0 | 243 | CHECK(JS_SetDebugModeForCompartment(cx, cx->compartment(), true)); |
michael@0 | 244 | CHECK(JS_SetInterrupt(rt, onStep, nullptr)); |
michael@0 | 245 | |
michael@0 | 246 | CHECK(JS_DefineFunction(cx, global, "setStepMode", setStepMode, 0, 0)); |
michael@0 | 247 | EXEC("var e;\n" |
michael@0 | 248 | "setStepMode();\n" |
michael@0 | 249 | "function f() { throw 0; }\n" |
michael@0 | 250 | "try { f(); }\n" |
michael@0 | 251 | "catch (x) { e = x; }\n"); |
michael@0 | 252 | return true; |
michael@0 | 253 | } |
michael@0 | 254 | |
michael@0 | 255 | static bool |
michael@0 | 256 | setStepMode(JSContext *cx, unsigned argc, jsval *vp) |
michael@0 | 257 | { |
michael@0 | 258 | CallArgs args = CallArgsFromVp(argc, vp); |
michael@0 | 259 | |
michael@0 | 260 | NonBuiltinScriptFrameIter iter(cx); |
michael@0 | 261 | JS::RootedScript script(cx, iter.script()); |
michael@0 | 262 | if (!JS_SetSingleStepMode(cx, script, true)) |
michael@0 | 263 | return false; |
michael@0 | 264 | |
michael@0 | 265 | args.rval().set(UndefinedValue()); |
michael@0 | 266 | return true; |
michael@0 | 267 | } |
michael@0 | 268 | |
michael@0 | 269 | static JSTrapStatus |
michael@0 | 270 | onStep(JSContext *cx, JSScript *script, jsbytecode *pc, jsval *rval, void *closure) |
michael@0 | 271 | { |
michael@0 | 272 | return JSTRAP_CONTINUE; |
michael@0 | 273 | } |
michael@0 | 274 | END_TEST(testDebugger_singleStepThrow) |