author | Carsten "Tomcat" Book <cbook@mozilla.com> |
Wed, 23 Oct 2013 14:59:15 +0200 | |
changeset 165635 | 4b26a2531e86f7c2cacc4d9a0d8255122d36edd6 |
parent 165634 | 2d4d0a353a839e6c9860d96ddfb6bcb1798d8204 (current diff) |
parent 165602 | 8803e8c0ee3efe4c0c07be612ce51c1d274b7c96 (diff) |
child 165636 | 443c255b048a21b3bd80c044ed6882e904c44aac |
push id | 3066 |
push user | akeybl@mozilla.com |
push date | Mon, 09 Dec 2013 19:58:46 +0000 |
treeherder | mozilla-beta@a31a0dce83aa [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 27.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
accessible/src/base/RoleAsserts.cpp | file | annotate | diff | comparison | revisions |
--- a/CLOBBER +++ b/CLOBBER @@ -13,9 +13,9 @@ # | | # O <-- Clobber O <-- Clobber # # Note: The description below will be part of the error message shown to users. # # Modifying this file will now automatically clobber the buildbot machines \o/ # -Bug 926091 says hello to bug 924992 by touching webidl +Bug 918207 needed a clobber on every platform because we can't have nice things
--- a/accessible/src/atk/AccessibleWrap.cpp +++ b/accessible/src/atk/AccessibleWrap.cpp @@ -866,66 +866,58 @@ refStateSetCB(AtkObject *aAtkObj) } // Map states TranslateStates(accWrap->State(), state_set); return state_set; } +static void +UpdateAtkRelation(RelationType aType, Accessible* aAcc, + AtkRelationType aAtkType, AtkRelationSet* aAtkSet) +{ + if (aAtkType == ATK_RELATION_NULL) + return; + + AtkRelation* atkRelation = + atk_relation_set_get_relation_by_type(aAtkSet, aAtkType); + if (atkRelation) + atk_relation_set_remove(aAtkSet, atkRelation); + + Relation rel(aAcc->RelationByType(aType)); + nsTArray<AtkObject*> targets; + Accessible* tempAcc = nullptr; + while ((tempAcc = rel.Next())) + targets.AppendElement(AccessibleWrap::GetAtkObject(tempAcc)); + + if (targets.Length()) { + atkRelation = atk_relation_new(targets.Elements(), + targets.Length(), aAtkType); + atk_relation_set_add(aAtkSet, atkRelation); + g_object_unref(atkRelation); + } +} + AtkRelationSet * refRelationSetCB(AtkObject *aAtkObj) { AtkRelationSet* relation_set = ATK_OBJECT_CLASS(parent_class)->ref_relation_set(aAtkObj); AccessibleWrap* accWrap = GetAccessibleWrap(aAtkObj); if (!accWrap) return relation_set; - // Keep in sync with AtkRelationType enum. - static const RelationType relationTypes[] = { - RelationType::CONTROLLED_BY, - RelationType::CONTROLLER_FOR, - RelationType::LABEL_FOR, - RelationType::LABELLED_BY, - RelationType::MEMBER_OF, - RelationType::NODE_CHILD_OF, - RelationType::FLOWS_TO, - RelationType::FLOWS_FROM, - RelationType::SUBWINDOW_OF, - RelationType::EMBEDS, - RelationType::EMBEDDED_BY, - RelationType::POPUP_FOR, - RelationType::PARENT_WINDOW_OF, - RelationType::DESCRIBED_BY, - RelationType::DESCRIPTION_FOR, - RelationType::NODE_PARENT_OF - }; +#define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \ + UpdateAtkRelation(RelationType::geckoType, accWrap, atkType, relation_set); - for (uint32_t i = 0; i < ArrayLength(relationTypes); i++) { - // Shift to 1 to skip ATK_RELATION_NULL. - AtkRelationType atkType = static_cast<AtkRelationType>(i + 1); - AtkRelation* atkRelation = - atk_relation_set_get_relation_by_type(relation_set, atkType); - if (atkRelation) - atk_relation_set_remove(relation_set, atkRelation); +#include "RelationTypeMap.h" - Relation rel(accWrap->RelationByType(relationTypes[i])); - nsTArray<AtkObject*> targets; - Accessible* tempAcc = nullptr; - while ((tempAcc = rel.Next())) - targets.AppendElement(AccessibleWrap::GetAtkObject(tempAcc)); - - if (targets.Length()) { - atkRelation = atk_relation_new(targets.Elements(), targets.Length(), atkType); - atk_relation_set_add(relation_set, atkRelation); - g_object_unref(atkRelation); - } - } +#undef RELATIONTYPE return relation_set; } // Check if aAtkObj is a valid MaiAtkObject, and return the AccessibleWrap // for it. AccessibleWrap* GetAccessibleWrap(AtkObject* aAtkObj)
rename from accessible/src/base/RoleAsserts.cpp rename to accessible/src/base/Asserts.cpp --- a/accessible/src/base/RoleAsserts.cpp +++ b/accessible/src/base/Asserts.cpp @@ -1,17 +1,26 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this file, * You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "nsIAccessibleRelation.h" #include "nsIAccessibleRole.h" +#include "RelationType.h" #include "Role.h" using namespace mozilla::a11y; #define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \ static_assert(static_cast<uint32_t>(roles::geckoRole) \ == static_cast<uint32_t>(nsIAccessibleRole::ROLE_ ## geckoRole), \ "internal and xpcom roles differ!"); #include "RoleMap.h" #undef ROLE + +#define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \ + static_assert(static_cast<uint32_t>(RelationType::geckoType) \ + == static_cast<uint32_t>(nsIAccessibleRelation::RELATION_ ## geckoType), \ + "internal and xpcom relations differ!"); +#include "RelationTypeMap.h" +#undef RELATIONTYPE
new file mode 100644 --- /dev/null +++ b/accessible/src/base/RelationTypeMap.h @@ -0,0 +1,112 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +/** + * Usage: declare the macro RELATIONTYPE()with the following arguments: + * RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) + */ + +RELATIONTYPE(LABELLED_BY, + "labelled by", + ATK_RELATION_LABELLED_BY, + NAVRELATION_LABELLED_BY, + IA2_RELATION_LABELLED_BY) + +RELATIONTYPE(LABEL_FOR, + "label for", + ATK_RELATION_LABEL_FOR, + NAVRELATION_LABEL_FOR, + IA2_RELATION_LABEL_FOR) + +RELATIONTYPE(DESCRIBED_BY, + "described by", + ATK_RELATION_DESCRIBED_BY, + NAVRELATION_DESCRIBED_BY, + IA2_RELATION_DESCRIBED_BY) + +RELATIONTYPE(DESCRIPTION_FOR, + "description for", + ATK_RELATION_DESCRIPTION_FOR, + NAVRELATION_DESCRIPTION_FOR, + IA2_RELATION_DESCRIPTION_FOR) + +RELATIONTYPE(NODE_CHILD_OF, + "node child of", + ATK_RELATION_NODE_CHILD_OF, + NAVRELATION_NODE_CHILD_OF, + IA2_RELATION_NODE_CHILD_OF) + +RELATIONTYPE(NODE_PARENT_OF, + "node parent of", + ATK_RELATION_NODE_PARENT_OF, + NAVRELATION_NODE_PARENT_OF, + IA2_RELATION_NODE_PARENT_OF) + +RELATIONTYPE(CONTROLLED_BY, + "controlled by", + ATK_RELATION_CONTROLLED_BY, + NAVRELATION_CONTROLLED_BY, + IA2_RELATION_CONTROLLED_BY) + +RELATIONTYPE(CONTROLLER_FOR, + "controller for", + ATK_RELATION_CONTROLLER_FOR, + NAVRELATION_CONTROLLER_FOR, + IA2_RELATION_CONTROLLER_FOR) + +RELATIONTYPE(FLOWS_TO, + "flows to", + ATK_RELATION_FLOWS_TO, + NAVRELATION_FLOWS_TO, + IA2_RELATION_FLOWS_TO) + +RELATIONTYPE(FLOWS_FROM, + "flows from", + ATK_RELATION_FLOWS_FROM, + NAVRELATION_FLOWS_FROM, + IA2_RELATION_FLOWS_FROM) + +RELATIONTYPE(MEMBER_OF, + "member of", + ATK_RELATION_MEMBER_OF, + NAVRELATION_MEMBER_OF, + IA2_RELATION_MEMBER_OF) + +RELATIONTYPE(SUBWINDOW_OF, + "subwindow of", + ATK_RELATION_SUBWINDOW_OF, + NAVRELATION_SUBWINDOW_OF, + IA2_RELATION_SUBWINDOW_OF) + +RELATIONTYPE(EMBEDS, + "embeds", + ATK_RELATION_EMBEDS, + NAVRELATION_EMBEDS, + IA2_RELATION_EMBEDS) + +RELATIONTYPE(EMBEDDED_BY, + "embedded by", + ATK_RELATION_EMBEDDED_BY, + NAVRELATION_EMBEDDED_BY, + IA2_RELATION_EMBEDDED_BY) + +RELATIONTYPE(POPUP_FOR, + "popup for", + ATK_RELATION_POPUP_FOR, + NAVRELATION_POPUP_FOR, + IA2_RELATION_POPUP_FOR) + +RELATIONTYPE(PARENT_WINDOW_OF, + "parent window of", + ATK_RELATION_PARENT_WINDOW_OF, + NAVRELATION_PARENT_WINDOW_OF, + IA2_RELATION_PARENT_WINDOW_OF) + +RELATIONTYPE(DEFAULT_BUTTON, + "default button", + ATK_RELATION_NULL, + NAVRELATION_DEFAULT_BUTTON, + IA2_RELATION_NULL)
--- a/accessible/src/base/moz.build +++ b/accessible/src/base/moz.build @@ -32,29 +32,29 @@ if CONFIG['MOZ_DEBUG']: CPP_SOURCES += [ 'AccCollector.cpp', 'AccEvent.cpp', 'AccGroupInfo.cpp', 'AccIterator.cpp', 'ARIAMap.cpp', 'ARIAStateMap.cpp', + 'Asserts.cpp', 'DocManager.cpp', 'EventQueue.cpp', 'Filters.cpp', 'FocusManager.cpp', 'NotificationController.cpp', 'nsAccessibilityService.cpp', 'nsAccessiblePivot.cpp', 'nsAccessNode.cpp', 'nsAccUtils.cpp', 'nsCoreUtils.cpp', 'nsEventShell.cpp', 'nsTextEquivUtils.cpp', - 'RoleAsserts.cpp', 'SelectionManager.cpp', 'StyleInfo.cpp', 'TextAttrs.cpp', 'TextUpdater.cpp', 'TreeWalker.cpp', ] if a11y_log:
--- a/accessible/src/base/nsAccessibilityService.cpp +++ b/accessible/src/base/nsAccessibilityService.cpp @@ -708,23 +708,32 @@ nsAccessibilityService::GetStringEventTy return NS_OK; } // nsIAccessibleRetrieval::getStringRelationType() NS_IMETHODIMP nsAccessibilityService::GetStringRelationType(uint32_t aRelationType, nsAString& aString) { - if (aRelationType >= ArrayLength(kRelationTypeNames)) { - aString.AssignLiteral("unknown"); + NS_ENSURE_ARG(aRelationType <= static_cast<uint32_t>(RelationType::LAST)); + +#define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \ + case RelationType::geckoType: \ + aString.AssignLiteral(geckoTypeName); \ return NS_OK; + + RelationType relationType = static_cast<RelationType>(aRelationType); + switch (relationType) { +#include "RelationTypeMap.h" + default: + aString.AssignLiteral("unknown"); + return NS_OK; } - CopyUTF8toUTF16(kRelationTypeNames[aRelationType], aString); - return NS_OK; +#undef RELATIONTYPE } NS_IMETHODIMP nsAccessibilityService::GetAccessibleFromCache(nsIDOMNode* aNode, nsIAccessible** aAccessible) { NS_ENSURE_ARG_POINTER(aAccessible); *aAccessible = nullptr;
--- a/accessible/src/base/nsAccessibilityService.h +++ b/accessible/src/base/nsAccessibilityService.h @@ -327,34 +327,10 @@ static const char kEventTypeNames[][40] "hypertext link selected", // EVENT_HYPERTEXT_LINK_SELECTED "hyperlink start index changed", // EVENT_HYPERLINK_START_INDEX_CHANGED "hypertext changed", // EVENT_HYPERTEXT_CHANGED "hypertext links count changed", // EVENT_HYPERTEXT_NLINKS_CHANGED "object attribute changed", // EVENT_OBJECT_ATTRIBUTE_CHANGED "virtual cursor changed" // EVENT_VIRTUALCURSOR_CHANGED }; -/** - * Map nsIAccessibleRelation constants to strings. Used by - * nsIAccessibleRetrieval::getStringRelationType() method. - */ -static const char kRelationTypeNames[][20] = { - "labelled by", // RELATION_LABELLED_BY - "label for", // RELATION_LABEL_FOR - "described by", // RELATION_DESCRIBED_BY - "description for", // RELATION_DESCRIPTION_FOR - "node child of", // RELATION_NODE_CHILD_OF - "node parent of", // RELATION_NODE_PARENT_OF - "controlled by", // RELATION_CONTROLLED_BY - "controller for", // RELATION_CONTROLLER_FOR - "flows to", // RELATION_FLOWS_TO - "flows from", // RELATION_FLOWS_FROM - "member of", // RELATION_MEMBER_OF - "subwindow of", // RELATION_SUBWINDOW_OF - "embeds", // RELATION_EMBEDS - "embedded by", // RELATION_EMBEDDED_BY - "popup for", // RELATION_POPUP_FOR - "parent window of", // RELATION_PARENT_WINDOW_OF - "default button" // RELATION_DEFAULT_BUTTON -}; - #endif /* __nsIAccessibilityService_h__ */
--- a/accessible/src/generic/Accessible.cpp +++ b/accessible/src/generic/Accessible.cpp @@ -25,16 +25,17 @@ #include "TableAccessible.h" #include "TableCellAccessible.h" #include "TreeWalker.h" #include "nsIDOMElement.h" #include "nsIDOMDocument.h" #include "nsIDOMNodeFilter.h" #include "nsIDOMHTMLElement.h" +#include "nsIDOMKeyEvent.h" #include "nsIDOMTreeWalker.h" #include "nsIDOMXULButtonElement.h" #include "nsIDOMXULDocument.h" #include "nsIDOMXULElement.h" #include "nsIDOMXULLabelElement.h" #include "nsIDOMXULSelectCntrlEl.h" #include "nsIDOMXULSelectCntrlItemEl.h" #include "nsPIDOMWindow.h"
--- a/accessible/src/windows/ia2/ia2Accessible.cpp +++ b/accessible/src/windows/ia2/ia2Accessible.cpp @@ -55,18 +55,21 @@ ia2Accessible::get_nRelations(long* aNRe if (!aNRelations) return E_INVALIDARG; *aNRelations = 0; AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); if (acc->IsDefunct()) return CO_E_OBJNOTCONNECTED; - for (unsigned int idx = 0; idx < ArrayLength(sRelationTypesForIA2); idx++) { - Relation rel = acc->RelationByType(sRelationTypesForIA2[idx]); + for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { + if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) + continue; + + Relation rel = acc->RelationByType(sRelationTypePairs[idx].first); if (rel.Next()) (*aNRelations)++; } return S_OK; A11Y_TRYBLOCK_END } @@ -80,18 +83,21 @@ ia2Accessible::get_relation(long aRelati return E_INVALIDARG; *aRelation = nullptr; AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); if (acc->IsDefunct()) return CO_E_OBJNOTCONNECTED; long relIdx = 0; - for (unsigned int idx = 0; idx < ArrayLength(sRelationTypesForIA2); idx++) { - RelationType relationType = sRelationTypesForIA2[idx]; + for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs); idx++) { + if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) + continue; + + RelationType relationType = sRelationTypePairs[idx].first; Relation rel = acc->RelationByType(relationType); nsRefPtr<ia2AccessibleRelation> ia2Relation = new ia2AccessibleRelation(relationType, &rel); if (ia2Relation->HasTargets()) { if (relIdx == aRelationIndex) { ia2Relation.forget(aRelation); return S_OK; } @@ -115,19 +121,22 @@ ia2Accessible::get_relations(long aMaxRe if (!aRelation || !aNRelations) return E_INVALIDARG; *aNRelations = 0; AccessibleWrap* acc = static_cast<AccessibleWrap*>(this); if (acc->IsDefunct()) return CO_E_OBJNOTCONNECTED; - for (unsigned int idx = 0; idx < ArrayLength(sRelationTypesForIA2) && + for (uint32_t idx = 0; idx < ArrayLength(sRelationTypePairs) && *aNRelations < aMaxRelations; idx++) { - RelationType relationType = sRelationTypesForIA2[idx]; + if (sRelationTypePairs[idx].second == IA2_RELATION_NULL) + continue; + + RelationType relationType = sRelationTypePairs[idx].first; Relation rel = acc->RelationByType(relationType); nsRefPtr<ia2AccessibleRelation> ia2Rel = new ia2AccessibleRelation(relationType, &rel); if (ia2Rel->HasTargets()) { ia2Rel.forget(aRelation + (*aNRelations)); (*aNRelations)++; } }
--- a/accessible/src/windows/ia2/ia2AccessibleRelation.cpp +++ b/accessible/src/windows/ia2/ia2AccessibleRelation.cpp @@ -28,74 +28,32 @@ ia2AccessibleRelation::ia2AccessibleRela IMPL_IUNKNOWN_QUERY_HEAD(ia2AccessibleRelation) IMPL_IUNKNOWN_QUERY_IFACE(IAccessibleRelation) IMPL_IUNKNOWN_QUERY_IFACE(IUnknown) IMPL_IUNKNOWN_QUERY_TAIL // IAccessibleRelation STDMETHODIMP -ia2AccessibleRelation::get_relationType(BSTR *aRelationType) +ia2AccessibleRelation::get_relationType(BSTR* aRelationType) { A11Y_TRYBLOCK_BEGIN if (!aRelationType) return E_INVALIDARG; *aRelationType = nullptr; +#define RELATIONTYPE(geckoType, geckoTypeName, atkType, msaaType, ia2Type) \ + case RelationType::geckoType: \ + *aRelationType = ::SysAllocString(ia2Type); \ + break; + switch (mType) { - case RelationType::CONTROLLED_BY: - *aRelationType = ::SysAllocString(IA2_RELATION_CONTROLLED_BY); - break; - case RelationType::CONTROLLER_FOR: - *aRelationType = ::SysAllocString(IA2_RELATION_CONTROLLER_FOR); - break; - case RelationType::DESCRIBED_BY: - *aRelationType = ::SysAllocString(IA2_RELATION_DESCRIBED_BY); - break; - case RelationType::DESCRIPTION_FOR: - *aRelationType = ::SysAllocString(IA2_RELATION_DESCRIPTION_FOR); - break; - case RelationType::EMBEDDED_BY: - *aRelationType = ::SysAllocString(IA2_RELATION_EMBEDDED_BY); - break; - case RelationType::EMBEDS: - *aRelationType = ::SysAllocString(IA2_RELATION_EMBEDS); - break; - case RelationType::FLOWS_FROM: - *aRelationType = ::SysAllocString(IA2_RELATION_FLOWS_FROM); - break; - case RelationType::FLOWS_TO: - *aRelationType = ::SysAllocString(IA2_RELATION_FLOWS_TO); - break; - case RelationType::LABEL_FOR: - *aRelationType = ::SysAllocString(IA2_RELATION_LABEL_FOR); - break; - case RelationType::LABELLED_BY: - *aRelationType = ::SysAllocString(IA2_RELATION_LABELED_BY); - break; - case RelationType::MEMBER_OF: - *aRelationType = ::SysAllocString(IA2_RELATION_MEMBER_OF); - break; - case RelationType::NODE_CHILD_OF: - *aRelationType = ::SysAllocString(IA2_RELATION_NODE_CHILD_OF); - break; - case RelationType::NODE_PARENT_OF: - *aRelationType = ::SysAllocString(IA2_RELATION_NODE_PARENT_OF); - break; - case RelationType::PARENT_WINDOW_OF: - *aRelationType = ::SysAllocString(IA2_RELATION_PARENT_WINDOW_OF); - break; - case RelationType::POPUP_FOR: - *aRelationType = ::SysAllocString(IA2_RELATION_POPUP_FOR); - break; - case RelationType::SUBWINDOW_OF: - *aRelationType = ::SysAllocString(IA2_RELATION_SUBWINDOW_OF); - break; +#include "RelationTypeMap.h" } return *aRelationType ? S_OK : E_OUTOFMEMORY; A11Y_TRYBLOCK_END } STDMETHODIMP @@ -161,9 +119,8 @@ ia2AccessibleRelation::get_targets(long for (long idx = 0; idx < maxTargets; idx++) get_target(idx, aTargets + idx); *aNTargets = maxTargets; return S_OK; A11Y_TRYBLOCK_END } -
--- a/accessible/src/windows/ia2/ia2AccessibleRelation.h +++ b/accessible/src/windows/ia2/ia2AccessibleRelation.h @@ -7,16 +7,17 @@ #ifndef _NS_ACCESSIBLE_RELATION_WRAP_H #define _NS_ACCESSIBLE_RELATION_WRAP_H #include "Accessible.h" #include "IUnknownImpl.h" #include "nsIAccessibleRelation.h" +#include <utility> #include "nsTArray.h" #include "AccessibleRelation.h" namespace mozilla { namespace a11y { class ia2AccessibleRelation MOZ_FINAL : public IAccessibleRelation @@ -55,34 +56,27 @@ private: ia2AccessibleRelation& operator = (const ia2AccessibleRelation&); RelationType mType; nsTArray<nsRefPtr<Accessible> > mTargets; }; /** - * Relations exposed to IAccessible2. + * Gecko to IAccessible2 relation types map. */ -static const RelationType sRelationTypesForIA2[] = { - RelationType::LABELLED_BY, - RelationType::LABEL_FOR, - RelationType::DESCRIBED_BY, - RelationType::DESCRIPTION_FOR, - RelationType::NODE_CHILD_OF, - RelationType::NODE_PARENT_OF, - RelationType::CONTROLLED_BY, - RelationType::CONTROLLER_FOR, - RelationType::FLOWS_TO, - RelationType::FLOWS_FROM, - RelationType::MEMBER_OF, - RelationType::SUBWINDOW_OF, - RelationType::EMBEDS, - RelationType::EMBEDDED_BY, - RelationType::POPUP_FOR, - RelationType::PARENT_WINDOW_OF + +const WCHAR *const IA2_RELATION_NULL = L""; + +#define RELATIONTYPE(geckoType, name, atkType, msaaType, ia2Type) \ + std::pair<RelationType, const WCHAR *const>(RelationType::geckoType, ia2Type), + +static const std::pair<RelationType, const WCHAR *const> sRelationTypePairs[] = { +#include "RelationTypeMap.h" }; +#undef RELATIONTYPE + } // namespace a11y } // namespace mozilla #endif
--- a/accessible/src/windows/msaa/AccessibleWrap.cpp +++ b/accessible/src/windows/msaa/AccessibleWrap.cpp @@ -893,16 +893,21 @@ AccessibleWrap::accNavigate( return E_INVALIDARG; if (accessible->IsDefunct()) return CO_E_OBJNOTCONNECTED; Accessible* navAccessible = nullptr; Maybe<RelationType> xpRelation; +#define RELATIONTYPE(geckoType, stringType, atkType, msaaType, ia2Type) \ + case msaaType: \ + xpRelation.construct(RelationType::geckoType); \ + break; + switch(navDir) { case NAVDIR_FIRSTCHILD: if (!nsAccUtils::MustPrune(accessible)) navAccessible = accessible->FirstChild(); break; case NAVDIR_LASTCHILD: if (!nsAccUtils::MustPrune(accessible)) navAccessible = accessible->LastChild(); @@ -915,72 +920,24 @@ AccessibleWrap::accNavigate( break; case NAVDIR_DOWN: case NAVDIR_LEFT: case NAVDIR_RIGHT: case NAVDIR_UP: return E_NOTIMPL; // MSAA relationship extensions to accNavigate - case NAVRELATION_CONTROLLED_BY: - xpRelation.construct(RelationType::CONTROLLED_BY); - break; - case NAVRELATION_CONTROLLER_FOR: - xpRelation.construct(RelationType::CONTROLLER_FOR); - break; - case NAVRELATION_LABEL_FOR: - xpRelation.construct(RelationType::LABEL_FOR); - break; - case NAVRELATION_LABELLED_BY: - xpRelation.construct(RelationType::LABELLED_BY); - break; - case NAVRELATION_MEMBER_OF: - xpRelation.construct(RelationType::MEMBER_OF); - break; - case NAVRELATION_NODE_CHILD_OF: - xpRelation.construct(RelationType::NODE_CHILD_OF); - break; - case NAVRELATION_FLOWS_TO: - xpRelation.construct(RelationType::FLOWS_TO); - break; - case NAVRELATION_FLOWS_FROM: - xpRelation.construct(RelationType::FLOWS_FROM); - break; - case NAVRELATION_SUBWINDOW_OF: - xpRelation.construct(RelationType::SUBWINDOW_OF); - break; - case NAVRELATION_EMBEDS: - xpRelation.construct(RelationType::EMBEDS); - break; - case NAVRELATION_EMBEDDED_BY: - xpRelation.construct(RelationType::EMBEDDED_BY); - break; - case NAVRELATION_POPUP_FOR: - xpRelation.construct(RelationType::POPUP_FOR); - break; - case NAVRELATION_PARENT_WINDOW_OF: - xpRelation.construct(RelationType::PARENT_WINDOW_OF); - break; - case NAVRELATION_DEFAULT_BUTTON: - xpRelation.construct(RelationType::DEFAULT_BUTTON); - break; - case NAVRELATION_DESCRIBED_BY: - xpRelation.construct(RelationType::DESCRIBED_BY); - break; - case NAVRELATION_DESCRIPTION_FOR: - xpRelation.construct(RelationType::DESCRIPTION_FOR); - break; - case NAVRELATION_NODE_PARENT_OF: - xpRelation.construct(RelationType::NODE_PARENT_OF); - break; +#include "RelationTypeMap.h" default: return E_INVALIDARG; } +#undef RELATIONTYPE + pvarEndUpAt->vt = VT_EMPTY; if (!xpRelation.empty()) { Relation rel = RelationByType(xpRelation.ref()); navAccessible = rel.Next(); } if (!navAccessible)
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -589,17 +589,17 @@ pref("browser.gesture.pinch.in.shift", " #endif pref("browser.gesture.twist.latched", false); pref("browser.gesture.twist.threshold", 0); pref("browser.gesture.twist.right", "cmd_gestureRotateRight"); pref("browser.gesture.twist.left", "cmd_gestureRotateLeft"); pref("browser.gesture.twist.end", "cmd_gestureRotateEnd"); pref("browser.gesture.tap", "cmd_fullZoomReset"); -pref("browser.snapshots.limit", 0); +pref("browser.snapshots.limit", 5); // 0: Nothing happens // 1: Scrolling contents // 2: Go back or go forward, in your history // 3: Zoom in or out. #ifdef XP_MACOSX // On OS X, if the wheel has one axis only, shift+wheel comes through as a // horizontal scroll event. Thus, we can't assign anything other than normal
--- a/browser/devtools/debugger/debugger-controller.js +++ b/browser/devtools/debugger/debugger-controller.js @@ -73,25 +73,28 @@ const EVENTS = { // When the widgets layout has been changed. LAYOUT_CHANGED: "Debugger:LayoutChanged" }; Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/devtools/dbg-client.jsm"); -let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise; Cu.import("resource:///modules/devtools/shared/event-emitter.js"); -Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm"); Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm"); Cu.import("resource:///modules/devtools/SideMenuWidget.jsm"); Cu.import("resource:///modules/devtools/VariablesView.jsm"); Cu.import("resource:///modules/devtools/VariablesViewController.jsm"); Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); +const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require; +const Editor = require("devtools/sourceeditor/editor"); +const promise = require("sdk/core/promise"); +const DebuggerEditor = require("devtools/sourceeditor/debugger.js"); + XPCOMUtils.defineLazyModuleGetter(this, "Parser", "resource:///modules/devtools/Parser.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "devtools", "resource://gre/modules/devtools/Loader.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "DevToolsUtils", "resource://gre/modules/devtools/DevToolsUtils.jsm"); @@ -603,17 +606,17 @@ StackFrames.prototype = { this.activeThread.fillFrames(CALL_STACK_PAGE_SIZE); DebuggerView.editor.focus(); }, /** * Handler for the thread client's resumed notification. */ _onResumed: function() { - DebuggerView.editor.setDebugLocation(-1); + DebuggerView.editor.clearDebugLocation(); // Prepare the watch expression evaluation string for the next pause. if (!this._isWatchExpressionsEvaluation) { this._currentWatchExpressions = this._syncedWatchExpressions; } }, /** @@ -1429,17 +1432,16 @@ EventListeners.prototype = { DebuggerView.EventListeners.commit(); } }; /** * Handles all the breakpoints in the current debugger. */ function Breakpoints() { - this._onEditorBreakpointChange = this._onEditorBreakpointChange.bind(this); this._onEditorBreakpointAdd = this._onEditorBreakpointAdd.bind(this); this._onEditorBreakpointRemove = this._onEditorBreakpointRemove.bind(this); this.addBreakpoint = this.addBreakpoint.bind(this); this.removeBreakpoint = this.removeBreakpoint.bind(this); } Breakpoints.prototype = { /** @@ -1452,90 +1454,76 @@ Breakpoints.prototype = { /** * Adds the source editor breakpoint handlers. * * @return object * A promise that is resolved when the breakpoints finishes initializing. */ initialize: function() { - DebuggerView.editor.addEventListener( - SourceEditor.EVENTS.BREAKPOINT_CHANGE, this._onEditorBreakpointChange); + DebuggerView.editor.on("breakpointAdded", this._onEditorBreakpointAdd); + DebuggerView.editor.on("breakpointRemoved", this._onEditorBreakpointRemove); // Initialization is synchronous, for now. return promise.resolve(null); }, /** * Removes the source editor breakpoint handlers & all the added breakpoints. * * @return object * A promise that is resolved when the breakpoints finishes destroying. */ destroy: function() { - DebuggerView.editor.removeEventListener( - SourceEditor.EVENTS.BREAKPOINT_CHANGE, this._onEditorBreakpointChange); + DebuggerView.editor.off("breakpointAdded", this._onEditorBreakpointAdd); + DebuggerView.editor.off("breakpointRemoved", this._onEditorBreakpointRemove); return this.removeAllBreakpoints(); }, /** - * Event handler for breakpoint changes that happen in the editor. This - * function syncs the breakpoints in the editor to those in the debugger. - * - * @param object aEvent - * The SourceEditor.EVENTS.BREAKPOINT_CHANGE event object. - */ - _onEditorBreakpointChange: function(aEvent) { - aEvent.added.forEach(this._onEditorBreakpointAdd, this); - aEvent.removed.forEach(this._onEditorBreakpointRemove, this); - }, - - /** * Event handler for new breakpoints that come from the editor. * - * @param object aEditorBreakpoint - * The breakpoint object coming from the editor. + * @param number aLine + * Line number where breakpoint was set. */ - _onEditorBreakpointAdd: function(aEditorBreakpoint) { + _onEditorBreakpointAdd: function(_, aLine) { let url = DebuggerView.Sources.selectedValue; - let line = aEditorBreakpoint.line + 1; - let location = { url: url, line: line }; + let location = { url: url, line: aLine + 1 }; // Initialize the breakpoint, but don't update the editor, since this // callback is invoked because a breakpoint was added in the editor itself. this.addBreakpoint(location, { noEditorUpdate: true }).then(aBreakpointClient => { // If the breakpoint client has an "requestedLocation" attached, then // the original requested placement for the breakpoint wasn't accepted. // In this case, we need to update the editor with the new location. if (aBreakpointClient.requestedLocation) { DebuggerView.editor.removeBreakpoint(aBreakpointClient.requestedLocation.line - 1); DebuggerView.editor.addBreakpoint(aBreakpointClient.location.line - 1); } // Notify that we've shown a breakpoint in the source editor. - window.emit(EVENTS.BREAKPOINT_SHOWN, aEditorBreakpoint); + window.emit(EVENTS.BREAKPOINT_SHOWN); }); }, /** * Event handler for breakpoints that are removed from the editor. * - * @param object aEditorBreakpoint - * The breakpoint object that was removed from the editor. + * @param number aLine + * Line number where breakpoint was removed. */ - _onEditorBreakpointRemove: function(aEditorBreakpoint) { + _onEditorBreakpointRemove: function(_, aLine) { let url = DebuggerView.Sources.selectedValue; - let line = aEditorBreakpoint.line + 1; - let location = { url: url, line: line }; + let location = { url: url, line: aLine + 1 }; // Destroy the breakpoint, but don't update the editor, since this callback // is invoked because a breakpoint was removed from the editor itself. this.removeBreakpoint(location, { noEditorUpdate: true }).then(() => { // Notify that we've hidden a breakpoint in the source editor. - window.emit(EVENTS.BREAKPOINT_HIDDEN, aEditorBreakpoint); + window.emit(EVENTS.BREAKPOINT_HIDDEN); }); }, /** * Update the breakpoints in the editor view. This function takes the list of * breakpoints in the debugger and adds them back into the editor view. * This is invoked when the selected script is changed, or when new sources * are received via the _onNewSource and _onSourcesAdded event listeners. @@ -1639,17 +1627,17 @@ Breakpoints.prototype = { // so that they may not be forgotten across target navigations. this._disabled.delete(identifier); // Preserve information about the breakpoint's line text, to display it // in the sources pane without requiring fetching the source (for example, // after the target navigated). Note that this will get out of sync // if the source text contents change. let line = aBreakpointClient.location.line - 1; - aBreakpointClient.text = DebuggerView.getEditorLineText(line).trim(); + aBreakpointClient.text = DebuggerView.editor.getText(line).trim(); // Show the breakpoint in the editor and breakpoints pane, and resolve. this._showBreakpoint(aBreakpointClient, aOptions); // Notify that we've added a breakpoint. window.emit(EVENTS.BREAKPOINT_ADDED, aBreakpointClient); deferred.resolve(aBreakpointClient); });
--- a/browser/devtools/debugger/debugger-panes.js +++ b/browser/devtools/debugger/debugger-panes.js @@ -9,17 +9,17 @@ * Functions handling the sources UI. */ function SourcesView() { dumpn("SourcesView was instantiated"); this.togglePrettyPrint = this.togglePrettyPrint.bind(this); this._onEditorLoad = this._onEditorLoad.bind(this); this._onEditorUnload = this._onEditorUnload.bind(this); - this._onEditorSelection = this._onEditorSelection.bind(this); + this._onEditorCursorActivity = this._onEditorCursorActivity.bind(this); this._onEditorContextMenu = this._onEditorContextMenu.bind(this); this._onSourceSelect = this._onSourceSelect.bind(this); this._onSourceClick = this._onSourceClick.bind(this); this._onBreakpointRemoved = this._onBreakpointRemoved.bind(this); this._onSourceCheck = this._onSourceCheck.bind(this); this._onStopBlackBoxing = this._onStopBlackBoxing.bind(this); this._onBreakpointClick = this._onBreakpointClick.bind(this); this._onBreakpointCheckboxClick = this._onBreakpointCheckboxClick.bind(this); @@ -474,17 +474,22 @@ SourcesView.prototype = Heritage.extend( } }, /** * Hides a conditional breakpoint's expression input popup. */ _hideConditionalPopup: function() { this._cbPanel.hidden = true; - this._cbPanel.hidePopup(); + + // Sometimes this._cbPanel doesn't have hidePopup method which doesn't + // break anything but simply outputs an exception to the console. + if (this._cbPanel.hidePopup) { + this._cbPanel.hidePopup(); + } }, /** * Customization function for creating a breakpoint item's UI. * * @param object aOptions * A couple of options or flags supported by this operation: * - location: the breakpoint's source location and line number @@ -640,53 +645,51 @@ SourcesView.prototype = Heritage.extend( this._selectedBreakpointItem = null; } }, /** * The load listener for the source editor. */ _onEditorLoad: function(aName, aEditor) { - aEditor.addEventListener(SourceEditor.EVENTS.SELECTION, this._onEditorSelection, false); - aEditor.addEventListener(SourceEditor.EVENTS.CONTEXT_MENU, this._onEditorContextMenu, false); + aEditor.on("cursorActivity", this._onEditorCursorActivity); + aEditor.on("contextMenu", this._onEditorContextMenu); }, /** * The unload listener for the source editor. */ _onEditorUnload: function(aName, aEditor) { - aEditor.removeEventListener(SourceEditor.EVENTS.SELECTION, this._onEditorSelection, false); - aEditor.removeEventListener(SourceEditor.EVENTS.CONTEXT_MENU, this._onEditorContextMenu, false); + aEditor.off("cursorActivity", this._onEditorCursorActivity); + aEditor.off("contextMenu", this._onEditorContextMenu); }, /** * The selection listener for the source editor. */ - _onEditorSelection: function(e) { - let { start, end } = e.newValue; + _onEditorCursorActivity: function(e) { + let editor = DebuggerView.editor; + let start = editor.getCursor("start").line + 1; + let end = editor.getCursor().line + 1; + let url = this.selectedValue; - let url = this.selectedValue; - let lineStart = DebuggerView.editor.getLineAtOffset(start) + 1; - let lineEnd = DebuggerView.editor.getLineAtOffset(end) + 1; - let location = { url: url, line: lineStart }; + let location = { url: url, line: start }; - if (this.getBreakpoint(location) && lineStart == lineEnd) { + if (this.getBreakpoint(location) && start == end) { this.highlightBreakpoint(location, { noEditorUpdate: true }); } else { this.unhighlightBreakpoint(); } }, /** * The context menu listener for the source editor. */ _onEditorContextMenu: function({ x, y }) { - let offset = DebuggerView.editor.getOffsetAtLocation(x, y); - let line = DebuggerView.editor.getLineAtOffset(offset); - this._editorContextMenuLineNumber = line; + this._editorContextMenuLineNumber = DebuggerView.editor.getPositionFromCoords(x, y).line; }, /** * The select listener for the sources container. */ _onSourceSelect: function({ detail: sourceItem }) { if (!sourceItem) { return; @@ -864,23 +867,24 @@ SourcesView.prototype = Heritage.extend( /** * Called when the add breakpoint key sequence was pressed. */ _onCmdAddBreakpoint: function() { // If this command was executed via the context menu, add the breakpoint // on the currently hovered line in the source editor. if (this._editorContextMenuLineNumber >= 0) { - DebuggerView.editor.setCaretPosition(this._editorContextMenuLineNumber); + DebuggerView.editor.setCursor({ line: this._editorContextMenuLineNumber, ch: 0 }); } + // Avoid placing breakpoints incorrectly when using key shortcuts. this._editorContextMenuLineNumber = -1; let url = DebuggerView.Sources.selectedValue; - let line = DebuggerView.editor.getCaretPosition().line + 1; + let line = DebuggerView.editor.getCursor().line + 1; let location = { url: url, line: line }; let breakpointItem = this.getBreakpoint(location); // If a breakpoint already existed, remove it now. if (breakpointItem) { DebuggerController.Breakpoints.removeBreakpoint(location); } // No breakpoint existed at the required location, add one now. @@ -891,23 +895,24 @@ SourcesView.prototype = Heritage.extend( /** * Called when the add conditional breakpoint key sequence was pressed. */ _onCmdAddConditionalBreakpoint: function() { // If this command was executed via the context menu, add the breakpoint // on the currently hovered line in the source editor. if (this._editorContextMenuLineNumber >= 0) { - DebuggerView.editor.setCaretPosition(this._editorContextMenuLineNumber); + DebuggerView.editor.setCursor({ line: this._editorContextMenuLineNumber, ch: 0 }); } + // Avoid placing breakpoints incorrectly when using key shortcuts. this._editorContextMenuLineNumber = -1; let url = DebuggerView.Sources.selectedValue; - let line = DebuggerView.editor.getCaretPosition().line + 1; + let line = DebuggerView.editor.getCursor().line + 1; let location = { url: url, line: line }; let breakpointItem = this.getBreakpoint(location); // If a breakpoint already existed or wasn't a conditional, morph it now. if (breakpointItem) { this.highlightBreakpoint(location, { openPopup: true }); } // No breakpoint existed at the required location, add one now. @@ -1451,17 +1456,17 @@ WatchExpressionsView.prototype = Heritag }, /** * Called when the add watch expression key sequence was pressed. */ _onCmdAddExpression: function(aText) { // Only add a new expression if there's no pending input. if (this.getAllStrings().indexOf("") == -1) { - this.addExpression(aText || DebuggerView.editor.getSelectedText()); + this.addExpression(aText || DebuggerView.editor.getSelection()); } }, /** * Called when the remove all watch expressions key sequence was pressed. */ _onCmdRemoveAllExpressions: function() { // Empty the view of all the watch expressions and clear the cache. @@ -2095,22 +2100,19 @@ GlobalSearchView.prototype = Heritage.ex sourceResultsItem.instance.expand(); this._currentlyFocusedMatch = LineResults.indexOfElement(target); this._scrollMatchIntoViewIfNeeded(target); this._bounceMatch(target); let url = sourceResultsItem.instance.url; let line = lineResultsItem.instance.line; + DebuggerView.setEditorLocation(url, line + 1, { noDebug: true }); - - let editor = DebuggerView.editor; - let offset = editor.getCaretOffset(); - let { start, length } = lineResultsItem.lineData.range; - editor.setSelection(offset + start, offset + start + length); + DebuggerView.editor.extendSelection(lineResultsItem.lineData.range); }, /** * Scrolls a match into view if not already visible. * * @param nsIDOMNode aMatch * The match to scroll into view. */
--- a/browser/devtools/debugger/debugger-toolbar.js +++ b/browser/devtools/debugger/debugger-toolbar.js @@ -856,38 +856,35 @@ FilterView.prototype = { * Performs a line search if necessary. * (Jump to lines in the currently visible source). * * @param number aLine * The source line number to jump to. */ _performLineSearch: function(aLine) { // Make sure we're actually searching for a valid line. - if (!aLine) { - return; + if (aLine) { + DebuggerView.editor.setCursor({ line: aLine - 1, ch: 0 }); } - DebuggerView.editor.setCaretPosition(aLine - 1); }, /** * Performs a token search if necessary. * (Search for tokens in the currently visible source). * * @param string aToken * The source token to find. */ _performTokenSearch: function(aToken) { // Make sure we're actually searching for a valid token. if (!aToken) { return; } - let offset = DebuggerView.editor.find(aToken, { ignoreCase: true }); - if (offset > -1) { - DebuggerView.editor.setSelection(offset, offset + aToken.length) - } + + DebuggerView.editor.find(aToken); }, /** * The click listener for the search container. */ _onClick: function() { // If there's some text in the searchbox, displaying a panel would // interfere with double/triple click default behaviors. @@ -1038,35 +1035,29 @@ FilterView.prototype = { } else { this.clearSearch(); } return; } // Jump to the next/previous instance of the currently searched token. if (isTokenSearch) { - let [, token] = args; - let methods = { selectNext: "findNext", selectPrev: "findPrevious" }; - - // Search for the token and select it. - let offset = DebuggerView.editor[methods[actionToPerform]](true); - if (offset > -1) { - DebuggerView.editor.setSelection(offset, offset + token.length) - } + let methods = { selectNext: "findNext", selectPrev: "findPrev" }; + DebuggerView.editor[methods[actionToPerform]](); return; } // Increment/decrement the currently searched caret line. if (isLineSearch) { let [, line] = args; let amounts = { selectNext: 1, selectPrev: -1 }; // Modify the line number and jump to it. line += !isReturnKey ? amounts[actionToPerform] : 0; - let lineCount = DebuggerView.editor.getLineCount(); + let lineCount = DebuggerView.editor.lineCount(); let lineTarget = line < 1 ? 1 : line > lineCount ? lineCount : line; this._doSearch(SEARCH_LINE_FLAG, lineTarget); return; } }, /** * The blur listener for the search container. @@ -1079,17 +1070,17 @@ FilterView.prototype = { * Called when a filtering key sequence was pressed. * * @param string aOperator * The operator to use for filtering. */ _doSearch: function(aOperator = "", aText = "") { this._searchbox.focus(); this._searchbox.value = ""; // Need to clear value beforehand. Bug 779738. - this._searchbox.value = aOperator + (aText || DebuggerView.editor.getSelectedText()); + this._searchbox.value = aOperator + (aText || DebuggerView.editor.getSelection()); }, /** * Called when the source location filter key sequence was pressed. */ _doFileSearch: function() { this._doSearch(); this._searchboxHelpPanel.openPopup(this._searchbox);
--- a/browser/devtools/debugger/debugger-view.js +++ b/browser/devtools/debugger/debugger-view.js @@ -23,23 +23,16 @@ const GLOBAL_SEARCH_EXPAND_MAX_RESULTS = const GLOBAL_SEARCH_LINE_MAX_LENGTH = 300; // chars const GLOBAL_SEARCH_ACTION_MAX_DELAY = 1500; // ms const FUNCTION_SEARCH_ACTION_MAX_DELAY = 400; // ms const SEARCH_GLOBAL_FLAG = "!"; const SEARCH_FUNCTION_FLAG = "@"; const SEARCH_TOKEN_FLAG = "#"; const SEARCH_LINE_FLAG = ":"; const SEARCH_VARIABLE_FLAG = "*"; -const DEFAULT_EDITOR_CONFIG = { - mode: SourceEditor.MODES.TEXT, - readOnly: true, - showLineNumbers: true, - showAnnotationRuler: true, - showOverviewRuler: true -}; /** * Object defining the debugger view components. */ let DebuggerView = { /** * Initializes the debugger view. * @@ -182,29 +175,53 @@ let DebuggerView = { case "properties": window.emit(EVENTS.FETCHED_PROPERTIES); break; } }); }, /** - * Initializes the SourceEditor instance. + * Initializes the Editor instance. * * @param function aCallback * Called after the editor finishes initializing. */ _initializeEditor: function(aCallback) { dumpn("Initializing the DebuggerView editor"); - this.editor = new SourceEditor(); - this.editor.init(document.getElementById("editor"), DEFAULT_EDITOR_CONFIG, () => { + // This needs to be more localizable: see bug 929234. + let extraKeys = {}; + extraKeys[(Services.appinfo.OS == "Darwin" ? "Cmd-" : "Ctrl-") + "F"] = (cm) => { + DebuggerView.Filtering._doTokenSearch(); + }; + + this.editor = new Editor({ + mode: Editor.modes.text, + readOnly: true, + lineNumbers: true, + showAnnotationRuler: true, + gutters: [ "breakpoints" ], + extraKeys: extraKeys, + contextMenu: "sourceEditorContextMenu" + }); + + this.editor.appendTo(document.getElementById("editor")).then(() => { + this.editor.extend(DebuggerEditor); this._loadingText = L10N.getStr("loadingText"); this._onEditorLoad(aCallback); }); + + this.editor.on("gutterClick", (ev, line) => { + if (this.editor.hasBreakpoint(line)) { + this.editor.removeBreakpoint(line); + } else { + this.editor.addBreakpoint(line); + } + }); }, /** * The load event handler for the source editor, also executing any necessary * post-load operations. * * @param function aCallback * Called after the editor finishes loading. @@ -214,17 +231,17 @@ let DebuggerView = { DebuggerController.Breakpoints.initialize().then(() => { window.emit(EVENTS.EDITOR_LOADED, this.editor); aCallback(); }); }, /** - * Destroys the SourceEditor instance and also executes any necessary + * Destroys the Editor instance and also executes any necessary * post-unload operations. * * @param function aCallback * Called after the editor finishes destroying. */ _destroyEditor: function(aCallback) { dumpn("Destroying the DebuggerView editor"); @@ -271,50 +288,52 @@ let DebuggerView = { /** * Sets the currently displayed text contents in the source editor. * This resets the mode and undo stack. * * @param string aTextContent * The source text content. */ _setEditorText: function(aTextContent = "") { - this.editor.setMode(SourceEditor.MODES.TEXT); + this.editor.setMode(Editor.modes.text); this.editor.setText(aTextContent); - this.editor.resetUndo(); + this.editor.clearHistory(); }, /** * Sets the proper editor mode (JS or HTML) according to the specified * content type, or by determining the type from the url or text content. * * @param string aUrl * The source url. * @param string aContentType [optional] * The source content type. * @param string aTextContent [optional] * The source text content. */ _setEditorMode: function(aUrl, aContentType = "", aTextContent = "") { // Avoid setting the editor mode for very large files. + // Is this still necessary? See bug 929225. if (aTextContent.length >= SOURCE_SYNTAX_HIGHLIGHT_MAX_FILE_SIZE) { - this.editor.setMode(SourceEditor.MODES.TEXT); + return void this.editor.setMode(Editor.modes.text); } + // Use JS mode for files with .js and .jsm extensions. - else if (SourceUtils.isJavaScript(aUrl, aContentType)) { - this.editor.setMode(SourceEditor.MODES.JAVASCRIPT); + if (SourceUtils.isJavaScript(aUrl, aContentType)) { + return void this.editor.setMode(Editor.modes.js); } + // Use HTML mode for files in which the first non whitespace character is // <, regardless of extension. - else if (aTextContent.match(/^\s*</)) { - this.editor.setMode(SourceEditor.MODES.HTML); + if (aTextContent.match(/^\s*</)) { + return void this.editor.setMode(Editor.modes.html); } - // Unknown languange, use plain text. - else { - this.editor.setMode(SourceEditor.MODES.TEXT); - } + + // Unknown language, use text. + this.editor.setMode(Editor.modes.text); }, /** * Sets the currently displayed source text in the editor. * * You should use DebuggerView.updateEditor instead. It updates the current * caret and debug location based on a requested url and line. * @@ -410,53 +429,46 @@ let DebuggerView = { if (frame && frame.where.url == aUrl) { aLine = frame.where.line; } } let sourceItem = this.Sources.getItemByValue(aUrl); let sourceForm = sourceItem.attachment.source; + // Once we change the editor location, it replaces editor's contents. + // This means that the debug location information is now obsolete, so + // we need to clear it. We set a new location below, in this function. + this.editor.clearDebugLocation(); + // Make sure the requested source client is shown in the editor, then // update the source editor's caret position and debug location. return this._setEditorSource(sourceForm, aFlags).then(() => { // Line numbers in the source editor should start from 1. If invalid // or not specified, then don't do anything. if (aLine < 1) { return; } + if (aFlags.charOffset) { - aLine += this.editor.getLineAtOffset(aFlags.charOffset); + aLine += this.editor.getPosition(aFlags.charOffset).line; } + if (aFlags.lineOffset) { aLine += aFlags.lineOffset; } + if (!aFlags.noCaret) { - this.editor.setCaretPosition(aLine - 1, aFlags.columnOffset); + this.editor.setCursor({ line: aLine -1, ch: aFlags.columnOffset || 0 }); } - if (!aFlags.noDebug) { - this.editor.setDebugLocation(aLine - 1, aFlags.columnOffset); - } - }); - }, - /** - * Gets the text in the source editor's specified line. - * - * @param number aLine [optional] - * The line to get the text from. If unspecified, it defaults to - * the current caret position. - * @return string - * The specified line's text. - */ - getEditorLineText: function(aLine) { - let line = aLine || this.editor.getCaretPosition().line; - let start = this.editor.getLineStart(line); - let end = this.editor.getLineEnd(line); - return this.editor.getText(start, end); + if (!aFlags.noDebug) { + this.editor.setDebugLocation(aLine - 1); + } + }).then(null, console.error); }, /** * Gets the visibility state of the instruments pane. * @return boolean */ get instrumentsPaneHidden() this._instrumentsPane.hasAttribute("pane-collapsed"), @@ -594,19 +606,19 @@ let DebuggerView = { this.GlobalSearch.clearView(); this.ChromeGlobals.empty(); this.StackFrames.empty(); this.Sources.empty(); this.Variables.empty(); this.EventListeners.empty(); if (this.editor) { - this.editor.setMode(SourceEditor.MODES.TEXT); + this.editor.setMode(Editor.modes.text); this.editor.setText(""); - this.editor.resetUndo(); + this.editor.clearHistory(); this._editorSource = {}; } }, _startup: null, _shutdown: null, Toolbar: null, Options: null,
--- a/browser/devtools/debugger/debugger.xul +++ b/browser/devtools/debugger/debugger.xul @@ -8,33 +8,31 @@ <?xml-stylesheet href="chrome://browser/skin/devtools/common.css" type="text/css"?> <?xml-stylesheet href="chrome://browser/skin/devtools/widgets.css" type="text/css"?> <?xml-stylesheet href="chrome://browser/skin/devtools/debugger.css" type="text/css"?> <!DOCTYPE window [ <!ENTITY % debuggerDTD SYSTEM "chrome://browser/locale/devtools/debugger.dtd"> %debuggerDTD; ]> <?xul-overlay href="chrome://global/content/editMenuOverlay.xul"?> -<?xul-overlay href="chrome://browser/content/devtools/source-editor-overlay.xul"?> <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" macanimationtype="document" fullscreenbutton="true" screenX="4" screenY="4" width="960" height="480" persist="screenX screenY width height sizemode"> <script type="text/javascript" src="chrome://global/content/globalOverlay.js"/> <script type="text/javascript" src="debugger-controller.js"/> <script type="text/javascript" src="debugger-view.js"/> <script type="text/javascript" src="debugger-toolbar.js"/> <script type="text/javascript" src="debugger-panes.js"/> <commandset id="editMenuCommands"/> - <commandset id="sourceEditorCommands"/> <commandset id="debuggerCommands"> <command id="prettyPrintCommand" oncommand="DebuggerView.Sources.togglePrettyPrint()"/> <command id="unBlackBoxButton" oncommand="DebuggerView.Sources._onStopBlackBoxing()"/> <command id="nextSourceCommand" oncommand="DebuggerView.Sources.selectNextItem()"/> @@ -81,33 +79,33 @@ <command id="toggleShowVariablesFilterBox" oncommand="DebuggerView.Options._toggleShowVariablesFilterBox()"/> <command id="toggleShowOriginalSource" oncommand="DebuggerView.Options._toggleShowOriginalSource()"/> </commandset> <popupset id="debuggerPopupset"> <menupopup id="sourceEditorContextMenu" - onpopupshowing="goUpdateSourceEditorMenuItems()"> + onpopupshowing="goUpdateGlobalEditMenuItems()"> <menuitem id="se-dbg-cMenu-addBreakpoint" label="&debuggerUI.seMenuBreak;" key="addBreakpointKey" command="addBreakpointCommand"/> <menuitem id="se-dbg-cMenu-addConditionalBreakpoint" label="&debuggerUI.seMenuCondBreak;" key="addConditionalBreakpointKey" command="addConditionalBreakpointCommand"/> <menuitem id="se-dbg-cMenu-addAsWatch" label="&debuggerUI.seMenuAddWatch;" key="addWatchExpressionKey" command="addWatchExpressionCommand"/> <menuseparator/> - <menuitem id="se-cMenu-copy"/> + <menuitem id="cMenu_copy"/> <menuseparator/> - <menuitem id="se-cMenu-selectAll"/> + <menuitem id="cMenu_selectAll"/> <menuseparator/> <menuitem id="se-dbg-cMenu-findFile" label="&debuggerUI.searchFile;" accesskey="&debuggerUI.searchFile.key;" key="fileSearchKey" command="fileSearchCommand"/> <menuitem id="se-dbg-cMenu-findGlobal" label="&debuggerUI.searchGlobal;"
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-actual-location.js +++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-actual-location.js @@ -30,17 +30,17 @@ function test() { function performTest() { is(gBreakpointsAdded.size, 0, "No breakpoints currently added."); is(gBreakpointsRemoving.size, 0, "No breakpoints currently being removed."); is(gEditor.getBreakpoints().length, 0, "No breakpoints currently shown in the editor."); - gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAdd); + gEditor.on("breakpointAdded", onEditorBreakpointAdd); gPanel.addBreakpoint({ url: gSources.selectedValue, line: 4 }).then(onBreakpointAdd); } let onBpDebuggerAdd = false; let onBpEditorAdd = false; function onBreakpointAdd(aBreakpointClient) { ok(aBreakpointClient, @@ -54,18 +54,18 @@ function test() { "Requested location url is correct"); is(aBreakpointClient.requestedLocation.line, 4, "Requested location line is correct"); onBpDebuggerAdd = true; maybeFinish(); } - function onEditorBreakpointAdd(aEvent) { - gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAdd); + function onEditorBreakpointAdd() { + gEditor.off("breakpointAdded", onEditorBreakpointAdd); is(gEditor.getBreakpoints().length, 1, "There is only one breakpoint in the editor"); ok(!gBreakpoints._getAdded({ url: gSources.selectedValue, line: 4 }), "There isn't any breakpoint added on an invalid line."); ok(!gBreakpoints._getRemoving({ url: gSources.selectedValue, line: 4 }), "There isn't any breakpoint removed from an invalid line.");
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-contextmenu.js +++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-contextmenu.js @@ -7,24 +7,23 @@ const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html"; function test() { // Debug test slaves are a bit slow at this test. requestLongerTimeout(2); let gTab, gDebuggee, gPanel, gDebugger; - let gEditor, gSources, gBreakpoints; + let gSources, gBreakpoints; initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => { gTab = aTab; gDebuggee = aDebuggee; gPanel = aPanel; gDebugger = gPanel.panelWin; - gEditor = gDebugger.DebuggerView.editor; gSources = gDebugger.DebuggerView.Sources; gBreakpoints = gDebugger.DebuggerController.Breakpoints; waitForSourceShown(gPanel, "-01.js") .then(performTestWhileNotPaused) .then(performTestWhilePaused) .then(() => resumeDebuggerThenCloseAndFinish(gPanel)) .then(null, aError => {
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-disabled-reload.js +++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-disabled-reload.js @@ -35,17 +35,17 @@ function test() { yield testWhenBreakpointEnabledAndSecondSourceShown(); yield resumeDebuggerThenCloseAndFinish(aPanel); }); function verifyView({ disabled, visible }) { return Task.spawn(function() { // It takes a tick for the checkbox in the SideMenuWidget and the - // gutter in the SourceEditor to get updated. + // gutter in the editor to get updated. yield waitForTick(); let breakpointItem = gSources.getBreakpoint(gBreakpointLocation); let visibleBreakpoints = gEditor.getBreakpoints(); is(!!breakpointItem.attachment.disabled, disabled, "The selected brekapoint state was correct."); is(breakpointItem.attachment.view.checkbox.hasAttribute("checked"), !disabled, "The selected brekapoint's checkbox state was correct.");
--- a/browser/devtools/debugger/test/browser_dbg_breakpoints-editor.js +++ b/browser/devtools/debugger/test/browser_dbg_breakpoints-editor.js @@ -49,35 +49,30 @@ function test() { ok(!gBreakpoints._getRemoving({ url: "bar", line: 3 }), "_getRemoving('bar', 3) returns falsey."); is(gSources.values[1], gSources.selectedValue, "The second source should be currently selected."); info("Add the first breakpoint."); let location = { url: gSources.selectedValue, line: 6 }; - gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddFirst); + gEditor.once("breakpointAdded", onEditorBreakpointAddFirst); gPanel.addBreakpoint(location).then(onBreakpointAddFirst); } let breakpointsAdded = 0; let breakpointsRemoved = 0; let editorBreakpointChanges = 0; - function onEditorBreakpointAddFirst(aEvent) { - gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddFirst); + function onEditorBreakpointAddFirst(aEvent, aLine) { editorBreakpointChanges++; ok(aEvent, "breakpoint1 added to the editor."); - is(aEvent.added.length, 1, - "One breakpoint added to the editor."); - is(aEvent.removed.length, 0, - "No breakpoint was removed from the editor."); - is(aEvent.added[0].line, 5, + is(aLine, 5, "Editor breakpoint line is correct."); is(gEditor.getBreakpoints().length, 1, "editor.getBreakpoints().length is correct."); } function onBreakpointAddFirst(aBreakpointClient) { breakpointsAdded++; @@ -103,31 +98,26 @@ function test() { is(aClient, aBreakpointClient, "_getAdded() returns the correct breakpoint."); }); is(gSources.values[1], gSources.selectedValue, "The second source should be currently selected."); info("Remove the first breakpoint."); - gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointRemoveFirst); + gEditor.once("breakpointRemoved", onEditorBreakpointRemoveFirst); gPanel.removeBreakpoint(aBreakpointClient.location).then(onBreakpointRemoveFirst); } - function onEditorBreakpointRemoveFirst(aEvent) { - gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointRemoveFirst); + function onEditorBreakpointRemoveFirst(aEvent, aLine) { editorBreakpointChanges++; ok(aEvent, "breakpoint1 removed from the editor."); - is(aEvent.added.length, 0, - "No breakpoint was added to the editor."); - is(aEvent.removed.length, 1, - "One breakpoint was removed from the editor."); - is(aEvent.removed[0].line, 5, + is(aLine, 5, "Editor breakpoint line is correct."); is(gEditor.getBreakpoints().length, 0, "editor.getBreakpoints().length is correct."); } function onBreakpointRemoveFirst(aLocation) { breakpointsRemoved++; @@ -156,26 +146,24 @@ function test() { "_getRemoving('gSources.selectedValue', 6) returns falsey."); is(gSources.values[1], gSources.selectedValue, "The second source should be currently selected."); info("Add a breakpoint to the first source, which is not selected."); let location = { url: gSources.values[0], line: 5 }; let options = { noEditorUpdate: true }; - gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddBackgroundTrap); + gEditor.on("breakpointAdded", onEditorBreakpointAddBackgroundTrap); gPanel.addBreakpoint(location, options).then(onBreakpointAddBackground); } - function onEditorBreakpointAddBackgroundTrap(aEvent) { + function onEditorBreakpointAddBackgroundTrap() { // Trap listener: no breakpoint must be added to the editor when a // breakpoint is added to a source that is not currently selected. - gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddBackgroundTrap); editorBreakpointChanges++; - ok(false, "breakpoint2 must not be added to the editor."); } function onBreakpointAddBackground(aBreakpointClient, aResponseError) { breakpointsAdded++; ok(aBreakpointClient, "breakpoint2 added, client received"); @@ -198,98 +186,81 @@ function test() { is(aClient, aBreakpointClient, "_getAdded() returns the correct breakpoint."); }); is(gSources.values[1], gSources.selectedValue, "The second source should be currently selected."); // Remove the trap listener. - gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddBackgroundTrap); + gEditor.off("breakpointAdded", onEditorBreakpointAddBackgroundTrap); info("Switch to the first source, which is not yet selected"); - gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddSwitch); - gEditor.addEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onEditorTextChanged); + gEditor.once("breakpointAdded", onEditorBreakpointAddSwitch); + gEditor.once("change", onEditorTextChanged); gSources.selectedIndex = 0; } - function onEditorBreakpointAddSwitch(aEvent) { - gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointAddSwitch); + function onEditorBreakpointAddSwitch(aEvent, aLine) { editorBreakpointChanges++; ok(aEvent, "breakpoint2 added to the editor."); - is(aEvent.added.length, 1, - "One breakpoint added to the editor."); - is(aEvent.removed.length, 0, - "No breakpoint was removed from the editor."); - is(aEvent.added[0].line, 4, + is(aLine, 4, "Editor breakpoint line is correct."); is(gEditor.getBreakpoints().length, 1, "editor.getBreakpoints().length is correct"); } function onEditorTextChanged() { // Wait for the actual text to be shown. - if (gEditor.getText() != gDebugger.L10N.getStr("loadingText")) { - onEditorTextReallyChanged(); - } - } - - function onEditorTextReallyChanged() { - gEditor.removeEventListener(SourceEditor.EVENTS.TEXT_CHANGED, onEditorTextChanged); + if (gEditor.getText() == gDebugger.L10N.getStr("loadingText")) + return void gEditor.once("change", onEditorTextChanged); is(gEditor.getText().indexOf("debugger"), -1, "The second source is no longer displayed."); is(gEditor.getText().indexOf("firstCall"), 118, "The first source is displayed."); is(gSources.values[0], gSources.selectedValue, "The first source should be currently selected."); - let window = gEditor.editorElement.contentWindow; + let window = gEditor.container.contentWindow; executeSoon(() => window.mozRequestAnimationFrame(onReadyForClick)); } function onReadyForClick() { info("Remove the second breakpoint using the mouse."); - gEditor.addEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointRemoveSecond); + gEditor.once("breakpointRemoved", onEditorBreakpointRemoveSecond); - let iframe = gEditor.editorElement; + let iframe = gEditor.container; let testWin = iframe.ownerDocument.defaultView; // Flush the layout for the iframe. info("rect " + iframe.contentDocument.documentElement.getBoundingClientRect()); let utils = testWin .QueryInterface(Ci.nsIInterfaceRequestor) .getInterface(Ci.nsIDOMWindowUtils); - let lineOffset = gEditor.getLineStart(4); - let coords = gEditor.getLocationAtOffset(lineOffset); - + let coords = gEditor.cursorCoords({ line: 4, ch: 0 }); let rect = iframe.getBoundingClientRect(); let left = rect.left + 10; - let top = rect.top + coords.y + 4; + let top = rect.top + coords.top + 4; utils.sendMouseEventToWindow("mousedown", left, top, 0, 1, 0, false, 0, 0); utils.sendMouseEventToWindow("mouseup", left, top, 0, 1, 0, false, 0, 0); } - function onEditorBreakpointRemoveSecond(aEvent) { - gEditor.removeEventListener(SourceEditor.EVENTS.BREAKPOINT_CHANGE, onEditorBreakpointRemoveSecond); + function onEditorBreakpointRemoveSecond(aEvent, aLine) { editorBreakpointChanges++; ok(aEvent, "breakpoint2 removed from the editor."); - is(aEvent.added.length, 0, - "No breakpoint was added to the editor."); - is(aEvent.removed.length, 1, - "One breakpoint was removed from the editor."); - is(aEvent.removed[0].line, 4, + is(aLine, 4, "Editor breakpoint line is correct."); is(gEditor.getBreakpoints().length, 0, "editor.getBreakpoints().length is correct."); waitForDebuggerEvents(gPanel, gDebugger.EVENTS.AFTER_FRAMES_CLEARED).then(() => { finalCheck(); closeDebuggerAndFinish(gPanel);
--- a/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-02.js +++ b/browser/devtools/debugger/test/browser_dbg_conditional-breakpoints-02.js @@ -176,17 +176,17 @@ function test() { ok(!selectedBreakpoint, "There should be no selected brekapoint in the sources pane for line " + aLine + "."); ok(isCaretPos(gPanel, aLine), "The editor caret position is not properly set."); } function setCaretPosition(aLine) { - gEditor.setCaretPosition(aLine - 1); + gEditor.setCursor({ line: aLine - 1, ch: 0 }); } function setContextPosition(aLine) { gSources._editorContextMenuLineNumber = aLine - 1; } function clickOnBreakpoint(aIndex) { EventUtils.sendMouseEvent({ type: "click" },
--- a/browser/devtools/debugger/test/browser_dbg_editor-contextmenu.js +++ b/browser/devtools/debugger/test/browser_dbg_editor-contextmenu.js @@ -15,17 +15,17 @@ function test() { gTab = aTab; gDebuggee = aDebuggee; gPanel = aPanel; gDebugger = gPanel.panelWin; gEditor = gDebugger.DebuggerView.editor; gSources = gDebugger.DebuggerView.Sources; gContextMenu = gDebugger.document.getElementById("sourceEditorContextMenu"); - waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest); + waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6).then(performTest).then(null, info); gDebuggee.firstCall(); }); function performTest() { is(gDebugger.gThreadClient.state, "paused", "Should only be getting stack frames while paused."); is(gSources.itemCount, 2, "Found the expected number of sources."); @@ -34,45 +34,30 @@ function test() { is(gSources.selectedValue, gSources.values[1], "The correct source is selected."); is(gEditor.getText().indexOf("\u263a"), 162, "Unicode characters are converted correctly."); ok(gContextMenu, "The source editor's context menupopup is available."); - ok(gEditor.readOnly, + ok(gEditor.isReadOnly(), "The source editor is read only."); gEditor.focus(); - gEditor.setSelection(0, 10); + gEditor.setSelection({ line: 1, ch: 0 }, { line: 1, ch: 10 }); - once(gContextMenu, "popupshown").then(testContextMenu); - gContextMenu.openPopup(gEditor.editorElement, "overlap", 0, 0, true, false); + once(gContextMenu, "popupshown").then(testContextMenu).then(null, info); + gContextMenu.openPopup(gEditor.container, "overlap", 0, 0, true, false); } function testContextMenu() { let document = gDebugger.document; ok(document.getElementById("editMenuCommands"), "#editMenuCommands found."); ok(!document.getElementById("editMenuKeys"), "#editMenuKeys not found."); - ok(document.getElementById("sourceEditorCommands"), - "#sourceEditorCommands found."); - - // Map command ids to their expected disabled state. - let commands = {"se-cmd-undo": true, "se-cmd-redo": true, - "se-cmd-cut": true, "se-cmd-paste": true, - "se-cmd-delete": true, "cmd_findAgain": true, - "cmd_findPrevious": true, "cmd_find": false, - "cmd_gotoLine": false, "cmd_copy": false, - "se-cmd-selectAll": false}; - - for (let id in commands) { - is(document.getElementById(id).hasAttribute("disabled"), commands[id], - "The element with id: " + id + " hasAttribute('disabled')."); - } gContextMenu.hidePopup(); resumeDebuggerThenCloseAndFinish(gPanel); } }
--- a/browser/devtools/debugger/test/browser_dbg_editor-mode.js +++ b/browser/devtools/debugger/test/browser_dbg_editor-mode.js @@ -32,17 +32,17 @@ function test() { gDebuggee.firstCall(); }); } function testInitialSource() { is(gSources.itemCount, 3, "Found the expected number of sources."); - is(gEditor.getMode(), SourceEditor.MODES.TEXT, + is(gEditor.getMode().name, "text", "Found the expected editor mode."); is(gEditor.getText().search(/firstCall/), -1, "The first source is not displayed."); is(gEditor.getText().search(/debugger/), 141, "The second source is displayed."); is(gEditor.getText().search(/banana/), -1, "The third source is not displayed."); @@ -50,17 +50,17 @@ function testInitialSource() { gSources.selectedLabel = "code_script-switching-01.js"; return finished; } function testSwitch1() { is(gSources.itemCount, 3, "Found the expected number of sources."); - is(gEditor.getMode(), SourceEditor.MODES.JAVASCRIPT, + is(gEditor.getMode().name, "javascript", "Found the expected editor mode."); is(gEditor.getText().search(/firstCall/), 118, "The first source is displayed."); is(gEditor.getText().search(/debugger/), -1, "The second source is not displayed."); is(gEditor.getText().search(/banana/), -1, "The third source is not displayed."); @@ -68,17 +68,17 @@ function testSwitch1() { gSources.selectedLabel = "doc_editor-mode.html"; return finished; } function testSwitch2() { is(gSources.itemCount, 3, "Found the expected number of sources."); - is(gEditor.getMode(), SourceEditor.MODES.HTML, + is(gEditor.getMode().name, "htmlmixed", "Found the expected editor mode."); is(gEditor.getText().search(/firstCall/), -1, "The first source is not displayed."); is(gEditor.getText().search(/debugger/), -1, "The second source is not displayed."); is(gEditor.getText().search(/banana/), 443, "The third source is displayed."); }
--- a/browser/devtools/debugger/test/browser_dbg_pretty-print-02.js +++ b/browser/devtools/debugger/test/browser_dbg_pretty-print-02.js @@ -34,17 +34,17 @@ function test() { }); } function selectContextMenuItem() { once(gContextMenu, "popupshown").then(() => { const menuItem = gDebugger.document.getElementById("se-dbg-cMenu-prettyPrint"); menuItem.click(); }); - gContextMenu.openPopup(gEditor.editorElement, "overlap", 0, 0, true, false); + gContextMenu.openPopup(gEditor.container, "overlap", 0, 0, true, false); } function testSourceIsPretty() { ok(gEditor.getText().contains("\n "), "The source should be pretty printed.") } registerCleanupFunction(function() {
--- a/browser/devtools/debugger/test/browser_dbg_pretty-print-05.js +++ b/browser/devtools/debugger/test/browser_dbg_pretty-print-05.js @@ -19,17 +19,17 @@ function test() { gEditor = gDebugger.DebuggerView.editor; gSources = gDebugger.DebuggerView.Sources; gControllerSources = gDebugger.DebuggerController.SourceScripts; Task.spawn(function() { yield waitForSourceShown(gPanel, TAB_URL); // From this point onward, the source editor's text should never change. - once(gEditor, SourceEditor.EVENTS.TEXT_CHANGED).then(() => { + gEditor.once("change", () => { ok(false, "The source editor text shouldn't have changed."); }); is(gSources.selectedValue, TAB_URL, "The correct source is currently selected."); ok(gEditor.getText().contains("myFunction"), "The source shouldn't be pretty printed yet.");
--- a/browser/devtools/debugger/test/browser_dbg_pretty-print-06.js +++ b/browser/devtools/debugger/test/browser_dbg_pretty-print-06.js @@ -35,17 +35,17 @@ function test() { return aOriginalRequestMethod(aPacket, aCallback); }; }(gClient.request)); Task.spawn(function() { yield waitForSourceShown(gPanel, JS_URL); // From this point onward, the source editor's text should never change. - once(gEditor, SourceEditor.EVENTS.TEXT_CHANGED).then(() => { + gEditor.once("change", () => { ok(false, "The source editor text shouldn't have changed."); }); is(gSources.selectedValue, JS_URL, "The correct source is currently selected."); ok(gEditor.getText().contains("myFunction"), "The source shouldn't be pretty printed yet.");
--- a/browser/devtools/debugger/test/browser_dbg_scripts-switching-01.js +++ b/browser/devtools/debugger/test/browser_dbg_scripts-switching-01.js @@ -99,17 +99,17 @@ function testSwitchPaused1() { "The first source is displayed."); is(gEditor.getText().search(/debugger/), -1, "The second source is not displayed."); // The editor's debug location takes a tick to update. executeSoon(() => { ok(isCaretPos(gPanel, 1), "Editor caret location is correct."); - is(gEditor.getDebugLocation(), -1, + is(gEditor.getDebugLocation(), null, "Editor debugger location is correct."); waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(deferred.resolve); gSources.selectedIndex = 1; }); return deferred.promise; }
--- a/browser/devtools/debugger/test/browser_dbg_scripts-switching-02.js +++ b/browser/devtools/debugger/test/browser_dbg_scripts-switching-02.js @@ -94,17 +94,18 @@ function testSwitchPaused1() { "The first source is displayed."); is(gEditor.getText().search(/debugger/), -1, "The second source is not displayed."); // The editor's debug location takes a tick to update. executeSoon(() => { ok(isCaretPos(gPanel, 1), "Editor caret location is correct."); - is(gEditor.getDebugLocation(), -1, + + is(gEditor.getDebugLocation(), null, "Editor debugger location is correct."); waitForDebuggerEvents(gPanel, gDebugger.EVENTS.SOURCE_SHOWN).then(deferred.resolve); gSources.selectedLabel = gLabel2; }); return deferred.promise; }
--- a/browser/devtools/debugger/test/browser_dbg_search-basic-01.js +++ b/browser/devtools/debugger/test/browser_dbg_search-basic-01.js @@ -125,17 +125,16 @@ function performTest() { "The editor didn't jump to the correct token (5)."); EventUtils.sendKey("UP", gDebugger); is(gFiltering.searchData.toSource(), '["#", ["", "debugger"]]', "The searchbox data wasn't parsed correctly."); ok(isCaretPos(gPanel, 26, 11 + token.length), "The editor didn't jump to the correct token (6)."); - setText(gSearchBox, ":bogus#" + token + ";"); is(gFiltering.searchData.toSource(), '["#", [":bogus", "debugger;"]]', "The searchbox data wasn't parsed correctly."); ok(isCaretPos(gPanel, 14, 9 + token.length + 1), "The editor didn't jump to the correct token (7)."); setText(gSearchBox, ":13#" + token + ";"); is(gFiltering.searchData.toSource(), '["#", [":13", "debugger;"]]', @@ -278,28 +277,28 @@ function performTest() { "The searchbox data wasn't parsed correctly."); ok(isCaretPos(gPanel, 26, 11 + token.length), "The editor didn't remain at the correct token (29)."); is(gSources.visibleItems.length, 1, "Not all the sources are shown after the search (30)."); gEditor.focus(); - gEditor.setSelection(1, 5); + gEditor.setSelection.apply(gEditor, gEditor.getPosition(1, 5)); ok(isCaretPos(gPanel, 1, 6), "The editor caret position didn't update after selecting some text."); EventUtils.synthesizeKey("F", { accelKey: true }); is(gFiltering.searchData.toSource(), '["#", ["", "!-- "]]', "The searchbox data wasn't parsed correctly."); is(gSearchBox.value, "#!-- ", "The search field has the right initial value (1)."); gEditor.focus(); - gEditor.setSelection(415, 418); + gEditor.setSelection.apply(gEditor, gEditor.getPosition(415, 418)); ok(isCaretPos(gPanel, 21, 30), "The editor caret position didn't update after selecting some number."); EventUtils.synthesizeKey("L", { accelKey: true }); is(gFiltering.searchData.toSource(), '[":", ["", 100]]', "The searchbox data wasn't parsed correctly."); is(gSearchBox.value, ":100", "The search field has the right initial value (2).");
--- a/browser/devtools/debugger/test/browser_dbg_search-basic-04.js +++ b/browser/devtools/debugger/test/browser_dbg_search-basic-04.js @@ -28,99 +28,99 @@ function test() { .then(null, aError => { ok(false, "Got an error: " + aError.message + "\n" + aError.stack); }); }); } function testLineSearch() { setText(gSearchBox, ":42"); - ok(isCaretPos(gPanel, 1), + ok(isCaretPos(gPanel, 7), "The editor caret position appears to be correct (1.1)."); - ok(isEditorSel(gPanel, [1, 1]), + ok(isEditorSel(gPanel, [160, 160]), "The editor selection appears to be correct (1.1)."); - is(gEditor.getSelectedText(), "", + is(gEditor.getSelection(), "", "The editor selected text appears to be correct (1.1)."); backspaceText(gSearchBox, 1); ok(isCaretPos(gPanel, 4), "The editor caret position appears to be correct (1.2)."); ok(isEditorSel(gPanel, [110, 110]), "The editor selection appears to be correct (1.2)."); - is(gEditor.getSelectedText(), "", + is(gEditor.getSelection(), "", "The editor selected text appears to be correct (1.2)."); backspaceText(gSearchBox, 1); ok(isCaretPos(gPanel, 4), "The editor caret position appears to be correct (1.3)."); ok(isEditorSel(gPanel, [110, 110]), "The editor selection appears to be correct (1.3)."); - is(gEditor.getSelectedText(), "", + is(gEditor.getSelection(), "", "The editor selected text appears to be correct (1.3)."); setText(gSearchBox, ":4"); ok(isCaretPos(gPanel, 4), "The editor caret position appears to be correct (1.4)."); ok(isEditorSel(gPanel, [110, 110]), "The editor selection appears to be correct (1.4)."); - is(gEditor.getSelectedText(), "", + is(gEditor.getSelection(), "", "The editor selected text appears to be correct (1.4)."); gSearchBox.select(); backspaceText(gSearchBox, 1); ok(isCaretPos(gPanel, 4), "The editor caret position appears to be correct (1.5)."); ok(isEditorSel(gPanel, [110, 110]), "The editor selection appears to be correct (1.5)."); - is(gEditor.getSelectedText(), "", + is(gEditor.getSelection(), "", "The editor selected text appears to be correct (1.5)."); is(gSearchBox.value, "", "The searchbox should have been cleared."); } function testTokenSearch() { setText(gSearchBox, "#;\""); ok(isCaretPos(gPanel, 5, 23), "The editor caret position appears to be correct (2.1)."); ok(isEditorSel(gPanel, [153, 155]), "The editor selection appears to be correct (2.1)."); - is(gEditor.getSelectedText(), ";\"", + is(gEditor.getSelection(), ";\"", "The editor selected text appears to be correct (2.1)."); backspaceText(gSearchBox, 1); ok(isCaretPos(gPanel, 5, 22), "The editor caret position appears to be correct (2.2)."); ok(isEditorSel(gPanel, [153, 154]), "The editor selection appears to be correct (2.2)."); - is(gEditor.getSelectedText(), ";", + is(gEditor.getSelection(), ";", "The editor selected text appears to be correct (2.2)."); backspaceText(gSearchBox, 1); ok(isCaretPos(gPanel, 5, 22), "The editor caret position appears to be correct (2.3)."); ok(isEditorSel(gPanel, [154, 154]), "The editor selection appears to be correct (2.3)."); - is(gEditor.getSelectedText(), "", + is(gEditor.getSelection(), "", "The editor selected text appears to be correct (2.3)."); setText(gSearchBox, "#;"); ok(isCaretPos(gPanel, 5, 22), "The editor caret position appears to be correct (2.4)."); ok(isEditorSel(gPanel, [153, 154]), "The editor selection appears to be correct (2.4)."); - is(gEditor.getSelectedText(), ";", + is(gEditor.getSelection(), ";", "The editor selected text appears to be correct (2.4)."); gSearchBox.select(); backspaceText(gSearchBox, 1); ok(isCaretPos(gPanel, 5, 22), "The editor caret position appears to be correct (2.5)."); ok(isEditorSel(gPanel, [154, 154]), "The editor selection appears to be correct (2.5)."); - is(gEditor.getSelectedText(), "", + is(gEditor.getSelection(), "", "The editor selected text appears to be correct (2.5)."); is(gSearchBox.value, "", "The searchbox should have been cleared."); } registerCleanupFunction(function() { gTab = null; gDebuggee = null;
--- a/browser/devtools/debugger/test/browser_dbg_search-global-02.js +++ b/browser/devtools/debugger/test/browser_dbg_search-global-02.js @@ -82,17 +82,17 @@ function doFirstJump() { "The currently shown source is incorrect (1)."); is(gSources.visibleItems.length, 2, "Not all the sources are shown after the global search (1)."); // The editor's selected text takes a tick to update. executeSoon(() => { ok(isCaretPos(gPanel, 5, 7), "The editor didn't jump to the correct line (1)."); - is(gEditor.getSelectedText(), "eval", + is(gEditor.getSelection(), "eval", "The editor didn't select the correct text (1)."); deferred.resolve(); }); }); EventUtils.sendKey("DOWN", gDebugger); @@ -110,17 +110,17 @@ function doSecondJump() { "The currently shown source is incorrect (2)."); is(gSources.visibleItems.length, 2, "Not all the sources are shown after the global search (2)."); // The editor's selected text takes a tick to update. executeSoon(() => { ok(isCaretPos(gPanel, 6, 7), "The editor didn't jump to the correct line (2)."); - is(gEditor.getSelectedText(), "eval", + is(gEditor.getSelection(), "eval", "The editor didn't select the correct text (2)."); deferred.resolve(); }); }); EventUtils.sendKey("DOWN", gDebugger); @@ -138,17 +138,17 @@ function doWrapAroundJump() { "The currently shown source is incorrect (3)."); is(gSources.visibleItems.length, 2, "Not all the sources are shown after the global search (3)."); // The editor's selected text takes a tick to update. executeSoon(() => { ok(isCaretPos(gPanel, 5, 7), "The editor didn't jump to the correct line (3)."); - is(gEditor.getSelectedText(), "eval", + is(gEditor.getSelection(), "eval", "The editor didn't select the correct text (3)."); deferred.resolve(); }); }); EventUtils.sendKey("DOWN", gDebugger); @@ -166,17 +166,17 @@ function doBackwardsWrapAroundJump() { "The currently shown source is incorrect (4)."); is(gSources.visibleItems.length, 2, "Not all the sources are shown after the global search (4)."); // The editor's selected text takes a tick to update. executeSoon(() => { ok(isCaretPos(gPanel, 6, 7), "The editor didn't jump to the correct line (4)."); - is(gEditor.getSelectedText(), "eval", + is(gEditor.getSelection(), "eval", "The editor didn't select the correct text (4)."); deferred.resolve(); }); }); EventUtils.sendKey("UP", gDebugger); @@ -190,17 +190,17 @@ function testSearchTokenEmpty() { info("Debugger editor text:\n" + gEditor.getText()); ok(gSources.selectedValue.contains("-02.js"), "The currently shown source is incorrect (4)."); is(gSources.visibleItems.length, 2, "Not all the sources are shown after the global search (4)."); ok(isCaretPos(gPanel, 6, 7), "The editor didn't remain at the correct line (4)."); - is(gEditor.getSelectedText(), "", + is(gEditor.getSelection(), "", "The editor shouldn't keep the previous text selected (4)."); is(gSearchView.itemCount, 0, "The global search pane shouldn't have any child nodes after clearing."); is(gSearchView.widget._parent.hidden, true, "The global search pane shouldn't be visible after clearing."); is(gSearchView._splitter.hidden, true, "The global search pane splitter shouldn't be visible after clearing.");
--- a/browser/devtools/debugger/test/browser_dbg_search-global-05.js +++ b/browser/devtools/debugger/test/browser_dbg_search-global-05.js @@ -95,17 +95,17 @@ function testClickLineToJump() { let firstLine = sourceResults[0].querySelector(".dbg-results-line-contents"); waitForSourceAndCaret(gPanel, "-01.js", 1, 5).then(() => { info("Current source url:\n" + gSources.selectedValue); info("Debugger editor text:\n" + gEditor.getText()); ok(isCaretPos(gPanel, 1, 5), "The editor didn't jump to the correct line (1)."); - is(gEditor.getSelectedText(), "A", + is(gEditor.getSelection(), "A", "The editor didn't select the correct text (1)."); ok(gSources.selectedValue.contains("-01.js"), "The currently shown source is incorrect (1)."); is(gSources.visibleItems.length, 2, "Not all the sources are shown after the global search (1)."); deferred.resolve(); }); @@ -124,17 +124,17 @@ function testClickMatchToJump() { let lastMatch = Array.slice(secondMatches).pop(); waitForSourceAndCaret(gPanel, "-02.js", 6, 6).then(() => { info("Current source url:\n" + gSources.selectedValue); info("Debugger editor text:\n" + gEditor.getText()); ok(isCaretPos(gPanel, 6, 6), "The editor didn't jump to the correct line (2)."); - is(gEditor.getSelectedText(), "a", + is(gEditor.getSelection(), "a", "The editor didn't select the correct text (2)."); ok(gSources.selectedValue.contains("-02.js"), "The currently shown source is incorrect (2)."); is(gSources.visibleItems.length, 2, "Not all the sources are shown after the global search (2)."); deferred.resolve(); });
--- a/browser/devtools/debugger/test/browser_dbg_search-sources-01.js +++ b/browser/devtools/debugger/test/browser_dbg_search-sources-01.js @@ -3,28 +3,27 @@ /** * Tests basic functionality of sources filtering (file search). */ const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html"; let gTab, gDebuggee, gPanel, gDebugger; -let gEditor, gSources, gSearchView, gSearchBox; +let gSources, gSearchView, gSearchBox; function test() { // Debug test slaves are a bit slow at this test. requestLongerTimeout(3); initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => { gTab = aTab; gDebuggee = aDebuggee; gPanel = aPanel; gDebugger = gPanel.panelWin; - gEditor = gDebugger.DebuggerView.editor; gSources = gDebugger.DebuggerView.Sources; gSearchView = gDebugger.DebuggerView.FilteredSources; gSearchBox = gDebugger.DebuggerView.Filtering._searchbox; waitForSourceShown(gPanel, "-01.js") .then(bogusSearch) .then(firstSearch) .then(secondSearch) @@ -225,13 +224,12 @@ function verifyContents(aArgs) { "No sources should be displayed in the sources container after a bogus search."); } registerCleanupFunction(function() { gTab = null; gDebuggee = null; gPanel = null; gDebugger = null; - gEditor = null; gSources = null; gSearchView = null; gSearchBox = null; });
--- a/browser/devtools/debugger/test/browser_dbg_search-sources-02.js +++ b/browser/devtools/debugger/test/browser_dbg_search-sources-02.js @@ -3,28 +3,27 @@ /** * Tests more complex functionality of sources filtering (file search). */ const TAB_URL = EXAMPLE_URL + "doc_editor-mode.html"; let gTab, gDebuggee, gPanel, gDebugger; -let gEditor, gSources, gSourceUtils, gSearchView, gSearchBox; +let gSources, gSourceUtils, gSearchView, gSearchBox; function test() { // Debug test slaves are a bit slow at this test. requestLongerTimeout(3); initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => { gTab = aTab; gDebuggee = aDebuggee; gPanel = aPanel; gDebugger = gPanel.panelWin; - gEditor = gDebugger.DebuggerView.editor; gSources = gDebugger.DebuggerView.Sources; gSourceUtils = gDebugger.SourceUtils; gSearchView = gDebugger.DebuggerView.FilteredSources; gSearchBox = gDebugger.DebuggerView.Filtering._searchbox; waitForSourceShown(gPanel, "-01.js") .then(firstSearch) .then(secondSearch) @@ -267,14 +266,13 @@ function verifyContents(aMatches) { } } registerCleanupFunction(function() { gTab = null; gDebuggee = null; gPanel = null; gDebugger = null; - gEditor = null; gSources = null; gSourceUtils = null; gSearchView = null; gSearchBox = null; });
--- a/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-01.js +++ b/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-01.js @@ -4,25 +4,24 @@ /** * Make sure that the searchbox popup is displayed when focusing the searchbox, * and hidden when the user starts typing. */ const TAB_URL = EXAMPLE_URL + "doc_script-switching-01.html"; let gTab, gDebuggee, gPanel, gDebugger; -let gEditor, gSearchBox, gSearchBoxPanel; +let gSearchBox, gSearchBoxPanel; function test() { initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => { gTab = aTab; gDebuggee = aDebuggee; gPanel = aPanel; gDebugger = gPanel.panelWin; - gEditor = gDebugger.DebuggerView.editor; gSearchBox = gDebugger.DebuggerView.Filtering._searchbox; gSearchBoxPanel = gDebugger.DebuggerView.Filtering._searchboxHelpPanel; waitForSourceAndCaretAndScopes(gPanel, "-02.js", 6) .then(showPopup) .then(hidePopup) .then(() => resumeDebuggerThenCloseAndFinish(gPanel)) .then(null, aError => { @@ -51,12 +50,11 @@ function hidePopup() { return finished; } registerCleanupFunction(function() { gTab = null; gDebuggee = null; gPanel = null; gDebugger = null; - gEditor = null; gSearchBox = null; gSearchBoxPanel = null; });
--- a/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-02.js +++ b/browser/devtools/debugger/test/browser_dbg_searchbox-help-popup-02.js @@ -39,17 +39,17 @@ function test() { } function tryShowPopup() { setText(gSearchBox, "#call()"); ok(isCaretPos(gPanel, 4, 22), "The editor caret position appears to be correct."); ok(isEditorSel(gPanel, [125, 131]), "The editor selection appears to be correct."); - is(gEditor.getSelectedText(), "Call()", + is(gEditor.getSelection(), "Call()", "The editor selected text appears to be correct."); is(gSearchBoxPanel.state, "closed", "The search box panel shouldn't be visible yet."); EventUtils.sendMouseEvent({ type: "click" }, gSearchBox, gDebugger); } @@ -63,17 +63,17 @@ function focusEditor() { return deferred.promise; } function testFocusLost() { ok(isCaretPos(gPanel, 6, 1), "The editor caret position appears to be correct after gaining focus."); ok(isEditorSel(gPanel, [165, 165]), "The editor selection appears to be correct after gaining focus."); - is(gEditor.getSelectedText(), "", + is(gEditor.getSelection(), "", "The editor selected text appears to be correct after gaining focus."); is(gSearchBoxPanel.state, "closed", "The search box panel should still not be visible."); } registerCleanupFunction(function() { gTab = null;
--- a/browser/devtools/debugger/test/browser_dbg_sources-labels.js +++ b/browser/devtools/debugger/test/browser_dbg_sources-labels.js @@ -80,17 +80,17 @@ function test() { "No item in the sources widget should be selected (2)."); is(gSources.selectedValue, "", "No item in the sources widget should be selected (3)."); for (let { href, leaf } of urls) { let url = href + leaf; let label = gUtils.getSourceLabel(url); let trimmedLabel = gUtils.trimUrlLength(label); - gSources.push([trimmedLabel, url]); + gSources.push([trimmedLabel, url], { attachment: {}}); } info("Source labels:"); info(gSources.labels.toSource()); info("Source locations:"); info(gSources.values.toSource());
--- a/browser/devtools/debugger/test/browser_dbg_stack-05.js +++ b/browser/devtools/debugger/test/browser_dbg_stack-05.js @@ -127,17 +127,17 @@ function testOldestTwoFrames() { function testAfterResume() { let deferred = promise.defer(); gDebugger.once(gDebugger.EVENTS.AFTER_FRAMES_CLEARED, () => { is(gFrames.itemCount, 0, "Should have no frames after resume."); ok(isCaretPos(gPanel, 5), "Editor caret location is correct after resume."); - is(gEditor.getDebugLocation(), -1, + is(gEditor.getDebugLocation(), null, "Editor debug location is correct after resume."); deferred.resolve(); }, true); gDebugger.gThreadClient.resume(); return deferred.promise;
--- a/browser/devtools/debugger/test/browser_dbg_variables-view-filter-05.js +++ b/browser/devtools/debugger/test/browser_dbg_variables-view-filter-05.js @@ -4,28 +4,27 @@ /** * Make sure that the variables view correctly shows/hides nodes when various * keyboard shortcuts are pressed in the debugger's searchbox. */ const TAB_URL = EXAMPLE_URL + "doc_with-frame.html"; let gTab, gDebuggee, gPanel, gDebugger; -let gEditor, gVariables, gSearchBox; +let gVariables, gSearchBox; function test() { // Debug test slaves are a bit slow at this test. requestLongerTimeout(2); initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => { gTab = aTab; gDebuggee = aDebuggee; gPanel = aPanel; gDebugger = gPanel.panelWin; - gEditor = gDebugger.DebuggerView.editor; gVariables = gDebugger.DebuggerView.Variables; gSearchBox = gDebugger.DebuggerView.Filtering._searchbox; // The first 'with' scope should be expanded by default, but the // variables haven't been fetched yet. This is how 'with' scopes work. promise.all([ waitForSourceAndCaret(gPanel, ".html", 22), waitForDebuggerEvents(gPanel, gDebugger.EVENTS.FETCHED_SCOPES), @@ -228,12 +227,11 @@ function prepareVariablesAndProperties() return deferred.promise; } registerCleanupFunction(function() { gTab = null; gDebuggee = null; gPanel = null; gDebugger = null; - gEditor = null; gVariables = null; gSearchBox = null; });
--- a/browser/devtools/debugger/test/browser_dbg_watch-expressions-02.js +++ b/browser/devtools/debugger/test/browser_dbg_watch-expressions-02.js @@ -7,24 +7,23 @@ const TAB_URL = EXAMPLE_URL + "doc_watch-expressions.html"; function test() { // Debug test slaves are a bit slow at this test. requestLongerTimeout(2); let gTab, gDebuggee, gPanel, gDebugger; - let gEditor, gWatch, gVariables; + let gWatch, gVariables; initDebugger(TAB_URL).then(([aTab, aDebuggee, aPanel]) => { gTab = aTab; gDebuggee = aDebuggee; gPanel = aPanel; gDebugger = gPanel.panelWin; - gEditor = gDebugger.DebuggerView.editor; gWatch = gDebugger.DebuggerView.WatchExpressions; gVariables = gDebugger.DebuggerView.Variables; gDebugger.DebuggerView.toggleInstrumentsPane({ visible: true, animated: false }); waitForSourceShown(gPanel, ".html", 1) .then(() => addExpressions()) .then(() => performTest())
--- a/browser/devtools/debugger/test/head.js +++ b/browser/devtools/debugger/test/head.js @@ -15,17 +15,16 @@ Services.prefs.setBoolPref("devtools.deb let { Task } = Cu.import("resource://gre/modules/Task.jsm", {}); let { Promise: promise } = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}); let { gDevTools } = Cu.import("resource:///modules/devtools/gDevTools.jsm", {}); let { devtools } = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}); let { DevToolsUtils } = Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm", {}); let { BrowserDebuggerProcess } = Cu.import("resource:///modules/devtools/DebuggerProcess.jsm", {}); let { DebuggerServer } = Cu.import("resource://gre/modules/devtools/dbg-server.jsm", {}); let { DebuggerClient } = Cu.import("resource://gre/modules/devtools/dbg-client.jsm", {}); -let { SourceEditor } = Cu.import("resource:///modules/devtools/sourceeditor/source-editor.jsm", {}); let { AddonManager } = Cu.import("resource://gre/modules/AddonManager.jsm", {}); let TargetFactory = devtools.TargetFactory; let Toolbox = devtools.Toolbox; const EXAMPLE_URL = "http://example.com/browser/browser/devtools/debugger/test/"; // All tests are asynchronous. waitForExplicitFinish(); @@ -241,19 +240,19 @@ function ensureSourceIs(aPanel, aUrl, aW if (aWaitFlag) { return waitForSourceShown(aPanel, aUrl); } ok(false, "Expected source was not already shown: " + aUrl); return promise.reject(null); } function waitForCaretUpdated(aPanel, aLine, aCol = 1) { - return waitForEditorEvents(aPanel, SourceEditor.EVENTS.SELECTION).then(() => { - let caret = aPanel.panelWin.DebuggerView.editor.getCaretPosition(); - info("Caret updated: " + (caret.line + 1) + ", " + (caret.col + 1)); + return waitForEditorEvents(aPanel, "cursorActivity").then(() => { + let cursor = aPanel.panelWin.DebuggerView.editor.getCursor(); + info("Caret updated: " + (cursor.line + 1) + ", " + (cursor.ch + 1)); if (!isCaretPos(aPanel, aLine, aCol)) { return waitForCaretUpdated(aPanel, aLine, aCol); } else { ok(true, "The correct caret position has been set."); } }); } @@ -267,26 +266,29 @@ function ensureCaretAt(aPanel, aLine, aC return waitForCaretUpdated(aPanel, aLine, aCol); } ok(false, "Expected caret position was not already set: " + aLine + "," + aCol); return promise.reject(null); } function isCaretPos(aPanel, aLine, aCol = 1) { let editor = aPanel.panelWin.DebuggerView.editor; - let caret = editor.getCaretPosition(); + let cursor = editor.getCursor(); // Source editor starts counting line and column numbers from 0. - info("Current editor caret position: " + (caret.line + 1) + ", " + (caret.col + 1)); - return caret.line == (aLine - 1) && caret.col == (aCol - 1); + info("Current editor caret position: " + (cursor.line + 1) + ", " + (cursor.ch + 1)); + return cursor.line == (aLine - 1) && cursor.ch == (aCol - 1); } function isEditorSel(aPanel, [start, end]) { let editor = aPanel.panelWin.DebuggerView.editor; - let range = editor.getSelection(); + let range = { + start: editor.getOffset(editor.getCursor("start")), + end: editor.getOffset(editor.getCursor()) + }; // Source editor starts counting line and column numbers from 0. info("Current editor selection: " + (range.start + 1) + ", " + (range.end + 1)); return range.start == (start - 1) && range.end == (end - 1); } function waitForSourceAndCaret(aPanel, aUrl, aLine, aCol) { return promise.all([ @@ -331,22 +333,22 @@ function waitForDebuggerEvents(aPanel, a function waitForEditorEvents(aPanel, aEventName, aEventRepeat = 1) { info("Waiting for editor event: '" + aEventName + "' to fire: " + aEventRepeat + " time(s)."); let deferred = promise.defer(); let editor = aPanel.panelWin.DebuggerView.editor; let count = 0; - editor.addEventListener(aEventName, function onEvent(...aArgs) { + editor.on(aEventName, function onEvent(...aArgs) { info("Editor event '" + aEventName + "' fired: " + (++count) + " time(s)."); if (count == aEventRepeat) { ok(true, "Enough '" + aEventName + "' editor events have been fired."); - editor.removeEventListener(aEventName, onEvent); + editor.off(aEventName, onEvent); deferred.resolve.apply(deferred, aArgs); } }); return deferred.promise; } function waitForThreadEvents(aPanel, aEventName, aEventRepeat = 1) {
--- a/browser/devtools/jar.mn +++ b/browser/devtools/jar.mn @@ -27,22 +27,27 @@ browser.jar: content/browser/devtools/layoutview/view.css (layoutview/view.css) content/browser/devtools/fontinspector/font-inspector.js (fontinspector/font-inspector.js) content/browser/devtools/fontinspector/font-inspector.xhtml (fontinspector/font-inspector.xhtml) content/browser/devtools/fontinspector/font-inspector.css (fontinspector/font-inspector.css) content/browser/devtools/orion.js (sourceeditor/orion/orion.js) content/browser/devtools/codemirror/codemirror.js (sourceeditor/codemirror/codemirror.js) content/browser/devtools/codemirror/codemirror.css (sourceeditor/codemirror/codemirror.css) content/browser/devtools/codemirror/javascript.js (sourceeditor/codemirror/javascript.js) + content/browser/devtools/codemirror/xml.js (sourceeditor/codemirror/xml.js) + content/browser/devtools/codemirror/css.js (sourceeditor/codemirror/css.js) + content/browser/devtools/codemirror/htmlmixed.js (sourceeditor/codemirror/htmlmixed.js) + content/browser/devtools/codemirror/activeline.js (sourceeditor/codemirror/activeline.js) content/browser/devtools/codemirror/matchbrackets.js (sourceeditor/codemirror/matchbrackets.js) content/browser/devtools/codemirror/comment.js (sourceeditor/codemirror/comment.js) content/browser/devtools/codemirror/searchcursor.js (sourceeditor/codemirror/search/searchcursor.js) content/browser/devtools/codemirror/search.js (sourceeditor/codemirror/search/search.js) content/browser/devtools/codemirror/dialog.js (sourceeditor/codemirror/dialog/dialog.js) content/browser/devtools/codemirror/dialog.css (sourceeditor/codemirror/dialog/dialog.css) + content/browser/devtools/codemirror/mozilla.css (sourceeditor/codemirror/mozilla.css) * content/browser/devtools/source-editor-overlay.xul (sourceeditor/source-editor-overlay.xul) content/browser/devtools/debugger.xul (debugger/debugger.xul) content/browser/devtools/debugger.css (debugger/debugger.css) content/browser/devtools/debugger-controller.js (debugger/debugger-controller.js) content/browser/devtools/debugger-view.js (debugger/debugger-view.js) content/browser/devtools/debugger-toolbar.js (debugger/debugger-toolbar.js) content/browser/devtools/debugger-panes.js (debugger/debugger-panes.js) content/browser/devtools/profiler.xul (profiler/profiler.xul)
--- a/browser/devtools/profiler/test/browser_profiler_bug_834878_source_buttons.js +++ b/browser/devtools/profiler/test/browser_profiler_bug_834878_source_buttons.js @@ -12,23 +12,22 @@ function test() { let data = { uri: SCRIPT, line: 5, isChrome: false }; panel.displaySource(data).then(function onOpen() { let target = TargetFactory.forTab(tab); let dbg = gDevTools.getToolbox(target).getPanel("jsdebugger"); let view = dbg.panelWin.DebuggerView; is(view.Sources.selectedValue, data.uri, "URI is different"); - is(view.editor.getCaretPosition().line, data.line - 1, - "Line is different"); + is(view.editor.getCursor().line, data.line - 1, "Line is different"); // Test the case where script is already loaded. - view.editor.setCaretPosition(1); + view.editor.setCursor({ line: 1, ch: 1 }); gDevTools.showToolbox(target, "jsprofiler").then(function () { panel.displaySource(data).then(function onOpenAgain() { - is(view.editor.getCaretPosition().line, data.line - 1, + is(view.editor.getCursor().line, data.line - 1, "Line is different"); tearDown(tab); }); }); }); }); }
--- a/browser/devtools/scratchpad/test/browser_scratchpad_bug684546_reset_undo.js +++ b/browser/devtools/scratchpad/test/browser_scratchpad_bug684546_reset_undo.js @@ -97,17 +97,17 @@ function fileAImported(aStatus, aFileCon ok(Components.isSuccessCode(aStatus), "the temporary file A was imported successfully with Scratchpad"); is(aFileContent, gFileAContent, "received data is correct"); is(gScratchpad.getText(), gFileAContent, "the editor content is correct"); gScratchpad.editor.replaceText("new text", - gScratchpad.editor.posFromIndex(gScratchpad.getText().length)); + gScratchpad.editor.getPosition(gScratchpad.getText().length)); is(gScratchpad.getText(), gFileAContent + "new text", "text updated correctly"); gScratchpad.undo(); is(gScratchpad.getText(), gFileAContent, "undo works"); gScratchpad.redo(); is(gScratchpad.getText(), gFileAContent + "new text", "redo works"); // Import the file B into Scratchpad. @@ -126,17 +126,17 @@ function fileBImported(aStatus, aFileCon ok(!gScratchpad.editor.canUndo(), "editor cannot undo after load"); gScratchpad.undo(); is(gScratchpad.getText(), gFileBContent, "the editor content is still correct after undo"); gScratchpad.editor.replaceText("new text", - gScratchpad.editor.posFromIndex(gScratchpad.getText().length)); + gScratchpad.editor.getPosition(gScratchpad.getText().length)); is(gScratchpad.getText(), gFileBContent + "new text", "text updated correctly"); gScratchpad.undo(); is(gScratchpad.getText(), gFileBContent, "undo works"); ok(!gScratchpad.editor.canUndo(), "editor cannot undo after load (again)"); gScratchpad.redo(); is(gScratchpad.getText(), gFileBContent + "new text", "redo works");
new file mode 100644 --- /dev/null +++ b/browser/devtools/sourceeditor/codemirror/activeline.js @@ -0,0 +1,40 @@ +/* vim:set ts=2 sw=2 sts=2 et tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +(function () { + "use strict"; + + const WRAP_CLASS = "CodeMirror-activeline"; + const BACK_CLASS = "CodeMirror-activeline-background"; + + CodeMirror.defineOption("styleActiveLine", false, function(cm, val, old) { + var prev = old && old != CodeMirror.Init; + + if (val && !prev) { + updateActiveLine(cm); + cm.on("cursorActivity", updateActiveLine); + } else if (!val && prev) { + cm.off("cursorActivity", updateActiveLine); + clearActiveLine(cm); + delete cm.state.activeLine; + } + }); + + function clearActiveLine(cm) { + if ("activeLine" in cm.state) { + cm.removeLineClass(cm.state.activeLine, "wrap", WRAP_CLASS); + cm.removeLineClass(cm.state.activeLine, "background", BACK_CLASS); + } + } + + function updateActiveLine(cm) { + var line = cm.getLineHandleVisualStart(cm.getCursor().line); + if (cm.state.activeLine == line) return; + clearActiveLine(cm); + cm.addLineClass(line, "wrap", WRAP_CLASS); + cm.addLineClass(line, "background", BACK_CLASS); + cm.state.activeLine = line; + } +})(); \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/browser/devtools/sourceeditor/codemirror/css.js @@ -0,0 +1,623 @@ +CodeMirror.defineMode("css", function(config) { + return CodeMirror.getMode(config, "text/css"); +}); + +CodeMirror.defineMode("css-base", function(config, parserConfig) { + "use strict"; + + var indentUnit = config.indentUnit, + hooks = parserConfig.hooks || {}, + atMediaTypes = parserConfig.atMediaTypes || {}, + atMediaFeatures = parserConfig.atMediaFeatures || {}, + propertyKeywords = parserConfig.propertyKeywords || {}, + colorKeywords = parserConfig.colorKeywords || {}, + valueKeywords = parserConfig.valueKeywords || {}, + allowNested = !!parserConfig.allowNested, + type = null; + + function ret(style, tp) { type = tp; return style; } + + function tokenBase(stream, state) { + var ch = stream.next(); + if (hooks[ch]) { + // result[0] is style and result[1] is type + var result = hooks[ch](stream, state); + if (result !== false) return result; + } + if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("def", stream.current());} + else if (ch == "=") ret(null, "compare"); + else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare"); + else if (ch == "\"" || ch == "'") { + state.tokenize = tokenString(ch); + return state.tokenize(stream, state); + } + else if (ch == "#") { + stream.eatWhile(/[\w\\\-]/); + return ret("atom", "hash"); + } + else if (ch == "!") { + stream.match(/^\s*\w*/); + return ret("keyword", "important"); + } + else if (/\d/.test(ch)) { + stream.eatWhile(/[\w.%]/); + return ret("number", "unit"); + } + else if (ch === "-") { + if (/\d/.test(stream.peek())) { + stream.eatWhile(/[\w.%]/); + return ret("number", "unit"); + } else if (stream.match(/^[^-]+-/)) { + return ret("meta", "meta"); + } + } + else if (/[,+>*\/]/.test(ch)) { + return ret(null, "select-op"); + } + else if (ch == "." && stream.match(/^-?[_a-z][_a-z0-9-]*/i)) { + return ret("qualifier", "qualifier"); + } + else if (ch == ":") { + return ret("operator", ch); + } + else if (/[;{}\[\]\(\)]/.test(ch)) { + return ret(null, ch); + } + else if (ch == "u" && stream.match("rl(")) { + stream.backUp(1); + state.tokenize = tokenParenthesized; + return ret("property", "variable"); + } + else { + stream.eatWhile(/[\w\\\-]/); + return ret("property", "variable"); + } + } + + function tokenString(quote, nonInclusive) { + return function(stream, state) { + var escaped = false, ch; + while ((ch = stream.next()) != null) { + if (ch == quote && !escaped) + break; + escaped = !escaped && ch == "\\"; + } + if (!escaped) { + if (nonInclusive) stream.backUp(1); + state.tokenize = tokenBase; + } + return ret("string", "string"); + }; + } + + function tokenParenthesized(stream, state) { + stream.next(); // Must be '(' + if (!stream.match(/\s*[\"\']/, false)) + state.tokenize = tokenString(")", true); + else + state.tokenize = tokenBase; + return ret(null, "("); + } + + return { + startState: function(base) { + return {tokenize: tokenBase, + baseIndent: base || 0, + stack: [], + lastToken: null}; + }, + + token: function(stream, state) { + + // Use these terms when applicable (see http://www.xanthir.com/blog/b4E50) + // + // rule** or **ruleset: + // A selector + braces combo, or an at-rule. + // + // declaration block: + // A sequence of declarations. + // + // declaration: + // A property + colon + value combo. + // + // property value: + // The entire value of a property. + // + // component value: + // A single piece of a property value. Like the 5px in + // text-shadow: 0 0 5px blue;. Can also refer to things that are + // multiple terms, like the 1-4 terms that make up the background-size + // portion of the background shorthand. + // + // term: + // The basic unit of author-facing CSS, like a single number (5), + // dimension (5px), string ("foo"), or function. Officially defined + // by the CSS 2.1 grammar (look for the 'term' production) + // + // + // simple selector: + // A single atomic selector, like a type selector, an attr selector, a + // class selector, etc. + // + // compound selector: + // One or more simple selectors without a combinator. div.example is + // compound, div > .example is not. + // + // complex selector: + // One or more compound selectors chained with combinators. + // + // combinator: + // The parts of selectors that express relationships. There are four + // currently - the space (descendant combinator), the greater-than + // bracket (child combinator), the plus sign (next sibling combinator), + // and the tilda (following sibling combinator). + // + // sequence of selectors: + // One or more of the named type of selector chained with commas. + + state.tokenize = state.tokenize || tokenBase; + if (state.tokenize == tokenBase && stream.eatSpace()) return null; + var style = state.tokenize(stream, state); + if (style && typeof style != "string") style = ret(style[0], style[1]); + + // Changing style returned based on context + var context = state.stack[state.stack.length-1]; + if (style == "variable") { + if (type == "variable-definition") state.stack.push("propertyValue"); + return state.lastToken = "variable-2"; + } else if (style == "property") { + var word = stream.current().toLowerCase(); + if (context == "propertyValue") { + if (valueKeywords.hasOwnProperty(word)) { + style = "string-2"; + } else if (colorKeywords.hasOwnProperty(word)) { + style = "keyword"; + } else { + style = "variable-2"; + } + } else if (context == "rule") { + if (!propertyKeywords.hasOwnProperty(word)) { + style += " error"; + } + } else if (context == "block") { + // if a value is present in both property, value, or color, the order + // of preference is property -> color -> value + if (propertyKeywords.hasOwnProperty(word)) { + style = "property"; + } else if (colorKeywords.hasOwnProperty(word)) { + style = "keyword"; + } else if (valueKeywords.hasOwnProperty(word)) { + style = "string-2"; + } else { + style = "tag"; + } + } else if (!context || context == "@media{") { + style = "tag"; + } else if (context == "@media") { + if (atMediaTypes[stream.current()]) { + style = "attribute"; // Known attribute + } else if (/^(only|not)$/.test(word)) { + style = "keyword"; + } else if (word == "and") { + style = "error"; // "and" is only allowed in @mediaType + } else if (atMediaFeatures.hasOwnProperty(word)) { + style = "error"; // Known property, should be in @mediaType( + } else { + // Unknown, expecting keyword or attribute, assuming attribute + style = "attribute error"; + } + } else if (context == "@mediaType") { + if (atMediaTypes.hasOwnProperty(word)) { + style = "attribute"; + } else if (word == "and") { + style = "operator"; + } else if (/^(only|not)$/.test(word)) { + style = "error"; // Only allowed in @media + } else { + // Unknown attribute or property, but expecting property (preceded + // by "and"). Should be in parentheses + style = "error"; + } + } else if (context == "@mediaType(") { + if (propertyKeywords.hasOwnProperty(word)) { + // do nothing, remains "property" + } else if (atMediaTypes.hasOwnProperty(word)) { + style = "error"; // Known property, should be in parentheses + } else if (word == "and") { + style = "operator"; + } else if (/^(only|not)$/.test(word)) { + style = "error"; // Only allowed in @media + } else { + style += " error"; + } + } else if (context == "@import") { + style = "tag"; + } else { + style = "error"; + } + } else if (style == "atom") { + if(!context || context == "@media{" || context == "block") { + style = "builtin"; + } else if (context == "propertyValue") { + if (!/^#([0-9a-fA-f]{3}|[0-9a-fA-f]{6})$/.test(stream.current())) { + style += " error"; + } + } else { + style = "error"; + } + } else if (context == "@media" && type == "{") { + style = "error"; + } + + // Push/pop context stack + if (type == "{") { + if (context == "@media" || context == "@mediaType") { + state.stack[state.stack.length-1] = "@media{"; + } + else { + var newContext = allowNested ? "block" : "rule"; + state.stack.push(newContext); + } + } + else if (type == "}") { + if (context == "interpolation") style = "operator"; + state.stack.pop(); + if (context == "propertyValue") state.stack.pop(); + } + else if (type == "interpolation") state.stack.push("interpolation"); + else if (type == "@media") state.stack.push("@media"); + else if (type == "@import") state.stack.push("@import"); + else if (context == "@media" && /\b(keyword|attribute)\b/.test(style)) + state.stack[state.stack.length-1] = "@mediaType"; + else if (context == "@mediaType" && stream.current() == ",") + state.stack[state.stack.length-1] = "@media"; + else if (type == "(") { + if (context == "@media" || context == "@mediaType") { + // Make sure @mediaType is used to avoid error on { + state.stack[state.stack.length-1] = "@mediaType"; + state.stack.push("@mediaType("); + } + } + else if (type == ")") { + if (context == "propertyValue" && state.stack[state.stack.length-2] == "@mediaType(") { + // In @mediaType( without closing ; after propertyValue + state.stack.pop(); + state.stack.pop(); + } + else if (context == "@mediaType(") { + state.stack.pop(); + } + } + else if (type == ":" && state.lastToken == "property") state.stack.push("propertyValue"); + else if (context == "propertyValue" && type == ";") state.stack.pop(); + else if (context == "@import" && type == ";") state.stack.pop(); + + return state.lastToken = style; + }, + + indent: function(state, textAfter) { + var n = state.stack.length; + if (/^\}/.test(textAfter)) + n -= state.stack[n-1] == "propertyValue" ? 2 : 1; + return state.baseIndent + n * indentUnit; + }, + + electricChars: "}", + blockCommentStart: "/*", + blockCommentEnd: "*/", + fold: "brace" + }; +}); + +(function() { + function keySet(array) { + var keys = {}; + for (var i = 0; i < array.length; ++i) { + keys[array[i]] = true; + } + return keys; + } + + var atMediaTypes = keySet([ + "all", "aural", "braille", "handheld", "print", "projection", "screen", + "tty", "tv", "embossed" + ]); + + var atMediaFeatures = keySet([ + "width", "min-width", "max-width", "height", "min-height", "max-height", + "device-width", "min-device-width", "max-device-width", "device-height", + "min-device-height", "max-device-height", "aspect-ratio", + "min-aspect-ratio", "max-aspect-ratio", "device-aspect-ratio", + "min-device-aspect-ratio", "max-device-aspect-ratio", "color", "min-color", + "max-color", "color-index", "min-color-index", "max-color-index", + "monochrome", "min-monochrome", "max-monochrome", "resolution", + "min-resolution", "max-resolution", "scan", "grid" + ]); + + var propertyKeywords = keySet([ + "align-content", "align-items", "align-self", "alignment-adjust", + "alignment-baseline", "anchor-point", "animation", "animation-delay", + "animation-direction", "animation-duration", "animation-iteration-count", + "animation-name", "animation-play-state", "animation-timing-function", + "appearance", "azimuth", "backface-visibility", "background", + "background-attachment", "background-clip", "background-color", + "background-image", "background-origin", "background-position", + "background-repeat", "background-size", "baseline-shift", "binding", + "bleed", "bookmark-label", "bookmark-level", "bookmark-state", + "bookmark-target", "border", "border-bottom", "border-bottom-color", + "border-bottom-left-radius", "border-bottom-right-radius", + "border-bottom-style", "border-bottom-width", "border-collapse", + "border-color", "border-image", "border-image-outset", + "border-image-repeat", "border-image-slice", "border-image-source", + "border-image-width", "border-left", "border-left-color", + "border-left-style", "border-left-width", "border-radius", "border-right", + "border-right-color", "border-right-style", "border-right-width", + "border-spacing", "border-style", "border-top", "border-top-color", + "border-top-left-radius", "border-top-right-radius", "border-top-style", + "border-top-width", "border-width", "bottom", "box-decoration-break", + "box-shadow", "box-sizing", "break-after", "break-before", "break-inside", + "caption-side", "clear", "clip", "color", "color-profile", "column-count", + "column-fill", "column-gap", "column-rule", "column-rule-color", + "column-rule-style", "column-rule-width", "column-span", "column-width", + "columns", "content", "counter-increment", "counter-reset", "crop", "cue", + "cue-after", "cue-before", "cursor", "direction", "display", + "dominant-baseline", "drop-initial-after-adjust", + "drop-initial-after-align", "drop-initial-before-adjust", + "drop-initial-before-align", "drop-initial-size", "drop-initial-value", + "elevation", "empty-cells", "fit", "fit-position", "flex", "flex-basis", + "flex-direction", "flex-flow", "flex-grow", "flex-shrink", "flex-wrap", + "float", "float-offset", "font", "font-feature-settings", "font-family", + "font-kerning", "font-language-override", "font-size", "font-size-adjust", + "font-stretch", "font-style", "font-synthesis", "font-variant", + "font-variant-alternates", "font-variant-caps", "font-variant-east-asian", + "font-variant-ligatures", "font-variant-numeric", "font-variant-position", + "font-weight", "grid-cell", "grid-column", "grid-column-align", + "grid-column-sizing", "grid-column-span", "grid-columns", "grid-flow", + "grid-row", "grid-row-align", "grid-row-sizing", "grid-row-span", + "grid-rows", "grid-template", "hanging-punctuation", "height", "hyphens", + "icon", "image-orientation", "image-rendering", "image-resolution", + "inline-box-align", "justify-content", "left", "letter-spacing", + "line-break", "line-height", "line-stacking", "line-stacking-ruby", + "line-stacking-shift", "line-stacking-strategy", "list-style", + "list-style-image", "list-style-position", "list-style-type", "margin", + "margin-bottom", "margin-left", "margin-right", "margin-top", + "marker-offset", "marks", "marquee-direction", "marquee-loop", + "marquee-play-count", "marquee-speed", "marquee-style", "max-height", + "max-width", "min-height", "min-width", "move-to", "nav-down", "nav-index", + "nav-left", "nav-right", "nav-up", "opacity", "order", "orphans", "outline", + "outline-color", "outline-offset", "outline-style", "outline-width", + "overflow", "overflow-style", "overflow-wrap", "overflow-x", "overflow-y", + "padding", "padding-bottom", "padding-left", "padding-right", "padding-top", + "page", "page-break-after", "page-break-before", "page-break-inside", + "page-policy", "pause", "pause-after", "pause-before", "perspective", + "perspective-origin", "pitch", "pitch-range", "play-during", "position", + "presentation-level", "punctuation-trim", "quotes", "rendering-intent", + "resize", "rest", "rest-after", "rest-before", "richness", "right", + "rotation", "rotation-point", "ruby-align", "ruby-overhang", + "ruby-position", "ruby-span", "size", "speak", "speak-as", "speak-header", + "speak-numeral", "speak-punctuation", "speech-rate", "stress", "string-set", + "tab-size", "table-layout", "target", "target-name", "target-new", + "target-position", "text-align", "text-align-last", "text-decoration", + "text-decoration-color", "text-decoration-line", "text-decoration-skip", + "text-decoration-style", "text-emphasis", "text-emphasis-color", + "text-emphasis-position", "text-emphasis-style", "text-height", + "text-indent", "text-justify", "text-outline", "text-overflow", "text-shadow", + "text-size-adjust", "text-space-collapse", "text-transform", "text-underline-position", + "text-wrap", "top", "transform", "transform-origin", "transform-style", + "transition", "transition-delay", "transition-duration", + "transition-property", "transition-timing-function", "unicode-bidi", + "vertical-align", "visibility", "voice-balance", "voice-duration", + "voice-family", "voice-pitch", "voice-range", "voice-rate", "voice-stress", + "voice-volume", "volume", "white-space", "widows", "width", "word-break", + "word-spacing", "word-wrap", "z-index", "zoom", + // SVG-specific + "clip-path", "clip-rule", "mask", "enable-background", "filter", "flood-color", + "flood-opacity", "lighting-color", "stop-color", "stop-opacity", "pointer-events", + "color-interpolation", "color-interpolation-filters", "color-profile", + "color-rendering", "fill", "fill-opacity", "fill-rule", "image-rendering", + "marker", "marker-end", "marker-mid", "marker-start", "shape-rendering", "stroke", + "stroke-dasharray", "stroke-dashoffset", "stroke-linecap", "stroke-linejoin", + "stroke-miterlimit", "stroke-opacity", "stroke-width", "text-rendering", + "baseline-shift", "dominant-baseline", "glyph-orientation-horizontal", + "glyph-orientation-vertical", "kerning", "text-anchor", "writing-mode" + ]); + + var colorKeywords = keySet([ + "aliceblue", "antiquewhite", "aqua", "aquamarine", "azure", "beige", + "bisque", "black", "blanchedalmond", "blue", "blueviolet", "brown", + "burlywood", "cadetblue", "chartreuse", "chocolate", "coral", "cornflowerblue", + "cornsilk", "crimson", "cyan", "darkblue", "darkcyan", "darkgoldenrod", + "darkgray", "darkgreen", "darkkhaki", "darkmagenta", "darkolivegreen", + "darkorange", "darkorchid", "darkred", "darksalmon", "darkseagreen", + "darkslateblue", "darkslategray", "darkturquoise", "darkviolet", + "deeppink", "deepskyblue", "dimgray", "dodgerblue", "firebrick", + "floralwhite", "forestgreen", "fuchsia", "gainsboro", "ghostwhite", + "gold", "goldenrod", "gray", "green", "greenyellow", "honeydew", + "hotpink", "indianred", "indigo", "ivory", "khaki", "lavender", + "lavenderblush", "lawngreen", "lemonchiffon", "lightblue", "lightcoral", + "lightcyan", "lightgoldenrodyellow", "lightgray", "lightgreen", "lightpink", + "lightsalmon", "lightseagreen", "lightskyblue", "lightslategray", + "lightsteelblue", "lightyellow", "lime", "limegreen", "linen", "magenta", + "maroon", "mediumaquamarine", "mediumblue", "mediumorchid", "mediumpurple", + "mediumseagreen", "mediumslateblue", "mediumspringgreen", "mediumturquoise", + "mediumvioletred", "midnightblue", "mintcream", "mistyrose", "moccasin", + "navajowhite", "navy", "oldlace", "olive", "olivedrab", "orange", "orangered", + "orchid", "palegoldenrod", "palegreen", "paleturquoise", "palevioletred", + "papayawhip", "peachpuff", "peru", "pink", "plum", "powderblue", + "purple", "red", "rosybrown", "royalblue", "saddlebrown", "salmon", + "sandybrown", "seagreen", "seashell", "sienna", "silver", "skyblue", + "slateblue", "slategray", "snow", "springgreen", "steelblue", "tan", + "teal", "thistle", "tomato", "turquoise", "violet", "wheat", "white", + "whitesmoke", "yellow", "yellowgreen" + ]); + + var valueKeywords = keySet([ + "above", "absolute", "activeborder", "activecaption", "afar", + "after-white-space", "ahead", "alias", "all", "all-scroll", "alternate", + "always", "amharic", "amharic-abegede", "antialiased", "appworkspace", + "arabic-indic", "armenian", "asterisks", "auto", "avoid", "background", + "backwards", "baseline", "below", "bidi-override", "binary", "bengali", + "blink", "block", "block-axis", "bold", "bolder", "border", "border-box", + "both", "bottom", "break-all", "break-word", "button", "button-bevel", + "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "cambodian", + "capitalize", "caps-lock-indicator", "caption", "captiontext", "caret", + "cell", "center", "checkbox", "circle", "cjk-earthly-branch", + "cjk-heavenly-stem", "cjk-ideographic", "clear", "clip", "close-quote", + "col-resize", "collapse", "compact", "condensed", "contain", "content", + "content-box", "context-menu", "continuous", "copy", "cover", "crop", + "cross", "crosshair", "currentcolor", "cursive", "dashed", "decimal", + "decimal-leading-zero", "default", "default-button", "destination-atop", + "destination-in", "destination-out", "destination-over", "devanagari", + "disc", "discard", "document", "dot-dash", "dot-dot-dash", "dotted", + "double", "down", "e-resize", "ease", "ease-in", "ease-in-out", "ease-out", + "element", "ellipsis", "embed", "end", "ethiopic", "ethiopic-abegede", + "ethiopic-abegede-am-et", "ethiopic-abegede-gez", "ethiopic-abegede-ti-er", + "ethiopic-abegede-ti-et", "ethiopic-halehame-aa-er", + "ethiopic-halehame-aa-et", "ethiopic-halehame-am-et", + "ethiopic-halehame-gez", "ethiopic-halehame-om-et", + "ethiopic-halehame-sid-et", "ethiopic-halehame-so-et", + "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", + "ethiopic-halehame-tig", "ew-resize", "expanded", "extra-condensed", + "extra-expanded", "fantasy", "fast", "fill", "fixed", "flat", "footnotes", + "forwards", "from", "geometricPrecision", "georgian", "graytext", "groove", + "gujarati", "gurmukhi", "hand", "hangul", "hangul-consonant", "hebrew", + "help", "hidden", "hide", "higher", "highlight", "highlighttext", + "hiragana", "hiragana-iroha", "horizontal", "hsl", "hsla", "icon", "ignore", + "inactiveborder", "inactivecaption", "inactivecaptiontext", "infinite", + "infobackground", "infotext", "inherit", "initial", "inline", "inline-axis", + "inline-block", "inline-table", "inset", "inside", "intrinsic", "invert", + "italic", "justify", "kannada", "katakana", "katakana-iroha", "khmer", + "landscape", "lao", "large", "larger", "left", "level", "lighter", + "line-through", "linear", "lines", "list-item", "listbox", "listitem", + "local", "logical", "loud", "lower", "lower-alpha", "lower-armenian", + "lower-greek", "lower-hexadecimal", "lower-latin", "lower-norwegian", + "lower-roman", "lowercase", "ltr", "malayalam", "match", + "media-controls-background", "media-current-time-display", + "media-fullscreen-button", "media-mute-button", "media-play-button", + "media-return-to-realtime-button", "media-rewind-button", + "media-seek-back-button", "media-seek-forward-button", "media-slider", + "media-sliderthumb", "media-time-remaining-display", "media-volume-slider", + "media-volume-slider-container", "media-volume-sliderthumb", "medium", + "menu", "menulist", "menulist-button", "menulist-text", + "menulist-textfield", "menutext", "message-box", "middle", "min-intrinsic", + "mix", "mongolian", "monospace", "move", "multiple", "myanmar", "n-resize", + "narrower", "ne-resize", "nesw-resize", "no-close-quote", "no-drop", + "no-open-quote", "no-repeat", "none", "normal", "not-allowed", "nowrap", + "ns-resize", "nw-resize", "nwse-resize", "oblique", "octal", "open-quote", + "optimizeLegibility", "optimizeSpeed", "oriya", "oromo", "outset", + "outside", "overlay", "overline", "padding", "padding-box", "painted", + "paused", "persian", "plus-darker", "plus-lighter", "pointer", "portrait", + "pre", "pre-line", "pre-wrap", "preserve-3d", "progress", "push-button", + "radio", "read-only", "read-write", "read-write-plaintext-only", "relative", + "repeat", "repeat-x", "repeat-y", "reset", "reverse", "rgb", "rgba", + "ridge", "right", "round", "row-resize", "rtl", "run-in", "running", + "s-resize", "sans-serif", "scroll", "scrollbar", "se-resize", "searchfield", + "searchfield-cancel-button", "searchfield-decoration", + "searchfield-results-button", "searchfield-results-decoration", + "semi-condensed", "semi-expanded", "separate", "serif", "show", "sidama", + "single", "skip-white-space", "slide", "slider-horizontal", + "slider-vertical", "sliderthumb-horizontal", "sliderthumb-vertical", "slow", + "small", "small-caps", "small-caption", "smaller", "solid", "somali", + "source-atop", "source-in", "source-out", "source-over", "space", "square", + "square-button", "start", "static", "status-bar", "stretch", "stroke", + "sub", "subpixel-antialiased", "super", "sw-resize", "table", + "table-caption", "table-cell", "table-column", "table-column-group", + "table-footer-group", "table-header-group", "table-row", "table-row-group", + "telugu", "text", "text-bottom", "text-top", "textarea", "textfield", "thai", + "thick", "thin", "threeddarkshadow", "threedface", "threedhighlight", + "threedlightshadow", "threedshadow", "tibetan", "tigre", "tigrinya-er", + "tigrinya-er-abegede", "tigrinya-et", "tigrinya-et-abegede", "to", "top", + "transparent", "ultra-condensed", "ultra-expanded", "underline", "up", + "upper-alpha", "upper-armenian", "upper-greek", "upper-hexadecimal", + "upper-latin", "upper-norwegian", "upper-roman", "uppercase", "urdu", "url", + "vertical", "vertical-text", "visible", "visibleFill", "visiblePainted", + "visibleStroke", "visual", "w-resize", "wait", "wave", "wider", + "window", "windowframe", "windowtext", "x-large", "x-small", "xor", + "xx-large", "xx-small" + ]); + + function tokenCComment(stream, state) { + var maybeEnd = false, ch; + while ((ch = stream.next()) != null) { + if (maybeEnd && ch == "/") { + state.tokenize = null; + break; + } + maybeEnd = (ch == "*"); + } + return ["comment", "comment"]; + } + + CodeMirror.defineMIME("text/css", { + atMediaTypes: atMediaTypes, + atMediaFeatures: atMediaFeatures, + propertyKeywords: propertyKeywords, + colorKeywords: colorKeywords, + valueKeywords: valueKeywords, + hooks: { + "<": function(stream, state) { + function tokenSGMLComment(stream, state) { + var dashes = 0, ch; + while ((ch = stream.next()) != null) { + if (dashes >= 2 && ch == ">") { + state.tokenize = null; + break; + } + dashes = (ch == "-") ? dashes + 1 : 0; + } + return ["comment", "comment"]; + } + if (stream.eat("!")) { + state.tokenize = tokenSGMLComment; + return tokenSGMLComment(stream, state); + } + }, + "/": function(stream, state) { + if (stream.eat("*")) { + state.tokenize = tokenCComment; + return tokenCComment(stream, state); + } + return false; + } + }, + name: "css-base" + }); + + CodeMirror.defineMIME("text/x-scss", { + atMediaTypes: atMediaTypes, + atMediaFeatures: atMediaFeatures, + propertyKeywords: propertyKeywords, + colorKeywords: colorKeywords, + valueKeywords: valueKeywords, + allowNested: true, + hooks: { + "$": function(stream) { + stream.match(/^[\w-]+/); + if (stream.peek() == ":") { + return ["variable", "variable-definition"]; + } + return ["variable", "variable"]; + }, + "/": function(stream, state) { + if (stream.eat("/")) { + stream.skipToEnd(); + return ["comment", "comment"]; + } else if (stream.eat("*")) { + state.tokenize = tokenCComment; + return tokenCComment(stream, state); + } else { + return ["operator", "operator"]; + } + }, + "#": function(stream) { + if (stream.eat("{")) { + return ["operator", "interpolation"]; + } else { + stream.eatWhile(/[\w\\\-]/); + return ["atom", "hash"]; + } + } + }, + name: "css-base" + }); +})();
new file mode 100644 --- /dev/null +++ b/browser/devtools/sourceeditor/codemirror/htmlmixed.js @@ -0,0 +1,104 @@ +CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { + var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true}); + var cssMode = CodeMirror.getMode(config, "css"); + + var scriptTypes = [], scriptTypesConf = parserConfig && parserConfig.scriptTypes; + scriptTypes.push({matches: /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, + mode: CodeMirror.getMode(config, "javascript")}); + if (scriptTypesConf) for (var i = 0; i < scriptTypesConf.length; ++i) { + var conf = scriptTypesConf[i]; + scriptTypes.push({matches: conf.matches, mode: conf.mode && CodeMirror.getMode(config, conf.mode)}); + } + scriptTypes.push({matches: /./, + mode: CodeMirror.getMode(config, "text/plain")}); + + function html(stream, state) { + var tagName = state.htmlState.tagName; + var style = htmlMode.token(stream, state.htmlState); + if (tagName == "script" && /\btag\b/.test(style) && stream.current() == ">") { + // Script block: mode to change to depends on type attribute + var scriptType = stream.string.slice(Math.max(0, stream.pos - 100), stream.pos).match(/\btype\s*=\s*("[^"]+"|'[^']+'|\S+)[^<]*$/i); + scriptType = scriptType ? scriptType[1] : ""; + if (scriptType && /[\"\']/.test(scriptType.charAt(0))) scriptType = scriptType.slice(1, scriptType.length - 1); + for (var i = 0; i < scriptTypes.length; ++i) { + var tp = scriptTypes[i]; + if (typeof tp.matches == "string" ? scriptType == tp.matches : tp.matches.test(scriptType)) { + if (tp.mode) { + state.token = script; + state.localMode = tp.mode; + state.localState = tp.mode.startState && tp.mode.startState(htmlMode.indent(state.htmlState, "")); + } + break; + } + } + } else if (tagName == "style" && /\btag\b/.test(style) && stream.current() == ">") { + state.token = css; + state.localMode = cssMode; + state.localState = cssMode.startState(htmlMode.indent(state.htmlState, "")); + } + return style; + } + function maybeBackup(stream, pat, style) { + var cur = stream.current(); + var close = cur.search(pat), m; + if (close > -1) stream.backUp(cur.length - close); + else if (m = cur.match(/<\/?$/)) { + stream.backUp(cur.length); + if (!stream.match(pat, false)) stream.match(cur[0]); + } + return style; + } + function script(stream, state) { + if (stream.match(/^<\/\s*script\s*>/i, false)) { + state.token = html; + state.localState = state.localMode = null; + return html(stream, state); + } + return maybeBackup(stream, /<\/\s*script\s*>/, + state.localMode.token(stream, state.localState)); + } + function css(stream, state) { + if (stream.match(/^<\/\s*style\s*>/i, false)) { + state.token = html; + state.localState = state.localMode = null; + return html(stream, state); + } + return maybeBackup(stream, /<\/\s*style\s*>/, + cssMode.token(stream, state.localState)); + } + + return { + startState: function() { + var state = htmlMode.startState(); + return {token: html, localMode: null, localState: null, htmlState: state}; + }, + + copyState: function(state) { + if (state.localState) + var local = CodeMirror.copyState(state.localMode, state.localState); + return {token: state.token, localMode: state.localMode, localState: local, + htmlState: CodeMirror.copyState(htmlMode, state.htmlState)}; + }, + + token: function(stream, state) { + return state.token(stream, state); + }, + + indent: function(state, textAfter) { + if (!state.localMode || /^\s*<\//.test(textAfter)) + return htmlMode.indent(state.htmlState, textAfter); + else if (state.localMode.indent) + return state.localMode.indent(state.localState, textAfter); + else + return CodeMirror.Pass; + }, + + electricChars: "/{}:", + + innerMode: function(state) { + return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode}; + } + }; +}, "xml", "javascript", "css"); + +CodeMirror.defineMIME("text/html", "htmlmixed");
new file mode 100644 --- /dev/null +++ b/browser/devtools/sourceeditor/codemirror/mozilla.css @@ -0,0 +1,30 @@ +.breakpoints { + width: 16px; +} + +.breakpoint, .debugLocation, .breakpoint-debugLocation { + display: inline-block; + margin-left: 5px; + width: 14px; + height: 14px; + background-repeat: no-repeat; + background-position: center center; + background-size: 12px; +} + +.breakpoint { + background-image: url("chrome://browser/skin/devtools/orion-breakpoint.png"); +} + +.debugLocation { + background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"); +} + +.breakpoint.debugLocation { + background-image: url("chrome://browser/skin/devtools/orion-debug-location.png"), + url("chrome://browser/skin/devtools/orion-breakpoint.png"); +} + +.CodeMirror-activeline-background { + background: #e8f2ff; +} \ No newline at end of file
new file mode 100644 --- /dev/null +++ b/browser/devtools/sourceeditor/codemirror/xml.js @@ -0,0 +1,341 @@ +CodeMirror.defineMode("xml", function(config, parserConfig) { + var indentUnit = config.indentUnit; + var multilineTagIndentFactor = parserConfig.multilineTagIndentFactor || 1; + var multilineTagIndentPastTag = parserConfig.multilineTagIndentPastTag || true; + + var Kludges = parserConfig.htmlMode ? { + autoSelfClosers: {'area': true, 'base': true, 'br': true, 'col': true, 'command': true, + 'embed': true, 'frame': true, 'hr': true, 'img': true, 'input': true, + 'keygen': true, 'link': true, 'meta': true, 'param': true, 'source': true, + 'track': true, 'wbr': true}, + implicitlyClosed: {'dd': true, 'li': true, 'optgroup': true, 'option': true, 'p': true, + 'rp': true, 'rt': true, 'tbody': true, 'td': true, 'tfoot': true, + 'th': true, 'tr': true}, + contextGrabbers: { + 'dd': {'dd': true, 'dt': true}, + 'dt': {'dd': true, 'dt': true}, + 'li': {'li': true}, + 'option': {'option': true, 'optgroup': true}, + 'optgroup': {'optgroup': true}, + 'p': {'address': true, 'article': true, 'aside': true, 'blockquote': true, 'dir': true, + 'div': true, 'dl': true, 'fieldset': true, 'footer': true, 'form': true, + 'h1': true, 'h2': true, 'h3': true, 'h4': true, 'h5': true, 'h6': true, + 'header': true, 'hgroup': true, 'hr': true, 'menu': true, 'nav': true, 'ol': true, + 'p': true, 'pre': true, 'section': true, 'table': true, 'ul': true}, + 'rp': {'rp': true, 'rt': true}, + 'rt': {'rp': true, 'rt': true}, + 'tbody': {'tbody': true, 'tfoot': true}, + 'td': {'td': true, 'th': true}, + 'tfoot': {'tbody': true}, + 'th': {'td': true, 'th': true}, + 'thead': {'tbody': true, 'tfoot': true}, + 'tr': {'tr': true} + }, + doNotIndent: {"pre": true}, + allowUnquoted: true, + allowMissing: true + } : { + autoSelfClosers: {}, + implicitlyClosed: {}, + contextGrabbers: {}, + doNotIndent: {}, + allowUnquoted: false, + allowMissing: false + }; + var alignCDATA = parserConfig.alignCDATA; + + // Return variables for tokenizers + var tagName, type; + + function inText(stream, state) { + function chain(parser) { + state.tokenize = parser; + return parser(stream, state); + } + + var ch = stream.next(); + if (ch == "<") { + if (stream.eat("!")) { + if (stream.eat("[")) { + if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>")); + else return null; + } else if (stream.match("--")) { + return chain(inBlock("comment", "-->")); + } else if (stream.match("DOCTYPE", true, true)) { + stream.eatWhile(/[\w\._\-]/); + return chain(doctype(1)); + } else { + return null; + } + } else if (stream.eat("?")) { + stream.eatWhile(/[\w\._\-]/); + state.tokenize = inBlock("meta", "?>"); + return "meta"; + } else { + var isClose = stream.eat("/"); + tagName = ""; + var c; + while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c; + if (!tagName) return "error"; + type = isClose ? "closeTag" : "openTag"; + state.tokenize = inTag; + return "tag"; + } + } else if (ch == "&") { + var ok; + if (stream.eat("#")) { + if (stream.eat("x")) { + ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";"); + } else { + ok = stream.eatWhile(/[\d]/) && stream.eat(";"); + } + } else { + ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";"); + } + return ok ? "atom" : "error"; + } else { + stream.eatWhile(/[^&<]/); + return null; + } + } + + function inTag(stream, state) { + var ch = stream.next(); + if (ch == ">" || (ch == "/" && stream.eat(">"))) { + state.tokenize = inText; + type = ch == ">" ? "endTag" : "selfcloseTag"; + return "tag"; + } else if (ch == "=") { + type = "equals"; + return null; + } else if (ch == "<") { + return "error"; + } else if (/[\'\"]/.test(ch)) { + state.tokenize = inAttribute(ch); + state.stringStartCol = stream.column(); + return state.tokenize(stream, state); + } else { + stream.eatWhile(/[^\s\u00a0=<>\"\']/); + return "word"; + } + } + + function inAttribute(quote) { + var closure = function(stream, state) { + while (!stream.eol()) { + if (stream.next() == quote) { + state.tokenize = inTag; + break; + } + } + return "string"; + }; + closure.isInAttribute = true; + return closure; + } + + function inBlock(style, terminator) { + return function(stream, state) { + while (!stream.eol()) { + if (stream.match(terminator)) { + state.tokenize = inText; + break; + } + stream.next(); + } + return style; + }; + } + function doctype(depth) { + return function(stream, state) { + var ch; + while ((ch = stream.next()) != null) { + if (ch == "<") { + state.tokenize = doctype(depth + 1); + return state.tokenize(stream, state); + } else if (ch == ">") { + if (depth == 1) { + state.tokenize = inText; + break; + } else { + state.tokenize = doctype(depth - 1); + return state.tokenize(stream, state); + } + } + } + return "meta"; + }; + } + + var curState, curStream, setStyle; + function pass() { + for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]); + } + function cont() { + pass.apply(null, arguments); + return true; + } + + function pushContext(tagName, startOfLine) { + var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent); + curState.context = { + prev: curState.context, + tagName: tagName, + indent: curState.indented, + startOfLine: startOfLine, + noIndent: noIndent + }; + } + function popContext() { + if (curState.context) curState.context = curState.context.prev; + } + + function element(type) { + if (type == "openTag") { + curState.tagName = tagName; + curState.tagStart = curStream.column(); + return cont(attributes, endtag(curState.startOfLine)); + } else if (type == "closeTag") { + var err = false; + if (curState.context) { + if (curState.context.tagName != tagName) { + if (Kludges.implicitlyClosed.hasOwnProperty(curState.context.tagName.toLowerCase())) { + popContext(); + } + err = !curState.context || curState.context.tagName != tagName; + } + } else { + err = true; + } + if (err) setStyle = "error"; + return cont(endclosetag(err)); + } + return cont(); + } + function endtag(startOfLine) { + return function(type) { + var tagName = curState.tagName; + curState.tagName = curState.tagStart = null; + if (type == "selfcloseTag" || + (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(tagName.toLowerCase()))) { + maybePopContext(tagName.toLowerCase()); + return cont(); + } + if (type == "endTag") { + maybePopContext(tagName.toLowerCase()); + pushContext(tagName, startOfLine); + return cont(); + } + return cont(); + }; + } + function endclosetag(err) { + return function(type) { + if (err) setStyle = "error"; + if (type == "endTag") { popContext(); return cont(); } + setStyle = "error"; + return cont(arguments.callee); + }; + } + function maybePopContext(nextTagName) { + var parentTagName; + while (true) { + if (!curState.context) { + return; + } + parentTagName = curState.context.tagName.toLowerCase(); + if (!Kludges.contextGrabbers.hasOwnProperty(parentTagName) || + !Kludges.contextGrabbers[parentTagName].hasOwnProperty(nextTagName)) { + return; + } + popContext(); + } + } + + function attributes(type) { + if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);} + if (type == "endTag" || type == "selfcloseTag") return pass(); + setStyle = "error"; + return cont(attributes); + } + function attribute(type) { + if (type == "equals") return cont(attvalue, attributes); + if (!Kludges.allowMissing) setStyle = "error"; + else if (type == "word") setStyle = "attribute"; + return (type == "endTag" || type == "selfcloseTag") ? pass() : cont(); + } + function attvalue(type) { + if (type == "string") return cont(attvaluemaybe); + if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();} + setStyle = "error"; + return (type == "endTag" || type == "selfCloseTag") ? pass() : cont(); + } + function attvaluemaybe(type) { + if (type == "string") return cont(attvaluemaybe); + else return pass(); + } + + return { + startState: function() { + return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, tagStart: null, context: null}; + }, + + token: function(stream, state) { + if (!state.tagName && stream.sol()) { + state.startOfLine = true; + state.indented = stream.indentation(); + } + if (stream.eatSpace()) return null; + + setStyle = type = tagName = null; + var style = state.tokenize(stream, state); + state.type = type; + if ((style || type) && style != "comment") { + curState = state; curStream = stream; + while (true) { + var comb = state.cc.pop() || element; + if (comb(type || style)) break; + } + } + state.startOfLine = false; + return setStyle || style; + }, + + indent: function(state, textAfter, fullLine) { + var context = state.context; + // Indent multi-line strings (e.g. css). + if (state.tokenize.isInAttribute) { + return state.stringStartCol + 1; + } + if ((state.tokenize != inTag && state.tokenize != inText) || + context && context.noIndent) + return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0; + // Indent the starts of attribute names. + if (state.tagName) { + if (multilineTagIndentPastTag) + return state.tagStart + state.tagName.length + 2; + else + return state.tagStart + indentUnit * multilineTagIndentFactor; + } + if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0; + if (context && /^<\//.test(textAfter)) + context = context.prev; + while (context && !context.startOfLine) + context = context.prev; + if (context) return context.indent + indentUnit; + else return 0; + }, + + electricChars: "/", + blockCommentStart: "<!--", + blockCommentEnd: "-->", + + configuration: parserConfig.htmlMode ? "html" : "xml", + helperType: parserConfig.htmlMode ? "html" : "xml" + }; +}); + +CodeMirror.defineMIME("text/xml", "xml"); +CodeMirror.defineMIME("application/xml", "xml"); +if (!CodeMirror.mimeModes.hasOwnProperty("text/html")) + CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
new file mode 100644 --- /dev/null +++ b/browser/devtools/sourceeditor/debugger.js @@ -0,0 +1,267 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + "use strict"; + +const dbginfo = new WeakMap(); + +// Private functions + +/** + * Adds a marker to the breakpoints gutter. + * Type should be either a 'breakpoint' or a 'debugLocation'. + */ +function addMarker(cm, line, type) { + let info = cm.lineInfo(line); + + if (info.gutterMarkers) + return void info.gutterMarkers.breakpoints.classList.add(type); + + let mark = cm.getWrapperElement().ownerDocument.createElement("div"); + mark.className = type; + mark.innerHTML = ""; + + cm.setGutterMarker(info.line, "breakpoints", mark); +} + +/** + * Removes a marker from the breakpoints gutter. + * Type should be either a 'breakpoint' or a 'debugLocation'. + */ +function removeMarker(cm, line, type) { + let info = cm.lineInfo(line); + + if (!info || !info.gutterMarkers) + return; + + info.gutterMarkers.breakpoints.classList.remove(type); +} + +// These functions implement search within the debugger. Since +// search in the debugger is different from other components, +// we can't use search.js CodeMirror addon. This is a slightly +// modified version of that addon. Depends on searchcursor.js. + +function SearchState() { + this.posFrom = this.posTo = this.query = null; +} + +function getSearchState(cm) { + return cm.state.search || (cm.state.search = new SearchState()); +} + +function getSearchCursor(cm, query, pos) { + // If the query string is all lowercase, do a case insensitive search. + return cm.getSearchCursor(query, pos, + typeof query == "string" && query == query.toLowerCase()); +} + +/** + * If there's a saved search, selects the next results. + * Otherwise, creates a new search and selects the first + * result. + */ +function doSearch(cm, rev, query) { + let state = getSearchState(cm); + + if (state.query) + return searchNext(cm, rev); + + cm.operation(function () { + if (state.query) return; + + state.query = query; + state.posFrom = state.posTo = { line: 0, ch: 0 }; + searchNext(cm, rev); + }); +} + +/** + * Selects the next result of a saved search. + */ +function searchNext(cm, rev) { + cm.operation(function () { + let state = getSearchState(cm) + let cursor = getSearchCursor(cm, state.query, rev ? state.posFrom : state.posTo); + + if (!cursor.find(rev)) { + cursor = getSearchCursor(cm, state.query, rev ? + { line: cm.lastLine(), ch: null } : { line: cm.firstLine(), ch: 0 }); + if (!cursor.find(rev)) + return; + } + + cm.setSelection(cursor.from(), cursor.to()); + state.posFrom = cursor.from(); + state.posTo = cursor.to(); + }); +} + +/** + * Clears the currently saved search. + */ +function clearSearch(cm) { + let state = getSearchState(cm); + + if (!state.query) + return; + + state.query = null; +} + +// Exported functions + +/** + * This function is called whenever Editor is extended with functions + * from this module. See Editor.extend for more info. + */ +function initialize(ctx) { + let { ed } = ctx; + + dbginfo.set(ed, { + breakpoints: {}, + debugLocation: null + }); +} + +/** + * True if editor has a visual breakpoint at that line, false + * otherwise. + */ +function hasBreakpoint(ctx, line) { + let { cm } = ctx; + let markers = cm.lineInfo(line).gutterMarkers; + + return markers != null && + markers.breakpoints.classList.contains("breakpoint"); +} + +/** + * Adds a visual breakpoint for a specified line. Third + * parameter 'cond' can hold any object. + * + * After adding a breakpoint, this function makes Editor to + * emit a breakpointAdded event. + */ +function addBreakpoint(ctx, line, cond) { + if (hasBreakpoint(ctx, line)) + return; + + let { ed, cm } = ctx; + let meta = dbginfo.get(ed); + let info = cm.lineInfo(line); + + addMarker(cm, line, "breakpoint"); + meta.breakpoints[line] = { condition: cond }; + + info.handle.on("delete", function onDelete() { + info.handle.off("delete", onDelete); + meta.breakpoints[info.line] = null; + }); + + ed.emit("breakpointAdded", line); +} + +/** + * Removes a visual breakpoint from a specified line and + * makes Editor to emit a breakpointRemoved event. + */ +function removeBreakpoint(ctx, line) { + if (!hasBreakpoint(ctx, line)) + return; + + let { ed, cm } = ctx; + let meta = dbginfo.get(ed); + let info = cm.lineInfo(line); + + meta.breakpoints[info.line] = null; + removeMarker(cm, info.line, "breakpoint"); + ed.emit("breakpointRemoved", line); +} + +/** + * Returns a list of all breakpoints in the current Editor. + */ +function getBreakpoints(ctx) { + let { ed } = ctx; + let meta = dbginfo.get(ed); + + return Object.keys(meta.breakpoints).reduce((acc, line) => { + if (meta.breakpoints[line] != null) + acc.push({ line: line, condition: meta.breakpoints[line].condition }); + return acc; + }, []); +} + +/** + * Saves a debug location information and adds a visual anchor to + * the breakpoints gutter. This is used by the debugger UI to + * display the line on which the Debugger is currently paused. + */ +function setDebugLocation(ctx, line) { + let { ed, cm } = ctx; + let meta = dbginfo.get(ed); + + meta.debugLocation = line; + addMarker(cm, line, "debugLocation"); +} + +/** + * Returns a line number that corresponds to the current debug + * location. + */ +function getDebugLocation(ctx) { + let { ed } = ctx; + let meta = dbginfo.get(ed); + + return meta.debugLocation; +} + +/** + * Clears the debug location. Clearing the debug location + * also removes a visual anchor from the breakpoints gutter. + */ +function clearDebugLocation(ctx) { + let { ed, cm } = ctx; + let meta = dbginfo.get(ed); + + if (meta.debugLocation != null) { + removeMarker(cm, meta.debugLocation, "debugLocation"); + meta.debugLocation = null; + } +} + +/** + * Starts a new search. + */ +function find(ctx, query) { + let { cm } = ctx; + clearSearch(cm); + doSearch(cm, false, query); +} + +/** + * Finds the next item based on the currently saved search. + */ +function findNext(ctx, query) { + let { cm } = ctx; + doSearch(cm, false, query); +} + +/** + * Finds the previous item based on the currently saved search. + */ +function findPrev(ctx, query) { + let { cm } = ctx; + doSearch(cm, true, query); +} + + +// Export functions + +[ + initialize, hasBreakpoint, addBreakpoint, removeBreakpoint, + getBreakpoints, setDebugLocation, getDebugLocation, + clearDebugLocation, find, findNext, findPrev +].forEach(function (func) { module.exports[func.name] = func; }); \ No newline at end of file
--- a/browser/devtools/sourceeditor/editor.js +++ b/browser/devtools/sourceeditor/editor.js @@ -19,26 +19,32 @@ Cu.import("resource://gre/modules/Servic const L10N = Services.strings.createBundle(L10N_BUNDLE); // CM_STYLES, CM_SCRIPTS and CM_IFRAME represent the HTML, // JavaScript and CSS that is injected into an iframe in // order to initialize a CodeMirror instance. const CM_STYLES = [ "chrome://browser/content/devtools/codemirror/codemirror.css", - "chrome://browser/content/devtools/codemirror/dialog.css" + "chrome://browser/content/devtools/codemirror/dialog.css", + "chrome://browser/content/devtools/codemirror/mozilla.css" ]; const CM_SCRIPTS = [ "chrome://browser/content/devtools/codemirror/codemirror.js", "chrome://browser/content/devtools/codemirror/dialog.js", "chrome://browser/content/devtools/codemirror/searchcursor.js", "chrome://browser/content/devtools/codemirror/search.js", "chrome://browser/content/devtools/codemirror/matchbrackets.js", - "chrome://browser/content/devtools/codemirror/comment.js" + "chrome://browser/content/devtools/codemirror/comment.js", + "chrome://browser/content/devtools/codemirror/javascript.js", + "chrome://browser/content/devtools/codemirror/xml.js", + "chrome://browser/content/devtools/codemirror/css.js", + "chrome://browser/content/devtools/codemirror/htmlmixed.js", + "chrome://browser/content/devtools/codemirror/activeline.js" ]; const CM_IFRAME = "data:text/html;charset=utf8,<!DOCTYPE html>" + "<html dir='ltr'>" + " <head>" + " <style>" + " html, body { height: 100%; }" + @@ -57,30 +63,33 @@ const CM_MAPPING = [ "getCursor", "somethingSelected", "setSelection", "getSelection", "replaceSelection", "undo", "redo", "clearHistory", - "posFromIndex", - "openDialog" + "openDialog", + "cursorCoords", + "lineCount" ]; const CM_JUMP_DIALOG = [ L10N.GetStringFromName("gotoLineCmd.promptTitle") + " <input type=text style='width: 10em'/>" ]; const editors = new WeakMap(); Editor.modes = { text: { name: "text" }, - js: { name: "javascript", url: "chrome://browser/content/devtools/codemirror/javascript.js" } + js: { name: "javascript" }, + html: { name: "htmlmixed" }, + css: { name: "css" } }; function ctrl(k) { return (Services.appinfo.OS == "Darwin" ? "Cmd-" : "Ctrl-") + k; } /** * A very thin wrapper around CodeMirror. Provides a number @@ -104,24 +113,25 @@ function ctrl(k) { * CodeMirror docs: http://codemirror.net/doc/manual.html */ function Editor(config) { const tabSize = Services.prefs.getIntPref(TAB_SIZE); const useTabs = !Services.prefs.getBoolPref(EXPAND_TAB); this.version = null; this.config = { - value: "", - mode: Editor.modes.text, - indentUnit: tabSize, - tabSize: tabSize, - contextMenu: null, - matchBrackets: true, - extraKeys: {}, - indentWithTabs: useTabs, + value: "", + mode: Editor.modes.text, + indentUnit: tabSize, + tabSize: tabSize, + contextMenu: null, + matchBrackets: true, + extraKeys: {}, + indentWithTabs: useTabs, + styleActiveLine: true }; // Overwrite default config with user-provided, if needed. Object.keys(config).forEach((k) => this.config[k] = config[k]); // Additional shortcuts. this.config.extraKeys[ctrl("J")] = (cm) => this.jumpToLine(); this.config.extraKeys[ctrl("/")] = "toggleComment"; @@ -146,18 +156,19 @@ function Editor(config) { if (cm.getCursor().ch !== 0) num -= 1; cm.replaceSelection(" ".repeat(num), "end", "+input"); }; events.decorate(this); } Editor.prototype = { + container: null, version: null, - config: null, + config: null, /** * Appends the current Editor instance to the element specified by * the only argument 'el'. This method actually creates and loads * CodeMirror and all its dependencies. * * This method is asynchronous and returns a promise. */ @@ -169,40 +180,41 @@ Editor.prototype = { env.flex = 1; if (cm) throw new Error("You can append an editor only once."); let onLoad = () => { // Once the iframe is loaded, we can inject CodeMirror // and its dependencies into its DOM. + env.removeEventListener("load", onLoad, true); let win = env.contentWindow.wrappedJSObject; CM_SCRIPTS.forEach((url) => Services.scriptloader.loadSubScript(url, win, "utf8")); - // Plain text mode doesn't need any additional files, - // all other modes (js, html, etc.) do. - if (this.config.mode.name !== "text") - Services.scriptloader.loadSubScript(this.config.mode.url, win, "utf8"); - // Create a CodeMirror instance add support for context menus and // overwrite the default controller (otherwise items in the top and // context menus won't work). cm = win.CodeMirror(win.document.body, this.config); cm.getWrapperElement().addEventListener("contextmenu", (ev) => { ev.preventDefault(); + this.emit("contextMenu"); this.showContextMenu(doc, ev.screenX, ev.screenY); }, false); cm.on("change", () => this.emit("change")); + cm.on("gutterClick", (cm, line) => this.emit("gutterClick", line)); + cm.on("cursorActivity", (cm) => this.emit("cursorActivity")); + doc.defaultView.controllers.insertControllerAt(0, controller(this, doc.defaultView)); + this.container = env; editors.set(this, cm); def.resolve(); }; env.addEventListener("load", onLoad, true); env.setAttribute("src", CM_IFRAME); el.appendChild(env); @@ -247,33 +259,61 @@ Editor.prototype = { */ getOffset: function (...args) { let cm = editors.get(this); let res = args.map((pos) => cm.indexFromPos(pos)); return args.length > 1 ? res : res[0]; }, /** - * Returns text from the text area. + * Returns text from the text area. If line argument is provided + * the method returns only that line. */ - getText: function () { + getText: function (line) { let cm = editors.get(this); - return cm.getValue(); + return line == null ? + cm.getValue() : (cm.lineInfo(line) ? cm.lineInfo(line).text : ""); }, /** * Replaces whatever is in the text area with the contents of * the 'value' argument. */ setText: function (value) { let cm = editors.get(this); cm.setValue(value); }, /** + * Changes the value of a currently used highlighting mode. + * See Editor.modes for the list of all suppoert modes. + */ + setMode: function (value) { + let cm = editors.get(this); + cm.setOption("mode", value); + }, + + /** + * Returns the currently active highlighting mode. + * See Editor.modes for the list of all suppoert modes. + */ + getMode: function () { + let cm = editors.get(this); + return cm.getOption("mode"); + }, + + /** + * True if the editor is in the read-only mode, false otherwise. + */ + isReadOnly: function () { + let cm = editors.get(this); + return cm.getOption("readOnly"); + }, + + /** * Replaces contents of a text area within the from/to {line, ch} * range. If neither from nor to arguments are provided works * exactly like setText. If only from object is provided, inserts * text at that point. */ replaceText: function (value, from, to) { let cm = editors.get(this); @@ -335,18 +375,69 @@ Editor.prototype = { * This method opens an in-editor dialog asking for a line to * jump to. Once given, it changes cursor to that line. */ jumpToLine: function () { this.openDialog(CM_JUMP_DIALOG, (line) => this.setCursor({ line: line - 1, ch: 0 })); }, + /** + * Returns a {line, ch} object that corresponds to the + * left, top coordinates. + */ + getPositionFromCoords: function (left, top) { + let cm = editors.get(this); + return cm.coordsChar({ left: left, top: top }); + }, + + /** + * Extends the current selection to the position specified + * by the provided {line, ch} object. + */ + extendSelection: function (pos) { + let cm = editors.get(this); + let cursor = cm.indexFromPos(cm.getCursor()); + let anchor = cm.posFromIndex(cursor + pos.start); + let head = cm.posFromIndex(cursor + pos.start + pos.length); + cm.setSelection(anchor, head); + }, + + /** + * Extends an instance of the Editor object with additional + * functions. Each function will be called with context as + * the first argument. Context is a {ed, cm} object where + * 'ed' is an instance of the Editor object and 'cm' is an + * instance of the CodeMirror object. Example: + * + * function hello(ctx, name) { + * let { cm, ed } = ctx; + * cm; // CodeMirror instance + * ed; // Editor instance + * name; // 'Mozilla' + * } + * + * editor.extend({ hello: hello }); + * editor.hello('Mozilla'); + */ + extend: function (funcs) { + Object.keys(funcs).forEach((name) => { + let cm = editors.get(this); + let ctx = { ed: this, cm: cm }; + + if (name === "initialize") + return void funcs[name](ctx); + + this[name] = funcs[name].bind(null, ctx); + }); + }, + destroy: function () { - this.config = null; + this.container = null; + this.config = null; this.version = null; this.emit("destroy"); } }; // Since Editor is a thin layer over CodeMirror some methods // are mapped directly—without any changes. @@ -360,18 +451,16 @@ CM_MAPPING.forEach(function (name) { /** * Returns a controller object that can be used for * editor-specific commands such as find, jump to line, * copy/paste, etc. */ function controller(ed, view) { return { supportsCommand: function (cmd) { - let cm = editors.get(ed); - switch (cmd) { case "cmd_find": case "cmd_findAgain": case "cmd_findPrevious": case "cmd_gotoLine": case "cmd_undo": case "cmd_redo": case "cmd_cut":
--- a/browser/devtools/sourceeditor/moz.build +++ b/browser/devtools/sourceeditor/moz.build @@ -4,14 +4,15 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. TEST_DIRS += ['test'] JS_MODULES_PATH = 'modules/devtools/sourceeditor' EXTRA_JS_MODULES += [ + 'debugger.js', 'editor.js', 'source-editor-orion.jsm', 'source-editor-ui.jsm', 'source-editor.jsm', ]
--- a/browser/devtools/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js +++ b/browser/devtools/webconsole/test/browser_webconsole_bug_766001_JS_Console_in_Debugger.js @@ -96,17 +96,17 @@ function onSource(aEvent, aSource) { function checkCorrectLine(aCallback) { waitForSuccess({ name: "correct source and line test for debugger for index " + index, validatorFn: function() { let debuggerView = dbg.panelWin.DebuggerView; if (debuggerView.editor && - debuggerView.editor.getCaretPosition().line == line - 1) { + debuggerView.editor.getCursor().line == line - 1) { return true; } return false; }, successFn: function() { aCallback && executeSoon(aCallback); },
--- a/browser/installer/package-manifest.in +++ b/browser/installer/package-manifest.in @@ -349,16 +349,18 @@ @BINPATH@/browser/components/nsBrowserContentHandler.js @BINPATH@/browser/components/nsBrowserGlue.js @BINPATH@/browser/components/nsSetDefaultBrowser.manifest @BINPATH@/browser/components/nsSetDefaultBrowser.js @BINPATH@/browser/components/BrowserDownloads.manifest @BINPATH@/browser/components/DownloadsStartup.js @BINPATH@/browser/components/DownloadsUI.js @BINPATH@/browser/components/BrowserPlaces.manifest +@BINPATH@/browser/components/devtools-clhandler.manifest +@BINPATH@/browser/components/devtools-clhandler.js @BINPATH@/components/Downloads.manifest @BINPATH@/components/DownloadLegacy.js @BINPATH@/components/BrowserPageThumbs.manifest @BINPATH@/components/SiteSpecificUserAgent.js @BINPATH@/components/SiteSpecificUserAgent.manifest @BINPATH@/components/toolkitsearch.manifest @BINPATH@/components/nsSearchService.js @BINPATH@/components/nsSearchSuggestions.js
--- a/browser/metro/base/content/bindings/grid.xml +++ b/browser/metro/base/content/bindings/grid.xml @@ -86,16 +86,28 @@ this._fireEvent("select"); } else { this._fireEvent("selectionchange"); } ]]> </body> </method> + <method name="selectNone"> + <body> + <![CDATA[ + let selectedCount = this.selectedItems.length; + this.clearSelection(); + if (selectedCount && "single" != this.getAttribute("seltype")) { + this._fireEvent("selectionchange"); + } + ]]> + </body> + </method> + <method name="handleItemClick"> <parameter name="aItem"/> <parameter name="aEvent"/> <body> <![CDATA[ if (!this.isBound) return; @@ -111,17 +123,17 @@ </body> </method> <method name="handleItemContextMenu"> <parameter name="aItem"/> <parameter name="aEvent"/> <body> <![CDATA[ - if (!this.isBound || this.suppressOnSelect) + if (!this.isBound || this.noContext) return; // we'll republish this as a selectionchange event on the grid aEvent.stopPropagation(); this.toggleItemSelection(aItem); ]]> </body> </method> @@ -187,17 +199,17 @@ ]]> </getter> <setter> <![CDATA[ if (val >= 0) { let selected = this.getItemAtIndex(val); this.selectItem(selected); } else { - this.clearSelection(); + this.selectNone(); } ]]> </setter> </property> <method name="appendItem"> <parameter name="aLabel"/> <parameter name="aValue"/> @@ -605,16 +617,19 @@ ]]> </body> </method> <!-- Inteface to suppress selection events --> <property name="suppressOnSelect" onget="return this.getAttribute('suppressonselect') == 'true';" onset="this.setAttribute('suppressonselect', val);"/> + <property name="noContext" + onget="return this.hasAttribute('nocontext');" + onset="if (val) this.setAttribute('nocontext', true); else this.removeAttribute('nocontext');"/> <property name="crossSlideBoundary" onget="return this.hasAttribute('crossslideboundary')? this.getAttribute('crossslideboundary') : Infinity;"/> <!-- Internal methods --> <field name="_xslideHandler"/> <constructor> <![CDATA[ // create our quota of item slots @@ -622,17 +637,17 @@ count < slotCount; count++) { this.appendChild( this._createItemElement() ); } if (this.controller && this.controller.gridBoundCallback != undefined) this.controller.gridBoundCallback(); // set up cross-slide gesture handling for multiple-selection grids - if ("undefined" !== typeof CrossSlide && "multiple" == this.getAttribute("seltype")) { + if ("undefined" !== typeof CrossSlide && !this.noContext) { this._xslideHandler = new CrossSlide.Handler(this, { REARRANGESTART: this.crossSlideBoundary }); } // XXX This event was never actually implemented (bug 223411). let event = document.createEvent("Events"); event.initEvent("contentgenerated", true, true); @@ -831,17 +846,17 @@ <!-- /item bend effect handler --> <handler event="context-action"> <![CDATA[ // context-action is an event fired by the appbar typically // which directs us to do something to the selected tiles switch (event.action) { case "clear": - this.clearSelection(); + this.selectNone(); break; default: if (this.controller && this.controller.doActionOnSelectedTiles) { this.controller.doActionOnSelectedTiles(event.action, event); } } ]]> </handler> @@ -891,17 +906,17 @@ event.target.removeAttribute('crosssliding'); event.target.style.removeProperty('transform'); break; } ]]> </handler> <handler event="MozCrossSlideSelect"> <![CDATA[ - if (this.suppressOnSelect) + if (this.noContext) return; this.toggleItemSelection(event.target); ]]> </handler> </handlers> </binding> <binding id="richgrid-item">
--- a/browser/metro/base/content/bindings/urlbar.xml +++ b/browser/metro/base/content/bindings/urlbar.xml @@ -500,31 +500,30 @@ </binding> <binding id="urlbar-autocomplete"> <content class="meta-section-container"> <xul:vbox class="meta-section" anonid="results-container" flex="1"> <xul:label class="meta-section-title" value="&autocompleteResultsHeader.label;"/> <richgrid anonid="results" rows="3" flex="1" - seltype="single" deferlayout="true"/> + seltype="single" nocontext="true" deferlayout="true"/> </xul:vbox> <xul:vbox class="meta-section" flex="1"> <xul:label anonid="searches-header" class="meta-section-title"/> <richgrid anonid="searches" rows="3" flex="1" - seltype="single" deferlayout="true"/> + seltype="single" nocontext="true" deferlayout="true"/> </xul:vbox> </content> <implementation implements="nsIAutoCompletePopup, nsIObserver"> <constructor> <![CDATA[ this.hidden = true; - Services.obs.addObserver(this, "browser-search-engine-modified", false); this._results.controller = this; this._searches.controller = this; ]]> </constructor> <destructor>
--- a/browser/metro/base/content/helperui/MenuUI.js +++ b/browser/metro/base/content/helperui/MenuUI.js @@ -373,23 +373,16 @@ MenuPopup.prototype = { } let width = this._popup.boxObject.width; let height = this._popup.boxObject.height; let halfWidth = width / 2; let screenWidth = ContentAreaObserver.width; let screenHeight = ContentAreaObserver.height; - // Add padding on the side of the menu per the user's hand preference - let leftHand = - Services.metro.handPreference == Ci.nsIWinMetroUtils.handPreferenceLeft; - if (aSource && aSource == Ci.nsIDOMMouseEvent.MOZ_SOURCE_TOUCH) { - this.commands.setAttribute("left-hand", leftHand); - } - if (aPositionOptions.rightAligned) aX -= width; if (aPositionOptions.bottomAligned) aY -= height; if (aPositionOptions.centerHorizontally) aX -= halfWidth;
--- a/browser/metro/base/tests/mochitest/browser_snappedState.js +++ b/browser/metro/base/tests/mochitest/browser_snappedState.js @@ -109,25 +109,64 @@ gTests.push({ ok(Browser.selectedBrowser.contentWindow.scrollMaxY !== 0, "Snapped scrolls vertically"); ok(Browser.selectedBrowser.contentWindow.scrollMaxX === 0, "Snapped does not scroll horizontally"); }, tearDown: function() { BookmarksTestHelper.restore(); yield restoreViewstate(); } }); +gTests.push({ + desc: "Test tile selection is cleared and disabled", + setUp: function() { + BookmarksTestHelper.setup(); + HistoryTestHelper.setup(); + showStartUI(); + }, + run: function() { + // minimal event mocking to trigger context-click handlers + function makeMockEvent(item) { + return { + stopPropagation: function() {}, + target: item + }; + } + let startWin = Browser.selectedBrowser.contentWindow; + // make sure the bookmarks grid is showing + startWin.StartUI.onNarrowTitleClick("start-bookmarks"); + let bookmarksGrid = startWin.document.querySelector("#start-bookmarks-grid"); + // sanity check + ok(bookmarksGrid, "matched bookmarks grid"); + ok(bookmarksGrid.children[0], "bookmarks grid has items"); + // select a tile (balancing implementation leakage with test simplicity) + let mockEvent = makeMockEvent(bookmarksGrid.children[0]); + bookmarksGrid.handleItemContextMenu(bookmarksGrid.children[0], mockEvent); + // check tile was selected + is(bookmarksGrid.selectedItems.length, 1, "Tile got selected in landscape view"); + // switch to snapped view + yield setSnappedViewstate(); + is(bookmarksGrid.selectedItems.length, 0, "grid items selection cleared in snapped view"); + // attempt to select a tile in snapped view + mockEvent = makeMockEvent(bookmarksGrid.children[0]); + bookmarksGrid.handleItemContextMenu(bookmarksGrid.children[0], mockEvent); + is(bookmarksGrid.selectedItems.length, 0, "no grid item selections possible in snapped view"); + }, + tearDown: function() { + BookmarksTestHelper.restore(); + HistoryTestHelper.restore(); + yield restoreViewstate(); + } +}); gTests.push({ desc: "Navbar contextual buttons are not shown in snapped", setUp: setUpSnapped, run: function() { let toolbarContextual = document.getElementById("toolbar-contextual"); - let visibility = getComputedStyle(toolbarContextual).getPropertyValue("visibility"); - ok(visibility === "collapse" || visibility === "hidden", "Contextual buttons not shown in navbar"); }, tearDown: restoreViewstate }); gTests.push({ desc: "Test Portrait titles", setUp: setUpPortrait,
--- a/browser/metro/base/tests/mochitest/browser_tilegrid.xul +++ b/browser/metro/base/tests/mochitest/browser_tilegrid.xul @@ -3,31 +3,28 @@ <!-- This Source Code Form is subject to the terms of the Mozilla Public - License, v. 2.0. If a copy of the MPL was not distributed with this - file, You can obtain one at http://mozilla.org/MPL/2.0/. --> <?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?> <?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?> <?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?> <?xml-stylesheet href="chrome://browser/skin/tiles.css" type="text/css"?> - <!DOCTYPE window []> - <window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"> - <vbox id="alayout"> - <richgrid id="grid_layout" seltype="single" flex="1"> + <richgrid id="grid_layout" seltype="single" nocontext="true" flex="1"> </richgrid> </vbox> <vbox> <richgrid id="slots_grid" seltype="single" minSlots="6" flex="1"/> </vbox> <vbox style="height:600px"> <hbox> - <richgrid id="clearGrid" seltype="single" flex="1" rows="2"> + <richgrid id="clearGrid" seltype="single" nocontext="true" flex="1" rows="2"> <richgriditem value="about:blank" id="clearGrid_item1" label="First item"/> <richgriditem value="about:blank" id="clearGrid_item2" label="2nd item"/> <richgriditem value="about:blank" id="clearGrid_item1" label="First item"/> </richgrid> </hbox> <hbox> <richgrid id="emptyGrid" seltype="single" flex="1" rows="2" minSlots="6"> </richgrid>
--- a/browser/metro/base/tests/mochitest/browser_tiles.js +++ b/browser/metro/base/tests/mochitest/browser_tiles.js @@ -350,33 +350,35 @@ gTests.push({ is(grid.selectedIndex, -1, "selectedIndex reports correctly with nothing selected"); // item selection grid.selectItem(grid.items[1]); ok(grid.items[1].selected, "Item selected property is truthy after grid.selectItem"); ok(grid.items[1].getAttribute("selected"), "Item selected attribute is truthy after grid.selectItem"); ok(grid.selectedItems.length, "There are selectedItems after grid.selectItem"); - // clearSelection - grid.selectItem(grid.items[0]); - grid.selectItem(grid.items[1]); - grid.clearSelection(); - is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection"); - is(grid.selectedIndex, -1, "selectedIndex resets after clearSelection"); - // select events // in seltype=single mode, select is like the default action for the tile // (think <a>, not <select multiple>) let handler = { handleEvent: function(aEvent) {} }; let handlerStub = stubMethod(handler, "handleEvent"); + + grid.items[1].selected = true; + doc.defaultView.addEventListener("select", handler, false); info("select listener added"); + // clearSelection + grid.clearSelection(); + is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection"); + is(grid.selectedIndex, -1, "selectedIndex resets after clearSelection"); + is(handlerStub.callCount, 0, "clearSelection should not fire a selectionchange event"); + info("calling selectItem, currently it is:" + grid.items[0].selected); // Note: A richgrid in seltype=single mode fires "select" events from selectItem grid.selectItem(grid.items[0]); info("calling selectItem, now it is:" + grid.items[0].selected); yield waitForMs(0); is(handlerStub.callCount, 1, "select event handler was called when we selected an item"); is(handlerStub.calledWith[0].type, "select", "handler got a select event"); @@ -407,48 +409,82 @@ gTests.push({ ok(grid.items[1].selected, "toggleItemSelection sets truthy selected prop on previously-unselected item"); is(grid.selectedItems.length, 1, "1 item selected when we first toggleItemSelection"); is(grid.selectedItems[0], grid.items[1], "the right item is selected"); is(grid.selectedIndex, 1, "selectedIndex is correct"); grid.toggleItemSelection(grid.items[1]); is(grid.selectedItems.length, 0, "Nothing selected when we toggleItemSelection again"); - // clearSelection - grid.items[0].selected=true; - grid.items[1].selected=true; - is(grid.selectedItems.length, 2, "Both items are selected before calling clearSelection"); - grid.clearSelection(); - is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection"); - ok(!(grid.items[0].selected || grid.items[1].selected), "selected properties all falsy when we clearSelection"); - // selectionchange events // in seltype=multiple mode, we track selected state on all items // (think <select multiple> not <a>) let handler = { handleEvent: function(aEvent) {} }; let handlerStub = stubMethod(handler, "handleEvent"); doc.defaultView.addEventListener("selectionchange", handler, false); info("selectionchange listener added"); + // clearSelection + grid.items[0].selected=true; + grid.items[1].selected=true; + is(grid.selectedItems.length, 2, "Both items are selected before calling clearSelection"); + grid.clearSelection(); + is(grid.selectedItems.length, 0, "Nothing selected when we clearSelection"); + ok(!(grid.items[0].selected || grid.items[1].selected), "selected properties all falsy when we clearSelection"); + is(handlerStub.callCount, 0, "clearSelection should not fire a selectionchange event"); + info("calling toggleItemSelection, currently it is:" + grid.items[0].selected); // Note: A richgrid in seltype=single mode fires "select" events from selectItem grid.toggleItemSelection(grid.items[0]); info("/calling toggleItemSelection, now it is:" + grid.items[0].selected); yield waitForMs(0); is(handlerStub.callCount, 1, "selectionchange event handler was called when we selected an item"); is(handlerStub.calledWith[0].type, "selectionchange", "handler got a selectionchange event"); is(handlerStub.calledWith[0].target, grid, "select event had the originating grid as the target"); handlerStub.restore(); doc.defaultView.removeEventListener("selectionchange", handler, false); } }); +gTests.push({ + desc: "selectNone", + run: function() { + let grid = doc.querySelector("#grid-select2"); + + is(typeof grid.selectNone, "function", "selectNone is a function on the grid"); + + is(grid.itemCount, 2, "2 items initially"); + + // selectNone should fire a selectionchange event + let handler = { + handleEvent: function(aEvent) {} + }; + let handlerStub = stubMethod(handler, "handleEvent"); + doc.defaultView.addEventListener("selectionchange", handler, false); + info("selectionchange listener added"); + + grid.items[0].selected=true; + grid.items[1].selected=true; + is(grid.selectedItems.length, 2, "Both items are selected before calling selectNone"); + grid.selectNone(); + + is(grid.selectedItems.length, 0, "Nothing selected when we selectNone"); + ok(!(grid.items[0].selected || grid.items[1].selected), "selected properties all falsy when we selectNone"); + + is(handlerStub.callCount, 1, "selectionchange event handler was called when we selectNone"); + is(handlerStub.calledWith[0].type, "selectionchange", "handler got a selectionchange event"); + is(handlerStub.calledWith[0].target, grid, "selectionchange event had the originating grid as the target"); + handlerStub.restore(); + doc.defaultView.removeEventListener("selectionchange", handler, false); + } +}); + function gridSlotsSetup() { let grid = this.grid = doc.createElement("richgrid"); grid.setAttribute("minSlots", 6); doc.documentElement.appendChild(grid); is(grid.ownerDocument, doc, "created grid in the expected document"); } function gridSlotsTearDown() { this.grid && this.grid.parentNode.removeChild(this.grid);
--- a/browser/metro/modules/View.jsm +++ b/browser/metro/modules/View.jsm @@ -36,29 +36,38 @@ function View(aSet) { } View.prototype = { destruct: function () { Services.obs.removeObserver(this.viewStateObserver, "metro_viewstate_changed"); }, _adjustDOMforViewState: function _adjustDOMforViewState(aState) { - if (this._set) { - if (undefined == aState) - aState = this._set.getAttribute("viewstate"); - - this._set.setAttribute("suppressonselect", (aState == "snapped")); - - if (aState == "portrait") { - this._set.setAttribute("vertical", true); - } else { - this._set.removeAttribute("vertical"); - } - - this._set.arrangeItems(); + let grid = this._set; + if (!grid) { + return; + } + if (!aState) { + aState = grid.getAttribute("viewstate"); + } + switch (aState) { + case "snapped": + grid.setAttribute("nocontext", true); + grid.selectNone(); + break; + case "portrait": + grid.removeAttribute("nocontext"); + grid.setAttribute("vertical", true); + break; + default: + grid.removeAttribute("nocontext"); + grid.removeAttribute("vertical"); + } + if ("arrangeItems" in grid) { + grid.arrangeItems(); } }, _updateFavicon: function pv__updateFavicon(aItem, aUri) { if ("string" == typeof aUri) { aUri = makeURI(aUri); } PlacesUtils.favicons.getFaviconURLForPage(aUri, this._gotIcon.bind(this, aItem));
--- a/browser/metro/theme/platform.css +++ b/browser/metro/theme/platform.css @@ -202,21 +202,17 @@ menulist { color: black; } .menu-popup richlistitem:not([disabled]):active { background-color: black; color: white; } -.menu-popup > richlistbox[left-hand="true"] > richlistitem { - padding-left: 50px; -} - -.menu-popup > richlistbox[left-hand="false"] > richlistitem { +.menu-popup > richlistbox > richlistitem { padding-right: 50px; } /* Additional styles applied to popups for form <select> elements. */ #select-container { padding: 0; position: absolute;
--- a/browser/modules/SignInToWebsite.jsm +++ b/browser/modules/SignInToWebsite.jsm @@ -22,38 +22,27 @@ XPCOMUtils.defineLazyModuleGetter(this, function log(...aMessageArgs) { Logger.log.apply(Logger, ["SignInToWebsiteUX"].concat(aMessageArgs)); } this.SignInToWebsiteUX = { init: function SignInToWebsiteUX_init() { - /* - * bug 793906 - temporarily disabling desktop UI so we can - * focus on b2g without worrying about desktop as well - * Services.obs.addObserver(this, "identity-request", false); Services.obs.addObserver(this, "identity-auth", false); Services.obs.addObserver(this, "identity-auth-complete", false); Services.obs.addObserver(this, "identity-login-state-changed", false); - */ }, uninit: function SignInToWebsiteUX_uninit() { - /* - * As above: - * bug 793906 - temporarily disabling desktop UI so we can - * focus on b2g without worrying about desktop as well - * Services.obs.removeObserver(this, "identity-request"); Services.obs.removeObserver(this, "identity-auth"); Services.obs.removeObserver(this, "identity-auth-complete"); Services.obs.removeObserver(this, "identity-login-state-changed"); - */ }, observe: function SignInToWebsiteUX_observe(aSubject, aTopic, aData) { log("observe: received", aTopic, "with", aData, "for", aSubject); let options = null; if (aSubject) { options = aSubject.wrappedJSObject; }
--- a/browser/modules/test/Makefile.in +++ b/browser/modules/test/Makefile.in @@ -2,11 +2,8 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. ifeq ($(MOZ_WIDGET_TOOLKIT),windows) MOCHITEST_BROWSER_FILES += \ browser_taskbar_preview.js \ $(NULL) endif - -# bug 793906 - temporarily disabling desktop UI while working on b2g -# browser_SignInToWebsite.js
--- a/browser/modules/test/browser.ini +++ b/browser/modules/test/browser.ini @@ -1,5 +1,6 @@ [DEFAULT] [browser_NetworkPrioritizer.js] +[browser_SignInToWebsite.js] [browser_UITour.js] support-files = uitour.* \ No newline at end of file
--- a/content/base/src/Element.cpp +++ b/content/base/src/Element.cpp @@ -3114,30 +3114,34 @@ Element::GetMarkup(bool aIncludeSelf, ns nsIDocument* doc = OwnerDoc(); if (IsInHTMLDocument()) { Serialize(this, !aIncludeSelf, aMarkup); return; } nsAutoString contentType; doc->GetContentType(contentType); + bool tryToCacheEncoder = !aIncludeSelf; nsCOMPtr<nsIDocumentEncoder> docEncoder = doc->GetCachedEncoder(); if (!docEncoder) { docEncoder = do_CreateInstance(PromiseFlatCString( nsDependentCString(NS_DOC_ENCODER_CONTRACTID_BASE) + NS_ConvertUTF16toUTF8(contentType) ).get()); } if (!docEncoder) { // This could be some type for which we create a synthetic document. Try // again as XML contentType.AssignLiteral("application/xml"); docEncoder = do_CreateInstance(NS_DOC_ENCODER_CONTRACTID_BASE "application/xml"); + // Don't try to cache the encoder since it would point to a different + // contentType once it has been reinitialized. + tryToCacheEncoder = false; } NS_ENSURE_TRUE_VOID(docEncoder); uint32_t flags = nsIDocumentEncoder::OutputEncodeBasicEntities | // Output DOM-standard newlines nsIDocumentEncoder::OutputLFLineBreak | // Don't do linebreaking that's not present in @@ -3158,17 +3162,17 @@ Element::GetMarkup(bool aIncludeSelf, ns if (aIncludeSelf) { docEncoder->SetNativeNode(this); } else { docEncoder->SetNativeContainerNode(this); } rv = docEncoder->EncodeToString(aMarkup); MOZ_ASSERT(NS_SUCCEEDED(rv)); - if (!aIncludeSelf) { + if (tryToCacheEncoder) { doc->SetCachedEncoder(docEncoder.forget()); } } /** * Fire mutation events for changes caused by parsing directly into a * context node. *
--- a/content/base/src/nsObjectLoadingContent.cpp +++ b/content/base/src/nsObjectLoadingContent.cpp @@ -73,16 +73,17 @@ #include "GeckoProfiler.h" #include "nsObjectFrame.h" #include "nsDOMClassInfo.h" #include "nsWrapperCacheInlines.h" #include "nsDOMJSUtils.h" #include "nsWidgetsCID.h" #include "nsContentCID.h" +#include "mozilla/BasicEvents.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/Telemetry.h" #ifdef XP_WIN // Thanks so much, Microsoft! :( #ifdef CreateEvent #undef CreateEvent #endif
--- a/content/events/src/nsContentEventHandler.cpp +++ b/content/events/src/nsContentEventHandler.cpp @@ -21,16 +21,17 @@ #include "nsTextFrame.h" #include "nsISelectionController.h" #include "nsISelectionPrivate.h" #include "nsContentUtils.h" #include "nsLayoutUtils.h" #include "nsIMEStateManager.h" #include "nsIObjectFrame.h" #include "mozilla/dom/Element.h" +#include "mozilla/TextEvents.h" #include <algorithm> using namespace mozilla; using namespace mozilla::dom; /******************************************************************/ /* nsContentEventHandler */ /******************************************************************/
--- a/content/events/src/nsEventStateManager.cpp +++ b/content/events/src/nsEventStateManager.cpp @@ -9,16 +9,17 @@ #include "mozilla/MathAlgorithms.h" #include "mozilla/MouseEvents.h" #include "mozilla/TextEvents.h" #include "mozilla/TouchEvents.h" #include "mozilla/dom/TabParent.h" #include "nsCOMPtr.h" #include "nsEventStateManager.h" +#include "nsFocusManager.h" #include "nsIMEStateManager.h" #include "nsContentEventHandler.h" #include "nsIContent.h" #include "nsINodeInfo.h" #include "nsIDocument.h" #include "nsIFrame.h" #include "nsIWidget.h" #include "nsPresContext.h" @@ -1950,17 +1951,17 @@ nsEventStateManager::FireContextClick() if (frameSel && frameSel->GetMouseDownState()) { // note that this can cause selection changed events to fire if we're in // a text field, which will null out mCurrentTarget frameSel->SetMouseDownState(false); } } nsIDocument* doc = mGestureDownContent->GetCurrentDoc(); - nsAutoHandlingUserInputStatePusher userInpStatePusher(true, &event, doc); + AutoHandlingUserInputStatePusher userInpStatePusher(true, &event, doc); // dispatch to DOM nsEventDispatcher::Dispatch(mGestureDownContent, mPresContext, &event, nullptr, &status); // We don't need to dispatch to frame handling because no frames // watch NS_CONTEXTMENU except for nsMenuFrame and that's only for // dismissal. That's just as well since we don't really know @@ -5843,9 +5844,56 @@ nsEventStateManager::Prefs::GetAccessMod return sChromeAccessModifierMask; case nsIDocShellTreeItem::typeContent: return sContentAccessModifierMask; default: return 0; } } - +/******************************************************************/ +/* mozilla::AutoHandlingUserInputStatePusher */ +/******************************************************************/ + +AutoHandlingUserInputStatePusher::AutoHandlingUserInputStatePusher( + bool aIsHandlingUserInput, + WidgetEvent* aEvent, + nsIDocument* aDocument) : + mIsHandlingUserInput(aIsHandlingUserInput), + mIsMouseDown(aEvent && aEvent->message == NS_MOUSE_BUTTON_DOWN), + mResetFMMouseDownState(false) +{ + if (!aIsHandlingUserInput) { + return; + } + nsEventStateManager::StartHandlingUserInput(); + if (!mIsMouseDown) { + return; + } + nsIPresShell::SetCapturingContent(nullptr, 0); + nsIPresShell::AllowMouseCapture(true); + if (!aDocument || !aEvent->mFlags.mIsTrusted) { + return; + } + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + NS_ENSURE_TRUE_VOID(fm); + fm->SetMouseButtonDownHandlingDocument(aDocument); + mResetFMMouseDownState = true; +} + +AutoHandlingUserInputStatePusher::~AutoHandlingUserInputStatePusher() +{ + if (!mIsHandlingUserInput) { + return; + } + nsEventStateManager::StopHandlingUserInput(); + if (!mIsMouseDown) { + return; + } + nsIPresShell::AllowMouseCapture(false); + if (!mResetFMMouseDownState) { + return; + } + nsFocusManager* fm = nsFocusManager::GetFocusManager(); + NS_ENSURE_TRUE_VOID(fm); + fm->SetMouseButtonDownHandlingDocument(nullptr); +} +
--- a/content/events/src/nsEventStateManager.h +++ b/content/events/src/nsEventStateManager.h @@ -1,30 +1,29 @@ /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsEventStateManager_h__ #define nsEventStateManager_h__ -#include "mozilla/BasicEvents.h" #include "mozilla/EventForwards.h" #include "mozilla/TypedEnum.h" #include "nsIObserver.h" #include "nsWeakReference.h" #include "nsCOMPtr.h" #include "nsCOMArray.h" #include "nsCycleCollectionParticipant.h" -#include "nsFocusManager.h" #include "mozilla/TimeStamp.h" #include "nsIFrame.h" #include "Units.h" +class nsFrameLoader; class nsIContent; class nsIDocument; class nsIDocShell; class nsIDocShellTreeNode; class nsIDocShellTreeItem; class imgIContainer; class nsDOMDataTransfer; class MouseEnterLeaveDispatcher; @@ -842,73 +841,43 @@ public: mozilla::WidgetGUIEvent* aMouseDownEvent); void KillClickHoldTimer(); void FireContextClick(); void SetPointerLock(nsIWidget* aWidget, nsIContent* aElement) ; static void sClickHoldCallback ( nsITimer* aTimer, void* aESM ) ; }; +namespace mozilla { + /** * This class is used while processing real user input. During this time, popups * are allowed. For mousedown events, mouse capturing is also permitted. */ -class nsAutoHandlingUserInputStatePusher +class AutoHandlingUserInputStatePusher { public: - nsAutoHandlingUserInputStatePusher(bool aIsHandlingUserInput, - mozilla::WidgetEvent* aEvent, - nsIDocument* aDocument) - : mIsHandlingUserInput(aIsHandlingUserInput), - mIsMouseDown(aEvent && aEvent->message == NS_MOUSE_BUTTON_DOWN), - mResetFMMouseDownState(false) - { - if (aIsHandlingUserInput) { - nsEventStateManager::StartHandlingUserInput(); - if (mIsMouseDown) { - nsIPresShell::SetCapturingContent(nullptr, 0); - nsIPresShell::AllowMouseCapture(true); - if (aDocument && aEvent->mFlags.mIsTrusted) { - nsFocusManager* fm = nsFocusManager::GetFocusManager(); - if (fm) { - fm->SetMouseButtonDownHandlingDocument(aDocument); - mResetFMMouseDownState = true; - } - } - } - } - } - - ~nsAutoHandlingUserInputStatePusher() - { - if (mIsHandlingUserInput) { - nsEventStateManager::StopHandlingUserInput(); - if (mIsMouseDown) { - nsIPresShell::AllowMouseCapture(false); - if (mResetFMMouseDownState) { - nsFocusManager* fm = nsFocusManager::GetFocusManager(); - if (fm) { - fm->SetMouseButtonDownHandlingDocument(nullptr); - } - } - } - } - } + AutoHandlingUserInputStatePusher(bool aIsHandlingUserInput, + WidgetEvent* aEvent, + nsIDocument* aDocument); + ~AutoHandlingUserInputStatePusher(); protected: bool mIsHandlingUserInput; bool mIsMouseDown; bool mResetFMMouseDownState; private: // Hide so that this class can only be stack-allocated static void* operator new(size_t /*size*/) CPP_THROW_NEW { return nullptr; } static void operator delete(void* /*memory*/) {} }; +} // namespace mozilla + // Click and double-click events need to be handled even for content that // has no frame. This is required for Web compatibility. #define NS_EVENT_NEEDS_FRAME(event) \ (!(event)->HasPluginActivationEventMessage() && \ (event)->message != NS_MOUSE_CLICK && \ (event)->message != NS_MOUSE_DOUBLECLICK) #endif // nsEventStateManager_h__
--- a/content/events/src/nsPrivateTextRange.h +++ b/content/events/src/nsPrivateTextRange.h @@ -5,17 +5,17 @@ #ifndef nsPrivateTextRange_h__ #define nsPrivateTextRange_h__ #include "nsIPrivateTextRange.h" #include "nsTArray.h" #include "nsAutoPtr.h" #include "mozilla/Attributes.h" -#include "mozilla/TextEvents.h" +#include "mozilla/TextRange.h" class nsPrivateTextRange MOZ_FINAL : public nsIPrivateTextRange { NS_DECL_ISUPPORTS public: nsPrivateTextRange(const mozilla::TextRange &aTextRange); virtual ~nsPrivateTextRange(void);
--- a/content/html/content/src/HTMLAudioElement.cpp +++ b/content/html/content/src/HTMLAudioElement.cpp @@ -12,33 +12,28 @@ #include "nsGkAtoms.h" #include "nsIDocument.h" #include "jsfriendapi.h" #include "nsContentUtils.h" #include "nsJSUtils.h" #include "AudioSampleFormat.h" #include "AudioChannelCommon.h" #include <algorithm> -#include "mozilla/Preferences.h" #include "nsComponentManagerUtils.h" #include "nsIHttpChannel.h" #include "mozilla/dom/TimeRanges.h" #include "AudioStream.h" -static bool -IsAudioAPIEnabled() -{ - return mozilla::Preferences::GetBool("media.audio_data.enabled", true); -} - NS_IMPL_NS_NEW_HTML_ELEMENT(Audio) namespace mozilla { namespace dom { +extern bool IsAudioAPIEnabled(); + NS_IMPL_ISUPPORTS_INHERITED4(HTMLAudioElement, HTMLMediaElement, nsIDOMHTMLMediaElement, nsIDOMHTMLAudioElement, nsITimerCallback, nsIAudioChannelAgentCallback) NS_IMPL_ELEMENT_CLONE(HTMLAudioElement) HTMLAudioElement::HTMLAudioElement(already_AddRefed<nsINodeInfo> aNodeInfo)
--- a/content/html/content/src/HTMLFormElement.cpp +++ b/content/html/content/src/HTMLFormElement.cpp @@ -802,19 +802,19 @@ HTMLFormElement::SubmitSubmission(nsForm // // Submit // nsCOMPtr<nsIDocShell> docShell; { nsAutoPopupStatePusher popupStatePusher(mSubmitPopupState); - nsAutoHandlingUserInputStatePusher userInpStatePusher( - mSubmitInitiatedFromUserInput, - nullptr, doc); + AutoHandlingUserInputStatePusher userInpStatePusher( + mSubmitInitiatedFromUserInput, + nullptr, doc); nsCOMPtr<nsIInputStream> postDataStream; rv = aFormSubmission->GetEncodedSubmission(actionURI, getter_AddRefs(postDataStream)); NS_ENSURE_SUBMIT_SUCCESS(rv); rv = linkHandler->OnLinkClickSync(this, actionURI, target.get(),
--- a/content/html/content/src/HTMLMediaElement.cpp +++ b/content/html/content/src/HTMLMediaElement.cpp @@ -987,16 +987,22 @@ static bool IsAutoplayEnabled() return Preferences::GetBool("media.autoplay.enabled"); } static bool UseAudioChannelService() { return Preferences::GetBool("media.useAudioChannelService"); } +// Not static because it's used in HTMLAudioElement. +bool IsAudioAPIEnabled() +{ + return mozilla::Preferences::GetBool("media.audio_data.enabled", false); +} + void HTMLMediaElement::UpdatePreloadAction() { PreloadAction nextAction = PRELOAD_UNDEFINED; // If autoplay is set, or we're playing, we should always preload data, // as we'll need it to play. if ((IsAutoplayEnabled() && HasAttr(kNameSpaceID_None, nsGkAtoms::autoplay)) || !mPaused) { @@ -1532,16 +1538,21 @@ NS_IMETHODIMP HTMLMediaElement::SetVolum ErrorResult rv; SetVolume(aVolume, rv); return rv.ErrorCode(); } uint32_t HTMLMediaElement::GetMozChannels(ErrorResult& aRv) const { + if (!IsAudioAPIEnabled()) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return 0; + } + if (!mDecoder && !mAudioStream) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return 0; } return mChannels; } @@ -1551,16 +1562,21 @@ HTMLMediaElement::GetMozChannels(uint32_ ErrorResult rv; *aMozChannels = GetMozChannels(rv); return rv.ErrorCode(); } uint32_t HTMLMediaElement::GetMozSampleRate(ErrorResult& aRv) const { + if (!IsAudioAPIEnabled()) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return 0; + } + if (!mDecoder && !mAudioStream) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return 0; } return mRate; } @@ -1642,16 +1658,21 @@ HTMLMediaElement::MozGetMetadata(JSConte } return rv.ErrorCode(); } uint32_t HTMLMediaElement::GetMozFrameBufferLength(ErrorResult& aRv) const { + if (!IsAudioAPIEnabled()) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return 0; + } + // The framebuffer (via MozAudioAvailable events) is only available // when reading vs. writing audio directly. if (!mDecoder) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return 0; } return mDecoder->GetFrameBufferLength(); @@ -1663,16 +1684,21 @@ HTMLMediaElement::GetMozFrameBufferLengt ErrorResult rv; *aMozFrameBufferLength = GetMozFrameBufferLength(rv); return rv.ErrorCode(); } void HTMLMediaElement::SetMozFrameBufferLength(uint32_t aMozFrameBufferLength, ErrorResult& aRv) { + if (!IsAudioAPIEnabled()) { + aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR); + return; + } + if (!mDecoder) { aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); return; } aRv = mDecoder->RequestFrameBufferLength(aMozFrameBufferLength); } @@ -3185,16 +3211,20 @@ nsresult HTMLMediaElement::DispatchAudio float aTime) { // Auto manage the memory for the frame buffer. If we fail and return // an error, this ensures we free the memory in the frame buffer. Otherwise // we hand off ownership of the frame buffer to the audioavailable event, // which frees the memory when it's destroyed. nsAutoArrayPtr<float> frameBuffer(aFrameBuffer); + if (!IsAudioAPIEnabled()) { + return NS_ERROR_DOM_NOT_SUPPORTED_ERR; + } + nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(OwnerDoc()); nsRefPtr<HTMLMediaElement> kungFuDeathGrip = this; NS_ENSURE_TRUE(domDoc, NS_ERROR_INVALID_ARG); nsCOMPtr<nsIDOMEvent> event; nsresult rv = domDoc->CreateEvent(NS_LITERAL_STRING("MozAudioAvailableEvent"), getter_AddRefs(event)); nsCOMPtr<nsIDOMNotifyAudioAvailableEvent> audioavailableEvent(do_QueryInterface(event));
--- a/content/media/ogg/OggCodecState.cpp +++ b/content/media/ogg/OggCodecState.cpp @@ -57,17 +57,17 @@ static int64_t LEInt64(const unsigned ch // Reads a little-endian encoded unsigned 16bit integer at p. static uint16_t LEUint16(const unsigned char* p) { return p[0] + (p[1] << 8); } // Reads a little-endian encoded signed 16bit integer at p. -static int16_t LEInt16(const unsigned char* p) +inline int16_t LEInt16(const unsigned char* p) { return static_cast<int16_t>(LEUint16(p)); } /** Decoder base class for Ogg-encapsulated streams. */ OggCodecState* OggCodecState::Create(ogg_page* aPage) {
--- a/content/media/ogg/OggReader.cpp +++ b/content/media/ogg/OggReader.cpp @@ -54,17 +54,19 @@ extern PRLogModuleInfo* gMediaDecoderLog // lands between the seek target and SEEK_FUZZ_USECS microseconds before the // seek target. This is becaue it's usually quicker to just keep downloading // from an exisiting connection than to do another bisection inside that // small range, which would open a new HTTP connetion. static const uint32_t SEEK_FUZZ_USECS = 500000; // The number of microseconds of "pre-roll" we use for Opus streams. // The specification recommends 80 ms. +#ifdef MOZ_OPUS static const int64_t SEEK_OPUS_PREROLL = 80 * USECS_PER_MS; +#endif /* MOZ_OPUS */ enum PageSyncResult { PAGE_SYNC_ERROR = 1, PAGE_SYNC_END_OF_RANGE= 2, PAGE_SYNC_OK = 3 }; // Reads a page from the media resource. @@ -81,18 +83,20 @@ PageSync(MediaResource* aResource, // is about 4300 bytes, so we read the file in chunks larger than that. static const int PAGE_STEP = 8192; OggReader::OggReader(AbstractMediaDecoder* aDecoder) : MediaDecoderReader(aDecoder), mMonitor("OggReader"), mTheoraState(nullptr), mVorbisState(nullptr), +#ifdef MOZ_OPUS mOpusState(nullptr), mOpusEnabled(MediaDecoder::IsOpusEnabled()), +#endif /* MOZ_OPUS */ mSkeletonState(nullptr), mVorbisSerial(0), mOpusSerial(0), mTheoraSerial(0), mOpusPreSkip(0), mIsChained(false), mDecodedAudioFrames(0) { @@ -126,19 +130,21 @@ nsresult OggReader::ResetDecode(bool sta res = NS_ERROR_FAILURE; } // Discard any previously buffered packets/pages. ogg_sync_reset(&mOggState); if (mVorbisState && NS_FAILED(mVorbisState->Reset())) { res = NS_ERROR_FAILURE; } +#ifdef MOZ_OPUS if (mOpusState && NS_FAILED(mOpusState->Reset(start))) { res = NS_ERROR_FAILURE; } +#endif /* MOZ_OPUS */ if (mTheoraState && NS_FAILED(mTheoraState->Reset())) { res = NS_ERROR_FAILURE; } return res; } bool OggReader::ReadHeaders(OggCodecState* aState) @@ -157,18 +163,20 @@ bool OggReader::ReadHeaders(OggCodecStat void OggReader::BuildSerialList(nsTArray<uint32_t>& aTracks) { if (HasVideo()) { aTracks.AppendElement(mTheoraState->mSerial); } if (HasAudio()) { if (mVorbisState) { aTracks.AppendElement(mVorbisState->mSerial); - } else if(mOpusState) { +#ifdef MOZ_OPUS + } else if (mOpusState) { aTracks.AppendElement(mOpusState->mSerial); +#endif /* MOZ_OPUS */ } } } nsresult OggReader::ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags) { NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); @@ -215,27 +223,29 @@ nsresult OggReader::ReadMetadata(MediaIn if (codecState && codecState->GetType() == OggCodecState::TYPE_THEORA && !mTheoraState) { // First Theora bitstream, we'll play this one. Subsequent Theora // bitstreams will be ignored. mTheoraState = static_cast<TheoraState*>(codecState); } +#ifdef MOZ_OPUS if (codecState && codecState->GetType() == OggCodecState::TYPE_OPUS && !mOpusState) { if (mOpusEnabled) { mOpusState = static_cast<OpusState*>(codecState); } else { NS_WARNING("Opus decoding disabled." " See media.opus.enabled in about:config"); } } +#endif /* MOZ_OPUS */ if (codecState && codecState->GetType() == OggCodecState::TYPE_SKELETON && !mSkeletonState) { mSkeletonState = static_cast<SkeletonState*>(codecState); } } @@ -249,17 +259,20 @@ nsresult OggReader::ReadMetadata(MediaIn // We've read all BOS pages, so we know the streams contained in the media. // Now process all available header packets in the active Theora, Vorbis and // Skeleton streams. // Deactivate any non-primary bitstreams. for (uint32_t i = 0; i < bitstreams.Length(); i++) { OggCodecState* s = bitstreams[i]; - if (s != mVorbisState && s != mOpusState && + if (s != mVorbisState && +#ifdef MOZ_OPUS + s != mOpusState && +#endif /* MOZ_OPUS */ s != mTheoraState && s != mSkeletonState) { s->Deactivate(); } } if (mTheoraState && ReadHeaders(mTheoraState)) { nsIntRect picture = nsIntRect(mTheoraState->mInfo.pic_x, mTheoraState->mInfo.pic_y, @@ -600,26 +613,32 @@ void OggReader::DownmixToStereo(nsAutoAr #endif channels = out_channels; buffer = dBuffer; } bool OggReader::DecodeAudioData() { NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); - NS_ASSERTION(mVorbisState != nullptr || mOpusState != nullptr, - "Need audio codec state to decode audio"); + DebugOnly<bool> haveCodecState = mVorbisState != nullptr +#ifdef MOZ_OPUS + || mOpusState != nullptr +#endif /* MOZ_OPUS */ + ; + NS_ASSERTION(haveCodecState, "Need audio codec state to decode audio"); // Read the next data packet. Skip any non-data packets we encounter. ogg_packet* packet = 0; OggCodecState* codecState; if (mVorbisState) codecState = static_cast<OggCodecState*>(mVorbisState); +#ifdef MOZ_OPUS else codecState = static_cast<OggCodecState*>(mOpusState); +#endif /* MOZ_OPUS */ do { if (packet) { OggCodecState::ReleasePacket(packet); } packet = NextOggPacket(codecState); } while (packet && codecState->IsHeader(packet)); if (!packet) { @@ -656,17 +675,19 @@ void OggReader::SetChained(bool aIsChain ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); mDecoder->SetMediaSeekable(false); } } bool OggReader::ReadOggChain() { bool chained = false; +#ifdef MOZ_OPUS OpusState* newOpusState = nullptr; +#endif /* MOZ_OPUS */ VorbisState* newVorbisState = nullptr; int channels = 0; long rate = 0; MetadataTags* tags = nullptr; if (HasVideo() || HasSkeleton() || !HasAudio()) { return false; } @@ -1335,20 +1356,22 @@ nsresult OggReader::SeekInUnbuffered(int // don't do this offsetting when seeking in a buffered range, // as the extra decoding causes a noticeable speed hit when all the data // is buffered (compared to just doing a bisection to exactly find the // keyframe). int64_t keyframeOffsetMs = 0; if (HasVideo() && mTheoraState) { keyframeOffsetMs = mTheoraState->MaxKeyframeOffset(); } +#ifdef MOZ_OPUS // Add in the Opus pre-roll if necessary, as well. if (HasAudio() && mOpusState) { keyframeOffsetMs = std::max(keyframeOffsetMs, SEEK_OPUS_PREROLL); } +#endif /* MOZ_OPUS */ int64_t seekTarget = std::max(aStartTime, aTarget - keyframeOffsetMs); // Minimize the bisection search space using the known timestamps from the // buffered ranges. SeekRange k = SelectSeekRange(aRanges, seekTarget, aStartTime, aEndTime, false); return SeekBisection(seekTarget, k, SEEK_FUZZ_USECS); } nsresult OggReader::Seek(int64_t aTarget, @@ -1359,19 +1382,21 @@ nsresult OggReader::Seek(int64_t aTarget NS_ASSERTION(mDecoder->OnDecodeThread(), "Should be on decode thread."); if (mIsChained) return NS_ERROR_FAILURE; LOG(PR_LOG_DEBUG, ("%p About to seek to %lld", mDecoder, aTarget)); nsresult res; MediaResource* resource = mDecoder->GetResource(); NS_ENSURE_TRUE(resource != nullptr, NS_ERROR_FAILURE); int64_t adjustedTarget = aTarget; +#ifdef MOZ_OPUS if (HasAudio() && mOpusState){ adjustedTarget = std::max(aStartTime, aTarget - SEEK_OPUS_PREROLL); } +#endif /* MOZ_OPUS */ if (adjustedTarget == aStartTime) { // We've seeked to the media start. Just seek to the offset of the first // content page. res = resource->Seek(nsISeekableStream::NS_SEEK_SET, 0); NS_ENSURE_SUCCESS(res,res); res = ResetDecode(true); @@ -1817,20 +1842,22 @@ nsresult OggReader::GetBuffered(TimeRang continue; } uint32_t serial = ogg_page_serialno(&page); if (mVorbisState && serial == mVorbisSerial) { startTime = VorbisState::Time(&mVorbisInfo, granulepos); NS_ASSERTION(startTime > 0, "Must have positive start time"); } +#ifdef MOZ_OPUS else if (mOpusState && serial == mOpusSerial) { startTime = OpusState::Time(mOpusPreSkip, granulepos); NS_ASSERTION(startTime > 0, "Must have positive start time"); } +#endif /* MOZ_OPUS */ else if (mTheoraState && serial == mTheoraSerial) { startTime = TheoraState::Time(&mTheoraInfo, granulepos); NS_ASSERTION(startTime > 0, "Must have positive start time"); } else if (mCodecStore.Contains(serial)) { // Stream is not the theora or vorbis stream we're playing, // but is one that we have header data for. startOffset += page.header_len + page.body_len;
--- a/content/media/ogg/OggReader.h +++ b/content/media/ogg/OggReader.h @@ -57,18 +57,21 @@ public: // If the Theora granulepos has not been captured, it may read several packets // until one with a granulepos has been captured, to ensure that all packets // read have valid time info. virtual bool DecodeVideoFrame(bool &aKeyframeSkip, int64_t aTimeThreshold); virtual bool HasAudio() { - return (mVorbisState != 0 && mVorbisState->mActive) || - (mOpusState != 0 && mOpusState->mActive); + return (mVorbisState != 0 && mVorbisState->mActive) +#ifdef MOZ_OPUS + || (mOpusState != 0 && mOpusState->mActive) +#endif /* MOZ_OPUS */ + ; } virtual bool HasVideo() { return mTheoraState != 0 && mTheoraState->mActive; } virtual nsresult ReadMetadata(MediaInfo* aInfo, MetadataTags** aTags); @@ -263,23 +266,25 @@ private: OggCodecStore mCodecStore; // Decode state of the Theora bitstream we're decoding, if we have video. TheoraState* mTheoraState; // Decode state of the Vorbis bitstream we're decoding, if we have audio. VorbisState* mVorbisState; +#ifdef MOZ_OPUS // Decode state of the Opus bitstream we're decoding, if we have one. OpusState *mOpusState; // Represents the user pref media.opus.enabled at the time our // contructor was called. We can't check it dynamically because // we're not on the main thread; bool mOpusEnabled; +#endif /* MOZ_OPUS */ // Decode state of the Skeleton bitstream. SkeletonState* mSkeletonState; // Ogg decoding state. ogg_sync_state mOggState; // Vorbis/Opus/Theora data used to compute timestamps. This is written on the
--- a/content/media/test/test_a4_tone.html +++ b/content/media/test/test_a4_tone.html @@ -10,19 +10,16 @@ https://bugzilla.mozilla.org/show_bug.cg <head> <title>Media test: simple audioAvalailable event checks</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=490705">Mozilla Bug 490705</a> -<!-- mute audio, since there is no need to hear the sound for these tests --> -<audio id='a1' controls></audio> - <pre id="test"> <script class="testbody" type="text/javascript"> /** * FFT is a class for calculating the Discrete Fourier Transform of a signal * with the Fast Fourier Transform algorithm. * * Source: github.com/corbanbrook/dsp.js; License: MIT; Copyright: Corban Brook @@ -238,27 +235,29 @@ function checkResults() { SimpleTest.finish(); } function audioEnded() { checkResults(); } function initTest() { - var a1 = document.getElementById('a1'); + var a1 = document.createElement("audio"); + a1.id = "a1"; a1.addEventListener("ended", audioEnded, false); a1.addEventListener("loadedmetadata", loadedMetadata, false); a1.addEventListener("MozAudioAvailable", audioAvailable, false); a1.src = testFile; a1.muted = true; a1.play(); + document.body.appendChild(a1); } window.addEventListener("load", function(e) { - initTest(); + SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]}, initTest); }, false); SimpleTest.waitForExplicitFinish(); </script> </pre> </body> </html>
--- a/content/media/test/test_audio_event_adopt.html +++ b/content/media/test/test_audio_event_adopt.html @@ -19,19 +19,21 @@ https://bugzilla.mozilla.org/show_bug.cg return document.adoptNode(resultNode).checked; } function endTest() { is(wasAudioAvailableCalled(), true, "audioAvailable was not called"); SimpleTest.finish(); } function startTest() { - var audio = adopt(); - audio.addEventListener("ended", endTest, false); - audio.play(); + SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]}, function () { + var audio = adopt(); + audio.addEventListener("ended", endTest, false); + audio.play(); + }); } SimpleTest.waitForExplicitFinish(); </script> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=490705">Mozilla Bug 490705</a>
--- a/content/media/test/test_audiowrite.html +++ b/content/media/test/test_audiowrite.html @@ -63,17 +63,17 @@ function runTests() { } catch(e) { writeArgsOK = true; } ok(writeArgsOK, "mozWriteAudio args test failed."); SimpleTest.finish(); } window.addEventListener("load", function(e) { - runTests(); + SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]}, runTests); }, false); SimpleTest.waitForExplicitFinish(); </script> </pre> </body> </html>
--- a/content/media/test/test_bug686137.html +++ b/content/media/test/test_bug686137.html @@ -7,37 +7,41 @@ https://bugzilla.mozilla.org/show_bug.cg <head> <title>Media test: changing mozFrameBufferLength</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=686137">Mozilla Bug 686137</a> -<audio id="a1" controls muted preload="metadata"></audio> - <pre id="test"> <script class="testbody" type="text/javascript"> -var testFile = "bug495794.ogg"; -var a1 = document.getElementById('a1'); + +SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]}, function () { + var a1 = document.createElement("audio"); + a1.controls = true; + a1.muted = true; + a1.preload = "metadata"; + a1.addEventListener("loadedmetadata", metaDataLoaded, false); + a1.src = "bug495794.ogg"; + a1.load(); +}); +SimpleTest.waitForExplicitFinish(); function audioAvailable(event) { + var a1 = event.target; a1.removeEventListener("MozAudioAvailable", audioAvailable); is( event.frameBuffer.length, 9001, "event.frameBuffer.length should be 9001."); is( event.frameBuffer.length, a1.mozFrameBufferLength, "event.frameBuffer.length should be " + a1.mozFrameBufferLength + "."); SimpleTest.finish(); } -function metaDataLoaded(){ +function metaDataLoaded(event){ + var a1 = event.target; a1.addEventListener("MozAudioAvailable", audioAvailable, false); a1.mozFrameBufferLength = 9001; a1.play(); } -a1.addEventListener("loadedmetadata", metaDataLoaded, false); -a1.src = testFile; -a1.load(); -SimpleTest.waitForExplicitFinish(); - </script> </pre> </body> </html>
--- a/content/media/test/test_framebuffer.html +++ b/content/media/test/test_framebuffer.html @@ -7,19 +7,16 @@ https://bugzilla.mozilla.org/show_bug.cg <head> <title>Media test: framebuffer size checks</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=490705">Mozilla Bug 490705</a> -<!-- mute audio, since there is no need to hear the sound for these tests --> -<audio id="a1" preload="metadata" controls></audio> - <pre id="test"> <script class="testbody" type="text/javascript"> var testFile = "bug495794.ogg"; var testFileDuration = 0.30; var testFileChannelCount = 2; var testFileSampleRate = 48000; var testFileFrameBufferLength = testFileChannelCount * 1024; @@ -79,27 +76,32 @@ function checkResults() { SimpleTest.finish(); } function audioEnded() { checkResults(); } function initTest() { - var a1 = document.getElementById('a1'); + var a1 = document.createElement("audio"); + a1.id = "a1"; + a1.preload = "metadata"; + a1.controls = true; + document.body.appendChild(a1); + a1.addEventListener("ended", audioEnded, false); a1.addEventListener("loadedmetadata", loadedMetadata, false); a1.addEventListener("MozAudioAvailable", audioAvailable, false); a1.src = testFile; a1.muted = true; a1.play(); } window.addEventListener("load", function(e) { - initTest(); + SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]}, initTest); }, false); SimpleTest.waitForExplicitFinish(); </script> </pre> </body> </html>
--- a/content/media/test/test_wave_data_s16.html +++ b/content/media/test/test_wave_data_s16.html @@ -29,19 +29,22 @@ function audioavailable(e) { // Only care about the first few samples SimpleTest.finish(); } function startTest() { if (completed) return; - var v = document.getElementById('v'); - v.addEventListener('MozAudioAvailable', audioavailable, false); - v.play(); + SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]}, + function () { + var v = document.getElementById('v'); + v.addEventListener('MozAudioAvailable', audioavailable, false); + v.play(); + }); } SimpleTest.waitForExplicitFinish(); </script> </pre> <audio id='v' preload="metadata" onloadedmetadata='return startTest();'>
--- a/content/media/test/test_wave_data_u8.html +++ b/content/media/test/test_wave_data_u8.html @@ -29,19 +29,22 @@ function audioavailable(e) { // Only care about the first few samples SimpleTest.finish(); } function startTest() { if (completed) return; - var v = document.getElementById('v'); - v.addEventListener('MozAudioAvailable', audioavailable, false); - v.play(); + SpecialPowers.pushPrefEnv({"set": [["media.audio_data.enabled", true]]}, + function () { + var v = document.getElementById('v'); + v.addEventListener('MozAudioAvailable', audioavailable, false); + v.play(); + }); } SimpleTest.waitForExplicitFinish(); </script> </pre> <audio id='v' preload="metadata" onloadedmetadata='return startTest();'>
--- a/content/media/webaudio/test/mochitest.ini +++ b/content/media/webaudio/test/mochitest.ini @@ -106,8 +106,9 @@ support-files = [test_scriptProcessorNodeChannelCount.html] [test_scriptProcessorNodeZeroInputOutput.html] [test_singleSourceDest.html] [test_waveShaper.html] [test_waveShaperNoCurve.html] [test_waveShaperZeroLengthCurve.html] [test_audioDestinationNode.html] [test_mozaudiochannel.html] +[test_waveDecoder.html]
new file mode 100644 --- /dev/null +++ b/content/media/webaudio/test/test_waveDecoder.html @@ -0,0 +1,69 @@ +<!DOCTYPE HTML> +<html> +<meta charset=utf-8> +<head> + <title>Test that we decode uint8 and sint16 wave files with correct conversion to float64</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +<pre id="test"> +<script class="testbody" type="text/javascript"> +var testsDone = 0; +var tests = ["UklGRjUrAABXQVZFZm10IBAAAAABAAEAESsAABErAAABAAgAZGF0YQMAAAD/AIA=", + "UklGRkZWAABXQVZFZm10IBAAAAABAAEAESsAACJWAAACABAAZGF0YQYAAAD/fwCAAAA="]; + +SimpleTest.waitForExplicitFinish(); + +function base64ToUint8Buffer(b64) { + var str = atob(b64) + var u8 = new Uint8Array(str.length); + for (var i = 0; i < str.length; ++i) { + u8[i] = str.charCodeAt(i); + } + return u8; +} + +function fixupBufferSampleRate(u8, rate) { + u8[24] = (rate & 0x000000ff) >> 0; + u8[25] = (rate & 0x0000ff00) >> 8; + u8[26] = (rate & 0x00ff0000) >> 16; + u8[27] = (rate & 0xff000000) >> 24; +} + +function finishTest() { + testsDone += 1; + if (testsDone == tests.length) { + SimpleTest.finish(); + } +} + +function decodeComplete(b) { + ok(true, "Decoding succeeded."); + is(b.numberOfChannels, 1, "Should have 1 channel."); + is(b.length, 3, "Should have three samples."); + var samples = b.getChannelData(0); + ok(samples[0] > 0.99 && samples[0] < 1.01, "Check near 1.0. Got " + samples[0]); + ok(samples[1] > -1.01 && samples[1] < -0.99, "Check near -1.0. Got " + samples[1]); + ok(samples[2] > -0.01 && samples[2] < 0.01, "Check near 0.0. Got " + samples[2]); + finishTest(); +} + +function decodeFailed() { + ok(false, "Decoding failed."); + finishTest(); +} + +addLoadEvent(function() { + var context = new AudioContext(); + + for (var i = 0; i < tests.length; ++i) { + var u8 = base64ToUint8Buffer(tests[i]); + fixupBufferSampleRate(u8, context.sampleRate); + context.decodeAudioData(u8.buffer, decodeComplete, decodeFailed); + } +}); +</script> +</pre> +</body> +</html>
--- a/dom/apps/src/InterAppComm.cpp +++ b/dom/apps/src/InterAppComm.cpp @@ -7,17 +7,17 @@ #include "nsPIDOMWindow.h" #include "nsJSPrincipals.h" #include "mozilla/Preferences.h" #include "AccessCheck.h" using namespace mozilla::dom; /* static */ bool -InterAppComm::EnabledForScope(JSContext* /* unused */, JSObject* aObj) +InterAppComm::EnabledForScope(JSContext* /* unused */, JS::Handle<JSObject*> aObj) { // Disable the constructors if they're disabled by the preference for sure. if (!Preferences::GetBool("dom.inter-app-communication-api.enabled", false)) { return false; } // Only expose the constructors to the chrome codes for Gecko internal uses. // The content pages shouldn't be aware of the constructors.
--- a/dom/apps/src/InterAppComm.h +++ b/dom/apps/src/InterAppComm.h @@ -12,15 +12,15 @@ struct JSContext; class JSObject; namespace mozilla { namespace dom { class InterAppComm { public: - static bool EnabledForScope(JSContext* /* unused */, JSObject* aObj); + static bool EnabledForScope(JSContext* /* unused */, JS::Handle<JSObject*> aObj); }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_apps_InterAppComm_h
--- a/dom/base/CompositionStringSynthesizer.h +++ b/dom/base/CompositionStringSynthesizer.h @@ -6,17 +6,17 @@ #ifndef mozilla_dom_compositionstringsynthesizer_h__ #define mozilla_dom_compositionstringsynthesizer_h__ #include "nsICompositionStringSynthesizer.h" #include "nsString.h" #include "nsTArray.h" #include "nsWeakReference.h" #include "mozilla/Attributes.h" -#include "mozilla/TextEvents.h" +#include "mozilla/TextRange.h" class nsIWidget; class nsPIDOMWindow; namespace mozilla { namespace dom { class CompositionStringSynthesizer MOZ_FINAL :
--- a/dom/base/nsWindowMemoryReporter.cpp +++ b/dom/base/nsWindowMemoryReporter.cpp @@ -3,16 +3,17 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "amIAddonManager.h" #include "nsWindowMemoryReporter.h" #include "nsGlobalWindow.h" #include "nsIDocument.h" +#include "nsIDOMWindowCollection.h" #include "nsIEffectiveTLDService.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/Preferences.h" #include "mozilla/Services.h" #include "mozilla/StaticPtr.h" #include "nsNetCID.h" #include "nsPrintfCString.h" #include "XPCJSMemoryReporter.h" @@ -26,24 +27,80 @@ StaticRefPtr<nsWindowMemoryReporter> sWi nsWindowMemoryReporter::nsWindowMemoryReporter() : mCheckForGhostWindowsCallbackPending(false) { } NS_IMPL_ISUPPORTS3(nsWindowMemoryReporter, nsIMemoryReporter, nsIObserver, nsSupportsWeakReference) -/* static */ -void +static nsresult +AddNonJSSizeOfWindowAndItsDescendents(nsGlobalWindow* aWindow, + nsTabSizes* aSizes) +{ + // Measure the window. + nsWindowSizes windowSizes(moz_malloc_size_of); + aWindow->AddSizeOfIncludingThis(&windowSizes); + windowSizes.addToTabSizes(aSizes); + + // Measure the inner window, if there is one. + nsWindowSizes innerWindowSizes(moz_malloc_size_of); + nsGlobalWindow* inner = aWindow->GetCurrentInnerWindowInternal(); + if (inner) { + inner->AddSizeOfIncludingThis(&innerWindowSizes); + innerWindowSizes.addToTabSizes(aSizes); + } + + nsCOMPtr<nsIDOMWindowCollection> frames; + nsresult rv = aWindow->GetFrames(getter_AddRefs(frames)); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t length; + rv = frames->GetLength(&length); + NS_ENSURE_SUCCESS(rv, rv); + + // Measure this window's descendents. + for (uint32_t i = 0; i < length; i++) { + nsCOMPtr<nsIDOMWindow> child; + rv = frames->Item(i, getter_AddRefs(child)); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_STATE(child); + + nsGlobalWindow* childWin = + static_cast<nsGlobalWindow*>(static_cast<nsIDOMWindow *>(child.get())); + + rv = AddNonJSSizeOfWindowAndItsDescendents(childWin, aSizes); + NS_ENSURE_SUCCESS(rv, rv); + } + return NS_OK; +} + +static nsresult +NonJSSizeOfTab(nsPIDOMWindow* aWindow, size_t* aDomSize, size_t* aStyleSize, size_t* aOtherSize) +{ + nsGlobalWindow* window = static_cast<nsGlobalWindow*>(aWindow); + + nsTabSizes sizes; + nsresult rv = AddNonJSSizeOfWindowAndItsDescendents(window, &sizes); + NS_ENSURE_SUCCESS(rv, rv); + + *aDomSize = sizes.mDom; + *aStyleSize = sizes.mStyle; + *aOtherSize = sizes.mOther; + return NS_OK; +} + +/* static */ void nsWindowMemoryReporter::Init() { MOZ_ASSERT(!sWindowReporter); sWindowReporter = new nsWindowMemoryReporter(); ClearOnShutdown(&sWindowReporter); NS_RegisterMemoryReporter(sWindowReporter); + RegisterNonJSSizeOfTab(NonJSSizeOfTab); nsCOMPtr<nsIObserverService> os = services::GetObserverService(); if (os) { // DOM_WINDOW_DESTROYED_TOPIC announces what we call window "detachment", // when a window's docshell is set to NULL. os->AddObserver(sWindowReporter, DOM_WINDOW_DESTROYED_TOPIC, /* weakRef = */ true); os->AddObserver(sWindowReporter, "after-minimize-memory-usage",
--- a/dom/base/nsWindowMemoryReporter.h +++ b/dom/base/nsWindowMemoryReporter.h @@ -7,48 +7,69 @@ #ifndef nsWindowMemoryReporter_h__ #define nsWindowMemoryReporter_h__ #include "nsIMemoryReporter.h" #include "nsIObserver.h" #include "nsDataHashtable.h" #include "nsWeakReference.h" #include "nsAutoPtr.h" +#include "mozilla/Attributes.h" +#include "mozilla/Assertions.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/PodOperations.h" #include "mozilla/TimeStamp.h" #include "nsArenaMemoryStats.h" -#include "mozilla/Attributes.h" // This should be used for any nsINode sub-class that has fields of its own // that it needs to measure; any sub-class that doesn't use it will inherit // SizeOfExcludingThis from its super-class. SizeOfIncludingThis() need not be // defined, it is inherited from nsINode. #define NS_DECL_SIZEOF_EXCLUDING_THIS \ virtual size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; class nsWindowSizes { +#define FOR_EACH_SIZE(macro) \ + macro(DOM, mDOMElementNodes) \ + macro(DOM, mDOMTextNodes) \ + macro(DOM, mDOMCDATANodes) \ + macro(DOM, mDOMCommentNodes) \ + macro(DOM, mDOMEventTargets) \ + macro(DOM, mDOMOther) \ + macro(Style, mStyleSheets) \ + macro(Other, mLayoutPresShell) \ + macro(Style, mLayoutStyleSets) \ + macro(Other, mLayoutTextRuns) \ + macro(Other, mLayoutPresContext) \ + macro(Other, mPropertyTables) \ + public: - nsWindowSizes(mozilla::MallocSizeOf aMallocSizeOf) { - memset(this, 0, sizeof(nsWindowSizes)); - mMallocSizeOf = aMallocSizeOf; + nsWindowSizes(mozilla::MallocSizeOf aMallocSizeOf) + : + #define ZERO_SIZE(kind, mSize) mSize(0), + FOR_EACH_SIZE(ZERO_SIZE) + #undef ZERO_SIZE + mArenaStats(), + mMallocSizeOf(aMallocSizeOf) + {} + + void addToTabSizes(nsTabSizes *sizes) const { + #define ADD_TO_TAB_SIZES(kind, mSize) sizes->add(nsTabSizes::kind, mSize); + FOR_EACH_SIZE(ADD_TO_TAB_SIZES) + #undef ADD_TO_TAB_SIZES + mArenaStats.addToTabSizes(sizes); } - mozilla::MallocSizeOf mMallocSizeOf; + + #define DECL_SIZE(kind, mSize) size_t mSize; + FOR_EACH_SIZE(DECL_SIZE); + #undef DECL_SIZE nsArenaMemoryStats mArenaStats; - size_t mDOMElementNodes; - size_t mDOMTextNodes; - size_t mDOMCDATANodes; - size_t mDOMCommentNodes; - size_t mDOMEventTargets; - size_t mDOMOther; - size_t mStyleSheets; - size_t mLayoutPresShell; - size_t mLayoutStyleSets; - size_t mLayoutTextRuns; - size_t mLayoutPresContext; - size_t mPropertyTables; + mozilla::MallocSizeOf mMallocSizeOf; + +#undef FOR_EACH_SIZE }; /** * nsWindowMemoryReporter is responsible for the 'explicit/window-objects' * memory reporter. * * We classify DOM window objects into one of three categories: *
--- a/dom/ipc/TabParent.h +++ b/dom/ipc/TabParent.h @@ -2,20 +2,20 @@ /* vim: set sw=4 ts=8 et tw=80 : */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef mozilla_tabs_TabParent_h #define mozilla_tabs_TabParent_h +#include "mozilla/EventForwards.h" #include "mozilla/dom/PBrowserParent.h" #include "mozilla/dom/PContentDialogParent.h" #include "mozilla/dom/TabContext.h" -#include "mozilla/TouchEvents.h" #include "nsCOMPtr.h" #include "nsIAuthPromptProvider.h" #include "nsIBrowserDOMWindow.h" #include "nsIDialogParamBlock.h" #include "nsISecureBrowserUI.h" #include "nsITabParent.h" #include "Units.h" #include "js/TypeDecls.h"
--- a/dom/webidl/HTMLMediaElement.webidl +++ b/dom/webidl/HTMLMediaElement.webidl @@ -109,21 +109,21 @@ partial interface HTMLMediaElement { readonly attribute boolean mozAudioCaptured; // Mozilla extension: extra stream metadata information, used as part // of MozAudioAvailable events and the mozWriteAudio() method. The // mozFrameBufferLength method allows for the size of the framebuffer // used within MozAudioAvailable events to be changed. The new size must // be between 512 and 16384. The default size, for a media element with // audio is (mozChannels * 1024). - [GetterThrows] + [Pref="media.audio_data.enabled", GetterThrows] readonly attribute unsigned long mozChannels; - [GetterThrows] + [Pref="media.audio_data.enabled", GetterThrows] readonly attribute unsigned long mozSampleRate; - [Throws] + [Pref="media.audio_data.enabled", Throws] attribute unsigned long mozFrameBufferLength; // Mozilla extension: return embedded metadata from the stream as a // JSObject with key:value pairs for each tag. This can be used by // player interfaces to display the song title, artist, etc. [Throws] object? mozGetMetadata();
--- a/editor/libeditor/html/nsHTMLDataTransfer.cpp +++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp @@ -3,16 +3,17 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <string.h> #include "mozilla/dom/DocumentFragment.h" #include "mozilla/Base64.h" +#include "mozilla/BasicEvents.h" #include "mozilla/Preferences.h" #include "mozilla/Selection.h" #include "mozilla/Util.h" #include "nsAString.h" #include "nsAutoPtr.h" #include "nsCOMArray.h" #include "nsCOMPtr.h" #include "nsCRT.h"
--- a/js/public/MemoryMetrics.h +++ b/js/public/MemoryMetrics.h @@ -20,16 +20,47 @@ #include "jspubtd.h" #include "js/HashTable.h" #include "js/Utility.h" #include "js/Vector.h" class nsISupports; // Needed for ObjectPrivateVisitor. +namespace JS { + +struct TabSizes +{ + enum Kind { + Objects, + Strings, + Private, + Other + }; + + TabSizes() { mozilla::PodZero(this); } + + void add(Kind kind, size_t n) { + switch (kind) { + case Objects: objects += n; break; + case Strings: strings += n; break; + case Private: private_ += n; break; + case Other: other += n; break; + default: MOZ_CRASH("bad TabSizes kind"); + } + } + + size_t objects; + size_t strings; + size_t private_; + size_t other; +}; + +} // namespace JS + namespace js { // In memory reporting, we have concept of "sundries", line items which are too // small to be worth reporting individually. Under some circumstances, a memory // reporter gets tossed into the sundries bucket if it's smaller than // MemoryReportingSundriesThreshold() bytes. // // We need to define this value here, rather than in the code which actually @@ -52,42 +83,43 @@ struct InefficientNonFlatteningStringHas // without updating all the required methods. So we define a single macro list // in each class to name the fields (and notable characteristics of them), and // then use the following macros to transform those lists into the required // methods. // // In some classes, one or more of the macro arguments aren't used. We use '_' // for those. // -#define DECL_SIZE(gc, mSize) size_t mSize; -#define ZERO_SIZE(gc, mSize) mSize(0), -#define COPY_OTHER_SIZE(gc, mSize) mSize(other.mSize), -#define ADD_OTHER_SIZE(gc, mSize) mSize += other.mSize; -#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(gc, mSize) n += (gc) ? mSize : 0; +#define DECL_SIZE(kind, gc, mSize) size_t mSize; +#define ZERO_SIZE(kind, gc, mSize) mSize(0), +#define COPY_OTHER_SIZE(kind, gc, mSize) mSize(other.mSize), +#define ADD_OTHER_SIZE(kind, gc, mSize) mSize += other.mSize; +#define ADD_SIZE_TO_N_IF_LIVE_GC_THING(kind, gc, mSize) n += (js::gc == js::IsLiveGCThing) ? mSize : 0; +#define ADD_TO_TAB_SIZES(kind, gc, mSize) sizes->add(JS::TabSizes::kind, mSize); // Used to annotate which size_t fields measure live GC things and which don't. enum { NotLiveGCThing = false, IsLiveGCThing = true }; struct ZoneStatsPod { #define FOR_EACH_SIZE(macro) \ - macro(NotLiveGCThing, gcHeapArenaAdmin) \ - macro(NotLiveGCThing, unusedGCThings) \ - macro(IsLiveGCThing, lazyScriptsGCHeap) \ - macro(NotLiveGCThing, lazyScriptsMallocHeap) \ - macro(IsLiveGCThing, ionCodesGCHeap) \ - macro(IsLiveGCThing, typeObjectsGCHeap) \ - macro(NotLiveGCThing, typeObjectsMallocHeap) \ - macro(NotLiveGCThing, typePool) \ - macro(IsLiveGCThing, stringsShortGCHeap) \ - macro(IsLiveGCThing, stringsNormalGCHeap) \ - macro(NotLiveGCThing, stringsNormalMallocHeap) + macro(Other, NotLiveGCThing, gcHeapArenaAdmin) \ + macro(Other, NotLiveGCThing, unusedGCThings) \ + macro(Other, IsLiveGCThing, lazyScriptsGCHeap) \ + macro(Other, NotLiveGCThing, lazyScriptsMallocHeap) \ + macro(Other, IsLiveGCThing, ionCodesGCHeap) \ + macro(Other, IsLiveGCThing, typeObjectsGCHeap) \ + macro(Other, NotLiveGCThing, typeObjectsMallocHeap) \ + macro(Other, NotLiveGCThing, typePool) \ + macro(Strings, IsLiveGCThing, stringsShortGCHeap) \ + macro(Strings, IsLiveGCThing, stringsNormalGCHeap) \ + macro(Strings, NotLiveGCThing, stringsNormalMallocHeap) ZoneStatsPod() : FOR_EACH_SIZE(ZERO_SIZE) extra() {} void add(const ZoneStatsPod &other) { FOR_EACH_SIZE(ADD_OTHER_SIZE) @@ -96,71 +128,80 @@ struct ZoneStatsPod size_t sizeOfLiveGCThings() const { size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) // Do nothing with |extra|. return n; } + void addToTabSizes(JS::TabSizes *sizes) const { + FOR_EACH_SIZE(ADD_TO_TAB_SIZES) + // Do nothing with |extra|. + } + FOR_EACH_SIZE(DECL_SIZE) void *extra; // This field can be used by embedders. #undef FOR_EACH_SIZE }; } // namespace js namespace JS { // Data for tracking memory usage of things hanging off objects. struct ObjectsExtraSizes { #define FOR_EACH_SIZE(macro) \ - macro(js::NotLiveGCThing, mallocHeapSlots) \ - macro(js::NotLiveGCThing, mallocHeapElementsNonAsmJS) \ - macro(js::NotLiveGCThing, mallocHeapElementsAsmJS) \ - macro(js::NotLiveGCThing, nonHeapElementsAsmJS) \ - macro(js::NotLiveGCThing, nonHeapCodeAsmJS) \ - macro(js::NotLiveGCThing, mallocHeapAsmJSModuleData) \ - macro(js::NotLiveGCThing, mallocHeapArgumentsData) \ - macro(js::NotLiveGCThing, mallocHeapRegExpStatics) \ - macro(js::NotLiveGCThing, mallocHeapPropertyIteratorData) \ - macro(js::NotLiveGCThing, mallocHeapCtypesData) + macro(Objects, NotLiveGCThing, mallocHeapSlots) \ + macro(Objects, NotLiveGCThing, mallocHeapElementsNonAsmJS) \ + macro(Objects, NotLiveGCThing, mallocHeapElementsAsmJS) \ + macro(Objects, NotLiveGCThing, nonHeapElementsAsmJS) \ + macro(Objects, NotLiveGCThing, nonHeapCodeAsmJS) \ + macro(Objects, NotLiveGCThing, mallocHeapAsmJSModuleData) \ + macro(Objects, NotLiveGCThing, mallocHeapArgumentsData) \ + macro(Objects, NotLiveGCThing, mallocHeapRegExpStatics) \ + macro(Objects, NotLiveGCThing, mallocHeapPropertyIteratorData) \ + macro(Objects, NotLiveGCThing, mallocHeapCtypesData) ObjectsExtraSizes() : FOR_EACH_SIZE(ZERO_SIZE) dummy() {} void add(const ObjectsExtraSizes &other) { FOR_EACH_SIZE(ADD_OTHER_SIZE) } size_t sizeOfLiveGCThings() const { size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) return n; } + void addToTabSizes(TabSizes *sizes) const { + FOR_EACH_SIZE(ADD_TO_TAB_SIZES) + } + FOR_EACH_SIZE(DECL_SIZE) int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) #undef FOR_EACH_SIZE }; // Data for tracking JIT-code memory usage. struct CodeSizes { #define FOR_EACH_SIZE(macro) \ - macro(_, ion) \ - macro(_, baseline) \ - macro(_, regexp) \ - macro(_, other) \ - macro(_, unused) + macro(_, _, ion) \ + macro(_, _, baseline) \ + macro(_, _, regexp) \ + macro(_, _, other) \ + macro(_, _, unused) CodeSizes() : FOR_EACH_SIZE(ZERO_SIZE) dummy() {} FOR_EACH_SIZE(DECL_SIZE) int dummy; // present just to absorb the trailing comma from FOR_EACH_SIZE(ZERO_SIZE) @@ -251,27 +292,27 @@ struct NotableStringInfo : public String char *buffer; }; // These measurements relate directly to the JSRuntime, and not to zones and // compartments within it. struct RuntimeSizes { #define FOR_EACH_SIZE(macro) \ - macro(_, object) \ - macro(_, atomsTable) \ - macro(_, contexts) \ - macro(_, dtoa) \ - macro(_, temporary) \ - macro(_, regexpData) \ - macro(_, interpreterStack) \ - macro(_, gcMarker) \ - macro(_, mathCache) \ - macro(_, scriptData) \ - macro(_, scriptSources) + macro(_, _, object) \ + macro(_, _, atomsTable) \ + macro(_, _, contexts) \ + macro(_, _, dtoa) \ + macro(_, _, temporary) \ + macro(_, _, regexpData) \ + macro(_, _, interpreterStack) \ + macro(_, _, gcMarker) \ + macro(_, _, mathCache) \ + macro(_, _, scriptData) \ + macro(_, _, scriptSources) RuntimeSizes() : FOR_EACH_SIZE(ZERO_SIZE) code() {} FOR_EACH_SIZE(DECL_SIZE) CodeSizes code; @@ -330,45 +371,45 @@ struct ZoneStats : js::ZoneStatsPod StringsHashMap strings; js::Vector<NotableStringInfo, 0, js::SystemAllocPolicy> notableStrings; }; struct CompartmentStats { #define FOR_EACH_SIZE(macro) \ - macro(js::IsLiveGCThing, objectsGCHeapOrdinary) \ - macro(js::IsLiveGCThing, objectsGCHeapFunction) \ - macro(js::IsLiveGCThing, objectsGCHeapDenseArray) \ - macro(js::IsLiveGCThing, objectsGCHeapSlowArray) \ - macro(js::IsLiveGCThing, objectsGCHeapCrossCompartmentWrapper) \ - macro(js::NotLiveGCThing, objectsPrivate) \ - macro(js::IsLiveGCThing, shapesGCHeapTreeGlobalParented) \ - macro(js::IsLiveGCThing, shapesGCHeapTreeNonGlobalParented) \ - macro(js::IsLiveGCThing, shapesGCHeapDict) \ - macro(js::IsLiveGCThing, shapesGCHeapBase) \ - macro(js::NotLiveGCThing, shapesMallocHeapTreeTables) \ - macro(js::NotLiveGCThing, shapesMallocHeapDictTables) \ - macro(js::NotLiveGCThing, shapesMallocHeapTreeShapeKids) \ - macro(js::NotLiveGCThing, shapesMallocHeapCompartmentTables) \ - macro(js::IsLiveGCThing, scriptsGCHeap) \ - macro(js::NotLiveGCThing, scriptsMallocHeapData) \ - macro(js::NotLiveGCThing, baselineData) \ - macro(js::NotLiveGCThing, baselineStubsFallback) \ - macro(js::NotLiveGCThing, baselineStubsOptimized) \ - macro(js::NotLiveGCThing, ionData) \ - macro(js::NotLiveGCThing, typeInferenceTypeScripts) \ - macro(js::NotLiveGCThing, typeInferencePendingArrays) \ - macro(js::NotLiveGCThing, typeInferenceAllocationSiteTables) \ - macro(js::NotLiveGCThing, typeInferenceArrayTypeTables) \ - macro(js::NotLiveGCThing, typeInferenceObjectTypeTables) \ - macro(js::NotLiveGCThing, compartmentObject) \ - macro(js::NotLiveGCThing, crossCompartmentWrappersTable) \ - macro(js::NotLiveGCThing, regexpCompartment) \ - macro(js::NotLiveGCThing, debuggeesSet) + macro(Objects, IsLiveGCThing, objectsGCHeapOrdinary) \ + macro(Objects, IsLiveGCThing, objectsGCHeapFunction) \ + macro(Objects, IsLiveGCThing, objectsGCHeapDenseArray) \ + macro(Objects, IsLiveGCThing, objectsGCHeapSlowArray) \ + macro(Objects, IsLiveGCThing, objectsGCHeapCrossCompartmentWrapper) \ + macro(Private, NotLiveGCThing, objectsPrivate) \ + macro(Other, IsLiveGCThing, shapesGCHeapTreeGlobalParented) \ + macro(Other, IsLiveGCThing, shapesGCHeapTreeNonGlobalParented) \ + macro(Other, IsLiveGCThing, shapesGCHeapDict) \ + macro(Other, IsLiveGCThing, shapesGCHeapBase) \ + macro(Other, NotLiveGCThing, shapesMallocHeapTreeTables) \ + macro(Other, NotLiveGCThing, shapesMallocHeapDictTables) \ + macro(Other, NotLiveGCThing, shapesMallocHeapTreeShapeKids) \ + macro(Other, NotLiveGCThing, shapesMallocHeapCompartmentTables) \ + macro(Other, IsLiveGCThing, scriptsGCHeap) \ + macro(Other, NotLiveGCThing, scriptsMallocHeapData) \ + macro(Other, NotLiveGCThing, baselineData) \ + macro(Other, NotLiveGCThing, baselineStubsFallback) \ + macro(Other, NotLiveGCThing, baselineStubsOptimized) \ + macro(Other, NotLiveGCThing, ionData) \ + macro(Other, NotLiveGCThing, typeInferenceTypeScripts) \ + macro(Other, NotLiveGCThing, typeInferencePendingArrays) \ + macro(Other, NotLiveGCThing, typeInferenceAllocationSiteTables) \ + macro(Other, NotLiveGCThing, typeInferenceArrayTypeTables) \ + macro(Other, NotLiveGCThing, typeInferenceObjectTypeTables) \ + macro(Other, NotLiveGCThing, compartmentObject) \ + macro(Other, NotLiveGCThing, crossCompartmentWrappersTable) \ + macro(Other, NotLiveGCThing, regexpCompartment) \ + macro(Other, NotLiveGCThing, debuggeesSet) CompartmentStats() : FOR_EACH_SIZE(ZERO_SIZE) objectsExtra(), extra() {} CompartmentStats(const CompartmentStats &other) @@ -386,32 +427,38 @@ struct CompartmentStats size_t sizeOfLiveGCThings() const { size_t n = 0; FOR_EACH_SIZE(ADD_SIZE_TO_N_IF_LIVE_GC_THING) n += objectsExtra.sizeOfLiveGCThings(); // Do nothing with |extra|. return n; } + void addToTabSizes(TabSizes *sizes) const { + FOR_EACH_SIZE(ADD_TO_TAB_SIZES); + objectsExtra.addToTabSizes(sizes); + // Do nothing with |extra|. + } + FOR_EACH_SIZE(DECL_SIZE) ObjectsExtraSizes objectsExtra; void *extra; // This field can be used by embedders. #undef FOR_EACH_SIZE }; struct RuntimeStats { #define FOR_EACH_SIZE(macro) \ - macro(_, gcHeapChunkTotal) \ - macro(_, gcHeapDecommittedArenas) \ - macro(_, gcHeapUnusedChunks) \ - macro(_, gcHeapUnusedArenas) \ - macro(_, gcHeapChunkAdmin) \ - macro(_, gcHeapGCThings) \ + macro(_, _, gcHeapChunkTotal) \ + macro(_, _, gcHeapDecommittedArenas) \ + macro(_, _, gcHeapUnusedChunks) \ + macro(_, _, gcHeapUnusedArenas) \ + macro(_, _, gcHeapChunkAdmin) \ + macro(_, _, gcHeapGCThings) \ RuntimeStats(mozilla::MallocSizeOf mallocSizeOf) : FOR_EACH_SIZE(ZERO_SIZE) runtime(), cTotals(), zTotals(), compartmentStatsVector(), zoneStatsVector(), @@ -483,11 +530,22 @@ extern JS_PUBLIC_API(size_t) SystemCompartmentCount(JSRuntime *rt); extern JS_PUBLIC_API(size_t) UserCompartmentCount(JSRuntime *rt); extern JS_PUBLIC_API(size_t) PeakSizeOfTemporary(const JSRuntime *rt); +extern JS_PUBLIC_API(bool) +AddSizeOfTab(JSRuntime *rt, JSObject *obj, mozilla::MallocSizeOf mallocSizeOf, + ObjectPrivateVisitor *opv, TabSizes *sizes); + } // namespace JS +#undef DECL_SIZE +#undef ZERO_SIZE +#undef COPY_OTHER_SIZE +#undef ADD_OTHER_SIZE +#undef ADD_SIZE_TO_N_IF_LIVE_GC_THING +#undef ADD_TO_TAB_SIZES + #endif /* js_MemoryMetrics_h */
--- a/js/src/gc/Iteration.cpp +++ b/js/src/gc/Iteration.cpp @@ -21,43 +21,66 @@ void js::TraceRuntime(JSTracer *trc) { JS_ASSERT(!IS_GC_MARKING_TRACER(trc)); AutoPrepareForTracing prep(trc->runtime); MarkRuntime(trc); } +static void +IterateCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data, + JSIterateCompartmentCallback compartmentCallback, + IterateArenaCallback arenaCallback, + IterateCellCallback cellCallback) +{ + for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) + (*compartmentCallback)(rt, data, comp); + + for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) { + JSGCTraceKind traceKind = MapAllocToTraceKind(AllocKind(thingKind)); + size_t thingSize = Arena::thingSize(AllocKind(thingKind)); + + for (ArenaIter aiter(zone, AllocKind(thingKind)); !aiter.done(); aiter.next()) { + ArenaHeader *aheader = aiter.get(); + (*arenaCallback)(rt, data, aheader->getArena(), traceKind, thingSize); + for (CellIterUnderGC iter(aheader); !iter.done(); iter.next()) + (*cellCallback)(rt, data, iter.getCell(), traceKind, thingSize); + } + } +} + void js::IterateZonesCompartmentsArenasCells(JSRuntime *rt, void *data, IterateZoneCallback zoneCallback, JSIterateCompartmentCallback compartmentCallback, IterateArenaCallback arenaCallback, IterateCellCallback cellCallback) { AutoPrepareForTracing prop(rt); for (ZonesIter zone(rt); !zone.done(); zone.next()) { (*zoneCallback)(rt, data, zone); - - for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) - (*compartmentCallback)(rt, data, comp); - - for (size_t thingKind = 0; thingKind != FINALIZE_LIMIT; thingKind++) { - JSGCTraceKind traceKind = MapAllocToTraceKind(AllocKind(thingKind)); - size_t thingSize = Arena::thingSize(AllocKind(thingKind)); + IterateCompartmentsArenasCells(rt, zone, data, + compartmentCallback, arenaCallback, cellCallback); + } +} - for (ArenaIter aiter(zone, AllocKind(thingKind)); !aiter.done(); aiter.next()) { - ArenaHeader *aheader = aiter.get(); - (*arenaCallback)(rt, data, aheader->getArena(), traceKind, thingSize); - for (CellIterUnderGC iter(aheader); !iter.done(); iter.next()) - (*cellCallback)(rt, data, iter.getCell(), traceKind, thingSize); - } - } - } +void +js::IterateZoneCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data, + IterateZoneCallback zoneCallback, + JSIterateCompartmentCallback compartmentCallback, + IterateArenaCallback arenaCallback, + IterateCellCallback cellCallback) +{ + AutoPrepareForTracing prop(rt); + + (*zoneCallback)(rt, data, zone); + IterateCompartmentsArenasCells(rt, zone, data, + compartmentCallback, arenaCallback, cellCallback); } void js::IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback) { AutoPrepareForTracing prep(rt); for (js::GCChunkSet::Range r = rt->gcChunkSet.all(); !r.empty(); r.popFront())
--- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -1269,28 +1269,39 @@ MarkStackRangeConservatively(JSTracer *t typedef void (*IterateChunkCallback)(JSRuntime *rt, void *data, gc::Chunk *chunk); typedef void (*IterateZoneCallback)(JSRuntime *rt, void *data, JS::Zone *zone); typedef void (*IterateArenaCallback)(JSRuntime *rt, void *data, gc::Arena *arena, JSGCTraceKind traceKind, size_t thingSize); typedef void (*IterateCellCallback)(JSRuntime *rt, void *data, void *thing, JSGCTraceKind traceKind, size_t thingSize); /* - * This function calls |compartmentCallback| on every compartment, - * |arenaCallback| on every in-use arena, and |cellCallback| on every in-use - * cell in the GC heap. + * This function calls |zoneCallback| on every zone, |compartmentCallback| on + * every compartment, |arenaCallback| on every in-use arena, and |cellCallback| + * on every in-use cell in the GC heap. */ extern void IterateZonesCompartmentsArenasCells(JSRuntime *rt, void *data, IterateZoneCallback zoneCallback, JSIterateCompartmentCallback compartmentCallback, IterateArenaCallback arenaCallback, IterateCellCallback cellCallback); /* + * This function is like IterateZonesCompartmentsArenasCells, but does it for a + * single zone. + */ +extern void +IterateZoneCompartmentsArenasCells(JSRuntime *rt, Zone *zone, void *data, + IterateZoneCallback zoneCallback, + JSIterateCompartmentCallback compartmentCallback, + IterateArenaCallback arenaCallback, + IterateCellCallback cellCallback); + +/* * Invoke chunkCallback on every in-use chunk. */ extern void IterateChunks(JSRuntime *rt, void *data, IterateChunkCallback chunkCallback); typedef void (*IterateScriptCallback)(JSRuntime *rt, void *data, JSScript *script); /*
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -38,16 +38,17 @@ #include "frontend/BytecodeCompiler.h" #include "gc/Marking.h" #include "jit/AsmJSModule.h" #include "jit/BaselineJIT.h" #include "js/MemoryMetrics.h" #include "js/OldDebugAPI.h" #include "vm/ArgumentsObject.h" #include "vm/Interpreter.h" +#include "vm/ProxyObject.h" #include "vm/RegExpStaticsObject.h" #include "vm/Shape.h" #include "jsatominlines.h" #include "jsboolinlines.h" #include "jscntxtinlines.h" #include "jscompartmentinlines.h" @@ -5675,18 +5676,34 @@ JSObject::addSizeOfExcludingThis(mozilla sizes->mallocHeapElementsAsmJS += mallocSizeOf(elements); #endif } else { sizes->mallocHeapElementsNonAsmJS += mallocSizeOf(elements); } } // Other things may be measured in the future if DMD indicates it is worthwhile. - // Note that sizes->private_ is measured elsewhere. - if (is<ArgumentsObject>()) { + if (is<JSFunction>() || + is<JSObject>() || + is<ArrayObject>() || + is<CallObject>() || + is<RegExpObject>() || + is<ProxyObject>()) + { + // Do nothing. But this function is hot, and we win by getting the + // common cases out of the way early. Some stats on the most common + // classes, as measured during a vanilla browser session: + // - (53.7%, 53.7%): Function + // - (18.0%, 71.7%): Object + // - (16.9%, 88.6%): Array + // - ( 3.9%, 92.5%): Call + // - ( 2.8%, 95.3%): RegExp + // - ( 1.0%, 96.4%): Proxy + + } else if (is<ArgumentsObject>()) { sizes->mallocHeapArgumentsData += as<ArgumentsObject>().sizeOfMisc(mallocSizeOf); } else if (is<RegExpStaticsObject>()) { sizes->mallocHeapRegExpStatics += as<RegExpStaticsObject>().sizeOfData(mallocSizeOf); } else if (is<PropertyIteratorObject>()) { sizes->mallocHeapPropertyIteratorData += as<PropertyIteratorObject>().sizeOfMisc(mallocSizeOf); #ifdef JS_ION } else if (is<AsmJSModuleObject>()) { as<AsmJSModuleObject>().addSizeOfMisc(mallocSizeOf, &sizes->nonHeapCodeAsmJS,
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -373,17 +373,17 @@ class JSObject : public js::ObjectImpl * The number of allocated slots is not stored explicitly, and changes to * the slots must track changes in the slot span. */ static bool growSlots(js::ThreadSafeContext *cx, js::HandleObject obj, uint32_t oldCount, uint32_t newCount); static void shrinkSlots(js::ThreadSafeContext *cx, js::HandleObject obj, uint32_t oldCount, uint32_t newCount); - bool hasDynamicSlots() const { return slots != nullptr; } + bool hasDynamicSlots() const { return !!slots; } protected: static inline bool updateSlotsForSpan(js::ThreadSafeContext *cx, js::HandleObject obj, size_t oldSpan, size_t newSpan); public: /* * Trigger the write barrier on a range of slots that will no longer be
--- a/js/src/vm/MemoryMetrics.cpp +++ b/js/src/vm/MemoryMetrics.cpp @@ -18,18 +18,19 @@ #include "jit/Ion.h" #include "vm/ArrayObject.h" #include "vm/Runtime.h" #include "vm/Shape.h" #include "vm/String.h" #include "vm/WrapperObject.h" using mozilla::DebugOnly; +using mozilla::MallocSizeOf; +using mozilla::MoveRef; using mozilla::OldMove; -using mozilla::MoveRef; using mozilla::PodEqual; using namespace js; using JS::RuntimeStats; using JS::ObjectPrivateVisitor; using JS::ZoneStats; using JS::CompartmentStats; @@ -263,23 +264,20 @@ StatsCellCallback(JSRuntime *rt, void *d cStats->objectsGCHeapDenseArray += thingSize; else if (obj->is<CrossCompartmentWrapperObject>()) cStats->objectsGCHeapCrossCompartmentWrapper += thingSize; else cStats->objectsGCHeapOrdinary += thingSize; obj->addSizeOfExcludingThis(rtStats->mallocSizeOf_, &cStats->objectsExtra); - // JSObject::sizeOfExcludingThis() doesn't measure objectsPrivate, - // so we do it here. if (ObjectPrivateVisitor *opv = closure->opv) { nsISupports *iface; - if (opv->getISupports_(obj, &iface) && iface) { + if (opv->getISupports_(obj, &iface) && iface) cStats->objectsPrivate += opv->sizeOfIncludingThis(iface); - } } break; } case JSTRACE_STRING: { JSString *str = static_cast<JSString *>(thing); size_t strCharsSize = str->sizeOfExcludingThis(rtStats->mallocSizeOf_); @@ -333,17 +331,16 @@ StatsCellCallback(JSRuntime *rt, void *d cStats->shapesGCHeapBase += thingSize; break; } case JSTRACE_SCRIPT: { JSScript *script = static_cast<JSScript *>(thing); CompartmentStats *cStats = GetCompartmentStats(script->compartment()); cStats->scriptsGCHeap += thingSize; - cStats->scriptsMallocHeapData += script->sizeOfData(rtStats->mallocSizeOf_); cStats->typeInferenceTypeScripts += script->sizeOfTypeScript(rtStats->mallocSizeOf_); #ifdef JS_ION jit::AddSizeOfBaselineData(script, rtStats->mallocSizeOf_, &cStats->baselineData, &cStats->baselineStubsFallback); cStats->ionData += jit::SizeOfIonData(script, rtStats->mallocSizeOf_); #endif @@ -441,24 +438,22 @@ JS::CollectRuntimeStats(JSRuntime *rt, R IterateChunks(rt, &rtStats->gcHeapDecommittedArenas, DecommittedArenasChunkCallback); // Take the per-compartment measurements. StatsClosure closure(rtStats, opv); if (!closure.init()) return false; - rtStats->runtime.scriptSources = 0; IterateZonesCompartmentsArenasCells(rt, &closure, StatsZoneCallback, StatsCompartmentCallback, StatsArenaCallback, StatsCellCallback); // Take the "explicit/js/runtime/" measurements. rt->addSizeOfIncludingThis(rtStats->mallocSizeOf_, &rtStats->runtime); - rtStats->gcHeapGCThings = 0; for (size_t i = 0; i < rtStats->zoneStatsVector.length(); i++) { ZoneStats &zStats = rtStats->zoneStatsVector[i]; rtStats->zTotals.add(zStats); // Move any strings which take up more than the sundries threshold // (counting all of their copies together) into notableStrings. FindNotableStrings(zStats); @@ -526,8 +521,67 @@ JS::UserCompartmentCount(JSRuntime *rt) } JS_PUBLIC_API(size_t) JS::PeakSizeOfTemporary(const JSRuntime *rt) { return rt->tempLifoAlloc.peakSizeOfExcludingThis(); } +namespace JS { + +JS_PUBLIC_API(bool) +AddSizeOfTab(JSRuntime *rt, JSObject *obj, MallocSizeOf mallocSizeOf, ObjectPrivateVisitor *opv, + TabSizes *sizes) +{ + class SimpleJSRuntimeStats : public JS::RuntimeStats + { + public: + SimpleJSRuntimeStats(MallocSizeOf mallocSizeOf) + : JS::RuntimeStats(mallocSizeOf) + {} + + virtual void initExtraZoneStats(JS::Zone *zone, JS::ZoneStats *zStats) + MOZ_OVERRIDE + {} + + virtual void initExtraCompartmentStats( + JSCompartment *c, JS::CompartmentStats *cStats) MOZ_OVERRIDE + {} + }; + + SimpleJSRuntimeStats rtStats(mallocSizeOf); + + JS::Zone *zone = GetObjectZone(obj); + + if (!rtStats.compartmentStatsVector.reserve(zone->compartments.length())) + return false; + + if (!rtStats.zoneStatsVector.reserve(1)) + return false; + + // Take the per-compartment measurements. + StatsClosure closure(&rtStats, opv); + if (!closure.init()) + return false; + IterateZoneCompartmentsArenasCells(rt, zone, &closure, StatsZoneCallback, + StatsCompartmentCallback, StatsArenaCallback, + StatsCellCallback); + + JS_ASSERT(rtStats.zoneStatsVector.length() == 1); + rtStats.zTotals.add(rtStats.zoneStatsVector[0]); + + for (size_t i = 0; i < rtStats.compartmentStatsVector.length(); i++) { + CompartmentStats &cStats = rtStats.compartmentStatsVector[i]; + rtStats.cTotals.add(cStats); + } + + for (CompartmentsInZoneIter comp(zone); !comp.done(); comp.next()) + comp->compartmentStats = NULL; + + rtStats.zTotals.addToTabSizes(sizes); + rtStats.cTotals.addToTabSizes(sizes); + + return true; +} + +} // namespace JS +
--- a/js/xpconnect/src/XPCJSRuntime.cpp +++ b/js/xpconnect/src/XPCJSRuntime.cpp @@ -2411,17 +2411,17 @@ SizeOfTreeIncludingThis(nsINode *tree) class OrphanReporter : public JS::ObjectPrivateVisitor { public: OrphanReporter(GetISupportsFun aGetISupports) : JS::ObjectPrivateVisitor(aGetISupports) { } - virtual size_t sizeOfIncludingThis(nsISupports *aSupports) { + virtual size_t sizeOfIncludingThis(nsISupports *aSupports) MOZ_OVERRIDE { size_t n = 0; nsCOMPtr<nsINode> node = do_QueryInterface(aSupports); // https://bugzilla.mozilla.org/show_bug.cgi?id=773533#c11 explains // that we have to skip XBL elements because they violate certain // assumptions. Yuk. if (node && !node->IsInDoc() && !(node->IsElement() && node->AsElement()->IsInNamespace(kNameSpaceID_XBL))) { @@ -2669,16 +2669,35 @@ JSReporter::CollectReports(WindowPaths * REPORT_BYTES(NS_LITERAL_CSTRING("explicit/xpconnect"), KIND_HEAP, xpconnect, "Memory used by XPConnect."); return NS_OK; } +static nsresult +JSSizeOfTab(JSObject *obj, size_t *jsObjectsSize, size_t *jsStringsSize, + size_t *jsPrivateSize, size_t *jsOtherSize) +{ + JSRuntime *rt = nsXPConnect::GetRuntimeInstance()->Runtime(); + + TabSizes sizes; + OrphanReporter orphanReporter(XPCConvert::GetISupportsFromJSObject); + NS_ENSURE_TRUE(JS::AddSizeOfTab(rt, obj, moz_malloc_size_of, + &orphanReporter, &sizes), + NS_ERROR_OUT_OF_MEMORY); + + *jsObjectsSize = sizes.objects; + *jsStringsSize = sizes.strings; + *jsPrivateSize = sizes.private_; + *jsOtherSize = sizes.other; + return NS_OK; +} + } // namespace xpc #ifdef MOZ_CRASHREPORTER static bool DiagnosticMemoryCallback(void *ptr, size_t size) { return CrashReporter::RegisterAppMemory(ptr, size) == NS_OK; } @@ -3035,16 +3054,17 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* // Register memory reporters and distinguished amount functions. NS_RegisterMemoryReporter(new JSMainRuntimeCompartmentsReporter); NS_RegisterMemoryReporter(new JSMainRuntimeTemporaryPeakReporter()); RegisterJSMainRuntimeGCHeapDistinguishedAmount(JSMainRuntimeGCHeapDistinguishedAmount); RegisterJSMainRuntimeTemporaryPeakDistinguishedAmount(JSMainRuntimeTemporaryPeakDistinguishedAmount); RegisterJSMainRuntimeCompartmentsSystemDistinguishedAmount(JSMainRuntimeCompartmentsSystemDistinguishedAmount); RegisterJSMainRuntimeCompartmentsUserDistinguishedAmount(JSMainRuntimeCompartmentsUserDistinguishedAmount); + mozilla::RegisterJSSizeOfTab(JSSizeOfTab); // Install a JavaScript 'debugger' keyword handler in debug builds only #ifdef DEBUG if (!JS_GetGlobalDebugHooks(runtime)->debuggerHandler) xpc_InstallJSDebuggerKeywordHandler(runtime); #endif }
--- a/layout/base/nsArenaMemoryStats.h +++ b/layout/base/nsArenaMemoryStats.h @@ -1,20 +1,81 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsArenaMemoryStats_h #define nsArenaMemoryStats_h +#include "mozilla/Assertions.h" +#include "mozilla/PodOperations.h" + +class nsTabSizes { +public: + enum Kind { + DOM, // DOM stuff. + Style, // Style stuff. + Other // Everything else. + }; + + nsTabSizes() { mozilla::PodZero(this); } + + void add(Kind kind, size_t n) + { + switch (kind) { + case DOM: mDom += n; break; + case Style: mStyle += n; break; + case Other: mOther += n; break; + default: MOZ_CRASH("bad nsTabSizes kind"); + } + } + + size_t mDom; + size_t mStyle; + size_t mOther; +}; + #define FRAME_ID_STAT_FIELD(classname) mArena##classname struct nsArenaMemoryStats { -#define FRAME_ID(classname) size_t FRAME_ID_STAT_FIELD(classname); -#include "nsFrameIdList.h" -#undef FRAME_ID - size_t mLineBoxes; - size_t mRuleNodes; - size_t mStyleContexts; - size_t mOther; +#define FOR_EACH_SIZE(macro) \ + macro(Other, mLineBoxes) \ + macro(Style, mRuleNodes) \ + macro(Style, mStyleContexts) \ + macro(Other, mOther) + + nsArenaMemoryStats() + : + #define ZERO_SIZE(kind, mSize) mSize(0), + FOR_EACH_SIZE(ZERO_SIZE) + #undef ZERO_SIZE + #define FRAME_ID(classname) FRAME_ID_STAT_FIELD(classname)(), + #include "nsFrameIdList.h" + #undef FRAME_ID + dummy() + {} + + void addToTabSizes(nsTabSizes *sizes) const + { + #define ADD_TO_TAB_SIZES(kind, mSize) sizes->add(nsTabSizes::kind, mSize); + FOR_EACH_SIZE(ADD_TO_TAB_SIZES) + #undef ADD_TO_TAB_SIZES + #define FRAME_ID(classname) \ + sizes->add(nsTabSizes::Other, FRAME_ID_STAT_FIELD(classname)); + #include "nsFrameIdList.h" + #undef FRAME_ID + } + + #define DECL_SIZE(kind, mSize) size_t mSize; + FOR_EACH_SIZE(DECL_SIZE) + #undef DECL_SIZE + #define FRAME_ID(classname) size_t FRAME_ID_STAT_FIELD(classname); + #include "nsFrameIdList.h" + #undef FRAME_ID + int dummy; // present just to absorb the trailing comma from FRAME_ID in the + // constructor + +#undef FOR_EACH_SIZE }; #endif // nsArenaMemoryStats_h
--- a/layout/base/nsPresShell.cpp +++ b/layout/base/nsPresShell.cpp @@ -6086,18 +6086,17 @@ PresShell::HandleEvent(nsIFrame* aFrame, } } if (aEvent->eventStructType == NS_KEY_EVENT && mDocument && mDocument->EventHandlingSuppressed()) { if (aEvent->message == NS_KEY_DOWN) { mNoDelayedKeyEvents = true; } else if (!mNoDelayedKeyEvents) { - nsDelayedEvent* event = - new nsDelayedKeyEvent(aEvent->AsKeyboardEvent()); + DelayedEvent* event = new DelayedKeyEvent(aEvent->AsKeyboardEvent()); if (!mDelayedEvents.AppendElement(event)) { delete event; } } return NS_OK; } nsIFrame* frame = aFrame; @@ -6308,17 +6307,17 @@ PresShell::HandleEvent(nsIFrame* aFrame, // Suppress mouse event if it's being targeted at an element inside // a document which needs events suppressed if (aEvent->eventStructType == NS_MOUSE_EVENT && frame->PresContext()->Document()->EventHandlingSuppressed()) { if (aEvent->message == NS_MOUSE_BUTTON_DOWN) { mNoDelayedMouseEvents = true; } else if (!mNoDelayedMouseEvents && aEvent->message == NS_MOUSE_BUTTON_UP) { - nsDelayedEvent* event = new nsDelayedMouseEvent(aEvent->AsMouseEvent()); + DelayedEvent* event = new DelayedMouseEvent(aEvent->AsMouseEvent()); if (!mDelayedEvents.AppendElement(event)) { delete event; } } return NS_OK; } @@ -6841,18 +6840,18 @@ PresShell::HandleEventInternal(WidgetEve return NS_OK; } if (mouseEvent->IsShift()) { aEvent->mFlags.mOnlyChromeDispatch = true; aEvent->mFlags.mRetargetToNonNativeAnonymous = true; } } - nsAutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput, - aEvent, mDocument); + AutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput, + aEvent, mDocument); if (aEvent->mFlags.mIsTrusted && aEvent->message == NS_MOUSE_MOVE) { nsIPresShell::AllowMouseCapture( nsEventStateManager::GetActiveEventStateManager() == manager); } nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent)); @@ -7601,19 +7600,19 @@ PresShell::FireOrClearDelayedEvents(bool mDelayedEvents.Clear(); return; } if (mDocument) { nsCOMPtr<nsIDocument> doc = mDocument; while (!mIsDestroying && mDelayedEvents.Length() && !doc->EventHandlingSuppressed()) { - nsAutoPtr<nsDelayedEvent> ev(mDelayedEvents[0].forget()); + nsAutoPtr<DelayedEvent> ev(mDelayedEvents[0].forget()); mDelayedEvents.RemoveElementAt(0); - ev->Dispatch(this); + ev->Dispatch(); } if (!doc->EventHandlingSuppressed()) { mDelayedEvents.Clear(); } } } static void @@ -8314,16 +8313,66 @@ nsIPresShell::RemovePostRefreshObserver( presContext->RefreshDriver()->RemovePostRefreshObserver(aObserver); return true; } //------------------------------------------------------ // End of protected and private methods on the PresShell //------------------------------------------------------ +//------------------------------------------------------------------ +//-- Delayed event Classes Impls +//------------------------------------------------------------------ + +PresShell::DelayedInputEvent::DelayedInputEvent() : + DelayedEvent(), + mEvent(nullptr) +{ +} + +PresShell::DelayedInputEvent::~DelayedInputEvent() +{ + delete mEvent; +} + +void +PresShell::DelayedInputEvent::Dispatch() +{ + if (!mEvent || !mEvent->widget) { + return; + } + nsCOMPtr<nsIWidget> widget = mEvent->widget; + nsEventStatus status; + widget->DispatchEvent(mEvent, status); +} + +PresShell::DelayedMouseEvent::DelayedMouseEvent(WidgetMouseEvent* aEvent) : + DelayedInputEvent() +{ + WidgetMouseEvent* mouseEvent = + new WidgetMouseEvent(aEvent->mFlags.mIsTrusted, + aEvent->message, + aEvent->widget, + aEvent->reason, + aEvent->context); + mouseEvent->AssignMouseEventData(*aEvent, false); + mEvent = mouseEvent; +} + +PresShell::DelayedKeyEvent::DelayedKeyEvent(WidgetKeyboardEvent* aEvent) : + DelayedInputEvent() +{ + WidgetKeyboardEvent* keyEvent = + new WidgetKeyboardEvent(aEvent->mFlags.mIsTrusted, + aEvent->message, + aEvent->widget); + keyEvent->AssignKeyEventData(*aEvent, false); + mEvent = keyEvent; +} + // Start of DEBUG only code #ifdef DEBUG static void LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg) { nsAutoString n1, n2;
--- a/layout/base/nsPresShell.h +++ b/layout/base/nsPresShell.h @@ -29,19 +29,18 @@ #include "nsCRT.h" #include "nsAutoPtr.h" #include "nsIWidget.h" #include "nsStyleSet.h" #include "nsFrameSelection.h" #include "nsContentUtils.h" // For AddScriptBlocker(). #include "nsRefreshDriver.h" #include "mozilla/Attributes.h" +#include "mozilla/EventForwards.h" #include "mozilla/MemoryReporting.h" -#include "mozilla/MouseEvents.h" -#include "mozilla/TextEvents.h" class nsRange; class nsIDragService; class nsCSSStyleSheet; struct RangePaintInfo; struct nsCallbackEventRequest; #ifdef MOZ_REFLOW_PERF @@ -542,77 +541,45 @@ protected: nsresult rv = NS_OK; if (GetCurrentEventFrame()) { rv = HandleEventInternal(aEvent, aStatus); } PopCurrentEventInfo(); return rv; } - class nsDelayedEvent - { - public: - virtual ~nsDelayedEvent() {}; - virtual void Dispatch(PresShell* aShell) {} - }; - - class nsDelayedInputEvent : public nsDelayedEvent + class DelayedEvent { public: - virtual void Dispatch(PresShell* aShell) - { - if (mEvent && mEvent->widget) { - nsCOMPtr<nsIWidget> w = mEvent->widget; - nsEventStatus status; - w->DispatchEvent(mEvent, status); - } - } + virtual ~DelayedEvent() { } + virtual void Dispatch() { } + }; + + class DelayedInputEvent : public DelayedEvent + { + public: + virtual void Dispatch() MOZ_OVERRIDE; protected: - nsDelayedInputEvent() - : nsDelayedEvent(), mEvent(nullptr) {} - - virtual ~nsDelayedInputEvent() - { - delete mEvent; - } + DelayedInputEvent(); + virtual ~DelayedInputEvent(); mozilla::WidgetInputEvent* mEvent; }; - class nsDelayedMouseEvent : public nsDelayedInputEvent + class DelayedMouseEvent : public DelayedInputEvent { public: - nsDelayedMouseEvent(mozilla::WidgetMouseEvent* aEvent) : - nsDelayedInputEvent() - { - mozilla::WidgetMouseEvent* mouseEvent = - new mozilla::WidgetMouseEvent(aEvent->mFlags.mIsTrusted, - aEvent->message, - aEvent->widget, - aEvent->reason, - aEvent->context); - mouseEvent->AssignMouseEventData(*aEvent, false); - mEvent = mouseEvent; - } + DelayedMouseEvent(mozilla::WidgetMouseEvent* aEvent); }; - class nsDelayedKeyEvent : public nsDelayedInputEvent + class DelayedKeyEvent : public DelayedInputEvent { public: - nsDelayedKeyEvent(mozilla::WidgetKeyboardEvent* aEvent) : - nsDelayedInputEvent() - { - mozilla::WidgetKeyboardEvent* keyEvent = - new mozilla::WidgetKeyboardEvent(aEvent->mFlags.mIsTrusted, - aEvent->message, - aEvent->widget); - keyEvent->AssignKeyEventData(*aEvent, false); - mEvent = keyEvent; - } + DelayedKeyEvent(mozilla::WidgetKeyboardEvent* aEvent); }; // Check if aEvent is a mouse event and record the mouse location for later // synth mouse moves. void RecordMouseLocation(mozilla::WidgetGUIEvent* aEvent); class nsSynthMouseMoveEvent MOZ_FINAL : public nsARefreshObserver { public: nsSynthMouseMoveEvent(PresShell* aPresShell, bool aFromScroll) @@ -754,17 +721,17 @@ protected: // Set of frames that we should mark with NS_FRAME_HAS_DIRTY_CHILDREN after // we finish reflowing mCurrentReflowRoot. nsTHashtable<nsPtrHashKey<nsIFrame> > mFramesToDirty; // Reflow roots that need to be reflowed. nsTArray<nsIFrame*> mDirtyRoots; - nsTArray<nsAutoPtr<nsDelayedEvent> > mDelayedEvents; + nsTArray<nsAutoPtr<DelayedEvent> > mDelayedEvents; nsRevocableEventPtr<nsRunnableMethod<PresShell> > mResizeEvent; nsCOMPtr<nsITimer> mAsyncResizeEventTimer; private: nsIFrame* mCurrentEventFrame; nsCOMPtr<nsIContent> mCurrentEventContent; nsTArray<nsIFrame*> mCurrentEventFrameStack; nsCOMArray<nsIContent> mCurrentEventContentStack; protected:
--- a/layout/generic/Selection.h +++ b/layout/generic/Selection.h @@ -9,17 +9,17 @@ #include "nsIWeakReference.h" #include "nsISelection.h" #include "nsISelectionController.h" #include "nsISelectionPrivate.h" #include "nsRange.h" #include "nsThreadUtils.h" -#include "mozilla/TextEvents.h" +#include "mozilla/TextRange.h" struct CachedOffsetForFrame; class nsAutoScrollTimer; class nsIContentIterator; class nsIFrame; class nsFrameSelection; struct SelectionDetails;
--- a/layout/generic/nsFrameSelection.h +++ b/layout/generic/nsFrameSelection.h @@ -3,17 +3,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsFrameSelection_h___ #define nsFrameSelection_h___ #include "mozilla/Attributes.h" #include "mozilla/EventForwards.h" #include "mozilla/Selection.h" -#include "mozilla/TextEvents.h" +#include "mozilla/TextRange.h" #include "nsIFrame.h" #include "nsIContent.h" #include "nsISelectionController.h" #include "nsITableCellLayout.h" #include "nsIDOMElement.h" #include "nsRange.h" class nsTableOuterFrame;
--- a/layout/generic/nsObjectFrame.cpp +++ b/layout/generic/nsObjectFrame.cpp @@ -140,17 +140,16 @@ extern "C" { extern void DisposeGWorld(GWorldPtr offscreenGWorld) __attribute__((weak_import)); #endif /* __QDOFFSCREEN__ */ } #endif /* #if defined(XP_MACOSX) && !defined(__LP64__) */ using namespace mozilla; -using namespace mozilla::plugins; using namespace mozilla::layers; class PluginBackgroundSink : public ReadbackSink { public: PluginBackgroundSink(nsObjectFrame* aFrame, uint64_t aStartSequenceNumber) : mLastSequenceNumber(aStartSequenceNumber), mFrame(aFrame) {} ~PluginBackgroundSink() { @@ -1786,17 +1785,17 @@ nsObjectFrame::PaintPlugin(nsDisplayList nsPoint origin; gfxWindowsNativeDrawing nativeDraw(ctx, frameGfxRect); if (nativeDraw.IsDoublePass()) { // OOP plugin specific: let the shim know before we paint if we are doing a // double pass render. If this plugin isn't oop, the register window message // will be ignored. NPEvent pluginEvent; - pluginEvent.event = DoublePassRenderingEvent(); + pluginEvent.event = plugins::DoublePassRenderingEvent(); pluginEvent.wParam = 0; pluginEvent.lParam = 0; if (pluginEvent.event) inst->HandleEvent(&pluginEvent, nullptr); } do { HDC hdc = nativeDraw.BeginNativeDrawing(); if (!hdc)
--- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -2415,18 +2415,19 @@ private: nsStyleCoord mFilterParameter; // coord, percent, factor, angle union { nsIURI* mURL; nsCSSShadowArray* mDropShadow; }; }; template<> -struct nsTArray_CopyElements<nsStyleFilter> - : public nsTArray_CopyWithConstructors<nsStyleFilter> {}; +struct nsTArray_CopyChooser<nsStyleFilter> { + typedef nsTArray_CopyWithConstructors<nsStyleFilter> Type; +}; struct nsStyleSVGReset { nsStyleSVGReset(); nsStyleSVGReset(const nsStyleSVGReset& aSource); ~nsStyleSVGReset(); void* operator new(size_t sz, nsPresContext* aContext) CPP_THROW_NEW { return aContext->AllocateFromShell(sz);
--- a/layout/xul/base/src/nsMenuPopupFrame.cpp +++ b/layout/xul/base/src/nsMenuPopupFrame.cpp @@ -14,16 +14,17 @@ #include "nsINameSpaceManager.h" #include "nsViewManager.h" #include "nsWidgetsCID.h" #include "nsMenuFrame.h" #include "nsMenuBarFrame.h" #include "nsPopupSetFrame.h" #include "nsEventDispatcher.h" #include "nsPIDOMWindow.h" +#include "nsIDOMKeyEvent.h" #include "nsIDOMScreen.h" #include "nsIPresShell.h" #include "nsFrameManager.h" #include "nsIDocument.h" #include "nsRect.h" #include "nsIComponentManager.h" #include "nsBoxLayoutState.h" #include "nsIScrollableFrame.h" @@ -1645,17 +1646,17 @@ nsMenuPopupFrame::FindMenuWithShortcut(n bool isMenu = parentContent && !parentContent->NodeInfo()->Equals(nsGkAtoms::menulist, kNameSpaceID_XUL); static DOMTimeStamp lastKeyTime = 0; DOMTimeStamp keyTime; aKeyEvent->GetTimeStamp(&keyTime); if (charCode == 0) { - if (keyCode == NS_VK_BACK) { + if (keyCode == nsIDOMKeyEvent::DOM_VK_BACK_SPACE) { if (!isMenu && !mIncrementalString.IsEmpty()) { mIncrementalString.SetLength(mIncrementalString.Length() - 1); return nullptr; } else { #ifdef XP_WIN nsCOMPtr<nsISound> soundInterface = do_CreateInstance("@mozilla.org/sound;1"); if (soundInterface)
--- a/layout/xul/base/src/nsXULPopupManager.cpp +++ b/layout/xul/base/src/nsXULPopupManager.cpp @@ -1841,17 +1841,18 @@ nsXULPopupManager::HandleKeyboardNavigat if (item) itemFrame = item->Frame(); else if (mActiveMenuBar) itemFrame = mActiveMenuBar; else return false; nsNavigationDirection theDirection; - NS_ASSERTION(aKeyCode >= NS_VK_END && aKeyCode <= NS_VK_DOWN, "Illegal key code"); + NS_ASSERTION(aKeyCode >= nsIDOMKeyEvent::DOM_VK_END && + aKeyCode <= nsIDOMKeyEvent::DOM_VK_DOWN, "Illegal key code"); theDirection = NS_DIRECTION_FROM_KEY_CODE(itemFrame, aKeyCode); // if a popup is open, first check for navigation within the popup if (item && HandleKeyboardNavigationInPopup(item, theDirection)) return true; // no popup handled the key, so check the active menubar, if any if (mActiveMenuBar) { @@ -2349,18 +2350,18 @@ nsXULMenuCommandEvent::Run() nsPresContext* presContext = menuFrame->PresContext(); nsCOMPtr<nsIPresShell> shell = presContext->PresShell(); nsRefPtr<nsViewManager> kungFuDeathGrip = shell->GetViewManager(); // Deselect ourselves. if (mCloseMenuMode != CloseMenuMode_None) menuFrame->SelectMenu(false); - nsAutoHandlingUserInputStatePusher userInpStatePusher(mUserInput, nullptr, - shell->GetDocument()); + AutoHandlingUserInputStatePusher userInpStatePusher(mUserInput, nullptr, + shell->GetDocument()); nsContentUtils::DispatchXULCommand(mMenu, mIsTrusted, nullptr, shell, mControl, mAlt, mShift, mMeta); } if (popup && mCloseMenuMode != CloseMenuMode_None) pm->HidePopup(popup, mCloseMenuMode == CloseMenuMode_Auto, true, false); return NS_OK;
--- a/mobile/android/chrome/content/InputWidgetHelper.js +++ b/mobile/android/chrome/content/InputWidgetHelper.js @@ -8,17 +8,17 @@ var InputWidgetHelper = { handleEvent: function(aEvent) { this.handleClick(aEvent.target); }, handleClick: function(aTarget) { // if we're busy looking at a InputWidget we want to eat any clicks that // come to us, but not to process them - if (this._uiBusy || !this._isValidInput(aTarget)) + if (this._uiBusy || !this.hasInputWidget(aTarget)) return; this._uiBusy = true; this.show(aTarget); this._uiBusy = false; }, show: function(aElement) { @@ -55,17 +55,17 @@ var InputWidgetHelper = { } // Else the user canceled the input. if (changed) this.fireOnChange(aElement); }).bind(this)); }, - _isValidInput: function(aElement) { + hasInputWidget: function(aElement) { if (!aElement instanceof HTMLInputElement) return false; let type = aElement.getAttribute('type'); if (type == "date" || type == "datetime" || type == "datetime-local" || type == "week" || type == "month" || type == "time") { return true; }
--- a/mobile/android/chrome/content/SelectionHandler.js +++ b/mobile/android/chrome/content/SelectionHandler.js @@ -281,17 +281,17 @@ var SelectionHandler = { * Initializes SelectionHandler and positions the caret handle. * * @param aX, aY tap location in client coordinates. */ attachCaret: function sh_attachCaret(aElement) { this._initTargetInfo(aElement); this._contentWindow.addEventListener("keydown", this, false); - this._contentWindow.addEventListener("blur", this, false); + this._contentWindow.addEventListener("blur", this, true); this._activeType = this.TYPE_CURSOR; this._positionHandles(); sendMessageToJava({ type: "TextSelection:ShowHandles", handles: [this.HANDLE_TYPE_MIDDLE] });
--- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -4303,18 +4303,19 @@ var BrowserEventHandler = { [x, y] = this._moveClickPoint(element, x, y); element = ElementTouchHelper.anyElementFromPoint(x, y); } this._sendMouseEvent("mousemove", element, x, y); this._sendMouseEvent("mousedown", element, x, y); this._sendMouseEvent("mouseup", element, x, y); - // See if its an input element, and it isn't disabled + // See if its an input element, and it isn't disabled, nor handled by Android native dialog if (!element.disabled && + !InputWidgetHelper.hasInputWidget(element) && ((element instanceof HTMLInputElement && element.mozIsTextField(false)) || (element instanceof HTMLTextAreaElement))) SelectionHandler.attachCaret(element); // scrollToFocusedInput does its own checks to find out if an element should be zoomed into BrowserApp.scrollToFocusedInput(BrowserApp.selectedBrowser); } catch(e) { Cu.reportError(e);
--- a/modules/libmar/tool/mar.c +++ b/modules/libmar/tool/mar.c @@ -149,17 +149,17 @@ int main(int argc, char **argv) { argc -= 2; } #if defined(XP_WIN) && !defined(MAR_NSS) && !defined(NO_SIGN_VERIFY) /* -D DERFilePath, also matches -D[index] DERFilePath We allow an index for verifying to be symmetric with the import and export command line arguments. */ else if (argv[1][0] == '-' && argv[1][1] == 'D' && - (argv[1][2] == '0' + certCount || argv[1][2] == '\0')) { + (argv[1][2] == (char)('0' + certCount) || argv[1][2] == '\0')) { if (certCount >= MAX_SIGNATURES) { print_usage(); return -1; } DERFilePaths[certCount++] = argv[2]; argv += 2; argc -= 2; }
--- a/testing/mochitest/runtestsremote.py +++ b/testing/mochitest/runtestsremote.py @@ -610,20 +610,19 @@ def main(): log.info("Running tests %d-%d/%d", start+1, end, len(tests)) deviceRoot = dm.getDeviceRoot() dm.removeFile(os.path.join(deviceRoot, "fennec_ids.txt")) fennec_ids = os.path.abspath("fennec_ids.txt") if not os.path.exists(fennec_ids) and options.robocopIds: fennec_ids = options.robocopIds dm.pushFile(fennec_ids, os.path.join(deviceRoot, "fennec_ids.txt")) - options.extraPrefs.append('robocop.logfile="%s/robocop.log"' % deviceRoot) options.extraPrefs.append('browser.search.suggest.enabled=true') options.extraPrefs.append('browser.search.suggest.prompted=true') - options.extraPrefs.append('layout.css.devPixelsPerPx="1.0"') + options.extraPrefs.append('layout.css.devPixelsPerPx=1.0') options.extraPrefs.append('browser.chrome.dynamictoolbar=false') if (options.dm_trans == 'adb' and options.robocopApk): dm._checkCmd(["install", "-r", options.robocopApk]) retVal = None for test in robocop_tests: if options.testPath and options.testPath != test['name']:
--- a/toolkit/components/aboutmemory/tests/test_memoryReporters.xul +++ b/toolkit/components/aboutmemory/tests/test_memoryReporters.xul @@ -150,16 +150,31 @@ // and this file hasn't been updated appropriately. dummy = mgr[amounts[i]]; ok(dummy !== undefined, "accessed an unknown distinguished amount: " + amounts[i]); } catch (ex) { } } + // Run sizeOfTab() to make sure it doesn't crash. We can't check the result + // values because they're non-deterministic. + let jsObjectsSize = {}; + let jsStringsSize = {}; + let jsOtherSize = {}; + let domSize = {}; + let styleSize = {}; + let otherSize = {}; + let totalSize = {}; + let jsMilliseconds = {}; + let nonJSMilliseconds = {}; + mgr.sizeOfTab(window, jsObjectsSize, jsStringsSize, jsOtherSize, + domSize, styleSize, otherSize, totalSize, + jsMilliseconds, nonJSMilliseconds); + let e = mgr.enumerateReporters(); while (e.hasMoreElements()) { let r = e.getNext().QueryInterface(Ci.nsIMemoryReporter); r.collectReports(handleReport, null); // Access |name| to make sure it doesn't crash or assert. dummy = r.name; }
--- a/toolkit/content/widgets/videocontrols.xml +++ b/toolkit/content/widgets/videocontrols.xml @@ -235,25 +235,19 @@ <tr> <td class="statLabel">&stats.volume;</td> <td class="statValue"><span class="statVolume"/></td> </tr> <tr> <!-- Localization note: readyState is a HTML5 API MediaElement-specific attribute and should not be localized. --> <td class="statLabel">readyState</td> <td class="statValue"><span class="statReadyState"/></td> </tr> <tr> - <td class="statLabel">&stats.channels;</td> <td class="statValue"><span class="statChannels"/></td> - </tr> - <tr> <!-- Localization note: networkState is a HTML5 API MediaElement-specific attribute and should not be localized. --> <td class="statLabel">networkState</td> <td class="statValue"><span class="statNetState"/></td> </tr> - <tr> - <td class="statLabel">&stats.sampleRate;</td> <td class="statValue"><span class="statSampleRate"/></td> - </tr> <tr style="height: 1em;"/> <tr> <td class="statLabel">&stats.framesParsed;</td> <td class="statValue"><span class="statFramesParsed"/></td> </tr> <tr> <td class="statLabel">&stats.framesDecoded;</td> @@ -1213,19 +1207,16 @@ s.framesDecoded.textContent = v.mozDecodedFrames; s.framesPresented.textContent = v.mozPresentedFrames; s.framesPainted.textContent = v.mozPaintedFrames; let volume = Math.round(v.volume * 100) + "%"; if (v.muted) volume += " (muted)"; s.volume.textContent = volume; - - s.channels.textContent = v.mozChannels; - s.sampRate.textContent = (v.mozSampleRate / 1000).toFixed(3) + " kHz"; }, keyHandler : function(event) { // Ignore keys when content might be providing its own. if (!this.video.hasAttribute("controls")) return; var keystroke = ""; @@ -1405,18 +1396,16 @@ this.clickToPlay = document.getAnonymousElementByAttribute(binding, "class", "clickToPlay"); this.fullscreenButton = document.getAnonymousElementByAttribute(binding, "class", "fullscreenButton"); this.statsTable = document.getAnonymousElementByAttribute(binding, "class", "statsTable"); this.stats.filename = document.getAnonymousElementByAttribute(binding, "class", "statFilename"); this.stats.size = document.getAnonymousElementByAttribute(binding, "class", "statSize"); this.stats.activity = document.getAnonymousElementByAttribute(binding, "class", "statActivity"); this.stats.volume = document.getAnonymousElementByAttribute(binding, "class", "statVolume"); - this.stats.channels = document.getAnonymousElementByAttribute(binding, "class", "statChannels"); - this.stats.sampRate = document.getAnonymousElementByAttribute(binding, "class", "statSampleRate"); this.stats.readyState = document.getAnonymousElementByAttribute(binding, "class", "statReadyState"); this.stats.netState = document.getAnonymousElementByAttribute(binding, "class", "statNetState"); this.stats.framesParsed = document.getAnonymousElementByAttribute(binding, "class", "statFramesParsed"); this.stats.framesDecoded = document.getAnonymousElementByAttribute(binding, "class", "statFramesDecoded"); this.stats.framesPresented = document.getAnonymousElementByAttribute(binding, "class", "statFramesPresented"); this.stats.framesPainted = document.getAnonymousElementByAttribute(binding, "class", "statFramesPainted"); this.setupInitialState();
--- a/toolkit/locales/en-US/chrome/global/videocontrols.dtd +++ b/toolkit/locales/en-US/chrome/global/videocontrols.dtd @@ -12,18 +12,16 @@ <!ENTITY stats.media "Media"> <!ENTITY stats.size "Size"> <!ENTITY stats.activity "Activity"> <!ENTITY stats.activityPaused "Paused"> <!ENTITY stats.activityPlaying "Playing"> <!ENTITY stats.activityEnded "Ended"> <!ENTITY stats.activitySeeking "(seeking)"> <!ENTITY stats.volume "Volume"> -<!ENTITY stats.channels "Channels"> -<!ENTITY stats.sampleRate "Sample Rate"> <!ENTITY stats.framesParsed "Frames parsed"> <!ENTITY stats.framesDecoded "Frames decoded"> <!ENTITY stats.framesPresented "Frames presented"> <!ENTITY stats.framesPainted "Frames painted"> <!ENTITY error.aborted "Video loading stopped."> <!ENTITY error.network "Video playback aborted due to a network error."> <!ENTITY error.decode "Video can't be played because the file is corrupt.">
--- a/tools/profiler/TableTicker.cpp +++ b/tools/profiler/TableTicker.cpp @@ -615,22 +615,24 @@ SyncProfile* NewSyncProfile() SyncProfile* TableTicker::GetBacktrace() { SyncProfile* profile = NewSyncProfile(); TickSample sample; sample.threadProfile = profile; +#if defined(HAVE_NATIVE_UNWIND) #if defined(XP_WIN) || defined(LINUX) tickcontext_t context; sample.PopulateContext(&context); #elif defined(XP_MACOSX) sample.PopulateContext(nullptr); #endif +#endif sample.isSamplingCurrentThread = true; sample.timestamp = mozilla::TimeStamp::Now(); if (!HasUnwinderThread()) { profile->BeginUnwind(); }
--- a/view/public/nsView.h +++ b/view/public/nsView.h @@ -6,16 +6,17 @@ #ifndef nsView_h__ #define nsView_h__ #include "nsCoord.h" #include "nsRect.h" #include "nsPoint.h" #include "nsRegion.h" #include "nsCRT.h" +#include "nsWidgetInitData.h" // for nsWindowType #include "nsIWidgetListener.h" #include "mozilla/EventForwards.h" class nsViewManager; class nsIWidget; class nsIFrame; // Enumerated type to indicate the visibility of a layer.
--- a/view/src/nsViewManager.cpp +++ b/view/src/nsViewManager.cpp @@ -22,16 +22,17 @@ #include "mozilla/StartupTimeline.h" #include "GeckoProfiler.h" #include "nsRefreshDriver.h" #include "mozilla/Preferences.h" #include "nsContentUtils.h" // for nsAutoScriptBlocker #include "nsLayoutUtils.h" #include "Layers.h" #include "gfxPlatform.h" +#include "nsIDocument.h" /** XXX TODO XXX DeCOMify newly private methods Optimize view storage */
--- a/widget/EventForwards.h +++ b/widget/EventForwards.h @@ -59,16 +59,18 @@ namespace mozilla { #undef NS_EVENT_CLASS #undef NS_ROOT_EVENT_CLASS // BasicEvents.h struct EventFlags; // TextEvents.h struct AlternativeCharCode; + +// TextRange.h struct TextRangeStyle; struct TextRange; typedef TextRange* TextRangeArray; } // namespace mozilla #endif // mozilla_EventForwards_h__
--- a/widget/TextEvents.h +++ b/widget/TextEvents.h @@ -6,23 +6,22 @@ #ifndef mozilla_TextEvents_h__ #define mozilla_TextEvents_h__ #include <stdint.h> #include "mozilla/Assertions.h" #include "mozilla/BasicEvents.h" #include "mozilla/EventForwards.h" // for KeyNameIndex, temporarily -#include "nsColor.h" +#include "mozilla/TextRange.h" #include "nsCOMPtr.h" #include "nsIDOMKeyEvent.h" #include "nsITransferable.h" #include "nsRect.h" #include "nsStringGlue.h" -#include "nsStyleConsts.h" #include "nsTArray.h" /****************************************************************************** * virtual keycode values ******************************************************************************/ #define NS_DEFINE_VK(aDOMKeyName, aDOMKeyCode) NS_##aDOMKeyName = aDOMKeyCode @@ -145,158 +144,16 @@ public: // Don't copy mNativeKeyEvent because it may be referred after its instance // is destroyed. mNativeKeyEvent = nullptr; mUniqueId = aEvent.mUniqueId; } }; /****************************************************************************** - * mozilla::TextRangeStyle - ******************************************************************************/ - -struct TextRangeStyle -{ - enum { - LINESTYLE_NONE = NS_STYLE_TEXT_DECORATION_STYLE_NONE, - LINESTYLE_SOLID = NS_STYLE_TEXT_DECORATION_STYLE_SOLID, - LINESTYLE_DOTTED = NS_STYLE_TEXT_DECORATION_STYLE_DOTTED, - LINESTYLE_DASHED = NS_STYLE_TEXT_DECORATION_STYLE_DASHED, - LINESTYLE_DOUBLE = NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE, - LINESTYLE_WAVY = NS_STYLE_TEXT_DECORATION_STYLE_WAVY - }; - - enum { - DEFINED_NONE = 0x00, - DEFINED_LINESTYLE = 0x01, - DEFINED_FOREGROUND_COLOR = 0x02, - DEFINED_BACKGROUND_COLOR = 0x04, - DEFINED_UNDERLINE_COLOR = 0x08 - }; - - // Initialize all members, because TextRange instances may be compared by - // memcomp. - TextRangeStyle() - { - Clear(); - } - - void Clear() - { - mDefinedStyles = DEFINED_NONE; - mLineStyle = LINESTYLE_NONE; - mIsBoldLine = false; - mForegroundColor = mBackgroundColor = mUnderlineColor = NS_RGBA(0, 0, 0, 0); - } - - bool IsDefined() const { return mDefinedStyles != DEFINED_NONE; } - - bool IsLineStyleDefined() const - { - return (mDefinedStyles & DEFINED_LINESTYLE) != 0; - } - - bool IsForegroundColorDefined() const - { - return (mDefinedStyles & DEFINED_FOREGROUND_COLOR) != 0; - } - - bool IsBackgroundColorDefined() const - { - return (mDefinedStyles & DEFINED_BACKGROUND_COLOR) != 0; - } - - bool IsUnderlineColorDefined() const - { - return (mDefinedStyles & DEFINED_UNDERLINE_COLOR) != 0; - } - - bool IsNoChangeStyle() const - { - return !IsForegroundColorDefined() && !IsBackgroundColorDefined() && - IsLineStyleDefined() && mLineStyle == LINESTYLE_NONE; - } - - bool Equals(const TextRangeStyle& aOther) - { - if (mDefinedStyles != aOther.mDefinedStyles) - return false; - if (IsLineStyleDefined() && (mLineStyle != aOther.mLineStyle || - !mIsBoldLine != !aOther.mIsBoldLine)) - return false; - if (IsForegroundColorDefined() && - (mForegroundColor != aOther.mForegroundColor)) - return false; - if (IsBackgroundColorDefined() && - (mBackgroundColor != aOther.mBackgroundColor)) - return false; - if (IsUnderlineColorDefined() && - (mUnderlineColor != aOther.mUnderlineColor)) - return false; - return true; - } - - bool operator !=(const TextRangeStyle &aOther) - { - return !Equals(aOther); - } - - bool operator ==(const TextRangeStyle &aOther) - { - return Equals(aOther); - } - - uint8_t mDefinedStyles; - uint8_t mLineStyle; // DEFINED_LINESTYLE - - bool mIsBoldLine; // DEFINED_LINESTYLE - - nscolor mForegroundColor; // DEFINED_FOREGROUND_COLOR - nscolor mBackgroundColor; // DEFINED_BACKGROUND_COLOR - nscolor mUnderlineColor; // DEFINED_UNDERLINE_COLOR -}; - -/****************************************************************************** - * mozilla::TextRange - ******************************************************************************/ - -// Sync with nsIPrivateTextRange.h when you change these constants. -#define NS_TEXTRANGE_CARETPOSITION 0x01 -#define NS_TEXTRANGE_RAWINPUT 0x02 -#define NS_TEXTRANGE_SELECTEDRAWTEXT 0x03 -#define NS_TEXTRANGE_CONVERTEDTEXT 0x04 -#define NS_TEXTRANGE_SELECTEDCONVERTEDTEXT 0x05 - -struct TextRange -{ - TextRange() : - mStartOffset(0), mEndOffset(0), mRangeType(0) - { - } - - uint32_t mStartOffset; - // XXX Storing end offset makes the initializing code very complicated. - // We should replace it with mLength. - uint32_t mEndOffset; - uint32_t mRangeType; - - TextRangeStyle mRangeStyle; - - uint32_t Length() const { return mEndOffset - mStartOffset; } -}; - -/****************************************************************************** - * mozilla::TextRangeArray - * - * XXX This should be replaced with nsTArray<TextRange>. - ******************************************************************************/ - -typedef TextRange* TextRangeArray; - -/****************************************************************************** * mozilla::WidgetTextEvent * * XXX WidgetTextEvent is fired with compositionupdate event almost every time. * This wastes performance and the cost of mantaining each platform's * implementation. Therefore, we should merge WidgetTextEvent and * WidgetCompositionEvent. Then, DOM compositionupdate should be fired * from TextComposition automatically. ******************************************************************************/
new file mode 100644 --- /dev/null +++ b/widget/TextRange.h @@ -0,0 +1,162 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef mozilla_TextRage_h_ +#define mozilla_TextRage_h_ + +#include <stdint.h> + +#include "nsColor.h" +#include "nsStyleConsts.h" + +namespace mozilla { + +/****************************************************************************** + * mozilla::TextRangeStyle + ******************************************************************************/ + +struct TextRangeStyle +{ + enum + { + LINESTYLE_NONE = NS_STYLE_TEXT_DECORATION_STYLE_NONE, + LINESTYLE_SOLID = NS_STYLE_TEXT_DECORATION_STYLE_SOLID, + LINESTYLE_DOTTED = NS_STYLE_TEXT_DECORATION_STYLE_DOTTED, + LINESTYLE_DASHED = NS_STYLE_TEXT_DECORATION_STYLE_DASHED, + LINESTYLE_DOUBLE = NS_STYLE_TEXT_DECORATION_STYLE_DOUBLE, + LINESTYLE_WAVY = NS_STYLE_TEXT_DECORATION_STYLE_WAVY + }; + + enum + { + DEFINED_NONE = 0x00, + DEFINED_LINESTYLE = 0x01, + DEFINED_FOREGROUND_COLOR = 0x02, + DEFINED_BACKGROUND_COLOR = 0x04, + DEFINED_UNDERLINE_COLOR = 0x08 + }; + + // Initialize all members, because TextRange instances may be compared by + // memcomp. + TextRangeStyle() + { + Clear(); + } + + void Clear() + { + mDefinedStyles = DEFINED_NONE; + mLineStyle = LINESTYLE_NONE; + mIsBoldLine = false; + mForegroundColor = mBackgroundColor = mUnderlineColor = NS_RGBA(0, 0, 0, 0); + } + + bool IsDefined() const { return mDefinedStyles != DEFINED_NONE; } + + bool IsLineStyleDefined() const + { + return (mDefinedStyles & DEFINED_LINESTYLE) != 0; + } + + bool IsForegroundColorDefined() const + { + return (mDefinedStyles & DEFINED_FOREGROUND_COLOR) != 0; + } + + bool IsBackgroundColorDefined() const + { + return (mDefinedStyles & DEFINED_BACKGROUND_COLOR) != 0; + } + + bool IsUnderlineColorDefined() const + { + return (mDefinedStyles & DEFINED_UNDERLINE_COLOR) != 0; + } + + bool IsNoChangeStyle() const + { + return !IsForegroundColorDefined() && !IsBackgroundColorDefined() && + IsLineStyleDefined() && mLineStyle == LINESTYLE_NONE; + } + + bool Equals(const TextRangeStyle& aOther) + { + if (mDefinedStyles != aOther.mDefinedStyles) + return false; + if (IsLineStyleDefined() && (mLineStyle != aOther.mLineStyle || + !mIsBoldLine != !aOther.mIsBoldLine)) + return false; + if (IsForegroundColorDefined() && + (mForegroundColor != aOther.mForegroundColor)) + return false; + if (IsBackgroundColorDefined() && + (mBackgroundColor != aOther.mBackgroundColor)) + return false; + if (IsUnderlineColorDefined() && + (mUnderlineColor != aOther.mUnderlineColor)) + return false; + return true; + } + + bool operator !=(const TextRangeStyle &aOther) + { + return !Equals(aOther); + } + + bool operator ==(const TextRangeStyle &aOther) + { + return Equals(aOther); + } + + uint8_t mDefinedStyles; + uint8_t mLineStyle; // DEFINED_LINESTYLE + + bool mIsBoldLine; // DEFINED_LINESTYLE + + nscolor mForegroundColor; // DEFINED_FOREGROUND_COLOR + nscolor mBackgroundColor; // DEFINED_BACKGROUND_COLOR + nscolor mUnderlineColor; // DEFINED_UNDERLINE_COLOR +}; + +/****************************************************************************** + * mozilla::TextRange + ******************************************************************************/ + +// Sync with nsIPrivateTextRange.h when you change these constants. +#define NS_TEXTRANGE_CARETPOSITION 0x01 +#define NS_TEXTRANGE_RAWINPUT 0x02 +#define NS_TEXTRANGE_SELECTEDRAWTEXT 0x03 +#define NS_TEXTRANGE_CONVERTEDTEXT 0x04 +#define NS_TEXTRANGE_SELECTEDCONVERTEDTEXT 0x05 + +struct TextRange +{ + TextRange() : + mStartOffset(0), mEndOffset(0), mRangeType(0) + { + } + + uint32_t mStartOffset; + // XXX Storing end offset makes the initializing code very complicated. + // We should replace it with mLength. + uint32_t mEndOffset; + uint32_t mRangeType; + + TextRangeStyle mRangeStyle; + + uint32_t Length() const { return mEndOffset - mStartOffset; } +}; + +/****************************************************************************** + * mozilla::TextRangeArray + * + * XXX This should be replaced with nsTArray<TextRange>. + ******************************************************************************/ + +typedef TextRange* TextRangeArray; + +} // namespace mozilla + +#endif // mozilla_TextRage_h_
--- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -6,18 +6,19 @@ #ifndef NSWINDOW_H_ #define NSWINDOW_H_ #include "nsBaseWidget.h" #include "gfxPoint.h" #include "nsIIdleServiceInternal.h" #include "nsTArray.h" #include "AndroidJavaWrappers.h" +#include "mozilla/EventForwards.h" #include "mozilla/StaticPtr.h" -#include "mozilla/TextEvents.h" +#include "mozilla/TextRange.h" class gfxASurface; struct ANPEvent; namespace mozilla { class AndroidGeckoEvent;
--- a/widget/gtk/nsDragService.cpp +++ b/widget/gtk/nsDragService.cpp @@ -17,16 +17,17 @@ #include "prlog.h" #include "nsTArray.h" #include "nsPrimitiveHelpers.h" #include "prtime.h" #include "prthread.h" #include <gtk/gtk.h> #include <gdk/gdkx.h> #include "nsCRT.h" +#include "mozilla/BasicEvents.h" #include "mozilla/Services.h" #include "gfxASurface.h" #include "gfxXlibSurface.h" #include "gfxContext.h" #include "nsImageToPixbuf.h" #include "nsPresContext.h" #include "nsIContent.h"
--- a/widget/gtk/nsWindow.h +++ b/widget/gtk/nsWindow.h @@ -25,17 +25,17 @@ #ifdef MOZ_X11 #include <gdk/gdkx.h> #endif /* MOZ_X11 */ #ifdef ACCESSIBILITY #include "mozilla/a11y/Accessible.h" #endif -#include "mozilla/MouseEvents.h" +#include "mozilla/EventForwards.h" #include "nsGtkIMModule.h" #undef LOG #ifdef MOZ_LOGGING // make sure that logging is enabled before including prlog.h #define FORCE_PR_LOG
--- a/widget/moz.build +++ b/widget/moz.build @@ -113,16 +113,17 @@ EXPORTS.mozilla += [ 'BasicEvents.h', 'ContentEvents.h', 'EventClassList.h', 'EventForwards.h', 'LookAndFeel.h', 'MiscEvents.h', 'MouseEvents.h', 'TextEvents.h', + 'TextRange.h', 'TouchEvents.h', 'WidgetUtils.h', ] if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']: EXPORTS.mozilla += [ 'WidgetTraceEvent.h', ]
--- a/widget/nsIWidgetListener.h +++ b/widget/nsIWidgetListener.h @@ -1,22 +1,24 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsIWidgetListener_h__ #define nsIWidgetListener_h__ -#include "nscore.h" -#include "nsIXULWindow.h" -#include "nsRegion.h" -#include "mozilla/BasicEvents.h" +#include <stdint.h> + +#include "mozilla/EventForwards.h" class nsView; +class nsIntRegion; class nsIPresShell; +class nsIWidget; +class nsIXULWindow; /** * sizemode is an adjunct to widget size */ enum nsSizeMode { nsSizeMode_Normal = 0, nsSizeMode_Minimized, @@ -38,107 +40,106 @@ class nsIWidgetListener { public: /** * If this listener is for an nsIXULWindow, return it. If this is null, then * this is likely a listener for a view, which can be determined using * GetView. If both methods return null, this will be an nsWebBrowser. */ - virtual nsIXULWindow* GetXULWindow() { return nullptr; } + virtual nsIXULWindow* GetXULWindow(); /** * If this listener is for an nsView, return it. */ - virtual nsView* GetView() { return nullptr; } + virtual nsView* GetView(); /** * Return the presshell for this widget listener. */ - virtual nsIPresShell* GetPresShell() { return nullptr; } + virtual nsIPresShell* GetPresShell(); /** * Called when a window is moved to location (x, y). Returns true if the * notification was handled. Coordinates are outer window screen coordinates. */ - virtual bool WindowMoved(nsIWidget* aWidget, int32_t aX, int32_t aY) { return false; } + virtual bool WindowMoved(nsIWidget* aWidget, int32_t aX, int32_t aY); /** * Called when a window is resized to (width, height). Returns true if the * notification was handled. Coordinates are outer window screen coordinates. */ - virtual bool WindowResized(nsIWidget* aWidget, int32_t aWidth, int32_t aHeight) { return false; } + virtual bool WindowResized(nsIWidget* aWidget, + int32_t aWidth, int32_t aHeight); /** * Called when the size mode (minimized, maximized, fullscreen) is changed. */ - virtual void SizeModeChanged(nsSizeMode sizeMode) { } + virtual void SizeModeChanged(nsSizeMode aSizeMode); /** * Called when the z-order of the window is changed. Returns true if the * notification was handled. aPlacement indicates the new z order. If * placement is nsWindowZRelative, then aRequestBelow should be the * window to place below. On return, aActualBelow will be set to the * window actually behind. This generally only applies to Windows. */ - virtual bool ZLevelChanged(bool aImmediate, nsWindowZ *aPlacement, - nsIWidget* aRequestBelow, nsIWidget** aActualBelow) { return false; } + virtual bool ZLevelChanged(bool aImmediate, nsWindowZ* aPlacement, + nsIWidget* aRequestBelow, + nsIWidget** aActualBelow); /** * Called when the window is activated and focused. */ - virtual void WindowActivated() { } + virtual void WindowActivated(); /** * Called when the window is deactivated and no longer focused. */ - virtual void WindowDeactivated() { } + virtual void WindowDeactivated(); /** * Called when the show/hide toolbar button on the Mac titlebar is pressed. */ - virtual void OSToolbarButtonPressed() { } + virtual void OSToolbarButtonPressed(); /** * Called when a request is made to close the window. Returns true if the * notification was handled. Returns true if the notification was handled. */ - virtual bool RequestWindowClose(nsIWidget* aWidget) { return false; } + virtual bool RequestWindowClose(nsIWidget* aWidget); /* * Indicate that a paint is about to occur on this window. This is called * at a time when it's OK to change the geometry of this widget or of * other widgets. Must be called before every call to PaintWindow. */ - virtual void WillPaintWindow(nsIWidget* aWidget) { } + virtual void WillPaintWindow(nsIWidget* aWidget); /** * Paint the specified region of the window. Returns true if the * notification was handled. * This is called at a time when it is not OK to change the geometry of * this widget or of other widgets. */ - virtual bool PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion) { return false; } + virtual bool PaintWindow(nsIWidget* aWidget, nsIntRegion aRegion); /** * Indicates that a paint occurred. * This is called at a time when it is OK to change the geometry of * this widget or of other widgets. * Must be called after every call to PaintWindow. */ - virtual void DidPaintWindow() { } + virtual void DidPaintWindow(); /** * Request that layout schedules a repaint on the next refresh driver tick. */ - virtual void RequestRepaint() { } + virtual void RequestRepaint(); /** * Handle an event. */ virtual nsEventStatus HandleEvent(mozilla::WidgetGUIEvent* aEvent, - bool aUseAttachedEvents) - { - return nsEventStatus_eIgnore; - } + bool aUseAttachedEvents); }; #endif
--- a/widget/nsIWinMetroUtils.idl +++ b/widget/nsIWinMetroUtils.idl @@ -7,35 +7,26 @@ /** * Integration with the "Metro"/"Modern" UI environment in Windows 8. * * Note: browser/metro/base/content/browser-scripts.js contains a stub * implementation of this interface for non-Windows systems, for testing and * development purposes only. */ -[scriptable, uuid(25524bde-8b30-4b49-8d67-7070c790aada)] +[scriptable, uuid(496b4450-5757-40f7-aeb9-a958ae86dbd1)] interface nsIWinMetroUtils : nsISupports { - /* return constants for the handPreference property */ - const long handPreferenceLeft = 0; - const long handPreferenceRight = 1; - /** * Determine if the current browser is running in the metro immersive * environment. */ readonly attribute boolean immersive; /** - * Determine if the user prefers left handed or right handed input. - */ - readonly attribute long handPreference; - - /** * Determine the activation URI */ readonly attribute AString activationURI; /** * Show the settings flyout */ void showSettingsFlyout();
--- a/widget/qt/nsWindow.cpp +++ b/widget/qt/nsWindow.cpp @@ -2803,16 +2803,24 @@ nsWindow::GetDPI() if (heightInches < 0.25) { // Something's broken, but we'd better not crash. return 96.0f; } return float(rootWindow->height()/heightInches); } +nsEventStatus +nsWindow::DispatchEvent(WidgetGUIEvent* aEvent) +{ + nsEventStatus status; + DispatchEvent(aEvent, status); + return status; +} + void nsWindow::DispatchActivateEvent(void) { if (mWidgetListener) mWidgetListener->WindowActivated(); } void @@ -3165,8 +3173,42 @@ nsWindow::GetGLFrameBufferFormat() { if (mLayerManager && mLayerManager->GetBackendType() == mozilla::layers::LAYERS_OPENGL) { return MozQGLWidgetWrapper::isRGBAContext() ? LOCAL_GL_RGBA : LOCAL_GL_RGB; } return LOCAL_GL_NONE; } +void +nsWindow::ProcessMotionEvent() +{ + if (mPinchEvent.needDispatch) { + double distance = DistanceBetweenPoints(mPinchEvent.centerPoint, + mPinchEvent.touchPoint); + distance *= 2; + mPinchEvent.delta = distance - mPinchEvent.prevDistance; + nsIntPoint centerPoint(mPinchEvent.centerPoint.x(), + mPinchEvent.centerPoint.y()); + DispatchGestureEvent(NS_SIMPLE_GESTURE_MAGNIFY_UPDATE, + 0, mPinchEvent.delta, centerPoint); + mPinchEvent.prevDistance = distance; + } + if (mMoveEvent.needDispatch) { + WidgetMouseEvent event(true, NS_MOUSE_MOVE, this, + WidgetMouseEvent::eReal); + + event.refPoint.x = nscoord(mMoveEvent.pos.x()); + event.refPoint.y = nscoord(mMoveEvent.pos.y()); + + event.InitBasicModifiers(mMoveEvent.modifiers & Qt::ControlModifier, + mMoveEvent.modifiers & Qt::AltModifier, + mMoveEvent.modifiers & Qt::ShiftModifier, + mMoveEvent.modifiers & Qt::MetaModifier); + event.clickCount = 0; + + DispatchEvent(&event); + mMoveEvent.needDispatch = false; + } + + mTimerStarted = false; +} +
--- a/widget/qt/nsWindow.h +++ b/widget/qt/nsWindow.h @@ -11,17 +11,17 @@ #include <QKeyEvent> #include <QGestureEvent> #include <qgraphicswidget.h> #include <QTime> #include "nsAutoPtr.h" #include "nsBaseWidget.h" -#include "mozilla/MouseEvents.h" +#include "mozilla/EventForwards.h" #include "nsWeakReference.h" #include "nsGkAtoms.h" #include "nsIIdleServiceInternal.h" #include "nsIRunnable.h" #include "nsThreadUtils.h" @@ -178,22 +178,17 @@ public: // event handling code void DispatchActivateEvent(void); void DispatchDeactivateEvent(void); void DispatchActivateEventOnTopLevelWindow(void); void DispatchDeactivateEventOnTopLevelWindow(void); void DispatchResizeEvent(nsIntRect &aRect, nsEventStatus &aStatus); - nsEventStatus DispatchEvent(mozilla::WidgetGUIEvent* aEvent) - { - nsEventStatus status; - DispatchEvent(aEvent, status); - return status; - } + nsEventStatus DispatchEvent(mozilla::WidgetGUIEvent* aEvent); // Some of the nsIWidget methods virtual bool IsEnabled() const; // called when we are destroyed void OnDestroy(void); // called to check and see if a widget's dimensions are sane @@ -373,45 +368,17 @@ private: *flag &= ~mask; } int32_t mQCursor; // Call this function when the users activity is the direct cause of an // event (like a keypress or mouse click). void UserActivity(); - inline void ProcessMotionEvent() { - if (mPinchEvent.needDispatch) { - double distance = DistanceBetweenPoints(mPinchEvent.centerPoint, mPinchEvent.touchPoint); - distance *= 2; - mPinchEvent.delta = distance - mPinchEvent.prevDistance; - nsIntPoint centerPoint(mPinchEvent.centerPoint.x(), mPinchEvent.centerPoint.y()); - DispatchGestureEvent(NS_SIMPLE_GESTURE_MAGNIFY_UPDATE, - 0, mPinchEvent.delta, centerPoint); - mPinchEvent.prevDistance = distance; - } - if (mMoveEvent.needDispatch) { - WidgetMouseEvent event(true, NS_MOUSE_MOVE, this, - WidgetMouseEvent::eReal); - - event.refPoint.x = nscoord(mMoveEvent.pos.x()); - event.refPoint.y = nscoord(mMoveEvent.pos.y()); - - event.InitBasicModifiers(mMoveEvent.modifiers & Qt::ControlModifier, - mMoveEvent.modifiers & Qt::AltModifier, - mMoveEvent.modifiers & Qt::ShiftModifier, - mMoveEvent.modifiers & Qt::MetaModifier); - event.clickCount = 0; - - DispatchEvent(&event); - mMoveEvent.needDispatch = false; - } - - mTimerStarted = false; - } + inline void ProcessMotionEvent(); void DispatchMotionToMainThread() { if (!mTimerStarted) { nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &nsWindow::ProcessMotionEvent); NS_DispatchToMainThread(event); mTimerStarted = true; }
--- a/widget/windows/KeyboardLayout.cpp +++ b/widget/windows/KeyboardLayout.cpp @@ -90,16 +90,48 @@ public: PRUnichar GetCompositeChar(PRUnichar aBaseChar) const; }; /***************************************************************************** * mozilla::widget::ModifierKeyState *****************************************************************************/ +ModifierKeyState::ModifierKeyState() +{ + Update(); +} + +ModifierKeyState::ModifierKeyState(bool aIsShiftDown, + bool aIsControlDown, + bool aIsAltDown) +{ + Update(); + Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT | MODIFIER_ALTGRAPH); + Modifiers modifiers = 0; + if (aIsShiftDown) { + modifiers |= MODIFIER_SHIFT; + } + if (aIsControlDown) { + modifiers |= MODIFIER_CONTROL; + } + if (aIsAltDown) { + modifiers |= MODIFIER_ALT; + } + if (modifiers) { + Set(modifiers); + } +} + +ModifierKeyState::ModifierKeyState(Modifiers aModifiers) : + mModifiers(aModifiers) +{ + EnsureAltGr(); +} + void ModifierKeyState::Update() { mModifiers = 0; if (IS_VK_DOWN(VK_SHIFT)) { mModifiers |= MODIFIER_SHIFT; } if (IS_VK_DOWN(VK_CONTROL)) { @@ -120,16 +152,32 @@ ModifierKeyState::Update() if (::GetKeyState(VK_SCROLL) & 1) { mModifiers |= MODIFIER_SCROLLLOCK; } EnsureAltGr(); } void +ModifierKeyState::Unset(Modifiers aRemovingModifiers) +{ + mModifiers &= ~aRemovingModifiers; + // Note that we don't need to unset AltGr flag here automatically. + // For nsEditor, we need to remove Alt and Control flags but AltGr isn't + // checked in nsEditor, so, it can be kept. +} + +void +ModifierKeyState::Set(Modifiers aAddingModifiers) +{ + mModifiers |= aAddingModifiers; + EnsureAltGr(); +} + +void ModifierKeyState::InitInputEvent(WidgetInputEvent& aInputEvent) const { aInputEvent.modifiers = mModifiers; switch(aInputEvent.eventStructType) { case NS_MOUSE_EVENT: case NS_MOUSE_SCROLL_EVENT: case NS_WHEEL_EVENT: @@ -165,16 +213,82 @@ ModifierKeyState::InitMouseEvent(WidgetI if (::GetKeyState(VK_XBUTTON1) < 0) { mouseEvent.buttons |= WidgetMouseEvent::e4thButtonFlag; } if (::GetKeyState(VK_XBUTTON2) < 0) { mouseEvent.buttons |= WidgetMouseEvent::e5thButtonFlag; } } +bool +ModifierKeyState::IsShift() const +{ + return (mModifiers & MODIFIER_SHIFT) != 0; +} + +bool +ModifierKeyState::IsControl() const +{ + return (mModifiers & MODIFIER_CONTROL) != 0; +} + +bool +ModifierKeyState::IsAlt() const +{ + return (mModifiers & MODIFIER_ALT) != 0; +} + +bool +ModifierKeyState::IsAltGr() const +{ + return IsControl() && IsAlt(); +} + +bool +ModifierKeyState::IsWin() const +{ + return (mModifiers & MODIFIER_OS) != 0; +} + +bool +ModifierKeyState::IsCapsLocked() const +{ + return (mModifiers & MODIFIER_CAPSLOCK) != 0; +} + +bool +ModifierKeyState::IsNumLocked() const +{ + return (mModifiers & MODIFIER_NUMLOCK) != 0; +} + +bool +ModifierKeyState::IsScrollLocked() const +{ + return (mModifiers & MODIFIER_SCROLLLOCK) != 0; +} + +Modifiers +ModifierKeyState::GetModifiers() const +{ + return mModifiers; +} + +void +ModifierKeyState::EnsureAltGr() +{ + // If both Control key and Alt key are pressed, it means AltGr is pressed. + // Ideally, we should check whether the current keyboard layout has AltGr + // or not. However, setting AltGr flags for keyboard which doesn't have + // AltGr must not be serious bug. So, it should be OK for now. + if (IsAltGr()) { + mModifiers |= MODIFIER_ALTGRAPH; + } +} + /***************************************************************************** * mozilla::widget::UniCharsAndModifiers *****************************************************************************/ void UniCharsAndModifiers::Append(PRUnichar aUniChar, Modifiers aModifiers) { MOZ_ASSERT(mLength < 5); @@ -231,16 +345,60 @@ UniCharsAndModifiers::operator+(const Un result += aOther; return result; } /***************************************************************************** * mozilla::widget::VirtualKey *****************************************************************************/ +// static +VirtualKey::ShiftState +VirtualKey::ModifiersToShiftState(Modifiers aModifiers) +{ + ShiftState state = 0; + if (aModifiers & MODIFIER_SHIFT) { + state |= STATE_SHIFT; + } + if (aModifiers & MODIFIER_CONTROL) { + state |= STATE_CONTROL; + } + if (aModifiers & MODIFIER_ALT) { + state |= STATE_ALT; + } + if (aModifiers & MODIFIER_CAPSLOCK) { + state |= STATE_CAPSLOCK; + } + return state; +} + +// static +Modifiers +VirtualKey::ShiftStateToModifiers(ShiftState aShiftState) +{ + Modifiers modifiers = 0; + if (aShiftState & STATE_SHIFT) { + modifiers |= MODIFIER_SHIFT; + } + if (aShiftState & STATE_CONTROL) { + modifiers |= MODIFIER_CONTROL; + } + if (aShiftState & STATE_ALT) { + modifiers |= MODIFIER_ALT; + } + if (aShiftState & STATE_CAPSLOCK) { + modifiers |= MODIFIER_CAPSLOCK; + } + if ((modifiers & (MODIFIER_ALT | MODIFIER_CONTROL)) == + (MODIFIER_ALT | MODIFIER_CONTROL)) { + modifiers |= MODIFIER_ALTGRAPH; + } + return modifiers; +} + inline PRUnichar VirtualKey::GetCompositeChar(ShiftState aShiftState, PRUnichar aBaseChar) const { return mShiftStates[aShiftState].DeadKey.Table->GetCompositeChar(aBaseChar); } const DeadKeyTable* VirtualKey::MatchingDeadKeyTable(const DeadKeyEntry* aDeadKeyArray, @@ -748,16 +906,22 @@ PRUnichar NativeKey::ComputeUnicharFromScanCode() const { return static_cast<PRUnichar>( ::MapVirtualKeyEx(ComputeVirtualKeyCodeFromScanCode(), MAPVK_VK_TO_CHAR, mKeyboardLayout)); } void +NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const +{ + InitKeyEvent(aKeyEvent, mModKeyState); +} + +void NativeKey::InitKeyEvent(WidgetKeyboardEvent& aKeyEvent, const ModifierKeyState& aModKeyState) const { nsIntPoint point(0, 0); mWidget->InitEvent(aKeyEvent, &point); switch (aKeyEvent.message) { case NS_KEY_DOWN:
--- a/widget/windows/KeyboardLayout.h +++ b/widget/windows/KeyboardLayout.h @@ -6,16 +6,17 @@ #ifndef KeyboardLayout_h__ #define KeyboardLayout_h__ #include "nscore.h" #include "nsAutoPtr.h" #include "nsString.h" #include "nsWindowBase.h" #include "nsWindowDefs.h" +#include "mozilla/Attributes.h" #include "mozilla/EventForwards.h" #include <windows.h> #define NS_NUM_OF_KEYS 70 #define VK_OEM_1 0xBA // ';:' for US #define VK_OEM_PLUS 0xBB // '+' any country #define VK_OEM_COMMA 0xBC @@ -49,94 +50,46 @@ static const uint32_t sModifierKeyMap[][ { nsIWidget::CTRL_L, VK_CONTROL, VK_LCONTROL }, { nsIWidget::CTRL_R, VK_CONTROL, VK_RCONTROL }, { nsIWidget::ALT_L, VK_MENU, VK_LMENU }, { nsIWidget::ALT_R, VK_MENU, VK_RMENU } }; class KeyboardLayout; -class ModifierKeyState { +class ModifierKeyState +{ public: - ModifierKeyState() - { - Update(); - } + ModifierKeyState(); + ModifierKeyState(bool aIsShiftDown, bool aIsControlDown, bool aIsAltDown); + ModifierKeyState(Modifiers aModifiers); - ModifierKeyState(bool aIsShiftDown, bool aIsControlDown, bool aIsAltDown) - { - Update(); - Unset(MODIFIER_SHIFT | MODIFIER_CONTROL | MODIFIER_ALT | MODIFIER_ALTGRAPH); - Modifiers modifiers = 0; - if (aIsShiftDown) { - modifiers |= MODIFIER_SHIFT; - } - if (aIsControlDown) { - modifiers |= MODIFIER_CONTROL; - } - if (aIsAltDown) { - modifiers |= MODIFIER_ALT; - } - if (modifiers) { - Set(modifiers); - } - } + MOZ_ALWAYS_INLINE void Update(); - ModifierKeyState(Modifiers aModifiers) : - mModifiers(aModifiers) - { - EnsureAltGr(); - } - - void Update(); - - void Unset(Modifiers aRemovingModifiers) - { - mModifiers &= ~aRemovingModifiers; - // Note that we don't need to unset AltGr flag here automatically. - // For nsEditor, we need to remove Alt and Control flags but AltGr isn't - // checked in nsEditor, so, it can be kept. - } - - void Set(Modifiers aAddingModifiers) - { - mModifiers |= aAddingModifiers; - EnsureAltGr(); - } + MOZ_ALWAYS_INLINE void Unset(Modifiers aRemovingModifiers); + MOZ_ALWAYS_INLINE void Set(Modifiers aAddingModifiers); void InitInputEvent(WidgetInputEvent& aInputEvent) const; - bool IsShift() const { return (mModifiers & MODIFIER_SHIFT) != 0; } - bool IsControl() const { return (mModifiers & MODIFIER_CONTROL) != 0; } - bool IsAlt() const { return (mModifiers & MODIFIER_ALT) != 0; } - bool IsAltGr() const { return IsControl() && IsAlt(); } - bool IsWin() const { return (mModifiers & MODIFIER_OS) != 0; } + MOZ_ALWAYS_INLINE bool IsShift() const; + MOZ_ALWAYS_INLINE bool IsControl() const; + MOZ_ALWAYS_INLINE bool IsAlt() const; + MOZ_ALWAYS_INLINE bool IsAltGr() const; + MOZ_ALWAYS_INLINE bool IsWin() const; - bool IsCapsLocked() const { return (mModifiers & MODIFIER_CAPSLOCK) != 0; } - bool IsNumLocked() const { return (mModifiers & MODIFIER_NUMLOCK) != 0; } - bool IsScrollLocked() const - { - return (mModifiers & MODIFIER_SCROLLLOCK) != 0; - } + MOZ_ALWAYS_INLINE bool IsCapsLocked() const; + MOZ_ALWAYS_INLINE bool IsNumLocked() const; + MOZ_ALWAYS_INLINE bool IsScrollLocked() const; - Modifiers GetModifiers() const { return mModifiers; } + MOZ_ALWAYS_INLINE Modifiers GetModifiers() const; private: Modifiers mModifiers; - void EnsureAltGr() - { - // If both Control key and Alt key are pressed, it means AltGr is pressed. - // Ideally, we should check whether the current keyboard layout has AltGr - // or not. However, setting AltGr flags for keyboard which doesn't have - // AltGr must not be serious bug. So, it should be OK for now. - if (IsAltGr()) { - mModifiers |= MODIFIER_ALTGRAPH; - } - } + MOZ_ALWAYS_INLINE void EnsureAltGr(); void InitMouseEvent(WidgetInputEvent& aMouseEvent) const; }; struct UniCharsAndModifiers { // Dead-key + up to 4 characters PRUnichar mChars[5]; @@ -191,55 +144,18 @@ public: STATE_SHIFT = 0x01, STATE_CONTROL = 0x02, STATE_ALT = 0x04, STATE_CAPSLOCK = 0x08 }; typedef uint8_t ShiftState; - static ShiftState ModifiersToShiftState(Modifiers aModifiers) - { - ShiftState state = 0; - if (aModifiers & MODIFIER_SHIFT) { - state |= STATE_SHIFT; - } - if (aModifiers & MODIFIER_CONTROL) { - state |= STATE_CONTROL; - } - if (aModifiers & MODIFIER_ALT) { - state |= STATE_ALT; - } - if (aModifiers & MODIFIER_CAPSLOCK) { - state |= STATE_CAPSLOCK; - } - return state; - } - - static Modifiers ShiftStateToModifiers(ShiftState aShiftState) - { - Modifiers modifiers = 0; - if (aShiftState & STATE_SHIFT) { - modifiers |= MODIFIER_SHIFT; - } - if (aShiftState & STATE_CONTROL) { - modifiers |= MODIFIER_CONTROL; - } - if (aShiftState & STATE_ALT) { - modifiers |= MODIFIER_ALT; - } - if (aShiftState & STATE_CAPSLOCK) { - modifiers |= MODIFIER_CAPSLOCK; - } - if ((modifiers & (MODIFIER_ALT | MODIFIER_CONTROL)) == - (MODIFIER_ALT | MODIFIER_CONTROL)) { - modifiers |= MODIFIER_ALTGRAPH; - } - return modifiers; - } + static ShiftState ModifiersToShiftState(Modifiers aModifiers); + static Modifiers ShiftStateToModifiers(ShiftState aShiftState); private: union KeyShiftState { struct { PRUnichar Chars[4]; } Normal; @@ -423,20 +339,17 @@ private: */ PRUnichar ComputeUnicharFromScanCode() const; /** * Initializes the aKeyEvent with the information stored in the instance. */ void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent, const ModifierKeyState& aModKeyState) const; - void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const - { - InitKeyEvent(aKeyEvent, mModKeyState); - } + void InitKeyEvent(WidgetKeyboardEvent& aKeyEvent) const; /** * Dispatches the key event. Returns true if the event is consumed. * Otherwise, false. */ bool DispatchKeyEvent(WidgetKeyboardEvent& aKeyEvent, const MSG* aMsgSentToPlugin = nullptr) const;
--- a/widget/windows/moz.build +++ b/widget/windows/moz.build @@ -56,16 +56,17 @@ CPP_SOURCES += [ 'nsScreenManagerWin.cpp', 'nsScreenWin.cpp', 'nsSound.cpp', 'nsToolkit.cpp', 'nsUXThemeData.cpp', 'nsWidgetFactory.cpp', 'nsWinGesture.cpp', 'nsWindow.cpp', + 'nsWindowBase.cpp', 'nsWindowDbg.cpp', 'nsWindowGfx.cpp', ] if CONFIG['MOZ_CRASHREPORTER']: CPP_SOURCES += [ 'LSPAnnotator.cpp', ]
--- a/widget/windows/nsTextStore.h +++ b/widget/windows/nsTextStore.h @@ -8,17 +8,17 @@ #include "nsAutoPtr.h" #include "nsString.h" #include "nsCOMPtr.h" #include "nsITimer.h" #include "nsIWidget.h" #include "nsWindowBase.h" #include "mozilla/Attributes.h" -#include "mozilla/TextEvents.h" +#include "mozilla/TextRange.h" #include <msctf.h> #include <textstor.h> // GUID_PROP_INPUTSCOPE is declared in inputscope.h using INIT_GUID. // With initguid.h, we get its instance instead of extern declaration. #ifdef INPUTSCOPE_INIT_GUID #include <initguid.h>
--- a/widget/windows/nsWindow.cpp +++ b/widget/windows/nsWindow.cpp @@ -167,16 +167,18 @@ #include "nsCrashOnException.h" #include "nsIXULRuntime.h" #include "nsIContent.h" #include "mozilla/HangMonitor.h" #include "WinIMEHandler.h" +#include "npapi.h" + using namespace mozilla; using namespace mozilla::dom; using namespace mozilla::layers; using namespace mozilla::widget; /************************************************************** ************************************************************** **
new file mode 100644 --- /dev/null +++ b/widget/windows/nsWindowBase.cpp @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsWindowBase.h" + +#include "mozilla/MiscEvents.h" +#include "npapi.h" + +using namespace mozilla; + +bool +nsWindowBase::DispatchPluginEvent(const MSG& aMsg) +{ + if (!PluginHasFocus()) { + return false; + } + WidgetPluginEvent pluginEvent(true, NS_PLUGIN_INPUT_EVENT, this); + nsIntPoint point(0, 0); + InitEvent(pluginEvent, &point); + NPEvent npEvent; + npEvent.event = aMsg.message; + npEvent.wParam = aMsg.wParam; + npEvent.lParam = aMsg.lParam; + pluginEvent.pluginEvent = &npEvent; + pluginEvent.retargetToFocusedDocument = true; + return DispatchWindowEvent(&pluginEvent); +}
--- a/widget/windows/nsWindowBase.h +++ b/widget/windows/nsWindowBase.h @@ -1,19 +1,18 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsWindowBase_h_ #define nsWindowBase_h_ -#include "mozilla/MiscEvents.h" +#include "mozilla/EventForwards.h" #include "nsBaseWidget.h" -#include "npapi.h" #include <windows.h> /* * nsWindowBase - Base class of common methods other classes need to access * in both win32 and winrt window classes. */ class nsWindowBase : public nsBaseWidget { @@ -54,32 +53,17 @@ public: * is called by KeyboardLayout to dispatch gecko events. * Returns true if it's consumed. Otherwise, false. */ virtual bool DispatchKeyboardEvent(mozilla::WidgetGUIEvent* aEvent) = 0; /* * Default dispatch of a plugin event. */ - virtual bool DispatchPluginEvent(const MSG &aMsg) - { - if (!PluginHasFocus()) { - return false; - } - mozilla::WidgetPluginEvent pluginEvent(true, NS_PLUGIN_INPUT_EVENT, this); - nsIntPoint point(0, 0); - InitEvent(pluginEvent, &point); - NPEvent npEvent; - npEvent.event = aMsg.message; - npEvent.wParam = aMsg.wParam; - npEvent.lParam = aMsg.lParam; - pluginEvent.pluginEvent = (void *)&npEvent; - pluginEvent.retargetToFocusedDocument = true; - return DispatchWindowEvent(&pluginEvent); - } + virtual bool DispatchPluginEvent(const MSG& aMsg); /* * Returns true if a plugin has focus on this widget. Otherwise, false. */ virtual bool PluginHasFocus() const MOZ_FINAL { return (mInputContext.mIMEState.mEnabled == IMEState::PLUGIN); }
--- a/widget/windows/winrt/nsWinMetroUtils.cpp +++ b/widget/windows/winrt/nsWinMetroUtils.cpp @@ -380,37 +380,16 @@ NS_IMETHODIMP nsWinMetroUtils::GetImmersive(bool *aImersive) { *aImersive = XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Metro; return NS_OK; } NS_IMETHODIMP -nsWinMetroUtils::GetHandPreference(int32_t *aHandPreference) -{ - if (XRE_GetWindowsEnvironment() == WindowsEnvironmentType_Desktop) { - *aHandPreference = nsIWinMetroUtils::handPreferenceRight; - return NS_OK; - } - - ComPtr<IUISettings> uiSettings; - AssertRetHRESULT(ActivateGenericInstance(RuntimeClass_Windows_UI_ViewManagement_UISettings, uiSettings), NS_ERROR_UNEXPECTED); - - HandPreference value; - uiSettings->get_HandPreference(&value); - if (value == HandPreference::HandPreference_LeftHanded) - *aHandPreference = nsIWinMetroUtils::handPreferenceLeft; - else - *aHandPreference = nsIWinMetroUtils::handPreferenceRight; - - return NS_OK; -} - -NS_IMETHODIMP nsWinMetroUtils::GetActivationURI(nsAString &aActivationURI) { if (!sFrameworkView) { NS_WARNING("GetActivationURI used before view is created!"); return NS_OK; } sFrameworkView->GetActivationURI(aActivationURI); return NS_OK;
--- a/widget/xpwidgets/moz.build +++ b/widget/xpwidgets/moz.build @@ -24,16 +24,17 @@ CPP_SOURCES += [ 'nsBaseDragService.cpp', 'nsBaseScreen.cpp', 'nsBaseWidget.cpp', 'nsClipboardHelper.cpp', 'nsClipboardPrivacyHandler.cpp', 'nsFilePickerProxy.cpp', 'nsHTMLFormatConverter.cpp', 'nsIdleService.cpp', + 'nsIWidgetListener.cpp', 'nsPrimitiveHelpers.cpp', 'nsPrintOptionsImpl.cpp', 'nsPrintSession.cpp', 'nsPrintSettingsImpl.cpp', 'nsTransferable.cpp', 'nsXPLookAndFeel.cpp', ]
new file mode 100644 --- /dev/null +++ b/widget/xpwidgets/nsIWidgetListener.cpp @@ -0,0 +1,114 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsIWidgetListener.h" + +#include "nsRegion.h" +#include "nsView.h" +#include "nsIPresShell.h" +#include "nsIWidget.h" +#include "nsIXULWindow.h" + +#include "mozilla/BasicEvents.h" + +using namespace mozilla; + +nsIXULWindow* +nsIWidgetListener::GetXULWindow() +{ + return nullptr; +} + +nsView* +nsIWidgetListener::GetView() +{ + return nullptr; +} + +nsIPresShell* +nsIWidgetListener::GetPresShell() +{ + return nullptr; +} + +bool +nsIWidgetListener::WindowMoved(nsIWidget* aWidget, + int32_t aX, + int32_t aY) +{ + return false; +} + +bool +nsIWidgetListener::WindowResized(nsIWidget* aWidget, + int32_t aWidth, + int32_t aHeight) +{ + return false; +} + +void +nsIWidgetListener::SizeModeChanged(nsSizeMode aSizeMode) +{ +} + +bool +nsIWidgetListener::ZLevelChanged(bool aImmediate, + nsWindowZ* aPlacement, + nsIWidget* aRequestBelow, + nsIWidget** aActualBelow) +{ + return false; +} + +void +nsIWidgetListener::WindowActivated() +{ +} + +void +nsIWidgetListener::WindowDeactivated() +{ +} + +void +nsIWidgetListener::OSToolbarButtonPressed() +{ +} + +bool +nsIWidgetListener::RequestWindowClose(nsIWidget* aWidget) +{ + return false; +} + +void +nsIWidgetListener::WillPaintWindow(nsIWidget* aWidget) +{ +} + +bool +nsIWidgetListener::PaintWindow(nsIWidget* aWidget, + nsIntRegion aRegion) +{ + return false; +} + +void +nsIWidgetListener::DidPaintWindow() +{ +} + +void +nsIWidgetListener::RequestRepaint() +{ +} + +nsEventStatus +nsIWidgetListener::HandleEvent(WidgetGUIEvent* aEvent, + bool aUseAttachedEvents) +{ + return nsEventStatus_eIgnore; +}
--- a/xpcom/base/nsIMemoryReporter.idl +++ b/xpcom/base/nsIMemoryReporter.idl @@ -1,22 +1,23 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "nsISupports.idl" -interface nsISimpleEnumerator; +interface nsICancelableRunnable; +interface nsIDOMWindow; interface nsIRunnable; -interface nsICancelableRunnable; +interface nsISimpleEnumerator; /* - * Memory reporters measure Firefox's memory usage. They are mainly used to + * Memory reporters measure Firefox's memory usage. They are primarily used to * generate the about:memory page. You should read * https://wiki.mozilla.org/Memory_Reporting before writing a memory * reporter. */ [scriptable, function, uuid(3a61be3b-b93b-461a-a4f8-388214f558b1)] interface nsIMemoryReporterCallback : nsISupports { @@ -303,22 +304,42 @@ interface nsIMemoryReporterManager : nsI [infallible] readonly attribute boolean hasMozMallocUsableSize; /* * Run a series of GC/CC's in an attempt to minimize the application's memory * usage. When we're finished, we invoke the given runnable if it's not * null. Returns a reference to the runnable used for carrying out the task. */ nsICancelableRunnable minimizeMemoryUsage(in nsIRunnable callback); + + /* + * Measure the memory that is known to be owned by this tab, split up into + * several broad categories. Note that this will be an underestimate of the + * true number, due to imperfect memory reporter coverage (corresponding to + * about:memory's "heap-unclassified"), and due to some memory shared between + * tabs not being counted. + * + * The time taken for the measurement (split into JS and non-JS parts) is + * also returned. + */ + void sizeOfTab(in nsIDOMWindow window, + out int64_t jsObjectsSize, out int64_t jsStringsSize, + out int64_t jsOtherSize, out int64_t domSize, + out int64_t styleSize, out int64_t otherSize, + out int64_t totalSize, + out double jsMilliseconds, out double nonJSMilliseconds); }; %{C++ +#include "js/TypeDecls.h" #include "nsStringGlue.h" +class nsPIDOMWindow; + // Note that the memory reporters are held in an nsCOMArray, which means // that individual reporters should be referenced with |nsIMemoryReporter *| // instead of nsCOMPtr<nsIMemoryReporter>. XPCOM_API(nsresult) NS_RegisterMemoryReporter(nsIMemoryReporter* aReporter); XPCOM_API(nsresult) NS_UnregisterMemoryReporter(nsIMemoryReporter* aReporter); namespace mozilla { @@ -350,16 +371,31 @@ DECL_UNREGISTER_DISTINGUISHED_AMOUNT(Sto DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual) DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical) DECL_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows) #undef DECL_REGISTER_DISTINGUISHED_AMOUNT #undef DECL_UNREGISTER_DISTINGUISHED_AMOUNT +// Likewise for per-tab measurement. + +typedef nsresult (*JSSizeOfTabFn)(JSObject* aObj, + size_t* aJsObjectsSize, + size_t* aJsStringSize, + size_t* aJsPrivateSize, + size_t* aJsOtherSize); +typedef nsresult (*NonJSSizeOfTabFn)(nsPIDOMWindow* aWindow, + size_t* aDomSize, + size_t* aStyleSize, + size_t* aOtherSize); + +nsresult RegisterJSSizeOfTab(JSSizeOfTabFn aSizeOfTabFn); +nsresult RegisterNonJSSizeOfTab(NonJSSizeOfTabFn aSizeOfTabFn); + } #if defined(MOZ_DMD) namespace mozilla { namespace dmd { // This runs all the memory reporters but does nothing with the results; i.e. // it does the minimal amount of work possible for DMD to do its thing. void RunReporters();
--- a/xpcom/base/nsMemoryInfoDumper.cpp +++ b/xpcom/base/nsMemoryInfoDumper.cpp @@ -605,29 +605,30 @@ namespace mozilla { #define DUMP(o, s) \ do { \ nsresult rv = (o)->Write(s); \ NS_ENSURE_SUCCESS(rv, rv); \ } while (0) static nsresult -DumpReport(nsIGZFileWriter *aWriter, bool aIsFirst, +DumpReport(nsIGZFileWriter *aWriter, bool *aIsFirstPtr, const nsACString &aProcess, const nsACString &aPath, int32_t aKind, int32_t aUnits, int64_t aAmount, const nsACString &aDescription) { - DUMP(aWriter, aIsFirst ? "[" : ","); - // We only want to dump reports for this process. If |aProcess| is // non-nullptr that means we've received it from another process in response // to a "child-memory-reporter-request" event; ignore such reports. if (!aProcess.IsEmpty()) { return NS_OK; } + DUMP(aWriter, *aIsFirstPtr ? "[" : ","); + *aIsFirstPtr = false; + // Generate the process identifier, which is of the form "$PROCESS_NAME // (pid $PID)", or just "(pid $PID)" if we don't have a process name. If // we're the main process, we let $PROCESS_NAME be "Main Process". nsAutoCString processId; if (XRE_GetProcessType() == GeckoProcessType_Default) { // We're the main process. processId.AssignLiteral("Main Process "); } else if (ContentChild *cc = ContentChild::GetSingleton()) { @@ -673,37 +674,32 @@ DumpReport(nsIGZFileWriter *aWriter, boo return NS_OK; } class DumpReporterCallback MOZ_FINAL : public nsIMemoryReporterCallback { public: NS_DECL_ISUPPORTS - DumpReporterCallback(bool* aIsFirstPtr) : mIsFirstPtr(aIsFirstPtr) {} + DumpReporterCallback() : mIsFirst(true) {} NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath, int32_t aKind, int32_t aUnits, int64_t aAmount, const nsACString &aDescription, nsISupports *aData) { nsCOMPtr<nsIGZFileWriter> writer = do_QueryInterface(aData); NS_ENSURE_TRUE(writer, NS_ERROR_FAILURE); - // The |isFirst = false| assumes that at least one single reporter is - // present and so will have been processed in - // DumpProcessMemoryReportsToGZFileWriter() below. - bool isFirst = *mIsFirstPtr; - *mIsFirstPtr = false; - return DumpReport(writer, isFirst, aProcess, aPath, aKind, aUnits, aAmount, - aDescription); + return DumpReport(writer, &mIsFirst, aProcess, aPath, aKind, aUnits, + aAmount, aDescription); } private: - bool* mIsFirstPtr; + bool mIsFirst; }; NS_IMPL_ISUPPORTS1(DumpReporterCallback, nsIMemoryReporterCallback) } // namespace mozilla static void MakeFilename(const char *aPrefix, const nsAString &aIdentifier, @@ -812,21 +808,20 @@ DumpProcessMemoryReportsToGZFileWriter(n do_GetService("@mozilla.org/memory-reporter-manager;1"); NS_ENSURE_STATE(mgr); DUMP(aWriter, mgr->GetHasMozMallocUsableSize() ? "true" : "false"); DUMP(aWriter, ",\n"); DUMP(aWriter, " \"reports\": "); // Process reporters. - bool isFirst = true; bool more; nsCOMPtr<nsISimpleEnumerator> e; mgr->EnumerateReporters(getter_AddRefs(e)); - nsRefPtr<DumpReporterCallback> cb = new DumpReporterCallback(&isFirst); + nsRefPtr<DumpReporterCallback> cb = new DumpReporterCallback(); while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) { nsCOMPtr<nsIMemoryReporter> r; e->GetNext(getter_AddRefs(r)); r->CollectReports(cb, aWriter); } DUMP(aWriter, "\n ]\n}\n");
--- a/xpcom/base/nsMemoryReporterManager.cpp +++ b/xpcom/base/nsMemoryReporterManager.cpp @@ -7,17 +7,20 @@ #include "nsAtomTable.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "nsCOMArray.h" #include "nsServiceManagerUtils.h" #include "nsMemoryReporterManager.h" #include "nsISimpleEnumerator.h" #include "nsThreadUtils.h" +#include "nsIDOMWindow.h" +#include "nsPIDOMWindow.h" #include "nsIObserverService.h" +#include "nsIGlobalObject.h" #if defined(XP_LINUX) #include "nsMemoryInfoDumper.h" #endif #include "mozilla/Attributes.h" #include "mozilla/PodOperations.h" #include "mozilla/Services.h" #include "mozilla/Telemetry.h" @@ -840,16 +843,17 @@ HashtableEnumerator::GetNext(nsISupports } // anonymous namespace nsMemoryReporterManager::nsMemoryReporterManager() : mMutex("nsMemoryReporterManager::mMutex"), mIsRegistrationBlocked(false) { PodZero(&mAmountFns); + PodZero(&mSizeOfTabFns); } nsMemoryReporterManager::~nsMemoryReporterManager() { } NS_IMETHODIMP nsMemoryReporterManager::EnumerateReporters(nsISimpleEnumerator** aResult) @@ -1262,16 +1266,66 @@ nsMemoryReporterManager::MinimizeMemoryU nsRefPtr<nsICancelableRunnable> runnable = new MinimizeMemoryUsageRunnable(aCallback); NS_ADDREF(*aResult = runnable); return NS_DispatchToMainThread(runnable); } +NS_IMETHODIMP +nsMemoryReporterManager::SizeOfTab(nsIDOMWindow* aTopWindow, + int64_t* aJSObjectsSize, + int64_t* aJSStringsSize, + int64_t* aJSOtherSize, + int64_t* aDomSize, + int64_t* aStyleSize, + int64_t* aOtherSize, + int64_t* aTotalSize, + double* aJSMilliseconds, + double* aNonJSMilliseconds) +{ + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aTopWindow); + nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(aTopWindow); + NS_ENSURE_TRUE(!!global && !!piWindow, NS_ERROR_FAILURE); + + TimeStamp t1 = TimeStamp::Now(); + + // Measure JS memory consumption (and possibly some non-JS consumption, via + // |jsPrivateSize|). + size_t jsObjectsSize, jsStringsSize, jsPrivateSize, jsOtherSize; + nsresult rv = mSizeOfTabFns.mJS(global->GetGlobalJSObject(), + &jsObjectsSize, &jsStringsSize, + &jsPrivateSize, &jsOtherSize); + NS_ENSURE_SUCCESS(rv, rv); + + TimeStamp t2 = TimeStamp::Now(); + + // Measure non-JS memory consumption. + size_t domSize, styleSize, otherSize; + mSizeOfTabFns.mNonJS(piWindow, &domSize, &styleSize, &otherSize); + + TimeStamp t3 = TimeStamp::Now(); + + *aTotalSize = 0; + #define DO(aN, n) { *aN = (n); *aTotalSize += (n); } + DO(aJSObjectsSize, jsObjectsSize); + DO(aJSStringsSize, jsStringsSize); + DO(aJSOtherSize, jsOtherSize); + DO(aDomSize, jsPrivateSize + domSize); + DO(aStyleSize, styleSize); + DO(aOtherSize, otherSize); + #undef DO + + *aJSMilliseconds = (t2 - t1).ToMilliseconds(); + *aNonJSMilliseconds = (t3 - t2).ToMilliseconds(); + + return NS_OK; +} + // Most memory reporters don't need thread safety, but some do. Make them all // thread-safe just to be safe. Memory reporters are created and destroyed // infrequently enough that the performance cost should be negligible. NS_IMPL_ISUPPORTS1(MemoryUniReporter, nsIMemoryReporter) nsresult NS_RegisterMemoryReporter(nsIMemoryReporter* aReporter) { @@ -1289,44 +1343,41 @@ NS_UnregisterMemoryReporter(nsIMemoryRep if (!mgr) { return NS_ERROR_FAILURE; } return mgr->UnregisterReporter(aReporter); } namespace mozilla { +#define GET_MEMORY_REPORTER_MANAGER(mgr) \ + nsCOMPtr<nsIMemoryReporterManager> imgr = \ + do_GetService("@mozilla.org/memory-reporter-manager;1"); \ + nsRefPtr<nsMemoryReporterManager> mgr = \ + static_cast<nsMemoryReporterManager*>(imgr.get()); \ + if (!mgr) { \ + return NS_ERROR_FAILURE; \ + } + // Macro for generating functions that register distinguished amount functions // with the memory reporter manager. #define DEFINE_REGISTER_DISTINGUISHED_AMOUNT(kind, name) \ nsresult \ Register##name##DistinguishedAmount(kind##AmountFn aAmountFn) \ { \ - nsCOMPtr<nsIMemoryReporterManager> imgr = \ - do_GetService("@mozilla.org/memory-reporter-manager;1"); \ - nsRefPtr<nsMemoryReporterManager> mgr = \ - static_cast<nsMemoryReporterManager*>(imgr.get()); \ - if (!mgr) { \ - return NS_ERROR_FAILURE; \ - } \ + GET_MEMORY_REPORTER_MANAGER(mgr) \ mgr->mAmountFns.m##name = aAmountFn; \ return NS_OK; \ } #define DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(name) \ nsresult \ Unregister##name##DistinguishedAmount() \ { \ - nsCOMPtr<nsIMemoryReporterManager> imgr = \ - do_GetService("@mozilla.org/memory-reporter-manager;1"); \ - nsRefPtr<nsMemoryReporterManager> mgr = \ - static_cast<nsMemoryReporterManager*>(imgr.get()); \ - if (!mgr) { \ - return NS_ERROR_FAILURE; \ - } \ + GET_MEMORY_REPORTER_MANAGER(mgr) \ mgr->mAmountFns.m##name = nullptr; \ return NS_OK; \ } DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeGCHeap) DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeTemporaryPeak) DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsSystem) DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, JSMainRuntimeCompartmentsUser) @@ -1339,16 +1390,32 @@ DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT(S DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsVirtual) DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, LowMemoryEventsPhysical) DEFINE_REGISTER_DISTINGUISHED_AMOUNT(Infallible, GhostWindows) #undef DEFINE_REGISTER_DISTINGUISHED_AMOUNT #undef DEFINE_UNREGISTER_DISTINGUISHED_AMOUNT +#define DEFINE_REGISTER_SIZE_OF_TAB(name) \ + nsresult