зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-central to b2g-inbound
--HG-- rename : browser/components/migration/tests/unit/test_fx_fhr.js => browser/components/migration/tests/unit/test_fx_telemetry.js extra : rebase_source : e274f592a1f49a14c57ea09dffa5c29e9c5ec2d1
This commit is contained in:
Коммит
b52ca99105
|
@ -141,6 +141,7 @@ NotificationController::IsUpdatePending()
|
|||
void
|
||||
NotificationController::WillRefresh(mozilla::TimeStamp aTime)
|
||||
{
|
||||
PROFILER_LABEL_FUNC(js::ProfileEntry::Category::OTHER);
|
||||
Telemetry::AutoTimer<Telemetry::A11Y_UPDATE_TIME> updateTimer;
|
||||
|
||||
// If the document accessible that notification collector was created for is
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_a11y_TextRange_inl_h__
|
||||
#define mozilla_a11y_TextRange_inl_h__
|
||||
|
||||
#include "TextRange.h"
|
||||
#include "HyperTextAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
inline Accessible*
|
||||
TextRange::Container() const
|
||||
{
|
||||
uint32_t pos1 = 0, pos2 = 0;
|
||||
nsAutoTArray<Accessible*, 30> parents1, parents2;
|
||||
return CommonParent(mStartContainer, mEndContainer,
|
||||
&parents1, &pos1, &parents2, &pos2);
|
||||
}
|
||||
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
|
@ -4,10 +4,9 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "TextRange.h"
|
||||
#include "TextRange-inl.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "HyperTextAccessible.h"
|
||||
#include "nsAccUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -59,42 +58,6 @@ TextRange::TextRange(HyperTextAccessible* aRoot,
|
|||
{
|
||||
}
|
||||
|
||||
Accessible*
|
||||
TextRange::Container() const
|
||||
{
|
||||
if (mStartContainer == mEndContainer)
|
||||
return mStartContainer;
|
||||
|
||||
// Build the chain of parents
|
||||
Accessible* p1 = mStartContainer;
|
||||
Accessible* p2 = mEndContainer;
|
||||
nsAutoTArray<Accessible*, 30> parents1, parents2;
|
||||
do {
|
||||
parents1.AppendElement(p1);
|
||||
p1 = p1->Parent();
|
||||
} while (p1);
|
||||
do {
|
||||
parents2.AppendElement(p2);
|
||||
p2 = p2->Parent();
|
||||
} while (p2);
|
||||
|
||||
// Find where the parent chain differs
|
||||
uint32_t pos1 = parents1.Length();
|
||||
uint32_t pos2 = parents2.Length();
|
||||
Accessible* parent = nullptr;
|
||||
uint32_t len = 0;
|
||||
for (len = std::min(pos1, pos2); len > 0; --len) {
|
||||
Accessible* child1 = parents1.ElementAt(--pos1);
|
||||
Accessible* child2 = parents2.ElementAt(--pos2);
|
||||
if (child1 != child2)
|
||||
break;
|
||||
|
||||
parent = child1;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::EmbeddedChildren(nsTArray<Accessible*>* aChildren) const
|
||||
{
|
||||
|
@ -111,28 +74,11 @@ TextRange::EmbeddedChildren(nsTArray<Accessible*>* aChildren) const
|
|||
|
||||
Accessible* p1 = mStartContainer->GetChildAtOffset(mStartOffset);
|
||||
Accessible* p2 = mEndContainer->GetChildAtOffset(mEndOffset);
|
||||
|
||||
uint32_t pos1 = 0, pos2 = 0;
|
||||
nsAutoTArray<Accessible*, 30> parents1, parents2;
|
||||
do {
|
||||
parents1.AppendElement(p1);
|
||||
p1 = p1->Parent();
|
||||
} while (p1);
|
||||
do {
|
||||
parents2.AppendElement(p2);
|
||||
p2 = p2->Parent();
|
||||
} while (p2);
|
||||
|
||||
// Find deepest common container.
|
||||
uint32_t pos1 = parents1.Length();
|
||||
uint32_t pos2 = parents2.Length();
|
||||
Accessible* container = nullptr;
|
||||
for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
|
||||
Accessible* child1 = parents1.ElementAt(--pos1);
|
||||
Accessible* child2 = parents2.ElementAt(--pos2);
|
||||
if (child1 != child2)
|
||||
break;
|
||||
|
||||
container = child1;
|
||||
}
|
||||
Accessible* container =
|
||||
CommonParent(p1, p2, &parents1, &pos1, &parents2, &pos2);
|
||||
|
||||
// Traverse the tree up to the container and collect embedded objects.
|
||||
for (uint32_t idx = 0; idx < pos1 - 1; idx++) {
|
||||
|
@ -196,6 +142,95 @@ TextRange::Normalize(ETextUnit aUnit)
|
|||
|
||||
}
|
||||
|
||||
bool
|
||||
TextRange::Crop(Accessible* aContainer)
|
||||
{
|
||||
uint32_t boundaryPos = 0, containerPos = 0;
|
||||
nsAutoTArray<Accessible*, 30> boundaryParents, containerParents;
|
||||
|
||||
// Crop the start boundary.
|
||||
Accessible* container = nullptr;
|
||||
Accessible* boundary = mStartContainer->GetChildAtOffset(mStartOffset);
|
||||
if (boundary != aContainer) {
|
||||
CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos,
|
||||
&containerParents, &containerPos);
|
||||
|
||||
if (boundaryPos == 0) {
|
||||
if (containerPos != 0) {
|
||||
// The container is contained by the start boundary, reduce the range to
|
||||
// the point starting at the container.
|
||||
aContainer->ToTextPoint(mStartContainer.StartAssignment(), &mStartOffset);
|
||||
static_cast<Accessible*>(mStartContainer)->AddRef();
|
||||
}
|
||||
else {
|
||||
// The start boundary and the container are siblings.
|
||||
container = aContainer;
|
||||
}
|
||||
}
|
||||
else if (containerPos != 0) {
|
||||
// The container does not contain the start boundary.
|
||||
boundary = boundaryParents[boundaryPos];
|
||||
container = containerParents[containerPos];
|
||||
}
|
||||
|
||||
if (container) {
|
||||
// If the range start is after the container, then make the range invalid.
|
||||
if (boundary->IndexInParent() > container->IndexInParent()) {
|
||||
return !!(mRoot = nullptr);
|
||||
}
|
||||
|
||||
// If the range starts before the container, then reduce the range to
|
||||
// the point starting at the container.
|
||||
if (boundary->IndexInParent() < container->IndexInParent()) {
|
||||
container->ToTextPoint(mStartContainer.StartAssignment(), &mStartOffset);
|
||||
mStartContainer.get()->AddRef();
|
||||
}
|
||||
}
|
||||
|
||||
boundaryParents.SetLengthAndRetainStorage(0);
|
||||
containerParents.SetLengthAndRetainStorage(0);
|
||||
}
|
||||
|
||||
boundary = mEndContainer->GetChildAtOffset(mEndOffset);
|
||||
if (boundary == aContainer) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Crop the end boundary.
|
||||
container = nullptr;
|
||||
CommonParent(boundary, aContainer, &boundaryParents, &boundaryPos,
|
||||
&containerParents, &containerPos);
|
||||
|
||||
if (boundaryPos == 0) {
|
||||
if (containerPos != 0) {
|
||||
aContainer->ToTextPoint(mEndContainer.StartAssignment(), &mEndOffset, false);
|
||||
static_cast<Accessible*>(mEndContainer)->AddRef();
|
||||
}
|
||||
else {
|
||||
container = aContainer;
|
||||
}
|
||||
}
|
||||
else if (containerPos != 0) {
|
||||
boundary = boundaryParents[boundaryPos];
|
||||
container = containerParents[containerPos];
|
||||
}
|
||||
|
||||
if (!container) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (boundary->IndexInParent() < container->IndexInParent()) {
|
||||
return !!(mRoot = nullptr);
|
||||
}
|
||||
|
||||
if (boundary->IndexInParent() > container->IndexInParent()) {
|
||||
container->ToTextPoint(mEndContainer.StartAssignment(), &mEndOffset, false);
|
||||
static_cast<Accessible*>(mEndContainer)->AddRef();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
TextRange::FindText(const nsAString& aText, EDirection aDirection,
|
||||
nsCaseTreatment aCaseSensitive, TextRange* aFoundRange) const
|
||||
|
@ -296,5 +331,46 @@ TextRange::MoveInternal(ETextUnit aUnit, int32_t aCount,
|
|||
|
||||
}
|
||||
|
||||
Accessible*
|
||||
TextRange::CommonParent(Accessible* aAcc1, Accessible* aAcc2,
|
||||
nsTArray<Accessible*>* aParents1, uint32_t* aPos1,
|
||||
nsTArray<Accessible*>* aParents2, uint32_t* aPos2) const
|
||||
{
|
||||
if (aAcc1 == aAcc2) {
|
||||
return aAcc1;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aParents1->Length() == 0 || aParents2->Length() == 0,
|
||||
"Wrong arguments");
|
||||
|
||||
// Build the chain of parents.
|
||||
Accessible* p1 = aAcc1;
|
||||
Accessible* p2 = aAcc2;
|
||||
do {
|
||||
aParents1->AppendElement(p1);
|
||||
p1 = p1->Parent();
|
||||
} while (p1);
|
||||
do {
|
||||
aParents2->AppendElement(p2);
|
||||
p2 = p2->Parent();
|
||||
} while (p2);
|
||||
|
||||
// Find where the parent chain differs
|
||||
*aPos1 = aParents1->Length();
|
||||
*aPos2 = aParents2->Length();
|
||||
Accessible* parent = nullptr;
|
||||
uint32_t len = 0;
|
||||
for (len = std::min(*aPos1, *aPos2); len > 0; --len) {
|
||||
Accessible* child1 = aParents1->ElementAt(--(*aPos1));
|
||||
Accessible* child2 = aParents2->ElementAt(--(*aPos2));
|
||||
if (child1 != child2)
|
||||
break;
|
||||
|
||||
parent = child1;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -131,6 +131,12 @@ public:
|
|||
*/
|
||||
void Normalize(ETextUnit aUnit);
|
||||
|
||||
/**
|
||||
* Crops the range if it overlaps the given accessible element boundaries,
|
||||
* returns true if the range was cropped successfully.
|
||||
*/
|
||||
bool Crop(Accessible* aContainer);
|
||||
|
||||
enum EDirection {
|
||||
eBackward,
|
||||
eForward
|
||||
|
@ -243,6 +249,14 @@ private:
|
|||
HyperTextAccessible* aStopContainer = nullptr,
|
||||
int32_t aStopOffset = 0);
|
||||
|
||||
/**
|
||||
* A helper method returning a common parent for two given accessible
|
||||
* elements.
|
||||
*/
|
||||
Accessible* CommonParent(Accessible* aAcc1, Accessible* aAcc2,
|
||||
nsTArray<Accessible*>* aParents1, uint32_t* aPos1,
|
||||
nsTArray<Accessible*>* aParents2, uint32_t* aPos2) const;
|
||||
|
||||
RefPtr<HyperTextAccessible> mRoot;
|
||||
RefPtr<HyperTextAccessible> mStartContainer;
|
||||
RefPtr<HyperTextAccessible> mEndContainer;
|
||||
|
|
|
@ -2250,6 +2250,30 @@ Accessible::AnchorURIAt(uint32_t aAnchorIndex)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
Accessible::ToTextPoint(HyperTextAccessible** aContainer, int32_t* aOffset,
|
||||
bool aIsBefore) const
|
||||
{
|
||||
if (IsHyperText()) {
|
||||
*aContainer = const_cast<Accessible*>(this)->AsHyperText();
|
||||
*aOffset = aIsBefore ? 0 : (*aContainer)->CharacterCount();
|
||||
return;
|
||||
}
|
||||
|
||||
const Accessible* child = nullptr;
|
||||
const Accessible* parent = this;
|
||||
do {
|
||||
child = parent;
|
||||
parent = parent->Parent();
|
||||
} while (parent && !parent->IsHyperText());
|
||||
|
||||
if (parent) {
|
||||
*aContainer = const_cast<Accessible*>(parent)->AsHyperText();
|
||||
*aOffset = (*aContainer)->GetChildOffset(
|
||||
child->IndexInParent() + static_cast<int32_t>(!aIsBefore));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// SelectAccessible
|
||||
|
|
|
@ -758,6 +758,12 @@ public:
|
|||
*/
|
||||
virtual already_AddRefed<nsIURI> AnchorURIAt(uint32_t aAnchorIndex);
|
||||
|
||||
/**
|
||||
* Returns a text point for the accessible element.
|
||||
*/
|
||||
void ToTextPoint(HyperTextAccessible** aContainer, int32_t* aOffset,
|
||||
bool aIsBefore = true) const;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// SelectAccessible
|
||||
|
||||
|
|
|
@ -1769,7 +1769,7 @@ HyperTextAccessible::EnclosingRange(a11y::TextRange& aRange) const
|
|||
void
|
||||
HyperTextAccessible::SelectionRanges(nsTArray<a11y::TextRange>* aRanges) const
|
||||
{
|
||||
NS_ASSERTION(aRanges->Length() != 0, "TextRange array supposed to be empty");
|
||||
MOZ_ASSERT(aRanges->Length() == 0, "TextRange array supposed to be empty");
|
||||
|
||||
dom::Selection* sel = DOMSelection();
|
||||
if (!sel)
|
||||
|
|
|
@ -258,7 +258,7 @@ public:
|
|||
* @param aInvalidateAfter [in, optional] indicates whether invalidate
|
||||
* cached offsets for next siblings of the child
|
||||
*/
|
||||
int32_t GetChildOffset(Accessible* aChild,
|
||||
int32_t GetChildOffset(const Accessible* aChild,
|
||||
bool aInvalidateAfter = false) const
|
||||
{
|
||||
int32_t index = GetIndexOf(aChild);
|
||||
|
|
|
@ -11,6 +11,7 @@ GARBAGE += $(MIDL_GENERATED_FILES)
|
|||
MIDL_INTERFACES = \
|
||||
Accessible2.idl \
|
||||
Accessible2_2.idl \
|
||||
Accessible2_3.idl \
|
||||
AccessibleAction.idl \
|
||||
AccessibleApplication.idl \
|
||||
AccessibleComponent.idl \
|
||||
|
|
|
@ -13,7 +13,7 @@ interface nsIVariant;
|
|||
/**
|
||||
* A range representing a piece of text in the document.
|
||||
*/
|
||||
[scriptable, uuid(525b3401-8a67-4822-b35d-661065767cd8)]
|
||||
[scriptable, uuid(c4515623-55f9-4543-a3d5-c1e9afa588f4)]
|
||||
interface nsIAccessibleTextRange : nsISupports
|
||||
{
|
||||
readonly attribute nsIAccessibleText startContainer;
|
||||
|
@ -81,6 +81,11 @@ interface nsIAccessibleTextRange : nsISupports
|
|||
*/
|
||||
void normalize(in unsigned long aUnit);
|
||||
|
||||
/**
|
||||
* Crops the range by the given accessible element.
|
||||
*/
|
||||
boolean crop(in nsIAccessible aContainer);
|
||||
|
||||
/**
|
||||
* Return range enclosing the found text.
|
||||
*/
|
||||
|
|
|
@ -473,6 +473,10 @@ function testTextRange(aRange, aRangeDescr, aStartContainer, aStartOffset,
|
|||
is(aRange.endOffset, aEndOffset,
|
||||
"Wrong end offset of " + aRangeDescr);
|
||||
|
||||
if (aText === undefined) {
|
||||
return;
|
||||
}
|
||||
|
||||
is(aRange.text, aText, "Wrong text of " + aRangeDescr);
|
||||
|
||||
var children = aRange.embeddedChildren;
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
[DEFAULT]
|
||||
|
||||
[test_general.html]
|
||||
[test_selection.html]
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Text Range selection tests</title>
|
||||
<link rel="stylesheet" type="text/css"
|
||||
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
|
||||
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../common.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../text.js"></script>
|
||||
<script type="application/javascript"
|
||||
src="../layout.js"></script>
|
||||
<script type="application/javascript">
|
||||
|
||||
function doTest()
|
||||
{
|
||||
var sel = window.getSelection();
|
||||
var p = getNode("p1");
|
||||
var a = getNode("p2_a");
|
||||
|
||||
var range = document.createRange();
|
||||
sel.addRange(range);
|
||||
|
||||
// the accessible is contained by the range
|
||||
range.selectNode(p);
|
||||
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #1", document, 3, document, 4);
|
||||
|
||||
ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #1.");
|
||||
testTextRange(a11yrange, "cropped range #1", a, 0, a, 5);
|
||||
|
||||
// the range is contained by the accessible
|
||||
range.selectNode(a);
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #2", p, 5, p, 6);
|
||||
|
||||
ok(a11yrange.crop(getAccessible(p)), "Range failed to crop #2.");
|
||||
testTextRange(a11yrange, "cropped range #2", p, 5, p, 6);
|
||||
|
||||
// the range starts before the accessible and ends inside it
|
||||
range.setStart(p, 0);
|
||||
range.setEndAfter(a.firstChild, 4);
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #3", p, 0, a, 4);
|
||||
|
||||
ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #3.");
|
||||
testTextRange(a11yrange, "cropped range #3", a, 0, a, 4);
|
||||
|
||||
// the range starts inside the accessible and ends after it
|
||||
range.setStart(a.firstChild, 1);
|
||||
range.setEndAfter(p);
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #4", a, 1, document, 4);
|
||||
|
||||
ok(a11yrange.crop(getAccessible(a)), "Range failed to crop #4.");
|
||||
testTextRange(a11yrange, "cropped range #4", a, 1, a, 5);
|
||||
|
||||
// the range ends before the accessible
|
||||
range.setStart(p.firstChild, 0);
|
||||
range.setEnd(p.firstChild, 4);
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #5", p, 0, p, 4);
|
||||
ok(!a11yrange.crop(getAccessible(a)), "Crop #5 succeeded while it shouldn't");
|
||||
|
||||
// the range starts after the accessible
|
||||
range.setStart(p.lastChild, 0);
|
||||
range.setEnd(p.lastChild, 4);
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #6", p, 6, p, 10);
|
||||
|
||||
ok(!a11yrange.crop(getAccessible(a)), "Crop #6 succeeded while it shouldn't");
|
||||
|
||||
// crop a range by a table
|
||||
range.selectNode(getNode("c2"));
|
||||
var a11yranges = getAccessible(document, [nsIAccessibleText]).selectionRanges;
|
||||
var a11yrange = a11yranges.queryElementAt(0, nsIAccessibleTextRange);
|
||||
|
||||
testTextRange(a11yrange, "selection range #7", document, 4, document, 5);
|
||||
|
||||
ok(a11yrange.crop(getAccessible("table")), "Range failed to crop #7.");
|
||||
testTextRange(a11yrange, "cropped range #7", "c2", 5, "c2", 6);
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addA11yLoadEvent(doTest);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<a target="_blank"
|
||||
title="Implement IAccessible2_3::selectionRanges"
|
||||
href="https://bugzilla.mozilla.org/show_bug.cgi?id=1233118">Bug 1233118</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
|
||||
<p id="p1">text <a id="p2_a" href="www">link<img id="p2_img", src="../moz.png"></a> text</p>
|
||||
|
||||
<div id="c2">start<table id="table"><tr><td>cell</td></tr></table>end</div>
|
||||
</body>
|
||||
</html>
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "Accessible2_i.c"
|
||||
#include "Accessible2_2_i.c"
|
||||
#include "Accessible2_3_i.c"
|
||||
#include "AccessibleRole.h"
|
||||
#include "AccessibleStates.h"
|
||||
|
||||
|
@ -18,6 +19,7 @@
|
|||
#include "nsIAccessibleTypes.h"
|
||||
#include "mozilla/a11y/PDocAccessible.h"
|
||||
#include "Relation.h"
|
||||
#include "TextRange-inl.h"
|
||||
#include "nsAccessibilityService.h"
|
||||
|
||||
#include "nsIPersistentProperties2.h"
|
||||
|
@ -40,7 +42,9 @@ ia2Accessible::QueryInterface(REFIID iid, void** ppv)
|
|||
|
||||
*ppv = nullptr;
|
||||
|
||||
if (IID_IAccessible2_2 == iid)
|
||||
if (IID_IAccessible2_3 == iid)
|
||||
*ppv = static_cast<IAccessible2_3*>(this);
|
||||
else if (IID_IAccessible2_2 == iid)
|
||||
*ppv = static_cast<IAccessible2_2*>(this);
|
||||
else if (IID_IAccessible2 == iid && !Compatibility::IsIA2Off())
|
||||
*ppv = static_cast<IAccessible2*>(this);
|
||||
|
@ -749,6 +753,58 @@ ia2Accessible::get_relationTargetsOfType(BSTR aType,
|
|||
A11Y_TRYBLOCK_END
|
||||
}
|
||||
|
||||
STDMETHODIMP
|
||||
ia2Accessible::get_selectionRanges(IA2Range** aRanges,
|
||||
long *aNRanges)
|
||||
{
|
||||
A11Y_TRYBLOCK_BEGIN
|
||||
|
||||
if (!aRanges || !aNRanges || aNRanges <= 0)
|
||||
return E_INVALIDARG;
|
||||
|
||||
*aNRanges = 0;
|
||||
|
||||
AccessibleWrap* acc = static_cast<AccessibleWrap*>(this);
|
||||
if (acc->IsDefunct())
|
||||
return CO_E_OBJNOTCONNECTED;
|
||||
|
||||
nsAutoTArray<TextRange, 1> ranges;
|
||||
acc->Document()->SelectionRanges(&ranges);
|
||||
uint32_t len = ranges.Length();
|
||||
for (uint32_t idx = 0; idx < len; idx++) {
|
||||
if (!ranges[idx].Crop(acc)) {
|
||||
ranges.RemoveElementAt(idx);
|
||||
}
|
||||
}
|
||||
|
||||
*aNRanges = ranges.Length();
|
||||
*aRanges = static_cast<IA2Range*>(
|
||||
::CoTaskMemAlloc(sizeof(IA2Range) * *aNRanges));
|
||||
if (!*aRanges)
|
||||
return E_OUTOFMEMORY;
|
||||
|
||||
for (uint32_t idx = 0; idx < static_cast<uint32_t>(*aNRanges); idx++) {
|
||||
AccessibleWrap* anchor =
|
||||
static_cast<AccessibleWrap*>(ranges[idx].StartContainer());
|
||||
(*aRanges)[idx].anchor = static_cast<IAccessible2*>(anchor);
|
||||
anchor->AddRef();
|
||||
|
||||
(*aRanges)[idx].anchorOffset = ranges[idx].StartOffset();
|
||||
|
||||
AccessibleWrap* active =
|
||||
static_cast<AccessibleWrap*>(ranges[idx].EndContainer());
|
||||
(*aRanges)[idx].active = static_cast<IAccessible2*>(active);
|
||||
active->AddRef();
|
||||
|
||||
(*aRanges)[idx].activeOffset = ranges[idx].EndOffset();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
|
||||
A11Y_TRYBLOCK_END
|
||||
}
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Helpers
|
||||
|
||||
|
|
|
@ -9,13 +9,13 @@
|
|||
|
||||
#include "nsISupports.h"
|
||||
|
||||
#include "Accessible2_2.h"
|
||||
#include "Accessible2_3.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
class Attribute;
|
||||
|
||||
class ia2Accessible : public IAccessible2_2
|
||||
class ia2Accessible : public IAccessible2_3
|
||||
{
|
||||
public:
|
||||
|
||||
|
@ -104,6 +104,11 @@ public:
|
|||
/* [out, retval] */ long* nTargets
|
||||
);
|
||||
|
||||
// IAccessible2_3
|
||||
virtual /* [propget] */ HRESULT STDMETHODCALLTYPE get_selectionRanges(
|
||||
/* [out, size_is(,*nRanges)] */ IA2Range** ranges,
|
||||
/* [out, retval] */ long *nRanges);
|
||||
|
||||
// Helper method
|
||||
static HRESULT ConvertToIA2Attributes(nsIPersistentProperties* aAttributes,
|
||||
BSTR* aIA2Attributes);
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
#include "xpcAccessibleTextRange.h"
|
||||
|
||||
#include "TextRange.h"
|
||||
#include "TextRange-inl.h"
|
||||
#include "xpcAccessibleDocument.h"
|
||||
|
||||
#include "nsIMutableArray.h"
|
||||
|
@ -170,6 +170,16 @@ xpcAccessibleTextRange::Normalize(uint32_t aUnit)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
xpcAccessibleTextRange::Crop(nsIAccessible* aContainer, bool* aSuccess)
|
||||
{
|
||||
Accessible* container = aContainer->ToInternalAccessible();
|
||||
NS_ENSURE_TRUE(container, NS_ERROR_INVALID_ARG);
|
||||
|
||||
*aSuccess = mRange.Crop(container);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
xpcAccessibleTextRange::FindText(const nsAString& aText, bool aIsBackward,
|
||||
bool aIsIgnoreCase,
|
||||
|
|
|
@ -49,6 +49,7 @@ public:
|
|||
NS_IMETHOD MoveStart(uint32_t aUnit, int32_t aCount) final override;
|
||||
NS_IMETHOD MoveEnd(uint32_t aUnit, int32_t aCount) final override;
|
||||
NS_IMETHOD Normalize(uint32_t aUnit) final override;
|
||||
NS_IMETHOD Crop(nsIAccessible* aContainer, bool* aSuccess) final override;
|
||||
NS_IMETHOD FindText(const nsAString& aText, bool aIsBackward, bool aIsIgnoreCase,
|
||||
nsIAccessibleTextRange** aRange) final override;
|
||||
NS_IMETHOD FindAttr(uint32_t aAttr, nsIVariant* aVal, bool aIsBackward,
|
||||
|
|
|
@ -1394,8 +1394,9 @@ pref("dom.identity.enabled", false);
|
|||
// Block insecure active content on https pages
|
||||
pref("security.mixed_content.block_active_content", true);
|
||||
|
||||
// Show degraded UI for http pages with password fields
|
||||
#ifdef NIGHTLY_BUILD
|
||||
// Show degraded UI for http pages with password fields.
|
||||
// Only for Nightly and Dev Edition for not, not for beta or release.
|
||||
#ifndef RELEASE_BUILD
|
||||
pref("security.insecure_password.ui.enabled", true);
|
||||
#else
|
||||
pref("security.insecure_password.ui.enabled", false);
|
||||
|
@ -1404,6 +1405,8 @@ pref("security.insecure_password.ui.enabled", false);
|
|||
// 1 = allow MITM for certificate pinning checks.
|
||||
pref("security.cert_pinning.enforcement_level", 1);
|
||||
|
||||
// NB: Changes to this pref affect CERT_CHAIN_SHA1_POLICY_STATUS telemetry.
|
||||
// See the comment in CertVerifier.cpp.
|
||||
// 0 = allow SHA-1
|
||||
pref("security.pki.sha1_enforcement_level", 0);
|
||||
|
||||
|
|
|
@ -135,11 +135,17 @@
|
|||
el.parentNode.removeChild(el);
|
||||
el = document.getElementById("errorShortDescText_forbidden");
|
||||
el.parentNode.removeChild(el);
|
||||
el = document.getElementById("whyForbiddenButton");
|
||||
el.parentNode.removeChild(el);
|
||||
} else {
|
||||
el = document.getElementById("ignoreWarningButton");
|
||||
el.parentNode.removeChild(el);
|
||||
el = document.getElementById("reportButton");
|
||||
el.parentNode.removeChild(el);
|
||||
|
||||
// Remove red style: A "forbidden site" does not warrant the same level
|
||||
// of anxiety as a security concern.
|
||||
document.documentElement.className = "";
|
||||
}
|
||||
|
||||
// Set sitename
|
||||
|
@ -197,7 +203,7 @@
|
|||
<h1 id="errorTitleText_phishing">&safeb.blocked.phishingPage.title;</h1>
|
||||
<h1 id="errorTitleText_malware">&safeb.blocked.malwarePage.title;</h1>
|
||||
<h1 id="errorTitleText_unwanted">&safeb.blocked.unwantedPage.title;</h1>
|
||||
<h1 id="errorTitleText_forbidden">&safeb.blocked.forbiddenPage.title;</h1>
|
||||
<h1 id="errorTitleText_forbidden">&safeb.blocked.forbiddenPage.title2;</h1>
|
||||
</div>
|
||||
|
||||
<div id="errorLongContent">
|
||||
|
@ -207,7 +213,7 @@
|
|||
<p id="errorShortDescText_phishing">&safeb.blocked.phishingPage.shortDesc;</p>
|
||||
<p id="errorShortDescText_malware">&safeb.blocked.malwarePage.shortDesc;</p>
|
||||
<p id="errorShortDescText_unwanted">&safeb.blocked.unwantedPage.shortDesc;</p>
|
||||
<p id="errorShortDescText_forbidden">&safeb.blocked.forbiddenPage.shortDesc;</p>
|
||||
<p id="errorShortDescText_forbidden">&safeb.blocked.forbiddenPage.shortDesc2;</p>
|
||||
</div>
|
||||
|
||||
<!-- Long Description -->
|
||||
|
@ -222,6 +228,7 @@
|
|||
<!-- Commands handled in browser.js -->
|
||||
<button id="getMeOutButton">&safeb.palm.accept.label;</button>
|
||||
<button id="reportButton">&safeb.palm.reportPage.label;</button>
|
||||
<button id="whyForbiddenButton">&safeb.palm.whyForbidden.label;</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="ignoreWarning">
|
||||
|
|
|
@ -2913,6 +2913,12 @@ var BrowserOnClick = {
|
|||
this.ignoreWarningButton(reason);
|
||||
}
|
||||
break;
|
||||
|
||||
case "whyForbiddenButton":
|
||||
// This is the "Why is this site blocked" button for family friendly browsing
|
||||
// for Fennec. There's no desktop focused support page yet.
|
||||
gBrowser.loadURI("https://support.mozilla.org/kb/controlledaccess");
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
|
|
@ -6031,8 +6031,11 @@
|
|||
// visual status to selected until after we receive confirmation that its content has painted.
|
||||
this._logicallySelected = val;
|
||||
|
||||
// If we're non-e10s we should update the visual selection as well at the same time
|
||||
if (!gMultiProcessBrowser) {
|
||||
// If we're non-e10s we should update the visual selection as well at the same time,
|
||||
// *or* if we're e10s and the visually selected tab isn't changing, in which case the
|
||||
// tab switcher code won't run and update anything else (like the before- and after-
|
||||
// selected attributes).
|
||||
if (!gMultiProcessBrowser || (val && this.hasAttribute("visuallyselected"))) {
|
||||
this._visuallySelected = val;
|
||||
}
|
||||
|
||||
|
|
|
@ -554,7 +554,7 @@ add_task(function* test_offline_cache() {
|
|||
// Store something to the offline cache
|
||||
var appcacheserv = Cc["@mozilla.org/network/application-cache-service;1"]
|
||||
.getService(Ci.nsIApplicationCacheService);
|
||||
var appcachegroupid = appcacheserv.buildGroupID(makeURI(URL + "/manifest"), LoadContextInfo.default);
|
||||
var appcachegroupid = appcacheserv.buildGroupIDForInfo(makeURI(URL + "/manifest"), LoadContextInfo.default);
|
||||
var appcache = appcacheserv.createApplicationCache(appcachegroupid);
|
||||
var storage = Services.cache2.appCacheStorage(LoadContextInfo.default, appcache);
|
||||
|
||||
|
|
|
@ -4,4 +4,3 @@ support-files =
|
|||
file_reflect_cookie_into_title.html
|
||||
|
||||
[browser_usercontext.js]
|
||||
skip-if = e10s
|
||||
|
|
|
@ -161,7 +161,7 @@ FirefoxProfileMigrator.prototype._getResourcesInternal = function(sourceProfileD
|
|||
}
|
||||
}
|
||||
|
||||
// FHR related migrations.
|
||||
// Telemetry related migrations.
|
||||
let times = {
|
||||
name: "times", // name is used only by tests.
|
||||
type: types.OTHERDATA,
|
||||
|
@ -178,69 +178,60 @@ FirefoxProfileMigrator.prototype._getResourcesInternal = function(sourceProfileD
|
|||
);
|
||||
}
|
||||
};
|
||||
let healthReporter = {
|
||||
name: "healthreporter", // name is used only by tests...
|
||||
let telemetry = {
|
||||
name: "telemetry", // name is used only by tests...
|
||||
type: types.OTHERDATA,
|
||||
migrate: aCallback => {
|
||||
// the health-reporter can't have been initialized yet so it's safe to
|
||||
// copy the SQL file.
|
||||
let createSubDir = (name) => {
|
||||
let dir = currentProfileDir.clone();
|
||||
dir.append(name);
|
||||
dir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
return dir;
|
||||
};
|
||||
|
||||
// We only support the default database name - copied from healthreporter.jsm
|
||||
const DEFAULT_DATABASE_NAME = "healthreport.sqlite";
|
||||
let path = OS.Path.join(sourceProfileDir.path, DEFAULT_DATABASE_NAME);
|
||||
let sqliteFile = FileUtils.File(path);
|
||||
if (sqliteFile.exists()) {
|
||||
sqliteFile.copyTo(currentProfileDir, "");
|
||||
}
|
||||
// In unusual cases there may be 2 additional files - a "write ahead log"
|
||||
// (-wal) file and a "shared memory file" (-shm). The wal file contains
|
||||
// data that will be replayed when the DB is next opened, while the shm
|
||||
// file is ignored in that case - the replay happens using only the wal.
|
||||
// So we *do* copy a wal if it exists, but not a shm.
|
||||
// See https://www.sqlite.org/tempfiles.html for more.
|
||||
// (Note also we attempt these copies even if we can't find the DB, and
|
||||
// rely on FHR itself to do the right thing if it can)
|
||||
path = OS.Path.join(sourceProfileDir.path, DEFAULT_DATABASE_NAME + "-wal");
|
||||
let sqliteWal = FileUtils.File(path);
|
||||
if (sqliteWal.exists()) {
|
||||
sqliteWal.copyTo(currentProfileDir, "");
|
||||
}
|
||||
|
||||
// If the 'healthreport' directory exists we copy everything from it.
|
||||
let subdir = this._getFileObject(sourceProfileDir, "healthreport");
|
||||
// If the 'datareporting' directory exists we migrate files from it.
|
||||
let haveStateFile = false;
|
||||
let subdir = this._getFileObject(sourceProfileDir, "datareporting");
|
||||
if (subdir && subdir.isDirectory()) {
|
||||
// Copy all regular files.
|
||||
let dest = currentProfileDir.clone();
|
||||
dest.append("healthreport");
|
||||
dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE,
|
||||
FileUtils.PERMS_DIRECTORY);
|
||||
// Copy only specific files.
|
||||
let toCopy = ["state.json", "session-state.json"];
|
||||
|
||||
let dest = createSubDir("datareporting");
|
||||
let enumerator = subdir.directoryEntries;
|
||||
while (enumerator.hasMoreElements()) {
|
||||
let file = enumerator.getNext().QueryInterface(Components.interfaces.nsIFile);
|
||||
if (file.isDirectory()) {
|
||||
let file = enumerator.getNext().QueryInterface(Ci.nsIFile);
|
||||
if (file.isDirectory() || toCopy.indexOf(file.leafName) == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (file.leafName == "state.json") {
|
||||
haveStateFile = true;
|
||||
}
|
||||
file.copyTo(dest, "");
|
||||
}
|
||||
}
|
||||
// If the 'datareporting' directory exists we copy just state.json
|
||||
subdir = this._getFileObject(sourceProfileDir, "datareporting");
|
||||
if (subdir && subdir.isDirectory()) {
|
||||
let stateFile = this._getFileObject(subdir, "state.json");
|
||||
if (stateFile) {
|
||||
let dest = currentProfileDir.clone();
|
||||
dest.append("datareporting");
|
||||
dest.create(Components.interfaces.nsIFile.DIRECTORY_TYPE,
|
||||
FileUtils.PERMS_DIRECTORY);
|
||||
stateFile.copyTo(dest, "");
|
||||
|
||||
if (!haveStateFile) {
|
||||
// Fall back to migrating the state file that contains the client id from healthreport/.
|
||||
// We first moved the client id management from the FHR implementation to the datareporting
|
||||
// service.
|
||||
// Consequently, we try to migrate an existing FHR state file here as a fallback.
|
||||
let subdir = this._getFileObject(sourceProfileDir, "healthreport");
|
||||
if (subdir && subdir.isDirectory()) {
|
||||
let stateFile = this._getFileObject(subdir, "state.json");
|
||||
if (stateFile) {
|
||||
let dest = createSubDir("healthreport");
|
||||
stateFile.copyTo(dest, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
aCallback(true);
|
||||
}
|
||||
}
|
||||
|
||||
return [places, cookies, passwords, formData, dictionary, bookmarksBackups,
|
||||
session, times, healthReporter].filter(r => r);
|
||||
session, times, telemetry].filter(r => r);
|
||||
};
|
||||
|
||||
Object.defineProperty(FirefoxProfileMigrator.prototype, "startupOnlyMigrator", {
|
||||
|
|
|
@ -77,6 +77,13 @@ function writeToFile(dir, leafName, contents) {
|
|||
outputStream.close();
|
||||
}
|
||||
|
||||
function createSubDir(dir, subDirName) {
|
||||
let subDir = dir.clone();
|
||||
subDir.append(subDirName);
|
||||
subDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
return subDir;
|
||||
}
|
||||
|
||||
function promiseMigrator(name, srcDir, targetDir) {
|
||||
let migrator = Cc["@mozilla.org/profile/migrator;1?app=browser&type=firefox"]
|
||||
.createInstance(Ci.nsISupports)
|
||||
|
@ -92,150 +99,168 @@ function promiseMigrator(name, srcDir, targetDir) {
|
|||
throw new Error("failed to find the " + name + " migrator");
|
||||
}
|
||||
|
||||
function promiseFHRMigrator(srcDir, targetDir) {
|
||||
return promiseMigrator("healthreporter", srcDir, targetDir);
|
||||
function promiseTelemetryMigrator(srcDir, targetDir) {
|
||||
return promiseMigrator("telemetry", srcDir, targetDir);
|
||||
}
|
||||
|
||||
add_task(function* test_empty() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
let ok = yield promiseFHRMigrator(srcDir, targetDir);
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true with empty directories");
|
||||
// check both are empty
|
||||
checkDirectoryContains(srcDir, {});
|
||||
checkDirectoryContains(targetDir, {});
|
||||
});
|
||||
|
||||
add_task(function* test_just_sqlite() {
|
||||
add_task(function* test_migrate_files() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
let contents = "hello there\n\n";
|
||||
writeToFile(srcDir, "healthreport.sqlite", contents);
|
||||
|
||||
let ok = yield promiseFHRMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true with sqlite file copied");
|
||||
|
||||
checkDirectoryContains(targetDir, {
|
||||
"healthreport.sqlite": contents,
|
||||
// Set up datareporting files, some to copy, some not.
|
||||
let stateContent = JSON.stringify({
|
||||
clientId: "68d5474e-19dc-45c1-8e9a-81fca592707c",
|
||||
});
|
||||
});
|
||||
let sessionStateContent = "foobar 5432";
|
||||
let subDir = createSubDir(srcDir, "datareporting");
|
||||
writeToFile(subDir, "state.json", stateContent);
|
||||
writeToFile(subDir, "session-state.json", sessionStateContent);
|
||||
writeToFile(subDir, "other.file", "do not copy");
|
||||
|
||||
add_task(function* test_sqlite_extras() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
let archived = createSubDir(subDir, "archived");
|
||||
writeToFile(archived, "other.file", "do not copy");
|
||||
|
||||
let contents_sqlite = "hello there\n\n";
|
||||
writeToFile(srcDir, "healthreport.sqlite", contents_sqlite);
|
||||
// Set up FHR files, they should not be copied.
|
||||
writeToFile(srcDir, "healthreport.sqlite", "do not copy");
|
||||
writeToFile(srcDir, "healthreport.sqlite-wal", "do not copy");
|
||||
subDir = createSubDir(srcDir, "healthreport");
|
||||
writeToFile(subDir, "state.json", "do not copy");
|
||||
writeToFile(subDir, "other.file", "do not copy");
|
||||
|
||||
let contents_wal = "this is the wal\n\n";
|
||||
writeToFile(srcDir, "healthreport.sqlite-wal", contents_wal);
|
||||
|
||||
// and the -shm - this should *not* be copied.
|
||||
writeToFile(srcDir, "healthreport.sqlite-shm", "whatever");
|
||||
|
||||
let ok = yield promiseFHRMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true with sqlite file copied");
|
||||
// Perform migration.
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true with important telemetry files copied");
|
||||
|
||||
checkDirectoryContains(targetDir, {
|
||||
"healthreport.sqlite": contents_sqlite,
|
||||
"healthreport.sqlite-wal": contents_wal,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_sqlite_healthreport_not_dir() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
let contents = "hello there\n\n";
|
||||
writeToFile(srcDir, "healthreport.sqlite", contents);
|
||||
writeToFile(srcDir, "healthreport", "I'm a file but should be a directory");
|
||||
|
||||
let ok = yield promiseFHRMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true even though the directory was a file");
|
||||
// We should have only the sqlite file
|
||||
checkDirectoryContains(targetDir, {
|
||||
"healthreport.sqlite": contents,
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_sqlite_healthreport_empty() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
let contents = "hello there\n\n";
|
||||
writeToFile(srcDir, "healthreport.sqlite", contents);
|
||||
|
||||
// create an empty 'healthreport' subdir.
|
||||
let subDir = srcDir.clone();
|
||||
subDir.append("healthreport");
|
||||
subDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
|
||||
let ok = yield promiseFHRMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
// we should end up with the .sqlite file and an empty subdir in the target.
|
||||
checkDirectoryContains(targetDir, {
|
||||
"healthreport.sqlite": contents,
|
||||
"healthreport": {},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_sqlite_healthreport_contents() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
let contents = "hello there\n\n";
|
||||
writeToFile(srcDir, "healthreport.sqlite", contents);
|
||||
|
||||
// create an empty 'healthreport' subdir.
|
||||
let subDir = srcDir.clone();
|
||||
subDir.append("healthreport");
|
||||
subDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
|
||||
writeToFile(subDir, "file1", "this is file 1");
|
||||
writeToFile(subDir, "file2", "this is file 2");
|
||||
|
||||
let ok = yield promiseFHRMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
// we should end up with the .sqlite file and an empty subdir in the target.
|
||||
checkDirectoryContains(targetDir, {
|
||||
"healthreport.sqlite": contents,
|
||||
"healthreport": {
|
||||
"file1": "this is file 1",
|
||||
"file2": "this is file 2",
|
||||
"datareporting": {
|
||||
"state.json": stateContent,
|
||||
"session-state.json": sessionStateContent,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_fallback_fhr_state() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// Test that we fall back to migrating FHR state if the datareporting
|
||||
// state file does not exist.
|
||||
let stateContent = JSON.stringify({
|
||||
clientId: "68d5474e-19dc-45c1-8e9a-81fca592707c",
|
||||
});
|
||||
let subDir = createSubDir(srcDir, "healthreport");
|
||||
writeToFile(subDir, "state.json", stateContent);
|
||||
|
||||
// Perform migration.
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
checkDirectoryContains(targetDir, {
|
||||
"healthreport": {
|
||||
"state.json": stateContent,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
add_task(function* test_datareporting_not_dir() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
writeToFile(srcDir, "datareporting", "I'm a file but should be a directory");
|
||||
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true even though the directory was a file");
|
||||
|
||||
checkDirectoryContains(targetDir, {});
|
||||
});
|
||||
|
||||
add_task(function* test_datareporting_empty() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// create an empty 'datareporting' subdir.
|
||||
let subDir = srcDir.clone();
|
||||
subDir.append("datareporting");
|
||||
subDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
|
||||
let ok = yield promiseFHRMigrator(srcDir, targetDir);
|
||||
// Migrate with an empty 'datareporting' subdir.
|
||||
let subDir = createSubDir(srcDir, "datareporting");
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
// we should end up with nothing at all in the destination - state.json was
|
||||
// missing so we didn't even create the target dir.
|
||||
// We should end up with no migrated files.
|
||||
checkDirectoryContains(targetDir, {
|
||||
"datareporting": {},
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_healthreport_empty() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// Migrate with no 'datareporting' and an empty 'healthreport' subdir.
|
||||
let subDir = createSubDir(srcDir, "healthreport");
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
// We should end up with no migrated files.
|
||||
checkDirectoryContains(targetDir, {});
|
||||
});
|
||||
|
||||
add_task(function* test_datareporting_many() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// create an empty 'datareporting' subdir.
|
||||
let subDir = srcDir.clone();
|
||||
subDir.append("datareporting");
|
||||
subDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
|
||||
|
||||
writeToFile(subDir, "state.json", "should be copied");
|
||||
// Create some datareporting files.
|
||||
let subDir = createSubDir(srcDir, "datareporting");
|
||||
let shouldBeCopied = "should be copied";
|
||||
writeToFile(subDir, "state.json", shouldBeCopied);
|
||||
writeToFile(subDir, "session-state.json", shouldBeCopied);
|
||||
writeToFile(subDir, "something.else", "should not");
|
||||
createSubDir(subDir, "emptyDir");
|
||||
|
||||
let ok = yield promiseFHRMigrator(srcDir, targetDir);
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
checkDirectoryContains(targetDir, {
|
||||
"datareporting" : {
|
||||
"state.json": "should be copied",
|
||||
"state.json": shouldBeCopied,
|
||||
"session-state.json": shouldBeCopied,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_no_session_state() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// Check that migration still works properly if we only have state.json.
|
||||
let subDir = createSubDir(srcDir, "datareporting");
|
||||
let stateContent = "abcd984";
|
||||
writeToFile(subDir, "state.json", stateContent);
|
||||
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
checkDirectoryContains(targetDir, {
|
||||
"datareporting" : {
|
||||
"state.json": stateContent,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
add_task(function* test_no_state() {
|
||||
let [srcDir, targetDir] = getTestDirs();
|
||||
|
||||
// Check that migration still works properly if we only have session-state.json.
|
||||
let subDir = createSubDir(srcDir, "datareporting");
|
||||
let sessionStateContent = "abcd512";
|
||||
writeToFile(subDir, "session-state.json", sessionStateContent);
|
||||
|
||||
let ok = yield promiseTelemetryMigrator(srcDir, targetDir);
|
||||
Assert.ok(ok, "callback should have been true");
|
||||
|
||||
checkDirectoryContains(targetDir, {
|
||||
"datareporting" : {
|
||||
"session-state.json": sessionStateContent,
|
||||
}
|
||||
});
|
||||
});
|
|
@ -12,7 +12,7 @@ skip-if = os != "mac" # Relies on ULibDir
|
|||
[test_Chrome_passwords.js]
|
||||
skip-if = os != "win"
|
||||
[test_Edge_availability.js]
|
||||
[test_fx_fhr.js]
|
||||
[test_fx_telemetry.js]
|
||||
[test_IE_bookmarks.js]
|
||||
skip-if = os != "win"
|
||||
[test_IE_cookies.js]
|
||||
|
|
|
@ -14,37 +14,28 @@ support-files =
|
|||
webapi.html
|
||||
|
||||
[browser_426329.js]
|
||||
skip-if = e10s # Bug ?????? - Test uses load event and checks event.target.
|
||||
[browser_483086.js]
|
||||
[browser_addEngine.js]
|
||||
[browser_amazon.js]
|
||||
[browser_amazon_behavior.js]
|
||||
skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}]
|
||||
[browser_bing.js]
|
||||
[browser_bing_behavior.js]
|
||||
skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}]
|
||||
[browser_contextmenu.js]
|
||||
skip-if = e10s # Bug ?????? - Test touches content (content.window.getSelection().QueryInterface(Ci.nsISelectionPrivate)....)
|
||||
[browser_eBay.js]
|
||||
[browser_eBay_behavior.js]
|
||||
skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}]
|
||||
[browser_google.js]
|
||||
[browser_google_behavior.js]
|
||||
skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}]
|
||||
[browser_healthreport.js]
|
||||
[browser_hiddenOneOffs_cleanup.js]
|
||||
[browser_hiddenOneOffs_diacritics.js]
|
||||
[browser_oneOffHeader.js]
|
||||
skip-if = e10s # bug ?????? - Test alters the searchbar textbox value which causes issues with other tests in e10s.
|
||||
[browser_private_search_perwindowpb.js]
|
||||
skip-if = e10s # Bug ?????? - Test uses load event and checks event.target.
|
||||
[browser_yahoo.js]
|
||||
[browser_yahoo_behavior.js]
|
||||
skip-if = e10s # Bug ?????? - some issue with progress listeners [JavaScript Error: "req.originalURI is null" {file: "chrome://mochitests/content/browser/browser/components/search/test/browser_bing_behavior.js" line: 127}]
|
||||
[browser_abouthome_behavior.js]
|
||||
skip-if = e10s || true # Bug ??????, Bug 1100301 - leaks windows until shutdown when --run-by-dir
|
||||
skip-if = true # Bug ??????, Bug 1100301 - leaks windows until shutdown when --run-by-dir
|
||||
[browser_searchbar_openpopup.js]
|
||||
skip-if = os == "linux" || e10s # Linux has different focus behaviours and e10s seems to have timing issues.
|
||||
skip-if = os == "linux" # Linux has different focus behaviours.
|
||||
[browser_searchbar_keyboard_navigation.js]
|
||||
[browser_searchbar_smallpanel_keyboard_navigation.js]
|
||||
[browser_webapi.js]
|
||||
|
|
|
@ -1,14 +1,5 @@
|
|||
// Instead of loading ChromeUtils.js into the test scope in browser-test.js for all tests,
|
||||
// we only need ChromeUtils.js for a few files which is why we are using loadSubScript.
|
||||
var ChromeUtils = {};
|
||||
this._scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
|
||||
getService(Ci.mozIJSSubScriptLoader);
|
||||
this._scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FormHistory",
|
||||
"resource://gre/modules/FormHistory.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
|
||||
function expectedURL(aSearchTerms) {
|
||||
const ENGINE_HTML_BASE = "http://mochi.test:8888/browser/browser/components/search/test/test.html";
|
||||
|
@ -53,73 +44,71 @@ function getMenuEntries() {
|
|||
return entries;
|
||||
}
|
||||
|
||||
function* countEntries(name, value) {
|
||||
let deferred = Promise.defer();
|
||||
let count = 0;
|
||||
let obj = name && value ? {fieldname: name, value: value} : {};
|
||||
FormHistory.count(obj,
|
||||
{ handleResult: function(result) { count = result; },
|
||||
handleError: function(error) { throw error; },
|
||||
handleCompletion: function(reason) {
|
||||
if (!reason) {
|
||||
deferred.resolve(count);
|
||||
function countEntries(name, value) {
|
||||
return new Promise(resolve => {
|
||||
let count = 0;
|
||||
let obj = name && value ? {fieldname: name, value: value} : {};
|
||||
FormHistory.count(obj,
|
||||
{ handleResult: function(result) { count = result; },
|
||||
handleError: function(error) { throw error; },
|
||||
handleCompletion: function(reason) {
|
||||
if (!reason) {
|
||||
resolve(count);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return deferred.promise;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var searchBar;
|
||||
var searchButton;
|
||||
var searchEntries = ["test"];
|
||||
function* promiseSetEngine() {
|
||||
let deferred = Promise.defer();
|
||||
var ss = Services.search;
|
||||
function promiseSetEngine() {
|
||||
return new Promise(resolve => {
|
||||
var ss = Services.search;
|
||||
|
||||
function observer(aSub, aTopic, aData) {
|
||||
switch (aData) {
|
||||
case "engine-added":
|
||||
var engine = ss.getEngineByName("Bug 426329");
|
||||
ok(engine, "Engine was added.");
|
||||
ss.currentEngine = engine;
|
||||
break;
|
||||
case "engine-current":
|
||||
ok(ss.currentEngine.name == "Bug 426329", "currentEngine set");
|
||||
searchBar = BrowserSearch.searchBar;
|
||||
searchButton = document.getAnonymousElementByAttribute(searchBar,
|
||||
"anonid", "search-go-button");
|
||||
ok(searchButton, "got search-go-button");
|
||||
searchBar.value = "test";
|
||||
function observer(aSub, aTopic, aData) {
|
||||
switch (aData) {
|
||||
case "engine-added":
|
||||
var engine = ss.getEngineByName("Bug 426329");
|
||||
ok(engine, "Engine was added.");
|
||||
ss.currentEngine = engine;
|
||||
break;
|
||||
case "engine-current":
|
||||
ok(ss.currentEngine.name == "Bug 426329", "currentEngine set");
|
||||
searchBar = BrowserSearch.searchBar;
|
||||
searchButton = document.getAnonymousElementByAttribute(searchBar,
|
||||
"anonid", "search-go-button");
|
||||
ok(searchButton, "got search-go-button");
|
||||
searchBar.value = "test";
|
||||
|
||||
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||
deferred.resolve();
|
||||
break;
|
||||
}
|
||||
};
|
||||
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||
resolve();
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/426329.xml",
|
||||
null, "data:image/x-icon,%00", false);
|
||||
|
||||
return deferred.promise;
|
||||
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
ss.addEngine("http://mochi.test:8888/browser/browser/components/search/test/426329.xml",
|
||||
null, "data:image/x-icon,%00", false);
|
||||
});
|
||||
}
|
||||
|
||||
function* promiseRemoveEngine() {
|
||||
let deferred = Promise.defer();
|
||||
var ss = Services.search;
|
||||
function promiseRemoveEngine() {
|
||||
return new Promise(resolve => {
|
||||
var ss = Services.search;
|
||||
|
||||
function observer(aSub, aTopic, aData) {
|
||||
if (aData == "engine-removed") {
|
||||
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||
deferred.resolve();
|
||||
}
|
||||
};
|
||||
function observer(aSub, aTopic, aData) {
|
||||
if (aData == "engine-removed") {
|
||||
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||
resolve();
|
||||
}
|
||||
};
|
||||
|
||||
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
var engine = ss.getEngineByName("Bug 426329");
|
||||
ss.removeEngine(engine);
|
||||
|
||||
return deferred.promise;
|
||||
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
var engine = ss.getEngineByName("Bug 426329");
|
||||
ss.removeEngine(engine);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -130,22 +119,15 @@ function* prepareTest() {
|
|||
preTabNo = gBrowser.tabs.length;
|
||||
searchBar = BrowserSearch.searchBar;
|
||||
|
||||
let windowFocused = Promise.defer();
|
||||
SimpleTest.waitForFocus(windowFocused.resolve, window);
|
||||
yield windowFocused.promise;
|
||||
yield SimpleTest.promiseFocus();
|
||||
|
||||
let deferred = Promise.defer();
|
||||
if (document.activeElement != searchBar) {
|
||||
searchBar.addEventListener("focus", function onFocus() {
|
||||
searchBar.removeEventListener("focus", onFocus);
|
||||
deferred.resolve();
|
||||
});
|
||||
gURLBar.focus();
|
||||
searchBar.focus();
|
||||
} else {
|
||||
deferred.resolve();
|
||||
}
|
||||
return deferred.promise;
|
||||
if (document.activeElement == searchBar)
|
||||
return;
|
||||
|
||||
let focusPromise = BrowserTestUtils.waitForEvent(searchBar, "focus");
|
||||
gURLBar.focus();
|
||||
searchBar.focus();
|
||||
yield focusPromise;
|
||||
}
|
||||
|
||||
add_task(function* testSetupEngine() {
|
||||
|
@ -153,90 +135,81 @@ add_task(function* testSetupEngine() {
|
|||
});
|
||||
|
||||
add_task(function* testReturn() {
|
||||
yield prepareTest();
|
||||
yield* prepareTest();
|
||||
EventUtils.synthesizeKey("VK_RETURN", {});
|
||||
let event = yield promiseOnLoad();
|
||||
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
|
||||
is(gBrowser.tabs.length, preTabNo, "Return key did not open new tab");
|
||||
is(event.originalTarget, preSelectedBrowser.contentDocument,
|
||||
"Return key loaded results in current tab");
|
||||
is(event.originalTarget.URL, expectedURL(searchBar.value), "testReturn opened correct search page");
|
||||
is(gBrowser.currentURI.spec, expectedURL(searchBar.value), "testReturn opened correct search page");
|
||||
});
|
||||
|
||||
add_task(function* testAltReturn() {
|
||||
yield prepareTest();
|
||||
EventUtils.synthesizeKey("VK_RETURN", { altKey: true });
|
||||
let event = yield promiseOnLoad();
|
||||
yield* prepareTest();
|
||||
yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
|
||||
EventUtils.synthesizeKey("VK_RETURN", { altKey: true });
|
||||
});
|
||||
|
||||
is(gBrowser.tabs.length, preTabNo + 1, "Alt+Return key added new tab");
|
||||
isnot(event.originalTarget, preSelectedBrowser.contentDocument,
|
||||
"Alt+Return key loaded results in new tab");
|
||||
is(event.originalTarget, gBrowser.contentDocument,
|
||||
"Alt+Return key loaded results in foreground tab");
|
||||
is(event.originalTarget.URL, expectedURL(searchBar.value), "testAltReturn opened correct search page");
|
||||
is(gBrowser.currentURI.spec, expectedURL(searchBar.value), "testAltReturn opened correct search page");
|
||||
});
|
||||
|
||||
//Shift key has no effect for now, so skip it
|
||||
add_task(function* testShiftAltReturn() {
|
||||
return;
|
||||
|
||||
yield prepareTest();
|
||||
yield* prepareTest();
|
||||
|
||||
let url = expectedURL(searchBar.value);
|
||||
|
||||
let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, url);
|
||||
EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true, altKey: true });
|
||||
let event = yield promiseOnLoad();
|
||||
let newTab = yield newTabPromise;
|
||||
|
||||
is(gBrowser.tabs.length, preTabNo + 1, "Shift+Alt+Return key added new tab");
|
||||
isnot(event.originalTarget, preSelectedBrowser.contentDocument,
|
||||
"Shift+Alt+Return key loaded results in new tab");
|
||||
isnot(event.originalTarget, gBrowser.contentDocument,
|
||||
"Shift+Alt+Return key loaded results in background tab");
|
||||
is(event.originalTarget.URL, expectedURL(searchBar.value), "testShiftAltReturn opened correct search page");
|
||||
is(gBrowser.currentURI.spec, url, "testShiftAltReturn opened correct search page");
|
||||
});
|
||||
|
||||
add_task(function* testLeftClick() {
|
||||
yield prepareTest();
|
||||
yield* prepareTest();
|
||||
simulateClick({ button: 0 }, searchButton);
|
||||
let event = yield promiseOnLoad();
|
||||
yield BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
is(gBrowser.tabs.length, preTabNo, "LeftClick did not open new tab");
|
||||
is(event.originalTarget, preSelectedBrowser.contentDocument,
|
||||
"LeftClick loaded results in current tab");
|
||||
is(event.originalTarget.URL, expectedURL(searchBar.value), "testLeftClick opened correct search page");
|
||||
is(gBrowser.currentURI.spec, expectedURL(searchBar.value), "testLeftClick opened correct search page");
|
||||
});
|
||||
|
||||
add_task(function* testMiddleClick() {
|
||||
yield prepareTest();
|
||||
simulateClick({ button: 1 }, searchButton);
|
||||
let event = yield promiseOnLoad();
|
||||
yield* prepareTest();
|
||||
yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
|
||||
simulateClick({ button: 1 }, searchButton);
|
||||
});
|
||||
is(gBrowser.tabs.length, preTabNo + 1, "MiddleClick added new tab");
|
||||
isnot(event.originalTarget, preSelectedBrowser.contentDocument,
|
||||
"MiddleClick loaded results in new tab");
|
||||
is(event.originalTarget, gBrowser.contentDocument,
|
||||
"MiddleClick loaded results in foreground tab");
|
||||
is(event.originalTarget.URL, expectedURL(searchBar.value), "testMiddleClick opened correct search page");
|
||||
is(gBrowser.currentURI.spec, expectedURL(searchBar.value), "testMiddleClick opened correct search page");
|
||||
});
|
||||
|
||||
add_task(function* testShiftMiddleClick() {
|
||||
yield prepareTest();
|
||||
yield* prepareTest();
|
||||
|
||||
let url = expectedURL(searchBar.value);
|
||||
|
||||
let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, url);
|
||||
simulateClick({ button: 1, shiftKey: true }, searchButton);
|
||||
let event = yield promiseOnLoad();
|
||||
let newTab = yield newTabPromise;
|
||||
|
||||
is(gBrowser.tabs.length, preTabNo + 1, "Shift+MiddleClick added new tab");
|
||||
isnot(event.originalTarget, preSelectedBrowser.contentDocument,
|
||||
"Shift+MiddleClick loaded results in new tab");
|
||||
isnot(event.originalTarget, gBrowser.contentDocument,
|
||||
"Shift+MiddleClick loaded results in background tab");
|
||||
is(event.originalTarget.URL, expectedURL(searchBar.value), "testShiftMiddleClick opened correct search page");
|
||||
is(newTab.linkedBrowser.currentURI.spec, url, "testShiftMiddleClick opened correct search page");
|
||||
});
|
||||
|
||||
add_task(function* testRightClick() {
|
||||
preTabNo = gBrowser.tabs.length;
|
||||
content.location.href = "about:blank";
|
||||
simulateClick({ button: 2 }, searchButton);
|
||||
let deferred = Promise.defer();
|
||||
setTimeout(function() {
|
||||
is(gBrowser.tabs.length, preTabNo, "RightClick did not open new tab");
|
||||
is(gBrowser.currentURI.spec, "about:blank", "RightClick did nothing");
|
||||
deferred.resolve();
|
||||
}, 5000);
|
||||
yield deferred.promise;
|
||||
gBrowser.selectedBrowser.loadURI("about:blank");
|
||||
yield new Promise(resolve => {
|
||||
setTimeout(function() {
|
||||
is(gBrowser.tabs.length, preTabNo, "RightClick did not open new tab");
|
||||
is(gBrowser.currentURI.spec, "about:blank", "RightClick did nothing");
|
||||
resolve();
|
||||
}, 5000);
|
||||
simulateClick({ button: 2 }, searchButton);
|
||||
});
|
||||
// The click in the searchbox focuses it, which opens the suggestion
|
||||
// panel. Clean up after ourselves.
|
||||
searchBar.textbox.popup.hidePopup();
|
||||
|
@ -252,7 +225,7 @@ add_task(function* testSearchHistory() {
|
|||
|
||||
add_task(function* testAutocomplete() {
|
||||
var popup = searchBar.textbox.popup;
|
||||
let popupShownPromise = promiseEvent(popup, "popupshown");
|
||||
let popupShownPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
|
||||
searchBar.textbox.showHistoryPopup();
|
||||
yield popupShownPromise;
|
||||
checkMenuEntries(searchEntries);
|
||||
|
@ -271,6 +244,6 @@ add_task(function* asyncCleanup() {
|
|||
while (gBrowser.tabs.length != 1) {
|
||||
gBrowser.removeTab(gBrowser.tabs[0], {animate: false});
|
||||
}
|
||||
content.location.href = "about:blank";
|
||||
gBrowser.selectedBrowser.loadURI("about:blank");
|
||||
yield promiseRemoveEngine();
|
||||
});
|
||||
|
|
|
@ -119,6 +119,9 @@ function test() {
|
|||
if (!(flags & docStart) || !webProgress.isTopLevel)
|
||||
return;
|
||||
|
||||
if (req.originalURI.spec == "about:blank")
|
||||
return;
|
||||
|
||||
info("received document start");
|
||||
|
||||
ok(req instanceof Ci.nsIChannel, "req is a channel");
|
||||
|
|
|
@ -136,6 +136,9 @@ function test() {
|
|||
if (!(flags & docStart) || !webProgress.isTopLevel)
|
||||
return;
|
||||
|
||||
if (req.originalURI.spec == "about:blank")
|
||||
return;
|
||||
|
||||
info("received document start");
|
||||
|
||||
ok(req instanceof Ci.nsIChannel, "req is a channel");
|
||||
|
|
|
@ -136,6 +136,9 @@ function test() {
|
|||
if (!(flags & docStart) || !webProgress.isTopLevel)
|
||||
return;
|
||||
|
||||
if (req.originalURI.spec == "about:blank")
|
||||
return;
|
||||
|
||||
info("received document start");
|
||||
|
||||
ok(req instanceof Ci.nsIChannel, "req is a channel");
|
||||
|
|
|
@ -4,9 +4,7 @@
|
|||
* Test searching for the selected text using the context menu
|
||||
*/
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
add_task(function* () {
|
||||
const ss = Services.search;
|
||||
const ENGINE_NAME = "Foo";
|
||||
var contextMenu;
|
||||
|
@ -22,90 +20,79 @@ function test() {
|
|||
resProt.setSubstitution("search-plugins",
|
||||
Services.io.newURI(url, null, null));
|
||||
|
||||
function observer(aSub, aTopic, aData) {
|
||||
switch (aData) {
|
||||
case "engine-added":
|
||||
var engine = ss.getEngineByName(ENGINE_NAME);
|
||||
ok(engine, "Engine was added.");
|
||||
ss.currentEngine = engine;
|
||||
envService.set("XPCSHELL_TEST_PROFILE_DIR", originalValue);
|
||||
resProt.setSubstitution("search-plugins", originalSubstitution);
|
||||
break;
|
||||
case "engine-current":
|
||||
is(ss.currentEngine.name, ENGINE_NAME, "currentEngine set");
|
||||
startTest();
|
||||
break;
|
||||
case "engine-removed":
|
||||
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||
finish();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
ss.addEngine("resource://search-plugins/testEngine_mozsearch.xml",
|
||||
null, "data:image/x-icon,%00", false);
|
||||
|
||||
function startTest() {
|
||||
contextMenu = document.getElementById("contentAreaContextMenu");
|
||||
ok(contextMenu, "Got context menu XUL");
|
||||
|
||||
doOnloadOnce(testContextMenu);
|
||||
let tab = gBrowser.selectedTab = gBrowser.addTab("data:text/plain;charset=utf8,test%20search");
|
||||
registerCleanupFunction(function () {
|
||||
gBrowser.removeTab(tab);
|
||||
});
|
||||
}
|
||||
|
||||
function testContextMenu() {
|
||||
function rightClickOnDocument() {
|
||||
info("rightClickOnDocument: " + content.window.location);
|
||||
waitForBrowserContextMenu(checkContextMenu);
|
||||
var clickTarget = content.document.body;
|
||||
var eventDetails = { type: "contextmenu", button: 2 };
|
||||
EventUtils.synthesizeMouseAtCenter(clickTarget, eventDetails, content);
|
||||
}
|
||||
|
||||
// check the search menu item and then perform a search
|
||||
function checkContextMenu() {
|
||||
info("checkContextMenu");
|
||||
var searchItem = contextMenu.getElementsByAttribute("id", "context-searchselect")[0];
|
||||
ok(searchItem, "Got search context menu item");
|
||||
is(searchItem.label, 'Search ' + ENGINE_NAME + ' for "test search"', "Check context menu label");
|
||||
is(searchItem.disabled, false, "Check that search context menu item is enabled");
|
||||
doOnloadOnce(checkSearchURL);
|
||||
searchItem.click();
|
||||
contextMenu.hidePopup();
|
||||
}
|
||||
|
||||
function checkSearchURL(event) {
|
||||
is(event.originalTarget.URL,
|
||||
"http://mochi.test:8888/browser/browser/components/search/test/?test=test+search&ie=utf-8&channel=contextsearch",
|
||||
"Checking context menu search URL");
|
||||
// Remove the tab opened by the search
|
||||
gBrowser.removeCurrentTab();
|
||||
ss.removeEngine(ss.currentEngine);
|
||||
}
|
||||
|
||||
var selectionListener = {
|
||||
notifySelectionChanged: function(doc, sel, reason) {
|
||||
if (reason != Ci.nsISelectionListener.SELECTALL_REASON || sel.toString() != "test search")
|
||||
return;
|
||||
info("notifySelectionChanged: Text selected");
|
||||
content.window.getSelection().QueryInterface(Ci.nsISelectionPrivate).
|
||||
removeSelectionListener(selectionListener);
|
||||
SimpleTest.executeSoon(rightClickOnDocument);
|
||||
let searchDonePromise;
|
||||
yield new Promise(resolve => {
|
||||
function observer(aSub, aTopic, aData) {
|
||||
switch (aData) {
|
||||
case "engine-added":
|
||||
var engine = ss.getEngineByName(ENGINE_NAME);
|
||||
ok(engine, "Engine was added.");
|
||||
ss.currentEngine = engine;
|
||||
envService.set("XPCSHELL_TEST_PROFILE_DIR", originalValue);
|
||||
resProt.setSubstitution("search-plugins", originalSubstitution);
|
||||
break;
|
||||
case "engine-current":
|
||||
is(ss.currentEngine.name, ENGINE_NAME, "currentEngine set");
|
||||
resolve();
|
||||
break;
|
||||
case "engine-removed":
|
||||
Services.obs.removeObserver(observer, "browser-search-engine-modified");
|
||||
if (searchDonePromise) {
|
||||
searchDonePromise();
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Delay the select all to avoid intermittent selection failures.
|
||||
setTimeout(function delaySelectAll() {
|
||||
info("delaySelectAll: " + content.window.location.toString());
|
||||
// add a listener to know when the selection takes effect
|
||||
content.window.getSelection().QueryInterface(Ci.nsISelectionPrivate).
|
||||
addSelectionListener(selectionListener);
|
||||
// select the text on the page
|
||||
goDoCommand('cmd_selectAll');
|
||||
}, 500);
|
||||
}
|
||||
}
|
||||
Services.obs.addObserver(observer, "browser-search-engine-modified", false);
|
||||
ss.addEngine("resource://search-plugins/testEngine_mozsearch.xml",
|
||||
null, "data:image/x-icon,%00", false);
|
||||
});
|
||||
|
||||
contextMenu = document.getElementById("contentAreaContextMenu");
|
||||
ok(contextMenu, "Got context menu XUL");
|
||||
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "data:text/plain;charset=utf8,test%20search");
|
||||
|
||||
yield ContentTask.spawn(tab.linkedBrowser, "", function*() {
|
||||
return new Promise(resolve => {
|
||||
content.document.addEventListener("selectionchange", function selectionChanged() {
|
||||
content.document.removeEventListener("selectionchange", selectionChanged);
|
||||
resolve();
|
||||
});
|
||||
content.document.getSelection().selectAllChildren(content.document.body);
|
||||
});
|
||||
});
|
||||
|
||||
var eventDetails = { type: "contextmenu", button: 2 };
|
||||
|
||||
let popupPromise = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
|
||||
BrowserTestUtils.synthesizeMouseAtCenter("body", eventDetails, gBrowser.selectedBrowser);
|
||||
yield popupPromise;
|
||||
|
||||
info("checkContextMenu");
|
||||
var searchItem = contextMenu.getElementsByAttribute("id", "context-searchselect")[0];
|
||||
ok(searchItem, "Got search context menu item");
|
||||
is(searchItem.label, 'Search ' + ENGINE_NAME + ' for "test search"', "Check context menu label");
|
||||
is(searchItem.disabled, false, "Check that search context menu item is enabled");
|
||||
|
||||
yield BrowserTestUtils.openNewForegroundTab(gBrowser, () => {
|
||||
searchItem.click();
|
||||
});
|
||||
|
||||
is(gBrowser.currentURI.spec,
|
||||
"http://mochi.test:8888/browser/browser/components/search/test/?test=test+search&ie=utf-8&channel=contextsearch",
|
||||
"Checking context menu search URL");
|
||||
|
||||
contextMenu.hidePopup();
|
||||
|
||||
// Remove the tab opened by the search
|
||||
gBrowser.removeCurrentTab();
|
||||
|
||||
yield new Promise(resolve => {
|
||||
searchDonePromise = resolve;
|
||||
ss.removeEngine(ss.currentEngine);
|
||||
});
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
});
|
||||
|
|
|
@ -136,6 +136,9 @@ function test() {
|
|||
if (!(flags & docStart) || !webProgress.isTopLevel)
|
||||
return;
|
||||
|
||||
if (req.originalURI.spec == "about:blank")
|
||||
return;
|
||||
|
||||
info("received document start");
|
||||
|
||||
ok(req instanceof Ci.nsIChannel, "req is a channel");
|
||||
|
|
|
@ -134,6 +134,9 @@ function test() {
|
|||
if (!(flags & docStart) || !webProgress.isTopLevel)
|
||||
return;
|
||||
|
||||
if (req.originalURI.spec == "about:blank")
|
||||
return;
|
||||
|
||||
info("received document start");
|
||||
|
||||
ok(req instanceof Ci.nsIChannel, "req is a channel");
|
||||
|
|
|
@ -2,103 +2,67 @@
|
|||
// search in a private window, and then checks in the public window
|
||||
// whether there is an autocomplete entry for the private search.
|
||||
|
||||
function test() {
|
||||
add_task(function* () {
|
||||
// Don't use about:home as the homepage for new windows
|
||||
Services.prefs.setIntPref("browser.startup.page", 0);
|
||||
registerCleanupFunction(() => Services.prefs.clearUserPref("browser.startup.page"));
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
let engineURL =
|
||||
"http://mochi.test:8888/browser/browser/components/search/test/";
|
||||
let windowsToClose = [];
|
||||
registerCleanupFunction(function() {
|
||||
let engine = Services.search.getEngineByName("Bug 426329");
|
||||
Services.search.removeEngine(engine);
|
||||
windowsToClose.forEach(function(win) {
|
||||
win.close();
|
||||
});
|
||||
});
|
||||
|
||||
function onPageLoad(aWin, aCallback) {
|
||||
aWin.gBrowser.addEventListener("DOMContentLoaded", function load(aEvent) {
|
||||
let doc = aEvent.originalTarget;
|
||||
info(doc.location.href);
|
||||
if (doc.location.href.indexOf(engineURL) != -1) {
|
||||
aWin.gBrowser.removeEventListener("DOMContentLoaded", load, false);
|
||||
aCallback();
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
function performSearch(aWin, aIsPrivate, aCallback) {
|
||||
function performSearch(aWin, aIsPrivate) {
|
||||
let searchBar = aWin.BrowserSearch.searchBar;
|
||||
ok(searchBar, "got search bar");
|
||||
onPageLoad(aWin, aCallback);
|
||||
|
||||
let loadPromise = BrowserTestUtils.browserLoaded(aWin.gBrowser.selectedBrowser);
|
||||
|
||||
searchBar.value = aIsPrivate ? "private test" : "public test";
|
||||
searchBar.focus();
|
||||
EventUtils.synthesizeKey("VK_RETURN", {}, aWin);
|
||||
|
||||
return loadPromise;
|
||||
}
|
||||
|
||||
function addEngine(aCallback) {
|
||||
let installCallback = {
|
||||
onSuccess: function (engine) {
|
||||
Services.search.currentEngine = engine;
|
||||
aCallback();
|
||||
},
|
||||
onError: function (errorCode) {
|
||||
ok(false, "failed to install engine: " + errorCode);
|
||||
}
|
||||
};
|
||||
Services.search.addEngine(engineURL + "426329.xml", null,
|
||||
"data:image/x-icon,%00", false, installCallback);
|
||||
}
|
||||
|
||||
function testOnWindow(aIsPrivate, aCallback) {
|
||||
let win = whenNewWindowLoaded({ private: aIsPrivate }, function() {
|
||||
waitForFocus(aCallback, win);
|
||||
});
|
||||
function* testOnWindow(aIsPrivate) {
|
||||
let win = yield BrowserTestUtils.openNewBrowserWindow({ private: aIsPrivate });
|
||||
yield SimpleTest.promiseFocus(win);
|
||||
windowsToClose.push(win);
|
||||
return win;
|
||||
}
|
||||
|
||||
addEngine(function() {
|
||||
testOnWindow(false, function(win) {
|
||||
performSearch(win, false, function() {
|
||||
testOnWindow(true, function(win) {
|
||||
performSearch(win, true, function() {
|
||||
testOnWindow(false, function(win) {
|
||||
checkSearchPopup(win, finish);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
yield promiseNewEngine("426329.xml", { iconURL: "data:image/x-icon,%00" });
|
||||
|
||||
function checkSearchPopup(aWin, aCallback) {
|
||||
let searchBar = aWin.BrowserSearch.searchBar;
|
||||
let newWindow = yield* testOnWindow(false);
|
||||
yield performSearch(newWindow, false);
|
||||
|
||||
newWindow = yield* testOnWindow(true);
|
||||
yield performSearch(newWindow, true);
|
||||
|
||||
newWindow = yield* testOnWindow(false);
|
||||
|
||||
let searchBar = newWindow.BrowserSearch.searchBar;
|
||||
searchBar.value = "p";
|
||||
searchBar.focus();
|
||||
|
||||
let popup = searchBar.textbox.popup;
|
||||
popup.addEventListener("popupshowing", function showing() {
|
||||
popup.removeEventListener("popupshowing", showing, false);
|
||||
|
||||
let entries = getMenuEntries(searchBar);
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
isnot(entries[i], "private test",
|
||||
"shouldn't see private autocomplete entries");
|
||||
}
|
||||
|
||||
searchBar.textbox.toggleHistoryPopup();
|
||||
searchBar.value = "";
|
||||
aCallback();
|
||||
}, false);
|
||||
|
||||
let popupPromise = BrowserTestUtils.waitForEvent(popup, "popupshown");
|
||||
searchBar.textbox.showHistoryPopup();
|
||||
}
|
||||
yield popupPromise;
|
||||
|
||||
let entries = getMenuEntries(searchBar);
|
||||
for (let i = 0; i < entries.length; i++) {
|
||||
isnot(entries[i], "private test",
|
||||
"shouldn't see private autocomplete entries");
|
||||
}
|
||||
|
||||
searchBar.textbox.toggleHistoryPopup();
|
||||
searchBar.value = "";
|
||||
|
||||
windowsToClose.forEach(function(win) {
|
||||
win.close();
|
||||
});
|
||||
});
|
||||
|
||||
function getMenuEntries(searchBar) {
|
||||
let entries = [];
|
||||
|
|
|
@ -381,20 +381,8 @@ add_task(function* test_tab_and_arrows() {
|
|||
});
|
||||
|
||||
add_task(function* test_open_search() {
|
||||
let tab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
|
||||
let rootDir = getRootDirectory(gTestPath);
|
||||
content.location = rootDir + "opensearch.html";
|
||||
|
||||
yield deferred.promise;
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, rootDir + "opensearch.html");
|
||||
|
||||
let promise = promiseEvent(searchPopup, "popupshown");
|
||||
info("Opening search panel");
|
||||
|
|
|
@ -413,7 +413,7 @@ add_no_popup_task(function* search_go_doesnt_open_popup() {
|
|||
textbox.value = "foo";
|
||||
searchbar.updateGoButtonVisibility();
|
||||
|
||||
let promise = promiseOnLoad();
|
||||
let promise = BrowserTestUtils.browserLoaded(gBrowser.selectedBrowser);
|
||||
EventUtils.synthesizeMouseAtCenter(goButton, {});
|
||||
yield promise;
|
||||
|
||||
|
|
|
@ -309,20 +309,8 @@ add_task(function* test_tab_and_arrows() {
|
|||
});
|
||||
|
||||
add_task(function* test_open_search() {
|
||||
let tab = gBrowser.addTab();
|
||||
gBrowser.selectedTab = tab;
|
||||
|
||||
let deferred = Promise.defer();
|
||||
let browser = gBrowser.selectedBrowser;
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
|
||||
let rootDir = getRootDirectory(gTestPath);
|
||||
content.location = rootDir + "opensearch.html";
|
||||
|
||||
yield deferred.promise;
|
||||
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, rootDir + "opensearch.html");
|
||||
|
||||
let promise = promiseEvent(searchPopup, "popupshown");
|
||||
info("Opening search panel");
|
||||
|
|
|
@ -136,6 +136,9 @@ function test() {
|
|||
if (!(flags & docStart) || !webProgress.isTopLevel)
|
||||
return;
|
||||
|
||||
if (req.originalURI.spec == "about:blank")
|
||||
return;
|
||||
|
||||
info("received document start");
|
||||
|
||||
ok(req instanceof Ci.nsIChannel, "req is a channel");
|
||||
|
|
|
@ -1,20 +1,6 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "Promise",
|
||||
"resource://gre/modules/Promise.jsm");
|
||||
|
||||
function whenNewWindowLoaded(aOptions, aCallback) {
|
||||
let win = OpenBrowserWindow(aOptions);
|
||||
let focused = SimpleTest.promiseFocus(win);
|
||||
let startupFinished = TestUtils.topicObserved("browser-delayed-startup-finished",
|
||||
subject => subject == win).then(() => win);
|
||||
Promise.all([focused, startupFinished])
|
||||
.then(results => executeSoon(() => aCallback(results[1])));
|
||||
|
||||
return win;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively compare two objects and check that every property of expectedObj has the same value
|
||||
* on actualObj.
|
||||
|
@ -53,64 +39,16 @@ function getLocalizedPref(aPrefName, aDefault) {
|
|||
return aDefault;
|
||||
}
|
||||
|
||||
function waitForPopupShown(aPopupId, aCallback) {
|
||||
let popup = document.getElementById(aPopupId);
|
||||
info("waitForPopupShown: got popup: " + popup.id);
|
||||
function onPopupShown() {
|
||||
info("onPopupShown");
|
||||
removePopupShownListener();
|
||||
SimpleTest.executeSoon(aCallback);
|
||||
}
|
||||
function removePopupShownListener() {
|
||||
popup.removeEventListener("popupshown", onPopupShown);
|
||||
}
|
||||
popup.addEventListener("popupshown", onPopupShown);
|
||||
registerCleanupFunction(removePopupShownListener);
|
||||
}
|
||||
|
||||
function promiseEvent(aTarget, aEventName, aPreventDefault) {
|
||||
let deferred = Promise.defer();
|
||||
aTarget.addEventListener(aEventName, function onEvent(aEvent) {
|
||||
aTarget.removeEventListener(aEventName, onEvent, true);
|
||||
function cancelEvent(event) {
|
||||
if (aPreventDefault) {
|
||||
aEvent.preventDefault();
|
||||
event.preventDefault();
|
||||
}
|
||||
deferred.resolve();
|
||||
}, true);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
function waitForBrowserContextMenu(aCallback) {
|
||||
waitForPopupShown(gBrowser.selectedBrowser.contextMenu, aCallback);
|
||||
}
|
||||
|
||||
function doOnloadOnce(aCallback) {
|
||||
function doOnloadOnceListener(aEvent) {
|
||||
info("doOnloadOnce: " + aEvent.originalTarget.location);
|
||||
removeDoOnloadOnceListener();
|
||||
SimpleTest.executeSoon(function doOnloadOnceCallback() {
|
||||
aCallback(aEvent);
|
||||
});
|
||||
return true;
|
||||
}
|
||||
function removeDoOnloadOnceListener() {
|
||||
gBrowser.removeEventListener("load", doOnloadOnceListener, true);
|
||||
}
|
||||
gBrowser.addEventListener("load", doOnloadOnceListener, true);
|
||||
registerCleanupFunction(removeDoOnloadOnceListener);
|
||||
}
|
||||
|
||||
function* promiseOnLoad() {
|
||||
return new Promise(resolve => {
|
||||
gBrowser.addEventListener("load", function onLoadListener(aEvent) {
|
||||
let cw = aEvent.target.defaultView;
|
||||
let tab = gBrowser._getTabForContentWindow(cw);
|
||||
if (tab) {
|
||||
info("onLoadListener: " + aEvent.originalTarget.location);
|
||||
gBrowser.removeEventListener("load", onLoadListener, true);
|
||||
resolve(aEvent);
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
return BrowserTestUtils.waitForEvent(aTarget, aEventName, false, cancelEvent);
|
||||
}
|
||||
|
||||
function promiseNewEngine(basename, options = {}) {
|
||||
|
@ -123,7 +61,7 @@ function promiseNewEngine(basename, options = {}) {
|
|||
onInitComplete: function() {
|
||||
let url = getRootDirectory(gTestPath) + basename;
|
||||
let current = Services.search.currentEngine;
|
||||
Services.search.addEngine(url, null, "", false, {
|
||||
Services.search.addEngine(url, null, options.iconURL || "", false, {
|
||||
onSuccess: function (engine) {
|
||||
info("Search engine added: " + basename);
|
||||
if (setAsCurrent) {
|
||||
|
|
|
@ -11,15 +11,20 @@ function retrieveUserContextId(browser) {
|
|||
|
||||
add_task(function() {
|
||||
for (let i = 0; i < 3; ++i) {
|
||||
let tab = gBrowser.addTab("about:blank");
|
||||
let tab = gBrowser.addTab("http://example.com/", {userContextId: i});
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
yield promiseBrowserLoaded(browser);
|
||||
yield promiseTabState(tab, { userContextId: i, entries: [{ url: "http://example.com/" }] });
|
||||
|
||||
let userContextId = yield retrieveUserContextId(browser);
|
||||
let tab2 = gBrowser.duplicateTab(tab);
|
||||
let browser2 = tab2.linkedBrowser;
|
||||
yield promiseTabRestored(tab2)
|
||||
|
||||
let userContextId = yield retrieveUserContextId(browser2);
|
||||
is(userContextId, i, "The docShell has the correct userContextId");
|
||||
|
||||
yield promiseRemoveTab(tab);
|
||||
yield promiseRemoveTab(tab2);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
<!ENTITY safeb.palm.decline.label "Ignore this warning">
|
||||
<!ENTITY safeb.palm.notforgery.label2 "This isn't a web forgery…">
|
||||
<!ENTITY safeb.palm.reportPage.label "Why was this page blocked?">
|
||||
<!ENTITY safeb.palm.whyForbidden.label "Why was this page blocked?">
|
||||
|
||||
<!ENTITY safeb.blocked.malwarePage.title "Reported Attack Page!">
|
||||
<!-- Localization note (safeb.blocked.malwarePage.shortDesc) - Please don't translate the contents of the <span id="malware_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
|
||||
|
@ -22,7 +23,7 @@
|
|||
<!ENTITY safeb.blocked.phishingPage.shortDesc "This web page at <span id='phishing_sitename'/> has been reported as a web forgery and has been blocked based on your security preferences.">
|
||||
<!ENTITY safeb.blocked.phishingPage.longDesc "<p>Web forgeries are designed to trick you into revealing personal or financial information by imitating sources you may trust.</p><p>Entering any information on this web page may result in identity theft or other fraud.</p>">
|
||||
|
||||
<!ENTITY safeb.blocked.forbiddenPage.title "Forbidden Site">
|
||||
<!-- Localization note (safeb.blocked.forbiddenPage.shortDesc) - Please don't translate the contents of the <span id="forbidden_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
|
||||
<!ENTITY safeb.blocked.forbiddenPage.shortDesc "This Web page at <span id='forbidden_sitename'/> has been blocked based on your browser configuration.">
|
||||
<!ENTITY safeb.blocked.forbiddenPage.title2 "Blocked Site">
|
||||
<!-- Localization note (safeb.blocked.forbiddenPage.shortDesc2) - Please don't translate the contents of the <span id="forbidden_sitename"/> tag. It will be replaced at runtime with a domain name (e.g. www.badsite.com) -->
|
||||
<!ENTITY safeb.blocked.forbiddenPage.shortDesc2 "The Web page at <span id='forbidden_sitename'/> has been blocked by your admin profile.">
|
||||
|
||||
|
|
|
@ -96,9 +96,6 @@ const MIN_VISIBLE_HISTORY_TILES = 8;
|
|||
// The max number of visible (not blocked) history tiles to test for inadjacency
|
||||
const MAX_VISIBLE_HISTORY_TILES = 15;
|
||||
|
||||
// Divide frecency by this amount for pings
|
||||
const PING_SCORE_DIVISOR = 10000;
|
||||
|
||||
// Allowed ping actions remotely stored as columns: case-insensitive [a-z0-9_]
|
||||
const PING_ACTIONS = ["block", "click", "pin", "sponsored", "sponsored_link", "unpin", "view"];
|
||||
|
||||
|
@ -566,50 +563,13 @@ var DirectoryLinksProvider = {
|
|||
}
|
||||
catch (ex) {}
|
||||
|
||||
// Only send pings when enhancing tiles with an endpoint and valid action
|
||||
// Bug 1240245 - We no longer send pings, but frequency capping and fetching
|
||||
// tests depend on the following actions, so references to PING remain.
|
||||
let invalidAction = PING_ACTIONS.indexOf(action) == -1;
|
||||
if (!newtabEnhanced || pingEndPoint == "" || invalidAction) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
let actionIndex;
|
||||
let data = {
|
||||
locale: this.locale,
|
||||
tiles: sites.reduce((tiles, site, pos) => {
|
||||
// Only add data for non-empty tiles
|
||||
if (site) {
|
||||
// Remember which tiles data triggered the action
|
||||
let {link} = site;
|
||||
let tilesIndex = tiles.length;
|
||||
if (triggeringSiteIndex == pos) {
|
||||
actionIndex = tilesIndex;
|
||||
}
|
||||
|
||||
// Make the payload in a way so keys can be excluded when stringified
|
||||
let id = link.directoryId;
|
||||
tiles.push({
|
||||
id: id || site.enhancedId,
|
||||
pin: site.isPinned() ? 1 : undefined,
|
||||
pos: pos != tilesIndex ? pos : undefined,
|
||||
past_impressions: pos == triggeringSiteIndex ? pastImpressions : undefined,
|
||||
score: Math.round(link.frecency / PING_SCORE_DIVISOR) || undefined,
|
||||
url: site.enhancedId && "",
|
||||
});
|
||||
}
|
||||
return tiles;
|
||||
}, []),
|
||||
};
|
||||
|
||||
// Provide a direct index to the tile triggering the action
|
||||
if (actionIndex !== undefined) {
|
||||
data[action] = actionIndex;
|
||||
}
|
||||
|
||||
// Package the data to be sent with the ping
|
||||
let ping = this._newXHR();
|
||||
ping.open("POST", pingEndPoint + (action == "view" ? "view" : "click"));
|
||||
ping.send(JSON.stringify(data));
|
||||
|
||||
return Task.spawn(function* () {
|
||||
// since we updated views/clicks we need write _frequencyCaps to disk
|
||||
yield this._writeFrequencyCapFile();
|
||||
|
|
|
@ -371,8 +371,6 @@ function TabWindow(win) {
|
|||
this.win = win;
|
||||
this.tabbrowser = win.gBrowser;
|
||||
|
||||
this.cacheDims();
|
||||
|
||||
this.previews = new Map();
|
||||
|
||||
for (let i = 0; i < this.tabEvents.length; i++)
|
||||
|
@ -394,6 +392,8 @@ function TabWindow(win) {
|
|||
|
||||
TabWindow.prototype = {
|
||||
_enabled: false,
|
||||
_cachedWidth: 0,
|
||||
_cachedHeight: 0,
|
||||
tabEvents: ["TabOpen", "TabClose", "TabSelect", "TabMove"],
|
||||
winEvents: ["resize"],
|
||||
|
||||
|
@ -404,8 +404,8 @@ TabWindow.prototype = {
|
|||
|
||||
this.tabbrowser.removeTabsProgressListener(this);
|
||||
|
||||
for (let i = 0; i < this.winEvents.length; i++)
|
||||
this.win.removeEventListener(this.winEvents[i], this, false);
|
||||
for (let i = 0; i < this.winEvents.length; i++)
|
||||
this.win.removeEventListener(this.winEvents[i], this, false);
|
||||
|
||||
for (let i = 0; i < this.tabEvents.length; i++)
|
||||
this.tabbrowser.tabContainer.removeEventListener(this.tabEvents[i], this, false);
|
||||
|
|
|
@ -716,113 +716,6 @@ add_task(function* test_frequencyCappedSites_click() {
|
|||
Services.prefs.setCharPref(kPingUrlPref, kPingUrl);
|
||||
});
|
||||
|
||||
add_task(function* test_reportSitesAction() {
|
||||
yield DirectoryLinksProvider.init();
|
||||
let deferred, expectedPath, expectedPost;
|
||||
let done = false;
|
||||
server.registerPrefixHandler(kPingPath, (aRequest, aResponse) => {
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
|
||||
do_check_eq(aRequest.path, expectedPath);
|
||||
|
||||
let bodyStream = new BinaryInputStream(aRequest.bodyInputStream);
|
||||
let bodyObject = JSON.parse(NetUtil.readInputStreamToString(bodyStream, bodyStream.available()));
|
||||
isIdentical(bodyObject, expectedPost);
|
||||
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
function sendPingAndTest(path, action, index) {
|
||||
deferred = Promise.defer();
|
||||
expectedPath = kPingPath + path;
|
||||
DirectoryLinksProvider.reportSitesAction(sites, action, index);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Start with a single pinned link at position 3
|
||||
let sites = [,,{
|
||||
isPinned: _ => true,
|
||||
link: {
|
||||
directoryId: 1,
|
||||
frecency: 30000,
|
||||
url: "http://directory1/",
|
||||
},
|
||||
}];
|
||||
|
||||
// Make sure we get the click ping for the directory link with fields we want
|
||||
// and unwanted fields removed by stringify/parse
|
||||
expectedPost = JSON.parse(JSON.stringify({
|
||||
click: 0,
|
||||
locale: "en-US",
|
||||
tiles: [{
|
||||
id: 1,
|
||||
pin: 1,
|
||||
pos: 2,
|
||||
score: 3,
|
||||
url: undefined,
|
||||
}],
|
||||
}));
|
||||
yield sendPingAndTest("click", "click", 2);
|
||||
|
||||
// Try a pin click ping
|
||||
delete expectedPost.click;
|
||||
expectedPost.pin = 0;
|
||||
yield sendPingAndTest("click", "pin", 2);
|
||||
|
||||
// Try a block click ping
|
||||
delete expectedPost.pin;
|
||||
expectedPost.block = 0;
|
||||
yield sendPingAndTest("click", "block", 2);
|
||||
|
||||
// A view ping has no actions
|
||||
delete expectedPost.block;
|
||||
expectedPost.view = 0;
|
||||
yield sendPingAndTest("view", "view", 2);
|
||||
|
||||
// Remove the identifier that makes it a directory link so just plain history
|
||||
delete sites[2].link.directoryId;
|
||||
delete expectedPost.tiles[0].id;
|
||||
yield sendPingAndTest("view", "view", 2);
|
||||
|
||||
// Add directory link at position 0
|
||||
sites[0] = {
|
||||
isPinned: _ => false,
|
||||
link: {
|
||||
directoryId: 1234,
|
||||
frecency: 1000,
|
||||
url: "http://directory/",
|
||||
}
|
||||
};
|
||||
expectedPost.tiles.unshift(JSON.parse(JSON.stringify({
|
||||
id: 1234,
|
||||
pin: undefined,
|
||||
pos: undefined,
|
||||
score: undefined,
|
||||
url: undefined,
|
||||
})));
|
||||
expectedPost.view = 1;
|
||||
yield sendPingAndTest("view", "view", 2);
|
||||
|
||||
// Make the history tile enhanced so it reports both id and url
|
||||
sites[2].enhancedId = "id from enhanced";
|
||||
expectedPost.tiles[1].id = "id from enhanced";
|
||||
expectedPost.tiles[1].url = "";
|
||||
yield sendPingAndTest("view", "view", 2);
|
||||
|
||||
// Click the 0th site / 0th tile
|
||||
delete expectedPost.view;
|
||||
expectedPost.click = 0;
|
||||
yield sendPingAndTest("click", "click", 0);
|
||||
|
||||
// Click the 2th site / 1th tile
|
||||
expectedPost.click = 1;
|
||||
yield sendPingAndTest("click", "click", 2);
|
||||
|
||||
done = true;
|
||||
});
|
||||
|
||||
add_task(function* test_fetchAndCacheLinks_local() {
|
||||
yield DirectoryLinksProvider.init();
|
||||
yield cleanJsonFile();
|
||||
|
@ -1901,110 +1794,6 @@ add_task(function* test_inadjecentSites() {
|
|||
yield promiseCleanDirectoryLinksProvider();
|
||||
});
|
||||
|
||||
add_task(function* test_reportPastImpressions() {
|
||||
let origIsTopPlacesSite = NewTabUtils.isTopPlacesSite;
|
||||
NewTabUtils.isTopPlacesSite = () => true;
|
||||
let origCurrentTopSiteCount = DirectoryLinksProvider._getCurrentTopSiteCount;
|
||||
DirectoryLinksProvider._getCurrentTopSiteCount = () => 8;
|
||||
|
||||
let testUrl = "http://frequency.capped/link";
|
||||
let targets = ["top.site.com"];
|
||||
let data = {
|
||||
suggested: [{
|
||||
type: "affiliate",
|
||||
frecent_sites: targets,
|
||||
url: testUrl,
|
||||
adgroup_name: "Test"
|
||||
}]
|
||||
};
|
||||
let dataURI = "data:application/json," + JSON.stringify(data);
|
||||
yield promiseSetupDirectoryLinksProvider({linksURL: dataURI});
|
||||
|
||||
// make DirectoryLinksProvider load json
|
||||
let loadPromise = Promise.defer();
|
||||
DirectoryLinksProvider.getLinks(_ => {loadPromise.resolve();});
|
||||
yield loadPromise.promise;
|
||||
|
||||
// setup ping handler
|
||||
let deferred, expectedPath, expectedAction, expectedImpressions;
|
||||
let done = false;
|
||||
server.registerPrefixHandler(kPingPath, (aRequest, aResponse) => {
|
||||
if (done) {
|
||||
return;
|
||||
}
|
||||
|
||||
do_check_eq(aRequest.path, expectedPath);
|
||||
|
||||
let bodyStream = new BinaryInputStream(aRequest.bodyInputStream);
|
||||
let bodyObject = JSON.parse(NetUtil.readInputStreamToString(bodyStream, bodyStream.available()));
|
||||
let expectedActionIndex = bodyObject[expectedAction];
|
||||
if (bodyObject.unpin) {
|
||||
// unpin should not report past_impressions
|
||||
do_check_false(bodyObject.tiles[expectedActionIndex].hasOwnProperty("past_impressions"));
|
||||
}
|
||||
else if (expectedImpressions) {
|
||||
do_check_eq(bodyObject.tiles[expectedActionIndex].past_impressions.total, expectedImpressions);
|
||||
do_check_eq(bodyObject.tiles[expectedActionIndex].past_impressions.daily, expectedImpressions);
|
||||
}
|
||||
else {
|
||||
do_check_eq(expectedPath, "/ping/view");
|
||||
do_check_false(bodyObject.tiles[expectedActionIndex].hasOwnProperty("past_impressions"));
|
||||
}
|
||||
|
||||
deferred.resolve();
|
||||
});
|
||||
|
||||
// setup ping sender
|
||||
function sendPingAndTest(path, action, index) {
|
||||
deferred = Promise.defer();
|
||||
expectedPath = kPingPath + path;
|
||||
expectedAction = action;
|
||||
DirectoryLinksProvider.reportSitesAction(sites, action, index);
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
// Start with a view ping first
|
||||
let site = {
|
||||
isPinned: _ => false,
|
||||
link: {
|
||||
directoryId: 1,
|
||||
frecency: 30000,
|
||||
frecent_sites: targets,
|
||||
targetedSite: targets[0],
|
||||
url: testUrl
|
||||
}
|
||||
};
|
||||
let sites = [,
|
||||
{
|
||||
isPinned: _ => false,
|
||||
link: {type: "history", url: "https://foo.com"}
|
||||
},
|
||||
site
|
||||
];
|
||||
|
||||
yield sendPingAndTest("view", "view", 2);
|
||||
yield sendPingAndTest("view", "view", 2);
|
||||
yield sendPingAndTest("view", "view", 2);
|
||||
|
||||
expectedImpressions = DirectoryLinksProvider._frequencyCaps[testUrl].totalViews;
|
||||
do_check_eq(expectedImpressions, 3);
|
||||
|
||||
// now report pin, unpin, block and click
|
||||
sites.isPinned = _ => true;
|
||||
yield sendPingAndTest("click", "pin", 2);
|
||||
sites.isPinned = _ => false;
|
||||
yield sendPingAndTest("click", "unpin", 2);
|
||||
sites.isPinned = _ => false;
|
||||
yield sendPingAndTest("click", "click", 2);
|
||||
sites.isPinned = _ => false;
|
||||
yield sendPingAndTest("click", "block", 2);
|
||||
|
||||
// Cleanup.
|
||||
done = true;
|
||||
NewTabUtils.isTopPlacesSite = origIsTopPlacesSite;
|
||||
DirectoryLinksProvider._getCurrentTopSiteCount = origCurrentTopSiteCount;
|
||||
});
|
||||
|
||||
add_task(function* test_blockSuggestedTiles() {
|
||||
// Initial setup
|
||||
let suggestedTile = suggestedTile1;
|
||||
|
|
|
@ -26,5 +26,9 @@ DIRS += [
|
|||
if CONFIG['MAKENSISU']:
|
||||
DIRS += ['installer/windows']
|
||||
|
||||
TEST_DIRS += [
|
||||
'tools/mozscreenshots',
|
||||
]
|
||||
|
||||
DIST_SUBDIR = 'browser'
|
||||
export('DIST_SUBDIR')
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
[DEFAULT]
|
||||
subsuite = screenshots
|
||||
support-files =
|
||||
head.js
|
||||
|
||||
[browser_screenshots.js]
|
||||
tags = screenshots
|
|
@ -0,0 +1,18 @@
|
|||
/* 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";
|
||||
|
||||
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
|
||||
add_task(function* test() {
|
||||
let { TestRunner } = Cu.import("chrome://mozscreenshots/content/TestRunner.jsm", {});
|
||||
let sets = ["TabsInTitlebar", "Tabs", "WindowSize", "Toolbars", "LightweightThemes"];
|
||||
let setsEnv = env.get("MOZSCREENSHOTS_SETS");
|
||||
if (setsEnv) {
|
||||
sets = setsEnv.trim().split(",");
|
||||
}
|
||||
|
||||
yield TestRunner.start(sets);
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
/* 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";
|
||||
|
||||
const {AddonWatcher} = Cu.import("resource://gre/modules/AddonWatcher.jsm", {});
|
||||
let TestRunner;
|
||||
|
||||
function setup() {
|
||||
requestLongerTimeout(10);
|
||||
|
||||
info("Checking for mozscreenshots extension");
|
||||
AddonManager.getAddonByID("mozscreenshots@mozilla.org", function(aAddon) {
|
||||
isnot(aAddon, null, "The mozscreenshots extension should be installed");
|
||||
AddonWatcher.ignoreAddonPermanently(aAddon.id);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(setup);
|
|
@ -0,0 +1,11 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['browser.ini']
|
||||
|
||||
TEST_DIRS += [
|
||||
'mozscreenshots/extension',
|
||||
]
|
|
@ -0,0 +1,12 @@
|
|||
# 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/.
|
||||
|
||||
TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
|
||||
GENERATED_DIRS = $(TEST_EXTENSIONS_DIR)
|
||||
XPI_PKGNAME = mozscreenshots@mozilla.org
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
libs::
|
||||
(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - $(XPI_NAME)) | (cd $(TEST_EXTENSIONS_DIR) && tar -xf -)
|
|
@ -0,0 +1,175 @@
|
|||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Screenshot"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
|
||||
// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
|
||||
const PREF_LOG_LEVEL = "extensions.mozscreenshots@mozilla.org.loglevel";
|
||||
XPCOMUtils.defineLazyGetter(this, "log", () => {
|
||||
let ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
|
||||
let consoleOptions = {
|
||||
maxLogLevel: "info",
|
||||
maxLogLevelPref: PREF_LOG_LEVEL,
|
||||
prefix: "mozscreenshots",
|
||||
};
|
||||
return new ConsoleAPI(consoleOptions);
|
||||
});
|
||||
|
||||
let Screenshot = {
|
||||
_extensionPath: null,
|
||||
_path: null,
|
||||
_imagePrefix: "",
|
||||
_imageExtension: ".png",
|
||||
_screenshotFunction: null,
|
||||
|
||||
init(path, extensionPath, imagePrefix = "") {
|
||||
this._path = path;
|
||||
|
||||
let dir = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
dir.initWithPath(this._path);
|
||||
if (!dir.exists()) {
|
||||
dir.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
|
||||
}
|
||||
|
||||
this._extensionPath = extensionPath;
|
||||
this._imagePrefix = imagePrefix;
|
||||
switch (Services.appinfo.OS) {
|
||||
case "WINNT":
|
||||
this._screenshotFunction = this._screenshotWindows;
|
||||
break;
|
||||
case "Darwin":
|
||||
this._screenshotFunction = this._screenshotOSX;
|
||||
break;
|
||||
case "Linux":
|
||||
this._screenshotFunction = this._screenshotLinux;
|
||||
break;
|
||||
default:
|
||||
throw new Error("Unsupported operating system");
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
_buildImagePath(baseName) {
|
||||
return OS.Path.join(this._path, this._imagePrefix + baseName + this._imageExtension);
|
||||
},
|
||||
|
||||
// Capture the whole screen using an external application.
|
||||
captureExternal(filename) {
|
||||
let imagePath = this._buildImagePath(filename);
|
||||
return this._screenshotFunction(imagePath).then(() => {
|
||||
log.debug("saved screenshot: " + filename);
|
||||
});
|
||||
},
|
||||
|
||||
///// helpers /////
|
||||
|
||||
_screenshotWindows(filename) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let exe = Services.dirsvc.get("GreBinD", Ci.nsIFile);
|
||||
exe.append("screenshot.exe");
|
||||
if (!exe.exists()) {
|
||||
exe = this._extensionPath.QueryInterface(Ci.nsIFileURL).file;
|
||||
exe.append("lib");
|
||||
exe.append("screenshot.exe");
|
||||
}
|
||||
let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
|
||||
process.init(exe);
|
||||
|
||||
let args = [filename];
|
||||
process.runAsync(args, args.length, this._processObserver(resolve, reject));
|
||||
});
|
||||
},
|
||||
|
||||
_screenshotOSX: Task.async(function*(filename) {
|
||||
let screencapture = (windowID = null) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Get the screencapture executable
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
file.initWithPath("/usr/sbin/screencapture");
|
||||
|
||||
let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
|
||||
process.init(file);
|
||||
|
||||
// Run the process.
|
||||
let args = ["-x", "-t", "png"];
|
||||
// Darwin version number for OS X 10.6 is 10.x
|
||||
if (windowID && Services.sysinfo.getProperty("version").indexOf("10.") !== 0) {
|
||||
// Capture only that window on 10.7+
|
||||
args.push("-l");
|
||||
args.push(windowID);
|
||||
}
|
||||
args.push(filename);
|
||||
process.runAsync(args, args.length, this._processObserver(resolve, reject));
|
||||
});
|
||||
};
|
||||
|
||||
function readWindowID() {
|
||||
let decoder = new TextDecoder();
|
||||
let promise = OS.File.read("/tmp/mozscreenshots-windowid");
|
||||
return promise.then(function onSuccess(array) {
|
||||
return decoder.decode(array);
|
||||
});
|
||||
}
|
||||
|
||||
let promiseWindowID = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
// Get the window ID of the application (assuming its front-most)
|
||||
let osascript = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
||||
osascript.initWithPath("/bin/bash");
|
||||
|
||||
let osascriptP = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
|
||||
osascriptP.init(osascript);
|
||||
let osaArgs = ["-c", "/usr/bin/osascript -e 'tell application (path to frontmost application as text) to set winID to id of window 1' > /tmp/mozscreenshots-windowid"];
|
||||
osascriptP.runAsync(osaArgs, osaArgs.length, this._processObserver(resolve, reject));
|
||||
});
|
||||
};
|
||||
|
||||
yield promiseWindowID();
|
||||
let windowID = yield readWindowID();
|
||||
yield screencapture(windowID);
|
||||
}),
|
||||
|
||||
_screenshotLinux(filename) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let file = Services.dirsvc.get("GreBinD", Ci.nsIFile);
|
||||
file.append("screentopng");
|
||||
let process = Cc["@mozilla.org/process/util;1"].createInstance(Ci.nsIProcess);
|
||||
process.init(file);
|
||||
|
||||
let args = [filename];
|
||||
process.runAsync(args, args.length, this._processObserver(resolve, reject));
|
||||
});
|
||||
},
|
||||
|
||||
_processObserver(resolve, reject) {
|
||||
return {
|
||||
observe(subject, topic, data) {
|
||||
switch (topic) {
|
||||
case "process-finished":
|
||||
try {
|
||||
// Wait 1s after process to resolve
|
||||
setTimeout(resolve, 1000);
|
||||
} catch (ex) {
|
||||
reject(ex);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
reject(topic);
|
||||
break;
|
||||
};
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
|
@ -0,0 +1,256 @@
|
|||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["TestRunner"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
const defaultSetNames = ["TabsInTitlebar", "Tabs", "WindowSize", "Toolbars", "LightweightThemes"];
|
||||
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
|
||||
Cu.import("resource://gre/modules/FileUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/osfile.jsm");
|
||||
Cu.import("chrome://mozscreenshots/content/Screenshot.jsm");
|
||||
|
||||
// Create a new instance of the ConsoleAPI so we can control the maxLogLevel with a pref.
|
||||
// See LOG_LEVELS in Console.jsm. Common examples: "All", "Info", "Warn", & "Error".
|
||||
const PREF_LOG_LEVEL = "extensions.mozscreenshots@mozilla.org.loglevel";
|
||||
XPCOMUtils.defineLazyGetter(this, "log", () => {
|
||||
let ConsoleAPI = Cu.import("resource://gre/modules/Console.jsm", {}).ConsoleAPI;
|
||||
let consoleOptions = {
|
||||
maxLogLevel: "info",
|
||||
maxLogLevelPref: PREF_LOG_LEVEL,
|
||||
prefix: "mozscreenshots",
|
||||
};
|
||||
return new ConsoleAPI(consoleOptions);
|
||||
});
|
||||
|
||||
this.TestRunner = {
|
||||
combos: null,
|
||||
completedCombos: 0,
|
||||
currentComboIndex: 0,
|
||||
_lastCombo: null,
|
||||
_libDir: null,
|
||||
|
||||
init(extensionPath) {
|
||||
let subDirs = ["mozscreenshots",
|
||||
(new Date()).toISOString().replace(/:/g, "-") + "_" + Services.appinfo.OS];
|
||||
let screenshotPath = FileUtils.getFile("TmpD", subDirs).path;
|
||||
|
||||
const MOZ_UPLOAD_DIR = env.get("MOZ_UPLOAD_DIR");
|
||||
if (MOZ_UPLOAD_DIR) {
|
||||
screenshotPath = MOZ_UPLOAD_DIR;
|
||||
}
|
||||
|
||||
log.info("Saving screenshots to:", screenshotPath);
|
||||
log.debug("TestRunner.init");
|
||||
|
||||
let screenshotPrefix = Services.appinfo.appBuildID + "_";
|
||||
Screenshot.init(screenshotPath, extensionPath, screenshotPrefix);
|
||||
this._libDir = extensionPath.QueryInterface(Ci.nsIFileURL).file.clone();
|
||||
this._libDir.append("chrome");
|
||||
this._libDir.append("mozscreenshots");
|
||||
this._libDir.append("lib");
|
||||
|
||||
// Setup some prefs
|
||||
Services.prefs.setCharPref("browser.aboutHomeSnippets.updateUrl", "data:");
|
||||
Services.prefs.setCharPref("extensions.ui.lastCategory", "addons://list/extension");
|
||||
// Don't let the caret blink since it causes false positives for image diffs
|
||||
Services.prefs.setIntPref("ui.caretBlinkTime", -1);
|
||||
},
|
||||
|
||||
/**
|
||||
* Load specified sets, execute all combinations of them, and capture screenshots.
|
||||
*/
|
||||
start(setNames = null) {
|
||||
setNames = setNames || defaultSetNames;
|
||||
let sets = this.loadSets(setNames);
|
||||
|
||||
log.info(sets.length + " sets:", setNames);
|
||||
this.combos = new LazyProduct(sets);
|
||||
log.info(this.combos.length + " combinations");
|
||||
|
||||
this.currentComboIndex = this.completedCombos = 0;
|
||||
this._lastCombo = null;
|
||||
|
||||
return Task.spawn(function* doStart() {
|
||||
for (let i = 0; i < this.combos.length;
|
||||
i++){
|
||||
this.currentComboIndex = i;
|
||||
yield* this._performCombo(this.combos.item(this.currentComboIndex));
|
||||
}
|
||||
|
||||
log.info("Done: Completed " + this.completedCombos + " out of " +
|
||||
this.combos.length + " configurations.");
|
||||
this.cleanup();
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
/**
|
||||
* Load sets of configurations from JSMs.
|
||||
* @param {String[]} setNames - array of set names (e.g. ["Tabs", "WindowSize"].
|
||||
* @return {Object[]} Array of sets containing `name` and `configurations` properties.
|
||||
*/
|
||||
loadSets(setNames) {
|
||||
let sets = [];
|
||||
for (let setName of setNames) {
|
||||
try {
|
||||
let imported = {};
|
||||
Cu.import("chrome://mozscreenshots/content/configurations/" + setName + ".jsm",
|
||||
imported);
|
||||
imported[setName].init(this._libDir);
|
||||
let configurationNames = Object.keys(imported[setName].configurations);
|
||||
if (!configurationNames.length) {
|
||||
throw new Error(setName + " has no configurations for this environment");
|
||||
}
|
||||
for (let config of configurationNames) {
|
||||
// Automatically set the name property of the configuration object to
|
||||
// its name from the configuration object.
|
||||
imported[setName].configurations[config].name = config;
|
||||
}
|
||||
sets.push(imported[setName].configurations);
|
||||
} catch (ex) {
|
||||
log.error("Error loading set: " + setName);
|
||||
log.error(ex);
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
return sets;
|
||||
},
|
||||
|
||||
cleanup() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let gBrowser = browserWindow.gBrowser;
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
|
||||
}
|
||||
gBrowser.unpinTab(gBrowser.selectedTab);
|
||||
gBrowser.selectedBrowser.loadURI("data:text/html;charset=utf-8,<h1>Done!");
|
||||
browserWindow.restore();
|
||||
},
|
||||
|
||||
///// helpers /////
|
||||
|
||||
_performCombo: function*(combo) {
|
||||
let paddedComboIndex = padLeft(this.currentComboIndex + 1, String(this.combos.length).length);
|
||||
log.info("Combination " + paddedComboIndex + "/" + this.combos.length + ": " +
|
||||
this._comboName(combo).substring(1));
|
||||
|
||||
function changeConfig(config) {
|
||||
log.debug("calling " + config.name);
|
||||
let promise = config.applyConfig();
|
||||
log.debug("called " + config.name);
|
||||
return promise;
|
||||
}
|
||||
|
||||
try {
|
||||
// First go through and actually apply all of the configs
|
||||
for (let i = 0; i < combo.length; i++) {
|
||||
let config = combo[i];
|
||||
if (!this._lastCombo || config !== this._lastCombo[i]) {
|
||||
log.debug("promising", config.name);
|
||||
yield changeConfig(config);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the lastCombo since it's now been applied regardless of whether it's accepted below.
|
||||
log.debug("fulfilled all applyConfig so setting lastCombo.");
|
||||
this._lastCombo = combo;
|
||||
|
||||
// Then ask configs if the current setup is valid. We can't can do this in
|
||||
// the applyConfig methods of the config since it doesn't know what configs
|
||||
// later in the loop will do that may invalidate the combo.
|
||||
for (let i = 0; i < combo.length; i++) {
|
||||
let config = combo[i];
|
||||
// A configuration can specify an optional verifyConfig method to indicate
|
||||
// if the current config is valid for a screenshot. This gets called even
|
||||
// if the this config was used in the lastCombo since another config may
|
||||
// have invalidated it.
|
||||
if (config.verifyConfig) {
|
||||
log.debug("checking if the combo is valid with", config.name);
|
||||
yield config.verifyConfig();
|
||||
}
|
||||
}
|
||||
} catch (ex) {
|
||||
log.warn("\tskipped configuration: " + ex);
|
||||
// Don't set lastCombo here so that we properly know which configurations
|
||||
// need to be applied since the last screenshot
|
||||
|
||||
// Return so we don't take a screenshot.
|
||||
return;
|
||||
}
|
||||
|
||||
yield this._onConfigurationReady(combo);
|
||||
},
|
||||
|
||||
_onConfigurationReady(combo) {
|
||||
let delayedScreenshot = () => {
|
||||
let filename = padLeft(this.currentComboIndex + 1,
|
||||
String(this.combos.length).length) + this._comboName(combo);
|
||||
return Screenshot.captureExternal(filename)
|
||||
.then(() => {
|
||||
this.completedCombos++;
|
||||
});
|
||||
};
|
||||
|
||||
log.debug("_onConfigurationReady");
|
||||
return Task.spawn(delayedScreenshot);
|
||||
},
|
||||
|
||||
_comboName(combo) {
|
||||
return combo.reduce(function(a, b) {
|
||||
return a + "_" + b.name;
|
||||
}, "");
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to lazily compute the Cartesian product of all of the sets of configurations.
|
||||
**/
|
||||
function LazyProduct(sets) {
|
||||
/**
|
||||
* An entry for each set with the value being:
|
||||
* [the number of permutations of the sets with lower index,
|
||||
* the number of items in the set at the index]
|
||||
*/
|
||||
this.sets = sets;
|
||||
this.lookupTable = [];
|
||||
let combinations = 1;
|
||||
for (let i = this.sets.length - 1; i >= 0; i--) {
|
||||
let set = this.sets[i];
|
||||
let setLength = Object.keys(set).length;
|
||||
this.lookupTable[i] = [combinations, setLength];
|
||||
combinations *= setLength;
|
||||
}
|
||||
}
|
||||
LazyProduct.prototype = {
|
||||
get length() {
|
||||
let last = this.lookupTable[0];
|
||||
if (!last)
|
||||
return 0;
|
||||
return last[0] * last[1];
|
||||
},
|
||||
|
||||
item(n) {
|
||||
// For set i, get the item from the set with the floored value of
|
||||
// (n / the number of permutations of the sets already chosen from) modulo the length of set i
|
||||
let result = [];
|
||||
for (let i = this.sets.length - 1; i >= 0; i--) {
|
||||
let priorCombinations = this.lookupTable[i][0];
|
||||
let setLength = this.lookupTable[i][1];
|
||||
let keyIndex = Math.floor(n / priorCombinations) % setLength;
|
||||
let keys = Object.keys(this.sets[i]);
|
||||
result[i] = this.sets[i][keys[keyIndex]];
|
||||
}
|
||||
return result;
|
||||
},
|
||||
};
|
||||
|
||||
function padLeft(number, width, padding = "0") {
|
||||
return padding.repeat(Math.max(0, width - String(number).length)) + number;
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
/*
|
||||
#if 0
|
||||
Workaround a build system bug where this file doesn't get packaged if not pre-processed.
|
||||
#endif
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
const env = Cc["@mozilla.org/process/environment;1"].getService(Ci.nsIEnvironment);
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/AddonManager.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TestRunner",
|
||||
"chrome://mozscreenshots/content/TestRunner.jsm");
|
||||
|
||||
function install(data, reason) {
|
||||
if (!isAppSupported()) {
|
||||
uninstallExtension(data);
|
||||
return;
|
||||
}
|
||||
|
||||
AddonManager.getAddonByID(data.id, function(addon) {
|
||||
// Enable on install in case the user disabled a prior version
|
||||
if (addon) {
|
||||
addon.userDisabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function startup(data, reason) {
|
||||
if (!isAppSupported()) {
|
||||
uninstallExtension(data);
|
||||
return;
|
||||
}
|
||||
|
||||
AddonManager.getAddonByID(data.id, function(addon) {
|
||||
let extensionPath = addon.getResourceURI();
|
||||
TestRunner.init(extensionPath);
|
||||
});
|
||||
}
|
||||
|
||||
function shutdown(data, reason) { }
|
||||
|
||||
function uninstall(data, reason) { }
|
||||
|
||||
/**
|
||||
* @return boolean whether the test suite applies to the application.
|
||||
*/
|
||||
function isAppSupported() {
|
||||
return true;
|
||||
}
|
||||
|
||||
function uninstallExtension(data) {
|
||||
AddonManager.getAddonByID(data.id, function(addon) {
|
||||
addon.uninstall();
|
||||
});
|
||||
}
|
||||
|
||||
function startRun() {
|
||||
let env = Cc["@mozilla.org/process/environment;1"]
|
||||
.getService(Ci.nsIEnvironment);
|
||||
let setsEnv = env.get("MOZSCREENSHOTS_SETS");
|
||||
let sets = setsEnv ? setsEnv.split(",") : null;
|
||||
TestRunner.start(sets);
|
||||
}
|
|
@ -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/. */
|
||||
|
||||
"use strict";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["AppMenu"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
this.AppMenu = {
|
||||
|
||||
init(libDir) {},
|
||||
|
||||
configurations: {
|
||||
appMenuClosed: {
|
||||
applyConfig: Task.async(function*() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
browserWindow.PanelUI.hide();
|
||||
}),
|
||||
},
|
||||
|
||||
appMenuMainView: {
|
||||
applyConfig() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let promise = browserWindow.PanelUI.show();
|
||||
browserWindow.PanelUI.showMainView();
|
||||
return promise;
|
||||
},
|
||||
},
|
||||
|
||||
appMenuHistorySubview: {
|
||||
applyConfig() {
|
||||
// History has a footer
|
||||
if (isCustomizing()) {
|
||||
return Promise.reject("Can't show subviews while customizing");
|
||||
}
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let promise = browserWindow.PanelUI.show();
|
||||
return promise.then(() => {
|
||||
browserWindow.PanelUI.showMainView();
|
||||
browserWindow.document.getElementById("history-panelmenu").click();
|
||||
});
|
||||
},
|
||||
|
||||
verifyConfig: verifyConfigHelper,
|
||||
},
|
||||
|
||||
appMenuHelpSubview: {
|
||||
applyConfig() {
|
||||
if (isCustomizing()) {
|
||||
return Promise.reject("Can't show subviews while customizing");
|
||||
}
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let promise = browserWindow.PanelUI.show();
|
||||
return promise.then(() => {
|
||||
browserWindow.PanelUI.showMainView();
|
||||
browserWindow.document.getElementById("PanelUI-help").click();
|
||||
});
|
||||
},
|
||||
|
||||
verifyConfig: verifyConfigHelper,
|
||||
},
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
function verifyConfigHelper() {
|
||||
if (isCustomizing()) {
|
||||
return Promise.reject("AppMenu verifyConfigHelper");
|
||||
}
|
||||
return Promise.resolve("AppMenu verifyConfigHelper");
|
||||
}
|
||||
|
||||
function isCustomizing() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browserWindow.document.documentElement.hasAttribute("customizing")) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Buttons"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource:///modules/CustomizableUI.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
this.Buttons = {
|
||||
|
||||
init(libDir) {
|
||||
createWidget();
|
||||
},
|
||||
|
||||
configurations: {
|
||||
navBarButtons: {
|
||||
applyConfig: Task.async(() =>{
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
CustomizableUI.addWidgetToArea("screenshot-widget", CustomizableUI.AREA_NAVBAR);
|
||||
}),
|
||||
},
|
||||
|
||||
tabsToolbarButtons: {
|
||||
applyConfig: Task.async(() => {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
CustomizableUI.addWidgetToArea("screenshot-widget", CustomizableUI.AREA_TABSTRIP);
|
||||
}),
|
||||
},
|
||||
|
||||
menuPanelButtons: {
|
||||
applyConfig: Task.async(() => {
|
||||
CustomizableUI.addWidgetToArea("screenshot-widget", CustomizableUI.AREA_PANEL);
|
||||
}),
|
||||
|
||||
verifyConfig() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browserWindow.PanelUI.panel.state == "closed") {
|
||||
return Promise.reject("The button isn't shown when the panel isn't open.");
|
||||
}
|
||||
return Promise.resolve("menuPanelButtons.verifyConfig");
|
||||
},
|
||||
},
|
||||
|
||||
custPaletteButtons: {
|
||||
applyConfig: Task.async(() => {
|
||||
CustomizableUI.removeWidgetFromArea("screenshot-widget");
|
||||
}),
|
||||
|
||||
verifyConfig() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browserWindow.document.documentElement.getAttribute("customizing") != "true") {
|
||||
return Promise.reject("The button isn't shown when we're not in customize mode.");
|
||||
}
|
||||
return Promise.resolve("custPaletteButtons.verifyConfig");
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function createWidget() {
|
||||
let id = "screenshot-widget";
|
||||
let spec = {
|
||||
id: id,
|
||||
label: "My Button",
|
||||
removable: true,
|
||||
tooltiptext: "",
|
||||
type: "button",
|
||||
};
|
||||
CustomizableUI.createWidget(spec);
|
||||
|
||||
// Append a <style> for the image
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let st = browserWindow.document.createElementNS("http://www.w3.org/1999/xhtml", "style");
|
||||
let styles = "" +
|
||||
"#screenshot-widget > .toolbarbutton-icon {" +
|
||||
" list-style-image: url(chrome://browser/skin/Toolbar.png);" +
|
||||
" -moz-image-region: rect(0px, 18px, 18px, 0px);" +
|
||||
"}";
|
||||
st.appendChild(browserWindow.document.createTextNode(styles));
|
||||
browserWindow.document.documentElement.appendChild(st);
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["CustomizeMode"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
this.CustomizeMode = {
|
||||
|
||||
init(libDir) {},
|
||||
|
||||
configurations: {
|
||||
notCustomizing: {
|
||||
applyConfig() {
|
||||
return new Promise((resolve) => {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (!browserWindow.document.documentElement.hasAttribute("customizing")) {
|
||||
resolve("notCustomizing: already not customizing");
|
||||
return;
|
||||
}
|
||||
function onCustomizationEnds() {
|
||||
browserWindow.gNavToolbox.removeEventListener("aftercustomization",
|
||||
onCustomizationEnds);
|
||||
// Wait for final changes
|
||||
setTimeout(() => resolve("notCustomizing: onCustomizationEnds"), 500);
|
||||
}
|
||||
browserWindow.gNavToolbox.addEventListener("aftercustomization",
|
||||
onCustomizationEnds);
|
||||
browserWindow.gCustomizeMode.exit();
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
customizing: {
|
||||
applyConfig() {
|
||||
return new Promise((resolve) => {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browserWindow.document.documentElement.hasAttribute("customizing")) {
|
||||
resolve("customizing: already customizing");
|
||||
return;
|
||||
}
|
||||
function onCustomizing() {
|
||||
browserWindow.gNavToolbox.removeEventListener("customizationready",
|
||||
onCustomizing);
|
||||
// Wait for final changes
|
||||
setTimeout(() => resolve("customizing: onCustomizing"), 500);
|
||||
}
|
||||
browserWindow.gNavToolbox.addEventListener("customizationready",
|
||||
onCustomizing);
|
||||
browserWindow.gCustomizeMode.enter();
|
||||
});
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,42 @@
|
|||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["DevEdition"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
const THEME_ID = "firefox-devedition@mozilla.org";
|
||||
|
||||
Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
this.DevEdition = {
|
||||
init(libDir) {},
|
||||
|
||||
configurations: {
|
||||
devEditionLight: {
|
||||
applyConfig: Task.async(() => {
|
||||
Services.prefs.setCharPref("devtools.theme", "light");
|
||||
LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme(THEME_ID);
|
||||
Services.prefs.setBoolPref("browser.devedition.theme.showCustomizeButton", true);
|
||||
}),
|
||||
},
|
||||
devEditionDark: {
|
||||
applyConfig: Task.async(() => {
|
||||
Services.prefs.setCharPref("devtools.theme", "dark");
|
||||
LightweightThemeManager.currentTheme = LightweightThemeManager.getUsedTheme(THEME_ID);
|
||||
Services.prefs.setBoolPref("browser.devedition.theme.showCustomizeButton", true);
|
||||
}),
|
||||
},
|
||||
devEditionOff: {
|
||||
applyConfig: Task.async(() => {
|
||||
Services.prefs.clearUserPref("devtools.theme");
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
Services.prefs.clearUserPref("browser.devedition.theme.showCustomizeButton");
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
|
@ -0,0 +1,50 @@
|
|||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["DevTools"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://devtools/client/framework/gDevTools.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
|
||||
let TargetFactory = devtools.TargetFactory;
|
||||
|
||||
function getTargetForSelectedTab() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let target = TargetFactory.forTab(browserWindow.gBrowser.selectedTab);
|
||||
return target;
|
||||
}
|
||||
|
||||
this.DevTools = {
|
||||
init(libDir) {
|
||||
let panels = ["options", "webconsole", "inspector", "jsdebugger", "netmonitor"];
|
||||
panels.forEach(panel => {
|
||||
this.configurations[panel] = {};
|
||||
this.configurations[panel].applyConfig = () => {
|
||||
return gDevTools.showToolbox(getTargetForSelectedTab(), panel, "bottom");
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
configurations: {
|
||||
bottomToolbox: {
|
||||
applyConfig() {
|
||||
return gDevTools.showToolbox(getTargetForSelectedTab(), "inspector", "bottom");
|
||||
},
|
||||
},
|
||||
sideToolbox: {
|
||||
applyConfig() {
|
||||
return gDevTools.showToolbox(getTargetForSelectedTab(), "inspector", "side");
|
||||
},
|
||||
},
|
||||
undockedToolbox: {
|
||||
applyConfig() {
|
||||
return gDevTools.showToolbox(getTargetForSelectedTab(), "inspector", "window");
|
||||
},
|
||||
}
|
||||
},
|
||||
};
|
|
@ -0,0 +1,92 @@
|
|||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["LightweightThemes"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/LightweightThemeManager.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
this.LightweightThemes = {
|
||||
init(libDir) {
|
||||
// convert -size 3000x200 canvas:black black_theme.png
|
||||
let blackImage = libDir.clone();
|
||||
blackImage.append("black_theme.png");
|
||||
this._blackImageURL = Services.io.newFileURI(blackImage).spec;
|
||||
|
||||
// convert -size 3000x200 canvas:white white_theme.png
|
||||
let whiteImage = libDir.clone();
|
||||
whiteImage.append("white_theme.png");
|
||||
this._whiteImageURL = Services.io.newFileURI(whiteImage).spec;
|
||||
},
|
||||
|
||||
configurations: {
|
||||
noLWT: {
|
||||
applyConfig: Task.async(function*() {
|
||||
LightweightThemeManager.currentTheme = null;
|
||||
}),
|
||||
},
|
||||
|
||||
darkLWT: {
|
||||
applyConfig() {
|
||||
LightweightThemeManager.setLocalTheme({
|
||||
id: "black",
|
||||
name: "black",
|
||||
headerURL: LightweightThemes._blackImageURL,
|
||||
footerURL: LightweightThemes._blackImageURL,
|
||||
textcolor: "#ffffff",
|
||||
accentcolor: "#111111",
|
||||
});
|
||||
|
||||
// Wait for LWT listener
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve("darkLWT");
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
|
||||
verifyConfig: verifyConfigHelper,
|
||||
},
|
||||
|
||||
lightLWT: {
|
||||
applyConfig() {
|
||||
LightweightThemeManager.setLocalTheme({
|
||||
id: "white",
|
||||
name: "white",
|
||||
headerURL: LightweightThemes._whiteImageURL,
|
||||
footerURL: LightweightThemes._whiteImageURL,
|
||||
textcolor: "#000000",
|
||||
accentcolor: "#eeeeee",
|
||||
});
|
||||
// Wait for LWT listener
|
||||
return new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve("lightLWT");
|
||||
}, 500);
|
||||
});
|
||||
},
|
||||
|
||||
verifyConfig: verifyConfigHelper,
|
||||
},
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
function verifyConfigHelper() {
|
||||
return new Promise((resolve, reject) => {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browserWindow.document.documentElement.hasAttribute("lwtheme")) {
|
||||
resolve("verifyConfigHelper");
|
||||
} else {
|
||||
reject("The @lwtheme attribute wasn't present so themes may not be available");
|
||||
}
|
||||
});
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Preferences"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
this.Preferences = {
|
||||
|
||||
init(libDir) {
|
||||
Services.prefs.setBoolPref("browser.preferences.inContent", true);
|
||||
|
||||
let panes = [
|
||||
["paneGeneral", null],
|
||||
["paneSearch", null],
|
||||
["paneContent", null],
|
||||
["paneApplications", null],
|
||||
["panePrivacy", null],
|
||||
["paneSecurity", null],
|
||||
["paneSync", null],
|
||||
["paneAdvanced", "generalTab"],
|
||||
["paneAdvanced", "dataChoicesTab"],
|
||||
["paneAdvanced", "networkTab"],
|
||||
["paneAdvanced", "updateTab"],
|
||||
["paneAdvanced", "encryptionTab"],
|
||||
];
|
||||
for (let [primary, advanced] of panes) {
|
||||
let configName = primary + ("-" + advanced || "");
|
||||
this.configurations[configName] = {};
|
||||
this.configurations[configName].applyConfig = prefHelper.bind(null, primary, advanced);
|
||||
}
|
||||
},
|
||||
|
||||
configurations: {},
|
||||
};
|
||||
|
||||
function prefHelper(primary, advanced) {
|
||||
return new Promise((resolve) => {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (primary == "paneAdvanced") {
|
||||
browserWindow.openAdvancedPreferences(advanced);
|
||||
} else {
|
||||
browserWindow.openPreferences(primary);
|
||||
}
|
||||
setTimeout(resolve, 50);
|
||||
});
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Tabs"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
const CUST_TAB = "chrome://browser/skin/customizableui/customizeFavicon.ico";
|
||||
const PREFS_TAB = "chrome://browser/skin/preferences/in-content/favicon.ico";
|
||||
const DEFAULT_FAVICON_TAB = `data:text/html,<meta charset="utf-8">
|
||||
<title>No favicon</title>`;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
this.Tabs = {
|
||||
init(libDir) {},
|
||||
|
||||
configurations: {
|
||||
fiveTabs: {
|
||||
applyConfig: Task.async(function*() {
|
||||
fiveTabsHelper();
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
hoverTab(browserWindow.gBrowser.tabs[3]);
|
||||
}),
|
||||
},
|
||||
|
||||
fourPinned: {
|
||||
applyConfig: Task.async(function*() {
|
||||
fiveTabsHelper();
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let tab = browserWindow.gBrowser.addTab(PREFS_TAB);
|
||||
browserWindow.gBrowser.pinTab(tab);
|
||||
tab = browserWindow.gBrowser.addTab(CUST_TAB);
|
||||
browserWindow.gBrowser.pinTab(tab);
|
||||
tab = browserWindow.gBrowser.addTab("about:privatebrowsing");
|
||||
browserWindow.gBrowser.pinTab(tab);
|
||||
tab = browserWindow.gBrowser.addTab("about:home");
|
||||
browserWindow.gBrowser.pinTab(tab);
|
||||
browserWindow.gBrowser.selectTabAtIndex(5);
|
||||
hoverTab(browserWindow.gBrowser.tabs[2]);
|
||||
// also hover the new tab button
|
||||
let newTabButton = browserWindow.document.getAnonymousElementByAttribute(browserWindow.
|
||||
gBrowser.tabContainer, "class", "tabs-newtab-button");
|
||||
hoverTab(newTabButton);
|
||||
browserWindow.gBrowser.tabs[browserWindow.gBrowser.tabs.length - 1].
|
||||
setAttribute("beforehovered", true);
|
||||
}),
|
||||
},
|
||||
|
||||
twoPinnedWithOverflow: {
|
||||
applyConfig: Task.async(function*() {
|
||||
fiveTabsHelper();
|
||||
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
browserWindow.gBrowser.loadTabs([
|
||||
"about:addons",
|
||||
"about:home",
|
||||
DEFAULT_FAVICON_TAB,
|
||||
"about:newtab",
|
||||
"about:addons",
|
||||
"about:home",
|
||||
DEFAULT_FAVICON_TAB,
|
||||
"about:newtab",
|
||||
"about:addons",
|
||||
"about:home",
|
||||
DEFAULT_FAVICON_TAB,
|
||||
"about:newtab",
|
||||
"about:addons",
|
||||
"about:home",
|
||||
DEFAULT_FAVICON_TAB,
|
||||
"about:newtab",
|
||||
"about:addons",
|
||||
"about:home",
|
||||
DEFAULT_FAVICON_TAB,
|
||||
"about:newtab",
|
||||
"about:addons",
|
||||
"about:home",
|
||||
DEFAULT_FAVICON_TAB,
|
||||
"about:newtab",
|
||||
"about:addons",
|
||||
"about:home",
|
||||
DEFAULT_FAVICON_TAB,
|
||||
"about:newtab",
|
||||
], true, true);
|
||||
let tab = browserWindow.gBrowser.addTab(PREFS_TAB);
|
||||
browserWindow.gBrowser.pinTab(tab);
|
||||
tab = browserWindow.gBrowser.addTab(CUST_TAB);
|
||||
browserWindow.gBrowser.pinTab(tab);
|
||||
browserWindow.gBrowser.selectTabAtIndex(4);
|
||||
hoverTab(browserWindow.gBrowser.tabs[6]);
|
||||
}),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
/* helpers */
|
||||
|
||||
function fiveTabsHelper() {
|
||||
// some with no favicon and some with. Selected tab in middle.
|
||||
closeAllButOneTab("about:addons");
|
||||
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
browserWindow.gBrowser.loadTabs([
|
||||
"about:addons",
|
||||
"about:home",
|
||||
DEFAULT_FAVICON_TAB,
|
||||
"about:newtab",
|
||||
CUST_TAB,
|
||||
], true, true);
|
||||
browserWindow.gBrowser.selectTabAtIndex(1);
|
||||
}
|
||||
|
||||
function closeAllButOneTab(url = "about:blank") {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let gBrowser = browserWindow.gBrowser;
|
||||
// Close all tabs except the last so we don't quit the browser.
|
||||
while (gBrowser.tabs.length > 1)
|
||||
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
|
||||
gBrowser.selectedBrowser.loadURI(url);
|
||||
if (gBrowser.selectedTab.pinned)
|
||||
gBrowser.unpinTab(gBrowser.selectedTab);
|
||||
let newTabButton = browserWindow.document.getAnonymousElementByAttribute(browserWindow.gBrowser.tabContainer, "class", "tabs-newtab-button");
|
||||
hoverTab(newTabButton, false);
|
||||
}
|
||||
|
||||
function hoverTab(tab, hover = true) {
|
||||
const inIDOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||
if (hover) {
|
||||
inIDOMUtils.addPseudoClassLock(tab, ":hover");
|
||||
} else {
|
||||
inIDOMUtils.clearPseudoClassLocks(tab);
|
||||
}
|
||||
// XXX TODO: this isn't necessarily testing what we ship
|
||||
if (tab.nextElementSibling)
|
||||
tab.nextElementSibling.setAttribute("afterhovered", hover || null);
|
||||
if (tab.previousElementSibling)
|
||||
tab.previousElementSibling.setAttribute("beforehovered", hover || null);
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["TabsInTitlebar"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
const PREF_TABS_IN_TITLEBAR = "browser.tabs.drawInTitlebar";
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
this.TabsInTitlebar = {
|
||||
|
||||
init(libDir) {},
|
||||
|
||||
configurations: {
|
||||
tabsInTitlebar: {
|
||||
applyConfig: Task.async(function*() {
|
||||
if (Services.appinfo.OS == "Linux") {
|
||||
return Promise.reject("TabsInTitlebar isn't supported on Linux");
|
||||
}
|
||||
Services.prefs.setBoolPref(PREF_TABS_IN_TITLEBAR, true);
|
||||
}),
|
||||
},
|
||||
|
||||
tabsOutsideTitlebar: {
|
||||
applyConfig: Task.async(function*() {
|
||||
Services.prefs.setBoolPref(PREF_TABS_IN_TITLEBAR, false);
|
||||
}),
|
||||
},
|
||||
|
||||
},
|
||||
};
|
|
@ -0,0 +1,56 @@
|
|||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["Toolbars"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
this.Toolbars = {
|
||||
init(libDir) {},
|
||||
|
||||
configurations: {
|
||||
onlyNavBar: {
|
||||
applyConfig: Task.async(function*() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let personalToolbar = browserWindow.document.getElementById("PersonalToolbar");
|
||||
browserWindow.setToolbarVisibility(personalToolbar, false);
|
||||
toggleMenubarIfNecessary(false);
|
||||
}),
|
||||
},
|
||||
|
||||
allToolbars: {
|
||||
applyConfig: Task.async(function*() { // Boookmarks and menubar
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
let personalToolbar = browserWindow.document.getElementById("PersonalToolbar");
|
||||
browserWindow.setToolbarVisibility(personalToolbar, true);
|
||||
toggleMenubarIfNecessary(true);
|
||||
}),
|
||||
|
||||
verifyConfig: Task.async(function*() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
if (browserWindow.fullScreen) {
|
||||
return Promise.reject("The bookmark toolbar and menubar are not shown in fullscreen.");
|
||||
}
|
||||
}),
|
||||
},
|
||||
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
///// helpers /////
|
||||
|
||||
function toggleMenubarIfNecessary(visible) {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
// The menubar is not shown on OS X or while in fullScreen
|
||||
if (Services.appinfo.OS != "Darwin" /*&& !browserWindow.fullScreen*/) {
|
||||
let menubar = browserWindow.document.getElementById("toolbar-menubar");
|
||||
browserWindow.setToolbarVisibility(menubar, visible);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
/* 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";
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["WindowSize"];
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://gre/modules/Timer.jsm");
|
||||
|
||||
this.WindowSize = {
|
||||
|
||||
init(libDir) {
|
||||
Services.prefs.setBoolPref("browser.fullscreen.autohide", false);
|
||||
},
|
||||
|
||||
configurations: {
|
||||
maximized: {
|
||||
applyConfig: Task.async(function*() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
browserWindow.fullScreen = false;
|
||||
|
||||
// Wait for the Lion fullscreen transition to end as there doesn't seem to be an event
|
||||
// and trying to maximize while still leaving fullscreen doesn't work.
|
||||
yield new Promise((resolve, reject) => {
|
||||
setTimeout(function waitToLeaveFS() {
|
||||
browserWindow.maximize();
|
||||
resolve();
|
||||
}, Services.appinfo.OS == "Darwin" ? 1500 : 0);
|
||||
});
|
||||
}),
|
||||
},
|
||||
|
||||
normal: {
|
||||
applyConfig: Task.async(() => {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
browserWindow.fullScreen = false;
|
||||
browserWindow.restore();
|
||||
}),
|
||||
},
|
||||
|
||||
fullScreen: {
|
||||
applyConfig: Task.async(function*() {
|
||||
let browserWindow = Services.wm.getMostRecentWindow("navigator:browser");
|
||||
browserWindow.fullScreen = true;
|
||||
// OS X Lion fullscreen transition takes a while
|
||||
yield new Promise((resolve, reject) => {
|
||||
setTimeout(function waitAfterEnteringFS() {
|
||||
resolve();
|
||||
}, Services.appinfo.OS == "Darwin" ? 1500 : 0);
|
||||
});
|
||||
}),
|
||||
},
|
||||
|
||||
},
|
||||
};
|
|
@ -0,0 +1,33 @@
|
|||
<?xml version="1.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/. -->
|
||||
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>mozscreenshots@mozilla.org</em:id>
|
||||
#expand <em:version>__MOZILLA_VERSION_U__</em:version>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
|
||||
<!-- for running custom screenshot binaries -->
|
||||
<em:unpack>true</em:unpack>
|
||||
|
||||
<!-- Front End MetaData -->
|
||||
<em:name>mozscreenshots</em:name>
|
||||
<em:description>Take screenshots of Mozilla applications in various UI configurations.</em:description>
|
||||
<em:creator>Mozilla</em:creator>
|
||||
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<!-- Firefox -->
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
#expand <em:minVersion>__MOZILLA_VERSION_U__</em:minVersion>
|
||||
<em:maxVersion>*</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
|
||||
</Description>
|
||||
</RDF>
|
|
@ -0,0 +1,6 @@
|
|||
mozscreenshots.jar:
|
||||
% content mozscreenshots chrome/mozscreenshots/
|
||||
Screenshot.jsm
|
||||
TestRunner.jsm
|
||||
configurations/ (configurations/*.jsm)
|
||||
lib/ (lib/*.png)
|
Двоичные данные
browser/tools/mozscreenshots/mozscreenshots/extension/lib/black_theme.png
Normal file
Двоичные данные
browser/tools/mozscreenshots/mozscreenshots/extension/lib/black_theme.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 343 B |
Двоичные данные
browser/tools/mozscreenshots/mozscreenshots/extension/lib/white_theme.png
Normal file
Двоичные данные
browser/tools/mozscreenshots/mozscreenshots/extension/lib/white_theme.png
Normal file
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 522 B |
|
@ -0,0 +1,17 @@
|
|||
# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
XPI_NAME = 'mozscreenshots'
|
||||
|
||||
JAR_MANIFESTS += ['jar.mn']
|
||||
|
||||
USE_EXTENSION_MANIFEST = True
|
||||
NO_JS_MANIFEST = True
|
||||
|
||||
FINAL_TARGET_PP_FILES += [
|
||||
'bootstrap.js',
|
||||
'install.rdf',
|
||||
]
|
|
@ -231,7 +231,7 @@ define([MOZ_BUILD_BACKEND],
|
|||
BUILD_BACKENDS="RecursiveMake"
|
||||
|
||||
MOZ_ARG_ENABLE_STRING(build-backend,
|
||||
[ --enable-build-backend={AndroidEclipse,CppEclipse,VisualStudio,FasterMake,CompileDB,ChromeMap}
|
||||
[ --enable-build-backend={$($(dirname ]$[0)/$1/mach python -c "from mozbuild.backend import backends; print ','.join(sorted(backends))")}
|
||||
Enable additional build backends],
|
||||
[ BUILD_BACKENDS="RecursiveMake `echo $enableval | sed 's/,/ /g'`"])
|
||||
|
||||
|
|
|
@ -258,7 +258,10 @@ Elf::Elf(std::ifstream &file)
|
|||
segment->addSection(sections[j]);
|
||||
// Make sure that our view of segments corresponds to the original
|
||||
// ELF file.
|
||||
assert(segment->getFileSize() == phdr.p_filesz);
|
||||
// GNU gold likes to start some segments before the first section
|
||||
// they contain. https://sourceware.org/bugzilla/show_bug.cgi?id=19392
|
||||
unsigned int gold_adjustment = segment->getAddr() - phdr.p_vaddr;
|
||||
assert(segment->getFileSize() == phdr.p_filesz - gold_adjustment);
|
||||
// gold makes TLS segments end on an aligned virtual address, even
|
||||
// when the underlying section ends before that, while bfd ld
|
||||
// doesn't. It's fine if we don't keep that alignment.
|
||||
|
@ -267,7 +270,7 @@ Elf::Elf(std::ifstream &file)
|
|||
unsigned int align = segment->getAlign();
|
||||
memsize = (memsize + align - 1) & ~(align - 1);
|
||||
}
|
||||
assert(memsize == phdr.p_memsz);
|
||||
assert(memsize == phdr.p_memsz - gold_adjustment);
|
||||
segments.push_back(segment);
|
||||
}
|
||||
|
||||
|
|
|
@ -692,6 +692,11 @@ VFY_VerifyDataWithAlgorithmID
|
|||
VFY_VerifyDigestDirect
|
||||
_SGN_VerifyPKCS1DigestInfo
|
||||
__PK11_SetCertificateNickname
|
||||
# These symbols are not used by Firefox itself, but are used by Java's security
|
||||
# libraries, which in turn are used by Java applets/plugins/etc. Provide them
|
||||
# to make Java code happy.
|
||||
NSS_VersionCheck
|
||||
NSS_Initialize
|
||||
#ifdef NSS_EXTRA_SYMBOLS_FILE
|
||||
#include @NSS_EXTRA_SYMBOLS_FILE@
|
||||
#endif
|
||||
|
|
|
@ -1581,7 +1581,3 @@ include $(MOZILLA_DIR)/config/makefiles/autotargets.mk
|
|||
ifneq ($(NULL),$(AUTO_DEPS))
|
||||
default all libs tools export:: $(AUTO_DEPS)
|
||||
endif
|
||||
|
||||
export:: $(GENERATED_FILES)
|
||||
|
||||
GARBAGE += $(GENERATED_FILES)
|
||||
|
|
|
@ -139,7 +139,7 @@ DIST="$MOZ_BUILD_ROOT/dist"
|
|||
|
||||
MOZ_PYTHON
|
||||
|
||||
MOZ_BUILD_BACKEND
|
||||
MOZ_BUILD_BACKEND(.)
|
||||
|
||||
MOZ_DEFAULT_COMPILER
|
||||
|
||||
|
@ -2288,6 +2288,11 @@ ia64*-hpux*)
|
|||
# At least one MSVC header and several headers in-tree have
|
||||
# unused typedefs, so turn this on.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-unused-local-typedef"
|
||||
# Several JS engine header files use __declspec(dllimport) on
|
||||
# classes, and clang-cl helpfully warns about its non-support
|
||||
# for such cases. We're not particularly worried about that,
|
||||
# so ignore that warning.
|
||||
CXXFLAGS="$CXXFLAGS -Wno-ignored-attributes"
|
||||
fi
|
||||
# make 'foo == bar;' error out
|
||||
CFLAGS="$CFLAGS -we4553"
|
||||
|
|
|
@ -81,8 +81,6 @@ var CallsListView = Heritage.extend(WidgetMethods, {
|
|||
gutter.appendChild(index);
|
||||
contents.appendChild(gutter);
|
||||
|
||||
// Not all function calls have a caller that was stringified (e.g.
|
||||
// context calls have a "gl" or "ctx" caller preview).
|
||||
if (call.callerPreview) {
|
||||
let context = document.createElement("label");
|
||||
context.className = "plain call-item-context";
|
||||
|
|
|
@ -40,7 +40,7 @@ function* ifTestingSupported() {
|
|||
is(functionCalls[0].line, 25,
|
||||
"The called function's line is correct.");
|
||||
|
||||
is(functionCalls[0].callerPreview, "ctx",
|
||||
is(functionCalls[0].callerPreview, "Object",
|
||||
"The called function's caller preview is correct.");
|
||||
is(functionCalls[0].argsPreview, "0, 0, 128, 128",
|
||||
"The called function's args preview is correct.");
|
||||
|
|
|
@ -41,7 +41,7 @@ function* ifTestingSupported() {
|
|||
"The first called function's line is correct.");
|
||||
is(functionCalls[0].argsPreview, "0, 0, 128, 128",
|
||||
"The first called function's args preview is correct.");
|
||||
is(functionCalls[0].callerPreview, "ctx",
|
||||
is(functionCalls[0].callerPreview, "Object",
|
||||
"The first called function's caller preview is correct.");
|
||||
|
||||
is(functionCalls[6].type, CallWatcherFront.METHOD_FUNCTION,
|
||||
|
@ -54,7 +54,7 @@ function* ifTestingSupported() {
|
|||
"The penultimate called function's line is correct.");
|
||||
is(functionCalls[6].argsPreview, "10, 10, 55, 50",
|
||||
"The penultimate called function's args preview is correct.");
|
||||
is(functionCalls[6].callerPreview, "ctx",
|
||||
is(functionCalls[6].callerPreview, "Object",
|
||||
"The penultimate called function's caller preview is correct.");
|
||||
|
||||
is(functionCalls[7].type, CallWatcherFront.METHOD_FUNCTION,
|
||||
|
@ -67,7 +67,7 @@ function* ifTestingSupported() {
|
|||
"The last called function's line is correct.");
|
||||
ok(functionCalls[7].argsPreview.includes("Function"),
|
||||
"The last called function's args preview is correct.");
|
||||
is(functionCalls[7].callerPreview, "",
|
||||
is(functionCalls[7].callerPreview, "Object",
|
||||
"The last called function's caller preview is correct.");
|
||||
|
||||
yield removeTab(target.tab);
|
||||
|
|
|
@ -41,7 +41,7 @@ function* ifTestingSupported() {
|
|||
"The first called function's line is correct.");
|
||||
is(functionCalls[0].argsPreview, "0, 0, 128, 128",
|
||||
"The first called function's args preview is correct.");
|
||||
is(functionCalls[0].callerPreview, "ctx",
|
||||
is(functionCalls[0].callerPreview, "Object",
|
||||
"The first called function's caller preview is correct.");
|
||||
|
||||
is(functionCalls[6].type, CallWatcherFront.METHOD_FUNCTION,
|
||||
|
@ -54,7 +54,7 @@ function* ifTestingSupported() {
|
|||
"The penultimate called function's line is correct.");
|
||||
is(functionCalls[6].argsPreview, "10, 10, 55, 50",
|
||||
"The penultimate called function's args preview is correct.");
|
||||
is(functionCalls[6].callerPreview, "ctx",
|
||||
is(functionCalls[6].callerPreview, "Object",
|
||||
"The penultimate called function's caller preview is correct.");
|
||||
|
||||
is(functionCalls[7].type, CallWatcherFront.METHOD_FUNCTION,
|
||||
|
@ -67,7 +67,7 @@ function* ifTestingSupported() {
|
|||
"The last called function's line is correct.");
|
||||
ok(functionCalls[7].argsPreview.includes("Function"),
|
||||
"The last called function's args preview is correct.");
|
||||
is(functionCalls[7].callerPreview, "",
|
||||
is(functionCalls[7].callerPreview, "Object",
|
||||
"The last called function's caller preview is correct.");
|
||||
|
||||
let firstNonDrawCall = yield functionCalls[1].getDetails();
|
||||
|
|
|
@ -21,22 +21,22 @@ function* ifTestingSupported() {
|
|||
"All the function calls should now be displayed in the UI.");
|
||||
|
||||
testItem(CallsListView.getItemAtIndex(0),
|
||||
"1", "ctx", "clearRect", "(0, 0, 128, 128)", "doc_simple-canvas.html:25");
|
||||
"1", "Object", "clearRect", "(0, 0, 128, 128)", "doc_simple-canvas.html:25");
|
||||
|
||||
testItem(CallsListView.getItemAtIndex(1),
|
||||
"2", "ctx", "fillStyle", " = rgb(192, 192, 192)", "doc_simple-canvas.html:20");
|
||||
"2", "Object", "fillStyle", " = rgb(192, 192, 192)", "doc_simple-canvas.html:20");
|
||||
testItem(CallsListView.getItemAtIndex(2),
|
||||
"3", "ctx", "fillRect", "(0, 0, 128, 128)", "doc_simple-canvas.html:21");
|
||||
"3", "Object", "fillRect", "(0, 0, 128, 128)", "doc_simple-canvas.html:21");
|
||||
|
||||
testItem(CallsListView.getItemAtIndex(3),
|
||||
"4", "ctx", "fillStyle", " = rgba(0, 0, 192, 0.5)", "doc_simple-canvas.html:20");
|
||||
"4", "Object", "fillStyle", " = rgba(0, 0, 192, 0.5)", "doc_simple-canvas.html:20");
|
||||
testItem(CallsListView.getItemAtIndex(4),
|
||||
"5", "ctx", "fillRect", "(30, 30, 55, 50)", "doc_simple-canvas.html:21");
|
||||
"5", "Object", "fillRect", "(30, 30, 55, 50)", "doc_simple-canvas.html:21");
|
||||
|
||||
testItem(CallsListView.getItemAtIndex(5),
|
||||
"6", "ctx", "fillStyle", " = rgba(192, 0, 0, 0.5)", "doc_simple-canvas.html:20");
|
||||
"6", "Object", "fillStyle", " = rgba(192, 0, 0, 0.5)", "doc_simple-canvas.html:20");
|
||||
testItem(CallsListView.getItemAtIndex(6),
|
||||
"7", "ctx", "fillRect", "(10, 10, 55, 50)", "doc_simple-canvas.html:21");
|
||||
"7", "Object", "fillRect", "(10, 10, 55, 50)", "doc_simple-canvas.html:21");
|
||||
|
||||
testItem(CallsListView.getItemAtIndex(7),
|
||||
"8", "", "requestAnimationFrame", "(Function)", "doc_simple-canvas.html:30");
|
||||
|
@ -53,7 +53,7 @@ function* ifTestingSupported() {
|
|||
is($(".call-item-context", item.target).getAttribute("value"), context,
|
||||
"The item's context label has the correct text.");
|
||||
} else {
|
||||
is($(".call-item-context", item.target), null,
|
||||
is($(".call-item-context", item.target) + "", "[object XULElement]",
|
||||
"The item's context label should not be available.");
|
||||
}
|
||||
|
||||
|
|
|
@ -40,7 +40,7 @@ function* ifTestingSupported() {
|
|||
"The visible item's line has the expected value.");
|
||||
is(CallsListView.visibleItems[0].attachment.actor.argsPreview, "0, 0, 128, 128",
|
||||
"The visible item's args have the expected value.");
|
||||
is(CallsListView.visibleItems[0].attachment.actor.callerPreview, "ctx",
|
||||
is(CallsListView.visibleItems[0].attachment.actor.callerPreview, "Object",
|
||||
"The visible item's caller has the expected value.");
|
||||
|
||||
let secondRecordingFinished = once(window, EVENTS.SNAPSHOT_RECORDING_FINISHED);
|
||||
|
|
|
@ -28,7 +28,10 @@ let promise = require("promise");
|
|||
|
||||
const TEST_DIR = gTestPath.substr(0, gTestPath.lastIndexOf("/"));
|
||||
const CHROME_URL_ROOT = TEST_DIR + "/";
|
||||
const URL_ROOT = CHROME_URL_ROOT.replace("chrome://mochitests/content/", "http://example.com/");
|
||||
const URL_ROOT = CHROME_URL_ROOT.replace("chrome://mochitests/content/",
|
||||
"http://example.com/");
|
||||
const URL_ROOT_SSL = CHROME_URL_ROOT.replace("chrome://mochitests/content/",
|
||||
"https://example.com/");
|
||||
|
||||
// All test are asynchronous
|
||||
waitForExplicitFinish();
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
const {PropertyView} =
|
||||
require("devtools/client/inspector/computed/computed");
|
||||
const TEST_URI = TEST_URL_ROOT + "doc_matched_selectors.html";
|
||||
const TEST_URI = URL_ROOT + "doc_matched_selectors.html";
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URI);
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Tests that we correctly display appropriate media query titles in the
|
||||
// property view.
|
||||
|
||||
const TEST_URI = TEST_URL_ROOT + "doc_media_queries.html";
|
||||
const TEST_URI = URL_ROOT + "doc_media_queries.html";
|
||||
|
||||
var {PropertyView} = require("devtools/client/inspector/computed/computed");
|
||||
var {CssLogic} = require("devtools/shared/inspector/css-logic");
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// Tests that the computed view shows the original source link when source maps
|
||||
// are enabled.
|
||||
|
||||
const TESTCASE_URI = TEST_URL_ROOT_SSL + "doc_sourcemaps.html";
|
||||
const TESTCASE_URI = URL_ROOT_SSL + "doc_sourcemaps.html";
|
||||
const PREF = "devtools.styleeditor.source-maps-enabled";
|
||||
const SCSS_LOC = "doc_sourcemaps.scss:4";
|
||||
const CSS_LOC = "doc_sourcemaps.css:1";
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
// Tests that pseudoelements are displayed correctly in the rule view.
|
||||
|
||||
const TEST_URI = TEST_URL_ROOT + "doc_pseudoelement.html";
|
||||
const TEST_URI = URL_ROOT + "doc_pseudoelement.html";
|
||||
|
||||
add_task(function*() {
|
||||
yield addTab(TEST_URI);
|
||||
|
|
|
@ -19,7 +19,9 @@ add_task(function*() {
|
|||
|
||||
info("Changing the node's style and waiting for the update");
|
||||
let onUpdated = inspector.once("computed-view-refreshed");
|
||||
getNode("#testdiv").style.cssText = "font-size: 15px; color: red;";
|
||||
// FIXME: use the testActor to set style on the node.
|
||||
content.document.querySelector("#testdiv")
|
||||
.style.cssText = "font-size: 15px; color: red;";
|
||||
yield onUpdated;
|
||||
|
||||
fontSize = getComputedViewPropertyValue(view, "font-size");
|
||||
|
|
|
@ -44,7 +44,7 @@ add_task(function*() {
|
|||
yield checkSelectAll(view);
|
||||
});
|
||||
|
||||
function checkCopySelection(view) {
|
||||
function* checkCopySelection(view) {
|
||||
info("Testing selection copy");
|
||||
|
||||
let contentDocument = view.styleDocument;
|
||||
|
@ -63,16 +63,15 @@ function checkCopySelection(view) {
|
|||
"font-size: 16px;[\\r\\n]+" +
|
||||
"font-variant-caps: small-caps;[\\r\\n]*";
|
||||
|
||||
return waitForClipboard(() => {
|
||||
fireCopyEvent(props[0]);
|
||||
}, () => {
|
||||
return checkClipboardData(expectedPattern);
|
||||
}).then(() => {}, () => {
|
||||
try {
|
||||
yield waitForClipboard(() => fireCopyEvent(props[0]),
|
||||
() => checkClipboardData(expectedPattern));
|
||||
} catch (e) {
|
||||
failedClipboard(expectedPattern);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkSelectAll(view) {
|
||||
function* checkSelectAll(view) {
|
||||
info("Testing select-all copy");
|
||||
|
||||
let contentDoc = view.styleDocument;
|
||||
|
@ -86,13 +85,12 @@ function checkSelectAll(view) {
|
|||
"font-size: 16px;[\\r\\n]+" +
|
||||
"font-variant-caps: small-caps;[\\r\\n]*";
|
||||
|
||||
return waitForClipboard(() => {
|
||||
fireCopyEvent(prop);
|
||||
}, () => {
|
||||
return checkClipboardData(expectedPattern);
|
||||
}).then(() => {}, () => {
|
||||
try {
|
||||
yield waitForClipboard(() => fireCopyEvent(prop),
|
||||
() => checkClipboardData(expectedPattern));
|
||||
} catch (e) {
|
||||
failedClipboard(expectedPattern);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function checkClipboardData(expectedPattern) {
|
||||
|
|
|
@ -1,137 +1,31 @@
|
|||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
let Cu = Components.utils;
|
||||
let {gDevTools} = Cu.import("resource://devtools/client/framework/gDevTools.jsm", {});
|
||||
let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
|
||||
let {TargetFactory} = require("devtools/client/framework/target");
|
||||
let {CssComputedView} =
|
||||
require("devtools/client/inspector/computed/computed");
|
||||
let DevToolsUtils = require("devtools/shared/DevToolsUtils");
|
||||
let promise = require("promise");
|
||||
let {console} =
|
||||
Components.utils.import("resource://gre/modules/Console.jsm", {});
|
||||
// Import the inspector's head.js first (which itself imports shared-head.js).
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
|
||||
this);
|
||||
|
||||
// All tests are asynchronous
|
||||
waitForExplicitFinish();
|
||||
|
||||
const TEST_URL_ROOT =
|
||||
"http://example.com/browser/devtools/client/inspector/computed/test/";
|
||||
const TEST_URL_ROOT_SSL =
|
||||
"https://example.com/browser/devtools/client/inspector/computed/test/";
|
||||
const ROOT_TEST_DIR = getRootDirectory(gTestPath);
|
||||
const FRAME_SCRIPT_URL = ROOT_TEST_DIR + "doc_frame_script.js";
|
||||
|
||||
// Auto clean-up when a test ends
|
||||
registerCleanupFunction(function*() {
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
yield gDevTools.closeToolbox(target);
|
||||
|
||||
while (gBrowser.tabs.length > 1) {
|
||||
gBrowser.removeCurrentTab();
|
||||
}
|
||||
});
|
||||
|
||||
// Uncomment this pref to dump all devtools emitted events to the console.
|
||||
// Services.prefs.setBoolPref("devtools.dump.emit", true);
|
||||
|
||||
// Set the testing flag on gDevTools and reset it when the test ends
|
||||
DevToolsUtils.testing = true;
|
||||
registerCleanupFunction(() => DevToolsUtils.testing = false);
|
||||
|
||||
// Clean-up all prefs that might have been changed during a test run
|
||||
// (safer here because if the test fails, then the pref is never reverted)
|
||||
registerCleanupFunction(() => {
|
||||
Services.prefs.clearUserPref("devtools.inspector.activeSidebar");
|
||||
Services.prefs.clearUserPref("devtools.dump.emit");
|
||||
Services.prefs.clearUserPref("devtools.defaultColorUnit");
|
||||
});
|
||||
|
||||
/**
|
||||
* The functions found below are here to ease test development and maintenance.
|
||||
* Most of these functions are stateless and will require some form of context
|
||||
* (the instance of the current toolbox, or inspector panel for instance).
|
||||
*
|
||||
* Most of these functions are async too and return promises.
|
||||
*
|
||||
* All tests should follow the following pattern:
|
||||
*
|
||||
* add_task(function*() {
|
||||
* yield addTab(TEST_URI);
|
||||
* let {toolbox, inspector, view} = yield openComputedView();
|
||||
*
|
||||
* yield selectNode("#test", inspector);
|
||||
* yield someAsyncTestFunction(view);
|
||||
* });
|
||||
*
|
||||
* add_task is the way to define the testcase in the test file. It accepts
|
||||
* a single generator-function argument.
|
||||
* The generator function should yield any async call.
|
||||
*
|
||||
* There is no need to clean tabs up at the end of a test as this is done
|
||||
* automatically.
|
||||
*
|
||||
* It is advised not to store any references on the global scope. There
|
||||
* shouldn't be a need to anyway. Thanks to add_task, test steps, even
|
||||
* though asynchronous, can be described in a nice flat way, and
|
||||
* if/for/while/... control flow can be used as in sync code, making it
|
||||
* possible to write the outline of the test case all in add_task, and delegate
|
||||
* actual processing and assertions to other functions.
|
||||
* Open the toolbox, with the inspector tool visible, and the computed-view
|
||||
* sidebar tab selected.
|
||||
* @return a promise that resolves when the inspector is ready and the computed
|
||||
* view is visible and ready
|
||||
*/
|
||||
|
||||
/* *********************************************
|
||||
* UTILS
|
||||
* *********************************************
|
||||
* General test utilities.
|
||||
* Add new tabs, open the toolbox and switch to the various panels, select
|
||||
* nodes, get node references, ...
|
||||
*/
|
||||
|
||||
/**
|
||||
* Add a new test tab in the browser and load the given url.
|
||||
*
|
||||
* @param {String} url
|
||||
* The url to be loaded in the new tab
|
||||
* @return a promise that resolves to the tab object when the url is loaded
|
||||
*/
|
||||
function addTab(url) {
|
||||
info("Adding a new tab with URL: '" + url + "'");
|
||||
let def = promise.defer();
|
||||
|
||||
window.focus();
|
||||
|
||||
let tab = window.gBrowser.selectedTab = window.gBrowser.addTab(url);
|
||||
let browser = tab.linkedBrowser;
|
||||
|
||||
info("Loading the helper frame script " + FRAME_SCRIPT_URL);
|
||||
browser.messageManager.loadFrameScript(FRAME_SCRIPT_URL, false);
|
||||
|
||||
browser.addEventListener("load", function onload() {
|
||||
browser.removeEventListener("load", onload, true);
|
||||
info("URL '" + url + "' loading complete");
|
||||
|
||||
def.resolve(tab);
|
||||
}, true);
|
||||
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Simple DOM node accesor function that takes either a node or a string css
|
||||
* selector as argument and returns the corresponding node
|
||||
*
|
||||
* @param {String|DOMNode} nodeOrSelector
|
||||
* @return {DOMNode|CPOW} Note that in e10s mode a CPOW object is returned which
|
||||
* doesn't implement *all* of the DOMNode's properties
|
||||
*/
|
||||
function getNode(nodeOrSelector) {
|
||||
info("Getting the node for '" + nodeOrSelector + "'");
|
||||
return typeof nodeOrSelector === "string" ?
|
||||
content.document.querySelector(nodeOrSelector) :
|
||||
nodeOrSelector;
|
||||
function openComputedView() {
|
||||
return openInspectorSidebarTab("computedview").then(objects => {
|
||||
return {
|
||||
toolbox: objects.toolbox,
|
||||
inspector: objects.inspector,
|
||||
view: objects.view.view
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -146,187 +40,6 @@ function getNodeFront(selector, {walker}) {
|
|||
return walker.querySelector(walker.rootNode, selector);
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the inspector's current selection to a node or to the first match of the
|
||||
* given css selector.
|
||||
*
|
||||
* @param {String|NodeFront} data
|
||||
* The node to select
|
||||
* @param {InspectorPanel} inspector
|
||||
* The instance of InspectorPanel currently loaded in the toolbox
|
||||
* @param {String} reason
|
||||
* Defaults to "test" which instructs the inspector not
|
||||
* to highlight the node upon selection
|
||||
* @return {Promise} Resolves when the inspector is updated with the new node
|
||||
*/
|
||||
var selectNode = Task.async(function*(data, inspector, reason="test") {
|
||||
info("Selecting the node for '" + data + "'");
|
||||
let nodeFront = data;
|
||||
if (!data._form) {
|
||||
nodeFront = yield getNodeFront(data, inspector);
|
||||
}
|
||||
let updated = inspector.once("inspector-updated");
|
||||
inspector.selection.setNodeFront(nodeFront, reason);
|
||||
yield updated;
|
||||
});
|
||||
|
||||
/**
|
||||
* Open the toolbox, with the inspector tool visible.
|
||||
*
|
||||
* @return a promise that resolves when the inspector is ready
|
||||
*/
|
||||
var openInspector = Task.async(function*() {
|
||||
info("Opening the inspector");
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
|
||||
let inspector, toolbox;
|
||||
|
||||
// Checking if the toolbox and the inspector are already loaded
|
||||
// The inspector-updated event should only be waited for if the inspector
|
||||
// isn't loaded yet
|
||||
toolbox = gDevTools.getToolbox(target);
|
||||
if (toolbox) {
|
||||
inspector = toolbox.getPanel("inspector");
|
||||
if (inspector) {
|
||||
info("Toolbox and inspector already open");
|
||||
return {
|
||||
toolbox: toolbox,
|
||||
inspector: inspector
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
info("Opening the toolbox");
|
||||
toolbox = yield gDevTools.showToolbox(target, "inspector");
|
||||
yield waitForToolboxFrameFocus(toolbox);
|
||||
inspector = toolbox.getPanel("inspector");
|
||||
|
||||
info("Waiting for the inspector to update");
|
||||
yield inspector.once("inspector-updated");
|
||||
|
||||
return {
|
||||
toolbox: toolbox,
|
||||
inspector: inspector
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Wait for the toolbox frame to receive focus after it loads
|
||||
*
|
||||
* @param {Toolbox} toolbox
|
||||
* @return a promise that resolves when focus has been received
|
||||
*/
|
||||
function waitForToolboxFrameFocus(toolbox) {
|
||||
info("Making sure that the toolbox's frame is focused");
|
||||
let def = promise.defer();
|
||||
let win = toolbox.frame.contentWindow;
|
||||
waitForFocus(def.resolve, win);
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the toolbox, with the inspector tool visible, and the sidebar that
|
||||
* corresponds to the given id selected
|
||||
*
|
||||
* @return a promise that resolves when the inspector is ready and the sidebar
|
||||
* view is visible and ready
|
||||
*/
|
||||
var openInspectorSideBar = Task.async(function*(id) {
|
||||
let {toolbox, inspector} = yield openInspector();
|
||||
|
||||
if (!hasSideBarTab(inspector, id)) {
|
||||
info("Waiting for the " + id + " sidebar to be ready");
|
||||
yield inspector.sidebar.once(id + "-ready");
|
||||
}
|
||||
|
||||
info("Selecting the " + id + " sidebar");
|
||||
inspector.sidebar.select(id);
|
||||
|
||||
return {
|
||||
toolbox: toolbox,
|
||||
inspector: inspector,
|
||||
view: inspector.sidebar.getWindowForTab(id)[id].view
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Open the toolbox, with the inspector tool visible, and the computed-view
|
||||
* sidebar tab selected.
|
||||
*
|
||||
* @return a promise that resolves when the inspector is ready and the computed
|
||||
* view is visible and ready
|
||||
*/
|
||||
function openComputedView() {
|
||||
return openInspectorSideBar("computedview");
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for eventName on target to be delivered a number of times.
|
||||
*
|
||||
* @param {Object} target
|
||||
* An observable object that either supports on/off or
|
||||
* addEventListener/removeEventListener
|
||||
* @param {String} eventName
|
||||
* @param {Number} numTimes
|
||||
* Number of deliveries to wait for.
|
||||
* @param {Boolean} useCapture
|
||||
* Optional, for addEventListener/removeEventListener
|
||||
* @return A promise that resolves when the event has been handled
|
||||
*/
|
||||
function waitForNEvents(target, eventName, numTimes, useCapture = false) {
|
||||
info("Waiting for event: '" + eventName + "' on " + target + ".");
|
||||
|
||||
let deferred = promise.defer();
|
||||
let count = 0;
|
||||
|
||||
for (let [add, remove] of [
|
||||
["addEventListener", "removeEventListener"],
|
||||
["addListener", "removeListener"],
|
||||
["on", "off"]
|
||||
]) {
|
||||
if ((add in target) && (remove in target)) {
|
||||
target[add](eventName, function onEvent(...aArgs) {
|
||||
if (++count == numTimes) {
|
||||
target[remove](eventName, onEvent, useCapture);
|
||||
deferred.resolve.apply(deferred, aArgs);
|
||||
}
|
||||
}, useCapture);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for eventName on target.
|
||||
*
|
||||
* @param {Object} target
|
||||
* An observable object that either supports on/off or
|
||||
* addEventListener/removeEventListener
|
||||
* @param {String} eventName
|
||||
* @param {Boolean} useCapture
|
||||
* Optional, for addEventListener/removeEventListener
|
||||
* @return A promise that resolves when the event has been handled
|
||||
*/
|
||||
function once(target, eventName, useCapture=false) {
|
||||
return waitForNEvents(target, eventName, 1, useCapture);
|
||||
}
|
||||
|
||||
/**
|
||||
* This shouldn't be used in the tests, but is useful when writing new tests or
|
||||
* debugging existing tests in order to introduce delays in the test steps
|
||||
*
|
||||
* @param {Number} ms
|
||||
* The time to wait
|
||||
* @return A promise that resolves when the time is passed
|
||||
*/
|
||||
function wait(ms) {
|
||||
let def = promise.defer();
|
||||
content.setTimeout(def.resolve, ms);
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listen for a new tab to open and return a promise that resolves when one
|
||||
* does and completes the load event.
|
||||
|
@ -343,23 +56,6 @@ var waitForTab = Task.async(function*() {
|
|||
return tab;
|
||||
});
|
||||
|
||||
/**
|
||||
* @see SimpleTest.waitForClipboard
|
||||
*
|
||||
* @param {Function} setup
|
||||
* Function to execute before checking for the
|
||||
* clipboard content
|
||||
* @param {String|Boolean} expected
|
||||
* An expected string or validator function
|
||||
* @return a promise that resolves when the expected string has been found or
|
||||
* the validator function has returned true, rejects otherwise.
|
||||
*/
|
||||
function waitForClipboard(setup, expected) {
|
||||
let def = promise.defer();
|
||||
SimpleTest.waitForClipboard(expected, setup, def.resolve, def.reject);
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch the copy event on the given element
|
||||
*/
|
||||
|
@ -369,18 +65,6 @@ function fireCopyEvent(element) {
|
|||
element.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the inspector's sidebar corresponding to the given id already
|
||||
* exists
|
||||
*
|
||||
* @param {InspectorPanel}
|
||||
* @param {String}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function hasSideBarTab(inspector, id) {
|
||||
return !!inspector.sidebar.getWindowForTab(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Simulate the key input for the given input in the window.
|
||||
*
|
||||
|
@ -395,13 +79,6 @@ function synthesizeKeys(input, win) {
|
|||
}
|
||||
}
|
||||
|
||||
/* *********************************************
|
||||
* COMPUTED-VIEW
|
||||
* *********************************************
|
||||
* Computed-view related utility functions.
|
||||
* Allows to get properties, links, expand properties, ...
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get references to the name and value span nodes corresponding to a given
|
||||
* property name in the computed-view
|
||||
|
@ -534,77 +211,3 @@ function getComputedViewLinkByIndex(view, index) {
|
|||
let links = view.styleDocument.querySelectorAll(".rule-link .link");
|
||||
return links[index];
|
||||
}
|
||||
|
||||
/* *********************************************
|
||||
* STYLE-EDITOR
|
||||
* *********************************************
|
||||
* Style-editor related utility functions.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Wait for the toolbox to emit the styleeditor-selected event and when done
|
||||
* wait for the stylesheet identified by href to be loaded in the stylesheet
|
||||
* editor
|
||||
*
|
||||
* @param {Toolbox} toolbox
|
||||
* @param {String} href
|
||||
* Optional, if not provided, wait for the first editor to be ready
|
||||
* @return a promise that resolves to the editor when the stylesheet editor is
|
||||
* ready
|
||||
*/
|
||||
function waitForStyleEditor(toolbox, href) {
|
||||
let def = promise.defer();
|
||||
|
||||
info("Waiting for the toolbox to switch to the styleeditor");
|
||||
toolbox.once("styleeditor-selected").then(() => {
|
||||
let panel = toolbox.getCurrentPanel();
|
||||
ok(panel && panel.UI, "Styleeditor panel switched to front");
|
||||
|
||||
// A helper that resolves the promise once it receives an editor that
|
||||
// matches the expected href. Returns false if the editor was not correct.
|
||||
let gotEditor = (event, editor) => {
|
||||
let currentHref = editor.styleSheet.href;
|
||||
if (!href || (href && currentHref.endsWith(href))) {
|
||||
info("Stylesheet editor selected");
|
||||
panel.UI.off("editor-selected", gotEditor);
|
||||
|
||||
editor.getSourceEditor().then(sourceEditor => {
|
||||
info("Stylesheet editor fully loaded");
|
||||
def.resolve(sourceEditor);
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
info("The editor was incorrect. Waiting for editor-selected event.");
|
||||
return false;
|
||||
};
|
||||
|
||||
// The expected editor may already be selected. Check the if the currently
|
||||
// selected editor is the expected one and if not wait for an
|
||||
// editor-selected event.
|
||||
if (!gotEditor("styleeditor-selected", panel.UI.selectedEditor)) {
|
||||
// The expected editor is not selected (yet). Wait for it.
|
||||
panel.UI.on("editor-selected", gotEditor);
|
||||
}
|
||||
});
|
||||
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reload the current page and wait for the inspector to be initialized after
|
||||
* the navigation
|
||||
*
|
||||
* @param {InspectorPanel} inspector
|
||||
* The instance of InspectorPanel currently loaded in the toolbox
|
||||
* @return a promise that resolves after page reload and inspector
|
||||
* initialization
|
||||
*/
|
||||
function reloadPage(inspector) {
|
||||
let onNewRoot = inspector.once("new-root");
|
||||
content.location.reload();
|
||||
return onNewRoot.then(() => {
|
||||
inspector.markup._waitForChildren();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -5,23 +5,23 @@
|
|||
|
||||
requestLongerTimeout(2);
|
||||
|
||||
const TEST_URI = BASE_URI + "browser_fontinspector.html";
|
||||
const TEST_URI = URL_ROOT + "browser_fontinspector.html";
|
||||
const FONTS = [
|
||||
{name: "Ostrich Sans Medium", remote: true, url: BASE_URI + "ostrich-regular.ttf",
|
||||
{name: "Ostrich Sans Medium", remote: true, url: URL_ROOT + "ostrich-regular.ttf",
|
||||
format: "truetype", cssName: "bar"},
|
||||
{name: "Ostrich Sans Black", remote: true, url: BASE_URI + "ostrich-black.ttf",
|
||||
{name: "Ostrich Sans Black", remote: true, url: URL_ROOT + "ostrich-black.ttf",
|
||||
format: "", cssName: "bar"},
|
||||
{name: "Ostrich Sans Black", remote: true, url: BASE_URI + "ostrich-black.ttf",
|
||||
{name: "Ostrich Sans Black", remote: true, url: URL_ROOT + "ostrich-black.ttf",
|
||||
format: "", cssName: "bar"},
|
||||
{name: "Ostrich Sans Medium", remote: true, url: BASE_URI + "ostrich-regular.ttf",
|
||||
{name: "Ostrich Sans Medium", remote: true, url: URL_ROOT + "ostrich-regular.ttf",
|
||||
format: "", cssName: "barnormal"},
|
||||
];
|
||||
|
||||
add_task(function*() {
|
||||
let { inspector, fontInspector } = yield openFontInspectorForURL(TEST_URI);
|
||||
ok(!!fontInspector, "Font inspector document is alive.");
|
||||
let { inspector, view } = yield openFontInspectorForURL(TEST_URI);
|
||||
ok(!!view, "Font inspector document is alive.");
|
||||
|
||||
let viewDoc = fontInspector.chromeDoc;
|
||||
let viewDoc = view.chromeDoc;
|
||||
|
||||
yield testBodyFonts(inspector, viewDoc);
|
||||
yield testDivFonts(inspector, viewDoc);
|
||||
|
|
|
@ -6,11 +6,11 @@
|
|||
// Test that correct previews are shown if the text is edited after 'Show all'
|
||||
// button is pressed.
|
||||
|
||||
const TEST_URI = BASE_URI + "browser_fontinspector.html";
|
||||
const TEST_URI = URL_ROOT + "browser_fontinspector.html";
|
||||
|
||||
add_task(function*() {
|
||||
let { inspector, fontInspector } = yield openFontInspectorForURL(TEST_URI);
|
||||
let viewDoc = fontInspector.chromeDoc;
|
||||
let { inspector, view } = yield openFontInspectorForURL(TEST_URI);
|
||||
let viewDoc = view.chromeDoc;
|
||||
|
||||
info("Selecting a node that doesn't contain all document fonts.");
|
||||
yield selectNode(".normal-text", inspector);
|
||||
|
@ -36,7 +36,7 @@ add_task(function*() {
|
|||
"The .normal-text didn't show all fonts.");
|
||||
|
||||
info("Editing the preview text.");
|
||||
yield updatePreviewText(fontInspector, "The quick brown");
|
||||
yield updatePreviewText(view, "The quick brown");
|
||||
|
||||
let numPreviews = viewDoc.querySelectorAll("#all-fonts .font-preview").length;
|
||||
is(numPreviews, allFontsNumPreviews,
|
||||
|
|
|
@ -7,25 +7,25 @@
|
|||
// exact preview images because they are drawn on a canvas causing them to vary
|
||||
// between systems, platforms and software versions.
|
||||
|
||||
const TEST_URI = BASE_URI + "browser_fontinspector.html";
|
||||
const TEST_URI = URL_ROOT + "browser_fontinspector.html";
|
||||
|
||||
add_task(function*() {
|
||||
let { inspector, fontInspector } = yield openFontInspectorForURL(TEST_URI);
|
||||
let viewDoc = fontInspector.chromeDoc;
|
||||
let {view} = yield openFontInspectorForURL(TEST_URI);
|
||||
let viewDoc = view.chromeDoc;
|
||||
|
||||
let previews = viewDoc.querySelectorAll("#all-fonts .font-preview");
|
||||
let initialPreviews = [...previews].map(p => p.src);
|
||||
|
||||
info("Typing 'Abc' to check that the reference previews are correct.");
|
||||
yield updatePreviewText(fontInspector, "Abc");
|
||||
yield updatePreviewText(view, "Abc");
|
||||
checkPreviewImages(viewDoc, initialPreviews, true);
|
||||
|
||||
info("Typing something else to the preview box.");
|
||||
yield updatePreviewText(fontInspector, "The quick brown");
|
||||
yield updatePreviewText(view, "The quick brown");
|
||||
checkPreviewImages(viewDoc, initialPreviews, false);
|
||||
|
||||
info("Blanking the input to restore default previews.");
|
||||
yield updatePreviewText(fontInspector, "");
|
||||
yield updatePreviewText(view, "");
|
||||
checkPreviewImages(viewDoc, initialPreviews, true);
|
||||
});
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ requestLongerTimeout(2);
|
|||
|
||||
const { getTheme, setTheme } = require("devtools/client/shared/theme");
|
||||
|
||||
const TEST_URI = BASE_URI + "browser_fontinspector.html";
|
||||
const TEST_URI = URL_ROOT + "browser_fontinspector.html";
|
||||
const originalTheme = getTheme();
|
||||
|
||||
registerCleanupFunction(() => {
|
||||
|
@ -18,8 +18,8 @@ registerCleanupFunction(() => {
|
|||
});
|
||||
|
||||
add_task(function* () {
|
||||
let { inspector, fontInspector } = yield openFontInspectorForURL(TEST_URI);
|
||||
let { chromeDoc: doc } = fontInspector;
|
||||
let { inspector, view } = yield openFontInspectorForURL(TEST_URI);
|
||||
let { chromeDoc: doc } = view;
|
||||
|
||||
yield selectNode(".normal-text", inspector);
|
||||
|
||||
|
|
|
@ -2,79 +2,21 @@
|
|||
/* 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";
|
||||
|
||||
// shared-head.js handles imports, constants, and utility functions
|
||||
Services.scriptloader.loadSubScript("chrome://mochitests/content/browser/devtools/client/framework/test/shared-head.js", this);
|
||||
|
||||
const BASE_URI = "http://mochi.test:8888/browser/devtools/client/inspector/fonts/test/";
|
||||
|
||||
/**
|
||||
* Open the toolbox, with the inspector tool visible.
|
||||
* @param {Function} cb Optional callback, if you don't want to use the returned
|
||||
* promise
|
||||
* @return a promise that resolves when the inspector is ready
|
||||
*/
|
||||
var openInspector = Task.async(function*(cb) {
|
||||
info("Opening the inspector");
|
||||
let target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
|
||||
let inspector, toolbox;
|
||||
|
||||
// Checking if the toolbox and the inspector are already loaded
|
||||
// The inspector-updated event should only be waited for if the inspector
|
||||
// isn't loaded yet
|
||||
toolbox = gDevTools.getToolbox(target);
|
||||
if (toolbox) {
|
||||
inspector = toolbox.getPanel("inspector");
|
||||
if (inspector) {
|
||||
info("Toolbox and inspector already open");
|
||||
if (cb) {
|
||||
return cb(inspector, toolbox);
|
||||
} else {
|
||||
return {
|
||||
toolbox: toolbox,
|
||||
inspector: inspector
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info("Opening the toolbox");
|
||||
toolbox = yield gDevTools.showToolbox(target, "inspector");
|
||||
yield waitForToolboxFrameFocus(toolbox);
|
||||
inspector = toolbox.getPanel("inspector");
|
||||
|
||||
info("Waiting for the inspector to update");
|
||||
yield inspector.once("inspector-updated");
|
||||
|
||||
if (cb) {
|
||||
return cb(inspector, toolbox);
|
||||
} else {
|
||||
return {
|
||||
toolbox: toolbox,
|
||||
inspector: inspector
|
||||
};
|
||||
}
|
||||
});
|
||||
// Import the inspector's head.js first (which itself imports shared-head.js).
|
||||
Services.scriptloader.loadSubScript(
|
||||
"chrome://mochitests/content/browser/devtools/client/inspector/test/head.js",
|
||||
this);
|
||||
|
||||
/**
|
||||
* Adds a new tab with the given URL, opens the inspector and selects the
|
||||
* font-inspector tab.
|
||||
*
|
||||
* @return Object
|
||||
* {
|
||||
* toolbox,
|
||||
* inspector,
|
||||
* fontInspector
|
||||
* }
|
||||
* @return {Promise} resolves to a {toolbox, inspector, view} object
|
||||
*/
|
||||
var openFontInspectorForURL = Task.async(function* (url) {
|
||||
info("Opening tab " + url);
|
||||
var openFontInspectorForURL = Task.async(function*(url) {
|
||||
yield addTab(url);
|
||||
|
||||
let { toolbox, inspector } = yield openInspector();
|
||||
let {toolbox, inspector} = yield openInspectorSidebarTab("fontinspector");
|
||||
|
||||
/**
|
||||
* Call selectNode to trigger font-inspector update so that we don't timeout
|
||||
|
@ -88,74 +30,29 @@ var openFontInspectorForURL = Task.async(function* (url) {
|
|||
* FontInspector.update that emits the 'fontinspector-updated' event.
|
||||
*/
|
||||
let onUpdated = inspector.once("fontinspector-updated");
|
||||
|
||||
yield selectNode("body", inspector);
|
||||
inspector.sidebar.select("fontinspector");
|
||||
|
||||
info("Waiting for font-inspector to update.");
|
||||
yield onUpdated;
|
||||
|
||||
info("Font Inspector ready.");
|
||||
|
||||
let { fontInspector } = inspector.sidebar.getWindowForTab("fontinspector");
|
||||
return {
|
||||
fontInspector,
|
||||
toolbox,
|
||||
inspector,
|
||||
toolbox
|
||||
view: inspector.sidebar.getWindowForTab("fontinspector").fontInspector
|
||||
};
|
||||
});
|
||||
|
||||
/**
|
||||
* Select a node in the inspector given its selector.
|
||||
*/
|
||||
var selectNode = Task.async(function*(selector, inspector, reason="test") {
|
||||
info("Selecting the node for '" + selector + "'");
|
||||
let nodeFront = yield getNodeFront(selector, inspector);
|
||||
let updated = inspector.once("inspector-updated");
|
||||
inspector.selection.setNodeFront(nodeFront, reason);
|
||||
yield updated;
|
||||
});
|
||||
|
||||
/**
|
||||
* Get the NodeFront for a given css selector, via the protocol
|
||||
* @param {String|NodeFront} selector
|
||||
* @param {InspectorPanel} inspector The instance of InspectorPanel currently
|
||||
* loaded in the toolbox
|
||||
* @return {Promise} Resolves to the NodeFront instance
|
||||
*/
|
||||
function getNodeFront(selector, {walker}) {
|
||||
if (selector._form) {
|
||||
return selector;
|
||||
}
|
||||
return walker.querySelector(walker.rootNode, selector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the toolbox frame to receive focus after it loads
|
||||
* @param {Toolbox} toolbox
|
||||
* @return a promise that resolves when focus has been received
|
||||
*/
|
||||
function waitForToolboxFrameFocus(toolbox) {
|
||||
info("Making sure that the toolbox's frame is focused");
|
||||
let def = promise.defer();
|
||||
let win = toolbox.frame.contentWindow;
|
||||
waitForFocus(def.resolve, win);
|
||||
return def.promise;
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the preview input field, types new text into it and waits for the
|
||||
* preview images to be updated.
|
||||
*
|
||||
* @param {FontInspector} fontInspector - The FontInspector instance.
|
||||
* @param {FontInspector} view - The FontInspector instance.
|
||||
* @param {String} text - The text to preview.
|
||||
*/
|
||||
function* updatePreviewText(fontInspector, text) {
|
||||
function* updatePreviewText(view, text) {
|
||||
info(`Changing the preview text to '${text}'`);
|
||||
|
||||
let doc = fontInspector.chromeDoc;
|
||||
let doc = view.chromeDoc;
|
||||
let input = doc.getElementById("preview-text-input");
|
||||
let update = fontInspector.inspector.once("fontinspector-updated");
|
||||
let update = view.inspector.once("fontinspector-updated");
|
||||
|
||||
info("Focusing the input field.");
|
||||
input.focus();
|
||||
|
|
|
@ -45,52 +45,29 @@ function selectAndHighlightNode(nodeOrSelector, inspector) {
|
|||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the inspector's sidebar corresponding to the given id already
|
||||
* exists
|
||||
* @param {InspectorPanel}
|
||||
* @param {String}
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function hasSideBarTab(inspector, id) {
|
||||
return !!inspector.sidebar.getWindowForTab(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open the toolbox, with the inspector tool visible, and the layout-view
|
||||
* sidebar tab selected.
|
||||
* @return a promise that resolves when the inspector is ready and the layout
|
||||
* view is visible and ready
|
||||
*/
|
||||
var openLayoutView = Task.async(function*() {
|
||||
let {toolbox, inspector} = yield openInspector();
|
||||
function openLayoutView() {
|
||||
return openInspectorSidebarTab("layoutview").then(objects => {
|
||||
// The actual highligher show/hide methods are mocked in layoutview tests.
|
||||
// The highlighter is tested in devtools/inspector/test.
|
||||
function mockHighlighter({highlighter}) {
|
||||
highlighter.showBoxModel = function(nodeFront, options) {
|
||||
return promise.resolve();
|
||||
};
|
||||
highlighter.hideBoxModel = function() {
|
||||
return promise.resolve();
|
||||
};
|
||||
}
|
||||
mockHighlighter(objects.toolbox);
|
||||
|
||||
// The actual highligher show/hide methods are mocked in layoutview tests.
|
||||
// The highlighter is tested in devtools/inspector/test.
|
||||
function mockHighlighter({highlighter}) {
|
||||
highlighter.showBoxModel = function(nodeFront, options) {
|
||||
return promise.resolve();
|
||||
};
|
||||
highlighter.hideBoxModel = function() {
|
||||
return promise.resolve();
|
||||
};
|
||||
}
|
||||
mockHighlighter(toolbox);
|
||||
|
||||
if (!hasSideBarTab(inspector, "layoutview")) {
|
||||
info("Waiting for the layoutview sidebar to be ready");
|
||||
yield inspector.sidebar.once("layoutview-ready");
|
||||
}
|
||||
|
||||
info("Selecting the layoutview sidebar");
|
||||
inspector.sidebar.select("layoutview");
|
||||
|
||||
return {
|
||||
toolbox: toolbox,
|
||||
inspector: inspector,
|
||||
view: inspector.sidebar.getWindowForTab("layoutview")["layoutview"]
|
||||
};
|
||||
});
|
||||
return objects;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for the layoutview-updated event.
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
"use strict";
|
||||
|
||||
// Test native anonymous content in the markupview.
|
||||
const TEST_URL = TEST_URL_ROOT + "doc_markup_anonymous.html";
|
||||
const TEST_URL = URL_ROOT + "doc_markup_anonymous.html";
|
||||
|
||||
add_task(function*() {
|
||||
let {inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
let {inspector} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
let pseudo = yield getNodeFront("#pseudo", inspector);
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ requestLongerTimeout(2);
|
|||
const TEST_URL = "chrome://devtools/content/scratchpad/scratchpad.xul";
|
||||
|
||||
add_task(function*() {
|
||||
let {inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
let {inspector} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
let toolbarbutton = yield getNodeFront("toolbarbutton", inspector);
|
||||
let children = yield inspector.walker.children(toolbarbutton);
|
||||
|
|
|
@ -7,12 +7,12 @@
|
|||
// Test shadow DOM content in the markupview.
|
||||
// Note that many features are not yet enabled, but basic listing
|
||||
// of elements should be working.
|
||||
const TEST_URL = TEST_URL_ROOT + "doc_markup_anonymous.html";
|
||||
const TEST_URL = URL_ROOT + "doc_markup_anonymous.html";
|
||||
|
||||
add_task(function*() {
|
||||
Services.prefs.setBoolPref("dom.webcomponents.enabled", true);
|
||||
|
||||
let {inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
let {inspector} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
let shadow = yield getNodeFront("#shadow", inspector.markup);
|
||||
let children = yield inspector.walker.children(shadow);
|
||||
|
|
|
@ -6,12 +6,12 @@
|
|||
|
||||
// Test native anonymous content in the markupview with devtools.inspector.showAllAnonymousContent
|
||||
// set to true
|
||||
const TEST_URL = TEST_URL_ROOT + "doc_markup_anonymous.html";
|
||||
const TEST_URL = URL_ROOT + "doc_markup_anonymous.html";
|
||||
|
||||
add_task(function*() {
|
||||
Services.prefs.setBoolPref("devtools.inspector.showAllAnonymousContent", true);
|
||||
|
||||
let {inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
let {inspector} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
let native = yield getNodeFront("#native", inspector);
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// The correctness and cycling of the suggestions is covered in the ruleview
|
||||
// tests.
|
||||
|
||||
const TEST_URL = TEST_URL_ROOT + "doc_markup_edit.html";
|
||||
const TEST_URL = URL_ROOT + "doc_markup_edit.html";
|
||||
// test data format :
|
||||
// [
|
||||
// what key to press,
|
||||
|
@ -65,7 +65,7 @@ const TEST_DATA = [
|
|||
|
||||
add_task(function*() {
|
||||
info("Opening the inspector on the test URL");
|
||||
let {inspector} = yield addTab(TEST_URL).then(openInspector);
|
||||
let {inspector} = yield openInspectorForURL(TEST_URL);
|
||||
|
||||
yield inspector.markup.expandAll();
|
||||
|
||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче