mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge m-c to f-t
This commit is contained in:
commit
341e5aef60
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -19,7 +19,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="platform_hardware_ril" path="hardware/ril" remote="b2g" revision="d5d3f93914558b6f168447b805cd799c8233e300"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<project name="platform_build" path="build" remote="b2g" revision="3ab0d9c70f0b2e1ededc679112c392303f037361">
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
|
||||
|
@ -4,6 +4,6 @@
|
||||
"remote": "",
|
||||
"branch": ""
|
||||
},
|
||||
"revision": "78d735b50d94254ff236fc34a6fbaa5ed27692a0",
|
||||
"revision": "415520315b048f40979e9bac344bec99e18df901",
|
||||
"repo_path": "integration/gaia-central"
|
||||
}
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
|
@ -15,7 +15,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
</project>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="fake-libdvm" path="dalvik" remote="b2g" revision="d50ae982b19f42f0b66d08b9eb306be81687869f"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="moztt" path="external/moztt" remote="b2g" revision="fe893bb760a3bb64375f62fdf4762a58c59df9ef"/>
|
||||
<project name="apitrace" path="external/apitrace" remote="apitrace" revision="6499615ecece69e726657dc5caaeefa05fbb66bf"/>
|
||||
|
@ -17,7 +17,7 @@
|
||||
<copyfile dest="Makefile" src="core/root.mk"/>
|
||||
</project>
|
||||
<project name="fake-dalvik" path="dalvik" remote="b2g" revision="ca1f327d5acc198bb4be62fa51db2c039032c9ce"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="80bc1445959db79e9d2e947cc56e1eb7b0d3d0f0"/>
|
||||
<project name="gaia.git" path="gaia" remote="mozillaorg" revision="1fff49c664f905f11a86426a9835e6df6b58e825"/>
|
||||
<project name="gonk-misc" path="gonk-misc" remote="b2g" revision="50ad16a280fe9cfa0716f8c6ba16afdf7f266b49"/>
|
||||
<project name="rilproxy" path="rilproxy" remote="b2g" revision="827214fcf38d6569aeb5c6d6f31cb296d1f09272"/>
|
||||
<project name="librecovery" path="librecovery" remote="b2g" revision="891e5069c0ad330d8191bf8c7b879c814258c89f"/>
|
||||
|
@ -488,3 +488,4 @@ skip-if = e10s # Bug 1100687 - test directly manipulates content (content.docume
|
||||
skip-if = e10s # bug 1100687 - test directly manipulates content (content.document.getElementById)
|
||||
[browser_mcb_redirect.js]
|
||||
skip-if = e10s # bug 1084504 - [e10s] Mixed content detection does not take redirection into account
|
||||
[browser_windowactivation.js]
|
||||
|
152
browser/base/content/test/general/browser_windowactivation.js
Normal file
152
browser/base/content/test/general/browser_windowactivation.js
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* This test checks that window activation state is set properly with multiple tabs.
|
||||
*/
|
||||
|
||||
let testPage = "data:text/html,<body><style>:-moz-window-inactive { background-color: red; }</style><div id='area'></div></body>";
|
||||
|
||||
let colorChangeNotifications = 0;
|
||||
let otherWindow;
|
||||
|
||||
let browser1, browser2;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
|
||||
let tab1 = gBrowser.addTab();
|
||||
let tab2 = gBrowser.addTab();
|
||||
browser1 = gBrowser.getBrowserForTab(tab1);
|
||||
browser2 = gBrowser.getBrowserForTab(tab2);
|
||||
|
||||
gURLBar.focus();
|
||||
|
||||
var loadCount = 0;
|
||||
function check()
|
||||
{
|
||||
// wait for both tabs to load
|
||||
if (++loadCount != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
browser1.removeEventListener("load", check, true);
|
||||
browser2.removeEventListener("load", check, true);
|
||||
|
||||
sendGetBackgroundRequest(true);
|
||||
}
|
||||
|
||||
// The test performs four checks, using -moz-window-inactive on two child tabs.
|
||||
// First, the initial state should be transparent. The second check is done
|
||||
// while another window is focused. The third check is done after that window
|
||||
// is closed and the main window focused again. The fourth check is done after
|
||||
// switching to the second tab.
|
||||
window.messageManager.addMessageListener("Test:BackgroundColorChanged", function(message) {
|
||||
colorChangeNotifications++;
|
||||
|
||||
switch (colorChangeNotifications) {
|
||||
case 1:
|
||||
is(message.data.color, "transparent", "first window initial");
|
||||
break;
|
||||
case 2:
|
||||
is(message.data.color, "transparent", "second window initial");
|
||||
runOtherWindowTests();
|
||||
break;
|
||||
case 3:
|
||||
is(message.data.color, "rgb(255, 0, 0)", "first window lowered");
|
||||
break;
|
||||
case 4:
|
||||
is(message.data.color, "rgb(255, 0, 0)", "second window lowered");
|
||||
sendGetBackgroundRequest(true);
|
||||
otherWindow.close();
|
||||
break;
|
||||
case 5:
|
||||
is(message.data.color, "transparent", "first window raised");
|
||||
break;
|
||||
case 6:
|
||||
is(message.data.color, "transparent", "second window raised");
|
||||
gBrowser.selectedTab = tab2;
|
||||
break;
|
||||
case 7:
|
||||
is(message.data.color, "transparent", "first window after tab switch");
|
||||
break;
|
||||
case 8:
|
||||
is(message.data.color, "transparent", "second window after tab switch");
|
||||
finishTest();
|
||||
break;
|
||||
case 9:
|
||||
ok(false, "too many color change notifications");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
window.messageManager.addMessageListener("Test:FocusReceived", function(message) {
|
||||
// No color change should occur after a tab switch.
|
||||
if (colorChangeNotifications == 6) {
|
||||
sendGetBackgroundRequest(false);
|
||||
}
|
||||
});
|
||||
|
||||
browser1.addEventListener("load", check, true);
|
||||
browser2.addEventListener("load", check, true);
|
||||
browser1.contentWindow.location = testPage;
|
||||
browser2.contentWindow.location = testPage;
|
||||
|
||||
browser1.messageManager.loadFrameScript("data:,(" + childFunction.toString() + ")();", true);
|
||||
browser2.messageManager.loadFrameScript("data:,(" + childFunction.toString() + ")();", true);
|
||||
|
||||
gBrowser.selectedTab = tab1;
|
||||
}
|
||||
|
||||
function sendGetBackgroundRequest(ifChanged)
|
||||
{
|
||||
browser1.messageManager.sendAsyncMessage("Test:GetBackgroundColor", { ifChanged: ifChanged });
|
||||
browser2.messageManager.sendAsyncMessage("Test:GetBackgroundColor", { ifChanged: ifChanged });
|
||||
}
|
||||
|
||||
function runOtherWindowTests() {
|
||||
otherWindow = window.open("data:text/html,<body>Hi</body>", "", "chrome");
|
||||
waitForFocus(function () {
|
||||
sendGetBackgroundRequest(true);
|
||||
}, otherWindow);
|
||||
}
|
||||
|
||||
function finishTest()
|
||||
{
|
||||
gBrowser.removeCurrentTab();
|
||||
gBrowser.removeCurrentTab();
|
||||
otherWindow = null;
|
||||
finish();
|
||||
}
|
||||
|
||||
function childFunction()
|
||||
{
|
||||
let oldColor = null;
|
||||
|
||||
let expectingResponse = false;
|
||||
let ifChanged = true;
|
||||
|
||||
addMessageListener("Test:GetBackgroundColor", function(message) {
|
||||
expectingResponse = true;
|
||||
ifChanged = message.data.ifChanged;
|
||||
});
|
||||
|
||||
content.addEventListener("focus", function () {
|
||||
sendAsyncMessage("Test:FocusReceived", { });
|
||||
}, false);
|
||||
|
||||
content.setInterval(function () {
|
||||
if (!expectingResponse) {
|
||||
return;
|
||||
}
|
||||
|
||||
let area = content.document.getElementById("area");
|
||||
if (!area) {
|
||||
return; /* hasn't loaded yet */
|
||||
}
|
||||
|
||||
let color = content.getComputedStyle(area, "").backgroundColor;
|
||||
if (oldColor != color || !ifChanged) {
|
||||
expectingResponse = false;
|
||||
oldColor = color;
|
||||
sendAsyncMessage("Test:BackgroundColorChanged", { color: color });
|
||||
}
|
||||
}, 20);
|
||||
}
|
@ -41,6 +41,7 @@
|
||||
#include "mozilla/dom/HTMLShadowElement.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/dom/TabParent.h"
|
||||
#include "mozilla/dom/TextDecoder.h"
|
||||
#include "mozilla/dom/TouchEvent.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
@ -98,6 +99,7 @@
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIDocumentEncoder.h"
|
||||
#include "nsIDOMChromeWindow.h"
|
||||
#include "nsIDOMDocument.h"
|
||||
#include "nsIDOMDocumentType.h"
|
||||
#include "nsIDOMEvent.h"
|
||||
@ -180,6 +182,11 @@
|
||||
|
||||
#include "nsIBidiKeyboard.h"
|
||||
|
||||
#if defined(XP_WIN)
|
||||
// Undefine LoadImage to prevent naming conflict with Windows.
|
||||
#undef LoadImage
|
||||
#endif
|
||||
|
||||
extern "C" int MOZ_XMLTranslateEntity(const char* ptr, const char* end,
|
||||
const char** next, char16_t* result);
|
||||
extern "C" int MOZ_XMLCheckQName(const char* ptr, const char* end,
|
||||
@ -7067,3 +7074,54 @@ nsContentUtils::GetHostOrIPv6WithBrackets(nsIURI* aURI, nsAString& aHost)
|
||||
|
||||
CopyUTF8toUTF16(hostname, aHost);
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::CallOnAllRemoteChildren(nsIMessageBroadcaster* aManager,
|
||||
CallOnRemoteChildFunction aCallback,
|
||||
void* aArg)
|
||||
{
|
||||
uint32_t tabChildCount = 0;
|
||||
aManager->GetChildCount(&tabChildCount);
|
||||
for (uint32_t j = 0; j < tabChildCount; ++j) {
|
||||
nsCOMPtr<nsIMessageListenerManager> childMM;
|
||||
aManager->GetChildAt(j, getter_AddRefs(childMM));
|
||||
if (!childMM) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMessageBroadcaster> nonLeafMM = do_QueryInterface(childMM);
|
||||
if (nonLeafMM) {
|
||||
CallOnAllRemoteChildren(nonLeafMM, aCallback, aArg);
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMessageSender> tabMM = do_QueryInterface(childMM);
|
||||
|
||||
mozilla::dom::ipc::MessageManagerCallback* cb =
|
||||
static_cast<nsFrameMessageManager*>(tabMM.get())->GetCallback();
|
||||
if (cb) {
|
||||
nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
|
||||
PBrowserParent* remoteBrowser = fl->GetRemoteBrowser();
|
||||
TabParent* remote = static_cast<TabParent*>(remoteBrowser);
|
||||
if (remote && aCallback) {
|
||||
aCallback(remote, aArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::CallOnAllRemoteChildren(nsIDOMWindow* aWindow,
|
||||
CallOnRemoteChildFunction aCallback,
|
||||
void* aArg)
|
||||
{
|
||||
nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(aWindow));
|
||||
if (chromeWindow) {
|
||||
nsCOMPtr<nsIMessageBroadcaster> windowMM;
|
||||
chromeWindow->GetMessageManager(getter_AddRefs(windowMM));
|
||||
if (windowMM) {
|
||||
CallOnAllRemoteChildren(windowMM, aCallback, aArg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -73,6 +73,7 @@ class nsIInterfaceRequestor;
|
||||
class nsIIOService;
|
||||
class nsIJSRuntimeService;
|
||||
class nsILineBreaker;
|
||||
class nsIMessageBroadcaster;
|
||||
class nsNameSpaceManager;
|
||||
class nsIObserver;
|
||||
class nsIParser;
|
||||
@ -121,6 +122,7 @@ class Element;
|
||||
class EventTarget;
|
||||
class NodeInfo;
|
||||
class Selection;
|
||||
class TabParent;
|
||||
} // namespace dom
|
||||
|
||||
namespace layers {
|
||||
@ -168,6 +170,9 @@ struct nsShortcutCandidate {
|
||||
bool mIgnoreShift;
|
||||
};
|
||||
|
||||
typedef void (*CallOnRemoteChildFunction) (mozilla::dom::TabParent* aTabParent,
|
||||
void* aArg);
|
||||
|
||||
class nsContentUtils
|
||||
{
|
||||
friend class nsAutoScriptBlockerSuppressNodeRemoved;
|
||||
@ -2211,6 +2216,14 @@ public:
|
||||
*/
|
||||
static void GetHostOrIPv6WithBrackets(nsIURI* aURI, nsAString& aHost);
|
||||
|
||||
/*
|
||||
* Call the given callback on all remote children of the given top-level
|
||||
* window.
|
||||
*/
|
||||
static void CallOnAllRemoteChildren(nsIDOMWindow* aWindow,
|
||||
CallOnRemoteChildFunction aCallback,
|
||||
void* aArg);
|
||||
|
||||
private:
|
||||
static bool InitializeEventTable();
|
||||
|
||||
@ -2246,6 +2259,10 @@ private:
|
||||
static AutocompleteAttrState InternalSerializeAutocompleteAttribute(const nsAttrValue* aAttrVal,
|
||||
mozilla::dom::AutocompleteInfo& aInfo);
|
||||
|
||||
static void CallOnAllRemoteChildren(nsIMessageBroadcaster* aManager,
|
||||
CallOnRemoteChildFunction aCallback,
|
||||
void* aArg);
|
||||
|
||||
static nsIXPConnect *sXPConnect;
|
||||
|
||||
static nsIScriptSecurityManager *sSecurityManager;
|
||||
|
@ -47,6 +47,7 @@
|
||||
#include "mozilla/LookAndFeel.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/unused.h"
|
||||
#include <algorithm>
|
||||
|
||||
#ifdef MOZ_XUL
|
||||
@ -172,6 +173,7 @@ static const char* kObservedPrefs[] = {
|
||||
};
|
||||
|
||||
nsFocusManager::nsFocusManager()
|
||||
: mParentFocusType(ParentFocusType_Ignore)
|
||||
{ }
|
||||
|
||||
nsFocusManager::~nsFocusManager()
|
||||
@ -706,15 +708,12 @@ nsFocusManager::WindowRaised(nsIDOMWindow* aWindow)
|
||||
}
|
||||
}
|
||||
|
||||
// inform the DOM window that it has activated, so that the active attribute
|
||||
// is updated on the window
|
||||
window->ActivateOrDeactivate(true);
|
||||
|
||||
// send activate event
|
||||
nsContentUtils::DispatchTrustedEvent(window->GetExtantDoc(),
|
||||
window,
|
||||
NS_LITERAL_STRING("activate"),
|
||||
true, true, nullptr);
|
||||
// If this is a parent or single process window, send the activate event.
|
||||
// Events for child process windows will be sent when ParentActivated
|
||||
// is called.
|
||||
if (mParentFocusType == ParentFocusType_Ignore) {
|
||||
ActivateOrDeactivate(window, true);
|
||||
}
|
||||
|
||||
// retrieve the last focused element within the window that was raised
|
||||
nsCOMPtr<nsPIDOMWindow> currentWindow;
|
||||
@ -771,15 +770,12 @@ nsFocusManager::WindowLowered(nsIDOMWindow* aWindow)
|
||||
// clear the mouse capture as the active window has changed
|
||||
nsIPresShell::SetCapturingContent(nullptr, 0);
|
||||
|
||||
// inform the DOM window that it has deactivated, so that the active
|
||||
// attribute is updated on the window
|
||||
window->ActivateOrDeactivate(false);
|
||||
|
||||
// send deactivate event
|
||||
nsContentUtils::DispatchTrustedEvent(window->GetExtantDoc(),
|
||||
window,
|
||||
NS_LITERAL_STRING("deactivate"),
|
||||
true, true, nullptr);
|
||||
// If this is a parent or single process window, send the deactivate event.
|
||||
// Events for child process windows will be sent when ParentActivated
|
||||
// is called.
|
||||
if (mParentFocusType == ParentFocusType_Ignore) {
|
||||
ActivateOrDeactivate(window, false);
|
||||
}
|
||||
|
||||
// keep track of the window being lowered, so that attempts to raise the
|
||||
// window can be prevented until we return. Otherwise, focus can get into
|
||||
@ -906,6 +902,10 @@ nsFocusManager::WindowShown(nsIDOMWindow* aWindow, bool aNeedsFocus)
|
||||
EnsureCurrentWidgetFocused();
|
||||
}
|
||||
|
||||
if (mParentFocusType == ParentFocusType_Active) {
|
||||
ActivateOrDeactivate(window, true);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@ -1065,6 +1065,19 @@ nsFocusManager::FocusPlugin(nsIContent* aContent)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsFocusManager::ParentActivated(nsIDOMWindow* aWindow, bool aActive)
|
||||
{
|
||||
nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow);
|
||||
NS_ENSURE_TRUE(window, NS_ERROR_INVALID_ARG);
|
||||
|
||||
window = window->GetOuterWindow();
|
||||
|
||||
mParentFocusType = aActive ? ParentFocusType_Active : ParentFocusType_Inactive;
|
||||
ActivateOrDeactivate(window, aActive);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsFocusManager::NotifyFocusStateChange(nsIContent* aContent,
|
||||
@ -1109,6 +1122,36 @@ nsFocusManager::EnsureCurrentWidgetFocused()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ActivateOrDeactivateChild(TabParent* aParent, void* aArg)
|
||||
{
|
||||
bool active = static_cast<bool>(aArg);
|
||||
unused << aParent->SendParentActivated(active);
|
||||
}
|
||||
|
||||
void
|
||||
nsFocusManager::ActivateOrDeactivate(nsPIDOMWindow* aWindow, bool aActive)
|
||||
{
|
||||
if (!aWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Inform the DOM window that it has activated or deactivated, so that
|
||||
// the active attribute is updated on the window.
|
||||
aWindow->ActivateOrDeactivate(aActive);
|
||||
|
||||
// Send the activate event.
|
||||
nsContentUtils::DispatchTrustedEvent(aWindow->GetExtantDoc(),
|
||||
aWindow,
|
||||
aActive ? NS_LITERAL_STRING("activate") :
|
||||
NS_LITERAL_STRING("deactivate"),
|
||||
true, true, nullptr);
|
||||
|
||||
// Look for any remote child frames, iterate over them and send the activation notification.
|
||||
nsContentUtils::CallOnAllRemoteChildren(aWindow, ActivateOrDeactivateChild,
|
||||
(void *)aActive);
|
||||
}
|
||||
|
||||
void
|
||||
nsFocusManager::SetFocusInner(nsIContent* aNewContent, int32_t aFlags,
|
||||
bool aFocusChanged, bool aAdjustWidget)
|
||||
|
@ -22,9 +22,16 @@
|
||||
class nsIContent;
|
||||
class nsIDocShellTreeItem;
|
||||
class nsPIDOMWindow;
|
||||
class nsIMessageBroadcaster;
|
||||
|
||||
struct nsDelayedBlurOrFocusEvent;
|
||||
|
||||
enum ParentFocusType {
|
||||
ParentFocusType_Ignore, // Parent or single process window or unknown
|
||||
ParentFocusType_Active, // Child process window in active parent
|
||||
ParentFocusType_Inactive, // Child process window in inactive parent
|
||||
};
|
||||
|
||||
/**
|
||||
* The focus manager keeps track of where the focus is, that is, the node
|
||||
* which receives key events.
|
||||
@ -90,6 +97,15 @@ public:
|
||||
*/
|
||||
void UpdateCaretForCaretBrowsingMode();
|
||||
|
||||
bool IsParentActivated()
|
||||
{
|
||||
if (mParentFocusType == ParentFocusType_Ignore) {
|
||||
return mActiveWindow != nullptr;
|
||||
}
|
||||
|
||||
return mParentFocusType == ParentFocusType_Active;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the content node that would be focused if aWindow was in an
|
||||
* active window. This will traverse down the frame hierarchy, starting at
|
||||
@ -134,6 +150,17 @@ protected:
|
||||
*/
|
||||
void EnsureCurrentWidgetFocused();
|
||||
|
||||
/**
|
||||
* Iterate over the children of the message broadcaster and notify them
|
||||
* of the activation change.
|
||||
*/
|
||||
void ActivateOrDeactivateChildren(nsIMessageBroadcaster* aManager, bool aActive);
|
||||
|
||||
/**
|
||||
* Activate or deactivate the window and send the activate/deactivate events.
|
||||
*/
|
||||
void ActivateOrDeactivate(nsPIDOMWindow* aWindow, bool aActive);
|
||||
|
||||
/**
|
||||
* Blur whatever is currently focused and focus aNewContent. aFlags is a
|
||||
* bitmask of the flags defined in nsIFocusManager. If aFocusChanged is
|
||||
@ -526,6 +553,9 @@ private:
|
||||
// moving focus.
|
||||
nsCOMPtr<nsIDocument> mMouseButtonEventHandlingDocument;
|
||||
|
||||
// Indicates a child process that is in an active window.
|
||||
ParentFocusType mParentFocusType;
|
||||
|
||||
static bool sTestMode;
|
||||
|
||||
// the single focus manager
|
||||
|
@ -1234,6 +1234,12 @@ nsGlobalWindow::nsGlobalWindow(nsGlobalWindow *aOuterWindow)
|
||||
if (sWindowsById) {
|
||||
sWindowsById->Put(mWindowID, this);
|
||||
}
|
||||
|
||||
// Ensure that the current active state is initialized for child process windows.
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
if (fm) {
|
||||
mIsActive = fm->IsParentActivated();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
@ -9821,17 +9827,21 @@ nsGlobalWindow::ActivateOrDeactivate(bool aActivate)
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
if (!mDoc) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set / unset mIsActive on the top level window, which is used for the
|
||||
// :-moz-window-inactive pseudoclass, and its sheet (if any).
|
||||
nsCOMPtr<nsIWidget> mainWidget = GetMainWidget();
|
||||
if (!mainWidget)
|
||||
return;
|
||||
|
||||
// Get the top level widget (if the main widget is a sheet, this will
|
||||
// be the sheet's top (non-sheet) parent).
|
||||
nsCOMPtr<nsIWidget> topLevelWidget = mainWidget->GetSheetWindowParent();
|
||||
if (!topLevelWidget) {
|
||||
topLevelWidget = mainWidget;
|
||||
nsCOMPtr<nsIWidget> topLevelWidget;
|
||||
if (mainWidget) {
|
||||
// Get the top level widget (if the main widget is a sheet, this will
|
||||
// be the sheet's top (non-sheet) parent).
|
||||
topLevelWidget = mainWidget->GetSheetWindowParent();
|
||||
if (!topLevelWidget) {
|
||||
topLevelWidget = mainWidget;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindow> piMainWindow(
|
||||
|
@ -187,6 +187,7 @@ static uint32_t sCleanupsSinceLastGC = UINT32_MAX;
|
||||
static bool sNeedsFullCC = false;
|
||||
static bool sNeedsGCAfterCC = false;
|
||||
static bool sIncrementalCC = false;
|
||||
static bool sDidPaintAfterPreviousICCSlice = false;
|
||||
|
||||
static nsScriptNameSpaceManager *gNameSpaceManager;
|
||||
|
||||
@ -1708,7 +1709,8 @@ nsJSContext::RunCycleCollectorSlice()
|
||||
}
|
||||
}
|
||||
|
||||
nsCycleCollector_collectSlice(budget);
|
||||
nsCycleCollector_collectSlice(budget, sDidPaintAfterPreviousICCSlice);
|
||||
sDidPaintAfterPreviousICCSlice = false;
|
||||
|
||||
gCCStats.FinishCycleCollectionSlice();
|
||||
}
|
||||
@ -2856,6 +2858,12 @@ nsJSContext::EnsureStatics()
|
||||
sIsInitialized = true;
|
||||
}
|
||||
|
||||
void
|
||||
nsJSContext::NotifyDidPaint()
|
||||
{
|
||||
sDidPaintAfterPreviousICCSlice = true;
|
||||
}
|
||||
|
||||
nsScriptNameSpaceManager*
|
||||
mozilla::dom::GetNameSpaceManager()
|
||||
{
|
||||
|
@ -136,6 +136,8 @@ public:
|
||||
JSObject* global = GetWindowProxy();
|
||||
return global ? mGlobalObjectRef.get() : nullptr;
|
||||
}
|
||||
|
||||
static void NotifyDidPaint();
|
||||
protected:
|
||||
virtual ~nsJSContext();
|
||||
|
||||
|
@ -8,7 +8,7 @@
|
||||
interface nsIDocument;
|
||||
interface nsIContent;
|
||||
|
||||
[scriptable, uuid(51db277b-7ee7-4bce-9b84-fd2efcd2c8bd)]
|
||||
[scriptable, uuid(c0716002-5602-4002-a0de-cc69b924b2c6)]
|
||||
/**
|
||||
* The focus manager deals with all focus related behaviour. Only one element
|
||||
* in the entire application may have the focus at a time; this element
|
||||
@ -250,4 +250,10 @@ interface nsIFocusManager : nsISupports
|
||||
* widget focus state is the responsibility of the caller.
|
||||
*/
|
||||
[noscript] void focusPlugin(in nsIContent aPlugin);
|
||||
|
||||
/**
|
||||
* Used in a child process to indicate that the parent window is now
|
||||
* active or deactive.
|
||||
*/
|
||||
[noscript] void parentActivated(in nsIDOMWindow aWindow, in bool active);
|
||||
};
|
||||
|
@ -442,6 +442,8 @@ child:
|
||||
|
||||
Deactivate();
|
||||
|
||||
ParentActivated(bool aActivated);
|
||||
|
||||
/**
|
||||
* @see nsIDOMWindowUtils sendMouseEvent.
|
||||
*/
|
||||
|
@ -52,6 +52,7 @@
|
||||
#include "nsIDOMEvent.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIDOMWindowUtils.h"
|
||||
#include "nsFocusManager.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIFrame.h"
|
||||
#include "nsIURI.h"
|
||||
@ -2222,6 +2223,16 @@ bool TabChild::RecvDeactivate()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TabChild::RecvParentActivated(const bool& aActivated)
|
||||
{
|
||||
nsFocusManager* fm = nsFocusManager::GetFocusManager();
|
||||
NS_ENSURE_TRUE(fm, true);
|
||||
|
||||
nsCOMPtr<nsIDOMWindow> window = do_GetInterface(WebNavigation());
|
||||
fm->ParentActivated(window, aActivated);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
TabChild::RecvMouseEvent(const nsString& aType,
|
||||
const float& aX,
|
||||
|
@ -503,6 +503,8 @@ protected:
|
||||
|
||||
virtual bool RecvRequestNotifyAfterRemotePaint();
|
||||
|
||||
virtual bool RecvParentActivated(const bool& aActivated) MOZ_OVERRIDE;
|
||||
|
||||
#ifdef MOZ_WIDGET_GONK
|
||||
void MaybeRequestPreinitCamera();
|
||||
#endif
|
||||
|
@ -609,17 +609,9 @@ Nfc.prototype = {
|
||||
* Process a message from the gMessageManager.
|
||||
*/
|
||||
receiveMessage: function receiveMessage(message) {
|
||||
let isRFAPI = message.name == "NFC:ChangeRFState";
|
||||
let isSendFile = message.name == "NFC:SendFile";
|
||||
let isInfoAPI = message.name == "NFC:QueryInfo";
|
||||
|
||||
if (!isRFAPI && !isInfoAPI && (this.rfState != NFC.NFC_RF_STATE_DISCOVERY)) {
|
||||
debug("NFC is not enabled. current rfState:" + this.rfState);
|
||||
this.sendNfcErrorResponse(message, NFC.NFC_GECKO_ERROR_NOT_ENABLED);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!isRFAPI && !isSendFile && !isInfoAPI) {
|
||||
if (["NFC:ChangeRFState",
|
||||
"NFC:SendFile",
|
||||
"NFC:QueryInfo"].indexOf(message.name) == -1) {
|
||||
// Update the current sessionId before sending to the NFC service.
|
||||
message.data.sessionId = SessionHelper.getId(message.data.sessionToken);
|
||||
}
|
||||
|
@ -26,14 +26,12 @@ this.DEBUG_NFC = DEBUG_ALL || false;
|
||||
this.NFC_GECKO_SUCCESS = 0;
|
||||
this.NFC_GECKO_ERROR_GENERIC_FAILURE = 1;
|
||||
this.NFC_GECKO_ERROR_P2P_REG_INVALID = 2;
|
||||
this.NFC_GECKO_ERROR_NOT_ENABLED = 3;
|
||||
this.NFC_GECKO_ERROR_SEND_FILE_FAILED = 4;
|
||||
this.NFC_GECKO_ERROR_BAD_SESSION_TOKEN = 5;
|
||||
this.NFC_GECKO_ERROR_SEND_FILE_FAILED = 3;
|
||||
this.NFC_GECKO_ERROR_BAD_SESSION_TOKEN = 4;
|
||||
|
||||
this.NFC_ERROR_MSG = {};
|
||||
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_GENERIC_FAILURE] = "NfcGenericFailureError";
|
||||
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_P2P_REG_INVALID] = "NfcP2PRegistrationInvalid";
|
||||
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_NOT_ENABLED] = "NfcNotEnabledError";
|
||||
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_SEND_FILE_FAILED] = "NfcSendFileFailed";
|
||||
this.NFC_ERROR_MSG[this.NFC_GECKO_ERROR_BAD_SESSION_TOKEN] = "NfcBadSessionToken";
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
if (!this.hasOwnProperty("TypedObject"))
|
||||
quit();
|
||||
|
||||
gczeal(2);
|
||||
var Vec3u16Type = TypedObject.uint16.array(3);
|
||||
|
@ -1,3 +1,5 @@
|
||||
if (!this.hasOwnProperty("TypedObject"))
|
||||
quit();
|
||||
|
||||
var Vec3u16Type = TypedObject.uint16.array(3);
|
||||
|
||||
|
@ -3438,6 +3438,7 @@ IonBuilder::improveTypesAtTest(MDefinition *ins, bool trueBranch, MTest *test)
|
||||
if (!ins->resultTypeSet() || ins->resultTypeSet()->unknown())
|
||||
return true;
|
||||
|
||||
types::TemporaryTypeSet *oldType = ins->resultTypeSet();
|
||||
types::TemporaryTypeSet *type;
|
||||
|
||||
// Decide either to set or filter.
|
||||
@ -3448,27 +3449,30 @@ IonBuilder::improveTypesAtTest(MDefinition *ins, bool trueBranch, MTest *test)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
type = ins->resultTypeSet()->filter(alloc_->lifoAlloc(), true, true);
|
||||
type = oldType->filter(alloc_->lifoAlloc(), true, true);
|
||||
} else {
|
||||
// According to the standards, we cannot filter out:
|
||||
// Strings, Int32, Double, Booleans, Objects (if they emulate undefined)
|
||||
// According to the standards, we cannot filter out: Strings,
|
||||
// Int32, Double, Booleans, Objects (if they emulate undefined)
|
||||
uint32_t flags = types::TYPE_FLAG_PRIMITIVE;
|
||||
|
||||
// If the typeset does not emulate undefined. Then we can filter out objects.
|
||||
if (!ins->resultTypeSet()->maybeEmulatesUndefined())
|
||||
flags &= ~types::TYPE_FLAG_ANYOBJECT;
|
||||
// If the typeset does emulate undefined, then we cannot filter out
|
||||
// objects.
|
||||
if (oldType->maybeEmulatesUndefined())
|
||||
flags |= types::TYPE_FLAG_ANYOBJECT;
|
||||
|
||||
// Only intersect the typesets if it will generate a more narrow typeset.
|
||||
if (!ins->resultTypeSet()->hasAnyFlag(~flags & types::TYPE_FLAG_BASE_MASK) &&
|
||||
ins->resultTypeSet()->getObjectCount() == 0)
|
||||
// Only intersect the typesets if it will generate a more narrow
|
||||
// typeset. The first part takes care of primitives and AnyObject,
|
||||
// while the second line specific (type)objects.
|
||||
if (!oldType->hasAnyFlag(~flags & types::TYPE_FLAG_BASE_MASK) &&
|
||||
(oldType->maybeEmulatesUndefined() || !oldType->maybeObject()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
types::TemporaryTypeSet base(flags, static_cast<types::TypeObjectKey**>(nullptr));
|
||||
type = types::TypeSet::intersectSets(&base, ins->resultTypeSet(), alloc_->lifoAlloc());
|
||||
replaceTypeSet(ins, type, test);
|
||||
type = types::TypeSet::intersectSets(&base, oldType, alloc_->lifoAlloc());
|
||||
}
|
||||
replaceTypeSet(ins, type, test);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4354,27 +4354,27 @@ nsLayoutUtils::ComputeCBDependentValue(nscoord aPercentBasis,
|
||||
}
|
||||
|
||||
/* static */ nscoord
|
||||
nsLayoutUtils::ComputeWidthValue(
|
||||
nsLayoutUtils::ComputeISizeValue(
|
||||
nsRenderingContext* aRenderingContext,
|
||||
nsIFrame* aFrame,
|
||||
nscoord aContainingBlockWidth,
|
||||
nscoord aContainingBlockISize,
|
||||
nscoord aContentEdgeToBoxSizing,
|
||||
nscoord aBoxSizingToMarginEdge,
|
||||
const nsStyleCoord& aCoord)
|
||||
{
|
||||
NS_PRECONDITION(aFrame, "non-null frame expected");
|
||||
NS_PRECONDITION(aRenderingContext, "non-null rendering context expected");
|
||||
NS_WARN_IF_FALSE(aContainingBlockWidth != NS_UNCONSTRAINEDSIZE,
|
||||
"have unconstrained width; this should only result from "
|
||||
"very large sizes, not attempts at intrinsic width "
|
||||
NS_WARN_IF_FALSE(aContainingBlockISize != NS_UNCONSTRAINEDSIZE,
|
||||
"have unconstrained inline-size; this should only result from "
|
||||
"very large sizes, not attempts at intrinsic inline-size "
|
||||
"calculation");
|
||||
NS_PRECONDITION(aContainingBlockWidth >= 0,
|
||||
"width less than zero");
|
||||
NS_PRECONDITION(aContainingBlockISize >= 0,
|
||||
"inline-size less than zero");
|
||||
|
||||
nscoord result;
|
||||
if (aCoord.IsCoordPercentCalcUnit()) {
|
||||
result = nsRuleNode::ComputeCoordPercentCalc(aCoord,
|
||||
aContainingBlockWidth);
|
||||
aContainingBlockISize);
|
||||
// The result of a calc() expression might be less than 0; we
|
||||
// should clamp at runtime (below). (Percentages and coords that
|
||||
// are less than 0 have already been dropped by the parser.)
|
||||
@ -4389,24 +4389,24 @@ nsLayoutUtils::ComputeWidthValue(
|
||||
switch (val) {
|
||||
case NS_STYLE_WIDTH_MAX_CONTENT:
|
||||
result = aFrame->GetPrefISize(aRenderingContext);
|
||||
NS_ASSERTION(result >= 0, "width less than zero");
|
||||
NS_ASSERTION(result >= 0, "inline-size less than zero");
|
||||
break;
|
||||
case NS_STYLE_WIDTH_MIN_CONTENT:
|
||||
result = aFrame->GetMinISize(aRenderingContext);
|
||||
NS_ASSERTION(result >= 0, "width less than zero");
|
||||
NS_ASSERTION(result >= 0, "inline-size less than zero");
|
||||
break;
|
||||
case NS_STYLE_WIDTH_FIT_CONTENT:
|
||||
{
|
||||
nscoord pref = aFrame->GetPrefISize(aRenderingContext),
|
||||
min = aFrame->GetMinISize(aRenderingContext),
|
||||
fill = aContainingBlockWidth -
|
||||
fill = aContainingBlockISize -
|
||||
(aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
|
||||
result = std::max(min, std::min(pref, fill));
|
||||
NS_ASSERTION(result >= 0, "width less than zero");
|
||||
NS_ASSERTION(result >= 0, "inline-size less than zero");
|
||||
}
|
||||
break;
|
||||
case NS_STYLE_WIDTH_AVAILABLE:
|
||||
result = aContainingBlockWidth -
|
||||
result = aContainingBlockISize -
|
||||
(aBoxSizingToMarginEdge + aContentEdgeToBoxSizing);
|
||||
}
|
||||
}
|
||||
@ -4415,28 +4415,28 @@ nsLayoutUtils::ComputeWidthValue(
|
||||
}
|
||||
|
||||
/* static */ nscoord
|
||||
nsLayoutUtils::ComputeHeightDependentValue(
|
||||
nscoord aContainingBlockHeight,
|
||||
nsLayoutUtils::ComputeBSizeDependentValue(
|
||||
nscoord aContainingBlockBSize,
|
||||
const nsStyleCoord& aCoord)
|
||||
{
|
||||
// XXXldb Some callers explicitly check aContainingBlockHeight
|
||||
// XXXldb Some callers explicitly check aContainingBlockBSize
|
||||
// against NS_AUTOHEIGHT *and* unit against eStyleUnit_Percent or
|
||||
// calc()s containing percents before calling this function.
|
||||
// However, it would be much more likely to catch problems without
|
||||
// the unit conditions.
|
||||
// XXXldb Many callers pass a non-'auto' containing block height when
|
||||
// according to CSS2.1 they should be passing 'auto'.
|
||||
NS_PRECONDITION(NS_AUTOHEIGHT != aContainingBlockHeight ||
|
||||
NS_PRECONDITION(NS_AUTOHEIGHT != aContainingBlockBSize ||
|
||||
!aCoord.HasPercent(),
|
||||
"unexpected containing block height");
|
||||
"unexpected containing block block-size");
|
||||
|
||||
if (aCoord.IsCoordPercentCalcUnit()) {
|
||||
return nsRuleNode::ComputeCoordPercentCalc(aCoord, aContainingBlockHeight);
|
||||
return nsRuleNode::ComputeCoordPercentCalc(aCoord, aContainingBlockBSize);
|
||||
}
|
||||
|
||||
NS_ASSERTION(aCoord.GetUnit() == eStyleUnit_None ||
|
||||
aCoord.GetUnit() == eStyleUnit_Auto,
|
||||
"unexpected height value");
|
||||
"unexpected block-size value");
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -4499,10 +4499,11 @@ nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(WritingMode aWM,
|
||||
const nsStylePosition* stylePos = aFrame->StylePosition();
|
||||
|
||||
// If we're a flex item, we'll compute our size a bit differently.
|
||||
bool isVertical = aWM.IsVertical();
|
||||
const nsStyleCoord* inlineStyleCoord =
|
||||
aWM.IsVertical() ? &stylePos->mHeight : &stylePos->mWidth;
|
||||
isVertical ? &stylePos->mHeight : &stylePos->mWidth;
|
||||
const nsStyleCoord* blockStyleCoord =
|
||||
aWM.IsVertical() ? &stylePos->mWidth : &stylePos->mHeight;
|
||||
isVertical ? &stylePos->mWidth : &stylePos->mHeight;
|
||||
|
||||
bool isFlexItem = aFrame->IsFlexItem();
|
||||
bool isInlineFlexItem = false;
|
||||
@ -4548,7 +4549,7 @@ nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(WritingMode aWM,
|
||||
// or (a * b) / c (which are equivalent).
|
||||
|
||||
const bool isAutoISize = inlineStyleCoord->GetUnit() == eStyleUnit_Auto;
|
||||
const bool isAutoBSize = IsAutoHeight(*blockStyleCoord, aCBSize.BSize(aWM));
|
||||
const bool isAutoBSize = IsAutoBSize(*blockStyleCoord, aCBSize.BSize(aWM));
|
||||
|
||||
LogicalSize boxSizingAdjust(aWM);
|
||||
switch (stylePos->mBoxSizing) {
|
||||
@ -4565,16 +4566,19 @@ nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(WritingMode aWM,
|
||||
nscoord iSize, minISize, maxISize, bSize, minBSize, maxBSize;
|
||||
|
||||
if (!isAutoISize) {
|
||||
iSize = nsLayoutUtils::ComputeWidthValue(aRenderingContext,
|
||||
iSize = nsLayoutUtils::ComputeISizeValue(aRenderingContext,
|
||||
aFrame, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
|
||||
boxSizingToMarginEdgeISize, *inlineStyleCoord);
|
||||
}
|
||||
|
||||
if (stylePos->mMaxWidth.GetUnit() != eStyleUnit_None &&
|
||||
const nsStyleCoord& maxISizeCoord =
|
||||
isVertical ? stylePos->mMaxHeight : stylePos->mMaxWidth;
|
||||
|
||||
if (maxISizeCoord.GetUnit() != eStyleUnit_None &&
|
||||
!(isFlexItem && isInlineFlexItem)) {
|
||||
maxISize = nsLayoutUtils::ComputeWidthValue(aRenderingContext,
|
||||
maxISize = nsLayoutUtils::ComputeISizeValue(aRenderingContext,
|
||||
aFrame, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
|
||||
boxSizingToMarginEdgeISize, stylePos->mMaxWidth);
|
||||
boxSizingToMarginEdgeISize, maxISizeCoord);
|
||||
} else {
|
||||
maxISize = nscoord_MAX;
|
||||
}
|
||||
@ -4582,11 +4586,15 @@ nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(WritingMode aWM,
|
||||
// NOTE: Flex items ignore their min & max sizing properties in their
|
||||
// flex container's main-axis. (Those properties get applied later in
|
||||
// the flexbox algorithm.)
|
||||
if (stylePos->mMinWidth.GetUnit() != eStyleUnit_Auto &&
|
||||
|
||||
const nsStyleCoord& minISizeCoord =
|
||||
isVertical ? stylePos->mMinHeight : stylePos->mMinWidth;
|
||||
|
||||
if (minISizeCoord.GetUnit() != eStyleUnit_Auto &&
|
||||
!(isFlexItem && isInlineFlexItem)) {
|
||||
minISize = nsLayoutUtils::ComputeWidthValue(aRenderingContext,
|
||||
minISize = nsLayoutUtils::ComputeISizeValue(aRenderingContext,
|
||||
aFrame, aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM),
|
||||
boxSizingToMarginEdgeISize, stylePos->mMinWidth);
|
||||
boxSizingToMarginEdgeISize, minISizeCoord);
|
||||
} else {
|
||||
// Treat "min-width: auto" as 0.
|
||||
// NOTE: Technically, "auto" is supposed to behave like "min-content" on
|
||||
@ -4597,25 +4605,29 @@ nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(WritingMode aWM,
|
||||
}
|
||||
|
||||
if (!isAutoBSize) {
|
||||
bSize = nsLayoutUtils::ComputeHeightValue(aCBSize.BSize(aWM),
|
||||
bSize = nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
|
||||
boxSizingAdjust.BSize(aWM),
|
||||
*blockStyleCoord);
|
||||
}
|
||||
|
||||
if (!IsAutoHeight(stylePos->mMaxHeight, aCBSize.BSize(aWM)) &&
|
||||
const nsStyleCoord& maxBSizeCoord =
|
||||
isVertical ? stylePos->mMaxWidth : stylePos->mMaxHeight;
|
||||
|
||||
if (!IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM)) &&
|
||||
!(isFlexItem && !isInlineFlexItem)) {
|
||||
maxBSize = nsLayoutUtils::ComputeHeightValue(aCBSize.BSize(aWM),
|
||||
boxSizingAdjust.BSize(aWM),
|
||||
stylePos->mMaxHeight);
|
||||
maxBSize = nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
|
||||
boxSizingAdjust.BSize(aWM), maxBSizeCoord);
|
||||
} else {
|
||||
maxBSize = nscoord_MAX;
|
||||
}
|
||||
|
||||
if (!IsAutoHeight(stylePos->mMinHeight, aCBSize.BSize(aWM)) &&
|
||||
const nsStyleCoord& minBSizeCoord =
|
||||
isVertical ? stylePos->mMinWidth : stylePos->mMinHeight;
|
||||
|
||||
if (!IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM)) &&
|
||||
!(isFlexItem && !isInlineFlexItem)) {
|
||||
minBSize = nsLayoutUtils::ComputeHeightValue(aCBSize.BSize(aWM),
|
||||
boxSizingAdjust.BSize(aWM),
|
||||
stylePos->mMinHeight);
|
||||
minBSize = nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
|
||||
boxSizingAdjust.BSize(aWM), minBSizeCoord);
|
||||
} else {
|
||||
minBSize = 0;
|
||||
}
|
||||
@ -4625,10 +4637,10 @@ nsLayoutUtils::ComputeSizeWithIntrinsicDimensions(WritingMode aWM,
|
||||
NS_ASSERTION(aCBSize.ISize(aWM) != NS_UNCONSTRAINEDSIZE,
|
||||
"Our containing block must not have unconstrained inline-size!");
|
||||
|
||||
const nsStyleCoord& isizeCoord(aWM.IsVertical() ?
|
||||
aIntrinsicSize.height : aIntrinsicSize.width);
|
||||
const nsStyleCoord& bsizeCoord(aWM.IsVertical() ?
|
||||
aIntrinsicSize.width : aIntrinsicSize.height);
|
||||
const nsStyleCoord& isizeCoord =
|
||||
isVertical ? aIntrinsicSize.height : aIntrinsicSize.width;
|
||||
const nsStyleCoord& bsizeCoord =
|
||||
isVertical ? aIntrinsicSize.width : aIntrinsicSize.height;
|
||||
|
||||
bool hasIntrinsicISize, hasIntrinsicBSize;
|
||||
nscoord intrinsicISize, intrinsicBSize;
|
||||
|
@ -1295,45 +1295,86 @@ public:
|
||||
* and margin that goes outside the rect chosen by box-sizing.
|
||||
* @param aCoord The width value to compute.
|
||||
*/
|
||||
// XXX to be removed
|
||||
static nscoord ComputeWidthValue(
|
||||
nsRenderingContext* aRenderingContext,
|
||||
nsIFrame* aFrame,
|
||||
nscoord aContainingBlockWidth,
|
||||
nscoord aContentEdgeToBoxSizing,
|
||||
nscoord aBoxSizingToMarginEdge,
|
||||
const nsStyleCoord& aCoord);
|
||||
const nsStyleCoord& aCoord)
|
||||
{
|
||||
return ComputeISizeValue(aRenderingContext,
|
||||
aFrame,
|
||||
aContainingBlockWidth,
|
||||
aContentEdgeToBoxSizing,
|
||||
aBoxSizingToMarginEdge,
|
||||
aCoord);
|
||||
}
|
||||
|
||||
static nscoord ComputeISizeValue(
|
||||
nsRenderingContext* aRenderingContext,
|
||||
nsIFrame* aFrame,
|
||||
nscoord aContainingBlockISize,
|
||||
nscoord aContentEdgeToBoxSizing,
|
||||
nscoord aBoxSizingToMarginEdge,
|
||||
const nsStyleCoord& aCoord);
|
||||
|
||||
/*
|
||||
* Convert nsStyleCoord to nscoord when percentages depend on the
|
||||
* containing block height.
|
||||
*/
|
||||
// XXX to be removed
|
||||
static nscoord ComputeHeightDependentValue(
|
||||
nscoord aContainingBlockHeight,
|
||||
const nsStyleCoord& aCoord)
|
||||
{
|
||||
return ComputeBSizeDependentValue(aContainingBlockHeight, aCoord);
|
||||
}
|
||||
|
||||
static nscoord ComputeBSizeDependentValue(
|
||||
nscoord aContainingBlockBSize,
|
||||
const nsStyleCoord& aCoord);
|
||||
|
||||
/*
|
||||
* Likewise, but for 'height', 'min-height', or 'max-height'.
|
||||
*/
|
||||
// XXX to be removed
|
||||
static nscoord ComputeHeightValue(nscoord aContainingBlockHeight,
|
||||
nscoord aContentEdgeToBoxSizingBoxEdge,
|
||||
const nsStyleCoord& aCoord)
|
||||
{
|
||||
MOZ_ASSERT(aContainingBlockHeight != nscoord_MAX || !aCoord.HasPercent(),
|
||||
"caller must deal with %% of unconstrained height");
|
||||
return ComputeBSizeValue(aContainingBlockHeight,
|
||||
aContentEdgeToBoxSizingBoxEdge,
|
||||
aCoord);
|
||||
}
|
||||
|
||||
static nscoord ComputeBSizeValue(nscoord aContainingBlockBSize,
|
||||
nscoord aContentEdgeToBoxSizingBoxEdge,
|
||||
const nsStyleCoord& aCoord)
|
||||
{
|
||||
MOZ_ASSERT(aContainingBlockBSize != nscoord_MAX || !aCoord.HasPercent(),
|
||||
"caller must deal with %% of unconstrained block-size");
|
||||
MOZ_ASSERT(aCoord.IsCoordPercentCalcUnit());
|
||||
|
||||
nscoord result =
|
||||
nsRuleNode::ComputeCoordPercentCalc(aCoord, aContainingBlockHeight);
|
||||
nsRuleNode::ComputeCoordPercentCalc(aCoord, aContainingBlockBSize);
|
||||
// Clamp calc(), and the subtraction for box-sizing.
|
||||
return std::max(0, result - aContentEdgeToBoxSizingBoxEdge);
|
||||
}
|
||||
|
||||
// XXX to be removed
|
||||
static bool IsAutoHeight(const nsStyleCoord &aCoord, nscoord aCBHeight)
|
||||
{
|
||||
return IsAutoBSize(aCoord, aCBHeight);
|
||||
}
|
||||
|
||||
static bool IsAutoBSize(const nsStyleCoord &aCoord, nscoord aCBBSize)
|
||||
{
|
||||
nsStyleUnit unit = aCoord.GetUnit();
|
||||
return unit == eStyleUnit_Auto || // only for 'height'
|
||||
unit == eStyleUnit_None || // only for 'max-height'
|
||||
(aCBHeight == nscoord_MAX && aCoord.HasPercent());
|
||||
(aCBBSize == nscoord_MAX && aCoord.HasPercent());
|
||||
}
|
||||
|
||||
static bool IsPaddingZero(const nsStyleCoord &aCoord)
|
||||
|
@ -1752,6 +1752,12 @@ nsPresContext::UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument,
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
NotifyUIResolutionChanged(TabParent* aTabParent, void* aArg)
|
||||
{
|
||||
aTabParent->UIResolutionChanged();
|
||||
}
|
||||
|
||||
void
|
||||
nsPresContext::UIResolutionChangedInternal()
|
||||
{
|
||||
@ -1762,52 +1768,15 @@ nsPresContext::UIResolutionChangedInternal()
|
||||
AppUnitsPerDevPixelChanged();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMChromeWindow> chromeWindow(do_QueryInterface(mDocument->GetWindow()));
|
||||
nsCOMPtr<nsIMessageBroadcaster> windowMM;
|
||||
if (chromeWindow) {
|
||||
chromeWindow->GetMessageManager(getter_AddRefs(windowMM));
|
||||
}
|
||||
if (windowMM) {
|
||||
NotifyUIResolutionChanged(windowMM);
|
||||
}
|
||||
// Recursively notify all remote leaf descendants that the
|
||||
// resolution of the user interface has changed.
|
||||
nsContentUtils::CallOnAllRemoteChildren(mDocument->GetWindow(),
|
||||
NotifyUIResolutionChanged, nullptr);
|
||||
|
||||
mDocument->EnumerateSubDocuments(UIResolutionChangedSubdocumentCallback,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
nsPresContext::NotifyUIResolutionChanged(nsIMessageBroadcaster* aManager)
|
||||
{
|
||||
uint32_t tabChildCount = 0;
|
||||
aManager->GetChildCount(&tabChildCount);
|
||||
for (uint32_t j = 0; j < tabChildCount; ++j) {
|
||||
nsCOMPtr<nsIMessageListenerManager> childMM;
|
||||
aManager->GetChildAt(j, getter_AddRefs(childMM));
|
||||
if (!childMM) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMessageBroadcaster> nonLeafMM = do_QueryInterface(childMM);
|
||||
if (nonLeafMM) {
|
||||
NotifyUIResolutionChanged(nonLeafMM);
|
||||
continue;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIMessageSender> tabMM = do_QueryInterface(childMM);
|
||||
|
||||
mozilla::dom::ipc::MessageManagerCallback* cb =
|
||||
static_cast<nsFrameMessageManager*>(tabMM.get())->GetCallback();
|
||||
if (cb) {
|
||||
nsFrameLoader* fl = static_cast<nsFrameLoader*>(cb);
|
||||
PBrowserParent* remoteBrowser = fl->GetRemoteBrowser();
|
||||
TabParent* remote = static_cast<TabParent*>(remoteBrowser);
|
||||
if (remote) {
|
||||
remote->UIResolutionChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsPresContext::EmulateMedium(const nsAString& aMediaType)
|
||||
{
|
||||
|
@ -799,12 +799,6 @@ public:
|
||||
*/
|
||||
void UIResolutionChanged();
|
||||
|
||||
/**
|
||||
* Recursively notify all remote leaf descendants of a given message manager
|
||||
* that the resolution of the user interface has changed.
|
||||
*/
|
||||
void NotifyUIResolutionChanged(nsIMessageBroadcaster* aManager);
|
||||
|
||||
/*
|
||||
* Notify the pres context that a system color has changed
|
||||
*/
|
||||
|
@ -50,7 +50,7 @@
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "nsDocShell.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
|
||||
#include "nsJSEnvironment.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
using namespace mozilla;
|
||||
@ -1369,6 +1369,7 @@ nsRefreshDriver::Tick(int64_t aNowEpoch, TimeStamp aNowTime)
|
||||
|
||||
if (nsContentUtils::XPConnect()) {
|
||||
nsContentUtils::XPConnect()->NotifyDidPaint();
|
||||
nsJSContext::NotifyDidPaint();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4139,15 +4139,12 @@ nsFrame::ComputeSize(nsRenderingContext *aRenderingContext,
|
||||
aMargin.ISize(aWM) + aBorder.ISize(aWM) + aPadding.ISize(aWM) -
|
||||
boxSizingAdjust.ISize(aWM);
|
||||
|
||||
const nsStyleCoord* inlineStyleCoord;
|
||||
const nsStyleCoord* blockStyleCoord;
|
||||
if (aWM.IsVertical()) {
|
||||
inlineStyleCoord = &(stylePos->mHeight);
|
||||
blockStyleCoord = &(stylePos->mWidth);
|
||||
} else {
|
||||
inlineStyleCoord = &(stylePos->mWidth);
|
||||
blockStyleCoord = &(stylePos->mHeight);
|
||||
}
|
||||
bool isVertical = aWM.IsVertical();
|
||||
|
||||
const nsStyleCoord* inlineStyleCoord =
|
||||
isVertical ? &(stylePos->mHeight) : &(stylePos->mWidth);
|
||||
const nsStyleCoord* blockStyleCoord =
|
||||
isVertical ? &(stylePos->mWidth) : &(stylePos->mHeight);
|
||||
|
||||
bool isFlexItem = IsFlexItem();
|
||||
bool isInlineFlexItem = false;
|
||||
@ -4188,30 +4185,36 @@ nsFrame::ComputeSize(nsRenderingContext *aRenderingContext,
|
||||
|
||||
if (inlineStyleCoord->GetUnit() != eStyleUnit_Auto) {
|
||||
result.ISize(aWM) =
|
||||
nsLayoutUtils::ComputeWidthValue(aRenderingContext, this,
|
||||
nsLayoutUtils::ComputeISizeValue(aRenderingContext, this,
|
||||
aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM), boxSizingToMarginEdgeISize,
|
||||
*inlineStyleCoord);
|
||||
}
|
||||
|
||||
const nsStyleCoord& maxISizeCoord =
|
||||
isVertical ? stylePos->mMaxHeight : stylePos->mMaxWidth;
|
||||
|
||||
// Flex items ignore their min & max sizing properties in their
|
||||
// flex container's main-axis. (Those properties get applied later in
|
||||
// the flexbox algorithm.)
|
||||
if (stylePos->mMaxWidth.GetUnit() != eStyleUnit_None &&
|
||||
if (maxISizeCoord.GetUnit() != eStyleUnit_None &&
|
||||
!(isFlexItem && isInlineFlexItem)) {
|
||||
nscoord maxISize =
|
||||
nsLayoutUtils::ComputeWidthValue(aRenderingContext, this,
|
||||
nsLayoutUtils::ComputeISizeValue(aRenderingContext, this,
|
||||
aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM), boxSizingToMarginEdgeISize,
|
||||
stylePos->mMaxWidth);
|
||||
maxISizeCoord);
|
||||
result.ISize(aWM) = std::min(maxISize, result.ISize(aWM));
|
||||
}
|
||||
|
||||
const nsStyleCoord& minISizeCoord =
|
||||
isVertical ? stylePos->mMinHeight : stylePos->mMinWidth;
|
||||
|
||||
nscoord minISize;
|
||||
if (stylePos->mMinWidth.GetUnit() != eStyleUnit_Auto &&
|
||||
if (minISizeCoord.GetUnit() != eStyleUnit_Auto &&
|
||||
!(isFlexItem && isInlineFlexItem)) {
|
||||
minISize =
|
||||
nsLayoutUtils::ComputeWidthValue(aRenderingContext, this,
|
||||
nsLayoutUtils::ComputeISizeValue(aRenderingContext, this,
|
||||
aCBSize.ISize(aWM), boxSizingAdjust.ISize(aWM), boxSizingToMarginEdgeISize,
|
||||
stylePos->mMinWidth);
|
||||
minISizeCoord);
|
||||
} else {
|
||||
// Treat "min-width: auto" as 0.
|
||||
// NOTE: Technically, "auto" is supposed to behave like "min-content" on
|
||||
@ -4226,30 +4229,36 @@ nsFrame::ComputeSize(nsRenderingContext *aRenderingContext,
|
||||
// (but not if we're auto-height or if we recieved the "eUseAutoHeight"
|
||||
// flag -- then, we'll just stick with the height that we already calculated
|
||||
// in the initial ComputeAutoSize() call.)
|
||||
if (!nsLayoutUtils::IsAutoHeight(*blockStyleCoord, aCBSize.BSize(aWM)) &&
|
||||
if (!nsLayoutUtils::IsAutoBSize(*blockStyleCoord, aCBSize.BSize(aWM)) &&
|
||||
!(aFlags & nsIFrame::eUseAutoHeight)) {
|
||||
result.BSize(aWM) =
|
||||
nsLayoutUtils::ComputeHeightValue(aCBSize.BSize(aWM),
|
||||
boxSizingAdjust.BSize(aWM),
|
||||
*blockStyleCoord);
|
||||
nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
|
||||
boxSizingAdjust.BSize(aWM),
|
||||
*blockStyleCoord);
|
||||
}
|
||||
|
||||
const nsStyleCoord& maxBSizeCoord =
|
||||
isVertical ? stylePos->mMaxWidth : stylePos->mMaxHeight;
|
||||
|
||||
if (result.BSize(aWM) != NS_UNCONSTRAINEDSIZE) {
|
||||
if (!nsLayoutUtils::IsAutoHeight(stylePos->mMaxHeight, aCBSize.BSize(aWM)) &&
|
||||
if (!nsLayoutUtils::IsAutoBSize(maxBSizeCoord, aCBSize.BSize(aWM)) &&
|
||||
!(isFlexItem && !isInlineFlexItem)) {
|
||||
nscoord maxBSize =
|
||||
nsLayoutUtils::ComputeHeightValue(aCBSize.BSize(aWM),
|
||||
boxSizingAdjust.BSize(aWM),
|
||||
stylePos->mMaxHeight);
|
||||
nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
|
||||
boxSizingAdjust.BSize(aWM),
|
||||
maxBSizeCoord);
|
||||
result.BSize(aWM) = std::min(maxBSize, result.BSize(aWM));
|
||||
}
|
||||
|
||||
if (!nsLayoutUtils::IsAutoHeight(stylePos->mMinHeight, aCBSize.BSize(aWM)) &&
|
||||
const nsStyleCoord& minBSizeCoord =
|
||||
isVertical ? stylePos->mMinWidth : stylePos->mMinHeight;
|
||||
|
||||
if (!nsLayoutUtils::IsAutoBSize(minBSizeCoord, aCBSize.BSize(aWM)) &&
|
||||
!(isFlexItem && !isInlineFlexItem)) {
|
||||
nscoord minBSize =
|
||||
nsLayoutUtils::ComputeHeightValue(aCBSize.BSize(aWM),
|
||||
boxSizingAdjust.BSize(aWM),
|
||||
stylePos->mMinHeight);
|
||||
nsLayoutUtils::ComputeBSizeValue(aCBSize.BSize(aWM),
|
||||
boxSizingAdjust.BSize(aWM),
|
||||
minBSizeCoord);
|
||||
result.BSize(aWM) = std::max(minBSize, result.BSize(aWM));
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.wrapper {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin: 20px;
|
||||
}
|
||||
.outer {
|
||||
height: 250px;
|
||||
width: 200px;
|
||||
border: 10px solid #ddd;
|
||||
}
|
||||
.inner {
|
||||
font: 12px monospace;
|
||||
background: yellow;
|
||||
overflow: hidden;
|
||||
}
|
||||
.test1 {
|
||||
width: 180px;
|
||||
height: 150px;
|
||||
}
|
||||
.test2 {
|
||||
width: 150px;
|
||||
height: 200px;
|
||||
}
|
||||
.lr {
|
||||
writing-mode: vertical-lr;
|
||||
}
|
||||
.rl {
|
||||
writing-mode: vertical-rl;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="outer">
|
||||
<div class="inner test1 lr">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="outer">
|
||||
<div class="inner test2 lr">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.wrapper {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin: 20px;
|
||||
}
|
||||
.outer {
|
||||
height: 250px;
|
||||
width: 200px;
|
||||
border: 10px solid #ddd;
|
||||
}
|
||||
.inner {
|
||||
font: 12px monospace;
|
||||
background: yellow;
|
||||
overflow: hidden;
|
||||
}
|
||||
.test1 {
|
||||
min-width: 180px;
|
||||
max-width: 180px;
|
||||
min-height: 150px;
|
||||
max-height: 150px;
|
||||
}
|
||||
.test2 {
|
||||
min-width: 150px;
|
||||
max-width: 150px;
|
||||
min-height: 200px;
|
||||
max-height: 200px;
|
||||
}
|
||||
.lr {
|
||||
writing-mode: vertical-lr;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="outer">
|
||||
<div class="inner test1 lr">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="outer">
|
||||
<div class="inner test2 lr">
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,61 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.wrapper {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin: 20px;
|
||||
}
|
||||
.outer {
|
||||
height: 250px;
|
||||
width: 200px;
|
||||
border: 10px solid #ddd;
|
||||
}
|
||||
.inner {
|
||||
overflow: hidden;
|
||||
background: yellow;
|
||||
}
|
||||
.test1 {
|
||||
width: 100px;
|
||||
height: 150px;
|
||||
}
|
||||
.test2 {
|
||||
width: 150px;
|
||||
height: 100px;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="outer">
|
||||
<img class="inner test1" src="foo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="outer">
|
||||
<img class="inner test1" src="foo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="outer">
|
||||
<img class="inner test2" src="foo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="outer">
|
||||
<img class="inner test2" src="foo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -0,0 +1,71 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<style>
|
||||
.wrapper {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin: 20px;
|
||||
}
|
||||
.outer {
|
||||
height: 250px;
|
||||
width: 200px;
|
||||
border: 10px solid #ddd;
|
||||
}
|
||||
.inner {
|
||||
overflow: hidden;
|
||||
background: yellow;
|
||||
}
|
||||
.test1 {
|
||||
min-width: 100px;
|
||||
max-width: 100px;
|
||||
min-height: 150px;
|
||||
max-height: 150px;
|
||||
}
|
||||
.test2 {
|
||||
min-width: 150px;
|
||||
max-width: 150px;
|
||||
min-height: 100px;
|
||||
max-height: 100px;
|
||||
}
|
||||
.lr {
|
||||
writing-mode: vertical-lr;
|
||||
}
|
||||
.rl {
|
||||
writing-mode: vertical-rl;
|
||||
}
|
||||
</style>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="outer">
|
||||
<img class="inner test1 lr" src="foo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="outer">
|
||||
<img class="inner test1 rl" src="foo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="outer">
|
||||
<img class="inner test2 lr" src="foo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="wrapper">
|
||||
<div class="outer">
|
||||
<img class="inner test2 rl" src="foo">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@ -25,3 +25,5 @@ random-if(gtk2Widget) == 1094434-2.html 1094434-2-ref.html # bug 1094845
|
||||
== 1096224-1a.html 1096224-1-ref.html
|
||||
== 1096224-1b.html 1096224-1-ref.html
|
||||
== 1103613-1.html 1103613-1-ref.html
|
||||
== 1105268-1-min-max-dimensions.html 1105268-1-min-max-dimensions-ref.html
|
||||
== 1105268-2-min-max-dimensions.html 1105268-2-min-max-dimensions-ref.html
|
||||
|
@ -523,6 +523,12 @@ static OPUS_INLINE int interp_bits2pulses(const CELTMode *m, int start, int end,
|
||||
return codedBands;
|
||||
}
|
||||
|
||||
#if !defined(__clang__) && defined(__GNUC__) && defined(__arm__) && \
|
||||
__GNUC__ == 4 && __GNUC_MINOR__ == 8
|
||||
#warning "OPUS library causes an internal compiler error for gcc-4.8 based toolchain in arm"
|
||||
#pragma GCC push_options
|
||||
#pragma GCC optimize ("O0")
|
||||
#endif
|
||||
int compute_allocation(const CELTMode *m, int start, int end, const int *offsets, const int *cap, int alloc_trim, int *intensity, int *dual_stereo,
|
||||
opus_int32 total, opus_int32 *balance, int *pulses, int *ebits, int *fine_priority, int C, int LM, ec_ctx *ec, int encode, int prev, int signalBandwidth)
|
||||
{
|
||||
@ -635,4 +641,7 @@ int compute_allocation(const CELTMode *m, int start, int end, const int *offsets
|
||||
RESTORE_STACK;
|
||||
return codedBands;
|
||||
}
|
||||
|
||||
#if !defined(__clang__) && defined(__GNUC__) && defined(__arm__) && \
|
||||
__GNUC__ == 4 && __GNUC_MINOR__ == 8
|
||||
#pragma GCC pop_options
|
||||
#endif
|
||||
|
39
media/libopus/gcc-4.8-ICE.patch
Normal file
39
media/libopus/gcc-4.8-ICE.patch
Normal file
@ -0,0 +1,39 @@
|
||||
From 609166a46f6a22ae2d0a0ab7c64415c779c65f37 Mon Sep 17 00:00:00 2001
|
||||
From: Juan Gomez <atilag@gmail.com>
|
||||
Date: Wed, 26 Nov 2014 23:57:49 +0100
|
||||
Subject: [PATCH] Bug 1056337 - Upgrade toolchain used for B2G ICS builds *
|
||||
Patch for gcc ICE in OPUS library (arm)
|
||||
|
||||
---
|
||||
media/libopus/celt/rate.c | 11 ++++++++++-
|
||||
1 file changed, 10 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/media/libopus/celt/rate.c b/media/libopus/celt/rate.c
|
||||
index e13d839..1055e63 100644
|
||||
--- a/media/libopus/celt/rate.c
|
||||
+++ b/media/libopus/celt/rate.c
|
||||
@@ -523,6 +523,12 @@ static OPUS_INLINE int interp_bits2pulses(const CELTMode *m, int start, int end,
|
||||
return codedBands;
|
||||
}
|
||||
|
||||
+#if !defined(__clang__) && defined(__GNUC__) && defined(__arm__) && \
|
||||
+ __GNUC__ == 4 && __GNUC_MINOR__ == 8
|
||||
+#warning "OPUS library causes an internal compiler error for gcc-4.8 based toolchain in arm"
|
||||
+#pragma GCC push_options
|
||||
+#pragma GCC optimize ("O0")
|
||||
+#endif
|
||||
int compute_allocation(const CELTMode *m, int start, int end, const int *offsets, const int *cap, int alloc_trim, int *intensity, int *dual_stereo,
|
||||
opus_int32 total, opus_int32 *balance, int *pulses, int *ebits, int *fine_priority, int C, int LM, ec_ctx *ec, int encode, int prev, int signalBandwidth)
|
||||
{
|
||||
@@ -635,4 +641,7 @@ int compute_allocation(const CELTMode *m, int start, int end, const int *offsets
|
||||
RESTORE_STACK;
|
||||
return codedBands;
|
||||
}
|
||||
-
|
||||
+#if !defined(__clang__) && defined(__GNUC__) && defined(__arm__) && \
|
||||
+ __GNUC__ == 4 && __GNUC_MINOR__ == 8
|
||||
+#pragma GCC pop_options
|
||||
+#endif
|
||||
--
|
||||
2.1.0
|
||||
|
@ -74,4 +74,4 @@ sed -e "s/DEFINES\['OPUS_VERSION'\][ \t]*=[ \t]*'\".*\"'/DEFINES['OPUS_VERSION']
|
||||
python gen-sources.py $1
|
||||
|
||||
# apply outstanding local patches
|
||||
# ... no patches to apply ...
|
||||
patch -p3 < ./gcc-4.8-ICE.patch
|
||||
|
@ -3569,14 +3569,17 @@ pref("font.name.monospace.x-western", "Fira Mono");
|
||||
pref("font.name.serif.zh-CN", "Charis SIL Compact");
|
||||
pref("font.name.sans-serif.zh-CN", "Fira Sans");
|
||||
pref("font.name.monospace.zh-CN", "Fira Mono");
|
||||
pref("font.name-list.sans-serif.zh-CN", "Fira Sans,Droid Sans Fallback");
|
||||
|
||||
pref("font.name.serif.zh-HK", "Charis SIL Compact");
|
||||
pref("font.name.sans-serif.zh-HK", "Fira Sans");
|
||||
pref("font.name.monospace.zh-HK", "Fira Mono");
|
||||
pref("font.name-list.sans-serif.zh-HK", "Fira Sans,Droid Sans Fallback");
|
||||
|
||||
pref("font.name.serif.zh-TW", "Charis SIL Compact");
|
||||
pref("font.name.sans-serif.zh-TW", "Fira Sans");
|
||||
pref("font.name.monospace.zh-TW", "Fira Mono");
|
||||
pref("font.name-list.sans-serif.zh-TW", "Fira Sans,Droid Sans Fallback");
|
||||
|
||||
#elif defined(ANDROID)
|
||||
// We use the bundled fonts for Firefox for Android
|
||||
|
@ -490,6 +490,8 @@ class CCacheStats(object):
|
||||
]
|
||||
|
||||
DIRECTORY_DESCRIPTION = "cache directory"
|
||||
PRIMARY_CONFIG_DESCRIPTION = "primary config"
|
||||
SECONDARY_CONFIG_DESCRIPTION = "secondary config (readonly)"
|
||||
ABSOLUTE_KEYS = {'cache_max_size'}
|
||||
FORMAT_KEYS = {'cache_size', 'cache_max_size'}
|
||||
|
||||
@ -501,6 +503,8 @@ class CCacheStats(object):
|
||||
"""Construct an instance from the output of ccache -s."""
|
||||
self._values = {}
|
||||
self.cache_dir = ""
|
||||
self.primary_config = ""
|
||||
self.secondary_config = ""
|
||||
|
||||
if not output:
|
||||
return
|
||||
@ -513,15 +517,20 @@ class CCacheStats(object):
|
||||
def _parse_line(self, line):
|
||||
if line.startswith(self.DIRECTORY_DESCRIPTION):
|
||||
self.cache_dir = self._strip_prefix(line, self.DIRECTORY_DESCRIPTION)
|
||||
return
|
||||
|
||||
for stat_key, stat_description in self.STATS_KEYS:
|
||||
if line.startswith(stat_description):
|
||||
raw_value = self._strip_prefix(line, stat_description)
|
||||
self._values[stat_key] = self._parse_value(raw_value)
|
||||
break
|
||||
elif line.startswith(self.PRIMARY_CONFIG_DESCRIPTION):
|
||||
self.primary_config = self._strip_prefix(
|
||||
line, self.PRIMARY_CONFIG_DESCRIPTION)
|
||||
elif line.startswith(self.SECONDARY_CONFIG_DESCRIPTION):
|
||||
self.secondary_config = self._strip_prefix(
|
||||
line, self.SECONDARY_CONFIG_DESCRIPTION)
|
||||
else:
|
||||
raise ValueError('Failed to parse ccache stats output: %s' % line)
|
||||
for stat_key, stat_description in self.STATS_KEYS:
|
||||
if line.startswith(stat_description):
|
||||
raw_value = self._strip_prefix(line, stat_description)
|
||||
self._values[stat_key] = self._parse_value(raw_value)
|
||||
break
|
||||
else:
|
||||
raise ValueError('Failed to parse ccache stats output: %s' % line)
|
||||
|
||||
@staticmethod
|
||||
def _strip_prefix(line, prefix):
|
||||
|
@ -59,6 +59,27 @@ class TestCcacheStats(unittest.TestCase):
|
||||
cache size 2.0 Gbytes
|
||||
max cache size 16.0 Gbytes"""
|
||||
|
||||
STAT3 = """
|
||||
cache directory /Users/tlin/.ccache
|
||||
primary config /Users/tlin/.ccache/ccache.conf
|
||||
secondary config (readonly) /usr/local/Cellar/ccache/3.2/etc/ccache.conf
|
||||
cache hit (direct) 12004
|
||||
cache hit (preprocessed) 1786
|
||||
cache miss 26348
|
||||
called for link 2338
|
||||
called for preprocessing 6313
|
||||
compile failed 399
|
||||
preprocessor error 390
|
||||
bad compiler arguments 86
|
||||
unsupported source language 66
|
||||
autoconf compile/link 2439
|
||||
unsupported compiler option 187
|
||||
no input file 1068
|
||||
files in cache 18044
|
||||
cache size 7.5 GB
|
||||
max cache size 8.6 GB
|
||||
"""
|
||||
|
||||
def test_parse_garbage_stats_message(self):
|
||||
self.assertRaises(ValueError, CCacheStats, self.STAT_GARBAGE)
|
||||
|
||||
@ -88,6 +109,13 @@ class TestCcacheStats(unittest.TestCase):
|
||||
self.assertFalse(stats_diff_negative1)
|
||||
self.assertFalse(stats_diff_negative2)
|
||||
|
||||
def test_stats_version32(self):
|
||||
stat2 = CCacheStats(self.STAT2)
|
||||
stat3 = CCacheStats(self.STAT3)
|
||||
stats_diff = stat3 - stat2
|
||||
self.assertTrue(stat3)
|
||||
self.assertTrue(stats_diff)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
@ -1210,9 +1210,9 @@ retryDueToTLSIntolerance(PRErrorCode err, nsNSSSocketInfo* socketInfo)
|
||||
.forgetIntolerance(socketInfo->GetHostName(), socketInfo->GetPort());
|
||||
|
||||
return false;
|
||||
} else if ((err == SSL_ERROR_NO_CYPHER_OVERLAP ||
|
||||
err == PR_END_OF_FILE_ERROR) &&
|
||||
nsNSSComponent::AreAnyWeakCiphersEnabled()) {
|
||||
}
|
||||
if ((err == SSL_ERROR_NO_CYPHER_OVERLAP || err == PR_END_OF_FILE_ERROR) &&
|
||||
nsNSSComponent::AreAnyWeakCiphersEnabled()) {
|
||||
if (socketInfo->SharedState().IOLayerHelpers()
|
||||
.rememberStrongCiphersFailed(socketInfo->GetHostName(),
|
||||
socketInfo->GetPort(), err)) {
|
||||
|
@ -33,7 +33,11 @@ TEST_F(TLSIntoleranceTest, Test_Full_Fallback_Process)
|
||||
ASSERT_EQ(SSL_LIBRARY_VERSION_TLS_1_2, range.max);
|
||||
ASSERT_EQ(StrongCipherStatusUnknown, strongCipherStatus);
|
||||
|
||||
ASSERT_TRUE(helpers.rememberStrongCiphersFailed(HOST, PORT, 0));
|
||||
ASSERT_TRUE(
|
||||
helpers.rememberStrongCiphersFailed(
|
||||
HOST, PORT, SSL_ERROR_NO_CYPHER_OVERLAP));
|
||||
ASSERT_EQ(SSL_ERROR_NO_CYPHER_OVERLAP,
|
||||
helpers.getIntoleranceReason(HOST, PORT));
|
||||
}
|
||||
|
||||
{
|
||||
|
@ -37,68 +37,67 @@ MobileIdentityVerificationFlow.prototype = {
|
||||
return Promise.reject(ERROR_INTERNAL_UNEXPECTED);
|
||||
}
|
||||
this.sessionToken = registerResult.msisdnSessionToken;
|
||||
return this._doVerification();
|
||||
// We save the timestamp of the start of the verification timeout to be
|
||||
// able to provide to the UI the remaining time on each retry.
|
||||
if (!this.timer) {
|
||||
log.debug("Creating verification code timer");
|
||||
this.timerCreation = Date.now();
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this.timer.initWithCallback(this.onVerificationCodeTimeout.bind(this),
|
||||
VERIFICATIONCODE_TIMEOUT,
|
||||
this.timer.TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
if (!this.verifyStrategy) {
|
||||
return Promise.reject(ERROR_INTERNAL_INVALID_VERIFICATION_FLOW);
|
||||
}
|
||||
|
||||
return this.verifyStrategy()
|
||||
.then(() => {
|
||||
return this._doVerification();
|
||||
}, (reason) => {
|
||||
this.verificationCodeDeferred.reject(reason);
|
||||
});
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
_doVerification: function() {
|
||||
log.debug("_doVerification");
|
||||
// We save the timestamp of the start of the verification timeout to be
|
||||
// able to provide to the UI the remaining time on each retry.
|
||||
if (!this.timer) {
|
||||
log.debug("Creating verification code timer");
|
||||
this.timerCreation = Date.now();
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this.timer.initWithCallback(this.onVerificationCodeTimeout.bind(this),
|
||||
VERIFICATIONCODE_TIMEOUT,
|
||||
this.timer.TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
if (!this.verifyStrategy) {
|
||||
return Promise.reject(ERROR_INTERNAL_INVALID_VERIFICATION_FLOW);
|
||||
}
|
||||
|
||||
this.verificationCodeDeferred = Promise.defer();
|
||||
|
||||
this.verifyStrategy()
|
||||
.then(
|
||||
() => {
|
||||
// If the verification flow can be for an external phone number,
|
||||
// we need to ask the user for the verification code.
|
||||
// In that case we don't do a notification about the verification
|
||||
// process being done until the user enters the verification code
|
||||
// in the UI.
|
||||
if (this.verificationOptions.external) {
|
||||
let timeLeft = 0;
|
||||
if (this.timer) {
|
||||
timeLeft = this.timerCreation + VERIFICATIONCODE_TIMEOUT -
|
||||
Date.now();
|
||||
}
|
||||
this.ui.verificationCodePrompt(this.retries,
|
||||
VERIFICATIONCODE_TIMEOUT / 1000,
|
||||
timeLeft / 1000)
|
||||
.then(
|
||||
(verificationCode) => {
|
||||
if (!verificationCode) {
|
||||
return this.verificationCodeDeferred.reject(
|
||||
ERROR_INTERNAL_INVALID_PROMPT_RESULT);
|
||||
}
|
||||
// If the user got the verification code that means that the
|
||||
// introduced phone number didn't belong to any of the inserted
|
||||
// SIMs.
|
||||
this.ui.verify();
|
||||
this.verificationCodeDeferred.resolve(verificationCode);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.ui.verify();
|
||||
}
|
||||
},
|
||||
(reason) => {
|
||||
this.verificationCodeDeferred.reject(reason);
|
||||
// If the verification flow can be for an external phone number,
|
||||
// we need to ask the user for the verification code.
|
||||
// In that case we don't do a notification about the verification
|
||||
// process being done until the user enters the verification code
|
||||
// in the UI.
|
||||
if (this.verificationOptions.external) {
|
||||
let timeLeft = 0;
|
||||
if (this.timer) {
|
||||
timeLeft = this.timerCreation + VERIFICATIONCODE_TIMEOUT -
|
||||
Date.now();
|
||||
}
|
||||
);
|
||||
this.ui.verificationCodePrompt(this.retries,
|
||||
VERIFICATIONCODE_TIMEOUT / 1000,
|
||||
timeLeft / 1000)
|
||||
.then(
|
||||
(verificationCode) => {
|
||||
if (!verificationCode) {
|
||||
return this.verificationCodeDeferred.reject(
|
||||
ERROR_INTERNAL_INVALID_PROMPT_RESULT);
|
||||
}
|
||||
// If the user got the verification code that means that the
|
||||
// introduced phone number didn't belong to any of the inserted
|
||||
// SIMs.
|
||||
this.ui.verify();
|
||||
this.verificationCodeDeferred.resolve(verificationCode);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.ui.verify();
|
||||
}
|
||||
|
||||
return this.verificationCodeDeferred.promise.then(
|
||||
this.onVerificationCode.bind(this)
|
||||
);
|
||||
@ -145,8 +144,11 @@ MobileIdentityVerificationFlow.prototype = {
|
||||
log.error("Retries left " + this.retries);
|
||||
if (!this.retries) {
|
||||
this.ui.error(ERROR_NO_RETRIES_LEFT);
|
||||
this.timer.cancel();
|
||||
this.timer = null;
|
||||
return Promise.reject(ERROR_NO_RETRIES_LEFT);
|
||||
}
|
||||
this.ui.error(ERROR_INVALID_VERIFICATION_CODE);
|
||||
this.verifying = false;
|
||||
if (this.queuedTimeout) {
|
||||
this.onVerificationCodeTimeout();
|
||||
|
@ -5,6 +5,8 @@ const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cm = Components.manager;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
@ -17,3 +19,444 @@ Cu.import("resource://gre/modules/Services.jsm");
|
||||
Services.prefs.setCharPref("services.mobileid.server.uri",
|
||||
"https://dummyurl.com");
|
||||
}).call(this);
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
const GET_ASSERTION_IPC_MSG = "MobileId:GetAssertion";
|
||||
const GET_ASSERTION_RETURN_OK = "MobileId:GetAssertion:Return:OK";
|
||||
const GET_ASSERTION_RETURN_KO = "MobileId:GetAssertion:Return:KO";
|
||||
|
||||
// === Globals ===
|
||||
|
||||
const ORIGIN = "app://afakeorigin";
|
||||
const APP_ID = 1;
|
||||
const PRINCIPAL = {
|
||||
origin: ORIGIN,
|
||||
appId: APP_ID
|
||||
};
|
||||
const PHONE_NUMBER = "+34666555444";
|
||||
const ANOTHER_PHONE_NUMBER = "+44123123123";
|
||||
const VERIFICATION_CODE = "123456";
|
||||
const SESSION_TOKEN = "aSessionToken";
|
||||
const ICC_ID = "aIccId";
|
||||
const ANOTHER_ICC_ID = "anotherIccId";
|
||||
const MNC = "aMnc";
|
||||
const ANOTHER_MNC = "anotherMnc";
|
||||
const MCC = "aMcc";
|
||||
const ANOTHER_MCC = "anotherMcc";
|
||||
const OPERATOR = "aOperator";
|
||||
const ANOTHER_OPERATOR = "anotherOperator";
|
||||
const RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: ICC_ID,
|
||||
mcc: MCC,
|
||||
mnc: MNC,
|
||||
msisdn: PHONE_NUMBER,
|
||||
operator: OPERATOR
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: OPERATOR
|
||||
},
|
||||
roaming: false
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: OPERATOR
|
||||
}
|
||||
}
|
||||
};
|
||||
const ANOTHER_RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: ANOTHER_ICC_ID,
|
||||
mcc: ANOTHER_MCC,
|
||||
mnc: ANOTHER_MNC,
|
||||
msisdn: ANOTHER_PHONE_NUMBER,
|
||||
operator: ANOTHER_OPERATOR
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: ANOTHER_OPERATOR
|
||||
},
|
||||
roaming: false
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: ANOTHER_OPERATOR
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const INVALID_RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: null,
|
||||
mcc: "",
|
||||
mnc: "",
|
||||
msisdn: "",
|
||||
operator: ""
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: ""
|
||||
},
|
||||
roaming: undefined
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: ""
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const CERTIFICATE = "eyJhbGciOiJEUzI1NiJ9.eyJsYXN0QXV0aEF0IjoxNDA0NDY5NzkyODc3LCJ2ZXJpZmllZE1TSVNETiI6IiszMTYxNzgxNTc1OCIsInB1YmxpYy1rZXkiOnsiYWxnb3JpdGhtIjoiRFMiLCJ5IjoiNGE5YzkzNDY3MWZhNzQ3YmM2ZjMyNjE0YTg1MzUyZjY5NDcwMDdhNTRkMDAxMDY4OWU5ZjJjZjc0ZGUwYTEwZTRlYjlmNDk1ZGFmZTA0NGVjZmVlNDlkN2YwOGU4ODQyMDJiOTE5OGRhNWZhZWE5MGUzZjRmNzE1YzZjNGY4Yjc3MGYxZTU4YWZhNDM0NzVhYmFiN2VlZGE1MmUyNjk2YzFmNTljNzMzYjFlYzBhNGNkOTM1YWIxYzkyNzAxYjNiYTA5ZDRhM2E2MzNjNTJmZjE2NGYxMWY3OTg1YzlmZjY3ZThmZDFlYzA2NDU3MTdkMjBiNDE4YmM5M2YzYzVkNCIsInAiOiJmZjYwMDQ4M2RiNmFiZmM1YjQ1ZWFiNzg1OTRiMzUzM2Q1NTBkOWYxYmYyYTk5MmE3YThkYWE2ZGMzNGY4MDQ1YWQ0ZTZlMGM0MjlkMzM0ZWVlYWFlZmQ3ZTIzZDQ4MTBiZTAwZTRjYzE0OTJjYmEzMjViYTgxZmYyZDVhNWIzMDVhOGQxN2ViM2JmNGEwNmEzNDlkMzkyZTAwZDMyOTc0NGE1MTc5MzgwMzQ0ZTgyYTE4YzQ3OTMzNDM4Zjg5MWUyMmFlZWY4MTJkNjljOGY3NWUzMjZjYjcwZWEwMDBjM2Y3NzZkZmRiZDYwNDYzOGMyZWY3MTdmYzI2ZDAyZTE3IiwicSI6ImUyMWUwNGY5MTFkMWVkNzk5MTAwOGVjYWFiM2JmNzc1OTg0MzA5YzMiLCJnIjoiYzUyYTRhMGZmM2I3ZTYxZmRmMTg2N2NlODQxMzgzNjlhNjE1NGY0YWZhOTI5NjZlM2M4MjdlMjVjZmE2Y2Y1MDhiOTBlNWRlNDE5ZTEzMzdlMDdhMmU5ZTJhM2NkNWRlYTcwNGQxNzVmOGViZjZhZjM5N2Q2OWUxMTBiOTZhZmIxN2M3YTAzMjU5MzI5ZTQ4MjliMGQwM2JiYzc4OTZiMTViNGFkZTUzZTEzMDg1OGNjMzRkOTYyNjlhYTg5MDQxZjQwOTEzNmM3MjQyYTM4ODk1YzlkNWJjY2FkNGYzODlhZjFkN2E0YmQxMzk4YmQwNzJkZmZhODk2MjMzMzk3YSJ9LCJwcmluY2lwYWwiOiIwMzgxOTgyYS0xZTgzLTI1NjYtNjgzZS05MDRmNDA0NGM1MGRAbXNpc2RuLWRldi5zdGFnZS5tb3phd3MubmV0IiwiaWF0IjoxNDA0NDY5NzgyODc3LCJleHAiOjE0MDQ0OTEzOTI4NzcsImlzcyI6Im1zaXNkbi1kZXYuc3RhZ2UubW96YXdzLm5ldCJ9."
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
function addPermission(aAction) {
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(ORIGIN, null, null);
|
||||
let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getAppCodebasePrincipal(uri, APP_ID, false);
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
pm.addFromPrincipal(_principal, MOBILEID_PERM, aAction);
|
||||
}
|
||||
|
||||
function removePermission() {
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(ORIGIN, null, null);
|
||||
let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getAppCodebasePrincipal(uri, APP_ID, false);
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
pm.removeFromPrincipal(_principal, MOBILEID_PERM);
|
||||
}
|
||||
|
||||
// === Mocks ===
|
||||
|
||||
let Mock = function(aOptions) {
|
||||
if (!aOptions) {
|
||||
aOptions = {};
|
||||
}
|
||||
this._options = aOptions;
|
||||
this._spied = {};
|
||||
};
|
||||
|
||||
Mock.prototype = {
|
||||
_: function(aMethod) {
|
||||
DEBUG && do_print("_ " + aMethod + JSON.stringify(this._spied));
|
||||
let self = this;
|
||||
return {
|
||||
callsLength: function(aNumberOfCalls) {
|
||||
if (aNumberOfCalls == 0) {
|
||||
do_check_eq(self._spied[aMethod], undefined);
|
||||
return;
|
||||
}
|
||||
do_check_eq(self._spied[aMethod].length, aNumberOfCalls);
|
||||
},
|
||||
call: function(aCallNumber) {
|
||||
return {
|
||||
arg: function(aArgNumber, aValue) {
|
||||
let _arg = self._spied[aMethod][aCallNumber - 1][aArgNumber - 1];
|
||||
if (Array.isArray(aValue)) {
|
||||
do_check_eq(_arg.length, aValue.length)
|
||||
for (let i = 0; i < _arg.length; i++) {
|
||||
do_check_eq(_arg[i], aValue[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof aValue === 'object') {
|
||||
do_check_eq(JSON.stringify(_arg), JSON.stringify(aValue));
|
||||
return;
|
||||
}
|
||||
|
||||
do_check_eq(_arg, aValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_spy: function(aMethod, aArgs) {
|
||||
DEBUG && do_print(aMethod + " - " + JSON.stringify(aArgs));
|
||||
if (!this._spied[aMethod]) {
|
||||
this._spied[aMethod] = [];
|
||||
}
|
||||
this._spied[aMethod].push(aArgs);
|
||||
},
|
||||
|
||||
getSpiedCalls: function(aMethod) {
|
||||
return this._spied[aMethod];
|
||||
}
|
||||
};
|
||||
|
||||
// UI Glue mock up.
|
||||
let MockUi = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockUi.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_startFlowResult: {
|
||||
phoneNumber: PHONE_NUMBER,
|
||||
mcc: MNC
|
||||
},
|
||||
|
||||
_verifyCodePromptResult: {
|
||||
verificationCode: VERIFICATION_CODE
|
||||
},
|
||||
|
||||
startFlow: function() {
|
||||
this._spy("startFlow", arguments);
|
||||
return Promise.resolve(this._options.startFlowResult ||
|
||||
this._startFlowResult);
|
||||
},
|
||||
|
||||
verificationCodePrompt: function() {
|
||||
this._spy("verifyCodePrompt", arguments);
|
||||
return Promise.resolve(this._options.verificationCodePromptResult ||
|
||||
this._verifyCodePromptResult);
|
||||
},
|
||||
|
||||
verify: function() {
|
||||
this._spy("verify", arguments);
|
||||
},
|
||||
|
||||
error: function() {
|
||||
this._spy("error", arguments);
|
||||
},
|
||||
|
||||
verified: function() {
|
||||
this._spy("verified", arguments);
|
||||
},
|
||||
|
||||
set oncancel(aCallback) {
|
||||
},
|
||||
|
||||
set onresendcode(aCallback) {
|
||||
}
|
||||
};
|
||||
|
||||
// Credentials store mock up.
|
||||
let MockCredStore = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockCredStore.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_getByOriginResult: null,
|
||||
|
||||
_getByMsisdnResult: null,
|
||||
|
||||
_getByIccIdResult: null,
|
||||
|
||||
getByOrigin: function() {
|
||||
this._spy("getByOrigin", arguments);
|
||||
let result = this._getByOriginResult;
|
||||
if (this._options.getByOriginResult) {
|
||||
if (Array.isArray(this._options.getByOriginResult)) {
|
||||
result = this._options.getByOriginResult.length ?
|
||||
this._options.getByOriginResult.shift() : null;
|
||||
} else {
|
||||
result = this._options.getByOriginResult;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
},
|
||||
|
||||
getByMsisdn: function() {
|
||||
this._spy("getByMsisdn", arguments);
|
||||
return Promise.resolve(this._options.getByMsisdnResult ||
|
||||
this._getByMsisdnResult);
|
||||
},
|
||||
|
||||
getByIccId: function() {
|
||||
this._spy("getByIccId", arguments);
|
||||
return Promise.resolve(this._options.getByIccIdResult ||
|
||||
this._getByIccIdResult);
|
||||
},
|
||||
|
||||
add: function() {
|
||||
this._spy("add", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
setDeviceIccIds: function() {
|
||||
this._spy("setDeviceIccIds", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
removeOrigin: function() {
|
||||
this._spy("removeOrigin", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
this._spy("delete", arguments);
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
// Client mock up.
|
||||
let MockClient = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockClient.prototype = {
|
||||
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_discoverResult: {
|
||||
verificationMethods: ["sms/mt"],
|
||||
verificationDetails: {
|
||||
"sms/mt": {
|
||||
mtSender: "123",
|
||||
url: "https://msisdn.accounts.firefox.com/v1/msisdn/sms/mt/verify"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_registerResult: {
|
||||
msisdnSessionToken: SESSION_TOKEN
|
||||
},
|
||||
|
||||
_smsMtVerifyResult: {},
|
||||
|
||||
_verifyCodeResult: {
|
||||
msisdn: PHONE_NUMBER
|
||||
},
|
||||
|
||||
_signResult: {
|
||||
cert: CERTIFICATE
|
||||
},
|
||||
|
||||
hawk: {
|
||||
now: function() {
|
||||
return Date.now();
|
||||
}
|
||||
},
|
||||
|
||||
discover: function() {
|
||||
this._spy("discover", arguments);
|
||||
return Promise.resolve(this._options.discoverResult ||
|
||||
this._discoverResult);
|
||||
},
|
||||
|
||||
register: function() {
|
||||
this._spy("register", arguments);
|
||||
return Promise.resolve(this._options.registerResult ||
|
||||
this._registerResult);
|
||||
},
|
||||
|
||||
smsMtVerify: function() {
|
||||
this._spy("smsMtVerify", arguments);
|
||||
return Promise.resolve(this._options.smsMtVerifyResult ||
|
||||
this._smsMtVerifyResult);
|
||||
},
|
||||
|
||||
verifyCode: function() {
|
||||
this._spy("verifyCode", arguments);
|
||||
if (this._options.verifyCodeError) {
|
||||
let error = Array.isArray(this._options.verifyCodeError) ?
|
||||
this._options.verifyCodeError.shift() :
|
||||
this._options.verifyCodeError;
|
||||
if (!this._options.verifyCodeError.length) {
|
||||
this._options.verifyCodeError = null;
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return Promise.resolve(this._options.verifyCodeResult ||
|
||||
this._verifyCodeResult);
|
||||
},
|
||||
|
||||
sign: function() {
|
||||
this._spy("sign", arguments);
|
||||
if (this._options.signError) {
|
||||
let error = Array.isArray(this._options.signError) ?
|
||||
this._options.signError.shift() :
|
||||
this._options.signError;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return Promise.resolve(this._options.signResult || this._signResult);
|
||||
}
|
||||
};
|
||||
|
||||
// Override MobileIdentityUIGlue.
|
||||
const kMobileIdentityUIGlueUUID = "{05df0566-ca8a-4ec7-bc76-78626ebfbe9a}";
|
||||
const kMobileIdentityUIGlueContractID =
|
||||
"@mozilla.org/services/mobileid-ui-glue;1";
|
||||
|
||||
// Save original factory.
|
||||
/*const kMobileIdentityUIGlueFactory =
|
||||
Cm.getClassObject(Cc[kMobileIdentityUIGlueContractID], Ci.nsIFactory);*/
|
||||
|
||||
let fakeMobileIdentityUIGlueFactory = {
|
||||
createInstance: function(aOuter, aIid) {
|
||||
return MobileIdentityUIGlue.QueryInterface(aIid);
|
||||
}
|
||||
};
|
||||
|
||||
// MobileIdentityUIGlue fake component.
|
||||
let MobileIdentityUIGlue = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileIdentityUIGlue]),
|
||||
|
||||
};
|
||||
|
||||
(function registerFakeMobileIdentityUIGlue() {
|
||||
Cm.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(Components.ID(kMobileIdentityUIGlueUUID),
|
||||
"MobileIdentityUIGlue",
|
||||
kMobileIdentityUIGlueContractID,
|
||||
fakeMobileIdentityUIGlueFactory);
|
||||
})();
|
||||
|
||||
// The tests rely on having an app registered. Otherwise, it will throw.
|
||||
// Override XULAppInfo.
|
||||
const XUL_APP_INFO_UUID = Components.ID("{84fdc459-d96d-421c-9bff-a8193233ae75}");
|
||||
const XUL_APP_INFO_CONTRACT_ID = "@mozilla.org/xre/app-info;1";
|
||||
|
||||
let (XULAppInfo = {
|
||||
vendor: "Mozilla",
|
||||
name: "MobileIdTest",
|
||||
ID: "{230de50e-4cd1-11dc-8314-0800200b9a66}",
|
||||
version: "1",
|
||||
appBuildID: "2007010101",
|
||||
platformVersion: "",
|
||||
platformBuildID: "2007010101",
|
||||
inSafeMode: false,
|
||||
logConsoleErrors: true,
|
||||
OS: "XPCShell",
|
||||
XPCOMABI: "noarch-spidermonkey",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIXULAppInfo,
|
||||
Ci.nsIXULRuntime,
|
||||
])
|
||||
}) {
|
||||
let XULAppInfoFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return XULAppInfo.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
Cm.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(XUL_APP_INFO_UUID,
|
||||
"XULAppInfo",
|
||||
XUL_APP_INFO_CONTRACT_ID,
|
||||
XULAppInfoFactory);
|
||||
}
|
||||
|
@ -3,424 +3,16 @@
|
||||
|
||||
"use strict";
|
||||
|
||||
const Cm = Components.manager;
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/MobileIdentityManager.jsm");
|
||||
Cu.import("resource://gre/modules/MobileIdentityCommon.jsm");
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
const GET_ASSERTION_IPC_MSG = "MobileId:GetAssertion";
|
||||
const GET_ASSERTION_RETURN_OK = "MobileId:GetAssertion:Return:OK";
|
||||
const GET_ASSERTION_RETURN_KO = "MobileId:GetAssertion:Return:KO";
|
||||
|
||||
// === Globals ===
|
||||
|
||||
const ORIGIN = "app://afakeorigin";
|
||||
const APP_ID = 1;
|
||||
const PRINCIPAL = {
|
||||
origin: ORIGIN,
|
||||
appId: APP_ID
|
||||
};
|
||||
const PHONE_NUMBER = "+34666555444";
|
||||
const ANOTHER_PHONE_NUMBER = "+44123123123";
|
||||
const VERIFICATION_CODE = "123456";
|
||||
const SESSION_TOKEN = "aSessionToken";
|
||||
const ICC_ID = "aIccId";
|
||||
const ANOTHER_ICC_ID = "anotherIccId";
|
||||
const MNC = "aMnc";
|
||||
const ANOTHER_MNC = "anotherMnc";
|
||||
const MCC = "aMcc";
|
||||
const ANOTHER_MCC = "anotherMcc";
|
||||
const OPERATOR = "aOperator";
|
||||
const ANOTHER_OPERATOR = "anotherOperator";
|
||||
const RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: ICC_ID,
|
||||
mcc: MCC,
|
||||
mnc: MNC,
|
||||
msisdn: PHONE_NUMBER,
|
||||
operator: OPERATOR
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: OPERATOR
|
||||
},
|
||||
roaming: false
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: OPERATOR
|
||||
}
|
||||
}
|
||||
};
|
||||
const ANOTHER_RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: ANOTHER_ICC_ID,
|
||||
mcc: ANOTHER_MCC,
|
||||
mnc: ANOTHER_MNC,
|
||||
msisdn: ANOTHER_PHONE_NUMBER,
|
||||
operator: ANOTHER_OPERATOR
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: ANOTHER_OPERATOR
|
||||
},
|
||||
roaming: false
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: ANOTHER_OPERATOR
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const INVALID_RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: null,
|
||||
mcc: "",
|
||||
mnc: "",
|
||||
msisdn: "",
|
||||
operator: ""
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: ""
|
||||
},
|
||||
roaming: undefined
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: ""
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const CERTIFICATE = "eyJhbGciOiJEUzI1NiJ9.eyJsYXN0QXV0aEF0IjoxNDA0NDY5NzkyODc3LCJ2ZXJpZmllZE1TSVNETiI6IiszMTYxNzgxNTc1OCIsInB1YmxpYy1rZXkiOnsiYWxnb3JpdGhtIjoiRFMiLCJ5IjoiNGE5YzkzNDY3MWZhNzQ3YmM2ZjMyNjE0YTg1MzUyZjY5NDcwMDdhNTRkMDAxMDY4OWU5ZjJjZjc0ZGUwYTEwZTRlYjlmNDk1ZGFmZTA0NGVjZmVlNDlkN2YwOGU4ODQyMDJiOTE5OGRhNWZhZWE5MGUzZjRmNzE1YzZjNGY4Yjc3MGYxZTU4YWZhNDM0NzVhYmFiN2VlZGE1MmUyNjk2YzFmNTljNzMzYjFlYzBhNGNkOTM1YWIxYzkyNzAxYjNiYTA5ZDRhM2E2MzNjNTJmZjE2NGYxMWY3OTg1YzlmZjY3ZThmZDFlYzA2NDU3MTdkMjBiNDE4YmM5M2YzYzVkNCIsInAiOiJmZjYwMDQ4M2RiNmFiZmM1YjQ1ZWFiNzg1OTRiMzUzM2Q1NTBkOWYxYmYyYTk5MmE3YThkYWE2ZGMzNGY4MDQ1YWQ0ZTZlMGM0MjlkMzM0ZWVlYWFlZmQ3ZTIzZDQ4MTBiZTAwZTRjYzE0OTJjYmEzMjViYTgxZmYyZDVhNWIzMDVhOGQxN2ViM2JmNGEwNmEzNDlkMzkyZTAwZDMyOTc0NGE1MTc5MzgwMzQ0ZTgyYTE4YzQ3OTMzNDM4Zjg5MWUyMmFlZWY4MTJkNjljOGY3NWUzMjZjYjcwZWEwMDBjM2Y3NzZkZmRiZDYwNDYzOGMyZWY3MTdmYzI2ZDAyZTE3IiwicSI6ImUyMWUwNGY5MTFkMWVkNzk5MTAwOGVjYWFiM2JmNzc1OTg0MzA5YzMiLCJnIjoiYzUyYTRhMGZmM2I3ZTYxZmRmMTg2N2NlODQxMzgzNjlhNjE1NGY0YWZhOTI5NjZlM2M4MjdlMjVjZmE2Y2Y1MDhiOTBlNWRlNDE5ZTEzMzdlMDdhMmU5ZTJhM2NkNWRlYTcwNGQxNzVmOGViZjZhZjM5N2Q2OWUxMTBiOTZhZmIxN2M3YTAzMjU5MzI5ZTQ4MjliMGQwM2JiYzc4OTZiMTViNGFkZTUzZTEzMDg1OGNjMzRkOTYyNjlhYTg5MDQxZjQwOTEzNmM3MjQyYTM4ODk1YzlkNWJjY2FkNGYzODlhZjFkN2E0YmQxMzk4YmQwNzJkZmZhODk2MjMzMzk3YSJ9LCJwcmluY2lwYWwiOiIwMzgxOTgyYS0xZTgzLTI1NjYtNjgzZS05MDRmNDA0NGM1MGRAbXNpc2RuLWRldi5zdGFnZS5tb3phd3MubmV0IiwiaWF0IjoxNDA0NDY5NzgyODc3LCJleHAiOjE0MDQ0OTEzOTI4NzcsImlzcyI6Im1zaXNkbi1kZXYuc3RhZ2UubW96YXdzLm5ldCJ9."
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
function addPermission(aAction) {
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(ORIGIN, null, null);
|
||||
let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getAppCodebasePrincipal(uri, APP_ID, false);
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
pm.addFromPrincipal(_principal, MOBILEID_PERM, aAction);
|
||||
}
|
||||
|
||||
function removePermission() {
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(ORIGIN, null, null);
|
||||
let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getAppCodebasePrincipal(uri, APP_ID, false);
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
pm.removeFromPrincipal(_principal, MOBILEID_PERM);
|
||||
}
|
||||
|
||||
// === Mocks ===
|
||||
|
||||
let Mock = function(aOptions) {
|
||||
if (!aOptions) {
|
||||
aOptions = {};
|
||||
}
|
||||
this._options = aOptions;
|
||||
this._spied = {};
|
||||
};
|
||||
|
||||
Mock.prototype = {
|
||||
_: function(aMethod) {
|
||||
DEBUG && do_print("_ " + aMethod + JSON.stringify(this._spied));
|
||||
let self = this;
|
||||
return {
|
||||
callsLength: function(aNumberOfCalls) {
|
||||
if (aNumberOfCalls == 0) {
|
||||
do_check_eq(self._spied[aMethod], undefined);
|
||||
return;
|
||||
}
|
||||
do_check_eq(self._spied[aMethod].length, aNumberOfCalls);
|
||||
},
|
||||
call: function(aCallNumber) {
|
||||
return {
|
||||
arg: function(aArgNumber, aValue) {
|
||||
let _arg = self._spied[aMethod][aCallNumber - 1][aArgNumber - 1];
|
||||
if (Array.isArray(aValue)) {
|
||||
do_check_eq(_arg.length, aValue.length)
|
||||
for (let i = 0; i < _arg.length; i++) {
|
||||
do_check_eq(_arg[i], aValue[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof aValue === 'object') {
|
||||
do_check_eq(JSON.stringify(_arg), JSON.stringify(aValue));
|
||||
return;
|
||||
}
|
||||
|
||||
do_check_eq(_arg, aValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_spy: function(aMethod, aArgs) {
|
||||
DEBUG && do_print(aMethod + " - " + JSON.stringify(aArgs));
|
||||
if (!this._spied[aMethod]) {
|
||||
this._spied[aMethod] = [];
|
||||
}
|
||||
this._spied[aMethod].push(aArgs);
|
||||
},
|
||||
|
||||
getSpiedCalls: function(aMethod) {
|
||||
return this._spied[aMethod];
|
||||
}
|
||||
};
|
||||
|
||||
// UI Glue mock up.
|
||||
let MockUi = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockUi.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_startFlowResult: {
|
||||
phoneNumber: PHONE_NUMBER,
|
||||
mcc: MNC
|
||||
},
|
||||
|
||||
_verifyCodePromptResult: {
|
||||
verificationCode: VERIFICATION_CODE
|
||||
},
|
||||
|
||||
startFlow: function() {
|
||||
this._spy("startFlow", arguments);
|
||||
return Promise.resolve(this._options.startFlowResult ||
|
||||
this._startFlowResult);
|
||||
},
|
||||
|
||||
verificationCodePrompt: function() {
|
||||
this._spy("verifyCodePrompt", arguments);
|
||||
return Promise.resolve(this._options.verificationCodePromptResult ||
|
||||
this._verifyCodePromptResult);
|
||||
},
|
||||
|
||||
verify: function() {
|
||||
this._spy("verify", arguments);
|
||||
},
|
||||
|
||||
error: function() {
|
||||
this._spy("error", arguments);
|
||||
},
|
||||
|
||||
verified: function() {
|
||||
this._spy("verified", arguments);
|
||||
},
|
||||
|
||||
set oncancel(aCallback) {
|
||||
},
|
||||
|
||||
set onresendcode(aCallback) {
|
||||
}
|
||||
};
|
||||
|
||||
// Save original credential store instance.
|
||||
const kMobileIdentityCredStore = MobileIdentityManager.credStore;
|
||||
|
||||
// Credentials store mock up.
|
||||
let MockCredStore = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockCredStore.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_getByOriginResult: null,
|
||||
|
||||
_getByMsisdnResult: null,
|
||||
|
||||
_getByIccIdResult: null,
|
||||
|
||||
getByOrigin: function() {
|
||||
this._spy("getByOrigin", arguments);
|
||||
let result = this._getByOriginResult;
|
||||
if (this._options.getByOriginResult) {
|
||||
if (Array.isArray(this._options.getByOriginResult)) {
|
||||
result = this._options.getByOriginResult.length ?
|
||||
this._options.getByOriginResult.shift() : null;
|
||||
} else {
|
||||
result = this._options.getByOriginResult;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
},
|
||||
|
||||
getByMsisdn: function() {
|
||||
this._spy("getByMsisdn", arguments);
|
||||
return Promise.resolve(this._options.getByMsisdnResult ||
|
||||
this._getByMsisdnResult);
|
||||
},
|
||||
|
||||
getByIccId: function() {
|
||||
this._spy("getByIccId", arguments);
|
||||
return Promise.resolve(this._options.getByIccIdResult ||
|
||||
this._getByIccIdResult);
|
||||
},
|
||||
|
||||
add: function() {
|
||||
this._spy("add", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
setDeviceIccIds: function() {
|
||||
this._spy("setDeviceIccIds", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
removeOrigin: function() {
|
||||
this._spy("removeOrigin", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
this._spy("delete", arguments);
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
// Save original client instance.
|
||||
const kMobileIdentityClient = MobileIdentityManager.client;
|
||||
|
||||
// Client mock up.
|
||||
let MockClient = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockClient.prototype = {
|
||||
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_discoverResult: {
|
||||
verificationMethods: ["sms/mt"],
|
||||
verificationDetails: {
|
||||
"sms/mt": {
|
||||
mtSender: "123",
|
||||
url: "https://msisdn.accounts.firefox.com/v1/msisdn/sms/mt/verify"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_registerResult: {
|
||||
msisdnSessionToken: SESSION_TOKEN
|
||||
},
|
||||
|
||||
_smsMtVerifyResult: {},
|
||||
|
||||
_verifyCodeResult: {
|
||||
msisdn: PHONE_NUMBER
|
||||
},
|
||||
|
||||
_signResult: {
|
||||
cert: CERTIFICATE
|
||||
},
|
||||
|
||||
hawk: {
|
||||
now: function() {
|
||||
return Date.now();
|
||||
}
|
||||
},
|
||||
|
||||
discover: function() {
|
||||
this._spy("discover", arguments);
|
||||
return Promise.resolve(this._options.discoverResult ||
|
||||
this._discoverResult);
|
||||
},
|
||||
|
||||
register: function() {
|
||||
this._spy("register", arguments);
|
||||
return Promise.resolve(this._options.registerResult ||
|
||||
this._registerResult);
|
||||
},
|
||||
|
||||
smsMtVerify: function() {
|
||||
this._spy("smsMtVerify", arguments);
|
||||
return Promise.resolve(this._options.smsMtVerifyResult ||
|
||||
this._smsMtVerifyResult);
|
||||
},
|
||||
|
||||
verifyCode: function() {
|
||||
this._spy("verifyCode", arguments);
|
||||
return Promise.resolve(this._options.verifyCodeResult ||
|
||||
this._verifyCodeResult);
|
||||
},
|
||||
|
||||
sign: function() {
|
||||
this._spy("sign", arguments);
|
||||
if (this._options.signError) {
|
||||
let error = Array.isArray(this._options.signError) ?
|
||||
this._options.signError.shift() :
|
||||
this._options.signError;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return Promise.resolve(this._options.signResult || this._signResult);
|
||||
}
|
||||
};
|
||||
|
||||
// The test rely on having an app registered. Otherwise, it will throw.
|
||||
// Override XULAppInfo.
|
||||
const XUL_APP_INFO_UUID = Components.ID("{84fdc459-d96d-421c-9bff-a8193233ae75}");
|
||||
const XUL_APP_INFO_CONTRACT_ID = "@mozilla.org/xre/app-info;1";
|
||||
|
||||
let (XULAppInfo = {
|
||||
vendor: "Mozilla",
|
||||
name: "MobileIdTest",
|
||||
ID: "{230de50e-4cd1-11dc-8314-0800200b9a66}",
|
||||
version: "1",
|
||||
appBuildID: "2007010101",
|
||||
platformVersion: "",
|
||||
platformBuildID: "2007010101",
|
||||
inSafeMode: false,
|
||||
logConsoleErrors: true,
|
||||
OS: "XPCShell",
|
||||
XPCOMABI: "noarch-spidermonkey",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIXULAppInfo,
|
||||
Ci.nsIXULRuntime,
|
||||
])
|
||||
}) {
|
||||
let XULAppInfoFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return XULAppInfo.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
Cm.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(XUL_APP_INFO_UUID,
|
||||
"XULAppInfo",
|
||||
XUL_APP_INFO_CONTRACT_ID,
|
||||
XULAppInfoFactory);
|
||||
}
|
||||
|
||||
// === Global cleanup ===
|
||||
|
||||
function cleanup() {
|
||||
MobileIdentityManager.credStore = kMobileIdentityCredStore;
|
||||
MobileIdentityManager.client = kMobileIdentityClient;
|
||||
@ -431,7 +23,6 @@ function cleanup() {
|
||||
|
||||
// Unregister mocks and restore original code.
|
||||
do_register_cleanup(cleanup);
|
||||
|
||||
// === Tests ===
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
|
@ -0,0 +1,42 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/MobileIdentityVerificationFlow.jsm");
|
||||
|
||||
function verifyStrategy() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function cleanupStrategy() {
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_print("= Bug 1101444: Invalid verification code shouldn't restart " +
|
||||
"verification flow =");
|
||||
|
||||
let client = new MockClient({
|
||||
// This will emulate two invalid attempts. The third time it will work.
|
||||
verifyCodeError: ["INVALID", "INVALID"]
|
||||
});
|
||||
let ui = new MockUi();
|
||||
|
||||
let verificationFlow = new MobileIdentityVerificationFlow({
|
||||
external: true,
|
||||
sessionToken: SESSION_TOKEN,
|
||||
msisdn: PHONE_NUMBER
|
||||
}, ui, client, verifyStrategy, cleanupStrategy);
|
||||
|
||||
verificationFlow.doVerification().then(() => {
|
||||
// We should only do the registration process once. We only try registering
|
||||
// again when the timeout fires, but not when we enter an invalid
|
||||
// verification code.
|
||||
client._("register").callsLength(1);
|
||||
client._("verifyCode").callsLength(3);
|
||||
// Because we do two invalid attempts, we should show the invalid code error twice.
|
||||
ui._("error").callsLength(2);
|
||||
});
|
||||
|
||||
do_test_finished();
|
||||
};
|
@ -1,7 +1,8 @@
|
||||
[DEFAULT]
|
||||
head = head.js
|
||||
tail =
|
||||
skip-if = toolkit == 'gonk'
|
||||
|
||||
[test_mobileid_manager.js]
|
||||
skip-if = 1
|
||||
[test_mobileid_client.js]
|
||||
[test_mobileid_verification_flow.js]
|
||||
|
@ -5,7 +5,7 @@
|
||||
from setuptools import setup
|
||||
|
||||
PACKAGE_NAME = 'mozdevice'
|
||||
PACKAGE_VERSION = '0.43'
|
||||
PACKAGE_VERSION = '0.44'
|
||||
|
||||
deps = ['mozfile >= 1.0',
|
||||
'mozlog >= 2.1',
|
||||
|
@ -1312,7 +1312,8 @@ public:
|
||||
|
||||
bool Collect(ccType aCCType,
|
||||
SliceBudget& aBudget,
|
||||
nsICycleCollectorListener* aManualListener);
|
||||
nsICycleCollectorListener* aManualListener,
|
||||
bool aPreferShorterSlices = false);
|
||||
void Shutdown();
|
||||
|
||||
void SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf,
|
||||
@ -3600,7 +3601,8 @@ PrintPhase(const char* aPhase)
|
||||
bool
|
||||
nsCycleCollector::Collect(ccType aCCType,
|
||||
SliceBudget& aBudget,
|
||||
nsICycleCollectorListener* aManualListener)
|
||||
nsICycleCollectorListener* aManualListener,
|
||||
bool aPreferShorterSlices)
|
||||
{
|
||||
CheckThreadSafety();
|
||||
|
||||
@ -3623,7 +3625,7 @@ nsCycleCollector::Collect(ccType aCCType,
|
||||
|
||||
++mResults.mNumSlices;
|
||||
|
||||
bool continueSlice = true;
|
||||
bool continueSlice = aBudget.isUnlimited() || !aPreferShorterSlices;
|
||||
do {
|
||||
switch (mIncrementalPhase) {
|
||||
case IdlePhase:
|
||||
@ -3640,7 +3642,8 @@ nsCycleCollector::Collect(ccType aCCType,
|
||||
// (There's no need to check if we've finished graph building, because
|
||||
// if we haven't, we've already exceeded our budget, and will finish
|
||||
// this slice anyways.)
|
||||
continueSlice = aBudget.isUnlimited() || mResults.mNumSlices < 3;
|
||||
continueSlice = aBudget.isUnlimited() ||
|
||||
(mResults.mNumSlices < 3 && !aPreferShorterSlices);
|
||||
break;
|
||||
case ScanAndCollectWhitePhase:
|
||||
// We do ScanRoots and CollectWhite in a single slice to ensure
|
||||
@ -4225,7 +4228,8 @@ nsCycleCollector_collect(nsICycleCollectorListener* aManualListener)
|
||||
}
|
||||
|
||||
void
|
||||
nsCycleCollector_collectSlice(SliceBudget& budget)
|
||||
nsCycleCollector_collectSlice(SliceBudget& budget,
|
||||
bool aPreferShorterSlices)
|
||||
{
|
||||
CollectorData* data = sCollectorData.get();
|
||||
|
||||
@ -4236,7 +4240,7 @@ nsCycleCollector_collectSlice(SliceBudget& budget)
|
||||
PROFILER_LABEL("nsCycleCollector", "collectSlice",
|
||||
js::ProfileEntry::Category::CC);
|
||||
|
||||
data->mCollector->Collect(SliceCC, budget, nullptr);
|
||||
data->mCollector->Collect(SliceCC, budget, nullptr, aPreferShorterSlices);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -58,7 +58,8 @@ already_AddRefed<nsICycleCollectorLogSink> nsCycleCollector_createLogSink();
|
||||
|
||||
void nsCycleCollector_collect(nsICycleCollectorListener* aManualListener);
|
||||
|
||||
void nsCycleCollector_collectSlice(js::SliceBudget& budget);
|
||||
void nsCycleCollector_collectSlice(js::SliceBudget& budget,
|
||||
bool aPreferShorterSlices = false);
|
||||
|
||||
uint32_t nsCycleCollector_suspectedCount();
|
||||
void nsCycleCollector_shutdown();
|
||||
|
Loading…
Reference in New Issue
Block a user