From d87ff895727dd9699ca08c2e7228525835e9bcbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rafael=20=C3=81vila=20de=20Esp=C3=ADndola?= Date: Mon, 7 Nov 2011 11:18:11 -0500 Subject: [PATCH 01/46] Bug 698738 - Use the observer method for idle-daily and formhistory-expire-now in nsFormHistory.js. r=mak. --- toolkit/components/satchel/nsFormHistory.js | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/toolkit/components/satchel/nsFormHistory.js b/toolkit/components/satchel/nsFormHistory.js index 65609c7e47a1..95d3468efade 100644 --- a/toolkit/components/satchel/nsFormHistory.js +++ b/toolkit/components/satchel/nsFormHistory.js @@ -138,8 +138,8 @@ FormHistory.prototype = { this.messageManager.addMessageListener("FormHistory:FormSubmitEntries", this); // Add observers - Services.obs.addObserver(function() { self.expireOldEntries() }, "idle-daily", false); - Services.obs.addObserver(function() { self.expireOldEntries() }, "formhistory-expire-now", false); + Services.obs.addObserver(this, "idle-daily", false); + Services.obs.addObserver(this, "formhistory-expire-now", false); }, /* ---- message listener ---- */ @@ -395,10 +395,18 @@ FormHistory.prototype = { observe : function (subject, topic, data) { - if (topic == "nsPref:changed") + switch(topic) { + case "nsPref:changed": this.updatePrefs(); - else + break; + case "idle-daily": + case "formhistory-expire-now": + this.expireOldEntries(); + break; + default: this.log("Oops! Unexpected notification: " + topic); + break; + } }, From f9f1340c27e72b1102198cb39de787bd334d0c8c Mon Sep 17 00:00:00 2001 From: Terrence Cole Date: Fri, 4 Nov 2011 15:56:40 -0700 Subject: [PATCH 02/46] Bug 692547 - Split up array_extra; r=Waldo Using array_extra as a common implementation method does not shave off many bytes of code and makes understanding all of the functions it implements very difficult. This patch splits this mess up and makes these methods follow ECMA steps. --HG-- extra : rebase_source : e31647801e4c54414534bc5770cabfa463c7d149 --- js/src/jsarray.cpp | 615 ++++++++++++++++++++++++++++----------------- 1 file changed, 391 insertions(+), 224 deletions(-) diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 9ceb5ed4b4d8..b21ce5c9ea7f 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -3279,256 +3279,423 @@ array_lastIndexOf(JSContext *cx, uintN argc, Value *vp) return array_indexOfHelper(cx, LastIndexOf, args); } -/* Order is important; extras that take a predicate funarg must follow MAP. */ -typedef enum ArrayExtraMode { - FOREACH, - REDUCE, - REDUCE_RIGHT, - MAP, - FILTER, - SOME, - EVERY -} ArrayExtraMode; - -#define REDUCE_MODE(mode) ((mode) == REDUCE || (mode) == REDUCE_RIGHT) - -static JSBool -array_extra(JSContext *cx, ArrayExtraMode mode, CallArgs &args) +/* ECMA 15.4.4.16-15.4.4.18. */ +class ArrayForEachBehavior { + public: + static bool shouldExit(Value &callval, Value *rval) { return false; } + static Value lateExitValue() { return UndefinedValue(); } +}; + +class ArrayEveryBehavior +{ + public: + static bool shouldExit(Value &callval, Value *rval) + { + if (!js_ValueToBoolean(callval)) { + *rval = BooleanValue(false); + return true; + } + return false; + } + static Value lateExitValue() { return BooleanValue(true); } +}; + +class ArraySomeBehavior +{ + public: + static bool shouldExit(Value &callval, Value *rval) + { + if (js_ValueToBoolean(callval)) { + *rval = BooleanValue(true); + return true; + } + return false; + } + static Value lateExitValue() { return BooleanValue(false); } +}; + +template +static inline bool +array_readonlyCommon(JSContext *cx, CallArgs &args) +{ + /* Step 1. */ JSObject *obj = ToObject(cx, &args.thisv()); if (!obj) return false; - jsuint length; - if (!js_GetLengthProperty(cx, obj, &length)) - return JS_FALSE; + /* Step 2-3. */ + uint32 len; + if (!js_GetLengthProperty(cx, obj, &len)) + return false; - /* - * First, get or compute our callee, so that we error out consistently - * when passed a non-callable object. - */ + /* Step 4. */ if (args.length() == 0) { js_ReportMissingArg(cx, args.calleev(), 0); - return JS_FALSE; + return false; } JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK); if (!callable) - return JS_FALSE; + return false; - /* - * Set our initial return condition, used for zero-length array cases - * (and pre-size our map return to match our known length, for all cases). - */ - jsuint newlen; - JSObject *newarr; - TypeObject *newtype = NULL; -#ifdef __GNUC__ /* quell GCC overwarning */ - newlen = 0; - newarr = NULL; -#endif - jsuint start = 0, end = length; - jsint step = 1; + /* Step 5. */ + Value thisv = args.length() >= 2 ? args[1] : UndefinedValue(); - switch (mode) { - case REDUCE_RIGHT: - start = length - 1, end = -1, step = -1; - /* FALL THROUGH */ - case REDUCE: - if (length == 0 && args.length() == 1) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_EMPTY_ARRAY_REDUCE); - return JS_FALSE; - } - if (args.length() >= 2) { - args.rval() = args[1]; - } else { - JSBool hole; - do { - if (!GetElement(cx, obj, start, &hole, &args.rval())) - return JS_FALSE; - start += step; - } while (hole && start != end); + /* Step 6. */ + uint32 k = 0; - if (hole && start == end) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, - JSMSG_EMPTY_ARRAY_REDUCE); - return JS_FALSE; - } - } - break; - case MAP: - case FILTER: - newlen = (mode == MAP) ? length : 0; - newarr = NewDenseAllocatedArray(cx, newlen); - if (!newarr) - return JS_FALSE; - newtype = GetTypeCallerInitObject(cx, JSProto_Array); - if (!newtype) - return JS_FALSE; - newarr->setType(newtype); - args.rval().setObject(*newarr); - break; - case SOME: - args.rval().setBoolean(false); - break; - case EVERY: - args.rval().setBoolean(true); - break; - case FOREACH: - args.rval().setUndefined(); - break; - } - - if (length == 0) - return JS_TRUE; - - Value thisv = (args.length() > 1 && !REDUCE_MODE(mode)) ? args[1] : UndefinedValue(); - - /* - * For all but REDUCE, we call with 3 args (value, index, array). REDUCE - * requires 4 args (accum, value, index, array). - */ - uintN agArgc = 3 + REDUCE_MODE(mode); - - MUST_FLOW_THROUGH("out"); - JSBool ok = JS_TRUE; - JSBool cond; - - Value objv = ObjectValue(*obj); - AutoValueRooter tvr(cx); + /* Step 7. */ InvokeArgsGuard ag; - for (jsuint i = start; i != end; i += step) { - JSBool hole; - ok = JS_CHECK_OPERATION_LIMIT(cx) && - GetElement(cx, obj, i, &hole, tvr.addr()); - if (!ok) - goto out; - if (hole) - continue; - - if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, agArgc, &ag)) + while (k < len) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) return false; - /* - * Push callable and 'this', then args. We must do this for every - * iteration around the loop since Invoke clobbers its arguments. - */ - ag.calleeHasBeenReset(); - ag.calleev() = ObjectValue(*callable); - ag.thisv() = thisv; - uintN argi = 0; - if (REDUCE_MODE(mode)) - ag[argi++] = args.rval(); - ag[argi++] = tvr.value(); - ag[argi++] = Int32Value(i); - ag[argi] = objv; + /* Step a, b, and c.i. */ + Value kValue; + JSBool kNotPresent; + if (!GetElement(cx, obj, k, &kNotPresent, &kValue)) + return false; - /* Do the call. */ - ok = Invoke(cx, ag); - if (!ok) - break; + /* Step c.ii-iii. */ + if (!kNotPresent) { + if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag)) + return false; + ag.calleeHasBeenReset(); + ag.calleev() = ObjectValue(*callable); + ag.thisv() = thisv; + ag[0] = kValue; + ag[1] = NumberValue(k); + ag[2] = ObjectValue(*obj); + if (!Invoke(cx, ag)) + return false; - const Value &rval = ag.rval(); - - if (mode > MAP) - cond = js_ValueToBoolean(rval); -#ifdef __GNUC__ /* quell GCC overwarning */ - else - cond = JS_FALSE; -#endif - - switch (mode) { - case FOREACH: - break; - case REDUCE: - case REDUCE_RIGHT: - args.rval() = rval; - break; - case MAP: - if (!ok) - goto out; - ok = SetArrayElement(cx, newarr, i, rval); - if (!ok) - goto out; - break; - case FILTER: - if (!cond) - break; - /* The element passed the filter, so push it onto our result. */ - if (!ok) - goto out; - ok = SetArrayElement(cx, newarr, newlen++, tvr.value()); - if (!ok) - goto out; - break; - case SOME: - if (cond) { - args.rval().setBoolean(true); - goto out; - } - break; - case EVERY: - if (!cond) { - args.rval().setBoolean(false); - goto out; - } - break; + if (Behavior::shouldExit(ag.rval(), &args.rval())) + return true; } + + /* Step d. */ + k++; } - out: - if (ok && mode == FILTER) - ok = js_SetLengthProperty(cx, newarr, newlen); - return ok; -} - -static JSBool -array_forEach(JSContext *cx, uintN argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return array_extra(cx, FOREACH, args); -} - -static JSBool -array_map(JSContext *cx, uintN argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return array_extra(cx, MAP, args); -} - -static JSBool -array_reduce(JSContext *cx, uintN argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return array_extra(cx, REDUCE, args); -} - -static JSBool -array_reduceRight(JSContext *cx, uintN argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return array_extra(cx, REDUCE_RIGHT, args); -} - -static JSBool -array_filter(JSContext *cx, uintN argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return array_extra(cx, FILTER, args); -} - -static JSBool -array_some(JSContext *cx, uintN argc, Value *vp) -{ - CallArgs args = CallArgsFromVp(argc, vp); - return array_extra(cx, SOME, args); -} - + /* Step 8. */ + args.rval() = Behavior::lateExitValue(); + return true; + } + +/* ES5 15.4.4.16. */ static JSBool array_every(JSContext *cx, uintN argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); - return array_extra(cx, EVERY, args); + return array_readonlyCommon(cx, args); +} + +/* ES5 15.4.4.17. */ +static JSBool +array_some(JSContext *cx, uintN argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return array_readonlyCommon(cx, args); +} + +/* ES5 15.4.4.18. */ +static JSBool +array_forEach(JSContext *cx, uintN argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return array_readonlyCommon(cx, args); +} + +/* ES5 15.4.4.19. */ +static JSBool +array_map(JSContext *cx, uintN argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + /* Step 1. */ + JSObject *obj = ToObject(cx, &args.thisv()); + if (!obj) + return false; + + /* Step 2-3. */ + uint32 len; + if (!js_GetLengthProperty(cx, obj, &len)) + return false; + + /* Step 4. */ + if (args.length() == 0) { + js_ReportMissingArg(cx, args.calleev(), 0); + return false; + } + JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK); + if (!callable) + return false; + + /* Step 5. */ + Value thisv = args.length() >= 2 ? args[1] : UndefinedValue(); + + /* Step 6. */ + JSObject *arr = NewDenseAllocatedArray(cx, len); + if (!arr) + return false; + TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array); + if (!newtype) + return false; + arr->setType(newtype); + + /* Step 7. */ + uint32 k = 0; + + /* Step 8. */ + InvokeArgsGuard ag; + while (k < len) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + + /* Step a, b, and c.i. */ + JSBool kNotPresent; + Value kValue; + if (!GetElement(cx, obj, k, &kNotPresent, &kValue)) + return false; + + /* Step c.ii-iii. */ + if (!kNotPresent) { + if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag)) + return false; + ag.calleeHasBeenReset(); + ag.calleev() = ObjectValue(*callable); + ag.thisv() = thisv; + ag[0] = kValue; + ag[1] = NumberValue(k); + ag[2] = ObjectValue(*obj); + if (!Invoke(cx, ag)) + return false; + if(!SetArrayElement(cx, arr, k, ag.rval())) + return false; + } + + /* Step d. */ + k++; + } + + /* Step 9. */ + args.rval().setObject(*arr); + return true; +} + +/* ES5 15.4.4.20. */ +static JSBool +array_filter(JSContext *cx, uintN argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + + /* Step 1. */ + JSObject *obj = ToObject(cx, &args.thisv()); + if (!obj) + return false; + + /* Step 2-3. */ + uint32 len; + if (!js_GetLengthProperty(cx, obj, &len)) + return false; + + /* Step 4. */ + if (args.length() == 0) { + js_ReportMissingArg(cx, args.calleev(), 0); + return false; + } + JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK); + if (!callable) + return false; + + /* Step 5. */ + Value thisv = args.length() >= 2 ? args[1] : UndefinedValue(); + + /* Step 6. */ + JSObject *arr = NewDenseAllocatedArray(cx, 0); + if (!arr) + return false; + TypeObject *newtype = GetTypeCallerInitObject(cx, JSProto_Array); + if (!newtype) + return false; + arr->setType(newtype); + + /* Step 7. */ + uint32 k = 0; + + /* Step 8. */ + uint32 to = 0; + + /* Step 9. */ + InvokeArgsGuard ag; + while (k < len) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + + /* Step a, b, and c.i. */ + JSBool kNotPresent; + Value kValue; + if (!GetElement(cx, obj, k, &kNotPresent, &kValue)) + return false; + + /* Step c.ii-iii. */ + if (!kNotPresent) { + if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 3, &ag)) + return false; + ag.calleeHasBeenReset(); + ag.calleev() = ObjectValue(*callable); + ag.thisv() = thisv; + ag[0] = kValue; + ag[1] = NumberValue(k); + ag[2] = ObjectValue(*obj); + if (!Invoke(cx, ag)) + return false; + + if (js_ValueToBoolean(ag.rval())) { + if(!SetArrayElement(cx, arr, to, kValue)) + return false; + to++; + } + } + + /* Step d. */ + k++; + } + + /* Step 10. */ + args.rval().setObject(*arr); + return true; +} + +/* ES5 15.4.4.21-15.4.4.22. */ +class ArrayReduceBehavior +{ + public: + static void initialize(uint32 len, uint32 *start, uint32 *end, int32 *step) + { + *start = 0; + *step = 1; + *end = len; + } +}; + +class ArrayReduceRightBehavior +{ + public: + static void initialize(uint32 len, uint32 *start, uint32 *end, int32 *step) + { + *start = len - 1; + *step = -1; + /* + * We rely on (well defined) unsigned integer underflow to check our + * end condition after visiting the full range (including 0). + */ + *end = (uint32)-1; + } +}; + +template +static inline bool +array_reduceCommon(JSContext *cx, CallArgs &args) +{ + /* Step 1. */ + JSObject *obj = ToObject(cx, &args.thisv()); + if (!obj) + return false; + + /* Step 2-3. */ + uint32 len; + if (!js_GetLengthProperty(cx, obj, &len)) + return false; + + /* Step 4. */ + if (args.length() == 0) { + js_ReportMissingArg(cx, args.calleev(), 0); + return false; + } + JSObject *callable = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK); + if (!callable) + return false; + + /* Step 5. */ + if (len == 0 && args.length() < 2) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE); + return false; + } + + /* Step 6. */ + uint32 k, end; + int32 step; + Behavior::initialize(len, &k, &end, &step); + + /* Step 7-8. */ + Value accumulator; + if (args.length() >= 2) { + accumulator = args[1]; + } else { + JSBool kNotPresent = true; + while (kNotPresent && k != end) { + if (!GetElement(cx, obj, k, &kNotPresent, &accumulator)) + return false; + k += step; + } + if (kNotPresent) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_EMPTY_ARRAY_REDUCE); + return false; + } + } + + /* Step 9. */ + InvokeArgsGuard ag; + while (k != end) { + if (!JS_CHECK_OPERATION_LIMIT(cx)) + return false; + + /* Step a, b, and c.i. */ + JSBool kNotPresent; + Value kValue; + if (!GetElement(cx, obj, k, &kNotPresent, &kValue)) + return false; + + /* Step c.ii. */ + if (!kNotPresent) { + if (!ag.pushed() && !cx->stack.pushInvokeArgs(cx, 4, &ag)) + return false; + ag.calleeHasBeenReset(); + ag.calleev() = ObjectValue(*callable); + ag.thisv() = UndefinedValue(); + ag[0] = accumulator; + ag[1] = kValue; + ag[2] = NumberValue(k); + ag[3] = ObjectValue(*obj); + if (!Invoke(cx, ag)) + return false; + accumulator = ag.rval(); + } + + /* Step d. */ + k += step; + } + + /* Step 10. */ + args.rval() = accumulator; + return true; +} + +/* ES5 15.4.4.21. */ +static JSBool +array_reduce(JSContext *cx, uintN argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return array_reduceCommon(cx, args); +} + +/* ES5 15.4.4.22. */ +static JSBool +array_reduceRight(JSContext *cx, uintN argc, Value *vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + return array_reduceCommon(cx, args); } static JSBool From f269279403257fd931869d7cfb789382a16e99c6 Mon Sep 17 00:00:00 2001 From: Gavin Sharp Date: Fri, 4 Nov 2011 14:45:01 -0700 Subject: [PATCH 03/46] Bug 691951: make startup notifications persist until the users close them (disable auto-dismissal), r=zpao --HG-- extra : transplant_source : %16%B44%1C%03%FC%C3%04%00%EA%8E%A6%D5%96%16%F2%16O%D4%40 --- browser/components/nsBrowserGlue.js | 27 ++++++++++++++------------- build/automation.py.in | 2 +- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index d851d67119bd..6c68fb4c4f15 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -639,8 +639,8 @@ BrowserGlue.prototype = { var currentVersion = Services.prefs.getIntPref("browser.rights.version"); Services.prefs.setBoolPref("browser.rights." + currentVersion + ".shown", true); - var box = notifyBox.appendNotification(notifyRightsText, "about-rights", null, notifyBox.PRIORITY_INFO_LOW, buttons); - box.persistence = 3; // arbitrary number, just so bar sticks around for a bit + var notification = notifyBox.appendNotification(notifyRightsText, "about-rights", null, notifyBox.PRIORITY_INFO_LOW, buttons); + notification.persistence = -1; // Until user closes it }, _showUpdateNotification: function BG__showUpdateNotification() { @@ -709,10 +709,10 @@ BrowserGlue.prototype = { } ]; - let box = notifyBox.appendNotification(text, "post-update-notification", - null, notifyBox.PRIORITY_INFO_LOW, - buttons); - box.persistence = 3; + let notification = notifyBox.appendNotification(text, "post-update-notification", + null, notifyBox.PRIORITY_INFO_LOW, + buttons); + notification.persistence = -1; // Until user closes it } if (actions.indexOf("showAlert") == -1) @@ -815,7 +815,7 @@ BrowserGlue.prototype = { var notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons); notification.setAttribute("hideclose", true); - notification.persistence = 6; // arbitrary number, just so bar sticks around for a bit + notification.persistence = -1; // Until user closes it let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; let link = notification.ownerDocument.createElementNS(XULNS, "label"); @@ -827,8 +827,9 @@ BrowserGlue.prototype = { // Remove the notification on which the user clicked notification.parentNode.removeNotification(notification, true); // Add a new notification to that tab, with no "Learn more" link - var notifyBox = browser.getNotificationBox(); - notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons); + notifyBox = browser.getNotificationBox(); + notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons); + notification.persistence = -1; // Until user closes it }, false); let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText"); description.appendChild(link); @@ -1092,10 +1093,10 @@ BrowserGlue.prototype = { ]; var notifyBox = browser.getNotificationBox(); - var box = notifyBox.appendNotification(text, title, null, - notifyBox.PRIORITY_CRITICAL_MEDIUM, - buttons); - box.persistence = -1; // Until user closes it + var notification = notifyBox.appendNotification(text, title, null, + notifyBox.PRIORITY_CRITICAL_MEDIUM, + buttons); + notification.persistence = -1; // Until user closes it }, _migrateUI: function BG__migrateUI() { diff --git a/build/automation.py.in b/build/automation.py.in index fb88e55dcd24..ce30cd9a644e 100644 --- a/build/automation.py.in +++ b/build/automation.py.in @@ -428,7 +428,7 @@ user_pref("security.warn_viewing_mixed", false); user_pref("app.update.enabled", false); user_pref("browser.panorama.experienced_first_run", true); // Assume experienced user_pref("dom.w3c_touch_events.enabled", true); -user_pref("toolkit.telemetry.prompted", true); +user_pref("toolkit.telemetry.prompted", 2); // Only load extensions from the application and user profile // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION From 2b678ca54cf81dc1c9ac15e1555c8105fa61f032 Mon Sep 17 00:00:00 2001 From: Tim Abraldes Date: Mon, 7 Nov 2011 12:53:41 -0600 Subject: [PATCH 04/46] Bug 668436 - Send service pack major and minor version info in AUS ping. r=rstrong --- toolkit/mozapps/update/nsUpdateService.js | 108 ++++++++++++++++ .../update/test/unit/test_0040_general.js | 122 ++++++++++++++++++ 2 files changed, 230 insertions(+) diff --git a/toolkit/mozapps/update/nsUpdateService.js b/toolkit/mozapps/update/nsUpdateService.js index 8f365e56df12..84bf3a4aec4d 100644 --- a/toolkit/mozapps/update/nsUpdateService.js +++ b/toolkit/mozapps/update/nsUpdateService.js @@ -46,6 +46,7 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/FileUtils.jsm"); Components.utils.import("resource://gre/modules/AddonManager.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); +Components.utils.import("resource://gre/modules/ctypes.jsm") const Cc = Components.classes; const Ci = Components.interfaces; @@ -198,6 +199,113 @@ XPCOMUtils.defineLazyGetter(this, "gOSVersion", function aus_gOSVersion() { } if (osVersion) { +#ifdef XP_WIN + const BYTE = ctypes.uint8_t; + const WORD = ctypes.uint16_t; + const DWORD = ctypes.uint32_t; + const WCHAR = ctypes.jschar; + const BOOL = ctypes.int; + + // This structure is described at: + // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx + const SZCSDVERSIONLENGTH = 128; + const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW', + [ + {dwOSVersionInfoSize: DWORD}, + {dwMajorVersion: DWORD}, + {dwMinorVersion: DWORD}, + {dwBuildNumber: DWORD}, + {dwPlatformId: DWORD}, + {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)}, + {wServicePackMajor: WORD}, + {wServicePackMinor: WORD}, + {wSuiteMask: WORD}, + {wProductType: BYTE}, + {wReserved: BYTE} + ]); + + // This structure is described at: + // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx + const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO', + [ + {wProcessorArchitecture: WORD}, + {wReserved: WORD}, + {dwPageSize: DWORD}, + {lpMinimumApplicationAddress: ctypes.voidptr_t}, + {lpMaximumApplicationAddress: ctypes.voidptr_t}, + {dwActiveProcessorMask: DWORD.ptr}, + {dwNumberOfProcessors: DWORD}, + {dwProcessorType: DWORD}, + {dwAllocationGranularity: DWORD}, + {wProcessorLevel: WORD}, + {wProcessorRevision: WORD} + ]); + + let kernel32 = false; + try { + kernel32 = ctypes.open("Kernel32"); + } catch (e) { + LOG("gOSVersion - Unable to open kernel32! " + e); + osVersion += ".unknown (unknown)"; + } + + if(kernel32) { + try { + // Get Service pack info + try { + let GetVersionEx = kernel32.declare("GetVersionExW", + ctypes.default_abi, + BOOL, + OSVERSIONINFOEXW.ptr); + let winVer = OSVERSIONINFOEXW(); + winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size; + + if(0 !== GetVersionEx(winVer.address())) { + osVersion += "." + winVer.wServicePackMajor + + "." + winVer.wServicePackMinor; + } else { + LOG("gOSVersion - Unknown failure in GetVersionEX (returned 0)"); + osVersion += ".unknown"; + } + } catch (e) { + LOG("gOSVersion - error getting service pack information. Exception: " + e); + osVersion += ".unknown"; + } + + // Get processor architecture + let arch = "unknown"; + try { + let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo", + ctypes.default_abi, + ctypes.void_t, + SYSTEM_INFO.ptr); + let sysInfo = SYSTEM_INFO(); + // Default to unknown + sysInfo.wProcessorArchitecture = 0xffff; + + GetNativeSystemInfo(sysInfo.address()); + switch(sysInfo.wProcessorArchitecture) { + case 9: + arch = "x64"; + break; + case 6: + arch = "IA64"; + break; + case 0: + arch = "x86"; + break; + } + } catch (e) { + LOG("gOSVersion - error getting processor architecture. Exception: " + e); + } finally { + osVersion += " (" + arch + ")"; + } + } finally { + kernel32.close(); + } + } +#endif + try { osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")"; } diff --git a/toolkit/mozapps/update/test/unit/test_0040_general.js b/toolkit/mozapps/update/test/unit/test_0040_general.js index 8c8e5500ea58..fe1f1315611a 100644 --- a/toolkit/mozapps/update/test/unit/test_0040_general.js +++ b/toolkit/mozapps/update/test/unit/test_0040_general.js @@ -36,6 +36,8 @@ * ***** END LICENSE BLOCK ***** */ +Components.utils.import("resource://gre/modules/ctypes.jsm") + /* General URL Construction Tests */ const URL_PREFIX = URL_HOST + URL_PATH + "/"; @@ -228,12 +230,132 @@ function run_test_pt9() { gUpdateChecker.checkForUpdates(updateCheckListener, true); } +function getServicePack() { + // NOTE: This function is a helper function and not a test. Thus, + // it uses throw() instead of do_throw(). Any tests that use this function + // should catch exceptions thrown in this function and deal with them + // appropriately (usually by calling do_throw). + const BYTE = ctypes.uint8_t; + const WORD = ctypes.uint16_t; + const DWORD = ctypes.uint32_t; + const WCHAR = ctypes.jschar; + const BOOL = ctypes.int; + + // This structure is described at: + // http://msdn.microsoft.com/en-us/library/ms724833%28v=vs.85%29.aspx + const SZCSDVERSIONLENGTH = 128; + const OSVERSIONINFOEXW = new ctypes.StructType('OSVERSIONINFOEXW', + [ + {dwOSVersionInfoSize: DWORD}, + {dwMajorVersion: DWORD}, + {dwMinorVersion: DWORD}, + {dwBuildNumber: DWORD}, + {dwPlatformId: DWORD}, + {szCSDVersion: ctypes.ArrayType(WCHAR, SZCSDVERSIONLENGTH)}, + {wServicePackMajor: WORD}, + {wServicePackMinor: WORD}, + {wSuiteMask: WORD}, + {wProductType: BYTE}, + {wReserved: BYTE} + ]); + + let kernel32 = ctypes.open("kernel32"); + try { + let GetVersionEx = kernel32.declare("GetVersionExW", + ctypes.default_abi, + BOOL, + OSVERSIONINFOEXW.ptr); + let winVer = OSVERSIONINFOEXW(); + winVer.dwOSVersionInfoSize = OSVERSIONINFOEXW.size; + + if(0 === GetVersionEx(winVer.address())) { + // Using "throw" instead of "do_throw" (see NOTE above) + throw("Failure in GetVersionEx (returned 0)"); + } + + return winVer.wServicePackMajor + "." + winVer.wServicePackMinor; + } finally { + kernel32.close(); + } +} + +function getProcArchitecture() { + // NOTE: This function is a helper function and not a test. Thus, + // it uses throw() instead of do_throw(). Any tests that use this function + // should catch exceptions thrown in this function and deal with them + // appropriately (usually by calling do_throw). + const WORD = ctypes.uint16_t; + const DWORD = ctypes.uint32_t; + + // This structure is described at: + // http://msdn.microsoft.com/en-us/library/ms724958%28v=vs.85%29.aspx + const SYSTEM_INFO = new ctypes.StructType('SYSTEM_INFO', + [ + {wProcessorArchitecture: WORD}, + {wReserved: WORD}, + {dwPageSize: DWORD}, + {lpMinimumApplicationAddress: ctypes.voidptr_t}, + {lpMaximumApplicationAddress: ctypes.voidptr_t}, + {dwActiveProcessorMask: DWORD.ptr}, + {dwNumberOfProcessors: DWORD}, + {dwProcessorType: DWORD}, + {dwAllocationGranularity: DWORD}, + {wProcessorLevel: WORD}, + {wProcessorRevision: WORD} + ]); + + let kernel32 = ctypes.open("kernel32"); + try { + let GetNativeSystemInfo = kernel32.declare("GetNativeSystemInfo", + ctypes.default_abi, + ctypes.void_t, + SYSTEM_INFO.ptr); + let sysInfo = SYSTEM_INFO(); + // Default to unknown + sysInfo.wProcessorArchitecture = 0xffff; + + GetNativeSystemInfo(sysInfo.address()); + switch(sysInfo.wProcessorArchitecture) { + case 9: + return "x64"; + case 6: + return "IA64"; + case 0: + return "x86"; + default: + // Using "throw" instead of "do_throw" (see NOTE above) + throw("Unknown architecture returned from GetNativeSystemInfo: " + sysInfo.wProcessorArchitecture); + } + } finally { + kernel32.close(); + } +} + function check_test_pt9() { var osVersion; var sysInfo = AUS_Cc["@mozilla.org/system-info;1"]. getService(AUS_Ci.nsIPropertyBag2); osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version"); + if(IS_WIN) { + try { + let servicePack = getServicePack(); + osVersion += "." + servicePack; + } catch (e) { + do_throw("Failure obtaining service pack: " + e); + } + + if("5.0" === sysInfo.getProperty("version")) { // Win2K + osVersion += " (unknown)"; + } else { + try { + osVersion += " (" + getProcArchitecture() + ")"; + } catch (e) { + do_throw("Failed to obtain processor architecture: " + e); + } + } + } + if (osVersion) { try { osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")"; From 1a4dfbfc57d4e19a65039bb29c4edba29f603f9d Mon Sep 17 00:00:00 2001 From: Henri Sivonen Date: Mon, 7 Nov 2011 21:20:14 +0200 Subject: [PATCH 05/46] Bug 700042 - Support turning off View Source syntax highlighting. r=smaug. --- parser/html/nsHtml5Highlighter.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/parser/html/nsHtml5Highlighter.cpp b/parser/html/nsHtml5Highlighter.cpp index 68832fdc3153..95de9924652c 100644 --- a/parser/html/nsHtml5Highlighter.cpp +++ b/parser/html/nsHtml5Highlighter.cpp @@ -776,6 +776,9 @@ nsHtml5Highlighter::AppendCharacters(const PRUnichar* aBuffer, void nsHtml5Highlighter::AddClass(const PRUnichar* aClass) { + if (!mSyntaxHighlight) { + return; + } mOpQueue.AppendElement()->InitAddClass(CurrentNode(), aClass); } @@ -795,6 +798,9 @@ nsHtml5Highlighter::AddViewSourceHref(const nsString& aValue) void nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId) { + if (!mSyntaxHighlight) { + return; + } nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); treeOp->Init(CurrentNode(), aMsgId); @@ -803,6 +809,9 @@ nsHtml5Highlighter::AddErrorToCurrentNode(const char* aMsgId) void nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId) { + if (!mSyntaxHighlight) { + return; + } NS_PRECONDITION(mCurrentRun, "Adding error to run without one!"); nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); @@ -813,6 +822,9 @@ void nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId, nsIAtom* aName) { + if (!mSyntaxHighlight) { + return; + } NS_PRECONDITION(mCurrentRun, "Adding error to run without one!"); nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); @@ -824,6 +836,9 @@ nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId, nsIAtom* aName, nsIAtom* aOther) { + if (!mSyntaxHighlight) { + return; + } NS_PRECONDITION(mCurrentRun, "Adding error to run without one!"); nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); @@ -833,6 +848,9 @@ nsHtml5Highlighter::AddErrorToCurrentRun(const char* aMsgId, void nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId) { + if (!mSyntaxHighlight) { + return; + } NS_PRECONDITION(mAmpersand, "Adding error to ampersand without one!"); nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); @@ -842,6 +860,9 @@ nsHtml5Highlighter::AddErrorToCurrentAmpersand(const char* aMsgId) void nsHtml5Highlighter::AddErrorToCurrentSlash(const char* aMsgId) { + if (!mSyntaxHighlight) { + return; + } NS_PRECONDITION(mSlash, "Adding error to slash without one!"); nsHtml5TreeOperation* treeOp = mOpQueue.AppendElement(); NS_ASSERTION(treeOp, "Tree op allocation failed."); From 5579c150ca49b6b21f2e433fc6df4332797bfd89 Mon Sep 17 00:00:00 2001 From: Henri Sivonen Date: Mon, 7 Nov 2011 21:20:14 +0200 Subject: [PATCH 06/46] Bug 700260 - Honor the view_source.tab_size pref in a way that actually works. r=smaug. --- parser/html/nsHtml5Highlighter.cpp | 2 +- parser/htmlparser/tests/reftest/bug700260-1-ref.html | 3 +++ parser/htmlparser/tests/reftest/bug700260-1.html | 3 +++ parser/htmlparser/tests/reftest/reftest.list | 1 + 4 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 parser/htmlparser/tests/reftest/bug700260-1-ref.html create mode 100644 parser/htmlparser/tests/reftest/bug700260-1.html diff --git a/parser/html/nsHtml5Highlighter.cpp b/parser/html/nsHtml5Highlighter.cpp index 95de9924652c..941b1f7f242d 100644 --- a/parser/html/nsHtml5Highlighter.cpp +++ b/parser/html/nsHtml5Highlighter.cpp @@ -152,7 +152,7 @@ nsHtml5Highlighter::Start() if (mTabSize > 0) { nsString* style = new nsString(NS_LITERAL_STRING("-moz-tab-size: ")); style->AppendInt(mTabSize); - bodyAttrs->addAttribute(nsHtml5AttributeName::ATTR_CLASS, style); + bodyAttrs->addAttribute(nsHtml5AttributeName::ATTR_STYLE, style); } Push(nsGkAtoms::body, bodyAttrs); diff --git a/parser/htmlparser/tests/reftest/bug700260-1-ref.html b/parser/htmlparser/tests/reftest/bug700260-1-ref.html new file mode 100644 index 000000000000..0ba4495a0950 --- /dev/null +++ b/parser/htmlparser/tests/reftest/bug700260-1-ref.html @@ -0,0 +1,3 @@ + 1 + 2 + 3 diff --git a/parser/htmlparser/tests/reftest/bug700260-1.html b/parser/htmlparser/tests/reftest/bug700260-1.html new file mode 100644 index 000000000000..37d300834751 --- /dev/null +++ b/parser/htmlparser/tests/reftest/bug700260-1.html @@ -0,0 +1,3 @@ + 1 + 2 + 3 diff --git a/parser/htmlparser/tests/reftest/reftest.list b/parser/htmlparser/tests/reftest/reftest.list index 26689eccd5a5..01211aeb9299 100644 --- a/parser/htmlparser/tests/reftest/reftest.list +++ b/parser/htmlparser/tests/reftest/reftest.list @@ -15,3 +15,4 @@ == view-source:bug673094-1.html view-source:bug673094-1-ref.html == bug696651-1.html bug696651-1-ref.html == bug696651-2.html bug696651-2-ref.html +== view-source:bug700260-1.html view-source:bug700260-1-ref.html From 57fc4a8bc56a9b4b72c77ae2636a103f91885236 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Fri, 21 Oct 2011 10:54:33 -0700 Subject: [PATCH 07/46] Bug 696813 - Simplify Parser::forStatement (r=jorendorff) --HG-- extra : rebase_source : 9e5d9eebffee46d35a55e869a45b8ec5db5877e6 --- js/src/frontend/Parser.cpp | 113 ++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 52 deletions(-) diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index 863698d45864..9c71a8ce96e9 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3076,60 +3076,70 @@ Parser::forStatement() } MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_AFTER_FOR); - TokenKind tt = tokenStream.peekToken(TSF_OPERAND); -#if JS_HAS_BLOCK_SCOPE +#ifdef JS_HAS_BLOCK_SCOPE bool let = false; #endif - ParseNode *pn1; - if (tt == TOK_SEMI) { - if (pn->pn_iflags & JSITER_FOREACH) { - reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP); - return NULL; - } + /* + * True if we have 'for (var/let/const ...)', except in the oddball case + * where 'let' begins a let-expression in 'for (let (...) ...)'. + */ + bool forDecl = false; - /* No initializer -- set first kid of left sub-node to null. */ - pn1 = NULL; - } else { - /* - * Set pn1 to a var list or an initializing expression. - * - * Set the TCF_IN_FOR_INIT flag during parsing of the first clause - * of the for statement. This flag will be used by the RelExpr - * production; if it is set, then the 'in' keyword will not be - * recognized as an operator, leaving it available to be parsed as - * part of a for/in loop. - * - * A side effect of this restriction is that (unparenthesized) - * expressions involving an 'in' operator are illegal in the init - * clause of an ordinary for loop. - */ - tc->flags |= TCF_IN_FOR_INIT; - if (tt == TOK_VAR) { - (void) tokenStream.getToken(); - pn1 = variables(false); -#if JS_HAS_BLOCK_SCOPE - } else if (tt == TOK_LET) { - let = true; - (void) tokenStream.getToken(); - if (tokenStream.peekToken() == TOK_LP) { - pn1 = letBlock(JS_FALSE); - tt = TOK_LEXICALSCOPE; - } else { - pnlet = PushLexicalScope(context, &tokenStream, tc, &blockInfo); - if (!pnlet) - return NULL; - blockInfo.flags |= SIF_FOR_BLOCK; - pn1 = variables(false); + /* Set to 'x' in 'for (x ;... ;...)' or 'for (x in ...)'. */ + ParseNode *pn1; + + { + TokenKind tt = tokenStream.peekToken(TSF_OPERAND); + if (tt == TOK_SEMI) { + if (pn->pn_iflags & JSITER_FOREACH) { + reportErrorNumber(pn, JSREPORT_ERROR, JSMSG_BAD_FOR_EACH_LOOP); + return NULL; } -#endif + + pn1 = NULL; } else { - pn1 = expr(); + /* + * Set pn1 to a var list or an initializing expression. + * + * Set the TCF_IN_FOR_INIT flag during parsing of the first clause + * of the for statement. This flag will be used by the RelExpr + * production; if it is set, then the 'in' keyword will not be + * recognized as an operator, leaving it available to be parsed as + * part of a for/in loop. + * + * A side effect of this restriction is that (unparenthesized) + * expressions involving an 'in' operator are illegal in the init + * clause of an ordinary for loop. + */ + tc->flags |= TCF_IN_FOR_INIT; + if (tt == TOK_VAR) { + forDecl = true; + (void) tokenStream.getToken(); + pn1 = variables(false); +#if JS_HAS_BLOCK_SCOPE + } else if (tt == TOK_LET) { + let = true; + (void) tokenStream.getToken(); + if (tokenStream.peekToken() == TOK_LP) { + pn1 = letBlock(JS_FALSE); + } else { + forDecl = true; + pnlet = PushLexicalScope(context, &tokenStream, tc, &blockInfo); + if (!pnlet) + return NULL; + blockInfo.flags |= SIF_FOR_BLOCK; + pn1 = variables(false); + } +#endif + } else { + pn1 = expr(); + } + tc->flags &= ~TCF_IN_FOR_INIT; + if (!pn1) + return NULL; } - tc->flags &= ~TCF_IN_FOR_INIT; - if (!pn1) - return NULL; } /* @@ -3154,8 +3164,7 @@ Parser::forStatement() stmtInfo.type = STMT_FOR_IN_LOOP; /* Check that the left side of the 'in' is valid. */ - JS_ASSERT(!TokenKindIsDecl(tt) || pn1->isKind(tt)); - if (TokenKindIsDecl(tt) + if (forDecl ? (pn1->pn_count > 1 || pn1->isOp(JSOP_DEFCONST) #if JS_HAS_DESTRUCTURING || (versionNumber() == JSVERSION_1_7 && @@ -3196,7 +3205,7 @@ Parser::forStatement() */ pn2 = NULL; uintN dflag = PND_ASSIGNED; - if (TokenKindIsDecl(tt)) { + if (forDecl) { /* Tell EmitVariables that pn1 is part of a for/in. */ pn1->pn_xflags |= PNX_FORINVAR; @@ -3211,10 +3220,10 @@ Parser::forStatement() * * Rewrite 'for ( x = i in o)' where is 'var' or * 'const' to hoist the initializer or the entire decl out of - * the loop head. TOK_VAR is the type for both 'var' and 'const'. + * the loop head. */ #if JS_HAS_BLOCK_SCOPE - if (tt == TOK_LET) { + if (let) { reportErrorNumber(pn2, JSREPORT_ERROR, JSMSG_INVALID_FOR_IN_INIT); return NULL; } @@ -3322,7 +3331,7 @@ Parser::forStatement() /* Parse the loop condition or null into pn2. */ MUST_MATCH_TOKEN(TOK_SEMI, JSMSG_SEMI_AFTER_FOR_INIT); - tt = tokenStream.peekToken(TSF_OPERAND); + TokenKind tt = tokenStream.peekToken(TSF_OPERAND); if (tt == TOK_SEMI) { pn2 = NULL; } else { From b25d2075b450b557b40548de01f3f6b72bda0472 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 24 Oct 2011 11:15:34 -0700 Subject: [PATCH 08/46] Bug 696813 - Simplify several bytecode generator Emit* functions (r=jorendorff) --HG-- extra : rebase_source : 2d85e4add09a9a99071755151ab8691299fc8c05 --- js/src/frontend/BytecodeEmitter.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 73302d80260d..69dc7e37b20b 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5265,7 +5265,7 @@ EmitIf(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) #if JS_HAS_BLOCK_SCOPE static bool -EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *&pn) +EmitLet(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { /* * pn represents one of these syntactic constructs: @@ -5394,7 +5394,7 @@ EmitXMLProcessingInstruction(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) #endif static bool -EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool &ok) +EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { StmtInfo stmtInfo; StmtInfo *stmt; @@ -5446,12 +5446,11 @@ EmitLexicalScope(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool &ok) if (!EmitLeaveBlock(cx, bce, op, objbox)) return false; - ok = PopStatementBCE(cx, bce); - return true; + return PopStatementBCE(cx, bce); } static bool -EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool &ok) +EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) { StmtInfo stmtInfo; if (!EmitTree(cx, bce, pn->pn_left)) @@ -5467,8 +5466,7 @@ EmitWith(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn, JSBool &ok) return false; if (Emit1(cx, bce, JSOP_LEAVEWITH) < 0) return false; - ok = PopStatementBCE(cx, bce); - return true; + return PopStatementBCE(cx, bce); } static bool @@ -6125,8 +6123,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) } case TOK_WITH: - if (!EmitWith(cx, bce, pn, ok)) - return false; + ok = EmitWith(cx, bce, pn); break; case TOK_TRY: @@ -6910,8 +6907,7 @@ frontend::EmitTree(JSContext *cx, BytecodeEmitter *bce, ParseNode *pn) } case TOK_LEXICALSCOPE: - if (!EmitLexicalScope(cx, bce, pn, ok)) - return false; + ok = EmitLexicalScope(cx, bce, pn); break; #if JS_HAS_BLOCK_SCOPE From ba6c290ff755760f713ef899c25f24935b15ebd2 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 7 Nov 2011 11:46:25 -0800 Subject: [PATCH 09/46] Bug 696813 - Remove dead Decompile parameter (r=waldo) --HG-- extra : rebase_source : a97c2551cae6de7a1c5b345ce5b7ceee26868cff --- js/src/jsopcode.cpp | 40 +++++++++++++++------------------------- 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 573f027bd0f7..de1c629572a8 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -1288,7 +1288,7 @@ SprintDoubleValue(Sprinter *sp, jsval v, JSOp *opp) } static jsbytecode * -Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop); +Decompile(SprintStack *ss, jsbytecode *pc, intN nb); static JSBool DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, @@ -1318,7 +1318,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, jp->indent += 2; js_printf(jp, "\t%s:\n", js_default_str); jp->indent += 2; - if (!Decompile(ss, pc + defaultOffset, diff, JSOP_NOP)) + if (!Decompile(ss, pc + defaultOffset, diff)) return JS_FALSE; jp->indent -= 4; } @@ -1341,10 +1341,8 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, nextCaseExprOff = (ptrdiff_t)JSVAL_TO_INT(key); nextCaseExprOff += js_CodeSpec[pc[nextCaseExprOff]].length; jp->indent += 2; - if (!Decompile(ss, pc + caseExprOff, - nextCaseExprOff - caseExprOff, JSOP_NOP)) { + if (!Decompile(ss, pc + caseExprOff, nextCaseExprOff - caseExprOff)) return JS_FALSE; - } caseExprOff = nextCaseExprOff; /* Balance the stack as if this JSOP_CASE matched. */ @@ -1391,7 +1389,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, if (off <= defaultOffset && defaultOffset < off2) { diff = defaultOffset - off; if (diff != 0) { - if (!Decompile(ss, pc + off, diff, JSOP_NOP)) + if (!Decompile(ss, pc + off, diff)) return JS_FALSE; off = defaultOffset; } @@ -1399,7 +1397,7 @@ DecompileSwitch(SprintStack *ss, TableEntry *table, uintN tableLength, js_printf(jp, "\t%s:\n", js_default_str); jp->indent += 2; } - if (!Decompile(ss, pc + off, off2 - off, JSOP_NOP)) + if (!Decompile(ss, pc + off, off2 - off)) return JS_FALSE; jp->indent -= 4; @@ -1615,7 +1613,7 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, */ todo = ss->sprinter.offset; ss->sprinter.offset = todo + PAREN_SLOP; - pc = Decompile(ss, pc, -((intN)ss->top), JSOP_NOP); + pc = Decompile(ss, pc, -((intN)ss->top)); if (!pc) return NULL; if (pc == endpc) @@ -1928,14 +1926,9 @@ InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) * If nb is non-negative, decompile nb bytecodes starting at pc. Otherwise * the decompiler starts at pc and continues until it reaches an opcode for * which decompiling would result in the stack depth equaling -(nb + 1). - * - * The nextop parameter is either JSOP_NOP or the "next" opcode in order of - * abstract interpretation (not necessarily physically next in a bytecode - * vector). So nextop is JSOP_POP for the last operand in a comma expression, - * or JSOP_AND for the right operand of &&. */ static jsbytecode * -Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) +Decompile(SprintStack *ss, jsbytecode *pc, intN nb) { JSContext *cx; JSPrinter *jp, *jp2; @@ -1980,9 +1973,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * Local macros */ #define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, NULL) -#define DECOMPILE_CODE_CLEANUP(pc,nb,cleanup) if (!Decompile(ss, pc, nb, JSOP_NOP)) cleanup +#define DECOMPILE_CODE_CLEANUP(pc,nb,cleanup) if (!Decompile(ss, pc, nb)) cleanup #define DECOMPILE_CODE(pc,nb) DECOMPILE_CODE_CLEANUP(pc,nb,return NULL) -#define NEXT_OP(pc) (((pc) + (len) == endpc) ? nextop : pc[len]) #define TOP_STR() GetStr(ss, ss->top - 1) #define POP_STR() PopStr(ss, op) #define POP_STR_PREC(prec) PopStrPrec(ss, prec) @@ -2647,7 +2639,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) pc += js_GetSrcNoteOffset(sn, 0); len = 0; - if (!Decompile(ss, done, pc - done, JSOP_POP)) { + if (!Decompile(ss, done, pc - done)) { cx->free_((char *)lval); return NULL; } @@ -2686,7 +2678,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) /* Set saveop to reflect what we will push. */ saveop = JSOP_LEAVEBLOCKEXPR; - if (!Decompile(ss, pc, len, saveop)) { + if (!Decompile(ss, pc, len)) { cx->free_((char *)lval); return NULL; } @@ -2793,7 +2785,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) js_printf(jp, "\t{\n"); jp->indent += 4; len = js_GetSrcNoteOffset(sn, 0); - ok = Decompile(ss, pc + oplen, len - oplen, JSOP_NOP) + ok = Decompile(ss, pc + oplen, len - oplen) != NULL; if (!ok) goto enterblock_out; @@ -2873,7 +2865,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) len -= pc - pc2; LOCAL_ASSERT_OUT(len > 0); js_printf(jp, " if "); - ok = Decompile(ss, pc, len, JSOP_NOP) != NULL; + ok = Decompile(ss, pc, len) != NULL; if (!ok) goto enterblock_out; js_printf(jp, "%s", POP_STR()); @@ -3490,7 +3482,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) done = pc + GetJumpOffset(pc, pc); pc += len; len = done - pc; - if (!Decompile(ss, pc, len, op)) { + if (!Decompile(ss, pc, len)) { cx->free_((char *)lval); return NULL; } @@ -4096,8 +4088,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) * Decompile only the main bytecode, to avoid tripping over * new prolog ops that have stack effects. */ - ok = Decompile(&ss2, inner->main(), inner->length - inner->mainOffset, - JSOP_NOP) + ok = Decompile(&ss2, inner->main(), inner->length - inner->mainOffset) != NULL; jp->script = outer; jp->fun = outerfun; @@ -4791,7 +4782,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop) */ #undef inXML #undef DECOMPILE_CODE -#undef NEXT_OP #undef TOP_STR #undef POP_STR #undef POP_STR_PREC @@ -4842,7 +4832,7 @@ DecompileCode(JSPrinter *jp, JSScript *script, jsbytecode *pc, uintN len, /* Call recursive subroutine to do the hard work. */ JSScript *oldscript = jp->script; jp->script = script; - bool ok = Decompile(&ss, pc, len, JSOP_NOP) != NULL; + bool ok = Decompile(&ss, pc, len) != NULL; jp->script = oldscript; /* If the given code didn't empty the stack, do it now. */ From 56bcb1338b0d51db19d31437762d56c6196bdb37 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 7 Nov 2011 11:46:25 -0800 Subject: [PATCH 10/46] Bug 696813 - Simplify Decompile (r=waldo) --HG-- extra : rebase_source : 452ffc58a6dc2f9d152fe6cbc2a1206fd3db0e27 --- js/src/jsopcode.cpp | 146 ++++++++++++++------------------------------ 1 file changed, 45 insertions(+), 101 deletions(-) diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index de1c629572a8..3d6e07204302 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -1533,25 +1533,23 @@ static jsbytecode * DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, JSBool *hole) { - JSContext *cx; JSPrinter *jp; JSOp op; const JSCodeSpec *cs; uintN oplen; jsint i; const char *lval, *xval; - ptrdiff_t todo; JSAtom *atom; *hole = JS_FALSE; - cx = ss->sprinter.context; jp = ss->printer; LOAD_OP_DATA(pc); switch (op) { case JSOP_POP: *hole = JS_TRUE; - todo = SprintPut(&ss->sprinter, ", ", 2); + if (SprintPut(&ss->sprinter, ", ", 2) < 0) + return NULL; break; case JSOP_DUP: @@ -1562,9 +1560,8 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, return pc; LOAD_OP_DATA(pc); lval = PopStr(ss, JSOP_NOP); - todo = SprintCString(&ss->sprinter, lval); - if (op == JSOP_POPN) - return pc; + if (SprintCString(&ss->sprinter, lval) < 0) + return NULL; LOCAL_ASSERT(*pc == JSOP_POP); break; @@ -1574,36 +1571,19 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, /* FALL THROUGH */ case JSOP_SETLOCALPOP: - atom = NULL; - lval = NULL; - if (op == JSOP_SETARG) { - atom = GetArgOrVarAtom(jp, GET_SLOTNO(pc)); - LOCAL_ASSERT(atom); - } else if (IsVarSlot(jp, pc, &i)) { + if (IsVarSlot(jp, pc, &i)) { atom = GetArgOrVarAtom(jp, i); LOCAL_ASSERT(atom); + if (!QuoteString(&ss->sprinter, atom, 0)) + return NULL; } else { lval = GetLocal(ss, i); - } - { - JSAutoByteString bytes; - if (atom) - lval = js_AtomToPrintableString(cx, atom, &bytes); - LOCAL_ASSERT(lval); - todo = SprintCString(&ss->sprinter, lval); - } - if (op != JSOP_SETLOCALPOP) { - pc += oplen; - if (pc == endpc) - return pc; - LOAD_OP_DATA(pc); - if (op == JSOP_POPN) - return pc; - LOCAL_ASSERT(op == JSOP_POP); + if (!lval || SprintCString(&ss->sprinter, lval) < 0) + return NULL; } break; - default: + default: { /* * We may need to auto-parenthesize the left-most value decompiled * here, so add back PAREN_SLOP temporarily. Then decompile until the @@ -1611,7 +1591,7 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, * pass to Decompile encoded as -(ss->top-1) - 1 or just -ss->top for * the nb parameter. */ - todo = ss->sprinter.offset; + ptrdiff_t todo = ss->sprinter.offset; ss->sprinter.offset = todo + PAREN_SLOP; pc = Decompile(ss, pc, -((intN)ss->top)); if (!pc) @@ -1636,12 +1616,12 @@ DecompileDestructuringLHS(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, : "%s[%s]", lval, xval); } + if (todo < 0) + return NULL; break; + } } - if (todo < 0) - return NULL; - LOCAL_ASSERT(pc < endpc); pc += oplen; return pc; @@ -1742,9 +1722,7 @@ DecompileDestructuring(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc) } break; - case JSOP_CALLPROP: case JSOP_GETPROP: - case JSOP_LENGTH: { LOAD_ATOM(0); *OFF2STR(&ss->sprinter, head) = '{'; @@ -2744,38 +2722,25 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_ENTERBLOCK: { - JSAtom **atomv, *smallv[5]; - LOAD_OBJECT(0); argc = OBJ_BLOCK_COUNT(cx, obj); - if ((size_t)argc <= ArrayLength(smallv)) { - atomv = smallv; - } else { - atomv = (JSAtom **) cx->malloc_(argc * sizeof(JSAtom *)); - if (!atomv) - return NULL; - } - MUST_FLOW_THROUGH("enterblock_out"); -#define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \ - goto enterblock_out) + Vector atomv(cx); + if (!atomv.resize(argc)) + return NULL; + for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) { const Shape &shape = r.front(); - - if (!shape.hasShortID()) - continue; - LOCAL_ASSERT_OUT(shape.shortid < argc); + LOCAL_ASSERT(shape.hasShortID()); + LOCAL_ASSERT(shape.shortid >= 0); + LOCAL_ASSERT(shape.shortid < argc); atomv[shape.shortid] = JSID_TO_ATOM(shape.propid); } - ok = JS_TRUE; for (i = 0; i < argc; i++) { atom = atomv[i]; rval = QuoteString(&ss->sprinter, atom, 0); - if (!rval || - !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) { - ok = JS_FALSE; - goto enterblock_out; - } + if (!rval || !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) + return NULL; } sn = js_GetSrcNote(jp->script, pc); @@ -2785,10 +2750,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) js_printf(jp, "\t{\n"); jp->indent += 4; len = js_GetSrcNoteOffset(sn, 0); - ok = Decompile(ss, pc + oplen, len - oplen) - != NULL; - if (!ok) - goto enterblock_out; + if (!Decompile(ss, pc + oplen, len - oplen)) + return NULL; jp->indent -= 4; js_printf(jp, "\t}\n"); break; @@ -2800,13 +2763,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) pc2 = pc; pc += oplen; - LOCAL_ASSERT_OUT(*pc == JSOP_EXCEPTION); + LOCAL_ASSERT(*pc == JSOP_EXCEPTION); pc += JSOP_EXCEPTION_LENGTH; todo = Sprint(&ss->sprinter, exception_cookie); - if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION)) { - ok = JS_FALSE; - goto enterblock_out; - } + if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION)) + return NULL; if (*pc == JSOP_DUP) { sn2 = js_GetSrcNote(jp->script, pc); @@ -2816,39 +2777,31 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) * It is emitted only when the catch head contains * an exception guard. */ - LOCAL_ASSERT_OUT(js_GetSrcNoteOffset(sn, 0) != 0); + LOCAL_ASSERT(js_GetSrcNoteOffset(sn, 0) != 0); pc += JSOP_DUP_LENGTH; todo = Sprint(&ss->sprinter, exception_cookie); - if (todo < 0 || - !PushOff(ss, todo, JSOP_EXCEPTION)) { - ok = JS_FALSE; - goto enterblock_out; - } + if (todo < 0 || !PushOff(ss, todo, JSOP_EXCEPTION)) + return NULL; } } #if JS_HAS_DESTRUCTURING if (*pc == JSOP_DUP) { pc = DecompileDestructuring(ss, pc, endpc); - if (!pc) { - ok = JS_FALSE; - goto enterblock_out; - } - LOCAL_ASSERT_OUT(*pc == JSOP_POP); + if (!pc) + return NULL; + LOCAL_ASSERT(*pc == JSOP_POP); pc += JSOP_POP_LENGTH; lval = PopStr(ss, JSOP_NOP); js_puts(jp, lval); } else { #endif - LOCAL_ASSERT_OUT(*pc == JSOP_SETLOCALPOP); - i = GET_SLOTNO(pc) - jp->script->nfixed; + LOCAL_ASSERT(*pc == JSOP_SETLOCALPOP); pc += JSOP_SETLOCALPOP_LENGTH; - atom = atomv[i - OBJ_BLOCK_DEPTH(cx, obj)]; - str = atom; - if (!QuoteString(&jp->sprinter, str, 0)) { - ok = JS_FALSE; - goto enterblock_out; - } + LOCAL_ASSERT(OBJ_BLOCK_COUNT(cx, obj) == 1); + atom = JSID_TO_ATOM(obj->lastProperty()->propid); + if (!QuoteString(&jp->sprinter, atom, 0)) + return NULL; #if JS_HAS_DESTRUCTURING } #endif @@ -2858,19 +2811,18 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) * guarded catch head) off the stack now. */ rval = PopStr(ss, JSOP_NOP); - LOCAL_ASSERT_OUT(strcmp(rval, exception_cookie) == 0); + LOCAL_ASSERT(strcmp(rval, exception_cookie) == 0); len = js_GetSrcNoteOffset(sn, 0); if (len) { len -= pc - pc2; - LOCAL_ASSERT_OUT(len > 0); + LOCAL_ASSERT(len > 0); js_printf(jp, " if "); - ok = Decompile(ss, pc, len) != NULL; - if (!ok) - goto enterblock_out; + if (!Decompile(ss, pc, len)) + return NULL; js_printf(jp, "%s", POP_STR()); pc += len; - LOCAL_ASSERT_OUT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); + LOCAL_ASSERT(*pc == JSOP_IFEQ || *pc == JSOP_IFEQX); pc += js_CodeSpec[*pc].length; } @@ -2878,18 +2830,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) jp->indent += 4; len = 0; break; - default: - break; + default:; } todo = -2; - -#undef LOCAL_ASSERT_OUT - enterblock_out: - if (atomv != smallv) - cx->free_(atomv); - if (!ok) - return NULL; } break; From 7f3836c56304a107c9208a139b06b099392ac8e7 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 7 Nov 2011 11:46:26 -0800 Subject: [PATCH 11/46] Bug 696813 - Hoist two functions out of Decompile JSOP_ENTERBLOCK case for later reuse (r=jorendorff) --HG-- extra : rebase_source : f9c0a19d1926efc7cc89ff64fa0eca3e73775830 --- js/src/jsopcode.cpp | 64 ++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 18 deletions(-) diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 3d6e07204302..7c8806e6f502 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -1878,6 +1878,50 @@ DecompileGroupAssignment(SprintStack *ss, jsbytecode *pc, jsbytecode *endpc, #endif /* JS_HAS_DESTRUCTURING */ +#define LOCAL_ASSERT(expr) LOCAL_ASSERT_RV(expr, false) + +typedef Vector AtomVector; + +/* + * The names of the vars of a let block/expr are stored as the ids of the + * shapes of the block object. Shapes are stored in a singly-linked list in + * reverse order of addition. This function takes care of putting the names + * back in declaration order. + */ +static bool +GetBlockNames(JSContext *cx, JSObject *blockObj, AtomVector *atoms) +{ + size_t numAtoms = OBJ_BLOCK_COUNT(cx, blockObj); + LOCAL_ASSERT(numAtoms > 0); + if (!atoms->resize(numAtoms)) + return false; + + uintN i = numAtoms; + for (Shape::Range r = blockObj->lastProperty()->all(); !r.empty(); r.popFront()) { + const Shape &shape = r.front(); + LOCAL_ASSERT(shape.hasShortID()); + --i; + LOCAL_ASSERT((uintN)shape.shortid == i); + (*atoms)[i] = JSID_TO_ATOM(shape.propid); + } + + LOCAL_ASSERT(i == 0); + return true; +} + +#undef LOCAL_ASSERT + +static bool +PushBlockNames(JSContext *cx, SprintStack *ss, const AtomVector &atoms) +{ + for (size_t i = 0; i < atoms.length(); i++) { + const char *name = QuoteString(&ss->sprinter, atoms[i], 0); + if (!name || !PushOff(ss, STR2OFF(&ss->sprinter, name), JSOP_ENTERBLOCK)) + return false; + } + return true; +} + static JSBool InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) { @@ -2723,26 +2767,10 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) case JSOP_ENTERBLOCK: { LOAD_OBJECT(0); - argc = OBJ_BLOCK_COUNT(cx, obj); - - Vector atomv(cx); - if (!atomv.resize(argc)) + AtomVector atoms(cx); + if (!GetBlockNames(cx, obj, &atoms) || !PushBlockNames(cx, ss, atoms)) return NULL; - for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront()) { - const Shape &shape = r.front(); - LOCAL_ASSERT(shape.hasShortID()); - LOCAL_ASSERT(shape.shortid >= 0); - LOCAL_ASSERT(shape.shortid < argc); - atomv[shape.shortid] = JSID_TO_ATOM(shape.propid); - } - for (i = 0; i < argc; i++) { - atom = atomv[i]; - rval = QuoteString(&ss->sprinter, atom, 0); - if (!rval || !PushOff(ss, STR2OFF(&ss->sprinter, rval), op)) - return NULL; - } - sn = js_GetSrcNote(jp->script, pc); switch (sn ? SN_TYPE(sn) : SRC_NULL) { #if JS_HAS_BLOCK_SCOPE From c49db18547d6a4ca630bed82afb3335353ee3742 Mon Sep 17 00:00:00 2001 From: Luke Wagner Date: Mon, 7 Nov 2011 11:46:26 -0800 Subject: [PATCH 12/46] Bug 696813 - Hoist function out of Decompile JSOP_POPV case for later reuse (r=jorendorff) --HG-- extra : rebase_source : 49b7adbbaf2e9d3fbf2f4756cefdc659109488d3 --- js/src/jsopcode.cpp | 69 ++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 29 deletions(-) diff --git a/js/src/jsopcode.cpp b/js/src/jsopcode.cpp index 7c8806e6f502..211103b1e210 100644 --- a/js/src/jsopcode.cpp +++ b/js/src/jsopcode.cpp @@ -137,6 +137,14 @@ const char *js_CodeName[] = { #define COUNTS_LEN 16 +typedef Vector DupBuffer; + +static bool +Dup(const char *chars, DupBuffer *cb) +{ + return cb->append(chars, strlen(chars) + 1); +} + static ptrdiff_t GetJumpOffset(jsbytecode *pc, jsbytecode *pc2) { @@ -1922,6 +1930,29 @@ PushBlockNames(JSContext *cx, SprintStack *ss, const AtomVector &atoms) return true; } +static ptrdiff_t +SprintLet(JSContext *cx, JSPrinter *jp, SprintStack *ss, jsbytecode *pc, ptrdiff_t bodyLength, + const char *headChars) +{ + if (pc[bodyLength] == JSOP_LEAVEBLOCK) { + js_printf(jp, "\tlet (%s) {\n", headChars); + jp->indent += 4; + if (!Decompile(ss, pc, bodyLength)) + return -1; + jp->indent -= 4; + js_printf(jp, "\t}\n"); + return -2; + } + + LOCAL_ASSERT_RV(pc[bodyLength] == JSOP_LEAVEBLOCKEXPR, -1); + if (!Decompile(ss, pc, bodyLength)) + return -1; + + const char *bodyChars = PopStr(ss, JSOP_SETNAME); + const char *format = *bodyChars == '{' ? "let (%s) (%s)" : "let (%s) %s"; + return Sprint(&ss->sprinter, format, headChars, bodyChars); +} + static JSBool InitSprintStack(JSContext *cx, SprintStack *ss, JSPrinter *jp, uintN depth) { @@ -2678,41 +2709,21 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb) break; case SRC_DECL: + { /* This pop is at the end of the let block/expr head. */ pc += JSOP_POP_LENGTH; #if JS_HAS_DESTRUCTURING do_letheadbody: #endif + DupBuffer head(cx); + if (!Dup(POP_STR(), &head)) + return NULL; + len = js_GetSrcNoteOffset(sn, 0); - if (pc[len] == JSOP_LEAVEBLOCK) { - js_printf(jp, "\tlet (%s) {\n", POP_STR()); - jp->indent += 4; - DECOMPILE_CODE(pc, len); - jp->indent -= 4; - js_printf(jp, "\t}\n"); - todo = -2; - } else { - LOCAL_ASSERT(pc[len] == JSOP_LEAVEBLOCKEXPR); - - lval = JS_strdup(cx, PopStr(ss, JSOP_NOP)); - if (!lval) - return NULL; - - /* Set saveop to reflect what we will push. */ - saveop = JSOP_LEAVEBLOCKEXPR; - if (!Decompile(ss, pc, len)) { - cx->free_((char *)lval); - return NULL; - } - rval = PopStr(ss, JSOP_SETNAME); - todo = Sprint(&ss->sprinter, - (*rval == '{') - ? "let (%s) (%s)" - : "let (%s) %s", - lval, rval); - cx->free_((char *)lval); - } - break; + saveop = (JSOp) pc[len]; + todo = SprintLet(cx, jp, ss, pc, len, head.begin()); + } + break; default: /* Turn off parens around a yield statement. */ From 383695dfd3fd9922260913131b36cb845515e726 Mon Sep 17 00:00:00 2001 From: Yury Date: Mon, 7 Nov 2011 19:57:59 +0000 Subject: [PATCH 13/46] Bug 674619 - cmyk image with adobe marker not converted properly. r=joe --- image/decoders/nsJPEGDecoder.cpp | 80 ++++++++++++++++++--- image/decoders/nsJPEGDecoder.h | 1 + image/test/reftest/jpeg/jpg-cmyk-embed.jpg | Bin 0 -> 229 bytes image/test/reftest/jpeg/jpg-cmyk-embed.png | Bin 0 -> 161 bytes image/test/reftest/jpeg/jpg-ycck-embed.jpg | Bin 0 -> 237 bytes image/test/reftest/jpeg/jpg-ycck-embed.png | Bin 0 -> 5304 bytes image/test/reftest/jpeg/jpg-ycck.jpg | Bin 0 -> 227 bytes image/test/reftest/jpeg/jpg-ycck.png | Bin 0 -> 4792 bytes image/test/reftest/jpeg/reftest.list | 4 ++ 9 files changed, 77 insertions(+), 8 deletions(-) create mode 100644 image/test/reftest/jpeg/jpg-cmyk-embed.jpg create mode 100644 image/test/reftest/jpeg/jpg-cmyk-embed.png create mode 100644 image/test/reftest/jpeg/jpg-ycck-embed.jpg create mode 100644 image/test/reftest/jpeg/jpg-ycck-embed.png create mode 100644 image/test/reftest/jpeg/jpg-ycck.jpg create mode 100644 image/test/reftest/jpeg/jpg-ycck.png diff --git a/image/decoders/nsJPEGDecoder.cpp b/image/decoders/nsJPEGDecoder.cpp index 002568178700..456c9448615b 100644 --- a/image/decoders/nsJPEGDecoder.cpp +++ b/image/decoders/nsJPEGDecoder.cpp @@ -24,6 +24,7 @@ * Stuart Parmenter * Federico Mena-Quintero * Bobby Holley + * Yury Delendik * * 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 @@ -72,6 +73,7 @@ ycc_rgb_convert_argb (j_decompress_ptr cinfo, } static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width); +static void cmyk_convert_rgb_inverted(JSAMPROW row, JDIMENSION width); namespace mozilla { namespace imagelib { @@ -99,6 +101,32 @@ GetICCProfile(struct jpeg_decompress_struct &info) return profile; } +#define JPEG_EMBED_MARKER (JPEG_APP0 + 12) + +/* + * Check for the EMBED marker for Adobe's CMYK images. When Adobe products save + * an image with YCCK components standalone, the CMYK values are inverted; + * however, when an image is embedded within a PDF or EPS, it contains normal + * values. This is described in Adobe tech note #5116. + */ +static bool IsEmbedMarkerPresent(struct jpeg_decompress_struct *info) +{ + jpeg_saved_marker_ptr marker; + // Looking for the APP12 marker that has only 'EMBED\0' in its data. + for (marker = info->marker_list; marker != NULL; marker = marker->next) { + if (marker->marker == JPEG_EMBED_MARKER && + marker->data_length == 6 && + marker->data[0] == 0x45 && + marker->data[1] == 0x4D && + marker->data[2] == 0x42 && + marker->data[3] == 0x45 && + marker->data[4] == 0x44 && + marker->data[5] == 0x00) + return true; + } + return false; +} + METHODDEF(void) init_source (j_decompress_ptr jd); METHODDEF(boolean) fill_input_buffer (j_decompress_ptr jd); METHODDEF(void) skip_input_data (j_decompress_ptr jd, long num_bytes); @@ -194,7 +222,7 @@ nsJPEGDecoder::InitInternal() mSourceMgr.resync_to_restart = jpeg_resync_to_restart; mSourceMgr.term_source = term_source; - /* Record app markers for ICC data */ + /* Record app markers for ICC data and EMBED marker */ for (PRUint32 m = 0; m < 16; m++) jpeg_save_markers(&mInfo, JPEG_APP0 + m, 0xFFFF); } @@ -297,13 +325,14 @@ nsJPEGDecoder::WriteInternal(const char *aBuffer, PRUint32 aCount) if (profileSpace == icSigRgbData) mInfo.out_color_space = JCS_RGB; else - // qcms doesn't support ycbcr + // qcms doesn't support ycbcr mismatch = true; break; case JCS_CMYK: case JCS_YCCK: - // qcms doesn't support cmyk - mismatch = true; + // qcms doesn't support ycck + mInvertedCMYK = !IsEmbedMarkerPresent(&mInfo); + mismatch = true; break; default: mState = JPEG_ERROR; @@ -371,6 +400,7 @@ nsJPEGDecoder::WriteInternal(const char *aBuffer, PRUint32 aCount) case JCS_YCCK: /* libjpeg can convert from YCCK to CMYK, but not to RGB */ mInfo.out_color_space = JCS_CMYK; + mInvertedCMYK = !IsEmbedMarkerPresent(&mInfo); break; default: mState = JPEG_ERROR; @@ -626,7 +656,12 @@ nsJPEGDecoder::OutputScanlines(bool* suspend) /* Convert from CMYK to RGB */ /* We cannot convert directly to Cairo, as the CMSRGBTransform may wants to do a RGB transform... */ /* Would be better to have platform CMSenabled transformation from CMYK to (A)RGB... */ - cmyk_convert_rgb((JSAMPROW)imageRow, mInfo.output_width); + /* When EMBED marker is present, the conversion produces the normal (not inverted) CMYK values. */ + if (mInvertedCMYK) { + cmyk_convert_rgb_inverted((JSAMPROW)imageRow, mInfo.output_width); + } else { + cmyk_convert_rgb((JSAMPROW)imageRow, mInfo.output_width); + } sampleRow += mInfo.output_width; } if (mCMSMode == eCMSMode_All) { @@ -1168,14 +1203,14 @@ ycc_rgb_convert_argb (j_decompress_ptr cinfo, } -/**************** Inverted CMYK -> RGB conversion **************/ +/**************** CMYK -> RGB conversions **************/ /* - * Input is (Inverted) CMYK stored as 4 bytes per pixel. + * Input is inverted CMYK stored as 4 bytes per pixel. * Output is RGB stored as 3 bytes per pixel. * @param row Points to row buffer containing the CMYK bytes for each pixel in the row. * @param width Number of pixels in the row. */ -static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width) +static void cmyk_convert_rgb_inverted(JSAMPROW row, JDIMENSION width) { /* Work from end to front to shrink from 4 bytes per pixel to 3 */ JSAMPROW in = row + width*4; @@ -1213,3 +1248,32 @@ static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width) out[2] = iY*iK/255; // Blue } } + +/**************** CMYK -> RGB conversions **************/ +/* + * Input is CMYK stored as 4 bytes per pixel. + * Output is RGB stored as 3 bytes per pixel. + * @param row Points to row buffer containing the CMYK bytes for each pixel in the row. + * @param width Number of pixels in the row. + */ +static void cmyk_convert_rgb(JSAMPROW row, JDIMENSION width) +{ + /* Work from end to front to shrink from 4 bytes per pixel to 3 */ + JSAMPROW in = row + width*4; + JSAMPROW out = in; + + for (PRUint32 i = width; i > 0; i--) { + in -= 4; + out -= 3; + + // Convert from Normal CMYK (0..255) to RGB (0..255) + // (see also cmyk_convert_rgb_inverted above) + const PRUint32 iC = 255 - in[0]; + const PRUint32 iM = 255 - in[1]; + const PRUint32 iY = 255 - in[2]; + const PRUint32 iK = 255 - in[3]; + out[0] = iC*iK/255; // Red + out[1] = iM*iK/255; // Green + out[2] = iY*iK/255; // Blue + } +} diff --git a/image/decoders/nsJPEGDecoder.h b/image/decoders/nsJPEGDecoder.h index 512e0600d5f1..6781d3dc7bdf 100644 --- a/image/decoders/nsJPEGDecoder.h +++ b/image/decoders/nsJPEGDecoder.h @@ -124,6 +124,7 @@ public: qcms_transform *mTransform; bool mReading; + bool mInvertedCMYK; PRUint32 mCMSMode; }; diff --git a/image/test/reftest/jpeg/jpg-cmyk-embed.jpg b/image/test/reftest/jpeg/jpg-cmyk-embed.jpg new file mode 100644 index 0000000000000000000000000000000000000000..fbb16239a46ae3ff37733ccafa3ec0c7e73a502f GIT binary patch literal 229 zcmex=Sg^_`Yfti7Y pfx(`^`6vGq`w9OT<)73yfE2s`01`kMM)?N}4nQVQOz_|Tn*f>L9%}#q literal 0 HcmV?d00001 diff --git a/image/test/reftest/jpeg/jpg-cmyk-embed.png b/image/test/reftest/jpeg/jpg-cmyk-embed.png new file mode 100644 index 0000000000000000000000000000000000000000..7622f07c5ba3f9c04bc41188b0449ef5ba13e043 GIT binary patch literal 161 zcmeAS@N?(olHy`uVBq!ia0vp^4nWMo!3HEflYZU@QjEnx?oJHr&dIz4a@dl*-CY>| zgW!U_%O?XxI14-?iy0WWg+Z8+Vb&Z8prDDTi(`n#@wXE;@&Zjebl`veG5&;tDeIQ- yELAz^cuVr0+lKS(2iXoEd|=6YfdPa*%w&D}j-7ww)UU=s{S2P2elF{r5}E*BdomsX literal 0 HcmV?d00001 diff --git a/image/test/reftest/jpeg/jpg-ycck-embed.jpg b/image/test/reftest/jpeg/jpg-ycck-embed.jpg new file mode 100644 index 0000000000000000000000000000000000000000..c7a029fc43c03bc7038904a7a0959b40d28b678b GIT binary patch literal 237 zcmex=*v&ER2E- zOo9x|f($Hz4F8WXn1bwJ5CD@$cK`J3J7?bf=9YQ)-Fs)wTOB&xk4n-a z0RX5RfA4Vg?txwe0uG%yiLZVJfRZ3&vqL#-Hd8FgOcJIG0LXTlt4F znV;nU7pd~Z5-9*sC~7^KFUaDdoR4yPj#R9W528#@Oj9t7QLqFJ5M>PoCx75c?`=M? zhl2STB0g%Ph*`|f;48QtHj#ssQhU4Wl+@igT+wT z`m~~DEcKuDiyof z*yQBoSPKO_Yehmoj=wbcX!++bZar(odLMJg^cEy>W$99;BB{L0^h}wAnI-1(1Wc>{ zZQ_6II4-PlaZCyqBncz}5xP_q+GRpfGFomCUnmu3ikL#tpH=uDx{ZrLfgk!B1zPPp zK%Zj`R4&i~@@pr+(8vIZ-->FWk97+oMghf_7p>R-q3=;f_3!8Zn&4ve5zZ1OGZkWX zcm$ItlVmFx{U#I>7Lb54paV^y3k-o7umbkL3Amy^BOed|LO=wF1+xJUq<{<{0XbkH zSOy9}5m*N{feKI!YC#=n0Q*5RI1EmJ)8IU~2(E(LKo0uAV=xF_fKdp7@DLTEL7I>r zWDHqC_7Dr22KhiiP&gC|B|yoL2$Deyq2*8!R035%+o3w>d*~2!0y+zILpPxwXaM>h zdJQ8m1*XF~unBAfvtUm+5RQOn!O8Gkcs`sDuZ7Ft8h9_<1fPJ zh(2P4un;y9g2W+7hy+=T6e6Wa4bp(LA|1$OM2 zQ=Ajd9~X;D#Vx?C!EM1c;*R6GalN=+_?<1cf_mE#v)F>7dHYJ`SqpYRuq8z8( zqzqB1R1>NPHI6E!uBFydPg3tuUnbdHb>POV&41{6A z;4sn|#f<%oYmCOxrS)VYeWL3`-o(O*`zPMghP5rU!?hP^@6hhl9@f#-@zW9OlR6M4PNMX*PLi zs$t48oo`xiddG}v=599Etj6q$InJDAo@Tzq{GtVH;b4(sQE73(61H@-6k2Yv?4E?3 z;mla z>{{)9x3{q8*;m?MnM|GRJ9)|EmdVeiSWXd4**fKh1I;1WVWmTx!&^ruM~P#Dm|8ov*Ui9< z>$crZK8-moVcNE7a(8`qu6vF9eGfwqzQ<0Feou4HRL{MhPrYotBwh!-M%XUwMeO6= zh_|12p?8;$nop!ph0i@-1K(uddf!1m2fqb=$NjPXf&T0LuXA)bJWd^FFu*BbQ9yej zC2&SyMd1A)iy%o*YcL!f5WFGy&UEAHqUp^cAcPaLA>?kTY3SV0AH#5Ap<$I_55n!j z7lwDtpv{;wW6z8i5nd6+5w|1FB4v?nQOZ&AQFT!x(caM|(efCZm_;$?W3^(1u`O}< zxTv_Dal8#vYXJ>28PM>{bj`AGtoC68igy@7l32(R|+#2q1 zA}6sbagfL6ZRS1Zd+;~%2Lx_{5&c#$U77tdCo1Pau4=9*w{xDwyw&si=ljn8b^&GqZ$aBa{e{aG_AK&T zw0$wOIB{{?62m3=OZt}jE#1A0v@C7ePkFX^C3(-6M=x*9*U4X+-@C$ZMSX!%fu!L2 zO4pUut1zpCtGZTCUR}QWT_L~lT#;>2Y0;ZCyfx>(vHPa%o3XV?YrBdai?^=BtrM-g zw%&dH?h@6K`6ay@f;Tj6WNs|n_sKjo*9mJMZsW8mt<&H!>QF_JMt}eUH9R z`2O1d!2N9pSO*$@F#e(HAnjmb6Vx=XX|Oq^S>6)c(tRl4P+O~O>%kwbf82Fg|8V6I z^&`bcNk{XKjvbqSZ1}kN_>&XKCwklFwB0@#bMi`iSbNv0z*A>V`<_18;n{KQjN6&R zXI;)7I>$QKeBSYVQ>R1c!Jiy{I@smd)pWt>Ld!+xi>=*LyN_OSzjWfV_vO=9I9JYJ zoqo0ZTI97G*W<6tZ}4wCyqSLU*{$qbqqmpd!QLsnt8%yWp7y<(pUr=6lsn3g^mzAl z-k)**b}z5@@h@4wyy?q-Kz&f!uh(Dq(Ej1!0pEd(k7hpVdn|rD`lR4j)nBWgnmzsD zH;>;s2V(~No=KjK{k~>Mb7}3R8AkvB literal 0 HcmV?d00001 diff --git a/image/test/reftest/jpeg/jpg-ycck.jpg b/image/test/reftest/jpeg/jpg-ycck.jpg new file mode 100644 index 0000000000000000000000000000000000000000..4405b6254679021e314bdc412324500af418ea78 GIT binary patch literal 227 zcmex=I~WAOB$QxeVrF4wW9Q)H;^yJy;}>8=mISf?-(uinVPs%pU}j)pV6bO!{=vZi wr`vF~;vXg{_f>Wq*k>4}crj?SGw?8}wuf0du&0@(*f%(Pd}Y7p!0`Vj0I;keXaE2J literal 0 HcmV?d00001 diff --git a/image/test/reftest/jpeg/jpg-ycck.png b/image/test/reftest/jpeg/jpg-ycck.png new file mode 100644 index 0000000000000000000000000000000000000000..4d0db1f5d93ca6f57a9d03d270b32a25a106b20d GIT binary patch literal 4792 zcmeH}cT`i^7KhKhH@(mjA|NI7CRKp|A|stU) zTwS;F4`DLD=>Ha}^28D;08j{OJ(VxW;Gvv{a!Qs|tk4HgrY}rZFpN>K1Pu^n4FxBB z;wc|(KCy>_`Kcm4YNLo*%unSjxCQ04nKA*&NFd7jnLDlaFOmoo5~WNto;lOb*_p-k z7i6Ufq*AL8E-#rY;WOE3sba2Z5dfc_S+NVKDRRq1lk8~c?C5A^Z;ht^^Z3)n&sKj9 zirRj%7!3VbGlwEE{J{>vN3g*7gYDPe+mfkYreT}7Z>CKM&0bJ<90%$N3*7xEQ^J zGlU6Dh0G2MXYyo{Oa-Iwgkr)15>N(opb2z=Aut10z#cdOSM+1#17?C?5Ducje82;V zAQebJ7RUiBK|Uw~8^9J&4t9YmPz`Fq5zqimfV1d_>k7CAZi0J24tl{eFaTbI5eS0t z5EY_9nvfo33|T_<5DS_C`9OhC7!(b~K?#rul0iApDyRr5fy$vhP&M=|bR0ShU4lBG zJ5V>&5B&; zgNG1+kP!x=k60lrgpC9vF-SZjL6#wfNGVc@)FMqtD{>u?BhQhy7#xO%VPdQ>t{8vJ z91IVWj#-9ThbhM#z|><}FxN5Nm;uZvmWtKJT4G(W9BdRe0h@)*$ChIEVUJc2N3bXO5@HA3fkiKE0p|{fq)Ya6dsYj@1t5>L> zRF^Xlh6#hiNMRH+jxcU9-c8b(tx>K!pTP_-_?e-Ewsb5muT#%k=l? zx9g7>m>SG6$Tg@lcwk5}WEsXAZZ>Q-95B)|3Np$usxi82Of+U0CmL@xZZ#e@F*S)a zS!2>*^2Aiblw-Qsw8r$l8P&|)EZwZq?1nkcoMoPDzQg>Q1#ID9k!Vq2an%yGbhH#& z?y&5bf}P?#MKq;yN|zPI%F`;-s>Z6@nqeJioon4-{ldn`CdOu?O{>kQt%GffZI$gk zJ2ktRcDZ&~Bn^PW7F-d}`y=m(wh#38w9wcH4pG5ah7Nq1oYsqm!e= zvDUHQ$;c_rX}ePwi_QvRtz%tu#yWdBFLyrS{MNzSjJd`6m}BFW@dX8i$RGj5`?j zo*T@qnk~w1TV%1QU{T*<-^E`q!7Sk|Y0lBl zS(($l)N|>cWze#P%bJ%PF3(%uyTWh9ft93{$t!=zwaqQbeYGlbRa2f$-io}Q)qbmM z@|E%>`CV&V*X&x0Su0%IUNE&_Tft}{zwmOAZBc2_`*pl^m%p<6s_d(=_3`W5iye!1 zZoq92ZMe13edB==)sn>}J)43y)o*5QF5LWT%YrSJN~e|X+Dh6g+bS;$ENk3mv~9z- z_vMM@H!Hj<>b7fdFW5f3gTJG5r^n9PT{^ppcD>sz+#KfZ|Kvo!Z~wKYbD#D;6F(byp8vDz z&)Z*^z4-1Ik6+pbq6T_jN?wlrx^7T&aQ~}mubPKKh9167eLXT<_(t>1fw!!;t?we= z^^Ih`$G Date: Mon, 7 Nov 2011 11:42:02 -0800 Subject: [PATCH 14/46] Bug 679986: Deoptimize unnecessary regexps. (r=cdleary) --HG-- extra : rebase_source : 63f2d3865ea362fd2071751edce00737ab83ae26 --- js/src/jit-test/tests/basic/bug679986-1.js | 2 ++ js/src/jit-test/tests/basic/bug679986-2.js | 2 ++ js/src/yarr/YarrJIT.cpp | 16 ++++++++++++++++ 3 files changed, 20 insertions(+) create mode 100644 js/src/jit-test/tests/basic/bug679986-1.js create mode 100644 js/src/jit-test/tests/basic/bug679986-2.js diff --git a/js/src/jit-test/tests/basic/bug679986-1.js b/js/src/jit-test/tests/basic/bug679986-1.js new file mode 100644 index 000000000000..e1e5128104bc --- /dev/null +++ b/js/src/jit-test/tests/basic/bug679986-1.js @@ -0,0 +1,2 @@ +// don't assert +var m = "aaaa".match(/(?:|a)*/); diff --git a/js/src/jit-test/tests/basic/bug679986-2.js b/js/src/jit-test/tests/basic/bug679986-2.js new file mode 100644 index 000000000000..95e84901b0a6 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug679986-2.js @@ -0,0 +1,2 @@ +// don't hang +var m = "aaaa".match(/(?:a?)*/); diff --git a/js/src/yarr/YarrJIT.cpp b/js/src/yarr/YarrJIT.cpp index d5b88fac7673..ea529f876319 100644 --- a/js/src/yarr/YarrJIT.cpp +++ b/js/src/yarr/YarrJIT.cpp @@ -2089,6 +2089,22 @@ class YarrGenerator : private MacroAssembler { alternativeEndOpCode = OpNestedAlternativeEnd; } } else if (term->parentheses.isTerminal) { + // Terminal groups are optimized on the assumption that matching will never + // backtrack into the terminal group. But this is false if there is more + // than one alternative and one of the alternatives can match empty. In that + // case, the empty match is counted as a failure, so we would need to backtrack. + // The backtracking code doesn't handle this case correctly, so we fall back + // to the interpreter. + Vector& alternatives = term->parentheses.disjunction->m_alternatives; + if (alternatives.size() != 1) { + for (unsigned i = 0; i < alternatives.size(); ++i) { + if (alternatives[i]->m_minimumSize == 0) { + m_shouldFallBack = true; + return; + } + } + } + // Select the 'Terminal' nodes. parenthesesBeginOpCode = OpParenthesesSubpatternTerminalBegin; parenthesesEndOpCode = OpParenthesesSubpatternTerminalEnd; From 75b4a80ee340fd768b3a1398cf7a4ebc8d7bd94c Mon Sep 17 00:00:00 2001 From: Felix Fung Date: Mon, 7 Nov 2011 12:08:04 -0800 Subject: [PATCH 15/46] Bug 700389 - Simplify sortByTotalScore. r=zpao --- toolkit/components/satchel/nsFormAutoComplete.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/toolkit/components/satchel/nsFormAutoComplete.js b/toolkit/components/satchel/nsFormAutoComplete.js index be2142c33d14..4cfe0a2af8b2 100644 --- a/toolkit/components/satchel/nsFormAutoComplete.js +++ b/toolkit/components/satchel/nsFormAutoComplete.js @@ -163,9 +163,7 @@ FormAutoComplete.prototype = { */ autoCompleteSearch : function (aInputName, aUntrimmedSearchString, aField, aPreviousResult) { function sortBytotalScore (a, b) { - let x = a.totalScore; - let y = b.totalScore; - return ((x > y) ? -1 : ((x < y) ? 1 : 0)); + return b.totalScore - a.totalScore; } if (!this._enabled) From b653f7af8e532f7ba052b021ab382d0dfc58a3e0 Mon Sep 17 00:00:00 2001 From: Marco Castelluccio Date: Mon, 7 Nov 2011 20:11:44 +0000 Subject: [PATCH 16/46] Bug 605808 - Make "layers.prefer-opengl" work when D3D10 is available; r=joedrew --- widget/src/windows/nsWindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index cbe1cd5d6d39..e9a7b48268df 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -3288,7 +3288,7 @@ nsWindow::GetLayerManager(PLayersChild* aShadowManager, } #ifdef MOZ_ENABLE_D3D10_LAYER - if (!prefs.mPreferD3D9) { + if (!prefs.mPreferD3D9 && !prefs.mPreferOpenGL) { nsRefPtr layerManager = new mozilla::layers::LayerManagerD3D10(this); if (layerManager->Initialize()) { From 929966d70792e86424a4b0b98c51bc308aa988c2 Mon Sep 17 00:00:00 2001 From: William Lachance Date: Mon, 7 Nov 2011 13:14:22 -0800 Subject: [PATCH 17/46] Bug 679759 - MINIDUMP_STACKWALK_CGI support, let harness download symbols as needed, r=ted --- build/automation.py.in | 66 +---------- build/automationutils.py | 182 +++++++++++++++++++++---------- layout/tools/reftest/Makefile.in | 1 - testing/mochitest/Makefile.in | 1 - testing/xpcshell/Makefile.in | 1 - 5 files changed, 123 insertions(+), 128 deletions(-) diff --git a/build/automation.py.in b/build/automation.py.in index ce30cd9a644e..008458dce092 100644 --- a/build/automation.py.in +++ b/build/automation.py.in @@ -52,7 +52,6 @@ import sys import threading import tempfile import sqlite3 -import zipfile SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))) sys.path.insert(0, SCRIPT_DIR) @@ -98,69 +97,6 @@ _log.setLevel(logging.INFO) _log.addHandler(handler) -class ZipFileReader(object): - """ - Class to read zip files in Python 2.5 and later. Limited to only what we - actually use. - """ - - def __init__(self, filename): - self._zipfile = zipfile.ZipFile(filename, "r") - - def __del__(self): - self._zipfile.close() - - def _getnormalizedpath(self, path): - """ - Gets a normalized path from 'path' (or the current working directory if - 'path' is None). Also asserts that the path exists. - """ - if path is None: - path = os.curdir - path = os.path.normpath(os.path.expanduser(path)) - assert os.path.isdir(path) - return path - - def _extractname(self, name, path): - """ - Extracts a file with the given name from the zip file to the given path. - Also creates any directories needed along the way. - """ - filename = os.path.normpath(os.path.join(path, name)) - if name.endswith("/"): - os.makedirs(filename) - else: - path = os.path.split(filename)[0] - if not os.path.isdir(path): - os.makedirs(path) - with open(filename, "wb") as dest: - dest.write(self._zipfile.read(name)) - - def namelist(self): - return self._zipfile.namelist() - - def read(self, name): - return self._zipfile.read(name) - - def extract(self, name, path = None): - if hasattr(self._zipfile, "extract"): - return self._zipfile.extract(name, path) - - # This will throw if name is not part of the zip file. - self._zipfile.getinfo(name) - - self._extractname(name, self._getnormalizedpath(path)) - - def extractall(self, path = None): - if hasattr(self._zipfile, "extractall"): - return self._zipfile.extractall(path) - - path = self._getnormalizedpath(path) - - for name in self._zipfile.namelist(): - self._extractname(name, path) - - ################# # PROFILE SETUP # ################# @@ -1052,7 +988,7 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t os.makedirs(extensionsRootDir) if os.path.isfile(extensionSource): - reader = ZipFileReader(extensionSource) + reader = automationutils.ZipFileReader(extensionSource) for filename in reader.namelist(): # Sanity check the zip file. diff --git a/build/automationutils.py b/build/automationutils.py index 3f52516af8d3..be7f34794df9 100644 --- a/build/automationutils.py +++ b/build/automationutils.py @@ -36,11 +36,12 @@ # # ***** END LICENSE BLOCK ***** */ -import glob, logging, os, platform, shutil, subprocess, sys +import glob, logging, os, platform, shutil, subprocess, sys, tempfile, urllib2, zipfile import re from urlparse import urlparse __all__ = [ + "ZipFileReader", "addCommonOptions", "checkForCrashes", "dumpLeakLog", @@ -70,6 +71,68 @@ DEBUGGER_INFO = { } } +class ZipFileReader(object): + """ + Class to read zip files in Python 2.5 and later. Limited to only what we + actually use. + """ + + def __init__(self, filename): + self._zipfile = zipfile.ZipFile(filename, "r") + + def __del__(self): + self._zipfile.close() + + def _getnormalizedpath(self, path): + """ + Gets a normalized path from 'path' (or the current working directory if + 'path' is None). Also asserts that the path exists. + """ + if path is None: + path = os.curdir + path = os.path.normpath(os.path.expanduser(path)) + assert os.path.isdir(path) + return path + + def _extractname(self, name, path): + """ + Extracts a file with the given name from the zip file to the given path. + Also creates any directories needed along the way. + """ + filename = os.path.normpath(os.path.join(path, name)) + if name.endswith("/"): + os.makedirs(filename) + else: + path = os.path.split(filename)[0] + if not os.path.isdir(path): + os.makedirs(path) + with open(filename, "wb") as dest: + dest.write(self._zipfile.read(name)) + + def namelist(self): + return self._zipfile.namelist() + + def read(self, name): + return self._zipfile.read(name) + + def extract(self, name, path = None): + if hasattr(self._zipfile, "extract"): + return self._zipfile.extract(name, path) + + # This will throw if name is not part of the zip file. + self._zipfile.getinfo(name) + + self._extractname(name, self._getnormalizedpath(path)) + + def extractall(self, path = None): + if hasattr(self._zipfile, "extractall"): + return self._zipfile.extractall(path) + + path = self._getnormalizedpath(path) + + for name in self._zipfile.namelist(): + self._extractname(name, path) + log = logging.getLogger() def isURL(thing): @@ -102,7 +165,6 @@ def addCommonOptions(parser, defaults={}): def checkForCrashes(dumpDir, symbolsPath, testName=None): stackwalkPath = os.environ.get('MINIDUMP_STACKWALK', None) - stackwalkCGI = os.environ.get('MINIDUMP_STACKWALK_CGI', None) # try to get the caller's filename if no test name is given if testName is None: try: @@ -110,70 +172,70 @@ def checkForCrashes(dumpDir, symbolsPath, testName=None): except: testName = "unknown" - foundCrash = False + # Check preconditions dumps = glob.glob(os.path.join(dumpDir, '*.dmp')) - for d in dumps: - log.info("PROCESS-CRASH | %s | application crashed (minidump found)", testName) - print "Crash dump filename: " + d - if symbolsPath and stackwalkPath and os.path.exists(stackwalkPath): - p = subprocess.Popen([stackwalkPath, d, symbolsPath], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - (out, err) = p.communicate() - if len(out) > 3: - # minidump_stackwalk is chatty, so ignore stderr when it succeeds. - print out - else: - print "stderr from minidump_stackwalk:" - print err - if p.returncode != 0: - print "minidump_stackwalk exited with return code %d" % p.returncode - elif stackwalkCGI and symbolsPath and isURL(symbolsPath): - f = None - try: - f = open(d, "rb") - sys.path.append(os.path.join(os.path.dirname(__file__), "poster.zip")) - from poster.encode import multipart_encode - from poster.streaminghttp import register_openers - import urllib2 - register_openers() - datagen, headers = multipart_encode({"minidump": f, - "symbols": symbolsPath}) - request = urllib2.Request(stackwalkCGI, datagen, headers) - result = urllib2.urlopen(request).read() - if len(result) > 3: - print result + if len(dumps) == 0: + return False + + foundCrash = False + removeSymbolsPath = False + + # If our symbols are at a remote URL, download them now + if isURL(symbolsPath): + print "Downloading symbols from: " + symbolsPath + removeSymbolsPath = True + # Get the symbols and write them to a temporary zipfile + data = urllib2.urlopen(symbolsPath) + symbolsFile = tempfile.TemporaryFile() + symbolsFile.write(data.read()) + # extract symbols to a temporary directory (which we'll delete after + # processing all crashes) + symbolsPath = tempfile.mkdtemp() + zfile = ZipFileReader(symbolsFile) + zfile.extractall(symbolsPath) + + try: + for d in dumps: + log.info("PROCESS-CRASH | %s | application crashed (minidump found)", testName) + print "Crash dump filename: " + d + if symbolsPath and stackwalkPath and os.path.exists(stackwalkPath): + # run minidump stackwalk + p = subprocess.Popen([stackwalkPath, d, symbolsPath], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (out, err) = p.communicate() + if len(out) > 3: + # minidump_stackwalk is chatty, so ignore stderr when it succeeds. + print out else: - print "stackwalkCGI returned nothing." - finally: - if f: - f.close() - else: - if not symbolsPath: - print "No symbols path given, can't process dump." - if not stackwalkPath and not stackwalkCGI: - print "Neither MINIDUMP_STACKWALK nor MINIDUMP_STACKWALK_CGI is set, can't process dump." + print "stderr from minidump_stackwalk:" + print err + if p.returncode != 0: + print "minidump_stackwalk exited with return code %d" % p.returncode else: - if stackwalkPath and not os.path.exists(stackwalkPath): + if not symbolsPath: + print "No symbols path given, can't process dump." + if not stackwalkPath: + print "MINIDUMP_STACKWALK not set, can't process dump." + elif stackwalkPath and not os.path.exists(stackwalkPath): print "MINIDUMP_STACKWALK binary not found: %s" % stackwalkPath - elif stackwalkCGI and not isURL(stackwalkCGI): - print "MINIDUMP_STACKWALK_CGI is not a URL: %s" % stackwalkCGI - elif symbolsPath and not isURL(symbolsPath): - print "symbolsPath is not a URL: %s" % symbolsPath - dumpSavePath = os.environ.get('MINIDUMP_SAVE_PATH', None) - if dumpSavePath: - shutil.move(d, dumpSavePath) - print "Saved dump as %s" % os.path.join(dumpSavePath, - os.path.basename(d)) - else: - os.remove(d) - extra = os.path.splitext(d)[0] + ".extra" - if os.path.exists(extra): - os.remove(extra) - foundCrash = True + dumpSavePath = os.environ.get('MINIDUMP_SAVE_PATH', None) + if dumpSavePath: + shutil.move(d, dumpSavePath) + print "Saved dump as %s" % os.path.join(dumpSavePath, + os.path.basename(d)) + else: + os.remove(d) + extra = os.path.splitext(d)[0] + ".extra" + if os.path.exists(extra): + os.remove(extra) + foundCrash = True + finally: + if removeSymbolsPath: + shutil.rmtree(symbolsPath) return foundCrash - + def getFullPath(directory, path): "Get an absolute path relative to 'directory'." return os.path.normpath(os.path.join(directory, os.path.expanduser(path))) diff --git a/layout/tools/reftest/Makefile.in b/layout/tools/reftest/Makefile.in index 7d589259262f..94b0ddba4a26 100644 --- a/layout/tools/reftest/Makefile.in +++ b/layout/tools/reftest/Makefile.in @@ -84,7 +84,6 @@ _HARNESS_FILES = \ $(topsrcdir)/build/mobile/devicemanagerADB.py \ $(topsrcdir)/build/mobile/devicemanagerSUT.py \ $(topsrcdir)/build/automationutils.py \ - $(topsrcdir)/build/poster.zip \ $(topsrcdir)/build/mobile/remoteautomation.py \ $(topsrcdir)/testing/mochitest/server.js \ $(topsrcdir)/build/pgo/server-locations.txt \ diff --git a/testing/mochitest/Makefile.in b/testing/mochitest/Makefile.in index 2a8eb7dd21a4..6e38bc9c4331 100644 --- a/testing/mochitest/Makefile.in +++ b/testing/mochitest/Makefile.in @@ -82,7 +82,6 @@ _SERV_FILES = \ $(topsrcdir)/build/mobile/devicemanagerADB.py \ $(topsrcdir)/build/mobile/devicemanagerSUT.py \ $(topsrcdir)/build/automationutils.py \ - $(topsrcdir)/build/poster.zip \ $(topsrcdir)/build/mobile/remoteautomation.py \ gen_template.pl \ server.js \ diff --git a/testing/xpcshell/Makefile.in b/testing/xpcshell/Makefile.in index 8528c3dba1b6..d1a37405526e 100644 --- a/testing/xpcshell/Makefile.in +++ b/testing/xpcshell/Makefile.in @@ -65,7 +65,6 @@ EXTRA_BUILD_FILES := \ automationutils.py \ manifestparser.py \ mozinfo.py \ - poster.zip \ $(NULL) # And files for running xpcshell remotely from $(topsrcdir)/build/mobile From 2edb9f1d74f2e31f91379466bbed4bbc9f205565 Mon Sep 17 00:00:00 2001 From: "Brian R. Bondy" Date: Mon, 7 Nov 2011 16:29:39 -0500 Subject: [PATCH 18/46] Bug 481815 - Strings for localization relating to maintenance service. r=rs --- browser/locales/en-US/chrome/browser/preferences/advanced.dtd | 3 +++ browser/locales/en-US/installer/custom.properties | 4 ++++ 2 files changed, 7 insertions(+) diff --git a/browser/locales/en-US/chrome/browser/preferences/advanced.dtd b/browser/locales/en-US/chrome/browser/preferences/advanced.dtd index c7b8fc9144ac..b37487d7d7e9 100644 --- a/browser/locales/en-US/chrome/browser/preferences/advanced.dtd +++ b/browser/locales/en-US/chrome/browser/preferences/advanced.dtd @@ -72,6 +72,9 @@ + + + diff --git a/browser/locales/en-US/installer/custom.properties b/browser/locales/en-US/installer/custom.properties index 7de9fec83a11..19e23c6b3ef4 100644 --- a/browser/locales/en-US/installer/custom.properties +++ b/browser/locales/en-US/installer/custom.properties @@ -57,6 +57,10 @@ OPTIONS_PAGE_TITLE=Setup Type OPTIONS_PAGE_SUBTITLE=Choose setup options SHORTCUTS_PAGE_TITLE=Set Up Shortcuts SHORTCUTS_PAGE_SUBTITLE=Create Program Icons +COMPONENTS_PAGE_TITLE=Set Up Optional Components +COMPONENTS_PAGE_SUBTITLE=Optional Recommended Components +OPTIONAL_COMPONENTS_DESC=The Maintenance Service will allow you to update $BrandShortName silently in the background. +MAINTENANCE_SERVICE_CHECKBOX_DESC=Install &Maintenance Service SUMMARY_PAGE_TITLE=Summary SUMMARY_PAGE_SUBTITLE=Ready to start installing $BrandShortName SUMMARY_INSTALLED_TO=$BrandShortName will be installed to the following location: From ccf92d7c36f3ac18b740872ca1b5edb6afdda7c9 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Mon, 7 Nov 2011 13:40:42 -0800 Subject: [PATCH 19/46] Back out cd695cdb3b4f (bug 679759) because of test failures --- build/automation.py.in | 66 ++++++++++- build/automationutils.py | 182 ++++++++++--------------------- layout/tools/reftest/Makefile.in | 1 + testing/mochitest/Makefile.in | 1 + testing/xpcshell/Makefile.in | 1 + 5 files changed, 128 insertions(+), 123 deletions(-) diff --git a/build/automation.py.in b/build/automation.py.in index 008458dce092..ce30cd9a644e 100644 --- a/build/automation.py.in +++ b/build/automation.py.in @@ -52,6 +52,7 @@ import sys import threading import tempfile import sqlite3 +import zipfile SCRIPT_DIR = os.path.abspath(os.path.realpath(os.path.dirname(sys.argv[0]))) sys.path.insert(0, SCRIPT_DIR) @@ -97,6 +98,69 @@ _log.setLevel(logging.INFO) _log.addHandler(handler) +class ZipFileReader(object): + """ + Class to read zip files in Python 2.5 and later. Limited to only what we + actually use. + """ + + def __init__(self, filename): + self._zipfile = zipfile.ZipFile(filename, "r") + + def __del__(self): + self._zipfile.close() + + def _getnormalizedpath(self, path): + """ + Gets a normalized path from 'path' (or the current working directory if + 'path' is None). Also asserts that the path exists. + """ + if path is None: + path = os.curdir + path = os.path.normpath(os.path.expanduser(path)) + assert os.path.isdir(path) + return path + + def _extractname(self, name, path): + """ + Extracts a file with the given name from the zip file to the given path. + Also creates any directories needed along the way. + """ + filename = os.path.normpath(os.path.join(path, name)) + if name.endswith("/"): + os.makedirs(filename) + else: + path = os.path.split(filename)[0] + if not os.path.isdir(path): + os.makedirs(path) + with open(filename, "wb") as dest: + dest.write(self._zipfile.read(name)) + + def namelist(self): + return self._zipfile.namelist() + + def read(self, name): + return self._zipfile.read(name) + + def extract(self, name, path = None): + if hasattr(self._zipfile, "extract"): + return self._zipfile.extract(name, path) + + # This will throw if name is not part of the zip file. + self._zipfile.getinfo(name) + + self._extractname(name, self._getnormalizedpath(path)) + + def extractall(self, path = None): + if hasattr(self._zipfile, "extractall"): + return self._zipfile.extractall(path) + + path = self._getnormalizedpath(path) + + for name in self._zipfile.namelist(): + self._extractname(name, path) + + ################# # PROFILE SETUP # ################# @@ -988,7 +1052,7 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t os.makedirs(extensionsRootDir) if os.path.isfile(extensionSource): - reader = automationutils.ZipFileReader(extensionSource) + reader = ZipFileReader(extensionSource) for filename in reader.namelist(): # Sanity check the zip file. diff --git a/build/automationutils.py b/build/automationutils.py index be7f34794df9..3f52516af8d3 100644 --- a/build/automationutils.py +++ b/build/automationutils.py @@ -36,12 +36,11 @@ # # ***** END LICENSE BLOCK ***** */ -import glob, logging, os, platform, shutil, subprocess, sys, tempfile, urllib2, zipfile +import glob, logging, os, platform, shutil, subprocess, sys import re from urlparse import urlparse __all__ = [ - "ZipFileReader", "addCommonOptions", "checkForCrashes", "dumpLeakLog", @@ -71,68 +70,6 @@ DEBUGGER_INFO = { } } -class ZipFileReader(object): - """ - Class to read zip files in Python 2.5 and later. Limited to only what we - actually use. - """ - - def __init__(self, filename): - self._zipfile = zipfile.ZipFile(filename, "r") - - def __del__(self): - self._zipfile.close() - - def _getnormalizedpath(self, path): - """ - Gets a normalized path from 'path' (or the current working directory if - 'path' is None). Also asserts that the path exists. - """ - if path is None: - path = os.curdir - path = os.path.normpath(os.path.expanduser(path)) - assert os.path.isdir(path) - return path - - def _extractname(self, name, path): - """ - Extracts a file with the given name from the zip file to the given path. - Also creates any directories needed along the way. - """ - filename = os.path.normpath(os.path.join(path, name)) - if name.endswith("/"): - os.makedirs(filename) - else: - path = os.path.split(filename)[0] - if not os.path.isdir(path): - os.makedirs(path) - with open(filename, "wb") as dest: - dest.write(self._zipfile.read(name)) - - def namelist(self): - return self._zipfile.namelist() - - def read(self, name): - return self._zipfile.read(name) - - def extract(self, name, path = None): - if hasattr(self._zipfile, "extract"): - return self._zipfile.extract(name, path) - - # This will throw if name is not part of the zip file. - self._zipfile.getinfo(name) - - self._extractname(name, self._getnormalizedpath(path)) - - def extractall(self, path = None): - if hasattr(self._zipfile, "extractall"): - return self._zipfile.extractall(path) - - path = self._getnormalizedpath(path) - - for name in self._zipfile.namelist(): - self._extractname(name, path) - log = logging.getLogger() def isURL(thing): @@ -165,6 +102,7 @@ def addCommonOptions(parser, defaults={}): def checkForCrashes(dumpDir, symbolsPath, testName=None): stackwalkPath = os.environ.get('MINIDUMP_STACKWALK', None) + stackwalkCGI = os.environ.get('MINIDUMP_STACKWALK_CGI', None) # try to get the caller's filename if no test name is given if testName is None: try: @@ -172,70 +110,70 @@ def checkForCrashes(dumpDir, symbolsPath, testName=None): except: testName = "unknown" - # Check preconditions - dumps = glob.glob(os.path.join(dumpDir, '*.dmp')) - if len(dumps) == 0: - return False - foundCrash = False - removeSymbolsPath = False - - # If our symbols are at a remote URL, download them now - if isURL(symbolsPath): - print "Downloading symbols from: " + symbolsPath - removeSymbolsPath = True - # Get the symbols and write them to a temporary zipfile - data = urllib2.urlopen(symbolsPath) - symbolsFile = tempfile.TemporaryFile() - symbolsFile.write(data.read()) - # extract symbols to a temporary directory (which we'll delete after - # processing all crashes) - symbolsPath = tempfile.mkdtemp() - zfile = ZipFileReader(symbolsFile) - zfile.extractall(symbolsPath) - - try: - for d in dumps: - log.info("PROCESS-CRASH | %s | application crashed (minidump found)", testName) - print "Crash dump filename: " + d - if symbolsPath and stackwalkPath and os.path.exists(stackwalkPath): - # run minidump stackwalk - p = subprocess.Popen([stackwalkPath, d, symbolsPath], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - (out, err) = p.communicate() - if len(out) > 3: - # minidump_stackwalk is chatty, so ignore stderr when it succeeds. - print out + dumps = glob.glob(os.path.join(dumpDir, '*.dmp')) + for d in dumps: + log.info("PROCESS-CRASH | %s | application crashed (minidump found)", testName) + print "Crash dump filename: " + d + if symbolsPath and stackwalkPath and os.path.exists(stackwalkPath): + p = subprocess.Popen([stackwalkPath, d, symbolsPath], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + (out, err) = p.communicate() + if len(out) > 3: + # minidump_stackwalk is chatty, so ignore stderr when it succeeds. + print out + else: + print "stderr from minidump_stackwalk:" + print err + if p.returncode != 0: + print "minidump_stackwalk exited with return code %d" % p.returncode + elif stackwalkCGI and symbolsPath and isURL(symbolsPath): + f = None + try: + f = open(d, "rb") + sys.path.append(os.path.join(os.path.dirname(__file__), "poster.zip")) + from poster.encode import multipart_encode + from poster.streaminghttp import register_openers + import urllib2 + register_openers() + datagen, headers = multipart_encode({"minidump": f, + "symbols": symbolsPath}) + request = urllib2.Request(stackwalkCGI, datagen, headers) + result = urllib2.urlopen(request).read() + if len(result) > 3: + print result else: - print "stderr from minidump_stackwalk:" - print err - if p.returncode != 0: - print "minidump_stackwalk exited with return code %d" % p.returncode + print "stackwalkCGI returned nothing." + finally: + if f: + f.close() + else: + if not symbolsPath: + print "No symbols path given, can't process dump." + if not stackwalkPath and not stackwalkCGI: + print "Neither MINIDUMP_STACKWALK nor MINIDUMP_STACKWALK_CGI is set, can't process dump." else: - if not symbolsPath: - print "No symbols path given, can't process dump." - if not stackwalkPath: - print "MINIDUMP_STACKWALK not set, can't process dump." - elif stackwalkPath and not os.path.exists(stackwalkPath): + if stackwalkPath and not os.path.exists(stackwalkPath): print "MINIDUMP_STACKWALK binary not found: %s" % stackwalkPath - dumpSavePath = os.environ.get('MINIDUMP_SAVE_PATH', None) - if dumpSavePath: - shutil.move(d, dumpSavePath) - print "Saved dump as %s" % os.path.join(dumpSavePath, - os.path.basename(d)) - else: - os.remove(d) - extra = os.path.splitext(d)[0] + ".extra" - if os.path.exists(extra): - os.remove(extra) - foundCrash = True - finally: - if removeSymbolsPath: - shutil.rmtree(symbolsPath) + elif stackwalkCGI and not isURL(stackwalkCGI): + print "MINIDUMP_STACKWALK_CGI is not a URL: %s" % stackwalkCGI + elif symbolsPath and not isURL(symbolsPath): + print "symbolsPath is not a URL: %s" % symbolsPath + dumpSavePath = os.environ.get('MINIDUMP_SAVE_PATH', None) + if dumpSavePath: + shutil.move(d, dumpSavePath) + print "Saved dump as %s" % os.path.join(dumpSavePath, + os.path.basename(d)) + else: + os.remove(d) + extra = os.path.splitext(d)[0] + ".extra" + if os.path.exists(extra): + os.remove(extra) + foundCrash = True return foundCrash - + def getFullPath(directory, path): "Get an absolute path relative to 'directory'." return os.path.normpath(os.path.join(directory, os.path.expanduser(path))) diff --git a/layout/tools/reftest/Makefile.in b/layout/tools/reftest/Makefile.in index 94b0ddba4a26..7d589259262f 100644 --- a/layout/tools/reftest/Makefile.in +++ b/layout/tools/reftest/Makefile.in @@ -84,6 +84,7 @@ _HARNESS_FILES = \ $(topsrcdir)/build/mobile/devicemanagerADB.py \ $(topsrcdir)/build/mobile/devicemanagerSUT.py \ $(topsrcdir)/build/automationutils.py \ + $(topsrcdir)/build/poster.zip \ $(topsrcdir)/build/mobile/remoteautomation.py \ $(topsrcdir)/testing/mochitest/server.js \ $(topsrcdir)/build/pgo/server-locations.txt \ diff --git a/testing/mochitest/Makefile.in b/testing/mochitest/Makefile.in index 6e38bc9c4331..2a8eb7dd21a4 100644 --- a/testing/mochitest/Makefile.in +++ b/testing/mochitest/Makefile.in @@ -82,6 +82,7 @@ _SERV_FILES = \ $(topsrcdir)/build/mobile/devicemanagerADB.py \ $(topsrcdir)/build/mobile/devicemanagerSUT.py \ $(topsrcdir)/build/automationutils.py \ + $(topsrcdir)/build/poster.zip \ $(topsrcdir)/build/mobile/remoteautomation.py \ gen_template.pl \ server.js \ diff --git a/testing/xpcshell/Makefile.in b/testing/xpcshell/Makefile.in index d1a37405526e..8528c3dba1b6 100644 --- a/testing/xpcshell/Makefile.in +++ b/testing/xpcshell/Makefile.in @@ -65,6 +65,7 @@ EXTRA_BUILD_FILES := \ automationutils.py \ manifestparser.py \ mozinfo.py \ + poster.zip \ $(NULL) # And files for running xpcshell remotely from $(topsrcdir)/build/mobile From 9122ac3770f8b4c85a6e23617d88e7d6367ce4cc Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Mon, 7 Nov 2011 13:45:42 -0800 Subject: [PATCH 20/46] Bug 693940: Restrict SVG-as-an-image to load URIs with URI_INHERITS_SECURITY_CONTEXT. r=bz --- content/base/src/nsDataDocumentContentPolicy.cpp | 12 +++++++----- layout/reftests/svg/as-image/reftest.list | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/content/base/src/nsDataDocumentContentPolicy.cpp b/content/base/src/nsDataDocumentContentPolicy.cpp index b0c52ed97dca..478db8c5cbdf 100644 --- a/content/base/src/nsDataDocumentContentPolicy.cpp +++ b/content/base/src/nsDataDocumentContentPolicy.cpp @@ -87,12 +87,14 @@ nsDataDocumentContentPolicy::ShouldLoad(PRUint32 aContentType, } if (doc->IsBeingUsedAsImage()) { - // Allow local resources for SVG-as-an-image documents, but disallow - // everything else, to prevent data leakage + // Only allow SVG-as-an-image to load local resources that inherit security + // context (basically just data: URIs), to prevent data leakage. bool hasFlags; - nsresult rv = NS_URIChainHasFlags(aContentLocation, - nsIProtocolHandler::URI_IS_LOCAL_RESOURCE, - &hasFlags); + nsresult rv = + NS_URIChainHasFlags(aContentLocation, + nsIProtocolHandler::URI_IS_LOCAL_RESOURCE | + nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT, + &hasFlags); if (NS_FAILED(rv) || !hasFlags) { // resource is not local (or we couldn't tell) - reject! *aDecision = nsIContentPolicy::REJECT_TYPE; diff --git a/layout/reftests/svg/as-image/reftest.list b/layout/reftests/svg/as-image/reftest.list index 54fc3ad92665..ce49679607eb 100644 --- a/layout/reftests/svg/as-image/reftest.list +++ b/layout/reftests/svg/as-image/reftest.list @@ -109,11 +109,11 @@ random == img-and-image-1.html img-and-image-1-ref.svg # bug 645267 # tests for external resources vs. data URIs in SVG as an image == svg-image-datauri-1.html lime100x100.svg HTTP == svg-image-datauri-1.html lime100x100.svg -fails-if(Android) == svg-image-external-1.html lime100x100.svg +== svg-image-external-1.html blue100x100.svg HTTP == svg-image-external-1.html blue100x100.svg == svg-stylesheet-datauri-1.html lime100x100.svg HTTP == svg-stylesheet-datauri-1.html lime100x100.svg -random == svg-stylesheet-external-1.html lime100x100.svg # see bug 629885 comment 9 +== svg-stylesheet-external-1.html blue100x100.svg HTTP == svg-stylesheet-external-1.html blue100x100.svg # test that :visited status is ignored in image documents From c94e26acf75e719bc623c0732e69fb35e078d5e1 Mon Sep 17 00:00:00 2001 From: Doug Sherk Date: Mon, 7 Nov 2011 17:13:34 -0500 Subject: [PATCH 21/46] Bug 665578: prevented ANGLE built-in function emulation on OS X 10.7 and newer r=bjacob --- content/canvas/src/WebGLContextGL.cpp | 28 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index c33dbb6550c2..11870f0d3418 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -64,6 +64,11 @@ #include "WebGLTexelConversions.h" #include "WebGLValidateStrings.h" +// needed to check if current OS is lower than 10.7 +#if defined(MOZ_WIDGET_COCOA) +#include +#endif + using namespace mozilla; static bool BaseTypeAndSizeFromUniformType(WebGLenum uType, WebGLenum *baseType, WebGLint *unitSize); @@ -4417,12 +4422,6 @@ WebGLContext::Viewport(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei heig return NS_OK; } -#ifdef XP_MACOSX -#define WEBGL_OS_IS_MAC 1 -#else -#define WEBGL_OS_IS_MAC 0 -#endif - NS_IMETHODIMP WebGLContext::CompileShader(nsIWebGLShader *sobj) { @@ -4477,12 +4476,23 @@ WebGLContext::CompileShader(nsIWebGLShader *sobj) return ErrorInvalidValue("compileShader: source has more than %d characters", maxSourceLength); const char *s = sourceCString.get(); - + int compileOptions = SH_OBJECT_CODE; - + +#ifdef XP_MACOSX // work around bug 665578 - if (WEBGL_OS_IS_MAC && gl->Vendor() == gl::GLContext::VendorATI) + PRInt32 version = 0; + OSErr err = ::Gestalt(gestaltSystemVersion, &version); + if (err != noErr) { + version = 0; + } else { + version &= 0xFFFF; // The system version is in the lower word. + } + + // If earlier than Snow Leopard and has an ATI card. + if (version < 0x1070 && gl->Vendor() == gl::GLContext::VendorATI) compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS; +#endif if (!ShCompile(compiler, &s, 1, compileOptions)) { int len = 0; From 555ebb514c6c492503046cb6bd525706ef03cb55 Mon Sep 17 00:00:00 2001 From: Benoit Jacob Date: Mon, 7 Nov 2011 17:32:07 -0500 Subject: [PATCH 22/46] Bug 665578 - Backed out changeset 82a297b0d0d3 for compile error --- content/canvas/src/WebGLContextGL.cpp | 28 +++++++++------------------ 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index 11870f0d3418..c33dbb6550c2 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -64,11 +64,6 @@ #include "WebGLTexelConversions.h" #include "WebGLValidateStrings.h" -// needed to check if current OS is lower than 10.7 -#if defined(MOZ_WIDGET_COCOA) -#include -#endif - using namespace mozilla; static bool BaseTypeAndSizeFromUniformType(WebGLenum uType, WebGLenum *baseType, WebGLint *unitSize); @@ -4422,6 +4417,12 @@ WebGLContext::Viewport(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei heig return NS_OK; } +#ifdef XP_MACOSX +#define WEBGL_OS_IS_MAC 1 +#else +#define WEBGL_OS_IS_MAC 0 +#endif + NS_IMETHODIMP WebGLContext::CompileShader(nsIWebGLShader *sobj) { @@ -4476,23 +4477,12 @@ WebGLContext::CompileShader(nsIWebGLShader *sobj) return ErrorInvalidValue("compileShader: source has more than %d characters", maxSourceLength); const char *s = sourceCString.get(); - + int compileOptions = SH_OBJECT_CODE; - -#ifdef XP_MACOSX + // work around bug 665578 - PRInt32 version = 0; - OSErr err = ::Gestalt(gestaltSystemVersion, &version); - if (err != noErr) { - version = 0; - } else { - version &= 0xFFFF; // The system version is in the lower word. - } - - // If earlier than Snow Leopard and has an ATI card. - if (version < 0x1070 && gl->Vendor() == gl::GLContext::VendorATI) + if (WEBGL_OS_IS_MAC && gl->Vendor() == gl::GLContext::VendorATI) compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS; -#endif if (!ShCompile(compiler, &s, 1, compileOptions)) { int len = 0; From da596112ca1582ebe0b1074ddbc5d1e1648f0354 Mon Sep 17 00:00:00 2001 From: Taras Glek Date: Mon, 7 Nov 2011 14:54:16 -0800 Subject: [PATCH 23/46] Bug 699942: Telemetry Yes->Yes, I want to help r=geekboy --- browser/locales/en-US/chrome/browser/browser.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties index a282acb0ecf5..380e3c3feb72 100644 --- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -331,7 +331,7 @@ syncPromoNotification.learnMoreLinkText=Learn More # and %2$S by the value of the toolkit.telemetry.server_owner preference. telemetryPrompt = Will you help improve %1$S by sending anonymous information about performance, hardware characteristics, feature usage, and browser customizations to %2$S? telemetryLinkLabel = Learn More -telemetryYesButtonLabel = Yes +telemetryYesButtonLabel2 = Yes, I want to help telemetryYesButtonAccessKey = Y telemetryNoButtonLabel = No telemetryNoButtonAccessKey = N From 4dd5d6ba50282bc8790eab9828fc1633c01b60ca Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Mon, 7 Nov 2011 15:03:53 -0800 Subject: [PATCH 24/46] Bug 671634 - Update Fennec useragent to more closely match stock browser. r=dougt --- embedding/android/GeckoAppShell.java | 13 +++++++++ netwerk/protocol/http/nsHttpHandler.cpp | 37 +++++++++++++++++++++++++ netwerk/protocol/http/nsHttpHandler.h | 2 ++ widget/src/android/AndroidBridge.cpp | 7 +++++ widget/src/android/AndroidBridge.h | 3 ++ xpcom/base/nsSystemInfo.cpp | 16 +++++------ 6 files changed, 69 insertions(+), 9 deletions(-) diff --git a/embedding/android/GeckoAppShell.java b/embedding/android/GeckoAppShell.java index 78c88ad52d23..0040b1e11f82 100644 --- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -1615,4 +1615,17 @@ public class GeckoAppShell public static float[] getCurrentBatteryInformation() { return GeckoBatteryManager.getCurrentInformation(); } + + public static boolean isTablet() { + if (android.os.Build.VERSION.SDK_INT >= 9) { + Configuration config = GeckoApp.mAppContext.getResources().getConfiguration(); + // xlarge is defined by android as screens larger than 960dp x 720dp + // and should include most devices ~7in and up. + // http://developer.android.com/guide/practices/screens_support.html + if ((config.screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK) >= Configuration.SCREENLAYOUT_SIZE_XLARGE) { + return true; + } + } + return false; + } } diff --git a/netwerk/protocol/http/nsHttpHandler.cpp b/netwerk/protocol/http/nsHttpHandler.cpp index f21598e03140..652faa3d59e2 100644 --- a/netwerk/protocol/http/nsHttpHandler.cpp +++ b/netwerk/protocol/http/nsHttpHandler.cpp @@ -615,6 +615,8 @@ nsHttpHandler::BuildUserAgent() mAppName.Length() + mAppVersion.Length() + mCompatFirefox.Length() + + mDeviceType.Length() + + mDeviceName.Length() + 13); // Application portion @@ -632,6 +634,10 @@ nsHttpHandler::BuildUserAgent() mUserAgent += mOscpu; mUserAgent.AppendLiteral("; "); mUserAgent += mMisc; + if (!mDeviceName.IsEmpty()) { + mUserAgent.AppendLiteral("; "); + mUserAgent += mDeviceName; + } mUserAgent += ')'; // Product portion @@ -646,6 +652,11 @@ nsHttpHandler::BuildUserAgent() mUserAgent += mCompatFirefox; } + if (!mDeviceType.IsEmpty()) { + mUserAgent += ' '; + mUserAgent += mDeviceType; + } + // App portion mUserAgent += ' '; mUserAgent += mAppName; @@ -683,6 +694,32 @@ nsHttpHandler::InitUserAgentComponents() #endif ); +#if defined(ANDROID) + nsCOMPtr infoService = do_GetService("@mozilla.org/system-info;1"); + NS_ASSERTION(infoService, "Could not find a system info service"); + + bool isTablet; + infoService->GetPropertyAsBool(NS_LITERAL_STRING("isTablet"), &isTablet); + if (!isTablet) { + mDeviceType.AssignLiteral("Mobile"); + } + infoService->GetPropertyAsACString(NS_LITERAL_STRING("device"), + mDeviceName); + nsXPIDLCString buildid; + infoService->GetPropertyAsACString(NS_LITERAL_STRING("buildid"), + buildid); + if (!buildid.IsEmpty()) { + mDeviceName += " Build/"; + mDeviceName += buildid; + } + + nsXPIDLCString shellVersion; + infoService->GetPropertyAsACString(NS_LITERAL_STRING("shellVersion"), + shellVersion); + mPlatform += " "; + mPlatform += shellVersion; +#endif + // Gather OS/CPU. #if defined(XP_OS2) ULONG os2ver = 0; diff --git a/netwerk/protocol/http/nsHttpHandler.h b/netwerk/protocol/http/nsHttpHandler.h index 4eb0abae246a..f9e84bd226c4 100644 --- a/netwerk/protocol/http/nsHttpHandler.h +++ b/netwerk/protocol/http/nsHttpHandler.h @@ -313,6 +313,8 @@ private: nsXPIDLCString mProductSub; nsXPIDLCString mAppName; nsXPIDLCString mAppVersion; + nsXPIDLCString mDeviceType; + nsXPIDLCString mDeviceName; nsCString mCompatFirefox; nsCString mUserAgent; diff --git a/widget/src/android/AndroidBridge.cpp b/widget/src/android/AndroidBridge.cpp index 4dc683518929..588876a85a9a 100644 --- a/widget/src/android/AndroidBridge.cpp +++ b/widget/src/android/AndroidBridge.cpp @@ -154,6 +154,7 @@ AndroidBridge::Init(JNIEnv *jEnv, jPostToJavaThread = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "postToJavaThread", "(Z)V"); jInitCamera = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "initCamera", "(Ljava/lang/String;III)[I"); jCloseCamera = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "closeCamera", "()V"); + jIsTablet = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "isTablet", "()Z"); jEnableBatteryNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "enableBatteryNotifications", "()V"); jDisableBatteryNotifications = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "disableBatteryNotifications", "()V"); jGetCurrentBatteryInformation = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "getCurrentBatteryInformation", "()[F"); @@ -1317,3 +1318,9 @@ AndroidBridge::UnlockWindow(void* window) return true; } + +bool +AndroidBridge::IsTablet() +{ + return mJNIEnv->CallStaticBooleanMethod(mGeckoAppShellClass, jIsTablet); +} diff --git a/widget/src/android/AndroidBridge.h b/widget/src/android/AndroidBridge.h index d5c95b5533e0..512357019914 100644 --- a/widget/src/android/AndroidBridge.h +++ b/widget/src/android/AndroidBridge.h @@ -295,6 +295,8 @@ public: void DisableBatteryNotifications(); void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo); + bool IsTablet(); + protected: static AndroidBridge *sBridge; @@ -366,6 +368,7 @@ protected: jmethodID jPostToJavaThread; jmethodID jInitCamera; jmethodID jCloseCamera; + jmethodID jIsTablet; jmethodID jEnableBatteryNotifications; jmethodID jDisableBatteryNotifications; jmethodID jGetCurrentBatteryInformation; diff --git a/xpcom/base/nsSystemInfo.cpp b/xpcom/base/nsSystemInfo.cpp index 7dedf9d6dc88..002eea0727aa 100644 --- a/xpcom/base/nsSystemInfo.cpp +++ b/xpcom/base/nsSystemInfo.cpp @@ -181,22 +181,20 @@ nsSystemInfo::Init() SetPropertyAsAString(NS_LITERAL_STRING("device"), str); if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "MANUFACTURER", str)) SetPropertyAsAString(NS_LITERAL_STRING("manufacturer"), str); + PRInt32 version; if (!mozilla::AndroidBridge::Bridge()->GetStaticIntField("android/os/Build$VERSION", "SDK_INT", &version)) version = 0; if (version >= 8 && mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "HARDWARE", str)) SetPropertyAsAString(NS_LITERAL_STRING("hardware"), str); + if (version >= 8 && mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build", "ID", str)) + SetPropertyAsAString(NS_LITERAL_STRING("buildid"), str); + SetPropertyAsAString(NS_LITERAL_STRING("shellName"), NS_LITERAL_STRING("Android")); - if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build$VERSION", "CODENAME", str)) { - if (version) { - str.Append(NS_LITERAL_STRING(" (")); - str.AppendInt(version); - str.Append(NS_LITERAL_STRING(")")); - } + if (mozilla::AndroidBridge::Bridge()->GetStaticStringField("android/os/Build$VERSION", "RELEASE", str)) SetPropertyAsAString(NS_LITERAL_STRING("shellVersion"), str); - } - - + SetPropertyAsBool(NS_LITERAL_STRING("isTablet"), + mozilla::AndroidBridge::Bridge()->IsTablet()); } #endif return NS_OK; From 6b269a80424cf2b8c739d37bd58d253853c170d7 Mon Sep 17 00:00:00 2001 From: Taras Glek Date: Mon, 7 Nov 2011 15:06:35 -0800 Subject: [PATCH 25/46] Bug 699942: backout incomplete string patch r=gavin --- browser/locales/en-US/chrome/browser/browser.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties index 380e3c3feb72..a282acb0ecf5 100644 --- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -331,7 +331,7 @@ syncPromoNotification.learnMoreLinkText=Learn More # and %2$S by the value of the toolkit.telemetry.server_owner preference. telemetryPrompt = Will you help improve %1$S by sending anonymous information about performance, hardware characteristics, feature usage, and browser customizations to %2$S? telemetryLinkLabel = Learn More -telemetryYesButtonLabel2 = Yes, I want to help +telemetryYesButtonLabel = Yes telemetryYesButtonAccessKey = Y telemetryNoButtonLabel = No telemetryNoButtonAccessKey = N From b0845302f7cc0a5de50409dfcbdd2cfe9bdbdbaf Mon Sep 17 00:00:00 2001 From: Taras Glek Date: Fri, 4 Nov 2011 15:19:45 -0700 Subject: [PATCH 26/46] Bug 699942: Telemetry Yes->Yes, I want to help r=geekboy --- browser/components/nsBrowserGlue.js | 2 +- browser/locales/en-US/chrome/browser/browser.properties | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/components/nsBrowserGlue.js b/browser/components/nsBrowserGlue.js index 6c68fb4c4f15..1f420cbfac54 100644 --- a/browser/components/nsBrowserGlue.js +++ b/browser/components/nsBrowserGlue.js @@ -793,7 +793,7 @@ BrowserGlue.prototype = { var buttons = [ { - label: browserBundle.GetStringFromName("telemetryYesButtonLabel"), + label: browserBundle.GetStringFromName("telemetryYesButtonLabel2"), accessKey: browserBundle.GetStringFromName("telemetryYesButtonAccessKey"), popup: null, callback: function(aNotificationBar, aButton) { diff --git a/browser/locales/en-US/chrome/browser/browser.properties b/browser/locales/en-US/chrome/browser/browser.properties index a282acb0ecf5..380e3c3feb72 100644 --- a/browser/locales/en-US/chrome/browser/browser.properties +++ b/browser/locales/en-US/chrome/browser/browser.properties @@ -331,7 +331,7 @@ syncPromoNotification.learnMoreLinkText=Learn More # and %2$S by the value of the toolkit.telemetry.server_owner preference. telemetryPrompt = Will you help improve %1$S by sending anonymous information about performance, hardware characteristics, feature usage, and browser customizations to %2$S? telemetryLinkLabel = Learn More -telemetryYesButtonLabel = Yes +telemetryYesButtonLabel2 = Yes, I want to help telemetryYesButtonAccessKey = Y telemetryNoButtonLabel = No telemetryNoButtonAccessKey = N From 2a2035a471cc5d36cbd7cbf7403d742aa8c0af3c Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Tue, 8 Nov 2011 00:29:38 +0100 Subject: [PATCH 27/46] Bug 700417 - Further reduce cache_size in Places. r=dietrich --- toolkit/components/places/Database.cpp | 72 ++++--------------- .../components/places/nsPlacesExpiration.js | 46 ++++++++++-- 2 files changed, 52 insertions(+), 66 deletions(-) diff --git a/toolkit/components/places/Database.cpp b/toolkit/components/places/Database.cpp index dc4252881cf3..9b8b4bc3f65a 100644 --- a/toolkit/components/places/Database.cpp +++ b/toolkit/components/places/Database.cpp @@ -80,29 +80,16 @@ // Set when the database file was found corrupt by a previous maintenance. #define PREF_FORCE_DATABASE_REPLACEMENT "places.database.replaceOnStartup" -// Set to the calculated optimal size of the database. This is a target size -// used to evaluate history limits. It's not mandatory. -#define PREF_OPTIMAL_DATABASE_SIZE "places.history.expiration.transient_optimal_database_size" -// To calculate the cache size we take into account the available physical -// memory and the current database size. This is the percentage of memory -// we reserve for the former case. -#define DATABASE_CACHE_TO_MEMORY_PERC 2 +// The wanted size of the cache. This is calculated based on current database +// size and clamped to the limits specified below. +#define DATABASE_CACHE_TO_DATABASE_PERC 10 // The minimum size of the cache. We should never work without a cache, since // that would badly hurt WAL journaling mode. -#define DATABASE_CACHE_MIN_BYTES (PRUint64)5242880 // 5MiB -// We calculate an optimal database size, based on hardware specs. This -// pertains more to expiration, but the code is pretty much the same used for -// cache_size, so it's here to reduce code duplication. -// This percentage of disk size is used to protect against calculating a too -// large size on disks with tiny quota or available space. -#define DATABASE_TO_DISK_PERC 2 -// Maximum size of the optimal database. High-end hardware has plenty of -// memory and disk space, but performances don't grow linearly. -#define DATABASE_MAX_SIZE (PRInt64)167772160 // 160MiB -// If the physical memory size is not available, use MEMSIZE_FALLBACK_BYTES -// instead. Must stay in sync with the code in nsPlacesExpiration.js. -#define MEMSIZE_FALLBACK_BYTES 268435456 // 256 M +#define DATABASE_CACHE_MIN_BYTES (PRUint64)4194304 // 4MiB +// The maximum size of the cache. This is the maximum memory that each +// connection may use. +#define DATABASE_CACHE_MAX_BYTES (PRUint64)8388608 // 8MiB // Maximum size for the WAL file. It should be small enough since in case of // crashes we could lose all the transactions in the file. But a too small @@ -498,42 +485,6 @@ Database::InitSchema(bool* aDatabaseMigrated) "PRAGMA temp_store = MEMORY")); NS_ENSURE_SUCCESS(rv, rv); - // We want to work with a cache that is at a maximum half of the database - // size. We also want it to respect the available memory size. - - // Calculate memory size, fallback to a meaningful value if it fails. - PRUint64 memSizeBytes = PR_GetPhysicalMemorySize(); - if (memSizeBytes == 0) { - memSizeBytes = MEMSIZE_FALLBACK_BYTES; - } - - PRUint64 cacheSize = memSizeBytes * DATABASE_CACHE_TO_MEMORY_PERC / 100; - - // Calculate an optimal database size for expiration purposes. - // We usually want to work with a cache that is half the database size. - // Limit the size to avoid extreme values on high-end hardware. - PRInt64 optimalDatabaseSize = NS_MIN(static_cast(cacheSize) * 2, - DATABASE_MAX_SIZE); - - // Protect against a full disk or tiny quota. - PRInt64 diskAvailableBytes = 0; - nsCOMPtr databaseFile; - mMainConn->GetDatabaseFile(getter_AddRefs(databaseFile)); - NS_ENSURE_STATE(databaseFile); - nsCOMPtr localFile = do_QueryInterface(databaseFile); - if (localFile && - NS_SUCCEEDED(localFile->GetDiskSpaceAvailable(&diskAvailableBytes)) && - diskAvailableBytes > 0) { - optimalDatabaseSize = NS_MIN(optimalDatabaseSize, - diskAvailableBytes * DATABASE_TO_DISK_PERC / 100); - } - - // Share the calculated size if it's meaningful. - if (optimalDatabaseSize < PR_INT32_MAX) { - (void)Preferences::SetInt(PREF_OPTIMAL_DATABASE_SIZE, - static_cast(optimalDatabaseSize)); - } - // Get the current database size. Due to chunked growth we have to use // page_count to evaluate it. PRUint64 databaseSizeBytes = 0; @@ -552,10 +503,11 @@ Database::InitSchema(bool* aDatabaseMigrated) databaseSizeBytes = pageCount * mDBPageSize; } - // Set cache to a maximum of half the database size. - cacheSize = NS_MIN(cacheSize, databaseSizeBytes / 2); - // Ensure we never work without a minimum cache. - cacheSize = NS_MAX(cacheSize, DATABASE_CACHE_MIN_BYTES); + // Clamp the cache size to a percentage of the database size, forcing + // meaningful limits. + PRInt64 cacheSize = clamped(databaseSizeBytes * DATABASE_CACHE_TO_DATABASE_PERC / 100, + DATABASE_CACHE_MIN_BYTES, + DATABASE_CACHE_MAX_BYTES); // Set the number of cached pages. // We don't use PRAGMA default_cache_size, since the database could be moved diff --git a/toolkit/components/places/nsPlacesExpiration.js b/toolkit/components/places/nsPlacesExpiration.js index 11cecc91474d..00e431946b45 100644 --- a/toolkit/components/places/nsPlacesExpiration.js +++ b/toolkit/components/places/nsPlacesExpiration.js @@ -110,10 +110,20 @@ const PREF_READONLY_CALCULATED_MAX_URIS = "transient_current_max_pages"; const PREF_INTERVAL_SECONDS = "interval_seconds"; const PREF_INTERVAL_SECONDS_NOTSET = 3 * 60; -// An optimal database size calculated by history. Used to evaluate a limit -// to the number of pages we may retain before hitting performance issues. -const PREF_OPTIMAL_DATABASE_SIZE = "transient_optimal_database_size"; -const PREF_OPTIMAL_DATABASE_SIZE_NOTSET = 167772160; // 160MiB +// We calculate an optimal database size, based on hardware specs. +// This percentage of memory size is used to protect against calculating a too +// large database size on systems with small memory. +const DATABASE_TO_MEMORY_PERC = 4; +// This percentage of disk size is used to protect against calculating a too +// large database size on disks with tiny quota or available space. +const DATABASE_TO_DISK_PERC = 2; +// Maximum size of the optimal database. High-end hardware has plenty of +// memory and disk space, but performances don't grow linearly. +const DATABASE_MAX_SIZE = 167772160; // 160MiB +// If the physical memory size is bogus, fallback to this. +const MEMSIZE_FALLBACK_BYTES = 268435456; // 256 MiB +// If the disk available space is bogus, fallback to this. +const DISKSIZE_FALLBACK_BYTES = 268435456; // 256 MiB // Max number of entries to expire at each expiration step. // This value is globally used for different kind of data we expire, can be @@ -782,12 +792,36 @@ nsPlacesExpiration.prototype = { // Calculate the number of unique places that may fit an optimal database // size on this hardware. If there are more than these unique pages, // some will be expired. - let optimalDatabaseSize = PREF_OPTIMAL_DATABASE_SIZE_NOTSET; + + let memSizeBytes = MEMSIZE_FALLBACK_BYTES; try { - optimalDatabaseSize = this._prefBranch.getIntPref(PREF_OPTIMAL_DATABASE_SIZE); + // Limit the size on systems with small memory. + memSizeBytes = this._sys.getProperty("memsize"); } catch (ex) {} + if (memSizeBytes <= 0) { + memsize = MEMSIZE_FALLBACK_BYTES; + } + + let diskAvailableBytes = DISKSIZE_FALLBACK_BYTES; + try { + // Protect against a full disk or tiny quota. + let dbFile = this._db.databaseFile; + dbFile.QueryInterface(Ci.nsILocalFile); + diskAvailableBytes = dbFile.diskSpaceAvailable; + } catch (ex) {} + if (diskAvailableBytes <= 0) { + diskAvailableBytes = DISKSIZE_FALLBACK_BYTES; + } + + let optimalDatabaseSize = Math.min( + memSizeBytes * DATABASE_TO_MEMORY_PERC / 100, + diskAvailableBytes * DATABASE_TO_DISK_PERC / 100, + DATABASE_MAX_SIZE + ); + this._urisLimit = Math.ceil(optimalDatabaseSize / URIENTRY_AVG_SIZE); } + // Expose the calculated limit to other components. this._prefBranch.setIntPref(PREF_READONLY_CALCULATED_MAX_URIS, this._urisLimit); From 8dab6dd693dd5e72ce9e234a5bfe88c0515bed95 Mon Sep 17 00:00:00 2001 From: Marco Bonardo Date: Tue, 8 Nov 2011 00:29:40 +0100 Subject: [PATCH 28/46] Bug 700314 - Remove nsNavHistory nsICharsetResolver implementation. r=bz --- toolkit/components/places/nsNavHistory.cpp | 42 ---------------------- toolkit/components/places/nsNavHistory.h | 4 --- 2 files changed, 46 deletions(-) diff --git a/toolkit/components/places/nsNavHistory.cpp b/toolkit/components/places/nsNavHistory.cpp index 5e80a6772773..6b80f362c0d5 100644 --- a/toolkit/components/places/nsNavHistory.cpp +++ b/toolkit/components/places/nsNavHistory.cpp @@ -222,7 +222,6 @@ NS_INTERFACE_MAP_BEGIN(nsNavHistory) NS_INTERFACE_MAP_ENTRY(nsIBrowserHistory) NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference) - NS_INTERFACE_MAP_ENTRY(nsICharsetResolver) NS_INTERFACE_MAP_ENTRY(nsPIPlacesDatabase) NS_INTERFACE_MAP_ENTRY(nsPIPlacesHistoryListenersNotifier) NS_INTERFACE_MAP_ENTRY(mozIStorageVacuumParticipant) @@ -5599,44 +5598,3 @@ nsNavHistory::GetDateFormatBundle() } return mDateFormatBundle; } - -//////////////////////////////////////////////////////////////////////////////// -//// nsICharsetResolver - -NS_IMETHODIMP -nsNavHistory::RequestCharset(nsIWebNavigation* aWebNavigation, - nsIChannel* aChannel, - bool* aWantCharset, - nsISupports** aClosure, - nsACString& aResult) -{ - NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread"); - NS_ENSURE_ARG(aChannel); - NS_ENSURE_ARG_POINTER(aWantCharset); - NS_ENSURE_ARG_POINTER(aClosure); - - *aWantCharset = false; - *aClosure = nsnull; - - nsCOMPtr uri; - nsresult rv = aChannel->GetURI(getter_AddRefs(uri)); - if (NS_FAILED(rv)) - return NS_OK; - - nsAutoString charset; - rv = GetCharsetForURI(uri, charset); - NS_ENSURE_SUCCESS(rv, rv); - - CopyUTF16toUTF8(charset, aResult); - return NS_OK; -} - -NS_IMETHODIMP -nsNavHistory::NotifyResolvedCharset(const nsACString& aCharset, - nsISupports* aClosure) -{ - NS_ASSERTION(NS_IsMainThread(), "This can only be called on the main thread"); - - NS_ERROR("Unexpected call to NotifyResolvedCharset -- we never set aWantCharset to true!"); - return NS_ERROR_NOT_IMPLEMENTED; -} diff --git a/toolkit/components/places/nsNavHistory.h b/toolkit/components/places/nsNavHistory.h index 5dfba3c29e76..bd9f9cefc83d 100644 --- a/toolkit/components/places/nsNavHistory.h +++ b/toolkit/components/places/nsNavHistory.h @@ -56,7 +56,6 @@ #include "nsICollation.h" #include "nsIStringBundle.h" #include "nsITimer.h" -#include "nsICharsetResolver.h" #include "nsMaybeWeakPtr.h" #include "nsCategoryCache.h" #include "nsNetCID.h" @@ -111,7 +110,6 @@ class nsNavHistory : public nsSupportsWeakReference , public nsIObserver , public nsIBrowserHistory , public nsIDownloadHistory - , public nsICharsetResolver , public nsPIPlacesDatabase , public nsPIPlacesHistoryListenersNotifier , public mozIStorageVacuumParticipant @@ -501,8 +499,6 @@ protected: */ nsresult DecayFrecency(); - NS_DECL_NSICHARSETRESOLVER - nsresult CalculateFrecency(PRInt64 aPageID, PRInt32 aTyped, PRInt32 aVisitCount, nsCAutoString &aURL, PRInt32 *aFrecency); nsresult CalculateFrecencyInternal(PRInt64 aPageID, PRInt32 aTyped, PRInt32 aVisitCount, bool aIsBookmarked, PRInt32 *aFrecency); From 033bc59a4b9f2d4bf756d1413622aa36e9bd4eca Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Mon, 7 Nov 2011 18:48:49 -0500 Subject: [PATCH 29/46] Bug 531590 - Increase the default validity of the self-signed certificate that we use for our tests to 10 years by default; r=mayhemer This is a dump of the new certificate information obtained by running `certutil -L -d . -n 'pgo server certificate'`: Certificate: Data: Version: 3 (0x2) Serial Number: 2 (0x2) Signature Algorithm: PKCS #1 SHA-1 With RSA Encryption Issuer: "CN=Temporary Certificate Authority,O=Mozilla Testing,OU=Prof ile Guided Optimization" Validity: Not Before: Mon Nov 07 20:38:29 2011 Not After : Sun Nov 07 20:38:29 2021 Subject: "CN=example.com" Subject Public Key Info: Public Key Algorithm: PKCS #1 RSA Encryption RSA Public Key: Modulus: d8:43:79:cf:52:ce:49:08:47:9c:57:9b:f8:0b:de:7a: ca:ba:1c:88:9f:fd:d8:0b:df:a8:98:92:22:46:08:3e: d2:25:4c:09:c2:32:3f:51:f9:79:60:b6:ac:94:0e:7a: 33:13:e7:0b:f7:97:72:3b:37:8f:d4:e5:ea:0c:e2:9e: 4a:5b:28:1d:8c:eb:a1:b4:96:47:37:bf:fc:f0:87:d3: ca:13:7e:38:45:f5:3f:75:1d:45:0d:72:36:b3:cf:57: 13:99:cd:6d:3c:b8:e9:9c:ec:af:2e:2c:25:3a:d5:13: 7e:6f:51:63:2a:eb:e1:4f:ee:68:42:63:c2:f4:e1:a3 Exponent: 65537 (0x10001) Signed Extensions: Name: Certificate Subject Alt Name DNS name: "example.com" DNS name: "test1.example.com" DNS name: "test2.example.com" DNS name: "sub1.test1.example.com" DNS name: "sub1.test2.example.com" DNS name: "sub2.test1.example.com" DNS name: "sub2.test2.example.com" DNS name: "requestclientcert.example.com" DNS name: "requireclientcert.example.com" DNS name: "xn--hxajbheg2az3al.xn--jxalpdlp" DNS name: "sub1.xn--hxajbheg2az3al.xn--jxalpdlp" DNS name: "sectest1.example.org" DNS name: "sub.sectest2.example.org" DNS name: "sectest2.example.org" DNS name: "sub.sectest1.example.org" DNS name: "redirproxy.example.com" Signature Algorithm: PKCS #1 SHA-1 With RSA Encryption Signature: a2:f1:08:1c:de:74:27:95:34:a0:1a:6c:9c:fe:8f:7f: 45:38:af:1f:bb:04:b6:e5:f8:e4:35:bf:ce:23:53:74: ca:89:26:6b:22:d7:f3:f7:66:d4:f1:8b:95:7d:c4:27: 44:57:10:f3:3d:ea:bb:0c:88:d2:09:67:e3:d1:47:6c: 2c:2b:6d:78:41:ab:7e:64:59:e3:df:05:fa:65:72:c9: fd:5b:f6:0e:39:7d:02:31:99:5b:fb:29:17:9a:c9:0e: 64:4d:8c:cd:bf:6e:d0:9e:b0:68:a6:d9:ee:a0:16:ec: 50:dc:58:7e:7b:82:3e:ce:98:a6:20:4d:a6:bd:ad:05 Fingerprint (MD5): CC:F2:AD:07:F9:B8:A5:3B:A6:BB:75:80:4E:E6:BB:08 Fingerprint (SHA1): 2D:E7:9A:AE:80:CB:FD:51:B1:23:E0:CF:6F:6B:51:19:E5:28:BB:95 Certificate Trust Flags: SSL Flags: Terminal Record Trusted User Email Flags: User Object Signing Flags: User --- build/pgo/certs/cert8.db | Bin 65536 -> 65536 bytes build/pgo/certs/key3.db | Bin 61440 -> 61440 bytes build/pgo/genpgocert.py.in | 2 +- 3 files changed, 1 insertion(+), 1 deletion(-) diff --git a/build/pgo/certs/cert8.db b/build/pgo/certs/cert8.db index 0fe4acb0462274872abe0dfbf54e8feb7cd7a79c..6e2c29365d412863dd306008cbd15e6bcd47ce79 100644 GIT binary patch delta 401 zcmV;C0dD?)fCPYm1h6oG1|%=;E+j9p(e5sjJg*81p#T>!f&-l}f&&JVQLl&_F)=YP zH!?6YI5Ig}7Y#BoF)=VVGB7hZGC5k4Z*mxu46q%4*h6{GQqD;TN1Rui_zT{8%DNng zpZ(Yi->8_9B1Q;4(j`m@!ZJTm`FUWrtdtIVGZW_v_m^@zH;>fi>I~wZN?Rx$jO(Ga zmPa?g{P2g<$`gJ#MfE>*9YqatHnY!H6Pe9zJhW zc`jvmVQp}1Ws_037k{Gh2pryYCzUjy8f={YkAFotuOGVvw&nQbHNVayQ*_FSCTk+s z^Y>=d@r#vx#3w{o5c56iy9|iZ31{QcM{F!BZFoVeeq>qW-v#<*a>@N$_6|9H0x_9e v`zaTi$qr;qjLpAp(4MeprrGYG7VJ>mSblqgKF*k?AWf#dtpNqI(y}g1iN~*6 delta 386 zcmV-|0e${}fCPYm1h6oG20SkYFFY@?(FQLS2mt^9Lj*tsKm&;Y7chbYg)o8w?vpvM zh#N35F)}$ZF*PtSGFlf6F)=YQGC46ZH83zTT9a>b7?T-#9e=gwjZ@iG;!fG6)h}iV zJW&v#A9Y!59@6S;}h0T$I=Pq>xhhYE@?bynGb% zOE6{$Py=NfZXfjm0|5X5qk;jAFoFSzFoFSv1_M)=z-l-DP7~_rKv+p3}@LineKLZoM8n%as9WT z3?$b2%+i>scB~Kwo*GSv0y0CQ$@E}ZF#QyqsmQTA&4)U=NaXo=6?0InW@O-Jg?T2Y g_KGp-RZvbhf^SfkAI0= z9UUlgI_rMPwa$1V0TwpCtX8^JCu!J2k$knMWH9z#_;Y@z`THt!cS|C8-Cs$)A`i}? zkrDST4~vU~C%CStTpB}X5kcShBZ;@K6RiH{)ADMlUo;Ghy=0%(*B@Y)^2quGXJLU}!Hiq=JwpYFAD1ZIxCs00l zflp$UwBtpNOJYX8S0Ec<_5`ZE{A8S;vffr~NFqY6<%7Xl2dv%)2LY6|6OFLc^LEa> z2&TrS~Sx#`g`OA(z%pfZ5yd75lSVfF`~-u~c=sYlDnG zVpUzj(V(#Jgq4P@(?Jl^HF30ih8CJ6N%sws&CvAiH zdrK6!Z|e{Kziu%l^t3Xngu%qvoT%xhZYeXIpJB`!x}))=*psxShm94W)w(2yq0W{({2#W}@<$pImQIJvR@KzwZ zA)&wz`nrQpPV`r{W^Byi!A)r5&XL$ddCyYLNeD-rSDW|?-g?Tq9EhL&*bCpNn35t! z2tLv!ObNm=KT-L4V79E34tg^a=L`3jayvJV)aB|7;+{%dC>@OJp|qAqH^2PwhttXv zemF(-KXn~N4RSWK&mmV6nayoHxapkiuP!VlI@J??Z&70^>)}uCXhLJc^x>nEKUN#F Hc@~EW=HsDA delta 47 zcmZp8z})bFd4e^g#zvbX_l4LP8u(A~c=1f(6yc0x^I?l)+stU-!!}tXS$cD&SUV>G Dc0CR= diff --git a/build/pgo/genpgocert.py.in b/build/pgo/genpgocert.py.in index 872b6439ea95..0449c3118d95 100644 --- a/build/pgo/genpgocert.py.in +++ b/build/pgo/genpgocert.py.in @@ -187,7 +187,7 @@ def createSSLServerCertificate(profileDir, srcDir): runUtil(certutil, ["-D", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfilePath, "-f", pwfilePath]) # Ignore the result, the certificate may not be present when new database is being built - status = runUtil(certutil, ["-S", "-s", "CN=%s" % firstLocation, "-t", "Pu,,", "-c", "pgo temporary ca", "-m", "2", "-8", locationsParam, "-v", "12", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfilePath, "-f", pwfilePath]) + status = runUtil(certutil, ["-S", "-s", "CN=%s" % firstLocation, "-t", "Pu,,", "-c", "pgo temporary ca", "-m", "2", "-8", locationsParam, "-v", "120", "-n", "pgo server certificate", "-d", srcDir, "-z", rndfilePath, "-f", pwfilePath]) if status != 0: return status From 5559fb1d836d92b6b08f529d7b61c3774d07a5b7 Mon Sep 17 00:00:00 2001 From: Chris Leary Date: Mon, 7 Nov 2011 13:42:31 -0800 Subject: [PATCH 30/46] Bug 634654 - Add RegExpPrivate cache. (r=Waldo) --- js/src/builtin/RegExp.cpp | 49 +++++----- js/src/jscntxt.cpp | 44 +++++++++ js/src/jscntxt.h | 24 +++++ js/src/jsgc.cpp | 3 + js/src/jsprvtd.h | 4 + js/src/jsstr.cpp | 19 ++-- js/src/vm/RegExpObject-inl.h | 145 +++++++++++++++++------------- js/src/vm/RegExpObject.cpp | 168 ++++++++++++++++++++++++----------- js/src/vm/RegExpObject.h | 89 +++++++++++++------ 9 files changed, 373 insertions(+), 172 deletions(-) diff --git a/js/src/builtin/RegExp.cpp b/js/src/builtin/RegExp.cpp index 6829db9d8684..6ccc1aa030bf 100644 --- a/js/src/builtin/RegExp.cpp +++ b/js/src/builtin/RegExp.cpp @@ -206,14 +206,6 @@ EscapeNakedForwardSlashes(JSContext *cx, JSLinearString *unescaped) return sb.empty() ? unescaped : sb.finishString(); } -static bool -ResetRegExpObjectWithStatics(JSContext *cx, RegExpObject *reobj, - JSLinearString *str, RegExpFlag flags = RegExpFlag(0)) -{ - flags = RegExpFlag(flags | cx->regExpStatics()->getFlags()); - return reobj->reset(cx, str, flags); -} - /* * Compile a new |RegExpPrivate| for the |RegExpObject|. * @@ -226,12 +218,15 @@ ResetRegExpObjectWithStatics(JSContext *cx, RegExpObject *reobj, * flags := ToString(flags) if defined(flags) else '' */ static bool -CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, Value *rval) +CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, + uintN argc, Value *argv, Value *rval) { if (argc == 0) { - if (!ResetRegExpObjectWithStatics(cx, obj, cx->runtime->emptyString)) + RegExpStatics *res = cx->regExpStatics(); + RegExpObject *reobj = builder.build(cx->runtime->emptyString, res->getFlags()); + if (!reobj) return false; - *rval = ObjectValue(*obj); + *rval = ObjectValue(*reobj); return true; } @@ -249,9 +244,10 @@ CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, V return false; } - if (!obj->reset(cx, sourceObj.asRegExp())) + RegExpObject *reobj = builder.build(sourceObj.asRegExp()); + if (!reobj) return false; - *rval = ObjectValue(*obj); + *rval = ObjectValue(*reobj); return true; } @@ -285,9 +281,12 @@ CompileRegExpObject(JSContext *cx, RegExpObject *obj, uintN argc, Value *argv, V if (!RegExpPrivateCode::checkSyntax(cx, NULL, escapedSourceStr)) return false; - if (!ResetRegExpObjectWithStatics(cx, obj, escapedSourceStr, flags)) - return false; - *rval = ObjectValue(*obj); + RegExpStatics *res = cx->regExpStatics(); + RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags())); + if (!reobj) + return NULL; + + *rval = ObjectValue(*reobj); return true; } @@ -301,8 +300,8 @@ regexp_compile(JSContext *cx, uintN argc, Value *vp) if (!obj) return ok; - RegExpObject *reobj = obj->asRegExp(); - return CompileRegExpObject(cx, reobj, args.length(), args.array(), &args.rval()); + RegExpObjectBuilder builder(cx, obj->asRegExp()); + return CompileRegExpObject(cx, builder, args.length(), args.array(), &args.rval()); } static JSBool @@ -322,14 +321,11 @@ regexp_construct(JSContext *cx, uintN argc, Value *vp) } } - JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass); - if (!obj) + RegExpObjectBuilder builder(cx); + if (!CompileRegExpObject(cx, builder, argc, argv, &JS_RVAL(cx, vp))) return false; - if (!CompileRegExpObject(cx, obj->asRegExp(), argc, argv, &JS_RVAL(cx, vp))) - return false; - - *vp = ObjectValue(*obj); + *vp = ObjectValue(*builder.reobj()); return true; } @@ -462,9 +458,11 @@ js_InitRegExpClass(JSContext *cx, JSObject *obj) JSObject *proto = global->createBlankPrototype(cx, &RegExpClass); if (!proto) return NULL; + proto->setPrivate(NULL); RegExpObject *reproto = proto->asRegExp(); - if (!reproto->reset(cx, cx->runtime->emptyString, RegExpFlag(0))) + RegExpObjectBuilder builder(cx, reproto); + if (!builder.build(cx->runtime->emptyString, RegExpFlag(0))) return NULL; if (!DefinePropertiesAndBrand(cx, proto, NULL, regexp_methods)) @@ -526,6 +524,7 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp) * have to take a defensive refcount here. */ AutoRefCount arc(cx, NeedsIncRef(rep)); + RegExpStatics *res = cx->regExpStatics(); /* Step 2. */ diff --git a/js/src/jscntxt.cpp b/js/src/jscntxt.cpp index 66b35162e0c2..5cef498fcaa9 100644 --- a/js/src/jscntxt.cpp +++ b/js/src/jscntxt.cpp @@ -111,6 +111,7 @@ ThreadData::ThreadData() #endif waiveGCQuota(false), tempLifoAlloc(TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE), + repCache(NULL), dtoaState(NULL), nativeStackBase(GetNativeStackBase()), pendingProxyOperation(NULL), @@ -153,6 +154,31 @@ ThreadData::triggerOperationCallback(JSRuntime *rt) #endif } +RegExpPrivateCache * +ThreadData::createRegExpPrivateCache(JSRuntime *rt) +{ + JS_ASSERT(!repCache); + RegExpPrivateCache *newCache = rt->new_(rt); + + if (!newCache || !newCache->init()) { + rt->delete_(newCache); + return NULL; + } + + repCache = newCache; + return repCache; +} + +void +ThreadData::purgeRegExpPrivateCache(JSRuntime *rt) +{ + if (!repCache) + return; + + rt->delete_(repCache); + repCache = NULL; +} + } /* namespace js */ JSScript * @@ -315,6 +341,24 @@ js_PurgeThreads(JSContext *cx) #endif } +void +js_PurgeThreads_PostGlobalSweep(JSContext *cx) +{ +#ifdef JS_THREADSAFE + for (JSThread::Map::Enum e(cx->runtime->threads); + !e.empty(); + e.popFront()) + { + JSThread *thread = e.front().value; + + JS_ASSERT(!JS_CLIST_IS_EMPTY(&thread->contextList)); + thread->data.purgeRegExpPrivateCache(cx->runtime); + } +#else + cx->runtime->threadData.purgeRegExpPrivateCache(cx->runtime); +#endif +} + JSContext * js_NewContext(JSRuntime *rt, size_t stackChunkSize) { diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h index 082f976b4c4c..aa7047b16c23 100644 --- a/js/src/jscntxt.h +++ b/js/src/jscntxt.h @@ -197,6 +197,25 @@ struct ThreadData { static const size_t TEMP_LIFO_ALLOC_PRIMARY_CHUNK_SIZE = 1 << 12; LifoAlloc tempLifoAlloc; + private: + js::RegExpPrivateCache *repCache; + + js::RegExpPrivateCache *createRegExpPrivateCache(JSRuntime *rt); + + public: + js::RegExpPrivateCache *getRegExpPrivateCache() { return repCache; } + + /* N.B. caller is responsible for reporting OOM. */ + js::RegExpPrivateCache *getOrCreateRegExpPrivateCache(JSRuntime *rt) { + if (repCache) + return repCache; + + return createRegExpPrivateCache(rt); + } + + /* Called at the end of the global GC sweep phase to deallocate repCache memory. */ + void purgeRegExpPrivateCache(JSRuntime *rt); + /* * The GSN cache is per thread since even multi-cx-per-thread embeddings * do not interleave js_GetSrcNote calls. @@ -1214,6 +1233,8 @@ struct JSContext js::GCHelperThread *gcBackgroundFree; #endif + js::ThreadData *threadData() { return JS_THREAD_DATA(this); } + inline void* malloc_(size_t bytes) { return runtime->malloc_(bytes, this); } @@ -1890,6 +1911,9 @@ js_FinishThreads(JSRuntime *rt); extern void js_PurgeThreads(JSContext *cx); +extern void +js_PurgeThreads_PostGlobalSweep(JSContext *cx); + namespace js { #ifdef JS_THREADSAFE diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 832588cdfd85..2623f9dbb97f 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2910,6 +2910,9 @@ GCCycle(JSContext *cx, JSCompartment *comp, JSGCInvocationKind gckind) MarkAndSweep(cx, gckind); + if (!comp) + js_PurgeThreads_PostGlobalSweep(cx); + #ifdef JS_THREADSAFE if (gckind != GC_LAST_CONTEXT && rt->state != JSRTS_LANDING) { JS_ASSERT(cx->gcBackgroundFree == &rt->gcHelperThread); diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h index a76f4dad0a2b..10a44d0b1711 100644 --- a/js/src/jsprvtd.h +++ b/js/src/jsprvtd.h @@ -124,6 +124,7 @@ struct Class; class RegExpObject; class RegExpPrivate; +class RegExpObjectBuilder; class RegExpStatics; class MatchPairs; @@ -232,6 +233,9 @@ typedef HashMap, Run class Debugger; class WatchpointMap; +typedef HashMap, RuntimeAllocPolicy> + RegExpPrivateCache; + typedef JSNative Native; typedef JSPropertyOp PropertyOp; typedef JSStrictPropertyOp StrictPropertyOp; diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 91df3731211b..b6f4108bce86 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -1264,8 +1264,14 @@ class FlatMatch int32 match() const { return match_; } }; +/* + * Some string methods operate on a RegExpObject, if it is present, but if it + * is absent create an internal regular expression matcher. This unifies the + * interface. + */ class RegExpPair { + JSContext *cx; AutoRefCount rep_; RegExpObject *reobj_; @@ -1273,9 +1279,10 @@ class RegExpPair void operator=(const RegExpPair &); public: - explicit RegExpPair(JSContext *cx) : rep_(cx) {} + explicit RegExpPair(JSContext *cx) : cx(cx), rep_(cx) {} bool resetWithObject(JSContext *cx, RegExpObject *reobj) { + JS_ASSERT(cx == this->cx); reobj_ = reobj; RegExpPrivate *rep = reobj_->asRegExp()->getOrCreatePrivate(cx); if (!rep) @@ -1302,9 +1309,8 @@ class RegExpPair /* * RegExpGuard factors logic out of String regexp operations. * - * @param optarg Indicates in which argument position RegExp - * flags will be found, if present. This is a Mozilla - * extension and not part of any ECMA spec. + * |optarg| indicates in which argument position RegExp flags will be found, if + * present. This is a Mozilla extension and not part of any ECMA spec. */ class RegExpGuard { @@ -1368,8 +1374,9 @@ class RegExpGuard * Attempt to match |patstr| to |textstr|. A flags argument, metachars in the * pattern string, or a lengthy pattern string can thwart this process. * - * @param checkMetaChars Look for regexp metachars in the pattern string. - * @return Whether flat matching could be used. + * |checkMetaChars| looks for regexp metachars in the pattern string. + * + * Return whether flat matching could be used. * * N.B. tryFlatMatch returns NULL on OOM, so the caller must check cx->isExceptionPending(). */ diff --git a/js/src/vm/RegExpObject-inl.h b/js/src/vm/RegExpObject-inl.h index 90e9ce4c8aa6..ca833dcd7b62 100644 --- a/js/src/vm/RegExpObject-inl.h +++ b/js/src/vm/RegExpObject-inl.h @@ -89,6 +89,12 @@ HasRegExpMetaChars(const jschar *chars, size_t length) return false; } +inline void +RegExpObject::setPrivate(RegExpPrivate *rep) +{ + JSObject::setPrivate(rep); +} + inline RegExpObject * RegExpObject::create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length, RegExpFlag flags, TokenStream *tokenStream) @@ -101,57 +107,44 @@ inline RegExpObject * RegExpObject::createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags, TokenStream *tokenStream) { - JSLinearString *source = js_NewStringCopyN(cx, chars, length); + JSAtom *source = js_AtomizeChars(cx, chars, length); if (!source) return NULL; - /* |NewBuiltinClassInstance| can GC. */ - JS::Anchor anchor(source); + return createNoStatics(cx, source, flags, tokenStream); +} +inline RegExpObject * +RegExpObject::createNoStatics(JSContext *cx, JSAtom *source, RegExpFlag flags, + TokenStream *tokenStream) +{ if (!RegExpPrivateCode::checkSyntax(cx, tokenStream, source)) return NULL; - JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass); - if (!obj) - return NULL; + RegExpObjectBuilder builder(cx); + return builder.build(source, flags); +} - RegExpObject *reobj = obj->asRegExp(); - return reobj->reset(cx, source, flags) ? reobj : NULL; +inline void +RegExpObject::purge(JSContext *cx) +{ + if (RegExpPrivate *rep = getPrivate()) { + rep->decref(cx); + setPrivate(NULL); + } } inline void RegExpObject::finalize(JSContext *cx) { - if (RegExpPrivate *rep = getPrivate()) - rep->decref(cx); + purge(cx); #ifdef DEBUG - setPrivate((void *) 0x1); /* Non-null but still in the zero page. */ + setPrivate((RegExpPrivate *) 0x1); /* Non-null but still in the zero page. */ #endif } inline bool -RegExpObject::reset(JSContext *cx, AlreadyIncRefed rep) -{ - if (!reset(cx, rep->getSource(), rep->getFlags())) - return false; - - setPrivate(rep.get()); - return true; -} - -inline bool -RegExpObject::reset(JSContext *cx, RegExpObject *other) -{ - if (RegExpPrivate *rep = other->getPrivate()) { - rep->incref(cx); - return reset(cx, AlreadyIncRefed(rep)); - } - - return reset(cx, other->getSource(), other->getFlags()); -} - -inline bool -RegExpObject::reset(JSContext *cx, JSLinearString *source, RegExpFlag flags) +RegExpObject::init(JSContext *cx, JSLinearString *source, RegExpFlag flags) { if (nativeEmpty()) { const js::Shape **shapep = &cx->compartment->initialRegExpShape; @@ -174,8 +167,8 @@ RegExpObject::reset(JSContext *cx, JSLinearString *source, RegExpFlag flags) MULTILINE_FLAG_SLOT); JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot == STICKY_FLAG_SLOT); + JS_ASSERT(!getPrivate()); zeroLastIndex(); - setPrivate(NULL); setSource(source); setGlobal(flags & GlobalFlag); setIgnoreCase(flags & IgnoreCaseFlag); @@ -191,11 +184,46 @@ RegExpPrivate::create(JSContext *cx, JSLinearString *source, RegExpFlag flags, T { typedef AlreadyIncRefed RetType; + /* + * We choose to only cache |RegExpPrivate|s who have atoms as + * sources, under the unverified premise that non-atoms will have a + * low hit rate (both hit ratio and absolute number of hits). + */ + bool cacheable = source->isAtom(); + + /* + * Refcount note: not all |RegExpPrivate|s are cached so we need to + * keep a refcount. The cache holds a "weak ref", where the + * |RegExpPrivate|'s deallocation decref will first cause it to + * remove itself from the cache. + */ + + JSRuntime *rt = cx->runtime; + RegExpPrivateCache *cache = NULL; /* Quell "may be used uninitialized". */ + RegExpPrivateCache::AddPtr addPtr; + if (cacheable) { + cache = cx->threadData()->getOrCreateRegExpPrivateCache(rt); + if (!cache) { + js_ReportOutOfMemory(cx); + return RetType(NULL); + } + + addPtr = cache->lookupForAdd(&source->asAtom()); + if (addPtr) { + RegExpPrivate *cached = addPtr->value; + if (cached->getFlags() == flags) { + cached->incref(cx); + return RetType(cached); + } + /* Note: on flag mismatch, we clobber the existing entry. */ + } + } + JSLinearString *flatSource = source->ensureLinear(cx); if (!flatSource) return RetType(NULL); - RegExpPrivate *self = cx->new_(flatSource, flags, cx->compartment); + RegExpPrivate *self = cx->new_(flatSource, flags); if (!self) return RetType(NULL); @@ -204,6 +232,18 @@ RegExpPrivate::create(JSContext *cx, JSLinearString *source, RegExpFlag flags, T return RetType(NULL); } + if (cacheable) { + if (addPtr) { + JS_ASSERT(addPtr->key == &self->getSource()->asAtom()); + addPtr->value = self; + } else { + if (!cache->add(addPtr, &source->asAtom(), self)) { + js_ReportOutOfMemory(cx); + return RetType(NULL); + } + } + } + return RetType(self); } @@ -331,40 +371,23 @@ RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t length, si inline void RegExpPrivate::incref(JSContext *cx) { -#ifdef DEBUG - assertSameCompartment(cx, compartment); -#endif ++refCount; } inline void RegExpPrivate::decref(JSContext *cx) { -#ifdef DEBUG - assertSameCompartment(cx, compartment); -#endif - if (--refCount == 0) - cx->delete_(this); -} + if (--refCount != 0) + return; -inline RegExpPrivate * -RegExpObject::getOrCreatePrivate(JSContext *cx) -{ - if (RegExpPrivate *rep = getPrivate()) - return rep; + RegExpPrivateCache *cache; + if (source->isAtom() && (cache = cx->threadData()->getRegExpPrivateCache())) { + RegExpPrivateCache::Ptr ptr = cache->lookup(&source->asAtom()); + if (ptr && ptr->value == this) + cache->remove(ptr); + } - return makePrivate(cx) ? getPrivate() : NULL; -} - -inline RegExpPrivate * -RegExpObject::getPrivate() const -{ - RegExpPrivate *rep = static_cast(JSObject::getPrivate()); -#ifdef DEBUG - if (rep) - CompartmentChecker::check(compartment(), rep->compartment); -#endif - return rep; + cx->delete_(this); } } /* namespace js */ diff --git a/js/src/vm/RegExpObject.cpp b/js/src/vm/RegExpObject.cpp index edd4d093bd66..9653e39b5bb2 100644 --- a/js/src/vm/RegExpObject.cpp +++ b/js/src/vm/RegExpObject.cpp @@ -59,6 +59,106 @@ JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB); JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE); JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY); +/* RegExpObjectBuilder */ + +bool +RegExpObjectBuilder::getOrCreate() +{ + if (reobj_) + return true; + + JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass); + if (!obj) + return false; + obj->setPrivate(NULL); + + reobj_ = obj->asRegExp(); + return true; +} + +bool +RegExpObjectBuilder::getOrCreateClone(RegExpObject *proto) +{ + JS_ASSERT(!reobj_); + + JSObject *clone = NewNativeClassInstance(cx, &RegExpClass, proto, proto->getParent()); + if (!clone) + return false; + clone->setPrivate(NULL); + + reobj_ = clone->asRegExp(); + return true; +} + +RegExpObject * +RegExpObjectBuilder::build(AlreadyIncRefed rep) +{ + if (!getOrCreate()) { + rep->decref(cx); + return NULL; + } + + reobj_->purge(cx); + if (!reobj_->init(cx, rep->getSource(), rep->getFlags())) { + rep->decref(cx); + return NULL; + } + reobj_->setPrivate(rep.get()); + + return reobj_; +} + +RegExpObject * +RegExpObjectBuilder::build(JSLinearString *source, RegExpFlag flags) +{ + if (!getOrCreate()) + return NULL; + + reobj_->purge(cx); + return reobj_->init(cx, source, flags) ? reobj_ : NULL; +} + +RegExpObject * +RegExpObjectBuilder::build(RegExpObject *other) +{ + RegExpPrivate *rep = other->getOrCreatePrivate(cx); + if (!rep) + return NULL; + + /* Now, incref it for the RegExpObject being built. */ + rep->incref(cx); + return build(AlreadyIncRefed(rep)); +} + +RegExpObject * +RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto) +{ + if (!getOrCreateClone(proto)) + return NULL; + + /* + * Check that the RegExpPrivate for the original is okay to use in + * the clone -- if the |RegExpStatics| provides more flags we'll + * need a different |RegExpPrivate|. + */ + RegExpStatics *res = cx->regExpStatics(); + RegExpFlag origFlags = other->getFlags(); + RegExpFlag staticsFlags = res->getFlags(); + if ((origFlags & staticsFlags) != staticsFlags) { + RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags); + return build(other->getSource(), newFlags); + } + + RegExpPrivate *toShare = other->getOrCreatePrivate(cx); + if (!toShare) + return NULL; + + toShare->incref(cx); + return build(AlreadyIncRefed(toShare)); +} + +/* MatchPairs */ + MatchPairs * MatchPairs::create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount) { @@ -130,16 +230,16 @@ RegExpPrivate::execute(JSContext *cx, const jschar *chars, size_t length, size_t return RegExpRunStatus_Success; } -bool +RegExpPrivate * RegExpObject::makePrivate(JSContext *cx) { JS_ASSERT(!getPrivate()); AlreadyIncRefed rep = RegExpPrivate::create(cx, getSource(), getFlags(), NULL); if (!rep) - return false; + return NULL; setPrivate(rep.get()); - return true; + return rep.get(); } RegExpRunStatus @@ -209,13 +309,11 @@ js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp) if (!JS_XDRString(xdr, &source) || !JS_XDRUint32(xdr, &flagsword)) return false; if (xdr->mode == JSXDR_DECODE) { - JS::Anchor anchor(source); - const jschar *chars = source->getChars(xdr->cx); - if (!chars) + JSAtom *atom = js_AtomizeString(xdr->cx, source); + if (!atom) return false; - size_t len = source->length(); - RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, chars, len, - RegExpFlag(flagsword), NULL); + RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx, atom, RegExpFlag(flagsword), + NULL); if (!reobj) return false; @@ -238,6 +336,12 @@ regexp_finalize(JSContext *cx, JSObject *obj) obj->asRegExp()->finalize(cx); } +static void +regexp_trace(JSTracer *trc, JSObject *obj) +{ + obj->asRegExp()->purge(trc->context); +} + Class js::RegExpClass = { js_RegExp_str, JSCLASS_HAS_PRIVATE | @@ -257,7 +361,7 @@ Class js::RegExpClass = { NULL, /* construct */ js_XDRRegExpObject, NULL, /* hasInstance */ - NULL /* trace */ + regexp_trace }; #if ENABLE_YARR_JIT @@ -378,54 +482,14 @@ RegExpPrivate::create(JSContext *cx, JSLinearString *str, JSString *opt, TokenSt return create(cx, str, flags, ts); } -RegExpObject * -RegExpObject::clone(JSContext *cx, RegExpObject *reobj, RegExpObject *proto) -{ - JSObject *clone = NewNativeClassInstance(cx, &RegExpClass, proto, proto->getParent()); - if (!clone) - return NULL; - - /* - * This clone functionality does not duplicate the JIT'd code blob, - * which is necessary for cross-compartment cloning functionality. - */ - assertSameCompartment(cx, reobj, clone); - - RegExpStatics *res = cx->regExpStatics(); - RegExpObject *reclone = clone->asRegExp(); - - /* - * Check that the RegExpPrivate for the original is okay to use in - * the clone -- if the |RegExpStatics| provides more flags we'll - * need a different |RegExpPrivate|. - */ - RegExpFlag origFlags = reobj->getFlags(); - RegExpFlag staticsFlags = res->getFlags(); - if ((origFlags & staticsFlags) != staticsFlags) { - RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags); - return reclone->reset(cx, reobj->getSource(), newFlags) ? reclone : NULL; - } - - RegExpPrivate *toShare = reobj->getOrCreatePrivate(cx); - if (!toShare) - return NULL; - - toShare->incref(cx); - if (!reclone->reset(cx, AlreadyIncRefed(toShare))) { - toShare->decref(cx); - return NULL; - } - - return reclone; -} - JSObject * JS_FASTCALL js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto) { JS_ASSERT(obj->isRegExp()); JS_ASSERT(proto->isRegExp()); - return RegExpObject::clone(cx, obj->asRegExp(), proto->asRegExp()); + RegExpObjectBuilder builder(cx); + return builder.clone(obj->asRegExp(), proto->asRegExp()); } #ifdef JS_TRACER diff --git a/js/src/vm/RegExpObject.h b/js/src/vm/RegExpObject.h index c48167ba337a..7535b2f51538 100644 --- a/js/src/vm/RegExpObject.h +++ b/js/src/vm/RegExpObject.h @@ -82,13 +82,14 @@ class RegExpObject : public ::JSObject */ static inline RegExpObject * create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length, RegExpFlag flags, - TokenStream *ts); + TokenStream *tokenStream); static inline RegExpObject * createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags, - TokenStream *ts); + TokenStream *tokenStream); - static RegExpObject *clone(JSContext *cx, RegExpObject *obj, RegExpObject *proto); + static inline RegExpObject * + createNoStatics(JSContext *cx, JSAtom *atom, RegExpFlag flags, TokenStream *tokenStream); /* Note: fallible. */ JSFlatString *toString(JSContext *cx) const; @@ -148,27 +149,35 @@ class RegExpObject : public ::JSObject bool multiline() const { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); } bool sticky() const { return getSlot(STICKY_FLAG_SLOT).toBoolean(); } - /* - * N.B. |RegExpObject|s can be mutated in place because of |RegExp.prototype.compile|, hence - * |reset| for re-initialization. - */ - - inline bool reset(JSContext *cx, RegExpObject *other); - inline bool reset(JSContext *cx, AlreadyIncRefed rep); - inline bool reset(JSContext *cx, JSLinearString *source, RegExpFlag flags); - - inline RegExpPrivate *getOrCreatePrivate(JSContext *cx); inline void finalize(JSContext *cx); + /* Clear out lazy |RegExpPrivate|. */ + inline void purge(JSContext *x); + + RegExpPrivate *getOrCreatePrivate(JSContext *cx) { + if (RegExpPrivate *rep = getPrivate()) + return rep; + + return makePrivate(cx); + } + private: + friend class RegExpObjectBuilder; + + inline bool init(JSContext *cx, JSLinearString *source, RegExpFlag flags); + /* The |RegExpPrivate| is lazily created at the time of use. */ - inline RegExpPrivate *getPrivate() const; + RegExpPrivate *getPrivate() const { + return static_cast(JSObject::getPrivate()); + } + + inline void setPrivate(RegExpPrivate *rep); /* * Precondition: the syntax for |source| has already been validated. * Side effect: sets the private field. */ - bool makePrivate(JSContext *cx); + RegExpPrivate *makePrivate(JSContext *cx); friend bool ResetRegExpObject(JSContext *, RegExpObject *, JSLinearString *, RegExpFlag); friend bool ResetRegExpObject(JSContext *, RegExpObject *, AlreadyIncRefed); @@ -184,6 +193,32 @@ class RegExpObject : public ::JSObject RegExpObject &operator=(const RegExpObject &reo); }; /* class RegExpObject */ +/* Either builds a new RegExpObject or re-initializes an existing one. */ +class RegExpObjectBuilder +{ + JSContext *cx; + RegExpObject *reobj_; + + bool getOrCreate(); + bool getOrCreateClone(RegExpObject *proto); + + public: + RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj = NULL) + : cx(cx), reobj_(reobj) + { } + + RegExpObject *reobj() { return reobj_; } + + /* Note: In case of failure, |rep| will be decrefed. */ + RegExpObject *build(AlreadyIncRefed rep); + + RegExpObject *build(JSLinearString *str, RegExpFlag flags); + RegExpObject *build(RegExpObject *other); + + /* Perform a VM-internal clone. */ + RegExpObject *clone(RegExpObject *other, RegExpObject *proto); +}; + /* Abstracts away the gross |RegExpPrivate| backend details. */ class RegExpPrivateCode { @@ -243,12 +278,6 @@ class RegExpPrivateCode static void reportPCREError(JSContext *cx, int error); #endif - inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount, - RegExpFlag flags); - - inline RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t start, - int *output, size_t outputCount); - static size_t getOutputSize(size_t pairCount) { #if ENABLE_YARR_JIT return pairCount * 2; @@ -256,6 +285,13 @@ class RegExpPrivateCode return pairCount * 3; /* Should be x2, but PCRE has... needs. */ #endif } + + inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount, + RegExpFlag flags); + + + inline RegExpRunStatus execute(JSContext *cx, const jschar *chars, size_t length, size_t start, + int *output, size_t outputCount); }; /* @@ -265,8 +301,8 @@ class RegExpPrivateCode * compilation. * * Non-atomic refcounting is used, so single-thread invariants must be - * maintained: we check regexp operations are performed in a single - * compartment. + * maintained. |RegExpPrivate|s are currently shared within a single + * |ThreadData|. * * Note: refCount cannot overflow because that would require more * referring regexp objects than there is space for in addressable @@ -280,12 +316,9 @@ class RegExpPrivate uintN parenCount; RegExpFlag flags; - public: - DebugOnly compartment; - private: - RegExpPrivate(JSLinearString *source, RegExpFlag flags, JSCompartment *compartment) - : source(source), refCount(1), parenCount(0), flags(flags), compartment(compartment) + RegExpPrivate(JSLinearString *source, RegExpFlag flags) + : source(source), refCount(1), parenCount(0), flags(flags) { } JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR; From e4244a46465f36752ca3f2541d290549c4b9c7b1 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Tue, 8 Nov 2011 12:58:48 +1300 Subject: [PATCH 31/46] Bug 699885 part 1 - Ensure we dispatch 'deactivate' event to chrome window when we lose focus while changing to full-screen mode. r=roc --- widget/src/windows/nsWindow.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/widget/src/windows/nsWindow.cpp b/widget/src/windows/nsWindow.cpp index e9a7b48268df..683585449420 100644 --- a/widget/src/windows/nsWindow.cpp +++ b/widget/src/windows/nsWindow.cpp @@ -5271,7 +5271,11 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam, break; case WM_KILLFOCUS: - if (sJustGotDeactivate) { + if (sJustGotDeactivate || !wParam) { + // Note: wParam is FALSE when the window has lost focus. Sometimes + // We can receive WM_KILLFOCUS with !wParam while changing to + // full-screen mode and we won't receive an WM_ACTIVATE/WA_INACTIVE + // message, so inform the focus manager that we've lost focus now. result = DispatchFocusToTopLevelWindow(NS_DEACTIVATE); } break; From bb48204f3124b0ead3eafe61993a60ea185e936c Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Tue, 8 Nov 2011 12:58:48 +1300 Subject: [PATCH 32/46] Bug 699885 part 2 - Exit DOM full-screen if we've changed tab or app by the time the request is granted. r=dao --- browser/base/content/browser.js | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js index 13a8c8103a04..ae06684d7a9d 100644 --- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -3913,14 +3913,36 @@ var FullScreen = { }, enterDomFullScreen : function(event) { + if (!document.mozFullScreen) { + return; + } + // We receive "mozfullscreenchange" events for each subdocument which // is an ancestor of the document containing the element which requested // full-screen. Only add listeners and show warning etc when the event we // receive is targeted at the chrome document, i.e. only once every time // we enter DOM full-screen mode. - if (!document.mozFullScreen || event.target.ownerDocument != document) { + let targetDoc = event.target.ownerDocument ? event.target.ownerDocument : event.target; + if (targetDoc != document) { + // However, if we receive a "mozfullscreenchange" event for a document + // which is not a subdocument of the currently selected tab, we know that + // we've switched tabs since the request to enter full-screen was made, + // so we should exit full-screen since the "full-screen document" isn't + // acutally visible. + if (targetDoc.defaultView.top != gBrowser.contentWindow) { + document.mozCancelFullScreen(); + } return; } + + let focusManger = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager); + if (focusManger.activeWindow != window) { + // The top-level window has lost focus since the request to enter + // full-screen was made. Cancel full-screen. + document.mozCancelFullScreen(); + return; + } + this.showWarning(true); // Exit DOM full-screen mode upon open, close, or change tab. From 896a06c2c948e612cab659d72b822986ed0d38e4 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Tue, 8 Nov 2011 12:58:48 +1300 Subject: [PATCH 33/46] Bug 699885 part 3 - Update full-screen mochitests wait for focus. r=roc --- .../content/test/file_fullscreen-api.html | 37 +++++++++++++------ 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/content/html/content/test/file_fullscreen-api.html b/content/html/content/test/file_fullscreen-api.html index 5776cb2b6180..d32a07ab5322 100644 --- a/content/html/content/test/file_fullscreen-api.html +++ b/content/html/content/test/file_fullscreen-api.html @@ -9,14 +9,14 @@ Test DOM full-screen API. Test for Bug 545812 + - - +
From 6e8498d824a498cb82fc4ea9fe523c8b54ee9106 Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Tue, 8 Nov 2011 13:09:39 +1300 Subject: [PATCH 34/46] Bug 696307. Don't overwrite the aChild parameter of BuildDisplayListForChild. r=mats --- layout/generic/nsFrame.cpp | 85 +++++++++++++++++++------------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index 8ce78119cfce..c5e9fc3b427c 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1862,14 +1862,15 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, if (aBuilder->IsBackgroundOnly()) return NS_OK; - if (aChild->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) + nsIFrame* child = aChild; + if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) return NS_OK; // true if this is a real or pseudo stacking context bool pseudoStackingContext = (aFlags & DISPLAY_CHILD_FORCE_PSEUDO_STACKING_CONTEXT) != 0; if ((aFlags & DISPLAY_CHILD_INLINE) && - !aChild->IsFrameOfType(eLineParticipant)) { + !child->IsFrameOfType(eLineParticipant)) { // child is a non-inline frame in an inline context, i.e., // it acts like inline-block or inline-table. Therefore it is a // pseudo-stacking-context. @@ -1877,24 +1878,24 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, } // dirty rect in child-relative coordinates - nsRect dirty = aDirtyRect - aChild->GetOffsetTo(this); + nsRect dirty = aDirtyRect - child->GetOffsetTo(this); - nsIAtom* childType = aChild->GetType(); + nsIAtom* childType = child->GetType(); if (childType == nsGkAtoms::placeholderFrame) { - nsPlaceholderFrame* placeholder = static_cast(aChild); - aChild = placeholder->GetOutOfFlowFrame(); - NS_ASSERTION(aChild, "No out of flow frame?"); - if (!aChild || nsLayoutUtils::IsPopup(aChild)) + nsPlaceholderFrame* placeholder = static_cast(child); + child = placeholder->GetOutOfFlowFrame(); + NS_ASSERTION(child, "No out of flow frame?"); + if (!child || nsLayoutUtils::IsPopup(child)) return NS_OK; // Make sure that any attempt to use childType below is disappointed. We // could call GetType again but since we don't currently need it, let's // avoid the virtual call. childType = nsnull; // Recheck NS_FRAME_TOO_DEEP_IN_FRAME_TREE - if (aChild->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) + if (child->GetStateBits() & NS_FRAME_TOO_DEEP_IN_FRAME_TREE) return NS_OK; nsRect* savedDirty = static_cast - (aChild->Properties().Get(nsDisplayListBuilder::OutOfFlowDirtyRectProperty())); + (child->Properties().Get(nsDisplayListBuilder::OutOfFlowDirtyRectProperty())); if (savedDirty) { dirty = *savedDirty; } else { @@ -1907,30 +1908,30 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, } // Mark the display list items for absolutely positioned children - aChild->MarkAbsoluteFramesForDisplayList(aBuilder, dirty); + child->MarkAbsoluteFramesForDisplayList(aBuilder, dirty); if (childType != nsGkAtoms::placeholderFrame && aBuilder->GetSelectedFramesOnly() && - aChild->IsLeaf() && - !(aChild->GetStateBits() & NS_FRAME_SELECTED_CONTENT)) { + child->IsLeaf() && + !(child->GetStateBits() & NS_FRAME_SELECTED_CONTENT)) { return NS_OK; } if (aBuilder->GetIncludeAllOutOfFlows() && - (aChild->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { - dirty = aChild->GetVisualOverflowRect(); - } else if (!(aChild->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) { - // No need to descend into aChild to catch placeholders for visible + (child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) { + dirty = child->GetVisualOverflowRect(); + } else if (!(child->GetStateBits() & NS_FRAME_FORCE_DISPLAY_LIST_DESCEND_INTO)) { + // No need to descend into child to catch placeholders for visible // positioned stuff. So see if we can short-circuit frame traversal here. - // We can stop if aChild's frame subtree's intersection with the + // We can stop if child's frame subtree's intersection with the // dirty area is empty. // If the child is a scrollframe that we want to ignore, then we need // to descend into it because its scrolled child may intersect the dirty // area even if the scrollframe itself doesn't. - if (aChild != aBuilder->GetIgnoreScrollFrame()) { + if (child != aBuilder->GetIgnoreScrollFrame()) { nsRect childDirty; - if (!childDirty.IntersectRect(dirty, aChild->GetVisualOverflowRect())) + if (!childDirty.IntersectRect(dirty, child->GetVisualOverflowRect())) return NS_OK; // Usually we could set dirty to childDirty now but there's no // benefit, and it can be confusing. It can especially confuse @@ -1951,10 +1952,10 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // Child is composited if it's transformed, partially transparent, or has // SVG effects. - const nsStyleDisplay* disp = aChild->GetStyleDisplay(); + const nsStyleDisplay* disp = child->GetStyleDisplay(); bool isVisuallyAtomic = disp->mOpacity != 1.0f - || aChild->IsTransformed() - || nsSVGIntegrationUtils::UsingEffectsForFrame(aChild); + || child->IsTransformed() + || nsSVGIntegrationUtils::UsingEffectsForFrame(child); bool isPositioned = disp->IsPositioned(); if (isVisuallyAtomic || isPositioned || disp->IsFloating() || @@ -1966,9 +1967,9 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, nsRect overflowClip; nscoord overflowClipRadii[8]; bool applyOverflowClip = - ApplyOverflowClipping(aBuilder, aChild, disp, &overflowClip); + ApplyOverflowClipping(aBuilder, child, disp, &overflowClip); if (applyOverflowClip) { - aChild->GetPaddingBoxBorderRadii(overflowClipRadii); + child->GetPaddingBoxBorderRadii(overflowClipRadii); } // Don't use overflowClip to restrict the dirty rect, since some of the // descendants may not be clipped by it. Even if we end up with unnecessary @@ -1984,34 +1985,34 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // Not a pseudo or real stacking context. Do the simple thing and // return early. if (applyOverflowClip) { - rv = BuildDisplayListWithOverflowClip(aBuilder, aChild, dirty, aLists, + rv = BuildDisplayListWithOverflowClip(aBuilder, child, dirty, aLists, overflowClip, overflowClipRadii); } else { - rv = aChild->BuildDisplayList(aBuilder, dirty, aLists); + rv = child->BuildDisplayList(aBuilder, dirty, aLists); if (NS_SUCCEEDED(rv)) { - rv = aBuilder->DisplayCaret(aChild, dirty, aLists.Content()); + rv = aBuilder->DisplayCaret(child, dirty, aLists.Content()); } } #ifdef NS_DEBUG - DisplayDebugBorders(aBuilder, aChild, aLists); + DisplayDebugBorders(aBuilder, child, aLists); #endif return rv; } nsDisplayList list; nsDisplayList extraPositionedDescendants; - const nsStylePosition* pos = aChild->GetStylePosition(); + const nsStylePosition* pos = child->GetStylePosition(); if ((isPositioned && pos->mZIndex.GetUnit() == eStyleUnit_Integer) || isVisuallyAtomic || (aFlags & DISPLAY_CHILD_FORCE_STACKING_CONTEXT)) { // True stacking context - rv = aChild->BuildDisplayListForStackingContext(aBuilder, dirty, &list); + rv = child->BuildDisplayListForStackingContext(aBuilder, dirty, &list); if (NS_SUCCEEDED(rv)) { - rv = aBuilder->DisplayCaret(aChild, dirty, &list); + rv = aBuilder->DisplayCaret(child, dirty, &list); } } else { nsRect clipRect; bool applyAbsPosClipping = - ApplyAbsPosClipping(aBuilder, disp, aChild, &clipRect); + ApplyAbsPosClipping(aBuilder, disp, child, &clipRect); // A pseudo-stacking context (e.g., a positioned element with z-index auto). // We allow positioned descendants of the child to escape to our parent // stacking context's positioned descendant list, because they might be @@ -2020,26 +2021,26 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, nsRect clippedDirtyRect = dirty; if (applyAbsPosClipping) { // clipRect is in builder-reference-frame coordinates, - // dirty/clippedDirtyRect are in aChild coordinates + // dirty/clippedDirtyRect are in child coordinates clippedDirtyRect.IntersectRect(clippedDirtyRect, - clipRect - aBuilder->ToReferenceFrame(aChild)); + clipRect - aBuilder->ToReferenceFrame(child)); } if (applyOverflowClip) { - rv = BuildDisplayListWithOverflowClip(aBuilder, aChild, clippedDirtyRect, + rv = BuildDisplayListWithOverflowClip(aBuilder, child, clippedDirtyRect, pseudoStack, overflowClip, overflowClipRadii); } else { - rv = aChild->BuildDisplayList(aBuilder, clippedDirtyRect, pseudoStack); + rv = child->BuildDisplayList(aBuilder, clippedDirtyRect, pseudoStack); if (NS_SUCCEEDED(rv)) { - rv = aBuilder->DisplayCaret(aChild, dirty, pseudoStack.Content()); + rv = aBuilder->DisplayCaret(child, dirty, pseudoStack.Content()); } } if (NS_SUCCEEDED(rv)) { if (applyAbsPosClipping) { nsAbsPosClipWrapper wrapper(clipRect); - rv = wrapper.WrapListsInPlace(aBuilder, aChild, pseudoStack); + rv = wrapper.WrapListsInPlace(aBuilder, child, pseudoStack); } } list.AppendToTop(pseudoStack.BorderBackground()); @@ -2049,7 +2050,7 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, list.AppendToTop(pseudoStack.Outlines()); extraPositionedDescendants.AppendToTop(pseudoStack.PositionedDescendants()); #ifdef NS_DEBUG - DisplayDebugBorders(aBuilder, aChild, aLists); + DisplayDebugBorders(aBuilder, child, aLists); #endif } NS_ENSURE_SUCCESS(rv, rv); @@ -2059,11 +2060,11 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, // Genuine stacking contexts, and positioned pseudo-stacking-contexts, // go in this level. rv = aLists.PositionedDescendants()->AppendNewToTop(new (aBuilder) - nsDisplayWrapList(aBuilder, aChild, &list)); + nsDisplayWrapList(aBuilder, child, &list)); NS_ENSURE_SUCCESS(rv, rv); } else if (disp->IsFloating()) { rv = aLists.Floats()->AppendNewToTop(new (aBuilder) - nsDisplayWrapList(aBuilder, aChild, &list)); + nsDisplayWrapList(aBuilder, child, &list)); NS_ENSURE_SUCCESS(rv, rv); } else { aLists.Content()->AppendToTop(&list); From 3efeb8c08dbf6ae5efb071b9c107f31ef4a2dc9a Mon Sep 17 00:00:00 2001 From: Robert O'Callahan Date: Tue, 8 Nov 2011 13:09:39 +1300 Subject: [PATCH 35/46] Bug 696307. Floats in a different block to their placeholders should only be painted once. r=mats --- layout/generic/nsFrame.cpp | 6 +++++- layout/reftests/bugs/696307-1-ref.html | 5 +++++ layout/reftests/bugs/696307-1.html | 4 ++++ layout/reftests/bugs/reftest.list | 1 + 4 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 layout/reftests/bugs/696307-1-ref.html create mode 100644 layout/reftests/bugs/696307-1.html diff --git a/layout/generic/nsFrame.cpp b/layout/generic/nsFrame.cpp index c5e9fc3b427c..dec6ab3fc65d 100644 --- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -1885,7 +1885,11 @@ nsIFrame::BuildDisplayListForChild(nsDisplayListBuilder* aBuilder, nsPlaceholderFrame* placeholder = static_cast(child); child = placeholder->GetOutOfFlowFrame(); NS_ASSERTION(child, "No out of flow frame?"); - if (!child || nsLayoutUtils::IsPopup(child)) + // If 'child' is a pushed float then it's owned by a block that's not an + // ancestor of the placeholder, and it will be painted by that block and + // should not be painted through the placeholder. + if (!child || nsLayoutUtils::IsPopup(child) || + (child->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT)) return NS_OK; // Make sure that any attempt to use childType below is disappointed. We // could call GetType again but since we don't currently need it, let's diff --git a/layout/reftests/bugs/696307-1-ref.html b/layout/reftests/bugs/696307-1-ref.html new file mode 100644 index 000000000000..e773a90cba08 --- /dev/null +++ b/layout/reftests/bugs/696307-1-ref.html @@ -0,0 +1,5 @@ + + +
X
+
Y
+ diff --git a/layout/reftests/bugs/696307-1.html b/layout/reftests/bugs/696307-1.html new file mode 100644 index 000000000000..26737407ac32 --- /dev/null +++ b/layout/reftests/bugs/696307-1.html @@ -0,0 +1,4 @@ + + +
X
Y
+ diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 9f92c3f2e42a..dda8f3b9b906 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -1670,4 +1670,5 @@ fails-if(layersGPUAccelerated&&cocoaWidget) == 654950-1.html 654950-1-ref.html # == 670467-2.html 670467-2-ref.html != 691087-1.html 691087-1-ref.html == 691571-1.html 691571-1-ref.html +== 696307-1.html 696307-1-ref.html == 696739-1.html 696739-1-ref.html From 77ca42d8f6c9e973454b2c0cb07e3987cfa762b4 Mon Sep 17 00:00:00 2001 From: Chris Pearce Date: Tue, 8 Nov 2011 13:20:53 +1300 Subject: [PATCH 36/46] Bug 700151 - Enable DOM full-screen API by default in desktop Firefox. r=roc --- browser/app/profile/firefox.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/browser/app/profile/firefox.js b/browser/app/profile/firefox.js index 112c1b9d5eb7..6bf49648a567 100644 --- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1064,3 +1064,6 @@ pref("browser.menu.showCharacterEncoding", "chrome://browser/locale/browser.prop pref("prompts.tab_modal.enabled", true); // Whether the Panorama should animate going in/out of tabs pref("browser.panorama.animate_zoom", true); + +// Enable the DOM full-screen API. +pref("full-screen-api.enabled", true); From a5b176e675aefcfad23de3f8cf3d7016ddd211e7 Mon Sep 17 00:00:00 2001 From: Doug Sherk Date: Mon, 7 Nov 2011 19:33:51 -0500 Subject: [PATCH 37/46] Bug 665578: prevented ANGLE built-in function emulation on OS X 10.7 and newer r=bjacob --- content/canvas/src/WebGLContextGL.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/content/canvas/src/WebGLContextGL.cpp b/content/canvas/src/WebGLContextGL.cpp index c33dbb6550c2..1ebce3767f7c 100644 --- a/content/canvas/src/WebGLContextGL.cpp +++ b/content/canvas/src/WebGLContextGL.cpp @@ -64,6 +64,11 @@ #include "WebGLTexelConversions.h" #include "WebGLValidateStrings.h" +// needed to check if current OS is lower than 10.7 +#if defined(MOZ_WIDGET_COCOA) +#include "nsCocoaFeatures.h" +#endif + using namespace mozilla; static bool BaseTypeAndSizeFromUniformType(WebGLenum uType, WebGLenum *baseType, WebGLint *unitSize); @@ -4417,12 +4422,6 @@ WebGLContext::Viewport(WebGLint x, WebGLint y, WebGLsizei width, WebGLsizei heig return NS_OK; } -#ifdef XP_MACOSX -#define WEBGL_OS_IS_MAC 1 -#else -#define WEBGL_OS_IS_MAC 0 -#endif - NS_IMETHODIMP WebGLContext::CompileShader(nsIWebGLShader *sobj) { @@ -4480,9 +4479,11 @@ WebGLContext::CompileShader(nsIWebGLShader *sobj) int compileOptions = SH_OBJECT_CODE; +#ifdef XP_MACOSX // work around bug 665578 - if (WEBGL_OS_IS_MAC && gl->Vendor() == gl::GLContext::VendorATI) + if (!nsCocoaFeatures::OnLionOrLater() && gl->Vendor() == gl::GLContext::VendorATI) compileOptions |= SH_EMULATE_BUILT_IN_FUNCTIONS; +#endif if (!ShCompile(compiler, &s, 1, compileOptions)) { int len = 0; From 66c050cdbbdaf162a087630130a59b827a03bfee Mon Sep 17 00:00:00 2001 From: Taras Glek Date: Mon, 7 Nov 2011 16:35:58 -0800 Subject: [PATCH 38/46] Bug 668392: backout commit without bug# --- toolkit/components/telemetry/TelemetryPing.js | 55 +------------------ .../tests/unit/test_TelemetryPing.js | 53 ++++++------------ toolkit/mozapps/extensions/XPIProvider.jsm | 3 - 3 files changed, 18 insertions(+), 93 deletions(-) diff --git a/toolkit/components/telemetry/TelemetryPing.js b/toolkit/components/telemetry/TelemetryPing.js index 5eca67c92c35..05a1b5829bbc 100644 --- a/toolkit/components/telemetry/TelemetryPing.js +++ b/toolkit/components/telemetry/TelemetryPing.js @@ -40,7 +40,6 @@ const Cu = Components.utils; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/LightweightThemeManager.jsm"); // When modifying the payload in incompatible ways, please bump this version number const PAYLOAD_VERSION = 1; @@ -231,54 +230,6 @@ TelemetryPing.prototype = { h.add(val); }, - /** - * Descriptive metadata - * - * @param reason - * The reason for the telemetry ping, this will be included in the - * returned metadata, - * @return The metadata as a JS object - */ - getMetadata: function getMetadata(reason) { - let ai = Services.appinfo; - let ret = { - reason: reason, - OS: ai.OS, - appID: ai.ID, - appVersion: ai.version, - appName: ai.name, - appBuildID: ai.appBuildID, - platformBuildID: ai.platformBuildID, - }; - - // sysinfo fields are not always available, get what we can. - let sysInfo = Cc["@mozilla.org/system-info;1"].getService(Ci.nsIPropertyBag2); - let fields = ["cpucount", "memsize", "arch", "version", "device", "manufacturer", "hardware"]; - for each (let field in fields) { - let value; - try { - value = sysInfo.getProperty(field); - } catch (e) { - continue - } - if (field == "memsize") { - // Send RAM size in megabytes. Rounding because sysinfo doesn't - // always provide RAM in multiples of 1024. - value = Math.round(value / 1024 / 1024) - } - ret[field] = value - } - - let theme = LightweightThemeManager.currentTheme; - if (theme) - ret.persona = theme.id; - - if (this._addons) - ret.addons = this._addons; - - return ret; - }, - /** * Pull values from about:memory into corresponding histograms */ @@ -345,11 +296,10 @@ TelemetryPing.prototype = { this.gatherMemory(); let payload = { ver: PAYLOAD_VERSION, - info: this.getMetadata(reason), + info: getMetadata(reason), simpleMeasurements: getSimpleMeasurements(), histograms: getHistograms() }; - let isTestPing = (reason == "test-ping"); // Generate a unique id once per session so the server can cope with duplicate submissions. // Use a deterministic url for testing. @@ -455,9 +405,6 @@ TelemetryPing.prototype = { var server = this._server; switch (aTopic) { - case "Add-ons": - this._addons = aData; - break; case "profile-after-change": this.setup(); break; diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js index 4ba4d31e3a77..589850bd8d65 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryPing.js @@ -10,7 +10,6 @@ do_load_httpd_js(); Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/LightweightThemeManager.jsm"); const PATH = "/submit/telemetry/test-ping"; const SERVER = "http://localhost:4444"; @@ -22,7 +21,6 @@ const BinaryInputStream = Components.Constructor( "setInputStream"); var httpserver = new nsHttpServer(); -var gFinished = false; function telemetry_ping () { const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver); @@ -49,12 +47,27 @@ function telemetryObserver(aSubject, aTopic, aData) { telemetry_ping(); } +function run_test() { + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); + Services.obs.addObserver(nonexistentServerObserver, "telemetry-test-xhr-complete", false); + telemetry_ping(); + // spin the event loop + do_test_pending(); +} + +function readBytesFromInputStream(inputStream, count) { + if (!count) { + count = inputStream.available(); + } + return new BinaryInputStream(inputStream).readBytes(count); +} + function checkHistograms(request, response) { // do not need the http server anymore httpserver.stop(do_test_finished); - let s = request.bodyInputStream; + let s = request.bodyInputStream let payload = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON) - .decodeFromStream(s, s.available()); + .decode(readBytesFromInputStream(s)) do_check_eq(request.getHeader("content-type"), "application/json; charset=UTF-8"); do_check_true(payload.simpleMeasurements.uptime >= 0) @@ -90,7 +103,6 @@ function checkHistograms(request, response) { let tc = payload.histograms[TELEMETRY_SUCCESS] do_check_eq(uneval(tc), uneval(expected_tc)); - gFinished = true; } // copied from toolkit/mozapps/extensions/test/xpcshell/head_addons.js @@ -141,34 +153,3 @@ function createAppInfo(id, name, version, platformVersion) { registrar.registerFactory(XULAPPINFO_CID, "XULAppInfo", XULAPPINFO_CONTRACTID, XULAppInfoFactory); } - -function dummyTheme(id) { - return { - id: id, - name: Math.random().toString(), - headerURL: "http://lwttest.invalid/a.png", - footerURL: "http://lwttest.invalid/b.png", - textcolor: Math.random().toString(), - accentcolor: Math.random().toString() - }; -} - -function run_test() { - // Addon manager needs a profile directory - do_get_profile(); - createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2"); - // try to make LightweightThemeManager do stuff - let gInternalManager = Cc["@mozilla.org/addons/integration;1"] - .getService(Ci.nsIObserver) - .QueryInterface(Ci.nsITimerCallback); - - gInternalManager.observe(null, "addons-startup", null); - LightweightThemeManager.currentTheme = dummyTheme("1234"); - - Services.obs.addObserver(nonexistentServerObserver, "telemetry-test-xhr-complete", false); - telemetry_ping(); - // spin the event loop - do_test_pending(); - // ensure that test runs to completion - do_register_cleanup(function () do_check_true(gFinished)) - } diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm index e0a632cae36c..ad6816e54215 100644 --- a/toolkit/mozapps/extensions/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/XPIProvider.jsm @@ -1762,9 +1762,6 @@ var XPIProvider = { Services.appinfo.annotateCrashReport("Add-ons", data); } catch (e) { } - - const TelemetryPing = Cc["@mozilla.org/base/telemetry-ping;1"].getService(Ci.nsIObserver); - TelemetryPing.observe(null, "Add-ons", data); }, /** From 3e76d2137904681876b811cecc77677e716820fd Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Mon, 7 Nov 2011 16:28:37 -0800 Subject: [PATCH 39/46] Bug 700498 - Change StringBuffer::append to take a JSLinearString*, not a JSAtom*. r=cdleary --- js/src/jsstrinlines.h | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/js/src/jsstrinlines.h b/js/src/jsstrinlines.h index ef44b03efb32..c0bec371492a 100644 --- a/js/src/jsstrinlines.h +++ b/js/src/jsstrinlines.h @@ -77,7 +77,7 @@ class StringBuffer bool append(const jschar *chars, size_t len); bool append(const jschar *begin, const jschar *end); bool append(JSString *str); - bool append(JSAtom *atom); + bool append(JSLinearString *str); bool appendN(const jschar c, size_t n); bool appendInflated(const char *cstr, size_t len); @@ -173,19 +173,14 @@ StringBuffer::append(JSString *str) JSLinearString *linear = str->ensureLinear(context()); if (!linear) return false; - size_t strLen = linear->length(); - if (!checkLength(cb.length() + strLen)) - return false; - return cb.append(linear->chars(), strLen); + return append(linear); } inline bool -StringBuffer::append(JSAtom *atom) +StringBuffer::append(JSLinearString *str) { - size_t strLen = atom->length(); - if (!checkLength(cb.length() + strLen)) - return false; - return cb.append(atom->chars(), strLen); + JS::Anchor anch(str); + return cb.append(str->chars(), str->length()); } inline bool From f4e9d098851a97a89f0422456a258242cb23ed70 Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 4 Nov 2011 01:37:53 -0700 Subject: [PATCH 40/46] Remove js::tl::ArraySize and js::tl::ArrayEnd. They're unused, and mozilla::ArrayLength and mozilla::ArrayEnd supersede them regardless. No bug, r=lumpy --- js/public/TemplateLib.h | 4 ---- 1 file changed, 4 deletions(-) diff --git a/js/public/TemplateLib.h b/js/public/TemplateLib.h index ba3171def008..c94e57fdb5b6 100644 --- a/js/public/TemplateLib.h +++ b/js/public/TemplateLib.h @@ -171,10 +171,6 @@ template <> struct IsPodType { static const bool result = template <> struct IsPodType { static const bool result = true; }; template struct IsPodType { static const bool result = true; }; -/* Return the size/end of an array without using macros. */ -template inline T *ArraySize(T (&)[N]) { return N; } -template inline T *ArrayEnd(T (&arr)[N]) { return arr + N; } - template struct If { static const T result = v1; }; template struct If { static const T result = v2; }; From 3ed290d62c6cee3268e0cbefd5b78541e397ab8a Mon Sep 17 00:00:00 2001 From: Jeff Walden Date: Fri, 4 Nov 2011 18:36:40 -0700 Subject: [PATCH 41/46] Bug 699691 - Remove an unused argument from ino2name in pathsub.{c,h}. r=ted --- config/pathsub.c | 4 ++-- config/pathsub.h | 2 +- js/src/config/pathsub.c | 4 ++-- js/src/config/pathsub.h | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/config/pathsub.c b/config/pathsub.c index 8384e5fb9828..d8629b793405 100644 --- a/config/pathsub.c +++ b/config/pathsub.c @@ -128,7 +128,7 @@ struct dirent *readdir(DIR *d) #endif char * -ino2name(ino_t ino, char *dir) +ino2name(ino_t ino) { DIR *dp; struct dirent *ep; @@ -230,7 +230,7 @@ reversepath(char *inpath, char *name, int len, char *outpath) if (strcmp(buf, "..") == 0) { if (stat(".", &sb) < 0) fail("cannot stat current directory"); - name = ino2name(sb.st_ino, ".."); + name = ino2name(sb.st_ino); len = strlen(name); cp -= len + 1; strcpy(cp, name); diff --git a/config/pathsub.h b/config/pathsub.h index ab1a7fee2bfc..8fe11a9fa639 100644 --- a/config/pathsub.h +++ b/config/pathsub.h @@ -59,7 +59,7 @@ extern char *program; extern void fail(char *format, ...); extern char *getcomponent(char *path, char *name); -extern char *ino2name(ino_t ino, char *dir); +extern char *ino2name(ino_t ino); extern void *xmalloc(size_t size); extern char *xstrdup(char *s); extern char *xbasename(char *path); diff --git a/js/src/config/pathsub.c b/js/src/config/pathsub.c index 8384e5fb9828..d8629b793405 100644 --- a/js/src/config/pathsub.c +++ b/js/src/config/pathsub.c @@ -128,7 +128,7 @@ struct dirent *readdir(DIR *d) #endif char * -ino2name(ino_t ino, char *dir) +ino2name(ino_t ino) { DIR *dp; struct dirent *ep; @@ -230,7 +230,7 @@ reversepath(char *inpath, char *name, int len, char *outpath) if (strcmp(buf, "..") == 0) { if (stat(".", &sb) < 0) fail("cannot stat current directory"); - name = ino2name(sb.st_ino, ".."); + name = ino2name(sb.st_ino); len = strlen(name); cp -= len + 1; strcpy(cp, name); diff --git a/js/src/config/pathsub.h b/js/src/config/pathsub.h index ab1a7fee2bfc..8fe11a9fa639 100644 --- a/js/src/config/pathsub.h +++ b/js/src/config/pathsub.h @@ -59,7 +59,7 @@ extern char *program; extern void fail(char *format, ...); extern char *getcomponent(char *path, char *name); -extern char *ino2name(ino_t ino, char *dir); +extern char *ino2name(ino_t ino); extern void *xmalloc(size_t size); extern char *xstrdup(char *s); extern char *xbasename(char *path); From e65834716c4f14f4f3acbc446b757cfd73e07991 Mon Sep 17 00:00:00 2001 From: Tobias Markus Date: Tue, 8 Nov 2011 01:51:50 +0000 Subject: [PATCH 42/46] Bug 603332 - Add extra prefs to about:support whitelist to simplify lookup for common support issues; r=gavin.sharp --- toolkit/content/aboutSupport.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/toolkit/content/aboutSupport.js b/toolkit/content/aboutSupport.js index ee2d6e983ee2..4b65318a5d82 100644 --- a/toolkit/content/aboutSupport.js +++ b/toolkit/content/aboutSupport.js @@ -51,10 +51,10 @@ const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", // under the "accessibility.*" branch. const PREFS_WHITELIST = [ "accessibility.", + "browser.display.", "browser.fixup.", "browser.history_expire_", "browser.link.open_newwindow", - "browser.mousewheel.", "browser.places.", "browser.startup.homepage", "browser.tabs.", @@ -63,6 +63,7 @@ const PREFS_WHITELIST = [ "extensions.checkCompatibility", "extensions.lastAppVersion", "font.", + "general.autoScroll", "general.useragent.", "gfx.", "html5.", @@ -70,7 +71,9 @@ const PREFS_WHITELIST = [ "javascript.", "keyword.", "layout.css.dpi", + "mousewheel.", "network.", + "permissions.default.image", "places.", "plugin.", "plugins.", From 57feb291db01300badb2ca2fac31522d1ba31dda Mon Sep 17 00:00:00 2001 From: Chris Double Date: Tue, 8 Nov 2011 14:38:17 +1300 Subject: [PATCH 43/46] Bug 691096 - Provide hard limit to number of media elements that can be decoded in parallel - r=cpearce --HG-- extra : rebase_source : b82ea2e14028f625ea247e93ca6be427e5db815d --- .../media/nsBuiltinDecoderStateMachine.cpp | 230 +++++++++++++++--- content/media/nsBuiltinDecoderStateMachine.h | 4 +- 2 files changed, 201 insertions(+), 33 deletions(-) diff --git a/content/media/nsBuiltinDecoderStateMachine.cpp b/content/media/nsBuiltinDecoderStateMachine.cpp index 3e0cec3710a4..bcf0729295fc 100644 --- a/content/media/nsBuiltinDecoderStateMachine.cpp +++ b/content/media/nsBuiltinDecoderStateMachine.cpp @@ -165,13 +165,6 @@ public: const PRUint32 mRate; }; -static PRUint32 gStateMachineCount = 0; -static nsIThread* gStateMachineThread = 0; - -nsIThread* nsBuiltinDecoderStateMachine::GetStateMachineThread() { - return gStateMachineThread; -} - // Shuts down a thread asynchronously. class ShutdownThreadEvent : public nsRunnable { @@ -187,6 +180,161 @@ private: nsCOMPtr mThread; }; +// Owns the global state machine thread and counts of +// state machine and decoder threads. There should +// only be one instance of this class. +class StateMachineTracker +{ +private: + StateMachineTracker() : + mMonitor("media.statemachinetracker"), + mStateMachineCount(0), + mDecodeThreadCount(0), + mStateMachineThread(nsnull) + { + MOZ_COUNT_CTOR(StateMachineTracker); + NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); + } + + ~StateMachineTracker() + { + NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); + + MOZ_COUNT_DTOR(StateMachineTracker); + } + +public: + // Access singleton instance. This is initially called on the main + // thread in the nsBuiltinDecoderStateMachine constructor resulting + // in the global object being created lazily. Non-main thread + // access always occurs after this and uses the monitor to + // safely access the decode thread counts. + static StateMachineTracker& Instance(); + + // Instantiate the global state machine thread if required. + // Call on main thread only. + void EnsureGlobalStateMachine(); + + // Destroy global state machine thread if required. + // Call on main thread only. + void CleanupGlobalStateMachine(); + + // Return the global state machine thread. Call from any thread. + nsIThread* GetGlobalStateMachineThread() + { + ReentrantMonitorAutoEnter mon(mMonitor); + NS_ASSERTION(mStateMachineThread, "Should have non-null state machine thread!"); + return mStateMachineThread; + } + + // Maximum number of active decode threads allowed. When more + // than this number are active the thread creation will fail. + static const PRUint32 MAX_DECODE_THREADS = 25; + + // Returns the number of active decode threads. + // Call on any thread. Holds the internal monitor so don't + // call with any other monitor held to avoid deadlock. + PRUint32 GetDecodeThreadCount(); + + // Keep track of the fact that a decode thread was created. + // Call on any thread. Holds the internal monitor so don't + // call with any other monitor held to avoid deadlock. + void NoteDecodeThreadCreated(); + + // Keep track of the fact that a decode thread was destroyed. + // Call on any thread. Holds the internal monitor so don't + // call with any other monitor held to avoid deadlock. + void NoteDecodeThreadDestroyed(); + +private: + // Holds global instance of StateMachineTracker. + // Writable on main thread only. + static StateMachineTracker* mInstance; + + // Reentrant monitor that must be obtained to access + // the decode thread count member and methods. + ReentrantMonitor mMonitor; + + // Number of instances of nsBuiltinDecoderStateMachine + // that are currently instantiated. Access on the + // main thread only. + PRUint32 mStateMachineCount; + + // Number of instances of decoder threads that are + // currently instantiated. Access only with the + // mMonitor lock held. Can be used from any thread. + PRUint32 mDecodeThreadCount; + + // Global state machine thread. Write on the main thread + // only, read from the decoder threads. Synchronized via + // the mMonitor. + nsIThread* mStateMachineThread; +}; + +StateMachineTracker* StateMachineTracker::mInstance = nsnull; + +StateMachineTracker& StateMachineTracker::Instance() +{ + if (!mInstance) { + NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); + mInstance = new StateMachineTracker(); + } + return *mInstance; +} + +void StateMachineTracker::EnsureGlobalStateMachine() +{ + NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); + ReentrantMonitorAutoEnter mon(mMonitor); + if (mStateMachineCount == 0) { + NS_ASSERTION(!mStateMachineThread, "Should have null state machine thread!"); + nsresult res = NS_NewThread(&mStateMachineThread, + nsnull); + NS_ABORT_IF_FALSE(NS_SUCCEEDED(res), "Can't create media state machine thread"); + } + mStateMachineCount++; +} + +void StateMachineTracker::CleanupGlobalStateMachine() +{ + NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); + NS_ABORT_IF_FALSE(mStateMachineCount > 0, + "State machine ref count must be > 0"); + mStateMachineCount--; + if (mStateMachineCount == 0) { + LOG(PR_LOG_DEBUG, ("Destroying media state machine thread")); + { + ReentrantMonitorAutoEnter mon(mMonitor); + nsCOMPtr event = new ShutdownThreadEvent(mStateMachineThread); + NS_RELEASE(mStateMachineThread); + mStateMachineThread = nsnull; + NS_DispatchToMainThread(event); + + NS_ASSERTION(mDecodeThreadCount == 0, "Decode thread count must be zero."); + mInstance = nsnull; + } + delete this; + } +} + +void StateMachineTracker::NoteDecodeThreadCreated() +{ + ReentrantMonitorAutoEnter mon(mMonitor); + ++mDecodeThreadCount; +} + +void StateMachineTracker::NoteDecodeThreadDestroyed() +{ + ReentrantMonitorAutoEnter mon(mMonitor); + --mDecodeThreadCount; +} + +PRUint32 StateMachineTracker::GetDecodeThreadCount() +{ + ReentrantMonitorAutoEnter mon(mMonitor); + return mDecodeThreadCount; +} + nsBuiltinDecoderStateMachine::nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDecoder, nsBuiltinDecoderReader* aReader, bool aRealTime) : @@ -221,14 +369,8 @@ nsBuiltinDecoderStateMachine::nsBuiltinDecoderStateMachine(nsBuiltinDecoder* aDe { MOZ_COUNT_CTOR(nsBuiltinDecoderStateMachine); NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); - if (gStateMachineCount == 0) { - NS_ASSERTION(!gStateMachineThread, "Should have null state machine thread!"); - nsresult res = NS_NewThread(&gStateMachineThread, - nsnull, - MEDIA_THREAD_STACK_SIZE); - NS_ABORT_IF_FALSE(NS_SUCCEEDED(res), "Can't create media state machine thread"); - } - gStateMachineCount++; + + StateMachineTracker::Instance().EnsureGlobalStateMachine(); // only enable realtime mode when "media.realtime_decoder.enabled" is true. if (Preferences::GetBool("media.realtime_decoder.enabled", false) == false) @@ -245,18 +387,8 @@ nsBuiltinDecoderStateMachine::~nsBuiltinDecoderStateMachine() if (mTimer) mTimer->Cancel(); mTimer = nsnull; - - NS_ASSERTION(NS_IsMainThread(), "Should be on main thread."); - NS_ABORT_IF_FALSE(gStateMachineCount > 0, - "State machine ref count must be > 0"); - gStateMachineCount--; - if (gStateMachineCount == 0) { - LOG(PR_LOG_DEBUG, ("Destroying media state machine thread")); - nsCOMPtr event = new ShutdownThreadEvent(gStateMachineThread); - NS_RELEASE(gStateMachineThread); - gStateMachineThread = nsnull; - NS_DispatchToMainThread(event); - } + + StateMachineTracker::Instance().CleanupGlobalStateMachine(); } bool nsBuiltinDecoderStateMachine::HasFutureAudio() const { @@ -1056,6 +1188,7 @@ void nsBuiltinDecoderStateMachine::StopDecodeThread() { ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor()); mDecodeThread->Shutdown(); + StateMachineTracker::Instance().NoteDecodeThreadDestroyed(); } mDecodeThread = nsnull; mDecodeThreadIdle = false; @@ -1082,23 +1215,48 @@ nsBuiltinDecoderStateMachine::StartDecodeThread() { NS_ASSERTION(OnStateMachineThread(), "Should be on state machine thread."); mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); + PRUint32 count = 0; + bool created = false; + { + ReentrantMonitorAutoExit mon(mDecoder->GetReentrantMonitor()); + count = StateMachineTracker::Instance().GetDecodeThreadCount(); + } + mStopDecodeThread = false; if ((mDecodeThread && !mDecodeThreadIdle) || mState >= DECODER_STATE_COMPLETED) return NS_OK; + if (!mDecodeThread && count > StateMachineTracker::MAX_DECODE_THREADS) { + // Have to run one iteration of the state machine loop to ensure the + // shutdown state is processed. + ScheduleStateMachine(); + mState = DECODER_STATE_SHUTDOWN; + return NS_ERROR_FAILURE; + } + if (!mDecodeThread) { nsresult rv = NS_NewThread(getter_AddRefs(mDecodeThread), nsnull, MEDIA_THREAD_STACK_SIZE); if (NS_FAILED(rv)) { + // Have to run one iteration of the state machine loop to ensure the + // shutdown state is processed. + ScheduleStateMachine(); mState = DECODER_STATE_SHUTDOWN; return rv; } + created = true; } nsCOMPtr event = NS_NewRunnableMethod(this, &nsBuiltinDecoderStateMachine::DecodeThreadRun); mDecodeThread->Dispatch(event, NS_DISPATCH_NORMAL); mDecodeThreadIdle = false; + + if (created) { + ReentrantMonitorAutoExit mon(mDecoder->GetReentrantMonitor()); + StateMachineTracker::Instance().NoteDecodeThreadCreated(); + } + return NS_OK; } @@ -1974,7 +2132,7 @@ nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine() { nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine(PRInt64 aUsecs) { mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); - NS_ABORT_IF_FALSE(gStateMachineThread, + NS_ABORT_IF_FALSE(GetStateMachineThread(), "Must have a state machine thread to schedule"); if (mState == DECODER_STATE_SHUTDOWN) { @@ -2009,7 +2167,7 @@ nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine(PRInt64 aUsecs) { // We're not currently running this state machine on the state machine // thread. Dispatch an event to run one cycle of the state machine. mDispatchedRunEvent = true; - return gStateMachineThread->Dispatch(this, NS_DISPATCH_NORMAL); + return GetStateMachineThread()->Dispatch(this, NS_DISPATCH_NORMAL); } // We're not currently running this state machine on the state machine // thread, but something has already dispatched an event to run it again, @@ -2023,7 +2181,7 @@ nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine(PRInt64 aUsecs) { if (!mTimer) { mTimer = do_CreateInstance("@mozilla.org/timer;1", &res); if (NS_FAILED(res)) return res; - mTimer->SetTarget(gStateMachineThread); + mTimer->SetTarget(GetStateMachineThread()); } res = mTimer->InitWithFuncCallback(::TimeoutExpired, @@ -2032,3 +2190,15 @@ nsresult nsBuiltinDecoderStateMachine::ScheduleStateMachine(PRInt64 aUsecs) { nsITimer::TYPE_ONE_SHOT); return res; } + +bool nsBuiltinDecoderStateMachine::OnStateMachineThread() const +{ + return IsCurrentThread(GetStateMachineThread()); +} + +nsIThread* nsBuiltinDecoderStateMachine::GetStateMachineThread() +{ + return StateMachineTracker::Instance().GetGlobalStateMachineThread(); +} + + diff --git a/content/media/nsBuiltinDecoderStateMachine.h b/content/media/nsBuiltinDecoderStateMachine.h index f0baada6a207..4a01a99562ee 100644 --- a/content/media/nsBuiltinDecoderStateMachine.h +++ b/content/media/nsBuiltinDecoderStateMachine.h @@ -205,9 +205,7 @@ public: return IsCurrentThread(mAudioThread); } - bool OnStateMachineThread() const { - return IsCurrentThread(GetStateMachineThread()); - } + bool OnStateMachineThread() const; nsresult GetBuffered(nsTimeRanges* aBuffered); From f4dc7146c28edfaab688a03922cc9877f2826f42 Mon Sep 17 00:00:00 2001 From: Chris Double Date: Tue, 8 Nov 2011 14:38:22 +1300 Subject: [PATCH 44/46] Bug 691096 - Add test - r=cpearce --HG-- extra : rebase_source : 076426e90960bbd0d60fc2186a93722734677d89 --- content/media/test/crashtests/691096-1.html | 26 +++++++++++++++++++ content/media/test/crashtests/crashtests.list | 1 + 2 files changed, 27 insertions(+) create mode 100644 content/media/test/crashtests/691096-1.html diff --git a/content/media/test/crashtests/691096-1.html b/content/media/test/crashtests/691096-1.html new file mode 100644 index 000000000000..fd6dbbb5c723 --- /dev/null +++ b/content/media/test/crashtests/691096-1.html @@ -0,0 +1,26 @@ + + + + + + + diff --git a/content/media/test/crashtests/crashtests.list b/content/media/test/crashtests/crashtests.list index dfcfdc34046e..42ff46579fef 100644 --- a/content/media/test/crashtests/crashtests.list +++ b/content/media/test/crashtests/crashtests.list @@ -8,3 +8,4 @@ load 493915-1.html skip-if(Android) load 495794-1.html load 492286-1.xhtml load 576612-1.html +load 691096-1.html From 5dec9d83e189ec62507400a43c31fe30b73ae878 Mon Sep 17 00:00:00 2001 From: Justin Lebar Date: Mon, 7 Nov 2011 16:32:36 -0500 Subject: [PATCH 45/46] Bug 699395 - Modify zone_good_size to call je_malloc_usable_size_in_advance. r=njn --HG-- extra : rebase_source : 24feb93e8ceebd74a8dbcb0084f7f27ecf6495e3 --- memory/jemalloc/jemalloc.c | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/memory/jemalloc/jemalloc.c b/memory/jemalloc/jemalloc.c index f676fb45852a..e33c6bf49469 100644 --- a/memory/jemalloc/jemalloc.c +++ b/memory/jemalloc/jemalloc.c @@ -6878,24 +6878,7 @@ zone_destroy(malloc_zone_t *zone) static size_t zone_good_size(malloc_zone_t *zone, size_t size) { - size_t ret; - void *p; - - /* - * Actually create an object of the appropriate size, then find out - * how large it could have been without moving up to the next size - * class. - * - * I sure hope this doesn't get called often. - */ - p = malloc(size); - if (p != NULL) { - ret = isalloc(p); - free(p); - } else - ret = size; - - return (ret); + return je_malloc_usable_size_in_advance(size); } static size_t From dc33fffa655079488312aedf957b16de02406f21 Mon Sep 17 00:00:00 2001 From: Chris Jones Date: Fri, 30 Sep 2011 00:00:48 -0700 Subject: [PATCH 46/46] Bug 679966, part 1: Add vibrator support for android. r=blassey,cjones --- content/base/src/Makefile.in | 1 + content/events/src/Makefile.in | 1 + dom/ipc/ContentChild.cpp | 11 ++ dom/ipc/ContentChild.h | 12 ++ dom/ipc/ContentParent.cpp | 4 + dom/ipc/PContent.ipdl | 2 + dom/ipc/TabChild.h | 8 + dom/ipc/TabParent.cpp | 9 + dom/ipc/TabParent.h | 8 + embedding/android/AndroidManifest.xml.in | 1 + embedding/android/GeckoAppShell.java | 16 ++ hal/Hal.cpp | 218 ++++++++++++++++++++++- hal/Hal.h | 143 ++++++++++++++- hal/android/AndroidHal.cpp | 31 +++- hal/fallback/FallbackHal.cpp | 8 +- hal/linux/LinuxHal.cpp | 6 +- hal/sandbox/PHal.ipdl | 4 +- hal/sandbox/SandboxHal.cpp | 55 +++++- widget/src/android/AndroidBridge.cpp | 65 ++++++- widget/src/android/AndroidBridge.h | 6 + 20 files changed, 586 insertions(+), 23 deletions(-) diff --git a/content/base/src/Makefile.in b/content/base/src/Makefile.in index ef44f3c7ec4a..1b984be36685 100644 --- a/content/base/src/Makefile.in +++ b/content/base/src/Makefile.in @@ -64,6 +64,7 @@ EXPORTS = \ nsStubMutationObserver.h \ nsTextFragment.h \ mozAutoDocUpdate.h \ + nsFrameMessageManager.h \ $(NULL) EXPORTS_NAMESPACES = mozilla/dom diff --git a/content/events/src/Makefile.in b/content/events/src/Makefile.in index 49c21bf53681..8c4458028824 100644 --- a/content/events/src/Makefile.in +++ b/content/events/src/Makefile.in @@ -49,6 +49,7 @@ LIBXUL_LIBRARY = 1 EXPORTS = \ nsEventStateManager.h \ nsEventListenerManager.h \ + nsDOMEventTargetHelper.h \ $(NULL) CPPSRCS = \ diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index f6bc2ad7c2f9..c7d339fecd4e 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -233,6 +233,7 @@ ContentChild* ContentChild::sSingleton; ContentChild::ContentChild() #ifdef ANDROID : mScreenSize(0, 0) + , mID(PRUint64(-1)) #endif { } @@ -803,5 +804,15 @@ ContentChild::RecvAppInfo(const nsCString& version, const nsCString& buildID) return true; } +bool +ContentChild::RecvSetID(const PRUint64 &id) +{ + if (mID != PRUint64(-1)) { + NS_WARNING("Setting content child's ID twice?"); + } + mID = id; + return true; +} + } // namespace dom } // namespace mozilla diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index 0f6c9a595fda..9da90f383709 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -168,6 +168,7 @@ public: virtual bool RecvCycleCollect(); virtual bool RecvAppInfo(const nsCString& version, const nsCString& buildID); + virtual bool RecvSetID(const PRUint64 &id); #ifdef ANDROID gfxIntSize GetScreenSize() { return mScreenSize; } @@ -177,6 +178,8 @@ public: // cache the value nsString &GetIndexedDBPath(); + PRUint64 GetID() { return mID; } + private: NS_OVERRIDE virtual void ActorDestroy(ActorDestroyReason why); @@ -198,6 +201,15 @@ private: AppInfo mAppInfo; + /** + * An ID unique to the process containing our corresponding + * content parent. + * + * We expect our content parent to set this ID immediately after opening a + * channel to us. + */ + PRUint64 mID; + static ContentChild* sSingleton; DISALLOW_EVIL_CONSTRUCTORS(ContentChild); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 5ae154217db1..e99ab420964c 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -162,6 +162,9 @@ MemoryReportRequestParent::~MemoryReportRequestParent() nsTArray* ContentParent::gContentParents; +// The first content child has ID 1, so the chrome process can have ID 0. +static PRUint64 gContentChildID = 1; + ContentParent* ContentParent::GetNewOrUsed() { @@ -424,6 +427,7 @@ ContentParent::ContentParent() mSubprocess = new GeckoChildProcessHost(GeckoProcessType_Content); mSubprocess->AsyncLaunch(); Open(mSubprocess->GetChannel(), mSubprocess->GetChildProcessHandle()); + unused << SendSetID(gContentChildID++); nsCOMPtr registrySvc = nsChromeRegistry::GetService(); nsChromeRegistryChrome* chromeRegistry = diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index e6de9465d0d4..f51f636e81a1 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -147,6 +147,8 @@ child: AppInfo(nsCString version, nsCString buildID); + SetID(PRUint64 id); + parent: PAudio(PRInt32 aNumChannels, PRInt32 aRate, PRInt32 aFormat); diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index 7293a22a577d..379b074d12a7 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -303,6 +303,14 @@ GetTabChildFrom(nsIPresShell* aPresShell) return GetTabChildFrom(docShell); } +inline TabChild* +GetTabChildFrom(nsIDOMWindow* aWindow) +{ + nsCOMPtr webNav = do_GetInterface(aWindow); + nsCOMPtr docShell = do_QueryInterface(webNav); + return GetTabChildFrom(docShell); +} + } } diff --git a/dom/ipc/TabParent.cpp b/dom/ipc/TabParent.cpp index 844a47856b48..f139b7f75bcb 100644 --- a/dom/ipc/TabParent.cpp +++ b/dom/ipc/TabParent.cpp @@ -91,6 +91,7 @@ TabParent::TabParent() , mIMECompositionEnding(false) , mIMESeqno(0) , mDPI(0) + , mActive(false) { } @@ -217,15 +218,23 @@ TabParent::UpdateDimensions(const nsRect& rect, const nsIntSize& size) void TabParent::Activate() { + mActive = true; unused << SendActivate(); } void TabParent::Deactivate() { + mActive = false; unused << SendDeactivate(); } +bool +TabParent::Active() +{ + return mActive; +} + NS_IMETHODIMP TabParent::Init(nsIDOMWindow *window) { diff --git a/dom/ipc/TabParent.h b/dom/ipc/TabParent.h index e8673b777c13..41853218cc66 100644 --- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -131,6 +131,13 @@ public: void UpdateDimensions(const nsRect& rect, const nsIntSize& size); void Activate(); void Deactivate(); + + /** + * Is this object active? That is, was Activate() called more recently than + * Deactivate()? + */ + bool Active(); + void SendMouseEvent(const nsAString& aType, float aX, float aY, PRInt32 aButton, PRInt32 aClickCount, PRInt32 aModifiers, bool aIgnoreRootScrollFrame); @@ -221,6 +228,7 @@ protected: PRUint32 mIMESeqno; float mDPI; + bool mActive; private: already_AddRefed GetFrameLoader() const; diff --git a/embedding/android/AndroidManifest.xml.in b/embedding/android/AndroidManifest.xml.in index 856e39e6a9ba..dad788ef5ec8 100644 --- a/embedding/android/AndroidManifest.xml.in +++ b/embedding/android/AndroidManifest.xml.in @@ -22,6 +22,7 @@ + diff --git a/embedding/android/GeckoAppShell.java b/embedding/android/GeckoAppShell.java index 0040b1e11f82..35e5102026ae 100644 --- a/embedding/android/GeckoAppShell.java +++ b/embedding/android/GeckoAppShell.java @@ -1043,6 +1043,22 @@ public class GeckoAppShell HapticFeedbackConstants.VIRTUAL_KEY); } + private static Vibrator vibrator() { + return (Vibrator) GeckoApp.surfaceView.getContext().getSystemService(Context.VIBRATOR_SERVICE); + } + + public static void vibrate(long milliseconds) { + vibrator().vibrate(milliseconds); + } + + public static void vibrate(long[] pattern, int repeat) { + vibrator().vibrate(pattern, repeat); + } + + public static void cancelVibrate() { + vibrator().cancel(); + } + public static void showInputMethodPicker() { InputMethodManager imm = (InputMethodManager) GeckoApp.surfaceView.getContext().getSystemService(Context.INPUT_METHOD_SERVICE); imm.showInputMethodPicker(); diff --git a/hal/Hal.cpp b/hal/Hal.cpp index 050e15415026..e02520aa4057 100644 --- a/hal/Hal.cpp +++ b/hal/Hal.cpp @@ -1,5 +1,5 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: sw=2 ts=8 et ft=cpp : */ +/* vim: set sw=2 ts=8 et ft=cpp : */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -38,10 +38,24 @@ * ***** END LICENSE BLOCK ***** */ #include "Hal.h" +#include "mozilla/dom/ContentChild.h" +#include "mozilla/dom/TabChild.h" #include "mozilla/Util.h" #include "nsThreadUtils.h" #include "nsXULAppAPI.h" #include "mozilla/Observer.h" +#include "nsIDOMDocument.h" +#include "nsIDOMWindow.h" +#include "nsPIDOMWindow.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "mozilla/Services.h" +#include "nsIWebNavigation.h" +#include "nsITabChild.h" +#include "nsIDocShell.h" + +using namespace mozilla::dom; +using namespace mozilla::services; #define PROXY_IF_SANDBOXED(_call) \ do { \ @@ -55,25 +69,215 @@ namespace mozilla { namespace hal { -static void +PRLogModuleInfo *sHalLog = PR_LOG_DEFINE("hal"); + +namespace { + +void AssertMainThread() { MOZ_ASSERT(NS_IsMainThread()); } -static bool +bool InSandbox() { return GeckoProcessType_Content == XRE_GetProcessType(); } -void -Vibrate(const nsTArray& pattern) +bool +WindowIsActive(nsIDOMWindow *window) { - AssertMainThread(); - PROXY_IF_SANDBOXED(Vibrate(pattern)); + NS_ENSURE_TRUE(window, false); + + nsCOMPtr doc; + window->GetDocument(getter_AddRefs(doc)); + NS_ENSURE_TRUE(doc, false); + + bool hidden = true; + doc->GetMozHidden(&hidden); + return !hidden; } +nsAutoPtr gLastIDToVibrate; + +// This observer makes sure we delete gLastIDToVibrate, so we don't +// leak. +class ShutdownObserver : public nsIObserver +{ +public: + ShutdownObserver() {} + virtual ~ShutdownObserver() {} + + NS_DECL_ISUPPORTS + + NS_IMETHOD Observe(nsISupports *subject, const char *aTopic, + const PRUnichar *aData) + { + MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0); + gLastIDToVibrate = nsnull; + return NS_OK; + } +}; + +NS_IMPL_ISUPPORTS1(ShutdownObserver, nsIObserver); + +void InitLastIDToVibrate() +{ + gLastIDToVibrate = new WindowIdentifier::IDArrayType(); + + nsCOMPtr observerService = GetObserverService(); + if (!observerService) { + NS_WARNING("Could not get observer service!"); + return; + } + + ShutdownObserver *obs = new ShutdownObserver(); + observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false); +} + +} // anonymous namespace + +WindowIdentifier::WindowIdentifier() + : mWindow(nsnull) + , mIsEmpty(true) +{ +} + +WindowIdentifier::WindowIdentifier(nsIDOMWindow *window) + : mWindow(window) + , mIsEmpty(false) +{ + mID.AppendElement(GetWindowID()); +} + +WindowIdentifier::WindowIdentifier(nsCOMPtr &window) + : mWindow(window) + , mIsEmpty(false) +{ + mID.AppendElement(GetWindowID()); +} + +WindowIdentifier::WindowIdentifier(const nsTArray &id, nsIDOMWindow *window) + : mID(id) + , mWindow(window) + , mIsEmpty(false) +{ + mID.AppendElement(GetWindowID()); +} + +WindowIdentifier::WindowIdentifier(const WindowIdentifier &other) + : mID(other.mID) + , mWindow(other.mWindow) + , mIsEmpty(other.mIsEmpty) +{ +} + +const InfallibleTArray& +WindowIdentifier::AsArray() const +{ + MOZ_ASSERT(!mIsEmpty); + return mID; +} + +bool +WindowIdentifier::HasTraveledThroughIPC() const +{ + MOZ_ASSERT(!mIsEmpty); + return mID.Length() >= 2; +} + +void +WindowIdentifier::AppendProcessID() +{ + MOZ_ASSERT(!mIsEmpty); + mID.AppendElement(ContentChild::GetSingleton()->GetID()); +} + +uint64 +WindowIdentifier::GetWindowID() const +{ + MOZ_ASSERT(!mIsEmpty); + nsCOMPtr pidomWindow = do_QueryInterface(mWindow); + if (!pidomWindow) { + return uint64(-1); + } + return pidomWindow->WindowID(); +} + +nsIDOMWindow* +WindowIdentifier::GetWindow() const +{ + MOZ_ASSERT(!mIsEmpty); + return mWindow; +} + +void +Vibrate(const nsTArray& pattern, const WindowIdentifier &id) +{ + AssertMainThread(); + + // Only active windows may start vibrations. If |id| hasn't gone + // through the IPC layer -- that is, if our caller is the outside + // world, not hal_proxy -- check whether the window is active. If + // |id| has gone through IPC, don't check the window's visibility; + // only the window corresponding to the bottommost process has its + // visibility state set correctly. + if (!id.HasTraveledThroughIPC() && !WindowIsActive(id.GetWindow())) { + HAL_LOG(("Vibrate: Window is inactive, dropping vibrate.")); + return; + } + + if (InSandbox()) { + hal_sandbox::Vibrate(pattern, id); + } + else { + if (!gLastIDToVibrate) + InitLastIDToVibrate(); + *gLastIDToVibrate = id.AsArray(); + + HAL_LOG(("Vibrate: Forwarding to hal_impl.")); + + // hal_impl doesn't need |id|. Send it an empty id, which will + // assert if it's used. + hal_impl::Vibrate(pattern, WindowIdentifier()); + } +} + +void +CancelVibrate(const WindowIdentifier &id) +{ + AssertMainThread(); + + // Although only active windows may start vibrations, a window may + // cancel its own vibration even if it's no longer active. + // + // After a window is marked as inactive, it sends a CancelVibrate + // request. We want this request to cancel a playing vibration + // started by that window, so we certainly don't want to reject the + // cancellation request because the window is now inactive. + // + // But it could be the case that, after this window became inactive, + // some other window came along and started a vibration. We don't + // want this window's cancellation request to cancel that window's + // actively-playing vibration! + // + // To solve this problem, we keep track of the id of the last window + // to start a vibration, and only accepts cancellation requests from + // the same window. All other cancellation requests are ignored. + + if (InSandbox()) { + hal_sandbox::CancelVibrate(id); + } + else if (*gLastIDToVibrate == id.AsArray()) { + // Don't forward our ID to hal_impl. It doesn't need it, and we + // don't want it to be tempted to read it. The empty identifier + // will assert if it's used. + HAL_LOG(("CancelVibrate: Forwarding to hal_impl.")); + hal_impl::CancelVibrate(WindowIdentifier()); + } +} + class BatteryObserversManager { public: diff --git a/hal/Hal.h b/hal/Hal.h index 4df365837ab5..209f7d7ca7e8 100644 --- a/hal/Hal.h +++ b/hal/Hal.h @@ -1,5 +1,5 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: sw=2 ts=8 et ft=cpp : */ +/* vim: set sw=2 ts=8 et ft=cpp : */ /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * @@ -43,9 +43,120 @@ #include "base/basictypes.h" #include "mozilla/Types.h" #include "nsTArray.h" +#include "nsCOMPtr.h" +#include "nsIDOMWindow.h" +#include "prlog.h" #include "mozilla/dom/battery/Types.h" #ifndef MOZ_HAL_NAMESPACE + +namespace mozilla { +namespace dom { +class TabChild; +class PBrowserChild; +} +} + +// Only include this hunk of code once, and include it before +// HalImpl.h and HalSandbox.h. +namespace mozilla { +namespace hal { + +extern PRLogModuleInfo *sHalLog; +#define HAL_LOG(msg) PR_LOG(sHalLog, PR_LOG_DEBUG, msg) + +/** + * This class serves two purposes. + * + * First, this class wraps a pointer to a window. + * + * Second, WindowIdentifier lets us uniquely identify a window across + * processes. A window exposes an ID which is unique only within its + * process. Thus to identify a window, we need to know the ID of the + * process which contains it. But the scope of a process's ID is its + * parent; that is, two processes with different parents might have + * the same ID. + * + * So to identify a window, we need its ID plus the IDs of all the + * processes in the path from the window's process to the root + * process. We throw in the IDs of the intermediate windows (a + * content window is contained in a window at each level of the + * process tree) for good measures. + * + * You can access this list of IDs by calling AsArray(). + */ +class WindowIdentifier +{ +public: + /** + * Create an empty WindowIdentifier. Calls to any of this object's + * public methods will assert -- an empty WindowIdentifier may be + * used only as a placeholder to code which promises not to touch + * the object. + */ + WindowIdentifier(); + + /** + * Copy constructor. + */ + WindowIdentifier(const WindowIdentifier& other); + + /** + * Wrap the given window in a WindowIdentifier. These two + * constructors automatically grab the window's ID and append it to + * the array of IDs. + * + * Note that these constructors allow an implicit conversion to a + * WindowIdentifier. + */ + WindowIdentifier(nsIDOMWindow* window); + WindowIdentifier(nsCOMPtr &window); + + /** + * Create a new WindowIdentifier with the given id array and window. + * This automatically grabs the window's ID and appends it to the + * array. + */ + WindowIdentifier(const nsTArray& id, nsIDOMWindow* window); + + /** + * Get the list of window and process IDs we contain. + */ + typedef InfallibleTArray IDArrayType; + const IDArrayType& AsArray() const; + + /** + * Append the ID of the ContentChild singleton to our array of + * window/process IDs. + */ + void AppendProcessID(); + + /** + * Does this WindowIdentifier identify both a window and the process + * containing that window? If so, we say it has traveled through + * IPC. + */ + bool HasTraveledThroughIPC() const; + + /** + * Get the window this object wraps. + */ + nsIDOMWindow* GetWindow() const; + +private: + /** + * Get the ID of the window object we wrap. + */ + uint64 GetWindowID() const; + + AutoInfallibleTArray mID; + nsCOMPtr mWindow; + bool mIsEmpty; +}; + +} // namespace hal +} // namespace mozilla + // This goop plays some cpp tricks to ensure a uniform API across the // API entry point, "sandbox" implementations (for content processes), // and "impl" backends where the real work happens. After this runs @@ -78,8 +189,32 @@ namespace MOZ_HAL_NAMESPACE /*hal*/ { * |pattern| is an "on" element, the next is "off", and so on. * * If |pattern| is empty, any in-progress vibration is canceled. + * + * Only an active window within an active tab may call Vibrate; calls + * from inactive windows and windows on inactive tabs do nothing. + * + * If you're calling hal::Vibrate from the outside world, pass an + * nsIDOMWindow* or an nsCOMPtr& in place of the + * WindowIdentifier parameter. It'll be converted to a WindowIdentifier + * automatically. */ -void Vibrate(const nsTArray& pattern); +void Vibrate(const nsTArray& pattern, + const hal::WindowIdentifier &id); + +/** + * Cancel a vibration started by the content window identified by + * WindowIdentifier. + * + * If the window was the last window to start a vibration, the + * cancellation request will go through even if the window is not + * active. + * + * As with hal::Vibrate(), if you're calling hal::CancelVibrate from + * the outside world, pass an nsIDOMWindow* or an + * nsCOMPtr&. This will automatically be converted to a + * WindowIdentifier object. + */ +void CancelVibrate(const hal::WindowIdentifier &id); /** * Inform the battery backend there is a new battery observer. @@ -122,8 +257,8 @@ void GetCurrentBatteryInformation(hal::BatteryInformation* aBatteryInfo); */ void NotifyBatteryChange(const hal::BatteryInformation& aBatteryInfo); -} -} +} // namespace MOZ_HAL_NAMESPACE +} // namespace mozilla #ifdef MOZ_DEFINED_HAL_NAMESPACE # undef MOZ_DEFINED_HAL_NAMESPACE diff --git a/hal/android/AndroidHal.cpp b/hal/android/AndroidHal.cpp index 143f434d3098..2e35834dbc2f 100644 --- a/hal/android/AndroidHal.cpp +++ b/hal/android/AndroidHal.cpp @@ -38,12 +38,39 @@ #include "Hal.h" #include "AndroidBridge.h" +using mozilla::hal::WindowIdentifier; + namespace mozilla { namespace hal_impl { void -Vibrate(const nsTArray& pattern) -{} +Vibrate(const nsTArray &pattern, const WindowIdentifier &) +{ + // Ignore the WindowIdentifier parameter; it's here only because hal::Vibrate, + // hal_sandbox::Vibrate, and hal_impl::Vibrate all must have the same + // signature. + + AndroidBridge* b = AndroidBridge::Bridge(); + if (!b) { + return; + } + + if (pattern.Length() == 0) { + b->CancelVibrate(); + } else { + b->Vibrate(pattern); + } +} + +void +CancelVibrate(const WindowIdentifier &) +{ + // Ignore WindowIdentifier parameter. + + AndroidBridge* b = AndroidBridge::Bridge(); + if (b) + b->CancelVibrate(); +} void EnableBatteryNotifications() diff --git a/hal/fallback/FallbackHal.cpp b/hal/fallback/FallbackHal.cpp index 0f36ebf3bf22..1cb7a5aa8208 100644 --- a/hal/fallback/FallbackHal.cpp +++ b/hal/fallback/FallbackHal.cpp @@ -40,11 +40,17 @@ #include "Hal.h" #include "mozilla/dom/battery/Constants.h" +using mozilla::hal::WindowIdentifier; + namespace mozilla { namespace hal_impl { void -Vibrate(const nsTArray& pattern) +Vibrate(const nsTArray& pattern, const hal::WindowIdentifier &) +{} + +void +CancelVibrate(const hal::WindowIdentifier &) {} void diff --git a/hal/linux/LinuxHal.cpp b/hal/linux/LinuxHal.cpp index 6bc510be5fd3..138238be370f 100644 --- a/hal/linux/LinuxHal.cpp +++ b/hal/linux/LinuxHal.cpp @@ -45,7 +45,11 @@ namespace mozilla { namespace hal_impl { void -Vibrate(const nsTArray& pattern) +Vibrate(const nsTArray& pattern, const hal::WindowIdentifier &) +{} + +void +CancelVibrate(const hal::WindowIdentifier &) {} #ifndef MOZ_ENABLE_DBUS diff --git a/hal/sandbox/PHal.ipdl b/hal/sandbox/PHal.ipdl index 330bb29f93d3..a6980de5a3cd 100644 --- a/hal/sandbox/PHal.ipdl +++ b/hal/sandbox/PHal.ipdl @@ -38,6 +38,7 @@ * ***** END LICENSE BLOCK ***** */ include protocol PContent; +include protocol PBrowser; namespace mozilla { @@ -57,7 +58,8 @@ child: NotifyBatteryChange(BatteryInformation aBatteryInfo); parent: - Vibrate(uint32[] pattern); + Vibrate(uint32[] pattern, uint64[] id, PBrowser browser); + CancelVibrate(uint64[] id, PBrowser browser); EnableBatteryNotifications(); DisableBatteryNotifications(); diff --git a/hal/sandbox/SandboxHal.cpp b/hal/sandbox/SandboxHal.cpp index 710cafea1b10..07884346f378 100644 --- a/hal/sandbox/SandboxHal.cpp +++ b/hal/sandbox/SandboxHal.cpp @@ -41,6 +41,8 @@ #include "mozilla/dom/ContentChild.h" #include "mozilla/hal_sandbox/PHalChild.h" #include "mozilla/hal_sandbox/PHalParent.h" +#include "mozilla/dom/TabParent.h" +#include "mozilla/dom/TabChild.h" #include "mozilla/dom/battery/Types.h" #include "mozilla/Observer.h" #include "mozilla/unused.h" @@ -63,10 +65,25 @@ Hal() } void -Vibrate(const nsTArray& pattern) +Vibrate(const nsTArray& pattern, const WindowIdentifier &id) { + HAL_LOG(("Vibrate: Sending to parent process.")); + AutoInfallibleTArray p(pattern); - Hal()->SendVibrate(p); + + WindowIdentifier newID(id); + newID.AppendProcessID(); + Hal()->SendVibrate(p, newID.AsArray(), GetTabChildFrom(newID.GetWindow())); +} + +void +CancelVibrate(const WindowIdentifier &id) +{ + HAL_LOG(("CancelVibrate: Sending to parent process.")); + + WindowIdentifier newID(id); + newID.AppendProcessID(); + Hal()->SendCancelVibrate(newID.AsArray(), GetTabChildFrom(newID.GetWindow())); } void @@ -91,11 +108,41 @@ class HalParent : public PHalParent , public BatteryObserver { public: NS_OVERRIDE virtual bool - RecvVibrate(const InfallibleTArray& pattern) { + RecvVibrate(const InfallibleTArray& pattern, + const InfallibleTArray &id, + PBrowserParent *browserParent) + { + // Check whether browserParent is active. We should have already + // checked that the corresponding window is active, but this check + // isn't redundant. A window may be inactive in an active + // browser. And a window is not notified synchronously when it's + // deactivated, so the window may think it's active when the tab + // is actually inactive. + TabParent *tabParent = static_cast(browserParent); + if (!tabParent->Active()) { + HAL_LOG(("RecvVibrate: Tab is not active. Cancelling.")); + return true; + } + // Forward to hal::, not hal_impl::, because we might be a // subprocess of another sandboxed process. The hal:: entry point // will do the right thing. - hal::Vibrate(pattern); + nsCOMPtr window = + do_QueryInterface(tabParent->GetBrowserDOMWindow()); + WindowIdentifier newID(id, window); + hal::Vibrate(pattern, newID); + return true; + } + + NS_OVERRIDE virtual bool + RecvCancelVibrate(const InfallibleTArray &id, + PBrowserParent *browserParent) + { + TabParent *tabParent = static_cast(browserParent); + nsCOMPtr window = + do_QueryInterface(tabParent->GetBrowserDOMWindow()); + WindowIdentifier newID(id, window); + hal::CancelVibrate(newID); return true; } diff --git a/widget/src/android/AndroidBridge.cpp b/widget/src/android/AndroidBridge.cpp index 588876a85a9a..3ee4c231f1a1 100644 --- a/widget/src/android/AndroidBridge.cpp +++ b/widget/src/android/AndroidBridge.cpp @@ -141,6 +141,9 @@ AndroidBridge::Init(JNIEnv *jEnv, jShowInputMethodPicker = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "showInputMethodPicker", "()V"); jHideProgressDialog = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "hideProgressDialog", "()V"); jPerformHapticFeedback = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "performHapticFeedback", "(Z)V"); + jVibrate1 = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "vibrate", "(J)V"); + jVibrateA = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "vibrate", "([JI)V"); + jCancelVibrate = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "cancelVibrate", "()V"); jSetKeepScreenOn = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "setKeepScreenOn", "(Z)V"); jIsNetworkLinkUp = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "isNetworkLinkUp", "()Z"); jIsNetworkLinkKnown = (jmethodID) jEnv->GetStaticMethodID(jGeckoAppShellClass, "isNetworkLinkKnown", "()Z"); @@ -436,9 +439,9 @@ AndroidBridge::GetHandlersForMimeType(const char *aMimeType, bool AndroidBridge::GetHandlersForURL(const char *aURL, - nsIMutableArray* aHandlersArray, - nsIHandlerApp **aDefaultApp, - const nsAString& aAction) + nsIMutableArray* aHandlersArray, + nsIHandlerApp **aDefaultApp, + const nsAString& aAction) { ALOG_BRIDGE("AndroidBridge::GetHandlersForURL"); @@ -676,6 +679,62 @@ AndroidBridge::PerformHapticFeedback(bool aIsLongPress) jPerformHapticFeedback, aIsLongPress); } +void +AndroidBridge::Vibrate(const nsTArray& aPattern) +{ + ALOG_BRIDGE("AndroidBridge::Vibrate"); + + PRUint32 len = aPattern.Length(); + if (!len) { + ALOG_BRIDGE(" invalid 0-length array"); + return; + } + + // It's clear if this worth special-casing, but it creates less + // java junk, so dodges the GC. + if (len == 1) { + jlong d = aPattern[0]; + if (d < 0) { + ALOG_BRIDGE(" invalid vibration duration < 0"); + return; + } + mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jVibrate1, d); + return; + } + + // First element of the array vibrate() expects is how long to wait + // *before* vibrating. For us, this is always 0. + + jlongArray array = mJNIEnv->NewLongArray(len + 1); + if (!array) { + ALOG_BRIDGE(" failed to allocate array"); + return; + } + + jlong* elts = mJNIEnv->GetLongArrayElements(array, nsnull); + elts[0] = 0; + for (PRUint32 i = 0; i < aPattern.Length(); ++i) { + jlong d = aPattern[i]; + if (d < 0) { + ALOG_BRIDGE(" invalid vibration duration < 0"); + mJNIEnv->ReleaseLongArrayElements(array, elts, JNI_ABORT); + return; + } + elts[i + 1] = d; + } + mJNIEnv->ReleaseLongArrayElements(array, elts, 0); + + mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jVibrateA, + array, -1/*don't repeat*/); + // GC owns |array| now? +} + +void +AndroidBridge::CancelVibrate() +{ + mJNIEnv->CallStaticVoidMethod(mGeckoAppShellClass, jCancelVibrate); +} + bool AndroidBridge::IsNetworkLinkUp() { diff --git a/widget/src/android/AndroidBridge.h b/widget/src/android/AndroidBridge.h index 512357019914..3cb1c9039587 100644 --- a/widget/src/android/AndroidBridge.h +++ b/widget/src/android/AndroidBridge.h @@ -199,6 +199,9 @@ public: void PerformHapticFeedback(bool aIsLongPress); + void Vibrate(const nsTArray& aPattern); + void CancelVibrate(); + void SetFullScreen(bool aFullScreen); void ShowInputMethodPicker(); @@ -355,6 +358,9 @@ protected: jmethodID jShowInputMethodPicker; jmethodID jHideProgressDialog; jmethodID jPerformHapticFeedback; + jmethodID jVibrate1; + jmethodID jVibrateA; + jmethodID jCancelVibrate; jmethodID jSetKeepScreenOn; jmethodID jIsNetworkLinkUp; jmethodID jIsNetworkLinkKnown;