Merge with mozilla central 2779c55431a4, no conflicts

This commit is contained in:
Oleg Romashin 2010-09-10 22:32:25 -07:00
commit 497f2db326
1156 changed files with 29287 additions and 8323 deletions

View File

@ -56,7 +56,7 @@ interface nsIDOMWindow;
* nsIAccessNode::GetAccessibleDocument() or
* nsIAccessibleEvent::GetAccessibleDocument()
*/
[scriptable, uuid(03c6ce8a-aa40-4484-9282-e6579c56e054)]
[scriptable, uuid(451242bd-8a0c-4198-ae88-c053609a4e5d)]
interface nsIAccessibleDocument : nsISupports
{
/**
@ -99,4 +99,19 @@ interface nsIAccessibleDocument : nsISupports
* For example, in Windows you can static cast it to an HWND.
*/
[noscript] readonly attribute voidPtr windowHandle;
/**
* Return the parent document accessible.
*/
readonly attribute nsIAccessibleDocument parentDocument;
/**
* Return the count of child document accessibles.
*/
readonly attribute unsigned long childDocumentCount;
/**
* Return the child document accessible at the given index.
*/
nsIAccessibleDocument getChildDocumentAt(in unsigned long index);
};

View File

@ -133,11 +133,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEventQueue");
cb.NoteXPCOMChild(tmp->mEventQueue.get());
PRUint32 i, length = tmp->mChildDocuments.Length();
for (i = 0; i < length; ++i) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mChildDocuments[i]");
cb.NoteXPCOMChild(static_cast<nsIAccessible*>(tmp->mChildDocuments[i].get()));
}
CycleCollectorTraverseCache(tmp->mAccessibleCache, &cb);
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsDocAccessible, nsAccessible)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mEventQueue)
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSTARRAY(mChildDocuments)
ClearCache(tmp->mAccessibleCache);
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
@ -498,6 +505,44 @@ nsDocAccessible::GetDOMDocument(nsIDOMDocument **aDOMDocument)
return NS_OK;
}
NS_IMETHODIMP
nsDocAccessible::GetParentDocument(nsIAccessibleDocument** aDocument)
{
NS_ENSURE_ARG_POINTER(aDocument);
*aDocument = nsnull;
if (!IsDefunct())
NS_IF_ADDREF(*aDocument = ParentDocument());
return NS_OK;
}
NS_IMETHODIMP
nsDocAccessible::GetChildDocumentCount(PRUint32* aCount)
{
NS_ENSURE_ARG_POINTER(aCount);
*aCount = 0;
if (!IsDefunct())
*aCount = ChildDocumentCount();
return NS_OK;
}
NS_IMETHODIMP
nsDocAccessible::GetChildDocumentAt(PRUint32 aIndex,
nsIAccessibleDocument** aDocument)
{
NS_ENSURE_ARG_POINTER(aDocument);
*aDocument = nsnull;
if (IsDefunct())
return NS_OK;
NS_IF_ADDREF(*aDocument = GetChildDocumentAt(aIndex));
return *aDocument ? NS_OK : NS_ERROR_INVALID_ARG;
}
// nsIAccessibleHyperText method
NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor)
{
@ -531,6 +576,7 @@ NS_IMETHODIMP nsDocAccessible::GetAssociatedEditor(nsIEditor **aEditor)
return NS_OK;
}
// nsDocAccessible public method
nsAccessible *
nsDocAccessible::GetCachedAccessible(void *aUniqueID)
{
@ -603,6 +649,10 @@ nsDocAccessible::Init()
AddEventListeners();
nsDocAccessible* parentDocument = mParent->GetDocAccessible();
if (parentDocument)
parentDocument->AppendChildDocument(this);
// Fire reorder event to notify new accessible document has been created and
// attached to the tree.
nsRefPtr<AccEvent> reorderEvent =
@ -629,8 +679,15 @@ nsDocAccessible::Shutdown()
RemoveEventListeners();
if (mParent)
if (mParent) {
nsDocAccessible* parentDocument = mParent->GetDocAccessible();
if (parentDocument)
parentDocument->RemoveChildDocument(this);
mParent->RemoveChild(this);
}
mChildDocuments.Clear();
mWeakShell = nsnull; // Avoid reentrancy
@ -1278,6 +1335,23 @@ nsDocAccessible::HandleAccEvent(AccEvent* aAccEvent)
////////////////////////////////////////////////////////////////////////////////
// Public members
nsAccessible*
nsDocAccessible::GetCachedAccessibleInSubtree(void* aUniqueID)
{
nsAccessible* child = GetCachedAccessible(aUniqueID);
if (child)
return child;
PRUint32 childDocCount = mChildDocuments.Length();
for (PRUint32 childDocIdx= 0; childDocIdx < childDocCount; childDocIdx++) {
nsDocAccessible* childDocument = mChildDocuments.ElementAt(childDocIdx);
child = childDocument->GetCachedAccessibleInSubtree(aUniqueID);
if (child)
return child;
}
return nsnull;
}
////////////////////////////////////////////////////////////////////////////////
// Protected members

View File

@ -141,6 +141,24 @@ public:
*/
void MarkAsLoaded() { mIsLoaded = PR_TRUE; }
/**
* Return the parent document.
*/
nsDocAccessible* ParentDocument() const
{ return mParent ? mParent->GetDocAccessible() : nsnull; }
/**
* Return the child document count.
*/
PRUint32 ChildDocumentCount() const
{ return mChildDocuments.Length(); }
/**
* Return the child document at the given index.
*/
nsDocAccessible* GetChildDocumentAt(PRUint32 aIndex) const
{ return mChildDocuments.SafeElementAt(aIndex, nsnull); }
/**
* Non-virtual method to fire a delayed event after a 0 length timeout.
*
@ -188,6 +206,12 @@ public:
*/
nsAccessible* GetCachedAccessible(void *aUniqueID);
/**
* Return the cached accessible by the given unique ID looking through
* this and nested documents.
*/
nsAccessible* GetCachedAccessibleInSubtree(void* aUniqueID);
/**
* Cache the accessible.
*
@ -217,6 +241,24 @@ protected:
void AddScrollListener();
void RemoveScrollListener();
/**
* Append the given document accessible to this document's child document
* accessibles.
*/
bool AppendChildDocument(nsDocAccessible* aChildDocument)
{
return mChildDocuments.AppendElement(aChildDocument);
}
/**
* Remove the given document accessible from this document's child document
* accessibles.
*/
void RemoveChildDocument(nsDocAccessible* aChildDocument)
{
mChildDocuments.RemoveElement(aChildDocument);
}
/**
* Invalidate parent-child relations for any cached accessible in the DOM
* subtree. Accessible objects aren't destroyed.
@ -337,6 +379,8 @@ protected:
static PRUint32 gLastFocusedAccessiblesState;
static nsIAtom *gLastFocusedFrameType;
nsTArray<nsRefPtr<nsDocAccessible> > mChildDocuments;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsDocAccessible,

View File

@ -282,24 +282,21 @@ STDMETHODIMP nsAccessibleWrap::get_accChild(
{
__try {
*ppdispChild = NULL;
if (!mWeakShell || varChild.vt != VT_I4)
if (IsDefunct())
return E_FAIL;
if (varChild.lVal == CHILDID_SELF) {
*ppdispChild = static_cast<IDispatch*>(this);
AddRef();
return S_OK;
}
// IAccessible::accChild is used to return this accessible or child accessible
// at the given index or to get an accessible by child ID in the case of
// document accessible (it's handled by overriden GetXPAccessibleFor method
// on the document accessible). The getting an accessible by child ID is used
// by AccessibleObjectFromEvent() called by AT when AT handles our MSAA event.
nsAccessible* child = GetXPAccessibleFor(varChild);
if (child)
*ppdispChild = NativeAccessible(child);
if (!nsAccUtils::MustPrune(this)) {
nsAccessible* child = GetChildAt(varChild.lVal - 1);
if (child) {
*ppdispChild = NativeAccessible(child);
}
}
} __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
return (*ppdispChild)? S_OK: E_FAIL;
return (*ppdispChild)? S_OK: E_INVALIDARG;
}
STDMETHODIMP nsAccessibleWrap::get_accName(
@ -1804,7 +1801,7 @@ nsAccessibleWrap::UnattachIEnumVariant()
nsAccessible*
nsAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
{
if (IsDefunct())
if (aVarChild.vt != VT_I4)
return nsnull;
// if its us real easy - this seems to always be the case

View File

@ -103,39 +103,13 @@ nsDocAccessibleWrap::GetXPAccessibleFor(const VARIANT& aVarChild)
// accessible through whole accessible subtree including subdocuments.
// Otherwise we treat lVal as index in parent.
if (aVarChild.lVal < 0)
return IsDefunct() ? nsnull : GetXPAccessibleForChildID(aVarChild);
return nsAccessibleWrap::GetXPAccessibleFor(aVarChild);
}
STDMETHODIMP
nsDocAccessibleWrap::get_accChild(VARIANT varChild,
IDispatch __RPC_FAR *__RPC_FAR *ppdispChild)
{
__try {
*ppdispChild = NULL;
if (varChild.vt == VT_I4 && varChild.lVal < 0) {
// IAccessible::accChild can be used to get an accessible by child ID.
// It is used by AccessibleObjectFromEvent() called by AT when AT handles
// our MSAA event.
nsAccessible *xpAccessible = GetXPAccessibleForChildID(varChild);
if (!xpAccessible)
return E_FAIL;
IAccessible *msaaAccessible = NULL;
xpAccessible->GetNativeInterface((void**)&msaaAccessible);
*ppdispChild = static_cast<IDispatch*>(msaaAccessible);
return S_OK;
if (aVarChild.vt == VT_I4 && aVarChild.lVal < 0) {
// Convert child ID to unique ID.
void* uniqueID = reinterpret_cast<void*>(-aVarChild.lVal);
return GetCachedAccessibleInSubtree(uniqueID);
}
// Otherwise, the normal get_accChild() will do
return nsAccessibleWrap::get_accChild(varChild, ppdispChild);
} __except(FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }
return E_FAIL;
return nsAccessibleWrap::GetXPAccessibleFor(aVarChild);
}
STDMETHODIMP nsDocAccessibleWrap::get_URL(/* [out] */ BSTR __RPC_FAR *aURL)
@ -271,14 +245,3 @@ STDMETHODIMP nsDocAccessibleWrap::get_accValue(
return get_URL(pszValue);
}
nsAccessible*
nsDocAccessibleWrap::GetXPAccessibleForChildID(const VARIANT& aVarChild)
{
NS_PRECONDITION(aVarChild.vt == VT_I4 && aVarChild.lVal < 0,
"Variant doesn't point to child ID!");
// Convert child ID to unique ID.
void *uniqueID = reinterpret_cast<void*>(-aVarChild.lVal);
return GetAccService()->FindAccessibleInCache(uniqueID);
}

View File

@ -83,10 +83,6 @@ public:
/* [in] */ BSTR __RPC_FAR *commaSeparatedMediaTypes);
// IAccessible
// Override get_accChild so that it can get any child via the unique ID
virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accChild(
/* [in] */ VARIANT varChild,
/* [retval][out] */ IDispatch __RPC_FAR *__RPC_FAR *ppdispChild);
// Override get_accValue to provide URL when no other value is available
virtual /* [id][propget] */ HRESULT STDMETHODCALLTYPE get_accValue(
@ -95,15 +91,6 @@ public:
// nsAccessibleWrap
virtual nsAccessible *GetXPAccessibleFor(const VARIANT& varChild);
// nsDocAccessibleWrap
/**
* Find an accessible by the given child ID in cached documents.
*
* @param aVarChild [in] variant pointing to the child ID
*/
static nsAccessible *GetXPAccessibleForChildID(const VARIANT& aVarChild);
};
#endif

View File

@ -4,7 +4,7 @@
<!-- Firefox tabbrowser -->
<?xml-stylesheet href="chrome://browser/content/browser.css"
type="text/css"?>
<!-- Seamonkey tabbrowser -->
<!-- SeaMonkey tabbrowser -->
<?xml-stylesheet href="chrome://navigator/content/navigator.css"
type="text/css"?>

View File

@ -4,7 +4,7 @@
<!-- Firefox tabbrowser -->
<?xml-stylesheet href="chrome://browser/content/browser.css"
type="text/css"?>
<!-- Seamonkey tabbrowser -->
<!-- SeaMonkey tabbrowser -->
<?xml-stylesheet href="chrome://navigator/content/navigator.css"
type="text/css"?>

View File

@ -13,11 +13,11 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=418368
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/common.js"></script>
src="../common.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/role.js"></script>
src="../role.js"></script>
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/states.js"></script>
src="../states.js"></script>
<script type="application/javascript">
function testThis(aID, aAcc, aRole, aAnchors, aName, aValid, aStartIndex,

View File

@ -12,11 +12,11 @@
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/common.js" />
src="../common.js" />
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/role.js" />
src="../role.js" />
<script type="application/javascript"
src="chrome://mochikit/content/a11y/accessible/states.js" />
src="../states.js" />
<script type="application/javascript">
<![CDATA[

View File

@ -4,7 +4,7 @@
<!-- Firefox tabbrowser -->
<?xml-stylesheet href="chrome://browser/content/browser.css"
type="text/css"?>
<!-- Seamonkey tabbrowser -->
<!-- SeaMonkey tabbrowser -->
<?xml-stylesheet href="chrome://navigator/content/navigator.css"
type="text/css"?>

View File

@ -2,8 +2,13 @@
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<!-- Firefox tabbrowser -->
<?xml-stylesheet href="chrome://browser/content/browser.css"
type="text/css"?>
<!-- SeaMonkey tabbrowser -->
<?xml-stylesheet href="chrome://navigator/content/navigator.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Accessible XUL tabbrowser relation tests">

View File

@ -2,8 +2,7 @@
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/a11y/accessible/treeview.css"
type="text/css"?>
<?xml-stylesheet href="../treeview.css" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="XUL tree selectable tests">

View File

@ -2,8 +2,7 @@
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/a11y/accessible/treeview.css"
type="text/css"?>
<?xml-stylesheet href="../treeview.css" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="XUL tree selectable tests">

View File

@ -2,8 +2,7 @@
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/a11y/accessible/treeview.css"
type="text/css"?>
<?xml-stylesheet href="../treeview.css" type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="XUL tree selectable tests">

View File

@ -53,6 +53,7 @@ _TEST_FILES =\
test_colorpicker.xul \
test_combobox.xul \
test_cssoverflow.html \
test_dochierarchy.html \
test_filectrl.html \
test_formctrl.html \
test_formctrl.xul \

View File

@ -0,0 +1,83 @@
<!DOCTYPE html>
<html>
<head>
<title>Test document hierarchy</title>
<link rel="stylesheet" type="text/css"
href="chrome://mochikit/content/tests/SimpleTest/test.css" />
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js"></script>
<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="../role.js"></script>
<script type="application/javascript"
src="../states.js"></script>
<script type="application/javascript">
function doTest()
{
// tabDoc and testDoc are different documents depending on whether test
// is running in standalone mode or not.
var root = getRootAccessible();
var tabDoc = window.parent ?
getAccessible(window.parent.document, [nsIAccessibleDocument]) :
getAccessible(document, [nsIAccessibleDocument]);
var testDoc = getAccessible(document, [nsIAccessibleDocument]);
var iframeDoc = getAccessible("iframe").firstChild.
QueryInterface(nsIAccessibleDocument);
is(root.parentDocument, null,
"Wrong parent document of root accessible");
is(root.childDocumentCount, 1,
"Wrong child document count of root accessible");
is(root.getChildDocumentAt(0), tabDoc,
"Wrong child document at index 0 of root accessible");
is(tabDoc.parentDocument, root,
"Wrong parent document of tab document");
is(tabDoc.childDocumentCount, 1,
"Wrong child document count of tab document");
is(tabDoc.getChildDocumentAt(0), (tabDoc == testDoc ? iframeDoc : testDoc),
"Wrong child document at index 0 of tab document");
if (tabDoc != testDoc) {
is(testDoc.parentDocument, tabDoc,
"Wrong parent document of test document");
is(testDoc.childDocumentCount, 1,
"Wrong child document count of test document");
is(testDoc.getChildDocumentAt(0), iframeDoc,
"Wrong child document at index 0 of test document");
}
is(iframeDoc.parentDocument, (tabDoc == testDoc ? tabDoc : testDoc),
"Wrong parent document of iframe document");
is(iframeDoc.childDocumentCount, 0,
"Wrong child document count of iframe document");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=592913"
title="Provide a way to quickly determine whether an accessible object is a descendant of a tab document">
Mozilla Bug 592913
</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<iframe src="about:mozilla" id="iframe"></iframe>
</body>
</html>

View File

@ -4,7 +4,7 @@
<!-- Firefox tabbrowser -->
<?xml-stylesheet href="chrome://browser/content/browser.css"
type="text/css"?>
<!-- Seamonkey tabbrowser -->
<!-- SeaMonkey tabbrowser -->
<?xml-stylesheet href="chrome://navigator/content/navigator.css"
type="text/css"?>

View File

@ -172,7 +172,7 @@ image.study-result {
color: white;
font-weight: bold;
padding: 2px;
-moz-border-radius: 10000px;
border-radius: 10000px;
margin-right: 25px;
margin-bottom: 13px;
}

View File

@ -102,7 +102,7 @@ src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
font-size: 16px;
padding: 8px 12px;
color: rgba(0, 0, 0, 0.8);
-moz-border-radius: 0.5em;
border-radius: 0.5em;
-webkit-border-radius: 0.5em;
-moz-box-shadow:
inset rgba(0, 0, 0, 0.2) 0 1px 1px,
@ -122,7 +122,7 @@ src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
padding: 8px 12px;
width: 240px;
color: rgba(0, 0, 0, 0.8);
-moz-border-radius: 0.5em;
border-radius: 0.5em;
-webkit-border-radius: 0.5em;
-moz-box-shadow:
inset rgba(0, 0, 0, 0.2) 0 1px 1px,
@ -143,7 +143,7 @@ src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
padding: 8px 24px;
margin: 24px auto;
color: rgba(0, 0, 0, 0.8);
-moz-border-radius: 0.5em;
border-radius: 0.5em;
-webkit-border-radius: 0.5em;
background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/callout.png') no-repeat top center;
-moz-box-shadow:
@ -161,7 +161,7 @@ src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
padding: 8px 24px;
margin: 8px auto;
color: rgba(0, 0, 0, 0.8);
-moz-border-radius: 0.5em;
border-radius: 0.5em;
-webkit-border-radius: 0.5em;
background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/callout.png') no-repeat top center;
-moz-box-shadow:
@ -197,7 +197,7 @@ src: url('chrome://testpilot/skin/fonts/DroidSans-Bold.ttf') format('truetype');
padding: 4px 40px;
width: 800px;
text-align: left;
-moz-border-radius: 0.25em;
border-radius: 0.25em;
-webkit-border-radius: 0.25em;
border-top: 1px solid #adb6ba;
border-left: 1px solid #adb6ba;

View File

@ -13,7 +13,7 @@
<Description>
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
<em:minVersion>3.5</em:minVersion>
<em:maxVersion>4.0b5</em:maxVersion>
<em:maxVersion>4.0.*</em:maxVersion>
</Description>
</em:targetApplication>

View File

@ -52,7 +52,7 @@ body {
.dataBox {
font-size: 16px;
padding: 6px 20px 20px 20px;
-moz-border-radius: 0.5em;
border-radius: 0.5em;
-webkit-border-radius: 0.5em;
background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout.png') no-repeat top center;
//display: inline;
@ -113,7 +113,7 @@ body {
font-size: 16px;
padding: 8px 12px;
color: rgba(0, 0, 0, 0.8);
-moz-border-radius: 0.5em;
border-radius: 0.5em;
-webkit-border-radius: 0.5em;
-moz-box-shadow:
inset rgba(0, 0, 0, 0.2) 0 1px 1px,
@ -132,7 +132,7 @@ body {
padding: 8px 12px;
width: 240px;
color: rgba(0, 0, 0, 0.8);
-moz-border-radius: 0.5em;
border-radius: 0.5em;
-webkit-border-radius: 0.5em;
-moz-box-shadow:
inset rgba(0, 0, 0, 0.2) 0 1px 1px,
@ -151,7 +151,7 @@ body {
padding: 8px 24px;
margin: 24px auto;
color: rgba(0, 0, 0, 0.8);
-moz-border-radius: 0.5em;
border-radius: 0.5em;
-webkit-border-radius: 0.5em;
background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout.png') no-repeat top center;
-moz-box-shadow:
@ -173,7 +173,7 @@ body {
padding: 8px 24px;
margin: 8px auto;
color: rgba(0, 0, 0, 0.8);
-moz-border-radius: 0.5em;
border-radius: 0.5em;
-webkit-border-radius: 0.5em;
background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout.png') no-repeat top center;
-moz-box-shadow:
@ -190,7 +190,7 @@ body {
padding: 8px 24px;
margin: 8px auto;
color: rgba(0, 0, 0, 0.8);
-moz-border-radius: 0.5em;
border-radius: 0.5em;
-webkit-border-radius: 0.5em;
background: rgba(220, 240, 247, 0.8) url('chrome://testpilot/skin/images/callout_continue.png') no-repeat top center;
-moz-box-shadow:
@ -237,7 +237,7 @@ body {
padding: 4px 40px;
width: 800px;
text-align: left;
-moz-border-radius: 0.25em;
border-radius: 0.25em;
-webkit-border-radius: 0.25em;
border-top: 1px solid #adb6ba;
border-left: 1px solid #adb6ba;

View File

@ -8,7 +8,7 @@
background-image: -moz-linear-gradient(hsla(0,0%,100%,.2), transparent);
-moz-box-shadow: inset 0 0 10px hsla(0,0%,100%,.2),
inset 0 1px 0 hsla(0,0%,100%,.3);
-moz-border-radius: 4px;
border-radius: 4px;
border: 1px solid Menu;
margin: -6px 0 0 0;
width: 480px;

View File

@ -7,7 +7,7 @@
rgba(60,60,60,.9) 51%, rgba(50,50,50,.9));
background-clip: padding-box;
background-origin: padding-box;
-moz-border-radius: 12px;
border-radius: 12px;
border: 1px solid rgba(0,0,0,.65);
-moz-box-shadow: inset 0 1px 0 rgba(255,255,255,.2),
inset 0 0 1px rgba(255,255,255,.1),

View File

@ -119,7 +119,10 @@ pref("app.update.cert.maxErrors", 5);
// no update available. This validation will not be performed when using the
// |app.update.url.override| preference for update checking.
pref("app.update.certs.1.issuerName", "OU=Equifax Secure Certificate Authority,O=Equifax,C=US");
pref("app.update.certs.1.commonName", "*.mozilla.org");
pref("app.update.certs.1.commonName", "aus3.mozilla.org");
pref("app.update.certs.2.issuerName", "CN=Thawte SSL CA,O=\"Thawte, Inc.\",C=US");
pref("app.update.certs.2.commonName", "aus3.mozilla.org");
// Whether or not app updates are enabled
pref("app.update.enabled", true);
@ -144,7 +147,7 @@ pref("app.update.mode", 1);
pref("app.update.silent", false);
// Update service URL:
pref("app.update.url", "https://aus2.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
pref("app.update.url", "https://aus3.mozilla.org/update/3/%PRODUCT%/%VERSION%/%BUILD_ID%/%BUILD_TARGET%/%LOCALE%/%CHANNEL%/%OS_VERSION%/%DISTRIBUTION%/%DISTRIBUTION_VERSION%/update.xml");
// app.update.url.manual is in branding section
// app.update.url.details is in branding section
@ -395,11 +398,9 @@ pref("browser.bookmarks.max_backups", 10);
// Scripts & Windows prefs
pref("dom.disable_open_during_load", true);
#ifdef DEBUG
pref("javascript.options.showInConsole", true);
#ifdef DEBUG
pref("general.warnOnAboutConfig", false);
#else
pref("javascript.options.showInConsole", false);
#endif
#ifdef WINCE
@ -782,6 +783,8 @@ pref("browser.sessionstore.postdata", 0);
// on which sites to save text data, POSTDATA and cookies
// 0 = everywhere, 1 = unencrypted sites, 2 = nowhere
pref("browser.sessionstore.privacy_level", 1);
// the same as browser.sessionstore.privacy_level, but for saving deferred session data
pref("browser.sessionstore.privacy_level_deferred", 2);
// how many tabs can be reopened (per window)
pref("browser.sessionstore.max_tabs_undo", 10);
// how many windows can be reopened (per session) - on non-OS X platforms this
@ -1030,3 +1033,9 @@ pref("services.sync.prefs.sync.signon.rememberSignons", true);
pref("services.sync.prefs.sync.spellchecker.dictionary", true);
pref("services.sync.prefs.sync.xpinstall.whitelist.required", true);
#endif
// Disable the Error Console
pref("devtools.errorconsole.enabled", false);
// disable the Inspector
pref("devtools.inspector.enabled", false);

View File

@ -54,7 +54,7 @@ html {
#brandStart {
background: -moz-linear-gradient(top, #42607C, #1E4262 30%, #1E4262 80%, #143552 98%, #244665);
-moz-border-radius: 5.6px;
border-radius: 5.6px;
padding-bottom: 0.2em;
-moz-padding-start: 0.5em;
font-size: 250%;
@ -82,7 +82,7 @@ body[dir="rtl"] #brandStart:before {
#searchContainer {
border: 1px solid ThreeDShadow;
-moz-border-radius: 5.6px;
border-radius: 5.6px;
padding: 3em;
}
#searchEngineLinks {

View File

@ -41,7 +41,7 @@
const SEARCH_ENGINES = {
"Google": {
image: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEYAAAAYCAMAAABwdHsxAAADAFBMVEVogZYwjM9NtOspS2u0KAfYZBb3rRTiurCMjYrUWhIaPlzDNgfVRgjaVgvoizTI9f2ta17/9Wzrs0v46M/+7VOW6vrJQAsBLochdr+nmF/++o+UDQK88vzaYQarq6scarXT+f68vLzjdRrjewp5d3WqGATkUwaTq9uk7PnUaydkICTJSBD4uCDgZAz9+LiK6PjRZFH33Xv01qbrlxznhgvwx5gCBCbOUhLjfCzhaxHztXG8RhX93UP5yTRGl9QhRGT5wigkRmXniRj70zoKWrT2yUdqjs3UhzXvl0JwU0teu+EBElGD2fH83FZo1vTPilT0vDmd0Z3dbhrWrHLhcQir8PzTTDfPrVGrajv03cf5+vkwUG6z8PrSUw571uzvpCGPTzBsPDMxMEcHagXrkg7voBHf/P8FGHHhzrbyryOQZAeDNCudnZyTJBL2zlT+xByoUCG1wspyY1nRiH7STA3yiB7Jjx/pqT/Pz8/7lhDvfBxTUkl95PY/RE0DI2SQbT7LzJjxdg1OMTzutynnm2v+5DWsNBX+/tVDqOa7tnX57OPGYSHysziEo5a3XBe3l3j+0SXCoaCsjTL++fMgQmLmxXdv0ezsv0ycQxf8p0X1hwrenDU/Tnj9//tkyNj3+fX89fDv22QXHEWQydee7/xEdKG84uRWi5bwaxyK3/L++/iU5PMWM2Q6GS7UXg81l9gmSGgXOVewsKO3x+ECK8h8x3j7/fs5bMBke6kuTmwoRV/SbArKeSrB1snY373x5aSIRz85YIvragDD5+lnorUvSWF80PCQr6o4KGKysrIeQmIgXJ1Vb4j///8+XXjs7ewaUZSutsKFkqY4T3IhXqgYSHtwiJwUNVLExcbk5eU4SGcrcLEsP1wPKV77+/vy8/NBkcBmrN+drLklNGIONHQ5WXVxyuk0VXHHz9dmxuonZahTrNqPobEcP19CVXMURIljzvE/odz29vYcP3VieZHo6OhCYHxOW29y2vNYwe79/f33+PnU2eExgcLX2Nje397NKLzjAAACjElEQVR42q2UMWvbQBTHPRgro6CDKIJOPQSeVH+CDuZ2E3BIVmNkBB5agTF0NYV00y7jdMji0XqxvNljAzmEPASrQ0Vwhk41tPZiC6l3OjmNXYeYujfo3nF3v/fe/+le5hX5HyOTzv1Of88bnXelq0q8C9NRHMcpFrHd3gPTD0qV17uisWeDqE269gyguU88pcrRDowNXrqYTt1/xkQwXi8mZ2Q/TO9vDNa2FVkJLUPocDsYDoe1lB6EURzZ4RrDThHi0RF7mRh+bFGirCEYCJnMLpzUase5XI2aZxhjKM6wS64YJlDZqWxAPEq6ztjFm01KEzGdTxGirgoNFpRfrU7IEg8IiQFoqRlmqYp0p4VGDEO8zB1sYoLyKJlFVCdxjotwntNpISJqYfjKMQKi1fDVvJlqg2G6gWmh98l8iuRoWL1O7Bc53b2TJoSFc88xquwKqig8SGyDFvxRMGyriEdDUF140+DR9HN6hGHOBJIsjinLpvHoV84EIPGCL/ClAzOiojzfKeejz9WX3H6ru98Td6GWaqOi3lbBQQsTlyzzOU1ajjimTgpUkxRDN7Uvtl1kmTGMgT5+28BcAAC/SBSJBl7mWS1kkwRVnpWvs8n1HP6nMswFQmJyp71+DK4mgZ0ssEarFsiyQe0sg9V03acnG2luClgcQx+DKaOyTwTjAUNiS5OKWFGwZbGYw5GsiqKYNI6erp8XTo7TGigAToecVSofjvrEzMuyPBIe9xvXux2MvXCZZroQhNU6a7/mJ5VsK87l+FaD2TLpTvTTbTbj7bb13PiJZ0zAT+u6PtH9nhuKxD270s0hGAfSBOCgaJLHxDrlffcQzOoXSKyYjksOwdDWOLcG2H5i8zf27La6kVLrqwAAAABJRU5ErkJggg=="
, params: "source=hp"
, params: "source=hp&channel=np"
, links: {
advanced: "http://www.google.com/advanced_search"
, preferences: "http://www.google.com/preferences"

View File

@ -397,6 +397,11 @@
oncommand="BrowserOpenSyncTabs();"
disabled="true"/>
#endif
<menuitem id="historyRestoreLastSession"
class="restoreLastSession"
label="&historyRestoreLastSession.label;"
oncommand="restoreLastSession();"
disabled="true"/>
<menu id="historyUndoMenu"
class="recentlyClosedTabsMenu"
label="&historyUndoMenu.label;"
@ -535,11 +540,13 @@
<menuseparator id="devToolsSeparator"/>
<menuitem id="menu_pageinspect"
type="checkbox"
hidden="true"
label="&inspectMenu.label;"
accesskey="&inspectMenu.accesskey;"
key="key_inspect"
command="Tools:Inspect"/>
<menuitem id="javascriptConsole"
hidden="true"
label="&errorConsoleCmd.label;"
accesskey="&errorConsoleCmd.accesskey;"
key="key_errorConsole"

View File

@ -758,6 +758,15 @@ HistoryMenu.prototype = {
#endif
},
toggleRestoreLastSession: function PHM_toggleRestoreLastSession() {
let restoreItem = this._rootElt.getElementsByClassName("restoreLastSession")[0];
if (this._ss.canRestoreLastSession)
restoreItem.removeAttribute("disabled");
else
restoreItem.setAttribute("disabled", true);
},
_onPopupShowing: function HM__onPopupShowing(aEvent) {
PlacesMenu.prototype._onPopupShowing.apply(this, arguments);
@ -768,6 +777,7 @@ HistoryMenu.prototype = {
this.toggleRecentlyClosedTabs();
this.toggleRecentlyClosedWindows();
this.toggleTabsFromOtherComputers();
this.toggleRestoreLastSession();
},
_onCommand: function HM__onCommand(aEvent) {

View File

@ -126,7 +126,7 @@
<command id="Tools:Search" oncommand="BrowserSearch.webSearch();"/>
<command id="Tools:Downloads" oncommand="BrowserDownloadsUI();"/>
<command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();"/>
<command id="Tools:Inspect" oncommand="InspectorUI.toggleInspectorUI();" disabled="true"/>
<command id="Tools:Addons" oncommand="BrowserOpenAddonsMgr();"/>
<command id="Tools:Sanitize"
oncommand="Cc['@mozilla.org/browser/browserglue;1'].getService(Ci.nsIBrowserGlue).sanitize(window);"/>
@ -236,7 +236,7 @@
<key id="key_openDownloads" key="&downloads.commandkey;" command="Tools:Downloads" modifiers="accel"/>
#endif
<key id="key_openAddons" key="&addons.commandkey;" command="Tools:Addons" modifiers="accel,shift"/>
<key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" oncommand="toJavaScriptConsole();" modifiers="accel,shift"/>
<key id="key_errorConsole" key="&errorConsoleCmd.commandkey;" oncommand="toJavaScriptConsole();" modifiers="accel,shift" disabled="true"/>
<key id="key_webConsole" key="&webConsoleCmd.commandkey;" oncommand="HUDConsoleUI.toggleHUD();" modifiers="accel,shift"/>
<key id="key_inspect" key="&inspectMenu.commandkey;" command="Tools:Inspect" modifiers="accel,shift"/>
<key id="openFileKb" key="&openFileCmd.commandkey;" command="Browser:OpenFile" modifiers="accel"/>

View File

@ -150,8 +150,11 @@ let TabView = {
let activeGroup = tab.tabItem.parent;
let groupItems = self._window.GroupItems.groupItems;
groupItems.forEach(function(groupItem) {
if (groupItem.getTitle().length > 0 &&
groupItems.forEach(function(groupItem) {
// if group has title, it's not hidden and there is no active group or
// the active group id doesn't match the group id, a group menu item
// would be added.
if (groupItem.getTitle().length > 0 && !groupItem.hidden &&
(!activeGroup || activeGroup.id != groupItem.id)) {
let menuItem = self._createGroupMenuItem(groupItem);
popup.insertBefore(menuItem, separator);

View File

@ -366,10 +366,16 @@ window[chromehidden~="toolbar"] toolbar:not(.toolbar-primary):not(.chromeclass-m
}
#notification-popup-box[anchorid="geo-notification-icon"] > #geo-notification-icon,
#notification-popup-box[anchorid="addons-notification-icon"] > #addons-notification-icon {
#notification-popup-box[anchorid="indexedDB-notification-icon"] > #indexedDB-notification-icon,
#notification-popup-box[anchorid="addons-notification-icon"] > #addons-notification-icon,
#notification-popup-box[anchorid="password-notification-icon"] > #password-notification-icon {
display: -moz-box;
}
#invalid-form-popup {
max-width: 280px;
}
#geolocation-notification {
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#geolocation-notification");
}

View File

@ -789,6 +789,72 @@ const gXPInstallObserver = {
}
};
const gFormSubmitObserver = {
QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
panel: null,
init: function()
{
this.panel = document.getElementById('invalid-form-popup');
this.panel.appendChild(document.createTextNode(""));
},
panelIsOpen: function()
{
return this.panel && this.panel.state != "hiding" &&
this.panel.state != "closed";
},
notifyInvalidSubmit : function (aFormElement, aInvalidElements)
{
// We are going to handle invalid form submission attempt by focusing the
// first invalid element and show the corresponding validation message in a
// panel attached to the element.
if (!aInvalidElements.length) {
return;
}
// Don't show the popup if the current tab doesn't contain the invalid form.
if (gBrowser.selectedTab.linkedBrowser.contentDocument !=
aFormElement.ownerDocument) {
return;
}
let element = aInvalidElements.queryElementAt(0, Ci.nsISupports);
if (!(element instanceof HTMLInputElement ||
element instanceof HTMLTextAreaElement ||
element instanceof HTMLSelectElement ||
element instanceof HTMLButtonElement)) {
return;
}
// Limit the message to 256 characters.
this.panel.firstChild.nodeValue = element.validationMessage.substring(0, 256);
element.focus();
// If the user type something or blur the element, we want to remove the popup.
// We could check for clicks but a click is already removing the popup.
let eventHandler = function(e) {
gFormSubmitObserver.panel.hidePopup();
};
element.addEventListener("input", eventHandler, false);
element.addEventListener("blur", eventHandler, false);
// One event to bring them all and in the darkness bind them all.
this.panel.addEventListener("popuphiding", function(aEvent) {
aEvent.target.removeEventListener("popuphiding", arguments.callee, false);
element.removeEventListener("input", eventHandler, false);
element.removeEventListener("blur", eventHandler, false);
}, false);
this.panel.hidden = false;
this.panel.openPopup(element, "after_start", 0, 0);
}
};
// Simple gestures support
//
// As per bug #412486, web content must not be allowed to receive any
@ -1318,9 +1384,12 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked", false);
Services.obs.addObserver(gXPInstallObserver, "addon-install-failed", false);
Services.obs.addObserver(gXPInstallObserver, "addon-install-complete", false);
Services.obs.addObserver(gFormSubmitObserver, "invalidformsubmit", false);
BrowserOffline.init();
OfflineApps.init();
IndexedDBPromptHelper.init();
gFormSubmitObserver.init();
gBrowser.addEventListener("pageshow", function(evt) { setTimeout(pageShowEventHandlers, 0, evt); }, true);
@ -1513,6 +1582,23 @@ function delayedStartup(isLoadingBlank, mustLoadSidebar) {
TabView.init();
// Enable Inspector?
let enabled = gPrefService.getBoolPref(InspectorUI.prefEnabledName);
if (enabled) {
document.getElementById("menu_pageinspect").setAttribute("hidden", false);
document.getElementById("Tools:Inspect").removeAttribute("disabled");
let appMenuInspect = document.getElementById("appmenu_pageInspect");
if (appMenuInspect)
appMenuInspect.setAttribute("hidden", false);
}
// Enable Error Console?
let consoleEnabled = gPrefService.getBoolPref("devtools.errorconsole.enabled");
if (consoleEnabled) {
document.getElementById("javascriptConsole").hidden = false;
document.getElementById("key_errorConsole").removeAttribute("disabled");
}
Services.obs.notifyObservers(window, "browser-delayed-startup-finished", "");
}
@ -1544,6 +1630,7 @@ function BrowserShutdown()
Services.obs.removeObserver(gXPInstallObserver, "addon-install-failed");
Services.obs.removeObserver(gXPInstallObserver, "addon-install-complete");
Services.obs.removeObserver(gPluginHandler.pluginCrashed, "plugin-crashed");
Services.obs.removeObserver(gFormSubmitObserver, "invalidformsubmit");
try {
gBrowser.removeProgressListener(window.XULBrowserWindow);
@ -1563,6 +1650,7 @@ function BrowserShutdown()
OfflineApps.uninit();
DownloadMonitorPanel.uninit();
gPrivateBrowsingUI.uninit();
IndexedDBPromptHelper.uninit();
var enumerator = Services.wm.getEnumerator(null);
enumerator.getNext();
@ -2870,25 +2958,7 @@ var browserDragAndDrop = {
},
drop: function (aEvent, aName) Services.droppedLinkHandler.dropLink(aEvent, aName)
}
var proxyIconDNDObserver = {
onDragStart: function (aEvent, aXferData, aDragAction)
{
if (gProxyFavIcon.getAttribute("pageproxystate") != "valid")
return;
var value = content.location.href;
var urlString = value + "\n" + content.document.title;
var htmlString = "<a href=\"" + value + "\">" + value + "</a>";
var dt = aEvent.dataTransfer;
dt.setData("text/x-moz-url", urlString);
dt.setData("text/uri-list", value);
dt.setData("text/plain", value);
dt.setData("text/html", htmlString);
}
}
};
var homeButtonObserver = {
onDrop: function (aEvent)
@ -4190,6 +4260,11 @@ var XULBrowserWindow = {
var location = aLocationURI ? aLocationURI.spec : "";
this._hostChanged = true;
// Hide the form invalid popup.
if (gFormSubmitObserver.panelIsOpen()) {
gFormSubmitObserver.panel.hidePopup();
}
if (document.tooltipNode) {
// Optimise for the common case
if (aWebProgress.DOMWindow == content) {
@ -5853,6 +5928,92 @@ var OfflineApps = {
}
};
var IndexedDBPromptHelper = {
_permissionsPrompt: "indexedDB-permissions-prompt",
_permissionsResponse: "indexedDB-permissions-response",
_quotaPrompt: "indexedDB-quota-prompt",
_quotaResponse: "indexedDB-quota-response",
_notificationIcon: "indexedDB-notification-icon",
init:
function IndexedDBPromptHelper_init() {
Services.obs.addObserver(this, this._permissionsPrompt, false);
Services.obs.addObserver(this, this._quotaPrompt, false);
},
uninit:
function IndexedDBPromptHelper_uninit() {
Services.obs.removeObserver(this, this._permissionsPrompt, false);
Services.obs.removeObserver(this, this._quotaPrompt, false);
},
observe:
function IndexedDBPromptHelper_observe(subject, topic, data) {
if (topic != this._permissionsPrompt &&
topic != this._quotaPrompt) {
throw new Error("Unexpected topic!");
}
var requestor = subject.QueryInterface(Ci.nsIInterfaceRequestor);
var contentWindow = requestor.getInterface(Ci.nsIDOMWindow);
var contentDocument = contentWindow.document;
var browserWindow =
OfflineApps._getBrowserWindowForContentWindow(contentWindow);
var browser =
OfflineApps._getBrowserForContentWindow(browserWindow, contentWindow);
if (!browser) {
// Must belong to some other window.
return;
}
var host = contentDocument.documentURIObject.asciiHost;
var message;
var responseTopic;
if (topic == this._permissionsPrompt) {
message = gNavigatorBundle.getFormattedString("offlineApps.available",
[ host ]);
responseTopic = this._permissionsResponse;
}
else if (topic == this._quotaPrompt) {
message = gNavigatorBundle.getFormattedString("indexedDB.usage",
[ host, data ]);
responseTopic = this._quotaResponse;
}
var self = this;
var observer = requestor.getInterface(Ci.nsIObserver);
var mainAction = {
label: gNavigatorBundle.getString("offlineApps.allow"),
accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
callback: function() {
observer.observe(null, responseTopic,
Ci.nsIPermissionManager.ALLOW_ACTION);
}
};
var secondaryActions = [
{
label: gNavigatorBundle.getString("offlineApps.never"),
accessKey: gNavigatorBundle.getString("offlineApps.neverAccessKey"),
callback: function() {
observer.observe(null, responseTopic,
Ci.nsIPermissionManager.DENY_ACTION);
}
}
];
PopupNotifications.show(browser, topic, message, this._notificationIcon,
mainAction, secondaryActions);
}
};
function WindowIsClosing()
{
var reallyClose = closeWindow(false, warnAboutClosingWindow);
@ -7225,6 +7386,22 @@ var gIdentityHandler = {
// Now open the popup, anchored off the primary chrome element
this._identityPopup.openPopup(this._identityBox, position);
},
onDragStart: function (event) {
if (gURLBar.getAttribute("pageproxystate") != "valid")
return;
var value = content.location.href;
var urlString = value + "\n" + content.document.title;
var htmlString = "<a href=\"" + value + "\">" + value + "</a>";
var dt = event.dataTransfer;
dt.setData("text/x-moz-url", urlString);
dt.setData("text/uri-list", value);
dt.setData("text/plain", value);
dt.setData("text/html", htmlString);
dt.setDragImage(event.currentTarget, 0, 0);
}
};
@ -7886,6 +8063,12 @@ function switchToTabHavingURI(aURI, aOpenNew, aCallback) {
return false;
}
function restoreLastSession() {
let ss = Cc["@mozilla.org/browser/sessionstore;1"].
getService(Ci.nsISessionStore);
ss.restoreLastSession();
}
var TabContextMenu = {
contextTab: null,
updateContextMenu: function updateContextMenu(aPopupMenu) {

View File

@ -120,11 +120,11 @@
accesskey="&openTabInNewWindow.accesskey;"
tbattr="tabbrowser-multiple"
oncommand="gBrowser.replaceTabWithWindow(TabContextMenu.contextTab);"/>
<menuitem id="context_pinTab" label="&pinTab.label;"
accesskey="&pinTab.accesskey;"
<menuitem id="context_pinTab" label="&pinAppTab.label;"
accesskey="&pinAppTab.accesskey;"
oncommand="gBrowser.pinTab(TabContextMenu.contextTab);"/>
<menuitem id="context_unpinTab" label="&unpinTab.label;" hidden="true"
accesskey="&unpinTab.accesskey;"
<menuitem id="context_unpinTab" label="&unpinAppTab.label;" hidden="true"
accesskey="&unpinAppTab.accesskey;"
oncommand="gBrowser.unpinTab(TabContextMenu.contextTab);"/>
<menu id="context_tabViewMenu" label="&moveToGroup.label;"
accesskey="&moveToGroup.accesskey;">
@ -163,6 +163,9 @@
<!-- for url bar autocomplete -->
<panel type="autocomplete-richlistbox" id="PopupAutoCompleteRichResult" noautofocus="true" hidden="true"/>
<!-- for invalid form error message -->
<panel id="invalid-form-popup" noautofocus="true" hidden="true" level="parent"/>
<panel id="editBookmarkPanel"
orient="vertical"
ignorekeys="true"
@ -268,12 +271,7 @@
class="toolbarbutton-text"
oncommand="InspectorUI.toggleDOMPanel();"/>
</toolbar>
<iframe id="inspector-tree-iframe"
flex="1"
type="content"
src="chrome://browser/content/inspector.html"
onclick="InspectorUI.onTreeClick(event);" />
<hbox align="end">
<hbox id="tree-panel-resizer-box" align="end">
<spacer flex="1" />
<resizer dir="bottomend" />
</hbox>
@ -451,8 +449,8 @@
<vbox id="appmenuPrimaryPane">
<hbox flex="1"
class="split-menuitem">
<menuitem id="menuitem-tooltip appmenu_newTab"
class="split-menuitem-item"
<menuitem id="appmenu_newTab"
class="menuitem-tooltip split-menuitem-item"
flex="1"
label="&tabCmd.label;"
command="cmd_newNavigatorTab"
@ -483,9 +481,9 @@
command="Tools:PrivateBrowsing"
key="key_privatebrowsing"/>
<menuseparator class="appmenu-menuseparator"/>
<hbox class="split-menuitem">
<menuitem id="appmenu-edit-menuitem"
label="&editMenu.label;"
<hbox>
<menuitem id="appmenu-edit-label"
label="&appMenuEdit.label;"
disabled="true"/>
<toolbarbutton id="appmenu-cut"
class="appmenu-edit-button"
@ -530,7 +528,8 @@
<menuitem id="appmenu_print_popup"
class="menuitem-iconic"
label="&printCmd.label;"
command="cmd_print"/>
command="cmd_print"
key="printKb"/>
<menuitem id="appmenu_printPreview"
label="&printPreviewCmd.label;"
command="cmd_printPreview"/>
@ -550,6 +549,7 @@
oncommand="HUDConsoleUI.toggleHUD();"
key="key_webConsole"/>
<menuitem id="appmenu_pageInspect"
hidden="true"
label="&inspectMenu.label;"
type="checkbox"
command="Tools:Inspect"
@ -670,6 +670,11 @@
key="key_sanitize"
command="Tools:Sanitize"/>
<menuseparator class="hide-if-empty-places-result"/>
<menuitem id="appmenu_restoreLastSession"
class="restoreLastSession"
label="&historyRestoreLastSession.label;"
oncommand="restoreLastSession();"
disabled="true"/>
<menu id="appmenu_recentlyClosedTabsMenu"
class="recentlyClosedTabsMenu"
label="&historyUndoMenu.label;"
@ -879,6 +884,8 @@
<box id="notification-popup-box" hidden="true" align="center">
<image id="geo-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="addons-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="indexedDB-notification-icon" class="notification-anchor-icon" role="button"/>
<image id="password-notification-icon" class="notification-anchor-icon" role="button"/>
</box>
<!-- Use onclick instead of normal popup= syntax since the popup
code fires onmousedown, and hence eats our favicon drag events.
@ -886,14 +893,14 @@
has focus, otherwise pressing F6 focuses it instead of the location bar -->
<box id="identity-box" role="button"
onclick="gIdentityHandler.handleIdentityButtonEvent(event);"
onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);">
onkeypress="gIdentityHandler.handleIdentityButtonEvent(event);"
ondragstart="gIdentityHandler.onDragStart(event);">
<hbox id="identity-box-inner" align="center">
<stack id="page-proxy-stack"
onclick="PageProxyClickHandler(event);">
<image id="urlbar-throbber" busy="false"/>
<image id="page-proxy-favicon" validate="never"
pageproxystate="invalid"
ondragstart="proxyIconDNDObserver.onDragStart(event);"
onerror="this.removeAttribute('src');"/>
</stack>
<hbox id="identity-icon-labels">

View File

@ -335,6 +335,8 @@ var InspectorUI = {
selectEventsSuppressed: false,
showTextNodesWithWhitespace: false,
inspecting: false,
treeLoaded: false,
prefEnabledName: "devtools.inspector.enabled",
/**
* Toggle the inspector interface elements on or off.
@ -434,6 +436,21 @@ var InspectorUI = {
return doc.documentElement.lastElementChild;
},
initializeTreePanel: function IUI_initializeTreePanel()
{
this.treeBrowserDocument = this.treeIFrame.contentDocument;
this.treePanelDiv = this.treeBrowserDocument.createElement("div");
this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
this.treePanelDiv.ownerPanel = this;
this.ioBox = new InsideOutBox(this, this.treePanelDiv);
this.ioBox.createObjectBox(this.win.document.documentElement);
this.treeLoaded = true;
if (this.isTreePanelOpen && this.isStylePanelOpen &&
this.isDOMPanelOpen && this.treeLoaded) {
this.notifyReady();
}
},
/**
* Open the inspector's tree panel and initialize it.
*/
@ -444,6 +461,17 @@ var InspectorUI = {
this.treePanel.hidden = false;
}
this.treeIFrame = document.getElementById("inspector-tree-iframe");
if (!this.treeIFrame) {
let resizerBox = document.getElementById("tree-panel-resizer-box");
this.treeIFrame = document.createElement("iframe");
this.treeIFrame.setAttribute("id", "inspector-tree-iframe");
this.treeIFrame.setAttribute("flex", "1");
this.treeIFrame.setAttribute("type", "content");
this.treeIFrame.setAttribute("onclick", "InspectorUI.onTreeClick(event)");
this.treeIFrame = this.treePanel.insertBefore(this.treeIFrame, resizerBox);
}
const panelWidthRatio = 7 / 8;
const panelHeightRatio = 1 / 5;
this.treePanel.openPopup(this.browser, "overlap", 80, this.win.innerHeight,
@ -451,13 +479,18 @@ var InspectorUI = {
this.treePanel.sizeTo(this.win.outerWidth * panelWidthRatio,
this.win.outerHeight * panelHeightRatio);
this.treeIFrame = document.getElementById("inspector-tree-iframe");
this.treeBrowserDocument = this.treeIFrame.contentDocument;
this.treePanelDiv = this.treeBrowserDocument.createElement("div");
this.treeBrowserDocument.body.appendChild(this.treePanelDiv);
this.treePanelDiv.ownerPanel = this;
this.ioBox = new InsideOutBox(this, this.treePanelDiv);
this.ioBox.createObjectBox(this.win.document.documentElement);
let src = this.treeIFrame.getAttribute("src");
if (src != "chrome://browser/content/inspector.html") {
let self = this;
this.treeIFrame.addEventListener("DOMContentLoaded", function() {
self.treeIFrame.removeEventListener("DOMContentLoaded", arguments.callee, true);
self.initializeTreePanel();
}, true);
this.treeIFrame.setAttribute("src", "chrome://browser/content/inspector.html");
} else {
this.initializeTreePanel();
}
},
createObjectBox: function IUI_createObjectBox(object, isRoot)
@ -773,6 +806,7 @@ var InspectorUI = {
this.browser = this.win = null; // null out references to browser and window
this.winID = null;
this.selection = null;
this.treeLoaded = false;
this.closing = false;
Services.obs.notifyObservers(null, "inspector-closed", null);
},
@ -964,6 +998,12 @@ var InspectorUI = {
/////////////////////////////////////////////////////////////////////////
//// Event Handling
notifyReady: function IUI_notifyReady()
{
document.removeEventListener("popupshowing", this, false);
Services.obs.notifyObservers(null, "inspector-opened", null);
},
/**
* Main callback handler for events.
*
@ -981,9 +1021,9 @@ var InspectorUI = {
if (event.target.id == "inspector-tree-panel" ||
event.target.id == "inspector-style-panel" ||
event.target.id == "inspector-dom-panel")
if (this.isTreePanelOpen && this.isStylePanelOpen && this.isDOMPanelOpen) {
document.removeEventListener("popupshowing", this, false);
Services.obs.notifyObservers(null, "inspector-opened", null);
if (this.isTreePanelOpen && this.isStylePanelOpen &&
this.isDOMPanelOpen && this.treeLoaded) {
this.notifyReady();
}
break;
case "TabSelect":

View File

@ -89,11 +89,13 @@
<command id="cmd_cookieDef" oncommand="onCheckboxClick('cookie');"/>
<command id="cmd_installDef" oncommand="onCheckboxClick('install');"/>
<command id="cmd_geoDef" oncommand="onCheckboxClick('geo');"/>
<command id="cmd_indexedDBDef" oncommand="onCheckboxClick('indexedDB');"/>
<command id="cmd_imageToggle" oncommand="onRadioClick('image');"/>
<command id="cmd_popupToggle" oncommand="onRadioClick('popup');"/>
<command id="cmd_cookieToggle" oncommand="onRadioClick('cookie');"/>
<command id="cmd_installToggle" oncommand="onRadioClick('install');"/>
<command id="cmd_geoToggle" oncommand="onRadioClick('geo');"/>
<command id="cmd_indexedDBToggle" oncommand="onRadioClick('indexedDB');"/>
</commandset>
<keyset>
@ -375,6 +377,23 @@
</radiogroup>
</hbox>
</vbox>
<vbox class="permission">
<label class="permissionLabel" id="permIndexedDBLabel"
value="&permIndexedDB;" control="indexedDBRadioGroup"/>
<hbox role="group" aria-labelledby="permIndexedDBLabel">
<checkbox id="indexedDBDef" command="cmd_indexedDBDef" label="&permAskAlways;"/>
<spacer flex="1"/>
<vbox pack="center">
<label id="indexedDBStatus" control="indexedDBClear"/>
</vbox>
<button id="indexedDBClear" label="&permClearStorage;"
accesskey="&permClearStorage.accesskey;" onclick="onIndexedDBClear();"/>
<radiogroup id="indexedDBRadioGroup" orient="horizontal">
<radio id="indexedDB#1" command="cmd_indexedDBToggle" label="&permAllow;"/>
<radio id="indexedDB#2" command="cmd_indexedDBToggle" label="&permBlock;"/>
</radiogroup>
</hbox>
</vbox>
</vbox>
</vbox>

View File

@ -36,6 +36,10 @@
const ALLOW = nsIPermissionManager.ALLOW_ACTION; // 1
const BLOCK = nsIPermissionManager.DENY_ACTION; // 2
const SESSION = nsICookiePermission.ACCESS_SESSION;// 8
const nsIIndexedDatabaseManager =
Components.interfaces.nsIIndexedDatabaseManager;
var gPermURI;
var gPrefs;
@ -73,7 +77,11 @@ var gPermObj = {
},
geo: function getGeoDefaultPermissions()
{
return BLOCK;
return BLOCK;
},
indexedDB: function getIndexedDBDefaultPermissions()
{
return BLOCK;
}
};
@ -137,6 +145,10 @@ function initRow(aPartId)
perm = gPermObj[aPartId]();
}
setRadioState(aPartId, perm);
if (aPartId == "indexedDB") {
initIndexedDBRow();
}
}
function onCheckboxClick(aPartId)
@ -148,6 +160,9 @@ function onCheckboxClick(aPartId)
var checkbox = document.getElementById(aPartId + "Def");
if (checkbox.checked) {
permissionManager.remove(gPermURI.host, aPartId);
if (aPartId == "indexedDB") {
permissionManager.remove(gPermURI.host, "indexedDB-unlimited");
}
command.setAttribute("disabled", "true");
var perm = gPermObj[aPartId]();
setRadioState(aPartId, perm);
@ -167,6 +182,9 @@ function onRadioClick(aPartId)
var id = radioGroup.selectedItem.id;
var permission = id.split('#')[1];
permissionManager.add(gPermURI, aPartId, permission);
if (aPartId == "indexedDB" && permission == BLOCK) {
permissionManager.remove(gPermURI.host, "indexedDB-unlimited");
}
}
function setRadioState(aPartId, aValue)
@ -174,3 +192,41 @@ function setRadioState(aPartId, aValue)
var radio = document.getElementById(aPartId + "#" + aValue);
radio.radioGroup.selectedItem = radio;
}
function initIndexedDBRow()
{
var status = document.getElementById("indexedDBStatus");
var button = document.getElementById("indexedDBClear");
var usage = Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
.getService(nsIIndexedDatabaseManager)
.getUsageForURI(gPermURI);
if (usage) {
if (!("DownloadUtils" in window)) {
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
}
status.value =
gBundle.getFormattedString("indexedDBUsage",
DownloadUtils.convertByteUnits(usage));
status.removeAttribute("hidden");
button.removeAttribute("hidden");
}
else {
status.value = "";
status.setAttribute("hidden", "true");
button.setAttribute("hidden", "true");
}
}
function onIndexedDBClear()
{
Components.classes["@mozilla.org/dom/indexeddb/manager;1"]
.getService(nsIIndexedDatabaseManager)
.clearDatabasesForURI(gPermURI);
var permissionManager = Components.classes[PERMISSION_CONTRACTID]
.getService(nsIPermissionManager);
permissionManager.remove(gPermURI.host, "indexedDB");
permissionManager.remove(gPermURI.host, "indexedDB-unlimited");
initIndexedDBRow();
}

View File

@ -58,6 +58,7 @@ const SETUP_SUCCESS_PAGE = 8;
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/PluralForm.jsm");
var gSyncSetup = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsISupports,
@ -718,7 +719,9 @@ var gSyncSetup = {
if (stm.step())
daysOfHistory = stm.getInt32(0);
document.getElementById("historyCount").value =
this._stringBundle.formatStringFromName("historyCount.label", [daysOfHistory], 1);
PluralForm.get(daysOfHistory,
this._stringBundle.GetStringFromName("historyDaysCount.label"))
.replace("#1", daysOfHistory);
// bookmarks
let bookmarks = 0;
@ -731,12 +734,16 @@ var gSyncSetup = {
if (stm.executeStep())
bookmarks = stm.row.bookmarks;
document.getElementById("bookmarkCount").value =
this._stringBundle.formatStringFromName("bookmarkCount.label", [bookmarks], 1);
PluralForm.get(bookmarks,
this._stringBundle.GetStringFromName("bookmarksCount.label"))
.replace("#1", bookmarks);
// passwords
let logins = Weave.Svc.Login.getAllLogins({});
document.getElementById("passwordCount").value =
this._stringBundle.formatStringFromName("passwordCount.label", [logins.length], 1);
PluralForm.get(logins.length,
this._stringBundle.GetStringFromName("passwordsCount.label"))
.replace("#1", logins.length);
this._case1Setup = true;
break;
case 2:
@ -762,7 +769,9 @@ var gSyncSetup = {
}
if (count > 5) {
let label =
this._stringBundle.formatStringFromName("additionalClients.label", [count - 5], 1);
PluralForm.get(count - 5,
this._stringBundle.GetStringFromName("additionalClientCount.label"))
.replace("#1", count - 5);
appendNode(label);
}
this._case2Setup = true;

View File

@ -185,6 +185,10 @@
aTab.setAttribute("pinned", "true");
this.tabContainer._positionPinnedTabs();
this.tabContainer.adjustTabstrip();
let event = document.createEvent("Events");
event.initEvent("TabPin", true, false);
aTab.dispatchEvent(event);
]]></body>
</method>
@ -200,6 +204,10 @@
aTab.style.MozMarginStart = "";
this.tabContainer._positionPinnedTabs();
this.tabContainer.adjustTabstrip();
let event = document.createEvent("Events");
event.initEvent("TabUnpin", true, false);
aTab.dispatchEvent(event);
]]></body>
</method>
@ -432,7 +440,7 @@
this.mTabBrowser.setTabTitleLoading(this.mTab);
}
if (this.mTabBrowser.mCurrentTab == this.mTab)
if (this.mTab.selected)
this.mTabBrowser.mIsBusy = true;
}
}
@ -468,7 +476,7 @@
if (this.mTab.label == this.mTabBrowser.mStringBundle.getString("tabs.loading"))
this.mTabBrowser.setTabTitle(this.mTab);
if (this.mTabBrowser.mCurrentTab == this.mTab)
if (this.mTab.selected)
this.mTabBrowser.mIsBusy = false;
}
@ -937,6 +945,10 @@
aTab.label = title;
aTab.crop = crop;
this._tabAttrModified(aTab);
if (aTab.selected)
this.updateTitlebar();
return true;
]]>
</body>
@ -2470,12 +2482,7 @@
var tab = this._getTabForContentWindow(contentWin);
var titleChanged = this.setTabTitle(tab);
if (!titleChanged)
return;
if (tab == this.mCurrentTab)
this.updateTitlebar();
else if (!tab.hasAttribute("busy"))
if (titleChanged && !tab.selected && !tab.hasAttribute("busy"))
tab.setAttribute("titlechanged", "true");
]]>
</handler>

View File

@ -64,7 +64,6 @@
// title - the title for the groupItem; otherwise blank
// dontPush - true if this groupItem shouldn't push away on creation; default is false
function GroupItem(listOfEls, options) {
try {
if (typeof options == 'undefined')
options = {};
@ -78,6 +77,7 @@ function GroupItem(listOfEls, options) {
this.expanded = null;
this.locked = (options.locked ? Utils.copy(options.locked) : {});
this.topChild = null;
this.hidden = false;
this.keepProportional = false;
@ -130,8 +130,7 @@ function GroupItem(listOfEls, options) {
.click(function() {
self.newTab();
})
.attr('title',
"New tab")
.attr('title', tabviewString('groupItem.newTabButton'))
.appendTo($container);
// ___ Resizer
@ -187,7 +186,7 @@ function GroupItem(listOfEls, options) {
}
};
var handleKeyPress = function(e) {
var handleKeyDown = function(e) {
if (e.which == 13 || e.which == 27) { // return & escape
(self.$title)[0].blur();
self.$title
@ -195,9 +194,16 @@ function GroupItem(listOfEls, options) {
.one("mouseout", function() {
self.$title.removeClass("transparentBorder");
});
} else
self.adjustTitleSize();
e.stopPropagation();
e.preventDefault();
}
};
var handleKeyUp = function(e) {
// NOTE: When user commits or cancels IME composition, the last key
// event fires only a keyup event. Then, we shouldn't take any
// reactions but we should update our status.
self.adjustTitleSize();
self.save();
};
@ -216,7 +222,8 @@ function GroupItem(listOfEls, options) {
.val('');
}
})
.keyup(handleKeyPress);
.keydown(handleKeyDown)
.keyup(handleKeyUp);
titleUnfocus();
@ -246,6 +253,16 @@ function GroupItem(listOfEls, options) {
.appendTo($container)
.hide();
// ___ app tabs: create app tab tray and populate it
this.$appTabTray = iQ("<div/>")
.addClass("appTabTray")
.appendTo($container);
AllTabs.tabs.forEach(function(xulTab) {
if (xulTab.pinned && xulTab.ownerDocument.defaultView == gWindow)
self.addAppTab(xulTab);
});
// ___ locking
if (this.locked.bounds)
$container.css({cursor: 'default'});
@ -253,6 +270,9 @@ function GroupItem(listOfEls, options) {
if (this.locked.close)
$close.hide();
// ___ Undo Close
this.$undoContainer = null;
// ___ Superclass initialization
this._init($container[0]);
@ -285,10 +305,6 @@ function GroupItem(listOfEls, options) {
this._inited = true;
this.save();
} catch(e) {
Utils.log("Error in GroupItem()");
Utils.log(e.stack);
}
};
// ----------
@ -296,19 +312,23 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// ----------
// Variable: defaultName
// The prompt text for the title field.
defaultName: "Name this tab group…",
defaultName: tabviewString('groupItem.defaultName'),
// -----------
// Function: setActiveTab
// Sets the active <TabItem> for this groupItem
// Sets the active <TabItem> for this groupItem; can be null, but only
// if there are no children.
setActiveTab: function GroupItem_setActiveTab(tab) {
Utils.assert(tab && tab.isATabItem, 'tab must be a TabItem');
Utils.assertThrow((!tab && this._children.length == 0) || tab.isATabItem,
"tab must be null (if no children) or a TabItem");
this._activeTab = tab;
},
// -----------
// Function: getActiveTab
// Gets the active <TabItem> for this groupItem
// Gets the active <TabItem> for this groupItem; can be null, but only
// if there are no children.
getActiveTab: function GroupItem_getActiveTab() {
return this._activeTab;
},
@ -390,6 +410,8 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
box.top += titleHeight;
box.height -= titleHeight;
box.width -= this.$appTabTray.width();
// Make the computed bounds' "padding" and new tab button margin actually be
// themeable --OR-- compute this from actual bounds. Bug 586546
box.inset(6, 6);
@ -523,9 +545,16 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
GroupItems.unregister(this);
this._sendToSubscribers("close");
this.removeTrenches();
iQ(this.container).fadeOut(function() {
iQ(this).remove();
Items.unsquish();
iQ(this.container).animate({
opacity: 0,
"-moz-transform": "scale(.3)",
}, {
duration: 170,
complete: function() {
iQ(this).remove();
Items.unsquish();
}
});
Storage.deleteGroupItem(gWindow, this.id);
@ -535,17 +564,137 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// Function: closeAll
// Closes the groupItem and all of its children.
closeAll: function GroupItem_closeAll() {
var self = this;
if (this._children.length) {
var toClose = this._children.concat();
if (this._children.length > 0) {
this._children.forEach(function(child) {
iQ(child.container).hide();
});
iQ(this.container).animate({
opacity: 0,
"-moz-transform": "scale(.3)",
}, {
duration: 170,
complete: function() {
iQ(this).hide();
}
});
this._createUndoButton();
} else {
if (!this.locked.close)
this.close();
}
},
// ----------
// Function: _createUndoButton
// Makes the affordance for undo a close group action
_createUndoButton: function() {
let self = this;
this.$undoContainer = iQ("<div/>")
.addClass("undo")
.attr("type", "button")
.text("Undo Close Group")
.appendTo("body");
let undoClose = iQ("<span/>")
.addClass("close")
.appendTo(this.$undoContainer);
this.$undoContainer.css({
left: this.bounds.left + this.bounds.width/2 - iQ(self.$undoContainer).width()/2,
top: this.bounds.top + this.bounds.height/2 - iQ(self.$undoContainer).height()/2,
"-moz-transform": "scale(.1)",
opacity: 0
});
this.hidden = true;
setTimeout(function() {
self.$undoContainer.animate({
"-moz-transform": "scale(1)",
"opacity": 1
}, {
easing: "tabviewBounce",
duration: 170,
complete: function() {
self._sendToSubscribers("groupHidden", { groupItemId: self.id });
}
});
}, 50);
let remove = function() {
// close all children
let toClose = self._children.concat();
toClose.forEach(function(child) {
child.removeSubscriber(self, "close");
child.close();
});
}
// remove all children
self.removeAll();
GroupItems.unregister(self);
self._sendToSubscribers("close");
self.removeTrenches();
if (!this.locked.close)
this.close();
iQ(self.container).remove();
self.$undoContainer.remove();
self.$undoContainer = null;
Items.unsquish();
Storage.deleteGroupItem(gWindow, self.id);
};
this.$undoContainer.click(function(e) {
// Only do this for clicks on this actual element.
if (e.target.nodeName != self.$undoContainer[0].nodeName)
return;
self.$undoContainer.fadeOut(function() {
iQ(this).remove();
self.hidden = false;
self.$undoContainer = null;
iQ(self.container).show().animate({
"-moz-transform": "scale(1)",
"opacity": 1
}, {
duration: 170,
complete: function() {
self._children.forEach(function(child) {
iQ(child.container).show();
});
}
});
self._sendToSubscribers("groupShown", { groupItemId: self.id });
});
});
undoClose.click(function() {
self.$undoContainer.fadeOut(remove);
});
// After 15 seconds, fade away.
const WAIT = 15000;
const FADE = 300;
let fadeaway = function() {
if (self.$undoContainer)
self.$undoContainer.animate({
color: "transparent",
opacity: 0
}, {
duration: FADE,
complete: remove
});
};
let timeoutId = setTimeout(fadeaway, WAIT);
// Cancel the fadeaway if you move the mouse over the undo
// button, and restart the countdown once you move out of it.
this.$undoContainer.mouseover(function() clearTimeout(timeoutId));
this.$undoContainer.mouseout(function() {
timeoutId = setTimeout(fadeaway, WAIT);
});
},
// ----------
@ -573,9 +722,6 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
item.removeTrenches();
if (!dropPos)
dropPos = {top:window.innerWidth, left:window.innerHeight};
if (typeof options == 'undefined')
options = {};
@ -629,7 +775,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
}
// Insert the tab into the right position.
var index = findInsertionPoint(dropPos);
var index = dropPos ? findInsertionPoint(dropPos) : this._children.length;
this._children.splice(index, 0, item);
item.setZ(this.getZ() + 1);
@ -655,6 +801,9 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
if (!options.dontArrange) {
this.arrange();
}
this._sendToSubscribers("childAdded",{ groupItemId: this.id, item: item });
UI.setReorderTabsOnHide(this);
} catch(e) {
Utils.log('GroupItem.add error', e);
@ -688,6 +837,13 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
var index = this._children.indexOf(item);
if (index != -1)
this._children.splice(index, 1);
if (item == this._activeTab) {
if (this._children.length)
this._activeTab = this._children[0];
else
this._activeTab = null;
}
item.setParent(null);
item.removeClass("tabInGroupItem");
@ -706,6 +862,9 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
} else if (!options.dontArrange) {
this.arrange();
}
this._sendToSubscribers("childRemoved",{ groupItemId: this.id, item: item });
} catch(e) {
Utils.log(e);
}
@ -722,6 +881,33 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
});
},
// ----------
// Adds the given xul:tab as an app tab in this group's apptab tray
addAppTab: function GroupItem_addAppTab(xulTab) {
let self = this;
let icon = xulTab.image || Utils.defaultFaviconURL;
let $appTab = iQ("<img>")
.addClass("appTabIcon")
.attr("src", icon)
.data("xulTab", xulTab)
.appendTo(this.$appTabTray)
.click(function(event) {
if (Utils.isRightClick(event))
return;
GroupItems.setActiveGroupItem(self);
GroupItems._updateTabBar();
UI.goToTab(iQ(this).data("xulTab"));
});
let columnWidth = $appTab.width();
if (parseInt(this.$appTabTray.css("width")) != columnWidth) {
this.$appTabTray.css({width: columnWidth});
this.arrange();
}
},
// ----------
// Function: hideExpandControl
// Hide the control which expands a stacked groupItem into a quick-look view.
@ -777,7 +963,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
this.topChild = null;
var box = new Rect(this.expanded.bounds);
box.inset(8, 8);
Items.arrange(this._children, box, Utils.extend({}, options, {padding: 8, z: 99999}));
Items.arrange(this._children, box, Utils.extend({}, options, {z: 99999}));
} else {
var bb = this.getContentBounds();
var count = this._children.length;
@ -1119,6 +1305,8 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
className.indexOf('name') != -1 ||
className.indexOf('close') != -1 ||
className.indexOf('newTabButton') != -1 ||
className.indexOf('appTabTray') != -1 ||
className.indexOf('appTabIcon') != -1 ||
className.indexOf('stackExpander') != -1) {
return;
}
@ -1171,7 +1359,9 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
GroupItems.setActiveGroupItem(this);
let newTab = gBrowser.loadOneTab(url || "about:blank", {inBackground: true});
// TabItems will have handled the new tab and added the tabItem property
// TabItems will have handled the new tab and added the tabItem property.
// We don't have to check if it's an app tab (and therefore wouldn't have a
// TabItem), since we've just created it.
let newItem = newTab.tabItem;
var self = this;
@ -1201,7 +1391,6 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
iQ(newItem.container).css({opacity: 1});
newItem.zoomIn(!url);
$anim.remove();
self._sendToSubscribers("tabAdded", {groupItemId: self.id});
}
});
}
@ -1407,21 +1596,6 @@ let GroupItems = {
return sane;
},
// ----------
// Function: getGroupItemWithTitle
// Returns the <GroupItem> that has the given title, or null if none found.
// TODO: what if there are multiple groupItems with the same title??
// Right now, looks like it'll return the last one. Bug 586557
getGroupItemWithTitle: function GroupItems_getGroupItemWithTitle(title) {
var result = null;
this.groupItems.forEach(function(groupItem) {
if (groupItem.getTitle() == title)
result = groupItem;
});
return result;
},
// ----------
// Function: register
// Adds the given <GroupItem> to the list of groupItems we're tracking.
@ -1551,14 +1725,13 @@ let GroupItems = {
// ----------
// Function: setActiveGroupItem
// Sets the active groupItem, thereby showing only the relevent tabs, and
// Sets the active groupItem, thereby showing only the relevant tabs and
// setting the groupItem which will receive new tabs.
//
// Paramaters:
// groupItem - the active <GroupItem> or <null> if no groupItem is active
// (which means we have an orphaned tab selected)
setActiveGroupItem: function GroupItems_setActiveGroupItem(groupItem) {
if (this._activeGroupItem)
iQ(this._activeGroupItem.container).removeClass('activeGroupItem');
@ -1610,16 +1783,18 @@ let GroupItems = {
// ----------
// Function: updateActiveGroupItemAndTabBar
// Sets active group item and updates tab bar
// Sets active TabItem and GroupItem, and updates tab bar appropriately.
updateActiveGroupItemAndTabBar: function GroupItems_updateActiveGroupItemAndTabBar(tabItem) {
if (tabItem.parent) {
let groupItem = tabItem.parent;
this.setActiveGroupItem(groupItem);
Utils.assertThrow(tabItem && tabItem.isATabItem, "tabItem must be a TabItem");
let groupItem = tabItem.parent;
this.setActiveGroupItem(groupItem);
if (groupItem)
groupItem.setActiveTab(tabItem);
} else {
this.setActiveGroupItem(null);
else
this.setActiveOrphanTab(tabItem);
}
this._updateTabBar();
},
@ -1651,10 +1826,12 @@ let GroupItems = {
if (!activeGroupItem) {
if (groupItems.length > 0) {
groupItems.some(function(groupItem) {
var child = groupItem.getChild(0);
if (child) {
tabItem = child;
return true;
if (!groupItem.hidden) {
var child = groupItem.getChild(0);
if (child) {
tabItem = child;
return true;
}
}
return false;
});
@ -1662,7 +1839,7 @@ let GroupItems = {
} else {
var currentIndex;
groupItems.some(function(groupItem, index) {
if (groupItem == activeGroupItem) {
if (!groupItem.hidden && groupItem == activeGroupItem) {
currentIndex = index;
return true;
}
@ -1670,10 +1847,12 @@ let GroupItems = {
});
var firstGroupItems = groupItems.slice(currentIndex + 1);
firstGroupItems.some(function(groupItem) {
var child = groupItem.getChild(0);
if (child) {
tabItem = child;
return true;
if (!groupItem.hidden) {
var child = groupItem.getChild(0);
if (child) {
tabItem = child;
return true;
}
}
return false;
});
@ -1685,10 +1864,12 @@ let GroupItems = {
if (!tabItem) {
var secondGroupItems = groupItems.slice(0, currentIndex);
secondGroupItems.some(function(groupItem) {
var child = groupItem.getChild(0);
if (child) {
tabItem = child;
return true;
if (!groupItem.hidden) {
var child = groupItem.getChild(0);
if (child) {
tabItem = child;
return true;
}
}
return false;
});
@ -1699,10 +1880,17 @@ let GroupItems = {
// ----------
// Function: moveTabToGroupItem
// Used for the right click menu in the tab strip; moves the given tab
// into the given group. Does nothing if the tab is an app tab.
// Paramaters:
// tab - the <xul:tab>.
// groupItemId - the <groupItem>'s id. If nothing, create a new <groupItem>.
moveTabToGroupItem : function GroupItems_moveTabToGroupItem (tab, groupItemId) {
if (tab.pinned)
return;
Utils.assertThrow(tab.tabItem, "tab must be linked to a TabItem");
let shouldUpdateTabBar = false;
let shouldShowTabView = false;
let groupItem;
@ -1757,6 +1945,8 @@ let GroupItems = {
// Function: killNewTabGroup
// Removes the New Tab Group, which is now defunct. See bug 575851 and comments therein.
killNewTabGroup: function GroupItems_killNewTabGroup() {
// not localized as the original "New Tabs" group title was never localized
// to begin with
let newTabGroupTitle = "New Tabs";
this.groupItems.forEach(function(groupItem) {
if (groupItem.getTitle() == newTabGroupTitle && groupItem.locked.title) {
@ -1764,5 +1954,25 @@ let GroupItems = {
groupItem.close();
}
});
},
// ----------
// Function: removeHiddenGroups
// Removes all hidden groups' data and its browser tabs.
removeHiddenGroups: function GroupItems_removeHiddenGroups() {
iQ(".undo").remove();
// ToDo: encapsulate this in the group item. bug 594863
this.groupItems.forEach(function(groupItem) {
if (groupItem.hidden) {
let toClose = groupItem._children.concat();
toClose.forEach(function(child) {
child.removeSubscriber(groupItem, "close");
child.close();
});
Storage.deleteGroupItem(gWindow, groupItem.id);
}
});
}
};

View File

@ -915,7 +915,11 @@ let Items = {
return rects;
var columns = 1;
var padding = options.padding || 0;
// We'll assume for the time being that all the items have the same styling
// and that the margin is the same width around.
var itemMargin = items && items.length ?
parseInt(iQ(items[0].container).css('margin-left')) : 0;
var padding = itemMargin * 2;
var yScale = 1.1; // to allow for titles
var rows;
var tabWidth;
@ -924,9 +928,9 @@ let Items = {
function figure() {
rows = Math.ceil(count / columns);
tabWidth = (bounds.width - (padding * (columns - 1))) / columns;
tabWidth = (bounds.width - (padding * columns)) / columns;
tabHeight = tabWidth * tabAspect;
totalHeight = (tabHeight * yScale * rows) + (padding * (rows - 1));
totalHeight = (tabHeight * yScale * rows) + (padding * rows);
}
figure();
@ -937,8 +941,7 @@ let Items = {
}
if (rows == 1) {
var maxWidth = Math.max(TabItems.tabWidth, bounds.width / 2);
tabWidth = Math.min(Math.min(maxWidth, bounds.width / count), bounds.height / tabAspect);
tabWidth = Math.min(tabWidth, (bounds.height - 2 * itemMargin) / tabAspect);
tabHeight = tabWidth * tabAspect;
}

View File

@ -322,6 +322,20 @@ Range.prototype = {
return false;
},
// ----------
// Function: overlaps
// Whether the <Range> overlaps with the given <Range> or value or not.
//
// Paramaters
// - a number or <Range>
overlaps: function Rect_overlaps(value) {
if (Utils.isNumber(value))
return this.contains(value);
if (Utils.isRange(value))
return !(value.max < this.min || this.max < value.min);
return false;
},
// ----------
// Function: proportion
// Maps the given value to the range [0,1], so that it returns 0 if the value is <= the min,
@ -464,8 +478,9 @@ Subscribable.prototype = {
// Class: Utils
// Singelton with common utility functions.
let Utils = {
// ___ Logging
defaultFaviconURL: "chrome://mozapps/skin/places/defaultFavicon.png",
// ___ Logging
useConsole: true, // as opposed to dump
showTime: false,

View File

@ -0,0 +1,395 @@
/* ***** 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 search.js.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Aza Raskin <aza@mozilla.com>
* Raymond Lee <raymond@raysquare.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 ***** */
/* ******************************
*
* This file incorporates work from:
* Quicksilver Score (qs_score):
* http://rails-oceania.googlecode.com/svn/lachiecox/qs_score/trunk/qs_score.js
* This incorporated work is covered by the following copyright and
* permission notice:
* Copyright 2008 Lachie Cox
* Licensed under the MIT license.
* http://jquery.org/license
*
* ***************************** */
// **********
// 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: 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;
}
TabMatcher.prototype = {
// ----------
// 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.
matched: function matched() {
var self = this;
if (this.term.length < 2)
return [];
var tabs = TabItems.getItems();
tabs = tabs.filter(function(tab){
var name = tab.nameEl.innerHTML;
return name.match(self.term, "i");
});
tabs.sort(function sorter(x, y){
var yScore = scorePatternMatch(self.term, y.nameEl.innerHTML);
var xScore = scorePatternMatch(self.term, x.nameEl.innerHTML);
return yScore - xScore;
});
return tabs;
},
// ----------
// Function: unmatched
// Returns all of <TabItem>s that .matched() doesn't return.
unmatched: function unmatched() {
var self = this;
var tabs = TabItems.getItems();
if ( this.term.length < 2 )
return tabs;
return tabs.filter(function(tab) {
var name = tab.nameEl.innerHTML;
return !name.match(self.term, "i");
});
},
// ----------
// Function: doSearch
// Performs the search. Lets you provide two functions, one that is called
// on all matched tabs, and one that is called on all unmatched tabs.
// Both functions take two parameters: A <TabItem> and its integer index
// indicating the absolute rank of the <TabItem> in terms of match to
// the search term.
doSearch: function(matchFunc, unmatchFunc) {
var matches = this.matched();
var unmatched = this.unmatched();
matches.forEach(function(tab, i) {
matchFunc(tab, i);
});
unmatched.forEach(function(tab, i) {
unmatchFunc(tab, i);
});
}
};
// ##########
// Class: SearchEventHandlerClass
//
// A singleton class that handles all of the
// event handlers.
function SearchEventHandlerClass() {
this.init();
}
SearchEventHandlerClass.prototype = {
// ----------
// 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 () {
var self = this;
iQ("#searchbox")[0].focus();
iQ("#search").hide().click(function(event) {
if ( event.target.id != "searchbox")
hideSearch();
});
iQ("#searchbox").keyup(function() {
performSearch();
});
iQ("#searchbutton").mousedown(function() {
ensureSearchShown(null);
self.switchToInMode();
});
this.currentHandler = null;
this.switchToBeforeMode();
},
// ----------
// Function: beforeSearchKeyHandler
// Handles all keypresses before the search interface is brought up.
beforeSearchKeyHandler: function (event) {
// Only match reasonable text-like characters for quick search.
var key = String.fromCharCode(event.which);
// TODO: Also include funky chars. Bug 593904
if (!key.match(/[A-Z0-9]/) || event.altKey || event.ctrlKey || event.metaKey)
return;
// If we are already in an input field, allow typing as normal.
if (event.target.nodeName == "INPUT")
return;
this.switchToInMode();
ensureSearchShown(event);
},
// ----------
// Function: inSearchKeyHandler
// Handles all keypresses while search mode.
inSearchKeyHandler: function (event) {
var term = iQ("#searchbox").val();
if (event.which == event.DOM_VK_ESCAPE)
hideSearch(event);
if (event.which == event.DOM_VK_BACK_SPACE && term.length <= 1)
hideSearch(event);
var matches = (new TabMatcher(term)).matched();
if (event.which == event.DOM_VK_RETURN && matches.length > 0) {
matches[0].zoomIn();
hideSearch(event);
}
},
// ----------
// Function: switchToBeforeMode
// Make sure the event handlers are appropriate for
// the before-search mode.
switchToBeforeMode: function switchToBeforeMode() {
var self = this;
iQ(document).unbind("keydown", this.currentHandler);
this.currentHandler = function(event) self.beforeSearchKeyHandler(event);
iQ(document).keydown(self.currentHandler);
},
// ----------
// Function: switchToInMode
// Make sure the event handlers are appropriate for
// the in-search mode.
switchToInMode: function switchToInMode() {
var self = this;
iQ(document).unbind("keydown", this.currentHandler);
this.currentHandler = function(event) self.inSearchKeyHandler(event);
iQ(document).keydown(self.currentHandler);
}
};
var TabHandlers = {
onMatch: function(tab, index){
tab.setZ(1010);
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.
iQ(tab.canvasEl)
.unbind("mousedown", TabHandlers._hideHandler)
.unbind("mouseup", TabHandlers._showHandler);
iQ(tab.canvasEl)
.mousedown(TabHandlers._hideHandler)
.mouseup(TabHandlers._showHandler);
},
onUnmatch: function(tab, index){
// TODO: Set back as value per tab. bug 593902
tab.setZ(500);
tab.removeClass("notMainMatch");
iQ(tab.canvasEl)
.unbind("mousedown", TabHandlers._hideHandler)
.unbind("mouseup", TabHandlers._showHandler);
},
_hideHandler: function(event){
iQ("#search").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();
return;
}
iQ("#search").show();
iQ("#searchbox")[0].focus();
// Marshal the search.
setTimeout(performSearch, 0);
},
_mouseDownLocation: null
};
function hideSearch(event){
iQ("#searchbox").val("");
iQ("#search").hide();
iQ("#searchbutton").css({top: 0, left: 0, opacity:.8});
var mainWindow = gWindow.document.getElementById("main-window");
mainWindow.setAttribute("activetitlebarcolor", "#C4C4C4");
performSearch();
SearchEventHandler.switchToBeforeMode();
if (event){
event.preventDefault();
event.stopPropagation();
}
let newEvent = document.createEvent("Events");
newEvent.initEvent("tabviewsearchdisabled", false, false);
dispatchEvent(newEvent);
}
function performSearch() {
var matcher = new TabMatcher(iQ("#searchbox").val());
matcher.doSearch(TabHandlers.onMatch, TabHandlers.onUnmatch);
}
function ensureSearchShown(event){
var $search = iQ("#search");
var $searchbox = iQ("#searchbox");
iQ("#searchbutton").css({top: -78, left: -300, opacity: 1});
if ($search.css("display") == "none") {
$search.show();
var mainWindow = gWindow.document.getElementById("main-window");
mainWindow.setAttribute("activetitlebarcolor", "#717171");
// Marshal the focusing, otherwise you end up with
// a race condition where only sometimes would the
// first keystroke be registered by the search box.
// When you marshal it never gets registered, so we
// manually
setTimeout(function focusSearch() {
$searchbox[0].focus();
$searchbox[0].val = '0';
$searchbox.css({"z-index":"1015"});
if (event != null){
var keyCode = event.which + (event.shiftKey ? 0 : 32);
$searchbox.val(String.fromCharCode(keyCode));
}
}, 0);
let newEvent = document.createEvent("Events");
newEvent.initEvent("tabviewsearchenabled", false, false);
dispatchEvent(newEvent);
}
}
var SearchEventHandler = new SearchEventHandlerClass();
// Features to add:
// (1) Make sure this looks good on Windows. Bug 594429
// (2) Make sure that we don't put the matched tab over the search box. Bug 594433
// (3) Group all of the highlighted tabs into a group? Bug 594434

View File

@ -49,7 +49,6 @@
// Parameters:
// tab - a xul:tab
function TabItem(tab) {
Utils.assert(tab, "tab");
this.tab = tab;
@ -59,7 +58,7 @@ function TabItem(tab) {
// ___ set up div
var $div = iQ('<div>')
.addClass('tab')
.html("<div class='thumb'><div class='thumb-shadow'></div>" +
.html("<div class='thumb'>" +
"<img class='cached-thumb' style='display:none'/><canvas/></div>" +
"<div class='favicon'><img/></div>" +
"<span class='tab-title'>&nbsp;</span>"
@ -489,17 +488,14 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// Function: makeActive
// Updates this item to visually indicate that it's active.
makeActive: function TabItem_makeActive() {
iQ(this.container).find("canvas").addClass("focus");
iQ(this.container).find("img.cached-thumb").addClass("focus");
iQ(this.container).addClass("focus");
},
// ----------
// Function: makeDeactive
// Updates this item to visually indicate that it's not active.
makeDeactive: function TabItem_makeDeactive() {
iQ(this.container).find("canvas").removeClass("focus");
iQ(this.container).find("img.cached-thumb").removeClass("focus");
iQ(this.container).removeClass("focus");
},
// ----------
@ -509,6 +505,10 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// Parameters:
// isNewBlankTab - boolean indicates whether it is a newly opened blank tab.
zoomIn: function TabItem_zoomIn(isNewBlankTab) {
// don't allow zoom in if its group is hidden
if (this.parent && this.parent.hidden)
return;
var self = this;
var $tabEl = iQ(this.container);
var childHitResult = { shouldZoom: true };
@ -528,11 +528,7 @@ TabItem.prototype = Utils.extend(new Item(), new Subscribable(), {
.css(orig.css())
.removeClass("front");
// If it's not focused, the onFocus lsitener would handle it.
if (gBrowser.selectedTab == tab)
UI.onTabSelect(tab);
else
gBrowser.selectedTab = tab;
UI.goToTab(tab);
if (isNewBlankTab)
gWindow.gURLBar.focus();
@ -666,7 +662,7 @@ let TabItems = {
// When a tab is opened, create the TabItem
this._eventListeners["open"] = function(tab) {
if (tab.ownerDocument.defaultView != gWindow)
if (tab.ownerDocument.defaultView != gWindow || tab.pinned)
return;
self.link(tab);
@ -674,14 +670,14 @@ let TabItems = {
// When a tab's content is loaded, show the canvas and hide the cached data
// if necessary.
this._eventListeners["attrModified"] = function(tab) {
if (tab.ownerDocument.defaultView != gWindow)
if (tab.ownerDocument.defaultView != gWindow || tab.pinned)
return;
self.update(tab);
}
// When a tab is closed, unlink.
this._eventListeners["close"] = function(tab) {
if (tab.ownerDocument.defaultView != gWindow)
if (tab.ownerDocument.defaultView != gWindow || tab.pinned)
return;
self.unlink(tab);
@ -692,7 +688,7 @@ let TabItems = {
// For each tab, create the link.
AllTabs.tabs.forEach(function(tab) {
if (tab.ownerDocument.defaultView != gWindow)
if (tab.ownerDocument.defaultView != gWindow || tab.pinned)
return;
self.link(tab);
@ -725,6 +721,8 @@ let TabItems = {
update: function TabItems_update(tab) {
try {
Utils.assertThrow(tab, "tab");
Utils.assertThrow(!tab.pinned, "shouldn't be an app tab");
Utils.assertThrow(tab.tabItem, "should already be linked");
let shouldDefer = (
this.isPaintingPaused() ||
@ -766,7 +764,7 @@ let TabItems = {
// ___ icon
let iconUrl = tab.image;
if (iconUrl == null)
iconUrl = "chrome://mozapps/skin/places/defaultFavicon.png";
iconUrl = Utils.defaultFaviconURL;
if (iconUrl != tabItem.favEl.src)
tabItem.favEl.src = iconUrl;
@ -815,10 +813,11 @@ let TabItems = {
// ----------
// Function: link
// Takes in a xul:tab.
// Takes in a xul:tab, creates a TabItem for it and adds it to the scene.
link: function TabItems_link(tab){
try {
Utils.assertThrow(tab, "tab");
Utils.assertThrow(!tab.pinned, "shouldn't be an app tab");
Utils.assertThrow(!tab.tabItem, "shouldn't already be linked");
new TabItem(tab); // sets tab.tabItem to itself
} catch(e) {
@ -828,10 +827,11 @@ let TabItems = {
// ----------
// Function: unlink
// Takes in a xul:tab.
// Takes in a xul:tab and destroys the TabItem associated with it.
unlink: function TabItems_unlink(tab) {
try {
Utils.assertThrow(tab, "tab");
Utils.assertThrow(!tab.pinned, "shouldn't be an app tab");
Utils.assertThrow(tab.tabItem, "should already be linked");
this.unregister(tab.tabItem);

View File

@ -1,4 +1,4 @@
/* Platform-independent structural styling for
/* Platform-independent structural styling for
* <strike>Tab Candy</strike> Panorama
----------------------------------*/
@ -52,10 +52,6 @@ body {
height: 100%;
}
.thumb-shadow {
position: absolute;
}
.favicon {
position: absolute;
}
@ -73,8 +69,7 @@ body {
position: absolute;
}
.stacked .tab-title,
.stacked .thumb-shadow {
.stacked .tab-title {
display: none;
}
@ -87,16 +82,35 @@ body {
.front {
z-index: 999999 !important;
-moz-border-radius: 0 !important;
border-radius: 0 !important;
-moz-box-shadow: none !important;
-moz-transform: none !important;
image-rendering: -moz-crisp-edges;
}
/* Groups
----------------------------------*/
.groupItem {
position: absolute;
}
.appTabTray {
position: absolute;
}
/* Other Items
----------------------------------*/
.groupItem,
.undo {
position: absolute;
}
.undo .close {
display: inline-block;
position: relative;
}
.info-item {
position: absolute;
}
@ -171,3 +185,40 @@ body {
.iq-resizable-autohide .iq-resizable-handle {
display: none;
}
/* Exit button
----------------------------------*/
#exit-button {
position: absolute;
z-index: 1000;
}
/* Search
----------------------------------*/
#search{
position: absolute;
top: 0px;
left: 0px;
z-index: 1000;
}
#searchbox{
position: absolute;
right: 20px;
top: 20px;
z-index: 1050;
}
#actions{
position: absolute;
top: 100px;
right: 0px;
z-index:10;
}
#actions #searchbutton{
position: relative;
top: 0;
left: 0;
}

View File

@ -10,10 +10,18 @@
<body transparent="true">
<div id="content">
<input id="exit-button" type="image" onclick="UI.onExitButtonPressed();"
alt="" />
<div id="actions">
<input type="button" id="searchbutton"/>
</div>
<div id="bg" />
</div>
<div id="search">
<input id="searchbox" type="text"/>
</div>
<script type="text/javascript;version=1.8" src="tabview.js"></script>
</body>
</html>

View File

@ -25,6 +25,13 @@ XPCOMUtils.defineLazyGetter(this, "gTabViewFrame", function() {
return gWindow.document.getElementById("tab-view");
});
XPCOMUtils.defineLazyGetter(this, "tabviewBundle", function() {
return Services.strings.
createBundle("chrome://browser/locale/tabview.properties");
});
function tabviewString(name) tabviewBundle.GetStringFromName('tabview.' + name);
# NB: Certain files need to evaluate before others
#include iq.js
@ -36,3 +43,6 @@ XPCOMUtils.defineLazyGetter(this, "gTabViewFrame", function() {
#include trench.js
#include infoitems.js
#include ui.js
#include search.js

View File

@ -382,7 +382,7 @@ Trench.prototype = {
ruleOverlaps: function Trench_ruleOverlaps(position, range) {
return (this.position - this.radius < position &&
position < this.position + this.radius &&
this.activeRange.contains(range));
this.activeRange.overlaps(range));
},
//----------

View File

@ -106,9 +106,9 @@ let UI = {
// ___ Dev Menu
// This dev menu is not meant for shipping, nor is it of general
// interest, but we still need it for the time being. Change the
// false below to enable; just remember to change back before
// committing. Bug 586721 will track the ultimate removal.
// interest, but we still need it for the time being. Change the
// false below to enable; just remember to change back before
// committing. Bug 586721 will track the ultimate removal.
if (false)
this._addDevMenu();
@ -163,7 +163,7 @@ let UI = {
if (firstTime) {
var padding = 10;
var infoWidth = 350;
var infoHeight = 350;
var infoHeight = 232;
var pageBounds = Items.getPageBounds();
pageBounds.inset(padding, padding);
@ -187,15 +187,10 @@ let UI = {
});
// ___ make info item
let welcome = "How to organize your tabs";
let more = "";
let video = "http://videos-cdn.mozilla.net/firefox4beta/tabcandy_howto.webm";
var html =
"<div class='intro'>"
+ "<h1>" + welcome + "</h1>"
+ ( more && more.length ? "<div>" + more + "</div><br>" : "")
+ "<video src='" + video + "' "
+ "width='100%' preload controls>"
+ "<video src='" + video + "' width='100%' preload controls>"
+ "</div>";
box.left = box.right + padding;
@ -219,8 +214,10 @@ let UI = {
var observer = {
observe : function(subject, topic, data) {
if (topic == "quit-application-requested") {
if (self._isTabViewVisible())
if (self._isTabViewVisible()) {
GroupItems.removeHiddenGroups();
TabItems.saveAll(true);
}
self._save();
}
}
@ -358,6 +355,7 @@ let UI = {
if (!this._isTabViewVisible())
return;
GroupItems.removeHiddenGroups();
TabItems.pausePainting();
this._reorderTabsOnHide.forEach(function(groupItem) {
@ -471,6 +469,16 @@ let UI = {
AllTabs.unregister(name, this._eventListeners[name]);
},
// ----------
// Selects the given xul:tab in the browser.
goToTab: function UI_goToTab(xulTab) {
// If it's not focused, the onFocus listener would handle it.
if (gBrowser.selectedTab == xulTab)
this.onTabSelect(xulTab);
else
gBrowser.selectedTab = xulTab;
},
// ----------
// Function: onTabSelect
// Called when the user switches from one tab to another outside of the TabView UI.
@ -496,7 +504,7 @@ let UI = {
let oldItem = null;
let newItem = null;
if (currentTab && currentTab.tabItem)
oldItem = currentTab.tabItem;
if (tab && tab.tabItem) {
@ -567,7 +575,8 @@ let UI = {
function getClosestTabBy(norm) {
var centers =
[[item.bounds.center(), item] for each(item in TabItems.getItems())];
[[item.bounds.center(), item]
for each(item in TabItems.getItems()) if (!item.parent || !item.parent.hidden)];
var myCenter = self.getActiveTab().bounds.center();
var matches = centers
.filter(function(item){return norm(item[0], myCenter)})
@ -619,13 +628,13 @@ let UI = {
event.stopPropagation();
event.preventDefault();
}
} else if (event.keyCode == KeyEvent.DOM_VK_ESCAPE ||
} else if (event.keyCode == KeyEvent.DOM_VK_ESCAPE ||
event.keyCode == KeyEvent.DOM_VK_RETURN ||
event.keyCode == KeyEvent.DOM_VK_ENTER) {
let activeTab = self.getActiveTab();
let activeGroupItem = GroupItems.getActiveGroupItem();
if (activeGroupItem && activeGroupItem.expanded &&
if (activeGroupItem && activeGroupItem.expanded &&
event.keyCode == KeyEvent.DOM_VK_ESCAPE)
activeGroupItem.collapse();
else if (activeTab)
@ -875,6 +884,17 @@ let UI = {
this._save();
},
// ----------
// Function: onExitButtonPressed
// Exits TabView UI.
onExitButtonPressed: function() {
let activeTab = this.getActiveTab();
if (!activeTab)
activeTab = gBrowser.selectedTab.tabItem;
if (activeTab)
activeTab.zoomIn();
},
// ----------
// Function: _addDevMenu
// Fills out the "dev menu" in the TabView UI.
@ -927,11 +947,6 @@ let UI = {
code: function() {
self._saveAll();
}
}, {
name: "group sites",
code: function() {
self._arrangeBySite();
}
}];
var count = commands.length;
@ -995,56 +1010,6 @@ let UI = {
GroupItems.saveAll();
TabItems.saveAll();
},
// ----------
// Function: _arrangeBySite
// Blows away all existing groupItems and organizes the tabs into new groupItems based
// on domain.
_arrangeBySite: function UI__arrangeBySite() {
function putInGroupItem(set, key) {
var groupItem = GroupItems.getGroupItemWithTitle(key);
if (groupItem) {
set.forEach(function(el) {
groupItem.add(el);
});
} else
new GroupItem(set, { dontPush: true, dontArrange: true, title: key });
}
GroupItems.removeAll();
var groupItems = [];
var leftovers = [];
var items = TabItems.getItems();
items.forEach(function(item) {
var url = item.tab.linkedBrowser.currentURI.spec;
var domain = url.split('/')[2];
if (!domain)
leftovers.push(item.container);
else {
var domainParts = domain.split(".");
var mainDomain = domainParts[domainParts.length - 2];
if (groupItems[mainDomain])
groupItems[mainDomain].push(item.container);
else
groupItems[mainDomain] = [item.container];
}
});
for (key in groupItems) {
var set = groupItems[key];
if (set.length > 1) {
putInGroupItem(set, key);
} else
leftovers.push(set[0]);
}
if (leftovers.length)
putInGroupItem(leftovers, "mixed");
GroupItems.arrange();
},
};
// ----------

View File

@ -141,6 +141,7 @@ _BROWSER_FILES = \
browser_bug556061.js \
browser_bug559991.js \
browser_bug561623.js \
browser_bug561636.js \
browser_bug562649.js \
browser_bug563588.js \
browser_bug577121.js \

View File

@ -47,7 +47,7 @@ function runOneTest() {
});
}, true);
browser.contentWindow.location =
"chrome://mochikit/content/browser/browser/base/content/test/test_bug462673.html";
var rootDir = getRootDirectory(gTestPath);
browser.contentWindow.location = rootDir + "test_bug462673.html"
}, false);
}

View File

@ -1,7 +1,7 @@
function test() {
waitForExplicitFinish();
let testPath = "chrome://mochikit/content/browser/browser/base/content/test/";
let testPath = getRootDirectory(gTestPath);
let tab = gBrowser.addTab(testPath + "file_bug550565_popup.html");

View File

@ -4,9 +4,19 @@
const TESTROOT = "http://example.com/browser/toolkit/mozapps/extensions/test/xpinstall/";
const TESTROOT2 = "http://example.org/browser/toolkit/mozapps/extensions/test/xpinstall/";
const CHROMEROOT = "chrome://mochikit/content/browser/toolkit/mozapps/extensions/test/xpinstall/";
const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
var rootDir = getRootDirectory(gTestPath);
var path = rootDir.split('/');
var chromeName = path[0] + '//' + path[2];
var croot = chromeName + "/content/browser/toolkit/mozapps/extensions/test/xpinstall/";
var jar = getJar(croot);
if (jar) {
var tmpdir = extractJarToTmp(jar);
croot = 'file://' + tmpdir.path + '/';
}
const CHROMEROOT = croot;
var gApp = document.getElementById("bundle_brand").getString("brandShortName");
var gVersion = Services.appinfo.version;
@ -313,8 +323,11 @@ function test_url() {
function test_localfile() {
var cr = Components.classes["@mozilla.org/chrome/chrome-registry;1"]
.getService(Components.interfaces.nsIChromeRegistry);
var path = cr.convertChromeURL(makeURI(CHROMEROOT + "corrupt.xpi")).spec;
try {
var path = cr.convertChromeURL(makeURI(CHROMEROOT + "corrupt.xpi")).spec;
} catch (ex) {
var path = CHROMEROOT + "corrupt.xpi";
}
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(path);

View File

@ -43,9 +43,9 @@ function test() {
// -------------
// Test clean-up
function endTest() {
gBrowser.removeTab(tab);
FullZoom._applyPrefToSetting = oldAPTS;
FullZoom.onLocationChange = oldOLC;
gBrowser.removeTab(tab);
oldAPTS = null;
oldOLC = null;

View File

@ -0,0 +1,374 @@
var gInvalidFormPopup = document.getElementById('invalid-form-popup');
ok(gInvalidFormPopup,
"The browser should have a popup to show when a form is invalid");
function checkPopupShow()
{
ok(gInvalidFormPopup.state == 'showing' || gInvalidFormPopup.state == 'open',
"The invalid form popup should be shown");
}
function checkPopupHide()
{
ok(gInvalidFormPopup.state != 'showing' && gInvalidFormPopup.state != 'open',
"The invalid form popup should not be shown");
}
function checkPopupMessage(doc)
{
is(gInvalidFormPopup.firstChild.nodeValue,
doc.getElementById('i').validationMessage.substring(0,256),
"The panel should show the 256 first characters of the validationMessage");
}
let gObserver = {
QueryInterface : XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
notifyInvalidSubmit : function (aFormElement, aInvalidElements)
{
}
};
function test()
{
waitForExplicitFinish();
test1();
}
/**
* In this test, we check that no popup appears if the form is valid.
*/
function test1() {
let uri = "data:text/html,<html><body><iframe name='t'></iframe><form target='t' action='data:text/html,'><input><input id='s' type='submit'></form></body></html>";
let tab = gBrowser.addTab();
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
let doc = gBrowser.contentDocument;
doc.getElementById('s').click();
executeSoon(function() {
checkPopupHide();
// Clean-up
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
// Next test
executeSoon(test2);
});
}, true);
gBrowser.selectedTab = tab;
gBrowser.selectedTab.linkedBrowser.loadURI(uri);
}
/**
* In this test, we check that, when an invalid form is submitted,
* the invalid element is focused and a popup appears.
*/
function test2()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input required id='i'><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
let doc = gBrowser.contentDocument;
is(doc.activeElement, doc.getElementById('i'),
"First invalid element should be focused");
checkPopupShow();
checkPopupMessage(doc);
// Clean-up and next test.
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
executeSoon(test3);
}, false);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
}, true);
gBrowser.selectedTab = tab;
gBrowser.selectedTab.linkedBrowser.loadURI(uri);
}
/**
* In this test, we check that, when an invalid form is submitted,
* the first invalid element is focused and a popup appears.
*/
function test3()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input><input id='i' required><input required><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
let doc = gBrowser.contentDocument;
is(doc.activeElement, doc.getElementById('i'),
"First invalid element should be focused");
checkPopupShow();
checkPopupMessage(doc);
// Clean-up and next test.
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
executeSoon(test4);
}, false);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
}, true);
gBrowser.selectedTab = tab;
gBrowser.selectedTab.linkedBrowser.loadURI(uri);
}
/**
* In this test, we check that the validation message is correctly cut.
*/
function test4()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i'><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
let doc = gBrowser.contentDocument;
is(doc.activeElement, doc.getElementById('i'),
"First invalid element should be focused");
checkPopupShow();
checkPopupMessage(doc);
// Clean-up and next test.
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
executeSoon(test5);
}, false);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
let msg = "";
for (let i=0; i<50; ++i) {
msg += "abcde ";
}
// msg has 300 characters
gBrowser.contentDocument.getElementById('i').setCustomValidity(msg);
gBrowser.contentDocument.getElementById('s').click();
}, true);
gBrowser.selectedTab = tab;
gBrowser.selectedTab.linkedBrowser.loadURI(uri);
}
/**
* In this test, we check that, we can hide the popup by interacting with the
* invalid element.
*/
function test5()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
let doc = gBrowser.contentDocument;
is(doc.activeElement, doc.getElementById('i'),
"First invalid element should be focused");
checkPopupShow();
checkPopupMessage(doc);
EventUtils.synthesizeKey("a", {});
executeSoon(function () {
checkPopupHide();
// Clean-up and next test.
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
executeSoon(test6);
});
}, false);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
}, true);
gBrowser.selectedTab = tab;
gBrowser.selectedTab.linkedBrowser.loadURI(uri);
}
/**
* In this test, we check that we can hide the popup by blurring the invalid
* element.
*/
function test6()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
let doc = gBrowser.contentDocument;
is(doc.activeElement, doc.getElementById('i'),
"First invalid element should be focused");
checkPopupShow();
checkPopupMessage(doc);
doc.getElementById('i').blur();
executeSoon(function () {
checkPopupHide();
// Clean-up and next test.
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
executeSoon(test7);
});
}, false);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
}, true);
gBrowser.selectedTab = tab;
gBrowser.selectedTab.linkedBrowser.loadURI(uri);
}
/**
* In this test, we check that we can hide the popup by pressing TAB.
*/
function test7()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
let doc = gBrowser.contentDocument;
is(doc.activeElement, doc.getElementById('i'),
"First invalid element should be focused");
checkPopupShow();
checkPopupMessage(doc);
EventUtils.synthesizeKey("VK_TAB", {});
executeSoon(function () {
checkPopupHide();
// Clean-up and next test.
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
executeSoon(test8);
});
}, false);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
}, true);
gBrowser.selectedTab = tab;
gBrowser.selectedTab.linkedBrowser.loadURI(uri);
}
/**
* In this test, we check that the popup will hide if we move to another tab.
*/
function test8()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
gInvalidFormPopup.addEventListener("popupshown", function() {
gInvalidFormPopup.removeEventListener("popupshown", arguments.callee, false);
let doc = gBrowser.contentDocument;
is(doc.activeElement, doc.getElementById('i'),
"First invalid element should be focused");
checkPopupShow();
checkPopupMessage(doc);
// Create a new tab and move to it.
gBrowser.selectedTab = gBrowser.addTab("about:blank", {skipAnimation: true});
executeSoon(function() {
checkPopupHide();
// Clean-up and next test.
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
gBrowser.removeTab(gBrowser.selectedTab, {animate: false});
executeSoon(test9);
});
}, false);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.contentDocument.getElementById('s').click();
}, true);
gBrowser.selectedTab = tab;
gBrowser.selectedTab.linkedBrowser.loadURI(uri);
}
/**
* In this test, we check that nothing happen (no focus nor popup) if the
* invalid form is submitted in another tab than the current focused one
* (submitted in background).
*/
function test9()
{
let uri = "data:text/html,<iframe name='t'></iframe><form target='t' action='data:text/html,'><input id='i' required><input id='s' type='submit'></form>";
let tab = gBrowser.addTab();
gObserver.notifyInvalidSubmit = function() {
executeSoon(function() {
let doc = tab.linkedBrowser.contentDocument;
isnot(doc.activeElement, doc.getElementById('i'),
"We should not focus the invalid element when the form is submitted in background");
checkPopupHide();
// Clean-up
Services.obs.removeObserver(gObserver, "invalidformsubmit");
gObserver.notifyInvalidSubmit = function () {};
gBrowser.removeTab(tab, {animate: false});
// Next test
executeSoon(finish);
});
};
Services.obs.addObserver(gObserver, "invalidformsubmit", false);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
isnot(gBrowser.selectedTab, tab,
"This tab should have been loaded in background");
tab.linkedBrowser.contentDocument.getElementById('s').click();
}, true);
tab.linkedBrowser.loadURI(uri);
}

View File

@ -19,7 +19,8 @@ function test() {
event.currentTarget.removeEventListener("load", arguments.callee, true);
iconDiscovery();
}, true);
content.location = "chrome://mochikit/content/browser/browser/base/content/test/discovery.html";
var rootDir = getRootDirectory(gTestPath);
content.location = rootDir + "discovery.html";
}
var iconDiscoveryTests = [
@ -56,8 +57,9 @@ function iconDiscovery() {
var head = doc().getElementById("linkparent");
var link = doc().createElement("link");
var rootDir = getRootDirectory(gTestPath);
var rel = test.rel || "icon";
var href = test.href || "chrome://mochikit/content/browser/browser/base/content/test/moz.png";
var href = test.href || rootDir + "/moz.png";
var type = test.type || "image/png";
if (test.pass == undefined)
test.pass = true;

View File

@ -17,12 +17,11 @@ function test()
data : htmlString }
] ];
// set the valid attribute so dropping is allowed
var proxyicon = document.getElementById("page-proxy-favicon")
var oldstate = proxyicon.getAttribute("pageproxystate");
proxyicon.setAttribute("pageproxystate", "valid");
var dt = EventUtils.synthesizeDragStart(proxyicon, expected);
var oldstate = gURLBar.getAttribute("pageproxystate");
gURLBar.setAttribute("pageproxystate", "valid");
var dt = EventUtils.synthesizeDragStart(document.getElementById("identity-box"), expected);
is(dt, null, "drag on proxy icon");
proxyicon.setAttribute("pageproxystate", oldstate);
gURLBar.setAttribute("pageproxystate", oldstate);
// Now, the identity information panel is opened by the proxy icon click.
// We need to close it for next tests.
EventUtils.synthesizeKey("VK_ESCAPE", {}, window);

View File

@ -50,6 +50,8 @@ function runInspectorTests()
{
Services.obs.removeObserver(runInspectorTests, "inspector-opened", false);
Services.obs.addObserver(finishInspectorTests, "inspector-closed", false);
let iframe = document.getElementById("inspector-tree-iframe");
is(InspectorUI.treeIFrame, iframe, "Inspector IFrame matches");
ok(InspectorUI.inspecting, "Inspector is highlighting");
ok(InspectorUI.isTreePanelOpen, "Inspector Tree Panel is open");
ok(InspectorUI.isStylePanelOpen, "Inspector Style Panel is open");

View File

@ -4,8 +4,8 @@ function test() {
var tab = gBrowser.addTab();
gBrowser.selectedTab = tab;
tab.linkedBrowser.addEventListener("load", checkPageStyleMenu, true);
content.location =
"chrome://mochikit/content/browser/browser/base/content/test/page_style_sample.html";
let rootDir = getRootDirectory(gTestPath);
content.location = rootDir + "page_style_sample.html";
}
function checkPageStyleMenu() {

View File

@ -11,6 +11,23 @@ function indexTest(tab, expectedIndex, msg) {
is(index(tabs[tab]), expectedIndex, msg);
}
function PinUnpinHandler(tab, eventName) {
this.eventCount = 0;
var self = this;
tab.addEventListener(eventName, function() {
tab.removeEventListener(eventName, arguments.callee, true);
self.eventCount++;
}, true);
gBrowser.tabContainer.addEventListener(eventName, function(e) {
gBrowser.tabContainer.removeEventListener(eventName, arguments.callee, true);
if (e.originalTarget == tab) {
self.eventCount++;
}
}, true);
}
function test() {
tabs = [gBrowser.selectedTab, gBrowser.addTab(), gBrowser.addTab(), gBrowser.addTab()];
indexTest(0, 0);
@ -18,13 +35,17 @@ function test() {
indexTest(2, 2);
indexTest(3, 3);
var eh = new PinUnpinHandler(tabs[3], "TabPin");
gBrowser.pinTab(tabs[3]);
is(eh.eventCount, 2, "TabPin event should be fired");
indexTest(0, 1);
indexTest(1, 2);
indexTest(2, 3);
indexTest(3, 0);
eh = new PinUnpinHandler(tabs[1], "TabPin");
gBrowser.pinTab(tabs[1]);
is(eh.eventCount, 2, "TabPin event should be fired");
indexTest(0, 2);
indexTest(1, 1);
indexTest(2, 3);
@ -36,10 +57,14 @@ function test() {
gBrowser.moveTabTo(tabs[2], 0);
indexTest(2, 2, "shouldn't be able to mix a normal tab into pinned tabs");
eh = new PinUnpinHandler(tabs[1], "TabUnpin");
gBrowser.unpinTab(tabs[1]);
is(eh.eventCount, 2, "TabUnpin event should be fired");
indexTest(1, 1, "unpinning a tab should move a tab to the start of normal tabs");
eh = new PinUnpinHandler(tabs[3], "TabUnpin");
gBrowser.unpinTab(tabs[3]);
is(eh.eventCount, 2, "TabUnpin event should be fired");
indexTest(3, 0, "unpinning a tab should move a tab to the start of normal tabs");
gBrowser.removeTab(tabs[1]);

View File

@ -1,4 +1,5 @@
const gTestRoot = "chrome://mochikit/content/browser/browser/base/content/test/";
var rootDir = getRootDirectory(gTestPath);
const gTestRoot = rootDir;
var gTestBrowser = null;
var gNextTest = null;

View File

@ -4,7 +4,7 @@
<title>Test file for bug 550565.</title>
<!--Set a favicon; that's the whole point of this file.-->
<link rel="icon" href="chrome://mochikit/content/browser/browser/base/content/test/file_bug550565_favicon.ico">
<link rel="icon" href="file_bug550565_favicon.ico">
</head>
<body>
Test file for bug 550565.

View File

@ -47,6 +47,12 @@ _BROWSER_FILES = \
browser_tabview_launch.js \
browser_tabview_dragdrop.js \
browser_tabview_group.js \
browser_tabview_search.js \
browser_tabview_snapping.js \
browser_tabview_bug591706.js \
browser_tabview_apptabs.js \
browser_tabview_undo_group.js \
browser_tabview_exit_button.js \
$(NULL)
libs:: $(_BROWSER_FILES)

View File

@ -0,0 +1,89 @@
/* ***** 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 tabview group test.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Raymond Lee <raymond@appcoast.com>
* Ian Gilman <ian@iangilman.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 ***** */
function test() {
waitForExplicitFinish();
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
TabView.toggle();
}
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
ok(TabView.isVisible(), "Tab View is visible");
let contentWindow = document.getElementById("tab-view").contentWindow;
// establish initial state
is(contentWindow.GroupItems.groupItems.length, 1, "we start with one group (the default)");
is(gBrowser.tabs.length, 1, "we start with one tab");
// create an app tab
let appXulTab = gBrowser.loadOneTab("about:blank");
gBrowser.pinTab(appXulTab);
is(gBrowser.tabs.length, 2, "we now have two tabs");
// Create a group
let box = new contentWindow.Rect(20, 20, 180, 180);
let groupItem = new contentWindow.GroupItem([], { bounds: box });
is(contentWindow.GroupItems.groupItems.length, 2, "we now have two groups");
// find app tab in group and hit it
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
ok(!TabView.isVisible(), "Tab View is hidden because we clicked on the app tab");
// clean up
gBrowser.unpinTab(appXulTab);
gBrowser.removeTab(appXulTab);
is(gBrowser.tabs.length, 1, "we finish with one tab");
groupItem.close();
is(contentWindow.GroupItems.groupItems.length, 1, "we finish with one group");
ok(!TabView.isVisible(), "Tab View is not visible");
finish();
};
window.addEventListener("tabviewhidden", onTabViewHidden, false);
let appTabButtons = groupItem.$appTabTray[0].getElementsByTagName("img");
ok(appTabButtons.length == 1, "there is one app tab button");
EventUtils.sendMouseEvent({ type: "click" }, appTabButtons[0], contentWindow);
}

View File

@ -0,0 +1,142 @@
/* ***** 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 bug 591706 test.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Michael Yoshitaka Erlewine <mitcho@mitcho.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 ***** */
function test() {
waitForExplicitFinish();
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
if (TabView.isVisible())
onTabViewWindowLoaded();
else
TabView.show();
}
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
ok(TabView.isVisible(), "Tab View is visible");
let contentWindow = document.getElementById("tab-view").contentWindow;
let [originalTab] = gBrowser.visibleTabs;
// Create a first tab and orphan it
let firstTab = gBrowser.loadOneTab("about:blank#1", {inBackground: true});
let firstTabItem = firstTab.tabItem;
let currentGroup = contentWindow.GroupItems.getActiveGroupItem();
ok(currentGroup.getChildren().some(function(child) child == firstTabItem),"The first tab was made in the current group");
contentWindow.GroupItems.getActiveGroupItem().remove(firstTabItem);
ok(!currentGroup.getChildren().some(function(child) child == firstTabItem),"The first tab was orphaned");
// Create a group and make it active
let box = new contentWindow.Rect(10, 10, 300, 300);
let group = new contentWindow.GroupItem([], { bounds: box });
ok(group.isEmpty(), "This group is empty");
contentWindow.GroupItems.setActiveGroupItem(group);
// Create a second tab in this new group
let secondTab = gBrowser.loadOneTab("about:blank#2", {inBackground: true});
let secondTabItem = secondTab.tabItem;
ok(group.getChildren().some(function(child) child == secondTabItem),"The second tab was made in our new group");
is(group.getChildren().length, 1, "Only one tab in the first group");
isnot(firstTab.linkedBrowser.contentWindow.location, secondTab.linkedBrowser.contentWindow.location, "The two tabs must have different locations");
// Add the first tab to the group *programmatically*, without specifying a dropPos
group.add(firstTabItem);
is(group.getChildren().length, 2, "Two tabs in the group");
is(group.getChildren()[0].tab.linkedBrowser.contentWindow.location, secondTab.linkedBrowser.contentWindow.location, "The second tab was there first");
is(group.getChildren()[1].tab.linkedBrowser.contentWindow.location, firstTab.linkedBrowser.contentWindow.location, "The first tab was just added and went to the end of the line");
group.addSubscriber(group, "close", function() {
group.removeSubscriber(group, "close");
ok(group.isEmpty(), "The group is empty again");
is(contentWindow.GroupItems.getActiveGroupItem(), null, "The active group is gone");
contentWindow.GroupItems.setActiveGroupItem(currentGroup);
isnot(contentWindow.GroupItems.getActiveGroupItem(), null, "There is an active group");
is(gBrowser.tabs.length, 1, "There is only one tab left");
is(gBrowser.visibleTabs.length, 1, "There is also only one visible tab");
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
finish();
};
window.addEventListener("tabviewhidden", onTabViewHidden, false);
gBrowser.selectedTab = originalTab;
TabView.hide();
});
// Get rid of the group and its children
group.closeAll();
// close undo group
let closeButton = group.$undoContainer.find(".close");
EventUtils.sendMouseEvent(
{ type: "click" }, closeButton[0], contentWindow);
}
function simulateDragDrop(srcElement, offsetX, offsetY, contentWindow) {
// enter drag mode
let dataTransfer;
EventUtils.synthesizeMouse(
srcElement, 1, 1, { type: "mousedown" }, contentWindow);
event = contentWindow.document.createEvent("DragEvents");
event.initDragEvent(
"dragenter", true, true, contentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 1, null, dataTransfer);
srcElement.dispatchEvent(event);
// drag over
for (let i = 4; i >= 0; i--)
EventUtils.synthesizeMouse(
srcElement, Math.round(offsetX/5), Math.round(offsetY/4),
{ type: "mousemove" }, contentWindow);
event = contentWindow.document.createEvent("DragEvents");
event.initDragEvent(
"dragover", true, true, contentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 0, null, dataTransfer);
srcElement.dispatchEvent(event);
// drop
EventUtils.synthesizeMouse(srcElement, 0, 0, { type: "mouseup" }, contentWindow);
event = contentWindow.document.createEvent("DragEvents");
event.initDragEvent(
"drop", true, true, contentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 0, null, dataTransfer);
srcElement.dispatchEvent(event);
}

View File

@ -20,6 +20,7 @@
*
* Contributor(s):
* Raymond Lee <raymond@appcoast.com>
* Michael Yoshitaka Erlewine <mitcho@mitcho.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
@ -51,21 +52,15 @@ function onTabViewWindowLoaded() {
let [originalTab] = gBrowser.visibleTabs;
// create group one and two
let padding = 10;
let pageBounds = contentWindow.Items.getPageBounds();
pageBounds.inset(padding, padding);
let box = new contentWindow.Rect(pageBounds);
box.width = 300;
box.height = 300;
let groupOne = new contentWindow.GroupItem([], { bounds: box });
let boxOne = new contentWindow.Rect(20, 20, 300, 300);
let groupOne = new contentWindow.GroupItem([], { bounds: boxOne });
ok(groupOne.isEmpty(), "This group is empty");
let groupTwo = new contentWindow.GroupItem([], { bounds: box });
let boxTwo = new contentWindow.Rect(20, 400, 300, 300);
let groupTwo = new contentWindow.GroupItem([], { bounds: boxTwo });
groupOne.addSubscriber(groupOne, "tabAdded", function() {
groupOne.removeSubscriber(groupOne, "tabAdded");
groupOne.addSubscriber(groupOne, "childAdded", function() {
groupOne.removeSubscriber(groupOne, "childAdded");
groupTwo.newTab();
});
@ -93,67 +88,85 @@ function addTest(contentWindow, groupOneId, groupTwoId, originalTab) {
let groupTwo = contentWindow.GroupItems.groupItem(groupTwoId);
let groupOneTabItemCount = groupOne.getChildren().length;
let groupTwoTabItemCount = groupTwo.getChildren().length;
is(groupOneTabItemCount, 1, "GroupItem one has a tab");
is(groupTwoTabItemCount, 1, "GroupItem two has two tabs");
is(groupOneTabItemCount, 1, "GroupItem one has one tab");
is(groupTwoTabItemCount, 1, "GroupItem two has one tab as well");
let srcElement = groupOne.getChild(0).container;
ok(srcElement, "The source element exists");
let tabItem = groupOne.getChild(0);
ok(tabItem, "The tab item exists");
// calculate the offsets
let groupTwoRect = groupTwo.container.getBoundingClientRect();
let srcElementRect = srcElement.getBoundingClientRect();
let groupTwoRect = groupTwo.getBounds();
let groupTwoRectCenter = groupTwoRect.center();
let tabItemRect = tabItem.getBounds();
let tabItemRectCenter = tabItemRect.center();
let offsetX =
Math.round(groupTwoRect.left + groupTwoRect.width/5) - srcElementRect.left;
Math.round(groupTwoRectCenter.x - tabItemRectCenter.x);
let offsetY =
Math.round(groupTwoRect.top + groupTwoRect.height/5) - srcElementRect.top;
Math.round(groupTwoRectCenter.y - tabItemRectCenter.y);
simulateDragDrop(srcElement, offsetX, offsetY, contentWindow);
function endGame() {
groupTwo.removeSubscriber(groupTwo, "childAdded");
is(groupOne.getChildren().length, --groupOneTabItemCount,
"The number of children in group one is decreased by 1");
is(groupTwo.getChildren().length, ++groupTwoTabItemCount,
"The number of children in group two is increased by 1");
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
groupTwo.closeAll();
};
groupTwo.addSubscriber(groupTwo, "close", function() {
groupTwo.removeSubscriber(groupTwo, "close");
finish();
});
window.addEventListener("tabviewhidden", onTabViewHidden, false);
gBrowser.selectedTab = originalTab;
is(groupOne.getChildren().length, --groupOneTabItemCount,
"The number of children in group one is decreased by 1");
is(groupTwo.getChildren().length, ++groupTwoTabItemCount,
"The number of children in group two is increased by 1");
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
groupTwo.closeAll();
// close undo group
let closeButton = groupTwo.$undoContainer.find(".close");
EventUtils.sendMouseEvent(
{ type: "click" }, closeButton[0], contentWindow);
};
groupTwo.addSubscriber(groupTwo, "close", function() {
groupTwo.removeSubscriber(groupTwo, "close");
finish();
});
window.addEventListener("tabviewhidden", onTabViewHidden, false);
gBrowser.selectedTab = originalTab;
}
groupTwo.addSubscriber(groupTwo, "childAdded", endGame);
simulateDragDrop(tabItem.container, offsetX, offsetY, contentWindow);
}
function simulateDragDrop(srcElement, offsetX, offsetY, contentWindow) {
function simulateDragDrop(tabItem, offsetX, offsetY, contentWindow) {
// enter drag mode
let dataTransfer;
EventUtils.synthesizeMouse(
srcElement, 1, 1, { type: "mousedown" }, contentWindow);
tabItem, 1, 1, { type: "mousedown" }, contentWindow);
event = contentWindow.document.createEvent("DragEvents");
event.initDragEvent(
"dragenter", true, true, contentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 1, null, dataTransfer);
srcElement.dispatchEvent(event);
tabItem.dispatchEvent(event);
// drag over
for (let i = 4; i >= 0; i--)
EventUtils.synthesizeMouse(
srcElement, Math.round(offsetX/5), Math.round(offsetY/4),
{ type: "mousemove" }, contentWindow);
event = contentWindow.document.createEvent("DragEvents");
event.initDragEvent(
"dragover", true, true, contentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 0, null, dataTransfer);
srcElement.dispatchEvent(event);
if (offsetX || offsetY) {
let Ci = Components.interfaces;
let utils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils);
let rect = tabItem.getBoundingClientRect();
for (let i = 1; i <= 5; i++) {
let left = rect.left + Math.round(i * offsetX / 5);
let top = rect.top + Math.round(i * offsetY / 5);
utils.sendMouseEvent("mousemove", left, top, 0, 1, 0);
}
event = contentWindow.document.createEvent("DragEvents");
event.initDragEvent(
"dragover", true, true, contentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 0, null, dataTransfer);
tabItem.dispatchEvent(event);
}
// drop
EventUtils.synthesizeMouse(srcElement, 0, 0, { type: "mouseup" }, contentWindow);
EventUtils.synthesizeMouse(tabItem, 0, 0, { type: "mouseup" }, contentWindow);
event = contentWindow.document.createEvent("DragEvents");
event.initDragEvent(
"drop", true, true, contentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 0, null, dataTransfer);
srcElement.dispatchEvent(event);
tabItem.dispatchEvent(event);
}

View File

@ -0,0 +1,62 @@
/* ***** 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 tabview exit button test.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Raymond Lee <raymond@appcoast.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 ***** */
function test() {
waitForExplicitFinish();
let onTabViewShown = function() {
window.removeEventListener("tabviewshown", onTabViewShown, false);
ok(TabView.isVisible(), "Tab View is visible");
let contentWindow = document.getElementById("tab-view").contentWindow;
let button = contentWindow.document.getElementById("exit-button");
ok(button, "Exit button exists");
EventUtils.sendMouseEvent({ type: "click" }, button, contentWindow);
}
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
ok(!TabView.isVisible(), "Tab View is hidden");
finish();
}
window.addEventListener("tabviewshown", onTabViewShown, false);
window.addEventListener("tabviewhidden", onTabViewHidden, false);
TabView.toggle();
}

View File

@ -42,17 +42,28 @@ function test() {
TabView.toggle();
}
let originalGroupItem = null;
let originalTab = null;
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
ok(TabView.isVisible(), "Tab View is visible");
let contentWindow = document.getElementById("tab-view").contentWindow;
is(contentWindow.GroupItems.groupItems.length, 1, "There is one group item on startup");
originalGroupItem = contentWindow.GroupItems.groupItems[0];
is(originalGroupItem.getChildren().length, 1, "There should be one Tab Item in that group.");
contentWindow.GroupItems.setActiveGroupItem(originalGroupItem);
[originalTab] = gBrowser.visibleTabs;
testEmptyGroupItem(contentWindow);
}
function testEmptyGroupItem(contentWindow) {
let groupItemCount = contentWindow.GroupItems.groupItems.length;
// create empty group item
let emptyGroupItem = createEmptyGroupItem(contentWindow, 100);
ok(emptyGroupItem.isEmpty(), "This group is empty");
@ -81,17 +92,19 @@ function testGroupItemWithTabItem(contentWindow) {
let groupItem = createEmptyGroupItem(contentWindow, 200);
let tabItemCount = 0;
groupItem.addSubscriber(groupItem, "tabAdded", function() {
groupItem.removeSubscriber(groupItem, "tabAdded");
TabView.toggle();
});
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
is(groupItem.getChildren().length, ++tabItemCount,
"The number of children in new tab group is increased by 1");
ok(!TabView.isVisible(), "Tab View is hidden because we just opened a tab");
TabView.toggle();
};
let onTabViewShown = function() {
window.removeEventListener("tabviewshown", onTabViewShown, false);
let tabItem = groupItem.getChild(groupItem.getChildren().length - 1);
ok(tabItem, "Tab item exists");
@ -106,8 +119,28 @@ function testGroupItemWithTabItem(contentWindow) {
ok(tabItemClosed, "The tab item is closed");
is(groupItem.getChildren().length, --tabItemCount,
"The number of children in new tab group is decreased by 1");
ok(TabView.isVisible(), "Tab View is still shown");
finish();
// Now there should only be one tab left, so we need to hide TabView
// and go into that tab.
is(gBrowser.tabs.length, 1, "There is only one tab left");
let endGame = function() {
window.removeEventListener("tabviewhidden", endGame, false);
ok(!TabView.isVisible(), "Tab View is hidden");
finish();
};
window.addEventListener("tabviewhidden", endGame, false);
// after the last selected tabitem is closed, there would be not active
// tabitem on the UI so we set the active tabitem before toggling the
// visibility of tabview
let tabItems = contentWindow.TabItems.getItems();
ok(tabItems[0], "A tab item exists");
contentWindow.UI.setActiveTab(tabItems[0]);
TabView.toggle();
});
// remove the tab item. The code detects mousedown and mouseup so we stimulate here
@ -116,9 +149,12 @@ function testGroupItemWithTabItem(contentWindow) {
EventUtils.sendMouseEvent({ type: "mousedown" }, closeButton[0], contentWindow);
EventUtils.sendMouseEvent({ type: "mouseup" }, closeButton[0], contentWindow);
};
TabView.toggle();
};
window.addEventListener("tabviewhidden", onTabViewHidden, false);
window.addEventListener("tabviewshown", onTabViewShown, false);
// click on the + button
let newTabButton = groupItem.container.getElementsByClassName("newTabButton");
ok(newTabButton[0], "New tab button exists");

View File

@ -0,0 +1,131 @@
/* ***** 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 tabview search test.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Raymond Lee <raymond@appcoast.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 newTabs = [];
function test() {
waitForExplicitFinish();
let tabOne = gBrowser.addTab();
let tabTwo = gBrowser.addTab("http://mochi.test:8888/");
let browser = gBrowser.getBrowserForTab(tabTwo);
let onLoad = function() {
browser.removeEventListener("load", onLoad, true);
// show the tab view
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
ok(!TabView.isVisible(), "Tab View is hidden");
TabView.toggle();
}
browser.addEventListener("load", onLoad, true);
newTabs = [ tabOne, tabTwo ];
}
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
ok(TabView.isVisible(), "Tab View is visible");
let contentWindow = document.getElementById("tab-view").contentWindow;
let search = contentWindow.document.getElementById("search");
let searchButton = contentWindow.document.getElementById("searchbutton");
ok(searchButton, "Search button exists");
let onSearchEnabled = function() {
ok(search.style.display != "none", "Search is enabled");
contentWindow.removeEventListener(
"tabviewsearchenabled", onSearchEnabled, false);
searchTest(contentWindow);
}
contentWindow.addEventListener("tabviewsearchenabled", onSearchEnabled, false);
// enter search mode
EventUtils.sendMouseEvent({ type: "mousedown" }, searchButton, contentWindow);
}
function searchTest(contentWindow) {
let searchBox = contentWindow.document.getElementById("searchbox");
// get the titles of tabs.
let tabNames = [];
let tabItems = contentWindow.TabItems.getItems();
ok(tabItems.length == 3, "Have three tab items");
tabItems.forEach(function(tab) {
tabNames.push(tab.nameEl.innerHTML);
});
ok(tabNames[0] && tabNames[0].length > 2,
"The title of tab item is longer than 2 chars")
// empty string
searchBox.setAttribute("value", "");
ok(new contentWindow.TabMatcher(
searchBox.getAttribute("value")).matched().length == 0,
"Match nothing if it's an empty string");
// one char
searchBox.setAttribute("value", tabNames[0].charAt(0));
ok(new contentWindow.TabMatcher(
searchBox.getAttribute("value")).matched().length == 0,
"Match nothing if the length of search term is less than 2");
// the full title
searchBox.setAttribute("value", tabNames[2]);
ok(new contentWindow.TabMatcher(
searchBox.getAttribute("value")).matched().length == 1,
"Match something when the whole title exists");
// part of titled
searchBox.setAttribute("value", tabNames[0].substr(1));
contentWindow.performSearch();
ok(new contentWindow.TabMatcher(
searchBox.getAttribute("value")).matched().length == 2,
"Match something when a part of title exists");
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
ok(!TabView.isVisible(), "Tab View is hidden");
gBrowser.removeTab(newTabs[0]);
gBrowser.removeTab(newTabs[1]);
finish();
}
window.addEventListener("tabviewhidden", onTabViewHidden, false);
EventUtils.synthesizeKey("VK_ENTER", {});
}

View File

@ -0,0 +1,202 @@
/* ***** 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 Tab View snapping test.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Michael Yoshitaka Erlewine <mitcho@mitcho.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 ***** */
function test() {
waitForExplicitFinish();
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
if (TabView.isVisible())
onTabViewWindowLoaded();
else
TabView.show();
}
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
let contentWindow = document.getElementById("tab-view").contentWindow;
let [originalTab] = gBrowser.visibleTabs;
ok(TabView.isVisible(), "Tab View is visible");
is(contentWindow.GroupItems.groupItems.length, 1, "There is only one group");
let currentActiveGroup = contentWindow.GroupItems.getActiveGroupItem();
// Create a group
// Note: 150 x 150 should be larger than the minimum size for a group item
let firstBox = new contentWindow.Rect(80, 80, 160, 160);
let firstGroup = new contentWindow.GroupItem([], { bounds: firstBox });
ok(firstGroup.getBounds().equals(firstBox), "This group got its bounds");
// Create a second group
let secondBox = new contentWindow.Rect(80, 280, 160, 160);
let secondGroup = new contentWindow.GroupItem([], { bounds: secondBox });
ok(secondGroup.getBounds().equals(secondBox), "This second group got its bounds");
// A third group is created later, but multiple functions need access to it.
let thirdGroup = null;
is(secondGroup.getBounds().top - firstGroup.getBounds().bottom, 40,
"There's currently 40 px between the first group and second group");
let endGame = function() {
dump("END GAME!");
firstGroup.container.parentNode.removeChild(firstGroup.container);
firstGroup.close();
thirdGroup.container.parentNode.removeChild(thirdGroup.container);
thirdGroup.close();
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
ok(!TabView.isVisible(), "TabView is shown");
finish();
};
window.addEventListener("tabviewhidden", onTabViewHidden, false);
ok(TabView.isVisible(), "TabView is shown");
gBrowser.selectedTab = originalTab;
TabView.hide();
}
let continueWithPart2 = function() {
ok(firstGroup.getBounds().equals(firstBox), "The first group should still have its bounds");
// Create a third group
let thirdBox = new contentWindow.Rect(80, 280, 200, 160);
thirdGroup = new contentWindow.GroupItem([], { bounds: thirdBox });
ok(thirdGroup.getBounds().equals(thirdBox), "This third group got its bounds");
is(thirdGroup.getBounds().top - firstGroup.getBounds().bottom, 40,
"There's currently 40 px between the first group and third group");
// Just move it to the left and drop it.
checkSnap(thirdGroup, 0, 0, contentWindow, function(snapped){
ok(!snapped,"Offset: Just move it to the left and drop it");
// Move the second group up 10 px. It shouldn't snap yet.
checkSnap(thirdGroup, 0, -10, contentWindow, function(snapped){
ok(!snapped,"Offset: Moving up 10 should not snap");
// Move the second group up 10 px. It now should snap.
checkSnap(thirdGroup, 0, -10, contentWindow, function(snapped){
ok(snapped,"Offset: Moving up 10 again should snap!");
contentWindow.Utils.log('endGame!');
endGame();
});
});
});
};
let part1 = function() {
// Just pick it up and drop it.
checkSnap(secondGroup, 0, 0, contentWindow, function(snapped){
ok(!snapped,"Right under: Just pick it up and drop it");
// Move the second group up 10 px. It shouldn't snap yet.
checkSnap(secondGroup, 0, -10, contentWindow, function(snapped){
ok(!snapped,"Right under: Moving up 10 should not snap");
// Move the second group up 10 px. It now should snap.
checkSnap(secondGroup, 0, -10, contentWindow, function(snapped){
ok(snapped,"Right under: Moving up 10 again should snap!");
// cheat by removing the second group, so that we disappear immediately
secondGroup.container.parentNode.removeChild(secondGroup.container);
secondGroup.close();
continueWithPart2();
});
});
});
}
part1();
}
function simulateDragDrop(tabItem, offsetX, offsetY, contentWindow) {
// enter drag mode
let dataTransfer;
EventUtils.synthesizeMouse(
tabItem.container, 1, 1, { type: "mousedown" }, contentWindow);
event = contentWindow.document.createEvent("DragEvents");
event.initDragEvent(
"dragenter", true, true, contentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 1, null, dataTransfer);
tabItem.container.dispatchEvent(event);
// drag over
if (offsetX || offsetY) {
let Ci = Components.interfaces;
let utils = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor).
getInterface(Ci.nsIDOMWindowUtils);
let rect = tabItem.getBounds();
for (let i = 1; i <= 5; i++) {
let left = rect.left + 1 + Math.round(i * offsetX / 5);
let top = rect.top + 1 + Math.round(i * offsetY / 5);
utils.sendMouseEvent("mousemove", left, top, 0, 1, 0);
}
event = contentWindow.document.createEvent("DragEvents");
event.initDragEvent(
"dragover", true, true, contentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 0, null, dataTransfer);
tabItem.container.dispatchEvent(event);
}
// drop
EventUtils.synthesizeMouse(
tabItem.container, 0, 0, { type: "mouseup" }, contentWindow);
event = contentWindow.document.createEvent("DragEvents");
event.initDragEvent(
"drop", true, true, contentWindow, 0, 0, 0, 0, 0,
false, false, false, false, 0, null, dataTransfer);
tabItem.container.dispatchEvent(event);
}
function checkSnap(item, offsetX, offsetY, contentWindow, callback) {
let firstTop = item.getBounds().top;
let firstLeft = item.getBounds().left;
let onDrop = function() {
let snapped = false;
item.container.removeEventListener('drop', onDrop, false);
if (item.getBounds().top != firstTop + offsetY)
snapped = true;
if (item.getBounds().left != firstLeft + offsetX)
snapped = true;
callback(snapped);
};
item.container.addEventListener('drop', onDrop, false);
simulateDragDrop(item, offsetX, offsetY, contentWindow);
}

View File

@ -0,0 +1,168 @@
/* ***** 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 tabview undo group test.
*
* The Initial Developer of the Original Code is
* Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Raymond Lee <raymond@appcoast.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 ***** */
function test() {
waitForExplicitFinish();
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
TabView.toggle();
}
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
ok(TabView.isVisible(), "Tab View is visible");
let contentWindow = document.getElementById("tab-view").contentWindow;
// create a group item
let box = new contentWindow.Rect(20, 400, 300, 300);
let groupItem = new contentWindow.GroupItem([], { bounds: box });
// create a tab item in the new group
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
ok(!TabView.isVisible(), "Tab View is hidden because we just opened a tab");
// show tab view
TabView.toggle();
};
let onTabViewShown = function() {
window.removeEventListener("tabviewshown", onTabViewShown, false);
is(groupItem.getChildren().length, 1, "The new group has a tab item");
// start the tests
testUndoGroup(contentWindow, groupItem);
};
window.addEventListener("tabviewhidden", onTabViewHidden, false);
window.addEventListener("tabviewshown", onTabViewShown, false);
// click on the + button
let newTabButton = groupItem.container.getElementsByClassName("newTabButton");
ok(newTabButton[0], "New tab button exists");
EventUtils.sendMouseEvent({ type: "click" }, newTabButton[0], contentWindow);
}
function testUndoGroup(contentWindow, groupItem) {
groupItem.addSubscriber(groupItem, "groupHidden", function() {
groupItem.removeSubscriber(groupItem, "groupHidden");
// check the data of the group
let theGroupItem = contentWindow.GroupItems.groupItem(groupItem.id);
ok(theGroupItem, "The group item still exists");
is(theGroupItem.getChildren().length, 1,
"The tab item in the group still exists");
// check the visibility of the group element and undo element
is(theGroupItem.container.style.display, "none",
"The group element is hidden");
ok(theGroupItem.$undoContainer, "Undo container is avaliable");
EventUtils.sendMouseEvent(
{ type: "click" }, theGroupItem.$undoContainer[0], contentWindow);
});
groupItem.addSubscriber(groupItem, "groupShown", function() {
groupItem.removeSubscriber(groupItem, "groupShown");
// check the data of the group
let theGroupItem = contentWindow.GroupItems.groupItem(groupItem.id);
ok(theGroupItem, "The group item still exists");
is(theGroupItem.getChildren().length, 1,
"The tab item in the group still exists");
// check the visibility of the group element and undo element
is(theGroupItem.container.style.display, "", "The group element is visible");
ok(!theGroupItem.$undoContainer, "Undo container is not avaliable");
// start the next test
testCloseUndoGroup(contentWindow, groupItem);
});
let closeButton = groupItem.container.getElementsByClassName("close");
ok(closeButton, "Group item close button exists");
EventUtils.sendMouseEvent({ type: "click" }, closeButton[0], contentWindow);
}
function testCloseUndoGroup(contentWindow, groupItem) {
groupItem.addSubscriber(groupItem, "groupHidden", function() {
groupItem.removeSubscriber(groupItem, "groupHidden");
// check the data of the group
let theGroupItem = contentWindow.GroupItems.groupItem(groupItem.id);
ok(theGroupItem, "The group item still exists");
is(theGroupItem.getChildren().length, 1,
"The tab item in the group still exists");
// check the visibility of the group element and undo element
is(theGroupItem.container.style.display, "none",
"The group element is hidden");
ok(theGroupItem.$undoContainer, "Undo container is avaliable");
// click on close
let closeButton = theGroupItem.$undoContainer.find(".close");
EventUtils.sendMouseEvent(
{ type: "click" }, closeButton[0], contentWindow);
});
groupItem.addSubscriber(groupItem, "close", function() {
groupItem.removeSubscriber(groupItem, "close");
let theGroupItem = contentWindow.GroupItems.groupItem(groupItem.id);
ok(!theGroupItem, "The group item doesn't exists");
let endGame = function() {
window.removeEventListener("tabviewhidden", endGame, false);
ok(!TabView.isVisible(), "Tab View is hidden");
finish();
};
window.addEventListener("tabviewhidden", endGame, false);
// after the last selected tabitem is closed, there would be not active
// tabitem on the UI so we set the active tabitem before toggling the
// visibility of tabview
let tabItems = contentWindow.TabItems.getItems();
ok(tabItems[0], "A tab item exists");
contentWindow.UI.setActiveTab(tabItems[0]);
TabView.toggle();
});
let closeButton = groupItem.container.getElementsByClassName("close");
ok(closeButton, "Group item close button exists");
EventUtils.sendMouseEvent({ type: "click" }, closeButton[0], contentWindow);
}

View File

@ -1,2 +1,2 @@
# Do NOT localize or otherwise change these values
browser.startup.homepage=http://www.mozilla.org/projects/minefield/
browser.startup.homepage=about:home

View File

@ -1,3 +1,2 @@
# Do NOT localize or otherwise change these values
browser.startup.homepage=http://www.mozilla.org/projects/devpreview/
browser.startup.homepage=about:home

View File

@ -29,5 +29,5 @@ category command-line-validator b-browser @mozilla.org/browser/clh;1 application
component {eab9012e-5f74-4cbc-b2b5-a590235513cc} nsBrowserGlue.js
contract @mozilla.org/browser/browserglue;1 {eab9012e-5f74-4cbc-b2b5-a590235513cc}
category app-startup nsBrowserGlue service,@mozilla.org/browser/browserglue;1
component {C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5} nsBrowserGlue.js
contract @mozilla.org/geolocation/prompt;1 {C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5}
component {d8903bf6-68d5-4e97-bcd1-e4d3012f721a} nsBrowserGlue.js
contract @mozilla.org/content-permission/prompt;1 {d8903bf6-68d5-4e97-bcd1-e4d3012f721a}

View File

@ -1323,15 +1323,20 @@ BrowserGlue.prototype = {
_xpcom_factory: BrowserGlueServiceFactory,
}
function GeolocationPrompt() {}
function ContentPermissionPrompt() {}
GeolocationPrompt.prototype = {
classID: Components.ID("{C6E8C44D-9F39-4AF7-BCC0-76E38A8310F5}"),
ContentPermissionPrompt.prototype = {
classID: Components.ID("{d8903bf6-68d5-4e97-bcd1-e4d3012f721a}"),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIGeolocationPrompt]),
QueryInterface: XPCOMUtils.generateQI([Ci.nsIContentPermissionPrompt]),
prompt: function GP_prompt(request) {
var requestingURI = request.requestingURI;
prompt: function CPP_prompt(request) {
if (request.type != "geolocation") {
return;
}
var requestingURI = request.uri;
// Ignore requests from non-nsIStandardURLs
if (!(requestingURI instanceof Ci.nsIStandardURL))
@ -1382,7 +1387,7 @@ GeolocationPrompt.prototype = {
// Different message/options if it is a local file
if (requestingURI.schemeIs("file")) {
message = browserBundle.formatStringFromName("geolocation.fileWantsToKnow",
[request.requestingURI.path], 1);
[requestingURI.path], 1);
} else {
message = browserBundle.formatStringFromName("geolocation.siteWantsToKnow",
[requestingURI.host], 1);
@ -1412,7 +1417,7 @@ GeolocationPrompt.prototype = {
}
}
var requestingWindow = request.requestingWindow.top;
var requestingWindow = request.window.top;
var chromeWin = getChromeWindow(requestingWindow).wrappedJSObject;
var browser = chromeWin.gBrowser.getBrowserForDocument(requestingWindow.document);
@ -1421,5 +1426,5 @@ GeolocationPrompt.prototype = {
}
};
var components = [BrowserGlue, GeolocationPrompt];
var components = [BrowserGlue, ContentPermissionPrompt];
var NSGetFactory = XPCOMUtils.generateNSGetFactory(components);

View File

@ -39,6 +39,7 @@
// Load DownloadUtils module for convertByteUnits
Components.utils.import("resource://gre/modules/DownloadUtils.jsm");
Components.utils.import("resource://gre/modules/Services.jsm");
var gAdvancedPane = {
_inited: false,
@ -170,6 +171,17 @@ var gAdvancedPane = {
} catch (e) { }
},
/**
* When the user toggles the layers.accelerate-none pref,
* sync its new value to the gfx.direct2d.disabled pref too.
*/
updateHardwareAcceleration: function()
{
#ifdef XP_WIN
var pref = document.getElementById("layers.accelerate-none");
Services.prefs.setBoolPref("gfx.direct2d.disabled", !pref.value);
#endif
},
// NETWORK TAB

View File

@ -68,9 +68,8 @@
<preference id="general.autoScroll" name="general.autoScroll" type="bool"/>
<preference id="general.smoothScroll" name="general.smoothScroll" type="bool"/>
#ifdef XP_WIN
<preference id="gfx.direct2d.disabled" name="gfx.direct2d.disabled" type="bool" inverted="true"/>
#endif
<preference id="layers.accelerate-none" name="layers.accelerate-none" type="bool" inverted="true"
onchange="gAdvancedPane.updateHardwareAcceleration()"/>
<preference id="layout.spellcheckDefault" name="layout.spellcheckDefault" type="int"/>
#ifdef HAVE_SHELL_SERVICE
@ -177,12 +176,10 @@
label="&useSmoothScrolling.label;"
accesskey="&useSmoothScrolling.accesskey;"
preference="general.smoothScroll"/>
#ifdef XP_WIN
<checkbox id="allowHWAccel"
label="&allowHWAccel.label;"
accesskey="&allowHWAccel.accesskey;"
preference="gfx.direct2d.disabled"/>
#endif
preference="layers.accelerate-none"/>
<checkbox id="checkSpelling"
label="&checkSpelling.label;"
accesskey="&checkSpelling.accesskey;"

View File

@ -38,7 +38,14 @@
function test() {
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://mochikit/content/browser/browser/components/preferences/tests/privacypane_tests.js", this);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path;
}
loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
run_test_subset([
test_pane_visibility,

View File

@ -38,7 +38,14 @@
function test() {
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://mochikit/content/browser/browser/components/preferences/tests/privacypane_tests.js", this);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path;
}
loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
run_test_subset([
test_historymode_retention("remember", undefined),

View File

@ -38,7 +38,13 @@
function test() {
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://mochikit/content/browser/browser/components/preferences/tests/privacypane_tests.js", this);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path;
}
loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
run_test_subset([
test_custom_retention("rememberHistory", "remember"),

View File

@ -38,7 +38,13 @@
function test() {
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://mochikit/content/browser/browser/components/preferences/tests/privacypane_tests.js", this);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path;
}
loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
run_test_subset([
test_custom_retention("acceptCookies", "remember"),

View File

@ -38,7 +38,13 @@
function test() {
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://mochikit/content/browser/browser/components/preferences/tests/privacypane_tests.js", this);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path;
}
loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
run_test_subset([
test_locbar_suggestion_retention(-1, undefined),

View File

@ -38,7 +38,13 @@
function test() {
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://mochikit/content/browser/browser/components/preferences/tests/privacypane_tests.js", this);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path;
}
loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
run_test_subset([
test_privatebrowsing_toggle,

View File

@ -38,7 +38,13 @@
function test() {
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://mochikit/content/browser/browser/components/preferences/tests/privacypane_tests.js", this);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path;
}
loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
run_test_subset([
test_privatebrowsing_ui,

View File

@ -37,7 +37,13 @@
function test() {
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
loader.loadSubScript("chrome://mochikit/content/browser/browser/components/preferences/tests/privacypane_tests.js", this);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path;
}
loader.loadSubScript(rootDir + "/privacypane_tests.js", this);
run_test_subset([
// history mode should be initialized to remember

View File

@ -43,7 +43,7 @@
* - and allows to restore everything into one window.
*/
[scriptable, uuid(c0b185e7-0d21-46ac-8eee-7b5065ee7ecd)]
[scriptable, uuid(e7bb7828-0e32-4995-a848-4aa35df603c7)]
interface nsISessionStartup: nsISupports
{
// Get session state as string
@ -55,13 +55,18 @@ interface nsISessionStartup: nsISupports
boolean doRestore();
/**
* What type of session we're restoring. If we have a session, we're
* either restoring state from a crash or restoring state that the user
* requested we save on shutdown.
* What type of session we're restoring.
* NO_SESSION There is no data available from the previous session
* RECOVER_SESSION The last session crashed. It will either be restored or
* about:sessionrestore will be shown.
* RESUME_SESSION The previous session should be restored at startup
* DEFER_SESSION The previous session is fine, but it shouldn't be restored
* without explicit action (with the exception of pinned tabs)
*/
const unsigned long NO_SESSION = 0;
const unsigned long RECOVER_SESSION = 1;
const unsigned long RESUME_SESSION = 2;
const unsigned long DEFER_SESSION = 3;
readonly attribute unsigned long sessionType;
};

View File

@ -59,7 +59,7 @@ interface nsIDOMNode;
* |gBrowser.tabContainer| such as e.g. |gBrowser.selectedTab|.
*/
[scriptable, uuid(70592a0d-87d3-459c-8db7-dcb8d47af78e)]
[scriptable, uuid(59bfaf00-e3d8-4728-b4f0-cc0b9dfb4806)]
interface nsISessionStore : nsISupports
{
/**
@ -67,6 +67,24 @@ interface nsISessionStore : nsISupports
*/
void init(in nsIDOMWindow aWindow);
/**
* Is it possible to restore the previous session. Will always be false when
* in Private Browsing mode.
*/
attribute boolean canRestoreLastSession;
/**
* Restore the previous session if possible. This will not overwrite the
* current session. Instead the previous session will be merged into the
* current session. Current windows will be reused if they were windows that
* pinned tabs were previously restored into. New windows will be opened as
* needed.
*
* Note: This will throw if there is no previous state to restore. Check with
* canRestoreLastSession first to avoid thrown errors.
*/
void restoreLastSession();
/**
* Get the current browsing state.
* @returns a JSON string representing the session state.

View File

@ -152,10 +152,12 @@ SessionStartup.prototype = {
this._sessionType = Ci.nsISessionStartup.RECOVER_SESSION;
else if (!lastSessionCrashed && doResumeSession)
this._sessionType = Ci.nsISessionStartup.RESUME_SESSION;
else if (initialState)
this._sessionType = Ci.nsISessionStartup.DEFER_SESSION;
else
this._iniString = null; // reset the state string
if (this._sessionType != Ci.nsISessionStartup.NO_SESSION) {
if (this.doRestore()) {
// wait for the first browser window to open
// Don't reset the initial window's default args (i.e. the home page(s))
@ -252,7 +254,8 @@ SessionStartup.prototype = {
* @returns bool
*/
doRestore: function sss_doRestore() {
return this._sessionType != Ci.nsISessionStartup.NO_SESSION;
return this._sessionType == Ci.nsISessionStartup.RECOVER_SESSION ||
this._sessionType == Ci.nsISessionStartup.RESUME_SESSION;
},
/**

View File

@ -201,6 +201,23 @@ SessionStoreService.prototype = {
// whether the last window was closed and should be restored
_restoreLastWindow: false,
// The state from the previous session (after restoring pinned tabs)
_lastSessionState: null,
/* ........ Public Getters .............. */
get canRestoreLastSession() {
// Always disallow restoring the previous session when in private browsing
return this._lastSessionState && !this._inPrivateBrowsing;
},
set canRestoreLastSession(val) {
// Cheat a bit; only allow false.
if (val)
return;
this._lastSessionState = null;
},
/* ........ Global Event Handlers .............. */
/**
@ -250,40 +267,54 @@ SessionStoreService.prototype = {
// get string containing session state
var iniString;
var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
getService(Ci.nsISessionStartup);
try {
var ss = Cc["@mozilla.org/browser/sessionstartup;1"].
getService(Ci.nsISessionStartup);
if (ss.doRestore())
if (ss.doRestore() ||
ss.sessionType == Ci.nsISessionStartup.DEFER_SESSION)
iniString = ss.state;
}
catch(ex) { dump(ex + "\n"); } // no state to restore, which is ok
if (iniString) {
try {
// parse the session state into JS objects
this._initialState = JSON.parse(iniString);
let lastSessionCrashed =
this._initialState.session && this._initialState.session.state &&
this._initialState.session.state == STATE_RUNNING_STR;
if (lastSessionCrashed) {
this._recentCrashes = (this._initialState.session &&
this._initialState.session.recentCrashes || 0) + 1;
if (this._needsRestorePage(this._initialState, this._recentCrashes)) {
// replace the crashed session with a restore-page-only session
let pageData = {
url: "about:sessionrestore",
formdata: { "#sessionData": iniString }
};
this._initialState = { windows: [{ tabs: [{ entries: [pageData] }] }] };
}
// If we're doing a DEFERRED session, then we want to pull pinned tabs
// out so they can be restored.
if (ss.sessionType == Ci.nsISessionStartup.DEFER_SESSION) {
let [iniState, remainingState] = this._prepDataForDeferredRestore(iniString);
// If we have a iniState with windows, that means that we have windows
// with app tabs to restore.
if (iniState.windows.length)
this._initialState = iniState;
if (remainingState.windows.length)
this._lastSessionState = remainingState;
}
else {
// parse the session state into JS objects
this._initialState = JSON.parse(iniString);
let lastSessionCrashed =
this._initialState.session && this._initialState.session.state &&
this._initialState.session.state == STATE_RUNNING_STR;
if (lastSessionCrashed) {
this._recentCrashes = (this._initialState.session &&
this._initialState.session.recentCrashes || 0) + 1;
if (this._needsRestorePage(this._initialState, this._recentCrashes)) {
// replace the crashed session with a restore-page-only session
let pageData = {
url: "about:sessionrestore",
formdata: { "#sessionData": iniString }
};
this._initialState = { windows: [{ tabs: [{ entries: [pageData] }] }] };
}
}
// make sure that at least the first window doesn't have anything hidden
delete this._initialState.windows[0].hidden;
// Since nothing is hidden in the first window, it cannot be a popup
delete this._initialState.windows[0].isPopup;
}
// make sure that at least the first window doesn't have anything hidden
delete this._initialState.windows[0].hidden;
// Since nothing is hidden in the first window, it cannot be a popup
delete this._initialState.windows[0].isPopup;
}
catch (ex) { debug("The session file is invalid: " + ex); }
}
@ -317,11 +348,6 @@ SessionStoreService.prototype = {
// save all data for session resuming
this.saveState(true);
if (!this._doResumeSession()) {
// discard all session related data
this._clearDisk();
}
// Make sure to break our cycle with the save timer
if (this._saveTimer) {
this._saveTimer.cancel();
@ -1134,6 +1160,78 @@ SessionStoreService.prototype = {
this.saveStateDelayed();
},
/**
* Restores the session state stored in _lastSessionState. This will attempt
* to merge data into the current session. If a window was opened at startup
* with pinned tab(s), then the remaining data from the previous session for
* that window will be opened into that winddow. Otherwise new windows will
* be opened.
*/
restoreLastSession: function sss_restoreLastSession() {
// Use the public getter since it also checks PB mode
if (!this.canRestoreLastSession)
throw (Components.returnCode = Cr.NS_ERROR_FAILURE);
// First collect each window with its id...
let windows = {};
this._forEachBrowserWindow(function(aWindow) {
if (aWindow.__SS_lastSessionWindowID)
windows[aWindow.__SS_lastSessionWindowID] = aWindow;
});
let lastSessionState = this._lastSessionState;
// This shouldn't ever be the case...
if (!lastSessionState.windows.length)
throw (Components.returnCode = Cr.NS_ERROR_UNEXPECTED);
// We're technically doing a restore, so set things up so we send the
// notification when we're done. We want to send "sessionstore-browser-state-restored".
this._restoreCount = lastSessionState.windows.length;
this._browserSetState = true;
// Restore into windows or open new ones as needed.
for (let i = 0; i < lastSessionState.windows.length; i++) {
let winState = lastSessionState.windows[i];
let lastSessionWindowID = winState.__lastSessionWindowID;
// delete lastSessionWindowID so we don't add that to the window again
delete winState.__lastSessionWindowID;
// Look to see if this window is already open...
if (windows[lastSessionWindowID]) {
// Since we're not overwriting existing tabs, we want to merge _closedTabs,
// putting existing ones first. Then make sure we're respecting the max pref.
if (winState._closedTabs && winState._closedTabs.length) {
let curWinState = this._windows[windows[lastSessionWindowID].__SSi];
curWinState._closedTabs = curWinState._closedTabs.concat(winState._closedTabs);
curWinState._closedTabs.splice(this._prefBranch.getIntPref("sessionstore.max_tabs_undo"));
}
// Restore into that window - pretend it's a followup since we'll already
// have a focused window.
//XXXzpao This is going to merge extData together (taking what was in
// winState over what is in the window already), so this is going
// to have an effect on Tab Candy.
// Bug 588217 should make this go away by merging the group data.
this.restoreWindow(windows[lastSessionWindowID], { windows: [winState] },
false, true);
}
else {
this._openWindowWithState({ windows: [winState] });
}
}
// Merge closed windows from this session with ones from last session
if (lastSessionState._closedWindows) {
this._closedWindows = this._closedWindows.concat(lastSessionState._closedWindows);
this._capClosedWindows();
}
// Set recent crashes
this._recentCrashes = lastSessionState.session &&
lastSessionState.session.recentCrashes || 0;
this._lastSessionState = null;
},
/* ........ Saving Functionality .............. */
/**
@ -1191,9 +1289,11 @@ SessionStoreService.prototype = {
tabData.index = history.index + 1;
}
else if (history && history.count > 0) {
for (var j = 0; j < history.count; j++)
tabData.entries.push(this._serializeHistoryEntry(history.getEntryAtIndex(j, false),
aFullData));
for (var j = 0; j < history.count; j++) {
let entry = this._serializeHistoryEntry(history.getEntryAtIndex(j, false),
aFullData, aTab.pinned);
tabData.entries.push(entry);
}
tabData.index = history.index + 1;
// make sure not to cache privacy sensitive data which shouldn't get out
@ -1242,7 +1342,8 @@ SessionStoreService.prototype = {
delete tabData.extData;
if (history && browser.docShell instanceof Ci.nsIDocShell)
this._serializeSessionStorage(tabData, history, browser.docShell, aFullData);
this._serializeSessionStorage(tabData, history, browser.docShell, aFullData,
aTab.pinned);
return tabData;
},
@ -1254,9 +1355,12 @@ SessionStoreService.prototype = {
* nsISHEntry instance
* @param aFullData
* always return privacy sensitive data (use with care)
* @param aIsPinned
* the tab is pinned and should be treated differently for privacy
* @returns object
*/
_serializeHistoryEntry: function sss_serializeHistoryEntry(aEntry, aFullData) {
_serializeHistoryEntry:
function sss_serializeHistoryEntry(aEntry, aFullData, aIsPinned) {
var entry = { url: aEntry.URI.spec };
if (aEntry.title && aEntry.title != entry.url) {
@ -1291,8 +1395,8 @@ SessionStoreService.prototype = {
try {
var prefPostdata = this._prefBranch.getIntPref("sessionstore.postdata");
if (aEntry.postData && (aFullData ||
prefPostdata && this._checkPrivacyLevel(aEntry.URI.schemeIs("https")))) {
if (aEntry.postData && (aFullData || prefPostdata &&
this._checkPrivacyLevel(aEntry.URI.schemeIs("https"), aIsPinned))) {
aEntry.postData.QueryInterface(Ci.nsISeekableStream).
seek(Ci.nsISeekableStream.NS_SEEK_SET, 0);
var stream = Cc["@mozilla.org/binaryinputstream;1"].
@ -1355,7 +1459,8 @@ SessionStoreService.prototype = {
for (var i = 0; i < aEntry.childCount; i++) {
var child = aEntry.GetChildAt(i);
if (child) {
entry.children.push(this._serializeHistoryEntry(child, aFullData));
entry.children.push(this._serializeHistoryEntry(child, aFullData,
aIsPinned));
}
else { // to maintain the correct frame order, insert a dummy entry
entry.children.push({ url: "about:blank" });
@ -1381,9 +1486,11 @@ SessionStoreService.prototype = {
* That tab's docshell (containing the sessionStorage)
* @param aFullData
* always return privacy sensitive data (use with care)
* @param aIsPinned
* the tab is pinned and should be treated differently for privacy
*/
_serializeSessionStorage:
function sss_serializeSessionStorage(aTabData, aHistory, aDocShell, aFullData) {
function sss_serializeSessionStorage(aTabData, aHistory, aDocShell, aFullData, aIsPinned) {
let storageData = {};
let hasContent = false;
@ -1396,7 +1503,8 @@ SessionStoreService.prototype = {
domain = uri.prePath;
}
catch (ex) { /* this throws for host-less URIs (such as about: or jar:) */ }
if (storageData[domain] || !(aFullData || this._checkPrivacyLevel(uri.schemeIs("https"))))
if (storageData[domain] ||
!(aFullData || this._checkPrivacyLevel(uri.schemeIs("https"), aIsPinned)))
continue;
let storage, storageItemCount = 0;
@ -1479,7 +1587,8 @@ SessionStoreService.prototype = {
this._updateTextAndScrollDataForFrame(aWindow, aBrowser.contentWindow,
aTabData.entries[tabIndex],
!aTabData._formDataSaved, aFullData);
!aTabData._formDataSaved, aFullData,
!!aTabData.pinned);
aTabData._formDataSaved = true;
if (aBrowser.currentURI.spec == "about:config")
aTabData.entries[tabIndex].formdata = {
@ -1500,18 +1609,21 @@ SessionStoreService.prototype = {
* update all form data for this tab
* @param aFullData
* always return privacy sensitive data (use with care)
* @param aIsPinned
* the tab is pinned and should be treated differently for privacy
*/
_updateTextAndScrollDataForFrame:
function sss_updateTextAndScrollDataForFrame(aWindow, aContent, aData,
aUpdateFormData, aFullData) {
aUpdateFormData, aFullData, aIsPinned) {
for (var i = 0; i < aContent.frames.length; i++) {
if (aData.children && aData.children[i])
this._updateTextAndScrollDataForFrame(aWindow, aContent.frames[i],
aData.children[i], aUpdateFormData, aFullData);
aData.children[i], aUpdateFormData,
aFullData, aIsPinned);
}
var isHTTPS = this._getURIFromString((aContent.parent || aContent).
document.location.href).schemeIs("https");
if (aFullData || this._checkPrivacyLevel(isHTTPS) ||
if (aFullData || this._checkPrivacyLevel(isHTTPS, aIsPinned) ||
aContent.top.document.location.href == "about:sessionrestore") {
if (aFullData || aUpdateFormData) {
let formData = this._collectFormDataForFrame(aContent.document);
@ -1642,6 +1754,42 @@ SessionStoreService.prototype = {
return data;
},
/**
* extract the base domain from a history entry and its children
* @param aEntry
* the history entry, serialized
* @param aHosts
* the hash that will be used to store hosts eg, { hostname: true }
* @param aCheckPrivacy
* should we check the privacy level for https
* @param aIsPinned
* is the entry we're evaluating for a pinned tab; used only if
* aCheckPrivacy
*/
_extractHostsForCookies:
function sss__extractHostsForCookies(aEntry, aHosts, aCheckPrivacy, aIsPinned) {
let match;
if ((match = /^https?:\/\/(?:[^@\/\s]+@)?([\w.-]+)/.exec(aEntry.url)) != null) {
if (!aHosts[match[1]] &&
(!aCheckPrivacy ||
this._checkPrivacyLevel(this._getURIFromString(aEntry.url).schemeIs("https"),
aIsPinned))) {
// By setting this to true or false, we can determine when looking at
// the host in _updateCookies if we should check for privacy.
aHosts[match[1]] = aIsPinned;
}
}
else if ((match = /^file:\/\/([^\/]*)/.exec(aEntry.url)) != null) {
aHosts[match[1]] = true;
}
if (aEntry.children) {
aEntry.children.forEach(function(entry) {
this._extractHostsForCookies(entry, aHosts, aCheckPrivacy, aIsPinned);
}, this);
}
},
/**
* store all hosts for a URL
* @param aWindow
@ -1649,25 +1797,12 @@ SessionStoreService.prototype = {
*/
_updateCookieHosts: function sss_updateCookieHosts(aWindow) {
var hosts = this._windows[aWindow.__SSi]._hosts = {};
// get the domain for each URL
function extractHosts(aEntry) {
var match;
if ((match = /^https?:\/\/(?:[^@\/\s]+@)?([\w.-]+)/.exec(aEntry.url)) != null) {
if (!hosts[match[1]] && _this._checkPrivacyLevel(_this._getURIFromString(aEntry.url).schemeIs("https"))) {
hosts[match[1]] = true;
}
}
else if ((match = /^file:\/\/([^\/]*)/.exec(aEntry.url)) != null) {
hosts[match[1]] = true;
}
if (aEntry.children) {
aEntry.children.forEach(extractHosts);
}
}
var _this = this;
this._windows[aWindow.__SSi].tabs.forEach(function(aTabData) { aTabData.entries.forEach(extractHosts); });
this._windows[aWindow.__SSi].tabs.forEach(function(aTabData) {
aTabData.entries.forEach(function(entry) {
this._extractHostsForCookies(entry, hosts, true, !!aTabData.pinned);
}, this);
}, this);
},
/**
@ -1695,11 +1830,16 @@ SessionStoreService.prototype = {
// MAX_EXPIRY should be 2^63-1, but JavaScript can't handle that precision
var MAX_EXPIRY = Math.pow(2, 62);
aWindows.forEach(function(aWindow) {
for (var host in aWindow._hosts) {
if (!aWindow._hosts)
return;
for (var [host, isPinned] in Iterator(aWindow._hosts)) {
var list = CookieSvc.getCookiesFromHost(host);
while (list.hasMoreElements()) {
var cookie = list.getNext().QueryInterface(Ci.nsICookie2);
if (cookie.isSession && _this._checkPrivacyLevel(cookie.isSecure)) {
// aWindow._hosts will only have hosts with the right privacy rules,
// so there is no need to do anything special with this call to
// _checkPrivacyLevel.
if (cookie.isSession && _this._checkPrivacyLevel(cookie.isSecure, isPinned)) {
// use the cookie's host, path, and name as keys into a hash,
// to make sure we serialize each cookie only once
if (!(cookie.host in jscookies &&
@ -1870,7 +2010,13 @@ SessionStoreService.prototype = {
this._updateTextAndScrollData(aWindow);
this._updateCookieHosts(aWindow);
this._updateWindowFeatures(aWindow);
// Make sure we keep __SS_lastSessionWindowID around for cases like entering
// or leaving PB mode.
if (aWindow.__SS_lastSessionWindowID)
this._windows[aWindow.__SSi].__lastSessionWindowID =
aWindow.__SS_lastSessionWindowID;
this._dirtyWindows[aWindow.__SSi] = false;
},
@ -1968,6 +2114,13 @@ SessionStoreService.prototype = {
tabs[t].hidden = winData.tabs[t].hidden;
}
// We want to correlate the window with data from the last session, so
// assign another id if we have one. Otherwise clear so we don't do
// anything with it.
delete aWindow.__SS_lastSessionWindowID;
if (winData.__lastSessionWindowID)
aWindow.__SS_lastSessionWindowID = winData.__lastSessionWindowID;
// when overwriting tabs, remove all superflous ones
if (aOverwriteTabs && newTabCount < openTabCount) {
Array.slice(tabbrowser.tabs, newTabCount, openTabCount)
@ -2618,11 +2771,9 @@ SessionStoreService.prototype = {
if (this._inPrivateBrowsing)
return;
var pinnedOnly = false;
if (this._loadState == STATE_QUITTING && !this._doResumeSession() ||
/* if crash recovery is disabled, only save session resuming information */
this._loadState == STATE_RUNNING && !this._resume_from_crash)
pinnedOnly = true;
// If crash recovery is disabled, we only want to resume with pinned tabs
// if we crash.
let pinnedOnly = this._loadState == STATE_RUNNING && !this._resume_from_crash;
var oState = this._getCurrentState(aUpdateAll, pinnedOnly);
if (!oState)
@ -2816,10 +2967,18 @@ SessionStoreService.prototype = {
* (distinguishes between encrypted and non-encrypted sites)
* @param aIsHTTPS
* Bool is encrypted
* @param aUseDefaultPref
* don't do normal check for deferred
* @returns bool
*/
_checkPrivacyLevel: function sss_checkPrivacyLevel(aIsHTTPS) {
return this._prefBranch.getIntPref("sessionstore.privacy_level") < (aIsHTTPS ? PRIVACY_ENCRYPTED : PRIVACY_FULL);
_checkPrivacyLevel: function sss_checkPrivacyLevel(aIsHTTPS, aUseDefaultPref) {
let pref = "sessionstore.privacy_level";
// If we're in the process of quitting and we're not autoresuming the session
// then we should treat it as a deferred session. We have a different privacy
// pref for that case.
if (!aUseDefaultPref && this._loadState == STATE_QUITTING && !this._doResumeSession())
pref = "sessionstore.privacy_level_deferred";
return this._prefBranch.getIntPref(pref) < (aIsHTTPS ? PRIVACY_ENCRYPTED : PRIVACY_FULL);
},
/**
@ -2931,6 +3090,133 @@ SessionStoreService.prototype = {
sessionAge && sessionAge >= SIX_HOURS_IN_MS);
},
/**
* This is going to take a state as provided at startup (via
* nsISessionStartup.state) and split it into 2 parts. The first part
* (defaultState) will be a state that should still be restored at startup,
* while the second part (state) is a state that should be saved for later.
* defaultState will be comprised of windows with only pinned tabs, extracted
* from state. It will contain the cookies that go along with the history
* entries in those tabs. It will also contain window position information.
*
* defaultState will be restored at startup. state will be placed into
* this._lastSessionState and will be kept in case the user explicitly wants
* to restore the previous session (publicly exposed as restoreLastSession).
*
* @param stateString
* The state string, presumably from nsISessionStartup.state
* @returns [defaultState, state]
*/
_prepDataForDeferredRestore: function sss__prepDataForDeferredRestore(stateString) {
let state = JSON.parse(stateString);
let defaultState = { windows: [], selectedWindow: 1 };
state.selectedWindow = state.selectedWindow || 1;
// Look at each window, remove pinned tabs, adjust selectedindex,
// remove window if necessary.
for (let wIndex = 0; wIndex < state.windows.length;) {
let window = state.windows[wIndex];
window.selected = window.selected || 1;
// We're going to put the state of the window into this object
let pinnedWindowState = { tabs: [], cookies: []};
for (let tIndex = 0; tIndex < window.tabs.length;) {
if (window.tabs[tIndex].pinned) {
// Adjust window.selected
if (tIndex + 1 < window.selected)
window.selected -= 1;
else if (tIndex + 1 == window.selected)
pinnedWindowState.selected = pinnedWindowState.tabs.length + 2;
// + 2 because the tab isn't actually in the array yet
// Now add the pinned tab to our window
pinnedWindowState.tabs =
pinnedWindowState.tabs.concat(window.tabs.splice(tIndex, 1));
// We don't want to increment tIndex here.
continue;
}
tIndex++;
}
// At this point the window in the state object has been modified (or not)
// We want to build the rest of this new window object if we have pinnedTabs.
if (pinnedWindowState.tabs.length) {
// First get the other attributes off the window
WINDOW_ATTRIBUTES.forEach(function(attr) {
if (attr in window) {
pinnedWindowState[attr] = window[attr];
delete window[attr];
}
});
// We're just copying position data into the pinned window.
// Not copying over:
// - _closedTabs
// - extData
// - isPopup
// - hidden
// Assign a unique ID to correlate the window to be opened with the
// remaining data
window.__lastSessionWindowID = pinnedWindowState.__lastSessionWindowID
= "" + Date.now() + Math.random();
// Extract the cookies that belong with each pinned tab
this._splitCookiesFromWindow(window, pinnedWindowState);
// Actually add this window to our defaultState
defaultState.windows.push(pinnedWindowState);
// Remove the window from the state if it doesn't have any tabs
if (!window.tabs.length) {
if (wIndex + 1 <= state.selectedWindow)
state.selectedWindow -= 1;
else if (wIndex + 1 == state.selectedWindow)
defaultState.selectedIndex = defaultState.windows.length + 1;
state.windows.splice(wIndex, 1);
// We don't want to increment wIndex here.
continue;
}
}
wIndex++;
}
return [defaultState, state];
},
/**
* Splits out the cookies from aWinState into aTargetWinState based on the
* tabs that are in aTargetWinState.
* This alters the state of aWinState and aTargetWinState.
*/
_splitCookiesFromWindow:
function sss__splitCookiesFromWindow(aWinState, aTargetWinState) {
if (!aWinState.cookies || !aWinState.cookies.length)
return;
// Get the hosts for history entries in aTargetWinState
let cookieHosts = {};
aTargetWinState.tabs.forEach(function(tab) {
tab.entries.forEach(function(entry) {
this._extractHostsForCookies(entry, cookieHosts, false)
}, this);
}, this);
// By creating a regex we reduce overhead and there is only one loop pass
// through either array (cookieHosts and aWinState.cookies).
let hosts = Object.keys(cookieHosts).join("|").replace("\\.", "\\.", "g");
let cookieRegex = new RegExp(".*(" + hosts + ")");
for (let cIndex = 0; cIndex < aWinState.cookies.length;) {
if (cookieRegex.test(aWinState.cookies[cIndex].host)) {
aTargetWinState.cookies =
aTargetWinState.cookies.concat(aWinState.cookies.splice(cIndex, 1));
continue;
}
cIndex++;
}
},
/**
* Converts a JavaScript object into a JSON string
* (see http://www.json.org/ for more information).
@ -2941,9 +3227,16 @@ SessionStoreService.prototype = {
* @returns the object's JSON representation
*/
_toJSONString: function sss_toJSONString(aJSObject) {
// We never want to save __lastSessionWindowID across sessions, but we do
// want it exported to consumers when running (eg. Private Browsing).
let internalKeys = INTERNAL_KEYS;
if (this._loadState == STATE_QUITTING) {
internalKeys = internalKeys.slice();
internalKeys.push("__lastSessionWindowID");
}
function exclude(key, value) {
// returning undefined results in the exclusion of that key
return INTERNAL_KEYS.indexOf(key) == -1 ? value : undefined;
return internalKeys.indexOf(key) == -1 ? value : undefined;
}
return JSON.stringify(aJSObject, exclude);
},

View File

@ -117,8 +117,8 @@ function test() {
// Test (B) : Session data restoration between modes //
//////////////////////////////////////////////////////////////////
const testURL = "chrome://mochikit/content/browser/" +
"browser/components/sessionstore/test/browser/browser_248970_b_sample.html";
let rootDir = getRootDirectory(gTestPath);
const testURL = rootDir + "browser_248970_b_sample.html";
const testURL2 = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser/browser_248970_b_sample.html";

View File

@ -117,8 +117,8 @@ function test() {
// make sure we don't save form data at all (except for tab duplication)
gPrefService.setIntPref("browser.sessionstore.privacy_level", 2);
let testURL = "chrome://mochikit/content/browser/" +
"browser/components/sessionstore/test/browser/browser_346337_sample.html";
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_346337_sample.html";
let tab = tabbrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);

View File

@ -40,8 +40,8 @@ function test() {
waitForExplicitFinish();
let pendingCount = 1;
let testUrl = "chrome://mochikit/content/browser/" +
"browser/components/sessionstore/test/browser/browser_408470_sample.html";
let rootDir = getRootDirectory(gTestPath);
let testUrl = rootDir + "browser_408470_sample.html";
let tab = gBrowser.addTab(testUrl);
tab.linkedBrowser.addEventListener("load", function(aEvent) {

View File

@ -47,8 +47,8 @@ function test() {
// make sure we do save form data
gPrefService.setIntPref("browser.sessionstore.privacy_level", 0);
let testURL = "chrome://mochikit/content/browser/" +
"browser/components/sessionstore/test/browser/browser_454908_sample.html";
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_454908_sample.html";
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);

View File

@ -42,8 +42,8 @@ function test() {
// make sure we do save form data
gPrefService.setIntPref("browser.sessionstore.privacy_level", 0);
let testURL = "chrome://mochikit/content/browser/" +
"browser/components/sessionstore/test/browser/browser_456342_sample.xhtml";
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_456342_sample.xhtml";
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
this.removeEventListener("load", arguments.callee, true);

View File

@ -22,8 +22,7 @@
var documentInjected = false;
document.getElementsByTagName("iframe")[0].onload =
function() { documentInjected = true; };
frames[0].location = "chrome://mochikit/content/browser/" +
"browser/components/sessionstore/test/browser/browser_459906_empty.html";
frames[0].location = "browser_459906_empty.html";
// ... and ensure that it has time to load
for (var c = 0; !documentInjected && c < 20; c++) {

View File

@ -39,15 +39,14 @@ function test() {
waitForExplicitFinish();
let testURL = "chrome://mochikit/content/browser/" +
"browser/components/sessionstore/test/browser/browser_463205_sample.html";
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_463205_sample.html";
let doneURL = "done";
let mainURL = testURL;
let frame1URL = "data:text/html,<input%20id='original'>";
let frame2URL = "chrome://mochikit/content/browser/" +
"browser/components/sessionstore/test/browser/browser_463205_helper.html";
let frame2URL = rootDir + "browser_463205_helper.html";
let frame3URL = "data:text/html,mark2";
let frameCount = 0;
@ -100,8 +99,7 @@ function test() {
mainURL = testURL;
frame1URL = "http://mochi.test:8888/browser/" +
"browser/components/sessionstore/test/browser/browser_463205_helper.html";
frame2URL = "chrome://mochikit/content/browser/" +
"browser/components/sessionstore/test/browser/browser_463205_helper.html";
frame2URL = rootDir + "browser_463205_helper.html";
frame3URL = "data:text/html,mark2";
frameCount = 0;

View File

@ -41,8 +41,8 @@ function test() {
let uniqueValue = Math.random();
let testURL = "chrome://mochikit/content/browser/" +
"browser/components/sessionstore/test/browser/browser_485482_sample.html";
let rootDir = getRootDirectory(gTestPath);
let testURL = rootDir + "browser_485482_sample.html";
let tab = gBrowser.addTab(testURL);
tab.linkedBrowser.addEventListener("load", function(aEvent) {
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);

View File

@ -154,9 +154,7 @@ function PreviewController(win, tab) {
this.linkedBrowser = tab.linkedBrowser;
this.linkedBrowser.addEventListener("MozAfterPaint", this, false);
this.linkedBrowser.addEventListener("DOMTitleChanged", this, false);
// pageshow is needed for when a tab is dragged across windows.
this.linkedBrowser.addEventListener("pageshow", this, false);
this.tab.addEventListener("TabAttrModified", this, false);
// Cannot perform the lookup during construction. See TabWindow.newTab
XPCOMUtils.defineLazyGetter(this, "preview", function () this.win.previewFromTab(this.tab));
@ -180,8 +178,7 @@ PreviewController.prototype = {
QueryInterface: XPCOMUtils.generateQI([Ci.nsITaskbarPreviewController,
Ci.nsIDOMEventListener]),
destroy: function () {
this.linkedBrowser.removeEventListener("pageshow", this, false);
this.linkedBrowser.removeEventListener("DOMTitleChanged", this, false);
this.tab.removeEventListener("TabAttrModified", this, false);
this.linkedBrowser.removeEventListener("MozAfterPaint", this, false);
// Break cycles, otherwise we end up leaking the window with everything
@ -355,11 +352,7 @@ PreviewController.prototype = {
if (preview.visible)
preview.invalidate();
break;
case "pageshow":
case "DOMTitleChanged":
// The tab's label is sometimes empty when dragging tabs between windows
// so we force the tab title to be updated (see bug 520579)
this.win.tabbrowser.setTabTitle(this.tab);
case "TabAttrModified":
this.updateTitleAndTooltip();
break;
}

View File

@ -9,6 +9,9 @@ var gTabCloseCount = 0;
var gTabMoveCount = 0;
var gPageLoadCount = 0;
var rootDir = getRootDirectory(gTestPath);
const CHROMEROOT = rootDir;
function test() {
waitForExplicitFinish();
@ -21,7 +24,7 @@ function test() {
activeWin.events.addListener("TabClose", onTabClose);
activeWin.events.addListener("TabMove", onTabMove);
gPageA = activeWin.open(makeURI("chrome://mochikit/content/browser/browser/fuel/test/ContentA.html"));
gPageA = activeWin.open(makeURI(CHROMEROOT + "ContentA.html"));
gPageA.events.addListener("load", onPageAFirstLoad);
is(activeWin.tabs.length, 2, "Checking length of 'Browser.tabs' after opening 1 additional tab");
@ -30,7 +33,7 @@ function test() {
gPageA.events.removeListener("load", onPageAFirstLoad);
is(gPageA.uri.spec, event.data.uri.spec, "Checking event browser tab is equal to page A");
gPageB = activeWin.open(makeURI("chrome://mochikit/content/browser/browser/fuel/test/ContentB.html"));
gPageB = activeWin.open(makeURI(CHROMEROOT + "ContentB.html"));
gPageB.events.addListener("load", delayAfterOpen);
gPageB.focus();
@ -46,8 +49,8 @@ function test() {
function afterOpen(event) {
gPageB.events.removeListener("load", delayAfterOpen);
// check actuals
is(gPageA.uri.spec, "chrome://mochikit/content/browser/browser/fuel/test/ContentA.html", "Checking 'BrowserTab.uri' after opening");
is(gPageB.uri.spec, "chrome://mochikit/content/browser/browser/fuel/test/ContentB.html", "Checking 'BrowserTab.uri' after opening");
is(gPageA.uri.spec, CHROMEROOT + "ContentA.html", "Checking 'BrowserTab.uri' after opening");
is(gPageB.uri.spec, CHROMEROOT + "ContentB.html", "Checking 'BrowserTab.uri' after opening");
// check event
is(gTabOpenCount, 2, "Checking event handler for tab open");
@ -91,7 +94,7 @@ function test() {
// test loading new content with a frame into a tab
// the event will be checked in onPageBLoadComplete
gPageB.events.addListener("load", onPageBLoadWithFrames);
gPageB.load(makeURI("chrome://mochikit/content/browser/browser/fuel/test/ContentWithFrames.html"));
gPageB.load(makeURI(CHROMEROOT + "ContentWithFrames.html"));
}
function onPageBLoadWithFrames(event) {
@ -107,12 +110,12 @@ function test() {
// test loading new content into a tab
// the event will be checked in onPageASecondLoad
gPageA.events.addListener("load", onPageASecondLoad);
gPageA.load(makeURI("chrome://mochikit/content/browser/browser/fuel/test/ContentB.html"));
gPageA.load(makeURI(CHROMEROOT + "ContentB.html"));
}
function onPageASecondLoad(event) {
gPageA.events.removeListener("load", onPageASecondLoad);
is(gPageA.uri.spec, "chrome://mochikit/content/browser/browser/fuel/test/ContentB.html", "Checking 'BrowserTab.uri' after loading new content");
is(gPageA.uri.spec, CHROMEROOT + "ContentB.html", "Checking 'BrowserTab.uri' after loading new content");
// start testing closing tabs
// the event will be checked in afterClose

View File

@ -149,6 +149,7 @@
@BINPATH@/components/dom_css.xpt
@BINPATH@/components/dom_events.xpt
@BINPATH@/components/dom_geolocation.xpt
@BINPATH@/components/dom_notification.xpt
@BINPATH@/components/dom_html.xpt
@BINPATH@/components/dom_indexeddb.xpt
@BINPATH@/components/dom_offline.xpt
@ -364,6 +365,7 @@
@BINPATH@/components/satchel.manifest
@BINPATH@/components/nsFormAutoComplete.js
@BINPATH@/components/nsFormHistory.js
@BINPATH@/components/nsInputListAutoComplete.js
@BINPATH@/components/contentSecurityPolicy.manifest
@BINPATH@/components/contentSecurityPolicy.js
@BINPATH@/components/contentAreaDropListener.manifest

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