This commit is contained in:
Richard Newman 2013-05-29 11:11:21 -07:00
Родитель 59ce4e8b0b b0dc90fb63
Коммит 8c448c5675
344 изменённых файлов: 9776 добавлений и 3289 удалений

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

@ -7,6 +7,9 @@
#include "nsAccUtils.h"
#include "mozilla/StaticPtr.h"
using namespace mozilla;
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
@ -61,4 +64,4 @@ nsEventShell::GetEventAttributes(nsINode *aNode,
// nsEventShell: private
bool nsEventShell::sEventFromUserInput = false;
nsCOMPtr<nsINode> nsEventShell::sEventTargetNode;
StaticRefPtr<nsINode> nsEventShell::sEventTargetNode;

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

@ -8,6 +8,9 @@
#include "AccEvent.h"
namespace mozilla {
template<typename T> class StaticRefPtr;
}
class nsIPersistentProperties;
/**
@ -43,7 +46,7 @@ public:
nsIPersistentProperties *aAttributes);
private:
static nsCOMPtr<nsINode> sEventTargetNode;
static mozilla::StaticRefPtr<nsINode> sEventTargetNode;
static bool sEventFromUserInput;
};

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

@ -19,6 +19,13 @@
using namespace mozilla::a11y;
/**
* The accessible for which we are computing a text equivalent. It is useful
* for bailing out during recursive text computation, or for special cases
* like step f. of the ARIA implementation guide.
*/
static Accessible* sInitiatorAcc = nullptr;
////////////////////////////////////////////////////////////////////////////////
// nsTextEquivUtils. Public.
@ -28,10 +35,10 @@ nsTextEquivUtils::GetNameFromSubtree(Accessible* aAccessible,
{
aName.Truncate();
if (gInitiatorAcc)
if (sInitiatorAcc)
return NS_OK;
gInitiatorAcc = aAccessible;
sInitiatorAcc = aAccessible;
if (GetRoleRule(aAccessible->Role()) == eNameFromSubtreeRule) {
//XXX: is it necessary to care the accessible is not a document?
if (aAccessible->IsContent()) {
@ -43,7 +50,7 @@ nsTextEquivUtils::GetNameFromSubtree(Accessible* aAccessible,
}
}
gInitiatorAcc = nullptr;
sInitiatorAcc = nullptr;
return NS_OK;
}
@ -92,10 +99,10 @@ nsTextEquivUtils::AppendTextEquivFromContent(Accessible* aInitiatorAcc,
nsAString *aString)
{
// Prevent recursion which can cause infinite loops.
if (gInitiatorAcc)
if (sInitiatorAcc)
return NS_OK;
gInitiatorAcc = aInitiatorAcc;
sInitiatorAcc = aInitiatorAcc;
// If the given content is not visible or isn't accessible then go down
// through the DOM subtree otherwise go down through accessible subtree and
@ -108,7 +115,7 @@ nsTextEquivUtils::AppendTextEquivFromContent(Accessible* aInitiatorAcc,
if (isVisible) {
Accessible* accessible =
gInitiatorAcc->Document()->GetAccessible(aContent);
sInitiatorAcc->Document()->GetAccessible(aContent);
if (accessible) {
rv = AppendFromAccessible(accessible, aString);
goThroughDOMSubtree = false;
@ -118,7 +125,7 @@ nsTextEquivUtils::AppendTextEquivFromContent(Accessible* aInitiatorAcc,
if (goThroughDOMSubtree)
rv = AppendFromDOMNode(aContent, aString);
gInitiatorAcc = nullptr;
sInitiatorAcc = nullptr;
return rv;
}
@ -176,8 +183,6 @@ nsTextEquivUtils::AppendTextEquivFromTextContent(nsIContent *aContent,
////////////////////////////////////////////////////////////////////////////////
// nsTextEquivUtils. Private.
nsRefPtr<Accessible> nsTextEquivUtils::gInitiatorAcc;
nsresult
nsTextEquivUtils::AppendFromAccessibleChildren(Accessible* aAccessible,
nsAString *aString)
@ -257,7 +262,7 @@ nsTextEquivUtils::AppendFromValue(Accessible* aAccessible,
// value if and only if the given accessible is in the middle of its parent.
nsAutoString text;
if (aAccessible != gInitiatorAcc) {
if (aAccessible != sInitiatorAcc) {
aAccessible->Value(text);
return AppendString(aString, text) ?

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

@ -153,13 +153,6 @@ private:
* Returns the rule (constant of ETextEquivRule) for a given role.
*/
static uint32_t GetRoleRule(mozilla::a11y::roles::Role aRole);
/**
* The accessible for which we are computing a text equivalent. It is useful
* for bailing out during recursive text computation, or for special cases
* like step f. of the ARIA implementation guide.
*/
static nsRefPtr<Accessible> gInitiatorAcc;
};
#endif

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

@ -26,7 +26,8 @@ BaseTraversalRule.prototype = {
},
preFilter: Ci.nsIAccessibleTraversalRule.PREFILTER_DEFUNCT |
Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE,
Ci.nsIAccessibleTraversalRule.PREFILTER_INVISIBLE |
Ci.nsIAccessibleTraversalRule.PREFILTER_ARIA_HIDDEN,
match: function BaseTraversalRule_match(aAccessible)
{

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

@ -184,6 +184,21 @@ this.Utils = {
return [state.value, extState.value];
},
getAttributes: function getAttributes(aAccessible) {
let attributesEnum = aAccessible.attributes.enumerate();
let attributes = {};
// Populate |attributes| object with |aAccessible|'s attribute key-value
// pairs.
while (attributesEnum.hasMoreElements()) {
let attribute = attributesEnum.getNext().QueryInterface(
Ci.nsIPropertyElement);
attributes[attribute.key] = attribute.value;
}
return attributes;
},
getVirtualCursor: function getVirtualCursor(aDocument) {
let doc = (aDocument instanceof Ci.nsIAccessible) ? aDocument :
this.AccRetrieval.getAccessibleFor(aDocument);

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

@ -12,6 +12,7 @@ const Cr = Components.results;
const INCLUDE_DESC = 0x01;
const INCLUDE_NAME = 0x02;
const INCLUDE_CUSTOM = 0x04;
const NAME_FROM_SUBTREE_RULE = 0x08;
const UTTERANCE_DESC_FIRST = 0;
@ -74,19 +75,32 @@ this.UtteranceGenerator = {
utterance.push.apply(utterance,
UtteranceGenerator.genForObject(aAccessible));
};
let roleString = Utils.AccRetrieval.getStringRole(aContext.accessible.role);
let nameRule = this.roleRuleMap[roleString] || 0;
let utteranceOrder = gUtteranceOrder.value || UTTERANCE_DESC_FIRST;
// Include subtree if the name is not explicit or the role's name rule is
// not the NAME_FROM_SUBTREE_RULE.
let includeSubtree = (Utils.getAttributes(aContext.accessible)[
'explicit-name'] !== 'true') || !(nameRule & NAME_FROM_SUBTREE_RULE);
if (utteranceOrder === UTTERANCE_DESC_FIRST) {
aContext.newAncestry.forEach(addUtterance);
addUtterance(aContext.accessible);
aContext.subtreePreorder.forEach(addUtterance);
if (includeSubtree) {
aContext.subtreePreorder.forEach(addUtterance);
}
} else {
aContext.subtreePostorder.forEach(addUtterance);
if (includeSubtree) {
aContext.subtreePostorder.forEach(addUtterance);
}
addUtterance(aContext.accessible);
aContext.newAncestry.reverse().forEach(addUtterance);
}
// Clean up the white space.
let trimmed;
utterance = [trimmed for (word of utterance) if (trimmed = word.trim())];
return utterance;
},
@ -98,7 +112,7 @@ this.UtteranceGenerator = {
* @return {Array} Two string array. The first string describes the object
* and its states. The second string is the object's name. Whether the
* object's description or it's role is included is determined by
* {@link verbosityRoleMap}.
* {@link roleRuleMap}.
*/
genForObject: function genForObject(aAccessible) {
let roleString = Utils.AccRetrieval.getStringRole(aAccessible.role);
@ -106,7 +120,7 @@ this.UtteranceGenerator = {
let func = this.objectUtteranceFunctions[roleString] ||
this.objectUtteranceFunctions.defaultFunc;
let flags = this.verbosityRoleMap[roleString] || UTTERANCE_DESC_FIRST;
let flags = this.roleRuleMap[roleString] || 0;
if (aAccessible.childCount == 0)
flags |= INCLUDE_NAME;
@ -182,29 +196,35 @@ this.UtteranceGenerator = {
aIsEditing ? 'editingMode' : 'navigationMode')];
},
verbosityRoleMap: {
roleRuleMap: {
'menubar': INCLUDE_DESC,
'scrollbar': INCLUDE_DESC,
'grip': INCLUDE_DESC,
'alert': INCLUDE_DESC | INCLUDE_NAME,
'menupopup': INCLUDE_DESC,
'menuitem': INCLUDE_DESC,
'tooltip': INCLUDE_DESC,
'menuitem': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'tooltip': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'columnheader': NAME_FROM_SUBTREE_RULE,
'rowheader': NAME_FROM_SUBTREE_RULE,
'column': NAME_FROM_SUBTREE_RULE,
'row': NAME_FROM_SUBTREE_RULE,
'application': INCLUDE_NAME,
'document': INCLUDE_NAME,
'grouping': INCLUDE_DESC | INCLUDE_NAME,
'toolbar': INCLUDE_DESC,
'table': INCLUDE_DESC | INCLUDE_NAME,
'link': INCLUDE_DESC,
'link': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'helpballoon': NAME_FROM_SUBTREE_RULE,
'list': INCLUDE_DESC | INCLUDE_NAME,
'listitem': INCLUDE_DESC,
'listitem': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'outline': INCLUDE_DESC,
'outlineitem': INCLUDE_DESC,
'pagetab': INCLUDE_DESC,
'outlineitem': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'pagetab': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'graphic': INCLUDE_DESC,
'pushbutton': INCLUDE_DESC,
'checkbutton': INCLUDE_DESC,
'radiobutton': INCLUDE_DESC,
'pushbutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'checkbutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'radiobutton': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'buttondropdown': NAME_FROM_SUBTREE_RULE,
'combobox': INCLUDE_DESC,
'droplist': INCLUDE_DESC,
'progressbar': INCLUDE_DESC,
@ -213,15 +233,20 @@ this.UtteranceGenerator = {
'diagram': INCLUDE_DESC,
'animation': INCLUDE_DESC,
'equation': INCLUDE_DESC,
'buttonmenu': INCLUDE_DESC,
'buttonmenu': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'buttondropdowngrid': NAME_FROM_SUBTREE_RULE,
'pagetablist': INCLUDE_DESC,
'canvas': INCLUDE_DESC,
'check menu item': INCLUDE_DESC,
'label': INCLUDE_DESC,
'check menu item': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'label': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'password text': INCLUDE_DESC,
'popup menu': INCLUDE_DESC,
'radio menu item': INCLUDE_DESC,
'toggle button': INCLUDE_DESC,
'radio menu item': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'table column header': NAME_FROM_SUBTREE_RULE,
'table row header': NAME_FROM_SUBTREE_RULE,
'tear off menu item': NAME_FROM_SUBTREE_RULE,
'toggle button': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'parent menuitem': NAME_FROM_SUBTREE_RULE,
'header': INCLUDE_DESC,
'footer': INCLUDE_DESC,
'entry': INCLUDE_DESC | INCLUDE_NAME,
@ -230,7 +255,14 @@ this.UtteranceGenerator = {
'heading': INCLUDE_DESC,
'calendar': INCLUDE_DESC | INCLUDE_NAME,
'combobox list': INCLUDE_DESC,
'combobox option': INCLUDE_DESC,
'combobox option': INCLUDE_DESC | NAME_FROM_SUBTREE_RULE,
'listbox option': NAME_FROM_SUBTREE_RULE,
'listbox rich option': NAME_FROM_SUBTREE_RULE,
'gridcell': NAME_FROM_SUBTREE_RULE,
'check rich option': NAME_FROM_SUBTREE_RULE,
'term': NAME_FROM_SUBTREE_RULE,
'definition': NAME_FROM_SUBTREE_RULE,
'key': NAME_FROM_SUBTREE_RULE,
'image map': INCLUDE_DESC,
'option': INCLUDE_DESC,
'listbox': INCLUDE_DESC,
@ -314,12 +346,16 @@ this.UtteranceGenerator = {
},
_addName: function _addName(utterance, aAccessible, aFlags) {
let name = (aFlags & INCLUDE_NAME) ? (aAccessible.name || '') : '';
let utteranceOrder = gUtteranceOrder.value || 0;
let name;
if (Utils.getAttributes(aAccessible)['explicit-name'] === 'true' ||
(aFlags & INCLUDE_NAME)) {
name = aAccessible.name;
}
if (name) {
let utteranceOrder = gUtteranceOrder.value || UTTERANCE_DESC_FIRST;
utterance[utteranceOrder === UTTERANCE_DESC_FIRST ?
"push" : "unshift"](name);
'push' : 'unshift'](name);
}
},

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

@ -21,9 +21,6 @@
src="../events.js" />
<script type="application/javascript">
SimpleTest.expectAssertions(0, 1);
function getColorBtn(aBtnObj)
{
var colorpicker = aBtnObj.colorpicker;

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

@ -13,7 +13,9 @@ include $(DEPTH)/config/autoconf.mk
MOCHITEST_A11Y_FILES =\
jsatcommon.js \
utterance.js \
test_alive.html \
test_explicit_names.html \
test_utterance_order.html \
$(NULL)

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

@ -0,0 +1,166 @@
<html>
<head>
<title>[AccessFu] Trust explicitly associated names when speaking certain elements</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="utterance.js"></script>
<script type="application/javascript">
function doTest() {
// Test the following accOrElmOrID.
var tests = [{
accOrElmOrID: "anchor1",
expected: ["link", "title"]
}, {
accOrElmOrID: "anchor2",
expected: ["link", "This is a link"]
}, {
accOrElmOrID: "button1",
expected: ["button", "Press me"]
}, {
accOrElmOrID: "button2",
expected: ["button", "Press me"]
}, {
accOrElmOrID: "textarea1",
expected: ["text area", "Test Text Area", "This is the text area text."]
}, {
accOrElmOrID: "textarea2",
expected: ["text area", "This is the text area text."]
}, {
accOrElmOrID: "heading1",
expected: ["heading level 1", "Test heading", "This is the heading."]
}, {
accOrElmOrID: "heading2",
expected: ["heading level 1", "This is the heading."]
}, {
accOrElmOrID: "list",
expected: ["list 2 items", "Test List", "First item", "Top of the list",
"1.", "list one", "Last item", "2.", "list two"]
}, {
accOrElmOrID: "dlist",
expected: ["definition list 0.5 items", "Test Definition List",
"dd one"]
}, {
accOrElmOrID: "li_one",
expected: ["list 2 items", "Test List", "First item", "Top of the list"]
}, {
accOrElmOrID: "li_two",
expected: ["list 2 items", "Test List", "Last item", "2.", "list two"]
}, {
accOrElmOrID: "cell",
expected: ["table", "Fruits and vegetables", "List of Fruits",
"list 4 items","First item", "link", "Apples", "link", "Bananas",
"link", "Peaches", "Last item", "link", "Plums"]
}, {
accOrElmOrID: "app.net",
expected: ["list 2 items", "First item", "link", "star", "Last item",
"link", "repost"]
}, {
// Test pivot to list from li_one.
accOrElmOrID: "list",
oldAccOrElmOrID: "li_one",
expected: ["list 2 items", "Test List", "First item", "Top of the list",
"1.", "list one", "Last item", "2.", "list two"]
}, {
// Test pivot to li_one from list.
accOrElmOrID: "li_one",
oldAccOrElmOrID: "list",
expected: ["list 2 items", "Test List", "First item", "Top of the list"]
}, {
// Test pivot to "apples" link from the table cell.
accOrElmOrID: "apples",
oldAccOrElmOrID: "cell",
expected: ["List of Fruits", "list 4 items", "First item", "link",
"Apples"]
}, {
// Test pivot to the table cell from the "apples" link.
accOrElmOrID: "cell",
oldAccOrElmOrID: "apples",
expected: ["List of Fruits", "list 4 items", "First item", "link",
"Apples", "link", "Bananas", "link", "Peaches", "Last item", "link",
"Plums"]
}];
// Test various explicit names vs the utterance generated from subtrees.
tests.forEach(function run(test) {
testUtterance(test.expected, test.accOrElmOrID, test.oldAccOrElmOrID);
});
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<div id="root">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=845870"
title="[AccessFu] Trust explicitly associated names when speaking certain elements">
Mozilla Bug 845870
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<button id="button1" aria-label="Press me">This is not a name</button>
<button id="button2">
Press me
</button>
<a id="anchor1" href="#test" title="title"></a>
<a id="anchor2" href="#test">This is a link</a>
<textarea id="textarea1" title="Test Text Area" cols="80" rows="5">This is the text area text.</textarea>
<textarea id="textarea2" cols="80" rows="5">
This is the text area text.
</textarea>
<h1 id="heading1" title="Test heading">This is the heading.</h1>
<h1 id="heading2">
This is the heading.
</h1>
<ol id="list" title="Test List">
<li id="li_one" aria-label="Top of the list">list one</li>
<li id="li_two">list two</li>
</ol>
<dl id="dlist" title="Test Definition List">
<dd id="dd_one">dd one</dd>
</dl>
<table>
<caption>Fruits and vegetables</caption>
<tr>
<td id="cell" aria-label="List of Fruits">
<ul style="list-style-type: none;">
<li><a id="apples" href="#">Apples</a></li>
<li><a id="bananas" href="#">Bananas</a></li>
<li><a href="#">Peaches</a></li>
<li>
<a href="#">
Plums
</a>
</li>
</ul>
</td>
</tr>
</table>
<!-- app.net -->
<ul id="app.net" class="unstyled ul-horizontal yui3-u fixed-right ta-right" style="list-style-type: none;">
<li class="yui3-u">
<a href="#star" data-starred="0" data-star-button="1" data-post-id="5098826">
<i aria-label="star" class="icon-star-empty"></i>
</a>
</li>
<li class="yui3-u repost">
<a href="#repost" title="repost" data-repost-button="1" data-reposted="0" data-post-id="5098826">
<i aria-label="repost" class="icon-repost"></i>
</a>
</li>
</ul>
</div>
</body>
</html>

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

@ -12,111 +12,79 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="./utterance.js"></script>
<script type="application/javascript">
const Cu = Components.utils;
const PREF_UTTERANCE_ORDER = "accessibility.accessfu.utterance";
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
Cu.import("resource://gre/modules/accessibility/UtteranceGenerator.jsm",
this);
// Test UtteranceGenerator.genForContext utterance order for
// a particular accessible context with an optional old accessible.
function testContextUtterance(expected, aAccOrElmOrID, oldAAccOrElmOrID) {
oldAAccOrElmOrID = oldAAccOrElmOrID || "root";
var accessible = getAccessible(aAccOrElmOrID);
var oldAccessible = getAccessible(oldAAccOrElmOrID);
var context = new PivotContext(accessible, oldAccessible);
var utterance = UtteranceGenerator.genForContext(context);
isDeeply(utterance, expected,
"Utterance order is correct for " + aAccOrElmOrID);
}
// Test UtteranceGenerator.genForObject for a particular aAccOrElmOrID.
function testUtterance(utteranceOrder, aAccOrElmOrID) {
var accessible = getAccessible(aAccOrElmOrID);
var utterance = UtteranceGenerator.genForObject(accessible);
var expectedNameIndex = utteranceOrder === 0 ? utterance.length - 1 : 0;
if (aAccOrElmOrID === "li_one" || aAccOrElmOrID === "cell") {
// List item's and table cell's name is not included into an object
// utterance.
expectedNameIndex = -1;
}
ok(utterance.indexOf(accessible.name) === expectedNameIndex,
"Object utterance is correct for " + aAccOrElmOrID);
}
function doTest() {
// Test the following aAccOrElmOrID (with optional old aAccOrElmOrID).
// Note: each aAccOrElmOrID entry maps to a unique object utterance
// Test the following accOrElmOrID (with optional old accOrElmOrID).
// Note: each accOrElmOrID entry maps to a unique object utterance
// generator function within the UtteranceGenerator.
var tests = [{
aAccOrElmOrID: "anchor",
accOrElmOrID: "anchor",
expected: [["link", "title"], ["title", "link"]]
}, {
aAccOrElmOrID: "textarea",
accOrElmOrID: "textarea",
expected: [[
"text area", "Test Text Area", "This is the text area text."
"text area", "This is the text area text."
], [
"This is the text area text.", "Test Text Area", "text area"
"This is the text area text.", "text area"
]]
}, {
aAccOrElmOrID: "heading",
accOrElmOrID: "heading",
expected: [
["heading level 1", "Test heading"],
["Test heading", "heading level 1"]
]
}, {
aAccOrElmOrID: "list",
accOrElmOrID: "list",
expected: [
["list 1 items", "Test List", "First item", "1. ", "list one"],
["1. ", "list one", "First item", "Test List", "list 1 items"]
["list 1 items", "First item", "1.", "list one"],
["1.", "list one", "First item", "list 1 items"]
]
}, {
aAccOrElmOrID: "dlist",
accOrElmOrID: "dlist",
expected: [
["definition list 0.5 items", "Test Definition List", "dd one "],
["dd one ", "Test Definition List", "definition list 0.5 items"]
["definition list 0.5 items", "dd one"],
["dd one", "definition list 0.5 items"]
]
}, {
aAccOrElmOrID: "li_one",
accOrElmOrID: "li_one",
expected: [
["list 1 items", "Test List", "First item", "1. ", "list one"],
["1. ", "list one", "First item", "Test List", "list 1 items"]
["list 1 items", "First item", "1.", "list one"],
["1.", "list one", "First item", "list 1 items"]
]
}, {
aAccOrElmOrID: "cell",
accOrElmOrID: "cell",
expected: [[
"table", "Fruits and vegetables", "list 4 items", "First item", " ",
"link", "Apples", " ", "link", "Bananas", " ", "link", "Peaches",
"Last item", " ", "link", "Plums"
"table", "Fruits and vegetables", "list 4 items", "First item",
"link", "Apples", "link", "Bananas", "link", "Peaches",
"Last item", "link", "Plums"
], [
" ", "Apples", "link", "First item", " ", "Bananas", "link", " ",
"Peaches", "link", " ", "Plums", "link", "Last item",
"list 4 items", "Fruits and vegetables", "table"
"Apples", "link", "First item", "Bananas", "link", "Peaches",
"link", "Plums", "link", "Last item", "list 4 items",
"Fruits and vegetables", "table"
]]
}, {
// Test pivot to list from li_one.
aAccOrElmOrID: "list",
oldAAccOrElmOrID: "li_one",
accOrElmOrID: "list",
oldAccOrElmOrID: "li_one",
expected: [
["list 1 items", "Test List", "First item", "1. ", "list one"],
["1. ", "list one", "First item", "Test List", "list 1 items"]
["list 1 items", "First item", "1.", "list one"],
["1.", "list one", "First item", "list 1 items"]
]
}, {
// Test pivot to "apples" link from the table cell.
aAccOrElmOrID: "apples",
oldAAccOrElmOrID: "cell",
accOrElmOrID: "apples",
oldAccOrElmOrID: "cell",
expected: [
["list 4 items", "First item", "link", "Apples"],
["Apples", "link", "First item", "list 4 items"]
]
}, {
// Test pivot to 'bananas' link from 'apples' link.
aAccOrElmOrID: "bananas",
oldAAccOrElmOrID: "apples",
accOrElmOrID: "bananas",
oldAccOrElmOrID: "apples",
expected: [["link", "Bananas"], ["Bananas", "link"]]
}];
@ -126,14 +94,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
utteranceOrderValues.forEach(
function testUtteranceOrder(utteranceOrder) {
SpecialPowers.setIntPref(PREF_UTTERANCE_ORDER, utteranceOrder);
testContextUtterance(test.expected[utteranceOrder],
test.aAccOrElmOrID, test.oldAAccOrElmOrID);
// Just need to test object utterance for individual
// aAccOrElmOrID.
if (test.oldAAccOrElmOrID) {
return;
}
testUtterance(utteranceOrder, test.aAccOrElmOrID);
var expected = test.expected[utteranceOrder];
testUtterance(expected, test.accOrElmOrID, test.oldAccOrElmOrID);
}
);
});
@ -158,13 +120,17 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
<div id="content" style="display: none"></div>
<pre id="test"></pre>
<a id="anchor" href="#test" title="title"></a>
<textarea id="textarea" title="Test Text Area" cols="80" rows="5">This is the text area text.</textarea>
<textarea id="textarea" cols="80" rows="5">
This is the text area text.
</textarea>
<h1 id="heading" title="Test heading"></h1>
<ol id="list" title="Test List">
<ol id="list">
<li id="li_one">list one</li>
</ol>
<dl id="dlist" title="Test Definition List">
<dd id="dd_one">dd one</li>
<dl id="dlist">
<dd id="dd_one">
dd one
</dd>
</dl>
<table>
<caption>Fruits and vegetables</caption>
@ -174,7 +140,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=753984
<li><a id="apples" href="#">Apples</a></li>
<li><a id="bananas" href="#">Bananas</a></li>
<li><a href="#">Peaches</a></li>
<li><a href="#">Plums</a></li>
<li>
<a href="#">
Plums
</a>
</li>
</ul>
</td>
</tr>

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

@ -0,0 +1,70 @@
const Cu = Components.utils;
const PREF_UTTERANCE_ORDER = "accessibility.accessfu.utterance";
Cu.import('resource://gre/modules/accessibility/Utils.jsm');
Cu.import("resource://gre/modules/accessibility/UtteranceGenerator.jsm",
this);
/**
* Test context utterance generation.
*
* @param expected {Array} expected utterance.
* @param aAccOrElmOrID identifier to get an accessible to test.
* @param aOldAccOrElmOrID optional identifier to get an accessible relative to
* the |aAccOrElmOrID|.
*
* Note: if |aOldAccOrElmOrID| is not provided, the |aAccOrElmOrID| must be
* scoped to the "root" element in markup.
*/
function testContextUtterance(expected, aAccOrElmOrID, aOldAccOrElmOrID) {
aOldAccOrElmOrID = aOldAccOrElmOrID || "root";
var accessible = getAccessible(aAccOrElmOrID);
var oldAccessible = getAccessible(aOldAccOrElmOrID);
var context = new PivotContext(accessible, oldAccessible);
var utterance = UtteranceGenerator.genForContext(context);
isDeeply(utterance, expected,
"Context utterance is correct for " + aAccOrElmOrID);
}
/**
* Test object utterance generated array that includes names.
* Note: test ignores utterances without the name.
*
* @param aAccOrElmOrID identifier to get an accessible to test.
*/
function testObjectUtterance(aAccOrElmOrID) {
var accessible = getAccessible(aAccOrElmOrID);
var utterance = UtteranceGenerator.genForObject(accessible);
var utteranceOrder;
try {
utteranceOrder = SpecialPowers.getIntPref(PREF_UTTERANCE_ORDER);
} catch (ex) {
// PREF_UTTERANCE_ORDER not set.
utteranceOrder = 0;
}
var expectedNameIndex = utteranceOrder === 0 ? utterance.length - 1 : 0;
var nameIndex = utterance.indexOf(accessible.name);
if (nameIndex > -1) {
ok(utterance.indexOf(accessible.name) === expectedNameIndex,
"Object utterance is correct for " + aAccOrElmOrID);
}
}
/**
* Test object and context utterance for an accessible.
*
* @param expected {Array} expected utterance.
* @param aAccOrElmOrID identifier to get an accessible to test.
* @param aOldAccOrElmOrID optional identifier to get an accessible relative to
* the |aAccOrElmOrID|.
*/
function testUtterance(expected, aAccOrElmOrID, aOldAccOrElmOrID) {
testContextUtterance(expected, aAccOrElmOrID, aOldAccOrElmOrID);
// Just need to test object utterance for individual
// accOrElmOrID.
if (aOldAccOrElmOrID) {
return;
}
testObjectUtterance(aAccOrElmOrID);
}

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

@ -16,8 +16,6 @@
src="../states.js"></script>
<script type="application/javascript">
SimpleTest.expectAssertions(0, 1);
function doTest()
{
var tree =

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

@ -50,6 +50,9 @@ pref("network.http.max-connections", 20);
pref("network.http.max-persistent-connections-per-server", 6);
pref("network.http.max-persistent-connections-per-proxy", 20);
// spdy
pref("network.http.spdy.push-allowance", 32768);
// See bug 545869 for details on why these are set the way they are
pref("network.buffer.cache.count", 24);
pref("network.buffer.cache.size", 16384);

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

@ -128,7 +128,7 @@ WebappsActor.prototype = {
let appType = self._getAppType(aManifest.type);
// In production builds, don't allow installation of certified apps.
#ifdef MOZ_OFFICIAL
#ifdef MOZ_OFFICIAL_BRANDING
if (appType == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
self._sendError("Installing certified apps is not allowed.", aId);
return;
@ -210,7 +210,7 @@ WebappsActor.prototype = {
let appType = self._getAppType(aManifest.type);
// In production builds, don't allow installation of certified apps.
#ifdef MOZ_OFFICIAL
#ifdef MOZ_OFFICIAL_BRANDING
if (appType == Ci.nsIPrincipal.APP_STATUS_CERTIFIED) {
self._sendError("Installing certified apps is not allowed.", aId);
return;
@ -286,7 +286,7 @@ WebappsActor.prototype = {
if (missing) {
try {
aDir.remove(true);
appDir.remove(true);
} catch(e) {}
return { error: "badParameterType",
message: "hosted app file is missing" }

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

@ -291,3 +291,15 @@ SettingsListener.observe('app.reportCrashes', 'ask', function(value) {
SettingsListener.observe('app.update.interval', 86400, function(value) {
Services.prefs.setIntPref('app.update.interval', value);
});
// ================ Debug ================
// XXX could factor out into a settings->pref map.
SettingsListener.observe("debug.fps.enabled", false, function(value) {
Services.prefs.setBoolPref("layers.acceleration.draw-fps", value);
});
SettingsListener.observe("debug.paint-flashing.enabled", false, function(value) {
Services.prefs.setBoolPref("nglayout.debug.paint_flashing", value);
});
SettingsListener.observe("layers.draw-borders", false, function(value) {
Services.prefs.setBoolPref("layers.draw-borders", value);
});

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

@ -320,14 +320,6 @@ var shell = {
IndexedDBPromptHelper.init();
CaptivePortalLoginHelper.init();
// XXX could factor out into a settings->pref map. Not worth it yet.
SettingsListener.observe("debug.fps.enabled", false, function(value) {
Services.prefs.setBoolPref("layers.acceleration.draw-fps", value);
});
SettingsListener.observe("debug.paint-flashing.enabled", false, function(value) {
Services.prefs.setBoolPref("nglayout.debug.paint_flashing", value);
});
this.contentBrowser.src = homeURL;
this.isHomeLoaded = false;

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

@ -4,8 +4,10 @@
"mock_target": "mozilla-centos6-x86_64",
"mock_packages": ["ccache", "make", "bison", "flex", "gcc", "g++", "mpfr", "zlib-devel", "ncurses-devel", "zip", "autoconf213", "glibc-static", "perl-Digest-SHA", "wget", "alsa-lib", "atk", "cairo", "dbus-glib", "fontconfig", "freetype", "glib2", "gtk2", "libXRender", "libXt", "pango", "mozilla-python27-mercurial", "openssh-clients", "nss-devel", "glibc-devel.i686", "libstdc++.i686", "zlib-devel.i686", "ncurses-devel.i686", "libX11-devel.i686", "mesa-libGL-devel.i686", "mesa-libGL-devel", "libX11-devel", "git"],
"mock_files": [["/home/cltbld/.ssh", "/home/mock_mozilla/.ssh"]],
"build_targets": ["droid", "package-emulator"],
"build_targets": ["droid", "package-emulator", "package-tests"],
"upload_files": [
"{workdir}/out/target/product/generic/*.tar.bz2",
"{workdir}/out/target/product/generic/tests/*.zip",
"{workdir}/out/emulator.tar.gz",
"{objdir}/dist/b2g-*.crashreporter-symbols.zip",
"{workdir}/sources.xml"

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

@ -18,7 +18,12 @@ var FullScreen = {
// Toggle the View:FullScreen command, which controls elements like the
// fullscreen menuitem, menubars, and the appmenu.
document.getElementById("View:FullScreen").setAttribute("checked", enterFS);
let fullscreenCommand = document.getElementById("View:FullScreen");
if (enterFS) {
fullscreenCommand.setAttribute("checked", enterFS);
} else {
fullscreenCommand.removeAttribute("checked");
}
#ifdef XP_MACOSX
// Make sure the menu items are adjusted.

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

@ -577,7 +577,8 @@
<menuitem id="menu_chromeDebugger"
observes="devtoolsMenuBroadcaster_ChromeDebugger"/>
<menuitem id="menu_browserConsole"
observes="devtoolsMenuBroadcaster_BrowserConsole"/>
observes="devtoolsMenuBroadcaster_BrowserConsole"
accesskey="&browserConsoleCmd.accesskey;"/>
<menuitem id="menu_responsiveUI"
observes="devtoolsMenuBroadcaster_ResponsiveUI"
accesskey="&responsiveDesignTool.accesskey;"/>

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

@ -96,7 +96,7 @@
<command id="Tools:DevToolbar" oncommand="DeveloperToolbar.toggle();" disabled="true" hidden="true"/>
<command id="Tools:DevToolbarFocus" oncommand="DeveloperToolbar.focusToggle();" disabled="true"/>
<command id="Tools:ChromeDebugger" oncommand="DebuggerUI.toggleChromeDebugger();" disabled="true" hidden="true"/>
<command id="Tools:BrowserConsole" oncommand="HUDConsoleUI.toggleBrowserConsole();" disabled="true" hidden="true"/>
<command id="Tools:BrowserConsole" oncommand="HUDConsoleUI.toggleBrowserConsole();"/>
<command id="Tools:Scratchpad" oncommand="Scratchpad.openScratchpad();" disabled="true" hidden="true"/>
<command id="Tools:ResponsiveUI" oncommand="ResponsiveUI.toggle();" disabled="true" hidden="true"/>
<command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
@ -187,6 +187,7 @@
command="Tools:ChromeDebugger"/>
<broadcaster id="devtoolsMenuBroadcaster_BrowserConsole"
label="&browserConsoleCmd.label;"
key="key_browserConsole"
command="Tools:BrowserConsole"/>
<broadcaster id="devtoolsMenuBroadcaster_Scratchpad"
label="&scratchpad.label;"
@ -203,7 +204,6 @@
command="View:PageSource"/>
<broadcaster id="devtoolsMenuBroadcaster_ErrorConsole"
label="&errorConsoleCmd.label;"
key="key_errorConsole"
command="Tools:ErrorConsole"/>
<broadcaster id="devtoolsMenuBroadcaster_GetMoreTools"
label="&getMoreDevtoolsCmd.label;"
@ -261,7 +261,7 @@
<key id="key_openDownloads" key="&downloads.commandkey;" command="Tools:Downloads" modifiers="accel"/>
#endif
<key id="key_openAddons" key="&addons.commandkey;" command="Tools:Addons" modifiers="accel,shift"/>
<key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" command="Tools:ErrorConsole" modifiers="accel,shift"/>
<key id="key_browserConsole" key="&browserConsoleCmd.commandkey;" command="Tools:BrowserConsole" modifiers="accel,shift"/>
<key id="key_devToolbar" keycode="&devToolbar.keycode;" modifiers="shift"
keytext="&devToolbar.keytext;" command="Tools:DevToolbarFocus"/>
<key id="key_responsiveUI" key="&responsiveDesignTool.commandkey;" command="Tools:ResponsiveUI"

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

@ -1193,17 +1193,8 @@ var gBrowserInit = {
cmd.removeAttribute("hidden");
}
// Enable the Browser Console?
if (chromeEnabled) {
let cmd = document.getElementById("Tools:BrowserConsole");
cmd.removeAttribute("disabled");
cmd.removeAttribute("hidden");
}
// Enable Error Console?
// Temporarily enabled. See bug 798925.
let consoleEnabled = true || gPrefService.getBoolPref("devtools.errorconsole.enabled") ||
chromeEnabled;
let consoleEnabled = gPrefService.getBoolPref("devtools.errorconsole.enabled");
if (consoleEnabled) {
let cmd = document.getElementById("Tools:ErrorConsole");
cmd.removeAttribute("disabled");

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

@ -13,6 +13,7 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/shared/event-emitter.js");
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource:///modules/devtools/ProfilerController.jsm");
const FORBIDDEN_IDS = new Set(["toolbox", ""]);
const MAX_ORDINAL = 99;
@ -621,6 +622,25 @@ let gDevToolsBrowser = {
}
},
/**
* Connects to the SPS profiler when the developer tools are open.
*/
_connectToProfiler: function DT_connectToProfiler() {
for (let win of gDevToolsBrowser._trackedBrowserWindows) {
if (devtools.TargetFactory.isKnownTab(win.gBrowser.selectedTab)) {
let target = devtools.TargetFactory.forTab(win.gBrowser.selectedTab);
if (gDevTools._toolboxes.has(target)) {
target.makeRemote().then(() => {
let profiler = new ProfilerController(target);
profiler.connect();
}).then(null, Cu.reportError);
return;
}
}
}
},
/**
* Remove the menuitem for a tool to all open browser windows.
*
@ -694,6 +714,7 @@ let gDevToolsBrowser = {
* All browser windows have been closed, tidy up remaining objects.
*/
destroy: function() {
gDevTools.off("toolbox-ready", gDevToolsBrowser._connectToProfiler);
Services.obs.removeObserver(gDevToolsBrowser.destroy, "quit-application");
},
}
@ -712,6 +733,7 @@ gDevTools.on("tool-unregistered", function(ev, toolId) {
});
gDevTools.on("toolbox-ready", gDevToolsBrowser._updateMenuCheckbox);
gDevTools.on("toolbox-ready", gDevToolsBrowser._connectToProfiler);
gDevTools.on("toolbox-destroyed", gDevToolsBrowser._updateMenuCheckbox);
Services.obs.addObserver(gDevToolsBrowser.destroy, "quit-application", false);

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

@ -207,6 +207,10 @@ TabTarget.prototype = {
return this._form;
},
get root() {
return this._root;
},
get client() {
return this._client;
},
@ -287,6 +291,7 @@ TabTarget.prototype = {
if (this.isLocalTab) {
this._client.connect((aType, aTraits) => {
this._client.listTabs(aResponse => {
this._root = aResponse;
this._form = aResponse.tabs[aResponse.selected];
attachTab();
});

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

@ -409,6 +409,49 @@ Toolbox.prototype = {
this._addKeysToWindow();
},
/**
* Load a tool with a given id.
*
* @param {string} id
* The id of the tool to load.
*/
loadTool: function TBOX_loadTool(id) {
let deferred = Promise.defer();
let iframe = this.doc.getElementById("toolbox-panel-iframe-" + id);
if (iframe) {
this.once(id + "-ready", () => { deferred.resolve() });
return deferred.promise;
}
let definition = gDevTools.getToolDefinitionMap().get(id);
iframe = this.doc.createElement("iframe");
iframe.className = "toolbox-panel-iframe";
iframe.id = "toolbox-panel-iframe-" + id;
iframe.setAttribute("flex", 1);
iframe.setAttribute("forceOwnRefreshDriver", "");
iframe.tooltip = "aHTMLTooltip";
let vbox = this.doc.getElementById("toolbox-panel-" + id);
vbox.appendChild(iframe);
let onLoad = () => {
iframe.removeEventListener("DOMContentLoaded", onLoad, true);
let built = definition.build(iframe.contentWindow, this);
Promise.resolve(built).then((panel) => {
this._toolPanels.set(id, panel);
this.emit(id + "-ready", panel);
gDevTools.emit(id + "-ready", this, panel);
deferred.resolve(panel);
});
};
iframe.addEventListener("DOMContentLoaded", onLoad, true);
iframe.setAttribute("src", definition.url);
return deferred.promise;
},
/**
* Switch to the tool with the given id
*
@ -464,8 +507,6 @@ Toolbox.prototype = {
let deck = this.doc.getElementById("toolbox-deck");
deck.selectedIndex = index;
let definition = gDevTools.getToolDefinitionMap().get(id);
this._currentToolId = id;
let resolveSelected = panel => {
@ -476,32 +517,11 @@ Toolbox.prototype = {
let iframe = this.doc.getElementById("toolbox-panel-iframe-" + id);
if (!iframe) {
iframe = this.doc.createElement("iframe");
iframe.className = "toolbox-panel-iframe";
iframe.id = "toolbox-panel-iframe-" + id;
iframe.setAttribute("flex", 1);
iframe.setAttribute("forceOwnRefreshDriver", "");
iframe.tooltip = "aHTMLTooltip";
let vbox = this.doc.getElementById("toolbox-panel-" + id);
vbox.appendChild(iframe);
let boundLoad = function() {
iframe.removeEventListener("DOMContentLoaded", boundLoad, true);
let built = definition.build(iframe.contentWindow, this);
Promise.resolve(built).then(function(panel) {
this._toolPanels.set(id, panel);
this.emit(id + "-ready", panel);
gDevTools.emit(id + "-ready", this, panel);
resolveSelected(panel);
}.bind(this));
}.bind(this);
iframe.addEventListener("DOMContentLoaded", boundLoad, true);
iframe.setAttribute("src", definition.url);
this.loadTool(id).then((panel) => {
this.emit("select", id);
this.emit(id + "-selected", panel);
deferred.resolve(panel);
});
} else {
let panel = this._toolPanels.get(id);
// only emit 'select' event if the iframe has been loaded

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

@ -11,7 +11,7 @@ function test() {
// This is receiving over 80 KB of json and will populate over 6000 items
// in a variables view instance. Debug builds are slow.
requestLongerTimeout(3);
requestLongerTimeout(4);
let { document, L10N, SourceEditor, NetMonitorView } = aMonitor.panelWin;
let { RequestsMenu } = NetMonitorView;
@ -29,16 +29,16 @@ function test() {
time: true
});
EventUtils.sendMouseEvent({ type: "mousedown" },
document.getElementById("details-pane-toggle"));
EventUtils.sendMouseEvent({ type: "mousedown" },
document.querySelectorAll("#details-pane tab")[3]);
aMonitor.panelWin.once("NetMonitor:ResponseBodyAvailable", () => {
testResponseTab();
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() {
let tab = document.querySelectorAll("#details-pane tab")[3];
let tabpanel = document.querySelectorAll("#details-pane tabpanel")[3];

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

@ -10,13 +10,16 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
Cu.import("resource://gre/modules/devtools/Console.jsm");
Cu.import("resource://gre/modules/AddonManager.jsm");
let EXPORTED_SYMBOLS = ["ProfilerController"];
XPCOMUtils.defineLazyGetter(this, "DebuggerServer", function () {
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
return DebuggerServer;
});
XPCOMUtils.defineLazyModuleGetter(this, "gDevTools",
"resource:///modules/devtools/gDevTools.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DebuggerServer",
"resource://gre/modules/devtools/dbg-server.jsm");
/**
* Data structure that contains information that has
@ -24,18 +27,24 @@ XPCOMUtils.defineLazyGetter(this, "DebuggerServer", function () {
* instances.
*/
const sharedData = {
startTime: 0,
data: new WeakMap(),
controllers: new WeakMap(),
};
/**
* Makes a structure representing an individual profile.
*/
function makeProfile(name) {
function makeProfile(name, def={}) {
if (def.timeStarted == null)
def.timeStarted = null;
if (def.timeEnded == null)
def.timeEnded = null;
return {
name: name,
timeStarted: null,
timeEnded: null
timeStarted: def.timeStarted,
timeEnded: def.timeEnded
};
}
@ -50,10 +59,6 @@ function getProfiles(target) {
return sharedData.data.get(target);
}
function getCurrentTime() {
return (new Date()).getTime() - sharedData.startTime;
}
/**
* Object to control the JavaScript Profiler over the remote
* debugging protocol.
@ -62,9 +67,14 @@ function getCurrentTime() {
* A target object as defined in Target.jsm
*/
function ProfilerController(target) {
if (sharedData.controllers.has(target)) {
return sharedData.controllers.get(target);
}
this.target = target;
this.client = target.client;
this.isConnected = false;
this.consoleProfiles = [];
addTarget(target);
@ -74,6 +84,8 @@ function ProfilerController(target) {
this.isConnected = true;
this.actor = target.form.profilerActor;
}
sharedData.controllers.set(target, this);
};
ProfilerController.prototype = {
@ -97,6 +109,76 @@ ProfilerController.prototype = {
return profile.timeStarted !== null && profile.timeEnded === null;
},
/**
* A listener that fires whenever console.profile or console.profileEnd
* is called.
*
* @param string type
* Type of a call. Either 'profile' or 'profileEnd'.
* @param object data
* Event data.
* @param object panel
* A reference to the ProfilerPanel in the current tab.
*/
onConsoleEvent: function (type, data, panel) {
let name = data.extra.name;
let profileStart = () => {
if (name && this.profiles.has(name))
return;
// Add profile to the UI (createProfile will return
// an automatically generated name if 'name' is falsey).
let profile = panel.createProfile(name);
profile.start((name, cb) => cb());
// Add profile structure to shared data.
this.profiles.set(profile.name, makeProfile(profile.name, {
timeStarted: data.extra.currentTime
}));
this.consoleProfiles.push(profile.name);
};
let profileEnd = () => {
if (!name && !this.consoleProfiles.length)
return;
if (!name)
name = this.consoleProfiles.pop();
else
this.consoleProfiles.filter((n) => n !== name);
if (!this.profiles.has(name))
return;
let profile = this.profiles.get(name);
if (!this.isProfileRecording(profile))
return;
let profileData = data.extra.profile;
profile.timeEnded = data.extra.currentTime;
profileData.threads = profileData.threads.map((thread) => {
let samples = thread.samples.filter((sample) => {
return sample.time >= profile.timeStarted;
});
return { samples: samples };
});
let ui = panel.getProfileByName(name);
ui.data = profileData;
ui.parse(profileData, () => panel.emit("parsed"));
ui.stop((name, cb) => cb());
};
if (type === "profile")
profileStart();
if (type === "profileEnd")
profileEnd();
},
/**
* Connects to the client unless we're already connected.
*
@ -105,16 +187,75 @@ ProfilerController.prototype = {
* the controller is already connected, this function
* will be called immediately (synchronously).
*/
connect: function (cb) {
connect: function (cb=function(){}) {
if (this.isConnected) {
return void cb();
}
// Check if we already have a grip to the listTabs response object
// and, if we do, use it to get to the profilerActor. Otherwise,
// call listTabs. The problem is that if we call listTabs twice
// webconsole tests fail (see bug 872826).
let register = () => {
let data = { events: ["console-api-profiler"] };
// Check if Gecko Profiler Addon [1] is installed and, if it is,
// don't register our own console event listeners. Gecko Profiler
// Addon takes care of console.profile and console.profileEnd methods
// and we don't want to break it.
//
// [1] - https://github.com/bgirard/Gecko-Profiler-Addon/
AddonManager.getAddonByID("jid0-edalmuivkozlouyij0lpdx548bc@jetpack", (addon) => {
if (addon && !addon.userDisabled && !addon.softDisabled)
return void cb();
this.request("registerEventNotifications", data, (resp) => {
this.client.addListener("eventNotification", (type, resp) => {
let toolbox = gDevTools.getToolbox(this.target);
if (toolbox == null)
return;
let panel = toolbox.getPanel("jsprofiler");
if (panel)
return void this.onConsoleEvent(resp.subject.action, resp.data, panel);
// Can't use a promise here because of a race condition when the promise
// is resolved only after -ready event is fired when creating a new panel
// and during the -ready event when waiting for a panel to be created:
//
// console.profile(); // creates a new panel, waits for the promise
// console.profileEnd(); // panel is not created yet but loading
//
// -> jsprofiler-ready event is fired which triggers a promise for profileEnd
// -> a promise for profile is triggered.
//
// And it should be the other way around. Hence the event.
toolbox.once("jsprofiler-ready", (_, panel) => {
this.onConsoleEvent(resp.subject.action, resp.data, panel);
});
toolbox.loadTool("jsprofiler");
});
});
cb();
});
};
if (this.target.root) {
this.actor = this.target.root.profilerActor;
this.isConnected = true;
return void register();
}
this.client.listTabs((resp) => {
this.actor = resp.profilerActor;
this.isConnected = true;
cb();
})
register();
});
},
/**
@ -144,7 +285,9 @@ ProfilerController.prototype = {
* value indicating if the profiler is active or not.
*/
isActive: function (cb) {
this.request("isActive", {}, (resp) => cb(resp.error, resp.isActive));
this.request("isActive", {}, (resp) => {
cb(resp.error, resp.isActive, resp.currentTime);
});
},
/**
@ -163,6 +306,7 @@ ProfilerController.prototype = {
}
let profile = makeProfile(name);
this.consoleProfiles.push(name);
this.profiles.set(name, profile);
// If profile is already running, no need to do anything.
@ -170,9 +314,9 @@ ProfilerController.prototype = {
return void cb();
}
this.isActive((err, isActive) => {
this.isActive((err, isActive, currentTime) => {
if (isActive) {
profile.timeStarted = getCurrentTime();
profile.timeStarted = currentTime;
return void cb();
}
@ -187,8 +331,7 @@ ProfilerController.prototype = {
return void cb(resp.error);
}
sharedData.startTime = (new Date()).getTime();
profile.timeStarted = getCurrentTime();
profile.timeStarted = 0;
cb();
});
});
@ -223,7 +366,7 @@ ProfilerController.prototype = {
}
let data = resp.profile;
profile.timeEnded = getCurrentTime();
profile.timeEnded = resp.currentTime;
// Filter out all samples that fall out of current
// profile's range.

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

@ -11,6 +11,7 @@ Cu.import("resource:///modules/devtools/ProfilerController.jsm");
Cu.import("resource:///modules/devtools/ProfilerHelpers.jsm");
Cu.import("resource:///modules/devtools/shared/event-emitter.js");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/devtools/Console.jsm");
this.EXPORTED_SYMBOLS = ["ProfilerPanel"];
@ -54,6 +55,7 @@ function ProfileUI(uid, name, panel) {
this.isStarted = false;
this.isFinished = false;
this.messages = [];
this.panel = panel;
this.uid = uid;
this.name = name;
@ -76,14 +78,6 @@ function ProfileUI(uid, name, panel) {
switch (event.data.status) {
case "loaded":
if (this.panel._runningUid !== null) {
this.iframe.contentWindow.postMessage(JSON.stringify({
uid: this._runningUid,
isCurrent: this._runningUid === uid,
task: "onStarted"
}), "*");
}
this.isReady = true;
this.emit("ready");
break;
@ -106,6 +100,22 @@ function ProfileUI(uid, name, panel) {
}
ProfileUI.prototype = {
/**
* Returns a contentWindow of the iframe pointing to Cleopatra
* if it exists and can be accessed. Otherwise returns null.
*/
get contentWindow() {
if (!this.iframe) {
return null;
}
try {
return this.iframe.contentWindow;
} catch (err) {
return null;
}
},
show: function PUI_show() {
this.iframe.removeAttribute("hidden");
},
@ -126,32 +136,27 @@ ProfileUI.prototype = {
*/
parse: function PUI_parse(data, onParsed) {
if (!this.isReady) {
return;
return void this.on("ready", this.parse.bind(this, data, onParsed));
}
let win = this.iframe.contentWindow;
this.message({ task: "receiveProfileData", rawProfile: data }).then(() => {
let poll = () => {
let wait = this.panel.window.setTimeout.bind(null, poll, 100);
let trail = this.contentWindow.gBreadcrumbTrail;
win.postMessage(JSON.stringify({
task: "receiveProfileData",
rawProfile: data
}), "*");
if (!trail) {
return wait();
}
let poll = function pollBreadcrumbs() {
let wait = this.panel.window.setTimeout.bind(null, poll, 100);
let trail = win.gBreadcrumbTrail;
if (!trail._breadcrumbs || !trail._breadcrumbs.length) {
return wait();
}
if (!trail) {
return wait();
}
onParsed();
};
if (!trail._breadcrumbs || !trail._breadcrumbs.length) {
return wait();
}
onParsed();
}.bind(this);
poll();
poll();
});
},
/**
@ -171,37 +176,90 @@ ProfileUI.prototype = {
* so that it could update the UI. Also, once started, we add a
* star to the profile name to indicate which profile is currently
* running.
*
* @param function startFn
* A function to use instead of the default
* this.panel.startProfiling. Useful when you
* need mark panel as started after the profiler
* has been started elsewhere. It must take two
* params and call the second one.
*/
start: function PUI_start() {
start: function PUI_start(startFn) {
if (this.isStarted || this.isFinished) {
return;
}
this.panel.startProfiling(this.name, function onStart() {
startFn = startFn || this.panel.startProfiling.bind(this.panel);
startFn(this.name, () => {
this.isStarted = true;
this.updateLabel(this.name + " *");
this.panel.broadcast(this.uid, {task: "onStarted"});
this.panel.broadcast(this.uid, {task: "onStarted"}); // Do we really need this?
this.emit("started");
}.bind(this));
});
},
/**
* Stop profiling and, once stopped, notify the underlying page so
* that it could update the UI and remove a star from the profile
* name.
*
* @param function stopFn
* A function to use instead of the default
* this.panel.stopProfiling. Useful when you
* need mark panel as stopped after the profiler
* has been stopped elsewhere. It must take two
* params and call the second one.
*/
stop: function PUI_stop() {
stop: function PUI_stop(stopFn) {
if (!this.isStarted || this.isFinished) {
return;
}
this.panel.stopProfiling(this.name, function onStop() {
stopFn = stopFn || this.panel.stopProfiling.bind(this.panel);
stopFn(this.name, () => {
this.isStarted = false;
this.isFinished = true;
this.updateLabel(this.name);
this.panel.broadcast(this.uid, {task: "onStopped"});
this.emit("stopped");
}.bind(this));
});
},
/**
* Send a message to Cleopatra instance. If a message cannot be
* sent, this method queues it for later.
*
* @param object data JSON data to send (must be serializable)
* @return promise
*/
message: function PIU_message(data) {
let deferred = Promise.defer();
let win = this.contentWindow;
data = JSON.stringify(data);
if (win) {
win.postMessage(data, "*");
deferred.resolve();
} else {
this.messages.push({ data: data, onSuccess: () => deferred.resolve() });
}
return deferred.promise;
},
/**
* Send all queued messages (see this.message for more info)
*/
flushMessages: function PIU_flushMessages() {
if (!this.contentWindow) {
return;
}
let msg;
while (msg = this.messages.shift()) {
this.contentWindow.postMessage(msg.data, "*");
msg.onSuccess();
}
},
/**
@ -212,6 +270,7 @@ ProfileUI.prototype = {
this.panel = null;
this.uid = null;
this.iframe = null;
this.messages = null;
}
};
@ -249,6 +308,7 @@ function ProfilerPanel(frame, toolbox) {
this.profiles = new Map();
this._uid = 0;
this._msgQueue = {};
EventEmitter.decorate(this);
}
@ -265,6 +325,7 @@ ProfilerPanel.prototype = {
_activeUid: null,
_runningUid: null,
_browserWin: null,
_msgQueue: null,
get activeProfile() {
return this.profiles.get(this._activeUid);
@ -297,6 +358,7 @@ ProfilerPanel.prototype = {
*/
open: function PP_open() {
let promise;
// Local profiling needs to make the target remote.
if (!this.target.isRemote) {
promise = this.target.makeRemote();
@ -350,7 +412,21 @@ ProfilerPanel.prototype = {
return this.getProfileByName(name);
}
let uid = ++this._uid;
let uid = ++this._uid;
// If profile is anonymous, increase its UID until we get
// to the unused name. This way if someone manually creates
// a profile named say 'Profile 2' we won't create a dup
// with the same name. We will just skip over uid 2.
if (!name) {
name = L10N.getFormatStr("profiler.profileName", [uid]);
while (this.getProfileByName(name)) {
uid = ++this._uid;
name = L10N.getFormatStr("profiler.profileName", [uid]);
}
}
let list = this.document.getElementById("profiles-list");
let item = this.document.createElement("li");
let wrap = this.document.createElement("h1");
@ -403,15 +479,17 @@ ProfilerPanel.prototype = {
this.activeProfile = profile;
if (profile.isReady) {
profile.flushMessages();
this.emit("profileSwitched", profile.uid);
onLoad();
return;
}
profile.once("ready", function () {
profile.once("ready", () => {
profile.flushMessages();
this.emit("profileSwitched", profile.uid);
onLoad();
}.bind(this));
});
},
/**
@ -422,15 +500,14 @@ ProfilerPanel.prototype = {
* that profiling had been successfuly started.
*/
startProfiling: function PP_startProfiling(name, onStart) {
this.controller.start(name, function (err) {
this.controller.start(name, (err) => {
if (err) {
Cu.reportError("ProfilerController.start: " + err.message);
return;
return void Cu.reportError("ProfilerController.start: " + err.message);
}
onStart();
this.emit("started");
}.bind(this));
});
},
/**
@ -503,6 +580,28 @@ ProfilerPanel.prototype = {
return this.profiles.get(uid) || null;
},
/**
* Iterates over each available profile and calls
* a callback with it as a parameter.
*
* @param function cb a callback to call
*/
eachProfile: function PP_eachProfile(cb) {
let uid = this._uid;
if (!this.profiles) {
return;
}
while (uid >= 0) {
if (this.profiles.has(uid)) {
cb(this.profiles.get(uid));
}
uid -= 1;
}
},
/**
* Broadcast messages to all Cleopatra instances.
*
@ -524,18 +623,13 @@ ProfilerPanel.prototype = {
this._runningUid = null;
}
let uid = this._uid;
while (uid >= 0) {
if (this.profiles.has(uid)) {
let iframe = this.profiles.get(uid).iframe;
iframe.contentWindow.postMessage(JSON.stringify({
uid: target,
isCurrent: target === uid,
task: data.task
}), "*");
}
uid -= 1;
}
this.eachProfile((profile) => {
profile.message({
uid: target,
isCurrent: target === profile.uid,
task: data.task
});
});
},
/**

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

@ -515,6 +515,7 @@ HistogramView.prototype = {
cancelAnimationFrame(self._pendingAnimationFrame);
self._pendingAnimationFrame = null;
self._render(highlightedCallstack);
self._busyCover.classList.remove("busy");
});
},
_render: function HistogramView__render(highlightedCallstack) {

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

@ -19,12 +19,17 @@ MOCHITEST_BROWSER_TESTS = \
browser_profiler_controller.js \
browser_profiler_bug_830664_multiple_profiles.js \
browser_profiler_bug_855244_multiple_tabs.js \
browser_profiler_console_api.js \
browser_profiler_console_api_named.js \
browser_profiler_console_api_mixed.js \
browser_profiler_console_api_content.js \
head.js \
$(NULL)
MOCHITEST_BROWSER_PAGES = \
mock_profiler_bug_834878_page.html \
mock_profiler_bug_834878_script.js \
mock_console_api.html \
$(NULL)
MOCHITEST_BROWSER_FILES_PARTS = MOCHITEST_BROWSER_TESTS MOCHITEST_BROWSER_PAGES

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

@ -33,11 +33,6 @@ function getCleoControls(doc) {
];
}
function sendFromProfile(uid, msg) {
let [win, doc] = getProfileInternals(uid);
win.parent.postMessage({ uid: uid, status: msg }, "*");
}
function startProfiling() {
gPanel.profiles.get(gPanel.activeProfile.uid).once("started", function () {
setTimeout(function () {

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

@ -0,0 +1,66 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const URL = "data:text/html;charset=utf8,<p>JavaScript Profiler test</p>";
let gTab, gPanel;
function test() {
waitForExplicitFinish();
setUp(URL, (tab, browser, panel) => {
gTab = tab;
gPanel = panel;
openConsole(tab, testConsoleProfile);
});
}
function testConsoleProfile(hud) {
hud.jsterm.clearOutput(true);
// Here we start two named profiles and then end one of them.
// profileEnd, when name is not provided, simply pops the latest
// profile.
let profilesStarted = 0;
function profileEnd(_, uid) {
let profile = gPanel.profiles.get(uid);
profile.once("started", () => {
if (++profilesStarted < 2)
return;
gPanel.off("profileCreated", profileEnd);
gPanel.profiles.get(3).once("stopped", () => {
openProfiler(gTab, checkProfiles);
});
hud.jsterm.execute("console.profileEnd()");
});
}
gPanel.on("profileCreated", profileEnd);
hud.jsterm.execute("console.profile()");
hud.jsterm.execute("console.profile()");
}
function checkProfiles(toolbox) {
let panel = toolbox.getPanel("jsprofiler");
let getTitle = (uid) =>
panel.document.querySelector("li#profile-" + uid + " > h1").textContent;
is(getTitle(1), "Profile 1", "Profile 1 doesn't have a star next to it.");
is(getTitle(2), "Profile 2 *", "Profile 2 doesn't have a star next to it.");
is(getTitle(3), "Profile 3", "Profile 3 doesn't have a star next to it.");
// Make sure we can still stop profiles via the UI.
gPanel.profiles.get(2).once("stopped", () => {
is(getTitle(2), "Profile 2", "Profile 2 doesn't have a star next to it.");
tearDown(gTab, () => gTab = gPanel = null);
});
sendFromProfile(2, "stop");
}

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

@ -0,0 +1,53 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const URL = "data:text/html;charset=utf8,<p>JavaScript Profiler test</p>";
const BASE = "http://example.com/browser/browser/devtools/profiler/test/";
const PAGE = BASE + "mock_console_api.html";
let gTab, gPanel, gToolbox;
function test() {
waitForExplicitFinish();
setUp(URL, (tab, browser, panel) => {
gTab = tab;
gPanel = panel;
openProfiler(tab, (toolbox) => {
gToolbox = toolbox;
loadUrl(PAGE, tab, () => {
gPanel.on("profileCreated", runTests);
});
});
});
}
function runTests() {
let getTitle = (uid) =>
gPanel.document.querySelector("li#profile-" + uid + " > h1").textContent;
is(getTitle(1), "Profile 1", "Profile 1 doesn't have a star next to it.");
is(getTitle(2), "Profile 2", "Profile 2 doesn't have a star next to it.");
gPanel.once("parsed", () => {
function assertSampleAndFinish() {
let [win,doc] = getProfileInternals();
let sample = doc.getElementsByClassName("samplePercentage");
if (sample.length <= 0)
return void setTimeout(assertSampleAndFinish, 100);
ok(sample.length > 0, "We have Cleopatra UI displayed");
tearDown(gTab, () => {
gTab = null;
gPanel = null;
gToolbox = null;
});
}
assertSampleAndFinish();
});
gPanel.switchToProfile(gPanel.profiles.get(2));
}

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

@ -0,0 +1,38 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const URL = "data:text/html;charset=utf8,<p>JavaScript Profiler test</p>";
let gTab, gPanel;
function test() {
waitForExplicitFinish();
setUp(URL, (tab, browser, panel) => {
gTab = tab;
gPanel = panel;
openProfiler(tab, runTests);
});
}
function runTests(toolbox) {
let panel = toolbox.getPanel("jsprofiler");
let getTitle = (uid) =>
panel.document.querySelector("li#profile-" + uid + " > h1").textContent;
panel.profiles.get(1).once("started", () => {
is(getTitle(1), "Profile 1 *", "Profile 1 has a start next to it.");
openConsole(gTab, (hud) => {
panel.profiles.get(1).once("stopped", () => {
is(getTitle(1), "Profile 1", "Profile 1 doesn't have a star next to it.");
tearDown(gTab, () => gTab = gPanel = null);
});
hud.jsterm.execute("console.profileEnd()");
});
});
sendFromProfile(1, "start");
}

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

@ -0,0 +1,66 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const URL = "data:text/html;charset=utf8,<p>JavaScript Profiler test</p>";
let gTab, gPanel;
function test() {
waitForExplicitFinish();
setUp(URL, (tab, browser, panel) => {
gTab = tab;
gPanel = panel;
openConsole(tab, testConsoleProfile);
});
}
function testConsoleProfile(hud) {
hud.jsterm.clearOutput(true);
// Here we start two named profiles and then end one of them.
let profilesStarted = 0;
function profileEnd(_, uid) {
let profile = gPanel.profiles.get(uid);
profile.once("started", () => {
if (++profilesStarted < 2)
return;
gPanel.off("profileCreated", profileEnd);
gPanel.profiles.get(2).once("stopped", () => {
openProfiler(gTab, checkProfiles);
});
hud.jsterm.execute("console.profileEnd('Second')");
});
}
gPanel.on("profileCreated", profileEnd);
hud.jsterm.execute("console.profile('Second')");
hud.jsterm.execute("console.profile('Third')");
}
function checkProfiles(toolbox) {
let panel = toolbox.getPanel("jsprofiler");
let getTitle = (uid) =>
panel.document.querySelector("li#profile-" + uid + " > h1").textContent;
is(getTitle(1), "Profile 1", "Profile 1 doesn't have a star next to it.");
is(getTitle(2), "Second", "Second doesn't have a star next to it.");
is(getTitle(3), "Third *", "Third does have a star next to it.");
// Make sure we can still stop profiles via the queue pop.
gPanel.profiles.get(3).once("stopped", () => {
openProfiler(gTab, () => {
is(getTitle(3), "Third", "Third doesn't have a star next to it.");
tearDown(gTab, () => gTab = gPanel = null);
});
});
openConsole(gTab, (hud) => hud.jsterm.execute("console.profileEnd()"));
}

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

@ -14,6 +14,9 @@ let TargetFactory = temp.devtools.TargetFactory;
Cu.import("resource://gre/modules/devtools/dbg-server.jsm", temp);
let DebuggerServer = temp.DebuggerServer;
Cu.import("resource:///modules/HUDService.jsm", temp);
let HUDService = temp.HUDService;
// Import the GCLI test helper
let testDir = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
Services.scriptloader.loadSubScript(testDir + "../../../commandline/test/helpers.js", this);
@ -33,11 +36,19 @@ function getProfileInternals(uid) {
return [win, doc];
}
function sendFromProfile(uid, msg) {
let [win, doc] = getProfileInternals(uid);
win.parent.postMessage({ uid: uid, status: msg }, "*");
}
function loadTab(url, callback) {
let tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
content.location.assign(url);
loadUrl(url, tab, callback);
}
function loadUrl(url, tab, callback) {
content.location.assign(url);
let browser = gBrowser.getBrowserForTab(tab);
if (browser.contentDocument.readyState === "complete") {
callback(tab, browser);
@ -57,6 +68,17 @@ function openProfiler(tab, callback) {
gDevTools.showToolbox(target, "jsprofiler").then(callback);
}
function openConsole(tab, cb=function(){}) {
// This function was borrowed from webconsole/test/head.js
let target = TargetFactory.forTab(tab);
gDevTools.showToolbox(target, "webconsole").then(function (toolbox) {
let hud = toolbox.getCurrentPanel().hud;
hud.jsterm._lazyVariablesView = false;
cb(hud);
});
}
function closeProfiler(tab, callback) {
let target = TargetFactory.forTab(tab);
let toolbox = gDevTools.getToolbox(target);

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

@ -0,0 +1,21 @@
<!-- Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ -->
<!DOCTYPE HTML>
<html>
<head>
<meta charset='utf-8'/>
<title>console.profile from content</title>
</head>
<body>
<script>
console.profile();
var a = new Array(500);
while (a.length) {
a.shift();
}
console.profileEnd();
</script>
</body>
</html>

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

@ -25,7 +25,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "gcli",
XPCOMUtils.defineLazyModuleGetter(this, "CmdCommands",
"resource:///modules/devtools/BuiltinCommands.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PageErrorListener",
XPCOMUtils.defineLazyModuleGetter(this, "ConsoleServiceListener",
"resource://gre/modules/devtools/WebConsoleUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PluralForm",
@ -444,8 +444,8 @@ DeveloperToolbar.prototype._initErrorsCount = function DT__initErrorsCount(aTab)
}
let window = aTab.linkedBrowser.contentWindow;
let listener = new PageErrorListener(window, {
onPageError: this._onPageError.bind(this, tabId),
let listener = new ConsoleServiceListener(window, {
onConsoleServiceMessage: this._onPageError.bind(this, tabId),
});
listener.init();

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

@ -131,6 +131,7 @@ MOCHITEST_BROWSER_FILES = \
browser_console_native_getters.js \
browser_bug_871156_ctrlw_close_tab.js \
browser_console_private_browsing.js \
browser_console_nsiconsolemessage.js \
head.js \
$(NULL)

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

@ -9,6 +9,15 @@ const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/te
function test()
{
let oldFunction = HUDConsoleUI.toggleBrowserConsole;
let functionExecuted = false;
HUDConsoleUI.toggleBrowserConsole = () => functionExecuted = true;
EventUtils.synthesizeKey("j", { accelKey: true, shiftKey: true }, content);
ok(functionExecuted,
"toggleBrowserConsole() was executed by the Ctrl-Shift-J key shortcut");
HUDConsoleUI.toggleBrowserConsole = oldFunction;
HUDConsoleUI.toggleBrowserConsole().then(consoleOpened);
}

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

@ -0,0 +1,79 @@
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Check that nsIConsoleMessages are displayed in the Browser Console.
// See bug 859756.
const TEST_URI = "data:text/html;charset=utf8,<title>bug859756</title>\n" +
"<p>hello world\n<p>nsIConsoleMessages ftw!";
function test()
{
addTab(TEST_URI);
browser.addEventListener("load", function onLoad() {
browser.removeEventListener("load", onLoad, true);
// Test for cached nsIConsoleMessages.
Services.console.logStringMessage("test1 for bug859756");
info("open web console");
openConsole(null, consoleOpened);
}, true);
}
function consoleOpened(hud)
{
ok(hud, "web console opened");
Services.console.logStringMessage("do-not-show-me");
content.console.log("foobarz");
waitForMessages({
webconsole: hud,
messages: [
{
text: "foobarz",
category: CATEGORY_WEBDEV,
severity: SEVERITY_LOG,
},
],
}).then(() => {
let text = hud.outputNode.textContent;
is(text.indexOf("do-not-show-me"), -1,
"nsIConsoleMessages are not displayed");
is(text.indexOf("test1 for bug859756"), -1,
"nsIConsoleMessages are not displayed (confirmed)");
closeConsole(null, onWebConsoleClose);
});
}
function onWebConsoleClose()
{
info("web console closed");
HUDConsoleUI.toggleBrowserConsole().then(onBrowserConsoleOpen);
}
function onBrowserConsoleOpen(hud)
{
ok(hud, "browser console opened");
Services.console.logStringMessage("test2 for bug859756");
waitForMessages({
webconsole: hud,
messages: [
{
text: "test1 for bug859756",
category: CATEGORY_JS,
},
{
text: "test2 for bug859756",
category: CATEGORY_JS,
},
{
text: "do-not-show-me",
category: CATEGORY_JS,
},
],
}).then(finishTest);
}

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

@ -961,6 +961,9 @@ WebConsoleFrame.prototype = {
[category, aMessage]);
break;
}
case "LogMessage":
this.handleLogMessage(aMessage);
break;
case "ConsoleAPI":
this.outputMessage(CATEGORY_WEBDEV, this.logConsoleAPIMessage,
[aMessage]);
@ -1198,6 +1201,21 @@ WebConsoleFrame.prototype = {
this.outputMessage(category, this.reportPageError, [category, aPageError]);
},
/**
* Handle log messages received from the server. This method outputs the given
* message.
*
* @param object aPacket
* The message packet received from the server.
*/
handleLogMessage: function WCF_handleLogMessage(aPacket)
{
this.outputMessage(CATEGORY_JS, () => {
return this.createMessageNode(CATEGORY_JS, SEVERITY_LOG, aPacket.message,
null, null, null, null, aPacket.timeStamp);
});
},
/**
* Log network event.
*
@ -4530,6 +4548,7 @@ function WebConsoleConnectionProxy(aWebConsole, aTarget)
this.target = aTarget;
this._onPageError = this._onPageError.bind(this);
this._onLogMessage = this._onLogMessage.bind(this);
this._onConsoleAPICall = this._onConsoleAPICall.bind(this);
this._onNetworkEvent = this._onNetworkEvent.bind(this);
this._onNetworkEventUpdate = this._onNetworkEventUpdate.bind(this);
@ -4634,6 +4653,7 @@ WebConsoleConnectionProxy.prototype = {
let client = this.client = this.target.client;
client.addListener("logMessage", this._onLogMessage);
client.addListener("pageError", this._onPageError);
client.addListener("consoleAPICall", this._onConsoleAPICall);
client.addListener("networkEvent", this._onNetworkEvent);
@ -4755,6 +4775,23 @@ WebConsoleConnectionProxy.prototype = {
}
},
/**
* The "logMessage" message type handler. We redirect any message to the UI
* for displaying.
*
* @private
* @param string aType
* Message type.
* @param object aPacket
* The message received from the server.
*/
_onLogMessage: function WCCP__onLogMessage(aType, aPacket)
{
if (this.owner && aPacket.from == this._consoleActor) {
this.owner.handleLogMessage(aPacket);
}
},
/**
* The "consoleAPICall" message type handler. We redirect any message to
* the UI for displaying.
@ -4899,6 +4936,7 @@ WebConsoleConnectionProxy.prototype = {
return this._disconnecter.promise;
}
this.client.removeListener("logMessage", this._onLogMessage);
this.client.removeListener("pageError", this._onPageError);
this.client.removeListener("consoleAPICall", this._onConsoleAPICall);
this.client.removeListener("networkEvent", this._onNetworkEvent);

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

@ -214,11 +214,12 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY errorConsoleCmd.label "Error Console">
<!ENTITY errorConsoleCmd.accesskey "C">
<!ENTITY errorConsoleCmd.commandkey "j">
<!ENTITY remoteWebConsoleCmd.label "Remote Web Console">
<!ENTITY browserConsoleCmd.label "Browser Console">
<!ENTITY browserConsoleCmd.commandkey "j">
<!ENTITY browserConsoleCmd.accesskey "B">
<!ENTITY inspectContextMenu.label "Inspect Element">
<!ENTITY inspectContextMenu.accesskey "Q">

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

@ -151,25 +151,6 @@
document.getAnonymousElementByAttribute(this, "anonid", "close-button");
</field>
<method name="contentHasChanged">
<body><![CDATA[
if (!this.isActive)
return;
// There is a race condition with getBoundingClientRect and when the
// box is displayed, the padding is ignored in the size calculation.
// A nested timeouts below are used to workaround this problem.
this.getBoundingClientRect();
setTimeout(function(self) {
setTimeout(function(self) {
let height = Math.floor(self.getBoundingClientRect().height);
self.top = window.innerHeight - height;
}, 0, self);
}, 0, this);
]]></body>
</method>
<property name="isActive" onget="return !!this.model;"/>
<field name="model">null</field>
@ -192,7 +173,6 @@
this._nextButton.disabled = document.getElementById(aModel.commands.next).getAttribute("disabled") == "true";
this.model = aModel;
this.contentHasChanged();
]]></body>
</method>
@ -204,7 +184,6 @@
this.removeAttribute("close");
this.removeAttribute("type");
this.contentHasChanged();
this.model = null;
]]></body>
</method>

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

@ -594,8 +594,6 @@ var BrowserUI = {
}
Elements.windowState.setAttribute("viewstate", currViewState);
}
// content navigator helper
document.getElementById("content-navigator").contentHasChanged();
},
_titleChanged: function(aBrowser) {
@ -761,7 +759,7 @@ var BrowserUI = {
}
// Check content helper
let contentHelper = document.getElementById("content-navigator");
let contentHelper = Elements.contentNavigator;
if (contentHelper.isActive) {
contentHelper.model.hide();
return;

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

@ -619,9 +619,9 @@ var Browser = {
let sslExceptions = new SSLExceptions();
if (json.action == "permanent")
sslExceptions.addPermanentException(uri, errorDoc.defaultView);
sslExceptions.addPermanentException(uri, window);
else
sslExceptions.addTemporaryException(uri, errorDoc.defaultView);
sslExceptions.addTemporaryException(uri, window);
} catch (e) {
dump("EXCEPTION handle content command: " + e + "\n" );
}

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

@ -39,7 +39,7 @@ var FindHelperUI = {
init: function findHelperInit() {
this._textbox = document.getElementById("find-helper-textbox");
this._container = document.getElementById("content-navigator");
this._container = Elements.contentNavigator;
this._cmdPrevious = document.getElementById(this.commands.previous);
this._cmdNext = document.getElementById(this.commands.next);
@ -50,10 +50,6 @@ var FindHelperUI = {
messageManager.addMessageListener("FindAssist:Show", this);
messageManager.addMessageListener("FindAssist:Hide", this);
// Listen for pan events happening on the browsers
Elements.browsers.addEventListener("PanBegin", this, false);
Elements.browsers.addEventListener("PanFinished", this, false);
// Listen for events where form assistant should be closed
Elements.tabList.addEventListener("TabSelect", this, true);
Elements.browsers.addEventListener("URLChanged", this, true);
@ -91,16 +87,6 @@ var FindHelperUI = {
this.hide();
break;
case "PanBegin":
this._container.style.visibility = "hidden";
this._textbox.collapsed = true;
break;
case "PanFinished":
this._container.style.visibility = "visible";
this._textbox.collapsed = false;
break;
case "keydown":
if (aEvent.keyCode == Ci.nsIDOMKeyEvent.DOM_VK_RETURN) {
if (aEvent.shiftKey) {
@ -113,6 +99,9 @@ var FindHelperUI = {
},
show: function findHelperShow() {
if (this._open)
return;
// Hide any menus
ContextUI.dismiss();
@ -124,6 +113,8 @@ var FindHelperUI = {
this._textbox.select();
this._textbox.focus();
this._open = true;
Elements.browsers.setAttribute("findbar", true);
setTimeout(() => this._container.setAttribute("showing", true), 0);
// Prevent the view to scroll automatically while searching
Browser.selectedBrowser.scrollSync = false;
@ -133,14 +124,21 @@ var FindHelperUI = {
if (!this._open)
return;
this._textbox.value = "";
this.status = null;
this._textbox.blur();
this._container.hide(this);
this._open = false;
let onTransitionEnd = () => {
this._container.removeEventListener("transitionend", onTransitionEnd, true);
this._textbox.value = "";
this.status = null;
this._textbox.blur();
this._container.hide(this);
this._open = false;
// Restore the scroll synchronisation
Browser.selectedBrowser.scrollSync = true;
// Restore the scroll synchronisation
Browser.selectedBrowser.scrollSync = true;
};
this._container.addEventListener("transitionend", onTransitionEnd, true);
this._container.removeAttribute("showing");
Elements.browsers.removeAttribute("findbar");
},
goToPrevious: function findHelperGoToPrevious() {

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

@ -194,7 +194,7 @@ var ContextMenuUI = {
// chrome calls don't need to be translated and as such
// don't provide target.
if (aMessage.target) {
if (aMessage.target && aMessage.target.localName === "browser") {
coords = aMessage.target.msgBrowserToClient(aMessage, true);
}
this._menuPopup.show(Util.extend({}, this._defaultPositionOptions, {

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

@ -37,6 +37,9 @@ static const WCHAR* kFirefoxExe = L"firefox.exe";
static const WCHAR* kMetroFirefoxExe = L"firefox.exe";
static const WCHAR* kDefaultMetroBrowserIDPathKey = L"FirefoxURL";
static bool GetDesktopBrowserPath(CStringW& aPathBuffer);
static bool GetDefaultBrowserPath(CStringW& aPathBuffer);
template <class T>void SafeRelease(T **ppT)
{
if (*ppT) {
@ -278,38 +281,49 @@ public:
bool IsDefaultBrowser()
{
bool result = false;
IApplicationAssociationRegistration* pAAR;
HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
NULL,
CLSCTX_INPROC,
IID_IApplicationAssociationRegistration,
(void**)&pAAR);
if (SUCCEEDED(hr)) {
BOOL res;
hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE,
APP_REG_NAME,
&res);
Log(L"QueryAppIsDefaultAll: %d", res);
if (!res)
return false;
// Make sure the Prog ID matches what we have
LPWSTR registeredApp;
hr = pAAR->QueryCurrentDefault(L"http", AT_URLPROTOCOL, AL_EFFECTIVE,
&registeredApp);
Log(L"QueryCurrentDefault: %X", hr);
if (SUCCEEDED(hr)) {
Log(L"registeredApp=%s", registeredApp);
result = !wcsicmp(registeredApp, kDefaultMetroBrowserIDPathKey);
CoTaskMemFree(registeredApp);
} else {
result = false;
}
if (FAILED(hr))
return false;
BOOL res = FALSE;
hr = pAAR->QueryAppIsDefaultAll(AL_EFFECTIVE,
APP_REG_NAME,
&res);
Log(L"QueryAppIsDefaultAll: %d", res);
if (!res) {
pAAR->Release();
return result;
return false;
}
return result;
// Make sure the Prog ID matches what we have
LPWSTR registeredApp;
hr = pAAR->QueryCurrentDefault(L"http", AT_URLPROTOCOL, AL_EFFECTIVE,
&registeredApp);
pAAR->Release();
Log(L"QueryCurrentDefault: %X", hr);
if (FAILED(hr))
return false;
Log(L"registeredApp=%s", registeredApp);
bool result = !wcsicmp(registeredApp, kDefaultMetroBrowserIDPathKey);
CoTaskMemFree(registeredApp);
if (!result)
return false;
// If the registry points another browser's path,
// activating the Metro browser will fail. So fallback to the desktop.
CStringW selfPath;
GetDesktopBrowserPath(selfPath);
selfPath.MakeLower();
CStringW browserPath;
GetDefaultBrowserPath(browserPath);
browserPath.MakeLower();
return selfPath == browserPath;
}
private:
~CExecuteCommandVerb()
@ -375,6 +389,32 @@ static bool GetDesktopBrowserPath(CStringW& aPathBuffer)
return true;
}
/*
* Retrieve the current default browser's path.
*
* @aPathBuffer Buffer to fill
*/
static bool GetDefaultBrowserPath(CStringW& aPathBuffer)
{
WCHAR buffer[MAX_PATH];
DWORD length = MAX_PATH;
if (FAILED(AssocQueryStringW(ASSOCF_NOTRUNCATE | ASSOCF_INIT_IGNOREUNKNOWN,
ASSOCSTR_EXECUTABLE,
kDefaultMetroBrowserIDPathKey, NULL,
buffer, &length))) {
Log(L"AssocQueryString failed.");
return false;
}
// sanity check
if (lstrcmpiW(PathFindFileNameW(buffer), kFirefoxExe))
return false;
aPathBuffer = buffer;
return true;
}
/*
* Retrieve the app model id of the firefox metro browser.
*
@ -540,6 +580,8 @@ void CExecuteCommandVerb::LaunchDesktopBrowser()
// be the browser exe or file.
CStringW params;
if (!IsTargetBrowser()) {
// Fallback to the module path if it failed to get the default browser.
GetDefaultBrowserPath(browserPath);
params += "-url ";
params += "\"";
params += mTarget;

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

@ -733,6 +733,14 @@ setting[type="radio"] > vbox {
transition-timing-function: ease-in-out;
}
#browsers browser {
transition: padding-bottom @metro_animation_duration@ @metro_animation_easing@;
}
#browsers[findbar] browser {
padding-bottom: @findbar_height@;
}
/* Panel UI ---------------------------------------------------------------- */
#panel-container {

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

@ -21,6 +21,7 @@
%define toolbar_horizontal_spacing 20px
%define toolbar_height 68px
%define tabs_height 178px
%define findbar_height 54px
%define progress_height 5px

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

@ -13,6 +13,12 @@
background-color: @metro_orange@;
bottom: 0;
position: fixed;
transition: margin-bottom @metro_animation_duration@ @metro_animation_easing@;
margin-bottom: -@findbar_height@;
}
#content-navigator[showing] {
margin-bottom: 0;
}
#content-navigator[type="find"],

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

@ -756,7 +756,7 @@ user_pref("camino.use_system_proxy_settings", false); // Camino-only, harmless t
# 841150, and 839677 (at least)
# Also (temporary) bug 870002 (mediastreamgraph)
if 'NSPR_LOG_MODULES' not in env:
env['NSPR_LOG_MODULES'] = 'signaling:5,mtransport:3,mediastreamgraph:4'
env['NSPR_LOG_MODULES'] = 'signaling:5,mtransport:3'
env['R_LOG_LEVEL'] = '5'
env['R_LOG_DESTINATION'] = 'stderr'
env['R_LOG_VERBOSE'] = '1'

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

@ -76,8 +76,9 @@ public class ASMozStub extends android.app.Service {
mStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature);
}
catch (NoSuchMethodException e) {
// Running on an older platform.
// Might be running on an older platform.
mStartForeground = mStopForeground = null;
Log.w("SUTAgent", "unable to find start/stopForeground method(s) -- older platform?");
}
try {
@ -85,6 +86,7 @@ public class ASMozStub extends android.app.Service {
}
catch (NoSuchMethodException e) {
mSetForeground = null;
Log.e("SUTAgent", "unable to find setForeground method!");
}
doToast("Listener Service created...");
@ -256,10 +258,10 @@ public class ASMozStub extends android.app.Service {
mStartForeground.invoke(this, mStartForegroundArgs);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("ScreenOnWidget", "Unable to invoke startForeground", e);
Log.e("SUTAgent", "Unable to invoke startForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("ScreenOnWidget", "Unable to invoke startForeground", e);
Log.e("SUTAgent", "Unable to invoke startForeground", e);
}
return;
}
@ -270,10 +272,13 @@ public class ASMozStub extends android.app.Service {
mSetForegroundArgs[0] = Boolean.TRUE;
mSetForeground.invoke(this, mSetForegroundArgs);
} catch (IllegalArgumentException e) {
Log.e("SUTAgent", "Unable to invoke setForeground", e);
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e("SUTAgent", "Unable to invoke setForeground", e);
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e("SUTAgent", "Unable to invoke setForeground", e);
e.printStackTrace();
}
}
@ -292,10 +297,10 @@ public class ASMozStub extends android.app.Service {
mStopForeground.invoke(this, mStopForegroundArgs);
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("ScreenOnWidget", "Unable to invoke stopForeground", e);
Log.e("SUTAgent", "Unable to invoke stopForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("ScreenOnWidget", "Unable to invoke stopForeground", e);
Log.e("SUTAgent", "Unable to invoke stopForeground", e);
}
return;
}
@ -308,10 +313,13 @@ public class ASMozStub extends android.app.Service {
mSetForegroundArgs[0] = Boolean.FALSE;
mSetForeground.invoke(this, mSetForegroundArgs);
} catch (IllegalArgumentException e) {
Log.e("SUTAgent", "Unable to invoke setForeground", e);
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e("SUTAgent", "Unable to invoke setForeground", e);
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e("SUTAgent", "Unable to invoke setForeground", e);
e.printStackTrace();
}
}

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

@ -107,7 +107,7 @@ public class DoCommand {
String ffxProvider = "org.mozilla.ffxcp";
String fenProvider = "org.mozilla.fencp";
private final String prgVersion = "SUTAgentAndroid Version 1.17";
private final String prgVersion = "SUTAgentAndroid Version 1.18";
public enum Command
{
@ -124,6 +124,7 @@ public class DoCommand {
ID ("id"),
UPTIME ("uptime"),
UPTIMEMILLIS ("uptimemillis"),
SUTUPTIMEMILLIS ("sutuptimemillis"),
SETTIME ("settime"),
SYSTIME ("systime"),
SCREEN ("screen"),
@ -429,6 +430,8 @@ public class DoCommand {
strReturn += "\n";
strReturn += GetUptimeMillis();
strReturn += "\n";
strReturn += GetSutUptimeMillis();
strReturn += "\n";
strReturn += GetScreenInfo();
strReturn += "\n";
strReturn += GetRotationInfo();
@ -486,6 +489,10 @@ public class DoCommand {
strReturn = GetUptimeMillis();
break;
case SUTUPTIMEMILLIS:
strReturn = GetSutUptimeMillis();
break;
case MEMORY:
strReturn = GetMemoryInfo();
break;
@ -3066,6 +3073,12 @@ private void CancelNotification()
return Long.toString(SystemClock.uptimeMillis());
}
public String GetSutUptimeMillis()
{
long now = System.currentTimeMillis();
return "SUTagent running for "+Long.toString(now - SUTAgentAndroid.nCreateTimeMillis)+" ms";
}
public String NewKillProc(String sProcId, OutputStream out)
{
String sRet = "";
@ -3889,6 +3902,7 @@ private void CancelNotification()
" [id] - unique identifier for device\n" +
" [uptime] - uptime for device\n" +
" [uptimemillis] - uptime for device in milliseconds\n" +
" [sutuptimemillis] - uptime for SUT in milliseconds\n" +
" [systime] - current system time\n" +
" [screen] - width, height and bits per pixel for device\n" +
" [memory] - physical, free, available, storage memory\n" +

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

@ -66,6 +66,7 @@ public class SUTAgentAndroid extends Activity
public static String sPowerStatus = null;
public static int nChargeLevel = 0;
public static int nBatteryTemp = 0;
public static long nCreateTimeMillis = System.currentTimeMillis();
String lineSep = System.getProperty("line.separator");
public PrintWriter dataOut = null;
@ -395,14 +396,12 @@ public class SUTAgentAndroid extends Activity
}
}
@Override
public void onLowMemory()
private void logMemory(String caller)
{
System.gc();
DoCommand dc = new DoCommand(getApplication());
if (dc != null)
{
log(dc, "onLowMemory");
log(dc, caller);
log(dc, dc.GetMemoryInfo());
String procInfo = dc.GetProcessInfo();
if (procInfo != null)
@ -424,10 +423,24 @@ public class SUTAgentAndroid extends Activity
}
else
{
Log.e("SUTAgentAndroid", "onLowMemory: unable to log to file!");
Log.e("SUTAgentAndroid", "logMemory: unable to log to file!");
}
}
@Override
public void onLowMemory()
{
System.gc();
logMemory("onLowMemory");
}
@Override
public void onTrimMemory(int level)
{
System.gc();
logMemory("onTrimMemory"+level);
}
private void monitorBatteryState()
{
battReceiver = new BroadcastReceiver()

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

@ -49,7 +49,8 @@ import android.os.Environment;
public class WatcherService extends Service
{
private final String prgVersion = "Watcher Version 1.15";
private final String prgVersion = "Watcher Version 1.16";
private final String LOGTAG = "Watcher";
String sErrorPrefix = "##Installer Error## ";
String currentDir = "/";
@ -60,7 +61,7 @@ public class WatcherService extends Service
boolean bStartSUTAgent = true;
boolean bStartedTimer = false;
Process pProc;
Process pProc;
Context myContext = null;
Timer myTimer = null;
private PowerManager.WakeLock pwl = null;
@ -69,12 +70,12 @@ public class WatcherService extends Service
@SuppressWarnings("unchecked")
private static final Class<?>[] mSetForegroundSignature = new Class[] {
boolean.class};
boolean.class};
@SuppressWarnings("unchecked")
private static final Class[] mStartForegroundSignature = new Class[] {
private static final Class<?>[] mStartForegroundSignature = new Class[] {
int.class, Notification.class};
@SuppressWarnings("unchecked")
private static final Class[] mStopForegroundSignature = new Class[] {
private static final Class<?>[] mStopForegroundSignature = new Class[] {
boolean.class};
private NotificationManager mNM;
@ -103,6 +104,8 @@ public class WatcherService extends Service
{
super.onCreate();
Log.i(LOGTAG, prgVersion);
myContext = this;
getKeyGuardAndWakeLock();
@ -112,7 +115,7 @@ public class WatcherService extends Service
String sIniFile = iniFile.getAbsolutePath();
String sHold = "";
Log.i("Watcher", String.format("Loading settings from %s", sIniFile));
Log.i(LOGTAG, String.format("Loading settings from %s", sIniFile));
this.sPingTarget = GetIniData("watcher", "PingTarget", sIniFile, "www.mozilla.org");
sHold = GetIniData("watcher", "delay", sIniFile, "60000");
this.lDelay = Long.parseLong(sHold.trim());
@ -120,8 +123,8 @@ public class WatcherService extends Service
this.lPeriod = Long.parseLong(sHold.trim());
sHold = GetIniData("watcher", "strikes", sIniFile,"0");
this.nMaxStrikes = Integer.parseInt(sHold.trim());
Log.i("Watcher", String.format("Pinging %s after a delay of %s sec, period of %s sec, max number of failed attempts is %s (if max # of failed attempts is 0, then no checking)",
this.sPingTarget, this.lDelay / 1000.0, this.lPeriod / 1000.0, nMaxStrikes));
Log.i(LOGTAG, String.format("Pinging %s after a delay of %s sec, period of %s sec, max number of failed attempts is %s (if max # of failed attempts is 0, then no checking)",
this.sPingTarget, this.lDelay / 1000.0, this.lPeriod / 1000.0, nMaxStrikes));
sHold = GetIniData("watcher", "StartSUTAgent", sIniFile, "true");
this.bStartSUTAgent = Boolean.parseBoolean(sHold.trim());
@ -151,7 +154,7 @@ public class WatcherService extends Service
String sLine = "";
boolean bFound = false;
BufferedReader in = null;
String sTmpFileName = fixFileName(sFile);
String sTmpFileName = fixFileName(sFile);
try {
in = new BufferedReader(new FileReader(sTmpFileName));
@ -217,19 +220,20 @@ public class WatcherService extends Service
String sOutFile = intent.getStringExtra("outFile");
boolean bReboot = intent.getBooleanExtra("reboot", true);
int nReboot = bReboot ? 1 : 0;
SendNotification("WatcherService updating " + sPkgName + " using file " + sPkgFile, "WatcherService updating " + sPkgName + " using file " + sPkgFile);
Log.i(LOGTAG, "WatcherService updating " + sPkgName + " using file " + sPkgFile);
UpdateApplication worker = new UpdateApplication(sPkgName, sPkgFile, sOutFile, nReboot);
UpdateApplication worker = new UpdateApplication(sPkgName, sPkgFile, sOutFile, nReboot);
}
else if (sCmd.equalsIgnoreCase("start"))
{
if (!this.bStartedTimer) {
if (!this.bStartedTimer)
{
doToast("WatcherService started");
myTimer = new Timer();
Date startSchedule = new Date(System.currentTimeMillis() + lDelay);
myTimer.schedule(new MyTime(), startSchedule, lPeriod);
this.bStartedTimer = true;
}
}
}
else
{
@ -244,12 +248,12 @@ public class WatcherService extends Service
PrintWriter pw = null;
String appPath = getApplicationContext().getFilesDir().getAbsolutePath();
String versionPath = appPath + "/version.txt";
Log.i("Watcher", "writing version string to: " + versionPath);
Log.i(LOGTAG, "writing version string to: " + versionPath);
try {
pw = new PrintWriter(new FileWriter(versionPath, true));
pw.println(this.prgVersion);
} catch (IOException ioe) {
Log.e("Watcher", "Exception writing version: " + this.prgVersion + " to file: " + versionPath);
Log.e(LOGTAG, "Exception writing version: " + this.prgVersion + " to file: " + versionPath);
} finally {
if (pw != null) {
pw.close();
@ -259,6 +263,7 @@ public class WatcherService extends Service
@Override
public void onStart(Intent intent, int startId) {
Log.i(LOGTAG, "onStart");
writeVersion();
handleCommand(intent);
return;
@ -266,10 +271,11 @@ public class WatcherService extends Service
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.i(LOGTAG, "onStartCommand");
writeVersion();
handleCommand(intent);
return START_STICKY;
}
}
@Override
public void onDestroy() {
@ -280,18 +286,34 @@ public class WatcherService extends Service
stopForegroundCompat(R.string.foreground_service_started);
}
@Override
public void onLowMemory() {
Log.e(LOGTAG, "onLowMemory");
System.gc();
}
@Override
public void onTrimMemory(int level) {
Log.e(LOGTAG, "onTrimMemory: "+level);
System.gc();
}
protected void getKeyGuardAndWakeLock()
{
// Fire off a thread to do some work that we shouldn't do directly in the UI thread
Thread t = new Thread() {
public void run() {
Log.i(LOGTAG, "worker thread started");
// Keep phone from locking or remove lock on screen
KeyguardManager km = (KeyguardManager)getSystemService(Context.KEYGUARD_SERVICE);
if (km != null)
{
KeyguardManager.KeyguardLock kl = km.newKeyguardLock("watcher");
if (kl != null)
{
kl.disableKeyguard();
Log.i(LOGTAG, "keyguard disabled");
}
}
// No sleeping on the job
@ -300,24 +322,39 @@ public class WatcherService extends Service
{
pwl = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, "watcher");
if (pwl != null)
{
pwl.acquire();
Log.i(LOGTAG, "wake lock acquired");
}
}
Class<?> serviceClass = null;
try {
serviceClass = Class.forName("com.mozilla.watcher.WatcherService");
}
catch (Exception e)
{
Log.e(LOGTAG, "unable to find service class: "+e.toString());
return;
}
mNM = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
try {
mStartForeground = getClass().getMethod("startForeground", mStartForegroundSignature);
mStopForeground = getClass().getMethod("stopForeground", mStopForegroundSignature);
mStartForeground = serviceClass.getMethod("startForeground", mStartForegroundSignature);
mStopForeground = serviceClass.getMethod("stopForeground", mStopForegroundSignature);
}
catch (NoSuchMethodException e)
{
// Running on an older platform.
// Might be running on an older platform.
mStartForeground = mStopForeground = null;
Log.w(LOGTAG, "unable to find start/stopForeground method(s) -- older platform?");
}
try {
mSetForeground = getClass().getMethod("setForeground", mSetForegroundSignature);
mSetForeground = serviceClass.getMethod("setForeground", mSetForegroundSignature);
}
catch (NoSuchMethodException e) {
catch (NoSuchMethodException e)
{
mSetForeground = null;
Log.e(LOGTAG, "unable to find setForeground method!");
}
Notification notification = new Notification();
startForegroundCompat(R.string.foreground_service_started, notification);
@ -337,12 +374,13 @@ public class WatcherService extends Service
mStartForegroundArgs[1] = notification;
try {
mStartForeground.invoke(this, mStartForegroundArgs);
Log.i(LOGTAG, "startForeground invoked");
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke startForeground", e);
Log.e(LOGTAG, "Unable to invoke startForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke startForeground", e);
Log.e(LOGTAG, "Unable to invoke startForeground", e);
}
return;
}
@ -352,11 +390,15 @@ public class WatcherService extends Service
try {
mSetForegroundArgs[0] = Boolean.TRUE;
mSetForeground.invoke(this, mSetForegroundArgs);
Log.i(LOGTAG, "setForeground(TRUE) invoked");
} catch (IllegalArgumentException e) {
Log.e(LOGTAG, "Unable to invoke setForeground", e);
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e(LOGTAG, "Unable to invoke setForeground", e);
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e(LOGTAG, "Unable to invoke setForeground", e);
e.printStackTrace();
}
}
@ -373,12 +415,13 @@ public class WatcherService extends Service
mStopForegroundArgs[0] = Boolean.TRUE;
try {
mStopForeground.invoke(this, mStopForegroundArgs);
Log.i(LOGTAG, "stopForeground invoked");
} catch (InvocationTargetException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke stopForeground", e);
Log.e(LOGTAG, "Unable to invoke stopForeground", e);
} catch (IllegalAccessException e) {
// Should not happen.
Log.w("ApiDemos", "Unable to invoke stopForeground", e);
Log.e(LOGTAG, "Unable to invoke stopForeground", e);
}
return;
}
@ -390,11 +433,15 @@ public class WatcherService extends Service
try {
mSetForegroundArgs[0] = Boolean.FALSE;
mSetForeground.invoke(this, mSetForegroundArgs);
Log.i(LOGTAG, "setForeground(FALSE) invoked");
} catch (IllegalArgumentException e) {
Log.e(LOGTAG, "Unable to invoke setForeground", e);
e.printStackTrace();
} catch (IllegalAccessException e) {
Log.e(LOGTAG, "Unable to invoke setForeground", e);
e.printStackTrace();
} catch (InvocationTargetException e) {
Log.e(LOGTAG, "Unable to invoke setForeground", e);
e.printStackTrace();
}
}
@ -402,20 +449,21 @@ public class WatcherService extends Service
public void doToast(String sMsg)
{
Log.i("Watcher", sMsg);
Log.i(LOGTAG, sMsg);
Toast toast = Toast.makeText(this, sMsg, Toast.LENGTH_LONG);
toast.setGravity(Gravity.TOP|Gravity.CENTER_HORIZONTAL, 0, 100);
toast.show();
}
public void CheckMem() {
System.gc();
public void CheckMem()
{
System.gc();
long lFreeMemory = Runtime.getRuntime().freeMemory();
long lTotMemory = Runtime.getRuntime().totalMemory();
long lMaxMemory = Runtime.getRuntime().maxMemory();
SendNotification("Memory Check", "Free: " + lFreeMemory + "Total: " + lTotMemory + "Max: " + lMaxMemory);
}
Log.i(LOGTAG, "Free: " + lFreeMemory + "Total: " + lTotMemory + "Max: " + lMaxMemory);
}
public int UpdtApp(String sPkgName, String sPkgFileName, String sOutFile, int bReboot)
{
@ -423,45 +471,43 @@ public class WatcherService extends Service
int lcv = 0;
String sRet = "";
// Debug.waitForDebugger();
FileOutputStream f = null;
try {
SendNotification("Killing " + sPkgName, "Step 1: Kill " + sPkgName + " if running");
try {
Log.i(LOGTAG, "Step 1: Kill " + sPkgName + " if running");
while (!IsProcessDead(sPkgName) && (lcv < 5)) {
if (KillProcess(sPkgName, null).startsWith("Successfully"))
break;
else
lcv++;
Thread.sleep(2000);
}
Thread.sleep(2000);
}
CheckMem();
CheckMem();
if ((sOutFile != null) && (sOutFile.length() > 0)) {
File outFile = new File(sOutFile);
if (outFile.exists() && outFile.canWrite()) {
f = new FileOutputStream(outFile, true);
} else {
SendNotification("File not found or cannot write to " + sOutFile, "File not found or cannot write to " + sOutFile);
Log.e(LOGTAG, "File not found or cannot write to " + sOutFile);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
e.printStackTrace();
} catch (FileNotFoundException e) {
SendNotification("File not found " + sOutFile, "Couldn't open " + sOutFile + " " + e.getLocalizedMessage());
Log.e(LOGTAG, "Couldn't open " + sOutFile + " " + e.getLocalizedMessage());
e.printStackTrace();
} catch (SecurityException e) {
SendNotification("Security excepetion for " + sOutFile, "Exception message " + e.getLocalizedMessage());
} catch (SecurityException e) {
Log.e(LOGTAG, "Exception message " + e.getLocalizedMessage());
e.printStackTrace();
}
}
if ((sPkgName != null) && (sPkgName.length() > 0))
{
SendNotification("Uninstalling " + sPkgName, "Step 2: Uninstall " + sPkgName);
Log.i(LOGTAG, "Step 2: Uninstall " + sPkgName);
sRet = UnInstallApp(sPkgName, null);
CheckMem();
CheckMem();
if ((sRet.length() > 0) && (f != null))
{
try {
@ -477,10 +523,10 @@ public class WatcherService extends Service
if ((sPkgFileName != null) && (sPkgFileName.length() > 0))
{
SendNotification("Installing " + sPkgFileName, "Step 3: Install " + sPkgFileName);
Log.i(LOGTAG, "Step 3: Install " + sPkgFileName);
sRet = InstallApp(sPkgFileName, null);
SendNotification("Installed " + sPkgFileName, "" + sRet);
CheckMem();
Log.i(LOGTAG, "" + sRet);
CheckMem();
if ((sRet.length() > 0) && (f != null))
{
try {
@ -533,7 +579,7 @@ public class WatcherService extends Service
theArgs[0] = "su";
theArgs[1] = "-c";
theArgs[2] = "reboot";
Log.i("Watcher", "Running reboot!");
Log.i(LOGTAG, "Running reboot!");
try
{
@ -568,7 +614,7 @@ public class WatcherService extends Service
List <ActivityManager.RunningAppProcessInfo> lProcesses = aMgr.getRunningAppProcesses();
int lcv = 0;
String strProcName = "";
int nPID = 0;
int nPID = 0;
int nProcs = 0;
if (lProcesses != null)
@ -698,22 +744,6 @@ public class WatcherService extends Service
return(sRet);
}
public String GetTmpDir()
{
String sRet = "";
Context ctx = getApplicationContext();
File dir = ctx.getFilesDir();
ctx = null;
try {
sRet = dir.getCanonicalPath();
}
catch (IOException e)
{
e.printStackTrace();
}
return(sRet);
}
public String UnInstallApp(String sApp, OutputStream out)
{
String sRet = "";
@ -782,11 +812,11 @@ public class WatcherService extends Service
private String SendPing(String sIPAddr)
{
Process pProc;
Process pProc;
String sRet = "";
String [] theArgs = new String [4];
boolean bStillRunning = true;
int nBytesOut = 0;
int nBytesOut = 0;
int nBytesErr = 0;
int nBytesRead = 0;
byte[] buffer = new byte[1024];
@ -795,7 +825,7 @@ public class WatcherService extends Service
theArgs[1] = "-c";
theArgs[2] = "3";
theArgs[3] = sIPAddr;
Log.i("Watcher", "Pinging " + sIPAddr);
Log.i(LOGTAG, "Pinging " + sIPAddr);
try
{
@ -872,7 +902,7 @@ public class WatcherService extends Service
e.printStackTrace();
}
Log.i("Watcher", String.format("Ping result was: '%s'", sRet.trim()));
Log.i(LOGTAG, String.format("Ping result was: '%s'", sRet.trim()));
return (sRet);
}
@ -903,7 +933,7 @@ public class WatcherService extends Service
String msPkgName = "";
String msPkgFileName = "";
String msOutFile = "";
int mbReboot = 0;
int mbReboot = 0;
public UpdateApplication(String sPkgName, String sPkgFileName, String sOutFile, int bReboot) {
runner = new Thread(this);
@ -915,9 +945,9 @@ public class WatcherService extends Service
}
public void run() {
bInstalling = true;
bInstalling = true;
UpdtApp(msPkgName, msPkgFileName, msOutFile, mbReboot);
bInstalling = false;
bInstalling = false;
}
}
@ -944,11 +974,11 @@ public class WatcherService extends Service
String sRet = SendPing(sPingTarget);
if (!sRet.contains("3 received"))
{
Log.i("Watcher", String.format("Failed ping attempt (remaining: %s)!",
nMaxStrikes - nStrikes));
Log.i(LOGTAG, String.format("Failed ping attempt (remaining: %s)!",
nMaxStrikes - nStrikes));
if (++nStrikes >= nMaxStrikes)
{
Log.e("Watcher", String.format("Number of failed ping attempts to %s (%s) exceeded maximum (%s), running reboot!", sPingTarget, nStrikes, nMaxStrikes));
Log.e(LOGTAG, String.format("Number of failed ping attempts to %s (%s) exceeded maximum (%s), running reboot!", sPingTarget, nStrikes, nMaxStrikes));
RunReboot(null);
}
}
@ -960,18 +990,16 @@ public class WatcherService extends Service
String sProgramName = "com.mozilla.SUTAgentAndroid";
// Debug.waitForDebugger();
// Ensure the sdcard is mounted before we even attempt to start the agent
// We will wait for the sdcard to mount for PERIODS_TO_WAIT_FOR_SDCARD
// after which time we go ahead and attempt to start the agent.
if (nPeriodsWaited++ < PERIODS_TO_WAIT_FOR_SDCARD) {
String state = Environment.getExternalStorageState();
if (Environment.MEDIA_MOUNTED.compareTo(state) != 0) {
Log.i("SUTAgentWatcher", "SDcard not mounted, waiting another turn");
Log.i(LOGTAG, "SDcard not mounted, waiting another turn");
return;
} else if (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)) {
Log.e("SUTAgentWatcher", "SDcard mounted read only not starting agent now, try again in 60s");
Log.e(LOGTAG, "SDcard mounted read only not starting agent now, try again in 60s");
return;
}
}
@ -979,7 +1007,7 @@ public class WatcherService extends Service
boolean isProc = GetProcessInfo(sProgramName);
if (bStartSUTAgent && !isProc)
{
Log.i("SUTAgentWatcher", "Starting SUTAgent from watcher code");
Log.i(LOGTAG, "Starting SUTAgent from watcher code");
Intent agentIntent = new Intent();
agentIntent.setPackage(sProgramName);
agentIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@ -1013,14 +1041,4 @@ public class WatcherService extends Service
}
}
}
private void SendNotification(String tickerText, String expandedText) {
Log.i("Watcher", expandedText);
}
private void CancelNotification() {
NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.cancel(NOTIFICATION_ID);
}
}

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

@ -1,8 +1,9 @@
. "$topsrcdir/build/unix/mozconfig.linux"
if test `uname -m` = "x86_64"; then
CC="$CC -m32"
CXX="$CXX -m32"
# -march=pentiumpro is what our 32-bit native toolchain defaults to
CC="$CC -m32 -march=pentiumpro"
CXX="$CXX -m32 -march=pentiumpro"
ac_add_options --target=i686-pc-linux
ac_add_options --x-libraries=/usr/lib
export PKG_CONFIG_LIBDIR=/usr/lib/pkgconfig:/usr/share/pkgconfig

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

@ -1728,7 +1728,6 @@ public:
#undef EVENT
protected:
static void Trace(nsINode *tmp, const TraceCallbacks &cb, void *closure);
static bool Traverse(nsINode *tmp, nsCycleCollectionTraversalCallback &cb);
static void Unlink(nsINode *tmp);

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

@ -66,9 +66,7 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Attr)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mAttrMap)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Attr)
nsINode::Trace(tmp, aCallbacks, aClosure);
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Attr)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Attr)
nsINode::Unlink(tmp);

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

@ -460,12 +460,12 @@ public:
void RemoveEntry(nsINode* aTextNode, Element* aElement)
{
if (mElements.Contains(aElement)) {
mElements.Remove(aElement);
NS_ASSERTION(mElements.Contains(aElement),
"element already removed from map");
aElement->ClearHasDirAutoSet();
aElement->UnsetProperty(nsGkAtoms::dirAutoSetBy);
}
mElements.Remove(aElement);
aElement->ClearHasDirAutoSet();
aElement->UnsetProperty(nsGkAtoms::dirAutoSetBy);
}
private:

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

@ -1156,9 +1156,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FragmentOrElement)
}
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(FragmentOrElement)
nsINode::Trace(tmp, aCallbacks, aClosure);
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FragmentOrElement)
void
FragmentOrElement::MarkUserData(void* aObject, nsIAtom* aKey, void* aChild,

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

@ -1803,7 +1803,7 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsDocument)
if (tmp->PreservingWrapper()) {
NS_IMPL_CYCLE_COLLECTION_TRACE_JSVAL_MEMBER_CALLBACK(mExpandoAndGeneration.expando);
}
nsINode::Trace(tmp, aCallbacks, aClosure);
NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_TRACE_END
@ -2500,29 +2500,30 @@ nsDocument::InitCSP(nsIChannel* aChannel)
}
}
// ----- Figure out if we need to apply an app default CSP
// Figure out if we need to apply an app default CSP or a CSP from an app manifest
bool applyAppDefaultCSP = false;
bool applyAppManifestCSP = false;
nsIPrincipal* principal = NodePrincipal();
uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
bool unknownAppId;
uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
nsAutoString appManifestCSP;
if (NS_SUCCEEDED(principal->GetUnknownAppId(&unknownAppId)) &&
!unknownAppId &&
NS_SUCCEEDED(principal->GetAppStatus(&appStatus))) {
applyAppDefaultCSP = ( appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED ||
appStatus == nsIPrincipal::APP_STATUS_CERTIFIED);
// Bug 773981. Allow a per-app policy from the manifest.
// Just read the CSP from the manifest into cspHeaderValue.
// That way we don't have to change the rest of the function logic
if (applyAppDefaultCSP || appStatus == nsIPrincipal::APP_STATUS_INSTALLED) {
nsCOMPtr<nsIAppsService> appsService =
do_GetService(APPS_SERVICE_CONTRACTID);
if (appsService) {
uint32_t appId;
if ( NS_SUCCEEDED(principal->GetAppId(&appId)) ) {
appsService->GetCSPByLocalId(appId, cspHeaderValue);
if (appStatus != nsIPrincipal::APP_STATUS_NOT_INSTALLED) {
nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID);
if (appsService) {
uint32_t appId = 0;
if (NS_SUCCEEDED(principal->GetAppId(&appId))) {
appsService->GetCSPByLocalId(appId, appManifestCSP);
if (!appManifestCSP.IsEmpty()) {
applyAppManifestCSP = true;
}
}
}
}
@ -2534,6 +2535,7 @@ nsDocument::InitCSP(nsIChannel* aChannel)
// If there's no CSP to apply, go ahead and return early
if (!applyAppDefaultCSP &&
!applyAppManifestCSP &&
cspHeaderValue.IsEmpty() &&
cspROHeaderValue.IsEmpty() &&
cspOldHeaderValue.IsEmpty() &&
@ -2572,7 +2574,13 @@ nsDocument::InitCSP(nsIChannel* aChannel)
// Store the request context for violation reports
csp->ScanRequestData(httpChannel);
// ----- process the app default policy, if necessary
// The CSP is refined in the following order:
// 1. Default app CSP, if applicable
// 2. App manifest CSP, if provided
// 3. HTTP header CSP, if provided
// Note that since each application of refinePolicy is a set intersection,
// the order in which multiple CSP's are refined does not matter.
if (applyAppDefaultCSP) {
nsAdoptingString appCSP;
if (appStatus == nsIPrincipal::APP_STATUS_PRIVILEGED) {
@ -2588,6 +2596,11 @@ nsDocument::InitCSP(nsIChannel* aChannel)
csp->RefinePolicy(appCSP, chanURI, specCompliantEnabled);
}
if (applyAppManifestCSP) {
// Use the 1.0 CSP parser for apps if the pref to do so is set.
csp->RefinePolicy(appManifestCSP, chanURI, specCompliantEnabled);
}
// While we are supporting both CSP 1.0 and the x- headers, the 1.0 headers
// take priority. If any spec-compliant headers are present, the x- headers
// are ignored, and the spec compliant parser is used.
@ -6854,7 +6867,7 @@ nsDocument::GetViewportInfo(uint32_t aDisplayWidth,
mWidthStrEmpty = widthStr.IsEmpty();
mValidScaleFloat = !scaleStr.IsEmpty() && NS_SUCCEEDED(scaleErrorCode);
mValidMaxScale = !maxScaleStr.IsEmpty() && NS_SUCCEEDED(scaleMaxErrorCode);
mViewportType = Specified;
}
case Specified:

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

@ -62,9 +62,7 @@ nsGenericDOMDataNode::~nsGenericDOMDataNode()
}
}
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsGenericDOMDataNode)
nsINode::Trace(tmp, aCallbacks, aClosure);
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(nsGenericDOMDataNode)
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(nsGenericDOMDataNode)
return Element::CanSkip(tmp, aRemovingAllowed);

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

@ -1163,14 +1163,6 @@ nsINode::GetContextForEventHandlers(nsresult* aRv)
return nsContentUtils::GetContextForEventHandlers(this, aRv);
}
/* static */
void
nsINode::Trace(nsINode *tmp, const TraceCallbacks& cb, void *closure)
{
tmp->TraceWrapper(cb, closure);
}
bool
nsINode::UnoptimizableCCNode() const
{

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

@ -629,6 +629,7 @@ MOCHITEST_FILES_C= \
test_bug868999.html \
test_bug869000.html \
test_bug869002.html \
test_bug876282.html \
$(NULL)
# OOP tests don't work on Windows (bug 763081) or native-fennec

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

@ -0,0 +1,45 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=647518
-->
<head>
<title>Test for Bug 647518</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=647518">Mozilla Bug 647518</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 647518 **/
SimpleTest.waitForExplicitFinish();
var counter = 3;
var called = false;
var handle1 = window.requestAnimationFrame(function() {
called = true;
});
ok(handle1 > 0, "Should get back a nonzero handle");
function checker() {
--counter;
if (counter == 0) {
is(called, false, "Canceled callback should not have been called");
SimpleTest.finish();
} else {
window.requestAnimationFrame(checker);
}
}
window.requestAnimationFrame(checker);
window.cancelAnimationFrame(handle1);
</script>
</pre>
</body>
</html>

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

@ -43,6 +43,8 @@ class MediaResource;
class MediaDecoder;
}
class nsITimer;
namespace mozilla {
namespace dom {
@ -533,6 +535,8 @@ protected:
WakeLockBoolWrapper(bool val = false)
: mValue(val), mCanPlay(true), mOuter(nullptr) {}
~WakeLockBoolWrapper();
void SetOuter(HTMLMediaElement* outer) { mOuter = outer; }
void SetCanPlay(bool aCanPlay);
@ -542,12 +546,15 @@ protected:
bool operator !() const { return !mValue; }
static void TimerCallback(nsITimer* aTimer, void* aClosure);
private:
void UpdateWakeLock();
bool mValue;
bool mCanPlay;
HTMLMediaElement* mOuter;
nsCOMPtr<nsITimer> mTimer;
};
/**

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

@ -1987,6 +1987,8 @@ HTMLMediaElement::~HTMLMediaElement()
if (mAudioStream) {
mAudioStream->Shutdown();
}
WakeLockRelease();
}
void
@ -2106,7 +2108,8 @@ NS_IMETHODIMP HTMLMediaElement::Play()
}
HTMLMediaElement::WakeLockBoolWrapper&
HTMLMediaElement::WakeLockBoolWrapper::operator=(bool val) {
HTMLMediaElement::WakeLockBoolWrapper::operator=(bool val)
{
if (mValue == val) {
return *this;
}
@ -2116,6 +2119,13 @@ HTMLMediaElement::WakeLockBoolWrapper::operator=(bool val) {
return *this;
}
HTMLMediaElement::WakeLockBoolWrapper::~WakeLockBoolWrapper()
{
if (mTimer) {
mTimer->Cancel();
}
}
void
HTMLMediaElement::WakeLockBoolWrapper::SetCanPlay(bool aCanPlay)
{
@ -2133,12 +2143,30 @@ HTMLMediaElement::WakeLockBoolWrapper::UpdateWakeLock()
bool playing = (!mValue && mCanPlay);
if (playing) {
if (mTimer) {
mTimer->Cancel();
mTimer = nullptr;
}
mOuter->WakeLockCreate();
} else {
mOuter->WakeLockRelease();
} else if (!mTimer) {
// Don't release the wake lock immediately; instead, release it after a
// grace period.
int timeout = Preferences::GetInt("media.wakelock_timeout", 2000);
mTimer = do_CreateInstance("@mozilla.org/timer;1");
mTimer->InitWithFuncCallback(TimerCallback, this, timeout,
nsITimer::TYPE_ONE_SHOT);
}
}
void
HTMLMediaElement::WakeLockBoolWrapper::TimerCallback(nsITimer* aTimer,
void* aClosure)
{
WakeLockBoolWrapper* wakeLock = static_cast<WakeLockBoolWrapper*>(aClosure);
wakeLock->mOuter->WakeLockRelease();
wakeLock->mTimer = nullptr;
}
void
HTMLMediaElement::WakeLockCreate()
{

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

@ -36,6 +36,36 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=549475
* The value sanitization algorithm is just an internal spec concept after all.
*/
// We buffer up the results of sets of sub-tests, and avoid outputting log
// entries for them all if they all pass. Otherwise, we have an enormous amount
// of test output.
var delayedTests = [];
var anyFailedDelayedTests = false;
function delayed_is(actual, expected, description)
{
var result = actual == expected;
delayedTests.push({ actual: actual, expected: expected, description: description });
if (!result) {
anyFailedDelayedTests = true;
}
}
function flushDelayedTests(description)
{
if (anyFailedDelayedTests) {
info("Outputting individual results for \"" + description + "\" due to failures in subtests");
for (var test of delayedTests) {
is(test.actual, test.expected, test.description);
}
} else {
ok(true, description + " (" + delayedTests.length + " subtests)");
}
delayedTests = [];
anyFailedDelayedTests = false;
}
// We are excluding "file" because it's too different from the other types.
// And it has no sanitizing algorithm.
var inputTypes =
@ -157,7 +187,7 @@ function sanitizeValue(aType, aValue)
}
}
function checkSanitizing(element)
function checkSanitizing(element, inputTypeDescription)
{
var testData =
[
@ -302,17 +332,17 @@ function checkSanitizing(element)
for (value of testData) {
element.setAttribute('value', value);
is(element.value, sanitizeValue(type, value),
delayed_is(element.value, sanitizeValue(type, value),
"The value has not been correctly sanitized for type=" + type);
is(element.getAttribute('value'), value,
delayed_is(element.getAttribute('value'), value,
"The content value should not have been sanitized");
if (type in valueModeValue) {
element.setAttribute('value', 'tulip');
element.value = value;
is(element.value, sanitizeValue(type, value),
delayed_is(element.value, sanitizeValue(type, value),
"The value has not been correctly sanitized for type=" + type);
is(element.getAttribute('value'), 'tulip',
delayed_is(element.getAttribute('value'), 'tulip',
"The content value should not have been sanitized");
}
@ -320,26 +350,28 @@ function checkSanitizing(element)
form.reset();
element.type = 'checkbox'; // We know this type has no sanitizing algorithm.
element.setAttribute('value', value);
is(element.value, value, "The value should not have been sanitized");
delayed_is(element.value, value, "The value should not have been sanitized");
element.type = type;
is(element.value, sanitizeValue(type, value),
delayed_is(element.value, sanitizeValue(type, value),
"The value has not been correctly sanitized for type=" + type);
is(element.getAttribute('value'), value,
delayed_is(element.getAttribute('value'), value,
"The content value should not have been sanitized");
element.setAttribute('value', '');
form.reset();
element.setAttribute('value', value);
form.reset();
is(element.value, sanitizeValue(type, value),
delayed_is(element.value, sanitizeValue(type, value),
"The value has not been correctly sanitized for type=" + type);
is(element.getAttribute('value'), value,
delayed_is(element.getAttribute('value'), value,
"The content value should not have been sanitized");
// Cleaning-up.
element.setAttribute('value', '');
form.reset();
}
flushDelayedTests(inputTypeDescription);
}
for (type of inputTypes) {
@ -349,17 +381,17 @@ for (type of inputTypes) {
element.type = type;
form.appendChild(element);
checkSanitizing(element); // no frame, no editor
checkSanitizing(element, "type=" + type + ", no frame, no editor");
element.style.display = "";
checkSanitizing(element); // frame, no editor
checkSanitizing(element, "type=" + type + ", frame, no editor");
element.focus();
element.blur();
checkSanitizing(element); // frame, editor
checkSanitizing(element, "type=" + type + ", frame, editor");
element.style.display = "none";
checkSanitizing(element); // no frame, editor
checkSanitizing(element, "type=" + type + ", no frame, editor");
form.removeChild(element);
}

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

@ -20,8 +20,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=868943
/** Test for Bug 868943 **/
SpecialPowers.addPermission("power", true, document);
function testAudioPlayPause() {
var lockState = true;
var count = 0;
@ -32,9 +30,11 @@ function testAudioPlayPause() {
audio.src = "wakelock.ogg";
content.appendChild(audio);
var startDate;
audio.addEventListener('progress', function() {
lockState = false;
audio.pause();
startDate = new Date();
});
navigator.mozPower.addWakeLockListener(function testAudioPlayListener(topic, state) {
@ -49,6 +49,9 @@ function testAudioPlayPause() {
// count == 2 is when the cpu wakelock is released
if (count == 2) {
var diffDate = (new Date() - startDate);
ok(diffDate > 200, "There was at least 200 milliseconds between the stop and the wakelock release");
content.removeChild(audio);
navigator.mozPower.removeWakeLockListener(testAudioPlayListener);
runTests();
@ -68,6 +71,11 @@ function testAudioPlay() {
audio.src = "wakelock.ogg";
content.appendChild(audio);
var startDate;
audio.addEventListener('progress', function() {
startDate = new Date();
});
navigator.mozPower.addWakeLockListener(function testAudioPlayListener(topic, state) {
is(topic, "cpu", "Audio element locked the target == cpu");
var locked = state == "locked-foreground" ||
@ -84,6 +92,9 @@ function testAudioPlay() {
// The next step is to unlock the resource.
lockState = false;
} else if (count == 2) {
var diffDate = (new Date() - startDate);
ok(diffDate > 200, "There was at least 200 milliseconds between the stop and the wakelock release");
content.removeChild(audio);
navigator.mozPower.removeWakeLockListener(testAudioPlayListener);
runTests();
@ -104,8 +115,10 @@ function runTests() {
test();
};
SpecialPowers.addPermission("power", true, document);
SpecialPowers.pushPrefEnv({"set": [["media.wakelock_timeout", 500]]}, runTests);
SimpleTest.waitForExplicitFinish();
runTests();
</script>
</pre>

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

@ -20,8 +20,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=868943
/** Test for Bug 868943 **/
SpecialPowers.addPermission("power", true, document);
function testVideoPlayPause() {
var lockState = true;
var count = 0;
@ -32,6 +30,15 @@ function testVideoPlayPause() {
video.src = "wakelock.ogv";
content.appendChild(video);
var startDate;
video.addEventListener('progress', function() {
startDate = new Date();
// The next step is to unlock the resource.
lockState = false;
video.pause();
});
navigator.mozPower.addWakeLockListener(function testVideoPlayPauseListener(topic, state) {
is(topic, "screen", "Video element locked the target == screen");
var locked = state == "locked-foreground" ||
@ -40,11 +47,10 @@ function testVideoPlayPause() {
is(locked, lockState, "Video element locked the screen - paused");
count++;
if (count == 1) {
// The next step is to unlock the resource.
lockState = false;
video.pause();
} else if (count == 2) {
if (count == 2) {
var diffDate = (new Date() - startDate);
ok(diffDate > 200, "There was at least 200 milliseconds between the stop and the wakelock release");
content.removeChild(video);
navigator.mozPower.removeWakeLockListener(testVideoPlayPauseListener);
runTests();
@ -64,6 +70,11 @@ function testVideoPlay() {
video.src = "wakelock.ogv";
content.appendChild(video);
var startDate;
video.addEventListener('progress', function() {
startDate = new Date();
});
navigator.mozPower.addWakeLockListener(function testVideoPlayListener(topic, state) {
is(topic, "screen", "Video element locked the target == screen");
var locked = state == "locked-foreground" ||
@ -76,6 +87,9 @@ function testVideoPlay() {
// The next step is to unlock the resource.
lockState = false;
} else if (count == 2) {
var diffDate = (new Date() - startDate);
ok(diffDate > 200, "There was at least milliseconds between the stop and the wakelock release");
content.removeChild(video);
navigator.mozPower.removeWakeLockListener(testVideoPlayListener);
runTests();
@ -96,8 +110,10 @@ function runTests() {
test();
};
SpecialPowers.addPermission("power", true, document);
SpecialPowers.pushPrefEnv({"set": [["media.wakelock_timeout", 500]]}, runTests);
SimpleTest.waitForExplicitFinish();
runTests();
</script>
</pre>

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

@ -0,0 +1,27 @@
<script>
var Context0= new AudioContext()
var BufferSource0=Context0.createBufferSource();
var BiquadFilter0=Context0.createBiquadFilter();
BufferSource0.start(0,0.6167310480959713,0.7142638498917222);
BiquadFilter0.connect(Context0.destination);
BufferSource0.buffer=function(){
var length=86333;
var Buffer=Context0.createBuffer(1,length,Context0.sampleRate);
var bufferData= Buffer.getChannelData(0);
for (var i = 0; i < length; ++i) { bufferData[i] = Math.sin(i*(-1))};
return Buffer;
}();
BufferSource0.connect(BiquadFilter0);
BufferSource0.buffer=function(){
var length=21989;
var Buffer=Context0.createBuffer(1,length,Context0.sampleRate);
var bufferData= Buffer.getChannelData(0);
for (var i = 0; i < length; ++i) { bufferData[i] = Math.sin(i*(0))};
return Buffer;
}();
BufferSource0.stop(0.04184641852043569);
</script>

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

@ -0,0 +1,4 @@
<!DOCTYPE html>
<script>
OfflineAudioContext(0, 0, 3229622);
</script>

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

@ -22,4 +22,6 @@ load 874952.html
load 875144.html
load 875596.html
load 875911.html
load 876249.html
load 876252.html
load 876834.html

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

@ -25,6 +25,7 @@
#include "ChannelMergerNode.h"
#include "ChannelSplitterNode.h"
#include "WaveShaperNode.h"
#include "WaveTable.h"
#include "nsNetUtil.h"
// Note that this number is an arbitrary large value to protect against OOM
@ -108,7 +109,7 @@ AudioContext::Constructor(const GlobalObject& aGlobal,
return nullptr;
}
if (aSampleRate <= 0.0f) {
if (aSampleRate <= 0.0f || aSampleRate >= TRACK_RATE_MAX) {
// The DOM binding protects us against infinity and NaN
aRv.Throw(NS_ERROR_DOM_SYNTAX_ERR);
return nullptr;
@ -308,6 +309,24 @@ AudioContext::CreateBiquadFilter()
return filterNode.forget();
}
already_AddRefed<WaveTable>
AudioContext::CreateWaveTable(const Float32Array& aRealData,
const Float32Array& aImagData,
ErrorResult& aRv)
{
if (aRealData.Length() != aImagData.Length() ||
aRealData.Length() == 0 ||
aRealData.Length() > 4096) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
nsRefPtr<WaveTable> waveTable =
new WaveTable(this, aRealData.Data(), aRealData.Length(),
aImagData.Data(), aImagData.Length());
return waveTable.forget();
}
AudioListener*
AudioContext::Listener()
{

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

@ -54,6 +54,7 @@ class OfflineRenderSuccessCallback;
class PannerNode;
class ScriptProcessorNode;
class WaveShaperNode;
class WaveTable;
class AudioContext MOZ_FINAL : public nsDOMEventTargetHelper,
public EnableWebAudioCheck
@ -178,6 +179,10 @@ public:
already_AddRefed<BiquadFilterNode>
CreateBiquadFilter();
already_AddRefed<WaveTable>
CreateWaveTable(const Float32Array& aRealData, const Float32Array& aImagData,
ErrorResult& aRv);
void DecodeAudioData(const ArrayBuffer& aBuffer,
DecodeSuccessCallback& aSuccessCallback,
const Optional<OwningNonNull<DecodeErrorCallback> >& aFailureCallback);

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

@ -0,0 +1,38 @@
/* -*- 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/. */
#include "WaveTable.h"
#include "AudioContext.h"
#include "mozilla/dom/WaveTableBinding.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_1(WaveTable, mContext)
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WaveTable, AddRef)
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WaveTable, Release)
WaveTable::WaveTable(AudioContext* aContext,
const float* aRealData,
uint32_t aRealDataLength,
const float* aImagData,
uint32_t aImagDataLength)
: mContext(aContext)
{
MOZ_ASSERT(aContext);
SetIsDOMBinding();
}
JSObject*
WaveTable::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
{
return WaveTableBinding::Wrap(aCx, aScope, this);
}
}
}

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

@ -0,0 +1,50 @@
/* -*- 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/. */
#ifndef WaveTable_h_
#define WaveTable_h_
#include "nsWrapperCache.h"
#include "nsCycleCollectionParticipant.h"
#include "mozilla/Attributes.h"
#include "EnableWebAudioCheck.h"
#include "AudioContext.h"
#include "nsAutoPtr.h"
namespace mozilla {
namespace dom {
class WaveTable MOZ_FINAL : public nsWrapperCache,
public EnableWebAudioCheck
{
public:
WaveTable(AudioContext* aContext,
const float* aRealData,
uint32_t aRealDataLength,
const float* aImagData,
uint32_t aImagDataLength);
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WaveTable)
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WaveTable)
AudioContext* GetParentObject() const
{
return mContext;
}
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aScope) MOZ_OVERRIDE;
private:
nsRefPtr<AudioContext> mContext;
};
}
}
#endif

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

@ -38,6 +38,7 @@ EXPORTS.mozilla.dom += [
'PannerNode.h',
'ScriptProcessorNode.h',
'WaveShaperNode.h',
'WaveTable.h',
]
CPP_SOURCES += [
@ -63,6 +64,7 @@ CPP_SOURCES += [
'ScriptProcessorNode.cpp',
'ThreeDPoint.cpp',
'WaveShaperNode.cpp',
'WaveTable.cpp',
'WebAudioUtils.cpp',
]

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

@ -69,6 +69,7 @@ MOCHITEST_FILES := \
test_waveShaper.html \
test_waveShaperNoCurve.html \
test_waveShaperZeroLengthCurve.html \
test_waveTable.html \
ting.ogg \
ting-expected.wav \
ting-dualchannel44.1.ogg \

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

@ -0,0 +1,36 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Test the WaveTable interface</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="webaudio.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
<pre id="test">
<script class="testbody" type="text/javascript">
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
SpecialPowers.setBoolPref("media.webaudio.enabled", true);
var ac = new AudioContext();
var real = new Float32Array(4096);
var imag = new Float32Array(4096);
var table = ac.createWaveTable(real, imag);
expectException(function() {
ac.createWaveTable(new Float32Array(512), imag);
}, DOMException.NOT_SUPPORTED_ERR);
expectException(function() {
ac.createWaveTable(new Float32Array(0), new Float32Array(0));
}, DOMException.NOT_SUPPORTED_ERR);
expectException(function() {
ac.createWaveTable(new Float32Array(4097), new Float32Array(4097));
}, DOMException.NOT_SUPPORTED_ERR);
SpecialPowers.clearUserPref("media.webaudio.enabled");
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

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

@ -20,6 +20,10 @@ Cu.import("resource://gre/modules/AlarmDB.jsm");
this.EXPORTED_SYMBOLS = ["AlarmService"];
XPCOMUtils.defineLazyGetter(this, "appsService", function() {
return Cc["@mozilla.org/AppsService;1"].getService(Ci.nsIAppsService);
});
XPCOMUtils.defineLazyServiceGetter(this, "ppmm",
"@mozilla.org/parentprocessmessagemanager;1",
"nsIMessageListenerManager");
@ -49,6 +53,7 @@ this.AlarmService = {
init: function init() {
debug("init()");
Services.obs.addObserver(this, "profile-change-teardown", false);
Services.obs.addObserver(this, "webapps-clear-data",false);
this._currentTimezoneOffset = (new Date()).getTimezoneOffset();
@ -496,12 +501,30 @@ this.AlarmService = {
switch (aTopic) {
case "profile-change-teardown":
this.uninit();
break;
case "webapps-clear-data":
let params =
aSubject.QueryInterface(Ci.mozIApplicationClearPrivateDataParams);
let manifestURL = appsService.getManifestURLByLocalId(params.appId);
this._db.getAll(
manifestURL,
function getAllSuccessCb(aAlarms) {
aAlarms.forEach(function removeAlarm(aAlarm) {
this.remove(aAlarm.id, manifestURL);
}, this);
}.bind(this),
function getAllErrorCb(aErrorMsg) {
throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
}
);
break;
}
},
uninit: function uninit() {
debug("uninit()");
Services.obs.removeObserver(this, "profile-change-teardown");
Services.obs.removeObserver(this, "webapps-clear-data");
this._messages.forEach(function(aMsgName) {
ppmm.removeMessageListener(aMsgName, this);

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

@ -4429,13 +4429,19 @@ nsGlobalWindow::RequestAnimationFrame(const JS::Value& aCallback,
NS_IMETHODIMP
nsGlobalWindow::MozCancelRequestAnimationFrame(int32_t aHandle)
{
return MozCancelAnimationFrame(aHandle);
return CancelAnimationFrame(aHandle);
}
NS_IMETHODIMP
nsGlobalWindow::MozCancelAnimationFrame(int32_t aHandle)
{
FORWARD_TO_INNER(MozCancelAnimationFrame, (aHandle),
return CancelAnimationFrame(aHandle);
}
NS_IMETHODIMP
nsGlobalWindow::CancelAnimationFrame(int32_t aHandle)
{
FORWARD_TO_INNER(CancelAnimationFrame, (aHandle),
NS_ERROR_NOT_INITIALIZED);
if (!mDoc) {

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

@ -52,7 +52,20 @@ def main():
if all(f.endswith("Binding") or f == "ParserResults.pkl" for f in changedDeps):
toRegenerate = filter(lambda f: f.endswith("Binding"), changedDeps)
toRegenerate = map(lambda f: f[:-len("Binding")] + ".webidl", toRegenerate)
if len(toRegenerate) == 0 and len(changedDeps) == 1:
# Work around build system bug 874923: if we get here that means
# that changedDeps contained only one entry and it was
# "ParserResults.pkl". That should never happen: if the
# ParserResults.pkl changes then either one of the globalgen files
# changed (in which case we wouldn't be in this "only
# ParserResults.pkl and *Binding changed" code) or some .webidl
# files changed (and then the corresponding *Binding files should
# show up in changedDeps). Since clearly the build system is
# confused, just regenerate everything to be safe.
toRegenerate = allWebIDLFiles
else:
toRegenerate = map(lambda f: f[:-len("Binding")] + ".webidl",
toRegenerate)
else:
toRegenerate = allWebIDLFiles

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

@ -1091,6 +1091,10 @@ DOMInterfaces = {
'VideoStreamTrack': {
},
'WaveTable' : {
'nativeOwnership': 'refcounted'
},
'WebGLActiveInfo': {
'nativeType': 'mozilla::WebGLActiveInfo',
'headerFile': 'WebGLContext.h',

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

@ -672,7 +672,8 @@ NS_IMETHODIMP
TransactionThreadPoolListener::OnThreadCreated()
{
MOZ_ASSERT(!NS_IsMainThread());
profiler_register_thread("IndexedDB Transaction");
char aLocal;
profiler_register_thread("IndexedDB Transaction", &aLocal);
return NS_OK;
}

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

@ -25,7 +25,7 @@ interface nsIVariant;
* @see <http://www.whatwg.org/html/#window>
*/
[scriptable, uuid(e0f33b20-72ef-415b-9ff2-8bf176f581f8)]
[scriptable, uuid(be62660a-e3f6-409c-a4a9-378364a9526f)]
interface nsIDOMWindow : nsISupports
{
// the current browsing context
@ -454,6 +454,7 @@ interface nsIDOMWindow : nsISupports
void mozCancelAnimationFrame(in long aHandle);
// Backwards-compat shim for now to make Google maps work
void mozCancelRequestAnimationFrame(in long aHandle);
void cancelAnimationFrame(in long aHandle);
/**
* The current animation start time in milliseconds since the epoch.
@ -508,5 +509,5 @@ interface nsIDOMWindowPerformance : nsISupports
* Empty interface for compatibility with older versions.
* @deprecated Use nsIDOMWindow instead
*/
[scriptable, uuid(36aeaa8e-3126-49de-9581-276dd7117826)]
[scriptable, uuid(ad5768c7-8668-4cd4-bcac-3d0a400d50be)]
interface nsIDOMWindowInternal : nsIDOMWindow {};

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

@ -2261,16 +2261,14 @@ _getvalue(NPP npp, NPNVariable variable, void *result)
}
case kJavaContext_ANPGetValue: {
LOG("get context");
JNIEnv* env = GetJNIForThread();
if (!env)
AndroidBridge *bridge = AndroidBridge::Bridge();
if (!bridge)
return NPERR_GENERIC_ERROR;
jobject ret = bridge->GetContext();
if (!ret)
return NPERR_GENERIC_ERROR;
jclass cls = env->FindClass("org/mozilla/gecko/GeckoApp");
jfieldID field = env->GetStaticFieldID(cls, "mAppContext",
"Lorg/mozilla/gecko/GeckoApp;");
jobject ret = env->GetStaticObjectField(cls, field);
env->DeleteLocalRef(cls);
int32_t* i = reinterpret_cast<int32_t*>(result);
*i = reinterpret_cast<int32_t>(ret);
return NPERR_NO_ERROR;

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

@ -27,6 +27,41 @@
#include "nsIFile.h"
#include "nsUnicharUtils.h"
#include <shlwapi.h>
#define SHOCKWAVE_BASE_FILENAME L"np32dsw"
/**
* Determines whether or not SetDllDirectory should be called for this plugin.
*
* @param pluginFilePath The full path of the plugin file
* @return true if SetDllDirectory can be called for the plugin
*/
bool
ShouldProtectPluginCurrentDirectory(LPCWSTR pluginFilePath)
{
LPCWSTR passedInFilename = PathFindFileName(pluginFilePath);
if (!passedInFilename) {
return true;
}
// Somewhere in the middle of 11.6 version of Shockwave, naming of the DLL
// after its version number is introduced.
if (!wcsicmp(passedInFilename, SHOCKWAVE_BASE_FILENAME L".dll")) {
return false;
}
// Shockwave versions before 1202122 will break if you call SetDllDirectory
const uint64_t kFixedShockwaveVersion = 1202122;
uint64_t version;
int found = swscanf(passedInFilename, SHOCKWAVE_BASE_FILENAME L"_%llu.dll",
&version);
if (found && version < kFixedShockwaveVersion) {
return false;
}
// We always want to call SetDllDirectory otherwise
return true;
}
using namespace mozilla;
/* Local helper functions */
@ -245,17 +280,13 @@ nsresult nsPluginFile::LoadPlugin(PRLibrary **outLibrary)
bool protectCurrentDirectory = true;
nsAutoString pluginFolderPath;
mPlugin->GetPath(pluginFolderPath);
int32_t idx = pluginFolderPath.RFindChar('\\');
if (kNotFound == idx)
return NS_ERROR_FILE_INVALID_PATH;
if (Substring(pluginFolderPath, idx).LowerCaseEqualsLiteral("\\np32dsw.dll")) {
protectCurrentDirectory = false;
}
nsAutoString pluginFilePath;
mPlugin->GetPath(pluginFilePath);
protectCurrentDirectory =
ShouldProtectPluginCurrentDirectory(pluginFilePath.BeginReading());
nsAutoString pluginFolderPath = pluginFilePath;
int32_t idx = pluginFilePath.RFindChar('\\');
pluginFolderPath.SetLength(idx);
BOOL restoreOrigDir = FALSE;

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

@ -15,6 +15,7 @@
#ifdef XP_WIN
#include <objbase.h>
bool ShouldProtectPluginCurrentDirectory(LPCWSTR pluginFilePath);
#endif
using mozilla::ipc::IOThreadChild;
@ -37,6 +38,7 @@ std::size_t caseInsensitiveFind(std::string aHaystack, std::string aNeedle) {
namespace mozilla {
namespace plugins {
bool
PluginProcessChild::Init()
{
@ -102,21 +104,12 @@ PluginProcessChild::Init()
CommandLine::ForCurrentProcess()->GetLooseValues();
NS_ABORT_IF_FALSE(values.size() >= 1, "not enough loose args");
pluginFilename = WideToUTF8(values[0]);
bool protectCurrentDirectory = true;
// Don't use SetDllDirectory for Shockwave Director
const std::string shockwaveDirectorPluginFilename("\\np32dsw.dll");
std::size_t index = caseInsensitiveFind(pluginFilename, shockwaveDirectorPluginFilename);
if (index != std::string::npos &&
index + shockwaveDirectorPluginFilename.length() == pluginFilename.length()) {
protectCurrentDirectory = false;
}
if (protectCurrentDirectory) {
if (ShouldProtectPluginCurrentDirectory(values[0].c_str())) {
SanitizeEnvironmentVariables();
SetDllDirectory(L"");
}
pluginFilename = WideToUTF8(values[0]);
#else
# error Sorry
#endif

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

@ -874,7 +874,7 @@ NetworkManager.prototype = {
try {
let file = new FileUtils.File(KERNEL_NETWORK_ENTRY + "/" +
this.possibleInterface[i]);
if (file.IsDirectory()) {
if (file.exists()) {
return this.possibleInterface[i];
}
} catch (e) {

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

@ -61,6 +61,9 @@ interface AudioContext : EventTarget {
[Creator]
DynamicsCompressorNode createDynamicsCompressor();
[Creator, Throws]
WaveTable createWaveTable(Float32Array real, Float32Array imag);
};
/*

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

@ -0,0 +1,17 @@
/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/.
*
* The origin of this IDL file is
* https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
*
* Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
* liability, trademark and document use rules apply.
*/
[PrefControlled]
interface WaveTable {
};

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

@ -338,6 +338,7 @@ webidl_files = \
USSDReceivedEvent.webidl \
VideoStreamTrack.webidl \
WaveShaperNode.webidl \
WaveTable.webidl \
Window.webidl \
XMLDocument.webidl \
XMLHttpRequest.webidl \

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

@ -514,7 +514,8 @@ public:
JSRuntime* rt = JS_GetRuntime(cx);
profiler_register_thread("WebWorker");
char aLocal;
profiler_register_thread("WebWorker", &aLocal);
#ifdef MOZ_ENABLE_PROFILER_SPS
if (PseudoStack* stack = mozilla_get_pseudo_stack())
stack->sampleRuntime(rt);

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

@ -3232,7 +3232,12 @@ _cairo_d2d_paint(void *surface,
op = _cairo_d2d_simplify_operator(op, source);
if (op == CAIRO_OPERATOR_SOURCE) {
return CAIRO_INT_STATUS_UNSUPPORTED;
if (!clip) {
_cairo_d2d_clear(d2dsurf, NULL);
op = CAIRO_OPERATOR_OVER;
} else {
return CAIRO_INT_STATUS_UNSUPPORTED;
}
}
if (op == CAIRO_OPERATOR_CLEAR) {

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

@ -1,6 +1,6 @@
This directory contains the Graphite2 library from http://hg.palaso.org/graphitedev
Current version derived from upstream changeset 51e72e74b9a6
Current version derived from upstream changeset 09707dd22634
See gfx/graphite2/moz-gr-update.sh for update procedure.

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

@ -30,7 +30,7 @@
#define GR2_VERSION_MAJOR 1
#define GR2_VERSION_MINOR 2
#define GR2_VERSION_BUGFIX 0
#define GR2_VERSION_BUGFIX 2
#ifdef __cplusplus
extern "C"

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

@ -37,10 +37,10 @@ enum DirCode { // Hungarian: dirc
R = 2, // right-to-left, strong - R
AL = 3, // Arabic letter, right-to-left, strong, AR
EN = 4, // European number, left-to-right, weak - EN
ES = 5, // European separator, left-to-right, weak - ES
EUS = 5, // European separator, left-to-right, weak - ES
ET = 6, // European number terminator, left-to-right, weak - ET
AN = 7, // Arabic number, left-to-right, weak - AN
CS = 8, // Common number separator, left-to-right, weak - CS
CUS = 8, // Common number separator, left-to-right, weak - CS
WS = 9, // white space, neutral - WS
BN = 10, // boundary neutral - BN

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше