summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--accessible/base/moz.build4
-rw-r--r--accessible/generic/moz.build4
-rw-r--r--accessible/html/moz.build4
-rw-r--r--accessible/ipc/moz.build4
-rw-r--r--accessible/ipc/other/moz.build4
-rw-r--r--accessible/mac/ARIAGridAccessibleWrap.h22
-rw-r--r--accessible/mac/AccessibleWrap.h103
-rw-r--r--accessible/mac/AccessibleWrap.mm256
-rw-r--r--accessible/mac/ApplicationAccessibleWrap.h22
-rw-r--r--accessible/mac/DocAccessibleWrap.h25
-rw-r--r--accessible/mac/DocAccessibleWrap.mm21
-rw-r--r--accessible/mac/HTMLTableAccessibleWrap.h24
-rw-r--r--accessible/mac/HyperTextAccessibleWrap.h20
-rw-r--r--accessible/mac/ImageAccessibleWrap.h22
-rw-r--r--accessible/mac/MacUtils.h26
-rw-r--r--accessible/mac/MacUtils.mm32
-rw-r--r--accessible/mac/Platform.mm174
-rw-r--r--accessible/mac/RootAccessibleWrap.h34
-rw-r--r--accessible/mac/RootAccessibleWrap.mm53
-rw-r--r--accessible/mac/TextLeafAccessibleWrap.h19
-rw-r--r--accessible/mac/XULListboxAccessibleWrap.h20
-rw-r--r--accessible/mac/XULMenuAccessibleWrap.h19
-rw-r--r--accessible/mac/XULTreeGridAccessibleWrap.h20
-rw-r--r--accessible/mac/moz.build44
-rw-r--r--accessible/mac/mozAccessible.h181
-rw-r--r--accessible/mac/mozAccessible.mm1197
-rw-r--r--accessible/mac/mozAccessibleProtocol.h69
-rw-r--r--accessible/mac/mozActionElements.h37
-rw-r--r--accessible/mac/mozActionElements.mm340
-rw-r--r--accessible/mac/mozDocAccessible.h31
-rw-r--r--accessible/mac/mozDocAccessible.mm111
-rw-r--r--accessible/mac/mozHTMLAccessible.h16
-rw-r--r--accessible/mac/mozHTMLAccessible.mm141
-rw-r--r--accessible/mac/mozTableAccessible.h28
-rw-r--r--accessible/mac/mozTableAccessible.mm281
-rw-r--r--accessible/mac/mozTextAccessible.h17
-rw-r--r--accessible/mac/mozTextAccessible.mm627
-rw-r--r--accessible/moz.build2
-rw-r--r--accessible/xpcom/moz.build4
-rw-r--r--accessible/xul/moz.build4
-rw-r--r--db/sqlite3/src/moz.build4
-rw-r--r--embedding/components/build/moz.build5
-rw-r--r--embedding/components/printingui/mac/moz.build15
-rw-r--r--embedding/components/printingui/mac/nsPrintProgress.cpp213
-rw-r--r--embedding/components/printingui/mac/nsPrintProgress.h44
-rw-r--r--embedding/components/printingui/mac/nsPrintProgressParams.cpp47
-rw-r--r--embedding/components/printingui/mac/nsPrintProgressParams.h28
-rw-r--r--embedding/components/printingui/mac/nsPrintingPromptService.h43
-rw-r--r--embedding/components/printingui/mac/nsPrintingPromptServiceX.mm128
-rw-r--r--embedding/components/printingui/moz.build2
-rw-r--r--gfx/cairo/cairo/src/moz.build4
-rw-r--r--gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp107
-rw-r--r--gfx/layers/basic/MacIOSurfaceTextureHostBasic.h97
-rw-r--r--gfx/layers/ipc/ShadowLayerUtilsMac.cpp40
-rw-r--r--gfx/layers/moz.build25
-rw-r--r--gfx/layers/opengl/GLManager.cpp70
-rw-r--r--gfx/layers/opengl/GLManager.h44
-rw-r--r--gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp140
-rw-r--r--gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h58
-rw-r--r--gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp180
-rw-r--r--gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h114
-rwxr-xr-xgfx/skia/generate_mozbuild.py3
-rw-r--r--gfx/skia/moz.build3
-rw-r--r--image/decoders/icon/mac/moz.build10
-rw-r--r--image/decoders/icon/mac/nsIconChannel.h59
-rw-r--r--image/decoders/icon/mac/nsIconChannelCocoa.mm565
-rw-r--r--image/decoders/icon/moz.build3
-rw-r--r--image/decoders/moz.build3
-rw-r--r--intl/locale/mac/moz.build15
-rw-r--r--intl/locale/mac/nsCollationMacUC.cpp253
-rw-r--r--intl/locale/mac/nsCollationMacUC.h44
-rw-r--r--intl/locale/mac/nsDateTimeFormatMac.cpp266
-rw-r--r--intl/locale/mac/nsDateTimeFormatMac.h61
-rw-r--r--intl/locale/mac/nsMacCharset.cpp59
-rw-r--r--intl/locale/moz.build2
-rw-r--r--intl/lwbrk/moz.build4
-rw-r--r--intl/lwbrk/nsCarbonBreaker.cpp44
-rw-r--r--js/xpconnect/shell/moz.build5
-rw-r--r--js/xpconnect/shell/xpcshellMacUtils.h8
-rw-r--r--js/xpconnect/shell/xpcshellMacUtils.mm18
-rw-r--r--layout/build/moz.build4
-rw-r--r--layout/reftests/reftest.list3
-rw-r--r--mailnews/base/ispdata/moz.build4
-rw-r--r--mailnews/base/src/moz.build2
-rw-r--r--mailnews/base/src/nsMessengerOSXIntegration.h63
-rw-r--r--mailnews/base/src/nsMessengerOSXIntegration.mm700
-rw-r--r--mailnews/build/moz.build4
-rw-r--r--mailnews/compose/src/moz.build10
-rw-r--r--mailnews/compose/src/nsMsgAppleCodes.h106
-rw-r--r--mailnews/compose/src/nsMsgAppleDouble.h207
-rw-r--r--mailnews/compose/src/nsMsgAppleDoubleEncode.cpp266
-rw-r--r--mailnews/compose/src/nsMsgAppleEncode.cpp703
-rw-r--r--mailnews/import/build/moz.build7
-rw-r--r--media/libcubeb/src/cubeb_osx_run_loop.c11
-rw-r--r--media/libcubeb/src/cubeb_osx_run_loop.h22
-rw-r--r--media/libcubeb/src/moz.build4
-rw-r--r--netwerk/base/NetworkInfoServiceCocoa.cpp103
-rw-r--r--netwerk/base/moz.build9
-rw-r--r--netwerk/base/nsURLHelperOSX.cpp216
-rw-r--r--netwerk/build/moz.build7
-rw-r--r--netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp779
-rw-r--r--netwerk/dns/mdns/libmdns/MDNSResponderOperator.h152
-rw-r--r--netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp302
-rw-r--r--netwerk/dns/mdns/libmdns/MDNSResponderReply.h164
-rw-r--r--netwerk/dns/mdns/libmdns/moz.build38
-rw-r--r--netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp262
-rw-r--r--netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h49
-rw-r--r--netwerk/streamconv/converters/moz.build6
-rw-r--r--netwerk/system/mac/moz.build13
-rw-r--r--netwerk/system/mac/nsNetworkLinkService.h54
-rw-r--r--netwerk/system/mac/nsNetworkLinkService.mm526
-rw-r--r--netwerk/system/moz.build2
-rw-r--r--toolkit/components/parentalcontrols/moz.build2
-rw-r--r--toolkit/components/parentalcontrols/nsParentalControlsServiceCocoa.mm79
-rw-r--r--toolkit/components/startup/moz.build6
-rw-r--r--toolkit/components/startup/nsUserInfoMac.h25
-rw-r--r--toolkit/components/startup/nsUserInfoMac.mm84
-rw-r--r--toolkit/library/moz.build42
-rw-r--r--toolkit/modules/moz.build2
-rw-r--r--toolkit/moz.build2
-rw-r--r--toolkit/moz.configure8
-rw-r--r--toolkit/mozapps/update/updater/Makefile.in13
-rw-r--r--toolkit/mozapps/update/updater/launchchild_osx.mm384
-rw-r--r--toolkit/mozapps/update/updater/macbuild/Contents/Info.plist.in39
-rw-r--r--toolkit/mozapps/update/updater/macbuild/Contents/PkgInfo1
-rw-r--r--toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in7
-rw-r--r--toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/MainMenu.nib/classes.nib19
-rw-r--r--toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/MainMenu.nib/info.nib22
-rw-r--r--toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nibbin0 -> 5567 bytes
-rw-r--r--toolkit/mozapps/update/updater/macbuild/Contents/Resources/updater.icnsbin0 -> 55969 bytes
-rw-r--r--toolkit/mozapps/update/updater/moz.build15
-rw-r--r--toolkit/mozapps/update/updater/progressui_osx.mm144
-rw-r--r--toolkit/mozapps/update/updater/updater-common.build18
-rw-r--r--toolkit/system/osxproxy/ProxyUtils.h21
-rw-r--r--toolkit/system/osxproxy/ProxyUtils.mm182
-rw-r--r--toolkit/system/osxproxy/moz.build13
-rw-r--r--toolkit/system/osxproxy/nsOSXSystemProxySettings.mm326
-rw-r--r--toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp41
-rw-r--r--toolkit/system/osxproxy/tests/gtest/moz.build17
-rw-r--r--toolkit/themes/moz.build4
-rw-r--r--toolkit/themes/osx/global/10pct_transparent_grey.pngbin0 -> 123 bytes
-rw-r--r--toolkit/themes/osx/global/50pct_transparent_grey.pngbin0 -> 107 bytes
-rw-r--r--toolkit/themes/osx/global/alerts/alert.css30
-rw-r--r--toolkit/themes/osx/global/arrow.css38
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-dn-dis.gifbin0 -> 65 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-dn-dis.pngbin0 -> 185 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-dn-sharp.gifbin0 -> 51 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-dn.gifbin0 -> 56 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-dn.pngbin0 -> 191 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-lft-dis.gifbin0 -> 105 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-lft-hov.gifbin0 -> 57 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-lft-sharp-end.gifbin0 -> 56 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-lft-sharp.gifbin0 -> 53 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-lft.gifbin0 -> 57 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-rit-dis.gifbin0 -> 105 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-rit-hov.gifbin0 -> 57 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-rit-sharp-end.gifbin0 -> 56 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-rit-sharp.gifbin0 -> 53 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-rit.gifbin0 -> 57 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-up-dis.gifbin0 -> 65 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-up-sharp.gifbin0 -> 52 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/arrow-up.gifbin0 -> 56 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/panelarrow-horizontal.pngbin0 -> 117 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/panelarrow-horizontal@2x.pngbin0 -> 267 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/panelarrow-vertical.pngbin0 -> 133 bytes
-rw-r--r--toolkit/themes/osx/global/arrow/panelarrow-vertical@2x.pngbin0 -> 227 bytes
-rw-r--r--toolkit/themes/osx/global/autocomplete.css174
-rw-r--r--toolkit/themes/osx/global/button.css85
-rw-r--r--toolkit/themes/osx/global/checkbox.css39
-rw-r--r--toolkit/themes/osx/global/checkbox/cbox-check-dis.gifbin0 -> 60 bytes
-rw-r--r--toolkit/themes/osx/global/checkbox/cbox-check.gifbin0 -> 54 bytes
-rw-r--r--toolkit/themes/osx/global/colorpicker.css41
-rw-r--r--toolkit/themes/osx/global/commonDialog.css35
-rw-r--r--toolkit/themes/osx/global/console/console-error-caret.gifbin0 -> 55 bytes
-rw-r--r--toolkit/themes/osx/global/console/console-error-dash.gifbin0 -> 48 bytes
-rw-r--r--toolkit/themes/osx/global/console/console.css165
-rw-r--r--toolkit/themes/osx/global/customizeToolbar.css38
-rw-r--r--toolkit/themes/osx/global/datetimepicker.css126
-rw-r--r--toolkit/themes/osx/global/dialog.css77
-rw-r--r--toolkit/themes/osx/global/dirListing/dirListing.css104
-rw-r--r--toolkit/themes/osx/global/dirListing/folder.pngbin0 -> 325 bytes
-rw-r--r--toolkit/themes/osx/global/dirListing/remote.pngbin0 -> 563 bytes
-rw-r--r--toolkit/themes/osx/global/dirListing/up.pngbin0 -> 617 bytes
-rw-r--r--toolkit/themes/osx/global/dropmarker.css31
-rw-r--r--toolkit/themes/osx/global/filefield.css38
-rw-r--r--toolkit/themes/osx/global/filters.svg14
-rw-r--r--toolkit/themes/osx/global/findBar.css270
-rw-r--r--toolkit/themes/osx/global/global.css378
-rw-r--r--toolkit/themes/osx/global/groupbox.css30
-rw-r--r--toolkit/themes/osx/global/icons/Error.pngbin0 -> 1439 bytes
-rw-r--r--toolkit/themes/osx/global/icons/autocomplete-dropmarker.pngbin0 -> 234 bytes
-rw-r--r--toolkit/themes/osx/global/icons/autocomplete-search.svg22
-rw-r--r--toolkit/themes/osx/global/icons/autoscroll.pngbin0 -> 2983 bytes
-rw-r--r--toolkit/themes/osx/global/icons/blacklist_64.pngbin0 -> 3771 bytes
-rw-r--r--toolkit/themes/osx/global/icons/blacklist_favicon.pngbin0 -> 543 bytes
-rw-r--r--toolkit/themes/osx/global/icons/checkbox.pngbin0 -> 1737 bytes
-rw-r--r--toolkit/themes/osx/global/icons/checkbox@2x.pngbin0 -> 1824 bytes
-rw-r--r--toolkit/themes/osx/global/icons/chevron-inverted.pngbin0 -> 247 bytes
-rw-r--r--toolkit/themes/osx/global/icons/chevron-inverted@2x.pngbin0 -> 481 bytes
-rw-r--r--toolkit/themes/osx/global/icons/chevron.pngbin0 -> 251 bytes
-rw-r--r--toolkit/themes/osx/global/icons/chevron@2x.pngbin0 -> 462 bytes
-rw-r--r--toolkit/themes/osx/global/icons/close.pngbin0 -> 1240 bytes
-rwxr-xr-xtoolkit/themes/osx/global/icons/close@2x.pngbin0 -> 2768 bytes
-rw-r--r--toolkit/themes/osx/global/icons/error-16.pngbin0 -> 677 bytes
-rw-r--r--toolkit/themes/osx/global/icons/error-64.pngbin0 -> 2533 bytes
-rw-r--r--toolkit/themes/osx/global/icons/error-large.pngbin0 -> 1996 bytes
-rw-r--r--toolkit/themes/osx/global/icons/glyph-dropdown.pngbin0 -> 99 bytes
-rw-r--r--toolkit/themes/osx/global/icons/glyph-dropdown@2x.pngbin0 -> 130 bytes
-rw-r--r--toolkit/themes/osx/global/icons/information-16.pngbin0 -> 818 bytes
-rw-r--r--toolkit/themes/osx/global/icons/information-24.pngbin0 -> 1289 bytes
-rw-r--r--toolkit/themes/osx/global/icons/information-32.pngbin0 -> 1773 bytes
-rw-r--r--toolkit/themes/osx/global/icons/information-64.pngbin0 -> 3687 bytes
-rw-r--r--toolkit/themes/osx/global/icons/information-large.pngbin0 -> 2592 bytes
-rw-r--r--toolkit/themes/osx/global/icons/loading_16.pngbin0 -> 9025 bytes
-rw-r--r--toolkit/themes/osx/global/icons/menulist-dropmarker.pngbin0 -> 158 bytes
-rw-r--r--toolkit/themes/osx/global/icons/notfound.pngbin0 -> 597 bytes
-rw-r--r--toolkit/themes/osx/global/icons/notloading_16.pngbin0 -> 686 bytes
-rw-r--r--toolkit/themes/osx/global/icons/panebutton-active.pngbin0 -> 400 bytes
-rw-r--r--toolkit/themes/osx/global/icons/panebutton-inactive.pngbin0 -> 257 bytes
-rw-r--r--toolkit/themes/osx/global/icons/panel-dropmarker.pngbin0 -> 161 bytes
-rw-r--r--toolkit/themes/osx/global/icons/question-16.pngbin0 -> 866 bytes
-rw-r--r--toolkit/themes/osx/global/icons/question-32.pngbin0 -> 1962 bytes
-rw-r--r--toolkit/themes/osx/global/icons/question-64.pngbin0 -> 3970 bytes
-rw-r--r--toolkit/themes/osx/global/icons/question-large.pngbin0 -> 2851 bytes
-rw-r--r--toolkit/themes/osx/global/icons/resizer-rtl.pngbin0 -> 192 bytes
-rw-r--r--toolkit/themes/osx/global/icons/resizer-rtl@2x.pngbin0 -> 284 bytes
-rw-r--r--toolkit/themes/osx/global/icons/resizer.pngbin0 -> 196 bytes
-rw-r--r--toolkit/themes/osx/global/icons/resizer@2x.pngbin0 -> 288 bytes
-rw-r--r--toolkit/themes/osx/global/icons/search-textbox.svg13
-rw-r--r--toolkit/themes/osx/global/icons/searchfield-cancel.svg20
-rw-r--r--toolkit/themes/osx/global/icons/sslWarning.pngbin0 -> 4120 bytes
-rw-r--r--toolkit/themes/osx/global/icons/tabprompts-bgtexture.pngbin0 -> 5940 bytes
-rw-r--r--toolkit/themes/osx/global/icons/warning-16.pngbin0 -> 690 bytes
-rw-r--r--toolkit/themes/osx/global/icons/warning-32.pngbin0 -> 1483 bytes
-rw-r--r--toolkit/themes/osx/global/icons/warning-64.pngbin0 -> 3308 bytes
-rw-r--r--toolkit/themes/osx/global/icons/warning-large.pngbin0 -> 2281 bytes
-rw-r--r--toolkit/themes/osx/global/in-content/common.css121
-rw-r--r--toolkit/themes/osx/global/in-content/info-pages.css1
-rw-r--r--toolkit/themes/osx/global/inContentUI.css144
-rw-r--r--toolkit/themes/osx/global/jar.mn156
-rw-r--r--toolkit/themes/osx/global/linkTree.css32
-rw-r--r--toolkit/themes/osx/global/listbox.css113
-rw-r--r--toolkit/themes/osx/global/menu.css187
-rw-r--r--toolkit/themes/osx/global/menulist.css65
-rw-r--r--toolkit/themes/osx/global/moz.build6
-rw-r--r--toolkit/themes/osx/global/nativescrollbars.css89
-rw-r--r--toolkit/themes/osx/global/netError.css145
-rw-r--r--toolkit/themes/osx/global/notification.css206
-rw-r--r--toolkit/themes/osx/global/notification/close.pngbin0 -> 795 bytes
-rw-r--r--toolkit/themes/osx/global/notification/error-icon.pngbin0 -> 518 bytes
-rw-r--r--toolkit/themes/osx/global/notification/info-icon.pngbin0 -> 533 bytes
-rw-r--r--toolkit/themes/osx/global/notification/warning-icon.pngbin0 -> 626 bytes
-rw-r--r--toolkit/themes/osx/global/numberbox.css33
-rw-r--r--toolkit/themes/osx/global/popup.css141
-rw-r--r--toolkit/themes/osx/global/preferences.css64
-rw-r--r--toolkit/themes/osx/global/progressmeter.css22
-rw-r--r--toolkit/themes/osx/global/radio.css43
-rw-r--r--toolkit/themes/osx/global/resizer.css69
-rw-r--r--toolkit/themes/osx/global/richlistbox.css27
-rw-r--r--toolkit/themes/osx/global/scale.css46
-rw-r--r--toolkit/themes/osx/global/scale/scale-tray-horiz.gifbin0 -> 50 bytes
-rw-r--r--toolkit/themes/osx/global/scale/scale-tray-vert.gifbin0 -> 50 bytes
-rw-r--r--toolkit/themes/osx/global/scrollbox.css62
-rw-r--r--toolkit/themes/osx/global/shared.inc20
-rw-r--r--toolkit/themes/osx/global/spinbuttons.css31
-rw-r--r--toolkit/themes/osx/global/splitter.css124
-rw-r--r--toolkit/themes/osx/global/splitter/dimple.pngbin0 -> 155 bytes
-rw-r--r--toolkit/themes/osx/global/splitter/grip-bottom.gifbin0 -> 145 bytes
-rw-r--r--toolkit/themes/osx/global/splitter/grip-left.gifbin0 -> 157 bytes
-rw-r--r--toolkit/themes/osx/global/splitter/grip-right.gifbin0 -> 157 bytes
-rw-r--r--toolkit/themes/osx/global/splitter/grip-top.gifbin0 -> 144 bytes
-rw-r--r--toolkit/themes/osx/global/tabbox.css148
-rw-r--r--toolkit/themes/osx/global/tabprompts.css67
-rw-r--r--toolkit/themes/osx/global/textbox.css102
-rw-r--r--toolkit/themes/osx/global/toolbar.css127
-rw-r--r--toolkit/themes/osx/global/toolbar/spring.pngbin0 -> 239 bytes
-rw-r--r--toolkit/themes/osx/global/toolbar/toolbar-separator.pngbin0 -> 115 bytes
-rw-r--r--toolkit/themes/osx/global/toolbarbutton.css124
-rw-r--r--toolkit/themes/osx/global/tree.css296
-rw-r--r--toolkit/themes/osx/global/tree/arrow-disclosure.svg28
-rw-r--r--toolkit/themes/osx/global/tree/columnpicker.gifbin0 -> 68 bytes
-rw-r--r--toolkit/themes/osx/global/tree/folder.pngbin0 -> 320 bytes
-rw-r--r--toolkit/themes/osx/global/tree/folder@2x.pngbin0 -> 589 bytes
-rw-r--r--toolkit/themes/osx/global/viewbuttons.css36
-rw-r--r--toolkit/themes/osx/global/wizard.css62
-rw-r--r--toolkit/themes/osx/mochitests/.eslintrc.js7
-rw-r--r--toolkit/themes/osx/mochitests/chrome.ini3
-rw-r--r--toolkit/themes/osx/mochitests/test_bug510426.xul54
-rw-r--r--toolkit/themes/osx/moz.build8
-rw-r--r--toolkit/themes/osx/mozapps/downloads/buttons.pngbin0 -> 2288 bytes
-rw-r--r--toolkit/themes/osx/mozapps/downloads/downloadIcon.pngbin0 -> 1301 bytes
-rw-r--r--toolkit/themes/osx/mozapps/downloads/downloads.css123
-rw-r--r--toolkit/themes/osx/mozapps/downloads/unknownContentType.css30
-rw-r--r--toolkit/themes/osx/mozapps/extensions/about.css78
-rw-r--r--toolkit/themes/osx/mozapps/extensions/alerticon-error.pngbin0 -> 3402 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/alerticon-info-negative.pngbin0 -> 1564 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/alerticon-info-positive.pngbin0 -> 1338 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/alerticon-warning.pngbin0 -> 1567 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/blocklist.css20
-rw-r--r--toolkit/themes/osx/mozapps/extensions/cancel.pngbin0 -> 115 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-available.pngbin0 -> 1671 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-dictionaries.pngbin0 -> 1769 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-discover.pngbin0 -> 1324 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-experiments.pngbin0 -> 822 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-plugins.pngbin0 -> 886 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-recent.pngbin0 -> 1642 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-search.pngbin0 -> 2600 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-searchengines.pngbin0 -> 2814 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/category-service.pngbin0 -> 2063 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/dictionaryGeneric-16.pngbin0 -> 742 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/dictionaryGeneric.pngbin0 -> 1769 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/discover-logo.pngbin0 -> 12007 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/eula.css47
-rw-r--r--toolkit/themes/osx/mozapps/extensions/experimentGeneric.pngbin0 -> 822 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/extensionGeneric-16.pngbin0 -> 554 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/extensionGeneric.pngbin0 -> 1302 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/extensions.css1206
-rw-r--r--toolkit/themes/osx/mozapps/extensions/heart.pngbin0 -> 2949 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/localeGeneric.pngbin0 -> 2410 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/navigation.pngbin0 -> 586 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/newaddon.css112
-rw-r--r--toolkit/themes/osx/mozapps/extensions/rating-not-won.pngbin0 -> 1559 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/rating-won.pngbin0 -> 1662 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/search.pngbin0 -> 423 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/selectAddons.css163
-rw-r--r--toolkit/themes/osx/mozapps/extensions/stripes-compatibility.pngbin0 -> 1041 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/stripes-error.pngbin0 -> 1979 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/stripes-info-negative.pngbin0 -> 2027 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/stripes-info-positive.pngbin0 -> 1852 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/stripes-warning.pngbin0 -> 2177 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/themeGeneric-16.pngbin0 -> 710 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/themeGeneric.pngbin0 -> 2185 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/toolbarbutton-dropmarker.pngbin0 -> 147 bytes
-rw-r--r--toolkit/themes/osx/mozapps/extensions/update.css26
-rw-r--r--toolkit/themes/osx/mozapps/extensions/xpinstallConfirm.css90
-rw-r--r--toolkit/themes/osx/mozapps/handling/handling.css30
-rw-r--r--toolkit/themes/osx/mozapps/jar.mn79
-rw-r--r--toolkit/themes/osx/mozapps/moz.build6
-rw-r--r--toolkit/themes/osx/mozapps/passwordmgr/key-16.pngbin0 -> 773 bytes
-rw-r--r--toolkit/themes/osx/mozapps/passwordmgr/key-64.pngbin0 -> 6142 bytes
-rw-r--r--toolkit/themes/osx/mozapps/passwordmgr/key.pngbin0 -> 658 bytes
-rw-r--r--toolkit/themes/osx/mozapps/plugins/notifyPluginGeneric.pngbin0 -> 313 bytes
-rw-r--r--toolkit/themes/osx/mozapps/plugins/pluginBlocked-64.pngbin0 -> 4563 bytes
-rw-r--r--toolkit/themes/osx/mozapps/plugins/pluginBlocked.pngbin0 -> 2152 bytes
-rw-r--r--toolkit/themes/osx/mozapps/plugins/pluginGeneric-16.pngbin0 -> 759 bytes
-rw-r--r--toolkit/themes/osx/mozapps/plugins/pluginGeneric.pngbin0 -> 1939 bytes
-rw-r--r--toolkit/themes/osx/mozapps/plugins/pluginHelp-16.pngbin0 -> 620 bytes
-rw-r--r--toolkit/themes/osx/mozapps/profile/profileSelection.css29
-rw-r--r--toolkit/themes/osx/mozapps/profile/profileicon-selected.pngbin0 -> 502 bytes
-rw-r--r--toolkit/themes/osx/mozapps/profile/profileicon.pngbin0 -> 588 bytes
-rw-r--r--toolkit/themes/osx/mozapps/update/buttons.pngbin0 -> 2288 bytes
-rw-r--r--toolkit/themes/osx/mozapps/update/updates.css171
-rw-r--r--toolkit/themes/osx/mozapps/viewsource/viewsource.css5
-rw-r--r--toolkit/themes/osx/reftests/482681-ref.xul21
-rw-r--r--toolkit/themes/osx/reftests/482681.xul22
-rw-r--r--toolkit/themes/osx/reftests/baseline.xul175
-rw-r--r--toolkit/themes/osx/reftests/checkboxsize-ref.xul32
-rw-r--r--toolkit/themes/osx/reftests/checkboxsize.xul31
-rw-r--r--toolkit/themes/osx/reftests/nostretch-ref.xul107
-rw-r--r--toolkit/themes/osx/reftests/nostretch.xul120
-rw-r--r--toolkit/themes/osx/reftests/radiosize-ref.xul32
-rw-r--r--toolkit/themes/osx/reftests/radiosize.xul31
-rw-r--r--toolkit/themes/osx/reftests/reftest-stylo.list6
-rw-r--r--toolkit/themes/osx/reftests/reftest.list5
-rw-r--r--toolkit/xre/MacApplicationDelegate.h16
-rw-r--r--toolkit/xre/MacApplicationDelegate.mm396
-rw-r--r--toolkit/xre/MacAutoreleasePool.h31
-rw-r--r--toolkit/xre/MacAutoreleasePool.mm20
-rw-r--r--toolkit/xre/MacLaunchHelper.h23
-rw-r--r--toolkit/xre/MacLaunchHelper.mm137
-rw-r--r--toolkit/xre/moz.build15
-rw-r--r--uriloader/exthandler/mac/nsDecodeAppleFile.cpp389
-rw-r--r--uriloader/exthandler/mac/nsDecodeAppleFile.h118
-rw-r--r--uriloader/exthandler/mac/nsLocalHandlerAppMac.h26
-rw-r--r--uriloader/exthandler/mac/nsLocalHandlerAppMac.mm84
-rw-r--r--uriloader/exthandler/mac/nsMIMEInfoMac.h34
-rw-r--r--uriloader/exthandler/mac/nsMIMEInfoMac.mm114
-rw-r--r--uriloader/exthandler/mac/nsOSHelperAppService.h48
-rw-r--r--uriloader/exthandler/mac/nsOSHelperAppService.mm569
-rw-r--r--uriloader/exthandler/moz.build10
-rw-r--r--xpcom/build/moz.build3
381 files changed, 22780 insertions, 36 deletions
diff --git a/accessible/base/moz.build b/accessible/base/moz.build
index 54627ca50c..ea9b67aee9 100644
--- a/accessible/base/moz.build
+++ b/accessible/base/moz.build
@@ -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',
diff --git a/accessible/generic/moz.build b/accessible/generic/moz.build
index 720d9bf01b..6855daf909 100644
--- a/accessible/generic/moz.build
+++ b/accessible/generic/moz.build
@@ -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',
diff --git a/accessible/html/moz.build b/accessible/html/moz.build
index a18c4e59b1..e486f10456 100644
--- a/accessible/html/moz.build
+++ b/accessible/html/moz.build
@@ -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',
diff --git a/accessible/ipc/moz.build b/accessible/ipc/moz.build
index 91fd1fa4d3..cb852de271 100644
--- a/accessible/ipc/moz.build
+++ b/accessible/ipc/moz.build
@@ -19,6 +19,10 @@ else:
LOCAL_INCLUDES += [
'/accessible/atk',
]
+ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/accessible/mac',
+ ]
else:
LOCAL_INCLUDES += [
'/accessible/other',
diff --git a/accessible/ipc/other/moz.build b/accessible/ipc/other/moz.build
index 489520cef6..50f96de040 100644
--- a/accessible/ipc/other/moz.build
+++ b/accessible/ipc/other/moz.build
@@ -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',
diff --git a/accessible/mac/ARIAGridAccessibleWrap.h b/accessible/mac/ARIAGridAccessibleWrap.h
new file mode 100644
index 0000000000..5d397e915c
--- /dev/null
+++ b/accessible/mac/ARIAGridAccessibleWrap.h
@@ -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
diff --git a/accessible/mac/AccessibleWrap.h b/accessible/mac/AccessibleWrap.h
new file mode 100644
index 0000000000..6c746ff0dc
--- /dev/null
+++ b/accessible/mac/AccessibleWrap.h
@@ -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
diff --git a/accessible/mac/AccessibleWrap.mm b/accessible/mac/AccessibleWrap.mm
new file mode 100644
index 0000000000..65f2e1db42
--- /dev/null
+++ b/accessible/mac/AccessibleWrap.mm
@@ -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;
+}
diff --git a/accessible/mac/ApplicationAccessibleWrap.h b/accessible/mac/ApplicationAccessibleWrap.h
new file mode 100644
index 0000000000..9343c29ddc
--- /dev/null
+++ b/accessible/mac/ApplicationAccessibleWrap.h
@@ -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
+
diff --git a/accessible/mac/DocAccessibleWrap.h b/accessible/mac/DocAccessibleWrap.h
new file mode 100644
index 0000000000..3e80a0d33c
--- /dev/null
+++ b/accessible/mac/DocAccessibleWrap.h
@@ -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
diff --git a/accessible/mac/DocAccessibleWrap.mm b/accessible/mac/DocAccessibleWrap.mm
new file mode 100644
index 0000000000..8a513f485a
--- /dev/null
+++ b/accessible/mac/DocAccessibleWrap.mm
@@ -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()
+{
+}
+
diff --git a/accessible/mac/HTMLTableAccessibleWrap.h b/accessible/mac/HTMLTableAccessibleWrap.h
new file mode 100644
index 0000000000..4f158e241d
--- /dev/null
+++ b/accessible/mac/HTMLTableAccessibleWrap.h
@@ -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
+
diff --git a/accessible/mac/HyperTextAccessibleWrap.h b/accessible/mac/HyperTextAccessibleWrap.h
new file mode 100644
index 0000000000..fb335ef0f7
--- /dev/null
+++ b/accessible/mac/HyperTextAccessibleWrap.h
@@ -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
+
diff --git a/accessible/mac/ImageAccessibleWrap.h b/accessible/mac/ImageAccessibleWrap.h
new file mode 100644
index 0000000000..069efb6511
--- /dev/null
+++ b/accessible/mac/ImageAccessibleWrap.h
@@ -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
+
diff --git a/accessible/mac/MacUtils.h b/accessible/mac/MacUtils.h
new file mode 100644
index 0000000000..f88a27ee58
--- /dev/null
+++ b/accessible/mac/MacUtils.h
@@ -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
diff --git a/accessible/mac/MacUtils.mm b/accessible/mac/MacUtils.mm
new file mode 100644
index 0000000000..2ce03fe966
--- /dev/null
+++ b/accessible/mac/MacUtils.mm
@@ -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);
+}
+
+}
+}
+}
diff --git a/accessible/mac/Platform.mm b/accessible/mac/Platform.mm
new file mode 100644
index 0000000000..a104bf904c
--- /dev/null
+++ b/accessible/mac/Platform.mm
@@ -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
+
diff --git a/accessible/mac/RootAccessibleWrap.h b/accessible/mac/RootAccessibleWrap.h
new file mode 100644
index 0000000000..aa53e06ac0
--- /dev/null
+++ b/accessible/mac/RootAccessibleWrap.h
@@ -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
diff --git a/accessible/mac/RootAccessibleWrap.mm b/accessible/mac/RootAccessibleWrap.mm
new file mode 100644
index 0000000000..037545cce2
--- /dev/null
+++ b/accessible/mac/RootAccessibleWrap.mm
@@ -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!");
+ }
+ }
+ }
+}
diff --git a/accessible/mac/TextLeafAccessibleWrap.h b/accessible/mac/TextLeafAccessibleWrap.h
new file mode 100644
index 0000000000..d07b9defec
--- /dev/null
+++ b/accessible/mac/TextLeafAccessibleWrap.h
@@ -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
diff --git a/accessible/mac/XULListboxAccessibleWrap.h b/accessible/mac/XULListboxAccessibleWrap.h
new file mode 100644
index 0000000000..f7dc6cc547
--- /dev/null
+++ b/accessible/mac/XULListboxAccessibleWrap.h
@@ -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
diff --git a/accessible/mac/XULMenuAccessibleWrap.h b/accessible/mac/XULMenuAccessibleWrap.h
new file mode 100644
index 0000000000..6efcf007eb
--- /dev/null
+++ b/accessible/mac/XULMenuAccessibleWrap.h
@@ -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
diff --git a/accessible/mac/XULTreeGridAccessibleWrap.h b/accessible/mac/XULTreeGridAccessibleWrap.h
new file mode 100644
index 0000000000..b3631e9adb
--- /dev/null
+++ b/accessible/mac/XULTreeGridAccessibleWrap.h
@@ -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
diff --git a/accessible/mac/moz.build b/accessible/mac/moz.build
new file mode 100644
index 0000000000..8d2e7b391f
--- /dev/null
+++ b/accessible/mac/moz.build
@@ -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')
diff --git a/accessible/mac/mozAccessible.h b/accessible/mac/mozAccessible.h
new file mode 100644
index 0000000000..6d7db3fe98
--- /dev/null
+++ b/accessible/mac/mozAccessible.h
@@ -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
+
diff --git a/accessible/mac/mozAccessible.mm b/accessible/mac/mozAccessible.mm
new file mode 100644
index 0000000000..a02779ef25
--- /dev/null
+++ b/accessible/mac/mozAccessible.mm
@@ -0,0 +1,1197 @@
+/* -*- 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 "mozAccessible.h"
+
+#import "MacUtils.h"
+#import "mozView.h"
+
+#include "Accessible-inl.h"
+#include "nsAccUtils.h"
+#include "nsIAccessibleRelation.h"
+#include "nsIAccessibleEditableText.h"
+#include "nsIPersistentProperties2.h"
+#include "Relation.h"
+#include "Role.h"
+#include "RootAccessible.h"
+#include "TableAccessible.h"
+#include "TableCellAccessible.h"
+#include "mozilla/a11y/PDocAccessible.h"
+#include "OuterDocAccessible.h"
+
+#include "mozilla/Services.h"
+#include "nsRect.h"
+#include "nsCocoaUtils.h"
+#include "nsCoord.h"
+#include "nsObjCExceptions.h"
+#include "nsWhitespaceTokenizer.h"
+#include <prdtoa.h>
+
+using namespace mozilla;
+using namespace mozilla::a11y;
+
+#define NSAccessibilityMathRootRadicandAttribute @"AXMathRootRadicand"
+#define NSAccessibilityMathRootIndexAttribute @"AXMathRootIndex"
+#define NSAccessibilityMathFractionNumeratorAttribute @"AXMathFractionNumerator"
+#define NSAccessibilityMathFractionDenominatorAttribute @"AXMathFractionDenominator"
+#define NSAccessibilityMathBaseAttribute @"AXMathBase"
+#define NSAccessibilityMathSubscriptAttribute @"AXMathSubscript"
+#define NSAccessibilityMathSuperscriptAttribute @"AXMathSuperscript"
+#define NSAccessibilityMathUnderAttribute @"AXMathUnder"
+#define NSAccessibilityMathOverAttribute @"AXMathOver"
+#define NSAccessibilityMathLineThicknessAttribute @"AXMathLineThickness"
+// XXX WebKit also defines the following attributes.
+// See bugs 1176970 and 1176983.
+// - NSAccessibilityMathFencedOpenAttribute @"AXMathFencedOpen"
+// - NSAccessibilityMathFencedCloseAttribute @"AXMathFencedClose"
+// - NSAccessibilityMathPrescriptsAttribute @"AXMathPrescripts"
+// - NSAccessibilityMathPostscriptsAttribute @"AXMathPostscripts"
+
+#pragma mark -
+
+@implementation mozAccessible
+
+- (id)initWithAccessible:(uintptr_t)aGeckoAccessible
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if ((self = [super init])) {
+ mGeckoAccessible = aGeckoAccessible;
+ if (aGeckoAccessible & IS_PROXY)
+ mRole = [self getProxyAccessible]->Role();
+ else
+ mRole = [self getGeckoAccessible]->Role();
+ }
+
+ return self;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (void)dealloc
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [mChildren release];
+ [super dealloc];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (mozilla::a11y::AccessibleWrap*)getGeckoAccessible
+{
+ // Check if mGeckoAccessible points at a proxy
+ if (mGeckoAccessible & IS_PROXY)
+ return nil;
+
+ return reinterpret_cast<AccessibleWrap*>(mGeckoAccessible);
+}
+
+- (mozilla::a11y::ProxyAccessible*)getProxyAccessible
+{
+ // Check if mGeckoAccessible points at a proxy
+ if (!(mGeckoAccessible & IS_PROXY))
+ return nil;
+
+ return reinterpret_cast<ProxyAccessible*>(mGeckoAccessible & ~IS_PROXY);
+}
+
+#pragma mark -
+
+- (BOOL)accessibilityIsIgnored
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ // unknown (either unimplemented, or irrelevant) elements are marked as ignored
+ // as well as expired elements.
+
+ bool noRole = [[self role] isEqualToString:NSAccessibilityUnknownRole];
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ return (noRole && !(accWrap->InteractiveState() & states::FOCUSABLE));
+
+ if (ProxyAccessible* proxy = [self getProxyAccessible])
+ return (noRole && !(proxy->State() & states::FOCUSABLE));
+
+ return true;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
+}
+
+- (NSArray*)additionalAccessibilityAttributeNames
+{
+ NSMutableArray* additional = [NSMutableArray array];
+ switch (mRole) {
+ case roles::MATHML_ROOT:
+ [additional addObject:NSAccessibilityMathRootIndexAttribute];
+ [additional addObject:NSAccessibilityMathRootRadicandAttribute];
+ break;
+ case roles::MATHML_SQUARE_ROOT:
+ [additional addObject:NSAccessibilityMathRootRadicandAttribute];
+ break;
+ case roles::MATHML_FRACTION:
+ [additional addObject:NSAccessibilityMathFractionNumeratorAttribute];
+ [additional addObject:NSAccessibilityMathFractionDenominatorAttribute];
+ [additional addObject:NSAccessibilityMathLineThicknessAttribute];
+ break;
+ case roles::MATHML_SUB:
+ case roles::MATHML_SUP:
+ case roles::MATHML_SUB_SUP:
+ [additional addObject:NSAccessibilityMathBaseAttribute];
+ [additional addObject:NSAccessibilityMathSubscriptAttribute];
+ [additional addObject:NSAccessibilityMathSuperscriptAttribute];
+ break;
+ case roles::MATHML_UNDER:
+ case roles::MATHML_OVER:
+ case roles::MATHML_UNDER_OVER:
+ [additional addObject:NSAccessibilityMathBaseAttribute];
+ [additional addObject:NSAccessibilityMathUnderAttribute];
+ [additional addObject:NSAccessibilityMathOverAttribute];
+ break;
+ // XXX bug 1176983
+ // roles::MATHML_MULTISCRIPTS should also have the following attributes:
+ // - NSAccessibilityMathPrescriptsAttribute
+ // - NSAccessibilityMathPostscriptsAttribute
+ // XXX bug 1176970
+ // roles::MATHML_FENCED should also have the following attributes:
+ // - NSAccessibilityMathFencedOpenAttribute
+ // - NSAccessibilityMathFencedCloseAttribute
+ default:
+ break;
+ }
+
+ return additional;
+}
+
+- (NSArray*)accessibilityAttributeNames
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ // if we're expired, we don't support any attributes.
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ if (!accWrap && !proxy)
+ return [NSArray array];
+
+ static NSArray* generalAttributes = nil;
+
+ if (!generalAttributes) {
+ // standard attributes that are shared and supported by all generic elements.
+ generalAttributes = [[NSArray alloc] initWithObjects: NSAccessibilityChildrenAttribute,
+ NSAccessibilityParentAttribute,
+ NSAccessibilityRoleAttribute,
+ NSAccessibilityTitleAttribute,
+ NSAccessibilityValueAttribute,
+ NSAccessibilitySubroleAttribute,
+ NSAccessibilityRoleDescriptionAttribute,
+ NSAccessibilityPositionAttribute,
+ NSAccessibilityEnabledAttribute,
+ NSAccessibilitySizeAttribute,
+ NSAccessibilityWindowAttribute,
+ NSAccessibilityFocusedAttribute,
+ NSAccessibilityHelpAttribute,
+ NSAccessibilityTitleUIElementAttribute,
+ NSAccessibilityTopLevelUIElementAttribute,
+#if DEBUG
+ @"AXMozDescription",
+#endif
+ nil];
+ }
+
+ NSArray* objectAttributes = generalAttributes;
+
+ NSArray* additionalAttributes = [self additionalAccessibilityAttributeNames];
+ if ([additionalAttributes count])
+ objectAttributes = [objectAttributes arrayByAddingObjectsFromArray:additionalAttributes];
+
+ return objectAttributes;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (id)childAt:(uint32_t)i
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ Accessible* child = accWrap->GetChildAt(i);
+ return child ? GetNativeFromGeckoAccessible(child) : nil;
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ ProxyAccessible* child = proxy->ChildAt(i);
+ return child ? GetNativeFromProxy(child) : nil;
+ }
+
+ return nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (id)accessibilityAttributeValue:(NSString*)attribute
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ if (!accWrap && !proxy)
+ return nil;
+
+#if DEBUG
+ if ([attribute isEqualToString:@"AXMozDescription"])
+ return [NSString stringWithFormat:@"role = %u native = %@", mRole, [self class]];
+#endif
+
+ if ([attribute isEqualToString:NSAccessibilityChildrenAttribute])
+ return [self children];
+ if ([attribute isEqualToString:NSAccessibilityParentAttribute])
+ return [self parent];
+
+#ifdef DEBUG_hakan
+ NSLog (@"(%@ responding to attr %@)", self, attribute);
+#endif
+
+ if ([attribute isEqualToString:NSAccessibilityRoleAttribute])
+ return [self role];
+ if ([attribute isEqualToString:NSAccessibilityPositionAttribute])
+ return [self position];
+ if ([attribute isEqualToString:NSAccessibilitySubroleAttribute])
+ return [self subrole];
+ if ([attribute isEqualToString:NSAccessibilityEnabledAttribute])
+ return [NSNumber numberWithBool:[self isEnabled]];
+ if ([attribute isEqualToString:NSAccessibilityValueAttribute])
+ return [self value];
+ if ([attribute isEqualToString:NSAccessibilityRoleDescriptionAttribute])
+ return [self roleDescription];
+ if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
+ return [NSNumber numberWithBool:[self isFocused]];
+ if ([attribute isEqualToString:NSAccessibilitySizeAttribute])
+ return [self size];
+ if ([attribute isEqualToString:NSAccessibilityWindowAttribute])
+ return [self window];
+ if ([attribute isEqualToString:NSAccessibilityTopLevelUIElementAttribute])
+ return [self window];
+ if ([attribute isEqualToString:NSAccessibilityTitleAttribute])
+ return [self title];
+ if ([attribute isEqualToString:NSAccessibilityTitleUIElementAttribute]) {
+ if (accWrap) {
+ Relation rel = accWrap->RelationByType(RelationType::LABELLED_BY);
+ Accessible* tempAcc = rel.Next();
+ return tempAcc ? GetNativeFromGeckoAccessible(tempAcc) : nil;
+ }
+ nsTArray<ProxyAccessible*> rel = proxy->RelationByType(RelationType::LABELLED_BY);
+ ProxyAccessible* tempProxy = rel.SafeElementAt(0);
+ return tempProxy ? GetNativeFromProxy(tempProxy) : nil;
+ }
+ if ([attribute isEqualToString:NSAccessibilityHelpAttribute])
+ return [self help];
+
+ switch (mRole) {
+ case roles::MATHML_ROOT:
+ if ([attribute isEqualToString:NSAccessibilityMathRootRadicandAttribute])
+ return [self childAt:0];
+ if ([attribute isEqualToString:NSAccessibilityMathRootIndexAttribute])
+ return [self childAt:1];
+ break;
+ case roles::MATHML_SQUARE_ROOT:
+ if ([attribute isEqualToString:NSAccessibilityMathRootRadicandAttribute])
+ return [self childAt:0];
+ break;
+ case roles::MATHML_FRACTION:
+ if ([attribute isEqualToString:NSAccessibilityMathFractionNumeratorAttribute])
+ return [self childAt:0];
+ if ([attribute isEqualToString:NSAccessibilityMathFractionDenominatorAttribute])
+ return [self childAt:1];
+ if ([attribute isEqualToString:NSAccessibilityMathLineThicknessAttribute]) {
+ // WebKit sets line thickness to some logical value parsed in the
+ // renderer object of the <mfrac> element. It's not clear whether the
+ // exact value is relevant to assistive technologies. From a semantic
+ // point of view, the only important point is to distinguish between
+ // <mfrac> elements that have a fraction bar and those that do not.
+ // Per the MathML 3 spec, the latter happens iff the linethickness
+ // attribute is of the form [zero-float][optional-unit]. In that case we
+ // set line thickness to zero and in the other cases we set it to one.
+ nsAutoString thickness;
+ if (accWrap) {
+ nsCOMPtr<nsIPersistentProperties> attributes = accWrap->Attributes();
+ nsAccUtils::GetAccAttr(attributes, nsGkAtoms::linethickness_, thickness);
+ } else {
+ AutoTArray<Attribute, 10> attrs;
+ proxy->Attributes(&attrs);
+ for (size_t i = 0 ; i < attrs.Length() ; i++) {
+ if (attrs.ElementAt(i).Name() == "thickness") {
+ thickness = attrs.ElementAt(i).Value();
+ break;
+ }
+ }
+ }
+ double value = 1.0;
+ if (!thickness.IsEmpty())
+ value = PR_strtod(NS_LossyConvertUTF16toASCII(thickness).get(),
+ nullptr);
+ return [NSNumber numberWithInteger:(value ? 1 : 0)];
+ }
+ break;
+ case roles::MATHML_SUB:
+ if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
+ return [self childAt:0];
+ if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute])
+ return [self childAt:1];
+#ifdef DEBUG
+ if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute])
+ return nil;
+#endif
+ break;
+ case roles::MATHML_SUP:
+ if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
+ return [self childAt:0];
+#ifdef DEBUG
+ if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute])
+ return nil;
+#endif
+ if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute])
+ return [self childAt:1];
+ break;
+ case roles::MATHML_SUB_SUP:
+ if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
+ return [self childAt:0];
+ if ([attribute isEqualToString:NSAccessibilityMathSubscriptAttribute])
+ return [self childAt:1];
+ if ([attribute isEqualToString:NSAccessibilityMathSuperscriptAttribute])
+ return [self childAt:2];
+ break;
+ case roles::MATHML_UNDER:
+ if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
+ return [self childAt:0];
+ if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute])
+ return [self childAt:1];
+#ifdef DEBUG
+ if ([attribute isEqualToString:NSAccessibilityMathOverAttribute])
+ return nil;
+#endif
+ break;
+ case roles::MATHML_OVER:
+ if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
+ return [self childAt:0];
+#ifdef DEBUG
+ if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute])
+ return nil;
+#endif
+ if ([attribute isEqualToString:NSAccessibilityMathOverAttribute])
+ return [self childAt:1];
+ break;
+ case roles::MATHML_UNDER_OVER:
+ if ([attribute isEqualToString:NSAccessibilityMathBaseAttribute])
+ return [self childAt:0];
+ if ([attribute isEqualToString:NSAccessibilityMathUnderAttribute])
+ return [self childAt:1];
+ if ([attribute isEqualToString:NSAccessibilityMathOverAttribute])
+ return [self childAt:2];
+ break;
+ // XXX bug 1176983
+ // roles::MATHML_MULTISCRIPTS should also have the following attributes:
+ // - NSAccessibilityMathPrescriptsAttribute
+ // - NSAccessibilityMathPostscriptsAttribute
+ // XXX bug 1176970
+ // roles::MATHML_FENCED should also have the following attributes:
+ // - NSAccessibilityMathFencedOpenAttribute
+ // - NSAccessibilityMathFencedCloseAttribute
+ default:
+ break;
+ }
+
+#ifdef DEBUG
+ NSLog (@"!!! %@ can't respond to attribute %@", self, attribute);
+#endif
+ return nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (BOOL)accessibilityIsAttributeSettable:(NSString*)attribute
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ if ([attribute isEqualToString:NSAccessibilityFocusedAttribute])
+ return [self canBeFocused];
+
+ return NO;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
+}
+
+- (void)accessibilitySetValue:(id)value forAttribute:(NSString*)attribute
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+#ifdef DEBUG_hakan
+ NSLog (@"[%@] %@='%@'", self, attribute, value);
+#endif
+
+ // we only support focusing elements so far.
+ if ([attribute isEqualToString:NSAccessibilityFocusedAttribute] && [value boolValue])
+ [self focus];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (id)accessibilityHitTest:(NSPoint)point
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ if (!accWrap && !proxy)
+ return nil;
+
+ // Convert the given screen-global point in the cocoa coordinate system (with
+ // origin in the bottom-left corner of the screen) into point in the Gecko
+ // coordinate system (with origin in a top-left screen point).
+ NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
+ NSPoint tmpPoint = NSMakePoint(point.x,
+ [mainView frame].size.height - point.y);
+ LayoutDeviceIntPoint geckoPoint = nsCocoaUtils::
+ CocoaPointsToDevPixels(tmpPoint, nsCocoaUtils::GetBackingScaleFactor(mainView));
+
+ mozAccessible* nativeChild = nil;
+ if (accWrap) {
+ Accessible* child = accWrap->ChildAtPoint(geckoPoint.x, geckoPoint.y,
+ Accessible::eDeepestChild);
+ if (child)
+ nativeChild = GetNativeFromGeckoAccessible(child);
+ } else if (proxy) {
+ ProxyAccessible* child = proxy->ChildAtPoint(geckoPoint.x, geckoPoint.y,
+ Accessible::eDeepestChild);
+ if (child)
+ nativeChild = GetNativeFromProxy(child);
+ }
+
+ if (nativeChild)
+ return nativeChild;
+
+ // if we didn't find anything, return ourself or child view.
+ return GetObjectOrRepresentedView(self);
+}
+
+- (NSArray*)accessibilityActionNames
+{
+ return nil;
+}
+
+- (NSString*)accessibilityActionDescription:(NSString*)action
+{
+ // by default we return whatever the MacOS API know about.
+ // if you have custom actions, override.
+ return NSAccessibilityActionDescription(action);
+}
+
+- (void)accessibilityPerformAction:(NSString*)action
+{
+}
+
+- (id)accessibilityFocusedUIElement
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+ if (!accWrap && !proxy)
+ return nil;
+
+ mozAccessible* focusedChild = nil;
+ if (accWrap) {
+ Accessible* focusedGeckoChild = accWrap->FocusedChild();
+ if (focusedGeckoChild)
+ focusedChild = GetNativeFromGeckoAccessible(focusedGeckoChild);
+ } else if (proxy) {
+ ProxyAccessible* focusedGeckoChild = proxy->FocusedChild();
+ if (focusedGeckoChild)
+ focusedChild = GetNativeFromProxy(focusedGeckoChild);
+ }
+
+ if (focusedChild)
+ return GetObjectOrRepresentedView(focusedChild);
+
+ // return ourself if we can't get a native focused child.
+ return GetObjectOrRepresentedView(self);
+}
+
+#pragma mark -
+
+- (id <mozAccessible>)parent
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ id nativeParent = nil;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ Accessible* accessibleParent = accWrap->Parent();
+ if (accessibleParent)
+ nativeParent = GetNativeFromGeckoAccessible(accessibleParent);
+ if (nativeParent)
+ return GetObjectOrRepresentedView(nativeParent);
+
+ // Return native of root accessible if we have no direct parent
+ nativeParent = GetNativeFromGeckoAccessible(accWrap->RootAccessible());
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ if (ProxyAccessible* proxyParent = proxy->Parent()) {
+ nativeParent = GetNativeFromProxy(proxyParent);
+ }
+
+ if (nativeParent)
+ return GetObjectOrRepresentedView(nativeParent);
+
+ Accessible* outerDoc = proxy->OuterDocOfRemoteBrowser();
+ nativeParent = outerDoc ?
+ GetNativeFromGeckoAccessible(outerDoc) : nil;
+ } else {
+ return nil;
+ }
+
+ NSAssert1 (nativeParent, @"!!! we can't find a parent for %@", self);
+
+ return GetObjectOrRepresentedView(nativeParent);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (BOOL)hasRepresentedView
+{
+ return NO;
+}
+
+- (id)representedView
+{
+ return nil;
+}
+
+- (BOOL)isRoot
+{
+ return NO;
+}
+
+// gets our native children lazily.
+// returns nil when there are no children.
+- (NSArray*)children
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ if (mChildren)
+ return mChildren;
+
+ // get the array of children.
+ mChildren = [[NSMutableArray alloc] init];
+
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ if (accWrap) {
+ uint32_t childCount = accWrap->ChildCount();
+ for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
+ mozAccessible* nativeChild = GetNativeFromGeckoAccessible(accWrap->GetChildAt(childIdx));
+ if (nativeChild)
+ [mChildren addObject:nativeChild];
+ }
+
+ // children from child if this is an outerdoc
+ OuterDocAccessible* docOwner = accWrap->AsOuterDoc();
+ if (docOwner) {
+ if (ProxyAccessible* proxyDoc = docOwner->RemoteChildDoc()) {
+ mozAccessible* nativeRemoteChild = GetNativeFromProxy(proxyDoc);
+ [mChildren insertObject:nativeRemoteChild atIndex:0];
+ NSAssert1 (nativeRemoteChild, @"%@ found a child remote doc missing a native\n", self);
+ }
+ }
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ uint32_t childCount = proxy->ChildrenCount();
+ for (uint32_t childIdx = 0; childIdx < childCount; childIdx++) {
+ mozAccessible* nativeChild = GetNativeFromProxy(proxy->ChildAt(childIdx));
+ if (nativeChild)
+ [mChildren addObject:nativeChild];
+ }
+
+ }
+
+ return mChildren;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (NSValue*)position
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ nsIntRect rect;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ rect = accWrap->Bounds();
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ rect = proxy->Bounds();
+ else
+ return nil;
+
+ NSScreen* mainView = [[NSScreen screens] objectAtIndex:0];
+ CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mainView);
+ NSPoint p = NSMakePoint(static_cast<CGFloat>(rect.x) / scaleFactor,
+ [mainView frame].size.height - static_cast<CGFloat>(rect.y + rect.height) / scaleFactor);
+
+ return [NSValue valueWithPoint:p];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (NSValue*)size
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ nsIntRect rect;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ rect = accWrap->Bounds();
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ rect = proxy->Bounds();
+ else
+ return nil;
+
+ CGFloat scaleFactor =
+ nsCocoaUtils::GetBackingScaleFactor([[NSScreen screens] objectAtIndex:0]);
+ return [NSValue valueWithSize:NSMakeSize(static_cast<CGFloat>(rect.width) / scaleFactor,
+ static_cast<CGFloat>(rect.height) / scaleFactor)];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (NSString*)role
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ if (accWrap) {
+ #ifdef DEBUG_A11Y
+ NS_ASSERTION(nsAccUtils::IsTextInterfaceSupportCorrect(accWrap),
+ "Does not support Text when it should");
+ #endif
+ } else if (![self getProxyAccessible]) {
+ return nil;
+ }
+
+#define ROLE(geckoRole, stringRole, atkRole, macRole, msaaRole, ia2Role, nameRule) \
+ case roles::geckoRole: \
+ return macRole;
+
+ switch (mRole) {
+#include "RoleMap.h"
+ default:
+ NS_NOTREACHED("Unknown role.");
+ return NSAccessibilityUnknownRole;
+ }
+
+#undef ROLE
+}
+
+- (NSString*)subrole
+{
+ AccessibleWrap* accWrap = [self getGeckoAccessible];
+ ProxyAccessible* proxy = [self getProxyAccessible];
+
+ // Deal with landmarks first
+ nsIAtom* landmark = nullptr;
+ if (accWrap)
+ landmark = accWrap->LandmarkRole();
+ else if (proxy)
+ landmark = proxy->LandmarkRole();
+
+ if (landmark) {
+ if (landmark == nsGkAtoms::application)
+ return @"AXLandmarkApplication";
+ if (landmark == nsGkAtoms::banner)
+ return @"AXLandmarkBanner";
+ if (landmark == nsGkAtoms::complementary)
+ return @"AXLandmarkComplementary";
+ if (landmark == nsGkAtoms::contentinfo)
+ return @"AXLandmarkContentInfo";
+ if (landmark == nsGkAtoms::form)
+ return @"AXLandmarkForm";
+ if (landmark == nsGkAtoms::main)
+ return @"AXLandmarkMain";
+ if (landmark == nsGkAtoms::navigation)
+ return @"AXLandmarkNavigation";
+ if (landmark == nsGkAtoms::search)
+ return @"AXLandmarkSearch";
+ if (landmark == nsGkAtoms::searchbox)
+ return @"AXSearchField";
+ }
+
+ // Now, deal with widget roles
+ nsIAtom* roleAtom = nullptr;
+ if (accWrap && accWrap->HasARIARole()) {
+ const nsRoleMapEntry* roleMap = accWrap->ARIARoleMap();
+ roleAtom = *roleMap->roleAtom;
+ }
+ if (proxy)
+ roleAtom = proxy->ARIARoleAtom();
+
+ if (roleAtom) {
+ if (roleAtom == nsGkAtoms::alert)
+ return @"AXApplicationAlert";
+ if (roleAtom == nsGkAtoms::alertdialog)
+ return @"AXApplicationAlertDialog";
+ if (roleAtom == nsGkAtoms::article)
+ return @"AXDocumentArticle";
+ if (roleAtom == nsGkAtoms::dialog)
+ return @"AXApplicationDialog";
+ if (roleAtom == nsGkAtoms::document)
+ return @"AXDocument";
+ if (roleAtom == nsGkAtoms::log_)
+ return @"AXApplicationLog";
+ if (roleAtom == nsGkAtoms::math)
+ return @"AXDocumentMath";
+ if (roleAtom == nsGkAtoms::note_)
+ return @"AXDocumentNote";
+ if (roleAtom == nsGkAtoms::region)
+ return @"AXDocumentRegion";
+ if (roleAtom == nsGkAtoms::status)
+ return @"AXApplicationStatus";
+ if (roleAtom == nsGkAtoms::tabpanel)
+ return @"AXTabPanel";
+ if (roleAtom == nsGkAtoms::timer)
+ return @"AXApplicationTimer";
+ if (roleAtom == nsGkAtoms::tooltip)
+ return @"AXUserInterfaceTooltip";
+ }
+
+ switch (mRole) {
+ case roles::LIST:
+ return @"AXContentList"; // 10.6+ NSAccessibilityContentListSubrole;
+
+ case roles::ENTRY:
+ if ((accWrap && accWrap->IsSearchbox()) ||
+ (proxy && proxy->IsSearchbox()))
+ return @"AXSearchField";
+ break;
+
+ case roles::DEFINITION_LIST:
+ return @"AXDefinitionList"; // 10.6+ NSAccessibilityDefinitionListSubrole;
+
+ case roles::TERM:
+ return @"AXTerm";
+
+ case roles::DEFINITION:
+ return @"AXDefinition";
+
+ case roles::MATHML_MATH:
+ return @"AXDocumentMath";
+
+ case roles::MATHML_FRACTION:
+ return @"AXMathFraction";
+
+ case roles::MATHML_FENCED:
+ // XXX bug 1176970
+ // This should be AXMathFence, but doing so without implementing the
+ // whole fence interface seems to make VoiceOver crash, so we present it
+ // as a row for now.
+ return @"AXMathRow";
+
+ case roles::MATHML_SUB:
+ case roles::MATHML_SUP:
+ case roles::MATHML_SUB_SUP:
+ return @"AXMathSubscriptSuperscript";
+
+ case roles::MATHML_ROW:
+ case roles::MATHML_STYLE:
+ case roles::MATHML_ERROR:
+ return @"AXMathRow";
+
+ case roles::MATHML_UNDER:
+ case roles::MATHML_OVER:
+ case roles::MATHML_UNDER_OVER:
+ return @"AXMathUnderOver";
+
+ case roles::MATHML_SQUARE_ROOT:
+ return @"AXMathSquareRoot";
+
+ case roles::MATHML_ROOT:
+ return @"AXMathRoot";
+
+ case roles::MATHML_TEXT:
+ return @"AXMathText";
+
+ case roles::MATHML_NUMBER:
+ return @"AXMathNumber";
+
+ case roles::MATHML_IDENTIFIER:
+ return @"AXMathIdentifier";
+
+ case roles::MATHML_TABLE:
+ return @"AXMathTable";
+
+ case roles::MATHML_TABLE_ROW:
+ return @"AXMathTableRow";
+
+ case roles::MATHML_CELL:
+ return @"AXMathTableCell";
+
+ // XXX: NSAccessibility also uses subroles AXMathSeparatorOperator and
+ // AXMathFenceOperator. We should use the NS_MATHML_OPERATOR_FENCE and
+ // NS_MATHML_OPERATOR_SEPARATOR bits of nsOperatorFlags, but currently they
+ // are only available from the MathML layout code. Hence we just fallback
+ // to subrole AXMathOperator for now.
+ // XXX bug 1175747 WebKit also creates anonymous operators for <mfenced>
+ // which have subroles AXMathSeparatorOperator and AXMathFenceOperator.
+ case roles::MATHML_OPERATOR:
+ return @"AXMathOperator";
+
+ case roles::MATHML_MULTISCRIPTS:
+ return @"AXMathMultiscript";
+
+ case roles::SWITCH:
+ return @"AXSwitch";
+
+ case roles::ALERT:
+ return @"AXApplicationAlert";
+
+ case roles::SEPARATOR:
+ return @"AXContentSeparator";
+
+ case roles::PROPERTYPAGE:
+ return @"AXTabPanel";
+
+ case roles::DETAILS:
+ return @"AXDetails";
+
+ case roles::SUMMARY:
+ return @"AXSummary";
+
+ default:
+ break;
+ }
+
+ return nil;
+}
+
+struct RoleDescrMap
+{
+ NSString* role;
+ const nsString description;
+};
+
+static const RoleDescrMap sRoleDescrMap[] = {
+ { @"AXApplicationAlert", NS_LITERAL_STRING("alert") },
+ { @"AXApplicationAlertDialog", NS_LITERAL_STRING("alertDialog") },
+ { @"AXApplicationLog", NS_LITERAL_STRING("log") },
+ { @"AXApplicationStatus", NS_LITERAL_STRING("status") },
+ { @"AXApplicationTimer", NS_LITERAL_STRING("timer") },
+ { @"AXContentSeparator", NS_LITERAL_STRING("separator") },
+ { @"AXDefinition", NS_LITERAL_STRING("definition") },
+ { @"AXDocument", NS_LITERAL_STRING("document") },
+ { @"AXDocumentArticle", NS_LITERAL_STRING("article") },
+ { @"AXDocumentMath", NS_LITERAL_STRING("math") },
+ { @"AXDocumentNote", NS_LITERAL_STRING("note") },
+ { @"AXDocumentRegion", NS_LITERAL_STRING("region") },
+ { @"AXLandmarkApplication", NS_LITERAL_STRING("application") },
+ { @"AXLandmarkBanner", NS_LITERAL_STRING("banner") },
+ { @"AXLandmarkComplementary", NS_LITERAL_STRING("complementary") },
+ { @"AXLandmarkContentInfo", NS_LITERAL_STRING("content") },
+ { @"AXLandmarkMain", NS_LITERAL_STRING("main") },
+ { @"AXLandmarkNavigation", NS_LITERAL_STRING("navigation") },
+ { @"AXLandmarkSearch", NS_LITERAL_STRING("search") },
+ { @"AXSearchField", NS_LITERAL_STRING("searchTextField") },
+ { @"AXTabPanel", NS_LITERAL_STRING("tabPanel") },
+ { @"AXTerm", NS_LITERAL_STRING("term") },
+ { @"AXUserInterfaceTooltip", NS_LITERAL_STRING("tooltip") }
+};
+
+struct RoleDescrComparator
+{
+ const NSString* mRole;
+ explicit RoleDescrComparator(const NSString* aRole) : mRole(aRole) {}
+ int operator()(const RoleDescrMap& aEntry) const {
+ return [mRole compare:aEntry.role];
+ }
+};
+
+- (NSString*)roleDescription
+{
+ if (mRole == roles::DOCUMENT)
+ return utils::LocalizedString(NS_LITERAL_STRING("htmlContent"));
+
+ NSString* subrole = [self subrole];
+
+ if (subrole) {
+ size_t idx = 0;
+ if (BinarySearchIf(sRoleDescrMap, 0, ArrayLength(sRoleDescrMap),
+ RoleDescrComparator(subrole), &idx)) {
+ return utils::LocalizedString(sRoleDescrMap[idx].description);
+ }
+ }
+
+ return NSAccessibilityRoleDescription([self role], subrole);
+}
+
+- (NSString*)title
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ nsAutoString title;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ accWrap->Name(title);
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ proxy->Name(title);
+
+ return nsCocoaUtils::ToNSString(title);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (id)value
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ nsAutoString value;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ accWrap->Value(value);
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ proxy->Value(value);
+
+ return nsCocoaUtils::ToNSString(value);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (void)valueDidChange
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+#ifdef DEBUG_hakan
+ NSLog(@"%@'s value changed!", self);
+#endif
+ // sending out a notification is expensive, so we don't do it other than for really important objects,
+ // like mozTextAccessible.
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void)selectedTextDidChange
+{
+ // Do nothing. mozTextAccessible will.
+}
+
+- (void)documentLoadComplete
+{
+ id realSelf = GetObjectOrRepresentedView(self);
+ NSAccessibilityPostNotification(realSelf, NSAccessibilityFocusedUIElementChangedNotification);
+ NSAccessibilityPostNotification(realSelf, @"AXLoadComplete");
+ NSAccessibilityPostNotification(realSelf, @"AXLayoutComplete");
+}
+
+- (NSString*)help
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ // What needs to go here is actually the accDescription of an item.
+ // The MSAA acc_help method has nothing to do with this one.
+ nsAutoString helpText;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ accWrap->Description(helpText);
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ proxy->Description(helpText);
+
+ return nsCocoaUtils::ToNSString(helpText);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+// objc-style description (from NSObject); not to be confused with the accessible description above.
+- (NSString*)description
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ return [NSString stringWithFormat:@"(%p) %@", self, [self role]];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (BOOL)isFocused
+{
+ return FocusMgr()->IsFocused([self getGeckoAccessible]);
+}
+
+- (BOOL)canBeFocused
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ return accWrap->InteractiveState() & states::FOCUSABLE;
+
+ if (ProxyAccessible* proxy = [self getProxyAccessible])
+ return proxy->State() & states::FOCUSABLE;
+
+ return false;
+}
+
+- (BOOL)focus
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ accWrap->TakeFocus();
+ else if (ProxyAccessible* proxy = [self getProxyAccessible])
+ proxy->TakeFocus();
+ else
+ return NO;
+
+ return YES;
+}
+
+- (BOOL)isEnabled
+{
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible])
+ return ((accWrap->InteractiveState() & states::UNAVAILABLE) == 0);
+
+ if (ProxyAccessible* proxy = [self getProxyAccessible])
+ return ((proxy->State() & states::UNAVAILABLE) == 0);
+
+ return false;
+}
+
+// The root accessible calls this when the focused node was
+// changed to us.
+- (void)didReceiveFocus
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+#ifdef DEBUG_hakan
+ NSLog (@"%@ received focus!", self);
+#endif
+ NSAccessibilityPostNotification(GetObjectOrRepresentedView(self),
+ NSAccessibilityFocusedUIElementChangedNotification);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (NSWindow*)window
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ // Get a pointer to the native window (NSWindow) we reside in.
+ NSWindow *nativeWindow = nil;
+ DocAccessible* docAcc = nullptr;
+ if (AccessibleWrap* accWrap = [self getGeckoAccessible]) {
+ docAcc = accWrap->Document();
+ } else if (ProxyAccessible* proxy = [self getProxyAccessible]) {
+ Accessible* outerDoc = proxy->OuterDocOfRemoteBrowser();
+ if (outerDoc)
+ docAcc = outerDoc->Document();
+ }
+
+ if (docAcc)
+ nativeWindow = static_cast<NSWindow*>(docAcc->GetNativeWindow());
+
+ NSAssert1(nativeWindow, @"Could not get native window for %@", self);
+ return nativeWindow;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+- (void)invalidateChildren
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ // make room for new children
+ [mChildren release];
+ mChildren = nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void)appendChild:(Accessible*)aAccessible
+{
+ // if mChildren is nil, then we don't even need to bother
+ if (!mChildren)
+ return;
+
+ mozAccessible *curNative = GetNativeFromGeckoAccessible(aAccessible);
+ if (curNative)
+ [mChildren addObject:curNative];
+}
+
+- (void)expire
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [self invalidateChildren];
+
+ mGeckoAccessible = 0;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (BOOL)isExpired
+{
+ return ![self getGeckoAccessible] && ![self getProxyAccessible];
+}
+
+#pragma mark -
+#pragma mark Debug methods
+#pragma mark -
+
+#ifdef DEBUG
+
+// will check that our children actually reference us as their
+// parent.
+- (void)sanityCheckChildren:(NSArray *)children
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ NSEnumerator *iter = [children objectEnumerator];
+ mozAccessible *curObj = nil;
+
+ NSLog(@"sanity checking %@", self);
+
+ while ((curObj = [iter nextObject])) {
+ id realSelf = GetObjectOrRepresentedView(self);
+ NSLog(@"checking %@", realSelf);
+ NSAssert2([curObj parent] == realSelf,
+ @"!!! %@ not returning %@ as AXParent, even though it is a AXChild of it!", curObj, realSelf);
+ }
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void)sanityCheckChildren
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [self sanityCheckChildren:[self children]];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void)printHierarchy
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [self printHierarchyWithLevel:0];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+- (void)printHierarchyWithLevel:(unsigned)level
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ NSAssert(![self isExpired], @"!!! trying to print hierarchy of expired object!");
+
+ // print this node
+ NSMutableString *indent = [NSMutableString stringWithCapacity:level];
+ unsigned i=0;
+ for (;i<level;i++)
+ [indent appendString:@" "];
+
+ NSLog (@"%@(#%i) %@", indent, level, self);
+
+ // use |children| method to make sure our children are lazily fetched first.
+ NSArray *children = [self children];
+ if (!children)
+ return;
+
+ [self sanityCheckChildren];
+
+ NSEnumerator *iter = [children objectEnumerator];
+ mozAccessible *object = nil;
+
+ while (iter && (object = [iter nextObject]))
+ // print every child node's subtree, increasing the indenting
+ // by two for every level.
+ [object printHierarchyWithLevel:(level+1)];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+#endif /* DEBUG */
+
+@end
diff --git a/accessible/mac/mozAccessibleProtocol.h b/accessible/mac/mozAccessibleProtocol.h
new file mode 100644
index 0000000000..5f67b1dcf2
--- /dev/null
+++ b/accessible/mac/mozAccessibleProtocol.h
@@ -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
+
diff --git a/accessible/mac/mozActionElements.h b/accessible/mac/mozActionElements.h
new file mode 100644
index 0000000000..a325921eb8
--- /dev/null
+++ b/accessible/mac/mozActionElements.h
@@ -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
diff --git a/accessible/mac/mozActionElements.mm b/accessible/mac/mozActionElements.mm
new file mode 100644
index 0000000000..5decd6cccc
--- /dev/null
+++ b/accessible/mac/mozActionElements.mm
@@ -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
diff --git a/accessible/mac/mozDocAccessible.h b/accessible/mac/mozDocAccessible.h
new file mode 100644
index 0000000000..c381773110
--- /dev/null
+++ b/accessible/mac/mozDocAccessible.h
@@ -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
diff --git a/accessible/mac/mozDocAccessible.mm b/accessible/mac/mozDocAccessible.mm
new file mode 100644
index 0000000000..4bae81f01c
--- /dev/null
+++ b/accessible/mac/mozDocAccessible.mm
@@ -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
diff --git a/accessible/mac/mozHTMLAccessible.h b/accessible/mac/mozHTMLAccessible.h
new file mode 100644
index 0000000000..c70a3c2a25
--- /dev/null
+++ b/accessible/mac/mozHTMLAccessible.h
@@ -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
diff --git a/accessible/mac/mozHTMLAccessible.mm b/accessible/mac/mozHTMLAccessible.mm
new file mode 100644
index 0000000000..2079a4aa6b
--- /dev/null
+++ b/accessible/mac/mozHTMLAccessible.mm
@@ -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
diff --git a/accessible/mac/mozTableAccessible.h b/accessible/mac/mozTableAccessible.h
new file mode 100644
index 0000000000..435b5adc57
--- /dev/null
+++ b/accessible/mac/mozTableAccessible.h
@@ -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
diff --git a/accessible/mac/mozTableAccessible.mm b/accessible/mac/mozTableAccessible.mm
new file mode 100644
index 0000000000..6ad157b9f0
--- /dev/null
+++ b/accessible/mac/mozTableAccessible.mm
@@ -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
diff --git a/accessible/mac/mozTextAccessible.h b/accessible/mac/mozTextAccessible.h
new file mode 100644
index 0000000000..8bc23ae8d5
--- /dev/null
+++ b/accessible/mac/mozTextAccessible.h
@@ -0,0 +1,17 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#import "mozAccessible.h"
+
+#import "HyperTextAccessible.h"
+
+@interface mozTextAccessible : mozAccessible
+{
+}
+@end
+
+@interface mozTextLeafAccessible : mozAccessible
+{
+}
+@end
diff --git a/accessible/mac/mozTextAccessible.mm b/accessible/mac/mozTextAccessible.mm
new file mode 100644
index 0000000000..1f433b802e
--- /dev/null
+++ b/accessible/mac/mozTextAccessible.mm
@@ -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
diff --git a/accessible/moz.build b/accessible/moz.build
index edfd88f504..aa1cccb982 100644
--- a/accessible/moz.build
+++ b/accessible/moz.build
@@ -9,6 +9,8 @@ if 'gtk' in toolkit:
DIRS += ['atk']
elif toolkit == 'windows':
DIRS += ['windows']
+elif toolkit == 'cocoa':
+ DIRS += ['mac']
else:
DIRS += ['other']
diff --git a/accessible/xpcom/moz.build b/accessible/xpcom/moz.build
index 5d1ad16884..d43efd06d0 100644
--- a/accessible/xpcom/moz.build
+++ b/accessible/xpcom/moz.build
@@ -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',
diff --git a/accessible/xul/moz.build b/accessible/xul/moz.build
index b04c35dc40..54182c0975 100644
--- a/accessible/xul/moz.build
+++ b/accessible/xul/moz.build
@@ -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',
diff --git a/db/sqlite3/src/moz.build b/db/sqlite3/src/moz.build
index 327e77677a..1ffc326f8a 100644
--- a/db/sqlite3/src/moz.build
+++ b/db/sqlite3/src/moz.build
@@ -45,6 +45,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
DEFINES['SQLITE_WIN32_GETVERSIONEX'] = 0
DEFINES['SQLITE_ALLOW_URI_AUTHORITY'] = 1
+# -DSQLITE_ENABLE_LOCKING_STYLE=1 to help with AFP folders
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ DEFINES['SQLITE_ENABLE_LOCKING_STYLE'] = 1
+
# sqlite defaults this to on on __APPLE_ but it breaks on newer iOS SDKs
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
DEFINES['SQLITE_ENABLE_LOCKING_STYLE'] = 0
diff --git a/embedding/components/build/moz.build b/embedding/components/build/moz.build
index c66f0c79ee..361fd8768a 100644
--- a/embedding/components/build/moz.build
+++ b/embedding/components/build/moz.build
@@ -22,6 +22,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
LOCAL_INCLUDES += [
'../printingui/win',
]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ DEFINES['PROXY_PRINTING'] = 1
+ LOCAL_INCLUDES += [
+ '../printingui/mac',
+ ]
if CONFIG['MOZ_PDF_PRINTING']:
DEFINES['PROXY_PRINTING'] = 1
diff --git a/embedding/components/printingui/mac/moz.build b/embedding/components/printingui/mac/moz.build
new file mode 100644
index 0000000000..c2fe2f4731
--- /dev/null
+++ b/embedding/components/printingui/mac/moz.build
@@ -0,0 +1,15 @@
+# -*- 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/.
+
+UNIFIED_SOURCES += [
+ 'nsPrintProgress.cpp',
+ 'nsPrintProgressParams.cpp',
+]
+
+SOURCES += [
+ 'nsPrintingPromptServiceX.mm',
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/embedding/components/printingui/mac/nsPrintProgress.cpp b/embedding/components/printingui/mac/nsPrintProgress.cpp
new file mode 100644
index 0000000000..0ae0a63d9a
--- /dev/null
+++ b/embedding/components/printingui/mac/nsPrintProgress.cpp
@@ -0,0 +1,213 @@
+/* -*- 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 "nsPrintProgress.h"
+
+#include "nsIBaseWindow.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIComponentManager.h"
+#include "nsPIDOMWindow.h"
+
+NS_IMPL_ADDREF(nsPrintProgress)
+NS_IMPL_RELEASE(nsPrintProgress)
+
+NS_INTERFACE_MAP_BEGIN(nsPrintProgress)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIPrintStatusFeedback)
+ NS_INTERFACE_MAP_ENTRY(nsIPrintProgress)
+ NS_INTERFACE_MAP_ENTRY(nsIPrintStatusFeedback)
+ NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
+NS_INTERFACE_MAP_END_THREADSAFE
+
+
+nsPrintProgress::nsPrintProgress()
+{
+ m_closeProgress = false;
+ m_processCanceled = false;
+ m_pendingStateFlags = -1;
+ m_pendingStateValue = NS_OK;
+}
+
+nsPrintProgress::~nsPrintProgress()
+{
+ (void)ReleaseListeners();
+}
+
+NS_IMETHODIMP nsPrintProgress::OpenProgressDialog(mozIDOMWindowProxy *parent,
+ const char *dialogURL,
+ nsISupports *parameters,
+ nsIObserver *openDialogObserver,
+ bool *notifyOnOpen)
+{
+ MOZ_ASSERT_UNREACHABLE("The nsPrintingPromptService::ShowProgress "
+ "implementation for OS X returns "
+ "NS_ERROR_NOT_IMPLEMENTED, so we should never get "
+ "here.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsPrintProgress::CloseProgressDialog(bool forceClose)
+{
+ MOZ_ASSERT_UNREACHABLE("The nsPrintingPromptService::ShowProgress "
+ "implementation for OS X returns "
+ "NS_ERROR_NOT_IMPLEMENTED, so we should never get "
+ "here.");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsPrintProgress::GetPrompter(nsIPrompt **_retval)
+{
+ NS_ENSURE_ARG_POINTER(_retval);
+ *_retval = nullptr;
+
+ if (! m_closeProgress && m_dialog) {
+ nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryInterface(m_dialog);
+ MOZ_ASSERT(window);
+ return window->GetPrompter(_retval);
+ }
+
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP nsPrintProgress::GetProcessCanceledByUser(bool *aProcessCanceledByUser)
+{
+ NS_ENSURE_ARG_POINTER(aProcessCanceledByUser);
+ *aProcessCanceledByUser = m_processCanceled;
+ return NS_OK;
+}
+NS_IMETHODIMP nsPrintProgress::SetProcessCanceledByUser(bool aProcessCanceledByUser)
+{
+ m_processCanceled = aProcessCanceledByUser;
+ OnStateChange(nullptr, nullptr, nsIWebProgressListener::STATE_STOP, NS_OK);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgress::RegisterListener(nsIWebProgressListener * listener)
+{
+ if (!listener) //Nothing to do with a null listener!
+ return NS_OK;
+
+ m_listenerList.AppendObject(listener);
+ if (m_closeProgress || m_processCanceled)
+ listener->OnStateChange(nullptr, nullptr,
+ nsIWebProgressListener::STATE_STOP, NS_OK);
+ else
+ {
+ listener->OnStatusChange(nullptr, nullptr, NS_OK, m_pendingStatus.get());
+ if (m_pendingStateFlags != -1)
+ listener->OnStateChange(nullptr, nullptr, m_pendingStateFlags, m_pendingStateValue);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgress::UnregisterListener(nsIWebProgressListener *listener)
+{
+ if (listener)
+ m_listenerList.RemoveObject(listener);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgress::DoneIniting()
+{
+ if (m_observer) {
+ m_observer->Observe(nullptr, nullptr, nullptr);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgress::OnStateChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t aStateFlags, nsresult aStatus)
+{
+ m_pendingStateFlags = aStateFlags;
+ m_pendingStateValue = aStatus;
+
+ uint32_t count = m_listenerList.Count();
+ for (uint32_t i = count - 1; i < count; i --)
+ {
+ nsCOMPtr<nsIWebProgressListener> progressListener = m_listenerList.SafeObjectAt(i);
+ if (progressListener)
+ progressListener->OnStateChange(aWebProgress, aRequest, aStateFlags, aStatus);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgress::OnProgressChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, int32_t aCurSelfProgress, int32_t aMaxSelfProgress, int32_t aCurTotalProgress, int32_t aMaxTotalProgress)
+{
+ uint32_t count = m_listenerList.Count();
+ for (uint32_t i = count - 1; i < count; i --)
+ {
+ nsCOMPtr<nsIWebProgressListener> progressListener = m_listenerList.SafeObjectAt(i);
+ if (progressListener)
+ progressListener->OnProgressChange(aWebProgress, aRequest, aCurSelfProgress, aMaxSelfProgress, aCurTotalProgress, aMaxTotalProgress);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgress::OnLocationChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsIURI *location, uint32_t aFlags)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgress::OnStatusChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsresult aStatus, const char16_t *aMessage)
+{
+ if (aMessage && *aMessage)
+ m_pendingStatus = aMessage;
+
+ uint32_t count = m_listenerList.Count();
+ for (uint32_t i = count - 1; i < count; i --)
+ {
+ nsCOMPtr<nsIWebProgressListener> progressListener = m_listenerList.SafeObjectAt(i);
+ if (progressListener)
+ progressListener->OnStatusChange(aWebProgress, aRequest, aStatus, aMessage);
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgress::OnSecurityChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t state)
+{
+ return NS_OK;
+}
+
+nsresult nsPrintProgress::ReleaseListeners()
+{
+ m_listenerList.Clear();
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgress::ShowStatusString(const char16_t *status)
+{
+ return OnStatusChange(nullptr, nullptr, NS_OK, status);
+}
+
+NS_IMETHODIMP nsPrintProgress::StartMeteors()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsPrintProgress::StopMeteors()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsPrintProgress::ShowProgress(int32_t percent)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsPrintProgress::SetDocShell(nsIDocShell *shell,
+ mozIDOMWindowProxy *window)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP nsPrintProgress::CloseWindow()
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
diff --git a/embedding/components/printingui/mac/nsPrintProgress.h b/embedding/components/printingui/mac/nsPrintProgress.h
new file mode 100644
index 0000000000..938558c3e1
--- /dev/null
+++ b/embedding/components/printingui/mac/nsPrintProgress.h
@@ -0,0 +1,44 @@
+/* -*- 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 __nsPrintProgress_h
+#define __nsPrintProgress_h
+
+#include "nsIPrintProgress.h"
+
+#include "nsCOMArray.h"
+#include "nsCOMPtr.h"
+#include "nsIPrintStatusFeedback.h"
+#include "nsIObserver.h"
+#include "nsString.h"
+
+class nsPrintProgress : public nsIPrintProgress, public nsIPrintStatusFeedback
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIPRINTPROGRESS
+ NS_DECL_NSIWEBPROGRESSLISTENER
+ NS_DECL_NSIPRINTSTATUSFEEDBACK
+
+ nsPrintProgress();
+
+protected:
+ virtual ~nsPrintProgress();
+
+private:
+ nsresult ReleaseListeners();
+
+ bool m_closeProgress;
+ bool m_processCanceled;
+ nsString m_pendingStatus;
+ int32_t m_pendingStateFlags;
+ nsresult m_pendingStateValue;
+ // XXX This member is read-only.
+ nsCOMPtr<mozIDOMWindowProxy> m_dialog;
+ nsCOMArray<nsIWebProgressListener> m_listenerList;
+ nsCOMPtr<nsIObserver> m_observer;
+};
+
+#endif
diff --git a/embedding/components/printingui/mac/nsPrintProgressParams.cpp b/embedding/components/printingui/mac/nsPrintProgressParams.cpp
new file mode 100644
index 0000000000..eba86b2987
--- /dev/null
+++ b/embedding/components/printingui/mac/nsPrintProgressParams.cpp
@@ -0,0 +1,47 @@
+/* -*- 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 "nsPrintProgressParams.h"
+#include "nsReadableUtils.h"
+
+
+NS_IMPL_ISUPPORTS(nsPrintProgressParams, nsIPrintProgressParams)
+
+nsPrintProgressParams::nsPrintProgressParams()
+{
+}
+
+nsPrintProgressParams::~nsPrintProgressParams()
+{
+}
+
+NS_IMETHODIMP nsPrintProgressParams::GetDocTitle(char16_t * *aDocTitle)
+{
+ NS_ENSURE_ARG(aDocTitle);
+
+ *aDocTitle = ToNewUnicode(mDocTitle);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgressParams::SetDocTitle(const char16_t * aDocTitle)
+{
+ mDocTitle = aDocTitle;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgressParams::GetDocURL(char16_t * *aDocURL)
+{
+ NS_ENSURE_ARG(aDocURL);
+
+ *aDocURL = ToNewUnicode(mDocURL);
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsPrintProgressParams::SetDocURL(const char16_t * aDocURL)
+{
+ mDocURL = aDocURL;
+ return NS_OK;
+}
+
diff --git a/embedding/components/printingui/mac/nsPrintProgressParams.h b/embedding/components/printingui/mac/nsPrintProgressParams.h
new file mode 100644
index 0000000000..65c00d98f0
--- /dev/null
+++ b/embedding/components/printingui/mac/nsPrintProgressParams.h
@@ -0,0 +1,28 @@
+/* -*- 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 __nsPrintProgressParams_h
+#define __nsPrintProgressParams_h
+
+#include "nsIPrintProgressParams.h"
+#include "nsString.h"
+
+class nsPrintProgressParams : public nsIPrintProgressParams
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIPRINTPROGRESSPARAMS
+
+ nsPrintProgressParams();
+
+protected:
+ virtual ~nsPrintProgressParams();
+
+private:
+ nsString mDocTitle;
+ nsString mDocURL;
+};
+
+#endif
diff --git a/embedding/components/printingui/mac/nsPrintingPromptService.h b/embedding/components/printingui/mac/nsPrintingPromptService.h
new file mode 100644
index 0000000000..0334db2230
--- /dev/null
+++ b/embedding/components/printingui/mac/nsPrintingPromptService.h
@@ -0,0 +1,43 @@
+/* -*- 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 __nsPrintingPromptService_h
+#define __nsPrintingPromptService_h
+
+// {E042570C-62DE-4bb6-A6E0-798E3C07B4DF}
+#define NS_PRINTINGPROMPTSERVICE_CID \
+ {0xe042570c, 0x62de, 0x4bb6, { 0xa6, 0xe0, 0x79, 0x8e, 0x3c, 0x7, 0xb4, 0xdf}}
+#define NS_PRINTINGPROMPTSERVICE_CONTRACTID \
+ "@mozilla.org/embedcomp/printingprompt-service;1"
+
+#include "nsCOMPtr.h"
+#include "nsIPrintingPromptService.h"
+#include "nsPIPromptService.h"
+#include "nsIWindowWatcher.h"
+
+// Printing Progress Includes
+#include "nsPrintProgress.h"
+#include "nsIWebProgressListener.h"
+
+class nsPrintingPromptService: public nsIPrintingPromptService,
+ public nsIWebProgressListener
+{
+public:
+ nsPrintingPromptService();
+
+ nsresult Init();
+
+ NS_DECL_NSIPRINTINGPROMPTSERVICE
+ NS_DECL_NSIWEBPROGRESSLISTENER
+ NS_DECL_ISUPPORTS
+
+protected:
+ virtual ~nsPrintingPromptService();
+
+private:
+ nsCOMPtr<nsIPrintProgress> mPrintProgress;
+};
+
+#endif
diff --git a/embedding/components/printingui/mac/nsPrintingPromptServiceX.mm b/embedding/components/printingui/mac/nsPrintingPromptServiceX.mm
new file mode 100644
index 0000000000..3b956100be
--- /dev/null
+++ b/embedding/components/printingui/mac/nsPrintingPromptServiceX.mm
@@ -0,0 +1,128 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nsPrintingPromptService.h"
+
+#include "nsCOMPtr.h"
+#include "nsServiceManagerUtils.h"
+#include "nsObjCExceptions.h"
+
+#include "nsIPrintingPromptService.h"
+#include "nsIFactory.h"
+#include "nsIPrintDialogService.h"
+#include "nsPIDOMWindow.h"
+
+//*****************************************************************************
+// nsPrintingPromptService
+//*****************************************************************************
+
+NS_IMPL_ISUPPORTS(nsPrintingPromptService, nsIPrintingPromptService, nsIWebProgressListener)
+
+nsPrintingPromptService::nsPrintingPromptService()
+{
+}
+
+nsPrintingPromptService::~nsPrintingPromptService()
+{
+}
+
+nsresult nsPrintingPromptService::Init()
+{
+ return NS_OK;
+}
+
+//*****************************************************************************
+// nsPrintingPromptService::nsIPrintingPromptService
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsPrintingPromptService::ShowPrintDialog(mozIDOMWindowProxy *parent, nsIWebBrowserPrint *webBrowserPrint, nsIPrintSettings *printSettings)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ nsCOMPtr<nsIPrintDialogService> dlgPrint(do_GetService(
+ NS_PRINTDIALOGSERVICE_CONTRACTID));
+ if (dlgPrint) {
+ return dlgPrint->Show(nsPIDOMWindowOuter::From(parent), printSettings,
+ webBrowserPrint);
+ }
+
+ return NS_ERROR_FAILURE;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsPrintingPromptService::ShowProgress(mozIDOMWindowProxy* parent,
+ nsIWebBrowserPrint* webBrowserPrint, // ok to be null
+ nsIPrintSettings* printSettings, // ok to be null
+ nsIObserver* openDialogObserver, // ok to be null
+ bool isForPrinting,
+ nsIWebProgressListener** webProgressListener,
+ nsIPrintProgressParams** printProgressParams,
+ bool* notifyOnOpen)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsPrintingPromptService::ShowPageSetup(mozIDOMWindowProxy *parent, nsIPrintSettings *printSettings, nsIObserver *aObs)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+ nsCOMPtr<nsIPrintDialogService> dlgPrint(do_GetService(
+ NS_PRINTDIALOGSERVICE_CONTRACTID));
+ if (dlgPrint) {
+ return dlgPrint->ShowPageSetup(nsPIDOMWindowOuter::From(parent), printSettings);
+ }
+
+ return NS_ERROR_FAILURE;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsPrintingPromptService::ShowPrinterProperties(mozIDOMWindowProxy *parent, const char16_t *printerName, nsIPrintSettings *printSettings)
+{
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+
+//*****************************************************************************
+// nsPrintingPromptService::nsIWebProgressListener
+//*****************************************************************************
+
+NS_IMETHODIMP
+nsPrintingPromptService::OnStateChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t aStateFlags, nsresult aStatus)
+{
+ return NS_OK;
+}
+
+/* void onProgressChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in long aCurSelfProgress, in long aMaxSelfProgress, in long aCurTotalProgress, in long aMaxTotalProgress); */
+NS_IMETHODIMP
+nsPrintingPromptService::OnProgressChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, int32_t aCurSelfProgress, int32_t aMaxSelfProgress, int32_t aCurTotalProgress, int32_t aMaxTotalProgress)
+{
+ return NS_OK;
+}
+
+/* void onLocationChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsIURI location, in unsigned long aFlags); */
+NS_IMETHODIMP
+nsPrintingPromptService::OnLocationChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsIURI *location, uint32_t aFlags)
+{
+ return NS_OK;
+}
+
+/* void onStatusChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in nsresult aStatus, in wstring aMessage); */
+NS_IMETHODIMP
+nsPrintingPromptService::OnStatusChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, nsresult aStatus, const char16_t *aMessage)
+{
+ return NS_OK;
+}
+
+/* void onSecurityChange (in nsIWebProgress aWebProgress, in nsIRequest aRequest, in unsigned long state); */
+NS_IMETHODIMP
+nsPrintingPromptService::OnSecurityChange(nsIWebProgress *aWebProgress, nsIRequest *aRequest, uint32_t state)
+{
+ return NS_OK;
+}
diff --git a/embedding/components/printingui/moz.build b/embedding/components/printingui/moz.build
index e0d6316931..c75471555e 100644
--- a/embedding/components/printingui/moz.build
+++ b/embedding/components/printingui/moz.build
@@ -10,5 +10,7 @@ DIRS += ['ipc']
if CONFIG['NS_PRINTING']:
if toolkit == 'windows':
DIRS += ['win']
+ elif toolkit == 'cocoa':
+ DIRS += ['mac']
elif CONFIG['MOZ_PDF_PRINTING']:
DIRS += ['unixshared']
diff --git a/gfx/cairo/cairo/src/moz.build b/gfx/cairo/cairo/src/moz.build
index 75201123ce..e097c45ca6 100644
--- a/gfx/cairo/cairo/src/moz.build
+++ b/gfx/cairo/cairo/src/moz.build
@@ -16,7 +16,7 @@ EXPORTS.cairo += [
'pixman-rename.h',
]
-if CONFIG['MOZ_WIDGET_TOOLKIT'] not in ('uikit'):
+if CONFIG['MOZ_WIDGET_TOOLKIT'] not in ('cocoa', 'uikit'):
EXPORTS.cairo += [
'cairo-pdf.h',
]
@@ -53,7 +53,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
]
else:
DEFINES['CAIRO_OMIT_WIN32_PRINTING'] = True
-elif CONFIG['MOZ_WIDGET_TOOLKIT'] in {'uikit'}:
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] in {'cocoa', 'uikit'}:
EXPORTS.cairo += [
'cairo-quartz-image.h',
'cairo-quartz.h',
diff --git a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp
new file mode 100644
index 0000000000..8834773e4c
--- /dev/null
+++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.cpp
@@ -0,0 +1,107 @@
+/* -*- Mode: C++; tab-width: 20; 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 "MacIOSurfaceTextureHostBasic.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "MacIOSurfaceHelpers.h"
+
+namespace mozilla {
+namespace layers {
+
+MacIOSurfaceTextureSourceBasic::MacIOSurfaceTextureSourceBasic(
+ BasicCompositor* aCompositor,
+ MacIOSurface* aSurface)
+ : mCompositor(aCompositor)
+ , mSurface(aSurface)
+{
+ MOZ_COUNT_CTOR(MacIOSurfaceTextureSourceBasic);
+}
+
+MacIOSurfaceTextureSourceBasic::~MacIOSurfaceTextureSourceBasic()
+{
+ MOZ_COUNT_DTOR(MacIOSurfaceTextureSourceBasic);
+}
+
+gfx::IntSize
+MacIOSurfaceTextureSourceBasic::GetSize() const
+{
+ return gfx::IntSize(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+}
+
+gfx::SurfaceFormat
+MacIOSurfaceTextureSourceBasic::GetFormat() const
+{
+ // Set the format the same way as CreateSourceSurfaceFromMacIOSurface.
+ return mSurface->GetFormat() == gfx::SurfaceFormat::NV12
+ ? gfx::SurfaceFormat::B8G8R8X8 : gfx::SurfaceFormat::B8G8R8A8;
+}
+
+MacIOSurfaceTextureHostBasic::MacIOSurfaceTextureHostBasic(
+ TextureFlags aFlags,
+ const SurfaceDescriptorMacIOSurface& aDescriptor
+)
+ : TextureHost(aFlags)
+{
+ mSurface = MacIOSurface::LookupSurface(aDescriptor.surfaceId(),
+ aDescriptor.scaleFactor(),
+ !aDescriptor.isOpaque());
+}
+
+gfx::SourceSurface*
+MacIOSurfaceTextureSourceBasic::GetSurface(gfx::DrawTarget* aTarget)
+{
+ if (!mSourceSurface) {
+ mSourceSurface = CreateSourceSurfaceFromMacIOSurface(mSurface);
+ }
+ return mSourceSurface;
+}
+
+void
+MacIOSurfaceTextureSourceBasic::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertBasicCompositor(aCompositor);
+}
+
+bool
+MacIOSurfaceTextureHostBasic::Lock()
+{
+ if (!mCompositor) {
+ return false;
+ }
+
+ if (!mTextureSource) {
+ mTextureSource = new MacIOSurfaceTextureSourceBasic(mCompositor, mSurface);
+ }
+ return true;
+}
+
+void
+MacIOSurfaceTextureHostBasic::SetCompositor(Compositor* aCompositor)
+{
+ BasicCompositor* compositor = AssertBasicCompositor(aCompositor);
+ if (!compositor) {
+ mTextureSource = nullptr;
+ return;
+ }
+ mCompositor = compositor;
+ if (mTextureSource) {
+ mTextureSource->SetCompositor(compositor);
+ }
+}
+
+gfx::SurfaceFormat
+MacIOSurfaceTextureHostBasic::GetFormat() const {
+ return mSurface->HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8 : gfx::SurfaceFormat::B8G8R8X8;
+}
+
+gfx::IntSize
+MacIOSurfaceTextureHostBasic::GetSize() const {
+ return gfx::IntSize(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h
new file mode 100644
index 0000000000..7949aecfca
--- /dev/null
+++ b/gfx/layers/basic/MacIOSurfaceTextureHostBasic.h
@@ -0,0 +1,97 @@
+/* -*- Mode: C++; tab-width: 20; 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_GFX_MACIOSURFACETEXTUREHOST_BASIC_H
+#define MOZILLA_GFX_MACIOSURFACETEXTUREHOST_BASIC_H
+
+#include "mozilla/layers/BasicCompositor.h"
+#include "mozilla/layers/TextureHostBasic.h"
+
+class MacIOSurface;
+
+namespace mozilla {
+namespace layers {
+
+class BasicCompositor;
+
+/**
+ * A texture source meant for use with BasicCompositor.
+ *
+ * It does not own any GL texture, and attaches its shared handle to one of
+ * the compositor's temporary textures when binding.
+ */
+class MacIOSurfaceTextureSourceBasic
+ : public TextureSourceBasic,
+ public TextureSource
+{
+public:
+ MacIOSurfaceTextureSourceBasic(BasicCompositor* aCompositor,
+ MacIOSurface* aSurface);
+ virtual ~MacIOSurfaceTextureSourceBasic();
+
+ virtual const char* Name() const override { return "MacIOSurfaceTextureSourceBasic"; }
+
+ virtual TextureSourceBasic* AsSourceBasic() override { return this; }
+
+ virtual gfx::IntSize GetSize() const override;
+ virtual gfx::SurfaceFormat GetFormat() const override;
+ virtual gfx::SourceSurface* GetSurface(gfx::DrawTarget* aTarget) override;
+
+ virtual void DeallocateDeviceData() override { }
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+protected:
+ RefPtr<BasicCompositor> mCompositor;
+ RefPtr<MacIOSurface> mSurface;
+ RefPtr<gfx::SourceSurface> mSourceSurface;
+};
+
+/**
+ * A TextureHost for shared MacIOSurface
+ *
+ * Most of the logic actually happens in MacIOSurfaceTextureSourceBasic.
+ */
+class MacIOSurfaceTextureHostBasic : public TextureHost
+{
+public:
+ MacIOSurfaceTextureHostBasic(TextureFlags aFlags,
+ const SurfaceDescriptorMacIOSurface& aDescriptor);
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual bool Lock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+ {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ virtual gfx::IntSize GetSize() const override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ virtual const char* Name() override { return "MacIOSurfaceTextureHostBasic"; }
+#endif
+
+protected:
+ RefPtr<BasicCompositor> mCompositor;
+ RefPtr<MacIOSurfaceTextureSourceBasic> mTextureSource;
+ RefPtr<MacIOSurface> mSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_BASIC_H
diff --git a/gfx/layers/ipc/ShadowLayerUtilsMac.cpp b/gfx/layers/ipc/ShadowLayerUtilsMac.cpp
new file mode 100644
index 0000000000..cc5199ea82
--- /dev/null
+++ b/gfx/layers/ipc/ShadowLayerUtilsMac.cpp
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * vim: sw=2 ts=8 et :
+ */
+/* 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 "mozilla/gfx/Point.h"
+#include "mozilla/layers/PLayerTransaction.h"
+#include "mozilla/layers/ShadowLayers.h"
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/CompositorTypes.h"
+
+#include "gfx2DGlue.h"
+#include "gfxPlatform.h"
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+/*static*/ void
+ShadowLayerForwarder::PlatformSyncBeforeUpdate()
+{
+}
+
+/*static*/ void
+LayerManagerComposite::PlatformSyncBeforeReplyUpdate()
+{
+}
+
+/*static*/ bool
+LayerManagerComposite::SupportsDirectTexturing()
+{
+ return false;
+}
+
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build
index 50c2b8f855..be0c95ba20 100644
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -104,6 +104,7 @@ EXPORTS.mozilla.layers += [
'AxisPhysicsModel.h',
'AxisPhysicsMSDModel.h',
'basic/BasicCompositor.h',
+ 'basic/MacIOSurfaceTextureHostBasic.h',
'basic/TextureHostBasic.h',
'BSPTree.h',
'BufferTexture.h',
@@ -172,6 +173,8 @@ EXPORTS.mozilla.layers += [
'LayersTypes.h',
'opengl/CompositingRenderTargetOGL.h',
'opengl/CompositorOGL.h',
+ 'opengl/MacIOSurfaceTextureClientOGL.h',
+ 'opengl/MacIOSurfaceTextureHostOGL.h',
'opengl/TextureClientOGL.h',
'opengl/TextureHostOGL.h',
'PersistentBufferProvider.h',
@@ -197,6 +200,21 @@ if CONFIG['MOZ_X11']:
'opengl/X11TextureSourceOGL.cpp',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ EXPORTS.mozilla.layers += [
+ 'opengl/GLManager.h',
+ ]
+ EXPORTS += [
+ 'MacIOSurfaceHelpers.h',
+ 'MacIOSurfaceImage.h',
+ ]
+ SOURCES += [
+ 'ipc/ShadowLayerUtilsMac.cpp',
+ 'MacIOSurfaceHelpers.cpp',
+ 'MacIOSurfaceImage.cpp',
+ 'opengl/GLManager.cpp',
+ ]
+
SOURCES += [
'apz/public/IAPZCTreeManager.cpp',
'apz/src/APZCTreeManager.cpp',
@@ -348,6 +366,13 @@ if CONFIG['_MSC_VER'] and CONFIG['CPU_ARCH'] == 'x86_64':
]:
SOURCES[src].no_pgo = True
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'basic/MacIOSurfaceTextureHostBasic.cpp',
+ 'opengl/MacIOSurfaceTextureClientOGL.cpp',
+ 'opengl/MacIOSurfaceTextureHostOGL.cpp',
+ ]
+
IPDL_SOURCES = [
'ipc/LayersMessages.ipdlh',
'ipc/LayersSurfaces.ipdlh',
diff --git a/gfx/layers/opengl/GLManager.cpp b/gfx/layers/opengl/GLManager.cpp
new file mode 100644
index 0000000000..e4c0b361fb
--- /dev/null
+++ b/gfx/layers/opengl/GLManager.cpp
@@ -0,0 +1,70 @@
+/* -*- Mode: C++; tab-width: 20; 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 "GLManager.h"
+#include "CompositorOGL.h" // for CompositorOGL
+#include "GLContext.h" // for GLContext
+#include "mozilla/Attributes.h" // for override
+#include "mozilla/RefPtr.h" // for RefPtr
+#include "mozilla/layers/Compositor.h" // for Compositor
+#include "mozilla/layers/LayerManagerComposite.h"
+#include "mozilla/layers/LayersTypes.h"
+#include "mozilla/mozalloc.h" // for operator new, etc
+
+using namespace mozilla::gl;
+
+namespace mozilla {
+namespace layers {
+
+class GLManagerCompositor : public GLManager
+{
+public:
+ explicit GLManagerCompositor(CompositorOGL* aCompositor)
+ : mImpl(aCompositor)
+ {}
+
+ virtual GLContext* gl() const override
+ {
+ return mImpl->gl();
+ }
+
+ virtual void ActivateProgram(ShaderProgramOGL *aProg) override
+ {
+ mImpl->ActivateProgram(aProg);
+ }
+
+ virtual ShaderProgramOGL* GetProgram(GLenum aTarget, gfx::SurfaceFormat aFormat) override
+ {
+ ShaderConfigOGL config = ShaderConfigFromTargetAndFormat(aTarget, aFormat);
+ return mImpl->GetShaderProgramFor(config);
+ }
+
+ virtual const gfx::Matrix4x4& GetProjMatrix() const override
+ {
+ return mImpl->GetProjMatrix();
+ }
+
+ virtual void BindAndDrawQuad(ShaderProgramOGL *aProg,
+ const gfx::Rect& aLayerRect,
+ const gfx::Rect& aTextureRect) override
+ {
+ mImpl->BindAndDrawQuad(aProg, aLayerRect, aTextureRect);
+ }
+
+private:
+ RefPtr<CompositorOGL> mImpl;
+};
+
+/* static */ GLManager*
+GLManager::CreateGLManager(LayerManagerComposite* aManager)
+{
+ if (aManager && aManager->GetCompositor()->GetBackendType() == LayersBackend::LAYERS_OPENGL) {
+ return new GLManagerCompositor(aManager->GetCompositor()->AsCompositorOGL());
+ }
+ return nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/GLManager.h b/gfx/layers/opengl/GLManager.h
new file mode 100644
index 0000000000..7c872758e9
--- /dev/null
+++ b/gfx/layers/opengl/GLManager.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C++; tab-width: 20; 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_GFX_GLMANAGER_H
+#define MOZILLA_GFX_GLMANAGER_H
+
+#include "mozilla/gfx/Types.h" // for SurfaceFormat
+#include "OGLShaderProgram.h"
+
+namespace mozilla {
+namespace gl {
+class GLContext;
+} // namespace gl
+
+namespace layers {
+
+class LayerManagerComposite;
+
+/**
+ * Minimal interface to allow widgets to draw using OpenGL. Abstracts
+ * CompositorOGL. Call CreateGLManager with a LayerManagerComposite
+ * backed by a CompositorOGL.
+ */
+class GLManager
+{
+public:
+ static GLManager* CreateGLManager(LayerManagerComposite* aManager);
+
+ virtual ~GLManager() {}
+
+ virtual gl::GLContext* gl() const = 0;
+ virtual ShaderProgramOGL* GetProgram(GLenum aTarget, gfx::SurfaceFormat aFormat) = 0;
+ virtual void ActivateProgram(ShaderProgramOGL* aPrg) = 0;
+ virtual const gfx::Matrix4x4& GetProjMatrix() const = 0;
+ virtual void BindAndDrawQuad(ShaderProgramOGL *aProg, const gfx::Rect& aLayerRect,
+ const gfx::Rect& aTextureRect) = 0;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp
new file mode 100644
index 0000000000..dd522e6503
--- /dev/null
+++ b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.cpp
@@ -0,0 +1,140 @@
+/* -*- Mode: C++; tab-width: 20; 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 "MacIOSurfaceTextureClientOGL.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "MacIOSurfaceHelpers.h"
+#include "gfxPlatform.h"
+
+namespace mozilla {
+namespace layers {
+
+using namespace gfx;
+
+MacIOSurfaceTextureData::MacIOSurfaceTextureData(MacIOSurface* aSurface,
+ BackendType aBackend)
+ : mSurface(aSurface)
+ , mBackend(aBackend)
+{
+ MOZ_ASSERT(mSurface);
+}
+
+MacIOSurfaceTextureData::~MacIOSurfaceTextureData()
+{}
+
+// static
+MacIOSurfaceTextureData*
+MacIOSurfaceTextureData::Create(MacIOSurface* aSurface, BackendType aBackend)
+{
+ MOZ_ASSERT(aSurface);
+ if (!aSurface) {
+ return nullptr;
+ }
+ return new MacIOSurfaceTextureData(aSurface, aBackend);
+}
+
+MacIOSurfaceTextureData*
+MacIOSurfaceTextureData::Create(const IntSize& aSize,
+ SurfaceFormat aFormat,
+ BackendType aBackend)
+{
+ if (aFormat != SurfaceFormat::B8G8R8A8 &&
+ aFormat != SurfaceFormat::B8G8R8X8) {
+ return nullptr;
+ }
+
+ RefPtr<MacIOSurface> surf = MacIOSurface::CreateIOSurface(aSize.width, aSize.height,
+ 1.0,
+ aFormat == SurfaceFormat::B8G8R8A8);
+ if (!surf) {
+ return nullptr;
+ }
+
+ return Create(surf, aBackend);
+}
+
+bool
+MacIOSurfaceTextureData::Serialize(SurfaceDescriptor& aOutDescriptor)
+{
+ aOutDescriptor = SurfaceDescriptorMacIOSurface(mSurface->GetIOSurfaceID(),
+ mSurface->GetContentsScaleFactor(),
+ !mSurface->HasAlpha());
+ return true;
+}
+
+void
+MacIOSurfaceTextureData::FillInfo(TextureData::Info& aInfo) const
+{
+ aInfo.size = gfx::IntSize(mSurface->GetDevicePixelWidth(), mSurface->GetDevicePixelHeight());
+ aInfo.format = mSurface->HasAlpha() ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8;
+ aInfo.hasIntermediateBuffer = false;
+ aInfo.hasSynchronization = false;
+ aInfo.supportsMoz2D = true;
+ aInfo.canExposeMappedData = false;
+}
+
+bool
+MacIOSurfaceTextureData::Lock(OpenMode)
+{
+ mSurface->Lock(false);
+ return true;
+}
+
+void
+MacIOSurfaceTextureData::Unlock()
+{
+ mSurface->Unlock(false);
+}
+
+already_AddRefed<DataSourceSurface>
+MacIOSurfaceTextureData::GetAsSurface()
+{
+ RefPtr<SourceSurface> surf = CreateSourceSurfaceFromMacIOSurface(mSurface);
+ return surf->GetDataSurface();
+}
+
+already_AddRefed<DrawTarget>
+MacIOSurfaceTextureData::BorrowDrawTarget()
+{
+ MOZ_ASSERT(mBackend != BackendType::NONE);
+ if (mBackend == BackendType::NONE) {
+ // shouldn't happen, but degrade gracefully
+ return nullptr;
+ }
+ return Factory::CreateDrawTargetForData(
+ mBackend,
+ (unsigned char*)mSurface->GetBaseAddress(),
+ IntSize(mSurface->GetWidth(), mSurface->GetHeight()),
+ mSurface->GetBytesPerRow(),
+ mSurface->HasAlpha() ? SurfaceFormat::B8G8R8A8 : SurfaceFormat::B8G8R8X8,
+ true);
+}
+
+void
+MacIOSurfaceTextureData::Deallocate(LayersIPCChannel*)
+{
+ mSurface = nullptr;
+}
+
+void
+MacIOSurfaceTextureData::Forget(LayersIPCChannel*)
+{
+ mSurface = nullptr;
+}
+
+bool
+MacIOSurfaceTextureData::UpdateFromSurface(gfx::SourceSurface* aSurface)
+{
+ RefPtr<DrawTarget> dt = BorrowDrawTarget();
+ if (!dt) {
+ return false;
+ }
+
+ dt->CopySurface(aSurface, IntRect(IntPoint(), aSurface->GetSize()), IntPoint());
+ return true;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h
new file mode 100644
index 0000000000..21e4459537
--- /dev/null
+++ b/gfx/layers/opengl/MacIOSurfaceTextureClientOGL.h
@@ -0,0 +1,58 @@
+/* -*- Mode: C++; tab-width: 20; 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_GFX_MACIOSURFACETEXTURECLIENTOGL_H
+#define MOZILLA_GFX_MACIOSURFACETEXTURECLIENTOGL_H
+
+#include "mozilla/layers/TextureClientOGL.h"
+
+class MacIOSurface;
+
+namespace mozilla {
+namespace layers {
+
+class MacIOSurfaceTextureData : public TextureData
+{
+public:
+ static MacIOSurfaceTextureData* Create(MacIOSurface* aSurface,
+ gfx::BackendType aBackend);
+
+ static MacIOSurfaceTextureData*
+ Create(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat,
+ gfx::BackendType aBackend);
+
+ ~MacIOSurfaceTextureData();
+
+ virtual void FillInfo(TextureData::Info& aInfo) const override;
+
+ virtual bool Lock(OpenMode) override;
+
+ virtual void Unlock() override;
+
+ virtual already_AddRefed<gfx::DrawTarget> BorrowDrawTarget() override;
+
+ virtual bool Serialize(SurfaceDescriptor& aOutDescriptor) override;
+
+ virtual void Deallocate(LayersIPCChannel*) override;
+
+ virtual void Forget(LayersIPCChannel*) override;
+
+ virtual bool UpdateFromSurface(gfx::SourceSurface* aSurface) override;
+
+ // For debugging purposes only.
+ already_AddRefed<gfx::DataSourceSurface> GetAsSurface();
+
+protected:
+ MacIOSurfaceTextureData(MacIOSurface* aSurface,
+ gfx::BackendType aBackend);
+
+ RefPtr<MacIOSurface> mSurface;
+ gfx::BackendType mBackend;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_MACIOSURFACETEXTURECLIENTOGL_H
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
new file mode 100644
index 0000000000..9736618f7e
--- /dev/null
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.cpp
@@ -0,0 +1,180 @@
+/* -*- Mode: C++; tab-width: 20; 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 "MacIOSurfaceTextureHostOGL.h"
+#include "mozilla/gfx/MacIOSurface.h"
+#include "GLContextCGL.h"
+
+namespace mozilla {
+namespace layers {
+
+MacIOSurfaceTextureHostOGL::MacIOSurfaceTextureHostOGL(TextureFlags aFlags,
+ const SurfaceDescriptorMacIOSurface& aDescriptor)
+ : TextureHost(aFlags)
+{
+ MOZ_COUNT_CTOR(MacIOSurfaceTextureHostOGL);
+ mSurface = MacIOSurface::LookupSurface(aDescriptor.surfaceId(),
+ aDescriptor.scaleFactor(),
+ !aDescriptor.isOpaque());
+}
+
+MacIOSurfaceTextureHostOGL::~MacIOSurfaceTextureHostOGL()
+{
+ MOZ_COUNT_DTOR(MacIOSurfaceTextureHostOGL);
+}
+
+GLTextureSource*
+MacIOSurfaceTextureHostOGL::CreateTextureSourceForPlane(size_t aPlane)
+{
+ MOZ_ASSERT(mSurface);
+
+ GLuint textureHandle;
+ gl::GLContext* gl = mCompositor->gl();
+ gl->fGenTextures(1, &textureHandle);
+ gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, textureHandle);
+ gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_T, LOCAL_GL_CLAMP_TO_EDGE);
+ gl->fTexParameteri(LOCAL_GL_TEXTURE_RECTANGLE_ARB, LOCAL_GL_TEXTURE_WRAP_S, LOCAL_GL_CLAMP_TO_EDGE);
+
+ mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext(), aPlane);
+
+ return new GLTextureSource(mCompositor, textureHandle, LOCAL_GL_TEXTURE_RECTANGLE_ARB,
+ gfx::IntSize(mSurface->GetDevicePixelWidth(aPlane),
+ mSurface->GetDevicePixelHeight(aPlane)),
+ // XXX: This isn't really correct (but isn't used), we should be using the
+ // format of the individual plane, not of the whole buffer.
+ mSurface->GetFormat());
+}
+
+bool
+MacIOSurfaceTextureHostOGL::Lock()
+{
+ if (!gl() || !gl()->MakeCurrent() || !mSurface) {
+ return false;
+ }
+
+ if (!mTextureSource) {
+ mTextureSource = CreateTextureSourceForPlane(0);
+
+ RefPtr<TextureSource> prev = mTextureSource;
+ for (size_t i = 1; i < mSurface->GetPlaneCount(); i++) {
+ RefPtr<TextureSource> next = CreateTextureSourceForPlane(i);
+ prev->SetNextSibling(next);
+ prev = next;
+ }
+ }
+ return true;
+}
+
+void
+MacIOSurfaceTextureHostOGL::SetCompositor(Compositor* aCompositor)
+{
+ CompositorOGL* glCompositor = AssertGLCompositor(aCompositor);
+ if (!glCompositor) {
+ mTextureSource = nullptr;
+ mCompositor = nullptr;
+ return;
+ }
+
+ if (mCompositor != glCompositor) {
+ // Cannot share GL texture identifiers across compositors.
+ mTextureSource = nullptr;
+ }
+ mCompositor = glCompositor;
+}
+
+gfx::SurfaceFormat
+MacIOSurfaceTextureHostOGL::GetFormat() const {
+ if (!mSurface) {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+ return mSurface->GetFormat();
+}
+
+gfx::SurfaceFormat
+MacIOSurfaceTextureHostOGL::GetReadFormat() const {
+ if (!mSurface) {
+ return gfx::SurfaceFormat::UNKNOWN;
+ }
+ return mSurface->GetReadFormat();
+}
+
+gfx::IntSize
+MacIOSurfaceTextureHostOGL::GetSize() const {
+ if (!mSurface) {
+ return gfx::IntSize();
+ }
+ return gfx::IntSize(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+}
+
+gl::GLContext*
+MacIOSurfaceTextureHostOGL::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+MacIOSurfaceTextureSourceOGL::MacIOSurfaceTextureSourceOGL(
+ CompositorOGL* aCompositor,
+ MacIOSurface* aSurface)
+ : mCompositor(aCompositor)
+ , mSurface(aSurface)
+{
+ MOZ_ASSERT(aCompositor);
+ MOZ_COUNT_CTOR(MacIOSurfaceTextureSourceOGL);
+}
+
+MacIOSurfaceTextureSourceOGL::~MacIOSurfaceTextureSourceOGL()
+{
+ MOZ_COUNT_DTOR(MacIOSurfaceTextureSourceOGL);
+}
+
+gfx::IntSize
+MacIOSurfaceTextureSourceOGL::GetSize() const
+{
+ return gfx::IntSize(mSurface->GetDevicePixelWidth(),
+ mSurface->GetDevicePixelHeight());
+}
+
+gfx::SurfaceFormat
+MacIOSurfaceTextureSourceOGL::GetFormat() const
+{
+ return mSurface->HasAlpha() ? gfx::SurfaceFormat::R8G8B8A8
+ : gfx::SurfaceFormat::R8G8B8X8;
+}
+
+void
+MacIOSurfaceTextureSourceOGL::BindTexture(GLenum aTextureUnit,
+ gfx::SamplingFilter aSamplingFilter)
+{
+ gl::GLContext* gl = this->gl();
+ if (!gl || !gl->MakeCurrent()) {
+ NS_WARNING("Trying to bind a texture without a working GLContext");
+ return;
+ }
+ GLuint tex = mCompositor->GetTemporaryTexture(GetTextureTarget(), aTextureUnit);
+
+ gl->fActiveTexture(aTextureUnit);
+ gl->fBindTexture(LOCAL_GL_TEXTURE_RECTANGLE_ARB, tex);
+ mSurface->CGLTexImageIOSurface2D(gl::GLContextCGL::Cast(gl)->GetCGLContext());
+ ApplySamplingFilterToBoundTexture(gl, aSamplingFilter, LOCAL_GL_TEXTURE_RECTANGLE_ARB);
+}
+
+void
+MacIOSurfaceTextureSourceOGL::SetCompositor(Compositor* aCompositor)
+{
+ mCompositor = AssertGLCompositor(aCompositor);
+ if (mCompositor && mNextSibling) {
+ mNextSibling->SetCompositor(aCompositor);
+ }
+}
+
+gl::GLContext*
+MacIOSurfaceTextureSourceOGL::gl() const
+{
+ return mCompositor ? mCompositor->gl() : nullptr;
+}
+
+} // namespace layers
+} // namespace mozilla
diff --git a/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
new file mode 100644
index 0000000000..55e2f5019a
--- /dev/null
+++ b/gfx/layers/opengl/MacIOSurfaceTextureHostOGL.h
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 20; 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_GFX_MACIOSURFACETEXTUREHOSTOGL_H
+#define MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
+
+#include "mozilla/layers/CompositorOGL.h"
+#include "mozilla/layers/TextureHostOGL.h"
+
+class MacIOSurface;
+
+namespace mozilla {
+namespace layers {
+
+/**
+ * A texture source meant for use with MacIOSurfaceTextureHostOGL.
+ *
+ * It does not own any GL texture, and attaches its shared handle to one of
+ * the compositor's temporary textures when binding.
+ */
+class MacIOSurfaceTextureSourceOGL : public TextureSource
+ , public TextureSourceOGL
+{
+public:
+ MacIOSurfaceTextureSourceOGL(CompositorOGL* aCompositor,
+ MacIOSurface* aSurface);
+ virtual ~MacIOSurfaceTextureSourceOGL();
+
+ virtual const char* Name() const override { return "MacIOSurfaceTextureSourceOGL"; }
+
+ virtual TextureSourceOGL* AsSourceOGL() override { return this; }
+
+ virtual void BindTexture(GLenum activetex,
+ gfx::SamplingFilter aSamplingFilter) override;
+
+ virtual bool IsValid() const override { return !!gl(); }
+
+ virtual gfx::IntSize GetSize() const override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+
+ virtual GLenum GetTextureTarget() const override { return LOCAL_GL_TEXTURE_RECTANGLE_ARB; }
+
+ virtual GLenum GetWrapMode() const override { return LOCAL_GL_CLAMP_TO_EDGE; }
+
+ // MacIOSurfaceTextureSourceOGL doesn't own any gl texture
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ gl::GLContext* gl() const;
+
+protected:
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<MacIOSurface> mSurface;
+};
+
+/**
+ * A TextureHost for shared MacIOSurface
+ *
+ * Most of the logic actually happens in MacIOSurfaceTextureSourceOGL.
+ */
+class MacIOSurfaceTextureHostOGL : public TextureHost
+{
+public:
+ MacIOSurfaceTextureHostOGL(TextureFlags aFlags,
+ const SurfaceDescriptorMacIOSurface& aDescriptor);
+ virtual ~MacIOSurfaceTextureHostOGL();
+
+ // MacIOSurfaceTextureSourceOGL doesn't own any GL texture
+ virtual void DeallocateDeviceData() override {}
+
+ virtual void SetCompositor(Compositor* aCompositor) override;
+
+ virtual Compositor* GetCompositor() override { return mCompositor; }
+
+ virtual bool Lock() override;
+
+ virtual gfx::SurfaceFormat GetFormat() const override;
+ virtual gfx::SurfaceFormat GetReadFormat() const override;
+
+ virtual bool BindTextureSource(CompositableTextureSourceRef& aTexture) override
+ {
+ aTexture = mTextureSource;
+ return !!aTexture;
+ }
+
+ virtual already_AddRefed<gfx::DataSourceSurface> GetAsSurface() override
+ {
+ return nullptr; // XXX - implement this (for MOZ_DUMP_PAINTING)
+ }
+
+ gl::GLContext* gl() const;
+
+ virtual gfx::IntSize GetSize() const override;
+
+#ifdef MOZ_LAYERS_HAVE_LOG
+ virtual const char* Name() override { return "MacIOSurfaceTextureHostOGL"; }
+#endif
+
+protected:
+ GLTextureSource* CreateTextureSourceForPlane(size_t aPlane);
+
+ RefPtr<CompositorOGL> mCompositor;
+ RefPtr<GLTextureSource> mTextureSource;
+ RefPtr<MacIOSurface> mSurface;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif // MOZILLA_GFX_MACIOSURFACETEXTUREHOSTOGL_H
diff --git a/gfx/skia/generate_mozbuild.py b/gfx/skia/generate_mozbuild.py
index 22801ea9a8..a15fd40861 100755
--- a/gfx/skia/generate_mozbuild.py
+++ b/gfx/skia/generate_mozbuild.py
@@ -65,6 +65,7 @@ LOCAL_INCLUDES += [
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] in {
+ 'cocoa',
'gtk2',
'gtk3',
'uikit',
@@ -443,7 +444,7 @@ def write_mozbuild(sources):
f.write("if CONFIG['MOZ_ENABLE_SKIA_GPU']:\n")
write_sources(f, sources['gpu'], 4)
- f.write("if CONFIG['MOZ_WIDGET_TOOLKIT'] in {'uikit'}:\n")
+ f.write("if CONFIG['MOZ_WIDGET_TOOLKIT'] in {'cocoa', 'uikit'}:\n")
write_sources(f, sources['mac'], 4)
f.write("if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:\n")
diff --git a/gfx/skia/moz.build b/gfx/skia/moz.build
index 1d9d342f41..75a66dd28e 100644
--- a/gfx/skia/moz.build
+++ b/gfx/skia/moz.build
@@ -516,7 +516,7 @@ if CONFIG['MOZ_ENABLE_SKIA_GPU']:
'skia/src/gpu/GrResourceCache.cpp',
'skia/src/image/SkImage_Gpu.cpp',
]
-if CONFIG['MOZ_WIDGET_TOOLKIT'] in {'uikit'}:
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in {'cocoa', 'uikit'}:
SOURCES += [
'skia/src/ports/SkDebug_stdio.cpp',
'skia/src/ports/SkOSFile_posix.cpp',
@@ -663,6 +663,7 @@ LOCAL_INCLUDES += [
]
if CONFIG['MOZ_WIDGET_TOOLKIT'] in {
+ 'cocoa',
'gtk2',
'gtk3',
'uikit',
diff --git a/image/decoders/icon/mac/moz.build b/image/decoders/icon/mac/moz.build
new file mode 100644
index 0000000000..f36d6ca53d
--- /dev/null
+++ b/image/decoders/icon/mac/moz.build
@@ -0,0 +1,10 @@
+# -*- 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/.
+
+SOURCES += [
+ 'nsIconChannelCocoa.mm',
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/image/decoders/icon/mac/nsIconChannel.h b/image/decoders/icon/mac/nsIconChannel.h
new file mode 100644
index 0000000000..9fef171193
--- /dev/null
+++ b/image/decoders/icon/mac/nsIconChannel.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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_image_encoders_icon_mac_nsIconChannel_h
+#define mozilla_image_encoders_icon_mac_nsIconChannel_h
+
+#include "mozilla/Attributes.h"
+
+#include "nsCOMPtr.h"
+#include "nsXPIDLString.h"
+#include "nsIChannel.h"
+#include "nsILoadGroup.h"
+#include "nsILoadInfo.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsIInputStreamPump.h"
+#include "nsIStreamListener.h"
+#include "nsIURI.h"
+
+class nsIFile;
+
+class nsIconChannel final : public nsIChannel, public nsIStreamListener
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIREQUEST
+ NS_DECL_NSICHANNEL
+ NS_DECL_NSIREQUESTOBSERVER
+ NS_DECL_NSISTREAMLISTENER
+
+ nsIconChannel();
+
+ nsresult Init(nsIURI* uri);
+
+protected:
+ virtual ~nsIconChannel();
+
+ nsCOMPtr<nsIURI> mUrl;
+ nsCOMPtr<nsIURI> mOriginalURI;
+ nsCOMPtr<nsILoadGroup> mLoadGroup;
+ nsCOMPtr<nsIInterfaceRequestor> mCallbacks;
+ nsCOMPtr<nsISupports> mOwner;
+ nsCOMPtr<nsILoadInfo> mLoadInfo;
+
+ nsCOMPtr<nsIInputStreamPump> mPump;
+ nsCOMPtr<nsIStreamListener> mListener;
+
+ nsresult MakeInputStream(nsIInputStream** _retval, bool nonBlocking);
+
+ nsresult ExtractIconInfoFromUrl(nsIFile** aLocalFile,
+ uint32_t* aDesiredImageSize,
+ nsACString& aContentType,
+ nsACString& aFileExtension);
+};
+
+#endif // mozilla_image_encoders_icon_mac_nsIconChannel_h
diff --git a/image/decoders/icon/mac/nsIconChannelCocoa.mm b/image/decoders/icon/mac/nsIconChannelCocoa.mm
new file mode 100644
index 0000000000..9c2686cdd2
--- /dev/null
+++ b/image/decoders/icon/mac/nsIconChannelCocoa.mm
@@ -0,0 +1,565 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 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/. */
+
+#include "nsContentUtils.h"
+#include "nsIconChannel.h"
+#include "mozilla/EndianUtils.h"
+#include "nsIIconURI.h"
+#include "nsIServiceManager.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIInterfaceRequestorUtils.h"
+#include "nsXPIDLString.h"
+#include "nsMimeTypes.h"
+#include "nsMemory.h"
+#include "nsIStringStream.h"
+#include "nsIURL.h"
+#include "nsNetCID.h"
+#include "nsIPipe.h"
+#include "nsIOutputStream.h"
+#include "nsIMIMEService.h"
+#include "nsCExternalHandlerService.h"
+#include "nsILocalFileMac.h"
+#include "nsIFileURL.h"
+#include "nsTArray.h"
+#include "nsObjCExceptions.h"
+#include "nsProxyRelease.h"
+#include "nsContentSecurityManager.h"
+
+#include <Cocoa/Cocoa.h>
+
+// nsIconChannel methods
+nsIconChannel::nsIconChannel()
+{
+}
+
+nsIconChannel::~nsIconChannel()
+{
+ if (mLoadInfo) {
+ NS_ReleaseOnMainThread(mLoadInfo.forget());
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsIconChannel,
+ nsIChannel,
+ nsIRequest,
+ nsIRequestObserver,
+ nsIStreamListener)
+
+nsresult
+nsIconChannel::Init(nsIURI* uri)
+{
+ NS_ASSERTION(uri, "no uri");
+ mUrl = uri;
+ mOriginalURI = uri;
+ nsresult rv;
+ mPump = do_CreateInstance(NS_INPUTSTREAMPUMP_CONTRACTID, &rv);
+ return rv;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIRequest methods:
+
+NS_IMETHODIMP
+nsIconChannel::GetName(nsACString& result)
+{
+ return mUrl->GetSpec(result);
+}
+
+NS_IMETHODIMP
+nsIconChannel::IsPending(bool* result)
+{
+ return mPump->IsPending(result);
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetStatus(nsresult* status)
+{
+ return mPump->GetStatus(status);
+}
+
+NS_IMETHODIMP
+nsIconChannel::Cancel(nsresult status)
+{
+ return mPump->Cancel(status);
+}
+
+NS_IMETHODIMP
+nsIconChannel::Suspend(void)
+{
+ return mPump->Suspend();
+}
+
+NS_IMETHODIMP
+nsIconChannel::Resume(void)
+{
+ return mPump->Resume();
+}
+
+// nsIRequestObserver methods
+NS_IMETHODIMP
+nsIconChannel::OnStartRequest(nsIRequest* aRequest,
+ nsISupports* aContext)
+{
+ if (mListener) {
+ return mListener->OnStartRequest(this, aContext);
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::OnStopRequest(nsIRequest* aRequest,
+ nsISupports* aContext,
+ nsresult aStatus)
+{
+ if (mListener) {
+ mListener->OnStopRequest(this, aContext, aStatus);
+ mListener = nullptr;
+ }
+
+ // Remove from load group
+ if (mLoadGroup) {
+ mLoadGroup->RemoveRequest(this, nullptr, aStatus);
+ }
+
+ return NS_OK;
+}
+
+// nsIStreamListener methods
+NS_IMETHODIMP
+nsIconChannel::OnDataAvailable(nsIRequest* aRequest,
+ nsISupports* aContext,
+ nsIInputStream* aStream,
+ uint64_t aOffset,
+ uint32_t aCount)
+{
+ if (mListener) {
+ return mListener->OnDataAvailable(this, aContext, aStream, aOffset, aCount);
+ }
+ return NS_OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// nsIChannel methods:
+
+NS_IMETHODIMP
+nsIconChannel::GetOriginalURI(nsIURI** aURI)
+{
+ *aURI = mOriginalURI;
+ NS_ADDREF(*aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetOriginalURI(nsIURI* aURI)
+{
+ NS_ENSURE_ARG_POINTER(aURI);
+ mOriginalURI = aURI;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetURI(nsIURI** aURI)
+{
+ *aURI = mUrl;
+ NS_IF_ADDREF(*aURI);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::Open(nsIInputStream** _retval)
+{
+ return MakeInputStream(_retval, false);
+}
+
+NS_IMETHODIMP
+nsIconChannel::Open2(nsIInputStream** aStream)
+{
+ nsCOMPtr<nsIStreamListener> listener;
+ nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return Open(aStream);
+}
+
+nsresult
+nsIconChannel::ExtractIconInfoFromUrl(nsIFile** aLocalFile,
+ uint32_t* aDesiredImageSize,
+ nsACString& aContentType,
+ nsACString& aFileExtension)
+{
+ nsresult rv = NS_OK;
+ nsCOMPtr<nsIMozIconURI> iconURI (do_QueryInterface(mUrl, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ iconURI->GetImageSize(aDesiredImageSize);
+ iconURI->GetContentType(aContentType);
+ iconURI->GetFileExtension(aFileExtension);
+
+ nsCOMPtr<nsIURL> url;
+ rv = iconURI->GetIconURL(getter_AddRefs(url));
+ if (NS_FAILED(rv) || !url) return NS_OK;
+
+ nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(url, &rv);
+ if (NS_FAILED(rv) || !fileURL) return NS_OK;
+
+ nsCOMPtr<nsIFile> file;
+ rv = fileURL->GetFile(getter_AddRefs(file));
+ if (NS_FAILED(rv) || !file) return NS_OK;
+
+ nsCOMPtr<nsILocalFileMac> localFileMac (do_QueryInterface(file, &rv));
+ if (NS_FAILED(rv) || !localFileMac) return NS_OK;
+
+ *aLocalFile = file;
+ NS_IF_ADDREF(*aLocalFile);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::AsyncOpen(nsIStreamListener* aListener,
+ nsISupports* ctxt)
+{
+ MOZ_ASSERT(!mLoadInfo ||
+ mLoadInfo->GetSecurityMode() == 0 ||
+ mLoadInfo->GetInitialSecurityCheckDone() ||
+ (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL &&
+ nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())),
+ "security flags in loadInfo but asyncOpen2() not called");
+
+ nsCOMPtr<nsIInputStream> inStream;
+ nsresult rv = MakeInputStream(getter_AddRefs(inStream), true);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Init our stream pump
+ rv = mPump->Init(inStream, int64_t(-1), int64_t(-1), 0, 0, false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mPump->AsyncRead(this, ctxt);
+ if (NS_SUCCEEDED(rv)) {
+ // Store our real listener
+ mListener = aListener;
+ // Add ourself to the load group, if available
+ if (mLoadGroup) {
+ mLoadGroup->AddRequest(this, nullptr);
+ }
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsIconChannel::AsyncOpen2(nsIStreamListener* aListener)
+{
+ nsCOMPtr<nsIStreamListener> listener = aListener;
+ nsresult rv = nsContentSecurityManager::doContentSecurityCheck(this, listener);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return AsyncOpen(listener, nullptr);
+}
+
+nsresult
+nsIconChannel::MakeInputStream(nsIInputStream** _retval,
+ bool aNonBlocking)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ nsXPIDLCString contentType;
+ nsAutoCString fileExt;
+ nsCOMPtr<nsIFile> fileloc; // file we want an icon for
+ uint32_t desiredImageSize;
+ nsresult rv = ExtractIconInfoFromUrl(getter_AddRefs(fileloc),
+ &desiredImageSize, contentType, fileExt);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool fileExists = false;
+ if (fileloc) {
+ // ensure that we DO NOT resolve aliases, very important for file views
+ fileloc->SetFollowLinks(false);
+ fileloc->Exists(&fileExists);
+ }
+
+ NSImage* iconImage = nil;
+
+ // first try to get the icon from the file if it exists
+ if (fileExists) {
+ nsCOMPtr<nsILocalFileMac> localFileMac(do_QueryInterface(fileloc, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CFURLRef macURL;
+ if (NS_SUCCEEDED(localFileMac->GetCFURL(&macURL))) {
+ iconImage = [[NSWorkspace sharedWorkspace]
+ iconForFile:[(NSURL*)macURL path]];
+ ::CFRelease(macURL);
+ }
+ }
+
+ // if we don't have an icon yet try to get one by extension
+ if (!iconImage && !fileExt.IsEmpty()) {
+ NSString* fileExtension = [NSString stringWithUTF8String:fileExt.get()];
+ iconImage = [[NSWorkspace sharedWorkspace] iconForFileType:fileExtension];
+ }
+
+ // If we still don't have an icon, get the generic document icon.
+ if (!iconImage) {
+ iconImage = [[NSWorkspace sharedWorkspace]
+ iconForFileType:NSFileTypeUnknown];
+ }
+
+ if (!iconImage) {
+ return NS_ERROR_FAILURE;
+ }
+
+ // we have an icon now, size it
+ NSRect desiredSizeRect = NSMakeRect(0, 0, desiredImageSize, desiredImageSize);
+ [iconImage setSize:desiredSizeRect.size];
+
+ [iconImage lockFocus];
+ NSBitmapImageRep* bitmapRep = [[[NSBitmapImageRep alloc]
+ initWithFocusedViewRect:desiredSizeRect]
+ autorelease];
+ [iconImage unlockFocus];
+
+ // we expect the following things to be true about our bitmapRep
+ NS_ENSURE_TRUE(![bitmapRep isPlanar] &&
+ // Not necessarily: on a HiDPI-capable system, we'll get
+ // a 2x bitmap
+ // (unsigned int)[bitmapRep bytesPerPlane] ==
+ // desiredImageSize * desiredImageSize * 4 &&
+ [bitmapRep bitsPerPixel] == 32 &&
+ [bitmapRep samplesPerPixel] == 4 &&
+ [bitmapRep hasAlpha] == YES,
+ NS_ERROR_UNEXPECTED);
+
+ // check what size we actually got, and ensure it isn't too big to return
+ uint32_t actualImageSize = [bitmapRep bytesPerRow] / 4;
+ NS_ENSURE_TRUE(actualImageSize < 256, NS_ERROR_UNEXPECTED);
+
+ // now we can validate the amount of data
+ NS_ENSURE_TRUE((unsigned int)[bitmapRep bytesPerPlane] ==
+ actualImageSize * actualImageSize * 4,
+ NS_ERROR_UNEXPECTED);
+
+ // rgba, pre-multiplied data
+ uint8_t* bitmapRepData = (uint8_t*)[bitmapRep bitmapData];
+
+ // create our buffer
+ int32_t bufferCapacity = 2 + [bitmapRep bytesPerPlane];
+ AutoTArray<uint8_t, 3 + 16 * 16 * 5> iconBuffer; // initial size is for
+ // 16x16
+ iconBuffer.SetLength(bufferCapacity);
+
+ uint8_t* iconBufferPtr = iconBuffer.Elements();
+
+ // write header data into buffer
+ *iconBufferPtr++ = actualImageSize;
+ *iconBufferPtr++ = actualImageSize;
+
+ uint32_t dataCount = [bitmapRep bytesPerPlane];
+ uint32_t index = 0;
+ while (index < dataCount) {
+ // get data from the bitmap
+ uint8_t r = bitmapRepData[index++];
+ uint8_t g = bitmapRepData[index++];
+ uint8_t b = bitmapRepData[index++];
+ uint8_t a = bitmapRepData[index++];
+
+ // write data out to our buffer
+ // non-cairo uses native image format, but the A channel is ignored.
+ // cairo uses ARGB (highest to lowest bits)
+#if MOZ_LITTLE_ENDIAN
+ *iconBufferPtr++ = b;
+ *iconBufferPtr++ = g;
+ *iconBufferPtr++ = r;
+ *iconBufferPtr++ = a;
+#else
+ *iconBufferPtr++ = a;
+ *iconBufferPtr++ = r;
+ *iconBufferPtr++ = g;
+ *iconBufferPtr++ = b;
+#endif
+ }
+
+ NS_ASSERTION(iconBufferPtr == iconBuffer.Elements() + bufferCapacity,
+ "buffer size miscalculation");
+
+ // Now, create a pipe and stuff our data into it
+ nsCOMPtr<nsIInputStream> inStream;
+ nsCOMPtr<nsIOutputStream> outStream;
+ rv = NS_NewPipe(getter_AddRefs(inStream), getter_AddRefs(outStream),
+ bufferCapacity, bufferCapacity, aNonBlocking);
+
+ if (NS_SUCCEEDED(rv)) {
+ uint32_t written;
+ rv = outStream->Write((char*)iconBuffer.Elements(), bufferCapacity,
+ &written);
+ if (NS_SUCCEEDED(rv)) {
+ NS_IF_ADDREF(*_retval = inStream);
+ }
+ }
+
+ // Drop notification callbacks to prevent cycles.
+ mCallbacks = nullptr;
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetLoadFlags(uint32_t* aLoadAttributes)
+{
+ return mPump->GetLoadFlags(aLoadAttributes);
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetLoadFlags(uint32_t aLoadAttributes)
+{
+ return mPump->SetLoadFlags(aLoadAttributes);
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetContentType(nsACString& aContentType)
+{
+ aContentType.AssignLiteral(IMAGE_ICON_MS);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetContentType(const nsACString& aContentType)
+{
+ //It doesn't make sense to set the content-type on this type
+ // of channel...
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetContentCharset(nsACString& aContentCharset)
+{
+ aContentCharset.AssignLiteral(IMAGE_ICON_MS);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetContentCharset(const nsACString& aContentCharset)
+{
+ //It doesn't make sense to set the content-type on this type
+ // of channel...
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetContentDisposition(uint32_t* aContentDisposition)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetContentDisposition(uint32_t aContentDisposition)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ GetContentDispositionFilename(nsAString& aContentDispositionFilename)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ SetContentDispositionFilename(const nsAString& aContentDispositionFilename)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ GetContentDispositionHeader(nsACString& aContentDispositionHeader)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetContentLength(int64_t* aContentLength)
+{
+ *aContentLength = 0;
+ return NS_ERROR_FAILURE;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetContentLength(int64_t aContentLength)
+{
+ NS_NOTREACHED("nsIconChannel::SetContentLength");
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetLoadGroup(nsILoadGroup** aLoadGroup)
+{
+ *aLoadGroup = mLoadGroup;
+ NS_IF_ADDREF(*aLoadGroup);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetLoadGroup(nsILoadGroup* aLoadGroup)
+{
+ mLoadGroup = aLoadGroup;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetOwner(nsISupports** aOwner)
+{
+ *aOwner = mOwner.get();
+ NS_IF_ADDREF(*aOwner);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetOwner(nsISupports* aOwner)
+{
+ mOwner = aOwner;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetLoadInfo(nsILoadInfo** aLoadInfo)
+{
+ NS_IF_ADDREF(*aLoadInfo = mLoadInfo);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::SetLoadInfo(nsILoadInfo* aLoadInfo)
+{
+ mLoadInfo = aLoadInfo;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ GetNotificationCallbacks(nsIInterfaceRequestor** aNotificationCallbacks)
+{
+ *aNotificationCallbacks = mCallbacks.get();
+ NS_IF_ADDREF(*aNotificationCallbacks);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::
+ SetNotificationCallbacks(nsIInterfaceRequestor* aNotificationCallbacks)
+{
+ mCallbacks = aNotificationCallbacks;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsIconChannel::GetSecurityInfo(nsISupports** aSecurityInfo)
+{
+ *aSecurityInfo = nullptr;
+ return NS_OK;
+}
+
diff --git a/image/decoders/icon/moz.build b/image/decoders/icon/moz.build
index 56465dd274..5a173d3169 100644
--- a/image/decoders/icon/moz.build
+++ b/image/decoders/icon/moz.build
@@ -21,5 +21,8 @@ if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
if CONFIG['OS_ARCH'] == 'WINNT':
platform = 'win'
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ platform = 'mac'
+
if platform:
LOCAL_INCLUDES += [platform]
diff --git a/image/decoders/moz.build b/image/decoders/moz.build
index 5a07bf765f..907b81bda8 100644
--- a/image/decoders/moz.build
+++ b/image/decoders/moz.build
@@ -13,6 +13,9 @@ if 'gtk' in toolkit:
if CONFIG['OS_ARCH'] == 'WINNT':
DIRS += ['icon/win', 'icon']
+if toolkit == 'cocoa':
+ DIRS += ['icon/mac', 'icon']
+
UNIFIED_SOURCES += [
'EXIF.cpp',
'iccjpeg.c',
diff --git a/intl/locale/mac/moz.build b/intl/locale/mac/moz.build
new file mode 100644
index 0000000000..422fd3e3cd
--- /dev/null
+++ b/intl/locale/mac/moz.build
@@ -0,0 +1,15 @@
+# -*- 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/.
+
+UNIFIED_SOURCES += [
+ 'nsCollationMacUC.cpp',
+ 'nsDateTimeFormatMac.cpp',
+ 'nsMacCharset.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
+LOCAL_INCLUDES += [
+ '..',
+]
diff --git a/intl/locale/mac/nsCollationMacUC.cpp b/intl/locale/mac/nsCollationMacUC.cpp
new file mode 100644
index 0000000000..d230f4d771
--- /dev/null
+++ b/intl/locale/mac/nsCollationMacUC.cpp
@@ -0,0 +1,253 @@
+/* -*- 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 "nsCollationMacUC.h"
+#include "nsILocaleService.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsIServiceManager.h"
+#include "prmem.h"
+#include "nsString.h"
+
+NS_IMPL_ISUPPORTS(nsCollationMacUC, nsICollation)
+
+nsCollationMacUC::nsCollationMacUC()
+ : mInit(false)
+ , mHasCollator(false)
+ , mLocaleICU(nullptr)
+ , mLastStrength(-1)
+ , mCollatorICU(nullptr)
+{ }
+
+nsCollationMacUC::~nsCollationMacUC()
+{
+#ifdef DEBUG
+ nsresult res =
+#endif
+ CleanUpCollator();
+ NS_ASSERTION(NS_SUCCEEDED(res), "CleanUpCollator failed");
+ if (mLocaleICU) {
+ free(mLocaleICU);
+ mLocaleICU = nullptr;
+ }
+}
+
+nsresult nsCollationMacUC::ConvertStrength(const int32_t aNSStrength,
+ UCollationStrength* aICUStrength,
+ UColAttributeValue* aCaseLevelOut)
+{
+ NS_ENSURE_ARG_POINTER(aICUStrength);
+ NS_ENSURE_TRUE((aNSStrength < 4), NS_ERROR_FAILURE);
+
+ UCollationStrength strength = UCOL_DEFAULT;
+ UColAttributeValue caseLevel = UCOL_OFF;
+ switch (aNSStrength) {
+ case kCollationCaseInSensitive:
+ strength = UCOL_PRIMARY;
+ break;
+ case kCollationCaseInsensitiveAscii:
+ strength = UCOL_SECONDARY;
+ break;
+ case kCollationAccentInsenstive:
+ caseLevel = UCOL_ON;
+ strength = UCOL_PRIMARY;
+ break;
+ case kCollationCaseSensitive:
+ strength = UCOL_TERTIARY;
+ break;
+ default:
+ NS_WARNING("Bad aNSStrength passed to ConvertStrength.");
+ return NS_ERROR_FAILURE;
+ }
+
+ *aICUStrength = strength;
+ *aCaseLevelOut = caseLevel;
+
+ return NS_OK;
+}
+
+nsresult nsCollationMacUC::ConvertLocaleICU(nsILocale* aNSLocale, char** aICULocale)
+{
+ NS_ENSURE_ARG_POINTER(aNSLocale);
+ NS_ENSURE_ARG_POINTER(aICULocale);
+
+ nsAutoString localeString;
+ nsresult res = aNSLocale->GetCategory(NS_LITERAL_STRING("NSILOCALE_COLLATE"), localeString);
+ NS_ENSURE_TRUE(NS_SUCCEEDED(res) && !localeString.IsEmpty(),
+ NS_ERROR_FAILURE);
+ NS_LossyConvertUTF16toASCII tmp(localeString);
+ tmp.ReplaceChar('-', '_');
+ char* locale = (char*)malloc(tmp.Length() + 1);
+ if (!locale) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ strcpy(locale, tmp.get());
+
+ *aICULocale = locale;
+
+ return NS_OK;
+}
+
+nsresult nsCollationMacUC::EnsureCollator(const int32_t newStrength)
+{
+ NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
+ if (mHasCollator && (mLastStrength == newStrength))
+ return NS_OK;
+
+ nsresult res;
+ res = CleanUpCollator();
+ NS_ENSURE_SUCCESS(res, res);
+
+ NS_ENSURE_TRUE(mLocaleICU, NS_ERROR_NOT_INITIALIZED);
+
+ UErrorCode status;
+ status = U_ZERO_ERROR;
+ mCollatorICU = ucol_open(mLocaleICU, &status);
+ NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+
+ UCollationStrength strength;
+ UColAttributeValue caseLevel;
+ res = ConvertStrength(newStrength, &strength, &caseLevel);
+ NS_ENSURE_SUCCESS(res, res);
+
+ status = U_ZERO_ERROR;
+ ucol_setAttribute(mCollatorICU, UCOL_STRENGTH, strength, &status);
+ NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+ ucol_setAttribute(mCollatorICU, UCOL_CASE_LEVEL, caseLevel, &status);
+ NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+ ucol_setAttribute(mCollatorICU, UCOL_ALTERNATE_HANDLING, UCOL_DEFAULT, &status);
+ NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+ ucol_setAttribute(mCollatorICU, UCOL_NUMERIC_COLLATION, UCOL_OFF, &status);
+ NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+ ucol_setAttribute(mCollatorICU, UCOL_NORMALIZATION_MODE, UCOL_ON, &status);
+ NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+ ucol_setAttribute(mCollatorICU, UCOL_CASE_FIRST, UCOL_DEFAULT, &status);
+ NS_ENSURE_TRUE(U_SUCCESS(status), NS_ERROR_FAILURE);
+
+ mHasCollator = true;
+
+ mLastStrength = newStrength;
+ return NS_OK;
+}
+
+nsresult nsCollationMacUC::CleanUpCollator(void)
+{
+ if (mHasCollator) {
+ ucol_close(mCollatorICU);
+ mHasCollator = false;
+ }
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsCollationMacUC::Initialize(nsILocale* locale)
+{
+ NS_ENSURE_TRUE((!mInit), NS_ERROR_ALREADY_INITIALIZED);
+ nsCOMPtr<nsILocale> appLocale;
+
+ nsresult rv;
+ if (!locale) {
+ nsCOMPtr<nsILocaleService> localeService = do_GetService(NS_LOCALESERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ rv = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
+ NS_ENSURE_SUCCESS(rv, rv);
+ locale = appLocale;
+ }
+
+ rv = ConvertLocaleICU(locale, &mLocaleICU);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ mInit = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsCollationMacUC::AllocateRawSortKey(int32_t strength, const nsAString& stringIn,
+ uint8_t** key, uint32_t* outLen)
+{
+ NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_ARG_POINTER(key);
+ NS_ENSURE_ARG_POINTER(outLen);
+
+ nsresult res = EnsureCollator(strength);
+ NS_ENSURE_SUCCESS(res, res);
+
+ uint32_t stringInLen = stringIn.Length();
+
+ const UChar* str = (const UChar*)stringIn.BeginReading();
+
+ int32_t keyLength = ucol_getSortKey(mCollatorICU, str, stringInLen, nullptr, 0);
+ NS_ENSURE_TRUE((stringInLen == 0 || keyLength > 0), NS_ERROR_FAILURE);
+
+ // Since key is freed elsewhere with PR_Free, allocate with PR_Malloc.
+ uint8_t* newKey = (uint8_t*)PR_Malloc(keyLength + 1);
+ if (!newKey) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ keyLength = ucol_getSortKey(mCollatorICU, str, stringInLen, newKey, keyLength + 1);
+ NS_ENSURE_TRUE((stringInLen == 0 || keyLength > 0), NS_ERROR_FAILURE);
+
+ *key = newKey;
+ *outLen = keyLength;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsCollationMacUC::CompareString(int32_t strength, const nsAString& string1,
+ const nsAString& string2, int32_t* result)
+{
+ NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_ARG_POINTER(result);
+ *result = 0;
+
+ nsresult rv = EnsureCollator(strength);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ UCollationResult uresult;
+ uresult = ucol_strcoll(mCollatorICU,
+ (const UChar*)string1.BeginReading(),
+ string1.Length(),
+ (const UChar*)string2.BeginReading(),
+ string2.Length());
+ int32_t res;
+ switch (uresult) {
+ case UCOL_LESS:
+ res = -1;
+ break;
+ case UCOL_EQUAL:
+ res = 0;
+ break;
+ case UCOL_GREATER:
+ res = 1;
+ break;
+ default:
+ MOZ_CRASH("ucol_strcoll returned bad UCollationResult");
+ }
+ *result = res;
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsCollationMacUC::CompareRawSortKey(const uint8_t* key1, uint32_t len1,
+ const uint8_t* key2, uint32_t len2,
+ int32_t* result)
+{
+ NS_ENSURE_TRUE(mInit, NS_ERROR_NOT_INITIALIZED);
+ NS_ENSURE_ARG_POINTER(key1);
+ NS_ENSURE_ARG_POINTER(key2);
+ NS_ENSURE_ARG_POINTER(result);
+ *result = 0;
+
+ int32_t tmpResult = strcmp((const char*)key1, (const char*)key2);
+ int32_t res;
+ if (tmpResult < 0) {
+ res = -1;
+ } else if (tmpResult > 0) {
+ res = 1;
+ } else {
+ res = 0;
+ }
+ *result = res;
+ return NS_OK;
+}
diff --git a/intl/locale/mac/nsCollationMacUC.h b/intl/locale/mac/nsCollationMacUC.h
new file mode 100644
index 0000000000..46bb0145de
--- /dev/null
+++ b/intl/locale/mac/nsCollationMacUC.h
@@ -0,0 +1,44 @@
+/* -*- 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 nsCollationMacUC_h_
+#define nsCollationMacUC_h_
+
+#include "nsICollation.h"
+#include "nsCollation.h"
+#include "mozilla/Attributes.h"
+
+#include "unicode/ucol.h"
+
+class nsCollationMacUC final : public nsICollation {
+
+public:
+ nsCollationMacUC();
+
+ // nsISupports interface
+ NS_DECL_ISUPPORTS
+
+ // nsICollation interface
+ NS_DECL_NSICOLLATION
+
+protected:
+ ~nsCollationMacUC();
+
+ nsresult ConvertLocaleICU(nsILocale* aNSLocale, char** aICULocale);
+ nsresult ConvertStrength(const int32_t aStrength,
+ UCollationStrength* aStrengthOut,
+ UColAttributeValue* aCaseLevelOut);
+ nsresult EnsureCollator(const int32_t newStrength);
+ nsresult CleanUpCollator(void);
+
+private:
+ bool mInit;
+ bool mHasCollator;
+ char* mLocaleICU;
+ int32_t mLastStrength;
+ UCollator* mCollatorICU;
+};
+
+#endif /* nsCollationMacUC_h_ */
diff --git a/intl/locale/mac/nsDateTimeFormatMac.cpp b/intl/locale/mac/nsDateTimeFormatMac.cpp
new file mode 100644
index 0000000000..6ee73292d7
--- /dev/null
+++ b/intl/locale/mac/nsDateTimeFormatMac.cpp
@@ -0,0 +1,266 @@
+/* -*- 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 <CoreFoundation/CoreFoundation.h>
+#include "nsIServiceManager.h"
+#include "nsDateTimeFormatMac.h"
+#include <CoreFoundation/CFDateFormatter.h>
+#include "nsIComponentManager.h"
+#include "nsILocaleService.h"
+#include "nsCRT.h"
+#include "plstr.h"
+#include "nsUnicharUtils.h"
+#include "nsTArray.h"
+
+
+NS_IMPL_ISUPPORTS(nsDateTimeFormatMac, nsIDateTimeFormat)
+
+nsresult nsDateTimeFormatMac::Initialize(nsILocale* locale)
+{
+ nsAutoString localeStr;
+ nsAutoString category(NS_LITERAL_STRING("NSILOCALE_TIME"));
+ nsresult res;
+
+ // use cached info if match with stored locale
+ if (nullptr == locale) {
+ if (!mLocale.IsEmpty() &&
+ mLocale.Equals(mAppLocale, nsCaseInsensitiveStringComparator())) {
+ return NS_OK;
+ }
+ }
+ else {
+ res = locale->GetCategory(category, localeStr);
+ if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
+ if (!mLocale.IsEmpty() &&
+ mLocale.Equals(localeStr,
+ nsCaseInsensitiveStringComparator())) {
+ return NS_OK;
+ }
+ }
+ }
+
+ // get application locale
+ nsCOMPtr<nsILocaleService> localeService =
+ do_GetService(NS_LOCALESERVICE_CONTRACTID, &res);
+ if (NS_SUCCEEDED(res)) {
+ nsCOMPtr<nsILocale> appLocale;
+ res = localeService->GetApplicationLocale(getter_AddRefs(appLocale));
+ if (NS_SUCCEEDED(res)) {
+ res = appLocale->GetCategory(category, localeStr);
+ if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
+ mAppLocale = localeStr; // cache app locale name
+ }
+ }
+ }
+
+ // use app default if no locale specified
+ if (nullptr == locale) {
+ mUseDefaultLocale = true;
+ }
+ else {
+ mUseDefaultLocale = false;
+ res = locale->GetCategory(category, localeStr);
+ }
+
+ if (NS_SUCCEEDED(res) && !localeStr.IsEmpty()) {
+ mLocale.Assign(localeStr); // cache locale name
+ }
+
+ return res;
+}
+
+// performs a locale sensitive date formatting operation on the time_t parameter
+nsresult nsDateTimeFormatMac::FormatTime(nsILocale* locale,
+ const nsDateFormatSelector dateFormatSelector,
+ const nsTimeFormatSelector timeFormatSelector,
+ const time_t timetTime,
+ nsAString& stringOut)
+{
+ struct tm tmTime;
+ return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, localtime_r(&timetTime, &tmTime), stringOut);
+}
+
+// performs a locale sensitive date formatting operation on the struct tm parameter
+nsresult nsDateTimeFormatMac::FormatTMTime(nsILocale* locale,
+ const nsDateFormatSelector dateFormatSelector,
+ const nsTimeFormatSelector timeFormatSelector,
+ const struct tm* tmTime,
+ nsAString& stringOut)
+{
+ nsresult res = NS_OK;
+
+ // set up locale data
+ (void) Initialize(locale);
+
+ // return, nothing to format
+ if (dateFormatSelector == kDateFormatNone && timeFormatSelector == kTimeFormatNone) {
+ stringOut.Truncate();
+ return NS_OK;
+ }
+
+ NS_ASSERTION(tmTime->tm_mon >= 0, "tm is not set correctly");
+ NS_ASSERTION(tmTime->tm_mday >= 1, "tm is not set correctly");
+ NS_ASSERTION(tmTime->tm_hour >= 0, "tm is not set correctly");
+ NS_ASSERTION(tmTime->tm_min >= 0, "tm is not set correctly");
+ NS_ASSERTION(tmTime->tm_sec >= 0, "tm is not set correctly");
+ NS_ASSERTION(tmTime->tm_wday >= 0, "tm is not set correctly");
+
+ // Got the locale for the formatter:
+ CFLocaleRef formatterLocale;
+ if (!locale) {
+ formatterLocale = CFLocaleCopyCurrent();
+ } else {
+ CFStringRef localeStr = CFStringCreateWithCharacters(nullptr,
+ reinterpret_cast<const UniChar*>(mLocale.get()),
+ mLocale.Length());
+ formatterLocale = CFLocaleCreate(nullptr, localeStr);
+ CFRelease(localeStr);
+ }
+
+ // Get the date style for the formatter:
+ CFDateFormatterStyle dateStyle;
+ switch (dateFormatSelector) {
+ case kDateFormatLong:
+ dateStyle = kCFDateFormatterLongStyle;
+ break;
+ case kDateFormatShort:
+ dateStyle = kCFDateFormatterShortStyle;
+ break;
+ case kDateFormatYearMonth:
+ case kDateFormatWeekday:
+ dateStyle = kCFDateFormatterNoStyle; // formats handled below
+ break;
+ case kDateFormatNone:
+ dateStyle = kCFDateFormatterNoStyle;
+ break;
+ default:
+ NS_ERROR("Unknown nsDateFormatSelector");
+ res = NS_ERROR_FAILURE;
+ dateStyle = kCFDateFormatterNoStyle;
+ }
+
+ // Get the time style for the formatter:
+ CFDateFormatterStyle timeStyle;
+ switch (timeFormatSelector) {
+ case kTimeFormatSeconds:
+ case kTimeFormatSecondsForce24Hour: // 24 hour part fixed below
+ timeStyle = kCFDateFormatterMediumStyle;
+ break;
+ case kTimeFormatNoSeconds:
+ case kTimeFormatNoSecondsForce24Hour: // 24 hour part fixed below
+ timeStyle = kCFDateFormatterShortStyle;
+ break;
+ case kTimeFormatNone:
+ timeStyle = kCFDateFormatterNoStyle;
+ break;
+ default:
+ NS_ERROR("Unknown nsTimeFormatSelector");
+ res = NS_ERROR_FAILURE;
+ timeStyle = kCFDateFormatterNoStyle;
+ }
+
+ // Create the formatter and fix up its formatting as necessary:
+ CFDateFormatterRef formatter =
+ CFDateFormatterCreate(nullptr, formatterLocale, dateStyle, timeStyle);
+
+ CFRelease(formatterLocale);
+
+ if (dateFormatSelector == kDateFormatYearMonth ||
+ dateFormatSelector == kDateFormatWeekday) {
+ CFStringRef dateFormat =
+ dateFormatSelector == kDateFormatYearMonth ? CFSTR("yyyy/MM ") : CFSTR("EEE ");
+
+ CFStringRef oldFormat = CFDateFormatterGetFormat(formatter);
+ CFMutableStringRef newFormat = CFStringCreateMutableCopy(nullptr, 0, oldFormat);
+ CFStringInsert(newFormat, 0, dateFormat);
+ CFDateFormatterSetFormat(formatter, newFormat);
+ CFRelease(newFormat); // note we don't own oldFormat
+ }
+
+ if (timeFormatSelector == kTimeFormatSecondsForce24Hour ||
+ timeFormatSelector == kTimeFormatNoSecondsForce24Hour) {
+ // Replace "h" with "H", and remove "a":
+ CFStringRef oldFormat = CFDateFormatterGetFormat(formatter);
+ CFMutableStringRef newFormat = CFStringCreateMutableCopy(nullptr, 0, oldFormat);
+ CFIndex replaceCount = CFStringFindAndReplace(newFormat,
+ CFSTR("h"), CFSTR("H"),
+ CFRangeMake(0, CFStringGetLength(newFormat)),
+ 0);
+ NS_ASSERTION(replaceCount <= 2, "Unexpected number of \"h\" occurrences");
+ replaceCount = CFStringFindAndReplace(newFormat,
+ CFSTR("a"), CFSTR(""),
+ CFRangeMake(0, CFStringGetLength(newFormat)),
+ 0);
+ NS_ASSERTION(replaceCount <= 1, "Unexpected number of \"a\" occurrences");
+ CFDateFormatterSetFormat(formatter, newFormat);
+ CFRelease(newFormat); // note we don't own oldFormat
+ }
+
+ // Now get the formatted date:
+ CFGregorianDate date;
+ date.second = tmTime->tm_sec;
+ date.minute = tmTime->tm_min;
+ date.hour = tmTime->tm_hour;
+ date.day = tmTime->tm_mday; // Mac is 1-based, tm is 1-based
+ date.month = tmTime->tm_mon + 1; // Mac is 1-based, tm is 0-based
+ date.year = tmTime->tm_year + 1900;
+
+ CFTimeZoneRef timeZone = CFTimeZoneCopySystem(); // tmTime is in local time
+ CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime(date, timeZone);
+ CFRelease(timeZone);
+
+ CFStringRef formattedDate = CFDateFormatterCreateStringWithAbsoluteTime(nullptr,
+ formatter,
+ absTime);
+
+ CFIndex stringLen = CFStringGetLength(formattedDate);
+
+ AutoTArray<UniChar, 256> stringBuffer;
+ stringBuffer.SetLength(stringLen + 1);
+ CFStringGetCharacters(formattedDate, CFRangeMake(0, stringLen), stringBuffer.Elements());
+ stringOut.Assign(reinterpret_cast<char16_t*>(stringBuffer.Elements()), stringLen);
+
+ CFRelease(formattedDate);
+ CFRelease(formatter);
+
+ return res;
+}
+
+// performs a locale sensitive date formatting operation on the PRTime parameter
+nsresult nsDateTimeFormatMac::FormatPRTime(nsILocale* locale,
+ const nsDateFormatSelector dateFormatSelector,
+ const nsTimeFormatSelector timeFormatSelector,
+ const PRTime prTime,
+ nsAString& stringOut)
+{
+ PRExplodedTime explodedTime;
+ PR_ExplodeTime(prTime, PR_LocalTimeParameters, &explodedTime);
+
+ return FormatPRExplodedTime(locale, dateFormatSelector, timeFormatSelector, &explodedTime, stringOut);
+}
+
+// performs a locale sensitive date formatting operation on the PRExplodedTime parameter
+nsresult nsDateTimeFormatMac::FormatPRExplodedTime(nsILocale* locale,
+ const nsDateFormatSelector dateFormatSelector,
+ const nsTimeFormatSelector timeFormatSelector,
+ const PRExplodedTime* explodedTime,
+ nsAString& stringOut)
+{
+ struct tm tmTime;
+ memset( &tmTime, 0, sizeof(tmTime) );
+
+ tmTime.tm_yday = explodedTime->tm_yday;
+ tmTime.tm_wday = explodedTime->tm_wday;
+ tmTime.tm_year = explodedTime->tm_year;
+ tmTime.tm_year -= 1900;
+ tmTime.tm_mon = explodedTime->tm_month;
+ tmTime.tm_mday = explodedTime->tm_mday;
+ tmTime.tm_hour = explodedTime->tm_hour;
+ tmTime.tm_min = explodedTime->tm_min;
+ tmTime.tm_sec = explodedTime->tm_sec;
+
+ return FormatTMTime(locale, dateFormatSelector, timeFormatSelector, &tmTime, stringOut);
+}
+
diff --git a/intl/locale/mac/nsDateTimeFormatMac.h b/intl/locale/mac/nsDateTimeFormatMac.h
new file mode 100644
index 0000000000..dfdf703780
--- /dev/null
+++ b/intl/locale/mac/nsDateTimeFormatMac.h
@@ -0,0 +1,61 @@
+
+/* -*- 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 nsDateTimeFormatMac_h__
+#define nsDateTimeFormatMac_h__
+
+
+#include "nsCOMPtr.h"
+#include "nsIDateTimeFormat.h"
+
+
+class nsDateTimeFormatMac : public nsIDateTimeFormat {
+
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // performs a locale sensitive date formatting operation on the time_t parameter
+ NS_IMETHOD FormatTime(nsILocale* locale,
+ const nsDateFormatSelector dateFormatSelector,
+ const nsTimeFormatSelector timeFormatSelector,
+ const time_t timetTime,
+ nsAString& stringOut) override;
+
+ // performs a locale sensitive date formatting operation on the struct tm parameter
+ NS_IMETHOD FormatTMTime(nsILocale* locale,
+ const nsDateFormatSelector dateFormatSelector,
+ const nsTimeFormatSelector timeFormatSelector,
+ const struct tm* tmTime,
+ nsAString& stringOut) override;
+ // performs a locale sensitive date formatting operation on the PRTime parameter
+ NS_IMETHOD FormatPRTime(nsILocale* locale,
+ const nsDateFormatSelector dateFormatSelector,
+ const nsTimeFormatSelector timeFormatSelector,
+ const PRTime prTime,
+ nsAString& stringOut) override;
+
+ // performs a locale sensitive date formatting operation on the PRExplodedTime parameter
+ NS_IMETHOD FormatPRExplodedTime(nsILocale* locale,
+ const nsDateFormatSelector dateFormatSelector,
+ const nsTimeFormatSelector timeFormatSelector,
+ const PRExplodedTime* explodedTime,
+ nsAString& stringOut) override;
+
+ nsDateTimeFormatMac() {}
+
+protected:
+ virtual ~nsDateTimeFormatMac() {}
+
+private:
+ // init this interface to a specified locale
+ NS_IMETHOD Initialize(nsILocale* locale);
+
+ nsString mLocale;
+ nsString mAppLocale;
+ bool mUseDefaultLocale;
+};
+
+#endif /* nsDateTimeFormatMac_h__ */
diff --git a/intl/locale/mac/nsMacCharset.cpp b/intl/locale/mac/nsMacCharset.cpp
new file mode 100644
index 0000000000..956560fba8
--- /dev/null
+++ b/intl/locale/mac/nsMacCharset.cpp
@@ -0,0 +1,59 @@
+/* -*- 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 <Carbon/Carbon.h>
+#include "nsIPlatformCharset.h"
+#include "nsCOMPtr.h"
+#include "nsIServiceManager.h"
+#include "nsReadableUtils.h"
+#include "nsPlatformCharset.h"
+#include "nsEncoderDecoderUtils.h"
+
+NS_IMPL_ISUPPORTS(nsPlatformCharset, nsIPlatformCharset)
+
+nsPlatformCharset::nsPlatformCharset()
+{
+}
+nsPlatformCharset::~nsPlatformCharset()
+{
+}
+
+NS_IMETHODIMP
+nsPlatformCharset::GetCharset(nsPlatformCharsetSel selector, nsACString& oResult)
+{
+ oResult.AssignLiteral("UTF-8");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPlatformCharset::GetDefaultCharsetForLocale(const nsAString& localeName, nsACString &oResult)
+{
+ oResult.AssignLiteral("UTF-8");
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsPlatformCharset::Init()
+{
+ return NS_OK;
+}
+
+nsresult
+nsPlatformCharset::MapToCharset(nsAString& inANSICodePage, nsACString& outCharset)
+{
+ return NS_OK;
+}
+
+nsresult
+nsPlatformCharset::InitGetCharset(nsACString &oString)
+{
+ return NS_OK;
+}
+
+nsresult
+nsPlatformCharset::VerifyCharset(nsCString &aCharset)
+{
+ return NS_OK;
+}
diff --git a/intl/locale/moz.build b/intl/locale/moz.build
index 4b3b3c0b88..94a30873ba 100644
--- a/intl/locale/moz.build
+++ b/intl/locale/moz.build
@@ -9,6 +9,8 @@ toolkit = CONFIG['MOZ_WIDGET_TOOLKIT']
if toolkit == 'windows':
DIRS += ['windows']
+elif toolkit == 'cocoa':
+ DIRS += ['mac']
else:
DIRS += ['unix']
diff --git a/intl/lwbrk/moz.build b/intl/lwbrk/moz.build
index dc7c2df1a2..da701be5e5 100644
--- a/intl/lwbrk/moz.build
+++ b/intl/lwbrk/moz.build
@@ -32,6 +32,10 @@ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
SOURCES += [
'nsUniscribeBreaker.cpp',
]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += [
+ 'nsCarbonBreaker.cpp',
+ ]
else:
SOURCES += [
'nsRuleBreaker.cpp',
diff --git a/intl/lwbrk/nsCarbonBreaker.cpp b/intl/lwbrk/nsCarbonBreaker.cpp
new file mode 100644
index 0000000000..1b37bc1298
--- /dev/null
+++ b/intl/lwbrk/nsCarbonBreaker.cpp
@@ -0,0 +1,44 @@
+/* -*- 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 <CoreFoundation/CoreFoundation.h>
+#include <stdint.h>
+#include "nsDebug.h"
+#include "nscore.h"
+
+void
+NS_GetComplexLineBreaks(const char16_t* aText, uint32_t aLength,
+ uint8_t* aBreakBefore)
+{
+ NS_ASSERTION(aText, "aText shouldn't be null");
+
+ memset(aBreakBefore, 0, aLength * sizeof(uint8_t));
+
+ CFStringRef str = ::CFStringCreateWithCharactersNoCopy(kCFAllocatorDefault, reinterpret_cast<const UniChar*>(aText), aLength, kCFAllocatorNull);
+ if (!str) {
+ return;
+ }
+
+ CFStringTokenizerRef st = ::CFStringTokenizerCreate(kCFAllocatorDefault, str,
+ ::CFRangeMake(0, aLength),
+ kCFStringTokenizerUnitLineBreak,
+ nullptr);
+ if (!st) {
+ ::CFRelease(str);
+ return;
+ }
+
+ CFStringTokenizerTokenType tt = ::CFStringTokenizerAdvanceToNextToken(st);
+ while (tt != kCFStringTokenizerTokenNone) {
+ CFRange r = ::CFStringTokenizerGetCurrentTokenRange(st);
+ if (r.location != 0) { // Ignore leading edge
+ aBreakBefore[r.location] = true;
+ }
+ tt = CFStringTokenizerAdvanceToNextToken(st);
+ }
+
+ ::CFRelease(st);
+ ::CFRelease(str);
+}
diff --git a/js/xpconnect/shell/moz.build b/js/xpconnect/shell/moz.build
index 3361b7d810..c1789fdc71 100644
--- a/js/xpconnect/shell/moz.build
+++ b/js/xpconnect/shell/moz.build
@@ -14,6 +14,11 @@ SOURCES += [
'xpcshell.cpp',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'xpcshellMacUtils.mm',
+ ]
+
include('/ipc/chromium/chromium-config.mozbuild')
LOCAL_INCLUDES += [
diff --git a/js/xpconnect/shell/xpcshellMacUtils.h b/js/xpconnect/shell/xpcshellMacUtils.h
new file mode 100644
index 0000000000..2e6b5cb359
--- /dev/null
+++ b/js/xpconnect/shell/xpcshellMacUtils.h
@@ -0,0 +1,8 @@
+/* -*- 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/. */
+
+// Functions to setup and release the Mac memory pool
+void InitAutoreleasePool();
+void FinishAutoreleasePool();
diff --git a/js/xpconnect/shell/xpcshellMacUtils.mm b/js/xpconnect/shell/xpcshellMacUtils.mm
new file mode 100644
index 0000000000..61d6a9ea9a
--- /dev/null
+++ b/js/xpconnect/shell/xpcshellMacUtils.mm
@@ -0,0 +1,18 @@
+/* -*- 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 <Foundation/Foundation.h>
+
+static NSAutoreleasePool *pool = NULL;
+
+void InitAutoreleasePool()
+{
+ pool = [[NSAutoreleasePool alloc] init];
+}
+
+void FinishAutoreleasePool()
+{
+ [pool release];
+}
diff --git a/layout/build/moz.build b/layout/build/moz.build
index 3e76edc491..d7996af8d9 100644
--- a/layout/build/moz.build
+++ b/layout/build/moz.build
@@ -63,6 +63,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
LOCAL_INCLUDES += [
'/dom/system/windows',
]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/dom/system/mac',
+ ]
elif 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
LOCAL_INCLUDES += [
'/widget/gtk',
diff --git a/layout/reftests/reftest.list b/layout/reftests/reftest.list
index 15840f38e5..90320cba30 100644
--- a/layout/reftests/reftest.list
+++ b/layout/reftests/reftest.list
@@ -347,6 +347,9 @@ include text-svgglyphs/reftest.list
# text-transform/
include text-transform/reftest.list
+# theme (osx)
+include ../../toolkit/themes/osx/reftests/reftest.list
+
include ../../toolkit/content/tests/reftests/reftest.list
# -moz-transform/
diff --git a/mailnews/base/ispdata/moz.build b/mailnews/base/ispdata/moz.build
index 363c2ba0c2..7de5dc2e32 100644
--- a/mailnews/base/ispdata/moz.build
+++ b/mailnews/base/ispdata/moz.build
@@ -2,4 +2,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/.
-# Stub. This was configuring special snowflake OSX stuff. \ No newline at end of file
+# Disable movemail for Thunderbird on OSX
+if CONFIG['MOZ_MOVEMAIL'] and not (CONFIG['MOZ_THUNDERBIRD'] and CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa'):
+ FINAL_TARGET_FILES.isp += ['movemail.rdf']
diff --git a/mailnews/base/src/moz.build b/mailnews/base/src/moz.build
index 65853daf96..91bf235c34 100644
--- a/mailnews/base/src/moz.build
+++ b/mailnews/base/src/moz.build
@@ -61,6 +61,8 @@ if CONFIG['OS_ARCH'] == 'WINNT':
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3', 'qt'):
SOURCES += ['nsMessengerUnixIntegration.cpp']
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += ['nsMessengerOSXIntegration.mm']
EXTRA_COMPONENTS += [
'folderLookupService.js',
diff --git a/mailnews/base/src/nsMessengerOSXIntegration.h b/mailnews/base/src/nsMessengerOSXIntegration.h
new file mode 100644
index 0000000000..91f42f2a2a
--- /dev/null
+++ b/mailnews/base/src/nsMessengerOSXIntegration.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __nsMessengerOSXIntegration_h
+#define __nsMessengerOSXIntegration_h
+
+#include "nsIMessengerOSIntegration.h"
+#include "nsIFolderListener.h"
+#include "nsIAtom.h"
+#include "nsITimer.h"
+#include "nsCOMPtr.h"
+#include "nsStringGlue.h"
+#include "nsIObserver.h"
+#include "nsIAlertsService.h"
+#include "mozINewMailListener.h"
+
+#define NS_MESSENGEROSXINTEGRATION_CID \
+ {0xaa83266, 0x4225, 0x4c4b, \
+ {0x93, 0xf8, 0x94, 0xb1, 0x82, 0x58, 0x6f, 0x93}}
+
+class nsIStringBundle;
+
+class nsMessengerOSXIntegration : public nsIMessengerOSIntegration,
+ public nsIFolderListener,
+ public nsIObserver,
+ public mozINewMailListener
+{
+public:
+ nsMessengerOSXIntegration();
+ virtual nsresult Init();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIMESSENGEROSINTEGRATION
+ NS_DECL_NSIFOLDERLISTENER
+ NS_DECL_NSIOBSERVER
+ NS_DECL_MOZINEWMAILLISTENER
+
+private:
+ virtual ~nsMessengerOSXIntegration();
+
+ nsCOMPtr<nsIAtom> mBiffStateAtom;
+ nsCOMPtr<nsIAtom> mNewMailReceivedAtom;
+ nsresult ShowAlertMessage(const nsAString& aAlertTitle, const nsAString& aAlertText, const nsACString& aFolderURI);
+ nsresult OnAlertFinished();
+ nsresult OnAlertClicked(const char16_t * aAlertCookie);
+#ifdef MOZ_SUITE
+ nsresult OnAlertClickedSimple();
+#endif
+ nsresult GetStringBundle(nsIStringBundle **aBundle);
+ void FillToolTipInfo(nsIMsgFolder *aFolder, int32_t aNewCount);
+ nsresult GetFirstFolderWithNewMail(nsIMsgFolder* aFolder, nsCString& aFolderURI);
+ nsresult BadgeDockIcon();
+ nsresult RestoreDockIcon();
+ nsresult BounceDockIcon();
+ nsresult GetNewMailAuthors(nsIMsgFolder* aFolder, nsString& aAuthors, int32_t aNewCount, int32_t* aNotDisplayed);
+
+ int32_t mUnreadTotal;
+ int32_t mUnreadChat;
+};
+
+#endif // __nsMessengerOSXIntegration_h
diff --git a/mailnews/base/src/nsMessengerOSXIntegration.mm b/mailnews/base/src/nsMessengerOSXIntegration.mm
new file mode 100644
index 0000000000..a38716e179
--- /dev/null
+++ b/mailnews/base/src/nsMessengerOSXIntegration.mm
@@ -0,0 +1,700 @@
+/* -*- 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 "nscore.h"
+#include "nsMsgUtils.h"
+#include "nsArrayUtils.h"
+#include "nsMessengerOSXIntegration.h"
+#include "nsIMsgMailSession.h"
+#include "nsIMsgIncomingServer.h"
+#include "nsIMsgIdentity.h"
+#include "nsIMsgAccount.h"
+#include "nsIMsgFolder.h"
+#include "nsCOMPtr.h"
+#include "nsMsgBaseCID.h"
+#include "nsMsgFolderFlags.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsIDirectoryService.h"
+#include "MailNewsTypes.h"
+#include "nsIWindowMediator.h"
+#include "nsIDOMChromeWindow.h"
+#include "mozIDOMWindow.h"
+#include "nsPIDOMWindow.h"
+#include "nsIDocShell.h"
+#include "nsIBaseWindow.h"
+#include "nsIWidget.h"
+#include "nsIObserverService.h"
+#include "nsIPrefService.h"
+#include "nsIPrefBranch.h"
+#include "nsIMessengerWindowService.h"
+#include "prprf.h"
+#include "nsIAlertsService.h"
+#include "nsIStringBundle.h"
+#include "nsToolkitCompsCID.h"
+#include "nsIMsgDatabase.h"
+#include "nsIMsgHdr.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIWindowWatcher.h"
+#include "nsMsgLocalCID.h"
+#include "nsIMsgMailNewsUrl.h"
+#include "nsIMsgWindow.h"
+#include "nsIMsgAccountManager.h"
+#include "nsIMessenger.h"
+#include "nsObjCExceptions.h"
+#include "nsComponentManagerUtils.h"
+#include "nsServiceManagerUtils.h"
+#include "mozINewMailNotificationService.h"
+#include "mozilla/mailnews/MimeHeaderParser.h"
+
+#include <Carbon/Carbon.h>
+#import <Cocoa/Cocoa.h>
+
+#define kBiffAnimateDockIconPref "mail.biff.animate_dock_icon"
+#define kMaxDisplayCount 10
+
+using namespace mozilla::mailnews;
+
+// HACK: Limitations in Focus/SetFocus on Mac (see bug 465446)
+nsresult FocusAppNative()
+{
+ ProcessSerialNumber psn;
+
+ if (::GetCurrentProcess(&psn) != 0)
+ return NS_ERROR_FAILURE;
+
+ if (::SetFrontProcess(&psn) != 0)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+}
+
+static void openMailWindow(const nsCString& aUri)
+{
+ nsresult rv;
+ nsCOMPtr<nsIMsgMailSession> mailSession ( do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return;
+
+ nsCOMPtr<nsIMsgWindow> topMostMsgWindow;
+ rv = mailSession->GetTopmostMsgWindow(getter_AddRefs(topMostMsgWindow));
+ if (topMostMsgWindow)
+ {
+ if (!aUri.IsEmpty())
+ {
+ nsCOMPtr<nsIMsgMailNewsUrl> msgUri(do_CreateInstance(NS_MAILBOXURL_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return;
+
+ rv = msgUri->SetSpec(aUri);
+ if (NS_FAILED(rv))
+ return;
+
+ bool isMessageUri = false;
+ msgUri->GetIsMessageUri(&isMessageUri);
+ if (isMessageUri)
+ {
+ nsCOMPtr<nsIWindowWatcher> wwatch(do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return;
+
+ // SeaMonkey only supports message uris, whereas Thunderbird only
+ // supports message headers. This should be simplified/removed when
+ // bug 507593 is implemented.
+#ifdef MOZ_SUITE
+ nsCOMPtr<mozIDOMWindowProxy> newWindow;
+ wwatch->OpenWindow(0, "chrome://messenger/content/messageWindow.xul",
+ "_blank", "all,chrome,dialog=no,status,toolbar", msgUri,
+ getter_AddRefs(newWindow));
+#else
+ nsCOMPtr<nsIMessenger> messenger(do_CreateInstance(NS_MESSENGER_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ return;
+
+ nsCOMPtr<nsIMsgDBHdr> msgHdr;
+ messenger->MsgHdrFromURI(aUri, getter_AddRefs(msgHdr));
+ if (msgHdr)
+ {
+ nsCOMPtr<mozIDOMWindowProxy> newWindow;
+ wwatch->OpenWindow(0, "chrome://messenger/content/messageWindow.xul",
+ "_blank", "all,chrome,dialog=no,status,toolbar", msgHdr,
+ getter_AddRefs(newWindow));
+ }
+#endif
+ }
+ else
+ {
+ nsCOMPtr<nsIMsgWindowCommands> windowCommands;
+ topMostMsgWindow->GetWindowCommands(getter_AddRefs(windowCommands));
+ if (windowCommands)
+ windowCommands->SelectFolder(aUri);
+ }
+ }
+
+ FocusAppNative();
+ nsCOMPtr<mozIDOMWindowProxy> domWindow;
+ topMostMsgWindow->GetDomWindow(getter_AddRefs(domWindow));
+ if (domWindow) {
+ nsCOMPtr<nsPIDOMWindowOuter> privateWindow = nsPIDOMWindowOuter::From(domWindow);
+ privateWindow->Focus();
+ }
+ }
+ else
+ {
+ // the user doesn't have a mail window open already so open one for them...
+ nsCOMPtr<nsIMessengerWindowService> messengerWindowService =
+ do_GetService(NS_MESSENGERWINDOWSERVICE_CONTRACTID);
+ // if we want to preselect the first account with new mail,
+ // here is where we would try to generate a uri to pass in
+ // (and add code to the messenger window service to make that work)
+ if (messengerWindowService)
+ messengerWindowService->OpenMessengerWindowWithUri(
+ "mail:3pane", aUri.get(), nsMsgKey_None);
+ }
+}
+
+nsMessengerOSXIntegration::nsMessengerOSXIntegration()
+{
+ mBiffStateAtom = MsgGetAtom("BiffState");
+ mNewMailReceivedAtom = MsgGetAtom("NewMailReceived");
+ mUnreadTotal = 0;
+}
+
+nsMessengerOSXIntegration::~nsMessengerOSXIntegration()
+{
+ RestoreDockIcon();
+}
+
+NS_IMPL_ADDREF(nsMessengerOSXIntegration)
+NS_IMPL_RELEASE(nsMessengerOSXIntegration)
+
+NS_INTERFACE_MAP_BEGIN(nsMessengerOSXIntegration)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIMessengerOSIntegration)
+ NS_INTERFACE_MAP_ENTRY(nsIMessengerOSIntegration)
+ NS_INTERFACE_MAP_ENTRY(nsIFolderListener)
+ NS_INTERFACE_MAP_ENTRY(nsIObserver)
+ NS_INTERFACE_MAP_ENTRY(mozINewMailListener)
+NS_INTERFACE_MAP_END
+
+
+nsresult
+nsMessengerOSXIntegration::Init()
+{
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+ return observerService->AddObserver(this, "mail-startup-done", false);
+}
+
+NS_IMETHODIMP
+nsMessengerOSXIntegration::OnItemPropertyChanged(nsIMsgFolder *, nsIAtom *, char const *, char const *)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMessengerOSXIntegration::OnItemUnicharPropertyChanged(nsIMsgFolder *, nsIAtom *, const char16_t *, const char16_t *)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMessengerOSXIntegration::OnItemRemoved(nsIMsgFolder *, nsISupports *)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMessengerOSXIntegration::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
+{
+ if (!strcmp(aTopic, "alertfinished"))
+ return OnAlertFinished();
+
+ if (!strcmp(aTopic, "alertclickcallback"))
+ return OnAlertClicked(aData);
+
+#ifdef MOZ_SUITE
+ // SeaMonkey does most of the GUI work in JS code when clicking on a mail
+ // notification, so it needs an extra function here
+ if (!strcmp(aTopic, "alertclicksimplecallback"))
+ return OnAlertClickedSimple();
+#endif
+
+ if (!strcmp(aTopic, "mail-startup-done")) {
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> observerService = do_GetService("@mozilla.org/observer-service;1", &rv);
+ if (NS_SUCCEEDED(rv)) {
+ observerService->RemoveObserver(this, "mail-startup-done");
+
+ nsCOMPtr<nsIPrefBranch> pref(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ }
+
+ // Register with the new mail service for changes to the unread message count
+ nsCOMPtr<mozINewMailNotificationService> newmail
+ = do_GetService(MOZ_NEWMAILNOTIFICATIONSERVICE_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv); // This should really be an assert with a helpful message
+ rv = newmail->AddListener(this, mozINewMailNotificationService::count);
+ NS_ENSURE_SUCCESS(rv, rv); // This should really be an assert with a helpful message
+
+ // Get the initial unread count. Ignore return value; if code above didn't fail, this won't
+ rv = newmail->GetMessageCount(&mUnreadTotal);
+ BadgeDockIcon();
+
+ // register with the mail sesson for folder events
+ // we care about new count, biff status
+ nsCOMPtr<nsIMsgMailSession> mailSession = do_GetService(NS_MSGMAILSESSION_CONTRACTID, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return mailSession->AddFolderListener(this, nsIFolderListener::boolPropertyChanged | nsIFolderListener::intPropertyChanged);
+ }
+
+ return NS_OK;
+}
+
+nsresult
+nsMessengerOSXIntegration::GetStringBundle(nsIStringBundle **aBundle)
+{
+ NS_ENSURE_ARG_POINTER(aBundle);
+ nsresult rv;
+ nsCOMPtr<nsIStringBundleService> bundleService = do_GetService(NS_STRINGBUNDLE_CONTRACTID, &rv);
+ nsCOMPtr<nsIStringBundle> bundle;
+ if (bundleService && NS_SUCCEEDED(rv))
+ bundleService->CreateBundle("chrome://messenger/locale/messenger.properties", getter_AddRefs(bundle));
+ bundle.swap(*aBundle);
+ return rv;
+}
+
+void
+nsMessengerOSXIntegration::FillToolTipInfo(nsIMsgFolder *aFolder, int32_t aNewCount)
+{
+ if (aFolder)
+ {
+ nsString authors;
+ int32_t numNotDisplayed;
+ nsresult rv = GetNewMailAuthors(aFolder, authors, aNewCount, &numNotDisplayed);
+
+ // If all senders are vetoed, the authors string will be empty.
+ if (NS_FAILED(rv) || authors.IsEmpty())
+ return;
+
+ // If this isn't the root folder, get it so we can report for it.
+ // GetRootFolder always returns the server's root, so calling on the root itself is fine.
+ nsCOMPtr<nsIMsgFolder> rootFolder;
+ aFolder->GetRootFolder(getter_AddRefs(rootFolder));
+ if (!rootFolder)
+ return;
+
+ nsString accountName;
+ rootFolder->GetPrettiestName(accountName);
+
+ nsCOMPtr<nsIStringBundle> bundle;
+ GetStringBundle(getter_AddRefs(bundle));
+ if (bundle)
+ {
+ nsAutoString numNewMsgsText;
+ numNewMsgsText.AppendInt(aNewCount);
+ nsString finalText;
+ nsCString uri;
+ aFolder->GetURI(uri);
+
+ if (numNotDisplayed > 0)
+ {
+ nsAutoString numNotDisplayedText;
+ numNotDisplayedText.AppendInt(numNotDisplayed);
+ const char16_t *formatStrings[3] = { numNewMsgsText.get(), authors.get(), numNotDisplayedText.get() };
+ bundle->FormatStringFromName(u"macBiffNotification_messages_extra",
+ formatStrings,
+ 3,
+ getter_Copies(finalText));
+ }
+ else
+ {
+ const char16_t *formatStrings[2] = { numNewMsgsText.get(), authors.get() };
+
+ if (aNewCount == 1)
+ {
+ bundle->FormatStringFromName(u"macBiffNotification_message",
+ formatStrings,
+ 2,
+ getter_Copies(finalText));
+ // Since there is only 1 message, use the most recent mail's URI instead of the folder's
+ nsCOMPtr<nsIMsgDatabase> db;
+ rv = aFolder->GetMsgDatabase(getter_AddRefs(db));
+ if (NS_SUCCEEDED(rv) && db)
+ {
+ uint32_t numNewKeys;
+ uint32_t *newMessageKeys;
+ rv = db->GetNewList(&numNewKeys, &newMessageKeys);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsCOMPtr<nsIMsgDBHdr> hdr;
+ rv = db->GetMsgHdrForKey(newMessageKeys[numNewKeys - 1],
+ getter_AddRefs(hdr));
+ if (NS_SUCCEEDED(rv) && hdr)
+ aFolder->GetUriForMsg(hdr, uri);
+ }
+ NS_Free(newMessageKeys);
+ }
+ }
+ else
+ bundle->FormatStringFromName(u"macBiffNotification_messages",
+ formatStrings,
+ 2,
+ getter_Copies(finalText));
+ }
+ ShowAlertMessage(accountName, finalText, uri);
+ } // if we got a bundle
+ } // if we got a folder
+}
+
+nsresult
+nsMessengerOSXIntegration::ShowAlertMessage(const nsAString& aAlertTitle,
+ const nsAString& aAlertText,
+ const nsACString& aFolderURI)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIAlertsService> alertsService (do_GetService(NS_ALERTSERVICE_CONTRACTID, &rv));
+ // If we have an nsIAlertsService implementation, use it:
+ if (NS_SUCCEEDED(rv))
+ {
+ alertsService->ShowAlertNotification(EmptyString(),
+ aAlertTitle, aAlertText, true,
+ NS_ConvertASCIItoUTF16(aFolderURI),
+ this, EmptyString(),
+ NS_LITERAL_STRING("auto"),
+ EmptyString(), EmptyString(),
+ nullptr,
+ false,
+ false);
+ }
+
+ BounceDockIcon();
+
+ if (NS_FAILED(rv))
+ OnAlertFinished();
+
+ return rv;
+}
+
+NS_IMETHODIMP
+nsMessengerOSXIntegration::OnItemIntPropertyChanged(nsIMsgFolder *aFolder,
+ nsIAtom *aProperty,
+ int64_t aOldValue,
+ int64_t aNewValue)
+{
+ // if we got new mail show an alert
+ if (aNewValue == nsIMsgFolder::nsMsgBiffState_NewMail)
+ {
+ bool performingBiff = false;
+ nsCOMPtr<nsIMsgIncomingServer> server;
+ aFolder->GetServer(getter_AddRefs(server));
+ if (server)
+ server->GetPerformingBiff(&performingBiff);
+ if (!performingBiff)
+ return NS_OK; // kick out right now...
+
+ // Biff happens for the root folder, but we want info for the child with new mail
+ nsCString folderUri;
+ GetFirstFolderWithNewMail(aFolder, folderUri);
+ nsCOMPtr<nsIMsgFolder> childFolder;
+ nsresult rv = aFolder->GetChildWithURI(folderUri, true, true,
+ getter_AddRefs(childFolder));
+ if (NS_FAILED(rv) || !childFolder)
+ return NS_ERROR_FAILURE;
+
+ int32_t numNewMessages = 0;
+ childFolder->GetNumNewMessages(true, &numNewMessages);
+ FillToolTipInfo(childFolder, numNewMessages);
+ }
+ else if (mNewMailReceivedAtom == aProperty)
+ {
+ FillToolTipInfo(aFolder, aNewValue);
+ }
+ return NS_OK;
+}
+
+nsresult
+nsMessengerOSXIntegration::OnAlertClicked(const char16_t* aAlertCookie)
+{
+ openMailWindow(NS_ConvertUTF16toUTF8(aAlertCookie));
+ return NS_OK;
+}
+
+#ifdef MOZ_SUITE
+nsresult
+nsMessengerOSXIntegration::OnAlertClickedSimple()
+{
+ // SeaMonkey only function; only focus the app here, rest of the work will
+ // be done in suite/mailnews/mailWidgets.xml
+ FocusAppNative();
+ return NS_OK;
+}
+#endif
+
+nsresult
+nsMessengerOSXIntegration::OnAlertFinished()
+{
+ return NS_OK;
+}
+
+nsresult
+nsMessengerOSXIntegration::BounceDockIcon()
+{
+ nsresult rv;
+ nsCOMPtr<nsIPrefBranch> prefBranch(do_GetService(NS_PREFSERVICE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ bool bounceDockIcon = false;
+ rv = prefBranch->GetBoolPref(kBiffAnimateDockIconPref, &bounceDockIcon);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!bounceDockIcon)
+ return NS_OK;
+
+ nsCOMPtr<nsIWindowMediator> mediator(do_GetService(NS_WINDOWMEDIATOR_CONTRACTID));
+ if (mediator)
+ {
+ nsCOMPtr<mozIDOMWindowProxy> domWindow;
+ mediator->GetMostRecentWindow(u"mail:3pane", getter_AddRefs(domWindow));
+ if (domWindow)
+ {
+ nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(domWindow));
+ chromeWindow->GetAttention();
+ }
+ }
+ return NS_OK;
+}
+
+nsresult
+nsMessengerOSXIntegration::RestoreDockIcon()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ id tile = [[NSApplication sharedApplication] dockTile];
+ [tile setBadgeLabel: nil];
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult
+nsMessengerOSXIntegration::BadgeDockIcon()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ int32_t unreadCount = mUnreadTotal;
+ // If count is less than one, we should restore the original dock icon.
+ if (unreadCount < 1)
+ {
+ RestoreDockIcon();
+ return NS_OK;
+ }
+
+ // Draw the number, first giving extensions a chance to modify.
+ // Extensions might wish to transform "1000" into "100+" or some
+ // other short string. Getting back the empty string will cause
+ // nothing to be drawn and us to return early.
+ nsresult rv;
+ nsCOMPtr<nsIObserverService> os
+ (do_GetService("@mozilla.org/observer-service;1", &rv));
+ if (NS_FAILED(rv))
+ {
+ RestoreDockIcon();
+ return rv;
+ }
+
+ nsCOMPtr<nsISupportsString> str
+ (do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv));
+ if (NS_FAILED(rv))
+ {
+ RestoreDockIcon();
+ return rv;
+ }
+
+ nsAutoString total;
+ total.AppendInt(unreadCount);
+ str->SetData(total);
+ os->NotifyObservers(str, "before-unread-count-display",
+ total.get());
+ nsAutoString badgeString;
+ str->GetData(badgeString);
+ if (badgeString.IsEmpty())
+ {
+ RestoreDockIcon();
+ return NS_OK;
+ }
+
+ id tile = [[NSApplication sharedApplication] dockTile];
+ [tile setBadgeLabel:[NSString stringWithFormat:@"%S", (const unichar*)badgeString.get()]];
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsMessengerOSXIntegration::OnItemPropertyFlagChanged(nsIMsgDBHdr *item, nsIAtom *property, uint32_t oldFlag, uint32_t newFlag)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMessengerOSXIntegration::OnItemAdded(nsIMsgFolder *, nsISupports *)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMessengerOSXIntegration::OnItemBoolPropertyChanged(nsIMsgFolder *aItem,
+ nsIAtom *aProperty,
+ bool aOldValue,
+ bool aNewValue)
+{
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsMessengerOSXIntegration::OnItemEvent(nsIMsgFolder *, nsIAtom *)
+{
+ return NS_OK;
+}
+
+nsresult
+nsMessengerOSXIntegration::GetNewMailAuthors(nsIMsgFolder* aFolder,
+ nsString& aAuthors,
+ int32_t aNewCount,
+ int32_t* aNotDisplayed)
+{
+ // Get a list of names or email addresses for the folder's authors
+ // with new mail. Note that we only process the most recent "new"
+ // mail (aNewCount), working from most recently added. Duplicates
+ // are removed, and names are displayed to a set limit
+ // (kMaxDisplayCount) with the remaining count being returned in
+ // aNotDisplayed. Extension developers can listen for
+ // "newmail-notification-requested" and then make a decision about
+ // including a given author or not. As a result, it is possible that
+ // the resulting length of aAuthors will be 0.
+ nsCOMPtr<nsIMsgDatabase> db;
+ nsresult rv = aFolder->GetMsgDatabase(getter_AddRefs(db));
+ uint32_t numNewKeys = 0;
+ if (NS_SUCCEEDED(rv) && db)
+ {
+ nsCOMPtr<nsIObserverService> os =
+ do_GetService("@mozilla.org/observer-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // Get proper l10n list separator -- ", " in English
+ nsCOMPtr<nsIStringBundle> bundle;
+ GetStringBundle(getter_AddRefs(bundle));
+ if (!bundle)
+ return NS_ERROR_FAILURE;
+
+ uint32_t *newMessageKeys;
+ rv = db->GetNewList(&numNewKeys, &newMessageKeys);
+ if (NS_SUCCEEDED(rv))
+ {
+ nsString listSeparator;
+ bundle->GetStringFromName(u"macBiffNotification_separator", getter_Copies(listSeparator));
+
+ int32_t displayed = 0;
+ for (int32_t i = numNewKeys - 1; i >= 0; i--, aNewCount--)
+ {
+ if (0 == aNewCount || displayed == kMaxDisplayCount)
+ break;
+
+ nsCOMPtr<nsIMsgDBHdr> hdr;
+ rv = db->GetMsgHdrForKey(newMessageKeys[i],
+ getter_AddRefs(hdr));
+ if (NS_SUCCEEDED(rv) && hdr)
+ {
+ nsString author;
+ rv = hdr->GetMime2DecodedAuthor(author);
+ if (NS_FAILED(rv))
+ continue;
+
+ nsString name;
+ ExtractName(DecodedHeader(author), name);
+
+ // Give extensions a chance to suppress notifications for this author
+ nsCOMPtr<nsISupportsPRBool> notify =
+ do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
+
+ notify->SetData(true);
+ os->NotifyObservers(notify, "newmail-notification-requested",
+ author.get());
+
+ bool includeSender;
+ notify->GetData(&includeSender);
+
+ // Don't add unwanted or duplicate names
+ if (includeSender && aAuthors.Find(name, true) == -1)
+ {
+ if (displayed > 0)
+ aAuthors.Append(listSeparator);
+ aAuthors.Append(name);
+ displayed++;
+ }
+ }
+ }
+ }
+ NS_Free(newMessageKeys);
+ }
+ *aNotDisplayed = aNewCount;
+ return rv;
+}
+
+nsresult
+nsMessengerOSXIntegration::GetFirstFolderWithNewMail(nsIMsgFolder* aFolder, nsCString& aFolderURI)
+{
+ // Find the subfolder in aFolder with new mail and return the folderURI
+ if (aFolder)
+ {
+ nsCOMPtr<nsIMsgFolder> msgFolder;
+ // enumerate over the folders under this root folder till we find one with new mail....
+ nsCOMPtr<nsIArray> allFolders;
+ nsresult rv = aFolder->GetDescendants(getter_AddRefs(allFolders));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ rv = allFolders->Enumerate(getter_AddRefs(enumerator));
+ if (NS_SUCCEEDED(rv) && enumerator)
+ {
+ nsCOMPtr<nsISupports> supports;
+ int32_t numNewMessages = 0;
+ bool hasMore = false;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMore)) && hasMore)
+ {
+ rv = enumerator->GetNext(getter_AddRefs(supports));
+ if (NS_SUCCEEDED(rv) && supports)
+ {
+ msgFolder = do_QueryInterface(supports, &rv);
+ if (msgFolder)
+ {
+ numNewMessages = 0;
+ msgFolder->GetNumNewMessages(false, &numNewMessages);
+ if (numNewMessages)
+ break; // kick out of the while loop
+ }
+ } // if we have a folder
+ } // if we have more potential folders to enumerate
+ } // if enumerator
+
+ if (msgFolder)
+ msgFolder->GetURI(aFolderURI);
+ }
+
+ return NS_OK;
+}
+
+/*
+ * Method implementations for mozINewMailListener
+ */
+NS_IMETHODIMP
+nsMessengerOSXIntegration::OnCountChanged(uint32_t count)
+{
+ mUnreadTotal = count;
+ BadgeDockIcon();
+ return NS_OK;
+}
diff --git a/mailnews/build/moz.build b/mailnews/build/moz.build
index 9561fd33d8..9620e8f3d4 100644
--- a/mailnews/build/moz.build
+++ b/mailnews/build/moz.build
@@ -35,6 +35,10 @@ if CONFIG['OS_ARCH'] == 'WINNT':
else:
OS_LIBS += CONFIG['MOZ_ZLIB_LIBS']
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ OS_LIBS += CONFIG['TK_LIBS']
+ OS_LIBS += ['-framework Cocoa']
+
LOCAL_INCLUDES += [
'/mailnews/addrbook/src',
'/mailnews/base/search/src',
diff --git a/mailnews/compose/src/moz.build b/mailnews/compose/src/moz.build
index 831a0340f4..dcb9960a66 100644
--- a/mailnews/compose/src/moz.build
+++ b/mailnews/compose/src/moz.build
@@ -35,6 +35,16 @@ SOURCES += [
'nsURLFetcher.cpp',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'nsMsgAppleDoubleEncode.cpp',
+ 'nsMsgAppleEncode.cpp',
+ ]
+ EXPORTS += [
+ 'nsMsgAppleCodes.h',
+ 'nsMsgAppleDouble.h',
+ ]
+
EXTRA_COMPONENTS += [
'nsSMTPProtocolHandler.js',
'nsSMTPProtocolHandler.manifest',
diff --git a/mailnews/compose/src/nsMsgAppleCodes.h b/mailnews/compose/src/nsMsgAppleCodes.h
new file mode 100644
index 0000000000..d8ca2f327c
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAppleCodes.h
@@ -0,0 +1,106 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+** AD_Codes.h
+**
+** ---------------
+**
+** Head file for Apple Decode/Encode essential codes.
+**
+**
+*/
+
+#ifndef ad_codes_h
+#define ad_codes_h
+
+/*
+** applefile definitions used
+*/
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=mac68k
+#endif
+
+#define APPLESINGLE_MAGIC 0x00051600L
+#define APPLEDOUBLE_MAGIC 0x00051607L
+#define VERSION 0x00020000
+
+#define NUM_ENTRIES 6
+
+#define ENT_DFORK 1L
+#define ENT_RFORK 2L
+#define ENT_NAME 3L
+#define ENT_COMMENT 4L
+#define ENT_DATES 8L
+#define ENT_FINFO 9L
+#define CONVERT_TIME 1265437696L
+
+/*
+** data type used in the encoder/decoder.
+*/
+typedef struct ap_header
+{
+ int32_t magic;
+ int32_t version;
+ char fill[16];
+ int16_t entries;
+
+} ap_header;
+
+typedef struct ap_entry
+{
+ int32_t id;
+ int32_t offset;
+ int32_t length;
+
+} ap_entry;
+
+typedef struct ap_dates
+{
+ int32_t create, modify, backup, access;
+
+} ap_dates;
+
+typedef struct myFInfo /* the mac FInfo structure for the cross platform. */
+{
+ int32_t fdType, fdCreator;
+ int16_t fdFlags;
+ int32_t fdLocation; /* it really should be a pointer, but just a place-holder */
+ int16_t fdFldr;
+
+} myFInfo;
+
+PR_BEGIN_EXTERN_C
+/*
+** string utils.
+*/
+int write_stream(appledouble_encode_object *p_ap_encode_obj, const char *s,int len);
+
+int fill_apple_mime_header(appledouble_encode_object *p_ap_encode_obj);
+int ap_encode_file_infor(appledouble_encode_object *p_ap_encode_obj);
+int ap_encode_header(appledouble_encode_object* p_ap_encode_obj, bool firstTime);
+int ap_encode_data( appledouble_encode_object* p_ap_encode_obj, bool firstTime);
+
+/*
+** the prototypes for the ap_decoder.
+*/
+int fetch_a_line(appledouble_decode_object* p_ap_decode_obj, char *buff);
+int ParseFileHeader(appledouble_decode_object* p_ap_decode_obj);
+int ap_seek_part_start(appledouble_decode_object* p_ap_decode_obj);
+void parse_param(char *p, char **param, char**define, char **np);
+int ap_seek_to_boundary(appledouble_decode_object* p_ap_decode_obj, bool firstime);
+int ap_parse_header(appledouble_decode_object* p_ap_decode_obj,bool firstime);
+int ap_decode_file_infor(appledouble_decode_object* p_ap_decode_obj);
+int ap_decode_process_header(appledouble_decode_object* p_ap_decode_obj, bool firstime);
+int ap_decode_process_data( appledouble_decode_object* p_ap_decode_obj, bool firstime);
+
+PR_END_EXTERN_C
+
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=reset
+#endif
+
+#endif /* ad_codes_h */
diff --git a/mailnews/compose/src/nsMsgAppleDouble.h b/mailnews/compose/src/nsMsgAppleDouble.h
new file mode 100644
index 0000000000..f4ae934add
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAppleDouble.h
@@ -0,0 +1,207 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+* AppleDouble.h
+* -------------
+*
+* The header file for a stream based apple single/double encodor/decodor.
+*
+* 2aug95 mym
+*
+*/
+
+
+#ifndef AppleDouble_h
+#define AppleDouble_h
+
+#include "msgCore.h"
+#include "nsComposeStrings.h"
+#include "nsIOutputStream.h"
+#include "nsCOMPtr.h"
+
+#include <CoreServices/CoreServices.h>
+
+#define NOERR 0
+#define errDone 1
+ /* Done with current operation. */
+#define errEOB 2
+ /* End of a buffer. */
+#define errEOP 3
+ /* End of a Part. */
+
+
+#define errFileOpen NS_ERROR_GET_CODE(NS_MSG_UNABLE_TO_OPEN_TMP_FILE)
+#define errFileWrite -202 /*Error writing temporary file.*/
+#define errUsrCancel -2 /*MK_INTERRUPTED */
+#define errDecoding -1
+
+/*
+** The envirment block data type.
+*/
+enum
+{
+ kInit,
+ kDoingHeaderPortion,
+ kDoneHeaderPortion,
+ kDoingDataPortion,
+ kDoneDataPortion
+};
+
+typedef struct _appledouble_encode_object
+{
+ char fname[256];
+ FSIORefNum fileId; /* the id for the open file (data/resource fork) */
+
+ int state;
+ int text_file_type; /* if the file has a text file type with it. */
+ char *boundary; /* the boundary string. */
+
+ int status; /* the error code if anyerror happens. */
+ char b_overflow[200];
+ int s_overflow;
+
+ int state64; /* the left over state of base64 enocding */
+ int ct; /* the character count of base64 encoding */
+ int c1, c2; /* the left of the last base64 encoding */
+
+ char *outbuff; /* the outbuff by the caller. */
+ int s_outbuff; /* the size of the buffer. */
+ int pos_outbuff; /* the offset in the current buffer. */
+
+} appledouble_encode_object;
+
+/* The possible content transfer encodings */
+
+enum
+{
+ kEncodeNone,
+ kEncodeQP,
+ kEncodeBase64,
+ kEncodeUU
+};
+
+enum
+{
+ kGeneralMine,
+ kAppleDouble,
+ kAppleSingle
+};
+
+enum
+{
+ kInline,
+ kDontCare
+};
+
+enum
+{
+ kHeaderPortion,
+ kDataPortion
+};
+
+/* the decode states. */
+enum
+{
+ kBeginParseHeader = 3,
+ kParsingHeader,
+ kBeginSeekBoundary,
+ kSeekingBoundary,
+ kBeginHeaderPortion,
+ kProcessingHeaderPortion,
+ kBeginDataPortion,
+ kProcessingDataPortion,
+ kFinishing
+};
+
+/* uuencode states */
+enum
+{
+ kWaitingForBegin = (int) 0,
+ kBegin,
+ kMainBody,
+ kEnd
+};
+
+typedef struct _appledouble_decode_object
+{
+ int is_binary;
+ int is_apple_single; /* if the object encoded is in apple single */
+ int write_as_binhex;
+
+ int messagetype;
+ char* boundary0; /* the boundary for the enclosure. */
+ int deposition; /* the deposition. */
+ int encoding; /* the encoding method. */
+ int which_part;
+
+ char fname[256];
+ // nsIOFileStream *fileSpec; /* the stream for data fork work. */
+
+ int state;
+
+ int rksize; /* the resource fork size count. */
+ int dksize; /* the data fork size count. */
+
+ int status; /* the error code if anyerror happens. */
+ char b_leftover[256];
+ int s_leftover;
+
+ int encode; /* the encode type of the message. */
+ int state64; /* the left over state of base64 enocding */
+ int left; /* the character count of base64 encoding */
+ int c[4]; /* the left of the last base64 encoding */
+ int uu_starts_line; /* is decoder at the start of a line? (uuencode) */
+ int uu_state; /* state w/r/t the uuencode body */
+ int uu_bytes_written; /* bytes written from the current tuple (uuencode) */
+ int uu_line_bytes; /* encoded bytes remaining in the current line (uuencode) */
+
+ char *inbuff; /* the outbuff by the caller. */
+ int s_inbuff; /* the size of the buffer. */
+ int pos_inbuff; /* the offset in the current buffer. */
+
+
+ nsCOMPtr <nsIFile> tmpFile; /* the temp file to hold the decode data fork */
+ /* when doing the binhex exporting. */
+ nsCOMPtr <nsIOutputStream> tmpFileStream; /* The output File Stream */
+ int32_t data_size; /* the size of the data in the tmp file. */
+
+} appledouble_decode_object;
+
+
+/*
+** The protypes.
+*/
+
+PR_BEGIN_EXTERN_C
+
+int ap_encode_init(appledouble_encode_object *p_ap_encode_obj,
+ const char* fname,
+ char* separator);
+
+int ap_encode_next(appledouble_encode_object* p_ap_encode_obj,
+ char *to_buff,
+ int32_t buff_size,
+ int32_t* real_size);
+
+int ap_encode_end(appledouble_encode_object* p_ap_encode_obj,
+ bool is_aborting);
+
+int ap_decode_init(appledouble_decode_object* p_ap_decode_obj,
+ bool is_apple_single,
+ bool write_as_bin_hex,
+ void *closure);
+
+int ap_decode_next(appledouble_decode_object* p_ap_decode_obj,
+ char *in_buff,
+ int32_t buff_size);
+
+int ap_decode_end(appledouble_decode_object* p_ap_decode_obj,
+ bool is_aborting);
+
+PR_END_EXTERN_C
+
+#endif
diff --git a/mailnews/compose/src/nsMsgAppleDoubleEncode.cpp b/mailnews/compose/src/nsMsgAppleDoubleEncode.cpp
new file mode 100644
index 0000000000..4d71781233
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAppleDoubleEncode.cpp
@@ -0,0 +1,266 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+*
+* apple-double.c
+* --------------
+*
+* The codes to do apple double encoding/decoding.
+*
+* 02aug95 mym created.
+*
+*/
+#include "nsID.h"
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsMsgAppleDouble.h"
+#include "nsMsgAppleCodes.h"
+#include "nsMsgCompUtils.h"
+#include "nsCExternalHandlerService.h"
+#include "nsIMIMEService.h"
+#include "nsMimeTypes.h"
+#include "prmem.h"
+#include "nsNetUtil.h"
+
+
+void
+MacGetFileType(nsIFile *fs,
+ bool *useDefault,
+ char **fileType,
+ char **encoding)
+{
+ if ((fs == NULL) || (fileType == NULL) || (encoding == NULL))
+ return;
+
+ bool exists = false;
+ fs->Exists(&exists);
+ if (!exists)
+ return;
+
+ *useDefault = TRUE;
+ *fileType = NULL;
+ *encoding = NULL;
+
+ nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(fs);
+ FSRef fsRef;
+ FSCatalogInfo catalogInfo;
+ OSErr err = errFileOpen;
+ if (NS_SUCCEEDED(macFile->GetFSRef(&fsRef)))
+ err = ::FSGetCatalogInfo(&fsRef, kFSCatInfoFinderInfo, &catalogInfo, nullptr, nullptr, nullptr);
+
+ if ( (err != noErr) || (((FileInfo*)(&catalogInfo.finderInfo))->fileType == 'TEXT') )
+ *fileType = strdup(APPLICATION_OCTET_STREAM);
+ else
+ {
+ // At this point, we should call the mime service and
+ // see what we can find out?
+ nsresult rv;
+ nsCOMPtr <nsIURI> tURI;
+ if (NS_SUCCEEDED(NS_NewFileURI(getter_AddRefs(tURI), fs)) && tURI)
+ {
+ nsCOMPtr<nsIMIMEService> mimeFinder (do_GetService(NS_MIMESERVICE_CONTRACTID, &rv));
+ if (NS_SUCCEEDED(rv) && mimeFinder)
+ {
+ nsAutoCString mimeType;
+ rv = mimeFinder->GetTypeFromURI(tURI, mimeType);
+ if (NS_SUCCEEDED(rv))
+ {
+ *fileType = ToNewCString(mimeType);
+ return;
+ }
+ }
+ }
+
+ // If we hit here, return something...default to this...
+ *fileType = strdup(APPLICATION_OCTET_STREAM);
+ }
+}
+
+//#pragma cplusplus reset
+
+/*
+* ap_encode_init
+* --------------
+*
+* Setup the encode envirment
+*/
+
+int ap_encode_init( appledouble_encode_object *p_ap_encode_obj,
+ const char *fname,
+ char *separator)
+{
+ nsCOMPtr <nsIFile> myFile;
+ NS_NewNativeLocalFile(nsDependentCString(fname), true, getter_AddRefs(myFile));
+ bool exists;
+ if (myFile && NS_SUCCEEDED(myFile->Exists(&exists)) && !exists)
+ return -1;
+
+ nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(myFile);
+ nsAutoCString path;
+ macFile->GetNativePath(path);
+
+ memset(p_ap_encode_obj, 0, sizeof(appledouble_encode_object));
+
+ /*
+ ** Fill out the source file inforamtion.
+ */
+ memcpy(p_ap_encode_obj->fname, path.get(), path.Length());
+ p_ap_encode_obj->fname[path.Length()] = '\0';
+
+ p_ap_encode_obj->boundary = strdup(separator);
+ return noErr;
+}
+
+/*
+** ap_encode_next
+** --------------
+**
+** return :
+** noErr : everything is ok
+** errDone : when encoding is done.
+** errors : otherwise.
+*/
+int ap_encode_next(
+ appledouble_encode_object* p_ap_encode_obj,
+ char *to_buff,
+ int32_t buff_size,
+ int32_t* real_size)
+{
+ int status;
+
+ /*
+ ** install the out buff now.
+ */
+ p_ap_encode_obj->outbuff = to_buff;
+ p_ap_encode_obj->s_outbuff = buff_size;
+ p_ap_encode_obj->pos_outbuff = 0;
+
+ /*
+ ** first copy the outstandind data in the overflow buff to the out buffer.
+ */
+ if (p_ap_encode_obj->s_overflow)
+ {
+ status = write_stream(p_ap_encode_obj,
+ (const char*)(p_ap_encode_obj->b_overflow),
+ p_ap_encode_obj->s_overflow);
+ if (status != noErr)
+ return status;
+
+ p_ap_encode_obj->s_overflow = 0;
+ }
+
+ /*
+ ** go the next processing stage based on the current state.
+ */
+ switch (p_ap_encode_obj->state)
+ {
+ case kInit:
+ /*
+ ** We are in the starting position, fill out the header.
+ */
+ status = fill_apple_mime_header(p_ap_encode_obj);
+ if (status != noErr)
+ break; /* some error happens */
+
+ p_ap_encode_obj->state = kDoingHeaderPortion;
+ status = ap_encode_header(p_ap_encode_obj, true);
+ /* it is the first time to calling */
+ if (status == errDone)
+ {
+ p_ap_encode_obj->state = kDoneHeaderPortion;
+ }
+ else
+ {
+ break; /* we need more work on header portion. */
+ }
+
+ /*
+ ** we are done with the header, so let's go to the data port.
+ */
+ p_ap_encode_obj->state = kDoingDataPortion;
+ status = ap_encode_data(p_ap_encode_obj, true);
+ /* it is first time call do data portion */
+
+ if (status == errDone)
+ {
+ p_ap_encode_obj->state = kDoneDataPortion;
+ status = noErr;
+ }
+ break;
+
+ case kDoingHeaderPortion:
+
+ status = ap_encode_header(p_ap_encode_obj, false);
+ /* continue with the header portion. */
+ if (status == errDone)
+ {
+ p_ap_encode_obj->state = kDoneHeaderPortion;
+ }
+ else
+ {
+ break; /* we need more work on header portion. */
+ }
+
+ /*
+ ** start the data portion.
+ */
+ p_ap_encode_obj->state = kDoingDataPortion;
+ status = ap_encode_data(p_ap_encode_obj, true);
+ /* it is the first time calling */
+ if (status == errDone)
+ {
+ p_ap_encode_obj->state = kDoneDataPortion;
+ status = noErr;
+ }
+ break;
+
+ case kDoingDataPortion:
+
+ status = ap_encode_data(p_ap_encode_obj, false);
+ /* it is not the first time */
+
+ if (status == errDone)
+ {
+ p_ap_encode_obj->state = kDoneDataPortion;
+ status = noErr;
+ }
+ break;
+
+ case kDoneDataPortion:
+ status = errDone; /* we are really done. */
+
+ break;
+ }
+
+ *real_size = p_ap_encode_obj->pos_outbuff;
+ return status;
+}
+
+/*
+** ap_encode_end
+** -------------
+**
+** clear the apple encoding.
+*/
+
+int ap_encode_end(
+ appledouble_encode_object *p_ap_encode_obj,
+ bool is_aborting)
+{
+ /*
+ ** clear up the apple doubler.
+ */
+ if (p_ap_encode_obj == NULL)
+ return noErr;
+
+ if (p_ap_encode_obj->fileId) /* close the file if it is open. */
+ ::FSCloseFork(p_ap_encode_obj->fileId);
+
+ PR_FREEIF(p_ap_encode_obj->boundary); /* the boundary string. */
+
+ return noErr;
+}
diff --git a/mailnews/compose/src/nsMsgAppleEncode.cpp b/mailnews/compose/src/nsMsgAppleEncode.cpp
new file mode 100644
index 0000000000..27e39a8cda
--- /dev/null
+++ b/mailnews/compose/src/nsMsgAppleEncode.cpp
@@ -0,0 +1,703 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+/*
+ *
+ * apple_double_encode.c
+ * ---------------------
+ *
+ * The routines doing the Apple Double Encoding.
+ *
+ * 2aug95 mym Created.
+ *
+ */
+
+#include "nscore.h"
+#include "nsStringGlue.h"
+#include "nsMimeTypes.h"
+#include "prprf.h"
+#include "nsServiceManagerUtils.h"
+#include "nsMsgAppleDouble.h"
+#include "nsMsgAppleCodes.h"
+#include "nsILocalFileMac.h"
+
+/*
+** Local Functions prototypes.
+*/
+static int output64chunk( appledouble_encode_object* p_ap_encode_obj,
+ int c1, int c2, int c3, int pads);
+
+static int to64(appledouble_encode_object* p_ap_encode_obj,
+ char *p,
+ int in_size);
+
+static int finish64(appledouble_encode_object* p_ap_encode_obj);
+
+
+#define BUFF_LEFT(p) ((p)->s_outbuff - (p)->pos_outbuff)
+
+/*
+** write_stream.
+*/
+int write_stream(
+ appledouble_encode_object *p_ap_encode_obj,
+ const char *out_string,
+ int len)
+{
+ if (p_ap_encode_obj->pos_outbuff + len < p_ap_encode_obj->s_outbuff)
+ {
+ memcpy(p_ap_encode_obj->outbuff + p_ap_encode_obj->pos_outbuff,
+ out_string,
+ len);
+ p_ap_encode_obj->pos_outbuff += len;
+ return noErr;
+ }
+ else
+ {
+ /*
+ ** If the buff doesn't have enough space, use the overflow buffer then.
+ */
+ int s_len = p_ap_encode_obj->s_outbuff - p_ap_encode_obj->pos_outbuff;
+
+ memcpy(p_ap_encode_obj->outbuff + p_ap_encode_obj->pos_outbuff,
+ out_string,
+ s_len);
+ memcpy(p_ap_encode_obj->b_overflow + p_ap_encode_obj->s_overflow,
+ out_string + s_len,
+ p_ap_encode_obj->s_overflow += (len - s_len));
+ p_ap_encode_obj->pos_outbuff += s_len;
+ return errEOB;
+ }
+}
+
+int fill_apple_mime_header(
+ appledouble_encode_object *p_ap_encode_obj)
+{
+ int status;
+
+ char tmpstr[266];
+
+#if 0
+// strcpy(tmpstr, "Content-Type: multipart/mixed; boundary=\"-\"\n\n---\n");
+// status = write_stream(p_ap_encode_env,
+// tmpstr,
+// strlen(tmpstr));
+// if (status != noErr)
+// return status;
+
+ PR_snprintf(tmpstr, sizeof(tmpstr),
+ "Content-Type: multipart/appledouble; boundary=\"=\"; name=\"");
+ status = write_stream(p_ap_encode_obj, (const char*)tmpstr, strlen(tmpstr));
+ if (status != noErr)
+ return status;
+
+ status = write_stream(p_ap_encode_obj,
+ p_ap_encode_obj->fname,
+ strlen(p_ap_encode_obj->fname));
+ if (status != noErr)
+ return status;
+
+ PR_snprintf(tmpstr, sizeof(tmpstr),
+ "\"\r\nContent-Disposition: inline; filename=\"%s\"\r\n\r\n\r\n--=\r\n",
+ p_ap_encode_obj->fname);
+#endif /* 0 */
+ PR_snprintf(tmpstr, sizeof(tmpstr), "--%s" CRLF, p_ap_encode_obj->boundary);
+ status = write_stream(p_ap_encode_obj, (const char*)tmpstr, strlen(tmpstr));
+ return status;
+}
+
+int ap_encode_file_infor(
+ appledouble_encode_object *p_ap_encode_obj)
+{
+ ap_header head;
+ ap_entry entries[NUM_ENTRIES];
+ ap_dates dates;
+ short i;
+ long comlen;
+ char comment[256];
+ int status;
+
+ nsCOMPtr <nsIFile> resFile;
+ NS_NewNativeLocalFile(nsDependentCString(p_ap_encode_obj->fname), true,
+ getter_AddRefs(resFile));
+ if (!resFile)
+ return errFileOpen;
+
+ FSRef ref;
+ nsCOMPtr <nsILocalFileMac> macFile = do_QueryInterface(resFile);
+ if (NS_FAILED(macFile->GetFSRef(&ref)))
+ return errFileOpen;
+
+ FSCatalogInfo catalogInfo;
+ if (::FSGetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catalogInfo, nullptr, nullptr, nullptr) != noErr)
+ {
+ return errFileOpen;
+ }
+
+ /* get a file comment, if possible */
+#if 1
+ // Carbon doesn't support GetWDInfo(). (Bug 555684)
+
+ // not sure why working directories are needed here...
+ comlen = 0;
+#else
+ long procID;
+ procID = 0;
+ GetWDInfo(p_ap_encode_obj->vRefNum, &fpb->ioVRefNum, &fpb->ioDirID, &procID);
+ IOParam vinfo;
+ memset((void *) &vinfo, '\0', sizeof (vinfo));
+ GetVolParmsInfoBuffer vp;
+ vinfo.ioCompletion = nil;
+ vinfo.ioVRefNum = fpb->ioVRefNum;
+ vinfo.ioBuffer = (Ptr) &vp;
+ vinfo.ioReqCount = sizeof (vp);
+ comlen = 0;
+ if (PBHGetVolParmsSync((HParmBlkPtr) &vinfo) == noErr &&
+ ((vp.vMAttrib >> bHasDesktopMgr) & 1))
+ {
+ DTPBRec dtp;
+ memset((void *) &dtp, '\0', sizeof (dtp));
+ dtp.ioVRefNum = fpb->ioVRefNum;
+ if (PBDTGetPath(&dtp) == noErr)
+ {
+ dtp.ioCompletion = nil;
+ dtp.ioDTBuffer = (Ptr) comment;
+ dtp.ioNamePtr = fpb->ioNamePtr;
+ dtp.ioDirID = fpb->ioFlParID;
+ if (PBDTGetCommentSync(&dtp) == noErr)
+ comlen = dtp.ioDTActCount;
+ }
+ }
+#endif /* ! 1 */
+
+ /* write header */
+// head.magic = dfork ? APPLESINGLE_MAGIC : APPLEDOUBLE_MAGIC;
+ head.magic = APPLEDOUBLE_MAGIC; /* always do apple double */
+ head.version = VERSION;
+ memset(head.fill, '\0', sizeof (head.fill));
+ head.entries = NUM_ENTRIES - 1;
+ status = to64(p_ap_encode_obj,
+ (char *) &head,
+ sizeof (head));
+ if (status != noErr)
+ return status;
+
+ /* write entry descriptors */
+ nsAutoCString leafname;
+ macFile->GetNativeLeafName(leafname);
+ entries[0].offset = sizeof (head) + sizeof (ap_entry) * head.entries;
+ entries[0].id = ENT_NAME;
+ entries[0].length = leafname.Length();
+ entries[1].id = ENT_FINFO;
+ entries[1].length = sizeof (FInfo) + sizeof (FXInfo);
+ entries[2].id = ENT_DATES;
+ entries[2].length = sizeof (ap_dates);
+ entries[3].id = ENT_COMMENT;
+ entries[3].length = comlen;
+ entries[4].id = ENT_RFORK;
+ entries[4].length = catalogInfo.rsrcLogicalSize;
+ entries[5].id = ENT_DFORK;
+ entries[5].length = catalogInfo.dataLogicalSize;
+
+ /* correct the link in the entries. */
+ for (i = 1; i < NUM_ENTRIES; ++i)
+ {
+ entries[i].offset = entries[i-1].offset + entries[i-1].length;
+ }
+ status = to64(p_ap_encode_obj,
+ (char *) entries,
+ sizeof (ap_entry) * head.entries);
+ if (status != noErr)
+ return status;
+
+ /* write name */
+ status = to64(p_ap_encode_obj,
+ (char *) leafname.get(),
+ leafname.Length());
+ if (status != noErr)
+ return status;
+
+ /* write finder info */
+ status = to64(p_ap_encode_obj,
+ (char *) &catalogInfo.finderInfo,
+ sizeof (FInfo));
+ if (status != noErr)
+ return status;
+
+ status = to64(p_ap_encode_obj,
+ (char *) &catalogInfo.extFinderInfo,
+ sizeof (FXInfo));
+ if (status != noErr)
+ return status;
+
+ /* write dates */
+ dates.create = catalogInfo.createDate.lowSeconds + CONVERT_TIME;
+ dates.modify = catalogInfo.contentModDate.lowSeconds + CONVERT_TIME;
+ dates.backup = catalogInfo.backupDate.lowSeconds + CONVERT_TIME;
+ dates.access = catalogInfo.accessDate.lowSeconds + CONVERT_TIME;
+ status = to64(p_ap_encode_obj,
+ (char *) &dates,
+ sizeof (ap_dates));
+ if (status != noErr)
+ return status;
+
+ /* write comment */
+ if (comlen)
+ {
+ status = to64(p_ap_encode_obj,
+ comment,
+ comlen * sizeof(char));
+ }
+ /*
+ ** Get some help information on deciding the file type.
+ */
+ if (((FileInfo*)(&catalogInfo.finderInfo))->fileType == 'TEXT' ||
+ ((FileInfo*)(&catalogInfo.finderInfo))->fileType == 'text')
+ {
+ p_ap_encode_obj->text_file_type = true;
+ }
+
+ return status;
+}
+/*
+** ap_encode_header
+**
+** encode the file header and the resource fork.
+**
+*/
+int ap_encode_header(
+ appledouble_encode_object* p_ap_encode_obj,
+ bool firstime)
+{
+ char rd_buff[256];
+ FSIORefNum fileId;
+ OSErr retval = noErr;
+ int status;
+ ByteCount inCount;
+
+ if (firstime)
+ {
+ PL_strcpy(rd_buff,
+ "Content-Type: application/applefile\r\nContent-Transfer-Encoding: base64\r\n\r\n");
+ status = write_stream(p_ap_encode_obj, (const char*)rd_buff, strlen(rd_buff));
+ if (status != noErr)
+ return status;
+
+ status = ap_encode_file_infor(p_ap_encode_obj);
+ if (status != noErr)
+ return status;
+
+ /*
+ ** preparing to encode the resource fork.
+ */
+ nsCOMPtr <nsIFile> myFile;
+ NS_NewNativeLocalFile(nsDependentCString(p_ap_encode_obj->fname), true, getter_AddRefs(myFile));
+ if (!myFile)
+ return errFileOpen;
+
+ FSRef ref;
+ nsCOMPtr <nsILocalFileMac> macFile = do_QueryInterface(myFile);
+ if (NS_FAILED(macFile->GetFSRef(&ref)))
+ return errFileOpen;
+
+ HFSUniStr255 forkName;
+ ::FSGetResourceForkName(&forkName);
+ retval = ::FSOpenFork(&ref, forkName.length, forkName.unicode, fsRdPerm, &p_ap_encode_obj->fileId);
+ if (retval != noErr)
+ return retval;
+ }
+
+ fileId = p_ap_encode_obj->fileId;
+ while (retval == noErr)
+ {
+ if (BUFF_LEFT(p_ap_encode_obj) < 400)
+ break;
+
+ inCount = 0;
+ retval = ::FSReadFork(fileId, fsAtMark, 0, 256, rd_buff, &inCount);
+ if (inCount)
+ {
+ status = to64(p_ap_encode_obj,
+ rd_buff,
+ inCount);
+ if (status != noErr)
+ return status;
+ }
+ }
+
+ if (retval == eofErr)
+ {
+ ::FSCloseFork(fileId);
+ p_ap_encode_obj->fileId = 0;
+
+ status = finish64(p_ap_encode_obj);
+ if (status != noErr)
+ return status;
+
+ /*
+ ** write out the boundary
+ */
+ PR_snprintf(rd_buff, sizeof(rd_buff),
+ CRLF "--%s" CRLF,
+ p_ap_encode_obj->boundary);
+
+ status = write_stream(p_ap_encode_obj, (const char*)rd_buff, strlen(rd_buff));
+ if (status == noErr)
+ status = errDone;
+ }
+ return status;
+}
+
+#if 0
+// This is unused for now and Clang complains about that is it is ifdefed out
+static void replace(char *p, int len, char frm, char to)
+{
+ for (; len > 0; len--, p++)
+ if (*p == frm) *p = to;
+}
+#endif
+
+/* Description of the various file formats and their magic numbers */
+struct magic
+{
+ const char *name; /* Name of the file format */
+ const char *num; /* The magic number */
+ int len; /* Length (0 means strlen(magicnum)) */
+};
+
+/* The magic numbers of the file formats we know about */
+static struct magic magic[] =
+{
+ { "image/gif", "GIF", 0 },
+ { "image/jpeg", "\377\330\377", 0 },
+ { "video/mpeg", "\0\0\001\263", 4 },
+ { "application/postscript", "%!", 0 },
+};
+static int num_magic = MOZ_ARRAY_LENGTH(magic);
+
+static const char *text_type = TEXT_PLAIN; /* the text file type. */
+static const char *default_type = APPLICATION_OCTET_STREAM;
+
+
+/*
+ * Determins the format of the file "inputf". The name
+ * of the file format (or NULL on error) is returned.
+ */
+static const char *magic_look(char *inbuff, int numread)
+{
+ int i, j;
+
+ for (i=0; i<num_magic; i++)
+ {
+ if (magic[i].len == 0)
+ magic[i].len = strlen(magic[i].num);
+ }
+
+ for (i=0; i<num_magic; i++)
+ {
+ if (numread >= magic[i].len)
+ {
+ for (j=0; j<magic[i].len; j++)
+ {
+ if (inbuff[j] != magic[i].num[j]) break;
+ }
+
+ if (j == magic[i].len)
+ return magic[i].name;
+ }
+ }
+
+ return default_type;
+}
+/*
+** ap_encode_data
+**
+** ---------------
+**
+** encode on the data fork.
+**
+*/
+int ap_encode_data(
+ appledouble_encode_object* p_ap_encode_obj,
+ bool firstime)
+{
+ char rd_buff[256];
+ FSIORefNum fileId;
+ OSErr retval = noErr;
+ ByteCount in_count;
+ int status;
+
+ if (firstime)
+ {
+ const char* magic_type;
+
+ /*
+ ** preparing to encode the data fork.
+ */
+ nsCOMPtr <nsIFile> resFile;
+ NS_NewNativeLocalFile(nsDependentCString(p_ap_encode_obj->fname), true,
+ getter_AddRefs(resFile));
+ if (!resFile)
+ return errFileOpen;
+
+ FSRef ref;
+ nsCOMPtr <nsILocalFileMac> macFile = do_QueryInterface(resFile);
+ if (NS_FAILED(macFile->GetFSRef(&ref)))
+ return errFileOpen;
+
+ HFSUniStr255 forkName;
+ ::FSGetDataForkName(&forkName);
+ retval = ::FSOpenFork(&ref, forkName.length, forkName.unicode, fsRdPerm, &fileId);
+ if (retval != noErr)
+ return retval;
+
+ p_ap_encode_obj->fileId = fileId;
+
+
+ if (!p_ap_encode_obj->text_file_type)
+ {
+ /*
+ ** do a smart check for the file type.
+ */
+ in_count = 0;
+ retval = ::FSReadFork(fileId, fsFromStart, 0, 256, rd_buff, &in_count);
+ magic_type = magic_look(rd_buff, in_count);
+
+ /* don't forget to rewind the index to start point. */
+ ::FSSetForkPosition(fileId, fsFromStart, 0);
+ /* and reset retVal just in case... */
+ if (retval == eofErr)
+ retval = noErr;
+ }
+ else
+ {
+ magic_type = text_type; /* we already know it is a text type. */
+ }
+
+ /*
+ ** the data portion header information.
+ */
+ nsAutoCString leafName;
+ resFile->GetNativeLeafName(leafName);
+ PR_snprintf(rd_buff, sizeof(rd_buff),
+ "Content-Type: %s; name=\"%s\"" CRLF "Content-Transfer-Encoding: base64" CRLF "Content-Disposition: inline; filename=\"%s\"" CRLF CRLF,
+ magic_type,
+ leafName.get(),
+ leafName.get());
+
+ status = write_stream(p_ap_encode_obj, (const char*)rd_buff, strlen(rd_buff));
+ if (status != noErr)
+ return status;
+ }
+
+ while (retval == noErr)
+ {
+ if (BUFF_LEFT(p_ap_encode_obj) < 400)
+ break;
+
+ in_count = 0;
+ retval = ::FSReadFork(p_ap_encode_obj->fileId, fsAtMark, 0, 256, rd_buff, &in_count);
+ if (in_count)
+ {
+#if 0
+/* replace(rd_buff, in_count, '\r', '\n'); */
+#endif
+/* ** may be need to do character set conversion here for localization. ** */
+ status = to64(p_ap_encode_obj,
+ rd_buff,
+ in_count);
+ if (status != noErr)
+ return status;
+ }
+ }
+
+ if (retval == eofErr)
+ {
+ ::FSCloseFork(p_ap_encode_obj->fileId);
+ p_ap_encode_obj->fileId = 0;
+
+ status = finish64(p_ap_encode_obj);
+ if (status != noErr)
+ return status;
+
+ /* write out the boundary */
+
+ PR_snprintf(rd_buff, sizeof(rd_buff),
+ CRLF "--%s--" CRLF CRLF,
+ p_ap_encode_obj->boundary);
+
+ status = write_stream(p_ap_encode_obj, (const char*)rd_buff, strlen(rd_buff));
+
+ if (status == noErr)
+ status = errDone;
+ }
+ return status;
+}
+
+static char basis_64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+** convert the stream in the inbuff to 64 format and put it in the out buff.
+** To make the life easier, the caller will responcable of the cheking of the outbuff's bundary.
+*/
+static int
+to64(appledouble_encode_object* p_ap_encode_obj,
+ char *p,
+ int in_size)
+{
+ int status;
+ int c1, c2, c3, ct;
+ unsigned char *inbuff = (unsigned char*)p;
+
+ ct = p_ap_encode_obj->ct; /* the char count left last time. */
+
+ /*
+ ** resume the left state of the last conversion.
+ */
+ switch (p_ap_encode_obj->state64)
+ {
+ case 0:
+ p_ap_encode_obj->c1 = c1 = *inbuff ++;
+ if (--in_size <= 0)
+ {
+ p_ap_encode_obj->state64 = 1;
+ return noErr;
+ }
+ p_ap_encode_obj->c2 = c2 = *inbuff ++;
+ if (--in_size <= 0)
+ {
+ p_ap_encode_obj->state64 = 2;
+ return noErr;
+ }
+ c3 = *inbuff ++; --in_size;
+ break;
+ case 1:
+ c1 = p_ap_encode_obj->c1;
+ p_ap_encode_obj->c2 = c2 = *inbuff ++;
+ if (--in_size <= 0)
+ {
+ p_ap_encode_obj->state64 = 2;
+ return noErr;
+ }
+ c3 = *inbuff ++; --in_size;
+ break;
+ case 2:
+ c1 = p_ap_encode_obj->c1;
+ c2 = p_ap_encode_obj->c2;
+ c3 = *inbuff ++; --in_size;
+ break;
+ }
+
+ while (in_size >= 0)
+ {
+ status = output64chunk(p_ap_encode_obj,
+ c1,
+ c2,
+ c3,
+ 0);
+ if (status != noErr)
+ return status;
+
+ ct += 4;
+ if (ct > 71)
+ {
+ status = write_stream(p_ap_encode_obj,
+ CRLF,
+ 2);
+ if (status != noErr)
+ return status;
+
+ ct = 0;
+ }
+
+ if (in_size <= 0)
+ {
+ p_ap_encode_obj->state64 = 0;
+ break;
+ }
+
+ c1 = (int)*inbuff++;
+ if (--in_size <= 0)
+ {
+ p_ap_encode_obj->c1 = c1;
+ p_ap_encode_obj->state64 = 1;
+ break;
+ }
+ c2 = *inbuff++;
+ if (--in_size <= 0)
+ {
+ p_ap_encode_obj->c1 = c1;
+ p_ap_encode_obj->c2 = c2;
+ p_ap_encode_obj->state64 = 2;
+ break;
+ }
+ c3 = *inbuff++;
+ in_size--;
+ }
+ p_ap_encode_obj->ct = ct;
+ return status;
+}
+
+/*
+** clear the left base64 encodes.
+*/
+static int
+finish64(appledouble_encode_object* p_ap_encode_obj)
+{
+ int status;
+
+ switch (p_ap_encode_obj->state64)
+ {
+ case 0:
+ break;
+ case 1:
+ status = output64chunk(p_ap_encode_obj,
+ p_ap_encode_obj->c1,
+ 0,
+ 0,
+ 2);
+ break;
+ case 2:
+ status = output64chunk(p_ap_encode_obj,
+ p_ap_encode_obj->c1,
+ p_ap_encode_obj->c2,
+ 0,
+ 1);
+ break;
+ }
+ status = write_stream(p_ap_encode_obj, CRLF, 2);
+ p_ap_encode_obj->state64 = 0;
+ p_ap_encode_obj->ct = 0;
+ return status;
+}
+
+static int output64chunk(
+ appledouble_encode_object* p_ap_encode_obj,
+ int c1, int c2, int c3, int pads)
+{
+ char tmpstr[32];
+ char *p = tmpstr;
+
+ *p++ = basis_64[c1>>2];
+ *p++ = basis_64[((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4)];
+ if (pads == 2)
+ {
+ *p++ = '=';
+ *p++ = '=';
+ }
+ else if (pads)
+ {
+ *p++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
+ *p++ = '=';
+ }
+ else
+ {
+ *p++ = basis_64[((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6)];
+ *p++ = basis_64[c3 & 0x3F];
+ }
+ return write_stream(p_ap_encode_obj, (const char*) tmpstr, p-tmpstr);
+}
diff --git a/mailnews/import/build/moz.build b/mailnews/import/build/moz.build
index 58814a6940..e8ac0751f5 100644
--- a/mailnews/import/build/moz.build
+++ b/mailnews/import/build/moz.build
@@ -35,6 +35,13 @@ LOCAL_INCLUDES += [
'../vcard/src',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '../applemail/src',
+ ]
+ OS_LIBS += CONFIG['TK_LIBS']
+ OS_LIBS += ['-framework Cocoa']
+
if CONFIG['OS_ARCH'] == 'WINNT':
LOCAL_INCLUDES += [
]
diff --git a/media/libcubeb/src/cubeb_osx_run_loop.c b/media/libcubeb/src/cubeb_osx_run_loop.c
new file mode 100644
index 0000000000..0ba9536560
--- /dev/null
+++ b/media/libcubeb/src/cubeb_osx_run_loop.c
@@ -0,0 +1,11 @@
+/* -*- 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 "OSXRunLoopSingleton.h"
+
+void cubeb_set_coreaudio_notification_runloop()
+{
+ mozilla_set_coreaudio_notification_runloop_if_needed();
+}
diff --git a/media/libcubeb/src/cubeb_osx_run_loop.h b/media/libcubeb/src/cubeb_osx_run_loop.h
new file mode 100644
index 0000000000..78cd68d09b
--- /dev/null
+++ b/media/libcubeb/src/cubeb_osx_run_loop.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright © 2014 Mozilla Foundation
+ *
+ * This program is made available under an ISC-style license. See the
+ * accompanying file LICENSE for details.
+ */
+
+/* On OSX 10.6 and after, the notification callbacks from the audio hardware are
+ * called on the main thread. Setting the kAudioHardwarePropertyRunLoop property
+ * to null tells the OSX to use a separate thread for that.
+ *
+ * This has to be called only once per process, so it is in a separate header
+ * for easy integration in other code bases. */
+#if defined(__cplusplus)
+extern "C" {
+#endif
+
+void cubeb_set_coreaudio_notification_runloop();
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/media/libcubeb/src/moz.build b/media/libcubeb/src/moz.build
index c21cc873db..65aaf7256a 100644
--- a/media/libcubeb/src/moz.build
+++ b/media/libcubeb/src/moz.build
@@ -55,6 +55,10 @@ if CONFIG['OS_TARGET'] == 'Darwin':
'cubeb_audiounit.cpp',
'cubeb_resampler.cpp'
]
+ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'cubeb_osx_run_loop.c',
+ ]
DEFINES['USE_AUDIOUNIT'] = True
if CONFIG['OS_TARGET'] == 'WINNT':
diff --git a/netwerk/base/NetworkInfoServiceCocoa.cpp b/netwerk/base/NetworkInfoServiceCocoa.cpp
new file mode 100644
index 0000000000..cdfa8e5c97
--- /dev/null
+++ b/netwerk/base/NetworkInfoServiceCocoa.cpp
@@ -0,0 +1,103 @@
+/* -*- Mode: C++; 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/. */
+
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <netdb.h>
+
+#include "mozilla/DebugOnly.h"
+#include "mozilla/ScopeExit.h"
+
+#include "NetworkInfoServiceImpl.h"
+
+namespace mozilla {
+namespace net {
+
+static nsresult
+ListInterfaceAddresses(int aFd, const char* aIface, AddrMapType& aAddrMap);
+
+nsresult
+DoListAddresses(AddrMapType& aAddrMap)
+{
+ int fd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ auto autoCloseSocket = MakeScopeExit([&] {
+ close(fd);
+ });
+
+ struct ifconf ifconf;
+ /* 16k of space should be enough to list all interfaces. Worst case, if it's
+ * not then we will error out and fail to list addresses. This should only
+ * happen on pathological machines with way too many interfaces.
+ */
+ char buf[16384];
+
+ ifconf.ifc_len = sizeof(buf);
+ ifconf.ifc_buf = buf;
+ if (ioctl(fd, SIOCGIFCONF, &ifconf) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ struct ifreq* ifreq = ifconf.ifc_req;
+ int i = 0;
+ while (i < ifconf.ifc_len) {
+ size_t len = IFNAMSIZ + ifreq->ifr_addr.sa_len;
+
+ DebugOnly<nsresult> rv =
+ ListInterfaceAddresses(fd, ifreq->ifr_name, aAddrMap);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "ListInterfaceAddresses failed");
+
+ ifreq = (struct ifreq*) ((char*)ifreq + len);
+ i += len;
+ }
+
+ autoCloseSocket.release();
+ return NS_OK;
+}
+
+static nsresult
+ListInterfaceAddresses(int aFd, const char* aInterface, AddrMapType& aAddrMap)
+{
+ struct ifreq ifreq;
+ memset(&ifreq, 0, sizeof(struct ifreq));
+ strncpy(ifreq.ifr_name, aInterface, IFNAMSIZ - 1);
+ if (ioctl(aFd, SIOCGIFADDR, &ifreq) != 0) {
+ return NS_ERROR_FAILURE;
+ }
+
+ char host[128];
+ int family;
+ switch(family=ifreq.ifr_addr.sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ getnameinfo(&ifreq.ifr_addr, sizeof(ifreq.ifr_addr), host, sizeof(host), 0, 0, NI_NUMERICHOST);
+ break;
+ case AF_UNSPEC:
+ return NS_OK;
+ default:
+ // Unknown family.
+ return NS_OK;
+ }
+
+ nsCString ifaceStr;
+ ifaceStr.AssignASCII(aInterface);
+
+ nsCString addrStr;
+ addrStr.AssignASCII(host);
+
+ aAddrMap.Put(ifaceStr, addrStr);
+
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build
index 3ec17b2b2a..1659299f7b 100644
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -257,6 +257,10 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'nsURLHelperWin.cpp',
'ShutdownLayer.cpp',
]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'nsURLHelperOSX.cpp',
+ ]
else:
SOURCES += [
'nsURLHelperUnix.cpp',
@@ -268,6 +272,11 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
'NetworkInfoServiceWindows.cpp',
'nsNetworkInfoService.cpp',
]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'NetworkInfoServiceCocoa.cpp',
+ 'nsNetworkInfoService.cpp',
+ ]
elif CONFIG['OS_ARCH'] == 'Linux':
SOURCES += [
'NetworkInfoServiceLinux.cpp',
diff --git a/netwerk/base/nsURLHelperOSX.cpp b/netwerk/base/nsURLHelperOSX.cpp
new file mode 100644
index 0000000000..bcc0b257fb
--- /dev/null
+++ b/netwerk/base/nsURLHelperOSX.cpp
@@ -0,0 +1,216 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 et cindent: */
+/* 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/. */
+
+/* Mac OS X-specific local file uri parsing */
+#include "nsURLHelper.h"
+#include "nsEscape.h"
+#include "nsIFile.h"
+#include "nsTArray.h"
+#include "nsReadableUtils.h"
+#include <Carbon/Carbon.h>
+
+static nsTArray<nsCString> *gVolumeList = nullptr;
+
+static bool pathBeginsWithVolName(const nsACString& path, nsACString& firstPathComponent)
+{
+ // Return whether the 1st path component in path (escaped) is equal to the name
+ // of a mounted volume. Return the 1st path component (unescaped) in any case.
+ // This needs to be done as quickly as possible, so we cache a list of volume names.
+ // XXX Register an event handler to detect drives being mounted/unmounted?
+
+ if (!gVolumeList) {
+ gVolumeList = new nsTArray<nsCString>;
+ if (!gVolumeList) {
+ return false; // out of memory
+ }
+ }
+
+ // Cache a list of volume names
+ if (!gVolumeList->Length()) {
+ OSErr err;
+ ItemCount volumeIndex = 1;
+
+ do {
+ HFSUniStr255 volName;
+ FSRef rootDirectory;
+ err = ::FSGetVolumeInfo(0, volumeIndex, nullptr, kFSVolInfoNone, nullptr,
+ &volName, &rootDirectory);
+ if (err == noErr) {
+ NS_ConvertUTF16toUTF8 volNameStr(Substring((char16_t *)volName.unicode,
+ (char16_t *)volName.unicode + volName.length));
+ gVolumeList->AppendElement(volNameStr);
+ volumeIndex++;
+ }
+ } while (err == noErr);
+ }
+
+ // Extract the first component of the path
+ nsACString::const_iterator start;
+ path.BeginReading(start);
+ start.advance(1); // path begins with '/'
+ nsACString::const_iterator directory_end;
+ path.EndReading(directory_end);
+ nsACString::const_iterator component_end(start);
+ FindCharInReadable('/', component_end, directory_end);
+
+ nsAutoCString flatComponent((Substring(start, component_end)));
+ NS_UnescapeURL(flatComponent);
+ int32_t foundIndex = gVolumeList->IndexOf(flatComponent);
+ firstPathComponent = flatComponent;
+ return (foundIndex != -1);
+}
+
+void
+net_ShutdownURLHelperOSX()
+{
+ delete gVolumeList;
+ gVolumeList = nullptr;
+}
+
+static nsresult convertHFSPathtoPOSIX(const nsACString& hfsPath, nsACString& posixPath)
+{
+ // Use CFURL to do the conversion. We don't want to do this by simply
+ // using SwapSlashColon - we need the charset mapped from MacRoman
+ // to UTF-8, and we need "/Volumes" (or whatever - Apple says this is subject to change)
+ // prepended if the path is not on the boot drive.
+
+ CFStringRef pathStrRef = CFStringCreateWithCString(nullptr,
+ PromiseFlatCString(hfsPath).get(),
+ kCFStringEncodingMacRoman);
+ if (!pathStrRef)
+ return NS_ERROR_FAILURE;
+
+ nsresult rv = NS_ERROR_FAILURE;
+ CFURLRef urlRef = CFURLCreateWithFileSystemPath(nullptr,
+ pathStrRef, kCFURLHFSPathStyle, true);
+ if (urlRef) {
+ UInt8 pathBuf[PATH_MAX];
+ if (CFURLGetFileSystemRepresentation(urlRef, true, pathBuf, sizeof(pathBuf))) {
+ posixPath = (char *)pathBuf;
+ rv = NS_OK;
+ }
+ }
+ CFRelease(pathStrRef);
+ if (urlRef)
+ CFRelease(urlRef);
+ return rv;
+}
+
+static void SwapSlashColon(char *s)
+{
+ while (*s) {
+ if (*s == '/')
+ *s = ':';
+ else if (*s == ':')
+ *s = '/';
+ s++;
+ }
+}
+
+nsresult
+net_GetURLSpecFromActualFile(nsIFile *aFile, nsACString &result)
+{
+ // NOTE: This is identical to the implementation in nsURLHelperUnix.cpp
+
+ nsresult rv;
+ nsAutoCString ePath;
+
+ // construct URL spec from native file path
+ rv = aFile->GetNativePath(ePath);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString escPath;
+ NS_NAMED_LITERAL_CSTRING(prefix, "file://");
+
+ // Escape the path with the directory mask
+ if (NS_EscapeURL(ePath.get(), ePath.Length(), esc_Directory+esc_Forced, escPath))
+ escPath.Insert(prefix, 0);
+ else
+ escPath.Assign(prefix + ePath);
+
+ // esc_Directory does not escape the semicolons, so if a filename
+ // contains semicolons we need to manually escape them.
+ // This replacement should be removed in bug #473280
+ escPath.ReplaceSubstring(";", "%3b");
+
+ result = escPath;
+ return NS_OK;
+}
+
+nsresult
+net_GetFileFromURLSpec(const nsACString &aURL, nsIFile **result)
+{
+ // NOTE: See also the implementation in nsURLHelperUnix.cpp
+ // This matches it except for the HFS path handling.
+
+ nsresult rv;
+
+ nsCOMPtr<nsIFile> localFile;
+ rv = NS_NewNativeLocalFile(EmptyCString(), true, getter_AddRefs(localFile));
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString directory, fileBaseName, fileExtension, path;
+ bool bHFSPath = false;
+
+ rv = net_ParseFileURL(aURL, directory, fileBaseName, fileExtension);
+ if (NS_FAILED(rv))
+ return rv;
+
+ if (!directory.IsEmpty()) {
+ NS_EscapeURL(directory, esc_Directory|esc_AlwaysCopy, path);
+
+ // The canonical form of file URLs on OSX use POSIX paths:
+ // file:///path-name.
+ // But, we still encounter file URLs that use HFS paths:
+ // file:///volume-name/path-name
+ // Determine that here and normalize HFS paths to POSIX.
+ nsAutoCString possibleVolName;
+ if (pathBeginsWithVolName(directory, possibleVolName)) {
+ // Though we know it begins with a volume name, it could still
+ // be a valid POSIX path if the boot drive is named "Mac HD"
+ // and there is a directory "Mac HD" at its root. If such a
+ // directory doesn't exist, we'll assume this is an HFS path.
+ FSRef testRef;
+ possibleVolName.Insert("/", 0);
+ if (::FSPathMakeRef((UInt8*)possibleVolName.get(), &testRef, nullptr) != noErr)
+ bHFSPath = true;
+ }
+
+ if (bHFSPath) {
+ // "%2F"s need to become slashes, while all other slashes need to
+ // become colons. If we start out by changing "%2F"s to colons, we
+ // can reply on SwapSlashColon() to do what we need
+ path.ReplaceSubstring("%2F", ":");
+ path.Cut(0, 1); // directory begins with '/'
+ SwapSlashColon((char *)path.get());
+ // At this point, path is an HFS path made using the same
+ // algorithm as nsURLHelperMac. We'll convert to POSIX below.
+ }
+ }
+ if (!fileBaseName.IsEmpty())
+ NS_EscapeURL(fileBaseName, esc_FileBaseName|esc_AlwaysCopy, path);
+ if (!fileExtension.IsEmpty()) {
+ path += '.';
+ NS_EscapeURL(fileExtension, esc_FileExtension|esc_AlwaysCopy, path);
+ }
+
+ NS_UnescapeURL(path);
+ if (path.Length() != strlen(path.get()))
+ return NS_ERROR_FILE_INVALID_PATH;
+
+ if (bHFSPath)
+ convertHFSPathtoPOSIX(path, path);
+
+ // assuming path is encoded in the native charset
+ rv = localFile->InitWithNativePath(path);
+ if (NS_FAILED(rv))
+ return rv;
+
+ localFile.forget(result);
+ return NS_OK;
+}
diff --git a/netwerk/build/moz.build b/netwerk/build/moz.build
index 7c8416b9ac..ebafda48bb 100644
--- a/netwerk/build/moz.build
+++ b/netwerk/build/moz.build
@@ -36,7 +36,12 @@ if CONFIG['OS_ARCH'] == 'WINNT':
'/netwerk/system/win32',
]
-if CONFIG['OS_ARCH'] == 'Linux':
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/netwerk/system/mac',
+ ]
+
+elif CONFIG['OS_ARCH'] == 'Linux':
LOCAL_INCLUDES += [
'/netwerk/system/linux',
]
diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp
new file mode 100644
index 0000000000..72b5577749
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.cpp
@@ -0,0 +1,779 @@
+/* -*- 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 "MDNSResponderOperator.h"
+#include "MDNSResponderReply.h"
+#include "mozilla/EndianUtils.h"
+#include "mozilla/Logging.h"
+#include "mozilla/ScopeExit.h"
+#include "mozilla/Unused.h"
+#include "nsComponentManagerUtils.h"
+#include "nsCOMPtr.h"
+#include "nsDebug.h"
+#include "nsDNSServiceInfo.h"
+#include "nsHashPropertyBag.h"
+#include "nsIProperty.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIVariant.h"
+#include "nsServiceManagerUtils.h"
+#include "nsNetAddr.h"
+#include "nsNetCID.h"
+#include "nsSocketTransportService2.h"
+#include "nsThreadUtils.h"
+#include "nsXPCOMCID.h"
+#include "private/pprio.h"
+
+#include "nsASocketHandler.h"
+
+namespace mozilla {
+namespace net {
+
+static LazyLogModule gMDNSLog("MDNSResponderOperator");
+#undef LOG_I
+#define LOG_I(...) MOZ_LOG(mozilla::net::gMDNSLog, mozilla::LogLevel::Debug, (__VA_ARGS__))
+#undef LOG_E
+#define LOG_E(...) MOZ_LOG(mozilla::net::gMDNSLog, mozilla::LogLevel::Error, (__VA_ARGS__))
+
+class MDNSResponderOperator::ServiceWatcher final
+ : public nsASocketHandler
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+
+ // nsASocketHandler methods
+ virtual void OnSocketReady(PRFileDesc* fd, int16_t outFlags) override
+ {
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+ MOZ_ASSERT(fd == mFD);
+
+ if (outFlags & (PR_POLL_ERR | PR_POLL_HUP | PR_POLL_NVAL)) {
+ LOG_E("error polling on listening socket (%p)", fd);
+ mCondition = NS_ERROR_UNEXPECTED;
+ }
+
+ if (!(outFlags & PR_POLL_READ)) {
+ return;
+ }
+
+ DNSServiceProcessResult(mService);
+ }
+
+ virtual void OnSocketDetached(PRFileDesc *fd) override
+ {
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+ MOZ_ASSERT(mThread);
+ MOZ_ASSERT(fd == mFD);
+
+ if (!mFD) {
+ return;
+ }
+
+ // Bug 1175387: do not double close the handle here.
+ PR_ChangeFileDescNativeHandle(mFD, -1);
+ PR_Close(mFD);
+ mFD = nullptr;
+
+ mThread->Dispatch(NewRunnableMethod(this, &ServiceWatcher::Deallocate),
+ NS_DISPATCH_NORMAL);
+ }
+
+ virtual void IsLocal(bool *aIsLocal) override { *aIsLocal = true; }
+
+ virtual void KeepWhenOffline(bool *aKeepWhenOffline) override
+ {
+ *aKeepWhenOffline = true;
+ }
+
+ virtual uint64_t ByteCountSent() override { return 0; }
+ virtual uint64_t ByteCountReceived() override { return 0; }
+
+ explicit ServiceWatcher(DNSServiceRef aService,
+ MDNSResponderOperator* aOperator)
+ : mThread(nullptr)
+ , mSts(nullptr)
+ , mOperatorHolder(aOperator)
+ , mService(aService)
+ , mFD(nullptr)
+ , mAttached(false)
+ {
+ if (!gSocketTransportService)
+ {
+ nsCOMPtr<nsISocketTransportService> sts =
+ do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID);
+ }
+ }
+
+ nsresult Init()
+ {
+ MOZ_ASSERT(PR_GetCurrentThread() != gSocketThread);
+ mThread = NS_GetCurrentThread();
+
+ if (!mService) {
+ return NS_OK;
+ }
+
+ if (!gSocketTransportService) {
+ return NS_ERROR_FAILURE;
+ }
+ mSts = gSocketTransportService;
+
+ int osfd = DNSServiceRefSockFD(mService);
+ if (osfd == -1) {
+ return NS_ERROR_FAILURE;
+ }
+
+ mFD = PR_ImportFile(osfd);
+ return PostEvent(&ServiceWatcher::OnMsgAttach);
+ }
+
+ void Close()
+ {
+ MOZ_ASSERT(PR_GetCurrentThread() != gSocketThread);
+
+ if (!gSocketTransportService) {
+ Deallocate();
+ return;
+ }
+
+ PostEvent(&ServiceWatcher::OnMsgClose);
+ }
+
+private:
+ ~ServiceWatcher() = default;
+
+ void Deallocate()
+ {
+ if (mService) {
+ DNSServiceRefDeallocate(mService);
+ mService = nullptr;
+ }
+ mOperatorHolder = nullptr;
+ }
+
+ nsresult PostEvent(void(ServiceWatcher::*func)(void))
+ {
+ return gSocketTransportService->Dispatch(NewRunnableMethod(this, func),
+ NS_DISPATCH_NORMAL);
+ }
+
+ void OnMsgClose()
+ {
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+ if (NS_FAILED(mCondition)) {
+ return;
+ }
+
+ // tear down socket. this signals the STS to detach our socket handler.
+ mCondition = NS_BINDING_ABORTED;
+
+ // if we are attached, then socket transport service will call our
+ // OnSocketDetached method automatically. Otherwise, we have to call it
+ // (and thus close the socket) manually.
+ if (!mAttached) {
+ OnSocketDetached(mFD);
+ }
+ }
+
+ void OnMsgAttach()
+ {
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+ if (NS_FAILED(mCondition)) {
+ return;
+ }
+
+ mCondition = TryAttach();
+
+ // if we hit an error while trying to attach then bail...
+ if (NS_FAILED(mCondition)) {
+ NS_ASSERTION(!mAttached, "should not be attached already");
+ OnSocketDetached(mFD);
+ }
+
+ }
+
+ nsresult TryAttach()
+ {
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+ nsresult rv;
+
+ if (!gSocketTransportService) {
+ return NS_ERROR_FAILURE;
+ }
+
+ //
+ // find out if it is going to be ok to attach another socket to the STS.
+ // if not then we have to wait for the STS to tell us that it is ok.
+ // the notification is asynchronous, which means that when we could be
+ // in a race to call AttachSocket once notified. for this reason, when
+ // we get notified, we just re-enter this function. as a result, we are
+ // sure to ask again before calling AttachSocket. in this way we deal
+ // with the race condition. though it isn't the most elegant solution,
+ // it is far simpler than trying to build a system that would guarantee
+ // FIFO ordering (which wouldn't even be that valuable IMO). see bug
+ // 194402 for more info.
+ //
+ if (!gSocketTransportService->CanAttachSocket()) {
+ nsCOMPtr<nsIRunnable> event =
+ NewRunnableMethod(this, &ServiceWatcher::OnMsgAttach);
+
+ nsresult rv = gSocketTransportService->NotifyWhenCanAttachSocket(event);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+ }
+
+ //
+ // ok, we can now attach our socket to the STS for polling
+ //
+ rv = gSocketTransportService->AttachSocket(mFD, this);
+ if (NS_FAILED(rv)) {
+ return rv;
+ }
+
+ mAttached = true;
+
+ //
+ // now, configure our poll flags for listening...
+ //
+ mPollFlags = (PR_POLL_READ | PR_POLL_EXCEPT);
+
+ return NS_OK;
+ }
+
+ nsCOMPtr<nsIThread> mThread;
+ RefPtr<nsSocketTransportService> mSts;
+ RefPtr<MDNSResponderOperator> mOperatorHolder;
+ DNSServiceRef mService;
+ PRFileDesc* mFD;
+ bool mAttached;
+};
+
+NS_IMPL_ISUPPORTS(MDNSResponderOperator::ServiceWatcher, nsISupports)
+
+MDNSResponderOperator::MDNSResponderOperator()
+ : mService(nullptr)
+ , mWatcher(nullptr)
+ , mThread(NS_GetCurrentThread())
+ , mIsCancelled(false)
+{
+}
+
+MDNSResponderOperator::~MDNSResponderOperator()
+{
+ Stop();
+}
+
+nsresult
+MDNSResponderOperator::Start()
+{
+ if (mIsCancelled) {
+ return NS_OK;
+ }
+
+ if (IsServing()) {
+ Stop();
+ }
+
+ return NS_OK;
+}
+
+nsresult
+MDNSResponderOperator::Stop()
+{
+ return ResetService(nullptr);
+}
+
+nsresult
+MDNSResponderOperator::ResetService(DNSServiceRef aService)
+{
+ nsresult rv;
+
+ if (aService != mService) {
+ if (mWatcher) {
+ mWatcher->Close();
+ mWatcher = nullptr;
+ }
+
+ if (aService) {
+ RefPtr<ServiceWatcher> watcher = new ServiceWatcher(aService, this);
+ if (NS_WARN_IF(NS_FAILED(rv = watcher->Init()))) {
+ return rv;
+ }
+ mWatcher = watcher;
+ }
+
+ mService = aService;
+ }
+ return NS_OK;
+}
+
+BrowseOperator::BrowseOperator(const nsACString& aServiceType,
+ nsIDNSServiceDiscoveryListener* aListener)
+ : MDNSResponderOperator()
+ , mServiceType(aServiceType)
+ , mListener(aListener)
+{
+}
+
+nsresult
+BrowseOperator::Start()
+{
+ nsresult rv;
+ if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) {
+ return rv;
+ }
+
+ DNSServiceRef service = nullptr;
+ DNSServiceErrorType err = DNSServiceBrowse(&service,
+ 0,
+ kDNSServiceInterfaceIndexAny,
+ mServiceType.get(),
+ nullptr,
+ &BrowseReplyRunnable::Reply,
+ this);
+ NS_WARNING_ASSERTION(kDNSServiceErr_NoError == err, "DNSServiceBrowse fail");
+
+ if (mListener) {
+ if (kDNSServiceErr_NoError == err) {
+ mListener->OnDiscoveryStarted(mServiceType);
+ } else {
+ mListener->OnStartDiscoveryFailed(mServiceType, err);
+ }
+ }
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != err)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ return ResetService(service);
+}
+
+nsresult
+BrowseOperator::Stop()
+{
+ bool isServing = IsServing();
+ nsresult rv = MDNSResponderOperator::Stop();
+
+ if (isServing && mListener) {
+ if (NS_SUCCEEDED(rv)) {
+ mListener->OnDiscoveryStopped(mServiceType);
+ } else {
+ mListener->OnStopDiscoveryFailed(mServiceType,
+ static_cast<uint32_t>(rv));
+ }
+ }
+
+ return rv;
+}
+
+void
+BrowseOperator::Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aServiceName,
+ const nsACString& aRegType,
+ const nsACString& aReplyDomain)
+{
+ MOZ_ASSERT(GetThread() == NS_GetCurrentThread());
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) {
+ LOG_E("BrowseOperator::Reply (%d)", aErrorCode);
+ if (mListener) {
+ mListener->OnStartDiscoveryFailed(mServiceType, aErrorCode);
+ }
+ return;
+ }
+
+ if (!mListener) { return; }
+ nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo();
+
+ if (NS_WARN_IF(!info)) { return; }
+ if (NS_WARN_IF(NS_FAILED(info->SetServiceName(aServiceName)))) { return; }
+ if (NS_WARN_IF(NS_FAILED(info->SetServiceType(aRegType)))) { return; }
+ if (NS_WARN_IF(NS_FAILED(info->SetDomainName(aReplyDomain)))) { return; }
+
+ if (aFlags & kDNSServiceFlagsAdd) {
+ mListener->OnServiceFound(info);
+ } else {
+ mListener->OnServiceLost(info);
+ }
+}
+
+RegisterOperator::RegisterOperator(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSRegistrationListener* aListener)
+ : MDNSResponderOperator()
+ , mServiceInfo(aServiceInfo)
+ , mListener(aListener)
+{
+}
+
+nsresult
+RegisterOperator::Start()
+{
+ nsresult rv;
+
+ rv = MDNSResponderOperator::Start();
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
+ uint16_t port;
+ if (NS_WARN_IF(NS_FAILED(rv = mServiceInfo->GetPort(&port)))) {
+ return rv;
+ }
+ nsAutoCString type;
+ if (NS_WARN_IF(NS_FAILED(rv = mServiceInfo->GetServiceType(type)))) {
+ return rv;
+ }
+
+ TXTRecordRef txtRecord;
+ char buf[TXT_BUFFER_SIZE] = { 0 };
+ TXTRecordCreate(&txtRecord, TXT_BUFFER_SIZE, buf);
+
+ nsCOMPtr<nsIPropertyBag2> attributes;
+ if (NS_FAILED(rv = mServiceInfo->GetAttributes(getter_AddRefs(attributes)))) {
+ LOG_I("register: no attributes");
+ } else {
+ nsCOMPtr<nsISimpleEnumerator> enumerator;
+ if (NS_WARN_IF(NS_FAILED(rv =
+ attributes->GetEnumerator(getter_AddRefs(enumerator))))) {
+ return rv;
+ }
+
+ bool hasMoreElements;
+ while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreElements)) &&
+ hasMoreElements) {
+ nsCOMPtr<nsISupports> element;
+ MOZ_ALWAYS_SUCCEEDS(enumerator->GetNext(getter_AddRefs(element)));
+ nsCOMPtr<nsIProperty> property = do_QueryInterface(element);
+ MOZ_ASSERT(property);
+
+ nsAutoString name;
+ nsCOMPtr<nsIVariant> value;
+ MOZ_ALWAYS_SUCCEEDS(property->GetName(name));
+ MOZ_ALWAYS_SUCCEEDS(property->GetValue(getter_AddRefs(value)));
+
+ nsAutoCString str;
+ if (NS_WARN_IF(NS_FAILED(value->GetAsACString(str)))) {
+ continue;
+ }
+
+ TXTRecordSetValue(&txtRecord,
+ /* it's safe because key name is ASCII only. */
+ NS_LossyConvertUTF16toASCII(name).get(),
+ str.Length(),
+ str.get());
+ }
+ }
+
+ nsAutoCString host;
+ nsAutoCString name;
+ nsAutoCString domain;
+
+ DNSServiceRef service = nullptr;
+ DNSServiceErrorType err =
+ DNSServiceRegister(&service,
+ 0,
+ 0,
+ NS_SUCCEEDED(mServiceInfo->GetServiceName(name)) ?
+ name.get() : nullptr,
+ type.get(),
+ NS_SUCCEEDED(mServiceInfo->GetDomainName(domain)) ?
+ domain.get() : nullptr,
+ NS_SUCCEEDED(mServiceInfo->GetHost(host)) ?
+ host.get() : nullptr,
+ NativeEndian::swapToNetworkOrder(port),
+ TXTRecordGetLength(&txtRecord),
+ TXTRecordGetBytesPtr(&txtRecord),
+ &RegisterReplyRunnable::Reply,
+ this);
+ NS_WARNING_ASSERTION(kDNSServiceErr_NoError == err,
+ "DNSServiceRegister fail");
+
+ TXTRecordDeallocate(&txtRecord);
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != err)) {
+ if (mListener) {
+ mListener->OnRegistrationFailed(mServiceInfo, err);
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ return ResetService(service);
+}
+
+nsresult
+RegisterOperator::Stop()
+{
+ bool isServing = IsServing();
+ nsresult rv = MDNSResponderOperator::Stop();
+
+ if (isServing && mListener) {
+ if (NS_SUCCEEDED(rv)) {
+ mListener->OnServiceUnregistered(mServiceInfo);
+ } else {
+ mListener->OnUnregistrationFailed(mServiceInfo,
+ static_cast<uint32_t>(rv));
+ }
+ }
+
+ return rv;
+}
+
+void
+RegisterOperator::Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aName,
+ const nsACString& aRegType,
+ const nsACString& aDomain)
+{
+ MOZ_ASSERT(GetThread() == NS_GetCurrentThread());
+
+ if (kDNSServiceErr_NoError != aErrorCode) {
+ LOG_E("RegisterOperator::Reply (%d)", aErrorCode);
+ }
+
+ if (!mListener) { return; }
+ nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo);
+ if (NS_WARN_IF(NS_FAILED(info->SetServiceName(aName)))) { return; }
+ if (NS_WARN_IF(NS_FAILED(info->SetServiceType(aRegType)))) { return; }
+ if (NS_WARN_IF(NS_FAILED(info->SetDomainName(aDomain)))) { return; }
+
+ if (kDNSServiceErr_NoError == aErrorCode) {
+ if (aFlags & kDNSServiceFlagsAdd) {
+ mListener->OnServiceRegistered(info);
+ } else {
+ // If a successfully-registered name later suffers a name conflict
+ // or similar problem and has to be deregistered, the callback will
+ // be invoked with the kDNSServiceFlagsAdd flag not set.
+ LOG_E("RegisterOperator::Reply: deregister");
+ if (NS_WARN_IF(NS_FAILED(Stop()))) {
+ return;
+ }
+ }
+ } else {
+ mListener->OnRegistrationFailed(info, aErrorCode);
+ }
+}
+
+ResolveOperator::ResolveOperator(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSServiceResolveListener* aListener)
+ : MDNSResponderOperator()
+ , mServiceInfo(aServiceInfo)
+ , mListener(aListener)
+{
+}
+
+nsresult
+ResolveOperator::Start()
+{
+ nsresult rv;
+ if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) {
+ return rv;
+ }
+
+ nsAutoCString name;
+ mServiceInfo->GetServiceName(name);
+ nsAutoCString type;
+ mServiceInfo->GetServiceType(type);
+ nsAutoCString domain;
+ mServiceInfo->GetDomainName(domain);
+
+ LOG_I("Resolve: (%s), (%s), (%s)", name.get(), type.get(), domain.get());
+
+ DNSServiceRef service = nullptr;
+ DNSServiceErrorType err =
+ DNSServiceResolve(&service,
+ 0,
+ kDNSServiceInterfaceIndexAny,
+ name.get(),
+ type.get(),
+ domain.get(),
+ (DNSServiceResolveReply)&ResolveReplyRunnable::Reply,
+ this);
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != err)) {
+ if (mListener) {
+ mListener->OnResolveFailed(mServiceInfo, err);
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ return ResetService(service);
+}
+
+void
+ResolveOperator::Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aFullName,
+ const nsACString& aHostTarget,
+ uint16_t aPort,
+ uint16_t aTxtLen,
+ const unsigned char* aTxtRecord)
+{
+ MOZ_ASSERT(GetThread() == NS_GetCurrentThread());
+
+ auto guard = MakeScopeExit([&] {
+ Unused << NS_WARN_IF(NS_FAILED(Stop()));
+ });
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) {
+ LOG_E("ResolveOperator::Reply (%d)", aErrorCode);
+ return;
+ }
+
+ // Resolve TXT record
+ int count = TXTRecordGetCount(aTxtLen, aTxtRecord);
+ LOG_I("resolve: txt count = %d, len = %d", count, aTxtLen);
+ nsCOMPtr<nsIWritablePropertyBag2> attributes = new nsHashPropertyBag();
+ if (NS_WARN_IF(!attributes)) {
+ return;
+ }
+ if (count) {
+ for (int i = 0; i < count; ++i) {
+ char key[TXT_BUFFER_SIZE] = { '\0' };
+ uint8_t vSize = 0;
+ const void* value = nullptr;
+ if (kDNSServiceErr_NoError !=
+ TXTRecordGetItemAtIndex(aTxtLen,
+ aTxtRecord,
+ i,
+ TXT_BUFFER_SIZE,
+ key,
+ &vSize,
+ &value)) {
+ break;
+ }
+
+ nsAutoCString str(reinterpret_cast<const char*>(value), vSize);
+ LOG_I("resolve TXT: (%d) %s=%s", vSize, key, str.get());
+
+ if (NS_WARN_IF(NS_FAILED(attributes->SetPropertyAsACString(
+ /* it's safe to convert because key name is ASCII only. */
+ NS_ConvertASCIItoUTF16(key),
+ str)))) {
+ break;
+ }
+ }
+ }
+
+ if (!mListener) { return; }
+ nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo);
+ if (NS_WARN_IF(NS_FAILED(info->SetHost(aHostTarget)))) { return; }
+ if (NS_WARN_IF(NS_FAILED(info->SetPort(aPort)))) { return; }
+ if (NS_WARN_IF(NS_FAILED(info->SetAttributes(attributes)))) { return; }
+
+ if (kDNSServiceErr_NoError == aErrorCode) {
+ GetAddrInfor(info);
+ }
+ else {
+ mListener->OnResolveFailed(info, aErrorCode);
+ Unused << NS_WARN_IF(NS_FAILED(Stop()));
+ }
+}
+
+void
+ResolveOperator::GetAddrInfor(nsIDNSServiceInfo* aServiceInfo)
+{
+ RefPtr<GetAddrInfoOperator> getAddreOp = new GetAddrInfoOperator(aServiceInfo,
+ mListener);
+ Unused << NS_WARN_IF(NS_FAILED(getAddreOp->Start()));
+}
+
+GetAddrInfoOperator::GetAddrInfoOperator(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSServiceResolveListener* aListener)
+ : MDNSResponderOperator()
+ , mServiceInfo(aServiceInfo)
+ , mListener(aListener)
+{
+}
+
+nsresult
+GetAddrInfoOperator::Start()
+{
+ nsresult rv;
+ if (NS_WARN_IF(NS_FAILED(rv = MDNSResponderOperator::Start()))) {
+ return rv;
+ }
+
+ nsAutoCString host;
+ mServiceInfo->GetHost(host);
+
+ LOG_I("GetAddrInfo: (%s)", host.get());
+
+ DNSServiceRef service = nullptr;
+ DNSServiceErrorType err =
+ DNSServiceGetAddrInfo(&service,
+ kDNSServiceFlagsForceMulticast,
+ kDNSServiceInterfaceIndexAny,
+ kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6,
+ host.get(),
+ (DNSServiceGetAddrInfoReply)&GetAddrInfoReplyRunnable::Reply,
+ this);
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != err)) {
+ if (mListener) {
+ mListener->OnResolveFailed(mServiceInfo, err);
+ }
+ return NS_ERROR_FAILURE;
+ }
+
+ return ResetService(service);
+}
+
+void
+GetAddrInfoOperator::Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aHostName,
+ const NetAddr& aAddress,
+ uint32_t aTTL)
+{
+ MOZ_ASSERT(GetThread() == NS_GetCurrentThread());
+
+ auto guard = MakeScopeExit([&] {
+ Unused << NS_WARN_IF(NS_FAILED(Stop()));
+ });
+
+ if (NS_WARN_IF(kDNSServiceErr_NoError != aErrorCode)) {
+ LOG_E("GetAddrInfoOperator::Reply (%d)", aErrorCode);
+ return;
+ }
+
+ if (!mListener) { return; }
+
+ NetAddr addr = aAddress;
+ nsCOMPtr<nsINetAddr> address = new nsNetAddr(&addr);
+ nsCString addressStr;
+ if (NS_WARN_IF(NS_FAILED(address->GetAddress(addressStr)))) { return; }
+
+ nsCOMPtr<nsIDNSServiceInfo> info = new nsDNSServiceInfo(mServiceInfo);
+ if (NS_WARN_IF(NS_FAILED(info->SetAddress(addressStr)))) { return; }
+
+ /**
+ * |kDNSServiceFlagsMoreComing| means this callback will be one or more
+ * callback events later, so this instance should be kept alive until all
+ * follow-up events are processed.
+ */
+ if (aFlags & kDNSServiceFlagsMoreComing) {
+ guard.release();
+ }
+
+ if (kDNSServiceErr_NoError == aErrorCode) {
+ mListener->OnServiceResolved(info);
+ } else {
+ mListener->OnResolveFailed(info, aErrorCode);
+ }
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h
new file mode 100644
index 0000000000..a932baa7cc
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderOperator.h
@@ -0,0 +1,152 @@
+/* -*- 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_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h
+#define mozilla_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h
+
+#include "dns_sd.h"
+#include "mozilla/Atomics.h"
+#include "mozilla/RefPtr.h"
+#include "nsCOMPtr.h"
+#include "nsIDNSServiceDiscovery.h"
+#include "nsIThread.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace net {
+
+class MDNSResponderOperator
+{
+ NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MDNSResponderOperator)
+
+public:
+ MDNSResponderOperator();
+
+ virtual nsresult Start();
+ virtual nsresult Stop();
+ void Cancel() { mIsCancelled = true; }
+ nsIThread* GetThread() const { return mThread; }
+
+protected:
+ virtual ~MDNSResponderOperator();
+
+ bool IsServing() const { return mService; }
+ nsresult ResetService(DNSServiceRef aService);
+
+private:
+ class ServiceWatcher;
+
+ DNSServiceRef mService;
+ RefPtr<ServiceWatcher> mWatcher;
+ nsCOMPtr<nsIThread> mThread; // remember caller thread for callback
+ Atomic<bool> mIsCancelled;
+};
+
+class BrowseOperator final : public MDNSResponderOperator
+{
+public:
+ BrowseOperator(const nsACString& aServiceType,
+ nsIDNSServiceDiscoveryListener* aListener);
+
+ nsresult Start() override;
+ nsresult Stop() override;
+
+ void Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aServiceName,
+ const nsACString& aRegType,
+ const nsACString& aReplyDomain);
+
+private:
+ ~BrowseOperator() = default;
+
+ nsCString mServiceType;
+ nsCOMPtr<nsIDNSServiceDiscoveryListener> mListener;
+};
+
+class RegisterOperator final : public MDNSResponderOperator
+{
+ enum { TXT_BUFFER_SIZE = 256 };
+
+public:
+ RegisterOperator(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSRegistrationListener* aListener);
+
+ nsresult Start() override;
+ nsresult Stop() override;
+
+ void Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aName,
+ const nsACString& aRegType,
+ const nsACString& aDomain);
+
+private:
+ ~RegisterOperator() = default;
+
+ nsCOMPtr<nsIDNSServiceInfo> mServiceInfo;
+ nsCOMPtr<nsIDNSRegistrationListener> mListener;
+};
+
+class ResolveOperator final : public MDNSResponderOperator
+{
+ enum { TXT_BUFFER_SIZE = 256 };
+
+public:
+ ResolveOperator(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSServiceResolveListener* aListener);
+
+ nsresult Start() override;
+
+ void Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aFullName,
+ const nsACString& aHostTarget,
+ uint16_t aPort,
+ uint16_t aTxtLen,
+ const unsigned char* aTxtRecord);
+
+private:
+ ~ResolveOperator() = default;
+ void GetAddrInfor(nsIDNSServiceInfo* aServiceInfo);
+
+ nsCOMPtr<nsIDNSServiceInfo> mServiceInfo;
+ nsCOMPtr<nsIDNSServiceResolveListener> mListener;
+};
+
+union NetAddr;
+
+class GetAddrInfoOperator final : public MDNSResponderOperator
+{
+public:
+ GetAddrInfoOperator(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSServiceResolveListener* aListener);
+
+ nsresult Start() override;
+
+ void Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aHostName,
+ const NetAddr& aAddress,
+ uint32_t aTTL);
+
+private:
+ ~GetAddrInfoOperator() = default;
+
+ nsCOMPtr<nsIDNSServiceInfo> mServiceInfo;
+ nsCOMPtr<nsIDNSServiceResolveListener> mListener;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_netwerk_dns_mdns_libmdns_MDNSResponderOperator_h
diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp b/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp
new file mode 100644
index 0000000000..7aa5b3759a
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderReply.cpp
@@ -0,0 +1,302 @@
+/* -*- 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 "MDNSResponderReply.h"
+#include "mozilla/EndianUtils.h"
+#include "private/pprio.h"
+
+namespace mozilla {
+namespace net {
+
+BrowseReplyRunnable::BrowseReplyRunnable(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aServiceName,
+ const nsACString& aRegType,
+ const nsACString& aReplyDomain,
+ BrowseOperator* aContext)
+ : mSdRef(aSdRef)
+ , mFlags(aFlags)
+ , mInterfaceIndex(aInterfaceIndex)
+ , mErrorCode(aErrorCode)
+ , mServiceName(aServiceName)
+ , mRegType(aRegType)
+ , mReplyDomain(aReplyDomain)
+ , mContext(aContext)
+{
+}
+
+NS_IMETHODIMP
+BrowseReplyRunnable::Run()
+{
+ MOZ_ASSERT(mContext);
+ mContext->Reply(mSdRef,
+ mFlags,
+ mInterfaceIndex,
+ mErrorCode,
+ mServiceName,
+ mRegType,
+ mReplyDomain);
+ return NS_OK;
+}
+
+void
+BrowseReplyRunnable::Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const char* aServiceName,
+ const char* aRegType,
+ const char* aReplyDomain,
+ void* aContext)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+ BrowseOperator* obj(reinterpret_cast<BrowseOperator*>(aContext));
+ if (!obj) {
+ return;
+ }
+
+ nsCOMPtr<nsIThread> thread(obj->GetThread());
+ if (!thread) {
+ return;
+ }
+
+ thread->Dispatch(new BrowseReplyRunnable(aSdRef,
+ aFlags,
+ aInterfaceIndex,
+ aErrorCode,
+ nsCString(aServiceName),
+ nsCString(aRegType),
+ nsCString(aReplyDomain),
+ obj),
+ NS_DISPATCH_NORMAL);
+}
+
+RegisterReplyRunnable::RegisterReplyRunnable(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aName,
+ const nsACString& aRegType,
+ const nsACString& domain,
+ RegisterOperator* aContext)
+ : mSdRef(aSdRef)
+ , mFlags(aFlags)
+ , mErrorCode(aErrorCode)
+ , mName(aName)
+ , mRegType(aRegType)
+ , mDomain(domain)
+ , mContext(aContext)
+{
+}
+
+NS_IMETHODIMP
+RegisterReplyRunnable::Run()
+{
+ MOZ_ASSERT(mContext);
+
+ mContext->Reply(mSdRef,
+ mFlags,
+ mErrorCode,
+ mName,
+ mRegType,
+ mDomain);
+ return NS_OK;
+}
+
+void
+RegisterReplyRunnable::Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode,
+ const char* aName,
+ const char* aRegType,
+ const char* domain,
+ void* aContext)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+ RegisterOperator* obj(reinterpret_cast<RegisterOperator*>(aContext));
+ if (!obj) {
+ return;
+ }
+
+ nsCOMPtr<nsIThread> thread(obj->GetThread());
+ if (!thread) {
+ return;
+ }
+
+ thread->Dispatch(new RegisterReplyRunnable(aSdRef,
+ aFlags,
+ aErrorCode,
+ nsCString(aName),
+ nsCString(aRegType),
+ nsCString(domain),
+ obj),
+ NS_DISPATCH_NORMAL);
+}
+
+ResolveReplyRunnable::ResolveReplyRunnable(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aFullName,
+ const nsACString& aHostTarget,
+ uint16_t aPort,
+ uint16_t aTxtLen,
+ const unsigned char* aTxtRecord,
+ ResolveOperator* aContext)
+ : mSdRef(aSdRef)
+ , mFlags(aFlags)
+ , mInterfaceIndex(aInterfaceIndex)
+ , mErrorCode(aErrorCode)
+ , mFullname(aFullName)
+ , mHosttarget(aHostTarget)
+ , mPort(aPort)
+ , mTxtLen(aTxtLen)
+ , mTxtRecord(new unsigned char[aTxtLen])
+ , mContext(aContext)
+{
+ if (mTxtRecord) {
+ memcpy(mTxtRecord.get(), aTxtRecord, aTxtLen);
+ }
+}
+
+ResolveReplyRunnable::~ResolveReplyRunnable()
+{
+}
+
+NS_IMETHODIMP
+ResolveReplyRunnable::Run()
+{
+ MOZ_ASSERT(mContext);
+ mContext->Reply(mSdRef,
+ mFlags,
+ mInterfaceIndex,
+ mErrorCode,
+ mFullname,
+ mHosttarget,
+ mPort,
+ mTxtLen,
+ mTxtRecord.get());
+ return NS_OK;
+}
+
+void
+ResolveReplyRunnable::Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const char* aFullName,
+ const char* aHostTarget,
+ uint16_t aPort,
+ uint16_t aTxtLen,
+ const unsigned char* aTxtRecord,
+ void* aContext)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+ ResolveOperator* obj(reinterpret_cast<ResolveOperator*>(aContext));
+ if (!obj) {
+ return;
+ }
+
+ nsCOMPtr<nsIThread> thread(obj->GetThread());
+ if (!thread) {
+ return;
+ }
+
+ thread->Dispatch(new ResolveReplyRunnable(aSdRef,
+ aFlags,
+ aInterfaceIndex,
+ aErrorCode,
+ nsCString(aFullName),
+ nsCString(aHostTarget),
+ NativeEndian::swapFromNetworkOrder(aPort),
+ aTxtLen,
+ aTxtRecord,
+ obj),
+ NS_DISPATCH_NORMAL);
+}
+
+GetAddrInfoReplyRunnable::GetAddrInfoReplyRunnable(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aHostName,
+ const mozilla::net::NetAddr& aAddress,
+ uint32_t aTTL,
+ GetAddrInfoOperator* aContext)
+ : mSdRef(aSdRef)
+ , mFlags(aFlags)
+ , mInterfaceIndex(aInterfaceIndex)
+ , mErrorCode(aErrorCode)
+ , mHostName(aHostName)
+ , mAddress(aAddress)
+ , mTTL(aTTL)
+ , mContext(aContext)
+{
+}
+
+GetAddrInfoReplyRunnable::~GetAddrInfoReplyRunnable()
+{
+}
+
+NS_IMETHODIMP
+GetAddrInfoReplyRunnable::Run()
+{
+ MOZ_ASSERT(mContext);
+ mContext->Reply(mSdRef,
+ mFlags,
+ mInterfaceIndex,
+ mErrorCode,
+ mHostName,
+ mAddress,
+ mTTL);
+ return NS_OK;
+}
+
+void
+GetAddrInfoReplyRunnable::Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const char* aHostName,
+ const struct sockaddr* aAddress,
+ uint32_t aTTL,
+ void* aContext)
+{
+ MOZ_ASSERT(PR_GetCurrentThread() == gSocketThread);
+
+ GetAddrInfoOperator* obj(reinterpret_cast<GetAddrInfoOperator*>(aContext));
+ if (!obj) {
+ return;
+ }
+
+ nsCOMPtr<nsIThread> thread(obj->GetThread());
+ if (!thread) {
+ return;
+ }
+
+ NetAddr address;
+ address.raw.family = aAddress->sa_family;
+
+ static_assert(sizeof(address.raw.data) >= sizeof(aAddress->sa_data),
+ "size of sockaddr.sa_data is too big");
+ memcpy(&address.raw.data, aAddress->sa_data, sizeof(aAddress->sa_data));
+
+ thread->Dispatch(new GetAddrInfoReplyRunnable(aSdRef,
+ aFlags,
+ aInterfaceIndex,
+ aErrorCode,
+ nsCString(aHostName),
+ address,
+ aTTL,
+ obj),
+ NS_DISPATCH_NORMAL);
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/mdns/libmdns/MDNSResponderReply.h b/netwerk/dns/mdns/libmdns/MDNSResponderReply.h
new file mode 100644
index 0000000000..794a585f80
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/MDNSResponderReply.h
@@ -0,0 +1,164 @@
+/* -*- 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_netwerk_dns_mdns_libmdns_MDNSResponderReply_h
+#define mozilla_netwerk_dns_mdns_libmdns_MDNSResponderReply_h
+
+#include "dns_sd.h"
+#include "MDNSResponderOperator.h"
+#include "mozilla/UniquePtr.h"
+#include "nsIThread.h"
+#include "mozilla/net/DNS.h"
+#include "mozilla/RefPtr.h"
+#include "nsThreadUtils.h"
+
+namespace mozilla {
+namespace net {
+
+class BrowseReplyRunnable final : public Runnable
+{
+public:
+ BrowseReplyRunnable(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aServiceName,
+ const nsACString& aRegType,
+ const nsACString& aReplyDomain,
+ BrowseOperator* aContext);
+
+ NS_IMETHOD Run() override;
+
+ static void Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const char* aServiceName,
+ const char* aRegType,
+ const char* aReplyDomain,
+ void* aContext);
+
+private:
+ DNSServiceRef mSdRef;
+ DNSServiceFlags mFlags;
+ uint32_t mInterfaceIndex;
+ DNSServiceErrorType mErrorCode;
+ nsCString mServiceName;
+ nsCString mRegType;
+ nsCString mReplyDomain;
+ RefPtr<BrowseOperator> mContext;
+};
+
+class RegisterReplyRunnable final : public Runnable
+{
+public:
+ RegisterReplyRunnable(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aName,
+ const nsACString& aRegType,
+ const nsACString& aDomain,
+ RegisterOperator* aContext);
+
+ NS_IMETHOD Run() override;
+
+ static void Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ DNSServiceErrorType aErrorCode,
+ const char* aName,
+ const char* aRegType,
+ const char* aDomain,
+ void* aContext);
+
+private:
+ DNSServiceRef mSdRef;
+ DNSServiceFlags mFlags;
+ DNSServiceErrorType mErrorCode;
+ nsCString mName;
+ nsCString mRegType;
+ nsCString mDomain;
+ RefPtr<RegisterOperator> mContext;
+};
+
+class ResolveReplyRunnable final : public Runnable
+{
+public:
+ ResolveReplyRunnable(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aFullName,
+ const nsACString& aHostTarget,
+ uint16_t aPort,
+ uint16_t aTxtLen,
+ const unsigned char* aTxtRecord,
+ ResolveOperator* aContext);
+ ~ResolveReplyRunnable();
+
+ NS_IMETHOD Run() override;
+
+ static void Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const char* aFullName,
+ const char* aHostTarget,
+ uint16_t aPort,
+ uint16_t aTxtLen,
+ const unsigned char* aTxtRecord,
+ void* aContext);
+
+private:
+ DNSServiceRef mSdRef;
+ DNSServiceFlags mFlags;
+ uint32_t mInterfaceIndex;
+ DNSServiceErrorType mErrorCode;
+ nsCString mFullname;
+ nsCString mHosttarget;
+ uint16_t mPort;
+ uint16_t mTxtLen;
+ UniquePtr<unsigned char> mTxtRecord;
+ RefPtr<ResolveOperator> mContext;
+};
+
+class GetAddrInfoReplyRunnable final : public Runnable
+{
+public:
+ GetAddrInfoReplyRunnable(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const nsACString& aHostName,
+ const mozilla::net::NetAddr& aAddress,
+ uint32_t aTTL,
+ GetAddrInfoOperator* aContext);
+ ~GetAddrInfoReplyRunnable();
+
+ NS_IMETHOD Run() override;
+
+ static void Reply(DNSServiceRef aSdRef,
+ DNSServiceFlags aFlags,
+ uint32_t aInterfaceIndex,
+ DNSServiceErrorType aErrorCode,
+ const char* aHostName,
+ const struct sockaddr* aAddress,
+ uint32_t aTTL,
+ void* aContext);
+
+private:
+ DNSServiceRef mSdRef;
+ DNSServiceFlags mFlags;
+ uint32_t mInterfaceIndex;
+ DNSServiceErrorType mErrorCode;
+ nsCString mHostName;
+ mozilla::net::NetAddr mAddress;
+ uint32_t mTTL;
+ RefPtr<GetAddrInfoOperator> mContext;
+};
+
+} // namespace net
+} // namespace mozilla
+
+ #endif // mozilla_netwerk_dns_mdns_libmdns_MDNSResponderReply_h
diff --git a/netwerk/dns/mdns/libmdns/moz.build b/netwerk/dns/mdns/libmdns/moz.build
index 5a67c06118..23445756c6 100644
--- a/netwerk/dns/mdns/libmdns/moz.build
+++ b/netwerk/dns/mdns/libmdns/moz.build
@@ -3,20 +3,32 @@
# 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/.
-EXTRA_COMPONENTS += [
- 'nsDNSServiceDiscovery.js',
- 'nsDNSServiceDiscovery.manifest',
-]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ SOURCES += [
+ 'MDNSResponderOperator.cpp',
+ 'MDNSResponderReply.cpp',
+ 'nsDNSServiceDiscovery.cpp',
+ ]
-EXTRA_JS_MODULES += [
- 'fallback/DataReader.jsm',
- 'fallback/DataWriter.jsm',
- 'fallback/DNSPacket.jsm',
- 'fallback/DNSRecord.jsm',
- 'fallback/DNSResourceRecord.jsm',
- 'fallback/DNSTypes.jsm',
- 'fallback/MulticastDNS.jsm',
-]
+ LOCAL_INCLUDES += [
+ '/netwerk/base',
+ ]
+
+else:
+ EXTRA_COMPONENTS += [
+ 'nsDNSServiceDiscovery.js',
+ 'nsDNSServiceDiscovery.manifest',
+ ]
+
+ EXTRA_JS_MODULES += [
+ 'fallback/DataReader.jsm',
+ 'fallback/DataWriter.jsm',
+ 'fallback/DNSPacket.jsm',
+ 'fallback/DNSRecord.jsm',
+ 'fallback/DNSResourceRecord.jsm',
+ 'fallback/DNSTypes.jsm',
+ 'fallback/MulticastDNS.jsm',
+ ]
SOURCES += [
'nsDNSServiceInfo.cpp',
diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp
new file mode 100644
index 0000000000..cec8033d18
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.cpp
@@ -0,0 +1,262 @@
+/* -*- 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 "nsDNSServiceDiscovery.h"
+#include "MDNSResponderOperator.h"
+#include "nsICancelable.h"
+#include "nsXULAppAPI.h"
+#include "private/pprio.h"
+
+namespace mozilla {
+namespace net {
+
+namespace {
+
+inline void
+StartService()
+{
+ /*** STUB ***/
+}
+
+inline void
+StopService()
+{
+ /*** STUB ***/
+}
+
+class ServiceCounter
+{
+public:
+ static bool IsServiceRunning()
+ {
+ return !!sUseCount;
+ }
+
+private:
+ static uint32_t sUseCount;
+
+protected:
+ ServiceCounter()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!sUseCount++) {
+ StartService();
+ }
+ }
+
+ virtual ~ServiceCounter()
+ {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!--sUseCount) {
+ StopService();
+ }
+ }
+};
+
+uint32_t ServiceCounter::sUseCount = 0;
+
+class DiscoveryRequest final : public nsICancelable
+ , private ServiceCounter
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICANCELABLE
+
+ explicit DiscoveryRequest(nsDNSServiceDiscovery* aService,
+ nsIDNSServiceDiscoveryListener* aListener);
+
+private:
+ virtual ~DiscoveryRequest() { Cancel(NS_OK); }
+
+ RefPtr<nsDNSServiceDiscovery> mService;
+ nsIDNSServiceDiscoveryListener* mListener;
+};
+
+NS_IMPL_ISUPPORTS(DiscoveryRequest, nsICancelable)
+
+DiscoveryRequest::DiscoveryRequest(nsDNSServiceDiscovery* aService,
+ nsIDNSServiceDiscoveryListener* aListener)
+ : mService(aService)
+ , mListener(aListener)
+{
+}
+
+NS_IMETHODIMP
+DiscoveryRequest::Cancel(nsresult aReason)
+{
+ if (mService) {
+ mService->StopDiscovery(mListener);
+ }
+
+ mService = nullptr;
+ return NS_OK;
+}
+
+class RegisterRequest final : public nsICancelable
+ , private ServiceCounter
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSICANCELABLE
+
+ explicit RegisterRequest(nsDNSServiceDiscovery* aService,
+ nsIDNSRegistrationListener* aListener);
+
+private:
+ virtual ~RegisterRequest() { Cancel(NS_OK); }
+
+ RefPtr<nsDNSServiceDiscovery> mService;
+ nsIDNSRegistrationListener* mListener;
+};
+
+NS_IMPL_ISUPPORTS(RegisterRequest, nsICancelable)
+
+RegisterRequest::RegisterRequest(nsDNSServiceDiscovery* aService,
+ nsIDNSRegistrationListener* aListener)
+ : mService(aService)
+ , mListener(aListener)
+{
+}
+
+NS_IMETHODIMP
+RegisterRequest::Cancel(nsresult aReason)
+{
+ if (mService) {
+ mService->UnregisterService(mListener);
+ }
+
+ mService = nullptr;
+ return NS_OK;
+}
+
+} // namespace anonymous
+
+NS_IMPL_ISUPPORTS(nsDNSServiceDiscovery, nsIDNSServiceDiscovery)
+
+nsDNSServiceDiscovery::~nsDNSServiceDiscovery()
+{
+}
+
+nsresult
+nsDNSServiceDiscovery::Init()
+{
+ if (!XRE_IsParentProcess()) {
+ MOZ_ASSERT(false, "nsDNSServiceDiscovery can only be used in parent process");
+ return NS_ERROR_FAILURE;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceDiscovery::StartDiscovery(const nsACString& aServiceType,
+ nsIDNSServiceDiscoveryListener* aListener,
+ nsICancelable** aRetVal)
+{
+ MOZ_ASSERT(aRetVal);
+
+ nsresult rv;
+ if (NS_WARN_IF(NS_FAILED(rv = StopDiscovery(aListener)))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsICancelable> req = new DiscoveryRequest(this, aListener);
+ RefPtr<BrowseOperator> browserOp = new BrowseOperator(aServiceType,
+ aListener);
+ if (NS_WARN_IF(NS_FAILED(rv = browserOp->Start()))) {
+ return rv;
+ }
+
+ mDiscoveryMap.Put(aListener, browserOp);
+
+ req.forget(aRetVal);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceDiscovery::StopDiscovery(nsIDNSServiceDiscoveryListener* aListener)
+{
+ nsresult rv;
+
+ RefPtr<BrowseOperator> browserOp;
+ if (!mDiscoveryMap.Get(aListener, getter_AddRefs(browserOp))) {
+ return NS_OK;
+ }
+
+ browserOp->Cancel(); // cancel non-started operation
+ if (NS_WARN_IF(NS_FAILED(rv = browserOp->Stop()))) {
+ return rv;
+ }
+
+ mDiscoveryMap.Remove(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceDiscovery::RegisterService(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSRegistrationListener* aListener,
+ nsICancelable** aRetVal)
+{
+ MOZ_ASSERT(aRetVal);
+
+ nsresult rv;
+ if (NS_WARN_IF(NS_FAILED(rv = UnregisterService(aListener)))) {
+ return rv;
+ }
+
+ nsCOMPtr<nsICancelable> req = new RegisterRequest(this, aListener);
+ RefPtr<RegisterOperator> registerOp = new RegisterOperator(aServiceInfo,
+ aListener);
+ if (NS_WARN_IF(NS_FAILED(rv = registerOp->Start()))) {
+ return rv;
+ }
+
+ mRegisterMap.Put(aListener, registerOp);
+
+ req.forget(aRetVal);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceDiscovery::UnregisterService(nsIDNSRegistrationListener* aListener)
+{
+ nsresult rv;
+
+ RefPtr<RegisterOperator> registerOp;
+ if (!mRegisterMap.Get(aListener, getter_AddRefs(registerOp))) {
+ return NS_OK;
+ }
+
+ registerOp->Cancel(); // cancel non-started operation
+ if (NS_WARN_IF(NS_FAILED(rv = registerOp->Stop()))) {
+ return rv;
+ }
+
+ mRegisterMap.Remove(aListener);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDNSServiceDiscovery::ResolveService(nsIDNSServiceInfo* aServiceInfo,
+ nsIDNSServiceResolveListener* aListener)
+{
+ if (!ServiceCounter::IsServiceRunning()) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsresult rv;
+
+ RefPtr<ResolveOperator> resolveOp = new ResolveOperator(aServiceInfo,
+ aListener);
+ if (NS_WARN_IF(NS_FAILED(rv = resolveOp->Start()))) {
+ return rv;
+ }
+
+ return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
diff --git a/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h
new file mode 100644
index 0000000000..abe98f3574
--- /dev/null
+++ b/netwerk/dns/mdns/libmdns/nsDNSServiceDiscovery.h
@@ -0,0 +1,49 @@
+/* -*- 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_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h
+#define mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h
+
+#include "MDNSResponderOperator.h"
+#include "nsIDNSServiceDiscovery.h"
+#include "nsCOMPtr.h"
+#include "mozilla/RefPtr.h"
+#include "nsRefPtrHashtable.h"
+
+namespace mozilla {
+namespace net {
+
+class BrowseOperator;
+class RegisterOperator;
+
+class nsDNSServiceDiscovery final : public nsIDNSServiceDiscovery
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIDNSSERVICEDISCOVERY
+
+ explicit nsDNSServiceDiscovery() = default;
+
+ /*
+ ** The mDNS service is started on demand. If no one uses, mDNS service will not
+ ** start. Therefore, all operations before service started will fail
+ ** and get error code |kDNSServiceErr_ServiceNotRunning| defined in dns_sd.h.
+ **/
+ nsresult Init();
+
+ nsresult StopDiscovery(nsIDNSServiceDiscoveryListener* aListener);
+ nsresult UnregisterService(nsIDNSRegistrationListener* aListener);
+
+private:
+ virtual ~nsDNSServiceDiscovery();
+
+ nsRefPtrHashtable<nsISupportsHashKey, BrowseOperator> mDiscoveryMap;
+ nsRefPtrHashtable<nsISupportsHashKey, RegisterOperator> mRegisterMap;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // mozilla_netwerk_dns_mdns_libmdns_nsDNSServiceDiscovery_h
diff --git a/netwerk/streamconv/converters/moz.build b/netwerk/streamconv/converters/moz.build
index 546cfb9989..8630922404 100644
--- a/netwerk/streamconv/converters/moz.build
+++ b/netwerk/streamconv/converters/moz.build
@@ -11,7 +11,6 @@ XPIDL_MODULE = 'necko_http'
SOURCES += [
'mozTXTToHTMLConv.cpp',
- 'nsBinHexDecoder.cpp',
'nsDirIndex.cpp',
'nsDirIndexParser.cpp',
'nsHTTPCompressConv.cpp',
@@ -27,6 +26,11 @@ if 'ftp' in CONFIG['NECKO_PROTOCOLS']:
'ParseFTPList.cpp',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'cocoa':
+ SOURCES += [
+ 'nsBinHexDecoder.cpp',
+ ]
+
FINAL_LIBRARY = 'xul'
LOCAL_INCLUDES += [
diff --git a/netwerk/system/mac/moz.build b/netwerk/system/mac/moz.build
new file mode 100644
index 0000000000..f884a08b7c
--- /dev/null
+++ b/netwerk/system/mac/moz.build
@@ -0,0 +1,13 @@
+# -*- 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/.
+
+SOURCES += [
+ 'nsNetworkLinkService.mm',
+]
+
+FINAL_LIBRARY = 'xul'
+
+if CONFIG['CLANG_CXX']:
+ CXXFLAGS += ['-Wno-error=shadow']
diff --git a/netwerk/system/mac/nsNetworkLinkService.h b/netwerk/system/mac/nsNetworkLinkService.h
new file mode 100644
index 0000000000..ee54622478
--- /dev/null
+++ b/netwerk/system/mac/nsNetworkLinkService.h
@@ -0,0 +1,54 @@
+/* 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 NSNETWORKLINKSERVICEMAC_H_
+#define NSNETWORKLINKSERVICEMAC_H_
+
+#include "nsINetworkLinkService.h"
+#include "nsIObserver.h"
+
+#include <SystemConfiguration/SCNetworkReachability.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+
+class nsNetworkLinkService : public nsINetworkLinkService,
+ public nsIObserver
+{
+public:
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSINETWORKLINKSERVICE
+ NS_DECL_NSIOBSERVER
+
+ nsNetworkLinkService();
+
+ nsresult Init();
+ nsresult Shutdown();
+
+protected:
+ virtual ~nsNetworkLinkService();
+
+private:
+ bool mLinkUp;
+ bool mStatusKnown;
+
+ // Toggles allowing the sending of network-changed event.
+ bool mAllowChangedEvent;
+
+ SCNetworkReachabilityRef mReachability;
+ CFRunLoopRef mCFRunLoop;
+ CFRunLoopSourceRef mRunLoopSource;
+ SCDynamicStoreRef mStoreRef;
+
+ void UpdateReachability();
+ void SendEvent(bool aNetworkChanged);
+ static void ReachabilityChanged(SCNetworkReachabilityRef target,
+ SCNetworkConnectionFlags flags,
+ void *info);
+ static void IPConfigChanged(SCDynamicStoreRef store,
+ CFArrayRef changedKeys,
+ void *info);
+ void calculateNetworkId(void);
+ nsCString mNetworkId;
+};
+
+#endif /* NSNETWORKLINKSERVICEMAC_H_ */
diff --git a/netwerk/system/mac/nsNetworkLinkService.mm b/netwerk/system/mac/nsNetworkLinkService.mm
new file mode 100644
index 0000000000..5b2d7575ac
--- /dev/null
+++ b/netwerk/system/mac/nsNetworkLinkService.mm
@@ -0,0 +1,526 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 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/. */
+
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <arpa/inet.h>
+#include "nsCOMPtr.h"
+#include "nsIObserverService.h"
+#include "nsServiceManagerUtils.h"
+#include "nsString.h"
+#include "nsCRT.h"
+#include "mozilla/Logging.h"
+#include "mozilla/Preferences.h"
+#include "mozilla/SHA1.h"
+#include "mozilla/Base64.h"
+#include "nsNetworkLinkService.h"
+
+#import <Cocoa/Cocoa.h>
+#import <netinet/in.h>
+
+#define NETWORK_NOTIFY_CHANGED_PREF "network.notify.changed"
+
+using namespace mozilla;
+
+static LazyLogModule gNotifyAddrLog("nsNotifyAddr");
+#define LOG(args) MOZ_LOG(gNotifyAddrLog, mozilla::LogLevel::Debug, args)
+
+// If non-successful, extract the error code and return it. This
+// error code dance is inspired by
+// http://developer.apple.com/technotes/tn/tn1145.html
+static OSStatus getErrorCodeBool(Boolean success)
+{
+ OSStatus err = noErr;
+ if (!success) {
+ int scErr = ::SCError();
+ if (scErr == kSCStatusOK) {
+ scErr = kSCStatusFailed;
+ }
+ err = scErr;
+ }
+ return err;
+}
+
+// If given a NULL pointer, return the error code.
+static OSStatus getErrorCodePtr(const void *value)
+{
+ return getErrorCodeBool(value != NULL);
+}
+
+// Convenience function to allow NULL input.
+static void CFReleaseSafe(CFTypeRef cf)
+{
+ if (cf) {
+ // "If cf is NULL, this will cause a runtime error and your
+ // application will crash." / Apple docs
+ ::CFRelease(cf);
+ }
+}
+
+NS_IMPL_ISUPPORTS(nsNetworkLinkService,
+ nsINetworkLinkService,
+ nsIObserver)
+
+nsNetworkLinkService::nsNetworkLinkService()
+ : mLinkUp(true)
+ , mStatusKnown(false)
+ , mAllowChangedEvent(true)
+ , mReachability(nullptr)
+ , mCFRunLoop(nullptr)
+ , mRunLoopSource(nullptr)
+ , mStoreRef(nullptr)
+{
+}
+
+nsNetworkLinkService::~nsNetworkLinkService() = default;
+
+NS_IMETHODIMP
+nsNetworkLinkService::GetIsLinkUp(bool *aIsUp)
+{
+ *aIsUp = mLinkUp;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNetworkLinkService::GetLinkStatusKnown(bool *aIsUp)
+{
+ *aIsUp = mStatusKnown;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNetworkLinkService::GetLinkType(uint32_t *aLinkType)
+{
+ NS_ENSURE_ARG_POINTER(aLinkType);
+
+ // XXX This function has not yet been implemented for this platform
+ *aLinkType = nsINetworkLinkService::LINK_TYPE_UNKNOWN;
+ return NS_OK;
+}
+
+#ifndef SA_SIZE
+#define SA_SIZE(sa) \
+ ( (!(sa) || ((struct sockaddr *)(sa))->sa_len == 0) ? \
+ sizeof(uint32_t) : \
+ 1 + ( (((struct sockaddr *)(sa))->sa_len - 1) | (sizeof(uint32_t) - 1) ) )
+#endif
+
+static char *getMac(struct sockaddr_dl *sdl, char *buf, size_t bufsize)
+{
+ char *cp;
+ int n, p = 0;
+
+ buf[0] = 0;
+ cp = (char *)LLADDR(sdl);
+ n = sdl->sdl_alen;
+ if (n > 0) {
+ while (--n >= 0) {
+ p += snprintf(&buf[p], bufsize - p, "%02x%s",
+ *cp++ & 0xff, n > 0 ? ":" : "");
+ }
+ }
+ return buf;
+}
+
+/* If the IP matches, get the MAC and return true */
+static bool matchIp(struct sockaddr_dl *sdl, struct sockaddr_inarp *addr,
+ char *ip, char *buf, size_t bufsize)
+{
+ if (sdl->sdl_alen) {
+ if (!strcmp(inet_ntoa(addr->sin_addr), ip)) {
+ getMac(sdl, buf, bufsize);
+ return true; /* done! */
+ }
+ }
+ return false; /* continue */
+}
+
+/*
+ * Scan for the 'IP' address in the ARP table and store the corresponding MAC
+ * address in 'mac'. The output buffer is 'maclen' bytes big.
+ *
+ * Returns 'true' if it found the IP and returns a MAC.
+ */
+static bool scanArp(char *ip, char *mac, size_t maclen)
+{
+ int mib[6];
+ char *lim, *next;
+ int st;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_FLAGS;
+ mib[5] = RTF_LLINFO;
+
+ size_t needed;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ return false;
+ }
+ if (needed == 0) {
+ // empty table
+ return false;
+ }
+
+ UniquePtr <char[]>buf(new char[needed]);
+
+ for (;;) {
+ st = sysctl(mib, 6, &buf[0], &needed, NULL, 0);
+ if (st == 0 || errno != ENOMEM) {
+ break;
+ }
+ needed += needed / 8;
+
+ auto tmp = MakeUnique<char[]>(needed);
+ memcpy(&tmp[0], &buf[0], needed);
+ buf = Move(tmp);
+ }
+ if (st == -1) {
+ return false;
+ }
+ lim = &buf[needed];
+
+ struct rt_msghdr *rtm;
+ for (next = &buf[0]; next < lim; next += rtm->rtm_msglen) {
+ rtm = reinterpret_cast<struct rt_msghdr *>(next);
+ struct sockaddr_inarp *sin2 =
+ reinterpret_cast<struct sockaddr_inarp *>(rtm + 1);
+ struct sockaddr_dl *sdl =
+ reinterpret_cast<struct sockaddr_dl *>
+ ((char *)sin2 + SA_SIZE(sin2));
+ if (matchIp(sdl, sin2, ip, mac, maclen)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static int routingTable(char *gw, size_t aGwLen)
+{
+ size_t needed;
+ int mib[6];
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa;
+ struct sockaddr_in *sockin;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = 0;
+ mib[4] = NET_RT_DUMP;
+ mib[5] = 0;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0) {
+ return 1;
+ }
+
+ UniquePtr <char[]>buf(new char[needed]);
+
+ if (sysctl(mib, 6, &buf[0], &needed, NULL, 0) < 0) {
+ return 3;
+ }
+
+ rtm = reinterpret_cast<struct rt_msghdr *>(&buf[0]);
+ sa = reinterpret_cast<struct sockaddr *>(rtm + 1);
+ sa = reinterpret_cast<struct sockaddr *>(SA_SIZE(sa) + (char *)sa);
+ sockin = reinterpret_cast<struct sockaddr_in *>(sa);
+ inet_ntop(AF_INET, &sockin->sin_addr.s_addr, gw, aGwLen-1);
+
+ return 0;
+}
+
+//
+// Figure out the current "network identification" string.
+//
+// It detects the IP of the default gateway in the routing table, then the MAC
+// address of that IP in the ARP table before it hashes that string (to avoid
+// information leakage).
+//
+void nsNetworkLinkService::calculateNetworkId(void)
+{
+ bool found = false;
+ char hw[MAXHOSTNAMELEN];
+ if (!routingTable(hw, sizeof(hw))) {
+ char mac[256]; // big enough for a printable MAC address
+ if (scanArp(hw, mac, sizeof(mac))) {
+ LOG(("networkid: MAC %s\n", hw));
+ nsAutoCString mac(hw);
+ // This 'addition' could potentially be a
+ // fixed number from the profile or something.
+ nsAutoCString addition("local-rubbish");
+ nsAutoCString output;
+ SHA1Sum sha1;
+ nsCString combined(mac + addition);
+ sha1.update(combined.get(), combined.Length());
+ uint8_t digest[SHA1Sum::kHashSize];
+ sha1.finish(digest);
+ nsCString newString(reinterpret_cast<char*>(digest),
+ SHA1Sum::kHashSize);
+ nsresult rv = Base64Encode(newString, output);
+ MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv));
+ LOG(("networkid: id %s\n", output.get()));
+ if (mNetworkId != output) {
+ // new id
+ mNetworkId = output;
+ }
+ else {
+ // same id
+ }
+ found = true;
+ }
+ }
+ if (!found) {
+ // no id
+ }
+}
+
+
+NS_IMETHODIMP
+nsNetworkLinkService::Observe(nsISupports *subject,
+ const char *topic,
+ const char16_t *data)
+{
+ if (!strcmp(topic, "xpcom-shutdown")) {
+ Shutdown();
+ }
+
+ return NS_OK;
+}
+
+/* static */
+void
+nsNetworkLinkService::IPConfigChanged(SCDynamicStoreRef aStoreREf,
+ CFArrayRef aChangedKeys,
+ void *aInfo)
+{
+ nsNetworkLinkService *service =
+ static_cast<nsNetworkLinkService*>(aInfo);
+ service->SendEvent(true);
+}
+
+nsresult
+nsNetworkLinkService::Init(void)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIObserverService> observerService =
+ do_GetService("@mozilla.org/observer-service;1", &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = observerService->AddObserver(this, "xpcom-shutdown", false);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ Preferences::AddBoolVarCache(&mAllowChangedEvent,
+ NETWORK_NOTIFY_CHANGED_PREF, true);
+
+ // If the network reachability API can reach 0.0.0.0 without
+ // requiring a connection, there is a network interface available.
+ struct sockaddr_in addr;
+ bzero(&addr, sizeof(addr));
+ addr.sin_len = sizeof(addr);
+ addr.sin_family = AF_INET;
+ mReachability =
+ ::SCNetworkReachabilityCreateWithAddress(NULL,
+ (struct sockaddr *)&addr);
+ if (!mReachability) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ SCNetworkReachabilityContext context = {0, this, NULL, NULL, NULL};
+ if (!::SCNetworkReachabilitySetCallback(mReachability,
+ ReachabilityChanged,
+ &context)) {
+ NS_WARNING("SCNetworkReachabilitySetCallback failed.");
+ ::CFRelease(mReachability);
+ mReachability = NULL;
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ SCDynamicStoreContext storeContext = {0, this, NULL, NULL, NULL};
+ mStoreRef =
+ ::SCDynamicStoreCreate(NULL,
+ CFSTR("AddIPAddressListChangeCallbackSCF"),
+ IPConfigChanged, &storeContext);
+
+ CFStringRef patterns[2] = {NULL, NULL};
+ OSStatus err = getErrorCodePtr(mStoreRef);
+ if (err == noErr) {
+ // This pattern is "State:/Network/Service/[^/]+/IPv4".
+ patterns[0] =
+ ::SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
+ kSCDynamicStoreDomainState,
+ kSCCompAnyRegex,
+ kSCEntNetIPv4);
+ err = getErrorCodePtr(patterns[0]);
+ if (err == noErr) {
+ // This pattern is "State:/Network/Service/[^/]+/IPv6".
+ patterns[1] =
+ ::SCDynamicStoreKeyCreateNetworkServiceEntity(NULL,
+ kSCDynamicStoreDomainState,
+ kSCCompAnyRegex,
+ kSCEntNetIPv6);
+ err = getErrorCodePtr(patterns[1]);
+ }
+ }
+
+ CFArrayRef patternList = NULL;
+ // Create a pattern list containing just one pattern,
+ // then tell SCF that we want to watch changes in keys
+ // that match that pattern list, then create our run loop
+ // source.
+ if (err == noErr) {
+ patternList = ::CFArrayCreate(NULL, (const void **) patterns,
+ 2, &kCFTypeArrayCallBacks);
+ if (!patternList) {
+ err = -1;
+ }
+ }
+ if (err == noErr) {
+ err =
+ getErrorCodeBool(::SCDynamicStoreSetNotificationKeys(mStoreRef,
+ NULL,
+ patternList));
+ }
+
+ if (err == noErr) {
+ mRunLoopSource =
+ ::SCDynamicStoreCreateRunLoopSource(NULL, mStoreRef, 0);
+ err = getErrorCodePtr(mRunLoopSource);
+ }
+
+ CFReleaseSafe(patterns[0]);
+ CFReleaseSafe(patterns[1]);
+ CFReleaseSafe(patternList);
+
+ if (err != noErr) {
+ CFReleaseSafe(mStoreRef);
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // Get the current run loop. This service is initialized at startup,
+ // so we shouldn't run in to any problems with modal dialog run loops.
+ mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop];
+ if (!mCFRunLoop) {
+ NS_WARNING("Could not get current run loop.");
+ ::CFRelease(mReachability);
+ mReachability = NULL;
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ ::CFRetain(mCFRunLoop);
+
+ ::CFRunLoopAddSource(mCFRunLoop, mRunLoopSource, kCFRunLoopDefaultMode);
+
+ if (!::SCNetworkReachabilityScheduleWithRunLoop(mReachability, mCFRunLoop,
+ kCFRunLoopDefaultMode)) {
+ NS_WARNING("SCNetworkReachabilityScheduleWIthRunLoop failed.");
+ ::CFRelease(mReachability);
+ mReachability = NULL;
+ ::CFRelease(mCFRunLoop);
+ mCFRunLoop = NULL;
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ UpdateReachability();
+
+ return NS_OK;
+}
+
+nsresult
+nsNetworkLinkService::Shutdown()
+{
+ if (!::SCNetworkReachabilityUnscheduleFromRunLoop(mReachability,
+ mCFRunLoop,
+ kCFRunLoopDefaultMode)) {
+ NS_WARNING("SCNetworkReachabilityUnscheduleFromRunLoop failed.");
+ }
+
+ CFRunLoopRemoveSource(mCFRunLoop, mRunLoopSource, kCFRunLoopDefaultMode);
+
+ ::CFRelease(mReachability);
+ mReachability = nullptr;
+
+ ::CFRelease(mCFRunLoop);
+ mCFRunLoop = nullptr;
+
+ ::CFRelease(mStoreRef);
+ mStoreRef = nullptr;
+
+ ::CFRelease(mRunLoopSource);
+ mRunLoopSource = nullptr;
+
+ return NS_OK;
+}
+
+void
+nsNetworkLinkService::UpdateReachability()
+{
+ if (!mReachability) {
+ return;
+ }
+
+ SCNetworkConnectionFlags flags;
+ if (!::SCNetworkReachabilityGetFlags(mReachability, &flags)) {
+ mStatusKnown = false;
+ return;
+ }
+
+ bool reachable = (flags & kSCNetworkFlagsReachable) != 0;
+ bool needsConnection = (flags & kSCNetworkFlagsConnectionRequired) != 0;
+
+ mLinkUp = (reachable && !needsConnection);
+ mStatusKnown = true;
+}
+
+void
+nsNetworkLinkService::SendEvent(bool aNetworkChanged)
+{
+ nsCOMPtr<nsIObserverService> observerService =
+ do_GetService("@mozilla.org/observer-service;1");
+ if (!observerService) {
+ return;
+ }
+
+ const char *event;
+ if (aNetworkChanged) {
+ if (!mAllowChangedEvent) {
+ return;
+ }
+ event = NS_NETWORK_LINK_DATA_CHANGED;
+ } else if (!mStatusKnown) {
+ event = NS_NETWORK_LINK_DATA_UNKNOWN;
+ } else {
+ event = mLinkUp ? NS_NETWORK_LINK_DATA_UP
+ : NS_NETWORK_LINK_DATA_DOWN;
+ }
+ LOG(("SendEvent: network is '%s'\n", event));
+
+ observerService->NotifyObservers(static_cast<nsINetworkLinkService*>(this),
+ NS_NETWORK_LINK_TOPIC,
+ NS_ConvertASCIItoUTF16(event).get());
+}
+
+/* static */
+void
+nsNetworkLinkService::ReachabilityChanged(SCNetworkReachabilityRef target,
+ SCNetworkConnectionFlags flags,
+ void *info)
+{
+ nsNetworkLinkService *service =
+ static_cast<nsNetworkLinkService*>(info);
+
+ service->UpdateReachability();
+ service->SendEvent(false);
+ service->calculateNetworkId();
+}
diff --git a/netwerk/system/moz.build b/netwerk/system/moz.build
index a8034d7497..dcf7d6c3f1 100644
--- a/netwerk/system/moz.build
+++ b/netwerk/system/moz.build
@@ -5,5 +5,7 @@
if CONFIG['OS_ARCH'] == 'WINNT':
DIRS += ['win32']
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ DIRS += ['mac']
elif CONFIG['OS_ARCH'] == 'Linux':
DIRS += ['linux']
diff --git a/toolkit/components/parentalcontrols/moz.build b/toolkit/components/parentalcontrols/moz.build
index 6c8bd9a8ce..577162945b 100644
--- a/toolkit/components/parentalcontrols/moz.build
+++ b/toolkit/components/parentalcontrols/moz.build
@@ -10,6 +10,8 @@ XPIDL_MODULE = 'parentalcontrols'
if not CONFIG['MOZ_DISABLE_PARENTAL_CONTROLS']:
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
SOURCES += ['nsParentalControlsServiceWin.cpp']
+ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += ['nsParentalControlsServiceCocoa.mm']
else:
SOURCES += ['nsParentalControlsServiceDefault.cpp']
diff --git a/toolkit/components/parentalcontrols/nsParentalControlsServiceCocoa.mm b/toolkit/components/parentalcontrols/nsParentalControlsServiceCocoa.mm
new file mode 100644
index 0000000000..0eb0184001
--- /dev/null
+++ b/toolkit/components/parentalcontrols/nsParentalControlsServiceCocoa.mm
@@ -0,0 +1,79 @@
+/* -*- 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 "nsParentalControlsService.h"
+#include "nsString.h"
+#include "nsIFile.h"
+
+#import <Cocoa/Cocoa.h>
+
+NS_IMPL_ISUPPORTS(nsParentalControlsService, nsIParentalControlsService)
+
+nsParentalControlsService::nsParentalControlsService() :
+ mEnabled(false)
+{
+ mEnabled = CFPreferencesAppValueIsForced(CFSTR("restrictWeb"),
+ CFSTR("com.apple.familycontrols.contentfilter"));
+}
+
+nsParentalControlsService::~nsParentalControlsService()
+{
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::GetParentalControlsEnabled(bool *aResult)
+{
+ *aResult = mEnabled;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::GetBlockFileDownloadsEnabled(bool *aResult)
+{
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::GetLoggingEnabled(bool *aResult)
+{
+ *aResult = false;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::Log(int16_t aEntryType,
+ bool blocked,
+ nsIURI *aSource,
+ nsIFile *aTarget)
+{
+ // silently drop on the floor
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::RequestURIOverride(nsIURI *aTarget,
+ nsIInterfaceRequestor *aWindowContext,
+ bool *_retval)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::RequestURIOverrides(nsIArray *aTargets,
+ nsIInterfaceRequestor *aWindowContext,
+ bool *_retval)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+nsParentalControlsService::IsAllowed(int16_t aAction,
+ nsIURI *aUri,
+ bool *_retval)
+{
+ return NS_ERROR_NOT_AVAILABLE;
+}
+
diff --git a/toolkit/components/startup/moz.build b/toolkit/components/startup/moz.build
index 7ee23d9ce8..b12fe9a534 100644
--- a/toolkit/components/startup/moz.build
+++ b/toolkit/components/startup/moz.build
@@ -7,7 +7,7 @@ DIRS += ['public']
EXPORTS.mozilla += ['StartupTimeline.h']
-SOURCES += [
+UNIFIED_SOURCES += [
'nsAppStartup.cpp',
'StartupTimeline.cpp',
]
@@ -16,7 +16,9 @@ if CONFIG['MOZ_USERINFO']:
if CONFIG['OS_ARCH'] == 'WINNT':
# This file cannot be built in unified mode because of name clashes with Windows headers.
SOURCES += ['nsUserInfoWin.cpp']
+ elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += ['nsUserInfoMac.mm']
else:
- SOURCES += ['nsUserInfoUnix.cpp']
+ UNIFIED_SOURCES += ['nsUserInfoUnix.cpp']
FINAL_LIBRARY = 'xul'
diff --git a/toolkit/components/startup/nsUserInfoMac.h b/toolkit/components/startup/nsUserInfoMac.h
new file mode 100644
index 0000000000..822e0edd5d
--- /dev/null
+++ b/toolkit/components/startup/nsUserInfoMac.h
@@ -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 __nsUserInfoMac_h
+#define __nsUserInfoMac_h
+
+#include "nsIUserInfo.h"
+#include "nsReadableUtils.h"
+
+class nsUserInfo: public nsIUserInfo
+{
+public:
+ nsUserInfo();
+
+ NS_DECL_ISUPPORTS
+ NS_DECL_NSIUSERINFO
+
+ nsresult GetPrimaryEmailAddress(nsCString &aEmailAddress);
+
+protected:
+ virtual ~nsUserInfo() {}
+};
+
+#endif /* __nsUserInfo_h */
diff --git a/toolkit/components/startup/nsUserInfoMac.mm b/toolkit/components/startup/nsUserInfoMac.mm
new file mode 100644
index 0000000000..1895cf1773
--- /dev/null
+++ b/toolkit/components/startup/nsUserInfoMac.mm
@@ -0,0 +1,84 @@
+/* -*- 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 "nsUserInfoMac.h"
+#include "nsObjCExceptions.h"
+#include "nsString.h"
+
+#import <Cocoa/Cocoa.h>
+#import <AddressBook/AddressBook.h>
+
+NS_IMPL_ISUPPORTS(nsUserInfo, nsIUserInfo)
+
+nsUserInfo::nsUserInfo() {}
+
+NS_IMETHODIMP
+nsUserInfo::GetFullname(char16_t **aFullname)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT
+
+ NS_ConvertUTF8toUTF16 fullName([NSFullUserName() UTF8String]);
+ *aFullname = ToNewUnicode(fullName);
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT
+}
+
+NS_IMETHODIMP
+nsUserInfo::GetUsername(char **aUsername)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT
+
+ nsAutoCString username([NSUserName() UTF8String]);
+ *aUsername = ToNewCString(username);
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT
+}
+
+nsresult
+nsUserInfo::GetPrimaryEmailAddress(nsCString &aEmailAddress)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT
+
+ // Try to get this user's primary email from the system addressbook's "me card"
+ // (if they've filled it)
+ ABPerson *me = [[ABAddressBook sharedAddressBook] me];
+ ABMultiValue *emailAddresses = [me valueForProperty:kABEmailProperty];
+ if ([emailAddresses count] > 0) {
+ // get the index of the primary email, in case there are more than one
+ int primaryEmailIndex = [emailAddresses indexForIdentifier:[emailAddresses primaryIdentifier]];
+ aEmailAddress.Assign([[emailAddresses valueAtIndex:primaryEmailIndex] UTF8String]);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT
+}
+
+NS_IMETHODIMP
+nsUserInfo::GetEmailAddress(char **aEmailAddress)
+{
+ nsAutoCString email;
+ if (NS_SUCCEEDED(GetPrimaryEmailAddress(email)))
+ *aEmailAddress = ToNewCString(email);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsUserInfo::GetDomain(char **aDomain)
+{
+ nsAutoCString email;
+ if (NS_SUCCEEDED(GetPrimaryEmailAddress(email))) {
+ int32_t index = email.FindChar('@');
+ if (index != -1) {
+ // chop off everything before, and including the '@'
+ *aDomain = ToNewCString(Substring(email, index + 1));
+ }
+ }
+ return NS_OK;
+}
diff --git a/toolkit/library/moz.build b/toolkit/library/moz.build
index 81f07be661..c2f12b7763 100644
--- a/toolkit/library/moz.build
+++ b/toolkit/library/moz.build
@@ -12,8 +12,14 @@ def Libxul_defines():
@template
def Libxul(name):
- GeckoSharedLibrary(name, linkage=None)
- SHARED_LIBRARY_NAME = 'xul'
+ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'uikit'):
+ # This is going to be a framework named "XUL", not an ordinary library named
+ # "libxul.dylib"
+ GeckoFramework(name, linkage=None)
+ SHARED_LIBRARY_NAME = 'XUL'
+ else:
+ GeckoSharedLibrary(name, linkage=None)
+ SHARED_LIBRARY_NAME = 'xul'
DELAYLOAD_DLLS += [
'comdlg32.dll',
@@ -121,6 +127,9 @@ if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT'] or \
'freetype',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ CXXFLAGS += CONFIG['TK_CFLAGS']
+
if CONFIG['MOZ_WEBRTC']:
if CONFIG['OS_TARGET'] == 'WINNT':
OS_LIBS += [
@@ -134,6 +143,19 @@ if CONFIG['MOZ_WEBRTC']:
'wininet',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ OS_LIBS += [
+ '-framework OpenGL',
+ '-framework SystemConfiguration',
+ '-framework AVFoundation',
+ '-framework CoreMedia',
+ '-framework IOKit',
+ '-F%s' % CONFIG['MACOS_PRIVATE_FRAMEWORKS_DIR'],
+ '-framework CoreUI',
+ '-framework CoreSymbolication',
+ 'cups',
+ ]
+
if CONFIG['MOZ_WMF']:
OS_LIBS += [
'mfuuid',
@@ -191,6 +213,9 @@ if 'rtsp' in CONFIG['NECKO_PROTOCOLS']:
OS_LIBS += CONFIG['ICONV_LIBS']
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'uikit'):
+ OS_LIBS += CONFIG['TK_LIBS']
+
if CONFIG['MOZ_SNDIO']:
OS_LIBS += [
'sndio',
@@ -276,11 +301,14 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
]
if CONFIG['COMPILE_ENVIRONMENT']:
- full_libname = '%s%s%s' % (
- CONFIG['DLL_PREFIX'],
- LIBRARY_NAME,
- CONFIG['DLL_SUFFIX']
- )
+ if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('cocoa', 'uikit'):
+ full_libname = SHARED_LIBRARY_NAME
+ else:
+ full_libname = '%s%s%s' % (
+ CONFIG['DLL_PREFIX'],
+ LIBRARY_NAME,
+ CONFIG['DLL_SUFFIX']
+ )
GENERATED_FILES += ['dependentlibs.list']
GENERATED_FILES['dependentlibs.list'].script = 'dependentlibs.py:gen_list'
GENERATED_FILES['dependentlibs.list'].inputs = [
diff --git a/toolkit/modules/moz.build b/toolkit/modules/moz.build
index e38b1b0b75..836fd66b36 100644
--- a/toolkit/modules/moz.build
+++ b/toolkit/modules/moz.build
@@ -94,7 +94,7 @@ EXTRA_PP_JS_MODULES += [
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3'):
DEFINES['MENUBAR_CAN_AUTOHIDE'] = 1
-if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3'):
+if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('windows', 'gtk2', 'gtk3', 'cocoa'):
DEFINES['HAVE_SHELL_SERVICE'] = 1
EXTRA_PP_JS_MODULES += [
diff --git a/toolkit/moz.build b/toolkit/moz.build
index 9bf579bb26..9e05b2a4fb 100644
--- a/toolkit/moz.build
+++ b/toolkit/moz.build
@@ -39,5 +39,7 @@ DIRS += ['xre']
if CONFIG['MOZ_WIDGET_TOOLKIT'] in ('gtk2', 'gtk3'):
DIRS += ['system/unixproxy']
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ DIRS += ['system/osxproxy']
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
DIRS += ['system/windowsproxy']
diff --git a/toolkit/moz.configure b/toolkit/moz.configure
index e3d3eca63d..643b4fbe34 100644
--- a/toolkit/moz.configure
+++ b/toolkit/moz.configure
@@ -48,7 +48,7 @@ set_config('L10NBASEDIR', l10n_base)
# reason.
option('--enable-default-toolkit', nargs=1,
choices=('cairo-windows', 'cairo-gtk2', 'cairo-gtk2-x11', 'cairo-gtk3',
- 'cairo-uikit'),
+ 'cairo-cocoa', 'cairo-uikit'),
help='Select default toolkit')
@depends('--enable-default-toolkit', target)
@@ -58,6 +58,8 @@ def toolkit(value, target):
os = target.os
if target.os == 'WINNT':
platform_choices = ('cairo-windows',)
+ elif target.os == 'OSX':
+ platform_choices = ('cairo-cocoa',)
elif target.os == 'iOS':
platform_choices = ('cairo-uikit',)
else:
@@ -180,7 +182,7 @@ option(env='MOZ_INSTRUMENT_EVENT_LOOP',
@depends('MOZ_INSTRUMENT_EVENT_LOOP', toolkit)
def instrument_event_loop(value, toolkit):
- if value or (toolkit in ('windows', 'gtk2', 'gtk3') and value.origin == 'default'):
+ if value or (toolkit in ('windows', 'gtk2', 'gtk3', 'cocoa') and value.origin == 'default'):
return True
set_config('MOZ_INSTRUMENT_EVENT_LOOP', instrument_event_loop)
@@ -266,7 +268,7 @@ add_old_configure_assignment('FT2_CFLAGS',
# ==============================================================
@depends(toolkit)
def applemedia(toolkit):
- if toolkit in ('uikit'):
+ if toolkit in ('cocoa', 'uikit'):
return True
set_config('MOZ_APPLEMEDIA', applemedia)
diff --git a/toolkit/mozapps/update/updater/Makefile.in b/toolkit/mozapps/update/updater/Makefile.in
index c1cfcead7e..84a843d185 100644
--- a/toolkit/mozapps/update/updater/Makefile.in
+++ b/toolkit/mozapps/update/updater/Makefile.in
@@ -14,3 +14,16 @@ endif
endif
include $(topsrcdir)/config/rules.mk
+
+ifeq (cocoa,$(MOZ_WIDGET_TOOLKIT))
+export::
+ sed -e 's/%MOZ_MACBUNDLE_ID%/$(MOZ_MACBUNDLE_ID)/' $(srcdir)/macbuild/Contents/Info.plist.in > $(DIST)/bin/Info.plist
+libs::
+ $(NSINSTALL) -D $(DIST)/bin/updater.app
+ rsync -a -C --exclude '*.in' $(srcdir)/macbuild/Contents $(DIST)/bin/updater.app
+ rsync -a -C $(DIST)/bin/Info.plist $(DIST)/bin/updater.app/Contents
+ sed -e 's/%APP_NAME%/$(MOZ_APP_DISPLAYNAME)/' $(srcdir)/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in | \
+ iconv -f UTF-8 -t UTF-16 > $(DIST)/bin/updater.app/Contents/Resources/English.lproj/InfoPlist.strings
+ $(NSINSTALL) -D $(DIST)/bin/updater.app/Contents/MacOS
+ $(NSINSTALL) $(DIST)/bin/org.mozilla.updater $(DIST)/bin/updater.app/Contents/MacOS
+endif
diff --git a/toolkit/mozapps/update/updater/launchchild_osx.mm b/toolkit/mozapps/update/updater/launchchild_osx.mm
new file mode 100644
index 0000000000..5a36ae6237
--- /dev/null
+++ b/toolkit/mozapps/update/updater/launchchild_osx.mm
@@ -0,0 +1,384 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 <Cocoa/Cocoa.h>
+#include <CoreServices/CoreServices.h>
+#include <crt_externs.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <spawn.h>
+#include <SystemConfiguration/SystemConfiguration.h>
+#include "readstrings.h"
+
+class MacAutoreleasePool {
+public:
+ MacAutoreleasePool()
+ {
+ mPool = [[NSAutoreleasePool alloc] init];
+ }
+ ~MacAutoreleasePool()
+ {
+ [mPool release];
+ }
+
+private:
+ NSAutoreleasePool* mPool;
+};
+
+void LaunchChild(int argc, const char** argv)
+{
+ MacAutoreleasePool pool;
+
+ @try {
+ NSString* launchPath = [NSString stringWithUTF8String:argv[0]];
+ NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:argc - 1];
+ for (int i = 1; i < argc; i++) {
+ [arguments addObject:[NSString stringWithUTF8String:argv[i]]];
+ }
+ [NSTask launchedTaskWithLaunchPath:launchPath
+ arguments:arguments];
+ } @catch (NSException* e) {
+ NSLog(@"%@: %@", e.name, e.reason);
+ }
+}
+
+void
+LaunchMacPostProcess(const char* aAppBundle)
+{
+ MacAutoreleasePool pool;
+
+ // Launch helper to perform post processing for the update; this is the Mac
+ // analogue of LaunchWinPostProcess (PostUpdateWin).
+ NSString* iniPath = [NSString stringWithUTF8String:aAppBundle];
+ iniPath =
+ [iniPath stringByAppendingPathComponent:@"Contents/Resources/updater.ini"];
+
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+ if (![fileManager fileExistsAtPath:iniPath]) {
+ // the file does not exist; there is nothing to run
+ return;
+ }
+
+ int readResult;
+ char values[2][MAX_TEXT_LEN];
+ readResult = ReadStrings([iniPath UTF8String],
+ "ExeRelPath\0ExeArg\0",
+ 2,
+ values,
+ "PostUpdateMac");
+ if (readResult) {
+ return;
+ }
+
+ NSString *exeRelPath = [NSString stringWithUTF8String:values[0]];
+ NSString *exeArg = [NSString stringWithUTF8String:values[1]];
+ if (!exeArg || !exeRelPath) {
+ return;
+ }
+
+ // The path must not traverse directories and it must be a relative path.
+ if ([exeRelPath rangeOfString:@".."].location != NSNotFound ||
+ [exeRelPath rangeOfString:@"./"].location != NSNotFound ||
+ [exeRelPath rangeOfString:@"/"].location == 0) {
+ return;
+ }
+
+ NSString* exeFullPath = [NSString stringWithUTF8String:aAppBundle];
+ exeFullPath = [exeFullPath stringByAppendingPathComponent:exeRelPath];
+
+ char optVals[1][MAX_TEXT_LEN];
+ readResult = ReadStrings([iniPath UTF8String],
+ "ExeAsync\0",
+ 1,
+ optVals,
+ "PostUpdateMac");
+
+ NSTask *task = [[NSTask alloc] init];
+ [task setLaunchPath:exeFullPath];
+ [task setArguments:[NSArray arrayWithObject:exeArg]];
+ [task launch];
+ if (!readResult) {
+ NSString *exeAsync = [NSString stringWithUTF8String:optVals[0]];
+ if ([exeAsync isEqualToString:@"false"]) {
+ [task waitUntilExit];
+ }
+ }
+ // ignore the return value of the task, there's nothing we can do with it
+ [task release];
+}
+
+id ConnectToUpdateServer()
+{
+ MacAutoreleasePool pool;
+
+ id updateServer = nil;
+ BOOL isConnected = NO;
+ int currTry = 0;
+ const int numRetries = 10; // Number of IPC connection retries before
+ // giving up.
+ while (!isConnected && currTry < numRetries) {
+ @try {
+ updateServer = (id)[NSConnection
+ rootProxyForConnectionWithRegisteredName:
+ @"org.mozilla.updater.server"
+ host:nil
+ usingNameServer:[NSSocketPortNameServer sharedInstance]];
+ if (!updateServer ||
+ ![updateServer respondsToSelector:@selector(abort)] ||
+ ![updateServer respondsToSelector:@selector(getArguments)] ||
+ ![updateServer respondsToSelector:@selector(shutdown)]) {
+ NSLog(@"Server doesn't exist or doesn't provide correct selectors.");
+ sleep(1); // Wait 1 second.
+ currTry++;
+ } else {
+ isConnected = YES;
+ }
+ } @catch (NSException* e) {
+ NSLog(@"Encountered exception, retrying: %@: %@", e.name, e.reason);
+ sleep(1); // Wait 1 second.
+ currTry++;
+ }
+ }
+ if (!isConnected) {
+ NSLog(@"Failed to connect to update server after several retries.");
+ return nil;
+ }
+ return updateServer;
+}
+
+void CleanupElevatedMacUpdate(bool aFailureOccurred)
+{
+ MacAutoreleasePool pool;
+
+ id updateServer = ConnectToUpdateServer();
+ if (updateServer) {
+ @try {
+ if (aFailureOccurred) {
+ [updateServer performSelector:@selector(abort)];
+ } else {
+ [updateServer performSelector:@selector(shutdown)];
+ }
+ } @catch (NSException* e) { }
+ }
+
+ NSFileManager* manager = [NSFileManager defaultManager];
+ [manager removeItemAtPath:@"/Library/PrivilegedHelperTools/org.mozilla.updater"
+ error:nil];
+ [manager removeItemAtPath:@"/Library/LaunchDaemons/org.mozilla.updater.plist"
+ error:nil];
+ const char* launchctlArgs[] = {"/bin/launchctl",
+ "remove",
+ "org.mozilla.updater"};
+ // The following call will terminate the current process due to the "remove"
+ // argument in launchctlArgs.
+ LaunchChild(3, launchctlArgs);
+}
+
+// Note: Caller is responsible for freeing argv.
+bool ObtainUpdaterArguments(int* argc, char*** argv)
+{
+ MacAutoreleasePool pool;
+
+ id updateServer = ConnectToUpdateServer();
+ if (!updateServer) {
+ // Let's try our best and clean up.
+ CleanupElevatedMacUpdate(true);
+ return false; // Won't actually get here due to CleanupElevatedMacUpdate.
+ }
+
+ @try {
+ NSArray* updaterArguments =
+ [updateServer performSelector:@selector(getArguments)];
+ *argc = [updaterArguments count];
+ char** tempArgv = (char**)malloc(sizeof(char*) * (*argc));
+ for (int i = 0; i < *argc; i++) {
+ int argLen = [[updaterArguments objectAtIndex:i] length] + 1;
+ tempArgv[i] = (char*)malloc(argLen);
+ strncpy(tempArgv[i], [[updaterArguments objectAtIndex:i] UTF8String],
+ argLen);
+ }
+ *argv = tempArgv;
+ } @catch (NSException* e) {
+ // Let's try our best and clean up.
+ CleanupElevatedMacUpdate(true);
+ return false; // Won't actually get here due to CleanupElevatedMacUpdate.
+ }
+ return true;
+}
+
+/**
+ * The ElevatedUpdateServer is launched from a non-elevated updater process.
+ * It allows an elevated updater process (usually a privileged helper tool) to
+ * connect to it and receive all the necessary arguments to complete a
+ * successful update.
+ */
+@interface ElevatedUpdateServer : NSObject
+{
+ NSArray* mUpdaterArguments;
+ BOOL mShouldKeepRunning;
+ BOOL mAborted;
+}
+- (id)initWithArgs:(NSArray*)args;
+- (BOOL)runServer;
+- (NSArray*)getArguments;
+- (void)abort;
+- (BOOL)wasAborted;
+- (void)shutdown;
+- (BOOL)shouldKeepRunning;
+@end
+
+@implementation ElevatedUpdateServer
+
+- (id)initWithArgs:(NSArray*)args
+{
+ self = [super init];
+ if (!self) {
+ return nil;
+ }
+ mUpdaterArguments = args;
+ mShouldKeepRunning = YES;
+ mAborted = NO;
+ return self;
+}
+
+- (BOOL)runServer
+{
+ NSPort* serverPort = [NSSocketPort port];
+ NSConnection* server = [NSConnection connectionWithReceivePort:serverPort
+ sendPort:serverPort];
+ [server setRootObject:self];
+ if ([server registerName:@"org.mozilla.updater.server"
+ withNameServer:[NSSocketPortNameServer sharedInstance]] == NO) {
+ NSLog(@"Unable to register as DirectoryServer.");
+ NSLog(@"Is another copy running?");
+ return NO;
+ }
+
+ while ([self shouldKeepRunning] &&
+ [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode
+ beforeDate:[NSDate distantFuture]]);
+ return ![self wasAborted];
+}
+
+- (NSArray*)getArguments
+{
+ return mUpdaterArguments;
+}
+
+- (void)abort
+{
+ mAborted = YES;
+ [self shutdown];
+}
+
+- (BOOL)wasAborted
+{
+ return mAborted;
+}
+
+- (void)shutdown
+{
+ mShouldKeepRunning = NO;
+}
+
+- (BOOL)shouldKeepRunning
+{
+ return mShouldKeepRunning;
+}
+
+@end
+
+bool ServeElevatedUpdate(int argc, const char** argv)
+{
+ MacAutoreleasePool pool;
+
+ NSMutableArray* updaterArguments = [NSMutableArray arrayWithCapacity:argc];
+ for (int i = 0; i < argc; i++) {
+ [updaterArguments addObject:[NSString stringWithUTF8String:argv[i]]];
+ }
+
+ ElevatedUpdateServer* updater =
+ [[ElevatedUpdateServer alloc] initWithArgs:[updaterArguments copy]];
+ bool didSucceed = [updater runServer];
+
+ [updater release];
+ return didSucceed;
+}
+
+bool IsOwnedByGroupAdmin(const char* aAppBundle)
+{
+ MacAutoreleasePool pool;
+
+ NSString* appDir = [NSString stringWithUTF8String:aAppBundle];
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+
+ NSDictionary* attributes = [fileManager attributesOfItemAtPath:appDir
+ error:nil];
+ bool isOwnedByAdmin = false;
+ if (attributes &&
+ [[attributes valueForKey:NSFileGroupOwnerAccountID] intValue] == 80) {
+ isOwnedByAdmin = true;
+ }
+ return isOwnedByAdmin;
+}
+
+void SetGroupOwnershipAndPermissions(const char* aAppBundle)
+{
+ MacAutoreleasePool pool;
+
+ NSString* appDir = [NSString stringWithUTF8String:aAppBundle];
+ NSFileManager* fileManager = [NSFileManager defaultManager];
+ NSError* error = nil;
+ NSArray* paths =
+ [fileManager subpathsOfDirectoryAtPath:appDir
+ error:&error];
+ if (error) {
+ return;
+ }
+
+ // Set group ownership of Firefox.app to 80 ("admin") and permissions to
+ // 0775.
+ if (![fileManager setAttributes:@{ NSFileGroupOwnerAccountID: @(80),
+ NSFilePosixPermissions: @(0775) }
+ ofItemAtPath:appDir
+ error:&error] || error) {
+ return;
+ }
+
+ NSArray* permKeys = [NSArray arrayWithObjects:NSFileGroupOwnerAccountID,
+ NSFilePosixPermissions,
+ nil];
+ // For all descendants of Firefox.app, set group ownership to 80 ("admin") and
+ // ensure write permission for the group.
+ for (NSString* currPath in paths) {
+ NSString* child = [appDir stringByAppendingPathComponent:currPath];
+ NSDictionary* oldAttributes =
+ [fileManager attributesOfItemAtPath:child
+ error:&error];
+ if (error) {
+ return;
+ }
+ // Skip symlinks, since they could be pointing to files outside of the .app
+ // bundle.
+ if ([oldAttributes fileType] == NSFileTypeSymbolicLink) {
+ continue;
+ }
+ NSNumber* oldPerms =
+ (NSNumber*)[oldAttributes valueForKey:NSFilePosixPermissions];
+ NSArray* permObjects =
+ [NSArray arrayWithObjects:
+ [NSNumber numberWithUnsignedLong:80],
+ [NSNumber numberWithUnsignedLong:[oldPerms shortValue] | 020],
+ nil];
+ NSDictionary* attributes = [NSDictionary dictionaryWithObjects:permObjects
+ forKeys:permKeys];
+ if (![fileManager setAttributes:attributes
+ ofItemAtPath:child
+ error:&error] || error) {
+ return;
+ }
+ }
+}
diff --git a/toolkit/mozapps/update/updater/macbuild/Contents/Info.plist.in b/toolkit/mozapps/update/updater/macbuild/Contents/Info.plist.in
new file mode 100644
index 0000000000..a9b9fcba9d
--- /dev/null
+++ b/toolkit/mozapps/update/updater/macbuild/Contents/Info.plist.in
@@ -0,0 +1,39 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>CFBundleDevelopmentRegion</key>
+ <string>English</string>
+ <key>CFBundleExecutable</key>
+ <string>org.mozilla.updater</string>
+ <key>CFBundleIconFile</key>
+ <string>updater.icns</string>
+ <key>CFBundleIdentifier</key>
+ <string>org.mozilla.updater</string>
+ <key>CFBundleInfoDictionaryVersion</key>
+ <string>6.0</string>
+ <key>CFBundlePackageType</key>
+ <string>APPL</string>
+ <key>CFBundleSignature</key>
+ <string>????</string>
+ <key>CFBundleVersion</key>
+ <string>1.0</string>
+ <key>NSMainNibFile</key>
+ <string>MainMenu</string>
+ <key>NSPrincipalClass</key>
+ <string>NSApplication</string>
+ <key>LSMinimumSystemVersion</key>
+ <string>10.5</string>
+ <key>LSMinimumSystemVersionByArchitecture</key>
+ <dict>
+ <key>i386</key>
+ <string>10.5.0</string>
+ <key>x86_64</key>
+ <string>10.6.0</string>
+ </dict>
+ <key>SMAuthorizedClients</key>
+ <array>
+ <string>identifier "%MOZ_MACBUNDLE_ID%" and ((anchor apple generic and certificate leaf[field.1.2.840.113635.100.6.1.9]) or (anchor apple generic and certificate 1[field.1.2.840.113635.100.6.2.6] and certificate leaf[field.1.2.840.113635.100.6.1.13] and certificate leaf[subject.OU] = "43AQ936H96"))</string>
+ </array>
+</dict>
+</plist>
diff --git a/toolkit/mozapps/update/updater/macbuild/Contents/PkgInfo b/toolkit/mozapps/update/updater/macbuild/Contents/PkgInfo
new file mode 100644
index 0000000000..bd04210fb4
--- /dev/null
+++ b/toolkit/mozapps/update/updater/macbuild/Contents/PkgInfo
@@ -0,0 +1 @@
+APPL???? \ No newline at end of file
diff --git a/toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in b/toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in
new file mode 100644
index 0000000000..bca4022e75
--- /dev/null
+++ b/toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/InfoPlist.strings.in
@@ -0,0 +1,7 @@
+/* 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/. */
+
+/* Localized versions of Info.plist keys */
+
+CFBundleName = "%APP_NAME% Software Update";
diff --git a/toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/MainMenu.nib/classes.nib b/toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/MainMenu.nib/classes.nib
new file mode 100644
index 0000000000..6cfb50406b
--- /dev/null
+++ b/toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/MainMenu.nib/classes.nib
@@ -0,0 +1,19 @@
+{
+ IBClasses = (
+ {
+ CLASS = FirstResponder;
+ LANGUAGE = ObjC;
+ SUPERCLASS = NSObject;
+},
+ {
+ CLASS = UpdaterUI;
+ LANGUAGE = ObjC;
+ OUTLETS = {
+ progressBar = NSProgressIndicator;
+ progressTextField = NSTextField;
+ };
+ SUPERCLASS = NSObject;
+}
+ );
+ IBVersion = 1;
+} \ No newline at end of file
diff --git a/toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/MainMenu.nib/info.nib b/toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/MainMenu.nib/info.nib
new file mode 100644
index 0000000000..1509178370
--- /dev/null
+++ b/toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/MainMenu.nib/info.nib
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+ <key>IBDocumentLocation</key>
+ <string>111 162 356 240 0 0 1440 878 </string>
+ <key>IBEditorPositions</key>
+ <dict>
+ <key>29</key>
+ <string>106 299 84 44 0 0 1440 878 </string>
+ </dict>
+ <key>IBFramework Version</key>
+ <string>489.0</string>
+ <key>IBOpenObjects</key>
+ <array>
+ <integer>21</integer>
+ <integer>29</integer>
+ </array>
+ <key>IBSystem Version</key>
+ <string>10J567</string>
+</dict>
+</plist>
diff --git a/toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nib b/toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nib
new file mode 100644
index 0000000000..61ff026009
--- /dev/null
+++ b/toolkit/mozapps/update/updater/macbuild/Contents/Resources/English.lproj/MainMenu.nib/keyedobjects.nib
Binary files differ
diff --git a/toolkit/mozapps/update/updater/macbuild/Contents/Resources/updater.icns b/toolkit/mozapps/update/updater/macbuild/Contents/Resources/updater.icns
new file mode 100644
index 0000000000..d7499c6692
--- /dev/null
+++ b/toolkit/mozapps/update/updater/macbuild/Contents/Resources/updater.icns
Binary files differ
diff --git a/toolkit/mozapps/update/updater/moz.build b/toolkit/mozapps/update/updater/moz.build
index 6cf377afef..4dc557ea34 100644
--- a/toolkit/mozapps/update/updater/moz.build
+++ b/toolkit/mozapps/update/updater/moz.build
@@ -3,13 +3,26 @@
# 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/.
-Program('updater')
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ Program('org.mozilla.updater')
+else:
+ Program('updater')
updater_rel_path = ''
include('updater-common.build')
CXXFLAGS += CONFIG['MOZ_BZ2_CFLAGS']
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LDFLAGS += ['-sectcreate',
+ '__TEXT',
+ '__info_plist',
+ TOPOBJDIR + '/dist/bin/Info.plist',
+ '-sectcreate',
+ '__TEXT',
+ '__launchd_plist',
+ SRCDIR + '/Launchd.plist']
+
GENERATED_FILES = [
'primaryCert.h',
'secondaryCert.h',
diff --git a/toolkit/mozapps/update/updater/progressui_osx.mm b/toolkit/mozapps/update/updater/progressui_osx.mm
new file mode 100644
index 0000000000..54c9c41b72
--- /dev/null
+++ b/toolkit/mozapps/update/updater/progressui_osx.mm
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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 <stdio.h>
+#include <unistd.h>
+#include "mozilla/Sprintf.h"
+#include "progressui.h"
+#include "readstrings.h"
+#include "errors.h"
+
+#define TIMER_INTERVAL 0.2
+
+static float sProgressVal; // between 0 and 100
+static BOOL sQuit = NO;
+static BOOL sIndeterminate = NO;
+static StringTable sLabels;
+static const char *sUpdatePath;
+
+@interface UpdaterUI : NSObject
+{
+ IBOutlet NSProgressIndicator *progressBar;
+ IBOutlet NSTextField *progressTextField;
+}
+@end
+
+@implementation UpdaterUI
+
+-(void)awakeFromNib
+{
+ NSWindow *w = [progressBar window];
+
+ [w setTitle:[NSString stringWithUTF8String:sLabels.title]];
+ [progressTextField setStringValue:[NSString stringWithUTF8String:sLabels.info]];
+
+ NSRect origTextFrame = [progressTextField frame];
+ [progressTextField sizeToFit];
+
+ int widthAdjust = progressTextField.frame.size.width - origTextFrame.size.width;
+
+ if (widthAdjust > 0) {
+ NSRect f;
+ f.size.width = w.frame.size.width + widthAdjust;
+ f.size.height = w.frame.size.height;
+ [w setFrame:f display:YES];
+ }
+
+ [w center];
+
+ [progressBar setIndeterminate:sIndeterminate];
+ [progressBar setDoubleValue:0.0];
+
+ [[NSTimer scheduledTimerWithTimeInterval:TIMER_INTERVAL target:self
+ selector:@selector(updateProgressUI:)
+ userInfo:nil repeats:YES] retain];
+
+ // Make sure we are on top initially
+ [NSApp activateIgnoringOtherApps:YES];
+}
+
+// called when the timer goes off
+-(void)updateProgressUI:(NSTimer *)aTimer
+{
+ if (sQuit) {
+ [aTimer invalidate];
+ [aTimer release];
+
+ // It seems to be necessary to activate and hide ourselves before we stop,
+ // otherwise the "run" method will not return until the user focuses some
+ // other app. The activate step is necessary if we are not the active app.
+ // This is a big hack, but it seems to do the trick.
+ [NSApp activateIgnoringOtherApps:YES];
+ [NSApp hide:self];
+ [NSApp stop:self];
+ }
+
+ float progress = sProgressVal;
+
+ [progressBar setDoubleValue:(double)progress];
+}
+
+// leave this as returning a BOOL instead of NSApplicationTerminateReply
+// for backward compatibility
+- (BOOL)applicationShouldTerminate:(NSApplication *)sender
+{
+ return sQuit;
+}
+
+@end
+
+int
+InitProgressUI(int *pargc, char ***pargv)
+{
+ sUpdatePath = (*pargv)[1];
+
+ return 0;
+}
+
+int
+ShowProgressUI(bool indeterminate)
+{
+ // Only show the Progress UI if the process is taking a significant amount of
+ // time where a significant amount of time is defined as .5 seconds after
+ // ShowProgressUI is called sProgress is less than 70.
+ usleep(500000);
+
+ if (sQuit || sProgressVal > 70.0f)
+ return 0;
+
+ char path[PATH_MAX];
+ SprintfLiteral(path, "%s/updater.ini", sUpdatePath);
+ if (ReadStrings(path, &sLabels) != OK)
+ return -1;
+
+ // Continue the update without showing the Progress UI if any of the supplied
+ // strings are larger than MAX_TEXT_LEN (Bug 628829).
+ if (!(strlen(sLabels.title) < MAX_TEXT_LEN - 1 &&
+ strlen(sLabels.info) < MAX_TEXT_LEN - 1))
+ return -1;
+
+ sIndeterminate = indeterminate;
+ [NSApplication sharedApplication];
+ [NSBundle loadNibNamed:@"MainMenu" owner:NSApp];
+ [NSApp run];
+
+ return 0;
+}
+
+// Called on a background thread
+void
+QuitProgressUI()
+{
+ sQuit = YES;
+}
+
+// Called on a background thread
+void
+UpdateProgressUI(float progress)
+{
+ sProgressVal = progress; // 32-bit writes are atomic
+}
diff --git a/toolkit/mozapps/update/updater/updater-common.build b/toolkit/mozapps/update/updater/updater-common.build
index 1ace8fcc71..ce4a219001 100644
--- a/toolkit/mozapps/update/updater/updater-common.build
+++ b/toolkit/mozapps/update/updater/updater-common.build
@@ -73,6 +73,24 @@ if 'gtk' in CONFIG['MOZ_WIDGET_TOOLKIT']:
'progressui_gtk.cpp',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ have_progressui = 1
+ srcs += [
+ 'launchchild_osx.mm',
+ 'progressui_osx.mm',
+ ]
+ OS_LIBS += [
+ '-framework Cocoa',
+ '-framework Security',
+ '-framework SystemConfiguration',
+ ]
+ UNIFIED_SOURCES += [
+ '/toolkit/xre/updaterfileutils_osx.mm',
+ ]
+ LOCAL_INCLUDES += [
+ '/toolkit/xre',
+ ]
+
if have_progressui == 0:
srcs += [
'progressui_null.cpp',
diff --git a/toolkit/system/osxproxy/ProxyUtils.h b/toolkit/system/osxproxy/ProxyUtils.h
new file mode 100644
index 0000000000..d6e5ddbd45
--- /dev/null
+++ b/toolkit/system/osxproxy/ProxyUtils.h
@@ -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/. */
+
+#ifndef mozilla_toolkit_system_osxproxy_ProxyUtils_h
+#define mozilla_toolkit_system_osxproxy_ProxyUtils_h
+
+#include "nsStringGlue.h"
+
+namespace mozilla {
+namespace toolkit {
+namespace system {
+
+bool IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride);
+
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
+
+#endif // mozilla_toolkit_system_osxproxy_ProxyUtils_h
diff --git a/toolkit/system/osxproxy/ProxyUtils.mm b/toolkit/system/osxproxy/ProxyUtils.mm
new file mode 100644
index 0000000000..4e59f226a0
--- /dev/null
+++ b/toolkit/system/osxproxy/ProxyUtils.mm
@@ -0,0 +1,182 @@
+/* -*- 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 "ProxyUtils.h"
+#include "nsTArray.h"
+#include "prnetdb.h"
+#include "prtypes.h"
+
+namespace mozilla {
+namespace toolkit {
+namespace system {
+
+/**
+ * Normalize the short IP form into the complete form.
+ * For example, it converts "192.168" into "192.168.0.0"
+ */
+static bool
+NormalizeAddr(const nsACString& aAddr, nsCString& aNormalized)
+{
+ nsTArray<nsCString> addr;
+ if (!ParseString(aAddr, '.', addr)) {
+ return false;
+ }
+ aNormalized = "";
+ for (uint32_t i = 0; i < 4; ++i) {
+ if (i != 0) {
+ aNormalized.Append(".");
+ }
+ if (i < addr.Length()) {
+ aNormalized.Append(addr[i]);
+ } else {
+ aNormalized.Append("0");
+ }
+ }
+ return true;
+}
+
+static PRUint32
+MaskIPv4Addr(PRUint32 aAddr, uint16_t aMaskLen)
+{
+ if (aMaskLen == 32) {
+ return aAddr;
+ }
+ return PR_htonl(PR_ntohl(aAddr) & (~0L << (32 - aMaskLen)));
+}
+
+static void
+MaskIPv6Addr(PRIPv6Addr& aAddr, uint16_t aMaskLen)
+{
+ if (aMaskLen == 128) {
+ return;
+ }
+
+ if (aMaskLen > 96) {
+ aAddr.pr_s6_addr32[3] = PR_htonl(
+ PR_ntohl(aAddr.pr_s6_addr32[3]) & (~0L << (128 - aMaskLen)));
+ } else if (aMaskLen > 64) {
+ aAddr.pr_s6_addr32[3] = 0;
+ aAddr.pr_s6_addr32[2] = PR_htonl(
+ PR_ntohl(aAddr.pr_s6_addr32[2]) & (~0L << (96 - aMaskLen)));
+ } else if (aMaskLen > 32) {
+ aAddr.pr_s6_addr32[3] = 0;
+ aAddr.pr_s6_addr32[2] = 0;
+ aAddr.pr_s6_addr32[1] = PR_htonl(
+ PR_ntohl(aAddr.pr_s6_addr32[1]) & (~0L << (64 - aMaskLen)));
+ } else {
+ aAddr.pr_s6_addr32[3] = 0;
+ aAddr.pr_s6_addr32[2] = 0;
+ aAddr.pr_s6_addr32[1] = 0;
+ aAddr.pr_s6_addr32[0] = PR_htonl(
+ PR_ntohl(aAddr.pr_s6_addr32[0]) & (~0L << (32 - aMaskLen)));
+ }
+
+ return;
+}
+
+static bool
+IsMatchMask(const nsACString& aHost, const nsACString& aOverride)
+{
+ nsresult rv;
+
+ auto tokenEnd = aOverride.FindChar('/');
+ if (tokenEnd == -1) {
+ return false;
+ }
+
+ nsAutoCString prefixStr(Substring(aOverride,
+ tokenEnd + 1,
+ aOverride.Length() - tokenEnd - 1));
+ auto maskLen = prefixStr.ToInteger(&rv);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return false;
+ }
+
+ nsAutoCString override(aOverride);
+ if (!NormalizeAddr(Substring(aOverride, 0, tokenEnd), override)) {
+ return false;
+ }
+
+ PRNetAddr prAddrHost;
+ PRNetAddr prAddrOverride;
+ if (PR_SUCCESS != PR_StringToNetAddr(PromiseFlatCString(aHost).get(),
+ &prAddrHost) ||
+ PR_SUCCESS != PR_StringToNetAddr(override.get(),
+ &prAddrOverride)) {
+ return false;
+ }
+
+ if (prAddrHost.raw.family == PR_AF_INET &&
+ prAddrOverride.raw.family == PR_AF_INET) {
+ return MaskIPv4Addr(prAddrHost.inet.ip, maskLen) ==
+ MaskIPv4Addr(prAddrOverride.inet.ip, maskLen);
+ }
+ else if (prAddrHost.raw.family == PR_AF_INET6 &&
+ prAddrOverride.raw.family == PR_AF_INET6) {
+ MaskIPv6Addr(prAddrHost.ipv6.ip, maskLen);
+ MaskIPv6Addr(prAddrOverride.ipv6.ip, maskLen);
+
+ return memcmp(&prAddrHost.ipv6.ip,
+ &prAddrOverride.ipv6.ip,
+ sizeof(PRIPv6Addr)) == 0;
+ }
+
+ return false;
+}
+
+static bool
+IsMatchWildcard(const nsACString& aHost, const nsACString& aOverride)
+{
+ nsAutoCString host(aHost);
+ nsAutoCString override(aOverride);
+
+ int32_t overrideLength = override.Length();
+ int32_t tokenStart = 0;
+ int32_t offset = 0;
+ bool star = false;
+
+ while (tokenStart < overrideLength) {
+ int32_t tokenEnd = override.FindChar('*', tokenStart);
+ if (tokenEnd == tokenStart) {
+ // Star is the first character in the token.
+ star = true;
+ tokenStart++;
+ // If the character following the '*' is a '.' character then skip
+ // it so that "*.foo.com" allows "foo.com".
+ if (override.FindChar('.', tokenStart) == tokenStart) {
+ nsAutoCString token(Substring(override,
+ tokenStart + 1,
+ overrideLength - tokenStart - 1));
+ if (host.Equals(token)) {
+ return true;
+ }
+ }
+ } else {
+ if (tokenEnd == -1) {
+ tokenEnd = overrideLength; // no '*' char, match rest of string
+ }
+ nsAutoCString token(Substring(override, tokenStart, tokenEnd - tokenStart));
+ offset = host.Find(token, offset);
+ if (offset == -1 || (!star && offset)) {
+ return false;
+ }
+ star = false;
+ tokenStart = tokenEnd;
+ offset += token.Length();
+ }
+ }
+
+ return (star || (offset == static_cast<int32_t>(host.Length())));
+}
+
+bool
+IsHostProxyEntry(const nsACString& aHost, const nsACString& aOverride)
+{
+ return IsMatchMask(aHost, aOverride) || IsMatchWildcard(aHost, aOverride);
+}
+
+} // namespace system
+} // namespace toolkit
+} // namespace mozilla
diff --git a/toolkit/system/osxproxy/moz.build b/toolkit/system/osxproxy/moz.build
new file mode 100644
index 0000000000..64a01ce6b8
--- /dev/null
+++ b/toolkit/system/osxproxy/moz.build
@@ -0,0 +1,13 @@
+# -*- 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/.
+
+TEST_DIRS += ['tests/gtest']
+
+SOURCES += [
+ 'nsOSXSystemProxySettings.mm',
+ 'ProxyUtils.mm',
+]
+
+FINAL_LIBRARY = 'xul'
diff --git a/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm b/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm
new file mode 100644
index 0000000000..77fd2e482c
--- /dev/null
+++ b/toolkit/system/osxproxy/nsOSXSystemProxySettings.mm
@@ -0,0 +1,326 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et: */
+/* 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 <SystemConfiguration/SystemConfiguration.h>
+
+#include "nsISystemProxySettings.h"
+#include "mozilla/ModuleUtils.h"
+#include "nsIServiceManager.h"
+#include "nsPrintfCString.h"
+#include "nsNetCID.h"
+#include "nsISupportsPrimitives.h"
+#include "nsIURI.h"
+#include "nsObjCExceptions.h"
+#include "mozilla/Attributes.h"
+#include "ProxyUtils.h"
+
+class nsOSXSystemProxySettings final : public nsISystemProxySettings {
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSISYSTEMPROXYSETTINGS
+
+ nsOSXSystemProxySettings();
+ nsresult Init();
+
+ // called by OSX when the proxy settings have changed
+ void ProxyHasChanged();
+
+ // is there a PAC url specified in the system configuration
+ bool IsAutoconfigEnabled() const;
+ // retrieve the pac url
+ nsresult GetAutoconfigURL(nsAutoCString& aResult) const;
+
+ // Find the SystemConfiguration proxy & port for a given URI
+ nsresult FindSCProxyPort(const nsACString &aScheme, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy);
+
+ // is host:port on the proxy exception list?
+ bool IsInExceptionList(const nsACString& aHost) const;
+
+private:
+ ~nsOSXSystemProxySettings();
+
+ SCDynamicStoreContext mContext;
+ SCDynamicStoreRef mSystemDynamicStore;
+ NSDictionary* mProxyDict;
+
+
+ // Mapping of URI schemes to SystemConfiguration keys
+ struct SchemeMapping {
+ const char* mScheme;
+ CFStringRef mEnabled;
+ CFStringRef mHost;
+ CFStringRef mPort;
+ bool mIsSocksProxy;
+ };
+ static const SchemeMapping gSchemeMappingList[];
+};
+
+NS_IMPL_ISUPPORTS(nsOSXSystemProxySettings, nsISystemProxySettings)
+
+NS_IMETHODIMP
+nsOSXSystemProxySettings::GetMainThreadOnly(bool *aMainThreadOnly)
+{
+ *aMainThreadOnly = true;
+ return NS_OK;
+}
+
+// Mapping of URI schemes to SystemConfiguration keys
+const nsOSXSystemProxySettings::SchemeMapping nsOSXSystemProxySettings::gSchemeMappingList[] = {
+ {"http", kSCPropNetProxiesHTTPEnable, kSCPropNetProxiesHTTPProxy, kSCPropNetProxiesHTTPPort, false},
+ {"https", kSCPropNetProxiesHTTPSEnable, kSCPropNetProxiesHTTPSProxy, kSCPropNetProxiesHTTPSPort, false},
+ {"ftp", kSCPropNetProxiesFTPEnable, kSCPropNetProxiesFTPProxy, kSCPropNetProxiesFTPPort, false},
+ {"socks", kSCPropNetProxiesSOCKSEnable, kSCPropNetProxiesSOCKSProxy, kSCPropNetProxiesSOCKSPort, true},
+ {NULL, NULL, NULL, NULL, false},
+};
+
+static void
+ProxyHasChangedWrapper(SCDynamicStoreRef aStore, CFArrayRef aChangedKeys, void* aInfo)
+{
+ static_cast<nsOSXSystemProxySettings*>(aInfo)->ProxyHasChanged();
+}
+
+
+nsOSXSystemProxySettings::nsOSXSystemProxySettings()
+ : mSystemDynamicStore(NULL), mProxyDict(NULL)
+{
+ mContext = (SCDynamicStoreContext){0, this, NULL, NULL, NULL};
+}
+
+nsresult
+nsOSXSystemProxySettings::Init()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ // Register for notification of proxy setting changes
+ // See: http://developer.apple.com/documentation/Networking/Conceptual/CFNetwork/CFStreamTasks/chapter_4_section_5.html
+ mSystemDynamicStore = SCDynamicStoreCreate(NULL, CFSTR("Mozilla"), ProxyHasChangedWrapper, &mContext);
+ if (!mSystemDynamicStore)
+ return NS_ERROR_FAILURE;
+
+ // Set up the store to monitor any changes to the proxies
+ CFStringRef proxiesKey = SCDynamicStoreKeyCreateProxies(NULL);
+ if (!proxiesKey)
+ return NS_ERROR_FAILURE;
+
+ CFArrayRef keyArray = CFArrayCreate(NULL, (const void**)(&proxiesKey), 1, &kCFTypeArrayCallBacks);
+ CFRelease(proxiesKey);
+ if (!keyArray)
+ return NS_ERROR_FAILURE;
+
+ SCDynamicStoreSetNotificationKeys(mSystemDynamicStore, keyArray, NULL);
+ CFRelease(keyArray);
+
+ // Add the dynamic store to the run loop
+ CFRunLoopSourceRef storeRLSource = SCDynamicStoreCreateRunLoopSource(NULL, mSystemDynamicStore, 0);
+ if (!storeRLSource)
+ return NS_ERROR_FAILURE;
+ CFRunLoopAddSource(CFRunLoopGetCurrent(), storeRLSource, kCFRunLoopCommonModes);
+ CFRelease(storeRLSource);
+
+ // Load the initial copy of proxy info
+ mProxyDict = (NSDictionary*)SCDynamicStoreCopyProxies(mSystemDynamicStore);
+ if (!mProxyDict)
+ return NS_ERROR_FAILURE;
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsOSXSystemProxySettings::~nsOSXSystemProxySettings()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [mProxyDict release];
+
+ if (mSystemDynamicStore) {
+ // Invalidate the dynamic store's run loop source
+ // to get the store out of the run loop
+ CFRunLoopSourceRef rls = SCDynamicStoreCreateRunLoopSource(NULL, mSystemDynamicStore, 0);
+ if (rls) {
+ CFRunLoopSourceInvalidate(rls);
+ CFRelease(rls);
+ }
+ CFRelease(mSystemDynamicStore);
+ }
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+
+void
+nsOSXSystemProxySettings::ProxyHasChanged()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [mProxyDict release];
+ mProxyDict = (NSDictionary*)SCDynamicStoreCopyProxies(mSystemDynamicStore);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+nsresult
+nsOSXSystemProxySettings::FindSCProxyPort(const nsACString &aScheme, nsACString& aResultHost, int32_t& aResultPort, bool& aResultSocksProxy)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NS_ENSURE_TRUE(mProxyDict != NULL, NS_ERROR_FAILURE);
+
+ for (const SchemeMapping* keys = gSchemeMappingList; keys->mScheme != NULL; ++keys) {
+ // Check for matching scheme (when appropriate)
+ if (strcasecmp(keys->mScheme, PromiseFlatCString(aScheme).get()) &&
+ !keys->mIsSocksProxy)
+ continue;
+
+ // Check the proxy is enabled
+ NSNumber* enabled = [mProxyDict objectForKey:(NSString*)keys->mEnabled];
+ NS_ENSURE_TRUE(enabled == NULL || [enabled isKindOfClass:[NSNumber class]], NS_ERROR_FAILURE);
+ if ([enabled intValue] == 0)
+ continue;
+
+ // Get the proxy host
+ NSString* host = [mProxyDict objectForKey:(NSString*)keys->mHost];
+ if (host == NULL)
+ break;
+ NS_ENSURE_TRUE([host isKindOfClass:[NSString class]], NS_ERROR_FAILURE);
+ aResultHost.Assign([host UTF8String]);
+
+ // Get the proxy port
+ NSNumber* port = [mProxyDict objectForKey:(NSString*)keys->mPort];
+ NS_ENSURE_TRUE([port isKindOfClass:[NSNumber class]], NS_ERROR_FAILURE);
+ aResultPort = [port intValue];
+
+ aResultSocksProxy = keys->mIsSocksProxy;
+
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+bool
+nsOSXSystemProxySettings::IsAutoconfigEnabled() const
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ NSNumber* value = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigEnable];
+ NS_ENSURE_TRUE(value == NULL || [value isKindOfClass:[NSNumber class]], false);
+ return ([value intValue] != 0);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
+}
+
+nsresult
+nsOSXSystemProxySettings::GetAutoconfigURL(nsAutoCString& aResult) const
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NSString* value = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesProxyAutoConfigURLString];
+ if (value != NULL) {
+ NS_ENSURE_TRUE([value isKindOfClass:[NSString class]], NS_ERROR_FAILURE);
+ aResult.Assign([value UTF8String]);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+bool
+nsOSXSystemProxySettings::IsInExceptionList(const nsACString& aHost) const
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ NS_ENSURE_TRUE(mProxyDict != NULL, false);
+
+ NSArray* exceptionList = [mProxyDict objectForKey:(NSString*)kSCPropNetProxiesExceptionsList];
+ NS_ENSURE_TRUE(exceptionList == NULL || [exceptionList isKindOfClass:[NSArray class]], false);
+
+ NSEnumerator* exceptionEnumerator = [exceptionList objectEnumerator];
+ NSString* currentValue = NULL;
+ while ((currentValue = [exceptionEnumerator nextObject])) {
+ NS_ENSURE_TRUE([currentValue isKindOfClass:[NSString class]], false);
+ nsAutoCString overrideStr([currentValue UTF8String]);
+ if (mozilla::toolkit::system::IsHostProxyEntry(aHost, overrideStr))
+ return true;
+ }
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false);
+}
+
+nsresult
+nsOSXSystemProxySettings::GetPACURI(nsACString& aResult)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NS_ENSURE_TRUE(mProxyDict != NULL, NS_ERROR_FAILURE);
+
+ nsAutoCString pacUrl;
+ if (IsAutoconfigEnabled() && NS_SUCCEEDED(GetAutoconfigURL(pacUrl))) {
+ aResult.Assign(pacUrl);
+ return NS_OK;
+ }
+
+ return NS_ERROR_FAILURE;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult
+nsOSXSystemProxySettings::GetProxyForURI(const nsACString & aSpec,
+ const nsACString & aScheme,
+ const nsACString & aHost,
+ const int32_t aPort,
+ nsACString & aResult)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ int32_t proxyPort;
+ nsAutoCString proxyHost;
+ bool proxySocks;
+ nsresult rv = FindSCProxyPort(aScheme, proxyHost, proxyPort, proxySocks);
+
+ if (NS_FAILED(rv) || IsInExceptionList(aHost)) {
+ aResult.AssignLiteral("DIRECT");
+ } else if (proxySocks) {
+ aResult.Assign(NS_LITERAL_CSTRING("SOCKS ") + proxyHost + nsPrintfCString(":%d", proxyPort));
+ } else {
+ aResult.Assign(NS_LITERAL_CSTRING("PROXY ") + proxyHost + nsPrintfCString(":%d", proxyPort));
+ }
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+#define NS_OSXSYSTEMPROXYSERVICE_CID /* 9afcd4b8-2e0f-41f4-8f1f-3bf0d3cf67de */\
+ { 0x9afcd4b8, 0x2e0f, 0x41f4, \
+ { 0x8f, 0x1f, 0x3b, 0xf0, 0xd3, 0xcf, 0x67, 0xde } }
+
+NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(nsOSXSystemProxySettings, Init);
+NS_DEFINE_NAMED_CID(NS_OSXSYSTEMPROXYSERVICE_CID);
+
+static const mozilla::Module::CIDEntry kOSXSysProxyCIDs[] = {
+ { &kNS_OSXSYSTEMPROXYSERVICE_CID, false, NULL, nsOSXSystemProxySettingsConstructor },
+ { NULL }
+};
+
+static const mozilla::Module::ContractIDEntry kOSXSysProxyContracts[] = {
+ { NS_SYSTEMPROXYSETTINGS_CONTRACTID, &kNS_OSXSYSTEMPROXYSERVICE_CID },
+ { NULL }
+};
+
+static const mozilla::Module kOSXSysProxyModule = {
+ mozilla::Module::kVersion,
+ kOSXSysProxyCIDs,
+ kOSXSysProxyContracts
+};
+
+NSMODULE_DEFN(nsOSXProxyModule) = &kOSXSysProxyModule;
diff --git a/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp b/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp
new file mode 100644
index 0000000000..7903491090
--- /dev/null
+++ b/toolkit/system/osxproxy/tests/gtest/TestProxyBypassRules.cpp
@@ -0,0 +1,41 @@
+/* -*- Mode: C++; 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/. */
+
+#include "gtest/gtest.h"
+#include "ProxyUtils.h"
+
+using namespace mozilla::toolkit::system;
+
+TEST(OSXProxy, TestProxyBypassRules)
+{
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"),NS_LITERAL_CSTRING("*mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("notmozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.org"), NS_LITERAL_CSTRING("*.mozilla.org")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("www.mozilla.com"), NS_LITERAL_CSTRING("*.mozilla.*")));
+}
+
+TEST(OSXProxy, TestProxyBypassRulesIPv4)
+{
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.1.*")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.2.*")));
+
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("10.1.2.3"), NS_LITERAL_CSTRING("10.0.0.0/8")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168/16")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168/17")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.192.1"), NS_LITERAL_CSTRING("192.168.128/17")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("192.168.1.1"), NS_LITERAL_CSTRING("192.168.1.1/32")));
+}
+
+TEST(OSXProxy, TestProxyBypassRulesIPv6)
+{
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/64")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0000:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/80")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/80")));
+ EXPECT_TRUE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0000:0000:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/96")));
+ EXPECT_FALSE(IsHostProxyEntry(NS_LITERAL_CSTRING("2001:0DB8:ABCD:0012:0123:4567:89AB:CDEF"), NS_LITERAL_CSTRING("2001:db8:abcd:0012::0/96")));
+}
diff --git a/toolkit/system/osxproxy/tests/gtest/moz.build b/toolkit/system/osxproxy/tests/gtest/moz.build
new file mode 100644
index 0000000000..94768a204e
--- /dev/null
+++ b/toolkit/system/osxproxy/tests/gtest/moz.build
@@ -0,0 +1,17 @@
+# -*- 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/.
+
+UNIFIED_SOURCES += [
+ 'TestProxyBypassRules.cpp',
+]
+
+LOCAL_INCLUDES += [
+ '/toolkit/system/osxproxy',
+]
+
+FINAL_LIBRARY = 'xul-gtest'
+
+if CONFIG['GNU_CXX']:
+ CXXFLAGS += ['-Wshadow']
diff --git a/toolkit/themes/moz.build b/toolkit/themes/moz.build
index b24036f1a0..74f53391e2 100644
--- a/toolkit/themes/moz.build
+++ b/toolkit/themes/moz.build
@@ -21,7 +21,9 @@ if CONFIG['MOZ_PHOENIX']:
else:
app = CONFIG['MOZ_BUILD_APP']
-if toolkit in ('gtk2', 'gtk3'):
+if toolkit == 'cocoa':
+ DIRS += ['osx']
+elif toolkit in ('gtk2', 'gtk3'):
DIRS += ['linux']
else:
DIRS += ['windows']
diff --git a/toolkit/themes/osx/global/10pct_transparent_grey.png b/toolkit/themes/osx/global/10pct_transparent_grey.png
new file mode 100644
index 0000000000..01f2edd9f4
--- /dev/null
+++ b/toolkit/themes/osx/global/10pct_transparent_grey.png
Binary files differ
diff --git a/toolkit/themes/osx/global/50pct_transparent_grey.png b/toolkit/themes/osx/global/50pct_transparent_grey.png
new file mode 100644
index 0000000000..0e462a46fa
--- /dev/null
+++ b/toolkit/themes/osx/global/50pct_transparent_grey.png
Binary files differ
diff --git a/toolkit/themes/osx/global/alerts/alert.css b/toolkit/themes/osx/global/alerts/alert.css
new file mode 100644
index 0000000000..3ca1a6e066
--- /dev/null
+++ b/toolkit/themes/osx/global/alerts/alert.css
@@ -0,0 +1,30 @@
+/* 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/. */
+
+/* ===== alert.css =====================================================
+ == Styles specific to the alerts dialog.
+ ======================================================================= */
+
+@import url("chrome://global/skin/alerts/alert-common.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#alertNotification {
+ -moz-appearance: none;
+ background: transparent;
+}
+
+#alertBox {
+ border: 1px solid ThreeDShadow;
+ border-radius: 1px;
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+.alertCloseButton {
+ -moz-appearance: none;
+ padding: 0;
+ margin: 2px;
+ border: none;
+}
diff --git a/toolkit/themes/osx/global/arrow.css b/toolkit/themes/osx/global/arrow.css
new file mode 100644
index 0000000000..f8d14becab
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow.css
@@ -0,0 +1,38 @@
+/* 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/. */
+
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.up {
+ min-width: 0px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+.up[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up-dis.gif");
+}
+
+.down {
+ min-width: 0px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+}
+.down[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.gif");
+}
+
+.left {
+ min-width: 0px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft.gif");
+}
+.left[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft-dis.gif");
+}
+
+.right {
+ min-width: 0px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit.gif");
+}
+.right[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit-dis.gif");
+}
diff --git a/toolkit/themes/osx/global/arrow/arrow-dn-dis.gif b/toolkit/themes/osx/global/arrow/arrow-dn-dis.gif
new file mode 100644
index 0000000000..3d62e40063
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-dn-dis.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-dn-dis.png b/toolkit/themes/osx/global/arrow/arrow-dn-dis.png
new file mode 100644
index 0000000000..a202fd85c9
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-dn-dis.png
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-dn-sharp.gif b/toolkit/themes/osx/global/arrow/arrow-dn-sharp.gif
new file mode 100644
index 0000000000..206d7c19dd
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-dn-sharp.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-dn.gif b/toolkit/themes/osx/global/arrow/arrow-dn.gif
new file mode 100644
index 0000000000..33849a6391
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-dn.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-dn.png b/toolkit/themes/osx/global/arrow/arrow-dn.png
new file mode 100644
index 0000000000..91486a3e9a
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-dn.png
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-lft-dis.gif b/toolkit/themes/osx/global/arrow/arrow-lft-dis.gif
new file mode 100644
index 0000000000..33243517b1
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-lft-dis.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-lft-hov.gif b/toolkit/themes/osx/global/arrow/arrow-lft-hov.gif
new file mode 100644
index 0000000000..3367bde312
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-lft-hov.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-lft-sharp-end.gif b/toolkit/themes/osx/global/arrow/arrow-lft-sharp-end.gif
new file mode 100644
index 0000000000..c22294ba21
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-lft-sharp-end.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-lft-sharp.gif b/toolkit/themes/osx/global/arrow/arrow-lft-sharp.gif
new file mode 100644
index 0000000000..ae9b1dd0fb
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-lft-sharp.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-lft.gif b/toolkit/themes/osx/global/arrow/arrow-lft.gif
new file mode 100644
index 0000000000..c5c362d89b
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-lft.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-rit-dis.gif b/toolkit/themes/osx/global/arrow/arrow-rit-dis.gif
new file mode 100644
index 0000000000..cda95fe215
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-rit-dis.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-rit-hov.gif b/toolkit/themes/osx/global/arrow/arrow-rit-hov.gif
new file mode 100644
index 0000000000..5010921adc
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-rit-hov.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-rit-sharp-end.gif b/toolkit/themes/osx/global/arrow/arrow-rit-sharp-end.gif
new file mode 100644
index 0000000000..c1b3750d4c
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-rit-sharp-end.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-rit-sharp.gif b/toolkit/themes/osx/global/arrow/arrow-rit-sharp.gif
new file mode 100644
index 0000000000..ca628ba69b
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-rit-sharp.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-rit.gif b/toolkit/themes/osx/global/arrow/arrow-rit.gif
new file mode 100644
index 0000000000..dce39aecc1
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-rit.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-up-dis.gif b/toolkit/themes/osx/global/arrow/arrow-up-dis.gif
new file mode 100644
index 0000000000..381dee3e5d
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-up-dis.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-up-sharp.gif b/toolkit/themes/osx/global/arrow/arrow-up-sharp.gif
new file mode 100644
index 0000000000..883a4f95ca
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-up-sharp.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/arrow-up.gif b/toolkit/themes/osx/global/arrow/arrow-up.gif
new file mode 100644
index 0000000000..b8e09b21b8
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/arrow-up.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/panelarrow-horizontal.png b/toolkit/themes/osx/global/arrow/panelarrow-horizontal.png
new file mode 100644
index 0000000000..c110f8592d
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/panelarrow-horizontal.png
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/panelarrow-horizontal@2x.png b/toolkit/themes/osx/global/arrow/panelarrow-horizontal@2x.png
new file mode 100644
index 0000000000..4cb7353e70
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/panelarrow-horizontal@2x.png
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/panelarrow-vertical.png b/toolkit/themes/osx/global/arrow/panelarrow-vertical.png
new file mode 100644
index 0000000000..3986f9cbf5
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/panelarrow-vertical.png
Binary files differ
diff --git a/toolkit/themes/osx/global/arrow/panelarrow-vertical@2x.png b/toolkit/themes/osx/global/arrow/panelarrow-vertical@2x.png
new file mode 100644
index 0000000000..a741dd0e16
--- /dev/null
+++ b/toolkit/themes/osx/global/arrow/panelarrow-vertical@2x.png
Binary files differ
diff --git a/toolkit/themes/osx/global/autocomplete.css b/toolkit/themes/osx/global/autocomplete.css
new file mode 100644
index 0000000000..7e05d2f29c
--- /dev/null
+++ b/toolkit/themes/osx/global/autocomplete.css
@@ -0,0 +1,174 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+/* .padded is used by autocomplete widgets that don't have an icon. Gross. -dwh */
+textbox:not(.padded) {
+ cursor: default;
+ padding: 0;
+}
+
+textbox[nomatch="true"][highlightnonmatches="true"] {
+ color: red;
+}
+
+textbox:not(.padded) .textbox-input-box {
+ margin: 0 3px;
+}
+
+.textbox-input-box {
+ -moz-box-align: center;
+}
+
+/* ::::: history button ::::: */
+
+.autocomplete-history-dropmarker {
+ -moz-appearance: none !important;
+ border: none !important;
+ background-color: transparent !important;
+ padding: 0px;
+ list-style-image: url("chrome://global/skin/icons/autocomplete-dropmarker.png");
+ margin: 0px;
+}
+
+/* ::::: autocomplete popups ::::: */
+
+panel[type="autocomplete"],
+panel[type="autocomplete-richlistbox"],
+.autocomplete-history-popup {
+ padding: 0px !important;
+ color: -moz-FieldText;
+ background-color: -moz-Field;
+ font: icon;
+ -moz-appearance: none;
+}
+
+.autocomplete-history-popup {
+ max-height: 180px;
+}
+
+/* ::::: tree ::::: */
+
+.autocomplete-tree {
+ -moz-appearance: none !important;
+ border: none !important;
+ background-color: transparent !important;
+}
+
+.autocomplete-treecol {
+ -moz-appearance: none !important;
+ margin: 0 !important;
+ border: none !important;
+ padding: 0 !important;
+}
+
+.autocomplete-treebody::-moz-tree-cell-text {
+ padding-left: 2px;
+}
+
+.autocomplete-treebody::-moz-tree-row {
+ border-top: none;
+}
+
+treechildren.autocomplete-treebody::-moz-tree-row(selected) {
+ background-color: Highlight;
+}
+
+treechildren.autocomplete-treebody::-moz-tree-cell-text(selected) {
+ color: HighlightText !important;
+}
+
+.autocomplete-treebody::-moz-tree-image(treecolAutoCompleteValue) {
+ max-width: 16px;
+ height: 16px;
+}
+
+/* ::::: richlistbox autocomplete ::::: */
+
+.autocomplete-richlistbox {
+ -moz-appearance: none;
+ margin: 0;
+}
+
+.ac-type-icon {
+ width: 16px;
+ height: 16px;
+ max-width: 16px;
+ max-height: 16px;
+ margin-inline-start: 16px;
+ margin-inline-end: 6px;
+}
+
+.ac-site-icon {
+ width: 16px;
+ height: 16px;
+ max-width: 16px;
+ max-height: 16px;
+ margin-inline-start: 0;
+ margin-inline-end: 11px;
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon.png");
+}
+
+.ac-site-icon[selected] {
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon-inverted.png");
+}
+
+@media (min-resolution: 2dppx) {
+ .ac-site-icon {
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon@2x.png");
+ }
+ .ac-site-icon[selected] {
+ list-style-image: url("chrome://mozapps/skin/places/defaultFavicon-inverted@2x.png");
+ }
+}
+
+.ac-title {
+ margin-inline-start: 0;
+ margin-inline-end: 6px;
+}
+
+html|span.ac-tag {
+ margin-inline-start: 0;
+ margin-inline-end: 2px;
+}
+
+.ac-tags {
+ margin-inline-start: 0;
+ margin-inline-end: 4px;
+}
+
+.ac-separator {
+ margin-inline-start: 0;
+ margin-inline-end: 6px;
+}
+
+/* Better align the URL/action with the title. */
+.ac-tags,
+.ac-separator,
+.ac-url,
+.ac-action {
+ margin-bottom: -2px;
+}
+
+.ac-title-text,
+.ac-tags-text,
+.ac-separator-text,
+.ac-url-text,
+.ac-action-text,
+.ac-text-overflow-container {
+ padding: 0 !important;
+ margin: 0 !important;
+}
+
+/* ::::: textboxes inside toolbarpaletteitems ::::: */
+
+toolbarpaletteitem > toolbaritem > textbox > hbox > hbox > html|*.textbox-input {
+ visibility: hidden;
+}
+
+toolbarpaletteitem > toolbaritem > * > textbox > hbox > hbox > html|*.textbox-input {
+ visibility: hidden;
+}
diff --git a/toolkit/themes/osx/global/button.css b/toolkit/themes/osx/global/button.css
new file mode 100644
index 0000000000..45f292e1f1
--- /dev/null
+++ b/toolkit/themes/osx/global/button.css
@@ -0,0 +1,85 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+button {
+ -moz-appearance: button;
+ /* The horizontal margin used here come from the Aqua Human Interface
+ Guidelines, there should be 12 pixels between two buttons. */
+ margin: 5px 6px 3px;
+ min-width: 79px;
+ color: ButtonText;
+ text-shadow: none;
+}
+
+button:not([disabled="true"]):hover:active {
+ color: -moz-mac-buttonactivetext;
+}
+
+/* When the window isn't focused, the default button background isn't drawn,
+ * so don't change the text color then: */
+button[default="true"]:not([disabled="true"]):not(:-moz-window-inactive) {
+ color: -moz-mac-defaultbuttontext;
+}
+
+/* Likewise, when active (mousedown) but not hovering, the default button
+ * background isn't drawn, override the previous selector for that case: */
+button[default="true"]:not(:hover):active {
+ color: ButtonText;
+}
+
+.button-text {
+ margin: 1px 0 !important;
+ margin-inline-start: 3px !important;
+ margin-inline-end: 2px !important;
+ text-align: center;
+}
+
+.button-icon {
+ margin-inline-start: 1px;
+}
+
+button[type="default"] {
+ font: menu;
+}
+
+/* .......... disabled state .......... */
+
+button[disabled="true"] {
+ color: GrayText;
+}
+
+/* ::::: menu/menu-button buttons ::::: */
+
+button[type="menu-button"] {
+ margin: 0;
+ border: none;
+}
+
+.button-menu-dropmarker,
+.button-menubutton-dropmarker {
+ -moz-appearance: none !important;
+ border: none;
+ background-color: transparent !important;
+ margin: 1px;
+}
+
+.button-menu-dropmarker {
+ display: none;
+}
+
+/* ::::: plain buttons ::::: */
+
+button.plain {
+ margin: 0 !important;
+ padding: 0 !important;
+}
+
+/* ::::: help button ::::: */
+
+button[dlgtype="help"] {
+ -moz-appearance: -moz-mac-help-button;
+ width: 20px;
+}
diff --git a/toolkit/themes/osx/global/checkbox.css b/toolkit/themes/osx/global/checkbox.css
new file mode 100644
index 0000000000..b49af98d04
--- /dev/null
+++ b/toolkit/themes/osx/global/checkbox.css
@@ -0,0 +1,39 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+checkbox {
+ -moz-appearance: checkbox-container;
+ -moz-box-align: center;
+ margin: 4px 2px;
+}
+
+.checkbox-icon {
+ margin-right: 2px;
+}
+
+.checkbox-label {
+ margin: 1px 0 !important;
+}
+
+/* ..... disabled state ..... */
+
+checkbox[disabled="true"] {
+ color: GrayText !important;
+}
+
+/* ::::: checkmark image ::::: */
+
+.checkbox-check {
+ -moz-appearance: checkbox;
+ margin: 1px 1px 0;
+ /* vertical-align tells native theming where to snap to. However, this doesn't
+ * always work reliably because of bug 503833. */
+ vertical-align: top;
+ width: 1.3em;
+ height: 1.3em;
+}
+
+
diff --git a/toolkit/themes/osx/global/checkbox/cbox-check-dis.gif b/toolkit/themes/osx/global/checkbox/cbox-check-dis.gif
new file mode 100644
index 0000000000..bd43dd17c3
--- /dev/null
+++ b/toolkit/themes/osx/global/checkbox/cbox-check-dis.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/checkbox/cbox-check.gif b/toolkit/themes/osx/global/checkbox/cbox-check.gif
new file mode 100644
index 0000000000..f6919f8fad
--- /dev/null
+++ b/toolkit/themes/osx/global/checkbox/cbox-check.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/colorpicker.css b/toolkit/themes/osx/global/colorpicker.css
new file mode 100644
index 0000000000..075437db89
--- /dev/null
+++ b/toolkit/themes/osx/global/colorpicker.css
@@ -0,0 +1,41 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: colorpicker button ::::: */
+
+colorpicker[type="button"] {
+ width: 38px;
+ height: 24px;
+ border: 1px solid #a7a7a7;
+ background-color: ThreeDFace;
+ padding: 3px;
+ -moz-appearance: button-bevel;
+}
+
+.colorpicker-button-colorbox {
+ border: 1px solid #000000;
+}
+
+/* ::::: colorpicker tiles ::::: */
+
+.colorpickertile {
+ width : 20px;
+ height : 20px;
+ margin : 1px;
+}
+
+.colorpickertile[selected="true"] {
+ border : 1px outset #C0C0C0;
+
+}
+
+.colorpickertile[hover="true"] {
+ border : 1px dotted #A7A7A7;
+}
+
+.cp-light[hover="true"] {
+ border : 1px dotted #000000;
+}
diff --git a/toolkit/themes/osx/global/commonDialog.css b/toolkit/themes/osx/global/commonDialog.css
new file mode 100644
index 0000000000..53b02796d2
--- /dev/null
+++ b/toolkit/themes/osx/global/commonDialog.css
@@ -0,0 +1,35 @@
+/* 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/. */
+
+#commonDialog {
+ line-height: 13px;
+}
+
+#filler {
+ margin: 0px -14px;
+}
+
+#infoContainer {
+ max-width: 33em;
+}
+
+#loginContainer {
+ padding-top: 10px;
+}
+
+#info\.icon {
+ margin-inline-end: 14px;
+}
+
+#info\.title,
+#info\.header,
+#info\.body {
+ font: menu;
+ line-height: 16px;
+ margin-bottom: 6px;
+}
+
+#info\.title {
+ font-weight: bold;
+}
diff --git a/toolkit/themes/osx/global/console/console-error-caret.gif b/toolkit/themes/osx/global/console/console-error-caret.gif
new file mode 100644
index 0000000000..a8f30f9263
--- /dev/null
+++ b/toolkit/themes/osx/global/console/console-error-caret.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/console/console-error-dash.gif b/toolkit/themes/osx/global/console/console-error-dash.gif
new file mode 100644
index 0000000000..74679a25e2
--- /dev/null
+++ b/toolkit/themes/osx/global/console/console-error-dash.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/console/console.css b/toolkit/themes/osx/global/console/console.css
new file mode 100644
index 0000000000..f18f6f680c
--- /dev/null
+++ b/toolkit/themes/osx/global/console/console.css
@@ -0,0 +1,165 @@
+/* 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/. */
+
+/* ===== console.css ====================================================
+ == Styles used by the Error Console window.
+ ======================================================================= */
+
+/* View buttons */
+@import "chrome://global/skin/viewbuttons.css";
+
+%include ../shared.inc
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.console-box {
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+ overflow: auto;
+}
+
+/* ::::: console rows ::::: */
+
+.console-row {
+ border-bottom: 1px solid #A3A3A3;
+ padding: 4px;
+}
+
+.console-row-file {
+ color: #505050;
+}
+
+.console-row-msg > label:first-child {
+ font-weight: bold;
+}
+
+.console-row-msg > label, .comsole-row-msg > description, .console-error-msg, .console-row-file, .console-row-code {
+ margin: 2px;
+}
+
+.console-row-file > label {
+ margin: 0;
+}
+
+.console-msg-text {
+ white-space: pre-wrap !important;
+}
+.console-icon {
+ list-style-image: inherit;
+ padding-right: 6px;
+ padding-left: 6px;
+}
+
+/* ..... error rows ..... */
+
+.console-row-code {
+ color: #0000BB;
+ font-size: larger;
+}
+
+.console-dots,
+.console-caret {
+ height: 9px;
+}
+
+.console-dots {
+ background: url("chrome://global/skin/console/console-error-dash.gif") repeat-x top;
+}
+
+.console-caret {
+ width: 7px;
+ background: url("chrome://global/skin/console/console-error-caret.gif") no-repeat top;
+}
+
+/* ..... message rows ..... */
+
+.console-row[type="message"] {
+ font-family: monospace;
+}
+
+/* ..... selected state ..... */
+
+.console-row[selected="true"] {
+ background-color: #3D80DF !important;
+ color: #FFF;
+}
+
+.console-row-code[selected="true"],
+.console-row-content[selected="true"] > .console-row-file,
+.console-row-content[selected="true"] > .console-row-file > .console-error-source > .text-link {
+ color: #FFF !important;
+}
+
+/* ::::: row colors ::::: */
+
+.console-row[type="error"],
+.console-row[type="exception"] {
+ background-color: #FFD0DC;
+}
+
+.console-row[type="warning"] {
+ background-color: #F8F3CC;
+}
+
+.console-row[type="message"] {
+ background-color: #D3EDFF;
+}
+
+/* ::::: toolbars ::::: */
+
+#ToolbarEval {
+ -moz-appearance: none;
+ background: @scopeBarBackground@;
+ border-bottom: @scopeBarSeparatorBorder@;
+ padding: 2px;
+}
+
+#ToolbarEval > label {
+ font-weight: bold;
+ color: @scopeBarTitleColor@;
+}
+
+#TextfieldEval {
+ margin: 2px !important;
+}
+
+#ButtonEval {
+ margin: 0 4px;
+ padding: 1px 10px;
+ -moz-appearance: none;
+ border-radius: 10000px;
+ border: @roundButtonBorder@;
+ background: @roundButtonBackground@;
+ box-shadow: @roundButtonShadow@;
+}
+
+#ButtonEval:hover:active {
+ text-shadow: @loweredShadow@;
+ background: @roundButtonPressedBackground@;
+ box-shadow: @roundButtonPressedShadow@;
+}
+
+toolbarseparator {
+ min-height: 1em;
+ background-image: none;
+}
+
+/* Toolbar icons */
+
+#ToolbarMode {
+ -moz-box-pack: center;
+}
+
+#ToolbarMode toolbarbutton > .toolbarbutton-icon {
+ display: none;
+}
+
+#Console\:clear {
+ -moz-box-orient: vertical;
+ -moz-box-align: center;
+ -moz-appearance: toolbarbutton;
+ font: menu;
+ text-shadow: @loweredShadow@;
+ margin: 4px 0 9px;
+ padding: 0 1px;
+}
diff --git a/toolkit/themes/osx/global/customizeToolbar.css b/toolkit/themes/osx/global/customizeToolbar.css
new file mode 100644
index 0000000000..bcedb2b99a
--- /dev/null
+++ b/toolkit/themes/osx/global/customizeToolbar.css
@@ -0,0 +1,38 @@
+/* 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/. */
+
+#palette-box {
+ margin-top: 2px;
+ -moz-appearance: listbox;
+ margin: 0 0 10px;
+}
+
+#palette-box > toolbarpaletteitem {
+ padding: 8px 2px;
+ margin: 0 8px;
+}
+
+#main-box {
+ padding: 12px;
+}
+
+#main-box > separator {
+ -moz-appearance: none;
+ border-bottom: none;
+}
+
+#instructions {
+ font: menu;
+ font-weight: bold;
+ line-height: 16pt;
+}
+
+hbox button {
+ font: menu;
+}
+
+#main-box > box > button {
+ min-height: 19px; /* aqua size for small buttons */
+ font: message-box;
+}
diff --git a/toolkit/themes/osx/global/datetimepicker.css b/toolkit/themes/osx/global/datetimepicker.css
new file mode 100644
index 0000000000..3d7b201f2e
--- /dev/null
+++ b/toolkit/themes/osx/global/datetimepicker.css
@@ -0,0 +1,126 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+datepicker, timepicker {
+ padding: 0 0 1px;
+ margin: 4px;
+ border: none;
+}
+
+.datetimepicker-input-box {
+ -moz-appearance: textfield;
+ cursor: text;
+ margin-right: 4px;
+ margin-bottom: 2px;
+ border: 3px solid;
+ -moz-border-top-colors: transparent #888888 #000000;
+ -moz-border-right-colors: transparent #FFFFFF #000000;
+ -moz-border-bottom-colors: transparent #FFFFFF #000000;
+ -moz-border-left-colors: transparent #888888 #000000;
+ border-top-right-radius: 2px;
+ border-bottom-left-radius: 2px;
+ padding: 0px;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+.datetimepicker-input-subbox {
+ width: 1.6em;
+}
+
+html|*.datetimepicker-input {
+ text-align: end;
+}
+
+.datetimepicker-separator {
+ margin: 0 !important;
+}
+
+.datetimepicker-year {
+ width: 3.2em;
+}
+
+.datepicker-dropmarker {
+ margin-bottom: 2px;
+}
+
+datepicker[readonly="true"],
+timepicker[readonly="true"] {
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+datepicker[disabled="true"],
+timepicker[disabled="true"] {
+ cursor: default;
+ -moz-border-top-colors: transparent ThreeDShadow -moz-Dialog;
+ -moz-border-right-colors: transparent ThreeDShadow -moz-Dialog;
+ -moz-border-bottom-colors: transparent ThreeDShadow -moz-Dialog;
+ -moz-border-left-colors: transparent ThreeDShadow -moz-Dialog;
+ background-color: -moz-Dialog;
+ color: GrayText;
+}
+
+.datepicker-mainbox {
+ margin: 2px 4px;
+ border: 2px solid;
+ -moz-border-top-colors: ThreeDShadow ThreeDDarkShadow;
+ -moz-border-right-colors: ThreeDHighlight ThreeDLightShadow;
+ -moz-border-bottom-colors: ThreeDHighlight ThreeDLightShadow;
+ -moz-border-left-colors: ThreeDShadow ThreeDDarkShadow;
+ background-color: #EEEEEE;
+ color: -moz-DialogText;
+}
+
+.datepicker-popupgrid > .datepicker-mainbox {
+ margin: 0;
+ border: none;
+}
+
+.datepicker-gridlabel, .datepicker-weeklabel {
+ text-align: center;
+}
+
+.datepicker-gridlabel[today="true"] {
+ background-color: darkgrey;
+ color: white;
+}
+
+.datepicker-gridlabel[selected="true"] {
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+.datepicker-button {
+ -moz-appearance: none;
+ min-width: 8px;
+ padding: 0px;
+}
+
+.datepicker-previous {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft.gif");
+}
+
+.datepicker-next {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit.gif");
+}
+
+.datepicker-previous:hover {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft-hov.gif");
+}
+
+.datepicker-next:hover {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit-hov.gif");
+}
+
+.datepicker-previous[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft-dis.gif");
+}
+
+.datepicker-next[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit-dis.gif");
+}
diff --git a/toolkit/themes/osx/global/dialog.css b/toolkit/themes/osx/global/dialog.css
new file mode 100644
index 0000000000..98ed3ca207
--- /dev/null
+++ b/toolkit/themes/osx/global/dialog.css
@@ -0,0 +1,77 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#commonDialog > image {
+ margin-inline-end: 14px !important;
+}
+
+#commonDialog > .dialog-button-box {
+ margin-inline-start: 80px;
+}
+
+dialog {
+ -moz-appearance: dialog;
+ padding: 14px;
+}
+
+/* ::::: dialog buttons ::::: */
+
+.dialog-button {
+ font: menu;
+}
+
+/* ::::: dialog header ::::: */
+
+dialogheader {
+ margin: 0 5px 5px;
+ padding: 5px 8px;
+}
+
+.dialogheader-title {
+ margin: 0 !important;
+ font-size: larger;
+ font-weight: bold;
+ display: none;
+}
+
+/* ::::: large dialog header ::::: */
+
+.header-large {
+ -moz-box-orient: vertical;
+ margin: -14px -14px 0;
+ padding: 12px;
+ padding-inline-end: 5px;
+ padding-inline-start: 25px;
+}
+
+.header-large > .dialogheader-title {
+ font: inherit;
+ font-weight: bold;
+}
+
+.header-large > .dialogheader-description {
+ margin-left: 12px !important;
+}
+
+.dialogheader-description {
+ font-weight: bold !important;
+}
+
+.dialogheader-title {
+ font-weight: bold !important;
+}
+
+/*XXX - belongs to toolkit/content/finddialog.xul: */
+
+#findDialog,
+#findDialog > menu,
+#findDialog > groupbox {
+ font: menu !important;
+}
+
+#dialog\.caseSensitive {
+ margin-top: 8px;
+}
diff --git a/toolkit/themes/osx/global/dirListing/dirListing.css b/toolkit/themes/osx/global/dirListing/dirListing.css
new file mode 100644
index 0000000000..de881a5e4b
--- /dev/null
+++ b/toolkit/themes/osx/global/dirListing/dirListing.css
@@ -0,0 +1,104 @@
+/* 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/. */
+
+:root {
+ background-color: -moz-dialog;
+ color: -moz-dialogtext;
+ font: message-box;
+ padding-left: 2em;
+ padding-right: 2em;
+}
+
+body {
+ border: 1px solid ThreeDShadow;
+ border-radius: 10px;
+ padding: 3em;
+ min-width: 30em;
+ max-width: 65em;
+ margin: 4em auto;
+ background-color: -moz-field;
+ color: -moz-fieldtext;
+}
+
+h1 {
+ font-size: 160%;
+ margin: 0 0 .6em;
+ border-bottom: 1px solid ThreeDLightShadow;
+ font-weight: normal;
+}
+
+a {
+ text-decoration: none;
+}
+
+a:hover {
+ text-decoration: underline;
+}
+
+p {
+ font-size: 110%;
+}
+
+#UI_goUp {
+ margin-top: 0;
+ float: left;
+}
+
+#UI_goUp:dir(rtl) {
+ float: right;
+}
+
+#UI_showHidden {
+ margin-top: 0;
+ float: right;
+}
+
+#UI_showHidden:dir(rtl) {
+ float: left;
+}
+
+table {
+ clear: both;
+ width: 90%;
+ margin: 0 auto;
+}
+
+thead {
+ font-size: 130%;
+}
+
+/* last modified */
+th:last-child {
+ text-align: center;
+}
+
+th:hover > a {
+ text-decoration: underline;
+}
+
+body > table > tbody > tr:hover {
+ outline: 1px solid ThreeDLightShadow;
+ -moz-outline-radius: .3em;
+}
+
+/* let 'Size' and 'Last Modified' take only as much space as they need and 'Name' all the rest */
+td:not(:first-child) {
+ width: 0;
+}
+
+.up {
+ padding: 0 .5em;
+ margin-inline-start: 20px;
+}
+
+.up::before {
+ margin-inline-end: 4px;
+ margin-inline-start: -20px;
+ vertical-align: middle;
+ content: url(chrome://global/skin/dirListing/up.png);
+}
+
+.dir::before {
+ content: url(chrome://global/skin/dirListing/folder.png);
+}
diff --git a/toolkit/themes/osx/global/dirListing/folder.png b/toolkit/themes/osx/global/dirListing/folder.png
new file mode 100644
index 0000000000..eb3a607e03
--- /dev/null
+++ b/toolkit/themes/osx/global/dirListing/folder.png
Binary files differ
diff --git a/toolkit/themes/osx/global/dirListing/remote.png b/toolkit/themes/osx/global/dirListing/remote.png
new file mode 100644
index 0000000000..d854bd9d9f
--- /dev/null
+++ b/toolkit/themes/osx/global/dirListing/remote.png
Binary files differ
diff --git a/toolkit/themes/osx/global/dirListing/up.png b/toolkit/themes/osx/global/dirListing/up.png
new file mode 100644
index 0000000000..7af8949ad3
--- /dev/null
+++ b/toolkit/themes/osx/global/dirListing/up.png
Binary files differ
diff --git a/toolkit/themes/osx/global/dropmarker.css b/toolkit/themes/osx/global/dropmarker.css
new file mode 100644
index 0000000000..701eea75c4
--- /dev/null
+++ b/toolkit/themes/osx/global/dropmarker.css
@@ -0,0 +1,31 @@
+/* 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/. */
+
+dropmarker {
+ -moz-appearance: menulist-button;
+ width: 16px;
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ border: 2px solid;
+ -moz-border-top-colors: ThreeDLightShadow ThreeDHighlight;
+ -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-bottom-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-left-colors: ThreeDLightShadow ThreeDHighlight;
+ background-color: -moz-Dialog;
+ padding: 1px;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+ -moz-image-region: auto;
+}
+
+dropmarker:hover:active:not([disabled="true"]) {
+ -moz-border-top-colors: ThreeDShadow ThreeDFace;
+ -moz-border-right-colors: ThreeDShadow ThreeDFace;
+ -moz-border-bottom-colors: ThreeDShadow ThreeDFace;
+ -moz-border-left-colors: ThreeDShadow ThreeDFace;
+ padding: 2px 0 0 2px;
+}
+
+dropmarker[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.gif");
+}
diff --git a/toolkit/themes/osx/global/filefield.css b/toolkit/themes/osx/global/filefield.css
new file mode 100644
index 0000000000..8ae3fdb52a
--- /dev/null
+++ b/toolkit/themes/osx/global/filefield.css
@@ -0,0 +1,38 @@
+/*
+# -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 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/.
+*/
+
+.fileFieldIcon {
+ width: 16px;
+ height: 16px;
+}
+
+.fileFieldIcon[disabled="true"] {
+ opacity: 0.5;
+}
+
+filefield {
+ margin: 4px;
+ margin-inline-start: 27px;
+ -moz-appearance: textfield;
+}
+
+.fileFieldContentBox {
+ margin: -3px;
+ background-color: rgba(230, 230, 230, 0.6);
+ color: -moz-DialogText;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ padding-inline-start: 5px;
+ padding-inline-end: 3px;
+}
+
+.fileFieldLabel {
+ -moz-appearance: none;
+ background-color: transparent;
+ border: none;
+ margin: 0 4px;
+}
diff --git a/toolkit/themes/osx/global/filters.svg b/toolkit/themes/osx/global/filters.svg
new file mode 100644
index 0000000000..d3ad6a76b8
--- /dev/null
+++ b/toolkit/themes/osx/global/filters.svg
@@ -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/. -->
+
+<svg xmlns="http://www.w3.org/2000/svg">
+ <filter id="iconPressed" color-interpolation-filters="sRGB">
+ <!-- Multiply all components with 0.55. -->
+ <feComponentTransfer>
+ <feFuncR type="linear" slope=".55"/>
+ <feFuncG type="linear" slope=".55"/>
+ <feFuncB type="linear" slope=".55"/>
+ </feComponentTransfer>
+ </filter>
+</svg>
diff --git a/toolkit/themes/osx/global/findBar.css b/toolkit/themes/osx/global/findBar.css
new file mode 100644
index 0000000000..869a624322
--- /dev/null
+++ b/toolkit/themes/osx/global/findBar.css
@@ -0,0 +1,270 @@
+/* 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 shared.inc
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+findbar {
+ background: @scopeBarBackground@;
+ border-top: @scopeBarSeparatorBorder@;
+ min-width: 1px;
+ padding: 4px 2px;
+ transition-property: margin-bottom, opacity, visibility;
+ transition-duration: 150ms, 150ms, 0s;
+ transition-timing-function: ease-in-out, ease-in-out, linear;
+}
+
+findbar[hidden] {
+ /* Override display:none to make the transition work. */
+ display: -moz-box;
+ visibility: collapse;
+ margin-bottom: -1em;
+ opacity: 0;
+ transition-delay: 0s, 0s, 150ms;
+}
+
+findbar:-moz-lwtheme {
+ -moz-appearance: none;
+ background: none;
+ border-style: none;
+}
+
+label.findbar-find-fast {
+ margin: 1px 3px 0 !important;
+ color: @scopeBarTitleColor@;
+ font-weight: bold;
+ text-shadow: @loweredShadow@;
+}
+
+label.findbar-find-fast:-moz-lwtheme,
+.findbar-find-status:-moz-lwtheme {
+ color: inherit;
+ text-shadow: inherit;
+}
+
+.findbar-closebutton {
+ padding: 0;
+ margin: 0 4px;
+ border: none;
+}
+
+.findbar-closebutton:-moz-lwtheme-brighttext {
+ list-style-image: url("chrome://global/skin/icons/close-inverted.png");
+}
+
+@media (min-resolution: 2dppx) {
+ .findbar-closebutton:-moz-lwtheme-brighttext {
+ list-style-image: url("chrome://global/skin/icons/close-inverted@2x.png");
+ }
+
+ .findbar-closebutton > .toolbarbutton-icon {
+ width: 16px;
+ }
+}
+
+.findbar-find-next,
+.findbar-find-previous,
+.findbar-highlight {
+ margin: 0 4px;
+ padding: 1px 3px;
+ -moz-appearance: none;
+ border-radius: 10000px;
+ border: @roundButtonBorder@;
+ background: @roundButtonBackground@;
+ box-shadow: @roundButtonShadow@;
+ color: buttontext;
+}
+
+.findbar-container > toolbarbutton:-moz-focusring {
+ position: relative;
+ box-shadow: @focusRingShadow@, @roundButtonShadow@;
+}
+
+.findbar-container > toolbarbutton > .toolbarbutton-text {
+ margin: 0 6px !important;
+}
+
+.findbar-container > toolbarbutton[disabled] {
+ color: GrayText !important;
+}
+
+.findbar-find-next:not([disabled]):hover:active,
+.findbar-find-previous:not([disabled]):hover:active,
+.findbar-highlight:not([disabled]):hover:active {
+ text-shadow: @loweredShadow@;
+ background: @roundButtonPressedBackground@;
+ box-shadow: @roundButtonPressedShadow@;
+}
+
+.findbar-container > toolbarbutton:hover:active:-moz-focusring {
+ text-shadow: @loweredShadow@;
+ background: @roundButtonPressedBackground@;
+ box-shadow: @focusRingShadow@, @roundButtonPressedShadow@;
+}
+
+.findbar-closebutton > .toolbarbutton-text {
+ display: none;
+}
+
+/* Match case checkbox */
+
+.findbar-container > checkbox {
+ list-style-image: url("chrome://global/skin/icons/checkbox.png");
+ -moz-image-region: rect(0px 16px 16px 0px);
+ -moz-appearance: none;
+ margin: 0 2px;
+ -moz-margin-start: 7px;
+}
+
+.findbar-container > checkbox:hover:active {
+ -moz-image-region: rect(0px 32px 16px 16px);
+}
+.findbar-container > checkbox[checked] {
+ -moz-image-region: rect(0px 48px 16px 32px);
+}
+.findbar-container > checkbox[checked]:hover:active {
+ -moz-image-region: rect(0px 64px 16px 48px);
+}
+
+@media (min-resolution: 2dppx) {
+ .findbar-container > checkbox {
+ list-style-image: url("chrome://global/skin/icons/checkbox@2x.png");
+ -moz-image-region: rect(0px 32px 32px 0px);
+ }
+
+ .findbar-container > checkbox:hover:active {
+ -moz-image-region: rect(0px 64px 32px 32px);
+ }
+ .findbar-container > checkbox[checked] {
+ -moz-image-region: rect(0px 96px 32px 64px);
+ }
+ .findbar-container > checkbox[checked]:hover:active {
+ -moz-image-region: rect(0px 128px 32px 96px);
+ }
+}
+
+
+
+.findbar-container > checkbox > .checkbox-check {
+ display: none;
+}
+
+.findbar-container > checkbox > .checkbox-label-box > .checkbox-label {
+ margin: 0 !important;
+ padding: 2px 0 0;
+}
+
+.findbar-container > checkbox > .checkbox-label-box > .checkbox-icon {
+ -moz-padding-start: 1px;
+ padding-bottom: 1px;
+}
+@media (min-resolution: 2dppx) {
+ .findbar-container > checkbox > .checkbox-label-box > .checkbox-icon {
+ width: 17px;
+ height: 17px;
+ }
+}
+
+.findbar-container > checkbox:-moz-focusring > .checkbox-label-box > .checkbox-icon {
+ border-radius: 4px;
+ box-shadow: @focusRingShadow@;
+}
+
+/* Search field */
+
+.findbar-textbox {
+ -moz-appearance: none;
+ border-radius: 10000px;
+ border: none;
+ box-shadow: 0 1px 1.5px rgba(0, 0, 0, .7) inset,
+ 0 0 0 1px rgba(0, 0, 0, .17) inset;
+ background: url("chrome://global/skin/icons/search-textbox.png") -moz-Field no-repeat 5px center;
+ margin: 0 4px -1px;
+ padding: 3px 8px 2px;
+ -moz-padding-start: 19px;
+}
+
+.findbar-textbox:not([focused="true"]):-moz-lwtheme {
+ opacity: 0.9;
+}
+
+.findbar-textbox[focused="true"] {
+ box-shadow: @focusRingShadow@,
+ 0 1px 1.5px rgba(0, 0, 0, .8) inset;
+}
+
+.findbar-textbox[flash="true"] {
+ background-color: #F7E379;
+ color: #000;
+}
+
+.findbar-textbox[status="notfound"] {
+ background-color: #FD919B;
+ color: #FFF;
+}
+
+/* find-next button */
+
+.findbar-find-next {
+ -moz-border-end: none;
+ -moz-margin-end: 0 !important;
+}
+
+.findbar-find-next:-moz-locale-dir(ltr),
+.findbar-find-previous:-moz-locale-dir(rtl) {
+ border-top-right-radius: 0px;
+ border-bottom-right-radius: 0px;
+}
+
+/* find-previous button */
+
+.findbar-find-previous {
+ -moz-margin-start: 0 !important;
+}
+
+.findbar-find-previous:-moz-locale-dir(ltr),
+.findbar-find-next:-moz-locale-dir(rtl) {
+ border-top-left-radius: 0px;
+ border-bottom-left-radius: 0px;
+}
+
+/* highlight button */
+
+.findbar-highlight {
+ -moz-margin-start: 8px;
+}
+
+.findbar-highlight > .toolbarbutton-icon {
+ width: 13px;
+ height: 8px;
+ margin: 0 4px;
+ -moz-margin-end: 0;
+ border: 1px solid #818181;
+ border-radius: 4px;
+ background-color: #F4F4F3;
+}
+
+
+.findbar-highlight[checked="true"] > .toolbarbutton-icon {
+ background-color: #FFFF00;
+ border-color: #818100;
+}
+
+.find-status-icon {
+ display: none;
+}
+
+.find-status-icon[status="pending"] {
+ display: block;
+ list-style-image: url("chrome://global/skin/icons/loading_16.png");
+}
+
+.findbar-find-status,
+.found-matches {
+ color: #436599;
+ font-weight: bold;
+ text-shadow: 0 1px rgba(255, 255, 255, .4);
+ margin: 1px 1px 0 !important;
+ -moz-margin-start: 12px !important;
+}
diff --git a/toolkit/themes/osx/global/global.css b/toolkit/themes/osx/global/global.css
new file mode 100644
index 0000000000..261abe3138
--- /dev/null
+++ b/toolkit/themes/osx/global/global.css
@@ -0,0 +1,378 @@
+/* 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/. */
+
+/* all localizable skin settings shall live here */
+@import url("chrome://global/locale/intl.css");
+
+%include shared.inc
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: XBL bindings ::::: */
+
+menulist > menupopup {
+ -moz-binding: url("chrome://global/content/bindings/popup.xml#popup-scrollbars");
+}
+
+/* ::::: Variables ::::: */
+:root {
+ --arrowpanel-padding: 16px;
+ --arrowpanel-background: linear-gradient(hsla(0,0%,99%,1), hsla(0,0%,99%,.975) 10%, hsla(0,0%,98%,.975));
+ --arrowpanel-color: hsl(0,0%,10%);
+ --arrowpanel-border-color: hsla(210,4%,10%,.05);
+ --arrowpanel-border-radius: 3.5px;
+}
+
+/* ::::: root elements ::::: */
+
+window,
+page,
+dialog,
+wizard,
+prefwindow {
+ -moz-appearance: dialog;
+ background-color: #FFFFFF;
+ color: -moz-DialogText;
+ font: message-box;
+}
+
+prefwindow[type="child"] {
+ padding-top: 18px;
+ padding-bottom: 15px;
+ padding-inline-start: 18px;
+ padding-inline-end: 20px;
+}
+
+/* deprecated */
+window.dialog {
+ padding-top: 8px;
+ padding-bottom: 10px;
+ padding-inline-start: 8px;
+ padding-inline-end: 10px;
+}
+
+/* ::::: alert icons :::::*/
+
+.message-icon,
+.alert-icon,
+.error-icon,
+.question-icon {
+ width: 64px;
+ height: 64px;
+ margin: 6px;
+ margin-inline-end: 20px;
+}
+
+.message-icon {
+ list-style-image: url("chrome://global/skin/icons/information-64.png");
+}
+
+.alert-dialog #info\.icon,
+.alert-icon {
+ list-style-image: url("chrome://global/skin/icons/warning-64.png");
+}
+
+.error-icon {
+ list-style-image: url("chrome://global/skin/icons/error-64.png");
+}
+
+.question-icon {
+ list-style-image: url("chrome://global/skin/icons/question-64.png");
+}
+
+/* ::::: iframe ::::: */
+
+iframe {
+ border: none;
+ width: 100px;
+ height: 100px;
+ min-width: 10px;
+ min-height: 10px;
+}
+
+/* ::::: statusbar ::::: */
+
+statusbar {
+ min-width: 1px; /* DON'T DELETE!
+ Prevents hiding of scrollbars in browser when window is made smaller.*/
+ min-height: 15px !important;
+ margin: 0px !important;
+ /* need to use padding-inline-end when/if bug 631729 gets fixed: */
+ padding: 0px 16px 1px 1px;
+ -moz-appearance: statusbar;
+ text-shadow: rgba(255, 255, 255, 0.4) 0 1px;
+}
+
+statusbarpanel {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ padding: 0 4px;
+}
+
+.statusbarpanel-iconic {
+ padding: 0px;
+}
+
+/* ::::: miscellaneous formatting ::::: */
+
+:root:-moz-lwtheme,
+[lwthemefooter="true"] {
+ -moz-appearance: none;
+}
+
+:root:-moz-lwtheme-darktext {
+ text-shadow: 0 -0.5px 1.5px white;
+}
+
+:root:-moz-lwtheme-brighttext {
+ text-shadow: 1px 1px 1.5px black;
+}
+
+statusbar:-moz-lwtheme {
+ -moz-appearance: none;
+ background: none;
+ border-style: none;
+ text-shadow: inherit;
+}
+
+.inset {
+ border: 1px solid ThreeDShadow;
+ border-right-color: ThreeDHighlight;
+ border-bottom-color: ThreeDHighlight;
+ margin: 0 5px 5px;
+}
+
+.outset {
+ border: 1px solid ThreeDShadow;
+ border-left-color: ThreeDHighlight;
+ border-top-color: ThreeDHighlight;
+}
+
+separator:not([orient="vertical"]) {
+ height: 1.5em;
+}
+separator[orient="vertical"] {
+ width: 1.5em;
+}
+
+separator.thin:not([orient="vertical"]) {
+ height: 0.5em;
+}
+separator.thin[orient="vertical"] {
+ width: 0.5em;
+}
+
+separator.groove:not([orient="vertical"]) {
+ border-top: 1px solid #A3A3A3;
+ height: 0;
+ margin-top: 0.4em;
+ margin-bottom: 0.4em;
+}
+separator.groove[orient="vertical"] {
+ border-left: 1px solid #A3A3A3;
+ width: 0;
+ margin-left: 0.4em;
+ margin-right: 0.4em;
+}
+
+.plain {
+ -moz-appearance: none;
+ margin: 0 !important;
+ border: none;
+ padding: 0;
+}
+
+description,
+label {
+ cursor: default;
+ margin-top: 1px;
+ margin-bottom: 2px;
+ margin-inline-start: 6px;
+ margin-inline-end: 5px;
+}
+
+description {
+ margin-bottom: 4px;
+}
+
+label[disabled="true"] {
+ color: GrayText;
+}
+
+.tooltip-label {
+ margin: 0;
+}
+
+.header {
+ font-weight: bold;
+}
+
+.monospace {
+ font-family: monospace;
+}
+
+.indent {
+ margin-inline-start: 23px;
+}
+
+.box-padded {
+ padding: 5px;
+}
+
+.spaced {
+ margin: 3px 5px 4px;
+}
+
+.wizard-box {
+ padding: 20px 44px 10px;
+}
+
+.text-link {
+ color: -moz-nativehyperlinktext;
+ cursor: pointer;
+}
+
+.text-link:hover {
+ text-decoration: underline;
+}
+
+.text-link:-moz-focusring {
+ box-shadow: @focusRingShadow@;
+}
+
+.toolbar-focustarget {
+ -moz-user-focus: ignore !important;
+}
+
+notification > button {
+ margin: 0 3px;
+ padding: 1px 10px;
+ min-width: 60px;
+ min-height: 16px;
+ -moz-appearance: none;
+ border-radius: 10000px;
+ border: @roundButtonBorder@;
+ text-shadow: @loweredShadow@;
+ background: @roundButtonBackground@;
+ box-shadow: @roundButtonShadow@;
+}
+
+notification > button:active:hover {
+ background: @roundButtonPressedBackground@;
+ box-shadow: @roundButtonPressedShadow@;
+}
+
+notification > button:-moz-focusring {
+ box-shadow: @focusRingShadow@, @roundButtonShadow@;
+}
+
+notification > button:active:hover:-moz-focusring {
+ box-shadow: @focusRingShadow@, @roundButtonPressedShadow@;
+}
+
+notification > button > .button-box > .button-text {
+ margin: 0 !important;
+}
+
+popupnotificationcontent {
+ margin-top: .5em;
+}
+
+/* :::::: autoscroll popup ::::: */
+
+.autoscroller {
+ height: 28px;
+ width: 28px;
+ border: none;
+ margin: -14px;
+ padding: 0;
+ background-image: url("chrome://global/skin/icons/autoscroll.png");
+ background-color: transparent;
+ background-position: right top;
+ -moz-appearance: none;
+ -moz-window-shadow: none;
+}
+
+.autoscroller[scrolldir="NS"] {
+ background-position: right center;
+}
+
+.autoscroller[scrolldir="EW"] {
+ background-position: right bottom;
+}
+
+/* autorepeatbuttons in menus */
+
+.popup-internal-box > autorepeatbutton {
+ height: 15px;
+ position: relative;
+ list-style-image: none;
+ /* Here we're using a little magic.
+ * The arrow button is supposed to overlay the scrollbox, blocking
+ * everything under it from reaching the screen. However, the menu background
+ * is slightly transparent, so how can we block something completely without
+ * messing up the transparency? It's easy: The native theming of the
+ * "menuitem" appearance uses CGContextClearRect before drawing, which
+ * clears everything under it.
+ * Without help from native theming this effect wouldn't be achievable.
+ */
+ -moz-appearance: menuitem;
+}
+
+.popup-internal-box > .autorepeatbutton-up {
+ padding-top: 1px; /* 4px padding-top from the .popup-internal-box. */
+ margin-bottom: -15px;
+}
+
+.popup-internal-box > .autorepeatbutton-up > .autorepeatbutton-icon {
+ -moz-appearance: button-arrow-up;
+}
+
+.popup-internal-box > .autorepeatbutton-down {
+ padding-top: 5px;
+ margin-top: -15px;
+}
+
+.popup-internal-box > .autorepeatbutton-down > .autorepeatbutton-icon {
+ -moz-appearance: button-arrow-down;
+}
+
+.popup-internal-box > autorepeatbutton[disabled="true"] {
+ visibility: collapse;
+}
+
+/* :::::: Close button icons ::::: */
+
+.close-icon {
+ list-style-image: url("chrome://global/skin/icons/close.png");
+ -moz-image-region: rect(0, 16px, 16px, 0);
+}
+
+.close-icon:hover {
+ -moz-image-region: rect(0, 32px, 16px, 16px);
+}
+
+.close-icon:hover:active {
+ -moz-image-region: rect(0, 48px, 16px, 32px);
+}
+
+@media (min-resolution: 2dppx) {
+ .close-icon > .button-icon,
+ .close-icon > .button-box > .button-icon,
+ .close-icon > .toolbarbutton-icon {
+ width: 16px;
+ }
+
+ .close-icon {
+ list-style-image: url("chrome://global/skin/icons/close@2x.png");
+ -moz-image-region: rect(0, 32px, 32px, 0);
+ }
+
+ .close-icon:hover {
+ -moz-image-region: rect(0, 64px, 32px, 32px);
+ }
+
+ .close-icon:hover:active {
+ -moz-image-region: rect(0, 96px, 32px, 64px);
+ }
+}
diff --git a/toolkit/themes/osx/global/groupbox.css b/toolkit/themes/osx/global/groupbox.css
new file mode 100644
index 0000000000..8406458278
--- /dev/null
+++ b/toolkit/themes/osx/global/groupbox.css
@@ -0,0 +1,30 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+groupbox {
+ padding: 5px 1px 1px;
+ padding-inline-start: 0;
+ margin: 6px;
+}
+
+.groupbox-body {
+ -moz-appearance: groupbox;
+ padding: 8px 8px 3px;
+ margin: 0;
+}
+
+caption {
+ padding-inline-start: 4px;
+ padding-bottom: 1px;
+ font: caption;
+}
+
+/* !important is needed to override label in global.css */
+.caption-text {
+ margin-top: 0 !important;
+ margin-bottom: 0 !important;
+ margin-inline-start: 1px !important;
+}
diff --git a/toolkit/themes/osx/global/icons/Error.png b/toolkit/themes/osx/global/icons/Error.png
new file mode 100644
index 0000000000..424ebfd4ad
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/Error.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/autocomplete-dropmarker.png b/toolkit/themes/osx/global/icons/autocomplete-dropmarker.png
new file mode 100644
index 0000000000..e48d044526
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/autocomplete-dropmarker.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/autocomplete-search.svg b/toolkit/themes/osx/global/icons/autocomplete-search.svg
new file mode 100644
index 0000000000..3d1795d29d
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/autocomplete-search.svg
@@ -0,0 +1,22 @@
+<?xml version="1.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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 16 16">
+ <style>
+ use:not(:target) {
+ display: none;
+ }
+ use {
+ fill: GrayText;
+ }
+ use[id$="-inverted"] {
+ fill: highlighttext;
+ }
+ </style>
+ <defs>
+ <path id="search" fill-rule="evenodd" d="M14.517,12.884l-3.279-3.287c0.545-0.851,0.864-1.861,0.864-2.947 c0-3.022-2.444-5.472-5.458-5.472c-3.014,0-5.458,2.45-5.458,5.472c0,3.022,2.444,5.471,5.458,5.471 c1.093,0,2.108-0.325,2.962-0.88l3.275,3.283c0.396,0.397,1.039,0.397,1.435,0l0.202-0.202 C14.913,13.925,14.913,13.281,14.517,12.884z M6.644,10.001c-1.846,0-3.344-1.501-3.344-3.352c0-1.851,1.497-3.352,3.344-3.352 c1.847,0,3.344,1.501,3.344,3.352C9.987,8.501,8.49,10.001,6.644,10.001z"/>
+ </defs>
+ <use id="search-icon" xlink:href="#search"/>
+ <use id="search-icon-inverted" xlink:href="#search"/>
+</svg>
diff --git a/toolkit/themes/osx/global/icons/autoscroll.png b/toolkit/themes/osx/global/icons/autoscroll.png
new file mode 100644
index 0000000000..c21e067d9a
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/autoscroll.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/blacklist_64.png b/toolkit/themes/osx/global/icons/blacklist_64.png
new file mode 100644
index 0000000000..90555a93bf
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/blacklist_64.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/blacklist_favicon.png b/toolkit/themes/osx/global/icons/blacklist_favicon.png
new file mode 100644
index 0000000000..e67d3d34f0
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/blacklist_favicon.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/checkbox.png b/toolkit/themes/osx/global/icons/checkbox.png
new file mode 100644
index 0000000000..137e4e801f
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/checkbox.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/checkbox@2x.png b/toolkit/themes/osx/global/icons/checkbox@2x.png
new file mode 100644
index 0000000000..61bcc59c7d
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/checkbox@2x.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/chevron-inverted.png b/toolkit/themes/osx/global/icons/chevron-inverted.png
new file mode 100644
index 0000000000..8ad164baaf
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/chevron-inverted.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/chevron-inverted@2x.png b/toolkit/themes/osx/global/icons/chevron-inverted@2x.png
new file mode 100644
index 0000000000..4327a1a457
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/chevron-inverted@2x.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/chevron.png b/toolkit/themes/osx/global/icons/chevron.png
new file mode 100644
index 0000000000..b2d31e38f5
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/chevron.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/chevron@2x.png b/toolkit/themes/osx/global/icons/chevron@2x.png
new file mode 100644
index 0000000000..dd91178030
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/chevron@2x.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/close.png b/toolkit/themes/osx/global/icons/close.png
new file mode 100644
index 0000000000..9bba044ce5
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/close.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/close@2x.png b/toolkit/themes/osx/global/icons/close@2x.png
new file mode 100755
index 0000000000..01c5ef4232
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/close@2x.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/error-16.png b/toolkit/themes/osx/global/icons/error-16.png
new file mode 100644
index 0000000000..41514d0806
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/error-16.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/error-64.png b/toolkit/themes/osx/global/icons/error-64.png
new file mode 100644
index 0000000000..972abaff3b
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/error-64.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/error-large.png b/toolkit/themes/osx/global/icons/error-large.png
new file mode 100644
index 0000000000..5a1479e28b
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/error-large.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/glyph-dropdown.png b/toolkit/themes/osx/global/icons/glyph-dropdown.png
new file mode 100644
index 0000000000..fa08515836
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/glyph-dropdown.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/glyph-dropdown@2x.png b/toolkit/themes/osx/global/icons/glyph-dropdown@2x.png
new file mode 100644
index 0000000000..653039a3e6
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/glyph-dropdown@2x.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/information-16.png b/toolkit/themes/osx/global/icons/information-16.png
new file mode 100644
index 0000000000..6fb2e3a804
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/information-16.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/information-24.png b/toolkit/themes/osx/global/icons/information-24.png
new file mode 100644
index 0000000000..6907d02e5f
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/information-24.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/information-32.png b/toolkit/themes/osx/global/icons/information-32.png
new file mode 100644
index 0000000000..4501bc813a
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/information-32.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/information-64.png b/toolkit/themes/osx/global/icons/information-64.png
new file mode 100644
index 0000000000..8d9b72498e
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/information-64.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/information-large.png b/toolkit/themes/osx/global/icons/information-large.png
new file mode 100644
index 0000000000..3912f1c79a
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/information-large.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/loading_16.png b/toolkit/themes/osx/global/icons/loading_16.png
new file mode 100644
index 0000000000..1b2df8093d
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/loading_16.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/menulist-dropmarker.png b/toolkit/themes/osx/global/icons/menulist-dropmarker.png
new file mode 100644
index 0000000000..689a1fe877
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/menulist-dropmarker.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/notfound.png b/toolkit/themes/osx/global/icons/notfound.png
new file mode 100644
index 0000000000..694dae910e
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/notfound.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/notloading_16.png b/toolkit/themes/osx/global/icons/notloading_16.png
new file mode 100644
index 0000000000..ece0ee18a1
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/notloading_16.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/panebutton-active.png b/toolkit/themes/osx/global/icons/panebutton-active.png
new file mode 100644
index 0000000000..ca241c7b8a
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/panebutton-active.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/panebutton-inactive.png b/toolkit/themes/osx/global/icons/panebutton-inactive.png
new file mode 100644
index 0000000000..de527b6627
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/panebutton-inactive.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/panel-dropmarker.png b/toolkit/themes/osx/global/icons/panel-dropmarker.png
new file mode 100644
index 0000000000..e605e835c7
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/panel-dropmarker.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/question-16.png b/toolkit/themes/osx/global/icons/question-16.png
new file mode 100644
index 0000000000..8d8311fced
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/question-16.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/question-32.png b/toolkit/themes/osx/global/icons/question-32.png
new file mode 100644
index 0000000000..7c80831b04
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/question-32.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/question-64.png b/toolkit/themes/osx/global/icons/question-64.png
new file mode 100644
index 0000000000..96fd746409
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/question-64.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/question-large.png b/toolkit/themes/osx/global/icons/question-large.png
new file mode 100644
index 0000000000..dd2b21874b
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/question-large.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/resizer-rtl.png b/toolkit/themes/osx/global/icons/resizer-rtl.png
new file mode 100644
index 0000000000..6ab7d33457
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/resizer-rtl.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/resizer-rtl@2x.png b/toolkit/themes/osx/global/icons/resizer-rtl@2x.png
new file mode 100644
index 0000000000..6c85d4f332
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/resizer-rtl@2x.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/resizer.png b/toolkit/themes/osx/global/icons/resizer.png
new file mode 100644
index 0000000000..efed0240b8
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/resizer.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/resizer@2x.png b/toolkit/themes/osx/global/icons/resizer@2x.png
new file mode 100644
index 0000000000..2304cc65fe
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/resizer@2x.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/search-textbox.svg b/toolkit/themes/osx/global/icons/search-textbox.svg
new file mode 100644
index 0000000000..12be833c40
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/search-textbox.svg
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
+ <style>
+ path {
+ fill: #4c4c4c;
+ fill-rule: evenodd;
+ }
+ </style>
+ <path id="glyph-search" d="M11.354,10.646l-0.707.707L7.295,8A4.483,4.483,0,1,1,9,4.5,4.458,4.458,0,0,1,8,7.295ZM4.5,1A3.5,3.5,0,1,0,8,4.5,3.5,3.5,0,0,0,4.5,1Z"/>
+</svg>
diff --git a/toolkit/themes/osx/global/icons/searchfield-cancel.svg b/toolkit/themes/osx/global/icons/searchfield-cancel.svg
new file mode 100644
index 0000000000..9899144e95
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/searchfield-cancel.svg
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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/. -->
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="14" height="14" viewBox="0 0 14 14">
+ <style>
+ circle {
+ fill: #808080;
+ }
+
+ line {
+ stroke: #fff;
+ stroke-width: 1.5px;
+ }
+ </style>
+
+ <circle cx="7" cy="7" r="7" />
+ <line x1="4" y1="4" x2="10" y2="10" />
+ <line x1="10" y1="4" x2="4" y2="10" />
+</svg> \ No newline at end of file
diff --git a/toolkit/themes/osx/global/icons/sslWarning.png b/toolkit/themes/osx/global/icons/sslWarning.png
new file mode 100644
index 0000000000..e8ad586b6e
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/sslWarning.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/tabprompts-bgtexture.png b/toolkit/themes/osx/global/icons/tabprompts-bgtexture.png
new file mode 100644
index 0000000000..caffc241cf
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/tabprompts-bgtexture.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/warning-16.png b/toolkit/themes/osx/global/icons/warning-16.png
new file mode 100644
index 0000000000..2ab4b3915e
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/warning-16.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/warning-32.png b/toolkit/themes/osx/global/icons/warning-32.png
new file mode 100644
index 0000000000..750abaa220
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/warning-32.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/warning-64.png b/toolkit/themes/osx/global/icons/warning-64.png
new file mode 100644
index 0000000000..37d2120538
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/warning-64.png
Binary files differ
diff --git a/toolkit/themes/osx/global/icons/warning-large.png b/toolkit/themes/osx/global/icons/warning-large.png
new file mode 100644
index 0000000000..73fd65f6fa
--- /dev/null
+++ b/toolkit/themes/osx/global/icons/warning-large.png
Binary files differ
diff --git a/toolkit/themes/osx/global/in-content/common.css b/toolkit/themes/osx/global/in-content/common.css
new file mode 100644
index 0000000000..a987cbfe1f
--- /dev/null
+++ b/toolkit/themes/osx/global/in-content/common.css
@@ -0,0 +1,121 @@
+/* - 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 ../shared.inc
+%include ../../../shared/in-content/common.inc.css
+
+xul|tabs {
+ padding-right: 0;
+ padding-left: 0;
+}
+
+xul|tab[visuallyselected] {
+ text-shadow: none;
+}
+
+xul|button,
+html|button,
+xul|colorpicker[type="button"],
+xul|menulist {
+ margin-top: 3px;
+}
+
+xul|button,
+html|button {
+ /* use the same margin of other elements for the alignment */
+ margin-left: 4px;
+ margin-right: 4px;
+}
+
+xul|caption {
+ padding-inline-start: 0;
+}
+
+xul|groupbox > xul|*.groupbox-body {
+ padding: 0;
+}
+
+xul|menulist:not([editable="true"]) > xul|menupopup > xul|menuitem[checked="true"]::before,
+xul|menulist:not([editable="true"]) > xul|menupopup > xul|menuitem[selected="true"]::before {
+ display: none;
+}
+
+xul|menulist:not([editable="true"]) > xul|*.menulist-dropmarker {
+ display: -moz-box;
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+
+xul|menulist > xul|menupopup xul|menu,
+xul|menulist > xul|menupopup xul|menuitem,
+xul|button[type="menu"] > xul|menupopup xul|menu,
+xul|button[type="menu"] > xul|menupopup xul|menuitem {
+ padding-inline-end: 34px;
+}
+
+xul|*.help-button > xul|*.button-box > xul|*.button-icon {
+ margin-inline-start: 0;
+}
+
+xul|*.checkbox-icon {
+ margin-right: 0;
+}
+
+xul|*.radio-icon {
+ margin-inline-end: 0;
+}
+
+xul|*.numberbox-input-box {
+ -moz-appearance: none;
+ border-width: 0;
+}
+
+xul|description {
+ font-size: 1.25rem;
+ line-height: 22px;
+}
+
+xul|*.text-link:-moz-focusring {
+ color: var(--in-content-link-highlight);
+ text-decoration: underline;
+ box-shadow: none;
+}
+
+xul|button:-moz-focusring,
+xul|menulist:-moz-focusring,
+xul|checkbox:-moz-focusring > .checkbox-check,
+html|input[type="checkbox"]:-moz-focusring + html|label:before,
+xul|radio[focused="true"] > .radio-check,
+xul|tab:-moz-focusring > .tab-middle > .tab-text {
+ outline: 2px solid rgba(0,149,221,0.5);
+ outline-offset: 1px;
+ -moz-outline-radius: 2px;
+}
+
+xul|radio[focused="true"] > .radio-check {
+ -moz-outline-radius: 100%;
+}
+
+xul|spinbuttons {
+ -moz-appearance: none;
+}
+
+xul|*.spinbuttons-up {
+ margin-top: 0 !important;
+ border-radius: 4px 4px 0 0;
+}
+
+xul|*.spinbuttons-down {
+ margin-bottom: 0 !important;
+ border-radius: 0 0 4px 4px;
+}
+
+xul|*.spinbuttons-button > xul|*.button-box {
+ padding-inline-start: 2px !important;
+ padding-inline-end: 3px !important;
+}
+
+xul|*.spinbuttons-button > xul|*.button-box > xul|*.button-text {
+ display: none;
+}
diff --git a/toolkit/themes/osx/global/in-content/info-pages.css b/toolkit/themes/osx/global/in-content/info-pages.css
new file mode 100644
index 0000000000..a25b9f6a3d
--- /dev/null
+++ b/toolkit/themes/osx/global/in-content/info-pages.css
@@ -0,0 +1 @@
+%include ../../../shared/in-content/info-pages.inc.css \ No newline at end of file
diff --git a/toolkit/themes/osx/global/inContentUI.css b/toolkit/themes/osx/global/inContentUI.css
new file mode 100644
index 0000000000..17e2e6ae33
--- /dev/null
+++ b/toolkit/themes/osx/global/inContentUI.css
@@ -0,0 +1,144 @@
+/* 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 shared.inc
+
+/*
+ * The default namespace for this file is XUL. Be sure to prefix rules that
+ * are applicable to both XUL and HTML with '*|'.
+ */
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+/* Page background */
+*|*:root {
+ -moz-appearance: none;
+ padding: 18px;
+ background-image: /* Texture */
+ url("chrome://global/skin/inContentUI/background-texture.png"),
+ /* Gradient */
+ linear-gradient(#ADB5C2, #BFC6D1);
+}
+
+/* Use the new in-content colors for #contentAreaDownloadsView. After landing
+ of bug 989469 the colors can be moved to *|*:root */
+*|*#contentAreaDownloadsView {
+ background: #f1f1f1;
+ color: #424e5a;
+}
+
+html|html {
+ font: message-box;
+}
+
+/* Content */
+*|*.main-content {
+ /* Needed to allow the radius to clip the inner content, see bug 595656 */
+ overflow: hidden;
+ background-image: linear-gradient(rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.25) 50%, rgba(255, 255, 255, 0.05));
+ border: 1px solid rgba(50, 65, 92, 0.4);
+ border-radius: 5px;
+}
+
+/* Buttons */
+*|button,
+menulist,
+colorpicker[type="button"] {
+ -moz-appearance: none;
+ padding: 1px 4px;
+ min-width: 60px;
+ border-radius: 3px;
+ border: 1px solid rgba(60,73,97,0.5);
+ box-shadow: inset 0 1px rgba(255,255,255,0.25), 0 1px rgba(255,255,255,0.25);
+ background-color: transparent;
+ background-image: linear-gradient(rgba(255,255,255,0.45), rgba(255,255,255,0.2));
+ background-clip: padding-box;
+ color: #252F3B;
+ text-shadow: @loweredShadow@;
+}
+
+button:-moz-focusring > .button-box,
+menulist:-moz-focusring:not([open="true"]) > .menulist-label-box,
+colorpicker[type="button"]:-moz-focusring:not([open="true"]) > .colorpicker-button-colorbox {
+ outline: 1px dotted #252F3B;
+}
+
+html|button[disabled],
+button[disabled="true"],
+menulist[disabled="true"],
+colorpicker[type="button"][disabled="true"] {
+ opacity: 0.8;
+ color: #505050;
+}
+
+html|button:not([disabled]):active:hover,
+button:not([disabled="true"]):active:hover,
+menulist[open="true"]:not([disabled="true"]),
+colorpicker[type="button"][open="true"]:not([disabled="true"]) {
+ box-shadow: inset 0 1px 3px rgba(0,0,0,.2), 0 1px rgba(255,255,255,0.25);
+ background-image: linear-gradient(rgba(45,54,71,0.3), rgba(45,54,71,0.1));
+ border-color: rgba(60,73,97,0.7);
+}
+
+menulist {
+ -moz-padding-end: 0;
+ margin-left: 5px;
+ margin-right: 5px;
+}
+
+/* Tweak margins so the focus ring is in the right place. */
+menulist > .menulist-label-box {
+ -moz-margin-end: 3px;
+ margin-top: 1px;
+}
+
+menulist > .menulist-label-box > .menulist-label {
+ margin-top: 0px !important;
+ margin-bottom: 0px !important;
+}
+
+menulist > .menulist-dropmarker {
+ -moz-appearance: none;
+ display: -moz-box;
+ background: transparent;
+ border: none;
+ -moz-border-start: 1px solid rgba(60,73,97,0.5);
+ margin-top: -1px;
+ margin-bottom: -1px;
+}
+
+colorpicker[type="button"] {
+ margin: 1px 5px 2px 5px;
+ padding: 3px;
+ height: 25px;
+}
+
+spinbuttons {
+ -moz-appearance: none;
+}
+
+spinbuttons > .spinbuttons-box > .spinbuttons-button {
+ min-width: 12px;
+}
+
+.spinbuttons-button > .button-box > .button-text {
+ display: none;
+}
+
+.spinbuttons-button[disabled="true"] > .button-box > .button-icon {
+ opacity: 0.5;
+}
+
+spinbuttons > .spinbuttons-box > .spinbuttons-up {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+ border-bottom-width: 0;
+ border-bottom-left-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+spinbuttons > .spinbuttons-box > .spinbuttons-down {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+ border-top-left-radius: 0;
+ border-top-right-radius: 0;
+}
diff --git a/toolkit/themes/osx/global/jar.mn b/toolkit/themes/osx/global/jar.mn
new file mode 100644
index 0000000000..9ca73cc6bc
--- /dev/null
+++ b/toolkit/themes/osx/global/jar.mn
@@ -0,0 +1,156 @@
+# 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 ../../shared/jar.inc.mn
+
+toolkit.jar:
+ skin/classic/global/10pct_transparent_grey.png
+ skin/classic/global/50pct_transparent_grey.png
+ skin/classic/global/arrow.css
+ skin/classic/global/autocomplete.css
+ skin/classic/global/button.css
+ skin/classic/global/checkbox.css
+ skin/classic/global/colorpicker.css
+ skin/classic/global/commonDialog.css
+ skin/classic/global/customizeToolbar.css
+ skin/classic/global/dialog.css
+ skin/classic/global/dropmarker.css
+ skin/classic/global/filefield.css
+ skin/classic/global/filters.svg
+* skin/classic/global/findBar.css
+* skin/classic/global/global.css
+ skin/classic/global/groupbox.css
+* skin/classic/global/inContentUI.css
+ skin/classic/global/linkTree.css
+ skin/classic/global/listbox.css
+ skin/classic/global/menu.css
+ skin/classic/global/menulist.css
+* skin/classic/global/notification.css
+ skin/classic/global/netError.css
+ skin/classic/global/numberbox.css
+ skin/classic/global/popup.css
+ skin/classic/global/preferences.css
+ skin/classic/global/progressmeter.css
+ skin/classic/global/radio.css
+ skin/classic/global/resizer.css
+ skin/classic/global/richlistbox.css
+ skin/classic/global/scrollbars.css (nativescrollbars.css)
+ skin/classic/global/scale.css
+ skin/classic/global/scrollbox.css
+ skin/classic/global/spinbuttons.css
+ skin/classic/global/splitter.css
+ skin/classic/global/tabprompts.css
+ skin/classic/global/tabbox.css
+ skin/classic/global/textbox.css
+ skin/classic/global/datetimepicker.css
+* skin/classic/global/toolbar.css
+ skin/classic/global/toolbarbutton.css
+* skin/classic/global/tree.css
+* skin/classic/global/viewbuttons.css
+ skin/classic/global/wizard.css
+ skin/classic/global/alerts/alert.css (alerts/alert.css)
+ skin/classic/global/arrow/arrow-dn-dis.gif (arrow/arrow-dn-dis.gif)
+ skin/classic/global/arrow/arrow-dn-dis.png (arrow/arrow-dn-dis.png)
+ skin/classic/global/arrow/arrow-dn-sharp.gif (arrow/arrow-dn-sharp.gif)
+ skin/classic/global/arrow/arrow-dn.gif (arrow/arrow-dn.gif)
+ skin/classic/global/arrow/arrow-dn.png (arrow/arrow-dn.png)
+ skin/classic/global/arrow/arrow-lft-dis.gif (arrow/arrow-lft-dis.gif)
+ skin/classic/global/arrow/arrow-lft-hov.gif (arrow/arrow-lft-hov.gif)
+ skin/classic/global/arrow/arrow-lft-sharp-end.gif (arrow/arrow-lft-sharp-end.gif)
+ skin/classic/global/arrow/arrow-lft-sharp.gif (arrow/arrow-lft-sharp.gif)
+ skin/classic/global/arrow/arrow-lft.gif (arrow/arrow-lft.gif)
+ skin/classic/global/arrow/arrow-rit-dis.gif (arrow/arrow-rit-dis.gif)
+ skin/classic/global/arrow/arrow-rit-hov.gif (arrow/arrow-rit-hov.gif)
+ skin/classic/global/arrow/arrow-rit-sharp-end.gif (arrow/arrow-rit-sharp-end.gif)
+ skin/classic/global/arrow/arrow-rit-sharp.gif (arrow/arrow-rit-sharp.gif)
+ skin/classic/global/arrow/arrow-rit.gif (arrow/arrow-rit.gif)
+ skin/classic/global/arrow/arrow-up-dis.gif (arrow/arrow-up-dis.gif)
+ skin/classic/global/arrow/arrow-up-sharp.gif (arrow/arrow-up-sharp.gif)
+ skin/classic/global/arrow/arrow-up.gif (arrow/arrow-up.gif)
+ skin/classic/global/arrow/panelarrow-horizontal.png (arrow/panelarrow-horizontal.png)
+ skin/classic/global/arrow/panelarrow-horizontal@2x.png (arrow/panelarrow-horizontal@2x.png)
+ skin/classic/global/arrow/panelarrow-vertical.png (arrow/panelarrow-vertical.png)
+ skin/classic/global/arrow/panelarrow-vertical@2x.png (arrow/panelarrow-vertical@2x.png)
+ skin/classic/global/checkbox/cbox-check.gif (checkbox/cbox-check.gif)
+ skin/classic/global/checkbox/cbox-check-dis.gif (checkbox/cbox-check-dis.gif)
+ skin/classic/global/console/console-error-caret.gif (console/console-error-caret.gif)
+ skin/classic/global/console/console-error-dash.gif (console/console-error-dash.gif)
+* skin/classic/global/console/console.css (console/console.css)
+ skin/classic/global/dirListing/dirListing.css (dirListing/dirListing.css)
+ skin/classic/global/dirListing/folder.png (dirListing/folder.png)
+ skin/classic/global/dirListing/remote.png (dirListing/remote.png)
+ skin/classic/global/dirListing/up.png (dirListing/up.png)
+ skin/classic/global/icons/autocomplete-dropmarker.png (icons/autocomplete-dropmarker.png)
+ skin/classic/global/icons/autocomplete-search.svg (icons/autocomplete-search.svg)
+ skin/classic/global/icons/autoscroll.png (icons/autoscroll.png)
+ skin/classic/global/icons/blacklist_favicon.png (icons/blacklist_favicon.png)
+ skin/classic/global/icons/blacklist_64.png (icons/blacklist_64.png)
+ skin/classic/global/icons/chevron.png (icons/chevron.png)
+ skin/classic/global/icons/chevron-inverted.png (icons/chevron-inverted.png)
+ skin/classic/global/icons/chevron@2x.png (icons/chevron@2x.png)
+ skin/classic/global/icons/chevron-inverted@2x.png (icons/chevron-inverted@2x.png)
+ skin/classic/global/icons/checkbox.png (icons/checkbox.png)
+ skin/classic/global/icons/checkbox@2x.png (icons/checkbox@2x.png)
+ skin/classic/global/icons/close.png (icons/close.png)
+ skin/classic/global/icons/close@2x.png (icons/close@2x.png)
+ skin/classic/global/icons/glyph-dropdown.png (icons/glyph-dropdown.png)
+ skin/classic/global/icons/glyph-dropdown@2x.png (icons/glyph-dropdown@2x.png)
+ skin/classic/global/icons/information-16.png (icons/information-16.png)
+ skin/classic/global/icons/information-24.png (icons/information-24.png)
+ skin/classic/global/icons/information-32.png (icons/information-32.png)
+ skin/classic/global/icons/information-64.png (icons/information-64.png)
+ skin/classic/global/icons/information-large.png (icons/information-large.png)
+ skin/classic/global/icons/loading_16.png (icons/loading_16.png)
+ skin/classic/global/icons/menulist-dropmarker.png (icons/menulist-dropmarker.png)
+ skin/classic/global/icons/notfound.png (icons/notfound.png)
+ skin/classic/global/icons/notloading_16.png (icons/notloading_16.png)
+ skin/classic/global/icons/panebutton-active.png (icons/panebutton-active.png)
+ skin/classic/global/icons/panebutton-inactive.png (icons/panebutton-inactive.png)
+ skin/classic/global/icons/panel-dropmarker.png (icons/panel-dropmarker.png)
+ skin/classic/global/icons/resizer.png (icons/resizer.png)
+ skin/classic/global/icons/resizer@2x.png (icons/resizer@2x.png)
+ skin/classic/global/icons/resizer-rtl.png (icons/resizer-rtl.png)
+ skin/classic/global/icons/resizer-rtl@2x.png (icons/resizer-rtl@2x.png)
+ skin/classic/global/icons/search-textbox.svg (icons/search-textbox.svg)
+ skin/classic/global/icons/searchfield-cancel.svg (icons/searchfield-cancel.svg)
+ skin/classic/global/icons/tabprompts-bgtexture.png (icons/tabprompts-bgtexture.png)
+ skin/classic/global/icons/warning-16.png (icons/warning-16.png)
+ skin/classic/global/icons/warning-32.png (icons/warning-32.png)
+ skin/classic/global/icons/warning-64.png (icons/warning-64.png)
+ skin/classic/global/icons/warning-large.png (icons/warning-large.png)
+ skin/classic/global/icons/error-16.png (icons/error-16.png)
+ skin/classic/global/icons/error-64.png (icons/error-64.png)
+ skin/classic/global/icons/error-large.png (icons/error-large.png)
+ skin/classic/global/icons/Error.png (icons/Error.png)
+ skin/classic/global/icons/question-16.png (icons/question-16.png)
+ skin/classic/global/icons/question-32.png (icons/question-32.png)
+ skin/classic/global/icons/question-64.png (icons/question-64.png)
+ skin/classic/global/icons/question-large.png (icons/question-large.png)
+ skin/classic/global/icons/sslWarning.png (icons/sslWarning.png)
+ skin/classic/global/notification/close.png (notification/close.png)
+ skin/classic/global/notification/error-icon.png (notification/error-icon.png)
+ skin/classic/global/notification/info-icon.png (notification/info-icon.png)
+ skin/classic/global/notification/warning-icon.png (notification/warning-icon.png)
+* skin/classic/global/in-content/common.css (in-content/common.css)
+* skin/classic/global/in-content/info-pages.css (in-content/info-pages.css)
+ skin/classic/global/scale/scale-tray-horiz.gif (scale/scale-tray-horiz.gif)
+ skin/classic/global/scale/scale-tray-vert.gif (scale/scale-tray-vert.gif)
+ skin/classic/global/splitter/dimple.png (splitter/dimple.png)
+ skin/classic/global/splitter/grip-bottom.gif (splitter/grip-bottom.gif)
+ skin/classic/global/splitter/grip-top.gif (splitter/grip-top.gif)
+ skin/classic/global/splitter/grip-left.gif (splitter/grip-left.gif)
+ skin/classic/global/splitter/grip-right.gif (splitter/grip-right.gif)
+ skin/classic/global/toolbar/spring.png (toolbar/spring.png)
+ skin/classic/global/toolbar/toolbar-separator.png (toolbar/toolbar-separator.png)
+ skin/classic/global/tree/arrow-disclosure.svg (tree/arrow-disclosure.svg)
+ skin/classic/global/tree/columnpicker.gif (tree/columnpicker.gif)
+ skin/classic/global/tree/folder.png (tree/folder.png)
+ skin/classic/global/tree/folder@2x.png (tree/folder@2x.png)
+
+#ifdef MOZ_PHOENIX
+[browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
+#elif MOZ_SEPARATE_MANIFEST_FOR_THEME_OVERRIDES
+[extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
+#endif
+% override chrome://global/skin/dirListing/local.png chrome://global/skin/dirListing/folder.png
diff --git a/toolkit/themes/osx/global/linkTree.css b/toolkit/themes/osx/global/linkTree.css
new file mode 100644
index 0000000000..d83c5bfd9d
--- /dev/null
+++ b/toolkit/themes/osx/global/linkTree.css
@@ -0,0 +1,32 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/**
+ * All the properties in this rule are important to avoid having to create
+ * a special type of tree. This stylesheet can be loaded into a document with
+ * a single tree that is a link tree. Hardly elegant but it's efficient.
+ */
+treeitem[selected="true"] > treerow
+ {
+ background : transparent !important;
+ border : none !important;
+ color : -moz-FieldText !important;
+ }
+
+treecell:hover
+ {
+ text-decoration : underline !important;
+ color : #000080 !important;
+ cursor : pointer;
+ }
+
+treecell:hover:active
+ {
+ text-decoration : underline !important;
+ color : red !important;
+ }
+
+
diff --git a/toolkit/themes/osx/global/listbox.css b/toolkit/themes/osx/global/listbox.css
new file mode 100644
index 0000000000..90928c7699
--- /dev/null
+++ b/toolkit/themes/osx/global/listbox.css
@@ -0,0 +1,113 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+listbox {
+ -moz-appearance: listbox;
+ margin: 2px 4px;
+ background-color: #FFFFFF;
+ color: -moz-FieldText;
+}
+
+.listcell-label {
+ margin: 0px !important;
+ padding-bottom: 1px;
+ padding-inline-start: 4px;
+ white-space: nowrap;
+}
+
+/* ::::: listitem ::::: */
+
+listitem {
+ border: 1px solid transparent;
+}
+
+listitem[selected="true"] {
+ background-color: -moz-mac-secondaryhighlight;
+ color: -moz-DialogText;
+}
+
+listbox:focus > listitem[selected="true"] {
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+/* ::::: listheader ::::: */
+
+listheader {
+ -moz-appearance: treeheadercell;
+ -moz-box-align: center;
+ border: 2px solid;
+ -moz-border-top-colors: ThreeDHighlight ThreeDLightShadow;
+ -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-bottom-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-left-colors: ThreeDHighlight ThreeDLightShadow;
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+ padding: 0 4px;
+}
+
+listheader[sortable="true"]:hover:active {
+ border-top: 2px solid;
+ border-right: 1px solid;
+ border-bottom: 1px solid;
+ border-left: 2px solid;
+ -moz-border-top-colors: ThreeDShadow -moz-Dialog;
+ -moz-border-right-colors: ThreeDShadow;
+ -moz-border-bottom-colors: ThreeDShadow;
+ -moz-border-left-colors: ThreeDShadow -moz-Dialog;
+ padding-top: 1px;
+ padding-inline-start: 5px;
+ padding-inline-end: 4px;
+}
+
+.listheader-icon {
+ margin-inline-end: 2px;
+}
+
+.listheader-label {
+ margin: 0px !important;
+}
+
+/* ::::: listcell ::::: */
+
+.listcell-label {
+ margin: 0px !important;
+ padding-bottom: 1px;
+ padding-inline-start: 4px;
+ white-space: nowrap;
+}
+
+.listcell-icon {
+ margin-inline-end: 2px;
+}
+
+.listcell-label[disabled="true"] {
+ color: GrayText;
+}
+
+/* ::::: listcell checkbox ::::: */
+
+.listcell-check {
+ -moz-appearance: checkbox;
+ -moz-box-align: center;
+ margin: 0px 2px;
+ border: 1px solid -moz-DialogText;
+ min-width: 13px;
+ min-height: 13px;
+ background: -moz-Field no-repeat 50% 50%;
+}
+
+.listcell-check[checked="true"] {
+ background-image: url("chrome://global/skin/checkbox/cbox-check.gif");
+}
+
+.listcell-check[disabled="true"] {
+ border-color: GrayText;
+}
+
+.listcell-check[disabled="true"][checked="true"] {
+ background-image: url("chrome://global/skin/checkbox/cbox-check-dis.gif");
+}
diff --git a/toolkit/themes/osx/global/menu.css b/toolkit/themes/osx/global/menu.css
new file mode 100644
index 0000000000..49ca168a64
--- /dev/null
+++ b/toolkit/themes/osx/global/menu.css
@@ -0,0 +1,187 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+menu,
+menuitem,
+menucaption {
+ -moz-appearance: menuitem;
+ -moz-box-align: center;
+ color: MenuText;
+ font: -moz-pull-down-menu;
+ list-style-image: none;
+ -moz-image-region: auto;
+ padding: 0 21px 2px;
+}
+
+menu[disabled="true"], menuitem[disabled="true"],
+menu[_moz-menuactive="true"][disabled="true"],
+menuitem[_moz-menuactive="true"][disabled="true"] {
+ color: -moz-mac-menutextdisable;
+}
+
+/* ..... internal content .... */
+
+.menu-text,
+.menu-iconic-text,
+.menu-accel,
+.menu-iconic-accel {
+ margin: 0 !important;
+}
+
+.menu-text,
+.menu-iconic-text {
+ font-weight: inherit;
+ color: inherit;
+}
+
+menucaption > .menu-text,
+menucaption > .menu-iconic-text {
+ font-weight: bold;
+}
+
+.menu-description {
+ font-style: italic;
+ color: -moz-mac-menutextdisable;
+ margin-inline-start: 1ex !important;
+}
+
+.menu-iconic-icon {
+ height: 16px;
+ margin-top: -2px;
+ margin-bottom: -2px;
+ margin-inline-end: 5px;
+ /* Empty icons shouldn't take up room, so we need to compensate
+ * the 5px margin-end with a negative margin-start.
+ */
+ margin-inline-start: -5px;
+}
+
+/* menuitems with icons */
+.menuitem-iconic,
+.menu-iconic,
+menuitem[image],
+menuitem[src] {
+ /* 2px higher than those without icons */
+ padding-top: 1px;
+ padding-bottom: 3px;
+}
+
+.menuitem-iconic > .menu-iconic-left > .menu-iconic-icon,
+.menu-iconic > .menu-iconic-left > .menu-iconic-icon,
+menuitem[image] > .menu-iconic-left > .menu-iconic-icon,
+menuitem[src] > .menu-iconic-left > .menu-iconic-icon {
+ margin-inline-start: 0;
+ width: 16px;
+}
+
+/* ..... menu arrow box ..... */
+
+.menu-right,
+.menu-accel-container {
+ margin-inline-start: 21px;
+ margin-inline-end: -9px;
+ -moz-box-pack: end;
+}
+
+.menu-right {
+ list-style-image: none;
+ -moz-appearance: menuarrow;
+}
+
+/* ::::: menu/menuitems in menubar ::::: */
+
+menubar > menu {
+ -moz-appearance: none;
+ padding: 2px 5px 2px 7px;
+ margin: 1px 0;
+}
+
+menubar > menu[_moz-menuactive="true"] {
+ color: inherit;
+ background-color: transparent;
+}
+
+menubar > menu[_moz-menuactive="true"][open="true"] {
+ -moz-appearance: menuitem;
+ color: -moz-mac-menutextselect;
+}
+
+/* ..... internal content .... */
+
+.menubar-left {
+ margin: 0 2px;
+ color: inherit;
+}
+
+.menubar-text {
+ margin: 0 1px !important;
+ color: inherit;
+}
+
+/* ::::: menu/menuitems in popups ::::: */
+
+menupopup > menu,
+menupopup > menuitem,
+menupopup > menucaption {
+ max-width: 42em;
+}
+
+menu[_moz-menuactive="true"],
+menuitem[_moz-menuactive="true"] {
+ color: -moz-mac-menutextselect;
+ background-color: Highlight;
+}
+
+/* ::::: menu/menuitems in menulist popups ::::: */
+
+menulist > menupopup > menuitem,
+menulist > menupopup > menucaption,
+menulist > menupopup > menu {
+ max-width: none;
+ font: inherit;
+ color: -moz-FieldText;
+}
+
+/* ::::: menuitems in editable menulist popups ::::: */
+
+menulist[editable="true"] > menupopup > menuitem,
+menulist[editable="true"] > menupopup > menucaption {
+ -moz-appearance: none;
+}
+
+menulist[editable="true"] > menupopup > :-moz-any(menuitem, menucaption) > .menu-iconic-left {
+ display: none;
+}
+
+/* ::::: checked menuitems ::::: */
+
+:not(menulist) > menupopup > menuitem[checked="true"],
+:not(menulist) > menupopup > menuitem[selected="true"] {
+ -moz-appearance: checkmenuitem;
+}
+
+menulist:not([editable="true"]) > menupopup > menuitem[checked="true"]::before,
+menulist:not([editable="true"]) > menupopup > menuitem[selected="true"]::before {
+ content: '\2713'; /* a checkmark */
+ display: block;
+ width: 15px;
+ margin-inline-start: -15px;
+}
+
+/* ::::: menuseparator ::::: */
+
+menuseparator {
+ -moz-appearance: menuseparator;
+ margin: 5px 0;
+ padding: 1px 0;
+}
+
+/* ::::: autocomplete ::::: */
+
+.autocomplete-history-popup > menuitem {
+ max-width: none !important;
+ font: message-box;
+}
diff --git a/toolkit/themes/osx/global/menulist.css b/toolkit/themes/osx/global/menulist.css
new file mode 100644
index 0000000000..a4feca947e
--- /dev/null
+++ b/toolkit/themes/osx/global/menulist.css
@@ -0,0 +1,65 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+menulist {
+ -moz-appearance: menulist;
+ margin: 5px 2px 3px;
+ color: -moz-DialogText;
+ text-shadow: none;
+}
+
+menulist:not([popuponly="true"]) {
+ min-height: 20px;
+}
+
+.menulist-label-box {
+ -moz-appearance: menulist-text;
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ margin-bottom: 1px;
+}
+
+.menulist-label {
+ margin: 1px 3px !important;
+}
+
+.menulist-description {
+ font-style: italic;
+ color: GrayText;
+ margin-inline-start: 1ex !important;
+}
+
+/* ..... dropmarker ..... */
+
+.menulist-dropmarker {
+ display: none;
+}
+
+/* ..... disabled state ..... */
+
+menulist[disabled="true"] {
+ color: GrayText;
+}
+
+menulist[disabled="true"] > .menulist-dropmarker {
+ padding-inline-start: 7px !important;
+}
+
+/* ::::: editable menulists ::::: */
+
+menulist[editable="true"] {
+ -moz-appearance: menulist-textfield;
+ margin: 4px 2px;
+}
+
+html|*.menulist-editable-input {
+ margin: 0px !important;
+ border: none !important;
+ padding: 0px !important;
+ background: inherit;
+ font: inherit;
+}
diff --git a/toolkit/themes/osx/global/moz.build b/toolkit/themes/osx/global/moz.build
new file mode 100644
index 0000000000..635fa39c99
--- /dev/null
+++ b/toolkit/themes/osx/global/moz.build
@@ -0,0 +1,6 @@
+# -*- 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_MANIFESTS += ['jar.mn'] \ No newline at end of file
diff --git a/toolkit/themes/osx/global/nativescrollbars.css b/toolkit/themes/osx/global/nativescrollbars.css
new file mode 100644
index 0000000000..82ef4d2ac1
--- /dev/null
+++ b/toolkit/themes/osx/global/nativescrollbars.css
@@ -0,0 +1,89 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+scrollbar {
+ -moz-appearance: scrollbar;
+ -moz-binding: url(chrome://global/content/bindings/scrollbar.xml#scrollbar);
+ cursor: default;
+ background-color: white;
+}
+
+scrollbar[root="true"] {
+ position: relative;
+ z-index: 2147483647; /* largest positive value of a signed 32-bit integer */
+}
+
+html|select[size]:not([size="0"]):not([size="1"]) > scrollbar,
+html|select[multiple] > scrollbar {
+ -moz-appearance: scrollbar-small;
+}
+
+@media all and (-moz-overlay-scrollbars) {
+ scrollbar:not([active="true"]),
+ scrollbar[disabled="true"] {
+ visibility: hidden;
+ }
+}
+
+/* ..... track ..... */
+
+slider {
+ -moz-appearance: scrollbartrack-horizontal;
+}
+
+slider[orient="vertical"] {
+ -moz-appearance: scrollbartrack-vertical;
+}
+
+/* ..... thumb ..... */
+
+thumb {
+ -moz-appearance: scrollbarthumb-horizontal;
+}
+
+thumb[orient="vertical"] {
+ -moz-appearance: scrollbarthumb-vertical;
+}
+
+/* ..... increment ..... */
+
+scrollbarbutton[type="increment"] {
+ -moz-appearance: scrollbarbutton-right;
+}
+
+scrollbar[orient="vertical"] > scrollbarbutton[type="increment"] {
+ -moz-appearance: scrollbarbutton-down;
+}
+
+/* ..... decrement ..... */
+
+scrollbarbutton[type="decrement"] {
+ -moz-appearance: scrollbarbutton-left;
+}
+
+scrollbar[orient="vertical"] > scrollbarbutton[type="decrement"] {
+ -moz-appearance: scrollbarbutton-up;
+}
+
+/* ::::: square at the corner of two scrollbars ::::: */
+
+scrollcorner {
+ /* XXX -moz-appearance: scrollcorner; */
+ -moz-binding: url(chrome://global/content/bindings/scrollbar.xml#scrollbar-base);
+ width: 16px;
+ cursor: default;
+ background-color: white;
+}
+
+/* ::::::::::::::::::::: MEDIA PRINT :::::::::::::::::::::: */
+@media print {
+ html|div scrollbar {
+ -moz-appearance: scrollbar;
+ -moz-binding: url(chrome://global/content/bindings/scrollbar.xml#scrollbar);
+ cursor: default;
+ }
+}
diff --git a/toolkit/themes/osx/global/netError.css b/toolkit/themes/osx/global/netError.css
new file mode 100644
index 0000000000..9255f958e3
--- /dev/null
+++ b/toolkit/themes/osx/global/netError.css
@@ -0,0 +1,145 @@
+/* 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/. */
+
+/*
+ * This defines the look-and-feel styling of the error pages.
+ * (see: netError.xhtml)
+ *
+ * Original styling by William Price <bugzilla@mob.rice.edu>
+ * Updated by: Steven Garrity <steven@silverorange.com>
+ * Henrik Skupin <mozilla@hskupin.info>
+ */
+
+html {
+ background: -moz-Dialog;
+}
+
+body {
+ margin: 0;
+ padding: 0 1em;
+ color: -moz-FieldText;
+ font: message-box;
+}
+
+h1 {
+ margin: 0 0 .6em 0;
+ border-bottom: 1px solid ThreeDLightShadow;
+ font-size: 160%;
+}
+
+ul, ol {
+ margin: 0;
+ margin-inline-start: 1.5em;
+ padding: 0;
+}
+
+ul > li, ol > li {
+ margin-bottom: .5em;
+}
+
+ul {
+ list-style: square;
+}
+
+#errorPageContainer {
+ position: relative;
+ min-width: 13em;
+ max-width: 52em;
+ margin: 4em auto;
+ border: 1px solid ThreeDShadow;
+ border-radius: 10px;
+ padding: 3em;
+ padding-inline-start: 30px;
+ background: url("chrome://global/skin/icons/warning-large.png") left 0 no-repeat -moz-Field;
+ background-origin: content-box;
+}
+
+#errorPageContainer.certerror {
+ background-image: url("chrome://global/skin/icons/sslWarning.png");
+}
+
+#errorPageContainer:dir(rtl) {
+ background-position: right 0;
+}
+
+#errorTitle {
+ margin-inline-start: 80px;
+}
+
+#errorLongContent {
+ margin-inline-start: 80px;
+}
+
+#errorShortDesc > p {
+ overflow: auto;
+ border-bottom: 1px solid ThreeDLightShadow;
+ padding-bottom: 1em;
+ font-size: 130%;
+ white-space: pre-wrap;
+}
+
+#errorLongDesc {
+ padding-inline-end: 3em;
+ font-size: 110%;
+}
+
+#errorLongDesc > p {
+}
+
+#errorTryAgain {
+ margin-top: 2em;
+ margin-inline-start: 80px;
+}
+
+#brand {
+ position: absolute;
+ right: 0;
+ bottom: -1.5em;
+ margin-inline-end: 10px;
+ opacity: .4;
+}
+
+#brand:dir(rtl) {
+ right: auto;
+ left: 0;
+}
+
+#brand > p {
+ margin: 0;
+}
+
+#errorContainer {
+ display: none;
+}
+
+#securityOverrideDiv {
+ padding-top: 10px;
+}
+
+#securityOverrideContent {
+ background-color: #FFF090; /* Pale yellow */
+ padding: 10px;
+ border-radius: 10px;
+}
+
+/* Custom styling for 'blacklist' error class */
+:root.blacklist #errorTitle, :root.blacklist #errorLongContent,
+:root.blacklist #errorShortDesc, :root.blacklist #errorLongDesc,
+:root.blacklist a {
+ background-color: #722; /* Dark red */
+ color: white;
+}
+
+:root.blacklist #errorPageContainer {
+ background-image: url("chrome://global/skin/icons/blacklist_64.png");
+ background-color: #722;
+}
+
+:root.blacklist {
+ background: #333;
+}
+
+:root.blacklist #errorTryAgain {
+ display: none;
+}
diff --git a/toolkit/themes/osx/global/notification.css b/toolkit/themes/osx/global/notification.css
new file mode 100644
index 0000000000..6d22cf9c86
--- /dev/null
+++ b/toolkit/themes/osx/global/notification.css
@@ -0,0 +1,206 @@
+/* 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 shared.inc
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+notification {
+ padding: 3px 3px 4px;
+ text-shadow: none;
+}
+
+notification[type="info"] {
+ color: rgba(255,255,255,0.95);
+ background: linear-gradient(#606060, #404040);
+ border-top: 1px solid #2a2a2a;
+ border-bottom: 1px solid #2a2a2a;
+}
+
+notification[type="warning"] {
+ color: rgba(0,0,0,0.95);
+ background: linear-gradient(#ffe13e, #ffc703);
+ border-top: 1px solid #bf8a01;
+ border-bottom: 1px solid #bf8a01;
+}
+
+notification[type="critical"] {
+ color: rgba(255,255,255,0.95);
+ background: linear-gradient(#d40000, #980000);
+ border-top: 1px solid #5d0000;
+ border-bottom: 1px solid #5d0000;
+}
+
+notificationbox[notificationside="top"] > notification {
+ border-top-style: none;
+}
+
+notificationbox[notificationside="bottom"] > notification {
+ border-bottom-style: none;
+}
+
+.messageText > .text-link {
+ color: inherit !important;
+ text-decoration: underline;
+}
+
+.messageImage {
+ width: 16px;
+ height: 16px;
+ margin: 0 4px;
+}
+
+/* Default icons for notifications */
+
+.messageImage[type="info"] {
+ list-style-image: url("chrome://global/skin/notification/info-icon.png");
+}
+
+.messageImage[type="warning"] {
+ list-style-image: url("chrome://global/skin/notification/warning-icon.png");
+}
+
+.messageImage[type="critical"] {
+ list-style-image: url("chrome://global/skin/notification/error-icon.png");
+}
+
+.messageText {
+ margin: 0 3px !important;
+ padding: 0;
+ font-weight: bold;
+}
+
+.messageCloseButton {
+ -moz-appearance: none;
+ padding: 0;
+ margin: 0 2px;
+ border: none;
+}
+
+/*
+ Invert the close icon for @type=info since both are normally dark. It's unclear
+ why !important is necessary here so remove it if it's no longer needed.
+*/
+notification[type="info"] .close-icon:not(:hover) {
+ -moz-image-region: rect(0, 64px, 16px, 48px) !important;
+}
+
+@media (min-resolution: 2dppx) {
+ notification[type="info"] .close-icon:not(:hover) {
+ -moz-image-region: rect(0, 128px, 32px, 96px) !important;
+ }
+}
+
+.messageCloseButton:-moz-focusring > .toolbarbutton-icon {
+ border-radius: 10000px;
+ box-shadow: 0 0 2px 1px -moz-mac-focusring,
+ 0 0 0 2px -moz-mac-focusring inset;
+}
+
+@media (min-resolution: 2dppx) {
+ .messageCloseButton > .toolbarbutton-icon {
+ width: 16px;
+ }
+}
+
+/* Popup notification */
+
+.popup-notification-body {
+ max-width: 25em;
+}
+
+.popup-notification-origin:not([value]),
+.popup-notification-learnmore-link:not([href]) {
+ display: none;
+}
+
+.popup-notification-origin {
+ margin-bottom: .3em !important;
+}
+
+.popup-notification-learnmore-link {
+ margin-top: .5em !important;
+}
+
+.popup-notification-button-container {
+ margin-top: 17px;
+}
+
+.popup-notification-menubutton {
+ -moz-appearance: none;
+}
+
+.popup-notification-menubutton:not([type="menu-button"]):-moz-focusring,
+.popup-notification-menubutton:-moz-focusring > .button-menubutton-dropmarker,
+.popup-notification-menubutton > .button-menubutton-button:-moz-focusring {
+ box-shadow: @focusRingShadow@;
+ position: relative;
+}
+
+.popup-notification-menubutton:not([type="menu-button"]),
+.popup-notification-menubutton > .button-menubutton-button,
+.popup-notification-menubutton > .button-menubutton-dropmarker {
+ -moz-appearance: none;
+ color: #434343;
+ border-radius: 4px;
+ border: 1px solid #b5b5b5;
+ background: linear-gradient(#fff, #f2f2f2);
+ box-shadow: inset 0 1px rgba(255,255,255,.8),
+ inset 0 0 1px rgba(255,255,255,.25),
+ 0 1px rgba(255,255,255,.3);
+ background-clip: padding-box;
+ background-origin: padding-box;
+ padding: 2px 6px;
+}
+
+.popup-notification-menubutton > .button-menubutton-button {
+ -moz-appearance: none;
+ margin: 0;
+ padding-top: 2px;
+ padding-bottom: 2px;
+ padding-inline-start: 8px;
+ padding-inline-end: 5px;
+}
+
+.popup-notification-menubutton > .button-menubutton-dropmarker {
+ padding: 7px 8px;
+ margin-top: 0;
+ margin-bottom: 0;
+ margin-inline-start: -1px;
+ list-style-image: url("chrome://global/skin/icons/panel-dropmarker.png");
+}
+
+.popup-notification-menubutton > .button-menubutton-button:-moz-locale-dir(ltr),
+.popup-notification-menubutton > .button-menubutton-dropmarker:-moz-locale-dir(rtl) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+}
+
+.popup-notification-menubutton > .button-menubutton-button:-moz-locale-dir(rtl),
+.popup-notification-menubutton > .button-menubutton-dropmarker:-moz-locale-dir(ltr) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+}
+
+.popup-notification-menubutton:not([type="menu-button"]):hover:active,
+.popup-notification-menubutton > .button-menubutton-button:hover:active,
+.popup-notification-menubutton[open="true"] > .button-menubutton-dropmarker {
+ box-shadow: inset 0 1px 4px -3px #000, 0 1px rgba(255, 255, 255, 0.3);
+}
+
+.popup-notification-closebutton {
+ margin-inline-end: -12px;
+ margin-top: -13px;
+}
+
+.popup-notification-closeitem > .menu-iconic-left {
+ display: none;
+}
+
+.popup-notification-menubutton > .button-menubutton-button[disabled] {
+ opacity: 0.5;
+}
+
+.popup-notification-warning {
+ color: #aa1b08;
+}
diff --git a/toolkit/themes/osx/global/notification/close.png b/toolkit/themes/osx/global/notification/close.png
new file mode 100644
index 0000000000..3300a4d61e
--- /dev/null
+++ b/toolkit/themes/osx/global/notification/close.png
Binary files differ
diff --git a/toolkit/themes/osx/global/notification/error-icon.png b/toolkit/themes/osx/global/notification/error-icon.png
new file mode 100644
index 0000000000..54cc7e663b
--- /dev/null
+++ b/toolkit/themes/osx/global/notification/error-icon.png
Binary files differ
diff --git a/toolkit/themes/osx/global/notification/info-icon.png b/toolkit/themes/osx/global/notification/info-icon.png
new file mode 100644
index 0000000000..55d45f165c
--- /dev/null
+++ b/toolkit/themes/osx/global/notification/info-icon.png
Binary files differ
diff --git a/toolkit/themes/osx/global/notification/warning-icon.png b/toolkit/themes/osx/global/notification/warning-icon.png
new file mode 100644
index 0000000000..13cf79d6df
--- /dev/null
+++ b/toolkit/themes/osx/global/notification/warning-icon.png
Binary files differ
diff --git a/toolkit/themes/osx/global/numberbox.css b/toolkit/themes/osx/global/numberbox.css
new file mode 100644
index 0000000000..e5de22d218
--- /dev/null
+++ b/toolkit/themes/osx/global/numberbox.css
@@ -0,0 +1,33 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+textbox[type="number"] {
+ -moz-appearance: none;
+ -moz-box-align: center;
+ padding: 0 !important;
+ border: none;
+ background-color: transparent;
+ cursor: default;
+}
+
+html|*.numberbox-input {
+ text-align: right;
+ padding: 0 1px !important;
+}
+
+.numberbox-input-box {
+ -moz-appearance: textfield;
+ margin-right: 4px;
+ border: 3px solid;
+ -moz-border-top-colors: transparent #888888 #000000;
+ -moz-border-right-colors: transparent #FFFFFF #000000;
+ -moz-border-bottom-colors: transparent #FFFFFF #000000;
+ -moz-border-left-colors: transparent #888888 #000000;
+ border-top-right-radius: 2px;
+ border-bottom-left-radius: 2px;
+ background-color: -moz-Field;
+}
diff --git a/toolkit/themes/osx/global/popup.css b/toolkit/themes/osx/global/popup.css
new file mode 100644
index 0000000000..cf0266a3a6
--- /dev/null
+++ b/toolkit/themes/osx/global/popup.css
@@ -0,0 +1,141 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+menupopup,
+panel {
+ -moz-appearance: menupopup;
+ background-color: Menu;
+}
+
+menupopup > menu > menupopup {
+ margin-top: -4px;
+}
+
+.popup-internal-box {
+ padding: 4px 0;
+}
+
+panel[titlebar] {
+ -moz-appearance: none; /* to disable rounded corners */
+}
+
+panel[type="arrow"] {
+ -moz-appearance: none;
+ background: transparent;
+}
+
+panel[type="arrow"][side="top"],
+panel[type="arrow"][side="bottom"] {
+ margin-left: -25px;
+ margin-right: -25px;
+}
+
+panel[type="arrow"][side="left"],
+panel[type="arrow"][side="right"] {
+ margin-top: -25px;
+ margin-bottom: -25px;
+}
+
+.panel-arrowcontent {
+ -moz-appearance: none;
+ background: var(--arrowpanel-background);
+ border-radius: var(--arrowpanel-border-radius);
+ box-shadow: 0 0 0 1px var(--arrowpanel-border-color);
+ color: var(--arrowpanel-color);
+ border: none;
+ padding: var(--arrowpanel-padding);
+ margin: 1px;
+}
+
+.panel-arrow[side="top"] {
+ list-style-image: var(--panel-arrow-image-vertical,
+ url("chrome://global/skin/arrow/panelarrow-vertical.png"));
+ margin-left: 16px;
+ margin-right: 16px;
+ margin-bottom: -1px;
+}
+
+.panel-arrow[side="bottom"] {
+ list-style-image: url("chrome://global/skin/arrow/panelarrow-vertical.png");
+ -moz-transform: scaleY(-1);
+ margin-left: 16px;
+ margin-right: 16px;
+ margin-top: -1px;
+}
+
+.panel-arrow[side="left"] {
+ list-style-image: url("chrome://global/skin/arrow/panelarrow-horizontal.png");
+ margin-top: 16px;
+ margin-bottom: 16px;
+ margin-right: -1px;
+}
+
+.panel-arrow[side="right"] {
+ list-style-image: url("chrome://global/skin/arrow/panelarrow-horizontal.png");
+ transform: scaleX(-1);
+ margin-top: 16px;
+ margin-bottom: 16px;
+ margin-left: -1px;
+}
+
+@media (min-resolution: 2dppx) {
+ .panel-arrow[side="top"],
+ .panel-arrow[side="bottom"] {
+ list-style-image: var(--panel-arrow-image-vertical,
+ url("chrome://global/skin/arrow/panelarrow-vertical@2x.png"));
+ width: 18px;
+ height: 10px;
+ }
+
+ .panel-arrow[side="left"],
+ .panel-arrow[side="right"] {
+ list-style-image: url("chrome://global/skin/arrow/panelarrow-horizontal@2x.png");
+ width: 10px;
+ height: 18px;
+ }
+}
+
+/* ::::: tooltip ::::: */
+
+tooltip {
+ -moz-appearance: tooltip;
+ margin-top: 18px;
+ padding: 2px 3px;
+ max-width: 40em;
+ color: InfoText;
+ font: message-box;
+ cursor: default;
+}
+
+tooltip[titletip="true"] {
+ /* See bug 32157 comment 128
+ * margin: -2px 0px 0px -3px;
+ */
+ max-width: none;
+}
+
+/* rules for popups associated with menulists */
+
+menulist > menupopup {
+ min-width: 0px;
+}
+
+menulist > menupopup:not([position]) {
+ margin-inline-start: -13px;
+ margin-top: -2px;
+}
+
+menulist[editable="true"] > menupopup {
+ -moz-appearance: none;
+}
+
+menulist > menupopup > .popup-internal-box {
+ padding: 0;
+}
+
+menulist:not([editable="true"]) > menupopup {
+ padding: 4px 0;
+}
diff --git a/toolkit/themes/osx/global/preferences.css b/toolkit/themes/osx/global/preferences.css
new file mode 100644
index 0000000000..d0b82a819c
--- /dev/null
+++ b/toolkit/themes/osx/global/preferences.css
@@ -0,0 +1,64 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+prefwindow {
+ padding: 0;
+ font: -moz-dialog !important;
+}
+
+prefpane {
+ padding: 12px 12px 0 12px;
+}
+
+prefwindow[type="child"] > prefpane {
+ padding: 0;
+}
+
+.prefWindow-dlgbuttons {
+ margin: 0 12px 12px;
+ padding-top: 0 !important;
+}
+
+.paneSelector {
+ font: message-box;
+ padding: 1px 4px;
+ -moz-appearance: toolbar;
+ margin: 0;
+}
+
+radio[pane] {
+ border: solid transparent;
+ border-width: 0 2px;
+ padding: 5px 4px 3px;
+ margin: 0;
+ -moz-appearance: none;
+ text-shadow: rgba(255, 255, 255, 0.4) 0 1px;
+}
+
+radio[pane]:active:hover {
+ text-shadow: none;
+}
+
+radio[pane] > .paneButtonIcon {
+ /* preload external filter file */
+ background-image: url("chrome://global/skin/filters.svg");
+}
+
+radio[pane]:active:hover > .paneButtonIcon {
+ filter: url("chrome://global/skin/filters.svg#iconPressed");
+}
+
+radio[pane][selected="true"] {
+ -moz-border-image: url("chrome://global/skin/icons/panebutton-active.png") 0 2 fill repeat stretch;
+}
+
+radio[pane][selected="true"]:-moz-window-inactive {
+ -moz-border-image: url("chrome://global/skin/icons/panebutton-inactive.png") 0 2 fill repeat stretch;
+}
+
+.paneButtonLabel {
+ margin: 0 !important;
+}
diff --git a/toolkit/themes/osx/global/progressmeter.css b/toolkit/themes/osx/global/progressmeter.css
new file mode 100644
index 0000000000..13fce252ab
--- /dev/null
+++ b/toolkit/themes/osx/global/progressmeter.css
@@ -0,0 +1,22 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+progressmeter {
+ -moz-appearance: progressbar;
+ margin: 2px 4px;
+ min-width: 128px;
+ height: 12px;
+}
+
+.progress-remainder[flex="100"], .progress-remainder[flex="0"] {
+ background-image: none !important;
+ -moz-appearance: none;
+}
+
+.progressmeter-statusbar {
+ margin: 0;
+ border-width: 1px;
+}
diff --git a/toolkit/themes/osx/global/radio.css b/toolkit/themes/osx/global/radio.css
new file mode 100644
index 0000000000..e21d93011b
--- /dev/null
+++ b/toolkit/themes/osx/global/radio.css
@@ -0,0 +1,43 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+radiogroup {
+ margin: 1px 0px 1px 0px;
+}
+
+radio {
+ -moz-appearance: radio-container;
+ -moz-box-align: center;
+ margin: 4px 2px;
+ -moz-user-focus: ignore;
+}
+
+.radio-label-box {
+ margin-inline-start: 0px;
+ padding: 0px;
+}
+
+.radio-icon {
+ margin-inline-end: 2px;
+}
+
+.radio-label {
+ margin: 1px 0 !important;
+}
+
+radio[disabled="true"] {
+ color: GrayText !important;
+}
+
+.radio-check, .radio-check-box1 {
+ -moz-appearance: radio;
+ margin: 0 1px 1px;
+ /* vertical-align tells native theming where to snap to. However, this doesn't
+ * always work reliably because of bug 503833. */
+ vertical-align: bottom;
+ width: 1.3em;
+ height: 1.3em;
+}
diff --git a/toolkit/themes/osx/global/resizer.css b/toolkit/themes/osx/global/resizer.css
new file mode 100644
index 0000000000..18cdd2bc93
--- /dev/null
+++ b/toolkit/themes/osx/global/resizer.css
@@ -0,0 +1,69 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+resizer {
+ -moz-appearance: resizer;
+ background: url("chrome://global/skin/icons/resizer.png") no-repeat;
+ background-size: 100% 100%;
+ cursor: se-resize;
+ min-width: 15px;
+ width: 15px;
+ min-height: 15px;
+ height: 15px;
+}
+@media (min-resolution: 2dppx) {
+ resizer {
+ background-image: url("chrome://global/skin/icons/resizer@2x.png");
+ background-size: 100% 100%;
+ }
+}
+
+resizer[type="window"] {
+ display: none;
+}
+
+resizer[rtl="true"],
+resizer[dir="bottomend"]:-moz-locale-dir(rtl) {
+ background: url("chrome://global/skin/icons/resizer-rtl.png") no-repeat;
+}
+@media (min-resolution: 2dppx) {
+ resizer[rtl="true"],
+ resizer[dir="bottomend"]:-moz-locale-dir(rtl) {
+ background-image: url("chrome://global/skin/icons/resizer-rtl@2x.png");
+ background-size: 100% 100%;
+ }
+}
+
+
+resizer[dir="left"],
+resizer[dir="bottomleft"],
+resizer[dir="bottomstart"] {
+ transform: scaleX(-1);
+}
+
+resizer[dir="bottomleft"],
+resizer[dir="bottomstart"]:not([rtl="true"]):not(:-moz-locale-dir(rtl)),
+resizer[dir="bottomend"][rtl="true"] {
+ cursor: sw-resize;
+}
+
+resizer[dir="top"],
+resizer[dir="bottom"] {
+ cursor: ns-resize;
+}
+
+resizer[dir="left"],
+resizer[dir="right"] {
+ cursor: ew-resize;
+}
+
+resizer[dir="topleft"] {
+ cursor: nw-resize;
+}
+
+resizer[dir="topright"] {
+ cursor: ne-resize;
+}
diff --git a/toolkit/themes/osx/global/richlistbox.css b/toolkit/themes/osx/global/richlistbox.css
new file mode 100644
index 0000000000..605c89abb3
--- /dev/null
+++ b/toolkit/themes/osx/global/richlistbox.css
@@ -0,0 +1,27 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+richlistbox {
+ -moz-appearance: listbox;
+ margin: 2px 4px;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+richlistbox[disabled="true"] {
+ color: GrayText;
+}
+
+richlistitem[selected="true"] {
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+richlistbox:focus > richlistitem[selected="true"] {
+ background-color: Highlight;
+ color: HighlightText;
+}
+
diff --git a/toolkit/themes/osx/global/scale.css b/toolkit/themes/osx/global/scale.css
new file mode 100644
index 0000000000..2e090bf28d
--- /dev/null
+++ b/toolkit/themes/osx/global/scale.css
@@ -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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+.scale-slider {
+ -moz-appearance: scale-horizontal;
+ background: url("chrome://global/skin/scale/scale-tray-horiz.gif") 0% 50% repeat-x;
+ margin: 2px 4px;
+ width: 100px;
+}
+
+.scale-slider[orient="vertical"]
+{
+ -moz-appearance: scale-vertical;
+ background: url("chrome://global/skin/scale/scale-tray-vert.gif") 50% 0% repeat-y;
+ margin: 4px 2px;
+ width: auto;
+ height: 100px;
+}
+
+.scale-thumb {
+ -moz-appearance: scalethumb-horizontal;
+ border: 2px solid;
+ -moz-border-top-colors: ThreeDLightShadow ThreeDHighlight;
+ -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-bottom-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-left-colors: ThreeDLightShadow ThreeDHighlight;
+ background-color: -moz-Dialog;
+ min-width: 30px;
+ min-height: 15px;
+}
+
+.scale-thumb[orient="vertical"] {
+ -moz-appearance: scalethumb-vertical;
+ min-width: 15px;
+ min-height: 30px;
+}
+
+.scale-thumb[disabled="true"] {
+ -moz-border-top-colors: ThreeDHighlight ThreeDLightShadow !important;
+ -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow !important;
+ -moz-border-bottom-colors: ThreeDDarkShadow ThreeDShadow !important;
+ -moz-border-left-colors: ThreeDHighlight ThreeDLightShadow !important;
+}
diff --git a/toolkit/themes/osx/global/scale/scale-tray-horiz.gif b/toolkit/themes/osx/global/scale/scale-tray-horiz.gif
new file mode 100644
index 0000000000..b87fe68c15
--- /dev/null
+++ b/toolkit/themes/osx/global/scale/scale-tray-horiz.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/scale/scale-tray-vert.gif b/toolkit/themes/osx/global/scale/scale-tray-vert.gif
new file mode 100644
index 0000000000..97687b2e22
--- /dev/null
+++ b/toolkit/themes/osx/global/scale/scale-tray-vert.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/scrollbox.css b/toolkit/themes/osx/global/scrollbox.css
new file mode 100644
index 0000000000..c9b7276698
--- /dev/null
+++ b/toolkit/themes/osx/global/scrollbox.css
@@ -0,0 +1,62 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* Horizontal enabled */
+.autorepeatbutton-up[orient="horizontal"],
+.scrollbutton-up[orient="horizontal"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft-sharp.gif");
+ -moz-image-region: auto; /* cut off inheritance */
+}
+
+.autorepeatbutton-down[orient="horizontal"],
+.scrollbutton-down[orient="horizontal"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit-sharp.gif");
+ -moz-image-region: auto; /* cut off inheritance */
+}
+
+/* Horizontal disabled */
+.autorepeatbutton-up[orient="horizontal"][disabled="true"],
+.scrollbutton-up[orient="horizontal"][disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-lft-dis.gif");
+ -moz-image-region: auto; /* cut off inheritance */
+}
+
+.autorepeatbutton-down[orient="horizontal"][disabled="true"],
+.scrollbutton-down[orient="horizontal"][disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-rit-dis.gif");
+ -moz-image-region: auto; /* cut off inheritance */
+}
+
+/* Vertical enabled */
+.autorepeatbutton-up:not([orient="horizontal"]),
+.scrollbutton-up {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up-sharp.gif");
+ -moz-image-region: auto; /* cut off inheritance */
+}
+
+.autorepeatbutton-down:not([orient="horizontal"]),
+.scrollbutton-down {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-sharp.gif");
+ -moz-image-region: auto; /* cut off inheritance */
+}
+
+/* Vertical disabled */
+.autorepeatbutton-up[disabled="true"]:not([orient="horizontal"]),
+.scrollbutton-up[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up-dis.gif");
+ -moz-image-region: auto; /* cut off inheritance */
+}
+
+.autorepeatbutton-down[disabled="true"]:not([orient="horizontal"]),
+.scrollbutton-down[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.gif");
+ -moz-image-region: auto; /* cut off inheritance */
+}
+
+.scrollbutton-up > .toolbarbutton-text,
+.scrollbutton-down > .toolbarbutton-text {
+ display: none;
+}
diff --git a/toolkit/themes/osx/global/shared.inc b/toolkit/themes/osx/global/shared.inc
new file mode 100644
index 0000000000..350fed1721
--- /dev/null
+++ b/toolkit/themes/osx/global/shared.inc
@@ -0,0 +1,20 @@
+%filter substitution
+
+%define loweredShadow 0 1px rgba(255, 255, 255, .4)
+%define focusRingShadow 0 0 1px -moz-mac-focusring inset, 0 0 4px 1px -moz-mac-focusring, 0 0 1.5px 1px -moz-mac-focusring
+%define yosemiteFocusRingShadow 0 0 0 0.5px -moz-mac-focusring inset, 0 0 0 2px -moz-mac-focusring
+
+%define roundButtonBorder 1px solid rgba(0,0,0,.35)
+%define roundButtonBackground linear-gradient(#f6f6f6, #e9e9e9)
+%define roundButtonShadow 0 1px rgba(255,255,255,.5), inset 0 1px 1px rgba(255,255,255,.5)
+%define roundButtonPressedBackground #dadada
+%define roundButtonPressedShadow 0 1px rgba(255,255,255,.4), inset 0 1px 3px rgba(0,0,0,.2)
+
+%define scopeBarBackground linear-gradient(#E8E8E8, #D0D0D0) repeat-x
+%define scopeBarSeparatorBorder 1px solid #888
+%define scopeBarTitleColor #6D6D6D
+
+%define toolbarbuttonCornerRadius 3px
+%define toolbarbuttonBackground linear-gradient(#FFF, #ADADAD) repeat-x
+%define toolbarbuttonPressedInnerShadow inset rgba(0, 0, 0, 0.3) 0 -6px 10px, inset #000 0 1px 3px, inset rgba(0, 0, 0, 0.2) 0 1px 3px
+%define toolbarbuttonInactiveBorderColor rgba(146, 146, 146, 0.84)
diff --git a/toolkit/themes/osx/global/spinbuttons.css b/toolkit/themes/osx/global/spinbuttons.css
new file mode 100644
index 0000000000..bf89520f68
--- /dev/null
+++ b/toolkit/themes/osx/global/spinbuttons.css
@@ -0,0 +1,31 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+spinbuttons {
+ height: 24px;
+ min-height: 24px;
+ -moz-appearance: spinner;
+ cursor: default;
+}
+
+.spinbuttons-up {
+ -moz-appearance: none;
+ -moz-box-flex: 1;
+ min-width: 1px;
+ min-height: 1px;
+ margin: 0;
+ padding: 0;
+}
+
+.spinbuttons-down {
+ -moz-appearance: none;
+ -moz-box-flex: 1;
+ min-width: 1px;
+ min-height: 1px;
+ margin: 0;
+ padding: 0;
+}
+
diff --git a/toolkit/themes/osx/global/splitter.css b/toolkit/themes/osx/global/splitter.css
new file mode 100644
index 0000000000..caaa83ad0a
--- /dev/null
+++ b/toolkit/themes/osx/global/splitter.css
@@ -0,0 +1,124 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+/* ::::: splitter (vertical) ::::: */
+
+splitter {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ cursor: ew-resize;
+ min-width: 9px;
+ min-height: 9px;
+ background: url("chrome://global/skin/splitter/dimple.png") transparent no-repeat center;
+}
+
+splitter[state="collapsed"][collapse="before"],
+splitter[state="collapsed"][substate="before"],
+splitter[state="collapsed"][collapse="after"]:-moz-locale-dir(rtl),
+splitter[state="collapsed"][substate="after"]:-moz-locale-dir(rtl) {
+ cursor: e-resize;
+}
+
+splitter[state="collapsed"][collapse="after"],
+splitter[state="collapsed"][substate="after"],
+splitter[state="collapsed"][collapse="before"]:-moz-locale-dir(rtl),
+splitter[state="collapsed"][substate="before"]:-moz-locale-dir(rtl) {
+ cursor: w-resize;
+}
+
+splitter:-moz-lwtheme {
+ background: none;
+}
+
+/* ::::: splitter (horizontal) ::::: */
+
+splitter[orient="vertical"] {
+ cursor: ns-resize;
+ min-width: 0px;
+ min-height: 9px;
+ min-width: 9px;
+ background: url("chrome://global/skin/splitter/dimple.png") transparent no-repeat center;
+}
+
+splitter[orient="vertical"][state="collapsed"][collapse="before"],
+splitter[orient="vertical"][state="collapsed"][substate="before"] {
+ cursor: s-resize;
+}
+
+splitter[orient="vertical"][state="collapsed"][collapse="after"],
+splitter[orient="vertical"][state="collapsed"][substate="after"] {
+ cursor: n-resize;
+}
+
+splitter[disabled="true"] {
+ cursor: default !important;
+}
+
+/* ::::: splitter grippy ::::: */
+
+grippy {
+ cursor: pointer;
+ margin: 0px 1px;
+ min-width: 4px;
+ min-height: 115px;
+ background-color: transparent;
+ background-repeat: no-repeat;
+}
+
+grippy:hover {
+ background-color: ThreeDHighlight;
+}
+
+splitter[orient="vertical"] > grippy {
+ margin: 1px 0px;
+ min-width: 115px;
+ min-height: 4px;
+}
+
+/* ..... normal state ..... */
+
+/* vertical grippies */
+splitter[collapse="before"] > grippy,
+splitter[collapse="after"] > grippy:-moz-locale-dir(rtl) {
+ background-image: url("chrome://global/skin/splitter/grip-left.gif");
+}
+
+splitter[collapse="after"] > grippy,
+splitter[collapse="before"] > grippy:-moz-locale-dir(rtl) {
+ background-image: url("chrome://global/skin/splitter/grip-right.gif");
+}
+
+/* horizontal grippies */
+splitter[collapse="before"][orient="vertical"] > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-top.gif");
+}
+
+splitter[collapse="after"][orient="vertical"] > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-bottom.gif");
+}
+
+/* ..... collapsed state ..... */
+
+/* vertical grippies */
+splitter[collapse="before"][state="collapsed"] > grippy,
+splitter[collapse="after"][state="collapsed"] > grippy:-moz-locale-dir(rtl) {
+ background-image: url("chrome://global/skin/splitter/grip-right.gif");
+}
+
+splitter[collapse="after"][state="collapsed"] > grippy,
+splitter[collapse="before"][state="collapsed"] > grippy:-moz-locale-dir(rtl) {
+ background-image: url("chrome://global/skin/splitter/grip-left.gif");
+}
+
+/* horizontal grippies */
+splitter[collapse="before"][state="collapsed"][orient="vertical"] > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-bottom.gif");
+}
+
+splitter[collapse="after"][state="collapsed"][orient="vertical"] > grippy {
+ background-image: url("chrome://global/skin/splitter/grip-top.gif");
+}
+
diff --git a/toolkit/themes/osx/global/splitter/dimple.png b/toolkit/themes/osx/global/splitter/dimple.png
new file mode 100644
index 0000000000..4d0b91bfea
--- /dev/null
+++ b/toolkit/themes/osx/global/splitter/dimple.png
Binary files differ
diff --git a/toolkit/themes/osx/global/splitter/grip-bottom.gif b/toolkit/themes/osx/global/splitter/grip-bottom.gif
new file mode 100644
index 0000000000..af6290fe9d
--- /dev/null
+++ b/toolkit/themes/osx/global/splitter/grip-bottom.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/splitter/grip-left.gif b/toolkit/themes/osx/global/splitter/grip-left.gif
new file mode 100644
index 0000000000..6be9bc4f40
--- /dev/null
+++ b/toolkit/themes/osx/global/splitter/grip-left.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/splitter/grip-right.gif b/toolkit/themes/osx/global/splitter/grip-right.gif
new file mode 100644
index 0000000000..71be69083e
--- /dev/null
+++ b/toolkit/themes/osx/global/splitter/grip-right.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/splitter/grip-top.gif b/toolkit/themes/osx/global/splitter/grip-top.gif
new file mode 100644
index 0000000000..3cba005946
--- /dev/null
+++ b/toolkit/themes/osx/global/splitter/grip-top.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/tabbox.css b/toolkit/themes/osx/global/tabbox.css
new file mode 100644
index 0000000000..4fcbac486d
--- /dev/null
+++ b/toolkit/themes/osx/global/tabbox.css
@@ -0,0 +1,148 @@
+/* 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/. */
+
+/*
+ The default style of these tabs is that of an NSTabView with tabs at
+ the top in the "regular" size. These tabs can be used with or without
+ a tabbox element.
+ For bottom tabs you should use the "tabs-bottom" class on the tabbox
+ or the tabs element. Bottom tabs use a style that's similar to the
+ one used in Adium.
+*/
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+tabbox {
+ margin: 0 5px;
+}
+
+tabpanels {
+ -moz-appearance: tabpanels;
+ padding: 33px 15px 15px;
+}
+
+tabs {
+ -moz-box-align: center;
+ font: menu;
+}
+
+tabbox > tabs {
+ padding: 0 10px;
+ margin-bottom: -12px;
+ position: relative;
+}
+
+tab {
+ -moz-appearance: tab;
+}
+
+tab:-moz-focusring {
+ /* Tab focus rings need to overlay adjacent tabs. */
+ position: relative;
+}
+
+tab:first-of-type {
+ padding-inline-start: 2px;
+}
+
+tab:last-of-type {
+ padding-inline-end: 2px;
+}
+
+tab[visuallyselected="true"] {
+ color: #FFF;
+ text-shadow: rgba(0, 0, 0, 0.4) 0 1px;
+}
+
+.tab-middle {
+ padding: 1px 6px 2px;
+}
+
+.tabs-left,
+.tabs-right {
+ -moz-box-flex: 1;
+}
+
+/* Tabs at the bottom
+ * These tabs are smaller, left aligned and don't extend into the tabpanel.
+ */
+
+tabbox.tabs-bottom > tabpanels {
+ padding: 10px;
+}
+
+tabbox.tabs-bottom > tabs,
+tabs.tabs-bottom {
+ background-color: rgba(0, 0, 0, 0.1);
+ padding: 0;
+ margin: 0;
+ border-top: 2px solid;
+ -moz-border-top-colors: #888 rgba(0, 0, 0, 0.08);
+ -moz-box-align: start;
+ font: message-box;
+}
+
+tabbox.tabs-bottom > tabs > .tabs-left,
+tabs.tabs-bottom > .tabs-left {
+ -moz-box-flex: 0;
+}
+
+tabbox.tabs-bottom > tabs > tab,
+tabs.tabs-bottom > tab {
+ -moz-appearance: none;
+ margin: -1px 0 0;
+ padding: 0 0 2px 0;
+ position: relative;
+ border-inline-end: 1px solid rgba(0, 0, 0, 0.19);
+}
+
+tabbox.tabs-bottom > tabs > tab > .tab-middle,
+tabs.tabs-bottom > tab > .tab-middle {
+ padding: 1px 2px 0 2px;
+}
+
+tabbox.tabs-bottom > tabs > tab:not([visuallyselected=true]):hover,
+tabs.tabs-bottom > tab:not([visuallyselected=true]):hover {
+ background-color: rgba(0, 0, 0, 0.1);
+ border-inline-end-color: rgba(0, 0, 0, 0.1);
+}
+
+tabbox.tabs-bottom > tabs > tab[visuallyselected=true],
+tabs.tabs-bottom > tab[visuallyselected=true] {
+ color: #000;
+ text-shadow: none;
+ border: solid #888;
+ border-width: 0 2px 2px;
+ border-radius: 2px;
+ -moz-border-left-colors: rgba(0, 0, 0, 0.08) #888;
+ -moz-border-right-colors: rgba(0, 0, 0, 0.08) #888;
+ -moz-border-bottom-colors: rgba(0, 0, 0, 0.08) #888;
+ margin-inline-end: -1px;
+ margin-top: -2px;
+ margin-bottom: 1px;
+ padding: 0;
+}
+
+tabbox.tabs-bottom > tabs > tab[beforeselected=true],
+tabs.tabs-bottom > tab[beforeselected=true] {
+ border-inline-end-color: transparent;
+ margin-inline-end: -2px;
+}
+
+tabbox.tabs-bottom > tabs > tab:first-of-type:not([visuallyselected=true]),
+tabs.tabs-bottom > tab:first-of-type:not([visuallyselected=true]) {
+ border-inline-start: 4px solid transparent;
+}
+
+tabbox.tabs-bottom > tabs > tab:first-of-type[visuallyselected=true],
+tabs.tabs-bottom > tab:first-of-type[visuallyselected=true] {
+ margin-inline-start: 2px;
+}
+
+tabbox.tabs-bottom,
+tabbox.tabs-bottom > tabpanels,
+tabbox.tabs-bottom > tabs > tab[visuallyselected=true] > .tab-middle,
+tabs.tabs-bottom > tab[visuallyselected=true] > .tab-middle {
+ -moz-appearance: dialog;
+}
diff --git a/toolkit/themes/osx/global/tabprompts.css b/toolkit/themes/osx/global/tabprompts.css
new file mode 100644
index 0000000000..14a23f2696
--- /dev/null
+++ b/toolkit/themes/osx/global/tabprompts.css
@@ -0,0 +1,67 @@
+/* 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/. */
+
+/* Tab Modal Prompt boxes */
+tabmodalprompt {
+ background-image: url(chrome://global/skin/icons/tabprompts-bgtexture.png);
+ background-color: hsla(0,0%,10%,.5);
+ font-family: sans-serif; /* use content font not system UI font */
+ font-size: 110%;
+}
+
+.mainContainer {
+ color: black;
+ background-color: hsla(0,0%,100%,.95);
+ background-clip: padding-box;
+ border-radius: 2px;
+ border: 1px solid hsla(0,0%,0%,.5);
+}
+
+.topContainer {
+ padding: 20px;
+}
+
+.buttonContainer {
+ padding: 12px 20px 15px;
+ background-color: hsla(0,0%,0%,.05);
+ border-top: 1px solid hsla(0,0%,0%,.05);
+}
+
+button {
+ -moz-appearance: none;
+ padding: 2px 0;
+ margin: 0;
+ margin-inline-start: 8px;
+ border-radius: 2px;
+ color: black !important;
+ background-color: hsl(0,0%,90%);
+ background-image: linear-gradient(hsla(0,0%,100%,.7), transparent);
+ background-clip: padding-box;
+ border: 1px solid;
+ border-color: hsl(0,0%,65%) hsl(0,0%,60%) hsl(0,0%,50%);
+ box-shadow: 0 1px 0 hsla(0,0%,100%,.9) inset,
+ 0 1px 2px hsla(0,0%,0%,.1);
+}
+
+
+button[default=true] {
+ background-color: hsl(0,0%,79%);
+}
+
+button:hover {
+ background-color: hsl(0,0%,96%);
+}
+
+button:hover:active {
+ background-image: linear-gradient(hsla(0,0%,100%,.2), transparent);
+ background-color: hsl(0,0%,70%);
+ box-shadow: 0 1px 0 hsla(0,0%,100%,.2) inset,
+ 0 1px 3px hsla(0,0%,0%,.2);
+}
+
+button:focus {
+ box-shadow: 0 0 1px -moz-mac-focusring inset,
+ 0 0 4px 1px -moz-mac-focusring,
+ 0 0 1.5px 1px -moz-mac-focusring;
+}
diff --git a/toolkit/themes/osx/global/textbox.css b/toolkit/themes/osx/global/textbox.css
new file mode 100644
index 0000000000..d7a31c7acc
--- /dev/null
+++ b/toolkit/themes/osx/global/textbox.css
@@ -0,0 +1,102 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+textbox {
+ -moz-appearance: textfield;
+ cursor: text;
+ margin: 4px;
+ border: 3px solid;
+ -moz-border-top-colors: transparent #888888 #000000;
+ -moz-border-right-colors: transparent #FFFFFF #000000;
+ -moz-border-bottom-colors: transparent #FFFFFF #000000;
+ -moz-border-left-colors: transparent #888888 #000000;
+ border-top-right-radius: 2px;
+ border-bottom-left-radius: 2px;
+ padding: 0px;
+ background-color: -moz-Field;
+ color: -moz-FieldText;
+}
+
+html|*.textbox-input,
+html|*.textbox-textarea {
+ margin: 0px !important;
+ border: none !important;
+ padding: 0px 1px !important;
+ background-color: inherit;
+ color: inherit;
+ font: inherit;
+}
+
+.textbox-contextmenu {
+ cursor: default;
+}
+
+textbox[focused="true"] {
+ -moz-border-top-colors: -moz-mac-focusring -moz-mac-focusring #000000;
+ -moz-border-right-colors: -moz-mac-focusring -moz-mac-focusring #000000;
+ -moz-border-bottom-colors: -moz-mac-focusring -moz-mac-focusring #000000;
+ -moz-border-left-colors: -moz-mac-focusring -moz-mac-focusring #000000;
+}
+
+textbox[readonly="true"] {
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+textbox[disabled="true"] {
+ cursor: default;
+ -moz-border-top-colors: transparent ThreeDShadow -moz-Dialog;
+ -moz-border-right-colors: transparent ThreeDShadow -moz-Dialog;
+ -moz-border-bottom-colors: transparent ThreeDShadow -moz-Dialog;
+ -moz-border-left-colors: transparent ThreeDShadow -moz-Dialog;
+ background-color: -moz-Dialog;
+ color: GrayText;
+}
+
+textbox.plain {
+ -moz-appearance: none !important;
+ background-color: transparent;
+ padding: 0px !important;
+ margin: 0px !important;
+ border: none !important;
+}
+
+textbox.plain html|*.textbox-input,
+textbox.plain html|*.textbox-textarea {
+ padding: 0px !important;
+}
+
+/* ::::: search box ::::: */
+
+textbox[type="search"] {
+ -moz-appearance: searchfield;
+ padding: 1px;
+ font-size: 12px;
+}
+
+.textbox-search-clear {
+ list-style-image: url(chrome://global/skin/icons/searchfield-cancel.svg);
+ -moz-image-region: rect(0, 14px, 14px, 0);
+ margin-bottom: 1px;
+}
+
+textbox[type="search"].compact {
+ padding: 0;
+ font-size: 11px;
+}
+
+textbox[type="search"].compact > .textbox-input-box > .textbox-search-icons > .textbox-search-clear {
+ width: 11px;
+}
+
+.textbox-search-clear:not([disabled]) {
+ cursor: default;
+}
+
+.textbox-search-icons:not([selectedIndex="1"]) {
+ visibility: hidden;
+}
diff --git a/toolkit/themes/osx/global/toolbar.css b/toolkit/themes/osx/global/toolbar.css
new file mode 100644
index 0000000000..f07332d2f3
--- /dev/null
+++ b/toolkit/themes/osx/global/toolbar.css
@@ -0,0 +1,127 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+toolbox {
+ /* Setting -moz-appearance:toolbox causes sheets to attach under the
+ * toolbox and has no other effects. It doesn't render anything. */
+ -moz-appearance: toolbox;
+}
+
+toolbar {
+ min-width: 1px;
+ min-height: 20px;
+ -moz-appearance: toolbar;
+}
+
+%ifdef MOZ_AUSTRALIS
+menubar:-moz-lwtheme,
+toolbar:-moz-lwtheme {
+ -moz-appearance: none;
+ background: none;
+ border-color: transparent;
+}
+%else
+menubar:-moz-lwtheme,
+toolbar:-moz-lwtheme {
+ -moz-appearance: none;
+ background: none;
+ border-style: none;
+}
+%endif
+
+menubar {
+ -moz-appearance: dialog; /* For content menubars, "toolbar" is too dark, so we use "dialog". */
+ min-width: 1px;
+}
+
+toolbarseparator {
+ -moz-appearance: none;
+ margin: 3px 4px;
+ background: url("chrome://global/skin/toolbar/toolbar-separator.png") transparent repeat-y;
+ padding: 0;
+ width: 1px !important;
+}
+
+/* ::::: toolbarpaletteitem ::::: */
+
+toolbarpaletteitem {
+ cursor: grab;
+}
+
+toolbar[iconsize="small"] toolbarpaletteitem[type="spacer"] {
+ min-width: 24px !important;
+}
+
+toolbarpaletteitem[type="spacer"] {
+ min-width: 32px !important;
+}
+
+.toolbarpaletteitem-box[type="spacer"] {
+ border: 1px solid #A3A3A3;
+ background: url("chrome://global/skin/10pct_transparent_grey.png") repeat;
+ width: 32px;
+ margin-top: 18px;
+}
+
+.toolbarpaletteitem-box[type="spring"] {
+ border: 1px solid #A3A3A3;
+ background: url("chrome://global/skin/toolbar/spring.png") #FFFFFF no-repeat;
+ width: 32px;
+ margin-top: 18px;
+}
+
+.toolbarpaletteitem-box[type="spring"][place="toolbar"] {
+ background: url("chrome://global/skin/10pct_transparent_grey.png") repeat;
+}
+
+.toolbarpaletteitem-box[type="spacer"][place="toolbar"],
+.toolbarpaletteitem-box[type="spring"][place="toolbar"] {
+ margin: 2px;
+}
+
+.toolbarpaletteitem-box[type="separator"][place="palette"] {
+ width: 2px;
+ height: 50px;
+}
+
+.toolbarpaletteitem-box[type="spacer"][place="palette"],
+.toolbarpaletteitem-box[type="spring"][place="palette"] {
+ margin-top: 0;
+ margin-bottom: 2px;
+ height: 32px;
+}
+
+.toolbarpaletteitem-box[type="spring"][place="palette"] {
+ background-position: center;
+ margin-left: 8px;
+ margin-right: 8px;
+}
+
+/* ..... drag and drop feedback ..... */
+
+toolbarpaletteitem[place="toolbar"] {
+ margin-left: -2px;
+ margin-right: -2px;
+ border-left: 2px solid transparent;
+ border-right: 2px solid transparent;
+}
+
+toolbarpaletteitem[dragover="left"] {
+ border-left-color: #000000;
+}
+
+toolbarpaletteitem[dragover="right"] {
+ border-right-color: #000000;
+}
+
+toolbar[iconsize="small"] toolbarspacer {
+ min-width: 24px !important;
+}
+
+toolbarspacer {
+ min-width: 32px !important;
+}
+
diff --git a/toolkit/themes/osx/global/toolbar/spring.png b/toolkit/themes/osx/global/toolbar/spring.png
new file mode 100644
index 0000000000..807e1f5e54
--- /dev/null
+++ b/toolkit/themes/osx/global/toolbar/spring.png
Binary files differ
diff --git a/toolkit/themes/osx/global/toolbar/toolbar-separator.png b/toolkit/themes/osx/global/toolbar/toolbar-separator.png
new file mode 100644
index 0000000000..21e17543a0
--- /dev/null
+++ b/toolkit/themes/osx/global/toolbar/toolbar-separator.png
Binary files differ
diff --git a/toolkit/themes/osx/global/toolbarbutton.css b/toolkit/themes/osx/global/toolbarbutton.css
new file mode 100644
index 0000000000..ab24387e3c
--- /dev/null
+++ b/toolkit/themes/osx/global/toolbarbutton.css
@@ -0,0 +1,124 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+toolbarbutton {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ margin: 0 2px;
+ padding: 3px 2px;
+ background-color: transparent;
+ text-shadow: 0 1px 0 rgba(255, 255, 255, 0.4);
+}
+
+toolbarbutton[open="true"],
+toolbarbutton:not([disabled="true"]):active:hover {
+ text-shadow: none;
+}
+
+.toolbarbutton-text {
+ margin: 0 !important; /* !important for overriding global.css */
+ padding: 0px;
+ text-align: center;
+ vertical-align: middle;
+}
+
+toolbarbutton[disabled="true"],
+toolbarbutton[disabled="true"]:hover,
+toolbarbutton[disabled="true"]:hover:active,
+toolbarbutton[disabled="true"][open="true"] {
+ color: -moz-mac-disabledtoolbartext !important;
+}
+
+/* ::::: toolbarbutton menu ::::: */
+
+.toolbarbutton-menu-dropmarker {
+ -moz-appearance: none !important;
+ border: none !important;
+ background-color: transparent !important;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+ padding: 0;
+ padding-inline-start: 2px;
+ width: auto;
+}
+
+.toolbarbutton-menu-dropmarker[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.png");
+ padding: 0;
+ padding-inline-start: 2px;
+}
+
+/* ::::: toolbarbutton menu-button ::::: */
+
+toolbarbutton[type="menu-button"] {
+ -moz-box-align: stretch;
+ -moz-box-orient: horizontal !important;
+}
+
+toolbarbutton[type="menu-button"],
+toolbarbutton[type="menu-button"]:hover,
+toolbarbutton[type="menu-button"]:hover:active,
+toolbarbutton[type="menu-button"][open="true"],
+toolbarbutton[type="menu-button"][disabled="true"],
+toolbarbutton[type="menu-button"][disabled="true"]:hover,
+toolbarbutton[type="menu-button"][disabled="true"]:hover:active {
+ background-color: transparent;
+}
+
+.toolbarbutton-menubutton-button {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ -moz-box-orient: vertical;
+ text-shadow: inherit;
+}
+
+/* ::::: toolbarbutton badged ::::: */
+
+.toolbarbutton-badge {
+ background-color: #d90000;
+ font-size: 9px;
+ padding: 1px 2px;
+ color: #fff;
+ border-radius: 2px;
+ box-shadow: 0 1px 0 hsla(0, 100%, 100%, .2) inset,
+ 0 -1px 0 hsla(0, 0%, 0%, .1) inset,
+ 0 1px 0 hsla(206, 50%, 10%, .2);
+ margin: -6px 0 0 !important;
+ margin-inline-end: -6px !important;
+ min-width: 14px;
+ max-width: 28px;
+ line-height: 10px;
+ text-align: center;
+ -moz-stack-sizing: ignore;
+}
+
+.toolbarbutton-badge:-moz-window-inactive {
+ background-color: rgb(230,230,230);
+ box-shadow: none;
+ color: rgb(192,192,192);
+}
+
+toolbar[mode="icons"] > *|* > .toolbarbutton-badge {
+ margin-inline-end: -10px !important;
+}
+
+/* .......... dropmarker .......... */
+
+.toolbarbutton-menubutton-dropmarker {
+ -moz-appearance: none;
+ border: none;
+ background-color: transparent !important;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.png");
+ width: auto;
+ padding: 0 5px;
+}
+
+.toolbarbutton-menubutton-dropmarker[disabled="true"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn-dis.png");
+}
+
+toolbarbutton.tabbable {
+ -moz-user-focus: normal !important;
+}
diff --git a/toolkit/themes/osx/global/tree.css b/toolkit/themes/osx/global/tree.css
new file mode 100644
index 0000000000..472a51fc91
--- /dev/null
+++ b/toolkit/themes/osx/global/tree.css
@@ -0,0 +1,296 @@
+/* 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 shared.inc
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+tree {
+ margin: 0px 4px;
+ color: -moz-DialogText;
+ background-color: #FFFFFF;
+ -moz-appearance: listbox;
+}
+
+/* ::::: tree focusring ::::: */
+
+.focusring > .tree-stack > .tree-rows > .tree-bodybox {
+ border: 1px solid transparent;
+}
+
+.focusring:focus > .tree-stack > .tree-rows > .tree-bodybox {
+ border: 1px solid -moz-mac-focusring;
+}
+
+
+/* ::::: tree rows ::::: */
+
+treechildren::-moz-tree-row {
+ border-top: 1px solid transparent;
+ height: 18px;
+ background-color: -moz-field;
+}
+
+treechildren:not(.autocomplete-treebody)::-moz-tree-row(multicol, odd) {
+ background-color: -moz-oddtreerow;
+}
+
+treechildren:not(.autocomplete-treebody)::-moz-tree-row(selected) {
+ background-color: -moz-mac-secondaryhighlight;
+}
+
+treechildren:not(.autocomplete-treebody)::-moz-tree-row(selected, focus) {
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+tree[seltype="cell"] > treechildren::-moz-tree-row,
+tree[seltype="text"] > treechildren::-moz-tree-row {
+ border-top: none;
+ background-color: transparent;
+}
+
+/* ::::: tree cells ::::: */
+
+treechildren::-moz-tree-cell {
+ padding: 0px 2px 0px 2px;
+}
+
+tree[seltype="cell"] > treechildren::-moz-tree-cell-text,
+tree[seltype="text"] > treechildren::-moz-tree-cell-text,
+treechildren::-moz-tree-cell-text {
+ color: inherit;
+}
+
+tree[seltype="cell"] > treechildren::-moz-tree-cell {
+ padding: 0px 1px 0px 1px;
+}
+
+tree[seltype="text"] > treechildren::-moz-tree-cell-text {
+ padding: 0px 1px 1px 1px;
+}
+
+treechildren::-moz-tree-cell-text(selected) {
+ color: -moz-DialogText;
+}
+
+tree[seltype="cell"] > treechildren::-moz-tree-cell(active, selected) {
+ background-color: -moz-mac-secondaryhighlight;
+
+}
+tree[seltype="cell"] > treechildren::-moz-tree-cell-text(active, selected) {
+ color: -moz-DialogText;
+}
+
+tree[seltype="text"] > treechildren::-moz-tree-cell-text(active, selected) {
+ background-color: -moz-mac-secondaryhighlight;
+ color: -moz-DialogText;
+}
+
+treechildren::-moz-tree-cell-text(selected, focus) {
+ color: HighlightText;
+}
+
+tree[seltype="cell"] > treechildren::-moz-tree-cell(active, selected, focus) {
+ background-color: Highlight;
+}
+tree[seltype="cell"] > treechildren::-moz-tree-cell-text(active, selected, focus) {
+ color: HighlightText;
+}
+
+tree[seltype="text"] > treechildren::-moz-tree-cell-text(active, selected, focus) {
+ background-color: Highlight;
+ color: HighlightText;
+}
+
+/* ::::: lines connecting cells ::::: */
+
+treechildren::-moz-tree-line {
+ /* XXX there should be no border on Mac, but trees currently
+ paint the line black by default, so I'll just leave this
+ for now. */
+ visibility: hidden;
+ border: 1px dotted grey;
+}
+
+
+/* ::::: tree separator ::::: */
+
+treechildren::-moz-tree-separator {
+ border-top: 1px dashed #C7C7C7;
+ margin: 0 2px;
+}
+
+
+/* ::::: drop feedback ::::: */
+
+tree[seltype="cell"] > treechildren::-moz-tree-cell(primary, dropOn),
+tree[seltype="text"] > treechildren::-moz-tree-cell(primary, dropOn),
+treechildren::-moz-tree-cell(primary, dropOn) {
+ background-color: #A1A1A1 !important;
+ color: #FFF !important;
+ background-image: none;
+}
+tree[seltype="cell"] > treechildren::-moz-tree-cell-text(primary, dropOn),
+tree[seltype="text"] > treechildren::-moz-tree-cell-text(primary, dropOn),
+treechildren::-moz-tree-cell-text(primary, dropOn) {
+ color: #FFF !important;
+}
+
+treechildren::-moz-tree-drop-feedback {
+ background-color: #A1A1A1;
+ width: 50px;
+ height: 2px;
+ margin-inline-start: 5px;
+}
+
+/* ::::: tree progress meter ::::: */
+
+treechildren::-moz-tree-progressmeter {
+ margin: 2px 4px;
+ border: 2px solid;
+ -moz-border-top-colors: #AAAAAA #000000;
+ -moz-border-right-colors: #FFFFFF #000000;
+ -moz-border-bottom-colors: #FFFFFF #000000;
+ -moz-border-left-colors: #AAAAAA #000000;
+}
+
+treechildren::-moz-tree-cell-text(progressmeter) {
+ margin: 2px 4px;
+ -moz-appearance: progressbar;
+}
+
+/* ::::: tree columns ::::: */
+
+treecol,
+treecolpicker {
+ -moz-appearance: treeheadercell;
+ -moz-box-align: center;
+ -moz-box-pack: center;
+ border: 2px solid;
+ -moz-border-top-colors: ThreeDHighlight ThreeDLightShadow;
+ -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-bottom-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-left-colors: ThreeDHighlight ThreeDLightShadow;
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+ padding: 0px 4px;
+}
+
+.treecol-image {
+ padding: 0px 1px;
+}
+
+.treecol-text {
+ margin: 0px !important;
+}
+
+treecol[hideheader="true"] {
+ -moz-appearance: none;
+ border: none;
+ padding: 0;
+ max-height: 0px;
+}
+
+/* ..... internal box ..... */
+
+treecol:hover:active,
+treecolpicker:hover:active {
+ border-top: 2px solid;
+ border-bottom: 1px solid;
+ border-inline-start: 2px solid;
+ border-inline-end: 1px solid;
+ -moz-border-top-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-right-colors: ThreeDDarkShadow;
+ -moz-border-bottom-colors: ThreeDDarkShadow;
+ -moz-border-left-colors: ThreeDDarkShadow ThreeDShadow;
+ background-color: #666666;
+}
+
+/* ::::: column drag and drop styles ::::: */
+
+treecol[dragging="true"] {
+ -moz-border-top-colors: ThreeDDarkShadow ThreeDShadow !important;
+ -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow!important;
+ -moz-border-bottom-colors: ThreeDDarkShadow ThreeDShadow !important;
+ -moz-border-left-colors: ThreeDDarkShadow ThreeDShadow !important;
+ padding: 0px 4px !important;
+ background-color: ThreeDShadow !important;
+ color: ThreeDHighlight !important;
+}
+
+treecol[insertafter="true"]:-moz-locale-dir(ltr),
+treecol[insertbefore="true"]:-moz-locale-dir(rtl) {
+ -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow;
+}
+
+treecol[insertafter="true"]:-moz-locale-dir(rtl),
+treecol[insertbefore="true"]:-moz-locale-dir(ltr) {
+ -moz-border-left-colors: ThreeDDarkShadow ThreeDShadow;
+}
+
+treechildren::-moz-tree-column(insertbefore) {
+ border-inline-start: 1px solid ThreeDShadow;
+}
+
+treechildren::-moz-tree-column(insertafter) {
+ border-inline-end: 1px solid ThreeDShadow;
+}
+
+/* ::::: column picker ::::: */
+
+.tree-columnpicker-icon {
+ list-style-image: url("chrome://global/skin/tree/columnpicker.gif");
+}
+
+/* ::::: twisty ::::: */
+
+treechildren::-moz-tree-twisty {
+ -moz-appearance: treetwisty;
+ padding-inline-end: 2px;
+}
+
+treechildren::-moz-tree-twisty(open) {
+ -moz-appearance: treetwistyopen;
+}
+
+treechildren::-moz-tree-twisty(Name, separator) {
+ -moz-appearance: none;
+}
+
+treechildren::-moz-tree-indentation {
+ width: 16px;
+}
+
+/* ::::: gridline style ::::: */
+
+treechildren.gridlines::-moz-tree-cell {
+ border-inline-end: 1px solid GrayText;
+ border-bottom: 1px solid GrayText;
+}
+
+treechildren.gridlines::-moz-tree-row {
+ border: none;
+}
+
+/* ::::: editable tree ::::: */
+
+.tree-input {
+ -moz-appearance: none;
+ border-width: 0;
+ box-shadow: @focusRingShadow@;
+ margin: 0;
+ margin-inline-start: -2px;
+ padding: 2px 1px 1px;
+}
+
+treechildren::-moz-tree-cell(active, selected, focus, editing),
+tree[seltype="cell"] > treechildren::-moz-tree-cell(active, selected, focus, editing),
+tree[seltype="text"] > treechildren::-moz-tree-cell(active, selected, focus, editing) {
+ background-color: transparent;
+ border: none;
+}
+
+treechildren::-moz-tree-cell-text(active, selected, editing) {
+ opacity: 0;
+}
diff --git a/toolkit/themes/osx/global/tree/arrow-disclosure.svg b/toolkit/themes/osx/global/tree/arrow-disclosure.svg
new file mode 100644
index 0000000000..0fee858071
--- /dev/null
+++ b/toolkit/themes/osx/global/tree/arrow-disclosure.svg
@@ -0,0 +1,28 @@
+<!-- This Source Code Form is subject to the terms of the Mozilla Public
+ - License, v. 2.0. If a copy of the MPL was not distributed with this
+ - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
+
+ <style>
+ .icon:not(:target) {
+ display: none;
+ }
+
+ .icon {
+ fill: #8c8c8c;
+ }
+
+ .icon.white {
+ fill: #fff;
+ }
+ </style>
+
+ <polygon id="arrow-disclosure-collapsed" class="icon" points="4,4 12,8.5 4,13" />
+ <polygon id="arrow-disclosure-collapsed-rtl" class="icon" points="4,8.5 12,4 12,13" />
+ <polygon id="arrow-disclosure-collapsed-inverted" class="icon white" points="4,4 12,8.5 4,13" />
+ <polygon id="arrow-disclosure-collapsed-inverted-rtl" class="icon white" points="4,8.5 12,4 12,13" />
+
+ <polygon id="arrow-disclosure-expanded" class="icon" points="3,5 12,5 7.5,13" />
+ <polygon id="arrow-disclosure-expanded-inverted" class="icon white" points="3,5 12,5 7.5,13" />
+
+</svg>
diff --git a/toolkit/themes/osx/global/tree/columnpicker.gif b/toolkit/themes/osx/global/tree/columnpicker.gif
new file mode 100644
index 0000000000..167f3789af
--- /dev/null
+++ b/toolkit/themes/osx/global/tree/columnpicker.gif
Binary files differ
diff --git a/toolkit/themes/osx/global/tree/folder.png b/toolkit/themes/osx/global/tree/folder.png
new file mode 100644
index 0000000000..8f21ff790a
--- /dev/null
+++ b/toolkit/themes/osx/global/tree/folder.png
Binary files differ
diff --git a/toolkit/themes/osx/global/tree/folder@2x.png b/toolkit/themes/osx/global/tree/folder@2x.png
new file mode 100644
index 0000000000..c07acf5ffb
--- /dev/null
+++ b/toolkit/themes/osx/global/tree/folder@2x.png
Binary files differ
diff --git a/toolkit/themes/osx/global/viewbuttons.css b/toolkit/themes/osx/global/viewbuttons.css
new file mode 100644
index 0000000000..bb407a64ed
--- /dev/null
+++ b/toolkit/themes/osx/global/viewbuttons.css
@@ -0,0 +1,36 @@
+/* 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 shared.inc
+
+#topBar {
+ -moz-appearance: toolbar;
+}
+
+.viewGroupWrapper {
+ -moz-box-align: center;
+ -moz-box-pack: center;
+}
+
+#viewGroup {
+ margin: 4px 0 9px;
+}
+
+#viewGroup > radio,
+#viewGroup > toolbarbutton {
+ -moz-box-orient: vertical;
+ -moz-box-align: center;
+ -moz-appearance: toolbarbutton;
+ font: menu;
+ text-shadow: @loweredShadow@;
+ margin: 0;
+ padding: 0 1px;
+ height: 22px;
+}
+
+#viewGroup > radio[selected=true],
+#viewGroup > toolbarbutton[checked=true] {
+ color: #FFF !important;
+ text-shadow: rgba(0, 0, 0, 0.4) 0 1px;
+}
diff --git a/toolkit/themes/osx/global/wizard.css b/toolkit/themes/osx/global/wizard.css
new file mode 100644
index 0000000000..9c5e6200ce
--- /dev/null
+++ b/toolkit/themes/osx/global/wizard.css
@@ -0,0 +1,62 @@
+/* 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/. */
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+wizard {
+ padding: 14px;
+}
+
+#header {
+ display: inline !important;
+}
+
+.wizard-header {
+ -moz-appearance: dialog;
+}
+
+.wizard-header-box-1 {
+ color: #000;
+}
+
+.wizard-header-box-text {
+ padding: 6px 10px;
+ font: menu;
+ font-weight: bold;
+}
+
+.wizard-header-label {
+ margin-left: 23px;
+ font-weight: bold;
+}
+
+.wizard-header-box-icon {
+ margin-top: 3px;
+ margin-inline-end: 20px;
+ margin-bottom: 0;
+ margin-inline-start: 3px;
+}
+
+wizard[branded="true"] .wizard-header-icon {
+ list-style-image: url("chrome://branding/content/icon48.png");
+}
+
+.wizard-page-box {
+ padding: 15px 23px;
+ -moz-appearance: dialog;
+}
+
+.wizard-buttons-separator {
+ margin: 0 !important;
+ border-bottom: 1px solid #fff !important;
+}
+
+.wizard-buttons-btm {
+ padding: 3px 6px 6px;
+}
+
+.wizard-button {
+ font: menu !important;
+}
+
diff --git a/toolkit/themes/osx/mochitests/.eslintrc.js b/toolkit/themes/osx/mochitests/.eslintrc.js
new file mode 100644
index 0000000000..2c669d844e
--- /dev/null
+++ b/toolkit/themes/osx/mochitests/.eslintrc.js
@@ -0,0 +1,7 @@
+"use strict";
+
+module.exports = {
+ "extends": [
+ "../../../../testing/mochitest/chrome.eslintrc.js"
+ ]
+};
diff --git a/toolkit/themes/osx/mochitests/chrome.ini b/toolkit/themes/osx/mochitests/chrome.ini
new file mode 100644
index 0000000000..b7c425bc70
--- /dev/null
+++ b/toolkit/themes/osx/mochitests/chrome.ini
@@ -0,0 +1,3 @@
+[DEFAULT]
+
+[test_bug510426.xul]
diff --git a/toolkit/themes/osx/mochitests/test_bug510426.xul b/toolkit/themes/osx/mochitests/test_bug510426.xul
new file mode 100644
index 0000000000..4294ce42de
--- /dev/null
+++ b/toolkit/themes/osx/mochitests/test_bug510426.xul
@@ -0,0 +1,54 @@
+<?xml version="1.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/. -->
+
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=510426
+-->
+<window title="Mozilla Bug 510426"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ align="start">
+ <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+<body xmlns="http://www.w3.org/1999/xhtml">
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=510426">Mozilla Bug 510426</a>
+</body>
+
+<notificationbox id="nb" width="300" height="100">
+ <box width="100" height="100" id="overflowGenerator"/>
+</notificationbox>
+
+<script class="testbody" type="application/javascript">
+<![CDATA[
+
+/** Test for Bug 510426 **/
+SimpleTest.waitForExplicitFinish();
+
+function openNotification() {
+ var nb = document.getElementById("nb");
+ var n = nb.appendNotification("Notification", "", null,
+ nb.PRIORITY_WARNING_LOW, [{
+ label: "Button",
+ accesskey: "u",
+ callback: null,
+ popup: null
+ }]);
+ n.addEventListener("transitionend", function (event) {
+ if (event.propertyName == "margin-top") {
+ setTimeout(function () {
+ is(n.getBoundingClientRect().height, 27, "notification bar has wrong height");
+ SimpleTest.finish();
+ }, 0);
+ }
+ }, false);
+}
+
+window.onload = openNotification;
+
+]]>
+</script>
+</window>
diff --git a/toolkit/themes/osx/moz.build b/toolkit/themes/osx/moz.build
new file mode 100644
index 0000000000..fab1daff25
--- /dev/null
+++ b/toolkit/themes/osx/moz.build
@@ -0,0 +1,8 @@
+# -*- 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/.
+
+DIRS += ['global', 'mozapps']
+
+MOCHITEST_CHROME_MANIFESTS += ['mochitests/chrome.ini']
diff --git a/toolkit/themes/osx/mozapps/downloads/buttons.png b/toolkit/themes/osx/mozapps/downloads/buttons.png
new file mode 100644
index 0000000000..04da26a252
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/downloads/buttons.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/downloads/downloadIcon.png b/toolkit/themes/osx/mozapps/downloads/downloadIcon.png
new file mode 100644
index 0000000000..42dc4943d7
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/downloads/downloadIcon.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/downloads/downloads.css b/toolkit/themes/osx/mozapps/downloads/downloads.css
new file mode 100644
index 0000000000..3ba246c1fc
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/downloads/downloads.css
@@ -0,0 +1,123 @@
+/* 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 ../../global/shared.inc
+
+#downloadView {
+ -moz-appearance: none;
+ margin: 0;
+ padding: 0;
+ border-width: 0;
+}
+
+/* Download View Items */
+richlistitem[type="download"] {
+ padding: 5px;
+ min-height: 44px !important;
+ border: 1px solid transparent;
+}
+
+richlistitem[type="download"]:not([selected="true"]):nth-child(odd) {
+ background-color: -moz-oddtreerow;
+}
+
+richlistitem[type="download"] .dateTime,
+richlistitem[type="download"] .status {
+ font-size: smaller;
+ color: #555;
+}
+
+richlistitem[selected="true"][type="download"] {
+ outline: none;
+}
+
+richlistbox:focus > richlistitem[selected="true"][type="download"] .dateTime,
+richlistbox:focus > richlistitem[selected="true"][type="download"] .status {
+ color: highlighttext;
+}
+
+
+richlistitem[type="download"] button {
+ -moz-appearance: none;
+ min-height: 16px;
+ min-width: 16px;
+ max-height: 16px;
+ max-width: 16px;
+ padding: 0;
+ margin: 0 1px 0 1px;
+}
+
+/**
+ * Images for buttons in the interface
+ */
+richlistitem[type="download"] button {
+ list-style-image: url(chrome://mozapps/skin/downloads/buttons.png);
+}
+.cancel {
+ -moz-image-region: rect(0px, 16px, 16px, 0px);
+}
+.cancel:hover {
+ -moz-image-region: rect(0px, 32px, 16px, 16px);
+}
+.cancel:hover:active {
+ -moz-image-region: rect(0px, 48px, 16px, 32px);
+}
+
+.pause {
+ -moz-image-region: rect(48px, 16px, 64px, 0px);
+}
+.pause:hover {
+ -moz-image-region: rect(48px, 32px, 64px, 16px);
+}
+.pause:not([disabled="true"]):hover:active {
+ -moz-image-region: rect(48px, 48px, 64px, 32px);
+}
+.pause[disabled="true"] {
+ -moz-image-region: rect(48px, 16px, 64px, 0px);
+}
+
+.resume {
+ -moz-image-region: rect(16px, 16px, 32px, 0px);
+}
+.resume:hover {
+ -moz-image-region: rect(16px, 32px, 32px, 16px);
+}
+.resume:hover:active {
+ -moz-image-region: rect(16px, 48px, 32px, 32px);
+}
+
+.retry {
+ -moz-image-region: rect(32px, 16px, 48px, 0px);
+}
+.retry:hover {
+ -moz-image-region: rect(32px, 32px, 48px, 16px);
+}
+.retry:hover:active {
+ -moz-image-region: rect(32px, 48px, 48px, 32px);
+}
+
+.blockedIcon {
+ list-style-image: url(chrome://global/skin/icons/Error.png);
+}
+
+/* prevent flickering when changing states */
+.downloadTypeIcon {
+ height: 32px;
+ width: 32px;
+ padding-inline-end: 2px;
+}
+
+#search {
+ -moz-box-pack: end;
+ padding-inline-end: 4px;
+ -moz-appearance: statusbar;
+}
+
+#clearListButton {
+ -moz-appearance: toolbarbutton;
+ min-height: 18px;
+ min-width: 0;
+ margin: 0 6px;
+ text-shadow: @loweredShadow@;
+}
diff --git a/toolkit/themes/osx/mozapps/downloads/unknownContentType.css b/toolkit/themes/osx/mozapps/downloads/unknownContentType.css
new file mode 100644
index 0000000000..28d01b57af
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/downloads/unknownContentType.css
@@ -0,0 +1,30 @@
+/* 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/. */
+
+#unknownContentType {
+ font: menu;
+}
+
+description {
+ font-weight: bold;
+}
+
+.plain {
+ background-color: transparent;
+ background-image: none;
+ border: none;
+}
+
+#contentTypeImage {
+ margin-right: 3px;
+ width: 16px;
+}
+
+#container > .small-indent {
+ margin-left: 0px;
+}
+
+.small-indent label {
+ margin-left: 0px;
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/about.css b/toolkit/themes/osx/mozapps/extensions/about.css
new file mode 100644
index 0000000000..cfabd47dba
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/about.css
@@ -0,0 +1,78 @@
+/* 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/. */
+
+#genericAbout {
+ padding: 0px;
+ min-height: 200px;
+ max-height: 400px;
+ width: 30em;
+}
+
+#clientBox {
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+.basic-info {
+ padding: 10px;
+}
+
+#extensionIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ max-width: 64px;
+ max-height: 64px;
+ -moz-margin-end: 6px;
+}
+
+#genericAbout[addontype="theme"] #extensionIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric.png");
+}
+
+#genericAbout[addontype="locale"] #extensionIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/localeGeneric.png");
+}
+
+#genericAbout[addontype="plugin"] #extensionIcon {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.png");
+}
+
+#genericAbout[addontype="dictionary"] #extensionIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric.png");
+}
+
+#extensionName {
+ font-size: 200%;
+ font-weight: bolder;
+}
+
+#extensionVersion {
+ font-weight: bold;
+}
+
+#extensionDescription {
+ margin-top: 4px;
+}
+
+#groove {
+ margin-top: 8px;
+}
+
+#extensionDetailsBox {
+ overflow: auto;
+ min-height: 100px;
+}
+
+.boxIndent {
+ -moz-margin-start: 18px;
+}
+
+#extensionCreator, .contributor {
+ margin: 0px;
+}
+
+.sectionTitle {
+ padding: 2px 0px 3px 0px;
+ margin-top: 3px;
+ font-weight: bold;
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/alerticon-error.png b/toolkit/themes/osx/mozapps/extensions/alerticon-error.png
new file mode 100644
index 0000000000..8740e4911a
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/alerticon-error.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/alerticon-info-negative.png b/toolkit/themes/osx/mozapps/extensions/alerticon-info-negative.png
new file mode 100644
index 0000000000..2c5f628ab6
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/alerticon-info-negative.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/alerticon-info-positive.png b/toolkit/themes/osx/mozapps/extensions/alerticon-info-positive.png
new file mode 100644
index 0000000000..a186c6b7ad
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/alerticon-info-positive.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/alerticon-warning.png b/toolkit/themes/osx/mozapps/extensions/alerticon-warning.png
new file mode 100644
index 0000000000..75ea826f91
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/alerticon-warning.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/blocklist.css b/toolkit/themes/osx/mozapps/extensions/blocklist.css
new file mode 100644
index 0000000000..b241c94468
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/blocklist.css
@@ -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/. */
+
+richlistitem {
+ padding-top: 6px;
+ padding-bottom: 6px;
+ -moz-padding-start: 7px;
+ -moz-padding-end: 7px;
+ border-bottom: 1px solid #C0C0C0;
+}
+
+.addon-name-version {
+ font-size: 110%;
+}
+
+.blockedLabel {
+ font-weight: bold;
+ font-style: italic;
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/cancel.png b/toolkit/themes/osx/mozapps/extensions/cancel.png
new file mode 100644
index 0000000000..0d98ab2359
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/cancel.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-available.png b/toolkit/themes/osx/mozapps/extensions/category-available.png
new file mode 100644
index 0000000000..d1b737ab05
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-available.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-dictionaries.png b/toolkit/themes/osx/mozapps/extensions/category-dictionaries.png
new file mode 100644
index 0000000000..54ae4f93fc
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-dictionaries.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-discover.png b/toolkit/themes/osx/mozapps/extensions/category-discover.png
new file mode 100644
index 0000000000..a6f5b49b37
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-discover.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-experiments.png b/toolkit/themes/osx/mozapps/extensions/category-experiments.png
new file mode 100644
index 0000000000..a9d00545ef
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-experiments.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-plugins.png b/toolkit/themes/osx/mozapps/extensions/category-plugins.png
new file mode 100644
index 0000000000..5c4d8bf471
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-plugins.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-recent.png b/toolkit/themes/osx/mozapps/extensions/category-recent.png
new file mode 100644
index 0000000000..7ecfc7d4c8
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-recent.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-search.png b/toolkit/themes/osx/mozapps/extensions/category-search.png
new file mode 100644
index 0000000000..52e91a7cea
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-search.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-searchengines.png b/toolkit/themes/osx/mozapps/extensions/category-searchengines.png
new file mode 100644
index 0000000000..b893cb48a2
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-searchengines.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/category-service.png b/toolkit/themes/osx/mozapps/extensions/category-service.png
new file mode 100644
index 0000000000..997c8541ca
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/category-service.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/dictionaryGeneric-16.png b/toolkit/themes/osx/mozapps/extensions/dictionaryGeneric-16.png
new file mode 100644
index 0000000000..4ad1a1a825
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/dictionaryGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/dictionaryGeneric.png b/toolkit/themes/osx/mozapps/extensions/dictionaryGeneric.png
new file mode 100644
index 0000000000..54ae4f93fc
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/dictionaryGeneric.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/discover-logo.png b/toolkit/themes/osx/mozapps/extensions/discover-logo.png
new file mode 100644
index 0000000000..cd50735a89
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/discover-logo.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/eula.css b/toolkit/themes/osx/mozapps/extensions/eula.css
new file mode 100644
index 0000000000..5fb2c52df2
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/eula.css
@@ -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/. */
+
+#icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ max-width: 48px;
+ max-height: 48px;
+ -moz-margin-end: 6px;
+}
+
+#eula-dialog[addontype="theme"] #icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric.png");
+}
+
+#eula-dialog[addontype="locale"] #icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/localeGeneric.png");
+}
+
+#eula-dialog[addontype="plugin"] #icon {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.png");
+}
+
+#eula-dialog[addontype="dictionary"] #icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric.png");
+}
+
+#heading-container {
+ -moz-box-align: center;
+}
+
+#heading {
+ font-size: 120%;
+}
+
+#eula {
+ -moz-appearance: none;
+ color: -moz-FieldText;
+ background-color: -moz-Field;
+ margin: 1em;
+ border: 1px solid;
+ -moz-border-top-colors: ActiveBorder;
+ -moz-border-right-colors: ActiveBorder;
+ -moz-border-bottom-colors: ActiveBorder;
+ -moz-border-left-colors: ActiveBorder;
+}
+
diff --git a/toolkit/themes/osx/mozapps/extensions/experimentGeneric.png b/toolkit/themes/osx/mozapps/extensions/experimentGeneric.png
new file mode 100644
index 0000000000..a9d00545ef
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/experimentGeneric.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/extensionGeneric-16.png b/toolkit/themes/osx/mozapps/extensions/extensionGeneric-16.png
new file mode 100644
index 0000000000..fc6c8a2583
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/extensionGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/extensionGeneric.png b/toolkit/themes/osx/mozapps/extensions/extensionGeneric.png
new file mode 100644
index 0000000000..6a76774c7b
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/extensionGeneric.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/extensions.css b/toolkit/themes/osx/mozapps/extensions/extensions.css
new file mode 100644
index 0000000000..474cb12d10
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/extensions.css
@@ -0,0 +1,1206 @@
+/* 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 url("chrome://global/skin/inContentUI.css");
+
+%include ../../global/shared.inc
+
+@namespace html url("http://www.w3.org/1999/xhtml");
+
+
+/*** global warnings ***/
+
+.global-warning-container {
+ overflow-x: hidden;
+}
+
+.global-warning {
+ -moz-box-align: center;
+ padding: 0 8px;
+ color: #916D15;
+ font-weight: bold;
+}
+
+.global-warning,
+.global-warning .button-link {
+ text-shadow: @loweredShadow@;
+}
+
+#addons-page[warning] .global-warning-container {
+ background-color: rgba(255, 255, 0, 0.1);
+ background-image: url("chrome://mozapps/skin/extensions/stripes-warning.png");
+ background-repeat: repeat-x;
+}
+
+#detail-view .global-warning {
+ padding: 4px 12px;
+ min-height: 31px;
+ border-bottom: 1px solid rgba(50, 65, 92, 0.4);
+}
+
+@media (max-width: 600px) {
+ .global-warning-text {
+ display: none;
+ }
+
+ .global-warning .warning-icon {
+ background-color: rgba(255, 255, 255, 0.7);
+ box-shadow: 0px 0px 2px 4px rgba(255, 255, 255, 0.7);
+ border-radius: 10px;
+ }
+}
+
+/*** global informations ***/
+#addons-page .global-info-container {
+ background-color: #e3e6eb;
+ border-top-right-radius: 5px;
+ border-top-left-radius: 5px;
+}
+
+/* Plugins aren't yet disabled by safemode (bug 342333),
+ so don't show that warning when viewing plugins. */
+#addons-page[warning="safemode"] .view-pane[type="plugin"] .global-warning-container,
+#addons-page[warning="safemode"] #detail-view[loading="true"] .global-warning-container {
+ background-color: inherit;
+ background-image: none;
+}
+
+
+/*** notification icons ***/
+
+.warning-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/alerticon-warning.png");
+ width: 16px;
+ height: 15px;
+ margin: 3px 0;
+}
+
+.error-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/alerticon-error.png");
+ width: 16px;
+ height: 15px;
+ margin: 3px 0;
+}
+
+.pending-icon,
+.info-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/alerticon-info-positive.png");
+ width: 16px;
+ height: 15px;
+ margin: 3px 0;
+}
+
+.addon-view[pending="disable"] .pending-icon,
+.addon-view[pending="uninstall"] .pending-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/alerticon-info-negative.png");
+ width: 16px;
+ height: 15px;
+ margin: 3px 0;
+}
+
+
+/*** view alert boxes ***/
+
+.alert-container {
+ -moz-box-align: center;
+}
+
+.alert-spacer-before {
+ -moz-box-flex: 1;
+}
+
+.alert-spacer-after {
+ -moz-box-flex: 3;
+}
+
+.alert {
+ -moz-box-align: center;
+ padding: 10px;
+ color: #373D48;
+ font-size: 12px;
+ border: 1px solid #A8B8D1;
+ border-radius: 8px;
+ background-image: linear-gradient(rgba(255, 255, 255, 0.7), rgba(236, 241, 247, 0.7));
+ background-clip: padding-box;
+ box-shadow: 0 -3px 0 rgba(58, 78, 103, 0.05) inset,
+ 0 3px 0 rgba(175, 195, 220, 0.3);
+}
+
+.alert .alert-title {
+ font-weight: bold;
+ font-size: 200%;
+ margin-bottom: 15px;
+}
+
+.alert button {
+ margin: 1em 2em;
+}
+
+.loading {
+ list-style-image: url("chrome://global/skin/icons/loading_16.png");
+ padding-left: 20px;
+ padding-right: 20px;
+}
+
+
+
+/*** category selector ***/
+
+#categories {
+ -moz-appearance: none;
+ border: none;
+ -moz-margin-end: -1px;
+ background-color: transparent;
+ position: relative;
+ margin-top: 31px;
+}
+
+.category {
+ -moz-appearance: none;
+ color: #252F3B;
+ border-width: 1px;
+ border-style: solid;
+ border-color: transparent;
+ padding: 10px 4px;
+ -moz-box-align: center;
+ overflow: hidden;
+ min-height: 0;
+}
+
+.category:-moz-locale-dir(ltr) {
+ border-top-left-radius: 5px;
+ border-bottom-left-radius: 5px;
+}
+
+.category:-moz-locale-dir(rtl) {
+ border-top-right-radius: 5px;
+ border-bottom-right-radius: 5px;
+}
+
+.category[disabled] {
+ border-top: 0;
+ border-bottom: 0;
+ height: 0;
+ opacity: 0;
+ transition-property: height, opacity;
+ transition-duration: 1s, 0.8s;
+}
+
+.category:not([disabled]) {
+ height: 52px;
+ transition-property: height, opacity;
+ transition-duration: 1s, 0.8s;
+}
+
+.category[selected] {
+ background-color: rgba(255, 255, 255, 0.35);
+ color: -moz-dialogtext;
+ border-color: rgba(50, 65, 92, 0.4);
+ -moz-border-end-color: #C9CFD7;
+}
+
+.category-name {
+ font-size: 150%;
+}
+
+/* Maximize the size of the viewport when the window is small */
+@media (max-width: 800px) {
+ .category-name {
+ display: none;
+ }
+}
+
+.category-badge {
+ background-color: #55D4FF;
+ padding: 2px 8px;
+ margin: 6px 0;
+ border-radius: 10000px;
+ color: #FFF;
+ font-weight: bold;
+ text-align: center;
+}
+
+.category-badge[value="0"] {
+ visibility: hidden;
+}
+
+.category-icon {
+ width: 32px;
+ height: 32px;
+ -moz-margin-start: 6px;
+}
+
+#category-search > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-search.png");
+}
+#category-discover > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-discover.png");
+}
+#category-locale > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-languages.png");
+}
+#category-searchengine > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-searchengines.png");
+}
+#category-extension > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-extensions.png");
+}
+#category-service > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-service.png");
+}
+#category-theme > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-themes.png");
+}
+#category-plugin > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-plugins.png");
+}
+#category-dictionary > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-dictionaries.png");
+}
+#category-experiment > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-experiments.png");
+}
+#category-availableUpdates > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-available.png");
+}
+#category-recentUpdates > .category-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/category-recent.png");
+}
+
+
+/*** header ***/
+
+#header {
+ margin-bottom: 18px;
+}
+
+.nav-button {
+ list-style-image: url(chrome://mozapps/skin/extensions/navigation.png);
+}
+
+#back-btn:-moz-locale-dir(ltr),
+#forward-btn:-moz-locale-dir(rtl) {
+ border-top-right-radius: 0;
+ border-bottom-right-radius: 0;
+ border-right: none;
+ -moz-image-region: rect(0, 20px, 20px, 0);
+ padding-right: 3px;
+}
+
+#back-btn:-moz-locale-dir(rtl),
+#forward-btn:-moz-locale-dir(ltr) {
+ border-top-left-radius: 0;
+ border-bottom-left-radius: 0;
+ -moz-image-region: rect(0, 40px, 20px, 20px);
+ padding-left: 3px;
+}
+
+#header-utils-btn {
+ list-style-image: url("chrome://mozapps/skin/extensions/utilities.svg#utilities");
+ -moz-margin-end: 18px;
+}
+
+#header-utils-btn > .toolbarbutton-menu-dropmarker {
+ list-style-image: url("chrome://mozapps/skin/extensions/toolbarbutton-dropmarker.png");
+ padding: 0;
+ -moz-margin-start: 2px;
+}
+
+#header-search {
+ margin: 0;
+ -moz-appearance: none;
+ padding: 3px 5px 2px;
+ border: 1px solid rgba(60,73,97,0.5);
+ border-radius: 10000px;
+ box-shadow: inset 0 1px 1px rgba(0,0,0,0.15), 0 1px rgba(255,255,255,0.25);
+ background: linear-gradient(rgba(255,255,255,0.2), rgba(255,255,255,0.3));
+ background-clip: padding-box;
+}
+
+@media (max-width: 600px) {
+ #header-search {
+ width: 12em;
+ }
+}
+
+#header-search[focused] {
+ box-shadow: @focusRingShadow@, inset 0 1px 1px rgba(0,0,0,0.15);
+ border-color: -moz-mac-focusring;
+}
+
+#header-search > .textbox-input-box {
+ -moz-padding-start: 15px;
+ background: url("chrome://mozapps/skin/extensions/search.png") left no-repeat;
+}
+
+#header-search > .textbox-input-box:-moz-locale-dir(rtl) {
+ background-position: right;
+}
+
+#header-search > .textbox-input-box > html|*.textbox-input::-moz-placeholder {
+ color: #5C6470;
+ opacity: 1.0;
+}
+
+.view-header {
+ padding: 4px;
+ margin: 0;
+ min-height: 31px;
+ border-bottom: 1px solid rgba(50, 65, 92, 0.4);
+ background-image: linear-gradient(rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0.05));
+}
+
+
+/*** sorters ***/
+
+.sort-controls {
+ -moz-appearance: none;
+}
+
+.sorter {
+ -moz-appearance: none;
+ border: none;
+ color: #41434B;
+ background-color: transparent;
+ border-radius: 10000px;
+ padding: 0 6px;
+ margin: 0 6px;
+ min-width: 12px !important;
+ -moz-box-direction: reverse;
+}
+
+.sorter[checkState="1"],
+.sorter[checkState="2"],
+.sorter:active:hover {
+ text-shadow: @loweredShadow@;
+ background-color: #C0C3CB;
+ box-shadow: inset #A3A6AC 0 1px 1px, @loweredShadow@;
+}
+
+.sorter:hover {
+ text-shadow: @loweredShadow@;
+ background-color: #C0C3CB;
+}
+
+.sorter[checkState="1"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+}
+
+.sorter[checkState="2"] {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+
+.sorter .button-icon {
+ -moz-margin-start: 4px;
+}
+
+
+/*** discover view ***/
+
+.discover-spacer-before,
+.discover-spacer-after {
+ -moz-box-flex: 1;
+}
+
+#discover-error .alert {
+ max-width: 45em;
+ -moz-box-flex: 1;
+}
+
+.discover-logo {
+ list-style-image: url("chrome://mozapps/skin/extensions/discover-logo.png");
+ -moz-margin-end: 15px;
+}
+
+.discover-title {
+ font-weight: bold;
+ font-size: 24px;
+ font-family: MetaWebPro-Book, "Trebuchet MS", sans-serif;
+ margin: 0 0 15px 0;
+}
+
+.discover-description {
+ text-align: justify;
+ margin: 0 0 15px 0;
+}
+
+.discover-footer {
+ text-align: justify;
+}
+
+
+/*** list ***/
+
+.list {
+ -moz-appearance: none;
+ margin: 0;
+ border: none;
+ background-color: transparent;
+}
+
+.addon {
+ border-bottom: 1px solid #B6B1B9;
+ padding: 5px;
+ color: #373D48;
+}
+
+.details {
+ cursor: pointer;
+ margin: 0;
+ -moz-margin-start: 10px;
+}
+
+.icon-container {
+ width: 48px;
+ height: 48px;
+ margin: 3px 7px;
+ -moz-box-align: center;
+ -moz-box-pack: center;
+}
+
+.icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+ max-width: 48px;
+ max-height: 48px;
+}
+
+.addon[active="false"] .icon {
+ filter: grayscale(1);
+}
+
+.addon-view[type="theme"] .icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric.png");
+}
+
+.addon-view[type="locale"] .icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/localeGeneric.png");
+}
+
+.addon-view[type="plugin"] .icon {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.png");
+}
+
+.addon-view[type="dictionary"] .icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric.png");
+}
+
+.addon-view[type="experiment"] .icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/experimentGeneric.png");
+}
+
+.name-container {
+ font-size: 150%;
+ margin-bottom: 0;
+ font-weight: bold;
+ color: #000;
+ text-shadow: @loweredShadow@;
+ -moz-box-align: end;
+ -moz-box-flex: 1;
+}
+
+.creator {
+ font-weight: bold;
+}
+
+.creator .text-link {
+ color: #0066CC;
+}
+
+.description-container {
+ margin-top: 8px;
+ -moz-margin-start: 6px;
+ -moz-box-align: center;
+}
+
+.description {
+ margin: 0;
+}
+
+.warning,
+.pending,
+.error {
+ -moz-margin-start: 48px;
+ font-weight: bold;
+ text-shadow: @loweredShadow@;
+ -moz-box-align: center;
+}
+
+.content-container,
+.basicinfo-container {
+ -moz-box-align: start;
+}
+
+.addon[status="installing"] > .content-container {
+ -moz-box-align: stretch;
+}
+
+.update-info-container {
+ -moz-box-align: center;
+}
+
+.advancedinfo-container,
+.update-available {
+ -moz-box-align: end;
+}
+
+.install-status-container {
+ -moz-box-pack: end;
+ -moz-box-align: end;
+}
+
+.name-outer-container {
+ -moz-box-pack: center;
+}
+
+.relnotes-toggle-container,
+.icon-outer-container {
+ -moz-box-pack: start;
+}
+
+.status-container,
+.control-container {
+ -moz-box-pack: end;
+}
+
+.addon-view .warning {
+ color: #916D15;
+}
+
+.addon-view .error {
+ color: #864441;
+}
+
+.addon-view .pending {
+ color: #1B7123;
+}
+
+.addon-view[pending="disable"] .pending,
+.addon-view[pending="uninstall"] .pending {
+ color: #62666E;
+}
+
+.addon-view[notification="warning"] {
+ background-image: linear-gradient(rgba(255, 255, 0, 0.2), rgba(255, 255, 0, 0.1));
+}
+
+.addon-view[notification="error"] {
+ background-image: linear-gradient(rgba(255, 0, 0, 0.2), rgba(255, 0, 0, 0.1));
+}
+
+.addon-view[notification="info"] {
+ background-image: linear-gradient(rgba(0, 0, 255, 0.2), rgba(0, 0, 255, 0.1));
+}
+
+.addon-view[pending="enable"],
+.addon-view[pending="upgrade"],
+.addon-view[pending="install"] {
+ background-image: linear-gradient(rgba(0, 255, 0, 0.2), rgba(0, 255, 0, 0.1));
+}
+
+.addon-view[pending="disable"],
+.addon-view[pending="uninstall"] {
+ background-image: linear-gradient(rgba(128, 128, 128, 0.2), rgba(128, 128, 128, 0.1));
+}
+
+.addon .relnotes-container {
+ -moz-box-align: start;
+ height: 0;
+ overflow: hidden;
+ opacity: 0;
+ transition-property: height, opacity;
+ transition-duration: 0.5s, 0.5s;
+}
+
+.addon[show-relnotes] .relnotes-container {
+ opacity: 1;
+ transition-property: height, opacity;
+ transition-duration: 0.5s, 0.5s;
+}
+
+.addon .relnotes-header {
+ font-weight: bold;
+ margin: 10px 0;
+}
+
+.addon .relnotes-toggle {
+ -moz-appearance: none;
+ border: none;
+ background: transparent;
+ font-weight: bold;
+ -moz-box-direction: reverse;
+ cursor: pointer;
+ list-style-image: url("chrome://global/skin/arrow/arrow-dn.gif");
+}
+
+.addon .relnotes-toggle > .button-box > .button-icon {
+ -moz-padding-start: 4px;
+}
+
+.addon[show-relnotes] .relnotes-toggle {
+ list-style-image: url("chrome://global/skin/arrow/arrow-up.gif");
+}
+
+.addon[active="false"] {
+ background-color: rgba(135, 135, 135, 0.1);
+ background-image: linear-gradient(rgba(135, 135, 135, 0),
+ rgba(135, 135, 135, 0.1));
+}
+
+.addon-view[active="false"],
+.addon-view[active="false"] .name-container {
+ color: #686A6B;
+}
+
+.addon-view[notification="warning"] {
+ background-image: url("chrome://mozapps/skin/extensions/stripes-warning.png"),
+ linear-gradient(rgba(255, 255, 0, 0.04),
+ rgba(255, 255, 0, 0));
+ background-repeat: repeat-x;
+}
+
+.addon-view[notification="warning"][native="false"] {
+ background-image: url("chrome://mozapps/skin/extensions/stripes-compatibility.png"),
+ linear-gradient(rgba(255, 128, 0, 0.04),
+ rgba(255, 128, 0, 0));
+ background-repeat: repeat-x;
+}
+
+.addon-view[notification="error"] {
+ background-image: url("chrome://mozapps/skin/extensions/stripes-error.png"),
+ linear-gradient(rgba(255, 0, 0, 0.04),
+ rgba(255, 0, 0, 0));
+ background-repeat: repeat-x;
+}
+
+.addon-view[pending="enable"],
+.addon-view[pending="upgrade"],
+.addon-view[pending="install"] {
+ background-image: url("chrome://mozapps/skin/extensions/stripes-info-positive.png"),
+ linear-gradient(rgba(0, 255, 0, 0.04),
+ rgba(0, 255, 0, 0));
+ background-repeat: repeat-x;
+}
+
+.addon-view[pending="disable"],
+.addon-view[pending="uninstall"] {
+ background-image: url("chrome://mozapps/skin/extensions/stripes-info-negative.png"),
+ linear-gradient(rgba(128, 128, 128, 0.04),
+ rgba(128, 128, 128, 0));
+ background-repeat: repeat-x;
+}
+
+.addon[selected] {
+ background-color: rgba(105, 125, 149, 0.39);
+ color: black;
+}
+
+.addon[selected] .name-container {
+ text-shadow: @loweredShadow@;
+}
+
+.addon[active="false"][selected] .name-container {
+ color: #3F3F3F;
+}
+
+
+/*** search view ***/
+
+#search-filter {
+ padding: 5px 20px;
+ font-size: 120%;
+ overflow-x: hidden;
+ border-bottom: 1px solid rgba(50, 65, 92, 0.4);
+}
+
+#search-filter-label {
+ font-weight: bold;
+ color: #666;
+}
+
+.search-filter-radio {
+ -moz-appearance: none;
+ padding: 0 10px;
+ margin: 0 3px;
+ border-radius: 10000px;
+}
+
+.search-filter-radio[selected] {
+ text-shadow: @loweredShadow@;
+ background-color: #C0C3CB;
+ box-shadow: inset #A3A6AC 0 1px 1px, @loweredShadow@;
+}
+
+.search-filter-radio:hover {
+ text-shadow: @loweredShadow@;
+ background-color: #C0C3CB;
+}
+
+.search-filter-radio .radio-check {
+ display: none;
+}
+
+.search-filter-radio .radio-icon {
+ display: none;
+}
+
+#search-allresults-link {
+ margin-top: 1em;
+ margin-bottom: 2em;
+}
+
+/*** detail view ***/
+
+#detail-view .loading {
+ opacity: 0;
+}
+
+#detail-view[loading-extended] .loading {
+ opacity: 1;
+ transition-property: opacity;
+ transition-duration: 1s;
+}
+
+.detail-view-container {
+ padding: 0 2em 2em 2em;
+ font-size: 110%;
+}
+
+#detail-notifications {
+ margin-top: 1em;
+ margin-bottom: 2em;
+}
+
+#detail-notifications .warning,
+#detail-notifications .pending,
+#detail-notifications .error {
+ -moz-margin-start: 0;
+}
+
+#detail-icon-container {
+ width: 64px;
+ -moz-margin-end: 10px;
+ margin-top: 6px;
+}
+
+#detail-icon {
+ max-width: 64px;
+ max-height: 64px;
+}
+
+#detail-summary {
+ margin-bottom: 2em;
+}
+
+#detail-name-container {
+ font-size: 200%;
+}
+
+#detail-screenshot {
+ -moz-margin-end: 2em;
+ max-width: 300px;
+ max-height: 300px;
+}
+
+#detail-screenshot[loading] {
+ background-image: url("chrome://global/skin/icons/loading_16.png"),
+ linear-gradient(rgba(255, 255, 255, 0.5), transparent);
+ background-position: 50% 50%;
+ background-repeat: no-repeat;
+ border-radius: 3px;
+}
+
+#detail-screenshot[loading="error"] {
+ background-image: url("chrome://global/skin/media/error.png"),
+ linear-gradient(rgba(255, 255, 255, 0.5), transparent);
+}
+
+#detail-desc-container {
+ margin-bottom: 2em;
+}
+
+#detail-desc, #detail-fulldesc {
+ -moz-margin-start: 6px;
+ /* This is necessary to fix layout issues with multi-line descriptions, see
+ bug 592712*/
+ outline: solid transparent;
+ white-space: pre-wrap;
+ min-width: 10em;
+}
+
+#detail-fulldesc {
+ margin-top: 1em;
+}
+
+#detail-contributions {
+ border-radius: 5px;
+ border: 1px solid rgba(50, 65, 92, 0.3);
+ margin-bottom: 2em;
+ padding: 1em;
+ background-color: rgba(255, 255, 255, 0.35);
+}
+
+#detail-contrib-description {
+ font-style: italic;
+ margin-bottom: 1em;
+ color: #373D48;
+}
+
+#detail-contrib-suggested {
+ color: grey;
+ font-weight: bold;
+}
+
+#detail-contrib-btn {
+ -moz-appearance: none;
+ color: #FFF;
+ border: 1px solid #3A4EEE;
+ border-radius: 3px;
+ list-style-image: url("chrome://mozapps/skin/extensions/heart.png");
+ background-color: #2F73EF;
+ background-image: linear-gradient(rgba(251, 252, 253, 0.70), rgba(246, 247, 248, 0.27) 49%,
+ rgba(231, 232, 233, 0.25) 51%, rgba(225, 226, 229, 0.1));
+}
+
+#detail-contrib-btn .button-box {
+ padding: 0 6px 1px 6px;
+}
+
+#detail-contrib-btn .button-icon {
+ -moz-margin-end: 3px;
+}
+
+#detail-contrib-btn:not(:active):hover {
+ border-color: #4271FF;
+ background-color: #0459F7;
+ box-shadow: 0 1px 0 rgba(0, 0, 0, 0.1),
+ 0 0 3.5px hsl(190, 90%, 80%);
+ transition: background-color .4s ease-in,
+ border-color .3s ease-in,
+ box-shadow .3s ease-in;
+}
+
+#detail-contrib-btn:active:hover {
+ background-color: #8FA1C1;
+ border-color: rgba(0, 0, 0, 0.65) rgba(0, 0, 0, 0.55) rgba(0, 0, 0, 0.5);
+ box-shadow: 0 0 6.5px rgba(0, 0, 0, 0.4) inset,
+ 0 0 2px rgba(0, 0, 0, 0.4) inset;
+}
+
+#detail-grid {
+ margin-bottom: 2em;
+}
+
+#detail-grid > columns > column:first-child {
+ min-width: 15em;
+ max-width: 25em;
+}
+
+.detail-row[first-row="true"],
+.detail-row-complex[first-row="true"],
+setting[first-row="true"] {
+ border-top: none;
+}
+
+.detail-row,
+.detail-row-complex,
+setting {
+ border-top: 2px solid;
+ -moz-border-top-colors: rgba(28, 31, 37, 0.2) rgba(255, 255, 255, 0.2);
+ -moz-box-align: center;
+ min-height: 30px;
+}
+
+#detail-controls {
+ margin-bottom: 1em;
+}
+
+#detail-view[active="false"]:not([pending]):not([notification]) {
+ background-image: linear-gradient(rgba(135, 135, 135, 0.1),
+ rgba(135, 135, 135, 0));
+}
+
+setting[first-row="true"] {
+ margin-top: 2em;
+}
+
+setting {
+ -moz-box-align: start;
+}
+
+.preferences-alignment {
+ min-height: 30px;
+ -moz-box-align: center;
+}
+
+.preferences-description {
+ font-size: 90.9%;
+ color: graytext;
+ margin-top: -2px;
+ -moz-margin-start: 2em;
+ white-space: pre-wrap;
+}
+
+.preferences-description:empty {
+ display: none;
+}
+
+setting[type="radio"] > radiogroup {
+ -moz-box-orient: horizontal;
+}
+
+
+/*** creator ***/
+
+.creator > label {
+ -moz-margin-start: 0;
+ -moz-margin-end: 0;
+}
+
+.creator > .text-link {
+ margin-top: 1px;
+ margin-bottom: 1px;
+}
+
+
+/*** rating ***/
+
+.meta-rating {
+ -moz-margin-end: 0;
+ margin-top: 2px;
+}
+
+.meta-rating > .star {
+ list-style-image: url("chrome://mozapps/skin/extensions/rating-not-won.png");
+ padding: 0 1px;
+}
+
+.meta-rating > .star[on="true"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/rating-won.png");
+}
+
+
+/*** download progress ***/
+
+.download-progress {
+ background-image: linear-gradient(#DCDEE3, #CBCED6);
+ border: 1px solid #858898;
+ border-radius: 3px;
+ box-shadow: inset #E3E8EC 0 1px 1px, @loweredShadow@;
+ width: 200px;
+ height: 21px;
+ margin: 0 8px;
+}
+
+.download-progress[mode="undetermined"] .progress {
+ -moz-binding: url("chrome://global/content/bindings/progressmeter.xml#progressmeter-undetermined");
+}
+
+.download-progress[mode="undetermined"] {
+ border-color: #2E773A;
+}
+
+.download-progress[mode="undetermined"] .status-container {
+ padding: 0 2px;
+}
+
+.download-progress .start-cap,
+.download-progress[complete] .end-cap,
+.download-progress[mode="undetermined"] .end-cap,
+.download-progress .progress .progress-bar {
+ -moz-appearance: none;
+ background-image: linear-gradient(#6AC47E, #4FAC6A);
+ margin-top: -1px;
+ margin-bottom: -1px;
+ border: 1px solid #2E773A;
+}
+
+.download-progress .start-cap {
+ -moz-margin-start: -1px;
+ -moz-border-end-width: 0;
+}
+
+.download-progress .end-cap {
+ -moz-margin-end: -1px;
+ -moz-border-start-width: 0px !important;
+}
+
+.download-progress .progress .progress-bar {
+ border-left-width: 0;
+ border-right-width: 0;
+ min-height: 21px;
+}
+
+.download-progress .progress {
+ -moz-appearance: none;
+ background-color: transparent;
+ padding: 0;
+ margin: 0;
+ border: none;
+}
+
+.download-progress .start-cap,
+.download-progress .end-cap {
+ width: 4px;
+}
+
+.download-progress .start-cap:-moz-locale-dir(ltr),
+.download-progress .end-cap:-moz-locale-dir(rtl) {
+ border-radius: 3px 0 0 3px;
+}
+
+.download-progress .end-cap:-moz-locale-dir(ltr),
+.download-progress .start-cap:-moz-locale-dir(rtl) {
+ border-radius: 0 3px 3px 0;
+}
+
+.download-progress .cancel {
+ -moz-appearance: none;
+ background-color: rgba(255, 255, 255, 0.15);
+ border: 1px solid rgba(0, 0, 0, 0.4);
+ padding: 3px;
+ border-radius: 3px;
+ min-width: 0;
+ margin: 3px;
+}
+
+.download-progress .cancel .button-text {
+ display: none;
+}
+
+.download-progress .cancel .button-icon {
+ -moz-margin-start: 0;
+}
+
+.download-progress .cancel {
+ list-style-image: url('chrome://mozapps/skin/extensions/cancel.png');
+}
+
+.download-progress .status-container {
+ -moz-box-align: center;
+}
+
+.download-progress .status {
+ text-shadow: @loweredShadow@;
+}
+
+
+/*** install status ***/
+
+.install-status {
+ -moz-box-align: center;
+}
+
+
+/*** check for updates ***/
+
+#updates-container {
+ -moz-box-align: center;
+}
+
+#updates-installed,
+#updates-downloaded {
+ color: #3C735C;
+ font-weight: bold;
+}
+
+#update-selected {
+ margin: 12px;
+}
+
+
+/*** buttons ***/
+
+.addon-control[disabled="true"]:not(.no-auto-hide) {
+ display: none;
+}
+
+.no-auto-hide .addon-control {
+ display: block !important;
+}
+
+.no-auto-hide > .menulist-dropmarker {
+ -moz-padding-start: 0px !important;
+}
+
+button.button-link {
+ -moz-appearance: none;
+ background: transparent;
+ border: none;
+ box-shadow: none;
+ text-decoration: underline;
+ color: #0066CC;
+ cursor: pointer;
+ min-width: 0;
+ margin: 0 6px;
+}
+
+.text-link {
+ color: #3386D5;
+}
+
+.button-link:hover,
+.text-link:hover {
+ color: #3DA1FF;
+}
+
+/* Needed to override normal button style from inContent.css */
+button.button-link:not([disabled="true"]):active:hover {
+ background: transparent;
+ border: none;
+ box-shadow: none;
+}
+
+.header-button {
+ -moz-appearance: none;
+ padding: 0 4px;
+ margin: 0;
+ height: 22px;
+ border: 1px solid rgba(60,73,97,0.5);
+ border-radius: @toolbarbuttonCornerRadius@;
+ box-shadow: inset 0 1px rgba(255,255,255,0.25), 0 1px rgba(255,255,255,0.25);
+ background: linear-gradient(rgba(255,255,255,0.45), transparent);
+ background-clip: padding-box;
+}
+
+.header-button .toolbarbutton-text {
+ display: none;
+}
+
+.header-button[disabled="true"] .toolbarbutton-icon {
+ opacity: 0.4;
+}
+
+.header-button:not([disabled="true"]):active:hover,
+.header-button[open="true"] {
+ border-color: rgba(45,54,71,0.7);
+ box-shadow: inset 0 0 4px rgb(45,54,71), 0 1px rgba(255,255,255,0.25);
+ background-image: linear-gradient(rgba(45,54,71,0.6), transparent);
+}
+
+/*** telemetry experiments ***/
+
+#detail-experiment-container {
+ font-size: 80%;
+ margin-bottom: 1em;
+}
+
+#detail-experiment-bullet-container,
+#detail-experiment-state,
+#detail-experiment-time,
+.experiment-bullet-container,
+.experiment-state,
+.experiment-time {
+ vertical-align: middle;
+ display: inline-block;
+}
+
+.addon .experiment-bullet,
+#detail-experiment-bullet {
+ fill: rgb(158, 158, 158);
+}
+
+.addon[active="true"] .experiment-bullet,
+#detail-view[active="true"] #detail-experiment-bullet {
+ fill: rgb(106, 201, 20);
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/heart.png b/toolkit/themes/osx/mozapps/extensions/heart.png
new file mode 100644
index 0000000000..655f4c4be7
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/heart.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/localeGeneric.png b/toolkit/themes/osx/mozapps/extensions/localeGeneric.png
new file mode 100644
index 0000000000..4d9ac5ad89
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/localeGeneric.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/navigation.png b/toolkit/themes/osx/mozapps/extensions/navigation.png
new file mode 100644
index 0000000000..ffc40d7e56
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/navigation.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/newaddon.css b/toolkit/themes/osx/mozapps/extensions/newaddon.css
new file mode 100644
index 0000000000..5bf04fab1d
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/newaddon.css
@@ -0,0 +1,112 @@
+/* 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 ../../global/shared.inc
+
+@import url("chrome://global/skin/inContentUI.css");
+
+#addon-page {
+ padding: 0;
+}
+
+#addon-scrollbox {
+ overflow: auto;
+ -moz-box-orient: vertical;
+ -moz-box-flex: 1;
+}
+
+#spacer-start {
+ -moz-box-flex: 1;
+}
+
+#spacer-end {
+ -moz-box-flex: 3;
+}
+
+#addon-container {
+ overflow: visible;
+ max-width: 600px;
+ margin: 20px;
+ padding: 30px 90px;
+}
+
+#addon-info {
+ -moz-box-align: start;
+ margin: 25px 10px;
+}
+
+#icon {
+ -moz-margin-end: 10px;
+ max-width: 64px;
+ max-height: 64px;
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+}
+
+.addon-info[type="theme"] #icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric.png");
+}
+
+.addon-info[type="locale"] #icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/localeGeneric.png");
+}
+
+.addon-info[type="plugin"] #icon {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.png");
+}
+
+.addon-info[type="dictionary"] #icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric.png");
+}
+
+#name {
+ font-size: 130%;
+}
+
+#author {
+ color: GrayText;
+}
+
+#location {
+ color: GrayText;
+}
+
+#warning {
+ margin-bottom: 25px;
+ -moz-box-align: start;
+}
+
+#warning-icon {
+ list-style-image: url("chrome://mozapps/skin/extensions/alerticon-warning.png");
+ width: 16px;
+ height: 15px;
+ -moz-margin-end: 5px;
+}
+
+#allow {
+ -moz-margin-start: 84px;
+ margin-bottom: 20px;
+}
+
+#continuePanel,
+#restartPanel {
+ margin-top: 25px;
+ -moz-box-align: center;
+ -moz-box-pack: end;
+}
+
+#continuePanel {
+ -moz-box-pack: end;
+}
+
+#restartMessage {
+ text-align: right;
+}
+
+#restartSpacer {
+ -moz-box-flex: 1;
+}
+
+#later {
+ color: GrayText;
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/rating-not-won.png b/toolkit/themes/osx/mozapps/extensions/rating-not-won.png
new file mode 100644
index 0000000000..2761f19255
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/rating-not-won.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/rating-won.png b/toolkit/themes/osx/mozapps/extensions/rating-won.png
new file mode 100644
index 0000000000..336dd8f6eb
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/rating-won.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/search.png b/toolkit/themes/osx/mozapps/extensions/search.png
new file mode 100644
index 0000000000..93196dbbf6
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/search.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/selectAddons.css b/toolkit/themes/osx/mozapps/extensions/selectAddons.css
new file mode 100644
index 0000000000..8682b04b51
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/selectAddons.css
@@ -0,0 +1,163 @@
+/* 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 ../../global/shared.inc
+
+.heading {
+ font-size: 270%;
+ text-align: center;
+ margin: 0 120px;
+}
+
+.progress {
+ margin: 10px 128px;
+}
+
+.progress-label,
+#errors-description {
+ text-align: center;
+ margin: 0 10px;
+}
+
+#checking-heading,
+#update-heading,
+#errors-heading {
+ margin-top: 90px;
+}
+
+#select-heading,
+#confirm-heading {
+ margin-top: 10px;
+ margin-bottom: 10px;
+ text-align: center;
+}
+
+#select-description,
+#confirm-description {
+ margin: 10px;
+}
+
+#select-list {
+ border: 1px solid WindowFrame;
+ background-color: Window;
+ margin: 10px;
+}
+
+#select-grid column {
+ -moz-box-align: center;
+}
+
+#select-grid row {
+ -moz-box-align: stretch;
+}
+
+#select-grid row:nth-of-type(odd) {
+ background-color: -moz-oddtreerow;
+}
+
+#select-grid label,
+#select-grid checkbox {
+ margin-top: 0;
+ margin-bottom: 0;
+}
+
+.select-cell {
+ -moz-box-align: center;
+ -moz-box-pack: start;
+ box-sizing: border-box;
+}
+
+#select-header {
+ background-color: Window !important;
+}
+
+#select-header .select-cell {
+ -moz-appearance: treeheadercell;
+ border: 2px solid;
+ -moz-border-top-colors: ThreeDHighlight ThreeDLightShadow;
+ -moz-border-right-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-bottom-colors: ThreeDDarkShadow ThreeDShadow;
+ -moz-border-left-colors: ThreeDHighlight ThreeDLightShadow;
+ background-color: -moz-Dialog;
+ color: -moz-DialogText;
+}
+
+.select-keep {
+ -moz-box-pack: center;
+}
+
+.select-icon {
+ width: 20px;
+}
+
+#select-grid separator {
+ display: none;
+}
+
+.addon-name,
+.addon-action-message,
+.addon-action-update {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 2px 6px;
+}
+
+.addon:not([active]) .addon-name,
+.addon:not([active]) .addon-action-message,
+.addon:not([active]) .addon-action-update {
+ color: GrayText;
+}
+
+.addon-icon {
+ height: 16px;
+ width: 16px;
+ margin: 2px;
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric-16.png");
+}
+
+.addon-icon[type="theme"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric-16.png");
+}
+
+.addon-icon[type="plugin"] {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric-16.png");
+}
+
+.addon-icon[type="dictionary"] {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric-16.png");
+}
+
+.action-list {
+ margin-top: 10px;
+ -moz-margin-start: 5em;
+}
+
+.action-header {
+ margin-bottom: 10px;
+}
+
+#confirm .addon {
+ -moz-margin-start: 3em;
+ -moz-box-align: center;
+}
+
+.addon:not([active]) .addon-icon,
+#disable-list .addon-icon,
+#incompatible-list .addon-icon {
+ filter: grayscale(1);
+}
+
+#footer {
+ padding: 15px 12px;
+ -moz-appearance: statusbar;
+ -moz-window-dragging: drag;
+}
+
+button {
+ -moz-appearance: toolbarbutton;
+ min-height: 22px;
+ margin: 0 6px;
+ padding: 0;
+ text-shadow: @loweredShadow@;
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/stripes-compatibility.png b/toolkit/themes/osx/mozapps/extensions/stripes-compatibility.png
new file mode 100644
index 0000000000..dee75516b7
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/stripes-compatibility.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/stripes-error.png b/toolkit/themes/osx/mozapps/extensions/stripes-error.png
new file mode 100644
index 0000000000..1dc2d8504c
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/stripes-error.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/stripes-info-negative.png b/toolkit/themes/osx/mozapps/extensions/stripes-info-negative.png
new file mode 100644
index 0000000000..901ab1ec29
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/stripes-info-negative.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/stripes-info-positive.png b/toolkit/themes/osx/mozapps/extensions/stripes-info-positive.png
new file mode 100644
index 0000000000..370ceec0f2
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/stripes-info-positive.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/stripes-warning.png b/toolkit/themes/osx/mozapps/extensions/stripes-warning.png
new file mode 100644
index 0000000000..69463fb1af
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/stripes-warning.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/themeGeneric-16.png b/toolkit/themes/osx/mozapps/extensions/themeGeneric-16.png
new file mode 100644
index 0000000000..190bb30d71
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/themeGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/themeGeneric.png b/toolkit/themes/osx/mozapps/extensions/themeGeneric.png
new file mode 100644
index 0000000000..be645f76df
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/themeGeneric.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/toolbarbutton-dropmarker.png b/toolkit/themes/osx/mozapps/extensions/toolbarbutton-dropmarker.png
new file mode 100644
index 0000000000..e7674c62a6
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/toolbarbutton-dropmarker.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/extensions/update.css b/toolkit/themes/osx/mozapps/extensions/update.css
new file mode 100644
index 0000000000..8e92556913
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/update.css
@@ -0,0 +1,26 @@
+/* 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/. */
+
+#alert {
+ list-style-image: url("chrome://mozapps/skin/update/warning.gif");
+}
+
+.throbber {
+ list-style-image: url("chrome://global/skin/icons/loading_16.png");
+ width: 16px;
+ height: 16px;
+ margin-top: 5px;
+ margin-bottom: 5px;
+ -moz-margin-start: 5px;
+ -moz-margin-end: 2px;
+}
+
+.alertBox {
+ background-color: InfoBackground;
+ color: InfoText;
+ border: 1px outset InfoBackground;
+ margin-left: 3px;
+ margin-right: 3px;
+ padding: 5px;
+}
diff --git a/toolkit/themes/osx/mozapps/extensions/xpinstallConfirm.css b/toolkit/themes/osx/mozapps/extensions/xpinstallConfirm.css
new file mode 100644
index 0000000000..0a1a84b249
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/extensions/xpinstallConfirm.css
@@ -0,0 +1,90 @@
+/* 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/. */
+
+#xpinstallheader {
+ margin-bottom: 2em;
+}
+
+.alert-icon {
+ width: 48px;
+ height: 48px;
+ list-style-image: url("chrome://global/skin/icons/warning-large.png");
+ margin-top: 0 !important;
+ margin-bottom: 6px !important;
+ -moz-margin-start: 6px !important;
+ -moz-margin-end: 20px !important;
+}
+
+#itemList {
+ -moz-appearance: listbox;
+ margin: 3px 4px 10px 4px;
+ height: 14em;
+}
+
+#itemWarningIntro {
+ -moz-margin-start: 8px;
+}
+
+#dialogContentBox {
+ padding: 5px;
+}
+
+installitem {
+ padding: 5px 0 5px 5px;
+ border-bottom: 1px dotted #C0C0C0;
+ margin-bottom: 5px;
+}
+
+.warning {
+ font-weight: bold;
+ font-size: 1.25em;
+ margin-bottom: 1em;
+}
+
+.xpinstallIconContainer {
+ width: 32px;
+ height: 32px;
+ -moz-margin-end: 5px;
+}
+
+.xpinstallItemName {
+ font-weight: bold;
+}
+
+.xpinstallItemSigned {
+ font-style: italic;
+ font-size: 0.9em;
+}
+
+.xpinstallItemURL {
+ -moz-appearance: none;
+ border: none;
+ background-color: Window;
+ margin-top: 2px;
+ margin-bottom: 1px;
+ -moz-margin-start: 6px;
+ -moz-margin-end: 5px;
+}
+
+.xpinstallItemIcon {
+ max-width: 32px;
+ max-height: 32px;
+ list-style-image: url("chrome://mozapps/skin/extensions/extensionGeneric.png");
+}
+
+installitem[type="theme"] .xpinstallItemIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/themeGeneric.png");
+}
+
+installitem[type="locale"] .xpinstallItemIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/localeGeneric.png");
+}
+
+installitem[type="plugin"] .xpinstallItemIcon {
+ list-style-image: url("chrome://mozapps/skin/plugins/pluginGeneric.png");
+}
+
+installitem[type="dictionary"] .xpinstallItemIcon {
+ list-style-image: url("chrome://mozapps/skin/extensions/dictionaryGeneric.png");
+}
diff --git a/toolkit/themes/osx/mozapps/handling/handling.css b/toolkit/themes/osx/mozapps/handling/handling.css
new file mode 100644
index 0000000000..9598bedaed
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/handling/handling.css
@@ -0,0 +1,30 @@
+/* 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/. */
+
+#description-image:not([src]) {
+ height: 32px;
+ width: 32px;
+}
+
+richlistitem[type] {
+ min-height: 36px; /* Don't forget to update the richlistbox height! */
+ padding-inline-start: 2px;
+ }
+
+richlistitem {
+ -moz-box-align: center;
+}
+
+richlistbox {
+ /* 3 items high, plus 4px for top and bottom margins, less 2px for border */
+ min-height: 110px;
+}
+
+.name {
+ font-weight: bold;
+}
+
+.description {
+ color: GrayText;
+}
diff --git a/toolkit/themes/osx/mozapps/jar.mn b/toolkit/themes/osx/mozapps/jar.mn
new file mode 100644
index 0000000000..eabd2eddb5
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/jar.mn
@@ -0,0 +1,79 @@
+# 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/.
+
+toolkit.jar:
+#include ../../shared/mozapps.inc.mn
+ skin/classic/mozapps/downloads/buttons.png (downloads/buttons.png)
+ skin/classic/mozapps/downloads/downloadIcon.png (downloads/downloadIcon.png)
+* skin/classic/mozapps/downloads/downloads.css (downloads/downloads.css)
+ skin/classic/mozapps/downloads/unknownContentType.css (downloads/unknownContentType.css)
+ skin/classic/mozapps/extensions/category-search.png (extensions/category-search.png)
+ skin/classic/mozapps/extensions/category-discover.png (extensions/category-discover.png)
+ skin/classic/mozapps/extensions/category-languages.png (extensions/localeGeneric.png)
+ skin/classic/mozapps/extensions/category-searchengines.png (extensions/category-searchengines.png)
+ skin/classic/mozapps/extensions/category-extensions.png (extensions/extensionGeneric.png)
+ skin/classic/mozapps/extensions/category-themes.png (extensions/themeGeneric.png)
+ skin/classic/mozapps/extensions/category-plugins.png (extensions/category-plugins.png)
+ skin/classic/mozapps/extensions/category-service.png (extensions/category-service.png)
+ skin/classic/mozapps/extensions/category-dictionaries.png (extensions/category-dictionaries.png)
+ skin/classic/mozapps/extensions/category-experiments.png (extensions/category-experiments.png)
+ skin/classic/mozapps/extensions/category-recent.png (extensions/category-recent.png)
+ skin/classic/mozapps/extensions/category-available.png (extensions/category-available.png)
+ skin/classic/mozapps/extensions/discover-logo.png (extensions/discover-logo.png)
+ skin/classic/mozapps/extensions/extensionGeneric.png (extensions/extensionGeneric.png)
+ skin/classic/mozapps/extensions/extensionGeneric-16.png (extensions/extensionGeneric-16.png)
+ skin/classic/mozapps/extensions/themeGeneric.png (extensions/themeGeneric.png)
+ skin/classic/mozapps/extensions/themeGeneric-16.png (extensions/themeGeneric-16.png)
+ skin/classic/mozapps/extensions/dictionaryGeneric.png (extensions/dictionaryGeneric.png)
+ skin/classic/mozapps/extensions/dictionaryGeneric-16.png (extensions/dictionaryGeneric-16.png)
+ skin/classic/mozapps/extensions/experimentGeneric.png (extensions/experimentGeneric.png)
+ skin/classic/mozapps/extensions/localeGeneric.png (extensions/localeGeneric.png)
+ skin/classic/mozapps/extensions/rating-won.png (extensions/rating-won.png)
+ skin/classic/mozapps/extensions/rating-not-won.png (extensions/rating-not-won.png)
+ skin/classic/mozapps/extensions/cancel.png (extensions/cancel.png)
+ skin/classic/mozapps/extensions/utilities.svg (../../shared/extensions/utilities.svg)
+ skin/classic/mozapps/extensions/toolbarbutton-dropmarker.png (extensions/toolbarbutton-dropmarker.png)
+ skin/classic/mozapps/extensions/heart.png (extensions/heart.png)
+ skin/classic/mozapps/extensions/navigation.png (extensions/navigation.png)
+ skin/classic/mozapps/extensions/stripes-warning.png (extensions/stripes-warning.png)
+ skin/classic/mozapps/extensions/stripes-error.png (extensions/stripes-error.png)
+ skin/classic/mozapps/extensions/stripes-info-positive.png (extensions/stripes-info-positive.png)
+ skin/classic/mozapps/extensions/stripes-info-negative.png (extensions/stripes-info-negative.png)
+ skin/classic/mozapps/extensions/alerticon-warning.png (extensions/alerticon-warning.png)
+ skin/classic/mozapps/extensions/alerticon-error.png (extensions/alerticon-error.png)
+ skin/classic/mozapps/extensions/alerticon-info-positive.png (extensions/alerticon-info-positive.png)
+ skin/classic/mozapps/extensions/alerticon-info-negative.png (extensions/alerticon-info-negative.png)
+ skin/classic/mozapps/extensions/search.png (extensions/search.png)
+ skin/classic/mozapps/extensions/about.css (extensions/about.css)
+* skin/classic/mozapps/extensions/extensions.css (extensions/extensions.css)
+* skin/classic/mozapps/extensions/selectAddons.css (extensions/selectAddons.css)
+ skin/classic/mozapps/extensions/update.css (extensions/update.css)
+ skin/classic/mozapps/extensions/eula.css (extensions/eula.css)
+ skin/classic/mozapps/extensions/blocklist.css (extensions/blocklist.css)
+* skin/classic/mozapps/extensions/newaddon.css (extensions/newaddon.css)
+ skin/classic/mozapps/passwordmgr/key.png (passwordmgr/key.png)
+ skin/classic/mozapps/passwordmgr/key-16.png (passwordmgr/key-16.png)
+ skin/classic/mozapps/passwordmgr/key-64.png (passwordmgr/key-64.png)
+ skin/classic/mozapps/plugins/notifyPluginGeneric.png (plugins/notifyPluginGeneric.png)
+ skin/classic/mozapps/plugins/pluginGeneric.png (plugins/pluginGeneric.png)
+ skin/classic/mozapps/plugins/pluginBlocked.png (plugins/pluginBlocked.png)
+ skin/classic/mozapps/plugins/pluginBlocked-64.png (plugins/pluginBlocked-64.png)
+ skin/classic/mozapps/plugins/pluginGeneric-16.png (plugins/pluginGeneric-16.png)
+ skin/classic/mozapps/plugins/pluginHelp-16.png (plugins/pluginHelp-16.png)
+ skin/classic/mozapps/profile/profileicon.png (profile/profileicon.png)
+ skin/classic/mozapps/profile/profileSelection.css (profile/profileSelection.css)
+ skin/classic/mozapps/profile/profileicon-selected.png (profile/profileicon-selected.png)
+ skin/classic/mozapps/update/buttons.png (update/buttons.png)
+* skin/classic/mozapps/update/updates.css (update/updates.css)
+ skin/classic/mozapps/viewsource/viewsource.css (viewsource/viewsource.css)
+ skin/classic/mozapps/xpinstall/xpinstallItemGeneric.png (extensions/extensionGeneric.png)
+ skin/classic/mozapps/xpinstall/xpinstallConfirm.css (extensions/xpinstallConfirm.css)
+ skin/classic/mozapps/handling/handling.css (handling/handling.css)
+
+#ifdef MOZ_PHOENIX
+[browser/extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
+#elif MOZ_SEPARATE_MANIFEST_FOR_THEME_OVERRIDES
+[extensions/{972ce4c6-7e08-4474-a285-3208198ce6fd}] chrome.jar:
+#endif
+% override chrome://mozapps/skin/plugins/notifyPluginCrashed.png chrome://mozapps/skin/plugins/notifyPluginGeneric.png
diff --git a/toolkit/themes/osx/mozapps/moz.build b/toolkit/themes/osx/mozapps/moz.build
new file mode 100644
index 0000000000..635fa39c99
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/moz.build
@@ -0,0 +1,6 @@
+# -*- 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_MANIFESTS += ['jar.mn'] \ No newline at end of file
diff --git a/toolkit/themes/osx/mozapps/passwordmgr/key-16.png b/toolkit/themes/osx/mozapps/passwordmgr/key-16.png
new file mode 100644
index 0000000000..ac135b847e
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/passwordmgr/key-16.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/passwordmgr/key-64.png b/toolkit/themes/osx/mozapps/passwordmgr/key-64.png
new file mode 100644
index 0000000000..0fb69f3828
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/passwordmgr/key-64.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/passwordmgr/key.png b/toolkit/themes/osx/mozapps/passwordmgr/key.png
new file mode 100644
index 0000000000..b5e8afefca
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/passwordmgr/key.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/plugins/notifyPluginGeneric.png b/toolkit/themes/osx/mozapps/plugins/notifyPluginGeneric.png
new file mode 100644
index 0000000000..449e081496
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/plugins/notifyPluginGeneric.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/plugins/pluginBlocked-64.png b/toolkit/themes/osx/mozapps/plugins/pluginBlocked-64.png
new file mode 100644
index 0000000000..56b8a3322d
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/plugins/pluginBlocked-64.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/plugins/pluginBlocked.png b/toolkit/themes/osx/mozapps/plugins/pluginBlocked.png
new file mode 100644
index 0000000000..6e8e1761bf
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/plugins/pluginBlocked.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/plugins/pluginGeneric-16.png b/toolkit/themes/osx/mozapps/plugins/pluginGeneric-16.png
new file mode 100644
index 0000000000..6956ffef81
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/plugins/pluginGeneric-16.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/plugins/pluginGeneric.png b/toolkit/themes/osx/mozapps/plugins/pluginGeneric.png
new file mode 100644
index 0000000000..6ada1616bb
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/plugins/pluginGeneric.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/plugins/pluginHelp-16.png b/toolkit/themes/osx/mozapps/plugins/pluginHelp-16.png
new file mode 100644
index 0000000000..9a577c08f2
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/plugins/pluginHelp-16.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/profile/profileSelection.css b/toolkit/themes/osx/mozapps/profile/profileSelection.css
new file mode 100644
index 0000000000..cc3ab451c7
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/profile/profileSelection.css
@@ -0,0 +1,29 @@
+/* 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 url("chrome://global/skin/global.css");
+
+@namespace url("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul");
+
+#profiles > listitem {
+ list-style-image: url("chrome://mozapps/skin/profile/profileicon.png");
+}
+
+#profiles:focus > listitem[selected="true"] {
+ list-style-image: url("chrome://mozapps/skin/profile/profileicon-selected.png");
+}
+
+#profiles > listitem > listcell > image {
+ width: 16px;
+ height: 16px;
+}
+
+box#managebuttons > button {
+ min-width: 8em;
+}
+
+#managebuttons {
+ padding-top: 1em;
+}
diff --git a/toolkit/themes/osx/mozapps/profile/profileicon-selected.png b/toolkit/themes/osx/mozapps/profile/profileicon-selected.png
new file mode 100644
index 0000000000..f3e1f8e110
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/profile/profileicon-selected.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/profile/profileicon.png b/toolkit/themes/osx/mozapps/profile/profileicon.png
new file mode 100644
index 0000000000..f67a43714e
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/profile/profileicon.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/update/buttons.png b/toolkit/themes/osx/mozapps/update/buttons.png
new file mode 100644
index 0000000000..04da26a252
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/update/buttons.png
Binary files differ
diff --git a/toolkit/themes/osx/mozapps/update/updates.css b/toolkit/themes/osx/mozapps/update/updates.css
new file mode 100644
index 0000000000..9bd78ef6f8
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/update/updates.css
@@ -0,0 +1,171 @@
+%include ../../global/shared.inc
+
+/* General */
+/* Specify the size for the wizardpage so the billboard has a fixed size. 3rd
+ party themes should typically specify the same values. */
+wizardpage {
+ height: 360px;
+ width: 700px;
+}
+
+/* Remove margin and padding so the billboard will extend to the edge of the
+ window. 3rd party themes should typically specify the same values. */
+#updates, .wizard-page-box {
+ margin: 0;
+ padding: 0;
+}
+
+.update-content {
+ padding: 6px 12px 12px 12px;
+}
+
+.wizard-header-box-text {
+ padding: 0;
+}
+
+.wizard-header {
+ margin: 12px 12px 0 12px;
+}
+
+.wizard-buttons-btm {
+ padding: 15px 12px;
+}
+
+/* Don't use top margin - it can cause a scrollbar on some pages */
+.wizard-buttons {
+ padding: 0;
+ -moz-appearance: statusbar;
+ -moz-window-dragging: drag;
+}
+
+.wizard-buttons button {
+ -moz-appearance: toolbarbutton;
+ color: ButtonText;
+ min-height: 22px;
+ margin: 0 6px;
+ padding: 0;
+ text-shadow: @loweredShadow@;
+}
+
+#finishedBackgroundMore {
+ margin-bottom: 6px;
+}
+
+.inline-link {
+ color: -moz-nativehyperlinktext;
+ text-decoration: none;
+}
+
+.inline-link:hover {
+ text-decoration: underline;
+}
+
+/* Unsupported Page */
+#unsupportedLabel, #unsupportedLinkLabel {
+ margin-inline-start: 0;
+ padding-inline-start: 0;
+}
+
+/* Update Found Basic Page */
+#updateName, #updateFinishedName {
+ font-weight: bold;
+ font-size: larger;
+}
+
+/* Downloading Page */
+#downloadStatusLine {
+ -moz-box-align: center;
+}
+
+#downloadStatus {
+ height: 3em !important;
+}
+
+#downloadStatusProgress {
+ padding-right: 5px;
+}
+
+#pauseButton {
+ list-style-image: url(chrome://mozapps/skin/update/buttons.png);
+ -moz-image-region: rect(48px, 16px, 64px, 0px);
+ -moz-appearance: none;
+ background-color: transparent;
+ border: none;
+ min-height: 16px;
+ min-width: 16px;
+ max-height: 16px;
+ max-width: 16px;
+ margin: 0 1px 0 1px;
+ padding: 0;
+}
+
+/* !Important must be used otherwise this won't immediately take affect */
+#pauseButton > .button-box {
+ padding: 0 !important;
+}
+
+#pauseButton:hover {
+ -moz-image-region: rect(48px, 32px, 64px, 16px);
+}
+
+#pauseButton:not([disabled="true"]):hover:active {
+ -moz-image-region: rect(48px, 48px, 64px, 32px);
+}
+
+#pauseButton[disabled="true"] {
+ -moz-image-region: rect(48px, 16px, 64px, 0px);
+}
+
+#pauseButton[paused="true"] {
+ -moz-image-region: rect(16px, 16px, 32px, 0px);
+}
+
+#pauseButton[paused="true"]:hover {
+ -moz-image-region: rect(16px, 32px, 32px, 16px);
+}
+
+#pauseButton[paused="true"]:hover:active {
+ -moz-image-region: rect(16px, 48px, 32px, 32px);
+}
+
+#verificationFailedIcon {
+ margin-left: 5px;
+ list-style-image: url("chrome://global/skin/icons/notfound.png");
+}
+
+/* Error Page */
+#errorReason {
+ margin-top: 1px;
+ margin-bottom: 2px;
+ margin-inline-start: 6px !important;
+ margin-inline-end: 5px;
+ font-weight: bold;
+}
+
+/* Update History Window */
+update {
+ border-bottom: 1px dotted #C0C0C0;
+}
+
+.update-name {
+ font-weight: bold;
+}
+
+.update-label-column {
+ -moz-box-align: end;
+}
+
+.update-type {
+ font-weight: bold;
+ color: #990000;
+}
+
+#historyItems {
+ -moz-appearance: listbox;
+ height: 200px;
+ margin: 1px 5px 4px 5px;
+}
+
+#historyItems > scrollbox {
+ margin-bottom: 1px;
+}
diff --git a/toolkit/themes/osx/mozapps/viewsource/viewsource.css b/toolkit/themes/osx/mozapps/viewsource/viewsource.css
new file mode 100644
index 0000000000..76c7d00b9d
--- /dev/null
+++ b/toolkit/themes/osx/mozapps/viewsource/viewsource.css
@@ -0,0 +1,5 @@
+/* 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/. */
+
+/* This is for styling the menus of the viewsource window */
diff --git a/toolkit/themes/osx/reftests/482681-ref.xul b/toolkit/themes/osx/reftests/482681-ref.xul
new file mode 100644
index 0000000000..62fb4bb8d5
--- /dev/null
+++ b/toolkit/themes/osx/reftests/482681-ref.xul
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="data:text/css,
+vbox { height: 50px; }
+box {
+ -moz-appearance: button;
+}
+" type="text/css"?>
+
+<window title="Reference for mini, small and regular button sizes"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <vbox>
+ <hbox><box width="79" height="16"/></hbox>
+ </vbox>
+ <vbox>
+ <hbox><box width="79" height="19"/></hbox>
+ </vbox>
+ <vbox>
+ <hbox><box width="79" height="22"/></hbox>
+ </vbox>
+</window>
diff --git a/toolkit/themes/osx/reftests/482681.xul b/toolkit/themes/osx/reftests/482681.xul
new file mode 100644
index 0000000000..6cb9aaeae4
--- /dev/null
+++ b/toolkit/themes/osx/reftests/482681.xul
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="data:text/css,
+vbox { height: 50px; }
+button {
+ color: transparent;
+ margin: 0;
+}
+" type="text/css"?>
+
+<window title="Buttons with mini, small and regular control font"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <vbox style="font-size: 9px">
+ <hbox><button label="Mini"/></hbox>
+ </vbox>
+ <vbox style="font: message-box">
+ <hbox><button label="Small"/></hbox>
+ </vbox>
+ <vbox style="font: -moz-dialog">
+ <hbox><button label="Regular"/></hbox>
+ </vbox>
+</window>
diff --git a/toolkit/themes/osx/reftests/baseline.xul b/toolkit/themes/osx/reftests/baseline.xul
new file mode 100644
index 0000000000..2ec6f5132f
--- /dev/null
+++ b/toolkit/themes/osx/reftests/baseline.xul
@@ -0,0 +1,175 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<!--
+ * This is a complicated test.
+ * XUL authors like to place several different widgets on the same line by
+ * putting them in a <hbox align="center">. In order for this to look good,
+ * the baselines of the text contained in the widgets should line up.
+ * This is what this test is testing.
+ * The test passes if it's completely white.
+ *
+ * It works like this:
+ * For every combination of two different widgets (where widget is one of
+ * label, radio, checkbox, button, textbox, menulist, menulist[editable="true"] or
+ * filefield), there's a stack with two layers. The back layer in the stack is
+ * just a vertically centered label with a bunch of underscores. This is the
+ * baseline that the text on the widgets should hit.
+ * On the foreground layer in the stack we've placed the pair of widgets we're
+ * testing. They also have underscores in their labels.
+ *
+ * Now we want to test whether the underscores in the foreground layer are directly
+ * on top of those in the back layer. For that we use color-keying and a tricky
+ * SVG color transformation.
+ * The back layer of the stack has a red background; the underscores of the
+ * back label are in white (and have a white text-shadow in order to fill up the
+ * gaps between the individual letters).
+ * Now we want the foreground layer to be solid white, except for those pixels
+ * that make up the labels: These should be transparent.
+ * So if the baselines line up, everything is white, since at those pixels where
+ * the foreground is transparent, only the white pixels from the back layer shine
+ * through. If the baselines don't line up, red pixels from the background will
+ * shine through, and the comparison with about:blank (completely white) will fail.
+ *
+ * So how do we get the foreground white and transparent? That's the job of the
+ * SVG color transformation filter. It's a simple matrix that makes turns opaque
+ * yellow into transparent and all other colors into white.
+ * -->
+
+<window title="Baseline test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ orient="vertical"
+ class="reftest-wait"
+ onload="loaded()">
+
+<html:style><![CDATA[
+window {
+ -moz-appearance: none;
+ background-color: white;
+}
+.regular {
+ font: -moz-dialog;
+}
+.small {
+ font: message-box;
+}
+.spacer {
+ height: 40px;
+}
+stack > hbox:first-child {
+ background: red;
+ color: white;
+ text-shadow: 5px 0 white, -5px 0 white;
+}
+stack > .foreground {
+ filter: url(#yellow2transparent);
+}
+stack > hbox:last-child > * {
+ color: yellow;
+}
+]]>
+</html:style>
+
+ <svg:svg style="visibility: collapse;">
+ <svg:filter id="yellow2transparent" color-interpolation-filters="sRGB">
+ <svg:feColorMatrix type="matrix"
+ values="0 0 0 0 1
+ 0 0 0 0 1
+ 0 0 0 0 1
+ -100 -100 100 -100 300"/>
+ </svg:filter>
+ </svg:svg>
+
+<script type="application/javascript;version=1.8"><![CDATA[
+
+function cE(elem) {
+ return document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", elem);
+}
+function elWithValue(elem, val) {
+ let e = cE(elem);
+ e.setAttribute(elem == "label" || elem == "textbox" ? "value" : "label", val);
+ return e;
+}
+
+function allPairs(set) {
+ let ps = [];
+ for(let i = 0; i < set.length; ++i) {
+ for (let j = i + 1; j < set.length; ++j) {
+ ps.push([set[i], set[j]]);
+ }
+ }
+ return ps;
+}
+
+function createLabel(v) {
+ return elWithValue("label", v);
+}
+function createRadio(v) {
+ return elWithValue("radio", v);
+}
+function createCheckbox(v) {
+ return elWithValue("checkbox", v);
+}
+function createButton(v) {
+ return elWithValue("button", v);
+}
+function createTextField(v) {
+ return elWithValue("textbox", v);
+}
+function createMenulist(v) {
+ let [list, popup, item] = [cE("menulist"), cE("menupopup"), elWithValue("menuitem", v)];
+ item.setAttribute("selected", "true");
+ popup.appendChild(item);
+ list.appendChild(popup);
+ return list;
+}
+function createEditableMenulist(v) {
+ let list = createMenulist(v);
+ list.setAttribute("editable", "true");
+ return list;
+}
+function createFileField(v) {
+ let field = elWithValue("filefield", v);
+ field.setAttribute("image", "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAIAAAD8GO2jAAAAAXNSR0IArs4c6QAAAChJREFUSMftzUEBAAAEBLCjf2dK8NsKrCaTT51nAoFAIBAIBAKB4MoCtVsCPjrGuiwAAAAASUVORK5CYII=");
+ return field;
+}
+function loaded() {
+ let template = document.getElementById("template");
+ ["regular", "small"].forEach(function(size) {
+ let wrapper = document.querySelectorAll("#wrapper > ." + size)[0];
+ allPairs([
+ createLabel, createRadio, createCheckbox, createButton, createMenulist, createTextField,
+ /* createEditableMenulist, createFileField, */ /* These don't inherit "color" properly */
+ ]).forEach(function(elemList) {
+ let newBox = template.cloneNode(true);
+ newBox.className = "spacer";
+ let foregroundRow = newBox.firstChild.lastChild;
+ elemList.forEach(function(creator) {
+ foregroundRow.appendChild(creator("______"));
+ });
+ wrapper.appendChild(newBox);
+ });
+ });
+ document.documentElement.className = "";
+}
+
+]]></script>
+ <vbox id="template">
+ <stack>
+ <hbox align="center">
+ <label value="______________________________________________"/>
+ </hbox>
+ <hbox align="center" class="foreground">
+ </hbox>
+ </stack>
+ </vbox>
+ <hbox id="wrapper">
+ <vbox class="regular" flex="1"/>
+ <vbox class="small" flex="1"/>
+ </hbox>
+
+ <spacer flex="1"/>
+
+</window>
diff --git a/toolkit/themes/osx/reftests/checkboxsize-ref.xul b/toolkit/themes/osx/reftests/checkboxsize-ref.xul
new file mode 100644
index 0000000000..08d1e9a670
--- /dev/null
+++ b/toolkit/themes/osx/reftests/checkboxsize-ref.xul
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="data:text/css,
+vbox { height: 50px; }
+box {
+ -moz-appearance: checkbox;
+ margin-left: 2px;
+ margin-top: 1px;
+}
+" type="text/css"?>
+
+<window title="Reference for mini, small and regular checkbox sizes"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <vbox>
+ <hbox><box width="11" height="11"/></hbox>
+ </vbox>
+ <vbox>
+ <hbox><box width="13" height="13"/></hbox>
+ </vbox>
+ <vbox>
+ <hbox><box width="16" height="16"/></hbox>
+ </vbox>
+ <vbox>
+ <hbox checked="true"><box width="11" height="11"/></hbox>
+ </vbox>
+ <vbox>
+ <hbox checked="true"><box width="13" height="13"/></hbox>
+ </vbox>
+ <vbox>
+ <hbox checked="true"><box width="16" height="16"/></hbox>
+ </vbox>
+</window>
diff --git a/toolkit/themes/osx/reftests/checkboxsize.xul b/toolkit/themes/osx/reftests/checkboxsize.xul
new file mode 100644
index 0000000000..55429ef8f8
--- /dev/null
+++ b/toolkit/themes/osx/reftests/checkboxsize.xul
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="data:text/css,
+vbox { height: 50px; }
+checkbox {
+ color: transparent;
+ margin: 0;
+}
+" type="text/css"?>
+
+<window title="Checkboxes with mini, small and regular control font"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <vbox style="font-size: 9px">
+ <hbox><checkbox label="Mini"/></hbox>
+ </vbox>
+ <vbox style="font: message-box">
+ <hbox><checkbox label="Small"/></hbox>
+ </vbox>
+ <vbox style="font: -moz-dialog">
+ <hbox><checkbox label="Regular"/></hbox>
+ </vbox>
+ <vbox style="font-size: 9px">
+ <hbox><checkbox label="Mini" checked="true"/></hbox>
+ </vbox>
+ <vbox style="font: message-box">
+ <hbox><checkbox label="Small" checked="true"/></hbox>
+ </vbox>
+ <vbox style="font: -moz-dialog">
+ <hbox><checkbox label="Regular" checked="true"/></hbox>
+ </vbox>
+</window>
diff --git a/toolkit/themes/osx/reftests/nostretch-ref.xul b/toolkit/themes/osx/reftests/nostretch-ref.xul
new file mode 100644
index 0000000000..a1aee555e6
--- /dev/null
+++ b/toolkit/themes/osx/reftests/nostretch-ref.xul
@@ -0,0 +1,107 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<window title="Stretched controls test reference"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ orient="vertical"
+ class="reftest-wait"
+ onload="loaded()">
+
+<html:style><![CDATA[
+.regular {
+ font: -moz-dialog;
+}
+.small {
+ font: message-box;
+}
+.spacer {
+ height: 40px;
+}
+.foreground > :nth-child(2) {
+ display: none; /* <----- This is the only difference from nostretch.xul */
+}
+]]>
+</html:style>
+
+<script type="application/javascript;version=1.8"><![CDATA[
+
+function cE(elem) {
+ return document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", elem);
+}
+function elWithValue(elem, val) {
+ let e = cE(elem);
+ e.setAttribute(elem == "label" || elem == "textbox" ? "value" : "label", val);
+ return e;
+}
+
+function allPairs(set) {
+ let ps = [];
+ for(let i = 0; i < set.length; ++i) {
+ for (let j = 0; j < set.length; ++j) {
+ if (i != j)
+ ps.push([set[i], set[j]]);
+ }
+ }
+ return ps;
+}
+
+function createLabel(v) {
+ return elWithValue("label", v);
+}
+function createRadio(v) {
+ return elWithValue("radio", v);
+}
+function createCheckbox(v) {
+ return elWithValue("checkbox", v);
+}
+function createButton(v) {
+ return elWithValue("button", v);
+}
+function createTextField(v) {
+ return elWithValue("textbox", v);
+}
+function createMenulist(v) {
+ let [list, popup, item] = [cE("menulist"), cE("menupopup"), elWithValue("menuitem", v)];
+ item.setAttribute("selected", "true");
+ popup.appendChild(item);
+ list.appendChild(popup);
+ return list;
+}
+function createEditableMenulist(v) {
+ let list = createMenulist(v);
+ list.setAttribute("editable", "true");
+ return list;
+}
+function loaded() {
+ let template = document.getElementById("template");
+ ["regular", "small"].forEach(function(size) {
+ let wrapper = document.querySelectorAll("#wrapper > ." + size)[0];
+ allPairs([
+ createButton, createMenulist, createTextField, createEditableMenulist,
+ ]).forEach(function(elemList) {
+ let newBox = template.cloneNode(true);
+ newBox.className = "spacer";
+ let foregroundRow = newBox.firstChild;
+ elemList.forEach(function(creator) {
+ foregroundRow.appendChild(creator("Label"));
+ });
+ wrapper.appendChild(newBox);
+ });
+ });
+ document.documentElement.className = "";
+}
+
+]]></script>
+ <vbox id="template">
+ <hbox class="foreground"/>
+ </vbox>
+ <hbox id="wrapper">
+ <vbox class="regular" width="500"/>
+ <vbox class="small" flex="1"/>
+ </hbox>
+
+ <spacer flex="1"/>
+
+</window>
diff --git a/toolkit/themes/osx/reftests/nostretch.xul b/toolkit/themes/osx/reftests/nostretch.xul
new file mode 100644
index 0000000000..cd28fa1b7b
--- /dev/null
+++ b/toolkit/themes/osx/reftests/nostretch.xul
@@ -0,0 +1,120 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+
+<!--
+ * This test tests whether you can put different widgets in the same
+ * hbox without stretching them, even if you don't set align="center".
+ * I.e. prior to the fix that added this patch, having a button and a
+ * menulist in the same hbox next to each other would stretch the menulist
+ * vertically because the button had such a big vertical margin.
+ *
+ * The test works like this: Two widgets are placed in a hbox, and the second
+ * widget is visibility: hidden. In the reference (nostretch-ref.xul), the
+ * second widget is display: none. If test and reference look the same,
+ * adding the second widget hasn't affected the appearance of the first widget,
+ * and everything's fine.
+ * -->
+<window title="Stretched controls test"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+ xmlns:html="http://www.w3.org/1999/xhtml"
+ xmlns:svg="http://www.w3.org/2000/svg"
+ orient="vertical"
+ class="reftest-wait"
+ onload="loaded()">
+
+<html:style><![CDATA[
+.regular {
+ font: -moz-dialog;
+}
+.small {
+ font: message-box;
+}
+.spacer {
+ height: 40px;
+}
+.foreground > :nth-child(2) {
+ visibility: hidden;
+}
+]]>
+</html:style>
+
+<script type="application/javascript;version=1.8"><![CDATA[
+
+function cE(elem) {
+ return document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul", elem);
+}
+function elWithValue(elem, val) {
+ let e = cE(elem);
+ e.setAttribute(elem == "label" || elem == "textbox" ? "value" : "label", val);
+ return e;
+}
+
+function allPairs(set) {
+ let ps = [];
+ for(let i = 0; i < set.length; ++i) {
+ for (let j = 0; j < set.length; ++j) {
+ if (i != j)
+ ps.push([set[i], set[j]]);
+ }
+ }
+ return ps;
+}
+
+function createLabel(v) {
+ return elWithValue("label", v);
+}
+function createRadio(v) {
+ return elWithValue("radio", v);
+}
+function createCheckbox(v) {
+ return elWithValue("checkbox", v);
+}
+function createButton(v) {
+ return elWithValue("button", v);
+}
+function createTextField(v) {
+ return elWithValue("textbox", v);
+}
+function createMenulist(v) {
+ let [list, popup, item] = [cE("menulist"), cE("menupopup"), elWithValue("menuitem", v)];
+ item.setAttribute("selected", "true");
+ popup.appendChild(item);
+ list.appendChild(popup);
+ return list;
+}
+function createEditableMenulist(v) {
+ let list = createMenulist(v);
+ list.setAttribute("editable", "true");
+ return list;
+}
+function loaded() {
+ let template = document.getElementById("template");
+ ["regular", "small"].forEach(function(size) {
+ let wrapper = document.querySelectorAll("#wrapper > ." + size)[0];
+ allPairs([
+ createButton, createMenulist, createTextField, createEditableMenulist,
+ ]).forEach(function(elemList) {
+ let newBox = template.cloneNode(true);
+ newBox.className = "spacer";
+ let foregroundRow = newBox.firstChild;
+ elemList.forEach(function(creator) {
+ foregroundRow.appendChild(creator("Label"));
+ });
+ wrapper.appendChild(newBox);
+ });
+ });
+ document.documentElement.className = "";
+}
+
+]]></script>
+ <vbox id="template">
+ <hbox class="foreground"/>
+ </vbox>
+ <hbox id="wrapper">
+ <vbox class="regular" width="500"/>
+ <vbox class="small" flex="1"/>
+ </hbox>
+
+ <spacer flex="1"/>
+
+</window>
diff --git a/toolkit/themes/osx/reftests/radiosize-ref.xul b/toolkit/themes/osx/reftests/radiosize-ref.xul
new file mode 100644
index 0000000000..632f39e41f
--- /dev/null
+++ b/toolkit/themes/osx/reftests/radiosize-ref.xul
@@ -0,0 +1,32 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="data:text/css,
+vbox { height: 50px; }
+box {
+ -moz-appearance: radio;
+ margin-left: 2px;
+ margin-top: 1px;
+}
+" type="text/css"?>
+
+<window title="Reference for mini, small and regular radio button sizes"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <vbox>
+ <hbox><box width="11" height="11"/></hbox>
+ </vbox>
+ <vbox>
+ <hbox><box width="13" height="13"/></hbox>
+ </vbox>
+ <vbox>
+ <hbox><box width="16" height="16"/></hbox>
+ </vbox>
+ <vbox>
+ <hbox selected="true"><box width="11" height="11"/></hbox>
+ </vbox>
+ <vbox>
+ <hbox selected="true"><box width="13" height="13"/></hbox>
+ </vbox>
+ <vbox>
+ <hbox selected="true"><box width="16" height="16"/></hbox>
+ </vbox>
+</window>
diff --git a/toolkit/themes/osx/reftests/radiosize.xul b/toolkit/themes/osx/reftests/radiosize.xul
new file mode 100644
index 0000000000..44f735db07
--- /dev/null
+++ b/toolkit/themes/osx/reftests/radiosize.xul
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="data:text/css,
+vbox { height: 50px; }
+radio {
+ color: transparent;
+ margin: 0;
+}
+" type="text/css"?>
+
+<window title="Radio buttons with mini, small and regular control font"
+ xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+ <vbox style="font-size: 9px">
+ <hbox><radio label="Mini"/></hbox>
+ </vbox>
+ <vbox style="font: message-box">
+ <hbox><radio label="Small"/></hbox>
+ </vbox>
+ <vbox style="font: -moz-dialog">
+ <hbox><radio label="Regular"/></hbox>
+ </vbox>
+ <vbox style="font-size: 9px">
+ <hbox><radio label="Mini" selected="true"/></hbox>
+ </vbox>
+ <vbox style="font: message-box">
+ <hbox><radio label="Small" selected="true"/></hbox>
+ </vbox>
+ <vbox style="font: -moz-dialog">
+ <hbox><radio label="Regular" selected="true"/></hbox>
+ </vbox>
+</window>
diff --git a/toolkit/themes/osx/reftests/reftest-stylo.list b/toolkit/themes/osx/reftests/reftest-stylo.list
new file mode 100644
index 0000000000..82df4273dc
--- /dev/null
+++ b/toolkit/themes/osx/reftests/reftest-stylo.list
@@ -0,0 +1,6 @@
+# DO NOT EDIT! This is a auto-generated temporary list for Stylo testing
+skip-if(!cocoaWidget) == 482681.xul 482681.xul
+skip-if(!cocoaWidget) == radiosize.xul radiosize.xul
+skip-if(!cocoaWidget) == checkboxsize.xul checkboxsize.xul
+skip-if(!cocoaWidget) == baseline.xul baseline.xul
+skip-if(!cocoaWidget) == nostretch.xul nostretch.xul
diff --git a/toolkit/themes/osx/reftests/reftest.list b/toolkit/themes/osx/reftests/reftest.list
new file mode 100644
index 0000000000..27034dd862
--- /dev/null
+++ b/toolkit/themes/osx/reftests/reftest.list
@@ -0,0 +1,5 @@
+skip-if(!cocoaWidget) == 482681.xul 482681-ref.xul
+skip-if(!cocoaWidget) == radiosize.xul radiosize-ref.xul
+skip-if(!cocoaWidget) == checkboxsize.xul checkboxsize-ref.xul
+skip-if(!cocoaWidget) == baseline.xul about:blank
+skip-if(!cocoaWidget) == nostretch.xul nostretch-ref.xul
diff --git a/toolkit/xre/MacApplicationDelegate.h b/toolkit/xre/MacApplicationDelegate.h
new file mode 100644
index 0000000000..74f9a93ed2
--- /dev/null
+++ b/toolkit/xre/MacApplicationDelegate.h
@@ -0,0 +1,16 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// This file defines the interface between Cocoa-specific Obj-C++ and generic C++,
+// so it itself cannot have any Obj-C bits in it.
+
+#ifndef MacApplicationDelegate_h_
+#define MacApplicationDelegate_h_
+
+void EnsureUseCocoaDockAPI(void);
+void SetupMacApplicationDelegate(void);
+void ProcessPendingGetURLAppleEvents(void);
+
+#endif
diff --git a/toolkit/xre/MacApplicationDelegate.mm b/toolkit/xre/MacApplicationDelegate.mm
new file mode 100644
index 0000000000..2b295aa7d9
--- /dev/null
+++ b/toolkit/xre/MacApplicationDelegate.mm
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+// NSApplication delegate for Mac OS X Cocoa API.
+
+// As of 10.4 Tiger, the system can send six kinds of Apple Events to an application;
+// a well-behaved XUL app should have some kind of handling for all of them.
+//
+// See http://developer.apple.com/documentation/Cocoa/Conceptual/ScriptableCocoaApplications/SApps_handle_AEs/chapter_11_section_3.html for details.
+
+#import <Cocoa/Cocoa.h>
+#import <Carbon/Carbon.h>
+
+#include "nsCOMPtr.h"
+#include "nsINativeAppSupport.h"
+#include "nsAppRunner.h"
+#include "nsAppShell.h"
+#include "nsComponentManagerUtils.h"
+#include "nsIServiceManager.h"
+#include "nsServiceManagerUtils.h"
+#include "nsIAppStartup.h"
+#include "nsIObserverService.h"
+#include "nsISupportsPrimitives.h"
+#include "nsObjCExceptions.h"
+#include "nsIFile.h"
+#include "nsDirectoryServiceDefs.h"
+#include "nsICommandLineRunner.h"
+#include "nsIMacDockSupport.h"
+#include "nsIStandaloneNativeMenu.h"
+#include "nsILocalFileMac.h"
+#include "nsString.h"
+#include "nsCommandLineServiceMac.h"
+
+class AutoAutoreleasePool {
+public:
+ AutoAutoreleasePool()
+ {
+ mLocalPool = [[NSAutoreleasePool alloc] init];
+ }
+ ~AutoAutoreleasePool()
+ {
+ [mLocalPool release];
+ }
+private:
+ NSAutoreleasePool *mLocalPool;
+};
+
+@interface MacApplicationDelegate : NSObject<NSApplicationDelegate>
+{
+}
+
+@end
+
+static bool sProcessedGetURLEvent = false;
+
+// Methods that can be called from non-Objective-C code.
+
+// This is needed, on relaunch, to force the OS to use the "Cocoa Dock API"
+// instead of the "Carbon Dock API". For more info see bmo bug 377166.
+void
+EnsureUseCocoaDockAPI()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ [GeckoNSApplication sharedApplication];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+void
+SetupMacApplicationDelegate()
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ // this is called during startup, outside an event loop, and therefore
+ // needs an autorelease pool to avoid cocoa object leakage (bug 559075)
+ AutoAutoreleasePool pool;
+
+ // Ensure that ProcessPendingGetURLAppleEvents() doesn't regress bug 377166.
+ [GeckoNSApplication sharedApplication];
+
+ // This call makes it so that application:openFile: doesn't get bogus calls
+ // from Cocoa doing its own parsing of the argument string. And yes, we need
+ // to use a string with a boolean value in it. That's just how it works.
+ [[NSUserDefaults standardUserDefaults] setObject:@"NO"
+ forKey:@"NSTreatUnknownArgumentsAsOpen"];
+
+ // Create the delegate. This should be around for the lifetime of the app.
+ id<NSApplicationDelegate> delegate = [[MacApplicationDelegate alloc] init];
+ [[GeckoNSApplication sharedApplication] setDelegate:delegate];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+// Indirectly make the OS process any pending GetURL Apple events. This is
+// done via _DPSNextEvent() (an undocumented AppKit function called from
+// [NSApplication nextEventMatchingMask:untilDate:inMode:dequeue:]). Apple
+// events are only processed if 'dequeue' is 'YES' -- so we need to call
+// [NSApplication sendEvent:] on any event that gets returned. 'event' will
+// never itself be an Apple event, and it may be 'nil' even when Apple events
+// are processed.
+void
+ProcessPendingGetURLAppleEvents()
+{
+ AutoAutoreleasePool pool;
+ bool keepSpinning = true;
+ while (keepSpinning) {
+ sProcessedGetURLEvent = false;
+ NSEvent *event = [NSApp nextEventMatchingMask:NSAnyEventMask
+ untilDate:nil
+ inMode:NSDefaultRunLoopMode
+ dequeue:YES];
+ if (event)
+ [NSApp sendEvent:event];
+ keepSpinning = sProcessedGetURLEvent;
+ }
+}
+
+@implementation MacApplicationDelegate
+
+- (id)init
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ if ((self = [super init])) {
+ NSAppleEventManager *aeMgr = [NSAppleEventManager sharedAppleEventManager];
+
+ [aeMgr setEventHandler:self
+ andSelector:@selector(handleAppleEvent:withReplyEvent:)
+ forEventClass:kInternetEventClass
+ andEventID:kAEGetURL];
+
+ [aeMgr setEventHandler:self
+ andSelector:@selector(handleAppleEvent:withReplyEvent:)
+ forEventClass:'WWW!'
+ andEventID:'OURL'];
+
+ [aeMgr setEventHandler:self
+ andSelector:@selector(handleAppleEvent:withReplyEvent:)
+ forEventClass:kCoreEventClass
+ andEventID:kAEOpenDocuments];
+
+ if (![NSApp windowsMenu]) {
+ // If the application has a windows menu, it will keep it up to date and
+ // prepend the window list to the Dock menu automatically.
+ NSMenu* windowsMenu = [[NSMenu alloc] initWithTitle:@"Window"];
+ [NSApp setWindowsMenu:windowsMenu];
+ [windowsMenu release];
+ }
+ }
+ return self;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(nil);
+}
+
+- (void)dealloc
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ NSAppleEventManager *aeMgr = [NSAppleEventManager sharedAppleEventManager];
+ [aeMgr removeEventHandlerForEventClass:kInternetEventClass andEventID:kAEGetURL];
+ [aeMgr removeEventHandlerForEventClass:'WWW!' andEventID:'OURL'];
+ [aeMgr removeEventHandlerForEventClass:kCoreEventClass andEventID:kAEOpenDocuments];
+ [super dealloc];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+// The method that NSApplication calls upon a request to reopen, such as when
+// the Dock icon is clicked and no windows are open. A "visible" window may be
+// miniaturized, so we can't skip nsCocoaNativeReOpen() if 'flag' is 'true'.
+- (BOOL)applicationShouldHandleReopen:(NSApplication*)theApp hasVisibleWindows:(BOOL)flag
+{
+ nsCOMPtr<nsINativeAppSupport> nas = do_CreateInstance(NS_NATIVEAPPSUPPORT_CONTRACTID);
+ NS_ENSURE_TRUE(nas, NO);
+
+ // Go to the common Carbon/Cocoa reopen method.
+ nsresult rv = nas->ReOpen();
+ NS_ENSURE_SUCCESS(rv, NO);
+
+ // NO says we don't want NSApplication to do anything else for us.
+ return NO;
+}
+
+// The method that NSApplication calls when documents are requested to be opened.
+// It will be called once for each selected document.
+- (BOOL)application:(NSApplication*)theApplication openFile:(NSString*)filename
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
+
+ NSURL *url = [NSURL fileURLWithPath:filename];
+ if (!url)
+ return NO;
+
+ NSString *urlString = [url absoluteString];
+ if (!urlString)
+ return NO;
+
+ // Add the URL to any command line we're currently setting up.
+ if (CommandLineServiceMac::AddURLToCurrentCommandLine([urlString UTF8String]))
+ return YES;
+
+ nsCOMPtr<nsILocalFileMac> inFile;
+ nsresult rv = NS_NewLocalFileWithCFURL((CFURLRef)url, true, getter_AddRefs(inFile));
+ if (NS_FAILED(rv))
+ return NO;
+
+ nsCOMPtr<nsICommandLineRunner> cmdLine(do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
+ if (!cmdLine) {
+ NS_ERROR("Couldn't create command line!");
+ return NO;
+ }
+
+ nsCString filePath;
+ rv = inFile->GetNativePath(filePath);
+ if (NS_FAILED(rv))
+ return NO;
+
+ nsCOMPtr<nsIFile> workingDir;
+ rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir));
+ if (NS_FAILED(rv))
+ return NO;
+
+ const char *argv[3] = {nullptr, "-file", filePath.get()};
+ rv = cmdLine->Init(3, argv, workingDir, nsICommandLine::STATE_REMOTE_EXPLICIT);
+ if (NS_FAILED(rv))
+ return NO;
+
+ if (NS_SUCCEEDED(cmdLine->Run()))
+ return YES;
+
+ return NO;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
+}
+
+// The method that NSApplication calls when documents are requested to be printed
+// from the Finder (under the "File" menu).
+// It will be called once for each selected document.
+- (BOOL)application:(NSApplication*)theApplication printFile:(NSString*)filename
+{
+ return NO;
+}
+
+// Create the menu that shows up in the Dock.
+- (NSMenu*)applicationDockMenu:(NSApplication*)sender
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ // Create the NSMenu that will contain the dock menu items.
+ NSMenu *menu = [[[NSMenu alloc] initWithTitle:@""] autorelease];
+ [menu setAutoenablesItems:NO];
+
+ // Add application-specific dock menu items. On error, do not insert the
+ // dock menu items.
+ nsresult rv;
+ nsCOMPtr<nsIMacDockSupport> dockSupport = do_GetService("@mozilla.org/widget/macdocksupport;1", &rv);
+ if (NS_FAILED(rv) || !dockSupport)
+ return menu;
+
+ nsCOMPtr<nsIStandaloneNativeMenu> dockMenu;
+ rv = dockSupport->GetDockMenu(getter_AddRefs(dockMenu));
+ if (NS_FAILED(rv) || !dockMenu)
+ return menu;
+
+ // Determine if the dock menu items should be displayed. This also gives
+ // the menu the opportunity to update itself before display.
+ bool shouldShowItems;
+ rv = dockMenu->MenuWillOpen(&shouldShowItems);
+ if (NS_FAILED(rv) || !shouldShowItems)
+ return menu;
+
+ // Obtain a copy of the native menu.
+ NSMenu * nativeDockMenu;
+ rv = dockMenu->GetNativeMenu(reinterpret_cast<void **>(&nativeDockMenu));
+ if (NS_FAILED(rv) || !nativeDockMenu)
+ return menu;
+
+ // Loop through the application-specific dock menu and insert its
+ // contents into the dock menu that we are building for Cocoa.
+ int numDockMenuItems = [nativeDockMenu numberOfItems];
+ if (numDockMenuItems > 0) {
+ if ([menu numberOfItems] > 0)
+ [menu addItem:[NSMenuItem separatorItem]];
+
+ for (int i = 0; i < numDockMenuItems; i++) {
+ NSMenuItem * itemCopy = [[nativeDockMenu itemAtIndex:i] copy];
+ [menu addItem:itemCopy];
+ [itemCopy release];
+ }
+ }
+
+ return menu;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+// If we don't handle applicationShouldTerminate:, a call to [NSApp terminate:]
+// (from the browser or from the OS) can result in an unclean shutdown.
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication *)sender
+{
+ nsCOMPtr<nsIObserverService> obsServ =
+ do_GetService("@mozilla.org/observer-service;1");
+ if (!obsServ)
+ return NSTerminateNow;
+
+ nsCOMPtr<nsISupportsPRBool> cancelQuit =
+ do_CreateInstance(NS_SUPPORTS_PRBOOL_CONTRACTID);
+ if (!cancelQuit)
+ return NSTerminateNow;
+
+ cancelQuit->SetData(false);
+ obsServ->NotifyObservers(cancelQuit, "quit-application-requested", nullptr);
+
+ bool abortQuit;
+ cancelQuit->GetData(&abortQuit);
+ if (abortQuit)
+ return NSTerminateCancel;
+
+ nsCOMPtr<nsIAppStartup> appService =
+ do_GetService("@mozilla.org/toolkit/app-startup;1");
+ if (appService)
+ appService->Quit(nsIAppStartup::eForceQuit);
+
+ return NSTerminateNow;
+}
+
+- (void)handleAppleEvent:(NSAppleEventDescriptor*)event withReplyEvent:(NSAppleEventDescriptor*)replyEvent
+{
+ if (!event)
+ return;
+
+ AutoAutoreleasePool pool;
+
+ bool isGetURLEvent =
+ ([event eventClass] == kInternetEventClass && [event eventID] == kAEGetURL);
+ if (isGetURLEvent)
+ sProcessedGetURLEvent = true;
+
+ if (isGetURLEvent ||
+ ([event eventClass] == 'WWW!' && [event eventID] == 'OURL')) {
+ NSString* urlString = [[event paramDescriptorForKeyword:keyDirectObject] stringValue];
+
+ // don't open chrome URLs
+ NSString* schemeString = [[NSURL URLWithString:urlString] scheme];
+ if (!schemeString ||
+ [schemeString compare:@"chrome"
+ options:NSCaseInsensitiveSearch
+ range:NSMakeRange(0, [schemeString length])] == NSOrderedSame) {
+ return;
+ }
+
+ // Add the URL to any command line we're currently setting up.
+ if (CommandLineServiceMac::AddURLToCurrentCommandLine([urlString UTF8String]))
+ return;
+
+ nsCOMPtr<nsICommandLineRunner> cmdLine(do_CreateInstance("@mozilla.org/toolkit/command-line;1"));
+ if (!cmdLine) {
+ NS_ERROR("Couldn't create command line!");
+ return;
+ }
+ nsCOMPtr<nsIFile> workingDir;
+ nsresult rv = NS_GetSpecialDirectory(NS_OS_CURRENT_WORKING_DIR, getter_AddRefs(workingDir));
+ if (NS_FAILED(rv))
+ return;
+ const char *argv[3] = {nullptr, "-url", [urlString UTF8String]};
+ rv = cmdLine->Init(3, argv, workingDir, nsICommandLine::STATE_REMOTE_EXPLICIT);
+ if (NS_FAILED(rv))
+ return;
+ rv = cmdLine->Run();
+ }
+ else if ([event eventClass] == kCoreEventClass && [event eventID] == kAEOpenDocuments) {
+ NSAppleEventDescriptor* fileListDescriptor = [event paramDescriptorForKeyword:keyDirectObject];
+ if (!fileListDescriptor)
+ return;
+
+ // Descriptor list indexing is one-based...
+ NSInteger numberOfFiles = [fileListDescriptor numberOfItems];
+ for (NSInteger i = 1; i <= numberOfFiles; i++) {
+ NSString* urlString = [[fileListDescriptor descriptorAtIndex:i] stringValue];
+ if (!urlString)
+ continue;
+
+ // We need a path, not a URL
+ NSURL* url = [NSURL URLWithString:urlString];
+ if (!url)
+ continue;
+
+ [self application:NSApp openFile:[url path]];
+ }
+ }
+}
+
+@end
diff --git a/toolkit/xre/MacAutoreleasePool.h b/toolkit/xre/MacAutoreleasePool.h
new file mode 100644
index 0000000000..7668957f19
--- /dev/null
+++ b/toolkit/xre/MacAutoreleasePool.h
@@ -0,0 +1,31 @@
+/* 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 MacAutoreleasePool_h_
+#define MacAutoreleasePool_h_
+
+// This needs to be #include-able from non-ObjC code in nsAppRunner.cpp
+#ifdef __OBJC__
+@class NSAutoreleasePool;
+#else
+class NSAutoreleasePool;
+#endif
+
+namespace mozilla {
+
+class MacAutoreleasePool {
+public:
+ MacAutoreleasePool();
+ ~MacAutoreleasePool();
+
+private:
+ NSAutoreleasePool *mPool;
+
+ MacAutoreleasePool(const MacAutoreleasePool&);
+ void operator=(const MacAutoreleasePool&);
+};
+
+} // namespace mozilla
+
+#endif // MacAutoreleasePool_h_
diff --git a/toolkit/xre/MacAutoreleasePool.mm b/toolkit/xre/MacAutoreleasePool.mm
new file mode 100644
index 0000000000..ae8ad51ff1
--- /dev/null
+++ b/toolkit/xre/MacAutoreleasePool.mm
@@ -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 "MacAutoreleasePool.h"
+#include "nsDebug.h"
+
+#import <Foundation/Foundation.h>
+
+using mozilla::MacAutoreleasePool;
+
+MacAutoreleasePool::MacAutoreleasePool()
+{
+ mPool = [[NSAutoreleasePool alloc] init];
+ NS_ASSERTION(mPool != nullptr, "failed to create pool, objects will leak");
+}
+
+MacAutoreleasePool::~MacAutoreleasePool() {
+ [mPool release];
+}
diff --git a/toolkit/xre/MacLaunchHelper.h b/toolkit/xre/MacLaunchHelper.h
new file mode 100644
index 0000000000..08035c53b6
--- /dev/null
+++ b/toolkit/xre/MacLaunchHelper.h
@@ -0,0 +1,23 @@
+/* -*- 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 MacLaunchHelper_h_
+#define MacLaunchHelper_h_
+
+#include <stdint.h>
+
+#include <unistd.h>
+
+extern "C" {
+ /**
+ * Passing an aPid parameter to LaunchChildMac will wait for the launched
+ * process to terminate. When the process terminates, aPid will be set to the
+ * pid of the terminated process to confirm that it executed successfully.
+ */
+ void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid = 0);
+ bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid = 0);
+}
+
+#endif
diff --git a/toolkit/xre/MacLaunchHelper.mm b/toolkit/xre/MacLaunchHelper.mm
new file mode 100644
index 0000000000..0dadb8de88
--- /dev/null
+++ b/toolkit/xre/MacLaunchHelper.mm
@@ -0,0 +1,137 @@
+/* -*- 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 "MacLaunchHelper.h"
+
+#include "MacAutoreleasePool.h"
+#include "mozilla/UniquePtr.h"
+#include "nsIAppStartup.h"
+#include "nsMemory.h"
+
+#include <Cocoa/Cocoa.h>
+#include <crt_externs.h>
+#include <ServiceManagement/ServiceManagement.h>
+#include <Security/Authorization.h>
+#include <spawn.h>
+#include <stdio.h>
+
+using namespace mozilla;
+
+void LaunchChildMac(int aArgc, char** aArgv, pid_t* aPid)
+{
+ MacAutoreleasePool pool;
+
+ @try {
+ NSString* launchPath = [NSString stringWithUTF8String:aArgv[0]];
+ NSMutableArray* arguments = [NSMutableArray arrayWithCapacity:aArgc - 1];
+ for (int i = 1; i < aArgc; i++) {
+ [arguments addObject:[NSString stringWithUTF8String:aArgv[i]]];
+ }
+ NSTask* child = [NSTask launchedTaskWithLaunchPath:launchPath
+ arguments:arguments];
+ if (aPid) {
+ *aPid = [child processIdentifier];
+ // We used to use waitpid to wait for the process to terminate. This is
+ // incompatible with NSTask and we wait for the process to exit here
+ // instead.
+ [child waitUntilExit];
+ }
+ } @catch (NSException* e) {
+ NSLog(@"%@: %@", e.name, e.reason);
+ }
+}
+
+BOOL InstallPrivilegedHelper()
+{
+ AuthorizationRef authRef = NULL;
+ OSStatus status = AuthorizationCreate(NULL,
+ kAuthorizationEmptyEnvironment,
+ kAuthorizationFlagDefaults |
+ kAuthorizationFlagInteractionAllowed,
+ &authRef);
+ if (status != errAuthorizationSuccess) {
+ // AuthorizationCreate really shouldn't fail.
+ NSLog(@"AuthorizationCreate failed! NSOSStatusErrorDomain / %d",
+ (int)status);
+ return NO;
+ }
+
+ BOOL result = NO;
+ AuthorizationItem authItem = { kSMRightBlessPrivilegedHelper, 0, NULL, 0 };
+ AuthorizationRights authRights = { 1, &authItem };
+ AuthorizationFlags flags = kAuthorizationFlagDefaults |
+ kAuthorizationFlagInteractionAllowed |
+ kAuthorizationFlagPreAuthorize |
+ kAuthorizationFlagExtendRights;
+
+ // Obtain the right to install our privileged helper tool.
+ status = AuthorizationCopyRights(authRef,
+ &authRights,
+ kAuthorizationEmptyEnvironment,
+ flags,
+ NULL);
+ if (status != errAuthorizationSuccess) {
+ NSLog(@"AuthorizationCopyRights failed! NSOSStatusErrorDomain / %d",
+ (int)status);
+ } else {
+ CFErrorRef cfError;
+ // This does all the work of verifying the helper tool against the
+ // application and vice-versa. Once verification has passed, the embedded
+ // launchd.plist is extracted and placed in /Library/LaunchDaemons and then
+ // loaded. The executable is placed in /Library/PrivilegedHelperTools.
+ result = (BOOL)SMJobBless(kSMDomainSystemLaunchd,
+ (CFStringRef)@"org.mozilla.updater",
+ authRef,
+ &cfError);
+ if (!result) {
+ NSLog(@"Unable to install helper!");
+ CFRelease(cfError);
+ }
+ }
+
+ return result;
+}
+
+void AbortElevatedUpdate()
+{
+ mozilla::MacAutoreleasePool pool;
+
+ id updateServer = nil;
+ int currTry = 0;
+ const int numRetries = 10; // Number of IPC connection retries before
+ // giving up.
+ while (currTry < numRetries) {
+ @try {
+ updateServer = (id)[NSConnection
+ rootProxyForConnectionWithRegisteredName:
+ @"org.mozilla.updater.server"
+ host:nil
+ usingNameServer:[NSSocketPortNameServer sharedInstance]];
+ if (updateServer &&
+ [updateServer respondsToSelector:@selector(abort)]) {
+ [updateServer performSelector:@selector(abort)];
+ return;
+ }
+ NSLog(@"Server doesn't exist or doesn't provide correct selectors.");
+ sleep(1); // Wait 1 second.
+ currTry++;
+ } @catch (NSException* e) {
+ NSLog(@"Encountered exception, retrying: %@: %@", e.name, e.reason);
+ sleep(1); // Wait 1 second.
+ currTry++;
+ }
+ }
+ NSLog(@"Unable to clean up updater.");
+}
+
+bool LaunchElevatedUpdate(int aArgc, char** aArgv, pid_t* aPid)
+{
+ LaunchChildMac(aArgc, aArgv, aPid);
+ bool didSucceed = InstallPrivilegedHelper();
+ if (!didSucceed) {
+ AbortElevatedUpdate();
+ }
+ return didSucceed;
+}
diff --git a/toolkit/xre/moz.build b/toolkit/xre/moz.build
index b717e971c3..4072fe134f 100644
--- a/toolkit/xre/moz.build
+++ b/toolkit/xre/moz.build
@@ -19,6 +19,15 @@ if CONFIG['MOZ_INSTRUMENT_EVENT_LOOP']:
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
UNIFIED_SOURCES += ['nsNativeAppSupportWin.cpp']
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += [
+ 'MacApplicationDelegate.mm',
+ 'MacAutoreleasePool.mm',
+ 'MacLaunchHelper.mm',
+ 'nsCommandLineServiceMac.cpp',
+ 'nsNativeAppSupportCocoa.mm',
+ 'updaterfileutils_osx.mm',
+ ]
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
UNIFIED_SOURCES += [
'nsNativeAppSupportDefault.cpp',
@@ -112,6 +121,12 @@ LOCAL_INCLUDES += [
'/xpcom/build',
]
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ LOCAL_INCLUDES += [
+ '/widget',
+ '/widget/cocoa',
+ ]
+
if CONFIG['MOZ_ENABLE_XREMOTE']:
LOCAL_INCLUDES += [
'/widget/xremoteclient',
diff --git a/uriloader/exthandler/mac/nsDecodeAppleFile.cpp b/uriloader/exthandler/mac/nsDecodeAppleFile.cpp
new file mode 100644
index 0000000000..1a428a62db
--- /dev/null
+++ b/uriloader/exthandler/mac/nsDecodeAppleFile.cpp
@@ -0,0 +1,389 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 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/. */
+
+#include "nsDecodeAppleFile.h"
+#include "prmem.h"
+#include "nsCRT.h"
+
+
+NS_IMPL_ADDREF(nsDecodeAppleFile)
+NS_IMPL_RELEASE(nsDecodeAppleFile)
+
+NS_INTERFACE_MAP_BEGIN(nsDecodeAppleFile)
+ NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIOutputStream)
+ NS_INTERFACE_MAP_ENTRY(nsIOutputStream)
+NS_INTERFACE_MAP_END_THREADSAFE
+
+nsDecodeAppleFile::nsDecodeAppleFile()
+{
+ m_state = parseHeaders;
+ m_dataBufferLength = 0;
+ m_dataBuffer = (unsigned char*) PR_MALLOC(MAX_BUFFERSIZE);
+ m_entries = nullptr;
+ m_rfRefNum = -1;
+ m_totalDataForkWritten = 0;
+ m_totalResourceForkWritten = 0;
+ m_headerOk = false;
+
+ m_comment[0] = 0;
+ memset(&m_dates, 0, sizeof(m_dates));
+ memset(&m_finderInfo, 0, sizeof(m_dates));
+ memset(&m_finderExtraInfo, 0, sizeof(m_dates));
+}
+
+nsDecodeAppleFile::~nsDecodeAppleFile()
+{
+
+ PR_FREEIF(m_dataBuffer);
+ if (m_entries)
+ delete [] m_entries;
+}
+
+NS_IMETHODIMP nsDecodeAppleFile::Initialize(nsIOutputStream *output, nsIFile *file)
+{
+ m_output = output;
+
+ nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(file);
+ macFile->GetTargetFSSpec(&m_fsFileSpec);
+
+ m_offset = 0;
+ m_dataForkOffset = 0;
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsDecodeAppleFile::Close(void)
+{
+ nsresult rv;
+ rv = m_output->Close();
+
+ int32_t i;
+
+ if (m_rfRefNum != -1)
+ FSClose(m_rfRefNum);
+
+ /* Check if the file is complete and if it's the case, write file attributes */
+ if (m_headerOk)
+ {
+ bool dataOk = true; /* It's ok if the file doesn't have a datafork, therefore set it to true by default. */
+ if (m_headers.magic == APPLESINGLE_MAGIC)
+ {
+ for (i = 0; i < m_headers.entriesCount; i ++)
+ if (ENT_DFORK == m_entries[i].id)
+ {
+ dataOk = (bool)(m_totalDataForkWritten == m_entries[i].length);
+ break;
+ }
+ }
+
+ bool resourceOk = FALSE;
+ for (i = 0; i < m_headers.entriesCount; i ++)
+ if (ENT_RFORK == m_entries[i].id)
+ {
+ resourceOk = (bool)(m_totalResourceForkWritten == m_entries[i].length);
+ break;
+ }
+
+ if (dataOk && resourceOk)
+ {
+ HFileInfo *fpb;
+ CInfoPBRec cipbr;
+
+ fpb = (HFileInfo *) &cipbr;
+ fpb->ioVRefNum = m_fsFileSpec.vRefNum;
+ fpb->ioDirID = m_fsFileSpec.parID;
+ fpb->ioNamePtr = m_fsFileSpec.name;
+ fpb->ioFDirIndex = 0;
+ PBGetCatInfoSync(&cipbr);
+
+ /* set finder info */
+ memcpy(&fpb->ioFlFndrInfo, &m_finderInfo, sizeof (FInfo));
+ memcpy(&fpb->ioFlXFndrInfo, &m_finderExtraInfo, sizeof (FXInfo));
+ fpb->ioFlFndrInfo.fdFlags &= 0xfc00; /* clear flags maintained by finder */
+
+ /* set file dates */
+ fpb->ioFlCrDat = m_dates.create - CONVERT_TIME;
+ fpb->ioFlMdDat = m_dates.modify - CONVERT_TIME;
+ fpb->ioFlBkDat = m_dates.backup - CONVERT_TIME;
+
+ /* update file info */
+ fpb->ioDirID = fpb->ioFlParID;
+ PBSetCatInfoSync(&cipbr);
+
+ /* set comment */
+ IOParam vinfo;
+ GetVolParmsInfoBuffer vp;
+ DTPBRec dtp;
+
+ memset((void *) &vinfo, 0, sizeof (vinfo));
+ vinfo.ioVRefNum = fpb->ioVRefNum;
+ vinfo.ioBuffer = (Ptr) &vp;
+ vinfo.ioReqCount = sizeof (vp);
+ if (PBHGetVolParmsSync((HParmBlkPtr) &vinfo) == noErr && ((vp.vMAttrib >> bHasDesktopMgr) & 1))
+ {
+ memset((void *) &dtp, 0, sizeof (dtp));
+ dtp.ioVRefNum = fpb->ioVRefNum;
+ if (PBDTGetPath(&dtp) == noErr)
+ {
+ dtp.ioDTBuffer = (Ptr) &m_comment[1];
+ dtp.ioNamePtr = fpb->ioNamePtr;
+ dtp.ioDirID = fpb->ioDirID;
+ dtp.ioDTReqCount = m_comment[0];
+ if (PBDTSetCommentSync(&dtp) == noErr)
+ PBDTFlushSync(&dtp);
+ }
+ }
+ }
+ }
+
+ return rv;
+}
+
+NS_IMETHODIMP nsDecodeAppleFile::Flush(void)
+{
+ return m_output->Flush();
+}
+
+NS_IMETHODIMP nsDecodeAppleFile::WriteFrom(nsIInputStream *inStr, uint32_t count, uint32_t *_retval)
+{
+ return m_output->WriteFrom(inStr, count, _retval);
+}
+
+NS_IMETHODIMP nsDecodeAppleFile::WriteSegments(nsReadSegmentFun reader, void * closure, uint32_t count, uint32_t *_retval)
+{
+ return m_output->WriteSegments(reader, closure, count, _retval);
+}
+
+NS_IMETHODIMP nsDecodeAppleFile::IsNonBlocking(bool *aNonBlocking)
+{
+ return m_output->IsNonBlocking(aNonBlocking);
+}
+
+NS_IMETHODIMP nsDecodeAppleFile::Write(const char *buffer, uint32_t bufferSize, uint32_t* writeCount)
+{
+ /* WARNING: to simplify my life, I presume that I should get all appledouble headers in the first block,
+ else I would have to implement a buffer */
+
+ const char * buffPtr = buffer;
+ uint32_t dataCount;
+ int32_t i;
+ nsresult rv = NS_OK;
+
+ *writeCount = 0;
+
+ while (bufferSize > 0 && NS_SUCCEEDED(rv))
+ {
+ switch (m_state)
+ {
+ case parseHeaders :
+ dataCount = sizeof(ap_header) - m_dataBufferLength;
+ if (dataCount > bufferSize)
+ dataCount = bufferSize;
+ memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount);
+ m_dataBufferLength += dataCount;
+
+ if (m_dataBufferLength == sizeof(ap_header))
+ {
+ memcpy(&m_headers, m_dataBuffer, sizeof(ap_header));
+
+ /* Check header to be sure we are dealing with the right kind of data, else just write it to the data fork. */
+ if ((m_headers.magic == APPLEDOUBLE_MAGIC || m_headers.magic == APPLESINGLE_MAGIC) &&
+ m_headers.version == VERSION && m_headers.entriesCount)
+ {
+ /* Just to be sure, the filler must contains only 0 */
+ for (i = 0; i < 4 && m_headers.fill[i] == 0L; i ++)
+ ;
+ if (i == 4)
+ m_state = parseEntries;
+ }
+ m_dataBufferLength = 0;
+
+ if (m_state == parseHeaders)
+ {
+ dataCount = 0;
+ m_state = parseWriteThrough;
+ }
+ }
+ break;
+
+ case parseEntries :
+ if (!m_entries)
+ {
+ m_entries = new ap_entry[m_headers.entriesCount];
+ if (!m_entries)
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ uint32_t entriesSize = sizeof(ap_entry) * m_headers.entriesCount;
+ dataCount = entriesSize - m_dataBufferLength;
+ if (dataCount > bufferSize)
+ dataCount = bufferSize;
+ memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount);
+ m_dataBufferLength += dataCount;
+
+ if (m_dataBufferLength == entriesSize)
+ {
+ for (i = 0; i < m_headers.entriesCount; i ++)
+ {
+ memcpy(&m_entries[i], &m_dataBuffer[i * sizeof(ap_entry)], sizeof(ap_entry));
+ if (m_headers.magic == APPLEDOUBLE_MAGIC)
+ {
+ uint32_t offset = m_entries[i].offset + m_entries[i].length;
+ if (offset > m_dataForkOffset)
+ m_dataForkOffset = offset;
+ }
+ }
+ m_headerOk = true;
+ m_state = parseLookupPart;
+ }
+ break;
+
+ case parseLookupPart :
+ /* which part are we parsing? */
+ m_currentPartID = -1;
+ for (i = 0; i < m_headers.entriesCount; i ++)
+ if (m_offset == m_entries[i].offset && m_entries[i].length)
+ {
+ m_currentPartID = m_entries[i].id;
+ m_currentPartLength = m_entries[i].length;
+ m_currentPartCount = 0;
+
+ switch (m_currentPartID)
+ {
+ case ENT_DFORK : m_state = parseDataFork; break;
+ case ENT_RFORK : m_state = parseResourceFork; break;
+
+ case ENT_COMMENT :
+ case ENT_DATES :
+ case ENT_FINFO :
+ m_dataBufferLength = 0;
+ m_state = parsePart;
+ break;
+
+ default : m_state = parseSkipPart; break;
+ }
+ break;
+ }
+
+ if (m_currentPartID == -1)
+ {
+ /* maybe is the datafork of an appledouble file? */
+ if (m_offset == m_dataForkOffset)
+ {
+ m_currentPartID = ENT_DFORK;
+ m_currentPartLength = -1;
+ m_currentPartCount = 0;
+ m_state = parseDataFork;
+ }
+ else
+ dataCount = 1;
+ }
+ break;
+
+ case parsePart :
+ dataCount = m_currentPartLength - m_dataBufferLength;
+ if (dataCount > bufferSize)
+ dataCount = bufferSize;
+ memcpy(&m_dataBuffer[m_dataBufferLength], buffPtr, dataCount);
+ m_dataBufferLength += dataCount;
+
+ if (m_dataBufferLength == m_currentPartLength)
+ {
+ switch (m_currentPartID)
+ {
+ case ENT_COMMENT :
+ m_comment[0] = m_currentPartLength > 255 ? 255 : m_currentPartLength;
+ memcpy(&m_comment[1], buffPtr, m_comment[0]);
+ break;
+ case ENT_DATES :
+ if (m_currentPartLength == sizeof(m_dates))
+ memcpy(&m_dates, buffPtr, m_currentPartLength);
+ break;
+ case ENT_FINFO :
+ if (m_currentPartLength == (sizeof(m_finderInfo) + sizeof(m_finderExtraInfo)))
+ {
+ memcpy(&m_finderInfo, buffPtr, sizeof(m_finderInfo));
+ memcpy(&m_finderExtraInfo, buffPtr + sizeof(m_finderInfo), sizeof(m_finderExtraInfo));
+ }
+ break;
+ }
+ m_state = parseLookupPart;
+ }
+ break;
+
+ case parseSkipPart :
+ dataCount = m_currentPartLength - m_currentPartCount;
+ if (dataCount > bufferSize)
+ dataCount = bufferSize;
+ else
+ m_state = parseLookupPart;
+ break;
+
+ case parseDataFork :
+ if (m_headers.magic == APPLEDOUBLE_MAGIC)
+ dataCount = bufferSize;
+ else
+ {
+ dataCount = m_currentPartLength - m_currentPartCount;
+ if (dataCount > bufferSize)
+ dataCount = bufferSize;
+ else
+ m_state = parseLookupPart;
+ }
+
+ if (m_output)
+ {
+ uint32_t writeCount;
+ rv = m_output->Write((const char *)buffPtr, dataCount, &writeCount);
+ if (dataCount != writeCount)
+ rv = NS_ERROR_FAILURE;
+ m_totalDataForkWritten += dataCount;
+ }
+
+ break;
+
+ case parseResourceFork :
+ dataCount = m_currentPartLength - m_currentPartCount;
+ if (dataCount > bufferSize)
+ dataCount = bufferSize;
+ else
+ m_state = parseLookupPart;
+
+ if (m_rfRefNum == -1)
+ {
+ if (noErr != FSpOpenRF(&m_fsFileSpec, fsWrPerm, &m_rfRefNum))
+ return NS_ERROR_FAILURE;
+ }
+
+ long count = dataCount;
+ if (noErr != FSWrite(m_rfRefNum, &count, buffPtr) || count != dataCount)
+ return NS_ERROR_FAILURE;
+ m_totalResourceForkWritten += dataCount;
+ break;
+
+ case parseWriteThrough :
+ dataCount = bufferSize;
+ if (m_output)
+ {
+ uint32_t writeCount;
+ rv = m_output->Write((const char *)buffPtr, dataCount, &writeCount);
+ if (dataCount != writeCount)
+ rv = NS_ERROR_FAILURE;
+ }
+ break;
+ }
+
+ if (dataCount)
+ {
+ *writeCount += dataCount;
+ bufferSize -= dataCount;
+ buffPtr += dataCount;
+ m_currentPartCount += dataCount;
+ m_offset += dataCount;
+ dataCount = 0;
+ }
+ }
+
+ return rv;
+}
diff --git a/uriloader/exthandler/mac/nsDecodeAppleFile.h b/uriloader/exthandler/mac/nsDecodeAppleFile.h
new file mode 100644
index 0000000000..cea2d701ec
--- /dev/null
+++ b/uriloader/exthandler/mac/nsDecodeAppleFile.h
@@ -0,0 +1,118 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 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 nsDecodeAppleFile_h__
+#define nsDecodeAppleFile_h__
+
+#include "nscore.h"
+#include "nsCOMPtr.h"
+#include "nsIFile.h"
+#include "nsILocalFileMac.h"
+#include "nsIOutputStream.h"
+
+/*
+** applefile definitions used
+*/
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=mac68k
+#endif
+
+#define APPLESINGLE_MAGIC 0x00051600L
+#define APPLEDOUBLE_MAGIC 0x00051607L
+#define VERSION 0x00020000
+
+#define NUM_ENTRIES 6
+
+#define ENT_DFORK 1L
+#define ENT_RFORK 2L
+#define ENT_NAME 3L
+#define ENT_COMMENT 4L
+#define ENT_DATES 8L
+#define ENT_FINFO 9L
+
+#define CONVERT_TIME 1265437696L
+
+/*
+** data type used in the header decoder.
+*/
+typedef struct ap_header
+{
+ int32_t magic;
+ int32_t version;
+ int32_t fill[4];
+ int16_t entriesCount;
+
+} ap_header;
+
+typedef struct ap_entry
+{
+ int32_t id;
+ int32_t offset;
+ int32_t length;
+
+} ap_entry;
+
+typedef struct ap_dates
+{
+ int32_t create, modify, backup, access;
+
+} ap_dates;
+
+#if PRAGMA_STRUCT_ALIGN
+ #pragma options align=reset
+#endif
+
+/*
+**Error codes
+*/
+enum {
+ errADNotEnoughData = -12099,
+ errADNotSupported,
+ errADBadVersion
+};
+
+
+class nsDecodeAppleFile : public nsIOutputStream
+{
+public:
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIOUTPUTSTREAM
+
+ nsDecodeAppleFile();
+ virtual ~nsDecodeAppleFile();
+
+ MOZ_MUST_USE nsresult Initialize(nsIOutputStream *output, nsIFile *file);
+
+private:
+ #define MAX_BUFFERSIZE 1024
+ enum ParserState {parseHeaders, parseEntries, parseLookupPart, parsePart, parseSkipPart,
+ parseDataFork, parseResourceFork, parseWriteThrough};
+
+ nsCOMPtr<nsIOutputStream> m_output;
+ FSSpec m_fsFileSpec;
+ SInt16 m_rfRefNum;
+
+ unsigned char * m_dataBuffer;
+ int32_t m_dataBufferLength;
+ ParserState m_state;
+ ap_header m_headers;
+ ap_entry * m_entries;
+ int32_t m_offset;
+ int32_t m_dataForkOffset;
+ int32_t m_totalDataForkWritten;
+ int32_t m_totalResourceForkWritten;
+ bool m_headerOk;
+
+ int32_t m_currentPartID;
+ int32_t m_currentPartLength;
+ int32_t m_currentPartCount;
+
+ Str255 m_comment;
+ ap_dates m_dates;
+ FInfo m_finderInfo;
+ FXInfo m_finderExtraInfo;
+};
+
+#endif
diff --git a/uriloader/exthandler/mac/nsLocalHandlerAppMac.h b/uriloader/exthandler/mac/nsLocalHandlerAppMac.h
new file mode 100644
index 0000000000..402ec52953
--- /dev/null
+++ b/uriloader/exthandler/mac/nsLocalHandlerAppMac.h
@@ -0,0 +1,26 @@
+/* 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 NSLOCALHANDLERAPPMAC_H_
+#define NSLOCALHANDLERAPPMAC_H_
+
+#include "nsLocalHandlerApp.h"
+
+class nsLocalHandlerAppMac : public nsLocalHandlerApp {
+
+ public:
+ nsLocalHandlerAppMac() { }
+
+ nsLocalHandlerAppMac(const char16_t *aName, nsIFile *aExecutable)
+ : nsLocalHandlerApp(aName, aExecutable) {}
+
+ nsLocalHandlerAppMac(const nsAString & aName, nsIFile *aExecutable)
+ : nsLocalHandlerApp(aName, aExecutable) {}
+ virtual ~nsLocalHandlerAppMac() { }
+
+ NS_IMETHOD LaunchWithURI(nsIURI* aURI, nsIInterfaceRequestor* aWindowContext);
+ NS_IMETHOD GetName(nsAString& aName);
+};
+
+#endif /*NSLOCALHANDLERAPPMAC_H_*/
diff --git a/uriloader/exthandler/mac/nsLocalHandlerAppMac.mm b/uriloader/exthandler/mac/nsLocalHandlerAppMac.mm
new file mode 100644
index 0000000000..fd633ab726
--- /dev/null
+++ b/uriloader/exthandler/mac/nsLocalHandlerAppMac.mm
@@ -0,0 +1,84 @@
+/* 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 <CoreFoundation/CoreFoundation.h>
+#import <ApplicationServices/ApplicationServices.h>
+
+#include "nsObjCExceptions.h"
+#include "nsLocalHandlerAppMac.h"
+#include "nsILocalFileMac.h"
+#include "nsIURI.h"
+
+// We override this to make sure app bundles display their pretty name (without .app suffix)
+NS_IMETHODIMP nsLocalHandlerAppMac::GetName(nsAString& aName)
+{
+ if (mExecutable) {
+ nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(mExecutable);
+ if (macFile) {
+ bool isPackage;
+ (void)macFile->IsPackage(&isPackage);
+ if (isPackage)
+ return macFile->GetBundleDisplayName(aName);
+ }
+ }
+
+ return nsLocalHandlerApp::GetName(aName);
+}
+
+/**
+ * mostly copy/pasted from nsMacShellService.cpp (which is in browser/,
+ * so we can't depend on it here). This code probably really wants to live
+ * somewhere more central (see bug 389922).
+ */
+NS_IMETHODIMP
+nsLocalHandlerAppMac::LaunchWithURI(nsIURI *aURI,
+ nsIInterfaceRequestor *aWindowContext)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ nsresult rv;
+ nsCOMPtr<nsILocalFileMac> lfm(do_QueryInterface(mExecutable, &rv));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ CFURLRef appURL;
+ rv = lfm->GetCFURL(&appURL);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsAutoCString uriSpec;
+ aURI->GetAsciiSpec(uriSpec);
+
+ const UInt8* uriString = reinterpret_cast<const UInt8*>(uriSpec.get());
+ CFURLRef uri = ::CFURLCreateWithBytes(NULL, uriString, uriSpec.Length(),
+ kCFStringEncodingUTF8, NULL);
+ if (!uri) {
+ ::CFRelease(appURL);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ CFArrayRef uris = ::CFArrayCreate(NULL, reinterpret_cast<const void**>(&uri),
+ 1, NULL);
+ if (!uris) {
+ ::CFRelease(uri);
+ ::CFRelease(appURL);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ LSLaunchURLSpec launchSpec;
+ launchSpec.appURL = appURL;
+ launchSpec.itemURLs = uris;
+ launchSpec.passThruParams = NULL;
+ launchSpec.launchFlags = kLSLaunchDefaults;
+ launchSpec.asyncRefCon = NULL;
+
+ OSErr err = ::LSOpenFromURLSpec(&launchSpec, NULL);
+
+ ::CFRelease(uris);
+ ::CFRelease(uri);
+ ::CFRelease(appURL);
+
+ return err != noErr ? NS_ERROR_FAILURE : NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
diff --git a/uriloader/exthandler/mac/nsMIMEInfoMac.h b/uriloader/exthandler/mac/nsMIMEInfoMac.h
new file mode 100644
index 0000000000..298357f757
--- /dev/null
+++ b/uriloader/exthandler/mac/nsMIMEInfoMac.h
@@ -0,0 +1,34 @@
+/* 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 nsMIMEInfoMac_h_
+#define nsMIMEInfoMac_h_
+
+#include "nsMIMEInfoImpl.h"
+
+class nsMIMEInfoMac : public nsMIMEInfoImpl {
+ public:
+ explicit nsMIMEInfoMac(const char* aMIMEType = "") : nsMIMEInfoImpl(aMIMEType) {}
+ explicit nsMIMEInfoMac(const nsACString& aMIMEType) : nsMIMEInfoImpl(aMIMEType) {}
+ nsMIMEInfoMac(const nsACString& aType, HandlerClass aClass) :
+ nsMIMEInfoImpl(aType, aClass) {}
+
+ NS_IMETHOD LaunchWithFile(nsIFile* aFile);
+ protected:
+ virtual MOZ_MUST_USE nsresult LoadUriInternal(nsIURI *aURI);
+#ifdef DEBUG
+ virtual MOZ_MUST_USE nsresult LaunchDefaultWithFile(nsIFile* aFile) {
+ NS_NOTREACHED("do not call this method, use LaunchWithFile");
+ return NS_ERROR_UNEXPECTED;
+ }
+#endif
+ static MOZ_MUST_USE nsresult OpenApplicationWithURI(nsIFile *aApplication,
+ const nsCString& aURI);
+
+ NS_IMETHOD GetDefaultDescription(nsAString& aDefaultDescription);
+
+};
+
+
+#endif
diff --git a/uriloader/exthandler/mac/nsMIMEInfoMac.mm b/uriloader/exthandler/mac/nsMIMEInfoMac.mm
new file mode 100644
index 0000000000..64d3a82dea
--- /dev/null
+++ b/uriloader/exthandler/mac/nsMIMEInfoMac.mm
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 3; 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 <ApplicationServices/ApplicationServices.h>
+
+#include "nsObjCExceptions.h"
+#include "nsMIMEInfoMac.h"
+#include "nsILocalFileMac.h"
+#include "nsIFileURL.h"
+
+// We override this to make sure app bundles display their pretty name (without .app suffix)
+NS_IMETHODIMP nsMIMEInfoMac::GetDefaultDescription(nsAString& aDefaultDescription)
+{
+ if (mDefaultApplication) {
+ nsCOMPtr<nsILocalFileMac> macFile = do_QueryInterface(mDefaultApplication);
+ if (macFile) {
+ bool isPackage;
+ (void)macFile->IsPackage(&isPackage);
+ if (isPackage)
+ return macFile->GetBundleDisplayName(aDefaultDescription);
+ }
+ }
+
+ return nsMIMEInfoImpl::GetDefaultDescription(aDefaultDescription);
+}
+
+NS_IMETHODIMP
+nsMIMEInfoMac::LaunchWithFile(nsIFile *aFile)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ nsCOMPtr<nsIFile> application;
+ nsresult rv;
+
+ NS_ASSERTION(mClass == eMIMEInfo, "only MIME infos are currently allowed"
+ "to pass content by value");
+
+ if (mPreferredAction == useHelperApp) {
+
+ // we don't yet support passing content by value (rather than reference)
+ // to web apps. at some point, we will probably want to.
+ nsCOMPtr<nsILocalHandlerApp> localHandlerApp =
+ do_QueryInterface(mPreferredApplication, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = localHandlerApp->GetExecutable(getter_AddRefs(application));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ } else if (mPreferredAction == useSystemDefault) {
+ application = mDefaultApplication;
+ }
+ else
+ return NS_ERROR_INVALID_ARG;
+
+ // if we've already got an app, just QI so we have the launchWithDoc method
+ nsCOMPtr<nsILocalFileMac> app;
+ if (application) {
+ app = do_QueryInterface(application, &rv);
+ if (NS_FAILED(rv)) return rv;
+ } else {
+ // otherwise ask LaunchServices for an app directly
+ nsCOMPtr<nsILocalFileMac> tempFile = do_QueryInterface(aFile, &rv);
+ if (NS_FAILED(rv)) return rv;
+
+ FSRef tempFileRef;
+ tempFile->GetFSRef(&tempFileRef);
+
+ FSRef appFSRef;
+ if (::LSGetApplicationForItem(&tempFileRef, kLSRolesAll, &appFSRef, nullptr) == noErr)
+ {
+ app = (do_CreateInstance("@mozilla.org/file/local;1"));
+ if (!app) return NS_ERROR_FAILURE;
+ app->InitWithFSRef(&appFSRef);
+ } else {
+ return NS_ERROR_FAILURE;
+ }
+ }
+ return app->LaunchWithDoc(aFile, false);
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult
+nsMIMEInfoMac::LoadUriInternal(nsIURI *aURI)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ NS_ENSURE_ARG_POINTER(aURI);
+
+ nsresult rv = NS_ERROR_FAILURE;
+
+ nsAutoCString uri;
+ aURI->GetSpec(uri);
+ if (!uri.IsEmpty()) {
+ CFURLRef myURLRef = ::CFURLCreateWithBytes(kCFAllocatorDefault,
+ (const UInt8*)uri.get(),
+ strlen(uri.get()),
+ kCFStringEncodingUTF8,
+ NULL);
+ if (myURLRef) {
+ OSStatus status = ::LSOpenCFURLRef(myURLRef, NULL);
+ if (status == noErr)
+ rv = NS_OK;
+ ::CFRelease(myURLRef);
+ }
+ }
+
+ return rv;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
diff --git a/uriloader/exthandler/mac/nsOSHelperAppService.h b/uriloader/exthandler/mac/nsOSHelperAppService.h
new file mode 100644
index 0000000000..7371e1f42d
--- /dev/null
+++ b/uriloader/exthandler/mac/nsOSHelperAppService.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C++; tab-width: 3; 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 nsOSHelperAppService_h__
+#define nsOSHelperAppService_h__
+
+// The OS helper app service is a subclass of nsExternalHelperAppService and is implemented on each
+// platform. It contains platform specific code for finding helper applications for a given mime type
+// in addition to launching those applications. This is the Mac version.
+
+#include "nsExternalHelperAppService.h"
+#include "nsCExternalHandlerService.h"
+#include "nsMIMEInfoImpl.h"
+#include "nsCOMPtr.h"
+
+class nsOSHelperAppService : public nsExternalHelperAppService
+{
+public:
+ nsOSHelperAppService();
+ virtual ~nsOSHelperAppService();
+
+ // override nsIExternalProtocolService methods
+ NS_IMETHOD GetApplicationDescription(const nsACString& aScheme, nsAString& _retval);
+
+ // method overrides --> used to hook the mime service into internet config....
+ NS_IMETHOD GetFromTypeAndExtension(const nsACString& aType, const nsACString& aFileExt, nsIMIMEInfo ** aMIMEInfo);
+ already_AddRefed<nsIMIMEInfo> GetMIMEInfoFromOS(const nsACString& aMIMEType, const nsACString& aFileExt, bool * aFound);
+ NS_IMETHOD GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
+ bool *found,
+ nsIHandlerInfo **_retval);
+
+ // GetFileTokenForPath must be implemented by each platform.
+ // platformAppPath --> a platform specific path to an application that we got out of the
+ // rdf data source. This can be a mac file spec, a unix path or a windows path depending on the platform
+ // aFile --> an nsIFile representation of that platform application path.
+ virtual MOZ_MUST_USE nsresult GetFileTokenForPath(const char16_t * platformAppPath, nsIFile ** aFile);
+
+ MOZ_MUST_USE nsresult OSProtocolHandlerExists(const char * aScheme,
+ bool * aHandlerExists);
+
+private:
+ uint32_t mPermissions;
+};
+
+#endif // nsOSHelperAppService_h__
diff --git a/uriloader/exthandler/mac/nsOSHelperAppService.mm b/uriloader/exthandler/mac/nsOSHelperAppService.mm
new file mode 100644
index 0000000000..00f12f5448
--- /dev/null
+++ b/uriloader/exthandler/mac/nsOSHelperAppService.mm
@@ -0,0 +1,569 @@
+/* -*- Mode: C++; tab-width: 3; 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 <sys/types.h>
+#include <sys/stat.h>
+#include "nsOSHelperAppService.h"
+#include "nsObjCExceptions.h"
+#include "nsISupports.h"
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsXPIDLString.h"
+#include "nsIURL.h"
+#include "nsIFile.h"
+#include "nsILocalFileMac.h"
+#include "nsMimeTypes.h"
+#include "nsIStringBundle.h"
+#include "nsIPromptService.h"
+#include "nsMemory.h"
+#include "nsCRT.h"
+#include "nsMIMEInfoMac.h"
+#include "nsEmbedCID.h"
+
+#import <CoreFoundation/CoreFoundation.h>
+#import <ApplicationServices/ApplicationServices.h>
+
+// chrome URL's
+#define HELPERAPPLAUNCHER_BUNDLE_URL "chrome://global/locale/helperAppLauncher.properties"
+#define BRAND_BUNDLE_URL "chrome://branding/locale/brand.properties"
+
+using mozilla::LogLevel;
+
+/* This is an undocumented interface (in the Foundation framework) that has
+ * been stable since at least 10.2.8 and is still present on SnowLeopard.
+ * Furthermore WebKit has three public methods (in WebKitSystemInterface.h)
+ * that are thin wrappers around this interface's last three methods. So
+ * it's unlikely to change anytime soon. Now that we're no longer using
+ * Internet Config Services, this is the only way to look up a MIME type
+ * from an extension, or vice versa.
+ */
+@class NSURLFileTypeMappingsInternal;
+
+@interface NSURLFileTypeMappings : NSObject
+{
+ NSURLFileTypeMappingsInternal *_internal;
+}
+
++ (NSURLFileTypeMappings*)sharedMappings;
+- (NSString*)MIMETypeForExtension:(NSString*)aString;
+- (NSString*)preferredExtensionForMIMEType:(NSString*)aString;
+- (NSArray*)extensionsForMIMEType:(NSString*)aString;
+@end
+
+nsOSHelperAppService::nsOSHelperAppService() : nsExternalHelperAppService()
+{
+ mode_t mask = umask(0777);
+ umask(mask);
+ mPermissions = 0666 & ~mask;
+}
+
+nsOSHelperAppService::~nsOSHelperAppService()
+{}
+
+nsresult nsOSHelperAppService::OSProtocolHandlerExists(const char * aProtocolScheme, bool * aHandlerExists)
+{
+ // CFStringCreateWithBytes() can fail even if we're not out of memory --
+ // for example if the 'bytes' parameter is something very wierd (like "ÿÿ~"
+ // aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
+ // specified in the 'encoding' parameter. See bug 548719.
+ CFStringRef schemeString = ::CFStringCreateWithBytes(kCFAllocatorDefault,
+ (const UInt8*)aProtocolScheme,
+ strlen(aProtocolScheme),
+ kCFStringEncodingUTF8,
+ false);
+ if (schemeString) {
+ // LSCopyDefaultHandlerForURLScheme() can fail to find the default handler
+ // for aProtocolScheme when it's never been explicitly set (using
+ // LSSetDefaultHandlerForURLScheme()). For example, Safari is the default
+ // handler for the "http" scheme on a newly installed copy of OS X. But
+ // this (presumably) wasn't done using LSSetDefaultHandlerForURLScheme(),
+ // so LSCopyDefaultHandlerForURLScheme() will fail to find Safari. To get
+ // around this we use LSCopyAllHandlersForURLScheme() instead -- which seems
+ // never to fail.
+ // http://lists.apple.com/archives/Carbon-dev/2007/May/msg00349.html
+ // http://www.realsoftware.com/listarchives/realbasic-nug/2008-02/msg00119.html
+ CFArrayRef handlerArray = ::LSCopyAllHandlersForURLScheme(schemeString);
+ *aHandlerExists = !!handlerArray;
+ if (handlerArray)
+ ::CFRelease(handlerArray);
+ ::CFRelease(schemeString);
+ } else {
+ *aHandlerExists = false;
+ }
+ return NS_OK;
+}
+
+NS_IMETHODIMP nsOSHelperAppService::GetApplicationDescription(const nsACString& aScheme, nsAString& _retval)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ nsresult rv = NS_ERROR_NOT_AVAILABLE;
+
+ CFStringRef schemeCFString =
+ ::CFStringCreateWithBytes(kCFAllocatorDefault,
+ (const UInt8 *)PromiseFlatCString(aScheme).get(),
+ aScheme.Length(),
+ kCFStringEncodingUTF8,
+ false);
+
+ if (schemeCFString) {
+ CFStringRef lookupCFString = ::CFStringCreateWithFormat(NULL, NULL, CFSTR("%@:"), schemeCFString);
+
+ if (lookupCFString) {
+ CFURLRef lookupCFURL = ::CFURLCreateWithString(NULL, lookupCFString, NULL);
+
+ if (lookupCFURL) {
+ CFURLRef appCFURL = NULL;
+ OSStatus theErr = ::LSGetApplicationForURL(lookupCFURL, kLSRolesAll, NULL, &appCFURL);
+
+ if (theErr == noErr) {
+ CFBundleRef handlerBundle = ::CFBundleCreate(NULL, appCFURL);
+
+ if (handlerBundle) {
+ // Get the human-readable name of the default handler bundle
+ CFStringRef bundleName =
+ (CFStringRef)::CFBundleGetValueForInfoDictionaryKey(handlerBundle,
+ kCFBundleNameKey);
+
+ if (bundleName) {
+ AutoTArray<UniChar, 255> buffer;
+ CFIndex bundleNameLength = ::CFStringGetLength(bundleName);
+ buffer.SetLength(bundleNameLength);
+ ::CFStringGetCharacters(bundleName, CFRangeMake(0, bundleNameLength),
+ buffer.Elements());
+ _retval.Assign(reinterpret_cast<char16_t*>(buffer.Elements()), bundleNameLength);
+ rv = NS_OK;
+ }
+
+ ::CFRelease(handlerBundle);
+ }
+
+ ::CFRelease(appCFURL);
+ }
+
+ ::CFRelease(lookupCFURL);
+ }
+
+ ::CFRelease(lookupCFString);
+ }
+
+ ::CFRelease(schemeCFString);
+ }
+
+ return rv;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+nsresult nsOSHelperAppService::GetFileTokenForPath(const char16_t * aPlatformAppPath, nsIFile ** aFile)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ nsresult rv;
+ nsCOMPtr<nsILocalFileMac> localFile (do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv));
+ NS_ENSURE_SUCCESS(rv,rv);
+
+ CFURLRef pathAsCFURL;
+ CFStringRef pathAsCFString = ::CFStringCreateWithCharacters(NULL,
+ reinterpret_cast<const UniChar*>(aPlatformAppPath),
+ NS_strlen(aPlatformAppPath));
+ if (!pathAsCFString)
+ return NS_ERROR_OUT_OF_MEMORY;
+
+ if (::CFStringGetCharacterAtIndex(pathAsCFString, 0) == '/') {
+ // we have a Posix path
+ pathAsCFURL = ::CFURLCreateWithFileSystemPath(nullptr, pathAsCFString,
+ kCFURLPOSIXPathStyle, false);
+ if (!pathAsCFURL) {
+ ::CFRelease(pathAsCFString);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ else {
+ // if it doesn't start with a / it's not an absolute Posix path
+ // let's check if it's a HFS path left over from old preferences
+
+ // If it starts with a ':' char, it's not an absolute HFS path
+ // so bail for that, and also if it's empty
+ if (::CFStringGetLength(pathAsCFString) == 0 ||
+ ::CFStringGetCharacterAtIndex(pathAsCFString, 0) == ':')
+ {
+ ::CFRelease(pathAsCFString);
+ return NS_ERROR_FILE_UNRECOGNIZED_PATH;
+ }
+
+ pathAsCFURL = ::CFURLCreateWithFileSystemPath(nullptr, pathAsCFString,
+ kCFURLHFSPathStyle, false);
+ if (!pathAsCFURL) {
+ ::CFRelease(pathAsCFString);
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+
+ rv = localFile->InitWithCFURL(pathAsCFURL);
+ ::CFRelease(pathAsCFString);
+ ::CFRelease(pathAsCFURL);
+ if (NS_FAILED(rv))
+ return rv;
+ *aFile = localFile;
+ NS_IF_ADDREF(*aFile);
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP nsOSHelperAppService::GetFromTypeAndExtension(const nsACString& aType, const nsACString& aFileExt, nsIMIMEInfo ** aMIMEInfo)
+{
+ return nsExternalHelperAppService::GetFromTypeAndExtension(aType, aFileExt, aMIMEInfo);
+}
+
+// Returns the MIME types an application bundle explicitly claims to handle.
+// Returns NULL if aAppRef doesn't explicitly claim to handle any MIME types.
+// If the return value is non-NULL, the caller is responsible for freeing it.
+// This isn't necessarily the same as the MIME types the application bundle
+// is registered to handle in the Launch Services database. (For example
+// the Preview application is normally registered to handle the application/pdf
+// MIME type, even though it doesn't explicitly claim to handle *any* MIME
+// types in its Info.plist. This is probably because Preview does explicitly
+// claim to handle the com.adobe.pdf UTI, and Launch Services somehow
+// translates this into a claim to support the application/pdf MIME type.
+// Launch Services doesn't provide any APIs (documented or undocumented) to
+// query which MIME types a given application is registered to handle. So any
+// app that wants this information (e.g. the Default Apps pref pane) needs to
+// iterate through the entire Launch Services database -- a process which can
+// take several seconds.)
+static CFArrayRef GetMIMETypesHandledByApp(FSRef *aAppRef)
+{
+ CFURLRef appURL = ::CFURLCreateFromFSRef(kCFAllocatorDefault, aAppRef);
+ if (!appURL) {
+ return NULL;
+ }
+ CFDictionaryRef infoDict = ::CFBundleCopyInfoDictionaryForURL(appURL);
+ ::CFRelease(appURL);
+ if (!infoDict) {
+ return NULL;
+ }
+ CFTypeRef cfObject = ::CFDictionaryGetValue(infoDict, CFSTR("CFBundleDocumentTypes"));
+ if (!cfObject || (::CFGetTypeID(cfObject) != ::CFArrayGetTypeID())) {
+ ::CFRelease(infoDict);
+ return NULL;
+ }
+
+ CFArrayRef docTypes = static_cast<CFArrayRef>(cfObject);
+ CFIndex docTypesCount = ::CFArrayGetCount(docTypes);
+ if (docTypesCount == 0) {
+ ::CFRelease(infoDict);
+ return NULL;
+ }
+
+ CFMutableArrayRef mimeTypes =
+ ::CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
+ for (CFIndex i = 0; i < docTypesCount; ++i) {
+ cfObject = ::CFArrayGetValueAtIndex(docTypes, i);
+ if (!cfObject || (::CFGetTypeID(cfObject) != ::CFDictionaryGetTypeID())) {
+ continue;
+ }
+ CFDictionaryRef typeDict = static_cast<CFDictionaryRef>(cfObject);
+
+ // When this key is present (on OS X 10.5 and later), its contents
+ // take precedence over CFBundleTypeMIMETypes (and CFBundleTypeExtensions
+ // and CFBundleTypeOSTypes).
+ cfObject = ::CFDictionaryGetValue(typeDict, CFSTR("LSItemContentTypes"));
+ if (cfObject && (::CFGetTypeID(cfObject) == ::CFArrayGetTypeID())) {
+ continue;
+ }
+
+ cfObject = ::CFDictionaryGetValue(typeDict, CFSTR("CFBundleTypeMIMETypes"));
+ if (!cfObject || (::CFGetTypeID(cfObject) != ::CFArrayGetTypeID())) {
+ continue;
+ }
+ CFArrayRef mimeTypeHolder = static_cast<CFArrayRef>(cfObject);
+ CFArrayAppendArray(mimeTypes, mimeTypeHolder,
+ ::CFRangeMake(0, ::CFArrayGetCount(mimeTypeHolder)));
+ }
+
+ ::CFRelease(infoDict);
+ if (!::CFArrayGetCount(mimeTypes)) {
+ ::CFRelease(mimeTypes);
+ mimeTypes = NULL;
+ }
+ return mimeTypes;
+}
+
+// aMIMEType and aFileExt might not match, If they don't we set *aFound to
+// false and return a minimal nsIMIMEInfo structure.
+already_AddRefed<nsIMIMEInfo>
+nsOSHelperAppService::GetMIMEInfoFromOS(const nsACString& aMIMEType,
+ const nsACString& aFileExt,
+ bool * aFound)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSNULL;
+
+ *aFound = false;
+
+ const nsCString& flatType = PromiseFlatCString(aMIMEType);
+ const nsCString& flatExt = PromiseFlatCString(aFileExt);
+
+ MOZ_LOG(mLog, LogLevel::Debug, ("Mac: HelperAppService lookup for type '%s' ext '%s'\n",
+ flatType.get(), flatExt.get()));
+
+ // Create a Mac-specific MIME info so we can use Mac-specific members.
+ RefPtr<nsMIMEInfoMac> mimeInfoMac = new nsMIMEInfoMac(aMIMEType);
+
+ NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
+
+ OSStatus err;
+ bool haveAppForType = false;
+ bool haveAppForExt = false;
+ bool typeAppIsDefault = false;
+ bool extAppIsDefault = false;
+ FSRef typeAppFSRef;
+ FSRef extAppFSRef;
+
+ CFStringRef cfMIMEType = NULL;
+
+ if (!aMIMEType.IsEmpty()) {
+ CFURLRef appURL = NULL;
+ // CFStringCreateWithCString() can fail even if we're not out of memory --
+ // for example if the 'cStr' parameter is something very weird (like "ÿÿ~"
+ // aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
+ // specified in the 'encoding' parameter. See bug 548719.
+ cfMIMEType = ::CFStringCreateWithCString(NULL, flatType.get(),
+ kCFStringEncodingUTF8);
+ if (cfMIMEType) {
+ err = ::LSCopyApplicationForMIMEType(cfMIMEType, kLSRolesAll, &appURL);
+ if ((err == noErr) && appURL && ::CFURLGetFSRef(appURL, &typeAppFSRef)) {
+ haveAppForType = true;
+ MOZ_LOG(mLog, LogLevel::Debug, ("LSCopyApplicationForMIMEType found a default application\n"));
+ }
+ if (appURL) {
+ ::CFRelease(appURL);
+ }
+ }
+ }
+ if (!aFileExt.IsEmpty()) {
+ // CFStringCreateWithCString() can fail even if we're not out of memory --
+ // for example if the 'cStr' parameter is something very wierd (like "ÿÿ~"
+ // aka "\xFF\xFF~"), or possibly if it can't be interpreted as using what's
+ // specified in the 'encoding' parameter. See bug 548719.
+ CFStringRef cfExt = ::CFStringCreateWithCString(NULL, flatExt.get(), kCFStringEncodingUTF8);
+ if (cfExt) {
+ err = ::LSGetApplicationForInfo(kLSUnknownType, kLSUnknownCreator, cfExt,
+ kLSRolesAll, &extAppFSRef, nullptr);
+ if (err == noErr) {
+ haveAppForExt = true;
+ MOZ_LOG(mLog, LogLevel::Debug, ("LSGetApplicationForInfo found a default application\n"));
+ }
+ ::CFRelease(cfExt);
+ }
+ }
+
+ if (haveAppForType && haveAppForExt) {
+ // Do aMIMEType and aFileExt match?
+ if (::FSCompareFSRefs((const FSRef *) &typeAppFSRef, (const FSRef *) &extAppFSRef) == noErr) {
+ typeAppIsDefault = true;
+ *aFound = true;
+ }
+ } else if (haveAppForType) {
+ // If aFileExt isn't empty, it doesn't match aMIMEType.
+ if (aFileExt.IsEmpty()) {
+ typeAppIsDefault = true;
+ *aFound = true;
+ }
+ } else if (haveAppForExt) {
+ // If aMIMEType isn't empty, it doesn't match aFileExt, which should mean
+ // that we haven't found a matching app. But make an exception for an app
+ // that also explicitly claims to handle aMIMEType, or which doesn't claim
+ // to handle any MIME types. This helps work around the following Apple
+ // design flaw:
+ //
+ // Launch Services is somewhat unreliable about registering Apple apps to
+ // handle MIME types. Probably this is because Apple has officially
+ // deprecated support for MIME types (in favor of UTIs). As a result,
+ // most of Apple's own apps don't explicitly claim to handle any MIME
+ // types (instead they claim to handle one or more UTIs). So Launch
+ // Services must contain logic to translate support for a given UTI into
+ // support for one or more MIME types, and it doesn't always do this
+ // correctly. For example DiskImageMounter isn't (by default) registered
+ // to handle the application/x-apple-diskimage MIME type. See bug 675356.
+ //
+ // Apple has also deprecated support for file extensions, and Apple apps
+ // also don't register to handle them. But for some reason Launch Services
+ // is (apparently) better about translating support for a given UTI into
+ // support for one or more file extensions. It's not at all clear why.
+ if (aMIMEType.IsEmpty()) {
+ extAppIsDefault = true;
+ *aFound = true;
+ } else {
+ CFArrayRef extAppMIMETypes = GetMIMETypesHandledByApp(&extAppFSRef);
+ if (extAppMIMETypes) {
+ if (cfMIMEType) {
+ if (::CFArrayContainsValue(extAppMIMETypes,
+ ::CFRangeMake(0, ::CFArrayGetCount(extAppMIMETypes)),
+ cfMIMEType)) {
+ extAppIsDefault = true;
+ *aFound = true;
+ }
+ }
+ ::CFRelease(extAppMIMETypes);
+ } else {
+ extAppIsDefault = true;
+ *aFound = true;
+ }
+ }
+ }
+
+ if (cfMIMEType) {
+ ::CFRelease(cfMIMEType);
+ }
+
+ if (aMIMEType.IsEmpty()) {
+ if (haveAppForExt) {
+ // If aMIMEType is empty and we've found a default app for aFileExt, try
+ // to get the MIME type from aFileExt. (It might also be worth doing
+ // this when aMIMEType isn't empty but haveAppForType is false -- but
+ // the doc for this method says that if we have a MIME type (in
+ // aMIMEType), we need to give it preference.)
+ NSURLFileTypeMappings *map = [NSURLFileTypeMappings sharedMappings];
+ NSString *extStr = [NSString stringWithCString:flatExt.get() encoding:NSASCIIStringEncoding];
+ NSString *typeStr = map ? [map MIMETypeForExtension:extStr] : NULL;
+ if (typeStr) {
+ nsAutoCString mimeType;
+ mimeType.Assign((char *)[typeStr cStringUsingEncoding:NSASCIIStringEncoding]);
+ mimeInfoMac->SetMIMEType(mimeType);
+ haveAppForType = true;
+ } else {
+ // Sometimes the OS won't give us a MIME type for an extension that's
+ // registered with Launch Services and has a default app: For example
+ // Real Player registers itself for the "ogg" extension and for the
+ // audio/x-ogg and application/x-ogg MIME types, but
+ // MIMETypeForExtension returns nil for the "ogg" extension even on
+ // systems where Real Player is installed. This is probably an Apple
+ // bug. But bad things happen if we return an nsIMIMEInfo structure
+ // with an empty MIME type and set *aFound to true. So in this
+ // case we need to set it to false here.
+ haveAppForExt = false;
+ extAppIsDefault = false;
+ *aFound = false;
+ }
+ } else {
+ // Otherwise set the MIME type to a reasonable fallback.
+ mimeInfoMac->SetMIMEType(NS_LITERAL_CSTRING(APPLICATION_OCTET_STREAM));
+ }
+ }
+
+ if (typeAppIsDefault || extAppIsDefault) {
+ if (haveAppForExt)
+ mimeInfoMac->AppendExtension(aFileExt);
+
+ nsCOMPtr<nsILocalFileMac> app(do_CreateInstance(NS_LOCAL_FILE_CONTRACTID));
+ if (!app) {
+ [localPool release];
+ return nullptr;
+ }
+
+ CFStringRef cfAppName = NULL;
+ if (typeAppIsDefault) {
+ app->InitWithFSRef(&typeAppFSRef);
+ ::LSCopyItemAttribute((const FSRef *) &typeAppFSRef, kLSRolesAll,
+ kLSItemDisplayName, (CFTypeRef *) &cfAppName);
+ } else {
+ app->InitWithFSRef(&extAppFSRef);
+ ::LSCopyItemAttribute((const FSRef *) &extAppFSRef, kLSRolesAll,
+ kLSItemDisplayName, (CFTypeRef *) &cfAppName);
+ }
+ if (cfAppName) {
+ AutoTArray<UniChar, 255> buffer;
+ CFIndex appNameLength = ::CFStringGetLength(cfAppName);
+ buffer.SetLength(appNameLength);
+ ::CFStringGetCharacters(cfAppName, CFRangeMake(0, appNameLength),
+ buffer.Elements());
+ nsAutoString appName;
+ appName.Assign(reinterpret_cast<char16_t*>(buffer.Elements()), appNameLength);
+ mimeInfoMac->SetDefaultDescription(appName);
+ ::CFRelease(cfAppName);
+ }
+
+ mimeInfoMac->SetDefaultApplication(app);
+ mimeInfoMac->SetPreferredAction(nsIMIMEInfo::useSystemDefault);
+ } else {
+ mimeInfoMac->SetPreferredAction(nsIMIMEInfo::saveToDisk);
+ }
+
+ nsAutoCString mimeType;
+ mimeInfoMac->GetMIMEType(mimeType);
+ if (*aFound && !mimeType.IsEmpty()) {
+ // If we have a MIME type, make sure its preferred extension is included
+ // in our list.
+ NSURLFileTypeMappings *map = [NSURLFileTypeMappings sharedMappings];
+ NSString *typeStr = [NSString stringWithCString:mimeType.get() encoding:NSASCIIStringEncoding];
+ NSString *extStr = map ? [map preferredExtensionForMIMEType:typeStr] : NULL;
+ if (extStr) {
+ nsAutoCString preferredExt;
+ preferredExt.Assign((char *)[extStr cStringUsingEncoding:NSASCIIStringEncoding]);
+ mimeInfoMac->AppendExtension(preferredExt);
+ }
+
+ CFStringRef cfType = ::CFStringCreateWithCString(NULL, mimeType.get(), kCFStringEncodingUTF8);
+ if (cfType) {
+ CFStringRef cfTypeDesc = NULL;
+ if (::LSCopyKindStringForMIMEType(cfType, &cfTypeDesc) == noErr) {
+ AutoTArray<UniChar, 255> buffer;
+ CFIndex typeDescLength = ::CFStringGetLength(cfTypeDesc);
+ buffer.SetLength(typeDescLength);
+ ::CFStringGetCharacters(cfTypeDesc, CFRangeMake(0, typeDescLength),
+ buffer.Elements());
+ nsAutoString typeDesc;
+ typeDesc.Assign(reinterpret_cast<char16_t*>(buffer.Elements()), typeDescLength);
+ mimeInfoMac->SetDescription(typeDesc);
+ }
+ if (cfTypeDesc) {
+ ::CFRelease(cfTypeDesc);
+ }
+ ::CFRelease(cfType);
+ }
+ }
+
+ MOZ_LOG(mLog, LogLevel::Debug, ("OS gave us: type '%s' found '%i'\n", mimeType.get(), *aFound));
+
+ [localPool release];
+ return mimeInfoMac.forget();
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSNULL;
+}
+
+NS_IMETHODIMP
+nsOSHelperAppService::GetProtocolHandlerInfoFromOS(const nsACString &aScheme,
+ bool *found,
+ nsIHandlerInfo **_retval)
+{
+ NS_ASSERTION(!aScheme.IsEmpty(), "No scheme was specified!");
+
+ nsresult rv = OSProtocolHandlerExists(nsPromiseFlatCString(aScheme).get(),
+ found);
+ if (NS_FAILED(rv))
+ return rv;
+
+ nsMIMEInfoMac *handlerInfo =
+ new nsMIMEInfoMac(aScheme, nsMIMEInfoBase::eProtocolInfo);
+ NS_ENSURE_TRUE(handlerInfo, NS_ERROR_OUT_OF_MEMORY);
+ NS_ADDREF(*_retval = handlerInfo);
+
+ if (!*found) {
+ // Code that calls this requires an object regardless if the OS has
+ // something for us, so we return the empty object.
+ return NS_OK;
+ }
+
+ nsAutoString desc;
+ rv = GetApplicationDescription(aScheme, desc);
+ NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "GetApplicationDescription failed");
+ handlerInfo->SetDefaultDescription(desc);
+
+ return NS_OK;
+}
+
diff --git a/uriloader/exthandler/moz.build b/uriloader/exthandler/moz.build
index f0bafd945b..127e4da2bd 100644
--- a/uriloader/exthandler/moz.build
+++ b/uriloader/exthandler/moz.build
@@ -21,6 +21,8 @@ XPIDL_MODULE = 'exthandler'
if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
osdir = 'win'
LOCAL_INCLUDES += ['win']
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ osdir = 'mac'
elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
osdir = 'uikit'
else:
@@ -51,7 +53,13 @@ UNIFIED_SOURCES += [
'nsMIMEInfoImpl.cpp',
]
-if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ UNIFIED_SOURCES += [
+ 'mac/nsLocalHandlerAppMac.mm',
+ 'mac/nsMIMEInfoMac.mm',
+ 'mac/nsOSHelperAppService.mm',
+ ]
+elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'uikit':
UNIFIED_SOURCES += [
'uikit/nsLocalHandlerAppUIKit.mm',
'uikit/nsMIMEInfoUIKit.mm',
diff --git a/xpcom/build/moz.build b/xpcom/build/moz.build
index 6226067897..bc20b8aedd 100644
--- a/xpcom/build/moz.build
+++ b/xpcom/build/moz.build
@@ -88,3 +88,6 @@ if CONFIG['MOZ_VPX']:
LOCAL_INCLUDES += [
'/media/libvpx',
]
+
+if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'cocoa':
+ CXXFLAGS += CONFIG['TK_CFLAGS']