From 359e977fccdd399c98787e68758323d9054694ea Mon Sep 17 00:00:00 2001 From: Armen Zambrano Gasparnian Date: Thu, 2 Feb 2012 11:03:47 -0500 Subject: [PATCH 1/7] Bug 719567. expand talos.json to support pageloader.xpi. r=jmaher --- testing/talos/talos.json | 9 ++++- testing/talos/talos_from_code.py | 62 +++++++++++++++++++++----------- 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/testing/talos/talos.json b/testing/talos/talos.json index e9df6bc5fc2..ba6b68ab2ca 100644 --- a/testing/talos/talos.json +++ b/testing/talos/talos.json @@ -1,3 +1,10 @@ { - "talos_zip": "http://build.mozilla.org/talos/zips/talos.bug721857.05f01e049452.zip" + "talos.zip": { + "url": "http://build.mozilla.org/talos/zips/talos.bug721857.05f01e049452.zip", + "path": "" + }, + "pageloader.xpi": { + "url": "http://build.mozilla.org/talos/xpis/pageloader.xpi", + "path": "talos/page_load_test" + } } diff --git a/testing/talos/talos_from_code.py b/testing/talos/talos_from_code.py index 2399cee8daf..574dd2767d6 100644 --- a/testing/talos/talos_from_code.py +++ b/testing/talos/talos_from_code.py @@ -1,4 +1,10 @@ #! /usr/bin/env python +# +# Script name: talos_from_code.py +# Purpose: Read from a talos.json file the different files to download for a talos job +# Author(s): Zambrano Gasparnian, Armen +# Target: Python 2.5 +# from optparse import OptionParser try: import json @@ -8,41 +14,49 @@ import re import urllib2 import urlparse import sys +import os def main(): + ''' + This script downloads a talos.json file which indicates which files to download + for a talos job. + See a talos.json file for a better understand: + http://hg.mozilla.org/mozilla-central/raw-file/default/testing/talos/talos.json + ''' parser = OptionParser() parser.add_option("--talos-json-url", dest="talos_json_url", type="string", help="It indicates from where to download the talos.json file.") (options, args) = parser.parse_args() + # 1) check that the url was passed if options.talos_json_url == None: print "You need to specify --talos-json-url." sys.exit(1) - # json file with info on which talos.zip to use - # the json file URL should look like this: - # %(repo_path)s/raw-file/%(revision)s/testing/talos/talos.json + # 2) try to download the talos.json file try: jsonFilename = download_file(options.talos_json_url) except Exception, e: - print "ERROR: We have been unable to download the talos.zip indicated " + \ - "in the talos.json file." + print "ERROR: We tried to download the talos.json file but something failed." print "ERROR: %s" % str(e) sys.exit(1) - print "INFO: talos.json URL: %s" % options.talos_json_url - talos_zip_url = get_value(jsonFilename, "talos_zip") - print "INFO: talos.zip URL: '%s'" % talos_zip_url + # 3) download the necessary files + print "INFO: talos.json URL: %s" % options.talos_json_url try: - if passesRestrictions(options.talos_json_url, talos_zip_url) == False: - print "ERROR: You have tried to download a talos.zip from a location " + \ - "different than http://build.mozilla.org/talos/zips" - print "ERROR: This is only allowed for the 'try' branch." - sys.exit(1) - download_file(talos_zip_url, "talos.zip") + for key in ('talos.zip', 'pageloader.xpi',): + entity = get_value(jsonFilename, key) + if passesRestrictions(options.talos_json_url, entity["url"]): + # the key is at the same time the filename e.g. talos.zip + download_file(entity["url"], entity["path"], key) + print "INFO: %s -> %s" % (entity["url"], os.path.join(entity["path"], key)) + else: + print "ERROR: You have tried to download a file " + \ + "from: %s " % fileUrl + \ + "which is a location different than http://build.mozilla.org/talos/" + print "ERROR: This is only allowed for the certain branches." + sys.exit(1) except Exception, e: - print "ERROR: We have been unable to download the talos.zip indicated " + \ - "in the talos.json file." print "ERROR: %s" % str(e) sys.exit(1) @@ -73,15 +87,21 @@ def get_filename_from_url(url): "but the URL seems to be incorrect." sys.exit(1) -def download_file(url, saveAs=None): +def download_file(url, path="", saveAs=None): ''' - It downloads a file from the URL indicated and can be saved locally with - a different name if needed. + It downloads a file from URL to the indicated path ''' req = urllib2.Request(url) - filename = get_filename_from_url(url) f = urllib2.urlopen(req) - local_file = open(saveAs if saveAs else filename, 'wb') + if path != "" and not os.path.isdir(path): + try: + os.makedirs(path) + print "INFO: directory %s created" % path + except Exception, e: + print "ERROR: %s" % str(e) + sys.exit(1) + filename = saveAs if saveAs else get_filename_from_url(url) + local_file = open(os.path.join(path, filename), 'wb') local_file.write(f.read()) local_file.close() return filename From 28c35b977eb2976640cf3b8eb7f1036dd7b47b00 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Thu, 2 Feb 2012 10:41:07 -0800 Subject: [PATCH 2/7] Bug 720747 - Completely silence RIL worker by default. r=qDot --- dom/system/b2g/RadioInterfaceLayer.js | 2 +- dom/system/b2g/ril_worker.js | 35 ++++++++++++++------------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/dom/system/b2g/RadioInterfaceLayer.js b/dom/system/b2g/RadioInterfaceLayer.js index 4dcc6950498..fc71580d22e 100644 --- a/dom/system/b2g/RadioInterfaceLayer.js +++ b/dom/system/b2g/RadioInterfaceLayer.js @@ -47,7 +47,7 @@ Cu.import("resource://gre/modules/Services.jsm"); var RIL = {}; Cu.import("resource://gre/modules/ril_consts.js", RIL); -const DEBUG = true; // set to false to suppress debug messages +const DEBUG = false; // set to true to see debug messages const RADIOINTERFACELAYER_CID = Components.ID("{2d831c8d-6017-435b-a80c-e5d422810cea}"); diff --git a/dom/system/b2g/ril_worker.js b/dom/system/b2g/ril_worker.js index 551369721ca..5dcce3f2a6f 100644 --- a/dom/system/b2g/ril_worker.js +++ b/dom/system/b2g/ril_worker.js @@ -432,7 +432,6 @@ let Buf = { /** * Process one parcel. */ - processParcel: function processParcel() { let response_type = this.readUint32(); let length = this.readIncoming - UINT32_SIZE; @@ -445,20 +444,24 @@ let Buf = { request_type = this.tokenRequestMap[token]; if (error) { //TODO - debug("Received error " + error + " for solicited parcel type " + - request_type); + if (DEBUG) { + debug("Received error " + error + " for solicited parcel type " + + request_type); + } return; } - debug("Solicited response for request type " + request_type + - ", token " + token); + if (DEBUG) { + debug("Solicited response for request type " + request_type + + ", token " + token); + } delete this.tokenRequestMap[token]; this.lastSolicitedToken = token; } else if (response_type == RESPONSE_TYPE_UNSOLICITED) { request_type = this.readUint32(); length -= UINT32_SIZE; - debug("Unsolicited response for request type " + request_type); + if (DEBUG) debug("Unsolicited response for request type " + request_type); } else { - debug("Unknown response type: " + response_type); + if (DEBUG) debug("Unknown response type: " + response_type); return; } @@ -483,9 +486,8 @@ let Buf = { }, /** - * Communication with the RIL IPC thread. + * Communicate with the RIL IPC thread. */ - sendParcel: function sendParcel() { // Compute the size of the parcel and write it to the front of the parcel // where we left room for it. Note that he parcel size does not include @@ -496,7 +498,7 @@ let Buf = { // This assumes that postRILMessage will make a copy of the ArrayBufferView // right away! let parcel = this.outgoingBytes.subarray(0, this.outgoingIndex); - debug("Outgoing parcel: " + Array.slice(parcel)); + if (DEBUG) debug("Outgoing parcel: " + Array.slice(parcel)); postRILMessage(parcel); this.outgoingIndex = PARCEL_SIZE_SIZE; }, @@ -865,7 +867,7 @@ let RIL = { handleParcel: function handleParcel(request_type, length) { let method = this[request_type]; if (typeof method == "function") { - debug("Handling parcel as " + method.name); + if (DEBUG) debug("Handling parcel as " + method.name); method.call(this, length); } } @@ -1218,9 +1220,6 @@ RIL[UNSOLICITED_RESEND_INCALL_MUTE] = null; */ let Phone = { - //XXX TODO beware, this is just demo code. It's still missing - // communication with the UI thread. - /** * One of the RADIO_STATE_* constants. */ @@ -1313,7 +1312,9 @@ let Phone = { */ onRadioStateChanged: function onRadioStateChanged(newState) { - debug("Radio state changed from " + this.radioState + " to " + newState); + if (DEBUG) { + debug("Radio state changed from " + this.radioState + " to " + newState); + } if (this.radioState == newState) { // No change in state, return. return; @@ -1455,13 +1456,13 @@ let Phone = { }, onNetworkStateChanged: function onNetworkStateChanged() { - debug("Network state changed, re-requesting phone state."); + if (DEBUG) debug("Network state changed, re-requesting phone state."); this.requestNetworkInfo(); }, onICCStatus: function onICCStatus(iccStatus) { if (DEBUG) { - debug("iccStatus: " + JSON.stringify(iccStatus)); + debug("iccStatus: " + JSON.stringify(iccStatus)); } this.iccStatus = iccStatus; From f76b8ed391560adf763309fbb38ec3a660e56545 Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Thu, 2 Feb 2012 10:41:07 -0800 Subject: [PATCH 3/7] Bug 716709 - RIL: call state on SGS2 has extra uint32. r=qDot --- dom/system/b2g/Makefile.in | 1 + dom/system/b2g/ril_worker.js | 62 ++++++++++++++++++++-------- dom/system/b2g/systemlibs.js | 79 ++++++++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 dom/system/b2g/systemlibs.js diff --git a/dom/system/b2g/Makefile.in b/dom/system/b2g/Makefile.in index 6daa3ab351d..31b337e74bb 100644 --- a/dom/system/b2g/Makefile.in +++ b/dom/system/b2g/Makefile.in @@ -83,6 +83,7 @@ EXTRA_COMPONENTS = \ EXTRA_JS_MODULES = \ ril_consts.js \ ril_worker.js \ + systemlibs.js \ $(NULL) include $(topsrcdir)/config/rules.mk diff --git a/dom/system/b2g/ril_worker.js b/dom/system/b2g/ril_worker.js index 5dcce3f2a6f..d5873b5aa3c 100644 --- a/dom/system/b2g/ril_worker.js +++ b/dom/system/b2g/ril_worker.js @@ -62,7 +62,7 @@ "use strict"; -importScripts("ril_consts.js"); +importScripts("ril_consts.js", "systemlibs.js"); let DEBUG = false; @@ -72,6 +72,8 @@ const UINT16_SIZE = 2; const UINT32_SIZE = 4; const PARCEL_SIZE_SIZE = UINT32_SIZE; +let RILQUIRKS_CALLSTATE_EXTRA_UINT32 = false; + /** * This object contains helpers buffering incoming data & deconstructing it * into parcels as well as buffering outgoing data & constructing parcels. @@ -518,6 +520,25 @@ let Buf = { */ let RIL = { + /** + * Set quirk flags based on the RIL model detected. Note that this + * requires the RIL being "warmed up" first, which happens when on + * an incoming or outgoing call. + */ + rilQuirksInitialized: false, + initRILQuirks: function initRILQuirks() { + // The Samsung Galaxy S2 I-9100 radio sends an extra Uint32 in the + // call state. + let model_id = libcutils.property_get("ril.model_id"); + if (DEBUG) debug("Detected RIL model " + model_id); + if (model_id == "I9100") { + if (DEBUG) debug("Enabling RILQUIRKS_CALLSTATE_EXTRA_UINT32 for I9100."); + RILQUIRKS_CALLSTATE_EXTRA_UINT32 = true; + } + + this.rilQuirksInitialized = true; + }, + /** * Retrieve the ICC's status. * @@ -917,6 +938,10 @@ RIL[REQUEST_CHANGE_SIM_PIN] = function REQUEST_CHANGE_SIM_PIN() { RIL[REQUEST_CHANGE_SIM_PIN2] = null; RIL[REQUEST_ENTER_NETWORK_DEPERSONALIZATION] = null; RIL[REQUEST_GET_CURRENT_CALLS] = function REQUEST_GET_CURRENT_CALLS(length) { + if (!this.rilQuirksInitialized) { + this.initRILQuirks(); + } + let calls_length = 0; // The RIL won't even send us the length integer if there are no active calls. // So only read this integer if the parcel actually has it. @@ -930,22 +955,24 @@ RIL[REQUEST_GET_CURRENT_CALLS] = function REQUEST_GET_CURRENT_CALLS(length) { let calls = {}; for (let i = 0; i < calls_length; i++) { - let call = { - state: Buf.readUint32(), // CALL_STATE_* - callIndex: Buf.readUint32(), // GSM index (1-based) - toa: Buf.readUint32(), - isMpty: Boolean(Buf.readUint32()), - isMT: Boolean(Buf.readUint32()), - als: Buf.readUint32(), - isVoice: Boolean(Buf.readUint32()), - isVoicePrivacy: Boolean(Buf.readUint32()), - somethingOrOther: Buf.readUint32(), //XXX TODO whatziz? not in ril.h, but it's in the output... - number: Buf.readString(), //TODO munge with TOA - numberPresentation: Buf.readUint32(), // CALL_PRESENTATION_* - name: Buf.readString(), - namePresentation: Buf.readUint32(), - uusInfo: null - }; + let call = {}; + call.state = Buf.readUint32(); // CALL_STATE_* + call.callIndex = Buf.readUint32(); // GSM index (1-based) + call.toa = Buf.readUint32(); + call.isMpty = Boolean(Buf.readUint32()); + call.isMT = Boolean(Buf.readUint32()); + call.als = Buf.readUint32(); + call.isVoice = Boolean(Buf.readUint32()); + call.isVoicePrivacy = Boolean(Buf.readUint32()); + if (RILQUIRKS_CALLSTATE_EXTRA_UINT32) { + Buf.readUint32(); + } + call.number = Buf.readString(); //TODO munge with TOA + call.numberPresentation = Buf.readUint32(); // CALL_PRESENTATION_* + call.name = Buf.readString(); + call.namePresentation = Buf.readUint32(); + + call.uusInfo = null; let uusInfoPresent = Buf.readUint32(); if (uusInfoPresent == 1) { call.uusInfo = { @@ -954,6 +981,7 @@ RIL[REQUEST_GET_CURRENT_CALLS] = function REQUEST_GET_CURRENT_CALLS(length) { userData: null //XXX TODO byte array?!? }; } + calls[call.callIndex] = call; } Phone.onCurrentCalls(calls); diff --git a/dom/system/b2g/systemlibs.js b/dom/system/b2g/systemlibs.js new file mode 100644 index 00000000000..df09d3a3f8c --- /dev/null +++ b/dom/system/b2g/systemlibs.js @@ -0,0 +1,79 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +const SYSTEM_PROPERTY_KEY_MAX = 32; +const SYSTEM_PROPERTY_VALUE_MAX = 92; + +/** + * Expose some system-level functions. + */ +let libcutils = (function() { + let lib; + try { + lib = ctypes.open("libcutils.so"); + } catch(ex) { + // Return a fallback option in case libcutils.so isn't present (e.g. + // when building Firefox with MOZ_B2G_RIL. + dump("Could not load libcutils.so. Using fake propdb."); + let fake_propdb = Object.create(null); + return { + property_get: function fake_property_get(key, defaultValue) { + if (key in fake_propdb) { + return fake_propdb[key]; + } + return defaultValue === undefined ? null : defaultValue; + }, + property_set: function fake_property_set(key, value) { + fake_propdb[key] = value; + } + }; + } + + let c_property_get = lib.declare("property_get", ctypes.default_abi, + ctypes.int, // return value: length + ctypes.char.ptr, // key + ctypes.char.ptr, // value + ctypes.char.ptr); // default + let c_property_set = lib.declare("property_set", ctypes.default_abi, + ctypes.int, // return value: success + ctypes.char.ptr, // key + ctypes.char.ptr); // value + let c_value_buf = ctypes.char.array(SYSTEM_PROPERTY_VALUE_MAX)(); + + return { + + /** + * Get a system property. + * + * @param key + * Name of the property + * @param defaultValue [optional] + * Default value to return if the property isn't set (default: null) + */ + property_get: function property_get(key, defaultValue) { + if (defaultValue === undefined) { + defaultValue = null; + } + c_property_get(key, c_value_buf, defaultValue); + return c_value_buf.readString(); + }, + + /** + * Set a system property + * + * @param key + * Name of the property + * @param value + * Value to set the property to. + */ + property_set: function property_set(key, value) { + let rv = c_property_set(key, value); + if (rv) { + throw Error('libcutils.property_set("' + key + '", "' + value + + '") failed with error ' + rv); + } + } + + }; +})(); From dd04869e9805d6dbeabfce25b72ae92cdc349e9e Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Thu, 2 Feb 2012 11:11:42 -0800 Subject: [PATCH 4/7] Bug 723356 - Make `console` output show up in logcat. r=gal --- b2g/chrome/content/shell.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index 6d07cfccbc7..057ae688ade 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -318,3 +318,12 @@ nsBrowserAccess.prototype = { } }; +// Pipe `console` log messages to the nsIConsoleService which writes them +// to logcat. +Services.obs.addObserver(function onConsoleAPILogEvent(subject, topic, data) { + let message = subject.wrappedJSObject; + let prefix = "Content JS " + message.level.toUpperCase() + + " at " + message.filename + ":" + message.lineNumber + + " in " + (message.functionName || "anonymous") + ": "; + Services.console.logStringMessage(prefix + Array.join(message.arguments, " ")); +}, "console-api-log-event", false); From 90ea87d1b43ecfb2554d296f6e2c6d7db2a3157c Mon Sep 17 00:00:00 2001 From: Philipp von Weitershausen Date: Thu, 2 Feb 2012 11:12:32 -0800 Subject: [PATCH 5/7] Bug 723372 - B2G telephony: audio should be turned on during dialing + ringing. r=bent DONTBUILD because NPOTB --- dom/system/b2g/RadioInterfaceLayer.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dom/system/b2g/RadioInterfaceLayer.js b/dom/system/b2g/RadioInterfaceLayer.js index fc71580d22e..351ab9a060b 100644 --- a/dom/system/b2g/RadioInterfaceLayer.js +++ b/dom/system/b2g/RadioInterfaceLayer.js @@ -241,7 +241,9 @@ RadioInterfaceLayer.prototype = { handleCallStateChange: function handleCallStateChange(call) { debug("handleCallStateChange: " + JSON.stringify(call)); call.state = convertRILCallState(call.state); - if (call.state == nsIRadioInterfaceLayer.CALL_STATE_CONNECTED) { + if (call.state == nsIRadioInterfaceLayer.CALL_STATE_DIALING || + call.state == nsIRadioInterfaceLayer.CALL_STATE_RINGING || + call.state == nsIRadioInterfaceLayer.CALL_STATE_CONNECTED) { // This is now the active call. this._activeCall = call; } From 9aeadab10e7d582a2e5b8a2757111aab855dac03 Mon Sep 17 00:00:00 2001 From: Olli Pettay Date: Thu, 2 Feb 2012 22:34:53 +0200 Subject: [PATCH 6/7] Bug 716014 - Use CompartmentGC more often, r=billm,mccr8 --- dom/base/nsDOMWindowUtils.cpp | 2 +- dom/base/nsJSEnvironment.cpp | 119 +++++++++++++++++++++++++++++++--- dom/base/nsJSEnvironment.h | 34 +++++++++- dom/ipc/ContentChild.cpp | 4 +- js/src/jsfriendapi.h | 3 +- 5 files changed, 147 insertions(+), 15 deletions(-) diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 19b950fdc33..3f3cb9039b3 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -817,7 +817,7 @@ nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener, } #endif - nsJSContext::GarbageCollectNow(js::gcreason::DOM_UTILS); + nsJSContext::GarbageCollectNow(js::gcreason::DOM_UTILS, nsGCNormal, true); nsJSContext::CycleCollectNow(aListener, aExtraForgetSkippableCalls); return NS_OK; diff --git a/dom/base/nsJSEnvironment.cpp b/dom/base/nsJSEnvironment.cpp index 0ea8e11e62d..03fbc361230 100644 --- a/dom/base/nsJSEnvironment.cpp +++ b/dom/base/nsJSEnvironment.cpp @@ -135,6 +135,8 @@ static PRLogModuleInfo* gJSDiagnostics; // doing the first GC. #define NS_FIRST_GC_DELAY 10000 // ms +#define NS_FULL_GC_DELAY 60000 // ms + // The amount of time we wait between a request to CC (after GC ran) // and doing the actual CC. #define NS_CC_DELAY 5000 // ms @@ -148,6 +150,7 @@ static PRLogModuleInfo* gJSDiagnostics; // if you add statics here, add them to the list in nsJSRuntime::Startup static nsITimer *sGCTimer; +static nsITimer *sFullGCTimer; static nsITimer *sShrinkGCBuffersTimer; static nsITimer *sCCTimer; @@ -166,6 +169,7 @@ static bool sLoadingInProgress; static PRUint32 sCCollectedWaitingForGC; static bool sPostGCEventsToConsole; +static bool sDisableExplicitCompartmentGC; static PRUint32 sCCTimerFireCount = 0; static PRUint32 sMinForgetSkippableTime = PR_UINT32_MAX; static PRUint32 sMaxForgetSkippableTime = 0; @@ -173,9 +177,15 @@ static PRUint32 sTotalForgetSkippableTime = 0; static PRUint32 sRemovedPurples = 0; static PRUint32 sForgetSkippableBeforeCC = 0; static PRUint32 sPreviousSuspectedCount = 0; +static PRUint32 sCompartmentGCCount = 0; +static nsJSContext* sTopEvaluator = nsnull; +static bool sContextDeleted = false; +static bool sPreviousWasChromeCompGC = false; static bool sCleanupSinceLastGC = true; +PRUint32 nsJSContext::sGlobalGCEpoch = 0; + nsScriptNameSpaceManager *gNameSpaceManager; static nsIJSRuntimeService *sRuntimeService; @@ -216,7 +226,8 @@ nsMemoryPressureObserver::Observe(nsISupports* aSubject, const char* aTopic, const PRUnichar* aData) { if (sGCOnMemoryPressure) { - nsJSContext::GarbageCollectNow(js::gcreason::MEM_PRESSURE, nsGCShrinking); + nsJSContext::GarbageCollectNow(js::gcreason::MEM_PRESSURE, nsGCShrinking, + true); nsJSContext::CycleCollectNow(); } return NS_OK; @@ -938,6 +949,8 @@ static const char js_pccounts_content_str[] = JS_OPTIONS_DOT_STR "pccounts.con static const char js_pccounts_chrome_str[] = JS_OPTIONS_DOT_STR "pccounts.chrome"; static const char js_jit_hardening_str[] = JS_OPTIONS_DOT_STR "jit_hardening"; static const char js_memlog_option_str[] = JS_OPTIONS_DOT_STR "mem.log"; +static const char js_disable_explicit_compartment_gc[] = + JS_OPTIONS_DOT_STR "disable_explicit_compartment_gc"; int nsJSContext::JSOptionChangedCallback(const char *pref, void *data) @@ -947,6 +960,8 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data) PRUint32 newDefaultJSOptions = oldDefaultJSOptions; sPostGCEventsToConsole = Preferences::GetBool(js_memlog_option_str); + sDisableExplicitCompartmentGC = + Preferences::GetBool(js_disable_explicit_compartment_gc); bool strict = Preferences::GetBool(js_strict_option_str); if (strict) @@ -1048,6 +1063,9 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data) nsJSContext::nsJSContext(JSRuntime *aRuntime) : mGCOnDestruction(true), + mChromeComp(false), + mGlobalGCEpoch(0), + mEvaluationCount(0), mExecuteDepth(0) { @@ -1098,6 +1116,9 @@ nsJSContext::~nsJSContext() DestroyJSContext(); --sContextCount; + if (sTopEvaluator == this) { + sTopEvaluator = nsnull; + } if (!sContextCount && sDidShutdown) { // The last context is being deleted, and we're already in the @@ -1124,6 +1145,7 @@ nsJSContext::DestroyJSContext() js_options_dot_str, this); if (mGCOnDestruction) { + sContextDeleted = true; PokeGC(js::gcreason::NSJSCONTEXT_DESTROY); } @@ -2237,6 +2259,7 @@ nsJSContext::CreateNativeGlobalForInner( { nsIXPConnect *xpc = nsContentUtils::XPConnect(); PRUint32 flags = aIsChrome? nsIXPConnect::FLAG_SYSTEM_GLOBAL_OBJECT : 0; + mChromeComp = aIsChrome; nsCOMPtr systemPrincipal; if (aIsChrome) { @@ -3159,6 +3182,12 @@ nsJSContext::ScriptEvaluated(bool aTerminated) if (aTerminated && mExecuteDepth == 0 && !JS_IsRunning(mContext)) { mOperationCallbackTime = 0; mModalStateTime = 0; + + IncreaseEvaluationCount(this); + if (EvaluationCount(sTopEvaluator) < EvaluationCount(this) && + (!mChromeComp || !sPreviousWasChromeCompGC)) { + sTopEvaluator = this; + } } } @@ -3231,9 +3260,20 @@ nsJSContext::ScriptExecuted() return NS_OK; } +void +FullGCTimerFired(nsITimer* aTimer, void* aClosure) +{ + NS_RELEASE(sFullGCTimer); + + uintptr_t reason = reinterpret_cast(aClosure); + nsJSContext::GarbageCollectNow(static_cast(reason), + nsGCNormal, true); +} + //static void -nsJSContext::GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind) +nsJSContext::GarbageCollectNow(js::gcreason::Reason aReason, PRUint32 aGckind, + bool aGlobal) { NS_TIME_FUNCTION_MIN(1.0); SAMPLE_LABEL("GC", "GarbageCollectNow"); @@ -3250,9 +3290,44 @@ nsJSContext::GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind) sPendingLoadCount = 0; sLoadingInProgress = false; - if (nsContentUtils::XPConnect()) { - nsContentUtils::XPConnect()->GarbageCollect(reason, gckind); + if (!nsContentUtils::XPConnect()) { + return; } + + // Use compartment GC when we're not asked to do a shrinking GC nor + // global GC and compartment GC has been called less than 10 times after + // the previous global GC. If a top level browsing context has been + // deleted, we do a global GC. + if (!sDisableExplicitCompartmentGC && + aGckind != nsGCShrinking && !aGlobal && + EvaluationCount(sTopEvaluator) > 0 && + !sContextDeleted && sCompartmentGCCount < 10) { + nsJSContext* top = sTopEvaluator; + sTopEvaluator = nsnull; + ResetEvaluationCount(top); + JSContext* cx = top->GetNativeContext(); + if (cx) { + JSObject* global = top->GetNativeGlobal(); + if (global) { + if (!sFullGCTimer) { + CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer); + } + if (sFullGCTimer) { + sFullGCTimer->Cancel(); + js::gcreason::Reason reason = js::gcreason::FULL_GC_TIMER; + sFullGCTimer->InitWithFuncCallback(FullGCTimerFired, + reinterpret_cast(reason), + NS_FULL_GC_DELAY, + nsITimer::TYPE_ONE_SHOT); + } + JSCompartment* comp = js::GetObjectCompartment(global); + js::CompartmentGCForReason(cx, comp, aReason); + return; + } + } + } + + nsContentUtils::XPConnect()->GarbageCollect(aReason, aGckind); } //static @@ -3356,7 +3431,8 @@ GCTimerFired(nsITimer *aTimer, void *aClosure) NS_RELEASE(sGCTimer); uintptr_t reason = reinterpret_cast(aClosure); - nsJSContext::GarbageCollectNow(static_cast(reason), nsGCNormal); + nsJSContext::GarbageCollectNow(static_cast(reason), + nsGCNormal, false); } void @@ -3524,6 +3600,16 @@ nsJSContext::KillGCTimer() } } +void +nsJSContext::KillFullGCTimer() +{ + if (sFullGCTimer) { + sFullGCTimer->Cancel(); + + NS_RELEASE(sFullGCTimer); + } +} + //static void nsJSContext::KillShrinkGCBuffersTimer() @@ -3552,8 +3638,8 @@ nsJSContext::GC(js::gcreason::Reason aReason) PokeGC(aReason); } -static void -DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status) +void +nsJSContext::DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status) { NS_ASSERTION(NS_IsMainThread(), "GCs must run on the main thread"); @@ -3579,6 +3665,19 @@ DOMGCFinishedCallback(JSRuntime *rt, JSCompartment *comp, const char *status) sCCollectedWaitingForGC = 0; sCleanupSinceLastGC = false; + if (!comp) { + sPreviousWasChromeCompGC = false; + sContextDeleted = false; + sCompartmentGCCount = 0; + ++sGlobalGCEpoch; + KillFullGCTimer(); + } else { + // Only every other compartment GC is allowed to collect a chrome + // compartment. Otherwise we'd collect chrome compartment all the time. + sPreviousWasChromeCompGC = xpc::AccessCheck::isChrome(comp); + ++sCompartmentGCCount; + } + if (sGCTimer) { // If we were waiting for a GC to happen, kill the timer. nsJSContext::KillGCTimer(); @@ -3699,13 +3798,14 @@ void nsJSRuntime::Startup() { // initialize all our statics, so that we can restart XPCOM - sGCTimer = sCCTimer = nsnull; + sGCTimer = sFullGCTimer = sCCTimer = nsnull; sGCHasRun = false; sLastCCEndTime = 0; sPendingLoadCount = 0; sLoadingInProgress = false; sCCollectedWaitingForGC = 0; sPostGCEventsToConsole = false; + sDisableExplicitCompartmentGC = false; gNameSpaceManager = nsnull; sRuntimeService = nsnull; sRuntime = nsnull; @@ -3862,7 +3962,7 @@ nsJSRuntime::Init() // Let's make sure that our main thread is the same as the xpcom main thread. NS_ASSERTION(NS_IsMainThread(), "bad"); - ::JS_SetGCFinishedCallback(sRuntime, DOMGCFinishedCallback); + ::JS_SetGCFinishedCallback(sRuntime, nsJSContext::DOMGCFinishedCallback); JSSecurityCallbacks *callbacks = JS_GetRuntimeSecurityCallbacks(sRuntime); NS_ASSERTION(callbacks, "SecMan should have set security callbacks!"); @@ -3947,6 +4047,7 @@ void nsJSRuntime::Shutdown() { nsJSContext::KillGCTimer(); + nsJSContext::KillFullGCTimer(); nsJSContext::KillShrinkGCBuffersTimer(); nsJSContext::KillCCTimer(); diff --git a/dom/base/nsJSEnvironment.h b/dom/base/nsJSEnvironment.h index 43b503e976b..5ea400ecb4b 100644 --- a/dom/base/nsJSEnvironment.h +++ b/dom/base/nsJSEnvironment.h @@ -181,7 +181,9 @@ public: static void LoadStart(); static void LoadEnd(); - static void GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind = nsGCNormal); + static void GarbageCollectNow(js::gcreason::Reason aReason, + PRUint32 aGckind, + bool aGlobal); static void ShrinkGCBuffersNow(); // If aExtraForgetSkippableCalls is -1, forgetSkippable won't be // called even if the previous collection was GC. @@ -190,6 +192,7 @@ public: static void PokeGC(js::gcreason::Reason aReason); static void KillGCTimer(); + static void KillFullGCTimer(); static void PokeShrinkGCBuffers(); static void KillShrinkGCBuffersTimer(); @@ -208,6 +211,29 @@ public: JSObject* global = JS_GetGlobalObject(mContext); return global ? mGlobalObjectRef.get() : nsnull; } + + static PRUint32 EvaluationCount(nsJSContext* aCx) + { + return aCx && aCx->mGlobalGCEpoch == sGlobalGCEpoch ? + aCx->mEvaluationCount : 0; + } + + static void IncreaseEvaluationCount(nsJSContext* aCx) + { + if (aCx->mGlobalGCEpoch != sGlobalGCEpoch) { + aCx->mEvaluationCount = 0; + aCx->mGlobalGCEpoch = sGlobalGCEpoch; + } + ++(aCx->mEvaluationCount); + } + + static void ResetEvaluationCount(nsJSContext* aCx) + { + aCx->mEvaluationCount = 0; + } + + static void DOMGCFinishedCallback(JSRuntime* aRt, JSCompartment* aComp, + const char* aStatus); protected: nsresult InitializeExternalClasses(); @@ -296,7 +322,9 @@ private: bool mScriptsEnabled; bool mGCOnDestruction; bool mProcessingScriptTag; - + bool mChromeComp; + PRUint32 mGlobalGCEpoch; + PRUint32 mEvaluationCount; PRUint32 mExecuteDepth; PRUint32 mDefaultJSOptions; PRTime mOperationCallbackTime; @@ -308,6 +336,8 @@ private: // context does. It is eventually collected by the cycle collector. nsCOMPtr mGlobalObjectRef; + static PRUint32 sGlobalGCEpoch; + static int JSOptionChangedCallback(const char *pref, void *data); static JSBool DOMOperationCallback(JSContext *cx); diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index c81f30fd054..462217e09f2 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -800,14 +800,14 @@ ContentChild::GetIndexedDBPath() bool ContentChild::RecvGarbageCollect() { - nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC); + nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC, nsGCNormal, true); return true; } bool ContentChild::RecvCycleCollect() { - nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC); + nsJSContext::GarbageCollectNow(js::gcreason::DOM_IPC, nsGCNormal, true); nsJSContext::CycleCollectNow(); return true; } diff --git a/js/src/jsfriendapi.h b/js/src/jsfriendapi.h index 724b37d59e1..d074d745ad9 100644 --- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -657,7 +657,8 @@ SizeOfJSContext(); D(DOM_IPC) \ D(DOM_WORKER) \ D(INTER_SLICE_GC) \ - D(REFRESH_FRAME) + D(REFRESH_FRAME) \ + D(FULL_GC_TIMER) namespace gcreason { From fda00c5f29ab059dd6a19d7f835b728a9fce5c90 Mon Sep 17 00:00:00 2001 From: Neil Rashbrook Date: Thu, 2 Feb 2012 22:54:58 +0100 Subject: [PATCH 7/7] Better test for bug 649840. r=ehsan. + bustage-fix by sgautherie. --- .../satchel/test/test_bug_511615.html | 4 +- toolkit/content/tests/chrome/Makefile.in | 1 - .../content/tests/chrome/test_bug649840.xul | 66 ------------------- 3 files changed, 3 insertions(+), 68 deletions(-) delete mode 100644 toolkit/content/tests/chrome/test_bug649840.xul diff --git a/toolkit/components/satchel/test/test_bug_511615.html b/toolkit/components/satchel/test/test_bug_511615.html index 0ba5ccd0121..0854689639d 100644 --- a/toolkit/components/satchel/test/test_bug_511615.html +++ b/toolkit/components/satchel/test/test_bug_511615.html @@ -12,7 +12,7 @@ Form History test: form field autocomplete

-
+
@@ -32,6 +32,7 @@ Form History test: form field autocomplete netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); var autocompletePopup = getAutocompletePopup(); +autocompletePopup.style.direction = "ltr"; var input = $_(1, "field1"); @@ -366,6 +367,7 @@ function runTest(testNum) { case 211: checkPopupOpen(false); checkForm(""); + is(autocompletePopup.style.direction, "rtl", "direction should have been changed from ltr to rtl"); SimpleTest.finish(); return; diff --git a/toolkit/content/tests/chrome/Makefile.in b/toolkit/content/tests/chrome/Makefile.in index 3d585c110d3..5e5fd4d640b 100644 --- a/toolkit/content/tests/chrome/Makefile.in +++ b/toolkit/content/tests/chrome/Makefile.in @@ -72,7 +72,6 @@ _TEST_FILES = findbar_window.xul \ test_bug570192.xul \ test_bug624329.xul \ bug624329_window.xul \ - test_bug649840.xul \ test_popup_preventdefault_chrome.xul \ window_popup_preventdefault_chrome.xul \ test_largemenu.xul \ diff --git a/toolkit/content/tests/chrome/test_bug649840.xul b/toolkit/content/tests/chrome/test_bug649840.xul deleted file mode 100644 index f2c0c0dc048..00000000000 --- a/toolkit/content/tests/chrome/test_bug649840.xul +++ /dev/null @@ -1,66 +0,0 @@ - - - - - - -