Merge MC -> JM

This commit is contained in:
Brian Hackett 2011-10-04 14:49:42 -07:00
commit 227285ac43
731 changed files with 16766 additions and 10301 deletions

View File

@ -324,6 +324,16 @@ private:
};
/**
* Accessible widget selection change event.
*/
class AccSelectionChangeEvent : public AccEvent
{
public:
};
/**
* Accessible table change event.
*/

View File

@ -180,13 +180,16 @@ HTMLLabelIterator::Next()
nsAccessible* walkUp = mAcc->Parent();
while (walkUp && !walkUp->IsDoc()) {
nsIContent* walkUpElm = walkUp->GetContent();
if (walkUpElm->Tag() == nsGkAtoms::label) {
mLabelFilter = eSkipAncestorLabel; // prevent infinite loop
return walkUp;
}
if (walkUpElm->IsHTML()) {
if (walkUpElm->Tag() == nsGkAtoms::label &&
!walkUpElm->HasAttr(kNameSpaceID_None, nsGkAtoms::_for)) {
mLabelFilter = eSkipAncestorLabel; // prevent infinite loop
return walkUp;
}
if (walkUpElm->Tag() == nsGkAtoms::form)
break;
if (walkUpElm->Tag() == nsGkAtoms::form)
break;
}
walkUp = walkUp->Parent();
}

View File

@ -49,6 +49,18 @@ namespace statistics {
inline void A11yInitialized()
{ Telemetry::Accumulate(Telemetry::A11Y_INSTANTIATED, true); }
/**
* Report that ISimpleDOM* has been used.
*/
inline void ISimpleDOMUsed()
{ Telemetry::Accumulate(Telemetry::ISIMPLE_DOM_USAGE, 1); }
/**
* Report that IAccessibleTable has been used.
*/
inline void IAccessibleTableUsed()
{ Telemetry::Accumulate(Telemetry::IACCESSIBLE_TABLE_USAGE, 1); }
} // namespace statistics
} // namespace a11y
} // namespace mozilla

View File

@ -961,10 +961,23 @@ nsAccessibilityService::GetOrCreateAccessible(nsINode* aNode,
// elements return the image frame as their primary frame. The main content
// for the image frame is the image content. If the frame is not an image
// frame or the node is not an area element then null is returned.
// This setup will change when bug 135040 is fixed.
return GetAreaAccessible(weakFrame.GetFrame(), aNode, aWeakShell);
// This setup will change when bug 135040 is fixed. Make sure we don't
// create area accessible here. Hopefully assertion below will handle that.
#ifdef DEBUG
nsImageFrame* imageFrame = do_QueryFrame(weakFrame.GetFrame());
NS_ASSERTION(imageFrame && content->IsHTML() && content->Tag() == nsGkAtoms::area,
"Unknown case of not main content for the frame!");
#endif
return nsnull;
}
#ifdef DEBUG
nsImageFrame* imageFrame = do_QueryFrame(weakFrame.GetFrame());
NS_ASSERTION(!imageFrame || !content->IsHTML() || content->Tag() != nsGkAtoms::area,
"Image map manages the area accessible creation!");
#endif
nsDocAccessible* docAcc =
GetAccService()->GetDocAccessible(aNode->GetOwnerDoc());
if (!docAcc) {
@ -1278,50 +1291,6 @@ nsAccessibilityService::HasUniversalAriaProperty(nsIContent *aContent)
nsAccUtils::HasDefinedARIAToken(aContent, nsGkAtoms::aria_relevant);
}
nsAccessible*
nsAccessibilityService::GetAreaAccessible(nsIFrame* aImageFrame,
nsINode* aAreaNode,
nsIWeakReference* aWeakShell,
nsAccessible** aImageAccessible)
{
// Check if frame is an image frame, and content is <area>.
nsImageFrame *imageFrame = do_QueryFrame(aImageFrame);
if (!imageFrame)
return nsnull;
nsCOMPtr<nsIDOMHTMLAreaElement> areaElmt = do_QueryInterface(aAreaNode);
if (!areaElmt)
return nsnull;
// Try to get image map accessible from the global cache or create it
// if failed.
nsRefPtr<nsAccessible> image =
GetAccessibleInWeakShell(aImageFrame->GetContent(), aWeakShell);
if (!image) {
image = CreateHTMLImageAccessible(aImageFrame->GetContent(),
aImageFrame->PresContext()->PresShell());
nsDocAccessible* document =
GetAccService()->GetDocAccessible(aAreaNode->GetOwnerDoc());
if (!document) {
NS_NOTREACHED("No document for accessible being created!");
return nsnull;
}
if (!document->BindToDocument(image, nsnull))
return nsnull;
}
if (aImageAccessible)
*aImageAccessible = image;
// Make sure <area> accessible children of the image map are cached so
// that they should be available in global cache.
image->EnsureChildren();
return GetAccessibleInWeakShell(aAreaNode, aWeakShell);
}
already_AddRefed<nsAccessible>
nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
nsIWeakReference* aWeakShell)

View File

@ -229,18 +229,6 @@ private:
*/
void Shutdown();
/**
* Return accessible for HTML area element associated with an image map.
*
* @param aImageFrame [in] image frame
* @param aAreaNode [in] area node
* @param aWeakShell [in] presshell of image frame
* @param aImageAccessible [out, optional] image accessible, isn't addrefed
*/
nsAccessible* GetAreaAccessible(nsIFrame* aImageFrame, nsINode* aAreaNode,
nsIWeakReference* aWeakShell,
nsAccessible** aImageAccessible = nsnull);
/**
* Create accessible for the element implementing nsIAccessibleProvider
* interface.

View File

@ -387,15 +387,10 @@ nsRootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
NS_ASSERTION(targetDocument, "No document while accessible is in document?!");
nsINode* targetNode = accessible->GetNode();
nsIContent* targetContent = targetNode->IsElement() ?
targetNode->AsElement() : nsnull;
nsIContent* origTargetContent = origTargetNode->IsElement() ?
origTargetNode->AsElement() : nsnull;
#ifdef MOZ_XUL
bool isTree = targetContent ?
targetContent->NodeInfo()->Equals(nsGkAtoms::tree, kNameSpaceID_XUL) :
PR_FALSE;
bool isTree = targetNode->IsElement() &&
targetNode->AsElement()->NodeInfo()->Equals(nsGkAtoms::tree, kNameSpaceID_XUL);
if (isTree) {
nsRefPtr<nsXULTreeAccessible> treeAcc = do_QueryObject(accessible);

View File

@ -278,8 +278,8 @@ nsHTMLButtonAccessible::NativeState()
{
PRUint64 state = nsHyperTextAccessibleWrap::NativeState();
if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
nsGkAtoms::submit, eIgnoreCase))
nsEventStates elmState = mContent->AsElement()->State();
if (elmState.HasState(NS_EVENT_STATE_DEFAULT))
state |= states::DEFAULT;
return state;
@ -382,8 +382,8 @@ nsHTML4ButtonAccessible::NativeState()
state |= states::FOCUSABLE;
if (mContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
nsGkAtoms::submit, eIgnoreCase))
nsEventStates elmState = mContent->AsElement()->State();
if (elmState.HasState(NS_EVENT_STATE_DEFAULT))
state |= states::DEFAULT;
return state;

View File

@ -1360,7 +1360,7 @@ nsHTMLTableAccessible::IsProbablyForLayout(bool *aIsProbablyForLayout)
#define RETURN_LAYOUT_ANSWER(isLayout, heuristic) { *aIsProbablyForLayout = isLayout; return NS_OK; }
#endif
*aIsProbablyForLayout = PR_FALSE;
*aIsProbablyForLayout = false;
if (IsDefunct())
return NS_ERROR_FAILURE;
@ -1369,7 +1369,7 @@ nsHTMLTableAccessible::IsProbablyForLayout(bool *aIsProbablyForLayout)
if (docAccessible) {
PRUint64 docState = docAccessible->State();
if (docState & states::EDITABLE) { // Need to see all elements while document is being edited
RETURN_LAYOUT_ANSWER(PR_FALSE, "In editable document");
RETURN_LAYOUT_ANSWER(false, "In editable document");
}
}
@ -1377,45 +1377,77 @@ nsHTMLTableAccessible::IsProbablyForLayout(bool *aIsProbablyForLayout)
// but for which we still expose table semantics (treegrid, for example).
bool hasNonTableRole = (Role() != nsIAccessibleRole::ROLE_TABLE);
if (hasNonTableRole) {
RETURN_LAYOUT_ANSWER(PR_FALSE, "Has role attribute");
RETURN_LAYOUT_ANSWER(false, "Has role attribute");
}
if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::role)) {
// Role attribute is present, but overridden roles have already been dealt with.
// Only landmarks and other roles that don't override the role from native
// markup are left to deal with here.
RETURN_LAYOUT_ANSWER(PR_FALSE, "Has role attribute, weak role, and role is table");
RETURN_LAYOUT_ANSWER(false, "Has role attribute, weak role, and role is table");
}
// Check for legitimate data table elements or attributes
// Check for legitimate data table attributes.
nsAutoString summary;
if ((mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) &&
!summary.IsEmpty()) ||
HasDescendant(NS_LITERAL_STRING("caption"), PR_FALSE) ||
HasDescendant(NS_LITERAL_STRING("th")) ||
HasDescendant(NS_LITERAL_STRING("thead")) ||
HasDescendant(NS_LITERAL_STRING("tfoot")) ||
HasDescendant(NS_LITERAL_STRING("colgroup"))) {
RETURN_LAYOUT_ANSWER(PR_FALSE, "Has caption, summary, th, thead, tfoot or colgroup -- legitimate table structures");
if (mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::summary, summary) &&
!summary.IsEmpty())
RETURN_LAYOUT_ANSWER(false, "Has summary -- legitimate table structures");
// Check for legitimate data table elements.
nsAccessible* caption = FirstChild();
if (caption && caption->Role() == nsIAccessibleRole::ROLE_CAPTION &&
caption->HasChildren()) {
RETURN_LAYOUT_ANSWER(false,
"Not empty caption -- legitimate table structures");
}
for (nsIContent* childElm = mContent->GetFirstChild(); childElm;
childElm = childElm->GetNextSibling()) {
if (!childElm->IsHTML())
continue;
if (childElm->Tag() == nsGkAtoms::col ||
childElm->Tag() == nsGkAtoms::colgroup ||
childElm->Tag() == nsGkAtoms::tfoot ||
childElm->Tag() == nsGkAtoms::thead) {
RETURN_LAYOUT_ANSWER(false,
"Has col, colgroup, tfoot or thead -- legitimate table structures");
}
if (childElm->Tag() == nsGkAtoms::tbody) {
for (nsIContent* rowElm = childElm->GetFirstChild(); rowElm;
rowElm = rowElm->GetNextSibling()) {
if (rowElm->IsHTML() && rowElm->Tag() == nsGkAtoms::tr) {
for (nsIContent* cellElm = rowElm->GetFirstChild(); cellElm;
cellElm = cellElm->GetNextSibling()) {
if (cellElm->IsHTML() && cellElm->Tag() == nsGkAtoms::th) {
RETURN_LAYOUT_ANSWER(false,
"Has th -- legitimate table structures");
}
}
}
}
}
}
if (HasDescendant(NS_LITERAL_STRING("table"))) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "Has a nested table within it");
RETURN_LAYOUT_ANSWER(true, "Has a nested table within it");
}
// If only 1 column or only 1 row, it's for layout
PRInt32 columns, rows;
GetColumnCount(&columns);
if (columns <=1) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "Has only 1 column");
RETURN_LAYOUT_ANSWER(true, "Has only 1 column");
}
GetRowCount(&rows);
if (rows <=1) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "Has only 1 row");
RETURN_LAYOUT_ANSWER(true, "Has only 1 row");
}
// Check for many columns
if (columns >= 5) {
RETURN_LAYOUT_ANSWER(PR_FALSE, ">=5 columns");
RETURN_LAYOUT_ANSWER(false, ">=5 columns");
}
// Now we know there are 2-4 columns and 2 or more rows
@ -1434,7 +1466,7 @@ nsHTMLTableAccessible::IsProbablyForLayout(bool *aIsProbablyForLayout)
nsMargin border;
cellFrame->GetBorder(border);
if (border.top && border.bottom && border.left && border.right) {
RETURN_LAYOUT_ANSWER(PR_FALSE, "Has nonzero border-width on table cell");
RETURN_LAYOUT_ANSWER(false, "Has nonzero border-width on table cell");
}
/**
@ -1462,21 +1494,21 @@ nsHTMLTableAccessible::IsProbablyForLayout(bool *aIsProbablyForLayout)
lastRowColor = color;
styleDecl->GetPropertyValue(NS_LITERAL_STRING("background-color"), color);
if (rowCount > 0 && PR_FALSE == lastRowColor.Equals(color)) {
RETURN_LAYOUT_ANSWER(PR_FALSE, "2 styles of row background color, non-bordered");
RETURN_LAYOUT_ANSWER(false, "2 styles of row background color, non-bordered");
}
}
// Check for many rows
const PRInt32 kMaxLayoutRows = 20;
if (rows > kMaxLayoutRows) { // A ton of rows, this is probably for data
RETURN_LAYOUT_ANSWER(PR_FALSE, ">= kMaxLayoutRows (20) and non-bordered");
RETURN_LAYOUT_ANSWER(false, ">= kMaxLayoutRows (20) and non-bordered");
}
// Check for very wide table
nsAutoString styledWidth;
GetComputedStyleValue(EmptyString(), NS_LITERAL_STRING("width"), styledWidth);
if (styledWidth.EqualsLiteral("100%")) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "<=4 columns and 100% width");
RETURN_LAYOUT_ANSWER(true, "<=4 columns and 100% width");
}
if (styledWidth.Find(NS_LITERAL_STRING("px"))) { // Hardcoded in pixels
nsIFrame *tableFrame = GetFrame();
@ -1494,24 +1526,24 @@ nsHTMLTableAccessible::IsProbablyForLayout(bool *aIsProbablyForLayout)
if (percentageOfDocWidth > 95) {
// 3-4 columns, no borders, not a lot of rows, and 95% of the doc's width
// Probably for layout
RETURN_LAYOUT_ANSWER(PR_TRUE, "<=4 columns, width hardcoded in pixels and 95% of document width");
RETURN_LAYOUT_ANSWER(true, "<=4 columns, width hardcoded in pixels and 95% of document width");
}
}
}
// Two column rules
if (rows * columns <= 10) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "2-4 columns, 10 cells or less, non-bordered");
RETURN_LAYOUT_ANSWER(true, "2-4 columns, 10 cells or less, non-bordered");
}
if (HasDescendant(NS_LITERAL_STRING("embed")) ||
HasDescendant(NS_LITERAL_STRING("object")) ||
HasDescendant(NS_LITERAL_STRING("applet")) ||
HasDescendant(NS_LITERAL_STRING("iframe"))) {
RETURN_LAYOUT_ANSWER(PR_TRUE, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements");
RETURN_LAYOUT_ANSWER(true, "Has no borders, and has iframe, object, applet or iframe, typical of advertisements");
}
RETURN_LAYOUT_ANSWER(PR_FALSE, "no layout factor strong enough, so will guess data");
RETURN_LAYOUT_ANSWER(false, "no layout factor strong enough, so will guess data");
}

View File

@ -49,10 +49,13 @@
#include "nsIWinAccessNode.h"
#include "nsAccessNodeWrap.h"
#include "nsWinUtils.h"
#include "Statistics.h"
#include "nsCOMPtr.h"
#include "nsString.h"
using namespace mozilla::a11y;
#define CANT_QUERY_ASSERTION_MSG \
"Subclass of CAccessibleTable doesn't implement nsIAccessibleTable"\
@ -64,6 +67,7 @@ CAccessibleTable::QueryInterface(REFIID iid, void** ppv)
*ppv = NULL;
if (IID_IAccessibleTable == iid) {
statistics::IAccessibleTableUsed();
*ppv = static_cast<IAccessibleTable*>(this);
(reinterpret_cast<IUnknown*>(*ppv))->AddRef();
return S_OK;

View File

@ -46,6 +46,7 @@
#include "nsCoreUtils.h"
#include "nsRootAccessible.h"
#include "nsWinUtils.h"
#include "Statistics.h"
#include "nsAttrName.h"
#include "nsIDocument.h"
@ -59,6 +60,7 @@
#include "mozilla/Preferences.h"
using namespace mozilla;
using namespace mozilla::a11y;
/// the accessible library and cached methods
HINSTANCE nsAccessNodeWrap::gmAccLib = nsnull;
@ -120,11 +122,14 @@ STDMETHODIMP nsAccessNodeWrap::QueryInterface(REFIID iid, void** ppv)
{
*ppv = nsnull;
if (IID_IUnknown == iid || IID_ISimpleDOMNode == iid)
if (IID_IUnknown == iid) {
*ppv = static_cast<ISimpleDOMNode*>(this);
if (nsnull == *ppv)
} else if (IID_ISimpleDOMNode == iid) {
statistics::ISimpleDOMUsed();
*ppv = static_cast<ISimpleDOMNode*>(this);
} else {
return E_NOINTERFACE; //iid not supported.
}
(reinterpret_cast<IUnknown*>(*ppv))->AddRef();
return S_OK;

View File

@ -43,6 +43,7 @@
#include "nsIAccessibilityService.h"
#include "nsRootAccessible.h"
#include "nsWinUtils.h"
#include "Statistics.h"
#include "nsIDocShell.h"
#include "nsIDocShellTreeNode.h"
@ -54,6 +55,8 @@
#include "nsIViewManager.h"
#include "nsIWebNavigation.h"
using namespace mozilla::a11y;
/* For documentation of the accessibility architecture,
* see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
*/
@ -91,12 +94,11 @@ STDMETHODIMP nsDocAccessibleWrap::QueryInterface(REFIID iid, void** ppv)
{
*ppv = NULL;
if (IID_ISimpleDOMDocument == iid)
*ppv = static_cast<ISimpleDOMDocument*>(this);
if (NULL == *ppv)
if (IID_ISimpleDOMDocument != iid)
return nsHyperTextAccessibleWrap::QueryInterface(iid, ppv);
statistics::ISimpleDOMUsed();
*ppv = static_cast<ISimpleDOMDocument*>(this);
(reinterpret_cast<IUnknown*>(*ppv))->AddRef();
return S_OK;
}

View File

@ -41,6 +41,7 @@
#include "nsCoreUtils.h"
#include "nsDocAccessible.h"
#include "Statistics.h"
#include "nsIFrame.h"
#include "nsFontMetrics.h"
#include "nsPresContext.h"
@ -48,6 +49,8 @@
#include "gfxFont.h"
using namespace mozilla::a11y;
////////////////////////////////////////////////////////////////////////////////
// nsTextAccessibleWrap Accessible
////////////////////////////////////////////////////////////////////////////////
@ -72,11 +75,14 @@ STDMETHODIMP nsTextAccessibleWrap::QueryInterface(REFIID iid, void** ppv)
{
*ppv = nsnull;
if (IID_IUnknown == iid || IID_ISimpleDOMText == iid)
if (IID_IUnknown == iid) {
*ppv = static_cast<ISimpleDOMText*>(this);
if (nsnull == *ppv)
} else if (IID_ISimpleDOMText == iid) {
statistics::ISimpleDOMUsed();
*ppv = static_cast<ISimpleDOMText*>(this);
} else {
return nsAccessibleWrap::QueryInterface(iid, ppv);
}
(reinterpret_cast<IUnknown*>(*ppv))->AddRef();
return S_OK;

View File

@ -817,22 +817,17 @@ Relation
nsXULTreeItemAccessibleBase::RelationByType(PRUint32 aType)
{
if (aType != nsIAccessibleRelation::RELATION_NODE_CHILD_OF)
return nsAccessible::RelationByType(aType);
return Relation();
Relation rel;
PRInt32 parentIndex;
if (!NS_SUCCEEDED(mTreeView->GetParentIndex(mRow, &parentIndex)))
return rel;
return Relation();
if (parentIndex == -1) {
rel.AppendTarget(mParent);
return rel;
}
if (parentIndex == -1)
return Relation(mParent);
nsRefPtr<nsXULTreeAccessible> treeAcc = do_QueryObject(mParent);
rel.AppendTarget(treeAcc->GetTreeItemAccessible(parentIndex));
return rel;
return Relation(treeAcc->GetTreeItemAccessible(parentIndex));
}
PRUint8

View File

@ -43,6 +43,7 @@
#include "nsAccUtils.h"
#include "nsDocAccessible.h"
#include "nsEventShell.h"
#include "Relation.h"
#include "States.h"
#include "nsITreeSelection.h"
@ -1237,6 +1238,12 @@ nsXULTreeGridCellAccessible::IndexInParent() const
return GetColumnIndex();
}
Relation
nsXULTreeGridCellAccessible::RelationByType(PRUint32 aType)
{
return Relation();
}
////////////////////////////////////////////////////////////////////////////////
// nsXULTreeGridCellAccessible: public implementation

View File

@ -163,6 +163,7 @@ public:
virtual nsAccessible* FocusedChild();
virtual nsresult GetAttributesInternal(nsIPersistentProperties *aAttributes);
virtual PRInt32 IndexInParent() const;
virtual Relation RelationByType(PRUint32 aType);
virtual PRUint32 NativeRole();
virtual PRUint64 NativeState();

View File

@ -76,6 +76,7 @@ _TEST_FILES =\
test_focus_menu.xul \
test_focus_name.html \
test_focus_selects.html \
test_focus_tabbox.xul \
test_focus_tree.xul \
test_menu.xul \
test_mutation.html \

View File

@ -106,9 +106,10 @@
gQueue.push(new synthContextMenu(editableDoc, new contextMenuChecker()));
gQueue.push(new synthDownKey(editableDoc, new focusContextMenuItemChecker()));
gQueue.push(new synthEscapeKey(editableDoc, new focusChecker(editableDoc)));
todo(false, "shift+tab doesn't issue the focus, see bug 684818");
//gQuee.push(new synthShiftTab("link", new focusChecker("link")));
if (LINUX)
todo(false, "shift tab from editable document Fails on linux!");
else
gQueue.push(new synthShiftTab("link", new focusChecker("link")));
gQueue.invoke(); // Will call SimpleTest.finish();
}

View File

@ -0,0 +1,94 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Tabbox focus testing">
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"/>
<script type="application/javascript"
src="../common.js" />
<script type="application/javascript"
src="../states.js" />
<script type="application/javascript"
src="../events.js" />
<script type="application/javascript">
//gA11yEventDumpID = "eventdump"; // debug stuff
//gA11yEventDumpToConsole = true; // debug stuff
var gQueue = null;
function doTests()
{
// Test focus events.
gQueue = new eventQueue();
gQueue.push(new synthClick("tab1", new focusChecker("tab1")));
gQueue.push(new synthTab("tab1", new focusChecker("checkbox1")));
gQueue.push(new synthKey("tab1", "VK_TAB", { ctrlKey: true },
new focusChecker("textbox")));
gQueue.push(new synthKey("tab2", "VK_TAB", { ctrlKey: true },
new focusChecker("tab3")));
gQueue.push(new synthKey("tab3", "VK_TAB", { ctrlKey: true },
new focusChecker("tab1")));
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTests);
</script>
<hbox flex="1" style="overflow: auto;">
<body xmlns="http://www.w3.org/1999/xhtml">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=370396"
title="Control+Tab to an empty tab panel in a tabbox causes focus to leave the tabbox">
Mozilla Bug 370396
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
</body>
<vbox flex="1">
<tabbox>
<tabs>
<tab id="tab1" label="Tab1" selected="true"/>
<tab id="tab2" label="Tab2" />
<tab id="tab3" label="Tab3" />
</tabs>
<tabpanels>
<tabpanel orient="vertical">
<groupbox orient="vertical">
<checkbox id="checkbox1" label="Monday" width="75"/>
<checkbox label="Tuesday" width="75"/>
<checkbox label="Wednesday" width="75"/>
<checkbox label="Thursday" width="75"/>
<checkbox label="Friday" width="75"/>
<checkbox label="Saturday" width="75"/>
<checkbox label="Sunday" width="75"/>
</groupbox>
<spacer style="height: 10px" />
<label value="Label After checkboxes" />
</tabpanel>
<tabpanel orient="vertical">
<textbox id="textbox" />
</tabpanel>
<tabpanel orient="vertical">
<description>Tab 3 content</description>
</tabpanel>
</tabpanels>
</tabbox>
<vbox id="eventdump"/>
</vbox>
</hbox>
</window>

View File

@ -170,11 +170,13 @@
testName("combo4", "Subscribe to ATOM feed.");
testName("comboinmiddle2", "Play the Haliluya sound when new mail arrives");
testName("combo5", "Play the Haliluya sound when new mail arrives");
testName("combo5", null); // label isn't used as a name for control
testName("checkbox", "Play the Haliluya sound when new mail arrives");
testName("comboinmiddle3", "Play the Haliluya sound when new mail arrives");
testName("combo6", "Play the Haliluya sound when new mail arrives");
testName("comboinend", "This day was sunny");
testName("combo6", "This day was");
testName("combo7", "This day was");
testName("textboxinend", "This day was sunny");
testName("textbox2", "This day was");
@ -213,6 +215,11 @@
title="Use placeholder as name if name is otherwise empty">
Mozilla Bug 604391
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=669312"
title="Accessible name is duplicated when input has a label associated uisng for/id and is wrapped around the input">
Mozilla Bug 669312
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
@ -437,10 +444,18 @@
</label>
<input id="checkbox" type="checkbox" />
<label id="comboinmiddle3" for="combo6">Play the
<select id="combo6">
<option>Haliluya</option>
<option>Hurra</option>
</select>
sound when new mail arrives
</label>
<!-- at the end (without and with whitespaces) -->
<label id="comboinend">
This day was
<select id="combo6" name="occupation">
<select id="combo7" name="occupation">
<option>sunny</option>
<option>rainy</option>
</select></label>

View File

@ -41,6 +41,10 @@
testRelation("control1_11", RELATION_LABELLED_BY, "label1_11");
testRelation("control1_12", RELATION_LABELLED_BY, "label1_12");
testRelation("label1_13", RELATION_LABEL_FOR, null);
testRelation("control1_13", RELATION_LABELLED_BY, null);
testRelation("control1_14", RELATION_LABELLED_BY, "label1_14");
// aria-labelledby
testRelation("label2", RELATION_LABEL_FOR, "checkbox2");
testRelation("checkbox2", RELATION_LABELLED_BY, "label2");
@ -157,6 +161,11 @@
title="make HTML <output> accessible">
Mozilla Bug 558036
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=682790"
title="Ignore implicit label association when it's associated explicitly">
Mozilla Bug 682790
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=687393"
title="HTML select options gets relation from containing label">
@ -204,6 +213,13 @@
<progress id="control1_12"></progress>
</label>
<label id="label1_13" for="">Label
<input id="control1_13">
</label>
<label id="label1_14" for="control1_14">Label
<input id="control1_14">
</label>
<span id="label2">label</span>
<span role="checkbox" id="checkbox2" aria-labelledby="label2"></span>

View File

@ -47,6 +47,10 @@
var treeitem6 = treeitem5.nextSibling;
testRelation(treeitem6, RELATION_NODE_CHILD_OF, [tree]);
// treeitems and treecells shouldn't pick up relations from tree
testRelation(treeitem1, RELATION_LABELLED_BY, null);
testRelation(treeitem1.firstChild, RELATION_LABELLED_BY, null);
SimpleTest.finish();
}
@ -68,7 +72,12 @@
href="https://bugzilla.mozilla.org/show_bug.cgi?id=503727"
title="Reorganize implementation of XUL tree accessibility">
Mozilla Bug 503727
</a><br/>
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=691248"
title="XUL tree items shouldn't pick up relations from XUL tree">
Mozilla Bug 691248
</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
@ -77,9 +86,11 @@
</body>
<vbox flex="1">
<label control="tree" value="It's a tree"/>
<tree id="tree" flex="1">
<treecols>
<treecol id="col" flex="1" primary="true" label="column"/>
<treecol id="col2" flex="1" label="column2"/>
</treecols>
<treechildren/>
</tree>

View File

@ -13,6 +13,7 @@
const STATE_CHECKED = nsIAccessibleStates.STATE_CHECKED;
const STATE_CHECKABLE = nsIAccessibleStates.STATE_CHECKABLE;
const STATE_COLLAPSED = nsIAccessibleStates.STATE_COLLAPSED;
const STATE_DEFAULT = nsIAccessibleStates.STATE_DEFAULT;
const STATE_EXPANDED = nsIAccessibleStates.STATE_EXPANDED;
const STATE_EXTSELECTABLE = nsIAccessibleStates.STATE_EXTSELECTABLE;
const STATE_FOCUSABLE = nsIAccessibleStates.STATE_FOCUSABLE;

View File

@ -49,6 +49,7 @@ _TEST_FILES =\
test_aria.html \
test_aria_imgmap.html \
test_aria_tabs.html \
test_buttons.html \
test_doc.html \
test_docarticle.html \
test_editablebody.html \

View File

@ -0,0 +1,79 @@
<!DOCTYPE html>
<html>
<head>
<title>HTML button accessible states</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="../states.js"></script>
<script type="application/javascript">
function doTest()
{
// Default state.
testStates("f1_image", STATE_DEFAULT);
testStates("f2_submit", STATE_DEFAULT);
testStates("f3_submitbutton", STATE_DEFAULT);
testStates("f4_button", 0, 0, STATE_DEFAULT);
testStates("f4_image1", STATE_DEFAULT);
testStates("f4_image2", 0, 0, STATE_DEFAULT);
testStates("f4_submit", 0, 0, STATE_DEFAULT);
testStates("f4_submitbutton", 0, 0, STATE_DEFAULT);
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=664142"
title="DEFAULT state exposed incorrectly for HTML">
Mozilla Bug 664142
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<p>A form with an image button</p>
<form name="form1" method="get">
<input type="text" name="hi">
<input id="f1_image" type="image" value="image-button">
</form>
<p>A form with a submit button:</p>
<form name="form2" method="get">
<input type="text" name="hi">
<input id="f2_submit" type="submit">
</form>
<p>A form with a HTML4 submit button:</p>
<form name="form3" method="get">
<input type="text" name="hi">
<button id="f3_submitbutton" type="submit">submit</button>
</form>
<p>A form with normal button, two image buttons, submit button,
HTML4 submit button:</p>
<form name="form4" method="get">
<input type="text" name="hi">
<input id="f4_button" type="button" value="normal" name="normal-button">
<input id="f4_image1" type="image" value="image-button1" name="image-button1">
<input id="f4_image2" type="image" value="image-button2" name="image-button2">
<input id="f4_submit" type="submit" value="real-submit" name="real-submit">
<button id="f4_submitbutton" type="submit">submit</button>
</form>
</body>
</html>

View File

@ -36,9 +36,6 @@
// table with empty caption
testAttrs("table4.2", attr, true);
// table with two captions
testAbsentAttrs("table4.3", attr);
// table with th element
testAbsentAttrs("table5", attr);
@ -48,8 +45,9 @@
// table with tfoot element
testAbsentAttrs("table7", attr);
// table with colgroup element
// table with colgroup or col elements
testAbsentAttrs("table8", attr);
testAbsentAttrs("table8.2", attr);
// layout table with nested table
testAttrs("table9", attr, true);
@ -87,6 +85,14 @@
// tree grid, no layout table
testAbsentAttrs("table20", attr);
// layout table containing nested data table (having data structures)
testAttrs("table21", attr, true);
testAttrs("table21.2", attr, true);
testAttrs("table21.3", attr, true);
testAttrs("table21.4", attr, true);
testAttrs("table21.5", attr, true);
testAttrs("table21.6", attr, true);
SimpleTest.finish();
}
@ -101,6 +107,11 @@
title="Don't treat tables that have a landmark role as layout table">
Mozilla Bug 495388
</a>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=690222"
title="Data table elements used to determine layout-guess attribute shouldn't be picked from nested tables">
Mozilla Bug 690222
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
@ -178,15 +189,6 @@
</tr>
</table>
<!-- table with two captions -->
<table id="table4.3">
<caption> </caption>
<tr>
<td>Cell1</td><td>cell2</td>
</tr>
<caption>a caption</caption>
</table>
<!-- table with th element -->
<table id="table5">
<tr>
@ -212,10 +214,17 @@
</tfoot>
</table>
<!-- table with colgroup element -->
<!-- table with colgroup and col elements -->
<table id="table8">
<colgroup width="20"></colgroup>
<tr>
<colgroup><td>Cell1</td><td>cell2</td></colgroup>
<td>Cell1</td><td>cell2</td>
</tr>
</table>
<table id="table8.2">
<col width="20">
<tr>
<td>Cell1</td><td>cell2</td>
</tr>
</table>
@ -339,5 +348,68 @@
<table id="table20" role="treegrid">
<tr role="treeitem"><td>Cell1</td><td>Cell2</td></tr>
</table>
<!-- layout table with nested data table containing data table elements -->
<table id="table21">
<tr>
<td>
<table>
<caption>table</caption>
<tr><td>Cell</td></tr>
</table>
</td>
</tr>
</table>
<table id="table21.2">
<tr>
<td>
<table>
<colgroup width="20"></colgroup>
<tr><th>Cell</th></tr>
</table>
</td>
</tr>
</table>
<table id="table21.3">
<tr>
<td>
<table>
<col width="20"></col>
<tr><th>Cell</th></tr>
</table>
</td>
</tr>
</table>
<table id="table21.4">
<tr>
<td>
<table>
<tr><th>Cell</th></tr>
</table>
</td>
</tr>
</table>
<table id="table21.5">
<tr>
<td>
<table>
<thead>
<tr><td>Cell</td></tr>
</thead>
</table>
</td>
</tr>
</table>
<table id="table21.6">
<tr>
<td>
<table>
<tfoot>
<tr><td>Cell</td></tr>
</tfoot>
</table>
</td>
</tr>
</table>
</body>
</html>

View File

@ -105,6 +105,14 @@
label="&mediaHideControls.label;"
accesskey="&mediaHideControls.accesskey;"
oncommand="gContextMenu.mediaCommand('hidecontrols');"/>
<menuitem id="context-video-showstats"
accesskey="&videoShowStats.accesskey;"
label="&videoShowStats.label;"
oncommand="gContextMenu.mediaCommand('showstats');"/>
<menuitem id="context-video-hidestats"
accesskey="&videoHideStats.accesskey;"
label="&videoHideStats.label;"
oncommand="gContextMenu.mediaCommand('hidestats');"/>
<menuitem id="context-video-fullscreen"
accesskey="&videoFullScreen.accesskey;"
label="&videoFullScreen.label;"
@ -167,6 +175,10 @@
label="&saveAudioCmd.label;"
accesskey="&saveAudioCmd.accesskey;"
oncommand="gContextMenu.saveMedia();"/>
<menuitem id="context-video-saveimage"
accesskey="&videoSaveImage.accesskey;"
label="&videoSaveImage.label;"
oncommand="gContextMenu.saveVideoFrameAsImage();"/>
<menuitem id="context-sendvideo"
label="&sendVideoCmd.label;"
accesskey="&sendVideoCmd.accesskey;"

View File

@ -224,6 +224,7 @@ nsContextMenu.prototype = {
this.showItem("context-saveimage", this.onLoadedImage || this.onCanvas);
this.showItem("context-savevideo", this.onVideo);
this.showItem("context-saveaudio", this.onAudio);
this.showItem("context-video-saveimage", this.onVideo);
this.setItemAttr("context-savevideo", "disabled", !this.mediaURL);
this.setItemAttr("context-saveaudio", "disabled", !this.mediaURL);
// Send media URL (but not for canvas, since it's a big data: URL)
@ -417,6 +418,10 @@ nsContextMenu.prototype = {
this.showItem("context-media-showcontrols", onMedia && !this.target.controls);
this.showItem("context-media-hidecontrols", onMedia && this.target.controls);
this.showItem("context-video-fullscreen", this.onVideo);
var statsShowing = this.onVideo && this.target.wrappedJSObject.mozMediaStatisticsShowing;
this.showItem("context-video-showstats", this.onVideo && this.target.controls && !statsShowing);
this.showItem("context-video-hidestats", this.onVideo && this.target.controls && statsShowing);
// Disable them when there isn't a valid media source loaded.
if (onMedia) {
var hasError = this.target.error != null ||
@ -427,8 +432,13 @@ nsContextMenu.prototype = {
this.setItemAttr("context-media-unmute", "disabled", hasError);
this.setItemAttr("context-media-showcontrols", "disabled", hasError);
this.setItemAttr("context-media-hidecontrols", "disabled", hasError);
if (this.onVideo)
this.setItemAttr("context-video-fullscreen", "disabled", hasError);
if (this.onVideo) {
let canSaveSnapshot = this.target.readyState >= this.target.HAVE_CURRENT_DATA;
this.setItemAttr("context-video-saveimage", "disabled", !canSaveSnapshot);
this.setItemAttr("context-video-fullscreen", "disabled", hasError);
this.setItemAttr("context-video-showstats", "disabled", hasError);
this.setItemAttr("context-video-hidestats", "disabled", hasError);
}
}
this.showItem("context-media-sep-commands", onMedia);
},
@ -826,6 +836,27 @@ nsContextMenu.prototype = {
openUILink(viewURL, e, null, null, null, null, doc.documentURIObject );
},
saveVideoFrameAsImage: function () {
urlSecurityCheck(this.mediaURL, this.browser.contentPrincipal,
Ci.nsIScriptSecurityManager.DISALLOW_SCRIPT);
let name = "";
try {
let uri = makeURI(this.mediaURL);
let url = uri.QueryInterface(Ci.nsIURL);
if (url.fileBaseName)
name = url.fileBaseName + ".jpg";
} catch (e) { }
if (!name)
name = "snapshot.jpg";
var video = this.target;
var canvas = document.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
var ctxDraw = canvas.getContext("2d");
ctxDraw.drawImage(video, 0, 0);
saveImageURL(canvas.toDataURL("image/jpg", ""), name, "SaveImageTitle", true, false, document.documentURIObject);
},
fullScreenVideo: function () {
this.target.pause();
@ -1411,6 +1442,16 @@ nsContextMenu.prototype = {
case "showcontrols":
media.setAttribute("controls", "true");
break;
case "showstats":
var event = document.createEvent("CustomEvent");
event.initCustomEvent("media-showStatistics", false, true, true);
media.dispatchEvent(event);
break;
case "hidestats":
var event = document.createEvent("CustomEvent");
event.initCustomEvent("media-showStatistics", false, true, false);
media.dispatchEvent(event);
break;
}
},

View File

@ -59,7 +59,7 @@ let gSyncAddDevice = {
pin2: this.pin3,
pin3: this.wizard.getButton("next")};
this.throbber = document.getElementById("add-device-throbber");
this.throbber = document.getElementById("pairDeviceThrobber");
this.errorRow = document.getElementById("errorRow");
// Kick off a sync. That way the server will have the most recent data from
@ -107,18 +107,33 @@ let gSyncAddDevice = {
startTransfer: function startTransfer() {
this.errorRow.hidden = true;
// When onAbort is called, Weave may already be gone.
const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT;
let self = this;
this._jpakeclient = new Weave.JPAKEClient({
let jpakeclient = this._jpakeclient = new Weave.JPAKEClient({
onPaired: function onPaired() {
let credentials = {account: Weave.Service.account,
password: Weave.Service.password,
synckey: Weave.Service.passphrase,
serverURL: Weave.Service.serverURL};
jpakeclient.sendAndComplete(credentials);
},
onComplete: function onComplete() {
delete self._jpakeclient;
self.wizard.pageIndex = DEVICE_CONNECTED_PAGE;
// Schedule a Sync for soonish to fetch the data uploaded by the
// device with which we just paired.
Weave.SyncScheduler.scheduleNextSync(Weave.SyncScheduler.activeInterval);
},
onAbort: function onAbort(error) {
delete self._jpakeclient;
// Aborted by user, ignore.
if (!error)
if (error == JPAKE_ERROR_USERABORT) {
return;
}
self.errorRow.hidden = false;
self.throbber.hidden = true;
@ -132,11 +147,8 @@ let gSyncAddDevice = {
this.wizard.canAdvance = false;
let pin = this.pin1.value + this.pin2.value + this.pin3.value;
let credentials = {account: Weave.Service.account,
password: Weave.Service.password,
synckey: Weave.Service.passphrase,
serverURL: Weave.Service.serverURL};
this._jpakeclient.sendWithPIN(pin, credentials);
let expectDelay = false;
jpakeclient.pairWithPIN(pin, expectDelay);
},
onWizardBack: function onWizardBack() {

View File

@ -52,7 +52,7 @@
<wizard xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
id="wizard"
title="&addDevice.title.label;"
title="&pairDevice.title.label;"
windowtype="Sync:AddDevice"
persist="screenX screenY"
onwizardnext="return gSyncAddDevice.onWizardAdvance();"
@ -70,10 +70,10 @@
src="chrome://global/content/printUtils.js"/>
<wizardpage id="addDevicePage"
label="&addDevice.title.label;"
label="&pairDevice.title.label;"
onpageshow="gSyncAddDevice.onPageShow();">
<description>
&addDevice.dialog.description.label;
&pairDevice.dialog.description.label;
<label class="text-link"
value="&addDevice.showMeHow.label;"
href="https://services.mozilla.com/sync/help/add-device"/>
@ -101,7 +101,7 @@
/>
</vbox>
<separator class="groove-thin"/>
<vbox id="add-device-throbber" align="center" hidden="true">
<vbox id="pairDeviceThrobber" align="center" hidden="true">
<image/>
</vbox>
<hbox id="errorRow" pack="center" hidden="true">

View File

@ -56,19 +56,20 @@
<implementation>
<constructor><![CDATA[
Cu.import("resource://services-sync/ext/Observers.js");
Cu.import("resource://services-sync/notifications.js");
let temp = {};
Cu.import("resource://services-sync/ext/Observers.js", temp);
temp.Observers.add("weave:notification:added", this.onNotificationAdded, this);
temp.Observers.add("weave:notification:removed", this.onNotificationRemoved, this);
Observers.add("weave:notification:added", this.onNotificationAdded, this);
Observers.add("weave:notification:removed", this.onNotificationRemoved, this);
for each (var notification in Notifications.notifications)
for each (var notification in Weave.Notifications.notifications)
this._appendNotification(notification);
]]></constructor>
<destructor><![CDATA[
Observers.remove("weave:notification:added", this.onNotificationAdded, this);
Observers.remove("weave:notification:removed", this.onNotificationRemoved, this);
let temp = {};
Cu.import("resource://services-sync/ext/Observers.js", temp);
temp.Observers.remove("weave:notification:added", this.onNotificationAdded, this);
temp.Observers.remove("weave:notification:removed", this.onNotificationRemoved, this);
]]></destructor>
<method name="onNotificationAdded">
@ -140,7 +141,7 @@
onset="this._notification = val; return val;"/>
<method name="close">
<body><![CDATA[
Notifications.remove(this.notification);
Weave.Notifications.remove(this.notification);
// We should be able to call the base class's close method here
// to remove the notification element from the notification box,

View File

@ -46,33 +46,38 @@ const Cu = Components.utils;
// page consts
const INTRO_PAGE = 0;
const NEW_ACCOUNT_START_PAGE = 1;
const NEW_ACCOUNT_PP_PAGE = 2;
const NEW_ACCOUNT_CAPTCHA_PAGE = 3;
const EXISTING_ACCOUNT_CONNECT_PAGE = 4;
const EXISTING_ACCOUNT_LOGIN_PAGE = 5;
const OPTIONS_PAGE = 6;
const OPTIONS_CONFIRM_PAGE = 7;
const SETUP_SUCCESS_PAGE = 8;
const PAIR_PAGE = 0;
const INTRO_PAGE = 1;
const NEW_ACCOUNT_START_PAGE = 2;
const EXISTING_ACCOUNT_CONNECT_PAGE = 3;
const EXISTING_ACCOUNT_LOGIN_PAGE = 4;
const OPTIONS_PAGE = 5;
const OPTIONS_CONFIRM_PAGE = 6;
const SETUP_SUCCESS_PAGE = 7;
// Broader than we'd like, but after this changed from api-secure.recaptcha.net
// we had no choice. At least we only do this for the duration of setup.
// See discussion in Bugs 508112 and 653307.
const RECAPTCHA_DOMAIN = "https://www.google.com";
const PIN_PART_LENGTH = 4;
Cu.import("resource://services-sync/main.js");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/PlacesUtils.jsm");
Cu.import("resource://gre/modules/PluralForm.jsm");
function setVisibility(element, visible) {
element.style.visibility = visible ? "visible" : "hidden";
}
var gSyncSetup = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
Ci.nsIWebProgressListener,
Ci.nsISupportsWeakReference]),
haveCaptcha: true,
captchaBrowser: null,
wizard: null,
_disabledSites: [],
@ -120,17 +125,23 @@ var gSyncSetup = {
}, 0);
this.captchaBrowser = document.getElementById("captcha");
this.wizard = document.getElementById("accountSetup");
if (window.arguments && window.arguments[0] == true) {
// we're resetting sync
this._resettingSync = true;
this.wizard.pageIndex = OPTIONS_PAGE;
this.wizardType = null;
if (window.arguments && window.arguments[0]) {
this.wizardType = window.arguments[0];
}
else {
this.wizard.canAdvance = false;
this.captchaBrowser.addProgressListener(this);
Weave.Svc.Prefs.set("firstSync", "notReady");
switch (this.wizardType) {
case null:
this.wizard.pageIndex = INTRO_PAGE;
// Fall through!
case "pair":
this.captchaBrowser.addProgressListener(this);
Weave.Svc.Prefs.set("firstSync", "notReady");
break;
case "reset":
this._resettingSync = true;
this.wizard.pageIndex = OPTIONS_PAGE;
break;
}
this.wizard.getButton("extra1").label =
@ -150,14 +161,19 @@ var gSyncSetup = {
return false;
this._settingUpNew = true;
this.wizard.pageIndex = NEW_ACCOUNT_START_PAGE;
this.loadCaptcha();
},
useExistingAccount: function () {
if (!Weave.Utils.ensureMPUnlocked())
return false;
this._settingUpNew = false;
this.wizard.pageIndex = EXISTING_ACCOUNT_CONNECT_PAGE;
if (this.wizardType == "pair") {
// We're already pairing, so there's no point in pairing again.
// Go straight to the manual login page.
this.wizard.pageIndex = EXISTING_ACCOUNT_LOGIN_PAGE;
} else {
this.wizard.pageIndex = EXISTING_ACCOUNT_CONNECT_PAGE;
}
},
resetPassphrase: function resetPassphrase() {
@ -207,6 +223,20 @@ var gSyncSetup = {
this.toggleLoginFeedback(true);
},
sendCredentialsAfterSync: function () {
let send = function() {
Services.obs.removeObserver("weave:service:sync:finish", send);
Services.obs.removeObserver("weave:service:sync:error", send);
let credentials = {account: Weave.Service.account,
password: Weave.Service.password,
synckey: Weave.Service.passphrase,
serverURL: Weave.Service.serverURL};
this._jpakeclient.sendAndComplete(credentials);
}.bind(this);
Services.obs.addObserver("weave:service:sync:finish", send, false);
Services.obs.addObserver("weave:service:sync:error", send, false);
},
toggleLoginFeedback: function (stop) {
document.getElementById("login-throbber").hidden = stop;
let password = document.getElementById("existingPasswordFeedbackRow");
@ -289,6 +319,15 @@ var gSyncSetup = {
return true;
},
onPINInput: function onPINInput(textbox) {
if (textbox && textbox.value.length == PIN_PART_LENGTH) {
this.nextFocusEl[textbox.id].focus();
}
this.wizard.canAdvance = (this.pin1.value.length == PIN_PART_LENGTH &&
this.pin2.value.length == PIN_PART_LENGTH &&
this.pin3.value.length == PIN_PART_LENGTH);
},
onEmailInput: function () {
// Check account validity when the user stops typing for 1 second.
if (this._checkAccountTimer)
@ -335,16 +374,8 @@ var gSyncSetup = {
onPasswordChange: function () {
let password = document.getElementById("weavePassword");
let valid, str;
if (password.value == document.getElementById("weavePassphrase").value) {
// xxxmpc - hack, sigh
valid = false;
errorString = Weave.Utils.getErrorString("change.password.pwSameAsRecoveryKey");
}
else {
let pwconfirm = document.getElementById("weavePasswordConfirm");
[valid, errorString] = gSyncUtils.validatePassword(password, pwconfirm);
}
let pwconfirm = document.getElementById("weavePasswordConfirm");
let [valid, errorString] = gSyncUtils.validatePassword(password, pwconfirm);
let feedback = document.getElementById("passwordFeedbackRow");
this._setFeedback(feedback, valid, errorString);
@ -353,32 +384,24 @@ var gSyncSetup = {
this.checkFields();
},
onPassphraseGenerate: function () {
let passphrase = Weave.Utils.generatePassphrase();
Weave.Service.passphrase = passphrase;
let el = document.getElementById("weavePassphrase");
el.value = Weave.Utils.hyphenatePassphrase(passphrase);
},
onPageShow: function() {
switch (this.wizard.pageIndex) {
case PAIR_PAGE:
this.wizard.getButton("back").hidden = true;
this.wizard.getButton("extra1").hidden = true;
this.onPINInput();
this.pin1.focus();
break;
case INTRO_PAGE:
// We may not need the captcha in the Existing Account branch of the
// wizard. However, we want to preload it to avoid any flickering while
// the Create Account page is shown.
this.loadCaptcha();
this.wizard.getButton("next").hidden = true;
this.wizard.getButton("back").hidden = true;
this.wizard.getButton("extra1").hidden = true;
break;
case NEW_ACCOUNT_PP_PAGE:
document.getElementById("saveSyncKeyButton").focus();
let el = document.getElementById("weavePassphrase");
if (!el.value)
this.onPassphraseGenerate();
this.checkFields();
break;
case NEW_ACCOUNT_CAPTCHA_PAGE:
if (!this.haveCaptcha) {
gSyncSetup.wizard.advance();
}
break;
case NEW_ACCOUNT_START_PAGE:
this.wizard.getButton("extra1").hidden = false;
this.wizard.getButton("next").hidden = false;
@ -396,6 +419,9 @@ var gSyncSetup = {
this.startEasySetup();
break;
case EXISTING_ACCOUNT_LOGIN_PAGE:
this.wizard.getButton("next").hidden = false;
this.wizard.getButton("back").hidden = false;
this.wizard.getButton("extra1").hidden = false;
this.wizard.canRewind = true;
this.checkFields();
break;
@ -407,6 +433,9 @@ var gSyncSetup = {
this.wizard.getButton("cancel").hidden = true;
this.wizard.getButton("finish").hidden = false;
this._handleSuccess();
if (this.wizardType == "pair") {
this.completePairing();
}
break;
case OPTIONS_PAGE:
this.wizard.canRewind = false;
@ -445,19 +474,23 @@ var gSyncSetup = {
return false;
}
if (!this.wizard.pageIndex)
return true;
switch (this.wizard.pageIndex) {
case PAIR_PAGE:
this.startPairing();
return false;
case NEW_ACCOUNT_START_PAGE:
// If the user selects Next (e.g. by hitting enter) when we haven't
// executed the delayed checks yet, execute them immediately.
if (this._checkAccountTimer)
if (this._checkAccountTimer) {
this.checkAccount();
if (this._checkServerTimer)
}
if (this._checkServerTimer) {
this.checkServer();
return this.wizard.canAdvance;
case NEW_ACCOUNT_CAPTCHA_PAGE:
}
if (!this.wizard.canAdvance) {
return false;
}
let doc = this.captchaBrowser.contentDocument;
let getField = function getField(field) {
let node = doc.getElementById("recaptcha_" + field + "_field");
@ -470,7 +503,7 @@ var gSyncSetup = {
let label = image.nextSibling;
image.setAttribute("status", "active");
label.value = this._stringBundle.GetStringFromName("verifying.label");
feedback.hidden = false;
setVisibility(feedback, true);
let password = document.getElementById("weavePassword").value;
let email = Weave.Utils.normalizeAccount(
@ -484,6 +517,7 @@ var gSyncSetup = {
if (error == null) {
Weave.Service.account = email;
Weave.Service.password = password;
Weave.Service.passphrase = Weave.Utils.generatePassphrase();
this._handleNoScript(false);
this.wizard.pageIndex = SETUP_SUCCESS_PAGE;
return false;
@ -529,6 +563,15 @@ var gSyncSetup = {
this.abortEasySetup();
this.wizard.pageIndex = INTRO_PAGE;
return false;
case EXISTING_ACCOUNT_LOGIN_PAGE:
// If we were already pairing on entry, we went straight to the manual
// login page. If subsequently we go back, return to the page that lets
// us choose whether we already have an account.
if (this.wizardType == "pair") {
this.wizard.pageIndex = INTRO_PAGE;
return false;
}
return true;
case OPTIONS_CONFIRM_PAGE:
// Backing up from the confirmation page = resetting first sync to merge.
document.getElementById("mergeChoiceRadio").selectedIndex = 0;
@ -594,6 +637,59 @@ var gSyncSetup = {
return false;
},
startPairing: function startPairing() {
this.pairDeviceErrorRow.hidden = true;
// When onAbort is called, Weave may already be gone.
const JPAKE_ERROR_USERABORT = Weave.JPAKE_ERROR_USERABORT;
let self = this;
let jpakeclient = this._jpakeclient = new Weave.JPAKEClient({
onPaired: function onPaired() {
self.wizard.pageIndex = INTRO_PAGE;
},
onComplete: function onComplete() {
// This method will never be called since SendCredentialsController
// will take over after the wizard completes.
},
onAbort: function onAbort(error) {
delete self._jpakeclient;
// Aborted by user, ignore. The window is almost certainly going to close
// or is already closed.
if (error == JPAKE_ERROR_USERABORT) {
return;
}
self.pairDeviceErrorRow.hidden = false;
self.pairDeviceThrobber.hidden = true;
self.pin1.value = self.pin2.value = self.pin3.value = "";
self.pin1.disabled = self.pin2.disabled = self.pin3.disabled = false;
if (self.wizard.pageIndex == PAIR_PAGE) {
self.pin1.focus();
}
}
});
this.pairDeviceThrobber.hidden = false;
this.pin1.disabled = this.pin2.disabled = this.pin3.disabled = true;
this.wizard.canAdvance = false;
let pin = this.pin1.value + this.pin2.value + this.pin3.value;
let expectDelay = true;
jpakeclient.pairWithPIN(pin, expectDelay);
},
completePairing: function completePairing() {
if (!this._jpakeclient) {
// The channel was aborted while we were setting up the account
// locally. XXX TODO should we do anything here, e.g. tell
// the user on the last wizard page that it's ok, they just
// have to pair again?
return;
}
let controller = new Weave.SendCredentialsController(this._jpakeclient);
this._jpakeclient.controller = controller;
},
startEasySetup: function () {
// Don't do anything if we have a client already (e.g. we went to
// Sync Options and just came back).
@ -611,6 +707,8 @@ var gSyncSetup = {
document.getElementById("easySetupPIN3").value = pin.slice(8);
},
onPairingStart: function onPairingStart() {},
onComplete: function onComplete(credentials) {
Weave.Service.account = credentials.account;
Weave.Service.password = credentials.password;
@ -708,7 +806,7 @@ var gSyncSetup = {
},
onServerCommand: function () {
document.getElementById("TOSRow").hidden = !this._usingMainServers;
setVisibility(document.getElementById("TOSRow"), this._usingMainServers);
let control = document.getElementById("server");
if (!this._usingMainServers) {
control.setAttribute("editable", "true");
@ -962,9 +1060,12 @@ var gSyncSetup = {
},
loadCaptcha: function loadCaptcha() {
let captchaURI = Weave.Service.miscAPI + "captcha_html";
// First check for NoScript and whitelist the right sites.
this._handleNoScript(true);
this.captchaBrowser.loadURI(Weave.Service.miscAPI + "captcha_html");
if (this.captchaBrowser.currentURI.spec != captchaURI) {
this.captchaBrowser.loadURI(captchaURI);
}
},
onStateChange: function(webProgress, request, stateFlags, status) {
@ -976,30 +1077,36 @@ var gSyncSetup = {
if ((stateFlags & Ci.nsIWebProgressListener.STATE_IS_WINDOW) == 0)
return;
// If we didn't find the captcha, assume it's not needed and move on
if (request.QueryInterface(Ci.nsIHttpChannel).responseStatus == 404) {
this.haveCaptcha = false;
// Hide the browser just in case we end up displaying the captcha page
// due to a sign up error.
this.captchaBrowser.hidden = true;
if (this.wizard.pageIndex == NEW_ACCOUNT_CAPTCHA_PAGE) {
this.onWizardAdvance();
}
} else {
this.haveCaptcha = true;
this.captchaBrowser.hidden = false;
}
// If we didn't find a captcha, assume it's not needed and don't show it.
let responseStatus = request.QueryInterface(Ci.nsIHttpChannel).responseStatus;
setVisibility(this.captchaBrowser, responseStatus != 404);
//XXX TODO we should really log any responseStatus other than 200
},
onProgressChange: function() {},
onStatusChange: function() {},
onSecurityChange: function() {},
onLocationChange: function () {}
}
};
// onWizardAdvance() and onPageShow() are run before init(), so we'll set
// wizard & _stringBundle up as lazy getters.
XPCOMUtils.defineLazyGetter(gSyncSetup, "wizard", function() {
return document.getElementById("accountSetup");
// Define lazy getters for various XUL elements.
//
// onWizardAdvance() and onPageShow() are run before init(), so we'll even
// define things that will almost certainly be used (like 'wizard') as a lazy
// getter here.
["wizard",
"pin1",
"pin2",
"pin3",
"pairDeviceErrorRow",
"pairDeviceThrobber"].forEach(function (id) {
XPCOMUtils.defineLazyGetter(gSyncSetup, id, function() {
return document.getElementById(id);
});
});
XPCOMUtils.defineLazyGetter(gSyncSetup, "nextFocusEl", function () {
return {pin1: this.pin2,
pin2: this.pin3,
pin3: this.wizard.getButton("next")};
});
XPCOMUtils.defineLazyGetter(gSyncSetup, "_stringBundle", function() {
return Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");

View File

@ -52,7 +52,8 @@
%syncBrandDTD;
%syncSetupDTD;
]>
<wizard id="accountSetup" title="&accountSetupTitle.label;"
<wizard id="wizard"
title="&accountSetupTitle.label;"
windowtype="Weave:AccountSetup"
persist="screenX screenY"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
@ -72,6 +73,48 @@
<script type="application/javascript"
src="chrome://global/content/printUtils.js"/>
<wizardpage id="addDevicePage"
label="&pairDevice.title.label;"
onpageshow="gSyncSetup.onPageShow()">
<description>
&pairDevice.dialog.description.label;
<label class="text-link"
value="&addDevice.showMeHow.label;"
href="https://services.mozilla.com/sync/help/add-device"/>
</description>
<separator class="groove-thin"/>
<description>
&addDevice.dialog.enterCode.label;
</description>
<separator class="groove-thin"/>
<vbox align="center">
<textbox id="pin1"
class="pin"
oninput="gSyncSetup.onPINInput(this);"
onfocus="this.select();"
/>
<textbox id="pin2"
class="pin"
oninput="gSyncSetup.onPINInput(this);"
onfocus="this.select();"
/>
<textbox id="pin3"
class="pin"
oninput="gSyncSetup.onPINInput(this);"
onfocus="this.select();"
/>
</vbox>
<separator class="groove-thin"/>
<vbox id="pairDeviceThrobber" align="center" hidden="true">
<image/>
</vbox>
<hbox id="pairDeviceErrorRow" pack="center" hidden="true">
<image class="statusIcon" status="error"/>
<label class="status"
value="&addDevice.dialog.tryAgain.label;"/>
</hbox>
</wizardpage>
<wizardpage id="pickSetupType"
label="&syncBrand.fullName.label;"
onpageshow="gSyncSetup.onPageShow()">
@ -79,22 +122,20 @@
<description style="padding: 0 7em;">
&setup.pickSetupType.description;
</description>
<spacer flex="1"/>
<spacer flex="3"/>
<button id="newAccount"
class="accountChoiceButton"
label="&button.createNewAccount.label;"
oncommand="gSyncSetup.startNewAccountSetup()"
align="center"/>
<spacer flex="3"/>
<spacer flex="1"/>
</vbox>
<separator class="groove"/>
<vbox align="center" flex="1">
<spacer flex="3"/>
<label value="&setup.haveAccount.label;" />
<spacer flex="1"/>
<button id="existingAccount"
class="accountChoiceButton"
label="&button.connect.label;"
label="&button.haveAccount.label;"
oncommand="gSyncSetup.useExistingAccount()"/>
<spacer flex="3"/>
</vbox>
@ -194,64 +235,27 @@
</row>
</rows>
</grid>
</wizardpage>
<wizardpage label="&setup.newRecoveryKeyPage.title.label;"
onextra1="gSyncSetup.onSyncOptions()"
onpageshow="gSyncSetup.onPageShow();">
<description>
&setup.newRecoveryKeyPage.description.label;
</description>
<spacer/>
<groupbox>
<label value="&recoveryKeyEntry.label;"
accesskey="&recoveryKeyEntry.accesskey;"
control="weavePassphrase"/>
<textbox id="weavePassphrase"
readonly="true"
onfocus="this.select();"/>
</groupbox>
<groupbox align="center">
<description>&recoveryKeyBackup.description;</description>
<hbox>
<button id="printSyncKeyButton"
label="&button.syncKeyBackup.print.label;"
accesskey="&button.syncKeyBackup.print.accesskey;"
oncommand="gSyncUtils.passphrasePrint('weavePassphrase');"/>
<button id="saveSyncKeyButton"
label="&button.syncKeyBackup.save.label;"
accesskey="&button.syncKeyBackup.save.accesskey;"
oncommand="gSyncUtils.passphraseSave('weavePassphrase');"/>
</hbox>
</groupbox>
</wizardpage>
<wizardpage label="&setup.captchaPage2.title.label;"
onextra1="gSyncSetup.onSyncOptions()"
onpageshow="gSyncSetup.onPageShow();">
<spacer flex="1"/>
<vbox flex="1" align="center">
<browser height="150"
width="450"
width="500"
id="captcha"
type="content"
disablehistory="true"/>
<spacer flex="1"/>
<hbox id="captchaFeedback" hidden="true">
<hbox id="captchaFeedback">
<image class="statusIcon"/>
<label class="status" value=" "/>
</hbox>
<spacer flex="3"/>
</vbox>
</wizardpage>
<wizardpage id="addDevice"
label="&addDevice.title.label;"
label="&pairDevice.title.label;"
onextra1="gSyncSetup.onSyncOptions()"
onpageshow="gSyncSetup.onPageShow()">
<description>
&addDevice.setup.description.label;
&pairDevice.setup.description.label;
<label class="text-link"
value="&addDevice.showMeHow.label;"
href="https://services.mozilla.com/sync/help/easy-setup"/>

View File

@ -1297,9 +1297,6 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// Returns true if the groupItem, given "count", should stack (instead of
// grid).
shouldStack: function GroupItem_shouldStack(count) {
if (count <= 1)
return false;
let bb = this.getContentBounds();
let options = {
return: 'widthAndColumns',
@ -2380,15 +2377,13 @@ let GroupItems = {
}
let targetGroupItem;
// find first visible non-app tab in the tabbar.
// find first non-app visible tab belongs a group, and add the new tabItem
// to that group
gBrowser.visibleTabs.some(function(tab) {
if (!tab.pinned && tab != tabItem.tab) {
if (tab._tabViewTabItem) {
if (!tab._tabViewTabItem.parent && !tab._tabViewTabItem.parent.hidden) {
// the first visible tab belongs to a group, add the new tabItem to
// that group
targetGroupItem = tab._tabViewTabItem.parent;
}
if (tab._tabViewTabItem && tab._tabViewTabItem.parent &&
!tab._tabViewTabItem.parent.hidden) {
targetGroupItem = tab._tabViewTabItem.parent;
}
return true;
}

View File

@ -53,80 +53,23 @@
// Title: search.js
// Implementation for the search functionality of Firefox Panorama.
// ----------
// Function: scorePatternMatch
// Given a pattern string, returns a score between 0 and 1 of how well
// that pattern matches the original string. It mimics the heuristics
// of the Mac application launcher Quicksilver.
function scorePatternMatch(pattern, matched, offset) {
offset = offset || 0;
pattern = pattern.toLowerCase();
matched = matched.toLowerCase();
if (pattern.length == 0) return 0.9;
if (pattern.length > matched.length) return 0.0;
for (var i = pattern.length; i > 0; i--) {
var sub_pattern = pattern.substring(0,i);
var index = matched.indexOf(sub_pattern);
if (index < 0) continue;
if (index + pattern.length > matched.length + offset) continue;
var next_string = matched.substring(index+sub_pattern.length);
var next_pattern = null;
if (i >= pattern.length)
next_pattern = '';
else
next_pattern = pattern.substring(i);
var remaining_score =
scorePatternMatch(next_pattern, next_string, offset + index);
if (remaining_score > 0) {
var score = matched.length-next_string.length;
if (index != 0) {
var j = 0;
var c = matched.charCodeAt(index-1);
if (c == 32 || c == 9) {
for (var j = (index - 2); j >= 0; j--) {
c = matched.charCodeAt(j);
score -= ((c == 32 || c == 9) ? 1 : 0.15);
}
} else {
score -= index;
}
}
score += remaining_score * next_string.length;
score /= matched.length;
return score;
}
}
return 0.0;
}
// ##########
// Class: TabUtils
//
// A collection of helper functions for dealing with both
// <TabItem>s and <xul:tab>s without having to worry which
// one is which.
var TabUtils = {
//
// A collection of helper functions for dealing with both <TabItem>s and
// <xul:tab>s without having to worry which one is which.
let TabUtils = {
// ----------
// Function: toString
// Prints [TabUtils] for debug use
// Prints [TabUtils] for debug use.
toString: function TabUtils_toString() {
return "[TabUtils]";
},
// ---------
// Function: _nameOfTab
// Function: nameOfTab
// Given a <TabItem> or a <xul:tab> returns the tab's name.
nameOf: function TabUtils_nameOfTab(tab) {
nameOf: function TabUtils_nameOf(tab) {
// We can have two types of tabs: A <TabItem> or a <xul:tab>
// because we have to deal with both tabs represented inside
// of active Panoramas as well as for windows in which
@ -134,88 +77,88 @@ var TabUtils = {
// determine the type of tab and then returns its name.
return tab.label != undefined ? tab.label : tab.$tabTitle[0].textContent;
},
// ---------
// Function: URLOf
// Given a <TabItem> or a <xul:tab> returns the URL of tab
// Given a <TabItem> or a <xul:tab> returns the URL of tab.
URLOf: function TabUtils_URLOf(tab) {
// Convert a <TabItem> to <xul:tab>
if(tab.tab != undefined)
if ("tab" in tab)
tab = tab.tab;
return tab.linkedBrowser.currentURI.spec;
},
// ---------
// Function: favURLOf
// Function: faviconURLOf
// Given a <TabItem> or a <xul:tab> returns the URL of tab's favicon.
faviconURLOf: function TabUtils_faviconURLOf(tab) {
return tab.image != undefined ? tab.image : tab.$favImage[0].src;
},
// ---------
// Function: focus
// Given a <TabItem> or a <xul:tab>, focuses it and it's window.
focus: function TabUtils_focus(tab) {
// Convert a <TabItem> to a <xul:tab>
if (tab.tab != undefined) tab = tab.tab;
if ("tab" in tab)
tab = tab.tab;
tab.ownerDocument.defaultView.gBrowser.selectedTab = tab;
tab.ownerDocument.defaultView.focus();
tab.ownerDocument.defaultView.focus();
}
};
// ##########
// Class: TabMatcher
//
// A singleton class that allows you to iterate over
// matching and not-matching tabs, given a case-insensitive
// search term.
function TabMatcher(term) {
this.term = term;
//
// A class that allows you to iterate over matching and not-matching tabs,
// given a case-insensitive search term.
function TabMatcher(term) {
this.term = term;
}
TabMatcher.prototype = {
// ----------
// Function: toString
// Prints [TabMatcher (term)] for debug use
// Prints [TabMatcher (term)] for debug use.
toString: function TabMatcher_toString() {
return "[TabMatcher (" + this.term + ")]";
},
// ---------
// Function: _filterAndSortMatches
// Function: _filterAndSortForMatches
// Given an array of <TabItem>s and <xul:tab>s returns a new array
// of tabs whose name matched the search term, sorted by lexical
// closeness.
// closeness.
_filterAndSortForMatches: function TabMatcher__filterAndSortForMatches(tabs) {
var self = this;
tabs = tabs.filter(function(tab){
let self = this;
tabs = tabs.filter(function TabMatcher__filterAndSortForMatches_filter(tab) {
let name = TabUtils.nameOf(tab);
let url = TabUtils.URLOf(tab);
return name.match(self.term, "i") || url.match(self.term, "i");
});
tabs.sort(function sorter(x, y){
var yScore = scorePatternMatch(self.term, TabUtils.nameOf(y));
var xScore = scorePatternMatch(self.term, TabUtils.nameOf(x));
return yScore - xScore;
tabs.sort(function TabMatcher__filterAndSortForMatches_sort(x, y) {
let yScore = self._scorePatternMatch(self.term, TabUtils.nameOf(y));
let xScore = self._scorePatternMatch(self.term, TabUtils.nameOf(x));
return yScore - xScore;
});
return tabs;
},
// ---------
// Function: _filterForUnmatches
// Given an array of <TabItem>s returns an unsorted array of tabs whose name
// does not match the the search term.
_filterForUnmatches: function TabMatcher__filterForUnmatches(tabs) {
var self = this;
return tabs.filter(function(tab) {
let self = this;
return tabs.filter(function TabMatcher__filterForUnmatches_filter(tab) {
let name = tab.$tabTitle[0].textContent;
let url = TabUtils.URLOf(tab);
return !name.match(self.term, "i") && !url.match(self.term, "i");
});
},
// ---------
// Function: _getTabsForOtherWindows
// Returns an array of <TabItem>s and <xul:tabs>s representing tabs
@ -224,18 +167,18 @@ TabMatcher.prototype = {
// <xul:tab>s will be returned for windows in which Panorama has never
// been activated.
_getTabsForOtherWindows: function TabMatcher__getTabsForOtherWindows() {
var enumerator = Services.wm.getEnumerator("navigator:browser");
var allTabs = [];
let enumerator = Services.wm.getEnumerator("navigator:browser");
let allTabs = [];
while (enumerator.hasMoreElements()) {
var win = enumerator.getNext();
let win = enumerator.getNext();
// This function gets tabs from other windows, not from the current window
if (win != gWindow)
allTabs.push.apply(allTabs, win.gBrowser.tabs);
}
return allTabs;
},
// ----------
// Function: matchedTabsFromOtherWindows
// Returns an array of <TabItem>s and <xul:tab>s that match the search term
@ -244,37 +187,34 @@ TabMatcher.prototype = {
// <xul:tab>s will be returned for windows in which Panorama has never
// been activated.
// (new TabMatcher("app")).matchedTabsFromOtherWindows();
matchedTabsFromOtherWindows: function TabMatcher_matchedTabsFromOtherWindows(){
matchedTabsFromOtherWindows: function TabMatcher_matchedTabsFromOtherWindows() {
if (this.term.length < 2)
return [];
var tabs = this._getTabsForOtherWindows();
tabs = this._filterAndSortForMatches(tabs);
return tabs;
let tabs = this._getTabsForOtherWindows();
return this._filterAndSortForMatches(tabs);
},
// ----------
// Function: matched
// Returns an array of <TabItem>s which match the current search term.
// If the term is less than 2 characters in length, it returns
// nothing.
// If the term is less than 2 characters in length, it returns nothing.
matched: function TabMatcher_matched() {
if (this.term.length < 2)
return [];
var tabs = TabItems.getItems();
tabs = this._filterAndSortForMatches(tabs);
return tabs;
let tabs = TabItems.getItems();
return this._filterAndSortForMatches(tabs);
},
// ----------
// Function: unmatched
// Returns all of <TabItem>s that .matched() doesn't return.
unmatched: function TabMatcher_unmatched() {
var tabs = TabItems.getItems();
let tabs = TabItems.getItems();
if (this.term.length < 2)
return tabs;
return this._filterForUnmatches(tabs);
},
@ -289,73 +229,222 @@ TabMatcher.prototype = {
// passed both <TabItem>s and <xul:tab>s and the index is offset by the
// number of matched tabs inside the window.
doSearch: function TabMatcher_doSearch(matchFunc, unmatchFunc, otherFunc) {
var matches = this.matched();
var unmatched = this.unmatched();
var otherMatches = this.matchedTabsFromOtherWindows();
let matches = this.matched();
let unmatched = this.unmatched();
let otherMatches = this.matchedTabsFromOtherWindows();
matches.forEach(function(tab, i) {
matchFunc(tab, i);
});
otherMatches.forEach(function(tab,i) {
otherFunc(tab, i+matches.length);
otherFunc(tab, i+matches.length);
});
unmatched.forEach(function(tab, i) {
unmatchFunc(tab, i);
});
});
},
// ----------
// Function: _scorePatternMatch
// Given a pattern string, returns a score between 0 and 1 of how well
// that pattern matches the original string. It mimics the heuristics
// of the Mac application launcher Quicksilver.
_scorePatternMatch: function TabMatcher__scorePatternMatch(pattern, matched, offset) {
offset = offset || 0;
pattern = pattern.toLowerCase();
matched = matched.toLowerCase();
if (pattern.length == 0)
return 0.9;
if (pattern.length > matched.length)
return 0.0;
for (let i = pattern.length; i > 0; i--) {
let sub_pattern = pattern.substring(0,i);
let index = matched.indexOf(sub_pattern);
if (index < 0)
continue;
if (index + pattern.length > matched.length + offset)
continue;
let next_string = matched.substring(index+sub_pattern.length);
let next_pattern = null;
if (i >= pattern.length)
next_pattern = '';
else
next_pattern = pattern.substring(i);
let remaining_score = this._scorePatternMatch(next_pattern, next_string, offset + index);
if (remaining_score > 0) {
let score = matched.length-next_string.length;
if (index != 0) {
let c = matched.charCodeAt(index-1);
if (c == 32 || c == 9) {
for (let j = (index - 2); j >= 0; j--) {
c = matched.charCodeAt(j);
score -= ((c == 32 || c == 9) ? 1 : 0.15);
}
} else {
score -= index;
}
}
score += remaining_score * next_string.length;
score /= matched.length;
return score;
}
}
return 0.0;
}
};
// ##########
// Class: SearchEventHandlerClass
// Class: TabHandlers
//
// A singleton class that handles all of the
// event handlers.
function SearchEventHandlerClass() {
this.init();
}
// A object that handles all of the event handlers.
let TabHandlers = {
_mouseDownLocation: null,
// ---------
// Function: onMatch
// Adds styles and event listeners to the matched tab items.
onMatch: function TabHandlers_onMatch(tab, index) {
tab.addClass("onTop");
index != 0 ? tab.addClass("notMainMatch") : tab.removeClass("notMainMatch");
// Remove any existing handlers before adding the new ones.
// If we don't do this, then we may add more handlers than
// we remove.
tab.$canvas
.unbind("mousedown", TabHandlers._hideHandler)
.unbind("mouseup", TabHandlers._showHandler);
tab.$canvas
.mousedown(TabHandlers._hideHandler)
.mouseup(TabHandlers._showHandler);
},
// ---------
// Function: onUnmatch
// Removes styles and event listeners from the unmatched tab items.
onUnmatch: function TabHandlers_onUnmatch(tab, index) {
tab.$container.removeClass("onTop");
tab.removeClass("notMainMatch");
tab.$canvas
.unbind("mousedown", TabHandlers._hideHandler)
.unbind("mouseup", TabHandlers._showHandler);
},
// ---------
// Function: onOther
// Removes styles and event listeners from the unmatched tabs.
onOther: function TabHandlers_onOther(tab, index) {
// Unlike the other on* functions, in this function tab can
// either be a <TabItem> or a <xul:tab>. In other functions
// it is always a <TabItem>. Also note that index is offset
// by the number of matches within the window.
let item = iQ("<div/>")
.addClass("inlineMatch")
.click(function TabHandlers_onOther_click(event) {
Search.hide(event);
TabUtils.focus(tab);
});
iQ("<img/>")
.attr("src", TabUtils.faviconURLOf(tab))
.appendTo(item);
iQ("<span/>")
.text(TabUtils.nameOf(tab))
.appendTo(item);
index != 0 ? item.addClass("notMainMatch") : item.removeClass("notMainMatch");
item.appendTo("#results");
iQ("#otherresults").show();
},
// ---------
// Function: _hideHandler
// Performs when mouse down on a canvas of tab item.
_hideHandler: function TabHandlers_hideHandler(event) {
iQ("#search").fadeOut();
iQ("#searchshade").fadeOut();
TabHandlers._mouseDownLocation = {x:event.clientX, y:event.clientY};
},
// ---------
// Function: _showHandler
// Performs when mouse up on a canvas of tab item.
_showHandler: function TabHandlers_showHandler(event) {
// If the user clicks on a tab without moving the mouse then
// they are zooming into the tab and we need to exit search
// mode.
if (TabHandlers._mouseDownLocation.x == event.clientX &&
TabHandlers._mouseDownLocation.y == event.clientY) {
Search.hide();
return;
}
iQ("#searchshade").show();
iQ("#search").show();
iQ("#searchbox")[0].focus();
// Marshal the search.
setTimeout(Search.perform, 0);
}
};
// ##########
// Class: Search
//
// A object that handles the search feature.
let Search = {
_initiatedBy: "",
_currentHandler: null,
SearchEventHandlerClass.prototype = {
// ----------
// Function: toString
// Prints [SearchEventHandler] for debug use
toString: function SearchEventHandlerClass_toString() {
return "[SearchEventHandler]";
// Prints [Search] for debug use.
toString: function Search_toString() {
return "[Search]";
},
// ----------
// Function: init
// Initializes the searchbox to be focused, and everything
// else to be hidden, and to have everything have the appropriate
// event handlers;
init: function () {
// Initializes the searchbox to be focused, and everything else to be hidden,
// and to have everything have the appropriate event handlers.
init: function Search_init() {
let self = this;
iQ("#search").hide();
iQ("#searchshade").hide().click(function(event) {
if ( event.target.id != "searchbox")
hideSearch();
if (event.target.id != "searchbox")
self.hide();
});
iQ("#searchbox").keyup(function() {
performSearch();
self.perform();
});
iQ("#searchbutton").mousedown(function() {
self.initiatedBy = "buttonclick";
ensureSearchShown();
self.switchToInMode();
self._initiatedBy = "buttonclick";
self.ensureShown();
self.switchToInMode();
});
this.initiatedBy = "";
this.currentHandler = null;
this.switchToBeforeMode();
},
// ----------
// Function: beforeSearchKeyHandler
// Function: _beforeSearchKeyHandler
// Handles all keydown before the search interface is brought up.
beforeSearchKeyHandler: function (event) {
_beforeSearchKeyHandler: function Search__beforeSearchKeyHandler(event) {
// Only match reasonable text-like characters for quick search.
if (event.altKey || event.ctrlKey || event.metaKey)
return;
@ -384,28 +473,29 @@ SearchEventHandlerClass.prototype = {
}
this.switchToInMode();
this.initiatedBy = "keydown";
ensureSearchShown(true);
this._initiatedBy = "keydown";
this.ensureShown(true);
},
// ----------
// Function: inSearchKeyHandler
// Function: _inSearchKeyHandler
// Handles all keydown while search mode.
inSearchKeyHandler: function (event) {
_inSearchKeyHandler: function Search__inSearchKeyHandler(event) {
let term = iQ("#searchbox").val();
if ((event.keyCode == event.DOM_VK_ESCAPE) ||
(event.keyCode == event.DOM_VK_BACK_SPACE && term.length <= 1 && this.initiatedBy == "keydown")) {
hideSearch(event);
if ((event.keyCode == event.DOM_VK_ESCAPE) ||
(event.keyCode == event.DOM_VK_BACK_SPACE && term.length <= 1 &&
this._initiatedBy == "keydown")) {
this.hide(event);
return;
}
let matcher = createSearchTabMacher();
let matcher = this.createSearchTabMatcher();
let matches = matcher.matched();
let others = matcher.matchedTabsFromOtherWindows();
if ((event.keyCode == event.DOM_VK_RETURN ||
event.keyCode == event.DOM_VK_ENTER) &&
if ((event.keyCode == event.DOM_VK_RETURN ||
event.keyCode == event.DOM_VK_ENTER) &&
(matches.length > 0 || others.length > 0)) {
hideSearch(event);
this.hide(event);
if (matches.length > 0)
matches[0].zoomIn();
else
@ -415,206 +505,135 @@ SearchEventHandlerClass.prototype = {
// ----------
// Function: switchToBeforeMode
// Make sure the event handlers are appropriate for
// the before-search mode.
switchToBeforeMode: function switchToBeforeMode() {
// Make sure the event handlers are appropriate for the before-search mode.
switchToBeforeMode: function Search_switchToBeforeMode() {
let self = this;
if (this.currentHandler)
iQ(window).unbind("keydown", this.currentHandler);
this.currentHandler = function(event) self.beforeSearchKeyHandler(event);
iQ(window).keydown(this.currentHandler);
if (this._currentHandler)
iQ(window).unbind("keydown", this._currentHandler);
this._currentHandler = function Search_switchToBeforeMode_handler(event) {
self._beforeSearchKeyHandler(event);
}
iQ(window).keydown(this._currentHandler);
},
// ----------
// Function: switchToInMode
// Make sure the event handlers are appropriate for
// the in-search mode.
switchToInMode: function switchToInMode() {
// Make sure the event handlers are appropriate for the in-search mode.
switchToInMode: function Search_switchToInMode() {
let self = this;
if (this.currentHandler)
iQ(window).unbind("keydown", this.currentHandler);
this.currentHandler = function(event) self.inSearchKeyHandler(event);
iQ(window).keydown(this.currentHandler);
}
};
var TabHandlers = {
onMatch: function(tab, index){
tab.addClass("onTop");
index != 0 ? tab.addClass("notMainMatch") : tab.removeClass("notMainMatch");
// Remove any existing handlers before adding the new ones.
// If we don't do this, then we may add more handlers than
// we remove.
tab.$canvas
.unbind("mousedown", TabHandlers._hideHandler)
.unbind("mouseup", TabHandlers._showHandler);
tab.$canvas
.mousedown(TabHandlers._hideHandler)
.mouseup(TabHandlers._showHandler);
if (this._currentHandler)
iQ(window).unbind("keydown", this._currentHandler);
this._currentHandler = function Search_switchToInMode_handler(event) {
self._inSearchKeyHandler(event);
}
iQ(window).keydown(this._currentHandler);
},
onUnmatch: function(tab, index){
tab.$container.removeClass("onTop");
tab.removeClass("notMainMatch");
tab.$canvas
.unbind("mousedown", TabHandlers._hideHandler)
.unbind("mouseup", TabHandlers._showHandler);
createSearchTabMatcher: function Search_createSearchTabMatcher() {
return new TabMatcher(iQ("#searchbox").val());
},
onOther: function(tab, index){
// Unlike the other on* functions, in this function tab can
// either be a <TabItem> or a <xul:tab>. In other functions
// it is always a <TabItem>. Also note that index is offset
// by the number of matches within the window.
let item = iQ("<div/>")
.addClass("inlineMatch")
.click(function(event){
hideSearch(event);
TabUtils.focus(tab);
});
iQ("<img/>")
.attr("src", TabUtils.faviconURLOf(tab) )
.appendTo(item);
iQ("<span/>")
.text( TabUtils.nameOf(tab) )
.appendTo(item);
index != 0 ? item.addClass("notMainMatch") : item.removeClass("notMainMatch");
item.appendTo("#results");
iQ("#otherresults").show();
// ----------
// Function: isEnabled
// Checks whether search mode is enabled or not.
isEnabled: function Search_isEnabled() {
return iQ("#search").css("display") != "none";
},
_hideHandler: function(event){
iQ("#search").fadeOut();
iQ("#searchshade").fadeOut();
TabHandlers._mouseDownLocation = {x:event.clientX, y:event.clientY};
},
_showHandler: function(event){
// If the user clicks on a tab without moving the mouse then
// they are zooming into the tab and we need to exit search
// mode.
if (TabHandlers._mouseDownLocation.x == event.clientX &&
TabHandlers._mouseDownLocation.y == event.clientY){
hideSearch();
// ----------
// Function: hide
// Hides search mode.
hide: function Search_hide(event) {
if (!this.isEnabled())
return;
iQ("#searchbox").val("");
iQ("#searchshade").hide();
iQ("#search").hide();
iQ("#searchbutton").css({ opacity:.8 });
#ifdef XP_MACOSX
UI.setTitlebarColors(true);
#endif
this.perform();
this.switchToBeforeMode();
if (event) {
// when hiding the search mode, we need to prevent the keypress handler
// in UI__setTabViewFrameKeyHandlers to handle the key press again. e.g. Esc
// which is already handled by the key down in this class.
if (event.type == "keydown")
UI.ignoreKeypressForSearch = true;
event.preventDefault();
event.stopPropagation();
}
iQ("#searchshade").show();
iQ("#search").show();
iQ("#searchbox")[0].focus();
// Marshal the search.
setTimeout(performSearch, 0);
// Return focus to the tab window
UI.blurAll();
gTabViewFrame.contentWindow.focus();
let newEvent = document.createEvent("Events");
newEvent.initEvent("tabviewsearchdisabled", false, false);
dispatchEvent(newEvent);
},
_mouseDownLocation: null
};
function createSearchTabMacher() {
return new TabMatcher(iQ("#searchbox").val());
}
// ----------
// Function: perform
// Performs a search.
perform: function Search_perform() {
let matcher = this.createSearchTabMatcher();
function hideSearch(event) {
if (!isSearchEnabled())
return;
// Remove any previous other-window search results and
// hide the display area.
iQ("#results").empty();
iQ("#otherresults").hide();
iQ("#otherresults>.label").text(tabviewString("search.otherWindowTabs"));
iQ("#searchbox").val("");
iQ("#searchshade").hide();
iQ("#search").hide();
matcher.doSearch(TabHandlers.onMatch, TabHandlers.onUnmatch, TabHandlers.onOther);
},
iQ("#searchbutton").css({ opacity:.8 });
#ifdef XP_MACOSX
UI.setTitlebarColors(true);
#endif
performSearch();
SearchEventHandler.switchToBeforeMode();
if (event) {
// when hiding the search mode, we need to prevent the keypress handler
// in UI__setTabViewFrameKeyHandlers to handle the key press again. e.g. Esc
// which is already handled by the key down in this class.
if (event.type == "keydown")
UI.ignoreKeypressForSearch = true;
event.preventDefault();
event.stopPropagation();
}
// Return focus to the tab window
UI.blurAll();
gTabViewFrame.contentWindow.focus();
let newEvent = document.createEvent("Events");
newEvent.initEvent("tabviewsearchdisabled", false, false);
dispatchEvent(newEvent);
}
function performSearch() {
let matcher = new TabMatcher(iQ("#searchbox").val());
// Remove any previous other-window search results and
// hide the display area.
iQ("#results").empty();
iQ("#otherresults").hide();
iQ("#otherresults>.label").text(tabviewString("search.otherWindowTabs"));
matcher.doSearch(TabHandlers.onMatch, TabHandlers.onUnmatch, TabHandlers.onOther);
}
// ----------
// Function: ensureSearchShown
// Ensure the search feature is displayed. If not, display it.
// Parameters:
// - a boolean indicates whether this is triggered by a keypress or not
function ensureSearchShown(activatedByKeypress) {
var $search = iQ("#search");
var $searchShade = iQ("#searchshade");
var $searchbox = iQ("#searchbox");
iQ("#searchbutton").css({ opacity: 1 });
if (!isSearchEnabled()) {
$searchShade.show();
$search.show();
#ifdef XP_MACOSX
UI.setTitlebarColors({active: "#717171", inactive: "#EDEDED"});
#endif
// ----------
// Function: ensureShown
// Ensures the search feature is displayed. If not, display it.
// Parameters:
// - a boolean indicates whether this is triggered by a keypress or not
ensureShown: function Search_ensureShown(activatedByKeypress) {
let $search = iQ("#search");
let $searchShade = iQ("#searchshade");
let $searchbox = iQ("#searchbox");
iQ("#searchbutton").css({ opacity: 1 });
// NOTE: when this function is called by keydown handler, next keypress
// event or composition events of IME will be fired on the focused editor.
let dispatchTabViewSearchEnabledEvent = function dispatchTabViewSearchEnabledEvent() {
function dispatchTabViewSearchEnabledEvent() {
let newEvent = document.createEvent("Events");
newEvent.initEvent("tabviewsearchenabled", false, false);
dispatchEvent(newEvent);
};
if (activatedByKeypress) {
// set the focus so key strokes are entered into the textbox.
$searchbox[0].focus();
dispatchTabViewSearchEnabledEvent();
} else {
// marshal the focusing, otherwise it ends up with searchbox[0].focus gets
// called before the search button gets the focus after being pressed.
setTimeout(function setFocusAndDispatchSearchEnabledEvent() {
if (!this.isEnabled()) {
$searchShade.show();
$search.show();
#ifdef XP_MACOSX
UI.setTitlebarColors({active: "#717171", inactive: "#EDEDED"});
#endif
if (activatedByKeypress) {
// set the focus so key strokes are entered into the textbox.
$searchbox[0].focus();
dispatchTabViewSearchEnabledEvent();
}, 0);
} else {
// marshal the focusing, otherwise it ends up with searchbox[0].focus gets
// called before the search button gets the focus after being pressed.
setTimeout(function setFocusAndDispatchSearchEnabledEvent() {
$searchbox[0].focus();
dispatchTabViewSearchEnabledEvent();
}, 0);
}
}
}
}
};
function isSearchEnabled() {
return iQ("#search").css("display") != "none";
}
var SearchEventHandler = new SearchEventHandlerClass();
// Features to add:
// (1) Make sure this looks good on Windows. Bug 594429
// (2) Group all of the highlighted tabs into a group? Bug 594434

View File

@ -609,7 +609,7 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
let $tabEl = this.$container;
let $canvas = this.$canvas;
hideSearch();
Search.hide();
UI.setActive(this);
TabItems._update(this.tab, {force: true});

View File

@ -78,5 +78,5 @@ let AllTabs = {
#include drag.js
#include trench.js
#include thumbnailStorage.js
#include ui.js
#include search.js
#include ui.js

View File

@ -185,6 +185,9 @@ let UI = {
this._storageSanity(data);
this._pageBounds = data.pageBounds;
// ___ search
Search.init();
// ___ currentTab
this._currentTab = gBrowser.selectedTab;
@ -717,7 +720,7 @@ let UI = {
}
} else if (topic == "private-browsing-change-granted") {
if (data == "enter" || data == "exit") {
hideSearch();
Search.hide();
self._privateBrowsing.transitionMode = data;
// make sure to save all thumbnails that haven't been saved yet
@ -1134,7 +1137,7 @@ let UI = {
}
}
if ((iQ(":focus").length > 0 && iQ(":focus")[0].nodeName == "INPUT") ||
isSearchEnabled() || self.ignoreKeypressForSearch) {
Search.isEnabled() || self.ignoreKeypressForSearch) {
self.ignoreKeypressForSearch = false;
processBrowserKeys(event);
return;
@ -1247,9 +1250,9 @@ let UI = {
// Function: enableSearch
// Enables the search feature.
enableSearch: function UI_enableSearch() {
if (!isSearchEnabled()) {
ensureSearchShown();
SearchEventHandler.switchToInMode();
if (!Search.isEnabled()) {
Search.ensureShown();
Search.switchToInMode();
}
},
@ -1516,15 +1519,15 @@ let UI = {
let self = this;
let zoomedIn = false;
if (isSearchEnabled()) {
let matcher = createSearchTabMacher();
if (Search.isEnabled()) {
let matcher = Search.createSearchTabMatcher();
let matches = matcher.matched();
if (matches.length > 0) {
matches[0].zoomIn();
zoomedIn = true;
}
hideSearch();
Search.hide();
}
if (!zoomedIn) {

View File

@ -12,9 +12,9 @@ Browser context menu subtest.
<input id="test-input"><br>
<img id="test-image" src="ctxmenu-image.png">
<canvas id="test-canvas" width="100" height="100" style="background-color: blue"></canvas>
<video id="test-video-ok" src="video.ogg" width="100" height="100" style="background-color: green"></video>
<video id="test-video-bad" src="bogus.duh" width="100" height="100" style="background-color: orange"></video>
<video id="test-video-bad2" width="100" height="100" style="background-color: yellow">
<video controls id="test-video-ok" src="video.ogg" width="100" height="100" style="background-color: green"></video>
<video controls id="test-video-bad" src="bogus.duh" width="100" height="100" style="background-color: orange"></video>
<video controls id="test-video-bad2" width="100" height="100" style="background-color: yellow">
<source src="bogus.duh" type="video/durrrr;">
</video>
<iframe id="test-iframe" width="98" height="98" style="border: 1px solid black"></iframe>

View File

@ -110,6 +110,7 @@ _BROWSER_FILES = \
browser_tabview_bug625195.js \
browser_tabview_bug625269.js \
browser_tabview_bug625424.js \
browser_tabview_bug625955.js \
browser_tabview_bug626368.js \
browser_tabview_bug626455.js \
browser_tabview_bug626525.js \

View File

@ -24,7 +24,7 @@ function test2() {
ok(TabView.isVisible(), "Tab View is visible");
whenSearchIsEnabled(function() {
ok(contentWindow.isSearchEnabled(), "The search is enabled")
ok(contentWindow.Search.isEnabled(), "The search is enabled")
whenSearchIsDisabled(test3);
hideSearch();
@ -33,7 +33,7 @@ function test2() {
}
function test3() {
ok(!contentWindow.isSearchEnabled(), "The search is disabled")
ok(!contentWindow.Search.isEnabled(), "The search is disabled")
is(gBrowser.tabs.length, 1, "There is one tab before cmd/ctrl + t is pressed");

View File

@ -50,7 +50,7 @@ function testThree() {
let tab = win.gBrowser.tabs[1];
searchBox.val(tab._tabViewTabItem.$tabTitle[0].innerHTML);
cw.performSearch();
cw.Search.perform();
whenTabViewIsHidden(function () {
is(tab, win.gBrowser.selectedTab, "The search result tab is shown");

View File

@ -20,7 +20,7 @@ function onTabViewWindowLoaded() {
executeSoon(function() {
let searchBox = contentWindow.document.getElementById("searchbox");
is(searchBox.value, number, "The seach box matches the number: " + number);
contentWindow.hideSearch(null);
contentWindow.Search.hide(null);
});
}
let onSearchDisabled = function() {

View File

@ -52,7 +52,7 @@ function onTabViewWindowLoaded(win, tab) {
let testClickOnOtherSearchResult = function () {
// search for the tab from our main window
searchbox.setAttribute('value', 'other');
contentWindow.performSearch();
contentWindow.Search.perform();
// prepare to finish when the main window gets focus back
window.addEventListener('focus', function onFocus() {

View File

@ -0,0 +1,25 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
waitForExplicitFinish();
showTabView(onTabViewShown);
}
function onTabViewShown() {
let contentWindow = TabView.getContentWindow();
is(contentWindow.GroupItems.groupItems.length, 1, "Has one groupItem only");
let groupItem = contentWindow.GroupItems.groupItems[0];
let tabItems = groupItem.getChildren();
is(tabItems.length, 1, "There is only one tabItems in the groupItem");
let bounds = groupItem.bounds;
gBrowser.addTab();
gBrowser.removeTab(gBrowser.tabs[1]);
ok(bounds.equals(groupItem.bounds), 'Group bounds recovered');
is(tabItems.length, 1, "There is only one tabItem in the groupItem");
hideTabView(finish);
}

View File

@ -12,7 +12,7 @@ function test() {
let keyCodes = [0, 91, 92];
keyCodes.forEach(function(keyCode) {
utils.sendKeyEvent("keydown", keyCode, 0, 0);
ok(!cw.isSearchEnabled(), "search is not enabled with keyCode: " + keyCode);
ok(!cw.Search.isEnabled(), "search is not enabled with keyCode: " + keyCode);
});
hideTabView(finish);

View File

@ -9,14 +9,14 @@ function test() {
registerCleanupFunction(function() {
if (cw)
cw.hideSearch();
cw.Search.hide();
TabView.hide();
pb.privateBrowsingEnabled = false;
});
let enableSearch = function (callback) {
if (cw.isSearchEnabled()) {
if (cw.Search.isEnabled()) {
callback();
return;
}
@ -26,7 +26,7 @@ function test() {
executeSoon(callback);
}, false);
cw.ensureSearchShown();
cw.Search.ensureShown();
};
let getSearchboxValue = function () {
@ -34,7 +34,7 @@ function test() {
};
let prepareSearchbox = function (callback) {
ok(!cw.isSearchEnabled(), "search is disabled");
ok(!cw.Search.isEnabled(), "search is disabled");
enableSearch(function () {
cw.iQ("#searchbox").val("moz");
@ -46,7 +46,7 @@ function test() {
prepareSearchbox(function () {
togglePrivateBrowsing(function () {
showTabView(function () {
ok(!cw.isSearchEnabled(), "search is disabled");
ok(!cw.Search.isEnabled(), "search is disabled");
is(getSearchboxValue(), "", "search box is empty");
callback();
});

View File

@ -14,11 +14,11 @@ function test() {
})
whenSearchIsEnabled(function() {
ok(cw.isSearchEnabled(), "The search is enabled before creating a new tab");
ok(cw.Search.isEnabled(), "The search is enabled before creating a new tab");
whenTabViewIsHidden(function() {
showTabView(function() {
ok(!cw.isSearchEnabled(), "The search is disabled when entering Tabview");
ok(!cw.Search.isEnabled(), "The search is disabled when entering Tabview");
hideTabView(finish);
})

View File

@ -100,7 +100,7 @@ function searchTest(contentWindow) {
// part of titled
searchBox.setAttribute("value", tabNames[0].substr(1));
contentWindow.performSearch();
contentWindow.Search.perform();
matchResults = getMatchResults(contentWindow, searchBox.getAttribute("value"));
is(matchResults.length, 1,
"Match something when a part of title exists");
@ -109,7 +109,7 @@ function searchTest(contentWindow) {
}
function cleanup(contentWindow) {
contentWindow.hideSearch(null);
contentWindow.Search.hide(null);
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);

View File

@ -93,21 +93,21 @@ function searchTest(contentWindow) {
// part of title
searchBox.setAttribute("value", "search");
contentWindow.performSearch();
contentWindow.Search.perform();
is(new contentWindow.TabMatcher(
searchBox.getAttribute("value")).matched().length, 2,
"Match something when a part of title exists");
// unique part of a url
searchBox.setAttribute("value", "search1.html");
contentWindow.performSearch();
contentWindow.Search.perform();
is(new contentWindow.TabMatcher(
searchBox.getAttribute("value")).matched().length, 1,
"Match something when a unique part of a url exists");
// common part of a url
searchBox.setAttribute("value", "tabview");
contentWindow.performSearch();
contentWindow.Search.perform();
is(new contentWindow.TabMatcher(
searchBox.getAttribute("value")).matched().length, 2,
"Match something when a common part of a url exists");
@ -117,7 +117,7 @@ function searchTest(contentWindow) {
// ----------
function cleanup(contentWindow) {
contentWindow.hideSearch(null);
contentWindow.Search.hide(null);
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
ok(!TabView.isVisible(), "Tab View is hidden");

View File

@ -199,7 +199,7 @@ function hideSearch(callback, win) {
win = win || window;
let contentWindow = win.TabView.getContentWindow();
if (!contentWindow.isSearchEnabled()) {
if (!contentWindow.Search.isEnabled()) {
if (callback)
callback();
return;
@ -208,7 +208,7 @@ function hideSearch(callback, win) {
if (callback)
whenSearchIsDisabled(callback, win);
contentWindow.hideSearch();
contentWindow.Search.hide();
}
// ----------
@ -216,7 +216,7 @@ function whenSearchIsEnabled(callback, win) {
win = win || window;
let contentWindow = win.TabView.getContentWindow();
if (contentWindow.isSearchEnabled()) {
if (contentWindow.Search.isEnabled()) {
callback();
return;
}
@ -232,7 +232,7 @@ function whenSearchIsDisabled(callback, win) {
win = win || window;
let contentWindow = win.TabView.getContentWindow();
if (!contentWindow.isSearchEnabled()) {
if (!contentWindow.Search.isEnabled()) {
callback();
return;
}

View File

@ -332,13 +332,15 @@ function runTest(testNum) {
// Context menu for a video (with a VALID media source)
checkContextMenu(["context-media-play", true,
"context-media-mute", true,
"context-media-showcontrols", true,
"context-media-hidecontrols", true,
"context-video-showstats", true,
"context-video-fullscreen", true,
"---", null,
"context-viewvideo", true,
"context-copyvideourl", true,
"---", null,
"context-savevideo", true,
"context-video-saveimage", true,
"context-sendvideo", true,
"---", null,
"context-inspect", true]);
@ -350,13 +352,15 @@ function runTest(testNum) {
// Context menu for a video (with an INVALID media source)
checkContextMenu(["context-media-play", false,
"context-media-mute", false,
"context-media-showcontrols", false,
"context-media-hidecontrols", false,
"context-video-showstats", false,
"context-video-fullscreen", false,
"---", null,
"context-viewvideo", true,
"context-copyvideourl", true,
"---", null,
"context-savevideo", true,
"context-video-saveimage", false,
"context-sendvideo", true,
"---", null,
"context-inspect", true]);
@ -368,13 +372,15 @@ function runTest(testNum) {
// Context menu for a video (with an INVALID media source)
checkContextMenu(["context-media-play", false,
"context-media-mute", false,
"context-media-showcontrols", false,
"context-media-hidecontrols", false,
"context-video-showstats", false,
"context-video-fullscreen", false,
"---", null,
"context-viewvideo", false,
"context-copyvideourl", false,
"---", null,
"context-savevideo", false,
"context-video-saveimage", false,
"context-sendvideo", false,
"---", null,
"context-inspect", true]);

View File

@ -51,9 +51,7 @@ DIRS = \
include $(topsrcdir)/config/rules.mk
_TEST_FILES = test_bug408328.html \
bug408328-data.xml \
test_bug368464.html \
_TEST_FILES = bug408328-data.xml \
bug368464-data.xml \
test_bug494328.html \
bug494328-data.xml \

View File

@ -51,6 +51,8 @@ _HTTP_FILES = \
_CHROME_FILES = \
test_423060.xul \
test_bug368464.html \
test_bug408328.html \
$(NULL)
libs:: $(_HTTP_FILES)

View File

@ -5,12 +5,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=368464
-->
<head>
<title>Test that RSS 0.90 isn't sniffed</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=368464">Mozilla Bug 368464</a>
<p id="display"><iframe id="testFrame" src="bug368464-data.xml"></iframe></p>
<p id="display"><iframe id="testFrame" src="http://mochi.test:8888/tests/browser/components/feeds/test/bug368464-data.xml"></iframe></p>
<div id="content" style="display: none">
</div>
@ -21,7 +21,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=368464
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
ok($("testFrame").contentDocument.documentElement.id != "feedHandler",
"RSS 0.90 shouldn't be sniffed as a feed");
});

View File

@ -5,12 +5,12 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=408328
-->
<head>
<title>Test feed preview safe-linkification</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=408328">Mozilla Bug 408328</a>
<p id="display"><iframe id="testFrame" src="bug408328-data.xml"></iframe></p>
<p id="display"><iframe id="testFrame" src="http://mochi.test:8888/tests/browser/components/feeds/test/bug408328-data.xml"></iframe></p>
<div id="content" style="display: none">
</div>
@ -21,7 +21,6 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=408328
SimpleTest.waitForExplicitFinish();
addLoadEvent(function() {
netscape.security.PrivilegeManager.enablePrivilege("UniversalBrowserRead");
var links = $("testFrame").contentDocument.getElementById("feedContent").getElementsByTagName("a");
is(links.length, 5, "wrong number of linked items in feed preview");
for (var i = 0; i < links.length; i++) {

View File

@ -648,36 +648,13 @@ nsBrowserContentHandler.prototype = {
},
get startPage() {
var prefb = Components.classes["@mozilla.org/preferences-service;1"]
.getService(nsIPrefBranch);
var uri = prefb.getComplexValue("browser.startup.homepage",
nsIPrefLocalizedString).data;
var uri = Services.prefs.getComplexValue("browser.startup.homepage",
nsIPrefLocalizedString).data;
if (!uri) {
prefb.clearUserPref("browser.startup.homepage");
uri = prefb.getComplexValue("browser.startup.homepage",
nsIPrefLocalizedString).data;
Services.prefs.clearUserPref("browser.startup.homepage");
uri = Services.prefs.getComplexValue("browser.startup.homepage",
nsIPrefLocalizedString).data;
}
var count;
try {
count = prefb.getIntPref("browser.startup.homepage.count");
}
catch (e) {
return uri;
}
for (var i = 1; i < count; ++i) {
try {
var page = prefb.getComplexValue("browser.startup.homepage." + i,
nsIPrefLocalizedString).data;
uri += "\n" + page;
}
catch (e) {
}
}
return uri;
},

View File

@ -763,15 +763,21 @@ BrowserGlue.prototype = {
const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
const PREF_TELEMETRY_INFOURL = "toolkit.telemetry.infoURL";
const PREF_TELEMETRY_SERVER_OWNER = "toolkit.telemetry.server_owner";
// This is used to reprompt users when privacy message changes
const TELEMETRY_PROMPT_REV = 2;
var telemetryPrompted = null;
try {
// If the user hasn't already been prompted, ask if they want to
// send telemetry data.
if (Services.prefs.getBoolPref(PREF_TELEMETRY_ENABLED) ||
Services.prefs.getBoolPref(PREF_TELEMETRY_PROMPTED))
return;
telemetryPrompted = Services.prefs.getIntPref(PREF_TELEMETRY_PROMPTED);
} catch(e) {}
// If the user has seen the latest telemetry prompt, do not prompt again
// else clear old prefs and reprompt
if (telemetryPrompted === TELEMETRY_PROMPT_REV)
return;
Services.prefs.clearUserPref(PREF_TELEMETRY_PROMPTED);
Services.prefs.clearUserPref(PREF_TELEMETRY_ENABLED);
// Stick the notification onto the selected tab of the active browser window.
var win = this.getMostRecentBrowserWindow();
var browser = win.gBrowser; // for closure in notification bar callback
@ -802,7 +808,7 @@ BrowserGlue.prototype = {
];
// Set pref to indicate we've shown the notification.
Services.prefs.setBoolPref(PREF_TELEMETRY_PROMPTED, true);
Services.prefs.setIntPref(PREF_TELEMETRY_PROMPTED, TELEMETRY_PROMPT_REV);
var notification = notifyBox.appendNotification(telemetryPrompt, "telemetry", null, notifyBox.PRIORITY_INFO_LOW, buttons);
notification.persistence = 6; // arbitrary number, just so bar sticks around for a bit

View File

@ -143,13 +143,23 @@ let gSyncPane = {
gSyncUtils.resetPassphrase();
},
openSetup: function (resetSync) {
/**
* Invoke the Sync setup wizard.
*
* @param wizardType
* Indicates type of wizard to launch:
* null -- regular set up wizard
* "pair" -- pair a device first
* "reset" -- reset sync
*/
openSetup: function (wizardType) {
var win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
if (win)
win.focus();
else {
window.openDialog("chrome://browser/content/syncSetup.xul",
"weaveSetup", "centerscreen,chrome,resizable=no", resetSync);
"weaveSetup", "centerscreen,chrome,resizable=no",
wizardType);
}
},
@ -175,7 +185,7 @@ let gSyncPane = {
},
resetSync: function () {
this.openSetup(true);
this.openSetup("reset");
}
}

View File

@ -74,14 +74,17 @@
<deck id="weavePrefsDeck">
<vbox id="noAccount" align="center">
<spacer flex="1"/>
<button id="setupButton"
label="&setupButton.label;"
accesskey="&setupButton.accesskey;"
oncommand="gSyncPane.openSetup();"/>
<separator/>
<description id="syncDesc" flex="1">
<description id="syncDesc">
&weaveDesc.label;
</description>
<separator/>
<label class="text-link"
onclick="event.stopPropagation(); gSyncPane.openSetup(null);"
value="&setupButton.label;"/>
<separator/>
<label class="text-link"
onclick="event.stopPropagation(); gSyncPane.openSetup('pair');"
value="&pairDevice.label;"/>
<spacer flex="3"/>
</vbox>
@ -116,7 +119,7 @@
<label id="syncAddDeviceLabel"
class="text-link"
onclick="gSyncPane.openAddDevice(); return false;"
value="&addDevice.label;"/>
value="&pairDevice.label;"/>
</hbox>
<vbox>

View File

@ -0,0 +1,29 @@
ac_add_options --enable-application=browser
ac_add_options --disable-optimize
ac_add_options --enable-debug
ac_add_options --enable-libxul
ac_add_options --enable-tests
ac_add_options --enable-trace-malloc
CC=/tools/gcc-4.5/bin/gcc
CXX=/tools/gcc-4.5/bin/g++
# Avoid dependency on libstdc++ 4.5
ac_add_options --enable-stdcxx-compat
export CFLAGS="-gdwarf-2"
export CXXFLAGS="-gdwarf-2"
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
# Enable parallel compiling
mk_add_options MOZ_MAKE_FLAGS="-j4"
#Use ccache
ac_add_options --with-ccache=/usr/bin/ccache

View File

@ -0,0 +1,8 @@
ac_add_options --with-l10n-base=../../l10n-central
ac_add_options --enable-application=browser
ac_add_options --enable-official-branding
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
CC=/tools/gcc-4.3.3/installed/bin/gcc
CXX=/tools/gcc-4.3.3/installed/bin/g++

View File

@ -0,0 +1,37 @@
ac_add_options --enable-application=browser
ac_add_options --enable-optimize
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --disable-debug
ac_add_options --enable-tests
ac_add_options --enable-codesighs
# Nightlies only since this has a cost in performance
ac_add_options --enable-js-diagnostics
CC=/tools/gcc-4.5/bin/gcc
CXX=/tools/gcc-4.5/bin/g++
# Avoid dependency on libstdc++ 4.5
ac_add_options --enable-stdcxx-compat
export CFLAGS="-gdwarf-2"
export CXXFLAGS="-gdwarf-2"
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
# PGO
mk_add_options MOZ_PGO=1
mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'
# Enable parallel compiling
mk_add_options MOZ_MAKE_FLAGS="-j4"
#Use ccache
ac_add_options --with-ccache=/usr/bin/ccache

View File

@ -0,0 +1,38 @@
ac_add_options --enable-application=browser
ac_add_options --enable-optimize
ac_add_options --enable-update-packaging
ac_add_options --disable-debug
ac_add_options --enable-tests
ac_add_options --enable-codesighs
CC=/tools/gcc-4.5/bin/gcc
CXX=/tools/gcc-4.5/bin/g++
# Avoid dependency on libstdc++ 4.5
ac_add_options --enable-stdcxx-compat
export CFLAGS="-gdwarf-2"
export CXXFLAGS="-gdwarf-2"
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
# PGO
mk_add_options MOZ_PGO=1
mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'
# Enable parallel compiling
mk_add_options MOZ_MAKE_FLAGS="-j4"
#Use ccache
ac_add_options --with-ccache=/usr/bin/ccache
# QT Options
export PKG_CONFIG_PATH=/tools/qt-4.6.3/qt/lib/pkgconfig
ac_add_options --with-qtdir=/tools/qt-4.6.3/qt
ac_add_options --enable-default-toolkit=cairo-qt
ac_add_options --disable-crashreporter

View File

@ -0,0 +1,28 @@
ac_add_options --enable-application=browser
ac_add_options --enable-optimize
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --disable-debug
ac_add_options --enable-tests
ac_add_options --enable-official-branding
CC=/tools/gcc-4.5/bin/gcc
CXX=/tools/gcc-4.5/bin/g++
# Avoid dependency on libstdc++ 4.5
ac_add_options --enable-stdcxx-compat
export CFLAGS="-gdwarf-2"
export CXXFLAGS="-gdwarf-2"
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# PGO
mk_add_options MOZ_PGO=1
mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1

View File

@ -0,0 +1,40 @@
ac_add_options --enable-application=browser
ac_add_options --enable-optimize
ac_add_options --enable-update-packaging
ac_add_options --disable-debug
ac_add_options --enable-tests
ac_add_options --enable-codesighs
# Options for rpm versions of mozconfigs
PREFIX=/usr
LIBDIR=${PREFIX}/lib
ac_add_options --with-app-name=mozilla-nightly
ac_add_options --disable-updater
ac_add_options --prefix=$PREFIX
ac_add_options --libdir=$LIBDIR
CC=/tools/gcc-4.5/bin/gcc
CXX=/tools/gcc-4.5/bin/g++
# Avoid dependency on libstdc++ 4.5
ac_add_options --enable-stdcxx-compat
export CFLAGS="-gdwarf-2"
export CXXFLAGS="-gdwarf-2"
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
mk_add_options MOZ_PGO=1
mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py'
# Enable parallel compiling
mk_add_options MOZ_MAKE_FLAGS="-j4"
#Use ccache
ac_add_options --with-ccache=/usr/bin/ccache

View File

@ -0,0 +1,25 @@
ac_add_options --enable-application=browser
ac_add_options --disable-optimize
ac_add_options --enable-debug
ac_add_options --enable-tests
ac_add_options --enable-trace-malloc
CC=/tools/gcc-4.5/bin/gcc
CXX=/tools/gcc-4.5/bin/g++
# Avoid dependency on libstdc++ 4.5
ac_add_options --enable-stdcxx-compat
#export CFLAGS="-gdwarf-2"
#export CXXFLAGS="-gdwarf-2"
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
# Enable parallel compiling
mk_add_options MOZ_MAKE_FLAGS="-j4"

View File

@ -0,0 +1,8 @@
ac_add_options --with-l10n-base=../../l10n-central
ac_add_options --enable-application=browser
ac_add_options --enable-official-branding
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
CC=/tools/gcc/bin/gcc
CXX=/tools/gcc/bin/g++

View File

@ -0,0 +1,37 @@
ac_add_options --enable-application=browser
ac_add_options --enable-optimize
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --disable-debug
ac_add_options --enable-tests
ac_add_options --enable-codesighs
# Nightlies only since this has a cost in performance
ac_add_options --enable-js-diagnostics
CC=/tools/gcc-4.5/bin/gcc
CXX=/tools/gcc-4.5/bin/g++
# Avoid dependency on libstdc++ 4.5
ac_add_options --enable-stdcxx-compat
export CFLAGS="-gdwarf-2"
export CXXFLAGS="-gdwarf-2"
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
# PGO
mk_add_options MOZ_PGO=1
mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'
# Enable parallel compiling
mk_add_options MOZ_MAKE_FLAGS="-j4"
#Use ccache
ac_add_options --with-ccache=/usr/bin/ccache

View File

@ -0,0 +1,28 @@
ac_add_options --enable-application=browser
ac_add_options --enable-optimize
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --disable-debug
ac_add_options --enable-tests
ac_add_options --enable-official-branding
CC=/tools/gcc-4.5/bin/gcc
CXX=/tools/gcc-4.5/bin/g++
# Avoid dependency on libstdc++ 4.5
ac_add_options --enable-stdcxx-compat
export CFLAGS="-gdwarf-2"
export CXXFLAGS="-gdwarf-2"
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# PGO
mk_add_options MOZ_PGO=1
mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py 10'
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1

View File

@ -0,0 +1,40 @@
ac_add_options --enable-application=browser
ac_add_options --enable-optimize
ac_add_options --enable-update-packaging
ac_add_options --disable-debug
ac_add_options --enable-tests
ac_add_options --enable-codesighs
# Options for rpm versions of mozconfigs
PREFIX=/usr
LIBDIR=${PREFIX}/lib64
ac_add_options --with-app-name=mozilla-nightly
ac_add_options --disable-updater
ac_add_options --prefix=$PREFIX
ac_add_options --libdir=$LIBDIR
CC=/tools/gcc-4.5/bin/gcc
CXX=/tools/gcc-4.5/bin/g++
# Avoid dependency on libstdc++ 4.5
ac_add_options --enable-stdcxx-compat
export CFLAGS="-gdwarf-2"
export CXXFLAGS="-gdwarf-2"
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
mk_add_options MOZ_PGO=1
mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) @MOZ_OBJDIR@/_profile/pgo/profileserver.py'
# Enable parallel compiling
mk_add_options MOZ_MAKE_FLAGS="-j4"
#Use ccache
ac_add_options --with-ccache=/usr/bin/ccache

View File

@ -0,0 +1,24 @@
. $topsrcdir/build/macosx/universal/mozconfig
ac_add_options --enable-application=browser
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --enable-tests
ac_add_options --enable-codesighs
ac_add_options --disable-install-strip
# Nightlies only since this has a cost in performance
ac_add_options --enable-js-diagnostics
export CFLAGS="-gdwarf-2"
export CXXFLAGS="-gdwarf-2"
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
mk_add_options MOZ_MAKE_FLAGS="-j4"

View File

@ -0,0 +1,19 @@
. $topsrcdir/build/macosx/universal/mozconfig
ac_add_options --enable-application=browser
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --enable-tests
ac_add_options --enable-official-branding
export CFLAGS="-gdwarf-2"
export CXXFLAGS="-gdwarf-2"
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1

View File

@ -0,0 +1,28 @@
# Just like nightlies, but without tests, not on an update channel, and with
# shark and dtrace enabled
. $topsrcdir/build/macosx/universal/mozconfig
ac_add_options --enable-application=browser
ac_add_options --disable-tests
ac_add_options --disable-install-strip
export CFLAGS="-gdwarf-2"
export CXXFLAGS="-gdwarf-2"
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
# Enable parallel compiling
mk_add_options MOZ_MAKE_FLAGS="-j4"
# shark specific options
ac_add_options --enable-shark
ac_add_options --enable-dtrace
ac_add_options --enable-debugger-info-modules
# Need this to prevent name conflicts with the normal nightly build packages
export MOZ_PKG_SPECIAL="shark"

View File

@ -0,0 +1,22 @@
# Don't use the standard mozconfig. We don't want universal for a debug build.
#. $topsrcdir/build/macosx/universal/mozconfig
ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.5.sdk
ac_add_options --disable-optimize
ac_add_options --enable-debug
ac_add_options --enable-libxul
ac_add_options --enable-application=browser
ac_add_options --enable-tests
ac_add_options --enable-trace-malloc
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# Enable parallel compiling
mk_add_options MOZ_MAKE_FLAGS="-j4"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1

View File

@ -0,0 +1,18 @@
ac_add_options --disable-optimize
ac_add_options --enable-debug
ac_add_options --enable-libxul
ac_add_options --enable-application=browser
ac_add_options --enable-tests
ac_add_options --enable-trace-malloc
ac_add_options --enable-accessibility
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
ac_add_options --enable-debug-symbols="-gdwarf-2"
# Enable parallel compiling
mk_add_options MOZ_MAKE_FLAGS="-j4"
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1

View File

@ -0,0 +1,5 @@
ac_add_options --with-l10n-base=../../l10n-central
ac_add_options --enable-application=browser
ac_add_options --enable-official-branding
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging

View File

@ -0,0 +1,15 @@
ac_add_options --enable-application=browser
ac_add_options --enable-jemalloc
ac_add_options --disable-optimize
ac_add_options --enable-debug
ac_add_options --enable-libxul
ac_add_options --enable-trace-malloc
ac_add_options --enable-tests
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
mk_add_options MOZ_MAKE_FLAGS=-j1

View File

@ -0,0 +1,5 @@
ac_add_options --enable-application=browser
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --enable-official-branding
ac_add_options --with-l10n-base=../../l10n-central

View File

@ -0,0 +1,21 @@
# for pgo
mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) $(MOZ_OBJDIR)/_profile/pgo/profileserver.py'
ac_add_options --enable-application=browser
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --enable-jemalloc
ac_add_options --enable-tests
# Nightlies only since this has a cost in performance
ac_add_options --enable-js-diagnostics
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
mk_add_options MOZ_MAKE_FLAGS=-j1

View File

@ -0,0 +1,17 @@
# for pgo
mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) $(MOZ_OBJDIR)/_profile/pgo/profileserver.py'
ac_add_options --enable-application=browser
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --enable-jemalloc
ac_add_options --enable-tests
ac_add_options --enable-official-branding
# For NSS symbols
export MOZ_DEBUG_SYMBOLS=1
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1

View File

@ -0,0 +1,14 @@
ac_add_options --target=x86_64-pc-mingw32
ac_add_options --host=x86_64-pc-mingw32
ac_add_options --enable-application=browser
ac_add_options --enable-jemalloc
ac_add_options --disable-optimize
ac_add_options --enable-debug
ac_add_options --enable-libxul
ac_add_options --enable-trace-malloc
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
mk_add_options MOZ_MAKE_FLAGS=-j1

View File

@ -0,0 +1,21 @@
ac_add_options --target=x86_64-pc-mingw32
ac_add_options --host=x86_64-pc-mingw32
# for pgo
mk_add_options PROFILE_GEN_SCRIPT='$(PYTHON) $(MOZ_OBJDIR)/_profile/pgo/profileserver.py'
ac_add_options --enable-application=browser
ac_add_options --enable-update-channel=${MOZ_UPDATE_CHANNEL}
ac_add_options --enable-update-packaging
ac_add_options --enable-jemalloc
ac_add_options --enable-debug-symbols
# Nightlies only since this has a cost in performance
ac_add_options --enable-js-diagnostics
# Needed to enable breakpad in application.ini
export MOZILLA_OFFICIAL=1
export MOZ_TELEMETRY_REPORTING=1
mk_add_options MOZ_MAKE_FLAGS=-j1

View File

@ -1,5 +1,5 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
@ -290,10 +290,14 @@ Highlighter.prototype = {
closeButton.id = "highlighter-close-button";
closeButton.appendChild(this.chromeDoc.createElement("image"));
closeButton.addEventListener("click",
this.IUI.closeInspectorUI.bind(this.IUI), false);
let boundCloseEventHandler = this.IUI.closeInspectorUI.bind(this.IUI, false);
closeButton.addEventListener("click", boundCloseEventHandler, false);
aParent.appendChild(closeButton);
this.boundCloseEventHandler = boundCloseEventHandler;
this.closeButton = closeButton;
},
/**
@ -303,6 +307,11 @@ Highlighter.prototype = {
{
this.browser.removeEventListener("scroll", this, true);
this.browser.removeEventListener("resize", this, true);
this.closeButton.removeEventListener("click", this.boundCloseEventHandler, false);
this.boundCloseEventHandler = null;
this.closeButton.parentNode.removeChild(this.closeButton);
this.closeButton = null;
this._contentRect = null;
this._highlightRect = null;
this._highlighting = false;
this.veilTopBox = null;
@ -337,7 +346,7 @@ Highlighter.prototype = {
highlight: function Highlighter_highlight(aScroll)
{
// node is not set or node is not highlightable, bail
if (!this.node || !this.isNodeHighlightable()) {
if (!this.node || !this.isNodeHighlightable(this.node)) {
return;
}
@ -439,28 +448,41 @@ Highlighter.prototype = {
*/
highlightRectangle: function Highlighter_highlightRectangle(aRect)
{
let oldRect = this._highlightRect;
let oldRect = this._contentRect;
if (oldRect && aRect.top == oldRect.top && aRect.left == oldRect.left &&
aRect.width == oldRect.width && aRect.height == oldRect.height) {
return this._highlighting; // same rectangle
}
if (aRect.left >= 0 && aRect.top >= 0 &&
aRect.width > 0 && aRect.height > 0) {
// get page zoom factor, if any
let zoom =
this.win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils)
.screenPixelsPerCSSPixel;
// adjust rect for zoom scaling
let aRectScaled = {};
for (let prop in aRect) {
aRectScaled[prop] = aRect[prop] * zoom;
}
if (aRectScaled.left >= 0 && aRectScaled.top >= 0 &&
aRectScaled.width > 0 && aRectScaled.height > 0) {
// The bottom div and the right div are flexibles (flex=1).
// We don't need to resize them.
this.veilTopBox.style.height = aRect.top + "px";
this.veilLeftBox.style.width = aRect.left + "px";
this.veilMiddleBox.style.height = aRect.height + "px";
this.veilTransparentBox.style.width = aRect.width + "px";
this.veilTopBox.style.height = aRectScaled.top + "px";
this.veilLeftBox.style.width = aRectScaled.left + "px";
this.veilMiddleBox.style.height = aRectScaled.height + "px";
this.veilTransparentBox.style.width = aRectScaled.width + "px";
this._highlighting = true;
} else {
this.unhighlight();
}
this._highlightRect = aRect;
this._contentRect = aRect; // save orig (non-scaled) rect
this._highlightRect = aRectScaled; // and save the scaled rect.
return this._highlighting;
},
@ -588,18 +610,18 @@ Highlighter.prototype = {
get highlitNode()
{
// Not highlighting? Bail.
if (!this._highlighting || !this._highlightRect) {
if (!this._highlighting || !this._contentRect) {
return null;
}
let a = {
x: this._highlightRect.left,
y: this._highlightRect.top
x: this._contentRect.left,
y: this._contentRect.top
};
let b = {
x: a.x + this._highlightRect.width,
y: a.y + this._highlightRect.height
x: a.x + this._contentRect.width,
y: a.y + this._contentRect.height
};
// Get midpoint of diagonal line.
@ -610,17 +632,19 @@ Highlighter.prototype = {
},
/**
* Is this.node highlightable?
* Is the specified node highlightable?
*
* @param nsIDOMNode aNode
* the DOM element in question
* @returns boolean
* True if the node is highlightable or false otherwise.
*/
isNodeHighlightable: function Highlighter_isNodeHighlightable()
isNodeHighlightable: function Highlighter_isNodeHighlightable(aNode)
{
if (!this.node || this.node.nodeType != this.node.ELEMENT_NODE) {
if (aNode.nodeType != aNode.ELEMENT_NODE) {
return false;
}
let nodeName = this.node.nodeName.toLowerCase();
let nodeName = aNode.nodeName.toLowerCase();
return !INSPECTOR_INVISIBLE_ELEMENTS[nodeName];
},
@ -1147,6 +1171,69 @@ InspectorUI.prototype = {
event.stopPropagation();
}
break;
case this.chromeWin.KeyEvent.DOM_VK_LEFT:
let node;
if (this.selection) {
node = this.selection.parentNode;
} else {
node = this.defaultSelection;
}
if (node && this.highlighter.isNodeHighlightable(node)) {
this.inspectNode(node, true);
}
event.preventDefault();
event.stopPropagation();
break;
case this.chromeWin.KeyEvent.DOM_VK_RIGHT:
if (this.selection) {
// Find the first child that is highlightable.
for (let i = 0; i < this.selection.childNodes.length; i++) {
node = this.selection.childNodes[i];
if (node && this.highlighter.isNodeHighlightable(node)) {
break;
}
}
} else {
node = this.defaultSelection;
}
if (node && this.highlighter.isNodeHighlightable(node)) {
this.inspectNode(node, true);
}
event.preventDefault();
event.stopPropagation();
break;
case this.chromeWin.KeyEvent.DOM_VK_UP:
if (this.selection) {
// Find a previous sibling that is highlightable.
node = this.selection.previousSibling;
while (node && !this.highlighter.isNodeHighlightable(node)) {
node = node.previousSibling;
}
} else {
node = this.defaultSelection;
}
if (node && this.highlighter.isNodeHighlightable(node)) {
this.inspectNode(node, true);
}
event.preventDefault();
event.stopPropagation();
break;
case this.chromeWin.KeyEvent.DOM_VK_DOWN:
if (this.selection) {
// Find a next sibling that is highlightable.
node = this.selection.nextSibling;
while (node && !this.highlighter.isNodeHighlightable(node)) {
node = node.nextSibling;
}
} else {
node = this.defaultSelection;
}
if (node && this.highlighter.isNodeHighlightable(node)) {
this.inspectNode(node, true);
}
event.preventDefault();
event.stopPropagation();
break;
}
break;
}
@ -1181,11 +1268,13 @@ InspectorUI.prototype = {
*
* @param aNode
* the element in the document to inspect
* @param aScroll
* force scroll?
*/
inspectNode: function IUI_inspectNode(aNode)
inspectNode: function IUI_inspectNode(aNode, aScroll)
{
this.select(aNode, true, true);
this.highlighter.highlightNode(aNode);
this.highlighter.highlightNode(aNode, { scroll: aScroll });
},
/**

View File

@ -60,6 +60,8 @@ _BROWSER_FILES = \
browser_inspector_editor.js \
browser_inspector_bug_566084_location_changed.js \
browser_inspector_infobar.js \
browser_inspector_bug_690361.js \
browser_inspector_bug_672902_keyboard_shortcuts.js \
$(NULL)
# Disabled due to constant failures

View File

@ -0,0 +1,142 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
// Tests that the keybindings for highlighting different elements work as
// intended.
function test()
{
waitForExplicitFinish();
let doc;
let node;
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function onload() {
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
doc = content.document;
waitForFocus(setupKeyBindingsTest, content);
}, true);
content.location = "data:text/html,<html><head><title>Test for the " +
"highlighter keybindings</title></head><body><h1>Hello" +
"</h1><p><strong>Greetings, earthlings!</strong> I come" +
" in peace.</body></html>";
function setupKeyBindingsTest()
{
Services.obs.addObserver(findAndHighlightNode,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED,
false);
InspectorUI.toggleInspectorUI();
}
function findAndHighlightNode()
{
Services.obs.removeObserver(findAndHighlightNode,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
executeSoon(function() {
Services.obs.addObserver(highlightBodyNode,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING,
false);
// Test that navigating around without a selected node gets us to the
// body element.
node = doc.querySelector("body");
EventUtils.synthesizeKey("VK_RIGHT", { });
});
}
function highlightBodyNode()
{
Services.obs.removeObserver(highlightBodyNode,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
is(InspectorUI.selection, node, "selected body element");
executeSoon(function() {
Services.obs.addObserver(highlightHeaderNode,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING,
false);
// Test that moving to the child works.
node = doc.querySelector("h1");
EventUtils.synthesizeKey("VK_RIGHT", { });
});
}
function highlightHeaderNode()
{
Services.obs.removeObserver(highlightHeaderNode,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
is(InspectorUI.selection, node, "selected h1 element");
executeSoon(function() {
Services.obs.addObserver(highlightParagraphNode,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING,
false);
// Test that moving to the next sibling works.
node = doc.querySelector("p");
EventUtils.synthesizeKey("VK_DOWN", { });
});
}
function highlightParagraphNode()
{
Services.obs.removeObserver(highlightParagraphNode,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
is(InspectorUI.selection, node, "selected p element");
executeSoon(function() {
Services.obs.addObserver(highlightHeaderNodeAgain,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING,
false);
// Test that moving to the previous sibling works.
node = doc.querySelector("h1");
EventUtils.synthesizeKey("VK_UP", { });
});
}
function highlightHeaderNodeAgain()
{
Services.obs.removeObserver(highlightHeaderNodeAgain,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
is(InspectorUI.selection, node, "selected h1 element");
executeSoon(function() {
Services.obs.addObserver(highlightParentNode,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING,
false);
// Test that moving to the parent works.
node = doc.querySelector("body");
EventUtils.synthesizeKey("VK_LEFT", { });
});
}
function highlightParentNode()
{
Services.obs.removeObserver(highlightParentNode,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
is(InspectorUI.selection, node, "selected body element");
// Test that locking works.
EventUtils.synthesizeKey("VK_RETURN", { });
executeSoon(isTheNodeLocked);
}
function isTheNodeLocked()
{
ok(!InspectorUI.inspecting, "the node is locked");
Services.obs.addObserver(finishUp,
InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED,
false);
InspectorUI.closeInspectorUI();
}
function finishUp() {
Services.obs.removeObserver(finishUp,
InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
doc = node = null;
gBrowser.removeCurrentTab();
finish();
}
}

View File

@ -0,0 +1,139 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=2 et sw=2 tw=80: */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Inspector Initialization and Shutdown Tests.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Rob Campbell <rcampbell@mozilla.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
let doc;
let salutation;
let closing;
function createDocument()
{
doc.body.innerHTML = '<div id="first" style="{ margin: 10em; ' +
'font-size: 14pt; font-family: helvetica, sans-serif; color: #AAA}">\n' +
'<h1>Some header text</h1>\n' +
'<p id="salutation" style="{font-size: 12pt}">hi.</p>\n' +
'<p id="body" style="{font-size: 12pt}">I am a test-case. This text exists ' +
'solely to provide some things to test the inspector initialization.</p>\n' +
'If you are reading this, you should go do something else instead. Maybe ' +
'read a book. Or better yet, write some test-cases for another bit of code. ' +
'<span style="{font-style: italic}">Maybe more inspector test-cases!</span></p>\n' +
'<p id="closing">end transmission</p>\n' +
'</div>';
doc.title = "Inspector Opening and Closing Test";
startInspectorTests();
}
function startInspectorTests()
{
ok(InspectorUI, "InspectorUI variable exists");
Services.obs.addObserver(runInspectorTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
InspectorUI.toggleInspectorUI();
}
function runInspectorTests()
{
Services.obs.removeObserver(runInspectorTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
Services.obs.addObserver(closeInspectorTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED, false);
ok(InspectorUI.toolbar, "we have the toolbar.");
ok(!InspectorUI.toolbar.hidden, "toolbar is visible");
ok(InspectorUI.inspecting, "Inspector is inspecting");
ok(!InspectorUI.treePanel.isOpen(), "Inspector Tree Panel is not open");
ok(InspectorUI.highlighter, "Highlighter is up");
salutation = doc.getElementById("salutation");
InspectorUI.inspectNode(salutation);
let button = document.getElementById("highlighter-close-button");
button.click();
}
function closeInspectorTests()
{
Services.obs.removeObserver(closeInspectorTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.CLOSED);
Services.obs.addObserver(inspectorOpenedTrap,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
ok(!InspectorUI.isInspectorOpen, "Inspector Tree Panel is not open");
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.removeCurrentTab();
}, true);
gBrowser.tabContainer.addEventListener("TabSelect", finishInspectorTests, false);
}
function inspectorOpenedTrap()
{
ok(false, "Inspector opened! Should not have done so.");
InspectorUI.closeInspectorUI(false);
}
function finishInspectorTests()
{
gBrowser.tabContainer.removeEventListener("TabSelect", finishInspectorTests, false);
Services.obs.removeObserver(inspectorOpenedTrap,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED);
requestLongerTimeout(4); // give the inspector a chance to open
executeSoon(function() {
gBrowser.removeCurrentTab();
finish();
});
}
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.selectedBrowser.addEventListener("load", function() {
gBrowser.selectedBrowser.removeEventListener("load", arguments.callee, true);
doc = content.document;
waitForFocus(createDocument, content);
}, true);
content.location = "data:text/html,basic tests for inspector";
}

View File

@ -40,6 +40,7 @@
let doc;
let h1;
let div;
function createDocument()
{
@ -49,7 +50,7 @@ function createDocument()
let p2 = doc.createElement("p");
let div2 = doc.createElement("div");
let p3 = doc.createElement("p");
doc.title = "Inspector Tree Selection Test";
doc.title = "Inspector Highlighter Meatballs";
h1.textContent = "Inspector Tree Selection Test";
p1.textContent = "This is some example text";
p2.textContent = "Lorem ipsum dolor sit amet, consectetur adipisicing " +
@ -66,19 +67,28 @@ function createDocument()
"dolor in reprehenderit in voluptate velit esse cillum dolore eu " +
"fugiat nulla pariatur. Excepteur sint occaecat cupidatat non " +
"proident, sunt in culpa qui officia deserunt mollit anim id est laborum.";
let div3 = doc.createElement("div");
div3.id = "checkOutThisWickedSpread";
div3.setAttribute("style", "position: absolute; top: 20px; right: 20px; height: 20px; width: 20px; background-color: yellow; border: 1px dashed black;");
let p4 = doc.createElement("p");
p4.setAttribute("style", "font-weight: 200; font-size: 8px; text-align: center;");
p4.textContent = "Smörgåsbord!";
div.appendChild(h1);
div.appendChild(p1);
div.appendChild(p2);
div2.appendChild(p3);
div3.appendChild(p4);
doc.body.appendChild(div);
doc.body.appendChild(div2);
doc.body.appendChild(div3);
setupHighlighterTests();
}
function setupHighlighterTests()
{
h1 = doc.querySelectorAll("h1")[0];
ok(h1, "we have the header node");
h1 = doc.querySelector("h1");
ok(h1, "we have the header");
Services.obs.addObserver(runSelectionTests,
InspectorUI.INSPECTOR_NOTIFICATIONS.OPENED, false);
InspectorUI.toggleInspectorUI();
@ -99,7 +109,7 @@ function runSelectionTests()
function performTestComparisons(evt)
{
Services.obs.removeObserver(performTestComparisons,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
InspectorUI.stopInspecting();
ok(InspectorUI.highlighter.isHighlighting, "highlighter is highlighting");
@ -107,7 +117,61 @@ function performTestComparisons(evt)
is(InspectorUI.selection, h1, "selection matches node");
is(InspectorUI.selection, InspectorUI.highlighter.highlitNode, "selection matches highlighter");
doc = h1 = null;
Services.obs.addObserver(finishTestComparisons,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING, false);
div = doc.querySelector("div#checkOutThisWickedSpread");
executeSoon(function() {
InspectorUI.inspectNode(div);
});
}
function finishTestComparisons()
{
Services.obs.removeObserver(finishTestComparisons,
InspectorUI.INSPECTOR_NOTIFICATIONS.HIGHLIGHTING);
// get dimensions of div element
let divDims = div.getBoundingClientRect();
let divWidth = divDims.width;
let divHeight = divDims.height;
// get dimensions of transparent veil box over element
let veilBoxDims =
InspectorUI.highlighter.veilTransparentBox.getBoundingClientRect();
let veilBoxWidth = veilBoxDims.width;
let veilBoxHeight = veilBoxDims.height;
is(veilBoxWidth, divWidth, "transparent veil box width matches dimensions of element (no zoom)");
is(veilBoxHeight, divHeight, "transparent veil box height matches dimensions of element (no zoom)");
// zoom the page by a factor of 2
let contentViewer = InspectorUI.browser.docShell.contentViewer
.QueryInterface(Ci.nsIMarkupDocumentViewer);
contentViewer.fullZoom = 2;
// check what zoom factor we're at, should be 2
let zoom =
InspectorUI.win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIDOMWindowUtils)
.screenPixelsPerCSSPixel;
is(zoom, 2, "zoom is 2?");
// simulate the zoomed dimensions of the div element
let divDims = div.getBoundingClientRect();
let divWidth = divDims.width * zoom;
let divHeight = divDims.height * zoom;
// now zoomed, get new dimensions of transparent veil box over element
let veilBoxDims = InspectorUI.highlighter.veilTransparentBox.getBoundingClientRect();
let veilBoxWidth = veilBoxDims.width;
let veilBoxHeight = veilBoxDims.height;
is(veilBoxWidth, divWidth, "transparent veil box width matches width of element (2x zoom)");
is(veilBoxHeight, divHeight, "transparent veil box height matches width of element (2x zoom)");
doc = h1 = div = null;
executeSoon(finishUp);
}

View File

@ -38,7 +38,6 @@
*
* ***** END LICENSE BLOCK ***** */
// WARNING: do not 'use_strict' without reading the notes in envEval;
var EXPORTED_SYMBOLS = ["Templater"];
@ -47,20 +46,31 @@ const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
const Node = Ci.nsIDOMNode;
// WARNING: do not 'use_strict' without reading the notes in _envEval();
/**
* A templater that allows one to quickly template DOM nodes.
*/
function Templater() {
this.scope = [];
this.stack = [];
}
/**
* Recursive function to walk the tree processing the attributes as it goes.
* @param node the node to process.
* @param node the node to process. If you pass a string in instead of a DOM
* element, it is assumed to be an id for use with document.getElementById()
* @param data the data to use for node processing.
*/
Templater.prototype.processNode = function(node, data) {
this.scope.push(node.nodeName + (node.id ? '#' + node.id : ''));
if (typeof node === 'string') {
node = document.getElementById(node);
}
if (data == null) {
data = {};
}
this.stack.push(node.nodeName + (node.id ? '#' + node.id : ''));
try {
// Process attributes
if (node.attributes && node.attributes.length) {
@ -68,11 +78,11 @@ Templater.prototype.processNode = function(node, data) {
// some types of processing from happening, and foreach must come first
// because it defines new data on which 'if' might depend.
if (node.hasAttribute('foreach')) {
this.processForEach(node, data);
this._processForEach(node, data);
return;
}
if (node.hasAttribute('if')) {
if (!this.processIf(node, data)) {
if (!this._processIf(node, data)) {
return;
}
}
@ -81,22 +91,22 @@ Templater.prototype.processNode = function(node, data) {
// It's good to clean up the attributes when we've processed them,
// but if we do it straight away, we mess up the array index
var attrs = Array.prototype.slice.call(node.attributes);
for (let i = 0, attLen = attrs.length; i < attLen; i++) {
for (var i = 0; i < attrs.length; i++) {
var value = attrs[i].value;
var name = attrs[i].name;
this.scope.push(name);
this.stack.push(name);
try {
if (name === 'save') {
// Save attributes are a setter using the node
value = this.stripBraces(value);
this.property(value, data, node);
value = this._stripBraces(value);
this._property(value, data, node);
node.removeAttribute('save');
} else if (name.substring(0, 2) === 'on') {
// Event registration relies on property doing a bind
value = this.stripBraces(value);
var func = this.property(value, data);
value = this._stripBraces(value);
var func = this._property(value, data);
if (typeof func !== 'function') {
this.handleError('Expected ' + value +
this._handleError('Expected ' + value +
' to resolve to a function, but got ' + typeof func);
}
node.removeAttribute(name);
@ -107,10 +117,9 @@ Templater.prototype.processNode = function(node, data) {
}
} else {
// Replace references in all other attributes
var self = this;
var newValue = value.replace(/\$\{[^}]*\}/g, function(path) {
return self.envEval(path.slice(2, -1), data, value);
});
return this._envEval(path.slice(2, -1), data, value);
}.bind(this));
// Remove '_' prefix of attribute names so the DOM won't try
// to use them before we've processed the template
if (name.charAt(0) === '_') {
@ -121,43 +130,44 @@ Templater.prototype.processNode = function(node, data) {
}
}
} finally {
this.scope.pop();
this.stack.pop();
}
}
}
// Loop through our children calling processNode. First clone them, so the
// set of nodes that we visit will be unaffected by additions or removals.
var children = Array.prototype.slice.call(node.childNodes);
for (let j = 0, numChildren = children.length; j < numChildren; j++) {
this.processNode(children[j], data);
var childNodes = Array.prototype.slice.call(node.childNodes);
for (var j = 0; j < childNodes.length; j++) {
this.processNode(childNodes[j], data);
}
if (node.nodeType === Ci.nsIDOMNode.TEXT_NODE) {
this.processTextNode(node, data);
if (node.nodeType === Node.TEXT_NODE) {
this._processTextNode(node, data);
}
} finally {
this.scope.pop();
delete data.__element;
this.stack.pop();
}
};
/**
* Handle <x if="${...}">
* @param node An element with an 'if' attribute
* @param data The data to use with envEval
* @param data The data to use with _envEval()
* @returns true if processing should continue, false otherwise
*/
Templater.prototype.processIf = function(node, data) {
this.scope.push('if');
Templater.prototype._processIf = function(node, data) {
this.stack.push('if');
try {
var originalValue = node.getAttribute('if');
var value = this.stripBraces(originalValue);
var value = this._stripBraces(originalValue);
var recurse = true;
try {
var reply = this.envEval(value, data, originalValue);
var reply = this._envEval(value, data, originalValue);
recurse = !!reply;
} catch (ex) {
this.handleError('Error with \'' + value + '\'', ex);
this._handleError('Error with \'' + value + '\'', ex);
recurse = false;
}
if (!recurse) {
@ -166,18 +176,22 @@ Templater.prototype.processIf = function(node, data) {
node.removeAttribute('if');
return recurse;
} finally {
this.scope.pop();
this.stack.pop();
}
};
/**
* Handle <x foreach="param in ${array}"> and the special case of
* <loop foreach="param in ${array}">
* <loop foreach="param in ${array}">.
* This function is responsible for extracting what it has to do from the
* attributes, and getting the data to work on (including resolving promises
* in getting the array). It delegates to _processForEachLoop to actually
* unroll the data.
* @param node An element with a 'foreach' attribute
* @param data The data to use with envEval
* @param data The data to use with _envEval()
*/
Templater.prototype.processForEach = function(node, data) {
this.scope.push('foreach');
Templater.prototype._processForEach = function(node, data) {
this.stack.push('foreach');
try {
var originalValue = node.getAttribute('foreach');
var value = originalValue;
@ -185,61 +199,86 @@ Templater.prototype.processForEach = function(node, data) {
var paramName = 'param';
if (value.charAt(0) === '$') {
// No custom loop variable name. Use the default: 'param'
value = this.stripBraces(value);
value = this._stripBraces(value);
} else {
// Extract the loop variable name from 'NAME in ${ARRAY}'
var nameArr = value.split(' in ');
paramName = nameArr[0].trim();
value = this.stripBraces(nameArr[1].trim());
value = this._stripBraces(nameArr[1].trim());
}
node.removeAttribute('foreach');
try {
var self = this;
// Process a single iteration of a loop
var processSingle = function(member, node, ref) {
var clone = node.cloneNode(true);
clone.removeAttribute('foreach');
ref.parentNode.insertBefore(clone, ref);
data[paramName] = member;
self.processNode(clone, data);
delete data[paramName];
};
// processSingle is no good for <loop> nodes where we want to work on
// the children rather than the node itself
var processAll = function(scope, member) {
self.scope.push(scope);
try {
if (node.nodeName === 'loop') {
for (let i = 0, numChildren = node.children.length; i < numChildren; i++) {
processSingle(member, node.children[i], node);
}
} else {
processSingle(member, node, node);
}
} finally {
self.scope.pop();
}
};
let reply = this.envEval(value, data, originalValue);
if (Array.isArray(reply)) {
reply.forEach(function(data, i) {
processAll('' + i, data)
}, this);
} else {
for (let param in reply) {
if (reply.hasOwnProperty(param)) {
processAll(param, param);
}
}
}
var evaled = this._envEval(value, data, originalValue);
this._handleAsync(evaled, node, function(reply, siblingNode) {
this._processForEachLoop(reply, node, siblingNode, data, paramName);
}.bind(this));
node.parentNode.removeChild(node);
} catch (ex) {
this.handleError('Error with \'' + value + '\'', ex);
this._handleError('Error with \'' + value + '\'', ex);
}
} finally {
this.scope.pop();
this.stack.pop();
}
};
/**
* Called by _processForEach to handle looping over the data in a foreach loop.
* This works with both arrays and objects.
* Calls _processForEachMember() for each member of 'set'
* @param set The object containing the data to loop over
* @param template The node to copy for each set member
* @param sibling The sibling node to which we add things
* @param data the data to use for node processing
* @param paramName foreach loops have a name for the parameter currently being
* processed. The default is 'param'. e.g. <loop foreach="param in ${x}">...
*/
Templater.prototype._processForEachLoop = function(set, template, sibling, data, paramName) {
if (Array.isArray(set)) {
set.forEach(function(member, i) {
this._processForEachMember(member, template, sibling, data, paramName, '' + i);
}, this);
} else {
for (var member in set) {
if (set.hasOwnProperty(member)) {
this._processForEachMember(member, template, sibling, data, paramName, member);
}
}
}
};
/**
* Called by _processForEachLoop() to resolve any promises in the array (the
* array itself can also be a promise, but that is resolved by
* _processForEach()). Handle <LOOP> elements (which are taken out of the DOM),
* clone the template, and pass the processing on to processNode().
* @param member The data item to use in templating
* @param template The node to copy for each set member
* @param siblingNode The parent node to which we add things
* @param data the data to use for node processing
* @param paramName The name given to 'member' by the foreach attribute
* @param frame A name to push on the stack for debugging
*/
Templater.prototype._processForEachMember = function(member, template, siblingNode, data, paramName, frame) {
this.stack.push(frame);
try {
this._handleAsync(member, siblingNode, function(reply, node) {
data[paramName] = reply;
if (node.nodeName.toLowerCase() === 'loop') {
for (var i = 0; i < node.childNodes.length; i++) {
var clone = node.childNodes[i].cloneNode(true);
node.parentNode.insertBefore(clone, node);
this.processNode(clone, data);
}
} else {
var clone = template.cloneNode(true);
clone.removeAttribute('foreach');
node.parentNode.insertBefore(clone, node);
this.processNode(clone, data);
}
delete data[paramName];
}.bind(this));
} finally {
this.stack.pop();
}
};
@ -248,9 +287,9 @@ Templater.prototype.processForEach = function(node, data) {
* sections parsed out. We replace the node by altering node.parentNode but
* we could probably use a DOM Text API to achieve the same thing.
* @param node The Text node to work on
* @param data The data to use in calls to envEval
* @param data The data to use in calls to _envEval()
*/
Templater.prototype.processTextNode = function(node, data) {
Templater.prototype._processTextNode = function(node, data) {
// Replace references in other attributes
var value = node.data;
// We can't use the string.replace() with function trick (see generic
@ -270,33 +309,68 @@ Templater.prototype.processTextNode = function(node, data) {
return;
}
if (part.charAt(0) === '$') {
part = this.envEval(part.slice(1), data, node.data);
part = this._envEval(part.slice(1), data, node.data);
}
// It looks like this was done a few lines above but see envEval
if (part === null) {
part = "null";
}
if (part === undefined) {
part = "undefined";
}
// if (isDOMElement(part)) { ... }
if (typeof part.cloneNode !== 'function') {
part = node.ownerDocument.createTextNode(part.toString());
}
node.parentNode.insertBefore(part, node);
this._handleAsync(part, node, function(reply, siblingNode) {
reply = this._toNode(reply, siblingNode.ownerDocument);
siblingNode.parentNode.insertBefore(reply, siblingNode);
}.bind(this));
}, this);
node.parentNode.removeChild(node);
}
};
/**
* Helper to convert a 'thing' to a DOM Node.
* This is (obviously) a no-op for DOM Elements (which are detected using
* 'typeof thing.cloneNode !== "function"' (is there a better way that will
* work in all environments, including a .jsm?)
* Non DOM elements are converted to a string and wrapped in a TextNode.
*/
Templater.prototype._toNode = function(thing, document) {
if (thing == null) {
thing = '' + thing;
}
// if (isDOMElement(reply)) { ... }
if (typeof thing.cloneNode !== 'function') {
thing = document.createTextNode(thing.toString());
}
return thing;
};
/**
* A function to handle the fact that some nodes can be promises, so we check
* and resolve if needed using a marker node to keep our place before calling
* an inserter function.
* @param thing The object which could be real data or a promise of real data
* we use it directly if it's not a promise, or resolve it if it is.
* @param siblingNode The element before which we insert new elements.
* @param inserter The function to to the insertion. If thing is not a promise
* then _handleAsync() is just 'inserter(thing, siblingNode)'
*/
Templater.prototype._handleAsync = function(thing, siblingNode, inserter) {
if (typeof thing.then === 'function') {
// Placeholder element to be replaced once we have the real data
var tempNode = siblingNode.ownerDocument.createElement('span');
siblingNode.parentNode.insertBefore(tempNode, siblingNode);
thing.then(function(delayed) {
inserter(delayed, tempNode);
tempNode.parentNode.removeChild(tempNode);
}.bind(this));
}
else {
inserter(thing, siblingNode);
}
};
/**
* Warn of string does not begin '${' and end '}'
* @param str the string to check.
* @return The string stripped of ${ and }, or untouched if it does not match
*/
Templater.prototype.stripBraces = function(str) {
Templater.prototype._stripBraces = function(str) {
if (!str.match(/\$\{.*\}/g)) {
this.handleError('Expected ' + str + ' to match ${...}');
this._handleError('Expected ' + str + ' to match ${...}');
return str;
}
return str.slice(2, -1);
@ -306,9 +380,9 @@ Templater.prototype.stripBraces = function(str) {
* Combined getter and setter that works with a path through some data set.
* For example:
* <ul>
* <li>property('a.b', { a: { b: 99 }}); // returns 99
* <li>property('a', { a: { b: 99 }}); // returns { b: 99 }
* <li>property('a', { a: { b: 99 }}, 42); // returns 99 and alters the
* <li>_property('a.b', { a: { b: 99 }}); // returns 99
* <li>_property('a', { a: { b: 99 }}); // returns { b: 99 }
* <li>_property('a', { a: { b: 99 }}, 42); // returns 99 and alters the
* input data to be { a: { b: 42 }}
* </ul>
* @param path An array of strings indicating the path through the data, or
@ -319,8 +393,8 @@ Templater.prototype.stripBraces = function(str) {
* @return The value pointed to by <tt>path</tt> before any
* <tt>newValue</tt> is applied.
*/
Templater.prototype.property = function(path, data, newValue) {
this.scope.push(path);
Templater.prototype._property = function(path, data, newValue) {
this.stack.push(path);
try {
if (typeof path === 'string') {
path = path.split('.');
@ -336,12 +410,12 @@ Templater.prototype.property = function(path, data, newValue) {
return value;
}
if (!value) {
this.handleError('Can\'t find path=' + path);
this._handleError('Can\'t find path=' + path);
return null;
}
return this.property(path.slice(1), value, newValue);
return this._property(path.slice(1), value, newValue);
} finally {
this.scope.pop();
this.stack.pop();
}
};
@ -353,21 +427,22 @@ Templater.prototype.property = function(path, data, newValue) {
* according to the X keys in the env object, and then call that function using
* the values in the env object. This is likely to be slow, but workable.
* @param script The string to be evaluated.
* @param env The environment in which to eval the script.
* @param context Optional debugging string in case of failure
* @param data The environment in which to eval the script.
* @param frame Optional debugging string in case of failure.
* @return The return value of the script, or the error message if the script
* execution failed.
*/
Templater.prototype.envEval = function(script, env, context) {
with (env) {
Templater.prototype._envEval = function(script, data, frame) {
with (data) {
try {
this.scope.push(context);
this.stack.push(frame);
return eval(script);
} catch (ex) {
this.handleError('Template error evaluating \'' + script + '\'', ex);
this._handleError('Template error evaluating \'' + script + '\'' +
' environment=' + Object.keys(data).join(', '), ex);
return script;
} finally {
this.scope.pop();
this.stack.pop();
}
}
};
@ -378,11 +453,11 @@ Templater.prototype.envEval = function(script, env, context) {
* @param message the error message to report.
* @param ex optional associated exception.
*/
Templater.prototype.handleError = function(message, ex) {
this.logError(message);
this.logError('In: ' + this.scope.join(' > '));
Templater.prototype._handleError = function(message, ex) {
this._logError(message);
this._logError('In: ' + this.stack.join(' > '));
if (ex) {
this.logError(ex);
this._logError(ex);
}
};
@ -392,7 +467,7 @@ Templater.prototype.handleError = function(message, ex) {
* environments.
* @param message the error message to report.
*/
Templater.prototype.logError = function(message) {
Templater.prototype._logError = function(message) {
Services.console.logStringMessage(message);
};

View File

@ -565,6 +565,10 @@ CssLogic.prototype = {
this._unmatchedSelectors = [];
this.forEachSheet(function (aSheet) {
if (aSheet.systemSheet) {
return;
}
aSheet.forEachRule(function (aRule) {
aRule.selectors.forEach(function (aSelector) {
if (aSelector._matchId != this._matchId) {
@ -1441,10 +1445,6 @@ CssPropertyInfo.prototype = {
_processUnmatchedSelector: function CPI_processUnmatchedSelector(aSelector)
{
let cssRule = aSelector._cssRule;
if (cssRule.systemRule) {
return;
}
let value = cssRule.getPropertyValue(this.property);
if (value) {
let selectorInfo = new CssSelectorInfo(aSelector, this.property, value,
@ -1469,30 +1469,24 @@ CssPropertyInfo.prototype = {
let passId = ++this._cssLogic._passId;
let ruleCount = 0;
if (this._matchedSelectors) {
this._matchedSelectors.forEach(function(aSelectorInfo) {
let cssRule = aSelectorInfo.selector._cssRule;
if (cssRule._passId != passId) {
if (cssRule.sheetAllowed) {
ruleCount++;
}
cssRule._passId = passId;
let iterator = function(aSelectorInfo) {
let cssRule = aSelectorInfo.selector._cssRule;
if (cssRule._passId != passId) {
if (cssRule.sheetAllowed) {
ruleCount++;
}
});
cssRule._passId = passId;
}
};
if (this._matchedSelectors) {
this._matchedSelectors.forEach(iterator);
this._matchedRuleCount = ruleCount;
}
if (this._unmatchedSelectors) {
ruleCount = 0;
this._unmatchedSelectors.forEach(function(aSelectorInfo) {
let cssRule = aSelectorInfo.selector._cssRule;
if (!cssRule.systemRule && cssRule._passId != passId) {
if (cssRule.sheetAllowed) {
ruleCount++;
}
cssRule._passId = passId;
}
});
this._unmatchedSelectors.forEach(iterator);
this._unmatchedRuleCount = ruleCount;
}

View File

@ -432,7 +432,12 @@ can reach it easily. -->
<!ENTITY mediaHideControls.accesskey "C">
<!ENTITY videoFullScreen.label "Full Screen">
<!ENTITY videoFullScreen.accesskey "F">
<!ENTITY videoSaveImage.label "Save Snapshot As…">
<!ENTITY videoSaveImage.accesskey "S">
<!ENTITY videoShowStats.label "Show Statistics">
<!ENTITY videoShowStats.accesskey "t">
<!ENTITY videoHideStats.label "Hide Statistics">
<!ENTITY videoHideStats.accesskey "t">
<!-- LOCALIZATION NOTE :
fullZoomEnlargeCmd.commandkey3, fullZoomReduceCmd.commandkey2 and

View File

@ -17,7 +17,7 @@
<!ENTITY myRecoveryKey.label "My Recovery Key">
<!ENTITY resetSync2.label "Reset Sync…">
<!ENTITY addDevice.label "Add a Device">
<!ENTITY pairDevice.label "Pair a Device">
<!ENTITY syncMy.label "Sync My">
<!ENTITY engine.bookmarks.label "Bookmarks">

View File

@ -4,8 +4,7 @@
<!ENTITY setup.pickSetupType.description "Welcome, if you've never used &syncBrand.fullName.label; before, you will need to create a new account.">
<!ENTITY button.createNewAccount.label "Create a New Account">
<!ENTITY setup.haveAccount.label "I already have a &syncBrand.fullName.label; account.">
<!ENTITY button.connect.label "Connect">
<!ENTITY button.haveAccount.label "I Have an Account">
<!ENTITY setup.choicePage.title.label "Have you used &syncBrand.fullName.label; before?">
<!ENTITY setup.choicePage.new.label "I've never used &syncBrand.shortName.label; before">
@ -40,7 +39,7 @@
<!ENTITY setup.tosAgree3.label "">
<!ENTITY setup.tosAgree2.accesskey "">
<!-- New Account Page 2: Sync Key -->
<!-- My Recovery Key dialog -->
<!ENTITY setup.newRecoveryKeyPage.title.label "&brandShortName; Cares About Your Privacy">
<!ENTITY setup.newRecoveryKeyPage.description.label "To ensure your total privacy, all of your data is encrypted prior to being uploaded. The Recovery Key which is necessary to decrypt your data is not uploaded.">
<!ENTITY recoveryKeyEntry.label "Your Recovery Key">
@ -53,15 +52,13 @@
<!ENTITY button.syncKeyBackup.save.label "Save…">
<!ENTITY button.syncKeyBackup.save.accesskey "S">
<!-- New Account Page 3: Captcha -->
<!ENTITY setup.captchaPage2.title.label "Please Confirm You're Not a Robot">
<!-- Existing Account Page 1: Add Device (incl. Add a Device dialog strings) -->
<!ENTITY addDevice.title.label "Add a Device">
<!-- Existing Account Page 1: Pair a Device (incl. Pair a Device dialog strings) -->
<!ENTITY pairDevice.title.label "Pair a Device">
<!ENTITY addDevice.showMeHow.label "Show me how.">
<!ENTITY addDevice.dontHaveDevice.label "I don't have the device with me">
<!ENTITY addDevice.setup.description.label "To activate, go to &syncBrand.shortName.label; Options on your other device and select &#x0022;Add a Device&#x0022;.">
<!ENTITY pairDevice.setup.description.label "To activate, select &#x0022;Pair a Device&#x0022; on your other device.">
<!ENTITY addDevice.setup.enterCode.label "Then, enter this code:">
<!ENTITY addDevice.dialog.description.label "To activate your new device, go to &syncBrand.shortName.label; Options on the device and select &#x0022;Connect.&#x0022;">
<!ENTITY pairDevice.dialog.description.label "To activate your new device, select &#x0022;Set Up Sync&#x0022; on the device.">
<!ENTITY addDevice.dialog.enterCode.label "Enter the code that the device provides:">
<!ENTITY addDevice.dialog.tryAgain.label "Please try again.">
<!ENTITY addDevice.dialog.successful.label "The device has been successfully added. The initial synchronization can take several minutes and will finish in the background.">

View File

@ -169,7 +169,7 @@ radio[pane=paneSync] {
/* Sync Pane */
#syncDesc {
padding: 0 12em;
padding: 0 8em;
}
#accountCaptionImage {

View File

@ -1,7 +1,7 @@
wizard {
-moz-appearance: none;
width: 55em;
height: 42em;
height: 45em;
padding: 0;
background-color: Window;
}
@ -17,7 +17,7 @@ wizardpage {
-moz-box-pack: center;
-moz-box-align: center;
margin: 0;
padding: 0 8em;
padding: 0 6em;
background-color: Window;
}
@ -109,11 +109,15 @@ description > .text-link:focus {
width: 0.5em;
}
#add-device-throbber > image,
#pairDeviceThrobber > image,
#login-throbber > image {
list-style-image: url("chrome://global/skin/icons/loading_16.png");
}
#captchaFeedback {
visibility: hidden;
}
#successPageIcon {
/* TODO replace this with a 128px version (bug 591122) */
list-style-image: url("chrome://browser/skin/sync-32.png");

View File

@ -227,7 +227,7 @@ caption {
/* ----- SYNC PANE ----- */
#syncDesc {
padding: 0 12em;
padding: 0 8em;
}
#accountCaptionImage {

View File

@ -1,7 +1,7 @@
wizard {
-moz-appearance: none;
width: 55em;
height: 42em;
height: 45em;
padding: 0;
background-color: Window;
}
@ -17,7 +17,7 @@ wizardpage {
-moz-box-pack: center;
-moz-box-align: center;
margin: 0;
padding: 0 8em;
padding: 0 6em;
background-color: Window;
}
@ -108,11 +108,15 @@ description > .text-link:focus {
width: 0.5em;
}
#add-device-throbber > image,
#pairDeviceThrobber > image,
#login-throbber > image {
list-style-image: url("chrome://global/skin/icons/loading_16.png");
}
#captchaFeedback {
visibility: hidden;
}
#successPageIcon {
/* TODO replace this with a 128px version (bug 591122) */
list-style-image: url("chrome://browser/skin/sync-32.png");

View File

@ -41,7 +41,7 @@
@media all and (-moz-windows-default-theme) {
#navigator-toolbox > toolbar:not(:-moz-lwtheme),
#addon-bar:not(:-moz-lwtheme) {
#browser-bottombox:not(:-moz-lwtheme) {
background-color: @customToolbarColor@;
}
@ -168,6 +168,7 @@
#main-window[sizemode=normal] #browser-bottombox {
border: 1px solid @toolbarShadowColor@;
border-top-style: none;
background-clip: padding-box;
}
#main-window[sizemode=normal][tabsontop=false] #PersonalToolbar:not(:-moz-lwtheme) {
@ -235,23 +236,6 @@
background-color: -moz-dialog;
}
#browser-bottombox:not(:-moz-lwtheme) {
background-color: -moz-dialog;
background-clip: padding-box;
}
#main-window[sizemode=normal] #browser-bottombox:not(:-moz-lwtheme),
#main-window[sizemode=normal] #addon-bar:not(:-moz-lwtheme) {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
}
#addon-bar:not(:-moz-lwtheme) {
-moz-appearance: none;
border-bottom-style: none;
background-image: -moz-linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
}
#main-menubar:not(:-moz-lwtheme):not(:-moz-window-inactive) {
background-color: rgba(255,255,255,.5);
border-radius: 4px;

View File

@ -141,6 +141,10 @@
-moz-appearance: toolbox;
}
#browser-bottombox:not(:-moz-lwtheme) {
background-color: -moz-dialog;
}
/* ::::: app menu button ::::: */
#appmenu-button {
@ -2467,12 +2471,14 @@ panel[dimmed="true"] {
/* Add-on bar */
#addon-bar {
-moz-appearance: none;
min-height: 20px;
border-top: 1px solid ThreeDShadow !important;
}
#addon-bar:not(:-moz-lwtheme) {
-moz-appearance: statusbar;
border-top-style: none;
border-bottom-style: none;
padding-top: 1px;
background-image: -moz-linear-gradient(rgba(0,0,0,.15) 1px, rgba(255,255,255,.15) 1px);
background-size: 100% 2px;
background-repeat: no-repeat;
}
#status-bar {

View File

@ -155,7 +155,7 @@ radio[pane=paneSync] {
/* Sync Pane */
#syncDesc {
padding: 0 12em;
padding: 0 8em;
}
.syncGroupBox {

View File

@ -1,7 +1,7 @@
wizard {
-moz-appearance: none;
width: 55em;
height: 42em;
height: 45em;
padding: 0;
background-color: Window;
}
@ -17,7 +17,7 @@ wizardpage {
-moz-box-pack: center;
-moz-box-align: center;
margin: 0;
padding: 0 8em;
padding: 0 6em;
background-color: Window;
}
@ -109,11 +109,15 @@ description > .text-link:focus {
width: 0.5em;
}
#add-device-throbber > image,
#pairDeviceThrobber > image,
#login-throbber > image {
list-style-image: url("chrome://global/skin/icons/loading_16.png");
}
#captchaFeedback {
visibility: hidden;
}
#successPageIcon {
/* TODO replace this with a 128px version (bug 591122) */
list-style-image: url("chrome://browser/skin/sync-32.png");

Some files were not shown because too many files have changed in this diff Show More