зеркало из https://github.com/mozilla/gecko-dev.git
Merge last green PGO from inbound to central
This commit is contained in:
Коммит
c76aee5341
|
@ -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 ®Exp() { 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();
|
||||
|
|
Загрузка…
Ссылка в новой задаче