diff --git a/b2g/chrome/content/shell.js b/b2g/chrome/content/shell.js index 26a73e17309..10ac9276b1b 100644 --- a/b2g/chrome/content/shell.js +++ b/b2g/chrome/content/shell.js @@ -1063,6 +1063,7 @@ var CaptivePortalLoginHelper = { init: function init() { Services.obs.addObserver(this, 'captive-portal-login', false); Services.obs.addObserver(this, 'captive-portal-login-abort', false); + Services.obs.addObserver(this, 'captive-portal-login-success', false); }, handleEvent: function handleEvent(detail) { Services.captivePortalDetector.cancelLogin(detail.id); diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 47e6a69982d..9d80900ac61 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -19,13 +19,13 @@ - + - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index ecb55ffcf80..ba513c79811 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -17,10 +17,10 @@ - + - + diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml index aaaca03bc04..5e6c6c4d723 100644 --- a/b2g/config/emulator-kk/sources.xml +++ b/b2g/config/emulator-kk/sources.xml @@ -15,7 +15,7 @@ - + @@ -23,7 +23,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 47e6a69982d..9d80900ac61 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -19,13 +19,13 @@ - + - + diff --git a/b2g/config/flame/sources.xml b/b2g/config/flame/sources.xml index 47866ce231c..6a580b3ca94 100644 --- a/b2g/config/flame/sources.xml +++ b/b2g/config/flame/sources.xml @@ -17,10 +17,10 @@ - + - + @@ -118,7 +118,7 @@ - + diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 784f0ff6598..a738b422118 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -4,6 +4,6 @@ "remote": "", "branch": "" }, - "revision": "46fb0be835267316bda52a12dedab53978456833", + "revision": "01ae06e7d0c3c72d51e6801986339d6c06229c9b", "repo_path": "/integration/gaia-central" } diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 22476a46c8e..7e18dfe5e80 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -17,12 +17,12 @@ - + - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index c9011e222a5..1b13fc66046 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -15,7 +15,7 @@ - + diff --git a/b2g/config/nexus-4/sources.xml b/b2g/config/nexus-4/sources.xml index a48be446cf5..ba850f5505f 100644 --- a/b2g/config/nexus-4/sources.xml +++ b/b2g/config/nexus-4/sources.xml @@ -17,10 +17,10 @@ - + - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index e36744852f8..84e455f83e4 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -17,12 +17,12 @@ - + - + diff --git a/content/base/src/nsGkAtomList.h b/content/base/src/nsGkAtomList.h index c7ad63de0e1..ca15e67032f 100644 --- a/content/base/src/nsGkAtomList.h +++ b/content/base/src/nsGkAtomList.h @@ -2031,8 +2031,9 @@ GK_ATOM(x_symbol, "x-symbol") GK_ATOM(az, "az") GK_ATOM(ba, "ba") GK_ATOM(crh, "crh") -GK_ATOM(nl, "nl") GK_ATOM(el, "el") +GK_ATOM(ga_ie, "ga-ie") +GK_ATOM(nl, "nl") // Names for editor transactions GK_ATOM(TypingTxnName, "Typing") diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index 7b0a931719e..3ec6f6f07c1 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -181,9 +181,7 @@ nsDOMWindowUtils::GetLayerTransaction() NS_IMETHODIMP nsDOMWindowUtils::GetImageAnimationMode(uint16_t *aMode) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_ARG_POINTER(aMode); *aMode = 0; @@ -198,9 +196,7 @@ nsDOMWindowUtils::GetImageAnimationMode(uint16_t *aMode) NS_IMETHODIMP nsDOMWindowUtils::SetImageAnimationMode(uint16_t aMode) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsPresContext* presContext = GetPresContext(); if (presContext) { @@ -215,9 +211,7 @@ nsDOMWindowUtils::GetDocCharsetIsForced(bool *aIsForced) { *aIsForced = false; - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsIDocument* doc = GetDocument(); *aIsForced = doc && @@ -229,9 +223,7 @@ NS_IMETHODIMP nsDOMWindowUtils::GetDocumentMetadata(const nsAString& aName, nsAString& aValue) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsIDocument* doc = GetDocument(); if (doc) { @@ -247,9 +239,7 @@ nsDOMWindowUtils::GetDocumentMetadata(const nsAString& aName, NS_IMETHODIMP nsDOMWindowUtils::Redraw(uint32_t aCount, uint32_t *aDurationOut) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); if (aCount == 0) aCount = 1; @@ -328,9 +318,7 @@ nsDOMWindowUtils::SetDisplayPortForElement(float aXPx, float aYPx, nsIDOMElement* aElement, uint32_t aPriority) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsIPresShell* presShell = GetPresShell(); if (!presShell) { @@ -453,9 +441,7 @@ nsDOMWindowUtils::SetDisplayPortBaseForElement(int32_t aX, int32_t aHeight, nsIDOMElement* aElement) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsIPresShell* presShell = GetPresShell(); if (!presShell) { @@ -505,9 +491,7 @@ nsDOMWindowUtils::SetResolution(float aXResolution, float aYResolution) NS_IMETHODIMP nsDOMWindowUtils::GetResolution(float* aXResolution, float* aYResolution) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsIPresShell* presShell = GetPresShell(); if (!presShell) { @@ -529,9 +513,7 @@ nsDOMWindowUtils::GetResolution(float* aXResolution, float* aYResolution) NS_IMETHODIMP nsDOMWindowUtils::GetIsResolutionSet(bool* aIsResolutionSet) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsIPresShell* presShell = GetPresShell(); if (!presShell) { @@ -715,9 +697,7 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType, bool *aPreventDefault, bool aIsSynthesized) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsPoint offset; @@ -810,9 +790,7 @@ nsDOMWindowUtils::SendPointerEvent(const nsAString& aType, uint8_t aOptionalArgCount, bool* aPreventDefault) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsPoint offset; @@ -884,9 +862,7 @@ nsDOMWindowUtils::SendWheelEvent(float aX, int32_t aLineOrPageDeltaY, uint32_t aOptions) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsPoint offset; @@ -1022,9 +998,7 @@ nsDOMWindowUtils::SendTouchEventCommon(const nsAString& aType, bool aToWindow, bool* aPreventDefault) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsPoint offset; @@ -1100,9 +1074,7 @@ nsDOMWindowUtils::SendKeyEvent(const nsAString& aType, uint32_t aAdditionalFlags, bool* aDefaultActionTaken) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsCOMPtr widget = GetWidget(); @@ -1214,9 +1186,7 @@ nsDOMWindowUtils::SendNativeKeyEvent(int32_t aNativeKeyboardLayout, const nsAString& aCharacters, const nsAString& aUnmodifiedCharacters) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsCOMPtr widget = GetWidget(); @@ -1234,9 +1204,7 @@ nsDOMWindowUtils::SendNativeMouseEvent(int32_t aScreenX, int32_t aModifierFlags, nsIDOMElement* aElement) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsCOMPtr widget = GetWidgetForElement(aElement); @@ -1258,9 +1226,7 @@ nsDOMWindowUtils::SendNativeMouseScrollEvent(int32_t aScreenX, uint32_t aAdditionalFlags, nsIDOMElement* aElement) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsCOMPtr widget = GetWidgetForElement(aElement); @@ -1284,9 +1250,7 @@ nsDOMWindowUtils::SendNativeTouchPoint(uint32_t aPointerId, double aPressure, uint32_t aOrientation) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr widget = GetWidget(); if (!widget) { @@ -1308,9 +1272,7 @@ nsDOMWindowUtils::SendNativeTouchTap(int32_t aScreenX, int32_t aScreenY, bool aLongTap) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr widget = GetWidget(); if (!widget) { @@ -1322,9 +1284,7 @@ nsDOMWindowUtils::SendNativeTouchTap(int32_t aScreenX, NS_IMETHODIMP nsDOMWindowUtils::ClearNativeTouchSequence() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr widget = GetWidget(); if (!widget) { @@ -1336,9 +1296,7 @@ nsDOMWindowUtils::ClearNativeTouchSequence() NS_IMETHODIMP nsDOMWindowUtils::ActivateNativeMenuItemAt(const nsAString& indexString) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsCOMPtr widget = GetWidget(); @@ -1351,9 +1309,7 @@ nsDOMWindowUtils::ActivateNativeMenuItemAt(const nsAString& indexString) NS_IMETHODIMP nsDOMWindowUtils::ForceUpdateNativeMenuAt(const nsAString& indexString) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsCOMPtr widget = GetWidget(); @@ -1407,9 +1363,7 @@ nsDOMWindowUtils::GetWidgetForElement(nsIDOMElement* aElement) NS_IMETHODIMP nsDOMWindowUtils::Focus(nsIDOMElement* aElement) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); nsIFocusManager* fm = nsFocusManager::GetFocusManager(); @@ -1429,13 +1383,7 @@ nsDOMWindowUtils::GarbageCollect(nsICycleCollectorListener *aListener, { PROFILER_LABEL("nsDOMWindowUtils", "GarbageCollect", js::ProfileEntry::Category::GC); - - // Always permit this in debug builds. -#ifndef DEBUG - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } -#endif + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsJSContext::GarbageCollectNow(JS::gcreason::DOM_UTILS); nsJSContext::CycleCollectNow(aListener, aExtraForgetSkippableCalls); @@ -1447,12 +1395,7 @@ NS_IMETHODIMP nsDOMWindowUtils::CycleCollect(nsICycleCollectorListener *aListener, int32_t aExtraForgetSkippableCalls) { - // Always permit this in debug builds. -#ifndef DEBUG - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } -#endif + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsJSContext::CycleCollectNow(aListener, aExtraForgetSkippableCalls); return NS_OK; @@ -1461,9 +1404,7 @@ nsDOMWindowUtils::CycleCollect(nsICycleCollectorListener *aListener, NS_IMETHODIMP nsDOMWindowUtils::RunNextCollectorTimer() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsJSContext::RunNextCollectorTimer(); @@ -1479,9 +1420,7 @@ nsDOMWindowUtils::SendSimpleGestureEvent(const nsAString& aType, int32_t aModifiers, uint32_t aClickCount) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsPoint offset; @@ -1546,9 +1485,7 @@ nsDOMWindowUtils::ElementFromPoint(float aX, float aY, bool aFlushLayout, nsIDOMElement** aReturn) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr doc = GetDocument(); NS_ENSURE_STATE(doc); @@ -1568,9 +1505,7 @@ nsDOMWindowUtils::NodesFromRect(float aX, float aY, bool aFlushLayout, nsIDOMNodeList** aReturn) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr doc = GetDocument(); NS_ENSURE_STATE(doc); @@ -1583,9 +1518,7 @@ NS_IMETHODIMP nsDOMWindowUtils::GetTranslationNodes(nsIDOMNode* aRoot, nsITranslationNodeList** aRetVal) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_ARG_POINTER(aRetVal); nsCOMPtr root = do_QueryInterface(aRoot); @@ -1686,9 +1619,7 @@ nsDOMWindowUtils::CompareCanvases(nsIDOMHTMLCanvasElement *aCanvas1, uint32_t* aMaxDifference, uint32_t* retVal) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); if (aCanvas1 == nullptr || aCanvas2 == nullptr || @@ -1754,9 +1685,7 @@ nsDOMWindowUtils::CompareCanvases(nsIDOMHTMLCanvasElement *aCanvas1, NS_IMETHODIMP nsDOMWindowUtils::GetIsMozAfterPaintPending(bool *aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_ARG_POINTER(aResult); *aResult = false; @@ -1770,9 +1699,7 @@ nsDOMWindowUtils::GetIsMozAfterPaintPending(bool *aResult) NS_IMETHODIMP nsDOMWindowUtils::ClearMozAfterPaintEvents() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsPresContext* presContext = GetPresContext(); if (!presContext) @@ -1784,9 +1711,7 @@ nsDOMWindowUtils::ClearMozAfterPaintEvents() NS_IMETHODIMP nsDOMWindowUtils::DisableNonTestMouseEvents(bool aDisable) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); @@ -1801,9 +1726,7 @@ nsDOMWindowUtils::DisableNonTestMouseEvents(bool aDisable) NS_IMETHODIMP nsDOMWindowUtils::SuppressEventHandling(bool aSuppress) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr doc = GetDocument(); NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); @@ -1819,9 +1742,7 @@ nsDOMWindowUtils::SuppressEventHandling(bool aSuppress) static nsresult getScrollXYAppUnits(nsWeakPtr aWindow, bool aFlushLayout, nsPoint& aScrollPos) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(aWindow); nsCOMPtr doc = window ? window->GetExtantDoc() : nullptr; @@ -1869,9 +1790,7 @@ NS_IMETHODIMP nsDOMWindowUtils::GetScrollbarSize(bool aFlushLayout, int32_t* aWidth, int32_t* aHeight) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); *aWidth = 0; *aHeight = 0; @@ -1900,9 +1819,7 @@ NS_IMETHODIMP nsDOMWindowUtils::GetBoundsWithoutFlushing(nsIDOMElement *aElement, nsIDOMClientRect** aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); @@ -1928,9 +1845,7 @@ nsDOMWindowUtils::GetBoundsWithoutFlushing(nsIDOMElement *aElement, NS_IMETHODIMP nsDOMWindowUtils::GetRootBounds(nsIDOMClientRect** aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsIDocument* doc = GetDocument(); NS_ENSURE_STATE(doc); @@ -1961,9 +1876,7 @@ nsDOMWindowUtils::GetRootBounds(nsIDOMClientRect** aResult) NS_IMETHODIMP nsDOMWindowUtils::GetIMEIsOpen(bool *aState) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_ARG_POINTER(aState); @@ -1987,9 +1900,7 @@ nsDOMWindowUtils::GetIMEIsOpen(bool *aState) NS_IMETHODIMP nsDOMWindowUtils::GetIMEStatus(uint32_t *aState) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_ARG_POINTER(aState); @@ -2005,9 +1916,7 @@ nsDOMWindowUtils::GetIMEStatus(uint32_t *aState) NS_IMETHODIMP nsDOMWindowUtils::GetFocusedInputType(char** aType) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_ARG_POINTER(aType); @@ -2025,9 +1934,7 @@ NS_IMETHODIMP nsDOMWindowUtils::FindElementWithViewId(nsViewID aID, nsIDOMElement** aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsRefPtr content = nsLayoutUtils::FindContentFor(aID); return content ? CallQueryInterface(content, aResult) : NS_OK; @@ -2056,9 +1963,7 @@ nsDOMWindowUtils::GetFullZoom(float* aFullZoom) { *aFullZoom = 1.0f; - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsPresContext* presContext = GetPresContext(); if (!presContext) { @@ -2076,9 +1981,7 @@ nsDOMWindowUtils::DispatchDOMEventViaPresShell(nsIDOMNode* aTarget, bool aTrusted, bool* aRetVal) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_STATE(aEvent); aEvent->SetTrusted(aTrusted); @@ -2117,9 +2020,7 @@ nsDOMWindowUtils::SendCompositionEvent(const nsAString& aType, const nsAString& aData, const nsAString& aLocale) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsCOMPtr widget = GetWidget(); @@ -2160,9 +2061,7 @@ nsDOMWindowUtils::CreateCompositionStringSynthesizer( NS_ENSURE_ARG_POINTER(aResult); *aResult = nullptr; - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_NOT_AVAILABLE); @@ -2180,9 +2079,7 @@ nsDOMWindowUtils::SendQueryContentEvent(uint32_t aType, { *aResult = nullptr; - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); @@ -2282,9 +2179,7 @@ nsDOMWindowUtils::SendSelectionSetEvent(uint32_t aOffset, { *aResult = false; - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsCOMPtr widget = GetWidget(); @@ -2313,9 +2208,7 @@ NS_IMETHODIMP nsDOMWindowUtils::SendContentCommandEvent(const nsAString& aType, nsITransferable * aTransferable) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // get the widget to send the event to nsCOMPtr widget = GetWidget(); @@ -2353,9 +2246,7 @@ NS_IMETHODIMP nsDOMWindowUtils::GetClassName(JS::Handle aObject, JSContext* aCx, char** aName) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // Our argument must be a non-null object. if (aObject.isPrimitive()) { @@ -2374,9 +2265,7 @@ nsDOMWindowUtils::GetVisitedDependentComputedStyle( { aResult.Truncate(); - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); @@ -2396,9 +2285,7 @@ nsDOMWindowUtils::GetVisitedDependentComputedStyle( NS_IMETHODIMP nsDOMWindowUtils::EnterModalState() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); @@ -2410,9 +2297,7 @@ nsDOMWindowUtils::EnterModalState() NS_IMETHODIMP nsDOMWindowUtils::LeaveModalState() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); @@ -2424,9 +2309,7 @@ nsDOMWindowUtils::LeaveModalState() NS_IMETHODIMP nsDOMWindowUtils::IsInModalState(bool *retval) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); @@ -2440,9 +2323,7 @@ nsDOMWindowUtils::GetParent(JS::Handle aObject, JSContext* aCx, JS::MutableHandle aParent) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // First argument must be an object. if (aObject.isPrimitive()) { @@ -2465,9 +2346,7 @@ nsDOMWindowUtils::GetParent(JS::Handle aObject, NS_IMETHODIMP nsDOMWindowUtils::GetOuterWindowID(uint64_t *aWindowID) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); @@ -2480,9 +2359,7 @@ nsDOMWindowUtils::GetOuterWindowID(uint64_t *aWindowID) NS_IMETHODIMP nsDOMWindowUtils::GetCurrentInnerWindowID(uint64_t *aWindowID) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_NOT_AVAILABLE); @@ -2500,9 +2377,7 @@ nsDOMWindowUtils::GetCurrentInnerWindowID(uint64_t *aWindowID) NS_IMETHODIMP nsDOMWindowUtils::SuspendTimeouts() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); @@ -2515,9 +2390,7 @@ nsDOMWindowUtils::SuspendTimeouts() NS_IMETHODIMP nsDOMWindowUtils::ResumeTimeouts() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); @@ -2530,9 +2403,7 @@ nsDOMWindowUtils::ResumeTimeouts() NS_IMETHODIMP nsDOMWindowUtils::GetLayerManagerType(nsAString& aType) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr widget = GetWidget(); if (!widget) @@ -2550,9 +2421,7 @@ nsDOMWindowUtils::GetLayerManagerType(nsAString& aType) NS_IMETHODIMP nsDOMWindowUtils::GetLayerManagerRemote(bool* retval) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr widget = GetWidget(); if (!widget) @@ -2569,9 +2438,7 @@ nsDOMWindowUtils::GetLayerManagerRemote(bool* retval) NS_IMETHODIMP nsDOMWindowUtils::StartFrameTimeRecording(uint32_t *startIndex) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_ARG_POINTER(startIndex); @@ -2598,9 +2465,7 @@ nsDOMWindowUtils::StopFrameTimeRecording(uint32_t startIndex, uint32_t *frameCount, float **frameIntervals) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_ARG_POINTER(frameCount); NS_ENSURE_ARG_POINTER(frameIntervals); @@ -2630,9 +2495,7 @@ nsDOMWindowUtils::StopFrameTimeRecording(uint32_t startIndex, NS_IMETHODIMP nsDOMWindowUtils::BeginTabSwitch() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr widget = GetWidget(); if (!widget) @@ -2673,9 +2536,7 @@ ComputeAnimationValue(nsCSSProperty aProperty, NS_IMETHODIMP nsDOMWindowUtils::AdvanceTimeAndRefresh(int64_t aMilliseconds) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsRefreshDriver* driver = GetPresContext()->RefreshDriver(); driver->AdvanceTimeAndRefresh(aMilliseconds); @@ -2691,9 +2552,7 @@ nsDOMWindowUtils::AdvanceTimeAndRefresh(int64_t aMilliseconds) NS_IMETHODIMP nsDOMWindowUtils::RestoreNormalRefresh() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // Kick the compositor out of test mode before the refresh driver, so that // the refresh driver doesn't send an update that gets ignored by the @@ -2712,9 +2571,7 @@ nsDOMWindowUtils::RestoreNormalRefresh() NS_IMETHODIMP nsDOMWindowUtils::GetIsTestControllingRefreshes(bool *aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsPresContext* pc = GetPresContext(); *aResult = @@ -2779,9 +2636,7 @@ nsDOMWindowUtils::ComputeAnimationDistance(nsIDOMElement* aElement, const nsAString& aValue2, double* aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsresult rv; nsCOMPtr content = do_QueryInterface(aElement, &rv); @@ -2825,9 +2680,7 @@ nsDOMWindowUtils::RenderDocument(const nsRect& aRect, nscolor aBackgroundColor, gfxContext* aThebesContext) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr doc = GetDocument(); NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); @@ -2843,9 +2696,7 @@ nsDOMWindowUtils::RenderDocument(const nsRect& aRect, NS_IMETHODIMP nsDOMWindowUtils::GetCursorType(int16_t *aCursor) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_ARG_POINTER(aCursor); @@ -2878,9 +2729,7 @@ nsDOMWindowUtils::GetCursorType(int16_t *aCursor) NS_IMETHODIMP nsDOMWindowUtils::GetDisplayDPI(float *aDPI) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr widget = GetWidget(); if (!widget) @@ -2896,9 +2745,7 @@ NS_IMETHODIMP nsDOMWindowUtils::GetOuterWindowWithId(uint64_t aWindowID, nsIDOMWindow** aWindow) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // XXX This method is deprecated. See bug 865664. nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, @@ -2915,9 +2762,7 @@ nsDOMWindowUtils::GetOuterWindowWithId(uint64_t aWindowID, NS_IMETHODIMP nsDOMWindowUtils::GetContainerElement(nsIDOMElement** aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); @@ -2933,9 +2778,7 @@ NS_IMETHODIMP nsDOMWindowUtils::WrapDOMFile(nsIFile *aFile, nsIDOMFile **aDOMFile) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); if (!aFile) { return NS_ERROR_FAILURE; @@ -2980,9 +2823,7 @@ CheckLeafLayers(Layer* aLayer, const nsIntPoint& aOffset, nsIntRegion* aCoveredR NS_IMETHODIMP nsDOMWindowUtils::LeafLayersPartitionWindow(bool* aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); *aResult = true; #ifdef DEBUG @@ -3014,9 +2855,7 @@ nsDOMWindowUtils::LeafLayersPartitionWindow(bool* aResult) NS_IMETHODIMP nsDOMWindowUtils::GetMayHaveTouchEventListeners(bool* aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); @@ -3029,9 +2868,7 @@ nsDOMWindowUtils::GetMayHaveTouchEventListeners(bool* aResult) NS_IMETHODIMP nsDOMWindowUtils::CheckAndClearPaintedState(nsIDOMElement* aElement, bool* aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); if (!aElement) { return NS_ERROR_INVALID_ARG; @@ -3066,9 +2903,7 @@ nsDOMWindowUtils::CheckAndClearPaintedState(nsIDOMElement* aElement, bool* aResu NS_IMETHODIMP nsDOMWindowUtils::EnableDialogs() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); @@ -3117,9 +2952,7 @@ GetFileOrBlob(const nsAString& aName, JS::Handle aBlobParts, JS::Handle aParameters, JSContext* aCx, uint8_t aOptionalArgCount, nsISupports** aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsresult rv; @@ -3152,9 +2985,7 @@ nsDOMWindowUtils::GetFile(const nsAString& aName, JS::Handle aBlobPar JS::Handle aParameters, JSContext* aCx, uint8_t aOptionalArgCount, nsIDOMFile** aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr file; nsresult rv = GetFileOrBlob(aName, aBlobParts, aParameters, aCx, @@ -3172,9 +3003,7 @@ nsDOMWindowUtils::GetBlob(JS::Handle aBlobParts, JS::Handle aParameters, JSContext* aCx, uint8_t aOptionalArgCount, nsIDOMBlob** aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr blob; nsresult rv = GetFileOrBlob(NullString(), aBlobParts, aParameters, aCx, @@ -3191,9 +3020,7 @@ NS_IMETHODIMP nsDOMWindowUtils::GetFileId(JS::Handle aFile, JSContext* aCx, int64_t* aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); if (!aFile.isPrimitive()) { JSObject* obj = aFile.toObjectOrNull(); @@ -3225,9 +3052,7 @@ nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName, int64_t aId, int32_t* aSliceRefCnt, JSContext* aCx, bool* aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); @@ -3268,9 +3093,7 @@ nsDOMWindowUtils::GetFileReferences(const nsAString& aDatabaseName, int64_t aId, NS_IMETHODIMP nsDOMWindowUtils::IsIncrementalGCEnabled(JSContext* cx, bool* aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); *aResult = JS::IsIncrementalGCEnabled(JS_GetRuntime(cx)); return NS_OK; @@ -3279,9 +3102,7 @@ nsDOMWindowUtils::IsIncrementalGCEnabled(JSContext* cx, bool* aResult) NS_IMETHODIMP nsDOMWindowUtils::StartPCCountProfiling(JSContext* cx) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); js::StartPCCountProfiling(cx); return NS_OK; @@ -3290,9 +3111,7 @@ nsDOMWindowUtils::StartPCCountProfiling(JSContext* cx) NS_IMETHODIMP nsDOMWindowUtils::StopPCCountProfiling(JSContext* cx) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); js::StopPCCountProfiling(cx); return NS_OK; @@ -3301,9 +3120,7 @@ nsDOMWindowUtils::StopPCCountProfiling(JSContext* cx) NS_IMETHODIMP nsDOMWindowUtils::PurgePCCounts(JSContext* cx) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); js::PurgePCCounts(cx); return NS_OK; @@ -3312,9 +3129,7 @@ nsDOMWindowUtils::PurgePCCounts(JSContext* cx) NS_IMETHODIMP nsDOMWindowUtils::GetPCCountScriptCount(JSContext* cx, int32_t *result) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); *result = js::GetPCCountScriptCount(cx); return NS_OK; @@ -3323,9 +3138,7 @@ nsDOMWindowUtils::GetPCCountScriptCount(JSContext* cx, int32_t *result) NS_IMETHODIMP nsDOMWindowUtils::GetPCCountScriptSummary(int32_t script, JSContext* cx, nsAString& result) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); JSString *text = js::GetPCCountScriptSummary(cx, script); if (!text) @@ -3342,9 +3155,7 @@ nsDOMWindowUtils::GetPCCountScriptSummary(int32_t script, JSContext* cx, nsAStri NS_IMETHODIMP nsDOMWindowUtils::GetPCCountScriptContents(int32_t script, JSContext* cx, nsAString& result) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); JSString *text = js::GetPCCountScriptContents(cx, script); if (!text) @@ -3361,9 +3172,7 @@ nsDOMWindowUtils::GetPCCountScriptContents(int32_t script, JSContext* cx, nsAStr NS_IMETHODIMP nsDOMWindowUtils::GetPaintingSuppressed(bool *aPaintingSuppressed) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_TRUE(window, NS_ERROR_FAILURE); @@ -3380,9 +3189,7 @@ nsDOMWindowUtils::GetPaintingSuppressed(bool *aPaintingSuppressed) NS_IMETHODIMP nsDOMWindowUtils::GetPlugins(JSContext* cx, JS::MutableHandle aPlugins) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr doc = GetDocument(); NS_ENSURE_STATE(doc); @@ -3476,9 +3283,7 @@ NS_IMETHODIMP nsDOMWindowUtils::SetContentDocumentFixedPositionMargins(float aTop, float aRight, float aBottom, float aLeft) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); if (!(aTop >= 0.0f && aRight >= 0.0f && aBottom >= 0.0f && aLeft >= 0.0f)) { return NS_ERROR_ILLEGAL_VALUE; @@ -3502,9 +3307,7 @@ nsresult nsDOMWindowUtils::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement, const nsAString& aNewOrigin) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr doc = GetDocument(); NS_ENSURE_STATE(doc); @@ -3516,9 +3319,7 @@ nsDOMWindowUtils::RemoteFrameFullscreenChanged(nsIDOMElement* aFrameElement, nsresult nsDOMWindowUtils::RemoteFrameFullscreenReverted() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr doc = GetDocument(); NS_ENSURE_STATE(doc); @@ -3530,9 +3331,7 @@ nsDOMWindowUtils::RemoteFrameFullscreenReverted() nsresult nsDOMWindowUtils::ExitFullscreen() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsIDocument::ExitFullscreen(nullptr, /* async */ false); return NS_OK; @@ -3543,9 +3342,7 @@ nsDOMWindowUtils::SelectAtPoint(float aX, float aY, uint32_t aSelectBehavior, bool *_retval) { *_retval = false; - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsSelectionAmount amount; switch (aSelectBehavior) { @@ -3635,9 +3432,7 @@ convertSheetType(uint32_t aSheetType) NS_IMETHODIMP nsDOMWindowUtils::LoadSheet(nsIURI *aSheetURI, uint32_t aSheetType) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_ARG_POINTER(aSheetURI); NS_ENSURE_ARG(aSheetType == AGENT_SHEET || @@ -3655,9 +3450,7 @@ nsDOMWindowUtils::LoadSheet(nsIURI *aSheetURI, uint32_t aSheetType) NS_IMETHODIMP nsDOMWindowUtils::RemoveSheet(nsIURI *aSheetURI, uint32_t aSheetType) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_ARG_POINTER(aSheetURI); NS_ENSURE_ARG(aSheetType == AGENT_SHEET || @@ -3676,9 +3469,7 @@ nsDOMWindowUtils::RemoveSheet(nsIURI *aSheetURI, uint32_t aSheetType) NS_IMETHODIMP nsDOMWindowUtils::GetIsHandlingUserInput(bool* aHandlingUserInput) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); *aHandlingUserInput = EventStateManager::IsHandlingUserInput(); @@ -3688,9 +3479,7 @@ nsDOMWindowUtils::GetIsHandlingUserInput(bool* aHandlingUserInput) NS_IMETHODIMP nsDOMWindowUtils::AllowScriptsToClose() { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); static_cast(window.get())->AllowScriptsToClose(); @@ -3700,9 +3489,7 @@ nsDOMWindowUtils::AllowScriptsToClose() NS_IMETHODIMP nsDOMWindowUtils::GetIsParentWindowMainWidgetVisible(bool* aIsVisible) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // this should reflect the "is parent window visible" logic in // nsWindowWatcher::OpenWindowInternal() @@ -3737,9 +3524,7 @@ NS_IMETHODIMP nsDOMWindowUtils::IsNodeDisabledForEvents(nsIDOMNode* aNode, bool* aRetVal) { *aRetVal = false; - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr n = do_QueryInterface(aNode); nsINode* node = n; while (node) { @@ -3791,9 +3576,7 @@ nsDOMWindowUtils::DispatchEventToChromeOnly(nsIDOMEventTarget* aTarget, bool* aRetVal) { *aRetVal = false; - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); NS_ENSURE_STATE(aTarget && aEvent); aEvent->GetInternalNSEvent()->mFlags.mOnlyChromeDispatch = true; aTarget->DispatchEvent(aEvent, aRetVal); @@ -3803,9 +3586,7 @@ nsDOMWindowUtils::DispatchEventToChromeOnly(nsIDOMEventTarget* aTarget, NS_IMETHODIMP nsDOMWindowUtils::RunInStableState(nsIRunnable *runnable) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr appShell(do_GetService(kAppShellCID)); if (!appShell) { @@ -3818,9 +3599,7 @@ nsDOMWindowUtils::RunInStableState(nsIRunnable *runnable) NS_IMETHODIMP nsDOMWindowUtils::RunBeforeNextEvent(nsIRunnable *runnable) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr appShell(do_GetService(kAppShellCID)); if (!appShell) { @@ -3835,9 +3614,7 @@ nsDOMWindowUtils::GetOMTAStyle(nsIDOMElement* aElement, const nsAString& aProperty, nsAString& aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr element = do_QueryInterface(aElement); if (!element) { @@ -3898,9 +3675,7 @@ nsDOMWindowUtils::GetOMTAOrComputedStyle(nsIDOMElement* aElement, const nsAString& aProperty, nsAString& aResult) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); // Try to get OMTA style nsresult rv = GetOMTAStyle(aElement, aProperty, aResult); @@ -3925,9 +3700,7 @@ NS_IMETHODIMP nsDOMWindowUtils::GetContentAPZTestData(JSContext* aContext, JS::MutableHandleValue aOutContentTestData) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); if (nsIWidget* widget = GetWidget()) { nsRefPtr lm = widget->GetLayerManager(); @@ -3946,9 +3719,7 @@ NS_IMETHODIMP nsDOMWindowUtils::GetCompositorAPZTestData(JSContext* aContext, JS::MutableHandleValue aOutCompositorTestData) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); if (nsIWidget* widget = GetWidget()) { nsRefPtr lm = widget->GetLayerManager(); @@ -3968,9 +3739,7 @@ nsDOMWindowUtils::GetCompositorAPZTestData(JSContext* aContext, NS_IMETHODIMP nsDOMWindowUtils::GetAudioMuted(bool* aMuted) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); @@ -3981,9 +3750,7 @@ nsDOMWindowUtils::GetAudioMuted(bool* aMuted) NS_IMETHODIMP nsDOMWindowUtils::SetAudioMuted(bool aMuted) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); @@ -3994,9 +3761,7 @@ nsDOMWindowUtils::SetAudioMuted(bool aMuted) NS_IMETHODIMP nsDOMWindowUtils::GetAudioVolume(float* aVolume) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); @@ -4007,9 +3772,7 @@ nsDOMWindowUtils::GetAudioVolume(float* aVolume) NS_IMETHODIMP nsDOMWindowUtils::SetAudioVolume(float aVolume) { - if (!nsContentUtils::IsCallerChrome()) { - return NS_ERROR_DOM_SECURITY_ERR; - } + MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome()); nsCOMPtr window = do_QueryReferent(mWindow); NS_ENSURE_STATE(window); diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index 7dec36d5446..6f9343d8914 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -1176,10 +1176,23 @@ ContentParent::ShutDownProcess(bool aCloseWithError) // shut down the cycle collector. But by then it's too late to release any // CC'ed objects, so we need to null them out here, while we still can. See // bug 899761. - if (mMessageManager) { - mMessageManager->Disconnect(); - mMessageManager = nullptr; - } + ShutDownMessageManager(); +} + +void +ContentParent::ShutDownMessageManager() +{ + if (!mMessageManager) { + return; + } + + mMessageManager->ReceiveMessage( + static_cast(mMessageManager.get()), + CHILD_PROCESS_SHUTDOWN_MESSAGE, false, + nullptr, nullptr, nullptr, nullptr); + + mMessageManager->Disconnect(); + mMessageManager = nullptr; } void @@ -1305,12 +1318,8 @@ ContentParent::ActorDestroy(ActorDestroyReason why) mForceKillTask = nullptr; } - nsRefPtr ppm = mMessageManager; - if (ppm) { - ppm->ReceiveMessage(static_cast(ppm.get()), - CHILD_PROCESS_SHUTDOWN_MESSAGE, false, - nullptr, nullptr, nullptr, nullptr); - } + ShutDownMessageManager(); + nsRefPtr kungFuDeathGrip(this); nsCOMPtr obs = mozilla::services::GetObserverService(); if (obs) { @@ -1321,10 +1330,6 @@ ContentParent::ActorDestroy(ActorDestroyReason why) } } - if (ppm) { - ppm->Disconnect(); - } - // Tell the memory reporter manager that this ContentParent is going away. nsRefPtr mgr = nsMemoryReporterManager::GetOrCreate(); diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 8c7f943f389..47d03b0c9f1 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -334,6 +334,10 @@ private: */ void ShutDownProcess(bool aCloseWithError); + // Perform any steps necesssary to gracefully shtudown the message + // manager and null out mMessageManager. + void ShutDownMessageManager(); + PCompositorParent* AllocPCompositorParent(mozilla::ipc::Transport* aTransport, base::ProcessId aOtherProcess) MOZ_OVERRIDE; diff --git a/dom/messages/SystemMessagePermissionsChecker.jsm b/dom/messages/SystemMessagePermissionsChecker.jsm index cd37ba03e22..359ee3f5bfe 100644 --- a/dom/messages/SystemMessagePermissionsChecker.jsm +++ b/dom/messages/SystemMessagePermissionsChecker.jsm @@ -56,6 +56,9 @@ this.SystemMessagePermissionsTable = { "bluetooth": [] }, "connection": { }, + "captive-portal": { + "wifi-manage": [] + }, "dummy-system-message": { }, // for system message testing framework "headset-button": { }, "icc-stkcommand": { diff --git a/dom/settings/tests/test_settings_blobs.html b/dom/settings/tests/test_settings_blobs.html index f2b557775cc..fa5c0cea76c 100644 --- a/dom/settings/tests/test_settings_blobs.html +++ b/dom/settings/tests/test_settings_blobs.html @@ -45,7 +45,7 @@ function onFailure() { let mozSettings = window.navigator.mozSettings; let req; -let storedBlob = new Blob([""], {"type": "text/xml"}); +let storedBlob = new Blob(['12345'], {"type": "text/plain"}); function checkBlob(blob) { try { diff --git a/dom/wifi/test/marionette/head.js b/dom/wifi/test/marionette/head.js new file mode 100644 index 00000000000..ef37b2a2a9e --- /dev/null +++ b/dom/wifi/test/marionette/head.js @@ -0,0 +1,731 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +let Promise = SpecialPowers.Cu.import('resource://gre/modules/Promise.jsm').Promise; + +const STOCK_HOSTAPD_NAME = 'goldfish-hostapd'; +const HOSTAPD_CONFIG_PATH = '/data/misc/wifi/hostapd/'; + +const HOSTAPD_COMMON_CONFIG = { + driver: 'test', + ctrl_interface: '/data/misc/wifi/hostapd', + test_socket: 'DIR:/data/misc/wifi/sockets', + hw_mode: 'b', + channel: '2', +}; + +const HOSTAPD_CONFIG_LIST = [ + { ssid: 'ap0' }, + + { ssid: 'ap1', + wpa: 1, + wpa_pairwise: 'TKIP CCMP', + wpa_passphrase: '12345678' + }, + + { ssid: 'ap2', + wpa: 2, + rsn_pairwise: 'CCMP', + wpa_passphrase: '12345678', + }, +]; + +let gTestSuite = (function() { + let suite = {}; + + // Private member variables of the returned object |suite|. + let wifiManager; + let wifiOrigEnabled; + let pendingEmulatorShellCount = 0; + + /** + * Send emulator shell command with safe guard. + * + * We should only call |finish()| after all emulator command transactions + * end, so here comes with the pending counter. Resolve when the emulator + * gives positive response, and reject otherwise. + * + * Fulfill params: + * result -- an array of emulator response lines. + * Reject params: + * result -- an array of emulator response lines. + * + * @param aCommand + * A string command to be passed to emulator through its telnet console. + * + * @return A deferred promise. + */ + function runEmulatorShellSafe(aCommand) { + let deferred = Promise.defer(); + + ++pendingEmulatorShellCount; + runEmulatorShell(aCommand, function(aResult) { + --pendingEmulatorShellCount; + + ok(true, "Emulator shell response: " + JSON.stringify(aResult)); + if (Array.isArray(aResult)) { + deferred.resolve(aResult); + } else { + deferred.reject(aResult); + } + }); + + return deferred.promise; + } + + /** + * Wait for one named MozWifiManager event. + * + * Resolve if that named event occurs. Never reject. + * + * Fulfill params: the DOMEvent passed. + * + * @param aEventName + * A string event name. + * + * @return A deferred promise. + */ + function waitForWifiManagerEventOnce(aEventName) { + let deferred = Promise.defer(); + + wifiManager.addEventListener(aEventName, function onevent(aEvent) { + wifiManager.removeEventListener(aEventName, onevent); + + ok(true, "WifiManager event '" + aEventName + "' got."); + deferred.resolve(aEvent); + }); + + return deferred.promise; + } + + /** + * Get the detail of currently running processes containing the given name. + * + * Use shell command 'ps' to get the desired process's detail. Never reject. + * + * Fulfill params: + * result -- an array of { pname, pid } + * + * @param aProcessName + * The process to get the detail. + * + * @return A deferred promise. + */ + function getProcessDetail(aProcessName) { + return runEmulatorShellSafe(['ps']) + .then(processes => { + // Sample 'ps' output: + // + // USER PID PPID VSIZE RSS WCHAN PC NAME + // root 1 0 284 204 c009e6c4 0000deb4 S /init + // root 2 0 0 0 c0052c64 00000000 S kthreadd + // root 3 2 0 0 c0044978 00000000 S ksoftirqd/0 + // + let detail = []; + + processes.shift(); // Skip the first line. + for (let i = 0; i < processes.length; i++) { + let tokens = processes[i].split(/\s+/); + let pname = tokens[tokens.length - 1]; + let pid = tokens[1]; + if (-1 !== pname.indexOf(aProcessName)) { + detail.push({ pname: pname, pid: pid }); + } + } + + return detail; + }); + } + + /** + * Add required permissions for wifi testing. Never reject. + * + * The permissions required for wifi testing are 'wifi-manage' and 'settings-write'. + * Never reject. + * + * Fulfill params: (none) + * + * @return A deferred promise. + */ + function addRequiredPermissions() { + let deferred = Promise.defer(); + + let permissions = [{ 'type': 'wifi-manage', 'allow': 1, 'context': window.document }, + { 'type': 'settings-write', 'allow': 1, 'context': window.document }]; + + SpecialPowers.pushPermissions(permissions, function() { + deferred.resolve(); + }); + + return deferred.promise; + } + + /** + * Wrap DOMRequest onsuccess/onerror events to Promise resolve/reject. + * + * Fulfill params: A DOMEvent. + * Reject params: A DOMEvent. + * + * @param aRequest + * A DOMRequest instance. + * + * @return A deferred promise. + */ + function wrapDomRequestAsPromise(aRequest) { + let deferred = Promise.defer(); + + ok(aRequest instanceof DOMRequest, + "aRequest is instanceof " + aRequest.constructor); + + aRequest.addEventListener("success", function(aEvent) { + deferred.resolve(aEvent); + }); + aRequest.addEventListener("error", function(aEvent) { + deferred.reject(aEvent); + }); + + return deferred.promise; + } + + /** + * Ensure wifi is enabled/disabled. + * + * Issue a wifi enable/disable request if wifi is not in the desired state; + * return a resolved promise otherwise. Note that you cannot rely on this + * function to test the correctness of enabling/disabling wifi. + * (use requestWifiEnabled instead) + * + * Fulfill params: (none) + * Reject params: (none) + * + * @return a resolved promise or deferred promise. + */ + function ensureWifiEnabled(aEnabled) { + if (wifiManager.enabled === aEnabled) { + log('Already ' + (aEnabled ? 'enabled' : 'disabled')); + return Promise.resolve(); + } + return requestWifiEnabled(aEnabled); + } + + /** + * Issue a request to enable/disable wifi. + * + * For current design, this function will attempt to enable/disable wifi by + * writing 'wifi.enabled' regardless of the wifi state. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @return A deferred promise. + */ + function requestWifiEnabled(aEnabled) { + return Promise.all([ + waitForWifiManagerEventOnce(aEnabled ? 'enabled' : 'disabled'), + setSettings({ 'wifi.enabled': aEnabled }), + ]); + } + + /** + * Issue a request to scan all wifi available networks. + * + * Resolve when we get the scan result; reject when any error + * occurs. + * + * Fulfill params: An array of MozWifiNetwork + * Reject params: (none) + * + * @return A deferred promise. + */ + function requestWifiScan() { + let request = wifiManager.getNetworks(); + return wrapDomRequestAsPromise(request) + .then(event => event.target.result); + } + + /** + * Request wifi scan and verify the scan result as well. + * + * Issue a wifi scan request and check if the result is expected. + * Since the old APs may be cached and the newly added APs may be + * still not scan-able, a couple of attempts are acceptable. + * Resolve if we eventually get the expected scan result; reject otherwise. + * + * Fulfill params: The scan result, which is an array of MozWifiNetwork + * Reject params: (none) + * + * @param aRetryCnt + * The maxmimum number of attempts until we get the expected scan result. + * @param aExpectedNetworks + * An array of object, each of which contains at least the |ssid| property. + * + * @return A deferred promise. + */ + function testWifiScanWithRetry(aRetryCnt, aExpectedNetworks) { + + // Check if every single ssid of each |aScanResult| exists in |aExpectedNetworks| + // as well as the length of |aScanResult| equals to |aExpectedNetworks|. + function isScanResultExpected(aScanResult) { + if (aScanResult.length !== aExpectedNetworks.length) { + return false; + } + + for (let i = 0; i < aScanResult.length; i++) { + if (-1 === getFirstIndexBySsid(aScanResult[i].ssid, aExpectedNetworks)) { + return false; + } + } + return true; + } + + return requestWifiScan() + .then(function (networks) { + if (isScanResultExpected(networks, aExpectedNetworks)) { + return networks; + } + if (aRetryCnt > 0) { + return testWifiScanWithRetry(aRetryCnt - 1, aExpectedNetworks); + } + throw 'Unexpected scan result!'; + }); + } + + /** + * Set mozSettings values. + * + * Resolve if that mozSettings value is set successfully, reject otherwise. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @param aSettings + * An object of format |{key1: value1, key2: value2, ...}|. + * @param aAllowError [optional] + * A boolean value. If set to true, an error response won't be treated + * as test failure. Default: false. + * + * @return A deferred promise. + */ + function setSettings(aSettings, aAllowError) { + let request = window.navigator.mozSettings.createLock().set(aSettings); + return wrapDomRequestAsPromise(request) + .then(function resolve() { + ok(true, "setSettings(" + JSON.stringify(aSettings) + ")"); + }, function reject() { + ok(aAllowError, "setSettings(" + JSON.stringify(aSettings) + ")"); + }); + } + + /** + * Start hostapd processes with given configuration list. + * + * For starting one hostapd, we need to generate a specific config file + * then launch a hostapd process with the confg file path passed. The + * config file is generated by two sources: one is the common + * part (HOSTAPD_COMMON_CONFIG) and the other is from the given |aConfigList|. + * Resolve when all the hostpads are requested to start. It is not guaranteed + * that all the hostapds will be up and running successfully. Never reject. + * + * Fulfill params: (none) + * + * @param aConfigList + * An array of config objects, each property in which will be + * output to the confg file with the format: [key]=[value] in one line. + * See http://hostap.epitest.fi/cgit/hostap/plain/hostapd/hostapd.conf + * for more information. + * + * @return A deferred promise. + */ + function startHostapds(aConfigList) { + + function createConfigFromCommon(aIndex) { + // Create an copy of HOSTAPD_COMMON_CONFIG. + let config = JSON.parse(JSON.stringify(HOSTAPD_COMMON_CONFIG)); + + // Add user config. + for (let key in aConfigList[aIndex]) { + config[key] = aConfigList[aIndex][key]; + } + + // 'interface' is a required field but no need of being configurable + // for a test case. So we initialize this field on our own. + config.interface = 'AP-' + aIndex; + + return config; + } + + function startOneHostapd(aIndex) { + let configFileName = HOSTAPD_CONFIG_PATH + 'ap' + aIndex + '.conf'; + return writeHostapdConfFile(configFileName, createConfigFromCommon(aIndex)) + .then(() => runEmulatorShellSafe(['hostapd', '-B', configFileName])) + .then(function (reply) { + // It may fail at the first time due to the previous ungracefully terminated one. + if (reply[0] === 'bind(PF_UNIX): Address already in use') { + return startOneHostapd(aIndex); + } + }); + } + + return Promise.all(aConfigList.map(function(aConfig, aIndex) { + return startOneHostapd(aIndex); + })); + } + + /** + * Kill all the running hostapd processes. + * + * Use shell command 'kill -9' to kill all hostapds. Never reject. + * + * Fulfill params: (none) + * + * @return A deferred promise. + */ + function killAllHostapd() { + return getProcessDetail('hostapd') + .then(function (runningHostapds) { + let promises = runningHostapds.map(runningHostapd => { + return runEmulatorShellSafe(['kill', '-9', runningHostapd.pid]); + }); + return Promise.all(promises); + }); + } + + /** + * Write out the config file to the given path. + * + * For each key/value pair in |aConfig|, + * + * [key]=[value] + * + * will be output to one new line. Never reject. + * + * Fulfill params: (none) + * + * @param aFilePath + * The file path that we desire the config file to be located. + * + * @param aConfig + * The config object. + * + * @return A deferred promise. + */ + function writeHostapdConfFile(aFilePath, aConfig) { + let content = ''; + for (let key in aConfig) { + if (aConfig.hasOwnProperty(key)) { + content += (key + '=' + aConfig[key] + '\n'); + } + } + return writeFile(aFilePath, content); + } + + /** + * Write file to the given path filled with given content. + * + * For now it is implemented by shell command 'echo'. Also, if the + * content contains whitespace, we need to quote the content to + * avoid error. Never reject. + * + * Fulfill params: (none) + * + * @param aFilePath + * The file path that we desire the file to be located. + * + * @param aContent + * The content as string which should be written to the file. + * + * @return A deferred promise. + */ + function writeFile(aFilePath, aContent) { + if (-1 === aContent.indexOf(' ')) { + aContent = '"' + aContent + '"'; + } + return runEmulatorShellSafe(['echo', aContent, '>', aFilePath]); + } + + /** + * Check if a init service is running or not. + * + * Check the android property 'init.svc.[aServiceName]' to determine if + * a init service is running. Reject if the propery is neither 'running' + * nor 'stopped'. + * + * Fulfill params: + * result -- |true| if the init service is running; |false| otherwise. + * Reject params: (none) + * + * @param aServiceName + * The init service name. + * + * @return A deferred promise. + */ + function isInitServiceRunning(aServiceName) { + return runEmulatorShellSafe(['getprop', 'init.svc.' + aServiceName]) + .then(function (result) { + if ('running' !== result[0] && 'stopped' !== result[0]) { + throw 'Init service running state should be "running" or "stopped".'; + } + return 'running' === result[0]; + }); + } + + /** + * Wait for timeout. + * + * Resolve when the given duration elapsed. Never reject. + * + * Fulfill params: (none) + * + * @param aTimeoutMs + * The duration after which the timeout event should occurs. + * + * @return A deferred promise. + */ + function waitForTimeout(aTimeoutMs) { + let deferred = Promise.defer(); + + setTimeout(function() { + deferred.resolve(); + }, aTimeoutMs); + + return deferred.promise; + } + + /** + * Start or stop an init service. + * + * Use shell command 'start'/'stop' to start/stop an init service. + * The running state will also be checked after we start/stop the service. + * Resolve if the service is successfully started/stopped; Reject otherwise. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @param aServiceName + * The name of the service we want to start/stop. + * + * @param aStart + * |true| for starting the init service. |false| for stopping. + * + * @return A deferred promise. + */ + function startStopInitService(aServiceName, aStart) { + let retryCnt = 5; + + return runEmulatorShellSafe([aStart ? 'start' : 'stop', aServiceName]) + .then(() => isInitServiceRunning(aServiceName)) + .then(function onIsServiceRunningResolved(aIsRunning) { + if (aStart === aIsRunning) { + return; + } + + if (retryCnt-- > 0) { + log('Failed to ' + (aStart ? 'start ' : 'stop ') + aServiceName + + '. Retry: ' + retryCnt); + + return waitForTimeout(500) + .then(() => isInitServiceRunning(aServiceName)) + .then(onIsServiceRunningResolved); + } + + throw 'Failed to ' + (aStart ? 'start' : 'stop') + ' ' + aServiceName; + }); + } + + /** + * Start the stock hostapd. + * + * Since the stock hostapd is an init service, use |startStopInitService| to + * start it. Note that we might fail to start the stock hostapd at the first time + * for unknown reason so give it the second chance to start again. + * Resolve when we are eventually successful to start the stock hostapd; Reject + * otherwise. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @return A deferred promise. + */ + function startStockHostapd() { + return startStopInitService(STOCK_HOSTAPD_NAME, true) + .then(null, function onreject() { + log('Failed to restart goldfish-hostapd at the first time. Try again!'); + return startStopInitService((STOCK_HOSTAPD_NAME), true); + }); + } + + /** + * Stop the stock hostapd. + * + * Since the stock hostapd is an init service, use |startStopInitService| to + * stop it. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @return A deferred promise. + */ + function stopStockHostapd() { + return startStopInitService(STOCK_HOSTAPD_NAME, false); + } + + /** + * Get the index of the first matching entry by |ssid|. + * + * Find the index of the first entry of |aArray| which property |ssid| + * is same as |aSsid|. + * + * @param aSsid + * The ssid that we want to match. + * @param aArray + * An array of objects, each of which should have the property |ssid|. + * + * @return The 0-based index of first matching entry if found; -1 otherwise. + */ + function getFirstIndexBySsid(aSsid, aArray) { + for (let i = 0; i < aArray.length; i++) { + if (aArray[i].ssid === aSsid) { + return i; + } + } + return -1; + } + + /** + * Count the number of running process and verify if the count is expected. + * + * Return a promise that resolves when the process has expected number + * of running instances and rejects otherwise. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @param aOrigWifiEnabled + * Boolean which indicates wifi was originally enabled. + * + * @return A deferred promise. + */ + function verifyNumOfProcesses(aProcessName, aExpectedNum) { + return getProcessDetail(aProcessName) + .then(function (detail) { + if (detail.length === aExpectedNum) { + return; + } + throw 'Unexpected number of running processes:' + aProcessName + + ', expected: ' + aExpectedNum + ', actual: ' + detail.length; + }); + } + + /** + * Clean up all the allocated resources and running services for the test. + * + * After the test no matter success or failure, we should + * 1) Restore to the wifi original state (enabled or disabled) + * 2) Wait until all pending emulator shell commands are done. + * + * |finsih| will be called in the end. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @return A deferred promise. + */ + function cleanUp() { + waitFor(function() { + return ensureWifiEnabled(wifiOrigEnabled) + .then(finish); + }, function() { + return pendingEmulatorShellCount === 0; + }); + } + + /** + * Init the test environment. + * + * Mainly add the required permissions and initialize the wifiManager + * and the orignal state of wifi. Reject if failing to create + * window.navigator.mozWifiManager; resolve if all is well. + * + * |finsih| will be called in the end. + * + * Fulfill params: (none) + * Reject params: The reject reason. + * + * @return A deferred promise. + */ + function initTestEnvironment() { + return addRequiredPermissions() + .then(function() { + wifiManager = window.navigator.mozWifiManager; + if (!wifiManager) { + throw 'window.navigator.mozWifiManager is NULL'; + } + wifiOrigEnabled = wifiManager.enabled; + }); + } + + //--------------------------------------------------- + // Public test suite functions + //--------------------------------------------------- + suite.getWifiManager = (() => wifiManager); + suite.ensureWifiEnabled = ensureWifiEnabled; + suite.requestWifiEnabled = requestWifiEnabled; + suite.startHostapds = startHostapds; + suite.getProcessDetail = getProcessDetail; + suite.killAllHostapd = killAllHostapd; + suite.wrapDomRequestAsPromise = wrapDomRequestAsPromise; + suite.waitForWifiManagerEventOnce = waitForWifiManagerEventOnce; + suite.verifyNumOfProcesses = verifyNumOfProcesses; + suite.testWifiScanWithRetry = testWifiScanWithRetry; + suite.getFirstIndexBySsid = getFirstIndexBySsid; + + /** + * Common test routine. + * + * Start a test with the given test case chain. The test environment will be + * settled down before the test. After the test, all the affected things will + * be restored. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @param aTestCaseChain + * The test case entry point, which can be a function or a promise. + * + * @return A deferred promise. + */ + suite.doTest = function(aTestCaseChain) { + return initTestEnvironment() + .then(aTestCaseChain) + .then(function onresolve() { + cleanUp(); + }, function onreject(aReason) { + ok(false, 'Promise rejects during test' + (aReason ? '(' + aReason + ')' : '')); + cleanUp(); + }); + }; + + /** + * Common test routine without the presence of stock hostapd. + * + * Same as doTest except stopping the stock hostapd before test + * and restarting it after test. + * + * Fulfill params: (none) + * Reject params: (none) + * + * @param aTestCaseChain + * The test case entry point, which can be a function or a promise. + * + * @return A deferred promise. + */ + suite.doTestWithoutStockAp = function(aTestCaseChain) { + return suite.doTest(function() { + return stopStockHostapd() + .then(aTestCaseChain) + .then(startStockHostapd); + }); + }; + + return suite; +})(); diff --git a/dom/wifi/test/marionette/manifest.ini b/dom/wifi/test/marionette/manifest.ini new file mode 100644 index 00000000000..f74e51c1388 --- /dev/null +++ b/dom/wifi/test/marionette/manifest.ini @@ -0,0 +1,8 @@ +[DEFAULT] +b2g = true +browser = false +qemu = true + +[test_wifi_enable.js] +[test_wifi_scan.js] +[test_wifi_associate.js] diff --git a/dom/wifi/test/marionette/test_wifi_associate.js b/dom/wifi/test/marionette/test_wifi_associate.js new file mode 100644 index 00000000000..1c7fc6a0590 --- /dev/null +++ b/dom/wifi/test/marionette/test_wifi_associate.js @@ -0,0 +1,121 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = 'head.js'; + +const SCAN_RETRY_CNT = 5; + +/** + * Test wifi association. + * + * Associate with the given network object which is obtained by + * MozWifiManager.getNetworks() (i.e. MozWifiNetwork). + * Resolve when the 'connected' status change event is received. + * Note that we might see other events like 'connecting' + * before 'connected'. So we need to call |waitForWifiManagerEventOnce| + * again whenever non 'connected' event is seen. Never reject. + * + * Fulfill params: (none) + * + * @param aNetwork + * An object of MozWifiNetwork. + * + * @return A deferred promise. + */ +function testAssociate(aNetwork) { + if (!setPasswordIfNeeded(aNetwork)) { + throw 'Failed to set password'; + } + + function waitForConnected() { + return gTestSuite.waitForWifiManagerEventOnce('statuschange') + .then(function onstatuschange(event) { + log("event.status: " + event.status); + log("event.network.ssid: " + (event.network ? event.network.ssid : '')); + + if ("connected" === event.status && + event.network.ssid === aNetwork.ssid) { + return; // Got expected 'connected' event from aNetwork.ssid. + } + + log('Not expected "connected" statuschange event. Wait again!'); + return waitForConnected(); + }); + } + + let promises = []; + + // Register the event listerner to wait for 'connected' event first + // to avoid racing issue. + promises.push(waitForConnected()); + + // Then we do the association. + let request = gTestSuite.getWifiManager().associate(aNetwork); + promises.push(gTestSuite.wrapDomRequestAsPromise(request)); + + return Promise.all(promises); +} + +/** + * Convert the given MozWifiNetwork object array to testAssociate chain. + * + * @param aNetworks + * An array of MozWifiNetwork which we want to convert. + * + * @return A promise chain which "then"s testAssociate accordingly. + */ +function convertToTestAssociateChain(aNetworks) { + let chain = Promise.resolve(); + + aNetworks.forEach(function (aNetwork) { + chain = chain.then(() => testAssociate(aNetwork)); + }); + + return chain; +} + +/** + * Set the password for associating the given network if needed. + * + * Set the password by looking up HOSTAPD_CONFIG_LIST. This function + * will also set |keyManagement| properly. + * + * @param aNetwork + * The MozWifiNetwork object. + * + * @return |true| if either insesure or successfully set the password/keyManagement. + * |false| if the given network is not found in HOSTAPD_CONFIG_LIST. + */ +function setPasswordIfNeeded(aNetwork) { + let i = gTestSuite.getFirstIndexBySsid(aNetwork.ssid, HOSTAPD_CONFIG_LIST); + if (-1 === i) { + log('unknown ssid: ' + aNetwork.ssid); + return false; // Error! + } + + if (!aNetwork.security.length) { + return true; // No need to set password. + } + + let security = aNetwork.security[0]; + if (/PSK$/.test(security)) { + aNetwork.psk = HOSTAPD_CONFIG_LIST[i].wpa_passphrase; + aNetwork.keyManagement = 'WPA-PSK'; + } else if (/WEP$/.test(security)) { + aNetwork.wep = HOSTAPD_CONFIG_LIST[i].wpa_passphrase; + aNetwork.keyManagement = 'WEP'; + } + + return true; +} + +gTestSuite.doTestWithoutStockAp(function() { + return gTestSuite.ensureWifiEnabled(true) + .then(() => gTestSuite.startHostapds(HOSTAPD_CONFIG_LIST)) + .then(() => gTestSuite.verifyNumOfProcesses('hostapd', HOSTAPD_CONFIG_LIST.length)) + .then(() => gTestSuite.testWifiScanWithRetry(SCAN_RETRY_CNT, HOSTAPD_CONFIG_LIST)) + .then(networks => convertToTestAssociateChain(networks)) + .then(gTestSuite.killAllHostapd) + .then(() => gTestSuite.verifyNumOfProcesses('hostapd', 0)); +}); diff --git a/dom/wifi/test/marionette/test_wifi_enable.js b/dom/wifi/test/marionette/test_wifi_enable.js new file mode 100644 index 00000000000..6a1bec7a298 --- /dev/null +++ b/dom/wifi/test/marionette/test_wifi_enable.js @@ -0,0 +1,11 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = 'head.js'; + +gTestSuite.doTest(function() { + return Promise.resolve() + .then(() => gTestSuite.ensureWifiEnabled(false)) + .then(() => gTestSuite.requestWifiEnabled(true)); +}); \ No newline at end of file diff --git a/dom/wifi/test/marionette/test_wifi_scan.js b/dom/wifi/test/marionette/test_wifi_scan.js new file mode 100644 index 00000000000..4bb7d1a3d5d --- /dev/null +++ b/dom/wifi/test/marionette/test_wifi_scan.js @@ -0,0 +1,43 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; +MARIONETTE_HEAD_JS = 'head.js'; + +const SCAN_RETRY_CNT = 5; + +/** + * Test scan with no AP present. + * + * The precondition is: + * 1) Wifi is enabled. + * 2) All the hostapds are turned off. + * + * @return deferred promise. + */ +function testScanNoAp() { + return gTestSuite.testWifiScanWithRetry(SCAN_RETRY_CNT, []); +} + +/** + * Test scan with APs present. + * + * The precondition is: + * 1) Wifi is enabled. + * 2) All the hostapds are turned off. + * + * @return deferred promise. + */ +function testScanWithAps() { + return gTestSuite.startHostapds(HOSTAPD_CONFIG_LIST) + .then(() => gTestSuite.verifyNumOfProcesses('hostapd', HOSTAPD_CONFIG_LIST.length)) + .then(() => gTestSuite.testWifiScanWithRetry(SCAN_RETRY_CNT, HOSTAPD_CONFIG_LIST)) + .then(gTestSuite.killAllHostapd) + .then(() => gTestSuite.verifyNumOfProcesses('hostapd', 0)); +} + +gTestSuite.doTestWithoutStockAp(function() { + return gTestSuite.ensureWifiEnabled(true) + .then(testScanNoAp) + .then(testScanWithAps); +}); diff --git a/gfx/gl/GLContextProviderEGL.cpp b/gfx/gl/GLContextProviderEGL.cpp index e6d89bf6d74..20f8ca6bd8c 100644 --- a/gfx/gl/GLContextProviderEGL.cpp +++ b/gfx/gl/GLContextProviderEGL.cpp @@ -268,6 +268,7 @@ GLContextEGL::~GLContextEGL() #endif sEGLLibrary.fDestroyContext(EGL_DISPLAY(), mContext); + sEGLLibrary.UnsetCachedCurrentContext(); #if defined(MOZ_WIDGET_GONK) && ANDROID_VERSION < 17 if (!mIsOffscreen) { @@ -381,7 +382,18 @@ GLContextEGL::MakeCurrentImpl(bool aForce) { // Assume that EGL has the same problem as WGL does, // where MakeCurrent with an already-current context is // still expensive. - if (aForce || sEGLLibrary.fGetCurrentContext() != mContext) { + bool hasDifferentContext = false; + if (sEGLLibrary.CachedCurrentContext() != mContext) { + // even if the cached context doesn't match the current one + // might still + if (sEGLLibrary.fGetCurrentContext() != mContext) { + hasDifferentContext = true; + } else { + sEGLLibrary.SetCachedCurrentContext(mContext); + } + } + + if (aForce || hasDifferentContext) { EGLSurface surface = mSurfaceOverride != EGL_NO_SURFACE ? mSurfaceOverride : mSurface; @@ -402,7 +414,11 @@ GLContextEGL::MakeCurrentImpl(bool aForce) { printf_stderr("EGL Error: 0x%04x\n", eglError); #endif } + } else { + sEGLLibrary.SetCachedCurrentContext(mContext); } + } else { + MOZ_ASSERT(sEGLLibrary.CachedCurrentContextMatches()); } return succeeded; diff --git a/gfx/gl/GLLibraryEGL.cpp b/gfx/gl/GLLibraryEGL.cpp index 9eb895fe3f5..3a484189fbc 100644 --- a/gfx/gl/GLLibraryEGL.cpp +++ b/gfx/gl/GLLibraryEGL.cpp @@ -6,6 +6,7 @@ #include "gfxCrashReporterUtils.h" #include "mozilla/Preferences.h" +#include "mozilla/Assertions.h" #include "nsDirectoryServiceDefs.h" #include "nsDirectoryServiceUtils.h" #include "nsPrintfCString.h" @@ -20,6 +21,9 @@ namespace mozilla { namespace gl { GLLibraryEGL sEGLLibrary; +#ifdef MOZ_B2G +ThreadLocal GLLibraryEGL::sCurrentContext; +#endif // should match the order of EGLExtensions, and be null-terminated. static const char *sEGLExtensionNames[] = { @@ -104,6 +108,11 @@ GLLibraryEGL::EnsureInitialized() mozilla::ScopedGfxFeatureReporter reporter("EGL"); +#ifdef MOZ_B2G + if (!sCurrentContext.init()) + MOZ_CRASH("Tls init failed"); +#endif + #ifdef XP_WIN #ifdef MOZ_WEBGL if (!mEGLLibrary) { diff --git a/gfx/gl/GLLibraryEGL.h b/gfx/gl/GLLibraryEGL.h index d34e2f67037..eaabf32681d 100644 --- a/gfx/gl/GLLibraryEGL.h +++ b/gfx/gl/GLLibraryEGL.h @@ -10,7 +10,7 @@ #endif #include "GLLibraryLoader.h" - +#include "mozilla/ThreadLocal.h" #include "nsIFile.h" #include @@ -529,6 +529,33 @@ public: static void AfterGLCall(const char* glFunction); #endif +#ifdef MOZ_B2G + EGLContext CachedCurrentContext() { + return sCurrentContext.get(); + } + void UnsetCachedCurrentContext() { + sCurrentContext.set(nullptr); + } + void SetCachedCurrentContext(EGLContext aCtx) { + sCurrentContext.set(aCtx); + } + bool CachedCurrentContextMatches() { + return sCurrentContext.get() == fGetCurrentContext(); + } + +private: + static ThreadLocal sCurrentContext; +public: + +#else + EGLContext CachedCurrentContext() { + return nullptr; + } + void UnsetCachedCurrentContext() {} + void SetCachedCurrentContext(EGLContext aCtx) { } + bool CachedCurrentContextMatches() { return true; } +#endif + private: bool mInitialized; PRLibrary* mEGLLibrary; diff --git a/gfx/layers/LayerTreeInvalidation.cpp b/gfx/layers/LayerTreeInvalidation.cpp index 470f29b4183..567171a9766 100644 --- a/gfx/layers/LayerTreeInvalidation.cpp +++ b/gfx/layers/LayerTreeInvalidation.cpp @@ -69,12 +69,12 @@ AddRegion(nsIntRegion& aDest, const nsIntRegion& aSource) aDest.SimplifyOutward(20); } + static nsIntRegion -TransformRegion(const nsIntRegion& aRegion, const gfx3DMatrix& aTransform) +TransformRegion(nsIntRegion& aRegion, const gfx3DMatrix& aTransform) { - nsIntRegion result; - AddTransformedRegion(result, aRegion, aTransform); - return result; + aRegion.Transform(aTransform); + return aRegion; } /** diff --git a/gfx/src/nsRegion.cpp b/gfx/src/nsRegion.cpp index 12daef16d30..e91a2877932 100644 --- a/gfx/src/nsRegion.cpp +++ b/gfx/src/nsRegion.cpp @@ -6,7 +6,8 @@ #include "nsRegion.h" #include "nsPrintfCString.h" #include "nsTArray.h" - +#include "gfx3DMatrix.h" +#include "gfxUtils.h" bool nsRegion::Contains(const nsRegion& aRgn) const { @@ -445,6 +446,44 @@ nsRegion& nsRegion::ScaleInverseRoundOut (float aXScale, float aYScale) return *this; } +static nsIntRect +TransformRect(const nsIntRect& aRect, const gfx3DMatrix& aTransform) +{ + if (aRect.IsEmpty()) { + return nsIntRect(); + } + + gfxRect rect(aRect.x, aRect.y, aRect.width, aRect.height); + rect = aTransform.TransformBounds(rect); + rect.RoundOut(); + + nsIntRect intRect; + if (!gfxUtils::GfxRectToIntRect(rect, &intRect)) { + return nsIntRect(); + } + + return intRect; +} + +nsRegion& nsRegion::Transform (const gfx3DMatrix &aTransform) +{ + int n; + pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n); + for (int i=0; i> 8) & 0xff; switch (block) { case 0x05: @@ -188,12 +188,15 @@ gfxAndroidPlatform::GetCommonFallbackFonts(const uint32_t aCh, aFontList.AppendElement("Droid Sans Arabic"); break; case 0x09: + aFontList.AppendElement("Noto Sans Devanagari"); aFontList.AppendElement("Droid Sans Devanagari"); break; case 0x0b: + aFontList.AppendElement("Noto Sans Tamil"); aFontList.AppendElement("Droid Sans Tamil"); break; case 0x0e: + aFontList.AppendElement("Noto Sans Thai"); aFontList.AppendElement("Droid Sans Thai"); break; case 0x10: case 0x2d: diff --git a/gfx/thebes/gfxDWriteFonts.cpp b/gfx/thebes/gfxDWriteFonts.cpp index 38c16bdd664..f188da3ca35 100644 --- a/gfx/thebes/gfxDWriteFonts.cpp +++ b/gfx/thebes/gfxDWriteFonts.cpp @@ -7,9 +7,7 @@ #include "mozilla/MemoryReporting.h" -#include "gfxHarfBuzzShaper.h" #include -#include "gfxGraphiteShaper.h" #include "gfxDWriteFontList.h" #include "gfxContext.h" #include @@ -106,14 +104,6 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry, } ComputeMetrics(anAAOption); - - if (FontCanSupportGraphite()) { - mGraphiteShaper = new gfxGraphiteShaper(this); - } - - if (FontCanSupportHarfBuzz()) { - mHarfBuzzShaper = new gfxHarfBuzzShaper(this); - } } gfxDWriteFont::~gfxDWriteFont() @@ -134,32 +124,6 @@ gfxDWriteFont::CopyWithAntialiasOption(AntialiasOption anAAOption) &mStyle, mNeedsBold, anAAOption); } -bool -gfxDWriteFont::ShapeText(gfxContext *aContext, - const char16_t *aText, - uint32_t aOffset, - uint32_t aLength, - int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping) -{ - bool ok = false; - - if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) { - ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength, - aScript, aShapedText); - } - - if (!ok && mHarfBuzzShaper) { - ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength, - aScript, aShapedText); - } - - PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText); - - return ok; -} - const gfxFont::Metrics& gfxDWriteFont::GetMetrics() { diff --git a/gfx/thebes/gfxDWriteFonts.h b/gfx/thebes/gfxDWriteFonts.h index 8ee972b553d..a2adea8ebf2 100644 --- a/gfx/thebes/gfxDWriteFonts.h +++ b/gfx/thebes/gfxDWriteFonts.h @@ -71,14 +71,6 @@ public: virtual cairo_scaled_font_t *GetCairoScaledFont(); protected: - virtual bool ShapeText(gfxContext *aContext, - const char16_t *aText, - uint32_t aOffset, - uint32_t aLength, - int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping = false); - bool GetFakeMetricsForArialBlack(DWRITE_FONT_METRICS *aFontMetrics); void ComputeMetrics(AntialiasOption anAAOption); diff --git a/gfx/thebes/gfxFT2Fonts.cpp b/gfx/thebes/gfxFT2Fonts.cpp index cb5088bcacd..fdc0e0e40cc 100644 --- a/gfx/thebes/gfxFT2Fonts.cpp +++ b/gfx/thebes/gfxFT2Fonts.cpp @@ -24,8 +24,6 @@ #include "gfxFT2Utils.h" #include "gfxFT2FontList.h" #include -#include "gfxHarfBuzzShaper.h" -#include "gfxGraphiteShaper.h" #include "nsGkAtoms.h" #include "nsTArray.h" #include "nsUnicodeRange.h" @@ -44,42 +42,20 @@ */ bool -gfxFT2Font::ShapeText(gfxContext *aContext, +gfxFT2Font::ShapeText(gfxContext *aContext, const char16_t *aText, - uint32_t aOffset, - uint32_t aLength, - int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping) + uint32_t aOffset, + uint32_t aLength, + int32_t aScript, + gfxShapedText *aShapedText) { - bool ok = false; - - if (FontCanSupportGraphite()) { - if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) { - if (!mGraphiteShaper) { - mGraphiteShaper = new gfxGraphiteShaper(this); - } - ok = mGraphiteShaper->ShapeText(aContext, aText, - aOffset, aLength, - aScript, aShapedText); - } - } - - if (!ok) { - if (!mHarfBuzzShaper) { - mHarfBuzzShaper = new gfxHarfBuzzShaper(this); - } - ok = mHarfBuzzShaper->ShapeText(aContext, aText, - aOffset, aLength, - aScript, aShapedText); - } - - if (!ok) { + if (!gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript, + aShapedText)) { + // harfbuzz must have failed(?!), just render raw glyphs AddRange(aText, aOffset, aLength, aShapedText); + PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText); } - PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText); - return true; } diff --git a/gfx/thebes/gfxFT2Fonts.h b/gfx/thebes/gfxFT2Fonts.h index 871b9397f9a..f0e390c5a60 100644 --- a/gfx/thebes/gfxFT2Fonts.h +++ b/gfx/thebes/gfxFT2Fonts.h @@ -77,8 +77,7 @@ protected: uint32_t aOffset, uint32_t aLength, int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping); + gfxShapedText *aShapedText); void FillGlyphDataForChar(uint32_t ch, CachedGlyphData *gd); diff --git a/gfx/thebes/gfxFont.cpp b/gfx/thebes/gfxFont.cpp index 024f3bee0a6..3d4c1b68a83 100644 --- a/gfx/thebes/gfxFont.cpp +++ b/gfx/thebes/gfxFont.cpp @@ -23,6 +23,7 @@ #include "gfxTypes.h" #include "gfxContext.h" #include "gfxFontMissingGlyphs.h" +#include "gfxGraphiteShaper.h" #include "gfxHarfBuzzShaper.h" #include "gfxUserFontSet.h" #include "gfxPlatformFontList.h" @@ -44,6 +45,8 @@ #include "gfxMathTable.h" #include "gfx2DGlue.h" +#include "GreekCasing.h" + #if defined(XP_MACOSX) #include "nsCocoaFeatures.h" #endif @@ -3942,8 +3945,7 @@ gfxFont::ShapeText(gfxContext *aContext, uint32_t aOffset, uint32_t aLength, int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping) + gfxShapedText *aShapedText) { nsDependentCSubstring ascii((const char*)aText, aLength); nsAutoString utf16; @@ -3952,7 +3954,7 @@ gfxFont::ShapeText(gfxContext *aContext, return false; } return ShapeText(aContext, utf16.BeginReading(), aOffset, aLength, - aScript, aShapedText, aPreferPlatformShaping); + aScript, aShapedText); } bool @@ -3961,30 +3963,29 @@ gfxFont::ShapeText(gfxContext *aContext, uint32_t aOffset, uint32_t aLength, int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping) + gfxShapedText *aShapedText) { bool ok = false; - if (mGraphiteShaper && gfxPlatform::GetPlatform()->UseGraphiteShaping()) { - ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength, - aScript, aShapedText); + if (FontCanSupportGraphite()) { + if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) { + if (!mGraphiteShaper) { + mGraphiteShaper = new gfxGraphiteShaper(this); + } + ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength, + aScript, aShapedText); + } } - if (!ok && mHarfBuzzShaper && !aPreferPlatformShaping) { + if (!ok) { + if (!mHarfBuzzShaper) { + mHarfBuzzShaper = new gfxHarfBuzzShaper(this); + } ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength, aScript, aShapedText); } - if (!ok) { - if (!mPlatformShaper) { - CreatePlatformShaper(); - } - if (mPlatformShaper) { - ok = mPlatformShaper->ShapeText(aContext, aText, aOffset, aLength, - aScript, aShapedText); - } - } + NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text"); PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText); @@ -5709,8 +5710,8 @@ gfxFont::InitFakeSmallCapsRun(gfxContext *aContext, // capitals where the accent is to be removed (bug 307039). // These are handled by using the full-size font with the // uppercasing transform. - GreekCasing::State state; - ch2 = GreekCasing::UpperCase(ch, state); + mozilla::GreekCasing::State state; + ch2 = mozilla::GreekCasing::UpperCase(ch, state); if (ch != ch2) { chCase = kSpecialUpper; } diff --git a/gfx/thebes/gfxFont.h b/gfx/thebes/gfxFont.h index cc9bde759ce..41f65b31be3 100644 --- a/gfx/thebes/gfxFont.h +++ b/gfx/thebes/gfxFont.h @@ -1470,12 +1470,12 @@ public: // Shape a piece of text and store the resulting glyph data into // aShapedText. Parameters aOffset/aLength indicate the range of // aShapedText to be updated; aLength is also the length of aText. - virtual bool ShapeText(gfxContext *aContext, + virtual bool ShapeText(gfxContext *aContext, const char16_t *aText, - uint32_t aOffset, - uint32_t aLength, - int32_t aScript, - gfxShapedText *aShapedText) = 0; + uint32_t aOffset, + uint32_t aLength, + int32_t aScript, + gfxShapedText *aShapedText) = 0; gfxFont *GetFont() const { return mFont; } @@ -1976,8 +1976,7 @@ protected: uint32_t aOffset, // dest offset in gfxShapedText uint32_t aLength, int32_t aScript, - gfxShapedText *aShapedText, // where to store the result - bool aPreferPlatformShaping = false); + gfxShapedText *aShapedText); // where to store the result // Call the appropriate shaper to generate glyphs for aText and store // them into aShapedText. @@ -1986,8 +1985,7 @@ protected: uint32_t aOffset, uint32_t aLength, int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping = false); + gfxShapedText *aShapedText); // Helper to adjust for synthetic bold and set character-type flags // in the shaped text; implementations of ShapeText should call this @@ -2146,19 +2144,14 @@ protected: // measurement by mathml code nsAutoPtr mNonAAFont; - // we may switch between these shapers on the fly, based on the script - // of the text run being shaped - nsAutoPtr mPlatformShaper; + // we create either or both of these shapers when needed, depending + // whether the font has graphite tables, and whether graphite shaping + // is actually enabled nsAutoPtr mHarfBuzzShaper; nsAutoPtr mGraphiteShaper; mozilla::RefPtr mAzureScaledFont; - // Create a default platform text shaper for this font. - // (TODO: This should become pure virtual once all font backends have - // been updated.) - virtual void CreatePlatformShaper() { } - // Helper for subclasses that want to initialize standard metrics from the // tables of sfnt (TrueType/OpenType) fonts. // This will use mFUnitsConvFactor if it is already set, else compute it diff --git a/gfx/thebes/gfxGDIFont.cpp b/gfx/thebes/gfxGDIFont.cpp index a410ff974bd..926fbb536d6 100644 --- a/gfx/thebes/gfxGDIFont.cpp +++ b/gfx/thebes/gfxGDIFont.cpp @@ -8,9 +8,7 @@ #include "mozilla/MemoryReporting.h" #include "mozilla/WindowsVersion.h" -#include "gfxHarfBuzzShaper.h" #include -#include "gfxGraphiteShaper.h" #include "gfxWindowsPlatform.h" #include "gfxContext.h" #include "mozilla/Preferences.h" @@ -51,10 +49,6 @@ gfxGDIFont::gfxGDIFont(GDIFontEntry *aFontEntry, mSpaceGlyph(0), mNeedsBold(aNeedsBold) { - if (FontCanSupportGraphite()) { - mGraphiteShaper = new gfxGraphiteShaper(this); - } - mHarfBuzzShaper = new gfxHarfBuzzShaper(this); } gfxGDIFont::~gfxGDIFont() @@ -79,13 +73,12 @@ gfxGDIFont::CopyWithAntialiasOption(AntialiasOption anAAOption) } bool -gfxGDIFont::ShapeText(gfxContext *aContext, +gfxGDIFont::ShapeText(gfxContext *aContext, const char16_t *aText, - uint32_t aOffset, - uint32_t aLength, - int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping) + uint32_t aOffset, + uint32_t aLength, + int32_t aScript, + gfxShapedText *aShapedText) { if (!mMetrics) { Initialize(); @@ -104,7 +97,7 @@ gfxGDIFont::ShapeText(gfxContext *aContext, } return gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript, - aShapedText, aPreferPlatformShaping); + aShapedText); } const gfxFont::Metrics& diff --git a/gfx/thebes/gfxGDIFont.h b/gfx/thebes/gfxGDIFont.h index 3b979c41387..13839599b1c 100644 --- a/gfx/thebes/gfxGDIFont.h +++ b/gfx/thebes/gfxGDIFont.h @@ -71,13 +71,12 @@ public: protected: /* override to ensure the cairo font is set up properly */ - virtual bool ShapeText(gfxContext *aContext, + virtual bool ShapeText(gfxContext *aContext, const char16_t *aText, - uint32_t aOffset, - uint32_t aLength, - int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping); + uint32_t aOffset, + uint32_t aLength, + int32_t aScript, + gfxShapedText *aShapedText); void Initialize(); // creates metrics and Cairo fonts diff --git a/gfx/thebes/gfxMacFont.cpp b/gfx/thebes/gfxMacFont.cpp index 3b7f752c5db..23e8301534e 100644 --- a/gfx/thebes/gfxMacFont.cpp +++ b/gfx/thebes/gfxMacFont.cpp @@ -8,9 +8,7 @@ #include "mozilla/MemoryReporting.h" #include "gfxCoreTextShaper.h" -#include "gfxHarfBuzzShaper.h" #include -#include "gfxGraphiteShaper.h" #include "gfxPlatformMac.h" #include "gfxContext.h" #include "gfxFontUtils.h" @@ -106,13 +104,6 @@ gfxMacFont::gfxMacFont(MacOSFontEntry *aFontEntry, const gfxFontStyle *aFontStyl NS_WARNING(warnBuf); #endif } - - if (FontCanSupportGraphite()) { - mGraphiteShaper = new gfxGraphiteShaper(this); - } - if (FontCanSupportHarfBuzz()) { - mHarfBuzzShaper = new gfxHarfBuzzShaper(this); - } } gfxMacFont::~gfxMacFont() @@ -126,29 +117,31 @@ gfxMacFont::~gfxMacFont() } bool -gfxMacFont::ShapeText(gfxContext *aContext, +gfxMacFont::ShapeText(gfxContext *aContext, const char16_t *aText, - uint32_t aOffset, - uint32_t aLength, - int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping) + uint32_t aOffset, + uint32_t aLength, + int32_t aScript, + gfxShapedText *aShapedText) { if (!mIsValid) { NS_WARNING("invalid font! expect incorrect text rendering"); return false; } - bool requiresAAT = - static_cast(GetFontEntry())->RequiresAATLayout(); - return gfxFont::ShapeText(aContext, aText, aOffset, aLength, - aScript, aShapedText, requiresAAT); -} + if (static_cast(GetFontEntry())->RequiresAATLayout()) { + if (!mCoreTextShaper) { + mCoreTextShaper = new gfxCoreTextShaper(this); + } + if (mCoreTextShaper->ShapeText(aContext, aText, aOffset, aLength, + aScript, aShapedText)) { + PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText); + return true; + } + } -void -gfxMacFont::CreatePlatformShaper() -{ - mPlatformShaper = new gfxCoreTextShaper(this); + return gfxFont::ShapeText(aContext, aText, aOffset, aLength, aScript, + aShapedText); } bool diff --git a/gfx/thebes/gfxMacFont.h b/gfx/thebes/gfxMacFont.h index 0e88c5d0f06..3942213c039 100644 --- a/gfx/thebes/gfxMacFont.h +++ b/gfx/thebes/gfxMacFont.h @@ -51,16 +51,13 @@ public: virtual FontType GetType() const { return FONT_TYPE_MAC; } protected: - virtual void CreatePlatformShaper(); - // override to prefer CoreText shaping with fonts that depend on AAT - virtual bool ShapeText(gfxContext *aContext, + virtual bool ShapeText(gfxContext *aContext, const char16_t *aText, - uint32_t aOffset, - uint32_t aLength, - int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping = false); + uint32_t aOffset, + uint32_t aLength, + int32_t aScript, + gfxShapedText *aShapedText); void InitMetrics(); void InitMetricsFromPlatform(); @@ -76,6 +73,8 @@ protected: cairo_font_face_t *mFontFace; + nsAutoPtr mCoreTextShaper; + Metrics mMetrics; uint32_t mSpaceGlyph; }; diff --git a/gfx/thebes/gfxPangoFonts.cpp b/gfx/thebes/gfxPangoFonts.cpp index abfc5e7c71e..2dbd0c233d8 100644 --- a/gfx/thebes/gfxPangoFonts.cpp +++ b/gfx/thebes/gfxPangoFonts.cpp @@ -20,8 +20,6 @@ #include "gfxFT2Utils.h" #include "harfbuzz/hb.h" #include "harfbuzz/hb-ot.h" -#include "gfxHarfBuzzShaper.h" -#include "gfxGraphiteShaper.h" #include "nsUnicodeProperties.h" #include "nsUnicodeScriptCodes.h" #include "gfxFontconfigUtils.h" @@ -663,14 +661,6 @@ public: protected: virtual already_AddRefed GetSmallCapsFont(); - virtual bool ShapeText(gfxContext *aContext, - const char16_t *aText, - uint32_t aOffset, - uint32_t aLength, - int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping); - private: gfxFcFont(cairo_scaled_font_t *aCairoFont, gfxFcFontEntry *aFontEntry, const gfxFontStyle *aFontStyle); @@ -1632,42 +1622,6 @@ gfxFcFont::GetSmallCapsFont() return font.forget(); } -bool -gfxFcFont::ShapeText(gfxContext *aContext, - const char16_t *aText, - uint32_t aOffset, - uint32_t aLength, - int32_t aScript, - gfxShapedText *aShapedText, - bool aPreferPlatformShaping) -{ - bool ok = false; - - if (FontCanSupportGraphite()) { - if (gfxPlatform::GetPlatform()->UseGraphiteShaping()) { - if (!mGraphiteShaper) { - mGraphiteShaper = new gfxGraphiteShaper(this); - } - ok = mGraphiteShaper->ShapeText(aContext, aText, aOffset, aLength, - aScript, aShapedText); - } - } - - if (!ok) { - if (!mHarfBuzzShaper) { - mHarfBuzzShaper = new gfxHarfBuzzShaper(this); - } - ok = mHarfBuzzShaper->ShapeText(aContext, aText, aOffset, aLength, - aScript, aShapedText); - } - - NS_WARN_IF_FALSE(ok, "shaper failed, expect scrambled or missing text"); - - PostShapingFixup(aContext, aText, aOffset, aLength, aShapedText); - - return ok; -} - /* static */ void gfxPangoFontGroup::Shutdown() { diff --git a/gfx/thebes/gfxUtils.cpp b/gfx/thebes/gfxUtils.cpp index 8b189d4b479..dcf30752181 100644 --- a/gfx/thebes/gfxUtils.cpp +++ b/gfx/thebes/gfxUtils.cpp @@ -724,6 +724,9 @@ gfxUtils::TransformRectToRect(const gfxRect& aFrom, const IntPoint& aToTopLeft, return m; } +/* This function is sort of shitty. We truncate doubles + * to ints then convert those ints back to doubles to make sure that + * they equal the doubles that we got in. */ bool gfxUtils::GfxRectToIntRect(const gfxRect& aIn, nsIntRect* aOut) { diff --git a/intl/unicharutil/util/GreekCasing.cpp b/intl/unicharutil/util/GreekCasing.cpp new file mode 100644 index 00000000000..5c805c27566 --- /dev/null +++ b/intl/unicharutil/util/GreekCasing.cpp @@ -0,0 +1,268 @@ +/* -*- 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 "GreekCasing.h" +#include "nsUnicharUtils.h" + +// Custom uppercase mapping for Greek; see bug 307039 for details +#define GREEK_LOWER_ALPHA 0x03B1 +#define GREEK_LOWER_ALPHA_TONOS 0x03AC +#define GREEK_LOWER_ALPHA_OXIA 0x1F71 +#define GREEK_LOWER_EPSILON 0x03B5 +#define GREEK_LOWER_EPSILON_TONOS 0x03AD +#define GREEK_LOWER_EPSILON_OXIA 0x1F73 +#define GREEK_LOWER_ETA 0x03B7 +#define GREEK_LOWER_ETA_TONOS 0x03AE +#define GREEK_LOWER_ETA_OXIA 0x1F75 +#define GREEK_LOWER_IOTA 0x03B9 +#define GREEK_LOWER_IOTA_TONOS 0x03AF +#define GREEK_LOWER_IOTA_OXIA 0x1F77 +#define GREEK_LOWER_IOTA_DIALYTIKA 0x03CA +#define GREEK_LOWER_IOTA_DIALYTIKA_TONOS 0x0390 +#define GREEK_LOWER_IOTA_DIALYTIKA_OXIA 0x1FD3 +#define GREEK_LOWER_OMICRON 0x03BF +#define GREEK_LOWER_OMICRON_TONOS 0x03CC +#define GREEK_LOWER_OMICRON_OXIA 0x1F79 +#define GREEK_LOWER_UPSILON 0x03C5 +#define GREEK_LOWER_UPSILON_TONOS 0x03CD +#define GREEK_LOWER_UPSILON_OXIA 0x1F7B +#define GREEK_LOWER_UPSILON_DIALYTIKA 0x03CB +#define GREEK_LOWER_UPSILON_DIALYTIKA_TONOS 0x03B0 +#define GREEK_LOWER_UPSILON_DIALYTIKA_OXIA 0x1FE3 +#define GREEK_LOWER_OMEGA 0x03C9 +#define GREEK_LOWER_OMEGA_TONOS 0x03CE +#define GREEK_LOWER_OMEGA_OXIA 0x1F7D +#define GREEK_UPPER_ALPHA 0x0391 +#define GREEK_UPPER_EPSILON 0x0395 +#define GREEK_UPPER_ETA 0x0397 +#define GREEK_UPPER_IOTA 0x0399 +#define GREEK_UPPER_IOTA_DIALYTIKA 0x03AA +#define GREEK_UPPER_OMICRON 0x039F +#define GREEK_UPPER_UPSILON 0x03A5 +#define GREEK_UPPER_UPSILON_DIALYTIKA 0x03AB +#define GREEK_UPPER_OMEGA 0x03A9 +#define GREEK_UPPER_ALPHA_TONOS 0x0386 +#define GREEK_UPPER_ALPHA_OXIA 0x1FBB +#define GREEK_UPPER_EPSILON_TONOS 0x0388 +#define GREEK_UPPER_EPSILON_OXIA 0x1FC9 +#define GREEK_UPPER_ETA_TONOS 0x0389 +#define GREEK_UPPER_ETA_OXIA 0x1FCB +#define GREEK_UPPER_IOTA_TONOS 0x038A +#define GREEK_UPPER_IOTA_OXIA 0x1FDB +#define GREEK_UPPER_OMICRON_TONOS 0x038C +#define GREEK_UPPER_OMICRON_OXIA 0x1FF9 +#define GREEK_UPPER_UPSILON_TONOS 0x038E +#define GREEK_UPPER_UPSILON_OXIA 0x1FEB +#define GREEK_UPPER_OMEGA_TONOS 0x038F +#define GREEK_UPPER_OMEGA_OXIA 0x1FFB +#define COMBINING_ACUTE_ACCENT 0x0301 +#define COMBINING_DIAERESIS 0x0308 +#define COMBINING_ACUTE_TONE_MARK 0x0341 +#define COMBINING_GREEK_DIALYTIKA_TONOS 0x0344 + +namespace mozilla { + +uint32_t +GreekCasing::UpperCase(uint32_t aCh, GreekCasing::State& aState) +{ + switch (aCh) { + case GREEK_UPPER_ALPHA: + case GREEK_LOWER_ALPHA: + aState = kAlpha; + return GREEK_UPPER_ALPHA; + + case GREEK_UPPER_EPSILON: + case GREEK_LOWER_EPSILON: + aState = kEpsilon; + return GREEK_UPPER_EPSILON; + + case GREEK_UPPER_ETA: + case GREEK_LOWER_ETA: + aState = kEta; + return GREEK_UPPER_ETA; + + case GREEK_UPPER_IOTA: + aState = kIota; + return GREEK_UPPER_IOTA; + + case GREEK_UPPER_OMICRON: + case GREEK_LOWER_OMICRON: + aState = kOmicron; + return GREEK_UPPER_OMICRON; + + case GREEK_UPPER_UPSILON: + switch (aState) { + case kOmicron: + aState = kOmicronUpsilon; + break; + default: + aState = kUpsilon; + break; + } + return GREEK_UPPER_UPSILON; + + case GREEK_UPPER_OMEGA: + case GREEK_LOWER_OMEGA: + aState = kOmega; + return GREEK_UPPER_OMEGA; + + // iota and upsilon may be the second vowel of a diphthong + case GREEK_LOWER_IOTA: + switch (aState) { + case kAlphaAcc: + case kEpsilonAcc: + case kOmicronAcc: + case kUpsilonAcc: + aState = kStart; + return GREEK_UPPER_IOTA_DIALYTIKA; + default: + break; + } + aState = kIota; + return GREEK_UPPER_IOTA; + + case GREEK_LOWER_UPSILON: + switch (aState) { + case kAlphaAcc: + case kEpsilonAcc: + case kEtaAcc: + case kOmicronAcc: + aState = kStart; + return GREEK_UPPER_UPSILON_DIALYTIKA; + case kOmicron: + aState = kOmicronUpsilon; + break; + default: + aState = kUpsilon; + break; + } + return GREEK_UPPER_UPSILON; + + case GREEK_UPPER_IOTA_DIALYTIKA: + case GREEK_LOWER_IOTA_DIALYTIKA: + case GREEK_UPPER_UPSILON_DIALYTIKA: + case GREEK_LOWER_UPSILON_DIALYTIKA: + case COMBINING_DIAERESIS: + aState = kDiaeresis; + return ToUpperCase(aCh); + + // remove accent if it follows a vowel or diaeresis, + // and set appropriate state for diphthong detection + case COMBINING_ACUTE_ACCENT: + case COMBINING_ACUTE_TONE_MARK: + switch (aState) { + case kAlpha: + aState = kAlphaAcc; + return uint32_t(-1); // omit this char from result string + case kEpsilon: + aState = kEpsilonAcc; + return uint32_t(-1); + case kEta: + aState = kEtaAcc; + return uint32_t(-1); + case kIota: + aState = kIotaAcc; + return uint32_t(-1); + case kOmicron: + aState = kOmicronAcc; + return uint32_t(-1); + case kUpsilon: + aState = kUpsilonAcc; + return uint32_t(-1); + case kOmicronUpsilon: + aState = kStart; // this completed a diphthong + return uint32_t(-1); + case kOmega: + aState = kOmegaAcc; + return uint32_t(-1); + case kDiaeresis: + aState = kStart; + return uint32_t(-1); + default: + break; + } + break; + + // combinations with dieresis+accent just strip the accent, + // and reset to start state (don't form diphthong with following vowel) + case GREEK_LOWER_IOTA_DIALYTIKA_TONOS: + case GREEK_LOWER_IOTA_DIALYTIKA_OXIA: + aState = kStart; + return GREEK_UPPER_IOTA_DIALYTIKA; + + case GREEK_LOWER_UPSILON_DIALYTIKA_TONOS: + case GREEK_LOWER_UPSILON_DIALYTIKA_OXIA: + aState = kStart; + return GREEK_UPPER_UPSILON_DIALYTIKA; + + case COMBINING_GREEK_DIALYTIKA_TONOS: + aState = kStart; + return COMBINING_DIAERESIS; + + // strip accents from vowels, and note the vowel seen so that we can detect + // diphthongs where diaeresis needs to be added + case GREEK_LOWER_ALPHA_TONOS: + case GREEK_LOWER_ALPHA_OXIA: + case GREEK_UPPER_ALPHA_TONOS: + case GREEK_UPPER_ALPHA_OXIA: + aState = kAlphaAcc; + return GREEK_UPPER_ALPHA; + + case GREEK_LOWER_EPSILON_TONOS: + case GREEK_LOWER_EPSILON_OXIA: + case GREEK_UPPER_EPSILON_TONOS: + case GREEK_UPPER_EPSILON_OXIA: + aState = kEpsilonAcc; + return GREEK_UPPER_EPSILON; + + case GREEK_LOWER_ETA_TONOS: + case GREEK_LOWER_ETA_OXIA: + case GREEK_UPPER_ETA_TONOS: + case GREEK_UPPER_ETA_OXIA: + aState = kEtaAcc; + return GREEK_UPPER_ETA; + + case GREEK_LOWER_IOTA_TONOS: + case GREEK_LOWER_IOTA_OXIA: + case GREEK_UPPER_IOTA_TONOS: + case GREEK_UPPER_IOTA_OXIA: + aState = kIotaAcc; + return GREEK_UPPER_IOTA; + + case GREEK_LOWER_OMICRON_TONOS: + case GREEK_LOWER_OMICRON_OXIA: + case GREEK_UPPER_OMICRON_TONOS: + case GREEK_UPPER_OMICRON_OXIA: + aState = kOmicronAcc; + return GREEK_UPPER_OMICRON; + + case GREEK_LOWER_UPSILON_TONOS: + case GREEK_LOWER_UPSILON_OXIA: + case GREEK_UPPER_UPSILON_TONOS: + case GREEK_UPPER_UPSILON_OXIA: + switch (aState) { + case kOmicron: + aState = kStart; // this completed a diphthong + break; + default: + aState = kUpsilonAcc; + break; + } + return GREEK_UPPER_UPSILON; + + case GREEK_LOWER_OMEGA_TONOS: + case GREEK_LOWER_OMEGA_OXIA: + case GREEK_UPPER_OMEGA_TONOS: + case GREEK_UPPER_OMEGA_OXIA: + aState = kOmegaAcc; + return GREEK_UPPER_OMEGA; + } + + // all other characters just reset the state, and use standard mappings + aState = kStart; + return ToUpperCase(aCh); +} + +} // namespace mozilla diff --git a/intl/unicharutil/util/GreekCasing.h b/intl/unicharutil/util/GreekCasing.h new file mode 100644 index 00000000000..5a25c789849 --- /dev/null +++ b/intl/unicharutil/util/GreekCasing.h @@ -0,0 +1,72 @@ +/* -*- 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 GreekCasing_h_ +#define GreekCasing_h_ + +#include + +namespace mozilla { + +class GreekCasing { + // When doing an Uppercase transform in Greek, we need to keep track of the + // current state while iterating through the string, to recognize and process + // diphthongs correctly. For clarity, we define a state for each vowel and + // each vowel with accent, although a few of these do not actually need any + // special treatment and could be folded into kStart. +private: + enum GreekStates { + kStart, + kAlpha, + kEpsilon, + kEta, + kIota, + kOmicron, + kUpsilon, + kOmega, + kAlphaAcc, + kEpsilonAcc, + kEtaAcc, + kIotaAcc, + kOmicronAcc, + kUpsilonAcc, + kOmegaAcc, + kOmicronUpsilon, + kDiaeresis + }; + +public: + class State { + public: + State() + : mState(kStart) + { + } + + State(const GreekStates& aState) + : mState(aState) + { + } + + void Reset() + { + mState = kStart; + } + + operator GreekStates() const + { + return mState; + } + + private: + GreekStates mState; + }; + + static uint32_t UpperCase(uint32_t aCh, State& aState); +}; + +} // namespace mozilla + +#endif diff --git a/intl/unicharutil/util/IrishCasing.cpp b/intl/unicharutil/util/IrishCasing.cpp new file mode 100644 index 00000000000..6c66ff51710 --- /dev/null +++ b/intl/unicharutil/util/IrishCasing.cpp @@ -0,0 +1,246 @@ +/* -*- 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/. */ + +/****************************************************************************** + +This file provides a finite state machine to support Irish Gaelic uppercasing +rules. + +The caller will need to iterate through a string, passing a State variable +along with the current character to each UpperCase call and checking the flags +that are returned: + + If aMarkPos is true, caller must remember the current index in the string as + a possible target for a future action. + + If aAction is non-zero, then one or more characters from the marked index are + to be modified: + 1 lowercase the marked letter + 2 lowercase the marked letter and its successor + 3 lowercase the marked letter, and delete its successor + + +### Rules from https://bugzilla.mozilla.org/show_bug.cgi?id=1014639, +### comments 1 and 4: + +v = [a,á,e,é,i,í,o,ó,u,ú] +V = [A,Á,E,É,I,Í,O,Ó,U,Ú] + +bhf -> bhF +bhF -> bhF +bp -> bP +bP -> bP +dt -> dT +dT -> dT +gc -> gC +gC -> gC +h{V} -> h{V} +mb -> mB +mB -> mB +n-{v} -> n{V} +n{V} -> n{V} +nd -> nD +nD -> nD +ng -> nG +nG -> nG +t-{v} -> t{V} +t{V} -> t{V} +ts{v} -> tS{V} +tS{v} -> tS{V} +tS{V} -> tS{V} +tsl -> tSL +tSl -> tSL +tSL -> tSL +tsn -> tSN +tSn -> tSN +tSN -> tSN +tsr -> tSR +tSr -> tSR +tSR -> tSR + +### Create table of states and actions for each input class. + +Start (non-word) state is #; generic in-word state is _, once we know there's +no special action to do in this word. + + # _ b bh d g h m n n- t t- ts +input\state +b b' _ _ _ _ _ _ 1 _ _ _ _ _ +B _ _ _ _ _ _ _ 1 _ _ _ _ _ +c _ _ _ _ _ 1 _ _ _ _ _ _ _ +C _ _ _ _ _ 1 _ _ _ _ _ _ _ +d d' _ _ _ _ _ _ _ 1 _ _ _ _ +D _ _ _ _ _ _ _ _ 1 _ _ _ _ +f _ _ _ 2 _ _ _ _ _ _ _ _ _ +F _ _ _ 2 _ _ _ _ _ _ _ _ _ +g g' _ _ _ _ _ _ _ 1 _ _ _ _ +G _ _ _ _ _ _ _ _ 1 _ _ _ _ +h h' _ bh _ _ _ _ _ _ _ _ _ _ +l _ _ _ _ _ _ _ _ _ _ _ _ 1 +L _ _ _ _ _ _ _ _ _ _ _ _ 1 +m m' _ _ _ _ _ _ _ _ _ _ _ _ +n n' _ _ _ _ _ _ _ _ _ _ _ 1 +N _ _ _ _ _ _ _ _ _ _ _ _ 1 +p _ _ 1 _ _ _ _ _ _ _ _ _ _ +P _ _ 1 _ _ _ _ _ _ _ _ _ _ +r _ _ _ _ _ _ _ _ _ _ _ _ 1 +R _ _ _ _ _ _ _ _ _ _ _ _ 1 +s _ _ _ _ _ _ _ _ _ _ ts _ _ +S _ _ _ _ _ _ _ _ _ _ ts _ _ +t t' _ _ _ 1 _ _ _ _ _ _ _ _ +T _ _ _ _ 1 _ _ _ _ _ _ _ _ +vowel _ _ _ _ _ _ _ _ _ 1d _ 1d 1 +Vowel _ _ _ _ _ _ 1 _ 1 _ 1 _ 1 +hyph _ _ _ _ _ _ _ _ n- _ t- _ _ +letter _ _ _ _ _ _ _ _ _ _ _ _ _ +other # # # # # # # # # # # # # + +Actions: + 1 lowercase one letter at start of word + 2 lowercase two letters at start of word + 1d lowercase one letter at start of word, and delete next + (and then go to state _, nothing further to do in this word) + +else just go to the given state; suffix ' indicates mark start-of-word. + +### Consolidate identical states and classes: + + 0 1 2 3 4 5 6 7 8 9 A B + # _ b bh d g h m n [nt]- t ts +input\state +b b' _ _ _ _ _ _ 1 _ _ _ _ +B _ _ _ _ _ _ _ 1 _ _ _ _ +[cC] _ _ _ _ _ 1 _ _ _ _ _ _ +d d' _ _ _ _ _ _ _ 1 _ _ _ +[DG] _ _ _ _ _ _ _ _ 1 _ _ _ +[fF] _ _ _ 2 _ _ _ _ _ _ _ _ +g g' _ _ _ _ _ _ _ 1 _ _ _ +h h' _ bh _ _ _ _ _ _ _ _ _ +[lLNrR] _ _ _ _ _ _ _ _ _ _ _ 1 +m m' _ _ _ _ _ _ _ _ _ _ _ +n n' _ _ _ _ _ _ _ _ _ _ 1 +[pP] _ _ 1 _ _ _ _ _ _ _ _ _ +[sS] _ _ _ _ _ _ _ _ _ _ ts _ +t t' _ _ _ 1 _ _ _ _ _ _ _ +T _ _ _ _ 1 _ _ _ _ _ _ _ +vowel _ _ _ _ _ _ _ _ _ 1d _ 1 +Vowel _ _ _ _ _ _ 1 _ 1 _ 1 1 +hyph _ _ _ _ _ _ _ _ [nt-] _ [nt-] _ +letter _ _ _ _ _ _ _ _ _ _ _ _ +other # # # # # # # # # # # # + +So we have 20 input classes, and 12 states. + +State table array will contain bytes that encode action and new state: + + 0x80 - bit flag: mark start-of-word position + 0x40 - currently unused + 0x30 - action mask: 4 values + 0x00 - do nothing + 0x10 - lowercase one letter + 0x20 - lowercase two letters + 0x30 - lowercase one, delete one + 0x0F - next-state mask +******************************************************************************/ + +#include "IrishCasing.h" + +#include "nsUnicodeProperties.h" +#include "nsUnicharUtils.h" + +namespace mozilla { + +const uint8_t +IrishCasing::sUppercaseStateTable[kNumClasses][kNumStates] = { +// # _ b bh d g h m n [nt]- t ts + { 0x82, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, 0x01, 0x01, 0x01, 0x01 }, // b + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, 0x01, 0x01, 0x01, 0x01 }, // B + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x10, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // [cC] + { 0x84, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, 0x01, 0x01, 0x01 }, // d + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, 0x01, 0x01, 0x01 }, // [DG] + { 0x01, 0x01, 0x01, 0x21, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // [fF] + { 0x85, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, 0x01, 0x01, 0x01 }, // g + { 0x86, 0x01, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // h + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11 }, // [lLNrR] + { 0x87, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // m + { 0x88, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11 }, // n + { 0x01, 0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // [pP] + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x0B, 0x01 }, // [sS] + { 0x8A, 0x01, 0x01, 0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // t + { 0x01, 0x01, 0x01, 0x01, 0x11, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // T + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x31, 0x01, 0x11 }, // vowel + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x11, 0x01, 0x11, 0x01, 0x11, 0x11 }, // Vowel + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x09, 0x01, 0x09, 0x01 }, // hyph + { 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01 }, // letter + { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 } // other +}; + +#define HYPHEN 0x2010 +#define NO_BREAK_HYPHEN 0x2011 +#define a_ACUTE 0x00e1 +#define e_ACUTE 0x00e9 +#define i_ACUTE 0x00ed +#define o_ACUTE 0x00f3 +#define u_ACUTE 0x00fa +#define A_ACUTE 0x00c1 +#define E_ACUTE 0x00c9 +#define I_ACUTE 0x00cd +#define O_ACUTE 0x00d3 +#define U_ACUTE 0x00da + +const uint8_t IrishCasing::sLcClasses[26] = { + kClass_vowel, kClass_b, kClass_cC, kClass_d, kClass_vowel, + kClass_fF, kClass_g, kClass_h, kClass_vowel, kClass_letter, + kClass_letter, kClass_lLNrR, kClass_m, kClass_n, kClass_vowel, + kClass_pP, kClass_letter, kClass_lLNrR, kClass_sS, kClass_t, + kClass_vowel, kClass_letter, kClass_letter, kClass_letter, kClass_letter, + kClass_letter +}; + +const uint8_t IrishCasing::sUcClasses[26] = { + kClass_Vowel, kClass_B, kClass_cC, kClass_DG, kClass_Vowel, + kClass_fF, kClass_DG, kClass_letter, kClass_Vowel, kClass_letter, + kClass_letter, kClass_lLNrR, kClass_letter, kClass_lLNrR, kClass_Vowel, + kClass_pP, kClass_letter, kClass_lLNrR, kClass_sS, kClass_T, + kClass_Vowel, kClass_letter, kClass_letter, kClass_letter, kClass_letter, + kClass_letter +}; + +uint32_t +IrishCasing::UpperCase(uint32_t aCh, State& aState, + bool& aMarkPos, uint8_t& aAction) +{ + using mozilla::unicode::GetGenCategory; + uint8_t cls; + + if (aCh >= 'a' && aCh <= 'z') { + cls = sLcClasses[aCh - 'a']; + } else if (aCh >= 'A' && aCh <= 'Z') { + cls = sUcClasses[aCh - 'A']; + } else if (GetGenCategory(aCh) == nsIUGenCategory::kLetter) { + if (aCh == a_ACUTE || aCh == e_ACUTE || aCh == i_ACUTE || + aCh == o_ACUTE || aCh == u_ACUTE) { + cls = kClass_vowel; + } else if (aCh == A_ACUTE || aCh == E_ACUTE || aCh == I_ACUTE || + aCh == O_ACUTE || aCh == U_ACUTE) { + cls = kClass_Vowel; + } else { + cls = kClass_letter; + } + } else if (aCh == '-' || aCh == HYPHEN || aCh == NO_BREAK_HYPHEN) { + cls = kClass_hyph; + } else { + cls = kClass_other; + } + + uint8_t stateEntry = sUppercaseStateTable[cls][aState]; + aMarkPos = !!(stateEntry & kMarkPositionFlag); + aAction = (stateEntry & kActionMask) >> kActionShift; + aState = (stateEntry & kNextStateMask); + + return ToUpperCase(aCh); +} + +} // namespace mozilla diff --git a/intl/unicharutil/util/IrishCasing.h b/intl/unicharutil/util/IrishCasing.h new file mode 100644 index 00000000000..a4b2edc2920 --- /dev/null +++ b/intl/unicharutil/util/IrishCasing.h @@ -0,0 +1,108 @@ +/* -*- 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 IrishCasing_h_ +#define IrishCasing_h_ + +#include + +namespace mozilla { + +class IrishCasing { +private: + enum IrishStates { + kState_Start, + kState_InWord, + kState_b, + kState_bh, + kState_d, + kState_g, + kState_h, + kState_m, + kState_n, + kState_nt_, + kState_t, + kState_ts, + kNumStates + }; + + enum IrishClasses { + kClass_b, + kClass_B, + kClass_cC, + kClass_d, + kClass_DG, + kClass_fF, + kClass_g, + kClass_h, + kClass_lLNrR, + kClass_m, + kClass_n, + kClass_pP, + kClass_sS, + kClass_t, + kClass_T, + kClass_vowel, + kClass_Vowel, + kClass_hyph, + kClass_letter, + kClass_other, + kNumClasses + }; + +public: + class State { + friend class IrishCasing; + + public: + State() + : mState(kState_Start) + { + } + + State(const IrishStates& aState) + : mState(aState) + { + } + + void Reset() + { + mState = kState_Start; + } + + operator IrishStates() const + { + return mState; + } + + private: + State(uint8_t aState) + : mState(IrishStates(aState)) + { + } + + uint8_t GetClass(uint32_t aCh); + + IrishStates mState; + }; + + enum { + kMarkPositionFlag = 0x80, + kActionMask = 0x30, + kActionShift = 4, + kNextStateMask = 0x0f + }; + + static const uint8_t sUppercaseStateTable[kNumClasses][kNumStates]; + static const uint8_t sLcClasses[26]; + static const uint8_t sUcClasses[26]; + + static uint32_t UpperCase(uint32_t aCh, State& aState, + bool& aMarkPos, uint8_t& aAction); +}; + +} // namespace mozilla + +#endif diff --git a/intl/unicharutil/util/moz.build b/intl/unicharutil/util/moz.build index 508c53f7667..3444abce2f2 100644 --- a/intl/unicharutil/util/moz.build +++ b/intl/unicharutil/util/moz.build @@ -7,7 +7,9 @@ DIRS += ['internal'] EXPORTS += [ + 'GreekCasing.h', 'ICUUtils.h', + 'IrishCasing.h', 'nsBidiUtils.h', 'nsSpecialCasingData.h', 'nsUnicharUtils.h', diff --git a/intl/unicharutil/util/nsUnicharUtils.cpp b/intl/unicharutil/util/nsUnicharUtils.cpp index 420797fc9b0..f82902f0fbf 100644 --- a/intl/unicharutil/util/nsUnicharUtils.cpp +++ b/intl/unicharutil/util/nsUnicharUtils.cpp @@ -214,263 +214,6 @@ ToTitleCase(uint32_t aChar) return mozilla::unicode::GetTitlecaseForLower(aChar); } -// Custom uppercase mapping for Greek; see bug 307039 for details -#define GREEK_LOWER_ALPHA 0x03B1 -#define GREEK_LOWER_ALPHA_TONOS 0x03AC -#define GREEK_LOWER_ALPHA_OXIA 0x1F71 -#define GREEK_LOWER_EPSILON 0x03B5 -#define GREEK_LOWER_EPSILON_TONOS 0x03AD -#define GREEK_LOWER_EPSILON_OXIA 0x1F73 -#define GREEK_LOWER_ETA 0x03B7 -#define GREEK_LOWER_ETA_TONOS 0x03AE -#define GREEK_LOWER_ETA_OXIA 0x1F75 -#define GREEK_LOWER_IOTA 0x03B9 -#define GREEK_LOWER_IOTA_TONOS 0x03AF -#define GREEK_LOWER_IOTA_OXIA 0x1F77 -#define GREEK_LOWER_IOTA_DIALYTIKA 0x03CA -#define GREEK_LOWER_IOTA_DIALYTIKA_TONOS 0x0390 -#define GREEK_LOWER_IOTA_DIALYTIKA_OXIA 0x1FD3 -#define GREEK_LOWER_OMICRON 0x03BF -#define GREEK_LOWER_OMICRON_TONOS 0x03CC -#define GREEK_LOWER_OMICRON_OXIA 0x1F79 -#define GREEK_LOWER_UPSILON 0x03C5 -#define GREEK_LOWER_UPSILON_TONOS 0x03CD -#define GREEK_LOWER_UPSILON_OXIA 0x1F7B -#define GREEK_LOWER_UPSILON_DIALYTIKA 0x03CB -#define GREEK_LOWER_UPSILON_DIALYTIKA_TONOS 0x03B0 -#define GREEK_LOWER_UPSILON_DIALYTIKA_OXIA 0x1FE3 -#define GREEK_LOWER_OMEGA 0x03C9 -#define GREEK_LOWER_OMEGA_TONOS 0x03CE -#define GREEK_LOWER_OMEGA_OXIA 0x1F7D -#define GREEK_UPPER_ALPHA 0x0391 -#define GREEK_UPPER_EPSILON 0x0395 -#define GREEK_UPPER_ETA 0x0397 -#define GREEK_UPPER_IOTA 0x0399 -#define GREEK_UPPER_IOTA_DIALYTIKA 0x03AA -#define GREEK_UPPER_OMICRON 0x039F -#define GREEK_UPPER_UPSILON 0x03A5 -#define GREEK_UPPER_UPSILON_DIALYTIKA 0x03AB -#define GREEK_UPPER_OMEGA 0x03A9 -#define GREEK_UPPER_ALPHA_TONOS 0x0386 -#define GREEK_UPPER_ALPHA_OXIA 0x1FBB -#define GREEK_UPPER_EPSILON_TONOS 0x0388 -#define GREEK_UPPER_EPSILON_OXIA 0x1FC9 -#define GREEK_UPPER_ETA_TONOS 0x0389 -#define GREEK_UPPER_ETA_OXIA 0x1FCB -#define GREEK_UPPER_IOTA_TONOS 0x038A -#define GREEK_UPPER_IOTA_OXIA 0x1FDB -#define GREEK_UPPER_OMICRON_TONOS 0x038C -#define GREEK_UPPER_OMICRON_OXIA 0x1FF9 -#define GREEK_UPPER_UPSILON_TONOS 0x038E -#define GREEK_UPPER_UPSILON_OXIA 0x1FEB -#define GREEK_UPPER_OMEGA_TONOS 0x038F -#define GREEK_UPPER_OMEGA_OXIA 0x1FFB -#define COMBINING_ACUTE_ACCENT 0x0301 -#define COMBINING_DIAERESIS 0x0308 -#define COMBINING_ACUTE_TONE_MARK 0x0341 -#define COMBINING_GREEK_DIALYTIKA_TONOS 0x0344 - -uint32_t -GreekCasing::UpperCase(uint32_t aCh, GreekCasing::State& aState) -{ - switch (aCh) { - case GREEK_UPPER_ALPHA: - case GREEK_LOWER_ALPHA: - aState = kAlpha; - return GREEK_UPPER_ALPHA; - - case GREEK_UPPER_EPSILON: - case GREEK_LOWER_EPSILON: - aState = kEpsilon; - return GREEK_UPPER_EPSILON; - - case GREEK_UPPER_ETA: - case GREEK_LOWER_ETA: - aState = kEta; - return GREEK_UPPER_ETA; - - case GREEK_UPPER_IOTA: - aState = kIota; - return GREEK_UPPER_IOTA; - - case GREEK_UPPER_OMICRON: - case GREEK_LOWER_OMICRON: - aState = kOmicron; - return GREEK_UPPER_OMICRON; - - case GREEK_UPPER_UPSILON: - switch (aState) { - case kOmicron: - aState = kOmicronUpsilon; - break; - default: - aState = kUpsilon; - break; - } - return GREEK_UPPER_UPSILON; - - case GREEK_UPPER_OMEGA: - case GREEK_LOWER_OMEGA: - aState = kOmega; - return GREEK_UPPER_OMEGA; - - // iota and upsilon may be the second vowel of a diphthong - case GREEK_LOWER_IOTA: - switch (aState) { - case kAlphaAcc: - case kEpsilonAcc: - case kOmicronAcc: - case kUpsilonAcc: - aState = kStart; - return GREEK_UPPER_IOTA_DIALYTIKA; - default: - break; - } - aState = kIota; - return GREEK_UPPER_IOTA; - - case GREEK_LOWER_UPSILON: - switch (aState) { - case kAlphaAcc: - case kEpsilonAcc: - case kEtaAcc: - case kOmicronAcc: - aState = kStart; - return GREEK_UPPER_UPSILON_DIALYTIKA; - case kOmicron: - aState = kOmicronUpsilon; - break; - default: - aState = kUpsilon; - break; - } - return GREEK_UPPER_UPSILON; - - case GREEK_UPPER_IOTA_DIALYTIKA: - case GREEK_LOWER_IOTA_DIALYTIKA: - case GREEK_UPPER_UPSILON_DIALYTIKA: - case GREEK_LOWER_UPSILON_DIALYTIKA: - case COMBINING_DIAERESIS: - aState = kDiaeresis; - return ToUpperCase(aCh); - - // remove accent if it follows a vowel or diaeresis, - // and set appropriate state for diphthong detection - case COMBINING_ACUTE_ACCENT: - case COMBINING_ACUTE_TONE_MARK: - switch (aState) { - case kAlpha: - aState = kAlphaAcc; - return uint32_t(-1); // omit this char from result string - case kEpsilon: - aState = kEpsilonAcc; - return uint32_t(-1); - case kEta: - aState = kEtaAcc; - return uint32_t(-1); - case kIota: - aState = kIotaAcc; - return uint32_t(-1); - case kOmicron: - aState = kOmicronAcc; - return uint32_t(-1); - case kUpsilon: - aState = kUpsilonAcc; - return uint32_t(-1); - case kOmicronUpsilon: - aState = kStart; // this completed a diphthong - return uint32_t(-1); - case kOmega: - aState = kOmegaAcc; - return uint32_t(-1); - case kDiaeresis: - aState = kStart; - return uint32_t(-1); - default: - break; - } - break; - - // combinations with dieresis+accent just strip the accent, - // and reset to start state (don't form diphthong with following vowel) - case GREEK_LOWER_IOTA_DIALYTIKA_TONOS: - case GREEK_LOWER_IOTA_DIALYTIKA_OXIA: - aState = kStart; - return GREEK_UPPER_IOTA_DIALYTIKA; - - case GREEK_LOWER_UPSILON_DIALYTIKA_TONOS: - case GREEK_LOWER_UPSILON_DIALYTIKA_OXIA: - aState = kStart; - return GREEK_UPPER_UPSILON_DIALYTIKA; - - case COMBINING_GREEK_DIALYTIKA_TONOS: - aState = kStart; - return COMBINING_DIAERESIS; - - // strip accents from vowels, and note the vowel seen so that we can detect - // diphthongs where diaeresis needs to be added - case GREEK_LOWER_ALPHA_TONOS: - case GREEK_LOWER_ALPHA_OXIA: - case GREEK_UPPER_ALPHA_TONOS: - case GREEK_UPPER_ALPHA_OXIA: - aState = kAlphaAcc; - return GREEK_UPPER_ALPHA; - - case GREEK_LOWER_EPSILON_TONOS: - case GREEK_LOWER_EPSILON_OXIA: - case GREEK_UPPER_EPSILON_TONOS: - case GREEK_UPPER_EPSILON_OXIA: - aState = kEpsilonAcc; - return GREEK_UPPER_EPSILON; - - case GREEK_LOWER_ETA_TONOS: - case GREEK_LOWER_ETA_OXIA: - case GREEK_UPPER_ETA_TONOS: - case GREEK_UPPER_ETA_OXIA: - aState = kEtaAcc; - return GREEK_UPPER_ETA; - - case GREEK_LOWER_IOTA_TONOS: - case GREEK_LOWER_IOTA_OXIA: - case GREEK_UPPER_IOTA_TONOS: - case GREEK_UPPER_IOTA_OXIA: - aState = kIotaAcc; - return GREEK_UPPER_IOTA; - - case GREEK_LOWER_OMICRON_TONOS: - case GREEK_LOWER_OMICRON_OXIA: - case GREEK_UPPER_OMICRON_TONOS: - case GREEK_UPPER_OMICRON_OXIA: - aState = kOmicronAcc; - return GREEK_UPPER_OMICRON; - - case GREEK_LOWER_UPSILON_TONOS: - case GREEK_LOWER_UPSILON_OXIA: - case GREEK_UPPER_UPSILON_TONOS: - case GREEK_UPPER_UPSILON_OXIA: - switch (aState) { - case kOmicron: - aState = kStart; // this completed a diphthong - break; - default: - aState = kUpsilonAcc; - break; - } - return GREEK_UPPER_UPSILON; - - case GREEK_LOWER_OMEGA_TONOS: - case GREEK_LOWER_OMEGA_OXIA: - case GREEK_UPPER_OMEGA_TONOS: - case GREEK_UPPER_OMEGA_OXIA: - aState = kOmegaAcc; - return GREEK_UPPER_OMEGA; - } - - // all other characters just reset the state, and use standard mappings - aState = kStart; - return ToUpperCase(aCh); -} - int32_t CaseInsensitiveCompare(const char16_t *a, const char16_t *b, diff --git a/intl/unicharutil/util/nsUnicharUtils.h b/intl/unicharutil/util/nsUnicharUtils.h index ec49c4ca066..3f33c851da8 100644 --- a/intl/unicharutil/util/nsUnicharUtils.h +++ b/intl/unicharutil/util/nsUnicharUtils.h @@ -37,63 +37,6 @@ inline bool IsLowerCase(uint32_t c) { return ToUpperCase(c) != c; } -class GreekCasing { - // When doing an Uppercase transform in Greek, we need to keep track of the - // current state while iterating through the string, to recognize and process - // diphthongs correctly. For clarity, we define a state for each vowel and - // each vowel with accent, although a few of these do not actually need any - // special treatment and could be folded into kStart. -private: - enum GreekStates { - kStart, - kAlpha, - kEpsilon, - kEta, - kIota, - kOmicron, - kUpsilon, - kOmega, - kAlphaAcc, - kEpsilonAcc, - kEtaAcc, - kIotaAcc, - kOmicronAcc, - kUpsilonAcc, - kOmegaAcc, - kOmicronUpsilon, - kDiaeresis - }; - -public: - class State { - public: - State() - : mState(kStart) - { - } - - State(const GreekStates& aState) - : mState(aState) - { - } - - void Reset() - { - mState = kStart; - } - - operator GreekStates() const - { - return mState; - } - - private: - GreekStates mState; - }; - - static uint32_t UpperCase(uint32_t aCh, State& aState); -}; - #ifdef MOZILLA_INTERNAL_API class nsCaseInsensitiveStringComparator : public nsStringComparator diff --git a/intl/unicharutil/util/objs.mozbuild b/intl/unicharutil/util/objs.mozbuild index b832a15eb64..d5c8a31ba31 100644 --- a/intl/unicharutil/util/objs.mozbuild +++ b/intl/unicharutil/util/objs.mozbuild @@ -4,7 +4,9 @@ # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. -intl_unicharutil_util_lcppsrcs = [] +intl_unicharutil_util_lcppsrcs = [ + 'GreekCasing.cpp', +] if CONFIG['ENABLE_INTL_API']: intl_unicharutil_util_lcppsrcs += [ @@ -12,6 +14,7 @@ if CONFIG['ENABLE_INTL_API']: ] intl_unicharutil_util_lcppsrcs += [ + 'IrishCasing.cpp', 'nsBidiUtils.cpp', 'nsSpecialCasingData.cpp', 'nsUnicharUtils.cpp', diff --git a/layout/generic/nsTextRunTransformations.cpp b/layout/generic/nsTextRunTransformations.cpp index ec37c297f69..db955571a38 100644 --- a/layout/generic/nsTextRunTransformations.cpp +++ b/layout/generic/nsTextRunTransformations.cpp @@ -17,6 +17,8 @@ #include "nsTextFrameUtils.h" #include "nsIPersistentProperties2.h" #include "nsNetUtil.h" +#include "GreekCasing.h" +#include "IrishCasing.h" // Unicode characters needing special casing treatment in tr/az languages #define LATIN_CAPITAL_LETTER_I_WITH_DOT_ABOVE 0x0130 @@ -224,9 +226,10 @@ GetParametersForInner(nsTransformedTextRun* aTextRun, uint32_t* aFlags, // same setting here, if the behavior is shared by other languages. enum LanguageSpecificCasingBehavior { eLSCB_None, // default non-lang-specific behavior - eLSCB_Turkish, // preserve dotted/dotless-i distinction in uppercase eLSCB_Dutch, // treat "ij" digraph as a unit for capitalization - eLSCB_Greek // strip accent when uppercasing Greek vowels + eLSCB_Greek, // strip accent when uppercasing Greek vowels + eLSCB_Irish, // keep prefix letters as lowercase when uppercasing Irish + eLSCB_Turkish // preserve dotted/dotless-i distinction in uppercase }; static LanguageSpecificCasingBehavior @@ -245,6 +248,9 @@ GetCasingFor(const nsIAtom* aLang) if (aLang == nsGkAtoms::el) { return eLSCB_Greek; } + if (aLang == nsGkAtoms::ga_ie) { + return eLSCB_Irish; + } return eLSCB_None; } @@ -277,7 +283,9 @@ nsCaseTransformTextRunFactory::TransformString( const nsIAtom* lang = aLanguage; LanguageSpecificCasingBehavior languageSpecificCasing = GetCasingFor(lang); - GreekCasing::State greekState; + mozilla::GreekCasing::State greekState; + mozilla::IrishCasing::State irishState; + uint32_t irishMark = uint32_t(-1); // location of possible prefix letter(s) for (uint32_t i = 0; i < length; ++i) { uint32_t ch = str[i]; @@ -292,11 +300,14 @@ nsCaseTransformTextRunFactory::TransformString( lang = styleContext->StyleFont()->mLanguage; languageSpecificCasing = GetCasingFor(lang); greekState.Reset(); + irishState.Reset(); + irishMark = uint32_t(-1); } } int extraChars = 0; const mozilla::unicode::MultiCharMapping *mcm; + bool inhibitBreakBefore = false; // have we just deleted preceding hyphen? if (NS_IS_HIGH_SURROGATE(ch) && i < length - 1 && NS_IS_LOW_SURROGATE(str[i + 1])) { @@ -396,10 +407,63 @@ nsCaseTransformTextRunFactory::TransformString( } if (languageSpecificCasing == eLSCB_Greek) { - ch = GreekCasing::UpperCase(ch, greekState); + ch = mozilla::GreekCasing::UpperCase(ch, greekState); break; } + if (languageSpecificCasing == eLSCB_Irish) { + bool mark; + uint8_t action; + ch = mozilla::IrishCasing::UpperCase(ch, irishState, mark, action); + if (mark) { + irishMark = aConvertedString.Length(); + break; + } else if (action) { + nsString& str = aConvertedString; // shorthand + switch (action) { + case 1: + // lowercase a single prefix letter + NS_ASSERTION(str.Length() > 0 && irishMark < str.Length(), + "bad irishMark!"); + str.SetCharAt(ToLowerCase(str[irishMark]), irishMark); + irishMark = uint32_t(-1); + break; + case 2: + // lowercase two prefix letters (immediately before current pos) + NS_ASSERTION(str.Length() >= 2 && irishMark == str.Length() - 2, + "bad irishMark!"); + str.SetCharAt(ToLowerCase(str[irishMark]), irishMark); + str.SetCharAt(ToLowerCase(str[irishMark + 1]), irishMark + 1); + irishMark = uint32_t(-1); + break; + case 3: + // lowercase one prefix letter, and delete following hyphen + // (which must be the immediately-preceding char) + NS_ASSERTION(str.Length() >= 2 && irishMark == str.Length() - 2, + "bad irishMark!"); + str.Replace(irishMark, 2, ToLowerCase(str[irishMark])); + aDeletedCharsArray[irishMark + 1] = true; + // Remove the trailing entries (corresponding to the deleted hyphen) + // from the auxiliary arrays. + aCharsToMergeArray.SetLength(aCharsToMergeArray.Length() - 1); + if (aTextRun) { + aStyleArray->SetLength(aStyleArray->Length() - 1); + aCanBreakBeforeArray->SetLength(aCanBreakBeforeArray->Length() - 1); + inhibitBreakBefore = true; + } + mergeNeeded = true; + irishMark = uint32_t(-1); + break; + } + // ch has been set to the uppercase for current char; + // No need to check for SpecialUpper here as none of the characters + // that could trigger an Irish casing action have special mappings. + break; + } + // If we didn't have any special action to perform, fall through + // to check for special uppercase (ß) + } + mcm = mozilla::unicode::SpecialUpper(ch); if (mcm) { int j = 0; @@ -467,7 +531,8 @@ nsCaseTransformTextRunFactory::TransformString( aCharsToMergeArray.AppendElement(false); if (aTextRun) { aStyleArray->AppendElement(styleContext); - aCanBreakBeforeArray->AppendElement(aTextRun->CanBreakLineBefore(i)); + aCanBreakBeforeArray->AppendElement(inhibitBreakBefore ? false : + aTextRun->CanBreakLineBefore(i)); } if (IS_IN_BMP(ch)) { diff --git a/layout/reftests/text-transform/irish-uppercase-1-ref.html b/layout/reftests/text-transform/irish-uppercase-1-ref.html new file mode 100644 index 00000000000..a9dc7ddc12c --- /dev/null +++ b/layout/reftests/text-transform/irish-uppercase-1-ref.html @@ -0,0 +1,120 @@ + + + + +Test for Irish uppercasing + + + + ORD NA bhFOCAL +/ COSÁN NA bhFILÍ +/ ÁR bPOBAL +/ NÓRA NA bPORTACH +/ I dTOSACH BÁIRE +/ AN GHAEILGE I dTUAISCEART NA hÉIREANN +/ AS AN gCEANTAR SIN +/ I gCONTAE NA MÍ AGUS I gCONAMARA +/ DÉ hAOINE +/ OIRTHEAR NA hÁISE +/ PARLAIMINT NA hEORPA +/ POBLACHT NA hÉIREANN +/ EALAÍN NA hIODÁILE +/ NA hÍOSÁNAIGH +/ ACADAMH NA hOLLSCOLAÍOCHTA +/ TÍR NA hÓIGE +/ TOGHCHÁN NA hUACHTARÁNACHTA +/ NA hÚDARÁIS CHÁNACH +/ I mBUN MO MHACHNAMH +/ I mBÉAL FEIRSTE AGUS I mBAILE ÁTHA CLIATH +/ ÁR nACMHAINNÍ UISCE +/ EOLAÍOCHT NA nÁBHAR +/ LUCHT NA nEALAÍON +/ CEOL NA nÉAN +/ ORD NA nIMEACHTAÍ +/ LUCHT ADHARTHA NA nÍOMHÁNNA +/ GNÉITHE DÁR nOIDHREACHT +/ CULTÚR NA nÓG +/ OCHT nUAIRE SA LÁ +/ FORMHÓR NA nÚDARÁS +/ ÁR nATHAIR +/ CLÁR NA nÁBHAR +/ LOCH nEATHACH +/ CUMANN NA nÉIREANNACH AONTAITHE +/ GRÉASÁN NA nIONTAS +/ NÓIBHÍSEACHT NA nÍOSÁNACH +/ I gCEANTAR NA nOILEÁN +/ TÍR NA nÓG +/ BAILE NA nULTACH +/ GORT NA nÚLL +/ CEOL NA nDAOINE +/ I nDÚN NA nGALL +/ TÁIM I nGRÁ LEAT +/ LABHAIR SÉ I nGAEILGE! +/ CÉN tAM É? +/ TÁ AN tÁDH ORM INNIU! +/ DEN OBAIR AN tEOLAS +/ AN tÉILEAMH A ÍOC +/ AN tINNEALL CUARDAIGH IS FEARR +/ AN tÍOCHTAR A CHUR IN UACHTAR +/ TABHAIR AN tORDÚ SEO DÓ! +/ TÁ AN tÓR BUÍ AIGE. +/ AN tUISCE BEATHA AR AN TÁBLA. +/ AN tÚRSCÉAL IS DEIREANAÍ +/ AN tACHT OIDEACHAIS +/ AN tÁIVÉ MÁIRIA +/ AN tEARRACH ARABACH +/ AN tÉIRÍ AMACH +/ AN tIMEALL +/ AN tÍOSÁNACH PEADAR CANISIUS +/ AN tOILEÁNACH +/ AN tÓR MUIRE +/ AN tUASAL ÉAMON Ó CUÍV +/ AN tÚDARÁS UM BÓITHRE NÁISIÚNTA +/ AR AON tSLÍ +/ BÉAL ÁTHA AN tSLÉIBHE +/ AMACH ÓN tSNÁTHAID +/ BANRÍON AN tSNEACHTA +/ AR AN tSRÁID +/ CAINT AN tSRÁIDBHAILE +/ CORA CRUA AN tSAOIL +/ BHOLADH AN tSÁILE +/ UAIR SA tSEACHTAIN +/ DEIREADH AN tSÉASÚIR +/ FEAR AN tSIOPA +/ AN tSÍOCHÁIN A CHOIMEÁD +/ AN tSOCHAÍ FAISNÉISE +/ GAOTH AN tSÓLÁIS +/ IS BEAG AN tSUIM IAD +/ INFHEICTHE AG AN tSÚIL +/ CNOC AN tSAMHRAIDH +/ CIONN tSÁILE +/ AN tSEIRBHÍS PHOIBLÍ +/ BAILE AN tSÉIPÉIL +/ AN tSIRIA +/ AN tSÍN +/ OIFIG AN tSOLÁTHAIR +/ POLL AN tSÓMAIS +/ EOLAIRE AN tSUÍMH +/ CASADH AN tSÚGÁIN +/ SCRÍOBHFAIDH +/ PREABPHAS +/ ÚSÁIDTEAR +/ SNAGCHEOL +/ STÁITSE IMBOLC +/ IN-ATHNUAITE AGATSA +/ TEANGA DHOMHANDA +/ RÉALTSRUTH +/ NA HATAÍ +/ NA HATAÍ +/ ÁR NATHAIR +/ ÁR NATHAIR +/ T-LÉINE +/ TORC ALLTA +/ TSK TSK TSK A CHARA + + diff --git a/layout/reftests/text-transform/irish-uppercase-1.html b/layout/reftests/text-transform/irish-uppercase-1.html new file mode 100644 index 00000000000..9e4743917ee --- /dev/null +++ b/layout/reftests/text-transform/irish-uppercase-1.html @@ -0,0 +1,120 @@ + + + + +Test for Irish uppercasing + + + + ord na bhfocal +/ Cosán na bhFilí +/ ár bpobal +/ Nóra na bPortach +/ i dtosach báire +/ An Ghaeilge i dTuaisceart na hÉireann +/ as an gceantar sin +/ I gContae na Mí agus i gConamara +/ Dé hAoine +/ Oirthear na hÁise +/ Parlaimint na hEorpa +/ Poblacht na hÉireann +/ Ealaín na hIodáile +/ na hÍosánaigh +/ Acadamh na hOllscolaíochta +/ Tír na hÓige +/ toghchán na hUachtaránachta +/ na hÚdaráis Chánach +/ I mbun mo mhachnamh +/ I mBéal Feirste agus i mBaile Átha Cliath +/ ár n-acmhainní uisce +/ eolaíocht na n-ábhar +/ lucht na n-ealaíon +/ ceol na n-éan +/ ord na n-imeachtaí +/ lucht adhartha na n-íomhánna +/ gnéithe dár n-oidhreacht +/ cultúr na n-óg +/ ocht n-uaire sa lá +/ formhór na n-údarás +/ Ár nAthair +/ Clár na nÁbhar +/ Loch nEathach +/ Cumann na nÉireannach Aontaithe +/ Gréasán na nIontas +/ nóibhíseacht na nÍosánach +/ i gCeantar na nOileán +/ Tír na nÓg +/ Baile na nUltach +/ Gort na nÚll +/ ceol na ndaoine +/ i nDún na nGall +/ táim i ngrá leat +/ labhair sé i nGaeilge! +/ cén t-am é? +/ tá an t-ádh orm inniu! +/ Den obair an t-eolas +/ An t-éileamh a íoc +/ an t-inneall cuardaigh is fearr +/ an t-íochtar a chur in uachtar +/ Tabhair an t-ordú seo dó! +/ Tá an t-ór buí aige. +/ an t-uisce beatha ar an tábla. +/ an t-úrscéal is deireanaí +/ An tAcht Oideachais +/ an tÁivé Máiria +/ An tEarrach Arabach +/ An tÉirí Amach +/ An tImeall +/ An tÍosánach Peadar Canisius +/ An tOileánach +/ An tÓr Muire +/ an tUasal Éamon Ó Cuív +/ An tÚdarás um Bóithre Náisiúnta +/ ar aon tslí +/ Béal Átha an tSléibhe +/ Amach ón tsnáthaid +/ Banríon an tSneachta +/ ar an tsráid +/ Caint an tSráidbhaile +/ cora crua an tsaoil +/ bholadh an tsáile +/ uair sa tseachtain +/ deireadh an tséasúir +/ fear an tsiopa +/ an tsíocháin a choimeád +/ an tsochaí faisnéise +/ gaoth an tsóláis +/ Is beag an tsuim iad +/ infheicthe ag an tsúil +/ Cnoc an tSamhraidh +/ Cionn tSáile +/ an tSeirbhís Phoiblí +/ Baile an tSéipéil +/ An tSiria +/ An tSín +/ Oifig an tSoláthair +/ Poll an tSómais +/ Eolaire an tSuímh +/ Casadh an tSúgáin +/ scríobhfaidh +/ preabphas +/ úsáidtear +/ snagcheol +/ Stáitse Imbolc +/ in-athnuaite agatsa +/ Teanga Dhomhanda +/ Réaltsruth +/ na hataí +/ Na Hataí +/ ár nathair +/ Ár Nathair +/ t-léine +/ torc allta +/ tsk tsk tsk a chara + + diff --git a/layout/reftests/text-transform/reftest.list b/layout/reftests/text-transform/reftest.list index f36280e9e01..be23faf642d 100644 --- a/layout/reftests/text-transform/reftest.list +++ b/layout/reftests/text-transform/reftest.list @@ -28,6 +28,7 @@ HTTP(..) != small-caps-turkish-1.html small-caps-turkish-1-notref.html == greek-uppercase-1.html greek-uppercase-1-ref.html == greek-uppercase-2.html greek-uppercase-2-ref.html HTTP(..) == greek-small-caps-1.html greek-small-caps-1-ref.html +== irish-uppercase-1.html irish-uppercase-1-ref.html == fullwidth-1.html fullwidth-1-ref.html == fullwidth-2.html fullwidth-2-ref.html == fullwidth-all.html fullwidth-all-ref.html diff --git a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp index b820ab2c5f1..29b43c29d12 100644 --- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp +++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp @@ -133,16 +133,13 @@ VcmSIPCCBinding::VcmSIPCCBinding () class VcmIceOpaque : public NrIceOpaque { public: - VcmIceOpaque(cc_streamid_t stream_id, - cc_call_handle_t call_handle, + VcmIceOpaque(cc_call_handle_t call_handle, uint16_t level) : - stream_id_(stream_id), call_handle_(call_handle), level_(level) {} virtual ~VcmIceOpaque() {} - cc_streamid_t stream_id_; cc_call_handle_t call_handle_; uint16_t level_; }; @@ -172,8 +169,8 @@ void VcmSIPCCBinding::CandidateReady(NrIceMediaStream* stream, MOZ_ASSERT(opaque); VcmIceOpaque *vcm_opaque = static_cast(opaque); - CSFLogDebug(logTag, "Candidate ready on call %u, level %u", - vcm_opaque->call_handle_, vcm_opaque->level_); + CSFLogDebug(logTag, "Candidate ready on call %u, level %u: %s", + vcm_opaque->call_handle_, vcm_opaque->level_, candidate.c_str()); char *candidate_tmp = (char *)malloc(candidate.size() + 1); if (!candidate_tmp) @@ -595,11 +592,15 @@ static short vcmRxAllocICE_s(TemporaryRef ctx_in, *candidatesp = nullptr; *candidate_ctp = 0; - // Set the opaque so we can correlate events. - stream->SetOpaque(new VcmIceOpaque(stream_id, call_handle, level)); + // This can be called multiple times; don't connect to the signal more than + // once (see bug 1018473 for an explanation). + if (!stream->opaque()) { + // Set the opaque so we can correlate events. + stream->SetOpaque(new VcmIceOpaque(call_handle, level)); - // Attach ourself to the candidate signal. - VcmSIPCCBinding::connectCandidateSignal(stream); + // Attach ourself to the candidate signal. + VcmSIPCCBinding::connectCandidateSignal(stream); + } std::vector candidates = stream->GetCandidates(); CSFLogDebug( logTag, "%s: Got %lu candidates", __FUNCTION__, (unsigned long) candidates.size()); diff --git a/media/webrtc/signaling/test/signaling_unittests.cpp b/media/webrtc/signaling/test/signaling_unittests.cpp index 13887fea34d..763631a272a 100644 --- a/media/webrtc/signaling/test/signaling_unittests.cpp +++ b/media/webrtc/signaling/test/signaling_unittests.cpp @@ -477,6 +477,12 @@ TestObserver::OnIceCandidate(uint16_t level, { std::cout << name << ": onIceCandidate [" << level << "/" << mid << "] " << candidate << std::endl; + + // Check for duplicates. + for (auto it = candidates.begin(); it != candidates.end(); ++it) { + EXPECT_NE(*it, candidate) << "Duplicate candidate"; + } + candidates.push_back(candidate); return NS_OK; } diff --git a/modules/libpref/src/init/all.js b/modules/libpref/src/init/all.js index 4ea7c8937f7..060b83cfe69 100644 --- a/modules/libpref/src/init/all.js +++ b/modules/libpref/src/init/all.js @@ -3144,7 +3144,7 @@ pref("font.name.monospace.ko", "Fira Mono OT"); pref("font.name.serif.th", "Charis SIL Compact"); pref("font.name.sans-serif.th", "Fira Sans OT"); pref("font.name.monospace.th", "Fira Mono OT"); -pref("font.name-list.sans-serif.th", "Fira Sans OT, Droid Sans Thai"); +pref("font.name-list.sans-serif.th", "Fira Sans OT, Noto Sans Thai, Droid Sans Thai"); pref("font.name.serif.tr", "Charis SIL Compact"); pref("font.name.sans-serif.tr", "Fira Sans OT"); diff --git a/testing/marionette/client/marionette/tests/unit-tests.ini b/testing/marionette/client/marionette/tests/unit-tests.ini index 86c3b63398e..9174d6c47fd 100644 --- a/testing/marionette/client/marionette/tests/unit-tests.ini +++ b/testing/marionette/client/marionette/tests/unit-tests.ini @@ -26,3 +26,4 @@ skip = false [include:../../../../../dom/system/tests/marionette/manifest.ini] [include:../../../../../dom/nfc/tests/marionette/manifest.ini] [include:../../../../../dom/events/test/marionette/manifest.ini] +[include:../../../../../dom/wifi/test/marionette/manifest.ini] diff --git a/testing/xpcshell/xpcshell_b2g.ini b/testing/xpcshell/xpcshell_b2g.ini index 7773dafe67a..5f797c8c315 100644 --- a/testing/xpcshell/xpcshell_b2g.ini +++ b/testing/xpcshell/xpcshell_b2g.ini @@ -8,6 +8,7 @@ [include:dom/system/gonk/tests/xpcshell.ini] [include:dom/wappush/tests/xpcshell.ini] [include:toolkit/components/osfile/tests/xpcshell/xpcshell.ini] +[include:toolkit/components/captivedetect/test/unit/xpcshell.ini] [include:toolkit/devtools/apps/tests/unit/xpcshell.ini] [include:toolkit/devtools/debugger/tests/unit/xpcshell.ini] [include:toolkit/devtools/qrcode/tests/unit/xpcshell.ini] diff --git a/toolkit/components/captivedetect/captivedetect.js b/toolkit/components/captivedetect/captivedetect.js index af1bd232665..24ac45b719d 100644 --- a/toolkit/components/captivedetect/captivedetect.js +++ b/toolkit/components/captivedetect/captivedetect.js @@ -10,6 +10,10 @@ const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components; Cu.import('resource://gre/modules/XPCOMUtils.jsm'); Cu.import('resource://gre/modules/Services.jsm'); +XPCOMUtils.defineLazyServiceGetter(this, "gSysMsgr", + "@mozilla.org/system-message-internal;1", + "nsISystemMessagesInternal"); + const DEBUG = false; // set to true to show debug messages const kCAPTIVEPORTALDETECTOR_CONTRACTID = '@mozilla.org/toolkit/captive-detector;1'; @@ -17,6 +21,9 @@ const kCAPTIVEPORTALDETECTOR_CID = Components.ID('{d9cd00ba-aa4d-47b1-879 const kOpenCaptivePortalLoginEvent = 'captive-portal-login'; const kAbortCaptivePortalLoginEvent = 'captive-portal-login-abort'; +const kCaptivePortalLoginSuccessEvent = 'captive-portal-login-success'; + +const kCaptivePortalSystemMessage = 'captive-portal'; function URLFetcher(url, timeout) { let self = this; @@ -332,6 +339,7 @@ CaptivePortalDetector.prototype = { this._loginObserver.attach(); this._runningRequest['eventId'] = id; this._sendEvent(kOpenCaptivePortalLoginEvent, details); + gSysMsgr.broadcastMessage(kCaptivePortalSystemMessage, {}); }, _mayRetry: function _mayRetry() { @@ -350,6 +358,16 @@ CaptivePortalDetector.prototype = { this._runningRequest.callback.complete(success); } + // Only when the request has a event id and |success| is true + // do we need to notify the login-success event. + if (this._runningRequest.hasOwnProperty('eventId') && success) { + let details = { + type: kCaptivePortalLoginSuccessEvent, + id: this._runningRequest['eventId'], + }; + this._sendEvent(kCaptivePortalLoginSuccessEvent, details); + } + // Continue the following request this._runningRequest['complete'] = true; this._removeRequest(this._runningRequest.interfaceName); diff --git a/toolkit/components/captivedetect/test/unit/test_captive_portal_found.js b/toolkit/components/captivedetect/test/unit/test_captive_portal_found.js index f8dbea40b85..457b0ad438c 100644 --- a/toolkit/components/captivedetect/test/unit/test_captive_portal_found.js +++ b/toolkit/components/captivedetect/test/unit/test_captive_portal_found.js @@ -32,6 +32,13 @@ function fakeUIResponse() { do_check_eq(++step, 2); } }, 'captive-portal-login', false); + + Services.obs.addObserver(function observe(subject, topic, data) { + if (topic === 'captive-portal-login-success') { + do_check_eq(++step, 4); + gServer.stop(do_test_finished); + } + }, 'captive-portal-login-success', false); } function test_portal_found() { @@ -44,9 +51,11 @@ function test_portal_found() { gCaptivePortalDetector.finishPreparation(kInterfaceName); }, complete: function complete(success) { + // Since this is a synchronous callback, it must happen before + // 'captive-portal-login-success' is received. + // (Check captivedetect.js::executeCallback do_check_eq(++step, 3); do_check_true(success); - gServer.stop(do_test_finished); }, }; diff --git a/toolkit/components/captivedetect/test/unit/test_captive_portal_found_303.js b/toolkit/components/captivedetect/test/unit/test_captive_portal_found_303.js index 857bb23c768..f0715d49a71 100644 --- a/toolkit/components/captivedetect/test/unit/test_captive_portal_found_303.js +++ b/toolkit/components/captivedetect/test/unit/test_captive_portal_found_303.js @@ -34,6 +34,13 @@ function fakeUIResponse() { do_check_eq(++step, 2); } }, 'captive-portal-login', false); + + Services.obs.addObserver(function observe(subject, topic, data) { + if (topic === 'captive-portal-login-success') { + do_check_eq(++step, 4); + gServer.stop(do_test_finished); + } + }, 'captive-portal-login-success', false); } function test_portal_found() { @@ -48,7 +55,6 @@ function test_portal_found() { complete: function complete(success) { do_check_eq(++step, 3); do_check_true(success); - gServer.stop(do_test_finished); }, }; @@ -57,12 +63,4 @@ function test_portal_found() { function run_test() { run_captivedetect_test(xhr_handler, fakeUIResponse, test_portal_found); - - server = new HttpServer(); - server.registerPathHandler(kCanonicalSitePath, xhr_handler); - server.start(4444); - - fakeUIResponse(); - - test_portal_found(); } diff --git a/toolkit/components/captivedetect/test/unit/test_multiple_requests.js b/toolkit/components/captivedetect/test/unit/test_multiple_requests.js index 63d8e66ebb9..2a45d371b40 100644 --- a/toolkit/components/captivedetect/test/unit/test_multiple_requests.js +++ b/toolkit/components/captivedetect/test/unit/test_multiple_requests.js @@ -10,6 +10,7 @@ const kOtherInterfaceName = 'ril'; var server; var step = 0; var loginFinished = false; +var loginSuccessCount = 0; function xhr_handler(metadata, response) { response.setStatusLine(metadata.httpVersion, 200, 'OK'); @@ -33,6 +34,16 @@ function fakeUIResponse() { do_check_eq(++step, 2); } }, 'captive-portal-login', false); + + Services.obs.addObserver(function observe(subject, topic, data) { + if (topic === 'captive-portal-login-success') { + loginSuccessCount++; + if (loginSuccessCount > 1) { + throw "We should only receive 'captive-portal-login-success' once"; + } + do_check_eq(++step, 4); + } + }, 'captive-portal-login-success', false); } function test_multiple_requests() { @@ -53,11 +64,11 @@ function test_multiple_requests() { let otherCallback = { QueryInterface: XPCOMUtils.generateQI([Ci.nsICaptivePortalCallback]), prepare: function prepare() { - do_check_eq(++step, 4); + do_check_eq(++step, 5); gCaptivePortalDetector.finishPreparation(kOtherInterfaceName); }, complete: function complete(success) { - do_check_eq(++step, 5); + do_check_eq(++step, 6); do_check_true(success); gServer.stop(do_test_finished); } diff --git a/widget/shared/NativeKeyToDOMCodeName.h b/widget/shared/NativeKeyToDOMCodeName.h index a75e20dcd48..ecb16427b50 100644 --- a/widget/shared/NativeKeyToDOMCodeName.h +++ b/widget/shared/NativeKeyToDOMCodeName.h @@ -694,36 +694,31 @@ CODE_MAP_X11(Pause, 0x007F) CODE_MAP_ANDROID(Pause, 0x0077) // Media keys - -// NOTE: Following media keys which cause scancode 0xE000 on Windows should be -// mapped with virtual keycode. -// See KeyboardLayout::ConvertScanCodeToCodeNameIndex() for the detail. - -// CODE_MAP_WIN(BrowserBack, 0xE000) // VK_BROWSER_BACK +CODE_MAP_WIN(BrowserBack, 0xE06A) CODE_MAP_X11(BrowserBack, 0x00A6) CODE_MAP_ANDROID(BrowserBack, 0x009E) -// CODE_MAP_WIN(BrowserFavorites, 0xE000) // VK_BROWSER_FAVORITES +CODE_MAP_WIN(BrowserFavorites, 0xE066) CODE_MAP_X11(BrowserFavorites, 0x00A4) CODE_MAP_ANDROID(BrowserFavorites, 0x009C) -// CODE_MAP_WIN(BrowserForward, 0xE000) // VK_BROWSER_FORWARD +CODE_MAP_WIN(BrowserForward, 0xE069) CODE_MAP_X11(BrowserForward, 0x00A7) CODE_MAP_ANDROID(BrowserForward, 0x009F) -// CODE_MAP_WIN(BrowserHome, 0xE000) // VK_BROWSER_HOME +CODE_MAP_WIN(BrowserHome, 0xE032) CODE_MAP_X11(BrowserHome, 0x00B4) // CODE_MAP_ANDROID(BrowserHome) // not available? works as Home key. -// CODE_MAP_WIN(BrowserRefresh, 0xE000) // VK_BROWSER_REFRESH +CODE_MAP_WIN(BrowserRefresh, 0xE067) CODE_MAP_X11(BrowserRefresh, 0x00B5) CODE_MAP_ANDROID(BrowserRefresh, 0x00AD) -// CODE_MAP_WIN(BrowserSearch, 0xE000) // VK_BROWSER_SEARCH +CODE_MAP_WIN(BrowserSearch, 0xE065) CODE_MAP_X11(BrowserSearch, 0x00E1) CODE_MAP_ANDROID(BrowserSearch, 0x00D9) -// CODE_MAP_WIN(BrowserStop, 0xE000) // VK_BROWSER_STOP +CODE_MAP_WIN(BrowserStop, 0xE068) CODE_MAP_X11(BrowserStop, 0x0088) CODE_MAP_ANDROID(BrowserStop, 0x0080) @@ -732,35 +727,35 @@ CODE_MAP_ANDROID(BrowserStop, 0x0080) CODE_MAP_X11(Eject, 0x00A9) CODE_MAP_ANDROID(Eject, 0x00A1) -// CODE_MAP_WIN(LaunchApp1, 0xE000) // VK_LAUNCH_APP1 +CODE_MAP_WIN(LaunchApp1, 0xE06B) CODE_MAP_X11(LaunchApp1, 0x0098) CODE_MAP_ANDROID(LaunchApp1, 0x0090) -// CODE_MAP_WIN(LaunchApp2, 0xE000) // VK_LAUNCH_APP2 +CODE_MAP_WIN(LaunchApp2, 0xE021) CODE_MAP_X11(LaunchApp2, 0x0094) // CODE_MAP_ANDROID(LaunchApp2) // not available? -// CODE_MAP_WIN(LaunchMail, 0xE000) // VK_LAUNCH_MAIL +CODE_MAP_WIN(LaunchMail, 0xE06C) CODE_MAP_X11(LaunchMail, 0x00A3) // CODE_MAP_ANDROID(LaunchMail) // not available? -// CODE_MAP_WIN(MediaPlayPause, 0xE000) // VK_MEDIA_PLAY_PAUSE +CODE_MAP_WIN(MediaPlayPause, 0xE022) CODE_MAP_X11(MediaPlayPause, 0x00AC) CODE_MAP_ANDROID(MediaPlayPause, 0x00A4) -// CODE_MAP_WIN(MediaSelect, 0xE000) // VK_LAUNCH_MEDIA_SELECT +CODE_MAP_WIN(MediaSelect, 0xE06D) CODE_MAP_X11(MediaSelect, 0x00B3) // CODE_MAP_ANDROID(MediaSelect) // not available? -// CODE_MAP_WIN(MediaStop, 0xE000) // VK_MEDIA_STOP +CODE_MAP_WIN(MediaStop, 0xE024) CODE_MAP_X11(MediaStop, 0x00AE) CODE_MAP_ANDROID(MediaStop, 0x00A6) -// CODE_MAP_WIN(MediaTrackNext, 0xE000) // VK_MEDIA_NEXT_TRACK +CODE_MAP_WIN(MediaTrackNext, 0xE019) CODE_MAP_X11(MediaTrackNext, 0x00AB) CODE_MAP_ANDROID(MediaTrackNext, 0x00A3) -// CODE_MAP_WIN(MediaTrackPrevious, 0xE000) // VK_MEDIA_PREV_TRACK +CODE_MAP_WIN(MediaTrackPrevious, 0xE010) CODE_MAP_X11(MediaTrackPrevious, 0x00AD) CODE_MAP_ANDROID(MediaTrackPrevious, 0x00A5) @@ -773,17 +768,17 @@ CODE_MAP_ANDROID(Power, 0x0074) // CODE_MAP_X11(Sleep) // not available? CODE_MAP_ANDROID(Sleep, 0x008E) -// CODE_MAP_WIN(VolumeDown, 0xE000) // VK_VOLUME_DOWN +CODE_MAP_WIN(VolumeDown, 0xE02E) CODE_MAP_MAC(VolumeDown, kVK_VolumeDown) // not available? CODE_MAP_X11(VolumeDown, 0x007A) CODE_MAP_ANDROID(VolumeDown, 0x0072) -// CODE_MAP_WIN(VolumeMute, 0xE000) // VK_VOLUME_MUTE +CODE_MAP_WIN(VolumeMute, 0xE020) CODE_MAP_MAC(VolumeMute, kVK_Mute) // not available? CODE_MAP_X11(VolumeMute, 0x0079) CODE_MAP_ANDROID(VolumeMute, 0x0071) -// CODE_MAP_WIN(VolumeUp, 0xE000) // VK_VOLUME_UP +CODE_MAP_WIN(VolumeUp, 0xE030) CODE_MAP_MAC(VolumeUp, kVK_VolumeUp) // not available? CODE_MAP_X11(VolumeUp, 0x007B) CODE_MAP_ANDROID(VolumeUp, 0x0073) // side of body, not on keyboard diff --git a/widget/windows/KeyboardLayout.cpp b/widget/windows/KeyboardLayout.cpp index ef4498413f8..2b2da8b6c35 100644 --- a/widget/windows/KeyboardLayout.cpp +++ b/widget/windows/KeyboardLayout.cpp @@ -40,6 +40,11 @@ #include #endif +// In WinUser.h, MAPVK_VK_TO_VSC_EX is defined only when WINVER >= 0x0600 +#ifndef MAPVK_VK_TO_VSC_EX +#define MAPVK_VK_TO_VSC_EX (4) +#endif + namespace mozilla { namespace widget { @@ -584,15 +589,25 @@ NativeKey::NativeKey(nsWindowBase* aWidget, mKeyboardLayout = keyboardLayout->GetLayout(); mScanCode = WinUtils::GetScanCode(mMsg.lParam); mIsExtended = WinUtils::IsExtendedScanCode(mMsg.lParam); - // On WinXP and WinServer2003, we cannot compute the virtual keycode for - // extended keys due to the API limitation. - bool canComputeVirtualKeyCodeFromScanCode = - (!mIsExtended || IsVistaOrLater()); switch (mMsg.message) { case WM_KEYDOWN: case WM_SYSKEYDOWN: case WM_KEYUP: case WM_SYSKEYUP: { + // If the key message is sent from other application like a11y tools, the + // scancode value might not be set proper value. Then, probably the value + // is 0. + // NOTE: If the virtual keycode can be caused by both non-extended key + // and extended key, the API returns the non-extended key's + // scancode. E.g., VK_LEFT causes "4" key on numpad. + if (!mScanCode) { + uint16_t scanCodeEx = ComputeScanCodeExFromVirtualKeyCode(mMsg.wParam); + if (scanCodeEx) { + mScanCode = static_cast(scanCodeEx & 0xFF); + uint8_t extended = static_cast((scanCodeEx & 0xFF00) >> 8); + mIsExtended = (extended == 0xE0) || (extended == 0xE1); + } + } // First, resolve the IME converted virtual keycode to its original // keycode. if (mMsg.wParam == VK_PROCESSKEY) { @@ -648,7 +663,7 @@ NativeKey::NativeKey(nsWindowBase* aWidget, break; } - if (!canComputeVirtualKeyCodeFromScanCode) { + if (!CanComputeVirtualKeyCodeFromScanCode()) { // The right control key and the right alt key are extended keys. // Therefore, we never get VK_RCONTRL and VK_RMENU for the result of // MapVirtualKeyEx() on WinXP or WinServer2003. @@ -679,11 +694,9 @@ NativeKey::NativeKey(nsWindowBase* aWidget, // Otherwise, compute the virtual keycode with MapVirtualKeyEx(). mVirtualKeyCode = ComputeVirtualKeyCodeFromScanCodeEx(); - // The result might be unexpected value due to the scan code is - // wrong. For example, any key messages can be generated by - // SendMessage() or PostMessage() from applications. So, it's possible - // failure. Then, let's respect the extended flag even if it might be - // set intentionally. + // Following code shouldn't be used now because we compute scancode value + // if we detect that the sender doesn't set proper scancode. + // However, the detection might fail. Therefore, let's keep using this. switch (mOriginalVirtualKeyCode) { case VK_CONTROL: if (mVirtualKeyCode != VK_LCONTROL && @@ -711,9 +724,13 @@ NativeKey::NativeKey(nsWindowBase* aWidget, case WM_CHAR: case WM_UNICHAR: case WM_SYSCHAR: + // NOTE: If other applications like a11y tools sends WM_*CHAR without + // scancode, we cannot compute virtual keycode. I.e., with such + // applications, we cannot generate proper KeyboardEvent.code value. + // We cannot compute the virtual key code from WM_CHAR message on WinXP // if it's caused by an extended key. - if (!canComputeVirtualKeyCodeFromScanCode) { + if (!CanComputeVirtualKeyCodeFromScanCode()) { break; } mVirtualKeyCode = mOriginalVirtualKeyCode = @@ -732,11 +749,9 @@ NativeKey::NativeKey(nsWindowBase* aWidget, keyboardLayout->ConvertNativeKeyCodeToDOMKeyCode(mOriginalVirtualKeyCode); mKeyNameIndex = keyboardLayout->ConvertNativeKeyCodeToKeyNameIndex(mOriginalVirtualKeyCode); - // Even on WinXP or WinServer 2003, we should use extended flag for computing - // the DOM code value since it's really our internal code. mCodeNameIndex = KeyboardLayout::ConvertScanCodeToCodeNameIndex( - mIsExtended ? (0xE000 | mScanCode) : mScanCode, mOriginalVirtualKeyCode); + GetScanCodeWithExtendedFlag()); keyboardLayout->InitNativeKey(*this, mModKeyState); @@ -875,6 +890,18 @@ NativeKey::GetKeyLocation() const } } +bool +NativeKey::CanComputeVirtualKeyCodeFromScanCode() const +{ + // Vista or later supports ScanCodeEx. + if (IsVistaOrLater()) { + return true; + } + // Otherwise, MapVirtualKeyEx() can compute virtual keycode only with + // non-extended key. + return !mIsExtended; +} + uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCode() const { @@ -885,14 +912,24 @@ NativeKey::ComputeVirtualKeyCodeFromScanCode() const uint8_t NativeKey::ComputeVirtualKeyCodeFromScanCodeEx() const { - // NOTE: WinXP doesn't support mapping scan code to virtual keycode of - // extended keys. - NS_ENSURE_TRUE(!mIsExtended || IsVistaOrLater(), 0); + if (NS_WARN_IF(!CanComputeVirtualKeyCodeFromScanCode())) { + return 0; + } return static_cast( ::MapVirtualKeyEx(GetScanCodeWithExtendedFlag(), MAPVK_VSC_TO_VK_EX, mKeyboardLayout)); } +uint16_t +NativeKey::ComputeScanCodeExFromVirtualKeyCode(UINT aVirtualKeyCode) const +{ + return static_cast( + ::MapVirtualKeyEx(aVirtualKeyCode, + IsVistaOrLater() ? MAPVK_VK_TO_VSC_EX : + MAPVK_VK_TO_VSC, + mKeyboardLayout)); +} + char16_t NativeKey::ComputeUnicharFromScanCode() const { @@ -2629,8 +2666,7 @@ KeyboardLayout::ConvertNativeKeyCodeToKeyNameIndex(uint8_t aVirtualKey) const // static CodeNameIndex -KeyboardLayout::ConvertScanCodeToCodeNameIndex(UINT aScanCode, - UINT aVirtualKeyCode) +KeyboardLayout::ConvertScanCodeToCodeNameIndex(UINT aScanCode) { switch (aScanCode) { @@ -2641,49 +2677,6 @@ KeyboardLayout::ConvertScanCodeToCodeNameIndex(UINT aScanCode, #undef NS_NATIVE_KEY_TO_DOM_CODE_NAME_INDEX - // Some special keys cuases 0xE000 of scan code. Then, we should compute - // the code value with virtual keycode. - case 0xE000: - switch (aVirtualKeyCode) { - case VK_BROWSER_BACK: - return CODE_NAME_INDEX_BrowserBack; - case VK_BROWSER_FAVORITES: - return CODE_NAME_INDEX_BrowserFavorites; - case VK_BROWSER_FORWARD: - return CODE_NAME_INDEX_BrowserForward; - case VK_BROWSER_HOME: - return CODE_NAME_INDEX_BrowserHome; - case VK_BROWSER_REFRESH: - return CODE_NAME_INDEX_BrowserRefresh; - case VK_BROWSER_SEARCH: - return CODE_NAME_INDEX_BrowserSearch; - case VK_BROWSER_STOP: - return CODE_NAME_INDEX_BrowserStop; - case VK_LAUNCH_APP1: // my computer - return CODE_NAME_INDEX_LaunchApp1; - case VK_LAUNCH_APP2: // calculator - return CODE_NAME_INDEX_LaunchApp2; - case VK_LAUNCH_MAIL: - return CODE_NAME_INDEX_LaunchMail; - case VK_LAUNCH_MEDIA_SELECT: - return CODE_NAME_INDEX_MediaSelect; - case VK_MEDIA_PLAY_PAUSE: - return CODE_NAME_INDEX_MediaPlayPause; - case VK_MEDIA_STOP: - return CODE_NAME_INDEX_MediaStop; - case VK_MEDIA_NEXT_TRACK: - return CODE_NAME_INDEX_MediaTrackNext; - case VK_MEDIA_PREV_TRACK: - return CODE_NAME_INDEX_MediaTrackPrevious; - case VK_VOLUME_MUTE: - return CODE_NAME_INDEX_VolumeMute; - case VK_VOLUME_DOWN: - return CODE_NAME_INDEX_VolumeDown; - case VK_VOLUME_UP: - return CODE_NAME_INDEX_VolumeUp; - default: - return CODE_NAME_INDEX_UNKNOWN; - } default: return CODE_NAME_INDEX_UNKNOWN; } diff --git a/widget/windows/KeyboardLayout.h b/widget/windows/KeyboardLayout.h index 45e413d60b3..e9a39197b5f 100644 --- a/widget/windows/KeyboardLayout.h +++ b/widget/windows/KeyboardLayout.h @@ -382,6 +382,11 @@ private: */ bool GetFollowingCharMessage(MSG& aCharMsg) const; + /** + * Whether the key event can compute virtual keycode from the scancode value. + */ + bool CanComputeVirtualKeyCodeFromScanCode() const; + /** * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK. */ @@ -392,6 +397,11 @@ private: */ uint8_t ComputeVirtualKeyCodeFromScanCodeEx() const; + /** + * Wraps MapVirtualKeyEx() with MAPVK_VK_TO_VSC_EX or MAPVK_VK_TO_VSC. + */ + uint16_t ComputeScanCodeExFromVirtualKeyCode(UINT aVirtualKeyCode) const; + /** * Wraps MapVirtualKeyEx() with MAPVK_VSC_TO_VK and MAPVK_VK_TO_CHAR. */ @@ -565,13 +575,8 @@ public: * ConvertScanCodeToCodeNameIndex() returns CodeNameIndex value for * the given scan code. aScanCode can be over 0xE000 since this method * doesn't use Windows API. - * - * NOTE: Some special keys always generate 0xE000 for the scan code but - * the virtual keycode indicates the key. In such case, this method - * computes CodeNameIndex from aVirtualKeyCode. */ - static CodeNameIndex ConvertScanCodeToCodeNameIndex(UINT aScanCode, - UINT aVirtualKeyCode); + static CodeNameIndex ConvertScanCodeToCodeNameIndex(UINT aScanCode); HKL GetLayout() const {