Merge latest green birch changeset and mozilla-central

This commit is contained in:
Ed Morley 2013-07-16 09:59:15 +01:00
commit a178dd5cec
183 changed files with 7939 additions and 2327 deletions

View File

@ -415,6 +415,31 @@ nsAccessiblePivot::MovePivotInternal(Accessible* aPosition,
return NotifyOfPivotChange(oldPosition, oldStart, oldEnd, aReason);
}
Accessible*
nsAccessiblePivot::AdjustStartPosition(Accessible* aAccessible,
RuleCache& aCache,
uint16_t* aFilterResult,
nsresult* aResult)
{
Accessible* matched = aAccessible;
*aResult = aCache.ApplyFilter(aAccessible, aFilterResult);
if (aAccessible != mRoot && aAccessible != mModalRoot) {
for (Accessible* temp = aAccessible->Parent();
temp && temp != mRoot && temp != mModalRoot; temp = temp->Parent()) {
uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
*aResult = aCache.ApplyFilter(temp, &filtered);
NS_ENSURE_SUCCESS(*aResult, nullptr);
if (filtered & nsIAccessibleTraversalRule::FILTER_IGNORE_SUBTREE) {
*aFilterResult = filtered;
matched = temp;
}
}
}
return matched;
}
Accessible*
nsAccessiblePivot::SearchBackward(Accessible* aAccessible,
nsIAccessibleTraversalRule* aRule,
@ -428,15 +453,13 @@ nsAccessiblePivot::SearchBackward(Accessible* aAccessible,
return nullptr;
RuleCache cache(aRule);
Accessible* accessible = aAccessible;
uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
Accessible* accessible = AdjustStartPosition(aAccessible, cache,
&filtered, aResult);
NS_ENSURE_SUCCESS(*aResult, nullptr);
if (aSearchCurrent) {
*aResult = cache.ApplyFilter(accessible, &filtered);
NS_ENSURE_SUCCESS(*aResult, nullptr);
if (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)
return accessible;
if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH)) {
return accessible;
}
Accessible* root = GetActiveRoot();
@ -492,7 +515,7 @@ nsAccessiblePivot::SearchForward(Accessible* aAccessible,
RuleCache cache(aRule);
uint16_t filtered = nsIAccessibleTraversalRule::FILTER_IGNORE;
*aResult = cache.ApplyFilter(accessible, &filtered);
accessible = AdjustStartPosition(accessible, cache, &filtered, aResult);
NS_ENSURE_SUCCESS(*aResult, nullptr);
if (aSearchCurrent && (filtered & nsIAccessibleTraversalRule::FILTER_MATCH))
return accessible;

View File

@ -16,6 +16,7 @@
#include "mozilla/Attributes.h"
class nsIAccessibleTraversalRule;
class RuleCache;
/**
* Class represents an accessible pivot.
@ -90,6 +91,18 @@ private:
*/
bool MovePivotInternal(Accessible* aPosition, PivotMoveReason aReason);
/*
* Get initial node we should start a search from with a given rule.
*
* When we do a move operation from one position to another,
* the initial position can be inside of a subtree that is ignored by
* the given rule. We need to step out of the ignored subtree and start
* the search from there.
*
*/
Accessible* AdjustStartPosition(Accessible* aAccessible, RuleCache& aCache,
uint16_t* aFilterResult, nsresult* aResult);
/*
* The root accessible.
*/

View File

@ -1220,7 +1220,41 @@ HyperTextAccessible::GetTextAfterOffset(int32_t aOffset,
return GetText(*aStartOffset, *aEndOffset, aText);
case BOUNDARY_LINE_START:
if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
offset = AdjustCaretOffset(offset);
// Down arrow, home key, down arrow, home key.
*aStartOffset = FindLineBoundary(offset, eDirNext, eSelectLine);
if (*aStartOffset != CharacterCount()) {
*aStartOffset = FindLineBoundary(*aStartOffset, eDirPrevious, eSelectBeginLine);
*aEndOffset = FindLineBoundary(*aStartOffset, eDirNext, eSelectLine);
if (*aEndOffset != CharacterCount())
*aEndOffset = FindLineBoundary(*aEndOffset, eDirPrevious, eSelectBeginLine);
} else {
*aEndOffset = CharacterCount();
}
return GetText(*aStartOffset, *aEndOffset, aText);
case BOUNDARY_LINE_END:
if (aOffset == nsIAccessibleText::TEXT_OFFSET_CARET)
offset = AdjustCaretOffset(offset);
// Empty last line doesn't have own frame (a previous line contains '\n'
// character instead) thus we can't operate on last line separately
// from the previous line.
if (IsEmptyLastLineOffset(offset)) {
*aStartOffset = *aEndOffset = offset;
return NS_OK;
}
// End key, down arrow, end key.
*aStartOffset = FindLineBoundary(offset, eDirNext, eSelectEndLine);
*aEndOffset = FindLineBoundary(*aStartOffset, eDirNext, eSelectLine);
if (*aEndOffset != CharacterCount())
*aEndOffset = FindLineBoundary(*aEndOffset, eDirNext, eSelectEndLine);
return GetText(*aStartOffset, *aEndOffset, aText);
case BOUNDARY_ATTRIBUTE_RANGE:
return GetTextHelper(eGetAfter, aBoundaryType, aOffset,
aStartOffset, aEndOffset, aText);

View File

@ -76,6 +76,21 @@ function VCChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets, aPivotMoveMetho
{
this.__proto__ = new invokerChecker(EVENT_VIRTUALCURSOR_CHANGED, aDocAcc);
this.match = function VCChangedChecker_check(aEvent)
{
var event = null;
try {
event = aEvent.QueryInterface(nsIAccessibleVirtualCursorChangeEvent);
} catch (e) {
return false;
}
var expectedReason = VCChangedChecker.methodReasonMap[aPivotMoveMethod] ||
nsIAccessiblePivot.REASON_NONE;
return event.reason == expectedReason;
};
this.check = function VCChangedChecker_check(aEvent)
{
SimpleTest.info("VCChangedChecker_check");
@ -87,11 +102,6 @@ function VCChangedChecker(aDocAcc, aIdOrNameOrAcc, aTextOffsets, aPivotMoveMetho
SimpleTest.ok(false, "Does not support correct interface: " + e);
}
SimpleTest.is(
event.reason,
VCChangedChecker.methodReasonMap[aPivotMoveMethod],
'wrong move reason');
var position = aDocAcc.virtualCursor.position;
var idMatches = position && position.DOMNode.id == aIdOrNameOrAcc;
var nameMatches = position && position.name == aIdOrNameOrAcc;
@ -196,9 +206,14 @@ function setVCPosInvoker(aDocAcc, aPivotMoveMethod, aRule, aIdOrNameOrAcc)
{
VCChangedChecker.
storePreviousPosAndOffset(aDocAcc.virtualCursor);
var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule);
SimpleTest.ok((expectMove && moved) || (!expectMove && !moved),
"moved pivot");
if (aPivotMoveMethod && aRule) {
var moved = aDocAcc.virtualCursor[aPivotMoveMethod](aRule);
SimpleTest.is(!!moved, !!expectMove,
"moved pivot with " + aPivotMoveMethod +
" to " + aIdOrNameOrAcc);
} else {
aDocAcc.virtualCursor.position = getAccessible(aIdOrNameOrAcc);
}
};
this.getID = function setVCPosInvoker_getID()
@ -447,7 +462,7 @@ function removeVCRootInvoker(aRootNode)
*/
function dumpTraversalSequence(aPivot, aRule)
{
var sequence = []
var sequence = [];
if (aPivot.moveFirst(aRule)) {
do {
sequence.push("'" + prettyName(aPivot.position) + "'");

View File

@ -15,7 +15,7 @@
<p id="paragraph-2" aria-hidden="">
Sed accumsan luctus lacus, vitae mollis arcu tristique vulputate.</p>
<p id="paragraph-3" aria-hidden="true">
Maybe it was the other <i>George Michael</i>.
<a href="#" id="hidden-link">Maybe</a> it was the other <i>George Michael</i>.
You know, the <a href="#">singer-songwriter</a>.
</p>
<iframe

View File

@ -95,6 +95,12 @@
gQueue.push(new setModalRootInvoker(docAcc, docAcc.parent,
NS_ERROR_INVALID_ARG));
// Put cursor in an ignored subtree
gQueue.push(new setVCPosInvoker(docAcc, null, null,
getAccessible(doc.getElementById("hidden-link"))));
// Next item shoud be outside of that subtree
gQueue.push(new setVCPosInvoker(docAcc, "moveNext", ObjectTraversalRule, "An "));
gQueue.invoke();
}

View File

@ -44,7 +44,7 @@
["textarea"]);
testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "", 15, 15,
"textarea", kOk, kTodo, kTodo);
[ "textarea" ]);
testTextAtOffset(kCaretOffset, BOUNDARY_LINE_START, "words", 10, 15,
[ "textarea" ]);
@ -72,10 +72,10 @@
this.finalCheck = function moveToLastLineStart_finalCheck()
{
testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_START, "", 15, 15,
"textarea", kTodo, kTodo, kOk);
[ "textarea" ]);
testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "", 15, 15,
"textarea", kTodo, kTodo, kOk);
[ "textarea" ]);
testTextAtOffset(kCaretOffset, BOUNDARY_LINE_START, "words", 10, 15,
[ "textarea" ]);
@ -104,10 +104,10 @@
this.finalCheck = function moveToMiddleLineStart_finalCheck()
{
testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_START, "words", 10, 15,
"textarea", kTodo, kTodo, kTodo);
[ "textarea" ]);
testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "words", 10, 15,
"textarea", kTodo, kTodo, kTodo);
[ "textarea" ]);
testTextAtOffset(kCaretOffset, BOUNDARY_LINE_START, "two ", 6, 10,
[ "textarea" ]);
@ -135,10 +135,10 @@
this.finalCheck = function moveToMiddleLineEnd_finalCheck()
{
testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_START, "words", 10, 15,
"textarea", kTodo, kTodo, kTodo);
[ "textarea" ]);
testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "words", 10, 15,
"textarea", kTodo, kTodo, kOk);
[ "textarea" ]);
testTextAtOffset(kCaretOffset, BOUNDARY_LINE_START, "two ", 6, 10,
[ "textarea" ]);
@ -166,10 +166,10 @@
this.finalCheck = function moveToFirstLineStart_finalCheck()
{
testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_START, "two ", 6, 10,
"textarea", kTodo, kTodo, kTodo);
[ "textarea" ]);
testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "aword", 0, 5,
"textarea", kOk, kOk, kOk);
testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "\ntwo ", 5, 10,
[ "textarea" ]);
testTextAtOffset(kCaretOffset, BOUNDARY_LINE_START, "aword\n", 0, 6,
[ "textarea" ]);
@ -197,10 +197,10 @@
this.finalCheck = function moveToFirstLineStart_finalCheck()
{
testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_START, "two ", 6, 10,
"textarea", kTodo, kTodo, kTodo);
[ "textarea" ]);
testTextAfterOffset(kCaretOffset, BOUNDARY_LINE_END, "\ntwo ", 5, 10,
"textarea", kTodo, kTodo, kTodo);
[ "textarea" ]);
testTextAtOffset(kCaretOffset, BOUNDARY_LINE_START, "aword\n", 0, 6,
[ "textarea" ]);
@ -224,8 +224,6 @@
var gQueue = null;
function doTest()
{
SimpleTest.expectAssertions(1);
gQueue = new eventQueue();
gQueue.push(new moveToLastLineEnd());
gQueue.push(new moveToLastLineStart());

View File

@ -16,8 +16,6 @@
function doTest()
{
SimpleTest.expectAssertions(5);
// __o__n__e__w__o__r__d__\n
// 0 1 2 3 4 5 6 7
// __\n
@ -34,74 +32,19 @@
// getTextAfterOffset
// BOUNDARY_LINE_START
testTextAfterOffset(0, BOUNDARY_LINE_START, "\n", 8, 9,
"div", kTodo, kTodo, kTodo,
"divbr", kTodo, kTodo, kTodo,
"editable", kTodo, kTodo, kTodo,
"editablebr", kTodo, kTodo, kTodo,
"textarea", kTodo, kTodo, kTodo);
testTextAfterOffset(7, BOUNDARY_LINE_START, "\n", 8, 9,
"div", kOk, kTodo, kTodo,
"divbr", kOk, kTodo, kTodo,
"editable", kOk, kTodo, kTodo,
"editablebr", kOk, kTodo, kTodo,
"textarea", kOk, kTodo, kTodo);
testTextAfterOffset(8, BOUNDARY_LINE_START, "two words\n", 9, 19,
"div", kTodo, kTodo, kTodo,
"divbr", kTodo, kTodo, kTodo,
"editable", kTodo, kTodo, kTodo,
"editablebr", kTodo, kTodo, kTodo,
"textarea", kTodo, kTodo, kTodo);
testTextAfterOffset(9, BOUNDARY_LINE_START, "", 19, 19,
"div", kTodo, kTodo, kTodo,
"divbr", kTodo, kTodo, kTodo,
"editable", kTodo, kTodo, kTodo,
"editablebr", kTodo, kTodo, kTodo,
"textarea", kTodo, kTodo, kTodo);
testTextAfterOffset(19, BOUNDARY_LINE_START, "", 19, 19,
"div", kOk, kOk, kTodo,
"divbr", kOk, kOk, kTodo,
"editable", kOk, kOk, kTodo,
"editablebr", kOk, kOk, kTodo,
"textarea", kOk, kOk, kTodo);
testTextAfterOffset(0, BOUNDARY_LINE_START, "\n", 8, 9, IDs);
testTextAfterOffset(7, BOUNDARY_LINE_START, "\n", 8, 9, IDs);
testTextAfterOffset(8, BOUNDARY_LINE_START, "two words\n", 9, 19, IDs);
testTextAfterOffset(9, BOUNDARY_LINE_START, "", 19, 19, IDs);
testTextAfterOffset(19, BOUNDARY_LINE_START, "", 19, 19, IDs);
// BOUNDARY_LINE_END
testTextAfterOffset(0, BOUNDARY_LINE_END, "\n", 7, 8,
"div", kTodo, kTodo, kTodo,
"divbr", kTodo, kTodo, kTodo,
"editable", kTodo, kTodo, kTodo,
"editablebr", kTodo, kTodo, kTodo,
"textarea", kTodo, kTodo, kTodo);
testTextAfterOffset(7, BOUNDARY_LINE_END, "\n", 7, 8,
"div", kOk, kOk, kOk,
"divbr", kOk, kOk, kOk,
"editable", kOk, kOk, kOk,
"editablebr", kOk, kOk, kOk,
"textarea", kOk, kOk, kOk);
testTextAfterOffset(8, BOUNDARY_LINE_END, "\ntwo words", 8, 18,
"div", kOk, kOk, kOk,
"divbr", kOk, kOk, kOk,
"editable", kOk, kOk, kOk,
"editablebr", kOk, kOk, kOk,
"textarea", kOk, kOk, kOk);
testTextAfterOffset(9, BOUNDARY_LINE_END, "\n", 18, 19,
"div", kTodo, kTodo, kTodo,
"divbr", kTodo, kTodo, kTodo,
"editable", kTodo, kTodo, kTodo,
"editablebr", kTodo, kTodo, kTodo,
"textarea", kTodo, kTodo, kTodo);
testTextAfterOffset(18, BOUNDARY_LINE_END, "\n", 18, 19,
"div", kOk, kOk, kOk,
"divbr", kOk, kOk, kOk,
"editable", kOk, kOk, kOk,
"editablebr", kOk, kOk, kOk,
"textarea", kOk, kOk, kOk);
testTextAfterOffset(19, BOUNDARY_LINE_END, "", 19, 19,
"div", kOk, kTodo, kTodo,
"divbr", kOk, kTodo, kTodo,
"editable", kOk, kTodo, kTodo,
"editablebr", kOk, kTodo, kTodo,
"textarea", kOk, kTodo, kTodo);
testTextAfterOffset(0, BOUNDARY_LINE_END, "\n", 7, 8, IDs);
testTextAfterOffset(7, BOUNDARY_LINE_END, "\n", 7, 8, IDs);
testTextAfterOffset(8, BOUNDARY_LINE_END, "\ntwo words", 8, 18, IDs);
testTextAfterOffset(9, BOUNDARY_LINE_END, "\n", 18, 19, IDs);
testTextAfterOffset(18, BOUNDARY_LINE_END, "\n", 18, 19, IDs);
testTextAfterOffset(19, BOUNDARY_LINE_END, "", 19, 19, IDs);
////////////////////////////////////////////////////////////////////////
// getTextBeforeOffset
@ -166,6 +109,11 @@
href="https://bugzilla.mozilla.org/show_bug.cgi?id=855732">
Bug 855732
</a>
<a target="_blank"
title=" getTextAfterOffset for line boundary on new rails"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=882292">
Bug 882292
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">

View File

@ -11,12 +11,6 @@
<script type="application/javascript"
src="../text.js"></script>
<script type="application/javascript">
if (navigator.platform.startsWith("Mac")) {
SimpleTest.expectAssertions(0, 4);
} else {
SimpleTest.expectAssertions(4);
}
function doTest()
{
// __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__
@ -29,48 +23,16 @@
var regularIDs = [ "input", "div", "editable" ];
// BOUNDARY_LINE_START
testTextAfterOffset(0, BOUNDARY_LINE_START, "", 15, 15,
"input", kTodo, kTodo, kOk,
"div", kTodo, kTodo, kOk,
"editable", kTodo, kTodo, kOk,
"textarea", kTodo, kTodo, kOk);
testTextAfterOffset(1, BOUNDARY_LINE_START, "", 15, 15,
"input", kTodo, kTodo, kOk,
"div", kTodo, kTodo, kOk,
"editable", kTodo, kTodo, kOk,
"textarea", kTodo, kTodo, kOk);
testTextAfterOffset(14, BOUNDARY_LINE_START, "", 15, 15,
"input", kTodo, kTodo, kOk,
"div", kTodo, kTodo, kOk,
"editable", kTodo, kTodo, kOk,
"textarea", kTodo, kTodo, kOk);
testTextAfterOffset(15, BOUNDARY_LINE_START, "", 15, 15,
"input", kOk, kOk, kOk,
"div", kOk, kOk, kOk,
"editable", kOk, kOk, kOk,
"textarea", kOk, kOk, kOk);
testTextAfterOffset(0, BOUNDARY_LINE_START, "", 15, 15, IDs);
testTextAfterOffset(1, BOUNDARY_LINE_START, "", 15, 15, IDs);
testTextAfterOffset(14, BOUNDARY_LINE_START, "", 15, 15, IDs);
testTextAfterOffset(15, BOUNDARY_LINE_START, "", 15, 15, IDs);
// BOUNDARY_LINE_END
testTextAfterOffset(0, BOUNDARY_LINE_END, "", 15, 15,
"input", kTodo, kTodo, kOk,
"div", kTodo, kTodo, kOk,
"editable", kTodo, kTodo, kOk,
"textarea", kTodo, kTodo, kOk);
testTextAfterOffset(1, BOUNDARY_LINE_END, "", 15, 15,
"input", kTodo, kTodo, kOk,
"div", kTodo, kTodo, kOk,
"editable", kTodo, kTodo, kOk,
"textarea", kTodo, kTodo, kOk);
testTextAfterOffset(14, BOUNDARY_LINE_END, "", 15, 15,
"input", kTodo, kTodo, kOk,
"div", kTodo, kTodo, kOk,
"editable", kTodo, kTodo, kOk,
"textarea", kTodo, kTodo, kOk);
testTextAfterOffset(15, BOUNDARY_LINE_END, "", 15, 15,
"input", kOk, kTodo, kTodo,
"div", kOk, kTodo, kTodo,
"editable", kOk, kTodo, kTodo,
"textarea", kOk, kTodo, kTodo);
testTextAfterOffset(0, BOUNDARY_LINE_END, "", 15, 15, IDs);
testTextAfterOffset(1, BOUNDARY_LINE_END, "", 15, 15, IDs);
testTextAfterOffset(14, BOUNDARY_LINE_END, "", 15, 15, IDs);
testTextAfterOffset(15, BOUNDARY_LINE_END, "", 15, 15, IDs);
////////////////////////////////////////////////////////////////////////
// getTextBeforeOffset
@ -131,6 +93,11 @@
href="https://bugzilla.mozilla.org/show_bug.cgi?id=855732">
Bug 855732
</a>
<a target="_blank"
title=" getTextAfterOffset for line boundary on new rails"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=882292">
Bug 882292
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">

View File

@ -559,10 +559,13 @@ pref("javascript.options.mem.log", false);
// Increase mark slice time from 10ms to 30ms
pref("javascript.options.mem.gc_incremental_slice_ms", 30);
pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 150);
// Increase time to get more high frequency GC on benchmarks from 1000ms to 1500ms
pref("javascript.options.mem.gc_high_frequency_time_limit_ms", 1500);
pref("javascript.options.mem.gc_high_frequency_heap_growth_max", 300);
pref("javascript.options.mem.gc_high_frequency_heap_growth_min", 120);
pref("javascript.options.mem.gc_high_frequency_high_limit_mb", 40);
pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 10);
pref("javascript.options.mem.gc_high_frequency_low_limit_mb", 0);
pref("javascript.options.mem.gc_low_frequency_heap_growth", 120);
pref("javascript.options.mem.high_water_mark", 6);
pref("javascript.options.mem.gc_allocation_threshold_mb", 1);

View File

@ -19,3 +19,6 @@ ifdef MOZ_MAINTENANCE_SERVICE
$(MAKE) -C installer/windows maintenanceservice_installer
endif
endif
check::
$(PYTHON) $(topsrcdir)/build/compare-mozconfig/compare-mozconfigs-wrapper.py

View File

@ -3668,10 +3668,8 @@ var XULBrowserWindow = {
init: function () {
this.throbberElement = document.getElementById("navigator-throbber");
// Initialize the security button's state and tooltip text. Remember to reset
// _hostChanged, otherwise onSecurityChange will short circuit.
// Initialize the security button's state and tooltip text.
var securityUI = gBrowser.securityUI;
this._hostChanged = true;
this.onSecurityChange(null, null, securityUI.state);
},
@ -3862,7 +3860,6 @@ var XULBrowserWindow = {
onLocationChange: function (aWebProgress, aRequest, aLocationURI, aFlags) {
var location = aLocationURI ? aLocationURI.spec : "";
this._hostChanged = true;
// Hide the form invalid popup.
if (gFormSubmitObserver.panel) {
@ -4004,37 +4001,18 @@ var XULBrowserWindow = {
// Properties used to cache security state used to update the UI
_state: null,
_hostChanged: false, // onLocationChange will flip this bit
_lastLocation: null,
onSecurityChange: function (aWebProgress, aRequest, aState) {
// Don't need to do anything if the data we use to update the UI hasn't
// changed
let uri = gBrowser.currentURI;
let spec = uri.spec;
if (this._state == aState &&
!this._hostChanged) {
#ifdef DEBUG
try {
var contentHost = gBrowser.contentWindow.location.host;
if (this._host !== undefined && this._host != contentHost) {
Components.utils.reportError(
"ASSERTION: browser.js host is inconsistent. Content window has " +
"<" + contentHost + "> but cached host is <" + this._host + ">.\n"
);
}
} catch (ex) {}
#endif
this._lastLocation == spec)
return;
}
this._state = aState;
#ifdef DEBUG
try {
this._host = gBrowser.contentWindow.location.host;
} catch(ex) {
this._host = null;
}
#endif
this._hostChanged = false;
this._lastLocation = spec;
// aState is defined as a bitmask that may be extended in the future.
// We filter out any unknown bits before testing for known values.
@ -4063,7 +4041,6 @@ var XULBrowserWindow = {
gURLBar.removeAttribute("level");
}
let uri = gBrowser.currentURI;
try {
uri = Services.uriFixup.createExposableURI(uri);
} catch (e) {}

View File

@ -87,7 +87,7 @@ function part7() {
ok(!objLoadingContent.activated, "plugin should not be activated");
EventUtils.synthesizeMouseAtCenter(plugin, {}, gNewWindow.gBrowser.selectedBrowser.contentWindow);
let condition = function() !PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser).dismissed;
let condition = function() !PopupNotifications.getNotification("click-to-play-plugins", gNewWindow.gBrowser.selectedBrowser).dismissed && gNewWindow.PopupNotifications.panel.firstChild;
waitForCondition(condition, part8, "waited too long for plugin to activate");
}

View File

@ -34,7 +34,6 @@ function test() {
function checkIdentityMode(win) {
let identityMode = win.document.getElementById("identity-box").className;
is(identityMode, "unknownIdentity", "Identity should be chromeUI but is currently " +
"shown as unknownIdentity for new windows.");
is(identityMode, "chromeUI", "Identity state should be chromeUI for about:home in a new window");
finish();
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 965 B

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -118,15 +118,20 @@
SimpleTest.isnot(listItem, null, "Valid listItem found");
tagsSelector.ensureElementIsVisible(listItem);
let visibleIndex = tagsSelector.getIndexOfFirstVisibleRow();
let firstVisibleTag = tags[tagsSelector.getIndexOfFirstVisibleRow()];
SimpleTest.ok(listItem.checked, "Item is checked " + i);
let selectedTag = listItem.label;
// Uncheck the tag.
listItem.checked = false;
SimpleTest.is(tagsSelector.getIndexOfFirstVisibleRow(),
Math.max(visibleIndex -1, 0),
// Ensure the first visible tag is still visible in the list.
let firstVisibleIndex = tagsSelector.getIndexOfFirstVisibleRow();
let lastVisibleIndex = firstVisibleIndex + tagsSelector.getNumberOfVisibleRows() -1;
let expectedTagIndex = tags.indexOf(firstVisibleTag);
SimpleTest.ok(expectedTagIndex >= firstVisibleIndex &&
expectedTagIndex <= lastVisibleIndex,
"Scroll position is correct");
// The listbox is rebuilt, so we have to get the new element.

View File

@ -0,0 +1,90 @@
# 'nightly' contains things that are in nightly mozconfigs and allowed to be missing from release builds.
# Other keys in whitelist contain things are in that branches mozconfigs and allowed to be missing from nightly builds.
whitelist = {
'release': {},
'nightly': {},
}
all_platforms = ['win32', 'linux32', 'linux64', 'macosx-universal']
for platform in all_platforms:
whitelist['nightly'][platform] = [
'ac_add_options --enable-update-channel=nightly',
'ac_add_options --enable-profiling',
'mk_add_options CLIENT_PY_ARGS="--hg-options=\'--verbose --time\' --hgtool=../tools/buildfarm/utils/hgtool.py --skip-chatzilla --skip-comm --skip-inspector --skip-venkman --tinderbox-print"'
]
for platform in ['linux32', 'linux64', 'macosx-universal']:
whitelist['nightly'][platform] += [
'ac_add_options --enable-codesighs',
'mk_add_options MOZ_MAKE_FLAGS="-j4"',
]
for platform in ['linux32', 'linux64', 'macosx-universal', 'win32']:
whitelist['nightly'][platform] += ['ac_add_options --enable-signmar']
whitelist['nightly'][platform] += ['ac_add_options --enable-js-diagnostics']
whitelist['nightly']['linux32'] += [
'CXX=$REAL_CXX',
'CXX="ccache $REAL_CXX"',
'CC="ccache $REAL_CC"',
'mk_add_options PROFILE_GEN_SCRIPT=@TOPSRCDIR@/build/profile_pageloader.pl',
'ac_add_options --with-ccache=/usr/bin/ccache',
'export MOZILLA_OFFICIAL=1',
'export MOZ_TELEMETRY_REPORTING=1',
"mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
'STRIP_FLAGS="--strip-debug"',
'ac_add_options --disable-elf-hack # --enable-elf-hack conflicts with --enable-profiling',
]
whitelist['nightly']['linux64'] += [
'export MOZILLA_OFFICIAL=1',
'export MOZ_TELEMETRY_REPORTING=1',
"mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
'STRIP_FLAGS="--strip-debug"',
'ac_add_options --with-ccache=/usr/bin/ccache',
'ac_add_options --disable-elf-hack # --enable-elf-hack conflicts with --enable-profiling',
]
whitelist['nightly']['macosx-universal'] += [
'ac_add_options --with-macbundlename-prefix=Firefox',
'mk_add_options MOZ_MAKE_FLAGS="-j12"',
'ac_add_options --with-ccache',
'ac_add_options --disable-install-strip',
'ac_add_options --enable-instruments',
'ac_add_options --enable-dtrace',
]
whitelist['nightly']['win32'] += [
'. $topsrcdir/configs/mozilla2/win32/include/choose-make-flags',
'mk_add_options MOZ_MAKE_FLAGS=-j1',
'if test "$IS_NIGHTLY" != ""; then',
'ac_add_options --disable-auto-deps',
'fi',
'ac_add_options --enable-metro',
]
for platform in all_platforms:
whitelist['release'][platform] = [
'ac_add_options --enable-update-channel=release',
'ac_add_options --enable-official-branding',
'mk_add_options MOZ_MAKE_FLAGS="-j4"',
'export BUILDING_RELEASE=1',
]
whitelist['release']['win32'] += ['mk_add_options MOZ_PGO=1']
whitelist['release']['linux32'] += [
'export MOZILLA_OFFICIAL=1',
'export MOZ_TELEMETRY_REPORTING=1',
'mk_add_options MOZ_PGO=1',
"mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
]
whitelist['release']['linux64'] += [
'export MOZILLA_OFFICIAL=1',
'export MOZ_TELEMETRY_REPORTING=1',
'mk_add_options MOZ_PGO=1',
"mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'",
]
if __name__ == '__main__':
import pprint
pprint.pprint(whitelist)

View File

@ -161,7 +161,7 @@ var ContextCommands = {
openLinkInNewTab: function cc_openLinkInNewTab() {
Browser.addTab(ContextMenuUI.popupState.linkURL, false, Browser.selectedTab);
ContextUI.peekTabs();
ContextUI.peekTabs(kOpenInNewTabAnimationDelayMsec);
},
copyLink: function cc_copyLink() {

View File

@ -10,12 +10,6 @@ const kContextUIDismissEvent = "MozContextUIDismiss";
const kContextUITabsShowEvent = "MozContextUITabsShow";
// add more as needed...
// delay for ContextUI's dismissTabsWithDelay
const kHideContextAndTrayDelayMsec = 3000;
// delay when showing the tab bar briefly as a new tab opens
const kNewTabAnimationDelayMsec = 1000;
/*
* Manages context UI (navbar, tabbar, appbar) and track visibility. Also
* tracks events that summon and hide the context UI.
@ -160,35 +154,24 @@ var ContextUI = {
/*
* Briefly show the tab bar and then hide it. Fires context ui events.
*/
peekTabs: function peekTabs() {
if (this.tabbarVisible) {
setTimeout(function () {
ContextUI.dismissTabsWithDelay(kNewTabAnimationDelayMsec);
}, 0);
} else {
BrowserUI.setOnTabAnimationEnd(function () {
ContextUI.dismissTabsWithDelay(kNewTabAnimationDelayMsec);
});
peekTabs: function peekTabs(aDelay) {
if (!this.tabbarVisible)
this.displayTabs();
}
ContextUI.dismissTabsWithDelay(aDelay);
},
/*
* Dismiss tab bar after a delay. Fires context ui events.
*/
dismissTabsWithDelay: function (aDelay) {
aDelay = aDelay || kHideContextAndTrayDelayMsec;
aDelay = aDelay || kNewTabAnimationDelayMsec;
this._clearDelayedTimeout();
this._hidingId = setTimeout(function () {
ContextUI.dismissTabs();
}, aDelay);
},
// Cancel any pending delayed dismiss
cancelDismiss: function cancelDismiss() {
this._clearDelayedTimeout();
},
// Display the nav bar
displayNavbar: function () {
this._clearDelayedTimeout();
@ -201,12 +184,6 @@ var ContextUI = {
this._setIsExpanded(true);
},
// Display the app bar
displayContextAppbar: function () {
this._clearDelayedTimeout();
Elements.contextappbar.show();
},
// Dismiss the navbar if visible.
dismissNavbar: function dismissNavbar() {
Elements.navbar.dismiss();
@ -312,15 +289,9 @@ var ContextUI = {
if (aEvent.button == 0 && this.isVisible)
this.dismiss();
break;
case 'URLChanged':
this.dismissTabs();
break;
case 'TabSelect':
this.dismissTabs();
break;
case 'ToolPanelShown':
case 'ToolPanelHidden':
case "ToolPanelShown":
case "ToolPanelHidden":
case "touchstart":
case "AlertActive":
this.dismiss();

View File

@ -84,8 +84,10 @@ const WebProgress = {
let spec = aJson.location;
let location = spec.split("#")[0]; // Ignore fragment identifier changes.
if (aTab == Browser.selectedTab)
if (aTab == Browser.selectedTab) {
BrowserUI.updateURI();
BrowserUI.update();
}
let locationHasChanged = (location != aTab.browser.lastLocation);
if (locationHasChanged) {
@ -123,22 +125,15 @@ const WebProgress = {
_networkStart: function _networkStart(aJson, aTab) {
aTab.startLoading();
if (aTab == Browser.selectedTab) {
if (aTab == Browser.selectedTab)
BrowserUI.update(TOOLBARSTATE_LOADING);
// We should at least show something in the URLBar until
// the load has progressed further along
if (aTab.browser.currentURI.spec == "about:blank")
BrowserUI.updateURI({ captionOnly: true });
}
},
_networkStop: function _networkStop(aJson, aTab) {
aTab.endLoading();
if (aTab == Browser.selectedTab) {
if (aTab == Browser.selectedTab)
BrowserUI.update(TOOLBARSTATE_LOADED);
}
},
_windowStart: function _windowStart(aJson, aTab) {
@ -162,7 +157,7 @@ const WebProgress = {
// 'Whoosh' in
this._progressCount = kProgressMarginStart;
Elements.progress.style.width = this._progressCount + "%";
Elements.progress.style.width = this._progressCount + "%";
Elements.progress.removeAttribute("fade");
// Create a pulse timer to keep things moving even if we don't
@ -204,7 +199,7 @@ const WebProgress = {
_progressStop: function _progressStop(aJson, aTab) {
this._progressActive = false;
// 'Whoosh out' and fade
Elements.progress.style.width = "100%";
Elements.progress.style.width = "100%";
Elements.progress.setAttribute("fade", true);
},
@ -213,7 +208,7 @@ const WebProgress = {
return;
// Close out fade finished, reset
if (data.propertyName == "opacity") {
Elements.progress.style.width = "0px";
Elements.progress.style.width = "0px";
Elements.progressContainer.setAttribute("collapsed", true);
}
},

View File

@ -115,7 +115,7 @@ var Appbar = {
}
var x = this.menuButton.getBoundingClientRect().left;
var y = Elements.navbar.getBoundingClientRect().top;
var y = Elements.toolbar.getBoundingClientRect().top;
ContextMenuUI.showContextMenu({
json: {
types: typesArray,

View File

@ -1,473 +0,0 @@
<?xml version="1.0" encoding="Windows-1252" ?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE bindings [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="autocomplete" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
<implementation>
<constructor>
<![CDATA[
this.minResultsForPopup = 0;
this.popup._input = this;
]]>
</constructor>
<method name="openPopup">
<body>
<![CDATA[
this.popup.openAutocompletePopup(this, null);
]]>
</body>
</method>
<method name="closePopup">
<body>
<![CDATA[
this.popup.closePopup(this, null);
]]>
</body>
</method>
<method name="formatValue">
<body>
<![CDATA[
BrowserUI.formatURI();
]]>
</body>
</method>
<method name="trimValue">
<parameter name="aURL"/>
<body><![CDATA[
return BrowserUI.trimURL(aURL);
]]></body>
</method>
</implementation>
<handlers>
<handler event="click" phase="capturing">
<![CDATA[
// If the urlbar is not already focused, focus it and select the contents.
if (Elements.urlbarState.getAttribute("mode") != "edit") {
BrowserUI._editURI(true);
}
]]>
</handler>
<handler event="dblclick" phase="capturing">
<![CDATA[
let selectAll = Services.prefs.getBoolPref("browser.urlbar.doubleClickSelectsAll");
if (selectAll)
this.select();
]]>
</handler>
<handler event="contextmenu" phase="capturing">
<![CDATA[
let box = this.inputField.parentNode;
box.showContextMenu(this, event, true);
]]>
</handler>
<handler event="keypress" phase="capturing" keycode="VK_RETURN">
<![CDATA[
event.preventDefault();
event.stopPropagation();
this.popup.handleCompletion();
]]>
</handler>
</handlers>
</binding>
<binding id="autocomplete-popup">
<content orient="horizontal">
<xul:vbox id="results-vbox" class="meta-section viewable-height" flex="1">
<xul:label class="meta-section-title" value="&autocompleteResultsHeader.label;"/>
<richgrid id="results-richgrid" deferlayout="true" anonid="results" seltype="single" flex="1"/>
</xul:vbox>
<xul:vbox id="searches-vbox" class="meta-section viewable-height" flex="1">
<xul:label class="meta-section-title" value="&autocompleteSearchesHeader.label;"/>
<richgrid id="searches-richgrid" deferlayout="true" anonid="searches" seltype="single" flex="1"/>
</xul:vbox>
</content>
<implementation implements="nsIAutoCompletePopup, nsIObserver">
<constructor>
<![CDATA[
Services.obs.addObserver(this, "browser-search-engine-modified", false);
this.updateSearchEngines();
this._results.controller = this;
this._searches.controller = this;
]]>
</constructor>
<destructor>
<![CDATA[
Services.obs.removeObserver(this, "browser-search-engine-modified");
]]>
</destructor>
<method name="handleItemClick">
<parameter name="aItem"/>
<body>
<![CDATA[
if (aItem.control == this._searches) {
let engineName = aItem.getAttribute("value");
BrowserUI.doOpenSearch(engineName);
} else {
let url = aItem.getAttribute("value");
Browser.loadURI(url);
}
]]>
</body>
</method>
<!-- nsIAutocompleteInput -->
<field name="_input">null</field>
<field name="_popupOpen">false</field>
<property name="overrideValue" readonly="true" onget="return null;"/>
<property name="selectedItem">
<getter>
<![CDATA[
return this._isGridBound(this._results) ? this._results.selectedItem : null;
]]>
</getter>
<setter>
<![CDATA[
return this._isGridBound(this._results) ? this._results.selectedItem : null;
]]>
</setter>
</property>
<property name="selectedIndex">
<getter>
<![CDATA[
return this._isGridBound(this._results) ? this._results.selectedIndex : -1;
]]>
</getter>
<setter>
<![CDATA[
return this._isGridBound(this._results) ? this._results.selectedIndex : -1;
]]>
</setter>
</property>
<property name="input" readonly="true" onget="return this._input;"/>
<property name="popupOpen" readonly="true" onget="return this._popupOpen;"/>
<property name="_matchCount" readonly="true" onget="return this.input.controller.matchCount;"/>
<method name="openAutocompletePopup">
<parameter name="aInput"/>
<parameter name="aElement"/>
<body>
<![CDATA[
if (this._popupOpen)
return;
ContextUI.dismissContextAppbar();
this._input = aInput;
this._popupOpen = true;
this._grid = this._results;
this.clearSelection();
this.invalidate();
this._results.arrangeItemsNow();
this._searches.arrangeItemsNow();
this._fire("autocompletestart");
]]>
</body>
</method>
<method name="gridBoundCallback">
<body>
<![CDATA[
this.updateResults();
]]>
</body>
</method>
<method name="closePopup">
<body>
<![CDATA[
if (!this._popupOpen)
return;
this.input.controller.stopSearch();
this._popupOpen = false;
this._fire("autocompleteend");
]]>
</body>
</method>
<method name="invalidate">
<body>
<![CDATA[
if (!this._popupOpen)
return;
this.updateResults();
this.updateSearchEngineSubtitles();
]]>
</body>
</method>
<method name="selectBy">
<parameter name="aReverse"/>
<parameter name="aPage"/>
<body>
<![CDATA[
let handleOnSelect = this._handleOnSelect;
this._handleOnSelect = false;
// TODO <jwilde>: Pressing page up/down should jump between rows,
// not just items in the grid
// Move between grids if we're at the edge of one
if ((this._grid.isSelectionAtEnd && !aReverse) ||
(this._grid.isSelectionAtStart && aReverse)) {
let index = aReverse ? this._otherGrid.itemCount - 1 : 0;
this._otherGrid.selectedIndex = index;
} else {
this._grid.offsetSelection(aReverse ? -1 : 1);
}
this._handleOnSelect = handleOnSelect;
]]>
</body>
</method>
<!-- nsIObserver -->
<method name="observe">
<parameter name="aSubject"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body>
<![CDATA[
if (aTopic != "browser-search-engine-modified")
return;
switch (aData) {
case "engine-added":
case "engine-removed":
case "engine-changed":
this.updateSearchEngines();
break;
case "engine-current":
// Not relevant
break;
}
]]>
</body>
</method>
<!-- Interface for updating various components of the popup. -->
<method name="updateResults">
<body>
<![CDATA[
if (!this._isGridBound(this._results))
return;
if (!this.input)
return;
let controller = this.input.controller;
let lastMatch = this._matchCount - 1;
let iterCount = Math.max(this._results.itemCount, this._matchCount);
// Swap out existing items for new search hit results
for (let i = 0; i < iterCount; i++) {
if (i > lastMatch) {
let lastItem = this._results.itemCount - 1;
this._results.removeItemAt(lastItem, true);
continue;
}
let value = controller.getValueAt(i);
let label = controller.getCommentAt(i) || value;
let iconURI = controller.getImageAt(i);
let item = this._results.getItemAtIndex(i);
if (item == null) {
item = this._results.appendItem(label, value, true);
item.setAttribute("autocomplete", "true");
} else {
item.setAttribute("label", label);
item.setAttribute("value", value);
}
item.setAttribute("iconURI", iconURI);
}
this._results.arrangeItems();
]]>
</body>
</method>
<method name="updateSearchEngines">
<body><![CDATA[
Services.search.init(this._onSearchServiceInit.bind(this));
]]></body>
</method>
<method name="_onSearchServiceInit">
<body>
<![CDATA[
if (!this._isGridBound(this._searches))
return;
this._engines = Services.search.getVisibleEngines();
while (this._searches.itemCount > 0)
this._searches.removeItemAt(0, true);
this._engines.forEach(function (anEngine) {
let item = this._searches.appendItem(anEngine.name, anEngine.name, true);
item.setAttribute("autocomplete", "true");
item.setAttribute("search", "true");
let iconURI = anEngine.iconURI ? anEngine.iconURI.spec : "";
item.setAttribute("iconURI", iconURI);
}.bind(this));
this._searches.arrangeItems();
]]>
</body>
</method>
<method name="updateSearchEngineSubtitles">
<body>
<![CDATA[
if (!this._isGridBound(this._searches))
return;
let searchString = this.input.controller.searchString;
let label = Strings.browser.formatStringFromName("opensearch.search", [searchString], 1);
for (let i = 0, len = this._searches.itemCount; i < len; i++) {
let item = this._searches.getItemAtIndex(i);
item.setAttribute("label", label);
item.refresh && item.refresh();
}
]]>
</body>
</method>
<!-- Interface for handling actions across grids -->
<method name="handleCompletion">
<body>
<![CDATA[
if (this._grid == this._results) {
this.input.controller.handleEnter(false);
return;
}
if (this._grid == this._searches) {
let engine = this._engines[this._grid.selectedIndex];
BrowserUI.doOpenSearch(engine.name);
this.closePopup();
return;
}
]]>
</body>
</method>
<method name="clearSelection">
<body>
<![CDATA[
if (this._isGridBound(this._results))
this._results.clearSelection();
if (this._isGridBound(this._searches))
this._searches.clearSelection();
]]>
</body>
</method>
<!-- Helpers -->
<field name="_engines">[]</field>
<field name="_handleOnSelect">true</field>
<field name="_grid">null</field>
<property name="_results" readonly="true" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'results');"/>
<property name="_searches" readonly="true" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'searches');"/>
<property name="_otherGrid" readonly="true">
<getter>
<![CDATA[
return (this._grid == this._results) ? this._searches : this._results;
]]>
</getter>
</property>
<method name="_isGridBound">
<parameter name="aGrid"/>
<body>
<![CDATA[
return aGrid && aGrid.itemCount != undefined;
]]>
</body>
</method>
<method name="_fire">
<parameter name="aName"/>
<body>
<![CDATA[
let event = document.createEvent("Events");
event.initEvent(aName, true, false);
this.dispatchEvent(event);
]]>
</body>
</method>
</implementation>
<handlers>
<handler event="select">
<![CDATA[
let grid = event.originalTarget;
if (grid != this._grid) {
if (this._grid.clearSelection)
this._grid.clearSelection();
this._grid = grid;
}
if (this._handleOnSelect && this._results.selectedItem) {
BrowserUI.goToURI(
this._results.selectedItem.getAttribute("value"));
this.closePopup();
} else if (this._handleOnSelect) {
this.handleCompletion();
}
]]>
</handler>
<handler event="contentgenerated">
<![CDATA[
let grid = event.originalTarget;
if (grid == this._searches)
this.updateSearchEngines();
]]>
</handler>
</handlers>
</binding>
</bindings>

View File

@ -86,12 +86,20 @@
<method name="handleItemClick">
<parameter name="aItem"/>
<parameter name="aEvent"/>
<body>
<![CDATA[
if(!this.isBound)
return;
if (this.controller)
this.controller.handleItemClick(aItem);
if ("single" == this.getAttribute("seltype")) {
// we'll republish this as a selectionchange event on the grid
aEvent.stopPropagation();
this.selectItem(aItem);
}
if (this.controller && this.controller.handleItemClick)
this.controller.handleItemClick(aItem, aEvent);
]]>
</body>
</method>

View File

@ -0,0 +1,838 @@
<?xml version="1.0" encoding="Windows-1252" ?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE bindings [
<!ENTITY % browserDTD SYSTEM "chrome://browser/locale/browser.dtd">
%browserDTD;
]>
<bindings
xmlns="http://www.mozilla.org/xbl"
xmlns:xbl="http://www.mozilla.org/xbl"
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
<binding id="urlbar" extends="chrome://global/content/bindings/autocomplete.xml#autocomplete">
<implementation implements="nsIObserver">
<constructor>
<![CDATA[
this._mayFormat = Services.prefs.getBoolPref("browser.urlbar.formatting.enabled");
this._mayTrimURLs = Services.prefs.getBoolPref("browser.urlbar.trimURLs");
this._maySelectAll = Services.prefs.getBoolPref("browser.urlbar.doubleClickSelectsAll");
Services.prefs.addObserver("browser.urlbar.formatting.enabled", this, false);
Services.prefs.addObserver("browser.urlbar.trimURLs", this, false);
Services.prefs.addObserver("browser.urlbar.doubleClickSelectsAll", this, false);
this.inputField.controllers.insertControllerAt(0, this._copyCutValueController);
this.minResultsForPopup = 0;
this.popup._input = this;
]]>
</constructor>
<destructor>
<![CDATA[
Services.prefs.removeObserver("browser.urlbar.formatting.enabled", this);
Services.prefs.removeObserver("browser.urlbar.trimURLs", this);
Services.prefs.removeObserver("browser.urlbar.doubleClickSelectsAll", this);
]]>
</destructor>
<field name="_mayFormat"/>
<field name="_mayTrimURLs"/>
<field name="_maySelectAll"/>
<field name="_lastKnownGoodURL"/>
<method name="openPopup">
<body>
<![CDATA[
this.popup.openAutocompletePopup(this, null);
]]>
</body>
</method>
<method name="closePopup">
<body>
<![CDATA[
this.popup.closePopup(this, null);
]]>
</body>
</method>
<!-- URI Display: Domain Highlighting -->
<method name="_clearFormatting">
<body>
<![CDATA[
if (!this._mayFormat)
return;
let controller = this.editor.selectionController;
let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
selection.removeAllRanges();
]]>
</body>
</method>
<method name="formatValue">
<body>
<![CDATA[
if (!this._mayFormat || this.isEditing)
return;
let controller = this.editor.selectionController;
let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
selection.removeAllRanges();
let textNode = this.editor.rootElement.firstChild;
let value = textNode.textContent;
let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/);
if (protocol &&
["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1)
return;
let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
if (!matchedURL)
return;
let [, preDomain, domain] = matchedURL;
let baseDomain = domain;
let subDomain = "";
// getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159)
if (domain[0] != "[") {
try {
baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
if (!domain.endsWith(baseDomain)) {
// getBaseDomainFromHost converts its resultant to ACE.
let IDNService = Cc["@mozilla.org/network/idn-service;1"]
.getService(Ci.nsIIDNService);
baseDomain = IDNService.convertACEtoUTF8(baseDomain);
}
} catch (e) {}
}
if (baseDomain != domain) {
subDomain = domain.slice(0, -baseDomain.length);
}
let rangeLength = preDomain.length + subDomain.length;
if (rangeLength) {
let range = document.createRange();
range.setStart(textNode, 0);
range.setEnd(textNode, rangeLength);
selection.addRange(range);
}
let startRest = preDomain.length + domain.length;
if (startRest < value.length) {
let range = document.createRange();
range.setStart(textNode, startRest);
range.setEnd(textNode, value.length);
selection.addRange(range);
}
]]>
</body>
</method>
<!-- URI Display: Scheme and Trailing Slash Triming -->
<method name="_trimURL">
<parameter name="aURL"/>
<body>
<![CDATA[
// This function must not modify the given URL such that calling
// nsIURIFixup::createFixupURI with the rfdesult will produce a different URI.
return aURL /* remove single trailing slash for http/https/ftp URLs */
.replace(/^((?:http|https|ftp):\/\/[^/]+)\/$/, "$1")
/* remove http:// unless the host starts with "ftp\d*\." or contains "@" */
.replace(/^http:\/\/((?!ftp\d*\.)[^\/@]+(?:\/|$))/, "$1");
]]>
</body>
</method>
<method name="_getSelectedValueForClipboard">
<body>
<![CDATA[
// Grab the actual input field's value, not our value, which could include moz-action:
let inputVal = this.inputField.value;
let selectedVal = inputVal.substring(this.selectionStart, this.electionEnd);
// If the selection doesn't start at the beginning or doesn't span the full domain or
// the URL bar is modified, nothing else to do here.
if (this.selectionStart > 0 || this.valueIsTyped)
return selectedVal;
// The selection doesn't span the full domain if it doesn't contain a slash and is
// followed by some character other than a slash.
if (!selectedVal.contains("/")) {
let remainder = inputVal.replace(selectedVal, "");
if (remainder != "" && remainder[0] != "/")
return selectedVal;
}
let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
let uri;
try {
uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_USE_UTF8);
} catch (e) {}
if (!uri)
return selectedVal;
// Only copy exposable URIs
try {
uri = uriFixup.createExposableURI(uri);
} catch (ex) {}
// If the entire URL is selected, just use the actual loaded URI.
if (inputVal == selectedVal) {
// ... but only if isn't a javascript: or data: URI, since those
// are hard to read when encoded
if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) {
// Parentheses are known to confuse third-party applications (bug 458565).
selectedVal = uri.spec.replace(/[()]/g, function (c) escape(c));
}
return selectedVal;
}
// Just the beginning of the URL is selected, check for a trimmed value
let spec = uri.spec;
let trimmedSpec = this._trimURL(spec);
if (spec != trimmedSpec) {
// Prepend the portion that trimURL removed from the beginning.
// This assumes trimURL will only truncate the URL at
// the beginning or end (or both).
let trimmedSegments = spec.split(trimmedSpec);
selectedVal = trimmedSegments[0] + selectedVal;
}
return selectedVal;
]]>
</body>
</method>
<field name="_copyCutValueController">
<![CDATA[
({
urlbar: this,
doCommand: function(aCommand) {
let urlbar = this.urlbar;
let val = urlbar._getSelectedValueForClipboard();
if (!val)
return;
if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) {
let start = urlbar.selectionStart;
let end = urlbar.selectionEnd;
urlbar.inputField.value = urlbar.inputField.value.substring(0, start) +
urlbar.inputField.value.substring(end);
urlbar.selectionStart = urlbar.selectionEnd = start;
}
Cc["@mozilla.org/widget/clipboardhelper;1"]
.getService(Ci.nsIClipboardHelper)
.copyString(val, document);
},
supportsCommand: function(aCommand) {
switch (aCommand) {
case "cmd_copy":
case "cmd_cut":
return true;
}
return false;
},
isCommandEnabled: function(aCommand) {
let urlbar = this.urlbar;
return this.supportsCommand(aCommand) &&
(aCommand != "cmd_cut" || !urlbar.readOnly) &&
urlbar.selectionStart < urlbar.selectionEnd;
},
onEvent: function(aEventName) {}
})
]]>
</field>
<method name="trimValue">
<parameter name="aURL"/>
<body>
<![CDATA[
return (this._mayTrimURLs) ? this._trimURL(aURL) : aURL;
]]>
</body>
</method>
<!-- URI Editing -->
<property name="isEditing" readonly="true">
<getter>
<![CDATA[
return Elements.urlbarState.getAttribute("mode") == "edit";
]]>
</getter>
</property>
<method name="beginEditing">
<parameter name="aShouldDismiss"/>
<body>
<![CDATA[
if (this.isEditing)
return;
Elements.urlbarState.setAttribute("mode", "edit");
this._lastKnownGoodURL = this.value;
if (!this.focused)
this.focus();
this._clearFormatting();
this.select();
if (aShouldDismiss)
ContextUI.dismissTabs();
]]>
</body>
</method>
<method name="endEditing">
<parameter name="aShouldRevert"/>
<body>
<![CDATA[
if (!this.isEditing)
return;
Elements.urlbarState.setAttribute("mode", "view");
this.closePopup();
this.formatValue();
if (this.focused)
this.blur();
if (aShouldRevert)
this.value = this._lastKnownGoodURL;
]]>
</body>
</method>
<!-- URI Submission -->
<method name="_canonizeURL">
<parameter name="aURL"/>
<parameter name="aTriggeringEvent"/>
<body>
<![CDATA[
if (!aURL)
return "";
// Only add the suffix when the URL bar value isn't already "URL-like",
// and only if we get a keyboard event, to match user expectations.
if (/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(aURL)) {
let accel = aTriggeringEvent.ctrlKey;
let shift = aTriggeringEvent.shiftKey;
let suffix = "";
switch (true) {
case (accel && shift):
suffix = ".org/";
break;
case (shift):
suffix = ".net/";
break;
case (accel):
try {
suffix = gPrefService.getCharPref("browser.fixup.alternate.suffix");
if (suffix.charAt(suffix.length - 1) != "/")
suffix += "/";
} catch(e) {
suffix = ".com/";
}
break;
}
if (suffix) {
// trim leading/trailing spaces (bug 233205)
aURL = aURL.trim();
// Tack www. and suffix on. If user has appended directories, insert
// suffix before them (bug 279035). Be careful not to get two slashes.
let firstSlash = aURL.indexOf("/");
if (firstSlash >= 0) {
aURL = aURL.substring(0, firstSlash) + suffix + aURL.substring(firstSlash + 1);
} else {
aURL = aURL + suffix;
}
aURL = "http://www." + aURL;
}
}
return aURL;
]]>
</body>
</method>
<method name="submitURL">
<parameter name="aEvent"/>
<body>
<![CDATA[
// If the address was typed in by a user, tidy it up
if (aEvent instanceof KeyEvent)
this.value = this._canonizeURL(this.value, aEvent);
this.endEditing();
BrowserUI.goToURI(this.value);
return true;
]]>
</body>
</method>
<method name="submitSearch">
<parameter name="anEngineName"/>
<body>
<![CDATA[
this.endEditing();
BrowserUI.doOpenSearch(anEngineName);
return true;
]]>
</body>
</method>
<!-- nsIObserver -->
<method name="observe">
<parameter name="aSubject"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body>
<![CDATA[
if (aTopic != "nsPref:changed")
return;
switch (aData) {
case "browser.urlbar.formatting.enabled":
this._mayFormat = Services.prefs.getBoolPref(aData);
break;
case "browser.urlbar.trimURLs":
this._mayTrimURLs = Services.prefs.getBoolPref(aData);
break;
case "browser.urlbar.doubleClickSelectsAll":
this._maySelectAll = Services.prefs.getBoolPref(aData);
break;
}
]]>
</body>
</method>
</implementation>
<handlers>
<!-- Entering editing mode -->
<handler event="input" phase="capturing">
<![CDATA[
// Ensures that paste-and-go actually brings the URL bar into editing mode
// and displays the half-height autocomplete popup.
this.beginEditing();
this.openPopup();
]]>
</handler>
<handler event="click" phase="capturing">
<![CDATA[
this.beginEditing(true);
]]>
</handler>
<!-- Editing mode behaviors -->
<handler event="dblclick" phase="capturing">
<![CDATA[
if (this._maySelectAll) this.select();
]]>
</handler>
<handler event="contextmenu" phase="capturing">
<![CDATA[
let box = this.inputField.parentNode;
box.showContextMenu(this, event, true);
]]>
</handler>
<!-- Leaving editing mode -->
<handler event="blur" phase="capturing">
<![CDATA[
this.endEditing();
]]>
</handler>
<handler event="keypress" phase="capturing" keycode="VK_RETURN">
<![CDATA[
if (this.popup.submitSelected())
return;
if (this.submitURL(event))
return;
]]>
</handler>
</handlers>
</binding>
<binding id="urlbar-autocomplete">
<content orient="horizontal">
<xul:vbox id="results-vbox" flex="1">
<xul:label class="meta-section-title" value="&autocompleteResultsHeader.label;"/>
<richgrid id="results-richgrid" rows="3" deferlayout="true" anonid="results" seltype="single" flex="1"/>
</xul:vbox>
<xul:vbox id="searches-vbox" flex="1">
<xul:label class="meta-section-title" value="&autocompleteSearchesHeader.label;"/>
<richgrid id="searches-richgrid" rows="3" deferlayout="true" anonid="searches" seltype="single" flex="1"/>
</xul:vbox>
</content>
<implementation implements="nsIAutoCompletePopup, nsIObserver">
<constructor>
<![CDATA[
this.hidden = true;
Services.obs.addObserver(this, "browser-search-engine-modified", false);
this._results.controller = this;
this._searches.controller = this;
]]>
</constructor>
<destructor>
<![CDATA[
Services.obs.removeObserver(this, "browser-search-engine-modified");
]]>
</destructor>
<!-- nsIAutocompleteInput -->
<field name="_input">null</field>
<property name="input" readonly="true" onget="return this._input;"/>
<property name="matchCount" readonly="true" onget="return this.input.controller.matchCount;"/>
<property name="popupOpen" readonly="true" onget="return !this.hidden"/>
<property name="overrideValue" readonly="true" onget="return null;"/>
<property name="selectedItem">
<getter>
<![CDATA[
return this._isGridBound(this._results) ? this._results.selectedItem : null;
]]>
</getter>
<setter>
<![CDATA[
return this._isGridBound(this._results) ? this._results.selectedItem : null;
]]>
</setter>
</property>
<property name="selectedIndex">
<getter>
<![CDATA[
return this._isGridBound(this._results) ? this._results.selectedIndex : -1;
]]>
</getter>
<setter>
<![CDATA[
return this._isGridBound(this._results) ? this._results.selectedIndex : -1;
]]>
</setter>
</property>
<method name="openAutocompletePopup">
<parameter name="aInput"/>
<parameter name="aElement"/>
<body>
<![CDATA[
if (this.popupOpen)
return;
ContextUI.dismissContextAppbar();
this._input = aInput;
this._grid = this._results;
this.clearSelection();
this.invalidate();
this._results.arrangeItemsNow();
this._searches.arrangeItemsNow();
this.hidden = false;
]]>
</body>
</method>
<method name="closePopup">
<body>
<![CDATA[
if (!this.popupOpen)
return;
this.input.controller.stopSearch();
this.hidden = true;
]]>
</body>
</method>
<!-- Updating grid content -->
<field name="_grid">null</field>
<field name="_results" readonly="true">document.getAnonymousElementByAttribute(this, 'anonid', 'results');</field>
<field name="_searches" readonly="true">document.getAnonymousElementByAttribute(this, 'anonid', 'searches');</field>
<property name="_otherGrid" readonly="true">
<getter>
<![CDATA[
if (this._grid == null)
return null;
return (this._grid == this._results) ? this._searches : this._results;
]]>
</getter>
</property>
<method name="_isGridBound">
<parameter name="aGrid"/>
<body>
<![CDATA[
return aGrid && aGrid.isBound;
]]>
</body>
</method>
<method name="invalidate">
<body>
<![CDATA[
if (!this.popupOpen)
return;
this.updateResults();
this.updateSearchEngineSubtitles();
]]>
</body>
</method>
<!-- Updating grid content: results -->
<method name="updateResults">
<body>
<![CDATA[
if (!this._isGridBound(this._results))
return;
if (!this.input)
return;
let controller = this.input.controller;
let lastMatch = this.matchCount - 1;
let iterCount = Math.max(this._results.itemCount, this.matchCount);
// Swap out existing items for new search hit results
for (let i = 0; i < iterCount; i++) {
if (i > lastMatch) {
let lastItem = this._results.itemCount - 1;
this._results.removeItemAt(lastItem, true);
continue;
}
let value = controller.getValueAt(i);
let label = controller.getCommentAt(i) || value;
let iconURI = controller.getImageAt(i);
let item = this._results.getItemAtIndex(i);
if (item == null) {
item = this._results.appendItem(label, value, true);
item.setAttribute("autocomplete", "true");
} else {
item.setAttribute("label", label);
item.setAttribute("value", value);
}
item.setAttribute("iconURI", iconURI);
}
this._results.arrangeItems();
]]>
</body>
</method>
<!-- Updating grid content: search engines -->
<field name="_engines">[]</field>
<method name="_initSearchEngines">
<body>
<![CDATA[
Services.search.init(this.updateSearchEngineGrid.bind(this));
]]>
</body>
</method>
<method name="updateSearchEngineGrid">
<body>
<![CDATA[
if (!this._isGridBound(this._searches))
return;
this._engines = Services.search.getVisibleEngines();
while (this._searches.itemCount > 0)
this._searches.removeItemAt(0, true);
this._engines.forEach(function (anEngine) {
let item = this._searches.appendItem(anEngine.name, anEngine.name, true);
item.setAttribute("autocomplete", "true");
item.setAttribute("search", "true");
let iconURI = anEngine.iconURI ? anEngine.iconURI.spec : "";
item.setAttribute("iconURI", iconURI);
}.bind(this));
this._searches.arrangeItems();
]]>
</body>
</method>
<method name="updateSearchEngineSubtitles">
<body>
<![CDATA[
if (!this._isGridBound(this._searches))
return;
let searchString = this.input.controller.searchString;
let label = Strings.browser.formatStringFromName("opensearch.search", [searchString], 1);
for (let i = 0, len = this._searches.itemCount; i < len; i++) {
let item = this._searches.getItemAtIndex(i);
item.setAttribute("label", label);
item.refresh && item.refresh();
}
]]>
</body>
</method>
<!-- Selecting results -->
<method name="selectBy">
<parameter name="aReverse"/>
<parameter name="aPage"/>
<body>
<![CDATA[
if (!this._isGridBound(this._results) ||
!this._isGridBound(this._searches))
return;
// Move between grids if we're at the edge of one.
if ((this._grid.isSelectionAtEnd && !aReverse) ||
(this._grid.isSelectionAtStart && aReverse)) {
let index = !aReverse ? 0 : this._otherGrid.itemCount - 1;
this._otherGrid.selectedIndex = index;
} else {
this._grid.offsetSelection(aReverse ? -1 : 1);
}
]]>
</body>
</method>
<method name="clearSelection">
<body>
<![CDATA[
if (this._isGridBound(this._results))
this._results.clearSelection();
if (this._isGridBound(this._searches))
this._searches.clearSelection();
]]>
</body>
</method>
<!-- Submitting selected results -->
<method name="submitSelected">
<body>
<![CDATA[
if (this._isGridBound(this._results) &&
this._results.selectedIndex >= 0) {
let url = this.input.controller.getValueAt(this._results.selectedIndex);
this.input.value = url;
return this.input.submitURL();
}
if (this._isGridBound(this._searches) &&
this._searches.selectedIndex >= 0) {
let engine = this._engines[this._searches.selectedIndex];
return this.input.submitSearch(engine.name);
}
return false;
]]>
</body>
</method>
<method name="handleItemClick">
<parameter name="aItem"/>
<parameter name="aEvent"/>
<body>
<![CDATA[
this.submitSelected();
]]>
</body>
</method>
<!-- nsIObserver -->
<method name="observe">
<parameter name="aSubject"/>
<parameter name="aTopic"/>
<parameter name="aData"/>
<body>
<![CDATA[
switch (aTopic) {
case "browser-search-engine-modified":
this.updateSearchEngineGrid();
break;
}
]]>
</body>
</method>
</implementation>
<handlers>
<handler event="contentgenerated">
<![CDATA[
let grid = event.originalTarget;
if (grid == this._searches)
this._initSearchEngines();
if (grid == this._results)
this.updateResults();
]]>
</handler>
<handler event="select">
<![CDATA[
let grid = event.originalTarget;
// If a selection was made on a different grid,
// remove selection from the current grid.
if (grid != this._grid) {
this._grid.clearSelection();
this._grid = this._otherGrid;
}
]]>
</handler>
</handlers>
</binding>
</bindings>

View File

@ -2,6 +2,7 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
Cu.import("resource://gre/modules/PageThumbs.jsm");
Cu.import("resource://gre/modules/devtools/dbg-server.jsm")
@ -22,6 +23,13 @@ const kStartOverlayURI = "about:start";
const debugServerStateChanged = "devtools.debugger.remote-enabled";
const debugServerPortChanged = "devtools.debugger.remote-port";
// delay when showing the tab bar briefly after a new (empty) tab opens
const kNewTabAnimationDelayMsec = 1000;
// delay when showing the tab bar after opening a link on a new tab
const kOpenInNewTabAnimationDelayMsec = 3000;
// delay before closing tab bar after selecting another tab
const kSelectTabAnimationDelayMsec = 500;
/**
* Cache of commonly used elements.
*/
@ -74,9 +82,10 @@ var BrowserUI = {
get _back() { return document.getElementById("cmd_back"); },
get _forward() { return document.getElementById("cmd_forward"); },
lastKnownGoodURL: "", //used when the user wants to escape unfinished url entry
init: function() {
lastKnownGoodURL: "", // used when the user wants to escape unfinished url entry
ready: false, // used for tests to determine when delayed initialization is done
init: function() {
// start the debugger now so we can use it on the startup code as well
if (Services.prefs.getBoolPref(debugServerStateChanged)) {
this.runDebugServer();
@ -100,11 +109,7 @@ var BrowserUI = {
window.addEventListener("MozImprecisePointer", this, true);
Services.prefs.addObserver("browser.cache.disk_cache_ssl", this, false);
Services.prefs.addObserver("browser.urlbar.formatting.enabled", this, false);
Services.prefs.addObserver("browser.urlbar.trimURLs", this, false);
Services.obs.addObserver(this, "metro_viewstate_changed", false);
this._edit.inputField.controllers.insertControllerAt(0, this._copyCutURIController);
// Init core UI modules
ContextUI.init();
@ -120,16 +125,17 @@ var BrowserUI = {
// We can delay some initialization until after startup. We wait until
// the first page is shown, then dispatch a UIReadyDelayed event.
messageManager.addMessageListener("pageshow", function() {
messageManager.addMessageListener("pageshow", function onPageShow() {
if (getBrowser().currentURI.spec == "about:blank")
return;
messageManager.removeMessageListener("pageshow", arguments.callee, true);
messageManager.removeMessageListener("pageshow", onPageShow);
setTimeout(function() {
let event = document.createEvent("Events");
event.initEvent("UIReadyDelayed", true, false);
window.dispatchEvent(event);
BrowserUI.ready = true;
}, 0);
});
@ -139,9 +145,9 @@ var BrowserUI = {
});
// Delay the panel UI and Sync initialization
window.addEventListener("UIReadyDelayed", function(aEvent) {
window.addEventListener("UIReadyDelayed", function delayedInit(aEvent) {
Util.dumpLn("* delay load started...");
window.removeEventListener(aEvent.type, arguments.callee, false);
window.removeEventListener("UIReadyDelayed", delayedInit, false);
// Login Manager and Form History initialization
Cc["@mozilla.org/login-manager;1"].getService(Ci.nsILoginManager);
@ -291,10 +297,22 @@ var BrowserUI = {
* Navigation
*/
/* Updates the overall state of the toolbar, but not the URL bar. */
update: function(aState) {
let uri = this.getDisplayURI(Browser.selectedBrowser);
StartUI.update(uri);
this._updateButtons();
this._updateToolbar();
},
/* Updates the URL bar. */
updateURI: function(aOptions) {
let uri = this.getDisplayURI(Browser.selectedBrowser);
let cleanURI = Util.isURLEmpty(uri) ? "" : uri;
this._edit.value = cleanURI;
},
getDisplayURI: function(browser) {
let uri = browser.currentURI;
let spec = uri.spec;
@ -313,64 +331,18 @@ var BrowserUI = {
return spec;
},
/**
* Some prefs that have consequences in both Metro and Desktop such as
* app-update prefs, are automatically pulled from Desktop here.
*/
_pullDesktopControlledPrefs: function() {
function pullDesktopControlledPrefType(prefType, prefFunc) {
try {
registry.create(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
"Software\\Mozilla\\Firefox\\Metro\\Prefs\\" + prefType,
Ci.nsIWindowsRegKey.ACCESS_ALL);
for (let i = 0; i < registry.valueCount; i++) {
let prefName = registry.getValueName(i);
let prefValue = registry.readStringValue(prefName);
if (prefType == Ci.nsIPrefBranch.PREF_BOOL) {
prefValue = prefValue == "true";
}
Services.prefs[prefFunc](prefName, prefValue);
}
} catch (ex) {
Util.dumpLn("Could not pull for prefType " + prefType + ": " + ex);
} finally {
registry.close();
}
}
let registry = Cc["@mozilla.org/windows-registry-key;1"].
createInstance(Ci.nsIWindowsRegKey);
pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_INT, "setIntPref");
pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_BOOL, "setBoolPref");
pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_STRING, "setCharPref");
},
/* Set the location to the current content */
updateURI: function(aOptions) {
aOptions = aOptions || {};
let uri = this.getDisplayURI(Browser.selectedBrowser);
let cleanURI = Util.isURLEmpty(uri) ? "" : uri;
this._setURI(cleanURI);
if ("captionOnly" in aOptions && aOptions.captionOnly)
return;
StartUI.update(uri);
this._updateButtons();
this._updateToolbar();
},
goToURI: function(aURI) {
aURI = aURI || this._edit.value;
if (!aURI)
return;
this._edit.value = aURI;
// Make sure we're online before attempting to load
Util.forceOnline();
BrowserUI.showContent(aURI);
content.focus();
this._setURI(aURI);
Browser.selectedBrowser.focus();
Task.spawn(function() {
let postData = {};
@ -387,75 +359,27 @@ var BrowserUI = {
});
},
handleUrlbarEnter: function handleUrlbarEnter(aEvent) {
let url = this._edit.value;
if (aEvent instanceof KeyEvent)
url = this._canonizeURL(url, aEvent);
this.goToURI(url);
},
_canonizeURL: function _canonizeURL(aUrl, aTriggeringEvent) {
if (!aUrl)
return "";
// Only add the suffix when the URL bar value isn't already "URL-like",
// and only if we get a keyboard event, to match user expectations.
if (/^\s*[^.:\/\s]+(?:\/.*|\s*)$/i.test(aUrl)) {
let accel = aTriggeringEvent.ctrlKey;
let shift = aTriggeringEvent.shiftKey;
let suffix = "";
switch (true) {
case (accel && shift):
suffix = ".org/";
break;
case (shift):
suffix = ".net/";
break;
case (accel):
try {
suffix = gPrefService.getCharPref("browser.fixup.alternate.suffix");
if (suffix.charAt(suffix.length - 1) != "/")
suffix += "/";
} catch(e) {
suffix = ".com/";
}
break;
}
if (suffix) {
// trim leading/trailing spaces (bug 233205)
aUrl = aUrl.trim();
// Tack www. and suffix on. If user has appended directories, insert
// suffix before them (bug 279035). Be careful not to get two slashes.
let firstSlash = aUrl.indexOf("/");
if (firstSlash >= 0) {
aUrl = aUrl.substring(0, firstSlash) + suffix + aUrl.substring(firstSlash + 1);
} else {
aUrl = aUrl + suffix;
}
aUrl = "http://www." + aUrl;
}
}
return aUrl;
},
doOpenSearch: function doOpenSearch(aName) {
// save the current value of the urlbar
let searchValue = this._edit.value;
let engine = Services.search.getEngineByName(aName);
let submission = engine.getSubmission(searchValue, null);
this._edit.value = submission.uri.spec;
// Make sure we're online before attempting to load
Util.forceOnline();
BrowserUI.showContent();
Browser.selectedBrowser.focus();
let engine = Services.search.getEngineByName(aName);
let submission = engine.getSubmission(searchValue, null);
Browser.loadURI(submission.uri.spec, { postData: submission.postData });
Task.spawn(function () {
Browser.loadURI(submission.uri.spec, { postData: submission.postData });
// loadURI may open a new tab, so get the selectedBrowser afterward.
Browser.selectedBrowser.userTypedValue = submission.uri.spec;
this._titleChanged(Browser.selectedBrowser);
// loadURI may open a new tab, so get the selectedBrowser afterward.
Browser.selectedBrowser.userTypedValue = submission.uri.spec;
BrowserUI._titleChanged(Browser.selectedBrowser);
});
},
/*********************************
@ -465,21 +389,9 @@ var BrowserUI = {
newTab: function newTab(aURI, aOwner) {
aURI = aURI || kStartOverlayURI;
let tab = Browser.addTab(aURI, true, aOwner);
ContextUI.peekTabs();
return tab;
},
newOrSelectTab: function newOrSelectTab(aURI, aOwner) {
let tabs = Browser.tabs;
for (let i = 0; i < tabs.length; i++) {
if (tabs[i].browser.currentURI.spec == aURI) {
Browser.selectedTab = tabs[i];
return;
}
}
this.newTab(aURI, aOwner);
},
setOnTabAnimationEnd: function setOnTabAnimationEnd(aCallback) {
Elements.tabs.addEventListener("animationend", function onAnimationEnd() {
Elements.tabs.removeEventListener("animationend", onAnimationEnd);
@ -502,9 +414,9 @@ var BrowserUI = {
}
this.setOnTabAnimationEnd(function() {
Browser.closeTab(tabToClose, { forceClose: true } );
if (wasCollapsed)
ContextUI.dismissTabsWithDelay(kNewTabAnimationDelayMsec);
Browser.closeTab(tabToClose, { forceClose: true } );
if (wasCollapsed)
ContextUI.dismissTabsWithDelay(kNewTabAnimationDelayMsec);
});
},
@ -546,7 +458,7 @@ var BrowserUI = {
selectTabAndDismiss: function selectTabAndDismiss(aTab) {
this.selectTab(aTab);
ContextUI.dismissTabs();
ContextUI.dismissTabsWithDelay(kSelectTabAnimationDelayMsec);
},
selectTabAtIndex: function selectTabAtIndex(aIndex) {
@ -621,12 +533,6 @@ var BrowserUI = {
case "browser.cache.disk_cache_ssl":
this._sslDiskCacheEnabled = Services.prefs.getBoolPref(aData);
break;
case "browser.urlbar.formatting.enabled":
this._formattingEnabled = Services.prefs.getBoolPref(aData);
break;
case "browser.urlbar.trimURLs":
this._mayTrimURLs = Services.prefs.getBoolPref(aData);
break;
case debugServerStateChanged:
if (Services.prefs.getBoolPref(aData)) {
this.runDebugServer();
@ -641,7 +547,7 @@ var BrowserUI = {
break;
case "metro_viewstate_changed":
this._adjustDOMforViewState();
let autocomplete = document.getElementById("start-autocomplete");
let autocomplete = document.getElementById("urlbar-autocomplete");
if (aData == "snapped") {
FlyoutPanelsUI.hide();
// Order matters (need grids to get dimensions, etc), now
@ -660,6 +566,37 @@ var BrowserUI = {
* Internal utils
*/
/**
* Some prefs that have consequences in both Metro and Desktop such as
* app-update prefs, are automatically pulled from Desktop here.
*/
_pullDesktopControlledPrefs: function() {
function pullDesktopControlledPrefType(prefType, prefFunc) {
try {
registry.create(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
"Software\\Mozilla\\Firefox\\Metro\\Prefs\\" + prefType,
Ci.nsIWindowsRegKey.ACCESS_ALL);
for (let i = 0; i < registry.valueCount; i++) {
let prefName = registry.getValueName(i);
let prefValue = registry.readStringValue(prefName);
if (prefType == Ci.nsIPrefBranch.PREF_BOOL) {
prefValue = prefValue == "true";
}
Services.prefs[prefFunc](prefName, prefValue);
}
} catch (ex) {
Util.dumpLn("Could not pull for prefType " + prefType + ": " + ex);
} finally {
registry.close();
}
}
let registry = Cc["@mozilla.org/windows-registry-key;1"].
createInstance(Ci.nsIWindowsRegKey);
pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_INT, "setIntPref");
pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_BOOL, "setBoolPref");
pullDesktopControlledPrefType(Ci.nsIPrefBranch.PREF_STRING, "setCharPref");
},
_adjustDOMforViewState: function() {
if (MetroUtils.immersive) {
let currViewState = "";
@ -727,204 +664,6 @@ var BrowserUI = {
Elements.urlbarState.setAttribute("mode", "view");
},
_trimURL: function _trimURL(aURL) {
// This function must not modify the given URL such that calling
// nsIURIFixup::createFixupURI with the result will produce a different URI.
return aURL /* remove single trailing slash for http/https/ftp URLs */
.replace(/^((?:http|https|ftp):\/\/[^/]+)\/$/, "$1")
/* remove http:// unless the host starts with "ftp\d*\." or contains "@" */
.replace(/^http:\/\/((?!ftp\d*\.)[^\/@]+(?:\/|$))/, "$1");
},
trimURL: function trimURL(aURL) {
return this.mayTrimURLs ? this._trimURL(aURL) : aURL;
},
_setURI: function _setURI(aURL) {
this._edit.value = aURL;
this.lastKnownGoodURL = aURL;
},
_getSelectedURIForClipboard: function _getSelectedURIForClipboard() {
// Grab the actual input field's value, not our value, which could include moz-action:
let inputVal = this._edit.inputField.value;
let selectedVal = inputVal.substring(this._edit.selectionStart, this._edit.electionEnd);
// If the selection doesn't start at the beginning or doesn't span the full domain or
// the URL bar is modified, nothing else to do here.
if (this._edit.selectionStart > 0 || this._edit.valueIsTyped)
return selectedVal;
// The selection doesn't span the full domain if it doesn't contain a slash and is
// followed by some character other than a slash.
if (!selectedVal.contains("/")) {
let remainder = inputVal.replace(selectedVal, "");
if (remainder != "" && remainder[0] != "/")
return selectedVal;
}
let uriFixup = Cc["@mozilla.org/docshell/urifixup;1"].getService(Ci.nsIURIFixup);
let uri;
try {
uri = uriFixup.createFixupURI(inputVal, Ci.nsIURIFixup.FIXUP_FLAG_USE_UTF8);
} catch (e) {}
if (!uri)
return selectedVal;
// Only copy exposable URIs
try {
uri = uriFixup.createExposableURI(uri);
} catch (ex) {}
// If the entire URL is selected, just use the actual loaded URI.
if (inputVal == selectedVal) {
// ... but only if isn't a javascript: or data: URI, since those
// are hard to read when encoded
if (!uri.schemeIs("javascript") && !uri.schemeIs("data")) {
// Parentheses are known to confuse third-party applications (bug 458565).
selectedVal = uri.spec.replace(/[()]/g, function (c) escape(c));
}
return selectedVal;
}
// Just the beginning of the URL is selected, check for a trimmed value
let spec = uri.spec;
let trimmedSpec = this.trimURL(spec);
if (spec != trimmedSpec) {
// Prepend the portion that trimURL removed from the beginning.
// This assumes trimURL will only truncate the URL at
// the beginning or end (or both).
let trimmedSegments = spec.split(trimmedSpec);
selectedVal = trimmedSegments[0] + selectedVal;
}
return selectedVal;
},
_copyCutURIController: {
doCommand: function(aCommand) {
let urlbar = BrowserUI._edit;
let val = BrowserUI._getSelectedURIForClipboard();
if (!val)
return;
if (aCommand == "cmd_cut" && this.isCommandEnabled(aCommand)) {
let start = urlbar.selectionStart;
let end = urlbar.selectionEnd;
urlbar.inputField.value = urlbar.inputField.value.substring(0, start) +
urlbar.inputField.value.substring(end);
urlbar.selectionStart = urlbar.selectionEnd = start;
}
Cc["@mozilla.org/widget/clipboardhelper;1"]
.getService(Ci.nsIClipboardHelper)
.copyString(val, document);
},
supportsCommand: function(aCommand) {
switch (aCommand) {
case "cmd_copy":
case "cmd_cut":
return true;
}
return false;
},
isCommandEnabled: function(aCommand) {
let urlbar = BrowserUI._edit;
return this.supportsCommand(aCommand) &&
(aCommand != "cmd_cut" || !urlbar.readOnly) &&
urlbar.selectionStart < urlbar.selectionEnd;
},
onEvent: function(aEventName) {}
},
_editURI: function _editURI(aShouldDismiss) {
this._edit.focus();
this._edit.select();
Elements.urlbarState.setAttribute("mode", "edit");
StartUI.show();
if (aShouldDismiss) {
ContextUI.dismissTabs();
}
},
formatURI: function formatURI() {
if (!this.formattingEnabled ||
Elements.urlbarState.getAttribute("mode") == "edit")
return;
let controller = this._edit.editor.selectionController;
let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
selection.removeAllRanges();
let textNode = this._edit.editor.rootElement.firstChild;
let value = textNode.textContent;
let protocol = value.match(/^[a-z\d.+\-]+:(?=[^\d])/);
if (protocol &&
["http:", "https:", "ftp:"].indexOf(protocol[0]) == -1)
return;
let matchedURL = value.match(/^((?:[a-z]+:\/\/)?(?:[^\/]+@)?)(.+?)(?::\d+)?(?:\/|$)/);
if (!matchedURL)
return;
let [, preDomain, domain] = matchedURL;
let baseDomain = domain;
let subDomain = "";
// getBaseDomainFromHost doesn't recognize IPv6 literals in brackets as IPs (bug 667159)
if (domain[0] != "[") {
try {
baseDomain = Services.eTLD.getBaseDomainFromHost(domain);
if (!domain.endsWith(baseDomain)) {
// getBaseDomainFromHost converts its resultant to ACE.
let IDNService = Cc["@mozilla.org/network/idn-service;1"]
.getService(Ci.nsIIDNService);
baseDomain = IDNService.convertACEtoUTF8(baseDomain);
}
} catch (e) {}
}
if (baseDomain != domain) {
subDomain = domain.slice(0, -baseDomain.length);
}
let rangeLength = preDomain.length + subDomain.length;
if (rangeLength) {
let range = document.createRange();
range.setStart(textNode, 0);
range.setEnd(textNode, rangeLength);
selection.addRange(range);
}
let startRest = preDomain.length + domain.length;
if (startRest < value.length) {
let range = document.createRange();
range.setStart(textNode, startRest);
range.setEnd(textNode, value.length);
selection.addRange(range);
}
},
_clearURIFormatting: function _clearURIFormatting() {
if (!this.formattingEnabled)
return;
let controller = this._edit.editor.selectionController;
let selection = controller.getSelection(controller.SELECTION_URLSECONDARY);
selection.removeAllRanges();
},
_urlbarBlurred: function _urlbarBlurred() {
let state = Elements.urlbarState;
if (state.getAttribute("mode") == "edit")
state.removeAttribute("mode");
this._updateToolbar();
this.formatURI();
},
_closeOrQuit: function _closeOrQuit() {
// Close active dialog, if we have one. If not then close the application.
if (!BrowserUI.isContentShowing()) {
@ -985,10 +724,7 @@ var BrowserUI = {
aEvent.preventDefault();
if (this._edit.popupOpen) {
this._edit.value = this.lastKnownGoodURL;
this._edit.closePopup();
StartUI.hide();
ContextUI.dismiss();
this._edit.endEditing(true);
return;
}
@ -1205,24 +941,6 @@ var BrowserUI = {
return this._sslDiskCacheEnabled;
},
_formattingEnabled: null,
get formattingEnabled() {
if (this._formattingEnabled === null) {
this._formattingEnabled = Services.prefs.getBoolPref("browser.urlbar.formatting.enabled");
}
return this._formattingEnabled;
},
_mayTrimURLs: null,
get mayTrimURLs() {
if (this._mayTrimURLs === null) {
this._mayTrimURLs = Services.prefs.getBoolPref("browser.urlbar.trimURLs");
}
return this._mayTrimURLs;
},
supportsCommand : function(cmd) {
var isSupported = false;
switch (cmd) {
@ -1302,7 +1020,7 @@ var BrowserUI = {
break;
case "cmd_openLocation":
ContextUI.displayNavbar();
this._editURI(true);
this._edit.beginEditing(true);
break;
case "cmd_addBookmark":
ContextUI.displayNavbar();
@ -1332,7 +1050,8 @@ var BrowserUI = {
break;
case "cmd_newTab":
this.newTab();
this._editURI(false);
this._edit.beginEditing(false);
ContextUI.peekTabs(kNewTabAnimationDelayMsec);
break;
case "cmd_closeTab":
this.closeTab();
@ -1372,9 +1091,8 @@ var BrowserUI = {
};
var StartUI = {
get isVisible() { return this.isStartPageVisible || this.isFiltering; },
get isVisible() { return this.isStartPageVisible; },
get isStartPageVisible() { return Elements.windowState.hasAttribute("startpage"); },
get isFiltering() { return Elements.windowState.hasAttribute("filtering"); },
get maxResultsPerSection() {
return Services.prefs.getIntPref("browser.display.startUI.maxresults");
@ -1389,8 +1107,6 @@ var StartUI = {
],
init: function init() {
Elements.startUI.addEventListener("autocompletestart", this, false);
Elements.startUI.addEventListener("autocompleteend", this, false);
Elements.startUI.addEventListener("contextmenu", this, false);
Elements.startUI.addEventListener("click", this, false);
Elements.startUI.addEventListener("MozMousePixelScroll", this, false);
@ -1429,24 +1145,6 @@ var StartUI = {
return true;
},
/** Show the autocomplete popup */
filter: function filter() {
if (this.isFiltering)
return;
BrowserUI._edit.openPopup();
Elements.windowState.setAttribute("filtering", "true");
},
/** Hide the autocomplete popup */
unfilter: function unfilter() {
if (!this.isFiltering)
return;
BrowserUI._edit.closePopup();
Elements.windowState.removeAttribute("filtering");
},
/** Hide the Firefox start page */
hide: function hide(aURI) {
aURI = aURI || Browser.selectedBrowser.currentURI.spec;
@ -1455,8 +1153,6 @@ var StartUI = {
Elements.contentShowing.removeAttribute("disabled");
Elements.windowState.removeAttribute("startpage");
this.unfilter();
return true;
},
@ -1488,12 +1184,6 @@ var StartUI = {
handleEvent: function handleEvent(aEvent) {
switch (aEvent.type) {
case "autocompletestart":
this.filter();
break;
case "autocompleteend":
this.unfilter();
break;
case "contextmenu":
let event = document.createEvent("Events");
event.initEvent("MozEdgeUICompleted", true, false);

View File

@ -95,11 +95,11 @@ setting[type="menulist"] {
}
#urlbar-edit {
-moz-binding: url("chrome://browser/content/bindings/autocomplete.xml#autocomplete");
-moz-binding: url("chrome://browser/content/bindings/urlbar.xml#urlbar");
}
#start-autocomplete {
-moz-binding: url("chrome://browser/content/bindings/autocomplete.xml#autocomplete-popup");
#urlbar-autocomplete {
-moz-binding: url("chrome://browser/content/bindings/urlbar.xml#urlbar-autocomplete");
}
richgrid {

View File

@ -574,6 +574,7 @@ var Browser = {
} else {
// Update all of our UI to reflect the new tab's location
BrowserUI.updateURI();
BrowserUI.update();
let event = document.createEvent("Events");
event.initEvent("TabSelect", true, false);
@ -1578,7 +1579,7 @@ Tab.prototype = {
// Ensure that history is initialized
history.QueryInterface(Ci.nsISHistoryInternal);
for (let i = 0, length = otherHistory.index; i <= length; i++)
history.addEntry(otherHistory.getEntryAtIndex(i, false), true);
},

View File

@ -179,7 +179,8 @@
<vbox id="page">
<vbox id="tray" class="tray-toolbar" observes="bcast_windowState" >
<!-- Tabs -->
<hbox id="tabs-container" observes="bcast_windowState">
<!-- onclick handler to work around bug 837242 -->
<hbox id="tabs-container" observes="bcast_windowState" onclick="void(0);">
<box id="tabs" flex="1"
observes="bcast_preciseInput"
onselect="BrowserUI.selectTabAndDismiss(this);"
@ -228,8 +229,6 @@
onclick="PanelUI.show('remotetabs-container');" inputProcessing="true"/>
</scrollbox>
</hbox>
<!-- Autocompletion interface -->
<box id="start-autocomplete" observes="bcast_windowState"/>
</hbox>
</vbox> <!-- end tray -->
@ -256,44 +255,48 @@
<hbox id="progress-control" />
</hbox>
<!-- Main Toolbar -->
<toolbar id="toolbar" observes="bcast_windowState" flex="1">
<observes element="bcast_windowState" attribute="*"/>
<observes element="bcast_urlbarState" attribute="*"/>
<vbox id="toolbar-autocomplete" flex="1">
<!-- Autocomplete -->
<scrollbox flex="1">
<hbox id="urlbar-autocomplete" observes="bcast_windowState"/>
</scrollbox>
<toolbarbutton id="back-button" class="appbar-primary" command="cmd_back"/>
<toolbarbutton id="forward-button" class="appbar-primary" command="cmd_forward"/>
<!-- Main Toolbar -->
<toolbar id="toolbar" observes="bcast_windowState" flex="1">
<observes element="bcast_windowState" attribute="*"/>
<observes element="bcast_urlbarState" attribute="*"/>
<hbox id="urlbar-container" flex="1" observes="bcast_urlbarState">
<hbox id="urlbar" flex="1">
<box id="identity-box" role="button">
<hbox id="identity-box-inner" align="center" mousethrough="always">
<image id="identity-icon"/>
</hbox>
</box>
<toolbarbutton id="back-button" class="appbar-primary" command="cmd_back"/>
<toolbarbutton id="forward-button" class="appbar-primary" command="cmd_forward"/>
<textbox id="urlbar-edit"
type="url"
class="uri-element"
autocompletesearch="history"
autocompletepopup="start-autocomplete"
completeselectedindex="true"
placeholder="&urlbar.emptytext;"
flex="1"
onpaste="this.focus();"
ontextentered="BrowserUI.handleUrlbarEnter(param);"
onblur="BrowserUI._urlbarBlurred();"/>
<hbox id="urlbar-container" flex="1" observes="bcast_urlbarState">
<hbox id="urlbar" flex="1">
<box id="identity-box" role="button">
<hbox id="identity-box-inner" align="center" mousethrough="always">
<image id="identity-icon"/>
</hbox>
</box>
<textbox id="urlbar-edit"
type="url"
class="uri-element"
autocompletesearch="history"
autocompletepopup="urlbar-autocomplete"
completeselectedindex="true"
placeholder="&urlbar.emptytext;"
flex="1"/>
</hbox>
<toolbarbutton id="reload-button" oncommand="CommandUpdater.doCommand(event.shiftKey ? 'cmd_forceReload' : 'cmd_reload');"/>
<toolbarbutton id="stop-button" command="cmd_stop"/>
</hbox>
<toolbarbutton id="reload-button" oncommand="CommandUpdater.doCommand(event.shiftKey ? 'cmd_forceReload' : 'cmd_reload');"/>
<toolbarbutton id="stop-button" command="cmd_stop"/>
</hbox>
<toolbarbutton id="download-button" oncommand="Appbar.onDownloadButton()"/>
<toolbarbutton id="star-button" class="appbar-primary" type="checkbox" oncommand="Appbar.onStarButton()"/>
<toolbarbutton id="pin-button" class="appbar-primary" type="checkbox" oncommand="Appbar.onPinButton()"/>
<toolbarbutton id="menu-button" class="appbar-primary" oncommand="Appbar.onMenuButton(event)"/>
</toolbar>
<toolbarbutton id="download-button" oncommand="Appbar.onDownloadButton()"/>
<toolbarbutton id="star-button" class="appbar-primary" type="checkbox" oncommand="Appbar.onStarButton()"/>
<toolbarbutton id="pin-button" class="appbar-primary" type="checkbox" oncommand="Appbar.onPinButton()"/>
<toolbarbutton id="menu-button" class="appbar-primary" oncommand="Appbar.onMenuButton(event)"/>
</toolbar>
</vbox>
</appbar>
<vbox id="panel-container" hidden="true" class="window-width window-height meta" observes="bcast_windowState">
@ -353,7 +356,7 @@
<button class="findbar-item previous-button" command="cmd_findPrevious"/>
<button class="findbar-item next-button" command="cmd_findNext"/>
<spacer flex="1"/>
<button class="findbar-item close-button" command="cmd_findClose"/>
<button id="findbar-close" class="findbar-item close-button" command="cmd_findClose"/>
</appbar>
<!-- Context button bar -->

View File

@ -338,6 +338,7 @@ function MenuPopup(aPanel, aPopup) {
this._panel = aPanel;
this._popup = aPopup;
this._wantTypeBehind = false;
this._willReshowPopup = false;
window.addEventListener('MozAppbarShowing', this, false);
}
@ -346,9 +347,19 @@ MenuPopup.prototype = {
get _commands() { return this._popup.childNodes[0]; },
show: function (aPositionOptions) {
if (this._visible)
return;
if (this._visible) {
this._willReshowPopup = true;
let self = this;
this._panel.addEventListener("transitionend", function () {
self._show(aPositionOptions);
self._panel.removeEventListener("transitionend", arguments.callee);
});
} else {
this._show(aPositionOptions);
}
},
_show: function (aPositionOptions) {
window.addEventListener("keypress", this, true);
window.addEventListener("mousedown", this, true);
Elements.stack.addEventListener("PopupChanged", this, false);
@ -362,9 +373,12 @@ MenuPopup.prototype = {
self._panel.removeEventListener("transitionend", arguments.callee);
self._panel.removeAttribute("showingfrom");
let eventName = self._willReshowPopup ? "popupmoved" : "popupshown";
let event = document.createEvent("Events");
event.initEvent("popupshown", true, false);
document.dispatchEvent(event);
event.initEvent(eventName, true, false);
self._panel.dispatchEvent(event);
self._willReshowPopup = false;
});
let popupFrom = !aPositionOptions.bottomAligned ? "above" : "below";
@ -393,9 +407,11 @@ MenuPopup.prototype = {
self._popup.style.maxWidth = "none";
self._popup.style.maxHeight = "none";
let event = document.createEvent("Events");
event.initEvent("popuphidden", true, false);
document.dispatchEvent(event);
if (!self._willReshowPopup) {
let event = document.createEvent("Events");
event.initEvent("popuphidden", true, false);
self._panel.dispatchEvent(event);
}
});
this._panel.setAttribute("hiding", "true");

View File

@ -21,7 +21,7 @@ chrome.jar:
content/bindings/dialog.xml (content/bindings/dialog.xml)
content/bindings/arrowbox.xml (content/bindings/arrowbox.xml)
content/bindings/grid.xml (content/bindings/grid.xml)
content/bindings/autocomplete.xml (content/bindings/autocomplete.xml)
content/bindings/urlbar.xml (content/bindings/urlbar.xml)
content/bindings/appbar.xml (content/bindings/appbar.xml)
content/bindings/flyoutpanel.xml (content/bindings/flyoutpanel.xml)
content/bindings/selectionoverlay.xml (content/bindings/selectionoverlay.xml)

View File

@ -12,6 +12,7 @@ include $(DEPTH)/config/autoconf.mk
BROWSER_TESTS = \
head.js \
browser_urlbar.js \
browser_bookmarks.js \
browser_canonizeURL.js \
browser_context_menu_tests.js \
@ -20,6 +21,8 @@ BROWSER_TESTS = \
browser_context_menu_tests_03.html \
browser_context_ui.js \
browser_downloads.js \
browser_findbar.js \
browser_findbar.html \
browser_history.js \
browser_onscreen_keyboard.js \
browser_onscreen_keyboard.html \
@ -60,6 +63,7 @@ BROWSER_TEST_RESOURCES = \
res/textblock01.html \
res/textinput01.html \
res/textarea01.html \
res/testEngine.xml \
$(NULL)
libs:: $(BROWSER_TESTS)

View File

@ -16,6 +16,6 @@ function test() {
[" example/a ", {ctrlKey: true}, "http://www.example.com/a"]
];
for (let [input, modifiers, result] of testcases) {
is(BrowserUI._canonizeURL(input, modifiers), result, input + " -> " + result);
is(BrowserUI._edit._canonizeURL(input, modifiers), result, input + " -> " + result);
}
}

View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Find bar tests</title>
</head>
<body>
<p>Find bar tests</title>
</body>
</html>

View File

@ -0,0 +1,56 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
function test() {
runTests();
}
gTests.push({
desc: "Access the find bar with the keyboard",
run: function() {
let tab = yield addTab(chromeRoot + "browser_findbar.html");
yield waitForCondition(() => BrowserUI.ready);
is(Elements.findbar.isShowing, false, "Find bar is hidden by default");
EventUtils.synthesizeKey("f", { accelKey: true });
yield waitForEvent(Elements.findbar, "transitionend");
is(Elements.findbar.isShowing, true, "Show find bar with Ctrl-F");
let textbox = document.getElementById("findbar-textbox");
is(textbox.value, "", "Find bar is empty");
EventUtils.sendString("bar");
is(textbox.value, "bar", "Type 'bar' into find bar");
EventUtils.synthesizeKey("VK_ESCAPE", { accelKey: true });
yield waitForEvent(Elements.findbar, "transitionend");
is(Elements.findbar.isShowing, false, "Hide find bar with Esc");
Browser.closeTab(tab);
}
});
gTests.push({
desc: "Show and hide the find bar with mouse",
run: function() {
let tab = yield addTab(chromeRoot + "browser_findbar.html");
yield waitForCondition(() => BrowserUI.ready);
is(Elements.findbar.isShowing, false, "Find bar is hidden by default");
ContextUI.displayNavbar();
EventUtils.sendMouseEvent({ type: "click" }, "menu-button");
EventUtils.sendMouseEvent({ type: "click" }, "context-findinpage");
yield waitForEvent(Elements.findbar, "transitionend");
is(Elements.findbar.isShowing, true, "Show find bar with menu item");
EventUtils.synthesizeMouse(document.getElementById("findbar-close"), 1, 1, {});
yield waitForEvent(Elements.findbar, "transitionend");
is(Elements.findbar.isShowing, false, "Hide find bar with close button");
Browser.closeTab(tab);
}
});

View File

@ -56,7 +56,7 @@ gTests.push({
var touchdrag = new TouchDragAndHold();
yield touchdrag.start(gWindow, xpos, ypos, 900, ypos);
yield waitForCondition(function () {
return getTrimmedSelection(edit).toString() ==
return getTrimmedSelection(edit).toString() ==
"mochitests/content/metro/browser/metro/base/tests/mochitest/res/textblock01.html";
}, kCommonWaitMs, kCommonPollMs);
touchdrag.end();
@ -69,6 +69,29 @@ gTests.push({
},
});
gTests.push({
desc: "bug 887120 - tap & hold to paste into urlbar",
run: function bug887120_test() {
gWindow = window;
yield showNavBar();
let edit = document.getElementById("urlbar-edit");
SpecialPowers.clipboardCopyString("mozilla");
sendContextMenuClickToElement(window, edit);
yield waitForEvent(document, "popupshown");
ok(ContextMenuUI._menuPopup._visible, "is visible");
let paste = document.getElementById("context-paste");
ok(!paste.hidden, "paste item is visible");
sendElementTap(window, paste);
ok(edit.popup.popupOpen, "bug: popup should be showing");
delete window.r;
}
});
function test() {
if (!isLandscapeMode()) {
todo(false, "browser_selection_tests need landscape mode to run.");

View File

@ -0,0 +1,232 @@
// -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
var gEdit = null;
/*=============================================================================
Search engine mocking utilities
=============================================================================*/
var gEngine = null;
const kSearchEngineName = "Foo";
const kSearchEngineURI = chromeRoot + "res/testEngine.xml";
/*
* addMockSearchDefault - adds a mock search engine to the top of the engine list.
*/
function addMockSearchDefault(aTimeoutMs) {
let deferred = Promise.defer();
let timeoutMs = aTimeoutMs || kDefaultWait;
let timerID = 0;
function engineAddObserver(aSubject, aTopic, aData) {
if (aData != "engine-added")
return;
gEngine = Services.search.getEngineByName(kSearchEngineName);
Services.obs.removeObserver(engineAddObserver, "browser-search-engine-modified");
clearTimeout(timerID);
gEngine.hidden = false;
ok(gEngine, "mock engine was added");
deferred.resolve();
}
if (gEngine) {
deferred.resolve();
return deferred.promise;
}
timerID = setTimeout(function ids_canceller() {
Services.obs.removeObserver(engineAddObserver, "browser-search-engine-modified");
deferred.reject(new Error("search add timeout"));
}, timeoutMs);
Services.obs.addObserver(engineAddObserver, "browser-search-engine-modified", false);
Services.search.addEngine(kSearchEngineURI, Ci.nsISearchEngine.DATA_XML,
"data:image/x-icon,%00", false);
return deferred.promise;
}
/*
* removeMockSearchDefault - removes mock "Foo" search engine.
*/
function removeMockSearchDefault(aTimeoutMs) {
let deferred = Promise.defer();
let timeoutMs = aTimeoutMs || kDefaultWait;
let timerID = 0;
function engineRemoveObserver(aSubject, aTopic, aData) {
if (aData != "engine-removed")
return;
clearTimeout(timerID);
gEngine = null;
Services.obs.removeObserver(engineRemoveObserver, "browser-search-engine-modified");
deferred.resolve();
}
if (!gEngine) {
deferred.resolve();
return deferred.promise;
}
timerID = setTimeout(function ids_canceller() {
Services.obs.removeObserver(engineRemoveObserver, "browser-search-engine-modified");
deferred.reject(new Error("search remove timeout"));
}, timeoutMs);
Services.obs.addObserver(engineRemoveObserver, "browser-search-engine-modified", false);
Services.search.removeEngine(gEngine);
return deferred.promise;
}
/*=============================================================================
Test cases
=============================================================================*/
function test() {
runTests();
}
function setUp() {
if (!gEdit)
gEdit = document.getElementById("urlbar-edit");
yield addTab("about:start");
yield showNavBar();
yield waitForCondition(function () {
return StartUI.isStartPageVisible;
});
}
function tearDown() {
yield removeMockSearchDefault();
Browser.closeTab(Browser.selectedTab, { forceClose: true });
delete window.r;
}
gTests.push({
desc: "search engines update",
setUp: setUp,
tearDown: tearDown,
run: function testSearchEngine() {
// If the XBL hasn't initialized yet, open the popup so that it will.
if (gEdit.popup._searches == undefined) {
gEdit.openPopup();
gEdit.closePopup();
}
let numSearches = gEdit.popup._searches.itemCount;
function getEngineItem() {
return gEdit.popup._searches.querySelector("richgriditem[value="+kSearchEngineName+"]");
}
yield addMockSearchDefault();
ok(gEdit.popup._searches.itemCount == numSearches + 1, "added search engine count");
ok(getEngineItem(), "added search engine item");
yield removeMockSearchDefault();
ok(gEdit.popup._searches.itemCount == numSearches, "normal search engine count");
ok(!getEngineItem(), "added search engine item");
}
});
gTests.push({
desc: "display autocomplete while typing, handle enter",
setUp: setUp,
tearDown: tearDown,
run: function testUrlbarTyping() {
sendElementTap(window, gEdit);
ok(gEdit.isEditing, "focus urlbar: in editing mode");
ok(!gEdit.popup.popupOpen, "focus urlbar: popup not open yet");
EventUtils.sendString("about:blank", window);
let opened = yield waitForCondition(() => gEdit.popup.popupOpen);
ok(opened, "type in urlbar: popup opens");
EventUtils.synthesizeKey("VK_RETURN", {}, window);
let closed = yield waitForCondition(() => !gEdit.popup.popupOpen);
ok(closed, "hit enter in urlbar: popup closes, page loads");
ok(!gEdit.isEditing, "hit enter in urlbar: not in editing mode");
}
});
gTests.push({
desc: "display and select a search with keyboard",
setUp: setUp,
tearDown: tearDown,
run: function testSearchKeyboard() {
yield addMockSearchDefault();
sendElementTap(window, gEdit);
ok(gEdit.isEditing, "focus urlbar: in editing mode");
ok(!gEdit.popup.popupOpen, "focus urlbar: popup not open yet");
let search = "mozilla";
EventUtils.sendString(search, window);
yield waitForCondition(() => gEdit.popup.popupOpen);
// XXX We should probably change the keyboard selection behavior entirely,
// given that it makes little to no sense, but that's a job for a later patch.
EventUtils.synthesizeKey("VK_DOWN", {}, window);
is(gEdit.popup.selectedIndex, -1, "key select search: no result selected");
is(gEdit.popup._searches.selectedIndex, 0, "key select search: first search selected");
let engines = Services.search.getVisibleEngines();
for (let i = 0, max = engines.length - 1; i < max; i++) {
is(gEdit.popup._searches.selectedIndex, i, "key select search: next index");
EventUtils.synthesizeKey("VK_DOWN", {}, window);
}
let existingValue = gEdit.value;
EventUtils.synthesizeKey("VK_RETURN", {}, window);
yield waitForCondition(() => gEdit.value != existingValue);
let closed = yield waitForCondition(() => !gEdit.popup.popupOpen);
ok(closed, "hit enter in urlbar: popup closes, page loads");
ok(!gEdit.isEditing, "hit enter in urlbar: not in editing mode");
let searchSubmission = gEngine.getSubmission(search, null);
let trimmedSubmission = gEdit.trimValue(searchSubmission.uri.spec);
is(gEdit.value, trimmedSubmission, "hit enter in urlbar: search conducted");
yield removeMockSearchDefault();
}
});
gTests.push({
desc: "display and select a search with touch",
setUp: setUp,
tearDown: tearDown,
run: function testUrlbarSearchesTouch() {
yield addMockSearchDefault();
sendElementTap(window, gEdit);
ok(gEdit.isEditing, "focus urlbar: in editing mode");
ok(!gEdit.popup.popupOpen, "focus urlbar: popup not open yet");
let search = "mozilla";
EventUtils.sendString(search, window);
yield waitForCondition(() => gEdit.popup.popupOpen);
sendElementTap(window, gEdit.popup._searches.lastChild);
let closed = yield waitForCondition(() => !gEdit.popup.popupOpen);
ok(closed, "tap search option: popup closes, page loads");
ok(!gEdit.isEditing, "tap search option: not in editing mode");
let searchSubmission = gEngine.getSubmission(search, null);
let trimmedSubmission = gEdit.trimValue(searchSubmission.uri.spec);
is(gEdit.value, trimmedSubmission, "tap search option: search conducted");
}
});

View File

@ -708,8 +708,8 @@ TouchDragAndHold.prototype = {
System utilities
=============================================================================*/
/*
* emptyClipboard - clear the windows clipbaord.
/*
* emptyClipboard - clear the windows clipboard.
*/
function emptyClipboard() {
Cc["@mozilla.org/widget/clipboard;1"].getService(Ci.nsIClipboard)

View File

@ -0,0 +1,12 @@
<OpenSearchDescription xmlns="http://a9.com/-/spec/opensearch/1.1/"
xmlns:moz="http://www.mozilla.org/2006/browser/search/">
<ShortName>Foo</ShortName>
<Description>Foo Search</Description>
<InputEncoding>utf-8</InputEncoding>
<Image width="16" height="16">data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABGklEQVQoz2NgGB6AnZ1dUlJSXl4eSDIyMhLW4Ovr%2B%2Fr168uXL69Zs4YoG%2BLi4i5dusTExMTGxsbNzd3f37937976%2BnpmZmagbHR09J49e5YvX66kpATVEBYW9ubNm2nTphkbG7e2tp44cQLIuHfvXm5urpaWFlDKysqqu7v73LlzECMYIiIiHj58mJCQoKKicvXq1bS0NKBgW1vbjh074uPjgeqAXE1NzSdPnvDz84M0AEUvXLgAsW379u1z5swBen3jxo2zZ892cHB4%2BvQp0KlAfwI1cHJyghQFBwfv2rULokFXV%2FfixYu7d%2B8GGqGgoMDKyrpu3br9%2B%2FcDuXl5eVA%2FAEWBfoWHAdAYoNuAYQ0XAeoUERFhGDYAAPoUaT2dfWJuAAAAAElFTkSuQmCC</Image>
<Url type="text/html" method="GET" template="http://mochi.test:8888/browser/browser/components/search/test/?search">
<Param name="test" value="{searchTerms}"/>
</Url>
<moz:SearchForm>http://mochi.test:8888/browser/browser/components/search/test/</moz:SearchForm>
<moz:Alias>fooalias</moz:Alias>
</OpenSearchDescription>

View File

@ -248,7 +248,7 @@ documenttab[selected] .documenttab-selection {
/* Toolbar ------------------------------------------------------------------ */
#toolbar {
#toolbar-autocomplete {
background-color: @panel_light_color@;
}
@ -364,7 +364,7 @@ appbar {
font-size: 0;
}
appbar > toolbar {
appbar toolbar {
-moz-appearance: none;
-moz-box-align: center;
border: 0;
@ -373,7 +373,7 @@ appbar > toolbar {
font-size: 1rem;
}
appbar > toolbar > toolbarbutton {
appbar toolbar > toolbarbutton {
border: 0;
margin: 0 @toolbar_horizontal_spacing@;
padding: 0;

View File

@ -194,16 +194,17 @@ menulist {
min-width: @touch_action_minwidth@; /* keep the button from being too narrow */
border: 0 none;
-moz-box-align: center;
font-weight: 600;
}
.menu-popup richlistitem:not([disabled]):hover {
background-color: #dedad0;
background-color: #ccc;
color: black;
}
.menu-popup richlistitem:not([disabled]):active {
background-color: @selected_color@ !important;
color: black;
background-color: black;
color: white;
}
.menu-popup > richlistbox[left-hand="true"] > richlistitem {
@ -214,7 +215,7 @@ menulist {
padding-right: 50px;
}
/* form select popup */
/* Additional styles applied to popups for form <select> elements. */
#select-container {
padding: 0;
@ -233,7 +234,7 @@ menulist {
}
/* listcell element doesn't have flex="1" so we need to force it */
#select-commands .option-command > listcell {
.option-command > listcell {
-moz-box-flex: 1 !important;
}
@ -244,8 +245,8 @@ menulist {
}
.option-command.selected {
background-color: @selected_color@ !important;
color: black;
background-color: #ff8000;
color: white;
}
.option-command.optgroup {
@ -255,12 +256,12 @@ menulist {
}
.option-command:not([disabled]):hover {
background-color: #dedad0;
background-color: #f0f0f0;
color: black;
}
.option-command:not([disabled]):active {
background-color: @selected_color@ !important;
background-color: #d3d3d3;
color: black;
}

View File

@ -91,11 +91,6 @@ toolbarbutton.bookmark-item[open="true"] {
height: 16px;
}
#PlacesToolbarItems > .bookmark-item:not([image]):not([label=""]):not([container]) > .toolbarbutton-icon {
display: none;
}
/* Prevent [mode="icons"] from hiding the label */
.bookmark-item > .toolbarbutton-text {
display: -moz-box !important;

View File

@ -216,11 +216,6 @@ toolbarbutton.bookmark-item > menupopup {
max-height: 16px;
}
#PlacesToolbarItems > .bookmark-item:not([image]):not([label=""]):not([container]) > .toolbarbutton-icon {
display: none;
}
.bookmark-item > .toolbarbutton-icon[label]:not([label=""]),
.bookmark-item > .toolbarbutton-icon[type="menu"] {
-moz-margin-end: 5px;

View File

@ -33,17 +33,17 @@
rgba(229,114,0,0));
%else
%if MOZ_UPDATE_CHANNEL == aurora
color: hsl(214,90%,23%);
background-image: linear-gradient(hsla(208,99%,37%,0),
hsla(214,90%,23%,.5) 35%,
hsla(214,90%,23%,.5) 65%,
hsla(214,90%,23%,0));
color: rgb(51,30,84);
background-image: linear-gradient(rgba(51,30,84,0),
rgba(51,30,84,.5) 35%,
rgba(51,30,84,.5) 65%,
rgba(51,30,84,0));
%else
color: hsl(211,33%,32%);
background-image: linear-gradient(hsla(211,33%,32%,0),
hsla(211,33%,32%,.5) 35%,
hsla(211,33%,32%,.5) 65%,
hsla(211,33%,32%,0));
color: rgb(0,33,71);
background-image: linear-gradient(rgba(0,33,71,0),
rgba(0,33,71,.5) 35%,
rgba(0,33,71,.5) 65%,
rgba(0,33,71,0));
%endif
%endif
}

View File

@ -90,6 +90,18 @@
border-color: hsla(206,100%,60%,.65) hsla(206,100%,55%,.65) hsla(206,100%,50%,.65);
}
#sidebar-header {
-moz-appearance: none;
color: black;
background-color: #EEF3FA;
border-bottom: none;
text-shadow: none;
}
#sidebar-title {
font-weight: bold;
}
.sidebar-splitter {
border: 0;
-moz-border-end: 1px solid #A9B7C9;

View File

@ -612,10 +612,6 @@ toolbarbutton.bookmark-item[open="true"] {
height: 16px;
}
#PlacesToolbarItems > .bookmark-item:not([image]):not([label=""]):not([container]) > .toolbarbutton-icon {
display: none;
}
/* Prevent [mode="icons"] from hiding the label */
.bookmark-item > .toolbarbutton-text {
display: -moz-box !important;

View File

@ -15,4 +15,8 @@
background-color: transparent;
border-top: none;
}
.sidebar-placesTreechildren::-moz-tree-cell-text(leaf, hover) {
text-decoration: none;
}
}

View File

@ -0,0 +1,53 @@
#!/usr/bin/python
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
import subprocess
import sys
from os import path
from buildconfig import substs
def determine_platform():
platform_mapping = {'WINNT': {'x86_64': 'win64',
'i386': 'win32'},
'Darwin': {'x86_64': 'macosx-universal',
'i386':'macosx-universal'},
'Linux': {'x86_64': 'linux64',
'i386': 'linux32'}}
os_type = substs['OS_TARGET']
cpu_type = substs['TARGET_CPU']
return platform_mapping.get(os_type, {}).get(cpu_type, None)
def main():
""" A wrapper script that calls compare-mozconfig.py
based on the platform that the machine is building for"""
platform = determine_platform()
if platform is not None:
python_exe = substs['PYTHON']
topsrcdir = substs['top_srcdir']
# construct paths and args for compare-mozconfig
browser_dir = path.join(topsrcdir, 'browser')
script_path = path.join(topsrcdir, 'build/compare-mozconfig/compare-mozconfigs.py')
whitelist_path = path.join(browser_dir, 'config/mozconfigs/whitelist')
beta_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'beta')
release_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'release')
nightly_mozconfig_path = path.join(browser_dir, 'config/mozconfigs', platform, 'nightly')
# compare beta vs nightly
ret_code = subprocess.call([python_exe, script_path, '--whitelist',
whitelist_path, '--no-download',
platform + ',' + beta_mozconfig_path +
',' + nightly_mozconfig_path])
if ret_code > 0:
sys.exit(ret_code)
# compare release vs nightly
ret_code = subprocess.call([python_exe, script_path, '--whitelist',
whitelist_path, '--no-download',
platform + ',' + release_mozconfig_path +
',' + nightly_mozconfig_path])

View File

@ -0,0 +1,68 @@
#!/usr/bin/python
import logging
import os
import site
import sys
import urllib2
site.addsitedir(os.path.join(os.path.dirname(__file__), "../../lib/python"))
from release.sanity import verify_mozconfigs
from release.info import readConfig
from util.hg import make_hg_url
FAILURE_CODE = 1
SUCCESS_CODE = 0
def get_mozconfig(path, options):
"""Consumes a path and returns a list of lines from
the mozconfig file. If download is required, the path
specified should be relative to the root of the hg
repository e.g browser/config/mozconfigs/linux32/nightly"""
if options.no_download:
return open(path, 'r').readlines()
else:
url = make_hg_url(options.hghost, options.branch, 'http',
options.revision, path)
return urllib2.urlopen(url).readlines()
if __name__ == '__main__':
from optparse import OptionParser
parser = OptionParser()
parser.add_option('--branch', dest='branch')
parser.add_option('--revision', dest='revision')
parser.add_option('--hghost', dest='hghost', default='hg.mozilla.org')
parser.add_option('--whitelist', dest='whitelist')
parser.add_option('--no-download', action='store_true', dest='no_download',
default=False)
options, args = parser.parse_args()
logging.basicConfig(level=logging.INFO)
missing_args = options.branch is None or options.revision is None
if not options.no_download and missing_args:
logging.error('Not enough arguments to download mozconfigs')
sys.exit(FAILURE_CODE)
mozconfig_whitelist = readConfig(options.whitelist, ['whitelist'])
for arg in args:
platform, mozconfig_path, nightly_mozconfig_path = arg.split(',')
mozconfig_lines = get_mozconfig(mozconfig_path, options)
nightly_mozconfig_lines = get_mozconfig(nightly_mozconfig_path, options)
mozconfig_pair = (mozconfig_path, mozconfig_lines)
nightly_mozconfig_pair = (nightly_mozconfig_path,
nightly_mozconfig_lines)
passed = verify_mozconfigs(mozconfig_pair, nightly_mozconfig_pair,
platform, mozconfig_whitelist)
if passed:
logging.info('Mozconfig check passed!')
else:
logging.error('Mozconfig check failed!')
sys.exit(FAILURE_CODE)
sys.exit(SUCCESS_CODE)

View File

@ -19,8 +19,7 @@ toolkit/library
security/manager
security/dbm
security/nss
accessible/build
accessible
accessible
dom
content
layout

View File

@ -131,6 +131,7 @@ def bootstrap(topsrcdir, mozilla_dir=None):
os.makedirs(state_env_dir, mode=0o770)
print('Please re-run mach.')
sys.exit(1)
state_dir = state_env_dir
else:
if not os.path.exists(state_user_dir):
print(STATE_DIR_FIRST_RUN.format(userdir=state_user_dir))
@ -146,6 +147,7 @@ def bootstrap(topsrcdir, mozilla_dir=None):
os.mkdir(state_user_dir)
print('Please re-run mach.')
sys.exit(1)
state_dir = state_user_dir
try:
import mach.main
@ -153,7 +155,12 @@ def bootstrap(topsrcdir, mozilla_dir=None):
sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS]
import mach.main
def populate_context(context):
context.state_dir = state_dir
mach = mach.main.Mach(topsrcdir)
mach.populate_context_handler = populate_context
for category, meta in CATEGORIES.items():
mach.define_category(category, meta['short'], meta['long'],
meta['priority'])

218
build/release/info.py Normal file
View File

@ -0,0 +1,218 @@
from datetime import datetime
import os
from os import path
import re
import shutil
import sys
from urllib2 import urlopen
from release.paths import makeCandidatesDir
import logging
log = logging.getLogger(__name__)
# If version has two parts with no trailing specifiers like "rc", we
# consider it a "final" release for which we only create a _RELEASE tag.
FINAL_RELEASE_REGEX = "^\d+\.\d+$"
class ConfigError(Exception):
pass
def getBuildID(platform, product, version, buildNumber, nightlyDir='nightly',
server='stage.mozilla.org'):
infoTxt = makeCandidatesDir(product, version, buildNumber, nightlyDir,
protocol='http', server=server) + \
'%s_info.txt' % platform
try:
buildInfo = urlopen(infoTxt).read()
except:
log.error("Failed to retrieve %s" % infoTxt)
raise
for line in buildInfo.splitlines():
key, value = line.rstrip().split('=', 1)
if key == 'buildID':
return value
def findOldBuildIDs(product, version, buildNumber, platforms,
nightlyDir='nightly', server='stage.mozilla.org'):
ids = {}
if buildNumber <= 1:
return ids
for n in range(1, buildNumber):
for platform in platforms:
if platform not in ids:
ids[platform] = []
try:
id = getBuildID(platform, product, version, n, nightlyDir,
server)
ids[platform].append(id)
except Exception, e:
log.error("Hit exception: %s" % e)
return ids
def getReleaseConfigName(product, branch, version=None, staging=False):
# XXX: Horrible hack for bug 842741. Because Thunderbird release
# and esr both build out of esr17 repositories we'll bump the wrong
# config for release without this.
if product == 'thunderbird' and 'esr17' in branch and version and 'esr' not in version:
cfg = 'release-thunderbird-comm-release.py'
else:
cfg = 'release-%s-%s.py' % (product, branch)
if staging:
cfg = 'staging_%s' % cfg
return cfg
def readReleaseConfig(configfile, required=[]):
return readConfig(configfile, keys=['releaseConfig'], required=required)
def readBranchConfig(dir, localconfig, branch, required=[]):
shutil.copy(localconfig, path.join(dir, "localconfig.py"))
oldcwd = os.getcwd()
os.chdir(dir)
sys.path.append(".")
try:
return readConfig("config.py", keys=['BRANCHES', branch],
required=required)
finally:
os.chdir(oldcwd)
sys.path.remove(".")
def readConfig(configfile, keys=[], required=[]):
c = {}
execfile(configfile, c)
for k in keys:
c = c[k]
items = c.keys()
err = False
for key in required:
if key not in items:
err = True
log.error("Required item `%s' missing from %s" % (key, c))
if err:
raise ConfigError("Missing at least one item in config, see above")
return c
def isFinalRelease(version):
return bool(re.match(FINAL_RELEASE_REGEX, version))
def getBaseTag(product, version):
product = product.upper()
version = version.replace('.', '_')
return '%s_%s' % (product, version)
def getTags(baseTag, buildNumber, buildTag=True):
t = ['%s_RELEASE' % baseTag]
if buildTag:
t.append('%s_BUILD%d' % (baseTag, int(buildNumber)))
return t
def getRuntimeTag(tag):
return "%s_RUNTIME" % tag
def getReleaseTag(tag):
return "%s_RELEASE" % tag
def generateRelbranchName(version, prefix='GECKO'):
return '%s%s_%s_RELBRANCH' % (
prefix, version.replace('.', ''),
datetime.now().strftime('%Y%m%d%H'))
def getReleaseName(product, version, buildNumber):
return '%s-%s-build%s' % (product.title(), version, str(buildNumber))
def getRepoMatchingBranch(branch, sourceRepositories):
for sr in sourceRepositories.values():
if branch in sr['path']:
return sr
return None
def fileInfo(filepath, product):
"""Extract information about a release file. Returns a dictionary with the
following keys set:
'product', 'version', 'locale', 'platform', 'contents', 'format',
'pathstyle'
'contents' is one of 'complete', 'installer'
'format' is one of 'mar' or 'exe'
'pathstyle' is either 'short' or 'long', and refers to if files are all in
one directory, with the locale as part of the filename ('short' paths,
firefox 3.0 style filenames), or if the locale names are part of the
directory structure, but not the file name itself ('long' paths,
firefox 3.5+ style filenames)
"""
try:
# Mozilla 1.9.0 style (aka 'short') paths
# e.g. firefox-3.0.12.en-US.win32.complete.mar
filename = os.path.basename(filepath)
m = re.match("^(%s)-([0-9.]+)\.([-a-zA-Z]+)\.(win32)\.(complete|installer)\.(mar|exe)$" % product, filename)
if not m:
raise ValueError("Could not parse: %s" % filename)
return {'product': m.group(1),
'version': m.group(2),
'locale': m.group(3),
'platform': m.group(4),
'contents': m.group(5),
'format': m.group(6),
'pathstyle': 'short',
'leading_path': '',
}
except:
# Mozilla 1.9.1 and on style (aka 'long') paths
# e.g. update/win32/en-US/firefox-3.5.1.complete.mar
# win32/en-US/Firefox Setup 3.5.1.exe
ret = {'pathstyle': 'long'}
if filepath.endswith('.mar'):
ret['format'] = 'mar'
m = re.search("update/(win32|linux-i686|linux-x86_64|mac|mac64)/([-a-zA-Z]+)/(%s)-(\d+\.\d+(?:\.\d+)?(?:\w+(?:\d+)?)?)\.(complete)\.mar" % product, filepath)
if not m:
raise ValueError("Could not parse: %s" % filepath)
ret['platform'] = m.group(1)
ret['locale'] = m.group(2)
ret['product'] = m.group(3)
ret['version'] = m.group(4)
ret['contents'] = m.group(5)
ret['leading_path'] = ''
elif filepath.endswith('.exe'):
ret['format'] = 'exe'
ret['contents'] = 'installer'
# EUballot builds use a different enough style of path than others
# that we can't catch them in the same regexp
if filepath.find('win32-EUballot') != -1:
ret['platform'] = 'win32'
m = re.search("(win32-EUballot/)([-a-zA-Z]+)/((?i)%s) Setup (\d+\.\d+(?:\.\d+)?(?:\w+\d+)?(?:\ \w+\ \d+)?)\.exe" % product, filepath)
if not m:
raise ValueError("Could not parse: %s" % filepath)
ret['leading_path'] = m.group(1)
ret['locale'] = m.group(2)
ret['product'] = m.group(3).lower()
ret['version'] = m.group(4)
else:
m = re.search("(partner-repacks/[-a-zA-Z0-9_]+/|)(win32|mac|linux-i686)/([-a-zA-Z]+)/((?i)%s) Setup (\d+\.\d+(?:\.\d+)?(?:\w+(?:\d+)?)?(?:\ \w+\ \d+)?)\.exe" % product, filepath)
if not m:
raise ValueError("Could not parse: %s" % filepath)
ret['leading_path'] = m.group(1)
ret['platform'] = m.group(2)
ret['locale'] = m.group(3)
ret['product'] = m.group(4).lower()
ret['version'] = m.group(5)
else:
raise ValueError("Unknown filetype for %s" % filepath)
return ret

124
build/release/sanity.py Normal file
View File

@ -0,0 +1,124 @@
import difflib
import logging
import re
import urllib2
from util.commands import run_cmd, get_output
from util.hg import get_repo_name, make_hg_url
from subprocess import CalledProcessError
log = logging.getLogger(__name__)
def check_buildbot():
"""check if buildbot command works"""
try:
run_cmd(['buildbot', '--version'])
except CalledProcessError:
log.error("FAIL: buildbot command doesn't work", exc_info=True)
raise
def find_version(contents, versionNumber):
"""Given an open readable file-handle look for the occurrence
of the version # in the file"""
ret = re.search(re.compile(re.escape(versionNumber), re.DOTALL), contents)
return ret
def locale_diff(locales1, locales2):
""" accepts two lists and diffs them both ways, returns any differences
found """
diff_list = [locale for locale in locales1 if not locale in locales2]
diff_list.extend(locale for locale in locales2 if not locale in locales1)
return diff_list
def get_buildbot_username_param():
cmd = ['buildbot', 'sendchange', '--help']
output = get_output(cmd)
if "-W, --who=" in output:
return "--who"
else:
return "--username"
def sendchange(branch, revision, username, master, products):
"""Send the change to buildbot to kick off the release automation"""
if isinstance(products, basestring):
products = [products]
cmd = [
'buildbot',
'sendchange',
get_buildbot_username_param(),
username,
'--master',
master,
'--branch',
branch,
'-p',
'products:%s' % ','.join(products),
'-p',
'script_repo_revision:%s' % revision,
'release_build'
]
logging.info("Executing: %s" % cmd)
run_cmd(cmd)
def verify_mozconfigs(mozconfig_pair, nightly_mozconfig_pair, platform,
mozconfigWhitelist={}):
"""Compares mozconfig to nightly_mozconfig and compare to an optional
whitelist of known differences. mozconfig_pair and nightly_mozconfig_pair
are pairs containing the mozconfig's identifier and the list of lines in
the mozconfig."""
# unpack the pairs to get the names, the names are just for
# identifying the mozconfigs when logging the error messages
mozconfig_name, mozconfig_lines = mozconfig_pair
nightly_mozconfig_name, nightly_mozconfig_lines = nightly_mozconfig_pair
missing_args = mozconfig_lines == [] or nightly_mozconfig_lines == []
if missing_args:
log.info("Missing mozconfigs to compare for %s" % platform)
return False
success = True
diffInstance = difflib.Differ()
diff_result = diffInstance.compare(mozconfig_lines, nightly_mozconfig_lines)
diffList = list(diff_result)
for line in diffList:
clean_line = line[1:].strip()
if (line[0] == '-' or line[0] == '+') and len(clean_line) > 1:
# skip comment lines
if clean_line.startswith('#'):
continue
# compare to whitelist
message = ""
if line[0] == '-':
if platform in mozconfigWhitelist.get('release', {}):
if clean_line in \
mozconfigWhitelist['release'][platform]:
continue
elif line[0] == '+':
if platform in mozconfigWhitelist.get('nightly', {}):
if clean_line in \
mozconfigWhitelist['nightly'][platform]:
continue
else:
log.warning("%s not in %s %s!" % (
clean_line, platform,
mozconfigWhitelist['nightly'][platform]))
else:
log.error("Skipping line %s!" % line)
continue
message = "found in %s but not in %s: %s"
if line[0] == '-':
log.error(message % (mozconfig_name,
nightly_mozconfig_name, clean_line))
else:
log.error(message % (nightly_mozconfig_name,
mozconfig_name, clean_line))
success = False
return success

611
build/util/hg.py Normal file
View File

@ -0,0 +1,611 @@
"""Functions for interacting with hg"""
import os
import re
import subprocess
from urlparse import urlsplit
from ConfigParser import RawConfigParser
from util.commands import run_cmd, get_output, remove_path
from util.retry import retry
import logging
log = logging.getLogger(__name__)
class DefaultShareBase:
pass
DefaultShareBase = DefaultShareBase()
class HgUtilError(Exception):
pass
def _make_absolute(repo):
if repo.startswith("file://"):
path = repo[len("file://"):]
repo = "file://%s" % os.path.abspath(path)
elif "://" not in repo:
repo = os.path.abspath(repo)
return repo
def make_hg_url(hgHost, repoPath, protocol='https', revision=None,
filename=None):
"""construct a valid hg url from a base hg url (hg.mozilla.org),
repoPath, revision and possible filename"""
base = '%s://%s' % (protocol, hgHost)
repo = '/'.join(p.strip('/') for p in [base, repoPath])
if not filename:
if not revision:
return repo
else:
return '/'.join([p.strip('/') for p in [repo, 'rev', revision]])
else:
assert revision
return '/'.join([p.strip('/') for p in [repo, 'raw-file', revision, filename]])
def get_repo_name(repo):
return repo.rstrip('/').split('/')[-1]
def get_repo_path(repo):
repo = _make_absolute(repo)
if repo.startswith("/"):
return repo.lstrip("/")
else:
return urlsplit(repo).path.lstrip("/")
def get_revision(path):
"""Returns which revision directory `path` currently has checked out."""
return get_output(['hg', 'parent', '--template', '{node|short}'], cwd=path)
def get_branch(path):
return get_output(['hg', 'branch'], cwd=path).strip()
def get_branches(path):
branches = []
for line in get_output(['hg', 'branches', '-c'], cwd=path).splitlines():
branches.append(line.split()[0])
return branches
def is_hg_cset(rev):
"""Retruns True if passed revision represents a valid HG revision
(long or short(er) 40 bit hex)"""
try:
int(rev, 16)
return True
except (TypeError, ValueError):
return False
def hg_ver():
"""Returns the current version of hg, as a tuple of
(major, minor, build)"""
ver_string = get_output(['hg', '-q', 'version'])
match = re.search("\(version ([0-9.]+)\)", ver_string)
if match:
bits = match.group(1).split(".")
if len(bits) < 3:
bits += (0,)
ver = tuple(int(b) for b in bits)
else:
ver = (0, 0, 0)
log.debug("Running hg version %s", ver)
return ver
def purge(dest):
"""Purge the repository of all untracked and ignored files."""
try:
run_cmd(['hg', '--config', 'extensions.purge=', 'purge',
'-a', '--all', dest], cwd=dest)
except subprocess.CalledProcessError, e:
log.debug('purge failed: %s' % e)
raise
def update(dest, branch=None, revision=None):
"""Updates working copy `dest` to `branch` or `revision`. If neither is
set then the working copy will be updated to the latest revision on the
current branch. Local changes will be discarded."""
# If we have a revision, switch to that
if revision is not None:
cmd = ['hg', 'update', '-C', '-r', revision]
run_cmd(cmd, cwd=dest)
else:
# Check & switch branch
local_branch = get_output(['hg', 'branch'], cwd=dest).strip()
cmd = ['hg', 'update', '-C']
# If this is different, checkout the other branch
if branch and branch != local_branch:
cmd.append(branch)
run_cmd(cmd, cwd=dest)
return get_revision(dest)
def clone(repo, dest, branch=None, revision=None, update_dest=True,
clone_by_rev=False, mirrors=None, bundles=None):
"""Clones hg repo and places it at `dest`, replacing whatever else is
there. The working copy will be empty.
If `revision` is set, only the specified revision and its ancestors will
be cloned.
If `update_dest` is set, then `dest` will be updated to `revision` if
set, otherwise to `branch`, otherwise to the head of default.
If `mirrors` is set, will try and clone from the mirrors before
cloning from `repo`.
If `bundles` is set, will try and download the bundle first and
unbundle it. If successful, will pull in new revisions from mirrors or
the master repo. If unbundling fails, will fall back to doing a regular
clone from mirrors or the master repo.
Regardless of how the repository ends up being cloned, the 'default' path
will point to `repo`.
"""
if os.path.exists(dest):
remove_path(dest)
if bundles:
log.info("Attempting to initialize clone with bundles")
for bundle in bundles:
if os.path.exists(dest):
remove_path(dest)
init(dest)
log.info("Trying to use bundle %s", bundle)
try:
if not unbundle(bundle, dest):
remove_path(dest)
continue
adjust_paths(dest, default=repo)
# Now pull / update
return pull(repo, dest, update_dest=update_dest,
mirrors=mirrors, revision=revision, branch=branch)
except Exception:
remove_path(dest)
log.exception("Problem unbundling/pulling from %s", bundle)
continue
else:
log.info("Using bundles failed; falling back to clone")
if mirrors:
log.info("Attempting to clone from mirrors")
for mirror in mirrors:
log.info("Cloning from %s", mirror)
try:
retval = clone(mirror, dest, branch, revision,
update_dest=update_dest, clone_by_rev=clone_by_rev)
adjust_paths(dest, default=repo)
return retval
except:
log.exception("Problem cloning from mirror %s", mirror)
continue
else:
log.info("Pulling from mirrors failed; falling back to %s", repo)
# We may have a partial repo here; mercurial() copes with that
# We need to make sure our paths are correct though
if os.path.exists(os.path.join(dest, '.hg')):
adjust_paths(dest, default=repo)
return mercurial(repo, dest, branch, revision, autoPurge=True,
update_dest=update_dest, clone_by_rev=clone_by_rev)
cmd = ['hg', 'clone']
if not update_dest:
cmd.append('-U')
if clone_by_rev:
if revision:
cmd.extend(['-r', revision])
elif branch:
# hg >= 1.6 supports -b branch for cloning
ver = hg_ver()
if ver >= (1, 6, 0):
cmd.extend(['-b', branch])
cmd.extend([repo, dest])
run_cmd(cmd)
if update_dest:
return update(dest, branch, revision)
def common_args(revision=None, branch=None, ssh_username=None, ssh_key=None):
"""Fill in common hg arguments, encapsulating logic checks that depend on
mercurial versions and provided arguments"""
args = []
if ssh_username or ssh_key:
opt = ['-e', 'ssh']
if ssh_username:
opt[1] += ' -l %s' % ssh_username
if ssh_key:
opt[1] += ' -i %s' % ssh_key
args.extend(opt)
if revision:
args.extend(['-r', revision])
elif branch:
if hg_ver() >= (1, 6, 0):
args.extend(['-b', branch])
return args
def pull(repo, dest, update_dest=True, mirrors=None, **kwargs):
"""Pulls changes from hg repo and places it in `dest`.
If `update_dest` is set, then `dest` will be updated to `revision` if
set, otherwise to `branch`, otherwise to the head of default.
If `mirrors` is set, will try and pull from the mirrors first before
`repo`."""
if mirrors:
for mirror in mirrors:
try:
return pull(mirror, dest, update_dest=update_dest, **kwargs)
except:
log.exception("Problem pulling from mirror %s", mirror)
continue
else:
log.info("Pulling from mirrors failed; falling back to %s", repo)
# Convert repo to an absolute path if it's a local repository
repo = _make_absolute(repo)
cmd = ['hg', 'pull']
# Don't pass -r to "hg pull", except when it's a valid HG revision.
# Pulling using tag names is dangerous: it uses the local .hgtags, so if
# the tag has moved on the remote side you won't pull the new revision the
# remote tag refers to.
pull_kwargs = kwargs.copy()
if 'revision' in pull_kwargs and \
not is_hg_cset(pull_kwargs['revision']):
del pull_kwargs['revision']
cmd.extend(common_args(**pull_kwargs))
cmd.append(repo)
run_cmd(cmd, cwd=dest)
if update_dest:
branch = None
if 'branch' in kwargs and kwargs['branch']:
branch = kwargs['branch']
revision = None
if 'revision' in kwargs and kwargs['revision']:
revision = kwargs['revision']
return update(dest, branch=branch, revision=revision)
# Defines the places of attributes in the tuples returned by `out'
REVISION, BRANCH = 0, 1
def out(src, remote, **kwargs):
"""Check for outgoing changesets present in a repo"""
cmd = ['hg', '-q', 'out', '--template', '{node} {branches}\n']
cmd.extend(common_args(**kwargs))
cmd.append(remote)
if os.path.exists(src):
try:
revs = []
for line in get_output(cmd, cwd=src).rstrip().split("\n"):
try:
rev, branch = line.split()
# Mercurial displays no branch at all if the revision is on
# "default"
except ValueError:
rev = line.rstrip()
branch = "default"
revs.append((rev, branch))
return revs
except subprocess.CalledProcessError, inst:
# In some situations, some versions of Mercurial return "1"
# if no changes are found, so we need to ignore this return code
if inst.returncode == 1:
return []
raise
def push(src, remote, push_new_branches=True, force=False, **kwargs):
cmd = ['hg', 'push']
cmd.extend(common_args(**kwargs))
if force:
cmd.append('-f')
if push_new_branches:
cmd.append('--new-branch')
cmd.append(remote)
run_cmd(cmd, cwd=src)
def mercurial(repo, dest, branch=None, revision=None, update_dest=True,
shareBase=DefaultShareBase, allowUnsharedLocalClones=False,
clone_by_rev=False, mirrors=None, bundles=None, autoPurge=False):
"""Makes sure that `dest` is has `revision` or `branch` checked out from
`repo`.
Do what it takes to make that happen, including possibly clobbering
dest.
If allowUnsharedLocalClones is True and we're trying to use the share
extension but fail, then we will be able to clone from the shared repo to
our destination. If this is False, the default, then if we don't have the
share extension we will just clone from the remote repository.
If `clone_by_rev` is True, use 'hg clone -r <rev>' instead of 'hg clone'.
This is slower, but useful when cloning repos with lots of heads.
If `mirrors` is set, will try and use the mirrors before `repo`.
If `bundles` is set, will try and download the bundle first and
unbundle it instead of doing a full clone. If successful, will pull in
new revisions from mirrors or the master repo. If unbundling fails, will
fall back to doing a regular clone from mirrors or the master repo.
"""
dest = os.path.abspath(dest)
if shareBase is DefaultShareBase:
shareBase = os.environ.get("HG_SHARE_BASE_DIR", None)
log.info("Reporting hg version in use")
cmd = ['hg', '-q', 'version']
run_cmd(cmd, cwd='.')
if shareBase:
# Check that 'hg share' works
try:
log.info("Checking if share extension works")
output = get_output(['hg', 'help', 'share'], dont_log=True)
if 'no commands defined' in output:
# Share extension is enabled, but not functional
log.info("Disabling sharing since share extension doesn't seem to work (1)")
shareBase = None
elif 'unknown command' in output:
# Share extension is disabled
log.info("Disabling sharing since share extension doesn't seem to work (2)")
shareBase = None
except subprocess.CalledProcessError:
# The command failed, so disable sharing
log.info("Disabling sharing since share extension doesn't seem to work (3)")
shareBase = None
# Check that our default path is correct
if os.path.exists(os.path.join(dest, '.hg')):
hgpath = path(dest, "default")
# Make sure that our default path is correct
if hgpath != _make_absolute(repo):
log.info("hg path isn't correct (%s should be %s); clobbering",
hgpath, _make_absolute(repo))
remove_path(dest)
# If the working directory already exists and isn't using share we update
# the working directory directly from the repo, ignoring the sharing
# settings
if os.path.exists(dest):
if not os.path.exists(os.path.join(dest, ".hg")):
log.warning("%s doesn't appear to be a valid hg directory; clobbering", dest)
remove_path(dest)
elif not os.path.exists(os.path.join(dest, ".hg", "sharedpath")):
try:
if autoPurge:
purge(dest)
return pull(repo, dest, update_dest=update_dest, branch=branch,
revision=revision,
mirrors=mirrors)
except subprocess.CalledProcessError:
log.warning("Error pulling changes into %s from %s; clobbering", dest, repo)
log.debug("Exception:", exc_info=True)
remove_path(dest)
# If that fails for any reason, and sharing is requested, we'll try to
# update the shared repository, and then update the working directory from
# that.
if shareBase:
sharedRepo = os.path.join(shareBase, get_repo_path(repo))
dest_sharedPath = os.path.join(dest, '.hg', 'sharedpath')
if os.path.exists(sharedRepo):
hgpath = path(sharedRepo, "default")
# Make sure that our default path is correct
if hgpath != _make_absolute(repo):
log.info("hg path isn't correct (%s should be %s); clobbering",
hgpath, _make_absolute(repo))
# we need to clobber both the shared checkout and the dest,
# since hgrc needs to be in both places
remove_path(sharedRepo)
remove_path(dest)
if os.path.exists(dest_sharedPath):
# Make sure that the sharedpath points to sharedRepo
dest_sharedPath_data = os.path.normpath(
open(dest_sharedPath).read())
norm_sharedRepo = os.path.normpath(os.path.join(sharedRepo, '.hg'))
if dest_sharedPath_data != norm_sharedRepo:
# Clobber!
log.info("We're currently shared from %s, but are being requested to pull from %s (%s); clobbering",
dest_sharedPath_data, repo, norm_sharedRepo)
remove_path(dest)
try:
log.info("Updating shared repo")
mercurial(repo, sharedRepo, branch=branch, revision=revision,
update_dest=False, shareBase=None, clone_by_rev=clone_by_rev,
mirrors=mirrors, bundles=bundles, autoPurge=False)
if os.path.exists(dest):
if autoPurge:
purge(dest)
return update(dest, branch=branch, revision=revision)
try:
log.info("Trying to share %s to %s", sharedRepo, dest)
return share(sharedRepo, dest, branch=branch, revision=revision)
except subprocess.CalledProcessError:
if not allowUnsharedLocalClones:
# Re-raise the exception so it gets caught below.
# We'll then clobber dest, and clone from original repo
raise
log.warning("Error calling hg share from %s to %s;"
"falling back to normal clone from shared repo",
sharedRepo, dest)
# Do a full local clone first, and then update to the
# revision we want
# This lets us use hardlinks for the local clone if the OS
# supports it
clone(sharedRepo, dest, update_dest=False,
mirrors=mirrors, bundles=bundles)
return update(dest, branch=branch, revision=revision)
except subprocess.CalledProcessError:
log.warning(
"Error updating %s from sharedRepo (%s): ", dest, sharedRepo)
log.debug("Exception:", exc_info=True)
remove_path(dest)
# end if shareBase
if not os.path.exists(os.path.dirname(dest)):
os.makedirs(os.path.dirname(dest))
# Share isn't available or has failed, clone directly from the source
return clone(repo, dest, branch, revision,
update_dest=update_dest, mirrors=mirrors,
bundles=bundles, clone_by_rev=clone_by_rev)
def apply_and_push(localrepo, remote, changer, max_attempts=10,
ssh_username=None, ssh_key=None, force=False):
"""This function calls `changer' to make changes to the repo, and tries
its hardest to get them to the origin repo. `changer' must be a
callable object that receives two arguments: the directory of the local
repository, and the attempt number. This function will push ALL
changesets missing from remote."""
assert callable(changer)
branch = get_branch(localrepo)
changer(localrepo, 1)
for n in range(1, max_attempts + 1):
new_revs = []
try:
new_revs = out(src=localrepo, remote=remote,
ssh_username=ssh_username,
ssh_key=ssh_key)
if len(new_revs) < 1:
raise HgUtilError("No revs to push")
push(src=localrepo, remote=remote, ssh_username=ssh_username,
ssh_key=ssh_key, force=force)
return
except subprocess.CalledProcessError, e:
log.debug("Hit error when trying to push: %s" % str(e))
if n == max_attempts:
log.debug("Tried %d times, giving up" % max_attempts)
for r in reversed(new_revs):
run_cmd(['hg', '--config', 'extensions.mq=', 'strip', '-n',
r[REVISION]], cwd=localrepo)
raise HgUtilError("Failed to push")
pull(remote, localrepo, update_dest=False,
ssh_username=ssh_username, ssh_key=ssh_key)
# After we successfully rebase or strip away heads the push is
# is attempted again at the start of the loop
try:
run_cmd(['hg', '--config', 'ui.merge=internal:merge',
'rebase'], cwd=localrepo)
except subprocess.CalledProcessError, e:
log.debug("Failed to rebase: %s" % str(e))
update(localrepo, branch=branch)
for r in reversed(new_revs):
run_cmd(['hg', '--config', 'extensions.mq=', 'strip', '-n',
r[REVISION]], cwd=localrepo)
changer(localrepo, n + 1)
def share(source, dest, branch=None, revision=None):
"""Creates a new working directory in "dest" that shares history with
"source" using Mercurial's share extension"""
run_cmd(['hg', 'share', '-U', source, dest])
return update(dest, branch=branch, revision=revision)
def cleanOutgoingRevs(reponame, remote, username, sshKey):
outgoingRevs = retry(out, kwargs=dict(src=reponame, remote=remote,
ssh_username=username,
ssh_key=sshKey))
for r in reversed(outgoingRevs):
run_cmd(['hg', '--config', 'extensions.mq=', 'strip', '-n',
r[REVISION]], cwd=reponame)
def path(src, name='default'):
"""Returns the remote path associated with "name" """
try:
return get_output(['hg', 'path', name], cwd=src).strip()
except subprocess.CalledProcessError:
return None
def init(dest):
"""Initializes an empty repo in `dest`"""
run_cmd(['hg', 'init', dest])
def unbundle(bundle, dest):
"""Unbundles the bundle located at `bundle` into `dest`.
`bundle` can be a local file or remote url."""
try:
get_output(['hg', 'unbundle', bundle], cwd=dest, include_stderr=True)
return True
except subprocess.CalledProcessError:
return False
def adjust_paths(dest, **paths):
"""Adjusts paths in `dest`/.hg/hgrc so that names in `paths` are set to
paths[name].
Note that any comments in the hgrc will be lost if changes are made to the
file."""
hgrc = os.path.join(dest, '.hg', 'hgrc')
config = RawConfigParser()
config.read(hgrc)
if not config.has_section('paths'):
config.add_section('paths')
changed = False
for path_name, path_value in paths.items():
if (not config.has_option('paths', path_name) or
config.get('paths', path_name) != path_value):
changed = True
config.set('paths', path_name, path_value)
if changed:
config.write(open(hgrc, 'w'))
def commit(dest, msg, user=None):
cmd = ['hg', 'commit', '-m', msg]
if user:
cmd.extend(['-u', user])
run_cmd(cmd, cwd=dest)
return get_revision(dest)
def tag(dest, tags, user=None, msg=None, rev=None, force=None):
cmd = ['hg', 'tag']
if user:
cmd.extend(['-u', user])
if msg:
cmd.extend(['-m', msg])
if rev:
cmd.extend(['-r', rev])
if force:
cmd.append('-f')
cmd.extend(tags)
run_cmd(cmd, cwd=dest)
return get_revision(dest)

View File

@ -430,20 +430,6 @@ private:
nsIPrincipal*
GetSubjectPrincipal(JSContext* cx, nsresult* rv);
// Returns null if a principal cannot be found. Note that rv can be NS_OK
// when this happens -- this means that there was no script. Callers MUST
// pass in a non-null rv here.
static nsIPrincipal*
GetScriptPrincipal(JSScript* script, nsresult* rv);
// Returns null if a principal cannot be found. Note that rv can be NS_OK
// when this happens -- this means that there was no script associated
// with the function object, and no global object associated with the scope
// of obj (the last object on its parent chain). Callers MUST pass in a
// non-null rv here.
static nsIPrincipal*
GetFunctionObjectPrincipal(JSContext* cx, JS::Handle<JSObject*> obj, nsresult* rv);
/**
* Check capability levels for an |aObj| that implements
* nsISecurityCheckedComponent.

View File

@ -1584,25 +1584,7 @@ nsScriptSecurityManager::CheckFunctionAccess(JSContext *aCx, void *aFunObj,
// This check is called for event handlers
nsresult rv;
JS::Rooted<JSObject*> rootedFunObj(aCx, static_cast<JSObject*>(aFunObj));
nsIPrincipal* subject =
GetFunctionObjectPrincipal(aCx, rootedFunObj, &rv);
// If subject is null, get a principal from the function object's scope.
if (NS_SUCCEEDED(rv) && !subject)
{
#ifdef DEBUG
{
JS_ASSERT(JS_ObjectIsFunction(aCx, rootedFunObj));
JS::Rooted<JSFunction*> fun(aCx, JS_GetObjectFunction(rootedFunObj));
JSScript *script = JS_GetFunctionScript(aCx, fun);
NS_ASSERTION(!script, "Null principal for non-native function!");
}
#endif
subject = doGetObjectPrincipal(rootedFunObj);
}
nsIPrincipal* subject = doGetObjectPrincipal(rootedFunObj);
if (!subject)
return NS_ERROR_FAILURE;
@ -1929,77 +1911,6 @@ nsScriptSecurityManager::GetCodebasePrincipalInternal(nsIURI *aURI,
return NS_OK;
}
// static
nsIPrincipal*
nsScriptSecurityManager::GetScriptPrincipal(JSScript *script,
nsresult* rv)
{
NS_PRECONDITION(rv, "Null out param");
*rv = NS_OK;
if (!script)
{
return nullptr;
}
JSPrincipals *jsp = JS_GetScriptPrincipals(script);
if (!jsp) {
*rv = NS_ERROR_FAILURE;
NS_ERROR("Script compiled without principals!");
return nullptr;
}
return nsJSPrincipals::get(jsp);
}
// static
nsIPrincipal*
nsScriptSecurityManager::GetFunctionObjectPrincipal(JSContext *cx,
JS::Handle<JSObject*> obj,
nsresult *rv)
{
NS_PRECONDITION(rv, "Null out param");
*rv = NS_OK;
if (!JS_ObjectIsFunction(cx, obj))
{
// Protect against pseudo-functions (like SJOWs).
nsIPrincipal *result = doGetObjectPrincipal(obj);
if (!result)
*rv = NS_ERROR_FAILURE;
return result;
}
JS::Rooted<JSFunction*> fun(cx, JS_GetObjectFunction(obj));
JSScript *script = JS_GetFunctionScript(cx, fun);
if (!script)
{
// A native function: skip it in order to find its scripted caller.
return nullptr;
}
if (!js::IsOriginalScriptFunction(fun))
{
// Here, obj is a cloned function object. In this case, the
// clone's prototype may have been precompiled from brutally
// shared chrome, or else it is a lambda or nested function.
// The general case here is a function compiled against a
// different scope than the one it is parented by at runtime,
// hence the creation of a clone to carry the correct scope
// chain linkage.
//
// Since principals follow scope, we must get the object
// principal from the clone's scope chain. There are no
// reliable principals compiled into the function itself.
nsIPrincipal *result = doGetObjectPrincipal(obj);
if (!result)
*rv = NS_ERROR_FAILURE;
return result;
}
return GetScriptPrincipal(script, rv);
}
nsIPrincipal*
nsScriptSecurityManager::GetSubjectPrincipal(JSContext *cx,
nsresult* rv)

View File

@ -2116,6 +2116,10 @@ public:
return mStyleSheetChangeEventsEnabled;
}
void ObsoleteSheet(nsIURI *aSheetURI, mozilla::ErrorResult& rv);
void ObsoleteSheet(const nsAString& aSheetURI, mozilla::ErrorResult& rv);
virtual nsHTMLDocument* AsHTMLDocument() { return nullptr; }
virtual JSObject* WrapObject(JSContext *aCx,

View File

@ -9449,6 +9449,30 @@ nsDocument::CaretPositionFromPoint(float aX, float aY, nsISupports** aCaretPos)
return NS_OK;
}
void
nsIDocument::ObsoleteSheet(nsIURI *aSheetURI, ErrorResult& rv)
{
nsresult res = CSSLoader()->ObsoleteSheet(aSheetURI);
if (NS_FAILED(res)) {
rv.Throw(res);
}
}
void
nsIDocument::ObsoleteSheet(const nsAString& aSheetURI, ErrorResult& rv)
{
nsCOMPtr<nsIURI> uri;
nsresult res = NS_NewURI(getter_AddRefs(uri), aSheetURI);
if (NS_FAILED(res)) {
rv.Throw(res);
return;
}
res = CSSLoader()->ObsoleteSheet(uri);
if (NS_FAILED(res)) {
rv.Throw(res);
}
}
namespace mozilla {
// Singleton class to manage the list of fullscreen documents which are the

View File

@ -636,6 +636,8 @@ MOCHITEST_FILES_C= \
file_CSP_bug885433_blocks.html \
file_CSP_bug885433_blocks.html^headers^ \
test_bug890580.html \
test_declare_stylesheet_obsolete.html \
variable_style_sheet.sjs \
$(NULL)
# OOP tests don't work on Windows (bug 763081) or native-fennec

View File

@ -0,0 +1,94 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=713564
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 713564</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<!-- Load the variable stylesheet, which changes the color of #content. -->
<link rel="stylesheet" type="text/css" href="variable_style_sheet.sjs"/>
<script type="application/javascript">
function insertLinkToVarSSAndRun(callback) {
var ss = document.createElement("link");
ss.rel = "stylesheet";
ss.type = "text/css";
ss.href = "variable_style_sheet.sjs";
document.getElementsByTagName("head")[0].appendChild(ss);
ss.addEventListener("load", callback);
}
/** Test for Bug 713564 **/
// Then you link to that sheet, remove the link from the DOM, insert a new link to
// the same url and check that there was no new access, then call our new method,
// insert _another_ <link> to the same url, and check that this time we hit the
// server.
SimpleTest.waitForExplicitFinish();
function do_test() {
var var_sheet = document.getElementsByTagName("link")[1];
var head = document.getElementsByTagName("head")[0];
var content = document.getElementById("content");
var var_sheet_url = var_sheet.href;
var previousBgColor = window.getComputedStyle(content).
getPropertyValue("background-color");
var_sheet.parentNode.removeChild(var_sheet);
insertLinkToVarSSAndRun(function() {
is(window.getComputedStyle(content).getPropertyValue("background-color"),
previousBgColor,
"Sheet should still be the same.");
// Obsolete sheet
try {
SpecialPowers.wrap(document).obsoleteSheet(var_sheet_url);
} catch (e) {
ok(false, "obsoleteSheet should not raise an error on valid URL.");
}
insertLinkToVarSSAndRun(function() {
isnot(window.getComputedStyle(content).getPropertyValue("background-color"),
previousBgColor,
"Sheet should change after obsoleted and reinserted.");
SimpleTest.finish();
});
});
// obsoleteSheet should throw with invalid input:
try {
SpecialPowers.wrap(document).obsoleteSheet("");
ok(false, "obsoleteSheet should throw with empty string.");
} catch (e) {
ok(true, "obsoleteSheet throws with empty string.");
}
try {
SpecialPowers.wrap(document).obsoleteSheet("foo");
ok(false, "obsoleteSheet should throw with invalid URL.");
} catch (e) {
ok(true, "obsoleteSheet throws with invalid URL.");
}
try {
SpecialPowers.wrap(document).obsoleteSheet("http://www.mozilla.org");
ok(true, "obsoleteSheet should not throw with valid URL.");
} catch (e) {
ok(false, "obsoleteSheet throws with valid URL.");
}
}
</script>
</head>
<body onload="do_test();">
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=713564">Mozilla Bug 713564</a>
<p id="display"></p>
<div id="content">
<br>
<br>
</div>
<pre id="test">
</pre>
</body>
</html>

View File

@ -0,0 +1,19 @@
function handleRequest(request, response)
{
response.setHeader("Cache-Control", "no-cache", false);
response.setHeader("Content-Type", "text/css", false);
var accessCount;
var state = getState("varSSAccessCount");
if (!state) {
setState("varSSAccessCount", "0");
accessCount = 0;
} else {
setState("varSSAccessCount", (parseInt(state) + 1).toString());
accessCount = parseInt(state) + 1;
}
response.write("#content { background-color: rgb(" +
(accessCount % 256) +
", 0, 0); }");
}

View File

@ -14,9 +14,6 @@
#include "nsWrapperCacheInlines.h"
#include "mozilla/dom/HTMLPropertiesCollectionBinding.h"
DOMCI_DATA(HTMLPropertiesCollection, mozilla::dom::HTMLPropertiesCollection)
DOMCI_DATA(PropertyNodeList, mozilla::dom::PropertyNodeList)
namespace mozilla {
namespace dom {

View File

@ -357,12 +357,35 @@ nsXBLBinding::GenerateAnonymousContent()
for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
mDefaultInsertionPoint->AppendInsertedChild(child, bindingManager);
}
} else if (!mInsertionPoints.IsEmpty()) {
} else {
// It is odd to come into this code if mInsertionPoints is not empty, but
// we need to make sure to do the compatibility hack below if the bound
// node has any non <xul:template> or <xul:observer> children.
ExplicitChildIterator iter(mBoundElement);
for (nsIContent* child = iter.GetNextChild(); child; child = iter.GetNextChild()) {
XBLChildrenElement* point = FindInsertionPointForInternal(child);
if (point) {
point->AppendInsertedChild(child, bindingManager);
} else {
nsINodeInfo *ni = child->NodeInfo();
if (ni->NamespaceID() != kNameSpaceID_XUL ||
(!ni->Equals(nsGkAtoms::_template) &&
!ni->Equals(nsGkAtoms::observer))) {
// Compatibility hack. For some reason the original XBL
// implementation dropped the content of a binding if any child of
// the bound element didn't match any of the <children> in the
// binding. This became a pseudo-API that we have to maintain.
// Undo InstallAnonymousContent
UninstallAnonymousContent(doc, mContent);
// Clear out our children elements to avoid dangling references.
ClearInsertionPoints();
// Pretend as though there was no content in the binding.
mContent = nullptr;
return;
}
}
}
}

View File

@ -147,10 +147,10 @@ $(binding_header_files): .BindingGen
$(binding_cpp_files): .BindingGen
# $(binding_dependency_trackers) pick up additional dependencies via .pp files
# The rule: just brings the tracker up to date, if it's out of date, so that
# we'll know that we have to redo binding generation and flag this prerequisite
# there as being newer than the bindinggen target.
$(binding_dependency_trackers):
# Just bring it up to date, if it's out of date, so that we'll know that
# we have to redo binding generation and flag this prerequisite there as
# being newer than the bindinggen target.
@$(TOUCH) $@
$(globalgen_targets): ParserResults.pkl
@ -179,34 +179,34 @@ $(CACHE_DIR)/.done:
$(MKDIR) -p $(CACHE_DIR)
@$(TOUCH) $@
# Running GlobalGen.py updates ParserResults.pkl as a side-effect
ParserResults.pkl: $(globalgen_dependencies)
# Running GlobalGen.py updates ParserResults.pkl as a side-effect
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
$(PLY_INCLUDE) -I$(srcdir)/parser \
$(srcdir)/GlobalGen.py $(srcdir)/Bindings.conf . \
--cachedir=$(CACHE_DIR) \
$(all_webidl_files)
$(PLY_INCLUDE) -I$(srcdir)/parser \
$(srcdir)/GlobalGen.py $(srcdir)/Bindings.conf . \
--cachedir=$(CACHE_DIR) \
$(all_webidl_files)
# Make sure .deps actually exists, since we'll try to write to it from
# BindingGen.py but we're typically running in the export phase, which is
# before anyone has bothered creating .deps.
# Then, pass our long lists through files to try to avoid blowing out the
# command line.
# Next, BindingGen.py will examine the changed dependency list to figure out
# what it really needs to regenerate.
# Finally, touch the .BindingGen file so that we don't have to keep redoing
# all that until something else actually changes.
.BindingGen: $(bindinggen_dependencies) $(binding_dependency_trackers)
# Make sure .deps actually exists, since we'll try to write to it from
# BindingGen.py but we're typically running in the export phase, which
# is before anyone has bothered creating .deps.
$(MKDIR) -p .deps
# Pass our long lists through files to try to avoid blowing
# out the command line
echo $(all_webidl_files) > .all-webidl-file-list
echo $? > .changed-dependency-list
# BindingGen.py will examine the changed dependency list to figure out
# what it really needs to regenerate.
PYTHONDONTWRITEBYTECODE=1 $(PYTHON) $(topsrcdir)/config/pythonpath.py \
$(PLY_INCLUDE) -I$(srcdir)/parser \
$(srcdir)/BindingGen.py \
$(srcdir)/Bindings.conf \
$(CURDIR) \
.all-webidl-file-list \
.changed-dependency-list
# Now touch the .BindingGen file so that we don't have to keep redoing
# all that until something else actually changes.
$(PLY_INCLUDE) -I$(srcdir)/parser \
$(srcdir)/BindingGen.py \
$(srcdir)/Bindings.conf \
$(CURDIR) \
.all-webidl-file-list \
.changed-dependency-list
@$(TOUCH) $@
GARBAGE += \

View File

@ -18,6 +18,7 @@
interface StyleSheetList;
interface WindowProxy;
interface nsISupports;
interface URI;
enum VisibilityState { "hidden", "visible" };
@ -316,6 +317,11 @@ partial interface Document {
[ChromeOnly]
attribute boolean styleSheetChangeEventsEnabled;
[ChromeOnly, Throws]
void obsoleteSheet(URI sheetURI);
[ChromeOnly, Throws]
void obsoleteSheet(DOMString sheetURI);
};
// Extension to give chrome JS the ability to determine when a document was

View File

@ -10,7 +10,7 @@
#include "SkPath.h"
#define kMaxQuadSubdivide 5
#define kMaxCubicSubdivide 4
#define kMaxCubicSubdivide 7
static inline bool degenerate_vector(const SkVector& v) {
return !SkPoint::CanNormalize(v.fX, v.fY);
@ -304,12 +304,8 @@ DRAW_LINE:
bool degenerateBC = !set_normal_unitnormal(pts[1], pts[2], fRadius,
&normalBC, &unitNormalBC);
#ifndef SK_IGNORE_CUBIC_STROKE_FIX
if (subDivide <= 0) {
if (degenerateBC) {
goto DRAW_LINE;
} else {
goto DRAW_CUBIC;
}
if (--subDivide < 0) {
goto DRAW_LINE;
}
#endif
if (degenerateBC || normals_too_curvy(unitNormalAB, unitNormalBC) ||
@ -330,9 +326,6 @@ DRAW_LINE:
// normals for CD
this->cubic_to(&tmp[3], norm, unit, &dummy, &unitDummy, subDivide);
} else {
#ifndef SK_IGNORE_CUBIC_STROKE_FIX
DRAW_CUBIC:
#endif
SkVector normalB, normalC;
// need normals to inset/outset the off-curve pts B and C

View File

@ -217,6 +217,14 @@ NS_GFX_(bool) NS_ColorNameToRGB(const nsAString& aColorName, nscolor* aResult)
return false;
}
// Returns kColorNames, an array of all possible color names, and sets
// *aSizeArray to the size of that array. Do NOT call free() on this array.
NS_GFX_(const char * const *) NS_AllColorNames(size_t *aSizeArray)
{
*aSizeArray = ArrayLength(kColorNames);
return kColorNames;
}
// Macro to blend two colors
//
// equivalent to target = (bg*(255-fgalpha) + fg*fgalpha)/255

View File

@ -66,6 +66,10 @@ NS_GFX_(bool) NS_LooseHexToRGB(const nsString& aBuf, nscolor* aResult);
// otherwise return false.
NS_GFX_(bool) NS_ColorNameToRGB(const nsAString& aBuf, nscolor* aResult);
// Returns an array of all possible color names, and sets
// *aSizeArray to the size of that array. Do NOT call |free()| on this array.
NS_GFX_(const char * const *) NS_AllColorNames(size_t *aSizeArray);
// function to convert from HSL color space to RGB color space
// the float parameters are all expected to be in the range 0-1
NS_GFX_(nscolor) NS_HSL2RGB(float h, float s, float l);

264
image/src/FrameAnimator.cpp Normal file
View File

@ -0,0 +1,264 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "FrameAnimator.h"
#include "imgIContainer.h"
using namespace mozilla::image;
using namespace mozilla;
FrameAnimator::FrameAnimator(FrameBlender& aFrameBlender)
: mCurrentAnimationFrameIndex(0)
, mLoopCount(-1)
, mFrameBlender(aFrameBlender)
, mAnimationMode(imgIContainer::kNormalAnimMode)
, mDoneDecoding(false)
{
}
uint32_t
FrameAnimator::GetSingleLoopTime() const
{
// If we aren't done decoding, we don't know the image's full play time.
if (!mDoneDecoding) {
return 0;
}
// If we're not looping, a single loop time has no meaning
if (mAnimationMode != imgIContainer::kNormalAnimMode) {
return 0;
}
uint32_t looptime = 0;
for (uint32_t i = 0; i < mFrameBlender.GetNumFrames(); ++i) {
int32_t timeout = mFrameBlender.RawGetFrame(i)->GetTimeout();
if (timeout > 0) {
looptime += static_cast<uint32_t>(timeout);
} else {
// If we have a frame that never times out, we're probably in an error
// case, but let's handle it more gracefully.
NS_WARNING("Negative frame timeout - how did this happen?");
return 0;
}
}
return looptime;
}
TimeStamp
FrameAnimator::GetCurrentImgFrameEndTime() const
{
imgFrame* currentFrame = mFrameBlender.RawGetFrame(mCurrentAnimationFrameIndex);
TimeStamp currentFrameTime = mCurrentAnimationFrameTime;
int64_t timeout = currentFrame->GetTimeout();
if (timeout < 0) {
// We need to return a sentinel value in this case, because our logic
// doesn't work correctly if we have a negative timeout value. The reason
// this positive infinity was chosen was because it works with the loop in
// RequestRefresh() below.
return TimeStamp() +
TimeDuration::FromMilliseconds(static_cast<double>(UINT64_MAX));
}
TimeDuration durationOfTimeout =
TimeDuration::FromMilliseconds(static_cast<double>(timeout));
TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
return currentFrameEndTime;
}
FrameAnimator::RefreshResult
FrameAnimator::AdvanceFrame(TimeStamp aTime)
{
NS_ASSERTION(aTime <= TimeStamp::Now(),
"Given time appears to be in the future");
uint32_t currentFrameIndex = mCurrentAnimationFrameIndex;
uint32_t nextFrameIndex = currentFrameIndex + 1;
uint32_t timeout = 0;
RefreshResult ret;
// If we're done decoding, we know we've got everything we're going to get.
// If we aren't, we only display fully-downloaded frames; everything else
// gets delayed.
bool needToWait = !mDoneDecoding &&
mFrameBlender.RawGetFrame(nextFrameIndex) &&
!mFrameBlender.RawGetFrame(nextFrameIndex)->ImageComplete();
if (needToWait) {
// Uh oh, the frame we want to show is currently being decoded (partial)
// Wait until the next refresh driver tick and try again
return ret;
} else {
// If we're done decoding the next frame, go ahead and display it now and
// reinit with the next frame's delay time.
if (mFrameBlender.GetNumFrames() == nextFrameIndex) {
// End of Animation, unless we are looping forever
// If animation mode is "loop once", it's time to stop animating
if (mAnimationMode == imgIContainer::kLoopOnceAnimMode || mLoopCount == 0) {
ret.animationFinished = true;
}
nextFrameIndex = 0;
if (mLoopCount > 0) {
mLoopCount--;
}
// If we're done, exit early.
if (ret.animationFinished) {
return ret;
}
}
timeout = mFrameBlender.GetFrame(nextFrameIndex)->GetTimeout();
}
// Bad data
if (!(timeout > 0)) {
ret.animationFinished = true;
ret.error = true;
}
if (nextFrameIndex == 0) {
ret.dirtyRect = mFirstFrameRefreshArea;
} else {
// Change frame
if (!mFrameBlender.DoBlend(&ret.dirtyRect, currentFrameIndex, nextFrameIndex)) {
// something went wrong, move on to next
NS_WARNING("FrameAnimator::AdvanceFrame(): Compositing of frame failed");
mFrameBlender.RawGetFrame(nextFrameIndex)->SetCompositingFailed(true);
mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime();
mCurrentAnimationFrameIndex = nextFrameIndex;
ret.error = true;
return ret;
}
mFrameBlender.RawGetFrame(nextFrameIndex)->SetCompositingFailed(false);
}
mCurrentAnimationFrameTime = GetCurrentImgFrameEndTime();
// If we can get closer to the current time by a multiple of the image's loop
// time, we should.
uint32_t loopTime = GetSingleLoopTime();
if (loopTime > 0) {
TimeDuration delay = aTime - mCurrentAnimationFrameTime;
if (delay.ToMilliseconds() > loopTime) {
// Explicitly use integer division to get the floor of the number of
// loops.
uint32_t loops = static_cast<uint32_t>(delay.ToMilliseconds()) / loopTime;
mCurrentAnimationFrameTime += TimeDuration::FromMilliseconds(loops * loopTime);
}
}
// Set currentAnimationFrameIndex at the last possible moment
mCurrentAnimationFrameIndex = nextFrameIndex;
// If we're here, we successfully advanced the frame.
ret.frameAdvanced = true;
return ret;
}
FrameAnimator::RefreshResult
FrameAnimator::RequestRefresh(const mozilla::TimeStamp& aTime)
{
// only advance the frame if the current time is greater than or
// equal to the current frame's end time.
TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
// By default, an empty RefreshResult.
RefreshResult ret;
while (currentFrameEndTime <= aTime) {
TimeStamp oldFrameEndTime = currentFrameEndTime;
RefreshResult frameRes = AdvanceFrame(aTime);
// Accumulate our result for returning to callers.
ret.Accumulate(frameRes);
currentFrameEndTime = GetCurrentImgFrameEndTime();
// if we didn't advance a frame, and our frame end time didn't change,
// then we need to break out of this loop & wait for the frame(s)
// to finish downloading
if (!frameRes.frameAdvanced && (currentFrameEndTime == oldFrameEndTime)) {
break;
}
}
return ret;
}
void
FrameAnimator::ResetAnimation()
{
mCurrentAnimationFrameIndex = 0;
}
void
FrameAnimator::SetDoneDecoding(bool aDone)
{
mDoneDecoding = aDone;
}
void
FrameAnimator::SetAnimationMode(uint16_t aAnimationMode)
{
mAnimationMode = aAnimationMode;
}
void
FrameAnimator::InitAnimationFrameTimeIfNecessary()
{
if (mCurrentAnimationFrameTime.IsNull()) {
mCurrentAnimationFrameTime = TimeStamp::Now();
}
}
void
FrameAnimator::SetAnimationFrameTime(const TimeStamp& aTime)
{
mCurrentAnimationFrameTime = aTime;
}
void
FrameAnimator::SetFirstFrameRefreshArea(const nsIntRect& aRect)
{
mFirstFrameRefreshArea = aRect;
}
void
FrameAnimator::UnionFirstFrameRefreshArea(const nsIntRect& aRect)
{
mFirstFrameRefreshArea.UnionRect(mFirstFrameRefreshArea, aRect);
}
void
FrameAnimator::SetLoopCount(int loopcount)
{
mLoopCount = loopcount;
}
uint32_t
FrameAnimator::GetCurrentAnimationFrameIndex() const
{
return mCurrentAnimationFrameIndex;
}
nsIntRect
FrameAnimator::GetFirstFrameRefreshArea() const
{
return mFirstFrameRefreshArea;
}

178
image/src/FrameAnimator.h Normal file
View File

@ -0,0 +1,178 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef mozilla_imagelib_FrameAnimator_h_
#define mozilla_imagelib_FrameAnimator_h_
#include "mozilla/TimeStamp.h"
#include "FrameBlender.h"
#include "nsRect.h"
namespace mozilla {
namespace image {
class FrameAnimator
{
public:
FrameAnimator(FrameBlender& aBlender);
/**
* Return value from RequestRefresh. Tells callers what happened in that call
* to RequestRefresh.
*/
struct RefreshResult
{
// The dirty rectangle to be re-drawn after this RequestRefresh().
nsIntRect dirtyRect;
// Whether any frame changed, and hence the dirty rect was set.
bool frameAdvanced : 1;
// Whether the animation has finished playing.
bool animationFinished : 1;
// Whether an error has occurred when trying to advance a frame. Note that
// errors do not, on their own, end the animation.
bool error : 1;
RefreshResult()
: frameAdvanced(false)
, animationFinished(false)
, error(false)
{}
void Accumulate(const RefreshResult& other)
{
frameAdvanced = frameAdvanced || other.frameAdvanced;
animationFinished = animationFinished || other.animationFinished;
error = error || other.error;
dirtyRect = dirtyRect.Union(other.dirtyRect);
}
};
/**
* Re-evaluate what frame we're supposed to be on, and do whatever blending
* is necessary to get us to that frame.
*
* Returns the result of that blending, including whether the current frame
* changed and what the resulting dirty rectangle is.
*/
RefreshResult RequestRefresh(const mozilla::TimeStamp& aTime);
/**
* Call when this image is finished decoding so we know that there aren't any
* more frames coming.
*/
void SetDoneDecoding(bool aDone);
/**
* Call when you need to re-start animating. Ensures we start from the first
* frame.
*/
void ResetAnimation();
/**
* Number of times to loop the image.
* @note -1 means forever.
*/
void SetLoopCount(int32_t aLoopCount);
/**
* The animation mode of the image.
*
* Constants defined in imgIContainer.idl.
*/
void SetAnimationMode(uint16_t aAnimationMode);
/**
* Set the area to refresh when we loop around to the first frame.
*/
void SetFirstFrameRefreshArea(const nsIntRect& aRect);
/**
* Union the area to refresh when we loop around to the first frame with this
* rect.
*/
void UnionFirstFrameRefreshArea(const nsIntRect& aRect);
/**
* If the animation frame time has not yet been set, set it to
* TimeStamp::Now().
*/
void InitAnimationFrameTimeIfNecessary();
/**
* Set the animation frame time to @aTime.
*/
void SetAnimationFrameTime(const TimeStamp& aTime);
/**
* The current frame we're on, from 0 to (numFrames - 1).
*/
uint32_t GetCurrentAnimationFrameIndex() const;
/**
* Get the area we refresh when we loop around to the first frame.
*/
nsIntRect GetFirstFrameRefreshArea() const;
private: // methods
/**
* Gets the length of a single loop of this image, in milliseconds.
*
* If this image is not finished decoding, is not animated, or it is animated
* but does not loop, returns 0.
*/
uint32_t GetSingleLoopTime() const;
/**
* Advances the animation. Typically, this will advance a single frame, but it
* may advance multiple frames. This may happen if we have infrequently
* "ticking" refresh drivers (e.g. in background tabs), or extremely short-
* lived animation frames.
*
* @param aTime the time that the animation should advance to. This will
* typically be <= TimeStamp::Now().
*
* @returns a RefreshResult that shows whether the frame was successfully
* advanced, and its resulting dirty rect.
*/
RefreshResult AdvanceFrame(mozilla::TimeStamp aTime);
/**
* Get the time the frame we're currently displaying is supposed to end.
*
* In the error case, returns an "infinity" timestamp.
*/
mozilla::TimeStamp GetCurrentImgFrameEndTime() const;
private: // data
//! Area of the first frame that needs to be redrawn on subsequent loops.
nsIntRect mFirstFrameRefreshArea;
//! the time that the animation advanced to the current frame
TimeStamp mCurrentAnimationFrameTime;
//! The current frame index we're on. 0 to (numFrames - 1).
uint32_t mCurrentAnimationFrameIndex;
//! number of loops remaining before animation stops (-1 no stop)
int32_t mLoopCount;
//! All the frames of the image, shared with our owner
FrameBlender& mFrameBlender;
//! The animation mode of this image. Constants defined in imgIContainer.
uint16_t mAnimationMode;
//! Whether this image is done being decoded.
bool mDoneDecoding;
};
} // namespace image
} // namespace mozilla
#endif /* mozilla_imagelib_FrameAnimator_h_ */

View File

@ -24,6 +24,7 @@
#include "nsIThreadPool.h"
#include "nsXPCOMCIDInternal.h"
#include "nsIObserverService.h"
#include "FrameAnimator.h"
#include "nsPNGDecoder.h"
#include "nsGIFDecoder2.h"
@ -390,7 +391,6 @@ RasterImage::RasterImage(imgStatusTracker* aStatusTracker,
mFrameDecodeFlags(DECODE_FLAGS_DEFAULT),
mMultipartDecodedFrame(nullptr),
mAnim(nullptr),
mLoopCount(-1),
mLockCount(0),
mDecodeCount(0),
#ifdef DEBUG
@ -528,135 +528,6 @@ RasterImage::Init(const char* aMimeType,
return NS_OK;
}
uint32_t
RasterImage::GetSingleLoopTime() const
{
if (!mAnim) {
return 0;
}
// If we aren't done decoding, we don't know the image's full play time.
if (!mHasBeenDecoded) {
return 0;
}
// If we're not looping, a single loop time has no meaning
if (mLoopCount == 0) {
return 0;
}
uint32_t looptime = 0;
for (uint32_t i = 0; i < GetNumFrames(); ++i) {
int32_t timeout = mFrameBlender.RawGetFrame(i)->GetTimeout();
if (timeout > 0) {
looptime += static_cast<uint32_t>(timeout);
} else {
// If we have a frame that never times out, we're probably in an error
// case, but let's handle it more gracefully.
NS_WARNING("Negative frame timeout - how did this happen?");
return 0;
}
}
return looptime;
}
bool
RasterImage::AdvanceFrame(TimeStamp aTime, nsIntRect* aDirtyRect)
{
NS_ASSERTION(aTime <= TimeStamp::Now(),
"Given time appears to be in the future");
uint32_t currentFrameIndex = mAnim->currentAnimationFrameIndex;
uint32_t nextFrameIndex = mAnim->currentAnimationFrameIndex + 1;
uint32_t timeout = 0;
// Figure out if we have the next full frame. This is more complicated than
// just checking GetNumFrames() because decoders append their frames
// before they're filled in.
NS_ABORT_IF_FALSE(mDecoder || nextFrameIndex <= GetNumFrames(),
"How did we get 2 indices too far by incrementing?");
// If we don't have a decoder, we know we've got everything we're going to
// get. If we do, we only display fully-downloaded frames; everything else
// gets delayed.
bool haveFullNextFrame = (mMultipart && mBytesDecoded == 0) || !mDecoder ||
nextFrameIndex < mDecoder->GetCompleteFrameCount();
// If we're done decoding the next frame, go ahead and display it now and
// reinit with the next frame's delay time.
if (haveFullNextFrame) {
if (GetNumFrames() == nextFrameIndex) {
// End of Animation, unless we are looping forever
// If animation mode is "loop once", it's time to stop animating
if (mAnimationMode == kLoopOnceAnimMode || mLoopCount == 0) {
mAnimationFinished = true;
EvaluateAnimation();
}
nextFrameIndex = 0;
if (mLoopCount > 0) {
mLoopCount--;
}
if (!mAnimating) {
// break out early if we are actually done animating
return false;
}
}
timeout = mFrameBlender.GetFrame(nextFrameIndex)->GetTimeout();
} else {
// Uh oh, the frame we want to show is currently being decoded (partial)
// Wait until the next refresh driver tick and try again
return false;
}
if (!(timeout > 0)) {
mAnimationFinished = true;
EvaluateAnimation();
}
if (nextFrameIndex == 0) {
*aDirtyRect = mAnim->firstFrameRefreshArea;
} else {
// Change frame
if (!mFrameBlender.DoBlend(aDirtyRect, currentFrameIndex, nextFrameIndex)) {
// something went wrong, move on to next
NS_WARNING("RasterImage::AdvanceFrame(): Compositing of frame failed");
mFrameBlender.RawGetFrame(nextFrameIndex)->SetCompositingFailed(true);
mAnim->currentAnimationFrameTime = GetCurrentImgFrameEndTime();
mAnim->currentAnimationFrameIndex = nextFrameIndex;
return false;
}
mFrameBlender.RawGetFrame(nextFrameIndex)->SetCompositingFailed(false);
}
mAnim->currentAnimationFrameTime = GetCurrentImgFrameEndTime();
// If we can get closer to the current time by a multiple of the image's loop
// time, we should.
uint32_t loopTime = GetSingleLoopTime();
if (loopTime > 0) {
TimeDuration delay = aTime - mAnim->currentAnimationFrameTime;
if (delay.ToMilliseconds() > loopTime) {
// Explicitly use integer division to get the floor of the number of
// loops.
uint32_t loops = static_cast<uint32_t>(delay.ToMilliseconds()) / loopTime;
mAnim->currentAnimationFrameTime += TimeDuration::FromMilliseconds(loops * loopTime);
}
}
// Set currentAnimationFrameIndex at the last possible moment
mAnim->currentAnimationFrameIndex = nextFrameIndex;
return true;
}
//******************************************************************************
// [notxpcom] void requestRefresh ([const] in TimeStamp aTime);
NS_IMETHODIMP_(void)
@ -668,34 +539,12 @@ RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
EvaluateAnimation();
// only advance the frame if the current time is greater than or
// equal to the current frame's end time.
TimeStamp currentFrameEndTime = GetCurrentImgFrameEndTime();
bool frameAdvanced = false;
// The dirtyRect variable will contain an accumulation of the sub-rectangles
// that are dirty for each frame we advance in AdvanceFrame().
nsIntRect dirtyRect;
while (currentFrameEndTime <= aTime) {
TimeStamp oldFrameEndTime = currentFrameEndTime;
nsIntRect frameDirtyRect;
bool didAdvance = AdvanceFrame(aTime, &frameDirtyRect);
frameAdvanced = frameAdvanced || didAdvance;
currentFrameEndTime = GetCurrentImgFrameEndTime();
// Accumulate the dirty area.
dirtyRect = dirtyRect.Union(frameDirtyRect);
// if we didn't advance a frame, and our frame end time didn't change,
// then we need to break out of this loop & wait for the frame(s)
// to finish downloading
if (!didAdvance && (currentFrameEndTime == oldFrameEndTime)) {
break;
}
FrameAnimator::RefreshResult res;
if (mAnim) {
res = mAnim->RequestRefresh(aTime);
}
if (frameAdvanced) {
if (res.frameAdvanced) {
// Notify listeners that our frame has actually changed, but do this only
// once for all frames that we've now passed (if AdvanceFrame() was called
// more than once).
@ -708,7 +557,12 @@ RasterImage::RequestRefresh(const mozilla::TimeStamp& aTime)
// Explicitly call this on mStatusTracker so we're sure to not interfere
// with the decoding process
if (mStatusTracker)
mStatusTracker->FrameChanged(&dirtyRect);
mStatusTracker->FrameChanged(&res.dirtyRect);
}
if (res.animationFinished) {
mAnimationFinished = true;
EvaluateAnimation();
}
}
@ -833,34 +687,11 @@ uint32_t
RasterImage::GetCurrentImgFrameIndex() const
{
if (mAnim)
return mAnim->currentAnimationFrameIndex;
return mAnim->GetCurrentAnimationFrameIndex();
return 0;
}
TimeStamp
RasterImage::GetCurrentImgFrameEndTime() const
{
imgFrame* currentFrame = mFrameBlender.RawGetFrame(mAnim->currentAnimationFrameIndex);
TimeStamp currentFrameTime = mAnim->currentAnimationFrameTime;
int64_t timeout = currentFrame->GetTimeout();
if (timeout < 0) {
// We need to return a sentinel value in this case, because our logic
// doesn't work correctly if we have a negative timeout value. The reason
// this positive infinity was chosen was because it works with the loop in
// RequestRefresh() above.
return TimeStamp() +
TimeDuration::FromMilliseconds(static_cast<double>(UINT64_MAX));
}
TimeDuration durationOfTimeout =
TimeDuration::FromMilliseconds(static_cast<double>(timeout));
TimeStamp currentFrameEndTime = currentFrameTime + durationOfTimeout;
return currentFrameEndTime;
}
imgFrame*
RasterImage::GetCurrentImgFrame()
{
@ -1230,6 +1061,30 @@ RasterImage::OutOfProcessSizeOfDecoded() const
NULL);
}
void
RasterImage::EnsureAnimExists()
{
if (!mAnim) {
// Create the animation context
mAnim = new FrameAnimator(mFrameBlender);
// We don't support discarding animated images (See bug 414259).
// Lock the image and throw away the key.
//
// Note that this is inefficient, since we could get rid of the source
// data too. However, doing this is actually hard, because we're probably
// calling ensureAnimExists mid-decode, and thus we're decoding out of
// the source buffer. Since we're going to fix this anyway later, and
// since we didn't kill the source data in the old world either, locking
// is acceptable for the moment.
LockImage();
// Notify our observers that we are starting animation.
CurrentStatusTracker().RecordImageIsAnimated();
}
}
nsresult
RasterImage::InternalAddFrameHelper(uint32_t framenum, imgFrame *aFrame,
uint8_t **imageData, uint32_t *imageLength,
@ -1310,15 +1165,13 @@ RasterImage::InternalAddFrame(uint32_t framenum,
int32_t frameDisposalMethod = mFrameBlender.RawGetFrame(0)->GetFrameDisposalMethod();
if (frameDisposalMethod == FrameBlender::kDisposeClear ||
frameDisposalMethod == FrameBlender::kDisposeRestorePrevious)
mAnim->firstFrameRefreshArea = mFrameBlender.RawGetFrame(0)->GetRect();
mAnim->SetFirstFrameRefreshArea(mFrameBlender.RawGetFrame(0)->GetRect());
}
// Calculate firstFrameRefreshArea
// Some gifs are huge but only have a small area that they animate
// We only need to refresh that small area when Frame 0 comes around again
nsIntRect frameRect = frame->GetRect();
mAnim->firstFrameRefreshArea.UnionRect(mAnim->firstFrameRefreshArea,
frameRect);
mAnim->UnionFirstFrameRefreshArea(frame->GetRect());
rv = InternalAddFrameHelper(framenum, frame.forget(), imageData, imageLength,
paletteData, paletteLength, aRetFrame);
@ -1541,9 +1394,22 @@ RasterImage::DecodingComplete()
}
}
if (mAnim) {
mAnim->SetDoneDecoding(true);
}
return NS_OK;
}
NS_IMETHODIMP
RasterImage::SetAnimationMode(uint16_t aAnimationMode)
{
if (mAnim) {
mAnim->SetAnimationMode(aAnimationMode);
}
return SetAnimationModeInternal(aAnimationMode);
}
//******************************************************************************
/* void StartAnimation () */
nsresult
@ -1565,9 +1431,7 @@ RasterImage::StartAnimation()
// We need to set the time that this initial frame was first displayed, as
// this is used in AdvanceFrame().
if (mAnim->currentAnimationFrameTime.IsNull()) {
mAnim->currentAnimationFrameTime = TimeStamp::Now();
}
mAnim->InitAnimationFrameTimeIfNecessary();
}
return NS_OK;
@ -1595,7 +1459,7 @@ RasterImage::ResetAnimation()
return NS_ERROR_FAILURE;
if (mAnimationMode == kDontAnimMode ||
!mAnim || mAnim->currentAnimationFrameIndex == 0)
!mAnim || mAnim->GetCurrentAnimationFrameIndex() == 0)
return NS_OK;
mAnimationFinished = false;
@ -1604,8 +1468,10 @@ RasterImage::ResetAnimation()
StopAnimation();
mFrameBlender.ResetAnimation();
if (mAnim) {
mAnim->ResetAnimation();
}
mAnim->currentAnimationFrameIndex = 0;
UpdateImageContainer();
// Note - We probably want to kick off a redecode somewhere around here when
@ -1613,7 +1479,8 @@ RasterImage::ResetAnimation()
// Update display if we were animating before
if (mAnimating && mStatusTracker) {
mStatusTracker->FrameChanged(&(mAnim->firstFrameRefreshArea));
nsIntRect rect = mAnim->GetFirstFrameRefreshArea();
mStatusTracker->FrameChanged(&rect);
}
if (ShouldAnimate()) {
@ -1635,7 +1502,7 @@ RasterImage::SetAnimationStartTime(const mozilla::TimeStamp& aTime)
if (mError || mAnimating || !mAnim)
return;
mAnim->currentAnimationFrameTime = aTime;
mAnim->SetAnimationFrameTime(aTime);
}
NS_IMETHODIMP_(float)
@ -1644,7 +1511,7 @@ RasterImage::GetFrameIndex(uint32_t aWhichFrame)
MOZ_ASSERT(aWhichFrame <= FRAME_MAX_VALUE, "Invalid argument");
return (aWhichFrame == FRAME_FIRST || !mAnim)
? 0.0f
: mAnim->currentAnimationFrameIndex;
: mAnim->GetCurrentAnimationFrameIndex();
}
void
@ -1653,11 +1520,9 @@ RasterImage::SetLoopCount(int32_t aLoopCount)
if (mError)
return;
// -1 infinite
// 0 no looping, one iteration
// 1 one loop, two iterations
// ...
mLoopCount = aLoopCount;
if (mAnim) {
mAnim->SetLoopCount(aLoopCount);
}
}
nsresult
@ -1929,6 +1794,10 @@ RasterImage::OnNewSourceData()
mWantFullDecode = true;
mDecodeRequest = nullptr;
if (mAnim) {
mAnim->SetDoneDecoding(false);
}
// We always need the size first.
rv = InitDecoder(/* aDoSizeDecode = */ true);
CONTAINER_ENSURE_SUCCESS(rv);

View File

@ -120,10 +120,6 @@ class nsIThreadPool;
* @par
* The mAnim structure has members only needed for animated images, so
* it's not allocated until the second frame is added.
*
* @note
* mAnimationMode and mLoopCount are not in the mAnim structure because
* they have public setters.
*/
class ScaleRequest;
@ -137,6 +133,7 @@ class Image;
namespace image {
class Decoder;
class FrameAnimator;
class RasterImage : public ImageResource
, public nsIProperties
@ -324,20 +321,6 @@ private:
nsresult OnImageDataCompleteCore(nsIRequest* aRequest, nsISupports*, nsresult aStatus);
struct Anim
{
//! Area of the first frame that needs to be redrawn on subsequent loops.
nsIntRect firstFrameRefreshArea;
uint32_t currentAnimationFrameIndex; // 0 to numFrames-1
// the time that the animation advanced to the current frame
TimeStamp currentAnimationFrameTime;
Anim() :
currentAnimationFrameIndex(0)
{}
};
/**
* Each RasterImage has a pointer to one or zero heap-allocated
* DecodeRequests.
@ -545,33 +528,6 @@ private:
uint32_t aFlags,
gfxImageSurface **_retval);
/**
* Advances the animation. Typically, this will advance a single frame, but it
* may advance multiple frames. This may happen if we have infrequently
* "ticking" refresh drivers (e.g. in background tabs), or extremely short-
* lived animation frames.
*
* @param aTime the time that the animation should advance to. This will
* typically be <= TimeStamp::Now().
*
* @param [out] aDirtyRect a pointer to an nsIntRect which encapsulates the
* area to be repainted after the frame is advanced.
*
* @returns true, if the frame was successfully advanced, false if it was not
* able to be advanced (e.g. the frame to which we want to advance is
* still decoding). Note: If false is returned, then aDirtyRect will
* remain unmodified.
*/
bool AdvanceFrame(mozilla::TimeStamp aTime, nsIntRect* aDirtyRect);
/**
* Gets the length of a single loop of this image, in milliseconds.
*
* If this image is not finished decoding, is not animated, or it is animated
* but does not loop, returns 0.
*/
uint32_t GetSingleLoopTime() const;
/**
* Deletes and nulls out the frame in mFrames[framenum].
*
@ -587,33 +543,11 @@ private:
imgFrame* GetDrawableImgFrame(uint32_t framenum);
imgFrame* GetCurrentImgFrame();
uint32_t GetCurrentImgFrameIndex() const;
mozilla::TimeStamp GetCurrentImgFrameEndTime() const;
size_t SizeOfDecodedWithComputedFallbackIfHeap(gfxASurface::MemoryLocation aLocation,
mozilla::MallocSizeOf aMallocSizeOf) const;
inline void EnsureAnimExists()
{
if (!mAnim) {
// Create the animation context
mAnim = new Anim();
// We don't support discarding animated images (See bug 414259).
// Lock the image and throw away the key.
//
// Note that this is inefficient, since we could get rid of the source
// data too. However, doing this is actually hard, because we're probably
// calling ensureAnimExists mid-decode, and thus we're decoding out of
// the source buffer. Since we're going to fix this anyway later, and
// since we didn't kill the source data in the old world either, locking
// is acceptable for the moment.
LockImage();
// Notify our observers that we are starting animation.
CurrentStatusTracker().RecordImageIsAnimated();
}
}
void EnsureAnimExists();
nsresult InternalAddFrameHelper(uint32_t framenum, imgFrame *frame,
uint8_t **imageData, uint32_t *imageLength,
@ -671,10 +605,7 @@ private: // data
// IMPORTANT: if you use mAnim in a method, call EnsureImageIsDecoded() first to ensure
// that the frames actually exist (they may have been discarded to save memory, or
// we maybe decoding on draw).
RasterImage::Anim* mAnim;
//! # loops remaining before animation stops (-1 no stop)
int32_t mLoopCount;
FrameAnimator* mAnim;
// Discard members
uint32_t mLockCount;
@ -791,10 +722,6 @@ inline NS_IMETHODIMP RasterImage::GetAnimationMode(uint16_t *aAnimationMode) {
return GetAnimationModeInternal(aAnimationMode);
}
inline NS_IMETHODIMP RasterImage::SetAnimationMode(uint16_t aAnimationMode) {
return SetAnimationModeInternal(aAnimationMode);
}
// Asynchronous Decode Requestor
//
// We use this class when someone calls requestDecode() from within a decode

View File

@ -509,7 +509,7 @@ nsresult imgFrame::ImageUpdated(const nsIntRect &aUpdateRect)
return NS_OK;
}
bool imgFrame::GetIsDirty()
bool imgFrame::GetIsDirty() const
{
MutexAutoLock lock(mDirtyMutex);
return mDirty;
@ -796,8 +796,11 @@ void imgFrame::SetBlendMethod(int32_t aBlendMethod)
mBlendMethod = (int8_t)aBlendMethod;
}
// This can be called from any thread.
bool imgFrame::ImageComplete() const
{
MutexAutoLock lock(mDirtyMutex);
return mDecoded.IsEqualInterior(nsIntRect(mOffset, mSize));
}

View File

@ -41,7 +41,7 @@ public:
uint32_t aImageFlags = imgIContainer::FLAG_NONE);
nsresult ImageUpdated(const nsIntRect &aUpdateRect);
bool GetIsDirty();
bool GetIsDirty() const;
nsIntRect GetRect() const;
gfxASurface::gfxImageFormat GetFormat() const;
@ -152,7 +152,7 @@ private: // data
nsIntRect mDecoded;
mozilla::Mutex mDirtyMutex;
mutable mozilla::Mutex mDirtyMutex;
// The palette and image data for images that are paletted, since Cairo
// doesn't support these images.

View File

@ -17,6 +17,7 @@ CPP_SOURCES += [
'ClippedImage.cpp',
'Decoder.cpp',
'DiscardTracker.cpp',
'FrameAnimator.cpp',
'FrameBlender.cpp',
'FrameSequence.cpp',
'FrozenImage.cpp',

View File

@ -210,8 +210,6 @@ class Vector : private AllocPolicy
bool growStorageBy(size_t incr);
bool convertToHeapStorage(size_t newCap);
template <bool InitNewElems> inline bool growByImpl(size_t inc);
/* magic constants */
static const int sMaxInlineBytes = 1024;
@ -756,9 +754,8 @@ Vector<T,N,AP>::shrinkBy(size_t incr)
}
template <class T, size_t N, class AP>
template <bool InitNewElems>
MOZ_ALWAYS_INLINE bool
Vector<T,N,AP>::growByImpl(size_t incr)
Vector<T,N,AP>::growBy(size_t incr)
{
REENTRANCY_GUARD_ET_AL;
if (incr > mCapacity - mLength && !growStorageBy(incr))
@ -766,8 +763,7 @@ Vector<T,N,AP>::growByImpl(size_t incr)
MOZ_ASSERT(mLength + incr <= mCapacity);
T *newend = endNoCheck() + incr;
if (InitNewElems)
Impl::initialize(endNoCheck(), newend);
Impl::initialize(endNoCheck(), newend);
mLength += incr;
#ifdef DEBUG
if (mLength > mReserved)
@ -776,18 +772,21 @@ Vector<T,N,AP>::growByImpl(size_t incr)
return true;
}
template <class T, size_t N, class AP>
MOZ_ALWAYS_INLINE bool
Vector<T,N,AP>::growBy(size_t incr)
{
return growByImpl<true>(incr);
}
template <class T, size_t N, class AP>
MOZ_ALWAYS_INLINE bool
Vector<T,N,AP>::growByUninitialized(size_t incr)
{
return growByImpl<false>(incr);
REENTRANCY_GUARD_ET_AL;
if (incr > mCapacity - mLength && !growStorageBy(incr))
return false;
MOZ_ASSERT(mLength + incr <= mCapacity);
mLength += incr;
#ifdef DEBUG
if (mLength > mReserved)
mReserved = mLength;
#endif
return true;
}
template <class T, size_t N, class AP>

View File

@ -604,6 +604,7 @@ if test -n "$_WIN32_MSVC"; then
# Common to all MSVC environments:
AC_DEFINE(HAVE_LOCALECONV)
AC_DEFINE(HAVE_HYPOT)
fi
fi # COMPILE_ENVIRONMENT
@ -2752,6 +2753,12 @@ AC_CHECK_FUNCS([fchmod flockfile getc_unlocked _getc_nolock getpagesize \
lchown localtime_r lstat64 memmove random rint sbrk snprintf \
stat64 statvfs statvfs64 strerror strtok_r truncate64])
dnl Checks for math functions.
dnl ========================================================
AC_CHECK_FUNCS(log2 log1p expm1 sqrt1pm1 acosh asinh atanh hypot trunc cbrt)
dnl check for wcrtomb/mbrtowc
dnl =======================================================================
if test -z "$MACOS_DEPLOYMENT_TARGET" || test "$MACOS_DEPLOYMENT_TARGET" -ge "100300"; then

View File

@ -358,10 +358,9 @@ frontend::CompileScript(JSContext *cx, HandleObject scopeChain,
}
bool
frontend::CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *lazy,
const jschar *chars, size_t length)
frontend::CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *chars, size_t length)
{
JS_ASSERT(cx->compartment() == fun->compartment());
JS_ASSERT(cx->compartment() == lazy->function()->compartment());
CompileOptions options(cx, lazy->version());
options.setPrincipals(cx->compartment()->principals)
@ -377,6 +376,7 @@ frontend::CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *laz
uint32_t staticLevel = lazy->staticLevel(cx);
Rooted<JSFunction*> fun(cx, lazy->function());
ParseNode *pn = parser.standaloneLazyFunction(fun, staticLevel, lazy->strict());
if (!pn)
return false;

View File

@ -26,8 +26,7 @@ CompileScript(JSContext *cx, HandleObject scopeChain, HandleScript evalCaller,
SourceCompressionToken *extraSct = NULL);
bool
CompileLazyFunction(JSContext *cx, HandleFunction fun, LazyScript *lazy,
const jschar *chars, size_t length);
CompileLazyFunction(JSContext *cx, LazyScript *lazy, const jschar *chars, size_t length);
bool
CompileFunctionBody(JSContext *cx, MutableHandleFunction fun, CompileOptions options,

View File

@ -2046,11 +2046,6 @@ EmitElemOperands(ExclusiveContext *cx, ParseNode *pn, JSOp op, BytecodeEmitter *
right = pn->pn_right;
}
if (op == JSOP_GETELEM && left->isKind(PNK_NAME) && right->isKind(PNK_NUMBER)) {
if (!BindNameToSlot(cx, bce, left))
return false;
}
if (!EmitTree(cx, bce, left))
return false;
@ -2731,65 +2726,90 @@ EmitDestructuringLHS(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn,
if (Emit1(cx, bce, JSOP_POP) < 0)
return false;
}
} else if (emitOption == PushInitialValues) {
// The lhs is a simple name so the to-be-destructured value is
// its initial value and there is nothing to do.
JS_ASSERT(pn->getOp() == JSOP_GETLOCAL);
JS_ASSERT(pn->pn_dflags & PND_BOUND);
} else {
if (emitOption == PushInitialValues) {
/*
* The lhs is a simple name so the to-be-destructured value is
* its initial value and there is nothing to do.
*/
JS_ASSERT(pn->getOp() == JSOP_GETLOCAL);
JS_ASSERT(pn->pn_dflags & PND_BOUND);
return true;
}
// All paths below must pop after assigning to the lhs.
/* All paths below must pop after assigning to the lhs. */
if (pn->isKind(PNK_NAME)) {
switch (pn->getKind()) {
case PNK_NAME:
if (!BindNameToSlot(cx, bce, pn))
return false;
/* Allow 'const [x,y] = o', make 'const x,y; [x,y] = o' a nop. */
// Allow 'const [x,y] = o', make 'const x,y; [x,y] = o' a nop.
if (pn->isConst() && !pn->isDefn())
return Emit1(cx, bce, JSOP_POP) >= 0;
}
switch (pn->getOp()) {
case JSOP_SETNAME:
case JSOP_SETGNAME:
/*
* NB: pn is a PN_NAME node, not a PN_BINARY. Nevertheless,
* we want to emit JSOP_ENUMELEM, which has format JOF_ELEM.
* So here and for JSOP_ENUMCONSTELEM, we use EmitElemOp.
*/
switch (pn->getOp()) {
case JSOP_SETNAME:
case JSOP_SETGNAME:
// This is like ordinary assignment, but with one difference.
//
// In `a = b`, we first determine a binding for `a` (using
// JSOP_BINDNAME or JSOP_BINDGNAME), then we evaluate `b`, then
// a JSOP_SETNAME instruction.
//
// In `[a] = [b]`, per spec, `b` is evaluated first, then we
// determine a binding for `a`. Then we need to do assignment--
// but the operands are on the stack in the wrong order for
// JSOP_SETPROP, so we use JSOP_ENUMELEM instead.
//
// EmitElemOp ordinarily works with PNK_ELEM nodes, naturally,
// but it has special code to handle PNK_NAME nodes in this one
// case.
if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, bce))
return false;
break;
case JSOP_SETCONST:
// As above.
if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, bce))
return false;
break;
case JSOP_SETLOCAL:
case JSOP_SETARG:
if (!EmitVarOp(cx, pn, pn->getOp(), bce))
return false;
if (Emit1(cx, bce, JSOP_POP) < 0)
return false;
break;
default:
MOZ_ASSUME_UNREACHABLE("EmitDestructuringLHS: bad name op");
}
break;
case PNK_DOT:
case PNK_ELEM:
// See the (PNK_NAME, JSOP_SETNAME) case above.
//
// In `a.x = b`, `a` is evaluated first, then `b`, then a
// JSOP_SETPROP instruction.
//
// In `[a.x] = [b]`, per spec, `b` is evaluated before `a`. Then we
// need a property set -- but the operands are on the stack in the
// wrong order for JSOP_SETPROP, so we use JSOP_ENUMELEM instead.
//
// EmitElemOp has special code to handle PNK_DOT nodes in this one
// case.
if (!EmitElemOp(cx, pn, JSOP_ENUMELEM, bce))
return false;
break;
case JSOP_SETCONST:
if (!EmitElemOp(cx, pn, JSOP_ENUMCONSTELEM, bce))
return false;
break;
case JSOP_SETLOCAL:
case JSOP_SETARG:
if (!EmitVarOp(cx, pn, pn->getOp(), bce))
return false;
if (Emit1(cx, bce, JSOP_POP) < 0)
return false;
break;
case JSOP_CALL:
case JSOP_EVAL:
case JSOP_FUNCALL:
case JSOP_FUNAPPLY:
case PNK_CALL:
JS_ASSERT(pn->pn_xflags & PNX_SETCALL);
if (!EmitTree(cx, bce, pn))
return false;
/*
* We just emitted JSOP_SETCALL which will always throw.
* Pop the call return value and the RHS.
*/
// Pop the call return value and the RHS, presumably for the
// benefit of bytecode analysis. (The interpreter will never reach
// these instructions since we just emitted JSOP_SETCALL, which
// always throws. It's possible no analyses actually depend on this
// either.)
if (Emit1(cx, bce, JSOP_POP) < 0)
return false;
if (Emit1(cx, bce, JSOP_POP) < 0)
@ -2797,16 +2817,7 @@ EmitDestructuringLHS(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn,
break;
default:
{
if (!EmitTree(cx, bce, pn))
return false;
if (!EmitElemOpBase(cx, bce, JSOP_ENUMELEM))
return false;
break;
}
case JSOP_ENUMELEM:
JS_ASSERT(0);
MOZ_ASSUME_UNREACHABLE("EmitDestructuringLHS: bad lhs kind");
}
}
@ -5681,6 +5692,7 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
bool restIsDefn = false;
if (fun->hasRest()) {
JS_ASSERT(!bce->sc->asFunctionBox()->argumentsHasLocalBinding());
// Defaults with a rest parameter need special handling. The
// rest parameter needs to be undefined while defaults are being
// processed. To do this, we create the rest argument and let it
@ -5694,6 +5706,7 @@ frontend::EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
if (Emit1(cx, bce, JSOP_REST) < 0)
return false;
CheckTypeSet(cx, bce, JSOP_REST);
// Only set the rest parameter if it's not aliased by a nested
// function in the body.
if (restIsDefn) {

View File

@ -86,17 +86,13 @@ class FullParseHandler
void prepareNodeForMutation(ParseNode *pn) { return allocator.prepareNodeForMutation(pn); }
const Token &currentToken() { return tokenStream.currentToken(); }
ParseNode *newName(PropertyName *name, InBlockBool inBlock, uint32_t blockid,
const TokenPos &pos)
{
return new_<NameNode>(PNK_NAME, JSOP_NAME, name, inBlock, blockid, pos);
ParseNode *newName(PropertyName *name, uint32_t blockid, const TokenPos &pos) {
return new_<NameNode>(PNK_NAME, JSOP_NAME, name, blockid, pos);
}
Definition *newPlaceholder(JSAtom *atom, InBlockBool inBlock, uint32_t blockid,
const TokenPos &pos)
{
Definition *newPlaceholder(JSAtom *atom, uint32_t blockid, const TokenPos &pos) {
Definition *dn =
(Definition *) new_<NameNode>(PNK_NAME, JSOP_NOP, atom, inBlock, blockid, pos);
(Definition *) new_<NameNode>(PNK_NAME, JSOP_NOP, atom, blockid, pos);
if (!dn)
return NULL;
dn->setDefn(true);

View File

@ -630,7 +630,6 @@ class ParseNode
#define PND_LET 0x01 /* let (block-scoped) binding */
#define PND_CONST 0x02 /* const binding (orthogonal to let) */
#define PND_ASSIGNED 0x04 /* set if ever LHS of assignment */
#define PND_BLOCKCHILD 0x08 /* use or def is direct block child */
#define PND_PLACEHOLDER 0x10 /* placeholder definition for lexdep */
#define PND_BOUND 0x20 /* bound to a stack or global slot */
#define PND_DEOPTIMIZED 0x40 /* former pn_used name node, pn_lexdef
@ -713,7 +712,6 @@ class ParseNode
bool isLet() const { return test(PND_LET); }
bool isConst() const { return test(PND_CONST); }
bool isBlockChild() const { return test(PND_BLOCKCHILD); }
bool isPlaceholder() const { return test(PND_PLACEHOLDER); }
bool isDeoptimized() const { return test(PND_DEOPTIMIZED); }
bool isAssigned() const { return test(PND_ASSIGNED); }
@ -976,21 +974,16 @@ struct CodeNode : public ParseNode
#endif
};
enum InBlockBool {
NotInBlock = false,
InBlock = true
};
struct NameNode : public ParseNode
{
NameNode(ParseNodeKind kind, JSOp op, JSAtom *atom, InBlockBool inBlock, uint32_t blockid,
NameNode(ParseNodeKind kind, JSOp op, JSAtom *atom, uint32_t blockid,
const TokenPos &pos)
: ParseNode(kind, op, PN_NAME, pos)
{
pn_atom = atom;
pn_expr = NULL;
pn_cookie.makeFree();
pn_dflags = inBlock ? PND_BLOCKCHILD : 0;
pn_dflags = 0;
pn_blockid = blockid;
JS_ASSERT(pn_blockid == blockid); // check for bitfield overflow
}

View File

@ -782,14 +782,14 @@ Parser<ParseHandler>::checkFinalReturn(Node pn)
}
/*
* Check that it is permitted to assign to lhs. Strict mode code may not
* assign to 'eval' or 'arguments'.
* Check that assigning to lhs is permitted. Assigning to 'eval' or
* 'arguments' is banned in strict mode and in destructuring assignment.
*/
template <typename ParseHandler>
bool
Parser<ParseHandler>::checkStrictAssignment(Node lhs)
Parser<ParseHandler>::checkStrictAssignment(Node lhs, AssignmentFlavor flavor)
{
if (!pc->sc->needStrictChecks())
if (!pc->sc->needStrictChecks() && flavor != KeyedDestructuringAssignment)
return true;
JSAtom *atom = handler.isName(lhs);
@ -798,12 +798,20 @@ Parser<ParseHandler>::checkStrictAssignment(Node lhs)
if (atom == context->names().eval || atom == context->names().arguments) {
JSAutoByteString name;
if (!AtomToPrintableString(context, atom, &name) ||
!report(ParseStrictError, pc->sc->strict, lhs,
JSMSG_DEPRECATED_ASSIGN, name.ptr()))
{
if (!AtomToPrintableString(context, atom, &name))
return false;
ParseReportKind kind;
unsigned errnum;
if (pc->sc->strict || flavor != KeyedDestructuringAssignment) {
kind = ParseStrictError;
errnum = JSMSG_BAD_STRICT_ASSIGN;
} else {
kind = ParseError;
errnum = JSMSG_BAD_DESTRUCT_ASSIGN;
}
if (!report(kind, pc->sc->strict, lhs, errnum, name.ptr()))
return false;
}
return true;
}
@ -816,7 +824,7 @@ Parser<ParseHandler>::checkStrictAssignment(Node lhs)
*/
template <typename ParseHandler>
bool
Parser<ParseHandler>::checkStrictBinding(HandlePropertyName name, Node pn)
Parser<ParseHandler>::checkStrictBinding(PropertyName *name, Node pn)
{
if (!pc->sc->needStrictChecks())
return true;
@ -1254,7 +1262,7 @@ Parser<ParseHandler>::getOrCreateLexicalDependency(ParseContext<ParseHandler> *p
if (p)
return p.value().get<ParseHandler>();
DefinitionNode dn = handler.newPlaceholder(atom, pc->inBlock(), pc->blockid(), pos());
DefinitionNode dn = handler.newPlaceholder(atom, pc->blockid(), pos());
if (!dn)
return ParseHandler::nullDefinition();
DefinitionSingle def = DefinitionSingle::new_<ParseHandler>(dn);
@ -1297,8 +1305,7 @@ ConvertDefinitionToNamedLambdaUse(TokenStream &ts, ParseContext<FullParseHandler
*/
template <>
bool
Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funName,
ParseContext<FullParseHandler> *outerpc,
Parser<FullParseHandler>::leaveFunction(ParseNode *fn, ParseContext<FullParseHandler> *outerpc,
FunctionSyntaxKind kind)
{
outerpc->blockidGen = pc->blockidGen;
@ -1306,9 +1313,6 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funNam
FunctionBox *funbox = fn->pn_funbox;
JS_ASSERT(funbox == pc->sc->asFunctionBox());
if (!outerpc->topStmt || outerpc->topStmt->type == STMT_BLOCK)
fn->pn_dflags |= PND_BLOCKCHILD;
/* Propagate unresolved lexical names up to outerpc->lexdeps. */
if (pc->lexdeps->count()) {
for (AtomDefnRange r = pc->lexdeps->all(); !r.empty(); r.popFront()) {
@ -1316,7 +1320,7 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funNam
Definition *dn = r.front().value().get<FullParseHandler>();
JS_ASSERT(dn->isPlaceholder());
if (atom == funName && kind == Expression) {
if (atom == funbox->function()->name() && kind == Expression) {
if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
return false;
continue;
@ -1400,8 +1404,7 @@ Parser<FullParseHandler>::leaveFunction(ParseNode *fn, HandlePropertyName funNam
template <>
bool
Parser<SyntaxParseHandler>::leaveFunction(Node fn, HandlePropertyName funName,
ParseContext<SyntaxParseHandler> *outerpc,
Parser<SyntaxParseHandler>::leaveFunction(Node fn, ParseContext<SyntaxParseHandler> *outerpc,
FunctionSyntaxKind kind)
{
outerpc->blockidGen = pc->blockidGen;
@ -1910,7 +1913,7 @@ Parser<SyntaxParseHandler>::checkFunctionDefinition(HandlePropertyName funName,
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream::Position &start,
size_t startOffset, FunctionType type, FunctionSyntaxKind kind)
FunctionType type, FunctionSyntaxKind kind)
{
JS_ASSERT_IF(kind == Statement, funName);
@ -1935,7 +1938,7 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
// directive, we backup and reparse it as strict.
bool initiallyStrict = pc->sc->strict;
bool becameStrict;
if (!functionArgsAndBody(pn, fun, funName, startOffset, type, kind, initiallyStrict,
if (!functionArgsAndBody(pn, fun, type, kind, initiallyStrict,
&becameStrict))
{
if (initiallyStrict || !becameStrict || tokenStream.hadError())
@ -1945,8 +1948,11 @@ Parser<ParseHandler>::functionDef(HandlePropertyName funName, const TokenStream:
tokenStream.seek(start);
if (funName && tokenStream.getToken() == TOK_ERROR)
return null();
// functionArgsAndBody may have already set pn->pn_body before failing.
handler.setFunctionBody(pn, null());
if (!functionArgsAndBody(pn, fun, funName, startOffset, type, kind, true))
if (!functionArgsAndBody(pn, fun, type, kind, true))
return null();
}
@ -2017,7 +2023,8 @@ Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbo
size_t numFreeVariables = pc->lexdeps->count();
size_t numInnerFunctions = pc->innerFunctions.length();
LazyScript *lazy = LazyScript::Create(context, numFreeVariables, numInnerFunctions, versionNumber(),
RootedFunction fun(context, funbox->function());
LazyScript *lazy = LazyScript::Create(context, fun, numFreeVariables, numInnerFunctions, versionNumber(),
funbox->bufStart, funbox->bufEnd,
funbox->startLine, funbox->startColumn);
if (!lazy)
@ -2039,16 +2046,14 @@ Parser<SyntaxParseHandler>::finishFunctionDefinition(Node pn, FunctionBox *funbo
lazy->setUsesArgumentsAndApply();
PropagateTransitiveParseFlags(funbox, lazy);
funbox->object->as<JSFunction>().initLazyScript(lazy);
fun->initLazyScript(lazy);
return true;
}
template <>
bool
Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
HandlePropertyName funName,
size_t startOffset, FunctionType type,
FunctionSyntaxKind kind,
FunctionType type, FunctionSyntaxKind kind,
bool strict, bool *becameStrict)
{
if (becameStrict)
@ -2078,7 +2083,7 @@ Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
return false;
if (!parser->functionArgsAndBodyGeneric(SyntaxParseHandler::NodeGeneric,
fun, funName, type, kind, strict, becameStrict))
fun, type, kind, becameStrict))
{
if (parser->hadAbortedSyntaxParse()) {
// Try again with a full parse.
@ -2111,10 +2116,10 @@ Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
if (!funpc.init())
return false;
if (!functionArgsAndBodyGeneric(pn, fun, funName, type, kind, strict, becameStrict))
if (!functionArgsAndBodyGeneric(pn, fun, type, kind, becameStrict))
return false;
if (!leaveFunction(pn, funName, outerpc, kind))
if (!leaveFunction(pn, outerpc, kind))
return false;
pn->pn_blockid = outerpc->blockid();
@ -2132,9 +2137,7 @@ Parser<FullParseHandler>::functionArgsAndBody(ParseNode *pn, HandleFunction fun,
template <>
bool
Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
HandlePropertyName funName,
size_t startOffset, FunctionType type,
FunctionSyntaxKind kind,
FunctionType type, FunctionSyntaxKind kind,
bool strict, bool *becameStrict)
{
if (becameStrict)
@ -2152,10 +2155,10 @@ Parser<SyntaxParseHandler>::functionArgsAndBody(Node pn, HandleFunction fun,
if (!funpc.init())
return false;
if (!functionArgsAndBodyGeneric(pn, fun, funName, type, kind, strict, becameStrict))
if (!functionArgsAndBodyGeneric(pn, fun, type, kind, becameStrict))
return false;
if (!leaveFunction(pn, funName, outerpc, kind))
if (!leaveFunction(pn, outerpc, kind))
return false;
// This is a lazy function inner to another lazy function. Remember the
@ -2183,13 +2186,11 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned st
if (!funpc.init())
return null();
RootedPropertyName funName(context, fun->atom() ? fun->atom()->asPropertyName() : NULL);
if (!functionArgsAndBodyGeneric(pn, fun, funName, Normal, Statement, strict, NULL))
if (!functionArgsAndBodyGeneric(pn, fun, Normal, Statement, NULL))
return null();
if (fun->isNamedLambda()) {
if (AtomDefnPtr p = pc->lexdeps->lookup(funName)) {
if (AtomDefnPtr p = pc->lexdeps->lookup(fun->name())) {
Definition *dn = p.value().get<FullParseHandler>();
if (!ConvertDefinitionToNamedLambdaUse(tokenStream, pc, funbox, dn))
return NULL;
@ -2206,10 +2207,8 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, unsigned st
template <typename ParseHandler>
bool
Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
HandlePropertyName funName, FunctionType type,
FunctionSyntaxKind kind,
bool strict, bool *becameStrict)
Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type,
FunctionSyntaxKind kind, bool *becameStrict)
{
// Given a properly initialized parse context, try to parse an actual
// function without concern for conversion to strict mode, use of lazy
@ -2265,7 +2264,7 @@ Parser<ParseHandler>::functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
if (!yieldGuard.empty() && !yieldGuard.ref().checkValidBody(body, JSMSG_YIELD_IN_ARROW))
return false;
if (funName && !checkStrictBinding(funName, pn))
if (fun->name() && !checkStrictBinding(fun->name(), pn))
return false;
#if JS_HAS_EXPR_CLOSURES
@ -2356,7 +2355,7 @@ Parser<ParseHandler>::functionStmt()
!report(ParseStrictError, pc->sc->strict, null(), JSMSG_STRICT_FUNCTION_STATEMENT))
return null();
return functionDef(name, start, tokenStream.positionToOffset(start), Normal, Statement);
return functionDef(name, start, Normal, Statement);
}
template <typename ParseHandler>
@ -2371,7 +2370,7 @@ Parser<ParseHandler>::functionExpr()
name = tokenStream.currentToken().name();
else
tokenStream.ungetToken();
return functionDef(name, start, tokenStream.positionToOffset(start), Normal, Expression);
return functionDef(name, start, Normal, Expression);
}
/*
@ -2919,57 +2918,6 @@ Parser<FullParseHandler>::bindDestructuringVar(BindData<FullParseHandler> *data,
return true;
}
/*
* Here, we are destructuring {... P: Q, ...} = R, where P is any id, Q is any
* LHS expression except a destructuring initialiser, and R is on the stack.
* Because R is already evaluated, the usual LHS-specialized bytecodes won't
* work. After pushing R[P] we need to evaluate Q's "reference base" QB and
* then push its property name QN. At this point the stack looks like
*
* [... R, R[P], QB, QN]
*
* We need to set QB[QN] = R[P]. This is a job for JSOP_ENUMELEM, which takes
* its operands with left-hand side above right-hand side:
*
* [rval, lval, xval]
*
* and pops all three values, setting lval[xval] = rval. But we cannot select
* JSOP_ENUMELEM yet, because the LHS may turn out to be an arg or local var,
* which can be optimized further. So we select JSOP_SETNAME.
*/
template <>
bool
Parser<FullParseHandler>::bindDestructuringLHS(ParseNode *pn)
{
switch (pn->getKind()) {
case PNK_NAME:
pn->markAsAssigned();
/* FALL THROUGH */
case PNK_DOT:
case PNK_ELEM:
/*
* We may be called on a name node that has already been specialized,
* in the very weird and ECMA-262-required "for (var [x] = i in o) ..."
* case. See bug 558633.
*/
if (!(js_CodeSpec[pn->getOp()].format & JOF_SET))
pn->setOp(JSOP_SETNAME);
break;
case PNK_CALL:
if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
return false;
break;
default:
report(ParseError, false, pn, JSMSG_BAD_LEFTSIDE_OF_ASS);
return false;
}
return true;
}
/*
* Destructuring patterns can appear in two kinds of contexts:
*
@ -3038,7 +2986,7 @@ Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler> *data,
}
ok = bindDestructuringVar(data, pn);
} else {
ok = bindDestructuringLHS(pn);
ok = checkAndMarkAsAssignmentLhs(pn, KeyedDestructuringAssignment);
}
}
if (!ok)
@ -3072,7 +3020,7 @@ Parser<FullParseHandler>::checkDestructuring(BindData<FullParseHandler> *data,
if (!noteNameUse(name, pn))
return false;
}
ok = bindDestructuringLHS(pn);
ok = checkAndMarkAsAssignmentLhs(pn, KeyedDestructuringAssignment);
}
if (!ok)
return false;
@ -3954,7 +3902,7 @@ Parser<FullParseHandler>::forStatement()
pn2 = pn1;
pn1 = NULL;
if (!setAssignmentLhsOps(pn2, true))
if (!checkAndMarkAsAssignmentLhs(pn2, PlainAssignment))
return null();
}
@ -4179,7 +4127,7 @@ Parser<SyntaxParseHandler>::forStatement()
return null();
}
if (!isForDecl && !setAssignmentLhsOps(lhsNode, true))
if (!isForDecl && !checkAndMarkAsAssignmentLhs(lhsNode, PlainAssignment))
return null();
if (!expr())
@ -5086,25 +5034,34 @@ Parser<ParseHandler>::condExpr1()
template <>
bool
Parser<FullParseHandler>::setAssignmentLhsOps(ParseNode *pn, bool isPlainAssignment)
Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentFlavor flavor)
{
switch (pn->getKind()) {
case PNK_NAME:
if (!checkStrictAssignment(pn))
if (!checkStrictAssignment(pn, flavor))
return false;
pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
if (flavor == KeyedDestructuringAssignment) {
/*
* We may be called on a name node that has already been
* specialized, in the very weird "for (var [x] = i in o) ..."
* case. See bug 558633.
*/
if (!(js_CodeSpec[pn->getOp()].format & JOF_SET))
pn->setOp(JSOP_SETNAME);
} else {
pn->setOp(pn->isOp(JSOP_GETLOCAL) ? JSOP_SETLOCAL : JSOP_SETNAME);
}
pn->markAsAssigned();
break;
case PNK_DOT:
pn->setOp(JSOP_SETPROP);
break;
case PNK_ELEM:
pn->setOp(JSOP_SETELEM);
break;
#if JS_HAS_DESTRUCTURING
case PNK_ARRAY:
case PNK_OBJECT:
if (!isPlainAssignment) {
if (flavor == CompoundAssignment) {
report(ParseError, false, null(), JSMSG_BAD_DESTRUCT_ASS);
return false;
}
@ -5112,12 +5069,14 @@ Parser<FullParseHandler>::setAssignmentLhsOps(ParseNode *pn, bool isPlainAssignm
return false;
break;
#endif
case PNK_CALL:
if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
return false;
break;
default:
report(ParseError, false, null(), JSMSG_BAD_LEFTSIDE_OF_ASS);
report(ParseError, false, pn, JSMSG_BAD_LEFTSIDE_OF_ASS);
return false;
}
return true;
@ -5125,7 +5084,7 @@ Parser<FullParseHandler>::setAssignmentLhsOps(ParseNode *pn, bool isPlainAssignm
template <>
bool
Parser<SyntaxParseHandler>::setAssignmentLhsOps(Node pn, bool isPlainAssignment)
Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor)
{
/* Full syntax checking of valid assignment LHS terms requires a parse tree. */
if (pn != SyntaxParseHandler::NodeName &&
@ -5134,7 +5093,7 @@ Parser<SyntaxParseHandler>::setAssignmentLhsOps(Node pn, bool isPlainAssignment)
{
return abortIfSyntaxParser();
}
return checkStrictAssignment(pn);
return checkStrictAssignment(pn, flavor);
}
template <typename ParseHandler>
@ -5202,10 +5161,9 @@ Parser<ParseHandler>::assignExpr()
if (tokenStream.getToken() == TOK_ERROR)
return null();
size_t offset = pos().begin;
tokenStream.ungetToken();
return functionDef(NullPtr(), start, offset, Normal, Arrow);
return functionDef(NullPtr(), start, Normal, Arrow);
}
default:
@ -5214,7 +5172,8 @@ Parser<ParseHandler>::assignExpr()
return lhs;
}
if (!setAssignmentLhsOps(lhs, kind == PNK_ASSIGN))
AssignmentFlavor flavor = kind == PNK_ASSIGN ? PlainAssignment : CompoundAssignment;
if (!checkAndMarkAsAssignmentLhs(lhs, flavor))
return null();
Node rhs = assignExpr();
@ -5224,59 +5183,48 @@ Parser<ParseHandler>::assignExpr()
return handler.newBinaryOrAppend(kind, lhs, rhs, pc, op);
}
template <> bool
Parser<FullParseHandler>::setLvalKid(ParseNode *pn, ParseNode *kid, const char *name)
{
if (!kid->isKind(PNK_NAME) &&
!kid->isKind(PNK_DOT) &&
(!kid->isKind(PNK_CALL) ||
(!kid->isOp(JSOP_CALL) && !kid->isOp(JSOP_EVAL) &&
!kid->isOp(JSOP_FUNCALL) && !kid->isOp(JSOP_FUNAPPLY))) &&
!kid->isKind(PNK_ELEM))
{
report(ParseError, false, null(), JSMSG_BAD_OPERAND, name);
return false;
}
if (!checkStrictAssignment(kid))
return false;
pn->pn_kid = kid;
return true;
}
static const char incop_name_str[][10] = {"increment", "decrement"};
template <>
bool
Parser<FullParseHandler>::setIncOpKid(ParseNode *pn, ParseNode *kid, TokenKind tt, bool preorder)
Parser<FullParseHandler>::checkAndMarkAsIncOperand(ParseNode *kid, TokenKind tt, bool preorder)
{
if (!setLvalKid(pn, kid, incop_name_str[tt == TOK_DEC]))
// Check.
if (!kid->isKind(PNK_NAME) &&
!kid->isKind(PNK_DOT) &&
!kid->isKind(PNK_ELEM) &&
!(kid->isKind(PNK_CALL) &&
(kid->isOp(JSOP_CALL) ||
kid->isOp(JSOP_EVAL) ||
kid->isOp(JSOP_FUNCALL) ||
kid->isOp(JSOP_FUNAPPLY))))
{
report(ParseError, false, null(), JSMSG_BAD_OPERAND, incop_name_str[tt == TOK_DEC]);
return false;
}
if (!checkStrictAssignment(kid, IncDecAssignment))
return false;
switch (kid->getKind()) {
case PNK_NAME:
// Mark.
if (kid->isKind(PNK_NAME)) {
kid->markAsAssigned();
break;
case PNK_CALL:
} else if (kid->isKind(PNK_CALL)) {
if (!makeSetCall(kid, JSMSG_BAD_INCOP_OPERAND))
return false;
break;
case PNK_DOT:
case PNK_ELEM:
break;
default:
JS_ASSERT(0);
}
return true;
}
template <>
bool
Parser<SyntaxParseHandler>::setIncOpKid(Node pn, Node kid, TokenKind tt, bool preorder)
Parser<SyntaxParseHandler>::checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder)
{
return setAssignmentLhsOps(kid, false);
// To the extent of what we support in syntax-parse mode, the rules for
// inc/dec operands are the same as for assignment. There are differences,
// such as destructuring; but if we hit any of those cases, we'll abort and
// reparse in full mode.
return checkAndMarkAsAssignmentLhs(kid, IncDecAssignment);
}
template <typename ParseHandler>
@ -5320,15 +5268,12 @@ Parser<ParseHandler>::unaryExpr()
pn2 = memberExpr(tt2, true);
if (!pn2)
return null();
pn = handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
JSOP_NOP,
begin,
pn2);
if (!pn)
if (!checkAndMarkAsIncOperand(pn2, tt, true))
return null();
if (!setIncOpKid(pn, pn2, tt, true))
return null();
break;
return handler.newUnary((tt == TOK_INC) ? PNK_PREINCREMENT : PNK_PREDECREMENT,
JSOP_NOP,
begin,
pn2);
}
case TOK_DELETE: {
@ -5359,19 +5304,16 @@ Parser<ParseHandler>::unaryExpr()
tt = tokenStream.peekTokenSameLine(TSF_OPERAND);
if (tt == TOK_INC || tt == TOK_DEC) {
tokenStream.consumeKnownToken(tt);
pn2 = handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
JSOP_NOP,
begin,
pn);
if (!pn2)
if (!checkAndMarkAsIncOperand(pn, tt, false))
return null();
if (!setIncOpKid(pn2, pn, tt, false))
return null();
pn = pn2;
return handler.newUnary((tt == TOK_INC) ? PNK_POSTINCREMENT : PNK_POSTDECREMENT,
JSOP_NOP,
begin,
pn);
}
break;
return pn;
}
return pn;
MOZ_ASSUME_UNREACHABLE("unaryExpr");
}
/*
@ -5645,8 +5587,8 @@ CompExprTransplanter::transplant(ParseNode *pn)
* generator) a use of a new placeholder in the generator's
* lexdeps.
*/
Definition *dn2 = parser->handler.newPlaceholder(
atom, parser->pc->inBlock(), parser->pc->blockid(), parser->pos());
Definition *dn2 = parser->handler.newPlaceholder(atom, parser->pc->blockid(),
parser->pos());
if (!dn2)
return false;
dn2->pn_pos = root->pn_pos;
@ -6074,8 +6016,7 @@ Parser<FullParseHandler>::generatorExpr(ParseNode *kid)
genfn->pn_pos.begin = body->pn_pos.begin = kid->pn_pos.begin;
genfn->pn_pos.end = body->pn_pos.end = pos().end;
RootedPropertyName funName(context);
if (!leaveFunction(genfn, funName, outerpc))
if (!leaveFunction(genfn, outerpc))
return null();
}
@ -6270,27 +6211,11 @@ Parser<ParseHandler>::memberExpr(TokenKind tt, bool allowCallSyntax)
return lhs;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::bracketedExpr()
{
/*
* Always accept the 'in' operator in a parenthesized expression,
* where it's unambiguous, even if we might be parsing the init of a
* for statement.
*/
bool oldParsingForInit = pc->parsingForInit;
pc->parsingForInit = false;
Node pn = expr();
pc->parsingForInit = oldParsingForInit;
return pn;
}
template <typename ParseHandler>
typename ParseHandler::Node
Parser<ParseHandler>::newName(PropertyName *name)
{
return handler.newName(name, pc->inBlock(), pc->blockid(), pos());
return handler.newName(name, pc->blockid(), pos());
}
template <typename ParseHandler>
@ -6573,8 +6498,7 @@ Parser<ParseHandler>::primaryExpr(TokenKind tt)
Rooted<PropertyName*> funName(context, NULL);
TokenStream::Position start(keepAtoms);
tokenStream.tell(&start);
pn2 = functionDef(funName, start, tokenStream.positionToOffset(start),
op == JSOP_INITPROP_GETTER ? Getter : Setter,
pn2 = functionDef(funName, start, op == JSOP_INITPROP_GETTER ? Getter : Setter,
Expression);
if (!pn2)
return null();
@ -6800,7 +6724,16 @@ Parser<ParseHandler>::parenExpr(bool *genexp)
GenexpGuard<ParseHandler> guard(this);
Node pn = bracketedExpr();
/*
* Always accept the 'in' operator in a parenthesized expression,
* where it's unambiguous, even if we might be parsing the init of a
* for statement.
*/
bool oldParsingForInit = pc->parsingForInit;
pc->parsingForInit = false;
Node pn = expr();
pc->parsingForInit = oldParsingForInit;
if (!pn)
return null();
guard.endBody();

View File

@ -272,7 +272,6 @@ struct ParseContext : public GenericParseContext
return decls_.init() && lexdeps.ensureMap(sc->context);
}
InBlockBool inBlock() const { return InBlockBool(!topStmt || topStmt->type == STMT_BLOCK); }
unsigned blockid() { return topStmt ? topStmt->blockid : bodyid; }
// True if we are at the topmost level of a entire script or function body.
@ -416,9 +415,8 @@ class Parser : private AutoGCRooter, public StrictModeGetter
enum FunctionBodyType { StatementListBody, ExpressionBody };
Node functionBody(FunctionSyntaxKind kind, FunctionBodyType type);
bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
HandlePropertyName funName, FunctionType type,
FunctionSyntaxKind kind, bool strict, bool *becameStrict);
bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun, FunctionType type,
FunctionSyntaxKind kind, bool *becameStrict);
virtual bool strictMode() { return pc->sc->strict; }
@ -486,9 +484,9 @@ class Parser : private AutoGCRooter, public StrictModeGetter
bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
size_t startOffset, FunctionType type, FunctionSyntaxKind kind);
bool functionArgsAndBody(Node pn, HandleFunction fun, HandlePropertyName funName,
size_t startOffset, FunctionType type, FunctionSyntaxKind kind,
FunctionType type, FunctionSyntaxKind kind);
bool functionArgsAndBody(Node pn, HandleFunction fun,
FunctionType type, FunctionSyntaxKind kind,
bool strict, bool *becameStrict = NULL);
Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin);
@ -500,7 +498,6 @@ class Parser : private AutoGCRooter, public StrictModeGetter
bool arrayInitializerComprehensionTail(Node pn);
Node generatorExpr(Node kid);
bool argumentList(Node listNode);
Node bracketedExpr();
Node letBlock(LetContext letContext);
Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt);
@ -514,7 +511,14 @@ class Parser : private AutoGCRooter, public StrictModeGetter
#endif
}
bool setAssignmentLhsOps(Node pn, bool isPlainAssignment);
enum AssignmentFlavor {
PlainAssignment,
CompoundAssignment,
KeyedDestructuringAssignment,
IncDecAssignment
};
bool checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
bool matchInOrOf(bool *isForOfp);
bool checkFunctionArguments();
@ -526,10 +530,9 @@ class Parser : private AutoGCRooter, public StrictModeGetter
bool isValidForStatementLHS(Node pn1, JSVersion version,
bool forDecl, bool forEach, bool forOf);
bool setLvalKid(Node pn, Node kid, const char *name);
bool setIncOpKid(Node pn, Node kid, TokenKind tt, bool preorder);
bool checkStrictAssignment(Node lhs);
bool checkStrictBinding(HandlePropertyName name, Node pn);
bool checkAndMarkAsIncOperand(Node kid, TokenKind tt, bool preorder);
bool checkStrictAssignment(Node lhs, AssignmentFlavor flavor);
bool checkStrictBinding(PropertyName *name, Node pn);
bool defineArg(Node funcpn, HandlePropertyName name,
bool disallowDuplicateArgs = false, Node *duplicatedArg = NULL);
Node pushLexicalScope(StmtInfoPC *stmt);
@ -568,8 +571,7 @@ class Parser : private AutoGCRooter, public StrictModeGetter
bool checkFinalReturn(Node pn);
DefinitionNode getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom);
bool leaveFunction(Node fn, HandlePropertyName funName,
ParseContext<ParseHandler> *outerpc,
bool leaveFunction(Node fn, ParseContext<ParseHandler> *outerpc,
FunctionSyntaxKind kind = Expression);
TokenPos pos() const { return tokenStream.currentToken().pos; }
@ -583,11 +585,11 @@ class Parser : private AutoGCRooter, public StrictModeGetter
template <>
bool
Parser<FullParseHandler>::setAssignmentLhsOps(ParseNode *pn, bool isPlainAssignment);
Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentFlavor flavor);
template <>
bool
Parser<SyntaxParseHandler>::setAssignmentLhsOps(Node pn, bool isPlainAssignment);
Parser<SyntaxParseHandler>::checkAndMarkAsAssignmentLhs(Node pn, AssignmentFlavor flavor);
} /* namespace frontend */
} /* namespace js */

View File

@ -53,14 +53,12 @@ class SyntaxParseHandler
void trace(JSTracer *trc) {}
Node newName(PropertyName *name, InBlockBool inBlock, uint32_t blockid, const TokenPos &pos) {
Node newName(PropertyName *name, uint32_t blockid, const TokenPos &pos) {
lastAtom = name;
return NodeName;
}
DefinitionNode newPlaceholder(JSAtom *atom, InBlockBool inBlock, uint32_t blockid,
const TokenPos &pos)
{
DefinitionNode newPlaceholder(JSAtom *atom, uint32_t blockid, const TokenPos &pos) {
return Definition::PLACEHOLDER;
}

View File

@ -132,6 +132,11 @@ class EncapsulatedPtr
~EncapsulatedPtr() { pre(); }
void init(T *v) {
JS_ASSERT(!IsPoisonedPtr<T>(v));
this->value = v;
}
/* Use to set the pointer to NULL. */
void clear() {
pre();

View File

@ -16,6 +16,7 @@
using namespace js;
using namespace js::gc;
using mozilla::ReentrancyGuard;
/*** SlotEdge ***/
@ -147,6 +148,7 @@ template <typename T>
void
StoreBuffer::MonoTypeBuffer<T>::mark(JSTracer *trc)
{
ReentrancyGuard g(*this);
compact();
T *cursor = base;
while (cursor != pos) {
@ -240,6 +242,8 @@ StoreBuffer::GenericBuffer::clear()
void
StoreBuffer::GenericBuffer::mark(JSTracer *trc)
{
ReentrancyGuard g(*this);
uint8_t *p = base;
while (p < pos) {
unsigned size = *((unsigned *)p);

View File

@ -13,6 +13,8 @@
# error "Generational GC requires exact rooting."
#endif
#include "mozilla/ReentrancyGuard.h"
#include "jsalloc.h"
#include "jsgc.h"
#include "jsobj.h"
@ -77,6 +79,7 @@ class StoreBuffer
class MonoTypeBuffer
{
friend class StoreBuffer;
friend class mozilla::ReentrancyGuard;
StoreBuffer *owner;
@ -97,8 +100,10 @@ class StoreBuffer
*/
EdgeSet duplicates;
bool entered;
MonoTypeBuffer(StoreBuffer *owner)
: owner(owner), base(NULL), pos(NULL), top(NULL)
: owner(owner), base(NULL), pos(NULL), top(NULL), entered(false)
{
duplicates.init();
}
@ -125,6 +130,7 @@ class StoreBuffer
/* Add one item to the buffer. */
void put(const T &v) {
mozilla::ReentrancyGuard g(*this);
JS_ASSERT(!owner->inParallelSection());
/* Check if we have been enabled. */
@ -180,6 +186,7 @@ class StoreBuffer
class GenericBuffer
{
friend class StoreBuffer;
friend class mozilla::ReentrancyGuard;
StoreBuffer *owner;
@ -187,8 +194,10 @@ class StoreBuffer
uint8_t *pos; /* Pointer to current buffer position. */
uint8_t *top; /* Pointer to one past the last entry. */
bool entered;
GenericBuffer(StoreBuffer *owner)
: owner(owner)
: owner(owner), base(NULL), pos(NULL), top(NULL), entered(false)
{}
GenericBuffer &operator=(const GenericBuffer& other) MOZ_DELETE;
@ -202,6 +211,7 @@ class StoreBuffer
template <typename T>
void put(const T &t) {
mozilla::ReentrancyGuard g(*this);
JS_ASSERT(!owner->inParallelSection());
/* Ensure T is derived from BufferableRef. */

View File

@ -3571,6 +3571,45 @@ CodeGenerator::visitMathFunctionD(LMathFunctionD *ins)
case MMathFunction::ACos:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_acos_impl);
break;
case MMathFunction::Log10:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_log10_impl);
break;
case MMathFunction::Log2:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_log2_impl);
break;
case MMathFunction::Log1P:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_log1p_impl);
break;
case MMathFunction::ExpM1:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_expm1_impl);
break;
case MMathFunction::CosH:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_cosh_impl);
break;
case MMathFunction::SinH:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_sinh_impl);
break;
case MMathFunction::TanH:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_tanh_impl);
break;
case MMathFunction::ACosH:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_acosh_impl);
break;
case MMathFunction::ASinH:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_asinh_impl);
break;
case MMathFunction::ATanH:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_atanh_impl);
break;
case MMathFunction::Sign:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_sign_impl);
break;
case MMathFunction::Trunc:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_trunc_impl);
break;
case MMathFunction::Cbrt:
funptr = JS_FUNC_TO_DATA_PTR(void *, js::math_cbrt_impl);
break;
default:
MOZ_ASSUME_UNREACHABLE("Unknown math function");
}

View File

@ -7903,13 +7903,8 @@ IonBuilder::getPropTryCache(bool *emitted, HandlePropertyName name, HandleId id,
return true;
}
MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
if (barrier || IsNullOrUndefined(rvalType) || accessGetter)
rvalType = MIRType_Value;
current->pop();
MGetPropertyCache *load = MGetPropertyCache::New(obj, name);
load->setResultType(rvalType);
// Try to mark the cache as idempotent. We only do this if JM is enabled
// (its ICs are used to mark property reads as likely non-idempotent) or
@ -7944,6 +7939,21 @@ IonBuilder::getPropTryCache(bool *emitted, HandlePropertyName name, HandleId id,
if (accessGetter)
barrier = true;
// ParallelGetPropertyIC cannot safely call TypeScript::Monitor to ensure
// that the observed type set contains undefined. To account for possible
// missing properties, which property types do not track, we must always
// insert a type barrier.
if (info().executionMode() == ParallelExecution &&
!types->hasType(types::Type::UndefinedType()))
{
barrier = true;
}
MIRType rvalType = MIRTypeFromValueType(types->getKnownTypeTag());
if (barrier || IsNullOrUndefined(rvalType))
rvalType = MIRType_Value;
load->setResultType(rvalType);
if (!pushTypeBarrier(load, types, barrier))
return false;

View File

@ -72,6 +72,32 @@ IonBuilder::inlineNativeCall(CallInfo &callInfo, JSNative native)
return inlineMathFunction(callInfo, MMathFunction::ASin);
if (native == js::math_acos)
return inlineMathFunction(callInfo, MMathFunction::ACos);
if (native == js::math_log10)
return inlineMathFunction(callInfo, MMathFunction::Log10);
if (native == js::math_log2)
return inlineMathFunction(callInfo, MMathFunction::Log2);
if (native == js::math_log1p)
return inlineMathFunction(callInfo, MMathFunction::Log1P);
if (native == js::math_expm1)
return inlineMathFunction(callInfo, MMathFunction::ExpM1);
if (native == js::math_cosh)
return inlineMathFunction(callInfo, MMathFunction::CosH);
if (native == js::math_sin)
return inlineMathFunction(callInfo, MMathFunction::SinH);
if (native == js::math_tan)
return inlineMathFunction(callInfo, MMathFunction::TanH);
if (native == js::math_acosh)
return inlineMathFunction(callInfo, MMathFunction::ACosH);
if (native == js::math_asin)
return inlineMathFunction(callInfo, MMathFunction::ASinH);
if (native == js::math_atan)
return inlineMathFunction(callInfo, MMathFunction::ATanH);
if (native == js::math_sign)
return inlineMathFunction(callInfo, MMathFunction::Sign);
if (native == js::math_trunc)
return inlineMathFunction(callInfo, MMathFunction::Trunc);
if (native == js::math_cbrt)
return inlineMathFunction(callInfo, MMathFunction::Cbrt);
// String natives.
if (native == js_String)

View File

@ -1581,7 +1581,6 @@ MCompare::infer(JSContext *cx, BaselineInspector *inspector, jsbytecode *pc)
bool relationalEq = !(looseEq || strictEq);
// Comparisons on unsigned integers may be treated as UInt32.
MDefinition *newlhs, *newrhs;
if (tryUseUnsignedOperands()) {
compareType_ = Compare_UInt32;
return;

View File

@ -3206,7 +3206,20 @@ class MMathFunction
Tan,
ACos,
ASin,
ATan
ATan,
Log10,
Log2,
Log1P,
ExpM1,
CosH,
SinH,
TanH,
ACosH,
ASinH,
ATanH,
Sign,
Trunc,
Cbrt
};
private:

View File

@ -895,6 +895,9 @@ MAbs::computeRange()
Range other(getOperand(0));
setRange(Range::abs(&other));
if (implicitTruncate_ && !range()->isInt32())
setRange(new Range(INT32_MIN, INT32_MAX));
}
void
@ -917,6 +920,9 @@ MAdd::computeRange()
Range right(getOperand(1));
Range *next = Range::add(&left, &right);
setRange(next);
if (isTruncated() && !range()->isInt32())
setRange(new Range(INT32_MIN, INT32_MAX));
}
void
@ -928,6 +934,9 @@ MSub::computeRange()
Range right(getOperand(1));
Range *next = Range::sub(&left, &right);
setRange(next);
if (isTruncated() && !range()->isInt32())
setRange(new Range(INT32_MIN, INT32_MAX));
}
void

View File

@ -341,6 +341,11 @@ MacroAssemblerARM::ma_movPatchable(Imm32 imm_, Register dest,
Assembler::Condition c, RelocStyle rs, Instruction *i)
{
int32_t imm = imm_.value;
if (i) {
// If the access goes through an iterator (which this doesn't) then
// all issues pertaining to reading guard instructions.
i = InstructionIterator(i).cur();
}
switch(rs) {
case L_MOVWT:
as_movw(dest, Imm16(imm & 0xffff), c, i);

View File

@ -0,0 +1,14 @@
function m()
{
"use asm";
function f()
{
var x = 0;
var y = 0;
x = (((0x77777777 - 0xcccccccc) | 0) % -1) | 0;
y = (((0x7FFFFFFF + 0x7FFFFFFF) | 0) % -1) | 0;
return (x+y)|0;
}
return f;
}
assertEq(m()(), 0)

Some files were not shown because too many files have changed in this diff Show More