author | Ryan VanderMeulen <ryanvm@gmail.com> |
Thu, 14 Aug 2014 22:15:29 -0400 | |
changeset 199669 | c9f8cc9ce89c95a173a8116a934a0689f5452aea |
parent 199668 | 9827d3cf6f69c33dd7321fc9ecb4e8529de7ae8e (current diff) |
parent 199624 | 150d3813133c7b30a5c8cd5f9b4afd630d3d0f36 (diff) |
child 199670 | eac6befe2416927871f6c100ffd9801bd2e06df5 |
child 199739 | 1135f262b3cab90b553c602e7f5be2c772d4d5c3 |
child 199782 | ea86f30734114b5a4f906073ae3463a4c5c801dc |
push id | 47702 |
push user | ryanvm@gmail.com |
push date | Fri, 15 Aug 2014 02:30:59 +0000 |
treeherder | mozilla-inbound@eac6befe2416 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 34.0a1 |
first release with | nightly linux32
c9f8cc9ce89c
/
34.0a1
/
20140815030202
/
files
nightly linux64
c9f8cc9ce89c
/
34.0a1
/
20140815030202
/
files
nightly mac
c9f8cc9ce89c
/
34.0a1
/
20140815030202
/
files
nightly win32
c9f8cc9ce89c
/
34.0a1
/
20140815030202
/
files
nightly win64
c9f8cc9ce89c
/
34.0a1
/
20140815030202
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
34.0a1
/
20140815030202
/
pushlog to previous
nightly linux64
34.0a1
/
20140815030202
/
pushlog to previous
nightly mac
34.0a1
/
20140815030202
/
pushlog to previous
nightly win32
34.0a1
/
20140815030202
/
pushlog to previous
nightly win64
34.0a1
/
20140815030202
/
pushlog to previous
|
intl/uconv/ucvcn/moz.build | file | annotate | diff | comparison | revisions | |
intl/uconv/ucvibm/moz.build | file | annotate | diff | comparison | revisions | |
intl/uconv/ucvja/moz.build | file | annotate | diff | comparison | revisions | |
intl/uconv/ucvko/moz.build | file | annotate | diff | comparison | revisions | |
intl/uconv/ucvlatin/moz.build | file | annotate | diff | comparison | revisions | |
intl/uconv/ucvtw/moz.build | file | annotate | diff | comparison | revisions | |
js/xpconnect/src/nsCxPusher.cpp | file | annotate | diff | comparison | revisions | |
js/xpconnect/src/nsCxPusher.h | file | annotate | diff | comparison | revisions |
--- a/accessible/base/nsAccessibilityService.cpp +++ b/accessible/base/nsAccessibilityService.cpp @@ -1284,17 +1284,17 @@ nsAccessibilityService::CreateAccessible nsGkAtoms::_true, eCaseMatters)) accessible = new XULAlertAccessible(aContent, aDoc); else accessible = new EnumRoleAccessible(aContent, aDoc, roles::PANE); } else if (role.EqualsLiteral("xul:progressmeter")) { accessible = new XULProgressMeterAccessible(aContent, aDoc); - } else if (role.EqualsLiteral("xulstatusbar")) { + } else if (role.EqualsLiteral("xul:statusbar")) { accessible = new XULStatusBarAccessible(aContent, aDoc); } else if (role.EqualsLiteral("xul:scale")) { accessible = new XULSliderAccessible(aContent, aDoc); } else if (role.EqualsLiteral("xul:radiobutton")) { accessible = new XULRadioButtonAccessible(aContent, aDoc);
--- a/accessible/tests/mochitest/role/test_general.xul +++ b/accessible/tests/mochitest/role/test_general.xul @@ -17,16 +17,17 @@ <script type="application/javascript"> <![CDATA[ function doTest() { ok(!isAccessible("statusbarpanel"), "statusbarpanel shouldn't be accessible."); testRole("statusbarpanel-iconic", ROLE_PUSHBUTTON); testRole("statusbarpanel-iconic-text", ROLE_PUSHBUTTON); + testRole("statusbar", ROLE_STATUSBAR); SimpleTest.finish(); } SimpleTest.waitForExplicitFinish(); addA11yLoadEvent(doTest); ]]> </script> @@ -44,12 +45,13 @@ </div> <pre id="test"> </pre> </body> <statusbarpanel id="statusbarpanel"></statusbarpanel> <statusbarpanel id="statusbarpanel-iconic" class="statusbarpanel-iconic"></statusbarpanel> <statusbarpanel id="statusbarpanel-iconic-text" class="statusbarpanel-iconic-text"></statusbarpanel> + <statusbar id="statusbar"></statusbar> </hbox> </window>
--- a/build/automation.py.in +++ b/build/automation.py.in @@ -212,17 +212,16 @@ class Automation(object): preexec_fn=None, close_fds=False, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0): - args = automationutils.wrapCommand(args) _log.info("INFO | automation.py | Launching: %s", subprocess.list2cmdline(args)) subprocess.Popen.__init__(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags) self.log = _log
--- a/build/automationutils.py +++ b/build/automationutils.py @@ -21,17 +21,16 @@ import mozinfo "ZipFileReader", "addCommonOptions", "dumpLeakLog", "isURL", "processLeakLog", "getDebuggerInfo", "DEBUGGER_INFO", "replaceBackSlashes", - "wrapCommand", 'KeyValueParseError', 'parseKeyValue', 'systemMemory', 'environment', 'dumpScreen', "ShutdownLeaks" ] @@ -397,29 +396,16 @@ def processLeakLog(leakLogFile, leakThre m = fileNameRegExp.search(fileName) if m: processType = m.group(1) processSingleLeakFile(thisFile, processType, leakThreshold) def replaceBackSlashes(input): return input.replace('\\', '/') -def wrapCommand(cmd): - """ - If running on OS X 10.5 or older, wrap |cmd| so that it will - be executed as an i386 binary, in case it's a 32-bit/64-bit universal - binary. - """ - if platform.system() == "Darwin" and \ - hasattr(platform, 'mac_ver') and \ - platform.mac_ver()[0][:4] < '10.6': - return ["arch", "-arch", "i386"] + cmd - # otherwise just execute the command normally - return cmd - class KeyValueParseError(Exception): """error when parsing strings of serialized key-values""" def __init__(self, msg, errors=()): self.errors = errors Exception.__init__(self, msg) def parseKeyValue(strings, separator='=', context='key, value: '): """
--- a/content/media/webaudio/AudioContext.cpp +++ b/content/media/webaudio/AudioContext.cpp @@ -535,18 +535,18 @@ AudioContext::DestinationStream() const } return nullptr; } double AudioContext::CurrentTime() const { MediaStream* stream = Destination()->Stream(); - return stream->StreamTimeToSeconds(stream->GetCurrentTime()) + - ExtraCurrentTime(); + return StreamTimeToDOMTime(stream-> + StreamTimeToSeconds(stream->GetCurrentTime())); } void AudioContext::Shutdown() { mIsShutDown = true; // We mute rather than suspending, because the delay between the ::Shutdown
--- a/content/media/webaudio/AudioContext.h +++ b/content/media/webaudio/AudioContext.h @@ -228,16 +228,21 @@ public: void UpdateNodeCount(int32_t aDelta); double DOMTimeToStreamTime(double aTime) const { return aTime - ExtraCurrentTime(); } + double StreamTimeToDOMTime(double aTime) const + { + return aTime + ExtraCurrentTime(); + } + IMPL_EVENT_HANDLER(mozinterruptbegin) IMPL_EVENT_HANDLER(mozinterruptend) private: /** * Returns the amount of extra time added to the current time of the * AudioDestinationNode's MediaStream to get this AudioContext's currentTime. * Must be subtracted from all DOM API parameter times that are on the same
--- a/content/media/webaudio/ScriptProcessorNode.cpp +++ b/content/media/webaudio/ScriptProcessorNode.cpp @@ -353,17 +353,16 @@ private: // we now have a full input buffer ready to be sent to the main thread. TrackTicks playbackTick = mSource->GetCurrentPosition(); // Add the duration of the current sample playbackTick += WEBAUDIO_BLOCK_SIZE; // Add the delay caused by the main thread playbackTick += mSharedBuffers->DelaySoFar(); // Compute the playback time in the coordinate system of the destination - // FIXME: bug 970773 double playbackTime = mSource->DestinationTimeFromTicks(mDestination, playbackTick); class Command : public nsRunnable { public: Command(AudioNodeStream* aStream, InputChannels& aInputChannels, @@ -378,49 +377,40 @@ private: for (uint32_t i = 0; i < mInputChannels.Length(); ++i) { mInputChannels[i] = aInputChannels[i].forget(); } } } NS_IMETHODIMP Run() { - // If it's not safe to run scripts right now, schedule this to run later - if (!nsContentUtils::IsSafeToRunScript()) { - nsContentUtils::AddScriptRunner(this); + nsRefPtr<ScriptProcessorNode> node = static_cast<ScriptProcessorNode*> + (mStream->Engine()->NodeMainThread()); + if (!node) { return NS_OK; } - - nsRefPtr<ScriptProcessorNode> node; - { - // No need to keep holding the lock for the whole duration of this - // function, since we're holding a strong reference to it, so if - // we can obtain the reference, we will hold the node alive in - // this function. - MutexAutoLock lock(mStream->Engine()->NodeMutex()); - node = static_cast<ScriptProcessorNode*>(mStream->Engine()->Node()); - } - if (!node || !node->Context()) { + AudioContext* context = node->Context(); + if (!context) { return NS_OK; } AutoJSAPI jsapi; if (NS_WARN_IF(!jsapi.Init(node->GetOwner()))) { return NS_OK; } JSContext* cx = jsapi.cx(); // Create the input buffer nsRefPtr<AudioBuffer> inputBuffer; if (!mNullInput) { ErrorResult rv; inputBuffer = - AudioBuffer::Create(node->Context(), mInputChannels.Length(), + AudioBuffer::Create(context, mInputChannels.Length(), node->BufferSize(), - node->Context()->SampleRate(), cx, rv); + context->SampleRate(), cx, rv); if (rv.Failed()) { return NS_OK; } // Put the channel data inside it for (uint32_t i = 0; i < mInputChannels.Length(); ++i) { inputBuffer->SetRawChannelContents(i, mInputChannels[i]); } } @@ -428,17 +418,17 @@ private: // Ask content to produce data in the output buffer // Note that we always avoid creating the output buffer here, and we try to // avoid creating the input buffer as well. The AudioProcessingEvent class // knows how to lazily create them if needed once the script tries to access // them. Otherwise, we may be able to get away without creating them! nsRefPtr<AudioProcessingEvent> event = new AudioProcessingEvent(node, nullptr, nullptr); event->InitEvent(inputBuffer, mInputChannels.Length(), - mPlaybackTime); + context->StreamTimeToDOMTime(mPlaybackTime)); node->DispatchTrustedEvent(event); // Steal the output buffers if they have been set. // Don't create a buffer if it hasn't been used to return output; // FinishProducingOutputBuffer() will optimize output = null. // GetThreadSharedChannelsForRate() may also return null after OOM. nsRefPtr<ThreadSharedFloatArrayBufferList> output; if (event->HasOutputBuffer()) {
--- a/content/media/webaudio/test/mochitest.ini +++ b/content/media/webaudio/test/mochitest.ini @@ -112,16 +112,17 @@ skip-if = (toolkit == 'gonk' && !debug) [test_pannerNode_equalPower.html] [test_pannerNodeAbove.html] [test_pannerNodeChannelCount.html] [test_pannerNodeHRTFSymmetry.html] [test_pannerNodeTail.html] [test_periodicWave.html] [test_scriptProcessorNode.html] [test_scriptProcessorNodeChannelCount.html] +[test_scriptProcessorNode_playbackTime1.html] [test_scriptProcessorNodeZeroInputOutput.html] [test_scriptProcessorNodeNotConnected.html] [test_singleSourceDest.html] [test_stereoPanningWithGain.html] [test_waveDecoder.html] [test_waveShaper.html] [test_waveShaperNoCurve.html] [test_waveShaperZeroLengthCurve.html]
--- a/content/media/webaudio/test/test_scriptProcessorNode.html +++ b/content/media/webaudio/test/test_scriptProcessorNode.html @@ -59,81 +59,68 @@ addLoadEvent(function() { } return buffer.length; } var sp = context.createScriptProcessor(2048); sourceSP.connect(sp); sp.connect(context.destination); var lastPlaybackTime = 0; - sp.onaudioprocess = function(e) { - isnot(buffer, null, "The audioprocess handler for sourceSP must be run at this point"); + + var emptyBuffer = context.createBuffer(1, 2048, context.sampleRate); + + function checkAudioProcessingEvent(e) { is(e.target, sp, "Correct event target"); ok(e.playbackTime > lastPlaybackTime, "playbackTime correctly set"); lastPlaybackTime = e.playbackTime; is(e.inputBuffer.numberOfChannels, 2, "Correct number of channels for the input buffer"); is(e.inputBuffer.length, 2048, "Correct length for the input buffer"); is(e.inputBuffer.sampleRate, context.sampleRate, "Correct sample rate for the input buffer"); is(e.outputBuffer.numberOfChannels, 2, "Correct number of channels for the output buffer"); is(e.outputBuffer.length, 2048, "Correct length for the output buffer"); is(e.outputBuffer.sampleRate, context.sampleRate, "Correct sample rate for the output buffer"); + compareChannels(e.outputBuffer.getChannelData(0), emptyBuffer.getChannelData(0)); + compareChannels(e.outputBuffer.getChannelData(1), emptyBuffer.getChannelData(0)); + } + + sp.onaudioprocess = function(e) { + isnot(buffer, null, "The audioprocess handler for sourceSP must be run at this point"); + checkAudioProcessingEvent(e); + // Because of the initial latency added by the second script processor node, // we will never see any generated audio frames in the first callback. - var emptyBuffer = context.createBuffer(1, 2048, context.sampleRate); compareChannels(e.inputBuffer.getChannelData(0), emptyBuffer.getChannelData(0)); compareChannels(e.inputBuffer.getChannelData(1), emptyBuffer.getChannelData(0)); - compareChannels(e.outputBuffer.getChannelData(0), emptyBuffer.getChannelData(0)); - compareChannels(e.outputBuffer.getChannelData(1), emptyBuffer.getChannelData(0)); sp.onaudioprocess = function(e) { - is(e.target, sp, "Correct event target"); - ok(e.playbackTime > lastPlaybackTime, "playbackTime correctly set"); - lastPlaybackTime = e.playbackTime; - is(e.inputBuffer.numberOfChannels, 2, "Correct number of channels for the input buffer"); - is(e.inputBuffer.length, 2048, "Correct length for the input buffer"); - is(e.inputBuffer.sampleRate, context.sampleRate, "Correct sample rate for the input buffer"); - is(e.outputBuffer.numberOfChannels, 2, "Correct number of channels for the output buffer"); - is(e.outputBuffer.length, 2048, "Correct length for the output buffer"); - is(e.outputBuffer.sampleRate, context.sampleRate, "Correct sample rate for the output buffer"); + checkAudioProcessingEvent(e); var firstNonZero = findFirstNonZeroSample(e.inputBuffer); ok(firstNonZero <= 2048, "First non-zero sample within range"); compareChannels(e.inputBuffer.getChannelData(0), emptyBuffer.getChannelData(0), firstNonZero); compareChannels(e.inputBuffer.getChannelData(1), emptyBuffer.getChannelData(0), firstNonZero); compareChannels(e.inputBuffer.getChannelData(0), buffer.getChannelData(0), 2048 - firstNonZero, firstNonZero, 0); compareChannels(e.inputBuffer.getChannelData(1), buffer.getChannelData(1), 2048 - firstNonZero, firstNonZero, 0); - compareChannels(e.outputBuffer.getChannelData(0), emptyBuffer.getChannelData(0)); - compareChannels(e.outputBuffer.getChannelData(1), emptyBuffer.getChannelData(0)); if (firstNonZero == 0) { // If we did not experience any delays, the test is done! sp.onaudioprocess = null; SimpleTest.finish(); } else if (firstNonZero != 2048) { // In case we just saw a zero buffer this time, wait one more round sp.onaudioprocess = function(e) { - is(e.target, sp, "Correct event target"); - ok(e.playbackTime > lastPlaybackTime, "playbackTime correctly set"); - lastPlaybackTime = e.playbackTime; - is(e.inputBuffer.numberOfChannels, 2, "Correct number of channels for the input buffer"); - is(e.inputBuffer.length, 2048, "Correct length for the input buffer"); - is(e.inputBuffer.sampleRate, context.sampleRate, "Correct sample rate for the input buffer"); - is(e.outputBuffer.numberOfChannels, 2, "Correct number of channels for the output buffer"); - is(e.outputBuffer.length, 2048, "Correct length for the output buffer"); - is(e.outputBuffer.sampleRate, context.sampleRate, "Correct sample rate for the output buffer"); + checkAudioProcessingEvent(e); compareChannels(e.inputBuffer.getChannelData(0), buffer.getChannelData(0), firstNonZero, 0, 2048 - firstNonZero); compareChannels(e.inputBuffer.getChannelData(1), buffer.getChannelData(1), firstNonZero, 0, 2048 - firstNonZero); compareChannels(e.inputBuffer.getChannelData(0), emptyBuffer.getChannelData(0), undefined, firstNonZero); compareChannels(e.inputBuffer.getChannelData(1), emptyBuffer.getChannelData(0), undefined, firstNonZero); - compareChannels(e.outputBuffer.getChannelData(0), emptyBuffer.getChannelData(0)); - compareChannels(e.outputBuffer.getChannelData(1), emptyBuffer.getChannelData(0)); sp.onaudioprocess = null; SimpleTest.finish(); }; } }; };
new file mode 100644 --- /dev/null +++ b/content/media/webaudio/test/test_scriptProcessorNode_playbackTime1.html @@ -0,0 +1,41 @@ +<!DOCTYPE HTML> +<html> +<head> + <title>Test ScriptProcessorNode playbackTime for bug 970773</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> + +SimpleTest.waitForExplicitFinish(); +addLoadEvent( + function() { + const delay = 0.1; + var context = new AudioContext(); + SimpleTest.executeSoon( // to ensure that AudioContext has started + function() { + setTimeout( // wait for |delay| + function() { + var sp = context.createScriptProcessor(256); + sp.connect(context.destination); + sp.onaudioprocess = + function(e) { + var minimum = + (delay + e.inputBuffer.length/context.sampleRate) * + (1.0 - 1.0/Math.pow(2.0,52.0)); // double precision + ok(e.playbackTime >= minimum, + "playbackTime " + e.playbackTime + + " beyond expected minimum " + minimum); + sp.onaudioprocess = null; + SimpleTest.finish(); + }; + }, 1000 * delay); + }); + }); + +</script> +</pre> +</body> +</html>
--- a/dom/base/ScriptSettings.h +++ b/dom/base/ScriptSettings.h @@ -66,22 +66,23 @@ inline JSObject& IncumbentJSGlobal() return *GetIncumbentGlobal()->GetGlobalJSObject(); } class ScriptSettingsStack; class ScriptSettingsStackEntry { friend class ScriptSettingsStack; public: - ScriptSettingsStackEntry(nsIGlobalObject *aGlobal, bool aCandidate); ~ScriptSettingsStackEntry(); bool NoJSAPI() { return !mGlobalObject; } protected: + ScriptSettingsStackEntry(nsIGlobalObject *aGlobal, bool aCandidate); + nsCOMPtr<nsIGlobalObject> mGlobalObject; bool mIsCandidateEntryPoint; private: // This constructor is only for use by AutoNoJSAPI. friend class AutoNoJSAPI; ScriptSettingsStackEntry();
--- a/intl/uconv/moz.build +++ b/intl/uconv/moz.build @@ -1,23 +1,14 @@ # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- # vim: set filetype=python: # 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/. -DIRS += [ - 'ucvja', - 'ucvcn', - 'ucvlatin', - 'ucvtw', - 'ucvko', - 'ucvibm', -] - TEST_DIRS += ['tests'] XPIDL_SOURCES += [ 'nsICurrentCharsetListener.idl', 'nsIScriptableUConv.idl', 'nsITextToSubURI.idl', 'nsIUTF8ConverterService.idl', ] @@ -26,16 +17,23 @@ XPIDL_MODULE = 'uconv' EXPORTS += [ 'nsEncoderDecoderUtils.h', 'nsIUnicodeDecoder.h', 'nsIUnicodeEncoder.h', 'nsUConvCID.h', 'nsUCSupport.h', 'uconvutil.h', + 'ucvcn/nsUCvCnCID.h', + 'ucvibm/nsUCvIBMCID.h', + 'ucvja/nsUCVJA2CID.h', + 'ucvja/nsUCVJACID.h', + 'ucvko/nsUCvKOCID.h', + 'ucvlatin/nsUCvLatinCID.h', + 'ucvtw/nsUCvTWCID.h', ] UNIFIED_SOURCES += [ 'nsConverterInputStream.cpp', 'nsConverterOutputStream.cpp', 'nsCP1252ToUnicode.cpp', 'nsISO88591ToUnicode.cpp', 'nsMacRomanToUnicode.cpp',
deleted file mode 100644 --- a/intl/uconv/ucvcn/moz.build +++ /dev/null @@ -1,9 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXPORTS += [ - 'nsUCvCnCID.h', -]
deleted file mode 100644 --- a/intl/uconv/ucvibm/moz.build +++ /dev/null @@ -1,9 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXPORTS += [ - 'nsUCvIBMCID.h', -]
deleted file mode 100644 --- a/intl/uconv/ucvja/moz.build +++ /dev/null @@ -1,10 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXPORTS += [ - 'nsUCVJA2CID.h', - 'nsUCVJACID.h', -]
deleted file mode 100644 --- a/intl/uconv/ucvko/moz.build +++ /dev/null @@ -1,9 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXPORTS += [ - 'nsUCvKOCID.h', -]
deleted file mode 100644 --- a/intl/uconv/ucvlatin/moz.build +++ /dev/null @@ -1,9 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXPORTS += [ - 'nsUCvLatinCID.h', -]
deleted file mode 100644 --- a/intl/uconv/ucvtw/moz.build +++ /dev/null @@ -1,9 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -EXPORTS += [ - 'nsUCvTWCID.h', -]
new file mode 100644 --- /dev/null +++ b/js/public/DebugAPI.h @@ -0,0 +1,256 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- + * vim: set ts=8 sts=4 et sw=4 tw=99: + * 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/. */ + +// Interfaces by which the embedding can interact with the Debugger API. + +#ifndef js_DebugAPI_h +#define js_DebugAPI_h + +#include "mozilla/Assertions.h" +#include "mozilla/Attributes.h" +#include "mozilla/Move.h" + +#include "jspubtd.h" + +#include "js/RootingAPI.h" +#include "js/TypeDecls.h" + +namespace js { +class Debugger; +} + +namespace JS { +namespace dbg { + +// Helping embedding code build objects for Debugger +// ------------------------------------------------- +// +// Some Debugger API features lean on the embedding application to construct +// their result values. For example, Debugger.Frame.prototype.scriptEntryReason +// calls hooks provided by the embedding to construct values explaining why it +// invoked JavaScript; if F is a frame called from a mouse click event handler, +// F.scriptEntryReason would return an object of the form: +// +// { eventType: "mousedown", event: <object> } +// +// where <object> is a Debugger.Object whose referent is the event being +// dispatched. +// +// However, Debugger implements a trust boundary. Debuggee code may be +// considered untrusted; debugger code needs to be protected from debuggee +// getters, setters, proxies, Object.watch watchpoints, and any other feature +// that might accidentally cause debugger code to set the debuggee running. The +// Debugger API tries to make it easy to write safe debugger code by only +// offering access to debuggee objects via Debugger.Object instances, which +// ensure that only those operations whose explicit purpose is to invoke +// debuggee code do so. But this protective membrane is only helpful if we +// interpose Debugger.Object instances in all the necessary spots. +// +// SpiderMonkey's compartment system also implements a trust boundary. The +// debuggee and debugger are always in different compartments. Inter-compartment +// work requires carefully tracking which compartment each JSObject or JS::Value +// belongs to, and ensuring that is is correctly wrapped for each operation. +// +// It seems precarious to expect the embedding's hooks to implement these trust +// boundaries. Instead, the JS::dbg::Builder API segregates the code which +// constructs trusted objects from that which deals with untrusted objects. +// Trusted objects have an entirely different C++ type, so code that improperly +// mixes trusted and untrusted objects is caught at compile time. +// +// In the structure shown above, there are two trusted objects, and one +// untrusted object: +// +// - The overall object, with the 'eventType' and 'event' properties, is a +// trusted object. We're going to return it to D.F.p.scriptEntryReason's +// caller, which will handle it directly. +// +// - The Debugger.Object instance appearing as the value of the 'event' property +// is a trusted object. It belongs to the same Debugger instance as the +// Debugger.Frame instance whose scriptEntryReason accessor was called, and +// presents a safe reflection-oriented API for inspecting its referent, which +// is: +// +// - The actual event object, an untrusted object, and the referent of the +// Debugger.Object above. (Content can do things like replacing accessors on +// Event.prototype.) +// +// Using JS::dbg::Builder, all objects and values the embedding deals with +// directly are considered untrusted, and are assumed to be debuggee values. The +// only way to construct trusted objects is to use Builder's own methods, which +// return a separate Object type. The only way to set a property on a trusted +// object is through that Object type. The actual trusted object is never +// exposed to the embedding. +// +// So, for example, the embedding might use code like the following to construct +// the object shown above, given a Builder passed to it by Debugger: +// +// bool +// MyScriptEntryReason::explain(JSContext *cx, +// Builder &builder, +// Builder::Object &result) +// { +// JSObject *eventObject = ... obtain debuggee event object somehow ...; +// if (!eventObject) +// return false; +// result = builder.newObject(cx); +// return result && +// result.defineProperty(cx, "eventType", SafelyFetchType(eventObject)) && +// result.defineProperty(cx, "event", eventObject); +// } +// +// +// Object::defineProperty also accepts an Object as the value to store on the +// property. By its type, we know that the value is trusted, so we set it +// directly as the property's value, without interposing a Debugger.Object +// wrapper. This allows the embedding to builted nested structures of trusted +// objects. +// +// The Builder and Builder::Object methods take care of doing whatever +// compartment switching and wrapping are necessary to construct the trusted +// values in the Debugger's compartment. +// +// The Object type is self-rooting. Construction, assignment, and destruction +// all properly root the referent object. + +class BuilderOrigin; + +class Builder { + // The Debugger instance whose client we are building a value for. We build + // objects in this object's compartment. + PersistentRootedObject debuggerObject; + + // debuggerObject's Debugger structure, for convenience. + js::Debugger *debugger; + + // Check that |thing| is in the same compartment as our debuggerObject. Used + // for assertions when constructing BuiltThings. We can overload this as we + // add more instantiations of BuiltThing. +#if DEBUG + void assertBuilt(JSObject *obj); +#else + void assertBuilt(JSObject *obj) { } +#endif + + protected: + // A reference to a trusted object or value. At the moment, we only use it + // with JSObject *. + template<typename T> + class BuiltThing { + friend class BuilderOrigin; + + void nonNull() {} + + protected: + // The Builder to which this trusted thing belongs. + Builder &owner; + + // A rooted reference to our value. + PersistentRooted<T> value; + + BuiltThing(JSContext *cx, Builder &owner_, T value_ = js::GCMethods<T>::initial()) + : owner(owner_), value(cx, value_) + { + owner.assertBuilt(value_); + } + + // Forward some things from our owner, for convenience. + js::Debugger *debugger() const { return owner.debugger; } + JSObject *debuggerObject() const { return owner.debuggerObject; } + + public: + BuiltThing(const BuiltThing &rhs) : owner(rhs.owner), value(rhs.value) { } + BuiltThing &operator=(const BuiltThing &rhs) { + MOZ_ASSERT(&owner == &rhs.owner); + owner.assertBuilt(rhs.value); + value = rhs.value; + return *this; + } + + typedef void (BuiltThing::* ConvertibleToBool)(); + operator ConvertibleToBool() const { + // If we ever instantiate BuiltThink<Value>, this might not suffice. + return value ? &BuiltThing::nonNull : 0; + } + + private: + BuiltThing() MOZ_DELETE; + }; + + public: + // A reference to a trusted object, possibly null. Instances of Object are + // always properly rooted. They can be copied and assigned, as if they were + // pointers. + class Object: private BuiltThing<JSObject *> { + friend class Builder; // for construction + friend class BuilderOrigin; // for unwrapping + + typedef BuiltThing<JSObject *> Base; + + // This is private, because only Builders can create Objects that + // actually point to something (hence the 'friend' declaration). + Object(JSContext *cx, Builder &owner_, HandleObject obj) : Base(cx, owner_, obj.get()) { } + + bool definePropertyToTrusted(JSContext *cx, const char *name, + JS::MutableHandleValue value); + + public: + Object(JSContext *cx, Builder &owner_) : Base(cx, owner_, nullptr) { } + Object(const Object &rhs) : Base(rhs) { } + + // Our automatically-generated assignment operator can see our base + // class's assignment operator, so we don't need to write one out here. + + // Set the property named |name| on this object to |value|. + // + // If |value| is a string or primitive, re-wrap it for the debugger's + // compartment. + // + // If |value| is an object, assume it is a debuggee object and make a + // Debugger.Object instance referring to it. Set that as the propery's + // value. + // + // If |value| is another trusted object, store it directly as the + // property's value. + // + // On error, report the problem on cx and return false. + bool defineProperty(JSContext *cx, const char *name, JS::HandleValue value); + bool defineProperty(JSContext *cx, const char *name, JS::HandleObject value); + bool defineProperty(JSContext *cx, const char *name, Object &value); + + using Base::ConvertibleToBool; + using Base::operator ConvertibleToBool; + }; + + // Build an empty object for direct use by debugger code, owned by this + // Builder. If an error occurs, report it on cx and return a false Object. + Object newObject(JSContext *cx); + + protected: + Builder(JSContext *cx, js::Debugger *debugger); +}; + +// Debugger itself instantiates this subclass of Builder, which can unwrap +// BuiltThings that belong to it. +class BuilderOrigin : public Builder { + template<typename T> + T unwrapAny(const BuiltThing<T> &thing) { + MOZ_ASSERT(&thing.owner == this); + return thing.value.get(); + } + + public: + BuilderOrigin(JSContext *cx, js::Debugger *debugger_) + : Builder(cx, debugger_) + { } + + JSObject *unwrap(Object &object) { return unwrapAny(object); } +}; + +} // namespace dbg +} // namespace JS + + +#endif /* js_DebugAPI_h */
--- a/js/public/UbiNode.h +++ b/js/public/UbiNode.h @@ -193,17 +193,20 @@ class Base { // Return the size of this node, in bytes. Include any structures that this // node owns exclusively that are not exposed as their own ubi::Nodes. virtual size_t size() const = 0; // Return an EdgeRange that initially contains all the referent's outgoing // edges. The EdgeRange should be freed with 'js_delete'. (You could use // ScopedDJSeletePtr<EdgeRange> to manage it.) On OOM, report an exception // on |cx| and return nullptr. - virtual EdgeRange *edges(JSContext *cx) const = 0; + // + // If wantNames is true, compute names for edges. Doing so can be expensive + // in time and memory. + virtual EdgeRange *edges(JSContext *cx, bool wantNames) const = 0; // Return the Zone to which this node's referent belongs, or nullptr if the // referent is not of a type allocated in SpiderMonkey Zones. virtual JS::Zone *zone() const = 0; // Return the compartment for this node. Some ubi::Node referents are not // associated with JSCompartments, such as JSStrings (which are associated // with Zones). When the referent is not associated with a compartment, @@ -328,19 +331,21 @@ class Node { // If this node refers to something that can be represented as a JavaScript // value that is safe to expose to JavaScript code, return that value. // Otherwise return UndefinedValue(). JSStrings, JS::Symbols, and some (but // not all!) JSObjects can be exposed. JS::Value exposeToJS() const; const jschar *typeName() const { return base()->typeName(); } size_t size() const { return base()->size(); } - EdgeRange *edges(JSContext *cx) const { return base()->edges(cx); } JS::Zone *zone() const { return base()->zone(); } JSCompartment *compartment() const { return base()->compartment(); } + EdgeRange *edges(JSContext *cx, bool wantNames = true) const { + return base()->edges(cx, wantNames); + } // A hash policy for ubi::Nodes. // This simply uses the stock PointerHasher on the ubi::Node's pointer. // We specialize DefaultHasher below to make this the default. class HashPolicy { typedef js::PointerHasher<void *, mozilla::tl::FloorLog2<sizeof(void *)>::value> PtrHash; public: @@ -360,17 +365,18 @@ class Node { // Each Edge class should inherit from this base class, overriding as // appropriate. class Edge { protected: Edge() : name(nullptr), referent() { } virtual ~Edge() { } public: - // This edge's name. + // This edge's name. This may be nullptr, if Node::edges was called with + // false as the wantNames parameter. // // The storage is owned by this Edge, and will be freed when this Edge is // destructed. // // (In real life we'll want a better representation for names, to avoid // creating tons of strings when the names follow a pattern; and we'll need // to think about lifetimes carefully to ensure traversal stays cheap.) const jschar *name; @@ -423,17 +429,17 @@ class EdgeRange { // Concrete classes for ubi::Node referent types. // A reusable ubi::Concrete specialization base class for types supported by // JS_TraceChildren. template<typename Referent> class TracerConcrete : public Base { const jschar *typeName() const MOZ_OVERRIDE { return concreteTypeName; } size_t size() const MOZ_OVERRIDE { return 0; } // not implemented yet; bug 1011300 - EdgeRange *edges(JSContext *) const MOZ_OVERRIDE; + EdgeRange *edges(JSContext *, bool wantNames) const MOZ_OVERRIDE; JS::Zone *zone() const MOZ_OVERRIDE { return get().zone(); } JSCompartment *compartment() const MOZ_OVERRIDE { return nullptr; } protected: explicit TracerConcrete(Referent *ptr) : Base(ptr) { } Referent &get() const { return *static_cast<Referent *>(ptr); } public: @@ -467,17 +473,17 @@ template<> struct Concrete<js::Shape> : template<> struct Concrete<js::BaseShape> : TracerConcreteWithCompartment<js::BaseShape> { }; template<> struct Concrete<js::types::TypeObject> : TracerConcrete<js::types::TypeObject> { }; // The ubi::Node null pointer. Any attempt to operate on a null ubi::Node asserts. template<> class Concrete<void> : public Base { const jschar *typeName() const MOZ_OVERRIDE; size_t size() const MOZ_OVERRIDE; - EdgeRange *edges(JSContext *cx) const MOZ_OVERRIDE; + EdgeRange *edges(JSContext *cx, bool wantNames) const MOZ_OVERRIDE; JS::Zone *zone() const MOZ_OVERRIDE; JSCompartment *compartment() const MOZ_OVERRIDE; explicit Concrete(void *ptr) : Base(ptr) { } public: static void construct(void *storage, void *ptr) { new (storage) Concrete(ptr); } static const jschar concreteTypeName[];
--- a/js/public/UbiNodeTraverse.h +++ b/js/public/UbiNodeTraverse.h @@ -79,27 +79,31 @@ struct BreadthFirst { // Construct a breadth-first traversal object that reports the nodes it // reaches to |handler|. The traversal object reports OOM on |cx|, and // asserts that no GC happens in |cx|'s runtime during its lifetime. // // We do nothing with noGC, other than require it to exist, with a lifetime // that encloses our own. BreadthFirst(JSContext *cx, Handler &handler, const JS::AutoCheckCannotGC &noGC) - : cx(cx), visited(cx), handler(handler), pending(cx), + : wantNames(true), cx(cx), visited(cx), handler(handler), pending(cx), traversalBegun(false), stopRequested(false), abandonRequested(false) { } // Initialize this traversal object. Return false on OOM. bool init() { return visited.init(); } // Add |node| as a starting point for the traversal. You may add // as many starting points as you like. Return false on OOM. bool addStart(Node node) { return pending.append(node); } + // True if the handler wants us to compute edge names; doing so can be + // expensive in time and memory. True by default. + bool wantNames; + // Traverse the graph in breadth-first order, starting at the given // start nodes, applying |handler::operator()| for each edge traversed // as described above. // // This should be called only once per instance of this class. // // Return false on OOM or error return from |handler::operator()|. bool traverse() @@ -108,17 +112,17 @@ struct BreadthFirst { traversalBegun = true; // While there are pending nodes, visit them, until we've found a path to the target. while (!pending.empty()) { Node origin = pending.front(); pending.popFront(); // Get a range containing all origin's outgoing edges. - js::ScopedJSDeletePtr<EdgeRange> range(origin.edges(cx)); + js::ScopedJSDeletePtr<EdgeRange> range(origin.edges(cx, wantNames)); if (!range) return false; // Traverse each edge. for (; !range->empty(); range->popFront()) { MOZ_ASSERT(!stopRequested); const Edge &edge = range->front();
--- a/js/src/asmjs/AsmJSLink.cpp +++ b/js/src/asmjs/AsmJSLink.cpp @@ -118,19 +118,16 @@ ValidateGlobalVariable(JSContext *cx, co break; } case AsmJSModule::Global::InitImport: { RootedPropertyName field(cx, global.varImportField()); RootedValue v(cx); if (!GetDataProperty(cx, importVal, field, &v)) return false; - if (!v.isPrimitive()) - return LinkFail(cx, "Imported values must be primitives"); - switch (global.varInitCoercion()) { case AsmJS_ToInt32: if (!ToInt32(cx, v, (int32_t *)datum)) return false; break; case AsmJS_ToNumber: if (!ToNumber(cx, v, (double *)datum)) return false; @@ -179,17 +176,16 @@ ValidateArrayView(JSContext *cx, AsmJSMo } static bool ValidateMathBuiltinFunction(JSContext *cx, AsmJSModule::Global &global, HandleValue globalVal) { RootedValue v(cx); if (!GetDataProperty(cx, globalVal, cx->names().Math, &v)) return false; - RootedPropertyName field(cx, global.mathName()); if (!GetDataProperty(cx, v, field, &v)) return false; Native native = nullptr; switch (global.mathBuiltinFunction()) { case AsmJSMathBuiltin_sin: native = math_sin; break; case AsmJSMathBuiltin_cos: native = math_cos; break; @@ -225,17 +221,16 @@ ValidateConstant(JSContext *cx, AsmJSMod if (global.constantKind() == AsmJSModule::Global::MathConstant) { if (!GetDataProperty(cx, v, cx->names().Math, &v)) return false; } if (!GetDataProperty(cx, v, field, &v)) return false; - if (!v.isNumber()) return LinkFail(cx, "math / global constant value needs to be a number"); // NaN != NaN if (IsNaN(global.constantValue())) { if (!IsNaN(v.toNumber())) return LinkFail(cx, "global constant value needs to be NaN"); } else {
--- a/js/src/jit-test/tests/asm.js/testGlobals.js +++ b/js/src/jit-test/tests/asm.js/testGlobals.js @@ -1,10 +1,9 @@ load(libdir + "asm.js"); -load(libdir + "asserts.js"); assertAsmTypeFail(USE_ASM + "var i; function f(){} return f"); assertAsmTypeFail(USE_ASM + "const i; function f(){} return f"); assertEq(asmLink(asmCompile(USE_ASM + "var i=0; function f(){} return f"))(), undefined); assertEq(asmLink(asmCompile(USE_ASM + "const i=0; function f(){} return f"))(), undefined); assertEq(asmLink(asmCompile(USE_ASM + "var i=42; function f(){ return i|0 } return f"))(), 42); assertEq(asmLink(asmCompile(USE_ASM + "const i=42; function f(){ return i|0 } return f"))(), 42); assertEq(asmLink(asmCompile(USE_ASM + "var i=4.2; function f(){ return +i } return f"))(), 4.2); @@ -113,24 +112,16 @@ assertEq(asmLink(asmCompile('global', 'i assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "const i=imp.i|0; function f() { return i|0 } return f")(null, {i:42})), 42); assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=imp.i|0; function f() { return i|0 } return f")(null, {i:1.4})), 1); assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "const i=imp.i|0; function f() { return i|0 } return f")(null, {i:1.4})), 1); assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=+imp.i; function f() { return +i } return f")(null, {i:42})), 42); assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "const i=+imp.i; function f() { return +i } return f")(null, {i:42})), 42); assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "var i=+imp.i; function f() { return +i } return f")(this, {i:1.4})), 1.4); assertEq(asmLink(asmCompile('global', 'imp', USE_ASM + "const i=+imp.i; function f() { return +i } return f")(this, {i:1.4})), 1.4); assertEq(asmLink(asmCompile(USE_ASM + "var g=0; function f() { var i=42; while (1) { break; } g = i; return g|0 } return f"))(), 42); -assertAsmLinkFail(asmCompile('glob','foreign', USE_ASM + 'var i = +foreign.x; function f() {} return f'), null, {x:{valueOf:function() { return 42 }}}); -assertAsmLinkFail(asmCompile('glob','foreign', USE_ASM + 'var i = foreign.x|0; function f() {} return f'), null, {x:{valueOf:function() { return 42 }}}); -assertEq(asmLink(asmCompile('glob','foreign', USE_ASM + 'var i = foreign.x|0; function f() { return i|0} return f'), null, {x:"blah"})(), 0); -assertEq(asmLink(asmCompile('glob','foreign', USE_ASM + 'var i = +foreign.x; function f() { return +i} return f'), null, {x:"blah"})(), NaN); -assertEq(asmLink(asmCompile('glob','foreign', USE_ASM + 'var tof = glob.Math.fround; var i = tof(foreign.x); function f() { return +i} return f'), this, {x:"blah"})(), NaN); -assertThrowsInstanceOf(() => asmCompile('glob','foreign',USE_ASM + 'var i = foreign.x|0; function f() { return i|0} return f')(null, {x:Symbol("blah")}), TypeError); -assertThrowsInstanceOf(() => asmCompile('glob','foreign',USE_ASM + 'var i = +foreign.x; function f() { return +i} return f')(null, {x:Symbol("blah")}), TypeError); -assertThrowsInstanceOf(() => asmCompile('glob','foreign',USE_ASM + 'var tof = glob.Math.fround; var i = tof(foreign.x); function f() { return +i} return f')(this, {x:Symbol("blah")}), TypeError); var f1 = asmCompile('global', 'foreign', 'heap', USE_ASM + 'var i32 = new global.Int32Array(heap); function g() { return i32[4]|0 } return g'); var global = this; var ab = new ArrayBuffer(4096); var p = new Proxy(global, {has:function(name) { f1(global, null, ab); return true}, getOwnPropertyDescriptor:function(name) { return {configurable:true, value:Int32Array}}}); new Int32Array(ab)[4] = 42;
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -681,22 +681,37 @@ class MDefinition : public MNode } uint32_t virtualRegister() const { JS_ASSERT(isLowered()); return virtualRegister_; } public: // Opcode testing and casts. + template<typename MIRType> bool is() const { + return op() == MIRType::classOpcode; + } + template<typename MIRType> MIRType *to() { + JS_ASSERT(is<MIRType>()); + return static_cast<MIRType *>(this); + } + template<typename MIRType> const MIRType *to() const { + JS_ASSERT(is<MIRType>()); + return static_cast<const MIRType *>(this); + } # define OPCODE_CASTS(opcode) \ bool is##opcode() const { \ - return op() == Op_##opcode; \ + return is<M##opcode>(); \ } \ - inline M##opcode *to##opcode(); \ - inline const M##opcode *to##opcode() const; + M##opcode *to##opcode() { \ + return to<M##opcode>(); \ + } \ + const M##opcode *to##opcode() const { \ + return to<M##opcode>(); \ + } MIR_OPCODE_LIST(OPCODE_CASTS) # undef OPCODE_CASTS inline MInstruction *toInstruction(); inline const MInstruction *toInstruction() const; bool isInstruction() const { return !isPhi(); } @@ -827,18 +842,19 @@ class MInstruction return false; } virtual MInstruction *clone(TempAllocator &alloc, const MDefinitionVector &inputs) const { MOZ_CRASH(); } }; #define INSTRUCTION_HEADER(opcode) \ + static const Opcode classOpcode = MDefinition::Op_##opcode; \ Opcode op() const { \ - return MDefinition::Op_##opcode; \ + return classOpcode; \ } \ const char *opName() const { \ return #opcode; \ } \ bool accept(MDefinitionVisitor *visitor) { \ return visitor->visit##opcode(this); \ } @@ -6627,17 +6643,18 @@ class MTypedArrayElements return AliasSet::Load(AliasSet::ObjectFields); } ALLOW_CLONE(MTypedArrayElements) }; // Checks whether a typed object is neutered. class MNeuterCheck - : public MUnaryInstruction + : public MUnaryInstruction, + public SingleObjectPolicy { private: explicit MNeuterCheck(MDefinition *object) : MUnaryInstruction(object) { JS_ASSERT(object->type() == MIRType_Object); setResultType(MIRType_Object); setResultTypeSet(object->resultTypeSet()); @@ -6658,16 +6675,20 @@ class MNeuterCheck bool congruentTo(const MDefinition *ins) const { return congruentIfOperandsEqual(ins); } AliasSet getAliasSet() const { return AliasSet::Load(AliasSet::ObjectFields); } + + TypePolicy *typePolicy() { + return this; + } }; // Load a binary data object's "elements", which is just its opaque // binary data space. Eventually this should probably be // unified with `MTypedArrayElements`. class MTypedObjectElements : public MUnaryInstruction, public SingleObjectPolicy @@ -11242,16 +11263,18 @@ class MAsmJSCall MOZ_FINAL : public MVar return spIncrement_; } bool possiblyCalls() const { return true; } }; +#undef INSTRUCTION_HEADER + void MUse::init(MDefinition *producer, MNode *consumer) { MOZ_ASSERT(!consumer_, "Initializing MUse that already has a consumer"); MOZ_ASSERT(!producer_, "Initializing MUse that already has a producer"); initUnchecked(producer, consumer); } void MUse::initUnchecked(MDefinition *producer, MNode *consumer) @@ -11279,32 +11302,17 @@ void MUse::replaceProducer(MDefinition * void MUse::discardProducer() { MOZ_ASSERT(consumer_, "Clearing MUse without a consumer"); producer_->removeUse(this); producer_ = nullptr; } -#undef INSTRUCTION_HEADER - -// Implement opcode casts now that the compiler can see the inheritance. -#define OPCODE_CASTS(opcode) \ - M##opcode *MDefinition::to##opcode() \ - { \ - JS_ASSERT(is##opcode()); \ - return static_cast<M##opcode *>(this); \ - } \ - const M##opcode *MDefinition::to##opcode() const \ - { \ - JS_ASSERT(is##opcode()); \ - return static_cast<const M##opcode *>(this); \ - } -MIR_OPCODE_LIST(OPCODE_CASTS) -#undef OPCODE_CASTS +// Implement cast functions now that the compiler can see the inheritance. MDefinition *MNode::toDefinition() { JS_ASSERT(isDefinition()); return (MDefinition *)this; } MResumePoint *MNode::toResumePoint()
--- a/js/src/jsapi-tests/testIntTypesABI.cpp +++ b/js/src/jsapi-tests/testIntTypesABI.cpp @@ -14,16 +14,17 @@ #include "jstypes.h" #include "js/Anchor.h" #include "js/CallArgs.h" #include "js/CallNonGenericMethod.h" #include "js/CharacterEncoding.h" #include "js/Class.h" #include "js/Date.h" +#include "js/DebugAPI.h" #include "js/GCAPI.h" #include "js/HashTable.h" #include "js/HeapAPI.h" #include "js/Id.h" /* LegacyIntTypes.h is deliberately exempted from this requirement */ #include "js/MemoryMetrics.h" #include "js/OldDebugAPI.h" #include "js/ProfilingStack.h"
--- a/js/src/moz.build +++ b/js/src/moz.build @@ -66,16 +66,17 @@ EXPORTS += [ # LegacyIntTypes.h below is deliberately exempted from this requirement. EXPORTS.js += [ '../public/Anchor.h', '../public/CallArgs.h', '../public/CallNonGenericMethod.h', '../public/CharacterEncoding.h', '../public/Class.h', '../public/Date.h', + '../public/DebugAPI.h', '../public/GCAPI.h', '../public/HashTable.h', '../public/HeapAPI.h', '../public/Id.h', '../public/LegacyIntTypes.h', '../public/MemoryMetrics.h', '../public/OldDebugAPI.h', '../public/Principals.h',
--- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -13,16 +13,17 @@ #include "jshashutil.h" #include "jsnum.h" #include "jsobj.h" #include "jswrapper.h" #include "frontend/BytecodeCompiler.h" #include "gc/Marking.h" #include "jit/BaselineJIT.h" +#include "js/DebugAPI.h" #include "js/GCAPI.h" #include "js/Vector.h" #include "vm/ArgumentsObject.h" #include "vm/DebuggerMemory.h" #include "vm/SPSProfiler.h" #include "vm/WrapperObject.h" #include "jsgcinlines.h" @@ -30,16 +31,17 @@ #include "jsopcodeinlines.h" #include "jsscriptinlines.h" #include "vm/ObjectImpl-inl.h" #include "vm/Stack-inl.h" using namespace js; +using JS::dbg::Builder; using js::frontend::IsIdentifier; using mozilla::ArrayLength; using mozilla::Maybe; /*** Forward declarations ************************************************************************/ extern const Class DebuggerFrame_class; @@ -6272,16 +6274,91 @@ static const JSFunctionSpec DebuggerEnv_ JS_FN("find", DebuggerEnv_find, 1, 0), JS_FN("getVariable", DebuggerEnv_getVariable, 1, 0), JS_FN("setVariable", DebuggerEnv_setVariable, 2, 0), JS_FS_END }; +/*** JS::dbg::Builder ****************************************************************************/ + +Builder::Builder(JSContext *cx, js::Debugger *debugger) + : debuggerObject(cx, debugger->toJSObject().get()), + debugger(debugger) +{ } + + +#if DEBUG +void +Builder::assertBuilt(JSObject *obj) +{ + // We can't use assertSameCompartment here, because that is always keyed to + // some JSContext's current compartment, whereas BuiltThings can be + // constructed and assigned to without respect to any particular context; + // the only constraint is that they should be in their debugger's compartment. + MOZ_ASSERT_IF(obj, debuggerObject->compartment() == obj->compartment()); +} +#endif + +bool +Builder::Object::definePropertyToTrusted(JSContext *cx, const char *name, + JS::MutableHandleValue trusted) +{ + // We should have checked for false Objects before calling this. + MOZ_ASSERT(value); + + JSAtom *atom = Atomize(cx, name, strlen(name)); + if (!atom) + return false; + RootedId id(cx, AtomToId(atom)); + + return JSObject::defineGeneric(cx, value, id, trusted); +} + +bool +Builder::Object::defineProperty(JSContext *cx, const char *name, JS::HandleValue propval_) +{ + AutoCompartment ac(cx, debuggerObject()); + + RootedValue propval(cx, propval_); + if (!debugger()->wrapDebuggeeValue(cx, &propval)) + return false; + + return definePropertyToTrusted(cx, name, &propval); +} + +bool +Builder::Object::defineProperty(JSContext *cx, const char *name, JS::HandleObject propval_) +{ + RootedValue propval(cx, ObjectOrNullValue(propval_)); + return defineProperty(cx, name, propval); +} + +bool +Builder::Object::defineProperty(JSContext *cx, const char *name, Builder::Object &propval_) +{ + AutoCompartment ac(cx, debuggerObject()); + + RootedValue propval(cx, ObjectOrNullValue(propval_.value)); + return definePropertyToTrusted(cx, name, &propval); +} + +Builder::Object +Builder::newObject(JSContext *cx) +{ + AutoCompartment ac(cx, debuggerObject); + + RootedObject obj(cx, NewBuiltinClassInstance(cx, &JSObject::class_)); + + // If the allocation failed, this will return a false Object, as the spec promises. + return Object(cx, *this, obj); +} + + /*** Glue ****************************************************************************************/ extern JS_PUBLIC_API(bool) JS_DefineDebuggerObject(JSContext *cx, HandleObject obj) { RootedObject objProto(cx), debugCtor(cx),
--- a/js/src/vm/DebuggerMemory.cpp +++ b/js/src/vm/DebuggerMemory.cpp @@ -638,16 +638,17 @@ DebuggerMemory::takeCensus(JSContext *cx return false; { JS::AutoCheckCannotGC noGC; dbg::DefaultCensusTraversal traversal(cx, handler, noGC); if (!traversal.init()) return false; + traversal.wantNames = false; // Walk the debuggee compartments, using it to set the starting points // (the debuggee globals) for the traversal, and to populate // census.debuggeeZones. for (GlobalObjectSet::Range r = debugger->debuggees.all(); !r.empty(); r.popFront()) { if (!census.debuggeeZones.put(r.front()->zone()) || !traversal.addStart(static_cast<JSObject *>(r.front()))) return false;
--- a/js/src/vm/UbiNode.cpp +++ b/js/src/vm/UbiNode.cpp @@ -23,21 +23,21 @@ using JS::Value; using JS::ubi::Concrete; using JS::ubi::Edge; using JS::ubi::EdgeRange; using JS::ubi::Node; using JS::ubi::TracerConcrete; // All operations on null ubi::Nodes crash. -const jschar *Concrete<void>::typeName() const { MOZ_CRASH("null ubi::Node"); } -size_t Concrete<void>::size() const { MOZ_CRASH("null ubi::Node"); } -EdgeRange *Concrete<void>::edges(JSContext *) const { MOZ_CRASH("null ubi::Node"); } -JS::Zone *Concrete<void>::zone() const { MOZ_CRASH("null ubi::Node"); } -JSCompartment *Concrete<void>::compartment() const { MOZ_CRASH("null ubi::Node"); } +const jschar *Concrete<void>::typeName() const { MOZ_CRASH("null ubi::Node"); } +size_t Concrete<void>::size() const { MOZ_CRASH("null ubi::Node"); } +EdgeRange *Concrete<void>::edges(JSContext *, bool) const { MOZ_CRASH("null ubi::Node"); } +JS::Zone *Concrete<void>::zone() const { MOZ_CRASH("null ubi::Node"); } +JSCompartment *Concrete<void>::compartment() const { MOZ_CRASH("null ubi::Node"); } Node::Node(JSGCTraceKind kind, void *ptr) { switch (kind) { case JSTRACE_OBJECT: construct(static_cast<JSObject *>(ptr)); break; case JSTRACE_STRING: construct(static_cast<JSString *>(ptr)); break; case JSTRACE_SYMBOL: construct(static_cast<JS::Symbol *>(ptr)); break; case JSTRACE_SCRIPT: construct(static_cast<JSScript *>(ptr)); break; @@ -127,92 +127,101 @@ typedef mozilla::Vector<SimpleEdge, 8, j // A JSTracer subclass that adds a SimpleEdge to a Vector for each edge on // which it is invoked. class SimpleEdgeVectorTracer : public JSTracer { // The vector to which we add SimpleEdges. SimpleEdgeVector *vec; + // True if we should populate the edge's names. + bool wantNames; + static void staticCallback(JSTracer *trc, void **thingp, JSGCTraceKind kind) { static_cast<SimpleEdgeVectorTracer *>(trc)->callback(thingp, kind); } void callback(void **thingp, JSGCTraceKind kind) { if (!okay) return; - // Ask the tracer to compute an edge name for us. - char buffer[1024]; - const char *name = getTracingEdgeName(buffer, sizeof(buffer)); + jschar *jsname = nullptr; + if (wantNames) { + // Ask the tracer to compute an edge name for us. + char buffer[1024]; + const char *name = getTracingEdgeName(buffer, sizeof(buffer)); - // Convert the name to jschars. - jschar *jsname = js_pod_malloc<jschar>(strlen(name) + 1); - if (!jsname) { - okay = false; - return; + // Convert the name to jschars. + jsname = js_pod_malloc<jschar>(strlen(name) + 1); + if (!jsname) { + okay = false; + return; + } + + size_t i; + for (i = 0; name[i]; i++) + jsname[i] = name[i]; + jsname[i] = '\0'; } - size_t i; - for (i = 0; name[i]; i++) - jsname[i] = name[i]; - jsname[i] = '\0'; - // The simplest code is correct! The temporary SimpleEdge takes // ownership of name; if the append succeeds, the vector element // then takes ownership; if the append fails, then the temporary // retains it, and its destructor will free it. if (!vec->append(mozilla::Move(SimpleEdge(jsname, Node(kind, *thingp))))) { okay = false; return; } } public: // True if no errors (OOM, say) have yet occurred. bool okay; - SimpleEdgeVectorTracer(JSContext *cx, SimpleEdgeVector *vec) - : JSTracer(JS_GetRuntime(cx), staticCallback), vec(vec), okay(true) { - } + SimpleEdgeVectorTracer(JSContext *cx, SimpleEdgeVector *vec, bool wantNames) + : JSTracer(JS_GetRuntime(cx), staticCallback), + vec(vec), + wantNames(wantNames), + okay(true) + { } }; // An EdgeRange concrete class that simply holds a vector of SimpleEdges, // populated by the init method. class SimpleEdgeRange : public EdgeRange { SimpleEdgeVector edges; size_t i; void settle() { front_ = i < edges.length() ? &edges[i] : nullptr; } public: explicit SimpleEdgeRange(JSContext *cx) : edges(cx), i(0) { } - bool init(JSContext *cx, void *thing, JSGCTraceKind kind) { - SimpleEdgeVectorTracer tracer(cx, &edges); + bool init(JSContext *cx, void *thing, JSGCTraceKind kind, bool wantNames = true) { + SimpleEdgeVectorTracer tracer(cx, &edges, wantNames); JS_TraceChildren(&tracer, thing, kind); settle(); return tracer.okay; } void popFront() MOZ_OVERRIDE { i++; settle(); } }; template<typename Referent> EdgeRange * -TracerConcrete<Referent>::edges(JSContext *cx) const { +TracerConcrete<Referent>::edges(JSContext *cx, bool wantNames) const { js::ScopedJSDeletePtr<SimpleEdgeRange> r(js_new<SimpleEdgeRange>(cx)); if (!r) return nullptr; - if (!r->init(cx, ptr, ::js::gc::MapTypeToTraceKind<Referent>::kind)) + if (!r->init(cx, ptr, ::js::gc::MapTypeToTraceKind<Referent>::kind, wantNames)) return nullptr; return r.forget(); } template<> const jschar TracerConcrete<JSObject>::concreteTypeName[] = MOZ_UTF16("JSObject"); template<> const jschar TracerConcrete<JSString>::concreteTypeName[] =
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/drop-shadow-default-color-ref.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Drop Shadow Default Color</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + filter: url(#drop-shadow); + background-color: #00f; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a blue square with a green drop shadow.</p> + <div id="target"></div> + <svg width="0" height="0"> + <filter id="drop-shadow" x="-50" y="-50" width="200" height="200" filterUnits="userSpaceOnUse"> + <feDropShadow dx="10" dy="10" stdDeviation="3" flood-color="#0f0" color-interpolation-filters="sRGB"/> + </filter> + </svg> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/drop-shadow-default-color.html @@ -0,0 +1,31 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Drop Shadow Default Color</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-drop-shadow"> + <link rel="help" href="http://www.w3.org/TR/css3-background/#the-box-shadow"> + <link rel="match" href="drop-shadow-default-color-ref.html"> + <meta name="assert" + content="If the color is unspecified in a CSS drop-shadow filter + function, it should default to the value of the CSS color + property."> + <style type="text/css"> + #target { + filter: drop-shadow(10px 10px 3px); + color: #0f0; + background-color: #00f; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a blue square with a green drop shadow.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/drop-shadow-negative-offset-ref.html @@ -0,0 +1,31 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> +<title>CSS Filters: Negative Drop Shadow Offset</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + filter: url(#drop-shadow); + background-color: #00f; + position: relative; + top: 20px; + left: 20px; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a blue square with a green drop shadow in its top left corner.</p> + <div id="target"></div> + <svg width="0" height="0"> + <filter id="drop-shadow" x="-50" y="-50" width="200" height="200" filterUnits="userSpaceOnUse"> + <feDropShadow dx="-10" dy="-10" stdDeviation="3" flood-color="#0f0" color-interpolation-filters="sRGB"/> + </filter> + </svg> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/drop-shadow-negative-offset.html @@ -0,0 +1,32 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Negative Drop Shadow Offset</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-drop-shadow"> + <link rel="match" href="drop-shadow-negative-offset-ref.html"> + <meta name="assert" + content="Given negative shadow offsets, the CSS drop-shadow filter + function should add a drop shadow extending from the top left + corner of an HTML element."> + <style type="text/css"> + #target { + filter: drop-shadow(-10px -10px 3px #0f0); + background-color: #00f; + position: relative; + top: 20px; + left: 20px; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a blue square with a green drop shadow in its top left corner.</p> + <div id="target"></div> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/drop-shadow-ref.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Drop Shadow on HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <style type="text/css"> + #target { + filter: url(#drop-shadow); + background-color: #00f; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a blue square with a green drop shadow.</p> + <div id="target"></div> + <svg width="0" height="0"> + <filter id="drop-shadow" x="-50" y="-50" width="200" height="200" filterUnits="userSpaceOnUse"> + <feDropShadow dx="10" dy="10" stdDeviation="3" flood-color="#0f0" color-interpolation-filters="sRGB"/> + </filter> + </svg> +</body> +</html>
new file mode 100644 --- /dev/null +++ b/layout/reftests/svg/filters/css-filters/drop-shadow.html @@ -0,0 +1,28 @@ +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<!DOCTYPE html> +<html> +<head> + <title>CSS Filters: Drop Shadow on HTML Element</title> + <link rel="author" title="Max Vujovic" href="mailto:mvujovic@adobe.com"> + <link rel="help" href="http://www.w3.org/TR/filter-effects-1/#funcdef-drop-shadow"> + <link rel="match" href="drop-shadow-ref.html"> + <meta name="assert" + content="The CSS drop-shadow filter function should add a drop shadow to + an HTML element."> + <style type="text/css"> + #target { + filter: drop-shadow(10px 10px 3px #0f0); + background-color: #00f; + width: 100px; + height: 100px; + } + </style> +</head> +<body> + <p>You should see a blue square with a green drop shadow.</p> + <div id="target"></div> +</body> +</html>
--- a/layout/reftests/svg/filters/css-filters/reftest.list +++ b/layout/reftests/svg/filters/css-filters/reftest.list @@ -2,8 +2,11 @@ # e.g. filter: blur(3px) default-preferences pref(layout.css.filters.enabled,true) == blur.html blur-ref.html == blur.svg blur-ref.svg == blur-zero-radius.html blur-zero-radius-ref.html == blur-zoomed-page.html blur-zoomed-page-ref.html +== drop-shadow.html drop-shadow-ref.html +== drop-shadow-default-color.html drop-shadow-default-color-ref.html +== drop-shadow-negative-offset.html drop-shadow-negative-offset-ref.html
--- a/layout/reftests/svg/smil/transform/reftest.list +++ b/layout/reftests/svg/smil/transform/reftest.list @@ -1,14 +1,14 @@ # Tests related to SVG Animation (using SMIL), focusing on the animateTransform # element. fuzzy(110,1802) == additive-1.svg additive-1-ref.svg # bug 981344 == animate-width-1.svg lime.svg -fuzzy-if(cocoaWidget,1,32) fuzzy-if(winWidget,1,1) == paced-1.svg paced-1-ref.svg # bug 981640 +fuzzy-if(cocoaWidget,1,32) fuzzy-if(winWidget,15,4) == paced-1.svg paced-1-ref.svg # bug 981640 == rotate-angle-1.svg rotate-angle-ref.svg == rotate-angle-2.svg rotate-angle-ref.svg == rotate-angle-3.svg rotate-angle-ref.svg == rotate-angle-4.svg rotate-angle-ref.svg == rotate-angle-5.svg rotate-angle-ref.svg fuzzy(12,27) == scale-1.svg scale-1-ref.svg # bug 981004 == set-transform-1.svg lime.svg fuzzy-if(winWidget,1,3) == skew-1.svg skew-1-ref.svg # bug 983671
--- a/layout/svg/nsCSSFilterInstance.cpp +++ b/layout/svg/nsCSSFilterInstance.cpp @@ -4,26 +4,29 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // Main header first: #include "nsCSSFilterInstance.h" // Keep others in (case-insensitive) order: #include "gfx2DGlue.h" #include "gfxUtils.h" +#include "nsIFrame.h" #include "nsStyleStruct.h" #include "nsTArray.h" using namespace mozilla; using namespace mozilla::gfx; nsCSSFilterInstance::nsCSSFilterInstance(const nsStyleFilter& aFilter, + nsIFrame *aTargetFrame, const nsIntRect& aTargetBBoxInFilterSpace, const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform) : mFilter(aFilter) + , mTargetFrame(aTargetFrame) , mTargetBBoxInFilterSpace(aTargetBBoxInFilterSpace) , mFrameSpaceInCSSPxToFilterSpaceTransform(aFrameSpaceInCSSPxToFilterSpaceTransform) { } nsresult nsCSSFilterInstance::BuildPrimitives(nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) { @@ -33,16 +36,19 @@ nsCSSFilterInstance::BuildPrimitives(nsT switch(mFilter.GetType()) { case NS_STYLE_FILTER_BLUR: descr = CreatePrimitiveDescription(PrimitiveType::GaussianBlur, aPrimitiveDescrs); result = SetAttributesForBlur(descr); break; case NS_STYLE_FILTER_BRIGHTNESS: case NS_STYLE_FILTER_CONTRAST: case NS_STYLE_FILTER_DROP_SHADOW: + descr = CreatePrimitiveDescription(PrimitiveType::DropShadow, aPrimitiveDescrs); + result = SetAttributesForDropShadow(descr); + break; case NS_STYLE_FILTER_GRAYSCALE: case NS_STYLE_FILTER_HUE_ROTATE: case NS_STYLE_FILTER_INVERT: case NS_STYLE_FILTER_OPACITY: case NS_STYLE_FILTER_SATURATE: case NS_STYLE_FILTER_SEPIA: return NS_ERROR_NOT_IMPLEMENTED; default: @@ -73,48 +79,105 @@ nsCSSFilterInstance::CreatePrimitiveDesc descr.SetInputColorSpace(0, ColorSpace::SRGB); descr.SetOutputColorSpace(ColorSpace::SRGB); return descr; } nsresult nsCSSFilterInstance::SetAttributesForBlur(FilterPrimitiveDescription& aDescr) { - // Get the radius from the style. - nsStyleCoord radiusStyleCoord = mFilter.GetFilterParameter(); - if (radiusStyleCoord.GetUnit() != eStyleUnit_Coord) { + const nsStyleCoord& radiusInFrameSpace = mFilter.GetFilterParameter(); + if (radiusInFrameSpace.GetUnit() != eStyleUnit_Coord) { NS_NOTREACHED("unexpected unit"); return NS_ERROR_FAILURE; } - // Get the radius in frame space. - nscoord radiusInFrameSpace = radiusStyleCoord.GetCoordValue(); + Size radiusInFilterSpace = BlurRadiusToFilterSpace(radiusInFrameSpace.GetCoordValue()); + aDescr.Attributes().Set(eGaussianBlurStdDeviation, radiusInFilterSpace); + return NS_OK; +} + +nsresult +nsCSSFilterInstance::SetAttributesForDropShadow(FilterPrimitiveDescription& aDescr) +{ + nsCSSShadowArray* shadows = mFilter.GetDropShadow(); + if (!shadows || shadows->Length() != 1) { + NS_NOTREACHED("Exactly one drop shadow should have been parsed."); + return NS_ERROR_FAILURE; + } + + nsCSSShadowItem* shadow = shadows->ShadowAt(0); + + // Set drop shadow blur radius. + Size radiusInFilterSpace = BlurRadiusToFilterSpace(shadow->mRadius); + aDescr.Attributes().Set(eDropShadowStdDeviation, radiusInFilterSpace); + + // Set offset. + IntPoint offsetInFilterSpace = OffsetToFilterSpace(shadow->mXOffset, shadow->mYOffset); + aDescr.Attributes().Set(eDropShadowOffset, offsetInFilterSpace); + + // Set color. If unspecified, use the CSS color property. + nscolor shadowColor = shadow->mHasColor ? + shadow->mColor : mTargetFrame->StyleColor()->mColor; + aDescr.Attributes().Set(eDropShadowColor, ToAttributeColor(shadowColor)); + + return NS_OK; +} + +Size +nsCSSFilterInstance::BlurRadiusToFilterSpace(nscoord aRadiusInFrameSpace) +{ float radiusInFrameSpaceInCSSPx = - nsPresContext::AppUnitsToFloatCSSPixels(radiusInFrameSpace); + nsPresContext::AppUnitsToFloatCSSPixels(aRadiusInFrameSpace); // Convert the radius to filter space. gfxSize radiusInFilterSpace(radiusInFrameSpaceInCSSPx, radiusInFrameSpaceInCSSPx); gfxSize frameSpaceInCSSPxToFilterSpaceScale = mFrameSpaceInCSSPxToFilterSpaceTransform.ScaleFactors(true); radiusInFilterSpace.Scale(frameSpaceInCSSPxToFilterSpaceScale.width, frameSpaceInCSSPxToFilterSpaceScale.height); // Check the radius limits. if (radiusInFilterSpace.width < 0 || radiusInFilterSpace.height < 0) { NS_NOTREACHED("we shouldn't have parsed a negative radius in the style"); - return NS_ERROR_FAILURE; + return Size(); } gfxFloat maxStdDeviation = (gfxFloat)kMaxStdDeviation; radiusInFilterSpace.width = std::min(radiusInFilterSpace.width, maxStdDeviation); radiusInFilterSpace.height = std::min(radiusInFilterSpace.height, maxStdDeviation); - // Set the radius parameter. - aDescr.Attributes().Set(eGaussianBlurStdDeviation, ToSize(radiusInFilterSpace)); - return NS_OK; + return ToSize(radiusInFilterSpace); +} + +IntPoint +nsCSSFilterInstance::OffsetToFilterSpace(nscoord aXOffsetInFrameSpace, + nscoord aYOffsetInFrameSpace) +{ + gfxPoint offsetInFilterSpace(nsPresContext::AppUnitsToFloatCSSPixels(aXOffsetInFrameSpace), + nsPresContext::AppUnitsToFloatCSSPixels(aYOffsetInFrameSpace)); + + // Convert the radius to filter space. + gfxSize frameSpaceInCSSPxToFilterSpaceScale = + mFrameSpaceInCSSPxToFilterSpaceTransform.ScaleFactors(true); + offsetInFilterSpace.x *= frameSpaceInCSSPxToFilterSpaceScale.width; + offsetInFilterSpace.y *= frameSpaceInCSSPxToFilterSpaceScale.height; + + return IntPoint(int32_t(offsetInFilterSpace.x), int32_t(offsetInFilterSpace.y)); +} + +Color +nsCSSFilterInstance::ToAttributeColor(nscolor aColor) +{ + return Color( + NS_GET_R(aColor) / 255.0, + NS_GET_G(aColor) / 255.0, + NS_GET_B(aColor) / 255.0, + NS_GET_A(aColor) / 255.0 + ); } int32_t nsCSSFilterInstance::GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs) { uint32_t numPrimitiveDescrs = aPrimitiveDescrs.Length(); return !numPrimitiveDescrs ? FilterPrimitiveDescription::kPrimitiveIndexSourceGraphic :
--- a/layout/svg/nsCSSFilterInstance.h +++ b/layout/svg/nsCSSFilterInstance.h @@ -4,41 +4,49 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef __NS_CSSFILTERINSTANCE_H__ #define __NS_CSSFILTERINSTANCE_H__ #include "FilterSupport.h" #include "gfxMatrix.h" #include "gfxRect.h" +#include "mozilla/gfx/Point.h" +#include "mozilla/gfx/Types.h" +#include "nsColor.h" +class nsIFrame; struct nsStyleFilter; template<class T> class nsTArray; /** * This class helps nsFilterInstance build its filter graph. It turns a CSS * filter function (e.g. blur(3px)) from the style system into a * FilterPrimitiveDescription connected to the filter graph. */ class nsCSSFilterInstance { + typedef mozilla::gfx::Color Color; typedef mozilla::gfx::FilterPrimitiveDescription FilterPrimitiveDescription; + typedef mozilla::gfx::IntPoint IntPoint; typedef mozilla::gfx::PrimitiveType PrimitiveType; + typedef mozilla::gfx::Size Size; public: /** * @param aFilter The CSS filter from the style system. This class stores * aFilter by reference, so callers should avoid modifying or deleting * aFilter during the lifetime of nsCSSFilterInstance. * @param mTargetBBoxInFilterSpace The frame of element being filtered, in * filter space. * @param aFrameSpaceInCSSPxToFilterSpaceTransform The transformation from * the filtered element's frame space in CSS pixels to filter space. */ nsCSSFilterInstance(const nsStyleFilter& aFilter, + nsIFrame *aTargetFrame, const nsIntRect& mTargetBBoxInFilterSpace, const gfxMatrix& aFrameSpaceInCSSPxToFilterSpaceTransform); /** * Creates at least one new FilterPrimitiveDescription based on the filter * from the style system. Appends the new FilterPrimitiveDescription(s) to the * aPrimitiveDescrs list. */ @@ -50,16 +58,17 @@ private: */ FilterPrimitiveDescription CreatePrimitiveDescription(PrimitiveType aType, const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs); /** * Sets aDescr's attributes using the style info in mFilter. */ nsresult SetAttributesForBlur(FilterPrimitiveDescription& aDescr); + nsresult SetAttributesForDropShadow(FilterPrimitiveDescription& aDescr); /** * Returns the index of the last result in the aPrimitiveDescrs, which we'll * use as the input to this CSS filter. */ int32_t GetLastResultIndex(const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs); /** @@ -67,21 +76,44 @@ private: * based on this CSS filter's input and its attributes. For example, a CSS * blur filter will have bounds equal to its input bounds, inflated by the * blur extents. */ void SetBounds(FilterPrimitiveDescription& aDescr, const nsTArray<FilterPrimitiveDescription>& aPrimitiveDescrs); /** + * Converts an nscolor to a Color, suitable for use as a + * FilterPrimitiveDescription attribute. + */ + Color ToAttributeColor(nscolor aColor); + + /** + * Converts a blur radius in frame space to filter space. + */ + Size BlurRadiusToFilterSpace(nscoord aRadiusInFrameSpace); + + /** + * Converts a point defined by a pair of nscoord x, y coordinates from frame + * space to filter space. + */ + IntPoint OffsetToFilterSpace(nscoord aXOffsetInFrameSpace, + nscoord aYOffsetInFrameSpace); + + /** * The CSS filter originally from the style system. */ const nsStyleFilter& mFilter; /** + * The frame for the element that is currently being filtered. + */ + nsIFrame* mTargetFrame; + + /** * The bounding box of the element being filtered, in filter space. Used for * input bounds if this CSS filter is the first in the filter chain. */ nsIntRect mTargetBBoxInFilterSpace; /** * The transformation from the filtered element's frame space in CSS pixels to * filter space. Used to transform style values to filter space.
--- a/layout/svg/nsFilterInstance.cpp +++ b/layout/svg/nsFilterInstance.cpp @@ -255,17 +255,18 @@ nsFilterInstance::BuildPrimitivesForFilt if (!svgFilterInstance.IsInitialized()) { return NS_ERROR_FAILURE; } return svgFilterInstance.BuildPrimitives(mPrimitiveDescriptions, mInputImages); } // Build primitives for a CSS filter. - nsCSSFilterInstance cssFilterInstance(aFilter, mTargetBBoxInFilterSpace, + nsCSSFilterInstance cssFilterInstance(aFilter, mTargetFrame, + mTargetBBoxInFilterSpace, mFrameSpaceInCSSPxToFilterSpaceTransform); return cssFilterInstance.BuildPrimitives(mPrimitiveDescriptions); } void nsFilterInstance::ComputeNeededBoxes() { if (mPrimitiveDescriptions.IsEmpty())
--- a/media/mtransport/third_party/nICEr/src/stun/stun_codec.c +++ b/media/mtransport/third_party/nICEr/src/stun/stun_codec.c @@ -114,17 +114,17 @@ nr_stun_encode_htonl(UINT4 data, int buf *offset += sizeof(d); return 0; } int nr_stun_encode_htonll(UINT8 data, int buflen, UCHAR *buf, int *offset) { - UINT8 d = htonll(data); + UINT8 d = nr_htonll(data); if (*offset + sizeof(d) > buflen) { r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); return R_BAD_DATA; } memcpy(&buf[*offset], &d, sizeof(d)); *offset += sizeof(d); @@ -188,17 +188,17 @@ nr_stun_decode_htonll(UCHAR *buf, int bu if (*offset + sizeof(d) > buflen) { r_log(NR_LOG_STUN, LOG_WARNING, "Attempted buffer overrun: %d + %zd > %d", *offset, sizeof(d), buflen); return R_BAD_DATA; } memcpy(&d, &buf[*offset], sizeof(d)); *offset += sizeof(d); - *data = htonll(d); + *data = nr_htonll(d); return 0; } int nr_stun_decode(int length, UCHAR *buf, int buflen, int *offset, UCHAR *data) { if (*offset + length > buflen) {
--- a/media/mtransport/third_party/nrappkit/src/util/byteorder.c +++ b/media/mtransport/third_party/nrappkit/src/util/byteorder.c @@ -48,29 +48,29 @@ static char *RCSSTRING __UNUSED__ ="$Id: #define IS_BIG_ENDIAN (htonl(0x1) == 0x1) #define BYTE(n,i) (((UCHAR*)&(n))[(i)]) #define SWAP(n,x,y) tmp=BYTE((n),(x)), \ BYTE((n),(x))=BYTE((n),(y)), \ BYTE((n),(y))=tmp UINT8 -htonll(UINT8 hostlonglong) +nr_htonll(UINT8 hostlonglong) { UINT8 netlonglong = hostlonglong; UCHAR tmp; if (!IS_BIG_ENDIAN) { SWAP(netlonglong, 0, 7); SWAP(netlonglong, 1, 6); SWAP(netlonglong, 2, 5); SWAP(netlonglong, 3, 4); } return netlonglong; } UINT8 -ntohll(UINT8 netlonglong) +nr_ntohll(UINT8 netlonglong) { - return htonll(netlonglong); + return nr_htonll(netlonglong); }
--- a/media/mtransport/third_party/nrappkit/src/util/byteorder.h +++ b/media/mtransport/third_party/nrappkit/src/util/byteorder.h @@ -34,14 +34,14 @@ briank@networkresonance.com Wed May 16 16:46:00 PDT 2007 */ #ifndef _byteorder_h #define _byteorder_h -UINT8 htonll(UINT8 hostlonglong); +UINT8 nr_htonll(UINT8 hostlonglong); -UINT8 ntohll(UINT8 netlonglong); +UINT8 nr_ntohll(UINT8 netlonglong); #endif
--- a/media/mtransport/transportlayerdtls.cpp +++ b/media/mtransport/transportlayerdtls.cpp @@ -587,17 +587,19 @@ bool TransportLayerDtls::Setup() { // builds, but can be disabled with prefs and they aren't on in our unit tests // since that uses NSS default configuration. // Only override prefs to comply with MUST statements in the security-arch. static const uint32_t EnabledCiphers[] = { TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA }; -// Disalbe all NSS suites modes without PFS or with old and rusty ciphersuites. +// Don't remove suites; TODO(mt@mozilla.com) restore; bug 1052610 +#if 0 +// Disable all NSS suites modes without PFS or with old and rusty ciphersuites. // Anything outside this list is governed by the usual combination of policy // and user preferences. static const uint32_t DisabledCiphers[] = { TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, TLS_ECDHE_RSA_WITH_RC4_128_SHA, @@ -642,16 +644,17 @@ static const uint32_t DisabledCiphers[] TLS_ECDHE_RSA_WITH_NULL_SHA, TLS_ECDH_ECDSA_WITH_NULL_SHA, TLS_ECDH_RSA_WITH_NULL_SHA, TLS_RSA_WITH_NULL_SHA, TLS_RSA_WITH_NULL_SHA256, TLS_RSA_WITH_NULL_MD5, }; +#endif // bug 1052610 bool TransportLayerDtls::SetupCipherSuites(PRFileDesc* ssl_fd) const { SECStatus rv; // Set the SRTP ciphers if (!srtp_ciphers_.empty()) { // Note: std::vector is guaranteed to contiguous rv = SSL_SetSRTPCiphers(ssl_fd, &srtp_ciphers_[0], srtp_ciphers_.size());
--- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -453,18 +453,18 @@ pref("apz.x_stationary_size_multiplier", pref("apz.y_stationary_size_multiplier", "3.5"); pref("apz.zoom_animation_duration_ms", 250); #ifdef XP_MACOSX // Layerize scrollable subframes to allow async panning pref("apz.subframe.enabled", true); pref("apz.fling_repaint_interval", 16); pref("apz.pan_repaint_interval", 16); -pref("apz.apz.x_skate_size_multiplier", "2.5"); -pref("apz.apz.y_skate_size_multiplier", "3.5"); +pref("apz.x_skate_size_multiplier", "2.5"); +pref("apz.y_skate_size_multiplier", "3.5"); #else pref("apz.subframe.enabled", false); pref("apz.fling_repaint_interval", 75); pref("apz.pan_repaint_interval", 250); pref("apz.x_skate_size_multiplier", "1.5"); pref("apz.y_skate_size_multiplier", "2.5"); #endif
--- a/security/sandbox/linux/Sandbox.cpp +++ b/security/sandbox/linux/Sandbox.cpp @@ -42,17 +42,17 @@ // See definition of SandboxDie, below. #include "sandbox/linux/seccomp-bpf/die.h" namespace mozilla { #if defined(ANDROID) #define LOG_ERROR(args...) __android_log_print(ANDROID_LOG_ERROR, "Sandbox", ## args) #else -#define LOG_ERROR(fmt, args...) fprintf(stderr, "Sandbox: " fmt, ## args) +#define LOG_ERROR(fmt, args...) fprintf(stderr, "Sandbox: " fmt "\n", ## args) #endif #ifdef MOZ_GMP_SANDBOX // For media plugins, we can start the sandbox before we dlopen the // module, so we have to pre-open the file and simulate the sandboxed // open(). static int gMediaPluginFileDesc = -1; static const char *gMediaPluginFilePath;
--- a/testing/mozbase/mozrunner/mozrunner/base/browser.py +++ b/testing/mozbase/mozrunner/mozrunner/base/browser.py @@ -49,23 +49,16 @@ class GeckoRuntimeRunner(BaseRunner): if mozinfo.isMac and '-foreground' not in self.cmdargs: # runner should specify '-foreground' on Mac; see # https://bugzilla.mozilla.org/show_bug.cgi?id=916512 self.cmdargs.append('-foreground') # Bug 775416 - Ensure that binary options are passed in first command[1:1] = self.cmdargs - # If running on OS X 10.5 or older, wrap |cmd| so that it will - # be executed as an i386 binary, in case it's a 32-bit/64-bit universal - # binary. - if mozinfo.isMac and hasattr(platform, 'mac_ver') and \ - platform.mac_ver()[0][:4] < '10.6': - command = ["arch", "-arch", "i386"] + command - if hasattr(self.app_ctx, 'wrap_command'): command = self.app_ctx.wrap_command(command) return command def start(self, *args, **kwargs): # ensure the profile exists if not self.profile.exists(): self.profile.reset()
--- a/xpcom/glue/BlockingResourceBase.cpp +++ b/xpcom/glue/BlockingResourceBase.cpp @@ -6,16 +6,23 @@ #include "mozilla/BlockingResourceBase.h" #ifdef DEBUG #include "prthread.h" #include "nsAutoPtr.h" +#ifndef MOZ_CALLSTACK_DISABLED +#include "CodeAddressService.h" +#include "nsHashKeys.h" +#include "nsStackWalk.h" +#include "nsTHashtable.h" +#endif + #include "mozilla/CondVar.h" #include "mozilla/DeadlockDetector.h" #include "mozilla/ReentrantMonitor.h" #include "mozilla/Mutex.h" #ifdef MOZILLA_INTERNAL_API #include "GeckoProfiler.h" #endif //MOZILLA_INTERNAL_API @@ -36,16 +43,41 @@ const char* const BlockingResourceBase:: #ifdef DEBUG PRCallOnceType BlockingResourceBase::sCallOnce; unsigned BlockingResourceBase::sResourceAcqnChainFrontTPI = (unsigned)-1; BlockingResourceBase::DDT* BlockingResourceBase::sDeadlockDetector; +void +BlockingResourceBase::StackWalkCallback(void* aPc, void* aSp, void* aClosure) +{ +#ifndef MOZ_CALLSTACK_DISABLED + AcquisitionState* state = (AcquisitionState*)aClosure; + state->AppendElement(aPc); +#endif +} + +void +BlockingResourceBase::GetStackTrace(AcquisitionState& aState) +{ +#ifndef MOZ_CALLSTACK_DISABLED + // Skip this function and the calling function. + const uint32_t kSkipFrames = 2; + + aState.Clear(); + + // NB: Ignore the return value, there's nothing useful we can do if this + // this fails. + NS_StackWalk(StackWalkCallback, kSkipFrames, + 24, &aState, 0, nullptr); +#endif +} + /** * PrintCycle * Append to |aOut| detailed information about the circular * dependency in |aCycle|. Returns true if it *appears* that this * cycle may represent an imminent deadlock, but this is merely a * heuristic; the value returned may be a false positive or false * negative. * @@ -80,44 +112,125 @@ PrintCycle(const BlockingResourceBase::D fputs("\n=== Cycle completed at\n", stderr); aOut += "Cycle completed at\n"; (*it)->Print(aOut); return maybeImminent; } +#ifndef MOZ_CALLSTACK_DISABLED +class CodeAddressServiceWriter MOZ_FINAL +{ +public: + explicit CodeAddressServiceWriter(nsACString& aOut) : mOut(aOut) {} + + void Write(const char* aFmt, ...) const + { + va_list ap; + va_start(ap, aFmt); + + const size_t kMaxLength = 4096; + char buffer[kMaxLength]; + + vsnprintf(buffer, kMaxLength, aFmt, ap); + mOut += buffer; + fprintf(stderr, "%s", buffer); + + va_end(ap); + } + +private: + nsACString& mOut; +}; + +struct CodeAddressServiceLock MOZ_FINAL +{ + static void Unlock() { } + static void Lock() { } + static bool IsLocked() { return true; } +}; + +struct CodeAddressServiceStringAlloc MOZ_FINAL +{ + static char* copy(const char* aString) { return ::strdup(aString); } + static void free(char* aString) { ::free(aString); } +}; + +class CodeAddressServiceStringTable MOZ_FINAL +{ +public: + CodeAddressServiceStringTable() : mSet(32) {} + + const char* Intern(const char* aString) + { + nsCharPtrHashKey* e = mSet.PutEntry(aString); + return e->GetKey(); + } + + size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const + { + return mSet.SizeOfExcludingThis(aMallocSizeOf); + } + +private: + typedef nsTHashtable<nsCharPtrHashKey> StringSet; + StringSet mSet; +}; + +typedef CodeAddressService<CodeAddressServiceStringTable, + CodeAddressServiceStringAlloc, + CodeAddressServiceWriter, + CodeAddressServiceLock> WalkTheStackCodeAddressService; +#endif bool BlockingResourceBase::Print(nsACString& aOut) const { fprintf(stderr, "--- %s : %s", kResourceTypeName[mType], mName); aOut += BlockingResourceBase::kResourceTypeName[mType]; aOut += " : "; aOut += mName; - if (mAcquired) { + bool acquired = IsAcquired(); + + if (acquired) { fputs(" (currently acquired)\n", stderr); aOut += " (currently acquired)\n"; } fputs(" calling context\n", stderr); +#ifdef MOZ_CALLSTACK_DISABLED fputs(" [stack trace unavailable]\n", stderr); +#else + const AcquisitionState& state = acquired ? mAcquired : mFirstSeen; - return mAcquired; + WalkTheStackCodeAddressService addressService; + CodeAddressServiceWriter writer(aOut); + for (uint32_t i = 0; i < state.Length(); i++) { + addressService.WriteLocation(writer, state[i]); + } + +#endif + + return acquired; } BlockingResourceBase::BlockingResourceBase( const char* aName, BlockingResourceBase::BlockingResourceType aType) : mName(aName) , mType(aType) +#ifdef MOZ_CALLSTACK_DISABLED , mAcquired(false) +#else + , mAcquired() +#endif { NS_ABORT_IF_FALSE(mName, "Name must be nonnull"); // PR_CallOnce guaranatees that InitStatics is called in a // thread-safe way if (PR_SUCCESS != PR_CallOnce(&sCallOnce, InitStatics)) { NS_RUNTIMEABORT("can't initialize blocking resource static members"); } @@ -177,16 +290,21 @@ BlockingResourceBase::CheckAcquire() BlockingResourceBase* chainFront = ResourceChainFront(); nsAutoPtr<DDT::ResourceAcquisitionArray> cycle( sDeadlockDetector->CheckAcquisition( chainFront ? chainFront : 0, this)); if (!cycle) { return; } +#ifndef MOZ_CALLSTACK_DISABLED + // Update the current stack before printing. + GetStackTrace(mAcquired); +#endif + fputs("###!!! ERROR: Potential deadlock detected:\n", stderr); nsAutoCString out("Potential deadlock detected:\n"); bool maybeImminent = PrintCycle(cycle, out); if (maybeImminent) { fputs("\n###!!! Deadlock may happen NOW!\n\n", stderr); out.AppendLiteral("\n###!!! Deadlock may happen NOW!\n\n"); } else { @@ -206,35 +324,44 @@ BlockingResourceBase::CheckAcquire() void BlockingResourceBase::Acquire() { if (mType == eCondVar) { NS_NOTYETIMPLEMENTED( "FIXME bug 456272: annots. to allow Acquire()ing condvars"); return; } - NS_ASSERTION(!mAcquired, + NS_ASSERTION(!IsAcquired(), "reacquiring already acquired resource"); ResourceChainAppend(ResourceChainFront()); + +#ifdef MOZ_CALLSTACK_DISABLED mAcquired = true; +#else + // Take a stack snapshot. + GetStackTrace(mAcquired); + if (mFirstSeen.IsEmpty()) { + mFirstSeen = mAcquired; + } +#endif } void BlockingResourceBase::Release() { if (mType == eCondVar) { NS_NOTYETIMPLEMENTED( "FIXME bug 456272: annots. to allow Release()ing condvars"); return; } BlockingResourceBase* chainFront = ResourceChainFront(); - NS_ASSERTION(chainFront && mAcquired, + NS_ASSERTION(chainFront && IsAcquired(), "Release()ing something that hasn't been Acquire()ed"); if (chainFront == this) { ResourceChainRemove(); } else { // not an error, but makes code hard to reason about. NS_WARNING("Resource acquired at calling context\n"); NS_WARNING(" [stack trace unavailable]\n"); @@ -250,17 +377,17 @@ BlockingResourceBase::Release() while (curr && (prev = curr->mChainPrev) && (prev != this)) { curr = prev; } if (prev == this) { curr->mChainPrev = prev->mChainPrev; } } - mAcquired = false; + ClearAcquisitionState(); } // // Debug implementation of (OffTheBooks)Mutex void OffTheBooksMutex::Lock() { @@ -335,20 +462,20 @@ ReentrantMonitor::Exit() nsresult ReentrantMonitor::Wait(PRIntervalTime aInterval) { AssertCurrentThreadIn(); // save monitor state and reset it to empty int32_t savedEntryCount = mEntryCount; - bool savedAcquisitionState = GetAcquisitionState(); + AcquisitionState savedAcquisitionState = GetAcquisitionState(); BlockingResourceBase* savedChainPrev = mChainPrev; mEntryCount = 0; - SetAcquisitionState(false); + ClearAcquisitionState(); mChainPrev = 0; nsresult rv; #ifdef MOZILLA_INTERNAL_API { GeckoProfilerSleepRAII profiler_sleep; #endif //MOZILLA_INTERNAL_API @@ -372,19 +499,19 @@ ReentrantMonitor::Wait(PRIntervalTime aI // // Debug implementation of CondVar nsresult CondVar::Wait(PRIntervalTime aInterval) { AssertCurrentThreadOwnsMutex(); // save mutex state and reset to empty - bool savedAcquisitionState = mLock->GetAcquisitionState(); + AcquisitionState savedAcquisitionState = mLock->GetAcquisitionState(); BlockingResourceBase* savedChainPrev = mLock->mChainPrev; - mLock->SetAcquisitionState(false); + mLock->ClearAcquisitionState(); mLock->mChainPrev = 0; // give up mutex until we're back from Wait() nsresult rv = PR_WaitCondVar(mCvar, aInterval) == PR_SUCCESS ? NS_OK : NS_ERROR_FAILURE; // restore saved state mLock->SetAcquisitionState(savedAcquisitionState);
--- a/xpcom/glue/BlockingResourceBase.h +++ b/xpcom/glue/BlockingResourceBase.h @@ -11,19 +11,28 @@ #include "prlog.h" #include "nscore.h" #include "nsDebug.h" #include "nsError.h" #include "nsISupportsImpl.h" #ifdef DEBUG + +// NB: Comment this out to enable callstack tracking. +#define MOZ_CALLSTACK_DISABLED + #include "prinit.h" #include "nsStringGlue.h" + +#ifndef MOZ_CALLSTACK_DISABLED +#include "nsTArray.h" +#endif + #include "nsXPCOM.h" #endif // // This header is not meant to be included by client code. // namespace mozilla { @@ -83,16 +92,22 @@ public: size_t n = aMallocSizeOf(this); return n; } // ``DDT'' = ``Deadlock Detector Type'' typedef DeadlockDetector<BlockingResourceBase> DDT; protected: +#ifdef MOZ_CALLSTACK_DISABLED + typedef bool AcquisitionState; +#else + typedef nsAutoTArray<void*, 24> AcquisitionState; +#endif + /** * BlockingResourceBase * Initialize this blocking resource. Also hooks the resource into * instrumentation code. * * Thread safe. * * @param aName A meaningful, unique name that can be used in @@ -179,33 +194,63 @@ protected: } //NS_NEEDS_RESOURCE(this) /** * GetAcquisitionState * Return whether or not this resource was acquired. * * *NOT* thread safe. Requires ownership of underlying resource. */ - bool GetAcquisitionState() + AcquisitionState GetAcquisitionState() { return mAcquired; } /** * SetAcquisitionState * Set whether or not this resource was acquired. * * *NOT* thread safe. Requires ownership of underlying resource. */ - void SetAcquisitionState(bool aAcquisitionState) + void SetAcquisitionState(const AcquisitionState& aAcquisitionState) { mAcquired = aAcquisitionState; } /** + * ClearAcquisitionState + * Indicate this resource is not acquired. + * + * *NOT* thread safe. Requires ownership of underlying resource. + */ + void ClearAcquisitionState() + { +#ifdef MOZ_CALLSTACK_DISABLED + mAcquired = false; +#else + mAcquired.Clear(); +#endif + } + + /** + * IsAcquired + * Indicates if this resource is acquired. + * + * *NOT* thread safe. Requires ownership of underlying resource. + */ + bool IsAcquired() const + { +#ifdef MOZ_CALLSTACK_DISABLED + return mAcquired; +#else + return !mAcquired.IsEmpty(); +#endif + } + + /** * mChainPrev * A series of resource acquisitions creates a chain of orders. This * chain is implemented as a linked list; |mChainPrev| points to the * resource most recently Acquire()'d before this one. **/ BlockingResourceBase* mChainPrev; private: @@ -222,17 +267,25 @@ private: * special semantics (e.g., reentrancy of monitors). **/ BlockingResourceType mType; /** * mAcquired * Indicates if this resource is currently acquired. */ - bool mAcquired; + AcquisitionState mAcquired; + +#ifndef MOZ_CALLSTACK_DISABLED + /** + * mFirstSeen + * Inidicates where this resource was first acquired. + */ + AcquisitionState mFirstSeen; +#endif /** * sCallOnce * Ensures static members are initialized only once, and in a * thread-safe way. */ static PRCallOnceType sCallOnce; @@ -261,16 +314,19 @@ private: /** * Shutdown * Free static members. * * *NOT* thread safe. */ static void Shutdown(); + static void StackWalkCallback(void* aPc, void* aSp, void* aClosure); + static void GetStackTrace(AcquisitionState& aState); + # ifdef MOZILLA_INTERNAL_API // so it can call BlockingResourceBase::Shutdown() friend void LogTerm(); # endif // ifdef MOZILLA_INTERNAL_API #else // non-DEBUG implementation BlockingResourceBase(const char* aName, BlockingResourceType aType) {}
--- a/xpcom/threads/ThreadStackHelper.cpp +++ b/xpcom/threads/ThreadStackHelper.cpp @@ -600,17 +600,20 @@ ThreadStackHelper::FillStackBuffer() continue; } #ifdef MOZ_THREADSTACKHELPER_NATIVE if (mContextToFill) { mContextToFill->mStackEnd = entry->stackAddress(); } #endif const char* const label = entry->label(); - if (mStackToFill->IsSameAsEntry(prevLabel, label)) { + if (mStackToFill->IsSameAsEntry(prevLabel, label) || + !strcmp(label, "js::RunScript")) { + // Avoid duplicate labels to save space in the stack. + // Avoid js::RunScript labels because we save actual JS frames above. continue; } mStackToFill->infallibleAppend(label); prevLabel = label; } // end != entry if we exited early due to not enough reserved frames. // Expand the number of reserved frames for next time.