Merge MC -> JM

This commit is contained in:
Brian Hackett 2011-07-21 18:53:37 -07:00
commit f631882466
613 changed files with 103077 additions and 11073 deletions

View File

@ -111,97 +111,53 @@ getActionNameCB(AtkAction *aAction, gint aActionIndex)
const gchar *
getKeyBindingCB(AtkAction *aAction, gint aActionIndex)
{
nsAccessibleWrap *accWrap = GetAccessibleWrap(ATK_OBJECT(aAction));
if (!accWrap)
return nsnull;
nsAccessibleWrap* acc = GetAccessibleWrap(ATK_OBJECT(aAction));
if (!acc)
return nsnull;
//return all KeyBindings including accesskey and shortcut
nsAutoString allKeyBinding;
// Return all key bindings including access key and keyboard shortcut.
nsAutoString keyBindingsStr;
//get accesskey
nsAutoString accessKey;
nsresult rv = accWrap->GetKeyboardShortcut(accessKey);
// Get access key.
KeyBinding keyBinding = acc->AccessKey();
if (!keyBinding.IsEmpty()) {
keyBinding.AppendToString(keyBindingsStr, KeyBinding::eAtkFormat);
if (NS_SUCCEEDED(rv) && !accessKey.IsEmpty()) {
nsAccessible* parent = accWrap->GetParent();
if (parent) {
PRUint32 atkRole = atkRoleMap[parent->NativeRole()];
nsAccessible* parent = acc->GetParent();
PRUint32 role = parent ? parent->Role() : 0;
if (role == nsIAccessibleRole::ROLE_PARENT_MENUITEM ||
role == nsIAccessibleRole::ROLE_MENUITEM ||
role == nsIAccessibleRole::ROLE_RADIO_MENU_ITEM ||
role == nsIAccessibleRole::ROLE_CHECK_MENU_ITEM) {
// It is submenu, expose keyboard shortcuts from menu hierarchy like
// "s;<Alt>f:s"
nsAutoString keysInHierarchyStr = keyBindingsStr;
do {
KeyBinding parentKeyBinding = parent->AccessKey();
if (!parentKeyBinding.IsEmpty()) {
nsAutoString str;
parentKeyBinding.ToString(str, KeyBinding::eAtkFormat);
str.Append(':');
if (atkRole == ATK_ROLE_MENU_BAR) {
//it is topmenu, change from "Alt+f" to "f;<Alt>f"
nsAutoString rightChar;
accessKey.Right(rightChar, 1);
allKeyBinding = rightChar + NS_LITERAL_STRING(";<Alt>") +
rightChar;
}
else if ((atkRole == ATK_ROLE_MENU) || (atkRole == ATK_ROLE_MENU_ITEM)) {
//it is submenu, change from "s" to "s;<Alt>f:s"
nsAutoString allKey = accessKey;
nsAccessible* grandParent = parent;
do {
nsAutoString grandParentKey;
grandParent->GetKeyboardShortcut(grandParentKey);
if (!grandParentKey.IsEmpty()) {
nsAutoString rightChar;
grandParentKey.Right(rightChar, 1);
allKey = rightChar + NS_LITERAL_STRING(":") + allKey;
}
} while ((grandParent = grandParent->GetParent()) &&
atkRoleMap[grandParent->NativeRole()] != ATK_ROLE_MENU_BAR);
allKeyBinding = accessKey + NS_LITERAL_STRING(";<Alt>") +
allKey;
}
}
else {
//default process, rarely happens.
nsAutoString rightChar;
accessKey.Right(rightChar, 1);
allKeyBinding = rightChar + NS_LITERAL_STRING(";<Alt>") + rightChar;
keysInHierarchyStr.Insert(str, 0);
}
} while ((parent = parent->GetParent()) &&
parent->Role() != nsIAccessibleRole::ROLE_MENUBAR);
keyBindingsStr.Append(';');
keyBindingsStr.Append(keysInHierarchyStr);
}
else //don't have accesskey
allKeyBinding.AssignLiteral(";");
} else {
// No access key, add ';' to point this.
keyBindingsStr.Append(';');
}
//get shortcut
nsAutoString subShortcut;
nsCOMPtr<nsIDOMDOMStringList> keyBindings;
rv = accWrap->GetKeyBindings(aActionIndex, getter_AddRefs(keyBindings));
// Get keyboard shortcut.
keyBindingsStr.Append(';');
keyBinding = acc->KeyboardShortcut();
if (!keyBinding.IsEmpty()) {
keyBinding.AppendToString(keyBindingsStr, KeyBinding::eAtkFormat);
}
if (NS_SUCCEEDED(rv) && keyBindings) {
PRUint32 length = 0;
keyBindings->GetLength(&length);
for (PRUint32 i = 0; i < length; i++) {
nsAutoString keyBinding;
keyBindings->Item(i, keyBinding);
//change the shortcut from "Ctrl+Shift+L" to "<Control><Shift>L"
PRInt32 oldPos, curPos=0;
while ((curPos != -1) && (curPos < (PRInt32)keyBinding.Length())) {
oldPos = curPos;
nsAutoString subString;
curPos = keyBinding.FindChar('+', oldPos);
if (curPos == -1) {
keyBinding.Mid(subString, oldPos, keyBinding.Length() - oldPos);
subShortcut += subString;
} else {
keyBinding.Mid(subString, oldPos, curPos - oldPos);
//change "Ctrl" to "Control"
if (subString.LowerCaseEqualsLiteral("ctrl"))
subString.AssignLiteral("Control");
subShortcut += NS_LITERAL_STRING("<") + subString +
NS_LITERAL_STRING(">");
curPos++;
}
}
}
}
allKeyBinding += NS_LITERAL_STRING(";") + subShortcut;
return nsAccessibleWrap::ReturnString(allKeyBinding);
return nsAccessibleWrap::ReturnString(keyBindingsStr);
}

View File

@ -45,6 +45,7 @@
#include "nsEventShell.h"
#include "nsTextAccessible.h"
#include "TextUpdater.h"
#include "mozilla/dom/Element.h"
////////////////////////////////////////////////////////////////////////////////
@ -258,17 +259,9 @@ NotificationController::WillRefresh(mozilla::TimeStamp aTime)
if (ownerContent) {
nsAccessible* outerDocAcc = mDocument->GetAccessible(ownerContent);
if (outerDocAcc && outerDocAcc->AppendChild(childDoc)) {
if (mDocument->AppendChildDocument(childDoc)) {
// Fire reorder event to notify new accessible document has been
// attached to the tree.
nsRefPtr<AccEvent> reorderEvent =
new AccEvent(nsIAccessibleEvent::EVENT_REORDER, outerDocAcc,
eAutoDetect, AccEvent::eCoalesceFromSameSubtree);
if (reorderEvent)
QueueEvent(reorderEvent);
if (mDocument->AppendChildDocument(childDoc))
continue;
}
outerDocAcc->RemoveChild(childDoc);
}

View File

@ -468,12 +468,13 @@ nsAccDocManager::CreateDocOrRootAccessible(nsIDocument *aDocument)
}
// Fire reorder event to notify new accessible document has been attached to
// the tree.
// the tree. The reorder event is delivered after the document tree is
// constructed because event processing and tree construction are done by
// the same document.
nsRefPtr<AccEvent> reorderEvent =
new AccEvent(nsIAccessibleEvent::EVENT_REORDER, appAcc, eAutoDetect,
AccEvent::eCoalesceFromSameSubtree);
if (reorderEvent)
docAcc->FireDelayedAccessibleEvent(reorderEvent);
docAcc->FireDelayedAccessibleEvent(reorderEvent);
} else {
parentDocAcc->BindChildDocument(docAcc);

View File

@ -76,7 +76,6 @@
*/
nsIStringBundle *nsAccessNode::gStringBundle = 0;
nsIStringBundle *nsAccessNode::gKeyStringBundle = 0;
nsINode *nsAccessNode::gLastFocusedNode = nsnull;
PRBool nsAccessNode::gIsFormFillEnabled = PR_FALSE;
@ -210,8 +209,6 @@ void nsAccessNode::InitXPAccessibility()
// Static variables are released in ShutdownAllXPAccessibility();
stringBundleService->CreateBundle(ACCESSIBLE_BUNDLE_URL,
&gStringBundle);
stringBundleService->CreateBundle(PLATFORM_KEYS_BUNDLE_URL,
&gKeyStringBundle);
}
nsAccessibilityAtoms::AddRefAtoms();
@ -246,7 +243,6 @@ void nsAccessNode::ShutdownXPAccessibility()
// at exit of program
NS_IF_RELEASE(gStringBundle);
NS_IF_RELEASE(gKeyStringBundle);
NS_IF_RELEASE(gLastFocusedNode);
// Release gApplicationAccessible after everything else is shutdown

View File

@ -215,7 +215,6 @@ protected:
// Static data, we do our own refcounting for our static data
static nsIStringBundle *gStringBundle;
static nsIStringBundle *gKeyStringBundle;
static PRBool gIsFormFillEnabled;

View File

@ -105,6 +105,7 @@
#include "mozilla/unused.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/Element.h"
using namespace mozilla;
@ -329,55 +330,6 @@ nsAccessible::Description(nsString& aDescription)
aDescription.CompressWhitespace();
}
// mask values for ui.key.chromeAccess and ui.key.contentAccess
#define NS_MODIFIER_SHIFT 1
#define NS_MODIFIER_CONTROL 2
#define NS_MODIFIER_ALT 4
#define NS_MODIFIER_META 8
// returns the accesskey modifier mask used in the given node's context
// (i.e. chrome or content), or 0 if an error occurs
static PRInt32
GetAccessModifierMask(nsIContent* aContent)
{
// use ui.key.generalAccessKey (unless it is -1)
switch (Preferences::GetInt("ui.key.generalAccessKey", -1)) {
case -1: break;
case nsIDOMKeyEvent::DOM_VK_SHIFT: return NS_MODIFIER_SHIFT;
case nsIDOMKeyEvent::DOM_VK_CONTROL: return NS_MODIFIER_CONTROL;
case nsIDOMKeyEvent::DOM_VK_ALT: return NS_MODIFIER_ALT;
case nsIDOMKeyEvent::DOM_VK_META: return NS_MODIFIER_META;
default: return 0;
}
// get the docShell to this DOMNode, return 0 on failure
nsCOMPtr<nsIDocument> document = aContent->GetCurrentDoc();
if (!document)
return 0;
nsCOMPtr<nsISupports> container = document->GetContainer();
if (!container)
return 0;
nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(container));
if (!treeItem)
return 0;
// determine the access modifier used in this context
nsresult rv = NS_ERROR_FAILURE;
PRInt32 itemType, accessModifierMask = 0;
treeItem->GetItemType(&itemType);
switch (itemType) {
case nsIDocShellTreeItem::typeChrome:
rv = Preferences::GetInt("ui.key.chromeAccess", &accessModifierMask);
break;
case nsIDocShellTreeItem::typeContent:
rv = Preferences::GetInt("ui.key.contentAccess", &accessModifierMask);
break;
}
return NS_SUCCEEDED(rv) ? accessModifierMask : 0;
}
NS_IMETHODIMP
nsAccessible::GetKeyboardShortcut(nsAString& aAccessKey)
{
@ -386,6 +338,13 @@ nsAccessible::GetKeyboardShortcut(nsAString& aAccessKey)
if (IsDefunct())
return NS_ERROR_FAILURE;
AccessKey().ToString(aAccessKey);
return NS_OK;
}
KeyBinding
nsAccessible::AccessKey() const
{
PRUint32 key = nsCoreUtils::GetAccessKeyFor(mContent);
if (!key && mContent->IsElement()) {
nsAccessible* label = nsnull;
@ -408,32 +367,54 @@ nsAccessible::GetKeyboardShortcut(nsAString& aAccessKey)
}
if (!key)
return NS_OK;
return KeyBinding();
nsAutoString accesskey(key);
// Append the modifiers in reverse order, result: Control+Alt+Shift+Meta+<key>
nsAutoString propertyKey;
PRInt32 modifierMask = GetAccessModifierMask(mContent);
if (modifierMask & NS_MODIFIER_META) {
propertyKey.AssignLiteral("VK_META");
nsAccessible::GetFullKeyName(propertyKey, accesskey, accesskey);
}
if (modifierMask & NS_MODIFIER_SHIFT) {
propertyKey.AssignLiteral("VK_SHIFT");
nsAccessible::GetFullKeyName(propertyKey, accesskey, accesskey);
}
if (modifierMask & NS_MODIFIER_ALT) {
propertyKey.AssignLiteral("VK_ALT");
nsAccessible::GetFullKeyName(propertyKey, accesskey, accesskey);
}
if (modifierMask & NS_MODIFIER_CONTROL) {
propertyKey.AssignLiteral("VK_CONTROL");
nsAccessible::GetFullKeyName(propertyKey, accesskey, accesskey);
// Get modifier mask. Use ui.key.generalAccessKey (unless it is -1).
switch (Preferences::GetInt("ui.key.generalAccessKey", -1)) {
case -1:
break;
case nsIDOMKeyEvent::DOM_VK_SHIFT:
return KeyBinding(key, KeyBinding::kShift);
case nsIDOMKeyEvent::DOM_VK_CONTROL:
return KeyBinding(key, KeyBinding::kControl);
case nsIDOMKeyEvent::DOM_VK_ALT:
return KeyBinding(key, KeyBinding::kAlt);
case nsIDOMKeyEvent::DOM_VK_META:
return KeyBinding(key, KeyBinding::kMeta);
default:
return KeyBinding();
}
aAccessKey = accesskey;
return NS_OK;
// Determine the access modifier used in this context.
nsIDocument* document = mContent->GetCurrentDoc();
if (!document)
return KeyBinding();
nsCOMPtr<nsISupports> container = document->GetContainer();
if (!container)
return KeyBinding();
nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(container));
if (!treeItem)
return KeyBinding();
nsresult rv = NS_ERROR_FAILURE;
PRInt32 itemType = 0, modifierMask = 0;
treeItem->GetItemType(&itemType);
switch (itemType) {
case nsIDocShellTreeItem::typeChrome:
rv = Preferences::GetInt("ui.key.chromeAccess", &modifierMask);
break;
case nsIDocShellTreeItem::typeContent:
rv = Preferences::GetInt("ui.key.contentAccess", &modifierMask);
break;
}
return NS_SUCCEEDED(rv) ? KeyBinding(key, modifierMask) : KeyBinding();
}
KeyBinding
nsAccessible::KeyboardShortcut() const
{
return KeyBinding();
}
NS_IMETHODIMP
@ -604,22 +585,6 @@ nsresult nsAccessible::GetTranslatedString(const nsAString& aKey, nsAString& aSt
return NS_OK;
}
nsresult nsAccessible::GetFullKeyName(const nsAString& aModifierName, const nsAString& aKeyName, nsAString& aStringOut)
{
nsXPIDLString modifierName, separator;
if (!gKeyStringBundle ||
NS_FAILED(gKeyStringBundle->GetStringFromName(PromiseFlatString(aModifierName).get(),
getter_Copies(modifierName))) ||
NS_FAILED(gKeyStringBundle->GetStringFromName(NS_LITERAL_STRING("MODIFIER_SEPARATOR").get(),
getter_Copies(separator)))) {
return NS_ERROR_FAILURE;
}
aStringOut = modifierName + separator + aKeyName;
return NS_OK;
}
PRBool
nsAccessible::IsVisible(PRBool* aIsOffscreen)
{
@ -1788,6 +1753,10 @@ NS_IMETHODIMP
nsAccessible::GetDefaultKeyBinding(nsAString& aKeyBinding)
{
aKeyBinding.Truncate();
if (IsDefunct())
return NS_ERROR_FAILURE;
KeyboardShortcut().ToString(aKeyBinding);
return NS_OK;
}
@ -3359,3 +3328,79 @@ nsAccessible::GetLevelInternal()
return level;
}
////////////////////////////////////////////////////////////////////////////////
// KeyBinding class
void
KeyBinding::ToPlatformFormat(nsAString& aValue) const
{
nsCOMPtr<nsIStringBundle> keyStringBundle;
nsCOMPtr<nsIStringBundleService> stringBundleService =
mozilla::services::GetStringBundleService();
if (stringBundleService)
stringBundleService->CreateBundle(PLATFORM_KEYS_BUNDLE_URL,
getter_AddRefs(keyStringBundle));
if (!keyStringBundle)
return;
nsAutoString separator;
keyStringBundle->GetStringFromName(NS_LITERAL_STRING("MODIFIER_SEPARATOR").get(),
getter_Copies(separator));
nsAutoString modifierName;
if (mModifierMask & kControl) {
keyStringBundle->GetStringFromName(NS_LITERAL_STRING("VK_CONTROL").get(),
getter_Copies(modifierName));
aValue.Append(modifierName);
aValue.Append(separator);
}
if (mModifierMask & kAlt) {
keyStringBundle->GetStringFromName(NS_LITERAL_STRING("VK_ALT").get(),
getter_Copies(modifierName));
aValue.Append(modifierName);
aValue.Append(separator);
}
if (mModifierMask & kShift) {
keyStringBundle->GetStringFromName(NS_LITERAL_STRING("VK_SHIFT").get(),
getter_Copies(modifierName));
aValue.Append(modifierName);
aValue.Append(separator);
}
if (mModifierMask & kMeta) {
keyStringBundle->GetStringFromName(NS_LITERAL_STRING("VK_META").get(),
getter_Copies(modifierName));
aValue.Append(modifierName);
aValue.Append(separator);
}
aValue.Append(mKey);
}
void
KeyBinding::ToAtkFormat(nsAString& aValue) const
{
nsAutoString modifierName;
if (mModifierMask & kControl)
aValue.Append(NS_LITERAL_STRING("<Control>"));
if (mModifierMask & kAlt)
aValue.Append(NS_LITERAL_STRING("<Alt>"));
if (mModifierMask & kShift)
aValue.Append(NS_LITERAL_STRING("<Shift>"));
if (mModifierMask & kMeta)
aValue.Append(NS_LITERAL_STRING("<Meta>"));
aValue.Append(mKey);
}

View File

@ -57,6 +57,7 @@
class AccEvent;
class AccGroupInfo;
class EmbeddedObjCollector;
class KeyBinding;
class nsAccessible;
class nsHyperTextAccessible;
class nsHTMLLIAccessible;
@ -401,6 +402,20 @@ public:
inline bool IsTextLeaf() const { return mFlags & eTextLeafAccessible; }
nsTextAccessible* AsTextLeaf();
//////////////////////////////////////////////////////////////////////////////
// ActionAccessible
/**
* Return access key, such as Alt+D.
*/
virtual KeyBinding AccessKey() const;
/**
* Return global keyboard shortcut for default action, such as Ctrl+O for
* Open file menuitem.
*/
virtual KeyBinding KeyboardShortcut() const;
//////////////////////////////////////////////////////////////////////////////
// HyperLinkAccessible
@ -692,4 +707,61 @@ protected:
NS_DEFINE_STATIC_IID_ACCESSOR(nsAccessible,
NS_ACCESSIBLE_IMPL_IID)
/**
* Represent key binding associated with accessible (such as access key and
* global keyboard shortcuts).
*/
class KeyBinding
{
public:
/**
* Modifier mask values.
*/
static const PRUint32 kShift = 1;
static const PRUint32 kControl = 2;
static const PRUint32 kAlt = 4;
static const PRUint32 kMeta = 8;
KeyBinding() : mKey(0), mModifierMask(0) {}
KeyBinding(PRUint32 aKey, PRUint32 aModifierMask) :
mKey(aKey), mModifierMask(aModifierMask) {};
inline bool IsEmpty() const { return !mKey; }
inline PRUint32 Key() const { return mKey; }
inline PRUint32 ModifierMask() const { return mModifierMask; }
enum Format {
ePlatformFormat,
eAtkFormat
};
/**
* Return formatted string for this key binding depending on the given format.
*/
inline void ToString(nsAString& aValue,
Format aFormat = ePlatformFormat) const
{
aValue.Truncate();
AppendToString(aValue, aFormat);
}
inline void AppendToString(nsAString& aValue,
Format aFormat = ePlatformFormat) const
{
if (mKey) {
if (aFormat == ePlatformFormat)
ToPlatformFormat(aValue);
else
ToAtkFormat(aValue);
}
}
private:
void ToPlatformFormat(nsAString& aValue) const;
void ToAtkFormat(nsAString& aValue) const;
PRUint32 mKey;
PRUint32 mModifierMask;
};
#endif

View File

@ -133,13 +133,6 @@ nsApplicationAccessible::Description(nsString &aDescription)
aDescription.Truncate();
}
NS_IMETHODIMP
nsApplicationAccessible::GetKeyboardShortcut(nsAString &aKeyboardShortcut)
{
aKeyboardShortcut.Truncate();
return NS_OK;
}
PRUint64
nsApplicationAccessible::State()
{
@ -384,6 +377,12 @@ nsApplicationAccessible::InvalidateChildren()
// and RemoveChild() method calls.
}
KeyBinding
nsApplicationAccessible::AccessKey() const
{
return KeyBinding();
}
////////////////////////////////////////////////////////////////////////////////
// nsAccessible protected methods

View File

@ -91,7 +91,6 @@ public:
NS_IMETHOD GetPreviousSibling(nsIAccessible **aPreviousSibling);
NS_IMETHOD GetName(nsAString &aName);
NS_IMETHOD GetValue(nsAString &aValue);
NS_IMETHOD GetKeyboardShortcut(nsAString &aKeyboardShortcut);
NS_IMETHOD GetAttributes(nsIPersistentProperties **aAttributes);
NS_IMETHOD GroupPosition(PRInt32 *aGroupLevel, PRInt32 *aSimilarItemsInGroup,
PRInt32 *aPositionInGroup);
@ -130,6 +129,9 @@ public:
virtual void InvalidateChildren();
// ActionAccessible
virtual KeyBinding AccessKey() const;
protected:
// nsAccessible

View File

@ -175,13 +175,11 @@ nsLinkableAccessible::DoAction(PRUint8 aIndex)
nsAccessibleWrap::DoAction(aIndex);
}
NS_IMETHODIMP
nsLinkableAccessible::GetKeyboardShortcut(nsAString& aKeyboardShortcut)
KeyBinding
nsLinkableAccessible::AccessKey() const
{
aKeyboardShortcut.Truncate();
return mActionAcc ? mActionAcc->GetKeyboardShortcut(aKeyboardShortcut) :
nsAccessible::GetKeyboardShortcut(aKeyboardShortcut);
return mActionAcc ?
mActionAcc->AccessKey() : nsAccessible::AccessKey();
}
////////////////////////////////////////////////////////////////////////////////

View File

@ -92,7 +92,6 @@ public:
NS_IMETHOD DoAction(PRUint8 index);
NS_IMETHOD GetValue(nsAString& _retval);
NS_IMETHOD TakeFocus();
NS_IMETHOD GetKeyboardShortcut(nsAString& _retval);
// nsAccessNode
virtual void Shutdown();
@ -100,6 +99,9 @@ public:
// nsAccessible
virtual PRUint64 NativeState();
// ActionAccessible
virtual KeyBinding AccessKey() const;
// HyperLinkAccessible
virtual already_AddRefed<nsIURI> AnchorURIAt(PRUint32 aAnchorIndex);

View File

@ -66,6 +66,7 @@
#include "nsContentCID.h"
#include "nsComponentManagerUtils.h"
#include "nsIInterfaceRequestorUtils.h"
#include "mozilla/dom/Element.h"
static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);

View File

@ -1494,6 +1494,17 @@ nsDocAccessible::NotifyOfInitialUpdate()
// Build initial tree.
CacheChildrenInSubtree(this);
// Fire reorder event after the document tree is constructed. Note, since
// this reorder event is processed by parent document then events targeted to
// this document may be fired prior to this reorder event. If this is
// a problem then consider to keep event processing per tab document.
if (!IsRoot()) {
nsRefPtr<AccEvent> reorderEvent =
new AccEvent(nsIAccessibleEvent::EVENT_REORDER, GetParent(),
eAutoDetect, AccEvent::eCoalesceFromSameSubtree);
ParentDocument()->FireDelayedAccessibleEvent(reorderEvent);
}
}
void

View File

@ -43,6 +43,7 @@
#include "nsCoreUtils.h"
#include "nsEventStates.h"
#include "mozilla/dom/Element.h"
////////////////////////////////////////////////////////////////////////////////
// nsHTMLLinkAccessible

View File

@ -42,11 +42,7 @@
#include "AccessibleAction_i.c"
#include "nsIAccessible.h"
#include "nsAccessNodeWrap.h"
#include "nsCOMPtr.h"
#include "nsString.h"
#include "nsIDOMDOMStringList.h"
#include "nsAccessible.h"
// IUnknown
@ -137,54 +133,44 @@ CAccessibleAction::get_keyBinding(long aActionIndex, long aNumMaxBinding,
long *aNumBinding)
{
__try {
if (!aKeyBinding)
return E_INVALIDARG;
*aKeyBinding = NULL;
if (!aNumBinding)
return E_INVALIDARG;
*aNumBinding = 0;
nsCOMPtr<nsIAccessible> acc(do_QueryObject(this));
if (!acc)
if (aActionIndex != 0 || aNumMaxBinding < 1)
return E_INVALIDARG;
nsRefPtr<nsAccessible> acc(do_QueryObject(this));
if (!acc || acc->IsDefunct())
return E_FAIL;
nsCOMPtr<nsIDOMDOMStringList> keys;
PRUint8 index = static_cast<PRUint8>(aActionIndex);
nsresult rv = acc->GetKeyBindings(index, getter_AddRefs(keys));
if (NS_FAILED(rv))
return GetHRESULT(rv);
PRUint32 length = 0;
keys->GetLength(&length);
if (length == 0)
// Expose keyboard shortcut if it's not exposed via MSAA keyboard shortcut.
KeyBinding keyBinding = acc->AccessKey();
if (keyBinding.IsEmpty())
return S_FALSE;
PRUint32 maxBinding = static_cast<PRUint32>(aNumMaxBinding);
PRUint32 numBinding = length > maxBinding ? maxBinding : length;
*aNumBinding = numBinding;
keyBinding = acc->KeyboardShortcut();
if (keyBinding.IsEmpty())
return S_FALSE;
*aKeyBinding = static_cast<BSTR*>(nsMemory::Alloc((numBinding) * sizeof(BSTR*)));
nsAutoString keyStr;
keyBinding.ToString(keyStr);
*aKeyBinding = static_cast<BSTR*>(::CoTaskMemAlloc(sizeof(BSTR*)));
if (!*aKeyBinding)
return E_OUTOFMEMORY;
PRBool outOfMemory = PR_FALSE;
PRUint32 i = 0;
for (; i < numBinding; i++) {
nsAutoString key;
keys->Item(i, key);
*(aKeyBinding[i]) = ::SysAllocStringLen(key.get(), key.Length());
if (!*(aKeyBinding[i])) {
outOfMemory = PR_TRUE;
break;
}
}
if (outOfMemory) {
for (PRUint32 j = 0; j < i; j++)
::SysFreeString(*(aKeyBinding[j]));
nsMemory::Free(*aKeyBinding);
*aKeyBinding = NULL;
*(aKeyBinding[0]) = ::SysAllocStringLen(keyStr.get(), keyStr.Length());
if (!*(aKeyBinding[0])) {
::CoTaskMemFree(*aKeyBinding);
return E_OUTOFMEMORY;
}
*aNumBinding = 1;
return S_OK;
} __except(nsAccessNodeWrap::FilterA11yExceptions(::GetExceptionCode(), GetExceptionInformation())) { }

View File

@ -486,16 +486,18 @@ STDMETHODIMP nsAccessibleWrap::get_accKeyboardShortcut(
__try {
if (!pszKeyboardShortcut)
return E_INVALIDARG;
*pszKeyboardShortcut = NULL;
nsAccessible *xpAccessible = GetXPAccessibleFor(varChild);
if (!xpAccessible || xpAccessible->IsDefunct())
nsAccessible* acc = GetXPAccessibleFor(varChild);
if (!acc || acc->IsDefunct())
return E_FAIL;
KeyBinding keyBinding = acc->AccessKey();
if (keyBinding.IsEmpty())
keyBinding = acc->KeyboardShortcut();
nsAutoString shortcut;
nsresult rv = xpAccessible->GetKeyboardShortcut(shortcut);
if (NS_FAILED(rv))
return GetHRESULT(rv);
keyBinding.ToString(shortcut);
*pszKeyboardShortcut = ::SysAllocStringLen(shortcut.get(),
shortcut.Length());

View File

@ -58,6 +58,7 @@
#include "nsWidgetsCID.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/Element.h"
using namespace mozilla;
@ -387,14 +388,10 @@ nsXULMenuitemAccessible::Description(nsString& aDescription)
aDescription);
}
//return menu accesskey: N or Alt+F
NS_IMETHODIMP
nsXULMenuitemAccessible::GetKeyboardShortcut(nsAString& aAccessKey)
KeyBinding
nsXULMenuitemAccessible::AccessKey() const
{
aAccessKey.Truncate();
if (IsDefunct())
return NS_ERROR_FAILURE;
// Return menu accesskey: N or Alt+F.
static PRInt32 gMenuAccesskeyModifier = -1; // magic value of -1 indicates unitialized state
// We do not use nsCoreUtils::GetAccesskeyFor() because accesskeys for
@ -403,7 +400,9 @@ nsXULMenuitemAccessible::GetKeyboardShortcut(nsAString& aAccessKey)
mContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::accesskey,
accesskey);
if (accesskey.IsEmpty())
return NS_OK;
return KeyBinding();
PRUint32 modifierKey = 0;
nsAccessible* parentAcc = GetParent();
if (parentAcc) {
@ -415,44 +414,89 @@ nsXULMenuitemAccessible::GetKeyboardShortcut(nsAString& aAccessKey)
gMenuAccesskeyModifier = Preferences::GetInt("ui.key.menuAccessKey", 0);
}
nsAutoString propertyKey;
switch (gMenuAccesskeyModifier) {
case nsIDOMKeyEvent::DOM_VK_CONTROL:
propertyKey.AssignLiteral("VK_CONTROL");
modifierKey = KeyBinding::kControl;
break;
case nsIDOMKeyEvent::DOM_VK_ALT:
propertyKey.AssignLiteral("VK_ALT");
modifierKey = KeyBinding::kAlt;
break;
case nsIDOMKeyEvent::DOM_VK_META:
propertyKey.AssignLiteral("VK_META");
modifierKey = KeyBinding::kMeta;
break;
}
if (!propertyKey.IsEmpty())
nsAccessible::GetFullKeyName(propertyKey, accesskey, aAccessKey);
}
}
if (aAccessKey.IsEmpty())
aAccessKey = accesskey;
return NS_OK;
return KeyBinding(accesskey[0], modifierKey);
}
//return menu shortcut: Ctrl+F or Ctrl+Shift+L
NS_IMETHODIMP
nsXULMenuitemAccessible::GetDefaultKeyBinding(nsAString& aKeyBinding)
KeyBinding
nsXULMenuitemAccessible::KeyboardShortcut() const
{
aKeyBinding.Truncate();
nsAutoString keyElmId;
mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyElmId);
if (keyElmId.IsEmpty())
return KeyBinding();
if (IsDefunct())
return NS_ERROR_FAILURE;
nsIDocument* document = mContent->GetOwnerDoc();
if (!document)
return KeyBinding();
nsAutoString accelText;
mContent->GetAttr(kNameSpaceID_None, nsAccessibilityAtoms::acceltext,
aKeyBinding);
nsIContent* keyElm = document->GetElementById(keyElmId);
if (!keyElm)
return KeyBinding();
return NS_OK;
PRUint32 key = 0;
nsAutoString keyStr;
keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::key, keyStr);
if (keyStr.IsEmpty()) {
nsAutoString keyCodeStr;
keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::keycode, keyCodeStr);
PRUint32 errorCode;
key = keyStr.ToInteger(&errorCode, kAutoDetect);
} else {
key = keyStr[0];
}
nsAutoString modifiersStr;
keyElm->GetAttr(kNameSpaceID_None, nsGkAtoms::modifiers, modifiersStr);
PRUint32 modifierMask = 0;
if (modifiersStr.Find("shift") != -1)
modifierMask != KeyBinding::kShift;
if (modifiersStr.Find("alt") != -1)
modifierMask |= KeyBinding::kAlt;
if (modifiersStr.Find("meta") != -1)
modifierMask |= KeyBinding::kMeta;
if (modifiersStr.Find("control") != -1)
modifierMask |= KeyBinding::kControl;
if (modifiersStr.Find("accel") != -1) {
// Get the accelerator key value from prefs, overriding the default.
switch (Preferences::GetInt("ui.key.accelKey", 0)) {
case nsIDOMKeyEvent::DOM_VK_META:
modifierMask |= KeyBinding::kMeta;
break;
case nsIDOMKeyEvent::DOM_VK_ALT:
modifierMask |= KeyBinding::kAlt;
break;
case nsIDOMKeyEvent::DOM_VK_CONTROL:
modifierMask |= KeyBinding::kControl;
break;
default:
#ifdef XP_MACOSX
modifierMask |= KeyBinding::kMeta;
#else
modifierMask |= KeyBinding::kControl;
#endif
}
}
return KeyBinding(key, modifierMask);
}
PRUint32

View File

@ -82,8 +82,6 @@ public:
nsXULMenuitemAccessible(nsIContent *aContent, nsIWeakReference *aShell);
// nsIAccessible
NS_IMETHOD GetKeyboardShortcut(nsAString& _retval);
NS_IMETHOD GetDefaultKeyBinding(nsAString& aKeyBinding);
NS_IMETHOD DoAction(PRUint8 index);
NS_IMETHOD GetActionName(PRUint8 aIndex, nsAString& aName);
NS_IMETHOD GetNumActions(PRUint8 *_retval);
@ -98,6 +96,10 @@ public:
PRInt32 *aSetSize);
virtual PRBool GetAllowsAnonChildAccessibles();
// ActionAccessible
virtual KeyBinding AccessKey() const;
virtual KeyBinding KeyboardShortcut() const;
};
/**

View File

@ -97,7 +97,6 @@ _TEST_FILES =\
test_elm_listbox.xul \
test_elm_nsApplicationAcc.html \
test_elm_plugin.html \
test_keys.html \
test_nsIAccessible_selects.html \
test_nsIAccessibleDocument.html \
test_nsIAccessibleImage.html \

View File

@ -51,6 +51,8 @@ _TEST_FILES =\
test_general.html \
test_general.xul \
test_inputs.html \
test_keys_menu.xul \
test_keys.html \
test_link.html \
test_media.html \
test_tree.xul \

View File

@ -12,7 +12,7 @@
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="common.js"></script>
src="../common.js"></script>
<script type="application/javascript">
function testKeyboardShortcut(aAccOrElmOrID, aKey)
@ -29,6 +29,7 @@
{
testKeyboardShortcut("input1", "");
testKeyboardShortcut("input2", MAC ? "⌃b" : "Alt+Shift+b");
testKeyboardShortcut("link", MAC ? "⌃l" : "Alt+Shift+l");
SimpleTest.finish();
}
@ -56,5 +57,6 @@
</label>
<label accesskey="b" for="input2">
<input id="input2"/>
<a id="link" accesskey="l">link</a>
</body>
</html>

View File

@ -0,0 +1,101 @@
<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
type="text/css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Accessible XUL access keys and shortcut keys tests">
<script type="application/javascript"
src="chrome://mochikit/content/MochiKit/packed.js" />
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
<script type="application/javascript"
src="../common.js" />
<script type="application/javascript"
src="../events.js" />
<script type="application/javascript">
<![CDATA[
function openMenu(aMenuID, aMenuitemID)
{
this.menuNode = getNode(aMenuID),
this.menuitemNode = getNode(aMenuitemID),
this.eventSeq = [
new invokerChecker(EVENT_FOCUS, this.menuNode)
];
this.invoke = function openMenu_invoke()
{
// Show menu.
this.menuNode.open = true;
}
this.finalCheck = function openMenu_finalCheck()
{
var menu = getAccessible(aMenuID);
is(menu.keyboardShortcut, (MAC ? "u" : "Alt+u"),
"Wrong accesskey on " + prettyName(this.menuitemNode));
var menuitem = getAccessible(aMenuitemID);
is(menuitem.keyboardShortcut, "p",
"Wrong accesskey on " + prettyName(this.menuitemNode));
is(menuitem.defaultKeyBinding, (MAC ? "⌃l" : "Ctrl+l"),
"Wrong keyboard shortcut on " + prettyName(this.menuitemNode));
}
this.getID = function openMenu_getID()
{
return "menuitem accesskey and shortcut test " +
prettyName(this.menuItemNode);
}
}
var gQueue = null;
function doTest()
{
gQueue = new eventQueue();
gQueue.push(new openMenu("menu", "menuitem"));
gQueue.invoke(); // Will call SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
]]>
</script>
<hbox flex="1" style="overflow: auto;">
<body xmlns="http://www.w3.org/1999/xhtml">
<a target="_blank"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=672092"
title="Reorganize access key and keyboard shortcut handling code">
Mozilla Bug 672092
</a><br/>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
</pre>
</body>
<vbox flex="1">
<keyset>
<key key="l" modifiers="control" id="key1"/>
</keyset>
<menubar>
<menu label="menu" id="menu" accesskey="u">
<menupopup>
<menuitem accesskey="p" key="key1" label="item1" id="menuitem"/>
</menupopup>
</menu>
</menubar>
<vbox id="debug"/>
</vbox>
</hbox>
</window>

View File

@ -159,11 +159,23 @@ const DO_NOT_FINISH_TEST = 1;
* // Checker object interface:
* //
* // var checker = {
* // type getter: function() {}, // DOM or a11y event type
* // target getter: function() {}, // DOM node or accessible
* // phase getter: function() {}, // DOM event phase (false - bubbling)
* // * DOM or a11y event type. *
* // type getter: function() {},
* //
* // * DOM node or accessible. *
* // target getter: function() {},
* //
* // * DOM event phase (false - bubbling). *
* // phase getter: function() {},
* //
* // * Callback, called when event is handled
* // check: function(aEvent) {},
* // getID: function() {}
* //
* // * Checker ID *
* // getID: function() {},
* //
* // * Event that don't have predefined order relative other events. *
* // async getter: function() {}
* // };
* eventSeq getter() {},
*
@ -327,10 +339,11 @@ function eventQueue(aEventType)
if ("debugCheck" in invoker)
invoker.debugCheck(aEvent);
// Search through handled expected events if one of them was handled again.
// Search through handled expected events to report error if one of them is
// handled for a second time.
var idx = 0;
for (; idx < this.mEventSeq.length; idx++) {
if (!this.isEventUnexpected(idx) && (invoker.wasCaught[idx] == true) &&
if (this.isEventExpected(idx) && (invoker.wasCaught[idx] == true) &&
this.isAlreadyCaught(idx, aEvent)) {
var msg = "Doubled event { event type: " +
@ -341,52 +354,85 @@ function eventQueue(aEventType)
}
}
// Search through unexpected events to ensure no one of them was handled.
// Search through unexpected events, any matches result in error report
// after this invoker processing.
for (idx = 0; idx < this.mEventSeq.length; idx++) {
if (this.isEventUnexpected(idx) && this.compareEvents(idx, aEvent))
invoker.wasCaught[idx] = true;
}
// We've handled all expected events, next invoker processing is pending.
if (this.mEventSeqIdx == this.mEventSeq.length)
// Nothing left, proceed next invoker in timeout. Otherwise check if
// handled event is matched.
var idxObj = {};
if (!this.prepareForExpectedEvent(invoker, idxObj))
return;
// Compute next expected event index.
for (idx = this.mEventSeqIdx + 1;
idx < this.mEventSeq.length && this.mEventSeq[idx].unexpected;
idx++);
// No expected events were registered, proceed to next invoker to ensure
// unexpected events for current invoker won't be handled.
if (idx == this.mEventSeq.length) {
this.mEventSeqIdx = idx;
this.processNextInvokerInTimeout();
return;
// Check if handled event matches expected sync event.
var matched = false;
idx = idxObj.value;
if (idx < this.mEventSeq.length) {
matched = this.compareEvents(idx, aEvent);
if (matched)
this.mEventSeqIdx = idx;
}
// Check if handled event matches expected event.
var matched = this.compareEvents(idx, aEvent);
// Check if handled event matches any expected async events.
if (!matched) {
for (idx = 0; idx < this.mEventSeq.length; idx++) {
if (this.mEventSeq[idx].async) {
matched = this.compareEvents(idx, aEvent);
if (matched)
break;
}
}
}
this.dumpEventToDOM(aEvent, idx, matched);
if (matched) {
this.checkEvent(idx, aEvent);
invoker.wasCaught[idx] = true;
this.mEventSeqIdx = idx;
// Get next expected event index.
while (++idx < this.mEventSeq.length && this.mEventSeq[idx].unexpected);
// If the last expected event was processed, proceed next invoker in
// timeout to ensure unexpected events for current invoker won't be
// handled.
if (idx == this.mEventSeq.length) {
this.mEventSeqIdx = idx;
this.processNextInvokerInTimeout();
}
this.prepareForExpectedEvent(invoker);
}
}
// Helpers
this.prepareForExpectedEvent =
function eventQueue_prepareForExpectedEvent(aInvoker, aIdxObj)
{
// Nothing left, wait for next invoker.
if (this.mEventSeqFinished)
return false;
// Compute next expected sync event index.
for (var idx = this.mEventSeqIdx + 1;
idx < this.mEventSeq.length &&
(this.mEventSeq[idx].unexpected || this.mEventSeq[idx].async);
idx++);
// If no expected events were left, proceed to next invoker in timeout
// to make sure unexpected events for current invoker aren't be handled.
if (idx == this.mEventSeq.length) {
var allHandled = true;
for (var jdx = 0; jdx < this.mEventSeq.length; jdx++) {
if (this.isEventExpected(jdx) && !aInvoker.wasCaught[jdx])
allHandled = false;
}
if (allHandled) {
this.mEventSeqIdx = this.mEventSeq.length;
this.mEventFinished = true;
this.processNextInvokerInTimeout();
return false;
}
}
if (aIdxObj)
aIdxObj.value = idx;
return true;
}
this.getInvoker = function eventQueue_getInvoker()
{
return this.mInvokers[this.mIndex];
@ -405,18 +451,24 @@ function eventQueue(aEventType)
aInvoker.eventSeq :
[ new invokerChecker(this.mDefEventType, aInvoker.DOMNode) ];
for (var idx = 0; idx < this.mEventSeq.length; idx++)
for (var idx = 0; idx < this.mEventSeq.length; idx++) {
this.mEventSeq[idx].unexpected = false;
if (!("async" in this.mEventSeq[idx]))
this.mEventSeq[idx].async = false;
}
var unexpectedSeq = aInvoker.unexpectedEventSeq;
if (unexpectedSeq) {
for (var idx = 0; idx < unexpectedSeq.length; idx++)
for (var idx = 0; idx < unexpectedSeq.length; idx++) {
unexpectedSeq[idx].unexpected = true;
unexpectedSeq[idx].async = false;
}
this.mEventSeq = this.mEventSeq.concat(unexpectedSeq);
}
this.mEventSeqIdx = -1;
this.mEventSeqFinished = false;
// Register event listeners
if (this.mEventSeq) {
@ -517,6 +569,10 @@ function eventQueue(aEventType)
{
return this.mEventSeq[aIdx].unexpected;
}
this.isEventExpected = function eventQueue_isEventExpected(aIdx)
{
return !this.mEventSeq[aIdx].unexpected;
}
this.compareEvents = function eventQueue_compareEvents(aIdx, aEvent)
{
@ -598,20 +654,17 @@ function eventQueue(aEventType)
gLogger.logToDOM(info);
}
var currType = this.getEventTypeAsString(aExpectedEventIdx);
var currTarget = this.getEventTarget(aExpectedEventIdx);
if (!aMatch)
return;
var msg = "EQ: ";
var emphText = "";
if (aMatch) {
emphText = "matched ";
var emphText = "matched ";
var consoleMsg = "*****\nEQ matched: " + currType + "\n*****";
gLogger.logToConsole(consoleMsg);
var currType = this.getEventTypeAsString(aExpectedEventIdx);
var currTarget = this.getEventTarget(aExpectedEventIdx);
var consoleMsg = "*****\nEQ matched: " + currType + "\n*****";
gLogger.logToConsole(consoleMsg);
} else {
msg += "expected";
}
msg += " event, type: " + currType + ", target: " + prettyName(currTarget);
gLogger.logToDOM(msg, true, emphText);
@ -624,6 +677,7 @@ function eventQueue(aEventType)
this.mEventSeq = null;
this.mEventSeqIdx = -1;
this.mEventSeqFinished = false;
}
@ -896,9 +950,10 @@ function synthSelectAll(aNodeOrID, aCheckerOrEventSeq, aEventType)
/**
* Common invoker checker (see eventSeq of eventQueue).
*/
function invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
function invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg, aIsAsync)
{
this.type = aEventType;
this.async = aIsAsync;
this.__defineGetter__("target", invokerChecker_targetGetter);
this.__defineSetter__("target", invokerChecker_targetSetter);
@ -932,6 +987,15 @@ function invokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
this.mTargetFuncArg = aTargetFuncArg;
}
/**
* Common invoker checker for async events.
*/
function asyncInvokerChecker(aEventType, aTargetOrFunc, aTargetFuncArg)
{
this.__proto__ = new invokerChecker(aEventType, aTargetOrFunc,
aTargetFuncArg, true);
}
/**
* Text inserted/removed events checker.
*/

View File

@ -36,6 +36,7 @@
}
var invokerChecker = gOpenerWnd.invokerChecker;
var asyncInvokerChecker = gOpenerWnd.asyncInvokerChecker;
const STATE_BUSY = gOpenerWnd.STATE_BUSY;
const EVENT_DOCUMENT_LOAD_COMPLETE =
@ -48,6 +49,8 @@
const nsIAccessibleStateChangeEvent =
gOpenerWnd.nsIAccessibleStateChangeEvent;
//gOpenerWnd.gA11yEventDumpToConsole = true; // debug
////////////////////////////////////////////////////////////////////////////
// Hacks to make xul:tabbrowser work.
@ -132,7 +135,7 @@
this.eventSeq = [
// We don't expect state change event for busy true since things happen
// quickly and it's coalesced.
new invokerChecker(EVENT_REORDER, getContainer),
new asyncInvokerChecker(EVENT_REORDER, getContainer),
new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, getDocument),
new stateBusyChecker(false)
];
@ -155,7 +158,7 @@
this.eventSeq = [
new documentReloadChecker(true),
new invokerChecker(EVENT_REORDER, getContainer),
new asyncInvokerChecker(EVENT_REORDER, getContainer),
new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, getDocument),
new stateBusyChecker(false)
];
@ -178,7 +181,7 @@
this.eventSeq = [
new documentReloadChecker(false),
new invokerChecker(EVENT_REORDER, getContainer),
new asyncInvokerChecker(EVENT_REORDER, getContainer),
new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, getDocument),
new stateBusyChecker(false)
];
@ -202,7 +205,7 @@
this.eventSeq = [
// We don't expect state change for busy true, load stopped events since
// things happen quickly and it's coalesced.
new invokerChecker(EVENT_REORDER, getContainer),
new asyncInvokerChecker(EVENT_REORDER, getContainer),
new invokerChecker(EVENT_DOCUMENT_LOAD_COMPLETE, getDocument),
new stateBusyChecker(false)
];

View File

@ -357,6 +357,36 @@
}
}
function changeSrc(aID)
{
this.containerNode = getNode(aID);
this.eventSeq = [
new invokerChecker(EVENT_REORDER, this.containerNode)
];
this.invoke = function changeSrc_invoke()
{
this.containerNode.src = "data:text/html,<html><input></html>";
}
this.finalCheck = function changeSrc_finalCheck()
{
var tree =
{ INTERNAL_FRAME: [
{ DOCUMENT: [
{ ENTRY: [ ] }
] };
] };
testAccessibleTree(this.containerNode, tree);
}
this.getID() = function changeSrc_getID()
{
return "change src on iframe";
}
}
////////////////////////////////////////////////////////////////////////////
// Test
@ -378,6 +408,7 @@
gQueue.push(new removeBodyFromIFrameDoc("iframe"));
gQueue.push(new insertElmUnderDocElmWhileBodyMissed("iframe"));
gQueue.push(new insertBodyToIFrameDoc("iframe"));
gQueue.push(new changeSrc("iframe"));
gQueue.invoke(); // SimpleTest.finish() will be called in the end
}
@ -394,6 +425,9 @@
<a target="_blank"
title="Elements inserted outside the body aren't accessible"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=608887">Mozilla Bug 608887</a>
<a target="_blank"
title="Reorder event for document must be fired after document initial tree creation"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=669263">Mozilla Bug 669263</a>
<p id="display"></p>
<div id="content" style="display: none"></div>

View File

@ -6797,9 +6797,9 @@ var gPluginHandler = {
submitReport : function(pluginDumpID, browserDumpID) {
// The crash reporter wants a DOM element it can append an IFRAME to,
// which it uses to submit a form. Let's just give it gBrowser.
this.CrashSubmit.submit(pluginDumpID, gBrowser, null, null);
this.CrashSubmit.submit(pluginDumpID);
if (browserDumpID)
this.CrashSubmit.submit(browserDumpID, gBrowser, null, null);
this.CrashSubmit.submit(browserDumpID);
},
// Callback for user clicking a "reload page" link

View File

@ -42,11 +42,11 @@ const Cu = Components.utils;
// Bug 671101 - directly using webNavigation in this context
// causes docshells to leak
__defineGetter__("webNavigation", function() {
__defineGetter__("webNavigation", function () {
return docShell.QueryInterface(Ci.nsIWebNavigation);
});
addMessageListener("WebNavigation:LoadURI", function(message) {
addMessageListener("WebNavigation:LoadURI", function (message) {
let flags = message.json.flags || webNavigation.LOAD_FLAGS_NONE;
webNavigation.loadURI(message.json.uri, flags, null, null, null);

View File

@ -45,6 +45,7 @@
# Justin Dolske <dolske@mozilla.com>
# Kathleen Brade <brade@pearlcrescent.com>
# Mark Smith <mcs@pearlcrescent.com>
# Kailas Patil <patilkr24@gmail.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
@ -879,17 +880,12 @@ nsContextMenu.prototype = {
saveDocument(this.target.ownerDocument);
},
// Save URL of clicked-on link.
saveLink: function() {
// Helper function to wait for appropriate MIME-type headers and
// then prompt the user with a file picker
saveHelper: function(linkURL, linkText, dialogTitle, bypassCache, doc) {
// canonical def in nsURILoader.h
const NS_ERROR_SAVE_LINK_AS_TIMEOUT = 0x805d0020;
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, doc.nodePrincipal);
var linkText = this.linkText();
var linkURL = this.linkURL;
// an object to proxy the data through to
// nsIExternalHelperAppService.doContent, which will wait for the
// appropriate MIME-type headers and then prompt the user with a
@ -941,7 +937,7 @@ nsContextMenu.prototype = {
if (aStatusCode == NS_ERROR_SAVE_LINK_AS_TIMEOUT) {
// do it the old fashioned way, which will pick the best filename
// it can without waiting.
saveURL(linkURL, linkText, null, true, false, doc.documentURIObject);
saveURL(linkURL, linkText, dialogTitle, bypassCache, false, doc.documentURIObject);
}
if (this.extListener)
this.extListener.onStopRequest(aRequest, aContext, aStatusCode);
@ -985,10 +981,19 @@ nsContextMenu.prototype = {
// set up a channel to do the saving
var ioService = Cc["@mozilla.org/network/io-service;1"].
getService(Ci.nsIIOService);
var channel = ioService.newChannelFromURI(this.getLinkURI());
var channel = ioService.newChannelFromURI(makeURI(linkURL));
channel.notificationCallbacks = new callbacks();
channel.loadFlags |= Ci.nsIRequest.LOAD_BYPASS_CACHE |
Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS;
let flags = Ci.nsIChannel.LOAD_CALL_CONTENT_SNIFFERS;
if (bypassCache)
flags |= Ci.nsIRequest.LOAD_BYPASS_CACHE;
if (channel instanceof Ci.nsICachingChannel)
flags |= Ci.nsICachingChannel.LOAD_BYPASS_LOCAL_CACHE_IF_BUSY;
channel.loadFlags |= flags;
if (channel instanceof Ci.nsIHttpChannel) {
channel.referrer = doc.documentURIObject;
if (channel instanceof Ci.nsIHttpChannelInternal)
@ -1006,6 +1011,14 @@ nsContextMenu.prototype = {
channel.asyncOpen(new saveAsListener(), null);
},
// Save URL of clicked-on link.
saveLink: function() {
var doc = this.target.ownerDocument;
urlSecurityCheck(this.linkURL, doc.nodePrincipal);
this.saveHelper(this.linkURL, this.linkText(), null, true, doc);
},
sendLink: function() {
// we don't know the title of the link so pass in an empty string
MailIntegration.sendMessage( this.linkURL, "" );
@ -1033,8 +1046,7 @@ nsContextMenu.prototype = {
else if (this.onVideo || this.onAudio) {
urlSecurityCheck(this.mediaURL, doc.nodePrincipal);
var dialogTitle = this.onVideo ? "SaveVideoTitle" : "SaveAudioTitle";
saveURL(this.mediaURL, null, dialogTitle, false,
false, doc.documentURIObject);
this.saveHelper(this.mediaURL, null, dialogTitle, false, doc);
}
},

View File

@ -1372,15 +1372,19 @@
// aReferrerURI is null or undefined if the tab is opened from
// an external application or bookmark, i.e. somewhere other
// than the current tab.
if ((aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) &&
Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
if (aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) {
let newTabPos = (this._lastRelatedTab ||
this.selectedTab)._tPos + 1;
if (this._lastRelatedTab)
this._lastRelatedTab.owner = null;
else
t.owner = this.selectedTab;
this.moveTabTo(t, newTabPos);
if (!this.selectedTab.pinned &&
Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent"))
this.moveTabTo(t, newTabPos);
this._lastRelatedTab = t;
}

View File

@ -86,6 +86,8 @@ function GroupItem(listOfEls, options) {
this.keepProportional = false;
this._frozenItemSizeData = {};
this._onChildClose = this._onChildClose.bind(this);
// Variable: _activeTab
// The <TabItem> for the groupItem's active tab.
this._activeTab = null;
@ -122,15 +124,6 @@ function GroupItem(listOfEls, options) {
.css({zIndex: -100})
.appendTo("body");
// ___ New Tab Button
this.$ntb = iQ("<div>")
.addClass('newTabButton')
.click(function() {
self.newTab();
})
.attr('title', tabviewString('groupItem.newTabButton'))
.appendTo($container);
// ___ Resizer
this.$resizer = iQ("<div>")
.addClass('resizer')
@ -818,7 +811,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
let shouldRemoveTabItems = [];
let toClose = this._children.concat();
toClose.forEach(function(child) {
child.removeSubscriber(self, "close");
child.removeSubscriber("close", self._onChildClose);
let removed = child.close(true);
if (removed) {
@ -826,7 +819,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
} else {
// child.removeSubscriber() must be called before child.close(),
// therefore we call child.addSubscriber() if the tab is not removed.
child.addSubscriber(self, "close", self._onChildClose.bind(self));
child.addSubscriber("close", self._onChildClose);
}
});
@ -1009,7 +1002,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
item.droppable(false);
item.groupItemData = {};
item.addSubscriber(this, "close", this._onChildClose.bind(this));
item.addSubscriber("close", this._onChildClose);
item.setParent(this);
if (typeof item.setResizable == 'function')
@ -1110,7 +1103,7 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
item.setBounds(item.getBounds(), true, {force: true});
item.droppable(true);
item.removeSubscriber(this, "close");
item.removeSubscriber("close", this._onChildClose);
if (typeof item.setResizable == 'function')
item.setResizable(true, options.immediately);
@ -1658,9 +1651,45 @@ GroupItem.prototype = Utils.extend(new Item(), new Subscribable(), {
// Helper routine for the constructor; adds various event handlers to the container.
_addHandlers: function GroupItem__addHandlers(container) {
let self = this;
let lastMouseDownTarget;
var dropIndex = false;
var dropSpaceTimer = null;
container.mousedown(function(e) {
let target = e.target;
// only set the last mouse down target if it is a left click, not on the
// close button, not on the new tab button, not on the title bar and its
// element
if (Utils.isLeftClick(e) &&
self.$closeButton[0] != target &&
self.$titlebar[0] != target &&
!self.$titlebar.contains(target) &&
!self.$appTabTray.contains(target)) {
lastMouseDownTarget = target;
} else {
lastMouseDownTarget = null;
}
});
container.mouseup(function(e) {
let same = (e.target == lastMouseDownTarget);
lastMouseDownTarget = null;
if (same && !self.isDragging) {
if (gBrowser.selectedTab.pinned &&
UI.getActiveTab() != self.getActiveTab() &&
self.getChildren().length > 0) {
UI.setActive(self, { dontSetActiveTabInGroup: true });
UI.goToTab(gBrowser.selectedTab);
} else {
let tabItem = self.getTopChild();
if (tabItem)
tabItem.zoomIn();
else
self.newTab();
}
}
});
let dropIndex = false;
let dropSpaceTimer = null;
// When the _dropSpaceActive flag is turned on on a group, and a tab is
// dragged on top, a space will open up.

View File

@ -405,10 +405,8 @@ Subscribable.prototype = {
// ----------
// Function: addSubscriber
// The given callback will be called when the Subscribable fires the given event.
// The refObject is used to facilitate removal if necessary.
addSubscriber: function Subscribable_addSubscriber(refObject, eventName, callback) {
addSubscriber: function Subscribable_addSubscriber(eventName, callback) {
try {
Utils.assertThrow(refObject, "refObject");
Utils.assertThrow(typeof callback == "function", "callback must be a function");
Utils.assertThrow(eventName && typeof eventName == "string",
"eventName must be a non-empty string");
@ -423,28 +421,17 @@ Subscribable.prototype = {
if (!this.subscribers[eventName])
this.subscribers[eventName] = [];
var subs = this.subscribers[eventName];
var existing = subs.filter(function(element) {
return element.refObject == refObject;
});
if (existing.length) {
Utils.assert(existing.length == 1, 'should only ever be one');
existing[0].callback = callback;
} else {
subs.push({
refObject: refObject,
callback: callback
});
}
let subscribers = this.subscribers[eventName];
if (subscribers.indexOf(callback) == -1)
subscribers.push(callback);
},
// ----------
// Function: removeSubscriber
// Removes the callback associated with refObject for the given event.
removeSubscriber: function Subscribable_removeSubscriber(refObject, eventName) {
// Removes the subscriber associated with the event for the given callback.
removeSubscriber: function Subscribable_removeSubscriber(eventName, callback) {
try {
Utils.assertThrow(refObject, "refObject");
Utils.assertThrow(typeof callback == "function", "callback must be a function");
Utils.assertThrow(eventName && typeof eventName == "string",
"eventName must be a non-empty string");
} catch(e) {
@ -455,9 +442,11 @@ Subscribable.prototype = {
if (!this.subscribers || !this.subscribers[eventName])
return;
this.subscribers[eventName] = this.subscribers[eventName].filter(function(element) {
return element.refObject != refObject;
});
let subscribers = this.subscribers[eventName];
let index = subscribers.indexOf(callback);
if (index > -1)
subscribers.splice(index, 1);
},
// ----------
@ -475,10 +464,10 @@ Subscribable.prototype = {
if (!this.subscribers || !this.subscribers[eventName])
return;
var subsCopy = this.subscribers[eventName].concat();
subsCopy.forEach(function(object) {
let subsCopy = this.subscribers[eventName].concat();
subsCopy.forEach(function (callback) {
try {
object.callback(this, eventInfo);
callback(this, eventInfo);
} catch(e) {
Utils.log(e);
}

View File

@ -144,11 +144,6 @@ body {
/* Other
----------------------------------*/
.newTabButton {
position: absolute !important;
z-index: 99999;
}
.titlebar {
position: absolute;
}

View File

@ -400,21 +400,28 @@ let UI = {
if (this._activeTab) {
this._activeTab.makeDeactive();
this._activeTab.removeSubscriber(this, "close");
this._activeTab.removeSubscriber("close", this._onActiveTabClosed);
}
this._activeTab = tabItem;
if (this._activeTab) {
let self = this;
this._activeTab.addSubscriber(this, "close", function(closedTabItem) {
if (self._activeTab == closedTabItem)
self._setActiveTab(null);
});
this._activeTab.addSubscriber("close", this._onActiveTabClosed);
this._activeTab.makeActive();
}
},
// ----------
// Function: _onActiveTabClosed
// Handles when the currently active tab gets closed.
//
// Parameters:
// - the <TabItem> that is closed
_onActiveTabClosed: function UI__onActiveTabClosed(tabItem){
if (UI._activeTab == tabItem)
UI._setActiveTab(null);
},
// ----------
// Function: setActive
// Sets the active tab item or group item
@ -438,6 +445,13 @@ let UI = {
}
},
// ----------
// Function: clearActiveTab
// Sets the active tab to 'null'.
clearActiveTab: function UI_clearActiveTab() {
this._setActiveTab(null);
},
// ----------
// Function: isTabViewVisible
// Returns true if the TabView UI is currently shown.
@ -470,7 +484,6 @@ let UI = {
var self = this;
var currentTab = this._currentTab;
var item = null;
this._reorderTabItemsOnShow.forEach(function(groupItem) {
groupItem.reorderTabItemsBasedOnTabOrder();
@ -495,7 +508,7 @@ let UI = {
Storage.saveVisibilityData(gWindow, "true");
if (zoomOut && currentTab && currentTab._tabViewTabItem) {
item = currentTab._tabViewTabItem;
let item = currentTab._tabViewTabItem;
// If there was a previous currentTab we want to animate
// its thumbnail (canvas) for the zoom out.
// Note that we start the animation on the chrome thread.
@ -516,6 +529,7 @@ let UI = {
TabItems.resumePainting();
});
} else {
self.clearActiveTab();
dispatchEvent(event);
// Flush pending updates

View File

@ -212,6 +212,10 @@ _BROWSER_FILES = \
browser_clearplugindata_noage.html \
browser_popupUI.js \
browser_sanitizeDialog.js \
browser_save_video.js \
bug564387.html \
bug564387_video1.ogv \
bug564387_video1.ogv^headers^ \
browser_scope.js \
browser_selectTabAtIndex.js \
browser_tab_dragdrop.js \

View File

@ -46,6 +46,10 @@ function testURL(url, loadFunc, endFunc) {
loadFunc(url);
addPageShowListener(function () {
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
is(fm.focusedElement, null, "should be no focused element");
is(fm.focusedWindow, gBrowser.contentWindow, "content window should be focused");
ok(!gBrowser.contentPrincipal.equals(pagePrincipal),
"load of " + url + " by " + loadFunc.name + " should produce a page with a different principal");
endFunc();

View File

@ -78,5 +78,14 @@ function test() {
testPosition(7, 8, "blank tab without referrer opens at the end");
testPosition(8, 9, "tab without referrer opens at the end");
gBrowser.selectedTab = tabs[0];
gBrowser.pinTab(gBrowser.selectedTab);
addTab("http://mochi.test:8888/#8", gBrowser.currentURI);
testPosition(9, 10, "tab with referrer should open at the end when the selected tab is pinned");
gBrowser.selectedTab = tabs[9];
gBrowser.removeTab(tabs.pop());
is(gBrowser.selectedTab, tabs[0],
"opening a tab from a pinned tab, selecting it and closing it should go back to the pinned tab");
tabs.forEach(gBrowser.removeTab, gBrowser);
}

View File

@ -0,0 +1,90 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/**
* TestCase for bug 564387
* <https://bugzilla.mozilla.org/show_bug.cgi?id=564387>
*/
function test() {
waitForExplicitFinish();
gBrowser.loadURI("http://mochi.test:8888/browser/browser/base/content/test/bug564387.html");
registerCleanupFunction(function () {
gBrowser.addTab();
gBrowser.removeCurrentTab();
});
gBrowser.addEventListener("pageshow", function pageShown(event) {
if (event.target.location == "about:blank")
return;
gBrowser.removeEventListener("pageshow", pageShown);
executeSoon(function () {
document.addEventListener("popupshown", contextMenuOpened);
var video1 = gBrowser.contentDocument.getElementById("video1");
EventUtils.synthesizeMouseAtCenter(video1,
{ type: "contextmenu", button: 2 },
gBrowser.contentWindow);
});
});
function contextMenuOpened(event) {
event.currentTarget.removeEventListener("popupshown", contextMenuOpened);
// Create the folder the video will be saved into.
var destDir = createTemporarySaveDirectory();
mockFilePickerSettings.destDir = destDir;
mockFilePickerSettings.filterIndex = 1; // kSaveAsType_URL
mockFilePickerRegisterer.register();
mockTransferCallback = onTransferComplete;
mockTransferRegisterer.register();
registerCleanupFunction(function () {
mockTransferRegisterer.unregister();
mockFilePickerRegisterer.unregister();
destDir.remove(true);
});
// Select "Save Video As" option from context menu
var saveVideoCommand = document.getElementById("context-savevideo");
saveVideoCommand.doCommand();
event.target.hidePopup();
}
function onTransferComplete(downloadSuccess) {
ok(downloadSuccess, "Video file should have been downloaded successfully");
// Read the name of the saved file.
var fileName = mockFilePickerResults.selectedFile.leafName;
is(fileName, "Bug564387-expectedName.ogv",
"Video file name is correctly retrieved from Content-Disposition http header");
finish();
}
}
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript("chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockTransfer.js",
this);
Cc["@mozilla.org/moz/jssubscript-loader;1"]
.getService(Ci.mozIJSSubScriptLoader)
.loadSubScript("chrome://mochitests/content/browser/toolkit/content/tests/browser/common/mockFilePicker.js",
this);
function createTemporarySaveDirectory() {
var saveDir = Cc["@mozilla.org/file/directory_service;1"]
.getService(Ci.nsIProperties)
.get("TmpD", Ci.nsIFile);
saveDir.append("testsavedir");
if (!saveDir.exists())
saveDir.create(Ci.nsIFile.DIRECTORY_TYPE, 0755);
return saveDir;
}

View File

@ -0,0 +1,11 @@
<html>
<!-- https://bugzilla.mozilla.org/show_bug.cgi?id=564387 -->
<head>
<title> Bug 564387 test</title>
</head>
<body>
Testing for Mozilla Bug: 564387
<br>
<video src="bug564387_video1.ogv" id="video1"> </video>
</body>
</html>

Binary file not shown.

View File

@ -0,0 +1,3 @@
Content-Disposition: filename="Bug564387-expectedName.ogv"
Content-Type: video/ogg

View File

@ -149,6 +149,8 @@ _BROWSER_FILES = \
browser_tabview_bug662266.js \
browser_tabview_bug663421.js \
browser_tabview_bug665502.js \
browser_tabview_bug669694.js \
browser_tabview_click_group.js \
browser_tabview_dragdrop.js \
browser_tabview_exit_button.js \
browser_tabview_expander.js \
@ -166,7 +168,6 @@ _BROWSER_FILES = \
head.js \
search1.html \
search2.html \
test_bug599626.html \
test_bug600645.html \
test_bug644097.html \
$(NULL)

View File

@ -1,53 +1,37 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let contentWindow;
let groupItemTwoId;
function test() {
waitForExplicitFinish();
window.addEventListener("tabviewshown", setup, false);
TabView.toggle();
registerCleanupFunction(function() {
while (gBrowser.tabs[1])
gBrowser.removeTab(gBrowser.tabs[1]);
hideTabView(function() {});
});
gBrowser.loadOneTab("about:blank", { inBackground: true });
showTabView(setup);
}
function setup() {
window.removeEventListener("tabviewshown", setup, false);
registerCleanupFunction(function() {
let groupItem = contentWindow.GroupItems.groupItem(groupItemTwoId);
if (groupItem)
closeGroupItem(groupItem, function() {});
});
let contentWindow = document.getElementById("tab-view").contentWindow;
let contentWindow = TabView.getContentWindow();
is(contentWindow.GroupItems.groupItems.length, 1, "Has only one group");
let groupItemOne = contentWindow.GroupItems.groupItems[0];
// add a blank tab to group one.
createNewTabItemInGroupItem(groupItemOne, contentWindow, function() {
is(groupItemOne.getChildren().length, 2, "Group one has 2 tab items");
is(groupItemOne.getChildren().length, 2, "Group one has 2 tab items");
// create group two with a blank tab.
let groupItemTwo = createEmptyGroupItem(contentWindow, 250, 250, 40);
createNewTabItemInGroupItem(groupItemTwo, contentWindow, function() {
// start the first test.
testGroups(groupItemOne, groupItemTwo, contentWindow);
});
});
}
function createNewTabItemInGroupItem(groupItem, contentWindow, callback) {
// click on the + button to create a blank tab in group item
let newTabButton = groupItem.container.getElementsByClassName("newTabButton");
ok(newTabButton[0], "New tab button exists");
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
ok(!TabView.isVisible(), "Tab View is hidden because we just opened a tab");
TabView.toggle();
};
let onTabViewShown = function() {
window.removeEventListener("tabviewshown", onTabViewShown, false);
ok(TabView.isVisible(), "Tab View is visible");
callback();
};
window.addEventListener("tabviewhidden", onTabViewHidden, false);
window.addEventListener("tabviewshown", onTabViewShown, false);
EventUtils.sendMouseEvent({ type: "click" }, newTabButton[0], contentWindow);
let groupItemTwo = createGroupItemWithBlankTabs(window, 250, 250, 40, 1);
groupItemTwoId = groupItemTwo.id;
testGroups(groupItemOne, groupItemTwo, contentWindow);
}
function testGroups(groupItemOne, groupItemTwo, contentWindow) {
@ -58,8 +42,8 @@ function testGroups(groupItemOne, groupItemTwo, contentWindow) {
"The first tab item in group two is active");
let tabItem = groupItemOne.getChild(1);
tabItem.addSubscriber(tabItem, "tabRemoved", function() {
tabItem.removeSubscriber(tabItem, "tabRemoved");
tabItem.addSubscriber("tabRemoved", function onTabRemoved() {
tabItem.removeSubscriber("tabRemoved", onTabRemoved);
is(groupItemOne.getChildren().length, 1,
"The num of childen in group one is 1");
@ -70,15 +54,12 @@ function testGroups(groupItemOne, groupItemTwo, contentWindow) {
is(contentWindow.UI.getActiveTab(), groupItemOne.getChild(0),
"The first tab item in group one is active");
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
whenTabViewIsHidden(function() {
is(groupItemOne.getChildren().length, 2,
"The num of childen in group one is 2");
// clean up and finish
groupItemTwo.addSubscriber(groupItemTwo, "close", function() {
groupItemTwo.removeSubscriber(groupItemTwo, "close");
closeGroupItem(groupItemTwo, function() {
gBrowser.removeTab(groupItemOne.getChild(1).tab);
is(contentWindow.GroupItems.groupItems.length, 1, "Has only one group");
is(groupItemOne.getChildren().length, 1,
@ -87,10 +68,7 @@ function testGroups(groupItemOne, groupItemTwo, contentWindow) {
finish();
});
gBrowser.removeTab(groupItemTwo.getChild(0).tab);
groupItemTwo.close();
}
window.addEventListener("tabviewhidden", onTabViewHidden, false);
});
EventUtils.synthesizeKey("t", { accelKey: true });
});
// close a tab item in group one

View File

@ -3,6 +3,7 @@
let originalTab;
let newTabOne;
let groupItemTwoId;
function test() {
waitForExplicitFinish();
@ -11,10 +12,20 @@ function test() {
// add a tab to the existing group.
newTabOne = gBrowser.addTab();
let onTabviewShown = function() {
window.removeEventListener("tabviewshown", onTabviewShown, false);
registerCleanupFunction(function() {
while (gBrowser.tabs[1])
gBrowser.removeTab(gBrowser.tabs[1]);
hideTabView(function() {});
});
let contentWindow = document.getElementById("tab-view").contentWindow;
showTabView(function() {
let contentWindow = TabView.getContentWindow();
registerCleanupFunction(function() {
let groupItem = contentWindow.GroupItems.groupItem(groupItemTwoId);
if (groupItem)
closeGroupItem(groupItem, function() {});
});
is(contentWindow.GroupItems.groupItems.length, 1,
"There is one group item on startup");
@ -25,22 +36,13 @@ function test() {
"The currently selected tab should be the first tab in the groupItemOne");
// create another group with a tab.
let groupItemTwo = createEmptyGroupItem(contentWindow, 300, 300, 200);
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
let groupItemTwo = createGroupItemWithBlankTabs(window, 300, 300, 200, 1);
groupItemTwoId = groupItemTwoId;
hideTabView(function() {
// start the test
testGroupSwitch(contentWindow, groupItemOne, groupItemTwo);
};
window.addEventListener("tabviewhidden", onTabViewHidden, false);
// click on the + button
let newTabButton = groupItemTwo.container.getElementsByClassName("newTabButton");
ok(newTabButton[0], "New tab button exists");
EventUtils.sendMouseEvent({ type: "click" }, newTabButton[0], contentWindow);
};
window.addEventListener("tabviewshown", onTabviewShown, false);
TabView.toggle();
});
});
}
function testGroupSwitch(contentWindow, groupItemOne, groupItemTwo) {

View File

@ -46,8 +46,8 @@ function onTabViewWindowLoaded() {
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");
group.addSubscriber("close", function onClose() {
group.removeSubscriber("close", onClose);
ok(group.isEmpty(), "The group is empty again");

View File

@ -29,14 +29,14 @@ function testCloseLastGroup() {
{ type: "click" }, groupItem.$undoContainer[0], contentWindow);
};
groupItem.addSubscriber(groupItem, "groupHidden", function() {
groupItem.removeSubscriber(groupItem, "groupHidden");
groupItem.addSubscriber("groupHidden", function onHidden() {
groupItem.removeSubscriber("groupHidden", onHidden);
// it should still stay after 3 ms.
setTimeout(checkExistence, 3);
});
groupItem.addSubscriber(groupItem, "groupShown", function() {
groupItem.removeSubscriber(groupItem, "groupShown");
groupItem.addSubscriber("groupShown", function onShown() {
groupItem.removeSubscriber("groupShown", onShown);
let endGame = function() {
window.removeEventListener("tabviewhidden", endGame, false);

View File

@ -23,75 +23,49 @@ function test() {
}
function testOne() {
whenSearchEnabledAndDisabled(testTwo);
hideSearchWhenSearchEnabled(testTwo);
// press cmd/ctrl F
EventUtils.synthesizeKey("f", {accelKey: true}, cw);
}
function testTwo() {
whenSearchEnabledAndDisabled(testThree);
hideSearchWhenSearchEnabled(testThree);
// press /
EventUtils.synthesizeKey("VK_SLASH", {}, cw);
}
function testThree() {
let onTabViewShown = function () {
is(cw.UI.getActiveTab(), groupItem.getChild(0),
"The active tab is newly created tab item");
ok(win.TabView.isVisible(), "Tab View is visible");
// create another group with a tab.
let groupItem = createGroupItemWithBlankTabs(win, 300, 300, 200, 1);
is(cw.UI.getActiveTab(), groupItem.getChild(0),
"The active tab is newly created tab item");
let onSearchEnabled = function () {
let doc = cw.document;
let searchBox = cw.iQ("#searchbox");
let hasFocus = doc.hasFocus() && doc.activeElement == searchBox[0];
ok(hasFocus, "The search box has focus");
whenSearchIsEnabled(function () {
let doc = cw.document;
let searchBox = cw.iQ("#searchbox");
let hasFocus = doc.hasFocus() && doc.activeElement == searchBox[0];
ok(hasFocus, "The search box has focus");
let tab = win.gBrowser.tabs[1];
searchBox.val(tab._tabViewTabItem.$tabTitle[0].innerHTML);
let tab = win.gBrowser.tabs[1];
searchBox.val(tab._tabViewTabItem.$tabTitle[0].innerHTML);
cw.performSearch();
cw.performSearch();
whenTabViewIsHidden(function () {
is(tab, win.gBrowser.selectedTab, "The search result tab is shown");
waitForFocus(finish);
}, win);
whenTabViewIsHidden(function () {
is(tab, win.gBrowser.selectedTab, "The search result tab is shown");
finish()
}, win);
// use the tabview menu (the same as pressing cmd/ctrl + e)
win.document.getElementById("menu_tabview").doCommand();
};
whenSearchEnabled(onSearchEnabled);
EventUtils.synthesizeKey("VK_SLASH", {}, cw);
};
whenTabViewIsHidden(function () {
showTabView(onTabViewShown, win);
// use the tabview menu (the same as pressing cmd/ctrl + e)
win.document.getElementById("menu_tabview").doCommand();
}, win);
// click on the + button
let groupItem = createEmptyGroupItem(cw, 300, 300, 200);
let newTabButton = groupItem.container.getElementsByClassName("newTabButton");
ok(newTabButton[0], "New tab button exists");
EventUtils.sendMouseEvent({type: "click"}, newTabButton[0], cw);
EventUtils.synthesizeKey("VK_SLASH", {}, cw);
}
function whenSearchEnabledAndDisabled(callback) {
whenSearchEnabled(function () {
whenSearchDisabled(callback);
cw.hideSearch();
});
function hideSearchWhenSearchEnabled(callback) {
whenSearchIsEnabled(function() {
hideSearch(callback, win);
}, win);
}
function whenSearchEnabled(callback) {
cw.addEventListener("tabviewsearchenabled", function onSearchEnabled() {
cw.removeEventListener("tabviewsearchenabled", onSearchEnabled, false);
callback();
}, false);
}
function whenSearchDisabled(callback) {
cw.addEventListener("tabviewsearchdisabled", function onSearchDisabled() {
cw.removeEventListener("tabviewsearchdisabled", onSearchDisabled, false);
callback();
}, false);
}

View File

@ -26,8 +26,8 @@ function onTabViewWindowLoaded() {
ok(group1.getChildren().some(function(child) child == tab1Item), "The tab was made in our new group");
is(group1.getChildren().length, 1, "Only one tab in the first group");
group1.addSubscriber(group1, "close", function() {
group1.removeSubscriber(group1, "close");
group1.addSubscriber("close", function onClose() {
group1.removeSubscriber("close", onClose);
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
@ -45,16 +45,11 @@ function onTabViewWindowLoaded() {
});
});
group1.addSubscriber(group1, "groupHidden", function() {
group1.removeSubscriber(group1, "groupHidden");
hideGroupItem(group1, function () {
// close undo group
let closeButton = group1.$undoContainer.find(".close");
EventUtils.sendMouseEvent(
{ type: "click" }, closeButton[0], contentWindow);
});
// Get rid of the group and its children
group1.closeAll();
}

View File

@ -35,8 +35,8 @@ function setupTwo(win) {
// force all canvases to update, and hook in imageData save detection
tabItems.forEach(function(tabItem) {
contentWindow.TabItems.update(tabItem.tab);
tabItem.addSubscriber(tabItem, "savedCachedImageData", function(item) {
item.removeSubscriber(item, "savedCachedImageData");
tabItem.addSubscriber("savedCachedImageData", function onSaved(item) {
item.removeSubscriber("savedCachedImageData", onSaved);
--numTabsToSave;
});
});
@ -87,8 +87,8 @@ function setupTwo(win) {
let count = tabItems.length;
tabItems.forEach(function(tabItem) {
tabItem.addSubscriber(tabItem, "loadedCachedImageData", function() {
tabItem.removeSubscriber(tabItem, "loadedCachedImageData");
tabItem.addSubscriber("loadedCachedImageData", function onLoaded() {
tabItem.removeSubscriber("loadedCachedImageData", onLoaded);
ok(tabItem.isShowingCachedData(),
"Tab item is showing cached data and is just connected. " +
tabItem.tab.linkedBrowser.currentURI.spec);

View File

@ -1,161 +1,87 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let handleDialog;
let timer; // keep in outer scope so it's not GC'd before firing
const TEST_URL = 'data:text/html,<script>window.onbeforeunload=' +
'function(e){e.returnValue="?"}</script>';
function test() {
waitForExplicitFinish();
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
TabView.toggle();
showTabView(onTabViewShown);
}
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
let contentWindow = document.getElementById("tab-view").contentWindow;
function onTabViewShown() {
let contentWindow = TabView.getContentWindow();
let groupItemOne = contentWindow.GroupItems.getActiveGroupItem();
let groupItemTwo = createGroupItemWithTabs(window, 300, 300, 10, [TEST_URL]);
// Create a group and make it active
let box = new contentWindow.Rect(10, 10, 300, 300);
let groupItemTwo = new contentWindow.GroupItem([], { bounds: box });
contentWindow.UI.setActive(groupItemTwo);
let testTab =
gBrowser.addTab(
"http://mochi.test:8888/browser/browser/base/content/test/tabview/test_bug599626.html");
let browser = gBrowser.getBrowserForTab(testTab);
let onLoad = function() {
browser.removeEventListener("load", onLoad, true);
afterAllTabsLoaded(function () {
testStayOnPage(contentWindow, groupItemOne, groupItemTwo);
}
browser.addEventListener("load", onLoad, true);
});
}
function testStayOnPage(contentWindow, groupItemOne, groupItemTwo) {
setupAndRun(contentWindow, groupItemOne, groupItemTwo, function(doc) {
groupItemTwo.addSubscriber(groupItemTwo, "groupShown", function() {
groupItemTwo.removeSubscriber(groupItemTwo, "groupShown");
whenDialogOpened(function (dialog) {
groupItemTwo.addSubscriber("groupShown", function onShown() {
groupItemTwo.removeSubscriber("groupShown", onShown);
is(gBrowser.tabs.length, 2,
"The total number of tab is 2 when staying on the page");
is(contentWindow.TabItems.getItems().length, 2,
"The total number of tab items is 2 when staying on the page");
let onTabViewShown = function() {
window.removeEventListener("tabviewshown", onTabViewShown, false);
showTabView(function () {
// start the next test
testLeavePage(contentWindow, groupItemOne, groupItemTwo);
};
window.addEventListener("tabviewshown", onTabViewShown, false);
TabView.toggle();
});
});
// stay on page
doc.documentElement.getButton("cancel").click();
dialog.cancelDialog();
});
closeGroupItem(groupItemTwo);
}
function testLeavePage(contentWindow, groupItemOne, groupItemTwo) {
setupAndRun(contentWindow, groupItemOne, groupItemTwo, function(doc) {
whenDialogOpened(function (dialog) {
// clean up and finish the test
groupItemTwo.addSubscriber(groupItemTwo, "close", function() {
groupItemTwo.removeSubscriber(groupItemTwo, "close");
groupItemTwo.addSubscriber("close", function onClose() {
groupItemTwo.removeSubscriber("close", onClose);
is(gBrowser.tabs.length, 1,
"The total number of tab is 1 after leaving the page");
is(contentWindow.TabItems.getItems().length, 1,
"The total number of tab items is 1 after leaving the page");
let endGame = function() {
window.removeEventListener("tabviewhidden", endGame, false);
finish();
};
window.addEventListener("tabviewhidden", endGame, false);
hideTabView(finish);
});
// Leave page
doc.documentElement.getButton("accept").click();
dialog.acceptDialog();
});
closeGroupItem(groupItemTwo);
}
function setupAndRun(contentWindow, groupItemOne, groupItemTwo, callback) {
let closeButton = groupItemTwo.container.getElementsByClassName("close");
ok(closeButton[0], "Group close button exists");
// click the close button
EventUtils.sendMouseEvent({ type: "click" }, closeButton[0], contentWindow);
// ----------
function whenDialogOpened(callback) {
let listener = {
onCloseWindow: function () {},
onWindowTitleChange: function () {},
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
onOpenWindow: function (xulWin) {
let domWin = xulWin.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow);
handleDialog = function(doc) {
callback(doc);
};
startCallbackTimer();
whenWindowLoaded(domWin, function () {
let dialog = domWin.document.querySelector("dialog");
if (dialog) {
Services.wm.removeListener(listener);
callback(dialog);
}
});
}
};
window.addEventListener("tabviewhidden", onTabViewHidden, false);
let tabItem = groupItemOne.getChild(0);
tabItem.zoomIn();
}
// Copied from http://mxr.mozilla.org/mozilla-central/source/toolkit/components/places/tests/mochitest/prompt_common.js
let observer = {
QueryInterface : function (iid) {
const interfaces = [Ci.nsIObserver, Ci.nsISupports, Ci.nsISupportsWeakReference];
if (!interfaces.some( function(v) { return iid.equals(v) } ))
throw Components.results.NS_ERROR_NO_INTERFACE;
return this;
},
observe : function (subject, topic, data) {
let doc = getDialogDoc();
if (doc)
handleDialog(doc);
else
startCallbackTimer(); // try again in a bit
}
};
function startCallbackTimer() {
// Delay before the callback twiddles the prompt.
const dialogDelay = 10;
// Use a timer to invoke a callback to twiddle the authentication dialog
timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
timer.init(observer, dialogDelay, Ci.nsITimer.TYPE_ONE_SHOT);
}
function getDialogDoc() {
// Find the <browser> which contains notifyWindow, by looking
// through all the open windows and all the <browsers> in each.
let wm = Cc["@mozilla.org/appshell/window-mediator;1"].
getService(Ci.nsIWindowMediator);
let enumerator = wm.getXULWindowEnumerator(null);
while (enumerator.hasMoreElements()) {
let win = enumerator.getNext();
let windowDocShell = win.QueryInterface(Ci.nsIXULWindow).docShell;
let containedDocShells = windowDocShell.getDocShellEnumerator(
Ci.nsIDocShellTreeItem.typeChrome,
Ci.nsIDocShell.ENUMERATE_FORWARDS);
while (containedDocShells.hasMoreElements()) {
// Get the corresponding document for this docshell
let childDocShell = containedDocShells.getNext();
// We don't want it if it's not done loading.
if (childDocShell.busyFlags != Ci.nsIDocShell.BUSY_FLAGS_NONE)
continue;
let childDoc = childDocShell.QueryInterface(Ci.nsIDocShell).
contentViewer.DOMDocument;
if (childDoc.location.href == "chrome://global/content/commonDialog.xul")
return childDoc;
}
}
return null;
Services.wm.addListener(listener);
}

View File

@ -26,8 +26,8 @@ function test() {
cw.TabItems.pausePainting();
groupItem.getChildren().forEach(function (tabItem) {
tabItem.addSubscriber(tabItem, "updated", function () {
tabItem.removeSubscriber(tabItem, "updated");
tabItem.addSubscriber("updated", function onUpdated() {
tabItem.removeSubscriber("updated", onUpdated);
tabItem._testLastTabUpdateTime = tabItem._lastTabUpdateTime;
if (--numTabsToUpdate)

View File

@ -36,12 +36,7 @@ function test() {
let groupItem = cw.GroupItems.groupItems[1];
is(groupItem.getTitle(), "t", "new groupItem's title is correct");
groupItem.addSubscriber(groupItem, "close", function () {
groupItem.removeSubscriber(groupItem, "close");
executeSoon(callback);
});
groupItem.closeAll();
closeGroupItem(groupItem, callback);
};
let testDragOutOfGroup = function (callback) {

View File

@ -28,8 +28,7 @@ function onTabViewWindowLoaded() {
ok(TabView.isVisible(), "Tab View is still visible after removing a tab");
is(groupItems[0].getChildren().length, 2, "The group has two tab items");
tabTwo = undoCloseTab(0);
whenTabIsReconnected(tabTwo, function() {
restoreTab(function (tabTwo) {
ok(TabView.isVisible(), "Tab View is still visible after restoring a tab");
is(groupItems[0].getChildren().length, 3, "The group still has three tab items");
@ -41,18 +40,3 @@ function onTabViewWindowLoaded() {
});
});
}
// ----------
function whenTabIsReconnected(tab, callback) {
let tabItem = tab._tabViewTabItem;
if (tabItem._reconnected) {
callback();
return;
}
tabItem.addSubscriber(tabItem, "reconnected", function () {
tabItem.removeSubscriber(tabItem, "reconnected");
callback();
});
}

View File

@ -18,9 +18,7 @@ function onTabViewWindowLoaded() {
is(gBrowser.tabs.length, 1, "There is one tab on startup");
let groupItem = contentWindow.GroupItems.groupItems[0];
groupItem.addSubscriber(groupItem, "groupHidden", function() {
groupItem.removeSubscriber(groupItem, "groupHidden");
hideGroupItem(groupItem, function () {
let onTabViewHidden = function() {
window.removeEventListener("tabviewhidden", onTabViewHidden, false);
is(contentWindow.GroupItems.groupItems.length, 1,
@ -35,5 +33,4 @@ function onTabViewWindowLoaded() {
TabView.hide();
});
groupItem.closeAll();
}

View File

@ -160,13 +160,13 @@ function test() {
ok(groupItem.isStacked(), 'testExpandedMode: group is stacked');
groupItem.addSubscriber(groupItem, 'expanded', function () {
groupItem.removeSubscriber(groupItem, 'expanded');
groupItem.addSubscriber('expanded', function onGroupExpanded() {
groupItem.removeSubscriber('expanded', onGroupExpanded);
onExpanded();
});
groupItem.addSubscriber(groupItem, 'collapsed', function () {
groupItem.removeSubscriber(groupItem, 'collapsed');
groupItem.addSubscriber('collapsed', function onGroupCollapsed() {
groupItem.removeSubscriber('collapsed', onGroupCollapsed);
onCollapsed();
});

View File

@ -36,8 +36,8 @@ function onTabViewWindowLoaded(win) {
ok(!group.shouldStack(group._children.length), "Group should not stack.");
// PREPARE FINISH:
group.addSubscriber(group, "close", function() {
group.removeSubscriber(group, "close");
group.addSubscriber("close", function onClose() {
group.removeSubscriber("close", onClose);
ok(group.isEmpty(), "The group is empty again");
@ -78,11 +78,7 @@ function onTabViewWindowLoaded(win) {
// Get rid of the group and its children
// The group close will trigger a finish().
group.addSubscriber(group, "groupHidden", function() {
group.removeSubscriber(group, "groupHidden");
group.closeHidden();
});
group.closeAll();
closeGroupItem(group);
}, win);
}, win);
}

View File

@ -20,23 +20,6 @@ function test() {
return groupItem;
}
let hideGroupItem = function (groupItem, callback) {
groupItem.addSubscriber(groupItem, 'groupHidden', function () {
groupItem.removeSubscriber(groupItem, 'groupHidden');
callback();
});
groupItem.closeAll();
}
let closeGroupItem = function (groupItem, callback) {
afterAllTabsLoaded(function () {
hideGroupItem(groupItem, function () {
groupItem.closeHidden();
callback();
});
});
}
let tests = [];
let next = function () {

View File

@ -41,20 +41,6 @@ function test() {
return createTab('about:blank');
}
let restoreTab = function (callback) {
let tab = undoCloseTab(0);
if (tab._tabViewTabItem._reconnected) {
afterAllTabsLoaded(callback);
return;
}
tab._tabViewTabItem.addSubscriber(tab, 'reconnected', function () {
tab._tabViewTabItem.removeSubscriber(tab, 'reconnected');
afterAllTabsLoaded(callback);
});
}
let finishTest = function () {
prefix = 'finish';
assertValidPrerequisites();

View File

@ -39,10 +39,7 @@ function test1() {
ok(!contentWindow.ThumbnailStorage._shouldSaveThumbnail(newTab),
"Should not save the thumbnail for tab");
tabItem.addSubscriber(tabItem, "deniedToCacheImageData", function() {
tabItem.removeSubscriber(tabItem, "deniedToCacheImageData");
test2();
});
whenDeniedToCacheImageData(tabItem, test2);
tabItem.save(true);
HttpRequestObserver.cacheControlValue = null;
});
@ -59,10 +56,7 @@ function test2() {
ok(contentWindow.ThumbnailStorage._shouldSaveThumbnail(newTab),
"Should save the thumbnail for tab");
tabItem.addSubscriber(tabItem, "savedCachedImageData", function() {
tabItem.removeSubscriber(tabItem, "savedCachedImageData");
test3();
});
whenSavedCachedImageData(tabItem, test3);
tabItem.save(true);
});
}
@ -82,11 +76,7 @@ function test3() {
ok(contentWindow.ThumbnailStorage._shouldSaveThumbnail(newTab),
"Should save the thumbnail for tab");
tabItem.addSubscriber(tabItem, "savedCachedImageData", function() {
tabItem.removeSubscriber(tabItem, "savedCachedImageData");
test4();
});
whenSavedCachedImageData(tabItem, test4);
tabItem.save(true);
});
}
@ -104,11 +94,7 @@ function test4() {
ok(contentWindow.ThumbnailStorage._shouldSaveThumbnail(newTab),
"Should save the thumbnail for tab");
tabItem.addSubscriber(tabItem, "savedCachedImageData", function() {
tabItem.removeSubscriber(tabItem, "savedCachedImageData");
test5();
});
whenSavedCachedImageData(tabItem, test5);
tabItem.save(true);
});
}
@ -124,9 +110,7 @@ function test5() {
ok(!contentWindow.ThumbnailStorage._shouldSaveThumbnail(newTab),
"Should not the thumbnail for tab");
tabItem.addSubscriber(tabItem, "deniedToCacheImageData", function() {
tabItem.removeSubscriber(tabItem, "deniedToCacheImageData");
whenDeniedToCacheImageData(tabItem, function () {
hideTabView(function () {
gBrowser.removeTab(gBrowser.tabs[1]);
finish();
@ -154,3 +138,17 @@ let HttpRequestObserver = {
Services.obs.removeObserver(this, "http-on-examine-response");
}
};
function whenSavedCachedImageData(tabItem, callback) {
tabItem.addSubscriber("savedCachedImageData", function onSaved() {
tabItem.removeSubscriber("savedCachedImageData", onSaved);
callback();
});
}
function whenDeniedToCacheImageData(tabItem, callback) {
tabItem.addSubscriber("deniedToCacheImageData", function onDenied() {
tabItem.removeSubscriber("deniedToCacheImageData", onDenied);
callback();
});
}

View File

@ -19,11 +19,11 @@ function test() {
afterAllTabsLoaded(function () {
tabItem = tab._tabViewTabItem;
tabItem.addSubscriber(tabItem, "savedCachedImageData", function () {
tabItem.removeSubscriber(tabItem, "savedCachedImageData");
tabItem.addSubscriber("savedCachedImageData", function onSaved() {
tabItem.removeSubscriber("savedCachedImageData", onSaved);
tabItem.addSubscriber(tabItem, "loadedCachedImageData", function () {
tabItem.removeSubscriber(tabItem, "loadedCachedImageData");
tabItem.addSubscriber("loadedCachedImageData", function onLoaded() {
tabItem.removeSubscriber("loadedCachedImageData", onLoaded);
ok(tabItem.isShowingCachedData(), 'tabItem shows cached data');
testChangeUrlAfterReconnect();

View File

@ -28,10 +28,10 @@ function onTabViewWindowLoaded(win) {
function check() {
if (groupOrTab == 'group') {
group.removeSubscriber(group, "groupHidden", check);
group.removeSubscriber("groupHidden", check);
group.closeHidden();
} else
tab.removeSubscriber(tab, "tabRemoved", check);
tab.removeSubscriber("tabRemoved", check);
is(contentWindow.GroupItems.getActiveGroupItem(), originalGroup,
"The original group is active.");
@ -42,10 +42,10 @@ function onTabViewWindowLoaded(win) {
}
if (groupOrTab == 'group') {
group.addSubscriber(group, "groupHidden", check);
group.addSubscriber("groupHidden", check);
group.closeAll();
} else {
tab.addSubscriber(tab, "tabRemoved", check);
tab.addSubscriber("tabRemoved", check);
tab.close();
}
}
@ -58,4 +58,4 @@ function onTabViewWindowLoaded(win) {
finish();
});
});
}
}

View File

@ -25,20 +25,6 @@ function test() {
return cw.GroupItems.groupItems[index];
}
let restoreTab = function (callback) {
let tab = undoCloseTab(0);
if (tab._tabViewTabItem._reconnected) {
callback();
return;
}
tab._tabViewTabItem.addSubscriber(tab, 'reconnected', function () {
tab._tabViewTabItem.removeSubscriber(tab, 'reconnected');
afterAllTabsLoaded(callback);
});
}
let activateFirstGroupItem = function () {
let activeTabItem = getGroupItem(0).getChild(0);
cw.GroupItems.updateActiveGroupItemAndTabBar(activeTabItem);

View File

@ -19,12 +19,7 @@ function test() {
// show the undo close group button
let group = contentWindow.GroupItems.groupItems[0];
group.closeAll();
group.addSubscriber(group, "groupHidden", function() {
group.removeSubscriber(group, "groupHidden");
restore(group.id);
});
hideGroupItem(group, function () restore(group.id));
},
function(newWin) {
win = newWin;

View File

@ -65,11 +65,6 @@ function test() {
simulateDoubleClick(container, 2);
assertNumberOfTabs(1);
// simulate double click with left mouse button
let container = groupItem.container;
simulateDoubleClick(container);
assertNumberOfTabs(1);
groupItem.close();
hideTabView(finishTest);
}

View File

@ -43,13 +43,13 @@ function test() {
}
let testDragOutOfExpandedStackedGroup = function () {
groupItem.addSubscriber(groupItem, "expanded", function () {
groupItem.removeSubscriber(groupItem, "expanded");
groupItem.addSubscriber("expanded", function onExpanded() {
groupItem.removeSubscriber("expanded", onExpanded);
dragTabItem();
});
groupItem.addSubscriber(groupItem, "collapsed", function () {
groupItem.removeSubscriber(groupItem, "collapsed");
groupItem.addSubscriber("collapsed", function onCollapsed() {
groupItem.removeSubscriber("collapsed", onCollapsed);
let secondGroup = cw.GroupItems.groupItems[1];
closeGroupItem(secondGroup, function () hideTabView(finishTest));

View File

@ -71,8 +71,8 @@ function testCreateTabAndThen(callback) {
registerCleanupFunction(function () gBrowser.removeTab(tab))
tabItem.addSubscriber(tabItem, "zoomedIn", function() {
tabItem.removeSubscriber(tabItem, "zoomedIn");
tabItem.addSubscriber("zoomedIn", function onZoomedIn() {
tabItem.removeSubscriber("zoomedIn", onZoomedIn);
is(gBrowser.selectedTab, tab,
"The selected tab is the same as the newly opened tab");

View File

@ -21,8 +21,8 @@ function test() {
cw.TabItems.pausePainting();
tabItem.addSubscriber(tabItem, "updated", function () {
tabItem.removeSubscriber(tabItem, "updated");
tabItem.addSubscriber("updated", function onUpdated() {
tabItem.removeSubscriber("updated", onUpdated);
ok(isIdle, "tabItem is updated only when UI is idle");
finish();
});

View File

@ -8,8 +8,8 @@ function test() {
let cw = win.TabView.getContentWindow();
let tabItem = win.gBrowser.tabs[0]._tabViewTabItem;
tabItem.addSubscriber(tabItem, "savedCachedImageData", function () {
tabItem.removeSubscriber(tabItem, "savedCachedImageData");
tabItem.addSubscriber("savedCachedImageData", function onSaved() {
tabItem.removeSubscriber("savedCachedImageData", onSaved);
ok(cw.UI.isDOMWindowClosing, "dom window is closing");
waitForFocus(finish);

View File

@ -18,8 +18,8 @@ function test() {
ok(groupItem.isStacked(), "groupItem is now stacked");
is(win.gBrowser.tabs.length, 5, "we have five tabs");
groupItem.addSubscriber(groupItem, "expanded", function onExpanded() {
groupItem.removeSubscriber(groupItem, "expanded");
groupItem.addSubscriber("expanded", function onExpanded() {
groupItem.removeSubscriber("expanded", onExpanded);
ok(groupItem.expanded, "groupItem is expanded");
let bounds = children[1].getBounds();

View File

@ -0,0 +1,48 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
waitForExplicitFinish();
function onLoad(win) {
registerCleanupFunction(function () win.close());
let tab = win.gBrowser.addTab();
win.gBrowser.pinTab(tab);
}
function onShow(win) {
let tabs = win.gBrowser.tabs;
// zoom into normal tab
zoomIn(tabs[1], function () {
is(win.gBrowser.selectedTab, tabs[1], "normal tab is selected");
// select app tab
win.gBrowser.selectedTab = tabs[0];
toggleTabView(win, function () {
is(win.gBrowser.selectedTab, tabs[0], "app tab is selected");
finish();
});
});
}
newWindowWithTabView(onShow, onLoad);
}
// ----------
function zoomIn(tab, callback) {
whenTabViewIsHidden(function () {
executeSoon(callback);
}, tab.ownerDocument.defaultView);
tab._tabViewTabItem.zoomIn();
}
// ----------
function toggleTabView(win, callback) {
showTabView(function () {
hideTabView(callback, win);
}, win);
}

View File

@ -0,0 +1,68 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
let contentWindow;
let groupItem;
let groupItemId;
function test() {
waitForExplicitFinish();
registerCleanupFunction(function() {
contentWindow.gPrefBranch.clearUserPref("animate_zoom");
let createdGroupItem = contentWindow.GroupItems.groupItem(groupItemId)
if (createdGroupItem)
closeGroupItem(createdGroupItem, function() {});
hideTabView(function() {});
});
showTabView(function() {
contentWindow = TabView.getContentWindow();
groupItem = createEmptyGroupItem(contentWindow, 300, 300, 200);
groupItemId = groupItem.id;
testMouseClickOnEmptyGroupItem();
});
}
function testMouseClickOnEmptyGroupItem() {
whenTabViewIsHidden(function() {
is(groupItem.getChildren().length, 1, "The group item contains one tab item now");
showTabView(testDraggingWithinGroupItem);
});
is(groupItem.getChildren().length, 0, "The group item doesn't contain any tab items");
EventUtils.sendMouseEvent({ type: "mousedown" }, groupItem.container, contentWindow);
EventUtils.sendMouseEvent({ type: "mouseup" }, groupItem.container, contentWindow);
}
function testDraggingWithinGroupItem() {
let target = groupItem.container;
contentWindow.gPrefBranch.setBoolPref("animate_zoom", false);
// stimulate drag and drop
EventUtils.sendMouseEvent( {type: "mousedown" }, target, contentWindow);
EventUtils.synthesizeMouse(target, 10, 10, { type: "mousemove" }, contentWindow);
ok(groupItem.isDragging, "The group item is being dragged")
EventUtils.sendMouseEvent({ type: "mouseup" }, target, contentWindow);
ok(!groupItem.isDragging, "The dragging is competely");
executeSoon(function() {
ok(TabView.isVisible(), "The tab view is still visible after dragging");
contentWindow.gPrefBranch.clearUserPref("animate_zoom");
testMouseClickOnGroupItem();
});
}
function testMouseClickOnGroupItem() {
whenTabViewIsHidden(function() {
is(groupItem.getChildren().length, 1, "The group item still contains one tab item");
closeGroupItem(groupItem, function() {
hideTabView(finish);
});
});
EventUtils.sendMouseEvent({ type: "mousedown" }, groupItem.container, contentWindow);
EventUtils.sendMouseEvent({ type: "mouseup" }, groupItem.container, contentWindow);
}

View File

@ -24,8 +24,8 @@ function onTabViewWindowLoaded() {
let boxTwo = new contentWindow.Rect(20, 400, 300, 300);
let groupTwo = new contentWindow.GroupItem([], { bounds: boxTwo });
groupOne.addSubscriber(groupOne, "childAdded", function() {
groupOne.removeSubscriber(groupOne, "childAdded");
groupOne.addSubscriber("childAdded", function onChildAdded() {
groupOne.removeSubscriber("childAdded", onChildAdded);
groupTwo.newTab();
});
@ -68,7 +68,7 @@ function addTest(contentWindow, groupOneId, groupTwoId, originalTab) {
Math.round(groupTwoRectCenter.y - tabItemRectCenter.y);
function endGame() {
groupTwo.removeSubscriber(groupTwo, "childAdded");
groupTwo.removeSubscriber("childAdded", endGame);
is(groupOne.getChildren().length, --groupOneTabItemCount,
"The number of children in group one is decreased by 1");
@ -83,14 +83,14 @@ function addTest(contentWindow, groupOneId, groupTwoId, originalTab) {
EventUtils.sendMouseEvent(
{ type: "click" }, closeButton[0], contentWindow);
};
groupTwo.addSubscriber(groupTwo, "close", function() {
groupTwo.removeSubscriber(groupTwo, "close");
finish();
groupTwo.addSubscriber("close", function onClose() {
groupTwo.removeSubscriber("close", onClose);
finish();
});
window.addEventListener("tabviewhidden", onTabViewHidden, false);
gBrowser.selectedTab = originalTab;
}
groupTwo.addSubscriber(groupTwo, "childAdded", endGame);
groupTwo.addSubscriber("childAdded", endGame);
simulateDragDrop(tabItem.container, offsetX, offsetY, contentWindow);
}

View File

@ -51,7 +51,7 @@ function onTabViewWindowLoaded(win) {
// Here, we just expand the group, click elsewhere, and make sure
// it collapsed.
let stage1expanded = function() {
group.removeSubscriber("test stage 1", "expanded", stage1expanded);
group.removeSubscriber("expanded", stage1expanded);
ok(group.expanded, "The group is now expanded.");
is(expander[0].style.display, "none", "The expander is hidden!");
@ -60,14 +60,14 @@ function onTabViewWindowLoaded(win) {
ok(overlay, "The expanded tray exists.");
let $overlay = contentWindow.iQ(overlay);
group.addSubscriber("test stage 1", "collapsed", stage1collapsed);
group.addSubscriber("collapsed", stage1collapsed);
// null type means "click", for some reason...
EventUtils.synthesizeMouse(contentWindow.document.body, 10, $overlay.bounds().bottom + 5,
{type: null}, contentWindow);
};
let stage1collapsed = function() {
group.removeSubscriber("test stage 1", "collapsed", stage1collapsed);
group.removeSubscriber("collapsed", stage1collapsed);
ok(!group.expanded, "The group is no longer expanded.");
isnot(expander[0].style.display, "none", "The expander is visible!");
let expanderBounds = expander.bounds();
@ -76,7 +76,7 @@ function onTabViewWindowLoaded(win) {
ok(stackCenter.y < expanderBounds.center().y, "The expander is below the stack.");
// now, try opening it up again.
group.addSubscriber("test stage 2", "expanded", stage2expanded);
group.addSubscriber("expanded", stage2expanded);
EventUtils.sendMouseEvent({ type: "click" }, expander[0], contentWindow);
};
@ -84,7 +84,7 @@ function onTabViewWindowLoaded(win) {
// Now make sure every child of the group shows up within this "tray", and
// click on one of them and make sure we go into the tab and the tray collapses.
let stage2expanded = function() {
group.removeSubscriber("test stage 2", "expanded", stage2expanded);
group.removeSubscriber("expanded", stage2expanded);
ok(group.expanded, "The group is now expanded.");
is(expander[0].style.display, "none", "The expander is hidden!");
@ -132,27 +132,27 @@ function onTabViewWindowLoaded(win) {
}
// okay, expand this group one last time
group.addSubscriber("test stage 3", "expanded", stage3expanded);
group.addSubscriber("expanded", stage3expanded);
EventUtils.sendMouseEvent({ type: "click" }, expander[0], contentWindow);
}
// STAGE 3:
// Ensure that stack still shows the same top item after a expand and a collapse.
let stage3expanded = function() {
group.removeSubscriber("test stage 3", "expanded", stage3expanded);
group.removeSubscriber("expanded", stage3expanded);
ok(group.expanded, "The group is now expanded.");
let overlay = contentWindow.document.getElementById("expandedTray");
let $overlay = contentWindow.iQ(overlay);
group.addSubscriber("test stage 3", "collapsed", stage3collapsed);
group.addSubscriber("collapsed", stage3collapsed);
// null type means "click", for some reason...
EventUtils.synthesizeMouse(contentWindow.document.body, 10, $overlay.bounds().bottom + 5,
{type: null}, contentWindow);
};
let stage3collapsed = function() {
group.removeSubscriber("test stage 3", "collapsed", stage3collapsed);
group.removeSubscriber("collapsed", stage3collapsed);
ok(!group.expanded, "The group is no longer expanded.");
isnot(expander[0].style.display, "none", "The expander is visible!");
@ -176,7 +176,7 @@ function onTabViewWindowLoaded(win) {
contentWindow.UI.setActive(originalTabItem);
// now, try opening it up again.
group.addSubscriber("test stage 4", "expanded", stage4expanded);
group.addSubscriber("expanded", stage4expanded);
EventUtils.sendMouseEvent({ type: "click" }, expander[0], contentWindow);
};
@ -185,7 +185,7 @@ function onTabViewWindowLoaded(win) {
// enter Panorama (i.e., zoom into this other group), and make sure we can go to
// it and that the tray gets collapsed.
let stage4expanded = function() {
group.removeSubscriber("test stage 4", "expanded", stage4expanded);
group.removeSubscriber("expanded", stage4expanded);
ok(group.expanded, "The group is now expanded.");
is(expander[0].style.display, "none", "The expander is hidden!");
@ -231,7 +231,7 @@ function onTabViewWindowLoaded(win) {
}
// get the ball rolling
group.addSubscriber("test stage 1", "expanded", stage1expanded);
group.addSubscriber("expanded", stage1expanded);
EventUtils.sendMouseEvent({ type: "click" }, expander[0], contentWindow);
}, win);
}

View File

@ -4,18 +4,16 @@
function test() {
waitForExplicitFinish();
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
TabView.toggle();
showTabView(onTabViewWindowLoaded);
}
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;
let contentWindow = TabView.getContentWindow();
is(contentWindow.GroupItems.groupItems.length, 1, "There is one group item on startup");
originalGroupItem = contentWindow.GroupItems.groupItems[0];
@ -37,8 +35,8 @@ function testEmptyGroupItem(contentWindow) {
is(contentWindow.GroupItems.groupItems.length, ++groupItemCount,
"The number of groups is increased by 1");
emptyGroupItem.addSubscriber(emptyGroupItem, "close", function() {
emptyGroupItem.removeSubscriber(emptyGroupItem, "close");
emptyGroupItem.addSubscriber("close", function onClose() {
emptyGroupItem.removeSubscriber("close", onClose);
// check the number of groups.
is(contentWindow.GroupItems.groupItems.length, --groupItemCount,
@ -58,46 +56,27 @@ function testGroupItemWithTabItem(contentWindow) {
let groupItem = createEmptyGroupItem(contentWindow, 300, 300, 200);
let tabItemCount = 0;
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");
let tabItemClosed = false;
tabItem.addSubscriber(tabItem, "close", function() {
tabItem.removeSubscriber(tabItem, "close");
tabItem.addSubscriber("close", function onClose() {
tabItem.removeSubscriber("close", onClose);
tabItemClosed = true;
});
tabItem.addSubscriber(tabItem, "tabRemoved", function() {
tabItem.removeSubscriber(tabItem, "tabRemoved");
tabItem.addSubscriber("tabRemoved", function onTabRemoved() {
tabItem.removeSubscriber("tabRemoved", onTabRemoved);
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");
// 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
@ -106,7 +85,11 @@ function testGroupItemWithTabItem(contentWindow) {
ok(tabItems[0], "A tab item exists");
contentWindow.UI.setActive(tabItems[0]);
TabView.toggle();
hideTabView(function() {
ok(!TabView.isVisible(), "Tab View is hidden");
closeGroupItem(groupItem, finish);
});
});
// remove the tab item. The code detects mousedown and mouseup so we stimulate here
@ -115,15 +98,14 @@ 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");
EventUtils.synthesizeMouse(newTabButton[0], 1, 1, {}, contentWindow);
whenTabViewIsHidden(function() {
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");
showTabView(onTabViewShown);
});
groupItem.newTab();
}

View File

@ -82,8 +82,8 @@ function testEmptyGroupItem(contentWindow) {
}
// Shut down
emptyGroupItem.addSubscriber(emptyGroupItem, "close", function() {
emptyGroupItem.removeSubscriber(emptyGroupItem, "close");
emptyGroupItem.addSubscriber("close", function onClose() {
emptyGroupItem.removeSubscriber("close", onClose);
// check the number of groups.
is(contentWindow.GroupItems.groupItems.length, --groupItemCount,

View File

@ -31,21 +31,26 @@ function test() {
let transitioned = 0;
let initCallback = function() {
tabViewWindow = win.TabView._window;
tabViewWindow = win.TabView.getContentWindow();
function onTransitionEnd(event) {
transitioned++;
info(transitioned);
}
tabViewWindow.document.addEventListener("transitionend", onTransitionEnd, false);
showTabView(function() {
// don't use showTabView() here because we only want to check whether
// zoom out animation happens. Other animations would happen before
// the callback as waitForFocus() was added to showTabView() in head.js
let onTabViewShown = function() {
tabViewWindow.removeEventListener("tabviewshown", onTabViewShown, false);
tabViewWindow.document.removeEventListener("transitionend", onTransitionEnd, false);
ok(!transitioned, "There should be no transitions");
tabViewWindow.document.removeEventListener(
"transitionend", onTransitionEnd, false);
finish();
}, win);
};
tabViewWindow.addEventListener("tabviewshown", onTabViewShown, false);
win.TabView.toggle();
};
win.TabView._initFrame(initCallback);

View File

@ -4,50 +4,38 @@
function test() {
waitForExplicitFinish();
window.addEventListener("tabviewshown", onTabViewWindowLoaded, false);
TabView.toggle();
registerCleanupFunction(function() {
while (gBrowser.tabs[1])
gBrowser.removeTab(gBrowser.tabs[1]);
hideTabView(function() {});
});
showTabView(onTabViewWindowLoaded);
}
function onTabViewWindowLoaded() {
window.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
ok(TabView.isVisible(), "Tab View is visible");
let contentWindow = document.getElementById("tab-view").contentWindow;
let contentWindow = TabView.getContentWindow();
registerCleanupFunction(function() {
let groupItem = contentWindow.GroupItems.groupItem(groupItemId);
if (groupItem)
closeGroupItem(groupItem, function() {});
});
// 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
waitForFocus(function() {
testUndoGroup(contentWindow, groupItem);
}, contentWindow);
};
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);
let groupItem = createGroupItemWithBlankTabs(window, 300, 300, 400, 1);
groupItemId = groupItem.id;
is(groupItem.getChildren().length, 1, "The new group has a tab item");
// start the tests
waitForFocus(function() {
testUndoGroup(contentWindow, groupItem);
}, contentWindow);
}
function testUndoGroup(contentWindow, groupItem) {
groupItem.addSubscriber(groupItem, "groupHidden", function() {
groupItem.removeSubscriber(groupItem, "groupHidden");
groupItem.addSubscriber("groupHidden", function onHidden() {
groupItem.removeSubscriber("groupHidden", onHidden);
// check the data of the group
let theGroupItem = contentWindow.GroupItems.groupItem(groupItem.id);
@ -64,8 +52,8 @@ function testUndoGroup(contentWindow, groupItem) {
{ type: "click" }, theGroupItem.$undoContainer[0], contentWindow);
});
groupItem.addSubscriber(groupItem, "groupShown", function() {
groupItem.removeSubscriber(groupItem, "groupShown");
groupItem.addSubscriber("groupShown", function onShown() {
groupItem.removeSubscriber("groupShown", onShown);
// check the data of the group
let theGroupItem = contentWindow.GroupItems.groupItem(groupItem.id);
@ -87,8 +75,8 @@ function testUndoGroup(contentWindow, groupItem) {
}
function testCloseUndoGroup(contentWindow, groupItem) {
groupItem.addSubscriber(groupItem, "groupHidden", function() {
groupItem.removeSubscriber(groupItem, "groupHidden");
groupItem.addSubscriber("groupHidden", function onHidden() {
groupItem.removeSubscriber("groupHidden", onHidden);
// check the data of the group
let theGroupItem = contentWindow.GroupItems.groupItem(groupItem.id);
@ -107,19 +95,12 @@ function testCloseUndoGroup(contentWindow, groupItem) {
{ type: "click" }, closeButton[0], contentWindow);
});
groupItem.addSubscriber(groupItem, "close", function() {
groupItem.removeSubscriber(groupItem, "close");
groupItem.addSubscriber("close", function onClose() {
groupItem.removeSubscriber("close", onClose);
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
@ -127,7 +108,10 @@ function testCloseUndoGroup(contentWindow, groupItem) {
ok(tabItems[0], "A tab item exists");
contentWindow.UI.setActive(tabItems[0]);
TabView.toggle();
hideTabView(function() {
ok(!TabView.isVisible(), "Tab View is hidden");
finish();
});
});
let closeButton = groupItem.container.getElementsByClassName("close");

View File

@ -32,6 +32,9 @@ function createGroupItemWithTabs(win, width, height, padding, urls, animate) {
ok(newItem.container, "Created element "+t+":"+newItem.container);
++t;
});
// to set one of tabItem to be active since we load tabs into a group
// in a non-standard flow.
contentWindow.UI.setActive(groupItem);
return groupItem;
}
@ -45,15 +48,15 @@ function createGroupItemWithBlankTabs(win, width, height, padding, numNewTabs, a
// ----------
function closeGroupItem(groupItem, callback) {
groupItem.addSubscriber(groupItem, "close", function () {
groupItem.removeSubscriber(groupItem, "close");
groupItem.addSubscriber("close", function onClose() {
groupItem.removeSubscriber("close", onClose);
if ("function" == typeof callback)
executeSoon(callback);
});
if (groupItem.getChildren().length) {
groupItem.addSubscriber(groupItem, "groupHidden", function () {
groupItem.removeSubscriber(groupItem, "groupHidden");
groupItem.addSubscriber("groupHidden", function onHide() {
groupItem.removeSubscriber("groupHidden", onHide);
groupItem.closeHidden();
});
}
@ -133,11 +136,13 @@ function showTabView(callback, win) {
win = win || window;
if (win.TabView.isVisible()) {
callback();
waitForFocus(callback, win);
return;
}
whenTabViewIsShown(callback, win);
whenTabViewIsShown(function() {
waitForFocus(callback, win);
}, win);
win.TabView.show();
}
@ -222,8 +227,8 @@ function whenSearchIsEnabled(callback, win) {
return;
}
contentWindow.addEventListener("tabviewsearchenabled", function () {
contentWindow.removeEventListener("tabviewsearchenabled", arguments.callee, false);
contentWindow.addEventListener("tabviewsearchenabled", function onSearchEnabled() {
contentWindow.removeEventListener("tabviewsearchenabled", onSearchEnabled, false);
callback();
}, false);
}
@ -238,8 +243,8 @@ function whenSearchIsDisabled(callback, win) {
return;
}
contentWindow.addEventListener("tabviewsearchdisabled", function () {
contentWindow.removeEventListener("tabviewsearchdisabled", arguments.callee, false);
contentWindow.addEventListener("tabviewsearchdisabled", function onSearchDisabled() {
contentWindow.removeEventListener("tabviewsearchdisabled", onSearchDisabled, false);
callback();
}, false);
}
@ -252,8 +257,8 @@ function hideGroupItem(groupItem, callback) {
return;
}
groupItem.addSubscriber(groupItem, "groupHidden", function () {
groupItem.removeSubscriber(groupItem, "groupHidden");
groupItem.addSubscriber("groupHidden", function onHide() {
groupItem.removeSubscriber("groupHidden", onHide);
callback();
});
groupItem.closeAll();
@ -266,8 +271,8 @@ function unhideGroupItem(groupItem, callback) {
return;
}
groupItem.addSubscriber(groupItem, "groupShown", function () {
groupItem.removeSubscriber(groupItem, "groupShown");
groupItem.addSubscriber("groupShown", function onShown() {
groupItem.removeSubscriber("groupShown", onShown);
callback();
});
groupItem._unhide();
@ -342,8 +347,8 @@ function restoreTab(callback, index, win) {
return;
}
tab._tabViewTabItem.addSubscriber(tab, "reconnected", function onReconnected() {
tab._tabViewTabItem.removeSubscriber(tab, "reconnected");
tab._tabViewTabItem.addSubscriber("reconnected", function onReconnected() {
tab._tabViewTabItem.removeSubscriber("reconnected", onReconnected);
finalize();
});
}

View File

@ -1,10 +0,0 @@
<html>
<script>
window.onbeforeunload = function(event){
event.returnValue = 'Confirmation? ';
}
</script>
<body>
Test page
</body>
</html>

View File

@ -327,6 +327,11 @@
gBrowser.loadURIWithFlags(url, flags, null, null, postData);
}
// Focus the content area before triggering loads, since if the load
// occurs in a new tab, we want focus to be restored to the content
// area when the current tab is re-selected.
gBrowser.selectedBrowser.focus();
if (aTriggeringEvent instanceof MouseEvent) {
// We have a mouse event (from the go button), so use the standard
// UI link behaviors
@ -335,18 +340,12 @@
loadCurrent();
} else {
this.handleRevert();
content.focus();
openUILinkIn(url, where,
{ allowThirdPartyFixup: true, postData: postData });
}
return;
}
if (aTriggeringEvent &&
aTriggeringEvent.altKey &&
!isTabEmpty(gBrowser.selectedTab)) {
} else if (aTriggeringEvent && aTriggeringEvent.altKey &&
!isTabEmpty(gBrowser.selectedTab)) {
this.handleRevert();
content.focus();
gBrowser.loadOneTab(url, {
postData: postData,
inBackground: false,
@ -356,8 +355,6 @@
} else {
loadCurrent();
}
gBrowser.selectedBrowser.focus();
]]></body>
</method>

View File

@ -378,9 +378,11 @@ BrowserGlue.prototype = {
// Show about:rights notification, if needed.
if (this._shouldShowRights()) {
this._showRightsNotification();
#ifdef MOZ_TELEMETRY_REPORTING
} else {
// Only show telemetry notification when about:rights notification is not shown.
this._showTelemetryNotification();
#endif
}
@ -740,6 +742,7 @@ BrowserGlue.prototype = {
}
},
#ifdef MOZ_TELEMETRY_REPORTING
_showTelemetryNotification: function BG__showTelemetryNotification() {
const PREF_TELEMETRY_PROMPTED = "toolkit.telemetry.prompted";
const PREF_TELEMETRY_ENABLED = "toolkit.telemetry.enabled";
@ -805,6 +808,7 @@ BrowserGlue.prototype = {
let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
description.appendChild(link);
},
#endif
_showPluginUpdatePage: function BG__showPluginUpdatePage() {
Services.prefs.setBoolPref(PREF_PLUGINS_NOTIFYUSER, false);

View File

@ -116,18 +116,7 @@ ifndef LIBXUL_SDK
INSTALL_SDK = 1
endif
ifneq (1_,$(if $(CROSS_COMPILE),1,0)_$(UNIVERSAL_BINARY))
ifdef RUN_TEST_PROGRAM
_ABS_RUN_TEST_PROGRAM = $(call core_abspath,$(RUN_TEST_PROGRAM))
endif
GENERATE_CACHE = \
$(_ABS_RUN_TEST_PROGRAM) $(LIBXUL_DIST)/bin/xpcshell$(BIN_SUFFIX) -g "$$PWD" -a "$$PWD" -f $(topsrcdir)/browser/installer/precompile_cache.js -e 'populate_startupcache("omni.jar", "startupCache.zip");' && \
rm -rf jsloader && \
$(UNZIP) startupCache.zip && \
rm startupCache.zip && \
$(ZIP) -r9m omni.jar jsloader
endif
GENERATE_CACHE = 1
include $(topsrcdir)/toolkit/mozapps/installer/packager.mk

View File

@ -992,7 +992,6 @@ xpicleanup@BIN_SUFFIX@
modules/services-sync/type_records/prefs.js
modules/services-sync/type_records/tabs.js
modules/services-sync/util.js
modules/SpatialNavigation.js
modules/stylePanel.jsm
modules/tabview/AllTabs.jsm
modules/tabview/groups.jsm

View File

@ -18,7 +18,7 @@
<!ENTITY useSmoothScrolling.label "Use smooth scrolling">
<!ENTITY useSmoothScrolling.accesskey "m">
<!ENTITY allowHWAccel.label "Use hardware acceleration when available">
<!ENTITY allowHWAccel.accesskey "h">
<!ENTITY allowHWAccel.accesskey "r">
<!ENTITY checkSpelling.label "Check my spelling as I type">
<!ENTITY checkSpelling.accesskey "t">

View File

@ -1,4 +1,3 @@
tabview.groupItem.newTabButton=New tab
tabview.groupItem.defaultName=Name this tab group…
tabview.groupItem.undoCloseGroup=Undo Close Group
tabview.search.otherWindowTabs=Tabs from other windows

View File

@ -79,7 +79,6 @@ browser.jar:
skin/classic/browser/tabbrowser/tab-overflow-border.png (tabbrowser/tab-overflow-border.png)
skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
skin/classic/browser/tabview/edit-light.png (tabview/edit-light.png)
skin/classic/browser/tabview/new-tab.png (tabview/new-tab.png)
skin/classic/browser/tabview/search.png (tabview/search.png)
skin/classic/browser/tabview/stack-expander.png (tabview/stack-expander.png)
skin/classic/browser/tabview/tabview.png (tabview/tabview.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 B

View File

@ -190,7 +190,7 @@ html[dir=rtl] .stack-trayed .tab-title {
----------------------------------*/
.groupItem {
cursor: move;
cursor: pointer;
border: 1px solid rgba(230,230,230,1);
background-color: window;
background-image: -moz-linear-gradient(rgba(255,255,255,.3),rgba(255,255,255,.1));
@ -385,25 +385,6 @@ html[dir=rtl] .guideTrench {
/* Other
----------------------------------*/
.newTabButton {
width: 16px;
height: 15px;
bottom: 10px;
left: 10px;
cursor: pointer;
opacity: .3;
background-image: url(chrome://browser/skin/tabview/new-tab.png);
}
html[dir=rtl] .newTabButton {
left: auto;
right: 10px;
}
.newTabButton:hover {
opacity: 1;
}
.active {
box-shadow: 5px 5px 3px rgba(0,0,0,.5);
}
@ -421,6 +402,7 @@ html[dir=rtl] .acceptsDrop {
}
.titlebar {
cursor: move;
font-size: 12px;
height: 18px;
}

View File

@ -117,7 +117,6 @@ browser.jar:
skin/classic/browser/tabbrowser/tabDragIndicator.png (tabbrowser/tabDragIndicator.png)
skin/classic/browser/tabview/close.png (tabview/close.png)
skin/classic/browser/tabview/edit-light.png (tabview/edit-light.png)
skin/classic/browser/tabview/new-tab.png (tabview/new-tab.png)
skin/classic/browser/tabview/search.png (tabview/search.png)
skin/classic/browser/tabview/stack-expander.png (tabview/stack-expander.png)
skin/classic/browser/tabview/tabview.png (tabview/tabview.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 B

View File

@ -191,7 +191,7 @@ html[dir=rtl] .stack-trayed .tab-title {
----------------------------------*/
.groupItem {
cursor: move;
cursor: pointer;
background-color: rgb(240,240,240);
border-radius: 0.4em;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
@ -376,25 +376,6 @@ html[dir=rtl] .guideTrench {
/* Other
----------------------------------*/
.newTabButton {
width: 16px;
height: 15px;
bottom: 10px;
left: 10px;
cursor: pointer;
opacity: .3;
background-image: url(chrome://browser/skin/tabview/new-tab.png);
}
html[dir=rtl] .newTabButton {
left: auto;
right: 10px;
}
.newTabButton:hover {
opacity: 1;
}
.active {
box-shadow: 5px 5px 3px rgba(0,0,0,.5);
}
@ -412,6 +393,7 @@ html[dir=rtl] .acceptsDrop {
}
.titlebar {
cursor: move;
font-size: 12px;
height: 18px;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -138,6 +138,13 @@
border-right-style: none !important;
}
#toolbar-menubar :-moz-any(@primaryToolbarButtons@):not(#alltabs-button):not(#tabview-button):not(#new-tab-button) > .toolbarbutton-icon:not(:-moz-lwtheme),
#TabsToolbar[tabsontop=true] :-moz-any(@primaryToolbarButtons@):not(#alltabs-button):not(#tabview-button):not(#new-tab-button) > .toolbarbutton-icon:not(:-moz-lwtheme),
#navigator-toolbox[tabsontop=false] > #nav-bar :-moz-any(@primaryToolbarButtons@):not(#alltabs-button):not(#tabview-button):not(#new-tab-button) > .toolbarbutton-icon:not(:-moz-lwtheme),
#nav-bar + #customToolbars + #PersonalToolbar[collapsed=true] + #TabsToolbar[tabsontop=false]:last-child :-moz-any(@primaryToolbarButtons@):not(#alltabs-button):not(#tabview-button):not(#new-tab-button) > .toolbarbutton-icon:not(:-moz-lwtheme) {
list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
}
/* Vertical toolbar border */
#main-window[sizemode=normal] #navigator-toolbox::after,
#main-window[sizemode=normal] #navigator-toolbox[tabsontop=true] > toolbar:not(#toolbar-menubar):not(#TabsToolbar),

View File

@ -57,6 +57,7 @@
%define bgTabTexture -moz-linear-gradient(transparent, hsla(0,0%,45%,.1) 1px, hsla(0,0%,32%,.2) 80%, hsla(0,0%,0%,.2))
%define bgTabTextureHover -moz-linear-gradient(hsla(0,0%,100%,.3) 1px, hsla(0,0%,75%,.2) 80%, hsla(0,0%,60%,.2))
%define navbarTextboxCustomBorder border-color: rgba(0,0,0,.32);
%define navbarLargeIcons #navigator-toolbox[iconsize=large][mode=icons] > #nav-bar
#menubar-items {
-moz-box-orient: vertical; /* for flex hack */
@ -582,6 +583,10 @@ menuitem.bookmark-item {
list-style-image: url("chrome://browser/skin/Toolbar.png");
}
.toolbarbutton-1:-moz-lwtheme-brighttext {
list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
}
.toolbarbutton-1:not([type="menu-button"]) {
-moz-box-orient: vertical;
}
@ -596,19 +601,18 @@ menuitem.bookmark-item {
counter-reset: smallicons;
}
#navigator-toolbox[iconsize=small] > #nav-bar {
padding-top: 1px;
padding-bottom: 1px;
}
#navigator-toolbox[iconsize=large][mode=icons] > #nav-bar {
@navbarLargeIcons@ {
-moz-padding-start: 0;
-moz-padding-end: 2px;
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button,
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker,
#nav-bar .toolbarbutton-1 {
@navbarLargeIcons@ :-moz-any(@primaryToolbarButtons@):not(#alltabs-button):not(#tabview-button):not(#new-tab-button) > .toolbarbutton-icon {
list-style-image: url("chrome://browser/skin/Toolbar.png") !important;
}
@navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-button,
@navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker,
@navbarLargeIcons@ .toolbarbutton-1 {
-moz-appearance: none;
padding: 1px 5px;
background: rgba(151,152,153,.05)
@ -624,14 +628,12 @@ menuitem.bookmark-item {
text-shadow: 0 0 2px white;
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker,
#navigator-toolbox[iconsize="small"][mode="icons"] > #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button,
#navigator-toolbox[iconsize="small"][mode="icons"] > #nav-bar .toolbarbutton-1 {
@navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
padding-left: 3px;
padding-right: 3px;
}
#nav-bar .toolbarbutton-1[type="menu-button"] {
@navbarLargeIcons@ .toolbarbutton-1[type="menu-button"] {
-moz-appearance: none;
padding: 0;
background: none !important;
@ -639,44 +641,39 @@ menuitem.bookmark-item {
box-shadow: none !important;
}
#nav-bar .toolbarbutton-1 {
@navbarLargeIcons@ .toolbarbutton-1 {
margin: 1px 3px;
}
#navigator-toolbox[iconsize="small"][mode="icons"] > #nav-bar .toolbarbutton-1 {
margin-left: 2px;
margin-right: 2px;
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
@navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker {
-moz-border-start-style: none;
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:-moz-locale-dir(ltr),
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker:-moz-locale-dir(rtl) {
@navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-button:-moz-locale-dir(ltr),
@navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker:-moz-locale-dir(rtl) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:-moz-locale-dir(rtl),
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker:-moz-locale-dir(ltr) {
@navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-button:-moz-locale-dir(rtl),
@navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker:-moz-locale-dir(ltr) {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
#nav-bar .toolbarbutton-1[disabled="true"] {
@navbarLargeIcons@ .toolbarbutton-1[disabled="true"] {
opacity: .4;
}
#nav-bar .toolbarbutton-1[disabled="true"] > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon {
@navbarLargeIcons@ .toolbarbutton-1[disabled="true"] > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
@navbarLargeIcons@ .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon {
opacity: 1;
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled="true"]):not(:active):hover,
#nav-bar .toolbarbutton-1:not([open="true"]):not(:active):hover > .toolbarbutton-menubutton-dropmarker:not([disabled="true"]),
#nav-bar .toolbarbutton-1:not([type="menu-button"]):not([disabled="true"]):not([checked="true"]):not([open="true"]):not(:active):hover,
#navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #back-button:not([disabled="true"]):not([open]):not(:active):hover > .toolbarbutton-icon {
@navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled="true"]):not(:active):hover,
@navbarLargeIcons@ .toolbarbutton-1:not([open="true"]):not(:active):hover > .toolbarbutton-menubutton-dropmarker:not([disabled="true"]),
@navbarLargeIcons@ .toolbarbutton-1:not([type="menu-button"]):not([disabled="true"]):not([checked="true"]):not([open="true"]):not(:active):hover,
@navbarLargeIcons@ #back-button:not([disabled="true"]):not([open]):not(:active):hover > .toolbarbutton-icon {
background-color: hsla(190,60%,70%,.5);
border-color: hsla(190,50%,65%,.8) hsla(190,50%,50%,.8) hsla(190,50%,40%,.8);
box-shadow: 0 0 0 1px rgba(255,255,255,.3) inset,
@ -687,12 +684,12 @@ menuitem.bookmark-item {
box-shadow .3s ease-in;
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled="true"]):hover:active,
#nav-bar .toolbarbutton-1:hover:active > .toolbarbutton-menubutton-dropmarker:not([disabled="true"]),
#nav-bar .toolbarbutton-1[open="true"] > .toolbarbutton-menubutton-dropmarker,
#nav-bar .toolbarbutton-1:not([type="menu-button"]):not([disabled="true"]):hover:active,
#nav-bar .toolbarbutton-1:not([type="menu-button"])[checked="true"],
#nav-bar .toolbarbutton-1[open="true"] {
@navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled="true"]):hover:active,
@navbarLargeIcons@ .toolbarbutton-1:hover:active > .toolbarbutton-menubutton-dropmarker:not([disabled="true"]),
@navbarLargeIcons@ .toolbarbutton-1[open="true"] > .toolbarbutton-menubutton-dropmarker,
@navbarLargeIcons@ .toolbarbutton-1:not([type="menu-button"]):not([disabled="true"]):hover:active,
@navbarLargeIcons@ .toolbarbutton-1:not([type="menu-button"])[checked="true"],
@navbarLargeIcons@ .toolbarbutton-1[open="true"] {
background-color: transparent;
border-color: rgba(0,0,0,.65) rgba(0,0,0,.55) rgba(0,0,0,.5);
box-shadow: 0 0 6.5px rgba(0,0,0,.4) inset,
@ -701,7 +698,7 @@ menuitem.bookmark-item {
text-shadow: none;
}
#nav-bar .toolbarbutton-1[checked="true"]:not(:active):hover {
@navbarLargeIcons@ .toolbarbutton-1[checked="true"]:not(:active):hover {
background-color: rgba(90%,90%,90%,.4);
-moz-transition: background-color .4s;
}
@ -711,8 +708,8 @@ menuitem.bookmark-item {
-moz-margin-end: 0;
}
#nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon {
@navbarLargeIcons@ .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon,
@navbarLargeIcons@ .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon {
margin: 1px;
}
@ -764,37 +761,31 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
-moz-image-region: rect(0, 36px, 18px, 18px);
}
#navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #back-button {
-moz-image-region: rect(18px, 20px, 38px, 0);
}
#back-button:-moz-locale-dir(rtl) > .toolbarbutton-icon,
#forward-button:-moz-locale-dir(rtl),
#forward-button:-moz-locale-dir(rtl) > .toolbarbutton-text {
-moz-transform: scaleX(-1);
}
#nav-bar #back-button {
-moz-margin-end: 0 !important;
}
#nav-bar #forward-button {
@navbarLargeIcons@ #forward-button {
border-left-style: none;
-moz-margin-start: 0 !important;
}
#nav-bar #back-button:-moz-locale-dir(ltr) {
@navbarLargeIcons@ #back-button:-moz-locale-dir(ltr) {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
#nav-bar #back-button:-moz-locale-dir(rtl),
#nav-bar #forward-button {
@navbarLargeIcons@ #back-button:-moz-locale-dir(rtl),
@navbarLargeIcons@ #forward-button {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
#navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #back-button {
@navbarLargeIcons@ #back-button {
-moz-image-region: rect(18px, 20px, 38px, 0);
-moz-margin-end: 0 !important;
margin: -5px 0;
padding-top: 0;
padding-bottom: 0;
@ -808,11 +799,11 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
box-shadow: none;
}
#navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #back-button:-moz-locale-dir(rtl) {
@navbarLargeIcons@ #back-button:-moz-locale-dir(rtl) {
border-radius: 10000px 0 0 10000px;
}
#navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #back-button > .toolbarbutton-icon {
@navbarLargeIcons@ #back-button > .toolbarbutton-icon {
border-radius: 10000px;
padding: 5px;
border: none;
@ -825,7 +816,7 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
0 1px 1px rgba(0,0,0,.3);
}
#navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #back-button:not([disabled="true"]):not([open="true"]):not(:active):hover > .toolbarbutton-icon {
@navbarLargeIcons@ #back-button:not([disabled="true"]):not([open="true"]):not(:active):hover > .toolbarbutton-icon {
box-shadow: 0 0 0 1px rgba(255,255,255,.3) inset,
0 0 0 2px rgba(255,255,255,.1) inset,
0 0 0 1px hsla(190,50%,40%,.3),
@ -834,16 +825,16 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
0 0 5px 1px hsl(190,90%,80%);
}
#navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #back-button:not([disabled="true"]):hover:active > .toolbarbutton-icon,
#navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #back-button[open="true"] > .toolbarbutton-icon {
@navbarLargeIcons@ #back-button:not([disabled="true"]):hover:active > .toolbarbutton-icon,
@navbarLargeIcons@ #back-button[open="true"] > .toolbarbutton-icon {
box-shadow: 0 0 6.5px rgba(0,0,0,.4) inset,
0 0 2px rgba(0,0,0,.4) inset,
0 0 0 1px rgba(0,0,0,.65),
0 2px 0 rgba(255,255,255,.4);
}
#navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar[currentset*="unified-back-forward-button"],
#navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar:not([currentset]) {
@navbarLargeIcons@[currentset*="unified-back-forward-button"],
@navbarLargeIcons@:not([currentset]) {
padding-top: 3px;
padding-bottom: 5px;
}
@ -853,7 +844,7 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
padding-top: 5px;
}
#navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #forward-button {
@navbarLargeIcons@ #forward-button {
/*mask: url(keyhole-forward-mask.svg#mask); XXX: this regresses twinopen */
mask: url(chrome://browser/content/browser.xul#winstripe-keyhole-forward-mask);
-moz-margin-start: -6px !important;
@ -861,7 +852,7 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
padding-right: 3px;
}
#navigator-toolbox[iconsize="large"][mode="icons"] > #nav-bar #forward-button:not([disabled="true"]):not(:active):hover {
@navbarLargeIcons@ #forward-button:not([disabled="true"]):not(:active):hover {
/*mask: url(keyhole-forward-mask.svg#mask-hover);*/
mask: url(chrome://browser/content/browser.xul#winstripe-keyhole-forward-mask-hover);
/* Don't animate the box shadow, as the blur and spread radii affect the mask. */
@ -895,6 +886,9 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
#home-button.bookmark-item {
list-style-image: url("chrome://browser/skin/Toolbar.png");
}
#home-button.bookmark-item:-moz-lwtheme-brighttext {
list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
}
#home-button {
-moz-image-region: rect(0, 90px, 18px, 72px);
}
@ -969,6 +963,10 @@ toolbar[mode="full"] .toolbarbutton-1 > .toolbarbutton-menubutton-button {
list-style-image: url("chrome://browser/skin/Toolbar.png");
}
#bookmarks-menu-button.bookmark-item:-moz-lwtheme-brighttext {
list-style-image: url("chrome://browser/skin/Toolbar-inverted.png");
}
#bookmarks-menu-button.toolbarbutton-1 {
-moz-box-orient: horizontal;
}

View File

@ -35,6 +35,7 @@ browser.jar:
skin/classic/browser/reload-stop-go.png
skin/classic/browser/Secure24.png (Secure24.png)
skin/classic/browser/Toolbar.png (Toolbar.png)
skin/classic/browser/Toolbar-inverted.png
skin/classic/browser/Go-arrow.png (Go-arrow.png)
* skin/classic/browser/searchbar.css (searchbar.css)
skin/classic/browser/section_collapsed.png
@ -96,7 +97,6 @@ browser.jar:
skin/classic/browser/tabview/close.png (tabview/close.png)
skin/classic/browser/tabview/edit-light.png (tabview/edit-light.png)
skin/classic/browser/tabview/grain.png (tabview/grain.png)
skin/classic/browser/tabview/new-tab.png (tabview/new-tab.png)
skin/classic/browser/tabview/search.png (tabview/search.png)
skin/classic/browser/tabview/stack-expander.png (tabview/stack-expander.png)
skin/classic/browser/tabview/tabview.png (tabview/tabview.png)
@ -149,6 +149,7 @@ browser.jar:
skin/classic/aero/browser/reload-stop-go.png
skin/classic/aero/browser/Secure24.png (Secure24-aero.png)
skin/classic/aero/browser/Toolbar.png
skin/classic/aero/browser/Toolbar-inverted.png
skin/classic/aero/browser/Go-arrow.png (Go-arrow-aero.png)
* skin/classic/aero/browser/searchbar.css (searchbar.css)
skin/classic/aero/browser/section_collapsed.png
@ -211,7 +212,6 @@ browser.jar:
skin/classic/aero/browser/tabview/close.png (tabview/close.png)
skin/classic/aero/browser/tabview/edit-light.png (tabview/edit-light.png)
skin/classic/aero/browser/tabview/grain.png (tabview/grain.png)
skin/classic/aero/browser/tabview/new-tab.png (tabview/new-tab.png)
skin/classic/aero/browser/tabview/search.png (tabview/search.png)
skin/classic/aero/browser/tabview/stack-expander.png (tabview/stack-expander.png)
skin/classic/aero/browser/tabview/tabview.png (tabview/tabview.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 466 B

View File

@ -204,7 +204,7 @@ html[dir=rtl] .tab.focus {
----------------------------------*/
.groupItem {
cursor: move;
cursor: pointer;
background-color: #E0EAF5;
border-radius: 0.4em;
box-shadow:
@ -398,25 +398,6 @@ html[dir=rtl] .guideTrench {
/* Other
----------------------------------*/
.newTabButton {
width: 16px;
height: 15px;
bottom: 10px;
left: 10px;
cursor: pointer;
opacity: .3;
background-image: url(chrome://browser/skin/tabview/new-tab.png);
}
html[dir=rtl] .newTabButton {
left: auto;
right: 10px;
}
.newTabButton:hover {
opacity: 1;
}
.active {
box-shadow: 5px 5px 3px rgba(0,0,0,.5);
}
@ -434,6 +415,7 @@ html[dir=rtl] .acceptsDrop {
}
.titlebar {
cursor: move;
font-size: 12px;
height: 18px;
}

View File

@ -1433,7 +1433,7 @@ case $os in
os=-dicos
;;
-android*)
os=android
os=-android
;;
-none)
;;

View File

@ -1,16 +1,30 @@
import subprocess
from devicemanager import DeviceManager, DMError
import re
import os
class DeviceManagerADB(DeviceManager):
def __init__(self, host = None, port = 20701, retrylimit = 5):
def __init__(self, host = None, port = 20701, retrylimit = 5, packageName = "org.mozilla.fennec_unofficial"):
self.host = host
self.port = port
self.retrylimit = retrylimit
self.retries = 0
self._sock = None
self.getDeviceRoot()
self.Init(packageName)
def Init(self, packageName):
# Initialization code that may fail: Catch exceptions here to allow
# successful initialization even if, for example, adb is not installed.
try:
root = self.getDeviceRoot()
self.verifyPackage(packageName)
self.tmpDir = root + "/tmp"
if (not self.dirExists(self.tmpDir)):
self.mkDir(self.tmpDir)
except:
self.packageName = None
self.tmpDir = None
try:
# a test to see if we have root privs
self.checkCmd(["shell", "ls", "/sbin"])
@ -26,7 +40,17 @@ class DeviceManagerADB(DeviceManager):
# failure: False
def pushFile(self, localname, destname):
try:
self.checkCmd(["push", localname, destname])
if (os.name == "nt"):
destname = destname.replace('\\', '/')
if (self.packageName):
remoteTmpFile = self.tmpDir + "/" + os.path.basename(localname)
self.checkCmd(["push", os.path.realpath(localname), remoteTmpFile])
self.checkCmdAs(["shell", "cp", remoteTmpFile, destname])
self.checkCmd(["shell", "rm", remoteTmpFile])
else:
self.checkCmd(["push", os.path.realpath(localname), destname])
if (self.isDir(destname)):
destname = destname + "/" + os.path.basename(localname)
self.chmodDir(destname)
return True
except:
@ -38,7 +62,7 @@ class DeviceManagerADB(DeviceManager):
# failure: None
def mkDir(self, name):
try:
self.checkCmd(["shell", "mkdir", name])
self.checkCmdAs(["shell", "mkdir", name])
self.chmodDir(name)
return name
except:
@ -50,8 +74,17 @@ class DeviceManagerADB(DeviceManager):
# success: directory structure that we created
# failure: None
def mkDirs(self, filename):
self.checkCmd(["shell", "mkdir", "-p ", name])
return filename
parts = filename.split('/')
name = ""
for part in parts:
if (part == parts[-1]): break
if (part != ""):
name += '/' + part
if (not self.dirExists(name)):
if (self.mkDir(name) == None):
print "failed making directory: " + str(name)
return None
return name
# push localDir from host to remoteDir on the device
# external function
@ -59,9 +92,29 @@ class DeviceManagerADB(DeviceManager):
# success: remoteDir
# failure: None
def pushDir(self, localDir, remoteDir):
# adb "push" accepts a directory as an argument, but if the directory
# contains symbolic links, the links are pushed, rather than the linked
# files; we push file-by-file to get around this limitation
try:
self.checkCmd(["push", localDir, remoteDir])
self.chmodDir(remoteDir)
if (not self.dirExists(remoteDir)):
self.mkDirs(remoteDir+"/x")
for root, dirs, files in os.walk(localDir):
relRoot = os.path.relpath(root, localDir)
for file in files:
localFile = os.path.join(root, file)
remoteFile = remoteDir + "/"
if (relRoot!="."):
remoteFile = remoteFile + relRoot + "/"
remoteFile = remoteFile + file
self.pushFile(localFile, remoteFile)
for dir in dirs:
targetDir = remoteDir + "/"
if (relRoot!="."):
targetDir = targetDir + relRoot + "/"
targetDir = targetDir + dir
if (not self.dirExists(targetDir)):
self.mkDir(targetDir)
self.checkCmdAs(["shell", "chmod", "777", remoteDir])
return True
except:
print "pushing " + localDir + " to " + remoteDir + " failed"
@ -72,11 +125,7 @@ class DeviceManagerADB(DeviceManager):
# success: True
# failure: False
def dirExists(self, dirname):
try:
self.checkCmd(["shell", "ls", dirname])
return True
except:
return False
return self.isDir(dirname)
# Because we always have / style paths we make this a lot easier with some
# assumptions
@ -112,19 +161,19 @@ class DeviceManagerADB(DeviceManager):
if (self.isDir(remoteDir.strip() + "/" + f.strip())):
out += self.removeDir(remoteDir.strip() + "/" + f.strip())
else:
out += self.removeFile(remoteDir.strip())
out += self.removeSingleDir(remoteDir)
out += self.removeFile(remoteDir.strip() + "/" + f.strip())
out += self.removeSingleDir(remoteDir.strip())
else:
out += self.removeFile(remoteDir.strip())
return out
def isDir(self, remotePath):
p = self.runCmd(["shell", "ls", remotePath])
p = self.runCmd(["shell", "ls", "-a", remotePath])
data = p.stdout.readlines()
if (len(data) == 0):
return True
if (len(data) == 1):
if (data[0] == remotePath):
if (data[0].rstrip() == remotePath):
return False
if (data[0].find("No such file or directory") != -1):
return False
@ -133,7 +182,7 @@ class DeviceManagerADB(DeviceManager):
return True
def listFiles(self, rootdir):
p = self.runCmd(["shell", "ls", rootdir])
p = self.runCmd(["shell", "ls", "-a", rootdir])
data = p.stdout.readlines()
if (len(data) == 1):
if (data[0] == rootdir):
@ -165,7 +214,11 @@ class DeviceManagerADB(DeviceManager):
# success: pid
# failure: None
def fireProcess(self, appname, failIfRunning=False):
return self.runCmd(["shell", appname]).pid
#strip out env vars
parts = appname.split('"');
if (len(parts) > 2):
parts = parts[2:]
return self.launchProcess(parts, failIfRunning)
# external function
# returns:
@ -173,7 +226,7 @@ class DeviceManagerADB(DeviceManager):
# failure: None
def launchProcess(self, cmd, outputFile = "process.txt", cwd = '', env = '', failIfRunning=False):
acmd = ["shell", "am","start"]
cmd = ' '.join(cmd)
cmd = ' '.join(cmd).strip()
i = cmd.find(" ")
acmd.append("-n")
acmd.append(cmd[0:i] + "/.App")
@ -299,9 +352,18 @@ class DeviceManagerADB(DeviceManager):
# success: path for device root
# failure: None
def getDeviceRoot(self):
if (not self.dirExists("/data/local/tests")):
self.mkDir("/data/local/tests")
return "/data/local/tests"
# /mnt/sdcard/tests is preferred to /data/local/tests, but this can be
# over-ridden by creating /data/local/tests
testRoot = "/data/local/tests"
if (self.dirExists(testRoot)):
return testRoot
root = "/mnt/sdcard"
if (not self.dirExists(root)):
root = "/data/local"
testRoot = root + "/tests"
if (not self.dirExists(testRoot)):
self.mkDir(testRoot)
return testRoot
# Either we will have /tests/fennec or /tests/firefox but we will never have
# both. Return the one that exists
@ -319,10 +381,16 @@ class DeviceManagerADB(DeviceManager):
return devroot + '/fennec'
elif (self.dirExists(devroot + '/firefox')):
return devroot + '/firefox'
elif (self.dirExsts('/data/data/org.mozilla.fennec')):
return 'org.mozilla.fennec'
elif (self.dirExists('/data/data/org.mozilla.fennec')):
return '/data/data/org.mozilla.fennec'
elif (self.dirExists('/data/data/org.mozilla.firefox')):
return 'org.mozilla.firefox'
return '/data/data/org.mozilla.firefox'
elif (self.dirExists('/data/data/org.mozilla.fennec_unofficial')):
return '/data/data/org.mozilla.fennec_unofficial'
elif (self.dirExists('/data/data/org.mozilla.fennec_aurora')):
return '/data/data/org.mozilla.fennec_aurora'
elif (self.dirExists('/data/data/org.mozilla.firefox_beta')):
return '/data/data/org.mozilla.firefox_beta'
# Failure (either not installed or not a recognized platform)
return None
@ -423,18 +491,34 @@ class DeviceManagerADB(DeviceManager):
args.insert(0, "adb")
return subprocess.check_call(args)
def checkCmdAs(self, args):
if (self.packageName):
args.insert(1, "run-as")
args.insert(2, self.packageName)
return self.checkCmd(args)
def chmodDir(self, remoteDir):
print "called chmodDir"
if (self.isDir(remoteDir)):
files = self.listFiles(remoteDir.strip())
for f in files:
if (self.isDir(remoteDir.strip() + "/" + f.strip())):
self.chmodDir(remoteDir.strip() + "/" + f.strip())
else:
self.checkCmd(["shell", "chmod", "777", remoteDir.strip()])
self.checkCmdAs(["shell", "chmod", "777", remoteDir.strip()])
print "chmod " + remoteDir.strip()
self.checkCmd(["shell", "chmod", "777", remoteDir])
self.checkCmdAs(["shell", "chmod", "777", remoteDir])
print "chmod " + remoteDir
else:
self.checkCmd(["shell", "chmod", "777", remoteDir.strip()])
print "chmod " + remoteDir
self.checkCmdAs(["shell", "chmod", "777", remoteDir.strip()])
print "chmod " + remoteDir.strip()
def verifyPackage(self, packageName):
# If a valid package name is specified, it will be used for certain
# file operations, so that pushed files and directories are created
# by the uid associated with the package.
self.packageName = None
if (packageName):
data = self.runCmd(["shell", "run-as", packageName, "pwd"]).stdout.read()
if (not re.search('is unknown', data)):
self.packageName = packageName
print "package set: " + self.packageName

View File

@ -64,24 +64,11 @@ if __name__ == '__main__':
from optparse import OptionParser
automation = Automation()
parser = OptionParser(usage='OBJDIR=path/to/objdir python %prog [NUM_RUNS]')
parser = OptionParser()
addCommonOptions(parser)
options, args = parser.parse_args()
if not os.getenv('OBJDIR'):
parser.error('Please specify the OBJDIR environment variable.')
if not args:
num_runs = 1
else:
try:
num_runs = int(args[0])
except:
parser.error('NUM_RUNS argument must be an integer.')
if num_runs < 1:
parser.error('NUM_RUNS must be greater than zero.')
debuggerInfo = getDebuggerInfo(".", options.debugger, options.debuggerArgs,
options.debuggerInteractive)
@ -91,21 +78,16 @@ if __name__ == '__main__':
t.start()
automation.setServerInfo("localhost", PORT)
automation.initializeProfile(PROFILE_DIRECTORY)
browserEnv = automation.environment()
browserEnv["XPCOM_DEBUG_BREAK"] = "warn"
browserEnv["MOZ_JAR_LOG_DIR"] = MOZ_JAR_LOG_DIR
url = "http://localhost:%d/index.html" % PORT
appPath = os.path.join(SCRIPT_DIR, automation.DEFAULT_APP)
for i in range(0, num_runs):
if num_runs != 1:
print "Starting profiling run %d of %d" % (i + 1, num_runs)
automation.initializeProfile(PROFILE_DIRECTORY)
status = automation.runApp(url, browserEnv, appPath, PROFILE_DIRECTORY, {},
debuggerInfo=debuggerInfo,
# the profiling HTML doesn't output anything,
# so let's just run this without a timeout
timeout = None)
if status != 0:
sys.exit(status)
status = automation.runApp(url, browserEnv, appPath, PROFILE_DIRECTORY, {},
debuggerInfo=debuggerInfo,
# the profiling HTML doesn't output anything,
# so let's just run this without a timeout
timeout = None)
sys.exit(status)

View File

@ -91,7 +91,6 @@
#include "nsIURIFixup.h"
#include "nsCDefaultURIFixup.h"
#include "nsIChromeRegistry.h"
#include "nsPrintfCString.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIAsyncVerifyRedirectCallback.h"
#include "mozilla/Preferences.h"

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