mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-01 22:55:23 +00:00
Merge m-c to s-c.
This commit is contained in:
commit
8c448c5675
@ -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)
|
||||
|
||||
|
166
accessible/tests/mochitest/jsat/test_explicit_names.html
Normal file
166
accessible/tests/mochitest/jsat/test_explicit_names.html
Normal file
@ -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>
|
||||
|
70
accessible/tests/mochitest/jsat/utterance.js
Normal file
70
accessible/tests/mochitest/jsat/utterance.js
Normal file
@ -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);
|
||||
|
21
browser/devtools/profiler/test/mock_console_api.html
Normal file
21
browser/devtools/profiler/test/mock_console_api.html
Normal file
@ -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,
|
||||
®isteredApp);
|
||||
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,
|
||||
®isteredApp);
|
||||
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
|
||||
|
45
content/base/test/test_bug876282.html
Normal file
45
content/base/test/test_bug876282.html
Normal file
@ -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>
|
||||
|
27
content/media/test/crashtests/876249.html
Normal file
27
content/media/test/crashtests/876249.html
Normal file
@ -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>
|
4
content/media/test/crashtests/876834.html
Normal file
4
content/media/test/crashtests/876834.html
Normal file
@ -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);
|
||||
|
38
content/media/webaudio/WaveTable.cpp
Normal file
38
content/media/webaudio/WaveTable.cpp
Normal file
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
50
content/media/webaudio/WaveTable.h
Normal file
50
content/media/webaudio/WaveTable.h
Normal file
@ -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 \
|
||||
|
36
content/media/webaudio/test/test_waveTable.html
Normal file
36
content/media/webaudio/test/test_waveTable.html
Normal file
@ -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);
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
|
17
dom/webidl/WaveTable.webidl
Normal file
17
dom/webidl/WaveTable.webidl
Normal file
@ -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
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user