Merge m-c to b2g-inbound.
5
.hgtags
|
@ -99,3 +99,8 @@ ad0ae007aa9e03cd74e9005cd6652e544139b3b5 FIREFOX_AURORA_25_BASE
|
|||
05025f4889a0bf4dc99ce0c244c750adc002f015 FIREFOX_AURORA_27_BASE
|
||||
ba2cc1eda988a1614d8986ae145d28e1268409b9 FIREFOX_AURORA_29_BASE-m
|
||||
ba2cc1eda988a1614d8986ae145d28e1268409b9 Tagging for mozilla-central version bumps CLOSED TREE DONTBUILD
|
||||
ba2cc1eda988a1614d8986ae145d28e1268409b9 Tagging for mozilla-central version bumps CLOSED TREE DONTBUILD
|
||||
0000000000000000000000000000000000000000 Tagging for mozilla-central version bumps CLOSED TREE DONTBUILD
|
||||
ba2cc1eda988a1614d8986ae145d28e1268409b9 FIREFOX_AURORA_29_BASE-m
|
||||
0000000000000000000000000000000000000000 FIREFOX_AURORA_29_BASE-m
|
||||
ba2cc1eda988a1614d8986ae145d28e1268409b9 FIREFOX_AURORA_29_BASE
|
||||
|
|
|
@ -231,18 +231,12 @@ HyperTextAccessible::TextSubstring(int32_t aStartOffset, int32_t aEndOffset,
|
|||
endChild->AppendTextTo(aText, 0, endOffset - endChildOffset);
|
||||
}
|
||||
|
||||
Accessible*
|
||||
HyperTextAccessible::DOMPointToHypertextOffset(nsINode* aNode,
|
||||
int32_t aNodeOffset,
|
||||
int32_t* aHyperTextOffset,
|
||||
bool aIsEndOffset) const
|
||||
int32_t
|
||||
HyperTextAccessible::DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset,
|
||||
bool aIsEndOffset) const
|
||||
{
|
||||
if (!aHyperTextOffset)
|
||||
return nullptr;
|
||||
*aHyperTextOffset = 0;
|
||||
|
||||
if (!aNode)
|
||||
return nullptr;
|
||||
return 0;
|
||||
|
||||
uint32_t addTextOffset = 0;
|
||||
nsINode* findNode = nullptr;
|
||||
|
@ -254,10 +248,11 @@ HyperTextAccessible::DOMPointToHypertextOffset(nsINode* aNode,
|
|||
// For text nodes, aNodeOffset comes in as a character offset
|
||||
// Text offset will be added at the end, if we find the offset in this hypertext
|
||||
// We want the "skipped" offset into the text (rendered text without the extra whitespace)
|
||||
nsIFrame *frame = aNode->AsContent()->GetPrimaryFrame();
|
||||
NS_ENSURE_TRUE(frame, nullptr);
|
||||
nsIFrame* frame = aNode->AsContent()->GetPrimaryFrame();
|
||||
NS_ENSURE_TRUE(frame, 0);
|
||||
|
||||
nsresult rv = ContentToRenderedOffset(frame, aNodeOffset, &addTextOffset);
|
||||
NS_ENSURE_SUCCESS(rv, nullptr);
|
||||
NS_ENSURE_SUCCESS(rv, 0);
|
||||
// Get the child node and
|
||||
findNode = aNode;
|
||||
|
||||
|
@ -276,8 +271,7 @@ HyperTextAccessible::DOMPointToHypertextOffset(nsINode* aNode,
|
|||
if (aNode == GetNode()) {
|
||||
// Case #1: this accessible has no children and thus has empty text,
|
||||
// we can only be at hypertext offset 0.
|
||||
*aHyperTextOffset = 0;
|
||||
return nullptr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Case #2: there are no children, we're at this node.
|
||||
|
@ -295,7 +289,7 @@ HyperTextAccessible::DOMPointToHypertextOffset(nsINode* aNode,
|
|||
|
||||
// Get accessible for this findNode, or if that node isn't accessible, use the
|
||||
// accessible for the next DOM node which has one (based on forward depth first search)
|
||||
Accessible* descendantAcc = nullptr;
|
||||
Accessible* descendant = nullptr;
|
||||
if (findNode) {
|
||||
nsCOMPtr<nsIContent> findContent(do_QueryInterface(findNode));
|
||||
if (findContent && findContent->IsHTML() &&
|
||||
|
@ -305,18 +299,17 @@ HyperTextAccessible::DOMPointToHypertextOffset(nsINode* aNode,
|
|||
nsGkAtoms::_true,
|
||||
eIgnoreCase)) {
|
||||
// This <br> is the hacky "bogus node" used when there is no text in a control
|
||||
*aHyperTextOffset = 0;
|
||||
return nullptr;
|
||||
return 0;
|
||||
}
|
||||
descendantAcc = GetFirstAvailableAccessible(findNode);
|
||||
descendant = GetFirstAvailableAccessible(findNode);
|
||||
}
|
||||
|
||||
// From the descendant, go up and get the immediate child of this hypertext
|
||||
Accessible* childAccAtOffset = nullptr;
|
||||
while (descendantAcc) {
|
||||
Accessible* parentAcc = descendantAcc->Parent();
|
||||
if (parentAcc == this) {
|
||||
childAccAtOffset = descendantAcc;
|
||||
Accessible* childAtOffset = nullptr;
|
||||
while (descendant) {
|
||||
Accessible* parent = descendant->Parent();
|
||||
if (parent == this) {
|
||||
childAtOffset = descendant;
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -328,42 +321,17 @@ HyperTextAccessible::DOMPointToHypertextOffset(nsINode* aNode,
|
|||
// is not at 0 offset then the returned offset should be after an embedded
|
||||
// character the original point belongs to.
|
||||
if (aIsEndOffset)
|
||||
addTextOffset = (addTextOffset > 0 || descendantAcc->IndexInParent() > 0) ? 1 : 0;
|
||||
addTextOffset = (addTextOffset > 0 || descendant->IndexInParent() > 0) ? 1 : 0;
|
||||
else
|
||||
addTextOffset = 0;
|
||||
|
||||
descendantAcc = parentAcc;
|
||||
descendant = parent;
|
||||
}
|
||||
|
||||
// Loop through, adding offsets until we reach childAccessible
|
||||
// If childAccessible is null we will end up adding up the entire length of
|
||||
// the hypertext, which is good -- it just means our offset node
|
||||
// came after the last accessible child's node
|
||||
uint32_t childCount = ChildCount();
|
||||
|
||||
uint32_t childIdx = 0;
|
||||
Accessible* childAcc = nullptr;
|
||||
for (; childIdx < childCount; childIdx++) {
|
||||
childAcc = mChildren[childIdx];
|
||||
if (childAcc == childAccAtOffset)
|
||||
break;
|
||||
|
||||
*aHyperTextOffset += nsAccUtils::TextLength(childAcc);
|
||||
}
|
||||
|
||||
if (childIdx < childCount) {
|
||||
*aHyperTextOffset += addTextOffset;
|
||||
NS_ASSERTION(childAcc == childAccAtOffset,
|
||||
"These should be equal whenever we exit loop and childAcc != nullptr");
|
||||
|
||||
if (childIdx < childCount - 1 ||
|
||||
addTextOffset < nsAccUtils::TextLength(childAccAtOffset)) {
|
||||
// If not at end of last text node, we will return the accessible we were in
|
||||
return childAccAtOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
// If the given DOM point cannot be mapped into offset relative this hypertext
|
||||
// offset then return length as fallback value.
|
||||
return childAtOffset ?
|
||||
GetChildOffset(childAtOffset) + addTextOffset : CharacterCount();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -490,17 +458,14 @@ HyperTextAccessible::FindOffset(int32_t aOffset, nsDirection aDirection,
|
|||
if (!pos.mResultContent)
|
||||
return -1;
|
||||
|
||||
// Turn the resulting node and offset into a hyperTextOffset
|
||||
// If finalAccessible is nullptr, then DOMPointToHypertextOffset() searched
|
||||
// through the hypertext children without finding the node/offset position.
|
||||
int32_t hyperTextOffset = 0;
|
||||
Accessible* finalAccessible =
|
||||
DOMPointToHypertextOffset(pos.mResultContent, pos.mContentOffset,
|
||||
&hyperTextOffset, aDirection == eDirNext);
|
||||
// Turn the resulting DOM point into an offset.
|
||||
int32_t hyperTextOffset = DOMPointToOffset(pos.mResultContent,
|
||||
pos.mContentOffset,
|
||||
aDirection == eDirNext);
|
||||
|
||||
// If we reached the end during search, this means we didn't find the DOM point
|
||||
// and we're actually at the start of the paragraph
|
||||
if (!finalAccessible && aDirection == eDirPrevious)
|
||||
if (hyperTextOffset == CharacterCount() && aDirection == eDirPrevious)
|
||||
return 0;
|
||||
|
||||
return hyperTextOffset;
|
||||
|
@ -1173,9 +1138,7 @@ HyperTextAccessible::CaretOffset() const
|
|||
return -1;
|
||||
}
|
||||
|
||||
int32_t caretOffset = -1;
|
||||
DOMPointToHypertextOffset(focusNode, focusOffset, &caretOffset);
|
||||
return caretOffset;
|
||||
return DOMPointToOffset(focusNode, focusOffset);
|
||||
}
|
||||
|
||||
int32_t
|
||||
|
@ -1384,13 +1347,8 @@ HyperTextAccessible::SelectionBoundsAt(int32_t aSelectionNum,
|
|||
endOffset = tempOffset;
|
||||
}
|
||||
|
||||
Accessible* startAccessible =
|
||||
DOMPointToHypertextOffset(startNode, startOffset, aStartOffset);
|
||||
if (!startAccessible) {
|
||||
*aStartOffset = 0; // Could not find start point within this hypertext, so starts before
|
||||
}
|
||||
|
||||
DOMPointToHypertextOffset(endNode, endOffset, aEndOffset, true);
|
||||
*aStartOffset = DOMPointToOffset(startNode, startOffset);
|
||||
*aEndOffset = DOMPointToOffset(endNode, endOffset, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -1781,7 +1739,7 @@ nsresult
|
|||
HyperTextAccessible::RangeBoundToHypertextOffset(nsRange* aRange,
|
||||
bool aIsStartBound,
|
||||
bool aIsStartHTOffset,
|
||||
int32_t* aHTOffset)
|
||||
int32_t* aOffset)
|
||||
{
|
||||
nsINode* node = nullptr;
|
||||
int32_t nodeOffset = 0;
|
||||
|
@ -1794,12 +1752,7 @@ HyperTextAccessible::RangeBoundToHypertextOffset(nsRange* aRange,
|
|||
nodeOffset = aRange->EndOffset();
|
||||
}
|
||||
|
||||
Accessible* startAcc =
|
||||
DOMPointToHypertextOffset(node, nodeOffset, aHTOffset);
|
||||
|
||||
if (aIsStartHTOffset && !startAcc)
|
||||
*aHTOffset = 0;
|
||||
|
||||
*aOffset = DOMPointToOffset(node, nodeOffset);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -99,34 +99,26 @@ public:
|
|||
// HyperTextAccessible: DOM point to text offset conversions.
|
||||
|
||||
/**
|
||||
* Turn a DOM Node and offset into a character offset into this hypertext.
|
||||
* Will look for closest match when the DOM node does not have an accessible
|
||||
* object associated with it. Will return an offset for the end of
|
||||
* the string if the node is not found.
|
||||
*
|
||||
* @param aNode - the node to look for
|
||||
* @param aNodeOffset - the offset to look for
|
||||
* if -1 just look directly for the node
|
||||
* if >=0 and aNode is text, this represents a char offset
|
||||
* if >=0 and aNode is not text, this represents a child node offset
|
||||
* @param aResultOffset - the character offset into the current
|
||||
* HyperTextAccessible
|
||||
* @param aIsEndOffset - if true, then then this offset is not inclusive. The character
|
||||
* indicated by the offset returned is at [offset - 1]. This means
|
||||
* if the passed-in offset is really in a descendant, then the offset returned
|
||||
* will come just after the relevant embedded object characer.
|
||||
* If false, then the offset is inclusive. The character indicated
|
||||
* by the offset returned is at [offset]. If the passed-in offset in inside a
|
||||
* descendant, then the returned offset will be on the relevant embedded object char.
|
||||
*
|
||||
* @return the accessible child which contained the offset, if
|
||||
* it is within the current HyperTextAccessible,
|
||||
* otherwise nullptr
|
||||
*/
|
||||
Accessible* DOMPointToHypertextOffset(nsINode *aNode,
|
||||
int32_t aNodeOffset,
|
||||
int32_t* aHypertextOffset,
|
||||
bool aIsEndOffset = false) const;
|
||||
* Turn a DOM point (node and offset) into a character offset of this
|
||||
* hypertext. Will look for closest match when the DOM node does not have
|
||||
* an accessible object associated with it. Will return an offset for the end
|
||||
* of the string if the node is not found.
|
||||
*
|
||||
* @param aNode [in] the node to look for
|
||||
* @param aNodeOffset [in] the offset to look for
|
||||
* if -1 just look directly for the node
|
||||
* if >=0 and aNode is text, this represents a char offset
|
||||
* if >=0 and aNode is not text, this represents a child node offset
|
||||
* @param aIsEndOffset [in] if true, then then this offset is not inclusive. The character
|
||||
* indicated by the offset returned is at [offset - 1]. This means
|
||||
* if the passed-in offset is really in a descendant, then the offset returned
|
||||
* will come just after the relevant embedded object characer.
|
||||
* If false, then the offset is inclusive. The character indicated
|
||||
* by the offset returned is at [offset]. If the passed-in offset in inside a
|
||||
* descendant, then the returned offset will be on the relevant embedded object char.
|
||||
*/
|
||||
int32_t DOMPointToOffset(nsINode* aNode, int32_t aNodeOffset,
|
||||
bool aIsEndOffset = false) const;
|
||||
|
||||
/**
|
||||
* Convert start and end hypertext offsets into DOM range.
|
||||
|
@ -480,7 +472,7 @@ protected:
|
|||
* Return hyper text offset for the specified bound of the given DOM range.
|
||||
* If the bound is outside of the hyper text then offset value is either
|
||||
* 0 or number of characters of hyper text, it depends on type of requested
|
||||
* offset. The method is a wrapper for DOMPointToHypertextOffset.
|
||||
* offset. The method is a wrapper for DOMPointToOffset.
|
||||
*
|
||||
* @param aRange [in] the given range
|
||||
* @param aIsStartBound [in] specifies whether the required range bound is
|
||||
|
|
|
@ -132,8 +132,12 @@ var gSimpleMatchFunc = function gSimpleMatchFunc(aAccessible) {
|
|||
case Roles.LINK:
|
||||
case Roles.HEADER:
|
||||
case Roles.HEADING:
|
||||
return hasZeroOrSingleChildDescendants() ?
|
||||
(Filters.MATCH | Filters.IGNORE_SUBTREE) : (Filters.IGNORE);
|
||||
if ((aAccessible.childCount > 0 || aAccessible.name) &&
|
||||
hasZeroOrSingleChildDescendants()) {
|
||||
return Filters.MATCH | Filters.IGNORE_SUBTREE;
|
||||
} else {
|
||||
return Filters.IGNORE;
|
||||
}
|
||||
default:
|
||||
// Ignore the subtree, if there is one. So that we don't land on
|
||||
// the same content that was already presented by its parent.
|
||||
|
@ -217,7 +221,10 @@ this.TraversalRules = {
|
|||
}),
|
||||
|
||||
Heading: new BaseTraversalRule(
|
||||
[Roles.HEADING]),
|
||||
[Roles.HEADING],
|
||||
function Heading_match(aAccessible) {
|
||||
return aAccessible.childCount > 0 ? Filters.MATCH : Filters.IGNORE;
|
||||
}),
|
||||
|
||||
ListItem: new BaseTraversalRule(
|
||||
[Roles.LISTITEM,
|
||||
|
|
|
@ -82,6 +82,7 @@
|
|||
<a id="anchor-2">Sentences</a>
|
||||
<a id="link-3" href="http://www.example.com">Link the third</a>
|
||||
<hr id="separator-1">
|
||||
<h6 id="heading-6"></h6>
|
||||
<table id="table-1">
|
||||
<tr>
|
||||
<td>3</td>
|
||||
|
|
|
@ -95,6 +95,30 @@
|
|||
queueTraversalSequence(gQueue, docAcc, TraversalRules.Table, null,
|
||||
['table-1', 'table-2']);
|
||||
|
||||
queueTraversalSequence(gQueue, docAcc, TraversalRules.Simple, null,
|
||||
['heading-1', 'Name:', 'input-1-1', 'label-1-2',
|
||||
'button-1-1', 'Radios are old: ', 'radio-1-1',
|
||||
'Radios are new: ', 'radio-1-2', 'Password:',
|
||||
'input-1-3', 'Unlucky number:', 'input-1-4',
|
||||
'button-1-2', 'Check me: ', 'checkbox-1-1',
|
||||
'select-1-1', 'Value 1', 'Value 2', 'Value 3',
|
||||
'Check me too: ', 'checkbox-1-2', 'But not me: ',
|
||||
'Or me! ', 'Value 1', 'Value 2', 'Value 3',
|
||||
'Electronic mailing address:', 'input-1-5',
|
||||
'button-1-3', 'heading-2', 'heading-3',
|
||||
'button-2-1', 'button-2-2', 'button-2-3',
|
||||
'button-2-4', 'Programming Language',
|
||||
'A esoteric weapon wielded by only the most ' +
|
||||
'formidable warriors, for its unrelenting strict' +
|
||||
' power is unfathomable.',
|
||||
'Lists of Programming Languages', 'Lisp ',
|
||||
'Scheme', 'Racket', 'Clojure', 'JavaScript', 'heading-5',
|
||||
'image-2', 'image-3', 'Not actually an image',
|
||||
'link-1', 'anchor-1', 'link-2', 'anchor-2', 'link-3',
|
||||
'3', '1', '4', '1', 'Just an innocuous separator',
|
||||
'Dirty Words', 'Meaning', 'Mud', 'Wet Dirt',
|
||||
'Dirt', 'Messy Stuff']);
|
||||
|
||||
gQueue.invoke();
|
||||
}
|
||||
|
||||
|
|
|
@ -265,7 +265,9 @@ function setVCTextInvoker(aDocAcc, aPivotMoveMethod, aBoundary, aTextOffsets, aI
|
|||
|
||||
this.getID = function setVCPosInvoker_getID()
|
||||
{
|
||||
return "Do " + (expectMove ? "" : "no-op ") + aPivotMoveMethod;
|
||||
return "Do " + (expectMove ? "" : "no-op ") + aPivotMoveMethod + " in " +
|
||||
prettyName(aIdOrNameOrAcc) + ", " + boundaryToString(aBoundary) +
|
||||
", [" + aTextOffsets + "]";
|
||||
};
|
||||
|
||||
if (expectMove) {
|
||||
|
@ -379,7 +381,9 @@ function queueTraversalSequence(aQueue, aDocAcc, aRule, aModalRoot, aSequence)
|
|||
// Add modal root (if any)
|
||||
aQueue.push(new setModalRootInvoker(aDocAcc, aModalRoot, 0));
|
||||
|
||||
for (var i = 0; i < aSequence.length; i++) {
|
||||
aQueue.push(new setVCPosInvoker(aDocAcc, "moveFirst", aRule, aSequence[0]));
|
||||
|
||||
for (var i = 1; i < aSequence.length; i++) {
|
||||
var invoker =
|
||||
new setVCPosInvoker(aDocAcc, "moveNext", aRule, aSequence[i]);
|
||||
aQueue.push(invoker);
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
</script>
|
||||
|
||||
<script type="application/javascript" src="../common.js"></script>
|
||||
<script type="application/javascript" src="../text.js"></script>
|
||||
<script type="application/javascript" src="../browser.js"></script>
|
||||
<script type="application/javascript" src="../events.js"></script>
|
||||
<script type="application/javascript" src="../role.js"></script>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
const { Cc, Ci } = require('chrome');
|
||||
const { isPrivate } = require('sdk/private-browsing');
|
||||
const { isWindowPBSupported } = require('sdk/private-browsing/utils');
|
||||
const { onFocus, getMostRecentWindow, getWindowTitle,
|
||||
const { onFocus, getMostRecentWindow, getWindowTitle, getInnerId,
|
||||
getFrames, windows, open: openWindow, isWindowPrivate } = require('sdk/window/utils');
|
||||
const { open, close, focus, promise } = require('sdk/window/helpers');
|
||||
const { browserWindows } = require("sdk/windows");
|
||||
|
@ -25,31 +25,28 @@ function makeEmptyBrowserWindow(options) {
|
|||
chrome: true,
|
||||
private: !!options.private
|
||||
}
|
||||
});
|
||||
}).then(focus);
|
||||
}
|
||||
|
||||
exports.testWindowTrackerIgnoresPrivateWindows = function(assert, done) {
|
||||
var myNonPrivateWindow, myPrivateWindow;
|
||||
var finished = false;
|
||||
var privateWindow;
|
||||
var myNonPrivateWindowId, myPrivateWindowId;
|
||||
var privateWindowClosed = false;
|
||||
var privateWindowOpened = false;
|
||||
var trackedWindowIds = [];
|
||||
|
||||
let wt = winUtils.WindowTracker({
|
||||
onTrack: function(window) {
|
||||
if (window === myPrivateWindow) {
|
||||
assert.equal(isPrivate(window), isWindowPBSupported);
|
||||
privateWindowOpened = true;
|
||||
}
|
||||
let id = getInnerId(window);
|
||||
trackedWindowIds.push(id);
|
||||
},
|
||||
onUntrack: function(window) {
|
||||
if (window === myPrivateWindow && isWindowPBSupported) {
|
||||
let id = getInnerId(window);
|
||||
if (id === myPrivateWindowId) {
|
||||
privateWindowClosed = true;
|
||||
}
|
||||
|
||||
if (window === myNonPrivateWindow) {
|
||||
assert.equal(privateWindowClosed, isWindowPBSupported);
|
||||
assert.ok(privateWindowOpened);
|
||||
if (id === myNonPrivateWindowId) {
|
||||
assert.equal(privateWindowClosed, true, 'private window was untracked');
|
||||
wt.unload();
|
||||
done();
|
||||
}
|
||||
|
@ -57,27 +54,23 @@ exports.testWindowTrackerIgnoresPrivateWindows = function(assert, done) {
|
|||
});
|
||||
|
||||
// make a new private window
|
||||
myPrivateWindow = openWindow(BROWSER, {
|
||||
features: {
|
||||
private: true
|
||||
}
|
||||
});
|
||||
promise(myPrivateWindow, 'load').then(function(window) {
|
||||
makeEmptyBrowserWindow({ private: true }).then(function(window) {
|
||||
myPrivateWindowId = getInnerId(window);
|
||||
|
||||
assert.ok(trackedWindowIds.indexOf(myPrivateWindowId) >= 0, 'private window was tracked');
|
||||
assert.equal(isPrivate(window), isWindowPBSupported, 'private window isPrivate');
|
||||
assert.equal(isWindowPrivate(window), isWindowPBSupported);
|
||||
assert.ok(getFrames(window).length > 1, 'there are frames for private window');
|
||||
assert.equal(getWindowTitle(window), window.document.title,
|
||||
'getWindowTitle works');
|
||||
|
||||
close(myPrivateWindow).then(function() {
|
||||
close(window).then(function() {
|
||||
assert.pass('private window was closed');
|
||||
|
||||
makeEmptyBrowserWindow().then(function(window) {
|
||||
myNonPrivateWindow = window;
|
||||
assert.notDeepEqual(myPrivateWindow, myNonPrivateWindow);
|
||||
assert.pass('opened new window');
|
||||
close(myNonPrivateWindow).then(function() {
|
||||
assert.pass('non private window was closed');
|
||||
})
|
||||
myNonPrivateWindowId = getInnerId(window);
|
||||
assert.notEqual(myPrivateWindowId, myNonPrivateWindowId, 'non private window was opened');
|
||||
close(window);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -95,7 +88,7 @@ exports.testSettingActiveWindowDoesNotIgnorePrivateWindow = function(assert, don
|
|||
// make a new private window
|
||||
makeEmptyBrowserWindow({
|
||||
private: true
|
||||
}).then(focus).then(function(window) {
|
||||
}).then(function(window) {
|
||||
let continueAfterFocus = function(window) onFocus(window).then(nextTest);
|
||||
|
||||
// PWPB case
|
||||
|
@ -172,7 +165,7 @@ exports.testActiveWindowDoesNotIgnorePrivateWindow = function(assert, done) {
|
|||
// make a new private window
|
||||
makeEmptyBrowserWindow({
|
||||
private: true
|
||||
}).then(focus).then(function(window) {
|
||||
}).then(function(window) {
|
||||
// PWPB case
|
||||
if (isWindowPBSupported) {
|
||||
assert.equal(isPrivate(winUtils.activeWindow), true,
|
||||
|
@ -212,7 +205,7 @@ exports.testWindowIteratorIgnoresPrivateWindows = function(assert, done) {
|
|||
// make a new private window
|
||||
makeEmptyBrowserWindow({
|
||||
private: true
|
||||
}).then(focus).then(function(window) {
|
||||
}).then(function(window) {
|
||||
assert.equal(isWindowPrivate(window), isWindowPBSupported);
|
||||
assert.ok(toArray(winUtils.windowIterator()).indexOf(window) > -1,
|
||||
"window is in windowIterator()");
|
||||
|
|
|
@ -128,12 +128,14 @@ SourcesView.prototype = Heritage.extend(WidgetMethods, {
|
|||
let url = aSource.url;
|
||||
let label = SourceUtils.getSourceLabel(url.split(" -> ").pop());
|
||||
let group = SourceUtils.getSourceGroup(url.split(" -> ").pop());
|
||||
let unicodeUrl = NetworkHelper.convertToUnicode(unescape(url));
|
||||
|
||||
let contents = document.createElement("label");
|
||||
contents.className = "plain dbg-source-item";
|
||||
contents.setAttribute("value", label);
|
||||
contents.setAttribute("crop", "start");
|
||||
contents.setAttribute("flex", "1");
|
||||
contents.setAttribute("tooltiptext", unicodeUrl);
|
||||
|
||||
// Append a source item to this container.
|
||||
this.push([contents, url], {
|
||||
|
|
|
@ -45,6 +45,13 @@ function testSourcesDisplay() {
|
|||
is(gSources.itemCount, 2,
|
||||
"Found the expected number of sources.");
|
||||
|
||||
is(gSources.items[0].target.querySelector(".dbg-source-item").getAttribute("tooltiptext"),
|
||||
EXAMPLE_URL + "code_script-switching-01.js",
|
||||
"The correct tooltip text is displayed for the first source.");
|
||||
is(gSources.items[1].target.querySelector(".dbg-source-item").getAttribute("tooltiptext"),
|
||||
EXAMPLE_URL + "code_script-switching-02.js",
|
||||
"The correct tooltip text is displayed for the second source.");
|
||||
|
||||
ok(gSources.containsValue(EXAMPLE_URL + gLabel1),
|
||||
"First source url is incorrect.");
|
||||
ok(gSources.containsValue(EXAMPLE_URL + gLabel2),
|
||||
|
|
|
@ -338,8 +338,9 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||
this._summary = $("#requests-menu-network-summary-label");
|
||||
this._summary.setAttribute("value", L10N.getStr("networkMenu.empty"));
|
||||
|
||||
this.sortContents(this._byTiming);
|
||||
this.allowFocusOnRightClick = true;
|
||||
this.widget.maintainSelectionVisible = false;
|
||||
this.maintainSelectionVisible = true;
|
||||
this.widget.autoscrollWithAppendedItems = true;
|
||||
|
||||
this.widget.addEventListener("select", this._onSelect, false);
|
||||
|
@ -471,27 +472,6 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new custom request form populated with the data from
|
||||
* the currently selected request.
|
||||
*/
|
||||
cloneSelectedRequest: function() {
|
||||
let selected = this.selectedItem.attachment;
|
||||
|
||||
// Create the element node for the network request item.
|
||||
let menuView = this._createMenuView(selected.method, selected.url);
|
||||
|
||||
// Append a network request item to this container.
|
||||
let newItem = this.push([menuView], {
|
||||
attachment: Object.create(selected, {
|
||||
isCustom: { value: true }
|
||||
})
|
||||
});
|
||||
|
||||
// Immediately switch to new request pane.
|
||||
this.selectedItem = newItem;
|
||||
},
|
||||
|
||||
/**
|
||||
* Opens selected item in a new tab.
|
||||
*/
|
||||
|
@ -521,16 +501,37 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Create a new custom request form populated with the data from
|
||||
* the currently selected request.
|
||||
*/
|
||||
cloneSelectedRequest: function() {
|
||||
let selected = this.selectedItem.attachment;
|
||||
|
||||
// Create the element node for the network request item.
|
||||
let menuView = this._createMenuView(selected.method, selected.url);
|
||||
|
||||
// Append a network request item to this container.
|
||||
let newItem = this.push([menuView], {
|
||||
attachment: Object.create(selected, {
|
||||
isCustom: { value: true }
|
||||
})
|
||||
});
|
||||
|
||||
// Immediately switch to new request pane.
|
||||
this.selectedItem = newItem;
|
||||
},
|
||||
|
||||
/**
|
||||
* Send a new HTTP request using the data in the custom request form.
|
||||
*/
|
||||
sendCustomRequest: function() {
|
||||
let selected = this.selectedItem.attachment;
|
||||
let data = Object.create(selected);
|
||||
|
||||
let data = Object.create(selected, {
|
||||
headers: { value: selected.requestHeaders.headers }
|
||||
});
|
||||
|
||||
if (selected.requestHeaders) {
|
||||
data.headers = selected.requestHeaders.headers;
|
||||
}
|
||||
if (selected.requestPostData) {
|
||||
data.body = selected.requestPostData.postData.text;
|
||||
}
|
||||
|
@ -548,9 +549,8 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
|
|||
*/
|
||||
closeCustomRequest: function() {
|
||||
this.remove(this.selectedItem);
|
||||
|
||||
NetMonitorView.Sidebar.toggle(false);
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Filters all network requests in this container by a specified type.
|
||||
|
@ -2126,26 +2126,41 @@ NetworkDetailsView.prototype = {
|
|||
// Handle json, which we tentatively identify by checking the MIME type
|
||||
// for "json" after any word boundary. This works for the standard
|
||||
// "application/json", and also for custom types like "x-bigcorp-json".
|
||||
// This should be marginally more reliable than just looking for "json".
|
||||
if (/\bjson/.test(mimeType)) {
|
||||
let jsonpRegex = /^[a-zA-Z0-9_$]+\(|\)$/g; // JSONP with callback.
|
||||
let sanitizedJSON = aString.replace(jsonpRegex, "");
|
||||
let callbackPadding = aString.match(jsonpRegex);
|
||||
// Additionally, we also directly parse the response text content to
|
||||
// verify whether it's json or not, to handle responses incorrectly
|
||||
// labeled as text/plain instead.
|
||||
let jsonMimeType, jsonObject, jsonObjectParseError;
|
||||
try {
|
||||
// Test the mime type *and* parse the string, because "JSONP" responses
|
||||
// (json with callback) aren't actually valid json.
|
||||
jsonMimeType = /\bjson/.test(mimeType);
|
||||
jsonObject = JSON.parse(aString);
|
||||
} catch (e) {
|
||||
jsonObjectParseError = e;
|
||||
}
|
||||
if (jsonMimeType || jsonObject) {
|
||||
// Extract the actual json substring in case this might be a "JSONP".
|
||||
// This regex basically parses a function call and captures the
|
||||
// function name and arguments in two separate groups.
|
||||
let jsonpRegex = /^\s*([\w$]+)\s*\(\s*([^]*)\s*\)\s*;?\s*$/;
|
||||
let [_, callbackPadding, jsonpString] = aString.match(jsonpRegex) || [];
|
||||
|
||||
// Make sure this is a valid JSON object first. If so, nicely display
|
||||
// the parsing results in a variables view. Otherwise, simply show
|
||||
// the contents as plain text.
|
||||
try {
|
||||
var jsonObject = JSON.parse(sanitizedJSON);
|
||||
} catch (e) {
|
||||
var parsingError = e;
|
||||
if (callbackPadding && jsonpString) {
|
||||
try {
|
||||
jsonObject = JSON.parse(jsonpString);
|
||||
} catch (e) {
|
||||
jsonObjectParseError = e;
|
||||
}
|
||||
}
|
||||
|
||||
// Valid JSON.
|
||||
// Valid JSON or JSONP.
|
||||
if (jsonObject) {
|
||||
$("#response-content-json-box").hidden = false;
|
||||
let jsonScopeName = callbackPadding
|
||||
? L10N.getFormatStr("jsonpScopeName", callbackPadding[0].slice(0, -1))
|
||||
? L10N.getFormatStr("jsonpScopeName", callbackPadding)
|
||||
: L10N.getStr("jsonScopeName");
|
||||
|
||||
return this._json.controller.setSingleVariable({
|
||||
|
@ -2157,8 +2172,8 @@ NetworkDetailsView.prototype = {
|
|||
else {
|
||||
$("#response-content-textarea-box").hidden = false;
|
||||
let infoHeader = $("#response-content-info-header");
|
||||
infoHeader.setAttribute("value", parsingError);
|
||||
infoHeader.setAttribute("tooltiptext", parsingError);
|
||||
infoHeader.setAttribute("value", jsonObjectParseError);
|
||||
infoHeader.setAttribute("tooltiptext", jsonObjectParseError);
|
||||
infoHeader.hidden = false;
|
||||
return NetMonitorView.editor("#response-content-textarea").then(aEditor => {
|
||||
aEditor.setMode(Editor.modes.js);
|
||||
|
@ -2328,13 +2343,9 @@ PerformanceStatisticsView.prototype = {
|
|||
id: "#primed-cache-chart",
|
||||
title: "charts.cacheEnabled",
|
||||
data: this._sanitizeChartDataSource(aItems),
|
||||
sorted: true,
|
||||
totals: {
|
||||
size: L10N.getStr("charts.totalSize"),
|
||||
time: L10N.getStr("charts.totalTime2"),
|
||||
cached: L10N.getStr("charts.totalCached"),
|
||||
count: L10N.getStr("charts.totalCount")
|
||||
}
|
||||
strings: this._commonChartStrings,
|
||||
totals: this._commonChartTotals,
|
||||
sorted: true
|
||||
});
|
||||
window.emit(EVENTS.PRIMED_CACHE_CHART_DISPLAYED);
|
||||
},
|
||||
|
@ -2350,26 +2361,54 @@ PerformanceStatisticsView.prototype = {
|
|||
id: "#empty-cache-chart",
|
||||
title: "charts.cacheDisabled",
|
||||
data: this._sanitizeChartDataSource(aItems, true),
|
||||
sorted: true,
|
||||
totals: {
|
||||
size: L10N.getStr("charts.totalSize"),
|
||||
time: L10N.getStr("charts.totalTime2"),
|
||||
cached: L10N.getStr("charts.totalCached"),
|
||||
count: L10N.getStr("charts.totalCount")
|
||||
}
|
||||
strings: this._commonChartStrings,
|
||||
totals: this._commonChartTotals,
|
||||
sorted: true
|
||||
});
|
||||
window.emit(EVENTS.EMPTY_CACHE_CHART_DISPLAYED);
|
||||
},
|
||||
|
||||
/**
|
||||
* Common stringifier predicates used for items and totals in both the
|
||||
* "primed" and "empty" cache charts.
|
||||
*/
|
||||
_commonChartStrings: {
|
||||
size: value => {
|
||||
let string = L10N.numberWithDecimals(value / 1024, CONTENT_SIZE_DECIMALS);
|
||||
return L10N.getFormatStr("charts.sizeKB", string);
|
||||
},
|
||||
time: value => {
|
||||
let string = L10N.numberWithDecimals(value / 1000, REQUEST_TIME_DECIMALS);
|
||||
return L10N.getFormatStr("charts.totalS", string);
|
||||
}
|
||||
},
|
||||
_commonChartTotals: {
|
||||
size: total => {
|
||||
let string = L10N.numberWithDecimals(total / 1024, CONTENT_SIZE_DECIMALS);
|
||||
return L10N.getFormatStr("charts.totalSize", string);
|
||||
},
|
||||
time: total => {
|
||||
let seconds = total / 1000;
|
||||
let string = L10N.numberWithDecimals(seconds, REQUEST_TIME_DECIMALS);
|
||||
return PluralForm.get(seconds, L10N.getStr("charts.totalSeconds")).replace("#1", string);
|
||||
},
|
||||
cached: total => {
|
||||
return L10N.getFormatStr("charts.totalCached", total);
|
||||
},
|
||||
count: total => {
|
||||
return L10N.getFormatStr("charts.totalCount", total);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Adds a specific chart to this container.
|
||||
*
|
||||
* @param object
|
||||
* An object containing all or some the following properties:
|
||||
* - id: either "#primed-cache-chart" or "#empty-cache-chart"
|
||||
* - title/data/sorted/totals: @see Chart.jsm for details
|
||||
* - title/data/strings/totals/sorted: @see Chart.jsm for details
|
||||
*/
|
||||
_createChart: function({ id, title, data, sorted, totals }) {
|
||||
_createChart: function({ id, title, data, strings, totals, sorted }) {
|
||||
let container = $(id);
|
||||
|
||||
// Nuke all existing charts of the specified type.
|
||||
|
@ -2382,8 +2421,9 @@ PerformanceStatisticsView.prototype = {
|
|||
diameter: NETWORK_ANALYSIS_PIE_CHART_DIAMETER,
|
||||
title: L10N.getStr(title),
|
||||
data: data,
|
||||
sorted: sorted,
|
||||
totals: totals
|
||||
strings: strings,
|
||||
totals: totals,
|
||||
sorted: sorted
|
||||
});
|
||||
|
||||
chart.on("click", (_, item) => {
|
||||
|
@ -2448,13 +2488,6 @@ PerformanceStatisticsView.prototype = {
|
|||
data[type].count++;
|
||||
}
|
||||
|
||||
for (let chartItem of data) {
|
||||
let size = L10N.numberWithDecimals(chartItem.size / 1024, CONTENT_SIZE_DECIMALS);
|
||||
let time = L10N.numberWithDecimals(chartItem.time / 1000, REQUEST_TIME_DECIMALS);
|
||||
chartItem.size = L10N.getFormatStr("charts.sizeKB", size);
|
||||
chartItem.time = L10N.getFormatStr("charts.totalS", time);
|
||||
}
|
||||
|
||||
return data.filter(e => e.count > 0);
|
||||
},
|
||||
};
|
||||
|
|
|
@ -215,6 +215,11 @@
|
|||
data-key="flash"
|
||||
label="&netmonitorUI.footer.filterFlash;">
|
||||
</button>
|
||||
<button id="requests-menu-filter-other-button"
|
||||
class="requests-menu-filter-button requests-menu-footer-button"
|
||||
data-key="other"
|
||||
label="&netmonitorUI.footer.filterOther;">
|
||||
</button>
|
||||
<spacer id="requests-menu-spacer"
|
||||
class="requests-menu-footer-spacer"
|
||||
flex="100"/>
|
||||
|
|
|
@ -10,6 +10,7 @@ support-files =
|
|||
html_json-custom-mime-test-page.html
|
||||
html_json-long-test-page.html
|
||||
html_json-malformed-test-page.html
|
||||
html_json-text-mime-test-page.html
|
||||
html_jsonp-test-page.html
|
||||
html_navigate-test-page.html
|
||||
html_post-data-test-page.html
|
||||
|
@ -46,6 +47,7 @@ support-files =
|
|||
[browser_net_json-long.js]
|
||||
[browser_net_json-malformed.js]
|
||||
[browser_net_json_custom_mime.js]
|
||||
[browser_net_json_text_mime.js]
|
||||
[browser_net_jsonp.js]
|
||||
[browser_net_large-response.js]
|
||||
[browser_net_open_request_in_tab.js]
|
||||
|
|
|
@ -9,24 +9,27 @@ function test() {
|
|||
initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, Chart } = aMonitor.panelWin;
|
||||
let { document, L10N, Chart } = aMonitor.panelWin;
|
||||
let container = document.createElement("box");
|
||||
|
||||
let table = Chart.Table(document, {
|
||||
title: "Table title",
|
||||
data: [{
|
||||
label1: 1,
|
||||
label2: "11.1foo"
|
||||
label2: 11.1
|
||||
}, {
|
||||
label1: 2,
|
||||
label2: "12.2bar"
|
||||
label2: 12.2
|
||||
}, {
|
||||
label1: 3,
|
||||
label2: "13.3baz"
|
||||
label2: 13.3
|
||||
}],
|
||||
strings: {
|
||||
label2: (value, index) => value + ["foo", "bar", "baz"][index]
|
||||
},
|
||||
totals: {
|
||||
label1: "Hello %S",
|
||||
label2: "World %S"
|
||||
label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
|
||||
label2: value => "World " + L10N.numberWithDecimals(value, 2)
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@ function test() {
|
|||
title: "Table title",
|
||||
data: null,
|
||||
totals: {
|
||||
label1: "Hello %S",
|
||||
label2: "World %S"
|
||||
label1: value => "Hello " + L10N.numberWithDecimals(value, 2),
|
||||
label2: value => "World " + L10N.numberWithDecimals(value, 2)
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -9,24 +9,27 @@ function test() {
|
|||
initNetMonitor(SIMPLE_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, Chart } = aMonitor.panelWin;
|
||||
let { document, L10N, Chart } = aMonitor.panelWin;
|
||||
let container = document.createElement("box");
|
||||
|
||||
let chart = Chart.PieTable(document, {
|
||||
title: "Table title",
|
||||
data: [{
|
||||
size: 1,
|
||||
label: "11.1foo"
|
||||
label: 11.1
|
||||
}, {
|
||||
size: 2,
|
||||
label: "12.2bar"
|
||||
label: 12.2
|
||||
}, {
|
||||
size: 3,
|
||||
label: "13.3baz"
|
||||
label: 13.3
|
||||
}],
|
||||
strings: {
|
||||
label2: (value, index) => value + ["foo", "bar", "baz"][index]
|
||||
},
|
||||
totals: {
|
||||
size: "Hello %S",
|
||||
label: "World %S"
|
||||
size: value => "Hello " + L10N.numberWithDecimals(value, 2),
|
||||
label: value => "World " + L10N.numberWithDecimals(value, 2)
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests if JSON responses with unusal/custom MIME types are handled correctly.
|
||||
*/
|
||||
|
||||
function test() {
|
||||
initNetMonitor(JSON_TEXT_MIME_URL).then(([aTab, aDebuggee, aMonitor]) => {
|
||||
info("Starting test... ");
|
||||
|
||||
let { document, L10N, NetMonitorView } = aMonitor.panelWin;
|
||||
let { RequestsMenu } = NetMonitorView;
|
||||
|
||||
RequestsMenu.lazyUpdate = false;
|
||||
|
||||
waitForNetworkEvents(aMonitor, 1).then(() => {
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=json-text-mime", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
type: "plain",
|
||||
fullMimeType: "text/plain; charset=utf-8",
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
|
||||
time: true
|
||||
});
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.getElementById("details-pane-toggle"));
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.querySelectorAll("#details-pane tab")[3]);
|
||||
|
||||
let RESPONSE_BODY_DISPLAYED = aMonitor.panelWin.EVENTS.RESPONSE_BODY_DISPLAYED;
|
||||
waitFor(aMonitor.panelWin, RESPONSE_BODY_DISPLAYED)
|
||||
.then(testResponseTab)
|
||||
.then(() => teardown(aMonitor))
|
||||
.then(finish);
|
||||
|
||||
function testResponseTab() {
|
||||
let tab = document.querySelectorAll("#details-pane tab")[3];
|
||||
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];
|
||||
|
||||
is(tab.getAttribute("selected"), "true",
|
||||
"The response tab in the network details pane should be selected.");
|
||||
|
||||
is(tabpanel.querySelector("#response-content-info-header")
|
||||
.hasAttribute("hidden"), true,
|
||||
"The response info header doesn't have the intended visibility.");
|
||||
is(tabpanel.querySelector("#response-content-json-box")
|
||||
.hasAttribute("hidden"), false,
|
||||
"The response content json box doesn't have the intended visibility.");
|
||||
is(tabpanel.querySelector("#response-content-textarea-box")
|
||||
.hasAttribute("hidden"), true,
|
||||
"The response content textarea box doesn't have the intended visibility.");
|
||||
is(tabpanel.querySelector("#response-content-image-box")
|
||||
.hasAttribute("hidden"), true,
|
||||
"The response content image box doesn't have the intended visibility.");
|
||||
|
||||
is(tabpanel.querySelectorAll(".variables-view-scope").length, 1,
|
||||
"There should be 1 json scope displayed in this tabpanel.");
|
||||
is(tabpanel.querySelectorAll(".variables-view-property").length, 2,
|
||||
"There should be 2 json properties displayed in this tabpanel.");
|
||||
is(tabpanel.querySelectorAll(".variables-view-empty-notice").length, 0,
|
||||
"The empty notice should not be displayed in this tabpanel.");
|
||||
|
||||
let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
|
||||
is(jsonScope.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
"greeting", "The first json property name was incorrect.");
|
||||
is(jsonScope.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
|
||||
"\"Hello third-party JSON!\"", "The first json property value was incorrect.");
|
||||
|
||||
is(jsonScope.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
"__proto__", "The second json property name was incorrect.");
|
||||
is(jsonScope.querySelectorAll(".variables-view-property .value")[1].getAttribute("value"),
|
||||
"Object", "The second json property value was incorrect.");
|
||||
}
|
||||
});
|
||||
|
||||
aDebuggee.performRequests();
|
||||
});
|
||||
}
|
|
@ -10,11 +10,12 @@ function test() {
|
|||
info("Starting test... ");
|
||||
|
||||
let { document, L10N, NetMonitorView } = aMonitor.panelWin;
|
||||
let { RequestsMenu } = NetMonitorView;
|
||||
let { RequestsMenu, NetworkDetails } = NetMonitorView;
|
||||
|
||||
RequestsMenu.lazyUpdate = false;
|
||||
NetworkDetails._json.lazyEmpty = false;
|
||||
|
||||
waitForNetworkEvents(aMonitor, 1).then(() => {
|
||||
waitForNetworkEvents(aMonitor, 2).then(() => {
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(0),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=jsonp&jsonp=$_0123Fun", {
|
||||
status: 200,
|
||||
|
@ -24,19 +25,37 @@ function test() {
|
|||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.04),
|
||||
time: true
|
||||
});
|
||||
verifyRequestItemTarget(RequestsMenu.getItemAtIndex(1),
|
||||
"GET", CONTENT_TYPE_SJS + "?fmt=jsonp2&jsonp=$_4567Sad", {
|
||||
status: 200,
|
||||
statusText: "OK",
|
||||
type: "json",
|
||||
fullMimeType: "text/json; charset=utf-8",
|
||||
size: L10N.getFormatStrWithNumbers("networkMenu.sizeKB", 0.05),
|
||||
time: true
|
||||
});
|
||||
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.getElementById("details-pane-toggle"));
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.querySelectorAll("#details-pane tab")[3]);
|
||||
Task.spawn(function() {
|
||||
let RESPONSE_BODY_DISPLAYED = aMonitor.panelWin.EVENTS.RESPONSE_BODY_DISPLAYED;
|
||||
|
||||
let RESPONSE_BODY_DISPLAYED = aMonitor.panelWin.EVENTS.RESPONSE_BODY_DISPLAYED;
|
||||
waitFor(aMonitor.panelWin, RESPONSE_BODY_DISPLAYED)
|
||||
.then(testResponseTab)
|
||||
.then(() => teardown(aMonitor))
|
||||
.then(finish);
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.getElementById("details-pane-toggle"));
|
||||
EventUtils.sendMouseEvent({ type: "mousedown" },
|
||||
document.querySelectorAll("#details-pane tab")[3]);
|
||||
|
||||
function testResponseTab() {
|
||||
yield waitFor(aMonitor.panelWin, RESPONSE_BODY_DISPLAYED);
|
||||
testResponseTab("$_0123Fun", "\"Hello JSONP!\"");
|
||||
|
||||
RequestsMenu.selectedIndex = 1;
|
||||
|
||||
yield waitFor(aMonitor.panelWin, RESPONSE_BODY_DISPLAYED);
|
||||
testResponseTab("$_4567Sad", "\"Hello weird JSONP!\"");
|
||||
|
||||
yield teardown(aMonitor);
|
||||
finish();
|
||||
});
|
||||
|
||||
function testResponseTab(aFunction, aGreeting) {
|
||||
let tab = document.querySelectorAll("#details-pane tab")[3];
|
||||
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];
|
||||
|
||||
|
@ -66,13 +85,13 @@ function test() {
|
|||
let jsonScope = tabpanel.querySelectorAll(".variables-view-scope")[0];
|
||||
|
||||
is(jsonScope.querySelector(".name").getAttribute("value"),
|
||||
L10N.getFormatStr("jsonpScopeName", "$_0123Fun"),
|
||||
L10N.getFormatStr("jsonpScopeName", aFunction),
|
||||
"The json scope doesn't have the correct title.");
|
||||
|
||||
is(jsonScope.querySelectorAll(".variables-view-property .name")[0].getAttribute("value"),
|
||||
"greeting", "The first json property name was incorrect.");
|
||||
is(jsonScope.querySelectorAll(".variables-view-property .value")[0].getAttribute("value"),
|
||||
"\"Hello JSONP!\"", "The first json property value was incorrect.");
|
||||
aGreeting, "The first json property value was incorrect.");
|
||||
|
||||
is(jsonScope.querySelectorAll(".variables-view-property .name")[1].getAttribute("value"),
|
||||
"__proto__", "The second json property name was incorrect.");
|
||||
|
|
|
@ -26,6 +26,7 @@ const JSONP_URL = EXAMPLE_URL + "html_jsonp-test-page.html";
|
|||
const JSON_LONG_URL = EXAMPLE_URL + "html_json-long-test-page.html";
|
||||
const JSON_MALFORMED_URL = EXAMPLE_URL + "html_json-malformed-test-page.html";
|
||||
const JSON_CUSTOM_MIME_URL = EXAMPLE_URL + "html_json-custom-mime-test-page.html";
|
||||
const JSON_TEXT_MIME_URL = EXAMPLE_URL + "html_json-text-mime-test-page.html";
|
||||
const SORTING_URL = EXAMPLE_URL + "html_sorting-test-page.html";
|
||||
const FILTERING_URL = EXAMPLE_URL + "html_filter-test-page.html";
|
||||
const INFINITE_GET_URL = EXAMPLE_URL + "html_infinite-get-page.html";
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
<!-- Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ -->
|
||||
<!doctype html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Network Monitor test page</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<p>JSON text test</p>
|
||||
|
||||
<script type="text/javascript">
|
||||
function get(aAddress, aCallback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open("GET", aAddress, true);
|
||||
|
||||
xhr.onreadystatechange = function() {
|
||||
if (this.readyState == this.DONE) {
|
||||
aCallback();
|
||||
}
|
||||
};
|
||||
xhr.send(null);
|
||||
}
|
||||
|
||||
function performRequests() {
|
||||
get("sjs_content-type-test-server.sjs?fmt=json-text-mime", function() {
|
||||
// Done.
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -26,7 +26,9 @@
|
|||
|
||||
function performRequests() {
|
||||
get("sjs_content-type-test-server.sjs?fmt=jsonp&jsonp=$_0123Fun", function() {
|
||||
// Done.
|
||||
get("sjs_content-type-test-server.sjs?fmt=jsonp2&jsonp=$_4567Sad", function() {
|
||||
// Done.
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -95,6 +95,15 @@ function handleRequest(request, response) {
|
|||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "jsonp2": {
|
||||
let fun = params.filter((s) => s.contains("jsonp="))[0].split("=")[1];
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "text/json; charset=utf-8", false);
|
||||
maybeMakeCached();
|
||||
response.write(" " + fun + " ( { \"greeting\": \"Hello weird JSONP!\" } ) ; ");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "json-long": {
|
||||
let str = "{ \"greeting\": \"Hello long string JSON!\" },";
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
|
@ -112,6 +121,14 @@ function handleRequest(request, response) {
|
|||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "json-text-mime": {
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "text/plain; charset=utf-8", false);
|
||||
maybeMakeCached();
|
||||
response.write("{ \"greeting\": \"Hello third-party JSON!\" }");
|
||||
response.finish();
|
||||
break;
|
||||
}
|
||||
case "json-custom-mime": {
|
||||
response.setStatusLine(request.httpVersion, status, "OK");
|
||||
response.setHeader("Content-Type", "text/x-bigcorp-json; charset=utf-8", false);
|
||||
|
|
|
@ -96,18 +96,19 @@ function PieTableChart(node, pie, table) {
|
|||
* - data: an array of items used to display each slice in the pie
|
||||
* and each row in the table;
|
||||
* @see `createPieChart` and `createTableChart` for details.
|
||||
* - strings: @see `createTableChart` for details.
|
||||
* - totals: @see `createTableChart` for details.
|
||||
* - sorted: a flag specifying if the `data` should be sorted
|
||||
* ascending by `size`.
|
||||
* - totals: @see `createTableChart` for details.
|
||||
* @return PieTableChart
|
||||
* A pie+table chart proxy instance, which emits the following events:
|
||||
* - "mouseenter", when the mouse enters a slice or a row
|
||||
* - "mouseleave", when the mouse leaves a slice or a row
|
||||
* - "click", when the mouse enters a slice or a row
|
||||
*/
|
||||
function createPieTableChart(document, { sorted, title, diameter, data, totals }) {
|
||||
function createPieTableChart(document, { title, diameter, data, strings, totals, sorted }) {
|
||||
if (sorted) {
|
||||
data = data.slice().sort((a, b) => +(parseFloat(a.size) < parseFloat(b.size)));
|
||||
data = data.slice().sort((a, b) => +(a.size < b.size));
|
||||
}
|
||||
|
||||
let pie = Chart.Pie(document, {
|
||||
|
@ -118,6 +119,7 @@ function createPieTableChart(document, { sorted, title, diameter, data, totals }
|
|||
let table = Chart.Table(document, {
|
||||
title: title,
|
||||
data: data,
|
||||
strings: strings,
|
||||
totals: totals
|
||||
});
|
||||
|
||||
|
@ -202,7 +204,7 @@ function createPieChart(document, { data, width, height, centerX, centerY, radiu
|
|||
let isPlaceholder = false;
|
||||
|
||||
// Filter out very small sizes, as they'll just render invisible slices.
|
||||
data = data ? data.filter(e => parseFloat(e.size) > EPSILON) : null;
|
||||
data = data ? data.filter(e => e.size > EPSILON) : null;
|
||||
|
||||
// If there's no data available, display an empty placeholder.
|
||||
if (!data || !data.length) {
|
||||
|
@ -222,10 +224,10 @@ function createPieChart(document, { data, width, height, centerX, centerY, radiu
|
|||
|
||||
let proxy = new PieChart(container);
|
||||
|
||||
let total = data.reduce((acc, e) => acc + parseFloat(e.size), 0);
|
||||
let angles = data.map(e => parseFloat(e.size) / total * (TAU - EPSILON));
|
||||
let largest = data.reduce((a, b) => parseFloat(a.size) > parseFloat(b.size) ? a : b);
|
||||
let smallest = data.reduce((a, b) => parseFloat(a.size) < parseFloat(b.size) ? a : b);
|
||||
let total = data.reduce((acc, e) => acc + e.size, 0);
|
||||
let angles = data.map(e => e.size / total * (TAU - EPSILON));
|
||||
let largest = data.reduce((a, b) => a.size > b.size ? a : b);
|
||||
let smallest = data.reduce((a, b) => a.size < b.size ? a : b);
|
||||
|
||||
let textDistance = radius / NAMED_SLICE_TEXT_DISTANCE_RATIO;
|
||||
let translateDistance = radius / HOVERED_SLICE_TRANSLATE_DISTANCE_RATIO;
|
||||
|
@ -307,19 +309,25 @@ function createPieChart(document, { data, width, height, centerX, centerY, radiu
|
|||
* should be objects representing columns, for which the
|
||||
* properties' values will be displayed in each cell of a row.
|
||||
* e.g: [{
|
||||
* size: 1,
|
||||
* label2: "1foo",
|
||||
* label3: "2yolo"
|
||||
* label1: 1,
|
||||
* label2: 3,
|
||||
* label3: "foo"
|
||||
* }, {
|
||||
* size: 2,
|
||||
* label2: "3bar",
|
||||
* label3: "4swag"
|
||||
* label1: 4,
|
||||
* label2: 6,
|
||||
* label3: "bar
|
||||
* }];
|
||||
* - strings: an object specifying for which rows in the `data` array
|
||||
* their cell values should be stringified and localized
|
||||
* based on a predicate function;
|
||||
* e.g: {
|
||||
* label1: value => l10n.getFormatStr("...", value)
|
||||
* }
|
||||
* - totals: an object specifying for which rows in the `data` array
|
||||
* the sum of their cells is to be displayed in the chart;
|
||||
* e.g: {
|
||||
* label1: "Total size: %S",
|
||||
* label3: "Total lolz: %S"
|
||||
* label1: total => l10n.getFormatStr("...", total), // 5
|
||||
* label2: total => l10n.getFormatStr("...", total), // 9
|
||||
* }
|
||||
* @return TableChart
|
||||
* A table chart proxy instance, which emits the following events:
|
||||
|
@ -327,7 +335,9 @@ function createPieChart(document, { data, width, height, centerX, centerY, radiu
|
|||
* - "mouseleave", when the mouse leaves a row
|
||||
* - "click", when the mouse clicks a row
|
||||
*/
|
||||
function createTableChart(document, { data, totals, title }) {
|
||||
function createTableChart(document, { title, data, strings, totals }) {
|
||||
strings = strings || {};
|
||||
totals = totals || {};
|
||||
let isPlaceholder = false;
|
||||
|
||||
// If there's no data available, display an empty placeholder.
|
||||
|
@ -365,10 +375,12 @@ function createTableChart(document, { data, totals, title }) {
|
|||
rowNode.appendChild(boxNode);
|
||||
|
||||
for (let [key, value] in Iterator(rowInfo)) {
|
||||
let index = data.indexOf(rowInfo);
|
||||
let stringified = strings[key] ? strings[key](value, index) : value;
|
||||
let labelNode = document.createElement("label");
|
||||
labelNode.className = "plain table-chart-row-label";
|
||||
labelNode.setAttribute("name", key);
|
||||
labelNode.setAttribute("value", value);
|
||||
labelNode.setAttribute("value", stringified);
|
||||
rowNode.appendChild(labelNode);
|
||||
}
|
||||
|
||||
|
@ -380,13 +392,13 @@ function createTableChart(document, { data, totals, title }) {
|
|||
let totalsNode = document.createElement("vbox");
|
||||
totalsNode.className = "table-chart-totals";
|
||||
|
||||
for (let [key, value] in Iterator(totals || {})) {
|
||||
let total = data.reduce((acc, e) => acc + parseFloat(e[key]), 0);
|
||||
let formatted = !isNaN(total) ? L10N.numberWithDecimals(total, 2) : 0;
|
||||
for (let [key, value] in Iterator(totals)) {
|
||||
let total = data.reduce((acc, e) => acc + e[key], 0);
|
||||
let stringified = totals[key] ? totals[key](total || 0) : total;
|
||||
let labelNode = document.createElement("label");
|
||||
labelNode.className = "plain table-chart-summary-label";
|
||||
labelNode.setAttribute("name", key);
|
||||
labelNode.setAttribute("value", value.replace("%S", formatted));
|
||||
labelNode.setAttribute("value", stringified);
|
||||
totalsNode.appendChild(labelNode);
|
||||
}
|
||||
|
||||
|
|
|
@ -423,7 +423,6 @@ function SideMenuGroup(aWidget, aName, aOptions={}) {
|
|||
let target = this._target = this.document.createElement("vbox");
|
||||
target.className = "side-menu-widget-group";
|
||||
target.setAttribute("name", aName);
|
||||
target.setAttribute("tooltiptext", aName);
|
||||
|
||||
let list = this._list = this.document.createElement("vbox");
|
||||
list.className = "side-menu-widget-group-list";
|
||||
|
|
|
@ -166,9 +166,11 @@ charts.cacheDisabled=Empty cache
|
|||
# in the performance analysis view for total requests size, in kilobytes.
|
||||
charts.totalSize=Size: %S KB
|
||||
|
||||
# LOCALIZATION NOTE (charts.totalTime2): This is the label displayed
|
||||
# in the performance analysis view for total requests time, in seconds.
|
||||
charts.totalTime2=Time: %S seconds
|
||||
# LOCALIZATION NOTE (charts.totalSeconds): Semi-colon list of plural forms.
|
||||
# See: http://developer.mozilla.org/en/docs/Localization_and_Plurals
|
||||
# This is the label displayed in the performance analysis view for the
|
||||
# total requests time, in seconds.
|
||||
charts.totalSeconds=Time: #1 second;Time: #1 seconds
|
||||
|
||||
# LOCALIZATION NOTE (charts.totalCached): This is the label displayed
|
||||
# in the performance analysis view for total cached responses.
|
||||
|
|
Двоичные данные
browser/themes/linux/devtools/inspect-button.png
До Ширина: | Высота: | Размер: 1.3 KiB |
|
@ -3,15 +3,6 @@
|
|||
* 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/. */
|
||||
|
||||
#inspector-inspect-toolbutton {
|
||||
list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
|
||||
-moz-image-region: rect(0px 16px 16px 0px);
|
||||
}
|
||||
|
||||
#inspector-inspect-toolbutton[checked=true] {
|
||||
-moz-image-region: rect(0px 32px 16px 16px);
|
||||
}
|
||||
|
||||
#inspector-searchbox {
|
||||
transition-property: max-width, -moz-padding-end, -moz-padding-start;
|
||||
transition-duration: 250ms;
|
||||
|
|
Двоичные данные
browser/themes/linux/devtools/magnifying-glass.png
До Ширина: | Высота: | Размер: 275 B |
|
@ -23,11 +23,6 @@ browser.jar:
|
|||
* skin/classic/browser/browser.css
|
||||
* skin/classic/browser/browser-lightweightTheme.css
|
||||
skin/classic/browser/click-to-play-warning-stripes.png
|
||||
skin/classic/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
|
||||
skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png)
|
||||
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
* skin/classic/browser/engineManager.css
|
||||
skin/classic/browser/fullscreen-darknoise.png
|
||||
skin/classic/browser/Geolocation-16.png
|
||||
|
@ -73,7 +68,13 @@ browser.jar:
|
|||
skin/classic/browser/webRTC-shareDevice-16.png
|
||||
skin/classic/browser/webRTC-shareDevice-64.png
|
||||
skin/classic/browser/webRTC-sharingDevice-16.png
|
||||
skin/classic/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
|
||||
skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png)
|
||||
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
* skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css)
|
||||
skin/classic/browser/customizableui/subView-arrow-back-inverted.png (../shared/customizableui/subView-arrow-back-inverted.png)
|
||||
skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
|
@ -190,7 +191,10 @@ browser.jar:
|
|||
* skin/classic/browser/devtools/profiler.css (devtools/profiler.css)
|
||||
* skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css)
|
||||
* skin/classic/browser/devtools/scratchpad.css (devtools/scratchpad.css)
|
||||
skin/classic/browser/devtools/magnifying-glass.png (devtools/magnifying-glass.png)
|
||||
skin/classic/browser/devtools/magnifying-glass.png (../shared/devtools/images/magnifying-glass.png)
|
||||
skin/classic/browser/devtools/magnifying-glass@2x.png (../shared/devtools/images/magnifying-glass@2x.png)
|
||||
skin/classic/browser/devtools/magnifying-glass-light.png (../shared/devtools/images/magnifying-glass-light.png)
|
||||
skin/classic/browser/devtools/magnifying-glass-light@2x.png (../shared/devtools/images/magnifying-glass-light@2x.png)
|
||||
skin/classic/browser/devtools/option-icon.png (../shared/devtools/images/option-icon.png)
|
||||
skin/classic/browser/devtools/itemToggle.png (../shared/devtools/images/itemToggle.png)
|
||||
skin/classic/browser/devtools/itemToggle-light.png (../shared/devtools/images/itemToggle-light.png)
|
||||
|
@ -200,7 +204,6 @@ browser.jar:
|
|||
skin/classic/browser/devtools/itemArrow-ltr.svg (../shared/devtools/images/itemArrow-ltr.svg)
|
||||
skin/classic/browser/devtools/background-noise-toolbar.png (devtools/background-noise-toolbar.png)
|
||||
skin/classic/browser/devtools/noise.png (devtools/noise.png)
|
||||
skin/classic/browser/devtools/inspect-button.png (devtools/inspect-button.png)
|
||||
skin/classic/browser/devtools/dropmarker.png (devtools/dropmarker.png)
|
||||
skin/classic/browser/devtools/layout-background-grid.png (devtools/layout-background-grid.png)
|
||||
skin/classic/browser/devtools/layoutview.css (devtools/layoutview.css)
|
||||
|
|
|
@ -1024,11 +1024,19 @@ toolbar .toolbarbutton-1:not([type="menu-button"]),
|
|||
-moz-image-region: rect(0px, 320px, 64px, 256px);
|
||||
}
|
||||
|
||||
#bookmarks-menu-button[cui-areatype="menu-panel"].panel-multiview-anchor {
|
||||
-moz-image-region: rect(64px, 320px, 128px, 256px);
|
||||
}
|
||||
|
||||
#history-panelmenu[cui-areatype="menu-panel"],
|
||||
toolbarpaletteitem[place="palette"] > #history-panelmenu {
|
||||
-moz-image-region: rect(0px, 448px, 64px, 384px);
|
||||
}
|
||||
|
||||
#history-panelmenu[cui-areatype="menu-panel"].panel-multiview-anchor {
|
||||
-moz-image-region: rect(64px, 448px, 128px, 384px);
|
||||
}
|
||||
|
||||
#downloads-button[cui-areatype="menu-panel"],
|
||||
toolbarpaletteitem[place="palette"] > #downloads-button {
|
||||
-moz-image-region: rect(0px, 512px, 64px, 448px);
|
||||
|
@ -1069,6 +1077,10 @@ toolbar .toolbarbutton-1:not([type="menu-button"]),
|
|||
-moz-image-region: rect(0, 960px, 64px, 896px);
|
||||
}
|
||||
|
||||
#characterencoding-button[cui-areatype="menu-panel"].panel-multiview-anchor {
|
||||
-moz-image-region: rect(64px, 960px, 128px, 896px);
|
||||
}
|
||||
|
||||
#new-window-button[cui-areatype="menu-panel"],
|
||||
toolbarpaletteitem[place="palette"] > #new-window-button {
|
||||
-moz-image-region: rect(0px, 1024px, 64px, 960px);
|
||||
|
@ -1109,6 +1121,10 @@ toolbar .toolbarbutton-1:not([type="menu-button"]),
|
|||
-moz-image-region: rect(0px, 1472px, 64px, 1408px);
|
||||
}
|
||||
|
||||
#developer-button[cui-areatype="menu-panel"].panel-multiview-anchor {
|
||||
-moz-image-region: rect(64px, 1472px, 128px, 1408px);
|
||||
}
|
||||
|
||||
#preferences-button[cui-areatype="menu-panel"],
|
||||
toolbarpaletteitem[place="palette"] > #preferences-button {
|
||||
-moz-image-region: rect(0px, 1536px, 64px, 1472px);
|
||||
|
|
|
@ -5,6 +5,12 @@
|
|||
%include ../../shared/customizableui/panelUIOverlay.inc.css
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
toolbarbutton.panel-multiview-anchor {
|
||||
background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted@2x.png),
|
||||
linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
|
||||
background-size: 16px;
|
||||
}
|
||||
|
||||
#PanelUI-customize {
|
||||
list-style-image: url(chrome://browser/skin/menuPanel-customize@2x.png);
|
||||
}
|
||||
|
|
Двоичные данные
browser/themes/osx/devtools/inspect-button.png
До Ширина: | Высота: | Размер: 1.3 KiB |
|
@ -6,15 +6,6 @@
|
|||
%include ../shared.inc
|
||||
%filter substitution
|
||||
|
||||
#inspector-inspect-toolbutton {
|
||||
list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
|
||||
-moz-image-region: rect(0px 16px 16px 0px);
|
||||
}
|
||||
|
||||
#inspector-inspect-toolbutton[checked=true] {
|
||||
-moz-image-region: rect(0px 32px 16px 16px);
|
||||
}
|
||||
|
||||
#inspector-toolbar {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
|
|
Двоичные данные
browser/themes/osx/devtools/magnifying-glass.png
До Ширина: | Высота: | Размер: 275 B |
|
@ -23,13 +23,6 @@ browser.jar:
|
|||
* skin/classic/browser/browser.css (browser.css)
|
||||
* skin/classic/browser/browser-lightweightTheme.css
|
||||
skin/classic/browser/click-to-play-warning-stripes.png
|
||||
skin/classic/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
|
||||
skin/classic/browser/customizableui/customize-titleBar-toggle.png (customizableui/customize-titleBar-toggle.png)
|
||||
skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png (customizableui/customize-titleBar-toggle@2x.png)
|
||||
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png)
|
||||
* skin/classic/browser/engineManager.css (engineManager.css)
|
||||
skin/classic/browser/fullscreen-darknoise.png
|
||||
skin/classic/browser/Geolocation-16.png
|
||||
|
@ -120,6 +113,15 @@ browser.jar:
|
|||
skin/classic/browser/webRTC-shareDevice-64@2x.png
|
||||
skin/classic/browser/webRTC-sharingDevice-16.png
|
||||
skin/classic/browser/webRTC-sharingDevice-16@2x.png
|
||||
skin/classic/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
|
||||
skin/classic/browser/customizableui/customize-titleBar-toggle.png (customizableui/customize-titleBar-toggle.png)
|
||||
skin/classic/browser/customizableui/customize-titleBar-toggle@2x.png (customizableui/customize-titleBar-toggle@2x.png)
|
||||
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png)
|
||||
skin/classic/browser/customizableui/subView-arrow-back-inverted.png (../shared/customizableui/subView-arrow-back-inverted.png)
|
||||
skin/classic/browser/customizableui/subView-arrow-back-inverted@2x.png (../shared/customizableui/subView-arrow-back-inverted@2x.png)
|
||||
* skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css)
|
||||
skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
|
@ -298,7 +300,10 @@ browser.jar:
|
|||
* skin/classic/browser/devtools/profiler.css (devtools/profiler.css)
|
||||
* skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css)
|
||||
* skin/classic/browser/devtools/scratchpad.css (devtools/scratchpad.css)
|
||||
skin/classic/browser/devtools/magnifying-glass.png (devtools/magnifying-glass.png)
|
||||
skin/classic/browser/devtools/magnifying-glass.png (../shared/devtools/images/magnifying-glass.png)
|
||||
skin/classic/browser/devtools/magnifying-glass@2x.png (../shared/devtools/images/magnifying-glass@2x.png)
|
||||
skin/classic/browser/devtools/magnifying-glass-light.png (../shared/devtools/images/magnifying-glass-light.png)
|
||||
skin/classic/browser/devtools/magnifying-glass-light@2x.png (../shared/devtools/images/magnifying-glass-light@2x.png)
|
||||
skin/classic/browser/devtools/option-icon.png (../shared/devtools/images/option-icon.png)
|
||||
skin/classic/browser/devtools/itemToggle.png (../shared/devtools/images/itemToggle.png)
|
||||
skin/classic/browser/devtools/itemToggle-light.png (../shared/devtools/images/itemToggle-light.png)
|
||||
|
@ -308,7 +313,6 @@ browser.jar:
|
|||
skin/classic/browser/devtools/itemArrow-ltr.svg (../shared/devtools/images/itemArrow-ltr.svg)
|
||||
skin/classic/browser/devtools/background-noise-toolbar.png (devtools/background-noise-toolbar.png)
|
||||
skin/classic/browser/devtools/noise.png (devtools/noise.png)
|
||||
skin/classic/browser/devtools/inspect-button.png (devtools/inspect-button.png)
|
||||
skin/classic/browser/devtools/dropmarker.png (devtools/dropmarker.png)
|
||||
skin/classic/browser/devtools/layout-background-grid.png (devtools/layout-background-grid.png)
|
||||
skin/classic/browser/devtools/layoutview.css (devtools/layoutview.css)
|
||||
|
|
|
@ -523,12 +523,22 @@ panelview toolbarseparator,
|
|||
height: 16px;
|
||||
}
|
||||
|
||||
#PanelUI-footer.panel-multiview-anchor,
|
||||
#PanelUI-footer.panel-multiview-anchor > #PanelUI-help,
|
||||
#PanelUI-footer > #PanelUI-footer-inner.panel-multiview-anchor,
|
||||
toolbarbutton.panel-multiview-anchor {
|
||||
background-color: Highlight;
|
||||
background-image: linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
|
||||
background-repeat: repeat-x;
|
||||
}
|
||||
|
||||
toolbarbutton.panel-multiview-anchor {
|
||||
background-image: url(chrome://browser/skin/customizableui/subView-arrow-back-inverted.png),
|
||||
linear-gradient(rgba(255,255,255,0.3), rgba(255,255,255,0));
|
||||
background-position: right 5px center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
#PanelUI-footer > #PanelUI-footer-inner.panel-multiview-anchor,
|
||||
toolbarbutton.panel-multiview-anchor,
|
||||
toolbarbutton.panel-multiview-anchor > .toolbarbutton-menubutton-button {
|
||||
color: HighlightText;
|
||||
}
|
||||
|
||||
|
|
После Ширина: | Высота: | Размер: 307 B |
После Ширина: | Высота: | Размер: 667 B |
После Ширина: | Высота: | Размер: 186 B |
После Ширина: | Высота: | Размер: 421 B |
После Ширина: | Высота: | Размер: 192 B |
После Ширина: | Высота: | Размер: 449 B |
|
@ -217,12 +217,30 @@
|
|||
padding-bottom: 3px;
|
||||
-moz-padding-start: 22px;
|
||||
-moz-padding-end: 12px;
|
||||
background-image: url(magnifying-glass.png);
|
||||
background-position: 8px center;
|
||||
background-size: 11px 11px;
|
||||
background-repeat: no-repeat;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
.theme-dark .devtools-searchinput {
|
||||
background-image: url(magnifying-glass.png);
|
||||
}
|
||||
|
||||
.theme-light .devtools-searchinput {
|
||||
background-image: url(magnifying-glass-light.png);
|
||||
}
|
||||
|
||||
@media (min-resolution: 2dppx) {
|
||||
.theme-dark .devtools-searchinput {
|
||||
background-image: url(magnifying-glass@2x.png);
|
||||
}
|
||||
|
||||
.theme-light .devtools-searchinput {
|
||||
background-image: url(magnifying-glass-light@2x.png);
|
||||
}
|
||||
}
|
||||
|
||||
.devtools-searchinput:-moz-locale-dir(rtl) {
|
||||
background-position: calc(100% - 8px) center;
|
||||
}
|
||||
|
|
|
@ -15,11 +15,19 @@ toolbarpaletteitem[place="palette"] > #bookmarks-menu-button {
|
|||
-moz-image-region: rect(0px, 160px, 32px, 128px);
|
||||
}
|
||||
|
||||
#bookmarks-menu-button[cui-areatype="menu-panel"].panel-multiview-anchor {
|
||||
-moz-image-region: rect(32px, 160px, 64px, 128px);
|
||||
}
|
||||
|
||||
#history-panelmenu[cui-areatype="menu-panel"],
|
||||
toolbarpaletteitem[place="palette"] > #history-panelmenu {
|
||||
-moz-image-region: rect(0px, 224px, 32px, 192px);
|
||||
}
|
||||
|
||||
#history-panelmenu[cui-areatype="menu-panel"].panel-multiview-anchor {
|
||||
-moz-image-region: rect(32px, 224px, 64px, 192px);
|
||||
}
|
||||
|
||||
#downloads-button[cui-areatype="menu-panel"],
|
||||
toolbarpaletteitem[place="palette"] > #downloads-button {
|
||||
-moz-image-region: rect(0px, 256px, 32px, 224px);
|
||||
|
@ -60,6 +68,10 @@ toolbarpaletteitem[place="palette"] > #characterencoding-button {
|
|||
-moz-image-region: rect(0px, 480px, 32px, 448px);
|
||||
}
|
||||
|
||||
#characterencoding-button[cui-areatype="menu-panel"].panel-multiview-anchor {
|
||||
-moz-image-region: rect(32px, 480px, 64px, 448px);
|
||||
}
|
||||
|
||||
#new-window-button[cui-areatype="menu-panel"],
|
||||
toolbarpaletteitem[place="palette"] > #new-window-button {
|
||||
-moz-image-region: rect(0px, 512px, 32px, 480px);
|
||||
|
@ -100,6 +112,10 @@ toolbarpaletteitem[place="palette"] > #developer-button {
|
|||
-moz-image-region: rect(0px, 736px, 32px, 704px);
|
||||
}
|
||||
|
||||
#developer-button[cui-areatype="menu-panel"].panel-multiview-anchor {
|
||||
-moz-image-region: rect(32px, 736px, 64px, 704px);
|
||||
}
|
||||
|
||||
#preferences-button[cui-areatype="menu-panel"],
|
||||
toolbarpaletteitem[place="palette"] > #preferences-button {
|
||||
-moz-image-region: rect(0px, 768px, 32px, 736px);
|
||||
|
|
Двоичные данные
browser/themes/windows/devtools/inspect-button.png
До Ширина: | Высота: | Размер: 1.3 KiB |
|
@ -3,15 +3,6 @@
|
|||
* 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/. */
|
||||
|
||||
#inspector-inspect-toolbutton {
|
||||
list-style-image: url("chrome://browser/skin/devtools/inspect-button.png");
|
||||
-moz-image-region: rect(0px 16px 16px 0px);
|
||||
}
|
||||
|
||||
#inspector-inspect-toolbutton[checked=true] {
|
||||
-moz-image-region: rect(0px 32px 16px 16px);
|
||||
}
|
||||
|
||||
#inspector-searchbox {
|
||||
transition-property: max-width, -moz-padding-end, -moz-padding-start;
|
||||
transition-duration: 250ms;
|
||||
|
|
Двоичные данные
browser/themes/windows/devtools/magnifying-glass.png
До Ширина: | Высота: | Размер: 1.1 KiB |
|
@ -25,12 +25,6 @@ browser.jar:
|
|||
* skin/classic/browser/browser.css
|
||||
* skin/classic/browser/browser-lightweightTheme.css
|
||||
skin/classic/browser/click-to-play-warning-stripes.png
|
||||
skin/classic/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
|
||||
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
skin/classic/browser/customizableui/customize-titleBar-toggle.png (customizableui/customize-titleBar-toggle.png)
|
||||
skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png)
|
||||
* skin/classic/browser/engineManager.css
|
||||
skin/classic/browser/fullscreen-darknoise.png
|
||||
skin/classic/browser/Geolocation-16.png
|
||||
|
@ -92,7 +86,14 @@ browser.jar:
|
|||
skin/classic/browser/webRTC-shareDevice-16.png
|
||||
skin/classic/browser/webRTC-shareDevice-64.png
|
||||
skin/classic/browser/webRTC-sharingDevice-16.png
|
||||
skin/classic/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
|
||||
skin/classic/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
skin/classic/browser/customizableui/customize-titleBar-toggle.png (customizableui/customize-titleBar-toggle.png)
|
||||
skin/classic/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
|
||||
skin/classic/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png)
|
||||
* skin/classic/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css)
|
||||
skin/classic/browser/customizableui/subView-arrow-back-inverted.png (../shared/customizableui/subView-arrow-back-inverted.png)
|
||||
* skin/classic/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay.css)
|
||||
skin/classic/browser/downloads/buttons.png (downloads/buttons.png)
|
||||
skin/classic/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
|
@ -219,17 +220,19 @@ browser.jar:
|
|||
* skin/classic/browser/devtools/profiler.css (devtools/profiler.css)
|
||||
* skin/classic/browser/devtools/netmonitor.css (devtools/netmonitor.css)
|
||||
* skin/classic/browser/devtools/scratchpad.css (devtools/scratchpad.css)
|
||||
skin/classic/browser/devtools/magnifying-glass.png (devtools/magnifying-glass.png)
|
||||
skin/classic/browser/devtools/magnifying-glass.png (../shared/devtools/images/magnifying-glass.png)
|
||||
skin/classic/browser/devtools/magnifying-glass@2x.png (../shared/devtools/images/magnifying-glass@2x.png)
|
||||
skin/classic/browser/devtools/magnifying-glass-light.png (../shared/devtools/images/magnifying-glass-light.png)
|
||||
skin/classic/browser/devtools/magnifying-glass-light@2x.png (../shared/devtools/images/magnifying-glass-light@2x.png)
|
||||
skin/classic/browser/devtools/option-icon.png (../shared/devtools/images/option-icon.png)
|
||||
skin/classic/browser/devtools/itemToggle.png (../shared/devtools/images/itemToggle.png)
|
||||
skin/classic/browser/devtools/itemToggle-light.png (../shared/devtools/images/itemToggle-light.png)
|
||||
skin/classic/browser/devtools/itemArrow-dark-rtl.png (../shared/devtools/images/itemArrow-dark-rtl.png)
|
||||
skin/classic/browser/devtools/itemArrow-dark-ltr.png (../shared/devtools/images/itemArrow-dark-ltr.png)
|
||||
skin/classic/browser/devtools/itemArrow-rtl.png (../shared/devtools/images/itemArrow-rtl.svg)
|
||||
skin/classic/browser/devtools/itemArrow-ltr.png (../shared/devtools/images/itemArrow-ltr.svg)
|
||||
skin/classic/browser/devtools/itemArrow-rtl.svg (../shared/devtools/images/itemArrow-rtl.svg)
|
||||
skin/classic/browser/devtools/itemArrow-ltr.svg (../shared/devtools/images/itemArrow-ltr.svg)
|
||||
skin/classic/browser/devtools/background-noise-toolbar.png (devtools/background-noise-toolbar.png)
|
||||
skin/classic/browser/devtools/noise.png (devtools/noise.png)
|
||||
skin/classic/browser/devtools/inspect-button.png (devtools/inspect-button.png)
|
||||
skin/classic/browser/devtools/dropmarker.png (devtools/dropmarker.png)
|
||||
skin/classic/browser/devtools/layout-background-grid.png (devtools/layout-background-grid.png)
|
||||
skin/classic/browser/devtools/layoutview.css (devtools/layoutview.css)
|
||||
|
@ -335,12 +338,6 @@ browser.jar:
|
|||
* skin/classic/aero/browser/browser.css (browser-aero.css)
|
||||
* skin/classic/aero/browser/browser-lightweightTheme.css
|
||||
skin/classic/aero/browser/click-to-play-warning-stripes.png
|
||||
skin/classic/aero/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
|
||||
skin/classic/aero/browser/customizableui/customize-titleBar-toggle.png (customizableui/customize-titleBar-toggle.png)
|
||||
skin/classic/aero/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
skin/classic/aero/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
|
||||
skin/classic/aero/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
|
||||
skin/classic/aero/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png)
|
||||
* skin/classic/aero/browser/engineManager.css
|
||||
skin/classic/aero/browser/fullscreen-darknoise.png
|
||||
skin/classic/aero/browser/Geolocation-16.png
|
||||
|
@ -401,7 +398,14 @@ browser.jar:
|
|||
skin/classic/aero/browser/webRTC-shareDevice-16.png
|
||||
skin/classic/aero/browser/webRTC-shareDevice-64.png
|
||||
skin/classic/aero/browser/webRTC-sharingDevice-16.png
|
||||
skin/classic/aero/browser/customizableui/background-noise-toolbar.png (customizableui/background-noise-toolbar.png)
|
||||
skin/classic/aero/browser/customizableui/customize-titleBar-toggle.png (customizableui/customize-titleBar-toggle.png)
|
||||
skin/classic/aero/browser/customizableui/customizeFavicon.ico (../shared/customizableui/customizeFavicon.ico)
|
||||
skin/classic/aero/browser/customizableui/customizeMode-gridTexture.png (customizableui/customizeMode-gridTexture.png)
|
||||
skin/classic/aero/browser/customizableui/customizeMode-separatorHorizontal.png (customizableui/customizeMode-separatorHorizontal.png)
|
||||
skin/classic/aero/browser/customizableui/customizeMode-separatorVertical.png (customizableui/customizeMode-separatorVertical.png)
|
||||
* skin/classic/aero/browser/customizableui/panelUIOverlay.css (customizableui/panelUIOverlay.css)
|
||||
skin/classic/aero/browser/customizableui/subView-arrow-back-inverted.png (../shared/customizableui/subView-arrow-back-inverted.png)
|
||||
* skin/classic/aero/browser/downloads/allDownloadsViewOverlay.css (downloads/allDownloadsViewOverlay-aero.css)
|
||||
skin/classic/aero/browser/downloads/buttons.png (downloads/buttons-aero.png)
|
||||
skin/classic/aero/browser/downloads/contentAreaDownloadsView.css (downloads/contentAreaDownloadsView.css)
|
||||
|
@ -429,6 +433,7 @@ browser.jar:
|
|||
skin/classic/aero/browser/places/bookmarksMenu.png (places/bookmarksMenu-aero.png)
|
||||
skin/classic/aero/browser/places/bookmarksToolbar.png (places/bookmarksToolbar-aero.png)
|
||||
skin/classic/aero/browser/places/bookmarksToolbar-menuPanel.png (places/bookmarksToolbar-menuPanel-aero.png)
|
||||
skin/classic/aero/browser/places/bookmarks-notification-finish.png (places/bookmarks-notification-finish.png)
|
||||
skin/classic/aero/browser/places/calendar.png (places/calendar-aero.png)
|
||||
skin/classic/aero/browser/places/toolbarDropMarker.png (places/toolbarDropMarker-aero.png)
|
||||
skin/classic/aero/browser/places/editBookmarkOverlay.css (places/editBookmarkOverlay.css)
|
||||
|
@ -527,7 +532,10 @@ browser.jar:
|
|||
* skin/classic/aero/browser/devtools/profiler.css (devtools/profiler.css)
|
||||
* skin/classic/aero/browser/devtools/netmonitor.css (devtools/netmonitor.css)
|
||||
* skin/classic/aero/browser/devtools/scratchpad.css (devtools/scratchpad.css)
|
||||
skin/classic/aero/browser/devtools/magnifying-glass.png (devtools/magnifying-glass.png)
|
||||
skin/classic/aero/browser/devtools/magnifying-glass.png (../shared/devtools/images/magnifying-glass.png)
|
||||
skin/classic/aero/browser/devtools/magnifying-glass@2x.png (../shared/devtools/images/magnifying-glass@2x.png)
|
||||
skin/classic/aero/browser/devtools/magnifying-glass-light.png (../shared/devtools/images/magnifying-glass-light.png)
|
||||
skin/classic/aero/browser/devtools/magnifying-glass-light@2x.png (../shared/devtools/images/magnifying-glass-light@2x.png)
|
||||
skin/classic/aero/browser/devtools/option-icon.png (../shared/devtools/images/option-icon.png)
|
||||
skin/classic/aero/browser/devtools/itemToggle.png (../shared/devtools/images/itemToggle.png)
|
||||
skin/classic/aero/browser/devtools/itemToggle-light.png (../shared/devtools/images/itemToggle-light.png)
|
||||
|
@ -537,7 +545,6 @@ browser.jar:
|
|||
skin/classic/aero/browser/devtools/itemArrow-ltr.png (../shared/devtools/images/itemArrow-ltr.svg)
|
||||
skin/classic/aero/browser/devtools/background-noise-toolbar.png (devtools/background-noise-toolbar.png)
|
||||
skin/classic/aero/browser/devtools/noise.png (devtools/noise.png)
|
||||
skin/classic/aero/browser/devtools/inspect-button.png (devtools/inspect-button.png)
|
||||
skin/classic/aero/browser/devtools/dropmarker.png (devtools/dropmarker.png)
|
||||
skin/classic/aero/browser/devtools/layout-background-grid.png (devtools/layout-background-grid.png)
|
||||
skin/classic/aero/browser/devtools/layoutview.css (devtools/layoutview.css)
|
||||
|
|
|
@ -3675,7 +3675,7 @@ MOZ_ARG_WITH_BOOL(system-nss,
|
|||
_USE_SYSTEM_NSS=1 )
|
||||
|
||||
if test -n "$_USE_SYSTEM_NSS"; then
|
||||
AM_PATH_NSS(3.15.4, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
|
||||
AM_PATH_NSS(3.15.5, [MOZ_NATIVE_NSS=1], [AC_MSG_ERROR([you don't have NSS installed or your version is too old])])
|
||||
fi
|
||||
|
||||
if test -n "$MOZ_NATIVE_NSS"; then
|
||||
|
|
|
@ -6915,6 +6915,14 @@ nsDocument::GetViewportInfo(const ScreenIntSize& aDisplaySize)
|
|||
// app that does not use it.
|
||||
nsCOMPtr<nsIDocShell> docShell(mDocumentContainer);
|
||||
if (docShell && docShell->GetIsApp()) {
|
||||
nsString uri;
|
||||
GetDocumentURI(uri);
|
||||
if (!uri.EqualsLiteral("about:blank")) {
|
||||
nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
|
||||
NS_LITERAL_CSTRING("DOM"), this,
|
||||
nsContentUtils::eDOM_PROPERTIES,
|
||||
"ImplicitMetaViewportTagFallback");
|
||||
}
|
||||
mViewportType = DisplayWidthHeightNoZoom;
|
||||
return nsViewportInfo(aDisplaySize, /* allowZoom */ false);
|
||||
}
|
||||
|
|
|
@ -3,5 +3,6 @@ conformance/misc/type-conversion-test.html
|
|||
conformance/more/conformance/quickCheckAPI-B2.html
|
||||
conformance/more/conformance/quickCheckAPI-B3.html
|
||||
conformance/more/conformance/quickCheckAPI-B4.html
|
||||
conformance/more/conformance/quickCheckAPI-C.html
|
||||
conformance/reading/read-pixels-test.html
|
||||
conformance/textures/texture-mips.html
|
||||
|
|
|
@ -23,7 +23,7 @@ function checkCache(url, inMemory, shouldExist, cb)
|
|||
this.onCacheEntryAvailable = function oCEA(entry, isNew, appCache, status) {
|
||||
if (shouldExist) {
|
||||
ok(entry, "Entry not found");
|
||||
is(this.inMemory, !entry.persistToDisk, "Entry is " + (inMemory ? "" : " not ") + " in memory as expected");
|
||||
is(this.inMemory, !entry.persistent, "Entry is " + (inMemory ? "" : " not ") + " in memory as expected");
|
||||
is(status, Components.results.NS_OK, "Entry not found");
|
||||
} else {
|
||||
ok(!entry, "Entry found");
|
||||
|
|
|
@ -430,3 +430,4 @@ support-files =
|
|||
[test_video_wakelock.html]
|
||||
[test_input_files_not_nsIFile.html]
|
||||
[test_ignoreuserfocus.html]
|
||||
[test_fragment_form_pointer.html]
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=946585
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test for Bug 946585</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=946585">Mozilla Bug 946585</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
<form><div id="formdiv"></div></form>
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
<script type="application/javascript">
|
||||
/** Test for Bug 946585 **/
|
||||
var formDiv = document.getElementById("formdiv");
|
||||
formDiv.innerHTML = '<form>';
|
||||
is(formDiv.firstChild, null, "InnerHTML should not produce form element because the div has a form pointer.");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,335 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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/. */
|
||||
#include "MediaData.h"
|
||||
#include "MediaInfo.h"
|
||||
#ifdef MOZ_OMX_DECODER
|
||||
#include "GrallocImages.h"
|
||||
#endif
|
||||
#include "VideoUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
using layers::ImageContainer;
|
||||
using layers::PlanarYCbCrImage;
|
||||
using layers::PlanarYCbCrData;
|
||||
|
||||
void
|
||||
AudioData::EnsureAudioBuffer()
|
||||
{
|
||||
if (mAudioBuffer)
|
||||
return;
|
||||
mAudioBuffer = SharedBuffer::Create(mFrames*mChannels*sizeof(AudioDataValue));
|
||||
|
||||
AudioDataValue* data = static_cast<AudioDataValue*>(mAudioBuffer->Data());
|
||||
for (uint32_t i = 0; i < mFrames; ++i) {
|
||||
for (uint32_t j = 0; j < mChannels; ++j) {
|
||||
data[j*mFrames + i] = mAudioData[i*mChannels + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane)
|
||||
{
|
||||
return aPlane.mWidth <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPlane.mHeight <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPlane.mWidth * aPlane.mHeight < MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
|
||||
aPlane.mStride > 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static bool
|
||||
IsYV12Format(const VideoData::YCbCrBuffer::Plane& aYPlane,
|
||||
const VideoData::YCbCrBuffer::Plane& aCbPlane,
|
||||
const VideoData::YCbCrBuffer::Plane& aCrPlane)
|
||||
{
|
||||
return
|
||||
aYPlane.mWidth % 2 == 0 &&
|
||||
aYPlane.mHeight % 2 == 0 &&
|
||||
aYPlane.mWidth / 2 == aCbPlane.mWidth &&
|
||||
aYPlane.mHeight / 2 == aCbPlane.mHeight &&
|
||||
aCbPlane.mWidth == aCrPlane.mWidth &&
|
||||
aCbPlane.mHeight == aCrPlane.mHeight;
|
||||
}
|
||||
#endif
|
||||
|
||||
VideoData::VideoData(int64_t aOffset, int64_t aTime, int64_t aDuration, int64_t aTimecode)
|
||||
: MediaData(VIDEO_FRAME, aOffset, aTime, aDuration),
|
||||
mTimecode(aTimecode),
|
||||
mDuplicate(true),
|
||||
mKeyframe(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(VideoData);
|
||||
NS_ASSERTION(mDuration >= 0, "Frame must have non-negative duration.");
|
||||
}
|
||||
|
||||
VideoData::VideoData(int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntSize aDisplay)
|
||||
: MediaData(VIDEO_FRAME, aOffset, aTime, aDuration),
|
||||
mDisplay(aDisplay),
|
||||
mTimecode(aTimecode),
|
||||
mDuplicate(false),
|
||||
mKeyframe(aKeyframe)
|
||||
{
|
||||
MOZ_COUNT_CTOR(VideoData);
|
||||
NS_ASSERTION(mDuration >= 0, "Frame must have non-negative duration.");
|
||||
}
|
||||
|
||||
VideoData::~VideoData()
|
||||
{
|
||||
MOZ_COUNT_DTOR(VideoData);
|
||||
}
|
||||
|
||||
/* static */
|
||||
VideoData* VideoData::ShallowCopyUpdateDuration(VideoData* aOther,
|
||||
int64_t aDuration)
|
||||
{
|
||||
VideoData* v = new VideoData(aOther->mOffset,
|
||||
aOther->mTime,
|
||||
aDuration,
|
||||
aOther->mKeyframe,
|
||||
aOther->mTimecode,
|
||||
aOther->mDisplay);
|
||||
v->mImage = aOther->mImage;
|
||||
return v;
|
||||
}
|
||||
|
||||
VideoData* VideoData::Create(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
Image* aImage,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const YCbCrBuffer& aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture)
|
||||
{
|
||||
if (!aImage && !aContainer) {
|
||||
// Create a dummy VideoData with no image. This gives us something to
|
||||
// send to media streams if necessary.
|
||||
nsAutoPtr<VideoData> v(new VideoData(aOffset,
|
||||
aTime,
|
||||
aDuration,
|
||||
aKeyframe,
|
||||
aTimecode,
|
||||
aInfo.mDisplay));
|
||||
return v.forget();
|
||||
}
|
||||
|
||||
// The following situation should never happen unless there is a bug
|
||||
// in the decoder
|
||||
if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth ||
|
||||
aBuffer.mPlanes[1].mHeight != aBuffer.mPlanes[2].mHeight) {
|
||||
NS_ERROR("C planes with different sizes");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The following situations could be triggered by invalid input
|
||||
if (aPicture.width <= 0 || aPicture.height <= 0) {
|
||||
NS_WARNING("Empty picture rect");
|
||||
return nullptr;
|
||||
}
|
||||
if (!ValidatePlane(aBuffer.mPlanes[0]) || !ValidatePlane(aBuffer.mPlanes[1]) ||
|
||||
!ValidatePlane(aBuffer.mPlanes[2])) {
|
||||
NS_WARNING("Invalid plane size");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure the picture size specified in the headers can be extracted out of
|
||||
// the frame we've been supplied without indexing out of bounds.
|
||||
CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
|
||||
CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
|
||||
if (!xLimit.isValid() || xLimit.value() > aBuffer.mPlanes[0].mStride ||
|
||||
!yLimit.isValid() || yLimit.value() > aBuffer.mPlanes[0].mHeight)
|
||||
{
|
||||
// The specified picture dimensions can't be contained inside the video
|
||||
// frame, we'll stomp memory if we try to copy it. Fail.
|
||||
NS_WARNING("Overflowing picture rect");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoPtr<VideoData> v(new VideoData(aOffset,
|
||||
aTime,
|
||||
aDuration,
|
||||
aKeyframe,
|
||||
aTimecode,
|
||||
aInfo.mDisplay));
|
||||
const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
|
||||
const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
|
||||
const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
|
||||
|
||||
if (!aImage) {
|
||||
// Currently our decoder only knows how to output to ImageFormat::PLANAR_YCBCR
|
||||
// format.
|
||||
#if 0
|
||||
if (IsYV12Format(Y, Cb, Cr)) {
|
||||
v->mImage = aContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
|
||||
}
|
||||
#endif
|
||||
if (!v->mImage) {
|
||||
v->mImage = aContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
|
||||
}
|
||||
} else {
|
||||
v->mImage = aImage;
|
||||
}
|
||||
|
||||
if (!v->mImage) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::PLANAR_YCBCR ||
|
||||
v->mImage->GetFormat() == ImageFormat::GRALLOC_PLANAR_YCBCR,
|
||||
"Wrong format?");
|
||||
PlanarYCbCrImage* videoImage = static_cast<PlanarYCbCrImage*>(v->mImage.get());
|
||||
|
||||
PlanarYCbCrData data;
|
||||
data.mYChannel = Y.mData + Y.mOffset;
|
||||
data.mYSize = IntSize(Y.mWidth, Y.mHeight);
|
||||
data.mYStride = Y.mStride;
|
||||
data.mYSkip = Y.mSkip;
|
||||
data.mCbChannel = Cb.mData + Cb.mOffset;
|
||||
data.mCrChannel = Cr.mData + Cr.mOffset;
|
||||
data.mCbCrSize = IntSize(Cb.mWidth, Cb.mHeight);
|
||||
data.mCbCrStride = Cb.mStride;
|
||||
data.mCbSkip = Cb.mSkip;
|
||||
data.mCrSkip = Cr.mSkip;
|
||||
data.mPicX = aPicture.x;
|
||||
data.mPicY = aPicture.y;
|
||||
data.mPicSize = aPicture.Size().ToIntSize();
|
||||
data.mStereoMode = aInfo.mStereoMode;
|
||||
|
||||
videoImage->SetDelayedConversion(true);
|
||||
if (!aImage) {
|
||||
videoImage->SetData(data);
|
||||
} else {
|
||||
videoImage->SetDataNoCopy(data);
|
||||
}
|
||||
|
||||
return v.forget();
|
||||
}
|
||||
|
||||
VideoData* VideoData::Create(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const YCbCrBuffer& aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture)
|
||||
{
|
||||
return Create(aInfo, aContainer, nullptr, aOffset, aTime, aDuration, aBuffer,
|
||||
aKeyframe, aTimecode, aPicture);
|
||||
}
|
||||
|
||||
VideoData* VideoData::Create(VideoInfo& aInfo,
|
||||
Image* aImage,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const YCbCrBuffer& aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture)
|
||||
{
|
||||
return Create(aInfo, nullptr, aImage, aOffset, aTime, aDuration, aBuffer,
|
||||
aKeyframe, aTimecode, aPicture);
|
||||
}
|
||||
|
||||
VideoData* VideoData::CreateFromImage(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const nsRefPtr<Image>& aImage,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture)
|
||||
{
|
||||
nsAutoPtr<VideoData> v(new VideoData(aOffset,
|
||||
aTime,
|
||||
aDuration,
|
||||
aKeyframe,
|
||||
aTimecode,
|
||||
aInfo.mDisplay));
|
||||
v->mImage = aImage;
|
||||
return v.forget();
|
||||
}
|
||||
|
||||
#ifdef MOZ_OMX_DECODER
|
||||
VideoData* VideoData::Create(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
mozilla::layers::GraphicBufferLocked* aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture)
|
||||
{
|
||||
if (!aContainer) {
|
||||
// Create a dummy VideoData with no image. This gives us something to
|
||||
// send to media streams if necessary.
|
||||
nsAutoPtr<VideoData> v(new VideoData(aOffset,
|
||||
aTime,
|
||||
aDuration,
|
||||
aKeyframe,
|
||||
aTimecode,
|
||||
aInfo.mDisplay));
|
||||
return v.forget();
|
||||
}
|
||||
|
||||
// The following situations could be triggered by invalid input
|
||||
if (aPicture.width <= 0 || aPicture.height <= 0) {
|
||||
NS_WARNING("Empty picture rect");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure the picture size specified in the headers can be extracted out of
|
||||
// the frame we've been supplied without indexing out of bounds.
|
||||
CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
|
||||
CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
|
||||
if (!xLimit.isValid() || !yLimit.isValid())
|
||||
{
|
||||
// The specified picture dimensions can't be contained inside the video
|
||||
// frame, we'll stomp memory if we try to copy it. Fail.
|
||||
NS_WARNING("Overflowing picture rect");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoPtr<VideoData> v(new VideoData(aOffset,
|
||||
aTime,
|
||||
aDuration,
|
||||
aKeyframe,
|
||||
aTimecode,
|
||||
aInfo.mDisplay));
|
||||
|
||||
v->mImage = aContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
|
||||
if (!v->mImage) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::GRALLOC_PLANAR_YCBCR,
|
||||
"Wrong format?");
|
||||
typedef mozilla::layers::GrallocImage GrallocImage;
|
||||
GrallocImage* videoImage = static_cast<GrallocImage*>(v->mImage.get());
|
||||
GrallocImage::GrallocData data;
|
||||
|
||||
data.mPicSize = aPicture.Size().ToIntSize();
|
||||
data.mGraphicBuffer = aBuffer;
|
||||
|
||||
videoImage->SetData(data);
|
||||
|
||||
return v.forget();
|
||||
}
|
||||
#endif // MOZ_OMX_DECODER
|
||||
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,249 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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/. */
|
||||
#if !defined(MediaData_h)
|
||||
#define MediaData_h
|
||||
|
||||
#include "nsSize.h"
|
||||
#include "nsRect.h"
|
||||
#include "AudioSampleFormat.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
#include "SharedBuffer.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace layers {
|
||||
class Image;
|
||||
class ImageContainer;
|
||||
}
|
||||
|
||||
// Container that holds media samples.
|
||||
class MediaData {
|
||||
public:
|
||||
|
||||
enum Type {
|
||||
AUDIO_SAMPLES = 0,
|
||||
VIDEO_FRAME = 1
|
||||
};
|
||||
|
||||
MediaData(Type aType,
|
||||
int64_t aOffset,
|
||||
int64_t aTimestamp,
|
||||
int64_t aDuration)
|
||||
: mType(aType)
|
||||
, mOffset(aOffset)
|
||||
, mTime(aTimestamp)
|
||||
, mDuration(aDuration)
|
||||
{}
|
||||
|
||||
virtual ~MediaData() {}
|
||||
|
||||
// Type of contained data.
|
||||
const Type mType;
|
||||
|
||||
// Approximate byte offset where this data was demuxed from its media.
|
||||
const int64_t mOffset;
|
||||
|
||||
// Start time of sample, in microseconds.
|
||||
const int64_t mTime;
|
||||
|
||||
// Duration of sample, in microseconds.
|
||||
const int64_t mDuration;
|
||||
|
||||
int64_t GetEndTime() const { return mTime + mDuration; }
|
||||
|
||||
};
|
||||
|
||||
// Holds chunk a decoded audio frames.
|
||||
class AudioData : public MediaData {
|
||||
public:
|
||||
|
||||
AudioData(int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
uint32_t aFrames,
|
||||
AudioDataValue* aData,
|
||||
uint32_t aChannels)
|
||||
: MediaData(AUDIO_SAMPLES, aOffset, aTime, aDuration)
|
||||
, mFrames(aFrames)
|
||||
, mChannels(aChannels)
|
||||
, mAudioData(aData)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AudioData);
|
||||
}
|
||||
|
||||
~AudioData()
|
||||
{
|
||||
MOZ_COUNT_DTOR(AudioData);
|
||||
}
|
||||
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
|
||||
size_t size = aMallocSizeOf(this) + aMallocSizeOf(mAudioData);
|
||||
if (mAudioBuffer) {
|
||||
size += mAudioBuffer->SizeOfIncludingThis(aMallocSizeOf);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
// If mAudioBuffer is null, creates it from mAudioData.
|
||||
void EnsureAudioBuffer();
|
||||
|
||||
const uint32_t mFrames;
|
||||
const uint32_t mChannels;
|
||||
// At least one of mAudioBuffer/mAudioData must be non-null.
|
||||
// mChannels channels, each with mFrames frames
|
||||
nsRefPtr<SharedBuffer> mAudioBuffer;
|
||||
// mFrames frames, each with mChannels values
|
||||
nsAutoArrayPtr<AudioDataValue> mAudioData;
|
||||
};
|
||||
|
||||
namespace layers {
|
||||
class GraphicBufferLocked;
|
||||
}
|
||||
|
||||
class VideoInfo;
|
||||
|
||||
// Holds a decoded video frame, in YCbCr format. These are queued in the reader.
|
||||
class VideoData : public MediaData {
|
||||
public:
|
||||
typedef layers::ImageContainer ImageContainer;
|
||||
typedef layers::Image Image;
|
||||
|
||||
// YCbCr data obtained from decoding the video. The index's are:
|
||||
// 0 = Y
|
||||
// 1 = Cb
|
||||
// 2 = Cr
|
||||
struct YCbCrBuffer {
|
||||
struct Plane {
|
||||
uint8_t* mData;
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
uint32_t mStride;
|
||||
uint32_t mOffset;
|
||||
uint32_t mSkip;
|
||||
};
|
||||
|
||||
Plane mPlanes[3];
|
||||
};
|
||||
|
||||
// Constructs a VideoData object. If aImage is nullptr, creates a new Image
|
||||
// holding a copy of the YCbCr data passed in aBuffer. If aImage is not
|
||||
// nullptr, it's stored as the underlying video image and aBuffer is assumed
|
||||
// to point to memory within aImage so no copy is made. aTimecode is a codec
|
||||
// specific number representing the timestamp of the frame of video data.
|
||||
// Returns nsnull if an error occurs. This may indicate that memory couldn't
|
||||
// be allocated to create the VideoData object, or it may indicate some
|
||||
// problem with the input data (e.g. negative stride).
|
||||
static VideoData* Create(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
Image* aImage,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const YCbCrBuffer &aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture);
|
||||
|
||||
// Variant that always makes a copy of aBuffer
|
||||
static VideoData* Create(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const YCbCrBuffer &aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture);
|
||||
|
||||
// Variant to create a VideoData instance given an existing aImage
|
||||
static VideoData* Create(VideoInfo& aInfo,
|
||||
Image* aImage,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const YCbCrBuffer &aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture);
|
||||
|
||||
static VideoData* Create(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
layers::GraphicBufferLocked* aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture);
|
||||
|
||||
static VideoData* CreateFromImage(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const nsRefPtr<Image>& aImage,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture);
|
||||
|
||||
// Creates a new VideoData identical to aOther, but with a different
|
||||
// specified duration. All data from aOther is copied into the new
|
||||
// VideoData. The new VideoData's mImage field holds a reference to
|
||||
// aOther's mImage, i.e. the Image is not copied. This function is useful
|
||||
// in reader backends that can't determine the duration of a VideoData
|
||||
// until the next frame is decoded, i.e. it's a way to change the const
|
||||
// duration field on a VideoData.
|
||||
static VideoData* ShallowCopyUpdateDuration(VideoData* aOther,
|
||||
int64_t aDuration);
|
||||
|
||||
// Constructs a duplicate VideoData object. This intrinsically tells the
|
||||
// player that it does not need to update the displayed frame when this
|
||||
// frame is played; this frame is identical to the previous.
|
||||
static VideoData* CreateDuplicate(int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
int64_t aTimecode)
|
||||
{
|
||||
return new VideoData(aOffset, aTime, aDuration, aTimecode);
|
||||
}
|
||||
|
||||
~VideoData();
|
||||
|
||||
// Dimensions at which to display the video frame. The picture region
|
||||
// will be scaled to this size. This is should be the picture region's
|
||||
// dimensions scaled with respect to its aspect ratio.
|
||||
const nsIntSize mDisplay;
|
||||
|
||||
// Codec specific internal time code. For Ogg based codecs this is the
|
||||
// granulepos.
|
||||
const int64_t mTimecode;
|
||||
|
||||
// This frame's image.
|
||||
nsRefPtr<Image> mImage;
|
||||
|
||||
// When true, denotes that this frame is identical to the frame that
|
||||
// came before; it's a duplicate. mBuffer will be empty.
|
||||
const bool mDuplicate;
|
||||
const bool mKeyframe;
|
||||
|
||||
public:
|
||||
VideoData(int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
int64_t aTimecode);
|
||||
|
||||
VideoData(int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntSize aDisplay);
|
||||
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MediaData_h
|
|
@ -1,13 +1,10 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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/. */
|
||||
|
||||
#include "MediaDecoderReader.h"
|
||||
#ifdef MOZ_OMX_DECODER
|
||||
#include "GrallocImages.h"
|
||||
#endif
|
||||
#include "AbstractMediaDecoder.h"
|
||||
#include "VideoUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
|
@ -18,20 +15,6 @@
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace mozilla::gfx;
|
||||
using layers::ImageContainer;
|
||||
using layers::PlanarYCbCrImage;
|
||||
using layers::PlanarYCbCrData;
|
||||
|
||||
// Verify these values are sane. Once we've checked the frame sizes, we then
|
||||
// can do less integer overflow checking.
|
||||
static_assert(MAX_VIDEO_WIDTH < PlanarYCbCrImage::MAX_DIMENSION,
|
||||
"MAX_VIDEO_WIDTH is too large");
|
||||
static_assert(MAX_VIDEO_HEIGHT < PlanarYCbCrImage::MAX_DIMENSION,
|
||||
"MAX_VIDEO_HEIGHT is too large");
|
||||
static_assert(PlanarYCbCrImage::MAX_DIMENSION < UINT32_MAX / PlanarYCbCrImage::MAX_DIMENSION,
|
||||
"MAX_DIMENSION*MAX_DIMENSION doesn't fit in 32 bits");
|
||||
|
||||
// Un-comment to enable logging of seek bisections.
|
||||
//#define SEEK_LOGGING
|
||||
|
||||
|
@ -48,344 +31,6 @@ extern PRLogModuleInfo* gMediaDecoderLog;
|
|||
#define SEEK_LOG(type, msg)
|
||||
#endif
|
||||
|
||||
void
|
||||
AudioData::EnsureAudioBuffer()
|
||||
{
|
||||
if (mAudioBuffer)
|
||||
return;
|
||||
mAudioBuffer = SharedBuffer::Create(mFrames*mChannels*sizeof(AudioDataValue));
|
||||
|
||||
AudioDataValue* data = static_cast<AudioDataValue*>(mAudioBuffer->Data());
|
||||
for (uint32_t i = 0; i < mFrames; ++i) {
|
||||
for (uint32_t j = 0; j < mChannels; ++j) {
|
||||
data[j*mFrames + i] = mAudioData[i*mChannels + j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
ValidatePlane(const VideoData::YCbCrBuffer::Plane& aPlane)
|
||||
{
|
||||
return aPlane.mWidth <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPlane.mHeight <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPlane.mWidth * aPlane.mHeight < MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
|
||||
aPlane.mStride > 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static bool
|
||||
IsYV12Format(const VideoData::YCbCrBuffer::Plane& aYPlane,
|
||||
const VideoData::YCbCrBuffer::Plane& aCbPlane,
|
||||
const VideoData::YCbCrBuffer::Plane& aCrPlane)
|
||||
{
|
||||
return
|
||||
aYPlane.mWidth % 2 == 0 &&
|
||||
aYPlane.mHeight % 2 == 0 &&
|
||||
aYPlane.mWidth / 2 == aCbPlane.mWidth &&
|
||||
aYPlane.mHeight / 2 == aCbPlane.mHeight &&
|
||||
aCbPlane.mWidth == aCrPlane.mWidth &&
|
||||
aCbPlane.mHeight == aCrPlane.mHeight;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
VideoInfo::ValidateVideoRegion(const nsIntSize& aFrame,
|
||||
const nsIntRect& aPicture,
|
||||
const nsIntSize& aDisplay)
|
||||
{
|
||||
return
|
||||
aFrame.width <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aFrame.height <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aFrame.width * aFrame.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
|
||||
aFrame.width * aFrame.height != 0 &&
|
||||
aPicture.width <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.x < PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.x + aPicture.width < PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.height <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.y < PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.y + aPicture.height < PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.width * aPicture.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
|
||||
aPicture.width * aPicture.height != 0 &&
|
||||
aDisplay.width <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aDisplay.height <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aDisplay.width * aDisplay.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
|
||||
aDisplay.width * aDisplay.height != 0;
|
||||
}
|
||||
|
||||
VideoData::VideoData(int64_t aOffset, int64_t aTime, int64_t aDuration, int64_t aTimecode)
|
||||
: MediaData(VIDEO_FRAME, aOffset, aTime, aDuration),
|
||||
mTimecode(aTimecode),
|
||||
mDuplicate(true),
|
||||
mKeyframe(false)
|
||||
{
|
||||
MOZ_COUNT_CTOR(VideoData);
|
||||
NS_ASSERTION(mDuration >= 0, "Frame must have non-negative duration.");
|
||||
}
|
||||
|
||||
VideoData::VideoData(int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntSize aDisplay)
|
||||
: MediaData(VIDEO_FRAME, aOffset, aTime, aDuration),
|
||||
mDisplay(aDisplay),
|
||||
mTimecode(aTimecode),
|
||||
mDuplicate(false),
|
||||
mKeyframe(aKeyframe)
|
||||
{
|
||||
MOZ_COUNT_CTOR(VideoData);
|
||||
NS_ASSERTION(mDuration >= 0, "Frame must have non-negative duration.");
|
||||
}
|
||||
|
||||
VideoData::~VideoData()
|
||||
{
|
||||
MOZ_COUNT_DTOR(VideoData);
|
||||
}
|
||||
|
||||
/* static */
|
||||
VideoData* VideoData::ShallowCopyUpdateDuration(VideoData* aOther,
|
||||
int64_t aDuration)
|
||||
{
|
||||
VideoData* v = new VideoData(aOther->mOffset,
|
||||
aOther->mTime,
|
||||
aDuration,
|
||||
aOther->mKeyframe,
|
||||
aOther->mTimecode,
|
||||
aOther->mDisplay);
|
||||
v->mImage = aOther->mImage;
|
||||
return v;
|
||||
}
|
||||
|
||||
VideoData* VideoData::Create(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
Image* aImage,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const YCbCrBuffer& aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture)
|
||||
{
|
||||
if (!aImage && !aContainer) {
|
||||
// Create a dummy VideoData with no image. This gives us something to
|
||||
// send to media streams if necessary.
|
||||
nsAutoPtr<VideoData> v(new VideoData(aOffset,
|
||||
aTime,
|
||||
aDuration,
|
||||
aKeyframe,
|
||||
aTimecode,
|
||||
aInfo.mDisplay));
|
||||
return v.forget();
|
||||
}
|
||||
|
||||
// The following situation should never happen unless there is a bug
|
||||
// in the decoder
|
||||
if (aBuffer.mPlanes[1].mWidth != aBuffer.mPlanes[2].mWidth ||
|
||||
aBuffer.mPlanes[1].mHeight != aBuffer.mPlanes[2].mHeight) {
|
||||
NS_ERROR("C planes with different sizes");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// The following situations could be triggered by invalid input
|
||||
if (aPicture.width <= 0 || aPicture.height <= 0) {
|
||||
NS_WARNING("Empty picture rect");
|
||||
return nullptr;
|
||||
}
|
||||
if (!ValidatePlane(aBuffer.mPlanes[0]) || !ValidatePlane(aBuffer.mPlanes[1]) ||
|
||||
!ValidatePlane(aBuffer.mPlanes[2])) {
|
||||
NS_WARNING("Invalid plane size");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure the picture size specified in the headers can be extracted out of
|
||||
// the frame we've been supplied without indexing out of bounds.
|
||||
CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
|
||||
CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
|
||||
if (!xLimit.isValid() || xLimit.value() > aBuffer.mPlanes[0].mStride ||
|
||||
!yLimit.isValid() || yLimit.value() > aBuffer.mPlanes[0].mHeight)
|
||||
{
|
||||
// The specified picture dimensions can't be contained inside the video
|
||||
// frame, we'll stomp memory if we try to copy it. Fail.
|
||||
NS_WARNING("Overflowing picture rect");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoPtr<VideoData> v(new VideoData(aOffset,
|
||||
aTime,
|
||||
aDuration,
|
||||
aKeyframe,
|
||||
aTimecode,
|
||||
aInfo.mDisplay));
|
||||
const YCbCrBuffer::Plane &Y = aBuffer.mPlanes[0];
|
||||
const YCbCrBuffer::Plane &Cb = aBuffer.mPlanes[1];
|
||||
const YCbCrBuffer::Plane &Cr = aBuffer.mPlanes[2];
|
||||
|
||||
if (!aImage) {
|
||||
// Currently our decoder only knows how to output to ImageFormat::PLANAR_YCBCR
|
||||
// format.
|
||||
#if 0
|
||||
if (IsYV12Format(Y, Cb, Cr)) {
|
||||
v->mImage = aContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
|
||||
}
|
||||
#endif
|
||||
if (!v->mImage) {
|
||||
v->mImage = aContainer->CreateImage(ImageFormat::PLANAR_YCBCR);
|
||||
}
|
||||
} else {
|
||||
v->mImage = aImage;
|
||||
}
|
||||
|
||||
if (!v->mImage) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::PLANAR_YCBCR ||
|
||||
v->mImage->GetFormat() == ImageFormat::GRALLOC_PLANAR_YCBCR,
|
||||
"Wrong format?");
|
||||
PlanarYCbCrImage* videoImage = static_cast<PlanarYCbCrImage*>(v->mImage.get());
|
||||
|
||||
PlanarYCbCrData data;
|
||||
data.mYChannel = Y.mData + Y.mOffset;
|
||||
data.mYSize = IntSize(Y.mWidth, Y.mHeight);
|
||||
data.mYStride = Y.mStride;
|
||||
data.mYSkip = Y.mSkip;
|
||||
data.mCbChannel = Cb.mData + Cb.mOffset;
|
||||
data.mCrChannel = Cr.mData + Cr.mOffset;
|
||||
data.mCbCrSize = IntSize(Cb.mWidth, Cb.mHeight);
|
||||
data.mCbCrStride = Cb.mStride;
|
||||
data.mCbSkip = Cb.mSkip;
|
||||
data.mCrSkip = Cr.mSkip;
|
||||
data.mPicX = aPicture.x;
|
||||
data.mPicY = aPicture.y;
|
||||
data.mPicSize = aPicture.Size().ToIntSize();
|
||||
data.mStereoMode = aInfo.mStereoMode;
|
||||
|
||||
videoImage->SetDelayedConversion(true);
|
||||
if (!aImage) {
|
||||
videoImage->SetData(data);
|
||||
} else {
|
||||
videoImage->SetDataNoCopy(data);
|
||||
}
|
||||
|
||||
return v.forget();
|
||||
}
|
||||
|
||||
VideoData* VideoData::Create(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const YCbCrBuffer& aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture)
|
||||
{
|
||||
return Create(aInfo, aContainer, nullptr, aOffset, aTime, aDuration, aBuffer,
|
||||
aKeyframe, aTimecode, aPicture);
|
||||
}
|
||||
|
||||
VideoData* VideoData::Create(VideoInfo& aInfo,
|
||||
Image* aImage,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const YCbCrBuffer& aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture)
|
||||
{
|
||||
return Create(aInfo, nullptr, aImage, aOffset, aTime, aDuration, aBuffer,
|
||||
aKeyframe, aTimecode, aPicture);
|
||||
}
|
||||
|
||||
VideoData* VideoData::CreateFromImage(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const nsRefPtr<Image>& aImage,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture)
|
||||
{
|
||||
nsAutoPtr<VideoData> v(new VideoData(aOffset,
|
||||
aTime,
|
||||
aDuration,
|
||||
aKeyframe,
|
||||
aTimecode,
|
||||
aInfo.mDisplay));
|
||||
v->mImage = aImage;
|
||||
return v.forget();
|
||||
}
|
||||
|
||||
#ifdef MOZ_OMX_DECODER
|
||||
VideoData* VideoData::Create(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
mozilla::layers::GraphicBufferLocked* aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture)
|
||||
{
|
||||
if (!aContainer) {
|
||||
// Create a dummy VideoData with no image. This gives us something to
|
||||
// send to media streams if necessary.
|
||||
nsAutoPtr<VideoData> v(new VideoData(aOffset,
|
||||
aTime,
|
||||
aDuration,
|
||||
aKeyframe,
|
||||
aTimecode,
|
||||
aInfo.mDisplay));
|
||||
return v.forget();
|
||||
}
|
||||
|
||||
// The following situations could be triggered by invalid input
|
||||
if (aPicture.width <= 0 || aPicture.height <= 0) {
|
||||
NS_WARNING("Empty picture rect");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Ensure the picture size specified in the headers can be extracted out of
|
||||
// the frame we've been supplied without indexing out of bounds.
|
||||
CheckedUint32 xLimit = aPicture.x + CheckedUint32(aPicture.width);
|
||||
CheckedUint32 yLimit = aPicture.y + CheckedUint32(aPicture.height);
|
||||
if (!xLimit.isValid() || !yLimit.isValid())
|
||||
{
|
||||
// The specified picture dimensions can't be contained inside the video
|
||||
// frame, we'll stomp memory if we try to copy it. Fail.
|
||||
NS_WARNING("Overflowing picture rect");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsAutoPtr<VideoData> v(new VideoData(aOffset,
|
||||
aTime,
|
||||
aDuration,
|
||||
aKeyframe,
|
||||
aTimecode,
|
||||
aInfo.mDisplay));
|
||||
|
||||
v->mImage = aContainer->CreateImage(ImageFormat::GRALLOC_PLANAR_YCBCR);
|
||||
if (!v->mImage) {
|
||||
return nullptr;
|
||||
}
|
||||
NS_ASSERTION(v->mImage->GetFormat() == ImageFormat::GRALLOC_PLANAR_YCBCR,
|
||||
"Wrong format?");
|
||||
typedef mozilla::layers::GrallocImage GrallocImage;
|
||||
GrallocImage* videoImage = static_cast<GrallocImage*>(v->mImage.get());
|
||||
GrallocImage::GrallocData data;
|
||||
|
||||
data.mPicSize = aPicture.Size().ToIntSize();
|
||||
data.mGraphicBuffer = aBuffer;
|
||||
|
||||
videoImage->SetData(data);
|
||||
|
||||
return v.forget();
|
||||
}
|
||||
#endif // MOZ_OMX_DECODER
|
||||
|
||||
void* MediaDecoderReader::VideoQueueMemoryFunctor::operator()(void* anObject) {
|
||||
const VideoData* v = static_cast<const VideoData*>(anObject);
|
||||
if (!v->mImage) {
|
||||
|
|
|
@ -1,471 +1,22 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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/. */
|
||||
#if !defined(MediaDecoderReader_h_)
|
||||
#define MediaDecoderReader_h_
|
||||
|
||||
#include <nsDeque.h>
|
||||
#include "nsSize.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
#include "SharedBuffer.h"
|
||||
#include "AudioSampleFormat.h"
|
||||
#include "AbstractMediaDecoder.h"
|
||||
#include "ImageTypes.h"
|
||||
#include "nsIMemoryReporter.h"
|
||||
|
||||
struct nsIntRect;
|
||||
#include "MediaInfo.h"
|
||||
#include "MediaData.h"
|
||||
#include "MediaQueue.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace layers {
|
||||
class Image;
|
||||
class ImageContainer;
|
||||
}
|
||||
|
||||
namespace dom {
|
||||
class TimeRanges;
|
||||
}
|
||||
|
||||
// Stores info relevant to presenting media frames.
|
||||
class VideoInfo {
|
||||
public:
|
||||
VideoInfo()
|
||||
: mDisplay(0,0),
|
||||
mStereoMode(StereoMode::MONO),
|
||||
mHasVideo(false)
|
||||
{}
|
||||
|
||||
// Returns true if it's safe to use aPicture as the picture to be
|
||||
// extracted inside a frame of size aFrame, and scaled up to and displayed
|
||||
// at a size of aDisplay. You should validate the frame, picture, and
|
||||
// display regions before using them to display video frames.
|
||||
static bool ValidateVideoRegion(const nsIntSize& aFrame,
|
||||
const nsIntRect& aPicture,
|
||||
const nsIntSize& aDisplay);
|
||||
|
||||
// Size in pixels at which the video is rendered. This is after it has
|
||||
// been scaled by its aspect ratio.
|
||||
nsIntSize mDisplay;
|
||||
|
||||
// Indicates the frame layout for single track stereo videos.
|
||||
StereoMode mStereoMode;
|
||||
|
||||
// True if we have an active video bitstream.
|
||||
bool mHasVideo;
|
||||
};
|
||||
|
||||
class AudioInfo {
|
||||
public:
|
||||
AudioInfo()
|
||||
: mRate(44100),
|
||||
mChannels(2),
|
||||
mHasAudio(false)
|
||||
{}
|
||||
|
||||
// Sample rate.
|
||||
uint32_t mRate;
|
||||
|
||||
// Number of audio channels.
|
||||
uint32_t mChannels;
|
||||
|
||||
// True if we have an active audio bitstream.
|
||||
bool mHasAudio;
|
||||
};
|
||||
|
||||
class MediaInfo {
|
||||
public:
|
||||
bool HasVideo() const
|
||||
{
|
||||
return mVideo.mHasVideo;
|
||||
}
|
||||
|
||||
bool HasAudio() const
|
||||
{
|
||||
return mAudio.mHasAudio;
|
||||
}
|
||||
|
||||
bool HasValidMedia() const
|
||||
{
|
||||
return HasVideo() || HasAudio();
|
||||
}
|
||||
|
||||
VideoInfo mVideo;
|
||||
AudioInfo mAudio;
|
||||
};
|
||||
|
||||
// Container that holds media samples.
|
||||
class MediaData {
|
||||
public:
|
||||
|
||||
enum Type {
|
||||
AUDIO_SAMPLES = 0,
|
||||
VIDEO_FRAME = 1
|
||||
};
|
||||
|
||||
MediaData(Type aType,
|
||||
int64_t aOffset,
|
||||
int64_t aTimestamp,
|
||||
int64_t aDuration)
|
||||
: mType(aType),
|
||||
mOffset(aOffset),
|
||||
mTime(aTimestamp),
|
||||
mDuration(aDuration)
|
||||
{}
|
||||
|
||||
virtual ~MediaData() {}
|
||||
|
||||
// Type of contained data.
|
||||
const Type mType;
|
||||
|
||||
// Approximate byte offset where this data was demuxed from its media.
|
||||
const int64_t mOffset;
|
||||
|
||||
// Start time of sample, in microseconds.
|
||||
const int64_t mTime;
|
||||
|
||||
// Duration of sample, in microseconds.
|
||||
const int64_t mDuration;
|
||||
|
||||
int64_t GetEndTime() const { return mTime + mDuration; }
|
||||
|
||||
};
|
||||
|
||||
// Holds chunk a decoded audio frames.
|
||||
class AudioData : public MediaData {
|
||||
public:
|
||||
|
||||
AudioData(int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
uint32_t aFrames,
|
||||
AudioDataValue* aData,
|
||||
uint32_t aChannels)
|
||||
: MediaData(AUDIO_SAMPLES, aOffset, aTime, aDuration),
|
||||
mFrames(aFrames),
|
||||
mChannels(aChannels),
|
||||
mAudioData(aData)
|
||||
{
|
||||
MOZ_COUNT_CTOR(AudioData);
|
||||
}
|
||||
|
||||
~AudioData()
|
||||
{
|
||||
MOZ_COUNT_DTOR(AudioData);
|
||||
}
|
||||
|
||||
size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
|
||||
size_t size = aMallocSizeOf(this) + aMallocSizeOf(mAudioData);
|
||||
if (mAudioBuffer) {
|
||||
size += mAudioBuffer->SizeOfIncludingThis(aMallocSizeOf);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
// If mAudioBuffer is null, creates it from mAudioData.
|
||||
void EnsureAudioBuffer();
|
||||
|
||||
const uint32_t mFrames;
|
||||
const uint32_t mChannels;
|
||||
// At least one of mAudioBuffer/mAudioData must be non-null.
|
||||
// mChannels channels, each with mFrames frames
|
||||
nsRefPtr<SharedBuffer> mAudioBuffer;
|
||||
// mFrames frames, each with mChannels values
|
||||
nsAutoArrayPtr<AudioDataValue> mAudioData;
|
||||
};
|
||||
|
||||
namespace layers {
|
||||
class GraphicBufferLocked;
|
||||
}
|
||||
|
||||
// Holds a decoded video frame, in YCbCr format. These are queued in the reader.
|
||||
class VideoData : public MediaData {
|
||||
public:
|
||||
typedef layers::ImageContainer ImageContainer;
|
||||
typedef layers::Image Image;
|
||||
|
||||
// YCbCr data obtained from decoding the video. The index's are:
|
||||
// 0 = Y
|
||||
// 1 = Cb
|
||||
// 2 = Cr
|
||||
struct YCbCrBuffer {
|
||||
struct Plane {
|
||||
uint8_t* mData;
|
||||
uint32_t mWidth;
|
||||
uint32_t mHeight;
|
||||
uint32_t mStride;
|
||||
uint32_t mOffset;
|
||||
uint32_t mSkip;
|
||||
};
|
||||
|
||||
Plane mPlanes[3];
|
||||
};
|
||||
|
||||
// Constructs a VideoData object. If aImage is nullptr, creates a new Image
|
||||
// holding a copy of the YCbCr data passed in aBuffer. If aImage is not
|
||||
// nullptr, it's stored as the underlying video image and aBuffer is assumed
|
||||
// to point to memory within aImage so no copy is made. aTimecode is a codec
|
||||
// specific number representing the timestamp of the frame of video data.
|
||||
// Returns nsnull if an error occurs. This may indicate that memory couldn't
|
||||
// be allocated to create the VideoData object, or it may indicate some
|
||||
// problem with the input data (e.g. negative stride).
|
||||
static VideoData* Create(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
Image* aImage,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const YCbCrBuffer &aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture);
|
||||
|
||||
// Variant that always makes a copy of aBuffer
|
||||
static VideoData* Create(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const YCbCrBuffer &aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture);
|
||||
|
||||
// Variant to create a VideoData instance given an existing aImage
|
||||
static VideoData* Create(VideoInfo& aInfo,
|
||||
Image* aImage,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const YCbCrBuffer &aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture);
|
||||
|
||||
static VideoData* Create(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
layers::GraphicBufferLocked* aBuffer,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture);
|
||||
|
||||
static VideoData* CreateFromImage(VideoInfo& aInfo,
|
||||
ImageContainer* aContainer,
|
||||
int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
const nsRefPtr<Image>& aImage,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntRect aPicture);
|
||||
|
||||
// Creates a new VideoData identical to aOther, but with a different
|
||||
// specified duration. All data from aOther is copied into the new
|
||||
// VideoData. The new VideoData's mImage field holds a reference to
|
||||
// aOther's mImage, i.e. the Image is not copied. This function is useful
|
||||
// in reader backends that can't determine the duration of a VideoData
|
||||
// until the next frame is decoded, i.e. it's a way to change the const
|
||||
// duration field on a VideoData.
|
||||
static VideoData* ShallowCopyUpdateDuration(VideoData* aOther,
|
||||
int64_t aDuration);
|
||||
|
||||
// Constructs a duplicate VideoData object. This intrinsically tells the
|
||||
// player that it does not need to update the displayed frame when this
|
||||
// frame is played; this frame is identical to the previous.
|
||||
static VideoData* CreateDuplicate(int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
int64_t aTimecode)
|
||||
{
|
||||
return new VideoData(aOffset, aTime, aDuration, aTimecode);
|
||||
}
|
||||
|
||||
~VideoData();
|
||||
|
||||
// Dimensions at which to display the video frame. The picture region
|
||||
// will be scaled to this size. This is should be the picture region's
|
||||
// dimensions scaled with respect to its aspect ratio.
|
||||
const nsIntSize mDisplay;
|
||||
|
||||
// Codec specific internal time code. For Ogg based codecs this is the
|
||||
// granulepos.
|
||||
const int64_t mTimecode;
|
||||
|
||||
// This frame's image.
|
||||
nsRefPtr<Image> mImage;
|
||||
|
||||
// When true, denotes that this frame is identical to the frame that
|
||||
// came before; it's a duplicate. mBuffer will be empty.
|
||||
const bool mDuplicate;
|
||||
const bool mKeyframe;
|
||||
|
||||
public:
|
||||
VideoData(int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
int64_t aTimecode);
|
||||
|
||||
VideoData(int64_t aOffset,
|
||||
int64_t aTime,
|
||||
int64_t aDuration,
|
||||
bool aKeyframe,
|
||||
int64_t aTimecode,
|
||||
nsIntSize aDisplay);
|
||||
|
||||
};
|
||||
|
||||
// Thread and type safe wrapper around nsDeque.
|
||||
template <class T>
|
||||
class MediaQueueDeallocator : public nsDequeFunctor {
|
||||
virtual void* operator() (void* anObject) {
|
||||
delete static_cast<T*>(anObject);
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> class MediaQueue : private nsDeque {
|
||||
public:
|
||||
|
||||
MediaQueue()
|
||||
: nsDeque(new MediaQueueDeallocator<T>()),
|
||||
mReentrantMonitor("mediaqueue"),
|
||||
mEndOfStream(false)
|
||||
{}
|
||||
|
||||
~MediaQueue() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
inline int32_t GetSize() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return nsDeque::GetSize();
|
||||
}
|
||||
|
||||
inline void Push(T* aItem) {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
nsDeque::Push(aItem);
|
||||
}
|
||||
|
||||
inline void PushFront(T* aItem) {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
nsDeque::PushFront(aItem);
|
||||
}
|
||||
|
||||
inline T* Pop() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return static_cast<T*>(nsDeque::Pop());
|
||||
}
|
||||
|
||||
inline T* PopFront() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return static_cast<T*>(nsDeque::PopFront());
|
||||
}
|
||||
|
||||
inline T* Peek() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return static_cast<T*>(nsDeque::Peek());
|
||||
}
|
||||
|
||||
inline T* PeekFront() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return static_cast<T*>(nsDeque::PeekFront());
|
||||
}
|
||||
|
||||
inline void Empty() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
nsDeque::Empty();
|
||||
}
|
||||
|
||||
inline void Erase() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
nsDeque::Erase();
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
while (GetSize() > 0) {
|
||||
T* x = PopFront();
|
||||
delete x;
|
||||
}
|
||||
mEndOfStream = false;
|
||||
}
|
||||
|
||||
bool AtEndOfStream() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return GetSize() == 0 && mEndOfStream;
|
||||
}
|
||||
|
||||
// Returns true if the media queue has had its last item added to it.
|
||||
// This happens when the media stream has been completely decoded. Note this
|
||||
// does not mean that the corresponding stream has finished playback.
|
||||
bool IsFinished() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return mEndOfStream;
|
||||
}
|
||||
|
||||
// Informs the media queue that it won't be receiving any more items.
|
||||
void Finish() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
mEndOfStream = true;
|
||||
}
|
||||
|
||||
// Returns the approximate number of microseconds of items in the queue.
|
||||
int64_t Duration() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
if (GetSize() < 2) {
|
||||
return 0;
|
||||
}
|
||||
T* last = Peek();
|
||||
T* first = PeekFront();
|
||||
return last->mTime - first->mTime;
|
||||
}
|
||||
|
||||
void LockedForEach(nsDequeFunctor& aFunctor) const {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
ForEach(aFunctor);
|
||||
}
|
||||
|
||||
// Extracts elements from the queue into aResult, in order.
|
||||
// Elements whose start time is before aTime are ignored.
|
||||
void GetElementsAfter(int64_t aTime, nsTArray<T*>* aResult) {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
if (!GetSize())
|
||||
return;
|
||||
int32_t i;
|
||||
for (i = GetSize() - 1; i > 0; --i) {
|
||||
T* v = static_cast<T*>(ObjectAt(i));
|
||||
if (v->GetEndTime() < aTime)
|
||||
break;
|
||||
}
|
||||
// Elements less than i have a end time before aTime. It's also possible
|
||||
// that the element at i has a end time before aTime, but that's OK.
|
||||
for (; i < GetSize(); ++i) {
|
||||
aResult->AppendElement(static_cast<T*>(ObjectAt(i)));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t FrameCount() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
uint32_t frames = 0;
|
||||
for (int32_t i = 0; i < GetSize(); ++i) {
|
||||
T* v = static_cast<T*>(ObjectAt(i));
|
||||
frames += v->mFrames;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable ReentrantMonitor mReentrantMonitor;
|
||||
|
||||
// True when we've decoded the last frame of data in the
|
||||
// bitstream for which we're queueing frame data.
|
||||
bool mEndOfStream;
|
||||
};
|
||||
|
||||
// Encapsulates the decoding and reading of media data. Reading can only be
|
||||
// done on the decode thread. Never hold the decoder monitor when
|
||||
// calling into this class. Unless otherwise specified, methods and fields of
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=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/. */
|
||||
#if !defined(MediaInfo_h)
|
||||
#define MediaInfo_h
|
||||
|
||||
#include "nsSize.h"
|
||||
#include "nsRect.h"
|
||||
#include "ImageTypes.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Stores info relevant to presenting media frames.
|
||||
class VideoInfo {
|
||||
public:
|
||||
VideoInfo()
|
||||
: mDisplay(0,0)
|
||||
, mStereoMode(StereoMode::MONO)
|
||||
, mHasVideo(false)
|
||||
{}
|
||||
|
||||
// Size in pixels at which the video is rendered. This is after it has
|
||||
// been scaled by its aspect ratio.
|
||||
nsIntSize mDisplay;
|
||||
|
||||
// Indicates the frame layout for single track stereo videos.
|
||||
StereoMode mStereoMode;
|
||||
|
||||
// True if we have an active video bitstream.
|
||||
bool mHasVideo;
|
||||
};
|
||||
|
||||
class AudioInfo {
|
||||
public:
|
||||
AudioInfo()
|
||||
: mRate(44100)
|
||||
, mChannels(2)
|
||||
, mHasAudio(false)
|
||||
{}
|
||||
|
||||
// Sample rate.
|
||||
uint32_t mRate;
|
||||
|
||||
// Number of audio channels.
|
||||
uint32_t mChannels;
|
||||
|
||||
// True if we have an active audio bitstream.
|
||||
bool mHasAudio;
|
||||
};
|
||||
|
||||
class MediaInfo {
|
||||
public:
|
||||
bool HasVideo() const
|
||||
{
|
||||
return mVideo.mHasVideo;
|
||||
}
|
||||
|
||||
bool HasAudio() const
|
||||
{
|
||||
return mAudio.mHasAudio;
|
||||
}
|
||||
|
||||
bool HasValidMedia() const
|
||||
{
|
||||
return HasVideo() || HasAudio();
|
||||
}
|
||||
|
||||
VideoInfo mVideo;
|
||||
AudioInfo mAudio;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // MediaInfo_h
|
|
@ -0,0 +1,165 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:set ts=2 sw=2 sts=2 et cindent: */
|
||||
/* 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/. */
|
||||
#if !defined(MediaQueue_h_)
|
||||
#define MediaQueue_h_
|
||||
|
||||
#include "nsDeque.h"
|
||||
#include "nsTArray.h"
|
||||
#include "mozilla/ReentrantMonitor.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
// Thread and type safe wrapper around nsDeque.
|
||||
template <class T>
|
||||
class MediaQueueDeallocator : public nsDequeFunctor {
|
||||
virtual void* operator() (void* anObject) {
|
||||
delete static_cast<T*>(anObject);
|
||||
return nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T> class MediaQueue : private nsDeque {
|
||||
public:
|
||||
|
||||
MediaQueue()
|
||||
: nsDeque(new MediaQueueDeallocator<T>()),
|
||||
mReentrantMonitor("mediaqueue"),
|
||||
mEndOfStream(false)
|
||||
{}
|
||||
|
||||
~MediaQueue() {
|
||||
Reset();
|
||||
}
|
||||
|
||||
inline int32_t GetSize() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return nsDeque::GetSize();
|
||||
}
|
||||
|
||||
inline void Push(T* aItem) {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
nsDeque::Push(aItem);
|
||||
}
|
||||
|
||||
inline void PushFront(T* aItem) {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
nsDeque::PushFront(aItem);
|
||||
}
|
||||
|
||||
inline T* Pop() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return static_cast<T*>(nsDeque::Pop());
|
||||
}
|
||||
|
||||
inline T* PopFront() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return static_cast<T*>(nsDeque::PopFront());
|
||||
}
|
||||
|
||||
inline T* Peek() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return static_cast<T*>(nsDeque::Peek());
|
||||
}
|
||||
|
||||
inline T* PeekFront() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return static_cast<T*>(nsDeque::PeekFront());
|
||||
}
|
||||
|
||||
inline void Empty() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
nsDeque::Empty();
|
||||
}
|
||||
|
||||
inline void Erase() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
nsDeque::Erase();
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
while (GetSize() > 0) {
|
||||
T* x = PopFront();
|
||||
delete x;
|
||||
}
|
||||
mEndOfStream = false;
|
||||
}
|
||||
|
||||
bool AtEndOfStream() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return GetSize() == 0 && mEndOfStream;
|
||||
}
|
||||
|
||||
// Returns true if the media queue has had its last item added to it.
|
||||
// This happens when the media stream has been completely decoded. Note this
|
||||
// does not mean that the corresponding stream has finished playback.
|
||||
bool IsFinished() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
return mEndOfStream;
|
||||
}
|
||||
|
||||
// Informs the media queue that it won't be receiving any more items.
|
||||
void Finish() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
mEndOfStream = true;
|
||||
}
|
||||
|
||||
// Returns the approximate number of microseconds of items in the queue.
|
||||
int64_t Duration() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
if (GetSize() < 2) {
|
||||
return 0;
|
||||
}
|
||||
T* last = Peek();
|
||||
T* first = PeekFront();
|
||||
return last->mTime - first->mTime;
|
||||
}
|
||||
|
||||
void LockedForEach(nsDequeFunctor& aFunctor) const {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
ForEach(aFunctor);
|
||||
}
|
||||
|
||||
// Extracts elements from the queue into aResult, in order.
|
||||
// Elements whose start time is before aTime are ignored.
|
||||
void GetElementsAfter(int64_t aTime, nsTArray<T*>* aResult) {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
if (!GetSize())
|
||||
return;
|
||||
int32_t i;
|
||||
for (i = GetSize() - 1; i > 0; --i) {
|
||||
T* v = static_cast<T*>(ObjectAt(i));
|
||||
if (v->GetEndTime() < aTime)
|
||||
break;
|
||||
}
|
||||
// Elements less than i have a end time before aTime. It's also possible
|
||||
// that the element at i has a end time before aTime, but that's OK.
|
||||
for (; i < GetSize(); ++i) {
|
||||
aResult->AppendElement(static_cast<T*>(ObjectAt(i)));
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t FrameCount() {
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
uint32_t frames = 0;
|
||||
for (int32_t i = 0; i < GetSize(); ++i) {
|
||||
T* v = static_cast<T*>(ObjectAt(i));
|
||||
frames += v->mFrames;
|
||||
}
|
||||
return frames;
|
||||
}
|
||||
|
||||
private:
|
||||
mutable ReentrantMonitor mReentrantMonitor;
|
||||
|
||||
// True when we've decoded the last frame of data in the
|
||||
// bitstream for which we're queueing frame data.
|
||||
bool mEndOfStream;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -8,11 +8,14 @@
|
|||
#include "nsMathUtils.h"
|
||||
#include "nsSize.h"
|
||||
#include "VorbisUtils.h"
|
||||
#include "ImageContainer.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using layers::PlanarYCbCrImage;
|
||||
|
||||
// Converts from number of audio frames to microseconds, given the specified
|
||||
// audio rate.
|
||||
CheckedInt64 FramesToUsecs(int64_t aFrames, uint32_t aRate) {
|
||||
|
@ -156,4 +159,27 @@ IsVideoContentType(const nsCString& aContentType)
|
|||
return false;
|
||||
}
|
||||
|
||||
} // end namespace mozilla
|
||||
bool
|
||||
IsValidVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture,
|
||||
const nsIntSize& aDisplay)
|
||||
{
|
||||
return
|
||||
aFrame.width <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aFrame.height <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aFrame.width * aFrame.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
|
||||
aFrame.width * aFrame.height != 0 &&
|
||||
aPicture.width <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.x < PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.x + aPicture.width < PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.height <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.y < PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.y + aPicture.height < PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aPicture.width * aPicture.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
|
||||
aPicture.width * aPicture.height != 0 &&
|
||||
aDisplay.width <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aDisplay.height <= PlanarYCbCrImage::MAX_DIMENSION &&
|
||||
aDisplay.width * aDisplay.height <= MAX_VIDEO_WIDTH * MAX_VIDEO_HEIGHT &&
|
||||
aDisplay.width * aDisplay.height != 0;
|
||||
}
|
||||
|
||||
} // end namespace mozilla
|
||||
|
|
|
@ -26,6 +26,7 @@ using mozilla::CheckedInt32;
|
|||
using mozilla::CheckedUint32;
|
||||
|
||||
struct nsIntSize;
|
||||
struct nsIntRect;
|
||||
|
||||
// This file contains stuff we'd rather put elsewhere, but which is
|
||||
// dependent on other changes which we don't want to wait for. We plan to
|
||||
|
@ -135,7 +136,7 @@ static const int32_t MAX_VIDEO_WIDTH = 4000;
|
|||
static const int32_t MAX_VIDEO_HEIGHT = 3000;
|
||||
|
||||
// Scales the display rect aDisplay by aspect ratio aAspectRatio.
|
||||
// Note that aDisplay must be validated by VideoInfo::ValidateVideoRegion()
|
||||
// Note that aDisplay must be validated by IsValidVideoRegion()
|
||||
// before being used!
|
||||
void ScaleDisplayByAspectRatio(nsIntSize& aDisplay, float aAspectRatio);
|
||||
|
||||
|
@ -157,6 +158,13 @@ int DownmixAudioToStereo(mozilla::AudioDataValue* buffer,
|
|||
|
||||
bool IsVideoContentType(const nsCString& aContentType);
|
||||
|
||||
// Returns true if it's safe to use aPicture as the picture to be
|
||||
// extracted inside a frame of size aFrame, and scaled up to and displayed
|
||||
// at a size of aDisplay. You should validate the frame, picture, and
|
||||
// display regions before using them to display video frames.
|
||||
bool IsValidVideoRegion(const nsIntSize& aFrame, const nsIntRect& aPicture,
|
||||
const nsIntSize& aDisplay);
|
||||
|
||||
} // end namespace mozilla
|
||||
|
||||
#endif
|
||||
|
|
|
@ -171,7 +171,7 @@ WMFVideoDecoder::ConfigureVideoFrameGeometry()
|
|||
nsIntSize frameSize = nsIntSize(width, height);
|
||||
nsIntSize displaySize = nsIntSize(pictureRegion.width, pictureRegion.height);
|
||||
ScaleDisplayByAspectRatio(displaySize, float(aspectNum) / float(aspectDenom));
|
||||
if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRegion, displaySize)) {
|
||||
if (!IsValidVideoRegion(frameSize, pictureRegion, displaySize)) {
|
||||
// Video track's frame sizes will overflow. Ignore the video track.
|
||||
return E_FAIL;
|
||||
}
|
||||
|
|
|
@ -73,11 +73,14 @@ EXPORTS += [
|
|||
'FileBlockCache.h',
|
||||
'Latency.h',
|
||||
'MediaCache.h',
|
||||
'MediaData.h',
|
||||
'MediaDecoder.h',
|
||||
'MediaDecoderOwner.h',
|
||||
'MediaDecoderReader.h',
|
||||
'MediaDecoderStateMachine.h',
|
||||
'MediaInfo.h',
|
||||
'MediaMetadataManager.h',
|
||||
'MediaQueue.h',
|
||||
'MediaRecorder.h',
|
||||
'MediaResource.h',
|
||||
'MediaSegment.h',
|
||||
|
@ -122,6 +125,7 @@ UNIFIED_SOURCES += [
|
|||
'EncodedBufferCache.cpp',
|
||||
'FileBlockCache.cpp',
|
||||
'MediaCache.cpp',
|
||||
'MediaData.cpp',
|
||||
'MediaDecoder.cpp',
|
||||
'MediaDecoderReader.cpp',
|
||||
'MediaDecoderStateMachine.cpp',
|
||||
|
|
|
@ -276,7 +276,7 @@ bool TheoraState::Init() {
|
|||
// maximum, or zero sized.
|
||||
nsIntSize frame(mInfo.frame_width, mInfo.frame_height);
|
||||
nsIntRect picture(mInfo.pic_x, mInfo.pic_y, mInfo.pic_width, mInfo.pic_height);
|
||||
if (!VideoInfo::ValidateVideoRegion(frame, picture, frame)) {
|
||||
if (!IsValidVideoRegion(frame, picture, frame)) {
|
||||
return mActive = false;
|
||||
}
|
||||
|
||||
|
|
|
@ -288,7 +288,7 @@ nsresult OggReader::ReadMetadata(MediaInfo* aInfo,
|
|||
|
||||
nsIntSize frameSize(mTheoraState->mInfo.frame_width,
|
||||
mTheoraState->mInfo.frame_height);
|
||||
if (VideoInfo::ValidateVideoRegion(frameSize, picture, displaySize)) {
|
||||
if (IsValidVideoRegion(frameSize, picture, displaySize)) {
|
||||
// Video track's frame sizes will not overflow. Activate the video track.
|
||||
mInfo.mVideo.mHasVideo = true;
|
||||
mInfo.mVideo.mDisplay = displaySize;
|
||||
|
|
|
@ -146,7 +146,7 @@ nsresult MediaOmxReader::ReadMetadata(MediaInfo* aInfo,
|
|||
// that our video frame creation code doesn't overflow.
|
||||
nsIntSize displaySize(width, height);
|
||||
nsIntSize frameSize(width, height);
|
||||
if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRect, displaySize)) {
|
||||
if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
@ -73,7 +73,7 @@ nsresult MediaPluginReader::ReadMetadata(MediaInfo* aInfo,
|
|||
// that our video frame creation code doesn't overflow.
|
||||
nsIntSize displaySize(width, height);
|
||||
nsIntSize frameSize(width, height);
|
||||
if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRect, displaySize)) {
|
||||
if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
@ -70,7 +70,7 @@ nsresult RawReader::ReadMetadata(MediaInfo* aInfo,
|
|||
ScaleDisplayByAspectRatio(display, pixelAspectRatio);
|
||||
mPicture = nsIntRect(0, 0, mMetadata.frameWidth, mMetadata.frameHeight);
|
||||
nsIntSize frameSize(mMetadata.frameWidth, mMetadata.frameHeight);
|
||||
if (!VideoInfo::ValidateVideoRegion(frameSize, mPicture, display)) {
|
||||
if (!IsValidVideoRegion(frameSize, mPicture, display)) {
|
||||
// Video track's frame sizes will overflow. Fail.
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
|
|
@ -329,7 +329,7 @@ nsresult WebMReader::ReadMetadata(MediaInfo* aInfo,
|
|||
// that our video frame creation code doesn't overflow.
|
||||
nsIntSize displaySize(params.display_width, params.display_height);
|
||||
nsIntSize frameSize(params.width, params.height);
|
||||
if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRect, displaySize)) {
|
||||
if (!IsValidVideoRegion(frameSize, pictureRect, displaySize)) {
|
||||
// Video track's frame sizes will overflow. Ignore the video track.
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -314,7 +314,7 @@ WMFReader::ConfigureVideoFrameGeometry(IMFMediaType* aMediaType)
|
|||
nsIntSize frameSize = nsIntSize(width, height);
|
||||
nsIntSize displaySize = nsIntSize(pictureRegion.width, pictureRegion.height);
|
||||
ScaleDisplayByAspectRatio(displaySize, float(aspectNum) / float(aspectDenom));
|
||||
if (!VideoInfo::ValidateVideoRegion(frameSize, pictureRegion, displaySize)) {
|
||||
if (!IsValidVideoRegion(frameSize, pictureRegion, displaySize)) {
|
||||
// Video track's frame sizes will overflow. Ignore the video track.
|
||||
return E_FAIL;
|
||||
}
|
||||
|
|
|
@ -6781,9 +6781,15 @@ nsDocShell::OnRedirectStateChange(nsIChannel* aOldChannel,
|
|||
// Permission will be checked in the parent process.
|
||||
appCacheChannel->SetChooseApplicationCache(true);
|
||||
} else {
|
||||
appCacheChannel->SetChooseApplicationCache(
|
||||
NS_ShouldCheckAppCache(newURI,
|
||||
mInPrivateBrowsing));
|
||||
nsCOMPtr<nsIScriptSecurityManager> secMan =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
||||
|
||||
if (secMan) {
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
secMan->GetDocShellCodebasePrincipal(newURI, this, getter_AddRefs(principal));
|
||||
appCacheChannel->SetChooseApplicationCache(NS_ShouldCheckAppCache(principal,
|
||||
mInPrivateBrowsing));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9713,8 +9719,15 @@ nsDocShell::DoURILoad(nsIURI * aURI,
|
|||
// Permission will be checked in the parent process
|
||||
appCacheChannel->SetChooseApplicationCache(true);
|
||||
} else {
|
||||
appCacheChannel->SetChooseApplicationCache(
|
||||
NS_ShouldCheckAppCache(aURI, mInPrivateBrowsing));
|
||||
nsCOMPtr<nsIScriptSecurityManager> secMan =
|
||||
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID);
|
||||
|
||||
if (secMan) {
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
secMan->GetDocShellCodebasePrincipal(aURI, this, getter_AddRefs(principal));
|
||||
appCacheChannel->SetChooseApplicationCache(
|
||||
NS_ShouldCheckAppCache(principal, mInPrivateBrowsing));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1575,9 +1575,8 @@ this.DOMApplicationRegistry = {
|
|||
},
|
||||
manifestURL: aApp.manifestURL
|
||||
});
|
||||
let cacheUpdate = aProfileDir
|
||||
? updateSvc.scheduleCustomProfileUpdate(appcacheURI, docURI, aProfileDir)
|
||||
: updateSvc.scheduleAppUpdate(appcacheURI, docURI, aApp.localId, false);
|
||||
let cacheUpdate = updateSvc.scheduleAppUpdate(
|
||||
appcacheURI, docURI, aApp.localId, false, aProfileDir);
|
||||
|
||||
// We save the download details for potential further usage like
|
||||
// cancelling it.
|
||||
|
|
|
@ -205,6 +205,7 @@ static const DOMJSClass Class = {
|
|||
nullptr, /* hasInstance */
|
||||
nullptr, /* construct */
|
||||
%s, /* trace */
|
||||
JS_NULL_CLASS_SPEC,
|
||||
JS_NULL_CLASS_EXT,
|
||||
JS_NULL_OBJECT_OPS
|
||||
},
|
||||
|
|
|
@ -221,6 +221,10 @@ this.DOMIdentity = {
|
|||
return this._serviceContexts.get(this._mmContexts.get(targetMM));
|
||||
},
|
||||
|
||||
hasContextForMM: function(targetMM) {
|
||||
return this._mmContexts.has(targetMM);
|
||||
},
|
||||
|
||||
/*
|
||||
* Delete the RPWatchContext object for a given message manager. Removes the
|
||||
* mapping both from _serviceContexts and _mmContexts.
|
||||
|
@ -344,6 +348,10 @@ this.DOMIdentity = {
|
|||
},
|
||||
|
||||
_childProcessShutdown: function DOMIdentity__childProcessShutdown(targetMM) {
|
||||
if (!this.hasContextForMM(targetMM)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.getContextForMM(targetMM).RP.childProcessShutdown(targetMM);
|
||||
this.deleteContextForMM(targetMM);
|
||||
|
||||
|
|
|
@ -144,3 +144,4 @@ UseOfDOM3LoadMethodWarning=Use of document.load() is deprecated. To upgrade your
|
|||
ShowModalDialogWarning=Use of window.showModalDialog() is deprecated. Use window.open() instead. For more help https://developer.mozilla.org/en-US/docs/Web/API/Window.open
|
||||
# LOCALIZATION NOTE: Do not translate "window._content" or "window.content"
|
||||
Window_ContentWarning=window._content is deprecated. Please use window.content instead.
|
||||
ImplicitMetaViewportTagFallback=No meta-viewport tag found. Please explicitly specify one to prevent unexpected behavioural changes in future versions. For more help https://developer.mozilla.org/en/docs/Mozilla/Mobile/Viewport_meta_tag
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "js/CharacterEncoding.h"
|
||||
#include "js/OldDebugAPI.h"
|
||||
#include "nsJSON.h"
|
||||
#include "nsIXPConnect.h"
|
||||
|
@ -524,8 +525,8 @@ nsJSONListener::OnStopRequest(nsIRequest *aRequest, nsISupports *aContext,
|
|||
|
||||
JS::Rooted<JS::Value> reviver(mCx, JS::NullValue()), value(mCx);
|
||||
|
||||
JS::StableCharPtr chars(reinterpret_cast<const jschar*>(mBufferedChars.Elements()),
|
||||
mBufferedChars.Length());
|
||||
JS::ConstTwoByteChars chars(reinterpret_cast<const jschar*>(mBufferedChars.Elements()),
|
||||
mBufferedChars.Length());
|
||||
bool ok = JS_ParseJSONWithReviver(mCx, chars.get(),
|
||||
uint32_t(mBufferedChars.Length()),
|
||||
reviver, &value);
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
[DEFAULT]
|
||||
# Timeouts on all platforms (bug 932350)
|
||||
skip-if = true
|
||||
support-files =
|
||||
child_ip_address.html
|
||||
file_crossdomainprops_inner.html
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DEFAULT]
|
||||
# Timeouts on OS X (bug 921635)
|
||||
skip-if = os == "mac"
|
||||
# Timeouts on all platforms (bug 932350)
|
||||
skip-if = true
|
||||
support-files =
|
||||
DOMTestCase.js
|
||||
activity-home.css
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DEFAULT]
|
||||
# Timeouts on OS X (bug 921635)
|
||||
skip-if = os == "mac"
|
||||
# Timeouts on all platforms (bug 932350)
|
||||
skip-if = true
|
||||
support-files =
|
||||
DOMTestCase.js
|
||||
exclusions.js
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
[DEFAULT]
|
||||
# Timeouts on OS X (bug 921635)
|
||||
skip-if = os == "mac"
|
||||
# Timeouts on all platforms (bug 932350)
|
||||
skip-if = true
|
||||
support-files =
|
||||
DOMTestCase.js
|
||||
files/anchor.html
|
||||
|
|
|
@ -65,15 +65,29 @@ MacIOSurfaceTextureClientOGL::GetSize() const
|
|||
return gfx::IntSize(mSurface->GetDevicePixelWidth(), mSurface->GetDevicePixelHeight());
|
||||
}
|
||||
|
||||
class MacIOSurfaceTextureClientData : public TextureClientData
|
||||
{
|
||||
public:
|
||||
MacIOSurfaceTextureClientData(MacIOSurface* aSurface)
|
||||
: mSurface(aSurface)
|
||||
{}
|
||||
|
||||
virtual void DeallocateSharedData(ISurfaceAllocator*) MOZ_OVERRIDE
|
||||
{
|
||||
mSurface = nullptr;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<MacIOSurface> mSurface;
|
||||
};
|
||||
|
||||
TextureClientData*
|
||||
MacIOSurfaceTextureClientOGL::DropTextureData()
|
||||
{
|
||||
// MacIOSurface has proper cross-process refcounting so we can just drop
|
||||
// our reference now, and the data will stay alive (at least) until the host
|
||||
// has also been torn down.
|
||||
TextureClientData* data = new MacIOSurfaceTextureClientData(mSurface);
|
||||
mSurface = nullptr;
|
||||
MarkInvalid();
|
||||
return nullptr;
|
||||
return data;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -788,10 +788,9 @@ gfxRect gfx3DMatrix::ProjectRectBounds(const gfxRect& aRect) const
|
|||
gfxPoint points[4];
|
||||
|
||||
points[0] = ProjectPoint(aRect.TopLeft());
|
||||
points[1] = ProjectPoint(gfxPoint(aRect.X() + aRect.Width(), aRect.Y()));
|
||||
points[2] = ProjectPoint(gfxPoint(aRect.X(), aRect.Y() + aRect.Height()));
|
||||
points[3] = ProjectPoint(gfxPoint(aRect.X() + aRect.Width(),
|
||||
aRect.Y() + aRect.Height()));
|
||||
points[1] = ProjectPoint(aRect.TopRight());
|
||||
points[2] = ProjectPoint(aRect.BottomLeft());
|
||||
points[3] = ProjectPoint(aRect.BottomRight());
|
||||
|
||||
gfxFloat min_x, max_x;
|
||||
gfxFloat min_y, max_y;
|
||||
|
@ -809,6 +808,34 @@ gfxRect gfx3DMatrix::ProjectRectBounds(const gfxRect& aRect) const
|
|||
return gfxRect(min_x, min_y, max_x - min_x, max_y - min_y);
|
||||
}
|
||||
|
||||
gfxRect gfx3DMatrix::UntransformBounds(const gfxRect& aRect, const gfxRect& aChildBounds) const
|
||||
{
|
||||
if (Is2D()) {
|
||||
return Inverse().TransformBounds(aRect);
|
||||
}
|
||||
gfxRect bounds = TransformBounds(aChildBounds);
|
||||
|
||||
gfxRect rect = aRect.Intersect(bounds);
|
||||
|
||||
return Inverse().ProjectRectBounds(rect);
|
||||
}
|
||||
|
||||
bool gfx3DMatrix::UntransformPoint(const gfxPoint& aPoint, const gfxRect& aChildBounds, gfxPoint* aOut) const
|
||||
{
|
||||
if (Is2D()) {
|
||||
*aOut = Inverse().Transform(aPoint);
|
||||
return true;
|
||||
}
|
||||
gfxRect bounds = TransformBounds(aChildBounds);
|
||||
|
||||
if (!bounds.Contains(aPoint)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
*aOut = Inverse().ProjectPoint(aPoint);
|
||||
return true;
|
||||
}
|
||||
|
||||
gfxPoint3D gfx3DMatrix::GetNormalVector() const
|
||||
{
|
||||
// Define a plane in transformed space as the transformations
|
||||
|
|
|
@ -250,6 +250,25 @@ public:
|
|||
gfxPoint ProjectPoint(const gfxPoint& aPoint) const;
|
||||
gfxRect ProjectRectBounds(const gfxRect& aRect) const;
|
||||
|
||||
/**
|
||||
* Transforms a point by the inverse of this matrix. In the case of perspective transforms, some screen
|
||||
* points have no equivalent in the untransformed plane (if they exist past the vanishing point). To
|
||||
* avoid this, we need to specify the bounds of the untransformed plane to restrict the search area.
|
||||
*
|
||||
* @param aPoint Point to untransform.
|
||||
* @param aChildBounds Bounds of the untransformed plane.
|
||||
* @param aOut Untransformed point.
|
||||
* @return Returns true if a point was found within a ChildBounds, false otherwise.
|
||||
*/
|
||||
bool UntransformPoint(const gfxPoint& aPoint, const gfxRect& aChildBounds, gfxPoint* aOut) const;
|
||||
|
||||
|
||||
/**
|
||||
* Same as UntransformPoint, but untransforms a rect and returns the bounding rect of the result.
|
||||
* Returns an empty rect if the result doesn't intersect aChildBounds.
|
||||
*/
|
||||
gfxRect UntransformBounds(const gfxRect& aRect, const gfxRect& aChildBounds) const;
|
||||
|
||||
|
||||
/**
|
||||
* Inverts this matrix, if possible. Otherwise, the matrix is left
|
||||
|
|
|
@ -28,11 +28,7 @@ SHLIB.c= $(CC) -dynamiclib -dynamic $(CFLAGS) $(LDFLAGS) $(LD_SOOPTIONS)
|
|||
SHLIB.cc= $(CXX) -dynamiclib -dynamic $(CXXFLAGS) $(LDFLAGS) $(LD_SOOPTIONS)
|
||||
|
||||
## Compiler switches to embed a library name and version information
|
||||
ifeq ($(ENABLE_RPATH),YES)
|
||||
LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name $(libdir)/$(notdir $(MIDDLE_SO_TARGET))
|
||||
else
|
||||
LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name $(notdir $(MIDDLE_SO_TARGET))
|
||||
endif
|
||||
LD_SONAME = -Wl,-compatibility_version -Wl,$(SO_TARGET_VERSION_MAJOR) -Wl,-current_version -Wl,$(SO_TARGET_VERSION) -install_name @executable_path/$(notdir $(MIDDLE_SO_TARGET))
|
||||
|
||||
## Compiler switch to embed a runtime search path
|
||||
LD_RPATH=
|
||||
|
|
|
@ -52,6 +52,7 @@ svn info $1 | grep -v '^Revision: [[:digit:]]\+$' > ${icu_dir}/SVN-INFO
|
|||
|
||||
patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/bug-724533
|
||||
patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/bug-899722-4
|
||||
patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/bug-915735
|
||||
patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/genrb-omitCollationRules.diff
|
||||
patch -d ${icu_dir}/../../ -p1 < ${icu_dir}/../icu-patches/qualify-uinitonce-windows.diff
|
||||
|
||||
|
|
|
@ -123,21 +123,6 @@ class TwoByteChars : public mozilla::Range<jschar>
|
|||
TwoByteChars(const jschar *aChars, size_t aLength) : Base(const_cast<jschar *>(aChars), aLength) {}
|
||||
};
|
||||
|
||||
/*
|
||||
* A non-convertible variant of TwoByteChars that does not refer to characters
|
||||
* inlined inside a JSShortString or a JSInlineString. StableTwoByteChars are
|
||||
* thus safe to hold across a GC.
|
||||
*/
|
||||
class StableTwoByteChars : public mozilla::Range<jschar>
|
||||
{
|
||||
typedef mozilla::Range<jschar> Base;
|
||||
|
||||
public:
|
||||
StableTwoByteChars() : Base() {}
|
||||
StableTwoByteChars(jschar *aChars, size_t aLength) : Base(aChars, aLength) {}
|
||||
StableTwoByteChars(const jschar *aChars, size_t aLength) : Base(const_cast<jschar *>(aChars), aLength) {}
|
||||
};
|
||||
|
||||
/*
|
||||
* A TwoByteChars, but \0 terminated for compatibility with JSFlatString.
|
||||
*/
|
||||
|
@ -157,6 +142,25 @@ class TwoByteCharsZ : public mozilla::RangedPtr<jschar>
|
|||
using Base::operator=;
|
||||
};
|
||||
|
||||
typedef mozilla::RangedPtr<const jschar> ConstCharPtr;
|
||||
|
||||
/*
|
||||
* Like TwoByteChars, but the chars are const.
|
||||
*/
|
||||
class ConstTwoByteChars : public mozilla::RangedPtr<const jschar>
|
||||
{
|
||||
public:
|
||||
ConstTwoByteChars(const ConstTwoByteChars &s) : ConstCharPtr(s) {}
|
||||
ConstTwoByteChars(const mozilla::RangedPtr<const jschar> &s) : ConstCharPtr(s) {}
|
||||
ConstTwoByteChars(const jschar *s, size_t len) : ConstCharPtr(s, len) {}
|
||||
ConstTwoByteChars(const jschar *pos, const jschar *start, size_t len)
|
||||
: ConstCharPtr(pos, start, len)
|
||||
{}
|
||||
|
||||
using ConstCharPtr::operator=;
|
||||
};
|
||||
|
||||
|
||||
/*
|
||||
* Convert a 2-byte character sequence to "ISO-Latin-1". This works by
|
||||
* truncating each 2-byte pair in the sequence to a 1-byte pair. If the source
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
*/
|
||||
|
||||
class JSFreeOp;
|
||||
struct JSFunctionSpec;
|
||||
|
||||
namespace js {
|
||||
|
||||
|
@ -416,6 +417,23 @@ struct ClassSizeMeasurement
|
|||
JS_CLASS_MEMBERS;
|
||||
};
|
||||
|
||||
// Callback for the creation of constructor and prototype objects.
|
||||
typedef JSObject *(*ClassObjectCreationOp)(JSContext *cx, JSProtoKey key);
|
||||
|
||||
// Callback for custom post-processing after class initialization via ClassSpec.
|
||||
typedef bool (*FinishClassInitOp)(JSContext *cx, JS::HandleObject ctor,
|
||||
JS::HandleObject proto);
|
||||
|
||||
struct ClassSpec
|
||||
{
|
||||
ClassObjectCreationOp createConstructor;
|
||||
ClassObjectCreationOp createPrototype;
|
||||
const JSFunctionSpec *constructorFunctions;
|
||||
const JSFunctionSpec *prototypeFunctions;
|
||||
FinishClassInitOp finishInit;
|
||||
bool defined() const { return !!createConstructor; }
|
||||
};
|
||||
|
||||
struct ClassExtension
|
||||
{
|
||||
JSObjectOp outerObject;
|
||||
|
@ -442,6 +460,7 @@ struct ClassExtension
|
|||
JSWeakmapKeyDelegateOp weakmapKeyDelegateOp;
|
||||
};
|
||||
|
||||
#define JS_NULL_CLASS_SPEC {nullptr,nullptr,nullptr,nullptr,nullptr}
|
||||
#define JS_NULL_CLASS_EXT {nullptr,nullptr,nullptr,false,nullptr}
|
||||
|
||||
struct ObjectOps
|
||||
|
@ -592,9 +611,11 @@ namespace js {
|
|||
struct Class
|
||||
{
|
||||
JS_CLASS_MEMBERS;
|
||||
ClassSpec spec;
|
||||
ClassExtension ext;
|
||||
ObjectOps ops;
|
||||
uint8_t pad[sizeof(JSClass) - sizeof(ClassSizeMeasurement) -
|
||||
sizeof(ClassSpec) -
|
||||
sizeof(ClassExtension) - sizeof(ObjectOps)];
|
||||
|
||||
/* Class is not native and its map is not a scope. */
|
||||
|
|
|
@ -832,12 +832,6 @@ class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
|
|||
Rooted(const Rooted &) MOZ_DELETE;
|
||||
};
|
||||
|
||||
#if !(defined(JSGC_ROOT_ANALYSIS) || defined(JSGC_USE_EXACT_ROOTING))
|
||||
// Defined in vm/String.h.
|
||||
template <>
|
||||
class Rooted<JSStableString *>;
|
||||
#endif
|
||||
|
||||
} /* namespace JS */
|
||||
|
||||
namespace js {
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
namespace JS {
|
||||
|
||||
class Latin1CharsZ;
|
||||
class StableCharPtr;
|
||||
class ConstTwoByteChars;
|
||||
class TwoByteChars;
|
||||
|
||||
class AutoFunctionVector;
|
||||
|
@ -62,7 +62,7 @@ using JS::UndefinedValue;
|
|||
using JS::IsPoisonedPtr;
|
||||
|
||||
using JS::Latin1CharsZ;
|
||||
using JS::StableCharPtr;
|
||||
using JS::ConstTwoByteChars;
|
||||
using JS::TwoByteChars;
|
||||
|
||||
using JS::AutoFunctionVector;
|
||||
|
|
|
@ -148,7 +148,7 @@ enum EvalJSONResult {
|
|||
|
||||
static EvalJSONResult
|
||||
TryEvalJSON(JSContext *cx, JSScript *callerScript,
|
||||
StableCharPtr chars, size_t length, MutableHandleValue rval)
|
||||
ConstTwoByteChars chars, size_t length, MutableHandleValue rval)
|
||||
{
|
||||
// If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
|
||||
// Try the JSON parser first because it's much faster. If the eval string
|
||||
|
@ -280,12 +280,12 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
|
|||
thisv = ObjectValue(*thisobj);
|
||||
}
|
||||
|
||||
Rooted<JSStableString*> stableStr(cx, str->ensureStable(cx));
|
||||
if (!stableStr)
|
||||
Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx));
|
||||
if (!flatStr)
|
||||
return false;
|
||||
|
||||
StableCharPtr chars = stableStr->chars();
|
||||
size_t length = stableStr->length();
|
||||
size_t length = flatStr->length();
|
||||
ConstTwoByteChars chars(flatStr->chars(), length);
|
||||
|
||||
JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
|
||||
|
||||
|
@ -297,7 +297,7 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
|
|||
EvalScriptGuard esg(cx);
|
||||
|
||||
if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame())
|
||||
esg.lookupInEvalCache(stableStr, callerScript, pc);
|
||||
esg.lookupInEvalCache(flatStr, callerScript, pc);
|
||||
|
||||
if (!esg.foundScript()) {
|
||||
unsigned lineno;
|
||||
|
@ -316,7 +316,7 @@ EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFrame
|
|||
.setOriginPrincipals(originPrincipals);
|
||||
JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
|
||||
scopeobj, callerScript, options,
|
||||
chars.get(), length, stableStr, staticLevel);
|
||||
chars.get(), length, flatStr, staticLevel);
|
||||
if (!compiled)
|
||||
return false;
|
||||
|
||||
|
@ -347,12 +347,12 @@ js::DirectEvalStringFromIon(JSContext *cx,
|
|||
|
||||
unsigned staticLevel = callerScript->staticLevel() + 1;
|
||||
|
||||
Rooted<JSStableString*> stableStr(cx, str->ensureStable(cx));
|
||||
if (!stableStr)
|
||||
Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx));
|
||||
if (!flatStr)
|
||||
return false;
|
||||
|
||||
StableCharPtr chars = stableStr->chars();
|
||||
size_t length = stableStr->length();
|
||||
size_t length = flatStr->length();
|
||||
ConstTwoByteChars chars(flatStr->chars(), length);
|
||||
|
||||
EvalJSONResult ejr = TryEvalJSON(cx, callerScript, chars, length, vp);
|
||||
if (ejr != EvalJSON_NotJSON)
|
||||
|
@ -363,7 +363,7 @@ js::DirectEvalStringFromIon(JSContext *cx,
|
|||
// Ion will not perform cross compartment direct eval calls.
|
||||
JSPrincipals *principals = cx->compartment()->principals;
|
||||
|
||||
esg.lookupInEvalCache(stableStr, callerScript, pc);
|
||||
esg.lookupInEvalCache(flatStr, callerScript, pc);
|
||||
|
||||
if (!esg.foundScript()) {
|
||||
unsigned lineno;
|
||||
|
@ -381,7 +381,7 @@ js::DirectEvalStringFromIon(JSContext *cx,
|
|||
.setOriginPrincipals(originPrincipals);
|
||||
JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(),
|
||||
scopeobj, callerScript, options,
|
||||
chars.get(), length, stableStr, staticLevel);
|
||||
chars.get(), length, flatStr, staticLevel);
|
||||
if (!compiled)
|
||||
return false;
|
||||
|
||||
|
|
|
@ -1313,6 +1313,7 @@ const Class CloneBufferObject::class_ = {
|
|||
nullptr, /* hasInstance */
|
||||
nullptr, /* construct */
|
||||
nullptr, /* trace */
|
||||
JS_NULL_CLASS_SPEC,
|
||||
JS_NULL_CLASS_EXT,
|
||||
JS_NULL_OBJECT_OPS
|
||||
};
|
||||
|
|
|
@ -2271,6 +2271,7 @@ const Class TypedObject::class_ = {
|
|||
nullptr, /* construct */
|
||||
nullptr, /* hasInstance */
|
||||
TypedDatum::obj_trace,
|
||||
JS_NULL_CLASS_SPEC,
|
||||
JS_NULL_CLASS_EXT,
|
||||
{
|
||||
TypedDatum::obj_lookupGeneric,
|
||||
|
@ -2438,6 +2439,7 @@ const Class TypedHandle::class_ = {
|
|||
nullptr, /* construct */
|
||||
nullptr, /* hasInstance */
|
||||
TypedDatum::obj_trace,
|
||||
JS_NULL_CLASS_SPEC,
|
||||
JS_NULL_CLASS_EXT,
|
||||
{
|
||||
TypedDatum::obj_lookupGeneric,
|
||||
|
|
|
@ -4820,10 +4820,7 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsOb
|
|||
return false;
|
||||
|
||||
RootedObject fieldType(cx, nullptr);
|
||||
JSFlatString* flat = ExtractStructField(cx, item, fieldType.address());
|
||||
if (!flat)
|
||||
return false;
|
||||
Rooted<JSStableString*> name(cx, flat->ensureStable(cx));
|
||||
Rooted<JSFlatString*> name(cx, ExtractStructField(cx, item, fieldType.address()));
|
||||
if (!name)
|
||||
return false;
|
||||
fieldRoots[i] = JS::ObjectValue(*fieldType);
|
||||
|
@ -4837,7 +4834,7 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj_, JSObject* fieldsOb
|
|||
|
||||
// Add the field to the StructType's 'prototype' property.
|
||||
if (!JS_DefineUCProperty(cx, prototype,
|
||||
name->chars().get(), name->length(), JSVAL_VOID,
|
||||
name->chars(), name->length(), JSVAL_VOID,
|
||||
StructType::FieldGetter, StructType::FieldSetter,
|
||||
JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_PERMANENT))
|
||||
return false;
|
||||
|
|
|
@ -96,7 +96,7 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter *parent,
|
|||
evalCaller(evalCaller),
|
||||
topStmt(nullptr),
|
||||
topScopeStmt(nullptr),
|
||||
blockChain(sc->context),
|
||||
staticScope(sc->context),
|
||||
atomIndices(sc->context),
|
||||
firstLine(lineNum),
|
||||
stackDepth(0), maxStackDepth(0),
|
||||
|
@ -493,7 +493,7 @@ class NonLocalExitScope {
|
|||
savedScopeIndex(bce->blockScopeList.length()),
|
||||
savedDepth(bce->stackDepth),
|
||||
openScopeIndex(UINT32_MAX) {
|
||||
if (bce->blockChain) {
|
||||
if (bce->staticScope) {
|
||||
StmtInfoBCE *stmt = bce->topStmt;
|
||||
while (1) {
|
||||
JS_ASSERT(stmt);
|
||||
|
@ -578,8 +578,7 @@ NonLocalExitScope::prepareForNonLocalJump(StmtInfoBCE *toStmt)
|
|||
}
|
||||
|
||||
if (stmt->isBlockScope) {
|
||||
JS_ASSERT(stmt->blockObj);
|
||||
StaticBlockObject &blockObj = *stmt->blockObj;
|
||||
StaticBlockObject &blockObj = stmt->staticBlock();
|
||||
if (!popScopeForNonLocalExit(blockObj, stmt->blockScopeIndex))
|
||||
return false;
|
||||
npops += blockObj.slotCount();
|
||||
|
@ -646,8 +645,8 @@ PushStatementBCE(BytecodeEmitter *bce, StmtInfoBCE *stmt, StmtType type, ptrdiff
|
|||
static JSObject *
|
||||
EnclosingStaticScope(BytecodeEmitter *bce)
|
||||
{
|
||||
if (bce->blockChain)
|
||||
return bce->blockChain;
|
||||
if (bce->staticScope)
|
||||
return bce->staticScope;
|
||||
|
||||
if (!bce->sc->isFunctionBox()) {
|
||||
JS_ASSERT(!bce->parent);
|
||||
|
@ -744,7 +743,7 @@ EmitInternedObjectOp(ExclusiveContext *cx, uint32_t index, JSOp op, BytecodeEmit
|
|||
// An element in the "block scope array" specifies the PC range, and links to a
|
||||
// StaticBlockObject in the object list of the script. That block is linked to
|
||||
// the previous block in the scope, if any. The static block chain at any
|
||||
// pre-retire PC can be retrieved using JSScript::getBlockScope(jsbytecode *pc).
|
||||
// pre-retire PC can be retrieved using JSScript::getStaticScope(jsbytecode *pc).
|
||||
//
|
||||
// When PUSHBLOCKSCOPE is executed, it assumes that the block's locals are
|
||||
// already on the stack. Initial values of "aliased" locals are copied from the
|
||||
|
@ -783,9 +782,9 @@ EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, O
|
|||
unsigned extraSlots)
|
||||
{
|
||||
uint32_t parent = BlockScopeNote::NoBlockScopeIndex;
|
||||
if (bce->blockChain) {
|
||||
if (bce->staticScope) {
|
||||
StmtInfoBCE *stmt = bce->topScopeStmt;
|
||||
for (; stmt->blockObj != bce->blockChain; stmt = stmt->down) {}
|
||||
for (; stmt->staticScope != bce->staticScope; stmt = stmt->down) {}
|
||||
parent = stmt->blockScopeIndex;
|
||||
}
|
||||
|
||||
|
@ -810,7 +809,7 @@ EnterBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce, StmtInfoBCE *stmt, O
|
|||
return false;
|
||||
|
||||
PushStatementBCE(bce, stmt, STMT_BLOCK, bce->offset());
|
||||
blockObj->initEnclosingStaticScope(EnclosingStaticScope(bce));
|
||||
blockObj->initEnclosingNestedScope(EnclosingStaticScope(bce));
|
||||
FinishPushBlockScope(bce, stmt, *blockObj);
|
||||
|
||||
JS_ASSERT(stmt->isBlockScope);
|
||||
|
@ -846,12 +845,13 @@ LeaveBlockScope(ExclusiveContext *cx, BytecodeEmitter *bce)
|
|||
JS_ASSERT(bce->blockScopeList.list[blockScopeIndex].length == 0);
|
||||
uint32_t blockObjIndex = bce->blockScopeList.list[blockScopeIndex].index;
|
||||
ObjectBox *blockObjBox = bce->objectList.find(blockObjIndex);
|
||||
StaticBlockObject *blockObj = &blockObjBox->object->as<StaticBlockObject>();
|
||||
JS_ASSERT(stmt->blockObj == blockObj);
|
||||
JS_ASSERT(blockObj == bce->blockChain);
|
||||
NestedScopeObject *staticScope = &blockObjBox->object->as<NestedScopeObject>();
|
||||
JS_ASSERT(stmt->staticScope == staticScope);
|
||||
JS_ASSERT(staticScope == bce->staticScope);
|
||||
#endif
|
||||
|
||||
bool blockOnChain = bce->blockChain->needsClone();
|
||||
JS_ASSERT(bce->staticScope->is<StaticBlockObject>());
|
||||
bool blockOnChain = bce->staticScope->as<StaticBlockObject>().needsClone();
|
||||
|
||||
if (!PopStatementBCE(cx, bce))
|
||||
return false;
|
||||
|
@ -998,16 +998,18 @@ EmitAliasedVarOp(ExclusiveContext *cx, JSOp op, ScopeCoordinate sc, BytecodeEmit
|
|||
return true;
|
||||
}
|
||||
|
||||
// Compute the number of nested scope objects that will actually be on the scope
|
||||
// chain at runtime, given the BCE's current staticScope.
|
||||
static unsigned
|
||||
ClonedBlockDepth(BytecodeEmitter *bce)
|
||||
DynamicNestedScopeDepth(BytecodeEmitter *bce)
|
||||
{
|
||||
unsigned clonedBlockDepth = 0;
|
||||
for (StaticBlockObject *b = bce->blockChain; b; b = b->enclosingBlock()) {
|
||||
if (b->needsClone())
|
||||
++clonedBlockDepth;
|
||||
unsigned depth = 0;
|
||||
for (NestedScopeObject *b = bce->staticScope; b; b = b->enclosingNestedScope()) {
|
||||
if (!b->is<StaticBlockObject>() || b->as<StaticBlockObject>().needsClone())
|
||||
++depth;
|
||||
}
|
||||
|
||||
return clonedBlockDepth;
|
||||
return depth;
|
||||
}
|
||||
|
||||
static bool
|
||||
|
@ -1078,7 +1080,7 @@ EmitAliasedVarOp(ExclusiveContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *
|
|||
* enclosing function scope of the definition being accessed.
|
||||
*/
|
||||
for (unsigned i = pn->pn_cookie.level(); i; i--) {
|
||||
skippedScopes += ClonedBlockDepth(bceOfDef);
|
||||
skippedScopes += DynamicNestedScopeDepth(bceOfDef);
|
||||
FunctionBox *funbox = bceOfDef->sc->asFunctionBox();
|
||||
if (funbox->isHeavyweight()) {
|
||||
skippedScopes++;
|
||||
|
@ -1093,30 +1095,33 @@ EmitAliasedVarOp(ExclusiveContext *cx, JSOp op, ParseNode *pn, BytecodeEmitter *
|
|||
}
|
||||
|
||||
/*
|
||||
* The final part of the skippedScopes computation depends on the type of variable. An arg or
|
||||
* local variable is at the outer scope of a function and so includes the full
|
||||
* ClonedBlockDepth. A let/catch-binding requires a search of the block chain to see how many
|
||||
* (dynamic) block objects to skip.
|
||||
* The final part of the skippedScopes computation depends on the type of
|
||||
* variable. An arg or local variable is at the outer scope of a function
|
||||
* and so includes the full DynamicNestedScopeDepth. A let/catch-binding
|
||||
* requires a search of the block chain to see how many (dynamic) block
|
||||
* objects to skip.
|
||||
*/
|
||||
ScopeCoordinate sc;
|
||||
if (IsArgOp(pn->getOp())) {
|
||||
if (!AssignHops(bce, pn, skippedScopes + ClonedBlockDepth(bceOfDef), &sc))
|
||||
if (!AssignHops(bce, pn, skippedScopes + DynamicNestedScopeDepth(bceOfDef), &sc))
|
||||
return false;
|
||||
JS_ALWAYS_TRUE(LookupAliasedNameSlot(bceOfDef->script, pn->name(), &sc));
|
||||
} else {
|
||||
JS_ASSERT(IsLocalOp(pn->getOp()) || pn->isKind(PNK_FUNCTION));
|
||||
uint32_t local = pn->pn_cookie.slot();
|
||||
if (local < bceOfDef->script->bindings.numVars()) {
|
||||
if (!AssignHops(bce, pn, skippedScopes + ClonedBlockDepth(bceOfDef), &sc))
|
||||
if (!AssignHops(bce, pn, skippedScopes + DynamicNestedScopeDepth(bceOfDef), &sc))
|
||||
return false;
|
||||
JS_ALWAYS_TRUE(LookupAliasedNameSlot(bceOfDef->script, pn->name(), &sc));
|
||||
} else {
|
||||
uint32_t depth = local - bceOfDef->script->bindings.numVars();
|
||||
Rooted<StaticBlockObject*> b(cx, bceOfDef->blockChain);
|
||||
JS_ASSERT(bceOfDef->staticScope->is<StaticBlockObject>());
|
||||
Rooted<StaticBlockObject*> b(cx, &bceOfDef->staticScope->as<StaticBlockObject>());
|
||||
while (!b->containsVarAtDepth(depth)) {
|
||||
if (b->needsClone())
|
||||
skippedScopes++;
|
||||
b = b->enclosingBlock();
|
||||
JS_ASSERT(b);
|
||||
}
|
||||
if (!AssignHops(bce, pn, skippedScopes, &sc))
|
||||
return false;
|
||||
|
@ -2891,8 +2896,6 @@ enum VarEmitOption
|
|||
InitializeVars = 2
|
||||
};
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
|
||||
typedef bool
|
||||
(*DestructuringDeclEmitter)(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp prologOp, ParseNode *pn);
|
||||
|
||||
|
@ -3340,8 +3343,6 @@ MaybeEmitLetGroupDecl(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn,
|
|||
return true;
|
||||
}
|
||||
|
||||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
||||
static bool
|
||||
EmitVariables(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmitOption emitOption,
|
||||
bool isLet = false)
|
||||
|
@ -3355,7 +3356,6 @@ EmitVariables(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmit
|
|||
|
||||
ParseNode *pn3;
|
||||
if (!pn2->isKind(PNK_NAME)) {
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
if (pn2->isKind(PNK_ARRAY) || pn2->isKind(PNK_OBJECT)) {
|
||||
/*
|
||||
* Emit variable binding ops, but not destructuring ops. The
|
||||
|
@ -3371,7 +3371,6 @@ EmitVariables(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmit
|
|||
return false;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* A destructuring initialiser assignment preceded by var will
|
||||
|
@ -3388,18 +3387,12 @@ EmitVariables(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmit
|
|||
* function f(){} precedes the var, detect simple name assignment
|
||||
* here and initialize the name.
|
||||
*/
|
||||
#if !JS_HAS_DESTRUCTURING
|
||||
JS_ASSERT(pn2->pn_left->isKind(PNK_NAME));
|
||||
#else
|
||||
if (pn2->pn_left->isKind(PNK_NAME))
|
||||
#endif
|
||||
{
|
||||
if (pn2->pn_left->isKind(PNK_NAME)) {
|
||||
pn3 = pn2->pn_right;
|
||||
pn2 = pn2->pn_left;
|
||||
goto do_name;
|
||||
}
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
ptrdiff_t stackDepthBefore = bce->stackDepth;
|
||||
JSOp op = JSOP_POP;
|
||||
if (pn->pn_count == 1) {
|
||||
|
@ -3447,7 +3440,6 @@ EmitVariables(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmit
|
|||
break;
|
||||
}
|
||||
goto emit_note_pop;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -3514,9 +3506,7 @@ EmitVariables(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, VarEmit
|
|||
return false;
|
||||
}
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
emit_note_pop:
|
||||
#endif
|
||||
if (!next)
|
||||
break;
|
||||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
|
@ -3578,11 +3568,9 @@ EmitAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp
|
|||
return false;
|
||||
offset += 2;
|
||||
break;
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case PNK_ARRAY:
|
||||
case PNK_OBJECT:
|
||||
break;
|
||||
#endif
|
||||
case PNK_CALL:
|
||||
JS_ASSERT(lhs->pn_xflags & PNX_SETCALL);
|
||||
if (!EmitTree(cx, bce, lhs))
|
||||
|
@ -3724,13 +3712,11 @@ EmitAssignment(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *lhs, JSOp
|
|||
if (Emit1(cx, bce, JSOP_SETELEM) < 0)
|
||||
return false;
|
||||
break;
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case PNK_ARRAY:
|
||||
case PNK_OBJECT:
|
||||
if (!EmitDestructuringOps(cx, bce, lhs))
|
||||
return false;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
JS_ASSERT(0);
|
||||
}
|
||||
|
@ -3902,7 +3888,6 @@ EmitCatch(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
|
||||
ParseNode *pn2 = pn->pn_kid1;
|
||||
switch (pn2->getKind()) {
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case PNK_ARRAY:
|
||||
case PNK_OBJECT:
|
||||
if (!EmitDestructuringOps(cx, bce, pn2))
|
||||
|
@ -3910,7 +3895,6 @@ EmitCatch(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
if (Emit1(cx, bce, JSOP_POP) < 0)
|
||||
return false;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case PNK_NAME:
|
||||
/* Inline and specialize BindNameToSlot for pn2. */
|
||||
|
@ -4679,13 +4663,11 @@ EmitNormalFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff
|
|||
op = JSOP_NOP;
|
||||
} else {
|
||||
bce->emittingForInit = true;
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
if (pn3->isKind(PNK_ASSIGN)) {
|
||||
JS_ASSERT(pn3->isOp(JSOP_NOP));
|
||||
if (!MaybeEmitGroupAssignment(cx, bce, op, pn3, GroupIsNotDecl, &op))
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (op == JSOP_POP) {
|
||||
if (!UpdateSourceCoordNotes(cx, bce, pn3->pn_pos.begin))
|
||||
return false;
|
||||
|
@ -4755,13 +4737,11 @@ EmitNormalFor(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn, ptrdiff
|
|||
if (!UpdateSourceCoordNotes(cx, bce, pn3->pn_pos.begin))
|
||||
return false;
|
||||
op = JSOP_POP;
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
if (pn3->isKind(PNK_ASSIGN)) {
|
||||
JS_ASSERT(pn3->isOp(JSOP_NOP));
|
||||
if (!MaybeEmitGroupAssignment(cx, bce, op, pn3, GroupIsNotDecl, &op))
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (op == JSOP_POP && !EmitTree(cx, bce, pn3))
|
||||
return false;
|
||||
|
||||
|
@ -4865,7 +4845,7 @@ EmitFunc(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
|
||||
if (fun->isInterpretedLazy()) {
|
||||
if (!fun->lazyScript()->sourceObject()) {
|
||||
JSObject *scope = bce->blockChain;
|
||||
JSObject *scope = bce->staticScope;
|
||||
if (!scope && bce->sc->isFunctionBox())
|
||||
scope = bce->sc->asFunctionBox()->function();
|
||||
JSObject *source = bce->script->sourceObject();
|
||||
|
@ -5401,14 +5381,12 @@ EmitStatement(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
|||
if (useful) {
|
||||
JSOp op = wantval ? JSOP_SETRVAL : JSOP_POP;
|
||||
JS_ASSERT_IF(pn2->isKind(PNK_ASSIGN), pn2->isOp(JSOP_NOP));
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
if (!wantval &&
|
||||
pn2->isKind(PNK_ASSIGN) &&
|
||||
!MaybeEmitGroupAssignment(cx, bce, op, pn2, GroupIsNotDecl, &op))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
if (op != JSOP_NOP) {
|
||||
if (!EmitTree(cx, bce, pn2))
|
||||
return false;
|
||||
|
@ -5885,12 +5863,10 @@ EmitConditionalExpression(ExclusiveContext *cx, BytecodeEmitter *bce, Conditiona
|
|||
MOZ_NEVER_INLINE static bool
|
||||
EmitObject(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn)
|
||||
{
|
||||
#if JS_HAS_DESTRUCTURING_SHORTHAND
|
||||
if (pn->pn_xflags & PNX_DESTRUCT) {
|
||||
bce->reportError(pn, JSMSG_BAD_OBJECT_INIT);
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!(pn->pn_xflags & PNX_NONCONST) && pn->pn_head && bce->checkSingletonContext())
|
||||
return EmitSingletonInitialiser(cx, bce, pn);
|
||||
|
|
|
@ -107,8 +107,8 @@ struct BytecodeEmitter
|
|||
|
||||
StmtInfoBCE *topStmt; /* top of statement info stack */
|
||||
StmtInfoBCE *topScopeStmt; /* top lexical scope statement */
|
||||
Rooted<StaticBlockObject *> blockChain;
|
||||
/* compile time block scope chain */
|
||||
Rooted<NestedScopeObject *> staticScope;
|
||||
/* compile time scope chain */
|
||||
|
||||
OwnedAtomIndexMapPtr atomIndices; /* literals indexed for mapping */
|
||||
unsigned firstLine; /* first line, for JSScript::initFromEmitter */
|
||||
|
|
|
@ -326,8 +326,6 @@ Definition::kindString(Kind kind)
|
|||
namespace js {
|
||||
namespace frontend {
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
|
||||
/*
|
||||
* This function assumes the cloned tree is for use in the same statement and
|
||||
* binding context as the original tree.
|
||||
|
@ -423,8 +421,6 @@ Parser<FullParseHandler>::cloneParseTree(ParseNode *opn)
|
|||
return pn;
|
||||
}
|
||||
|
||||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
||||
/*
|
||||
* Used by Parser::forStatement and comprehensionTail to clone the TARGET in
|
||||
* for (var/const/let TARGET in EXPR)
|
||||
|
@ -447,7 +443,6 @@ Parser<FullParseHandler>::cloneLeftHandSide(ParseNode *opn)
|
|||
pn->setDefn(opn->isDefn());
|
||||
pn->setUsed(opn->isUsed());
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
if (opn->isArity(PN_LIST)) {
|
||||
JS_ASSERT(opn->isKind(PNK_ARRAY) || opn->isKind(PNK_OBJECT));
|
||||
pn->makeEmpty();
|
||||
|
@ -479,7 +474,6 @@ Parser<FullParseHandler>::cloneLeftHandSide(ParseNode *opn)
|
|||
pn->pn_xflags = opn->pn_xflags;
|
||||
return pn;
|
||||
}
|
||||
#endif
|
||||
|
||||
JS_ASSERT(opn->isArity(PN_NAME));
|
||||
JS_ASSERT(opn->isKind(PNK_NAME));
|
||||
|
|
|
@ -49,6 +49,8 @@ namespace frontend {
|
|||
|
||||
typedef Rooted<StaticBlockObject*> RootedStaticBlockObject;
|
||||
typedef Handle<StaticBlockObject*> HandleStaticBlockObject;
|
||||
typedef Rooted<NestedScopeObject*> RootedNestedScopeObject;
|
||||
typedef Handle<NestedScopeObject*> HandleNestedScopeObject;
|
||||
|
||||
|
||||
/*
|
||||
|
@ -1505,7 +1507,6 @@ Parser<ParseHandler>::defineArg(Node funcpn, HandlePropertyName name,
|
|||
return pc->define(tokenStream, name, argpn, Definition::ARG);
|
||||
}
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
template <typename ParseHandler>
|
||||
/* static */ bool
|
||||
Parser<ParseHandler>::bindDestructuringArg(BindData<ParseHandler> *data,
|
||||
|
@ -1524,7 +1525,6 @@ Parser<ParseHandler>::bindDestructuringArg(BindData<ParseHandler> *data,
|
|||
|
||||
return pc->define(parser->tokenStream, name, data->pn, Definition::VAR);
|
||||
}
|
||||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
||||
template <typename ParseHandler>
|
||||
bool
|
||||
|
@ -1558,9 +1558,7 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
|
|||
if (parenFreeArrow || !tokenStream.matchToken(TOK_RP)) {
|
||||
bool hasDefaults = false;
|
||||
Node duplicatedArg = null();
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
Node list = null();
|
||||
#endif
|
||||
|
||||
do {
|
||||
if (*hasRest) {
|
||||
|
@ -1571,7 +1569,6 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
|
|||
TokenKind tt = tokenStream.getToken();
|
||||
JS_ASSERT_IF(parenFreeArrow, tt == TOK_NAME);
|
||||
switch (tt) {
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case TOK_LB:
|
||||
case TOK_LC:
|
||||
{
|
||||
|
@ -1628,7 +1625,6 @@ Parser<ParseHandler>::functionArguments(FunctionSyntaxKind kind, Node *listp, No
|
|||
}
|
||||
break;
|
||||
}
|
||||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
||||
case TOK_YIELD:
|
||||
if (!checkYieldNameValidity())
|
||||
|
@ -2017,7 +2013,6 @@ Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *f
|
|||
{
|
||||
pn->pn_pos.end = pos().end;
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
/*
|
||||
* If there were destructuring formal parameters, prepend the initializing
|
||||
* comma expression that we synthesized to body. If the body is a return
|
||||
|
@ -2050,7 +2045,6 @@ Parser<FullParseHandler>::finishFunctionDefinition(ParseNode *pn, FunctionBox *f
|
|||
++body->pn_count;
|
||||
body->pn_xflags |= PNX_DESTRUCT;
|
||||
}
|
||||
#endif
|
||||
|
||||
JS_ASSERT(pn->pn_funbox == funbox);
|
||||
pn->pn_body->append(body);
|
||||
|
@ -2676,7 +2670,7 @@ Parser<ParseHandler>::reportRedeclaration(Node pn, bool isConst, JSAtom *atom)
|
|||
* must already be in such a scope.
|
||||
*
|
||||
* Throw a SyntaxError if 'atom' is an invalid name. Otherwise create a
|
||||
* property for the new variable on the block object, pc->blockChain;
|
||||
* property for the new variable on the block object, pc->staticScope;
|
||||
* populate data->pn->pn_{op,cookie,defn,dflags}; and stash a pointer to
|
||||
* data->pn in a slot of the block object.
|
||||
*/
|
||||
|
@ -2779,15 +2773,18 @@ template <typename ParseHandler>
|
|||
static void
|
||||
PopStatementPC(TokenStream &ts, ParseContext<ParseHandler> *pc)
|
||||
{
|
||||
RootedStaticBlockObject blockObj(ts.context(), pc->topStmt->blockObj);
|
||||
JS_ASSERT(!!blockObj == (pc->topStmt->isBlockScope));
|
||||
RootedNestedScopeObject scopeObj(ts.context(), pc->topStmt->staticScope);
|
||||
JS_ASSERT(!!scopeObj == (pc->topStmt->isBlockScope));
|
||||
|
||||
FinishPopStatement(pc);
|
||||
|
||||
if (blockObj) {
|
||||
JS_ASSERT(!blockObj->inDictionaryMode());
|
||||
ForEachLetDef(ts, pc, blockObj, PopLetDecl<ParseHandler>());
|
||||
blockObj->resetPrevBlockChainFromParser();
|
||||
if (scopeObj) {
|
||||
if (scopeObj->is<StaticBlockObject>()) {
|
||||
RootedStaticBlockObject blockObj(ts.context(), &scopeObj->as<StaticBlockObject>());
|
||||
JS_ASSERT(!blockObj->inDictionaryMode());
|
||||
ForEachLetDef(ts, pc, blockObj, PopLetDecl<ParseHandler>());
|
||||
}
|
||||
scopeObj->resetEnclosingNestedScopeFromParser();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2820,7 +2817,7 @@ LexicalLookup(ContextT *ct, HandleAtom atom, int *slotp, typename ContextT::Stmt
|
|||
if (!stmt->isBlockScope)
|
||||
continue;
|
||||
|
||||
StaticBlockObject &blockObj = *stmt->blockObj;
|
||||
StaticBlockObject &blockObj = stmt->staticBlock();
|
||||
Shape *shape = blockObj.nativeLookup(ct->sc->context, id);
|
||||
if (shape) {
|
||||
JS_ASSERT(shape->hasShortID());
|
||||
|
@ -2990,8 +2987,6 @@ Parser<ParseHandler>::noteNameUse(HandlePropertyName name, Node pn)
|
|||
return true;
|
||||
}
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
|
||||
template <>
|
||||
bool
|
||||
Parser<FullParseHandler>::bindDestructuringVar(BindData<FullParseHandler> *data, ParseNode *pn)
|
||||
|
@ -3189,8 +3184,6 @@ Parser<ParseHandler>::destructuringExpr(BindData<ParseHandler> *data, TokenKind
|
|||
return pn;
|
||||
}
|
||||
|
||||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
||||
template <typename ParseHandler>
|
||||
typename ParseHandler::Node
|
||||
Parser<ParseHandler>::pushLexicalScope(HandleStaticBlockObject blockObj, StmtInfoPC *stmt)
|
||||
|
@ -3202,7 +3195,7 @@ Parser<ParseHandler>::pushLexicalScope(HandleStaticBlockObject blockObj, StmtInf
|
|||
return null();
|
||||
|
||||
PushStatementPC(pc, stmt, STMT_BLOCK);
|
||||
blockObj->initPrevBlockChainFromParser(pc->blockChain);
|
||||
blockObj->initEnclosingNestedScopeFromParser(pc->staticScope);
|
||||
FinishPushBlockScope(pc, stmt, *blockObj.get());
|
||||
|
||||
Node pn = handler.newLexicalScope(blockbox);
|
||||
|
@ -3461,7 +3454,6 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
|
|||
first = false;
|
||||
|
||||
TokenKind tt = tokenStream.getToken();
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
if (tt == TOK_LB || tt == TOK_LC) {
|
||||
if (psimple)
|
||||
*psimple = false;
|
||||
|
@ -3493,7 +3485,6 @@ Parser<ParseHandler>::variables(ParseNodeKind kind, bool *psimple,
|
|||
handler.addList(pn, pn2);
|
||||
continue;
|
||||
}
|
||||
#endif /* JS_HAS_DESTRUCTURING */
|
||||
|
||||
if (tt != TOK_NAME) {
|
||||
if (tt == TOK_YIELD) {
|
||||
|
@ -3560,7 +3551,7 @@ Parser<FullParseHandler>::letDeclaration()
|
|||
}
|
||||
|
||||
if (stmt && stmt->isBlockScope) {
|
||||
JS_ASSERT(pc->blockChain == stmt->blockObj);
|
||||
JS_ASSERT(pc->staticScope == stmt->staticScope);
|
||||
} else {
|
||||
if (pc->atBodyLevel()) {
|
||||
/*
|
||||
|
@ -3606,9 +3597,9 @@ Parser<FullParseHandler>::letDeclaration()
|
|||
stmt->downScope = pc->topScopeStmt;
|
||||
pc->topScopeStmt = stmt;
|
||||
|
||||
blockObj->initPrevBlockChainFromParser(pc->blockChain);
|
||||
pc->blockChain = blockObj;
|
||||
stmt->blockObj = blockObj;
|
||||
blockObj->initEnclosingNestedScopeFromParser(pc->staticScope);
|
||||
pc->staticScope = blockObj;
|
||||
stmt->staticScope = blockObj;
|
||||
|
||||
#ifdef DEBUG
|
||||
ParseNode *tmp = pc->blockNode;
|
||||
|
@ -3628,7 +3619,7 @@ Parser<FullParseHandler>::letDeclaration()
|
|||
pc->blockNode = pn1;
|
||||
}
|
||||
|
||||
pn = variables(PNK_LET, nullptr, pc->blockChain, HoistVars);
|
||||
pn = variables(PNK_LET, nullptr, &pc->staticScope->as<StaticBlockObject>(), HoistVars);
|
||||
if (!pn)
|
||||
return null();
|
||||
pn->pn_xflags = PNX_POPVAR;
|
||||
|
@ -4063,7 +4054,6 @@ Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion versi
|
|||
if (pn1->isOp(JSOP_DEFCONST))
|
||||
return false;
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
// In JS 1.7 only, for (var [K, V] in EXPR) has a special meaning.
|
||||
// Hence all other destructuring decls are banned there.
|
||||
if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN) {
|
||||
|
@ -4076,7 +4066,6 @@ Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion versi
|
|||
if (lhs->isKind(PNK_ARRAY) && lhs->pn_count != 2)
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -4087,7 +4076,6 @@ Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion versi
|
|||
case PNK_ELEM:
|
||||
return true;
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case PNK_ARRAY:
|
||||
case PNK_OBJECT:
|
||||
// In JS 1.7 only, for ([K, V] in EXPR) has a special meaning.
|
||||
|
@ -4095,7 +4083,6 @@ Parser<FullParseHandler>::isValidForStatementLHS(ParseNode *pn1, JSVersion versi
|
|||
if (version == JSVERSION_1_7 && !isForEach && headKind == PNK_FORIN)
|
||||
return pn1->isKind(PNK_ARRAY) && pn1->pn_count == 2;
|
||||
return true;
|
||||
#endif
|
||||
|
||||
default:
|
||||
return false;
|
||||
|
@ -4246,12 +4233,7 @@ Parser<FullParseHandler>::forStatement()
|
|||
*/
|
||||
if (isForDecl) {
|
||||
pn2 = pn1->pn_head;
|
||||
if ((pn2->isKind(PNK_NAME) && pn2->maybeExpr())
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
|| pn2->isKind(PNK_ASSIGN)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if ((pn2->isKind(PNK_NAME) && pn2->maybeExpr()) || pn2->isKind(PNK_ASSIGN)) {
|
||||
/*
|
||||
* Declaration with initializer.
|
||||
*
|
||||
|
@ -4280,13 +4262,11 @@ Parser<FullParseHandler>::forStatement()
|
|||
pn1->pn_xflags |= PNX_POPVAR;
|
||||
pn1 = nullptr;
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
if (pn2->isKind(PNK_ASSIGN)) {
|
||||
pn2 = pn2->pn_left;
|
||||
JS_ASSERT(pn2->isKind(PNK_ARRAY) || pn2->isKind(PNK_OBJECT) ||
|
||||
pn2->isKind(PNK_NAME));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
/* Not a declaration. */
|
||||
|
@ -4334,7 +4314,6 @@ Parser<FullParseHandler>::forStatement()
|
|||
pn2->markAsAssigned();
|
||||
break;
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case PNK_ASSIGN:
|
||||
MOZ_ASSUME_UNREACHABLE("forStatement TOK_ASSIGN");
|
||||
|
||||
|
@ -4349,7 +4328,6 @@ Parser<FullParseHandler>::forStatement()
|
|||
iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:;
|
||||
}
|
||||
|
@ -5078,20 +5056,19 @@ Parser<ParseHandler>::tryStatement()
|
|||
* scoped, not a property of a new Object instance. This is
|
||||
* an intentional change that anticipates ECMA Ed. 4.
|
||||
*/
|
||||
data.initLet(HoistVars, *pc->blockChain, JSMSG_TOO_MANY_CATCH_VARS);
|
||||
data.initLet(HoistVars, pc->staticScope->template as<StaticBlockObject>(),
|
||||
JSMSG_TOO_MANY_CATCH_VARS);
|
||||
JS_ASSERT(data.let.blockObj);
|
||||
|
||||
tt = tokenStream.getToken();
|
||||
Node catchName;
|
||||
switch (tt) {
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case TOK_LB:
|
||||
case TOK_LC:
|
||||
catchName = destructuringExpr(&data, tt);
|
||||
if (!catchName)
|
||||
return null();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TOK_YIELD:
|
||||
if (!checkYieldNameValidity())
|
||||
|
@ -5522,7 +5499,6 @@ Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentF
|
|||
case PNK_ELEM:
|
||||
break;
|
||||
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case PNK_ARRAY:
|
||||
case PNK_OBJECT:
|
||||
if (flavor == CompoundAssignment) {
|
||||
|
@ -5532,7 +5508,6 @@ Parser<FullParseHandler>::checkAndMarkAsAssignmentLhs(ParseNode *pn, AssignmentF
|
|||
if (!checkDestructuring(nullptr, pn))
|
||||
return false;
|
||||
break;
|
||||
#endif
|
||||
|
||||
case PNK_CALL:
|
||||
if (!makeSetCall(pn, JSMSG_BAD_LEFTSIDE_OF_ASS))
|
||||
|
@ -6097,8 +6072,8 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
|||
if (!transplanter.transplant(kid))
|
||||
return null();
|
||||
|
||||
JS_ASSERT(pc->blockChain && pc->blockChain == pn->pn_objbox->object);
|
||||
data.initLet(HoistVars, *pc->blockChain, JSMSG_ARRAY_INIT_TOO_BIG);
|
||||
JS_ASSERT(pc->staticScope && pc->staticScope == pn->pn_objbox->object);
|
||||
data.initLet(HoistVars, pc->staticScope->as<StaticBlockObject>(), JSMSG_ARRAY_INIT_TOO_BIG);
|
||||
|
||||
do {
|
||||
/*
|
||||
|
@ -6121,7 +6096,6 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
|||
RootedPropertyName name(context);
|
||||
tt = tokenStream.getToken();
|
||||
switch (tt) {
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case TOK_LB:
|
||||
case TOK_LC:
|
||||
pc->inDeclDestructuring = true;
|
||||
|
@ -6130,7 +6104,6 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
|||
if (!pn3)
|
||||
return null();
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TOK_NAME:
|
||||
name = tokenStream.currentName();
|
||||
|
@ -6182,7 +6155,6 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
|||
}
|
||||
|
||||
switch (tt) {
|
||||
#if JS_HAS_DESTRUCTURING
|
||||
case TOK_LB:
|
||||
case TOK_LC:
|
||||
if (!checkDestructuring(&data, pn3))
|
||||
|
@ -6204,7 +6176,6 @@ Parser<FullParseHandler>::comprehensionTail(ParseNode *kid, unsigned blockid, bo
|
|||
pn2->pn_iflags |= JSITER_FOREACH | JSITER_KEYVALUE;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case TOK_NAME:
|
||||
data.pn = pn3;
|
||||
|
@ -6635,15 +6606,15 @@ typename ParseHandler::Node
|
|||
Parser<ParseHandler>::newRegExp()
|
||||
{
|
||||
// Create the regexp even when doing a syntax parse, to check the regexp's syntax.
|
||||
const jschar *chars = tokenStream.getTokenbuf().begin();
|
||||
size_t length = tokenStream.getTokenbuf().length();
|
||||
const StableCharPtr chars(tokenStream.getTokenbuf().begin(), length);
|
||||
RegExpFlag flags = tokenStream.currentToken().regExpFlags();
|
||||
|
||||
Rooted<RegExpObject*> reobj(context);
|
||||
if (RegExpStatics *res = context->global()->getRegExpStatics())
|
||||
reobj = RegExpObject::create(context, res, chars.get(), length, flags, &tokenStream);
|
||||
reobj = RegExpObject::create(context, res, chars, length, flags, &tokenStream);
|
||||
else
|
||||
reobj = RegExpObject::createNoStatics(context, chars.get(), length, flags, &tokenStream);
|
||||
reobj = RegExpObject::createNoStatics(context, chars, length, flags, &tokenStream);
|
||||
|
||||
if (!reobj)
|
||||
return null();
|
||||
|
@ -6915,7 +6886,6 @@ Parser<ParseHandler>::objectLiteral()
|
|||
if (!handler.addPropertyDefinition(literal, propname, propexpr))
|
||||
return null();
|
||||
}
|
||||
#if JS_HAS_DESTRUCTURING_SHORTHAND
|
||||
else if (ltok == TOK_NAME && (tt == TOK_COMMA || tt == TOK_RC)) {
|
||||
/*
|
||||
* Support, e.g., |var {x, y} = o| as destructuring shorthand
|
||||
|
@ -6934,7 +6904,6 @@ Parser<ParseHandler>::objectLiteral()
|
|||
if (!handler.addShorthandPropertyDefinition(literal, propname))
|
||||
return null();
|
||||
}
|
||||
#endif
|
||||
else {
|
||||
report(ParseError, false, null(), JSMSG_COLON_AFTER_ID);
|
||||
return null();
|
||||
|
|
|
@ -98,8 +98,7 @@ struct ParseContext : public GenericParseContext
|
|||
|
||||
StmtInfoPC *topStmt; /* top of statement info stack */
|
||||
StmtInfoPC *topScopeStmt; /* top lexical scope statement */
|
||||
Rooted<StaticBlockObject *> blockChain;
|
||||
/* compile time block scope chain */
|
||||
Rooted<NestedScopeObject *> staticScope; /* compile time scope chain */
|
||||
Node maybeFunction; /* sc->isFunctionBox, the pn where pn->pn_funbox == sc */
|
||||
|
||||
const unsigned staticLevel; /* static compilation unit nesting level */
|
||||
|
@ -250,7 +249,7 @@ struct ParseContext : public GenericParseContext
|
|||
blockidGen(bodyid), // used to set |bodyid| and subsequently incremented in init()
|
||||
topStmt(nullptr),
|
||||
topScopeStmt(nullptr),
|
||||
blockChain(prs->context),
|
||||
staticScope(prs->context),
|
||||
maybeFunction(maybeFunction),
|
||||
staticLevel(staticLevel),
|
||||
lastYieldOffset(NoYieldOffset),
|
||||
|
|