зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound.
This commit is contained in:
Коммит
e6a700a873
|
@ -1012,7 +1012,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;
|
||||
|
|
|
@ -134,6 +134,10 @@ static PRLogModuleInfo* gJSDiagnostics;
|
|||
// doing the first GC.
|
||||
#define NS_FIRST_GC_DELAY 10000 // ms
|
||||
|
||||
#define NS_FULL_GC_DELAY 60000 // ms
|
||||
|
||||
#define NS_MAX_COMPARTMENT_GC_COUNT 20
|
||||
|
||||
// Maximum amount of time that should elapse between incremental GC slices
|
||||
#define NS_INTERSLICE_GC_DELAY 100 // ms
|
||||
|
||||
|
@ -159,6 +163,7 @@ static PRLogModuleInfo* gJSDiagnostics;
|
|||
static nsITimer *sGCTimer;
|
||||
static nsITimer *sShrinkGCBuffersTimer;
|
||||
static nsITimer *sCCTimer;
|
||||
static nsITimer *sFullGCTimer;
|
||||
|
||||
static PRTime sLastCCEndTime;
|
||||
|
||||
|
@ -178,6 +183,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;
|
||||
|
@ -185,9 +191,10 @@ static PRUint32 sTotalForgetSkippableTime = 0;
|
|||
static PRUint32 sRemovedPurples = 0;
|
||||
static PRUint32 sForgetSkippableBeforeCC = 0;
|
||||
static PRUint32 sPreviousSuspectedCount = 0;
|
||||
|
||||
static PRUint32 sCompartmentGCCount = NS_MAX_COMPARTMENT_GC_COUNT;
|
||||
static PRUint32 sCleanupsSinceLastGC = PR_UINT32_MAX;
|
||||
static bool sNeedsFullCC = false;
|
||||
static nsJSContext *sContextList = nsnull;
|
||||
|
||||
nsScriptNameSpaceManager *gNameSpaceManager;
|
||||
|
||||
|
@ -229,7 +236,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;
|
||||
|
@ -929,6 +937,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)
|
||||
|
@ -938,6 +948,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)
|
||||
|
@ -1038,9 +1050,16 @@ nsJSContext::JSOptionChangedCallback(const char *pref, void *data)
|
|||
}
|
||||
|
||||
nsJSContext::nsJSContext(JSRuntime *aRuntime)
|
||||
: mGCOnDestruction(true),
|
||||
: mActive(false),
|
||||
mGCOnDestruction(true),
|
||||
mExecuteDepth(0)
|
||||
{
|
||||
mNext = sContextList;
|
||||
mPrev = &sContextList;
|
||||
if (sContextList) {
|
||||
sContextList->mPrev = &mNext;
|
||||
}
|
||||
sContextList = this;
|
||||
|
||||
++sContextCount;
|
||||
|
||||
|
@ -1079,6 +1098,11 @@ nsJSContext::~nsJSContext()
|
|||
nsCycleCollector_DEBUG_wasFreed(static_cast<nsIScriptContext*>(this));
|
||||
#endif
|
||||
|
||||
*mPrev = mNext;
|
||||
if (mNext) {
|
||||
mNext->mPrev = mPrev;
|
||||
}
|
||||
|
||||
// We may still have pending termination functions if the context is destroyed
|
||||
// before they could be executed. In this case, free the references to their
|
||||
// parameters, but don't execute the functions (see bug 622326).
|
||||
|
@ -2849,6 +2873,7 @@ nsJSContext::ScriptEvaluated(bool aTerminated)
|
|||
if (aTerminated) {
|
||||
mOperationCallbackTime = 0;
|
||||
mModalStateTime = 0;
|
||||
mActive = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2916,9 +2941,20 @@ nsJSContext::ScriptExecuted()
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
FullGCTimerFired(nsITimer* aTimer, void* aClosure)
|
||||
{
|
||||
NS_RELEASE(sFullGCTimer);
|
||||
|
||||
uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
|
||||
nsJSContext::GarbageCollectNow(static_cast<js::gcreason::Reason>(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");
|
||||
|
@ -2935,9 +2971,35 @@ 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
|
||||
// NS_MAX_COMPARTMENT_GC_COUNT times after the previous global GC.
|
||||
if (!sDisableExplicitCompartmentGC &&
|
||||
aGckind != nsGCShrinking && !aGlobal &&
|
||||
sCompartmentGCCount < NS_MAX_COMPARTMENT_GC_COUNT) {
|
||||
js::PrepareForFullGC(nsJSRuntime::sRuntime);
|
||||
for (nsJSContext* cx = sContextList; cx; cx = cx->mNext) {
|
||||
if (!cx->mActive && cx->mContext) {
|
||||
if (JSObject* global = cx->GetNativeGlobal()) {
|
||||
js::SkipCompartmentForGC(js::GetObjectCompartment(global));
|
||||
}
|
||||
}
|
||||
cx->mActive = false;
|
||||
}
|
||||
if (js::IsGCScheduled(nsJSRuntime::sRuntime)) {
|
||||
js::IncrementalGC(nsJSRuntime::sRuntime, aReason);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
for (nsJSContext* cx = sContextList; cx; cx = cx->mNext) {
|
||||
cx->mActive = false;
|
||||
}
|
||||
nsContentUtils::XPConnect()->GarbageCollect(aReason, aGckind);
|
||||
}
|
||||
|
||||
//static
|
||||
|
@ -2963,7 +3025,7 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
|
|||
|
||||
if (sCCLockedOut) {
|
||||
// We're in the middle of an incremental GC; finish it first
|
||||
nsJSContext::GarbageCollectNow(js::gcreason::CC_FORCED, nsGCNormal);
|
||||
nsJSContext::GarbageCollectNow(js::gcreason::CC_FORCED, nsGCNormal, true);
|
||||
}
|
||||
|
||||
SAMPLE_LABEL("GC", "CycleCollectNow");
|
||||
|
@ -3100,7 +3162,8 @@ GCTimerFired(nsITimer *aTimer, void *aClosure)
|
|||
NS_RELEASE(sGCTimer);
|
||||
|
||||
uintptr_t reason = reinterpret_cast<uintptr_t>(aClosure);
|
||||
nsJSContext::GarbageCollectNow(static_cast<js::gcreason::Reason>(reason), nsGCIncremental);
|
||||
nsJSContext::GarbageCollectNow(static_cast<js::gcreason::Reason>(reason),
|
||||
nsGCNormal, false);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -3156,7 +3219,7 @@ CCTimerFired(nsITimer *aTimer, void *aClosure)
|
|||
}
|
||||
|
||||
// Finish the current incremental GC
|
||||
nsJSContext::GarbageCollectNow(js::gcreason::CC_FORCED, nsGCNormal);
|
||||
nsJSContext::GarbageCollectNow(js::gcreason::CC_FORCED, nsGCNormal, true);
|
||||
}
|
||||
|
||||
++sCCTimerFireCount;
|
||||
|
@ -3309,6 +3372,15 @@ nsJSContext::KillGCTimer()
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsJSContext::KillFullGCTimer()
|
||||
{
|
||||
if (sFullGCTimer) {
|
||||
sFullGCTimer->Cancel();
|
||||
NS_RELEASE(sFullGCTimer);
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void
|
||||
nsJSContext::KillShrinkGCBuffersTimer()
|
||||
|
@ -3336,6 +3408,7 @@ nsJSContext::KillCCTimer()
|
|||
void
|
||||
nsJSContext::GC(js::gcreason::Reason aReason)
|
||||
{
|
||||
mActive = true;
|
||||
PokeGC(aReason);
|
||||
}
|
||||
|
||||
|
@ -3417,20 +3490,23 @@ DOMGCSliceCallback(JSRuntime *aRt, js::GCProgress aProgress, const js::GCDescrip
|
|||
|
||||
sCCollectedWaitingForGC = 0;
|
||||
sCleanupsSinceLastGC = 0;
|
||||
|
||||
if (aDesc.isCompartment) {
|
||||
// If this is a compartment GC, restart it. We still want
|
||||
// a full GC to happen. Compartment GCs usually happen as a
|
||||
// result of last-ditch or MaybeGC. In both cases it is
|
||||
// probably a time of heavy activity and we want to delay
|
||||
// the full GC, but we do want it to happen eventually.
|
||||
nsJSContext::PokeGC(js::gcreason::POST_COMPARTMENT);
|
||||
}
|
||||
|
||||
sNeedsFullCC = true;
|
||||
nsJSContext::MaybePokeCC();
|
||||
|
||||
if (!aDesc.isCompartment) {
|
||||
if (aDesc.isCompartment) {
|
||||
++sCompartmentGCCount;
|
||||
if (!sFullGCTimer) {
|
||||
CallCreateInstance("@mozilla.org/timer;1", &sFullGCTimer);
|
||||
js::gcreason::Reason reason = js::gcreason::FULL_GC_TIMER;
|
||||
sFullGCTimer->InitWithFuncCallback(FullGCTimerFired,
|
||||
reinterpret_cast<void *>(reason),
|
||||
NS_FULL_GC_DELAY,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
}
|
||||
} else {
|
||||
sCompartmentGCCount = 0;
|
||||
nsJSContext::KillFullGCTimer();
|
||||
|
||||
// Avoid shrinking during heavy activity, which is suggested by
|
||||
// compartment GC.
|
||||
nsJSContext::PokeShrinkGCBuffers();
|
||||
|
@ -3530,7 +3606,7 @@ void
|
|||
nsJSRuntime::Startup()
|
||||
{
|
||||
// initialize all our statics, so that we can restart XPCOM
|
||||
sGCTimer = sCCTimer = nsnull;
|
||||
sGCTimer = sFullGCTimer = sCCTimer = nsnull;
|
||||
sCCLockedOut = false;
|
||||
sCCLockedOutTime = 0;
|
||||
sLastCCEndTime = 0;
|
||||
|
@ -3538,6 +3614,7 @@ nsJSRuntime::Startup()
|
|||
sLoadingInProgress = false;
|
||||
sCCollectedWaitingForGC = 0;
|
||||
sPostGCEventsToConsole = false;
|
||||
sDisableExplicitCompartmentGC = false;
|
||||
sNeedsFullCC = false;
|
||||
gNameSpaceManager = nsnull;
|
||||
sRuntimeService = nsnull;
|
||||
|
@ -3829,6 +3906,7 @@ nsJSRuntime::Shutdown()
|
|||
nsJSContext::KillGCTimer();
|
||||
nsJSContext::KillShrinkGCBuffersTimer();
|
||||
nsJSContext::KillCCTimer();
|
||||
nsJSContext::KillFullGCTimer();
|
||||
|
||||
NS_IF_RELEASE(gNameSpaceManager);
|
||||
|
||||
|
|
|
@ -184,7 +184,9 @@ public:
|
|||
static void LoadStart();
|
||||
static void LoadEnd();
|
||||
|
||||
static void GarbageCollectNow(js::gcreason::Reason reason, PRUint32 gckind = nsGCNormal);
|
||||
static void GarbageCollectNow(js::gcreason::Reason reason,
|
||||
PRUint32 aGckind,
|
||||
bool aGlobal);
|
||||
static void ShrinkGCBuffersNow();
|
||||
// If aExtraForgetSkippableCalls is -1, forgetSkippable won't be
|
||||
// called even if the previous collection was GC.
|
||||
|
@ -199,6 +201,7 @@ public:
|
|||
|
||||
static void MaybePokeCC();
|
||||
static void KillCCTimer();
|
||||
static void KillFullGCTimer();
|
||||
|
||||
virtual void GC(js::gcreason::Reason aReason);
|
||||
|
||||
|
@ -238,7 +241,7 @@ private:
|
|||
nsrefcnt GetCCRefcnt();
|
||||
|
||||
JSContext *mContext;
|
||||
PRUint32 mNumEvaluations;
|
||||
bool mActive;
|
||||
|
||||
protected:
|
||||
struct TerminationFuncHolder;
|
||||
|
@ -307,6 +310,9 @@ private:
|
|||
PRTime mModalStateTime;
|
||||
PRUint32 mModalStateDepth;
|
||||
|
||||
nsJSContext *mNext;
|
||||
nsJSContext **mPrev;
|
||||
|
||||
// mGlobalObjectRef ensures that the outer window stays alive as long as the
|
||||
// context does. It is eventually collected by the cycle collector.
|
||||
nsCOMPtr<nsIScriptGlobalObject> mGlobalObjectRef;
|
||||
|
|
|
@ -2810,7 +2810,8 @@ class Parser(Tokenizer):
|
|||
|
||||
def p_ExtendedAttributeIdent(self, p):
|
||||
"""
|
||||
ExtendedAttributeIdent : IDENTIFIER EQUALS IDENTIFIER
|
||||
ExtendedAttributeIdent : IDENTIFIER EQUALS STRING
|
||||
| IDENTIFIER EQUALS IDENTIFIER
|
||||
"""
|
||||
p[0] = (p[1], p[3])
|
||||
|
||||
|
|
|
@ -9,3 +9,13 @@ def WebIDLTest(parser, harness):
|
|||
""")
|
||||
|
||||
results = parser.finish()
|
||||
|
||||
parser = parser.reset()
|
||||
parser.parse("""
|
||||
[Flippety="foo.bar",Floppety=flop]
|
||||
interface TestExtendedAttr {
|
||||
[Foopy="foo.bar"] attribute byte b;
|
||||
};
|
||||
""")
|
||||
|
||||
results = parser.finish()
|
||||
|
|
|
@ -791,14 +791,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;
|
||||
}
|
||||
|
|
|
@ -81,6 +81,7 @@ waitFor(function() {
|
|||
# Verify that the callstate changes to connected on the caller as well.
|
||||
self.assertTrue(receiver.execute_async_script("""
|
||||
waitFor(function() {
|
||||
window.wrappedJSObject.incoming.hangUp();
|
||||
marionetteScriptFinished(true);
|
||||
}, function() {
|
||||
return window.wrappedJSObject.callstate == "connected";
|
||||
|
|
|
@ -52,4 +52,6 @@ window.navigator.mozTelephony.dial("%s");
|
|||
# Verify the phone number of the incoming call.
|
||||
self.assertEqual(received, fromPhoneNumber)
|
||||
|
||||
sender.execute_script("window.navigator.mozTelephony.calls[0].hangUp();")
|
||||
receiver.execute_script("window.navigator.mozTelephony.calls[0].hangUp();")
|
||||
|
||||
|
|
|
@ -181,6 +181,12 @@ struct JSCompartment
|
|||
gcState = GCScheduled;
|
||||
}
|
||||
|
||||
void unscheduleGC() {
|
||||
JS_ASSERT(!rt->gcRunning);
|
||||
JS_ASSERT(gcState != GCRunning);
|
||||
gcState = NoGCScheduled;
|
||||
}
|
||||
|
||||
bool isGCScheduled() const {
|
||||
return gcState == GCScheduled;
|
||||
}
|
||||
|
|
|
@ -155,6 +155,12 @@ js::IsGCScheduled(JSRuntime *rt)
|
|||
return false;
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js::SkipCompartmentForGC(JSCompartment *comp)
|
||||
{
|
||||
comp->unscheduleGC();
|
||||
}
|
||||
|
||||
JS_FRIEND_API(void)
|
||||
js::GCForReason(JSRuntime *rt, gcreason::Reason reason)
|
||||
{
|
||||
|
|
|
@ -622,7 +622,8 @@ SizeOfJSContext();
|
|||
D(DOM_IPC) \
|
||||
D(DOM_WORKER) \
|
||||
D(INTER_SLICE_GC) \
|
||||
D(REFRESH_FRAME)
|
||||
D(REFRESH_FRAME) \
|
||||
D(FULL_GC_TIMER)
|
||||
|
||||
namespace gcreason {
|
||||
|
||||
|
@ -646,6 +647,9 @@ PrepareForFullGC(JSRuntime *rt);
|
|||
extern JS_FRIEND_API(bool)
|
||||
IsGCScheduled(JSRuntime *rt);
|
||||
|
||||
extern JS_FRIEND_API(void)
|
||||
SkipCompartmentForGC(JSCompartment *comp);
|
||||
|
||||
/*
|
||||
* When triggering a GC using one of the functions below, it is first necessary
|
||||
* to select the compartments to be collected. To do this, you can call
|
||||
|
|
|
@ -111,6 +111,7 @@ class Marionette(object):
|
|||
self.session = None
|
||||
self.window = None
|
||||
self.emulator = None
|
||||
self.extra_emulators = []
|
||||
self.homedir = homedir
|
||||
self.baseurl = baseurl
|
||||
self.noWindow = noWindow
|
||||
|
@ -141,6 +142,8 @@ class Marionette(object):
|
|||
self.emulator.close()
|
||||
if self.b2gbin:
|
||||
self.b2ginstance.close()
|
||||
for qemu in self.extra_emulators:
|
||||
qemu.emulator.close()
|
||||
|
||||
def _send_message(self, command, response_key, **kwargs):
|
||||
if not self.session and command not in ('newSession', 'getStatus'):
|
||||
|
@ -313,7 +316,7 @@ class Marionette(object):
|
|||
|
||||
return unwrapped
|
||||
|
||||
def execute_js_script(self, script, script_args=None, timeout=True):
|
||||
def execute_js_script(self, script, script_args=None, timeout=True, new_sandbox=True):
|
||||
if script_args is None:
|
||||
script_args = []
|
||||
args = self.wrapArguments(script_args)
|
||||
|
@ -321,21 +324,30 @@ class Marionette(object):
|
|||
'value',
|
||||
value=script,
|
||||
args=args,
|
||||
timeout=timeout)
|
||||
timeout=timeout,
|
||||
newSandbox=new_sandbox)
|
||||
return self.unwrapValue(response)
|
||||
|
||||
def execute_script(self, script, script_args=None):
|
||||
def execute_script(self, script, script_args=None, new_sandbox=True):
|
||||
if script_args is None:
|
||||
script_args = []
|
||||
args = self.wrapArguments(script_args)
|
||||
response = self._send_message('executeScript', 'value', value=script, args=args)
|
||||
response = self._send_message('executeScript',
|
||||
'value',
|
||||
value=script,
|
||||
args=args,
|
||||
newSandbox=new_sandbox)
|
||||
return self.unwrapValue(response)
|
||||
|
||||
def execute_async_script(self, script, script_args=None):
|
||||
def execute_async_script(self, script, script_args=None, new_sandbox=True):
|
||||
if script_args is None:
|
||||
script_args = []
|
||||
args = self.wrapArguments(script_args)
|
||||
response = self._send_message('executeAsyncScript', 'value', value=script, args=args)
|
||||
response = self._send_message('executeAsyncScript',
|
||||
'value',
|
||||
value=script,
|
||||
args=args,
|
||||
newSandbox=new_sandbox)
|
||||
return self.unwrapValue(response)
|
||||
|
||||
def find_element(self, method, target, id=None):
|
||||
|
|
|
@ -18,7 +18,6 @@ def skip_if_b2g(target):
|
|||
class CommonTestCase(unittest.TestCase):
|
||||
|
||||
def __init__(self, methodName):
|
||||
self._qemu = []
|
||||
unittest.TestCase.__init__(self, methodName)
|
||||
|
||||
def kill_gaia_app(self, url):
|
||||
|
@ -56,26 +55,27 @@ window.addEventListener('message', function frameload(e) {
|
|||
def tearDown(self):
|
||||
if self.marionette.session is not None:
|
||||
self.marionette.delete_session()
|
||||
for _qemu in self._qemu:
|
||||
_qemu.emulator.close()
|
||||
_qemu = None
|
||||
self._qemu = []
|
||||
|
||||
|
||||
class MarionetteTestCase(CommonTestCase):
|
||||
|
||||
def __init__(self, marionette, methodName='runTest', **kwargs):
|
||||
self.marionette = marionette
|
||||
self.extra_emulator_index = -1
|
||||
CommonTestCase.__init__(self, methodName, **kwargs)
|
||||
|
||||
def get_new_emulator(self):
|
||||
_qemu = Marionette(emulator=True,
|
||||
self.extra_emulator_index += 1
|
||||
if len(self.marionette.extra_emulators) == self.extra_emulator_index:
|
||||
qemu = Marionette(emulator=True,
|
||||
homedir=self.marionette.homedir,
|
||||
baseurl=self.marionette.baseurl,
|
||||
noWindow=self.marionette.noWindow)
|
||||
_qemu.start_session()
|
||||
self._qemu.append(_qemu)
|
||||
return _qemu
|
||||
qemu.start_session()
|
||||
self.marionette.extra_emulators.append(qemu)
|
||||
else:
|
||||
qemu = self.marionette.extra_emulators[self.extra_emulator_index]
|
||||
return qemu
|
||||
|
||||
|
||||
class MarionetteJSTestCase(CommonTestCase):
|
||||
|
|
|
@ -71,11 +71,11 @@ class TestExecuteAsyncContent(MarionetteTestCase):
|
|||
def test_same_context(self):
|
||||
var1 = 'testing'
|
||||
self.assertEqual(self.marionette.execute_script("""
|
||||
window.wrappedJSObject._testvar = '%s';
|
||||
return window.wrappedJSObject._testvar;
|
||||
this.testvar = '%s';
|
||||
return this.testvar;
|
||||
""" % var1), var1)
|
||||
self.assertEqual(self.marionette.execute_async_script(
|
||||
"marionetteScriptFinished(window.wrappedJSObject._testvar);"), var1)
|
||||
"marionetteScriptFinished(this.testvar);", new_sandbox=False), var1)
|
||||
|
||||
def test_execute_no_return(self):
|
||||
self.assertEqual(self.marionette.execute_async_script("marionetteScriptFinished()"), None)
|
||||
|
@ -103,6 +103,19 @@ let prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
|||
marionetteScriptFinished(1);
|
||||
""")
|
||||
|
||||
def test_sandbox_reuse(self):
|
||||
# Sandboxes between `execute_script()` invocations are shared.
|
||||
self.marionette.execute_async_script("this.foobar = [23, 42];"
|
||||
"marionetteScriptFinished();")
|
||||
self.assertEqual(self.marionette.execute_async_script(
|
||||
"marionetteScriptFinished(this.foobar);", new_sandbox=False), [23, 42])
|
||||
|
||||
self.marionette.execute_async_script("global.barfoo = [42, 23];"
|
||||
"marionetteScriptFinished();")
|
||||
self.assertEqual(self.marionette.execute_async_script(
|
||||
"marionetteScriptFinished(global.barfoo);", new_sandbox=False), [42, 23])
|
||||
|
||||
|
||||
class TestExecuteAsyncChrome(TestExecuteAsyncContent):
|
||||
def setUp(self):
|
||||
super(TestExecuteAsyncChrome, self).setUp()
|
||||
|
@ -120,3 +133,6 @@ var c = Components.classes;
|
|||
marionetteScriptFinished(1);
|
||||
"""))
|
||||
|
||||
def test_sandbox_reuse(self):
|
||||
pass
|
||||
|
||||
|
|
|
@ -67,6 +67,13 @@ let prefs = Components.classes["@mozilla.org/preferences-service;1"]
|
|||
self.assertEqual(self.marionette.execute_script("return {'foo': [1, 'a', 2]};"),
|
||||
{'foo': [1, 'a', 2]})
|
||||
|
||||
def test_sandbox_reuse(self):
|
||||
# Sandboxes between `execute_script()` invocations are shared.
|
||||
self.marionette.execute_script("this.foobar = [23, 42];")
|
||||
self.assertEqual(self.marionette.execute_script("return this.foobar;", new_sandbox=False), [23, 42])
|
||||
|
||||
self.marionette.execute_script("global.barfoo = [42, 23];")
|
||||
self.assertEqual(self.marionette.execute_script("return global.barfoo;", new_sandbox=False), [42, 23])
|
||||
|
||||
class TestExecuteChrome(TestExecuteContent):
|
||||
def setUp(self):
|
||||
|
@ -76,3 +83,5 @@ class TestExecuteChrome(TestExecuteContent):
|
|||
def test_execute_permission(self):
|
||||
self.assertEqual(1, self.marionette.execute_script("var c = Components.classes;return 1;"))
|
||||
|
||||
def test_sandbox_reuse(self):
|
||||
pass
|
||||
|
|
|
@ -472,8 +472,16 @@ MarionetteDriverActor.prototype = {
|
|||
* function body
|
||||
*/
|
||||
execute: function MDA_execute(aRequest, directInject) {
|
||||
logger.info("newSandbox: " + aRequest.newSandbox);
|
||||
if (aRequest.newSandbox == undefined) {
|
||||
//if client does not send a value in newSandbox,
|
||||
//then they expect the same behaviour as webdriver
|
||||
aRequest.newSandbox = true;
|
||||
}
|
||||
if (this.context == "content") {
|
||||
this.sendAsync("executeScript", {value: aRequest.value, args: aRequest.args});
|
||||
this.sendAsync("executeScript", {value: aRequest.value,
|
||||
args: aRequest.args,
|
||||
newSandbox:aRequest.newSandbox});
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -533,6 +541,11 @@ MarionetteDriverActor.prototype = {
|
|||
*/
|
||||
executeJSScript: function MDA_executeJSScript(aRequest) {
|
||||
//all pure JS scripts will need to call Marionette.finish() to complete the test.
|
||||
if (aRequest.newSandbox == undefined) {
|
||||
//if client does not send a value in newSandbox,
|
||||
//then they expect the same behaviour as webdriver
|
||||
aRequest.newSandbox = true;
|
||||
}
|
||||
if (this.context == "chrome") {
|
||||
if (aRequest.timeout) {
|
||||
this.executeWithCallback(aRequest, aRequest.timeout);
|
||||
|
@ -562,12 +575,18 @@ MarionetteDriverActor.prototype = {
|
|||
* function body
|
||||
*/
|
||||
executeWithCallback: function MDA_executeWithCallback(aRequest, directInject) {
|
||||
if (aRequest.newSandbox == undefined) {
|
||||
//if client does not send a value in newSandbox,
|
||||
//then they expect the same behaviour as webdriver
|
||||
aRequest.newSandbox = true;
|
||||
}
|
||||
this.command_id = this.uuidGen.generateUUID().toString();
|
||||
|
||||
if (this.context == "content") {
|
||||
this.sendAsync("executeAsyncScript", {value: aRequest.value,
|
||||
args: aRequest.args,
|
||||
id: this.command_id});
|
||||
id: this.command_id,
|
||||
newSandbox: aRequest.newSandbox});
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,6 +29,15 @@ let activeFrame = null;
|
|||
let curWindow = content;
|
||||
let elementManager = new ElementManager([]);
|
||||
|
||||
// The sandbox we execute test scripts in. Gets lazily created in
|
||||
// createExecuteContentSandbox().
|
||||
let sandbox;
|
||||
|
||||
// Flag to indicate whether an async script is currently running or not.
|
||||
let asyncTestRunning = false;
|
||||
let asyncTestCommandId;
|
||||
let asyncTestTimeoutId;
|
||||
|
||||
/**
|
||||
* Called when listener is first started up.
|
||||
* The listener sends its unique window ID and its current URI to the actor.
|
||||
|
@ -178,6 +187,7 @@ function sendError(message, status, trace, command_id) {
|
|||
* Clear test values after completion of test
|
||||
*/
|
||||
function resetValues() {
|
||||
sandbox = null;
|
||||
marionetteTimeout = null;
|
||||
curWin = content;
|
||||
}
|
||||
|
@ -197,28 +207,54 @@ function errUnload() {
|
|||
/**
|
||||
* Returns a content sandbox that can be used by the execute_foo functions.
|
||||
*/
|
||||
function createExecuteContentSandbox(aWindow, marionette, args) {
|
||||
try {
|
||||
args = elementManager.convertWrappedArguments(args, aWindow);
|
||||
}
|
||||
catch(e) {
|
||||
sendError(e.message, e.num, e.stack);
|
||||
return;
|
||||
}
|
||||
|
||||
function createExecuteContentSandbox(aWindow) {
|
||||
let sandbox = new Cu.Sandbox(aWindow);
|
||||
sandbox.global = sandbox;
|
||||
sandbox.window = aWindow;
|
||||
sandbox.document = sandbox.window.document;
|
||||
sandbox.navigator = sandbox.window.navigator;
|
||||
sandbox.__namedArgs = elementManager.applyNamedArgs(args);
|
||||
sandbox.__marionetteParams = args;
|
||||
sandbox.__proto__ = sandbox.window;
|
||||
sandbox.testUtils = utils;
|
||||
|
||||
let marionette = new Marionette(false, aWindow, "content", marionetteLogObj);
|
||||
sandbox.marionette = marionette;
|
||||
marionette.exports.forEach(function(fn) {
|
||||
sandbox[fn] = marionette[fn].bind(marionette);
|
||||
});
|
||||
|
||||
sandbox.asyncComplete = function sandbox_asyncComplete(value, status) {
|
||||
curWindow.removeEventListener("unload", errUnload, false);
|
||||
|
||||
/* clear all timeouts potentially generated by the script*/
|
||||
for (let i = 0; i <= asyncTestTimeoutId; i++) {
|
||||
curWindow.clearTimeout(i);
|
||||
}
|
||||
|
||||
sendSyncMessage("Marionette:testLog",
|
||||
{value: elementManager.wrapValue(marionetteLogObj.getLogs())});
|
||||
marionetteLogObj.clearLogs();
|
||||
if (status == 0){
|
||||
sendResponse({value: elementManager.wrapValue(value), status: status}, asyncTestCommandId);
|
||||
}
|
||||
else {
|
||||
sendError(value, status, null, asyncTestCommandId);
|
||||
}
|
||||
|
||||
asyncTestRunning = false;
|
||||
asyncTestTimeoutId = undefined;
|
||||
asyncTestCommandId = undefined;
|
||||
};
|
||||
sandbox.finish = function sandbox_finish() {
|
||||
if (asyncTestRunning) {
|
||||
sandbox.asyncComplete(marionette.generate_results(), 0);
|
||||
} else {
|
||||
return marionette.generate_results();
|
||||
}
|
||||
};
|
||||
sandbox.marionetteScriptFinished = function sandbox_marionetteScriptFinished(value) {
|
||||
return sandbox.asyncComplete(value, 0);
|
||||
};
|
||||
|
||||
return sandbox;
|
||||
}
|
||||
|
||||
|
@ -228,15 +264,14 @@ function createExecuteContentSandbox(aWindow, marionette, args) {
|
|||
*/
|
||||
function executeScript(msg, directInject) {
|
||||
let script = msg.json.value;
|
||||
let marionette = new Marionette(false, curWindow, "content", marionetteLogObj);
|
||||
|
||||
let sandbox = createExecuteContentSandbox(curWindow, marionette, msg.json.args);
|
||||
if (!sandbox)
|
||||
if (msg.json.newSandbox || !sandbox) {
|
||||
sandbox = createExecuteContentSandbox(curWindow);
|
||||
if (!sandbox) {
|
||||
sendError("Could not create sandbox!");
|
||||
return;
|
||||
|
||||
sandbox.finish = function sandbox_finish() {
|
||||
return marionette.generate_results();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (directInject) {
|
||||
|
@ -251,6 +286,15 @@ function executeScript(msg, directInject) {
|
|||
}
|
||||
}
|
||||
else {
|
||||
try {
|
||||
sandbox.__marionetteParams = elementManager.convertWrappedArguments(
|
||||
msg.json.args, curWindow);
|
||||
}
|
||||
catch(e) {
|
||||
sendError(e.message, e.num, e.stack);
|
||||
return;
|
||||
}
|
||||
|
||||
let scriptSrc = "let __marionetteFunc = function(){" + script + "};" +
|
||||
"__marionetteFunc.apply(null, __marionetteParams);";
|
||||
let res = Cu.evalInSandbox(scriptSrc, sandbox, "1.8");
|
||||
|
@ -302,39 +346,29 @@ function executeJSScript(msg) {
|
|||
function executeWithCallback(msg, timeout) {
|
||||
curWindow.addEventListener("unload", errUnload, false);
|
||||
let script = msg.json.value;
|
||||
let command_id = msg.json.id;
|
||||
asyncTestCommandId = msg.json.id;
|
||||
|
||||
// Error code 28 is scriptTimeout, but spec says execute_async should return 21 (Timeout),
|
||||
// see http://code.google.com/p/selenium/wiki/JsonWireProtocol#/session/:sessionId/execute_async.
|
||||
// However Selenium code returns 28, see
|
||||
// http://code.google.com/p/selenium/source/browse/trunk/javascript/firefox-driver/js/evaluate.js.
|
||||
// We'll stay compatible with the Selenium code.
|
||||
let timeoutId = curWindow.setTimeout(function() {
|
||||
contentAsyncReturnFunc('timed out', 28);
|
||||
asyncTestTimeoutId = curWindow.setTimeout(function() {
|
||||
sandbox.asyncComplete('timed out', 28);
|
||||
}, marionetteTimeout);
|
||||
curWindow.addEventListener('error', function win__onerror(evt) {
|
||||
curWindow.removeEventListener('error', win__onerror, true);
|
||||
contentAsyncReturnFunc(evt, 17);
|
||||
sandbox.asyncComplete(evt, 17);
|
||||
return true;
|
||||
}, true);
|
||||
|
||||
function contentAsyncReturnFunc(value, status) {
|
||||
curWindow.removeEventListener("unload", errUnload, false);
|
||||
|
||||
/* clear all timeouts potentially generated by the script*/
|
||||
for(let i=0; i<=timeoutId; i++) {
|
||||
curWindow.clearTimeout(i);
|
||||
if (msg.json.newSandbox || !sandbox) {
|
||||
sandbox = createExecuteContentSandbox(curWindow);
|
||||
if (!sandbox) {
|
||||
sendError("Could not create sandbox!");
|
||||
return;
|
||||
}
|
||||
|
||||
sendSyncMessage("Marionette:testLog", {value: elementManager.wrapValue(marionetteLogObj.getLogs())});
|
||||
marionetteLogObj.clearLogs();
|
||||
if (status == 0){
|
||||
sendResponse({value: elementManager.wrapValue(value), status: status}, command_id);
|
||||
}
|
||||
else {
|
||||
sendError(value, status, null, command_id);
|
||||
}
|
||||
};
|
||||
|
||||
let scriptSrc;
|
||||
if (timeout) {
|
||||
|
@ -344,24 +378,22 @@ function executeWithCallback(msg, timeout) {
|
|||
scriptSrc = script;
|
||||
}
|
||||
else {
|
||||
scriptSrc = "let marionetteScriptFinished = function(value) { return asyncComplete(value,0);};" +
|
||||
"__marionetteParams.push(marionetteScriptFinished);" +
|
||||
try {
|
||||
sandbox.__marionetteParams = elementManager.convertWrappedArguments(
|
||||
msg.json.args, curWindow);
|
||||
}
|
||||
catch(e) {
|
||||
sendError(e.message, e.num, e.stack);
|
||||
return;
|
||||
}
|
||||
|
||||
scriptSrc = "__marionetteParams.push(marionetteScriptFinished);" +
|
||||
"let __marionetteFunc = function() { " + script + "};" +
|
||||
"__marionetteFunc.apply(null, __marionetteParams); ";
|
||||
}
|
||||
|
||||
let marionette = new Marionette(true, curWindow, "content", marionetteLogObj);
|
||||
|
||||
let sandbox = createExecuteContentSandbox(curWindow, marionette, msg.json.args);
|
||||
if (!sandbox)
|
||||
return;
|
||||
|
||||
sandbox.asyncComplete = contentAsyncReturnFunc;
|
||||
sandbox.finish = function sandbox_finish() {
|
||||
contentAsyncReturnFunc(marionette.generate_results(), 0);
|
||||
};
|
||||
|
||||
try {
|
||||
asyncTestRunning = true;
|
||||
Cu.evalInSandbox(scriptSrc, sandbox, "1.8");
|
||||
} catch (e) {
|
||||
// 17 = JavascriptException
|
||||
|
@ -621,13 +653,15 @@ function switchToFrame(msg) {
|
|||
}
|
||||
break;
|
||||
}
|
||||
if (foundFrame != null) {
|
||||
if (foundFrame == null) {
|
||||
sendError("Unable to locate frame: " + msg.json.value, 8, null);
|
||||
return;
|
||||
}
|
||||
curWindow = curWindow.frames[foundFrame];
|
||||
curWindow.focus();
|
||||
sendOk();
|
||||
} else {
|
||||
sendError("Unable to locate frame: " + msg.json.value, 8, null);
|
||||
}
|
||||
|
||||
sandbox = null;
|
||||
}
|
||||
|
||||
//call register self when we get loaded
|
||||
|
|
|
@ -11,10 +11,11 @@ function Marionette(is_async, window, context, logObj) {
|
|||
this.tests = [];
|
||||
this.logObj = logObj;
|
||||
this.context = context;
|
||||
this.exports = ['ok', 'is', 'isnot', 'log', 'getLogs', 'generate_results', 'waitFor'];
|
||||
}
|
||||
|
||||
Marionette.prototype = {
|
||||
exports: ['ok', 'is', 'isnot', 'log', 'getLogs', 'generate_results', 'waitFor'],
|
||||
|
||||
ok: function Marionette__ok(condition, name, diag) {
|
||||
let test = {'result': !!condition, 'name': name, 'diag': diag};
|
||||
this.logResult(test, "TEST-PASS", "TEST-UNEXPECTED-FAIL");
|
||||
|
@ -62,6 +63,8 @@ Marionette.prototype = {
|
|||
'diag': this.tests[i].diag});
|
||||
}
|
||||
}
|
||||
// Reset state in case this object is reused for more tests.
|
||||
this.tests = [];
|
||||
return {"passed": passed, "failed": failed, "failures": failures};
|
||||
},
|
||||
|
||||
|
|
|
@ -0,0 +1,291 @@
|
|||
# ***** BEGIN LICENSE BLOCK *****
|
||||
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
#
|
||||
# The contents of this file are subject to the Mozilla Public License Version
|
||||
# 1.1 (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
# http://www.mozilla.org/MPL/
|
||||
#
|
||||
# Software distributed under the License is distributed on an "AS IS" basis,
|
||||
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
# for the specific language governing rights and limitations under the
|
||||
# License.
|
||||
#
|
||||
# The Original Code is an NSIS installer for the maintenance service
|
||||
#
|
||||
# The Initial Developer of the Original Code is
|
||||
# Mozilla Foundation.
|
||||
# Portions created by the Initial Developer are Copyright (C) 2011
|
||||
# the Initial Developer. All Rights Reserved.
|
||||
#
|
||||
# Contributor(s):
|
||||
# Brian R. Bondy <netzen@gmail.com>
|
||||
#
|
||||
# Alternatively, the contents of this file may be used under the terms of
|
||||
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
# in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
# of those above. If you wish to allow use of your version of this file only
|
||||
# under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
# use your version of this file under the terms of the MPL, indicate your
|
||||
# decision by deleting the provisions above and replace them with the notice
|
||||
# and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
# the provisions above, a recipient may use your version of this file under
|
||||
# the terms of any one of the MPL, the GPL or the LGPL.
|
||||
#
|
||||
# ***** END LICENSE BLOCK *****
|
||||
|
||||
; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
|
||||
!verbose 3
|
||||
|
||||
; 7-Zip provides better compression than the lzma from NSIS so we add the files
|
||||
; uncompressed and use 7-Zip to create a SFX archive of it
|
||||
SetDatablockOptimize on
|
||||
SetCompress off
|
||||
CRCCheck on
|
||||
|
||||
RequestExecutionLevel admin
|
||||
!addplugindir ./
|
||||
|
||||
; Variables
|
||||
Var TempMaintServiceName
|
||||
Var BrandFullNameDA
|
||||
Var BrandFullName
|
||||
|
||||
; Other included files may depend upon these includes!
|
||||
; The following includes are provided by NSIS.
|
||||
!include FileFunc.nsh
|
||||
!include LogicLib.nsh
|
||||
!include MUI.nsh
|
||||
!include WinMessages.nsh
|
||||
!include WinVer.nsh
|
||||
!include WordFunc.nsh
|
||||
|
||||
!insertmacro GetOptions
|
||||
!insertmacro GetParameters
|
||||
!insertmacro GetSize
|
||||
|
||||
; The test slaves use this fallback key to run tests.
|
||||
; And anyone that wants to run tests themselves should already have
|
||||
; this installed.
|
||||
!define FallbackKey \
|
||||
"SOFTWARE\Mozilla\MaintenanceService\3932ecacee736d366d6436db0f55bce4"
|
||||
|
||||
!define CompanyName "Mozilla Corporation"
|
||||
!define BrandFullNameInternal ""
|
||||
|
||||
; The following includes are custom.
|
||||
!include defines.nsi
|
||||
; We keep defines.nsi defined so that we get other things like
|
||||
; the version number, but we redefine BrandFullName
|
||||
!define MaintFullName "Mozilla Maintenance Service"
|
||||
!undef BrandFullName
|
||||
!define BrandFullName "${MaintFullName}"
|
||||
|
||||
!include common.nsh
|
||||
!include locales.nsi
|
||||
|
||||
VIAddVersionKey "FileDescription" "${MaintFullName} Installer"
|
||||
VIAddVersionKey "OriginalFilename" "maintenanceservice_installer.exe"
|
||||
|
||||
Name "${MaintFullName}"
|
||||
OutFile "maintenanceservice_installer.exe"
|
||||
|
||||
; Get installation folder from registry if available
|
||||
InstallDirRegKey HKLM "Software\Mozilla\MaintenanceService" ""
|
||||
|
||||
SetOverwrite on
|
||||
|
||||
!define MaintUninstallKey \
|
||||
"Software\Microsoft\Windows\CurrentVersion\Uninstall\MozillaMaintenanceService"
|
||||
|
||||
; The HAVE_64BIT_OS define also means that we have an x64 build,
|
||||
; not just an x64 OS.
|
||||
!ifdef HAVE_64BIT_OS
|
||||
; See below, we actually abort the install for x64 builds currently.
|
||||
InstallDir "$PROGRAMFILES64\${MaintFullName}\"
|
||||
!else
|
||||
InstallDir "$PROGRAMFILES32\${MaintFullName}\"
|
||||
!endif
|
||||
ShowUnInstDetails nevershow
|
||||
|
||||
################################################################################
|
||||
# Modern User Interface - MUI
|
||||
|
||||
!define MUI_ICON setup.ico
|
||||
!define MUI_UNICON setup.ico
|
||||
!define MUI_WELCOMEPAGE_TITLE_3LINES
|
||||
!define MUI_UNWELCOMEFINISHPAGE_BITMAP wizWatermark.bmp
|
||||
|
||||
;Interface Settings
|
||||
!define MUI_ABORTWARNING
|
||||
|
||||
; Uninstaller Pages
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
################################################################################
|
||||
# Language
|
||||
|
||||
!insertmacro MOZ_MUI_LANGUAGE 'baseLocale'
|
||||
!verbose push
|
||||
!verbose 3
|
||||
!include "overrideLocale.nsh"
|
||||
!include "customLocale.nsh"
|
||||
!verbose pop
|
||||
|
||||
; Set this after the locale files to override it if it is in the locale
|
||||
; using " " for BrandingText will hide the "Nullsoft Install System..." branding
|
||||
BrandingText " "
|
||||
|
||||
Function .onInit
|
||||
SetSilent silent
|
||||
!ifdef HAVE_64BIT_OS
|
||||
; We plan to eventually enable 64bit native builds to use the maintenance
|
||||
; service, but for the initial release, to reduce testing and development,
|
||||
; 64-bit builds will not install the maintenanceservice.
|
||||
Abort
|
||||
!endif
|
||||
|
||||
; On Windows 2000 we do not install the maintenance service.
|
||||
; We won't run this installer from the parent installer, but just in case
|
||||
; someone tries to execute it on Windows 2000...
|
||||
${Unless} ${AtLeastWinXP}
|
||||
Abort
|
||||
${EndUnless}
|
||||
FunctionEnd
|
||||
|
||||
Function un.onInit
|
||||
StrCpy $BrandFullNameDA "${MaintFullName}"
|
||||
StrCpy $BrandFullName "${MaintFullName}"
|
||||
FunctionEnd
|
||||
|
||||
Section "MaintenanceService"
|
||||
AllowSkipFiles off
|
||||
|
||||
CreateDirectory $INSTDIR
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
; If the service already exists, then it will be stopped when upgrading it
|
||||
; via the maintenanceservice_tmp.exe command executed below.
|
||||
; The maintenanceservice_tmp.exe command will rename the file to
|
||||
; maintenanceservice.exe if maintenanceservice_tmp.exe is newer.
|
||||
; If the service does not exist yet, we install it and drop the file on
|
||||
; disk as maintenanceservice.exe directly.
|
||||
StrCpy $TempMaintServiceName "maintenanceservice.exe"
|
||||
IfFileExists "$INSTDIR\maintenanceservice.exe" 0 skipAlreadyExists
|
||||
StrCpy $TempMaintServiceName "maintenanceservice_tmp.exe"
|
||||
skipAlreadyExists:
|
||||
|
||||
; We always write out a copy and then decide whether to install it or
|
||||
; not via calling its 'install' cmdline which works by version comparison.
|
||||
CopyFiles "$EXEDIR\maintenanceservice.exe" "$INSTDIR\$TempMaintServiceName"
|
||||
|
||||
; The updater.ini file is only used when performing an install or upgrade,
|
||||
; and only if that install or upgrade is successful. If an old updater.ini
|
||||
; happened to be copied into the maintenance service installation directory
|
||||
; but the service was not newer, the updater.ini file would be unused.
|
||||
; It is used to fill the description of the service on success.
|
||||
CopyFiles "$EXEDIR\updater.ini" "$INSTDIR\updater.ini"
|
||||
|
||||
; Install the application maintenance service.
|
||||
; If a service already exists, the command line parameter will stop the
|
||||
; service and only install itself if it is newer than the already installed
|
||||
; service. If successful it will remove the old maintenanceservice.exe
|
||||
; and replace it with maintenanceservice_tmp.exe.
|
||||
ClearErrors
|
||||
;${GetParameters} $0
|
||||
;${GetOptions} "$0" "/Upgrade" $0
|
||||
;${If} ${Errors}
|
||||
nsExec::Exec '"$INSTDIR\$TempMaintServiceName" forceinstall'
|
||||
;${Else}
|
||||
; The upgrade cmdline is the same as install except
|
||||
; It will fail if the service isn't already installed.
|
||||
; nsExec::Exec '"$INSTDIR\$TempMaintServiceName" upgrade'
|
||||
;${EndIf}
|
||||
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
WriteRegStr HKLM "${MaintUninstallKey}" "DisplayName" "${MaintFullName}"
|
||||
WriteRegStr HKLM "${MaintUninstallKey}" "UninstallString" \
|
||||
'"$INSTDIR\uninstall.exe"'
|
||||
WriteRegStr HKLM "${MaintUninstallKey}" "DisplayIcon" \
|
||||
"$INSTDIR\Uninstall.exe,0"
|
||||
WriteRegStr HKLM "${MaintUninstallKey}" "DisplayVersion" "${AppVersion}"
|
||||
WriteRegStr HKLM "${MaintUninstallKey}" "Publisher" "Mozilla"
|
||||
WriteRegStr HKLM "${MaintUninstallKey}" "Comments" \
|
||||
"${BrandFullName} ${AppVersion} (${ARCH} ${AB_CD})"
|
||||
WriteRegDWORD HKLM "${MaintUninstallKey}" "NoModify" 1
|
||||
${GetSize} "$INSTDIR" "/S=0K" $R2 $R3 $R4
|
||||
WriteRegDWORD HKLM "${MaintUninstallKey}" "EstimatedSize" $R2
|
||||
|
||||
; Write out that a maintenance service was attempted.
|
||||
; We do this because on upgrades we will check this value and we only
|
||||
; want to install once on the first upgrade to maintenance service.
|
||||
; Also write out that we are currently installed, preferences will check
|
||||
; this value to determine if we should show the service update pref.
|
||||
; Since the Maintenance service can be installed either x86 or x64,
|
||||
; always use the 64-bit registry for checking if an attempt was made.
|
||||
${If} ${RunningX64}
|
||||
SetRegView 64
|
||||
${EndIf}
|
||||
WriteRegDWORD HKLM "Software\Mozilla\MaintenanceService" "Attempted" 1
|
||||
WriteRegDWORD HKLM "Software\Mozilla\MaintenanceService" "Installed" 1
|
||||
|
||||
; Included here for debug purposes only.
|
||||
; These keys are used to bypass the installation dir is a valid installation
|
||||
; check from the service so that tests can be run.
|
||||
WriteRegStr HKLM "${FallbackKey}\0" "name" "Mozilla Corporation"
|
||||
WriteRegStr HKLM "${FallbackKey}\0" "issuer" "Thawte Code Signing CA - G2"
|
||||
WriteRegStr HKLM "${FallbackKey}\1" "name" "Mozilla Fake SPC"
|
||||
WriteRegStr HKLM "${FallbackKey}\1" "issuer" "Mozilla Fake CA"
|
||||
${If} ${RunningX64}
|
||||
SetRegView lastused
|
||||
${EndIf}
|
||||
SectionEnd
|
||||
|
||||
; By renaming before deleting we improve things slightly in case
|
||||
; there is a file in use error. In this case a new install can happen.
|
||||
Function un.RenameDelete
|
||||
Pop $9
|
||||
; If the .moz-delete file already exists previously, delete it
|
||||
; If it doesn't exist, the call is ignored.
|
||||
; We don't need to pass /REBOOTOK here since it was already marked that way
|
||||
; if it exists.
|
||||
Delete "$9.moz-delete"
|
||||
Rename "$9" "$9.moz-delete"
|
||||
${If} ${Errors}
|
||||
Delete /REBOOTOK "$9"
|
||||
${Else}
|
||||
Delete /REBOOTOK "$9.moz-delete"
|
||||
${EndIf}
|
||||
ClearErrors
|
||||
FunctionEnd
|
||||
|
||||
Section "Uninstall"
|
||||
; Delete the service so that no updates will be attempted
|
||||
nsExec::Exec '"$INSTDIR\maintenanceservice.exe" uninstall'
|
||||
|
||||
Push "$INSTDIR\updater.ini"
|
||||
Call un.RenameDelete
|
||||
Push "$INSTDIR\maintenanceservice.exe"
|
||||
Call un.RenameDelete
|
||||
Push "$INSTDIR\maintenanceservice_tmp.exe"
|
||||
Call un.RenameDelete
|
||||
Push "$INSTDIR\maintenanceservice.old"
|
||||
Call un.RenameDelete
|
||||
Push "$INSTDIR\Uninstall.exe"
|
||||
Call un.RenameDelete
|
||||
RMDir /REBOOTOK "$INSTDIR"
|
||||
|
||||
DeleteRegKey HKLM "${MaintUninstallKey}"
|
||||
|
||||
${If} ${RunningX64}
|
||||
SetRegView 64
|
||||
${EndIf}
|
||||
DeleteRegValue HKLM "Software\Mozilla\MaintenanceService" "Installed"
|
||||
DeleteRegKey HKLM "${FallbackKey}\"
|
||||
${If} ${RunningX64}
|
||||
SetRegView lastused
|
||||
${EndIf}
|
||||
SectionEnd
|
||||
|
Загрузка…
Ссылка в новой задаче