Compare commits
293
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9edf1b7bea | ||
|
|
930ddd693b | ||
|
|
4cdd532738 | ||
|
|
6f8d063b18 | ||
|
|
b1957eb7c9 | ||
|
|
a5b4a440d6 | ||
|
|
9a84af4d5b | ||
|
|
eecc234b44 | ||
|
|
2366accae5 | ||
|
|
d01ae5cf25 | ||
|
|
92b3def6a8 | ||
|
|
006b8f5c6e | ||
|
|
152974a8fe | ||
|
|
6d480d82a5 | ||
|
|
fa72aef363 | ||
|
|
2ed7526705 | ||
|
|
9ecfdbe358 | ||
|
|
97dcd8009a | ||
|
|
e1fc99d6ef | ||
|
|
0de24cc207 | ||
|
|
28ad4a4b7c | ||
|
|
f41a43acf2 | ||
|
|
1dd2c05377 | ||
|
|
f48c32ebc1 | ||
|
|
b0b90b81f3 | ||
|
|
6f9c302a32 | ||
|
|
a3bee9a6a8 | ||
|
|
26d1ff2bd6 | ||
|
|
825ae7cb05 | ||
|
|
2302734e6f | ||
|
|
40c0791320 | ||
|
|
ab0be54f2d | ||
|
|
6b4ef9af48 | ||
|
|
425677ada7 | ||
|
|
633728842e | ||
|
|
bc2841f9f9 | ||
|
|
d78e03d528 | ||
|
|
bebeb4e26a | ||
|
|
bf110158b9 | ||
|
|
983a87a9d7 | ||
|
|
bf8359c704 | ||
|
|
4e5b100cca | ||
|
|
a874f27b5a | ||
|
|
6b2f1524f8 | ||
|
|
93b170e629 | ||
|
|
33523f5d5e | ||
|
|
1eaadfbc8d | ||
|
|
0102047d68 | ||
|
|
b07c898b06 | ||
|
|
6dd43c3e2f | ||
|
|
b9b1abc871 | ||
|
|
768231cf6a | ||
|
|
c82b759d56 | ||
|
|
9e2a89c71d | ||
|
|
68d1c14bdf | ||
|
|
b73c17b7c6 | ||
|
|
1a31160dde | ||
|
|
e764f258e2 | ||
|
|
3daf711085 | ||
|
|
7fe7026030 | ||
|
|
93407295fc | ||
|
|
db8d2ef672 | ||
|
|
f4f9b0cec7 | ||
|
|
adf51bc0f5 | ||
|
|
378738aaa9 | ||
|
|
c482078b09 | ||
|
|
82f11ad8aa | ||
|
|
72ff18e58e | ||
|
|
a0bdc21cf5 | ||
|
|
42ee994d99 | ||
|
|
f25a45b9e6 | ||
|
|
677085e0c4 | ||
|
|
fbbcbffcc7 | ||
|
|
5d5f72d4bc | ||
|
|
c8b89feae8 | ||
|
|
17b2aa7650 | ||
|
|
6f685140aa | ||
|
|
5aa65d2aea | ||
|
|
712ca7303b | ||
|
|
8c824b8a75 | ||
|
|
7aa445162e | ||
|
|
643e298a55 | ||
|
|
f82c95d1eb | ||
|
|
01747509f6 | ||
|
|
22b8e6b79f | ||
|
|
f9b28d2006 | ||
|
|
185a027640 | ||
|
|
99ec088ecf | ||
|
|
062a5085b1 | ||
|
|
65ed8de1fa | ||
|
|
2e34db5696 | ||
|
|
3a565183f0 | ||
|
|
7ed9c249be | ||
|
|
37c86f2c07 | ||
|
|
8d8da66f94 | ||
|
|
145947d86b | ||
|
|
c2b4aa8cf2 | ||
|
|
129bb5568d | ||
|
|
5b84c3e982 | ||
|
|
3abcec5d8b | ||
|
|
6466bf6f5b | ||
|
|
f4e8585e60 | ||
|
|
cc231f0a26 | ||
|
|
86b17cfb4a | ||
|
|
d2a4597cfc | ||
|
|
43165d9edb | ||
|
|
88624edc98 | ||
|
|
c58e7c72b4 | ||
|
|
8bbfcc4c81 | ||
|
|
f2ad2ba0de | ||
|
|
2af23767e1 | ||
|
|
bb9700d982 | ||
|
|
077b053ba4 | ||
|
|
279ed4a9cf | ||
|
|
5f8057cbed | ||
|
|
95cda916d1 | ||
|
|
08842d77ae | ||
|
|
1191c9e371 | ||
|
|
4ea9a83b75 | ||
|
|
9a2da3012a | ||
|
|
022ed72339 | ||
|
|
f5c363c01d | ||
|
|
41fc1ccf4d | ||
|
|
7abed0e352 | ||
|
|
d430c6398b | ||
|
|
e44c44b749 | ||
|
|
d347a1e0c6 | ||
|
|
39ba93e1d7 | ||
|
|
5cecc096a3 | ||
|
|
8feed35c61 | ||
|
|
7b706516bc | ||
|
|
144b3844da | ||
|
|
eb96f030b9 | ||
|
|
981154dc48 | ||
|
|
a0da6b5b68 | ||
|
|
bd99ce1461 | ||
|
|
bed3776982 | ||
|
|
ef236bef4f | ||
|
|
d90ce1e30a | ||
|
|
bee1a6fe1c | ||
|
|
f9caae6cfd | ||
|
|
e10f9df9df | ||
|
|
7d87b7a270 | ||
|
|
3d43617bc1 | ||
|
|
ac9fdbf123 | ||
|
|
e6101f7fa6 | ||
|
|
52bcfa36bf | ||
|
|
6d9b8e43bf | ||
|
|
ae8cc0e398 | ||
|
|
7d49b154e2 | ||
|
|
d5ae68701b | ||
|
|
330b866f46 | ||
|
|
edcbb85f5e | ||
|
|
c683df5483 | ||
|
|
8862a2ebf7 | ||
|
|
82fa1fbf14 | ||
|
|
faec3f2140 | ||
|
|
ab5f44b61d | ||
|
|
c332210793 | ||
|
|
6709388785 | ||
|
|
2829b9387b | ||
|
|
1e1a1e5c71 | ||
|
|
55d1aa0748 | ||
|
|
9fd02b65e1 | ||
|
|
e6a9d13e64 | ||
|
|
8832bfb62f | ||
|
|
44e35f67b4 | ||
|
|
56d9003017 | ||
|
|
e49b80dba7 | ||
|
|
834ded7d18 | ||
|
|
c617545914 | ||
|
|
ee4e449e04 | ||
|
|
fd51da7587 | ||
|
|
e1a982d677 | ||
|
|
b76d12c2bd | ||
|
|
57dffbd698 | ||
|
|
e0e12932bc | ||
|
|
9a1e152ba3 | ||
|
|
a9046dcd55 | ||
|
|
c265d1cb7c | ||
|
|
d61076d50e | ||
|
|
43bed4dad8 | ||
|
|
3a27ace545 | ||
|
|
d3cc8bc1ae | ||
|
|
4f6406f033 | ||
|
|
ce1c300502 | ||
|
|
6a0d77b68c | ||
|
|
3ceb3ee7ef | ||
|
|
3adf5e5d9c | ||
|
|
b2ad7e8ed9 | ||
|
|
3b48d984b2 | ||
|
|
02afcba614 | ||
|
|
aed6c5eb62 | ||
|
|
429a4cd9d7 | ||
|
|
acf30bc5f4 | ||
|
|
ca38eaca70 | ||
|
|
567ba7ede6 | ||
|
|
79b640795a | ||
|
|
0b75a2a787 | ||
|
|
b8d18bace6 | ||
|
|
ecbe89cd7d | ||
|
|
a7bb296293 | ||
|
|
7da587d0aa | ||
|
|
ac50292859 | ||
|
|
633a742916 | ||
|
|
63b8aaa495 | ||
|
|
4897e3653a | ||
|
|
af32d0b7d1 | ||
|
|
e6c662b5c6 | ||
|
|
6abe7e7914 | ||
|
|
7ab66b1c6a | ||
|
|
df3eed4ef9 | ||
|
|
804d0f6604 | ||
|
|
327f5499ca | ||
|
|
97f911427a | ||
|
|
3b575dfced | ||
|
|
0bc66e3b04 | ||
|
|
6e6b5d8996 | ||
|
|
12635c1c22 | ||
|
|
4c0d99d173 | ||
|
|
8399d85036 | ||
|
|
ef5083db5f | ||
|
|
cff9ef7fb8 | ||
|
|
124a5a8eb7 | ||
|
|
a9376e01cb | ||
|
|
8884db9790 | ||
|
|
28a69cf1e2 | ||
|
|
bdfc1adbd2 | ||
|
|
f990d9a46a | ||
|
|
497e453fe7 | ||
|
|
7dec0edf50 | ||
|
|
97ee3ee428 | ||
|
|
18cfe514ab | ||
|
|
15aafa8a46 | ||
|
|
757a30f720 | ||
|
|
78d1f68932 | ||
|
|
b226f8942d | ||
|
|
f1c2ba75d1 | ||
|
|
f3707e0637 | ||
|
|
ed8d7f1391 | ||
|
|
7e975c2c5b | ||
|
|
4608ae53aa | ||
|
|
121ee9438c | ||
|
|
ba4eba034f | ||
|
|
534de5cc5b | ||
|
|
023a71c6f2 | ||
|
|
c83282fd4d | ||
|
|
d60103257e | ||
|
|
ee86d3eb9c | ||
|
|
d0acadd727 | ||
|
|
9a811901ef | ||
|
|
4a7af0d90f | ||
|
|
0f52679d2e | ||
|
|
e00bc543ad | ||
|
|
1e8c040307 | ||
|
|
7c90c0205c | ||
|
|
a95439db61 | ||
|
|
f5fcb8a32c | ||
|
|
ce0cefe053 | ||
|
|
35e7103093 | ||
|
|
426a9755d9 | ||
|
|
c14bb310ce | ||
|
|
ccee5c781b | ||
|
|
d5934d65de | ||
|
|
39f9ab375b | ||
|
|
28a3cd1028 | ||
|
|
f987138760 | ||
|
|
1803c596dc | ||
|
|
6042fdd44c | ||
|
|
c56023163b | ||
|
|
1caa678592 | ||
|
|
ddaee7c1be | ||
|
|
c030a50228 | ||
|
|
e4c6db86b8 | ||
|
|
a7b63631b0 | ||
|
|
cdff23ad0b | ||
|
|
3635039d2b | ||
|
|
711e049432 | ||
|
|
57c4f24573 | ||
|
|
808f96cc8f | ||
|
|
2a9d0cc5af | ||
|
|
1fa4cfb004 | ||
|
|
44d2b36eff | ||
|
|
bb830b178a | ||
|
|
c6b1b30bb5 | ||
|
|
5f1430b0bd | ||
|
|
b818d43967 | ||
|
|
a21085b86b | ||
|
|
89c3761e5c | ||
|
|
893fc90379 | ||
|
|
41b1b3947c | ||
|
|
c23dab7adc | ||
|
|
cc75c6a6e9 |
No files matched your search
@@ -36,11 +36,6 @@ def FlagsForFile(filename):
|
||||
|
||||
flag_list = shlex.split(out.getvalue())
|
||||
|
||||
# This flag is added by Fennec for android build and causes ycmd to fail to parse the file.
|
||||
# Removing this flag is a workaround until ycmd starts to handle this flag properly.
|
||||
# https://github.com/Valloric/YouCompleteMe/issues/1490
|
||||
final_flags = [x for x in flag_list if not x.startswith('-march=armv')]
|
||||
|
||||
return {
|
||||
'flags': final_flags,
|
||||
'do_cache': True
|
||||
|
||||
@@ -22,4 +22,4 @@
|
||||
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
|
||||
# don't change CLOBBER for WebIDL changes any more.
|
||||
|
||||
Clobber for NSS update
|
||||
Clobber for NSS update
|
||||
@@ -9,8 +9,6 @@ selective cherry-picking of directly-applicable patches, this repository has its
|
||||
own development and holds the base for a maintained platform to be used by XUL
|
||||
applications.
|
||||
|
||||
For a list of active projects making use of the Unified XUL Platform, checkout http://thereisonlyxul.org/.
|
||||
|
||||
## Additional documentation
|
||||
|
||||
Additional documentation relevant to this source code can be found in the `/docs`
|
||||
@@ -23,7 +21,7 @@ You are also always welcome to get in touch with our community on the [Pale Moon
|
||||
|
||||
### A note about trademarks and branding
|
||||
|
||||
Although this repository is licensed under Mozilla Public License v2.0, the
|
||||
Although this repository is primarily licensed under Mozilla Public License v2.0, the
|
||||
trademarks and brands contained herein remain the property of their respective
|
||||
owners. For more details, please see the notifications in the respective directories.
|
||||
|
||||
|
||||
+2
-3
@@ -15,14 +15,14 @@ What should be considered vulnerabilities or security hazards by default:
|
||||
|
||||
Generally not security vulnerabilities:
|
||||
- Null dereferencing crashes;
|
||||
- Malware extensions (but please do report those on the forum in the extensions board!);
|
||||
- Malware extensions (but please do report those on the forum in the add-ons board!);
|
||||
- Denial-of-service (AKA "evil trap sites")
|
||||
- Browser hangs
|
||||
- Issues with non-standard manual configuration (either at build time or by manipulating about:config directly)
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
If you find an issue in UXP or the applications it builds on that could impact the security or safety of users please **do not**
|
||||
If you find an issue in UXP, or the applications building on it, that could impact the security or safety of users please **do not**
|
||||
make an issue on Gitea about it. Gitea does not support restricted viewability for security sensitive bugs.
|
||||
|
||||
If you want to report a security-sensitive issue then please go to the [forum](https://forum.palemoon.org) and report the issue
|
||||
@@ -33,4 +33,3 @@ except the recipient (not even moderators!).
|
||||
You will be informed via private message if the vulnerability report is accepted or declined, with reasoning.
|
||||
Security updates occur regularly and are given priority over most other development tasks. In general, they can be solved
|
||||
relatively quickly and will be included in the next point release (third digit if not rolled into a more major one).
|
||||
|
||||
@@ -5,9 +5,7 @@
|
||||
#include "AccIterator.h"
|
||||
|
||||
#include "AccGroupInfo.h"
|
||||
#ifdef MOZ_XUL
|
||||
#include "XULTreeAccessible.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/dom/HTMLLabelElement.h"
|
||||
|
||||
|
||||
@@ -341,8 +341,6 @@ LogRequest(nsIRequest* aRequest)
|
||||
printf("targeted; ");
|
||||
if (loadFlags & nsIChannel::LOAD_CALL_CONTENT_SNIFFERS)
|
||||
printf("call content sniffers; ");
|
||||
if (loadFlags & nsIChannel::LOAD_CLASSIFY_URI)
|
||||
printf("classify uri; ");
|
||||
} else {
|
||||
printf(" no request");
|
||||
}
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
|
||||
#include "mozilla/dom/TabChild.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
@@ -35,10 +35,10 @@ EPlatformDisabledState PlatformDisabledState();
|
||||
void PreInit();
|
||||
#endif
|
||||
|
||||
#if defined(MOZ_ACCESSIBILITY_ATK)
|
||||
#if defined(MOZ_ACCESSIBILITY_ATK) || defined(XP_MACOSX)
|
||||
/**
|
||||
* Is platform accessibility enabled.
|
||||
* Only used on linux with atk.
|
||||
* Only used on linux with atk and MacOS for now.
|
||||
*/
|
||||
bool ShouldA11yBeEnabled();
|
||||
#endif
|
||||
|
||||
@@ -96,6 +96,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
'/accessible/windows/ia2',
|
||||
'/accessible/windows/msaa',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/mac',
|
||||
]
|
||||
else:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/other',
|
||||
|
||||
@@ -68,7 +68,6 @@
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsDeckFrame.h"
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
#include "XULAlertAccessible.h"
|
||||
#include "XULColorPickerAccessible.h"
|
||||
#include "XULComboboxAccessible.h"
|
||||
@@ -79,7 +78,6 @@
|
||||
#include "XULSliderAccessible.h"
|
||||
#include "XULTabAccessible.h"
|
||||
#include "XULTreeGridAccessibleWrap.h"
|
||||
#endif
|
||||
|
||||
#if defined(XP_WIN) || defined(MOZ_ACCESSIBILITY_ATK)
|
||||
#include "nsNPAPIPluginInstance.h"
|
||||
@@ -1356,7 +1354,6 @@ nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
|
||||
}
|
||||
|
||||
RefPtr<Accessible> accessible;
|
||||
#ifdef MOZ_XUL
|
||||
// XUL controls
|
||||
if (role.EqualsLiteral("xul:alert")) {
|
||||
accessible = new XULAlertAccessible(aContent, aDoc);
|
||||
@@ -1515,7 +1512,6 @@ nsAccessibilityService::CreateAccessibleByType(nsIContent* aContent,
|
||||
accessible = new XULToolbarButtonAccessible(aContent, aDoc);
|
||||
|
||||
}
|
||||
#endif // MOZ_XUL
|
||||
|
||||
return accessible.forget();
|
||||
}
|
||||
@@ -1739,7 +1735,6 @@ nsAccessibilityService::HasAccessible(nsIDOMNode* aDOMNode)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// nsAccessibilityService private (DON'T put methods here)
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
already_AddRefed<Accessible>
|
||||
nsAccessibilityService::CreateAccessibleForXULTree(nsIContent* aContent,
|
||||
DocAccessible* aDoc)
|
||||
@@ -1769,7 +1764,6 @@ nsAccessibilityService::CreateAccessibleForXULTree(nsIContent* aContent,
|
||||
new XULTreeGridAccessibleWrap(aContent, aDoc, treeFrame);
|
||||
return accessible.forget();
|
||||
}
|
||||
#endif
|
||||
|
||||
nsAccessibilityService*
|
||||
GetOrCreateAccService(uint32_t aNewConsumer)
|
||||
|
||||
@@ -274,13 +274,11 @@ private:
|
||||
CreateAccessibleByFrameType(nsIFrame* aFrame, nsIContent* aContent,
|
||||
Accessible* aContext);
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
/**
|
||||
* Create accessible for XUL tree element.
|
||||
*/
|
||||
already_AddRefed<Accessible>
|
||||
CreateAccessibleForXULTree(nsIContent* aContent, DocAccessible* aDoc);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Reference for accessibility service instance.
|
||||
|
||||
@@ -48,9 +48,7 @@
|
||||
#include "mozilla/dom/DocumentType.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
#include "nsIXULDocument.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
@@ -210,11 +208,9 @@ DocAccessible::NativeRole()
|
||||
return roles::CHROME_WINDOW;
|
||||
|
||||
if (itemType == nsIDocShellTreeItem::typeContent) {
|
||||
#ifdef MOZ_XUL
|
||||
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocumentNode));
|
||||
if (xulDoc)
|
||||
return roles::APPLICATION;
|
||||
#endif
|
||||
return roles::DOCUMENT;
|
||||
}
|
||||
}
|
||||
@@ -387,13 +383,11 @@ DocAccessible::URL(nsAString& aURL) const
|
||||
void
|
||||
DocAccessible::DocType(nsAString& aType) const
|
||||
{
|
||||
#ifdef MOZ_XUL
|
||||
nsCOMPtr<nsIXULDocument> xulDoc(do_QueryInterface(mDocumentNode));
|
||||
if (xulDoc) {
|
||||
aType.AssignLiteral("window"); // doctype not implemented for XUL at time of writing - causes assertion
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
dom::DocumentType* docType = mDocumentNode->GetDoctype();
|
||||
if (docType)
|
||||
docType->GetPublicId(aType);
|
||||
|
||||
@@ -19,9 +19,7 @@
|
||||
#include "Relation.h"
|
||||
#include "Role.h"
|
||||
#include "States.h"
|
||||
#ifdef MOZ_XUL
|
||||
#include "XULTreeAccessible.h"
|
||||
#endif
|
||||
|
||||
#include "mozilla/dom/Element.h"
|
||||
|
||||
@@ -43,10 +41,8 @@
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
#include "nsIXULDocument.h"
|
||||
#include "nsIXULWindow.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
@@ -102,7 +98,6 @@ RootAccessible::NativeRole()
|
||||
}
|
||||
|
||||
// RootAccessible protected member
|
||||
#ifdef MOZ_XUL
|
||||
uint32_t
|
||||
RootAccessible::GetChromeFlags()
|
||||
{
|
||||
@@ -122,7 +117,6 @@ RootAccessible::GetChromeFlags()
|
||||
xulWin->GetChromeFlags(&chromeFlags);
|
||||
return chromeFlags;
|
||||
}
|
||||
#endif
|
||||
|
||||
uint64_t
|
||||
RootAccessible::NativeState()
|
||||
@@ -131,7 +125,6 @@ RootAccessible::NativeState()
|
||||
if (state & states::DEFUNCT)
|
||||
return state;
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
uint32_t chromeFlags = GetChromeFlags();
|
||||
if (chromeFlags & nsIWebBrowserChrome::CHROME_WINDOW_RESIZE)
|
||||
state |= states::SIZEABLE;
|
||||
@@ -142,7 +135,6 @@ RootAccessible::NativeState()
|
||||
state |= states::MOVEABLE;
|
||||
if (chromeFlags & nsIWebBrowserChrome::CHROME_MODAL)
|
||||
state |= states::MODAL;
|
||||
#endif
|
||||
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
if (fm && fm->GetActiveWindow() == mDocumentNode->GetWindow())
|
||||
@@ -292,7 +284,6 @@ RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
if (!accessible)
|
||||
return;
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
XULTreeAccessible* treeAcc = accessible->AsXULTree();
|
||||
if (treeAcc) {
|
||||
if (eventType.EqualsLiteral("TreeRowCountChanged")) {
|
||||
@@ -305,7 +296,6 @@ RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (eventType.EqualsLiteral("RadioStateChange")) {
|
||||
uint64_t state = accessible->State();
|
||||
@@ -341,7 +331,6 @@ RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
}
|
||||
|
||||
Accessible* treeItemAcc = nullptr;
|
||||
#ifdef MOZ_XUL
|
||||
// If it's a tree element, need the currently selected item.
|
||||
if (treeAcc) {
|
||||
treeItemAcc = accessible->CurrentItem();
|
||||
@@ -385,29 +374,22 @@ RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
nsEventShell::FireEvent(selChangeEvent);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (eventType.EqualsLiteral("AlertActive")) {
|
||||
} else if (eventType.EqualsLiteral("AlertActive")) {
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_ALERT, accessible);
|
||||
}
|
||||
else if (eventType.EqualsLiteral("popupshown")) {
|
||||
} else if (eventType.EqualsLiteral("popupshown")) {
|
||||
HandlePopupShownEvent(accessible);
|
||||
}
|
||||
else if (eventType.EqualsLiteral("DOMMenuInactive")) {
|
||||
} else if (eventType.EqualsLiteral("DOMMenuInactive")) {
|
||||
if (accessible->Role() == roles::MENUPOPUP) {
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENUPOPUP_END,
|
||||
accessible);
|
||||
}
|
||||
}
|
||||
else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
|
||||
} else if (eventType.EqualsLiteral("DOMMenuItemActive")) {
|
||||
FocusMgr()->ActiveItemChanged(accessible);
|
||||
#ifdef A11Y_LOG
|
||||
if (logging::IsEnabled(logging::eFocus))
|
||||
logging::ActiveItemChangeCausedBy("DOMMenuItemActive", accessible);
|
||||
#endif
|
||||
}
|
||||
else if (eventType.EqualsLiteral("DOMMenuItemInactive")) {
|
||||
} else if (eventType.EqualsLiteral("DOMMenuItemInactive")) {
|
||||
// Process DOMMenuItemInactive event for autocomplete only because this is
|
||||
// unique widget that may acquire focus from autocomplete popup while popup
|
||||
// stays open and has no active item. In case of XUL tree autocomplete
|
||||
@@ -421,8 +403,7 @@ RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
logging::ActiveItemChangeCausedBy("DOMMenuItemInactive", accessible);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always from user input
|
||||
} else if (eventType.EqualsLiteral("DOMMenuBarActive")) { // Always from user input
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_START,
|
||||
accessible, eFromUserInput);
|
||||
|
||||
@@ -440,8 +421,7 @@ RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
logging::ActiveItemChangeCausedBy("DOMMenuBarActive", accessible);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always from user input
|
||||
} else if (eventType.EqualsLiteral("DOMMenuBarInactive")) { // Always from user input
|
||||
nsEventShell::FireEvent(nsIAccessibleEvent::EVENT_MENU_END,
|
||||
accessible, eFromUserInput);
|
||||
|
||||
@@ -450,8 +430,7 @@ RootAccessible::ProcessDOMEvent(nsIDOMEvent* aDOMEvent)
|
||||
if (logging::IsEnabled(logging::eFocus))
|
||||
logging::ActiveItemChangeCausedBy("DOMMenuBarInactive", accessible);
|
||||
#endif
|
||||
}
|
||||
else if (accessible->NeedsDOMUIEvent() &&
|
||||
} else if (accessible->NeedsDOMUIEvent() &&
|
||||
eventType.EqualsLiteral("ValueChange")) {
|
||||
uint32_t event = accessible->HasNumericValue()
|
||||
? nsIAccessibleEvent::EVENT_VALUE_CHANGE
|
||||
@@ -650,7 +629,6 @@ RootAccessible::HandlePopupHidingEvent(nsINode* aPopupNode)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
void
|
||||
RootAccessible::HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
|
||||
XULTreeAccessible* aAccessible)
|
||||
@@ -714,7 +692,6 @@ RootAccessible::HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
|
||||
|
||||
aAccessible->TreeViewInvalidated(startRow, endRow, startCol, endCol);
|
||||
}
|
||||
#endif
|
||||
|
||||
ProxyAccessible*
|
||||
RootAccessible::GetPrimaryRemoteTopLevelContentDoc() const
|
||||
|
||||
@@ -70,14 +70,12 @@ protected:
|
||||
*/
|
||||
void HandlePopupHidingEvent(nsINode* aNode);
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
void HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
|
||||
XULTreeAccessible* aAccessible);
|
||||
void HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
|
||||
XULTreeAccessible* aAccessible);
|
||||
void HandleTreeRowCountChangedEvent(nsIDOMEvent* aEvent,
|
||||
XULTreeAccessible* aAccessible);
|
||||
void HandleTreeInvalidatedEvent(nsIDOMEvent* aEvent,
|
||||
XULTreeAccessible* aAccessible);
|
||||
|
||||
uint32_t GetChromeFlags();
|
||||
#endif
|
||||
uint32_t GetChromeFlags();
|
||||
};
|
||||
|
||||
inline RootAccessible*
|
||||
|
||||
@@ -52,6 +52,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
'/accessible/windows/ia2',
|
||||
'/accessible/windows/msaa',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/mac',
|
||||
]
|
||||
else:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/other',
|
||||
|
||||
@@ -32,6 +32,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
'/accessible/windows/ia2',
|
||||
'/accessible/windows/msaa',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/mac',
|
||||
]
|
||||
else:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/other',
|
||||
|
||||
@@ -19,6 +19,10 @@ else:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/atk',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/mac',
|
||||
]
|
||||
else:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/other',
|
||||
|
||||
@@ -28,6 +28,10 @@ if CONFIG['ACCESSIBILITY']:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/atk',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/mac',
|
||||
]
|
||||
else:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/other',
|
||||
|
||||
@@ -786,7 +786,7 @@ this.UtteranceGenerator = { // jshint ignore:line
|
||||
// This is because we expose the checked information on the node itself.
|
||||
// XXX: this means the checked state is always appended to the end,
|
||||
// regardless of the utterance ordering preference.
|
||||
if ((Utils.AndroidSdkVersion < 16 || Utils.MozBuildApp === 'browser') &&
|
||||
if (Utils.MozBuildApp === 'browser' &&
|
||||
aState.contains(States.CHECKABLE)) {
|
||||
let checked = aState.contains(States.CHECKED);
|
||||
let statetr;
|
||||
|
||||
@@ -39,13 +39,6 @@ var PointerRelay = { // jshint ignore:line
|
||||
delete this._eventsOfInterest;
|
||||
|
||||
switch (Utils.widgetToolkit) {
|
||||
case 'android':
|
||||
this._eventsOfInterest = {
|
||||
'touchstart' : true,
|
||||
'touchmove' : true,
|
||||
'touchend' : true };
|
||||
break;
|
||||
|
||||
default:
|
||||
// Desktop.
|
||||
this._eventsOfInterest = {
|
||||
@@ -110,11 +103,6 @@ var PointerRelay = { // jshint ignore:line
|
||||
target: aEvent.target
|
||||
}];
|
||||
|
||||
if (Utils.widgetToolkit === 'android' &&
|
||||
changedTouches.length === 1 && changedTouches[0].identifier === 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (changedTouches.length === 1 &&
|
||||
changedTouches[0].identifier === SYNTH_ID) {
|
||||
return;
|
||||
|
||||
@@ -257,11 +257,8 @@ AndroidPresenter.prototype.pivotChanged =
|
||||
|
||||
let androidEvents = [];
|
||||
|
||||
let isExploreByTouch = (aReason == Ci.nsIAccessiblePivot.REASON_POINT &&
|
||||
Utils.AndroidSdkVersion >= 14);
|
||||
let focusEventType = (Utils.AndroidSdkVersion >= 16) ?
|
||||
this.ANDROID_VIEW_ACCESSIBILITY_FOCUSED :
|
||||
this.ANDROID_VIEW_FOCUSED;
|
||||
let isExploreByTouch = (aReason == Ci.nsIAccessiblePivot.REASON_POINT);
|
||||
let focusEventType = this.ANDROID_VIEW_ACCESSIBILITY_FOCUSED;
|
||||
|
||||
if (isExploreByTouch) {
|
||||
// This isn't really used by TalkBack so this is a half-hearted attempt
|
||||
@@ -270,25 +267,21 @@ AndroidPresenter.prototype.pivotChanged =
|
||||
}
|
||||
|
||||
let brailleOutput = {};
|
||||
if (Utils.AndroidSdkVersion >= 16) {
|
||||
if (!this._braillePresenter) {
|
||||
this._braillePresenter = new BraillePresenter();
|
||||
}
|
||||
brailleOutput = this._braillePresenter.pivotChanged(aContext, aReason).
|
||||
details;
|
||||
if (!this._braillePresenter) {
|
||||
this._braillePresenter = new BraillePresenter();
|
||||
}
|
||||
brailleOutput = this._braillePresenter.pivotChanged(aContext, aReason).
|
||||
details;
|
||||
|
||||
if (aReason === Ci.nsIAccessiblePivot.REASON_TEXT) {
|
||||
if (Utils.AndroidSdkVersion >= 16) {
|
||||
let adjustedText = aContext.textAndAdjustedOffsets;
|
||||
let adjustedText = aContext.textAndAdjustedOffsets;
|
||||
|
||||
androidEvents.push({
|
||||
eventType: this.ANDROID_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
text: [adjustedText.text],
|
||||
fromIndex: adjustedText.startOffset,
|
||||
toIndex: adjustedText.endOffset
|
||||
});
|
||||
}
|
||||
androidEvents.push({
|
||||
eventType: this.ANDROID_VIEW_TEXT_TRAVERSED_AT_MOVEMENT_GRANULARITY,
|
||||
text: [adjustedText.text],
|
||||
fromIndex: adjustedText.startOffset,
|
||||
toIndex: adjustedText.endOffset
|
||||
});
|
||||
} else {
|
||||
let state = Utils.getState(aContext.accessible);
|
||||
androidEvents.push({eventType: (isExploreByTouch) ?
|
||||
@@ -371,7 +364,7 @@ AndroidPresenter.prototype.textSelectionChanged =
|
||||
aOldEnd, aIsFromUserInput) {
|
||||
let androidEvents = [];
|
||||
|
||||
if (Utils.AndroidSdkVersion >= 14 && !aIsFromUserInput) {
|
||||
if (!aIsFromUserInput) {
|
||||
if (!this._braillePresenter) {
|
||||
this._braillePresenter = new BraillePresenter();
|
||||
}
|
||||
@@ -388,7 +381,7 @@ AndroidPresenter.prototype.textSelectionChanged =
|
||||
});
|
||||
}
|
||||
|
||||
if (Utils.AndroidSdkVersion >= 16 && aIsFromUserInput) {
|
||||
if (aIsFromUserInput) {
|
||||
let [from, to] = aOldStart < aStart ?
|
||||
[aOldStart, aStart] : [aStart, aOldStart];
|
||||
androidEvents.push({
|
||||
@@ -407,10 +400,6 @@ AndroidPresenter.prototype.textSelectionChanged =
|
||||
|
||||
AndroidPresenter.prototype.viewportChanged =
|
||||
function AndroidPresenter_viewportChanged(aWindow, aCurrentContext) {
|
||||
if (Utils.AndroidSdkVersion < 14) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let events = [{
|
||||
eventType: this.ANDROID_VIEW_SCROLLED,
|
||||
text: [],
|
||||
@@ -420,7 +409,7 @@ AndroidPresenter.prototype.viewportChanged =
|
||||
maxScrollY: aWindow.scrollMaxY
|
||||
}];
|
||||
|
||||
if (Utils.AndroidSdkVersion >= 16 && aCurrentContext) {
|
||||
if (aCurrentContext) {
|
||||
let currentAcc = aCurrentContext.accessibleForBounds;
|
||||
if (Utils.isAliveAndVisible(currentAcc)) {
|
||||
events.push({
|
||||
@@ -447,8 +436,7 @@ AndroidPresenter.prototype.announce =
|
||||
return {
|
||||
type: this.type,
|
||||
details: [{
|
||||
eventType: (Utils.AndroidSdkVersion >= 16) ?
|
||||
this.ANDROID_ANNOUNCEMENT : this.ANDROID_VIEW_TEXT_CHANGED,
|
||||
eventType: this.ANDROID_ANNOUNCEMENT,
|
||||
text: [localizedAnnouncement],
|
||||
addedCount: localizedAnnouncement.length,
|
||||
removedCount: 0,
|
||||
|
||||
@@ -109,35 +109,13 @@ this.Utils = { // jshint ignore:line
|
||||
return this._ScriptName;
|
||||
},
|
||||
|
||||
get AndroidSdkVersion() {
|
||||
if (!this._AndroidSdkVersion) {
|
||||
if (Services.appinfo.OS == 'Android') {
|
||||
this._AndroidSdkVersion = Services.sysinfo.getPropertyAsInt32(
|
||||
'version');
|
||||
} else {
|
||||
// Most useful in desktop debugging.
|
||||
this._AndroidSdkVersion = 16;
|
||||
}
|
||||
}
|
||||
return this._AndroidSdkVersion;
|
||||
},
|
||||
|
||||
set AndroidSdkVersion(value) {
|
||||
// When we want to mimic another version.
|
||||
this._AndroidSdkVersion = value;
|
||||
},
|
||||
|
||||
get BrowserApp() {
|
||||
if (!this.win) {
|
||||
return null;
|
||||
}
|
||||
switch (this.MozBuildApp) {
|
||||
case 'mobile/android':
|
||||
return this.win.BrowserApp;
|
||||
case 'browser':
|
||||
return this.win.gBrowser;
|
||||
case 'b2g':
|
||||
return this.win.shell;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -147,9 +125,6 @@ this.Utils = { // jshint ignore:line
|
||||
if (!this.BrowserApp) {
|
||||
return null;
|
||||
}
|
||||
if (this.MozBuildApp == 'b2g') {
|
||||
return this.BrowserApp.contentBrowser;
|
||||
}
|
||||
return this.BrowserApp.selectedBrowser;
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=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_A11Y_ARIAGRIDACCESSIBLEWRAP_H
|
||||
#define MOZILLA_A11Y_ARIAGRIDACCESSIBLEWRAP_H
|
||||
|
||||
#include "ARIAGridAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class ARIAGridAccessible ARIAGridAccessibleWrap;
|
||||
typedef class ARIAGridCellAccessible ARIAGridCellAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,103 @@
|
||||
/* -*- Mode: Objective-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/. */
|
||||
|
||||
/* For documentation of the accessibility architecture,
|
||||
* see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
|
||||
*/
|
||||
|
||||
#ifndef _AccessibleWrap_H_
|
||||
#define _AccessibleWrap_H_
|
||||
|
||||
#include <objc/objc.h>
|
||||
|
||||
#include "Accessible.h"
|
||||
#include "States.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#include "nsTArray.h"
|
||||
|
||||
#if defined(__OBJC__)
|
||||
@class mozAccessible;
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class AccessibleWrap : public Accessible
|
||||
{
|
||||
public: // construction, destruction
|
||||
AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc);
|
||||
virtual ~AccessibleWrap();
|
||||
|
||||
/**
|
||||
* Get the native Obj-C object (mozAccessible).
|
||||
*/
|
||||
virtual void GetNativeInterface(void** aOutAccessible) override;
|
||||
|
||||
/**
|
||||
* The objective-c |Class| type that this accessible's native object
|
||||
* should be instantied with. used on runtime to determine the
|
||||
* right type for this accessible's associated native object.
|
||||
*/
|
||||
virtual Class GetNativeType ();
|
||||
|
||||
virtual void Shutdown () override;
|
||||
|
||||
virtual bool InsertChildAt(uint32_t aIdx, Accessible* aChild) override;
|
||||
virtual bool RemoveChild(Accessible* aAccessible) override;
|
||||
|
||||
virtual nsresult HandleAccEvent(AccEvent* aEvent) override;
|
||||
|
||||
protected:
|
||||
|
||||
/**
|
||||
* Return true if the parent doesn't have children to expose to AT.
|
||||
*/
|
||||
bool AncestorIsFlat();
|
||||
|
||||
/**
|
||||
* Get the native object. Create it if needed.
|
||||
*/
|
||||
#if defined(__OBJC__)
|
||||
mozAccessible* GetNativeObject();
|
||||
#else
|
||||
id GetNativeObject();
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
* Our native object. Private because its creation is done lazily.
|
||||
* Don't access it directly. Ever. Unless you are GetNativeObject() or
|
||||
* Shutdown()
|
||||
*/
|
||||
#if defined(__OBJC__)
|
||||
// if we are in Objective-C, we use the actual Obj-C class.
|
||||
mozAccessible* mNativeObject;
|
||||
#else
|
||||
id mNativeObject;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* We have created our native. This does not mean there is one.
|
||||
* This can never go back to false.
|
||||
* We need it because checking whether we need a native object cost time.
|
||||
*/
|
||||
bool mNativeInited;
|
||||
};
|
||||
|
||||
#if defined(__OBJC__)
|
||||
void FireNativeEvent(mozAccessible* aNativeAcc, uint32_t aEventType);
|
||||
#else
|
||||
void FireNativeEvent(id aNativeAcc, uint32_t aEventType);
|
||||
#endif
|
||||
|
||||
Class GetTypeFromRole(roles::Role aRole);
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,256 @@
|
||||
/* -*- 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 "DocAccessible.h"
|
||||
#include "nsObjCExceptions.h"
|
||||
|
||||
#include "Accessible-inl.h"
|
||||
#include "nsAccUtils.h"
|
||||
#include "Role.h"
|
||||
|
||||
#import "mozAccessible.h"
|
||||
#import "mozActionElements.h"
|
||||
#import "mozHTMLAccessible.h"
|
||||
#import "mozTableAccessible.h"
|
||||
#import "mozTextAccessible.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
AccessibleWrap::
|
||||
AccessibleWrap(nsIContent* aContent, DocAccessible* aDoc) :
|
||||
Accessible(aContent, aDoc), mNativeObject(nil),
|
||||
mNativeInited(false)
|
||||
{
|
||||
}
|
||||
|
||||
AccessibleWrap::~AccessibleWrap()
|
||||
{
|
||||
}
|
||||
|
||||
mozAccessible*
|
||||
AccessibleWrap::GetNativeObject()
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
if (!mNativeInited && !mNativeObject && !IsDefunct() && !AncestorIsFlat()) {
|
||||
uintptr_t accWrap = reinterpret_cast<uintptr_t>(this);
|
||||
mNativeObject = [[GetNativeType() alloc] initWithAccessible:accWrap];
|
||||
}
|
||||
|
||||
mNativeInited = true;
|
||||
|
||||
return mNativeObject;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
void
|
||||
AccessibleWrap::GetNativeInterface(void** aOutInterface)
|
||||
{
|
||||
*aOutInterface = static_cast<void*>(GetNativeObject());
|
||||
}
|
||||
|
||||
// overridden in subclasses to create the right kind of object. by default we create a generic
|
||||
// 'mozAccessible' node.
|
||||
Class
|
||||
AccessibleWrap::GetNativeType ()
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
if (IsXULTabpanels())
|
||||
return [mozPaneAccessible class];
|
||||
|
||||
if (IsTable())
|
||||
return [mozTableAccessible class];
|
||||
|
||||
if (IsTableRow())
|
||||
return [mozTableRowAccessible class];
|
||||
|
||||
if (IsTableCell())
|
||||
return [mozTableCellAccessible class];
|
||||
|
||||
return GetTypeFromRole(Role());
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
// this method is very important. it is fired when an accessible object "dies". after this point
|
||||
// the object might still be around (because some 3rd party still has a ref to it), but it is
|
||||
// in fact 'dead'.
|
||||
void
|
||||
AccessibleWrap::Shutdown ()
|
||||
{
|
||||
// this ensure we will not try to re-create the native object.
|
||||
mNativeInited = true;
|
||||
|
||||
// we really intend to access the member directly.
|
||||
if (mNativeObject) {
|
||||
[mNativeObject expire];
|
||||
[mNativeObject release];
|
||||
mNativeObject = nil;
|
||||
}
|
||||
|
||||
Accessible::Shutdown();
|
||||
}
|
||||
|
||||
nsresult
|
||||
AccessibleWrap::HandleAccEvent(AccEvent* aEvent)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
nsresult rv = Accessible::HandleAccEvent(aEvent);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
if (IPCAccessibilityActive()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
uint32_t eventType = aEvent->GetEventType();
|
||||
|
||||
// ignore everything but focus-changed, value-changed, caret, selection
|
||||
// and document load complete events for now.
|
||||
if (eventType != nsIAccessibleEvent::EVENT_FOCUS &&
|
||||
eventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE &&
|
||||
eventType != nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE &&
|
||||
eventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED &&
|
||||
eventType != nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED &&
|
||||
eventType != nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE)
|
||||
return NS_OK;
|
||||
|
||||
Accessible* accessible = aEvent->GetAccessible();
|
||||
NS_ENSURE_STATE(accessible);
|
||||
|
||||
mozAccessible *nativeAcc = nil;
|
||||
accessible->GetNativeInterface((void**)&nativeAcc);
|
||||
if (!nativeAcc)
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
FireNativeEvent(nativeAcc, eventType);
|
||||
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
||||
bool
|
||||
AccessibleWrap::InsertChildAt(uint32_t aIdx, Accessible* aAccessible)
|
||||
{
|
||||
bool inserted = Accessible::InsertChildAt(aIdx, aAccessible);
|
||||
if (inserted && mNativeObject)
|
||||
[mNativeObject appendChild:aAccessible];
|
||||
|
||||
return inserted;
|
||||
}
|
||||
|
||||
bool
|
||||
AccessibleWrap::RemoveChild(Accessible* aAccessible)
|
||||
{
|
||||
bool removed = Accessible::RemoveChild(aAccessible);
|
||||
|
||||
if (removed && mNativeObject)
|
||||
[mNativeObject invalidateChildren];
|
||||
|
||||
return removed;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// AccessibleWrap protected
|
||||
|
||||
bool
|
||||
AccessibleWrap::AncestorIsFlat()
|
||||
{
|
||||
// We don't create a native object if we're child of a "flat" accessible;
|
||||
// for example, on OS X buttons shouldn't have any children, because that
|
||||
// makes the OS confused.
|
||||
//
|
||||
// To maintain a scripting environment where the XPCOM accessible hierarchy
|
||||
// look the same on all platforms, we still let the C++ objects be created
|
||||
// though.
|
||||
|
||||
Accessible* parent = Parent();
|
||||
while (parent) {
|
||||
if (nsAccUtils::MustPrune(parent))
|
||||
return true;
|
||||
|
||||
parent = parent->Parent();
|
||||
}
|
||||
// no parent was flat
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
a11y::FireNativeEvent(mozAccessible* aNativeAcc, uint32_t aEventType)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
switch (aEventType) {
|
||||
case nsIAccessibleEvent::EVENT_FOCUS:
|
||||
[aNativeAcc didReceiveFocus];
|
||||
break;
|
||||
case nsIAccessibleEvent::EVENT_VALUE_CHANGE:
|
||||
case nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE:
|
||||
[aNativeAcc valueDidChange];
|
||||
break;
|
||||
case nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED:
|
||||
case nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED:
|
||||
[aNativeAcc selectedTextDidChange];
|
||||
break;
|
||||
case nsIAccessibleEvent::EVENT_DOCUMENT_LOAD_COMPLETE:
|
||||
[aNativeAcc documentLoadComplete];
|
||||
break;
|
||||
}
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
||||
Class
|
||||
a11y::GetTypeFromRole(roles::Role aRole)
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
switch (aRole) {
|
||||
case roles::COMBOBOX:
|
||||
case roles::PUSHBUTTON:
|
||||
case roles::SPLITBUTTON:
|
||||
case roles::TOGGLE_BUTTON:
|
||||
{
|
||||
return [mozButtonAccessible class];
|
||||
}
|
||||
|
||||
case roles::PAGETAB:
|
||||
return [mozButtonAccessible class];
|
||||
|
||||
case roles::CHECKBUTTON:
|
||||
return [mozCheckboxAccessible class];
|
||||
|
||||
case roles::HEADING:
|
||||
return [mozHeadingAccessible class];
|
||||
|
||||
case roles::PAGETABLIST:
|
||||
return [mozTabsAccessible class];
|
||||
|
||||
case roles::ENTRY:
|
||||
case roles::STATICTEXT:
|
||||
case roles::CAPTION:
|
||||
case roles::ACCEL_LABEL:
|
||||
case roles::PASSWORD_TEXT:
|
||||
// normal textfield (static or editable)
|
||||
return [mozTextAccessible class];
|
||||
|
||||
case roles::TEXT_LEAF:
|
||||
return [mozTextLeafAccessible class];
|
||||
|
||||
case roles::LINK:
|
||||
return [mozLinkAccessible class];
|
||||
|
||||
default:
|
||||
return [mozAccessible class];
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* vim:expandtab:shiftwidth=4:tabstop=4:
|
||||
*/
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_a11y_ApplicationAccessibleWrap_h__
|
||||
#define mozilla_a11y_ApplicationAccessibleWrap_h__
|
||||
|
||||
#include "ApplicationAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef ApplicationAccessible ApplicationAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
/* -*- 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_a11y_DocAccessibleWrap_h__
|
||||
#define mozilla_a11y_DocAccessibleWrap_h__
|
||||
|
||||
#include "DocAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class DocAccessibleWrap : public DocAccessible
|
||||
{
|
||||
public:
|
||||
DocAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell);
|
||||
virtual ~DocAccessibleWrap();
|
||||
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,21 @@
|
||||
/* -*- 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 "DocAccessibleWrap.h"
|
||||
|
||||
#import "mozAccessible.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
DocAccessibleWrap::
|
||||
DocAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell) :
|
||||
DocAccessible(aDocument, aPresShell)
|
||||
{
|
||||
}
|
||||
|
||||
DocAccessibleWrap::~DocAccessibleWrap()
|
||||
{
|
||||
}
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=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_a11y_HTMLTableAccessibleWrap_h__
|
||||
#define mozilla_a11y_HTMLTableAccessibleWrap_h__
|
||||
|
||||
#include "HTMLTableAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class HTMLTableAccessible HTMLTableAccessibleWrap;
|
||||
typedef class HTMLTableCellAccessible HTMLTableCellAccessibleWrap;
|
||||
typedef class HTMLTableHeaderCellAccessible HTMLTableHeaderCellAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/* -*- 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_a11y_HyperTextAccessibleWrap_h__
|
||||
#define mozilla_a11y_HyperTextAccessibleWrap_h__
|
||||
|
||||
#include "HyperTextAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class HyperTextAccessible HyperTextAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=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_a11y_ImageAccessibleWrap_h__
|
||||
#define mozilla_a11y_ImageAccessibleWrap_h__
|
||||
|
||||
#include "ImageAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class ImageAccessible ImageAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
/* -*- Mode: Objective-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 _MacUtils_H_
|
||||
#define _MacUtils_H_
|
||||
|
||||
@class NSString;
|
||||
class nsString;
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
namespace utils {
|
||||
|
||||
/**
|
||||
* Get a localized string from the string bundle.
|
||||
* Return nil if not found.
|
||||
*/
|
||||
NSString* LocalizedString(const nsString& aString);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,32 @@
|
||||
/* -*- Mode: Objective-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/. */
|
||||
|
||||
#import "MacUtils.h"
|
||||
|
||||
#include "Accessible.h"
|
||||
|
||||
#include "nsCocoaUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
namespace utils {
|
||||
|
||||
/**
|
||||
* Get a localized string from the a11y string bundle.
|
||||
* Return nil if not found.
|
||||
*/
|
||||
NSString*
|
||||
LocalizedString(const nsString& aString)
|
||||
{
|
||||
nsString text;
|
||||
|
||||
Accessible::TranslateString(aString, text);
|
||||
|
||||
return text.IsEmpty() ? nil : nsCocoaUtils::ToNSString(text);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
/* -*- Mode: Objective-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/. */
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#include "Platform.h"
|
||||
#include "ProxyAccessible.h"
|
||||
#include "DocAccessibleParent.h"
|
||||
#include "mozTableAccessible.h"
|
||||
|
||||
#include "nsAppShell.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
// Mac a11y whitelisting
|
||||
static bool sA11yShouldBeEnabled = false;
|
||||
|
||||
bool
|
||||
ShouldA11yBeEnabled()
|
||||
{
|
||||
EPlatformDisabledState disabledState = PlatformDisabledState();
|
||||
return (disabledState == ePlatformIsForceEnabled) || ((disabledState == ePlatformIsEnabled) && sA11yShouldBeEnabled);
|
||||
}
|
||||
|
||||
void
|
||||
PlatformInit()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
PlatformShutdown()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ProxyCreated(ProxyAccessible* aProxy, uint32_t)
|
||||
{
|
||||
// Pass in dummy state for now as retrieving proxy state requires IPC.
|
||||
// Note that we can use ProxyAccessible::IsTable* functions here because they
|
||||
// do not use IPC calls but that might change after bug 1210477.
|
||||
Class type;
|
||||
if (aProxy->IsTable())
|
||||
type = [mozTableAccessible class];
|
||||
else if (aProxy->IsTableRow())
|
||||
type = [mozTableRowAccessible class];
|
||||
else if (aProxy->IsTableCell())
|
||||
type = [mozTableCellAccessible class];
|
||||
else
|
||||
type = GetTypeFromRole(aProxy->Role());
|
||||
|
||||
uintptr_t accWrap = reinterpret_cast<uintptr_t>(aProxy) | IS_PROXY;
|
||||
mozAccessible* mozWrapper = [[type alloc] initWithAccessible:accWrap];
|
||||
aProxy->SetWrapper(reinterpret_cast<uintptr_t>(mozWrapper));
|
||||
|
||||
mozAccessible* nativeParent = nullptr;
|
||||
if (aProxy->IsDoc() && aProxy->AsDoc()->IsTopLevel()) {
|
||||
// If proxy is top level, the parent we need to invalidate the children of
|
||||
// will be a non-remote accessible.
|
||||
Accessible* outerDoc = aProxy->OuterDocOfRemoteBrowser();
|
||||
if (outerDoc) {
|
||||
nativeParent = GetNativeFromGeckoAccessible(outerDoc);
|
||||
}
|
||||
} else {
|
||||
// Non-top level proxies need proxy parents' children invalidated.
|
||||
ProxyAccessible* parent = aProxy->Parent();
|
||||
nativeParent = GetNativeFromProxy(parent);
|
||||
NS_ASSERTION(parent, "a non-top-level proxy is missing a parent?");
|
||||
}
|
||||
|
||||
if (nativeParent) {
|
||||
[nativeParent invalidateChildren];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ProxyDestroyed(ProxyAccessible* aProxy)
|
||||
{
|
||||
mozAccessible* nativeParent = nil;
|
||||
if (aProxy->IsDoc() && aProxy->AsDoc()->IsTopLevel()) {
|
||||
// Invalidate native parent in parent process's children on proxy destruction
|
||||
Accessible* outerDoc = aProxy->OuterDocOfRemoteBrowser();
|
||||
if (outerDoc) {
|
||||
nativeParent = GetNativeFromGeckoAccessible(outerDoc);
|
||||
}
|
||||
} else {
|
||||
if (!aProxy->Document()->IsShutdown()) {
|
||||
// Only do if the document has not been shut down, else parent will return
|
||||
// garbage since we don't shut down children from top down.
|
||||
ProxyAccessible* parent = aProxy->Parent();
|
||||
// Invalidate proxy parent's children.
|
||||
if (parent) {
|
||||
nativeParent = GetNativeFromProxy(parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mozAccessible* wrapper = GetNativeFromProxy(aProxy);
|
||||
[wrapper expire];
|
||||
[wrapper release];
|
||||
aProxy->SetWrapper(0);
|
||||
|
||||
if (nativeParent) {
|
||||
[nativeParent invalidateChildren];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ProxyEvent(ProxyAccessible* aProxy, uint32_t aEventType)
|
||||
{
|
||||
// ignore everything but focus-changed, value-changed, caret and selection
|
||||
// events for now.
|
||||
if (aEventType != nsIAccessibleEvent::EVENT_FOCUS &&
|
||||
aEventType != nsIAccessibleEvent::EVENT_VALUE_CHANGE &&
|
||||
aEventType != nsIAccessibleEvent::EVENT_TEXT_VALUE_CHANGE &&
|
||||
aEventType != nsIAccessibleEvent::EVENT_TEXT_CARET_MOVED &&
|
||||
aEventType != nsIAccessibleEvent::EVENT_TEXT_SELECTION_CHANGED)
|
||||
return;
|
||||
|
||||
mozAccessible* wrapper = GetNativeFromProxy(aProxy);
|
||||
if (wrapper)
|
||||
FireNativeEvent(wrapper, aEventType);
|
||||
}
|
||||
|
||||
void
|
||||
ProxyStateChangeEvent(ProxyAccessible* aProxy, uint64_t, bool)
|
||||
{
|
||||
// mac doesn't care about state change events
|
||||
}
|
||||
|
||||
void
|
||||
ProxyCaretMoveEvent(ProxyAccessible* aTarget, int32_t aOffset)
|
||||
{
|
||||
mozAccessible* wrapper = GetNativeFromProxy(aTarget);
|
||||
if (wrapper)
|
||||
[wrapper selectedTextDidChange];
|
||||
}
|
||||
|
||||
void
|
||||
ProxyTextChangeEvent(ProxyAccessible*, const nsString&, int32_t, uint32_t,
|
||||
bool, bool)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ProxyShowHideEvent(ProxyAccessible*, ProxyAccessible*, bool, bool)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ProxySelectionEvent(ProxyAccessible*, ProxyAccessible*, uint32_t)
|
||||
{
|
||||
}
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
@interface GeckoNSApplication(a11y)
|
||||
-(void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute;
|
||||
@end
|
||||
|
||||
@implementation GeckoNSApplication(a11y)
|
||||
|
||||
-(void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute
|
||||
{
|
||||
if ([attribute isEqualToString:@"AXEnhancedUserInterface"])
|
||||
mozilla::a11y::sA11yShouldBeEnabled = ([value intValue] == 1);
|
||||
|
||||
return [super accessibilitySetValue:value forAttribute:attribute];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/* -*- 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/. */
|
||||
|
||||
/* For documentation of the accessibility architecture,
|
||||
* see http://lxr.mozilla.org/seamonkey/source/accessible/accessible-docs.html
|
||||
*/
|
||||
|
||||
#ifndef mozilla_a11y_RootAccessibleWrap_h__
|
||||
#define mozilla_a11y_RootAccessibleWrap_h__
|
||||
|
||||
#include "RootAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
class RootAccessibleWrap : public RootAccessible
|
||||
{
|
||||
public:
|
||||
RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell);
|
||||
virtual ~RootAccessibleWrap();
|
||||
|
||||
Class GetNativeType ();
|
||||
|
||||
// let's our native accessible get in touch with the
|
||||
// native cocoa view that is our accessible parent.
|
||||
void GetNativeWidget (void **aOutView);
|
||||
};
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,53 @@
|
||||
/* -*- 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 "RootAccessibleWrap.h"
|
||||
|
||||
#include "mozDocAccessible.h"
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsObjCExceptions.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsView.h"
|
||||
#include "nsIWidget.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
RootAccessibleWrap::
|
||||
RootAccessibleWrap(nsIDocument* aDocument, nsIPresShell* aPresShell) :
|
||||
RootAccessible(aDocument, aPresShell)
|
||||
{
|
||||
}
|
||||
|
||||
RootAccessibleWrap::~RootAccessibleWrap()
|
||||
{
|
||||
}
|
||||
|
||||
Class
|
||||
RootAccessibleWrap::GetNativeType()
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
return [mozRootAccessible class];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
void
|
||||
RootAccessibleWrap::GetNativeWidget(void** aOutView)
|
||||
{
|
||||
nsIFrame *frame = GetFrame();
|
||||
if (frame) {
|
||||
nsView *view = frame->GetView();
|
||||
if (view) {
|
||||
nsIWidget *widget = view->GetWidget();
|
||||
if (widget) {
|
||||
*aOutView = (void**)widget->GetNativeData (NS_NATIVE_WIDGET);
|
||||
NS_ASSERTION (*aOutView,
|
||||
"Couldn't get the native NSView parent we need to connect the accessibility hierarchy!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/* -*- 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_a11y_TextLeafAccessibleWrap_h__
|
||||
#define mozilla_a11y_TextLeafAccessibleWrap_h__
|
||||
|
||||
#include "TextLeafAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class TextLeafAccessible TextLeafAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
/* -*- 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_a11y_XULListboxAccessibleWrap_h__
|
||||
#define mozilla_a11y_XULListboxAccessibleWrap_h__
|
||||
|
||||
#include "XULListboxAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class XULListboxAccessible XULListboxAccessibleWrap;
|
||||
typedef class XULListCellAccessible XULListCellAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,19 @@
|
||||
/* -*- 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_a11y_XULMenuAccessibleWrap_h__
|
||||
#define mozilla_a11y_XULMenuAccessibleWrap_h__
|
||||
|
||||
#include "XULMenuAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class XULMenuitemAccessible XULMenuitemAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
/* -*- 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_a11y_XULTreeGridAccessibleWrap_h__
|
||||
#define mozilla_a11y_XULTreeGridAccessibleWrap_h__
|
||||
|
||||
#include "XULTreeGridAccessible.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
typedef class XULTreeGridAccessible XULTreeGridAccessibleWrap;
|
||||
typedef class XULTreeGridCellAccessible XULTreeGridCellAccessibleWrap;
|
||||
|
||||
} // namespace a11y
|
||||
} // namespace mozilla
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,44 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# 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/.
|
||||
|
||||
EXPORTS += [
|
||||
'mozAccessibleProtocol.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.a11y += [
|
||||
'AccessibleWrap.h',
|
||||
'HyperTextAccessibleWrap.h',
|
||||
]
|
||||
|
||||
SOURCES += [
|
||||
'AccessibleWrap.mm',
|
||||
'DocAccessibleWrap.mm',
|
||||
'MacUtils.mm',
|
||||
'mozAccessible.mm',
|
||||
'mozActionElements.mm',
|
||||
'mozDocAccessible.mm',
|
||||
'mozHTMLAccessible.mm',
|
||||
'mozTableAccessible.mm',
|
||||
'mozTextAccessible.mm',
|
||||
'Platform.mm',
|
||||
'RootAccessibleWrap.mm',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/base',
|
||||
'/accessible/generic',
|
||||
'/accessible/html',
|
||||
'/accessible/ipc',
|
||||
'/accessible/ipc/other',
|
||||
'/accessible/xul',
|
||||
'/layout/generic',
|
||||
'/layout/xul',
|
||||
'/widget',
|
||||
'/widget/cocoa',
|
||||
]
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
@@ -0,0 +1,181 @@
|
||||
/* -*- Mode: Objective-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 "AccessibleWrap.h"
|
||||
#include "ProxyAccessible.h"
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "mozAccessibleProtocol.h"
|
||||
|
||||
@class mozRootAccessible;
|
||||
|
||||
/**
|
||||
* All mozAccessibles are either abstract objects (that correspond to XUL
|
||||
* widgets, HTML frames, etc) or are attached to a certain view; for example
|
||||
* a document view. When we hand an object off to an AT, we always want
|
||||
* to give it the represented view, in the latter case.
|
||||
*/
|
||||
|
||||
namespace mozilla {
|
||||
namespace a11y {
|
||||
|
||||
inline id <mozAccessible>
|
||||
GetObjectOrRepresentedView(id <mozAccessible> aObject)
|
||||
{
|
||||
return [aObject hasRepresentedView] ? [aObject representedView] : aObject;
|
||||
}
|
||||
|
||||
inline mozAccessible*
|
||||
GetNativeFromGeckoAccessible(Accessible* aAccessible)
|
||||
{
|
||||
mozAccessible* native = nil;
|
||||
aAccessible->GetNativeInterface((void**)&native);
|
||||
return native;
|
||||
}
|
||||
|
||||
inline mozAccessible*
|
||||
GetNativeFromProxy(const ProxyAccessible* aProxy)
|
||||
{
|
||||
return reinterpret_cast<mozAccessible*>(aProxy->GetWrapper());
|
||||
}
|
||||
|
||||
} // a11y
|
||||
} // mozilla
|
||||
|
||||
// This is OR'd with the Accessible owner to indicate the wrap-ee is a proxy.
|
||||
static const uintptr_t IS_PROXY = 1;
|
||||
|
||||
@interface mozAccessible : NSObject <mozAccessible>
|
||||
{
|
||||
/**
|
||||
* Weak reference; it owns us.
|
||||
*/
|
||||
uintptr_t mGeckoAccessible;
|
||||
|
||||
/**
|
||||
* Strong ref to array of children
|
||||
*/
|
||||
NSMutableArray* mChildren;
|
||||
|
||||
/**
|
||||
* Weak reference to the parent
|
||||
*/
|
||||
mozAccessible* mParent;
|
||||
|
||||
/**
|
||||
* The role of our gecko accessible.
|
||||
*/
|
||||
mozilla::a11y::role mRole;
|
||||
}
|
||||
|
||||
// return the Accessible for this mozAccessible if it exists.
|
||||
- (mozilla::a11y::AccessibleWrap*)getGeckoAccessible;
|
||||
|
||||
// return the ProxyAccessible for this mozAccessible if it exists.
|
||||
- (mozilla::a11y::ProxyAccessible*)getProxyAccessible;
|
||||
|
||||
// inits with the gecko owner.
|
||||
- (id)initWithAccessible:(uintptr_t)aGeckoObj;
|
||||
|
||||
// our accessible parent (AXParent)
|
||||
- (id <mozAccessible>)parent;
|
||||
|
||||
// a lazy cache of our accessible children (AXChildren). updated
|
||||
- (NSArray*)children;
|
||||
|
||||
// returns the size of this accessible.
|
||||
- (NSValue*)size;
|
||||
|
||||
// returns the position, in cocoa coordinates.
|
||||
- (NSValue*)position;
|
||||
|
||||
// can be overridden to report another role name.
|
||||
- (NSString*)role;
|
||||
|
||||
// a subrole is a more specialized variant of the role. for example,
|
||||
// the role might be "textfield", while the subrole is "password textfield".
|
||||
- (NSString*)subrole;
|
||||
|
||||
// Return the role description, as there are a few exceptions.
|
||||
- (NSString*)roleDescription;
|
||||
|
||||
// returns the native window we're inside.
|
||||
- (NSWindow*)window;
|
||||
|
||||
// the value of this element.
|
||||
- (id)value;
|
||||
|
||||
// name that is associated with this accessible (for buttons, etc)
|
||||
- (NSString*)title;
|
||||
|
||||
// the accessible description (help text) of this particular instance.
|
||||
- (NSString*)help;
|
||||
|
||||
- (BOOL)isEnabled;
|
||||
|
||||
// information about focus.
|
||||
- (BOOL)isFocused;
|
||||
- (BOOL)canBeFocused;
|
||||
|
||||
// returns NO if for some reason we were unable to focus the element.
|
||||
- (BOOL)focus;
|
||||
|
||||
// notifications sent out to listening accessible providers.
|
||||
- (void)didReceiveFocus;
|
||||
- (void)valueDidChange;
|
||||
- (void)selectedTextDidChange;
|
||||
- (void)documentLoadComplete;
|
||||
|
||||
// internal method to retrieve a child at a given index.
|
||||
- (id)childAt:(uint32_t)i;
|
||||
|
||||
#pragma mark -
|
||||
|
||||
// invalidates and removes all our children from our cached array.
|
||||
- (void)invalidateChildren;
|
||||
|
||||
/**
|
||||
* Append a child if they are already cached.
|
||||
*/
|
||||
- (void)appendChild:(mozilla::a11y::Accessible*)aAccessible;
|
||||
|
||||
// makes ourselves "expired". after this point, we might be around if someone
|
||||
// has retained us (e.g., a third-party), but we really contain no information.
|
||||
- (void)expire;
|
||||
- (BOOL)isExpired;
|
||||
|
||||
#ifdef DEBUG
|
||||
- (void)printHierarchy;
|
||||
- (void)printHierarchyWithLevel:(unsigned)numSpaces;
|
||||
|
||||
- (void)sanityCheckChildren;
|
||||
- (void)sanityCheckChildren:(NSArray*)theChildren;
|
||||
#endif
|
||||
|
||||
// ---- NSAccessibility methods ---- //
|
||||
|
||||
// whether to skip this element when traversing the accessibility
|
||||
// hierarchy.
|
||||
- (BOOL)accessibilityIsIgnored;
|
||||
|
||||
// called by third-parties to determine the deepest child element under the mouse
|
||||
- (id)accessibilityHitTest:(NSPoint)point;
|
||||
|
||||
// returns the deepest unignored focused accessible element
|
||||
- (id)accessibilityFocusedUIElement;
|
||||
|
||||
// a mozAccessible needs to at least provide links to its parent and
|
||||
// children.
|
||||
- (NSArray*)accessibilityAttributeNames;
|
||||
|
||||
// value for the specified attribute
|
||||
- (id)accessibilityAttributeValue:(NSString*)attribute;
|
||||
|
||||
- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute;
|
||||
- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute;
|
||||
|
||||
@end
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,69 @@
|
||||
/* -*- Mode: Objective-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/. */
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
|
||||
#import "mozView.h"
|
||||
|
||||
/* This protocol's primary use is so widget/cocoa can talk back to us
|
||||
properly.
|
||||
|
||||
ChildView owns the topmost mozRootAccessible, and needs to take care of setting up
|
||||
that parent/child relationship.
|
||||
|
||||
This protocol is thus used to make sure it knows it's talking to us, and not
|
||||
just some random |id|.
|
||||
*/
|
||||
|
||||
@protocol mozAccessible
|
||||
|
||||
// returns whether this accessible is the root accessible. there is one
|
||||
// root accessible per window.
|
||||
- (BOOL)isRoot;
|
||||
|
||||
// some mozAccessibles implement accessibility support in place of another object. for example,
|
||||
// ChildView gets its support from us.
|
||||
//
|
||||
// instead of returning a mozAccessible to the OS when it wants an object, we need to pass the view we represent, so the
|
||||
// OS doesn't get confused and think we return some random object.
|
||||
- (BOOL)hasRepresentedView;
|
||||
- (id)representedView;
|
||||
|
||||
#ifdef DEBUG
|
||||
// debug utility that will print the native accessibility tree, starting
|
||||
// at this node.
|
||||
- (void)printHierarchy;
|
||||
#endif
|
||||
|
||||
/*** general ***/
|
||||
|
||||
// returns the accessible at the specified point.
|
||||
- (id)accessibilityHitTest:(NSPoint)point;
|
||||
|
||||
// whether this element is flagged as ignored.
|
||||
- (BOOL)accessibilityIsIgnored;
|
||||
|
||||
// currently focused UI element (possibly a child accessible)
|
||||
- (id)accessibilityFocusedUIElement;
|
||||
|
||||
/*** attributes ***/
|
||||
|
||||
// all supported attributes
|
||||
- (NSArray*)accessibilityAttributeNames;
|
||||
|
||||
// value for given attribute.
|
||||
- (id)accessibilityAttributeValue:(NSString*)attribute;
|
||||
|
||||
// whether a particular attribute can be modified
|
||||
- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute;
|
||||
|
||||
/*** actions ***/
|
||||
|
||||
- (NSArray*)accessibilityActionNames;
|
||||
- (NSString*)accessibilityActionDescription:(NSString*)action;
|
||||
- (void)accessibilityPerformAction:(NSString*)action;
|
||||
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/* -*- Mode: Objective-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/. */
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "mozAccessible.h"
|
||||
|
||||
/* Simple subclasses for things like checkboxes, buttons, etc. */
|
||||
|
||||
@interface mozButtonAccessible : mozAccessible
|
||||
{
|
||||
}
|
||||
- (BOOL)hasPopup;
|
||||
- (void)click;
|
||||
- (BOOL)isTab;
|
||||
@end
|
||||
|
||||
@interface mozCheckboxAccessible : mozButtonAccessible
|
||||
// returns one of the constants defined in CheckboxValue
|
||||
- (int)isChecked;
|
||||
@end
|
||||
|
||||
/* Class for tabs - not individual tabs */
|
||||
@interface mozTabsAccessible : mozAccessible
|
||||
{
|
||||
NSMutableArray* mTabs;
|
||||
}
|
||||
-(id)tabs;
|
||||
@end
|
||||
|
||||
/**
|
||||
* Accessible for a PANE
|
||||
*/
|
||||
@interface mozPaneAccessible : mozAccessible
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,340 @@
|
||||
/* -*- Mode: Objective-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/. */
|
||||
|
||||
#import "mozActionElements.h"
|
||||
|
||||
#import "MacUtils.h"
|
||||
#include "Accessible-inl.h"
|
||||
#include "DocAccessible.h"
|
||||
#include "XULTabAccessible.h"
|
||||
|
||||
#include "nsDeckFrame.h"
|
||||
#include "nsObjCExceptions.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
enum CheckboxValue {
|
||||
// these constants correspond to the values in the OS
|
||||
kUnchecked = 0,
|
||||
kChecked = 1,
|
||||
kMixed = 2
|
||||
};
|
||||
|
||||
@implementation mozButtonAccessible
|
||||
|
||||
- (NSArray*)accessibilityAttributeNames
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
static NSArray *attributes = nil;
|
||||
if (!attributes) {
|
||||
attributes = [[NSArray alloc] initWithObjects:NSAccessibilityParentAttribute, // required
|
||||
NSAccessibilityRoleAttribute, // required
|
||||
NSAccessibilityRoleDescriptionAttribute,
|
||||
NSAccessibilityPositionAttribute, // required
|
||||
NSAccessibilitySizeAttribute, // required
|
||||
NSAccessibilityWindowAttribute, // required
|
||||
NSAccessibilityPositionAttribute, // required
|
||||
NSAccessibilityTopLevelUIElementAttribute, // required
|
||||
NSAccessibilityHelpAttribute,
|
||||
NSAccessibilityEnabledAttribute, // required
|
||||
NSAccessibilityFocusedAttribute, // required
|
||||
NSAccessibilityTitleAttribute, // required
|
||||
NSAccessibilityChildrenAttribute,
|
||||
NSAccessibilityDescriptionAttribute,
|
||||
#if DEBUG
|
||||
@"AXMozDescription",
|
||||
#endif
|
||||
nil];
|
||||
}
|
||||
return attributes;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString *)attribute
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
|
||||
if ([self hasPopup])
|
||||
return [self children];
|
||||
return nil;
|
||||
}
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute]) {
|
||||
if ([self isTab])
|
||||
return utils::LocalizedString(NS_LITERAL_STRING("tab"));
|
||||
|
||||
return NSAccessibilityRoleDescription([self role], nil);
|
||||
}
|
||||
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (BOOL)accessibilityIsIgnored
|
||||
{
|
||||
return ![self getGeckoAccessible] && ![self getProxyAccessible];
|
||||
}
|
||||
|
||||
- (NSArray*)accessibilityActionNames
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
if ([self isEnabled]) {
|
||||
if ([self hasPopup])
|
||||
return [NSArray arrayWithObjects:NSAccessibilityPressAction,
|
||||
NSAccessibilityShowMenuAction,
|
||||
nil];
|
||||
return [NSArray arrayWithObject:NSAccessibilityPressAction];
|
||||
}
|
||||
return nil;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (NSString*)accessibilityActionDescription:(NSString*)action
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
if ([action isEqualToString:NSAccessibilityPressAction]) {
|
||||
if ([self isTab])
|
||||
return utils::LocalizedString(NS_LITERAL_STRING("switch"));
|
||||
|
||||
return @"press button"; // XXX: localize this later?
|
||||
}
|
||||
|
||||
if ([self hasPopup]) {
|
||||
if ([action isEqualToString:NSAccessibilityShowMenuAction])
|
||||
return @"show menu";
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (void)accessibilityPerformAction:(NSString*)action
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
if ([self isEnabled] && [action isEqualToString:NSAccessibilityPressAction]) {
|
||||
// TODO: this should bring up the menu, but currently doesn't.
|
||||
// once msaa and atk have merged better, they will implement
|
||||
// the action needed to show the menu.
|
||||
[self click];
|
||||
}
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
||||
- (void)click
|
||||
{
|
||||
// both buttons and checkboxes have only one action. we should really stop using arbitrary
|
||||
// arrays with actions, and define constants for these actions.
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
||||
accWrap->DoAction(0);
|
||||
else if (ProxyAccessible* proxy = [self getProxyAccessible])
|
||||
proxy->DoAction(0);
|
||||
}
|
||||
|
||||
- (BOOL)isTab
|
||||
{
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
||||
return accWrap->Role() == roles::PAGETAB;
|
||||
|
||||
if (ProxyAccessible* proxy = [self getProxyAccessible])
|
||||
return proxy->Role() == roles::PAGETAB;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
- (BOOL)hasPopup
|
||||
{
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
||||
return accWrap->NativeState() & states::HASPOPUP;
|
||||
|
||||
if (ProxyAccessible* proxy = [self getProxyAccessible])
|
||||
return proxy->NativeState() & states::HASPOPUP;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation mozCheckboxAccessible
|
||||
|
||||
- (NSString*)accessibilityActionDescription:(NSString*)action
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
if ([action isEqualToString:NSAccessibilityPressAction]) {
|
||||
if ([self isChecked] != kUnchecked)
|
||||
return @"uncheck checkbox"; // XXX: localize this later?
|
||||
|
||||
return @"check checkbox"; // XXX: localize this later?
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (int)isChecked
|
||||
{
|
||||
uint64_t state = 0;
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible])
|
||||
state = accWrap->NativeState();
|
||||
else if (ProxyAccessible* proxy = [self getProxyAccessible])
|
||||
state = proxy->NativeState();
|
||||
|
||||
// check if we're checked or in a mixed state
|
||||
if (state & states::CHECKED) {
|
||||
return (state & states::MIXED) ? kMixed : kChecked;
|
||||
}
|
||||
|
||||
return kUnchecked;
|
||||
}
|
||||
|
||||
- (id)value
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
return [NSNumber numberWithInt:[self isChecked]];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation mozTabsAccessible
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[mTabs release];
|
||||
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSArray*)accessibilityAttributeNames
|
||||
{
|
||||
// standard attributes that are shared and supported by root accessible (AXMain) elements.
|
||||
static NSMutableArray* attributes = nil;
|
||||
|
||||
if (!attributes) {
|
||||
attributes = [[super accessibilityAttributeNames] mutableCopy];
|
||||
[attributes addObject:NSAccessibilityContentsAttribute];
|
||||
[attributes addObject:NSAccessibilityTabsAttribute];
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString *)attribute
|
||||
{
|
||||
if ([attribute isEqualToString:NSAccessibilityContentsAttribute])
|
||||
return [super children];
|
||||
if ([attribute isEqualToString:NSAccessibilityTabsAttribute])
|
||||
return [self tabs];
|
||||
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the selected tab (the mozAccessible)
|
||||
*/
|
||||
- (id)value
|
||||
{
|
||||
mozAccessible* nativeAcc = nil;
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
||||
if (Accessible* accTab = accWrap->GetSelectedItem(0)) {
|
||||
accTab->GetNativeInterface((void**)&nativeAcc);
|
||||
}
|
||||
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
if (ProxyAccessible* proxyTab = proxy->GetSelectedItem(0)) {
|
||||
nativeAcc = GetNativeFromProxy(proxyTab);
|
||||
}
|
||||
}
|
||||
|
||||
return nativeAcc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the mozAccessibles that are the tabs.
|
||||
*/
|
||||
- (id)tabs
|
||||
{
|
||||
if (mTabs)
|
||||
return mTabs;
|
||||
|
||||
NSArray* children = [self children];
|
||||
NSEnumerator* enumerator = [children objectEnumerator];
|
||||
mTabs = [[NSMutableArray alloc] init];
|
||||
|
||||
id obj;
|
||||
while ((obj = [enumerator nextObject]))
|
||||
if ([obj isTab])
|
||||
[mTabs addObject:obj];
|
||||
|
||||
return mTabs;
|
||||
}
|
||||
|
||||
- (void)invalidateChildren
|
||||
{
|
||||
[super invalidateChildren];
|
||||
|
||||
[mTabs release];
|
||||
mTabs = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation mozPaneAccessible
|
||||
|
||||
- (NSUInteger)accessibilityArrayAttributeCount:(NSString*)attribute
|
||||
{
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
ProxyAccessible* proxy = [self getProxyAccessible];
|
||||
if (!accWrap && !proxy)
|
||||
return 0;
|
||||
|
||||
// By default this calls -[[mozAccessible children] count].
|
||||
// Since we don't cache mChildren. This is faster.
|
||||
if ([attribute isEqualToString:NSAccessibilityChildrenAttribute]) {
|
||||
if (accWrap)
|
||||
return accWrap->ChildCount() ? 1 : 0;
|
||||
|
||||
return proxy->ChildrenCount() ? 1 : 0;
|
||||
}
|
||||
|
||||
return [super accessibilityArrayAttributeCount:attribute];
|
||||
}
|
||||
|
||||
- (NSArray*)children
|
||||
{
|
||||
if (![self getGeckoAccessible])
|
||||
return nil;
|
||||
|
||||
nsDeckFrame* deckFrame = do_QueryFrame([self getGeckoAccessible]->GetFrame());
|
||||
nsIFrame* selectedFrame = deckFrame ? deckFrame->GetSelectedBox() : nullptr;
|
||||
|
||||
Accessible* selectedAcc = nullptr;
|
||||
if (selectedFrame) {
|
||||
nsINode* node = selectedFrame->GetContent();
|
||||
selectedAcc = [self getGeckoAccessible]->Document()->GetAccessible(node);
|
||||
}
|
||||
|
||||
if (selectedAcc) {
|
||||
mozAccessible *curNative = GetNativeFromGeckoAccessible(selectedAcc);
|
||||
if (curNative)
|
||||
return [NSArray arrayWithObjects:GetObjectOrRepresentedView(curNative), nil];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,31 @@
|
||||
/* -*- Mode: Objective-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/. */
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import "mozAccessible.h"
|
||||
|
||||
// our protocol that we implement (so cocoa widgets can talk to us)
|
||||
#import "mozAccessibleProtocol.h"
|
||||
|
||||
/*
|
||||
The root accessible. There is one per window.
|
||||
Created by the RootAccessibleWrap.
|
||||
*/
|
||||
@interface mozRootAccessible : mozAccessible
|
||||
{
|
||||
// the mozView that we're representing.
|
||||
// all outside communication goes through the mozView.
|
||||
// in reality, it's just piping all calls to us, and we're
|
||||
// doing its dirty work!
|
||||
//
|
||||
// whenever someone asks who we are (e.g., a child asking
|
||||
// for its parent, or our parent asking for its child), we'll
|
||||
// respond the mozView. it is absolutely necessary for third-
|
||||
// party tools that we do this!
|
||||
//
|
||||
// /hwaara
|
||||
id <mozView, mozAccessible> mParallelView; // weak ref
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,111 @@
|
||||
/* -*- Mode: Objective-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 "RootAccessibleWrap.h"
|
||||
|
||||
#import "mozDocAccessible.h"
|
||||
|
||||
#import "mozView.h"
|
||||
|
||||
// This must be included last:
|
||||
#include "nsObjCExceptions.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
static id <mozAccessible, mozView>
|
||||
getNativeViewFromRootAccessible(Accessible* aAccessible)
|
||||
{
|
||||
RootAccessibleWrap* root =
|
||||
static_cast<RootAccessibleWrap*>(aAccessible->AsRoot());
|
||||
id <mozAccessible, mozView> nativeView = nil;
|
||||
root->GetNativeWidget ((void**)&nativeView);
|
||||
return nativeView;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
@implementation mozRootAccessible
|
||||
|
||||
- (NSArray*)accessibilityAttributeNames
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
// if we're expired, we don't support any attributes.
|
||||
if (![self getGeckoAccessible])
|
||||
return [NSArray array];
|
||||
|
||||
// standard attributes that are shared and supported by root accessible (AXMain) elements.
|
||||
static NSMutableArray* attributes = nil;
|
||||
|
||||
if (!attributes) {
|
||||
attributes = [[super accessibilityAttributeNames] mutableCopy];
|
||||
[attributes addObject:NSAccessibilityMainAttribute];
|
||||
[attributes addObject:NSAccessibilityMinimizedAttribute];
|
||||
}
|
||||
|
||||
return attributes;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString *)attribute
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityMainAttribute])
|
||||
return [NSNumber numberWithBool:[[self window] isMainWindow]];
|
||||
if ([attribute isEqualToString:NSAccessibilityMinimizedAttribute])
|
||||
return [NSNumber numberWithBool:[[self window] isMiniaturized]];
|
||||
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
|
||||
// return the AXParent that our parallell NSView tells us about.
|
||||
- (id)parent
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
if (!mParallelView)
|
||||
mParallelView = (id<mozView, mozAccessible>)[self representedView];
|
||||
|
||||
if (mParallelView)
|
||||
return [mParallelView accessibilityAttributeValue:NSAccessibilityParentAttribute];
|
||||
|
||||
NSAssert(mParallelView, @"we're a root accessible w/o native view?");
|
||||
return [super parent];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (BOOL)hasRepresentedView
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
// this will return our parallell NSView. see mozDocAccessible.h
|
||||
- (id)representedView
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
if (mParallelView)
|
||||
return (id)mParallelView;
|
||||
|
||||
mParallelView = getNativeViewFromRootAccessible ([self getGeckoAccessible]);
|
||||
|
||||
NSAssert(mParallelView, @"can't return root accessible's native parallel view.");
|
||||
return mParallelView;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (BOOL)isRoot
|
||||
{
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,16 @@
|
||||
/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=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/. */
|
||||
|
||||
#import "mozAccessible.h"
|
||||
|
||||
@interface mozHeadingAccessible : mozAccessible
|
||||
|
||||
@end
|
||||
|
||||
@interface mozLinkAccessible : mozAccessible
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,141 @@
|
||||
/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=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/. */
|
||||
|
||||
#import "mozHTMLAccessible.h"
|
||||
|
||||
#import "Accessible-inl.h"
|
||||
#import "HyperTextAccessible.h"
|
||||
|
||||
#import "nsCocoaUtils.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
@implementation mozHeadingAccessible
|
||||
|
||||
- (NSString*)title
|
||||
{
|
||||
nsAutoString title;
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
||||
mozilla::ErrorResult rv;
|
||||
// XXX use the flattening API when there are available
|
||||
// see bug 768298
|
||||
accWrap->GetContent()->GetTextContent(title, rv);
|
||||
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
proxy->Title(title);
|
||||
}
|
||||
|
||||
return nsCocoaUtils::ToNSString(title);
|
||||
}
|
||||
|
||||
- (id)value
|
||||
{
|
||||
uint32_t level = 0;
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
||||
level = accWrap->GetLevelInternal();
|
||||
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
level = proxy->GetLevelInternal();
|
||||
}
|
||||
|
||||
return [NSNumber numberWithInt:level];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface mozLinkAccessible ()
|
||||
-(NSURL*)url;
|
||||
@end
|
||||
|
||||
@implementation mozLinkAccessible
|
||||
|
||||
- (NSArray*)accessibilityAttributeNames
|
||||
{
|
||||
// if we're expired, we don't support any attributes.
|
||||
if (![self getGeckoAccessible] && ![self getProxyAccessible])
|
||||
return [NSArray array];
|
||||
|
||||
static NSMutableArray* attributes = nil;
|
||||
|
||||
if (!attributes) {
|
||||
attributes = [[super accessibilityAttributeNames] mutableCopy];
|
||||
[attributes addObject:NSAccessibilityURLAttribute];
|
||||
}
|
||||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString *)attribute
|
||||
{
|
||||
if ([attribute isEqualToString:NSAccessibilityURLAttribute])
|
||||
return [self url];
|
||||
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
}
|
||||
|
||||
- (NSArray*)accessibilityActionNames
|
||||
{
|
||||
// if we're expired, we don't support any attributes.
|
||||
if (![self getGeckoAccessible] && ![self getProxyAccessible])
|
||||
return [NSArray array];
|
||||
|
||||
static NSArray* actionNames = nil;
|
||||
|
||||
if (!actionNames) {
|
||||
actionNames = [[NSArray alloc] initWithObjects:NSAccessibilityPressAction,
|
||||
nil];
|
||||
}
|
||||
|
||||
return actionNames;
|
||||
}
|
||||
|
||||
- (void)accessibilityPerformAction:(NSString*)action
|
||||
{
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
ProxyAccessible* proxy = [self getProxyAccessible];
|
||||
if (!accWrap && !proxy) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ([action isEqualToString:NSAccessibilityPressAction]) {
|
||||
if (accWrap) {
|
||||
accWrap->DoAction(0);
|
||||
} else if (proxy) {
|
||||
proxy->DoAction(0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
[super accessibilityPerformAction:action];
|
||||
|
||||
}
|
||||
|
||||
- (NSString*)customDescription
|
||||
{
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (NSString*)value
|
||||
{
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (NSURL*)url
|
||||
{
|
||||
nsAutoString value;
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
||||
accWrap->Value(value);
|
||||
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
proxy->Value(value);
|
||||
}
|
||||
|
||||
NSString* urlString = value.IsEmpty() ? nil : nsCocoaUtils::ToNSString(value);
|
||||
if (!urlString)
|
||||
return nil;
|
||||
|
||||
return [NSURL URLWithString:urlString];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,28 @@
|
||||
/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=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/. */
|
||||
|
||||
#import "mozAccessible.h"
|
||||
|
||||
@interface mozTablePartAccessible : mozAccessible
|
||||
- (BOOL)isLayoutTablePart;
|
||||
- (NSString*)role;
|
||||
@end
|
||||
|
||||
@interface mozTableAccessible : mozTablePartAccessible
|
||||
- (NSArray*)additionalAccessibilityAttributeNames;
|
||||
- (id)accessibilityAttributeValue:(NSString*)attribute;
|
||||
@end
|
||||
|
||||
@interface mozTableRowAccessible : mozTablePartAccessible
|
||||
- (NSArray*)additionalAccessibilityAttributeNames;
|
||||
- (id)accessibilityAttributeValue:(NSString*)attribute;
|
||||
@end
|
||||
|
||||
@interface mozTableCellAccessible : mozTablePartAccessible
|
||||
- (NSArray*)additionalAccessibilityAttributeNames;
|
||||
- (id)accessibilityAttributeValue:(NSString*)attribute;
|
||||
@end
|
||||
@@ -0,0 +1,281 @@
|
||||
/* -*- Mode: Objective-C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim:expandtab:shiftwidth=2:tabstop=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/. */
|
||||
|
||||
#import "Accessible-inl.h"
|
||||
#import "mozTableAccessible.h"
|
||||
#import "TableAccessible.h"
|
||||
#import "TableCellAccessible.h"
|
||||
#import "nsCocoaUtils.h"
|
||||
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
// convert an array of Gecko accessibles to an NSArray of native accessibles
|
||||
static inline NSMutableArray*
|
||||
ConvertToNSArray(nsTArray<Accessible*>& aArray)
|
||||
{
|
||||
NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
|
||||
|
||||
// iterate through the list, and get each native accessible.
|
||||
size_t totalCount = aArray.Length();
|
||||
for (size_t i = 0; i < totalCount; i++) {
|
||||
Accessible* curAccessible = aArray.ElementAt(i);
|
||||
mozAccessible* curNative = GetNativeFromGeckoAccessible(curAccessible);
|
||||
if (curNative)
|
||||
[nativeArray addObject:GetObjectOrRepresentedView(curNative)];
|
||||
}
|
||||
|
||||
return nativeArray;
|
||||
}
|
||||
|
||||
// convert an array of Gecko proxy accessibles to an NSArray of native accessibles
|
||||
static inline NSMutableArray*
|
||||
ConvertToNSArray(nsTArray<ProxyAccessible*>& aArray)
|
||||
{
|
||||
NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
|
||||
|
||||
// iterate through the list, and get each native accessible.
|
||||
size_t totalCount = aArray.Length();
|
||||
for (size_t i = 0; i < totalCount; i++) {
|
||||
ProxyAccessible* curAccessible = aArray.ElementAt(i);
|
||||
mozAccessible* curNative = GetNativeFromProxy(curAccessible);
|
||||
if (curNative)
|
||||
[nativeArray addObject:GetObjectOrRepresentedView(curNative)];
|
||||
}
|
||||
|
||||
return nativeArray;
|
||||
}
|
||||
|
||||
@implementation mozTablePartAccessible
|
||||
- (BOOL)isLayoutTablePart;
|
||||
{
|
||||
if (Accessible* accWrap = [self getGeckoAccessible]) {
|
||||
while (accWrap) {
|
||||
if (accWrap->IsTable()) {
|
||||
return accWrap->AsTable()->IsProbablyLayoutTable();
|
||||
}
|
||||
accWrap = accWrap->Parent();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
while (proxy) {
|
||||
if (proxy->IsTable()) {
|
||||
return proxy->TableIsProbablyForLayout();
|
||||
}
|
||||
proxy = proxy->Parent();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
- (NSString*)role
|
||||
{
|
||||
return [self isLayoutTablePart] ? NSAccessibilityGroupRole : [super role];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation mozTableAccessible
|
||||
- (NSArray*)additionalAccessibilityAttributeNames
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
NSArray* additionalAttributes = [super additionalAccessibilityAttributeNames];
|
||||
if ([self isLayoutTablePart]) {
|
||||
return additionalAttributes;
|
||||
}
|
||||
|
||||
static NSArray* tableAttrs = nil;
|
||||
if (!tableAttrs) {
|
||||
NSMutableArray* tempArray = [NSMutableArray new];
|
||||
[tempArray addObject:NSAccessibilityRowCountAttribute];
|
||||
[tempArray addObject:NSAccessibilityColumnCountAttribute];
|
||||
[tempArray addObject:NSAccessibilityRowsAttribute];
|
||||
tableAttrs = [[NSArray alloc] initWithArray:tempArray];
|
||||
[tempArray release];
|
||||
}
|
||||
|
||||
return [additionalAttributes arrayByAddingObjectsFromArray:tableAttrs];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString*)attribute
|
||||
{
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
||||
TableAccessible* table = accWrap->AsTable();
|
||||
if ([attribute isEqualToString:NSAccessibilityRowCountAttribute])
|
||||
return @(table->RowCount());
|
||||
if ([attribute isEqualToString:NSAccessibilityColumnCountAttribute])
|
||||
return @(table->ColCount());
|
||||
if ([attribute isEqualToString:NSAccessibilityRowsAttribute]) {
|
||||
// Create a new array with the list of table rows.
|
||||
NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
|
||||
uint32_t totalCount = accWrap->ChildCount();
|
||||
for (uint32_t i = 0; i < totalCount; i++) {
|
||||
if (accWrap->GetChildAt(i)->IsTableRow()) {
|
||||
mozAccessible* curNative =
|
||||
GetNativeFromGeckoAccessible(accWrap->GetChildAt(i));
|
||||
if (curNative)
|
||||
[nativeArray addObject:GetObjectOrRepresentedView(curNative)];
|
||||
}
|
||||
}
|
||||
return nativeArray;
|
||||
}
|
||||
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
if ([attribute isEqualToString:NSAccessibilityRowCountAttribute])
|
||||
return @(proxy->TableRowCount());
|
||||
if ([attribute isEqualToString:NSAccessibilityColumnCountAttribute])
|
||||
return @(proxy->TableColumnCount());
|
||||
if ([attribute isEqualToString:NSAccessibilityRowsAttribute]) {
|
||||
// Create a new array with the list of table rows.
|
||||
NSMutableArray* nativeArray = [[NSMutableArray alloc] init];
|
||||
uint32_t totalCount = proxy->ChildrenCount();
|
||||
for (uint32_t i = 0; i < totalCount; i++) {
|
||||
if (proxy->ChildAt(i)->IsTableRow()) {
|
||||
mozAccessible* curNative =
|
||||
GetNativeFromProxy(proxy->ChildAt(i));
|
||||
if (curNative)
|
||||
[nativeArray addObject:GetObjectOrRepresentedView(curNative)];
|
||||
}
|
||||
}
|
||||
return nativeArray;
|
||||
}
|
||||
}
|
||||
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation mozTableRowAccessible
|
||||
- (NSArray*)additionalAccessibilityAttributeNames
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
NSArray* additionalAttributes = [super additionalAccessibilityAttributeNames];
|
||||
if ([self isLayoutTablePart]) {
|
||||
return additionalAttributes;
|
||||
}
|
||||
|
||||
static NSArray* tableRowAttrs = nil;
|
||||
if (!tableRowAttrs) {
|
||||
NSMutableArray* tempArray = [NSMutableArray new];
|
||||
[tempArray addObject:NSAccessibilityIndexAttribute];
|
||||
tableRowAttrs = [[NSArray alloc] initWithArray:tempArray];
|
||||
[tempArray release];
|
||||
}
|
||||
|
||||
return [additionalAttributes arrayByAddingObjectsFromArray:tableRowAttrs];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString*)attribute
|
||||
{
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
||||
if ([attribute isEqualToString:NSAccessibilityIndexAttribute]) {
|
||||
// Count the number of rows before that one to obtain the row index.
|
||||
uint32_t index = 0;
|
||||
Accessible* parent = accWrap->Parent();
|
||||
if (parent) {
|
||||
for (int32_t i = accWrap->IndexInParent() - 1; i >= 0; i--) {
|
||||
if (parent->GetChildAt(i)->IsTableRow()) {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [NSNumber numberWithUnsignedInteger:index];
|
||||
}
|
||||
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
if ([attribute isEqualToString:NSAccessibilityIndexAttribute]) {
|
||||
// Count the number of rows before that one to obtain the row index.
|
||||
uint32_t index = 0;
|
||||
ProxyAccessible* parent = proxy->Parent();
|
||||
if (parent) {
|
||||
for (int32_t i = proxy->IndexInParent() - 1; i >= 0; i--) {
|
||||
if (parent->ChildAt(i)->IsTableRow()) {
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return [NSNumber numberWithUnsignedInteger:index];
|
||||
}
|
||||
}
|
||||
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
}
|
||||
@end
|
||||
|
||||
@implementation mozTableCellAccessible
|
||||
- (NSArray*)additionalAccessibilityAttributeNames
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
NSArray* additionalAttributes = [super additionalAccessibilityAttributeNames];
|
||||
if ([self isLayoutTablePart]) {
|
||||
return additionalAttributes;
|
||||
}
|
||||
|
||||
static NSArray* tableCellAttrs = nil;
|
||||
if (!tableCellAttrs) {
|
||||
NSMutableArray* tempArray = [NSMutableArray new];
|
||||
[tempArray addObject:NSAccessibilityRowIndexRangeAttribute];
|
||||
[tempArray addObject:NSAccessibilityColumnIndexRangeAttribute];
|
||||
[tempArray addObject:NSAccessibilityRowHeaderUIElementsAttribute];
|
||||
[tempArray addObject:NSAccessibilityColumnHeaderUIElementsAttribute];
|
||||
tableCellAttrs = [[NSArray alloc] initWithArray:tempArray];
|
||||
[tempArray release];
|
||||
}
|
||||
|
||||
return [additionalAttributes arrayByAddingObjectsFromArray:tableCellAttrs];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString*)attribute
|
||||
{
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
||||
TableCellAccessible* cell = accWrap->AsTableCell();
|
||||
if ([attribute isEqualToString:NSAccessibilityRowIndexRangeAttribute])
|
||||
return [NSValue valueWithRange:NSMakeRange(cell->RowIdx(),
|
||||
cell->RowExtent())];
|
||||
if ([attribute isEqualToString:NSAccessibilityColumnIndexRangeAttribute])
|
||||
return [NSValue valueWithRange:NSMakeRange(cell->ColIdx(),
|
||||
cell->ColExtent())];
|
||||
if ([attribute isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) {
|
||||
AutoTArray<Accessible*, 10> headerCells;
|
||||
cell->RowHeaderCells(&headerCells);
|
||||
return ConvertToNSArray(headerCells);
|
||||
}
|
||||
if ([attribute isEqualToString:NSAccessibilityColumnHeaderUIElementsAttribute]) {
|
||||
AutoTArray<Accessible*, 10> headerCells;
|
||||
cell->ColHeaderCells(&headerCells);
|
||||
return ConvertToNSArray(headerCells);
|
||||
}
|
||||
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
if ([attribute isEqualToString:NSAccessibilityRowIndexRangeAttribute])
|
||||
return [NSValue valueWithRange:NSMakeRange(proxy->RowIdx(),
|
||||
proxy->RowExtent())];
|
||||
if ([attribute isEqualToString:NSAccessibilityColumnIndexRangeAttribute])
|
||||
return [NSValue valueWithRange:NSMakeRange(proxy->ColIdx(),
|
||||
proxy->ColExtent())];
|
||||
if ([attribute isEqualToString:NSAccessibilityRowHeaderUIElementsAttribute]) {
|
||||
nsTArray<ProxyAccessible*> headerCells;
|
||||
proxy->RowHeaderCells(&headerCells);
|
||||
return ConvertToNSArray(headerCells);
|
||||
}
|
||||
if ([attribute isEqualToString:NSAccessibilityColumnHeaderUIElementsAttribute]) {
|
||||
nsTArray<ProxyAccessible*> headerCells;
|
||||
proxy->ColHeaderCells(&headerCells);
|
||||
return ConvertToNSArray(headerCells);
|
||||
}
|
||||
}
|
||||
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
}
|
||||
@end
|
||||
@@ -1,9 +1,17 @@
|
||||
/* -*- Mode: IDL; tab-width: 8; 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/. */
|
||||
|
||||
[Pref="dom.archivereader.enabled"]
|
||||
interface ArchiveRequest : DOMRequest {
|
||||
readonly attribute ArchiveReader reader;
|
||||
};
|
||||
#import "mozAccessible.h"
|
||||
|
||||
#import "HyperTextAccessible.h"
|
||||
|
||||
@interface mozTextAccessible : mozAccessible
|
||||
{
|
||||
}
|
||||
@end
|
||||
|
||||
@interface mozTextLeafAccessible : mozAccessible
|
||||
{
|
||||
}
|
||||
@end
|
||||
@@ -0,0 +1,627 @@
|
||||
/* -*- Mode: Objective-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 "Accessible-inl.h"
|
||||
#include "HyperTextAccessible-inl.h"
|
||||
#include "TextLeafAccessible.h"
|
||||
|
||||
#include "nsCocoaUtils.h"
|
||||
#include "nsObjCExceptions.h"
|
||||
|
||||
#import "mozTextAccessible.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::a11y;
|
||||
|
||||
inline bool
|
||||
ToNSRange(id aValue, NSRange* aRange)
|
||||
{
|
||||
NS_PRECONDITION(aRange, "aRange is nil");
|
||||
|
||||
if ([aValue isKindOfClass:[NSValue class]] &&
|
||||
strcmp([(NSValue*)aValue objCType], @encode(NSRange)) == 0) {
|
||||
*aRange = [aValue rangeValue];
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline NSString*
|
||||
ToNSString(id aValue)
|
||||
{
|
||||
if ([aValue isKindOfClass:[NSString class]]) {
|
||||
return aValue;
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
@interface mozTextAccessible ()
|
||||
- (NSString*)subrole;
|
||||
- (NSString*)selectedText;
|
||||
- (NSValue*)selectedTextRange;
|
||||
- (NSValue*)visibleCharacterRange;
|
||||
- (long)textLength;
|
||||
- (BOOL)isReadOnly;
|
||||
- (NSNumber*)caretLineNumber;
|
||||
- (void)setText:(NSString*)newText;
|
||||
- (NSString*)text;
|
||||
- (NSString*)stringFromRange:(NSRange*)range;
|
||||
@end
|
||||
|
||||
@implementation mozTextAccessible
|
||||
|
||||
- (BOOL)accessibilityIsIgnored
|
||||
{
|
||||
return ![self getGeckoAccessible] && ![self getProxyAccessible];
|
||||
}
|
||||
|
||||
- (NSArray*)accessibilityAttributeNames
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
static NSMutableArray* supportedAttributes = nil;
|
||||
if (!supportedAttributes) {
|
||||
// text-specific attributes to supplement the standard one
|
||||
supportedAttributes = [[NSMutableArray alloc] initWithObjects:
|
||||
NSAccessibilitySelectedTextAttribute, // required
|
||||
NSAccessibilitySelectedTextRangeAttribute, // required
|
||||
NSAccessibilityNumberOfCharactersAttribute, // required
|
||||
NSAccessibilityVisibleCharacterRangeAttribute, // required
|
||||
NSAccessibilityInsertionPointLineNumberAttribute,
|
||||
@"AXRequired",
|
||||
@"AXInvalid",
|
||||
nil
|
||||
];
|
||||
[supportedAttributes addObjectsFromArray:[super accessibilityAttributeNames]];
|
||||
}
|
||||
return supportedAttributes;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString*)attribute
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityNumberOfCharactersAttribute])
|
||||
return [NSNumber numberWithInt:[self textLength]];
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityInsertionPointLineNumberAttribute])
|
||||
return [self caretLineNumber];
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute])
|
||||
return [self selectedTextRange];
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute])
|
||||
return [self selectedText];
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
|
||||
return @"";
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
|
||||
// Apple's SpeechSynthesisServer expects AXValue to return an AXStaticText
|
||||
// object's AXSelectedText attribute. See bug 674612 for details.
|
||||
// Also if there is no selected text, we return the full text.
|
||||
// See bug 369710 for details.
|
||||
if ([[self role] isEqualToString:NSAccessibilityStaticTextRole]) {
|
||||
NSString* selectedText = [self selectedText];
|
||||
return (selectedText && [selectedText length]) ? selectedText : [self text];
|
||||
}
|
||||
|
||||
return [self text];
|
||||
}
|
||||
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
||||
if ([attribute isEqualToString:@"AXRequired"]) {
|
||||
return [NSNumber numberWithBool:!!(accWrap->State() & states::REQUIRED)];
|
||||
}
|
||||
|
||||
if ([attribute isEqualToString:@"AXInvalid"]) {
|
||||
return [NSNumber numberWithBool:!!(accWrap->State() & states::INVALID)];
|
||||
}
|
||||
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
if ([attribute isEqualToString:@"AXRequired"]) {
|
||||
return [NSNumber numberWithBool:!!(proxy->State() & states::REQUIRED)];
|
||||
}
|
||||
|
||||
if ([attribute isEqualToString:@"AXInvalid"]) {
|
||||
return [NSNumber numberWithBool:!!(proxy->State() & states::INVALID)];
|
||||
}
|
||||
}
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute])
|
||||
return [self visibleCharacterRange];
|
||||
|
||||
// let mozAccessible handle all other attributes
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (NSArray*)accessibilityParameterizedAttributeNames
|
||||
{
|
||||
static NSArray* supportedParametrizedAttributes = nil;
|
||||
// text specific parametrized attributes
|
||||
if (!supportedParametrizedAttributes) {
|
||||
supportedParametrizedAttributes = [[NSArray alloc] initWithObjects:
|
||||
NSAccessibilityStringForRangeParameterizedAttribute,
|
||||
NSAccessibilityLineForIndexParameterizedAttribute,
|
||||
NSAccessibilityRangeForLineParameterizedAttribute,
|
||||
NSAccessibilityAttributedStringForRangeParameterizedAttribute,
|
||||
NSAccessibilityBoundsForRangeParameterizedAttribute,
|
||||
#if DEBUG
|
||||
NSAccessibilityRangeForPositionParameterizedAttribute,
|
||||
NSAccessibilityRangeForIndexParameterizedAttribute,
|
||||
NSAccessibilityRTFForRangeParameterizedAttribute,
|
||||
NSAccessibilityStyleRangeForIndexParameterizedAttribute,
|
||||
#endif
|
||||
nil
|
||||
];
|
||||
}
|
||||
return supportedParametrizedAttributes;
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString*)attribute forParameter:(id)parameter
|
||||
{
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
ProxyAccessible* proxy = [self getProxyAccessible];
|
||||
|
||||
HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
|
||||
if (!textAcc && !proxy)
|
||||
return nil;
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityStringForRangeParameterizedAttribute]) {
|
||||
NSRange range;
|
||||
if (!ToNSRange(parameter, &range)) {
|
||||
#if DEBUG
|
||||
NSLog(@"%@: range not set", attribute);
|
||||
#endif
|
||||
return @"";
|
||||
}
|
||||
|
||||
return [self stringFromRange:&range];
|
||||
}
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityRangeForLineParameterizedAttribute]) {
|
||||
// XXX: actually get the integer value for the line #
|
||||
return [NSValue valueWithRange:NSMakeRange(0, [self textLength])];
|
||||
}
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityAttributedStringForRangeParameterizedAttribute]) {
|
||||
NSRange range;
|
||||
if (!ToNSRange(parameter, &range)) {
|
||||
#if DEBUG
|
||||
NSLog(@"%@: range not set", attribute);
|
||||
#endif
|
||||
return @"";
|
||||
}
|
||||
|
||||
return [[[NSAttributedString alloc] initWithString:[self stringFromRange:&range]] autorelease];
|
||||
}
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityLineForIndexParameterizedAttribute]) {
|
||||
// XXX: actually return the line #
|
||||
return [NSNumber numberWithInt:0];
|
||||
}
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityBoundsForRangeParameterizedAttribute]) {
|
||||
NSRange range;
|
||||
if (!ToNSRange(parameter, &range)) {
|
||||
#if DEBUG
|
||||
NSLog(@"%@:no range", attribute);
|
||||
#endif
|
||||
return nil;
|
||||
}
|
||||
|
||||
int32_t start = range.location;
|
||||
int32_t end = start + range.length;
|
||||
DesktopIntRect bounds;
|
||||
if (textAcc) {
|
||||
bounds =
|
||||
DesktopIntRect::FromUnknownRect(textAcc->TextBounds(start, end));
|
||||
} else if (proxy) {
|
||||
bounds =
|
||||
DesktopIntRect::FromUnknownRect(proxy->TextBounds(start, end));
|
||||
}
|
||||
|
||||
return [NSValue valueWithRect:nsCocoaUtils::GeckoRectToCocoaRect(bounds)];
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
NSLog(@"unhandled attribute:%@ forParameter:%@", attribute, parameter);
|
||||
#endif
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityValueAttribute])
|
||||
return ![self isReadOnly];
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute] ||
|
||||
[attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute] ||
|
||||
[attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute])
|
||||
return YES;
|
||||
|
||||
return [super accessibilityIsAttributeSettable:attribute];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
||||
}
|
||||
|
||||
- (void)accessibilitySetValue:(id)value forAttribute:(NSString *)attribute
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
ProxyAccessible* proxy = [self getProxyAccessible];
|
||||
|
||||
HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
|
||||
if (!textAcc && !proxy)
|
||||
return;
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityValueAttribute]) {
|
||||
[self setText:ToNSString(value)];
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilitySelectedTextAttribute]) {
|
||||
NSString* stringValue = ToNSString(value);
|
||||
if (!stringValue)
|
||||
return;
|
||||
|
||||
int32_t start = 0, end = 0;
|
||||
nsString text;
|
||||
if (textAcc) {
|
||||
textAcc->SelectionBoundsAt(0, &start, &end);
|
||||
textAcc->DeleteText(start, end - start);
|
||||
nsCocoaUtils::GetStringForNSString(stringValue, text);
|
||||
textAcc->InsertText(text, start);
|
||||
} else if (proxy) {
|
||||
nsString data;
|
||||
proxy->SelectionBoundsAt(0, data, &start, &end);
|
||||
proxy->DeleteText(start, end - start);
|
||||
nsCocoaUtils::GetStringForNSString(stringValue, text);
|
||||
proxy->InsertText(text, start);
|
||||
}
|
||||
}
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilitySelectedTextRangeAttribute]) {
|
||||
NSRange range;
|
||||
if (!ToNSRange(value, &range))
|
||||
return;
|
||||
|
||||
if (textAcc) {
|
||||
textAcc->SetSelectionBoundsAt(0, range.location,
|
||||
range.location + range.length);
|
||||
} else if (proxy) {
|
||||
proxy->SetSelectionBoundsAt(0, range.location,
|
||||
range.location + range.length);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityVisibleCharacterRangeAttribute]) {
|
||||
NSRange range;
|
||||
if (!ToNSRange(value, &range))
|
||||
return;
|
||||
|
||||
if (textAcc) {
|
||||
textAcc->ScrollSubstringTo(range.location, range.location + range.length,
|
||||
nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE);
|
||||
} else if (proxy) {
|
||||
proxy->ScrollSubstringTo(range.location, range.location + range.length,
|
||||
nsIAccessibleScrollType::SCROLL_TYPE_TOP_EDGE);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
[super accessibilitySetValue:value forAttribute:attribute];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
||||
- (NSString*)subrole
|
||||
{
|
||||
if(mRole == roles::PASSWORD_TEXT)
|
||||
return NSAccessibilitySecureTextFieldSubrole;
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
#pragma mark -
|
||||
|
||||
- (BOOL)isReadOnly
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
||||
|
||||
if ([[self role] isEqualToString:NSAccessibilityStaticTextRole])
|
||||
return YES;
|
||||
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
|
||||
if (textAcc)
|
||||
return (accWrap->State() & states::READONLY) == 0;
|
||||
|
||||
if (ProxyAccessible* proxy = [self getProxyAccessible])
|
||||
return (proxy->State() & states::READONLY) == 0;
|
||||
|
||||
return NO;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
|
||||
}
|
||||
|
||||
- (NSNumber*)caretLineNumber
|
||||
{
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
|
||||
|
||||
int32_t lineNumber = -1;
|
||||
if (textAcc) {
|
||||
lineNumber = textAcc->CaretLineNumber() - 1;
|
||||
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
lineNumber = proxy->CaretLineNumber() - 1;
|
||||
}
|
||||
|
||||
return (lineNumber >= 0) ? [NSNumber numberWithInt:lineNumber] : nil;
|
||||
}
|
||||
|
||||
- (void)setText:(NSString*)aNewString
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
|
||||
|
||||
nsString text;
|
||||
nsCocoaUtils::GetStringForNSString(aNewString, text);
|
||||
if (textAcc) {
|
||||
textAcc->ReplaceText(text);
|
||||
} else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
proxy->ReplaceText(text);
|
||||
}
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
||||
- (NSString*)text
|
||||
{
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
ProxyAccessible* proxy = [self getProxyAccessible];
|
||||
HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
|
||||
if (!textAcc && !proxy)
|
||||
return nil;
|
||||
|
||||
// A password text field returns an empty value
|
||||
if (mRole == roles::PASSWORD_TEXT)
|
||||
return @"";
|
||||
|
||||
nsAutoString text;
|
||||
if (textAcc) {
|
||||
textAcc->TextSubstring(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT, text);
|
||||
} else if (proxy) {
|
||||
proxy->TextSubstring(0, nsIAccessibleText::TEXT_OFFSET_END_OF_TEXT, text);
|
||||
}
|
||||
|
||||
return nsCocoaUtils::ToNSString(text);
|
||||
}
|
||||
|
||||
- (long)textLength
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
||||
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
ProxyAccessible* proxy = [self getProxyAccessible];
|
||||
HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
|
||||
if (!textAcc && !proxy)
|
||||
return 0;
|
||||
|
||||
return textAcc ? textAcc->CharacterCount() : proxy->CharacterCount();
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
|
||||
}
|
||||
|
||||
- (long)selectedTextLength
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
|
||||
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
ProxyAccessible* proxy = [self getProxyAccessible];
|
||||
HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
|
||||
if (!textAcc && !proxy)
|
||||
return 0;
|
||||
|
||||
int32_t start = 0, end = 0;
|
||||
if (textAcc) {
|
||||
textAcc->SelectionBoundsAt(0, &start, &end);
|
||||
} else if (proxy) {
|
||||
nsString data;
|
||||
proxy->SelectionBoundsAt(0, data, &start, &end);
|
||||
}
|
||||
return (end - start);
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(0);
|
||||
}
|
||||
|
||||
- (NSString*)selectedText
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
ProxyAccessible* proxy = [self getProxyAccessible];
|
||||
HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
|
||||
if (!textAcc && !proxy)
|
||||
return nil;
|
||||
|
||||
int32_t start = 0, end = 0;
|
||||
nsAutoString selText;
|
||||
if (textAcc) {
|
||||
textAcc->SelectionBoundsAt(0, &start, &end);
|
||||
if (start != end) {
|
||||
textAcc->TextSubstring(start, end, selText);
|
||||
}
|
||||
} else if (proxy) {
|
||||
proxy->SelectionBoundsAt(0, selText, &start, &end);
|
||||
}
|
||||
|
||||
return nsCocoaUtils::ToNSString(selText);
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (NSValue*)selectedTextRange
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
|
||||
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
ProxyAccessible* proxy = [self getProxyAccessible];
|
||||
HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
|
||||
|
||||
int32_t start = 0;
|
||||
int32_t end = 0;
|
||||
int32_t count = 0;
|
||||
if (textAcc) {
|
||||
count = textAcc->SelectionCount();
|
||||
if (count) {
|
||||
textAcc->SelectionBoundsAt(0, &start, &end);
|
||||
return [NSValue valueWithRange:NSMakeRange(start, end - start)];
|
||||
}
|
||||
|
||||
start = textAcc->CaretOffset();
|
||||
return [NSValue valueWithRange:NSMakeRange(start != -1 ? start : 0, 0)];
|
||||
}
|
||||
|
||||
if (proxy) {
|
||||
count = proxy->SelectionCount();
|
||||
if (count) {
|
||||
nsString data;
|
||||
proxy->SelectionBoundsAt(0, data, &start, &end);
|
||||
return [NSValue valueWithRange:NSMakeRange(start, end - start)];
|
||||
}
|
||||
|
||||
start = proxy->CaretOffset();
|
||||
return [NSValue valueWithRange:NSMakeRange(start != -1 ? start : 0, 0)];
|
||||
}
|
||||
|
||||
return [NSValue valueWithRange:NSMakeRange(0, 0)];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
|
||||
}
|
||||
|
||||
- (NSValue*)visibleCharacterRange
|
||||
{
|
||||
// XXX this won't work with Textarea and such as we actually don't give
|
||||
// the visible character range.
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
ProxyAccessible* proxy = [self getProxyAccessible];
|
||||
HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
|
||||
if (!textAcc && !proxy)
|
||||
return 0;
|
||||
|
||||
return [NSValue valueWithRange:
|
||||
NSMakeRange(0, textAcc ?
|
||||
textAcc->CharacterCount() : proxy->CharacterCount())];
|
||||
}
|
||||
|
||||
- (void)valueDidChange
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
|
||||
NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
|
||||
NSAccessibilityValueChangedNotification);
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
}
|
||||
|
||||
- (void)selectedTextDidChange
|
||||
{
|
||||
NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
|
||||
NSAccessibilitySelectedTextChangedNotification);
|
||||
}
|
||||
|
||||
- (NSString*)stringFromRange:(NSRange*)range
|
||||
{
|
||||
NS_PRECONDITION(range, "no range");
|
||||
|
||||
AccessibleWrap* accWrap = [self getGeckoAccessible];
|
||||
ProxyAccessible* proxy = [self getProxyAccessible];
|
||||
HyperTextAccessible* textAcc = accWrap? accWrap->AsHyperText() : nullptr;
|
||||
if (!textAcc && !proxy)
|
||||
return nil;
|
||||
|
||||
nsAutoString text;
|
||||
if (textAcc) {
|
||||
textAcc->TextSubstring(range->location,
|
||||
range->location + range->length, text);
|
||||
} else if (proxy) {
|
||||
proxy->TextSubstring(range->location,
|
||||
range->location + range->length, text);
|
||||
}
|
||||
|
||||
return nsCocoaUtils::ToNSString(text);
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation mozTextLeafAccessible
|
||||
|
||||
- (NSArray*)accessibilityAttributeNames
|
||||
{
|
||||
static NSMutableArray* supportedAttributes = nil;
|
||||
if (!supportedAttributes) {
|
||||
supportedAttributes = [[super accessibilityAttributeNames] mutableCopy];
|
||||
[supportedAttributes removeObject:NSAccessibilityChildrenAttribute];
|
||||
}
|
||||
|
||||
return supportedAttributes;
|
||||
}
|
||||
|
||||
- (id)accessibilityAttributeValue:(NSString*)attribute
|
||||
{
|
||||
if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
|
||||
return @"";
|
||||
|
||||
if ([attribute isEqualToString:NSAccessibilityValueAttribute])
|
||||
return [self text];
|
||||
|
||||
return [super accessibilityAttributeValue:attribute];
|
||||
}
|
||||
|
||||
- (NSString*)text
|
||||
{
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
||||
return nsCocoaUtils::ToNSString(accWrap->AsTextLeaf()->Text());
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
nsString text;
|
||||
proxy->Text(&text);
|
||||
return nsCocoaUtils::ToNSString(text);
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (long)textLength
|
||||
{
|
||||
if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
|
||||
return accWrap->AsTextLeaf()->Text().Length();
|
||||
}
|
||||
|
||||
if (ProxyAccessible* proxy = [self getProxyAccessible]) {
|
||||
nsString text;
|
||||
proxy->Text(&text);
|
||||
return text.Length();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -9,6 +9,8 @@ if 'gtk' in toolkit:
|
||||
DIRS += ['atk']
|
||||
elif toolkit == 'windows':
|
||||
DIRS += ['windows']
|
||||
elif toolkit == 'cocoa':
|
||||
DIRS += ['mac']
|
||||
else:
|
||||
DIRS += ['other']
|
||||
|
||||
@@ -19,12 +21,10 @@ DIRS += [ 'aom',
|
||||
'interfaces',
|
||||
'ipc',
|
||||
'jsat',
|
||||
'xpcom'
|
||||
'xpcom',
|
||||
'xul'
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_XUL']:
|
||||
DIRS += ['xul']
|
||||
|
||||
TEST_DIRS += ['tests/mochitest']
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
|
||||
@@ -35,19 +35,6 @@ a11y::PlatformInit()
|
||||
|
||||
nsWinUtils::MaybeStartWindowEmulation();
|
||||
ia2AccessibleText::InitTextChangeData();
|
||||
if (BrowserTabsRemoteAutostart()) {
|
||||
mscom::InterceptorLog::Init();
|
||||
UniquePtr<RegisteredProxy> regProxy(
|
||||
mscom::RegisterProxy(L"ia2marshal.dll"));
|
||||
gRegProxy = regProxy.release();
|
||||
UniquePtr<RegisteredProxy> regAccTlb(
|
||||
mscom::RegisterTypelib(L"oleacc.dll",
|
||||
RegistrationFlags::eUseSystemDirectory));
|
||||
gRegAccTlb = regAccTlb.release();
|
||||
UniquePtr<RegisteredProxy> regMiscTlb(
|
||||
mscom::RegisterTypelib(L"Accessible.tlb"));
|
||||
gRegMiscTlb = regMiscTlb.release();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -33,15 +33,11 @@ SOURCES += [
|
||||
'RootAccessibleWrap.cpp',
|
||||
'ServiceProvider.cpp',
|
||||
'TextLeafAccessibleWrap.cpp',
|
||||
'XULListboxAccessibleWrap.cpp',
|
||||
'XULMenuAccessibleWrap.cpp',
|
||||
'XULTreeGridAccessibleWrap.cpp',
|
||||
]
|
||||
|
||||
if CONFIG['MOZ_XUL']:
|
||||
SOURCES += [
|
||||
'XULListboxAccessibleWrap.cpp',
|
||||
'XULMenuAccessibleWrap.cpp',
|
||||
'XULTreeGridAccessibleWrap.cpp',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/base',
|
||||
'/accessible/generic',
|
||||
|
||||
@@ -42,6 +42,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/windows/msaa',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/mac',
|
||||
]
|
||||
else:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/other',
|
||||
|
||||
@@ -37,6 +37,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
|
||||
'/accessible/windows/ia2',
|
||||
'/accessible/windows/msaa',
|
||||
]
|
||||
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/mac',
|
||||
]
|
||||
else:
|
||||
LOCAL_INCLUDES += [
|
||||
'/accessible/other',
|
||||
|
||||
@@ -1,54 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors;
|
||||
|
||||
/**
|
||||
* Object holding annotation data. Used by GeneratableElementIterator.
|
||||
*/
|
||||
public class AnnotationInfo {
|
||||
public enum ExceptionMode {
|
||||
ABORT,
|
||||
NSRESULT,
|
||||
IGNORE;
|
||||
|
||||
String nativeValue() {
|
||||
return "mozilla::jni::ExceptionMode::" + name();
|
||||
}
|
||||
}
|
||||
|
||||
public enum CallingThread {
|
||||
GECKO,
|
||||
UI,
|
||||
ANY;
|
||||
|
||||
String nativeValue() {
|
||||
return "mozilla::jni::CallingThread::" + name();
|
||||
}
|
||||
}
|
||||
|
||||
public enum DispatchTarget {
|
||||
GECKO,
|
||||
PROXY,
|
||||
CURRENT;
|
||||
|
||||
String nativeValue() {
|
||||
return "mozilla::jni::DispatchTarget::" + name();
|
||||
}
|
||||
}
|
||||
|
||||
public final String wrapperName;
|
||||
public final ExceptionMode exceptionMode;
|
||||
public final CallingThread callingThread;
|
||||
public final DispatchTarget dispatchTarget;
|
||||
|
||||
public AnnotationInfo(String wrapperName, ExceptionMode exceptionMode,
|
||||
CallingThread callingThread, DispatchTarget dispatchTarget) {
|
||||
|
||||
this.wrapperName = wrapperName;
|
||||
this.exceptionMode = exceptionMode;
|
||||
this.callingThread = callingThread;
|
||||
this.dispatchTarget = dispatchTarget;
|
||||
}
|
||||
}
|
||||
@@ -1,175 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors;
|
||||
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.IterableJarLoadingURLClassLoader;
|
||||
import org.mozilla.gecko.annotationProcessors.utils.GeneratableElementIterator;
|
||||
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
|
||||
public class AnnotationProcessor {
|
||||
public static final String GENERATED_COMMENT =
|
||||
"// GENERATED CODE\n" +
|
||||
"// Generated by the Java program at /build/annotationProcessors at compile time\n" +
|
||||
"// from annotations on Java methods. To update, change the annotations on the\n" +
|
||||
"// corresponding Java methods and rerun the build. Manually updating this file\n" +
|
||||
"// will cause your build to fail.\n" +
|
||||
"\n";
|
||||
|
||||
private static final StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
|
||||
private static final StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
|
||||
private static final StringBuilder nativesFile = new StringBuilder(GENERATED_COMMENT);
|
||||
|
||||
public static void main(String[] args) {
|
||||
// We expect a list of jars on the commandline. If missing, whinge about it.
|
||||
if (args.length <= 2) {
|
||||
System.err.println("Usage: java AnnotationProcessor outprefix jarfiles ...");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
final String OUTPUT_PREFIX = args[0];
|
||||
final String SOURCE_FILE = OUTPUT_PREFIX + "JNIWrappers.cpp";
|
||||
final String HEADER_FILE = OUTPUT_PREFIX + "JNIWrappers.h";
|
||||
final String NATIVES_FILE = OUTPUT_PREFIX + "JNINatives.h";
|
||||
|
||||
System.out.println("Processing annotations...");
|
||||
|
||||
// We want to produce the same output as last time as often as possible. Ordering of
|
||||
// generated statements, therefore, needs to be consistent.
|
||||
final String[] jars = Arrays.copyOfRange(args, 1, args.length);
|
||||
Arrays.sort(jars);
|
||||
|
||||
// Start the clock!
|
||||
long s = System.currentTimeMillis();
|
||||
|
||||
// Get an iterator over the classes in the jar files given...
|
||||
Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(jars);
|
||||
|
||||
headerFile.append(
|
||||
"#ifndef " + getHeaderGuardName(HEADER_FILE) + "\n" +
|
||||
"#define " + getHeaderGuardName(HEADER_FILE) + "\n" +
|
||||
"\n" +
|
||||
"#include \"mozilla/jni/Refs.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace java {\n" +
|
||||
"\n");
|
||||
|
||||
implementationFile.append(
|
||||
"#include \"" + HEADER_FILE + "\"\n" +
|
||||
"#include \"mozilla/jni/Accessors.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace java {\n" +
|
||||
"\n");
|
||||
|
||||
nativesFile.append(
|
||||
"#ifndef " + getHeaderGuardName(NATIVES_FILE) + "\n" +
|
||||
"#define " + getHeaderGuardName(NATIVES_FILE) + "\n" +
|
||||
"\n" +
|
||||
"#include \"" + HEADER_FILE + "\"\n" +
|
||||
"#include \"mozilla/jni/Natives.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace java {\n" +
|
||||
"\n");
|
||||
|
||||
while (jarClassIterator.hasNext()) {
|
||||
generateClass(jarClassIterator.next());
|
||||
}
|
||||
|
||||
implementationFile.append(
|
||||
"} /* java */\n" +
|
||||
"} /* mozilla */\n");
|
||||
|
||||
headerFile.append(
|
||||
"} /* java */\n" +
|
||||
"} /* mozilla */\n" +
|
||||
"#endif // " + getHeaderGuardName(HEADER_FILE) + "\n");
|
||||
|
||||
nativesFile.append(
|
||||
"} /* java */\n" +
|
||||
"} /* mozilla */\n" +
|
||||
"#endif // " + getHeaderGuardName(NATIVES_FILE) + "\n");
|
||||
|
||||
writeOutputFile(SOURCE_FILE, implementationFile);
|
||||
writeOutputFile(HEADER_FILE, headerFile);
|
||||
writeOutputFile(NATIVES_FILE, nativesFile);
|
||||
|
||||
long e = System.currentTimeMillis();
|
||||
System.out.println("Annotation processing complete in " + (e - s) + "ms");
|
||||
}
|
||||
|
||||
private static void generateClass(final ClassWithOptions annotatedClass) {
|
||||
// Get an iterator over the appropriately generated methods of this class
|
||||
final GeneratableElementIterator methodIterator
|
||||
= new GeneratableElementIterator(annotatedClass);
|
||||
final ClassWithOptions[] innerClasses = methodIterator.getInnerClasses();
|
||||
|
||||
if (!methodIterator.hasNext() && innerClasses.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
final CodeGenerator generatorInstance = new CodeGenerator(annotatedClass);
|
||||
generatorInstance.generateClasses(innerClasses);
|
||||
|
||||
// Iterate all annotated members in this class..
|
||||
while (methodIterator.hasNext()) {
|
||||
AnnotatableEntity aElementTuple = methodIterator.next();
|
||||
switch (aElementTuple.mEntityType) {
|
||||
case METHOD:
|
||||
generatorInstance.generateMethod(aElementTuple);
|
||||
break;
|
||||
case NATIVE:
|
||||
generatorInstance.generateNative(aElementTuple);
|
||||
break;
|
||||
case FIELD:
|
||||
generatorInstance.generateField(aElementTuple);
|
||||
break;
|
||||
case CONSTRUCTOR:
|
||||
generatorInstance.generateConstructor(aElementTuple);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
headerFile.append(generatorInstance.getHeaderFileContents());
|
||||
implementationFile.append(generatorInstance.getWrapperFileContents());
|
||||
nativesFile.append(generatorInstance.getNativesFileContents());
|
||||
|
||||
for (ClassWithOptions innerClass : innerClasses) {
|
||||
generateClass(innerClass);
|
||||
}
|
||||
}
|
||||
|
||||
private static String getHeaderGuardName(final String name) {
|
||||
return name.replaceAll("\\W", "_");
|
||||
}
|
||||
|
||||
private static void writeOutputFile(final String name,
|
||||
final StringBuilder content) {
|
||||
FileOutputStream outStream = null;
|
||||
try {
|
||||
outStream = new FileOutputStream(name);
|
||||
outStream.write(content.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to write " + name + ". Perhaps a permissions issue?");
|
||||
e.printStackTrace(System.err);
|
||||
} finally {
|
||||
if (outStream != null) {
|
||||
try {
|
||||
outStream.close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to close outStream due to "+e);
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,627 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors;
|
||||
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions;
|
||||
import org.mozilla.gecko.annotationProcessors.utils.Utils;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class CodeGenerator {
|
||||
private static final Class<?>[] EMPTY_CLASS_ARRAY = new Class<?>[0];
|
||||
|
||||
// Buffers holding the strings to ultimately be written to the output files.
|
||||
private final StringBuilder cpp = new StringBuilder();
|
||||
private final StringBuilder header = new StringBuilder();
|
||||
private final StringBuilder natives = new StringBuilder();
|
||||
private final StringBuilder nativesInits = new StringBuilder();
|
||||
|
||||
private final Class<?> cls;
|
||||
private final String clsName;
|
||||
private AnnotationInfo.CallingThread callingThread = null;
|
||||
private int numNativesInits;
|
||||
|
||||
private final HashSet<String> takenMethodNames = new HashSet<String>();
|
||||
|
||||
public CodeGenerator(ClassWithOptions annotatedClass) {
|
||||
this.cls = annotatedClass.wrappedClass;
|
||||
this.clsName = annotatedClass.generatedName;
|
||||
|
||||
final String unqualifiedName = Utils.getUnqualifiedName(clsName);
|
||||
header.append(
|
||||
"class " + clsName + " : public mozilla::jni::ObjectBase<" +
|
||||
unqualifiedName + ">\n" +
|
||||
"{\n" +
|
||||
"public:\n" +
|
||||
" static const char name[];\n" +
|
||||
"\n" +
|
||||
" explicit " + unqualifiedName + "(const Context& ctx) : ObjectBase<" +
|
||||
unqualifiedName + ">(ctx) {}\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
"const char " + clsName + "::name[] =\n" +
|
||||
" \"" + cls.getName().replace('.', '/') + "\";\n" +
|
||||
"\n");
|
||||
|
||||
natives.append(
|
||||
"template<class Impl>\n" +
|
||||
"class " + clsName + "::Natives : " +
|
||||
"public mozilla::jni::NativeImpl<" + unqualifiedName + ", Impl>\n" +
|
||||
"{\n" +
|
||||
"public:\n");
|
||||
}
|
||||
|
||||
private String getTraitsName(String uniqueName, boolean includeScope) {
|
||||
return (includeScope ? clsName + "::" : "") + uniqueName + "_t";
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the C++ type name for this class or any class within the chain
|
||||
* of declaring classes, if the target class matches the given type.
|
||||
*
|
||||
* Return null if the given type does not match any class searched.
|
||||
*/
|
||||
private String getMatchingClassType(final Class<?> type) {
|
||||
Class<?> cls = this.cls;
|
||||
String clsName = this.clsName;
|
||||
|
||||
while (cls != null) {
|
||||
if (type.equals(cls)) {
|
||||
return clsName;
|
||||
}
|
||||
cls = cls.getDeclaringClass();
|
||||
clsName = clsName.substring(0, Math.max(0, clsName.lastIndexOf("::")));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getNativeParameterType(Class<?> type, AnnotationInfo info) {
|
||||
final String clsName = getMatchingClassType(type);
|
||||
if (clsName != null) {
|
||||
return Utils.getUnqualifiedName(clsName) + "::Param";
|
||||
}
|
||||
return Utils.getNativeParameterType(type, info);
|
||||
}
|
||||
|
||||
private String getNativeReturnType(Class<?> type, AnnotationInfo info) {
|
||||
final String clsName = getMatchingClassType(type);
|
||||
if (clsName != null) {
|
||||
return Utils.getUnqualifiedName(clsName) + "::LocalRef";
|
||||
}
|
||||
return Utils.getNativeReturnType(type, info);
|
||||
}
|
||||
|
||||
private void generateMember(AnnotationInfo info, Member member,
|
||||
String uniqueName, Class<?> type, Class<?>[] argTypes) {
|
||||
final StringBuilder args = new StringBuilder();
|
||||
for (Class<?> argType : argTypes) {
|
||||
args.append("\n " + getNativeParameterType(argType, info) + ",");
|
||||
}
|
||||
if (args.length() > 0) {
|
||||
args.setLength(args.length() - 1);
|
||||
}
|
||||
|
||||
header.append(
|
||||
" struct " + getTraitsName(uniqueName, /* includeScope */ false) + " {\n" +
|
||||
" typedef " + Utils.getUnqualifiedName(clsName) + " Owner;\n" +
|
||||
" typedef " + getNativeReturnType(type, info) + " ReturnType;\n" +
|
||||
" typedef " + getNativeParameterType(type, info) + " SetterType;\n" +
|
||||
" typedef mozilla::jni::Args<" + args + "> Args;\n" +
|
||||
" static constexpr char name[] = \"" +
|
||||
Utils.getMemberName(member) + "\";\n" +
|
||||
" static constexpr char signature[] =\n" +
|
||||
" \"" + Utils.getSignature(member) + "\";\n" +
|
||||
" static const bool isStatic = " + Utils.isStatic(member) + ";\n" +
|
||||
" static const mozilla::jni::ExceptionMode exceptionMode =\n" +
|
||||
" " + info.exceptionMode.nativeValue() + ";\n" +
|
||||
" static const mozilla::jni::CallingThread callingThread =\n" +
|
||||
" " + info.callingThread.nativeValue() + ";\n" +
|
||||
" static const mozilla::jni::DispatchTarget dispatchTarget =\n" +
|
||||
" " + info.dispatchTarget.nativeValue() + ";\n" +
|
||||
" };\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
"constexpr char " + getTraitsName(uniqueName, /* includeScope */ true) +
|
||||
"::name[];\n" +
|
||||
"constexpr char " + getTraitsName(uniqueName, /* includeScope */ true) +
|
||||
"::signature[];\n" +
|
||||
"\n");
|
||||
|
||||
if (this.callingThread == null) {
|
||||
this.callingThread = info.callingThread;
|
||||
} else if (this.callingThread != info.callingThread) {
|
||||
// We have a mix of calling threads, so specify "any" for the whole class.
|
||||
this.callingThread = AnnotationInfo.CallingThread.ANY;
|
||||
}
|
||||
}
|
||||
|
||||
private String getUniqueMethodName(String basename) {
|
||||
String newName = basename;
|
||||
int index = 1;
|
||||
|
||||
while (takenMethodNames.contains(newName)) {
|
||||
newName = basename + (++index);
|
||||
}
|
||||
|
||||
takenMethodNames.add(newName);
|
||||
return newName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a method prototype that includes return and argument types,
|
||||
* without specifiers (static, const, etc.).
|
||||
*/
|
||||
private String generatePrototype(String name, Class<?>[] argTypes,
|
||||
Class<?> returnType, AnnotationInfo info,
|
||||
boolean includeScope, boolean includeArgName,
|
||||
boolean isConst) {
|
||||
|
||||
final StringBuilder proto = new StringBuilder();
|
||||
int argIndex = 0;
|
||||
|
||||
proto.append("auto ");
|
||||
|
||||
if (includeScope) {
|
||||
proto.append(clsName).append("::");
|
||||
}
|
||||
|
||||
proto.append(name).append('(');
|
||||
|
||||
for (Class<?> argType : argTypes) {
|
||||
proto.append(getNativeParameterType(argType, info));
|
||||
if (includeArgName) {
|
||||
proto.append(" a").append(argIndex++);
|
||||
}
|
||||
proto.append(", ");
|
||||
}
|
||||
|
||||
if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT &&
|
||||
!returnType.equals(void.class)) {
|
||||
proto.append(getNativeReturnType(returnType, info)).append('*');
|
||||
if (includeArgName) {
|
||||
proto.append(" a").append(argIndex++);
|
||||
}
|
||||
proto.append(", ");
|
||||
}
|
||||
|
||||
if (proto.substring(proto.length() - 2).equals(", ")) {
|
||||
proto.setLength(proto.length() - 2);
|
||||
}
|
||||
|
||||
proto.append(')');
|
||||
|
||||
if (isConst) {
|
||||
proto.append(" const");
|
||||
}
|
||||
|
||||
if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT) {
|
||||
proto.append(" -> nsresult");
|
||||
} else {
|
||||
proto.append(" -> ").append(getNativeReturnType(returnType, info));
|
||||
}
|
||||
return proto.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a method declaration that includes the prototype with specifiers,
|
||||
* but without the method body.
|
||||
*/
|
||||
private String generateDeclaration(String name, Class<?>[] argTypes,
|
||||
Class<?> returnType, AnnotationInfo info,
|
||||
boolean isStatic) {
|
||||
|
||||
return (isStatic ? "static " : "") +
|
||||
generatePrototype(name, argTypes, returnType, info,
|
||||
/* includeScope */ false, /* includeArgName */ false,
|
||||
/* isConst */ !isStatic) + ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a method definition that includes the prototype with specifiers,
|
||||
* and with the method body.
|
||||
*/
|
||||
private String generateDefinition(String accessorName, String name, Class<?>[] argTypes,
|
||||
Class<?> returnType, AnnotationInfo info, boolean isStatic) {
|
||||
|
||||
final StringBuilder def = new StringBuilder(
|
||||
generatePrototype(name, argTypes, returnType, info,
|
||||
/* includeScope */ true, /* includeArgName */ true,
|
||||
/* isConst */ !isStatic));
|
||||
def.append("\n{\n");
|
||||
|
||||
|
||||
// Generate code to handle the return value, if needed.
|
||||
// We initialize rv to NS_OK instead of NS_ERROR_* because loading NS_OK (0) uses
|
||||
// fewer instructions. We are guaranteed to set rv to the correct value later.
|
||||
|
||||
if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT &&
|
||||
returnType.equals(void.class)) {
|
||||
def.append(
|
||||
" nsresult rv = NS_OK;\n" +
|
||||
" ");
|
||||
|
||||
} else if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT) {
|
||||
// Non-void return type
|
||||
final String resultArg = "a" + argTypes.length;
|
||||
def.append(
|
||||
" MOZ_ASSERT(" + resultArg + ");\n" +
|
||||
" nsresult rv = NS_OK;\n" +
|
||||
" *" + resultArg + " = ");
|
||||
|
||||
} else {
|
||||
def.append(
|
||||
" return ");
|
||||
}
|
||||
|
||||
|
||||
// Generate a call, e.g., Method<Traits>::Call(a0, a1, a2);
|
||||
|
||||
def.append(accessorName).append("(")
|
||||
.append(Utils.getUnqualifiedName(clsName) +
|
||||
(isStatic ? "::Context()" : "::mCtx"));
|
||||
|
||||
if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT) {
|
||||
def.append(", &rv");
|
||||
} else {
|
||||
def.append(", nullptr");
|
||||
}
|
||||
|
||||
// Generate the call argument list.
|
||||
for (int argIndex = 0; argIndex < argTypes.length; argIndex++) {
|
||||
def.append(", a").append(argIndex);
|
||||
}
|
||||
|
||||
def.append(");\n");
|
||||
|
||||
|
||||
if (info.exceptionMode == AnnotationInfo.ExceptionMode.NSRESULT) {
|
||||
def.append(" return rv;\n");
|
||||
}
|
||||
|
||||
return def.append("}").toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the appropriate generated code to the buffers for the method provided.
|
||||
*
|
||||
* @param annotatedMethod The Java method, plus annotation data.
|
||||
*/
|
||||
public void generateMethod(AnnotatableEntity annotatedMethod) {
|
||||
// Unpack the tuple and extract some useful fields from the Method..
|
||||
final Method method = annotatedMethod.getMethod();
|
||||
final AnnotationInfo info = annotatedMethod.mAnnotationInfo;
|
||||
final String uniqueName = getUniqueMethodName(info.wrapperName);
|
||||
final Class<?>[] argTypes = method.getParameterTypes();
|
||||
final Class<?> returnType = method.getReturnType();
|
||||
|
||||
if (method.isSynthetic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (info.dispatchTarget != AnnotationInfo.DispatchTarget.CURRENT) {
|
||||
throw new IllegalStateException("Invalid dispatch target \"" +
|
||||
info.dispatchTarget.name().toLowerCase() +
|
||||
"\" for non-native method " + clsName + "::" + uniqueName);
|
||||
}
|
||||
|
||||
generateMember(info, method, uniqueName, returnType, argTypes);
|
||||
|
||||
final boolean isStatic = Utils.isStatic(method);
|
||||
|
||||
header.append(
|
||||
" " + generateDeclaration(info.wrapperName, argTypes,
|
||||
returnType, info, isStatic) + "\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
generateDefinition(
|
||||
"mozilla::jni::Method<" +
|
||||
getTraitsName(uniqueName, /* includeScope */ false) + ">::Call",
|
||||
info.wrapperName, argTypes, returnType, info, isStatic) + "\n" +
|
||||
"\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Append the appropriate generated code to the buffers for the native method provided.
|
||||
*
|
||||
* @param annotatedMethod The Java native method, plus annotation data.
|
||||
*/
|
||||
public void generateNative(AnnotatableEntity annotatedMethod) {
|
||||
// Unpack the tuple and extract some useful fields from the Method..
|
||||
final Method method = annotatedMethod.getMethod();
|
||||
final AnnotationInfo info = annotatedMethod.mAnnotationInfo;
|
||||
final String uniqueName = getUniqueMethodName(info.wrapperName);
|
||||
final Class<?>[] argTypes = method.getParameterTypes();
|
||||
final Class<?> returnType = method.getReturnType();
|
||||
|
||||
// Sanity check
|
||||
if (info.exceptionMode != AnnotationInfo.ExceptionMode.ABORT &&
|
||||
info.exceptionMode != AnnotationInfo.ExceptionMode.IGNORE) {
|
||||
throw new IllegalStateException("Invalid exception mode \"" +
|
||||
info.exceptionMode.name().toLowerCase() +
|
||||
"\" for native method " + clsName + "::" + uniqueName);
|
||||
}
|
||||
if (info.dispatchTarget != AnnotationInfo.DispatchTarget.CURRENT &&
|
||||
returnType != void.class) {
|
||||
throw new IllegalStateException(
|
||||
"Must return void when not dispatching to current thread for native method " +
|
||||
clsName + "::" + uniqueName);
|
||||
}
|
||||
|
||||
generateMember(info, method, uniqueName, returnType, argTypes);
|
||||
|
||||
final String traits = getTraitsName(uniqueName, /* includeScope */ true);
|
||||
|
||||
if (nativesInits.length() > 0) {
|
||||
nativesInits.append(',');
|
||||
}
|
||||
|
||||
nativesInits.append(
|
||||
"\n" +
|
||||
"\n" +
|
||||
" mozilla::jni::MakeNativeMethod<" + traits + ">(\n" +
|
||||
" mozilla::jni::NativeStub<" + traits + ", Impl>\n" +
|
||||
" ::template Wrap<&Impl::" + info.wrapperName + ">)");
|
||||
numNativesInits++;
|
||||
}
|
||||
|
||||
private String getLiteral(Object val, AnnotationInfo info) {
|
||||
final Class<?> type = val.getClass();
|
||||
|
||||
if (type.equals(char.class) || type.equals(Character.class)) {
|
||||
final char c = (char) val;
|
||||
if (c >= 0x20 && c < 0x7F) {
|
||||
return "'" + c + '\'';
|
||||
}
|
||||
return "u'\\u" + Integer.toHexString(0x10000 | (int) c).substring(1) + '\'';
|
||||
|
||||
} else if (type.equals(CharSequence.class) || type.equals(String.class)) {
|
||||
final CharSequence str = (CharSequence) val;
|
||||
final StringBuilder out = new StringBuilder("u\"");
|
||||
for (int i = 0; i < str.length(); i++) {
|
||||
final char c = str.charAt(i);
|
||||
if (c >= 0x20 && c < 0x7F) {
|
||||
out.append(c);
|
||||
} else {
|
||||
out.append("\\u").append(Integer.toHexString(0x10000 | (int) c).substring(1));
|
||||
}
|
||||
}
|
||||
return out.append('"').toString();
|
||||
}
|
||||
|
||||
return String.valueOf(val);
|
||||
}
|
||||
|
||||
public void generateField(AnnotatableEntity annotatedField) {
|
||||
final Field field = annotatedField.getField();
|
||||
final AnnotationInfo info = annotatedField.mAnnotationInfo;
|
||||
final String uniqueName = info.wrapperName;
|
||||
final Class<?> type = field.getType();
|
||||
|
||||
// Handles a peculiar case when dealing with enum types. We don't care about this field.
|
||||
// It just gets in the way and stops our code from compiling.
|
||||
if (field.isSynthetic() || field.getName().equals("$VALUES")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (info.dispatchTarget != AnnotationInfo.DispatchTarget.CURRENT) {
|
||||
throw new IllegalStateException("Invalid dispatch target \"" +
|
||||
info.dispatchTarget.name().toLowerCase() +
|
||||
"\" for field " + clsName + "::" + uniqueName);
|
||||
}
|
||||
|
||||
final boolean isStatic = Utils.isStatic(field);
|
||||
final boolean isFinal = Utils.isFinal(field);
|
||||
|
||||
if (isStatic && isFinal && (type.isPrimitive() || type.equals(String.class))) {
|
||||
Object val = null;
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
val = field.get(null);
|
||||
} catch (final IllegalAccessException e) {
|
||||
}
|
||||
|
||||
if (val != null && type.isPrimitive()) {
|
||||
// For static final primitive fields, we can use a "static const" declaration.
|
||||
header.append(
|
||||
" static const " + Utils.getNativeReturnType(type, info) +
|
||||
' ' + info.wrapperName + " = " + getLiteral(val, info) + ";\n" +
|
||||
"\n");
|
||||
return;
|
||||
|
||||
} else if (val != null && type.equals(String.class)) {
|
||||
final String nativeType = "char16_t";
|
||||
|
||||
header.append(
|
||||
" static const " + nativeType + ' ' + info.wrapperName + "[];\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
"const " + nativeType + ' ' + clsName + "::" + info.wrapperName +
|
||||
"[] = " + getLiteral(val, info) + ";\n" +
|
||||
"\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Fall back to using accessors if we encounter an exception.
|
||||
}
|
||||
|
||||
generateMember(info, field, uniqueName, type, EMPTY_CLASS_ARRAY);
|
||||
|
||||
final Class<?>[] getterArgs = EMPTY_CLASS_ARRAY;
|
||||
|
||||
header.append(
|
||||
" " + generateDeclaration(info.wrapperName, getterArgs,
|
||||
type, info, isStatic) + "\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
generateDefinition(
|
||||
"mozilla::jni::Field<" +
|
||||
getTraitsName(uniqueName, /* includeScope */ false) + ">::Get",
|
||||
info.wrapperName, getterArgs, type, info, isStatic) + "\n" +
|
||||
"\n");
|
||||
|
||||
if (isFinal) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Class<?>[] setterArgs = new Class<?>[] { type };
|
||||
|
||||
header.append(
|
||||
" " + generateDeclaration(info.wrapperName, setterArgs,
|
||||
void.class, info, isStatic) + "\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
generateDefinition(
|
||||
"mozilla::jni::Field<" +
|
||||
getTraitsName(uniqueName, /* includeScope */ false) + ">::Set",
|
||||
info.wrapperName, setterArgs, void.class, info, isStatic) + "\n" +
|
||||
"\n");
|
||||
}
|
||||
|
||||
public void generateConstructor(AnnotatableEntity annotatedConstructor) {
|
||||
// Unpack the tuple and extract some useful fields from the Method..
|
||||
final Constructor<?> method = annotatedConstructor.getConstructor();
|
||||
final AnnotationInfo info = annotatedConstructor.mAnnotationInfo;
|
||||
final String wrapperName = "New";
|
||||
final String uniqueName = getUniqueMethodName(wrapperName);
|
||||
final Class<?>[] argTypes = method.getParameterTypes();
|
||||
final Class<?> returnType = cls;
|
||||
|
||||
if (method.isSynthetic()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Sanity check
|
||||
if (info.dispatchTarget != AnnotationInfo.DispatchTarget.CURRENT) {
|
||||
throw new IllegalStateException("Invalid dispatch target \"" +
|
||||
info.dispatchTarget.name().toLowerCase() +
|
||||
"\" for constructor " + clsName + "::" + uniqueName);
|
||||
}
|
||||
|
||||
generateMember(info, method, uniqueName, returnType, argTypes);
|
||||
|
||||
header.append(
|
||||
" " + generateDeclaration(wrapperName, argTypes,
|
||||
returnType, info, /* isStatic */ true) + "\n" +
|
||||
"\n");
|
||||
|
||||
cpp.append(
|
||||
generateDefinition(
|
||||
"mozilla::jni::Constructor<" +
|
||||
getTraitsName(uniqueName, /* includeScope */ false) + ">::Call",
|
||||
wrapperName, argTypes, returnType, info, /* isStatic */ true) + "\n" +
|
||||
"\n");
|
||||
}
|
||||
|
||||
public void generateMembers(Member[] members) {
|
||||
for (Member m : members) {
|
||||
if (!Modifier.isPublic(m.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
String name = Utils.getMemberName(m);
|
||||
name = name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
|
||||
// Default for SDK bindings.
|
||||
final AnnotationInfo info = new AnnotationInfo(name,
|
||||
AnnotationInfo.ExceptionMode.NSRESULT,
|
||||
AnnotationInfo.CallingThread.ANY,
|
||||
AnnotationInfo.DispatchTarget.CURRENT);
|
||||
final AnnotatableEntity entity = new AnnotatableEntity(m, info);
|
||||
|
||||
if (m instanceof Constructor) {
|
||||
generateConstructor(entity);
|
||||
} else if (m instanceof Method) {
|
||||
generateMethod(entity);
|
||||
} else if (m instanceof Field) {
|
||||
generateField(entity);
|
||||
} else {
|
||||
throw new IllegalArgumentException(
|
||||
"expected member to be Constructor, Method, or Field");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void generateClasses(final ClassWithOptions[] classes) {
|
||||
if (classes.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (final ClassWithOptions cls : classes) {
|
||||
// Extract "Inner" from "Outer::Inner".
|
||||
header.append(
|
||||
" class " + Utils.getUnqualifiedName(cls.generatedName) + ";\n");
|
||||
}
|
||||
header.append('\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the finalised bytes to go into the generated wrappers file.
|
||||
*
|
||||
* @return The bytes to be written to the wrappers file.
|
||||
*/
|
||||
public String getWrapperFileContents() {
|
||||
return cpp.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the finalised bytes to go into the generated header file.
|
||||
*
|
||||
* @return The bytes to be written to the header file.
|
||||
*/
|
||||
public String getHeaderFileContents() {
|
||||
if (this.callingThread == null) {
|
||||
this.callingThread = AnnotationInfo.CallingThread.ANY;
|
||||
}
|
||||
|
||||
header.append(
|
||||
" static const mozilla::jni::CallingThread callingThread =\n" +
|
||||
" " + this.callingThread.nativeValue() + ";\n" +
|
||||
"\n");
|
||||
|
||||
if (nativesInits.length() > 0) {
|
||||
header.append(
|
||||
" template<class Impl> class Natives;\n");
|
||||
}
|
||||
header.append(
|
||||
"};\n" +
|
||||
"\n");
|
||||
return header.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the finalised bytes to go into the generated natives header file.
|
||||
*
|
||||
* @return The bytes to be written to the header file.
|
||||
*/
|
||||
public String getNativesFileContents() {
|
||||
if (nativesInits.length() == 0) {
|
||||
return "";
|
||||
}
|
||||
natives.append(
|
||||
" static const JNINativeMethod methods[" + numNativesInits + "];\n" +
|
||||
"};\n" +
|
||||
"\n" +
|
||||
"template<class Impl>\n" +
|
||||
"const JNINativeMethod " + clsName + "::Natives<Impl>::methods[] = {" + nativesInits + '\n' +
|
||||
"};\n" +
|
||||
"\n");
|
||||
return natives.toString();
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
# Include Android specific java flags, instead of what's in rules.mk.
|
||||
include $(topsrcdir)/config/android-common.mk
|
||||
|
||||
export:: annotationProcessors.jar
|
||||
@@ -1,258 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors;
|
||||
|
||||
import com.android.tools.lint.checks.ApiLookup;
|
||||
import com.android.tools.lint.LintCliClient;
|
||||
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.IterableJarLoadingURLClassLoader;
|
||||
import org.mozilla.gecko.annotationProcessors.utils.GeneratableElementIterator;
|
||||
import org.mozilla.gecko.annotationProcessors.utils.Utils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
import java.util.Scanner;
|
||||
import java.util.Vector;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
public class SDKProcessor {
|
||||
public static final String GENERATED_COMMENT =
|
||||
"// GENERATED CODE\n" +
|
||||
"// Generated by the Java program at /build/annotationProcessors at compile time\n" +
|
||||
"// from annotations on Java methods. To update, change the annotations on the\n" +
|
||||
"// corresponding Javamethods and rerun the build. Manually updating this file\n" +
|
||||
"// will cause your build to fail.\n" +
|
||||
"\n";
|
||||
|
||||
private static ApiLookup sApiLookup;
|
||||
private static int sMaxSdkVersion;
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
// We expect a list of jars on the commandline. If missing, whinge about it.
|
||||
if (args.length < 5) {
|
||||
System.err.println("Usage: java SDKProcessor sdkjar classlistfile outdir fileprefix max-sdk-version");
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
System.out.println("Processing platform bindings...");
|
||||
|
||||
String sdkJar = args[0];
|
||||
Vector classes = getClassList(args[1]);
|
||||
String outdir = args[2];
|
||||
String generatedFilePrefix = args[3];
|
||||
sMaxSdkVersion = Integer.parseInt(args[4]);
|
||||
|
||||
LintCliClient lintClient = new LintCliClient();
|
||||
sApiLookup = ApiLookup.get(lintClient);
|
||||
|
||||
// Start the clock!
|
||||
long s = System.currentTimeMillis();
|
||||
|
||||
// Get an iterator over the classes in the jar files given...
|
||||
// Iterator<ClassWithOptions> jarClassIterator = IterableJarLoadingURLClassLoader.getIteratorOverJars(args);
|
||||
|
||||
StringBuilder headerFile = new StringBuilder(GENERATED_COMMENT);
|
||||
headerFile.append(
|
||||
"#ifndef " + generatedFilePrefix + "_h__\n" +
|
||||
"#define " + generatedFilePrefix + "_h__\n" +
|
||||
"\n" +
|
||||
"#include \"mozilla/jni/Refs.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace java {\n" +
|
||||
"namespace sdk {\n" +
|
||||
"\n");
|
||||
|
||||
StringBuilder implementationFile = new StringBuilder(GENERATED_COMMENT);
|
||||
implementationFile.append(
|
||||
"#include \"" + generatedFilePrefix + ".h\"\n" +
|
||||
"#include \"mozilla/jni/Accessors.h\"\n" +
|
||||
"\n" +
|
||||
"namespace mozilla {\n" +
|
||||
"namespace java {\n" +
|
||||
"namespace sdk {\n" +
|
||||
"\n");
|
||||
|
||||
// Used to track the calls to the various class-specific initialisation functions.
|
||||
ClassLoader loader = null;
|
||||
try {
|
||||
loader = URLClassLoader.newInstance(new URL[] { new URL("file://" + sdkJar) },
|
||||
SDKProcessor.class.getClassLoader());
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.toString());
|
||||
}
|
||||
|
||||
for (Iterator<String> i = classes.iterator(); i.hasNext(); ) {
|
||||
String className = i.next();
|
||||
System.out.println("Looking up: " + className);
|
||||
|
||||
generateClass(Class.forName(className, true, loader),
|
||||
implementationFile,
|
||||
headerFile);
|
||||
}
|
||||
|
||||
implementationFile.append(
|
||||
"} /* sdk */\n" +
|
||||
"} /* java */\n" +
|
||||
"} /* mozilla */\n");
|
||||
|
||||
headerFile.append(
|
||||
"} /* sdk */\n" +
|
||||
"} /* java */\n" +
|
||||
"} /* mozilla */\n" +
|
||||
"#endif\n");
|
||||
|
||||
writeOutputFiles(outdir, generatedFilePrefix, headerFile, implementationFile);
|
||||
long e = System.currentTimeMillis();
|
||||
System.out.println("SDK processing complete in " + (e - s) + "ms");
|
||||
}
|
||||
|
||||
private static int getAPIVersion(Class<?> cls, Member m) {
|
||||
if (m instanceof Method || m instanceof Constructor) {
|
||||
return sApiLookup.getCallVersion(
|
||||
cls.getName().replace('.', '/'),
|
||||
Utils.getMemberName(m),
|
||||
Utils.getSignature(m));
|
||||
} else if (m instanceof Field) {
|
||||
return sApiLookup.getFieldVersion(
|
||||
Utils.getClassDescriptor(m.getDeclaringClass()), m.getName());
|
||||
} else {
|
||||
throw new IllegalArgumentException("expected member to be Method, Constructor, or Field");
|
||||
}
|
||||
}
|
||||
|
||||
private static Member[] sortAndFilterMembers(Class<?> cls, Member[] members) {
|
||||
Arrays.sort(members, new Comparator<Member>() {
|
||||
@Override
|
||||
public int compare(Member a, Member b) {
|
||||
return a.getName().compareTo(b.getName());
|
||||
}
|
||||
});
|
||||
|
||||
ArrayList<Member> list = new ArrayList<>();
|
||||
for (Member m : members) {
|
||||
// Sometimes (e.g. Bundle) has methods that moved to/from a superclass in a later SDK
|
||||
// version, so we check for both classes and see if we can find a minimum SDK version.
|
||||
int version = getAPIVersion(cls, m);
|
||||
final int version2 = getAPIVersion(m.getDeclaringClass(), m);
|
||||
if (version2 > 0 && version2 < version) {
|
||||
version = version2;
|
||||
}
|
||||
if (version > sMaxSdkVersion) {
|
||||
System.out.println("Skipping " + m.getDeclaringClass().getName() + "." + m.getName() +
|
||||
", version " + version + " > " + sMaxSdkVersion);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Sometimes (e.g. KeyEvent) a field can appear in both a class and a superclass. In
|
||||
// that case we want to filter out the version that appears in the superclass, or
|
||||
// we'll have bindings with duplicate names.
|
||||
try {
|
||||
if (m instanceof Field && !m.equals(cls.getField(m.getName()))) {
|
||||
// m is a field in a superclass that has been hidden by
|
||||
// a field with the same name in a subclass.
|
||||
System.out.println("Skipping " + m.getName() +
|
||||
" from " + m.getDeclaringClass());
|
||||
continue;
|
||||
}
|
||||
} catch (final NoSuchFieldException e) {
|
||||
}
|
||||
|
||||
list.add(m);
|
||||
}
|
||||
|
||||
return list.toArray(new Member[list.size()]);
|
||||
}
|
||||
|
||||
private static void generateClass(Class<?> clazz,
|
||||
StringBuilder implementationFile,
|
||||
StringBuilder headerFile) {
|
||||
String generatedName = clazz.getSimpleName();
|
||||
|
||||
CodeGenerator generator = new CodeGenerator(new ClassWithOptions(clazz, generatedName));
|
||||
|
||||
generator.generateMembers(sortAndFilterMembers(clazz, clazz.getConstructors()));
|
||||
generator.generateMembers(sortAndFilterMembers(clazz, clazz.getMethods()));
|
||||
generator.generateMembers(sortAndFilterMembers(clazz, clazz.getFields()));
|
||||
|
||||
headerFile.append(generator.getHeaderFileContents());
|
||||
implementationFile.append(generator.getWrapperFileContents());
|
||||
}
|
||||
|
||||
private static Vector<String> getClassList(String path) {
|
||||
Scanner scanner = null;
|
||||
try {
|
||||
scanner = new Scanner(new FileInputStream(path));
|
||||
|
||||
Vector lines = new Vector();
|
||||
while (scanner.hasNextLine()) {
|
||||
lines.add(scanner.nextLine());
|
||||
}
|
||||
return lines;
|
||||
} catch (Exception e) {
|
||||
System.out.println(e.toString());
|
||||
return null;
|
||||
} finally {
|
||||
if (scanner != null) {
|
||||
scanner.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeOutputFiles(String aOutputDir, String aPrefix, StringBuilder aHeaderFile,
|
||||
StringBuilder aImplementationFile) {
|
||||
FileOutputStream implStream = null;
|
||||
try {
|
||||
implStream = new FileOutputStream(new File(aOutputDir, aPrefix + ".cpp"));
|
||||
implStream.write(aImplementationFile.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to write " + aOutputDir + ". Perhaps a permissions issue?");
|
||||
e.printStackTrace(System.err);
|
||||
} finally {
|
||||
if (implStream != null) {
|
||||
try {
|
||||
implStream.close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to close implStream due to "+e);
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FileOutputStream headerStream = null;
|
||||
try {
|
||||
headerStream = new FileOutputStream(new File(aOutputDir, aPrefix + ".h"));
|
||||
headerStream.write(aHeaderFile.toString().getBytes());
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to write " + aOutputDir + ". Perhaps a permissions issue?");
|
||||
e.printStackTrace(System.err);
|
||||
} finally {
|
||||
if (headerStream != null) {
|
||||
try {
|
||||
headerStream.close();
|
||||
} catch (IOException e) {
|
||||
System.err.println("Unable to close headerStream due to "+e);
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors.classloader;
|
||||
|
||||
import org.mozilla.gecko.annotationProcessors.AnnotationInfo;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
/**
|
||||
* Union type to hold either a method, field, or ctor. Allows us to iterate "The generatable stuff", despite
|
||||
* the fact that such things can be of either flavour.
|
||||
*/
|
||||
public class AnnotatableEntity {
|
||||
public enum ENTITY_TYPE {METHOD, NATIVE, FIELD, CONSTRUCTOR}
|
||||
|
||||
private final Member mMember;
|
||||
public final ENTITY_TYPE mEntityType;
|
||||
|
||||
public final AnnotationInfo mAnnotationInfo;
|
||||
|
||||
public AnnotatableEntity(Member aObject, AnnotationInfo aAnnotationInfo) {
|
||||
mMember = aObject;
|
||||
mAnnotationInfo = aAnnotationInfo;
|
||||
|
||||
if (aObject instanceof Method) {
|
||||
if (Modifier.isNative(aObject.getModifiers())) {
|
||||
mEntityType = ENTITY_TYPE.NATIVE;
|
||||
} else {
|
||||
mEntityType = ENTITY_TYPE.METHOD;
|
||||
}
|
||||
} else if (aObject instanceof Field) {
|
||||
mEntityType = ENTITY_TYPE.FIELD;
|
||||
} else {
|
||||
mEntityType = ENTITY_TYPE.CONSTRUCTOR;
|
||||
}
|
||||
}
|
||||
|
||||
public Method getMethod() {
|
||||
if (mEntityType != ENTITY_TYPE.METHOD && mEntityType != ENTITY_TYPE.NATIVE) {
|
||||
throw new UnsupportedOperationException("Attempt to cast to unsupported member type.");
|
||||
}
|
||||
return (Method) mMember;
|
||||
}
|
||||
public Field getField() {
|
||||
if (mEntityType != ENTITY_TYPE.FIELD) {
|
||||
throw new UnsupportedOperationException("Attempt to cast to unsupported member type.");
|
||||
}
|
||||
return (Field) mMember;
|
||||
}
|
||||
public Constructor<?> getConstructor() {
|
||||
if (mEntityType != ENTITY_TYPE.CONSTRUCTOR) {
|
||||
throw new UnsupportedOperationException("Attempt to cast to unsupported member type.");
|
||||
}
|
||||
return (Constructor<?>) mMember;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors.classloader;
|
||||
|
||||
public class ClassWithOptions {
|
||||
public final Class<?> wrappedClass;
|
||||
public final String generatedName;
|
||||
|
||||
public ClassWithOptions(Class<?> someClass, String name) {
|
||||
wrappedClass = someClass;
|
||||
generatedName = name;
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors.classloader;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
/**
|
||||
* A classloader which can be initialised with a list of jar files and which can provide an iterator
|
||||
* over the top level classes in the jar files it was initialised with.
|
||||
* classNames is kept sorted to ensure iteration order is consistent across program invocations.
|
||||
* Otherwise, we'd forever be reporting the outdatedness of the generated code as we permute its
|
||||
* contents.
|
||||
*/
|
||||
public class IterableJarLoadingURLClassLoader extends URLClassLoader {
|
||||
LinkedList<String> classNames = new LinkedList<String>();
|
||||
|
||||
/**
|
||||
* Create an instance and return its iterator. Provides an iterator over the classes in the jar
|
||||
* files provided as arguments.
|
||||
* Inner classes are not supported.
|
||||
*
|
||||
* @param args A list of jar file names an iterator over the classes of which is desired.
|
||||
* @return An iterator over the top level classes in the jar files provided, in arbitrary order.
|
||||
*/
|
||||
public static Iterator<ClassWithOptions> getIteratorOverJars(String[] args) {
|
||||
URL[] urlArray = new URL[args.length];
|
||||
LinkedList<String> aClassNames = new LinkedList<String>();
|
||||
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
try {
|
||||
urlArray[i] = (new File(args[i])).toURI().toURL();
|
||||
|
||||
Enumeration<JarEntry> entries = new JarFile(args[i]).entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry e = entries.nextElement();
|
||||
String fName = e.getName();
|
||||
if (!fName.endsWith(".class")) {
|
||||
continue;
|
||||
}
|
||||
final String className = fName.substring(0, fName.length() - 6).replace('/', '.');
|
||||
|
||||
aClassNames.add(className);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
System.err.println("Error loading jar file \"" + args[i] + '"');
|
||||
e.printStackTrace(System.err);
|
||||
}
|
||||
}
|
||||
Collections.sort(aClassNames);
|
||||
return new JarClassIterator(new IterableJarLoadingURLClassLoader(urlArray, aClassNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a classloader capable of loading all classes given as URLs in urls. Used by static
|
||||
* method above.
|
||||
*
|
||||
* @param urls URLs for all classes the new instance shall be capable of loading.
|
||||
* @param aClassNames A list of names of the classes this instance shall be capable of loading.
|
||||
*/
|
||||
protected IterableJarLoadingURLClassLoader(URL[] urls, LinkedList<String> aClassNames) {// Array to populate with URLs for each class in the given jars.
|
||||
super(urls);
|
||||
classNames = aClassNames;
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors.classloader;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Class for iterating over an IterableJarLoadingURLClassLoader's classes.
|
||||
*
|
||||
* This class is not thread safe: use it only from a single thread.
|
||||
*/
|
||||
public class JarClassIterator implements Iterator<ClassWithOptions> {
|
||||
private IterableJarLoadingURLClassLoader mTarget;
|
||||
private Iterator<String> mTargetClassListIterator;
|
||||
|
||||
private ClassWithOptions lookAhead;
|
||||
|
||||
public JarClassIterator(IterableJarLoadingURLClassLoader aTarget) {
|
||||
mTarget = aTarget;
|
||||
mTargetClassListIterator = aTarget.classNames.iterator();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return fillLookAheadIfPossible();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassWithOptions next() {
|
||||
if (!fillLookAheadIfPossible()) {
|
||||
throw new IllegalStateException("Failed to look ahead in next()!");
|
||||
}
|
||||
ClassWithOptions next = lookAhead;
|
||||
lookAhead = null;
|
||||
return next;
|
||||
}
|
||||
|
||||
private boolean fillLookAheadIfPossible() {
|
||||
if (lookAhead != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mTargetClassListIterator.hasNext()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String className = mTargetClassListIterator.next();
|
||||
try {
|
||||
Class<?> ret = mTarget.loadClass(className);
|
||||
|
||||
// Incremental builds can leave stale classfiles in the jar. Such classfiles will cause
|
||||
// an exception at this point. We can safely ignore these classes - they cannot possibly
|
||||
// ever be loaded as they conflict with their parent class and will be killed by
|
||||
// Proguard later on anyway.
|
||||
final Class<?> enclosingClass;
|
||||
try {
|
||||
enclosingClass = ret.getEnclosingClass();
|
||||
} catch (IncompatibleClassChangeError e) {
|
||||
return fillLookAheadIfPossible();
|
||||
}
|
||||
|
||||
if (enclosingClass != null) {
|
||||
// Anonymous inner class - unsupported.
|
||||
// Or named inner class, which will be processed when we process the outer class.
|
||||
return fillLookAheadIfPossible();
|
||||
}
|
||||
|
||||
lookAhead = new ClassWithOptions(ret, ret.getSimpleName());
|
||||
return true;
|
||||
} catch (ClassNotFoundException e) {
|
||||
System.err.println("Unable to enumerate class: " + className + ". Corrupted jar file?");
|
||||
e.printStackTrace();
|
||||
System.exit(2);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Removal of classes from iterator not supported.");
|
||||
}
|
||||
}
|
||||
@@ -1,23 +0,0 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# 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/.
|
||||
|
||||
jar = add_java_jar('annotationProcessors')
|
||||
jar.sources += [
|
||||
'AnnotationInfo.java',
|
||||
'AnnotationProcessor.java',
|
||||
'classloader/AnnotatableEntity.java',
|
||||
'classloader/ClassWithOptions.java',
|
||||
'classloader/IterableJarLoadingURLClassLoader.java',
|
||||
'classloader/JarClassIterator.java',
|
||||
'CodeGenerator.java',
|
||||
'SDKProcessor.java',
|
||||
'utils/AlphabeticAnnotatableEntityComparator.java',
|
||||
'utils/GeneratableElementIterator.java',
|
||||
'utils/Utils.java',
|
||||
]
|
||||
jar.extra_jars += [
|
||||
CONFIG['ANDROID_TOOLS'] + '/lib/lint.jar',
|
||||
CONFIG['ANDROID_TOOLS'] + '/lib/lint-checks.jar',
|
||||
]
|
||||
@@ -1,81 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors.utils;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class AlphabeticAnnotatableEntityComparator<T extends Member> implements Comparator<T> {
|
||||
@Override
|
||||
public int compare(T aLhs, T aRhs) {
|
||||
// Constructors, Methods, Fields.
|
||||
boolean lIsConstructor = aLhs instanceof Constructor;
|
||||
boolean rIsConstructor = aRhs instanceof Constructor;
|
||||
boolean lIsMethod = aLhs instanceof Method;
|
||||
boolean rIsField = aRhs instanceof Field;
|
||||
|
||||
if (lIsConstructor) {
|
||||
if (!rIsConstructor) {
|
||||
return -1;
|
||||
}
|
||||
} else if (lIsMethod) {
|
||||
if (rIsConstructor) {
|
||||
return 1;
|
||||
} else if (rIsField) {
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (!rIsField) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// Verify these objects are the same type and cast them.
|
||||
if (aLhs instanceof Method) {
|
||||
return compare((Method) aLhs, (Method) aRhs);
|
||||
} else if (aLhs instanceof Field) {
|
||||
return compare((Field) aLhs, (Field) aRhs);
|
||||
} else {
|
||||
return compare((Constructor) aLhs, (Constructor) aRhs);
|
||||
}
|
||||
}
|
||||
|
||||
// Alas, the type system fails us.
|
||||
private static int compare(Method aLhs, Method aRhs) {
|
||||
// Initially, attempt to differentiate the methods be name alone..
|
||||
String lName = aLhs.getName();
|
||||
String rName = aRhs.getName();
|
||||
|
||||
int ret = lName.compareTo(rName);
|
||||
if (ret != 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// The names were the same, so we need to compare signatures to find their uniqueness..
|
||||
lName = Utils.getSignature(aLhs);
|
||||
rName = Utils.getSignature(aRhs);
|
||||
|
||||
return lName.compareTo(rName);
|
||||
}
|
||||
|
||||
private static int compare(Constructor<?> aLhs, Constructor<?> aRhs) {
|
||||
// The names will be the same, so we need to compare signatures to find their uniqueness..
|
||||
String lName = Utils.getSignature(aLhs);
|
||||
String rName = Utils.getSignature(aRhs);
|
||||
|
||||
return lName.compareTo(rName);
|
||||
}
|
||||
|
||||
private static int compare(Field aLhs, Field aRhs) {
|
||||
// Compare field names..
|
||||
String lName = aLhs.getName();
|
||||
String rName = aRhs.getName();
|
||||
|
||||
return lName.compareTo(rName);
|
||||
}
|
||||
}
|
||||
@@ -1,267 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors.utils;
|
||||
|
||||
import org.mozilla.gecko.annotationProcessors.AnnotationInfo;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.AnnotatableEntity;
|
||||
import org.mozilla.gecko.annotationProcessors.classloader.ClassWithOptions;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
|
||||
/**
|
||||
* Iterator over the methods in a given method list which have the WrappedJNIMethod
|
||||
* annotation. Returns an object containing both the annotation (Which may contain interesting
|
||||
* parameters) and the argument.
|
||||
*/
|
||||
public class GeneratableElementIterator implements Iterator<AnnotatableEntity> {
|
||||
private final ClassWithOptions mClass;
|
||||
private final Member[] mObjects;
|
||||
private AnnotatableEntity mNextReturnValue;
|
||||
private int mElementIndex;
|
||||
private AnnotationInfo mClassInfo;
|
||||
|
||||
private boolean mIterateEveryEntry;
|
||||
private boolean mSkipCurrentEntry;
|
||||
|
||||
public GeneratableElementIterator(ClassWithOptions annotatedClass) {
|
||||
mClass = annotatedClass;
|
||||
|
||||
final Class<?> aClass = annotatedClass.wrappedClass;
|
||||
// Get all the elements of this class as AccessibleObjects.
|
||||
Member[] aMethods = aClass.getDeclaredMethods();
|
||||
Member[] aFields = aClass.getDeclaredFields();
|
||||
Member[] aCtors = aClass.getDeclaredConstructors();
|
||||
|
||||
// Shove them all into one buffer.
|
||||
Member[] objs = new Member[aMethods.length + aFields.length + aCtors.length];
|
||||
|
||||
int offset = 0;
|
||||
System.arraycopy(aMethods, 0, objs, 0, aMethods.length);
|
||||
offset += aMethods.length;
|
||||
System.arraycopy(aFields, 0, objs, offset, aFields.length);
|
||||
offset += aFields.length;
|
||||
System.arraycopy(aCtors, 0, objs, offset, aCtors.length);
|
||||
|
||||
// Sort the elements to ensure determinism.
|
||||
Arrays.sort(objs, new AlphabeticAnnotatableEntityComparator<Member>());
|
||||
mObjects = objs;
|
||||
|
||||
// Check for "Wrap ALL the things" flag.
|
||||
for (Annotation annotation : aClass.getDeclaredAnnotations()) {
|
||||
mClassInfo = buildAnnotationInfo(aClass, annotation);
|
||||
if (mClassInfo != null) {
|
||||
mIterateEveryEntry = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSkipCurrentEntry) {
|
||||
throw new IllegalArgumentException("Cannot skip entire class");
|
||||
}
|
||||
|
||||
findNextValue();
|
||||
}
|
||||
|
||||
private Class<?>[] getFilteredInnerClasses() {
|
||||
// Go through all inner classes and see which ones we want to generate.
|
||||
final Class<?>[] candidates = mClass.wrappedClass.getDeclaredClasses();
|
||||
int count = 0;
|
||||
|
||||
for (int i = 0; i < candidates.length; ++i) {
|
||||
final GeneratableElementIterator testIterator
|
||||
= new GeneratableElementIterator(new ClassWithOptions(candidates[i], null));
|
||||
if (testIterator.hasNext()
|
||||
|| testIterator.getFilteredInnerClasses() != null) {
|
||||
count++;
|
||||
continue;
|
||||
}
|
||||
// Clear out ones that don't match.
|
||||
candidates[i] = null;
|
||||
}
|
||||
return count > 0 ? candidates : null;
|
||||
}
|
||||
|
||||
public ClassWithOptions[] getInnerClasses() {
|
||||
final Class<?>[] candidates = getFilteredInnerClasses();
|
||||
if (candidates == null) {
|
||||
return new ClassWithOptions[0];
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
for (Class<?> candidate : candidates) {
|
||||
if (candidate != null) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
final ClassWithOptions[] ret = new ClassWithOptions[count];
|
||||
count = 0;
|
||||
for (Class<?> candidate : candidates) {
|
||||
if (candidate != null) {
|
||||
ret[count++] = new ClassWithOptions(
|
||||
candidate, mClass.generatedName + "::" + candidate.getSimpleName());
|
||||
}
|
||||
}
|
||||
assert ret.length == count;
|
||||
|
||||
Arrays.sort(ret, new Comparator<ClassWithOptions>() {
|
||||
@Override public int compare(ClassWithOptions lhs, ClassWithOptions rhs) {
|
||||
return lhs.generatedName.compareTo(rhs.generatedName);
|
||||
}
|
||||
});
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static <T extends Enum<T>> T getEnumValue(Class<T> type, String name)
|
||||
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
|
||||
try {
|
||||
return Enum.valueOf(type, name.toUpperCase());
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
Object[] values = (Object[]) type.getDeclaredMethod("values").invoke(null);
|
||||
StringBuilder names = new StringBuilder();
|
||||
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (i != 0) {
|
||||
names.append(", ");
|
||||
}
|
||||
names.append(values[i].toString().toLowerCase());
|
||||
}
|
||||
|
||||
System.err.println("***");
|
||||
System.err.println("*** Invalid value \"" + name + "\" for " + type.getSimpleName());
|
||||
System.err.println("*** Specify one of " + names.toString());
|
||||
System.err.println("***");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(6);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private AnnotationInfo buildAnnotationInfo(AnnotatedElement element, Annotation annotation) {
|
||||
Class<? extends Annotation> annotationType = annotation.annotationType();
|
||||
final String annotationTypeName = annotationType.getName();
|
||||
if (!annotationTypeName.equals("org.mozilla.gecko.annotation.WrapForJNI")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String stubName = null;
|
||||
AnnotationInfo.ExceptionMode exceptionMode = null;
|
||||
AnnotationInfo.CallingThread callingThread = null;
|
||||
AnnotationInfo.DispatchTarget dispatchTarget = null;
|
||||
|
||||
try {
|
||||
final Method skipMethod = annotationType.getDeclaredMethod("skip");
|
||||
skipMethod.setAccessible(true);
|
||||
if ((Boolean) skipMethod.invoke(annotation)) {
|
||||
mSkipCurrentEntry = true;
|
||||
return null;
|
||||
}
|
||||
|
||||
// Determine the explicitly-given name of the stub to generate, if any.
|
||||
final Method stubNameMethod = annotationType.getDeclaredMethod("stubName");
|
||||
stubNameMethod.setAccessible(true);
|
||||
stubName = (String) stubNameMethod.invoke(annotation);
|
||||
|
||||
final Method exceptionModeMethod = annotationType.getDeclaredMethod("exceptionMode");
|
||||
exceptionModeMethod.setAccessible(true);
|
||||
exceptionMode = getEnumValue(
|
||||
AnnotationInfo.ExceptionMode.class,
|
||||
(String) exceptionModeMethod.invoke(annotation));
|
||||
|
||||
final Method calledFromMethod = annotationType.getDeclaredMethod("calledFrom");
|
||||
calledFromMethod.setAccessible(true);
|
||||
callingThread = getEnumValue(
|
||||
AnnotationInfo.CallingThread.class,
|
||||
(String) calledFromMethod.invoke(annotation));
|
||||
|
||||
final Method dispatchToMethod = annotationType.getDeclaredMethod("dispatchTo");
|
||||
dispatchToMethod.setAccessible(true);
|
||||
dispatchTarget = getEnumValue(
|
||||
AnnotationInfo.DispatchTarget.class,
|
||||
(String) dispatchToMethod.invoke(annotation));
|
||||
|
||||
} catch (NoSuchMethodException e) {
|
||||
System.err.println("Unable to find expected field on WrapForJNI annotation. Did the signature change?");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(3);
|
||||
} catch (IllegalAccessException e) {
|
||||
System.err.println("IllegalAccessException reading fields on WrapForJNI annotation. Seems the semantics of Reflection have changed...");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(4);
|
||||
} catch (InvocationTargetException e) {
|
||||
System.err.println("InvocationTargetException reading fields on WrapForJNI annotation. This really shouldn't happen.");
|
||||
e.printStackTrace(System.err);
|
||||
System.exit(5);
|
||||
}
|
||||
|
||||
// If the method name was not explicitly given in the annotation generate one...
|
||||
if (stubName.isEmpty()) {
|
||||
stubName = Utils.getNativeName(element);
|
||||
}
|
||||
|
||||
return new AnnotationInfo(stubName, exceptionMode, callingThread, dispatchTarget);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find and cache the next appropriately annotated method, plus the annotation parameter, if
|
||||
* one exists. Otherwise cache null, so hasNext returns false.
|
||||
*/
|
||||
private void findNextValue() {
|
||||
while (mElementIndex < mObjects.length) {
|
||||
Member candidateElement = mObjects[mElementIndex];
|
||||
mElementIndex++;
|
||||
for (Annotation annotation : ((AnnotatedElement) candidateElement).getDeclaredAnnotations()) {
|
||||
AnnotationInfo info = buildAnnotationInfo((AnnotatedElement)candidateElement, annotation);
|
||||
if (info != null) {
|
||||
mNextReturnValue = new AnnotatableEntity(candidateElement, info);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (mSkipCurrentEntry) {
|
||||
mSkipCurrentEntry = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// If no annotation found, we might be expected to generate anyway
|
||||
// using default arguments, thanks to the "Generate everything" annotation.
|
||||
if (mIterateEveryEntry) {
|
||||
AnnotationInfo annotationInfo = new AnnotationInfo(
|
||||
Utils.getNativeName(candidateElement),
|
||||
mClassInfo.exceptionMode,
|
||||
mClassInfo.callingThread,
|
||||
mClassInfo.dispatchTarget);
|
||||
mNextReturnValue = new AnnotatableEntity(candidateElement, annotationInfo);
|
||||
return;
|
||||
}
|
||||
}
|
||||
mNextReturnValue = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return mNextReturnValue != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AnnotatableEntity next() {
|
||||
AnnotatableEntity ret = mNextReturnValue;
|
||||
findNextValue();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException("Removal of methods from GeneratableElementIterator not supported.");
|
||||
}
|
||||
}
|
||||
@@ -1,288 +0,0 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko.annotationProcessors.utils;
|
||||
|
||||
import org.mozilla.gecko.annotationProcessors.AnnotationInfo;
|
||||
|
||||
import java.lang.reflect.AnnotatedElement;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Member;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A collection of utility methods used by CodeGenerator. Largely used for translating types.
|
||||
*/
|
||||
public class Utils {
|
||||
|
||||
// A collection of lookup tables to simplify the functions to follow...
|
||||
private static final HashMap<String, String> NATIVE_TYPES = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
NATIVE_TYPES.put("void", "void");
|
||||
NATIVE_TYPES.put("boolean", "bool");
|
||||
NATIVE_TYPES.put("byte", "int8_t");
|
||||
NATIVE_TYPES.put("char", "char16_t");
|
||||
NATIVE_TYPES.put("short", "int16_t");
|
||||
NATIVE_TYPES.put("int", "int32_t");
|
||||
NATIVE_TYPES.put("long", "int64_t");
|
||||
NATIVE_TYPES.put("float", "float");
|
||||
NATIVE_TYPES.put("double", "double");
|
||||
}
|
||||
|
||||
private static final HashMap<String, String> NATIVE_ARRAY_TYPES = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
NATIVE_ARRAY_TYPES.put("boolean", "mozilla::jni::BooleanArray");
|
||||
NATIVE_ARRAY_TYPES.put("byte", "mozilla::jni::ByteArray");
|
||||
NATIVE_ARRAY_TYPES.put("char", "mozilla::jni::CharArray");
|
||||
NATIVE_ARRAY_TYPES.put("short", "mozilla::jni::ShortArray");
|
||||
NATIVE_ARRAY_TYPES.put("int", "mozilla::jni::IntArray");
|
||||
NATIVE_ARRAY_TYPES.put("long", "mozilla::jni::LongArray");
|
||||
NATIVE_ARRAY_TYPES.put("float", "mozilla::jni::FloatArray");
|
||||
NATIVE_ARRAY_TYPES.put("double", "mozilla::jni::DoubleArray");
|
||||
}
|
||||
|
||||
private static final HashMap<String, String> CLASS_DESCRIPTORS = new HashMap<String, String>();
|
||||
|
||||
static {
|
||||
CLASS_DESCRIPTORS.put("void", "V");
|
||||
CLASS_DESCRIPTORS.put("boolean", "Z");
|
||||
CLASS_DESCRIPTORS.put("byte", "B");
|
||||
CLASS_DESCRIPTORS.put("char", "C");
|
||||
CLASS_DESCRIPTORS.put("short", "S");
|
||||
CLASS_DESCRIPTORS.put("int", "I");
|
||||
CLASS_DESCRIPTORS.put("long", "J");
|
||||
CLASS_DESCRIPTORS.put("float", "F");
|
||||
CLASS_DESCRIPTORS.put("double", "D");
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the C++ type corresponding to the provided type parameter.
|
||||
*
|
||||
* @param type Class to determine the corresponding JNI type for.
|
||||
* @return C++ type as a String
|
||||
*/
|
||||
public static String getNativeParameterType(Class<?> type, AnnotationInfo info) {
|
||||
final String name = type.getName().replace('.', '/');
|
||||
|
||||
if (NATIVE_TYPES.containsKey(name)) {
|
||||
return NATIVE_TYPES.get(name);
|
||||
}
|
||||
|
||||
if (type.isArray()) {
|
||||
final String compName = type.getComponentType().getName();
|
||||
if (NATIVE_ARRAY_TYPES.containsKey(compName)) {
|
||||
return NATIVE_ARRAY_TYPES.get(compName) + "::Param";
|
||||
}
|
||||
return "mozilla::jni::ObjectArray::Param";
|
||||
}
|
||||
|
||||
if (type.equals(String.class) || type.equals(CharSequence.class)) {
|
||||
return "mozilla::jni::String::Param";
|
||||
}
|
||||
|
||||
if (type.equals(Class.class)) {
|
||||
// You're doing reflection on Java objects from inside C, returning Class objects
|
||||
// to C, generating the corresponding code using this Java program. Really?!
|
||||
return "mozilla::jni::Class::Param";
|
||||
}
|
||||
|
||||
if (type.equals(Throwable.class)) {
|
||||
return "mozilla::jni::Throwable::Param";
|
||||
}
|
||||
|
||||
if (type.equals(ByteBuffer.class)) {
|
||||
return "mozilla::jni::ByteBuffer::Param";
|
||||
}
|
||||
|
||||
return "mozilla::jni::Object::Param";
|
||||
}
|
||||
|
||||
public static String getNativeReturnType(Class<?> type, AnnotationInfo info) {
|
||||
final String name = type.getName().replace('.', '/');
|
||||
|
||||
if (NATIVE_TYPES.containsKey(name)) {
|
||||
return NATIVE_TYPES.get(name);
|
||||
}
|
||||
|
||||
if (type.isArray()) {
|
||||
final String compName = type.getComponentType().getName();
|
||||
if (NATIVE_ARRAY_TYPES.containsKey(compName)) {
|
||||
return NATIVE_ARRAY_TYPES.get(compName) + "::LocalRef";
|
||||
}
|
||||
return "mozilla::jni::ObjectArray::LocalRef";
|
||||
}
|
||||
|
||||
if (type.equals(String.class)) {
|
||||
return "mozilla::jni::String::LocalRef";
|
||||
}
|
||||
|
||||
if (type.equals(Class.class)) {
|
||||
// You're doing reflection on Java objects from inside C, returning Class objects
|
||||
// to C, generating the corresponding code using this Java program. Really?!
|
||||
return "mozilla::jni::Class::LocalRef";
|
||||
}
|
||||
|
||||
if (type.equals(Throwable.class)) {
|
||||
return "mozilla::jni::Throwable::LocalRef";
|
||||
}
|
||||
|
||||
if (type.equals(ByteBuffer.class)) {
|
||||
return "mozilla::jni::ByteBuffer::LocalRef";
|
||||
}
|
||||
|
||||
return "mozilla::jni::Object::LocalRef";
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JNI class descriptor corresponding to the provided type parameter.
|
||||
*
|
||||
* @param type Class to determine the corresponding JNI descriptor for.
|
||||
* @return Class descripor as a String
|
||||
*/
|
||||
public static String getClassDescriptor(Class<?> type) {
|
||||
final String name = type.getName().replace('.', '/');
|
||||
|
||||
if (CLASS_DESCRIPTORS.containsKey(name)) {
|
||||
return CLASS_DESCRIPTORS.get(name);
|
||||
}
|
||||
|
||||
if (type.isArray()) {
|
||||
// Array names are already in class descriptor form.
|
||||
return name;
|
||||
}
|
||||
|
||||
return "L" + name + ';';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JNI signaure for a member.
|
||||
*
|
||||
* @param member Member to get the signature for.
|
||||
* @return JNI signature as a string
|
||||
*/
|
||||
public static String getSignature(Member member) {
|
||||
return member instanceof Field ? getSignature((Field) member) :
|
||||
member instanceof Method ? getSignature((Method) member) :
|
||||
getSignature((Constructor<?>) member);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JNI signaure for a field.
|
||||
*
|
||||
* @param member Field to get the signature for.
|
||||
* @return JNI signature as a string
|
||||
*/
|
||||
public static String getSignature(Field member) {
|
||||
return getClassDescriptor(member.getType());
|
||||
}
|
||||
|
||||
private static String getSignature(Class<?>[] args, Class<?> ret) {
|
||||
final StringBuilder sig = new StringBuilder("(");
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
sig.append(getClassDescriptor(args[i]));
|
||||
}
|
||||
return sig.append(')').append(getClassDescriptor(ret)).toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JNI signaure for a method.
|
||||
*
|
||||
* @param member Method to get the signature for.
|
||||
* @return JNI signature as a string
|
||||
*/
|
||||
public static String getSignature(Method member) {
|
||||
return getSignature(member.getParameterTypes(), member.getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JNI signaure for a constructor.
|
||||
*
|
||||
* @param member Constructor to get the signature for.
|
||||
* @return JNI signature as a string
|
||||
*/
|
||||
public static String getSignature(Constructor<?> member) {
|
||||
return getSignature(member.getParameterTypes(), void.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the C++ name for a member.
|
||||
*
|
||||
* @param member Member to get the name for.
|
||||
* @return JNI name as a string
|
||||
*/
|
||||
public static String getNativeName(Member member) {
|
||||
final String name = getMemberName(member);
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the C++ name for a member.
|
||||
*
|
||||
* @param member Member to get the name for.
|
||||
* @return JNI name as a string
|
||||
*/
|
||||
public static String getNativeName(Class<?> clz) {
|
||||
final String name = clz.getName();
|
||||
return name.substring(0, 1).toUpperCase() + name.substring(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the C++ name for a member.
|
||||
*
|
||||
* @param member Member to get the name for.
|
||||
* @return JNI name as a string
|
||||
*/
|
||||
public static String getNativeName(AnnotatedElement element) {
|
||||
if (element instanceof Class<?>) {
|
||||
return getNativeName((Class<?>)element);
|
||||
} else if (element instanceof Member) {
|
||||
return getNativeName((Member)element);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JNI name for a member.
|
||||
*
|
||||
* @param member Member to get the name for.
|
||||
* @return JNI name as a string
|
||||
*/
|
||||
public static String getMemberName(Member member) {
|
||||
if (member instanceof Constructor) {
|
||||
return "<init>";
|
||||
}
|
||||
return member.getName();
|
||||
}
|
||||
|
||||
public static String getUnqualifiedName(String name) {
|
||||
return name.substring(name.lastIndexOf(':') + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a member is declared static.
|
||||
*
|
||||
* @param member The Member to check.
|
||||
* @return true if the member is declared static, false otherwise.
|
||||
*/
|
||||
public static boolean isStatic(final Member member) {
|
||||
return Modifier.isStatic(member.getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if a member is declared final.
|
||||
*
|
||||
* @param member The Member to check.
|
||||
* @return true if the member is declared final, false otherwise.
|
||||
*/
|
||||
public static boolean isFinal(final Member member) {
|
||||
return Modifier.isFinal(member.getModifiers());
|
||||
}
|
||||
}
|
||||
@@ -28,24 +28,12 @@ MOZ_ARG_WITH_STRING(arch,
|
||||
if test -z "$MOZ_ARCH"; then
|
||||
dnl Defaults
|
||||
case "${CPU_ARCH}-${OS_TARGET}" in
|
||||
arm-Android)
|
||||
MOZ_THUMB=yes
|
||||
MOZ_ARCH=armv7-a
|
||||
MOZ_FPU=vfp
|
||||
MOZ_FLOAT_ABI=softfp
|
||||
MOZ_ALIGN=no
|
||||
;;
|
||||
arm-Darwin)
|
||||
MOZ_ARCH=toolchain-default
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if test "$MOZ_ARCH" = "armv6" -a "$OS_TARGET" = "Android"; then
|
||||
MOZ_FPU=vfp
|
||||
MOZ_FLOAT_ABI=softfp
|
||||
fi
|
||||
|
||||
MOZ_ARG_WITH_STRING(thumb,
|
||||
[ --with-thumb[[=yes|no|toolchain-default]]]
|
||||
[ Use Thumb instruction set (-mthumb)],
|
||||
|
||||
@@ -16,7 +16,7 @@ dnl set DEVELOPER_OPTIONS early; MOZ_DEFAULT_COMPILER is usually the first non-s
|
||||
DEVELOPER_OPTIONS=,
|
||||
DEVELOPER_OPTIONS=1)
|
||||
|
||||
dnl Default to MSVC for win32 and gcc-4.2 for darwin
|
||||
dnl Default to MSVC for win32
|
||||
dnl ==============================================================
|
||||
if test -z "$CROSS_COMPILE"; then
|
||||
case "$target" in
|
||||
@@ -176,8 +176,8 @@ if test "$GNU_CC"; then
|
||||
CFLAGS="$CFLAGS -ffunction-sections -fdata-sections"
|
||||
CXXFLAGS="$CXXFLAGS -ffunction-sections -fdata-sections"
|
||||
fi
|
||||
CFLAGS="$CFLAGS -fno-math-errno"
|
||||
CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-math-errno"
|
||||
CFLAGS="$CFLAGS -fno-math-errno -pipe"
|
||||
CXXFLAGS="$CXXFLAGS -fno-exceptions -fno-math-errno -pipe"
|
||||
|
||||
if test "$CPU_ARCH" = "x86" -o "$CPU_ARCH" = "x86_64"; then
|
||||
CFLAGS="$CFLAGS -msse2 -mfpmath=sse"
|
||||
@@ -186,7 +186,8 @@ if test "$GNU_CC"; then
|
||||
|
||||
if test -z "$CLANG_CC"; then
|
||||
case "$CC_VERSION" in
|
||||
4.* | 5.*)
|
||||
4.* | 5.* | 6.*)
|
||||
AC_MSG_ERROR([Unsupported GCC version.])
|
||||
;;
|
||||
*)
|
||||
# Lifetime Dead Store Elimination level 2 (default in GCC6+) breaks Gecko.
|
||||
@@ -270,18 +271,9 @@ if test "$GNU_CC" -a "$GCC_USE_GNU_LD" -a -z "$DEVELOPER_OPTIONS"; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# bionic in Android < 4.1 doesn't support PIE
|
||||
# On OSX, the linker defaults to building PIE programs when targetting OSX 10.7+,
|
||||
# but not when targetting OSX < 10.7. OSX < 10.7 doesn't support running PIE
|
||||
# programs, so as long as support for OSX 10.6 is kept, we can't build PIE.
|
||||
# Even after dropping 10.6 support, MOZ_PIE would not be useful since it's the
|
||||
# default (and clang says the -pie option is not used).
|
||||
# On other Unix systems, some file managers (Nautilus) can't start PIE programs
|
||||
if test -n "$gonkdir" && test "$ANDROID_VERSION" -ge 16; then
|
||||
MOZ_PIE=1
|
||||
else
|
||||
MOZ_PIE=
|
||||
fi
|
||||
# On some Unix systems, some file managers (e.g. Nautilus) can't start PIE programs
|
||||
# so don't enable it by default.
|
||||
MOZ_PIE=
|
||||
|
||||
MOZ_ARG_ENABLE_BOOL(pie,
|
||||
[ --enable-pie Enable Position Independent Executables],
|
||||
|
||||
Vendored
+2
-6
@@ -114,7 +114,7 @@ esac
|
||||
# Here we must recognize all the valid KERNEL-OS combinations.
|
||||
maybe_os=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\2/'`
|
||||
case $maybe_os in
|
||||
nto-qnx* | linux-gnu* | linux-android* | linux-dietlibc | linux-newlib* | \
|
||||
nto-qnx* | linux-gnu* | linux-dietlibc | linux-newlib* | \
|
||||
linux-musl* | linux-uclibc* | uclinux-uclibc* | uclinux-gnu* | kfreebsd*-gnu* | \
|
||||
knetbsd*-gnu* | netbsd*-gnu* | netbsd*-eabi* | \
|
||||
kopensolaris*-gnu* | \
|
||||
@@ -122,10 +122,6 @@ case $maybe_os in
|
||||
os=-$maybe_os
|
||||
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`
|
||||
;;
|
||||
android-linux)
|
||||
os=-linux-android
|
||||
basic_machine=`echo $1 | sed 's/^\(.*\)-\([^-]*-[^-]*\)$/\1/'`-unknown
|
||||
;;
|
||||
*)
|
||||
basic_machine=`echo $1 | sed 's/-[^-]*$//'`
|
||||
if [ $basic_machine != $1 ]
|
||||
@@ -1389,7 +1385,7 @@ case $os in
|
||||
| -udi* | -eabi* | -lites* | -ieee* | -go32* | -aux* \
|
||||
| -chorusos* | -chorusrdb* | -cegcc* \
|
||||
| -cygwin* | -msys* | -pe* | -psos* | -moss* | -proelf* | -rtems* \
|
||||
| -mingw32* | -mingw64* | -linux-gnu* | -linux-android* \
|
||||
| -mingw32* | -mingw64* | -linux-gnu* \
|
||||
| -linux-newlib* | -linux-musl* | -linux-uclibc* \
|
||||
| -uxpv* | -beos* | -mpeix* | -udk* | -moxiebox* \
|
||||
| -interix* | -uwin* | -mks* | -rhapsody* | -darwin* | -opened* \
|
||||
|
||||
@@ -7,11 +7,6 @@ dnl disabling frame pointers in this architecture based on the configure
|
||||
dnl options
|
||||
|
||||
AC_DEFUN([MOZ_SET_FRAMEPTR_FLAGS], [
|
||||
case "$target" in
|
||||
*android*)
|
||||
unwind_tables="-funwind-tables"
|
||||
;;
|
||||
esac
|
||||
if test "$GNU_CC"; then
|
||||
MOZ_ENABLE_FRAME_PTR="-fno-omit-frame-pointer $unwind_tables"
|
||||
MOZ_DISABLE_FRAME_PTR="-fomit-frame-pointer"
|
||||
|
||||
@@ -138,6 +138,8 @@ if test -n "$NSPR_CFLAGS" -o -n "$NSPR_LIBS"; then
|
||||
,
|
||||
AC_MSG_ERROR([system NSPR does not support PR_UINT64 or including prtypes.h does not provide it]))
|
||||
CFLAGS=$_SAVE_CFLAGS
|
||||
NSPR_INCLUDE_DIR=`echo ${NSPR_CFLAGS} | sed -e 's/.*-I\([^ ]*\).*/\1/'`
|
||||
NSPR_LIB_DIR=`echo ${NSPR_LIBS} | sed -e 's/.*-L\([^ ]*\).*/\1/'`
|
||||
elif test -z "$JS_POSIX_NSPR"; then
|
||||
NSPR_INCLUDE_DIR="${DIST}/include/nspr"
|
||||
NSPR_CFLAGS="-I${NSPR_INCLUDE_DIR}"
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
.. _build_androideclipse:
|
||||
|
||||
========================
|
||||
Android Eclipse Projects
|
||||
========================
|
||||
|
||||
The build system contains alpha support for generating Android Eclipse
|
||||
project files to aid with development.
|
||||
|
||||
To generate Android Eclipse project files, you'll need to have a fully
|
||||
built and packaged tree::
|
||||
|
||||
mach build && mach package
|
||||
|
||||
(This is because Eclipse itself packages an APK containing
|
||||
``omni.ja``, and ``omni.ja`` is only assembled during packaging.)
|
||||
|
||||
Then, simply generate the Android Eclipse build backend::
|
||||
|
||||
mach build-backend -b AndroidEclipse
|
||||
|
||||
If all goes well, the path to the generated projects should be
|
||||
printed (currently, ``$OBJDIR/android_eclipse``).
|
||||
|
||||
To use the generated Android Eclipse project files, you'll need to
|
||||
have a recent version of Eclipse (see `Tested Versions`_) with the
|
||||
`Eclipse ADT plugin
|
||||
<http://developer.android.com/tools/sdk/eclipse-adt.html>`_
|
||||
installed. You can then import all the projects into Eclipse using
|
||||
*File > Import ... > General > Existing Projects into Workspace*.
|
||||
|
||||
Updating Project Files
|
||||
======================
|
||||
|
||||
As you pull and update the source tree, your Android Eclipse files may
|
||||
fall out of sync with the build configuration. The tree should still
|
||||
build fine from within Eclipse, but source files may be missing and in
|
||||
rare circumstances Eclipse's index may not have the proper build
|
||||
configuration.
|
||||
|
||||
To account for this, you'll want to periodically regenerate the
|
||||
Android Eclipse project files. You can do this by running ``mach build
|
||||
&& mach package && mach build-backend -b AndroidEclipse`` from the
|
||||
command line. It's a good idea to refresh and clean build all projects
|
||||
in Eclipse after doing this.
|
||||
|
||||
In future, we'd like to include an Android Eclipse run configuration
|
||||
or build target that integrates updating the project files.
|
||||
|
||||
Currently, regeneration rewrites the original project files. **If
|
||||
you've made any customizations to the projects, they will likely get
|
||||
overwritten.** We would like to improve this user experience in the
|
||||
future.
|
||||
|
||||
Troubleshooting
|
||||
===============
|
||||
|
||||
If Eclipse's builder gets confused, you should always refresh and
|
||||
clean build all projects. If Eclipse's builder is continually
|
||||
confused, you can see a log of what is happening at
|
||||
``$OBJDIR/android_eclipse/build.log``.
|
||||
|
||||
If you run into memory problems executing ``dex``, you should
|
||||
`Increase Eclipse's memory limits <http://stackoverflow.com/a/11093228>`_.
|
||||
|
||||
The produced Android Eclipse project files are unfortunately not
|
||||
portable. Please don't move them around.
|
||||
|
||||
Structure of Android Eclipse projects
|
||||
=====================================
|
||||
|
||||
The Android Eclipse backend generates several projects spanning Fennec
|
||||
itself and its tests. You'll mostly interact with the *Fennec* project
|
||||
itself.
|
||||
|
||||
In future, we'd like to expand this documentation to include some of
|
||||
the technical details of how the Eclipse integration works, and how to
|
||||
add additional Android Eclipse projects using the ``moz.build``
|
||||
system.
|
||||
|
||||
Tested Versions
|
||||
===============
|
||||
|
||||
=============== ==================================== =================
|
||||
OS Version Working as of
|
||||
=============== ==================================== =================
|
||||
Mac OS X Luna (Build id: 20130919-0819) February 2014
|
||||
Mac OS X Kepler (Build id: 20131219-0014) February 2014
|
||||
Mac OS X 10.8.5 Kepler (Build id: 20130919-0819) February 2014
|
||||
=============== ==================================== =================
|
||||
@@ -18,14 +18,14 @@ built tree::
|
||||
|
||||
mach build
|
||||
|
||||
Then, simply generate the Android Eclipse build backend::
|
||||
Then, simply generate the Eclipse build backend::
|
||||
|
||||
mach build-backend -b CppEclipse
|
||||
|
||||
If all goes well, the path to the generated workspace should be
|
||||
printed (currently, ``$OBJDIR/android_eclipse``).
|
||||
printed.
|
||||
|
||||
To use the generated Android Eclipse project files, you'll need to
|
||||
To use the generated Eclipse project files, you'll need to
|
||||
have a Eclipse CDT 8.3 (We plan to follow the latest Eclipse release)
|
||||
`Eclipse CDT plugin
|
||||
<https://www.eclipse.org/cdt/>`_
|
||||
@@ -43,7 +43,7 @@ rare circumstances Eclipse's index may not have the proper build
|
||||
configuration.
|
||||
|
||||
To account for this, you'll want to periodically regenerate the
|
||||
Android Eclipse project files. You can do this by running ``mach build
|
||||
Eclipse project files. You can do this by running ``mach build
|
||||
&& mach build-backend -b CppEclipse`` from the
|
||||
command line.
|
||||
|
||||
|
||||
@@ -33,7 +33,6 @@ integrated development environment (IDE)
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
androideclipse
|
||||
cppeclipse
|
||||
visualstudio
|
||||
|
||||
|
||||
@@ -17,84 +17,5 @@ like a re-packaging post-processing step.
|
||||
There are scripts in-tree in mozharness to orchestrate these re-packaging
|
||||
steps for `Desktop
|
||||
<https://dxr.mozilla.org/mozilla-central/source/testing/mozharness/scripts/desktop_l10n.py>`_
|
||||
and `Android
|
||||
<https://dxr.mozilla.org/mozilla-central/source/testing/mozharness/scripts/mobile_l10n.py>`_
|
||||
but they rely heavily on buildbot information so they are almost impossible to
|
||||
run locally.
|
||||
|
||||
The following instructions are extracted from the `Android script with hg hash
|
||||
494289c7
|
||||
<https://dxr.mozilla.org/mozilla-central/rev/494289c72ba3997183e7b5beaca3e0447ecaf96d/testing/mozharness/scripts/mobile_l10n.py>`_,
|
||||
and may need to be updated and slightly modified for Desktop.
|
||||
|
||||
Step by step instructions for Android
|
||||
-------------------------------------
|
||||
|
||||
This assumes that ``$AB_CD`` is the locale you want to repack with; I tested
|
||||
with "ar" and "en-GB".
|
||||
|
||||
.. warning:: l10n repacks do not work with artifact builds. Repackaging
|
||||
compiles no code so supporting ``--disable-compile-environment`` would not
|
||||
save much, if any, time.
|
||||
|
||||
#. You must have a built and packaged object directory, or a pre-built
|
||||
``en-US`` package.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
./mach build
|
||||
./mach package
|
||||
|
||||
#. Clone ``l10n-central/$AB_CD`` so that it is a sibling to your
|
||||
``mozilla-central`` directory.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
$ ls -al
|
||||
mozilla-central
|
||||
...
|
||||
$ mkdir -p l10n-central
|
||||
$ hg clone https://hg.mozilla.org/l10n-central/$AB_CD l10n-central/$AB_CD
|
||||
$ ls -al
|
||||
mozilla-central
|
||||
l10n-central/$AB_CD
|
||||
...
|
||||
|
||||
#. Copy your ``mozconfig`` to ``mozconfig.l10n`` and add the following.
|
||||
|
||||
::
|
||||
|
||||
ac_add_options --with-l10n-base=../../l10n-central
|
||||
ac_add_options --disable-tests
|
||||
mk_add_options MOZ_OBJDIR=./objdir-l10n
|
||||
|
||||
#. Configure and prepare the l10n object directory.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
MOZCONFIG=mozconfig.l10n ./mach configure
|
||||
MOZCONFIG=mozconfig.l10n ./mach build -C config export
|
||||
MOZCONFIG=mozconfig.l10n ./mach build buildid.h
|
||||
|
||||
#. Copy your built package and unpack it into the l10n object directory.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
cp $OBJDIR/dist/fennec-*en-US*.apk ./objdir-l10n/dist
|
||||
MOZCONFIG=mozconfig.l10n ./mach build -C mobile/android/locales unpack
|
||||
|
||||
#. Run the ``compare-locales`` script to write locale-specific changes into
|
||||
``objdir-l10n/merged``.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
MOZCONFIG=mozconfig.l10n ./mach compare-locales --merge-dir objdir-l10n/merged $AB_CD
|
||||
|
||||
#. Finally, repackage using the locale-specific changes.
|
||||
|
||||
.. code-block:: shell
|
||||
|
||||
MOZCONFIG=mozconfig.l10n LOCALE_MERGEDIR=`realpath objdir-l10n/merged` ./mach build -C mobile/android/locales installers-$AB_CD
|
||||
|
||||
(Note the absolute path for ``LOCALE_MERGEDIR``.) You should find a
|
||||
re-packaged build at ``objdir-l10n/dist/fennec-*$AB_CD*.apk``.
|
||||
+4
-19
@@ -62,8 +62,7 @@ bits
|
||||
buildapp
|
||||
The path to the XUL application being built.
|
||||
|
||||
For desktop Firefox, this is ``browser``. For Fennec, it's
|
||||
``mobile/android``. For B2G, it's ``b2g``.
|
||||
For desktop Firefox, this is ``browser``.
|
||||
|
||||
crashreporter
|
||||
Whether the crash reporter is enabled for this build.
|
||||
@@ -72,13 +71,6 @@ crashreporter
|
||||
|
||||
Always defined.
|
||||
|
||||
datareporting
|
||||
Whether data reporting (MOZ_DATA_REPORTING) is enabled for this build.
|
||||
|
||||
Values are ``true`` and ``false``.
|
||||
|
||||
Always defined.
|
||||
|
||||
debug
|
||||
Whether this is a debug build.
|
||||
|
||||
@@ -86,13 +78,6 @@ debug
|
||||
|
||||
Always defined.
|
||||
|
||||
healthreport
|
||||
Whether the Health Report feature is enabled.
|
||||
|
||||
Values are ``true`` and ``false``.
|
||||
|
||||
Always defined.
|
||||
|
||||
mozconfig
|
||||
The path of the :ref:`mozconfig file <mozconfig>` used to produce this build.
|
||||
|
||||
@@ -107,9 +92,9 @@ nightly_build
|
||||
|
||||
os
|
||||
The operating system the build is produced for. Values for tier-1
|
||||
supported platforms are ``linux``, ``win``, ``mac``, ``b2g``, and
|
||||
``android``. For other platforms, the value is the lowercase version
|
||||
of the ``OS_TARGET`` variable from ``config.status``.
|
||||
supported platforms are ``linux`` and ``win``. For other platforms,
|
||||
the value is the lowercase version of the ``OS_TARGET`` variable
|
||||
from ``config.status``.
|
||||
|
||||
Always defined.
|
||||
|
||||
|
||||
@@ -54,58 +54,3 @@ archive::
|
||||
$ ./mach python build/windows_toolchain.py create-zip vs2015u3
|
||||
|
||||
The produced archive will be the argument to ``create-zip`` + ``.zip``.
|
||||
|
||||
Firefox for Android with Gradle
|
||||
===============================
|
||||
|
||||
To build Firefox for Android with Gradle in automation, archives
|
||||
containing both the Gradle executable and a Maven repository
|
||||
comprising the exact build dependencies are produced and uploaded to
|
||||
an internal Mozilla server. The build automation will download,
|
||||
verify, and extract these archive before building. These archives
|
||||
provide a self-contained Gradle and Maven repository so that machines
|
||||
don't need to fetch additional Maven dependencies at build time.
|
||||
(Gradle and the downloaded Maven dependencies can be both
|
||||
redistributed publicly.)
|
||||
|
||||
Archiving the Gradle executable is straight-forward, but archiving a
|
||||
local Maven repository is not. Therefore a special Task Cluster
|
||||
Docker image and job exist for producing the required archives. The
|
||||
Docker image definition is rooted in
|
||||
``taskcluster/docker/android-gradle-build``. The Task Cluster job
|
||||
definition is in
|
||||
``testing/taskcluster/tasks/builds/android_api_15_gradle_dependencies.yml``.
|
||||
The job runs in a container based on the custom Docker image and
|
||||
spawns a Sonatype Nexus proxying Maven repository process in the
|
||||
background. The job builds Firefox for Android using Gradle and the
|
||||
in-tree Gradle configuration rooted at ``build.gradle``. The spawned
|
||||
proxying Maven repository downloads external dependencies and collects
|
||||
them. After the Gradle build completes, the job archives the Gradle
|
||||
version used to build, and the downloaded Maven repository, and
|
||||
exposes them as Task Cluster artifacts.
|
||||
|
||||
Here is `an example try job fetching these dependencies
|
||||
<https://treeherder.mozilla.org/#/jobs?repo=try&revision=75bc98935147&selectedJob=17793653>`_.
|
||||
The resulting task produced a `Gradle archive
|
||||
<https://queue.taskcluster.net/v1/task/CeYMgAP3Q-KF8h37nMhJjg/runs/0/artifacts/public%2Fbuild%2Fgradle.tar.xz>`_
|
||||
and a `Maven repository archive
|
||||
<https://queue.taskcluster.net/v1/task/CeYMgAP3Q-KF8h37nMhJjg/runs/0/artifacts/public%2Fbuild%2Fjcentral.tar.xz>`_.
|
||||
These archives were then uploaded (manually) to Mozilla automation
|
||||
using tooltool for consumption in Gradle builds.
|
||||
|
||||
To update the version of Gradle in the archive produced, update
|
||||
``gradle/wrapper/gradle-wrapper.properties``. Be sure to also update
|
||||
the SHA256 checksum to prevent poisoning the build machines!
|
||||
|
||||
To update the versions of Gradle dependencies used, update
|
||||
``dependencies`` sections in the in-tree Gradle configuration rooted
|
||||
at ``build.gradle``. Once you are confident your changes build
|
||||
locally, push a fresh try build with an invocation like::
|
||||
|
||||
$ hg push-to-try -m "try: -b o -p android-api-15-gradle-dependencies"
|
||||
|
||||
Then `upload your archives to tooltool
|
||||
<https://wiki.mozilla.org/ReleaseEngineering/Applications/Tooltool#How_To_Upload_To_Tooltool>`_,
|
||||
update the in-tree manifests in
|
||||
``mobile/android/config/tooltool-manifests``, and push a fresh try
|
||||
build.
|
||||
+4
-39
@@ -3,7 +3,9 @@
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
gyp_vars = {
|
||||
include('gyp_base.mozbuild')
|
||||
|
||||
gyp_vars.update({
|
||||
'lsan': 0,
|
||||
'asan': 0,
|
||||
'build_with_mozilla': 1,
|
||||
@@ -18,8 +20,6 @@ gyp_vars = {
|
||||
'clang_use_chrome_plugins': 0,
|
||||
'enable_protobuf': 0,
|
||||
'include_tests': 0,
|
||||
'enable_android_opensl': 1,
|
||||
'enable_android_opensl_output': 0,
|
||||
# use_system_lib* still seems to be in use in trunk/build
|
||||
'use_system_libjpeg': 0,
|
||||
'use_system_libvpx': 0,
|
||||
@@ -67,42 +67,7 @@ gyp_vars = {
|
||||
# linker to throw away uneeded bits.
|
||||
'include_isac': 1,
|
||||
'include_pcm16b': 1,
|
||||
}
|
||||
|
||||
os = CONFIG['OS_TARGET']
|
||||
|
||||
if os == 'WINNT':
|
||||
gyp_vars.update(
|
||||
MSVS_VERSION=CONFIG['_MSVS_VERSION'],
|
||||
MSVS_OS_BITS=64 if CONFIG['HAVE_64BIT_BUILD'] else 32,
|
||||
)
|
||||
elif os == 'Android':
|
||||
gyp_vars.update(
|
||||
gtest_target_type='executable',
|
||||
moz_webrtc_mediacodec=1,
|
||||
android_toolchain=CONFIG.get('ANDROID_TOOLCHAIN', ''),
|
||||
)
|
||||
|
||||
flavors = {
|
||||
'WINNT': 'win',
|
||||
'Android': 'android',
|
||||
'Linux': 'linux',
|
||||
'SunOS': 'solaris',
|
||||
'GNU/kFreeBSD': 'freebsd',
|
||||
'DragonFly': 'dragonfly',
|
||||
'FreeBSD': 'freebsd',
|
||||
'NetBSD': 'netbsd',
|
||||
'OpenBSD': 'openbsd',
|
||||
}
|
||||
gyp_vars['OS'] = flavors.get(os)
|
||||
|
||||
arches = {
|
||||
'x86_64': 'x64',
|
||||
'x86': 'ia32',
|
||||
'aarch64': 'arm64',
|
||||
}
|
||||
|
||||
gyp_vars['target_arch'] = arches.get(CONFIG['CPU_ARCH'], CONFIG['CPU_ARCH'])
|
||||
})
|
||||
|
||||
if CONFIG['ARM_ARCH']:
|
||||
if int(CONFIG['ARM_ARCH']) < 7:
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# 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/.
|
||||
|
||||
gyp_vars = {}
|
||||
|
||||
os = CONFIG['OS_TARGET']
|
||||
|
||||
if os == 'WINNT':
|
||||
gyp_vars.update(
|
||||
MSVS_VERSION=CONFIG['_MSVS_VERSION'],
|
||||
MSVS_OS_BITS=64 if CONFIG['HAVE_64BIT_BUILD'] else 32,
|
||||
)
|
||||
|
||||
flavors = {
|
||||
'WINNT': 'win',
|
||||
'Linux': 'linux',
|
||||
'Darwin': 'mac' if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa' else 'ios',
|
||||
'SunOS': 'solaris',
|
||||
'GNU/kFreeBSD': 'freebsd',
|
||||
'DragonFly': 'dragonfly',
|
||||
'FreeBSD': 'freebsd',
|
||||
'NetBSD': 'netbsd',
|
||||
'OpenBSD': 'openbsd',
|
||||
}
|
||||
gyp_vars['OS'] = flavors.get(os)
|
||||
|
||||
arches = {
|
||||
'x86_64': 'x64',
|
||||
'x86': 'ia32',
|
||||
'aarch64': 'arm64',
|
||||
}
|
||||
|
||||
gyp_vars['host_arch'] = arches.get(CONFIG['HOST_CPU_ARCH'], CONFIG['HOST_CPU_ARCH'])
|
||||
gyp_vars['target_arch'] = arches.get(CONFIG['CPU_ARCH'], CONFIG['CPU_ARCH'])
|
||||
+14
-10
@@ -35,7 +35,6 @@ Press ENTER/RETURN to continue or CTRL+c to abort.
|
||||
# TODO Bug 794506 Integrate with the in-tree virtualenv configuration.
|
||||
SEARCH_PATHS = [
|
||||
'python/mach',
|
||||
'python/mozboot',
|
||||
'python/mozbuild',
|
||||
'python/mozlint',
|
||||
'python/mozversioncontrol',
|
||||
@@ -65,11 +64,6 @@ SEARCH_PATHS = [
|
||||
'other-licenses/ply',
|
||||
'taskcluster',
|
||||
'testing',
|
||||
'testing/firefox-ui/harness',
|
||||
'testing/marionette/client',
|
||||
'testing/marionette/harness',
|
||||
'testing/marionette/harness/marionette_harness/runner/mixins/browsermob-proxy-py',
|
||||
'testing/marionette/puppeteer/firefox',
|
||||
'testing/mozbase/mozcrash',
|
||||
'testing/mozbase/mozdebug',
|
||||
'testing/mozbase/mozdevice',
|
||||
@@ -105,16 +99,13 @@ MACH_MODULES = [
|
||||
'python/mach/mach/commands/commandinfo.py',
|
||||
'python/mach/mach/commands/settings.py',
|
||||
'python/compare-locales/mach_commands.py',
|
||||
'python/mozboot/mozboot/mach_commands.py',
|
||||
'python/mozbuild/mozbuild/mach_commands.py',
|
||||
'python/mozbuild/mozbuild/backend/mach_commands.py',
|
||||
'python/mozbuild/mozbuild/compilation/codecomplete.py',
|
||||
'python/mozbuild/mozbuild/frontend/mach_commands.py',
|
||||
'services/common/tests/mach_commands.py',
|
||||
'taskcluster/mach_commands.py',
|
||||
'testing/firefox-ui/mach_commands.py',
|
||||
'testing/mach_commands.py',
|
||||
'testing/marionette/mach_commands.py',
|
||||
'testing/mochitest/mach_commands.py',
|
||||
'testing/mozharness/mach_commands.py',
|
||||
'testing/talos/mach_commands.py',
|
||||
@@ -198,7 +189,20 @@ def bootstrap(topsrcdir, mozilla_dir=None):
|
||||
# like surprises.
|
||||
sys.path[0:0] = [os.path.join(mozilla_dir, path) for path in SEARCH_PATHS]
|
||||
import mach.main
|
||||
from mozboot.util import get_state_dir
|
||||
|
||||
def get_state_dir():
|
||||
"""Obtain path to a directory to hold state.
|
||||
|
||||
Returns a tuple of the path and a bool indicating whether the
|
||||
value came from an environment variable.
|
||||
"""
|
||||
state_user_dir = os.path.expanduser('~/.mozbuild')
|
||||
state_env_dir = os.environ.get('MOZBUILD_STATE_PATH')
|
||||
|
||||
if state_env_dir:
|
||||
return state_env_dir, True
|
||||
else:
|
||||
return state_user_dir, False
|
||||
|
||||
def telemetry_handler(context, data):
|
||||
# We have not opted-in to telemetry
|
||||
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -e
|
||||
|
||||
if ! git remote -v | grep origin | grep -q cctools-port; then
|
||||
echo "must be in a cctools-port checkout"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir build-cctools
|
||||
cd build-cctools
|
||||
|
||||
CFLAGS='-mcpu=generic -mtune=generic' MACOSX_DEPLOYMENT_TARGET=10.7 ../cctools/configure --target=x86_64-apple-darwin11
|
||||
env MACOSX_DEPLOYMENT_TARGET=10.7 make -s -j4
|
||||
|
||||
if test ! -e ld64/src/ld/ld; then
|
||||
echo "ld did not get built"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
gtar jcf cctools.tar.bz2 ld64/src/ld/ld --transform 's#ld64/src/ld#cctools/bin#'
|
||||
|
||||
cd ../
|
||||
|
||||
echo "build from $(git show --pretty=format:%H -s HEAD) complete!"
|
||||
echo "upload the build-cctools/cctools.tar.bz2 file to tooltool"
|
||||
@@ -0,0 +1,47 @@
|
||||
# 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/.
|
||||
|
||||
MOZ_AUTOMATION_L10N_CHECK=0
|
||||
|
||||
if [ "x$IS_NIGHTLY" = "xyes" ]; then
|
||||
# Some nightlies (eg: Mulet) don't want these set.
|
||||
MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1}
|
||||
MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
|
||||
fi
|
||||
. "$topsrcdir/build/mozconfig.common"
|
||||
|
||||
# ld needs libLTO.so from llvm
|
||||
mk_add_options "export LD_LIBRARY_PATH=$topsrcdir/clang/lib"
|
||||
|
||||
CROSS_CCTOOLS_PATH=$topsrcdir/cctools
|
||||
CROSS_SYSROOT=$topsrcdir/MacOSX10.7.sdk
|
||||
CROSS_PRIVATE_FRAMEWORKS=$CROSS_SYSROOT/System/Library/PrivateFrameworks
|
||||
FLAGS="-target x86_64-apple-darwin10 -mlinker-version=136 -B $CROSS_CCTOOLS_PATH/bin -isysroot $CROSS_SYSROOT"
|
||||
|
||||
export CC="$topsrcdir/clang/bin/clang $FLAGS"
|
||||
export CXX="$topsrcdir/clang/bin/clang++ $FLAGS"
|
||||
export CPP="$topsrcdir/clang/bin/clang $FLAGS -E"
|
||||
export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
|
||||
export LDFLAGS="-Wl,-syslibroot,$CROSS_SYSROOT -Wl,-dead_strip"
|
||||
export TOOLCHAIN_PREFIX=$CROSS_CCTOOLS_PATH/bin/x86_64-apple-darwin10-
|
||||
export DSYMUTIL=$topsrcdir/clang/bin/llvm-dsymutil
|
||||
export GENISOIMAGE=$topsrcdir/genisoimage/genisoimage
|
||||
export DMG_TOOL=$topsrcdir/dmg/dmg
|
||||
|
||||
export HOST_CC="$topsrcdir/clang/bin/clang"
|
||||
export HOST_CXX="$topsrcdir/clang/bin/clang++"
|
||||
export HOST_CPP="$topsrcdir/clang/bin/clang -E"
|
||||
export HOST_CFLAGS="-g"
|
||||
export HOST_CXXFLAGS="-g"
|
||||
export HOST_LDFLAGS="-g"
|
||||
|
||||
ac_add_options --target=x86_64-apple-darwin
|
||||
ac_add_options --with-macos-private-frameworks=$CROSS_PRIVATE_FRAMEWORKS
|
||||
|
||||
# Enable static analysis checks by default on OSX cross builds.
|
||||
ac_add_options --enable-clang-plugin
|
||||
|
||||
. "$topsrcdir/build/mozconfig.cache"
|
||||
|
||||
export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
|
||||
@@ -0,0 +1,46 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
if [ "x$IS_NIGHTLY" = "xyes" ]; then
|
||||
# Some nightlies (eg: Mulet) don't want these set.
|
||||
MOZ_AUTOMATION_UPLOAD_SYMBOLS=${MOZ_AUTOMATION_UPLOAD_SYMBOLS-1}
|
||||
MOZ_AUTOMATION_UPDATE_PACKAGING=${MOZ_AUTOMATION_UPDATE_PACKAGING-1}
|
||||
MOZ_AUTOMATION_SDK=${MOZ_AUTOMATION_SDK-1}
|
||||
fi
|
||||
. "$topsrcdir/build/mozconfig.common"
|
||||
|
||||
if [ -d "$topsrcdir/clang" ]; then
|
||||
# mozilla-central based build
|
||||
export CC=$topsrcdir/clang/bin/clang
|
||||
export CXX=$topsrcdir/clang/bin/clang++
|
||||
export LLVMCONFIG=$topsrcdir/clang/bin/llvm-config
|
||||
export DSYMUTIL=$topsrcdir/clang/bin/llvm-dsymutil
|
||||
# Use an updated linker.
|
||||
ldflags="-B$topsrcdir/cctools/bin"
|
||||
elif [ -d "$topsrcdir/../clang" ]; then
|
||||
# comm-central based build
|
||||
export CC=$topsrcdir/../clang/bin/clang
|
||||
export CXX=$topsrcdir/../clang/bin/clang++
|
||||
export LLVMCONFIG=$topsrcdir/../clang/bin/llvm-config
|
||||
export DSYMUTIL=$topsrcdir/../clang/bin/llvm-dsymutil
|
||||
# Use an updated linker.
|
||||
ldflags="-B$topsrcdir/../cctools/bin"
|
||||
fi
|
||||
|
||||
# Ensure the updated linker doesn't generate things our older build tools
|
||||
# don't understand.
|
||||
ldflags="$ldflags -Wl,-no_data_in_code_info"
|
||||
export LDFLAGS="$ldflags"
|
||||
|
||||
# If not set use the system default clang
|
||||
if [ -z "$CC" ]; then
|
||||
export CC=clang
|
||||
fi
|
||||
|
||||
# If not set use the system default clang++
|
||||
if [ -z "$CXX" ]; then
|
||||
export CXX=clang++
|
||||
fi
|
||||
|
||||
export SOCORRO_SYMBOL_UPLOAD_TOKEN_FILE=/builds/crash-stats-api.token
|
||||
@@ -0,0 +1,5 @@
|
||||
if test `uname -s` = Linux; then
|
||||
. $topsrcdir/build/macosx/cross-mozconfig.common
|
||||
else
|
||||
. $topsrcdir/build/macosx/local-mozconfig.common
|
||||
fi
|
||||
@@ -0,0 +1,20 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2)
|
||||
return 1;
|
||||
|
||||
uid_t realuser = getuid();
|
||||
char uidstring[20];
|
||||
snprintf(uidstring, 19, "%i", realuser);
|
||||
uidstring[19] = '\0';
|
||||
|
||||
return execl("/usr/sbin/chown",
|
||||
"/usr/sbin/chown", "-R", "-h", uidstring, argv[1], (char*) 0);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
/* 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 <unistd.h>
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc != 2)
|
||||
return 1;
|
||||
|
||||
return execl("/usr/sbin/chown",
|
||||
"/usr/sbin/chown", "-R", "-h", "root:admin", argv[1], (char*) 0);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
# 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/.
|
||||
|
||||
# i386/x86-64 Universal Build mozconfig
|
||||
|
||||
# As used here, arguments in $MOZ_BUILD_PROJECTS are suitable as arguments
|
||||
# to gcc's -arch parameter.
|
||||
mk_add_options MOZ_BUILD_PROJECTS="x86_64 i386"
|
||||
|
||||
. $topsrcdir/build/macosx/universal/mozconfig.common
|
||||
@@ -0,0 +1,55 @@
|
||||
# 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/.
|
||||
|
||||
mk_add_options MOZ_UNIFY_BDATE=1
|
||||
|
||||
DARWIN_VERSION=10
|
||||
ac_add_app_options i386 --target=i386-apple-darwin$DARWIN_VERSION
|
||||
ac_add_app_options x86_64 --target=x86_64-apple-darwin$DARWIN_VERSION
|
||||
ac_add_app_options i386 --with-unify-dist=../x86_64/dist
|
||||
ac_add_app_options x86_64 --with-unify-dist=../i386/dist
|
||||
|
||||
if ! test `uname -s` = Linux; then
|
||||
# Cross-universal builds already do the equivalent of this by setting -isysroot directly
|
||||
ac_add_options --with-macos-sdk=/Developer/SDKs/MacOSX10.7.sdk
|
||||
fi
|
||||
|
||||
. $topsrcdir/build/macosx/mozconfig.common
|
||||
|
||||
# $MOZ_BUILD_APP is only defined when sourced by configure. That's not a
|
||||
# problem, because the variables it affects only need to be set for
|
||||
# configure.
|
||||
if test -n "$MOZ_BUILD_APP" ; then
|
||||
if test "$MOZ_BUILD_APP" = "i386" -o "$MOZ_BUILD_APP" = "x86_64"; then
|
||||
TARGET_CPU=$MOZ_BUILD_APP
|
||||
|
||||
# $HOST_CXX is presently unused. $HOST_CC will only be used during a cross
|
||||
# compile.
|
||||
HOST_CC=$CC
|
||||
HOST_CXX=$CXX
|
||||
|
||||
NATIVE_CPU=`$topsrcdir/build/autoconf/config.guess | cut -f1 -d-`
|
||||
|
||||
# It's not strictly necessary to specify -arch during native builds, but it
|
||||
# makes the merged about:buildconfig easier to follow, and it reduces
|
||||
# conditionalized differences between builds.
|
||||
CC="$CC -arch $TARGET_CPU"
|
||||
CXX="$CXX -arch $TARGET_CPU"
|
||||
|
||||
# These must be set for cross builds, and don't hurt straight builds.
|
||||
RANLIB="${TOOLCHAIN_PREFIX}ranlib"
|
||||
AR="${TOOLCHAIN_PREFIX}ar"
|
||||
AS=$CC
|
||||
LD=ld
|
||||
STRIP="${TOOLCHAIN_PREFIX}strip"
|
||||
OTOOL="${TOOLCHAIN_PREFIX}otool"
|
||||
|
||||
# Each per-CPU build should be entirely oblivious to the fact that a
|
||||
# universal binary will be produced. The exception is packager.mk, which
|
||||
# needs to know to look for universal bits when building the .dmg.
|
||||
UNIVERSAL_BINARY=1
|
||||
|
||||
export CC CXX HOST_CC HOST_CXX RANLIB AR AS LD STRIP OTOOL
|
||||
fi
|
||||
fi
|
||||
Executable
+1525
File diff suppressed because it is too large.
Load diff
@@ -1,451 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
# You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import datetime
|
||||
import mozcrash
|
||||
import threading
|
||||
import os
|
||||
import posixpath
|
||||
import Queue
|
||||
import re
|
||||
import shutil
|
||||
import signal
|
||||
import tempfile
|
||||
import time
|
||||
import traceback
|
||||
import zipfile
|
||||
|
||||
from automation import Automation
|
||||
from mozlog import get_default_logger
|
||||
from mozprocess import ProcessHandlerMixin
|
||||
|
||||
|
||||
class StdOutProc(ProcessHandlerMixin):
|
||||
"""Process handler for b2g which puts all output in a Queue.
|
||||
"""
|
||||
|
||||
def __init__(self, cmd, queue, **kwargs):
|
||||
self.queue = queue
|
||||
kwargs.setdefault('processOutputLine', []).append(self.handle_output)
|
||||
ProcessHandlerMixin.__init__(self, cmd, **kwargs)
|
||||
|
||||
def handle_output(self, line):
|
||||
self.queue.put_nowait(line)
|
||||
|
||||
|
||||
class B2GRemoteAutomation(Automation):
|
||||
_devicemanager = None
|
||||
|
||||
def __init__(self, deviceManager, appName='', remoteLog=None,
|
||||
marionette=None):
|
||||
self._devicemanager = deviceManager
|
||||
self._appName = appName
|
||||
self._remoteProfile = None
|
||||
self._remoteLog = remoteLog
|
||||
self.marionette = marionette
|
||||
self._is_emulator = False
|
||||
self.test_script = None
|
||||
self.test_script_args = None
|
||||
|
||||
# Default our product to b2g
|
||||
self._product = "b2g"
|
||||
self.lastTestSeen = "b2gautomation.py"
|
||||
# Default log finish to mochitest standard
|
||||
self.logFinish = 'INFO SimpleTest FINISHED'
|
||||
Automation.__init__(self)
|
||||
|
||||
def setEmulator(self, is_emulator):
|
||||
self._is_emulator = is_emulator
|
||||
|
||||
def setDeviceManager(self, deviceManager):
|
||||
self._devicemanager = deviceManager
|
||||
|
||||
def setAppName(self, appName):
|
||||
self._appName = appName
|
||||
|
||||
def setRemoteProfile(self, remoteProfile):
|
||||
self._remoteProfile = remoteProfile
|
||||
|
||||
def setProduct(self, product):
|
||||
self._product = product
|
||||
|
||||
def setRemoteLog(self, logfile):
|
||||
self._remoteLog = logfile
|
||||
|
||||
def getExtensionIDFromRDF(self, rdfSource):
|
||||
"""
|
||||
Retrieves the extension id from an install.rdf file (or string).
|
||||
"""
|
||||
from xml.dom.minidom import parse, parseString, Node
|
||||
|
||||
if isinstance(rdfSource, file):
|
||||
document = parse(rdfSource)
|
||||
else:
|
||||
document = parseString(rdfSource)
|
||||
|
||||
# Find the <em:id> element. There can be multiple <em:id> tags
|
||||
# within <em:targetApplication> tags, so we have to check this way.
|
||||
for rdfChild in document.documentElement.childNodes:
|
||||
if rdfChild.nodeType == Node.ELEMENT_NODE and rdfChild.tagName == "Description":
|
||||
for descChild in rdfChild.childNodes:
|
||||
if descChild.nodeType == Node.ELEMENT_NODE and descChild.tagName == "em:id":
|
||||
return descChild.childNodes[0].data
|
||||
return None
|
||||
|
||||
def installExtension(self, extensionSource, profileDir, extensionID=None):
|
||||
# Bug 827504 - installing special-powers extension separately causes problems in B2G
|
||||
if extensionID != "special-powers@mozilla.org":
|
||||
if not os.path.isdir(profileDir):
|
||||
self.log.info("INFO | automation.py | Cannot install extension, invalid profileDir at: %s", profileDir)
|
||||
return
|
||||
|
||||
installRDFFilename = "install.rdf"
|
||||
|
||||
extensionsRootDir = os.path.join(profileDir, "extensions", "staged")
|
||||
if not os.path.isdir(extensionsRootDir):
|
||||
os.makedirs(extensionsRootDir)
|
||||
|
||||
if os.path.isfile(extensionSource):
|
||||
reader = zipfile.ZipFile(extensionSource, "r")
|
||||
|
||||
for filename in reader.namelist():
|
||||
# Sanity check the zip file.
|
||||
if os.path.isabs(filename):
|
||||
self.log.info("INFO | automation.py | Cannot install extension, bad files in xpi")
|
||||
return
|
||||
|
||||
# We may need to dig the extensionID out of the zip file...
|
||||
if extensionID is None and filename == installRDFFilename:
|
||||
extensionID = self.getExtensionIDFromRDF(reader.read(filename))
|
||||
|
||||
# We must know the extensionID now.
|
||||
if extensionID is None:
|
||||
self.log.info("INFO | automation.py | Cannot install extension, missing extensionID")
|
||||
return
|
||||
|
||||
# Make the extension directory.
|
||||
extensionDir = os.path.join(extensionsRootDir, extensionID)
|
||||
os.mkdir(extensionDir)
|
||||
|
||||
# Extract all files.
|
||||
reader.extractall(extensionDir)
|
||||
|
||||
elif os.path.isdir(extensionSource):
|
||||
if extensionID is None:
|
||||
filename = os.path.join(extensionSource, installRDFFilename)
|
||||
if os.path.isfile(filename):
|
||||
with open(filename, "r") as installRDF:
|
||||
extensionID = self.getExtensionIDFromRDF(installRDF)
|
||||
|
||||
if extensionID is None:
|
||||
self.log.info("INFO | automation.py | Cannot install extension, missing extensionID")
|
||||
return
|
||||
|
||||
# Copy extension tree into its own directory.
|
||||
# "destination directory must not already exist".
|
||||
shutil.copytree(extensionSource, os.path.join(extensionsRootDir, extensionID))
|
||||
|
||||
else:
|
||||
self.log.info("INFO | automation.py | Cannot install extension, invalid extensionSource at: %s", extensionSource)
|
||||
|
||||
# Set up what we need for the remote environment
|
||||
def environment(self, env=None, xrePath=None, crashreporter=True, debugger=False):
|
||||
# Because we are running remote, we don't want to mimic the local env
|
||||
# so no copying of os.environ
|
||||
if env is None:
|
||||
env = {}
|
||||
|
||||
# We always hide the results table in B2G; it's much slower if we don't.
|
||||
env['MOZ_HIDE_RESULTS_TABLE'] = '1'
|
||||
return env
|
||||
|
||||
def waitForNet(self):
|
||||
active = False
|
||||
time_out = 0
|
||||
while not active and time_out < 40:
|
||||
data = self._devicemanager._runCmd(['shell', '/system/bin/netcfg']).stdout.readlines()
|
||||
data.pop(0)
|
||||
for line in data:
|
||||
if (re.search(r'UP\s+(?:[0-9]{1,3}\.){3}[0-9]{1,3}', line)):
|
||||
active = True
|
||||
break
|
||||
time_out += 1
|
||||
time.sleep(1)
|
||||
return active
|
||||
|
||||
def checkForCrashes(self, directory, symbolsPath):
|
||||
crashed = False
|
||||
remote_dump_dir = self._remoteProfile + '/minidumps'
|
||||
print "checking for crashes in '%s'" % remote_dump_dir
|
||||
if self._devicemanager.dirExists(remote_dump_dir):
|
||||
local_dump_dir = tempfile.mkdtemp()
|
||||
self._devicemanager.getDirectory(remote_dump_dir, local_dump_dir)
|
||||
try:
|
||||
logger = get_default_logger()
|
||||
if logger is not None:
|
||||
crashed = mozcrash.log_crashes(logger, local_dump_dir, symbolsPath, test=self.lastTestSeen)
|
||||
else:
|
||||
crashed = mozcrash.check_for_crashes(local_dump_dir, symbolsPath, test_name=self.lastTestSeen)
|
||||
except:
|
||||
traceback.print_exc()
|
||||
finally:
|
||||
shutil.rmtree(local_dump_dir)
|
||||
self._devicemanager.removeDir(remote_dump_dir)
|
||||
return crashed
|
||||
|
||||
def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs):
|
||||
# if remote profile is specified, use that instead
|
||||
if (self._remoteProfile):
|
||||
profileDir = self._remoteProfile
|
||||
|
||||
cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs)
|
||||
|
||||
return app, args
|
||||
|
||||
def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime,
|
||||
debuggerInfo, symbolsPath, outputHandler=None):
|
||||
""" Wait for tests to finish (as evidenced by a signature string
|
||||
in logcat), or for a given amount of time to elapse with no
|
||||
output.
|
||||
"""
|
||||
timeout = timeout or 120
|
||||
while True:
|
||||
lines = proc.getStdoutLines(timeout)
|
||||
if lines:
|
||||
currentlog = '\n'.join(lines)
|
||||
|
||||
if outputHandler:
|
||||
for line in lines:
|
||||
outputHandler(line)
|
||||
else:
|
||||
print(currentlog)
|
||||
|
||||
# Match the test filepath from the last TEST-START line found in the new
|
||||
# log content. These lines are in the form:
|
||||
# ... INFO TEST-START | /filepath/we/wish/to/capture.html\n
|
||||
testStartFilenames = re.findall(r"TEST-START \| ([^\s]*)", currentlog)
|
||||
if testStartFilenames:
|
||||
self.lastTestSeen = testStartFilenames[-1]
|
||||
if (outputHandler and outputHandler.suite_finished) or (
|
||||
hasattr(self, 'logFinish') and self.logFinish in currentlog):
|
||||
return 0
|
||||
else:
|
||||
self.log.info("TEST-UNEXPECTED-FAIL | %s | application timed "
|
||||
"out after %d seconds with no output",
|
||||
self.lastTestSeen, int(timeout))
|
||||
self._devicemanager.killProcess('/system/b2g/b2g', sig=signal.SIGABRT)
|
||||
|
||||
timeout = 10 # seconds
|
||||
starttime = datetime.datetime.utcnow()
|
||||
while datetime.datetime.utcnow() - starttime < datetime.timedelta(seconds=timeout):
|
||||
if not self._devicemanager.processExist('/system/b2g/b2g'):
|
||||
break
|
||||
time.sleep(1)
|
||||
else:
|
||||
print "timed out after %d seconds waiting for b2g process to exit" % timeout
|
||||
return 1
|
||||
|
||||
self.checkForCrashes(None, symbolsPath)
|
||||
return 1
|
||||
|
||||
def getDeviceStatus(self, serial=None):
|
||||
# Get the current status of the device. If we know the device
|
||||
# serial number, we look for that, otherwise we use the (presumably
|
||||
# only) device shown in 'adb devices'.
|
||||
serial = serial or self._devicemanager._deviceSerial
|
||||
status = 'unknown'
|
||||
|
||||
for line in self._devicemanager._runCmd(['devices']).stdout.readlines():
|
||||
result = re.match('(.*?)\t(.*)', line)
|
||||
if result:
|
||||
thisSerial = result.group(1)
|
||||
if not serial or thisSerial == serial:
|
||||
serial = thisSerial
|
||||
status = result.group(2)
|
||||
|
||||
return (serial, status)
|
||||
|
||||
def restartB2G(self):
|
||||
# TODO hangs in subprocess.Popen without this delay
|
||||
time.sleep(5)
|
||||
self._devicemanager._checkCmd(['shell', 'stop', 'b2g'])
|
||||
# Wait for a bit to make sure B2G has completely shut down.
|
||||
time.sleep(10)
|
||||
self._devicemanager._checkCmd(['shell', 'start', 'b2g'])
|
||||
if self._is_emulator:
|
||||
self.marionette.emulator.wait_for_port(self.marionette.port)
|
||||
|
||||
def rebootDevice(self):
|
||||
# find device's current status and serial number
|
||||
serial, status = self.getDeviceStatus()
|
||||
|
||||
# reboot!
|
||||
self._devicemanager._runCmd(['shell', '/system/bin/reboot'])
|
||||
|
||||
# The above command can return while adb still thinks the device is
|
||||
# connected, so wait a little bit for it to disconnect from adb.
|
||||
time.sleep(10)
|
||||
|
||||
# wait for device to come back to previous status
|
||||
print 'waiting for device to come back online after reboot'
|
||||
start = time.time()
|
||||
rserial, rstatus = self.getDeviceStatus(serial)
|
||||
while rstatus != 'device':
|
||||
if time.time() - start > 120:
|
||||
# device hasn't come back online in 2 minutes, something's wrong
|
||||
raise Exception("Device %s (status: %s) not back online after reboot" % (serial, rstatus))
|
||||
time.sleep(5)
|
||||
rserial, rstatus = self.getDeviceStatus(serial)
|
||||
print 'device:', serial, 'status:', rstatus
|
||||
|
||||
def Process(self, cmd, stdout=None, stderr=None, env=None, cwd=None):
|
||||
# On a desktop or fennec run, the Process method invokes a gecko
|
||||
# process in which to the tests. For B2G, we simply
|
||||
# reboot the device (which was configured with a test profile
|
||||
# already), wait for B2G to start up, and then navigate to the
|
||||
# test url using Marionette. There doesn't seem to be any way
|
||||
# to pass env variables into the B2G process, but this doesn't
|
||||
# seem to matter.
|
||||
|
||||
# reboot device so it starts up with the mochitest profile
|
||||
# XXX: We could potentially use 'stop b2g' + 'start b2g' to achieve
|
||||
# a similar effect; will see which is more stable while attempting
|
||||
# to bring up the continuous integration.
|
||||
if not self._is_emulator:
|
||||
self.rebootDevice()
|
||||
time.sleep(5)
|
||||
#wait for wlan to come up
|
||||
if not self.waitForNet():
|
||||
raise Exception("network did not come up, please configure the network" +
|
||||
" prior to running before running the automation framework")
|
||||
|
||||
# stop b2g
|
||||
self._devicemanager._runCmd(['shell', 'stop', 'b2g'])
|
||||
time.sleep(5)
|
||||
|
||||
# For some reason user.js in the profile doesn't get picked up.
|
||||
# Manually copy it over to prefs.js. See bug 1009730 for more details.
|
||||
self._devicemanager.moveTree(posixpath.join(self._remoteProfile, 'user.js'),
|
||||
posixpath.join(self._remoteProfile, 'prefs.js'))
|
||||
|
||||
# relaunch b2g inside b2g instance
|
||||
instance = self.B2GInstance(self._devicemanager, env=env)
|
||||
|
||||
time.sleep(5)
|
||||
|
||||
# Set up port forwarding again for Marionette, since any that
|
||||
# existed previously got wiped out by the reboot.
|
||||
if not self._is_emulator:
|
||||
self._devicemanager._checkCmd(['forward',
|
||||
'tcp:%s' % self.marionette.port,
|
||||
'tcp:%s' % self.marionette.port])
|
||||
|
||||
if self._is_emulator:
|
||||
self.marionette.emulator.wait_for_port(self.marionette.port)
|
||||
else:
|
||||
time.sleep(5)
|
||||
|
||||
# start a marionette session
|
||||
session = self.marionette.start_session()
|
||||
if 'b2g' not in session:
|
||||
raise Exception("bad session value %s returned by start_session" % session)
|
||||
|
||||
with self.marionette.using_context(self.marionette.CONTEXT_CHROME):
|
||||
self.marionette.execute_script("""
|
||||
let SECURITY_PREF = "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer";
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
Services.prefs.setBoolPref(SECURITY_PREF, true);
|
||||
|
||||
if (!testUtils.hasOwnProperty("specialPowersObserver")) {
|
||||
let loader = Components.classes["@mozilla.org/moz/jssubscript-loader;1"]
|
||||
.getService(Components.interfaces.mozIJSSubScriptLoader);
|
||||
loader.loadSubScript("chrome://specialpowers/content/SpecialPowersObserver.jsm",
|
||||
testUtils);
|
||||
testUtils.specialPowersObserver = new testUtils.SpecialPowersObserver();
|
||||
testUtils.specialPowersObserver.init();
|
||||
}
|
||||
""")
|
||||
|
||||
# run the script that starts the tests
|
||||
if self.test_script:
|
||||
if os.path.isfile(self.test_script):
|
||||
script = open(self.test_script, 'r')
|
||||
self.marionette.execute_script(script.read(), script_args=self.test_script_args)
|
||||
script.close()
|
||||
elif isinstance(self.test_script, basestring):
|
||||
self.marionette.execute_script(self.test_script, script_args=self.test_script_args)
|
||||
else:
|
||||
# assumes the tests are started on startup automatically
|
||||
pass
|
||||
|
||||
return instance
|
||||
|
||||
# be careful here as this inner class doesn't have access to outer class members
|
||||
class B2GInstance(object):
|
||||
"""Represents a B2G instance running on a device, and exposes
|
||||
some process-like methods/properties that are expected by the
|
||||
automation.
|
||||
"""
|
||||
|
||||
def __init__(self, dm, env=None):
|
||||
self.dm = dm
|
||||
self.env = env or {}
|
||||
self.stdout_proc = None
|
||||
self.queue = Queue.Queue()
|
||||
|
||||
# Launch b2g in a separate thread, and dump all output lines
|
||||
# into a queue. The lines in this queue are
|
||||
# retrieved and returned by accessing the stdout property of
|
||||
# this class.
|
||||
cmd = [self.dm._adbPath]
|
||||
if self.dm._deviceSerial:
|
||||
cmd.extend(['-s', self.dm._deviceSerial])
|
||||
cmd.append('shell')
|
||||
for k, v in self.env.iteritems():
|
||||
cmd.append("%s=%s" % (k, v))
|
||||
cmd.append('/system/bin/b2g.sh')
|
||||
proc = threading.Thread(target=self._save_stdout_proc, args=(cmd, self.queue))
|
||||
proc.daemon = True
|
||||
proc.start()
|
||||
|
||||
def _save_stdout_proc(self, cmd, queue):
|
||||
self.stdout_proc = StdOutProc(cmd, queue)
|
||||
self.stdout_proc.run()
|
||||
if hasattr(self.stdout_proc, 'processOutput'):
|
||||
self.stdout_proc.processOutput()
|
||||
self.stdout_proc.wait()
|
||||
self.stdout_proc = None
|
||||
|
||||
@property
|
||||
def pid(self):
|
||||
# a dummy value to make the automation happy
|
||||
return 0
|
||||
|
||||
def getStdoutLines(self, timeout):
|
||||
# Return any lines in the queue used by the
|
||||
# b2g process handler.
|
||||
lines = []
|
||||
# get all of the lines that are currently available
|
||||
while True:
|
||||
try:
|
||||
lines.append(self.queue.get_nowait())
|
||||
except Queue.Empty:
|
||||
break
|
||||
|
||||
# wait 'timeout' for any additional lines
|
||||
if not lines:
|
||||
try:
|
||||
lines.append(self.queue.get(True, timeout))
|
||||
except Queue.Empty:
|
||||
pass
|
||||
return lines
|
||||
|
||||
def wait(self, timeout=None):
|
||||
# this should never happen
|
||||
raise Exception("'wait' called on B2GInstance")
|
||||
|
||||
def kill(self):
|
||||
# this should never happen
|
||||
raise Exception("'kill' called on B2GInstance")
|
||||
|
||||
@@ -1,401 +0,0 @@
|
||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
import datetime
|
||||
import glob
|
||||
import time
|
||||
import re
|
||||
import os
|
||||
import posixpath
|
||||
import tempfile
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from automation import Automation
|
||||
from mozdevice import DMError, DeviceManager
|
||||
from mozlog import get_default_logger
|
||||
import mozcrash
|
||||
|
||||
# signatures for logcat messages that we don't care about much
|
||||
fennecLogcatFilters = [ "The character encoding of the HTML document was not declared",
|
||||
"Use of Mutation Events is deprecated. Use MutationObserver instead.",
|
||||
"Unexpected value from nativeGetEnabledTags: 0" ]
|
||||
|
||||
class RemoteAutomation(Automation):
|
||||
_devicemanager = None
|
||||
|
||||
# Part of a hack for Robocop: "am COMMAND" is handled specially if COMMAND
|
||||
# is in this set. See usages below.
|
||||
_specialAmCommands = ('instrument', 'start')
|
||||
|
||||
def __init__(self, deviceManager, appName = '', remoteLog = None,
|
||||
processArgs=None):
|
||||
self._devicemanager = deviceManager
|
||||
self._appName = appName
|
||||
self._remoteProfile = None
|
||||
self._remoteLog = remoteLog
|
||||
self._processArgs = processArgs or {};
|
||||
|
||||
# Default our product to fennec
|
||||
self._product = "fennec"
|
||||
self.lastTestSeen = "remoteautomation.py"
|
||||
Automation.__init__(self)
|
||||
|
||||
def setDeviceManager(self, deviceManager):
|
||||
self._devicemanager = deviceManager
|
||||
|
||||
def setAppName(self, appName):
|
||||
self._appName = appName
|
||||
|
||||
def setRemoteProfile(self, remoteProfile):
|
||||
self._remoteProfile = remoteProfile
|
||||
|
||||
def setProduct(self, product):
|
||||
self._product = product
|
||||
|
||||
def setRemoteLog(self, logfile):
|
||||
self._remoteLog = logfile
|
||||
|
||||
# Set up what we need for the remote environment
|
||||
def environment(self, env=None, xrePath=None, crashreporter=True, debugger=False, dmdPath=None, lsanPath=None):
|
||||
# Because we are running remote, we don't want to mimic the local env
|
||||
# so no copying of os.environ
|
||||
if env is None:
|
||||
env = {}
|
||||
|
||||
if dmdPath:
|
||||
env['MOZ_REPLACE_MALLOC_LIB'] = os.path.join(dmdPath, 'libdmd.so')
|
||||
|
||||
# Except for the mochitest results table hiding option, which isn't
|
||||
# passed to runtestsremote.py as an actual option, but through the
|
||||
# MOZ_HIDE_RESULTS_TABLE environment variable.
|
||||
if 'MOZ_HIDE_RESULTS_TABLE' in os.environ:
|
||||
env['MOZ_HIDE_RESULTS_TABLE'] = os.environ['MOZ_HIDE_RESULTS_TABLE']
|
||||
|
||||
env['MOZ_CRASHREPORTER_DISABLE'] = '1'
|
||||
|
||||
# Crash on non-local network connections by default.
|
||||
# MOZ_DISABLE_NONLOCAL_CONNECTIONS can be set to "0" to temporarily
|
||||
# enable non-local connections for the purposes of local testing.
|
||||
# Don't override the user's choice here. See bug 1049688.
|
||||
env.setdefault('MOZ_DISABLE_NONLOCAL_CONNECTIONS', '1')
|
||||
|
||||
# Send an env var noting that we are in automation. Passing any
|
||||
# value except the empty string will declare the value to exist.
|
||||
#
|
||||
# This may be used to disabled network connections during testing, e.g.
|
||||
# Switchboard & telemetry uploads.
|
||||
env.setdefault('MOZ_IN_AUTOMATION', '1')
|
||||
|
||||
# Set WebRTC logging in case it is not set yet.
|
||||
# On Android, environment variables cannot contain ',' so the
|
||||
# standard WebRTC setting for NSPR_LOG_MODULES is not available.
|
||||
# env.setdefault('NSPR_LOG_MODULES', 'signaling:5,mtransport:5,datachannel:5,jsep:5,MediaPipelineFactory:5')
|
||||
env.setdefault('R_LOG_LEVEL', '6')
|
||||
env.setdefault('R_LOG_DESTINATION', 'stderr')
|
||||
env.setdefault('R_LOG_VERBOSE', '1')
|
||||
|
||||
return env
|
||||
|
||||
def waitForFinish(self, proc, utilityPath, timeout, maxTime, startTime, debuggerInfo, symbolsPath, outputHandler=None):
|
||||
""" Wait for tests to finish.
|
||||
If maxTime seconds elapse or no output is detected for timeout
|
||||
seconds, kill the process and fail the test.
|
||||
"""
|
||||
# maxTime is used to override the default timeout, we should honor that
|
||||
status = proc.wait(timeout = maxTime, noOutputTimeout = timeout)
|
||||
self.lastTestSeen = proc.getLastTestSeen
|
||||
|
||||
topActivity = self._devicemanager.getTopActivity()
|
||||
if topActivity == proc.procName:
|
||||
proc.kill(True)
|
||||
if status == 1:
|
||||
if maxTime:
|
||||
print "TEST-UNEXPECTED-FAIL | %s | application ran for longer than " \
|
||||
"allowed maximum time of %s seconds" % (self.lastTestSeen, maxTime)
|
||||
else:
|
||||
print "TEST-UNEXPECTED-FAIL | %s | application ran for longer than " \
|
||||
"allowed maximum time" % (self.lastTestSeen)
|
||||
if status == 2:
|
||||
print "TEST-UNEXPECTED-FAIL | %s | application timed out after %d seconds with no output" \
|
||||
% (self.lastTestSeen, int(timeout))
|
||||
|
||||
return status
|
||||
|
||||
def deleteANRs(self):
|
||||
# empty ANR traces.txt file; usually need root permissions
|
||||
# we make it empty and writable so we can test the ANR reporter later
|
||||
traces = "/data/anr/traces.txt"
|
||||
try:
|
||||
self._devicemanager.shellCheckOutput(['echo', '', '>', traces], root=True,
|
||||
timeout=DeviceManager.short_timeout)
|
||||
self._devicemanager.shellCheckOutput(['chmod', '666', traces], root=True,
|
||||
timeout=DeviceManager.short_timeout)
|
||||
except DMError:
|
||||
print "Error deleting %s" % traces
|
||||
pass
|
||||
|
||||
def checkForANRs(self):
|
||||
traces = "/data/anr/traces.txt"
|
||||
if self._devicemanager.fileExists(traces):
|
||||
try:
|
||||
t = self._devicemanager.pullFile(traces)
|
||||
if t:
|
||||
stripped = t.strip()
|
||||
if len(stripped) > 0:
|
||||
print "Contents of %s:" % traces
|
||||
print t
|
||||
# Once reported, delete traces
|
||||
self.deleteANRs()
|
||||
except DMError:
|
||||
print "Error pulling %s" % traces
|
||||
except IOError:
|
||||
print "Error pulling %s" % traces
|
||||
else:
|
||||
print "%s not found" % traces
|
||||
|
||||
def deleteTombstones(self):
|
||||
# delete any existing tombstone files from device
|
||||
remoteDir = "/data/tombstones"
|
||||
try:
|
||||
self._devicemanager.shellCheckOutput(['rm', '-r', remoteDir], root=True,
|
||||
timeout=DeviceManager.short_timeout)
|
||||
except DMError:
|
||||
# This may just indicate that the tombstone directory is missing
|
||||
pass
|
||||
|
||||
def checkForTombstones(self):
|
||||
# pull any tombstones from device and move to MOZ_UPLOAD_DIR
|
||||
remoteDir = "/data/tombstones"
|
||||
blobberUploadDir = os.environ.get('MOZ_UPLOAD_DIR', None)
|
||||
if blobberUploadDir:
|
||||
if not os.path.exists(blobberUploadDir):
|
||||
os.mkdir(blobberUploadDir)
|
||||
if self._devicemanager.dirExists(remoteDir):
|
||||
# copy tombstone files from device to local blobber upload directory
|
||||
try:
|
||||
self._devicemanager.shellCheckOutput(['chmod', '777', remoteDir], root=True,
|
||||
timeout=DeviceManager.short_timeout)
|
||||
self._devicemanager.shellCheckOutput(['chmod', '666', os.path.join(remoteDir, '*')], root=True,
|
||||
timeout=DeviceManager.short_timeout)
|
||||
self._devicemanager.getDirectory(remoteDir, blobberUploadDir, False)
|
||||
except DMError:
|
||||
# This may just indicate that no tombstone files are present
|
||||
pass
|
||||
self.deleteTombstones()
|
||||
# add a .txt file extension to each tombstone file name, so
|
||||
# that blobber will upload it
|
||||
for f in glob.glob(os.path.join(blobberUploadDir, "tombstone_??")):
|
||||
# add a unique integer to the file name, in case there are
|
||||
# multiple tombstones generated with the same name, for
|
||||
# instance, after multiple robocop tests
|
||||
for i in xrange(1, sys.maxint):
|
||||
newname = "%s.%d.txt" % (f, i)
|
||||
if not os.path.exists(newname):
|
||||
os.rename(f, newname)
|
||||
break
|
||||
else:
|
||||
print "%s does not exist; tombstone check skipped" % remoteDir
|
||||
else:
|
||||
print "MOZ_UPLOAD_DIR not defined; tombstone check skipped"
|
||||
|
||||
def checkForCrashes(self, directory, symbolsPath):
|
||||
self.checkForANRs()
|
||||
self.checkForTombstones()
|
||||
|
||||
logcat = self._devicemanager.getLogcat(filterOutRegexps=fennecLogcatFilters)
|
||||
|
||||
javaException = mozcrash.check_for_java_exception(logcat, test_name=self.lastTestSeen)
|
||||
if javaException:
|
||||
return True
|
||||
|
||||
# No crash reporting means we can't say anything.
|
||||
return False
|
||||
|
||||
|
||||
def buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs):
|
||||
# If remote profile is specified, use that instead
|
||||
if (self._remoteProfile):
|
||||
profileDir = self._remoteProfile
|
||||
|
||||
# Hack for robocop, if app & testURL == None and extraArgs contains the rest of the stuff, lets
|
||||
# assume extraArgs is all we need
|
||||
if app == "am" and extraArgs[0] in RemoteAutomation._specialAmCommands:
|
||||
return app, extraArgs
|
||||
|
||||
cmd, args = Automation.buildCommandLine(self, app, debuggerInfo, profileDir, testURL, extraArgs)
|
||||
# Remove -foreground if it exists, if it doesn't this just returns
|
||||
try:
|
||||
args.remove('-foreground')
|
||||
except:
|
||||
pass
|
||||
#TODO: figure out which platform require NO_EM_RESTART
|
||||
# return app, ['--environ:NO_EM_RESTART=1'] + args
|
||||
return app, args
|
||||
|
||||
def Process(self, cmd, stdout = None, stderr = None, env = None, cwd = None):
|
||||
if stdout == None or stdout == -1 or stdout == subprocess.PIPE:
|
||||
stdout = self._remoteLog
|
||||
|
||||
return self.RProcess(self._devicemanager, cmd, stdout, stderr, env, cwd, self._appName,
|
||||
**self._processArgs)
|
||||
|
||||
# be careful here as this inner class doesn't have access to outer class members
|
||||
class RProcess(object):
|
||||
# device manager process
|
||||
dm = None
|
||||
def __init__(self, dm, cmd, stdout=None, stderr=None, env=None, cwd=None, app=None,
|
||||
messageLogger=None):
|
||||
self.dm = dm
|
||||
self.stdoutlen = 0
|
||||
self.lastTestSeen = "remoteautomation.py"
|
||||
self.proc = dm.launchProcess(cmd, stdout, cwd, env, True)
|
||||
self.messageLogger = messageLogger
|
||||
|
||||
if (self.proc is None):
|
||||
if cmd[0] == 'am':
|
||||
self.proc = stdout
|
||||
else:
|
||||
raise Exception("unable to launch process")
|
||||
self.procName = cmd[0].split('/')[-1]
|
||||
if cmd[0] == 'am' and cmd[1] in RemoteAutomation._specialAmCommands:
|
||||
self.procName = app
|
||||
|
||||
# Setting timeout at 1 hour since on a remote device this takes much longer.
|
||||
# Temporarily increased to 90 minutes because no more chunks can be created.
|
||||
self.timeout = 5400
|
||||
# The benefit of the following sleep is unclear; it was formerly 15 seconds
|
||||
time.sleep(1)
|
||||
|
||||
# Used to buffer log messages until we meet a line break
|
||||
self.logBuffer = ""
|
||||
|
||||
@property
|
||||
def pid(self):
|
||||
pid = self.dm.processExist(self.procName)
|
||||
# HACK: we should probably be more sophisticated about monitoring
|
||||
# running processes for the remote case, but for now we'll assume
|
||||
# that this method can be called when nothing exists and it is not
|
||||
# an error
|
||||
if pid is None:
|
||||
return 0
|
||||
return pid
|
||||
|
||||
def read_stdout(self):
|
||||
"""
|
||||
Fetch the full remote log file using devicemanager, process them and
|
||||
return whether there were any new log entries since the last call.
|
||||
"""
|
||||
if not self.dm.fileExists(self.proc):
|
||||
return False
|
||||
try:
|
||||
newLogContent = self.dm.pullFile(self.proc, self.stdoutlen)
|
||||
except DMError:
|
||||
# we currently don't retry properly in the pullFile
|
||||
# function in dmSUT, so an error here is not necessarily
|
||||
# the end of the world
|
||||
return False
|
||||
if not newLogContent:
|
||||
return False
|
||||
|
||||
self.stdoutlen += len(newLogContent)
|
||||
|
||||
if self.messageLogger is None:
|
||||
testStartFilenames = re.findall(r"TEST-START \| ([^\s]*)", newLogContent)
|
||||
if testStartFilenames:
|
||||
self.lastTestSeen = testStartFilenames[-1]
|
||||
print newLogContent
|
||||
return True
|
||||
|
||||
self.logBuffer += newLogContent
|
||||
lines = self.logBuffer.split('\n')
|
||||
lines = [l for l in lines if l]
|
||||
|
||||
if lines:
|
||||
if self.logBuffer.endswith('\n'):
|
||||
# all lines are complete; no need to buffer
|
||||
self.logBuffer = ""
|
||||
else:
|
||||
# keep the last (unfinished) line in the buffer
|
||||
self.logBuffer = lines[-1]
|
||||
del lines[-1]
|
||||
|
||||
if not lines:
|
||||
return False
|
||||
|
||||
for line in lines:
|
||||
# This passes the line to the logger (to be logged or buffered)
|
||||
parsed_messages = self.messageLogger.write(line)
|
||||
for message in parsed_messages:
|
||||
if isinstance(message, dict) and message.get('action') == 'test_start':
|
||||
self.lastTestSeen = message['test']
|
||||
return True
|
||||
|
||||
@property
|
||||
def getLastTestSeen(self):
|
||||
return self.lastTestSeen
|
||||
|
||||
# Wait for the remote process to end (or for its activity to go to background).
|
||||
# While waiting, periodically retrieve the process output and print it.
|
||||
# If the process is still running after *timeout* seconds, return 1;
|
||||
# If the process is still running but no output is received in *noOutputTimeout*
|
||||
# seconds, return 2;
|
||||
# Else, once the process exits/goes to background, return 0.
|
||||
def wait(self, timeout = None, noOutputTimeout = None):
|
||||
timer = 0
|
||||
noOutputTimer = 0
|
||||
interval = 10
|
||||
if timeout == None:
|
||||
timeout = self.timeout
|
||||
status = 0
|
||||
top = self.procName
|
||||
slowLog = False
|
||||
while (top == self.procName):
|
||||
# Get log updates on each interval, but if it is taking
|
||||
# too long, only do it every 60 seconds
|
||||
if (not slowLog) or (timer % 60 == 0):
|
||||
startRead = datetime.datetime.utcnow()
|
||||
hasOutput = self.read_stdout()
|
||||
if (datetime.datetime.utcnow() - startRead) > datetime.timedelta(seconds=5):
|
||||
slowLog = True
|
||||
if hasOutput:
|
||||
noOutputTimer = 0
|
||||
time.sleep(interval)
|
||||
timer += interval
|
||||
noOutputTimer += interval
|
||||
if (timer > timeout):
|
||||
status = 1
|
||||
break
|
||||
if (noOutputTimeout and noOutputTimer > noOutputTimeout):
|
||||
status = 2
|
||||
break
|
||||
top = self.dm.getTopActivity()
|
||||
# Flush anything added to stdout during the sleep
|
||||
self.read_stdout()
|
||||
return status
|
||||
|
||||
def kill(self, stagedShutdown = False):
|
||||
if stagedShutdown:
|
||||
# Trigger an ANR report with "kill -3" (SIGQUIT)
|
||||
self.dm.killProcess(self.procName, 3)
|
||||
time.sleep(3)
|
||||
# Trigger a breakpad dump with "kill -6" (SIGABRT)
|
||||
self.dm.killProcess(self.procName, 6)
|
||||
# Wait for process to end
|
||||
retries = 0
|
||||
while retries < 3:
|
||||
pid = self.dm.processExist(self.procName)
|
||||
if pid and pid > 0:
|
||||
print "%s still alive after SIGABRT: waiting..." % self.procName
|
||||
time.sleep(5)
|
||||
else:
|
||||
return
|
||||
retries += 1
|
||||
self.dm.killProcess(self.procName, 9)
|
||||
pid = self.dm.processExist(self.procName)
|
||||
if pid and pid > 0:
|
||||
self.dm.killProcess(self.procName)
|
||||
else:
|
||||
self.dm.killProcess(self.procName)
|
||||
@@ -15,7 +15,7 @@ ifndef JS_STANDALONE
|
||||
include $(topsrcdir)/toolkit/mozapps/installer/package-name.mk
|
||||
include $(topsrcdir)/toolkit/mozapps/installer/upload-files.mk
|
||||
|
||||
# Clear out DIST_FILES if it was set by upload-files.mk (for Android builds)
|
||||
# Clear out DIST_FILES if it was set
|
||||
DIST_FILES =
|
||||
endif
|
||||
|
||||
|
||||
+1
-7
@@ -3,9 +3,6 @@
|
||||
# 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/.
|
||||
|
||||
with Files('**'):
|
||||
BUG_COMPONENT = ('Core', 'Build Config')
|
||||
|
||||
# This cannot be named "build" because of bug 922191.
|
||||
SPHINX_TREES['buildsystem'] = 'docs'
|
||||
|
||||
@@ -14,9 +11,6 @@ if CONFIG['OS_ARCH'] == 'WINNT':
|
||||
else:
|
||||
DIRS += ['unix']
|
||||
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'android':
|
||||
DIRS += ['annotationProcessors']
|
||||
|
||||
for var in ('GRE_MILESTONE',
|
||||
'MOZ_APP_VERSION',
|
||||
'MOZ_APP_BASENAME',
|
||||
@@ -70,7 +64,7 @@ if CONFIG['MOZ_ASAN'] and CONFIG['CLANG_CL']:
|
||||
|
||||
if CONFIG['MOZ_APP_BASENAME']:
|
||||
FINAL_TARGET_PP_FILES += ['application.ini']
|
||||
if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android' and CONFIG['MOZ_UPDATER']:
|
||||
if CONFIG['MOZ_UPDATER']:
|
||||
FINAL_TARGET_PP_FILES += ['update-settings.ini']
|
||||
|
||||
if CONFIG['MOZ_APP_STATIC_INI']:
|
||||
|
||||
@@ -1,149 +0,0 @@
|
||||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# 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/.
|
||||
|
||||
|
||||
js_option('--with-android-ndk', nargs=1,
|
||||
help='location where the Android NDK can be found')
|
||||
|
||||
js_option('--with-android-toolchain', nargs=1,
|
||||
help='location of the Android toolchain')
|
||||
|
||||
js_option('--with-android-gnu-compiler-version', nargs=1,
|
||||
help='GNU compiler version to use')
|
||||
|
||||
min_android_version = dependable(lambda: '9')
|
||||
|
||||
js_option('--with-android-version',
|
||||
nargs=1,
|
||||
help='android platform version',
|
||||
default=min_android_version)
|
||||
|
||||
@depends('--with-android-version', min_android_version)
|
||||
@imports(_from='__builtin__', _import='ValueError')
|
||||
def android_version(value, min_version):
|
||||
if not value:
|
||||
# Someone has passed --without-android-version.
|
||||
die('--with-android-version cannot be disabled.')
|
||||
|
||||
try:
|
||||
version = int(value[0])
|
||||
except ValueError:
|
||||
die('--with-android-version expects an integer value')
|
||||
|
||||
if version < int(min_version):
|
||||
die('--with-android-version must be at least %s (got %s)',
|
||||
min_version, value[0])
|
||||
|
||||
return version
|
||||
|
||||
add_old_configure_assignment('android_version', android_version)
|
||||
|
||||
@depends('--with-android-ndk', build_project)
|
||||
def ndk(value, build_project):
|
||||
if build_project == 'mobile/android' and not value:
|
||||
die('You must specify --with-android-ndk=/path/to/ndk when '
|
||||
'building mobile/android')
|
||||
if value:
|
||||
return value[0]
|
||||
|
||||
set_config('ANDROID_NDK', ndk)
|
||||
add_old_configure_assignment('android_ndk', ndk)
|
||||
|
||||
@depends(target, android_version, ndk)
|
||||
@checking('for android platform directory')
|
||||
@imports(_from='os.path', _import='isdir')
|
||||
def android_platform(target, android_version, ndk):
|
||||
if target.os != 'Android':
|
||||
return
|
||||
|
||||
if 'mips' in target.cpu:
|
||||
target_dir_name = 'mips'
|
||||
elif 'aarch64' == target.cpu:
|
||||
target_dir_name = 'arm64'
|
||||
else:
|
||||
target_dir_name = target.cpu
|
||||
|
||||
# Not all Android releases have their own platform release. We use
|
||||
# the next lower platform version in these cases.
|
||||
if android_version in (11, 10):
|
||||
platform_version = 9
|
||||
elif android_version in (20, 22):
|
||||
platform_version = android_version - 1
|
||||
else:
|
||||
platform_version = android_version
|
||||
|
||||
platform_dir = os.path.join(ndk,
|
||||
'platforms',
|
||||
'android-%s' % platform_version,
|
||||
'arch-%s' % target_dir_name)
|
||||
|
||||
if not isdir(platform_dir):
|
||||
die("Android platform directory not found. With the current "
|
||||
"configuration, it should be in %s" % platform_dir)
|
||||
|
||||
return platform_dir
|
||||
|
||||
add_old_configure_assignment('android_platform', android_platform)
|
||||
|
||||
@depends(android_platform)
|
||||
def extra_toolchain_flags(platform_dir):
|
||||
if not platform_dir:
|
||||
return []
|
||||
return ['-idirafter',
|
||||
os.path.join(platform_dir, 'usr', 'include')]
|
||||
|
||||
@depends(target, host, ndk, '--with-android-toolchain',
|
||||
'--with-android-gnu-compiler-version')
|
||||
@checking('for the Android toolchain directory', lambda x: x or 'not found')
|
||||
@imports(_from='os.path', _import='isdir')
|
||||
@imports(_from='mozbuild.shellutil', _import='quote')
|
||||
def android_toolchain(target, host, ndk, toolchain, gnu_compiler_version):
|
||||
if not ndk:
|
||||
return
|
||||
if toolchain:
|
||||
return toolchain[0]
|
||||
else:
|
||||
if target.cpu == 'arm' and target.endianness == 'little':
|
||||
target_base = 'arm-linux-androideabi'
|
||||
elif target.cpu == 'x86':
|
||||
target_base = 'x86'
|
||||
elif target.cpu == 'mips32' and target.endianness == 'little':
|
||||
target_base = 'mipsel-linux-android'
|
||||
elif target.cpu == 'aarch64' and target.endianness == 'little':
|
||||
target_base = 'aarch64-linux-android'
|
||||
else:
|
||||
die('Target cpu is not supported.')
|
||||
|
||||
toolchain_format = '%s/toolchains/%s-%s/prebuilt/%s-%s'
|
||||
|
||||
for version in gnu_compiler_version or ['4.9', '4.8', '4.7']:
|
||||
toolchain = toolchain_format % (ndk, target_base, version,
|
||||
host.kernel.lower(), host.cpu)
|
||||
log.debug('Trying %s' % quote(toolchain))
|
||||
if not isdir(toolchain) and host.cpu == 'x86_64':
|
||||
toolchain = toolchain_format % (ndk, target_base, version,
|
||||
host.kernel.lower(), 'x86')
|
||||
log.debug('Trying %s' % quote(toolchain))
|
||||
if isdir(toolchain):
|
||||
return toolchain
|
||||
else:
|
||||
if gnu_compiler_version:
|
||||
die('Your --with-android-gnu-compiler-version may be wrong')
|
||||
die('You have to specify --with-android-toolchain='
|
||||
'/path/to/ndk/toolchain.')
|
||||
|
||||
set_config('ANDROID_TOOLCHAIN', android_toolchain)
|
||||
|
||||
@depends(target, android_toolchain)
|
||||
def android_toolchain_prefix(target, toolchain):
|
||||
if toolchain:
|
||||
if target.cpu == 'x86':
|
||||
# Ideally, the --target should just have the right x86 variant
|
||||
# in the first place.
|
||||
return '%s/bin/i686-linux-android-' % toolchain
|
||||
return '%s/bin/%s-' % (toolchain, target.toolchain)
|
||||
|
||||
imply_option('--with-toolchain-prefix', android_toolchain_prefix,
|
||||
reason='--with-android-ndk')
|
||||
Loaded 100 of 3014 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user