Merge branch 'mozilla-central'
@ -27,8 +27,8 @@ TreeWalker::
|
|||||||
{
|
{
|
||||||
NS_ASSERTION(aContent, "No node for the accessible tree walker!");
|
NS_ASSERTION(aContent, "No node for the accessible tree walker!");
|
||||||
|
|
||||||
mChildFilter = mContext->CanHaveAnonChildren() ?
|
mChildFilter = mContext->NoXBLKids() ?
|
||||||
nsIContent::eAllChildren : nsIContent::eAllButXBL;
|
nsIContent::eAllButXBL : nsIContent::eAllChildren;
|
||||||
mChildFilter |= nsIContent::eSkipPlaceholderContent;
|
mChildFilter |= nsIContent::eSkipPlaceholderContent;
|
||||||
|
|
||||||
if (aContent)
|
if (aContent)
|
||||||
|
@ -302,12 +302,6 @@ Accessible::KeyboardShortcut() const
|
|||||||
return KeyBinding();
|
return KeyBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
Accessible::CanHaveAnonChildren()
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut)
|
Accessible::TranslateString(const nsString& aKey, nsAString& aStringOut)
|
||||||
{
|
{
|
||||||
|
@ -494,11 +494,6 @@ public:
|
|||||||
*/
|
*/
|
||||||
virtual nsresult HandleAccEvent(AccEvent* aAccEvent);
|
virtual nsresult HandleAccEvent(AccEvent* aAccEvent);
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if this accessible allows accessible children from anonymous subtree.
|
|
||||||
*/
|
|
||||||
virtual bool CanHaveAnonChildren();
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if the accessible is an acceptable child.
|
* Return true if the accessible is an acceptable child.
|
||||||
*/
|
*/
|
||||||
@ -930,6 +925,12 @@ public:
|
|||||||
mStateFlags &= ~eRelocated;
|
mStateFlags &= ~eRelocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return true if the accessible doesn't allow accessible children from XBL
|
||||||
|
* anonymous subtree.
|
||||||
|
*/
|
||||||
|
bool NoXBLKids() { return mStateFlags & eNoXBLKids; }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return true if this accessible has a parent whose name depends on this
|
* Return true if this accessible has a parent whose name depends on this
|
||||||
* accessible.
|
* accessible.
|
||||||
@ -1021,8 +1022,9 @@ protected:
|
|||||||
eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
|
eIgnoreDOMUIEvent = 1 << 7, // don't process DOM UI events for a11y events
|
||||||
eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
|
eSurvivingInUpdate = 1 << 8, // parent drops children to recollect them
|
||||||
eRelocated = 1 << 9, // accessible was moved in tree
|
eRelocated = 1 << 9, // accessible was moved in tree
|
||||||
|
eNoXBLKids = 1 << 10, // accessible don't allows XBL children
|
||||||
|
|
||||||
eLastStateFlag = eRelocated
|
eLastStateFlag = eNoXBLKids
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1137,7 +1139,7 @@ protected:
|
|||||||
int32_t mIndexInParent;
|
int32_t mIndexInParent;
|
||||||
|
|
||||||
static const uint8_t kChildrenFlagsBits = 2;
|
static const uint8_t kChildrenFlagsBits = 2;
|
||||||
static const uint8_t kStateFlagsBits = 10;
|
static const uint8_t kStateFlagsBits = 11;
|
||||||
static const uint8_t kContextFlagsBits = 2;
|
static const uint8_t kContextFlagsBits = 2;
|
||||||
static const uint8_t kTypeBits = 6;
|
static const uint8_t kTypeBits = 6;
|
||||||
static const uint8_t kGenericTypesBits = 14;
|
static const uint8_t kGenericTypesBits = 14;
|
||||||
|
@ -18,22 +18,26 @@
|
|||||||
src="../layout.js"></script>
|
src="../layout.js"></script>
|
||||||
|
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
|
var kX = 10, kY = 10, kWidth = 150, kHeight = 100;
|
||||||
function doTest()
|
function doTest()
|
||||||
{
|
{
|
||||||
var canv = document.getElementById("c");
|
var canv = document.getElementById("c");
|
||||||
var context = canv.getContext('2d');
|
var context = canv.getContext('2d');
|
||||||
var element = document.getElementById("showA");
|
var element = document.getElementById("showA");
|
||||||
context.beginPath();
|
context.beginPath();
|
||||||
context.rect(10, 10, 150, 100);
|
context.rect(kX, kY, kWidth, kHeight);
|
||||||
context.addHitRegion({control: element});
|
context.addHitRegion({control: element});
|
||||||
var input = getAccessible("showA");
|
|
||||||
var input = getAccessible("showA");
|
var input = getAccessible("showA");
|
||||||
var [cnvX, cnvY, cnvWidth, cnvHeight] = getBoundsForDOMElm(canv);
|
var [cnvX, cnvY, cnvWidth, cnvHeight] = getBoundsForDOMElm(canv);
|
||||||
var [accX, accY, accWidth, accHeight] = getBounds(input);
|
var [accX, accY, accWidth, accHeight] = getBounds(input);
|
||||||
is(accX, cnvX + 10, "accX should be 10 and not " + accX);
|
|
||||||
is(accY, cnvY + 10, "accY should be 10 and not " + accY);
|
var [x, y, w, h] = CSSToDevicePixels(window, kX, kY, kWidth, kHeight);
|
||||||
is(accWidth, 150, "accWidth should be 150 and not " + accWidth);
|
is(accX, cnvX + x, "wrong accX");
|
||||||
is(accHeight, 100, "accHeight should be 100 and not " + accHeight);
|
is(accY, cnvY + y, "wrong accY");
|
||||||
|
is(accWidth, w, "wrong accWidth");
|
||||||
|
is(accHeight, h, "wrong accHeight");
|
||||||
|
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,10 +345,17 @@ function eventQueue(aEventType)
|
|||||||
var msg = "Test with ID = '" + this.getEventID(checker) +
|
var msg = "Test with ID = '" + this.getEventID(checker) +
|
||||||
"' succeed. ";
|
"' succeed. ";
|
||||||
|
|
||||||
if (checker.unexpected)
|
if (checker.unexpected) {
|
||||||
ok(true, msg + "There's no unexpected " + typeStr + " event.");
|
if (checker.todo) {
|
||||||
else
|
todo(false, "Event " + typeStr + " event is still missing");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ok(true, msg + "There's no unexpected " + typeStr + " event.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
ok(true, msg + "Event " + typeStr + " was handled.");
|
ok(true, msg + "Event " + typeStr + " was handled.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -371,8 +378,13 @@ function eventQueue(aEventType)
|
|||||||
ok(false, msg + "Dupe " + typeStr + " event.");
|
ok(false, msg + "Dupe " + typeStr + " event.");
|
||||||
|
|
||||||
if (checker.unexpected) {
|
if (checker.unexpected) {
|
||||||
if (checker.wasCaught)
|
if (checker.todo) {
|
||||||
|
todo(checker.wasCaught,
|
||||||
|
"Event " + typeStr + " event is still missing");
|
||||||
|
}
|
||||||
|
else if (checker.wasCaught) {
|
||||||
ok(false, msg + "There's unexpected " + typeStr + " event.");
|
ok(false, msg + "There's unexpected " + typeStr + " event.");
|
||||||
|
}
|
||||||
} else if (!checker.wasCaught) {
|
} else if (!checker.wasCaught) {
|
||||||
ok(false, msg + typeStr + " event was missed.");
|
ok(false, msg + typeStr + " event was missed.");
|
||||||
}
|
}
|
||||||
@ -1667,6 +1679,18 @@ function invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg, aIsAsync)
|
|||||||
this.mTargetFuncArg = aTargetFuncArg;
|
this.mTargetFuncArg = aTargetFuncArg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generic invoker checker for todo events.
|
||||||
|
*/
|
||||||
|
function todo_invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
|
||||||
|
{
|
||||||
|
this.__proto__ = new invokerChecker(aEventType, aTargetOrFunc,
|
||||||
|
aTargetFuncArg, true);
|
||||||
|
|
||||||
|
this.unexpected = true;
|
||||||
|
this.todo = true;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generic invoker checker for unexpected events.
|
* Generic invoker checker for unexpected events.
|
||||||
*/
|
*/
|
||||||
|
@ -358,6 +358,25 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function showHiddenParentOfVisibleChild()
|
||||||
|
{
|
||||||
|
this.eventSeq = [
|
||||||
|
new todo_invokerChecker(EVENT_HIDE, getNode("c4_child")),
|
||||||
|
new invokerChecker(EVENT_SHOW, getNode("c4_middle")),
|
||||||
|
new invokerChecker(EVENT_REORDER, getNode("c4"))
|
||||||
|
];
|
||||||
|
|
||||||
|
this.invoke = function showHiddenParentOfVisibleChild_invoke()
|
||||||
|
{
|
||||||
|
getNode("c4_middle").style.visibility = 'visible';
|
||||||
|
}
|
||||||
|
|
||||||
|
this.getID = function showHiddenParentOfVisibleChild_getID()
|
||||||
|
{
|
||||||
|
return "show hidden parent of visible child";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Target getters.
|
* Target getters.
|
||||||
*/
|
*/
|
||||||
@ -484,6 +503,7 @@
|
|||||||
gQueue.push(new test2("testContainer", "testNestedContainer"));
|
gQueue.push(new test2("testContainer", "testNestedContainer"));
|
||||||
gQueue.push(new test3("testContainer"));
|
gQueue.push(new test3("testContainer"));
|
||||||
gQueue.push(new insertReferredElm("testContainer3"));
|
gQueue.push(new insertReferredElm("testContainer3"));
|
||||||
|
gQueue.push(new showHiddenParentOfVisibleChild());
|
||||||
|
|
||||||
gQueue.invoke(); // Will call SimpleTest.finish();
|
gQueue.invoke(); // Will call SimpleTest.finish();
|
||||||
}
|
}
|
||||||
@ -544,5 +564,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div id="testContainer2"></div>
|
<div id="testContainer2"></div>
|
||||||
<div id="testContainer3"></div>
|
<div id="testContainer3"></div>
|
||||||
|
|
||||||
|
<div id="c4">
|
||||||
|
<div style="visibility:hidden" id="c4_middle">
|
||||||
|
<div style="visibility:visible" id="c4_child"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -41,29 +41,31 @@
|
|||||||
|
|
||||||
function doTest()
|
function doTest()
|
||||||
{
|
{
|
||||||
|
var offsetX = 20, offsetY = 40;
|
||||||
getNode("hitcanvas").scrollIntoView(true);
|
getNode("hitcanvas").scrollIntoView(true);
|
||||||
|
|
||||||
var context = document.getElementById("hitcanvas").getContext('2d');
|
var context = document.getElementById("hitcanvas").getContext('2d');
|
||||||
redrawCheckbox(context, document.getElementById('hitcheck'), 20, 40);
|
redrawCheckbox(context, document.getElementById('hitcheck'),
|
||||||
|
offsetX, offsetY);
|
||||||
|
|
||||||
var hitcanvas = getAccessible("hitcanvas");
|
var hitcanvas = getAccessible("hitcanvas");
|
||||||
var hitcheck = getAccessible("hitcheck");
|
var hitcheck = getAccessible("hitcheck");
|
||||||
|
|
||||||
var [hitX, hitY, hitWidth, hitHeight] = getBounds(hitcanvas);
|
var [hitX, hitY, hitWidth, hitHeight] = getBounds(hitcanvas);
|
||||||
|
var [deltaX, deltaY] = CSSToDevicePixels(window, offsetX, offsetY);
|
||||||
|
|
||||||
var docAcc = getAccessible(document);
|
var docAcc = getAccessible(document);
|
||||||
var tgtX = hitX+25;
|
|
||||||
var tgtY = hitY+45;
|
|
||||||
hitAcc = docAcc.getDeepestChildAtPoint(tgtX, tgtY);
|
|
||||||
// test if we hit the region associated with the shadow dom checkbox
|
|
||||||
is(hitAcc, hitcheck, "Hit match at " + tgtX + "," + tgtY +
|
|
||||||
". Found: " + prettyName(hitAcc));
|
|
||||||
|
|
||||||
tgtY = hitY+75;
|
// test if we hit the region associated with the shadow dom checkbox
|
||||||
|
var tgtX = hitX + deltaX;
|
||||||
|
var tgtY = hitY + deltaY;
|
||||||
hitAcc = docAcc.getDeepestChildAtPoint(tgtX, tgtY);
|
hitAcc = docAcc.getDeepestChildAtPoint(tgtX, tgtY);
|
||||||
|
isObject(hitAcc, hitcheck, `Hit match at (${tgtX}, ${tgtY}`);
|
||||||
|
|
||||||
// test that we don't hit the region associated with the shadow dom checkbox
|
// test that we don't hit the region associated with the shadow dom checkbox
|
||||||
is(hitAcc, hitcanvas, "Hit match at " + tgtX + "," + tgtY +
|
tgtY = hitY + deltaY * 2;
|
||||||
". Found: " + prettyName(hitAcc));
|
hitAcc = docAcc.getDeepestChildAtPoint(tgtX, tgtY);
|
||||||
|
isObject(hitAcc, hitcanvas, `Hit match at (${tgtX}, ${tgtY}`);
|
||||||
|
|
||||||
SimpleTest.finish();
|
SimpleTest.finish();
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
[test_bindings.xhtml]
|
[test_bindings.xhtml]
|
||||||
[test_embeds.xul]
|
[test_embeds.xul]
|
||||||
skip-if = (os == "linux" && (debug || asan)) # Bug 845176
|
|
||||||
[test_general.html]
|
[test_general.html]
|
||||||
[test_general.xul]
|
[test_general.xul]
|
||||||
[test_tabbrowser.xul]
|
[test_tabbrowser.xul]
|
||||||
|
@ -37,7 +37,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.eventSeq = [
|
this.eventSeq = [
|
||||||
new invokerChecker(EVENT_REORDER, currentBrowser)
|
new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument)
|
||||||
];
|
];
|
||||||
|
|
||||||
this.finalCheck = function loadURI_finalCheck()
|
this.finalCheck = function loadURI_finalCheck()
|
||||||
@ -52,27 +52,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function browserReorderChecker()
|
|
||||||
{
|
|
||||||
this.type = EVENT_REORDER;
|
|
||||||
|
|
||||||
this.match = function browserReorderChecker_match(aEvent)
|
|
||||||
{
|
|
||||||
if (!isAccessible(currentBrowser()))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Reorder event might be duped because of temporary document creation.
|
|
||||||
if (aEvent.accessible == getAccessible(currentBrowser())) {
|
|
||||||
this.cnt++;
|
|
||||||
return this.cnt != 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cnt = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function loadOneTab(aURI)
|
function loadOneTab(aURI)
|
||||||
{
|
{
|
||||||
this.invoke = function loadOneTab_invoke()
|
this.invoke = function loadOneTab_invoke()
|
||||||
@ -81,7 +60,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.eventSeq = [
|
this.eventSeq = [
|
||||||
new browserReorderChecker()
|
new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, currentTabDocument)
|
||||||
];
|
];
|
||||||
|
|
||||||
this.finalCheck = function loadURI_finalCheck()
|
this.finalCheck = function loadURI_finalCheck()
|
||||||
@ -99,7 +78,7 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
// Testing
|
// Testing
|
||||||
|
|
||||||
gA11yEventDumpToConsole = true; // debug
|
//gA11yEventDumpToConsole = true; // debug
|
||||||
|
|
||||||
var gQueue = null;
|
var gQueue = null;
|
||||||
function doTests()
|
function doTests()
|
||||||
|
@ -32,7 +32,8 @@
|
|||||||
anchor = getAccessible("bottom2");
|
anchor = getAccessible("bottom2");
|
||||||
var [x, y] = getPos(anchor);
|
var [x, y] = getPos(anchor);
|
||||||
var wnd = getRootAccessible().DOMDocument.defaultView;
|
var wnd = getRootAccessible().DOMDocument.defaultView;
|
||||||
var scrollToX = docX - wnd.screenX, scrollToY = docY - wnd.screenY;
|
var [screenX, screenY] = CSSToDevicePixels(wnd, wnd.screenX, wnd.screenY);
|
||||||
|
var scrollToX = docX - screenX, scrollToY = docY - screenY;
|
||||||
|
|
||||||
anchor.scrollToPoint(COORDTYPE_WINDOW_RELATIVE, scrollToX, scrollToY);
|
anchor.scrollToPoint(COORDTYPE_WINDOW_RELATIVE, scrollToX, scrollToY);
|
||||||
testPos(anchor, [x, docY]);
|
testPos(anchor, [x, docY]);
|
||||||
|
@ -16,6 +16,8 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=429659
|
|||||||
src="role.js"></script>
|
src="role.js"></script>
|
||||||
<script type="application/javascript"
|
<script type="application/javascript"
|
||||||
src="attributes.js"></script>
|
src="attributes.js"></script>
|
||||||
|
<script type="application/javascript"
|
||||||
|
src="layout.js"></script>
|
||||||
|
|
||||||
<script type="application/javascript">
|
<script type="application/javascript">
|
||||||
function testCoordinates(aID, aAcc, aWidth, aHeight)
|
function testCoordinates(aID, aAcc, aWidth, aHeight)
|
||||||
@ -70,10 +72,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=429659
|
|||||||
// "Wrong screen y coordinate for " + aID + "!");
|
// "Wrong screen y coordinate for " + aID + "!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var [expected_w, expected_h] = CSSToDevicePixels(window, aWidth, aHeight);
|
||||||
var width = {}, height = {};
|
var width = {}, height = {};
|
||||||
aAcc.getImageSize(width, height);
|
aAcc.getImageSize(width, height);
|
||||||
is(width.value, aWidth, "Wrong width for " + aID + "!");
|
is(width.value, expected_w, "Wrong width for " + aID + "!");
|
||||||
is(height.value, aHeight, "wrong height for " + aID + "!");
|
is(height.value, expected_h, "wrong height for " + aID + "!");
|
||||||
}
|
}
|
||||||
|
|
||||||
function testThis(aID, aSRC, aWidth, aHeight,
|
function testThis(aID, aSRC, aWidth, aHeight,
|
||||||
|
@ -31,6 +31,15 @@ XULComboboxAccessible::
|
|||||||
mGenericTypes |= eAutoComplete;
|
mGenericTypes |= eAutoComplete;
|
||||||
else
|
else
|
||||||
mGenericTypes |= eCombobox;
|
mGenericTypes |= eCombobox;
|
||||||
|
|
||||||
|
// Both the XUL <textbox type="autocomplete"> and <menulist editable="true">
|
||||||
|
// widgets use XULComboboxAccessible. We need to walk the anonymous children
|
||||||
|
// for these so that the entry field is a child. Otherwise no XBL children.
|
||||||
|
if (!mContent->NodeInfo()->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL) &&
|
||||||
|
!mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
|
||||||
|
nsGkAtoms::_true, eIgnoreCase)) {
|
||||||
|
mStateFlags |= eNoXBLKids;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
role
|
role
|
||||||
@ -96,23 +105,6 @@ XULComboboxAccessible::Value(nsString& aValue)
|
|||||||
menuList->GetLabel(aValue);
|
menuList->GetLabel(aValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
XULComboboxAccessible::CanHaveAnonChildren()
|
|
||||||
{
|
|
||||||
if (mContent->NodeInfo()->Equals(nsGkAtoms::textbox, kNameSpaceID_XUL) ||
|
|
||||||
mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::editable,
|
|
||||||
nsGkAtoms::_true, eIgnoreCase)) {
|
|
||||||
// Both the XUL <textbox type="autocomplete"> and <menulist editable="true"> widgets
|
|
||||||
// use XULComboboxAccessible. We need to walk the anonymous children for these
|
|
||||||
// so that the entry field is a child
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Argument of false indicates we don't walk anonymous children for
|
|
||||||
// menuitems
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t
|
uint8_t
|
||||||
XULComboboxAccessible::ActionCount()
|
XULComboboxAccessible::ActionCount()
|
||||||
{
|
{
|
||||||
|
@ -26,7 +26,6 @@ public:
|
|||||||
virtual void Value(nsString& aValue) override;
|
virtual void Value(nsString& aValue) override;
|
||||||
virtual a11y::role NativeRole() override;
|
virtual a11y::role NativeRole() override;
|
||||||
virtual uint64_t NativeState() override;
|
virtual uint64_t NativeState() override;
|
||||||
virtual bool CanHaveAnonChildren() override;
|
|
||||||
|
|
||||||
// ActionAccessible
|
// ActionAccessible
|
||||||
virtual uint8_t ActionCount() override;
|
virtual uint8_t ActionCount() override;
|
||||||
|
@ -544,6 +544,10 @@ XULListitemAccessible::
|
|||||||
nsGkAtoms::checkbox,
|
nsGkAtoms::checkbox,
|
||||||
eCaseMatters);
|
eCaseMatters);
|
||||||
mType = eXULListItemType;
|
mType = eXULListItemType;
|
||||||
|
|
||||||
|
// Walk XBL anonymous children for list items. Overrides the flag value from
|
||||||
|
// base XULMenuitemAccessible class.
|
||||||
|
mStateFlags &= ~eNoXBLKids;
|
||||||
}
|
}
|
||||||
|
|
||||||
XULListitemAccessible::~XULListitemAccessible()
|
XULListitemAccessible::~XULListitemAccessible()
|
||||||
@ -668,13 +672,6 @@ XULListitemAccessible::ActionNameAt(uint8_t aIndex, nsAString& aName)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
XULListitemAccessible::CanHaveAnonChildren()
|
|
||||||
{
|
|
||||||
// That indicates we should walk anonymous children for listitems
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// XULListitemAccessible: Widgets
|
// XULListitemAccessible: Widgets
|
||||||
|
|
||||||
|
@ -115,7 +115,6 @@ public:
|
|||||||
virtual a11y::role NativeRole() override;
|
virtual a11y::role NativeRole() override;
|
||||||
virtual uint64_t NativeState() override;
|
virtual uint64_t NativeState() override;
|
||||||
virtual uint64_t NativeInteractiveState() const override;
|
virtual uint64_t NativeInteractiveState() const override;
|
||||||
virtual bool CanHaveAnonChildren() override;
|
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
virtual void ActionNameAt(uint8_t index, nsAString& aName) override;
|
virtual void ActionNameAt(uint8_t index, nsAString& aName) override;
|
||||||
|
@ -41,6 +41,7 @@ XULMenuitemAccessible::
|
|||||||
XULMenuitemAccessible(nsIContent* aContent, DocAccessible* aDoc) :
|
XULMenuitemAccessible(nsIContent* aContent, DocAccessible* aDoc) :
|
||||||
AccessibleWrap(aContent, aDoc)
|
AccessibleWrap(aContent, aDoc)
|
||||||
{
|
{
|
||||||
|
mStateFlags |= eNoXBLKids;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
@ -267,13 +268,6 @@ XULMenuitemAccessible::GetLevelInternal()
|
|||||||
return nsAccUtils::GetLevelForXULContainerItem(mContent);
|
return nsAccUtils::GetLevelForXULContainerItem(mContent);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
XULMenuitemAccessible::CanHaveAnonChildren()
|
|
||||||
{
|
|
||||||
// That indicates we don't walk anonymous children for menuitems
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
XULMenuitemAccessible::DoAction(uint8_t index)
|
XULMenuitemAccessible::DoAction(uint8_t index)
|
||||||
{
|
{
|
||||||
@ -413,6 +407,8 @@ XULMenupopupAccessible::
|
|||||||
mSelectControl = do_QueryInterface(mContent->GetFlattenedTreeParent());
|
mSelectControl = do_QueryInterface(mContent->GetFlattenedTreeParent());
|
||||||
if (!mSelectControl)
|
if (!mSelectControl)
|
||||||
mGenericTypes &= ~eSelect;
|
mGenericTypes &= ~eSelect;
|
||||||
|
|
||||||
|
mStateFlags |= eNoXBLKids;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t
|
uint64_t
|
||||||
|
@ -30,8 +30,6 @@ public:
|
|||||||
virtual uint64_t NativeInteractiveState() const override;
|
virtual uint64_t NativeInteractiveState() const override;
|
||||||
virtual int32_t GetLevelInternal() override;
|
virtual int32_t GetLevelInternal() override;
|
||||||
|
|
||||||
virtual bool CanHaveAnonChildren() override;
|
|
||||||
|
|
||||||
// ActionAccessible
|
// ActionAccessible
|
||||||
virtual uint8_t ActionCount() override;
|
virtual uint8_t ActionCount() override;
|
||||||
virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
|
virtual void ActionNameAt(uint8_t aIndex, nsAString& aName) override;
|
||||||
|
@ -23,7 +23,7 @@ XULSliderAccessible::
|
|||||||
XULSliderAccessible(nsIContent* aContent, DocAccessible* aDoc) :
|
XULSliderAccessible(nsIContent* aContent, DocAccessible* aDoc) :
|
||||||
AccessibleWrap(aContent, aDoc)
|
AccessibleWrap(aContent, aDoc)
|
||||||
{
|
{
|
||||||
mStateFlags |= eHasNumericValue;
|
mStateFlags |= eHasNumericValue | eNoXBLKids;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Accessible
|
// Accessible
|
||||||
@ -127,13 +127,6 @@ XULSliderAccessible::SetCurValue(double aValue)
|
|||||||
return SetSliderAttr(nsGkAtoms::curpos, aValue);
|
return SetSliderAttr(nsGkAtoms::curpos, aValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
XULSliderAccessible::CanHaveAnonChildren()
|
|
||||||
{
|
|
||||||
// Do not allow anonymous xul:slider be accessible.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Utils
|
// Utils
|
||||||
|
|
||||||
nsIContent*
|
nsIContent*
|
||||||
|
@ -26,7 +26,6 @@ public:
|
|||||||
virtual a11y::role NativeRole() override;
|
virtual a11y::role NativeRole() override;
|
||||||
virtual uint64_t NativeInteractiveState() const override;
|
virtual uint64_t NativeInteractiveState() const override;
|
||||||
virtual bool NativelyUnavailable() const override;
|
virtual bool NativelyUnavailable() const override;
|
||||||
virtual bool CanHaveAnonChildren() override;
|
|
||||||
|
|
||||||
// Value
|
// Value
|
||||||
virtual double MaxValue() const override;
|
virtual double MaxValue() const override;
|
||||||
|
@ -9,6 +9,8 @@ var StarUI = {
|
|||||||
_itemId: -1,
|
_itemId: -1,
|
||||||
uri: null,
|
uri: null,
|
||||||
_batching: false,
|
_batching: false,
|
||||||
|
_isNewBookmark: false,
|
||||||
|
_autoCloseTimer: 0,
|
||||||
|
|
||||||
_element: function(aID) {
|
_element: function(aID) {
|
||||||
return document.getElementById(aID);
|
return document.getElementById(aID);
|
||||||
@ -21,8 +23,11 @@ var StarUI = {
|
|||||||
// initially the panel is hidden
|
// initially the panel is hidden
|
||||||
// to avoid impacting startup / new window performance
|
// to avoid impacting startup / new window performance
|
||||||
element.hidden = false;
|
element.hidden = false;
|
||||||
element.addEventListener("popuphidden", this, false);
|
|
||||||
element.addEventListener("keypress", this, false);
|
element.addEventListener("keypress", this, false);
|
||||||
|
element.addEventListener("mouseout", this, false);
|
||||||
|
element.addEventListener("mouseover", this, false);
|
||||||
|
element.addEventListener("popuphidden", this, false);
|
||||||
|
element.addEventListener("popupshown", this, false);
|
||||||
return this.panel = element;
|
return this.panel = element;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -58,7 +63,11 @@ var StarUI = {
|
|||||||
// nsIDOMEventListener
|
// nsIDOMEventListener
|
||||||
handleEvent(aEvent) {
|
handleEvent(aEvent) {
|
||||||
switch (aEvent.type) {
|
switch (aEvent.type) {
|
||||||
|
case "mouseover":
|
||||||
|
clearTimeout(this._autoCloseTimer);
|
||||||
|
break;
|
||||||
case "popuphidden":
|
case "popuphidden":
|
||||||
|
clearTimeout(this._autoCloseTimer);
|
||||||
if (aEvent.originalTarget == this.panel) {
|
if (aEvent.originalTarget == this.panel) {
|
||||||
if (!this._element("editBookmarkPanelContent").hidden)
|
if (!this._element("editBookmarkPanelContent").hidden)
|
||||||
this.quitEditMode();
|
this.quitEditMode();
|
||||||
@ -72,44 +81,42 @@ var StarUI = {
|
|||||||
if (this._batching)
|
if (this._batching)
|
||||||
this.endBatch();
|
this.endBatch();
|
||||||
|
|
||||||
switch (this._actionOnHide) {
|
if (this._uriForRemoval) {
|
||||||
case "cancel": {
|
if (this._isNewBookmark) {
|
||||||
if (!PlacesUIUtils.useAsyncTransactions) {
|
if (!PlacesUtils.useAsyncTransactions) {
|
||||||
PlacesUtils.transactionManager.undoTransaction();
|
PlacesUtils.transactionManager.undoTransaction();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
PlacesTransactions.undo().catch(Cu.reportError);
|
PlacesTransactions().undo().catch(Cu.reportError);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case "remove": {
|
// Remove all bookmarks for the bookmark's url, this also removes
|
||||||
// Remove all bookmarks for the bookmark's url, this also removes
|
// the tags for the url.
|
||||||
// the tags for the url.
|
if (!PlacesUIUtils.useAsyncTransactions) {
|
||||||
if (!PlacesUIUtils.useAsyncTransactions) {
|
let itemIds = PlacesUtils.getBookmarksForURI(this._uriForRemoval);
|
||||||
let itemIds = PlacesUtils.getBookmarksForURI(this._uriForRemoval);
|
for (let itemId of itemIds) {
|
||||||
for (let itemId of itemIds) {
|
let txn = new PlacesRemoveItemTransaction(itemId);
|
||||||
let txn = new PlacesRemoveItemTransaction(itemId);
|
PlacesUtils.transactionManager.doTransaction(txn);
|
||||||
PlacesUtils.transactionManager.doTransaction(txn);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PlacesTransactions.RemoveBookmarksForUrls(this._uriForRemoval)
|
|
||||||
.transact().catch(Cu.reportError);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlacesTransactions.RemoveBookmarksForUrls([this._uriForRemoval])
|
||||||
|
.transact().catch(Cu.reportError);
|
||||||
}
|
}
|
||||||
this._actionOnHide = "";
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "keypress":
|
case "keypress":
|
||||||
|
clearTimeout(this._autoCloseTimer);
|
||||||
|
|
||||||
if (aEvent.defaultPrevented) {
|
if (aEvent.defaultPrevented) {
|
||||||
// The event has already been consumed inside of the panel.
|
// The event has already been consumed inside of the panel.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (aEvent.keyCode) {
|
switch (aEvent.keyCode) {
|
||||||
case KeyEvent.DOM_VK_ESCAPE:
|
case KeyEvent.DOM_VK_ESCAPE:
|
||||||
if (!this._element("editBookmarkPanelContent").hidden)
|
this.panel.hidePopup();
|
||||||
this.cancelButtonOnCommand();
|
|
||||||
break;
|
break;
|
||||||
case KeyEvent.DOM_VK_RETURN:
|
case KeyEvent.DOM_VK_RETURN:
|
||||||
if (aEvent.target.classList.contains("expander-up") ||
|
if (aEvent.target.classList.contains("expander-up") ||
|
||||||
@ -123,12 +130,40 @@ var StarUI = {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "mouseout": {
|
||||||
|
// Don't handle events for descendent elements.
|
||||||
|
if (aEvent.target != aEvent.currentTarget) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Explicit fall-through
|
||||||
|
}
|
||||||
|
case "popupshown":
|
||||||
|
// auto-close if new and not interacted with
|
||||||
|
if (this._isNewBookmark) {
|
||||||
|
// 3500ms matches the timeout that Pocket uses in
|
||||||
|
// browser/extensions/pocket/content/panels/js/saved.js
|
||||||
|
let delay = 3500;
|
||||||
|
if (this._closePanelQuickForTesting) {
|
||||||
|
delay /= 10;
|
||||||
|
}
|
||||||
|
this._autoCloseTimer = setTimeout(() => this.panel.hidePopup(), delay, this);
|
||||||
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_overlayLoaded: false,
|
_overlayLoaded: false,
|
||||||
_overlayLoading: false,
|
_overlayLoading: false,
|
||||||
showEditBookmarkPopup: Task.async(function* (aNode, aAnchorElement, aPosition) {
|
showEditBookmarkPopup: Task.async(function* (aNode, aAnchorElement, aPosition, aIsNewBookmark) {
|
||||||
|
// Slow double-clicks (not true double-clicks) shouldn't
|
||||||
|
// cause the panel to flicker.
|
||||||
|
if (this.panel.state == "showing" ||
|
||||||
|
this.panel.state == "open") {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._isNewBookmark = aIsNewBookmark;
|
||||||
|
this._uriForRemoval = "";
|
||||||
// TODO: Deprecate this once async transactions are enabled and the legacy
|
// TODO: Deprecate this once async transactions are enabled and the legacy
|
||||||
// transactions code is gone (bug 1131491) - we don't want addons to to use
|
// transactions code is gone (bug 1131491) - we don't want addons to to use
|
||||||
// the completeNodeLikeObjectForItemId, so it's better if they keep passing
|
// the completeNodeLikeObjectForItemId, so it's better if they keep passing
|
||||||
@ -177,26 +212,18 @@ var StarUI = {
|
|||||||
if (this.panel.state != "closed")
|
if (this.panel.state != "closed")
|
||||||
return;
|
return;
|
||||||
|
|
||||||
this._blockCommands(); // un-done in the popuphiding handler
|
this._blockCommands(); // un-done in the popuphidden handler
|
||||||
|
|
||||||
// Set panel title:
|
|
||||||
// if we are batching, i.e. the bookmark has been added now,
|
|
||||||
// then show Page Bookmarked, else if the bookmark did already exist,
|
|
||||||
// we are about editing it, then use Edit This Bookmark.
|
|
||||||
this._element("editBookmarkPanelTitle").value =
|
this._element("editBookmarkPanelTitle").value =
|
||||||
this._batching ?
|
this._isNewBookmark ?
|
||||||
gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") :
|
gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") :
|
||||||
gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle");
|
gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle");
|
||||||
|
|
||||||
// No description; show the Done, Cancel;
|
// No description; show the Done, Remove;
|
||||||
this._element("editBookmarkPanelDescription").textContent = "";
|
this._element("editBookmarkPanelDescription").textContent = "";
|
||||||
this._element("editBookmarkPanelBottomButtons").hidden = false;
|
this._element("editBookmarkPanelBottomButtons").hidden = false;
|
||||||
this._element("editBookmarkPanelContent").hidden = false;
|
this._element("editBookmarkPanelContent").hidden = false;
|
||||||
|
|
||||||
// The remove button is shown only if we're not already batching, i.e.
|
|
||||||
// if the cancel button/ESC does not remove the bookmark.
|
|
||||||
this._element("editBookmarkPanelRemoveButton").hidden = this._batching;
|
|
||||||
|
|
||||||
// The label of the remove button differs if the URI is bookmarked
|
// The label of the remove button differs if the URI is bookmarked
|
||||||
// multiple times.
|
// multiple times.
|
||||||
let bookmarks = PlacesUtils.getBookmarksForURI(gBrowser.currentURI);
|
let bookmarks = PlacesUtils.getBookmarksForURI(gBrowser.currentURI);
|
||||||
@ -250,14 +277,8 @@ var StarUI = {
|
|||||||
gEditItemOverlay.uninitPanel(true);
|
gEditItemOverlay.uninitPanel(true);
|
||||||
},
|
},
|
||||||
|
|
||||||
cancelButtonOnCommand: function SU_cancelButtonOnCommand() {
|
|
||||||
this._actionOnHide = "cancel";
|
|
||||||
this.panel.hidePopup(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
removeBookmarkButtonCommand: function SU_removeBookmarkButtonCommand() {
|
removeBookmarkButtonCommand: function SU_removeBookmarkButtonCommand() {
|
||||||
this._uriForRemoval = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
|
this._uriForRemoval = PlacesUtils.bookmarks.getBookmarkURI(this._itemId);
|
||||||
this._actionOnHide = "remove";
|
|
||||||
this.panel.hidePopup();
|
this.panel.hidePopup();
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -325,7 +346,8 @@ var PlacesCommandHook = {
|
|||||||
|
|
||||||
var uri = aBrowser.currentURI;
|
var uri = aBrowser.currentURI;
|
||||||
var itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
|
var itemId = PlacesUtils.getMostRecentBookmarkForURI(uri);
|
||||||
if (itemId == -1) {
|
let isNewBookmark = itemId == -1;
|
||||||
|
if (isNewBookmark) {
|
||||||
// Bug 1148838 - Make this code work for full page plugins.
|
// Bug 1148838 - Make this code work for full page plugins.
|
||||||
var title;
|
var title;
|
||||||
var description;
|
var description;
|
||||||
@ -342,10 +364,10 @@ var PlacesCommandHook = {
|
|||||||
}
|
}
|
||||||
catch (e) { }
|
catch (e) { }
|
||||||
|
|
||||||
if (aShowEditUI) {
|
if (aShowEditUI && isNewBookmark) {
|
||||||
// If we bookmark the page here (i.e. page was not "starred" already)
|
// If we bookmark the page here but open right into a cancelable
|
||||||
// but open right into the "edit" state, start batching here, so
|
// state (i.e. new bookmark in Library), start batching here so
|
||||||
// "Cancel" in that state removes the bookmark.
|
// all of the actions can be undone in a single undo step.
|
||||||
StarUI.beginBatch();
|
StarUI.beginBatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -376,16 +398,16 @@ var PlacesCommandHook = {
|
|||||||
// 3. the content area
|
// 3. the content area
|
||||||
if (BookmarkingUI.anchor) {
|
if (BookmarkingUI.anchor) {
|
||||||
StarUI.showEditBookmarkPopup(itemId, BookmarkingUI.anchor,
|
StarUI.showEditBookmarkPopup(itemId, BookmarkingUI.anchor,
|
||||||
"bottomcenter topright");
|
"bottomcenter topright", isNewBookmark);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let identityIcon = document.getElementById("identity-icon");
|
let identityIcon = document.getElementById("identity-icon");
|
||||||
if (isElementVisible(identityIcon)) {
|
if (isElementVisible(identityIcon)) {
|
||||||
StarUI.showEditBookmarkPopup(itemId, identityIcon,
|
StarUI.showEditBookmarkPopup(itemId, identityIcon,
|
||||||
"bottomcenter topright");
|
"bottomcenter topright", isNewBookmark);
|
||||||
} else {
|
} else {
|
||||||
StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap");
|
StarUI.showEditBookmarkPopup(itemId, aBrowser, "overlap", isNewBookmark);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -394,6 +416,7 @@ var PlacesCommandHook = {
|
|||||||
_bookmarkPagePT: Task.async(function* (aBrowser, aParentId, aShowEditUI) {
|
_bookmarkPagePT: Task.async(function* (aBrowser, aParentId, aShowEditUI) {
|
||||||
let url = new URL(aBrowser.currentURI.spec);
|
let url = new URL(aBrowser.currentURI.spec);
|
||||||
let info = yield PlacesUtils.bookmarks.fetch({ url });
|
let info = yield PlacesUtils.bookmarks.fetch({ url });
|
||||||
|
let isNewBookmark = !info;
|
||||||
if (!info) {
|
if (!info) {
|
||||||
let parentGuid = aParentId !== undefined ?
|
let parentGuid = aParentId !== undefined ?
|
||||||
yield PlacesUtils.promiseItemGuid(aParentId) :
|
yield PlacesUtils.promiseItemGuid(aParentId) :
|
||||||
@ -417,10 +440,10 @@ var PlacesCommandHook = {
|
|||||||
Components.utils.reportError(e);
|
Components.utils.reportError(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (aShowEditUI) {
|
if (aShowEditUI && isNewBookmark) {
|
||||||
// If we bookmark the page here (i.e. page was not "starred" already)
|
// If we bookmark the page here but open right into a cancelable
|
||||||
// but open right into the "edit" state, start batching here, so
|
// state (i.e. new bookmark in Library), start batching here so
|
||||||
// "Cancel" in that state removes the bookmark.
|
// all of the actions can be undone in a single undo step.
|
||||||
StarUI.beginBatch();
|
StarUI.beginBatch();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -452,16 +475,16 @@ var PlacesCommandHook = {
|
|||||||
// 3. the content area
|
// 3. the content area
|
||||||
if (BookmarkingUI.anchor) {
|
if (BookmarkingUI.anchor) {
|
||||||
StarUI.showEditBookmarkPopup(node, BookmarkingUI.anchor,
|
StarUI.showEditBookmarkPopup(node, BookmarkingUI.anchor,
|
||||||
"bottomcenter topright");
|
"bottomcenter topright", isNewBookmark);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let identityIcon = document.getElementById("identity-icon");
|
let identityIcon = document.getElementById("identity-icon");
|
||||||
if (isElementVisible(identityIcon)) {
|
if (isElementVisible(identityIcon)) {
|
||||||
StarUI.showEditBookmarkPopup(node, identityIcon,
|
StarUI.showEditBookmarkPopup(node, identityIcon,
|
||||||
"bottomcenter topright");
|
"bottomcenter topright", isNewBookmark);
|
||||||
} else {
|
} else {
|
||||||
StarUI.showEditBookmarkPopup(node, aBrowser, "overlap");
|
StarUI.showEditBookmarkPopup(node, aBrowser, "overlap", isNewBookmark);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
|
|
||||||
@ -1703,19 +1726,15 @@ var BookmarkingUI = {
|
|||||||
let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID)
|
let widget = CustomizableUI.getWidget(this.BOOKMARK_BUTTON_ID)
|
||||||
.forWindow(window);
|
.forWindow(window);
|
||||||
if (widget.overflowed) {
|
if (widget.overflowed) {
|
||||||
// Allow to close the panel if the page is already bookmarked, cause
|
// Close the overflow panel because the Edit Bookmark panel will appear.
|
||||||
// we are going to open the edit bookmark panel.
|
widget.node.removeAttribute("closemenu");
|
||||||
if (isBookmarked)
|
|
||||||
widget.node.removeAttribute("closemenu");
|
|
||||||
else
|
|
||||||
widget.node.setAttribute("closemenu", "none");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ignore clicks on the star if we are updating its state.
|
// Ignore clicks on the star if we are updating its state.
|
||||||
if (!this._pendingStmt) {
|
if (!this._pendingStmt) {
|
||||||
if (!isBookmarked)
|
if (!isBookmarked)
|
||||||
this._showBookmarkedNotification();
|
this._showBookmarkedNotification();
|
||||||
PlacesCommandHook.bookmarkCurrentPage(isBookmarked);
|
PlacesCommandHook.bookmarkCurrentPage(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -599,6 +599,10 @@ toolbarbutton.bookmark-item {
|
|||||||
#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-icon {
|
#personal-bookmarks[cui-areatype="toolbar"] > #bookmarks-toolbar-placeholder > .toolbarbutton-icon {
|
||||||
image-rendering: -moz-crisp-edges;
|
image-rendering: -moz-crisp-edges;
|
||||||
}
|
}
|
||||||
|
/* Synced Tabs sidebar */
|
||||||
|
html|*.tabs-container html|*.item-tabs-list html|*.item-icon-container {
|
||||||
|
image-rendering: -moz-crisp-edges;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#editBMPanel_tagsSelector {
|
#editBMPanel_tagsSelector {
|
||||||
|
@ -2345,7 +2345,12 @@ function URLBarSetURI(aURI) {
|
|||||||
checkEmptyPageOrigin(gBrowser.selectedBrowser, uri)) {
|
checkEmptyPageOrigin(gBrowser.selectedBrowser, uri)) {
|
||||||
value = "";
|
value = "";
|
||||||
} else {
|
} else {
|
||||||
value = losslessDecodeURI(uri);
|
// We should deal with losslessDecodeURI throwing for exotic URIs
|
||||||
|
try {
|
||||||
|
value = losslessDecodeURI(uri);
|
||||||
|
} catch (ex) {
|
||||||
|
value = "about:blank";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
valid = !isBlankPageURL(uri.spec);
|
valid = !isBlankPageURL(uri.spec);
|
||||||
@ -6443,13 +6448,14 @@ function checkEmptyPageOrigin(browser = gBrowser.selectedBrowser,
|
|||||||
}
|
}
|
||||||
// Not all principals have URIs...
|
// Not all principals have URIs...
|
||||||
if (contentPrincipal.URI) {
|
if (contentPrincipal.URI) {
|
||||||
|
// A manually entered about:blank URI is slightly magical:
|
||||||
if (uri.spec == "about:blank" && contentPrincipal.isNullPrincipal) {
|
if (uri.spec == "about:blank" && contentPrincipal.isNullPrincipal) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return contentPrincipal.URI.equals(uri);
|
return contentPrincipal.URI.equals(uri);
|
||||||
}
|
}
|
||||||
// ... so for those that don't have them, enforce that the page has the
|
// ... so for those that don't have them, enforce that the page has the
|
||||||
// system principal (this matches e.g. on about:home).
|
// system principal (this matches e.g. on about:newtab).
|
||||||
let ssm = Services.scriptSecurityManager;
|
let ssm = Services.scriptSecurityManager;
|
||||||
return ssm.isSystemPrincipal(contentPrincipal);
|
return ssm.isSystemPrincipal(contentPrincipal);
|
||||||
}
|
}
|
||||||
|
@ -180,12 +180,6 @@
|
|||||||
<vbox>
|
<vbox>
|
||||||
<label id="editBookmarkPanelTitle"/>
|
<label id="editBookmarkPanelTitle"/>
|
||||||
<description id="editBookmarkPanelDescription"/>
|
<description id="editBookmarkPanelDescription"/>
|
||||||
<hbox>
|
|
||||||
<button id="editBookmarkPanelRemoveButton"
|
|
||||||
class="editBookmarkPanelHeaderButton"
|
|
||||||
oncommand="StarUI.removeBookmarkButtonCommand();"
|
|
||||||
accesskey="&editBookmark.removeBookmark.accessKey;"/>
|
|
||||||
</hbox>
|
|
||||||
</vbox>
|
</vbox>
|
||||||
</row>
|
</row>
|
||||||
<vbox id="editBookmarkPanelContent" flex="1" hidden="true"/>
|
<vbox id="editBookmarkPanelContent" flex="1" hidden="true"/>
|
||||||
@ -196,15 +190,15 @@
|
|||||||
label="&editBookmark.done.label;"
|
label="&editBookmark.done.label;"
|
||||||
default="true"
|
default="true"
|
||||||
oncommand="StarUI.panel.hidePopup();"/>
|
oncommand="StarUI.panel.hidePopup();"/>
|
||||||
<button id="editBookmarkPanelDeleteButton"
|
<button id="editBookmarkPanelRemoveButton"
|
||||||
class="editBookmarkPanelBottomButton"
|
class="editBookmarkPanelBottomButton"
|
||||||
label="&editBookmark.cancel.label;"
|
oncommand="StarUI.removeBookmarkButtonCommand();"
|
||||||
oncommand="StarUI.cancelButtonOnCommand();"/>
|
accesskey="&editBookmark.removeBookmark.accessKey;"/>
|
||||||
#else
|
#else
|
||||||
<button id="editBookmarkPanelDeleteButton"
|
<button id="editBookmarkPanelRemoveButton"
|
||||||
class="editBookmarkPanelBottomButton"
|
class="editBookmarkPanelBottomButton"
|
||||||
label="&editBookmark.cancel.label;"
|
oncommand="StarUI.removeBookmarkButtonCommand();"
|
||||||
oncommand="StarUI.cancelButtonOnCommand();"/>
|
accesskey="&editBookmark.removeBookmark.accessKey;"/>
|
||||||
<button id="editBookmarkPanelDoneButton"
|
<button id="editBookmarkPanelDoneButton"
|
||||||
class="editBookmarkPanelBottomButton"
|
class="editBookmarkPanelBottomButton"
|
||||||
label="&editBookmark.done.label;"
|
label="&editBookmark.done.label;"
|
||||||
|
@ -564,12 +564,18 @@ nsContextMenu.prototype = {
|
|||||||
LoginHelper.openPasswordManager(window, gContextMenuContentData.documentURIObject.host);
|
LoginHelper.openPasswordManager(window, gContextMenuContentData.documentURIObject.host);
|
||||||
},
|
},
|
||||||
|
|
||||||
inspectNode: function CM_inspectNode() {
|
inspectNode: function() {
|
||||||
let {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
let {devtools} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||||
let gBrowser = this.browser.ownerDocument.defaultView.gBrowser;
|
let gBrowser = this.browser.ownerDocument.defaultView.gBrowser;
|
||||||
let tt = devtools.TargetFactory.forTab(gBrowser.selectedTab);
|
let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
|
||||||
return gDevTools.showToolbox(tt, "inspector").then(function(toolbox) {
|
|
||||||
|
return gDevTools.showToolbox(target, "inspector").then(toolbox => {
|
||||||
let inspector = toolbox.getCurrentPanel();
|
let inspector = toolbox.getCurrentPanel();
|
||||||
|
|
||||||
|
// new-node-front tells us when the node has been selected, whether the
|
||||||
|
// browser is remote or not.
|
||||||
|
let onNewNode = inspector.selection.once("new-node-front");
|
||||||
|
|
||||||
if (this.isRemote) {
|
if (this.isRemote) {
|
||||||
this.browser.messageManager.sendAsyncMessage("debug:inspect", {}, {node: this.target});
|
this.browser.messageManager.sendAsyncMessage("debug:inspect", {}, {node: this.target});
|
||||||
inspector.walker.findInspectingNode().then(nodeFront => {
|
inspector.walker.findInspectingNode().then(nodeFront => {
|
||||||
@ -578,7 +584,13 @@ nsContextMenu.prototype = {
|
|||||||
} else {
|
} else {
|
||||||
inspector.selection.setNode(this.target, "browser-context-menu");
|
inspector.selection.setNode(this.target, "browser-context-menu");
|
||||||
}
|
}
|
||||||
}.bind(this));
|
|
||||||
|
return onNewNode.then(() => {
|
||||||
|
// Now that the node has been selected, wait until the inspector is
|
||||||
|
// fully updated.
|
||||||
|
return inspector.once("inspector-updated");
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// Set various context menu attributes based on the state of the world.
|
// Set various context menu attributes based on the state of the world.
|
||||||
@ -981,11 +993,16 @@ nsContextMenu.prototype = {
|
|||||||
catch (e) { }
|
catch (e) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
let params = this._openLinkInParameters({
|
let params = {
|
||||||
allowMixedContent: persistAllowMixedContentInChildTab,
|
allowMixedContent: persistAllowMixedContentInChildTab,
|
||||||
userContextId: parseInt(event.target.getAttribute('usercontextid')),
|
userContextId: parseInt(event.target.getAttribute('usercontextid'))
|
||||||
});
|
};
|
||||||
openLinkIn(this.linkURL, "tab", params);
|
|
||||||
|
if (params.userContextId != this.principal.originAttributes.userContextId) {
|
||||||
|
params.noReferrer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
openLinkIn(this.linkURL, "tab", this._openLinkInParameters(params));
|
||||||
},
|
},
|
||||||
|
|
||||||
// open URL in current tab
|
// open URL in current tab
|
||||||
|
@ -111,7 +111,8 @@ Sanitizer.prototype = {
|
|||||||
|
|
||||||
// Store the list of items to clear, in case we are killed before we
|
// Store the list of items to clear, in case we are killed before we
|
||||||
// get a chance to complete.
|
// get a chance to complete.
|
||||||
Preferences.set(Sanitizer.PREF_SANITIZE_IN_PROGRESS, JSON.stringify(itemsToClear));
|
Preferences.set(Sanitizer.PREF_SANITIZE_IN_PROGRESS,
|
||||||
|
JSON.stringify(itemsToClear));
|
||||||
|
|
||||||
// Store the list of items to clear, for debugging/forensics purposes
|
// Store the list of items to clear, for debugging/forensics purposes
|
||||||
for (let k of itemsToClear) {
|
for (let k of itemsToClear) {
|
||||||
@ -677,10 +678,18 @@ Sanitizer.prototype = {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// "Static" members
|
// The preferences branch for the sanitizer.
|
||||||
Sanitizer.PREF_DOMAIN = "privacy.sanitize.";
|
Sanitizer.PREF_DOMAIN = "privacy.sanitize.";
|
||||||
|
// Whether we should sanitize on shutdown.
|
||||||
Sanitizer.PREF_SANITIZE_ON_SHUTDOWN = "privacy.sanitize.sanitizeOnShutdown";
|
Sanitizer.PREF_SANITIZE_ON_SHUTDOWN = "privacy.sanitize.sanitizeOnShutdown";
|
||||||
|
// During a sanitization this is set to a json containing the array of items
|
||||||
|
// being sanitized, then cleared once the sanitization is complete.
|
||||||
|
// This allows to retry a sanitization on startup in case it was interrupted
|
||||||
|
// by a crash.
|
||||||
Sanitizer.PREF_SANITIZE_IN_PROGRESS = "privacy.sanitize.sanitizeInProgress";
|
Sanitizer.PREF_SANITIZE_IN_PROGRESS = "privacy.sanitize.sanitizeInProgress";
|
||||||
|
// Whether the previous shutdown sanitization completed successfully.
|
||||||
|
// Note that PREF_SANITIZE_IN_PROGRESS would be enough to detect an interrupted
|
||||||
|
// sanitization, but this is still supported for backwards compatibility.
|
||||||
Sanitizer.PREF_SANITIZE_DID_SHUTDOWN = "privacy.sanitize.didShutdownSanitize";
|
Sanitizer.PREF_SANITIZE_DID_SHUTDOWN = "privacy.sanitize.didShutdownSanitize";
|
||||||
|
|
||||||
// Time span constants corresponding to values of the privacy.sanitize.timeSpan
|
// Time span constants corresponding to values of the privacy.sanitize.timeSpan
|
||||||
@ -766,6 +775,15 @@ Sanitizer.sanitize = function(aParentWindow)
|
|||||||
};
|
};
|
||||||
|
|
||||||
Sanitizer.onStartup = Task.async(function*() {
|
Sanitizer.onStartup = Task.async(function*() {
|
||||||
|
// Check if we were interrupted during the last shutdown sanitization.
|
||||||
|
let shutownSanitizationWasInterrupted =
|
||||||
|
Preferences.get(Sanitizer.PREF_SANITIZE_ON_SHUTDOWN, false) &&
|
||||||
|
!Preferences.has(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN);
|
||||||
|
// Regardless, reset the pref, since we want to check it at the next startup
|
||||||
|
// even if the browser exits abruptly.
|
||||||
|
Preferences.reset(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN);
|
||||||
|
Services.prefs.savePrefFile(null);
|
||||||
|
|
||||||
// Make sure that we are triggered during shutdown, at the right time,
|
// Make sure that we are triggered during shutdown, at the right time,
|
||||||
// and only once.
|
// and only once.
|
||||||
let placesClient = Cc["@mozilla.org/browser/nav-history-service;1"]
|
let placesClient = Cc["@mozilla.org/browser/nav-history-service;1"]
|
||||||
@ -786,18 +804,19 @@ Sanitizer.onStartup = Task.async(function*() {
|
|||||||
}
|
}
|
||||||
placesClient.addBlocker("sanitize.js: Sanitize on shutdown", doSanitize);
|
placesClient.addBlocker("sanitize.js: Sanitize on shutdown", doSanitize);
|
||||||
|
|
||||||
// Handle incomplete sanitizations
|
// Check if Firefox crashed before completing a sanitization.
|
||||||
if (Preferences.has(Sanitizer.PREF_SANITIZE_IN_PROGRESS)) {
|
let lastInterruptedSanitization = Preferences.get(Sanitizer.PREF_SANITIZE_IN_PROGRESS, "");
|
||||||
// Firefox crashed during sanitization.
|
if (lastInterruptedSanitization) {
|
||||||
let s = new Sanitizer();
|
let s = new Sanitizer();
|
||||||
let json = Preferences.get(Sanitizer.PREF_SANITIZE_IN_PROGRESS);
|
// If the json is invalid this will just throw and reject the Task.
|
||||||
let itemsToClear = JSON.parse(json);
|
let itemsToClear = JSON.parse(lastInterruptedSanitization);
|
||||||
yield s.sanitize(itemsToClear);
|
yield s.sanitize(itemsToClear);
|
||||||
}
|
} else if (shutownSanitizationWasInterrupted) {
|
||||||
if (Preferences.has(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN)) {
|
// Ideally lastInterruptedSanitization should always be set when a
|
||||||
// Firefox crashed before having a chance to sanitize during shutdown.
|
// sanitization is interrupted, but some add-ons or Firefox previous
|
||||||
// (note that if Firefox crashed during shutdown sanitization, we
|
// versions may not set the pref.
|
||||||
// will hit both `if` so we will run a second double-sanitization).
|
// In such a case, we can still detect an interrupted shutdown sanitization,
|
||||||
|
// and just redo it.
|
||||||
yield Sanitizer.onShutdown();
|
yield Sanitizer.onShutdown();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -810,5 +829,8 @@ Sanitizer.onShutdown = Task.async(function*() {
|
|||||||
let s = new Sanitizer();
|
let s = new Sanitizer();
|
||||||
s.prefDomain = "privacy.clearOnShutdown.";
|
s.prefDomain = "privacy.clearOnShutdown.";
|
||||||
yield s.sanitize();
|
yield s.sanitize();
|
||||||
|
// We didn't crash during shutdown sanitization, so annotate it to avoid
|
||||||
|
// sanitizing again on startup.
|
||||||
Preferences.set(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN, true);
|
Preferences.set(Sanitizer.PREF_SANITIZE_DID_SHUTDOWN, true);
|
||||||
|
Services.prefs.savePrefFile(null);
|
||||||
});
|
});
|
||||||
|
@ -1026,11 +1026,16 @@
|
|||||||
|
|
||||||
if (!aForceUpdate) {
|
if (!aForceUpdate) {
|
||||||
TelemetryStopwatch.start("FX_TAB_SWITCH_UPDATE_MS");
|
TelemetryStopwatch.start("FX_TAB_SWITCH_UPDATE_MS");
|
||||||
if (!Services.appinfo.browserTabsRemoteAutostart) {
|
if (!gMultiProcessBrowser) {
|
||||||
// old way of measuring tab paint which is not
|
// old way of measuring tab paint which is not valid with e10s.
|
||||||
// valid with e10s.
|
// Waiting until the next MozAfterPaint ensures that we capture
|
||||||
window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils)
|
// the time it takes to paint, upload the textures to the compositor,
|
||||||
.beginTabSwitch();
|
// and then composite.
|
||||||
|
TelemetryStopwatch.start("FX_TAB_SWITCH_TOTAL_MS");
|
||||||
|
window.addEventListener("MozAfterPaint", function onMozAfterPaint() {
|
||||||
|
TelemetryStopwatch.finish("FX_TAB_SWITCH_TOTAL_MS");
|
||||||
|
window.removeEventListener("MozAfterPaint", onMozAfterPaint);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5891,7 +5896,7 @@
|
|||||||
bgLoad = !bgLoad;
|
bgLoad = !bgLoad;
|
||||||
|
|
||||||
let tab = this._getDragTargetTab(event, true);
|
let tab = this._getDragTargetTab(event, true);
|
||||||
if (!tab || dropEffect == "copy") {
|
if (!tab) {
|
||||||
// We're adding a new tab.
|
// We're adding a new tab.
|
||||||
let newIndex = this._getDropIndex(event, true);
|
let newIndex = this._getDropIndex(event, true);
|
||||||
let newTab = this.tabbrowser.loadOneTab(url, {inBackground: bgLoad, allowThirdPartyFixup: true});
|
let newTab = this.tabbrowser.loadOneTab(url, {inBackground: bgLoad, allowThirdPartyFixup: true});
|
||||||
@ -6243,9 +6248,11 @@
|
|||||||
if (browser.audioMuted) {
|
if (browser.audioMuted) {
|
||||||
browser.unmute();
|
browser.unmute();
|
||||||
this.removeAttribute("muted");
|
this.removeAttribute("muted");
|
||||||
|
BrowserUITelemetry.countTabMutingEvent("unmute", aMuteReason);
|
||||||
} else {
|
} else {
|
||||||
browser.mute();
|
browser.mute();
|
||||||
this.setAttribute("muted", "true");
|
this.setAttribute("muted", "true");
|
||||||
|
BrowserUITelemetry.countTabMutingEvent("mute", aMuteReason);
|
||||||
}
|
}
|
||||||
this.muteReason = aMuteReason || null;
|
this.muteReason = aMuteReason || null;
|
||||||
tabContainer.tabbrowser._tabAttrModified(this, ["muted"]);
|
tabContainer.tabbrowser._tabAttrModified(this, ["muted"]);
|
||||||
|
@ -8,5 +8,6 @@ support-files =
|
|||||||
[browser_notification_open_settings.js]
|
[browser_notification_open_settings.js]
|
||||||
[browser_notification_remove_permission.js]
|
[browser_notification_remove_permission.js]
|
||||||
[browser_notification_permission_migration.js]
|
[browser_notification_permission_migration.js]
|
||||||
|
[browser_notification_replace.js]
|
||||||
[browser_notification_tab_switching.js]
|
[browser_notification_tab_switching.js]
|
||||||
skip-if = buildapp == 'mulet'
|
skip-if = buildapp == 'mulet'
|
||||||
|
@ -28,7 +28,7 @@ add_task(function* test_notificationClose() {
|
|||||||
let alertTitleLabel = alertWindow.document.getElementById("alertTitleLabel");
|
let alertTitleLabel = alertWindow.document.getElementById("alertTitleLabel");
|
||||||
is(alertTitleLabel.value, "Test title", "Title text of notification should be present");
|
is(alertTitleLabel.value, "Test title", "Title text of notification should be present");
|
||||||
let alertTextLabel = alertWindow.document.getElementById("alertTextLabel");
|
let alertTextLabel = alertWindow.document.getElementById("alertTextLabel");
|
||||||
is(alertTextLabel.textContent, "Test body", "Body text of notification should be present");
|
is(alertTextLabel.textContent, "Test body 2", "Body text of notification should be present");
|
||||||
|
|
||||||
let alertCloseButton = alertWindow.document.querySelector(".alertCloseButton");
|
let alertCloseButton = alertWindow.document.querySelector(".alertCloseButton");
|
||||||
is(alertCloseButton.localName, "toolbarbutton", "close button found");
|
is(alertCloseButton.localName, "toolbarbutton", "close button found");
|
||||||
|
@ -0,0 +1,38 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
let notificationURL = "http://example.org/browser/browser/base/content/test/alerts/file_dom_notifications.html";
|
||||||
|
|
||||||
|
add_task(function* test_notificationReplace() {
|
||||||
|
let pm = Services.perms;
|
||||||
|
pm.add(makeURI(notificationURL), "desktop-notification", pm.ALLOW_ACTION);
|
||||||
|
|
||||||
|
yield BrowserTestUtils.withNewTab({
|
||||||
|
gBrowser,
|
||||||
|
url: notificationURL
|
||||||
|
}, function* dummyTabTask(aBrowser) {
|
||||||
|
yield ContentTask.spawn(aBrowser, {}, function* () {
|
||||||
|
let win = content.window.wrappedJSObject;
|
||||||
|
let notification = win.showNotification1();
|
||||||
|
let promiseCloseEvent = ContentTaskUtils.waitForEvent(notification, "close");
|
||||||
|
|
||||||
|
let showEvent = yield ContentTaskUtils.waitForEvent(notification, "show");
|
||||||
|
is(showEvent.target.body, "Test body 1", "Showed tagged notification");
|
||||||
|
|
||||||
|
let newNotification = win.showNotification2();
|
||||||
|
let newShowEvent = yield ContentTaskUtils.waitForEvent(newNotification, "show");
|
||||||
|
is(newShowEvent.target.body, "Test body 2", "Showed new notification with same tag");
|
||||||
|
|
||||||
|
let closeEvent = yield promiseCloseEvent;
|
||||||
|
is(closeEvent.target.body, "Test body 1", "Closed previous tagged notification");
|
||||||
|
|
||||||
|
let promiseNewCloseEvent = ContentTaskUtils.waitForEvent(newNotification, "close");
|
||||||
|
newNotification.close();
|
||||||
|
let newCloseEvent = yield promiseNewCloseEvent;
|
||||||
|
is(newCloseEvent.target.body, "Test body 2", "Closed new notification");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* cleanup() {
|
||||||
|
Services.perms.remove(makeURI(notificationURL), "desktop-notification");
|
||||||
|
});
|
@ -8,7 +8,7 @@ function showNotification1() {
|
|||||||
var options = {
|
var options = {
|
||||||
dir: undefined,
|
dir: undefined,
|
||||||
lang: undefined,
|
lang: undefined,
|
||||||
body: "Test body",
|
body: "Test body 1",
|
||||||
tag: "Test tag",
|
tag: "Test tag",
|
||||||
icon: undefined,
|
icon: undefined,
|
||||||
};
|
};
|
||||||
@ -23,7 +23,7 @@ function showNotification2() {
|
|||||||
var options = {
|
var options = {
|
||||||
dir: undefined,
|
dir: undefined,
|
||||||
lang: undefined,
|
lang: undefined,
|
||||||
body: "Test body",
|
body: "Test body 2",
|
||||||
tag: "Test tag",
|
tag: "Test tag",
|
||||||
icon: undefined,
|
icon: undefined,
|
||||||
};
|
};
|
||||||
|
@ -154,6 +154,7 @@ skip-if = e10s # Bug 1101993 - times out for unknown reasons when run in the dir
|
|||||||
skip-if = os == "mac" # The Fitt's Law back button is not supported on OS X
|
skip-if = os == "mac" # The Fitt's Law back button is not supported on OS X
|
||||||
[browser_beforeunload_duplicate_dialogs.js]
|
[browser_beforeunload_duplicate_dialogs.js]
|
||||||
[browser_blob-channelname.js]
|
[browser_blob-channelname.js]
|
||||||
|
[browser_bookmark_popup.js]
|
||||||
[browser_bookmark_titles.js]
|
[browser_bookmark_titles.js]
|
||||||
skip-if = buildapp == 'mulet' || toolkit == "windows" # Disabled on Windows due to frequent failures (bugs 825739, 841341)
|
skip-if = buildapp == 'mulet' || toolkit == "windows" # Disabled on Windows due to frequent failures (bugs 825739, 841341)
|
||||||
[browser_bug304198.js]
|
[browser_bug304198.js]
|
||||||
|
271
browser/base/content/test/general/browser_bookmark_popup.js
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test opening and closing the bookmarks panel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
let bookmarkPanel = document.getElementById("editBookmarkPanel");
|
||||||
|
let bookmarkStar = document.getElementById("bookmarks-menu-button");
|
||||||
|
let bookmarkPanelTitle = document.getElementById("editBookmarkPanelTitle");
|
||||||
|
|
||||||
|
StarUI._closePanelQuickForTesting = true;
|
||||||
|
Services.prefs.setBoolPref("browser.bookmarks.closePanelQuickForTesting", true);
|
||||||
|
|
||||||
|
function* test_bookmarks_popup({isNewBookmark, popupShowFn, popupEditFn,
|
||||||
|
shouldAutoClose, popupHideFn, isBookmarkRemoved}) {
|
||||||
|
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:home");
|
||||||
|
try {
|
||||||
|
if (!isNewBookmark) {
|
||||||
|
yield PlacesUtils.bookmarks.insert({
|
||||||
|
parentGuid: PlacesUtils.bookmarks.unfiledGuid,
|
||||||
|
url: "about:home",
|
||||||
|
title: "Home Page"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
is(bookmarkStar.hasAttribute("starred"), !isNewBookmark,
|
||||||
|
"Page should only be starred prior to popupshown if editing bookmark");
|
||||||
|
is(bookmarkPanel.state, "closed", "Panel should be 'closed' to start test");
|
||||||
|
let shownPromise = promisePopupShown(bookmarkPanel);
|
||||||
|
yield popupShowFn(tab.linkedBrowser);
|
||||||
|
yield shownPromise;
|
||||||
|
is(bookmarkPanel.state, "open", "Panel should be 'open' after shownPromise is resolved");
|
||||||
|
|
||||||
|
if (popupEditFn) {
|
||||||
|
yield popupEditFn();
|
||||||
|
}
|
||||||
|
let bookmarks = [];
|
||||||
|
yield PlacesUtils.bookmarks.fetch({url: "about:home"}, bm => bookmarks.push(bm));
|
||||||
|
is(bookmarks.length, 1, "Only one bookmark should exist");
|
||||||
|
is(bookmarkStar.getAttribute("starred"), "true", "Page is starred");
|
||||||
|
is(bookmarkPanel.state, "open", "Check that panel state is 'open'");
|
||||||
|
is(bookmarkPanelTitle.value,
|
||||||
|
isNewBookmark ?
|
||||||
|
gNavigatorBundle.getString("editBookmarkPanel.pageBookmarkedTitle") :
|
||||||
|
gNavigatorBundle.getString("editBookmarkPanel.editBookmarkTitle"),
|
||||||
|
"title should match isEditingBookmark state");
|
||||||
|
|
||||||
|
if (!shouldAutoClose) {
|
||||||
|
yield new Promise(resolve => setTimeout(resolve, 400));
|
||||||
|
}
|
||||||
|
|
||||||
|
let hiddenPromise = promisePopupHidden(bookmarkPanel);
|
||||||
|
if (popupHideFn) {
|
||||||
|
yield popupHideFn();
|
||||||
|
}
|
||||||
|
yield hiddenPromise;
|
||||||
|
is(bookmarkStar.hasAttribute("starred"), !isBookmarkRemoved,
|
||||||
|
"Page is starred after closing");
|
||||||
|
} finally {
|
||||||
|
let bookmark = yield PlacesUtils.bookmarks.fetch({url: "about:home"});
|
||||||
|
is(!!bookmark, !isBookmarkRemoved,
|
||||||
|
"bookmark should not be present if a panel action should've removed it");
|
||||||
|
if (bookmark) {
|
||||||
|
yield PlacesUtils.bookmarks.remove(bookmark);
|
||||||
|
}
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(function* panel_shown_for_new_bookmarks_and_autocloses() {
|
||||||
|
yield test_bookmarks_popup({
|
||||||
|
isNewBookmark: true,
|
||||||
|
popupShowFn() {
|
||||||
|
bookmarkStar.click();
|
||||||
|
},
|
||||||
|
shouldAutoClose: true,
|
||||||
|
isBookmarkRemoved: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* panel_shown_for_once_for_doubleclick_on_new_bookmark_star_and_autocloses() {
|
||||||
|
yield test_bookmarks_popup({
|
||||||
|
isNewBookmark: true,
|
||||||
|
popupShowFn() {
|
||||||
|
EventUtils.synthesizeMouse(bookmarkStar, 10, 10, { clickCount: 2 },
|
||||||
|
window);
|
||||||
|
},
|
||||||
|
shouldAutoClose: true,
|
||||||
|
isBookmarkRemoved: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* panel_shown_once_for_slow_doubleclick_on_new_bookmark_star_and_autocloses() {
|
||||||
|
todo(false, "bug 1250267, may need to add some tracking state to " +
|
||||||
|
"browser-places.js for this.");
|
||||||
|
return;
|
||||||
|
|
||||||
|
yield test_bookmarks_popup({
|
||||||
|
isNewBookmark: true,
|
||||||
|
*popupShowFn() {
|
||||||
|
EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window);
|
||||||
|
yield new Promise(resolve => setTimeout(resolve, 300));
|
||||||
|
EventUtils.synthesizeMouse(bookmarkStar, 10, 10, window);
|
||||||
|
},
|
||||||
|
shouldAutoClose: true,
|
||||||
|
isBookmarkRemoved: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* panel_shown_for_keyboardshortcut_on_new_bookmark_star_and_autocloses() {
|
||||||
|
yield test_bookmarks_popup({
|
||||||
|
isNewBookmark: true,
|
||||||
|
popupShowFn() {
|
||||||
|
EventUtils.synthesizeKey("D", {accelKey: true}, window);
|
||||||
|
},
|
||||||
|
shouldAutoClose: true,
|
||||||
|
isBookmarkRemoved: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* panel_shown_for_new_bookmarks_mouseover_mouseout() {
|
||||||
|
yield test_bookmarks_popup({
|
||||||
|
isNewBookmark: true,
|
||||||
|
popupShowFn() {
|
||||||
|
bookmarkStar.click();
|
||||||
|
},
|
||||||
|
*popupEditFn() {
|
||||||
|
let mouseOverPromise = new Promise(resolve => {
|
||||||
|
bookmarkPanel.addEventListener("mouseover", function onmouseover() {
|
||||||
|
bookmarkPanel.removeEventListener("mouseover", onmouseover);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
yield new Promise(resolve => {
|
||||||
|
EventUtils.synthesizeNativeMouseMove(bookmarkPanel, 0, 0, resolve, window);
|
||||||
|
});
|
||||||
|
info("Waiting for mouseover event");
|
||||||
|
yield mouseOverPromise;
|
||||||
|
info("Got mouseover event");
|
||||||
|
|
||||||
|
yield new Promise(resolve => setTimeout(resolve, 400));
|
||||||
|
is(bookmarkPanel.state, "open", "Panel should still be open on mouseover");
|
||||||
|
|
||||||
|
let mouseOutPromise = new Promise(resolve => {
|
||||||
|
bookmarkPanel.addEventListener("mouseout", function onmouseout() {
|
||||||
|
bookmarkPanel.removeEventListener("mouseout", onmouseout);
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
yield new Promise(resolve => {
|
||||||
|
EventUtils.synthesizeNativeMouseMove(bookmarkStar, 0, 0, resolve, window);
|
||||||
|
});
|
||||||
|
info("Waiting for mouseout event");
|
||||||
|
yield mouseOutPromise;
|
||||||
|
info("Got mouseout event, should autoclose now");
|
||||||
|
},
|
||||||
|
shouldAutoClose: true,
|
||||||
|
isBookmarkRemoved: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* panel_shown_for_new_bookmark_no_autoclose_close_with_ESC() {
|
||||||
|
yield test_bookmarks_popup({
|
||||||
|
isNewBookmark: false,
|
||||||
|
popupShowFn() {
|
||||||
|
bookmarkStar.click();
|
||||||
|
},
|
||||||
|
shouldAutoClose: false,
|
||||||
|
popupHideFn() {
|
||||||
|
EventUtils.synthesizeKey("VK_ESCAPE", {accelKey: true}, window);
|
||||||
|
},
|
||||||
|
isBookmarkRemoved: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* panel_shown_for_editing_no_autoclose_close_with_ESC() {
|
||||||
|
yield test_bookmarks_popup({
|
||||||
|
isNewBookmark: false,
|
||||||
|
popupShowFn() {
|
||||||
|
bookmarkStar.click();
|
||||||
|
},
|
||||||
|
shouldAutoClose: false,
|
||||||
|
popupHideFn() {
|
||||||
|
EventUtils.synthesizeKey("VK_ESCAPE", {accelKey: true}, window);
|
||||||
|
},
|
||||||
|
isBookmarkRemoved: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* panel_shown_for_new_bookmark_keypress_no_autoclose() {
|
||||||
|
yield test_bookmarks_popup({
|
||||||
|
isNewBookmark: true,
|
||||||
|
popupShowFn() {
|
||||||
|
bookmarkStar.click();
|
||||||
|
},
|
||||||
|
popupEditFn() {
|
||||||
|
EventUtils.sendChar("VK_TAB", window);
|
||||||
|
},
|
||||||
|
shouldAutoClose: false,
|
||||||
|
popupHideFn() {
|
||||||
|
bookmarkPanel.hidePopup();
|
||||||
|
},
|
||||||
|
isBookmarkRemoved: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* contextmenu_new_bookmark_click_no_autoclose() {
|
||||||
|
yield test_bookmarks_popup({
|
||||||
|
isNewBookmark: true,
|
||||||
|
*popupShowFn(browser) {
|
||||||
|
let contextMenu = document.getElementById("contentAreaContextMenu");
|
||||||
|
let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu,
|
||||||
|
"popupshown");
|
||||||
|
let awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu,
|
||||||
|
"popuphidden");
|
||||||
|
yield BrowserTestUtils.synthesizeMouseAtCenter("body", {
|
||||||
|
type: "contextmenu",
|
||||||
|
button: 2
|
||||||
|
}, browser);
|
||||||
|
yield awaitPopupShown;
|
||||||
|
document.getElementById("context-bookmarkpage").click();
|
||||||
|
contextMenu.hidePopup();
|
||||||
|
yield awaitPopupHidden;
|
||||||
|
},
|
||||||
|
popupEditFn() {
|
||||||
|
bookmarkPanelTitle.click();
|
||||||
|
},
|
||||||
|
shouldAutoClose: false,
|
||||||
|
popupHideFn() {
|
||||||
|
bookmarkPanel.hidePopup();
|
||||||
|
},
|
||||||
|
isBookmarkRemoved: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* bookmarks_menu_new_bookmark_remove_bookmark() {
|
||||||
|
yield test_bookmarks_popup({
|
||||||
|
isNewBookmark: true,
|
||||||
|
popupShowFn(browser) {
|
||||||
|
document.getElementById("menu_bookmarkThisPage").doCommand();
|
||||||
|
},
|
||||||
|
shouldAutoClose: true,
|
||||||
|
popupHideFn() {
|
||||||
|
document.getElementById("editBookmarkPanelRemoveButton").click();
|
||||||
|
},
|
||||||
|
isBookmarkRemoved: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* ctrl_d_edit_bookmark_remove_bookmark() {
|
||||||
|
yield test_bookmarks_popup({
|
||||||
|
isNewBookmark: false,
|
||||||
|
popupShowFn(browser) {
|
||||||
|
EventUtils.synthesizeKey("D", {accelKey: true}, window);
|
||||||
|
},
|
||||||
|
shouldAutoClose: true,
|
||||||
|
popupHideFn() {
|
||||||
|
document.getElementById("editBookmarkPanelRemoveButton").click();
|
||||||
|
},
|
||||||
|
isBookmarkRemoved: true,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
registerCleanupFunction(function() {
|
||||||
|
Services.prefs.clearUserPref("browser.bookmarks.closePanelQuickForTesting");
|
||||||
|
delete StarUI._closePanelQuickForTesting;
|
||||||
|
})
|
@ -60,6 +60,9 @@ function test() {
|
|||||||
testVal("<user:pass@sub1.sub2.sub3.>mozilla.org");
|
testVal("<user:pass@sub1.sub2.sub3.>mozilla.org");
|
||||||
testVal("<user:pass@>mozilla.org");
|
testVal("<user:pass@>mozilla.org");
|
||||||
|
|
||||||
|
testVal("<https://>mozilla.org< >");
|
||||||
|
testVal("mozilla.org< >");
|
||||||
|
|
||||||
testVal("<https://>mozilla.org</file.ext>");
|
testVal("<https://>mozilla.org</file.ext>");
|
||||||
testVal("<https://>mozilla.org</sub/file.ext>");
|
testVal("<https://>mozilla.org</sub/file.ext>");
|
||||||
testVal("<https://>mozilla.org</sub/file.ext?foo>");
|
testVal("<https://>mozilla.org</sub/file.ext?foo>");
|
||||||
|
@ -13,3 +13,5 @@ skip-if = os == 'linux' # Bug 1144816
|
|||||||
[browser_referrer_open_link_in_window.js]
|
[browser_referrer_open_link_in_window.js]
|
||||||
skip-if = os == 'linux' # Bug 1145199
|
skip-if = os == 'linux' # Bug 1145199
|
||||||
[browser_referrer_simple_click.js]
|
[browser_referrer_simple_click.js]
|
||||||
|
[browser_referrer_open_link_in_container_tab.js]
|
||||||
|
skip-if = os == 'linux' # Bug 1144816
|
||||||
|
@ -0,0 +1,50 @@
|
|||||||
|
// Tests referrer on context menu navigation - open link in new container tab.
|
||||||
|
// Selects "open link in new container tab" from the context menu.
|
||||||
|
|
||||||
|
function getReferrerTest(aTestNumber) {
|
||||||
|
let test = _referrerTests[aTestNumber];
|
||||||
|
if (test) {
|
||||||
|
// We want all the referrer tests to fail!
|
||||||
|
test.result = "";
|
||||||
|
}
|
||||||
|
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
|
function startNewTabTestCase(aTestNumber) {
|
||||||
|
info("browser_referrer_open_link_in_container_tab: " +
|
||||||
|
getReferrerTestDescription(aTestNumber));
|
||||||
|
contextMenuOpened(gTestWindow, "testlink").then(function(aContextMenu) {
|
||||||
|
someTabLoaded(gTestWindow).then(function(aNewTab) {
|
||||||
|
gTestWindow.gBrowser.selectedTab = aNewTab;
|
||||||
|
|
||||||
|
checkReferrerAndStartNextTest(aTestNumber, null, aNewTab,
|
||||||
|
startNewTabTestCase);
|
||||||
|
});
|
||||||
|
|
||||||
|
let menu = gTestWindow.document.getElementById("context-openlinkinusercontext-menu");
|
||||||
|
ok(menu && menu.firstChild, "The menu exists and it has a first child node.");
|
||||||
|
|
||||||
|
let menupopup = menu.firstChild;
|
||||||
|
is(menupopup.nodeType, Node.ELEMENT_NODE, "We have a menupopup.");
|
||||||
|
ok(menupopup.firstChild, "We have a first container entry.");
|
||||||
|
|
||||||
|
let firstContext = menupopup.firstChild;
|
||||||
|
is(firstContext.nodeType, Node.ELEMENT_NODE, "We have a first container entry.");
|
||||||
|
ok(firstContext.hasAttribute('usercontextid'), "We have a usercontextid value.");
|
||||||
|
|
||||||
|
firstContext.doCommand();
|
||||||
|
aContextMenu.hidePopup();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
waitForExplicitFinish();
|
||||||
|
|
||||||
|
SpecialPowers.pushPrefEnv(
|
||||||
|
{set: [["privacy.userContext.enabled", true]]},
|
||||||
|
function() {
|
||||||
|
requestLongerTimeout(10); // slowwww shutdown on e10s
|
||||||
|
startReferrerTest(startNewTabTestCase);
|
||||||
|
});
|
||||||
|
}
|
@ -1,3 +1,4 @@
|
|||||||
|
[browser_moz_action_link.js]
|
||||||
[browser_urlbar_blanking.js]
|
[browser_urlbar_blanking.js]
|
||||||
support-files =
|
support-files =
|
||||||
file_blank_but_not_blank.html
|
file_blank_but_not_blank.html
|
||||||
|
31
browser/base/content/test/urlbar/browser_moz_action_link.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
const kURIs = [
|
||||||
|
"moz-action:foo,",
|
||||||
|
"moz-action:foo",
|
||||||
|
];
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
for (let uri of kURIs) {
|
||||||
|
let dataURI = `data:text/html,<a id=a href="${uri}" target=_blank>Link</a>`;
|
||||||
|
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, dataURI);
|
||||||
|
|
||||||
|
let tabSwitchPromise = BrowserTestUtils.switchTab(gBrowser, function() {});
|
||||||
|
yield ContentTask.spawn(tab.linkedBrowser, null, function*() {
|
||||||
|
content.document.getElementById("a").click();
|
||||||
|
});
|
||||||
|
yield tabSwitchPromise;
|
||||||
|
isnot(gBrowser.selectedTab, tab, "Switched to new tab!");
|
||||||
|
is(gURLBar.value, "about:blank", "URL bar should be displaying about:blank");
|
||||||
|
let newTab = gBrowser.selectedTab;
|
||||||
|
yield BrowserTestUtils.switchTab(gBrowser, tab);
|
||||||
|
yield BrowserTestUtils.switchTab(gBrowser, newTab);
|
||||||
|
is(gBrowser.selectedTab, newTab, "Switched to new tab again!");
|
||||||
|
is(gURLBar.value, "about:blank", "URL bar should be displaying about:blank after tab switch");
|
||||||
|
// Finally, check that directly setting it produces the right results, too:
|
||||||
|
URLBarSetURI(makeURI(uri));
|
||||||
|
is(gURLBar.value, "about:blank", "URL bar should still be displaying about:blank");
|
||||||
|
yield BrowserTestUtils.removeTab(newTab);
|
||||||
|
yield BrowserTestUtils.removeTab(tab);
|
||||||
|
}
|
||||||
|
});
|
@ -261,7 +261,7 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||||||
trimmedLength = "http://".length;
|
trimmedLength = "http://".length;
|
||||||
}
|
}
|
||||||
|
|
||||||
let matchedURL = value.match(/^((?:[a-z]+:\/\/)(?:[^\/#?]+@)?)(.+?)(?::\d+)?(?:[\/#?]|$)/);
|
let matchedURL = value.match(/^((?:[a-z]+:\/\/)(?:[^\/#?]+@)?)(\S+?)(?::\d+)?\s*(?:[\/#?]|$)/);
|
||||||
if (!matchedURL)
|
if (!matchedURL)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -839,12 +839,13 @@ file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|||||||
<method name="_parseActionUrl">
|
<method name="_parseActionUrl">
|
||||||
<parameter name="aUrl"/>
|
<parameter name="aUrl"/>
|
||||||
<body><![CDATA[
|
<body><![CDATA[
|
||||||
if (!aUrl.startsWith("moz-action:"))
|
const MOZ_ACTION_REGEX = /^moz-action:([^,]+),(.*)$/;
|
||||||
|
if (!MOZ_ACTION_REGEX.test(aUrl))
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// URL is in the format moz-action:ACTION,PARAMS
|
// URL is in the format moz-action:ACTION,PARAMS
|
||||||
// Where PARAMS is a JSON encoded object.
|
// Where PARAMS is a JSON encoded object.
|
||||||
let [, type, params] = aUrl.match(/^moz-action:([^,]+),(.*)$/);
|
let [, type, params] = aUrl.match(MOZ_ACTION_REGEX);
|
||||||
|
|
||||||
let action = {
|
let action = {
|
||||||
type: type,
|
type: type,
|
||||||
|
@ -33,7 +33,6 @@ skip-if = os == "mac"
|
|||||||
skip-if = os == "linux"
|
skip-if = os == "linux"
|
||||||
|
|
||||||
[browser_901207_searchbar_in_panel.js]
|
[browser_901207_searchbar_in_panel.js]
|
||||||
skip-if = e10s # bug 1090656
|
|
||||||
[browser_913972_currentset_overflow.js]
|
[browser_913972_currentset_overflow.js]
|
||||||
skip-if = os == "linux"
|
skip-if = os == "linux"
|
||||||
|
|
||||||
@ -69,9 +68,9 @@ skip-if = os == "linux"
|
|||||||
[browser_947914_button_addons.js]
|
[browser_947914_button_addons.js]
|
||||||
skip-if = os == "linux" # Intermittent failures
|
skip-if = os == "linux" # Intermittent failures
|
||||||
[browser_947914_button_copy.js]
|
[browser_947914_button_copy.js]
|
||||||
skip-if = os == "linux" || e10s # Intermittent failures on Linux, e10s issues are bug 1091561
|
skip-if = os == "linux" # Intermittent failures on Linux
|
||||||
[browser_947914_button_cut.js]
|
[browser_947914_button_cut.js]
|
||||||
skip-if = os == "linux" || e10s # Intermittent failures on Linux, e10s issues are bug 1091561
|
skip-if = os == "linux" # Intermittent failures on Linux
|
||||||
[browser_947914_button_find.js]
|
[browser_947914_button_find.js]
|
||||||
skip-if = os == "linux" # Intermittent failures
|
skip-if = os == "linux" # Intermittent failures
|
||||||
[browser_947914_button_history.js]
|
[browser_947914_button_history.js]
|
||||||
@ -81,9 +80,9 @@ skip-if = os == "linux" # Intermittent failures
|
|||||||
[browser_947914_button_newWindow.js]
|
[browser_947914_button_newWindow.js]
|
||||||
skip-if = os == "linux" # Intermittent failures
|
skip-if = os == "linux" # Intermittent failures
|
||||||
[browser_947914_button_paste.js]
|
[browser_947914_button_paste.js]
|
||||||
skip-if = os == "linux" || e10s # Intermittent failures on Linux, e10s issues are bug 1091561
|
skip-if = os == "linux" # Intermittent failures on Linux
|
||||||
[browser_947914_button_print.js]
|
[browser_947914_button_print.js]
|
||||||
skip-if = os == "linux" || (os == "win" && e10s) # Intermittent failures on Linux, e10s issues on Windows (bug 1088714)
|
skip-if = os == "linux" # Intermittent failures on Linux
|
||||||
[browser_947914_button_savePage.js]
|
[browser_947914_button_savePage.js]
|
||||||
skip-if = os == "linux" # Intermittent failures
|
skip-if = os == "linux" # Intermittent failures
|
||||||
[browser_947914_button_zoomIn.js]
|
[browser_947914_button_zoomIn.js]
|
||||||
@ -123,7 +122,6 @@ skip-if = os == "linux"
|
|||||||
[browser_984455_bookmarks_items_reparenting.js]
|
[browser_984455_bookmarks_items_reparenting.js]
|
||||||
skip-if = os == "linux"
|
skip-if = os == "linux"
|
||||||
[browser_985815_propagate_setToolbarVisibility.js]
|
[browser_985815_propagate_setToolbarVisibility.js]
|
||||||
skip-if = e10s # bug 1090635
|
|
||||||
[browser_987177_destroyWidget_xul.js]
|
[browser_987177_destroyWidget_xul.js]
|
||||||
[browser_987177_xul_wrapper_updating.js]
|
[browser_987177_xul_wrapper_updating.js]
|
||||||
[browser_987185_syncButton.js]
|
[browser_987185_syncButton.js]
|
||||||
@ -133,7 +131,6 @@ skip-if = e10s # Bug 1088710
|
|||||||
[browser_988072_sidebar_events.js]
|
[browser_988072_sidebar_events.js]
|
||||||
[browser_989338_saved_placements_not_resaved.js]
|
[browser_989338_saved_placements_not_resaved.js]
|
||||||
[browser_989751_subviewbutton_class.js]
|
[browser_989751_subviewbutton_class.js]
|
||||||
skip-if = os == "linux" && e10s # Bug 1102900, bug 1104745, bug 1104761
|
|
||||||
[browser_992747_toggle_noncustomizable_toolbar.js]
|
[browser_992747_toggle_noncustomizable_toolbar.js]
|
||||||
[browser_993322_widget_notoolbar.js]
|
[browser_993322_widget_notoolbar.js]
|
||||||
[browser_995164_registerArea_during_customize_mode.js]
|
[browser_995164_registerArea_during_customize_mode.js]
|
||||||
|
@ -8,57 +8,52 @@ var initialLocation = gBrowser.currentURI.spec;
|
|||||||
var globalClipboard;
|
var globalClipboard;
|
||||||
|
|
||||||
add_task(function*() {
|
add_task(function*() {
|
||||||
info("Check copy button existence and functionality");
|
yield BrowserTestUtils.withNewTab({gBrowser, url: "about:blank"}, function*() {
|
||||||
|
info("Check copy button existence and functionality");
|
||||||
|
|
||||||
let testText = "copy text test";
|
let testText = "copy text test";
|
||||||
|
|
||||||
gURLBar.focus();
|
gURLBar.focus();
|
||||||
info("The URL bar was focused");
|
info("The URL bar was focused");
|
||||||
yield PanelUI.show();
|
yield PanelUI.show();
|
||||||
info("Menu panel was opened");
|
info("Menu panel was opened");
|
||||||
|
|
||||||
let copyButton = document.getElementById("copy-button");
|
let copyButton = document.getElementById("copy-button");
|
||||||
ok(copyButton, "Copy button exists in Panel Menu");
|
ok(copyButton, "Copy button exists in Panel Menu");
|
||||||
ok(copyButton.getAttribute("disabled"), "Copy button is initially disabled");
|
ok(copyButton.getAttribute("disabled"), "Copy button is initially disabled");
|
||||||
|
|
||||||
// copy text from URL bar
|
// copy text from URL bar
|
||||||
gURLBar.value = testText;
|
gURLBar.value = testText;
|
||||||
gURLBar.focus();
|
gURLBar.focus();
|
||||||
gURLBar.select();
|
gURLBar.select();
|
||||||
yield PanelUI.show();
|
yield PanelUI.show();
|
||||||
info("Menu panel was opened");
|
info("Menu panel was opened");
|
||||||
|
|
||||||
ok(!copyButton.hasAttribute("disabled"), "Copy button is enabled when selecting");
|
ok(!copyButton.hasAttribute("disabled"), "Copy button is enabled when selecting");
|
||||||
|
|
||||||
copyButton.click();
|
copyButton.click();
|
||||||
is(gURLBar.value, testText, "Selected text is unaltered when clicking copy");
|
is(gURLBar.value, testText, "Selected text is unaltered when clicking copy");
|
||||||
|
|
||||||
// check that the text was added to the clipboard
|
// check that the text was added to the clipboard
|
||||||
let clipboard = Services.clipboard;
|
let clipboard = Services.clipboard;
|
||||||
let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
|
let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
|
||||||
globalClipboard = clipboard.kGlobalClipboard;
|
globalClipboard = clipboard.kGlobalClipboard;
|
||||||
|
|
||||||
transferable.init(null);
|
transferable.init(null);
|
||||||
transferable.addDataFlavor("text/unicode");
|
transferable.addDataFlavor("text/unicode");
|
||||||
clipboard.getData(transferable, globalClipboard);
|
clipboard.getData(transferable, globalClipboard);
|
||||||
let str = {}, strLength = {};
|
let str = {}, strLength = {};
|
||||||
transferable.getTransferData("text/unicode", str, strLength);
|
transferable.getTransferData("text/unicode", str, strLength);
|
||||||
let clipboardValue = "";
|
let clipboardValue = "";
|
||||||
|
|
||||||
if (str.value) {
|
if (str.value) {
|
||||||
str.value.QueryInterface(Ci.nsISupportsString);
|
str.value.QueryInterface(Ci.nsISupportsString);
|
||||||
clipboardValue = str.value.data;
|
clipboardValue = str.value.data;
|
||||||
}
|
}
|
||||||
is(clipboardValue, testText, "Data was copied to the clipboard.");
|
is(clipboardValue, testText, "Data was copied to the clipboard.");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(function* asyncCleanup() {
|
registerCleanupFunction(function cleanup() {
|
||||||
// clear the clipboard
|
|
||||||
Services.clipboard.emptyClipboard(globalClipboard);
|
Services.clipboard.emptyClipboard(globalClipboard);
|
||||||
info("Clipboard was cleared");
|
|
||||||
|
|
||||||
// restore the tab as it was at the begining of the test
|
|
||||||
gBrowser.addTab(initialLocation);
|
|
||||||
gBrowser.removeTab(gBrowser.selectedTab);
|
|
||||||
info("Tabs were restored");
|
|
||||||
});
|
});
|
||||||
|
@ -8,55 +8,50 @@ var initialLocation = gBrowser.currentURI.spec;
|
|||||||
var globalClipboard;
|
var globalClipboard;
|
||||||
|
|
||||||
add_task(function*() {
|
add_task(function*() {
|
||||||
info("Check cut button existence and functionality");
|
yield BrowserTestUtils.withNewTab({gBrowser, url: "about:blank"}, function*() {
|
||||||
|
info("Check cut button existence and functionality");
|
||||||
|
|
||||||
let testText = "cut text test";
|
let testText = "cut text test";
|
||||||
|
|
||||||
gURLBar.focus();
|
gURLBar.focus();
|
||||||
yield PanelUI.show();
|
yield PanelUI.show();
|
||||||
info("Menu panel was opened");
|
info("Menu panel was opened");
|
||||||
|
|
||||||
let cutButton = document.getElementById("cut-button");
|
let cutButton = document.getElementById("cut-button");
|
||||||
ok(cutButton, "Cut button exists in Panel Menu");
|
ok(cutButton, "Cut button exists in Panel Menu");
|
||||||
ok(cutButton.hasAttribute("disabled"), "Cut button is disabled");
|
ok(cutButton.hasAttribute("disabled"), "Cut button is disabled");
|
||||||
|
|
||||||
// cut text from URL bar
|
// cut text from URL bar
|
||||||
gURLBar.value = testText;
|
gURLBar.value = testText;
|
||||||
gURLBar.focus();
|
gURLBar.focus();
|
||||||
gURLBar.select();
|
gURLBar.select();
|
||||||
yield PanelUI.show();
|
yield PanelUI.show();
|
||||||
info("Menu panel was opened");
|
info("Menu panel was opened");
|
||||||
|
|
||||||
ok(!cutButton.hasAttribute("disabled"), "Cut button is enabled when selecting");
|
ok(!cutButton.hasAttribute("disabled"), "Cut button is enabled when selecting");
|
||||||
cutButton.click();
|
cutButton.click();
|
||||||
is(gURLBar.value, "", "Selected text is removed from source when clicking on cut");
|
is(gURLBar.value, "", "Selected text is removed from source when clicking on cut");
|
||||||
|
|
||||||
// check that the text was added to the clipboard
|
// check that the text was added to the clipboard
|
||||||
let clipboard = Services.clipboard;
|
let clipboard = Services.clipboard;
|
||||||
let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
|
let transferable = Cc["@mozilla.org/widget/transferable;1"].createInstance(Ci.nsITransferable);
|
||||||
globalClipboard = clipboard.kGlobalClipboard;
|
globalClipboard = clipboard.kGlobalClipboard;
|
||||||
|
|
||||||
transferable.init(null);
|
transferable.init(null);
|
||||||
transferable.addDataFlavor("text/unicode");
|
transferable.addDataFlavor("text/unicode");
|
||||||
clipboard.getData(transferable, globalClipboard);
|
clipboard.getData(transferable, globalClipboard);
|
||||||
let str = {}, strLength = {};
|
let str = {}, strLength = {};
|
||||||
transferable.getTransferData("text/unicode", str, strLength);
|
transferable.getTransferData("text/unicode", str, strLength);
|
||||||
let clipboardValue = "";
|
let clipboardValue = "";
|
||||||
|
|
||||||
if (str.value) {
|
if (str.value) {
|
||||||
str.value.QueryInterface(Ci.nsISupportsString);
|
str.value.QueryInterface(Ci.nsISupportsString);
|
||||||
clipboardValue = str.value.data;
|
clipboardValue = str.value.data;
|
||||||
}
|
}
|
||||||
is(clipboardValue, testText, "Data was copied to the clipboard.");
|
is(clipboardValue, testText, "Data was copied to the clipboard.");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(function* asyncCleanup() {
|
registerCleanupFunction(function cleanup() {
|
||||||
// clear the clipboard
|
|
||||||
Services.clipboard.emptyClipboard(globalClipboard);
|
Services.clipboard.emptyClipboard(globalClipboard);
|
||||||
info("Clipboard was cleared");
|
|
||||||
|
|
||||||
// restore the tab as it was at the begining of the test
|
|
||||||
gBrowser.addTab(initialLocation);
|
|
||||||
gBrowser.removeTab(gBrowser.selectedTab);
|
|
||||||
info("Tabs were restored");
|
|
||||||
});
|
});
|
||||||
|
@ -8,40 +8,34 @@ var initialLocation = gBrowser.currentURI.spec;
|
|||||||
var globalClipboard;
|
var globalClipboard;
|
||||||
|
|
||||||
add_task(function*() {
|
add_task(function*() {
|
||||||
info("Check paste button existence and functionality");
|
yield BrowserTestUtils.withNewTab({gBrowser, url: "about:blank"}, function*() {
|
||||||
|
info("Check paste button existence and functionality");
|
||||||
|
|
||||||
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
let clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].getService(Ci.nsIClipboardHelper);
|
||||||
globalClipboard = Services.clipboard.kGlobalClipboard;
|
globalClipboard = Services.clipboard.kGlobalClipboard;
|
||||||
|
|
||||||
yield PanelUI.show();
|
yield PanelUI.show();
|
||||||
info("Menu panel was opened");
|
info("Menu panel was opened");
|
||||||
|
|
||||||
let pasteButton = document.getElementById("paste-button");
|
let pasteButton = document.getElementById("paste-button");
|
||||||
ok(pasteButton, "Paste button exists in Panel Menu");
|
ok(pasteButton, "Paste button exists in Panel Menu");
|
||||||
|
|
||||||
// add text to clipboard
|
// add text to clipboard
|
||||||
let text = "Sample text for testing";
|
let text = "Sample text for testing";
|
||||||
clipboard.copyString(text);
|
clipboard.copyString(text);
|
||||||
|
|
||||||
// test paste button by pasting text to URL bar
|
// test paste button by pasting text to URL bar
|
||||||
gURLBar.focus();
|
gURLBar.focus();
|
||||||
yield PanelUI.show();
|
yield PanelUI.show();
|
||||||
info("Menu panel was opened");
|
info("Menu panel was opened");
|
||||||
|
|
||||||
ok(!pasteButton.hasAttribute("disabled"), "Paste button is enabled");
|
ok(!pasteButton.hasAttribute("disabled"), "Paste button is enabled");
|
||||||
pasteButton.click();
|
pasteButton.click();
|
||||||
|
|
||||||
is(gURLBar.value, text, "Text pasted successfully");
|
is(gURLBar.value, text, "Text pasted successfully");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(function* asyncCleanup() {
|
registerCleanupFunction(function cleanup() {
|
||||||
// clear the clipboard
|
|
||||||
Services.clipboard.emptyClipboard(globalClipboard);
|
Services.clipboard.emptyClipboard(globalClipboard);
|
||||||
info("Clipboard was cleared");
|
|
||||||
|
|
||||||
// restore the tab as it was at the begining of the test
|
|
||||||
gBrowser.addTab(initialLocation);
|
|
||||||
gBrowser.removeTab(gBrowser.selectedTab);
|
|
||||||
info("Tabs were restored");
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -157,10 +157,11 @@ const Utils = {
|
|||||||
// We also reject handlers registered from a different host (see bug 402287)
|
// We also reject handlers registered from a different host (see bug 402287)
|
||||||
// The pref allows us to test the feature
|
// The pref allows us to test the feature
|
||||||
let pb = Services.prefs;
|
let pb = Services.prefs;
|
||||||
if ((!pb.prefHasUserValue(PREF_ALLOW_DIFFERENT_HOST) ||
|
if (!pb.getBoolPref(PREF_ALLOW_DIFFERENT_HOST) &&
|
||||||
!pb.getBoolPref(PREF_ALLOW_DIFFERENT_HOST)) &&
|
(!["http:", "https:"].includes(aContentWindow.location.protocol) ||
|
||||||
aContentWindow.location.hostname != uri.host)
|
aContentWindow.location.hostname != uri.host)) {
|
||||||
throw("Permission denied to add " + uri.spec + " as a content or protocol handler");
|
throw("Permission denied to add " + uri.spec + " as a content or protocol handler");
|
||||||
|
}
|
||||||
|
|
||||||
// If the uri doesn't contain '%s', it won't be a good handler
|
// If the uri doesn't contain '%s', it won't be a good handler
|
||||||
if (uri.spec.indexOf("%s") < 0)
|
if (uri.spec.indexOf("%s") < 0)
|
||||||
@ -413,7 +414,7 @@ WebContentConverterRegistrar.prototype = {
|
|||||||
let notificationValue = "Protocol Registration: " + aProtocol;
|
let notificationValue = "Protocol Registration: " + aProtocol;
|
||||||
let addButton = {
|
let addButton = {
|
||||||
label: this._getString("addProtocolHandlerAddButton"),
|
label: this._getString("addProtocolHandlerAddButton"),
|
||||||
accessKey: this._getString("addHandlerAddButtonAccesskey"),
|
accessKey: this._getString("addProtocolHandlerAddButtonAccesskey"),
|
||||||
protocolInfo: { protocol: aProtocol, uri: uri.spec, name: aTitle },
|
protocolInfo: { protocol: aProtocol, uri: uri.spec, name: aTitle },
|
||||||
|
|
||||||
callback(aNotification, aButtonInfo) {
|
callback(aNotification, aButtonInfo) {
|
||||||
|
@ -103,6 +103,9 @@ let gOpenDBs = new Map();
|
|||||||
|
|
||||||
// Track open libraries
|
// Track open libraries
|
||||||
let gLibs = {};
|
let gLibs = {};
|
||||||
|
this.ESE = ESE; // Required for tests.
|
||||||
|
this.KERNEL = KERNEL; // ditto
|
||||||
|
this.gLibs = gLibs; // ditto
|
||||||
|
|
||||||
function convertESEError(errorCode) {
|
function convertESEError(errorCode) {
|
||||||
switch (errorCode) {
|
switch (errorCode) {
|
||||||
@ -285,7 +288,10 @@ ESEDB.prototype = {
|
|||||||
ESE.SetSystemParameterW(this._instanceId.address(), 0,
|
ESE.SetSystemParameterW(this._instanceId.address(), 0,
|
||||||
2 /* JET_paramLogFilePath*/, 0, this.logPath);
|
2 /* JET_paramLogFilePath*/, 0, this.logPath);
|
||||||
|
|
||||||
|
// Shouldn't try to call JetTerm if the following call fails.
|
||||||
|
this._instanceCreated = false;
|
||||||
ESE.Init(this._instanceId.address());
|
ESE.Init(this._instanceId.address());
|
||||||
|
this._instanceCreated = true;
|
||||||
this._sessionId = new ESE.JET_SESID();
|
this._sessionId = new ESE.JET_SESID();
|
||||||
ESE.BeginSessionW(this._instanceId, this._sessionId.address(), null,
|
ESE.BeginSessionW(this._instanceId, this._sessionId.address(), null,
|
||||||
null);
|
null);
|
||||||
|
@ -52,15 +52,17 @@ XPCOMUtils.defineLazyGetter(this, "gEdgeDatabase", function() {
|
|||||||
* @param {function} filterFn a function that is called for each row.
|
* @param {function} filterFn a function that is called for each row.
|
||||||
* Only rows for which it returns a truthy
|
* Only rows for which it returns a truthy
|
||||||
* value are included in the result.
|
* value are included in the result.
|
||||||
|
* @param {nsIFile} dbFile the database file to use. Defaults to
|
||||||
|
* the main Edge database.
|
||||||
* @returns {Array} An array of row objects.
|
* @returns {Array} An array of row objects.
|
||||||
*/
|
*/
|
||||||
function readTableFromEdgeDB(tableName, columns, filterFn) {
|
function readTableFromEdgeDB(tableName, columns, filterFn, dbFile=gEdgeDatabase) {
|
||||||
let database;
|
let database;
|
||||||
let rows = [];
|
let rows = [];
|
||||||
try {
|
try {
|
||||||
let logFile = gEdgeDatabase.parent;
|
let logFile = dbFile.parent;
|
||||||
logFile.append("LogFiles");
|
logFile.append("LogFiles");
|
||||||
database = ESEDBReader.openDB(gEdgeDatabase.parent, gEdgeDatabase, logFile);
|
database = ESEDBReader.openDB(dbFile.parent, dbFile, logFile);
|
||||||
|
|
||||||
if (typeof columns == "function") {
|
if (typeof columns == "function") {
|
||||||
columns = columns(database);
|
columns = columns(database);
|
||||||
@ -74,7 +76,7 @@ function readTableFromEdgeDB(tableName, columns, filterFn) {
|
|||||||
}
|
}
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Cu.reportError("Failed to extract items from table " + tableName + " in Edge database at " +
|
Cu.reportError("Failed to extract items from table " + tableName + " in Edge database at " +
|
||||||
gEdgeDatabase.path + " due to the following error: " + ex);
|
dbFile.path + " due to the following error: " + ex);
|
||||||
// Deliberately make this fail so we expose failure in the UI:
|
// Deliberately make this fail so we expose failure in the UI:
|
||||||
throw ex;
|
throw ex;
|
||||||
} finally {
|
} finally {
|
||||||
@ -221,33 +223,36 @@ EdgeReadingListMigrator.prototype = {
|
|||||||
}),
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
function EdgeBookmarksMigrator() {
|
function EdgeBookmarksMigrator(dbOverride) {
|
||||||
|
this.dbOverride = dbOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
EdgeBookmarksMigrator.prototype = {
|
EdgeBookmarksMigrator.prototype = {
|
||||||
type: MigrationUtils.resourceTypes.BOOKMARKS,
|
type: MigrationUtils.resourceTypes.BOOKMARKS,
|
||||||
|
|
||||||
|
get db() { return this.dbOverride || gEdgeDatabase; },
|
||||||
|
|
||||||
get TABLE_NAME() { return "Favorites" },
|
get TABLE_NAME() { return "Favorites" },
|
||||||
|
|
||||||
get exists() {
|
get exists() {
|
||||||
if ("_exists" in this) {
|
if ("_exists" in this) {
|
||||||
return this._exists;
|
return this._exists;
|
||||||
}
|
}
|
||||||
return this._exists = (!!gEdgeDatabase && this._checkTableExists());
|
return this._exists = (!!this.db && this._checkTableExists());
|
||||||
},
|
},
|
||||||
|
|
||||||
_checkTableExists() {
|
_checkTableExists() {
|
||||||
let database;
|
let database;
|
||||||
let rv;
|
let rv;
|
||||||
try {
|
try {
|
||||||
let logFile = gEdgeDatabase.parent;
|
let logFile = this.db.parent;
|
||||||
logFile.append("LogFiles");
|
logFile.append("LogFiles");
|
||||||
database = ESEDBReader.openDB(gEdgeDatabase.parent, gEdgeDatabase, logFile);
|
database = ESEDBReader.openDB(this.db.parent, this.db, logFile);
|
||||||
|
|
||||||
rv = database.tableExists(this.TABLE_NAME);
|
rv = database.tableExists(this.TABLE_NAME);
|
||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
Cu.reportError("Failed to check for table " + tableName + " in Edge database at " +
|
Cu.reportError("Failed to check for table " + this.TABLE_NAME + " in Edge database at " +
|
||||||
gEdgeDatabase.path + " due to the following error: " + ex);
|
this.db.path + " due to the following error: " + ex);
|
||||||
return false;
|
return false;
|
||||||
} finally {
|
} finally {
|
||||||
if (database) {
|
if (database) {
|
||||||
@ -348,7 +353,7 @@ EdgeBookmarksMigrator.prototype = {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
let bookmarks = readTableFromEdgeDB(this.TABLE_NAME, columns, filterFn);
|
let bookmarks = readTableFromEdgeDB(this.TABLE_NAME, columns, filterFn, this.db);
|
||||||
return {bookmarks, folderMap};
|
return {bookmarks, folderMap};
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -388,10 +393,15 @@ EdgeBookmarksMigrator.prototype = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function EdgeProfileMigrator() {
|
function EdgeProfileMigrator() {
|
||||||
|
this.wrappedJSObject = this;
|
||||||
}
|
}
|
||||||
|
|
||||||
EdgeProfileMigrator.prototype = Object.create(MigratorPrototype);
|
EdgeProfileMigrator.prototype = Object.create(MigratorPrototype);
|
||||||
|
|
||||||
|
EdgeProfileMigrator.prototype.getESEMigratorForTesting = function(dbOverride) {
|
||||||
|
return new EdgeBookmarksMigrator(dbOverride);
|
||||||
|
};
|
||||||
|
|
||||||
EdgeProfileMigrator.prototype.getResources = function() {
|
EdgeProfileMigrator.prototype.getResources = function() {
|
||||||
let bookmarksMigrator = new EdgeBookmarksMigrator();
|
let bookmarksMigrator = new EdgeBookmarksMigrator();
|
||||||
if (!bookmarksMigrator.exists) {
|
if (!bookmarksMigrator.exists) {
|
||||||
|
@ -0,0 +1,465 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/ctypes.jsm");
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
let eseBackStage = Cu.import("resource:///modules/ESEDBReader.jsm");
|
||||||
|
let ESE = eseBackStage.ESE;
|
||||||
|
let KERNEL = eseBackStage.KERNEL;
|
||||||
|
let gLibs = eseBackStage.gLibs;
|
||||||
|
let COLUMN_TYPES = eseBackStage.COLUMN_TYPES;
|
||||||
|
let declareESEFunction = eseBackStage.declareESEFunction;
|
||||||
|
let loadLibraries = eseBackStage.loadLibraries;
|
||||||
|
|
||||||
|
let gESEInstanceCounter = 1;
|
||||||
|
|
||||||
|
ESE.JET_COLUMNCREATE_W = new ctypes.StructType("JET_COLUMNCREATE_W", [
|
||||||
|
{"cbStruct": ctypes.unsigned_long},
|
||||||
|
{"szColumnName": ESE.JET_PCWSTR},
|
||||||
|
{"coltyp": ESE.JET_COLTYP },
|
||||||
|
{"cbMax": ctypes.unsigned_long },
|
||||||
|
{"grbit": ESE.JET_GRBIT },
|
||||||
|
{"pvDefault": ctypes.voidptr_t},
|
||||||
|
{"cbDefault": ctypes.unsigned_long },
|
||||||
|
{"cp": ctypes.unsigned_long },
|
||||||
|
{"columnid": ESE.JET_COLUMNID},
|
||||||
|
{"err": ESE.JET_ERR},
|
||||||
|
]);
|
||||||
|
|
||||||
|
function createColumnCreationWrapper({name, type, cbMax}) {
|
||||||
|
// We use a wrapper object because we need to be sure the JS engine won't GC
|
||||||
|
// data that we're "only" pointing to.
|
||||||
|
let wrapper = {};
|
||||||
|
wrapper.column = new ESE.JET_COLUMNCREATE_W();
|
||||||
|
wrapper.column.cbStruct = ESE.JET_COLUMNCREATE_W.size;
|
||||||
|
let wchar_tArray = ctypes.ArrayType(ctypes.char16_t);
|
||||||
|
wrapper.name = new wchar_tArray(name.length + 1);
|
||||||
|
wrapper.name.value = String(name);
|
||||||
|
wrapper.column.szColumnName = wrapper.name;
|
||||||
|
wrapper.column.coltyp = type;
|
||||||
|
let fallback = 0;
|
||||||
|
switch (type) {
|
||||||
|
case COLUMN_TYPES.JET_coltypText:
|
||||||
|
fallback = 255;
|
||||||
|
case COLUMN_TYPES.JET_coltypLongText:
|
||||||
|
wrapper.column.cbMax = cbMax || fallback || 64 * 1024;
|
||||||
|
break;
|
||||||
|
case COLUMN_TYPES.JET_coltypGUID:
|
||||||
|
wrapper.column.cbMax = 16;
|
||||||
|
break;
|
||||||
|
case COLUMN_TYPES.JET_coltypBit:
|
||||||
|
wrapper.column.cbMax = 1;
|
||||||
|
break;
|
||||||
|
case COLUMN_TYPES.JET_coltypLongLong:
|
||||||
|
wrapper.column.cbMax = 8;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw "Unknown column type!";
|
||||||
|
}
|
||||||
|
|
||||||
|
wrapper.column.columnid = new ESE.JET_COLUMNID();
|
||||||
|
wrapper.column.grbit = 0;
|
||||||
|
wrapper.column.pvDefault = null;
|
||||||
|
wrapper.column.cbDefault = 0;
|
||||||
|
wrapper.column.cp = 0;
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
// "forward declarations" of indexcreate and setinfo structs, which we don't use.
|
||||||
|
ESE.JET_INDEXCREATE = new ctypes.StructType("JET_INDEXCREATE");
|
||||||
|
ESE.JET_SETINFO = new ctypes.StructType("JET_SETINFO");
|
||||||
|
|
||||||
|
ESE.JET_TABLECREATE_W = new ctypes.StructType("JET_TABLECREATE_W", [
|
||||||
|
{"cbStruct": ctypes.unsigned_long},
|
||||||
|
{"szTableName": ESE.JET_PCWSTR},
|
||||||
|
{"szTemplateTableName": ESE.JET_PCWSTR},
|
||||||
|
{"ulPages": ctypes.unsigned_long},
|
||||||
|
{"ulDensity": ctypes.unsigned_long},
|
||||||
|
{"rgcolumncreate": ESE.JET_COLUMNCREATE_W.ptr},
|
||||||
|
{"cColumns": ctypes.unsigned_long},
|
||||||
|
{"rgindexcreate": ESE.JET_INDEXCREATE.ptr},
|
||||||
|
{"cIndexes": ctypes.unsigned_long},
|
||||||
|
{"grbit": ESE.JET_GRBIT},
|
||||||
|
{"tableid": ESE.JET_TABLEID},
|
||||||
|
{"cCreated": ctypes.unsigned_long},
|
||||||
|
]);
|
||||||
|
|
||||||
|
function createTableCreationWrapper(tableName, columns) {
|
||||||
|
let wrapper = {};
|
||||||
|
let wchar_tArray = ctypes.ArrayType(ctypes.char16_t);
|
||||||
|
wrapper.name = new wchar_tArray(tableName.length + 1);
|
||||||
|
wrapper.name.value = String(tableName);
|
||||||
|
wrapper.table = new ESE.JET_TABLECREATE_W();
|
||||||
|
wrapper.table.cbStruct = ESE.JET_TABLECREATE_W.size;
|
||||||
|
wrapper.table.szTableName = wrapper.name;
|
||||||
|
wrapper.table.szTemplateTableName = null;
|
||||||
|
wrapper.table.ulPages = 1;
|
||||||
|
wrapper.table.ulDensity = 0;
|
||||||
|
let columnArrayType = ESE.JET_COLUMNCREATE_W.array(columns.length);
|
||||||
|
wrapper.columnAry = new columnArrayType();
|
||||||
|
wrapper.table.rgcolumncreate = wrapper.columnAry.addressOfElement(0);
|
||||||
|
wrapper.table.cColumns = columns.length;
|
||||||
|
wrapper.columns = [];
|
||||||
|
for (let i = 0; i < columns.length; i++) {
|
||||||
|
let column = columns[i];
|
||||||
|
let columnWrapper = createColumnCreationWrapper(column);
|
||||||
|
wrapper.columnAry.addressOfElement(i).contents = columnWrapper.column;
|
||||||
|
wrapper.columns.push(columnWrapper);
|
||||||
|
}
|
||||||
|
wrapper.table.rgindexcreate = null;
|
||||||
|
wrapper.table.cIndexes = 0;
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
function convertValueForWriting(value, valueType) {
|
||||||
|
let buffer;
|
||||||
|
let valueOfValueType = ctypes.UInt64.lo(valueType);
|
||||||
|
switch (valueOfValueType) {
|
||||||
|
case COLUMN_TYPES.JET_coltypLongLong:
|
||||||
|
if (value instanceof Date) {
|
||||||
|
buffer = new KERNEL.FILETIME();
|
||||||
|
let sysTime = new KERNEL.SYSTEMTIME();
|
||||||
|
sysTime.wYear = value.getUTCFullYear();
|
||||||
|
sysTime.wMonth = value.getUTCMonth() + 1;
|
||||||
|
sysTime.wDay = value.getUTCDate();
|
||||||
|
sysTime.wHour = value.getUTCHours();
|
||||||
|
sysTime.wMinute = value.getUTCMinutes();
|
||||||
|
sysTime.wSecond = value.getUTCSeconds();
|
||||||
|
sysTime.wMilliseconds = value.getUTCMilliseconds();
|
||||||
|
let rv = KERNEL.SystemTimeToFileTime(sysTime.address(), buffer.address());
|
||||||
|
if (!rv) {
|
||||||
|
throw new Error("Failed to get FileTime.");
|
||||||
|
}
|
||||||
|
return [buffer, KERNEL.FILETIME.size];
|
||||||
|
}
|
||||||
|
throw new Error("Unrecognized value for longlong column");
|
||||||
|
case COLUMN_TYPES.JET_coltypLongText:
|
||||||
|
let wchar_tArray = ctypes.ArrayType(ctypes.char16_t);
|
||||||
|
buffer = new wchar_tArray(value.length + 1);
|
||||||
|
buffer.value = String(value);
|
||||||
|
return [buffer, buffer.length * 2];
|
||||||
|
case COLUMN_TYPES.JET_coltypBit:
|
||||||
|
buffer = new ctypes.uint8_t();
|
||||||
|
// Bizarre boolean values, but whatever:
|
||||||
|
buffer.value = value ? 255 : 0;
|
||||||
|
return [buffer, 1];
|
||||||
|
case COLUMN_TYPES.JET_coltypGUID:
|
||||||
|
let byteArray = ctypes.ArrayType(ctypes.uint8_t);
|
||||||
|
buffer = new byteArray(16);
|
||||||
|
let j = 0;
|
||||||
|
for (let i = 0; i < value.length; i++) {
|
||||||
|
if (!(/[0-9a-f]/i).test(value[i])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let byteAsHex = value.substr(i, 2);
|
||||||
|
buffer[j++] = parseInt(byteAsHex, 16);
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
return [buffer, 16];
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Error("Unknown type " + valueType);
|
||||||
|
}
|
||||||
|
|
||||||
|
let initializedESE = false;
|
||||||
|
|
||||||
|
let eseDBWritingHelpers = {
|
||||||
|
setupDB(dbFile, tableName, columns, rows) {
|
||||||
|
if (!initializedESE) {
|
||||||
|
initializedESE = true;
|
||||||
|
loadLibraries();
|
||||||
|
|
||||||
|
KERNEL.SystemTimeToFileTime = gLibs.kernel.declare("SystemTimeToFileTime",
|
||||||
|
ctypes.default_abi, ctypes.bool, KERNEL.SYSTEMTIME.ptr, KERNEL.FILETIME.ptr);
|
||||||
|
|
||||||
|
declareESEFunction("CreateDatabaseW", ESE.JET_SESID, ESE.JET_PCWSTR,
|
||||||
|
ESE.JET_PCWSTR, ESE.JET_DBID.ptr, ESE.JET_GRBIT);
|
||||||
|
declareESEFunction("CreateTableColumnIndexW", ESE.JET_SESID, ESE.JET_DBID,
|
||||||
|
ESE.JET_TABLECREATE_W.ptr);
|
||||||
|
declareESEFunction("BeginTransaction", ESE.JET_SESID);
|
||||||
|
declareESEFunction("CommitTransaction", ESE.JET_SESID, ESE.JET_GRBIT);
|
||||||
|
declareESEFunction("PrepareUpdate", ESE.JET_SESID, ESE.JET_TABLEID,
|
||||||
|
ctypes.unsigned_long);
|
||||||
|
declareESEFunction("Update", ESE.JET_SESID, ESE.JET_TABLEID,
|
||||||
|
ctypes.voidptr_t, ctypes.unsigned_long,
|
||||||
|
ctypes.unsigned_long.ptr);
|
||||||
|
declareESEFunction("SetColumn", ESE.JET_SESID, ESE.JET_TABLEID,
|
||||||
|
ESE.JET_COLUMNID, ctypes.voidptr_t,
|
||||||
|
ctypes.unsigned_long, ESE.JET_GRBIT, ESE.JET_SETINFO.ptr);
|
||||||
|
ESE.SetSystemParameterW(null, 0, 64 /* JET_paramDatabasePageSize*/,
|
||||||
|
8192, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
let rootPath = dbFile.parent.path + "\\";
|
||||||
|
let logPath = rootPath + "LogFiles\\";
|
||||||
|
|
||||||
|
try {
|
||||||
|
this._instanceId = new ESE.JET_INSTANCE();
|
||||||
|
ESE.CreateInstanceW(this._instanceId.address(),
|
||||||
|
"firefox-dbwriter-" + (gESEInstanceCounter++));
|
||||||
|
this._instanceCreated = true;
|
||||||
|
|
||||||
|
ESE.SetSystemParameterW(this._instanceId.address(), 0,
|
||||||
|
0 /* JET_paramSystemPath*/, 0, rootPath);
|
||||||
|
ESE.SetSystemParameterW(this._instanceId.address(), 0,
|
||||||
|
1 /* JET_paramTempPath */, 0, rootPath);
|
||||||
|
ESE.SetSystemParameterW(this._instanceId.address(), 0,
|
||||||
|
2 /* JET_paramLogFilePath*/, 0, logPath);
|
||||||
|
// Shouldn't try to call JetTerm if the following call fails.
|
||||||
|
this._instanceCreated = false;
|
||||||
|
ESE.Init(this._instanceId.address());
|
||||||
|
this._instanceCreated = true;
|
||||||
|
this._sessionId = new ESE.JET_SESID();
|
||||||
|
ESE.BeginSessionW(this._instanceId, this._sessionId.address(), null,
|
||||||
|
null);
|
||||||
|
this._sessionCreated = true;
|
||||||
|
|
||||||
|
this._dbId = new ESE.JET_DBID();
|
||||||
|
this._dbPath = rootPath + "spartan.edb";
|
||||||
|
ESE.CreateDatabaseW(this._sessionId, this._dbPath, null,
|
||||||
|
this._dbId.address(), 0);
|
||||||
|
this._opened = this._attached = true;
|
||||||
|
|
||||||
|
let tableCreationWrapper = createTableCreationWrapper(tableName, columns);
|
||||||
|
ESE.CreateTableColumnIndexW(this._sessionId, this._dbId,
|
||||||
|
tableCreationWrapper.table.address());
|
||||||
|
this._tableId = tableCreationWrapper.table.tableid;
|
||||||
|
|
||||||
|
let columnIdMap = new Map();
|
||||||
|
if (rows.length) {
|
||||||
|
// Iterate over the struct we passed into ESENT because they have the
|
||||||
|
// created column ids.
|
||||||
|
let columnCount = ctypes.UInt64.lo(tableCreationWrapper.table.cColumns);
|
||||||
|
let columnsPassed = tableCreationWrapper.table.rgcolumncreate;
|
||||||
|
for (let i = 0; i < columnCount; i++) {
|
||||||
|
let column = columnsPassed.contents;
|
||||||
|
columnIdMap.set(column.szColumnName.readString(), column);
|
||||||
|
columnsPassed = columnsPassed.increment();
|
||||||
|
}
|
||||||
|
let rv = ESE.ManualMove(this._sessionId, this._tableId,
|
||||||
|
-2147483648 /* JET_MoveFirst */, 0);
|
||||||
|
ESE.BeginTransaction(this._sessionId);
|
||||||
|
for (let row of rows) {
|
||||||
|
ESE.PrepareUpdate(this._sessionId, this._tableId, 0 /* JET_prepInsert */);
|
||||||
|
for (let columnName in row) {
|
||||||
|
let col = columnIdMap.get(columnName);
|
||||||
|
let colId = col.columnid;
|
||||||
|
let [val, valSize] = convertValueForWriting(row[columnName], col.coltyp);
|
||||||
|
/* JET_bitSetOverwriteLV */
|
||||||
|
ESE.SetColumn(this._sessionId, this._tableId, colId, val.address(), valSize, 4, null);
|
||||||
|
}
|
||||||
|
let actualBookmarkSize = new ctypes.unsigned_long();
|
||||||
|
ESE.Update(this._sessionId, this._tableId, null, 0, actualBookmarkSize.address());
|
||||||
|
}
|
||||||
|
ESE.CommitTransaction(this._sessionId, 0 /* JET_bitWaitLastLevel0Commit */);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
this._close();
|
||||||
|
} catch (ex) {
|
||||||
|
Cu.reportError(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
_close() {
|
||||||
|
if (this._tableId) {
|
||||||
|
ESE.FailSafeCloseTable(this._sessionId, this._tableId);
|
||||||
|
delete this._tableId;
|
||||||
|
}
|
||||||
|
if (this._opened) {
|
||||||
|
ESE.FailSafeCloseDatabase(this._sessionId, this._dbId, 0);
|
||||||
|
this._opened = false;
|
||||||
|
}
|
||||||
|
if (this._attached) {
|
||||||
|
ESE.FailSafeDetachDatabaseW(this._sessionId, this._dbPath);
|
||||||
|
this._attached = false;
|
||||||
|
}
|
||||||
|
if (this._sessionCreated) {
|
||||||
|
ESE.FailSafeEndSession(this._sessionId, 0);
|
||||||
|
this._sessionCreated = false;
|
||||||
|
}
|
||||||
|
if (this._instanceCreated) {
|
||||||
|
ESE.FailSafeTerm(this._instanceId);
|
||||||
|
this._instanceCreated = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
add_task(function*() {
|
||||||
|
let tempFile = Services.dirsvc.get("TmpD", Ci.nsIFile);
|
||||||
|
tempFile.append("fx-xpcshell-edge-db");
|
||||||
|
tempFile.createUnique(tempFile.DIRECTORY_TYPE, 0o600);
|
||||||
|
|
||||||
|
let db = tempFile.clone();
|
||||||
|
db.append("spartan.edb");
|
||||||
|
|
||||||
|
let logs = tempFile.clone();
|
||||||
|
logs.append("LogFiles");
|
||||||
|
logs.create(tempFile.DIRECTORY_TYPE, 0o600);
|
||||||
|
|
||||||
|
let creationDate = new Date(Date.now() - 5000);
|
||||||
|
const kEdgeMenuParent = "62d07e2b-5f0d-4e41-8426-5f5ec9717beb";
|
||||||
|
let itemsInDB = [
|
||||||
|
{
|
||||||
|
URL: "http://www.mozilla.org/",
|
||||||
|
Title: "Mozilla",
|
||||||
|
DateUpdated: new Date(creationDate.valueOf() + 100),
|
||||||
|
ItemId: "1c00c10a-15f6-4618-92dd-22575102a4da",
|
||||||
|
ParentId: kEdgeMenuParent,
|
||||||
|
IsFolder: false,
|
||||||
|
IsDeleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Folder",
|
||||||
|
DateUpdated: new Date(creationDate.valueOf() + 200),
|
||||||
|
ItemId: "564b21f2-05d6-4f7d-8499-304d00ccc3aa",
|
||||||
|
ParentId: kEdgeMenuParent,
|
||||||
|
IsFolder: true,
|
||||||
|
IsDeleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Item in folder",
|
||||||
|
URL: "http://www.iteminfolder.org/",
|
||||||
|
DateUpdated: new Date(creationDate.valueOf() + 300),
|
||||||
|
ItemId: "c295ddaf-04a1-424a-866c-0ebde011e7c8",
|
||||||
|
ParentId: "564b21f2-05d6-4f7d-8499-304d00ccc3aa",
|
||||||
|
IsFolder: false,
|
||||||
|
IsDeleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Deleted folder",
|
||||||
|
DateUpdated: new Date(creationDate.valueOf() + 400),
|
||||||
|
ItemId: "a547573c-4d4d-4406-a736-5b5462d93bca",
|
||||||
|
ParentId: kEdgeMenuParent,
|
||||||
|
IsFolder: true,
|
||||||
|
IsDeleted: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Deleted item",
|
||||||
|
URL: "http://www.deleteditem.org/",
|
||||||
|
DateUpdated: new Date(creationDate.valueOf() + 500),
|
||||||
|
ItemId: "37a574bb-b44b-4bbc-a414-908615536435",
|
||||||
|
ParentId: kEdgeMenuParent,
|
||||||
|
IsFolder: false,
|
||||||
|
IsDeleted: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Item in deleted folder (should be in root)",
|
||||||
|
URL: "http://www.itemindeletedfolder.org/",
|
||||||
|
DateUpdated: new Date(creationDate.valueOf() + 600),
|
||||||
|
ItemId: "74dd1cc3-4c5d-471f-bccc-7bc7c72fa621",
|
||||||
|
ParentId: "a547573c-4d4d-4406-a736-5b5462d93bca",
|
||||||
|
IsFolder: false,
|
||||||
|
IsDeleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "_Favorites_Bar_",
|
||||||
|
DateUpdated: new Date(creationDate.valueOf() + 700),
|
||||||
|
ItemId: "921dc8a0-6c83-40ef-8df1-9bd1c5c56aaf",
|
||||||
|
ParentId: kEdgeMenuParent,
|
||||||
|
IsFolder: true,
|
||||||
|
IsDeleted: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Title: "Item in favorites bar",
|
||||||
|
URL: "http://www.iteminfavoritesbar.org/",
|
||||||
|
DateUpdated: new Date(creationDate.valueOf() + 800),
|
||||||
|
ItemId: "9f2b1ff8-b651-46cf-8f41-16da8bcb6791",
|
||||||
|
ParentId: "921dc8a0-6c83-40ef-8df1-9bd1c5c56aaf",
|
||||||
|
IsFolder: false,
|
||||||
|
IsDeleted: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
eseDBWritingHelpers.setupDB(db, "Favorites", [
|
||||||
|
{type: COLUMN_TYPES.JET_coltypLongText, name: "URL", cbMax: 4096},
|
||||||
|
{type: COLUMN_TYPES.JET_coltypLongText, name: "Title", cbMax: 4096},
|
||||||
|
{type: COLUMN_TYPES.JET_coltypLongLong, name: "DateUpdated"},
|
||||||
|
{type: COLUMN_TYPES.JET_coltypGUID, name: "ItemId"},
|
||||||
|
{type: COLUMN_TYPES.JET_coltypBit, name: "IsDeleted"},
|
||||||
|
{type: COLUMN_TYPES.JET_coltypBit, name: "IsFolder"},
|
||||||
|
{type: COLUMN_TYPES.JET_coltypGUID, name: "ParentId"},
|
||||||
|
], itemsInDB);
|
||||||
|
|
||||||
|
let migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=edge"]
|
||||||
|
.createInstance(Ci.nsIBrowserProfileMigrator);
|
||||||
|
let bookmarksMigrator = migrator.wrappedJSObject.getESEMigratorForTesting(db);
|
||||||
|
Assert.ok(bookmarksMigrator.exists, "Should recognize table we just created");
|
||||||
|
|
||||||
|
let seenBookmarks = [];
|
||||||
|
let bookmarkObserver = {
|
||||||
|
onItemAdded(itemId, parentId, index, itemType, url, title, dateAdded, itemGuid, parentGuid) {
|
||||||
|
if (title.startsWith("Deleted")) {
|
||||||
|
ok(false, "Should not see deleted items being bookmarked!");
|
||||||
|
}
|
||||||
|
seenBookmarks.push({itemId, parentId, index, itemType, url, title, dateAdded, itemGuid, parentGuid});
|
||||||
|
},
|
||||||
|
onBeginUpdateBatch() {},
|
||||||
|
onEndUpdateBatch() {},
|
||||||
|
onItemRemoved() {},
|
||||||
|
onItemChanged() {},
|
||||||
|
onItemVisited() {},
|
||||||
|
onItemMoved() {},
|
||||||
|
}
|
||||||
|
PlacesUtils.bookmarks.addObserver(bookmarkObserver, false);
|
||||||
|
|
||||||
|
let migrateResult = yield new Promise(resolve => bookmarksMigrator.migrate(resolve)).catch(ex => {
|
||||||
|
Cu.reportError(ex);
|
||||||
|
Assert.ok(false, "Got an exception trying to migrate data! " + ex);
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
PlacesUtils.bookmarks.removeObserver(bookmarkObserver);
|
||||||
|
Assert.ok(migrateResult, "Migration should succeed");
|
||||||
|
Assert.equal(seenBookmarks.length, 7, "Should have seen 7 items being bookmarked.");
|
||||||
|
|
||||||
|
let menuParents = seenBookmarks.filter(item => item.parentGuid == PlacesUtils.bookmarks.menuGuid);
|
||||||
|
Assert.equal(menuParents.length, 1, "Should have a single folder added to the menu");
|
||||||
|
let toolbarParents = seenBookmarks.filter(item => item.parentGuid == PlacesUtils.bookmarks.toolbarGuid);
|
||||||
|
Assert.equal(toolbarParents.length, 1, "Should have a single item added to the toolbar");
|
||||||
|
let menuParentGuid = menuParents[0].itemGuid;
|
||||||
|
let toolbarParentGuid = toolbarParents[0].itemGuid;
|
||||||
|
|
||||||
|
let expectedTitlesInMenu = itemsInDB.filter(item => item.ParentId == kEdgeMenuParent).map(item => item.Title);
|
||||||
|
// Hacky, but seems like much the simplest way:
|
||||||
|
expectedTitlesInMenu.push("Item in deleted folder (should be in root)");
|
||||||
|
let expectedTitlesInToolbar = itemsInDB.filter(item => item.ParentId == "921dc8a0-6c83-40ef-8df1-9bd1c5c56aaf").map(item => item.Title);
|
||||||
|
|
||||||
|
let edgeNameStr = MigrationUtils.getLocalizedString("sourceNameEdge");
|
||||||
|
let importParentFolderName = MigrationUtils.getLocalizedString("importedBookmarksFolder", [edgeNameStr]);
|
||||||
|
|
||||||
|
for (let bookmark of seenBookmarks) {
|
||||||
|
let shouldBeInMenu = expectedTitlesInMenu.includes(bookmark.title);
|
||||||
|
let shouldBeInToolbar = expectedTitlesInToolbar.includes(bookmark.title);
|
||||||
|
if (bookmark.title == "Folder" || bookmark.title == importParentFolderName) {
|
||||||
|
Assert.equal(bookmark.itemType, PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||||
|
"Bookmark " + bookmark.title + " should be a folder");
|
||||||
|
} else {
|
||||||
|
Assert.notEqual(bookmark.itemType, PlacesUtils.bookmarks.TYPE_FOLDER,
|
||||||
|
"Bookmark " + bookmark.title + " should not be a folder");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldBeInMenu) {
|
||||||
|
Assert.equal(bookmark.parentGuid, menuParentGuid, "Item '" + bookmark.title + "' should be in menu");
|
||||||
|
} else if (shouldBeInToolbar) {
|
||||||
|
Assert.equal(bookmark.parentGuid, toolbarParentGuid, "Item '" + bookmark.title + "' should be in toolbar");
|
||||||
|
} else if (bookmark.itemGuid == menuParentGuid || bookmark.itemGuid == toolbarParentGuid) {
|
||||||
|
Assert.ok(true, "Expect toolbar and menu folders to not be in menu or toolbar");
|
||||||
|
} else {
|
||||||
|
// Bit hacky, but we do need to check this.
|
||||||
|
Assert.equal(bookmark.title, "Item in folder", "Subfoldered item shouldn't be in menu or toolbar");
|
||||||
|
let parent = seenBookmarks.find(maybeParent => maybeParent.itemGuid == bookmark.parentGuid);
|
||||||
|
Assert.equal(parent && parent.title, "Folder", "Subfoldered item should be in subfolder labeled 'Folder'");
|
||||||
|
}
|
||||||
|
|
||||||
|
let dbItem = itemsInDB.find(dbItem => bookmark.title == dbItem.Title);
|
||||||
|
if (!dbItem) {
|
||||||
|
Assert.equal(bookmark.title, importParentFolderName, "Only the extra layer of folders isn't in the input we stuck in the DB.");
|
||||||
|
Assert.ok([menuParentGuid, toolbarParentGuid].includes(bookmark.itemGuid), "This item should be one of the containers");
|
||||||
|
} else {
|
||||||
|
Assert.equal(dbItem.URL || null, bookmark.url && bookmark.url.spec, "URL is correct");
|
||||||
|
Assert.equal(dbItem.DateUpdated.valueOf(), (new Date(bookmark.dateAdded / 1000)).valueOf(), "Date added is correct");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -12,6 +12,8 @@ skip-if = os != "mac" # Relies on ULibDir
|
|||||||
[test_Chrome_passwords.js]
|
[test_Chrome_passwords.js]
|
||||||
skip-if = os != "win"
|
skip-if = os != "win"
|
||||||
[test_Edge_availability.js]
|
[test_Edge_availability.js]
|
||||||
|
[test_Edge_db_migration.js]
|
||||||
|
skip-if = os != "win" || os_version == "5.1" || os_version == "5.2" # Relies on post-XP bits of ESEDB
|
||||||
[test_fx_telemetry.js]
|
[test_fx_telemetry.js]
|
||||||
[test_IE_bookmarks.js]
|
[test_IE_bookmarks.js]
|
||||||
skip-if = os != "win"
|
skip-if = os != "win"
|
||||||
|
@ -271,9 +271,7 @@ var gSyncPane = {
|
|||||||
gSyncPane.signIn();
|
gSyncPane.signIn();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
setEventListener("verifiedManage", "click",
|
setEventListener("fxaUnlinkButton", "command", function () {
|
||||||
gSyncPane.manageFirefoxAccount);
|
|
||||||
setEventListener("fxaUnlinkButton", "click", function () {
|
|
||||||
gSyncPane.unlinkFirefoxAccount(true);
|
gSyncPane.unlinkFirefoxAccount(true);
|
||||||
});
|
});
|
||||||
setEventListener("verifyFxaAccount", "command",
|
setEventListener("verifyFxaAccount", "command",
|
||||||
@ -550,13 +548,34 @@ var gSyncPane = {
|
|||||||
this._openAboutAccounts("reauth");
|
this._openAboutAccounts("reauth");
|
||||||
},
|
},
|
||||||
|
|
||||||
openChangeProfileImage: function() {
|
|
||||||
fxAccounts.promiseAccountsChangeProfileURI(this._getEntryPoint(), "avatar")
|
clickOrSpaceOrEnterPressed: function(event) {
|
||||||
.then(url => {
|
// Note: charCode is deprecated, but 'char' not yet implemented.
|
||||||
|
// Replace charCode with char when implemented, see Bug 680830
|
||||||
|
if ((event.type == "click" && event.button == 0) || // button 0 = 'main button', typically left click.
|
||||||
|
(event.type == "keypress" &&
|
||||||
|
(event.charCode == KeyEvent.DOM_VK_SPACE || event.keyCode == KeyEvent.DOM_VK_RETURN))) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
openChangeProfileImage: function(event) {
|
||||||
|
if (this.clickOrSpaceOrEnterPressed(event)) {
|
||||||
|
fxAccounts.promiseAccountsChangeProfileURI(this._getEntryPoint(), "avatar")
|
||||||
|
.then(url => {
|
||||||
this.openContentInBrowser(url, {
|
this.openContentInBrowser(url, {
|
||||||
replaceQueryString: true
|
replaceQueryString: true
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
openManageFirefoxAccount: function(event) {
|
||||||
|
if (this.clickOrSpaceOrEnterPressed(event)) {
|
||||||
|
this.manageFirefoxAccount();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
manageFirefoxAccount: function() {
|
manageFirefoxAccount: function() {
|
||||||
|
@ -199,8 +199,8 @@
|
|||||||
<vbox flex="1">
|
<vbox flex="1">
|
||||||
<label id="signedOutAccountBoxTitle">&signedOut.accountBox.title;</label>
|
<label id="signedOutAccountBoxTitle">&signedOut.accountBox.title;</label>
|
||||||
<hbox class="fxaAccountBoxButtons" align="center">
|
<hbox class="fxaAccountBoxButtons" align="center">
|
||||||
<button id="noFxaSignUp">&signedOut.accountBox.create;</button>
|
<button id="noFxaSignUp" label="&signedOut.accountBox.create;"></button>
|
||||||
<button id="noFxaSignIn">&signedOut.accountBox.signin;</button>
|
<button id="noFxaSignIn" label="&signedOut.accountBox.signin;"></button>
|
||||||
</hbox>
|
</hbox>
|
||||||
</vbox>
|
</vbox>
|
||||||
</hbox>
|
</hbox>
|
||||||
@ -234,16 +234,21 @@
|
|||||||
<!-- logged in and verified and all is good -->
|
<!-- logged in and verified and all is good -->
|
||||||
<hbox id="fxaLoginVerified" class="fxaAccountBox">
|
<hbox id="fxaLoginVerified" class="fxaAccountBox">
|
||||||
<vbox>
|
<vbox>
|
||||||
<image id="fxaProfileImage"
|
<image id="fxaProfileImage" class="actionable"
|
||||||
onclick="gSyncPane.openChangeProfileImage();" hidden="true"
|
role="button"
|
||||||
tooltiptext="&profilePicture.tooltip;" class="actionable"/>
|
onclick="gSyncPane.openChangeProfileImage(event);" hidden="true"
|
||||||
|
onkeypress="gSyncPane.openChangeProfileImage(event);"
|
||||||
|
tooltiptext="&profilePicture.tooltip;"/>
|
||||||
</vbox>
|
</vbox>
|
||||||
<vbox flex="1">
|
<vbox flex="1">
|
||||||
<label id="fxaEmailAddress1"/>
|
<label id="fxaEmailAddress1"/>
|
||||||
<label id="fxaDisplayName" hidden="true"/>
|
<label id="fxaDisplayName" hidden="true"/>
|
||||||
<hbox class="fxaAccountBoxButtons" align="center">
|
<hbox class="fxaAccountBoxButtons" align="center">
|
||||||
<button id="fxaUnlinkButton">&disconnect.label;</button>
|
<button id="fxaUnlinkButton" label="&disconnect.label;"/>
|
||||||
<label id="verifiedManage" class="text-link">&verifiedManage.label;</label>
|
<label id="verifiedManage" class="text-link"
|
||||||
|
onclick="gSyncPane.openManageFirefoxAccount(event);"
|
||||||
|
onkeypress="gSyncPane.openManageFirefoxAccount(event);"><!--
|
||||||
|
-->&verifiedManage.label;</label>
|
||||||
</hbox>
|
</hbox>
|
||||||
</vbox>
|
</vbox>
|
||||||
</hbox>
|
</hbox>
|
||||||
|
@ -141,6 +141,10 @@ var SessionHistoryInternal = {
|
|||||||
entry.referrerPolicy = shEntry.referrerPolicy;
|
entry.referrerPolicy = shEntry.referrerPolicy;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shEntry.originalURI) {
|
||||||
|
entry.originalURI = shEntry.originalURI.spec;
|
||||||
|
}
|
||||||
|
|
||||||
if (shEntry.srcdocData)
|
if (shEntry.srcdocData)
|
||||||
entry.srcdocData = shEntry.srcdocData;
|
entry.srcdocData = shEntry.srcdocData;
|
||||||
|
|
||||||
@ -309,6 +313,9 @@ var SessionHistoryInternal = {
|
|||||||
shEntry.referrerURI = Utils.makeURI(entry.referrer);
|
shEntry.referrerURI = Utils.makeURI(entry.referrer);
|
||||||
shEntry.referrerPolicy = entry.referrerPolicy;
|
shEntry.referrerPolicy = entry.referrerPolicy;
|
||||||
}
|
}
|
||||||
|
if (entry.originalURI) {
|
||||||
|
shEntry.originalURI = Utils.makeURI(entry.originalURI);
|
||||||
|
}
|
||||||
if (entry.isSrcdocEntry)
|
if (entry.isSrcdocEntry)
|
||||||
shEntry.srcdocData = entry.srcdocData;
|
shEntry.srcdocData = entry.srcdocData;
|
||||||
if (entry.baseURI)
|
if (entry.baseURI)
|
||||||
|
@ -11,6 +11,8 @@ const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
|
|||||||
Cu.import("resource://gre/modules/AppConstants.jsm");
|
Cu.import("resource://gre/modules/AppConstants.jsm");
|
||||||
Cu.import("resource://gre/modules/Services.jsm");
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
|
XPCOMUtils.defineLazyModuleGetter(this, "WindowsRegistry",
|
||||||
|
"resource://gre/modules/WindowsRegistry.jsm");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal functionality to save and restore the docShell.allow* properties.
|
* Internal functionality to save and restore the docShell.allow* properties.
|
||||||
@ -54,11 +56,30 @@ let ShellServiceInternal = {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser");
|
if (!Services.prefs.getBoolPref("browser.shell.checkDefaultBrowser")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AppConstants.platform == "win") {
|
||||||
|
let optOutValue = WindowsRegistry.readRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
|
||||||
|
"Software\\Mozilla\\Firefox",
|
||||||
|
"DefaultBrowserOptOut");
|
||||||
|
WindowsRegistry.removeRegKey(Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
|
||||||
|
"Software\\Mozilla\\Firefox",
|
||||||
|
"DefaultBrowserOptOut");
|
||||||
|
if (optOutValue == "True") {
|
||||||
|
Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", false);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
},
|
},
|
||||||
|
|
||||||
set shouldCheckDefaultBrowser(shouldCheck) {
|
set shouldCheckDefaultBrowser(shouldCheck) {
|
||||||
Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", !!shouldCheck);
|
Services.prefs.setBoolPref("browser.shell.checkDefaultBrowser", !!shouldCheck);
|
||||||
},
|
},
|
||||||
|
|
||||||
isDefaultBrowser(startupCheck, forAllTypes) {
|
isDefaultBrowser(startupCheck, forAllTypes) {
|
||||||
// If this is the first browser window, maintain internal state that we've
|
// If this is the first browser window, maintain internal state that we've
|
||||||
// checked this session (so that subsequent window opens don't show the
|
// checked this session (so that subsequent window opens don't show the
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
<link rel="stylesheet" type="text/css" media="all" href="chrome://browser/skin/syncedtabs/sidebar.css"/>
|
<link rel="stylesheet" type="text/css" media="all" href="chrome://browser/skin/syncedtabs/sidebar.css"/>
|
||||||
<link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/"/>
|
<link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/"/>
|
||||||
<link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/textbox.css"/>
|
<link rel="stylesheet" type="text/css" media="all" href="chrome://global/skin/textbox.css"/>
|
||||||
|
<link rel="stylesheet" type="text/css" media="all" href="chrome://browser/content/browser.css"/>
|
||||||
<title>&syncedTabs.sidebar.label;</title>
|
<title>&syncedTabs.sidebar.label;</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -234,8 +234,21 @@ if (typeof Mozilla == 'undefined') {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Mozilla.UITour.showFirefoxAccounts = function() {
|
/**
|
||||||
_sendEvent('showFirefoxAccounts');
|
* Request the browser open the Firefox Accounts page.
|
||||||
|
*
|
||||||
|
* @param {Object} extraURLCampaignParams - An object containing additional
|
||||||
|
* paramaters for the URL opened by the browser for reasons of promotional
|
||||||
|
* campaign tracking. Each attribute of the object must have a name that
|
||||||
|
* begins with "utm_" and a value that is a string. Both the name and value
|
||||||
|
* must contain only alphanumeric characters, dashes or underscores (meaning
|
||||||
|
* that you are limited to values that don't need encoding, as any such
|
||||||
|
* characters in the name or value will be rejected.)
|
||||||
|
*/
|
||||||
|
Mozilla.UITour.showFirefoxAccounts = function(extraURLCampaignParams) {
|
||||||
|
_sendEvent('showFirefoxAccounts', {
|
||||||
|
extraURLCampaignParams: JSON.stringify(extraURLCampaignParams),
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Mozilla.UITour.resetFirefox = function() {
|
Mozilla.UITour.resetFirefox = function() {
|
||||||
|
@ -610,8 +610,15 @@ this.UITour = {
|
|||||||
case "showFirefoxAccounts": {
|
case "showFirefoxAccounts": {
|
||||||
// 'signup' is the only action that makes sense currently, so we don't
|
// 'signup' is the only action that makes sense currently, so we don't
|
||||||
// accept arbitrary actions just to be safe...
|
// accept arbitrary actions just to be safe...
|
||||||
|
let p = new URLSearchParams("action=signup&entrypoint=uitour");
|
||||||
|
// Call our helper to validate extraURLCampaignParams and populate URLSearchParams
|
||||||
|
if (!this._populateCampaignParams(p, data.extraURLCampaignParams)) {
|
||||||
|
log.warn("showFirefoxAccounts: invalid campaign args specified");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// We want to replace the current tab.
|
// We want to replace the current tab.
|
||||||
browser.loadURI("about:accounts?action=signup&entrypoint=uitour");
|
browser.loadURI("about:accounts?" + p.toString());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -805,6 +812,52 @@ this.UITour = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Given a string that is a JSONified represenation of an object with
|
||||||
|
// additional utm_* URL params that should be appended, validate and append
|
||||||
|
// them to the passed URLSearchParams object. Returns true if the params
|
||||||
|
// were validated and appended, and false if the request should be ignored.
|
||||||
|
_populateCampaignParams: function(urlSearchParams, extraURLCampaignParams) {
|
||||||
|
// We are extra paranoid about what params we allow to be appended.
|
||||||
|
if (typeof extraURLCampaignParams == "undefined") {
|
||||||
|
// no params, so it's all good.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (typeof extraURLCampaignParams != "string") {
|
||||||
|
log.warn("_populateCampaignParams: extraURLCampaignParams is not a string");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
let campaignParams;
|
||||||
|
try {
|
||||||
|
if (extraURLCampaignParams) {
|
||||||
|
campaignParams = JSON.parse(extraURLCampaignParams);
|
||||||
|
if (typeof campaignParams != "object") {
|
||||||
|
log.warn("_populateCampaignParams: extraURLCampaignParams is not a stringified object");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
log.warn("_populateCampaignParams: extraURLCampaignParams is not a JSON object");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (campaignParams) {
|
||||||
|
// The regex that both the name and value of each param must match.
|
||||||
|
let reSimpleString = /^[-_a-zA-Z0-9]*$/;
|
||||||
|
for (let name in campaignParams) {
|
||||||
|
let value = campaignParams[name];
|
||||||
|
if (typeof name != "string" || typeof value != "string" ||
|
||||||
|
!name.startsWith("utm_") ||
|
||||||
|
value.length == 0 ||
|
||||||
|
!reSimpleString.test(name) ||
|
||||||
|
!reSimpleString.test(value)) {
|
||||||
|
log.warn("_populateCampaignParams: invalid campaign param specified");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
urlSearchParams.append(name, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
setTelemetryBucket: function(aPageID) {
|
setTelemetryBucket: function(aPageID) {
|
||||||
let bucket = BUCKET_NAME + BrowserUITelemetry.BUCKET_SEPARATOR + aPageID;
|
let bucket = BUCKET_NAME + BrowserUITelemetry.BUCKET_SEPARATOR + aPageID;
|
||||||
BrowserUITelemetry.setBucket(bucket);
|
BrowserUITelemetry.setBucket(bucket);
|
||||||
@ -1700,7 +1753,7 @@ this.UITour = {
|
|||||||
|
|
||||||
// An event object is expected but we don't want to toggle the panel with a click if the panel
|
// An event object is expected but we don't want to toggle the panel with a click if the panel
|
||||||
// is already open.
|
// is already open.
|
||||||
aWindow.LoopUI.openCallPanel({ target: toolbarButton.node, }, "rooms").then(() => {
|
aWindow.LoopUI.openPanel({ target: toolbarButton.node, }, "rooms").then(() => {
|
||||||
if (aOpenCallback) {
|
if (aOpenCallback) {
|
||||||
aOpenCallback();
|
aOpenCallback();
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,7 @@ skip-if = e10s # Bug 1240747 - UITour.jsm not e10s friendly
|
|||||||
skip-if = e10s # Bug 1240747 - UITour.jsm not e10s friendly.
|
skip-if = e10s # Bug 1240747 - UITour.jsm not e10s friendly.
|
||||||
[browser_UITour_loop.js]
|
[browser_UITour_loop.js]
|
||||||
skip-if = true # Bug 1225832 - New Loop architecture is not compatible with test.
|
skip-if = true # Bug 1225832 - New Loop architecture is not compatible with test.
|
||||||
|
[browser_UITour_loop_panel.js]
|
||||||
[browser_UITour_modalDialog.js]
|
[browser_UITour_modalDialog.js]
|
||||||
skip-if = os != "mac" || e10s # modal dialog disabling only working on OS X. Bug 1240747 - UITour.jsm not e10s friendly
|
skip-if = os != "mac" || e10s # modal dialog disabling only working on OS X. Bug 1240747 - UITour.jsm not e10s friendly
|
||||||
[browser_UITour_observe.js]
|
[browser_UITour_observe.js]
|
||||||
|
@ -112,29 +112,6 @@ var tests = [
|
|||||||
|
|
||||||
checkLoopPanelIsHidden();
|
checkLoopPanelIsHidden();
|
||||||
}),
|
}),
|
||||||
taskify(function* test_menu_show_hide() {
|
|
||||||
// The targets to highlight only appear after getting started is launched.
|
|
||||||
// Set latestFTUVersion to lower number to show FTU panel.
|
|
||||||
Services.prefs.setIntPref("loop.gettingStarted.latestFTUVersion", 0);
|
|
||||||
is(loopButton.open, false, "Menu should initially be closed");
|
|
||||||
gContentAPI.showMenu("loop");
|
|
||||||
|
|
||||||
yield waitForConditionPromise(() => {
|
|
||||||
return loopButton.open;
|
|
||||||
}, "Menu should be visible after showMenu()");
|
|
||||||
|
|
||||||
ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel");
|
|
||||||
ok(loopPanel.hasAttribute("panelopen"), "The panel should have @panelopen");
|
|
||||||
is(loopPanel.state, "open", "The panel should be open");
|
|
||||||
ok(loopButton.hasAttribute("open"), "Loop button should know that the menu is open");
|
|
||||||
|
|
||||||
gContentAPI.hideMenu("loop");
|
|
||||||
yield waitForConditionPromise(() => {
|
|
||||||
return !loopButton.open;
|
|
||||||
}, "Menu should be hidden after hideMenu()");
|
|
||||||
|
|
||||||
checkLoopPanelIsHidden();
|
|
||||||
}),
|
|
||||||
// Test the menu was cleaned up in teardown.
|
// Test the menu was cleaned up in teardown.
|
||||||
taskify(function* setup_menu_cleanup() {
|
taskify(function* setup_menu_cleanup() {
|
||||||
gContentAPI.showMenu("loop");
|
gContentAPI.showMenu("loop");
|
||||||
|
68
browser/components/uitour/test/browser_UITour_loop_panel.js
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
"use strict";
|
||||||
|
|
||||||
|
var gTestTab;
|
||||||
|
var gContentAPI;
|
||||||
|
var gContentWindow;
|
||||||
|
var gMessageHandlers;
|
||||||
|
var loopButton;
|
||||||
|
var fakeRoom;
|
||||||
|
var loopPanel = document.getElementById("loop-notification-panel");
|
||||||
|
|
||||||
|
const { LoopAPI } = Cu.import("chrome://loop/content/modules/MozLoopAPI.jsm", {});
|
||||||
|
const { LoopRooms } = Cu.import("chrome://loop/content/modules/LoopRooms.jsm", {});
|
||||||
|
|
||||||
|
if (!Services.prefs.getBoolPref("loop.enabled")) {
|
||||||
|
ok(true, "Loop is disabled so skip the UITour Loop tests");
|
||||||
|
} else {
|
||||||
|
function checkLoopPanelIsHidden() {
|
||||||
|
ok(!loopPanel.hasAttribute("noautohide"), "@noautohide on the loop panel should have been cleaned up");
|
||||||
|
ok(!loopPanel.hasAttribute("panelopen"), "The panel shouldn't have @panelopen");
|
||||||
|
isnot(loopPanel.state, "open", "The panel shouldn't be open");
|
||||||
|
is(loopButton.hasAttribute("open"), false, "Loop button should know that the panel is closed");
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(setup_UITourTest);
|
||||||
|
|
||||||
|
add_task(function() {
|
||||||
|
loopButton = window.LoopUI.toolbarButton.node;
|
||||||
|
|
||||||
|
registerCleanupFunction(() => {
|
||||||
|
Services.prefs.clearUserPref("loop.gettingStarted.latestFTUVersion");
|
||||||
|
Services.io.offline = false;
|
||||||
|
|
||||||
|
// Copied from browser/components/loop/test/mochitest/head.js
|
||||||
|
// Remove the iframe after each test. This also avoids mochitest complaining
|
||||||
|
// about leaks on shutdown as we intentionally hold the iframe open for the
|
||||||
|
// life of the application.
|
||||||
|
let frameId = loopButton.getAttribute("notificationFrameId");
|
||||||
|
let frame = document.getElementById(frameId);
|
||||||
|
if (frame) {
|
||||||
|
frame.remove();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_UITour_task(function* test_menu_show_hide() {
|
||||||
|
// The targets to highlight only appear after getting started is launched.
|
||||||
|
// Set latestFTUVersion to lower number to show FTU panel.
|
||||||
|
Services.prefs.setIntPref("loop.gettingStarted.latestFTUVersion", 0);
|
||||||
|
is(loopButton.open, false, "Menu should initially be closed");
|
||||||
|
gContentAPI.showMenu("loop");
|
||||||
|
|
||||||
|
yield waitForConditionPromise(() => {
|
||||||
|
return loopButton.open;
|
||||||
|
}, "Menu should be visible after showMenu()");
|
||||||
|
|
||||||
|
ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel");
|
||||||
|
ok(loopPanel.hasAttribute("panelopen"), "The panel should have @panelopen");
|
||||||
|
is(loopPanel.state, "open", "The panel should be open");
|
||||||
|
ok(loopButton.hasAttribute("open"), "Loop button should know that the menu is open");
|
||||||
|
|
||||||
|
gContentAPI.hideMenu("loop");
|
||||||
|
yield waitForConditionPromise(() => {
|
||||||
|
return !loopButton.open;
|
||||||
|
}, "Menu should be hidden after hideMenu()");
|
||||||
|
|
||||||
|
checkLoopPanelIsHidden();
|
||||||
|
});
|
||||||
|
}
|
@ -22,8 +22,50 @@ add_UITour_task(function* test_checkSyncSetup_enabled() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// The showFirefoxAccounts API is sync related, so we test that here too...
|
// The showFirefoxAccounts API is sync related, so we test that here too...
|
||||||
add_UITour_task(function* test_firefoxAccounts() {
|
add_UITour_task(function* test_firefoxAccountsNoParams() {
|
||||||
yield gContentAPI.showFirefoxAccounts();
|
yield gContentAPI.showFirefoxAccounts();
|
||||||
yield BrowserTestUtils.browserLoaded(gTestTab.linkedBrowser, false,
|
yield BrowserTestUtils.browserLoaded(gTestTab.linkedBrowser, false,
|
||||||
"about:accounts?action=signup&entrypoint=uitour");
|
"about:accounts?action=signup&entrypoint=uitour");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add_UITour_task(function* test_firefoxAccountsValidParams() {
|
||||||
|
yield gContentAPI.showFirefoxAccounts({ utm_foo: "foo", utm_bar: "bar" });
|
||||||
|
yield BrowserTestUtils.browserLoaded(gTestTab.linkedBrowser, false,
|
||||||
|
"about:accounts?action=signup&entrypoint=uitour&utm_foo=foo&utm_bar=bar");
|
||||||
|
});
|
||||||
|
|
||||||
|
// A helper to check the request was ignored due to invalid params.
|
||||||
|
function* checkAboutAccountsNotLoaded() {
|
||||||
|
try {
|
||||||
|
yield waitForConditionPromise(() => {
|
||||||
|
return gBrowser.selectedBrowser.currentURI.spec.startsWith("about:accounts");
|
||||||
|
}, "Check if about:accounts opened");
|
||||||
|
ok(false, "No about:accounts tab should have opened");
|
||||||
|
} catch (ex) {
|
||||||
|
ok(true, "No about:accounts tab opened");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
add_UITour_task(function* test_firefoxAccountsNonObject() {
|
||||||
|
// non-string should be rejected.
|
||||||
|
yield gContentAPI.showFirefoxAccounts(99);
|
||||||
|
yield checkAboutAccountsNotLoaded();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_UITour_task(function* test_firefoxAccountsNonUtmPrefix() {
|
||||||
|
// Any non "utm_" name should should be rejected.
|
||||||
|
yield gContentAPI.showFirefoxAccounts({ utm_foo: "foo", bar: "bar" });
|
||||||
|
yield checkAboutAccountsNotLoaded();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_UITour_task(function* test_firefoxAccountsNonAlphaName() {
|
||||||
|
// Any "utm_" name which includes non-alpha chars should be rejected.
|
||||||
|
yield gContentAPI.showFirefoxAccounts({ utm_foo: "foo", "utm_bar=": "bar" });
|
||||||
|
yield checkAboutAccountsNotLoaded();
|
||||||
|
});
|
||||||
|
|
||||||
|
add_UITour_task(function* test_firefoxAccountsNonAlphaValue() {
|
||||||
|
// Any non-alpha value should be rejected.
|
||||||
|
yield gContentAPI.showFirefoxAccounts({ utm_foo: "foo&" });
|
||||||
|
yield checkAboutAccountsNotLoaded();
|
||||||
|
});
|
||||||
|
@ -147,13 +147,14 @@ function hideInfoPromise(...args) {
|
|||||||
*/
|
*/
|
||||||
function showInfoPromise(target, title, text, icon, buttonsFunctionName, optionsFunctionName) {
|
function showInfoPromise(target, title, text, icon, buttonsFunctionName, optionsFunctionName) {
|
||||||
let popup = document.getElementById("UITourTooltip");
|
let popup = document.getElementById("UITourTooltip");
|
||||||
|
let shownPromise = promisePanelElementShown(window, popup);
|
||||||
return ContentTask.spawn(gTestTab.linkedBrowser, [...arguments], args => {
|
return ContentTask.spawn(gTestTab.linkedBrowser, [...arguments], args => {
|
||||||
let contentWin = Components.utils.waiveXrays(content);
|
let contentWin = Components.utils.waiveXrays(content);
|
||||||
let [target, title, text, icon, buttonsFunctionName, optionsFunctionName] = args;
|
let [target, title, text, icon, buttonsFunctionName, optionsFunctionName] = args;
|
||||||
let buttons = buttonsFunctionName ? contentWin[buttonsFunctionName]() : null;
|
let buttons = buttonsFunctionName ? contentWin[buttonsFunctionName]() : null;
|
||||||
let options = optionsFunctionName ? contentWin[optionsFunctionName]() : null;
|
let options = optionsFunctionName ? contentWin[optionsFunctionName]() : null;
|
||||||
contentWin.Mozilla.UITour.showInfo(target, title, text, icon, buttons, options);
|
contentWin.Mozilla.UITour.showInfo(target, title, text, icon, buttons, options);
|
||||||
}).then(() => promisePanelElementShown(window, popup));
|
}).then(() => shownPromise);
|
||||||
}
|
}
|
||||||
|
|
||||||
function showHighlightPromise(...args) {
|
function showHighlightPromise(...args) {
|
||||||
@ -193,7 +194,7 @@ function promisePanelElementEvent(win, aPanel, aEvent) {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
let timeoutId = win.setTimeout(() => {
|
let timeoutId = win.setTimeout(() => {
|
||||||
aPanel.removeEventListener(aEvent, onPanelEvent);
|
aPanel.removeEventListener(aEvent, onPanelEvent);
|
||||||
reject("Event did not happen within 5 seconds.");
|
reject(aEvent + " event did not happen within 5 seconds.");
|
||||||
}, 5000);
|
}, 5000);
|
||||||
|
|
||||||
function onPanelEvent(e) {
|
function onPanelEvent(e) {
|
||||||
|
@ -28,6 +28,7 @@ LOOPDIR=browser/extensions/loop
|
|||||||
|
|
||||||
TESTS="
|
TESTS="
|
||||||
${LOOPDIR}/chrome/test/mochitest
|
${LOOPDIR}/chrome/test/mochitest
|
||||||
|
browser/components/uitour/test/browser_UITour_loop_panel.js
|
||||||
browser/base/content/test/general/browser_devices_get_user_media_about_urls.js
|
browser/base/content/test/general/browser_devices_get_user_media_about_urls.js
|
||||||
browser/base/content/test/general/browser_parsable_css.js
|
browser/base/content/test/general/browser_parsable_css.js
|
||||||
"
|
"
|
||||||
|
4
browser/extensions/pocket/bootstrap.js
vendored
@ -357,7 +357,7 @@ var PocketOverlay = {
|
|||||||
PocketContextMenu.init();
|
PocketContextMenu.init();
|
||||||
PocketReader.startup();
|
PocketReader.startup();
|
||||||
|
|
||||||
if (reason == ADDON_ENABLE) {
|
if (reason != APP_STARTUP) {
|
||||||
for (let win of allBrowserWindows()) {
|
for (let win of allBrowserWindows()) {
|
||||||
this.setWindowScripts(win);
|
this.setWindowScripts(win);
|
||||||
this.addStyles(win);
|
this.addStyles(win);
|
||||||
@ -529,7 +529,7 @@ function startup(data, reason) {
|
|||||||
function shutdown(data, reason) {
|
function shutdown(data, reason) {
|
||||||
// For speed sake, we should only do a shutdown if we're being disabled.
|
// For speed sake, we should only do a shutdown if we're being disabled.
|
||||||
// On an app shutdown, just let it fade away...
|
// On an app shutdown, just let it fade away...
|
||||||
if (reason == ADDON_DISABLE) {
|
if (reason != APP_SHUTDOWN) {
|
||||||
Services.prefs.removeObserver("extensions.pocket.enabled", prefObserver);
|
Services.prefs.removeObserver("extensions.pocket.enabled", prefObserver);
|
||||||
PocketOverlay.shutdown(reason);
|
PocketOverlay.shutdown(reason);
|
||||||
}
|
}
|
||||||
|
@ -605,6 +605,12 @@ Section "-InstallEndCleanup"
|
|||||||
GetFunctionAddress $0 SetAsDefaultAppUserHKCU
|
GetFunctionAddress $0 SetAsDefaultAppUserHKCU
|
||||||
UAC::ExecCodeSegment $0
|
UAC::ExecCodeSegment $0
|
||||||
${EndIf}
|
${EndIf}
|
||||||
|
${Else}
|
||||||
|
${LogHeader} "Writing default-browser opt-out"
|
||||||
|
WriteRegStr HKCU "Software\Mozilla\Firefox" "DefaultBrowserOptOut" "True"
|
||||||
|
${If} ${Errors}
|
||||||
|
${LogHeader} "Error writing default-browser opt-out"
|
||||||
|
${EndIf}
|
||||||
${EndIf}
|
${EndIf}
|
||||||
${EndUnless}
|
${EndUnless}
|
||||||
|
|
||||||
|
@ -744,7 +744,6 @@ you can use these alternative items. Otherwise, their values should be empty. -
|
|||||||
<!ENTITY spellAddDictionaries.accesskey "A">
|
<!ENTITY spellAddDictionaries.accesskey "A">
|
||||||
|
|
||||||
<!ENTITY editBookmark.done.label "Done">
|
<!ENTITY editBookmark.done.label "Done">
|
||||||
<!ENTITY editBookmark.cancel.label "Cancel">
|
|
||||||
<!ENTITY editBookmark.removeBookmark.accessKey "R">
|
<!ENTITY editBookmark.removeBookmark.accessKey "R">
|
||||||
|
|
||||||
<!ENTITY identity.connectionSecure "Secure Connection">
|
<!ENTITY identity.connectionSecure "Secure Connection">
|
||||||
|
@ -49,3 +49,4 @@ feedSubscriptionVideoPodcast2=You can subscribe to this video podcast to receive
|
|||||||
# "Add %appName (%appDomain) as an application for %protocolType links?"
|
# "Add %appName (%appDomain) as an application for %protocolType links?"
|
||||||
addProtocolHandler=Add %S (%S) as an application for %S links?
|
addProtocolHandler=Add %S (%S) as an application for %S links?
|
||||||
addProtocolHandlerAddButton=Add Application
|
addProtocolHandlerAddButton=Add Application
|
||||||
|
addProtocolHandlerAddButtonAccesskey=A
|
@ -134,6 +134,7 @@ XPCOMUtils.defineLazyGetter(this, "ALL_BUILTIN_ITEMS", function() {
|
|||||||
"BMB_unsortedBookmarksPopup",
|
"BMB_unsortedBookmarksPopup",
|
||||||
"BMB_bookmarksToolbarPopup",
|
"BMB_bookmarksToolbarPopup",
|
||||||
"search-go-button",
|
"search-go-button",
|
||||||
|
"soundplaying-icon",
|
||||||
]
|
]
|
||||||
return DEFAULT_ITEMS.concat(PALETTE_ITEMS)
|
return DEFAULT_ITEMS.concat(PALETTE_ITEMS)
|
||||||
.concat(SPECIAL_CASES);
|
.concat(SPECIAL_CASES);
|
||||||
@ -610,6 +611,10 @@ this.BrowserUITelemetry = {
|
|||||||
this._countEvent(["forget-button", timeId]);
|
this._countEvent(["forget-button", timeId]);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
countTabMutingEvent: function(action, reason) {
|
||||||
|
this._countEvent(["tab-audio-control", action, reason || "no reason given"]);
|
||||||
|
},
|
||||||
|
|
||||||
_logAwesomeBarSearchResult: function (url) {
|
_logAwesomeBarSearchResult: function (url) {
|
||||||
let spec = Services.search.parseSubmissionURL(url);
|
let spec = Services.search.parseSubmissionURL(url);
|
||||||
if (spec.engine) {
|
if (spec.engine) {
|
||||||
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 25 KiB |
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 56 KiB |
@ -25,6 +25,10 @@
|
|||||||
margin-top: 2px !important;
|
margin-top: 2px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#fxaProfileImage {
|
||||||
|
-moz-user-focus: normal;
|
||||||
|
}
|
||||||
|
|
||||||
menulist.actionsMenu > .menulist-dropmarker {
|
menulist.actionsMenu > .menulist-dropmarker {
|
||||||
margin-top: 11px;
|
margin-top: 11px;
|
||||||
margin-bottom: 11px;
|
margin-bottom: 11px;
|
||||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Before Width: | Height: | Size: 56 KiB After Width: | Height: | Size: 57 KiB |
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 31 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 73 KiB |
@ -19,6 +19,10 @@ prefpane .groupbox-title {
|
|||||||
-moz-padding-start: 3px;
|
-moz-padding-start: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#fxaProfileImage {
|
||||||
|
-moz-user-focus: normal;
|
||||||
|
}
|
||||||
|
|
||||||
textbox + button {
|
textbox + button {
|
||||||
-moz-margin-start: -4px;
|
-moz-margin-start: -4px;
|
||||||
}
|
}
|
||||||
|
@ -63,6 +63,10 @@
|
|||||||
-moz-image-region: rect(0px, 1024px, 32px, 992px);
|
-moz-image-region: rect(0px, 1024px, 32px, 992px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#sync-button[cui-areatype="menu-panel"][panel-multiview-anchor=true] {
|
||||||
|
-moz-image-region: rect(32px, 1024px, 64px, 992px);
|
||||||
|
}
|
||||||
|
|
||||||
#feed-button[cui-areatype="menu-panel"],
|
#feed-button[cui-areatype="menu-panel"],
|
||||||
toolbarpaletteitem[place="palette"] > #feed-button {
|
toolbarpaletteitem[place="palette"] > #feed-button {
|
||||||
-moz-image-region: rect(0px, 416px, 32px, 384px);
|
-moz-image-region: rect(0px, 416px, 32px, 384px);
|
||||||
@ -263,6 +267,10 @@
|
|||||||
-moz-image-region: rect(0px, 2048px, 64px, 1984px);
|
-moz-image-region: rect(0px, 2048px, 64px, 1984px);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#sync-button[cui-areatype="menu-panel"][panel-multiview-anchor=true] {
|
||||||
|
-moz-image-region: rect(64px, 2048px, 128px, 1984px);
|
||||||
|
}
|
||||||
|
|
||||||
#feed-button[cui-areatype="menu-panel"],
|
#feed-button[cui-areatype="menu-panel"],
|
||||||
toolbarpaletteitem[place="palette"] > #feed-button {
|
toolbarpaletteitem[place="palette"] > #feed-button {
|
||||||
-moz-image-region: rect(0px, 832px, 64px, 768px);
|
-moz-image-region: rect(0px, 832px, 64px, 768px);
|
||||||
|
Before Width: | Height: | Size: 35 KiB After Width: | Height: | Size: 35 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 94 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 43 KiB |
@ -51,6 +51,10 @@ filefield + button {
|
|||||||
padding-bottom: 0; /* override padding from normal preferences.css */
|
padding-bottom: 0; /* override padding from normal preferences.css */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#fxaProfileImage {
|
||||||
|
-moz-user-focus: normal;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Dialog
|
* Dialog
|
||||||
*/
|
*/
|
||||||
|
@ -27,6 +27,8 @@ If you would like to use a different directory, hit CTRL+c and set the
|
|||||||
MOZBUILD_STATE_PATH environment variable to the directory you would like to
|
MOZBUILD_STATE_PATH environment variable to the directory you would like to
|
||||||
use and re-run mach. For this change to take effect forever, you'll likely
|
use and re-run mach. For this change to take effect forever, you'll likely
|
||||||
want to export this environment variable from your shell's init scripts.
|
want to export this environment variable from your shell's init scripts.
|
||||||
|
|
||||||
|
Press ENTER/RETURN to continue or CTRL+c to abort.
|
||||||
'''.lstrip()
|
'''.lstrip()
|
||||||
|
|
||||||
NO_MERCURIAL_SETUP = '''
|
NO_MERCURIAL_SETUP = '''
|
||||||
@ -254,8 +256,26 @@ def bootstrap(topsrcdir, mozilla_dir=None):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
# Add common metadata to help submit sorted data later on.
|
# Add common metadata to help submit sorted data later on.
|
||||||
# For now, we'll just record the mach command that was invoked.
|
|
||||||
data['argv'] = sys.argv
|
data['argv'] = sys.argv
|
||||||
|
data.setdefault('system', {}).update(dict(
|
||||||
|
architecture=list(platform.architecture()),
|
||||||
|
machine=platform.machine(),
|
||||||
|
python_version=platform.python_version(),
|
||||||
|
release=platform.release(),
|
||||||
|
system=platform.system(),
|
||||||
|
version=platform.version(),
|
||||||
|
))
|
||||||
|
|
||||||
|
if platform.system() == 'Linux':
|
||||||
|
dist = list(platform.linux_distribution())
|
||||||
|
data['system']['linux_distribution'] = dist
|
||||||
|
elif platform.system() == 'Windows':
|
||||||
|
win32_ver=list((platform.win32_ver())),
|
||||||
|
data['system']['win32_ver'] = win32_ver
|
||||||
|
elif platform.system() == 'Darwin':
|
||||||
|
# mac version is a special Cupertino snowflake
|
||||||
|
r, v, m = platform.mac_ver()
|
||||||
|
data['system']['mac_ver'] = [r, list(v), m]
|
||||||
|
|
||||||
with open(os.path.join(outgoing_dir, str(uuid.uuid4()) + '.json'),
|
with open(os.path.join(outgoing_dir, str(uuid.uuid4()) + '.json'),
|
||||||
'w') as f:
|
'w') as f:
|
||||||
@ -383,25 +403,18 @@ def bootstrap(topsrcdir, mozilla_dir=None):
|
|||||||
if is_environ:
|
if is_environ:
|
||||||
if not os.path.exists(state_dir):
|
if not os.path.exists(state_dir):
|
||||||
print('Creating global state directory from environment variable: %s'
|
print('Creating global state directory from environment variable: %s'
|
||||||
% state_dir)
|
% state_dir)
|
||||||
os.makedirs(state_dir, mode=0o770)
|
os.makedirs(state_dir, mode=0o770)
|
||||||
print('Please re-run mach.')
|
|
||||||
sys.exit(1)
|
|
||||||
else:
|
else:
|
||||||
if not os.path.exists(state_dir):
|
if not os.path.exists(state_dir):
|
||||||
print(STATE_DIR_FIRST_RUN.format(userdir=state_dir))
|
print(STATE_DIR_FIRST_RUN.format(userdir=state_dir))
|
||||||
try:
|
try:
|
||||||
for i in range(20, -1, -1):
|
sys.stdin.readline()
|
||||||
time.sleep(1)
|
|
||||||
sys.stdout.write('%d ' % i)
|
|
||||||
sys.stdout.flush()
|
|
||||||
except KeyboardInterrupt:
|
except KeyboardInterrupt:
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
print('\nCreating default state directory: %s' % state_dir)
|
print('\nCreating default state directory: %s' % state_dir)
|
||||||
os.mkdir(state_dir)
|
os.makedirs(state_dir, mode=0o770)
|
||||||
print('Please re-run mach.')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
return state_dir
|
return state_dir
|
||||||
|
|
||||||
|
24
build/telemetry-schema.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"argv": {"type": "array"},
|
||||||
|
"system": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"architecture": {"type": "array"},
|
||||||
|
"linux_distribution": {"type": "array"},
|
||||||
|
"mac_ver": {"type": "array"},
|
||||||
|
"machine": {"type": "string"},
|
||||||
|
"python_version": {"type": "string"},
|
||||||
|
"release": {"type": "string"},
|
||||||
|
"system": {"type": "string"},
|
||||||
|
"version": {"type": "string"},
|
||||||
|
"win_ver": {"type": "array"}
|
||||||
|
},
|
||||||
|
"required": ["architecture", "machine", "python_version",
|
||||||
|
"release", "system", "version"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["argv", "system"]
|
||||||
|
}
|
35
configure.in
@ -53,7 +53,7 @@ dnl ========================================================
|
|||||||
MOZJPEG=62
|
MOZJPEG=62
|
||||||
MOZPNG=10619
|
MOZPNG=10619
|
||||||
NSPR_VERSION=4
|
NSPR_VERSION=4
|
||||||
NSPR_MINVER=4.11
|
NSPR_MINVER=4.12
|
||||||
NSS_VERSION=3
|
NSS_VERSION=3
|
||||||
|
|
||||||
dnl Set the minimum version of toolkit libs used by mozilla
|
dnl Set the minimum version of toolkit libs used by mozilla
|
||||||
@ -3851,18 +3851,6 @@ MOZ_ARG_WITH_STRING(gcm-senderid-keyfile,
|
|||||||
MOZ_ANDROID_GCM_SENDERID=`cat $withval`)
|
MOZ_ANDROID_GCM_SENDERID=`cat $withval`)
|
||||||
AC_SUBST(MOZ_ANDROID_GCM_SENDERID)
|
AC_SUBST(MOZ_ANDROID_GCM_SENDERID)
|
||||||
|
|
||||||
# Whether to include optional-but-large font files in the final APK.
|
|
||||||
# We want this in mobile/android/confvars.sh, so it goes early.
|
|
||||||
MOZ_ARG_DISABLE_BOOL(android-include-fonts,
|
|
||||||
[ --disable-android-include-fonts
|
|
||||||
Disable the inclusion of fonts into the final APK],
|
|
||||||
MOZ_ANDROID_EXCLUDE_FONTS=1)
|
|
||||||
|
|
||||||
if test -n "$MOZ_ANDROID_EXCLUDE_FONTS"; then
|
|
||||||
AC_DEFINE(MOZ_ANDROID_EXCLUDE_FONTS)
|
|
||||||
fi
|
|
||||||
AC_SUBST(MOZ_ANDROID_EXCLUDE_FONTS)
|
|
||||||
|
|
||||||
# Whether this APK is destined for resource constrained devices.
|
# Whether this APK is destined for resource constrained devices.
|
||||||
# We want this in mobile/android/confvars.sh, so it goes early.
|
# We want this in mobile/android/confvars.sh, so it goes early.
|
||||||
MOZ_ARG_ENABLE_BOOL(android-resource-constrained,
|
MOZ_ARG_ENABLE_BOOL(android-resource-constrained,
|
||||||
@ -4775,6 +4763,13 @@ if test -n "$MOZ_ANDROID_MLS_STUMBLER"; then
|
|||||||
AC_DEFINE(MOZ_ANDROID_MLS_STUMBLER)
|
AC_DEFINE(MOZ_ANDROID_MLS_STUMBLER)
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
dnl =========================================================
|
||||||
|
dnl = Whether to exclude font files in the build
|
||||||
|
dnl =========================================================
|
||||||
|
if test -n "$MOZ_ANDROID_EXCLUDE_FONTS"; then
|
||||||
|
AC_DEFINE(MOZ_ANDROID_EXCLUDE_FONTS)
|
||||||
|
fi
|
||||||
|
|
||||||
dnl =========================================================
|
dnl =========================================================
|
||||||
dnl = Whether to exclude hyphenations files in the build
|
dnl = Whether to exclude hyphenations files in the build
|
||||||
dnl =========================================================
|
dnl =========================================================
|
||||||
@ -8457,6 +8452,7 @@ AC_SUBST(MOZ_ANDROID_APPLICATION_CLASS)
|
|||||||
AC_SUBST(MOZ_ANDROID_BROWSER_INTENT_CLASS)
|
AC_SUBST(MOZ_ANDROID_BROWSER_INTENT_CLASS)
|
||||||
AC_SUBST(MOZ_ANDROID_SEARCH_INTENT_CLASS)
|
AC_SUBST(MOZ_ANDROID_SEARCH_INTENT_CLASS)
|
||||||
AC_SUBST(MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE)
|
AC_SUBST(MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE)
|
||||||
|
AC_SUBST(MOZ_ANDROID_EXCLUDE_FONTS)
|
||||||
AC_SUBST(MOZ_EXCLUDE_HYPHENATION_DICTIONARIES)
|
AC_SUBST(MOZ_EXCLUDE_HYPHENATION_DICTIONARIES)
|
||||||
AC_SUBST(MOZ_INSTALL_TRACKING)
|
AC_SUBST(MOZ_INSTALL_TRACKING)
|
||||||
AC_SUBST(MOZ_SWITCHBOARD)
|
AC_SUBST(MOZ_SWITCHBOARD)
|
||||||
@ -8562,10 +8558,12 @@ AC_SUBST(MOZ_CHILD_PROCESS_BUNDLE)
|
|||||||
# "Profile" field, which controls profile location.
|
# "Profile" field, which controls profile location.
|
||||||
# - MOZ_APP_ID: When set, used for application.ini's "ID" field, and
|
# - MOZ_APP_ID: When set, used for application.ini's "ID" field, and
|
||||||
# crash reporter server url.
|
# crash reporter server url.
|
||||||
# - MOZ_APP_ANDROID_VERSION_CODE: On android, "android:versionCode" for
|
# - MOZ_APP_ANDROID_VERSION_CODE: On Android, "android:versionCode" for
|
||||||
# the main application is set to the value of this variable. If not
|
# the main application is set to the value of this variable. If not
|
||||||
# set, it falls back to a Mozilla-specific value derived from the
|
# set, it falls back to a Mozilla-specific value derived from the
|
||||||
# build ID.
|
# build ID.
|
||||||
|
# - MOZ_ANDROID_SHARED_ID: On Android, "android:sharedUserId" for all Android
|
||||||
|
# - packages produced.
|
||||||
# - MOZ_PROFILE_MIGRATOR: When set, enables profile migrator.
|
# - MOZ_PROFILE_MIGRATOR: When set, enables profile migrator.
|
||||||
|
|
||||||
if test -z "$MOZ_APP_NAME"; then
|
if test -z "$MOZ_APP_NAME"; then
|
||||||
@ -8584,6 +8582,14 @@ if test -z "$ANDROID_PACKAGE_NAME" ; then
|
|||||||
ANDROID_PACKAGE_NAME="org.mozilla.$MOZ_APP_NAME"
|
ANDROID_PACKAGE_NAME="org.mozilla.$MOZ_APP_NAME"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Mozilla released Firefox for Android {Release,Beta} and {Aurora,Nightly} to
|
||||||
|
# the public with specific common shared IDs and we need to keep them
|
||||||
|
# consistent forever. The specific common values are set by per-channel
|
||||||
|
# branding; all other channels use a generic sharedID, set below.
|
||||||
|
if test -z "$MOZ_ANDROID_SHARED_ID" ; then
|
||||||
|
MOZ_ANDROID_SHARED_ID="${ANDROID_PACKAGE_NAME}.sharedID"
|
||||||
|
fi
|
||||||
|
|
||||||
# For extensions and langpacks, we require a max version that is compatible
|
# For extensions and langpacks, we require a max version that is compatible
|
||||||
# across security releases. MOZ_APP_MAXVERSION is our method for doing that.
|
# across security releases. MOZ_APP_MAXVERSION is our method for doing that.
|
||||||
# 24.0a1 and 24.0a2 aren't affected
|
# 24.0a1 and 24.0a2 aren't affected
|
||||||
@ -8614,6 +8620,7 @@ AC_SUBST(MOZ_APP_VENDOR)
|
|||||||
AC_SUBST(MOZ_APP_PROFILE)
|
AC_SUBST(MOZ_APP_PROFILE)
|
||||||
AC_SUBST(MOZ_APP_ID)
|
AC_SUBST(MOZ_APP_ID)
|
||||||
AC_SUBST(MOZ_APP_ANDROID_VERSION_CODE)
|
AC_SUBST(MOZ_APP_ANDROID_VERSION_CODE)
|
||||||
|
AC_SUBST(MOZ_ANDROID_SHARED_ID)
|
||||||
AC_SUBST(MAR_CHANNEL_ID)
|
AC_SUBST(MAR_CHANNEL_ID)
|
||||||
AC_SUBST(ACCEPTED_MAR_CHANNEL_IDS)
|
AC_SUBST(ACCEPTED_MAR_CHANNEL_IDS)
|
||||||
AC_SUBST(MOZ_PROFILE_MIGRATOR)
|
AC_SUBST(MOZ_PROFILE_MIGRATOR)
|
||||||
|
@ -265,8 +265,8 @@
|
|||||||
// Disallow unreachable statements after a return, throw, continue, or break
|
// Disallow unreachable statements after a return, throw, continue, or break
|
||||||
// statement.
|
// statement.
|
||||||
"no-unreachable": 2,
|
"no-unreachable": 2,
|
||||||
// Disallow declaration of variables that are not used in the code
|
// Disallow global and local variables that aren't used, but allow unused function arguments.
|
||||||
"no-unused-vars": 2,
|
"no-unused-vars": [2, {"vars": "all", "args": "none"}],
|
||||||
// Allow using variables before they are defined.
|
// Allow using variables before they are defined.
|
||||||
"no-use-before-define": 0,
|
"no-use-before-define": 0,
|
||||||
// We use var-only-at-top-level instead of no-var as we allow top level
|
// We use var-only-at-top-level instead of no-var as we allow top level
|
||||||
|
@ -16,11 +16,6 @@ button {
|
|||||||
padding-right: 20px;
|
padding-right: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#body {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Category tabs */
|
/* Category tabs */
|
||||||
|
|
||||||
.category {
|
.category {
|
||||||
@ -33,6 +28,13 @@ button {
|
|||||||
cursor: default;
|
cursor: default;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100%;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
}
|
||||||
|
|
||||||
.main-content {
|
.main-content {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
@ -41,17 +43,6 @@ button {
|
|||||||
max-width: 800px;
|
max-width: 800px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab:not(.active) {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Prefs */
|
|
||||||
|
|
||||||
label {
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Targets */
|
/* Targets */
|
||||||
|
|
||||||
.targets {
|
.targets {
|
||||||
@ -82,11 +73,16 @@ label {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-controls {
|
.addons-controls {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
}
|
}
|
||||||
|
|
||||||
.addon-options {
|
.addons-options {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.addons-debugging-label {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 5px 5px 0;
|
||||||
|
}
|
@ -1,179 +0,0 @@
|
|||||||
/* 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/. */
|
|
||||||
|
|
||||||
/* eslint-env browser */
|
|
||||||
/* global AddonsComponent, DebuggerClient, DebuggerServer, React,
|
|
||||||
WorkersComponent */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
const { classes: Cc, interfaces: Ci } = Components;
|
|
||||||
const { loader } = Components.utils.import(
|
|
||||||
"resource://devtools/shared/Loader.jsm", {});
|
|
||||||
|
|
||||||
loader.lazyRequireGetter(this, "AddonsComponent",
|
|
||||||
"devtools/client/aboutdebugging/components/addons", true);
|
|
||||||
loader.lazyRequireGetter(this, "DebuggerClient",
|
|
||||||
"devtools/shared/client/main", true);
|
|
||||||
loader.lazyRequireGetter(this, "DebuggerServer",
|
|
||||||
"devtools/server/main", true);
|
|
||||||
loader.lazyRequireGetter(this, "Telemetry",
|
|
||||||
"devtools/client/shared/telemetry");
|
|
||||||
loader.lazyRequireGetter(this, "WorkersComponent",
|
|
||||||
"devtools/client/aboutdebugging/components/workers", true);
|
|
||||||
loader.lazyRequireGetter(this, "Services");
|
|
||||||
|
|
||||||
loader.lazyImporter(this, "AddonManager",
|
|
||||||
"resource://gre/modules/AddonManager.jsm");
|
|
||||||
|
|
||||||
const Strings = Services.strings.createBundle(
|
|
||||||
"chrome://devtools/locale/aboutdebugging.properties");
|
|
||||||
|
|
||||||
var AboutDebugging = {
|
|
||||||
_prefListeners: [],
|
|
||||||
|
|
||||||
// Pointer to the current React component.
|
|
||||||
_component: null,
|
|
||||||
|
|
||||||
_categories: null,
|
|
||||||
get categories() {
|
|
||||||
// If needed, initialize the list of available categories.
|
|
||||||
if (!this._categories) {
|
|
||||||
let elements = document.querySelectorAll(".category");
|
|
||||||
this._categories = Array.map(elements, element => {
|
|
||||||
let value = element.getAttribute("value");
|
|
||||||
element.addEventListener("click", this.showTab.bind(this, value));
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return this._categories;
|
|
||||||
},
|
|
||||||
|
|
||||||
showTab(category) {
|
|
||||||
// If no category was specified, try the URL hash.
|
|
||||||
if (!category) {
|
|
||||||
category = location.hash.substr(1);
|
|
||||||
}
|
|
||||||
// If no corresponding category can be found, use the first available.
|
|
||||||
let categories = this.categories;
|
|
||||||
if (categories.indexOf(category) < 0) {
|
|
||||||
category = categories[0];
|
|
||||||
}
|
|
||||||
// Show the corresponding tab and hide the others.
|
|
||||||
document.querySelector(".tab.active").classList.remove("active");
|
|
||||||
document.querySelector("#tab-" + category).classList.add("active");
|
|
||||||
document.querySelector(".category[selected]").removeAttribute("selected");
|
|
||||||
document.querySelector(".category[value=" + category + "]")
|
|
||||||
.setAttribute("selected", "true");
|
|
||||||
location.hash = "#" + category;
|
|
||||||
|
|
||||||
if (category == "addons") {
|
|
||||||
this._component = React.render(React.createElement(AddonsComponent,
|
|
||||||
{client: this.client}), document.querySelector("#addons"));
|
|
||||||
} else if (category == "workers") {
|
|
||||||
this._component = React.render(React.createElement(WorkersComponent,
|
|
||||||
{client: this.client}), document.querySelector("#workers"));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
init() {
|
|
||||||
let telemetry = this._telemetry = new Telemetry();
|
|
||||||
telemetry.toolOpened("aboutdebugging");
|
|
||||||
|
|
||||||
// Link checkboxes to prefs.
|
|
||||||
let elements = document.querySelectorAll("input[type=checkbox][data-pref]");
|
|
||||||
Array.map(elements, element => {
|
|
||||||
let pref = element.dataset.pref;
|
|
||||||
|
|
||||||
let updatePref = () => {
|
|
||||||
Services.prefs.setBoolPref(pref, element.checked);
|
|
||||||
};
|
|
||||||
element.addEventListener("change", updatePref, false);
|
|
||||||
|
|
||||||
let onPreferenceChanged = () => {
|
|
||||||
element.checked = Services.prefs.getBoolPref(pref);
|
|
||||||
this.update();
|
|
||||||
};
|
|
||||||
|
|
||||||
Services.prefs.addObserver(pref, onPreferenceChanged, false);
|
|
||||||
this._prefListeners.push([pref, onPreferenceChanged]);
|
|
||||||
|
|
||||||
// Initialize the current checkbox element.
|
|
||||||
element.checked = Services.prefs.getBoolPref(pref);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Link buttons to their associated actions.
|
|
||||||
let loadAddonButton = document.getElementById("load-addon-from-file");
|
|
||||||
loadAddonButton.addEventListener("click", this.loadAddonFromFile);
|
|
||||||
|
|
||||||
if (!DebuggerServer.initialized) {
|
|
||||||
DebuggerServer.init();
|
|
||||||
DebuggerServer.addBrowserActors();
|
|
||||||
}
|
|
||||||
DebuggerServer.allowChromeProcess = true;
|
|
||||||
this.client = new DebuggerClient(DebuggerServer.connectPipe());
|
|
||||||
|
|
||||||
this.client.connect().then(() => {
|
|
||||||
// Show the first available tab.
|
|
||||||
this.showTab();
|
|
||||||
window.addEventListener("hashchange", () => this.showTab());
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
update() {
|
|
||||||
if (this._component) {
|
|
||||||
this._component.setState({});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
loadAddonFromFile() {
|
|
||||||
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
|
||||||
fp.init(window,
|
|
||||||
Strings.GetStringFromName("selectAddonFromFile"),
|
|
||||||
Ci.nsIFilePicker.modeOpen);
|
|
||||||
let res = fp.show();
|
|
||||||
if (res == Ci.nsIFilePicker.returnCancel || !fp.file) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
let file = fp.file;
|
|
||||||
// AddonManager.installTemporaryAddon accepts either
|
|
||||||
// addon directory or final xpi file.
|
|
||||||
if (!file.isDirectory() && !file.leafName.endsWith(".xpi")) {
|
|
||||||
file = file.parent;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
AddonManager.installTemporaryAddon(file);
|
|
||||||
} catch (e) {
|
|
||||||
alert("Error while installing the addon:\n" + e.message + "\n");
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
destroy() {
|
|
||||||
let telemetry = this._telemetry;
|
|
||||||
telemetry.toolClosed("aboutdebugging");
|
|
||||||
telemetry.destroy();
|
|
||||||
|
|
||||||
this._prefListeners.forEach(([pref, listener]) => {
|
|
||||||
Services.prefs.removeObserver(pref, listener);
|
|
||||||
});
|
|
||||||
this._prefListeners = [];
|
|
||||||
|
|
||||||
React.unmountComponentAtNode(document.querySelector("#addons"));
|
|
||||||
React.unmountComponentAtNode(document.querySelector("#workers"));
|
|
||||||
|
|
||||||
this.client.close();
|
|
||||||
this.client = null;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
window.addEventListener("DOMContentLoaded", function load() {
|
|
||||||
window.removeEventListener("DOMContentLoaded", load);
|
|
||||||
AboutDebugging.init();
|
|
||||||
});
|
|
||||||
|
|
||||||
window.addEventListener("unload", function unload() {
|
|
||||||
window.removeEventListener("unload", unload);
|
|
||||||
AboutDebugging.destroy();
|
|
||||||
});
|
|
@ -16,39 +16,8 @@
|
|||||||
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css" type="text/css"/>
|
<link rel="stylesheet" href="chrome://global/skin/in-content/common.css" type="text/css"/>
|
||||||
<link rel="stylesheet" href="chrome://devtools/content/aboutdebugging/aboutdebugging.css" type="text/css"/>
|
<link rel="stylesheet" href="chrome://devtools/content/aboutdebugging/aboutdebugging.css" type="text/css"/>
|
||||||
<script type="application/javascript" src="resource://devtools/client/shared/vendor/react.js"></script>
|
<script type="application/javascript" src="resource://devtools/client/shared/vendor/react.js"></script>
|
||||||
<script type="application/javascript;version=1.8" src="chrome://devtools/content/aboutdebugging/aboutdebugging.js"></script>
|
<script type="application/javascript;version=1.8" src="chrome://devtools/content/aboutdebugging/initializer.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body id="body">
|
<body id="body">
|
||||||
<div id="categories">
|
|
||||||
<div class="category" value="addons" selected="true">
|
|
||||||
<img class="category-icon" src="chrome://devtools/skin/images/debugging-addons.svg"/>
|
|
||||||
<div class="category-name">&aboutDebugging.addons;</div>
|
|
||||||
</div>
|
|
||||||
<div class="category" value="workers">
|
|
||||||
<img class="category-icon" src="chrome://devtools/skin/images/debugging-workers.svg"/>
|
|
||||||
<div class="category-name">&aboutDebugging.workers;</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="main-content">
|
|
||||||
<div id="tab-addons" class="tab active">
|
|
||||||
<div class="header">
|
|
||||||
<h1 class="header-name">&aboutDebugging.addons;</h1>
|
|
||||||
</div>
|
|
||||||
<div class="addon-controls">
|
|
||||||
<div class="addon-options">
|
|
||||||
<input id="enable-addon-debugging" type="checkbox" data-pref="devtools.chrome.enabled"/>
|
|
||||||
<label for="enable-addon-debugging" title="&aboutDebugging.addonDebugging.tooltip;">&aboutDebugging.addonDebugging.label;</label>
|
|
||||||
</div>
|
|
||||||
<button id="load-addon-from-file">&aboutDebugging.loadTemporaryAddon;</button>
|
|
||||||
</div>
|
|
||||||
<div id="addons"></div>
|
|
||||||
</div>
|
|
||||||
<div id="tab-workers" class="tab">
|
|
||||||
<div class="header">
|
|
||||||
<h1 class="header-name">&aboutDebugging.workers;</h1>
|
|
||||||
</div>
|
|
||||||
<div id="workers"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
84
devtools/client/aboutdebugging/components/aboutdebugging.js
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/* global React */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
loader.lazyRequireGetter(this, "React",
|
||||||
|
"devtools/client/shared/vendor/react");
|
||||||
|
loader.lazyRequireGetter(this, "AddonsTab",
|
||||||
|
"devtools/client/aboutdebugging/components/addons-tab", true);
|
||||||
|
loader.lazyRequireGetter(this, "TabMenu",
|
||||||
|
"devtools/client/aboutdebugging/components/tab-menu", true);
|
||||||
|
loader.lazyRequireGetter(this, "WorkersTab",
|
||||||
|
"devtools/client/aboutdebugging/components/workers-tab", true);
|
||||||
|
|
||||||
|
const Strings = Services.strings.createBundle(
|
||||||
|
"chrome://devtools/locale/aboutdebugging.properties");
|
||||||
|
|
||||||
|
const tabs = [
|
||||||
|
{ id: "addons", name: Strings.GetStringFromName("addons"),
|
||||||
|
icon: "chrome://devtools/skin/images/debugging-addons.svg",
|
||||||
|
component: AddonsTab },
|
||||||
|
{ id: "workers", name: Strings.GetStringFromName("workers"),
|
||||||
|
icon: "chrome://devtools/skin/images/debugging-workers.svg",
|
||||||
|
component: WorkersTab },
|
||||||
|
];
|
||||||
|
const defaultTabId = "addons";
|
||||||
|
|
||||||
|
exports.AboutDebuggingApp = React.createClass({
|
||||||
|
displayName: "AboutDebuggingApp",
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {
|
||||||
|
selectedTabId: defaultTabId
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.props.window.addEventListener("hashchange", this.onHashChange);
|
||||||
|
this.onHashChange();
|
||||||
|
this.props.telemetry.toolOpened("aboutdebugging");
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.props.window.removeEventListener("hashchange", this.onHashChange);
|
||||||
|
this.props.telemetry.toolClosed("aboutdebugging");
|
||||||
|
this.props.telemetry.destroy();
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { client } = this.props;
|
||||||
|
let { selectedTabId } = this.state;
|
||||||
|
let selectTab = this.selectTab;
|
||||||
|
|
||||||
|
let selectedTab = tabs.find(t => t.id == selectedTabId);
|
||||||
|
|
||||||
|
return React.createElement(
|
||||||
|
"div", { className: "app"},
|
||||||
|
React.createElement(TabMenu, { tabs, selectedTabId, selectTab }),
|
||||||
|
React.createElement("div", { className: "main-content" },
|
||||||
|
React.createElement(selectedTab.component, { client }))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
onHashChange() {
|
||||||
|
let tabId = this.props.window.location.hash.substr(1);
|
||||||
|
|
||||||
|
let isValid = tabs.some(t => t.id == tabId);
|
||||||
|
if (isValid) {
|
||||||
|
this.setState({ selectedTabId: tabId });
|
||||||
|
} else {
|
||||||
|
// If the current hash matches no valid category, navigate to the default
|
||||||
|
// tab.
|
||||||
|
this.selectTab(defaultTabId);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
selectTab(tabId) {
|
||||||
|
let win = this.props.window;
|
||||||
|
win.location.hash = "#" + tabId;
|
||||||
|
}
|
||||||
|
});
|
85
devtools/client/aboutdebugging/components/addons-controls.js
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/* global React */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
loader.lazyRequireGetter(this, "Ci", "chrome", true);
|
||||||
|
loader.lazyRequireGetter(this, "Cc", "chrome", true);
|
||||||
|
loader.lazyRequireGetter(this, "React", "devtools/client/shared/vendor/react");
|
||||||
|
loader.lazyRequireGetter(this, "Services");
|
||||||
|
|
||||||
|
loader.lazyImporter(this, "AddonManager",
|
||||||
|
"resource://gre/modules/AddonManager.jsm");
|
||||||
|
|
||||||
|
const Strings = Services.strings.createBundle(
|
||||||
|
"chrome://devtools/locale/aboutdebugging.properties");
|
||||||
|
|
||||||
|
const MORE_INFO_URL = "https://developer.mozilla.org/docs/Tools" +
|
||||||
|
"/about:debugging#Enabling_add-on_debugging";
|
||||||
|
|
||||||
|
exports.AddonsControls = React.createClass({
|
||||||
|
displayName: "AddonsControls",
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { debugDisabled } = this.props;
|
||||||
|
|
||||||
|
return React.createElement(
|
||||||
|
"div", { className: "addons-controls" }, React.createElement(
|
||||||
|
"div", { className: "addons-options" },
|
||||||
|
React.createElement("input", {
|
||||||
|
id: "enable-addon-debugging",
|
||||||
|
type: "checkbox",
|
||||||
|
checked: !debugDisabled,
|
||||||
|
onChange: this.onEnableAddonDebuggingChange,
|
||||||
|
}),
|
||||||
|
React.createElement("label", {
|
||||||
|
className: "addons-debugging-label",
|
||||||
|
htmlFor: "enable-addon-debugging",
|
||||||
|
title: Strings.GetStringFromName("addonDebugging.tooltip")
|
||||||
|
}, Strings.GetStringFromName("addonDebugging.label")),
|
||||||
|
"(",
|
||||||
|
React.createElement("a", { href: MORE_INFO_URL, target: "_blank" },
|
||||||
|
Strings.GetStringFromName("addonDebugging.moreInfo")),
|
||||||
|
")"
|
||||||
|
),
|
||||||
|
React.createElement("button", {
|
||||||
|
id: "load-addon-from-file",
|
||||||
|
onClick: this.loadAddonFromFile,
|
||||||
|
}, Strings.GetStringFromName("loadTemporaryAddon"))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
onEnableAddonDebuggingChange(event) {
|
||||||
|
let enabled = event.target.checked;
|
||||||
|
Services.prefs.setBoolPref("devtools.chrome.enabled", enabled);
|
||||||
|
Services.prefs.setBoolPref("devtools.debugger.remote-enabled", enabled);
|
||||||
|
},
|
||||||
|
|
||||||
|
loadAddonFromFile(event) {
|
||||||
|
let win = event.target.ownerDocument.defaultView;
|
||||||
|
|
||||||
|
let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker);
|
||||||
|
fp.init(win,
|
||||||
|
Strings.GetStringFromName("selectAddonFromFile"),
|
||||||
|
Ci.nsIFilePicker.modeOpen);
|
||||||
|
let res = fp.show();
|
||||||
|
if (res == Ci.nsIFilePicker.returnCancel || !fp.file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let file = fp.file;
|
||||||
|
// AddonManager.installTemporaryAddon accepts either
|
||||||
|
// addon directory or final xpi file.
|
||||||
|
if (!file.isDirectory() && !file.leafName.endsWith(".xpi")) {
|
||||||
|
file = file.parent;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
AddonManager.installTemporaryAddon(file);
|
||||||
|
} catch (e) {
|
||||||
|
win.alert("Error while installing the addon:\n" + e.message + "\n");
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
128
devtools/client/aboutdebugging/components/addons-tab.js
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/* global AddonManager, React */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
loader.lazyRequireGetter(this, "React",
|
||||||
|
"devtools/client/shared/vendor/react");
|
||||||
|
loader.lazyRequireGetter(this, "TargetList",
|
||||||
|
"devtools/client/aboutdebugging/components/target-list", true);
|
||||||
|
loader.lazyRequireGetter(this, "TabHeader",
|
||||||
|
"devtools/client/aboutdebugging/components/tab-header", true);
|
||||||
|
loader.lazyRequireGetter(this, "AddonsControls",
|
||||||
|
"devtools/client/aboutdebugging/components/addons-controls", true);
|
||||||
|
loader.lazyRequireGetter(this, "Services");
|
||||||
|
|
||||||
|
loader.lazyImporter(this, "AddonManager",
|
||||||
|
"resource://gre/modules/AddonManager.jsm");
|
||||||
|
|
||||||
|
const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
|
||||||
|
const Strings = Services.strings.createBundle(
|
||||||
|
"chrome://devtools/locale/aboutdebugging.properties");
|
||||||
|
|
||||||
|
const CHROME_ENABLED_PREF = "devtools.chrome.enabled";
|
||||||
|
const REMOTE_ENABLED_PREF = "devtools.debugger.remote-enabled";
|
||||||
|
|
||||||
|
exports.AddonsTab = React.createClass({
|
||||||
|
displayName: "AddonsTab",
|
||||||
|
|
||||||
|
getInitialState() {
|
||||||
|
return {
|
||||||
|
extensions: [],
|
||||||
|
debugDisabled: false,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
AddonManager.addAddonListener(this);
|
||||||
|
|
||||||
|
Services.prefs.addObserver(CHROME_ENABLED_PREF,
|
||||||
|
this.updateDebugStatus, false);
|
||||||
|
Services.prefs.addObserver(REMOTE_ENABLED_PREF,
|
||||||
|
this.updateDebugStatus, false);
|
||||||
|
|
||||||
|
this.updateDebugStatus();
|
||||||
|
this.updateAddonsList();
|
||||||
|
},
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
AddonManager.removeAddonListener(this);
|
||||||
|
Services.prefs.removeObserver(CHROME_ENABLED_PREF,
|
||||||
|
this.updateDebugStatus);
|
||||||
|
Services.prefs.removeObserver(REMOTE_ENABLED_PREF,
|
||||||
|
this.updateDebugStatus);
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { client } = this.props;
|
||||||
|
let { debugDisabled, extensions: targets } = this.state;
|
||||||
|
let name = Strings.GetStringFromName("extensions");
|
||||||
|
|
||||||
|
return React.createElement(
|
||||||
|
"div", { id: "tab-addons", className: "tab", role: "tabpanel",
|
||||||
|
"aria-labelledby": "tab-addons-header-name" },
|
||||||
|
React.createElement(TabHeader, {
|
||||||
|
id: "tab-addons-header-name",
|
||||||
|
name: Strings.GetStringFromName("addons")}),
|
||||||
|
React.createElement(AddonsControls, { debugDisabled }),
|
||||||
|
React.createElement(
|
||||||
|
"div", { id: "addons" },
|
||||||
|
React.createElement(TargetList,
|
||||||
|
{ name, targets, client, debugDisabled })
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
updateDebugStatus() {
|
||||||
|
let debugDisabled =
|
||||||
|
!Services.prefs.getBoolPref(CHROME_ENABLED_PREF) ||
|
||||||
|
!Services.prefs.getBoolPref(REMOTE_ENABLED_PREF);
|
||||||
|
|
||||||
|
this.setState({ debugDisabled });
|
||||||
|
},
|
||||||
|
|
||||||
|
updateAddonsList() {
|
||||||
|
AddonManager.getAllAddons(addons => {
|
||||||
|
let extensions = addons.filter(addon => addon.isDebuggable).map(addon => {
|
||||||
|
return {
|
||||||
|
name: addon.name,
|
||||||
|
icon: addon.iconURL || ExtensionIcon,
|
||||||
|
type: addon.type,
|
||||||
|
addonID: addon.id
|
||||||
|
};
|
||||||
|
});
|
||||||
|
this.setState({ extensions });
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mandatory callback as AddonManager listener.
|
||||||
|
*/
|
||||||
|
onInstalled() {
|
||||||
|
this.updateAddonsList();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mandatory callback as AddonManager listener.
|
||||||
|
*/
|
||||||
|
onUninstalled() {
|
||||||
|
this.updateAddonsList();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mandatory callback as AddonManager listener.
|
||||||
|
*/
|
||||||
|
onEnabled() {
|
||||||
|
this.updateAddonsList();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mandatory callback as AddonManager listener.
|
||||||
|
*/
|
||||||
|
onDisabled() {
|
||||||
|
this.updateAddonsList();
|
||||||
|
},
|
||||||
|
});
|
@ -1,80 +0,0 @@
|
|||||||
/* 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/. */
|
|
||||||
|
|
||||||
/* global AddonManager, React, TargetListComponent */
|
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
loader.lazyRequireGetter(this, "React",
|
|
||||||
"devtools/client/shared/vendor/react");
|
|
||||||
loader.lazyRequireGetter(this, "TargetListComponent",
|
|
||||||
"devtools/client/aboutdebugging/components/target-list", true);
|
|
||||||
loader.lazyRequireGetter(this, "Services");
|
|
||||||
|
|
||||||
loader.lazyImporter(this, "AddonManager",
|
|
||||||
"resource://gre/modules/AddonManager.jsm");
|
|
||||||
|
|
||||||
const ExtensionIcon = "chrome://mozapps/skin/extensions/extensionGeneric.svg";
|
|
||||||
const Strings = Services.strings.createBundle(
|
|
||||||
"chrome://devtools/locale/aboutdebugging.properties");
|
|
||||||
|
|
||||||
exports.AddonsComponent = React.createClass({
|
|
||||||
displayName: "AddonsComponent",
|
|
||||||
|
|
||||||
getInitialState() {
|
|
||||||
return {
|
|
||||||
extensions: []
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
componentDidMount() {
|
|
||||||
AddonManager.addAddonListener(this);
|
|
||||||
this.update();
|
|
||||||
},
|
|
||||||
|
|
||||||
componentWillUnmount() {
|
|
||||||
AddonManager.removeAddonListener(this);
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
let client = this.props.client;
|
|
||||||
let targets = this.state.extensions;
|
|
||||||
let name = Strings.GetStringFromName("extensions");
|
|
||||||
let debugDisabled = !Services.prefs.getBoolPref("devtools.chrome.enabled");
|
|
||||||
return React.createElement("div", null,
|
|
||||||
React.createElement(TargetListComponent,
|
|
||||||
{ name, targets, client, debugDisabled })
|
|
||||||
);
|
|
||||||
},
|
|
||||||
|
|
||||||
update() {
|
|
||||||
AddonManager.getAllAddons(addons => {
|
|
||||||
let extensions = addons.filter(addon => addon.isDebuggable).map(addon => {
|
|
||||||
return {
|
|
||||||
name: addon.name,
|
|
||||||
icon: addon.iconURL || ExtensionIcon,
|
|
||||||
type: addon.type,
|
|
||||||
addonID: addon.id
|
|
||||||
};
|
|
||||||
});
|
|
||||||
this.setState({ extensions });
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
onInstalled() {
|
|
||||||
this.update();
|
|
||||||
},
|
|
||||||
|
|
||||||
onUninstalled() {
|
|
||||||
this.update();
|
|
||||||
},
|
|
||||||
|
|
||||||
onEnabled() {
|
|
||||||
this.update();
|
|
||||||
},
|
|
||||||
|
|
||||||
onDisabled() {
|
|
||||||
this.update();
|
|
||||||
},
|
|
||||||
});
|
|
@ -3,8 +3,13 @@
|
|||||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
DevToolsModules(
|
DevToolsModules(
|
||||||
'addons.js',
|
'aboutdebugging.js',
|
||||||
|
'addons-controls.js',
|
||||||
|
'addons-tab.js',
|
||||||
|
'tab-header.js',
|
||||||
|
'tab-menu-entry.js',
|
||||||
|
'tab-menu.js',
|
||||||
'target-list.js',
|
'target-list.js',
|
||||||
'target.js',
|
'target.js',
|
||||||
'workers.js',
|
'workers-tab.js',
|
||||||
)
|
)
|
||||||
|
22
devtools/client/aboutdebugging/components/tab-header.js
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/* global React */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
loader.lazyRequireGetter(this, "React",
|
||||||
|
"devtools/client/shared/vendor/react");
|
||||||
|
|
||||||
|
exports.TabHeader = React.createClass({
|
||||||
|
displayName: "TabHeader",
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { name, id } = this.props;
|
||||||
|
|
||||||
|
return React.createElement(
|
||||||
|
"div", { className: "header" }, React.createElement(
|
||||||
|
"h1", { id, className: "header-name" }, name));
|
||||||
|
},
|
||||||
|
});
|
33
devtools/client/aboutdebugging/components/tab-menu-entry.js
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/* global React */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
loader.lazyRequireGetter(this, "React",
|
||||||
|
"devtools/client/shared/vendor/react");
|
||||||
|
|
||||||
|
exports.TabMenuEntry = React.createClass({
|
||||||
|
displayName: "TabMenuEntry",
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { icon, name, selected } = this.props;
|
||||||
|
|
||||||
|
// Here .category, .category-icon, .category-name classnames are used to
|
||||||
|
// apply common styles defined.
|
||||||
|
let className = "category" + (selected ? " selected" : "");
|
||||||
|
return React.createElement(
|
||||||
|
"div", { className, onClick: this.onClick,
|
||||||
|
"aria-selected": selected, role: "tab" },
|
||||||
|
React.createElement("img", { className: "category-icon", src: icon,
|
||||||
|
role: "presentation" }),
|
||||||
|
React.createElement("div", { className: "category-name" }, name)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
onClick() {
|
||||||
|
this.props.selectTab(this.props.tabId);
|
||||||
|
}
|
||||||
|
});
|
28
devtools/client/aboutdebugging/components/tab-menu.js
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/* global React */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
loader.lazyRequireGetter(this, "React",
|
||||||
|
"devtools/client/shared/vendor/react");
|
||||||
|
loader.lazyRequireGetter(this, "TabMenuEntry",
|
||||||
|
"devtools/client/aboutdebugging/components/tab-menu-entry", true);
|
||||||
|
|
||||||
|
exports.TabMenu = React.createClass({
|
||||||
|
displayName: "TabMenu",
|
||||||
|
|
||||||
|
render() {
|
||||||
|
let { tabs, selectedTabId, selectTab } = this.props;
|
||||||
|
let tabLinks = tabs.map(({ id, name, icon }) => {
|
||||||
|
let selected = id == selectedTabId;
|
||||||
|
return React.createElement(TabMenuEntry,
|
||||||
|
{ tabId: id, name, icon, selected, selectTab });
|
||||||
|
});
|
||||||
|
|
||||||
|
// "categories" id used for styling purposes
|
||||||
|
return React.createElement("div", { id: "categories" }, tabLinks);
|
||||||
|
},
|
||||||
|
});
|
@ -2,13 +2,13 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
/* global React, TargetComponent */
|
/* global React */
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
loader.lazyRequireGetter(this, "React",
|
loader.lazyRequireGetter(this, "React",
|
||||||
"devtools/client/shared/vendor/react");
|
"devtools/client/shared/vendor/react");
|
||||||
loader.lazyRequireGetter(this, "TargetComponent",
|
loader.lazyRequireGetter(this, "Target",
|
||||||
"devtools/client/aboutdebugging/components/target", true);
|
"devtools/client/aboutdebugging/components/target", true);
|
||||||
loader.lazyRequireGetter(this, "Services");
|
loader.lazyRequireGetter(this, "Services");
|
||||||
|
|
||||||
@ -18,15 +18,13 @@ const LocaleCompare = (a, b) => {
|
|||||||
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
return a.name.toLowerCase().localeCompare(b.name.toLowerCase());
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.TargetListComponent = React.createClass({
|
exports.TargetList = React.createClass({
|
||||||
displayName: "TargetListComponent",
|
displayName: "TargetList",
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let client = this.props.client;
|
let { client, debugDisabled } = this.props;
|
||||||
let debugDisabled = this.props.debugDisabled;
|
|
||||||
let targets = this.props.targets.sort(LocaleCompare).map(target => {
|
let targets = this.props.targets.sort(LocaleCompare).map(target => {
|
||||||
return React.createElement(TargetComponent,
|
return React.createElement(Target, { client, target, debugDisabled });
|
||||||
{ client, target, debugDisabled });
|
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
React.createElement("div", { id: this.props.id, className: "targets" },
|
React.createElement("div", { id: this.props.id, className: "targets" },
|
||||||
|
@ -23,12 +23,11 @@ loader.lazyRequireGetter(this, "gDevTools",
|
|||||||
const Strings = Services.strings.createBundle(
|
const Strings = Services.strings.createBundle(
|
||||||
"chrome://devtools/locale/aboutdebugging.properties");
|
"chrome://devtools/locale/aboutdebugging.properties");
|
||||||
|
|
||||||
exports.TargetComponent = React.createClass({
|
exports.Target = React.createClass({
|
||||||
displayName: "TargetComponent",
|
displayName: "Target",
|
||||||
|
|
||||||
debug() {
|
debug() {
|
||||||
let client = this.props.client;
|
let { client, target } = this.props;
|
||||||
let target = this.props.target;
|
|
||||||
switch (target.type) {
|
switch (target.type) {
|
||||||
case "extension":
|
case "extension":
|
||||||
BrowserToolboxProcess.init({ addonID: target.addonID });
|
BrowserToolboxProcess.init({ addonID: target.addonID });
|
||||||
@ -53,12 +52,11 @@ exports.TargetComponent = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let target = this.props.target;
|
let { target, debugDisabled } = this.props;
|
||||||
let debugDisabled = this.props.debugDisabled;
|
|
||||||
|
|
||||||
return React.createElement("div", { className: "target" },
|
return React.createElement("div", { className: "target" },
|
||||||
React.createElement("img", {
|
React.createElement("img", {
|
||||||
className: "target-icon",
|
className: "target-icon",
|
||||||
|
role: "presentation",
|
||||||
src: target.icon }),
|
src: target.icon }),
|
||||||
React.createElement("div", { className: "target-details" },
|
React.createElement("div", { className: "target-details" },
|
||||||
React.createElement("div", { className: "target-name" }, target.name),
|
React.createElement("div", { className: "target-name" }, target.name),
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
/* global React, TargetListComponent */
|
/* global React */
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
@ -10,8 +10,10 @@ loader.lazyRequireGetter(this, "Ci",
|
|||||||
"chrome", true);
|
"chrome", true);
|
||||||
loader.lazyRequireGetter(this, "React",
|
loader.lazyRequireGetter(this, "React",
|
||||||
"devtools/client/shared/vendor/react");
|
"devtools/client/shared/vendor/react");
|
||||||
loader.lazyRequireGetter(this, "TargetListComponent",
|
loader.lazyRequireGetter(this, "TargetList",
|
||||||
"devtools/client/aboutdebugging/components/target-list", true);
|
"devtools/client/aboutdebugging/components/target-list", true);
|
||||||
|
loader.lazyRequireGetter(this, "TabHeader",
|
||||||
|
"devtools/client/aboutdebugging/components/tab-header", true);
|
||||||
loader.lazyRequireGetter(this, "Services");
|
loader.lazyRequireGetter(this, "Services");
|
||||||
|
|
||||||
loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm");
|
loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm");
|
||||||
@ -20,8 +22,8 @@ const Strings = Services.strings.createBundle(
|
|||||||
"chrome://devtools/locale/aboutdebugging.properties");
|
"chrome://devtools/locale/aboutdebugging.properties");
|
||||||
const WorkerIcon = "chrome://devtools/skin/images/debugging-workers.svg";
|
const WorkerIcon = "chrome://devtools/skin/images/debugging-workers.svg";
|
||||||
|
|
||||||
exports.WorkersComponent = React.createClass({
|
exports.WorkersTab = React.createClass({
|
||||||
displayName: "WorkersComponent",
|
displayName: "WorkersTab",
|
||||||
|
|
||||||
getInitialState() {
|
getInitialState() {
|
||||||
return {
|
return {
|
||||||
@ -47,22 +49,30 @@ exports.WorkersComponent = React.createClass({
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
let client = this.props.client;
|
let { client } = this.props;
|
||||||
let workers = this.state.workers;
|
let { workers } = this.state;
|
||||||
return React.createElement("div", { className: "inverted-icons" },
|
|
||||||
React.createElement(TargetListComponent, {
|
return React.createElement(
|
||||||
id: "service-workers",
|
"div", { id: "tab-workers", className: "tab", role: "tabpanel",
|
||||||
name: Strings.GetStringFromName("serviceWorkers"),
|
"aria-labelledby": "tab-workers-header-name" },
|
||||||
targets: workers.service, client }),
|
React.createElement(TabHeader, {
|
||||||
React.createElement(TargetListComponent, {
|
id: "tab-workers-header-name",
|
||||||
id: "shared-workers",
|
name: Strings.GetStringFromName("workers")}),
|
||||||
name: Strings.GetStringFromName("sharedWorkers"),
|
React.createElement(
|
||||||
targets: workers.shared, client }),
|
"div", { id: "workers", className: "inverted-icons" },
|
||||||
React.createElement(TargetListComponent, {
|
React.createElement(TargetList, {
|
||||||
id: "other-workers",
|
id: "service-workers",
|
||||||
name: Strings.GetStringFromName("otherWorkers"),
|
name: Strings.GetStringFromName("serviceWorkers"),
|
||||||
targets: workers.other, client })
|
targets: workers.service, client }),
|
||||||
);
|
React.createElement(TargetList, {
|
||||||
|
id: "shared-workers",
|
||||||
|
name: Strings.GetStringFromName("sharedWorkers"),
|
||||||
|
targets: workers.shared, client }),
|
||||||
|
React.createElement(TargetList, {
|
||||||
|
id: "other-workers",
|
||||||
|
name: Strings.GetStringFromName("otherWorkers"),
|
||||||
|
targets: workers.other, client }))
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
update() {
|
update() {
|
55
devtools/client/aboutdebugging/initializer.js
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
/* eslint-env browser */
|
||||||
|
/* global DebuggerClient, DebuggerServer, React */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const { loader } = Components.utils.import(
|
||||||
|
"resource://devtools/shared/Loader.jsm", {});
|
||||||
|
|
||||||
|
loader.lazyRequireGetter(this, "DebuggerClient",
|
||||||
|
"devtools/shared/client/main", true);
|
||||||
|
loader.lazyRequireGetter(this, "DebuggerServer",
|
||||||
|
"devtools/server/main", true);
|
||||||
|
loader.lazyRequireGetter(this, "Telemetry",
|
||||||
|
"devtools/client/shared/telemetry");
|
||||||
|
loader.lazyRequireGetter(this, "AboutDebuggingApp",
|
||||||
|
"devtools/client/aboutdebugging/components/aboutdebugging", true);
|
||||||
|
|
||||||
|
var AboutDebugging = {
|
||||||
|
init() {
|
||||||
|
if (!DebuggerServer.initialized) {
|
||||||
|
DebuggerServer.init();
|
||||||
|
DebuggerServer.addBrowserActors();
|
||||||
|
}
|
||||||
|
DebuggerServer.allowChromeProcess = true;
|
||||||
|
this.client = new DebuggerClient(DebuggerServer.connectPipe());
|
||||||
|
|
||||||
|
this.client.connect().then(() => {
|
||||||
|
let client = this.client;
|
||||||
|
let telemetry = new Telemetry();
|
||||||
|
React.render(React.createElement(AboutDebuggingApp,
|
||||||
|
{ client, telemetry, window }), document.querySelector("#body"));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
destroy() {
|
||||||
|
React.unmountComponentAtNode(document.querySelector("#body"));
|
||||||
|
|
||||||
|
this.client.close();
|
||||||
|
this.client = null;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
window.addEventListener("DOMContentLoaded", function load() {
|
||||||
|
window.removeEventListener("DOMContentLoaded", load);
|
||||||
|
AboutDebugging.init();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("unload", function unload() {
|
||||||
|
window.removeEventListener("unload", unload);
|
||||||
|
AboutDebugging.destroy();
|
||||||
|
});
|
@ -8,6 +8,7 @@ support-files =
|
|||||||
service-workers/empty-sw.html
|
service-workers/empty-sw.html
|
||||||
service-workers/empty-sw.js
|
service-workers/empty-sw.js
|
||||||
|
|
||||||
|
[browser_addons_debugging_initial_state.js]
|
||||||
[browser_addons_install.js]
|
[browser_addons_install.js]
|
||||||
[browser_addons_toggle_debug.js]
|
[browser_addons_toggle_debug.js]
|
||||||
[browser_service_workers.js]
|
[browser_service_workers.js]
|
||||||
|
@ -0,0 +1,67 @@
|
|||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test that addons debugging controls are properly enabled/disabled depending
|
||||||
|
// on the values of the relevant preferences:
|
||||||
|
// - devtools.chrome.enabled
|
||||||
|
// - devtools.debugger.remote-enabled
|
||||||
|
|
||||||
|
const ADDON_ID = "test-devtools@mozilla.org";
|
||||||
|
|
||||||
|
const TEST_DATA = [
|
||||||
|
{
|
||||||
|
chromeEnabled: false,
|
||||||
|
debuggerRemoteEnable: false,
|
||||||
|
expected: false,
|
||||||
|
}, {
|
||||||
|
chromeEnabled: false,
|
||||||
|
debuggerRemoteEnable: true,
|
||||||
|
expected: false,
|
||||||
|
}, {
|
||||||
|
chromeEnabled: true,
|
||||||
|
debuggerRemoteEnable: false,
|
||||||
|
expected: false,
|
||||||
|
}, {
|
||||||
|
chromeEnabled: true,
|
||||||
|
debuggerRemoteEnable: true,
|
||||||
|
expected: true,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
add_task(function* () {
|
||||||
|
for (let testData of TEST_DATA) {
|
||||||
|
yield testCheckboxState(testData);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function* testCheckboxState(testData) {
|
||||||
|
info("Set preferences as defined by the current test data.");
|
||||||
|
yield new Promise(resolve => {
|
||||||
|
let options = {"set": [
|
||||||
|
["devtools.chrome.enabled", testData.chromeEnabled],
|
||||||
|
["devtools.debugger.remote-enabled", testData.debuggerRemoteEnable],
|
||||||
|
]};
|
||||||
|
SpecialPowers.pushPrefEnv(options, resolve);
|
||||||
|
});
|
||||||
|
|
||||||
|
let { tab, document } = yield openAboutDebugging("addons");
|
||||||
|
|
||||||
|
info("Install a test addon.");
|
||||||
|
yield installAddon(document, "addons/unpacked/install.rdf", "test-devtools");
|
||||||
|
|
||||||
|
info("Test checkbox checked state.");
|
||||||
|
let addonDebugCheckbox = document.querySelector("#enable-addon-debugging");
|
||||||
|
is(addonDebugCheckbox.checked, testData.expected,
|
||||||
|
"Addons debugging checkbox should be in expected state.");
|
||||||
|
|
||||||
|
info("Test debug buttons disabled state.");
|
||||||
|
let debugButtons = [...document.querySelectorAll("#addons .debug-button")];
|
||||||
|
ok(debugButtons.every(b => b.disabled != testData.expected),
|
||||||
|
"Debug buttons should be in the expected state");
|
||||||
|
|
||||||
|
info("Uninstall test addon installed earlier.");
|
||||||
|
yield uninstallAddon(ADDON_ID);
|
||||||
|
|
||||||
|
yield closeAboutDebugging(tab);
|
||||||
|
}
|