зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-i to m-c.
This commit is contained in:
Коммит
9ea644607b
|
@ -335,6 +335,21 @@ FocusManager::ProcessFocusEvent(AccEvent* aEvent)
|
|||
nsRefPtr<AccEvent> focusEvent =
|
||||
new AccEvent(nsIAccessibleEvent::EVENT_FOCUS, target, fromUserInputFlag);
|
||||
nsEventShell::FireEvent(focusEvent);
|
||||
|
||||
// Fire scrolling_start event when the document receives the focus if it has
|
||||
// an anchor jump. If an accessible within the document receive the focus
|
||||
// then null out the anchor jump because it no longer applies.
|
||||
nsDocAccessible* targetDocument = target->GetDocAccessible();
|
||||
nsAccessible* anchorJump = targetDocument->AnchorJump();
|
||||
if (anchorJump) {
|
||||
if (target == targetDocument) {
|
||||
// XXX: bug 625699, note in some cases the node could go away before we
|
||||
// we receive focus event, for example if the node is removed from DOM.
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_SCROLLING_START,
|
||||
anchorJump, fromUserInputFlag);
|
||||
}
|
||||
targetDocument->SetAnchorJump(nsnull);
|
||||
}
|
||||
}
|
||||
|
||||
nsIContent*
|
||||
|
|
|
@ -162,7 +162,7 @@ nsAccessibilityService::NotifyOfAnchorJumpTo(nsIContent* aTargetNode)
|
|||
if (documentNode) {
|
||||
nsDocAccessible* document = GetDocAccessible(documentNode);
|
||||
if (document)
|
||||
document->HandleAnchorJump(aTargetNode);
|
||||
document->SetAnchorJump(aTargetNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1769,22 +1769,6 @@ nsDocAccessible::ProcessPendingEvent(AccEvent* aEvent)
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocAccessible::ProcessAnchorJump(nsIContent* aTargetNode)
|
||||
{
|
||||
// If the jump target is not accessible then fire an event for nearest
|
||||
// accessible in parent chain.
|
||||
nsAccessible* target = GetAccessibleOrContainer(aTargetNode);
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
// XXX: bug 625699, note in some cases the node could go away before we flush
|
||||
// the queue, for example if the node becomes inaccessible, or is removed from
|
||||
// the DOM.
|
||||
FireDelayedAccessibleEvent(nsIAccessibleEvent::EVENT_SCROLLING_START,
|
||||
target->GetNode());
|
||||
}
|
||||
|
||||
void
|
||||
nsDocAccessible::ProcessContentInserted(nsAccessible* aContainer,
|
||||
const nsTArray<nsCOMPtr<nsIContent> >* aInsertedContent)
|
||||
|
|
|
@ -205,13 +205,13 @@ public:
|
|||
nsresult FireDelayedAccessibleEvent(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Handle anchor jump when page is loaded.
|
||||
* Get/set the anchor jump.
|
||||
*/
|
||||
inline void HandleAnchorJump(nsIContent* aTargetNode)
|
||||
{
|
||||
HandleNotification<nsDocAccessible, nsIContent>
|
||||
(this, &nsDocAccessible::ProcessAnchorJump, aTargetNode);
|
||||
}
|
||||
inline nsAccessible* AnchorJump()
|
||||
{ return GetAccessibleOrContainer(mAnchorJumpElm); }
|
||||
|
||||
inline void SetAnchorJump(nsIContent* aTargetNode)
|
||||
{ mAnchorJumpElm = aTargetNode; }
|
||||
|
||||
/**
|
||||
* Bind the child document to the tree.
|
||||
|
@ -467,11 +467,6 @@ protected:
|
|||
*/
|
||||
void ProcessPendingEvent(AccEvent* aEvent);
|
||||
|
||||
/**
|
||||
* Process anchor jump notification and fire scrolling end event.
|
||||
*/
|
||||
void ProcessAnchorJump(nsIContent* aTargetNode);
|
||||
|
||||
/**
|
||||
* Update the accessible tree for inserted content.
|
||||
*/
|
||||
|
@ -572,6 +567,11 @@ protected:
|
|||
*/
|
||||
PRUint32 mLoadEventType;
|
||||
|
||||
/**
|
||||
* Reference to anchor jump element.
|
||||
*/
|
||||
nsCOMPtr<nsIContent> mAnchorJumpElm;
|
||||
|
||||
/**
|
||||
* Keep the ARIA attribute old value that is initialized by
|
||||
* AttributeWillChange and used by AttributeChanged notifications.
|
||||
|
|
|
@ -35,6 +35,7 @@
|
|||
// Test
|
||||
|
||||
// gA11yEventDumpID = "debug"; // debug stuff
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
|
||||
function doTest()
|
||||
{
|
||||
|
|
|
@ -16,11 +16,12 @@
|
|||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochitests/content/a11y/accessible/treeview.js" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="../common.js" />
|
||||
<script type="application/javascript"
|
||||
src="../role.js" />
|
||||
<script type="application/javascript"
|
||||
src="../states.js" />
|
||||
<script type="application/javascript"
|
||||
src="../events.js" />
|
||||
|
||||
|
@ -34,6 +35,7 @@
|
|||
// Hack to make xul:tabbrowser work
|
||||
|
||||
const Ci = Components.interfaces;
|
||||
const CC = Components.classes;
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
|
@ -45,23 +47,78 @@
|
|||
}
|
||||
};
|
||||
|
||||
var gURLBar = {
|
||||
focused: false
|
||||
};
|
||||
|
||||
var gFindBarInitialized = false;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Tests
|
||||
|
||||
var gScrollHandler = {
|
||||
handleEvent: function gScrollHandler_handleEvent(aEvent) {
|
||||
if (aEvent.DOMNode.getAttribute("name") == "link1") {
|
||||
ok(true, "scrolling start event was handled on link1");
|
||||
unregisterA11yEventListener(EVENT_SCROLLING_START, this);
|
||||
SimpleTest.finish();
|
||||
function getTabDocument()
|
||||
{
|
||||
return getNode("tabBrowser").selectedBrowser.contentDocument;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function getAnchorJumpInTabDocument()
|
||||
{
|
||||
return getTabDocument().querySelector("a[name='link1']");
|
||||
}
|
||||
|
||||
function loadTab(aTabBrowserID, aURL)
|
||||
{
|
||||
function loadTabChecker()
|
||||
{
|
||||
this.type = EVENT_REORDER;
|
||||
this.match = function loadTabChecker_match(aEvent)
|
||||
{
|
||||
var target = aEvent.accessible;
|
||||
if (target.role == ROLE_INTERNAL_FRAME &&
|
||||
target.parent.parent == getAccessible(getNode(aTabBrowserID).mTabBox.tabpanels)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
this.eventSeq = [ new loadTabChecker() ];
|
||||
|
||||
this.invoke = function loadTab_invoke()
|
||||
{
|
||||
getNode(aTabBrowserID).loadURI(aURL);
|
||||
}
|
||||
|
||||
this.getID = function loadTab_getID()
|
||||
{
|
||||
return "load tab " + aURL + " for " + prettyName(aTabBrowserID);
|
||||
}
|
||||
}
|
||||
|
||||
function advanceFocusIntoTab(aTabBrowserID)
|
||||
{
|
||||
this.eventSeq = [
|
||||
new focusChecker(getTabDocument),
|
||||
new invokerChecker(EVENT_SCROLLING_START, getAnchorJumpInTabDocument)
|
||||
];
|
||||
|
||||
this.invoke = function advanceFocusIntoTab_invoke()
|
||||
{
|
||||
var tabDoc = getAccessible(getTabDocument());
|
||||
tabDoc.takeFocus();
|
||||
}
|
||||
|
||||
this.getID = function advanceFocusIntoTab_getID()
|
||||
{
|
||||
return "advance focus into loaded tab";
|
||||
}
|
||||
}
|
||||
|
||||
//gA11yEventDumpToConsole = true; // debug stuff
|
||||
|
||||
var gQueue = null;
|
||||
function doTest()
|
||||
{
|
||||
registerA11yEventListener(EVENT_SCROLLING_START, gScrollHandler);
|
||||
|
||||
var rootDir = getRootDirectory(window.location.href);
|
||||
|
||||
/*
|
||||
|
@ -74,10 +131,12 @@
|
|||
var tmpdir = extractJarToTmp(jar);
|
||||
rootDir = "file://" + tmpdir.path + '/';
|
||||
}
|
||||
|
||||
var url = rootDir + "scroll.html#link1";
|
||||
var tabBrowser = document.getElementById("tabBrowser");
|
||||
tabBrowser.loadURI(url);
|
||||
|
||||
gQueue = new eventQueue();
|
||||
gQueue.push(new loadTab("tabBrowser", url));
|
||||
gQueue.push(new advanceFocusIntoTab("tabBrowser"));
|
||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
@ -97,6 +156,11 @@
|
|||
title="Same page links to targets with content fires scrolling start accessible event on leaf text node">
|
||||
Mozilla Bug 519303
|
||||
</a>
|
||||
<a target="_blank"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=691734"
|
||||
title="Make sure scrolling start event is fired when document receive focus">
|
||||
Mozilla Bug 691734
|
||||
</a>
|
||||
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
@ -115,13 +179,17 @@
|
|||
</menupopup>
|
||||
</menu>
|
||||
</menubar>
|
||||
<keyset>
|
||||
<key id="key_close"/>
|
||||
</keyset>
|
||||
|
||||
<hbox>
|
||||
<tabs id="tabbrowser-tabs" class="tabbrowser-tabs"
|
||||
tabbrowser="tabBrowser"
|
||||
flex="1"
|
||||
setfocus="false">
|
||||
<tab class="tabbrowser-tab" selected="true"/>
|
||||
flex="1">
|
||||
<tab class="tabbrowser-tab" selected="true" label="tab"/>
|
||||
</tabs>
|
||||
</hbox>
|
||||
<tabbrowser id="tabBrowser"
|
||||
type="content-primary"
|
||||
tabcontainer="tabbrowser-tabs"
|
||||
|
|
|
@ -357,9 +357,14 @@ pref("browser.search.suggest.enabled", true);
|
|||
pref("browser.sessionhistory.max_entries", 50);
|
||||
|
||||
// handle links targeting new windows
|
||||
// 0=default window, 1=current window/tab, 2=new window, 3=new tab in most recent window
|
||||
// 1=current window/tab, 2=new window, 3=new tab in most recent window
|
||||
pref("browser.link.open_newwindow", 3);
|
||||
|
||||
// handle external links (i.e. links opened from a different application)
|
||||
// default: use browser.link.open_newwindow
|
||||
// 1-3: see browser.link.open_newwindow for interpretation
|
||||
pref("browser.link.open_newwindow.override.external", -1);
|
||||
|
||||
// 0: no restrictions - divert everything
|
||||
// 1: don't divert window.open at all
|
||||
// 2: don't divert window.open with features
|
||||
|
|
|
@ -4925,8 +4925,13 @@ nsBrowserAccess.prototype = {
|
|||
return null;
|
||||
}
|
||||
|
||||
if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW)
|
||||
if (aWhere == Ci.nsIBrowserDOMWindow.OPEN_DEFAULTWINDOW) {
|
||||
if (isExternal &&
|
||||
gPrefService.prefHasUserValue("browser.link.open_newwindow.override.external"))
|
||||
aWhere = gPrefService.getIntPref("browser.link.open_newwindow.override.external");
|
||||
else
|
||||
aWhere = gPrefService.getIntPref("browser.link.open_newwindow");
|
||||
}
|
||||
switch (aWhere) {
|
||||
case Ci.nsIBrowserDOMWindow.OPEN_NEWWINDOW :
|
||||
// FIXME: Bug 408379. So how come this doesn't send the
|
||||
|
@ -8524,7 +8529,7 @@ function switchToTabHavingURI(aURI, aOpenNew) {
|
|||
if (isBrowserWindow && isTabEmpty(gBrowser.selectedTab))
|
||||
gBrowser.selectedBrowser.loadURI(aURI.spec);
|
||||
else
|
||||
openUILinkIn(aURI.spec, "tab");
|
||||
openUILinkIn(aURI.spec, "tab", { inBackground: false });
|
||||
}
|
||||
|
||||
return false;
|
||||
|
|
|
@ -566,10 +566,10 @@
|
|||
if (this.mTab.hasAttribute("busy")) {
|
||||
this.mTab.removeAttribute("busy");
|
||||
this.mTabBrowser._tabAttrModified(this.mTab);
|
||||
}
|
||||
this.mTab.removeAttribute("progress");
|
||||
if (!this.mTab.selected)
|
||||
this.mTab.setAttribute("unread", "true");
|
||||
}
|
||||
this.mTab.removeAttribute("progress");
|
||||
|
||||
var location = aRequest.QueryInterface(nsIChannel).URI;
|
||||
|
||||
|
@ -876,7 +876,6 @@
|
|||
(window.windowState != window.STATE_MINIMIZED);
|
||||
this.mCurrentBrowser = newBrowser;
|
||||
this.mCurrentTab = this.selectedTab;
|
||||
this.mCurrentTab.removeAttribute("unread");
|
||||
this.showTab(this.mCurrentTab);
|
||||
|
||||
if (updatePageReport)
|
||||
|
@ -909,8 +908,9 @@
|
|||
true, false);
|
||||
}
|
||||
|
||||
// Don't switch the fast find or update the titlebar (bug 540248) - this tab switch is temporary
|
||||
if (!this._previewMode) {
|
||||
this.mCurrentTab.removeAttribute("unread");
|
||||
|
||||
#ifdef MOZ_E10S_COMPAT
|
||||
// Bug 666816 - TypeAheadFind support for e10s
|
||||
#else
|
||||
|
|
|
@ -73,7 +73,6 @@ let gTests = [
|
|||
check: function(aTab) {
|
||||
// Right click should do nothing (context menu will be shown)
|
||||
is(gURLBar.value, TEST_VALUE, "Urlbar still has the value we entered");
|
||||
ok(gURLBar.focused, "Urlbar is still focused after click");
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -62,6 +62,8 @@ function test() {
|
|||
testVal("<https://>mozilla.org</sub/file.ext?foo&bar#top>");
|
||||
|
||||
testVal("<https://sub.>mozilla.org<:666/file.ext>");
|
||||
testVal("<sub.>mozilla.org<:666/file.ext>");
|
||||
testVal("localhost<:666/file.ext>");
|
||||
|
||||
let IPs = ["192.168.1.1",
|
||||
"[::]",
|
||||
|
@ -87,6 +89,7 @@ function test() {
|
|||
IPs.forEach(function (IP) {
|
||||
testVal(IP);
|
||||
testVal(IP + "</file.ext>");
|
||||
testVal(IP + "<:666/file.ext>");
|
||||
testVal("<https://>" + IP);
|
||||
testVal("<https://>" + IP + "</file.ext>");
|
||||
testVal("<https://user:pass@>" + IP + "<:666/file.ext>");
|
||||
|
|
|
@ -204,7 +204,7 @@
|
|||
let textNode = this.editor.rootElement.firstChild;
|
||||
let value = textNode.textContent;
|
||||
|
||||
let protocol = value.match(/^[a-z\d.+\-]+:/);
|
||||
let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/);
|
||||
if (protocol &&
|
||||
["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1)
|
||||
return;
|
||||
|
|
|
@ -44,7 +44,6 @@ var gTabsPane = {
|
|||
*
|
||||
* browser.link.open_newwindow
|
||||
* - determines where pages which would open in a new window are opened:
|
||||
* 0 opens such links in the default window,
|
||||
* 1 opens such links in the most recent window or tab,
|
||||
* 2 opens such links in a new window,
|
||||
* 3 opens such links in a new tab
|
||||
|
|
|
@ -324,7 +324,6 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
|
|||
if (aAttribute == *lengthListInfo.mLengthListInfo[i].mName) {
|
||||
rv = lengthListInfo.mLengthLists[i].SetBaseValueString(aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReportAttributeParseFailure(GetOwnerDoc(), aAttribute, aValue);
|
||||
lengthListInfo.Reset(i);
|
||||
}
|
||||
foundMatch = PR_TRUE;
|
||||
|
@ -340,7 +339,6 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
|
|||
if (aAttribute == *numberListInfo.mNumberListInfo[i].mName) {
|
||||
rv = numberListInfo.mNumberLists[i].SetBaseValueString(aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReportAttributeParseFailure(GetOwnerDoc(), aAttribute, aValue);
|
||||
numberListInfo.Reset(i);
|
||||
}
|
||||
foundMatch = PR_TRUE;
|
||||
|
@ -356,7 +354,6 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
|
|||
if (pointList) {
|
||||
rv = pointList->SetBaseValueString(aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReportAttributeParseFailure(GetOwnerDoc(), aAttribute, aValue);
|
||||
// The spec says we parse everything up to the failure, so we don't
|
||||
// call pointList->ClearBaseValue()
|
||||
}
|
||||
|
@ -372,7 +369,6 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
|
|||
if (segList) {
|
||||
rv = segList->SetBaseValueString(aValue);
|
||||
if (NS_FAILED(rv)) {
|
||||
ReportAttributeParseFailure(GetOwnerDoc(), aAttribute, aValue);
|
||||
// The spec says we parse everything up to the failure, so we don't
|
||||
// call segList->ClearBaseValue()
|
||||
}
|
||||
|
@ -556,13 +552,12 @@ nsSVGElement::ParseAttribute(PRInt32 aNamespaceID,
|
|||
aResult);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
||||
void
|
||||
nsSVGElement::UnsetAttrInternal(PRInt32 aNamespaceID, nsIAtom* aName,
|
||||
bool aNotify)
|
||||
{
|
||||
// XXXbz there's a bunch of redundancy here with AfterSetAttr.
|
||||
// Maybe consolidate?
|
||||
nsresult rv = nsSVGElementBase::UnsetAttr(aNamespaceID, aName, aNotify);
|
||||
|
||||
if (aNamespaceID == kNameSpaceID_None) {
|
||||
// If this is an svg presentation attribute, remove rule to force an update
|
||||
|
@ -575,7 +570,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
nsIAtom* eventName = GetEventNameForAttr(aName);
|
||||
manager->RemoveScriptEventListener(eventName);
|
||||
}
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if this is a length attribute going away
|
||||
|
@ -585,7 +580,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (aName == *lenInfo.mLengthInfo[i].mName) {
|
||||
lenInfo.Reset(i);
|
||||
DidChangeLength(i, PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -596,7 +591,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (aName == *lengthListInfo.mLengthListInfo[i].mName) {
|
||||
lengthListInfo.Reset(i);
|
||||
DidChangeLengthList(i, PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -607,7 +602,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (aName == *numberListInfo.mNumberListInfo[i].mName) {
|
||||
numberListInfo.Reset(i);
|
||||
DidChangeNumberList(i, PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -616,7 +611,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
SVGAnimatedPointList *pointList = GetAnimatedPointList();
|
||||
if (pointList) {
|
||||
pointList->ClearBaseValue();
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -626,7 +621,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (segList) {
|
||||
segList->ClearBaseValue();
|
||||
DidChangePathSegList(PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -637,7 +632,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (aName == *numInfo.mNumberInfo[i].mName) {
|
||||
numInfo.Reset(i);
|
||||
DidChangeNumber(i, PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -648,7 +643,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (aName == *numPairInfo.mNumberPairInfo[i].mName) {
|
||||
numPairInfo.Reset(i);
|
||||
DidChangeNumberPair(i, PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -659,7 +654,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (aName == *intInfo.mIntegerInfo[i].mName) {
|
||||
intInfo.Reset(i);
|
||||
DidChangeInteger(i, PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -670,7 +665,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (aName == *intPairInfo.mIntegerPairInfo[i].mName) {
|
||||
intPairInfo.Reset(i);
|
||||
DidChangeIntegerPair(i, PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -681,7 +676,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (aName == *angleInfo.mAngleInfo[i].mName) {
|
||||
angleInfo.Reset(i);
|
||||
DidChangeAngle(i, PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -692,7 +687,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (aName == *boolInfo.mBooleanInfo[i].mName) {
|
||||
boolInfo.Reset(i);
|
||||
DidChangeBoolean(i, PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -703,7 +698,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (aName == *enumInfo.mEnumInfo[i].mName) {
|
||||
enumInfo.Reset(i);
|
||||
DidChangeEnum(i, PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -713,9 +708,10 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (viewBox) {
|
||||
viewBox->Init();
|
||||
DidChangeViewBox(PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this is a preserveAspectRatio attribute going away
|
||||
if (aName == nsGkAtoms::preserveAspectRatio) {
|
||||
SVGAnimatedPreserveAspectRatio *preserveAspectRatio =
|
||||
|
@ -724,7 +720,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (preserveAspectRatio) {
|
||||
preserveAspectRatio->Init();
|
||||
DidChangePreserveAspectRatio(PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -734,7 +730,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
if (transformList) {
|
||||
transformList->ClearBaseValue();
|
||||
DidChangeTransformList(PR_FALSE);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -744,7 +740,7 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
|
||||
if (svgClass) {
|
||||
svgClass->Init();
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -757,11 +753,17 @@ nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
|||
aName == *stringInfo.mStringInfo[i].mName) {
|
||||
stringInfo.Reset(i);
|
||||
DidChangeString(i);
|
||||
return rv;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
nsresult
|
||||
nsSVGElement::UnsetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
|
||||
bool aNotify)
|
||||
{
|
||||
UnsetAttrInternal(aNamespaceID, aName, aNotify);
|
||||
return nsSVGElementBase::UnsetAttr(aNamespaceID, aName, aNotify);
|
||||
}
|
||||
|
||||
nsChangeHint
|
||||
|
@ -1304,7 +1306,7 @@ nsSVGElement::SetLength(nsIAtom* aName, const nsSVGLength2 &aLength)
|
|||
for (PRUint32 i = 0; i < lengthInfo.mLengthCount; i++) {
|
||||
if (aName == *lengthInfo.mLengthInfo[i].mName) {
|
||||
lengthInfo.mLengths[i] = aLength;
|
||||
DidChangeLength(i, PR_TRUE);
|
||||
DidAnimateLength(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -519,6 +519,9 @@ protected:
|
|||
static nsSVGEnumMapping sSVGUnitTypesMap[];
|
||||
|
||||
private:
|
||||
void UnsetAttrInternal(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
|
||||
bool aNotify);
|
||||
|
||||
nsRefPtr<mozilla::css::StyleRule> mContentStyleRule;
|
||||
};
|
||||
|
||||
|
|
|
@ -417,24 +417,25 @@ bool nsSVGUseElement::HasValidDimensions()
|
|||
}
|
||||
|
||||
void
|
||||
nsSVGUseElement::SyncWidthHeight(PRUint8 aAttrEnum)
|
||||
nsSVGUseElement::SyncWidthHeight(nsIAtom* aName)
|
||||
{
|
||||
NS_ASSERTION(aName == nsGkAtoms::width || aName == nsGkAtoms::height,
|
||||
"The clue is in the function name");
|
||||
|
||||
if (HasValidDimensions() == !mClone) {
|
||||
TriggerReclone();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mClone && (aAttrEnum == WIDTH || aAttrEnum == HEIGHT)) {
|
||||
if (mClone) {
|
||||
nsCOMPtr<nsIDOMSVGSymbolElement> symbol = do_QueryInterface(mClone);
|
||||
nsCOMPtr<nsIDOMSVGSVGElement> svg = do_QueryInterface(mClone);
|
||||
|
||||
if (symbol || svg) {
|
||||
nsIAtom* aAttrName = aAttrEnum == WIDTH ?
|
||||
nsGkAtoms::width : nsGkAtoms::height;
|
||||
|
||||
if (mLengthAttributes[aAttrEnum].IsExplicitlySet()) {
|
||||
PRUint32 index = *sLengthInfo[WIDTH].mName == aName ? WIDTH : HEIGHT;
|
||||
if (mLengthAttributes[index].IsExplicitlySet()) {
|
||||
static_cast<nsSVGElement*>(mClone.get())->
|
||||
SetLength(aAttrName, mLengthAttributes[aAttrEnum]);
|
||||
SetLength(aName, mLengthAttributes[index]);
|
||||
} else {
|
||||
// Our width/height attribute is now no longer explicitly set, so we
|
||||
// need to revert the clone's width/height to the width/height of the
|
||||
|
@ -497,22 +498,6 @@ nsSVGUseElement::PrependLocalTransformTo(const gfxMatrix &aMatrix) const
|
|||
return matrix.PreMultiply(gfxMatrix().Translate(gfxPoint(x, y)));
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGUseElement::DidChangeLength(PRUint8 aAttrEnum, bool aDoSetAttr)
|
||||
{
|
||||
nsSVGUseElementBase::DidChangeLength(aAttrEnum, aDoSetAttr);
|
||||
|
||||
SyncWidthHeight(aAttrEnum);
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGUseElement::DidAnimateLength(PRUint8 aAttrEnum)
|
||||
{
|
||||
nsSVGUseElementBase::DidAnimateLength(aAttrEnum);
|
||||
|
||||
SyncWidthHeight(aAttrEnum);
|
||||
}
|
||||
|
||||
nsSVGElement::LengthAttributesInfo
|
||||
nsSVGUseElement::GetLengthInfo()
|
||||
{
|
||||
|
@ -520,33 +505,6 @@ nsSVGUseElement::GetLengthInfo()
|
|||
NS_ARRAY_LENGTH(sLengthInfo));
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGUseElement::DidChangeString(PRUint8 aAttrEnum)
|
||||
{
|
||||
nsSVGUseElementBase::DidChangeString(aAttrEnum);
|
||||
|
||||
if (aAttrEnum == HREF) {
|
||||
// we're changing our nature, clear out the clone information
|
||||
mOriginal = nsnull;
|
||||
UnlinkSource();
|
||||
TriggerReclone();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsSVGUseElement::DidAnimateString(PRUint8 aAttrEnum)
|
||||
{
|
||||
if (aAttrEnum == HREF) {
|
||||
// we're changing our nature, clear out the clone information
|
||||
mOriginal = nsnull;
|
||||
UnlinkSource();
|
||||
TriggerReclone();
|
||||
return;
|
||||
}
|
||||
|
||||
nsSVGUseElementBase::DidAnimateString(aAttrEnum);
|
||||
}
|
||||
|
||||
nsSVGElement::StringAttributesInfo
|
||||
nsSVGUseElement::GetStringInfo()
|
||||
{
|
||||
|
|
|
@ -103,10 +103,6 @@ public:
|
|||
|
||||
// nsSVGElement specializations:
|
||||
virtual gfxMatrix PrependLocalTransformTo(const gfxMatrix &aMatrix) const;
|
||||
virtual void DidChangeLength(PRUint8 aAttrEnum, bool aDoSetAttr);
|
||||
virtual void DidAnimateLength(PRUint8 aAttrEnum);
|
||||
virtual void DidChangeString(PRUint8 aAttrEnum);
|
||||
virtual void DidAnimateString(PRUint8 aAttrEnum);
|
||||
|
||||
// nsIContent interface
|
||||
virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const;
|
||||
|
@ -133,7 +129,7 @@ protected:
|
|||
virtual StringAttributesInfo GetStringInfo();
|
||||
|
||||
bool HasValidDimensions();
|
||||
void SyncWidthHeight(PRUint8 aAttrEnum);
|
||||
void SyncWidthHeight(nsIAtom *aName);
|
||||
void LookupHref();
|
||||
void TriggerReclone();
|
||||
void UnlinkSource();
|
||||
|
|
|
@ -141,7 +141,9 @@ EventTarget::AddEventListener(JSContext* aCx, uintN aArgc, jsval* aVp)
|
|||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
|
||||
EventTarget* self = GetPrivate(aCx, obj);
|
||||
JS_ASSERT(self);
|
||||
if (!self) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JSString* type;
|
||||
JSObject* listener;
|
||||
|
@ -167,7 +169,9 @@ EventTarget::RemoveEventListener(JSContext* aCx, uintN aArgc, jsval* aVp)
|
|||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
|
||||
EventTarget* self = GetPrivate(aCx, obj);
|
||||
JS_ASSERT(self);
|
||||
if (!self) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JSString* type;
|
||||
JSObject* listener;
|
||||
|
@ -193,7 +197,9 @@ EventTarget::DispatchEvent(JSContext* aCx, uintN aArgc, jsval* aVp)
|
|||
JSObject* obj = JS_THIS_OBJECT(aCx, aVp);
|
||||
|
||||
EventTarget* self = GetPrivate(aCx, obj);
|
||||
JS_ASSERT(self);
|
||||
if (!self) {
|
||||
return true;
|
||||
}
|
||||
|
||||
JSObject* event;
|
||||
if (!JS_ConvertArguments(aCx, aArgc, JS_ARGV(aCx, aVp), "o", &event)) {
|
||||
|
|
|
@ -112,6 +112,29 @@ public:
|
|||
return proto;
|
||||
}
|
||||
|
||||
static void
|
||||
ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers)
|
||||
{
|
||||
JS_ASSERT(!JS_IsExceptionPending(aCx));
|
||||
|
||||
WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aCx, aObj);
|
||||
JS_ASSERT(worker);
|
||||
|
||||
if (aSaveEventHandlers) {
|
||||
for (int index = 0; index < STRING_COUNT; index++) {
|
||||
const char* name = sEventStrings[index];
|
||||
jsval listener;
|
||||
if (!worker->GetEventListenerOnEventTarget(aCx, name + 2, &listener) ||
|
||||
!JS_DefineProperty(aCx, aObj, name, listener, NULL, NULL,
|
||||
(PROPERTY_FLAGS & ~JSPROP_SHARED))) {
|
||||
JS_ClearPendingException(aCx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SetJSPrivateSafeish(aCx, aObj, NULL);
|
||||
}
|
||||
|
||||
protected:
|
||||
static WorkerPrivate*
|
||||
GetInstancePrivate(JSContext* aCx, JSObject* aObj, const char* aFunctionName);
|
||||
|
@ -225,7 +248,7 @@ private:
|
|||
JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass);
|
||||
WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aCx, aObj);
|
||||
if (worker) {
|
||||
worker->FinalizeInstance(aCx);
|
||||
worker->FinalizeInstance(aCx, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -337,6 +360,12 @@ public:
|
|||
return proto;
|
||||
}
|
||||
|
||||
static void
|
||||
ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers)
|
||||
{
|
||||
Worker::ClearPrivateSlot(aCx, aObj, aSaveEventHandlers);
|
||||
}
|
||||
|
||||
private:
|
||||
// No instance of this class should ever be created so these are explicitly
|
||||
// left without an implementation to prevent linking in case someone tries to
|
||||
|
@ -369,7 +398,7 @@ private:
|
|||
JS_ASSERT(JS_GET_CLASS(aCx, aObj) == &sClass);
|
||||
WorkerPrivate* worker = GetJSPrivateSafeish<WorkerPrivate>(aCx, aObj);
|
||||
if (worker) {
|
||||
worker->FinalizeInstance(aCx);
|
||||
worker->FinalizeInstance(aCx, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -425,6 +454,20 @@ InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
|
|||
return Worker::InitClass(aCx, aGlobal, aProto, aMainRuntime);
|
||||
}
|
||||
|
||||
void
|
||||
ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers)
|
||||
{
|
||||
JSClass* clasp = JS_GET_CLASS(aCx, aObj);
|
||||
JS_ASSERT(clasp == Worker::Class() || clasp == ChromeWorker::Class());
|
||||
|
||||
if (clasp == ChromeWorker::Class()) {
|
||||
ChromeWorker::ClearPrivateSlot(aCx, aObj, aSaveEventHandlers);
|
||||
}
|
||||
else {
|
||||
Worker::ClearPrivateSlot(aCx, aObj, aSaveEventHandlers);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace worker
|
||||
|
||||
namespace chromeworker {
|
||||
|
|
|
@ -51,6 +51,9 @@ JSObject*
|
|||
InitClass(JSContext* aCx, JSObject* aGlobal, JSObject* aProto,
|
||||
bool aMainRuntime);
|
||||
|
||||
void
|
||||
ClearPrivateSlot(JSContext* aCx, JSObject* aObj, bool aSaveEventHandlers);
|
||||
|
||||
} // namespace worker
|
||||
|
||||
namespace chromeworker {
|
||||
|
|
|
@ -76,6 +76,7 @@
|
|||
#include "Principal.h"
|
||||
#include "RuntimeService.h"
|
||||
#include "ScriptLoader.h"
|
||||
#include "Worker.h"
|
||||
#include "WorkerFeature.h"
|
||||
#include "WorkerScope.h"
|
||||
|
||||
|
@ -646,7 +647,7 @@ public:
|
|||
NS_WARNING("Failed to dispatch, going to leak!");
|
||||
}
|
||||
|
||||
mFinishedWorker->FinalizeInstance(aCx);
|
||||
mFinishedWorker->FinalizeInstance(aCx, false);
|
||||
|
||||
RuntimeService* runtime = RuntimeService::GetService();
|
||||
NS_ASSERTION(runtime, "This should never be null!");
|
||||
|
@ -678,7 +679,7 @@ public:
|
|||
|
||||
RuntimeService::AutoSafeJSContext cx;
|
||||
|
||||
mFinishedWorker->FinalizeInstance(cx);
|
||||
mFinishedWorker->FinalizeInstance(cx, false);
|
||||
|
||||
RuntimeService* runtime = RuntimeService::GetService();
|
||||
NS_ASSERTION(runtime, "This should never be null!");
|
||||
|
@ -1772,7 +1773,7 @@ WorkerPrivateParent<Derived>::Notify(JSContext* aCx, Status aStatus)
|
|||
mParentStatus = aStatus;
|
||||
}
|
||||
|
||||
FinalizeInstance(aCx);
|
||||
FinalizeInstance(aCx, false);
|
||||
|
||||
if (pending) {
|
||||
WorkerPrivate* self = ParentAsWorkerPrivate();
|
||||
|
@ -1867,13 +1868,21 @@ WorkerPrivateParent<Derived>::Resume(JSContext* aCx)
|
|||
|
||||
template <class Derived>
|
||||
void
|
||||
WorkerPrivateParent<Derived>::FinalizeInstance(JSContext* aCx)
|
||||
WorkerPrivateParent<Derived>::FinalizeInstance(JSContext* aCx,
|
||||
bool aFromJSFinalizer)
|
||||
{
|
||||
AssertIsOnParentThread();
|
||||
|
||||
if (mJSObject) {
|
||||
// Make sure we're in the right compartment.
|
||||
JSAutoEnterCompartment ac;
|
||||
if (!ac.enter(aCx, mJSObject)) {
|
||||
NS_ERROR("How can this fail?!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Decouple the object from the private now.
|
||||
SetJSPrivateSafeish(aCx, mJSObject, nsnull);
|
||||
worker::ClearPrivateSlot(aCx, mJSObject, !aFromJSFinalizer);
|
||||
|
||||
// Clear the JS object.
|
||||
mJSObject = nsnull;
|
||||
|
|
|
@ -276,7 +276,7 @@ public:
|
|||
}
|
||||
|
||||
void
|
||||
FinalizeInstance(JSContext* aCx);
|
||||
FinalizeInstance(JSContext* aCx, bool aFromJSFinalizer);
|
||||
|
||||
bool
|
||||
Terminate(JSContext* aCx)
|
||||
|
|
|
@ -26,6 +26,61 @@ Tests of DOM Worker terminate feature
|
|||
|
||||
var interval;
|
||||
|
||||
var worker;
|
||||
|
||||
function messageListener(event) {
|
||||
is(event.data, "Still alive!", "Correct message!");
|
||||
if (messageCount++ == 20) {
|
||||
ok(worker.onmessage === messageListener,
|
||||
"Correct listener before terminate");
|
||||
|
||||
worker.terminate();
|
||||
|
||||
var exception = false;
|
||||
try {
|
||||
worker.addEventListener("message", messageListener, false);
|
||||
}
|
||||
catch (e) {
|
||||
exception = true;
|
||||
}
|
||||
is(exception, false, "addEventListener didn't throw after terminate");
|
||||
|
||||
exception = false;
|
||||
try {
|
||||
worker.removeEventListener("message", messageListener, false);
|
||||
}
|
||||
catch (e) {
|
||||
exception = true;
|
||||
}
|
||||
is(exception, false, "removeEventListener didn't throw after terminate");
|
||||
|
||||
exception = false;
|
||||
try {
|
||||
worker.postMessage("foo");
|
||||
}
|
||||
catch (e) {
|
||||
exception = true;
|
||||
}
|
||||
is(exception, false, "postMessage didn't throw after terminate");
|
||||
|
||||
exception = false;
|
||||
try {
|
||||
worker.terminate();
|
||||
}
|
||||
catch (e) {
|
||||
exception = true;
|
||||
}
|
||||
is(exception, false, "terminate didn't throw after terminate");
|
||||
|
||||
ok(worker.onmessage === messageListener,
|
||||
"Correct listener after terminate");
|
||||
|
||||
worker.onmessage = function(event) { }
|
||||
|
||||
interval = setInterval(testCount, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
function testCount() {
|
||||
is(messageCount, 21, "Received another message after terminated!");
|
||||
if (intervalCount++ == 5) {
|
||||
|
@ -34,19 +89,8 @@ Tests of DOM Worker terminate feature
|
|||
}
|
||||
}
|
||||
|
||||
var worker = new Worker("terminate_worker.js");
|
||||
worker.onmessage = function(event) {
|
||||
is(event.data, "Still alive!", "Bad message!");
|
||||
if (messageCount++ == 20) {
|
||||
worker.terminate();
|
||||
interval = setInterval(testCount, 1000);
|
||||
}
|
||||
};
|
||||
|
||||
worker.onerror = function(event) {
|
||||
ok(false, "Worker had an error: " + event.data);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
worker = new Worker("terminate_worker.js");
|
||||
worker.onmessage = messageListener;
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
|
|
|
@ -179,6 +179,8 @@ CanvasLayerD3D10::UpdateSurface()
|
|||
destination = (PRUint8*)map.pData;
|
||||
}
|
||||
|
||||
mGLContext->MakeCurrent();
|
||||
|
||||
// We have to flush to ensure that any buffered GL operations are
|
||||
// in the framebuffer before we read.
|
||||
mGLContext->fFlush();
|
||||
|
|
|
@ -113,6 +113,8 @@ CanvasLayerD3D9::UpdateSurface()
|
|||
destination = (PRUint8*)r.pBits;
|
||||
}
|
||||
|
||||
mGLContext->MakeCurrent();
|
||||
|
||||
// We have to flush to ensure that any buffered GL operations are
|
||||
// in the framebuffer before we read.
|
||||
mGLContext->fFlush();
|
||||
|
|
|
@ -247,14 +247,14 @@ protected:
|
|||
log.SetLength(len);
|
||||
|
||||
if (!success) {
|
||||
fprintf (stderr, "=== SHADER COMPILATION FAILED ===\n");
|
||||
printf_stderr("=== SHADER COMPILATION FAILED ===\n");
|
||||
} else {
|
||||
fprintf (stderr, "=== SHADER COMPILATION WARNINGS ===\n");
|
||||
printf_stderr("=== SHADER COMPILATION WARNINGS ===\n");
|
||||
}
|
||||
|
||||
fprintf (stderr, "=== Source:\n%s\n", aShaderSource);
|
||||
fprintf (stderr, "=== Log:\n%s\n", log.get());
|
||||
fprintf (stderr, "============\n");
|
||||
printf_stderr("=== Source:\n%s\n", aShaderSource);
|
||||
printf_stderr("=== Log:\n%s\n", log.get());
|
||||
printf_stderr("============\n");
|
||||
|
||||
if (!success) {
|
||||
mGL->fDeleteShader(sh);
|
||||
|
@ -303,12 +303,12 @@ protected:
|
|||
log.SetLength(len);
|
||||
|
||||
if (!success) {
|
||||
fprintf (stderr, "=== PROGRAM LINKING FAILED ===\n");
|
||||
printf_stderr("=== PROGRAM LINKING FAILED ===\n");
|
||||
} else {
|
||||
fprintf (stderr, "=== PROGRAM LINKING WARNINGS ===\n");
|
||||
printf_stderr("=== PROGRAM LINKING WARNINGS ===\n");
|
||||
}
|
||||
fprintf (stderr, "=== Log:\n%s\n", log.get());
|
||||
fprintf (stderr, "============\n");
|
||||
printf_stderr("=== Log:\n%s\n", log.get());
|
||||
printf_stderr("============\n");
|
||||
}
|
||||
|
||||
// We can mark the shaders for deletion; they're attached to the program
|
||||
|
|
|
@ -891,15 +891,13 @@ gfxHarfBuzzShaper::InitTextRun(gfxContext *aContext,
|
|||
hb_buffer_reverse(buffer);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
nsresult rv =
|
||||
#endif
|
||||
SetGlyphsFromRun(aContext, aTextRun, buffer, aRunStart, aRunLength);
|
||||
NS_WARN_IF_FALSE(NS_SUCCEEDED(rv), "failed to store glyphs into textrun");
|
||||
hb_buffer_destroy(buffer);
|
||||
hb_font_destroy(font);
|
||||
|
||||
return PR_TRUE;
|
||||
return NS_SUCCEEDED(rv);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -144,7 +144,7 @@ namespace StructType {
|
|||
namespace FunctionType {
|
||||
static JSBool Create(JSContext* cx, uintN argc, jsval* vp);
|
||||
static JSBool ConstructData(JSContext* cx, JSObject* typeObj,
|
||||
JSObject* dataObj, JSObject* fnObj, JSObject* thisObj);
|
||||
JSObject* dataObj, JSObject* fnObj, JSObject* thisObj, jsval errVal);
|
||||
|
||||
static JSBool Call(JSContext* cx, uintN argc, jsval* vp);
|
||||
|
||||
|
@ -3286,8 +3286,8 @@ PointerType::ConstructData(JSContext* cx,
|
|||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (argc > 2) {
|
||||
JS_ReportError(cx, "constructor takes 0, 1, or 2 arguments");
|
||||
if (argc > 3) {
|
||||
JS_ReportError(cx, "constructor takes 0, 1, 2, or 3 arguments");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
|
@ -3298,21 +3298,47 @@ PointerType::ConstructData(JSContext* cx,
|
|||
// Set return value early, must not observe *vp after
|
||||
JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(result));
|
||||
|
||||
if (argc == 0) {
|
||||
// Construct a null pointer.
|
||||
// There are 3 things that we might be creating here:
|
||||
// 1 - A null pointer (no arguments)
|
||||
// 2 - An initialized pointer (1 argument)
|
||||
// 3 - A closure (1-3 arguments)
|
||||
//
|
||||
// The API doesn't give us a perfect way to distinguish 2 and 3, but the
|
||||
// heuristics we use should be fine.
|
||||
|
||||
//
|
||||
// Case 1 - Null pointer
|
||||
//
|
||||
if (argc == 0)
|
||||
return JS_TRUE;
|
||||
|
||||
// Analyze the arguments a bit to decide what to do next.
|
||||
jsval* argv = JS_ARGV(cx, vp);
|
||||
JSObject* baseObj = PointerType::GetBaseType(cx, obj);
|
||||
bool looksLikeClosure = CType::GetTypeCode(cx, baseObj) == TYPE_function &&
|
||||
JSVAL_IS_OBJECT(argv[0]) &&
|
||||
JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(argv[0]));
|
||||
|
||||
//
|
||||
// Case 2 - Initialized pointer
|
||||
//
|
||||
if (!looksLikeClosure) {
|
||||
if (argc != 1) {
|
||||
JS_ReportError(cx, "first argument must be a function");
|
||||
return JS_FALSE;
|
||||
}
|
||||
return ExplicitConvert(cx, argv[0], obj, CData::GetData(cx, result));
|
||||
}
|
||||
|
||||
jsval* argv = JS_ARGV(cx, vp);
|
||||
if (argc >= 1) {
|
||||
JSObject* baseObj = PointerType::GetBaseType(cx, obj);
|
||||
if (CType::GetTypeCode(cx, baseObj) == TYPE_function &&
|
||||
JSVAL_IS_OBJECT(argv[0]) &&
|
||||
JS_ObjectIsCallable(cx, JSVAL_TO_OBJECT(argv[0]))) {
|
||||
// Construct a FunctionType.ptr from a JS function, and allow an
|
||||
// optional 'this' argument.
|
||||
//
|
||||
// Case 3 - Closure
|
||||
//
|
||||
|
||||
// The second argument is an optional 'this' parameter with which to invoke
|
||||
// the given js function. Callers may leave this blank, or pass null if they
|
||||
// wish to pass the third argument.
|
||||
JSObject* thisObj = NULL;
|
||||
if (argc == 2) {
|
||||
if (argc >= 2) {
|
||||
if (JSVAL_IS_OBJECT(argv[1])) {
|
||||
thisObj = JSVAL_TO_OBJECT(argv[1]);
|
||||
} else if (!JS_ValueToObject(cx, argv[1], &thisObj)) {
|
||||
|
@ -3320,18 +3346,15 @@ PointerType::ConstructData(JSContext* cx,
|
|||
}
|
||||
}
|
||||
|
||||
// The third argument is an optional error sentinel that js-ctypes will return
|
||||
// if an exception is raised while executing the closure. The type must match
|
||||
// the return type of the callback.
|
||||
jsval errVal = JSVAL_VOID;
|
||||
if (argc == 3)
|
||||
errVal = argv[2];
|
||||
|
||||
JSObject* fnObj = JSVAL_TO_OBJECT(argv[0]);
|
||||
return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj);
|
||||
}
|
||||
|
||||
if (argc == 2) {
|
||||
JS_ReportError(cx, "first argument must be a function");
|
||||
return JS_FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
// Construct from a raw pointer value.
|
||||
return ExplicitConvert(cx, argv[0], obj, CData::GetData(cx, result));
|
||||
return FunctionType::ConstructData(cx, baseObj, result, fnObj, thisObj, errVal);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
|
@ -4922,7 +4945,8 @@ FunctionType::ConstructData(JSContext* cx,
|
|||
JSObject* typeObj,
|
||||
JSObject* dataObj,
|
||||
JSObject* fnObj,
|
||||
JSObject* thisObj)
|
||||
JSObject* thisObj,
|
||||
jsval errVal)
|
||||
{
|
||||
JS_ASSERT(CType::GetTypeCode(cx, typeObj) == TYPE_function);
|
||||
|
||||
|
@ -4939,7 +4963,7 @@ FunctionType::ConstructData(JSContext* cx,
|
|||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JSObject* closureObj = CClosure::Create(cx, typeObj, fnObj, thisObj, data);
|
||||
JSObject* closureObj = CClosure::Create(cx, typeObj, fnObj, thisObj, errVal, data);
|
||||
if (!closureObj)
|
||||
return JS_FALSE;
|
||||
js::AutoObjectRooter root(cx, closureObj);
|
||||
|
@ -5220,6 +5244,7 @@ CClosure::Create(JSContext* cx,
|
|||
JSObject* typeObj,
|
||||
JSObject* fnObj,
|
||||
JSObject* thisObj,
|
||||
jsval errVal,
|
||||
PRFuncPtr* fnptr)
|
||||
{
|
||||
JS_ASSERT(fnObj);
|
||||
|
@ -5234,7 +5259,7 @@ CClosure::Create(JSContext* cx,
|
|||
JS_ASSERT(!fninfo->mIsVariadic);
|
||||
JS_ASSERT(GetABICode(cx, fninfo->mABI) != ABI_WINAPI);
|
||||
|
||||
AutoPtr<ClosureInfo> cinfo(cx->new_<ClosureInfo>());
|
||||
AutoPtr<ClosureInfo> cinfo(cx->new_<ClosureInfo>(JS_GetRuntime(cx)));
|
||||
if (!cinfo) {
|
||||
JS_ReportOutOfMemory(cx);
|
||||
return NULL;
|
||||
|
@ -5277,6 +5302,37 @@ CClosure::Create(JSContext* cx,
|
|||
cinfo->cxThread = JS_GetContextThread(cx);
|
||||
#endif
|
||||
|
||||
// Prepare the error sentinel value. It's important to do this now, because
|
||||
// we might be unable to convert the value to the proper type. If so, we want
|
||||
// the caller to know about it _now_, rather than some uncertain time in the
|
||||
// future when the error sentinel is actually needed.
|
||||
if (!JSVAL_IS_VOID(errVal)) {
|
||||
|
||||
// Make sure the callback returns something.
|
||||
if (CType::GetTypeCode(cx, fninfo->mReturnType) == TYPE_void_t) {
|
||||
JS_ReportError(cx, "A void callback can't pass an error sentinel");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// With the exception of void, the FunctionType constructor ensures that
|
||||
// the return type has a defined size.
|
||||
JS_ASSERT(CType::IsSizeDefined(cx, fninfo->mReturnType));
|
||||
|
||||
// Allocate a buffer for the return value.
|
||||
size_t rvSize = CType::GetSize(cx, fninfo->mReturnType);
|
||||
cinfo->errResult = cx->malloc_(rvSize);
|
||||
if (!cinfo->errResult)
|
||||
return NULL;
|
||||
|
||||
// Do the value conversion. This might fail, in which case we throw.
|
||||
if (!ImplicitConvert(cx, errVal, fninfo->mReturnType, cinfo->errResult,
|
||||
false, NULL))
|
||||
return NULL;
|
||||
} else {
|
||||
cinfo->errResult = NULL;
|
||||
}
|
||||
|
||||
// Copy the important bits of context into cinfo.
|
||||
cinfo->closureObj = result;
|
||||
cinfo->typeObj = typeObj;
|
||||
cinfo->thisObj = thisObj;
|
||||
|
@ -5294,17 +5350,14 @@ CClosure::Create(JSContext* cx,
|
|||
ffi_status status = ffi_prep_closure_loc(cinfo->closure, &fninfo->mCIF,
|
||||
CClosure::ClosureStub, cinfo.get(), code);
|
||||
if (status != FFI_OK) {
|
||||
ffi_closure_free(cinfo->closure);
|
||||
JS_ReportError(cx, "couldn't create closure - libffi error");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Stash the ClosureInfo struct on our new object.
|
||||
if (!JS_SetReservedSlot(cx, result, SLOT_CLOSUREINFO,
|
||||
PRIVATE_TO_JSVAL(cinfo.get()))) {
|
||||
ffi_closure_free(cinfo->closure);
|
||||
PRIVATE_TO_JSVAL(cinfo.get())))
|
||||
return NULL;
|
||||
}
|
||||
cinfo.forget();
|
||||
|
||||
// Casting between void* and a function pointer is forbidden in C and C++.
|
||||
|
@ -5344,9 +5397,6 @@ CClosure::Finalize(JSContext* cx, JSObject* obj)
|
|||
return;
|
||||
|
||||
ClosureInfo* cinfo = static_cast<ClosureInfo*>(JSVAL_TO_PRIVATE(slot));
|
||||
if (cinfo->closure)
|
||||
ffi_closure_free(cinfo->closure);
|
||||
|
||||
cx->delete_(cinfo);
|
||||
}
|
||||
|
||||
|
@ -5385,8 +5435,9 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
|
|||
// Initialize the result to zero, in case something fails. Small integer types
|
||||
// are promoted to a word-sized ffi_arg, so we must be careful to zero the
|
||||
// whole word.
|
||||
size_t rvSize = 0;
|
||||
if (cif->rtype != &ffi_type_void) {
|
||||
size_t size = cif->rtype->size;
|
||||
rvSize = cif->rtype->size;
|
||||
switch (typeCode) {
|
||||
#define DEFINE_INT_TYPE(name, type, ffiType) \
|
||||
case TYPE_##name:
|
||||
|
@ -5395,12 +5446,12 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
|
|||
#define DEFINE_CHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
|
||||
#define DEFINE_JSCHAR_TYPE(x, y, z) DEFINE_INT_TYPE(x, y, z)
|
||||
#include "typedefs.h"
|
||||
size = Align(size, sizeof(ffi_arg));
|
||||
rvSize = Align(rvSize, sizeof(ffi_arg));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
memset(result, 0, size);
|
||||
memset(result, 0, rvSize);
|
||||
}
|
||||
|
||||
// Get a death grip on 'closureObj'.
|
||||
|
@ -5425,17 +5476,51 @@ CClosure::ClosureStub(ffi_cif* cif, void* result, void** args, void* userData)
|
|||
// Call the JS function. 'thisObj' may be NULL, in which case the JS engine
|
||||
// will find an appropriate object to use.
|
||||
jsval rval;
|
||||
if (!JS_CallFunctionValue(cx, thisObj, OBJECT_TO_JSVAL(jsfnObj), cif->nargs,
|
||||
argv.begin(), &rval))
|
||||
return;
|
||||
JSBool success = JS_CallFunctionValue(cx, thisObj, OBJECT_TO_JSVAL(jsfnObj),
|
||||
cif->nargs, argv.begin(), &rval);
|
||||
|
||||
// Convert the result. Note that we pass 'isArgument = false', such that
|
||||
// ImplicitConvert will *not* autoconvert a JS string into a pointer-to-char
|
||||
// type, which would require an allocation that we can't track. The JS
|
||||
// function must perform this conversion itself and return a PointerType
|
||||
// CData; thusly, the burden of freeing the data is left to the user.
|
||||
if (!ImplicitConvert(cx, rval, fninfo->mReturnType, result, false, NULL))
|
||||
if (success && cif->rtype != &ffi_type_void)
|
||||
success = ImplicitConvert(cx, rval, fninfo->mReturnType, result, false,
|
||||
NULL);
|
||||
|
||||
if (!success) {
|
||||
// Something failed. The callee may have thrown, or it may not have
|
||||
// returned a value that ImplicitConvert() was happy with. Depending on how
|
||||
// prudent the consumer has been, we may or may not have a recovery plan.
|
||||
|
||||
// In any case, a JS exception cannot be passed to C code, so report the
|
||||
// exception if any and clear it from the cx.
|
||||
if (JS_IsExceptionPending(cx))
|
||||
JS_ReportPendingException(cx);
|
||||
|
||||
if (cinfo->errResult) {
|
||||
// Good case: we have a sentinel that we can return. Copy it in place of
|
||||
// the actual return value, and then proceed.
|
||||
|
||||
// The buffer we're returning might be larger than the size of the return
|
||||
// type, due to libffi alignment issues (see above). But it should never
|
||||
// be smaller.
|
||||
size_t copySize = CType::GetSize(cx, fninfo->mReturnType);
|
||||
JS_ASSERT(copySize <= rvSize);
|
||||
memcpy(result, cinfo->errResult, copySize);
|
||||
} else {
|
||||
// Bad case: not much we can do here. The rv is already zeroed out, so we
|
||||
// just report (another) error and hope for the best. JS_ReportError will
|
||||
// actually throw an exception here, so then we have to report it. Again.
|
||||
// Ugh.
|
||||
JS_ReportError(cx, "JavaScript callback failed, and an error sentinel "
|
||||
"was not specified.");
|
||||
if (JS_IsExceptionPending(cx))
|
||||
JS_ReportPendingException(cx);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Small integer types must be returned as a word-sized ffi_arg. Coerce it
|
||||
// back into the size libffi expects.
|
||||
|
|
|
@ -322,14 +322,32 @@ struct FunctionInfo
|
|||
struct ClosureInfo
|
||||
{
|
||||
JSContext* cx; // JSContext to use
|
||||
JSRuntime* rt; // Used in the destructor, where cx might have already
|
||||
// been GCed.
|
||||
JSObject* closureObj; // CClosure object
|
||||
JSObject* typeObj; // FunctionType describing the C function
|
||||
JSObject* thisObj; // 'this' object to use for the JS function call
|
||||
JSObject* jsfnObj; // JS function
|
||||
void* errResult; // Result that will be returned if the closure throws
|
||||
ffi_closure* closure; // The C closure itself
|
||||
#ifdef DEBUG
|
||||
jsword cxThread; // The thread on which the context may be used
|
||||
#endif
|
||||
|
||||
// Anything conditionally freed in the destructor should be initialized to
|
||||
// NULL here.
|
||||
ClosureInfo(JSRuntime* runtime)
|
||||
: rt(runtime)
|
||||
, errResult(NULL)
|
||||
, closure(NULL)
|
||||
{}
|
||||
|
||||
~ClosureInfo() {
|
||||
if (closure)
|
||||
ffi_closure_free(closure);
|
||||
if (errResult)
|
||||
rt->free_(errResult);
|
||||
};
|
||||
};
|
||||
|
||||
bool IsCTypesGlobal(JSContext* cx, JSObject* obj);
|
||||
|
@ -491,7 +509,7 @@ namespace FunctionType {
|
|||
|
||||
namespace CClosure {
|
||||
JSObject* Create(JSContext* cx, JSObject* typeObj, JSObject* fnObj,
|
||||
JSObject* thisObj, PRFuncPtr* fnptr);
|
||||
JSObject* thisObj, jsval errVal, PRFuncPtr* fnptr);
|
||||
}
|
||||
|
||||
namespace CData {
|
||||
|
|
|
@ -12362,7 +12362,7 @@ $as_echo "#define HAVE_AS_STRING_PSEUDO_OP 1" >>confdefs.h
|
|||
fi
|
||||
|
||||
case "$target" in
|
||||
*-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*)
|
||||
*-apple-darwin1* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*)
|
||||
|
||||
$as_echo "#define FFI_MMAP_EXEC_WRIT 1" >>confdefs.h
|
||||
|
||||
|
|
|
@ -316,7 +316,8 @@ if test x$TARGET = xX86 || test x$TARGET = xX86_WIN32 || test x$TARGET = xX86_64
|
|||
fi
|
||||
|
||||
case "$target" in
|
||||
*-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*)
|
||||
# Darwin 10 (OSX 10.6) and beyond allocate non-executable pages
|
||||
*-apple-darwin1* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*)
|
||||
AC_DEFINE(FFI_MMAP_EXEC_WRIT, 1,
|
||||
[Cannot use malloc on this target, so, we revert to
|
||||
alternative means])
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
# HG changeset patch
|
||||
# Parent e357f3f732a0f3e98f8bd4661de03c9042c5c330
|
||||
# User Landry Breuil <landry@openbsd.org>
|
||||
treat powerpc-*-openbsd* as powerpc-*-freebsd*
|
||||
|
||||
|
||||
diff --git a/js/src/ctypes/libffi/configure b/js/src/ctypes/libffi/configure
|
||||
--- a/js/src/ctypes/libffi/configure
|
||||
+++ b/js/src/ctypes/libffi/configure
|
||||
@@ -11272,17 +11272,17 @@ case "$host" in
|
||||
TARGET=POWERPC; TARGETDIR=powerpc
|
||||
;;
|
||||
powerpc-*-darwin*)
|
||||
TARGET=POWERPC_DARWIN; TARGETDIR=powerpc
|
||||
;;
|
||||
powerpc-*-aix* | rs6000-*-aix*)
|
||||
TARGET=POWERPC_AIX; TARGETDIR=powerpc
|
||||
;;
|
||||
- powerpc-*-freebsd*)
|
||||
+ powerpc-*-freebsd* | powerpc-*-openbsd*)
|
||||
TARGET=POWERPC_FREEBSD; TARGETDIR=powerpc
|
||||
;;
|
||||
powerpc*-*-rtems*)
|
||||
TARGET=POWERPC; TARGETDIR=powerpc
|
||||
;;
|
||||
|
||||
s390-*-* | s390x-*-*)
|
||||
TARGET=S390; TARGETDIR=s390
|
|
@ -0,0 +1,27 @@
|
|||
diff --git a/js/src/ctypes/libffi/configure b/js/src/ctypes/libffi/configure
|
||||
index 2c08e1b..37e3055 100755
|
||||
--- a/js/src/ctypes/libffi/configure
|
||||
+++ b/js/src/ctypes/libffi/configure
|
||||
@@ -12362,7 +12362,7 @@ $as_echo "#define HAVE_AS_STRING_PSEUDO_OP 1" >>confdefs.h
|
||||
fi
|
||||
|
||||
case "$target" in
|
||||
- *-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*)
|
||||
+ *-apple-darwin1* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*)
|
||||
|
||||
$as_echo "#define FFI_MMAP_EXEC_WRIT 1" >>confdefs.h
|
||||
|
||||
diff --git a/js/src/ctypes/libffi/configure.ac b/js/src/ctypes/libffi/configure.ac
|
||||
index e85cff1..1db02ce 100644
|
||||
--- a/js/src/ctypes/libffi/configure.ac
|
||||
+++ b/js/src/ctypes/libffi/configure.ac
|
||||
@@ -316,7 +316,8 @@ if test x$TARGET = xX86 || test x$TARGET = xX86_WIN32 || test x$TARGET = xX86_64
|
||||
fi
|
||||
|
||||
case "$target" in
|
||||
- *-apple-darwin10* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*)
|
||||
+ # Darwin 10 (OSX 10.6) and beyond allocate non-executable pages
|
||||
+ *-apple-darwin1* | *-*-freebsd* | *-*-openbsd* | *-pc-solaris*)
|
||||
AC_DEFINE(FFI_MMAP_EXEC_WRIT, 1,
|
||||
[Cannot use malloc on this target, so, we revert to
|
||||
alternative means])
|
|
@ -0,0 +1,3 @@
|
|||
var rg = /(X(?:.(?!X))*?Y)|(Y(?:.(?!Y))*?Z)/g;
|
||||
var str = "Y aaa X Match1 Y aaa Y Match2 Z";
|
||||
assertEq(str.match(rg) + "", "X Match1 Y,Y Match2 Z");
|
|
@ -555,7 +555,7 @@ js_Atomize(JSContext *cx, const char *bytes, size_t length, InternBehavior ib, F
|
|||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
if (!CheckStringLength(cx, length))
|
||||
if (!JSString::validateLength(cx, length))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
|
@ -597,7 +597,7 @@ js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, InternBehavio
|
|||
{
|
||||
CHECK_REQUEST(cx);
|
||||
|
||||
if (!CheckStringLength(cx, length))
|
||||
if (!JSString::validateLength(cx, length))
|
||||
return NULL;
|
||||
|
||||
return AtomizeInline(cx, &chars, length, ib);
|
||||
|
|
|
@ -254,6 +254,15 @@ types::TypeObjectString(TypeObject *type)
|
|||
return TypeString(Type::ObjectType(type));
|
||||
}
|
||||
|
||||
unsigned JSScript::id() {
|
||||
if (!id_) {
|
||||
id_ = ++compartment()->types.scriptCount;
|
||||
InferSpew(ISpewOps, "script #%u: %p %s:%d",
|
||||
id_, this, filename ? filename : "<null>", lineno);
|
||||
}
|
||||
return id_;
|
||||
}
|
||||
|
||||
void
|
||||
types::InferSpew(SpewChannel channel, const char *fmt, ...)
|
||||
{
|
||||
|
@ -874,12 +883,8 @@ public:
|
|||
|
||||
void newType(JSContext *cx, TypeSet *source, Type type)
|
||||
{
|
||||
if (!target->hasType(type)) {
|
||||
if (!target->hasType(type))
|
||||
script->analysis()->addTypeBarrier(cx, pc, target, type);
|
||||
return;
|
||||
}
|
||||
|
||||
target->addType(cx, type);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -2907,7 +2912,7 @@ TypeObject::setFlags(JSContext *cx, TypeObjectFlags flags)
|
|||
|
||||
this->flags |= flags;
|
||||
|
||||
InferSpew(ISpewOps, "%s: setFlags %u", TypeObjectString(this), flags);
|
||||
InferSpew(ISpewOps, "%s: setFlags 0x%x", TypeObjectString(this), flags);
|
||||
|
||||
ObjectStateChange(cx, this, false, false);
|
||||
}
|
||||
|
|
|
@ -1088,7 +1088,7 @@ JSScript::NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natom
|
|||
JS_ASSERT(cursor + length * sizeof(jsbytecode) + nsrcnotes * sizeof(jssrcnote) == data + size);
|
||||
|
||||
#ifdef DEBUG
|
||||
script->id_ = ++cx->compartment->types.scriptCount;
|
||||
script->id_ = 0;
|
||||
#endif
|
||||
|
||||
JS_ASSERT(script->getVersion() == version);
|
||||
|
|
|
@ -594,7 +594,7 @@ struct JSScript : public js::gc::Cell {
|
|||
*/
|
||||
uint32 id_;
|
||||
uint32 idpad;
|
||||
unsigned id() { return id_; }
|
||||
unsigned id();
|
||||
#else
|
||||
unsigned id() { return 0; }
|
||||
#endif
|
||||
|
|
|
@ -3033,10 +3033,8 @@ js_InitStringClass(JSContext *cx, JSObject *obj)
|
|||
JSFixedString *
|
||||
js_NewString(JSContext *cx, jschar *chars, size_t length)
|
||||
{
|
||||
if (!CheckStringLength(cx, length))
|
||||
return NULL;
|
||||
|
||||
JSFixedString *s = JSFixedString::new_(cx, chars, length);
|
||||
if (s)
|
||||
Probes::createString(cx, s, length);
|
||||
return s;
|
||||
}
|
||||
|
|
|
@ -45,30 +45,10 @@
|
|||
|
||||
#include "jscntxtinlines.h"
|
||||
#include "jsgcinlines.h"
|
||||
#include "vm/String-inl.h"
|
||||
|
||||
namespace js {
|
||||
|
||||
static inline bool
|
||||
CheckStringLength(JSContext *cx, size_t length)
|
||||
{
|
||||
if (JS_UNLIKELY(length > JSString::MAX_LENGTH)) {
|
||||
if (JS_ON_TRACE(cx)) {
|
||||
/*
|
||||
* If we can't leave the trace, signal OOM condition, otherwise
|
||||
* exit from trace before throwing.
|
||||
*/
|
||||
if (!CanLeaveTrace(cx))
|
||||
return NULL;
|
||||
|
||||
LeaveTrace(cx);
|
||||
}
|
||||
js_ReportAllocationOverflow(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* String builder that eagerly checks for over-allocation past the maximum
|
||||
* string length.
|
||||
|
@ -242,7 +222,7 @@ StringBuffer::length() const
|
|||
inline bool
|
||||
StringBuffer::checkLength(size_t length)
|
||||
{
|
||||
return CheckStringLength(context(), length);
|
||||
return JSString::validateLength(context(), length);
|
||||
}
|
||||
|
||||
extern bool
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
/* -*- Mode: C++ tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
|
|
|
@ -324,13 +324,11 @@ RegExpPrivate::create(JSContext *cx, JSString *source, RegExpFlag flags, TokenSt
|
|||
return RetType(self);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function should be deleted once we can. See bug 604774.
|
||||
*/
|
||||
static inline bool
|
||||
EnableYarrJIT(JSContext *cx)
|
||||
/* This function should be deleted once bad Android platforms phase out. See bug 604774. */
|
||||
inline bool
|
||||
RegExpPrivateCode::isJITRuntimeEnabled(JSContext *cx)
|
||||
{
|
||||
#if defined ANDROID && defined(JS_TRACER) && defined(JS_METHODJIT)
|
||||
#if defined(ANDROID) && defined(JS_TRACER) && defined(JS_METHODJIT)
|
||||
return cx->traceJitEnabled || cx->methodJitEnabled;
|
||||
#else
|
||||
return true;
|
||||
|
@ -338,38 +336,47 @@ EnableYarrJIT(JSContext *cx)
|
|||
}
|
||||
|
||||
inline bool
|
||||
RegExpPrivate::compileHelper(JSContext *cx, JSLinearString &pattern, TokenStream *ts)
|
||||
RegExpPrivateCode::compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts,
|
||||
uintN *parenCount, RegExpFlag flags)
|
||||
{
|
||||
#if ENABLE_YARR_JIT
|
||||
JSC::Yarr::ErrorCode yarrError;
|
||||
JSC::Yarr::YarrPattern yarrPattern(pattern, ignoreCase(), multiline(), &yarrError);
|
||||
/* Parse the pattern. */
|
||||
ErrorCode yarrError;
|
||||
YarrPattern yarrPattern(pattern, bool(flags & IgnoreCaseFlag), bool(flags & MultilineFlag),
|
||||
&yarrError);
|
||||
if (yarrError) {
|
||||
reportYarrError(cx, ts, yarrError);
|
||||
return false;
|
||||
}
|
||||
parenCount = yarrPattern.m_numSubpatterns;
|
||||
*parenCount = yarrPattern.m_numSubpatterns;
|
||||
|
||||
#if defined(JS_METHODJIT)
|
||||
if (EnableYarrJIT(cx) && !yarrPattern.m_containsBackreferences) {
|
||||
bool ok = cx->compartment->ensureJaegerCompartmentExists(cx);
|
||||
if (!ok)
|
||||
/*
|
||||
* The YARR JIT compiler attempts to compile the parsed pattern. If
|
||||
* it cannot, it informs us via |codeBlock.isFallBack()|, in which
|
||||
* case we have to bytecode compile it.
|
||||
*/
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) {
|
||||
if (!cx->compartment->ensureJaegerCompartmentExists(cx))
|
||||
return false;
|
||||
JSC::Yarr::JSGlobalData globalData(cx->compartment->jaegerCompartment()->execAlloc());
|
||||
JSC::Yarr::jitCompile(yarrPattern, &globalData, codeBlock);
|
||||
|
||||
JSGlobalData globalData(cx->compartment->jaegerCompartment()->execAlloc());
|
||||
jitCompile(yarrPattern, &globalData, codeBlock);
|
||||
if (!codeBlock.isFallBack())
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
codeBlock.setFallBack(true);
|
||||
byteCode = JSC::Yarr::byteCompile(yarrPattern, cx->compartment->regExpAllocator).get();
|
||||
byteCode = byteCompile(yarrPattern, cx->compartment->regExpAllocator).get();
|
||||
return true;
|
||||
#else
|
||||
#else /* !defined(ENABLE_YARR_JIT) */
|
||||
int error = 0;
|
||||
compiled = jsRegExpCompile(pattern.chars(), pattern.length(),
|
||||
ignoreCase() ? JSRegExpIgnoreCase : JSRegExpDoNotIgnoreCase,
|
||||
multiline() ? JSRegExpMultiline : JSRegExpSingleLine,
|
||||
&parenCount, &error);
|
||||
parenCount, &error);
|
||||
if (error) {
|
||||
reportPCREError(cx, error);
|
||||
return false;
|
||||
|
@ -386,7 +393,7 @@ RegExpPrivate::compile(JSContext *cx, TokenStream *ts)
|
|||
return false;
|
||||
|
||||
if (!sticky())
|
||||
return compileHelper(cx, *source, ts);
|
||||
return code.compile(cx, *source, ts, &parenCount, getFlags());
|
||||
|
||||
/*
|
||||
* The sticky case we implement hackily by prepending a caret onto the front
|
||||
|
@ -405,7 +412,36 @@ RegExpPrivate::compile(JSContext *cx, TokenStream *ts)
|
|||
JSLinearString *fakeySource = sb.finishString();
|
||||
if (!fakeySource)
|
||||
return false;
|
||||
return compileHelper(cx, *fakeySource, ts);
|
||||
return code.compile(cx, *fakeySource, ts, &parenCount, getFlags());
|
||||
}
|
||||
|
||||
inline RegExpPrivateCode::ExecuteResult
|
||||
RegExpPrivateCode::execute(JSContext *cx, const jschar *chars, size_t start, size_t length,
|
||||
int *output, size_t outputCount)
|
||||
{
|
||||
int result;
|
||||
#if ENABLE_YARR_JIT
|
||||
(void) cx; /* Unused. */
|
||||
if (codeBlock.isFallBack())
|
||||
result = JSC::Yarr::interpret(byteCode, chars, start, length, output);
|
||||
else
|
||||
result = JSC::Yarr::execute(codeBlock, chars, start, length, output);
|
||||
#else
|
||||
result = jsRegExpExecute(cx, compiled, chars, length, start, output, outputCount);
|
||||
#endif
|
||||
|
||||
if (result == -1)
|
||||
return Success_NotFound;
|
||||
|
||||
#if !ENABLE_YARR_JIT
|
||||
if (result < 0) {
|
||||
reportPCREError(cx, result);
|
||||
return Error;
|
||||
}
|
||||
#endif
|
||||
|
||||
JS_ASSERT(result >= 0);
|
||||
return Success;
|
||||
}
|
||||
|
||||
inline void
|
||||
|
|
|
@ -64,8 +64,8 @@ RegExpPrivate::executeInternal(JSContext *cx, RegExpStatics *res, JSString *inpu
|
|||
size_t *lastIndex, bool test, Value *rval)
|
||||
{
|
||||
const size_t pairCount = parenCount + 1;
|
||||
const size_t bufCount = pairCount * 3; /* Should be x2, but PCRE has... needs. */
|
||||
const size_t matchItemCount = pairCount * 2;
|
||||
const size_t bufCount = RegExpPrivateCode::getOutputSize(pairCount);
|
||||
|
||||
LifoAllocScope las(&cx->tempLifoAlloc());
|
||||
int *buf = cx->tempLifoAlloc().newArray<int>(bufCount);
|
||||
|
@ -100,27 +100,19 @@ RegExpPrivate::executeInternal(JSContext *cx, RegExpStatics *res, JSString *inpu
|
|||
inputOffset = *lastIndex;
|
||||
}
|
||||
|
||||
int result;
|
||||
#if ENABLE_YARR_JIT
|
||||
if (!codeBlock.isFallBack())
|
||||
result = JSC::Yarr::execute(codeBlock, chars, *lastIndex - inputOffset, len, buf);
|
||||
else
|
||||
result = JSC::Yarr::interpret(byteCode, chars, *lastIndex - inputOffset, len, buf);
|
||||
#else
|
||||
result = jsRegExpExecute(cx, compiled, chars, len, *lastIndex - inputOffset, buf, bufCount);
|
||||
#endif
|
||||
if (result == -1) {
|
||||
size_t start = *lastIndex - inputOffset;
|
||||
RegExpPrivateCode::ExecuteResult result = code.execute(cx, chars, start, len, buf, bufCount);
|
||||
|
||||
switch (result) {
|
||||
case RegExpPrivateCode::Error:
|
||||
return false;
|
||||
case RegExpPrivateCode::Success_NotFound:
|
||||
*rval = NullValue();
|
||||
return true;
|
||||
default:
|
||||
JS_ASSERT(result == RegExpPrivateCode::Success);
|
||||
}
|
||||
|
||||
#if !ENABLE_YARR_JIT
|
||||
if (result < 0) {
|
||||
reportPCREError(cx, result);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Adjust buf for the inputOffset. Use of sticky is rare and the matchItemCount is small, so
|
||||
* just do another pass.
|
||||
|
@ -260,9 +252,42 @@ Class js::RegExpClass = {
|
|||
NULL /* trace */
|
||||
};
|
||||
|
||||
#if !ENABLE_YARR_JIT
|
||||
#if ENABLE_YARR_JIT
|
||||
void
|
||||
RegExpPrivate::reportPCREError(JSContext *cx, int error)
|
||||
RegExpPrivateCode::reportYarrError(JSContext *cx, TokenStream *ts, ErrorCode error)
|
||||
{
|
||||
switch (error) {
|
||||
case JSC::Yarr::NoError:
|
||||
JS_NOT_REACHED("Called reportYarrError with value for no error");
|
||||
return;
|
||||
#define COMPILE_EMSG(__code, __msg) \
|
||||
case JSC::Yarr::__code: \
|
||||
if (ts) \
|
||||
ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, __msg); \
|
||||
else \
|
||||
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
|
||||
return
|
||||
COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX);
|
||||
COMPILE_EMSG(QuantifierOutOfOrder, JSMSG_BAD_QUANTIFIER);
|
||||
COMPILE_EMSG(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER);
|
||||
COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN);
|
||||
COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN);
|
||||
COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); /* "(?" with bad next char */
|
||||
COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE);
|
||||
COMPILE_EMSG(CharacterClassInvalidRange, JSMSG_BAD_CLASS_RANGE);
|
||||
COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE);
|
||||
COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER);
|
||||
COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH);
|
||||
#undef COMPILE_EMSG
|
||||
default:
|
||||
JS_NOT_REACHED("Unknown Yarr error code");
|
||||
}
|
||||
}
|
||||
|
||||
#else /* !ENABLE_YARR_JIT */
|
||||
|
||||
void
|
||||
RegExpPrivateCode::reportPCREError(JSContext *cx, int error)
|
||||
{
|
||||
#define REPORT(msg_) \
|
||||
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, msg_); \
|
||||
|
@ -292,38 +317,7 @@ RegExpPrivate::reportPCREError(JSContext *cx, int error)
|
|||
}
|
||||
#undef REPORT
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
RegExpPrivate::reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error)
|
||||
{
|
||||
switch (error) {
|
||||
case JSC::Yarr::NoError:
|
||||
JS_NOT_REACHED("Called reportYarrError with value for no error");
|
||||
return;
|
||||
#define COMPILE_EMSG(__code, __msg) \
|
||||
case JSC::Yarr::__code: \
|
||||
if (ts) \
|
||||
ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, __msg); \
|
||||
else \
|
||||
JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
|
||||
return
|
||||
COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX);
|
||||
COMPILE_EMSG(QuantifierOutOfOrder, JSMSG_BAD_QUANTIFIER);
|
||||
COMPILE_EMSG(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER);
|
||||
COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN);
|
||||
COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN);
|
||||
COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); /* "(?" with bad next char */
|
||||
COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE);
|
||||
COMPILE_EMSG(CharacterClassInvalidRange, JSMSG_BAD_CLASS_RANGE);
|
||||
COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE);
|
||||
COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER);
|
||||
COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH);
|
||||
#undef COMPILE_EMSG
|
||||
default:
|
||||
JS_NOT_REACHED("Unknown Yarr error code");
|
||||
}
|
||||
}
|
||||
#endif /* ENABLE_YARR_JIT */
|
||||
|
||||
bool
|
||||
js::ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut)
|
||||
|
|
|
@ -149,6 +149,69 @@ ResetRegExpObject(JSContext *cx, RegExpObject *reobj, JSString *str, RegExpFlag
|
|||
inline bool
|
||||
ResetRegExpObject(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
|
||||
|
||||
/* Abstracts away the gross |RegExpPrivate| backend details. */
|
||||
class RegExpPrivateCode
|
||||
{
|
||||
#if ENABLE_YARR_JIT
|
||||
typedef JSC::Yarr::BytecodePattern BytecodePattern;
|
||||
typedef JSC::Yarr::ErrorCode ErrorCode;
|
||||
typedef JSC::Yarr::JSGlobalData JSGlobalData;
|
||||
typedef JSC::Yarr::YarrCodeBlock YarrCodeBlock;
|
||||
typedef JSC::Yarr::YarrPattern YarrPattern;
|
||||
|
||||
/* Note: Native code is valid only if |codeBlock.isFallBack() == false|. */
|
||||
YarrCodeBlock codeBlock;
|
||||
BytecodePattern *byteCode;
|
||||
#else
|
||||
JSRegExp *compiled;
|
||||
#endif
|
||||
|
||||
public:
|
||||
RegExpPrivateCode()
|
||||
:
|
||||
#if ENABLE_YARR_JIT
|
||||
codeBlock(),
|
||||
byteCode(NULL)
|
||||
#else
|
||||
compiled(NULL)
|
||||
#endif
|
||||
{ }
|
||||
|
||||
~RegExpPrivateCode() {
|
||||
#if ENABLE_YARR_JIT
|
||||
codeBlock.release();
|
||||
if (byteCode)
|
||||
Foreground::delete_<BytecodePattern>(byteCode);
|
||||
#else
|
||||
if (compiled)
|
||||
jsRegExpFree(compiled);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if ENABLE_YARR_JIT
|
||||
static inline bool isJITRuntimeEnabled(JSContext *cx);
|
||||
void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error);
|
||||
#else
|
||||
void reportPCREError(JSContext *cx, int error);
|
||||
#endif
|
||||
|
||||
inline bool compile(JSContext *cx, JSLinearString &pattern, TokenStream *ts, uintN *parenCount,
|
||||
RegExpFlag flags);
|
||||
|
||||
enum ExecuteResult { Error, Success, Success_NotFound };
|
||||
|
||||
inline ExecuteResult execute(JSContext *cx, const jschar *chars, size_t start, size_t length,
|
||||
int *output, size_t outputCount);
|
||||
|
||||
static size_t getOutputSize(size_t pairCount) {
|
||||
#if ENABLE_YARR_JIT
|
||||
return pairCount * 2;
|
||||
#else
|
||||
return pairCount * 3; /* Should be x2, but PCRE has... needs. */
|
||||
#endif
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* The "meat" of the builtin regular expression objects: it contains the
|
||||
* mini-program that represents the source of the regular expression.
|
||||
|
@ -165,58 +228,23 @@ ResetRegExpObject(JSContext *cx, AlreadyIncRefed<RegExpPrivate> rep);
|
|||
*/
|
||||
class RegExpPrivate
|
||||
{
|
||||
#if ENABLE_YARR_JIT
|
||||
/* Note: Native code is valid only if |codeBlock.isFallBack() == false|. */
|
||||
JSC::Yarr::YarrCodeBlock codeBlock;
|
||||
JSC::Yarr::BytecodePattern *byteCode;
|
||||
#else
|
||||
JSRegExp *compiled;
|
||||
#endif
|
||||
|
||||
RegExpPrivateCode code;
|
||||
JSLinearString *source;
|
||||
size_t refCount;
|
||||
unsigned parenCount; /* Must be |unsigned| to interface with YARR. */
|
||||
uintN parenCount;
|
||||
RegExpFlag flags;
|
||||
#ifdef DEBUG
|
||||
|
||||
public:
|
||||
JSCompartment *compartment;
|
||||
DebugOnly<JSCompartment *> compartment;
|
||||
|
||||
private:
|
||||
#endif
|
||||
|
||||
RegExpPrivate(JSLinearString *source, RegExpFlag flags, JSCompartment *compartment)
|
||||
:
|
||||
#if ENABLE_YARR_JIT
|
||||
codeBlock(),
|
||||
byteCode(NULL),
|
||||
#else
|
||||
compiled(NULL),
|
||||
#endif
|
||||
source(source), refCount(1), parenCount(0), flags(flags)
|
||||
#ifdef DEBUG
|
||||
, compartment(compartment)
|
||||
#endif
|
||||
: source(source), refCount(1), parenCount(0), flags(flags), compartment(compartment)
|
||||
{ }
|
||||
|
||||
JS_DECLARE_ALLOCATION_FRIENDS_FOR_PRIVATE_CONSTRUCTOR;
|
||||
|
||||
~RegExpPrivate() {
|
||||
#if ENABLE_YARR_JIT
|
||||
codeBlock.release();
|
||||
if (byteCode)
|
||||
Foreground::delete_<JSC::Yarr::BytecodePattern>(byteCode);
|
||||
#else
|
||||
if (compiled)
|
||||
jsRegExpFree(compiled);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool compileHelper(JSContext *cx, JSLinearString &pattern, TokenStream *ts);
|
||||
bool compile(JSContext *cx, TokenStream *ts);
|
||||
#if !ENABLE_YARR_JIT
|
||||
void reportPCREError(JSContext *cx, int error);
|
||||
#endif
|
||||
void reportYarrError(JSContext *cx, TokenStream *ts, JSC::Yarr::ErrorCode error);
|
||||
static inline void checkMatchPairs(JSString *input, int *buf, size_t matchItemCount);
|
||||
static JSObject *createResult(JSContext *cx, JSString *input, int *buf, size_t matchItemCount);
|
||||
bool executeInternal(JSContext *cx, RegExpStatics *res, JSString *input,
|
||||
|
|
|
@ -43,8 +43,30 @@
|
|||
|
||||
#include "String.h"
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jsgcinlines.h"
|
||||
|
||||
JS_ALWAYS_INLINE bool
|
||||
JSString::validateLength(JSContext *cx, size_t length)
|
||||
{
|
||||
if (JS_UNLIKELY(length > JSString::MAX_LENGTH)) {
|
||||
if (JS_ON_TRACE(cx)) {
|
||||
/*
|
||||
* If we can't leave the trace, signal OOM condition, otherwise
|
||||
* exit from trace before throwing.
|
||||
*/
|
||||
if (!js::CanLeaveTrace(cx))
|
||||
return NULL;
|
||||
|
||||
js::LeaveTrace(cx);
|
||||
}
|
||||
js_ReportAllocationOverflow(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE void
|
||||
JSRope::init(JSString *left, JSString *right, size_t length)
|
||||
{
|
||||
|
@ -56,6 +78,8 @@ JSRope::init(JSString *left, JSString *right, size_t length)
|
|||
JS_ALWAYS_INLINE JSRope *
|
||||
JSRope::new_(JSContext *cx, JSString *left, JSString *right, size_t length)
|
||||
{
|
||||
if (!validateLength(cx, length))
|
||||
return NULL;
|
||||
JSRope *str = (JSRope *)js_NewGCString(cx);
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
@ -114,9 +138,10 @@ JSFixedString::init(const jschar *chars, size_t length)
|
|||
JS_ALWAYS_INLINE JSFixedString *
|
||||
JSFixedString::new_(JSContext *cx, const jschar *chars, size_t length)
|
||||
{
|
||||
JS_ASSERT(length <= MAX_LENGTH);
|
||||
JS_ASSERT(chars[length] == jschar(0));
|
||||
|
||||
if (!validateLength(cx, length))
|
||||
return NULL;
|
||||
JSFixedString *str = (JSFixedString *)js_NewGCString(cx);
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
@ -185,6 +210,8 @@ JSExternalString::new_(JSContext *cx, const jschar *chars, size_t length, intN t
|
|||
JS_ASSERT(uintN(type) < JSExternalString::TYPE_LIMIT);
|
||||
JS_ASSERT(chars[length] == 0);
|
||||
|
||||
if (!validateLength(cx, length))
|
||||
return NULL;
|
||||
JSExternalString *str = js_NewGCExternalString(cx);
|
||||
if (!str)
|
||||
return NULL;
|
||||
|
|
|
@ -268,6 +268,8 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
|
|||
return left;
|
||||
|
||||
size_t wholeLength = leftLen + rightLen;
|
||||
if (!JSString::validateLength(cx, wholeLength))
|
||||
return NULL;
|
||||
|
||||
if (JSShortString::lengthFits(wholeLength)) {
|
||||
JSShortString *str = js_NewGCShortString(cx);
|
||||
|
@ -287,16 +289,6 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
|
|||
return str;
|
||||
}
|
||||
|
||||
if (wholeLength > JSString::MAX_LENGTH) {
|
||||
if (JS_ON_TRACE(cx)) {
|
||||
if (!CanLeaveTrace(cx))
|
||||
return NULL;
|
||||
LeaveTrace(cx);
|
||||
}
|
||||
js_ReportAllocationOverflow(cx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return JSRope::new_(cx, left, right, wholeLength);
|
||||
}
|
||||
|
||||
|
|
|
@ -261,6 +261,13 @@ class JSString : public js::gc::Cell
|
|||
return (length << LENGTH_SHIFT) | flags;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to validate that a string of a given length is
|
||||
* representable by a JSString. An allocation overflow is reported if false
|
||||
* is returned.
|
||||
*/
|
||||
static inline bool validateLength(JSContext *cx, size_t length);
|
||||
|
||||
static void staticAsserts() {
|
||||
JS_STATIC_ASSERT(JS_BITS_PER_WORD >= 32);
|
||||
JS_STATIC_ASSERT(((JSString::MAX_LENGTH << JSString::LENGTH_SHIFT) >>
|
||||
|
|
|
@ -139,5 +139,5 @@ XPCStringConvert::ReadableToJSVal(JSContext *cx,
|
|||
if (!str)
|
||||
JS_free(cx, chars);
|
||||
}
|
||||
return STRING_TO_JSVAL(str);
|
||||
return str ? STRING_TO_JSVAL(str) : JSVAL_NULL;
|
||||
}
|
||||
|
|
|
@ -2979,8 +2979,8 @@ CallMethodHelper::ConvertDependentParams()
|
|||
|
||||
if((datum_type.IsPointer() &&
|
||||
(datum_type.TagPart() == nsXPTType::T_IID ||
|
||||
datum_type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS) ||
|
||||
datum_type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS) ||
|
||||
datum_type.TagPart() == nsXPTType::T_PSTRING_SIZE_IS ||
|
||||
datum_type.TagPart() == nsXPTType::T_PWSTRING_SIZE_IS)) ||
|
||||
(isArray && datum_type.TagPart() == nsXPTType::T_CHAR_STR))
|
||||
{
|
||||
dp->SetValNeedsCleanup();
|
||||
|
|
|
@ -860,12 +860,11 @@ public:
|
|||
resetMatches(term, context);
|
||||
freeParenthesesDisjunctionContext(context);
|
||||
|
||||
if (result == JSRegExpNoMatch) {
|
||||
if (result != JSRegExpNoMatch)
|
||||
return result;
|
||||
JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack);
|
||||
if (backtrackResult != JSRegExpMatch)
|
||||
return backtrackResult;
|
||||
} else
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -947,12 +946,11 @@ public:
|
|||
resetMatches(term, context);
|
||||
freeParenthesesDisjunctionContext(context);
|
||||
|
||||
if (result == JSRegExpNoMatch) {
|
||||
if (result != JSRegExpNoMatch)
|
||||
return result;
|
||||
JSRegExpResult backtrackResult = parenthesesDoBacktrack(term, backTrack);
|
||||
if (backtrackResult != JSRegExpMatch)
|
||||
return backtrackResult;
|
||||
} else
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1036,6 +1034,7 @@ public:
|
|||
popParenthesesDisjunctionContext(backTrack);
|
||||
freeParenthesesDisjunctionContext(context);
|
||||
|
||||
if (result != JSRegExpNoMatch)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -9931,14 +9931,12 @@ FirstLetterCount(const nsTextFragment* aFragment)
|
|||
{
|
||||
PRInt32 count = 0;
|
||||
PRInt32 firstLetterLength = 0;
|
||||
bool done = false;
|
||||
|
||||
PRInt32 i, n = aFragment->GetLength();
|
||||
for (i = 0; i < n; i++) {
|
||||
PRUnichar ch = aFragment->CharAt(i);
|
||||
if (XP_IS_SPACE(ch)) {
|
||||
if (firstLetterLength) {
|
||||
done = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
count++;
|
||||
|
@ -9947,7 +9945,6 @@ FirstLetterCount(const nsTextFragment* aFragment)
|
|||
// XXX I18n
|
||||
if ((ch == '\'') || (ch == '\"')) {
|
||||
if (firstLetterLength) {
|
||||
done = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
// keep looping
|
||||
|
@ -9955,7 +9952,6 @@ FirstLetterCount(const nsTextFragment* aFragment)
|
|||
}
|
||||
else {
|
||||
count++;
|
||||
done = PR_TRUE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4948,6 +4948,9 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
|
|||
}
|
||||
|
||||
// Draw text
|
||||
const nsStyleText* textStyle = GetStyleText();
|
||||
nsRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
|
||||
aDirtyRect.width, aDirtyRect.height);
|
||||
SelectionIterator iterator(prevailingSelections, aContentOffset, aContentLength,
|
||||
aProvider, mTextRun, startXOffset);
|
||||
while (iterator.GetNextSegment(&xOffset, &offset, &length, &hyphenWidth,
|
||||
|
@ -4955,11 +4958,23 @@ nsTextFrame::PaintTextWithSelectionColors(gfxContext* aCtx,
|
|||
nscolor foreground, background;
|
||||
GetSelectionTextColors(type, aTextPaintStyle, rangeStyle,
|
||||
&foreground, &background);
|
||||
gfxPoint textBaselinePt(aFramePt.x + xOffset, aTextBaselinePt.y);
|
||||
|
||||
// Draw shadows, if any
|
||||
if (textStyle->mTextShadow) {
|
||||
for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) {
|
||||
PaintOneShadow(offset, length,
|
||||
textStyle->mTextShadow->ShadowAt(i - 1), &aProvider,
|
||||
dirtyRect, aFramePt, textBaselinePt, aCtx,
|
||||
foreground, aClipEdges, xOffset);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw text segment
|
||||
aCtx->SetColor(gfxRGBA(foreground));
|
||||
gfxFloat advance;
|
||||
|
||||
DrawText(aCtx, aDirtyRect, aFramePt, gfxPoint(aFramePt.x + xOffset, aTextBaselinePt.y),
|
||||
DrawText(aCtx, aDirtyRect, aFramePt, textBaselinePt,
|
||||
offset, length, aProvider, aTextPaintStyle, aClipEdges, advance,
|
||||
hyphenWidth > 0);
|
||||
if (hyphenWidth) {
|
||||
|
@ -5280,20 +5295,6 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
|
|||
nsCharClipDisplayItem::ClipEdges clipEdges(aItem, snappedLeftEdge,
|
||||
snappedRightEdge);
|
||||
nsTextPaintStyle textPaintStyle(this);
|
||||
nscolor foregroundColor = textPaintStyle.GetTextColor();
|
||||
|
||||
// Paint the text shadow before doing any foreground stuff
|
||||
const nsStyleText* textStyle = GetStyleText();
|
||||
if (textStyle->mTextShadow) {
|
||||
// Text shadow happens with the last value being painted at the back,
|
||||
// ie. it is painted first.
|
||||
for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) {
|
||||
PaintOneShadow(startOffset, maxLength,
|
||||
textStyle->mTextShadow->ShadowAt(i - 1), &provider,
|
||||
aDirtyRect, framePt, textBaselinePt, ctx,
|
||||
foregroundColor, clipEdges, snappedLeftEdge);
|
||||
}
|
||||
}
|
||||
|
||||
gfxRect dirtyRect(aDirtyRect.x, aDirtyRect.y,
|
||||
aDirtyRect.width, aDirtyRect.height);
|
||||
|
@ -5309,6 +5310,19 @@ nsTextFrame::PaintText(nsRenderingContext* aRenderingContext, nsPoint aPt,
|
|||
return;
|
||||
}
|
||||
|
||||
nscolor foregroundColor = textPaintStyle.GetTextColor();
|
||||
const nsStyleText* textStyle = GetStyleText();
|
||||
if (textStyle->mTextShadow) {
|
||||
// Text shadow happens with the last value being painted at the back,
|
||||
// ie. it is painted first.
|
||||
for (PRUint32 i = textStyle->mTextShadow->Length(); i > 0; --i) {
|
||||
PaintOneShadow(startOffset, maxLength,
|
||||
textStyle->mTextShadow->ShadowAt(i - 1), &provider,
|
||||
aDirtyRect, framePt, textBaselinePt, ctx,
|
||||
foregroundColor, clipEdges, snappedLeftEdge);
|
||||
}
|
||||
}
|
||||
|
||||
ctx->SetColor(gfxRGBA(foregroundColor));
|
||||
|
||||
gfxFloat advanceWidth;
|
||||
|
|
|
@ -20,3 +20,6 @@ random-if(Android) == basic-negcoord.xul basic-negcoord-ref.xul
|
|||
== overflow-not-scrollable-1.html overflow-not-scrollable-1-ref.html
|
||||
== overflow-not-scrollable-1.html overflow-not-scrollable-1-ref2.html
|
||||
== overflow-not-scrollable-2.html overflow-not-scrollable-2-ref.html
|
||||
|
||||
!= text-shadow-selected-1.html text-shadow-selected-1-notref.html
|
||||
== text-shadow-selected-1.html text-shadow-selected-1-ref.html
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
function onload() {
|
||||
var range = document.createRange();
|
||||
range.selectNodeContents(document.getElementById("selectMe"));
|
||||
var sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
</script>
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: lightGray;
|
||||
}
|
||||
div {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
font: 36px monospace;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<!-- NOTREF case has selected text but the shadow is missing -->
|
||||
<body onload="onload()">
|
||||
<div id="selectMe">
|
||||
selected shadowed text
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
function onload() {
|
||||
var range = document.createRange();
|
||||
range.selectNodeContents(document.getElementById("selectMe"));
|
||||
var sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
</script>
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: lightGray;
|
||||
}
|
||||
div {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
font: 36px monospace;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<!-- REF case renders the selection first, using to avoid any show-through of glyph edges,
|
||||
then the shadowed text on top of that -->
|
||||
<body onload="onload()">
|
||||
<div id="selectMe">
|
||||
|
||||
</div>
|
||||
<div style="text-shadow:1px 1px 1px red;">
|
||||
selected shadowed text
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,32 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<script type="text/javascript">
|
||||
function onload() {
|
||||
var range = document.createRange();
|
||||
range.selectNodeContents(document.getElementById("selectMe"));
|
||||
var sel = window.getSelection();
|
||||
sel.removeAllRanges();
|
||||
sel.addRange(range);
|
||||
}
|
||||
</script>
|
||||
<style type="text/css">
|
||||
body {
|
||||
background: lightGray;
|
||||
}
|
||||
div {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
font: 36px monospace;
|
||||
color: white;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<!-- test for bug 692752 - paint text shadow on top of selection highlight -->
|
||||
<body onload="onload()">
|
||||
<div style="text-shadow:1px 1px 1px red;" id="selectMe">
|
||||
selected shadowed text
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -138,14 +138,24 @@ nsSVGUseFrame::AttributeChanged(PRInt32 aNameSpaceID,
|
|||
nsIAtom* aAttribute,
|
||||
PRInt32 aModType)
|
||||
{
|
||||
if (aNameSpaceID == kNameSpaceID_None &&
|
||||
(aAttribute == nsGkAtoms::x ||
|
||||
aAttribute == nsGkAtoms::y)) {
|
||||
if (aNameSpaceID == kNameSpaceID_None) {
|
||||
if (aAttribute == nsGkAtoms::x ||
|
||||
aAttribute == nsGkAtoms::y) {
|
||||
// make sure our cached transform matrix gets (lazily) updated
|
||||
mCanvasTM = nsnull;
|
||||
|
||||
nsSVGUtils::NotifyChildrenOfSVGChange(this, TRANSFORM_CHANGED);
|
||||
return NS_OK;
|
||||
} else if (aAttribute == nsGkAtoms::width ||
|
||||
aAttribute == nsGkAtoms::height) {
|
||||
static_cast<nsSVGUseElement*>(mContent)->SyncWidthHeight(aAttribute);
|
||||
}
|
||||
} else if (aNameSpaceID == kNameSpaceID_XLink &&
|
||||
aAttribute == nsGkAtoms::href) {
|
||||
// we're changing our nature, clear out the clone information
|
||||
nsSVGUseElement *use = static_cast<nsSVGUseElement*>(mContent);
|
||||
use->mOriginal = nsnull;
|
||||
use->UnlinkSource();
|
||||
use->TriggerReclone();
|
||||
}
|
||||
|
||||
return nsSVGUseFrameBase::AttributeChanged(aNameSpaceID,
|
||||
|
|
|
@ -157,7 +157,7 @@
|
|||
function openLocalePicker() {
|
||||
let win = getChromeWin();
|
||||
win.BrowserUI.showPanel("prefs-container");
|
||||
win.document.getElementById("prefs-languages").click();
|
||||
win.document.getElementById("prefs-uilanguage-button").click();
|
||||
}
|
||||
|
||||
function init() {
|
||||
|
@ -192,10 +192,15 @@
|
|||
return;
|
||||
}
|
||||
|
||||
let content = NetUtil.readInputStreamToString(aStream, aStream.available()) || "";
|
||||
let fileSize = aStream.available();
|
||||
let cvstream = Cc["@mozilla.org/intl/converter-input-stream;1"].createInstance(Ci.nsIConverterInputStream);
|
||||
cvstream.init(aStream, "UTF-8", fileSize, Ci.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
||||
let content = {};
|
||||
cvstream.readString(fileSize, content);
|
||||
cvstream.close();
|
||||
aStream.close();
|
||||
|
||||
aCallback(content.replace(/\r\n?/g, "\n"));
|
||||
aCallback(content.value.replace(/\r\n?/g, "\n"));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -899,6 +899,7 @@ var ContextHandler = {
|
|||
}
|
||||
|
||||
let elem = popupNode;
|
||||
let isText = false;
|
||||
while (elem) {
|
||||
if (elem.nodeType == Ci.nsIDOMNode.ELEMENT_NODE) {
|
||||
// Link?
|
||||
|
@ -947,14 +948,16 @@ var ContextHandler = {
|
|||
elem instanceof Ci.nsIDOMHTMLPreElement ||
|
||||
elem instanceof Ci.nsIDOMHTMLHeadingElement ||
|
||||
elem instanceof Ci.nsIDOMHTMLTableCellElement) {
|
||||
state.types.push("content-text");
|
||||
break;
|
||||
isText = true;
|
||||
}
|
||||
}
|
||||
|
||||
elem = elem.parentNode;
|
||||
}
|
||||
|
||||
if (isText)
|
||||
state.types.push("content-text");
|
||||
|
||||
for (let i = 0; i < this._types.length; i++)
|
||||
if (this._types[i].handler(state, popupNode))
|
||||
state.types.push(this._types[i].name);
|
||||
|
|
|
@ -250,11 +250,11 @@
|
|||
-moz-box-ordinal-group: 2; /* put the image on the right side */
|
||||
}
|
||||
|
||||
.option-command {
|
||||
.option-command:not([disabled="true"]):not(.optgroup) {
|
||||
list-style-image: url("chrome://browser/skin/images/radio-unselected-hdpi.png");
|
||||
}
|
||||
|
||||
.option-command.selected {
|
||||
.option-command.selected:not(.optgroup) {
|
||||
list-style-image: url("chrome://browser/skin/images/radio-selected-hdpi.png");
|
||||
}
|
||||
|
||||
|
|
|
@ -258,11 +258,11 @@
|
|||
border-bottom: none;
|
||||
}
|
||||
|
||||
.option-command {
|
||||
.option-command:not([disabled="true"]):not(.optgroup) {
|
||||
list-style-image: url("chrome://browser/skin/images/radio-unselected-hdpi.png");
|
||||
}
|
||||
|
||||
.option-command.selected {
|
||||
.option-command.selected:not(.optgroup) {
|
||||
list-style-image: url("chrome://browser/skin/images/radio-selected-hdpi.png");
|
||||
}
|
||||
|
||||
|
|
|
@ -1525,10 +1525,8 @@ RasterImage::Notify(nsITimer *timer)
|
|||
}
|
||||
|
||||
nsIntRect dirtyRect;
|
||||
imgFrame *frameToUse = nsnull;
|
||||
|
||||
if (nextFrameIndex == 0) {
|
||||
frameToUse = nextFrame;
|
||||
dirtyRect = mAnim->firstFrameRefreshArea;
|
||||
} else {
|
||||
imgFrame *prevFrame = mFrames[previousFrameIndex];
|
||||
|
@ -1536,7 +1534,7 @@ RasterImage::Notify(nsITimer *timer)
|
|||
return NS_OK;
|
||||
|
||||
// Change frame and announce it
|
||||
if (NS_FAILED(DoComposite(&frameToUse, &dirtyRect, prevFrame,
|
||||
if (NS_FAILED(DoComposite(&dirtyRect, prevFrame,
|
||||
nextFrame, nextFrameIndex))) {
|
||||
// something went wrong, move on to next
|
||||
NS_WARNING("RasterImage::Notify(): Composing Frame Failed\n");
|
||||
|
@ -1559,8 +1557,7 @@ RasterImage::Notify(nsITimer *timer)
|
|||
// DoComposite gets called when the timer for animation get fired and we have to
|
||||
// update the composited frame of the animation.
|
||||
nsresult
|
||||
RasterImage::DoComposite(imgFrame** aFrameToUse,
|
||||
nsIntRect* aDirtyRect,
|
||||
RasterImage::DoComposite(nsIntRect* aDirtyRect,
|
||||
imgFrame* aPrevFrame,
|
||||
imgFrame* aNextFrame,
|
||||
PRInt32 aNextFrameIndex)
|
||||
|
@ -1568,7 +1565,6 @@ RasterImage::DoComposite(imgFrame** aFrameToUse,
|
|||
NS_ENSURE_ARG_POINTER(aDirtyRect);
|
||||
NS_ENSURE_ARG_POINTER(aPrevFrame);
|
||||
NS_ENSURE_ARG_POINTER(aNextFrame);
|
||||
NS_ENSURE_ARG_POINTER(aFrameToUse);
|
||||
|
||||
PRInt32 prevFrameDisposalMethod = aPrevFrame->GetFrameDisposalMethod();
|
||||
if (prevFrameDisposalMethod == kDisposeRestorePrevious &&
|
||||
|
@ -1597,7 +1593,6 @@ RasterImage::DoComposite(imgFrame** aFrameToUse,
|
|||
// whole image
|
||||
if (prevFrameDisposalMethod == kDisposeClearAll) {
|
||||
aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
|
||||
*aFrameToUse = aNextFrame;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1607,7 +1602,6 @@ RasterImage::DoComposite(imgFrame** aFrameToUse,
|
|||
(nextFrameDisposalMethod != kDisposeRestorePrevious) &&
|
||||
!aNextFrame->GetHasAlpha()) {
|
||||
aDirtyRect->SetRect(0, 0, mSize.width, mSize.height);
|
||||
*aFrameToUse = aNextFrame;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
@ -1648,7 +1642,6 @@ RasterImage::DoComposite(imgFrame** aFrameToUse,
|
|||
// On the second loop, we do not need to rebuild the frame
|
||||
// since it's still sitting in compositingFrame)
|
||||
if (mAnim->lastCompositedFrameIndex == aNextFrameIndex) {
|
||||
*aFrameToUse = mAnim->compositingFrame;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -1817,13 +1810,11 @@ RasterImage::DoComposite(imgFrame** aFrameToUse,
|
|||
if (CopyFrameImage(mAnim->compositingFrame, aNextFrame)) {
|
||||
aPrevFrame->SetFrameDisposalMethod(kDisposeClearAll);
|
||||
mAnim->lastCompositedFrameIndex = -1;
|
||||
*aFrameToUse = aNextFrame;
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
mAnim->lastCompositedFrameIndex = aNextFrameIndex;
|
||||
*aFrameToUse = mAnim->compositingFrame;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -408,14 +408,12 @@ private:
|
|||
|
||||
/** Function for doing the frame compositing of animations
|
||||
*
|
||||
* @param aFrameToUse Set by DoComposite
|
||||
* (aNextFrame, compositingFrame, or compositingPrevFrame)
|
||||
* @param aDirtyRect Area that the display will need to update
|
||||
* @param aPrevFrame Last Frame seen/processed
|
||||
* @param aNextFrame Frame we need to incorperate/display
|
||||
* @param aNextFrameIndex Position of aNextFrame in mFrames list
|
||||
*/
|
||||
nsresult DoComposite(imgFrame** aFrameToUse, nsIntRect* aDirtyRect,
|
||||
nsresult DoComposite(nsIntRect* aDirtyRect,
|
||||
imgFrame* aPrevFrame,
|
||||
imgFrame* aNextFrame,
|
||||
PRInt32 aNextFrameIndex);
|
||||
|
|
|
@ -2268,6 +2268,8 @@ function run_single_closure_tests(library, abi, suffix)
|
|||
|
||||
function closure_fn(i)
|
||||
{
|
||||
if (i == 42)
|
||||
throw "7.5 million years for that?";
|
||||
return "a" in this ? i + this.a : i + b;
|
||||
}
|
||||
|
||||
|
@ -2289,6 +2291,38 @@ function run_single_closure_tests(library, abi, suffix)
|
|||
let closure2 = fn_t(closure_fn, thisobj);
|
||||
do_check_eq(closure2(-17), -17 + thisobj.a);
|
||||
do_check_eq(test_closure(-52, closure2), -52 + thisobj.a);
|
||||
|
||||
// Specify an error sentinel, and have the JS code throw (see bug 599791).
|
||||
let closure3 = fn_t(closure_fn, null, 54);
|
||||
do_check_eq(closure3(42), 54);
|
||||
do_check_eq(test_closure(42, closure3), 54);
|
||||
|
||||
// Check what happens when the return type is bigger than a word.
|
||||
var fn_64_t = ctypes.FunctionType(ctypes.default_abi, ctypes.uint64_t, [ctypes.bool]).ptr;
|
||||
var bignum1 = ctypes.UInt64.join(0xDEADBEEF, 0xBADF00D);
|
||||
var bignum2 = ctypes.UInt64.join(0xDEFEC8ED, 0xD15EA5E);
|
||||
function closure_fn_64(fail)
|
||||
{
|
||||
if (fail)
|
||||
throw "Just following orders, sir!";
|
||||
return bignum1;
|
||||
};
|
||||
var closure64 = fn_64_t(closure_fn_64, null, bignum2);
|
||||
do_check_eq(ctypes.UInt64.compare(closure64(false), bignum1), 0);
|
||||
do_check_eq(ctypes.UInt64.compare(closure64(true), bignum2), 0);
|
||||
|
||||
// Test a callback that returns void (see bug 682504).
|
||||
var fn_v_t = ctypes.FunctionType(ctypes.default_abi, ctypes.void_t, []).ptr;
|
||||
fn_v_t(function() {})(); // Don't crash
|
||||
|
||||
// Make sure that a void callback can't return an error sentinel.
|
||||
var sentinelThrew = false;
|
||||
try {
|
||||
fn_v_t(function() {}, null, -1);
|
||||
} catch(e) {
|
||||
sentinelThrew = true;
|
||||
}
|
||||
do_check_true(sentinelThrew);
|
||||
}
|
||||
|
||||
function run_variadic_tests(library) {
|
||||
|
|
|
@ -43,10 +43,20 @@
|
|||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
|
||||
// Fired by TelemetryPing when async telemetry data should be collected.
|
||||
const TOPIC_GATHER_TELEMETRY = "gather-telemetry";
|
||||
|
||||
// Seconds between maintenance runs.
|
||||
const MAINTENANCE_INTERVAL_SECONDS = 7 * 86400;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Imports
|
||||
|
||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Components.utils.import("resource://gre/modules/PlacesUtils.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "PlacesDBUtils",
|
||||
"resource://gre/modules/PlacesDBUtils.jsm");
|
||||
|
||||
/**
|
||||
* This component can be used as a starter for modules that have to run when
|
||||
|
@ -54,6 +64,8 @@ Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
|||
*/
|
||||
function PlacesCategoriesStarter()
|
||||
{
|
||||
Services.obs.addObserver(this, TOPIC_GATHER_TELEMETRY, false);
|
||||
Services.obs.addObserver(this, PlacesUtils.TOPIC_SHUTDOWN, false);
|
||||
}
|
||||
|
||||
PlacesCategoriesStarter.prototype = {
|
||||
|
@ -63,15 +75,22 @@ PlacesCategoriesStarter.prototype = {
|
|||
observe: function PCS_observe(aSubject, aTopic, aData)
|
||||
{
|
||||
switch (aTopic) {
|
||||
case PlacesUtils.TOPIC_SHUTDOWN:
|
||||
Services.obs.removeObserver(this, PlacesUtils.TOPIC_SHUTDOWN);
|
||||
Services.obs.removeObserver(this, TOPIC_GATHER_TELEMETRY);
|
||||
break;
|
||||
case TOPIC_GATHER_TELEMETRY:
|
||||
PlacesDBUtils.telemetry();
|
||||
break;
|
||||
case "idle-daily":
|
||||
// Once a week run places.sqlite maintenance tasks.
|
||||
let lastMaintenance = 0;
|
||||
try {
|
||||
lastMaintenance = Services.prefs.getIntPref("places.database.lastMaintenance");
|
||||
lastMaintenance =
|
||||
Services.prefs.getIntPref("places.database.lastMaintenance");
|
||||
} catch (ex) {}
|
||||
let nowSeconds = parseInt(Date.now() / 1000);
|
||||
if (lastMaintenance < nowSeconds - MAINTENANCE_INTERVAL_SECONDS) {
|
||||
Components.utils.import("resource://gre/modules/PlacesDBUtils.jsm");
|
||||
PlacesDBUtils.maintenanceOnIdle();
|
||||
}
|
||||
break;
|
||||
|
|
|
@ -54,6 +54,8 @@ let EXPORTED_SYMBOLS = [ "PlacesDBUtils" ];
|
|||
|
||||
const FINISHED_MAINTENANCE_TOPIC = "places-maintenance-finished";
|
||||
|
||||
const BYTES_PER_MEBIBYTE = 1048576;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
//// Smart getters
|
||||
|
||||
|
@ -115,7 +117,6 @@ let PlacesDBUtils = {
|
|||
this.checkIntegrity
|
||||
, this.checkCoherence
|
||||
, this._refreshUI
|
||||
, this._telemetry
|
||||
]);
|
||||
tasks.callback = aCallback;
|
||||
tasks.scope = aScope;
|
||||
|
@ -861,7 +862,7 @@ let PlacesDBUtils = {
|
|||
* @param [optional] aTasks
|
||||
* Tasks object to execute.
|
||||
*/
|
||||
_telemetry: function PDBU__telemetry(aTasks)
|
||||
telemetry: function PDBU_telemetry(aTasks)
|
||||
{
|
||||
let tasks = new Tasks(aTasks);
|
||||
|
||||
|
@ -911,7 +912,7 @@ let PlacesDBUtils = {
|
|||
let DBFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
DBFile.append("places.sqlite");
|
||||
try {
|
||||
return parseInt(DBFile.fileSize / 1024);
|
||||
return parseInt(DBFile.fileSize / BYTES_PER_MEBIBYTE);
|
||||
} catch (ex) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -921,7 +922,7 @@ let PlacesDBUtils = {
|
|||
let DBFile = Services.dirsvc.get("ProfD", Ci.nsILocalFile);
|
||||
DBFile.append("places.sqlite-wal");
|
||||
try {
|
||||
return parseInt(DBFile.fileSize / 1024);
|
||||
return parseInt(DBFile.fileSize / BYTES_PER_MEBIBYTE);
|
||||
} catch (ex) {
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -163,7 +163,7 @@ using namespace mozilla::places;
|
|||
|
||||
// This is the schema version, update it at any schema change and add a
|
||||
// corresponding migrateVxx method below.
|
||||
#define DATABASE_SCHEMA_VERSION 11
|
||||
#define DATABASE_SCHEMA_VERSION 12
|
||||
|
||||
// Filename of the database.
|
||||
#define DATABASE_FILENAME NS_LITERAL_STRING("places.sqlite")
|
||||
|
@ -313,6 +313,49 @@ namespace mozilla {
|
|||
_sqlFragment.AppendLiteral(" AS tags ");
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates sqlite_stat1 table through ANALYZE.
|
||||
* Since also nsPlacesExpiration.js executes ANALYZE, the analyzed tables
|
||||
* must be the same in both components. So ensure they are in sync.
|
||||
*/
|
||||
nsresult updateSQLiteStatistics(mozIStorageConnection* aDBConn)
|
||||
{
|
||||
nsCOMPtr<mozIStorageAsyncStatement> analyzePlacesStmt;
|
||||
nsresult rv = aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"ANALYZE moz_places"
|
||||
), getter_AddRefs(analyzePlacesStmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<mozIStorageAsyncStatement> analyzeBookmarksStmt;
|
||||
rv = aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"ANALYZE moz_bookmarks"
|
||||
), getter_AddRefs(analyzeBookmarksStmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<mozIStorageAsyncStatement> analyzeVisitsStmt;
|
||||
rv = aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"ANALYZE moz_historyvisits"
|
||||
), getter_AddRefs(analyzeVisitsStmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
nsCOMPtr<mozIStorageAsyncStatement> analyzeInputStmt;
|
||||
rv = aDBConn->CreateAsyncStatement(NS_LITERAL_CSTRING(
|
||||
"ANALYZE moz_inputhistory"
|
||||
), getter_AddRefs(analyzeInputStmt));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mozIStorageBaseStatement *stmts[] = {
|
||||
analyzePlacesStmt,
|
||||
analyzeBookmarksStmt,
|
||||
analyzeVisitsStmt,
|
||||
analyzeInputStmt
|
||||
};
|
||||
|
||||
nsCOMPtr<mozIStoragePendingStatement> ps;
|
||||
rv = aDBConn->ExecuteAsync(stmts, NS_ARRAY_LENGTH(stmts), nsnull,
|
||||
getter_AddRefs(ps));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace places
|
||||
} // namespace mozilla
|
||||
|
||||
|
@ -928,6 +971,9 @@ nsNavHistory::InitDB()
|
|||
rv = UpdateSchemaVersion();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = updateSQLiteStatistics(mDBConn);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = transaction.Commit();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
|
|
|
@ -254,6 +254,22 @@ function nsPlacesAutoComplete()
|
|||
// Create our in-memory tables for tab tracking.
|
||||
initTempTable(db);
|
||||
|
||||
// Populate the table with current open pages cache contents.
|
||||
if (this._openPagesCache.length > 0) {
|
||||
// Avoid getter re-entrance from the _registerOpenPageQuery lazy getter.
|
||||
let stmt = this._registerOpenPageQuery =
|
||||
db.createAsyncStatement(this._registerOpenPageQuerySQL);
|
||||
let params = stmt.newBindingParamsArray();
|
||||
for (let i = 0; i < this._openPagesCache.length; i++) {
|
||||
let bp = params.newBindingParams();
|
||||
bp.bindByName("page_url", this._openPagesCache[i]);
|
||||
params.addParams(bp);
|
||||
}
|
||||
stmt.bindParameters(params);
|
||||
stmt.executeAsync();
|
||||
delete this._openPagesCache;
|
||||
}
|
||||
|
||||
return db;
|
||||
});
|
||||
|
||||
|
@ -379,9 +395,8 @@ function nsPlacesAutoComplete()
|
|||
);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "_registerOpenPageQuery", function() {
|
||||
return this._db.createAsyncStatement(
|
||||
"INSERT OR REPLACE INTO moz_openpages_temp (url, open_count) "
|
||||
this._registerOpenPageQuerySQL = "INSERT OR REPLACE INTO moz_openpages_temp "
|
||||
+ "(url, open_count) "
|
||||
+ "VALUES (:page_url, "
|
||||
+ "IFNULL("
|
||||
+ "("
|
||||
|
@ -391,8 +406,9 @@ function nsPlacesAutoComplete()
|
|||
+ "), "
|
||||
+ "1"
|
||||
+ ")"
|
||||
+ ")"
|
||||
);
|
||||
+ ")";
|
||||
XPCOMUtils.defineLazyGetter(this, "_registerOpenPageQuery", function() {
|
||||
return this._db.createAsyncStatement(this._registerOpenPageQuerySQL);
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "_unregisterOpenPageQuery", function() {
|
||||
|
@ -509,19 +525,33 @@ nsPlacesAutoComplete.prototype = {
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// mozIPlacesAutoComplete
|
||||
|
||||
// If the connection has not yet been started, use this local cache. This
|
||||
// prevents autocomplete from initing the database till the first search.
|
||||
_openPagesCache: [],
|
||||
registerOpenPage: function PAC_registerOpenPage(aURI)
|
||||
{
|
||||
if (!this._databaseInitialized) {
|
||||
this._openPagesCache.push(aURI.spec);
|
||||
return;
|
||||
}
|
||||
|
||||
let stmt = this._registerOpenPageQuery;
|
||||
stmt.params.page_url = aURI.spec;
|
||||
|
||||
stmt.executeAsync();
|
||||
},
|
||||
|
||||
unregisterOpenPage: function PAC_unregisterOpenPage(aURI)
|
||||
{
|
||||
if (!this._databaseInitialized) {
|
||||
let index = this._openPagesCache.indexOf(aURI.spec);
|
||||
if (index != -1) {
|
||||
this._openPagesCache.splice(index, 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let stmt = this._unregisterOpenPageQuery;
|
||||
stmt.params.page_url = aURI.spec;
|
||||
|
||||
stmt.executeAsync();
|
||||
},
|
||||
|
||||
|
@ -613,7 +643,7 @@ nsPlacesAutoComplete.prototype = {
|
|||
}
|
||||
}
|
||||
|
||||
if (Object.getOwnPropertyDescriptor(this, "_db").value !== undefined) {
|
||||
if (this._databaseInitialized) {
|
||||
this._db.asyncClose();
|
||||
}
|
||||
}
|
||||
|
@ -625,6 +655,9 @@ nsPlacesAutoComplete.prototype = {
|
|||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// nsPlacesAutoComplete
|
||||
|
||||
get _databaseInitialized()
|
||||
Object.getOwnPropertyDescriptor(this, "_db").value !== undefined,
|
||||
|
||||
/**
|
||||
* Used to unescape encoded URI strings, and drop information that we do not
|
||||
* care about for searching.
|
||||
|
|
|
@ -404,6 +404,8 @@ const EXPIRATION_QUERIES = {
|
|||
// The following queries are used to adjust the sqlite_stat1 table to help the
|
||||
// query planner create better queries. These should always be run LAST, and
|
||||
// are therefore at the end of the object.
|
||||
// Since also nsNavHistory.cpp executes ANALYZE, the analyzed tables
|
||||
// must be the same in both components. So ensure they are in sync.
|
||||
|
||||
QUERY_ANALYZE_MOZ_PLACES: {
|
||||
sql: "ANALYZE moz_places",
|
||||
|
|
|
@ -35,6 +35,8 @@
|
|||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
const CURRENT_SCHEMA_VERSION = 12;
|
||||
|
||||
const NS_APP_USER_PROFILE_50_DIR = "ProfD";
|
||||
const NS_APP_PROFILE_DIR_STARTUP = "ProfDS";
|
||||
const NS_APP_BOOKMARKS_50_FILE = "BMarks";
|
||||
|
@ -145,7 +147,7 @@ function readInputStreamData(aStream) {
|
|||
bistream.setInputStream(aStream);
|
||||
let expectedData = [];
|
||||
let avail;
|
||||
while (avail = bistream.available()) {
|
||||
while ((avail = bistream.available())) {
|
||||
expectedData = expectedData.concat(bistream.readByteArray(avail));
|
||||
}
|
||||
return expectedData;
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* This file tests migration invariants from schema version 10 to schema version
|
||||
* 11.
|
||||
* This file tests migration invariants from schema version 10 to the current
|
||||
* schema version.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -296,7 +296,7 @@ function test_final_state()
|
|||
do_check_true(db.indexExists("moz_bookmarks_guid_uniqueindex"));
|
||||
do_check_true(db.indexExists("moz_places_guid_uniqueindex"));
|
||||
|
||||
do_check_eq(db.schemaVersion, 11);
|
||||
do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION);
|
||||
|
||||
db.close();
|
||||
run_next_test();
|
|
@ -4,7 +4,7 @@
|
|||
/**
|
||||
* This file tests migration invariants from a database with schema version 11
|
||||
* that was then downgraded to a database with a schema version 10. Places
|
||||
* should then migrate this database to one with a schema version of 11.
|
||||
* should then migrate this database to one with the current schema version.
|
||||
*/
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -109,7 +109,7 @@ function test_final_state()
|
|||
dbFile.append(kDBName);
|
||||
let db = Services.storage.openUnsharedDatabase(dbFile);
|
||||
|
||||
do_check_eq(db.schemaVersion, 11);
|
||||
do_check_eq(db.schemaVersion, CURRENT_SCHEMA_VERSION);
|
||||
|
||||
db.close();
|
||||
run_next_test();
|
|
@ -2,5 +2,5 @@
|
|||
head = head_migration.js
|
||||
tail =
|
||||
|
||||
[test_v11_from_v10.js]
|
||||
[test_v11_from_v10_migrated_from_v11.js]
|
||||
[test_current_from_v10.js]
|
||||
[test_current_from_v10_migrated_from_v11.js]
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests sqlite_sta1 table exists, it should be created by analyze, but since
|
||||
// the tables are empty it won't contain any data.
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
|
||||
let stmt = DBConn().createAsyncStatement(
|
||||
"SELECT ROWID FROM sqlite_stat1"
|
||||
);
|
||||
stmt.executeAsync({
|
||||
_gotResult: false,
|
||||
handleResult: function(aResultSet) {
|
||||
this._gotResult = true;
|
||||
},
|
||||
handleError: function(aError) {
|
||||
do_throw("Unexpected error (" + aError.result + "): " + aError.message);
|
||||
},
|
||||
handleCompletion: function(aReason) {
|
||||
do_check_false(this._gotResult);
|
||||
do_test_finished();
|
||||
}
|
||||
});
|
||||
stmt.finalize();
|
||||
}
|
|
@ -64,8 +64,10 @@ function run_test() {
|
|||
PlacesUtils.tagging.tagURI(uri, ["tag"]);
|
||||
PlacesUtils.bookmarks.setKeywordForBookmark(itemId, "keyword");
|
||||
|
||||
// Test generic database probes.
|
||||
PlacesDBUtils._telemetry();
|
||||
// Request to gather telemetry data.
|
||||
Cc["@mozilla.org/places/categoriesStarter;1"]
|
||||
.getService(Ci.nsIObserver)
|
||||
.observe(null, "gather-telemetry", null);
|
||||
|
||||
waitForAsyncUpdates(continue_test);
|
||||
}
|
||||
|
|
|
@ -48,6 +48,7 @@ skip-if = os == "android"
|
|||
# Bug 676989: test hangs consistently on Android
|
||||
skip-if = os == "android"
|
||||
[test_adaptive_bug527311.js]
|
||||
[test_analyze.js]
|
||||
[test_annotations.js]
|
||||
[test_asyncExecuteLegacyQueries.js]
|
||||
# Bug 676989: test hangs consistently on Android
|
||||
|
|
|
@ -184,6 +184,11 @@ var satchelFormListener = {
|
|||
if (!name)
|
||||
continue;
|
||||
|
||||
if (name == 'searchbar-history') {
|
||||
this.log('addEntry for input name "' + name + '" is denied')
|
||||
continue;
|
||||
}
|
||||
|
||||
// Limit stored data to 200 characters.
|
||||
if (name.length > 200 || value.length > 200) {
|
||||
this.log("skipping input that has a name/value too large");
|
||||
|
|
|
@ -171,6 +171,12 @@ FormAutoComplete.prototype = {
|
|||
if (!this._enabled)
|
||||
return null;
|
||||
|
||||
// don't allow form inputs (aField != null) to get results from search bar history
|
||||
if (aInputName == 'searchbar-history' && aField) {
|
||||
this.log('autoCompleteSearch for input name "' + aInputName + '" is denied');
|
||||
return null;
|
||||
}
|
||||
|
||||
this.log("AutoCompleteSearch invoked. Search is: " + aUntrimmedSearchString);
|
||||
let searchString = aUntrimmedSearchString.trim().toLowerCase();
|
||||
let result = null;
|
||||
|
|
|
@ -62,6 +62,10 @@ const MEM_HISTOGRAMS = {
|
|||
"heap-allocated": "MEMORY_HEAP_ALLOCATED",
|
||||
"page-faults-hard": "PAGE_FAULTS_HARD"
|
||||
};
|
||||
// Seconds of idle time before pinging.
|
||||
// On idle-daily a gather-telemetry notification is fired, during it probes can
|
||||
// start asynchronous tasks to gather data. On the next idle the data is sent.
|
||||
const IDLE_TIMEOUT_SECONDS = 5 * 60;
|
||||
|
||||
var gLastMemoryPoll = null;
|
||||
|
||||
|
@ -341,6 +345,10 @@ TelemetryPing.prototype = {
|
|||
return;
|
||||
Services.obs.removeObserver(this, "idle-daily");
|
||||
Services.obs.removeObserver(this, "cycle-collector-begin");
|
||||
if (this._isIdleObserver) {
|
||||
idle.removeIdleObserver(this, IDLE_TIMEOUT_SECONDS);
|
||||
this._isIdleObserver = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -416,11 +424,26 @@ TelemetryPing.prototype = {
|
|||
this.attachObservers()
|
||||
}
|
||||
break;
|
||||
case "idle-daily":
|
||||
// Enqueue to main-thread, otherwise components may be inited by the
|
||||
// idle-daily category and miss the gather-telemetry notification.
|
||||
Services.tm.mainThread.dispatch((function() {
|
||||
// Notify that data should be gathered now, since ping will happen soon.
|
||||
Services.obs.notifyObservers(null, "gather-telemetry", null);
|
||||
// The ping happens at the first idle of length IDLE_TIMEOUT_SECONDS.
|
||||
idle.addIdleObserver(this, IDLE_TIMEOUT_SECONDS);
|
||||
this._isIdleObserver = true;
|
||||
}).bind(this), Ci.nsIThread.DISPATCH_NORMAL);
|
||||
break;
|
||||
case "test-ping":
|
||||
server = aData;
|
||||
// fall through
|
||||
case "idle-daily":
|
||||
this.send(aTopic, server);
|
||||
case "idle":
|
||||
if (this._isIdleObserver) {
|
||||
idle.removeIdleObserver(this, IDLE_TIMEOUT_SECONDS);
|
||||
this._isIdleObserver = false;
|
||||
}
|
||||
this.send(aTopic == "idle" ? "idle-daily" : aTopic, server);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Check that TelemetryPing notifies correctly on idle.
|
||||
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
function run_test() {
|
||||
do_test_pending();
|
||||
|
||||
Services.obs.addObserver(function observeTelemetry() {
|
||||
Services.obs.removeObserver(observeTelemetry, "gather-telemetry");
|
||||
do_test_finished();
|
||||
}, "gather-telemetry", false);
|
||||
|
||||
Components.classes["@mozilla.org/base/telemetry-ping;1"]
|
||||
.getService(Components.interfaces.nsIObserver)
|
||||
.observe(null, "idle-daily", null);
|
||||
}
|
|
@ -6,3 +6,4 @@ tail =
|
|||
[test_TelemetryPing.js]
|
||||
# Bug 676989: test fails consistently on Android
|
||||
# fail-if = os == "android"
|
||||
[test_TelemetryPing_idle.js]
|
||||
|
|
|
@ -2651,17 +2651,22 @@ NS_IMETHODIMP nsWindow::HideWindowChrome(bool aShouldHide)
|
|||
|
||||
/**************************************************************
|
||||
*
|
||||
* SECTION: nsIWidget::Invalidate
|
||||
* SECTION: nsWindow::Invalidate
|
||||
*
|
||||
* Invalidate an area of the client for painting.
|
||||
*
|
||||
**************************************************************/
|
||||
|
||||
// Invalidate this component visible area
|
||||
NS_METHOD nsWindow::Invalidate(bool aIsSynchronous)
|
||||
{
|
||||
if (mWnd)
|
||||
NS_METHOD nsWindow::Invalidate(bool aIsSynchronous,
|
||||
bool aEraseBackground,
|
||||
bool aUpdateNCArea,
|
||||
bool aIncludeChildren)
|
||||
{
|
||||
if (!mWnd) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef WIDGET_DEBUG_OUTPUT
|
||||
debug_DumpInvalidate(stdout,
|
||||
this,
|
||||
|
@ -2671,12 +2676,21 @@ NS_METHOD nsWindow::Invalidate(bool aIsSynchronous)
|
|||
(PRInt32) mWnd);
|
||||
#endif // WIDGET_DEBUG_OUTPUT
|
||||
|
||||
VERIFY(::InvalidateRect(mWnd, NULL, FALSE));
|
||||
|
||||
DWORD flags = RDW_INVALIDATE;
|
||||
if (aEraseBackground) {
|
||||
flags |= RDW_ERASE;
|
||||
}
|
||||
if (aIsSynchronous) {
|
||||
VERIFY(::UpdateWindow(mWnd));
|
||||
flags |= RDW_UPDATENOW;
|
||||
}
|
||||
if (aUpdateNCArea) {
|
||||
flags |= RDW_FRAME;
|
||||
}
|
||||
if (aIncludeChildren) {
|
||||
flags |= RDW_ALLCHILDREN;
|
||||
}
|
||||
|
||||
VERIFY(::RedrawWindow(mWnd, NULL, NULL, flags));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
@ -4687,7 +4701,7 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam,
|
|||
|
||||
// Invalidate the window so that the repaint will
|
||||
// pick up the new theme.
|
||||
Invalidate(false);
|
||||
Invalidate(true, true, true, true);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -5366,7 +5380,7 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam,
|
|||
BroadcastMsg(mWnd, WM_DWMCOMPOSITIONCHANGED);
|
||||
DispatchStandardEvent(NS_THEMECHANGED);
|
||||
UpdateGlass();
|
||||
Invalidate(false);
|
||||
Invalidate(true, true, true, true);
|
||||
break;
|
||||
#endif
|
||||
|
||||
|
|
|
@ -145,7 +145,10 @@ public:
|
|||
virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
|
||||
NS_IMETHOD MakeFullScreen(bool aFullScreen);
|
||||
NS_IMETHOD HideWindowChrome(bool aShouldHide);
|
||||
NS_IMETHOD Invalidate(bool aIsSynchronous);
|
||||
NS_IMETHOD Invalidate(bool aIsSynchronous,
|
||||
bool aEraseBackground = false,
|
||||
bool aUpdateNCArea = false,
|
||||
bool aIncludeChildren = false);
|
||||
NS_IMETHOD Invalidate(const nsIntRect & aRect, bool aIsSynchronous);
|
||||
NS_IMETHOD Update();
|
||||
virtual void* GetNativeData(PRUint32 aDataType);
|
||||
|
|
|
@ -1289,12 +1289,14 @@ private:
|
|||
template<class E, PRUint32 N, class Alloc=nsTArrayDefaultAllocator>
|
||||
class nsAutoTArray : public nsAutoArrayBase<nsTArray<E, Alloc>, N>
|
||||
{
|
||||
typedef nsAutoArrayBase<nsTArray<E, Alloc>, N> Base;
|
||||
|
||||
public:
|
||||
nsAutoTArray() {}
|
||||
|
||||
template<typename Allocator>
|
||||
nsAutoTArray(const nsTArray<E, Allocator>& other) {
|
||||
AppendElements(other);
|
||||
Base::AppendElements(other);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1317,12 +1319,14 @@ PR_STATIC_ASSERT(sizeof(nsAutoTArray<PRUint32, 2>) ==
|
|||
template<class E, PRUint32 N>
|
||||
class AutoFallibleTArray : public nsAutoArrayBase<FallibleTArray<E>, N>
|
||||
{
|
||||
typedef nsAutoArrayBase<FallibleTArray<E>, N> Base;
|
||||
|
||||
public:
|
||||
AutoFallibleTArray() {}
|
||||
|
||||
template<typename Allocator>
|
||||
AutoFallibleTArray(const nsTArray<E, Allocator>& other) {
|
||||
AppendElements(other);
|
||||
Base::AppendElements(other);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1330,12 +1334,14 @@ public:
|
|||
template<class E, PRUint32 N>
|
||||
class AutoInfallibleTArray : public nsAutoArrayBase<InfallibleTArray<E>, N>
|
||||
{
|
||||
typedef nsAutoArrayBase<InfallibleTArray<E>, N> Base;
|
||||
|
||||
public:
|
||||
AutoInfallibleTArray() {}
|
||||
|
||||
template<typename Allocator>
|
||||
AutoInfallibleTArray(const nsTArray<E, Allocator>& other) {
|
||||
AppendElements(other);
|
||||
Base::AppendElements(other);
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче