Merge last green PGO from inbound to central

This commit is contained in:
Marco Bonardo 2012-02-24 11:22:53 +01:00
Родитель cb367b9369 89aab08c22
Коммит c76aee5341
91 изменённых файлов: 2686 добавлений и 953 удалений

Просмотреть файл

@ -390,6 +390,7 @@ pref("layers.acceleration.force-enabled", true);
// screen.enabled and screen.brightness properties.
pref("dom.screenEnabledProperty.enabled", true);
pref("dom.screenBrightnessProperty.enabled", true);
pref("dom.mozScreenWhitelist", "http://localhost:7777");
// handle links targeting new windows
// 1=current window/tab, 2=new window, 3=new tab in most recent window
@ -428,6 +429,10 @@ pref("layout.frame_rate.precise", true);
pref("b2g.remote-js.enabled", true);
pref("b2g.remote-js.port", 9999);
// Handle hardware buttons in the b2g chrome package
pref("b2g.keys.menu.enabled", true);
pref("b2g.keys.search.enabled", false);
// Screen timeout in minutes
pref("power.screen.timeout", 60);

Просмотреть файл

@ -37,6 +37,23 @@ XPCOMUtils.defineLazyServiceGetter(Services, 'fm', function(){
.getService(Ci.nsFocusManager);
});
#ifndef MOZ_WIDGET_GONK
// In order to use http:// scheme instead of file:// scheme
// (that is much more restricted) the following code kick-off
// a local http server listening on http://127.0.0.1:7777 and
// http://localhost:7777.
function startupHttpd(baseDir, port) {
const httpdURL = 'chrome://browser/content/httpd.js';
let httpd = {};
Services.scriptloader.loadSubScript(httpdURL, httpd);
let server = new httpd.nsHttpServer();
server.registerDirectory('/', new LocalFile(baseDir));
server.registerContentType('appcache', 'text/cache-manifest');
server.start(port);
}
#endif
// FIXME Bug 707625
// until we have a proper security model, add some rights to
// the pre-installed web applications
@ -105,7 +122,21 @@ var shell = {
let fileScheme = 'file://';
if (homeURL.substring(0, fileScheme.length) == fileScheme) {
#ifndef MOZ_WIDGET_GONK
homeURL = homeURL.replace(fileScheme, '');
let baseDir = homeURL.split('/');
baseDir.pop();
baseDir = baseDir.join('/');
const SERVER_PORT = 7777;
startupHttpd(baseDir, SERVER_PORT);
let baseHost = 'http://localhost';
homeURL = homeURL.replace(baseDir, baseHost + ':' + SERVER_PORT);
#else
homeURL = 'http://localhost:7777' + homeURL.replace(fileScheme, '');
#endif
}
addPermissions([homeURL]);
} catch (e) {
@ -213,10 +244,12 @@ var shell = {
case 'AppCommand':
switch (evt.command) {
case 'Menu':
this.sendEvent(content, 'menu');
if (Services.prefs.getBoolPref('b2g.keys.menu.enabled'))
this.sendEvent(content, 'menu');
break;
case 'Search':
this.toggleDebug();
if (Services.prefs.getBoolPref('b2g.keys.search.enabled'))
this.toggleDebug();
break;
case 'VolumeUp':
this.changeVolume(1);

Просмотреть файл

@ -20,6 +20,9 @@
#ifndef MOZ_TOUCH
<script type="application/javascript" src="chrome://browser/content/touch.js"/>
#endif
#ifndef MOZ_WIDGET_GONK
<script type="application/javascript" src="chrome://browser/content/httpd.js"/>
#endif
<commandset id="mainCommandSet">
<command id="cmd_close" oncommand="CommandUpdater.doCommand(this.id);"/>

Просмотреть файл

@ -322,7 +322,7 @@ const KineticPanning = {
return this.target !== null;
},
_target: null,
target: null,
start: function kp_start(target) {
this.target = target;

Просмотреть файл

@ -5,12 +5,14 @@ chrome.jar:
% content browser %content/
* content/shell.xul (content/shell.xul)
content/shell.js (content/shell.js)
* content/shell.js (content/shell.js)
#ifndef MOZ_TOUCH
content/touch.js (content/touch.js)
#endif
content/commandUtil.js (content/commandUtil.js)
#ifndef MOZ_WIDGET_GONK
content/httpd.js (content/httpd.js)
#endif
content/webapi.js (content/webapi.js)
content/content.css (content/content.css)

Просмотреть файл

@ -224,7 +224,6 @@ FirefoxProfileMigrator.prototype = {
migrate : function Firefox_migrate(aItems, aStartup, aProfile)
{
if (aStartup) {
aStartup.doStartup();
this._replaceBookmarks = true;
}
@ -246,6 +245,11 @@ FirefoxProfileMigrator.prototype = {
if (aItems & MIGRATOR.PASSWORDS)
this._migratePasswords();
// The password manager encryption key must be copied before startup.
if (aStartup) {
aStartup.doStartup();
}
if (aItems & MIGRATOR.FORMDATA)
this._migrateFormData();

Просмотреть файл

@ -169,11 +169,11 @@ libs:: $(addsuffix .xml,$(SEARCH_PLUGINS))
done
install:: $(addsuffix .xml,$(SEARCH_PLUGINS))
$(NSINSTALL) -D $(DESTDIR)$mozappdir/searchplugins
$(NSINSTALL) -D $(DESTDIR)$(mozappdir)/searchplugins
for i in $^; do \
SEARCH_PLUGIN_BASE=`basename $$SEARCH_PLUGIN`;\
$(PYTHON) $(topsrcdir)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) \
$$SEARCH_PLUGIN > $(DESTDIR)$mozappdir/searchplugins/$$SEARCH_PLUGIN_BASE; \
$$SEARCH_PLUGIN > $(DESTDIR)$(mozappdir)/searchplugins/$$SEARCH_PLUGIN_BASE; \
done

Просмотреть файл

@ -792,8 +792,10 @@ EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-
EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(MKSHLIB)
ifdef STDCXX_COMPAT
ifneq ($(OS_ARCH),Darwin)
CHECK_STDCXX = objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' > /dev/null && echo "TEST-UNEXPECTED-FAIL | | We don't want these libstdc++ symbols to be used:" && objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && exit 1 || exit 0
endif
endif
# autoconf.mk sets OBJ_SUFFIX to an error to avoid use before including
# this file

Просмотреть файл

@ -691,6 +691,7 @@ DragDataProducer::Produce(nsDOMDataTransfer* aDataTransfer,
rv = nsCopySupport::GetTransferableForNode(nodeToSerialize, doc,
getter_AddRefs(transferable));
}
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISupportsString> data;
PRUint32 dataSize;

Просмотреть файл

@ -94,7 +94,8 @@ nsSMILAnimationFunction::nsSMILAnimationFunction()
mLastValue(false),
mHasChanged(true),
mValueNeedsReparsingEverySample(false),
mPrevSampleWasSingleValueAnimation(false)
mPrevSampleWasSingleValueAnimation(false),
mWasSkippedInPrevSample(false)
{
}
@ -233,6 +234,7 @@ nsSMILAnimationFunction::ComposeResult(const nsISMILAttr& aSMILAttr,
{
mHasChanged = false;
mPrevSampleWasSingleValueAnimation = false;
mWasSkippedInPrevSample = false;
// Skip animations that are inactive or in error
if (!IsActiveOrFrozen() || mErrorFlags != 0)

Просмотреть файл

@ -247,6 +247,24 @@ public:
*/
bool UpdateCachedTarget(const nsSMILTargetIdentifier& aNewTarget);
/**
* Returns true if this function was skipped in the previous sample (because
* there was a higher-priority non-additive animation). If a skipped animation
* function is later used, then the animation sandwich must be recomposited.
*/
bool WasSkippedInPrevSample() const {
return mWasSkippedInPrevSample;
}
/**
* Mark this animation function as having been skipped. By marking the
* function as skipped, if it is used in a subsequent sample we'll know to
* recomposite the sandwich.
*/
void SetWasSkipped() {
mWasSkippedInPrevSample = true;
}
// Comparator utility class, used for sorting nsSMILAnimationFunctions
class Comparator {
public:
@ -466,12 +484,13 @@ protected:
nsSMILWeakTargetIdentifier mLastTarget;
// Boolean flags
bool mIsActive:1;
bool mIsFrozen:1;
bool mLastValue:1;
bool mHasChanged:1;
bool mValueNeedsReparsingEverySample:1;
bool mPrevSampleWasSingleValueAnimation:1;
bool mIsActive:1;
bool mIsFrozen:1;
bool mLastValue:1;
bool mHasChanged:1;
bool mValueNeedsReparsingEverySample:1;
bool mPrevSampleWasSingleValueAnimation:1;
bool mWasSkippedInPrevSample:1;
};
#endif // NS_SMILANIMATIONFUNCTION_H_

Просмотреть файл

@ -173,16 +173,31 @@ nsSMILCompositor::GetFirstFuncToAffectSandwich()
PRUint32 i;
for (i = mAnimationFunctions.Length(); i > 0; --i) {
nsSMILAnimationFunction* curAnimFunc = mAnimationFunctions[i-1];
if (curAnimFunc->UpdateCachedTarget(mKey) ||
(!mForceCompositing && curAnimFunc->HasChanged())) {
mForceCompositing = true;
}
// In the following, the lack of short-circuit behavior of |= means that we
// will ALWAYS run UpdateCachedTarget (even if mForceCompositing is true)
// but only call HasChanged and WasSkippedInPrevSample if necessary. This
// is important since we need UpdateCachedTarget to run in order to detect
// changes to the target in subsequent samples.
mForceCompositing |=
curAnimFunc->UpdateCachedTarget(mKey) ||
curAnimFunc->HasChanged() ||
curAnimFunc->WasSkippedInPrevSample();
if (curAnimFunc->WillReplace()) {
--i;
break;
}
}
// Mark remaining animation functions as having been skipped so if we later
// use them we'll know to force compositing.
// Note that we only really need to do this if something has changed
// (otherwise we would have set the flag on a previous sample) and if
// something has changed mForceCompositing will be true.
if (mForceCompositing) {
for (PRUint32 j = i; j > 0; --j) {
mAnimationFunctions[j-1]->SetWasSkipped();
}
}
return i;
}

Просмотреть файл

@ -131,7 +131,7 @@ public:
bool mForceCompositing;
// Cached base value, so we can detect & force-recompose when it changes
// from one sample to the next. (nsSMILAnimationController copies this
// from one sample to the next. (nsSMILAnimationController copies this
// forward from the previous sample's compositor.)
nsAutoPtr<nsSMILValue> mCachedBaseValue;
};

Просмотреть файл

@ -615,11 +615,6 @@ nsSMILParserUtils::ParseValuesGeneric(const nsAString& aSpec,
}
}
// Disallow ;-terminated values lists.
if (tokenizer.lastTokenEndedWithSeparator()) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}

Просмотреть файл

@ -55,6 +55,7 @@
#include "nsString.h"
#include "mozilla/AutoRestore.h"
#include "mozilla/Util.h"
#include "nsCharSeparatedTokenizer.h"
using namespace mozilla;
@ -1271,10 +1272,6 @@ nsSMILTimedElement::SetBeginOrEndSpec(const nsAString& aSpec,
bool aIsBegin,
RemovalTestFunction aRemove)
{
PRInt32 start;
PRInt32 end = -1;
PRInt32 length;
nsresult rv = NS_OK;
TimeValueSpecList& timeSpecsList = aIsBegin ? mBeginSpecs : mEndSpecs;
InstanceTimeList& instances = aIsBegin ? mBeginInstances : mEndInstances;
@ -1282,17 +1279,20 @@ nsSMILTimedElement::SetBeginOrEndSpec(const nsAString& aSpec,
AutoIntervalUpdateBatcher updateBatcher(*this);
do {
start = end + 1;
end = aSpec.FindChar(';', start);
length = (end == -1) ? -1 : end - start;
nsCharSeparatedTokenizer tokenizer(aSpec, ';');
if (!tokenizer.hasMoreTokens()) { // Empty list
return NS_ERROR_FAILURE;
}
nsresult rv = NS_OK;
while (tokenizer.hasMoreTokens() && NS_SUCCEEDED(rv)) {
nsAutoPtr<nsSMILTimeValueSpec>
spec(new nsSMILTimeValueSpec(*this, aIsBegin));
rv = spec->SetSpec(Substring(aSpec, start, length), aContextNode);
rv = spec->SetSpec(tokenizer.nextToken(), aContextNode);
if (NS_SUCCEEDED(rv)) {
timeSpecsList.AppendElement(spec.forget());
}
} while (end != -1 && NS_SUCCEEDED(rv));
}
if (NS_FAILED(rv)) {
ClearSpecs(timeSpecsList, instances, aRemove);

Просмотреть файл

@ -91,6 +91,7 @@ _TEST_FILES = \
test_smilTiming.xhtml \
test_smilTimingZeroIntervals.xhtml \
test_smilUpdatedInterval.xhtml \
test_smilValues.xhtml \
test_smilXHR.xhtml \
$(NULL)

Просмотреть файл

@ -40,6 +40,8 @@
/* Lists of valid & invalid values for the various <animateMotion> attributes */
const gValidValues = [
"10 10",
"10 10;", // Trailing semicolons are allowed
"10 10; ",
" 10 10em ",
"1 2 ; 3,4",
"1,2;3,4",
@ -49,7 +51,6 @@ const gValidValues = [
const gInvalidValues = [
";10 10",
"10 10;", // We treat semicolon-terminated value-lists as failure cases
"10 10;;",
"1 2 3",
"1 2 3 4",
@ -128,3 +129,30 @@ const gValidPathWithErrors = [
"m0 0 L30,,30",
"M10 10 L50 50 abc",
];
const gValidKeyPoints = [
"0; 0.5; 1",
"0;.5;1",
"0; 0; 1",
"0; 1; 1",
"0; 0; 1;", // Trailing semicolons are allowed
"0; 0; 1; ",
"0; 0.000; 1",
"0; 0.000001; 1",
];
const gInvalidKeyPoints = [
"0; 1",
"0; 1;",
"0",
"1",
"a",
"",
" ",
"0; -0.1; 1",
"0; 1.1; 1",
"0; 0.1; 1.1",
"-0.1; 0.1; 1",
"0; a; 1",
"0;;1",
];

Просмотреть файл

@ -64,6 +64,13 @@ function testAttr(aAttrName, aAttrValueArray, aIsValid, aIsTodo)
// our value is rejected.
anim.setAttribute("rotate", Math.PI/4);
componentsToCheck = CTMUtil.CTM_COMPONENTS_ALL;
if (aAttrName == "keyPoints") {
// Add three times so we can test a greater range of values for
// keyPoints
anim.setAttribute("values", "0 0; 25 25; 50 50");
anim.setAttribute("keyTimes", "0; 0.5; 1");
anim.setAttribute("calcMode", "discrete");
}
}
var curCTM = gRect.getCTM();
@ -158,6 +165,9 @@ function main()
testAttr("path", gInvalidPath, false, false);
testAttr("path", gValidPathWithErrors, true, false);
testAttr("keyPoints", gValidKeyPoints, true, false);
testAttr("keyPoints", gInvalidKeyPoints, false, false);
testMpathElem(gValidPath, true, false);
testMpathElem(gInvalidPath, false, false);

Просмотреть файл

@ -18,91 +18,268 @@
/** Test for SMIL timing **/
/* Global Variables */
const svgns="http://www.w3.org/2000/svg";
var svg = document.getElementById("svg");
var circle = document.getElementById('circle');
const svgns = "http://www.w3.org/2000/svg";
var gSvg = document.getElementById("svg");
var gCircle = document.getElementById('circle');
SimpleTest.waitForExplicitFinish();
function createAnim() {
function main() {
ok(gSvg.animationsPaused(), "should be paused by <svg> load handler");
is(gSvg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler");
var testCases = Array();
const secPerMin = 60;
const secPerHour = secPerMin * 60;
// In the following tests that compare start times, getStartTime will round
// the start time to three decimal places since we expect our implementation
// to be millisecond accurate.
// Offset syntax
// -- Basic tests, sign and whitespace
testCases.push(StartTimeTest('3s', 3));
testCases.push(StartTimeTest('0s', 0));
testCases.push(StartTimeTest('+2s', 2));
testCases.push(StartTimeTest('-1s\t\r', -1));
testCases.push(StartTimeTest('- 1s', -1));
testCases.push(StartTimeTest(' -1s', -1));
testCases.push(StartTimeTest(' - 1s', -1));
testCases.push(StartTimeTest(' \t\n\r-1s', -1));
testCases.push(StartTimeTest('+\n5s', 5));
testCases.push(StartTimeTest('-\n5s', -5));
testCases.push(StartTimeTest('\t 5s', 5));
// -- These tests are from SMILANIM 3.6.7
testCases.push(StartTimeTest('02:30:03', 2*secPerHour + 30*secPerMin + 3));
testCases.push(StartTimeTest('50:00:10.25', 50*secPerHour + 10.25));
testCases.push(StartTimeTest('02:33', 2*secPerMin + 33));
testCases.push(StartTimeTest('00:10.5', 10.5));
testCases.push(StartTimeTest('3.2h', 3.2*secPerHour));
testCases.push(StartTimeTest('45min', 45*secPerMin));
testCases.push(StartTimeTest('30s', 30));
testCases.push(StartTimeTest('5ms', 0.005));
testCases.push(StartTimeTest('12.467', 12.467));
testCases.push(StartTimeTest('00.5s', 0.5));
testCases.push(StartTimeTest('00:00.005', 0.005));
// -- Additional tests
testCases.push(StartTimeTest('61:59:59', 61*secPerHour + 59*secPerMin + 59));
testCases.push(StartTimeTest('02:59.999999999999999999999', 3*secPerMin));
testCases.push(StartTimeTest('1234:23:45',
1234*secPerHour + 23*secPerMin + 45));
testCases.push(StartTimeTest('61min', 61*secPerMin));
testCases.push(StartTimeTest('0:30:03', 30*secPerMin + 3));
// -- Fractional precision
testCases.push(StartTimeTest('25.4567', 25.457));
testCases.push(StartTimeTest('0.123456789', 0.123));
testCases.push(StartTimeTest('0.00000000000000000000001', 0));
testCases.push(StartTimeTest('-0.00000000000000000000001', 0));
testCases.push(StartTimeTest('0.0009', 0.001));
testCases.push(StartTimeTest('0.99999999999999999999999999999999999999', 1));
testCases.push(StartTimeTest('23.4567ms', 0.023));
testCases.push(StartTimeTest('23.7ms', 0.024));
// -- Test errors
testCases.push(StartTimeTest(' + +3s', 'none'));
testCases.push(StartTimeTest(' +-3s', 'none'));
testCases.push(StartTimeTest('1:12:12:12', 'none'));
testCases.push(StartTimeTest('4:50:60', 'none'));
testCases.push(StartTimeTest('4:60:0', 'none'));
testCases.push(StartTimeTest('4:60', 'none'));
testCases.push(StartTimeTest('4:-1:00', 'none'));
testCases.push(StartTimeTest('4 5m', 'none'));
testCases.push(StartTimeTest('4 5ms', 'none'));
testCases.push(StartTimeTest('02:3:03', 'none'));
testCases.push(StartTimeTest('45.7 s', 'none'));
testCases.push(StartTimeTest(' 3 h ', 'none'));
testCases.push(StartTimeTest('2:33 ', 'none'));
testCases.push(StartTimeTest('02:33 2', 'none'));
testCases.push(StartTimeTest('\u000B 02:33', 'none'));
testCases.push(StartTimeTest('h', 'none'));
testCases.push(StartTimeTest('23.s', 'none'));
testCases.push(StartTimeTest('23.', 'none'));
testCases.push(StartTimeTest('23.54.2s', 'none'));
testCases.push(StartTimeTest('23sec', 'none'));
testCases.push(StartTimeTest('five', 'none'));
testCases.push(StartTimeTest('', 'none'));
testCases.push(StartTimeTest('02:33s', 'none'));
testCases.push(StartTimeTest('02:33 s', 'none'));
testCases.push(StartTimeTest('2.54e6', 'none'));
testCases.push(StartTimeTest('02.5:33', 'none'));
testCases.push(StartTimeTest('2:-45:33', 'none'));
testCases.push(StartTimeTest('2:4.5:33', 'none'));
testCases.push(StartTimeTest('45m', 'none'));
testCases.push(StartTimeTest(':20:30', 'none'));
testCases.push(StartTimeTest('1.5:30', 'none'));
testCases.push(StartTimeTest('15:-30', 'none'));
testCases.push(StartTimeTest('::30', 'none'));
testCases.push(StartTimeTest('15:30s', 'none'));
testCases.push(StartTimeTest('2:1.:30', 'none'));
testCases.push(StartTimeTest('2:.1:30', 'none'));
testCases.push(StartTimeTest('2.0:15:30', 'none'));
testCases.push(StartTimeTest('2.:15:30', 'none'));
testCases.push(StartTimeTest('.2:15:30', 'none'));
testCases.push(StartTimeTest('70:15', 'none'));
testCases.push(StartTimeTest('media', 'none'));
testCases.push(StartTimeTest('5mi', 'none'));
testCases.push(StartTimeTest('5hours', 'none'));
testCases.push(StartTimeTest('h05:30', 'none'));
testCases.push(StartTimeTest('05:40\x9A', 'none'));
testCases.push(StartTimeTest('05:40\u30D5', 'none'));
testCases.push(StartTimeTest('05:40β', 'none'));
// List syntax
testCases.push(StartTimeTest('3', 3));
testCases.push(StartTimeTest('3;', 3));
testCases.push(StartTimeTest('3; ', 3));
testCases.push(StartTimeTest('3 ; ', 3));
testCases.push(StartTimeTest('3;;', 'none'));
testCases.push(StartTimeTest('3;; ', 'none'));
testCases.push(StartTimeTest(';3', 'none'));
testCases.push(StartTimeTest(' ;3', 'none'));
testCases.push(StartTimeTest('3;4', 3));
testCases.push(StartTimeTest(' 3 ; 4 ', 3));
// List syntax on end times
testCases.push({
'attr' : { 'begin': '0s',
'end': '1s; 2s' },
'times': [ [ 0, 0 ],
[ 1, -100 ] ]
});
testCases.push({
'attr' : { 'begin': '0s',
'end': '1s; 2s; ' },
'times': [ [ 0, 0 ],
[ 1, -100 ] ]
});
testCases.push({
'attr' : { 'begin': '0s',
'end': '3s; 2s' },
'times': [ [ 0, 0 ],
[ 1, 10 ],
[ 2, -100 ] ]
});
// Simple case
testCases.push({
'attr' : { 'begin': '3s' },
'times': [ [ 0, -100 ],
[ 4, 10 ] ]
});
// Multiple begins
testCases.push({
'attr' : { 'begin': '2s; 6s',
'dur': '2s' },
'times': [ [ 0, -100 ],
[ 3, 50 ],
[ 4, -100 ],
[ 7, 50 ],
[ 8, -100 ] ]
});
// Negative begins
testCases.push({
'attr' : { 'begin': '-3s; 1s ; 4s',
'dur': '2s ',
'fill': 'freeze' },
'times': [ [ 0, -100 ],
[ 0.5, -100 ],
[ 1, 0 ],
[ 2, 50 ],
[ 3, 100 ],
[ 5, 50 ] ]
});
// Sorting
testCases.push({
'attr' : { 'begin': '-3s; 110s; 1s; 4s; -5s; -10s',
'end': '111s; -5s; -15s; 6s; -5s; 1.2s',
'dur': '2s ',
'fill': 'freeze' },
'times': [ [ 0, -100 ],
[ 1, 0 ],
[ 2, 10 ],
[ 4, 0 ],
[ 5, 50 ],
[ 109, 100 ],
[ 110, 0 ],
[ 112, 50 ] ]
});
for (var i = 0; i < testCases.length; i++) {
gSvg.setCurrentTime(0);
var test = testCases[i];
// Generate string version of params for output messages
var params = "";
for (var name in test.attr) {
params += name + '="' + test.attr[name] + '" ';
}
params = params.trim();
// Create animation elements
var anim = createAnim(test.attr);
// Run samples
if ('times' in test) {
for (var j = 0; j < test.times.length; j++) {
var curSample = test.times[j];
checkSample(curSample[0], curSample[1], params);
}
}
// Check start time
if ('startTime' in test) {
is(getStartTime(anim), test.startTime,
"Got unexpected start time for " + params);
}
anim.parentNode.removeChild(anim);
}
SimpleTest.finish();
}
function createAnim(attr) {
var anim = document.createElementNS(svgns,'animate');
anim.setAttribute('attributeName','cx');
anim.setAttribute('from','0');
anim.setAttribute('to','100');
anim.setAttribute('dur','10s');
anim.setAttribute('begin','indefinite');
return circle.appendChild(anim);
}
function removeAnim(anim) {
anim.parentNode.removeChild(anim);
}
function main() {
ok(svg.animationsPaused(), "should be paused by <svg> load handler");
is(svg.getCurrentTime(), 0, "should be paused at 0 in <svg> load handler");
var tests =
[ testOffsetStartup,
testMultipleBegins,
testNegativeBegins,
testSorting
];
for (var i = 0; i < tests.length; i++) {
var anim = createAnim();
svg.setCurrentTime(0);
tests[i](anim);
removeAnim(anim);
for (name in attr) {
anim.setAttribute(name, attr[name]);
}
SimpleTest.finish();
return gCircle.appendChild(anim);
}
function checkSample(time, expectedValue) {
svg.setCurrentTime(time);
is(circle.cx.animVal.value, expectedValue);
function checkSample(time, expectedValue, params) {
gSvg.setCurrentTime(time);
var msg = "Unexpected sample value for " + params +
" at t=" + time + ": ";
is(gCircle.cx.animVal.value, expectedValue);
}
function testOffsetStartup(anim) {
anim.setAttribute('begin', '3s');
checkSample(0,-100);
checkSample(4,10);
function getStartTime(anim) {
var startTime;
try {
startTime = anim.getStartTime();
// We round start times to 3 decimal places to make comparisons simpler
startTime = parseFloat(startTime.toFixed(3));
} catch(e) {
if (e.code == DOMException.INVALID_STATE_ERR) {
startTime = 'none';
} else {
ok(false, "Unexpected exception: " + e);
}
}
return startTime;
}
function testMultipleBegins(anim) {
anim.setAttribute('begin', '2s; 6s');
anim.setAttribute('dur', ' 2s');
checkSample(0,-100);
checkSample(3,50);
checkSample(4,-100);
checkSample(7,50);
checkSample(8,-100);
}
function testNegativeBegins(anim) {
anim.setAttribute('begin', '-3s; 1s ; 4s');
anim.setAttribute('dur', '2s ');
anim.setAttribute('fill', 'freeze');
checkSample(0,-100);
checkSample(0.5,-100);
checkSample(1,0);
checkSample(2,50);
checkSample(3,100);
checkSample(5,50);
}
function testSorting(anim) {
anim.setAttribute('begin', '-3s; 110s; 1s; 4s; -5s; -10s');
anim.setAttribute('end', '111s; -5s; -15s; 6s; -5s; 1.2s');
anim.setAttribute('dur', '2s ');
anim.setAttribute('fill', 'freeze');
checkSample(0,-100);
checkSample(1,0);
checkSample(2,10);
checkSample(4,0);
checkSample(5,50);
checkSample(109,100);
checkSample(110,0);
checkSample(112,50);
function StartTimeTest(beginSpec, expectedStartTime) {
return { 'attr' : { 'begin': beginSpec },
'startTime': expectedStartTime };
}
window.addEventListener("load", main, false);

Просмотреть файл

@ -0,0 +1,170 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Test for SMIL values</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=557885">Mozilla Bug
474742</a>
<p id="display"></p>
<div id="content" style="display: none">
<svg id="svg" xmlns="http://www.w3.org/2000/svg" width="120px" height="120px">
<circle cx="-100" cy="20" r="15" fill="blue" id="circle"/>
</svg>
</div>
<pre id="test">
<script class="testbody" type="text/javascript">
<![CDATA[
/** Test for SMIL values **/
var gSvg = document.getElementById("svg");
SimpleTest.waitForExplicitFinish();
function main()
{
gSvg.pauseAnimations();
var testCases = Array();
// Single value
testCases.push({
'attr' : { 'values': 'a' },
'times': [ [ 0, 'a' ] ]
});
// The parsing below is based on the following discussion:
//
// http://lists.w3.org/Archives/Public/www-svg/2011Nov/0136.html
//
// In summary:
// * Values lists are semi-colon delimited and semi-colon terminated.
// * However, if there are extra non-whitespace characters after the final
// semi-colon then there's an implied semi-colon at the end.
//
// This differs to what is specified in SVG 1.1 but is consistent with the
// majority of browsers and with existing content (particularly that generated
// by Ikivo Animator).
// Trailing semi-colon
testCases.push({
'attr' : { 'values': 'a;' },
'times': [ [ 0, 'a' ], [ 10, 'a' ] ]
});
// Trailing semi-colon + whitespace
testCases.push({
'attr' : { 'values': 'a; ' },
'times': [ [ 0, 'a' ], [ 10, 'a' ] ]
});
// Whitespace + trailing semi-colon
testCases.push({
'attr' : { 'values': 'a ;' },
'times': [ [ 0, 'a' ], [ 10, 'a' ] ]
});
// Empty at end
testCases.push({
'attr' : { 'values': 'a;;' },
'times': [ [ 0, 'a' ], [ 5, '' ], [ 10, '' ] ]
});
// Empty at end + whitespace
testCases.push({
'attr' : { 'values': 'a;; ' },
'times': [ [ 0, 'a' ], [ 4, 'a' ], [ 5, '' ], [ 10, '' ] ]
});
// Empty in middle
testCases.push({
'attr' : { 'values': 'a;;b' },
'times': [ [ 0, 'a' ], [ 5, '' ], [ 10, 'b' ] ]
});
// Empty in middle + trailing semi-colon
testCases.push({
'attr' : { 'values': 'a;;b;' },
'times': [ [ 0, 'a' ], [ 5, '' ], [ 10, 'b' ] ]
});
// Whitespace in middle
testCases.push({
'attr' : { 'values': 'a; ;b' },
'times': [ [ 0, 'a' ], [ 5, '' ], [ 10, 'b' ] ]
});
// Empty at start
testCases.push({
'attr' : { 'values': ';a' },
'times': [ [ 0, '' ], [ 5, 'a' ], [ 10, 'a' ] ]
});
// Whitespace at start
testCases.push({
'attr' : { 'values': ' ;a' },
'times': [ [ 0, '' ], [ 5, 'a' ], [ 10, 'a' ] ]
});
// Embedded whitespace
testCases.push({
'attr' : { 'values': ' a b ; c d ' },
'times': [ [ 0, 'a b' ], [ 5, 'c d' ], [ 10, 'c d' ] ]
});
// Whitespace only
testCases.push({
'attr' : { 'values': ' ' },
'times': [ [ 0, '' ], [ 10, '' ] ]
});
for (var i = 0; i < testCases.length; i++) {
gSvg.setCurrentTime(0);
var test = testCases[i];
// Create animation elements
var anim = createAnim(test.attr);
// Run samples
for (var j = 0; j < test.times.length; j++) {
var curSample = test.times[j];
gSvg.setCurrentTime(curSample[0]);
checkSample(anim, curSample[1], curSample[0], i);
}
anim.parentNode.removeChild(anim);
}
SimpleTest.finish();
}
function createAnim(attr)
{
const svgns = "http://www.w3.org/2000/svg";
var anim = document.createElementNS(svgns, 'animate');
anim.setAttribute('attributeName','class');
anim.setAttribute('dur','10s');
anim.setAttribute('begin','0s');
anim.setAttribute('fill','freeze');
for (name in attr) {
anim.setAttribute(name, attr[name]);
}
return document.getElementById('circle').appendChild(anim);
}
function checkSample(anim, expectedValue, sampleTime, caseNum)
{
var msg = "Test case " + caseNum +
" (values: '" + anim.getAttribute('values') + "')," +
"t=" + sampleTime +
": Unexpected sample value:";
is(anim.targetElement.className.animVal, expectedValue, msg);
}
window.addEventListener("load", main, false);
]]>
</script>
</pre>
</body>
</html>

Просмотреть файл

@ -447,7 +447,7 @@ SVGMotionSMILAnimationFunction::SetKeyPoints(const nsAString& aKeyPoints,
void
SVGMotionSMILAnimationFunction::UnsetKeyPoints()
{
mKeyTimes.Clear();
mKeyPoints.Clear();
SetKeyPointsErrorFlag(false);
mHasChanged = true;
}

Просмотреть файл

@ -2343,8 +2343,17 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
return NS_OK;
nsCOMPtr<nsIContent> startContent = aStartContent;
if (!startContent && aType != MOVEFOCUS_CARET)
startContent = aWindow->GetFocusedNode();
if (!startContent && aType != MOVEFOCUS_CARET) {
if (aType == MOVEFOCUS_FORWARDDOC || aType == MOVEFOCUS_BACKWARDDOC) {
// When moving between documents, make sure to get the right
// starting content in a descendant.
nsCOMPtr<nsPIDOMWindow> focusedWindow;
startContent = GetFocusedDescendant(aWindow, true, getter_AddRefs(focusedWindow));
}
else {
startContent = aWindow->GetFocusedNode();
}
}
nsCOMPtr<nsIDocument> doc;
if (startContent)
@ -2362,11 +2371,11 @@ nsFocusManager::DetermineElementToMoveFocus(nsPIDOMWindow* aWindow,
return NS_OK;
}
if (aType == MOVEFOCUS_FORWARDDOC) {
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(true));
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, true));
return NS_OK;
}
if (aType == MOVEFOCUS_BACKWARDDOC) {
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(false));
NS_IF_ADDREF(*aNextContent = GetNextTabbableDocument(startContent, false));
return NS_OK;
}
@ -3136,66 +3145,192 @@ nsFocusManager::GetPreviousDocShell(nsIDocShellTreeItem* aItem,
}
nsIContent*
nsFocusManager::GetNextTabbableDocument(bool aForward)
nsFocusManager::GetNextTabbablePanel(nsIDocument* aDocument, nsIFrame* aCurrentPopup, bool aForward)
{
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
if (!pm)
return nsnull;
// Iterate through the array backwards if aForward is false.
nsTArray<nsIFrame *> popups = pm->GetVisiblePopups();
PRInt32 i = aForward ? 0 : popups.Length() - 1;
PRInt32 end = aForward ? popups.Length() : -1;
for (; i != end; aForward ? i++ : i--) {
nsIFrame* popupFrame = popups[i];
if (aCurrentPopup) {
// If the current popup is set, then we need to skip over this popup and
// wait until the currently focused popup is found. Once found, the
// current popup will be cleared so that the next popup is used.
if (aCurrentPopup == popupFrame)
aCurrentPopup = nsnull;
continue;
}
// Skip over non-panels
if (popupFrame->GetContent()->Tag() != nsGkAtoms::panel ||
(aDocument && popupFrame->GetContent()->GetCurrentDoc() != aDocument)) {
continue;
}
// Find the first focusable content within the popup. If there isn't any
// focusable content in the popup, skip to the next popup.
nsIPresShell* presShell = popupFrame->PresContext()->GetPresShell();
if (presShell) {
nsCOMPtr<nsIContent> nextFocus;
nsIContent* popup = popupFrame->GetContent();
nsresult rv = GetNextTabbableContent(presShell, popup,
nsnull, popup,
true, 1, false,
getter_AddRefs(nextFocus));
if (NS_SUCCEEDED(rv) && nextFocus) {
return nextFocus.get();
}
}
}
return nsnull;
}
nsIContent*
nsFocusManager::GetNextTabbableDocument(nsIContent* aStartContent, bool aForward)
{
// If currentPopup is set, then the starting content is in a panel.
nsIFrame* currentPopup = nsnull;
nsCOMPtr<nsIDocument> doc;
nsCOMPtr<nsIDocShellTreeItem> startItem;
if (mFocusedWindow) {
if (aStartContent) {
doc = aStartContent->GetCurrentDoc();
if (doc) {
startItem = do_QueryInterface(doc->GetWindow()->GetDocShell());
}
// Check if the starting content is inside a panel. Document navigation
// must start from this panel instead of the document root.
nsIContent* content = aStartContent;
while (content) {
if (content->NodeInfo()->Equals(nsGkAtoms::panel, kNameSpaceID_XUL)) {
currentPopup = content->GetPrimaryFrame();
break;
}
content = content->GetParent();
}
}
else if (mFocusedWindow) {
startItem = do_QueryInterface(mFocusedWindow->GetDocShell());
doc = do_QueryInterface(mFocusedWindow->GetExtantDocument());
}
else {
nsCOMPtr<nsIWebNavigation> webnav = do_GetInterface(mActiveWindow);
startItem = do_QueryInterface(webnav);
if (mActiveWindow) {
doc = do_QueryInterface(mActiveWindow->GetExtantDocument());
}
}
if (!startItem)
return nsnull;
// perform a depth first search (preorder) of the docshell tree
// looking for an HTML Frame or a chrome document
nsIContent* content = nsnull;
nsIContent* content = aStartContent;
nsCOMPtr<nsIDocShellTreeItem> curItem = startItem;
nsCOMPtr<nsIDocShellTreeItem> nextItem;
do {
if (aForward) {
GetNextDocShell(curItem, getter_AddRefs(nextItem));
if (!nextItem) {
// wrap around to the beginning, which is the top of the tree
startItem->GetRootTreeItem(getter_AddRefs(nextItem));
}
}
else {
GetPreviousDocShell(curItem, getter_AddRefs(nextItem));
if (!nextItem) {
// wrap around to the end, which is the last item in the tree
nsCOMPtr<nsIDocShellTreeItem> rootItem;
startItem->GetRootTreeItem(getter_AddRefs(rootItem));
GetLastDocShell(rootItem, getter_AddRefs(nextItem));
// If moving forward, check for a panel in the starting document. If one
// exists with focusable content, return that content instead of the next
// document. If currentPopup is set, then, another panel may exist. If no
// such panel exists, then continue on to check the next document.
// When moving backwards, and the starting content is in a panel, then
// check for additional panels in the starting document. If the starting
// content is not in a panel, move back to the previous document and check
// for panels there.
bool checkPopups = false;
nsCOMPtr<nsPIDOMWindow> nextFrame = nsnull;
if (doc && (aForward || currentPopup)) {
nsIContent* popupContent = GetNextTabbablePanel(doc, currentPopup, aForward);
if (popupContent)
return popupContent;
if (!aForward && currentPopup) {
// The starting content was in a popup, yet no other popups were
// found. Move onto the starting content's document.
nextFrame = doc->GetWindow();
}
}
curItem = nextItem;
nsCOMPtr<nsPIDOMWindow> nextFrame = do_GetInterface(nextItem);
// Look for the next or previous document.
if (!nextFrame) {
if (aForward) {
GetNextDocShell(curItem, getter_AddRefs(nextItem));
if (!nextItem) {
// wrap around to the beginning, which is the top of the tree
startItem->GetRootTreeItem(getter_AddRefs(nextItem));
}
}
else {
GetPreviousDocShell(curItem, getter_AddRefs(nextItem));
if (!nextItem) {
// wrap around to the end, which is the last item in the tree
nsCOMPtr<nsIDocShellTreeItem> rootItem;
startItem->GetRootTreeItem(getter_AddRefs(rootItem));
GetLastDocShell(rootItem, getter_AddRefs(nextItem));
}
// When going back to the previous document, check for any focusable
// popups in that previous document first.
checkPopups = true;
}
curItem = nextItem;
nextFrame = do_GetInterface(nextItem);
}
if (!nextFrame)
return nsnull;
nsCOMPtr<nsIDocument> doc = do_QueryInterface(nextFrame->GetExtantDocument());
if (doc && !doc->EventHandlingSuppressed()) {
content = GetRootForFocus(nextFrame, doc, true, true);
if (content && !GetRootForFocus(nextFrame, doc, false, false)) {
// if the found content is in a chrome shell or a frameset, navigate
// forward one tabbable item so that the first item is focused. Note
// that we always go forward and not back here.
nsCOMPtr<nsIContent> nextFocus;
Element* rootElement = doc->GetRootElement();
nsIPresShell* presShell = doc->GetShell();
if (presShell) {
nsresult rv = GetNextTabbableContent(presShell, rootElement,
nsnull, rootElement,
true, 1, false,
getter_AddRefs(nextFocus));
return NS_SUCCEEDED(rv) ? nextFocus.get() : nsnull;
}
// Clear currentPopup for the next iteration
currentPopup = nsnull;
// If event handling is suppressed, move on to the next document. Set
// content to null so that the popup check will be skipped on the next
// loop iteration.
doc = do_QueryInterface(nextFrame->GetExtantDocument());
if (!doc || doc->EventHandlingSuppressed()) {
content = nsnull;
continue;
}
if (checkPopups) {
// When iterating backwards, check the panels of the previous document
// first. If a panel exists that has focusable content, focus that.
// Otherwise, continue on to focus the document.
nsIContent* popupContent = GetNextTabbablePanel(doc, nsnull, false);
if (popupContent)
return popupContent;
}
content = GetRootForFocus(nextFrame, doc, true, true);
if (content && !GetRootForFocus(nextFrame, doc, false, false)) {
// if the found content is in a chrome shell or a frameset, navigate
// forward one tabbable item so that the first item is focused. Note
// that we always go forward and not back here.
nsCOMPtr<nsIContent> nextFocus;
Element* rootElement = doc->GetRootElement();
nsIPresShell* presShell = doc->GetShell();
if (presShell) {
nsresult rv = GetNextTabbableContent(presShell, rootElement,
nsnull, rootElement,
true, 1, false,
getter_AddRefs(nextFocus));
return NS_SUCCEEDED(rv) ? nextFocus.get() : nsnull;
}
}
} while (!content);
return content;

Просмотреть файл

@ -459,16 +459,31 @@ protected:
nsIDocShellTreeItem** aResult);
/**
* Get the tabbable next document from the currently focused frame if
* aForward is true, or the previously tabbable document if aForward is
* false. If this document is a chrome or frameset document, returns
* the first focusable element within this document, otherwise, returns
* the root node of the document.
* Determine the first panel with focusable content in document tab order
* from the given document. aForward indicates the direction to scan. If
* aCurrentPopup is set to a panel, the next or previous popup after
* aCurrentPopup after it is used. If aCurrentPopup is null, then the first
* or last popup is used. If a panel has no focusable content, it is skipped.
* Null is returned if no panel is open or no open panel contains a focusable
* element.
*/
nsIContent* GetNextTabbablePanel(nsIDocument* aDocument, nsIFrame* aCurrentPopup, bool aForward);
/**
* Get the tabbable next document from aStartContent or, if null, the
* currently focused frame if aForward is true, or the previously tabbable
* document if aForward is false. If this document is a chrome or frameset
* document, returns the first focusable element within this document,
* otherwise, returns the root node of the document.
*
*
* Panels with focusable content are also placed in the cycling order, just
* after the document containing that panel.
*
* This method would be used for document navigation, which is typically
* invoked by pressing F6.
*/
nsIContent* GetNextTabbableDocument(bool aForward);
nsIContent* GetNextTabbableDocument(nsIContent* aStartContent, bool aForward);
/**
* Retreives a focusable element within the current selection of aWindow.

Просмотреть файл

@ -3275,8 +3275,9 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
if (!sCleanupSinceLastGC && aExtraForgetSkippableCalls >= 0) {
nsCycleCollector_forgetSkippable();
}
PRUint32 collected = nsCycleCollector_collect(aListener);
sCCollectedWaitingForGC += collected;
nsCycleCollectorResults ccResults;
nsCycleCollector_collect(&ccResults, aListener);
sCCollectedWaitingForGC += ccResults.mFreedRefCounted + ccResults.mFreedGCed;
// If we collected a substantial amount of cycles, poke the GC since more objects
// might be unreachable now.
@ -3303,16 +3304,23 @@ nsJSContext::CycleCollectNow(nsICycleCollectorListener *aListener,
sFirstCollectionTime = now;
}
nsString gcmsg;
if (ccResults.mForcedGC) {
gcmsg.AssignLiteral(", forced a GC");
}
NS_NAMED_MULTILINE_LITERAL_STRING(kFmt,
NS_LL("CC(T+%.1f) collected: %lu (%lu waiting for GC), suspected: %lu, duration: %llu ms.\n")
NS_LL("CC(T+%.1f) duration: %llums, suspected: %lu, visited: %lu RCed and %lu GCed, collected: %lu RCed and %lu GCed (%lu waiting for GC)%s\n")
NS_LL("ForgetSkippable %lu times before CC, min: %lu ms, max: %lu ms, avg: %lu ms, total: %lu ms, removed: %lu"));
nsString msg;
PRUint32 cleanups = sForgetSkippableBeforeCC ? sForgetSkippableBeforeCC : 1;
sMinForgetSkippableTime = (sMinForgetSkippableTime == PR_UINT32_MAX)
? 0 : sMinForgetSkippableTime;
msg.Adopt(nsTextFormatter::smprintf(kFmt.get(), double(delta) / PR_USEC_PER_SEC,
collected, sCCollectedWaitingForGC, suspected,
(now - start) / PR_USEC_PER_MSEC,
(now - start) / PR_USEC_PER_MSEC, suspected,
ccResults.mVisitedRefCounted, ccResults.mVisitedGCed,
ccResults.mFreedRefCounted, ccResults.mFreedGCed,
sCCollectedWaitingForGC, gcmsg.get(),
sForgetSkippableBeforeCC,
sMinForgetSkippableTime / PR_USEC_PER_MSEC,
sMaxForgetSkippableTime / PR_USEC_PER_MSEC,

Просмотреть файл

@ -45,6 +45,7 @@
#include "nsIInterfaceRequestorUtils.h"
#include "nsIDocShellTreeItem.h"
#include "nsLayoutUtils.h"
#include "nsContentUtils.h"
#include "mozilla/Preferences.h"
using namespace mozilla;
@ -262,9 +263,7 @@ nsScreen::GetAvailRect(nsRect& aRect)
namespace {
bool
IsChromeType(nsIDocShell *aDocShell)
{
bool IsWhiteListed(nsIDocShell *aDocShell) {
nsCOMPtr<nsIDocShellTreeItem> ds = do_QueryInterface(aDocShell);
if (!ds) {
return false;
@ -272,7 +271,21 @@ IsChromeType(nsIDocShell *aDocShell)
PRInt32 itemType;
ds->GetItemType(&itemType);
return itemType == nsIDocShellTreeItem::typeChrome;
if (itemType == nsIDocShellTreeItem::typeChrome) {
return true;
}
nsCOMPtr<nsIDocument> doc = do_GetInterface(aDocShell);
nsIPrincipal *principal = doc->NodePrincipal();
nsCOMPtr<nsIURI> principalURI;
principal->GetURI(getter_AddRefs(principalURI));
if (nsContentUtils::URIIsChromeOrInPref(principalURI,
"dom.mozScreenWhitelist")) {
return true;
}
return false;
}
} // anonymous namespace
@ -280,7 +293,7 @@ IsChromeType(nsIDocShell *aDocShell)
nsresult
nsScreen::GetMozEnabled(bool *aEnabled)
{
if (!sAllowScreenEnabledProperty || !IsChromeType(mDocShell)) {
if (!sAllowScreenEnabledProperty || !IsWhiteListed(mDocShell)) {
*aEnabled = true;
return NS_OK;
}
@ -292,7 +305,7 @@ nsScreen::GetMozEnabled(bool *aEnabled)
nsresult
nsScreen::SetMozEnabled(bool aEnabled)
{
if (!sAllowScreenEnabledProperty || !IsChromeType(mDocShell)) {
if (!sAllowScreenEnabledProperty || !IsWhiteListed(mDocShell)) {
return NS_OK;
}
@ -305,7 +318,7 @@ nsScreen::SetMozEnabled(bool aEnabled)
nsresult
nsScreen::GetMozBrightness(double *aBrightness)
{
if (!sAllowScreenBrightnessProperty || !IsChromeType(mDocShell)) {
if (!sAllowScreenEnabledProperty || !IsWhiteListed(mDocShell)) {
*aBrightness = 1;
return NS_OK;
}
@ -317,7 +330,7 @@ nsScreen::GetMozBrightness(double *aBrightness)
nsresult
nsScreen::SetMozBrightness(double aBrightness)
{
if (!sAllowScreenBrightnessProperty || !IsChromeType(mDocShell)) {
if (!sAllowScreenEnabledProperty || !IsWhiteListed(mDocShell)) {
return NS_OK;
}

Просмотреть файл

@ -74,6 +74,8 @@ _TEST_FILES = \
test_moving_xhr.xul \
test_nodesFromRect.html \
489127.html \
test_focus_docnav.xul \
window_focus_docnav.xul \
$(NULL)
ifeq (WINNT,$(OS_ARCH))

Просмотреть файл

@ -3,7 +3,8 @@
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script>
SimpleTest.waitForFocus(function () opener.framesetWindowLoaded(window));
if (opener)
SimpleTest.waitForFocus(function () opener.framesetWindowLoaded(window));
</script>
<frameset rows="30%, 70%">

Просмотреть файл

@ -0,0 +1,28 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
<window onload="runTest();"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
<script>
SimpleTest.waitForExplicitFinish();
function runTest()
{
window.open("window_focus_docnav.xul", "_blank", "chrome,width=600,height=550");
}
</script>
<body xmlns="http://www.w3.org/1999/xhtml">
<p id="display">
</p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
</window>

Просмотреть файл

@ -0,0 +1,104 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<window onload="start()"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<textbox id="textbox"/>
<panel id="panel2" onpopupshown="runTests(this, 2);" onpopuphidden="document.getElementById('panel').hidePopup()">
<textbox id="p2textbox" value="Popup2"/>
</panel>
<panel id="panel" onpopupshown="runTests(this, 1);"
onpopuphidden="done()">
<textbox id="p1textbox" value="Popup1"/>
</panel>
<browser id="browser" type="content" src="focus_frameset.html" width="500" height="400"/>
<script type="application/javascript">
<![CDATA[
var fm = Components.classes["@mozilla.org/focus-manager;1"].
getService(Components.interfaces.nsIFocusManager);
function is(l, r, n) { window.opener.wrappedJSObject.SimpleTest.is(l,r,n); }
function ok(v, n) { window.opener.wrappedJSObject.SimpleTest.ok(v,n); }
function done()
{
var opener = window.opener;
window.close();
opener.wrappedJSObject.SimpleTest.finish();
}
function previous(expectedWindow, expectedElement, desc)
{
synthesizeKey("VK_F6", { shiftKey: true });
is(fm.focusedWindow, expectedWindow, desc);
is(fm.focusedElement, expectedElement, desc + " element");
}
function next(expectedWindow, expectedElement, desc)
{
synthesizeKey("VK_F6", { });
is(fm.focusedWindow, expectedWindow, desc);
is(fm.focusedElement, expectedElement, desc + " element" + "::" + (fm.focusedElement ? fm.focusedElement.parentNode.id : "<none>"));
}
// This test runs through three cases. Document navigation forward and
// backward using the F6 key when no popups are open, with one popup open and
// with two popups open.
function runTests(panel, popupCount)
{
if (!popupCount || popupCount > 2)
popupCount = 0;
fm.clearFocus(window);
var childwin = document.getElementById("browser").contentWindow;
if (popupCount) {
if (popupCount == 2) {
next(window, document.getElementById("p2textbox").inputField, "First into popup 2 with " + popupCount);
}
next(window, document.getElementById("p1textbox").inputField, "First into popup 1 with " + popupCount);
}
next(childwin.frames[0], childwin.frames[0].document.documentElement, "First with " + popupCount);
next(childwin.frames[1], childwin.frames[1].document.documentElement, "Second with " + popupCount);
previous(childwin.frames[0], childwin.frames[0].document.documentElement, "Second back with " + popupCount);
if (popupCount) {
previous(window, document.getElementById("p1textbox").inputField, "First back from popup 1 with " + popupCount);
if (popupCount == 2) {
previous(window, document.getElementById("p2textbox").inputField, "First back from popup 2 with " + popupCount);
}
}
previous(window, document.getElementById("textbox").inputField, "First back with " + popupCount);
if (panel == document.getElementById("panel"))
document.getElementById("panel2").openPopup(null, "after_start", 100, 20);
else if (panel == document.getElementById("panel2"))
panel.hidePopup();
else
document.getElementById("panel").openPopup(null, "after_start");
}
function start()
{
window.opener.wrappedJSObject.SimpleTest.waitForExplicitFinish();
window.opener.wrappedJSObject.SimpleTest.waitForFocus(
function() { runTests(null, 0); },
document.getElementById("browser").contentWindow);
}
]]></script>
</window>

Просмотреть файл

@ -117,6 +117,12 @@ function doTest()
// synthesizeDrop(text, textarea, [[{type: "text/plain", data: "Somewhat Longer Text"}]], "copy");
// is(textarea.value, "Somewhat Longer Text", "Drag text/plain onto textarea");
// -------- Test dragging special text type of text/plain to contenteditable
selection.selectAllChildren(text);
synthesizeDrop(text, input, [[{type: "text/x-moz-text-internal", data: "Some Special Text"}]], "copy");
is(input.value, "Some Plain Text", "Drag text/x-moz-text-internal onto input");
// -------- Test dragging regular text of text/plain to contenteditable
selection.selectAllChildren(text);

Просмотреть файл

@ -169,12 +169,16 @@ nsresult nsPlaintextEditor::InsertFromDataTransfer(nsIDOMDataTransfer *aDataTran
nsCOMPtr<nsIVariant> data;
aDataTransfer->MozGetDataAt(NS_LITERAL_STRING("text/plain"), aIndex,
getter_AddRefs(data));
nsAutoString insertText;
data->GetAsAString(insertText);
nsContentUtils::PlatformToDOMLineBreaks(insertText);
if (data) {
nsAutoString insertText;
data->GetAsAString(insertText);
nsContentUtils::PlatformToDOMLineBreaks(insertText);
nsAutoEditBatch beginBatching(this);
return InsertTextAt(insertText, aDestinationNode, aDestOffset, aDoDeleteSelection);
nsAutoEditBatch beginBatching(this);
return InsertTextAt(insertText, aDestinationNode, aDestOffset, aDoDeleteSelection);
}
return NS_OK;
}
nsresult nsPlaintextEditor::InsertFromDrop(nsIDOMEvent* aDropEvent)

Просмотреть файл

@ -255,11 +255,11 @@ CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
*/
RegExpFlag flags;
{
RegExpShared *shared = RegExpToShared(cx, sourceObj);
if (!shared)
RegExpGuard g;
if (!RegExpToShared(cx, sourceObj, &g))
return false;
flags = shared->getFlags();
flags = g->getFlags();
}
/*
@ -542,18 +542,18 @@ StartsWithGreedyStar(JSAtom *source)
#endif
}
static inline RegExpShared *
GetSharedForGreedyStar(JSContext *cx, JSAtom *source, RegExpFlag flags)
static inline bool
GetSharedForGreedyStar(JSContext *cx, JSAtom *source, RegExpFlag flags, RegExpGuard *g)
{
if (RegExpShared *hit = cx->compartment->regExps.lookupHack(cx, source, flags))
return hit;
if (cx->compartment->regExps.lookupHack(source, flags, cx, g))
return true;
JSAtom *hackedSource = js_AtomizeChars(cx, source->chars() + ArrayLength(GreedyStarChars),
source->length() - ArrayLength(GreedyStarChars));
if (!hackedSource)
return NULL;
return false;
return cx->compartment->regExps.getHack(cx, source, hackedSource, flags);
return cx->compartment->regExps.getHack(cx, source, hackedSource, flags, g);
}
/*
@ -575,16 +575,15 @@ ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
RegExpObject &reobj = obj->asRegExp();
RegExpShared *shared;
if (StartsWithGreedyStar(reobj.getSource()))
shared = GetSharedForGreedyStar(cx, reobj.getSource(), reobj.getFlags());
else
shared = reobj.getShared(cx);
RegExpGuard re;
if (StartsWithGreedyStar(reobj.getSource())) {
if (!GetSharedForGreedyStar(cx, reobj.getSource(), reobj.getFlags(), &re))
return false;
} else {
if (!reobj.getShared(cx, &re))
return false;
}
if (!shared)
return false;
RegExpShared::Guard re(*shared);
RegExpStatics *res = cx->regExpStatics();
/* Step 2. */

Просмотреть файл

@ -792,8 +792,10 @@ EXPAND_LD = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-
EXPAND_MKSHLIB = $(EXPAND_LIBS_EXEC) --uselist $(if $(REORDER),--reorder $(REORDER))-- $(MKSHLIB)
ifdef STDCXX_COMPAT
ifneq ($(OS_ARCH),Darwin)
CHECK_STDCXX = objdump -p $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' > /dev/null && echo "TEST-UNEXPECTED-FAIL | | We don't want these libstdc++ symbols to be used:" && objdump -T $(1) | grep -e 'GLIBCXX_3\.4\.\(9\|[1-9][0-9]\)' && exit 1 || exit 0
endif
endif
# autoconf.mk sets OBJ_SUFFIX to an error to avoid use before including
# this file

Просмотреть файл

@ -155,7 +155,7 @@ js::IsIdentifier(JSLinearString *str)
/* Initialize members that aren't initialized in |init|. */
TokenStream::TokenStream(JSContext *cx, JSPrincipals *prin, JSPrincipals *originPrin)
: tokens(), cursor(), lookahead(), flags(), listenerTSData(), tokenbuf(cx),
cx(cx), originPrincipals(originPrin ? originPrin : prin)
cx(cx), originPrincipals(JSScript::normalizeOriginPrincipals(prin, originPrin))
{
if (originPrincipals)
JSPRINCIPALS_HOLD(cx, originPrincipals);

Просмотреть файл

@ -0,0 +1,65 @@
var expected = '';
function TestCase(n, d, e, a) {}
function reportFailure (msg) {}
function toPrinted(value) {
value = value.replace(/\\\n/g, 'NL')
.replace(/[^\x20-\x7E]+/g, escapeString);
}
function escapeString (str) {}
function reportCompare (expected, actual, description) {
var expected_t = typeof expected;
var actual_t = typeof actual;
var output = "";
var testcase = new TestCase("unknown-test-name", description, expected, actual);
reportFailure (description + " : " + output);
}
function enterFunc (funcName)
callStack.push(funcName);
try {
reportCompare(expectCompile, actualCompile,
summary + ': compile actual');
} catch(ex) { }
var lfcode = new Array();
lfcode.push("gczeal(2);\
enterFunc ('test');\
");
lfcode.push("{}");
lfcode.push("noexist.js");
lfcode.push("var BUGNUMBER = 305064;\
var summary = 'Tests the trim, trimRight and trimLeft methods';\
var trimMethods = ['trim', 'trimLeft', 'trimRight'];\
var whitespace = [\
{s : '\\u0009', t : 'HORIZONTAL TAB'},\
{s : '\\u200A', t : 'HAIR SPACE'},\
];\
for (var j = 0; j < trimMethods.length; ++j)\
var method = trimMethods[j];\
reportCompare(true, true, 'Test skipped. String.prototype.' + method + ' is not supported');\
str = '';\
for (var i = 0; i < whitespace.length; ++i) {\
var v = whitespace[i].s;\
var t = (summary)[i].t;\
v = v + v + v;\
print('Test ' + method + ' with with leading whitespace. : ' + t);\
actual = str[method]();\
reportCompare(expected, actual, t + ':' + '\"' + toPrinted(str) + '\".' + method + '()');\
str = v + 'a' + v;\
}\
");
while (true) {
var file = lfcode.shift(); if (file == undefined) { break; }
if (file == "evaluate") {
} else {
loadFile(file);
}
}
function loadFile(lfVarx) {
try {
if (lfVarx.substr(-3) == ".js") {
switch (lfRunTypeId) {
}
} else {
eval(lfVarx);
}
} catch (lfVare) { }
}

Просмотреть файл

@ -0,0 +1,23 @@
var lfcode = new Array();
lfcode.push("\
test();\
function test() {\
function removeAllProperties(o) {\
bar() = thaw, patterns;\
}\
var o = {};\
o.first = { toSource: function() { removeAllProperties(o); } };\
return o.toSource();\
}\
");
lfcode.push("test();");
gczeal(4);
while (true) {
var file = lfcode.shift(); if (file == undefined) { break; }
loadFile(file);
}
function loadFile(lfVarx) {
try {
evaluate(lfVarx);
} catch (lfVare) { }
}

Просмотреть файл

@ -0,0 +1,20 @@
function f() {
try {} catch (e) {}
}
function g(code) {
function m() {
return "(function(){return " + code + "})()"
}
var codeNestedDeep = m(codeNestedDeep)
h(m(code), "same-compartment")
h(codeNestedDeep, "same-compartment")
}
function h(code, globalType) {
try {
evalcx(code, newGlobal(globalType))
} catch (e) {
"" + f()
}
}
function p()(function() function() {})
g("print(let(x=verifybarriers(),q)((x(\"\",l('')))?(\"\"):(\"\")))()")

Просмотреть файл

@ -0,0 +1,2 @@
var r = /a/;
r.compile(r);

Просмотреть файл

@ -1218,38 +1218,39 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
* Current value of each variable and stack value. Empty for missing or
* untracked entries, i.e. escaping locals and arguments.
*/
SSAValue *values = (SSAValue *)
cx->calloc_((numSlots + maxDepth) * sizeof(SSAValue));
SSAValueInfo *values = (SSAValueInfo *)
cx->calloc_((numSlots + maxDepth) * sizeof(SSAValueInfo));
if (!values) {
setOOM(cx);
return;
}
struct FreeSSAValues {
JSContext *cx;
SSAValue *values;
FreeSSAValues(JSContext *cx, SSAValue *values) : cx(cx), values(values) {}
SSAValueInfo *values;
FreeSSAValues(JSContext *cx, SSAValueInfo *values) : cx(cx), values(values) {}
~FreeSSAValues() { cx->free_(values); }
} free(cx, values);
SSAValue *stack = values + numSlots;
SSAValueInfo *stack = values + numSlots;
uint32_t stackDepth = 0;
for (uint32_t slot = ArgSlot(0); slot < numSlots; slot++) {
if (trackSlot(slot))
values[slot].initInitial(slot);
values[slot].v.initInitial(slot);
}
/*
* All target offsets for forward jumps we in the middle of. We lazily add
* pending entries at these targets for the original value of variables
* modified before the branch rejoins.
* All target offsets for forward jumps we have seen (including ones whose
* target we have advanced past). We lazily add pending entries at these
* targets for the original value of variables modified before the branch
* rejoins.
*/
Vector<uint32_t> branchTargets(cx);
/*
* Subset of branchTargets which are also exception handlers. Any value of
* a variable modified before the target is reached is a potential value
* at that target, along with the lazily added original value.
* Subset of branchTargets which are exception handlers at future offsets.
* Any new value of a variable modified before the target is reached is a
* potential value at that target, along with the lazy original value.
*/
Vector<uint32_t> exceptionTargets(cx);
@ -1266,6 +1267,17 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
continue;
}
if (code->exceptionEntry) {
/* Remove from exception targets list, which reflects only future targets. */
for (size_t i = 0; i < exceptionTargets.length(); i++) {
if (exceptionTargets[i] == offset) {
exceptionTargets[i] = exceptionTargets.back();
exceptionTargets.popBack();
break;
}
}
}
if (code->stackDepth > stackDepth)
PodZero(stack + stackDepth, code->stackDepth - stackDepth);
stackDepth = code->stackDepth;
@ -1287,9 +1299,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
* loops), but in such cases the original value is pushed back.
*/
Vector<SlotValue> *&pending = code->pendingValues;
if (pending) {
removeBranchTarget(branchTargets, exceptionTargets, offset);
} else {
if (!pending) {
pending = cx->new_< Vector<SlotValue> >(cx);
if (!pending) {
setOOM(cx);
@ -1305,7 +1315,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
for (unsigned i = 0; i < pending->length(); i++) {
SlotValue &v = (*pending)[i];
if (v.slot < numSlots && liveness(v.slot).firstWrite(code->loop) != UINT32_MAX) {
if (v.value.kind() != SSAValue::PHI || v.value.phiOffset() < offset) {
if (v.value.kind() != SSAValue::PHI || v.value.phiOffset() != offset) {
SSAValue ov = v.value;
if (!makePhi(cx, v.slot, offset, &ov))
return;
@ -1314,9 +1324,9 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
}
}
if (code->fallthrough || code->jumpFallthrough)
mergeValue(cx, offset, values[v.slot], &v);
mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets);
values[v.slot] = v.value;
mergeValue(cx, offset, values[v.slot].v, &v);
mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets, offset - 1);
values[v.slot].v = v.value;
}
/*
@ -1331,7 +1341,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
continue;
if (liveness(slot).firstWrite(code->loop) == UINT32_MAX)
continue;
if (values[slot].kind() == SSAValue::PHI && values[slot].phiOffset() == offset) {
if (values[slot].v.kind() == SSAValue::PHI && values[slot].v.phiOffset() == offset) {
/* There is already a pending entry for this slot. */
continue;
}
@ -1339,9 +1349,9 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
if (!makePhi(cx, slot, offset, &ov))
return;
if (code->fallthrough || code->jumpFallthrough)
insertPhi(cx, ov, values[slot]);
mergeBranchTarget(cx, values[slot], slot, branchTargets);
values[slot] = ov;
insertPhi(cx, ov, values[slot].v);
mergeBranchTarget(cx, values[slot], slot, branchTargets, offset - 1);
values[slot].v = ov;
if (!pending->append(SlotValue(slot, ov))) {
setOOM(cx);
return;
@ -1358,16 +1368,16 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
* that values written inside the block but not subsequently
* overwritten are picked up.
*/
bool exception = removeBranchTarget(branchTargets, exceptionTargets, offset);
bool exception = getCode(offset).exceptionEntry;
Vector<SlotValue> *pending = code->pendingValues;
for (unsigned i = 0; i < pending->length(); i++) {
SlotValue &v = (*pending)[i];
if (code->fallthrough || code->jumpFallthrough ||
(exception && values[v.slot].kind() != SSAValue::EMPTY)) {
mergeValue(cx, offset, values[v.slot], &v);
(exception && values[v.slot].v.kind() != SSAValue::EMPTY)) {
mergeValue(cx, offset, values[v.slot].v, &v);
}
mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets);
values[v.slot] = v.value;
mergeBranchTarget(cx, values[v.slot], v.slot, branchTargets, offset);
values[v.slot].v = v.value;
}
freezeNewValues(cx, offset);
}
@ -1390,7 +1400,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
return;
}
for (unsigned i = 0; i < nuses; i++) {
SSAValue &v = stack[stackDepth - 1 - i];
SSAValue &v = stack[stackDepth - 1 - i].v;
code->poppedValues[i] = v;
v.clear();
}
@ -1401,7 +1411,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
*/
uint32_t slot = GetBytecodeSlot(script, pc);
if (trackSlot(slot))
code->poppedValues[nuses] = values[slot];
code->poppedValues[nuses] = values[slot].v;
else
code->poppedValues[nuses].clear();
}
@ -1430,7 +1440,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
stackDepth -= nuses;
for (unsigned i = 0; i < ndefs; i++)
stack[stackDepth + i].initPushed(offset, i);
stack[stackDepth + i].v.initPushed(offset, i);
unsigned xdefs = ExtendedDef(pc) ? ndefs + 1 : ndefs;
if (xdefs) {
@ -1447,9 +1457,9 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
if (BytecodeUpdatesSlot(op)) {
uint32_t slot = GetBytecodeSlot(script, pc);
if (trackSlot(slot)) {
mergeBranchTarget(cx, values[slot], slot, branchTargets);
mergeExceptionTarget(cx, values[slot], slot, exceptionTargets);
values[slot].initWritten(slot, offset);
mergeBranchTarget(cx, values[slot], slot, branchTargets, offset);
mergeExceptionTarget(cx, values[slot].v, slot, exceptionTargets);
values[slot].v.initWritten(slot, offset);
}
}
@ -1462,7 +1472,7 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
* Propagate the current value of the local to the pushed value,
* and remember it with an extended use on the opcode.
*/
stack[stackDepth - 1] = code->poppedValues[0] = values[slot];
stack[stackDepth - 1].v = code->poppedValues[0] = values[slot].v;
}
break;
}
@ -1470,34 +1480,34 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
/* Short circuit ops which push back one of their operands. */
case JSOP_MOREITER:
stack[stackDepth - 2] = code->poppedValues[0];
stack[stackDepth - 2].v = code->poppedValues[0];
break;
case JSOP_INITPROP:
case JSOP_INITMETHOD:
stack[stackDepth - 1] = code->poppedValues[1];
stack[stackDepth - 1].v = code->poppedValues[1];
break;
case JSOP_INITELEM:
stack[stackDepth - 1] = code->poppedValues[2];
stack[stackDepth - 1].v = code->poppedValues[2];
break;
case JSOP_DUP:
stack[stackDepth - 1] = stack[stackDepth - 2] = code->poppedValues[0];
stack[stackDepth - 1].v = stack[stackDepth - 2].v = code->poppedValues[0];
break;
case JSOP_DUP2:
stack[stackDepth - 1] = stack[stackDepth - 3] = code->poppedValues[0];
stack[stackDepth - 2] = stack[stackDepth - 4] = code->poppedValues[1];
stack[stackDepth - 1].v = stack[stackDepth - 3].v = code->poppedValues[0];
stack[stackDepth - 2].v = stack[stackDepth - 4].v = code->poppedValues[1];
break;
case JSOP_SWAP:
/* Swap is like pick 1. */
case JSOP_PICK: {
unsigned pickedDepth = (op == JSOP_SWAP ? 1 : pc[1]);
stack[stackDepth - 1] = code->poppedValues[pickedDepth];
stack[stackDepth - 1].v = code->poppedValues[pickedDepth];
for (unsigned i = 0; i < pickedDepth; i++)
stack[stackDepth - 2 - i] = code->poppedValues[i];
stack[stackDepth - 2 - i].v = code->poppedValues[i];
break;
}
@ -1514,14 +1524,19 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
jsint high = GET_JUMP_OFFSET(pc2);
pc2 += JUMP_OFFSET_LEN;
checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth);
Vector<SlotValue> *pending = NULL;
uint32_t pendingOffset = 0;
for (jsint i = low; i <= high; i++) {
unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
if (targetOffset != offset)
checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth);
checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth,
&pending, &pendingOffset);
pc2 += JUMP_OFFSET_LEN;
}
checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth,
&pending, &pendingOffset);
break;
}
@ -1531,15 +1546,20 @@ ScriptAnalysis::analyzeSSA(JSContext *cx)
unsigned npairs = GET_UINT16(pc2);
pc2 += UINT16_LEN;
checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth);
Vector<SlotValue> *pending = NULL;
uint32_t pendingOffset = 0;
while (npairs) {
pc2 += INDEX_LEN;
unsigned targetOffset = offset + GET_JUMP_OFFSET(pc2);
checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth);
checkBranchTarget(cx, targetOffset, branchTargets, values, stackDepth,
&pending, &pendingOffset);
pc2 += JUMP_OFFSET_LEN;
npairs--;
}
checkBranchTarget(cx, defaultOffset, branchTargets, values, stackDepth,
&pending, &pendingOffset);
break;
}
@ -1676,7 +1696,7 @@ ScriptAnalysis::mergeValue(JSContext *cx, uint32_t offset, const SSAValue &v, Sl
if (v == pv->value)
return;
if (pv->value.kind() != SSAValue::PHI || pv->value.phiOffset() < offset) {
if (pv->value.kind() != SSAValue::PHI || pv->value.phiOffset() != offset) {
SSAValue ov = pv->value;
if (makePhi(cx, pv->slot, offset, &pv->value)) {
insertPhi(cx, pv->value, v);
@ -1685,7 +1705,6 @@ ScriptAnalysis::mergeValue(JSContext *cx, uint32_t offset, const SSAValue &v, Sl
return;
}
JS_ASSERT(pv->value.phiOffset() == offset);
insertPhi(cx, pv->value, v);
}
@ -1707,7 +1726,8 @@ ScriptAnalysis::checkPendingValue(JSContext *cx, const SSAValue &v, uint32_t slo
void
ScriptAnalysis::checkBranchTarget(JSContext *cx, uint32_t targetOffset,
Vector<uint32_t> &branchTargets,
SSAValue *values, uint32_t stackDepth)
SSAValueInfo *values, uint32_t stackDepth,
Vector<SlotValue> **ppending, uint32_t *ppendingOffset)
{
unsigned targetDepth = getCode(targetOffset).stackDepth;
JS_ASSERT(targetDepth <= stackDepth);
@ -1721,14 +1741,28 @@ ScriptAnalysis::checkBranchTarget(JSContext *cx, uint32_t targetOffset,
if (pending) {
for (unsigned i = 0; i < pending->length(); i++) {
SlotValue &v = (*pending)[i];
mergeValue(cx, targetOffset, values[v.slot], &v);
mergeValue(cx, targetOffset, values[v.slot].v, &v);
}
} else {
pending = cx->new_< Vector<SlotValue> >(cx);
if (!pending || !branchTargets.append(targetOffset)) {
if (ppending && *ppending) {
JS_ASSERT(*ppendingOffset != targetOffset);
pending = *ppending;
getCode(Min(targetOffset, *ppendingOffset)).switchSharesPending = true;
} else {
pending = cx->new_< Vector<SlotValue> >(cx);
if (!pending) {
setOOM(cx);
return;
}
}
if (!branchTargets.append(targetOffset)) {
setOOM(cx);
return;
}
if (ppending) {
*ppending = pending;
*ppendingOffset = Max(targetOffset, *ppendingOffset);
}
}
/*
@ -1739,7 +1773,7 @@ ScriptAnalysis::checkBranchTarget(JSContext *cx, uint32_t targetOffset,
*/
for (unsigned i = 0; i < targetDepth; i++) {
uint32_t slot = StackSlot(script, i);
checkPendingValue(cx, values[slot], slot, pending);
checkPendingValue(cx, values[slot].v, slot, pending);
}
}
@ -1747,6 +1781,8 @@ void
ScriptAnalysis::checkExceptionTarget(JSContext *cx, uint32_t catchOffset,
Vector<uint32_t> &exceptionTargets)
{
JS_ASSERT(getCode(catchOffset).exceptionEntry);
/*
* The catch offset will already be in the branch targets, just check
* whether this is already a known exception target.
@ -1760,8 +1796,8 @@ ScriptAnalysis::checkExceptionTarget(JSContext *cx, uint32_t catchOffset,
}
void
ScriptAnalysis::mergeBranchTarget(JSContext *cx, const SSAValue &value, uint32_t slot,
const Vector<uint32_t> &branchTargets)
ScriptAnalysis::mergeBranchTarget(JSContext *cx, SSAValueInfo &value, uint32_t slot,
const Vector<uint32_t> &branchTargets, uint32_t currentOffset)
{
if (slot >= numSlots) {
/*
@ -1777,11 +1813,27 @@ ScriptAnalysis::mergeBranchTarget(JSContext *cx, const SSAValue &value, uint32_t
/*
* Before changing the value of a variable, make sure the old value is
* marked at the target of any branches jumping over the current opcode.
* Only look at new branch targets which have appeared since the last time
* the variable was written.
*/
for (unsigned i = 0; i < branchTargets.length(); i++) {
Vector<SlotValue> *pending = getCode(branchTargets[i]).pendingValues;
checkPendingValue(cx, value, slot, pending);
for (int i = branchTargets.length() - 1; i >= value.branchSize; i--) {
if (branchTargets[i] <= currentOffset)
continue;
const Bytecode &code = getCode(branchTargets[i]);
/*
* If the pending array for this offset is shared with a later branch
* target, it will be updated when that offset is handled.
*/
if (code.switchSharesPending)
continue;
Vector<SlotValue> *pending = code.pendingValues;
checkPendingValue(cx, value.v, slot, pending);
}
value.branchSize = branchTargets.length();
}
void
@ -1818,7 +1870,7 @@ ScriptAnalysis::mergeExceptionTarget(JSContext *cx, const SSAValue &value, uint3
}
void
ScriptAnalysis::mergeAllExceptionTargets(JSContext *cx, SSAValue *values,
ScriptAnalysis::mergeAllExceptionTargets(JSContext *cx, SSAValueInfo *values,
const Vector<uint32_t> &exceptionTargets)
{
for (unsigned i = 0; i < exceptionTargets.length(); i++) {
@ -1826,36 +1878,11 @@ ScriptAnalysis::mergeAllExceptionTargets(JSContext *cx, SSAValue *values,
for (unsigned i = 0; i < pending->length(); i++) {
const SlotValue &v = (*pending)[i];
if (trackSlot(v.slot))
mergeExceptionTarget(cx, values[v.slot], v.slot, exceptionTargets);
mergeExceptionTarget(cx, values[v.slot].v, v.slot, exceptionTargets);
}
}
}
bool
ScriptAnalysis::removeBranchTarget(Vector<uint32_t> &branchTargets,
Vector<uint32_t> &exceptionTargets,
uint32_t offset)
{
bool exception = false;
for (unsigned i = 0; i < exceptionTargets.length(); i++) {
if (exceptionTargets[i] == offset) {
exceptionTargets[i] = branchTargets.back();
exceptionTargets.popBack();
exception = true;
break;
}
}
for (unsigned i = 0; i < branchTargets.length(); i++) {
if (branchTargets[i] == offset) {
branchTargets[i] = branchTargets.back();
branchTargets.popBack();
return exception;
}
}
JS_ASSERT(OOM());
return exception;
}
void
ScriptAnalysis::freezeNewValues(JSContext *cx, uint32_t offset)
{
@ -1866,7 +1893,8 @@ ScriptAnalysis::freezeNewValues(JSContext *cx, uint32_t offset)
unsigned count = pending->length();
if (count == 0) {
cx->delete_(pending);
if (!code.switchSharesPending)
cx->delete_(pending);
return;
}
@ -1881,7 +1909,8 @@ ScriptAnalysis::freezeNewValues(JSContext *cx, uint32_t offset)
code.newValues[count].slot = 0;
code.newValues[count].value.clear();
cx->delete_(pending);
if (!code.switchSharesPending)
cx->delete_(pending);
}
CrossSSAValue

Просмотреть файл

@ -142,6 +142,12 @@ class Bytecode
bool getStringElement:1; /* GETELEM which has accessed string properties. */
bool accessGetter: 1; /* Property read on a shape with a getter hook. */
/*
* Switch target other than the last one, which shares its pending values
* with a later offset during SSA analysis.
*/
bool switchSharesPending : 1;
/* Stack depth before this opcode. */
uint32_t stackDepth;
@ -1107,10 +1113,7 @@ class ScriptAnalysis
{
SSAUseChain *uses = useChain(SSAValue::PushedValue(pc - script->code, 0));
JS_ASSERT(uses && uses->popped);
JS_ASSERT_IF(uses->next,
!uses->next->next &&
uses->next->popped &&
script->code[uses->next->offset] == JSOP_SWAP);
JS_ASSERT(js_CodeSpec[script->code[uses->offset]].format & JOF_INVOKE);
return script->code + uses->offset;
}
@ -1190,6 +1193,19 @@ class ScriptAnalysis
inline void extendVariable(JSContext *cx, LifetimeVariable &var, unsigned start, unsigned end);
inline void ensureVariable(LifetimeVariable &var, unsigned until);
/* Current value for a variable or stack value, as tracked during SSA. */
struct SSAValueInfo
{
SSAValue v;
/*
* Sizes of branchTargets the last time this slot was written. Branches less
* than this threshold do not need to be inspected if the slot is written
* again, as they will already reflect the slot's value at the branch.
*/
int32_t branchSize;
};
/* SSA helpers */
bool makePhi(JSContext *cx, uint32_t slot, uint32_t offset, SSAValue *pv);
void insertPhi(JSContext *cx, SSAValue &phi, const SSAValue &v);
@ -1197,18 +1213,16 @@ class ScriptAnalysis
void checkPendingValue(JSContext *cx, const SSAValue &v, uint32_t slot,
Vector<SlotValue> *pending);
void checkBranchTarget(JSContext *cx, uint32_t targetOffset, Vector<uint32_t> &branchTargets,
SSAValue *values, uint32_t stackDepth);
SSAValueInfo *values, uint32_t stackDepth,
Vector<SlotValue> **ppending = NULL, uint32_t *ppendingOffset = NULL);
void checkExceptionTarget(JSContext *cx, uint32_t catchOffset,
Vector<uint32_t> &exceptionTargets);
void mergeBranchTarget(JSContext *cx, const SSAValue &value, uint32_t slot,
const Vector<uint32_t> &branchTargets);
void mergeBranchTarget(JSContext *cx, SSAValueInfo &value, uint32_t slot,
const Vector<uint32_t> &branchTargets, uint32_t currentOffset);
void mergeExceptionTarget(JSContext *cx, const SSAValue &value, uint32_t slot,
const Vector<uint32_t> &exceptionTargets);
void mergeAllExceptionTargets(JSContext *cx, SSAValue *values,
void mergeAllExceptionTargets(JSContext *cx, SSAValueInfo *values,
const Vector<uint32_t> &exceptionTargets);
bool removeBranchTarget(Vector<uint32_t> &branchTargets,
Vector<uint32_t> &exceptionTargets,
uint32_t offset);
void freezeNewValues(JSContext *cx, uint32_t offset);
struct TypeInferenceState {

Просмотреть файл

@ -67,6 +67,7 @@ CPPSRCS = \
testFuncCallback.cpp \
testFunctionProperties.cpp \
testGCOutOfMemory.cpp \
testOOM.cpp \
testGetPropertyDefault.cpp \
testIndexToString.cpp \
testIntString.cpp \

Просмотреть файл

@ -6,6 +6,8 @@
#include "tests.h"
#include "jsapi.h"
#include "jsdbgapi.h"
#include "jsxdrapi.h"
BEGIN_TEST(test_cloneScript)
{
@ -48,3 +50,122 @@ BEGIN_TEST(test_cloneScript)
return true;
}
END_TEST(test_cloneScript)
void
DestroyPrincipals(JSContext *cx, JSPrincipals *principals)
{
delete principals;
}
struct Principals : public JSPrincipals
{
public:
Principals(const char *name)
{
refcount = 0;
codebase = const_cast<char *>(name);
destroy = DestroyPrincipals;
subsume = NULL;
}
};
class AutoDropPrincipals
{
JSContext *cx;
JSPrincipals *principals;
public:
AutoDropPrincipals(JSContext *cx, JSPrincipals *principals)
: cx(cx), principals(principals)
{
JSPRINCIPALS_HOLD(cx, principals);
}
~AutoDropPrincipals()
{
JSPRINCIPALS_DROP(cx, principals);
}
};
JSBool
TranscodePrincipals(JSXDRState *xdr, JSPrincipals **principalsp)
{
return JS_XDRBytes(xdr, reinterpret_cast<char *>(principalsp), sizeof(*principalsp));
}
BEGIN_TEST(test_cloneScriptWithPrincipals)
{
JSSecurityCallbacks cbs = {
NULL,
TranscodePrincipals,
NULL,
NULL
};
JS_SetRuntimeSecurityCallbacks(rt, &cbs);
JSPrincipals *principalsA = new Principals("A");
AutoDropPrincipals dropA(cx, principalsA);
JSPrincipals *principalsB = new Principals("B");
AutoDropPrincipals dropB(cx, principalsB);
JSObject *A, *B;
CHECK(A = createGlobal(principalsA));
CHECK(B = createGlobal(principalsB));
const char *argnames[] = { "arg" };
const char *source = "return function() { return arg; }";
JSObject *obj;
// Compile in A
{
JSAutoEnterCompartment a;
if (!a.enter(cx, A))
return false;
JSFunction *fun;
CHECK(fun = JS_CompileFunctionForPrincipals(cx, A, principalsA, "f",
mozilla::ArrayLength(argnames), argnames,
source, strlen(source), __FILE__, 1));
JSScript *script;
CHECK(script = JS_GetFunctionScript(cx, fun));
CHECK(JS_GetScriptPrincipals(cx, script) == principalsA);
CHECK(obj = JS_GetFunctionObject(fun));
}
// Clone into B
{
JSAutoEnterCompartment b;
if (!b.enter(cx, B))
return false;
JSObject *cloned;
CHECK(cloned = JS_CloneFunctionObject(cx, obj, B));
JSFunction *fun;
CHECK(fun = JS_ValueToFunction(cx, JS::ObjectValue(*cloned)));
JSScript *script;
CHECK(script = JS_GetFunctionScript(cx, fun));
CHECK(JS_GetScriptPrincipals(cx, script) == principalsB);
JS::Value v;
JS::Value args[] = { JS::Int32Value(1) };
CHECK(JS_CallFunctionValue(cx, B, JS::ObjectValue(*cloned), 1, args, &v));
CHECK(v.isObject());
JSObject *funobj = &v.toObject();
CHECK(JS_ObjectIsFunction(cx, funobj));
CHECK(fun = JS_ValueToFunction(cx, v));
CHECK(script = JS_GetFunctionScript(cx, fun));
CHECK(JS_GetScriptPrincipals(cx, script) == principalsB);
}
return true;
}
END_TEST(test_cloneScriptWithPrincipals)

Просмотреть файл

@ -0,0 +1,19 @@
#include "tests.h"
BEGIN_TEST(testOOM)
{
JSString *jsstr = JS_ValueToString(cx, INT_TO_JSVAL(9));
jsval tmp = STRING_TO_JSVAL(jsstr);
JS_SetProperty(cx, global, "rootme", &tmp);
const jschar *s = JS_GetStringCharsZ(cx, jsstr);
JS_ASSERT(s[0] == '9' && s[1] == '\0');
return true;
}
virtual JSRuntime * createRuntime()
{
JSRuntime *rt = JS_NewRuntime(0);
JS_SetGCParameter(rt, JSGC_MAX_BYTES, (uint32_t)-1);
return rt;
}
END_TEST(testOOM)

Просмотреть файл

@ -157,10 +157,40 @@ Error(JSContext *cx, const char (&input)[N])
AutoInflatedString str(cx);
jsval dummy;
str = input;
CHECK(!JS_ParseJSON(cx, str.chars(), str.length(), &dummy));
JS_ClearPendingException(cx);
ContextPrivate p = {0, 0};
CHECK(!JS_GetContextPrivate(cx));
JS_SetContextPrivate(cx, &p);
JSErrorReporter old = JS_SetErrorReporter(cx, reportJSONEror);
JSBool ok = JS_ParseJSON(cx, str.chars(), str.length(), &dummy);
JS_SetErrorReporter(cx, old);
JS_SetContextPrivate(cx, NULL);
CHECK(!ok);
CHECK(!p.unexpectedErrorCount);
CHECK(p.expectedErrorCount == 1);
/* We do not execute JS, so there should be no exception thrown. */
CHECK(!JS_IsExceptionPending(cx));
return true;
}
struct ContextPrivate {
unsigned unexpectedErrorCount;
unsigned expectedErrorCount;
};
static void
reportJSONEror(JSContext *cx, const char *message, JSErrorReport *report)
{
ContextPrivate *p = static_cast<ContextPrivate *>(JS_GetContextPrivate(cx));
if (report->errorNumber == JSMSG_JSON_BAD_PARSE)
p->expectedErrorCount++;
else
p->unexpectedErrorCount++;
}
END_TEST(testParseJSON_error)
static JSBool

Просмотреть файл

@ -6,6 +6,236 @@
#include "jsscript.h"
#include "jsxdrapi.h"
static JSScript *
CompileScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj,
JSPrincipals *principals, JSPrincipals *originPrincipals,
const char *bytes, size_t nbytes,
const char *filename, uintN lineno,
JSVersion version)
{
size_t nchars;
if (!JS_DecodeBytes(cx, bytes, nbytes, NULL, &nchars))
return NULL;
jschar *chars = static_cast<jschar *>(JS_malloc(cx, nchars * sizeof(jschar)));
if (!chars)
return NULL;
JS_ALWAYS_TRUE(JS_DecodeBytes(cx, bytes, nbytes, chars, &nchars));
JSScript *script = JS_CompileUCScriptForPrincipalsVersionOrigin(cx, obj,
principals, originPrincipals,
chars, nchars,
filename, lineno, version);
free(chars);
return script;
}
template<typename T>
T *
FreezeThawImpl(JSContext *cx, T *thing, JSBool (*xdrAction)(JSXDRState *xdr, T **))
{
// freeze
JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
if (!w)
return NULL;
void *memory = NULL;
uint32_t nbytes;
if (xdrAction(w, &thing)) {
void *p = JS_XDRMemGetData(w, &nbytes);
if (p) {
memory = JS_malloc(cx, nbytes);
if (memory)
memcpy(memory, p, nbytes);
}
}
JS_XDRDestroy(w);
if (!memory)
return NULL;
// thaw
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
JS_XDRMemSetData(r, memory, nbytes);
if (!xdrAction(r, &thing))
thing = NULL;
JS_XDRDestroy(r); // this frees `memory
return thing;
}
static JSScript *
FreezeThaw(JSContext *cx, JSScript *script)
{
return FreezeThawImpl(cx, script, JS_XDRScript);
}
static JSObject *
FreezeThaw(JSContext *cx, JSObject *funobj)
{
return FreezeThawImpl(cx, funobj, JS_XDRFunctionObject);
}
static JSBool
SubsumePrincipals(JSPrincipals *, JSPrincipals *)
{
return true;
}
static JSPrincipals testPrincipals[] = {
{ const_cast<char *>("foo.bar"), 1, NULL, SubsumePrincipals },
{ const_cast<char *>("dot.com"), 1, NULL, SubsumePrincipals },
};
static JSBool
CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode, jsval *vp)
{
return true;
}
static JSBool
TranscodePrincipals(JSXDRState *xdr, JSPrincipals **principalsp)
{
uint32_t index;
if (xdr->mode == JSXDR_ENCODE) {
JSPrincipals *p = *principalsp;
for (index = 0; ; ++index) {
if (index == mozilla::ArrayLength(testPrincipals))
return false;
if (p == &testPrincipals[index])
break;
}
}
if (!JS_XDRUint32(xdr, &index))
return false;
if (xdr->mode == JSXDR_DECODE) {
if (index >= mozilla::ArrayLength(testPrincipals))
return false;
*principalsp = &testPrincipals[index];
JSPRINCIPALS_HOLD(xdr->cx, *principalsp);
}
return true;
}
BEGIN_TEST(testXDR_principals)
{
static JSSecurityCallbacks seccb = {
CheckAccess,
TranscodePrincipals,
NULL,
NULL
};
JS_SetRuntimeSecurityCallbacks(rt, &seccb);
JSScript *script;
for (int i = TEST_FIRST; i != TEST_END; ++i) {
script = createScriptViaXDR(NULL, NULL, i);
CHECK(script);
CHECK(!JS_GetScriptPrincipals(cx, script));
CHECK(!JS_GetScriptOriginPrincipals(cx, script));
script = createScriptViaXDR(NULL, NULL, i);
CHECK(script);
CHECK(!JS_GetScriptPrincipals(cx, script));
CHECK(!JS_GetScriptOriginPrincipals(cx, script));
script = createScriptViaXDR(&testPrincipals[0], NULL, i);
CHECK(script);
CHECK(JS_GetScriptPrincipals(cx, script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[0]);
script = createScriptViaXDR(&testPrincipals[0], &testPrincipals[0], i);
CHECK(script);
CHECK(JS_GetScriptPrincipals(cx, script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[0]);
script = createScriptViaXDR(&testPrincipals[0], &testPrincipals[1], i);
CHECK(script);
CHECK(JS_GetScriptPrincipals(cx, script) == &testPrincipals[0]);
CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[1]);
script = createScriptViaXDR(NULL, &testPrincipals[1], i);
CHECK(script);
CHECK(!JS_GetScriptPrincipals(cx, script));
CHECK(JS_GetScriptOriginPrincipals(cx, script) == &testPrincipals[1]);
}
return true;
}
enum TestCase {
TEST_FIRST,
TEST_SCRIPT = TEST_FIRST,
TEST_FUNCTION,
TEST_SERIALIZED_FUNCTION,
TEST_END
};
JSScript *createScriptViaXDR(JSPrincipals *prin, JSPrincipals *orig, int testCase)
{
const char src[] =
"function f() { return 1; }\n"
"f;\n";
JSScript *script = CompileScriptForPrincipalsVersionOrigin(cx, global, prin, orig,
src, strlen(src), "test", 1,
JSVERSION_DEFAULT);
if (!script)
return NULL;
if (testCase == TEST_SCRIPT || testCase == TEST_SERIALIZED_FUNCTION) {
script = FreezeThaw(cx, script);
if (!script)
return NULL;
if (testCase == TEST_SCRIPT)
return script;
}
JS::Value v;
JSBool ok = JS_ExecuteScript(cx, global, script, &v);
if (!ok || !v.isObject())
return NULL;
JSObject *funobj = &v.toObject();
if (testCase == TEST_FUNCTION) {
funobj = FreezeThaw(cx, funobj);
if (!funobj)
return NULL;
}
return JS_GetFunctionScript(cx, JS_GetObjectFunction(funobj));
}
END_TEST(testXDR_principals)
BEGIN_TEST(testXDR_atline)
{
JS_ToggleOptions(cx, JSOPTION_ATLINE);
CHECK(JS_GetOptions(cx) & JSOPTION_ATLINE);
const char src[] =
"//@line 100 \"foo\"\n"
"function nested() { }\n"
"//@line 200 \"bar\"\n"
"nested;\n";
JSScript *script = JS_CompileScript(cx, global, src, strlen(src), "internal", 1);
CHECK(script);
CHECK(script = FreezeThaw(cx, script));
CHECK(!strcmp("bar", JS_GetScriptFilename(cx, script)));
JS::Value v;
JSBool ok = JS_ExecuteScript(cx, global, script, &v);
CHECK(ok);
CHECK(v.isObject());
JSObject *funobj = &v.toObject();
script = JS_GetFunctionScript(cx, JS_GetObjectFunction(funobj));
CHECK(!strcmp("foo", JS_GetScriptFilename(cx, script)));
return true;
}
END_TEST(testXDR_atline)
BEGIN_TEST(testXDR_bug506491)
{
const char *s =
@ -19,24 +249,8 @@ BEGIN_TEST(testXDR_bug506491)
JSScript *script = JS_CompileScript(cx, global, s, strlen(s), __FILE__, __LINE__);
CHECK(script);
// freeze
JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
CHECK(w);
CHECK(JS_XDRScript(w, &script));
uint32_t nbytes;
void *p = JS_XDRMemGetData(w, &nbytes);
CHECK(p);
void *frozen = JS_malloc(cx, nbytes);
CHECK(frozen);
js_memcpy(frozen, p, nbytes);
JS_XDRDestroy(w);
// thaw
script = NULL;
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
JS_XDRMemSetData(r, frozen, nbytes);
CHECK(JS_XDRScript(r, &script));
JS_XDRDestroy(r); // this frees `frozen`
script = FreezeThaw(cx, script);
CHECK(script);
// execute
jsvalRoot v2(cx);
@ -59,24 +273,8 @@ BEGIN_TEST(testXDR_bug516827)
JSScript *script = JS_CompileScript(cx, global, "", 0, __FILE__, __LINE__);
CHECK(script);
// freeze
JSXDRState *w = JS_XDRNewMem(cx, JSXDR_ENCODE);
CHECK(w);
CHECK(JS_XDRScript(w, &script));
uint32_t nbytes;
void *p = JS_XDRMemGetData(w, &nbytes);
CHECK(p);
void *frozen = JS_malloc(cx, nbytes);
CHECK(frozen);
js_memcpy(frozen, p, nbytes);
JS_XDRDestroy(w);
// thaw
script = NULL;
JSXDRState *r = JS_XDRNewMem(cx, JSXDR_DECODE);
JS_XDRMemSetData(r, frozen, nbytes);
CHECK(JS_XDRScript(r, &script));
JS_XDRDestroy(r); // this frees `frozen`
script = FreezeThaw(cx, script);
CHECK(script);
// execute with null result meaning no result wanted
CHECK(JS_ExecuteScript(cx, global, script, NULL));

Просмотреть файл

@ -2456,10 +2456,6 @@ JS_CallTracer(JSTracer *trc, void *thing, JSGCTraceKind kind)
#ifdef DEBUG
#ifdef HAVE_XPCONNECT
#include "dump_xpc.h"
#endif
JS_PUBLIC_API(void)
JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
JSGCTraceKind kind, JSBool details)
@ -2473,20 +2469,7 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
switch (kind) {
case JSTRACE_OBJECT:
{
JSObject *obj = (JSObject *)thing;
Class *clasp = obj->getClass();
name = clasp->name;
#ifdef HAVE_XPCONNECT
if (clasp->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) {
void *privateThing = obj->getPrivate();
if (privateThing) {
const char *xpcClassName = GetXPCObjectClassName(privateThing);
if (xpcClassName)
name = xpcClassName;
}
}
#endif
name = static_cast<JSObject *>(thing)->getClass()->name;
break;
}
@ -2525,36 +2508,39 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
js_memcpy(buf, name, n + 1);
buf += n;
bufsize -= n;
*buf = '\0';
if (details && bufsize > 2) {
*buf++ = ' ';
bufsize--;
switch (kind) {
case JSTRACE_OBJECT:
{
JSObject *obj = (JSObject *)thing;
JSObject *obj = (JSObject *)thing;
Class *clasp = obj->getClass();
if (clasp == &FunctionClass) {
JSFunction *fun = obj->toFunction();
if (!fun) {
JS_snprintf(buf, bufsize, "<newborn>");
JS_snprintf(buf, bufsize, " <newborn>");
} else if (fun != obj) {
JS_snprintf(buf, bufsize, "%p", fun);
JS_snprintf(buf, bufsize, " %p", fun);
} else {
if (fun->atom)
if (fun->atom) {
*buf++ = ' ';
bufsize--;
PutEscapedString(buf, bufsize, fun->atom, 0);
}
}
} else if (clasp->flags & JSCLASS_HAS_PRIVATE) {
JS_snprintf(buf, bufsize, "%p", obj->getPrivate());
JS_snprintf(buf, bufsize, " %p", obj->getPrivate());
} else {
JS_snprintf(buf, bufsize, "<no private>");
JS_snprintf(buf, bufsize, " <no private>");
}
break;
}
case JSTRACE_STRING:
{
*buf++ = ' ';
bufsize--;
JSString *str = (JSString *)thing;
if (str->isLinear())
PutEscapedString(buf, bufsize, &str->asLinear(), 0);
@ -2566,7 +2552,7 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
case JSTRACE_SCRIPT:
{
JSScript *script = static_cast<JSScript *>(thing);
JS_snprintf(buf, bufsize, "%s:%u", script->filename, unsigned(script->lineno));
JS_snprintf(buf, bufsize, " %s:%u", script->filename, unsigned(script->lineno));
break;
}
@ -2581,7 +2567,7 @@ JS_PrintTraceThingInfo(char *buf, size_t bufsize, JSTracer *trc, void *thing,
extern const char *js_xml_class_str[];
JSXML *xml = (JSXML *)thing;
JS_snprintf(buf, bufsize, "%s", js_xml_class_str[xml->xml_class]);
JS_snprintf(buf, bufsize, " %s", js_xml_class_str[xml->xml_class]);
break;
}
#endif
@ -4835,7 +4821,8 @@ JS_OPTIONS_TO_TCFLAGS(JSContext *cx)
}
static JSScript *
CompileUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, JSPrincipals *principals,
CompileUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj,
JSPrincipals *principals, JSPrincipals *originPrincipals,
const jschar *chars, size_t length,
const char *filename, uintN lineno, JSVersion version)
{
@ -4846,7 +4833,7 @@ CompileUCScriptForPrincipalsCommon(JSContext *cx, JSObject *obj, JSPrincipals *p
AutoLastFrameCheck lfc(cx);
uint32_t tcflags = JS_OPTIONS_TO_TCFLAGS(cx) | TCF_NEED_SCRIPT_GLOBAL;
return frontend::CompileScript(cx, obj, NULL, principals, NULL, tcflags,
return frontend::CompileScript(cx, obj, NULL, principals, originPrincipals, tcflags,
chars, length, filename, lineno, version);
}
@ -4858,8 +4845,21 @@ JS_CompileUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
JSVersion version)
{
AutoVersionAPI avi(cx, version);
return CompileUCScriptForPrincipalsCommon(cx, obj, principals, chars, length, filename, lineno,
avi.version());
return CompileUCScriptForPrincipalsCommon(cx, obj, principals, NULL, chars, length,
filename, lineno, avi.version());
}
extern JS_PUBLIC_API(JSScript *)
JS_CompileUCScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj,
JSPrincipals *principals,
JSPrincipals *originPrincipals,
const jschar *chars, size_t length,
const char *filename, uintN lineno,
JSVersion version)
{
AutoVersionAPI avi(cx, version);
return CompileUCScriptForPrincipalsCommon(cx, obj, principals, originPrincipals,
chars, length, filename, lineno, avi.version());
}
JS_PUBLIC_API(JSScript *)
@ -4867,8 +4867,8 @@ JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, JSPrincipals *prin
const jschar *chars, size_t length,
const char *filename, uintN lineno)
{
return CompileUCScriptForPrincipalsCommon(cx, obj, principals, chars, length, filename, lineno,
cx->findVersion());
return CompileUCScriptForPrincipalsCommon(cx, obj, principals, NULL, chars, length,
filename, lineno, cx->findVersion());
}
JS_PUBLIC_API(JSScript *)

Просмотреть файл

@ -4258,6 +4258,17 @@ JS_CompileUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
const jschar *chars, size_t length,
const char *filename, uintN lineno,
JSVersion version);
/*
* If originPrincipals is null, then the value of principals is used as origin
* principals for the compiled script.
*/
extern JS_PUBLIC_API(JSScript *)
JS_CompileUCScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj,
JSPrincipals *principals,
JSPrincipals *originPrincipals,
const jschar *chars, size_t length,
const char *filename, uintN lineno,
JSVersion version);
extern JS_PUBLIC_API(JSScript *)
JS_CompileUTF8File(JSContext *cx, JSObject *obj, const char *filename);
@ -4422,7 +4433,8 @@ JS_EvaluateUCScriptForPrincipalsVersion(JSContext *cx, JSObject *obj,
* A script's originPrincipals may be retrieved through the debug API (via
* JS_GetScriptOriginPrincipals) and the originPrincipals are transitively
* assigned to any nested scripts (including scripts dynamically created via
* eval and the Function constructor).
* eval and the Function constructor). If originPrincipals is null, then the
* value of principals is used as origin principals for the script.
*/
extern JS_PUBLIC_API(JSBool)
JS_EvaluateUCScriptForPrincipalsVersionOrigin(JSContext *cx, JSObject *obj,

Просмотреть файл

@ -142,6 +142,8 @@ Boolean(JSContext *cx, uintN argc, Value *vp)
if (IsConstructing(vp)) {
JSObject *obj = BooleanObject::create(cx, b);
if (!obj)
return false;
args.rval().setObject(*obj);
} else {
args.rval().setBoolean(b);

Просмотреть файл

@ -545,6 +545,7 @@ js::DumpHeapComplete(JSContext *cx, FILE *fp)
}
dtrc.visited.finish();
fflush(dtrc.output);
}
#endif

Просмотреть файл

@ -4322,7 +4322,12 @@ EndVerifyBarriers(JSContext *cx)
if (!trc)
return;
/*
* We need to bump gcNumber so that the methodjit knows that jitcode has
* been discarded.
*/
JS_ASSERT(trc->number == rt->gcNumber);
rt->gcNumber++;
/* We need to disable barriers before tracing, which may invoke barriers. */
for (CompartmentsIter c(rt); !c.done(); c.next())

Просмотреть файл

@ -306,11 +306,11 @@ ProxyHandler::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
return fun_toStringHelper(cx, &fval.toObject(), indent);
}
RegExpShared *
ProxyHandler::regexp_toShared(JSContext *cx, JSObject *proxy)
bool
ProxyHandler::regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g)
{
JS_NOT_REACHED("This should have been a wrapped regexp");
return (RegExpShared *)NULL;
return false;
}
bool
@ -965,12 +965,12 @@ Proxy::fun_toString(JSContext *cx, JSObject *proxy, uintN indent)
return GetProxyHandler(proxy)->fun_toString(cx, proxy, indent);
}
RegExpShared *
Proxy::regexp_toShared(JSContext *cx, JSObject *proxy)
bool
Proxy::regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g)
{
JS_CHECK_RECURSION(cx, return NULL);
AutoPendingProxyOperation pending(cx, proxy);
return GetProxyHandler(proxy)->regexp_toShared(cx, proxy);
return GetProxyHandler(proxy)->regexp_toShared(cx, proxy, g);
}
bool

Просмотреть файл

@ -84,7 +84,7 @@ class JS_FRIEND_API(ProxyHandler) {
virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx);
virtual JSString *obj_toString(JSContext *cx, JSObject *proxy);
virtual JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy);
virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g);
virtual bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp);
virtual bool iteratorNext(JSContext *cx, JSObject *proxy, Value *vp);
virtual void finalize(JSContext *cx, JSObject *proxy);
@ -139,7 +139,7 @@ class Proxy {
static bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx);
static JSString *obj_toString(JSContext *cx, JSObject *proxy);
static JSString *fun_toString(JSContext *cx, JSObject *proxy, uintN indent);
static RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy);
static bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g);
static bool defaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp);
static bool iteratorNext(JSContext *cx, JSObject *proxy, Value *vp);
};

Просмотреть файл

@ -123,6 +123,7 @@ namespace js {
struct ArgumentsData;
struct Class;
class RegExpGuard;
class RegExpObject;
class RegExpObjectBuilder;
class RegExpShared;

Просмотреть файл

@ -429,7 +429,7 @@ XDRScriptConst(JSXDRState *xdr, HeapValue *vp)
}
return true;
}
static const char *
SaveScriptFilename(JSContext *cx, const char *filename);
@ -441,7 +441,9 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
SavedCallerFun,
StrictModeCode,
UsesEval,
UsesArguments
UsesArguments,
OwnFilename,
SharedFilename
};
uint32_t length, lineno, nslots;
@ -449,17 +451,12 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
uint32_t prologLength, version, encodedClosedCount;
uint16_t nClosedArgs = 0, nClosedVars = 0;
uint32_t nTypeSets = 0;
uint32_t encodeable, sameOriginPrincipals;
JSSecurityCallbacks *callbacks;
uint32_t scriptBits = 0;
JSContext *cx = xdr->cx;
JSScript *script;
nsrcnotes = ntrynotes = natoms = nobjects = nregexps = nconsts = 0;
jssrcnote *notes = NULL;
XDRScriptState *state = xdr->state;
JS_ASSERT(state);
/* XDR arguments, local vars, and upvars. */
uint16_t nargs, nvars, nupvars;
@ -611,6 +608,12 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
scriptBits |= (1 << UsesEval);
if (script->usesArguments)
scriptBits |= (1 << UsesArguments);
if (script->filename) {
scriptBits |= (script->filename != xdr->sharedFilename)
? (1 << OwnFilename)
: (1 << SharedFilename);
}
JS_ASSERT(!script->compileAndGo);
JS_ASSERT(!script->hasSingletons);
}
@ -686,52 +689,39 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
return false;
}
if (xdr->mode == JSXDR_DECODE && state->filename) {
if (!state->filenameSaved) {
const char *filename = state->filename;
filename = SaveScriptFilename(xdr->cx, filename);
xdr->cx->free_((void *) state->filename);
state->filename = filename;
state->filenameSaved = true;
if (!filename)
if (scriptBits & (1 << OwnFilename)) {
char *filename;
if (xdr->mode == JSXDR_ENCODE)
filename = const_cast<char *>(script->filename);
if (!JS_XDRCString(xdr, &filename))
return false;
if (xdr->mode == JSXDR_DECODE) {
script->filename = SaveScriptFilename(xdr->cx, filename);
Foreground::free_(filename);
if (!script->filename)
return false;
if (!xdr->sharedFilename)
xdr->sharedFilename = script->filename;
}
script->filename = state->filename;
} else if (scriptBits & (1 << SharedFilename)) {
JS_ASSERT(xdr->sharedFilename);
if (xdr->mode == JSXDR_DECODE)
script->filename = xdr->sharedFilename;
}
JS_ASSERT_IF(xdr->mode == JSXDR_ENCODE, state->filename == script->filename);
if (xdr->mode == JSXDR_DECODE) {
JS_ASSERT(!script->principals);
JS_ASSERT(!script->originPrincipals);
callbacks = JS_GetSecurityCallbacks(cx);
if (xdr->mode == JSXDR_ENCODE)
encodeable = script->principals && callbacks && callbacks->principalsTranscoder;
if (!JS_XDRUint32(xdr, &encodeable))
return false;
if (encodeable) {
if (!callbacks || !callbacks->principalsTranscoder) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_CANT_DECODE_PRINCIPALS);
return false;
/* The origin principals must be normalized at this point. */
JS_ASSERT_IF(script->principals, script->originPrincipals);
if (xdr->principals) {
script->principals = xdr->principals;
JSPRINCIPALS_HOLD(cx, xdr->principals);
}
if (!callbacks->principalsTranscoder(xdr, &script->principals))
return false;
if (xdr->mode == JSXDR_ENCODE)
sameOriginPrincipals = script->principals == script->originPrincipals;
if (!JS_XDRUint32(xdr, &sameOriginPrincipals))
return false;
if (sameOriginPrincipals) {
if (xdr->mode == JSXDR_DECODE) {
script->originPrincipals = script->principals;
JSPRINCIPALS_HOLD(cx, script->originPrincipals);
}
} else {
if (!callbacks->principalsTranscoder(xdr, &script->originPrincipals))
return false;
if (xdr->originPrincipals) {
script->originPrincipals = xdr->originPrincipals;
JSPRINCIPALS_HOLD(cx, xdr->originPrincipals);
}
}
@ -823,8 +813,8 @@ XDRScript(JSXDRState *xdr, JSScript **scriptp)
} while (tn != tnfirst);
}
if (nconsts) {
HeapValue *vector = script->consts()->vector;
if (nconsts) {
HeapValue *vector = script->consts()->vector;
for (i = 0; i != nconsts; ++i) {
if (!XDRScriptConst(xdr, &vector[i]))
return false;
@ -923,7 +913,19 @@ SaveScriptFilename(JSContext *cx, const char *filename)
}
}
return (*p)->filename;
ScriptFilenameEntry *sfe = *p;
#ifdef JSGC_INCREMENTAL
/*
* During the IGC we need to ensure that filename is marked whenever it is
* accessed even if the name was already in the table. At this point old
* scripts or exceptions pointing to the filename may no longer be
* reachable.
*/
if (comp->needsBarrier() && !sfe->marked)
sfe->marked = true;
#endif
return sfe->filename;
}
} /* namespace js */
@ -1705,29 +1707,8 @@ CurrentScriptFileLineOriginSlow(JSContext *cx, const char **file, uintN *linenop
*origin = script->originPrincipals;
}
class DisablePrincipalsTranscoding {
JSSecurityCallbacks *callbacks;
JSPrincipalsTranscoder temp;
public:
DisablePrincipalsTranscoding(JSContext *cx)
: callbacks(JS_GetRuntimeSecurityCallbacks(cx->runtime)),
temp(NULL)
{
if (callbacks) {
temp = callbacks->principalsTranscoder;
callbacks->principalsTranscoder = NULL;
}
}
~DisablePrincipalsTranscoding() {
if (callbacks)
callbacks->principalsTranscoder = temp;
}
};
class AutoJSXDRState {
public:
public:
AutoJSXDRState(JSXDRState *x
JS_GUARD_OBJECT_NOTIFIER_PARAM)
: xdr(x)
@ -1744,7 +1725,12 @@ public:
return xdr;
}
private:
JSXDRState* operator->() const
{
return xdr;
}
private:
JSXDRState *const xdr;
JS_DECL_USE_GUARD_OBJECT_NOTIFIER
};
@ -1754,18 +1740,11 @@ CloneScript(JSContext *cx, JSScript *script)
{
JS_ASSERT(cx->compartment != script->compartment());
// serialize script
/* Serialize script. */
AutoJSXDRState w(JS_XDRNewMem(cx, JSXDR_ENCODE));
if (!w)
return NULL;
// we don't want gecko to transcribe our principals for us
DisablePrincipalsTranscoding disable(cx);
XDRScriptState wstate(w);
#ifdef DEBUG
wstate.filename = script->filename;
#endif
if (!XDRScript(w, &script))
return NULL;
@ -1774,35 +1753,25 @@ CloneScript(JSContext *cx, JSScript *script)
if (!p)
return NULL;
// de-serialize script
/* De-serialize script. */
AutoJSXDRState r(JS_XDRNewMem(cx, JSXDR_DECODE));
if (!r)
return NULL;
// Hand p off from w to r. Don't want them to share the data
// mem, lest they both try to free it in JS_XDRDestroy
/*
* Hand p off from w to r. Don't want them to share the data mem, lest
* they both try to free it in JS_XDRDestroy.
*/
JS_XDRMemSetData(r, p, nbytes);
JS_XDRMemSetData(w, NULL, 0);
XDRScriptState rstate(r);
rstate.filename = script->filename;
rstate.filenameSaved = true;
r->principals = cx->compartment->principals;
r->originPrincipals = JSScript::normalizeOriginPrincipals(cx->compartment->principals,
script->originPrincipals);
JSScript *newScript = NULL;
if (!XDRScript(r, &newScript))
return NULL;
// set the proper principals for the script's new compartment
// the originPrincipals are not related to compartment, so just copy
newScript->principals = newScript->compartment()->principals;
newScript->originPrincipals = script->originPrincipals;
if (!newScript->originPrincipals)
newScript->originPrincipals = newScript->principals;
if (newScript->principals) {
JSPRINCIPALS_HOLD(cx, newScript->principals);
JSPRINCIPALS_HOLD(cx, newScript->originPrincipals);
}
return newScript;
}

Просмотреть файл

@ -672,7 +672,7 @@ struct JSScript : public js::gc::Cell {
}
/*
* computedSizeOfData() is the in-use size of all the data sections.
* computedSizeOfData() is the in-use size of all the data sections.
* sizeOfData() is the size of the block allocated to hold all the data sections
* (which can be larger than the in-use size).
*/
@ -829,6 +829,11 @@ struct JSScript : public js::gc::Cell {
static inline void writeBarrierPost(JSScript *script, void *addr);
static inline js::ThingRootKind rootKind() { return js::THING_ROOT_SCRIPT; }
static JSPrincipals *normalizeOriginPrincipals(JSPrincipals *principals,
JSPrincipals *originPrincipals) {
return originPrincipals ? originPrincipals : principals;
}
};
/* If this fails, padding_ can be removed. */
@ -932,7 +937,7 @@ extern JSScript *
CloneScript(JSContext *cx, JSScript *script);
/*
* NB: after a successful JSXDR_DECODE, js_XDRScript callers must do any
* NB: after a successful JSXDR_DECODE, XDRScript callers must do any
* required subsequent set-up of owning function or script object and then call
* js_CallNewScriptHook.
*/

Просмотреть файл

@ -1339,7 +1339,7 @@ class FlatMatch
size_t patlen;
int32_t match_;
friend class RegExpGuard;
friend class StringRegExpGuard;
public:
FlatMatch() : patstr(NULL) {} /* Old GCC wants this initialization. */
@ -1378,18 +1378,18 @@ HasRegExpMetaChars(const jschar *chars, size_t length)
}
/*
* RegExpGuard factors logic out of String regexp operations.
* StringRegExpGuard factors logic out of String regexp operations.
*
* |optarg| indicates in which argument position RegExp flags will be found, if
* present. This is a Mozilla extension and not part of any ECMA spec.
*/
class RegExpGuard
class StringRegExpGuard
{
RegExpGuard(const RegExpGuard &) MOZ_DELETE;
void operator=(const RegExpGuard &) MOZ_DELETE;
StringRegExpGuard(const StringRegExpGuard &) MOZ_DELETE;
void operator=(const StringRegExpGuard &) MOZ_DELETE;
RegExpShared::Guard re_;
FlatMatch fm;
RegExpGuard re_;
FlatMatch fm;
/*
* Upper bound on the number of characters we are willing to potentially
@ -1420,16 +1420,14 @@ class RegExpGuard
}
public:
RegExpGuard() {}
StringRegExpGuard() {}
/* init must succeed in order to call tryFlatMatch or normalizeRegExp. */
bool init(JSContext *cx, CallArgs args, bool convertVoid = false)
{
if (args.length() != 0 && IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
RegExpShared *shared = RegExpToShared(cx, args[0].toObject());
if (!shared)
if (!RegExpToShared(cx, args[0].toObject(), &re_))
return false;
re_.init(*shared);
} else {
if (convertVoid && (args.length() == 0 || args[0].isUndefined())) {
fm.patstr = cx->runtime->emptyString;
@ -1516,12 +1514,7 @@ class RegExpGuard
}
JS_ASSERT(patstr);
RegExpShared *re = cx->compartment->regExps.get(cx, patstr, opt);
if (!re)
return false;
re_.init(*re);
return true;
return cx->compartment->regExps.get(cx, patstr, opt, &re_);
}
RegExpShared &regExp() { return *re_; }
@ -1640,7 +1633,7 @@ js::str_match(JSContext *cx, uintN argc, Value *vp)
if (!str)
return false;
RegExpGuard g;
StringRegExpGuard g;
if (!g.init(cx, args, true))
return false;
@ -1676,7 +1669,7 @@ js::str_search(JSContext *cx, uintN argc, Value *vp)
if (!str)
return false;
RegExpGuard g;
StringRegExpGuard g;
if (!g.init(cx, args, true))
return false;
if (const FlatMatch *fm = g.tryFlatMatch(cx, str, 1, args.length())) {
@ -1718,7 +1711,7 @@ struct ReplaceData
{}
JSString *str; /* 'this' parameter object as a string */
RegExpGuard g; /* regexp parameter object and private data */
StringRegExpGuard g; /* regexp parameter object and private data */
JSObject *lambda; /* replacement function object or null */
JSObject *elembase; /* object for function(a){return b[a]} replace */
JSLinearString *repstr; /* replacement string */
@ -2572,15 +2565,13 @@ js::str_split(JSContext *cx, uintN argc, Value *vp)
}
/* Step 8. */
RegExpShared::Guard re;
RegExpGuard re;
JSLinearString *sepstr = NULL;
bool sepUndefined = (args.length() == 0 || args[0].isUndefined());
if (!sepUndefined) {
if (IsObjectWithClass(args[0], ESClass_RegExp, cx)) {
RegExpShared *shared = RegExpToShared(cx, args[0].toObject());
if (!shared)
if (!RegExpToShared(cx, args[0].toObject(), &re))
return false;
re.init(*shared);
} else {
sepstr = ArgToRootedString(cx, args, 0);
if (!sepstr)

Просмотреть файл

@ -330,10 +330,10 @@ Wrapper::fun_toString(JSContext *cx, JSObject *wrapper, uintN indent)
return str;
}
RegExpShared *
Wrapper::regexp_toShared(JSContext *cx, JSObject *wrapper)
bool
Wrapper::regexp_toShared(JSContext *cx, JSObject *wrapper, RegExpGuard *g)
{
return wrappedObject(wrapper)->asRegExp().getShared(cx);
return wrappedObject(wrapper)->asRegExp().getShared(cx, g);
}
bool
@ -792,9 +792,9 @@ CrossCompartmentWrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *cla
if (!Wrapper::nativeCall(cx, wrapper, clasp, native, dstArgs))
return false;
srcArgs.rval() = dstArgs.rval();
dstArgs.pop();
call.leave();
srcArgs.rval() = dstArgs.rval();
return call.origin->wrap(cx, &srcArgs.rval());
}
@ -908,10 +908,10 @@ SecurityWrapper<Base>::objectClassIs(JSObject *obj, ESClassValue classValue, JSC
}
template <class Base>
RegExpShared *
SecurityWrapper<Base>::regexp_toShared(JSContext *cx, JSObject *obj)
bool
SecurityWrapper<Base>::regexp_toShared(JSContext *cx, JSObject *obj, RegExpGuard *g)
{
return Base::regexp_toShared(cx, obj);
return Base::regexp_toShared(cx, obj, g);
}

Просмотреть файл

@ -94,7 +94,7 @@ class JS_FRIEND_API(Wrapper) : public ProxyHandler
virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE;
virtual JSString *obj_toString(JSContext *cx, JSObject *wrapper) MOZ_OVERRIDE;
virtual JSString *fun_toString(JSContext *cx, JSObject *wrapper, uintN indent) MOZ_OVERRIDE;
virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE;
virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g) MOZ_OVERRIDE;
virtual bool defaultValue(JSContext *cx, JSObject *wrapper, JSType hint, Value *vp) MOZ_OVERRIDE;
virtual bool iteratorNext(JSContext *cx, JSObject *wrapper, Value *vp) MOZ_OVERRIDE;
@ -181,7 +181,7 @@ class JS_FRIEND_API(SecurityWrapper) : public Base
virtual bool nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs args) MOZ_OVERRIDE;
virtual bool objectClassIs(JSObject *obj, ESClassValue classValue, JSContext *cx) MOZ_OVERRIDE;
virtual RegExpShared *regexp_toShared(JSContext *cx, JSObject *proxy) MOZ_OVERRIDE;
virtual bool regexp_toShared(JSContext *cx, JSObject *proxy, RegExpGuard *g) MOZ_OVERRIDE;
};
typedef SecurityWrapper<Wrapper> SameCompartmentSecurityWrapper;

Просмотреть файл

@ -237,7 +237,9 @@ JS_XDRInitBase(JSXDRState *xdr, JSXDRMode mode, JSContext *cx)
xdr->mode = mode;
xdr->cx = cx;
xdr->userdata = NULL;
xdr->state = NULL;
xdr->sharedFilename = NULL;
xdr->principals = NULL;
xdr->originPrincipals = NULL;
}
JS_PUBLIC_API(JSXDRState *)
@ -394,19 +396,6 @@ JS_XDRCString(JSXDRState *xdr, char **sp)
return JS_TRUE;
}
JS_PUBLIC_API(JSBool)
JS_XDRCStringOrNull(JSXDRState *xdr, char **sp)
{
uint32_t null = (*sp == NULL);
if (!JS_XDRUint32(xdr, &null))
return JS_FALSE;
if (null) {
*sp = NULL;
return JS_TRUE;
}
return JS_XDRCString(xdr, sp);
}
static JSBool
XDRChars(JSXDRState *xdr, jschar *chars, uint32_t nchars)
{
@ -543,44 +532,100 @@ js_XDRAtom(JSXDRState *xdr, JSAtom **atomp)
return JS_TRUE;
}
XDRScriptState::XDRScriptState(JSXDRState *x)
: xdr(x)
, filename(NULL)
, filenameSaved(false)
static bool
XDRPrincipals(JSXDRState *xdr)
{
JS_ASSERT(!xdr->state);
const uint8_t HAS_PRINCIPALS = 1;
const uint8_t HAS_ORIGIN = 2;
xdr->state = this;
uint8_t flags = 0;
if (xdr->mode == JSXDR_ENCODE) {
if (xdr->principals)
flags |= HAS_PRINCIPALS;
/*
* For the common case when principals == originPrincipals we want to
* avoid serializing the same principal twice. As originPrincipals are
* normalized and principals imply originPrincipals we simply set
* HAS_ORIGIN only if originPrincipals is set and different from
* principals. During decoding we re-normalize originPrincipals.
*/
JS_ASSERT_IF(xdr->principals, xdr->originPrincipals);
if (xdr->originPrincipals && xdr->originPrincipals != xdr->principals)
flags |= HAS_ORIGIN;
}
if (!JS_XDRUint8(xdr, &flags))
return false;
if (flags & (HAS_PRINCIPALS | HAS_ORIGIN)) {
JSSecurityCallbacks *scb = JS_GetSecurityCallbacks(xdr->cx);
if (xdr->mode == JSXDR_DECODE) {
if (!scb || !scb->principalsTranscoder) {
JS_ReportErrorNumber(xdr->cx, js_GetErrorMessage, NULL,
JSMSG_CANT_DECODE_PRINCIPALS);
return false;
}
} else {
JS_ASSERT(scb);
JS_ASSERT(scb->principalsTranscoder);
}
if (flags & HAS_PRINCIPALS) {
if (!scb->principalsTranscoder(xdr, &xdr->principals))
return false;
}
if (flags & HAS_ORIGIN) {
if (!scb->principalsTranscoder(xdr, &xdr->originPrincipals))
return false;
} else if (xdr->mode == JSXDR_DECODE && xdr->principals) {
xdr->originPrincipals = xdr->principals;
JSPRINCIPALS_HOLD(xdr->cx, xdr->principals);
}
}
return true;
}
XDRScriptState::~XDRScriptState()
{
xdr->state = NULL;
if (xdr->mode == JSXDR_DECODE && filename && !filenameSaved)
xdr->cx->free_((void *)filename);
}
namespace {
struct AutoDropXDRPrincipals {
JSXDRState *const xdr;
AutoDropXDRPrincipals(JSXDRState *xdr)
: xdr(xdr) { }
~AutoDropXDRPrincipals() {
if (xdr->mode == JSXDR_DECODE) {
if (xdr->principals)
JSPRINCIPALS_DROP(xdr->cx, xdr->principals);
if (xdr->originPrincipals)
JSPRINCIPALS_DROP(xdr->cx, xdr->originPrincipals);
}
xdr->principals = NULL;
xdr->originPrincipals = NULL;
}
};
} /* namespace anonymous */
JS_PUBLIC_API(JSBool)
JS_XDRFunctionObject(JSXDRState *xdr, JSObject **objp)
{
XDRScriptState fstate(xdr);
AutoDropXDRPrincipals drop(xdr);
if (xdr->mode == JSXDR_ENCODE) {
JSFunction* fun = (*objp)->toFunction();
fstate.filename = fun->script()->filename;
JSScript *script = (*objp)->toFunction()->script();
xdr->principals = script->principals;
xdr->originPrincipals = script->originPrincipals;
}
if (!JS_XDRCStringOrNull(xdr, (char **) &fstate.filename))
return false;
return XDRFunctionObject(xdr, objp);
return XDRPrincipals(xdr) && XDRFunctionObject(xdr, objp);
}
JS_PUBLIC_API(JSBool)
JS_XDRScript(JSXDRState *xdr, JSScript **scriptp)
{
JS_ASSERT(!xdr->state);
JSScript *script;
uint32_t magic;
uint32_t bytecodeVer;
@ -605,17 +650,16 @@ JS_XDRScript(JSXDRState *xdr, JSScript **scriptp)
return false;
}
XDRScriptState state(xdr);
if (!xdr->state)
return false;
if (xdr->mode == JSXDR_ENCODE)
state.filename = script->filename;
if (!JS_XDRCStringOrNull(xdr, (char **) &state.filename))
return false;
if (!XDRScript(xdr, &script))
return false;
{
AutoDropXDRPrincipals drop(xdr);
if (xdr->mode == JSXDR_ENCODE) {
xdr->principals = script->principals;
xdr->originPrincipals = script->originPrincipals;
}
if (!XDRPrincipals(xdr) || !XDRScript(xdr, &script))
return false;
}
if (xdr->mode == JSXDR_DECODE) {
JS_ASSERT(!script->compileAndGo);

Просмотреть файл

@ -105,28 +105,14 @@ typedef struct JSXDROps {
void (*finalize)(JSXDRState *);
} JSXDROps;
struct JSXDRState;
namespace js {
class XDRScriptState {
public:
XDRScriptState(JSXDRState *x);
~XDRScriptState();
JSXDRState *xdr;
const char *filename;
bool filenameSaved;
};
} /* namespace JS */
struct JSXDRState {
JSXDRMode mode;
JSXDROps *ops;
JSContext *cx;
void *userdata;
js::XDRScriptState *state;
const char *sharedFilename;
JSPrincipals *principals;
JSPrincipals *originPrincipals;
};
extern JS_PUBLIC_API(void)
@ -165,9 +151,6 @@ JS_XDRBytes(JSXDRState *xdr, char *bytes, uint32_t len);
extern JS_PUBLIC_API(JSBool)
JS_XDRCString(JSXDRState *xdr, char **sp);
extern JS_PUBLIC_API(JSBool)
JS_XDRCStringOrNull(JSXDRState *xdr, char **sp);
extern JS_PUBLIC_API(JSBool)
JS_XDRString(JSXDRState *xdr, JSString **strp);
@ -209,7 +192,9 @@ JS_XDRScript(JSXDRState *xdr, JSScript **scriptp);
* and saved versions. If deserialization fails, the data should be
* invalidated if possible.
*/
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 107)
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 108)
JS_END_EXTERN_C
/*
* Library-private functions.
@ -217,6 +202,14 @@ JS_XDRScript(JSXDRState *xdr, JSScript **scriptp);
extern JSBool
js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
JS_END_EXTERN_C
/*
* Set principals that should be assigned to decoded scripts and functions.
* The principals is not held via JS_HoldPrincipals/JS_DropPrincipals unless
* they are stored in a decoded script. Thus the caller must either ensure
* that principal outlive the XDR instance or are explicitly set to NULL
* before they release by the caller.
*/
extern void
js_XDRSetPrincipals(JSXDRState *xdr, JSPrincipals *principals, JSPrincipals *originPrincipals);
#endif /* ! jsxdrapi_h___ */

Просмотреть файл

@ -6944,7 +6944,8 @@ mjit::Compiler::jsop_regexp()
* RegExpShared. We don't do this during an incremental
* GC, since we don't discard JIT code after every marking slice.
*/
if (!reobj->getShared(cx))
RegExpGuard g;
if (!reobj->getShared(cx, &g))
return false;
RegisterID result = frame.allocReg();

Просмотреть файл

@ -1274,7 +1274,10 @@ GC(JSContext *cx, uintN argc, jsval *vp)
#endif
);
#endif
*vp = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, buf));
JSString *str = JS_NewStringCopyZ(cx, buf);
if (!str)
return false;
*vp = STRING_TO_JSVAL(str);
return true;
}

Просмотреть файл

@ -59,33 +59,34 @@ JSObject::asRegExp()
namespace js {
inline RegExpShared &
RegExpObject::shared() const
{
JS_ASSERT(JSObject::getPrivate() != NULL);
return *static_cast<RegExpShared *>(JSObject::getPrivate());
}
inline RegExpShared *
RegExpObject::maybeShared()
RegExpObject::maybeShared() const
{
return static_cast<RegExpShared *>(JSObject::getPrivate());
}
inline RegExpShared *
RegExpObject::getShared(JSContext *cx)
inline void
RegExpObject::shared(RegExpGuard *g) const
{
if (RegExpShared *shared = maybeShared())
return shared;
return createShared(cx);
JS_ASSERT(maybeShared() != NULL);
g->init(*maybeShared());
}
inline bool
RegExpObject::getShared(JSContext *cx, RegExpGuard *g)
{
if (RegExpShared *shared = maybeShared()) {
g->init(*shared);
return true;
}
return createShared(cx, g);
}
inline void
RegExpObject::setShared(JSContext *cx, RegExpShared *shared)
RegExpObject::setShared(JSContext *cx, RegExpShared &shared)
{
if (shared)
shared->prepareForUse(cx);
JSObject::setPrivate(shared);
shared.prepareForUse(cx);
JSObject::setPrivate(&shared);
}
inline void
@ -147,13 +148,13 @@ detail::RegExpCode::isJITRuntimeEnabled(JSContext *cx)
#endif
}
inline RegExpShared *
RegExpToShared(JSContext *cx, JSObject &obj)
inline bool
RegExpToShared(JSContext *cx, JSObject &obj, RegExpGuard *g)
{
JS_ASSERT(ObjectClassIs(obj, ESClass_RegExp, cx));
if (obj.isRegExp())
return obj.asRegExp().getShared(cx);
return Proxy::regexp_toShared(cx, &obj);
return obj.asRegExp().getShared(cx, g);
return Proxy::regexp_toShared(cx, &obj, g);
}
inline void

Просмотреть файл

@ -60,10 +60,7 @@ JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
RegExpObjectBuilder::RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj)
: cx(cx), reobj_(reobj)
{
if (reobj_)
reobj_->setShared(cx, NULL);
}
{}
bool
RegExpObjectBuilder::getOrCreate()
@ -103,7 +100,7 @@ RegExpObjectBuilder::build(JSAtom *source, RegExpShared &shared)
if (!reobj_->init(cx, source, shared.getFlags()))
return NULL;
reobj_->setShared(cx, &shared);
reobj_->setShared(cx, shared);
return reobj_;
}
@ -135,11 +132,11 @@ RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto)
return build(other->getSource(), newFlags);
}
RegExpShared *toShare = other->getShared(cx);
if (!toShare)
RegExpGuard g;
if (!other->getShared(cx, &g))
return NULL;
return build(other->getSource(), *toShare);
return build(other->getSource(), *g);
}
/* MatchPairs */
@ -393,16 +390,15 @@ RegExpObject::createNoStatics(JSContext *cx, JSAtom *source, RegExpFlag flags,
return builder.build(source, flags);
}
RegExpShared *
RegExpObject::createShared(JSContext *cx)
bool
RegExpObject::createShared(JSContext *cx, RegExpGuard *g)
{
JS_ASSERT(!maybeShared());
RegExpShared *shared = cx->compartment->regExps.get(cx, getSource(), getFlags());
if (!shared)
return NULL;
if (!cx->compartment->regExps.get(cx, getSource(), getFlags(), g))
return false;
setShared(cx, shared);
return shared;
setShared(cx, **g);
return true;
}
Shape *
@ -468,7 +464,12 @@ RegExpObject::init(JSContext *cx, JSAtom *source, RegExpFlag flags)
MULTILINE_FLAG_SLOT);
JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot() == STICKY_FLAG_SLOT);
JS_ASSERT(!maybeShared());
/*
* If this is a re-initialization with an existing RegExpShared, 'flags'
* may not match getShared()->flags, so forget the RegExpShared.
*/
JSObject::setPrivate(NULL);
zeroLastIndex();
setSource(source);
setGlobal(flags & GlobalFlag);
@ -482,10 +483,10 @@ RegExpRunStatus
RegExpObject::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
MatchPairs **output)
{
RegExpShared *shared = getShared(cx);
if (!shared)
RegExpGuard g;
if (!getShared(cx, &g))
return RegExpRunStatus_Error;
return shared->execute(cx, chars, length, lastIndex, output);
return g->execute(cx, chars, length, lastIndex, output);
}
JSFlatString *
@ -627,18 +628,24 @@ RegExpCompartment::sweep(JSRuntime *rt)
}
}
inline RegExpShared *
RegExpCompartment::get(JSContext *cx, JSAtom *keyAtom, JSAtom *source, RegExpFlag flags, Type type)
inline bool
RegExpCompartment::get(JSContext *cx, JSAtom *keyAtom, JSAtom *source, RegExpFlag flags, Type type,
RegExpGuard *g)
{
DebugOnly<uint64_t> gcNumberBefore = cx->runtime->gcNumber;
Key key(keyAtom, flags, type);
Map::AddPtr p = map_.lookupForAdd(key);
if (p)
return p->value;
if (p) {
g->init(*p->value);
return true;
}
RegExpShared *shared = cx->runtime->new_<RegExpShared>(cx->runtime, flags);
if (!shared || !shared->compile(cx, source))
if (!shared)
goto error;
if (!shared->compile(cx, source))
goto error;
/*
@ -650,42 +657,51 @@ RegExpCompartment::get(JSContext *cx, JSAtom *keyAtom, JSAtom *source, RegExpFla
if (!map_.add(p, key, shared))
goto error;
return shared;
/*
* Since 'error' deletes 'shared', only guard 'shared' on success. This is
* safe since 'shared' cannot be deleted by GC until after the call to
* map_.add() directly above.
*/
g->init(*shared);
return true;
error:
Foreground::delete_(shared);
js_ReportOutOfMemory(cx);
return NULL;
return false;
}
RegExpShared *
RegExpCompartment::get(JSContext *cx, JSAtom *source, RegExpFlag flags)
bool
RegExpCompartment::get(JSContext *cx, JSAtom *source, RegExpFlag flags, RegExpGuard *g)
{
return get(cx, source, source, flags, Normal);
return get(cx, source, source, flags, Normal, g);
}
RegExpShared *
RegExpCompartment::getHack(JSContext *cx, JSAtom *source, JSAtom *hackedSource, RegExpFlag flags)
bool
RegExpCompartment::getHack(JSContext *cx, JSAtom *source, JSAtom *hackedSource, RegExpFlag flags,
RegExpGuard *g)
{
return get(cx, source, hackedSource, flags, Hack);
return get(cx, source, hackedSource, flags, Hack, g);
}
RegExpShared *
RegExpCompartment::lookupHack(JSContext *cx, JSAtom *source, RegExpFlag flags)
bool
RegExpCompartment::lookupHack(JSAtom *source, RegExpFlag flags, JSContext *cx, RegExpGuard *g)
{
if (Map::Ptr p = map_.lookup(Key(source, flags, Hack)))
return p->value;
return NULL;
if (Map::Ptr p = map_.lookup(Key(source, flags, Hack))) {
g->init(*p->value);
return true;
}
return false;
}
RegExpShared *
RegExpCompartment::get(JSContext *cx, JSAtom *atom, JSString *opt)
bool
RegExpCompartment::get(JSContext *cx, JSAtom *atom, JSString *opt, RegExpGuard *g)
{
RegExpFlag flags = RegExpFlag(0);
if (opt && !ParseRegExpFlags(cx, opt, &flags))
return NULL;
return false;
return get(cx, atom, flags);
return get(cx, atom, flags, g);
}
/* Functions */

Просмотреть файл

@ -74,7 +74,7 @@
* up in a per-compartment table to allow reuse between objects. Lastly, on
* GC, every RegExpShared (that is not active on the callstack) is discarded.
* Because of the last point, any code using a RegExpShared (viz., by executing
* a regexp) must indicate the RegExpShared is active via RegExpShared::Guard.
* a regexp) must indicate the RegExpShared is active via RegExpGuard.
*/
namespace js {
@ -85,117 +85,6 @@ enum RegExpRunStatus
RegExpRunStatus_Success_NotFound
};
class RegExpObject : public JSObject
{
typedef detail::RegExpCode RegExpCode;
static const uintN LAST_INDEX_SLOT = 0;
static const uintN SOURCE_SLOT = 1;
static const uintN GLOBAL_FLAG_SLOT = 2;
static const uintN IGNORE_CASE_FLAG_SLOT = 3;
static const uintN MULTILINE_FLAG_SLOT = 4;
static const uintN STICKY_FLAG_SLOT = 5;
public:
static const uintN RESERVED_SLOTS = 6;
/*
* Note: The regexp statics flags are OR'd into the provided flags,
* so this function is really meant for object creation during code
* execution, as opposed to during something like XDR.
*/
static RegExpObject *
create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
RegExpFlag flags, TokenStream *ts);
static RegExpObject *
createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags,
TokenStream *ts);
static RegExpObject *
createNoStatics(JSContext *cx, JSAtom *atom, RegExpFlag flags, TokenStream *ts);
/*
* Run the regular expression over the input text.
*
* Results are placed in |output| as integer pairs. For eaxmple,
* |output[0]| and |output[1]| represent the text indices that make
* up the "0" (whole match) pair. Capturing parens will result in
* more output.
*
* N.B. it's the responsibility of the caller to hook the |output|
* into the |RegExpStatics| appropriately, if necessary.
*/
RegExpRunStatus
execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
MatchPairs **output);
/* Accessors. */
const Value &getLastIndex() const {
return getSlot(LAST_INDEX_SLOT);
}
inline void setLastIndex(const Value &v);
inline void setLastIndex(double d);
inline void zeroLastIndex();
JSFlatString *toString(JSContext *cx) const;
JSAtom *getSource() const {
return &getSlot(SOURCE_SLOT).toString()->asAtom();
}
inline void setSource(JSAtom *source);
RegExpFlag getFlags() const {
uintN flags = 0;
flags |= global() ? GlobalFlag : 0;
flags |= ignoreCase() ? IgnoreCaseFlag : 0;
flags |= multiline() ? MultilineFlag : 0;
flags |= sticky() ? StickyFlag : 0;
return RegExpFlag(flags);
}
/* Flags. */
inline void setIgnoreCase(bool enabled);
inline void setGlobal(bool enabled);
inline void setMultiline(bool enabled);
inline void setSticky(bool enabled);
bool ignoreCase() const { return getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean(); }
bool global() const { return getSlot(GLOBAL_FLAG_SLOT).toBoolean(); }
bool multiline() const { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
bool sticky() const { return getSlot(STICKY_FLAG_SLOT).toBoolean(); }
inline RegExpShared &shared() const;
inline RegExpShared *maybeShared();
inline RegExpShared *getShared(JSContext *cx);
inline void setShared(JSContext *cx, RegExpShared *shared);
private:
friend class RegExpObjectBuilder;
/*
* Compute the initial shape to associate with fresh RegExp objects,
* encoding their initial properties. Return the shape after
* changing this regular expression object's last property to it.
*/
Shape *assignInitialShape(JSContext *cx);
inline bool init(JSContext *cx, JSAtom *source, RegExpFlag flags);
/*
* Precondition: the syntax for |source| has already been validated.
* Side effect: sets the private field.
*/
RegExpShared *createShared(JSContext *cx);
RegExpObject() MOZ_DELETE;
RegExpObject &operator=(const RegExpObject &reo) MOZ_DELETE;
/* Call setShared in preference to setPrivate. */
void setPrivate(void *priv) MOZ_DELETE;
};
class RegExpObjectBuilder
{
JSContext *cx;
@ -320,6 +209,7 @@ class RegExpCode
class RegExpShared
{
friend class RegExpCompartment;
friend class RegExpGuard;
detail::RegExpCode code;
uintN parenCount;
@ -333,34 +223,6 @@ class RegExpShared
JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
public:
/*
* Extend the lifetime of a given RegExpShared to at least the lifetime of
* the Guard object. See Regular Expression comment at the top.
*/
class Guard {
RegExpShared *re_;
Guard(const Guard &) MOZ_DELETE;
void operator=(const Guard &) MOZ_DELETE;
public:
Guard() : re_(NULL) {}
Guard(RegExpShared &re) : re_(&re) {
re_->activeUseCount++;
}
void init(RegExpShared &re) {
JS_ASSERT(!re_);
re_ = &re;
re_->activeUseCount++;
}
~Guard() {
if (re_) {
JS_ASSERT(re_->activeUseCount > 0);
re_->activeUseCount--;
}
}
bool initialized() const { return !!re_; }
RegExpShared *operator->() { JS_ASSERT(initialized()); return re_; }
RegExpShared &operator*() { JS_ASSERT(initialized()); return *re_; }
};
/* Called when a RegExpShared is installed into a RegExpObject. */
inline void prepareForUse(JSContext *cx);
@ -385,6 +247,36 @@ class RegExpShared
bool sticky() const { return flags & StickyFlag; }
};
/*
* Extend the lifetime of a given RegExpShared to at least the lifetime of
* the guard object. See Regular Expression comment at the top.
*/
class RegExpGuard
{
RegExpShared *re_;
RegExpGuard(const RegExpGuard &) MOZ_DELETE;
void operator=(const RegExpGuard &) MOZ_DELETE;
public:
RegExpGuard() : re_(NULL) {}
RegExpGuard(RegExpShared &re) : re_(&re) {
re_->activeUseCount++;
}
void init(RegExpShared &re) {
JS_ASSERT(!re_);
re_ = &re;
re_->activeUseCount++;
}
~RegExpGuard() {
if (re_) {
JS_ASSERT(re_->activeUseCount > 0);
re_->activeUseCount--;
}
}
bool initialized() const { return !!re_; }
RegExpShared *operator->() { JS_ASSERT(initialized()); return re_; }
RegExpShared &operator*() { JS_ASSERT(initialized()); return *re_; }
};
class RegExpCompartment
{
enum Type { Normal = 0x0, Hack = 0x1 };
@ -408,7 +300,8 @@ class RegExpCompartment
typedef HashMap<Key, RegExpShared *, Key, RuntimeAllocPolicy> Map;
Map map_;
RegExpShared *get(JSContext *cx, JSAtom *key, JSAtom *source, RegExpFlag flags, Type type);
bool get(JSContext *cx, JSAtom *key, JSAtom *source, RegExpFlag flags, Type type,
RegExpGuard *g);
public:
RegExpCompartment(JSRuntime *rt);
@ -418,10 +311,10 @@ class RegExpCompartment
void sweep(JSRuntime *rt);
/* Return a regexp corresponding to the given (source, flags) pair. */
RegExpShared *get(JSContext *cx, JSAtom *source, RegExpFlag flags);
bool get(JSContext *cx, JSAtom *source, RegExpFlag flags, RegExpGuard *g);
/* Like 'get', but compile 'maybeOpt' (if non-null). */
RegExpShared *get(JSContext *cx, JSAtom *source, JSString *maybeOpt);
bool get(JSContext *cx, JSAtom *source, JSString *maybeOpt, RegExpGuard *g);
/*
* A 'hacked' RegExpShared is one where the input 'source' doesn't match
@ -434,14 +327,126 @@ class RegExpCompartment
* only applies to 'getHack': a single 'source' value may be passed to both
* 'get' and 'getHack'.
*/
RegExpShared *getHack(JSContext *cx, JSAtom *source, JSAtom *hackedSource, RegExpFlag flags);
bool getHack(JSContext *cx, JSAtom *source, JSAtom *hackedSource, RegExpFlag flags,
RegExpGuard *g);
/*
* To avoid atomizing 'hackedSource', callers may call 'lookupHack',
* passing only the original 'source'. Due to the abovementioned unique
* mapping property, 'hackedSource' is unambiguous.
*/
RegExpShared *lookupHack(JSContext *cx, JSAtom *source, RegExpFlag flags);
bool lookupHack(JSAtom *source, RegExpFlag flags, JSContext *cx, RegExpGuard *g);
};
class RegExpObject : public JSObject
{
typedef detail::RegExpCode RegExpCode;
static const uintN LAST_INDEX_SLOT = 0;
static const uintN SOURCE_SLOT = 1;
static const uintN GLOBAL_FLAG_SLOT = 2;
static const uintN IGNORE_CASE_FLAG_SLOT = 3;
static const uintN MULTILINE_FLAG_SLOT = 4;
static const uintN STICKY_FLAG_SLOT = 5;
public:
static const uintN RESERVED_SLOTS = 6;
/*
* Note: The regexp statics flags are OR'd into the provided flags,
* so this function is really meant for object creation during code
* execution, as opposed to during something like XDR.
*/
static RegExpObject *
create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
RegExpFlag flags, TokenStream *ts);
static RegExpObject *
createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags,
TokenStream *ts);
static RegExpObject *
createNoStatics(JSContext *cx, JSAtom *atom, RegExpFlag flags, TokenStream *ts);
/*
* Run the regular expression over the input text.
*
* Results are placed in |output| as integer pairs. For eaxmple,
* |output[0]| and |output[1]| represent the text indices that make
* up the "0" (whole match) pair. Capturing parens will result in
* more output.
*
* N.B. it's the responsibility of the caller to hook the |output|
* into the |RegExpStatics| appropriately, if necessary.
*/
RegExpRunStatus
execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
MatchPairs **output);
/* Accessors. */
const Value &getLastIndex() const {
return getSlot(LAST_INDEX_SLOT);
}
inline void setLastIndex(const Value &v);
inline void setLastIndex(double d);
inline void zeroLastIndex();
JSFlatString *toString(JSContext *cx) const;
JSAtom *getSource() const {
return &getSlot(SOURCE_SLOT).toString()->asAtom();
}
inline void setSource(JSAtom *source);
RegExpFlag getFlags() const {
uintN flags = 0;
flags |= global() ? GlobalFlag : 0;
flags |= ignoreCase() ? IgnoreCaseFlag : 0;
flags |= multiline() ? MultilineFlag : 0;
flags |= sticky() ? StickyFlag : 0;
return RegExpFlag(flags);
}
/* Flags. */
inline void setIgnoreCase(bool enabled);
inline void setGlobal(bool enabled);
inline void setMultiline(bool enabled);
inline void setSticky(bool enabled);
bool ignoreCase() const { return getSlot(IGNORE_CASE_FLAG_SLOT).toBoolean(); }
bool global() const { return getSlot(GLOBAL_FLAG_SLOT).toBoolean(); }
bool multiline() const { return getSlot(MULTILINE_FLAG_SLOT).toBoolean(); }
bool sticky() const { return getSlot(STICKY_FLAG_SLOT).toBoolean(); }
inline void shared(RegExpGuard *g) const;
inline bool getShared(JSContext *cx, RegExpGuard *g);
inline void setShared(JSContext *cx, RegExpShared &shared);
private:
friend class RegExpObjectBuilder;
/*
* Compute the initial shape to associate with fresh RegExp objects,
* encoding their initial properties. Return the shape after
* changing this regular expression object's last property to it.
*/
Shape *assignInitialShape(JSContext *cx);
inline bool init(JSContext *cx, JSAtom *source, RegExpFlag flags);
/*
* Precondition: the syntax for |source| has already been validated.
* Side effect: sets the private field.
*/
bool createShared(JSContext *cx, RegExpGuard *g);
RegExpShared *maybeShared() const;
RegExpObject() MOZ_DELETE;
RegExpObject &operator=(const RegExpObject &reo) MOZ_DELETE;
/* Call setShared in preference to setPrivate. */
void setPrivate(void *priv) MOZ_DELETE;
};
/*
@ -457,12 +462,12 @@ ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut);
* Assuming ObjectClassIs(obj, ESClass_RegExp), return obj's RegExpShared.
*
* Beware: this RegExpShared can be owned by a compartment other than
* cx->compartment. Normal RegExpShared::Guard (which is necessary anyways)
* cx->compartment. Normal RegExpGuard (which is necessary anyways)
* will protect the object but it is important not to assign the return value
* to be the private of any RegExpObject.
*/
inline RegExpShared *
RegExpToShared(JSContext *cx, JSObject &obj);
inline bool
RegExpToShared(JSContext *cx, JSObject &obj, RegExpGuard *g);
bool
XDRScriptRegExpObject(JSXDRState *xdr, HeapPtrObject *objp);

Просмотреть файл

@ -736,6 +736,8 @@ ContextStack::pushInvokeArgs(JSContext *cx, uintN argc, InvokeArgsGuard *iag)
if (!firstUnused)
return false;
MakeRangeGCSafe(firstUnused, argc);
ImplicitCast<CallArgs>(*iag) = CallArgsFromVp(argc, firstUnused);
seg_->pushCall(*iag);

Просмотреть файл

@ -95,10 +95,10 @@ namespace detail {
*
* SpiderMonkey uses a per-thread stack to store the activation records,
* parameters, locals, and expression temporaries for the stack of actively
* executing scripts, functions and generators. The per-thread stack is owned
* by the StackSpace object stored in the thread's ThreadData.
* executing scripts, functions and generators. The stack is owned by the
* StackSpace object stored in the runtime.
*
* The per-thread stack is subdivided into contiguous segments of memory which
* The stack is subdivided into contiguous segments of memory which
* have a memory layout invariant that allows fixed offsets to be used for stack
* access (by jit code) as well as fast call/return. This memory layout is
* encapsulated by a set of types that describe different regions of memory.

Просмотреть файл

@ -491,27 +491,29 @@ StaticStrings::init(JSContext *cx)
}
}
initialized = true;
return true;
}
void
StaticStrings::trace(JSTracer *trc)
{
if (!initialized)
return;
/* These strings never change, so barriers are not needed. */
for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++)
MarkStringUnbarriered(trc, unitStaticTable[i], "unit-static-string");
for (uint32_t i = 0; i < UNIT_STATIC_LIMIT; i++) {
if (JSAtom *atom = unitStaticTable[i])
MarkStringUnbarriered(trc, atom, "unit-static-string");
}
for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++)
MarkStringUnbarriered(trc, length2StaticTable[i], "length2-static-string");
for (uint32_t i = 0; i < NUM_SMALL_CHARS * NUM_SMALL_CHARS; i++) {
if (JSAtom *atom = length2StaticTable[i])
MarkStringUnbarriered(trc, atom, "length2-static-string");
}
/* This may mark some strings more than once, but so be it. */
for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++)
MarkStringUnbarriered(trc, intStaticTable[i], "int-static-string");
for (uint32_t i = 0; i < INT_STATIC_LIMIT; i++) {
if (JSAtom *atom = intStaticTable[i])
MarkStringUnbarriered(trc, atom, "int-static-string");
}
}
bool

Просмотреть файл

@ -696,8 +696,6 @@ namespace js {
class StaticStrings
{
private:
bool initialized;
/* Bigger chars cannot be in a length-2 string. */
static const size_t SMALL_CHAR_LIMIT = 128U;
static const size_t NUM_SMALL_CHARS = 64U;
@ -712,7 +710,11 @@ class StaticStrings
static const size_t UNIT_STATIC_LIMIT = 256U;
JSAtom *unitStaticTable[UNIT_STATIC_LIMIT];
StaticStrings() : initialized(false) {}
StaticStrings() {
PodArrayZero(unitStaticTable);
PodArrayZero(length2StaticTable);
PodArrayZero(intStaticTable);
}
bool init(JSContext *cx);
void trace(JSTracer *trc);

Просмотреть файл

@ -1164,19 +1164,21 @@ mozJSComponentLoader::ImportInto(const nsACString & aLocation,
if (!newEntry || !mInProgressImports.Put(key, newEntry))
return NS_ERROR_OUT_OF_MEMORY;
jsval exception = JSVAL_VOID;
JS::Anchor<jsval> exception(JSVAL_VOID);
rv = GlobalForLocation(sourceLocalFile, resURI, &newEntry->global,
&newEntry->location, &exception);
&newEntry->location, &exception.get());
mInProgressImports.Remove(key);
if (NS_FAILED(rv)) {
*_retval = nsnull;
if (!JSVAL_IS_VOID(exception)) {
if (!JSVAL_IS_VOID(exception.get())) {
// An exception was thrown during compilation. Propagate it
// out to our caller so they can report it.
JS_SetPendingException(callercx, exception);
if (!JS_WrapValue(callercx, &exception.get()))
return NS_ERROR_OUT_OF_MEMORY;
JS_SetPendingException(callercx, exception.get());
return NS_OK;
}

Просмотреть файл

@ -5350,6 +5350,7 @@ PresShell::ProcessSynthMouseMoveEvent(bool aFromScroll)
// If drag session has started, we shouldn't synthesize mousemove event.
nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
if (dragSession) {
mSynthMouseMoveEvent.Forget();
return;
}

Просмотреть файл

@ -5117,7 +5117,9 @@ GetNearestBlockContainer(nsIFrame* frame)
// another such pseudo, this shouldn't cause anything bad to happen.
// Also the anonymous blocks inside table cells are not containing blocks.
while (frame->IsFrameOfType(nsIFrame::eLineParticipant) ||
frame->IsBlockWrapper()) {
frame->IsBlockWrapper() ||
// Table rows are not containing blocks either
frame->GetType() == nsGkAtoms::tableRowFrame) {
frame = frame->GetParent();
NS_ASSERTION(frame, "How come we got to the root frame without seeing a containing block?");
}

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<body>
<table style="width: 100px; height: 100px;">
<tbody style="height: 100%;">
<tr style="height: 100%">
<td style="height: 100%;">
<div style="height: 100%; width: 100%;overflow:scroll;">
<div style="height: 200px">
</div>
Some text
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html>
<body>
<table style="width: 100px; height: 100px;">
<tbody style="height: 100%;"> <!-- required for older ffox versions -->
<tr>
<td style="height: 100%;">
<div style="height: 100%; width: 100%;overflow:scroll;">
<div style="height: 200px">
</div>
Some text
</div>
</td>
</tr>
</tbody>
</table>
</body>
</html>

Просмотреть файл

@ -1693,3 +1693,4 @@ needs-focus != 703186-1.html 703186-2.html
fuzzy-if(d2d,1,19) fuzzy-if(cocoaWidget,1,170) == 718521.html 718521-ref.html
== 720987.html 720987-ref.html
== 722923-1.html 722923-1-ref.html
== 729143-1.html 729143-1-ref.html

Просмотреть файл

@ -0,0 +1,27 @@
<svg xmlns="http://www.w3.org/2000/svg"
class="reftest-wait"
onload="go()">
<!-- Bug 728758 - Removing a stacked animation fails to update -->
<!-- In this test we check that removing an animation applied on top of
another animation causes the underlying animation's result to show. -->
<script>
function go() {
document.documentElement.pauseAnimations();
// Force a sample after starting the bottom animation, but before starting
// the top animation.
document.documentElement.setCurrentTime(0.5);
// Sample again after the top animation has started
document.documentElement.setCurrentTime(1);
// Remove top animation
var anim = document.getElementById("anim");
anim.parentNode.removeChild(anim);
// Sample again
document.documentElement.setCurrentTime(1);
document.documentElement.removeAttribute("class");
}
</script>
<rect x="15" y="15" width="200" height="200" fill="orange">
<set attributeName="fill" to="blue"/>
<set attributeName="fill" to="red" begin="1s" id="anim"/>
</rect>
</svg>

После

Ширина:  |  Высота:  |  Размер: 1.1 KiB

Просмотреть файл

@ -171,6 +171,7 @@ fuzzy-if(/^Windows\x20NT\x205\.1/.test(http.oscpu),2,1) == anim-path-d-01.svg an
== anim-remove-7.svg anim-standard-ref.svg
== anim-remove-8css.svg anim-standard-ref.svg
== anim-remove-8xml.svg anim-standard-ref.svg
== anim-remove-9.svg anim-standard-ref.svg
== anim-retarget-1.svg anim-standard-ref.svg
== anim-retarget-2.svg anim-standard-ref.svg
== anim-retarget-3.svg anim-standard-ref.svg

Просмотреть файл

@ -100,7 +100,9 @@
<img class="favicon"/>
<div class="inner">
<div class="details">
<div class="title"></div><div class="version"></div><div class="tag"></div>
<div class="tag"></div>
<div class="title"></div>
<div class="version"></div>
</div>
<div class="description-full"></div>
</div>
@ -144,6 +146,7 @@
AddonManager.addInstallListener(Addons);
Addons.getAddons();
showList();
}
function uninit() {
@ -170,14 +173,18 @@
let detailItem = document.querySelector("#addons-details > .addon-item");
detailItem.addon = null;
// Hide the detail page and show the list
let details = document.querySelector("#addons-details");
details.style.display = "none";
let list = document.querySelector("#addons-list");
list.style.display = "block";
showList();
}
}
function showList() {
// Hide the detail page and show the list
let details = document.querySelector("#addons-details");
details.style.display = "none";
let list = document.querySelector("#addons-list");
list.style.display = "block";
}
var Addons = {
_createItem: function _createItem(aAddon) {
let outer = document.createElement("div");
@ -201,6 +208,11 @@
details.className = "details";
inner.appendChild(details);
let tagPart = document.createElement("div");
tagPart.textContent = gStringBundle.GetStringFromName("addonType." + aAddon.type);
tagPart.className = "tag";
details.appendChild(tagPart);
let titlePart = document.createElement("div");
titlePart.textContent = aAddon.name;
titlePart.className = "title";
@ -211,11 +223,6 @@
versionPart.className = "version";
details.appendChild(versionPart);
let tagPart = document.createElement("div");
tagPart.textContent = gStringBundle.GetStringFromName("addonType." + aAddon.type);
tagPart.className = "tag";
details.appendChild(tagPart);
if ("description" in aAddon) {
let descPart = document.createElement("div");
descPart.textContent = aAddon.description;
@ -276,9 +283,6 @@
list.appendChild(item);
}
list.style.display = "block";
document.getElementById("addons-header").setAttribute("showlist", "true");
// Load the search engines
let defaults = Services.search.getDefaultEngines({ }).map(function (e) e.name);
function isDefault(aEngine)
@ -325,10 +329,14 @@
detailItem.setAttribute("isDisabled", aListItem.getAttribute("isDisabled"));
detailItem.setAttribute("opType", aListItem.getAttribute("opType"));
detailItem.setAttribute("optionsURL", aListItem.getAttribute("optionsURL"));
detailItem.addon = aListItem.addon;
let addon = detailItem.addon = aListItem.addon;
let favicon = document.querySelector("#addons-details > .addon-item .favicon");
if (addon.iconURL)
favicon.setAttribute("src", addon.iconURL);
else
favicon.removeAttribute("src");
let addon = detailItem.addon;
document.querySelector("#addons-details > .addon-item .favicon").setAttribute("src", addon.iconURL);
document.querySelector("#addons-details > .addon-item .title").textContent = addon.name;
document.querySelector("#addons-details > .addon-item .version").textContent = addon.version;
document.querySelector("#addons-details > .addon-item .tag").textContent = gStringBundle.GetStringFromName("addonType." + addon.type);

Просмотреть файл

@ -98,15 +98,12 @@ body {
}
.details {
display: -moz-box;
-moz-box-orient: horizontal;
width: 100%;
margin-top: 8px;
padding-top: 8px;
}
.details > div {
display: -moz-box;
-moz-box-align: end;
display: inline;
}
.title {
@ -117,19 +114,15 @@ body {
.version {
/* title is not localized, so keep the margin on the left side */
margin-left: 12px;
/* push the tag to the end of the box */
-moz-box-flex: 1;
/* compensate for the title baseline */
padding-bottom: 1px;
}
.tag {
text-align: end;
float: right;
margin-left: 12px;
/* compensate for the title baseline */
padding-bottom: 1px;
position: relative;
bottom: -3px;
}
.description {

Просмотреть файл

@ -817,6 +817,8 @@ pref("network.http.spdy.chunk-size", 4096);
pref("network.http.spdy.timeout", 180);
pref("network.http.spdy.coalesce-hostnames", true);
pref("network.http.spdy.use-alternate-protocol", true);
pref("network.http.spdy.ping-threshold", 44);
pref("network.http.spdy.ping-timeout", 8);
// default values for FTP
// in a DSCP environment this should be 40 (0x28, or AF11), per RFC-4594,

Просмотреть файл

@ -90,7 +90,10 @@ SpdySession::SpdySession(nsAHttpTransaction *aHttpTransaction,
mServerPushedResources(0),
mOutputQueueSize(kDefaultQueueSize),
mOutputQueueUsed(0),
mOutputQueueSent(0)
mOutputQueueSent(0),
mLastReadEpoch(PR_IntervalNow()),
mPingSentEpoch(0),
mNextPingID(1)
{
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
@ -107,6 +110,7 @@ SpdySession::SpdySession(nsAHttpTransaction *aHttpTransaction,
mSendingChunkSize = gHttpHandler->SpdySendingChunkSize();
AddStream(aHttpTransaction, firstPriority);
mLastDataReadEpoch = mLastReadEpoch;
}
PLDHashOperator
@ -212,6 +216,63 @@ SpdySession::RoomForMoreStreams()
return !mShouldGoAway;
}
PRIntervalTime
SpdySession::IdleTime()
{
return PR_IntervalNow() - mLastDataReadEpoch;
}
void
SpdySession::ReadTimeoutTick(PRIntervalTime now)
{
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
NS_ABORT_IF_FALSE(mNextPingID & 1, "Ping Counter Not Odd");
PRIntervalTime threshold = gHttpHandler->SpdyPingThreshold();
if (!threshold)
return;
LOG(("SpdySession::ReadTimeoutTick %p delta since last read %ds\n",
this, PR_IntervalToSeconds(now - mLastReadEpoch)));
if ((now - mLastReadEpoch) < threshold) {
// recent activity means ping is not an issue
mPingSentEpoch = 0;
return;
}
if (mPingSentEpoch) {
LOG(("SpdySession::ReadTimeoutTick %p handle outstanding ping\n"));
if ((now - mPingSentEpoch) >= gHttpHandler->SpdyPingTimeout()) {
LOG(("SpdySession::ReadTimeoutTick %p Ping Timer Exhaustion\n",
this));
Close(NS_ERROR_NET_TIMEOUT);
}
return;
}
LOG(("SpdySession::ReadTimeoutTick %p generating ping 0x%x\n",
this, mNextPingID));
if (mNextPingID == 0xffffffff) {
LOG(("SpdySession::ReadTimeoutTick %p cannot form ping - ids exhausted\n",
this));
return;
}
mPingSentEpoch = PR_IntervalNow();
if (!mPingSentEpoch)
mPingSentEpoch = 1; // avoid the 0 sentinel value
GeneratePing(mNextPingID);
mNextPingID += 2;
if (mNextPingID == 0xffffffff) {
LOG(("SpdySession::ReadTimeoutTick %p "
"ping ids exhausted marking goaway\n", this));
mShouldGoAway = true;
}
}
PRUint32
SpdySession::RegisterStreamID(SpdyStream *stream)
{
@ -307,6 +368,18 @@ SpdySession::ProcessPending()
}
}
nsresult
SpdySession::NetworkRead(nsAHttpSegmentWriter *writer, char *buf,
PRUint32 count, PRUint32 *countWritten)
{
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
nsresult rv = writer->OnWriteSegment(buf, count, countWritten);
if (NS_SUCCEEDED(rv) && *countWritten > 0)
mLastReadEpoch = PR_IntervalNow();
return rv;
}
void
SpdySession::SetWriteCallbacks()
{
@ -942,6 +1015,7 @@ SpdySession::HandleSynReplyForValidStream()
return rv;
mInputFrameDataStream->UpdateTransportReadEvents(mInputFrameDataSize);
mLastDataReadEpoch = mLastReadEpoch;
ChangeDownstreamState(PROCESSING_CONTROL_SYN_REPLY);
return NS_OK;
}
@ -1112,12 +1186,11 @@ SpdySession::HandlePing(SpdySession *self)
LOG3(("SpdySession::HandlePing %p PING ID 0x%X.", self, pingID));
if (pingID & 0x01) {
// We never expect to see an odd PING beacuse we never generate PING.
// The spec mandates ignoring this
LOG3(("SpdySession::HandlePing %p PING ID from server was odd.",
self));
// presumably a reply to our timeout ping
self->mPingSentEpoch = 0;
}
else {
// Servers initiate even numbered pings, go ahead and echo it back
self->GeneratePing(pingID);
}
@ -1173,6 +1246,7 @@ SpdySession::HandleHeaders(SpdySession *self)
LOG3(("SpdySession::HandleHeaders %p HEADERS for Stream 0x%X. "
"They are ignored in the HTTP/SPDY mapping.",
self, streamID));
self->mLastDataReadEpoch = self->mLastReadEpoch;
self->ResetDownstreamState();
return NS_OK;
}
@ -1348,8 +1422,8 @@ SpdySession::ReadSegments(nsAHttpSegmentReader *reader,
// OnWriteSegment(). That function will gateway it into http and feed
// it to the appropriate transaction.
// we call writer->OnWriteSegment to get a spdy header.. and decide if it is
// data or control.. if it is control, just deal with it.
// we call writer->OnWriteSegment via NetworkRead() to get a spdy header..
// and decide if it is data or control.. if it is control, just deal with it.
// if it is data, identify the spdy stream
// call stream->WriteSegemnts which can call this::OnWriteSegment to get the
// data. It always gets full frames if they are part of the stream
@ -1381,9 +1455,9 @@ SpdySession::WriteSegments(nsAHttpSegmentWriter *writer,
NS_ABORT_IF_FALSE(mInputFrameBufferUsed < 8,
"Frame Buffer Used Too Large for State");
rv = writer->OnWriteSegment(mInputFrameBuffer + mInputFrameBufferUsed,
8 - mInputFrameBufferUsed,
countWritten);
rv = NetworkRead(writer, mInputFrameBuffer + mInputFrameBufferUsed,
8 - mInputFrameBufferUsed, countWritten);
if (NS_FAILED(rv)) {
LOG3(("SpdySession %p buffering frame header read failure %x\n",
this, rv));
@ -1462,6 +1536,7 @@ SpdySession::WriteSegments(nsAHttpSegmentWriter *writer,
"Session=%p Stream ID 0x%x Stream Ptr %p Fin=%d Len=%d",
this, streamID, mInputFrameDataStream, mInputFrameDataLast,
mInputFrameDataSize));
mLastDataReadEpoch = mLastReadEpoch;
if (mInputFrameBuffer[4] & kFlag_Data_ZLIB) {
LOG3(("Data flag has ZLIB flag set which is not valid >=2 spdy"));
@ -1499,6 +1574,8 @@ SpdySession::WriteSegments(nsAHttpSegmentWriter *writer,
rv = mInputFrameDataStream->WriteSegments(this, count, countWritten);
mSegmentWriter = nsnull;
mLastDataReadEpoch = mLastReadEpoch;
if (rv == NS_BASE_STREAM_CLOSED) {
// This will happen when the transaction figures out it is EOF, generally
// due to a content-length match being made
@ -1530,7 +1607,7 @@ SpdySession::WriteSegments(nsAHttpSegmentWriter *writer,
return NS_BASE_STREAM_WOULD_BLOCK;
}
rv = writer->OnWriteSegment(trash, count, countWritten);
rv = NetworkRead(writer, trash, count, countWritten);
if (NS_FAILED(rv)) {
LOG3(("SpdySession %p discard frame read failure %x\n", this, rv));
@ -1558,9 +1635,9 @@ SpdySession::WriteSegments(nsAHttpSegmentWriter *writer,
NS_ABORT_IF_FALSE(mInputFrameBufferUsed == 8,
"Frame Buffer Header Not Present");
rv = writer->OnWriteSegment(mInputFrameBuffer + 8 + mInputFrameDataRead,
mInputFrameDataSize - mInputFrameDataRead,
countWritten);
rv = NetworkRead(writer, mInputFrameBuffer + 8 + mInputFrameDataRead,
mInputFrameDataSize - mInputFrameDataRead, countWritten);
if (NS_FAILED(rv)) {
LOG3(("SpdySession %p buffering control frame read failure %x\n",
this, rv));
@ -1768,7 +1845,7 @@ SpdySession::OnWriteSegment(char *buf,
}
count = NS_MIN(count, mInputFrameDataSize - mInputFrameDataRead);
rv = mSegmentWriter->OnWriteSegment(buf, count, countWritten);
rv = NetworkRead(mSegmentWriter, buf, count, countWritten);
if (NS_FAILED(rv))
return rv;

Просмотреть файл

@ -77,6 +77,13 @@ public:
bool CanReuse() { return !mShouldGoAway && !mClosed; }
void DontReuse();
bool RoomForMoreStreams();
// When the connection is active this is called every 15 seconds
void ReadTimeoutTick(PRIntervalTime now);
// Idle time represents time since "goodput".. e.g. a data or header frame
PRIntervalTime IdleTime();
PRUint32 RegisterStreamID(SpdyStream *);
const static PRUint8 kFlag_Control = 0x80;
@ -202,6 +209,10 @@ private:
void ActivateStream(SpdyStream *);
void ProcessPending();
// a wrapper for all calls to the nshttpconnection level segment writer. Used
// to track network I/O for timeout purposes
nsresult NetworkRead(nsAHttpSegmentWriter *, char *, PRUint32, PRUint32 *);
static PLDHashOperator ShutdownEnumerator(nsAHttpTransaction *,
nsAutoPtr<SpdyStream> &,
void *);
@ -333,6 +344,11 @@ private:
PRUint32 mOutputQueueUsed;
PRUint32 mOutputQueueSent;
nsAutoArrayPtr<char> mOutputQueueBuffer;
PRIntervalTime mLastReadEpoch; // used for ping timeouts
PRIntervalTime mLastDataReadEpoch; // used for IdleTime()
PRIntervalTime mPingSentEpoch;
PRUint32 mNextPingID;
};
}} // namespace mozilla::net

Просмотреть файл

@ -153,8 +153,8 @@ nsHttpConnection::Init(nsHttpConnectionInfo *info,
NS_ENSURE_TRUE(!mConnInfo, NS_ERROR_ALREADY_INITIALIZED);
mConnInfo = info;
mMaxHangTime = maxHangTime;
mLastReadTime = NowInSeconds();
mMaxHangTime = PR_SecondsToInterval(maxHangTime);
mLastReadTime = PR_IntervalNow();
mSocketTransport = transport;
mSocketIn = instream;
@ -535,9 +535,7 @@ nsHttpConnection::CanReuse()
else
canReuse = IsKeepAlive();
canReuse = canReuse &&
(NowInSeconds() - mLastReadTime < mIdleTimeout) &&
IsAlive();
canReuse = canReuse && (IdleTime() < mIdleTimeout) && IsAlive();
// An idle persistent connection should not have data waiting to be read
// before a request is sent. Data here is likely a 408 timeout response
@ -565,13 +563,27 @@ nsHttpConnection::CanDirectlyActivate()
return UsingSpdy() && CanReuse() && mSpdySession->RoomForMoreStreams();
}
PRUint32 nsHttpConnection::TimeToLive()
PRIntervalTime
nsHttpConnection::IdleTime()
{
PRInt32 tmp = mIdleTimeout - (NowInSeconds() - mLastReadTime);
if (0 > tmp)
tmp = 0;
return mSpdySession ?
mSpdySession->IdleTime() : (PR_IntervalNow() - mLastReadTime);
}
return tmp;
// returns the number of seconds left before the allowable idle period
// expires, or 0 if the period has already expied.
PRUint32
nsHttpConnection::TimeToLive()
{
if (IdleTime() >= mIdleTimeout)
return 0;
PRUint32 timeToLive = PR_IntervalToSeconds(mIdleTimeout - IdleTime());
// a positive amount of time can be rounded to 0. Because 0 is used
// as the expiration signal, round all values from 0 to 1 up to 1.
if (!timeToLive)
timeToLive = 1;
return timeToLive;
}
bool
@ -735,7 +747,7 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
if (!mUsingSpdy) {
const char *cp = PL_strcasestr(val, "timeout=");
if (cp)
mIdleTimeout = (PRUint32) atoi(cp + 8);
mIdleTimeout = PR_SecondsToInterval((PRUint32) atoi(cp + 8));
else
mIdleTimeout = gHttpHandler->IdleTimeout();
}
@ -743,7 +755,8 @@ nsHttpConnection::OnHeadersAvailable(nsAHttpTransaction *trans,
mIdleTimeout = gHttpHandler->SpdyTimeout();
}
LOG(("Connection can be reused [this=%x idle-timeout=%u]\n", this, mIdleTimeout));
LOG(("Connection can be reused [this=%x idle-timeout=%usec]\n",
this, PR_IntervalToSeconds(mIdleTimeout)));
}
if (!mProxyConnectStream)
@ -851,6 +864,26 @@ nsHttpConnection::TakeTransport(nsISocketTransport **aTransport,
return NS_OK;
}
void
nsHttpConnection::ReadTimeoutTick(PRIntervalTime now)
{
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
// make sure timer didn't tick before Activate()
if (!mTransaction)
return;
// Spdy in the future actually should implement some timeout handling
// using the SPDY ping frame.
if (mSpdySession) {
mSpdySession->ReadTimeoutTick(now);
return;
}
// Pending patches places pipeline rescheduling code will go here
}
void
nsHttpConnection::GetSecurityInfo(nsISupports **secinfo)
{
@ -1136,9 +1169,9 @@ nsHttpConnection::OnSocketReadable()
{
LOG(("nsHttpConnection::OnSocketReadable [this=%x]\n", this));
PRUint32 now = NowInSeconds();
PRIntervalTime now = PR_IntervalNow();
if (mKeepAliveMask && (now - mLastReadTime >= PRUint32(mMaxHangTime))) {
if (mKeepAliveMask && ((now - mLastReadTime) >= mMaxHangTime)) {
LOG(("max hang time exceeded!\n"));
// give the handler a chance to create a new persistent connection to
// this host if we've been busy for too long.
@ -1340,3 +1373,4 @@ nsHttpConnection::GetInterface(const nsIID &iid, void **result)
return mCallbacks->GetInterface(iid, result);
return NS_ERROR_NO_INTERFACE;
}

Просмотреть файл

@ -140,7 +140,7 @@ public:
bool IsPersistent() { return IsKeepAlive(); }
bool IsReused();
void SetIsReusedAfter(PRUint32 afterMilliseconds);
void SetIdleTimeout(PRUint16 val) {mIdleTimeout = val;}
void SetIdleTimeout(PRIntervalTime val) {mIdleTimeout = val;}
nsresult PushBack(const char *data, PRUint32 length);
nsresult ResumeSend();
nsresult ResumeRecv();
@ -158,6 +158,9 @@ public:
bool UsingSpdy() { return mUsingSpdy; }
// When the connection is active this is called every 15 seconds
void ReadTimeoutTick(PRIntervalTime now);
private:
// called to cause the underlying socket to start speaking SSL
nsresult ProxyStartSSL();
@ -168,6 +171,7 @@ private:
nsresult SetupProxyConnect();
PRIntervalTime IdleTime();
bool IsAlive();
bool SupportsPipelining(nsHttpResponseHead *);
@ -207,8 +211,8 @@ private:
nsRefPtr<nsHttpConnectionInfo> mConnInfo;
PRUint32 mLastReadTime;
PRUint16 mMaxHangTime; // max download time before dropping keep-alive status
PRUint16 mIdleTimeout; // value of keep-alive: timeout=
PRIntervalTime mMaxHangTime; // max download time before dropping keep-alive status
PRIntervalTime mIdleTimeout; // value of keep-alive: timeout=
PRIntervalTime mConsiderReusedAfterInterval;
PRIntervalTime mConsiderReusedAfterEpoch;
PRInt64 mCurrentBytesRead; // data read per activation

Просмотреть файл

@ -95,6 +95,7 @@ nsHttpConnectionMgr::nsHttpConnectionMgr()
, mNumActiveConns(0)
, mNumIdleConns(0)
, mTimeOfNextWakeUp(LL_MAXUINT)
, mReadTimeoutTickArmed(false)
{
LOG(("Creating nsHttpConnectionMgr @%x\n", this));
mCT.Init();
@ -105,6 +106,8 @@ nsHttpConnectionMgr::nsHttpConnectionMgr()
nsHttpConnectionMgr::~nsHttpConnectionMgr()
{
LOG(("Destroying nsHttpConnectionMgr @%x\n", this));
if (mReadTimeoutTick)
mReadTimeoutTick->Cancel();
}
nsresult
@ -263,13 +266,19 @@ nsHttpConnectionMgr::Observe(nsISupports *subject,
{
LOG(("nsHttpConnectionMgr::Observe [topic=\"%s\"]\n", topic));
if (0 == strcmp(topic, "timer-callback")) {
// prune dead connections
PruneDeadConnections();
#ifdef DEBUG
if (0 == strcmp(topic, NS_TIMER_CALLBACK_TOPIC)) {
nsCOMPtr<nsITimer> timer = do_QueryInterface(subject);
NS_ASSERTION(timer == mTimer, "unexpected timer-callback");
#endif
if (timer == mTimer) {
PruneDeadConnections();
}
else if (timer == mReadTimeoutTick) {
ReadTimeoutTick();
}
else {
NS_ABORT_IF_FALSE(false, "unexpected timer-callback");
LOG(("Unexpected timer object\n"));
return NS_ERROR_UNEXPECTED;
}
}
return NS_OK;
@ -1167,6 +1176,7 @@ nsHttpConnectionMgr::AddActiveConn(nsHttpConnection *conn,
NS_ADDREF(conn);
ent->mActiveConns.AppendElement(conn);
mNumActiveConns++;
ActivateTimeoutTick();
}
void
@ -1472,6 +1482,12 @@ nsHttpConnectionMgr::OnMsgShutdown(PRInt32, void *)
mCT.Enumerate(ShutdownPassCB, this);
if (mReadTimeoutTick) {
mReadTimeoutTick->Cancel();
mReadTimeoutTick = nsnull;
mReadTimeoutTickArmed = false;
}
// signal shutdown complete
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mon.Notify();
@ -1711,6 +1727,77 @@ nsHttpConnectionMgr::nsConnectionEntry::~nsConnectionEntry()
NS_RELEASE(mConnInfo);
}
// Read Timeout Tick handlers
void
nsHttpConnectionMgr::ActivateTimeoutTick()
{
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
LOG(("nsHttpConnectionMgr::ActivateTimeoutTick() "
"this=%p mReadTimeoutTick=%p\n"));
// right now the spdy timeout code is the only thing hooked to the timeout
// tick, so disable it if spdy is not being used. However pipelining code
// will also want this functionality soon.
if (!gHttpHandler->IsSpdyEnabled())
return;
// The timer tick should be enabled if it is not already pending.
// Upon running the tick will rearm itself if there are active
// connections available.
if (mReadTimeoutTick && mReadTimeoutTickArmed)
return;
if (!mReadTimeoutTick) {
mReadTimeoutTick = do_CreateInstance(NS_TIMER_CONTRACTID);
if (!mReadTimeoutTick) {
NS_WARNING("failed to create timer for http timeout management");
return;
}
}
NS_ABORT_IF_FALSE(!mReadTimeoutTickArmed, "timer tick armed");
mReadTimeoutTickArmed = true;
// pipeline will expect a 1000ms granuality
mReadTimeoutTick->Init(this, 15000, nsITimer::TYPE_REPEATING_SLACK);
}
void
nsHttpConnectionMgr::ReadTimeoutTick()
{
NS_ABORT_IF_FALSE(PR_GetCurrentThread() == gSocketThread, "wrong thread");
NS_ABORT_IF_FALSE(mReadTimeoutTick, "no readtimeout tick");
LOG(("nsHttpConnectionMgr::ReadTimeoutTick active=%d\n",
mNumActiveConns));
if (!mNumActiveConns && mReadTimeoutTickArmed) {
mReadTimeoutTick->Cancel();
mReadTimeoutTickArmed = false;
return;
}
mCT.Enumerate(ReadTimeoutTickCB, this);
}
PLDHashOperator
nsHttpConnectionMgr::ReadTimeoutTickCB(const nsACString &key,
nsAutoPtr<nsConnectionEntry> &ent,
void *closure)
{
nsHttpConnectionMgr *self = (nsHttpConnectionMgr *) closure;
LOG(("nsHttpConnectionMgr::ReadTimeoutTickCB() this=%p host=%s\n",
self, ent->mConnInfo->Host()));
PRIntervalTime now = PR_IntervalNow();
for (PRUint32 index = 0; index < ent->mActiveConns.Length(); ++index)
ent->mActiveConns[index]->ReadTimeoutTick(now);
return PL_DHASH_NEXT;
}
//-----------------------------------------------------------------------------
// nsHttpConnectionMgr::nsConnectionHandle
@ -2093,7 +2180,11 @@ nsHalfOpenSocket::OnOutputStreamReady(nsIAsyncOutputStream *out)
// We need to establish a small non-zero idle timeout so the connection
// mgr perceives this socket as suitable for persistent connection reuse
conn->SetIdleTimeout(NS_MIN((PRUint16) 5, gHttpHandler->IdleTimeout()));
const PRIntervalTime k5Sec = PR_SecondsToInterval(5);
if (k5Sec < gHttpHandler->IdleTimeout())
conn->SetIdleTimeout(k5Sec);
else
conn->SetIdleTimeout(gHttpHandler->IdleTimeout());
// After about 1 second allow for the possibility of restarting a
// transaction due to server close. Keep at sub 1 second as that is the

Просмотреть файл

@ -410,6 +410,12 @@ private:
// Timer for next pruning of dead connections.
nsCOMPtr<nsITimer> mTimer;
// A 1s tick to call nsHttpConnection::ReadTimeoutTick on
// active http/1 connections. Disabled when there are no
// active connections.
nsCOMPtr<nsITimer> mReadTimeoutTick;
bool mReadTimeoutTickArmed;
//
// the connection table
//
@ -424,6 +430,12 @@ private:
PLDHashEntryHdr *hdr,
PRUint32 number,
void *closure);
// Read Timeout Tick handlers
void ActivateTimeoutTick();
void ReadTimeoutTick();
static PLDHashOperator ReadTimeoutTickCB(const nsACString &key,
nsAutoPtr<nsConnectionEntry> &ent,
void *closure);
};
#endif // !nsHttpConnectionMgr_h__

Просмотреть файл

@ -174,8 +174,8 @@ nsHttpHandler::nsHttpHandler()
, mProxyCapabilities(NS_HTTP_ALLOW_KEEPALIVE)
, mReferrerLevel(0xff) // by default we always send a referrer
, mFastFallbackToIPv4(false)
, mIdleTimeout(10)
, mSpdyTimeout(180)
, mIdleTimeout(PR_SecondsToInterval(10))
, mSpdyTimeout(PR_SecondsToInterval(180))
, mMaxRequestAttempts(10)
, mMaxRequestDelay(10)
, mIdleSynTimeout(250)
@ -204,6 +204,8 @@ nsHttpHandler::nsHttpHandler()
, mCoalesceSpdy(true)
, mUseAlternateProtocol(false)
, mSpdySendingChunkSize(SpdySession::kSendingChunkSize)
, mSpdyPingThreshold(PR_SecondsToInterval(44))
, mSpdyPingTimeout(PR_SecondsToInterval(8))
{
#if defined(PR_LOGGING)
gHttpLog = PR_NewLogModule("nsHttp");
@ -840,7 +842,7 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
if (PREF_CHANGED(HTTP_PREF("keep-alive.timeout"))) {
rv = prefs->GetIntPref(HTTP_PREF("keep-alive.timeout"), &val);
if (NS_SUCCEEDED(rv))
mIdleTimeout = (PRUint16) clamped(val, 1, 0xffff);
mIdleTimeout = PR_SecondsToInterval(clamped(val, 1, 0xffff));
}
if (PREF_CHANGED(HTTP_PREF("request.max-attempts"))) {
@ -1111,7 +1113,7 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
if (PREF_CHANGED(HTTP_PREF("spdy.timeout"))) {
rv = prefs->GetIntPref(HTTP_PREF("spdy.timeout"), &val);
if (NS_SUCCEEDED(rv))
mSpdyTimeout = (PRUint16) clamped(val, 1, 0xffff);
mSpdyTimeout = PR_SecondsToInterval(clamped(val, 1, 0xffff));
}
if (PREF_CHANGED(HTTP_PREF("spdy.chunk-size"))) {
@ -1120,6 +1122,24 @@ nsHttpHandler::PrefsChanged(nsIPrefBranch *prefs, const char *pref)
mSpdySendingChunkSize = (PRUint32) clamped(val, 1, 0x7fffffff);
}
// The amount of idle seconds on a spdy connection before initiating a
// server ping. 0 will disable.
if (PREF_CHANGED(HTTP_PREF("spdy.ping-threshold"))) {
rv = prefs->GetIntPref(HTTP_PREF("spdy.ping-threshold"), &val);
if (NS_SUCCEEDED(rv))
mSpdyPingThreshold =
PR_SecondsToInterval((PRUint16) clamped(val, 0, 0x7fffffff));
}
// The amount of seconds to wait for a spdy ping response before
// closing the session.
if (PREF_CHANGED(HTTP_PREF("spdy.ping-timeout"))) {
rv = prefs->GetIntPref(HTTP_PREF("spdy.ping-timeout"), &val);
if (NS_SUCCEEDED(rv))
mSpdyPingTimeout =
PR_SecondsToInterval((PRUint16) clamped(val, 0, 0x7fffffff));
}
//
// INTL options
//

Просмотреть файл

@ -100,8 +100,8 @@ public:
PRUint8 ReferrerLevel() { return mReferrerLevel; }
bool SendSecureXSiteReferrer() { return mSendSecureXSiteReferrer; }
PRUint8 RedirectionLimit() { return mRedirectionLimit; }
PRUint16 IdleTimeout() { return mIdleTimeout; }
PRUint16 SpdyTimeout() { return mSpdyTimeout; }
PRIntervalTime IdleTimeout() { return mIdleTimeout; }
PRIntervalTime SpdyTimeout() { return mSpdyTimeout; }
PRUint16 MaxRequestAttempts() { return mMaxRequestAttempts; }
const char *DefaultSocketType() { return mDefaultSocketType.get(); /* ok to return null */ }
nsIIDNService *IDNConverter() { return mIDNConverter; }
@ -117,6 +117,8 @@ public:
bool CoalesceSpdy() { return mCoalesceSpdy; }
bool UseAlternateProtocol() { return mUseAlternateProtocol; }
PRUint32 SpdySendingChunkSize() { return mSpdySendingChunkSize; }
PRIntervalTime SpdyPingThreshold() { return mSpdyPingThreshold; }
PRIntervalTime SpdyPingTimeout() { return mSpdyPingTimeout; }
bool PromptTempRedirect() { return mPromptTempRedirect; }
@ -269,8 +271,9 @@ private:
bool mFastFallbackToIPv4;
PRUint16 mIdleTimeout;
PRUint16 mSpdyTimeout;
PRIntervalTime mIdleTimeout;
PRIntervalTime mSpdyTimeout;
PRUint16 mMaxRequestAttempts;
PRUint16 mMaxRequestDelay;
PRUint16 mIdleSynTimeout;
@ -345,6 +348,8 @@ private:
bool mCoalesceSpdy;
bool mUseAlternateProtocol;
PRUint32 mSpdySendingChunkSize;
PRIntervalTime mSpdyPingThreshold;
PRIntervalTime mSpdyPingTimeout;
};
//-----------------------------------------------------------------------------

Просмотреть файл

@ -50,6 +50,7 @@
#include "nsNetUtil.h"
#include "nsISupportsPrimitives.h"
#include "nsIGSettingsService.h"
#include "nsInterfaceHashtable.h"
class nsUnixSystemProxySettings : public nsISystemProxySettings {
public:
@ -64,6 +65,8 @@ private:
nsCOMPtr<nsIGConfService> mGConf;
nsCOMPtr<nsIGSettingsService> mGSettings;
nsCOMPtr<nsIGSettingsCollection> mProxySettings;
nsInterfaceHashtable<nsCStringHashKey, nsIGSettingsCollection> mSchemeProxySettings;
bool IsProxyMode(const char* aMode);
nsresult SetProxyResultFromGConf(const char* aKeyBase, const char* aType, nsACString& aResult);
nsresult GetProxyFromGConf(const nsACString& aScheme, const nsACString& aHost, PRInt32 aPort, nsACString& aResult);
@ -76,8 +79,14 @@ NS_IMPL_ISUPPORTS1(nsUnixSystemProxySettings, nsISystemProxySettings)
nsresult
nsUnixSystemProxySettings::Init()
{
mSchemeProxySettings.Init(5);
mGConf = do_GetService(NS_GCONFSERVICE_CONTRACTID);
mGSettings = do_GetService(NS_GSETTINGSSERVICE_CONTRACTID);
if (mGSettings) {
mGSettings->GetCollectionForSchema(NS_LITERAL_CSTRING("org.gnome.system.proxy"),
getter_AddRefs(mProxySettings));
}
return NS_OK;
}
@ -92,22 +101,17 @@ nsUnixSystemProxySettings::IsProxyMode(const char* aMode)
nsresult
nsUnixSystemProxySettings::GetPACURI(nsACString& aResult)
{
if (mGSettings) {
nsCOMPtr<nsIGSettingsCollection> proxy_settings;
mGSettings->GetCollectionForSchema(NS_LITERAL_CSTRING("org.gnome.system.proxy"),
getter_AddRefs(proxy_settings));
if (proxy_settings) {
nsCString proxyMode;
// Check if mode is auto
nsresult rv = proxy_settings->GetString(NS_LITERAL_CSTRING("mode"), proxyMode);
if (rv == NS_OK && proxyMode.Equals("auto")) {
return proxy_settings->GetString(NS_LITERAL_CSTRING("autoconfig-url"), aResult);
}
/* The org.gnome.system.proxy schema has been found, but auto mode is not set.
* Don't try the GConf and return empty string. */
aResult.Truncate();
return NS_OK;
if (mProxySettings) {
nsCString proxyMode;
// Check if mode is auto
nsresult rv = mProxySettings->GetString(NS_LITERAL_CSTRING("mode"), proxyMode);
if (rv == NS_OK && proxyMode.Equals("auto")) {
return mProxySettings->GetString(NS_LITERAL_CSTRING("autoconfig-url"), aResult);
}
/* The org.gnome.system.proxy schema has been found, but auto mode is not set.
* Don't try the GConf and return empty string. */
aResult.Truncate();
return NS_OK;
}
if (mGConf && IsProxyMode("auto")) {
@ -266,10 +270,16 @@ nsresult
nsUnixSystemProxySettings::SetProxyResultFromGSettings(const char* aKeyBase, const char* aType,
nsACString& aResult)
{
nsCOMPtr<nsIGSettingsCollection> proxy_settings;
nsresult rv = mGSettings->GetCollectionForSchema(nsDependentCString(aKeyBase),
getter_AddRefs(proxy_settings));
NS_ENSURE_SUCCESS(rv, rv);
nsDependentCString key(aKeyBase);
nsCOMPtr<nsIGSettingsCollection> proxy_settings = mSchemeProxySettings.Get(key);
nsresult rv;
if (!proxy_settings) {
rv = mGSettings->GetCollectionForSchema(key, getter_AddRefs(proxy_settings));
NS_ENSURE_SUCCESS(rv, rv);
mSchemeProxySettings.Put(key, proxy_settings);
}
nsCAutoString host;
rv = proxy_settings->GetString(NS_LITERAL_CSTRING("host"), host);
@ -451,16 +461,8 @@ nsUnixSystemProxySettings::GetProxyFromGSettings(const nsACString& aScheme,
PRInt32 aPort,
nsACString& aResult)
{
nsCOMPtr<nsIGSettingsCollection> proxy_settings;
nsresult rv;
rv = mGSettings->GetCollectionForSchema(NS_LITERAL_CSTRING("org.gnome.system.proxy"),
getter_AddRefs(proxy_settings));
if (NS_FAILED(rv))
return rv;
nsCString proxyMode;
rv = proxy_settings->GetString(NS_LITERAL_CSTRING("mode"), proxyMode);
nsresult rv = mProxySettings->GetString(NS_LITERAL_CSTRING("mode"), proxyMode);
NS_ENSURE_SUCCESS(rv, rv);
if (!proxyMode.Equals("manual")) {
@ -469,7 +471,7 @@ nsUnixSystemProxySettings::GetProxyFromGSettings(const nsACString& aScheme,
}
nsCOMPtr<nsIArray> ignoreList;
if (NS_SUCCEEDED(proxy_settings->GetStringList(NS_LITERAL_CSTRING("ignore-hosts"),
if (NS_SUCCEEDED(mProxySettings->GetStringList(NS_LITERAL_CSTRING("ignore-hosts"),
getter_AddRefs(ignoreList))) && ignoreList) {
PRUint32 len = 0;
ignoreList->GetLength(&len);
@ -526,7 +528,7 @@ nsUnixSystemProxySettings::GetProxyForURI(nsIURI* aURI, nsACString& aResult)
rv = aURI->GetPort(&port);
NS_ENSURE_SUCCESS(rv, rv);
if (mGSettings) {
if (mProxySettings) {
rv = GetProxyFromGSettings(scheme, host, port, aResult);
if (rv == NS_OK)
return rv;

Просмотреть файл

@ -1112,7 +1112,7 @@ struct nsCycleCollector
bool mCollectionInProgress;
bool mScanInProgress;
bool mFollowupCollection;
PRUint32 mCollectedObjects;
nsCycleCollectorResults *mResults;
TimeStamp mCollectionStart;
nsCycleCollectionLanguageRuntime *mRuntimes[nsIProgrammingLanguage::MAX+1];
@ -1159,11 +1159,13 @@ struct nsCycleCollector
nsPurpleBufferEntry* Suspect2(nsISupports *n);
bool Forget2(nsPurpleBufferEntry *e);
PRUint32 Collect(PRUint32 aTryCollections,
nsICycleCollectorListener *aListener);
void Collect(nsCycleCollectorResults *aResults,
PRUint32 aTryCollections,
nsICycleCollectorListener *aListener);
// Prepare for and cleanup after one or more collection(s).
bool PrepareForCollection(nsTArray<PtrInfo*> *aWhiteNodes);
bool PrepareForCollection(nsCycleCollectorResults *aResults,
nsTArray<PtrInfo*> *aWhiteNodes);
void GCIfNeeded(bool aForceGC);
void CleanupAfterCollection();
@ -2394,6 +2396,7 @@ nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
"FinishCollection wasn't called?");
mWhiteNodes->SetCapacity(mWhiteNodeCount);
PRUint32 numWhiteGCed = 0;
NodePool::Enumerator etor(mGraph.mNodes);
while (!etor.IsDone())
@ -2404,9 +2407,21 @@ nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
if (NS_FAILED(rv)) {
Fault("Failed root call while unlinking", pinfo);
mWhiteNodes->RemoveElementAt(mWhiteNodes->Length() - 1);
} else if (pinfo->mRefCount == 0) {
// only JS objects have a refcount of 0
++numWhiteGCed;
}
}
}
PRUint32 count = mWhiteNodes->Length();
NS_ASSERTION(numWhiteGCed <= count,
"More freed GCed nodes than total freed nodes.");
if (mResults) {
mResults->mFreedRefCounted += count - numWhiteGCed;
mResults->mFreedGCed += numWhiteGCed;
}
timeLog.Checkpoint("CollectWhite::Root");
if (mBeforeUnlinkCB) {
@ -2418,17 +2433,15 @@ nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
_CrtMemCheckpoint(&ms1);
#endif
PRUint32 i, count = mWhiteNodes->Length();
if (aListener) {
for (i = 0; i < count; ++i) {
for (PRUint32 i = 0; i < count; ++i) {
PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
aListener->DescribeGarbage((PRUint64)pinfo->mPointer);
}
aListener->End();
}
for (i = 0; i < count; ++i) {
for (PRUint32 i = 0; i < count; ++i) {
PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
rv = pinfo->mParticipant->Unlink(pinfo->mPointer);
if (NS_FAILED(rv)) {
@ -2445,7 +2458,7 @@ nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
}
timeLog.Checkpoint("CollectWhite::Unlink");
for (i = 0; i < count; ++i) {
for (PRUint32 i = 0; i < count; ++i) {
PtrInfo *pinfo = mWhiteNodes->ElementAt(i);
rv = pinfo->mParticipant->Unroot(pinfo->mPointer);
if (NS_FAILED(rv))
@ -2459,7 +2472,6 @@ nsCycleCollector::CollectWhite(nsICycleCollectorListener *aListener)
mStats.mFreedBytes += (ms1.lTotalCount - ms2.lTotalCount);
#endif
mCollectedObjects += count;
return count > 0;
}
@ -2662,10 +2674,10 @@ InitMemHook(void)
// Collector implementation
////////////////////////////////////////////////////////////////////////
nsCycleCollector::nsCycleCollector() :
nsCycleCollector::nsCycleCollector() :
mCollectionInProgress(false),
mScanInProgress(false),
mCollectedObjects(0),
mResults(nsnull),
mWhiteNodes(nsnull),
mWhiteNodeCount(0),
mVisitedRefCounted(0),
@ -3036,6 +3048,8 @@ nsCycleCollector::GCIfNeeded(bool aForceGC)
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_NEED_GC, needGC);
if (!needGC)
return;
if (mResults)
mResults->mForcedGC = true;
}
TimeLog timeLog;
@ -3048,7 +3062,8 @@ nsCycleCollector::GCIfNeeded(bool aForceGC)
}
bool
nsCycleCollector::PrepareForCollection(nsTArray<PtrInfo*> *aWhiteNodes)
nsCycleCollector::PrepareForCollection(nsCycleCollectorResults *aResults,
nsTArray<PtrInfo*> *aWhiteNodes)
{
#if defined(DEBUG_CC) && !defined(__MINGW32__)
if (!mParams.mDoNothing && mParams.mHookMalloc)
@ -3073,8 +3088,8 @@ nsCycleCollector::PrepareForCollection(nsTArray<PtrInfo*> *aWhiteNodes)
obs->NotifyObservers(nsnull, "cycle-collector-begin", nsnull);
mFollowupCollection = false;
mCollectedObjects = 0;
mResults = aResults;
mWhiteNodes = aWhiteNodes;
timeLog.Checkpoint("PrepareForCollection()");
@ -3098,10 +3113,21 @@ nsCycleCollector::CleanupAfterCollection()
PRUint32 interval = (PRUint32) ((TimeStamp::Now() - mCollectionStart).ToMilliseconds());
#ifdef COLLECT_TIME_DEBUG
printf("cc: total cycle collector time was %ums\n", interval);
printf("cc: visited %u ref counted and %u GCed objects, freed %d.\n",
mVisitedRefCounted, mVisitedGCed, mWhiteNodeCount);
if (mResults) {
printf("cc: visited %u ref counted and %u GCed objects, freed %d ref counted and %d GCed objects.\n",
mVisitedRefCounted, mVisitedGCed,
mResults->mFreedRefCounted, mResults->mFreedGCed);
} else {
printf("cc: visited %u ref counted and %u GCed objects, freed %d.\n",
mVisitedRefCounted, mVisitedGCed, mWhiteNodeCount);
}
printf("cc: \n");
#endif
if (mResults) {
mResults->mVisitedRefCounted = mVisitedRefCounted;
mResults->mVisitedGCed = mVisitedGCed;
mResults = nsnull;
}
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR, interval);
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_REF_COUNTED, mVisitedRefCounted);
Telemetry::Accumulate(Telemetry::CYCLE_COLLECTOR_VISITED_GCED, mVisitedGCed);
@ -3112,14 +3138,15 @@ nsCycleCollector::CleanupAfterCollection()
#endif
}
PRUint32
nsCycleCollector::Collect(PRUint32 aTryCollections,
void
nsCycleCollector::Collect(nsCycleCollectorResults *aResults,
PRUint32 aTryCollections,
nsICycleCollectorListener *aListener)
{
nsAutoTArray<PtrInfo*, 4000> whiteNodes;
if (!PrepareForCollection(&whiteNodes))
return 0;
if (!PrepareForCollection(aResults, &whiteNodes))
return;
PRUint32 totalCollections = 0;
while (aTryCollections > totalCollections) {
@ -3135,8 +3162,6 @@ nsCycleCollector::Collect(PRUint32 aTryCollections,
}
CleanupAfterCollection();
return mCollectedObjects;
}
bool
@ -3313,7 +3338,7 @@ nsCycleCollector::Shutdown()
if (mParams.mLogGraphs) {
listener = new nsCycleCollectorLogger();
}
Collect(SHUTDOWN_COLLECTIONS(mParams), listener);
Collect(nsnull, SHUTDOWN_COLLECTIONS(mParams), listener);
#ifdef DEBUG_CC
GCGraphBuilder builder(mGraph, mRuntimes, nsnull);
@ -3919,7 +3944,8 @@ public:
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
}
PRUint32 Collect(nsICycleCollectorListener* aListener)
void Collect(nsCycleCollectorResults *aResults,
nsICycleCollectorListener *aListener)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@ -3934,11 +3960,11 @@ public:
MutexAutoLock autoLock(mLock);
if (!mRunning)
return 0;
return;
nsAutoTArray<PtrInfo*, 4000> whiteNodes;
if (!mCollector->PrepareForCollection(&whiteNodes))
return 0;
if (!mCollector->PrepareForCollection(aResults, &whiteNodes))
return;
NS_ASSERTION(!mListener, "Should have cleared this already!");
if (aListener && NS_FAILED(aListener->Begin()))
@ -3956,14 +3982,9 @@ public:
mListener = nsnull;
if (mCollected) {
mCollected = mCollector->FinishCollection(aListener);
mCollector->FinishCollection(aListener);
mCollector->CleanupAfterCollection();
return mCollected ? mCollector->mCollectedObjects : 0;
}
return 0;
}
void Shutdown()
@ -4037,8 +4058,9 @@ nsCycleCollector_forgetSkippable()
}
}
PRUint32
nsCycleCollector_collect(nsICycleCollectorListener *aListener)
void
nsCycleCollector_collect(nsCycleCollectorResults *aResults,
nsICycleCollectorListener *aListener)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
SAMPLE_LABEL("CC", "nsCycleCollector_collect");
@ -4047,9 +4069,11 @@ nsCycleCollector_collect(nsICycleCollectorListener *aListener)
listener = new nsCycleCollectorLogger();
}
if (sCollectorRunner)
return sCollectorRunner->Collect(listener);
return sCollector ? sCollector->Collect(1, listener) : 0;
if (sCollectorRunner) {
sCollectorRunner->Collect(aResults, listener);
} else if (sCollector) {
sCollector->Collect(aResults, 1, listener);
}
}
void

Просмотреть файл

@ -62,6 +62,20 @@ struct nsCycleCollectionLanguageRuntime
#endif
};
// Contains various stats about the cycle collection.
class nsCycleCollectorResults
{
public:
nsCycleCollectorResults() :
mForcedGC(false), mVisitedRefCounted(0), mVisitedGCed(0),
mFreedRefCounted(0), mFreedGCed(0) {}
bool mForcedGC;
PRUint32 mVisitedRefCounted;
PRUint32 mVisitedGCed;
PRUint32 mFreedRefCounted;
PRUint32 mFreedGCed;
};
nsresult nsCycleCollector_startup();
typedef void (*CC_BeforeUnlinkCallback)(void);
@ -76,8 +90,8 @@ void nsCycleCollector_forgetSkippable();
void nsCycleCollector_logPurpleRemoval(void* aObject);
#endif
// Returns the number of collected nodes.
PRUint32 nsCycleCollector_collect(nsICycleCollectorListener *aListener);
void nsCycleCollector_collect(nsCycleCollectorResults *aResults,
nsICycleCollectorListener *aListener);
PRUint32 nsCycleCollector_suspectedCount();
void nsCycleCollector_shutdownThreads();
void nsCycleCollector_shutdown();