diff --git a/content/html/content/public/nsHTMLMediaElement.h b/content/html/content/public/nsHTMLMediaElement.h
index 1b2694ac9af..45d31997e0b 100644
--- a/content/html/content/public/nsHTMLMediaElement.h
+++ b/content/html/content/public/nsHTMLMediaElement.h
@@ -50,6 +50,7 @@
#include "nsIObserver.h"
#include "ImageLayers.h"
#include "nsAudioStream.h"
+#include "nsTimeRanges.h"
// Define to output information on decoding and painting framerate
/* #define DEBUG_FRAME_RATE 1 */
@@ -624,6 +625,12 @@ protected:
// a media and element same-origin check.
PRBool mAllowAudioData;
+ // Range of time played.
+ nsTimeRanges mPlayed;
+
+ // Temporary variable for storing a time, when the stream starts to play
+ double mCurrentPlayRangeStart;
+
// If true then we have begun downloading the media content.
// Set to false when completed, or not yet started.
PRPackedBool mBegun;
diff --git a/content/html/content/src/nsHTMLMediaElement.cpp b/content/html/content/src/nsHTMLMediaElement.cpp
index 7e57f3f8e1e..3ca71f0a0ed 100644
--- a/content/html/content/src/nsHTMLMediaElement.cpp
+++ b/content/html/content/src/nsHTMLMediaElement.cpp
@@ -39,7 +39,6 @@
#include "nsIDOMHTMLMediaElement.h"
#include "nsIDOMHTMLSourceElement.h"
#include "nsHTMLMediaElement.h"
-#include "nsTimeRanges.h"
#include "nsGenericHTMLElement.h"
#include "nsPresContext.h"
#include "nsIPresShell.h"
@@ -1092,6 +1091,16 @@ NS_IMETHODIMP nsHTMLMediaElement::SetCurrentTime(double aCurrentTime)
{
StopSuspendingAfterFirstFrame();
+ if (mCurrentPlayRangeStart != -1) {
+ double oldCurrentTime = 0;
+ GetCurrentTime(&oldCurrentTime);
+ LOG(PR_LOG_DEBUG, ("Adding a range: [%f, %f]", mCurrentPlayRangeStart, oldCurrentTime));
+ // Multiple seek without playing
+ if (mCurrentPlayRangeStart != oldCurrentTime) {
+ mPlayed.Add(mCurrentPlayRangeStart, oldCurrentTime);
+ }
+ }
+
if (!mDecoder) {
LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) failed: no decoder", this, aCurrentTime));
return NS_ERROR_DOM_INVALID_STATE_ERR;
@@ -1121,6 +1130,9 @@ NS_IMETHODIMP nsHTMLMediaElement::SetCurrentTime(double aCurrentTime)
LOG(PR_LOG_DEBUG, ("%p SetCurrentTime(%f) starting seek", this, aCurrentTime));
nsresult rv = mDecoder->Seek(clampedTime);
+ // Start a new range at position we seeked to
+ mCurrentPlayRangeStart = clampedTime;
+
// We changed whether we're seeking so we need to AddRemoveSelfReference
AddRemoveSelfReference();
@@ -1142,6 +1154,35 @@ NS_IMETHODIMP nsHTMLMediaElement::GetPaused(PRBool *aPaused)
return NS_OK;
}
+/* readonly attribute nsIDOMHTMLTimeRanges played; */
+NS_IMETHODIMP nsHTMLMediaElement::GetPlayed(nsIDOMTimeRanges** aPlayed)
+{
+ nsRefPtr ranges = new nsTimeRanges();
+
+ PRUint32 timeRangeCount = 0;
+ mPlayed.GetLength(&timeRangeCount);
+ for (PRUint32 i = 0; i < timeRangeCount; i++) {
+ double begin;
+ double end;
+ mPlayed.Start(i, &begin);
+ mPlayed.End(i, &end);
+ ranges->Add(begin, end);
+ }
+
+ if (mCurrentPlayRangeStart != -1.0) {
+ double now = 0.0;
+ GetCurrentTime(&now);
+ if (mCurrentPlayRangeStart != now) {
+ ranges->Add(mCurrentPlayRangeStart, now);
+ }
+ }
+
+ ranges->Normalize();
+
+ ranges.forget(aPlayed);
+ return NS_OK;
+}
+
/* void pause (); */
NS_IMETHODIMP nsHTMLMediaElement::Pause()
{
@@ -1280,6 +1321,7 @@ nsHTMLMediaElement::nsHTMLMediaElement(already_AddRefed aNodeInfo,
mMediaSize(-1,-1),
mLastCurrentTime(0.0),
mAllowAudioData(PR_FALSE),
+ mCurrentPlayRangeStart(-1.0),
mBegun(PR_FALSE),
mLoadedFirstFrame(PR_FALSE),
mAutoplaying(PR_TRUE),
@@ -1380,6 +1422,10 @@ NS_IMETHODIMP nsHTMLMediaElement::Play()
}
}
+ if (mCurrentPlayRangeStart == -1.0) {
+ GetCurrentTime(&mCurrentPlayRangeStart);
+ }
+
// TODO: If the playback has ended, then the user agent must set
// seek to the effective start.
// TODO: The playback rate must be set to the default playback rate.
@@ -2034,6 +2080,13 @@ void nsHTMLMediaElement::PlaybackEnded()
// We changed the state of IsPlaybackEnded which can affect AddRemoveSelfReference
AddRemoveSelfReference();
+ double end = 0.0;
+ GetCurrentTime(&end);
+ if (mCurrentPlayRangeStart != end) {
+ mPlayed.Add(mCurrentPlayRangeStart, end);
+ }
+ mCurrentPlayRangeStart = -1.0;
+
if (mDecoder && mDecoder->IsInfinite()) {
LOG(PR_LOG_DEBUG, ("%p, got duration by reaching the end of the stream", this));
DispatchAsyncEvent(NS_LITERAL_STRING("durationchange"));
@@ -2455,7 +2508,7 @@ void nsHTMLMediaElement::NotifyAddedSource()
}
// A load was paused in the resource selection algorithm, waiting for
- // a new source child to be added, resume the resource selction algorithm.
+ // a new source child to be added, resume the resource selection algorithm.
if (mLoadWaitStatus == WAITING_FOR_SOURCE) {
QueueLoadFromSourceTask();
}
diff --git a/content/html/content/src/nsTimeRanges.cpp b/content/html/content/src/nsTimeRanges.cpp
index 70c3e1889f1..90367f53a7f 100644
--- a/content/html/content/src/nsTimeRanges.cpp
+++ b/content/html/content/src/nsTimeRanges.cpp
@@ -85,3 +85,28 @@ void
nsTimeRanges::Add(double aStart, double aEnd) {
mRanges.AppendElement(TimeRange(aStart,aEnd));
}
+
+void
+nsTimeRanges::Normalize() {
+ if (mRanges.Length() <= 1) {
+ return;
+ }
+ nsAutoTArray normalized;
+
+ mRanges.Sort(CompareTimeRanges());
+
+ // This merges the intervals
+ TimeRange current(mRanges[0]);
+ for (PRUint32 i = 1; i < mRanges.Length(); i++) {
+ if (current.mEnd >= mRanges[i].mStart) {
+ current.mEnd = NS_MAX(current.mEnd, mRanges[i].mEnd);
+ } else {
+ normalized.AppendElement(current);
+ current = mRanges[i];
+ }
+ }
+
+ normalized.AppendElement(current);
+
+ mRanges = normalized;
+}
diff --git a/content/html/content/src/nsTimeRanges.h b/content/html/content/src/nsTimeRanges.h
index 172cadb5b85..59678a0a4e7 100644
--- a/content/html/content/src/nsTimeRanges.h
+++ b/content/html/content/src/nsTimeRanges.h
@@ -55,6 +55,9 @@ public:
void Add(double aStart, double aEnd);
+ // See .
+ void Normalize();
+
private:
struct TimeRange {
@@ -65,6 +68,21 @@ private:
double mEnd;
};
+ struct CompareTimeRanges
+ {
+ PRBool Equals(const TimeRange& tr1, const TimeRange& tr2) const
+ {
+ return tr1.mStart == tr2.mStart && tr1.mEnd == tr2.mEnd;
+ }
+
+ // Here, we aim at time range normalization. That why we order only by start
+ // time, since the ranges can overlap.
+ PRBool LessThan(const TimeRange& tr1, const TimeRange& tr2) const
+ {
+ return tr1.mStart < tr2.mStart;
+ }
+ };
+
nsAutoTArray mRanges;
};
diff --git a/content/media/test/Makefile.in b/content/media/test/Makefile.in
index a538b7550a0..b26441e1387 100644
--- a/content/media/test/Makefile.in
+++ b/content/media/test/Makefile.in
@@ -126,6 +126,7 @@ _TEST_FILES = \
test_paused_after_ended.html \
test_play_events.html \
test_play_events_2.html \
+ test_played.html \
test_playback.html \
test_playback_errors.html \
test_preload_actions.html \
diff --git a/content/media/test/manifest.js b/content/media/test/manifest.js
index 7ef33b9fa77..763d16a798d 100644
--- a/content/media/test/manifest.js
+++ b/content/media/test/manifest.js
@@ -23,6 +23,14 @@ var gProgressTests = [
{ name:"bogus.duh", type:"bogus/duh" }
];
+// Used by test_played
+var gPlayedTests = [
+ { name:"big.wav", type:"audio/x-wav", duration:9.0, size:102444 },
+ { name:"sound.ogg", type:"audio/ogg", duration:4.0, size:2603 },
+ { name:"seek.ogv", type:"video/ogg", duration:3.966, size:285310 },
+ { name:"seek.webm", type:"video/webm", duration:3.966 }
+];
+
// Used by test_mozLoadFrom. Need one test file per decoder backend, plus
// anything for testing clone-specific bugs.
var gCloneTests = gSmallTests.concat([
diff --git a/content/media/test/test_played.html b/content/media/test/test_played.html
new file mode 100644
index 00000000000..9c298b35ed6
--- /dev/null
+++ b/content/media/test/test_played.html
@@ -0,0 +1,233 @@
+
+
+
+Test played member for media elements
+
+
+
+
+
+
+
+
+
+
+
diff --git a/dom/interfaces/html/nsIDOMHTMLAudioElement.idl b/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
index a0f44339e58..6c4805d081f 100644
--- a/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLAudioElement.idl
@@ -52,7 +52,7 @@
* @status UNDER_DEVELOPMENT
*/
-[scriptable, uuid(f0d4977c-9632-4fab-bc9b-91c250a6ef96)]
+[scriptable, uuid(1f2437f1-6037-40c4-bfb6-105c6c60f0ca)]
interface nsIDOMHTMLAudioElement : nsIDOMHTMLMediaElement
{
// Setup the audio stream for writing
diff --git a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
index dcbcad76c3a..8181aeb768c 100644
--- a/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLMediaElement.idl
@@ -57,7 +57,7 @@
#endif
%}
-[scriptable, uuid(d8213322-46d8-47ca-a15c-2abae47ddfde)]
+[scriptable, uuid(c8a5f714-97de-4e2c-8394-2397870224bb)]
interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
{
// error state
@@ -89,6 +89,7 @@ interface nsIDOMHTMLMediaElement : nsIDOMHTMLElement
attribute double currentTime;
readonly attribute double duration;
readonly attribute boolean paused;
+ readonly attribute nsIDOMTimeRanges played;
readonly attribute boolean ended;
readonly attribute boolean mozAutoplayEnabled;
attribute boolean autoplay;
diff --git a/dom/interfaces/html/nsIDOMHTMLVideoElement.idl b/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
index ac0f5d07e79..d4b0fe60b06 100644
--- a/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
+++ b/dom/interfaces/html/nsIDOMHTMLVideoElement.idl
@@ -48,15 +48,15 @@
* @status UNDER_DEVELOPMENT
*/
-[scriptable, uuid(00c757ec-db7b-477e-95cd-b2a03b0f8634)]
+[scriptable, uuid(169f0ff1-511a-453d-86b6-346c1e936122)]
interface nsIDOMHTMLVideoElement : nsIDOMHTMLMediaElement
{
- attribute long width;
+ attribute long width;
attribute long height;
readonly attribute unsigned long videoWidth;
readonly attribute unsigned long videoHeight;
attribute DOMString poster;
-
+
// A count of the number of video frames that have demuxed from the media
// resource. If we were playing perfectly, we'd be able to paint this many
// frames.
diff --git a/js/src/jit-test/tests/basic/testBug663789-2.js b/js/src/jit-test/tests/basic/testBug663789-2.js
index 46311648842..d7ee3059986 100644
--- a/js/src/jit-test/tests/basic/testBug663789-2.js
+++ b/js/src/jit-test/tests/basic/testBug663789-2.js
@@ -1,11 +1,6 @@
// |jit-test| debug;mjit
-/*
- * NOTE: this evalInFrame is explicitly exposing an optimization artifact that
- * InvokeSessionGuard leaves the callee frame on the stack between invocations.
- * If this ever gets fixed or InvokeSessionGuard gets removed, this test will
- * fail and it can be removed.
- */
-o = { toString:function() { return evalInFrame(1, "arguments; x") } }
+o = { toString:function() { return evalInFrame(1, "x") } }
+var x = 'C';
var s = "aaaaaaaaaa".replace(/a/g, function() { var x = 'B'; return o });
-assertEq(s, "BBBBBBBBBB");
+assertEq(s, "CCCCCCCCCC");
diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
index 5e692174238..dc603bb742c 100644
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -4473,7 +4473,7 @@ CompileUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, JSPrincipals *p
if (script) {
scriptObj = js_NewScriptObject(cx, script);
if (!scriptObj)
- js_DestroyScript(cx, script);
+ js_DestroyScript(cx, script, 3);
}
LAST_FRAME_CHECKS(cx, scriptObj);
return scriptObj;
@@ -4660,7 +4660,7 @@ CompileFileHelper(JSContext *cx, JSObject *obj, JSPrincipals *principals,
JSObject *scriptObj = js_NewScriptObject(cx, script);
if (!scriptObj)
- js_DestroyScript(cx, script);
+ js_DestroyScript(cx, script, 4);
return scriptObj;
}
@@ -4967,7 +4967,7 @@ EvaluateUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj,
bool ok = ExternalExecute(cx, script, *obj, Valueify(rval));
LAST_FRAME_CHECKS(cx, ok);
- js_DestroyScript(cx, script);
+ js_DestroyScript(cx, script, 5);
return ok;
}
diff --git a/js/src/jsdbgapi.cpp b/js/src/jsdbgapi.cpp
index deb983832a7..4d8a1fdddd5 100644
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -1125,7 +1125,7 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fpArg,
bool ok = Execute(cx, script, *scobj, fp->thisValue(), EXECUTE_DEBUG, fp, Valueify(rval));
- js_DestroyScript(cx, script);
+ js_DestroyScript(cx, script, 6);
return ok;
}
diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp
index baac27d2a4b..50495327dab 100644
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1586,8 +1586,14 @@ js_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
fun->u.i.wrapper = JSPackedBool((firstword >> 1) & 1);
}
- if (!js_XDRScript(xdr, &fun->u.i.script))
+ /*
+ * Don't directly store into fun->u.i.script because we want this to happen
+ * at the same time as we set the script's owner.
+ */
+ JSScript *script = fun->u.i.script;
+ if (!js_XDRScript(xdr, &script))
return false;
+ fun->u.i.script = script;
if (xdr->mode == JSXDR_DECODE) {
*objp = FUN_OBJECT(fun);
@@ -2474,9 +2480,10 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
JS_ASSERT(script->compartment != cx->compartment);
JS_OPT_ASSERT(script->ownerObject == fun);
- cfun->u.i.script = js_CloneScript(cx, script);
- if (!cfun->script())
+ JSScript *cscript = js_CloneScript(cx, script);
+ if (!cscript)
return NULL;
+ cfun->u.i.script = cscript;
cfun->script()->setOwnerObject(cfun);
#ifdef CHECK_SCRIPT_OWNER
cfun->script()->owner = NULL;
diff --git a/js/src/jsinterp.cpp b/js/src/jsinterp.cpp
index face6685480..fdef86e682a 100644
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -709,6 +709,9 @@ InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &this
savedCallee_ = args_.calleev() = calleev;
savedThis_ = args_.thisv() = thisv;
+ /* If anyone (through jsdbgapi) finds this frame, make it safe. */
+ MakeRangeGCSafe(args_.argv(), args_.argc());
+
do {
/* Hoist dynamic checks from scripted Invoke. */
if (!calleev.isObject())
diff --git a/js/src/jsparse.cpp b/js/src/jsparse.cpp
index aba95a8d5da..9c87e9e2208 100644
--- a/js/src/jsparse.cpp
+++ b/js/src/jsparse.cpp
@@ -1142,7 +1142,7 @@ Compiler::compileScript(JSContext *cx, JSObject *scopeChain, StackFrame *callerF
late_error:
if (script) {
- js_DestroyScript(cx, script);
+ js_DestroyScript(cx, script, 7);
script = NULL;
}
goto out;
diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp
index 28a6865636b..528f1c754d4 100644
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -754,7 +754,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp)
error:
if (xdr->mode == JSXDR_DECODE) {
- js_DestroyScript(cx, script);
+ js_DestroyScript(cx, script, 1);
*scriptp = NULL;
}
xdr->script = oldscript;
@@ -1270,7 +1270,7 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
return script;
bad:
- js_DestroyScript(cx, script);
+ js_DestroyScript(cx, script, 2);
return NULL;
}
@@ -1345,7 +1345,7 @@ CheckCompartmentScripts(JSCompartment *comp)
} /* namespace js */
static void
-DestroyScript(JSContext *cx, JSScript *script, JSObject *owner)
+DestroyScript(JSContext *cx, JSScript *script, JSObject *owner, uint32 caller)
{
CheckScript(script, NULL);
CheckScriptOwner(script, owner);
@@ -1408,16 +1408,17 @@ DestroyScript(JSContext *cx, JSScript *script, JSObject *owner)
if (script->sourceMap)
cx->free_(script->sourceMap);
- memset(script, JS_FREE_PATTERN, script->totalSize());
+ memset(script, 0xdb, script->totalSize());
+ *(uint32 *)script = caller;
cx->free_(script);
}
void
-js_DestroyScript(JSContext *cx, JSScript *script)
+js_DestroyScript(JSContext *cx, JSScript *script, uint32 caller)
{
JS_ASSERT(!cx->runtime->gcRunning);
js_CallDestroyScriptHook(cx, script);
- DestroyScript(cx, script, JS_NEW_SCRIPT);
+ DestroyScript(cx, script, JS_NEW_SCRIPT, caller);
}
void
@@ -1425,14 +1426,14 @@ js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner)
{
JS_ASSERT(cx->runtime->gcRunning);
js_CallDestroyScriptHook(cx, script);
- DestroyScript(cx, script, owner);
+ DestroyScript(cx, script, owner, 100);
}
void
js_DestroyCachedScript(JSContext *cx, JSScript *script)
{
JS_ASSERT(cx->runtime->gcRunning);
- DestroyScript(cx, script, JS_CACHED_SCRIPT);
+ DestroyScript(cx, script, JS_CACHED_SCRIPT, 101);
}
void
diff --git a/js/src/jsscript.h b/js/src/jsscript.h
index d27b42c46b7..f0cad899a9c 100644
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -733,7 +733,7 @@ js_CallDestroyScriptHook(JSContext *cx, JSScript *script);
* only on the current thread.
*/
extern void
-js_DestroyScript(JSContext *cx, JSScript *script);
+js_DestroyScript(JSContext *cx, JSScript *script, uint32 caller);
extern void
js_DestroyScriptFromGC(JSContext *cx, JSScript *script, JSObject *owner);
diff --git a/js/src/jsutil.cpp b/js/src/jsutil.cpp
index bf1aee66b73..35a4c8463f6 100644
--- a/js/src/jsutil.cpp
+++ b/js/src/jsutil.cpp
@@ -70,19 +70,26 @@ JS_STATIC_ASSERT(sizeof(void *) == sizeof(void (*)()));
static JS_NEVER_INLINE void
CrashInJS()
{
+ /*
+ * We write 123 here so that the machine code for this function is
+ * unique. Otherwise the linker, trying to be smart, might use the
+ * same code for CrashInJS and for some other function. That
+ * messes up the signature in minidumps.
+ */
+
#if defined(WIN32)
/*
* We used to call DebugBreak() on Windows, but amazingly, it causes
* the MSVS 2010 debugger not to be able to recover a call stack.
*/
- *((int *) NULL) = 0;
+ *((int *) NULL) = 123;
exit(3);
#elif defined(__APPLE__)
/*
* On Mac OS X, Breakpad ignores signals. Only real Mach exceptions are
* trapped.
*/
- *((int *) NULL) = 0; /* To continue from here in GDB: "return" then "continue". */
+ *((int *) NULL) = 123; /* To continue from here in GDB: "return" then "continue". */
raise(SIGABRT); /* In case above statement gets nixed by the optimizer. */
#else
raise(SIGABRT); /* To continue from here in GDB: "signal 0". */
diff --git a/js/src/jsxdrapi.cpp b/js/src/jsxdrapi.cpp
index ce38636c1f4..8585ca0fe9c 100644
--- a/js/src/jsxdrapi.cpp
+++ b/js/src/jsxdrapi.cpp
@@ -720,7 +720,7 @@ JS_XDRScriptObject(JSXDRState *xdr, JSObject **scriptObjp)
js_CallNewScriptHook(xdr->cx, script, NULL);
*scriptObjp = js_NewScriptObject(xdr->cx, script);
if (!*scriptObjp) {
- js_DestroyScript(xdr->cx, script);
+ js_DestroyScript(xdr->cx, script, 8);
return false;
}
}
diff --git a/js/src/vm/Stack.cpp b/js/src/vm/Stack.cpp
index def187658b5..24d14ee9922 100644
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -963,6 +963,12 @@ StackIter::settleOnNewState()
continue;
}
+ /* Censor pushed-but-not-active frames from InvokeSessionGuard. */
+ if (containsCall && !calls_->active() && calls_->argv() == fp_->actualArgs()) {
+ popFrame();
+ continue;
+ }
+
/*
* As an optimization, there is no CallArgsList element pushed for
* natives called directly by a script (compiled or interpreted).
diff --git a/js/src/vm/String.cpp b/js/src/vm/String.cpp
index 5eb6da56221..dc34c5060e0 100644
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -116,30 +116,31 @@ JSString::charsHeapSize()
return length() * sizeof(jschar);
}
-static JS_ALWAYS_INLINE size_t
-RopeCapacityFor(size_t length)
+static JS_ALWAYS_INLINE bool
+AllocChars(JSContext *maybecx, size_t length, jschar **chars, size_t *capacity)
{
- static const size_t ROPE_DOUBLING_MAX = 1024 * 1024;
+ /*
+ * String length doesn't include the null char, so include it here before
+ * doubling. Adding the null char after doubling would interact poorly with
+ * round-up malloc schemes.
+ */
+ size_t numChars = length + 1;
/*
* Grow by 12.5% if the buffer is very large. Otherwise, round up to the
* next power of 2. This is similar to what we do with arrays; see
* JSObject::ensureDenseArrayElements.
*/
- if (length > ROPE_DOUBLING_MAX)
- return length + (length / 8);
- return RoundUpPow2(length);
-}
+ static const size_t DOUBLING_MAX = 1024 * 1024;
+ numChars = numChars > DOUBLING_MAX ? numChars + (numChars / 8) : RoundUpPow2(numChars);
+
+ /* Like length, capacity does not include the null char, so take it out. */
+ *capacity = numChars - 1;
-static JS_ALWAYS_INLINE jschar *
-AllocChars(JSContext *maybecx, size_t wholeCapacity)
-{
- /* +1 for the null char at the end. */
JS_STATIC_ASSERT(JSString::MAX_LENGTH * sizeof(jschar) < UINT32_MAX);
- size_t bytes = (wholeCapacity + 1) * sizeof(jschar);
- if (maybecx)
- return (jschar *)maybecx->malloc_(bytes);
- return (jschar *)OffTheBooks::malloc_(bytes);
+ size_t bytes = numChars * sizeof(jschar);
+ *chars = (jschar *)(maybecx ? maybecx->malloc_(bytes) : OffTheBooks::malloc_(bytes));
+ return *chars != NULL;
}
JSFlatString *
@@ -197,9 +198,7 @@ JSRope::flatten(JSContext *maybecx)
}
}
- wholeCapacity = RopeCapacityFor(wholeLength);
- wholeChars = AllocChars(maybecx, wholeCapacity);
- if (!wholeChars)
+ if (!AllocChars(maybecx, wholeLength, &wholeChars, &wholeCapacity))
return NULL;
pos = wholeChars;
diff --git a/js/src/vm/String.h b/js/src/vm/String.h
index c47f89e38c3..2bbffebe908 100644
--- a/js/src/vm/String.h
+++ b/js/src/vm/String.h
@@ -55,8 +55,11 @@ class JSAtom;
/*
* JavaScript strings
*
- * Conceptually, a JS string is just an array of chars and a length. To improve
- * performance of common string operations, the following optimizations are
+ * Conceptually, a JS string is just an array of chars and a length. This array
+ * of chars may or may not be null-terminated and, if it is, the null character
+ * is not included in the length.
+ *
+ * To improve performance of common operations, the following optimizations are
* made which affect the engine's representation of strings:
*
* - The plain vanilla representation is a "flat" string which consists of a
@@ -110,7 +113,7 @@ class JSAtom;
* | \
* | JSDependentString base / -
* |
- * JSFlatString (abstract) chars / not null-terminated
+ * JSFlatString (abstract) chars / null-terminated
* | \
* | JSExtensibleString capacity / no external pointers into char array
* |
diff --git a/modules/libpr0n/src/RasterImage.cpp b/modules/libpr0n/src/RasterImage.cpp
index e86e7fbf935..6500a736a87 100644
--- a/modules/libpr0n/src/RasterImage.cpp
+++ b/modules/libpr0n/src/RasterImage.cpp
@@ -842,8 +842,7 @@ RasterImage::InternalAddFrame(PRUint32 framenum,
if (mFrames.Length() == 1) {
// Since we're about to add our second frame, initialize animation stuff
- if (!ensureAnimExists())
- return NS_ERROR_OUT_OF_MEMORY;
+ EnsureAnimExists();
// If we dispose of the first frame by clearing it, then the
// First Frame's refresh area is all of itself.
@@ -1113,8 +1112,7 @@ RasterImage::StartAnimation()
NS_ABORT_IF_FALSE(ShouldAnimate(), "Should not animate!");
- if (!ensureAnimExists())
- return NS_ERROR_OUT_OF_MEMORY;
+ EnsureAnimExists();
NS_ABORT_IF_FALSE(mAnim && !mAnim->timer, "Anim must exist and not have a timer yet");
diff --git a/modules/libpr0n/src/RasterImage.h b/modules/libpr0n/src/RasterImage.h
index bb2208f49e2..5d1f0009dda 100644
--- a/modules/libpr0n/src/RasterImage.h
+++ b/modules/libpr0n/src/RasterImage.h
@@ -382,7 +382,7 @@ private:
imgFrame* GetCurrentDrawableImgFrame();
PRUint32 GetCurrentImgFrameIndex() const;
- inline Anim* ensureAnimExists()
+ inline void EnsureAnimExists()
{
if (!mAnim) {
@@ -400,7 +400,6 @@ private:
// is acceptable for the moment.
LockImage();
}
- return mAnim;
}
/** Function for doing the frame compositing of animations
diff --git a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h
index 55c4c8a3468..1228b977b33 100644
--- a/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h
+++ b/toolkit/crashreporter/google-breakpad/src/client/mac/handler/minidump_generator.h
@@ -43,7 +43,7 @@
#include "dynamic_images.h"
-#if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_7
+#if MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
#define HAS_PPC_SUPPORT
#endif